1 /*
2 * Copyright 2023 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/codec/SkAndroidCodec.h"
9 #include "include/codec/SkCodec.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkShader.h"
15 #include "include/core/SkSize.h"
16 #include "include/core/SkStream.h"
17 #include "include/core/SkTypes.h"
18 #include "include/encode/SkJpegEncoder.h"
19 #include "include/private/SkGainmapInfo.h"
20 #include "include/private/SkGainmapShader.h"
21 #include "include/private/SkJpegGainmapEncoder.h"
22 #include "src/codec/SkJpegCodec.h"
23 #include "src/codec/SkJpegConstants.h"
24 #include "src/codec/SkJpegMultiPicture.h"
25 #include "src/codec/SkJpegSegmentScan.h"
26 #include "src/codec/SkJpegSourceMgr.h"
27 #include "src/codec/SkTiffUtility.h"
28 #include "tests/Test.h"
29 #include "tools/Resources.h"
30
31 #include <cstdint>
32 #include <cstring>
33 #include <memory>
34 #include <utility>
35 #include <vector>
36
37 namespace {
38
39 // Return true if the relative difference between x and y is less than epsilon.
approx_eq(float x,float y,float epsilon)40 static bool approx_eq(float x, float y, float epsilon) {
41 float numerator = std::abs(x - y);
42 // To avoid being too sensitive around zero, set the minimum denominator to epsilon.
43 float denominator = std::max(std::min(std::abs(x), std::abs(y)), epsilon);
44 if (numerator / denominator > epsilon) {
45 return false;
46 }
47 return true;
48 }
49
approx_eq(const SkColor4f & x,const SkColor4f & y,float epsilon)50 static bool approx_eq(const SkColor4f& x, const SkColor4f& y, float epsilon) {
51 return approx_eq(x.fR, y.fR, epsilon) && approx_eq(x.fG, y.fG, epsilon) &&
52 approx_eq(x.fB, y.fB, epsilon);
53 }
54
55 template <typename Reporter>
expect_approx_eq_info(Reporter & r,const SkGainmapInfo & a,const SkGainmapInfo & b)56 void expect_approx_eq_info(Reporter& r, const SkGainmapInfo& a, const SkGainmapInfo& b) {
57 float kEpsilon = 1e-4f;
58 REPORTER_ASSERT(r, approx_eq(a.fGainmapRatioMin, b.fGainmapRatioMin, kEpsilon));
59 REPORTER_ASSERT(r, approx_eq(a.fGainmapRatioMin, b.fGainmapRatioMin, kEpsilon));
60 REPORTER_ASSERT(r, approx_eq(a.fGainmapGamma, b.fGainmapGamma, kEpsilon));
61 REPORTER_ASSERT(r, approx_eq(a.fEpsilonSdr, b.fEpsilonSdr, kEpsilon));
62 REPORTER_ASSERT(r, approx_eq(a.fEpsilonHdr, b.fEpsilonHdr, kEpsilon));
63 REPORTER_ASSERT(r, approx_eq(a.fDisplayRatioSdr, b.fDisplayRatioSdr, kEpsilon));
64 REPORTER_ASSERT(r, approx_eq(a.fDisplayRatioHdr, b.fDisplayRatioHdr, kEpsilon));
65 REPORTER_ASSERT(r, a.fType == b.fType);
66 REPORTER_ASSERT(r, a.fBaseImageType == b.fBaseImageType);
67
68 REPORTER_ASSERT(r, !!a.fGainmapMathColorSpace == !!b.fGainmapMathColorSpace);
69 if (a.fGainmapMathColorSpace) {
70 skcms_TransferFunction a_fn;
71 skcms_Matrix3x3 a_m;
72 a.fGainmapMathColorSpace->transferFn(&a_fn);
73 a.fGainmapMathColorSpace->toXYZD50(&a_m);
74 skcms_TransferFunction b_fn;
75 skcms_Matrix3x3 b_m;
76 b.fGainmapMathColorSpace->transferFn(&b_fn);
77 b.fGainmapMathColorSpace->toXYZD50(&b_m);
78
79 REPORTER_ASSERT(r, approx_eq(a_fn.g, b_fn.g, kEpsilon));
80 REPORTER_ASSERT(r, approx_eq(a_fn.a, b_fn.a, kEpsilon));
81 REPORTER_ASSERT(r, approx_eq(a_fn.b, b_fn.b, kEpsilon));
82 REPORTER_ASSERT(r, approx_eq(a_fn.c, b_fn.c, kEpsilon));
83 REPORTER_ASSERT(r, approx_eq(a_fn.d, b_fn.d, kEpsilon));
84 REPORTER_ASSERT(r, approx_eq(a_fn.e, b_fn.e, kEpsilon));
85 REPORTER_ASSERT(r, approx_eq(a_fn.f, b_fn.f, kEpsilon));
86
87 // The round-trip of the color space through the ICC profile loses significant precision.
88 // Use a larger epsilon for it.
89 const float kMatrixEpsilon = 1e-2f;
90 for (int i = 0; i < 3; ++i) {
91 for (int j = 0; j < 3; ++j) {
92 REPORTER_ASSERT(r, approx_eq(a_m.vals[i][j], b_m.vals[i][j], kMatrixEpsilon));
93 }
94 }
95 }
96 }
97
98 // A test stream to stress the different SkJpegSourceMgr sub-classes.
99 class TestStream : public SkStream {
100 public:
101 enum class Type {
102 kUnseekable, // SkJpegUnseekableSourceMgr
103 kSeekable, // SkJpegBufferedSourceMgr
104 kMemoryMapped, // SkJpegMemorySourceMgr
105 };
TestStream(Type type,SkStream * stream)106 TestStream(Type type, SkStream* stream)
107 : fStream(stream)
108 , fSeekable(type != Type::kUnseekable)
109 , fMemoryMapped(type == Type::kMemoryMapped) {}
~TestStream()110 ~TestStream() override {}
111
read(void * buffer,size_t size)112 size_t read(void* buffer, size_t size) override { return fStream->read(buffer, size); }
peek(void * buffer,size_t size) const113 size_t peek(void* buffer, size_t size) const override { return fStream->peek(buffer, size); }
isAtEnd() const114 bool isAtEnd() const override { return fStream->isAtEnd(); }
rewind()115 bool rewind() override {
116 if (!fSeekable) {
117 return false;
118 }
119 return fStream->rewind();
120 }
hasPosition() const121 bool hasPosition() const override {
122 if (!fSeekable) {
123 return false;
124 }
125 return fStream->hasPosition();
126 }
getPosition() const127 size_t getPosition() const override {
128 if (!fSeekable) {
129 return 0;
130 }
131 return fStream->hasPosition();
132 }
seek(size_t position)133 bool seek(size_t position) override {
134 if (!fSeekable) {
135 return 0;
136 }
137 return fStream->seek(position);
138 }
move(long offset)139 bool move(long offset) override {
140 if (!fSeekable) {
141 return 0;
142 }
143 return fStream->move(offset);
144 }
hasLength() const145 bool hasLength() const override {
146 if (!fMemoryMapped) {
147 return false;
148 }
149 return fStream->hasLength();
150 }
getLength() const151 size_t getLength() const override {
152 if (!fMemoryMapped) {
153 return 0;
154 }
155 return fStream->getLength();
156 }
getMemoryBase()157 const void* getMemoryBase() override {
158 if (!fMemoryMapped) {
159 return nullptr;
160 }
161 return fStream->getMemoryBase();
162 }
163
164 private:
165 SkStream* const fStream;
166 bool fSeekable = false;
167 bool fMemoryMapped = false;
168 };
169
170 } // namespace
171
DEF_TEST(Codec_jpegSegmentScan,r)172 DEF_TEST(Codec_jpegSegmentScan, r) {
173 const struct Rec {
174 const char* path;
175 size_t sosSegmentCount;
176 size_t eoiSegmentCount;
177 size_t testSegmentIndex;
178 uint8_t testSegmentMarker;
179 size_t testSegmentOffset;
180 uint16_t testSegmentParameterLength;
181 } recs[] = {
182 {"images/wide_gamut_yellow_224_224_64.jpeg", 11, 15, 10, 0xda, 9768, 12},
183 {"images/CMYK.jpg", 7, 8, 1, 0xee, 2, 14},
184 {"images/b78329453.jpeg", 10, 23, 3, 0xe2, 154, 540},
185 {"images/brickwork-texture.jpg", 8, 28, 12, 0xc4, 34183, 42},
186 {"images/brickwork_normal-map.jpg", 8, 28, 27, 0xd9, 180612, 0},
187 {"images/cmyk_yellow_224_224_32.jpg", 19, 23, 2, 0xed, 854, 2828},
188 {"images/color_wheel.jpg", 10, 11, 2, 0xdb, 20, 67},
189 {"images/cropped_mandrill.jpg", 10, 11, 4, 0xc0, 158, 17},
190 {"images/dog.jpg", 10, 11, 5, 0xc4, 177, 28},
191 {"images/ducky.jpg", 12, 13, 10, 0xc4, 3718, 181},
192 {"images/exif-orientation-2-ur.jpg", 11, 12, 2, 0xe1, 20, 130},
193 {"images/flutter_logo.jpg", 9, 27, 21, 0xda, 5731, 8},
194 {"images/grayscale.jpg", 6, 16, 9, 0xda, 327, 8},
195 {"images/icc-v2-gbr.jpg", 12, 25, 24, 0xd9, 43832, 0},
196 {"images/mandrill_512_q075.jpg", 10, 11, 7, 0xc4, 393, 31},
197 {"images/mandrill_cmyk.jpg", 19, 35, 16, 0xdd, 574336, 4},
198 {"images/mandrill_h1v1.jpg", 10, 11, 1, 0xe0, 2, 16},
199 {"images/mandrill_h2v1.jpg", 10, 11, 0, 0xd8, 0, 0},
200 {"images/randPixels.jpg", 10, 11, 6, 0xc4, 200, 30},
201 {"images/wide_gamut_yellow_224_224_64.jpeg", 11, 15, 10, 0xda, 9768, 12},
202 };
203
204 for (const auto& rec : recs) {
205 auto stream = GetResourceAsStream(rec.path);
206 if (!stream) {
207 continue;
208 }
209
210 // Scan all the way to EndOfImage.
211 auto sourceMgr = SkJpegSourceMgr::Make(stream.get());
212 const auto& segments = sourceMgr->getAllSegments();
213
214 // Verify we got the expected number of segments at EndOfImage
215 REPORTER_ASSERT(r, rec.eoiSegmentCount == segments.size());
216
217 // Verify we got the expected number of segments before StartOfScan
218 for (size_t i = 0; i < segments.size(); ++i) {
219 if (segments[i].marker == kJpegMarkerStartOfScan) {
220 REPORTER_ASSERT(r, rec.sosSegmentCount == i + 1);
221 break;
222 }
223 }
224
225 // Verify the values for a randomly pre-selected segment index.
226 const auto& segment = segments[rec.testSegmentIndex];
227 REPORTER_ASSERT(r, rec.testSegmentMarker == segment.marker);
228 REPORTER_ASSERT(r, rec.testSegmentOffset == segment.offset);
229 REPORTER_ASSERT(r, rec.testSegmentParameterLength == segment.parameterLength);
230 }
231 }
232
find_mp_params_segment(SkStream * stream,std::unique_ptr<SkJpegMultiPictureParameters> * outMpParams,SkJpegSegment * outMpParamsSegment)233 static bool find_mp_params_segment(SkStream* stream,
234 std::unique_ptr<SkJpegMultiPictureParameters>* outMpParams,
235 SkJpegSegment* outMpParamsSegment) {
236 auto sourceMgr = SkJpegSourceMgr::Make(stream);
237 for (const auto& segment : sourceMgr->getAllSegments()) {
238 if (segment.marker != kMpfMarker) {
239 continue;
240 }
241 auto parameterData = sourceMgr->getSegmentParameters(segment);
242 if (!parameterData) {
243 continue;
244 }
245 *outMpParams = SkJpegMultiPictureParameters::Make(parameterData);
246 if (*outMpParams) {
247 *outMpParamsSegment = segment;
248 return true;
249 }
250 }
251 return false;
252 }
253
DEF_TEST(Codec_multiPictureParams,r)254 DEF_TEST(Codec_multiPictureParams, r) {
255 // Little-endian test.
256 {
257 const uint8_t bytes[] = {
258 0x4d, 0x50, 0x46, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03,
259 0x00, 0x00, 0xb0, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x31, 0x30, 0x30,
260 0x01, 0xb0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
261 0xb0, 0x07, 0x00, 0x20, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
262 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x20, 0xcf, 0x49, 0x00, 0x00, 0x00, 0x00,
263 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x28, 0x01, 0x00,
264 0xf9, 0xb7, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
265 };
266 auto mpParams =
267 SkJpegMultiPictureParameters::Make(SkData::MakeWithoutCopy(bytes, sizeof(bytes)));
268 REPORTER_ASSERT(r, mpParams);
269 REPORTER_ASSERT(r, mpParams->images.size() == 2);
270 REPORTER_ASSERT(r, mpParams->images[0].dataOffset == 0);
271 REPORTER_ASSERT(r, mpParams->images[0].size == 4837152);
272 REPORTER_ASSERT(r, mpParams->images[1].dataOffset == 3979257);
273 REPORTER_ASSERT(r, mpParams->images[1].size == 76014);
274 }
275
276 // Big-endian test.
277 {
278 const uint8_t bytes[] = {
279 0x4d, 0x50, 0x46, 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00,
280 0x03, 0xb0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x31, 0x30, 0x30,
281 0xb0, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0xb0,
282 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
283 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x56, 0xda, 0x2f, 0x00, 0x00, 0x00,
284 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xc6, 0x01,
285 0x00, 0x55, 0x7c, 0x1f, 0x00, 0x00, 0x00, 0x00,
286 };
287 auto mpParams =
288 SkJpegMultiPictureParameters::Make(SkData::MakeWithoutCopy(bytes, sizeof(bytes)));
289 REPORTER_ASSERT(r, mpParams);
290 REPORTER_ASSERT(r, mpParams->images.size() == 2);
291 REPORTER_ASSERT(r, mpParams->images[0].dataOffset == 0);
292 REPORTER_ASSERT(r, mpParams->images[0].size == 5691951);
293 REPORTER_ASSERT(r, mpParams->images[1].dataOffset == 5602335);
294 REPORTER_ASSERT(r, mpParams->images[1].size == 1361409);
295 }
296
297 // Three entry test.
298 {
299 const uint8_t bytes[] = {
300 0x4d, 0x50, 0x46, 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00,
301 0x03, 0xb0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x31, 0x30, 0x30,
302 0xb0, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0xb0,
303 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
304 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x1c, 0xc2, 0x00, 0x00, 0x00,
305 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0xb0,
306 0x00, 0x1f, 0x12, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307 0x00, 0x96, 0x6b, 0x00, 0x22, 0x18, 0x9c, 0x00, 0x00, 0x00, 0x00,
308 };
309 auto mpParams =
310 SkJpegMultiPictureParameters::Make(SkData::MakeWithoutCopy(bytes, sizeof(bytes)));
311 REPORTER_ASSERT(r, mpParams);
312 REPORTER_ASSERT(r, mpParams->images.size() == 3);
313 REPORTER_ASSERT(r, mpParams->images[0].dataOffset == 0);
314 REPORTER_ASSERT(r, mpParams->images[0].size == 2038978);
315 REPORTER_ASSERT(r, mpParams->images[1].dataOffset == 2036460);
316 REPORTER_ASSERT(r, mpParams->images[1].size == 198064);
317 REPORTER_ASSERT(r, mpParams->images[2].dataOffset == 2234524);
318 REPORTER_ASSERT(r, mpParams->images[2].size == 38507);
319 }
320
321 // Inserting various corrupt values.
322 {
323 const uint8_t bytes[] = {
324 0x4d, 0x50, 0x46, 0x00, // 0: {'M', 'P', 'F', 0} signature
325 0x4d, 0x4d, 0x00, 0x2a, // 4: {'M', 'M', 0, '*'} big-endian
326 0x00, 0x00, 0x00, 0x08, // 8: Index IFD offset
327 0x00, 0x03, // 12: Number of tags
328 0xb0, 0x00, // 14: Version tag
329 0x00, 0x07, // 16: Undefined type
330 0x00, 0x00, 0x00, 0x04, // 18: Size
331 0x30, 0x31, 0x30, 0x30, // 22: Value
332 0xb0, 0x01, // 26: Number of images
333 0x00, 0x04, // 28: Unsigned long type
334 0x00, 0x00, 0x00, 0x01, // 30: Count
335 0x00, 0x00, 0x00, 0x02, // 34: Value
336 0xb0, 0x02, // 38: MP entry tag
337 0x00, 0x07, // 40: Undefined type
338 0x00, 0x00, 0x00, 0x20, // 42: Size
339 0x00, 0x00, 0x00, 0x32, // 46: Value (offset)
340 0x00, 0x00, 0x00, 0x00, // 50: Next IFD offset (null)
341 0x20, 0x03, 0x00, 0x00, // 54: MP Entry 0 attributes
342 0x00, 0x56, 0xda, 0x2f, // 58: MP Entry 0 size (5691951)
343 0x00, 0x00, 0x00, 0x00, // 62: MP Entry 0 offset (0)
344 0x00, 0x00, 0x00, 0x00, // 66: MP Entry 0 dependencies
345 0x00, 0x00, 0x00, 0x00, // 70: MP Entry 1 attributes.
346 0x00, 0x14, 0xc6, 0x01, // 74: MP Entry 1 size (1361409)
347 0x00, 0x55, 0x7c, 0x1f, // 78: MP Entry 1 offset (5602335)
348 0x00, 0x00, 0x00, 0x00, // 82: MP Entry 1 dependencies
349 };
350
351 // Verify the offsets labeled above.
352 REPORTER_ASSERT(r, bytes[22] == 0x30);
353 REPORTER_ASSERT(r, bytes[26] == 0xb0);
354 REPORTER_ASSERT(r, bytes[38] == 0xb0);
355 REPORTER_ASSERT(r, bytes[54] == 0x20);
356 REPORTER_ASSERT(r, bytes[81] == 0x1f);
357
358 {
359 // Change the version to {'0', '1', '0', '1'}.
360 auto bytesInvalid = SkData::MakeWithCopy(bytes, sizeof(bytes));
361 REPORTER_ASSERT(r, bytes[25] == '0');
362 reinterpret_cast<uint8_t*>(bytesInvalid->writable_data())[25] = '1';
363 REPORTER_ASSERT(r, SkJpegMultiPictureParameters::Make(bytesInvalid) == nullptr);
364 }
365
366 {
367 // Change the number of images to be undefined type instead of unsigned long type.
368 auto bytesInvalid = SkData::MakeWithCopy(bytes, sizeof(bytes));
369 REPORTER_ASSERT(r, bytes[29] == 0x04);
370 reinterpret_cast<uint8_t*>(bytesInvalid->writable_data())[29] = 0x07;
371 REPORTER_ASSERT(r, SkJpegMultiPictureParameters::Make(bytesInvalid) == nullptr);
372 }
373
374 {
375 // Make the MP entries point off of the end of the buffer.
376 auto bytesInvalid = SkData::MakeWithCopy(bytes, sizeof(bytes));
377 REPORTER_ASSERT(r, bytes[49] == 0x32);
378 reinterpret_cast<uint8_t*>(bytesInvalid->writable_data())[49] = 0xFE;
379 REPORTER_ASSERT(r, SkJpegMultiPictureParameters::Make(bytesInvalid) == nullptr);
380 }
381
382 {
383 // Make the MP entries too small.
384 auto bytesInvalid = SkData::MakeWithCopy(bytes, sizeof(bytes));
385 REPORTER_ASSERT(r, bytes[45] == 0x20);
386 reinterpret_cast<uint8_t*>(bytesInvalid->writable_data())[45] = 0x1F;
387 REPORTER_ASSERT(r, SkJpegMultiPictureParameters::Make(bytesInvalid) == nullptr);
388 }
389 }
390 }
391
DEF_TEST(Codec_jpegMultiPicture,r)392 DEF_TEST(Codec_jpegMultiPicture, r) {
393 const char* path = "images/iphone_13_pro.jpeg";
394 auto stream = GetResourceAsStream(path);
395 REPORTER_ASSERT(r, stream);
396
397 // Search and parse the MPF header.
398 std::unique_ptr<SkJpegMultiPictureParameters> mpParams;
399 SkJpegSegment mpParamsSegment;
400 REPORTER_ASSERT(r, find_mp_params_segment(stream.get(), &mpParams, &mpParamsSegment));
401
402 // Verify that we get the same parameters when we re-serialize and de-serialize them
403 {
404 auto mpParamsSerialized = mpParams->serialize(0);
405 REPORTER_ASSERT(r, mpParamsSerialized);
406 auto mpParamsRoundTripped = SkJpegMultiPictureParameters::Make(mpParamsSerialized);
407 REPORTER_ASSERT(r, mpParamsRoundTripped);
408 REPORTER_ASSERT(r, mpParamsRoundTripped->images.size() == mpParams->images.size());
409 for (size_t i = 0; i < mpParamsRoundTripped->images.size(); ++i) {
410 REPORTER_ASSERT(r, mpParamsRoundTripped->images[i].size == mpParams->images[i].size);
411 REPORTER_ASSERT(
412 r,
413 mpParamsRoundTripped->images[i].dataOffset == mpParams->images[i].dataOffset);
414 }
415 }
416
417 const struct Rec {
418 const TestStream::Type streamType;
419 const bool skipFirstImage;
420 const size_t bufferSize;
421 } recs[] = {
422 {TestStream::Type::kMemoryMapped, false, 1024},
423 {TestStream::Type::kMemoryMapped, true, 1024},
424 {TestStream::Type::kSeekable, false, 1024},
425 {TestStream::Type::kSeekable, true, 1024},
426 {TestStream::Type::kSeekable, false, 7},
427 {TestStream::Type::kSeekable, true, 13},
428 {TestStream::Type::kSeekable, true, 1024 * 1024 * 16},
429 {TestStream::Type::kUnseekable, false, 1024},
430 {TestStream::Type::kUnseekable, true, 1024},
431 {TestStream::Type::kUnseekable, false, 1},
432 {TestStream::Type::kUnseekable, true, 1},
433 {TestStream::Type::kUnseekable, false, 7},
434 {TestStream::Type::kUnseekable, true, 13},
435 {TestStream::Type::kUnseekable, false, 1024 * 1024 * 16},
436 {TestStream::Type::kUnseekable, true, 1024 * 1024 * 16},
437 };
438 for (const auto& rec : recs) {
439 stream->rewind();
440 TestStream testStream(rec.streamType, stream.get());
441 auto sourceMgr = SkJpegSourceMgr::Make(&testStream, rec.bufferSize);
442
443 // Decode the images into bitmaps.
444 size_t numberOfImages = mpParams->images.size();
445 std::vector<SkBitmap> bitmaps(numberOfImages);
446 for (size_t i = 0; i < numberOfImages; ++i) {
447 if (i == 0) {
448 REPORTER_ASSERT(r, mpParams->images[i].dataOffset == 0);
449 continue;
450 }
451 if (i == 1 && rec.skipFirstImage) {
452 continue;
453 }
454 auto imageData = sourceMgr->getSubsetData(
455 SkJpegMultiPictureParameters::GetImageAbsoluteOffset(
456 mpParams->images[i].dataOffset, mpParamsSegment.offset),
457 mpParams->images[i].size);
458 REPORTER_ASSERT(r, imageData);
459
460 std::unique_ptr<SkCodec> codec =
461 SkCodec::MakeFromStream(SkMemoryStream::Make(imageData));
462 REPORTER_ASSERT(r, codec);
463
464 SkBitmap bm;
465 bm.allocPixels(codec->getInfo());
466 REPORTER_ASSERT(r,
467 SkCodec::kSuccess ==
468 codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()));
469 bitmaps[i] = bm;
470 }
471
472 // Spot-check the image size and pixels.
473 if (!rec.skipFirstImage) {
474 REPORTER_ASSERT(r, bitmaps[1].dimensions() == SkISize::Make(1512, 2016));
475 REPORTER_ASSERT(r, bitmaps[1].getColor(0, 0) == 0xFF3B3B3B);
476 REPORTER_ASSERT(r, bitmaps[1].getColor(1511, 2015) == 0xFF101010);
477 }
478 REPORTER_ASSERT(r, bitmaps[2].dimensions() == SkISize::Make(576, 768));
479 REPORTER_ASSERT(r, bitmaps[2].getColor(0, 0) == 0xFF010101);
480 REPORTER_ASSERT(r, bitmaps[2].getColor(575, 767) == 0xFFB5B5B5);
481 }
482 }
483
484 // Decode an image and its gainmap.
485 template <typename Reporter>
decode_all(Reporter & r,std::unique_ptr<SkStream> stream,SkBitmap & baseBitmap,SkBitmap & gainmapBitmap,SkGainmapInfo & gainmapInfo)486 void decode_all(Reporter& r,
487 std::unique_ptr<SkStream> stream,
488 SkBitmap& baseBitmap,
489 SkBitmap& gainmapBitmap,
490 SkGainmapInfo& gainmapInfo) {
491 // Decode the base bitmap.
492 SkCodec::Result result = SkCodec::kSuccess;
493 std::unique_ptr<SkCodec> baseCodec = SkJpegCodec::MakeFromStream(std::move(stream), &result);
494 REPORTER_ASSERT(r, baseCodec);
495 baseBitmap.allocPixels(baseCodec->getInfo());
496 REPORTER_ASSERT(r,
497 SkCodec::kSuccess == baseCodec->getPixels(baseBitmap.info(),
498 baseBitmap.getPixels(),
499 baseBitmap.rowBytes()));
500 std::unique_ptr<SkAndroidCodec> androidCodec =
501 SkAndroidCodec::MakeFromCodec(std::move(baseCodec));
502 REPORTER_ASSERT(r, androidCodec);
503
504 // Extract the gainmap info and codec.
505 std::unique_ptr<SkAndroidCodec> gainmapCodec;
506 REPORTER_ASSERT(r, androidCodec->getGainmapAndroidCodec(&gainmapInfo, &gainmapCodec));
507 REPORTER_ASSERT(r, gainmapCodec);
508
509 // Decode the gainmap bitmap.
510 SkBitmap bm;
511 bm.allocPixels(gainmapCodec->getInfo());
512 gainmapBitmap.allocPixels(gainmapCodec->getInfo());
513 REPORTER_ASSERT(r,
514 SkCodec::kSuccess == gainmapCodec->getAndroidPixels(gainmapBitmap.info(),
515 gainmapBitmap.getPixels(),
516 gainmapBitmap.rowBytes()));
517 }
518
DEF_TEST(AndroidCodec_jpegGainmapDecode,r)519 DEF_TEST(AndroidCodec_jpegGainmapDecode, r) {
520 const struct Rec {
521 const char* path;
522 SkISize dimensions;
523 SkColor originColor;
524 SkColor farCornerColor;
525 SkGainmapInfo info;
526 } recs[] = {
527 {"images/iphone_13_pro.jpeg",
528 SkISize::Make(1512, 2016),
529 0xFF3B3B3B,
530 0xFF101010,
531 {{1.f, 1.f, 1.f, 1.f},
532 {3.482202f, 3.482202f, 3.482202f, 1.f},
533 {1.f, 1.f, 1.f, 1.f},
534 {0.f, 0.f, 0.f, 1.f},
535 {0.f, 0.f, 0.f, 1.f},
536 1.f,
537 3.482202f,
538 SkGainmapInfo::BaseImageType::kSDR,
539 SkGainmapInfo::Type::kApple,
540 nullptr}},
541 {"images/iphone_15.jpeg",
542 SkISize::Make(2016, 1512),
543 0xFF5C5C5C,
544 0xFF656565,
545 {{1.f, 1.f, 1.f, 1.f},
546 {3.755272f, 3.755272f, 3.755272f, 1.f},
547 {1.f, 1.f, 1.f, 1.f},
548 {0.f, 0.f, 0.f, 1.f},
549 {0.f, 0.f, 0.f, 1.f},
550 1.f,
551 3.755272f,
552 SkGainmapInfo::BaseImageType::kSDR,
553 SkGainmapInfo::Type::kApple,
554 nullptr}},
555 {"images/gainmap_gcontainer_only.jpg",
556 SkISize::Make(32, 32),
557 0xffffffff,
558 0xffffffff,
559 {{25.f, 0.5f, 1.f, 1.f},
560 {2.f, 4.f, 8.f, 1.f},
561 {0.5, 1.f, 2.f, 1.f},
562 {0.01f, 0.001f, 0.0001f, 1.f},
563 {0.0001f, 0.001f, 0.01f, 1.f},
564 2.f,
565 4.f,
566 SkGainmapInfo::BaseImageType::kSDR,
567 SkGainmapInfo::Type::kDefault,
568 nullptr}},
569 {"images/gainmap_iso21496_1_adobe_gcontainer.jpg",
570 SkISize::Make(32, 32),
571 0xffffffff,
572 0xff000000,
573 {{25.f, 0.5f, 1.f, 1.f},
574 {2.f, 4.f, 8.f, 1.f},
575 {0.5, 1.f, 2.f, 1.f},
576 {0.01f, 0.001f, 0.0001f, 1.f},
577 {0.0001f, 0.001f, 0.01f, 1.f},
578 2.f,
579 4.f,
580 SkGainmapInfo::BaseImageType::kSDR,
581 SkGainmapInfo::Type::kDefault,
582 nullptr}},
583 {"images/gainmap_iso21496_1.jpg",
584 SkISize::Make(32, 32),
585 0xffffffff,
586 0xff000000,
587 {{25.f, 0.5f, 1.f, 1.f},
588 {2.f, 4.f, 8.f, 1.f},
589 {0.5, 1.f, 2.f, 1.f},
590 {0.01f, 0.001f, 0.0001f, 1.f},
591 {0.0001f, 0.001f, 0.01f, 1.f},
592 2.f,
593 4.f,
594 SkGainmapInfo::BaseImageType::kHDR,
595 SkGainmapInfo::Type::kDefault,
596 SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kRec2020)}},
597 };
598
599 TestStream::Type kStreamTypes[] = {
600 TestStream::Type::kUnseekable,
601 TestStream::Type::kSeekable,
602 TestStream::Type::kMemoryMapped,
603 };
604 for (const auto& streamType : kStreamTypes) {
605 bool useFileStream = streamType != TestStream::Type::kMemoryMapped;
606 for (const auto& rec : recs) {
607 auto stream = GetResourceAsStream(rec.path, useFileStream);
608 REPORTER_ASSERT(r, stream);
609 auto testStream = std::make_unique<TestStream>(streamType, stream.get());
610
611 SkBitmap baseBitmap;
612 SkBitmap gainmapBitmap;
613 SkGainmapInfo gainmapInfo;
614 decode_all(r, std::move(testStream), baseBitmap, gainmapBitmap, gainmapInfo);
615
616 // Spot-check the image size and pixels.
617 REPORTER_ASSERT(r, gainmapBitmap.dimensions() == rec.dimensions);
618 REPORTER_ASSERT(r, gainmapBitmap.getColor(0, 0) == rec.originColor);
619 REPORTER_ASSERT(
620 r,
621 gainmapBitmap.getColor(rec.dimensions.fWidth - 1, rec.dimensions.fHeight - 1) ==
622 rec.farCornerColor);
623
624 // Verify the gainmap rendering parameters.
625 expect_approx_eq_info(r, rec.info, gainmapInfo);
626 }
627 }
628 }
629
DEF_TEST(AndroidCodec_jpegNoGainmap,r)630 DEF_TEST(AndroidCodec_jpegNoGainmap, r) {
631 // This test image has a large APP16 segment that will stress the various SkJpegSourceMgrs'
632 // data skipping paths.
633 const char* path = "images/icc-v2-gbr.jpg";
634
635 TestStream::Type kStreamTypes[] = {
636 TestStream::Type::kUnseekable,
637 TestStream::Type::kSeekable,
638 TestStream::Type::kMemoryMapped,
639 };
640 for (const auto& streamType : kStreamTypes) {
641 bool useFileStream = streamType != TestStream::Type::kMemoryMapped;
642 auto stream = GetResourceAsStream(path, useFileStream);
643 REPORTER_ASSERT(r, stream);
644 auto testStream = std::make_unique<TestStream>(streamType, stream.get());
645
646 // Decode the base bitmap.
647 SkCodec::Result result = SkCodec::kSuccess;
648 std::unique_ptr<SkCodec> baseCodec =
649 SkJpegCodec::MakeFromStream(std::move(testStream), &result);
650 REPORTER_ASSERT(r, baseCodec);
651 SkBitmap baseBitmap;
652 baseBitmap.allocPixels(baseCodec->getInfo());
653 REPORTER_ASSERT(r,
654 SkCodec::kSuccess == baseCodec->getPixels(baseBitmap.info(),
655 baseBitmap.getPixels(),
656 baseBitmap.rowBytes()));
657
658 std::unique_ptr<SkAndroidCodec> androidCodec =
659 SkAndroidCodec::MakeFromCodec(std::move(baseCodec));
660 REPORTER_ASSERT(r, androidCodec);
661
662 // Try to extract the gainmap info and stream. It should fail.
663 SkGainmapInfo gainmapInfo;
664 std::unique_ptr<SkStream> gainmapStream;
665 REPORTER_ASSERT(r, !androidCodec->getAndroidGainmap(&gainmapInfo, &gainmapStream));
666 }
667 }
668
669 #if !defined(SK_ENABLE_NDK_IMAGES)
670
DEF_TEST(AndroidCodec_gainmapInfoEncode,r)671 DEF_TEST(AndroidCodec_gainmapInfoEncode, r) {
672 SkDynamicMemoryWStream encodeStream;
673
674 constexpr size_t kNumTests = 4;
675
676 SkBitmap baseBitmap;
677 baseBitmap.allocPixels(SkImageInfo::MakeN32Premul(16, 16));
678
679 SkBitmap gainmapBitmaps[kNumTests];
680 gainmapBitmaps[0].allocPixels(SkImageInfo::MakeN32Premul(16, 16));
681 gainmapBitmaps[1].allocPixels(SkImageInfo::MakeN32Premul(8, 8));
682 gainmapBitmaps[2].allocPixels(
683 SkImageInfo::Make(4, 4, kAlpha_8_SkColorType, kPremul_SkAlphaType));
684 gainmapBitmaps[3].allocPixels(
685 SkImageInfo::Make(8, 8, kGray_8_SkColorType, kPremul_SkAlphaType));
686
687 SkGainmapInfo infos[kNumTests] = {
688 // Multi-channel, UltraHDR-compatible.
689 {{1.f, 2.f, 4.f, 1.f},
690 {8.f, 16.f, 32.f, 1.f},
691 {64.f, 128.f, 256.f, 1.f},
692 {1 / 10.f, 1 / 11.f, 1 / 12.f, 1.f},
693 {1 / 13.f, 1 / 14.f, 1 / 15.f, 1.f},
694 4.f,
695 32.f,
696 SkGainmapInfo::BaseImageType::kSDR,
697 SkGainmapInfo::Type::kDefault,
698 nullptr},
699 // Multi-channel, not UltraHDR-compatible.
700 {{1.f, 2.f, 4.f, 1.f},
701 {8.f, 16.f, 32.f, 1.f},
702 {64.f, 128.f, 256.f, 1.f},
703 {1 / 10.f, 1 / 11.f, 1 / 12.f, 1.f},
704 {1 / 13.f, 1 / 14.f, 1 / 15.f, 1.f},
705 4.f,
706 32.f,
707 SkGainmapInfo::BaseImageType::kSDR,
708 SkGainmapInfo::Type::kDefault,
709 SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3)},
710 // Single-channel, UltraHDR-compatible.
711 {{1.f, 1.f, 1.f, 1.f},
712 {8.f, 8.f, 8.f, 1.f},
713 {64.f, 64.f, 64.f, 1.f},
714 {1 / 128.f, 1 / 128.f, 1 / 128.f, 1.f},
715 {1 / 256.f, 1 / 256.f, 1 / 256.f, 1.f},
716 4.f,
717 32.f,
718 SkGainmapInfo::BaseImageType::kSDR,
719 SkGainmapInfo::Type::kDefault,
720 nullptr},
721 // Single-channel, not UltraHDR-compatible.
722 {{1.f, 1.f, 1.f, 1.f},
723 {8.f, 8.f, 8.f, 1.f},
724 {64.f, 64.f, 64.f, 1.f},
725 {1 / 128.f, 1 / 128.f, 1 / 128.f, 1.f},
726 {1 / 256.f, 1 / 256.f, 1 / 256.f, 1.f},
727 4.f,
728 32.f,
729 SkGainmapInfo::BaseImageType::kHDR,
730 SkGainmapInfo::Type::kDefault,
731 SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3)},
732 };
733
734 for (size_t i = 0; i < kNumTests; ++i) {
735 // Encode |gainmapInfo|.
736 bool encodeResult = SkJpegGainmapEncoder::EncodeHDRGM(&encodeStream,
737 baseBitmap.pixmap(),
738 SkJpegEncoder::Options(),
739 gainmapBitmaps[i].pixmap(),
740 SkJpegEncoder::Options(),
741 infos[i]);
742 REPORTER_ASSERT(r, encodeResult);
743
744 // Decode into |decodedGainmapInfo|.
745 SkGainmapInfo decodedGainmapInfo;
746 SkBitmap decodedBaseBitmap;
747 SkBitmap decodedGainmapBitmap;
748 auto decodeStream = std::make_unique<SkMemoryStream>(encodeStream.detachAsData());
749 decode_all(r,
750 std::move(decodeStream),
751 decodedBaseBitmap,
752 decodedGainmapBitmap,
753 decodedGainmapInfo);
754
755 // Verify that the decode reproducd the input.
756 expect_approx_eq_info(r, infos[i], decodedGainmapInfo);
757 }
758 }
759
760 // Render an applied gainmap.
render_gainmap(const SkImageInfo & renderInfo,float renderHdrRatio,const SkBitmap & baseBitmap,const SkBitmap & gainmapBitmap,const SkGainmapInfo & gainmapInfo,int x,int y)761 static SkBitmap render_gainmap(const SkImageInfo& renderInfo,
762 float renderHdrRatio,
763 const SkBitmap& baseBitmap,
764 const SkBitmap& gainmapBitmap,
765 const SkGainmapInfo& gainmapInfo,
766 int x,
767 int y) {
768 SkRect baseRect = SkRect::MakeXYWH(x, y, renderInfo.width(), renderInfo.height());
769
770 float scaleX = gainmapBitmap.width() / static_cast<float>(baseBitmap.width());
771 float scaleY = gainmapBitmap.height() / static_cast<float>(baseBitmap.height());
772 SkRect gainmapRect = SkRect::MakeXYWH(baseRect.x() * scaleX,
773 baseRect.y() * scaleY,
774 baseRect.width() * scaleX,
775 baseRect.height() * scaleY);
776
777 SkRect dstRect = SkRect::Make(renderInfo.dimensions());
778
779 sk_sp<SkImage> baseImage = SkImages::RasterFromBitmap(baseBitmap);
780 sk_sp<SkImage> gainmapImage = SkImages::RasterFromBitmap(gainmapBitmap);
781 sk_sp<SkShader> shader = SkGainmapShader::Make(baseImage,
782 baseRect,
783 SkSamplingOptions(),
784 gainmapImage,
785 gainmapRect,
786 SkSamplingOptions(),
787 gainmapInfo,
788 dstRect,
789 renderHdrRatio,
790 renderInfo.refColorSpace());
791
792 SkBitmap result;
793 result.allocPixels(renderInfo);
794 result.eraseColor(SK_ColorTRANSPARENT);
795 SkCanvas canvas(result);
796
797 SkPaint paint;
798 paint.setShader(shader);
799 canvas.drawRect(dstRect, paint);
800
801 return result;
802 }
803
804 // Render a single pixel of an applied gainmap and return it.
render_gainmap_pixel(float renderHdrRatio,const SkBitmap & baseBitmap,const SkBitmap & gainmapBitmap,const SkGainmapInfo & gainmapInfo,int x,int y)805 static SkColor4f render_gainmap_pixel(float renderHdrRatio,
806 const SkBitmap& baseBitmap,
807 const SkBitmap& gainmapBitmap,
808 const SkGainmapInfo& gainmapInfo,
809 int x,
810 int y) {
811 SkImageInfo testPixelInfo = SkImageInfo::Make(
812 /*width=*/1,
813 /*height=*/1,
814 kRGBA_F16_SkColorType,
815 kPremul_SkAlphaType,
816 SkColorSpace::MakeSRGB());
817 SkBitmap testPixelBitmap = render_gainmap(
818 testPixelInfo, renderHdrRatio, baseBitmap, gainmapBitmap, gainmapInfo, x, y);
819 return testPixelBitmap.getColor4f(0, 0);
820 }
821
DEF_TEST(AndroidCodec_jpegGainmapTranscode,r)822 DEF_TEST(AndroidCodec_jpegGainmapTranscode, r) {
823 const char* path = "images/iphone_13_pro.jpeg";
824 SkBitmap baseBitmap[2];
825 SkBitmap gainmapBitmap[2];
826 SkGainmapInfo gainmapInfo[2];
827
828 // Decode an MPF-based gainmap image.
829 decode_all(r, GetResourceAsStream(path), baseBitmap[0], gainmapBitmap[0], gainmapInfo[0]);
830
831 // This test was written before SkGainmapShader added support for kApple type. Strip the
832 // type out.
833 gainmapInfo[0].fType = SkGainmapInfo::Type::kDefault;
834
835 constexpr float kEpsilon = 1e-2f;
836 {
837 SkDynamicMemoryWStream encodeStream;
838
839 // Transcode to UltraHDR.
840 bool encodeResult = SkJpegGainmapEncoder::EncodeHDRGM(&encodeStream,
841 baseBitmap[0].pixmap(),
842 SkJpegEncoder::Options(),
843 gainmapBitmap[0].pixmap(),
844 SkJpegEncoder::Options(),
845 gainmapInfo[0]);
846 REPORTER_ASSERT(r, encodeResult);
847 auto encodeData = encodeStream.detachAsData();
848
849 // Decode the just-encoded image.
850 auto decodeStream = std::make_unique<SkMemoryStream>(encodeData);
851 decode_all(r, std::move(decodeStream), baseBitmap[1], gainmapBitmap[1], gainmapInfo[1]);
852
853 // HDRGM will have the same rendering parameters.
854 expect_approx_eq_info(r, gainmapInfo[0], gainmapInfo[1]);
855
856 // Render a few pixels and verify that they come out the same. Rendering requires SkSL.
857 const struct Rec {
858 int x;
859 int y;
860 float hdrRatio;
861 SkColor4f expectedColor;
862 SkColorType forcedColorType;
863 } recs[] = {
864 {1446, 1603, 1.05f, {0.984375f, 1.004883f, 1.008789f, 1.f}, kUnknown_SkColorType},
865 {1446, 1603, 100.f, {1.147461f, 1.170898f, 1.174805f, 1.f}, kUnknown_SkColorType},
866 {1446, 1603, 100.f, {1.147461f, 1.170898f, 1.174805f, 1.f}, kGray_8_SkColorType},
867 {1446, 1603, 100.f, {1.147461f, 1.170898f, 1.174805f, 1.f}, kAlpha_8_SkColorType},
868 {1446, 1603, 100.f, {1.147461f, 1.170898f, 1.174805f, 1.f}, kR8_unorm_SkColorType},
869 };
870
871 for (const auto& rec : recs) {
872 SkBitmap gainmapBitmap0;
873 SkASSERT(gainmapBitmap[0].colorType() == kGray_8_SkColorType);
874
875 // Force various different single-channel formats, to ensure that they all work. Note
876 // that when the color type is forced to kAlpha_8_SkColorType, the shader will always
877 // read (0,0,0,1) if the alpha type is kOpaque_SkAlphaType.
878 if (rec.forcedColorType == kUnknown_SkColorType) {
879 gainmapBitmap0 = gainmapBitmap[0];
880 } else {
881 gainmapBitmap0.installPixels(gainmapBitmap[0]
882 .info()
883 .makeColorType(rec.forcedColorType)
884 .makeAlphaType(kPremul_SkAlphaType),
885 gainmapBitmap[0].getPixels(),
886 gainmapBitmap[0].rowBytes());
887 }
888 SkColor4f p0 = render_gainmap_pixel(
889 rec.hdrRatio, baseBitmap[0], gainmapBitmap0, gainmapInfo[0], rec.x, rec.y);
890 SkColor4f p1 = render_gainmap_pixel(
891 rec.hdrRatio, baseBitmap[1], gainmapBitmap[1], gainmapInfo[1], rec.x, rec.y);
892
893 REPORTER_ASSERT(r, approx_eq(p0, p1, kEpsilon));
894 }
895 }
896 }
897
get_mp_image(sk_sp<SkData> imageData,size_t imageNumber)898 static sk_sp<SkData> get_mp_image(sk_sp<SkData> imageData, size_t imageNumber) {
899 SkMemoryStream stream(imageData);
900 auto sourceMgr = SkJpegSourceMgr::Make(&stream);
901
902 std::unique_ptr<SkJpegMultiPictureParameters> mpParams;
903 SkJpegSegment mpParamsSegment;
904 if (!find_mp_params_segment(&stream, &mpParams, &mpParamsSegment)) {
905 return nullptr;
906 }
907 return SkData::MakeSubset(
908 imageData.get(),
909 SkJpegMultiPictureParameters::GetImageAbsoluteOffset(
910 mpParams->images[imageNumber].dataOffset, mpParamsSegment.offset),
911 mpParams->images[imageNumber].size);
912 }
913
get_ifd(sk_sp<SkData> imageData,uint8_t marker,const void * sig,size_t sigSize,size_t pad)914 static std::unique_ptr<SkTiff::ImageFileDirectory> get_ifd(
915 sk_sp<SkData> imageData, uint8_t marker, const void* sig, size_t sigSize, size_t pad) {
916 SkMemoryStream stream(imageData);
917 auto sourceMgr = SkJpegSourceMgr::Make(&stream);
918 for (const auto& segment : sourceMgr->getAllSegments()) {
919 if (segment.marker != marker) {
920 continue;
921 }
922 auto parameterData = sourceMgr->getSegmentParameters(segment);
923 if (!parameterData) {
924 continue;
925 }
926 if (parameterData->size() < sigSize || memcmp(sig, parameterData->data(), sigSize) != 0) {
927 continue;
928 }
929 auto ifdData = SkData::MakeSubset(
930 parameterData.get(), sigSize + pad, parameterData->size() - (sigSize + pad));
931
932 bool littleEndian = false;
933 uint32_t ifdOffset = 0;
934 if (!SkTiff::ImageFileDirectory::ParseHeader(ifdData.get(), &littleEndian, &ifdOffset)) {
935 return nullptr;
936 }
937 return SkTiff::ImageFileDirectory::MakeFromOffset(ifdData, littleEndian, ifdOffset);
938 }
939 return nullptr;
940 }
941
get_mpf_ifd(sk_sp<SkData> imageData)942 static std::unique_ptr<SkTiff::ImageFileDirectory> get_mpf_ifd(sk_sp<SkData> imageData) {
943 return get_ifd(std::move(imageData), kMpfMarker, kMpfSig, sizeof(kMpfSig), 0);
944 }
945
get_exif_ifd(sk_sp<SkData> imageData)946 static std::unique_ptr<SkTiff::ImageFileDirectory> get_exif_ifd(sk_sp<SkData> imageData) {
947 return get_ifd(std::move(imageData), kExifMarker, kExifSig, sizeof(kExifSig), 1);
948 }
949
DEF_TEST(AndroidCodec_mpfParse,r)950 DEF_TEST(AndroidCodec_mpfParse, r) {
951 sk_sp<SkData> inputData = GetResourceAsData("images/iphone_13_pro.jpeg");
952
953 {
954 // The MPF in iPhone images has 3 entries: version, image count, and the MP entries.
955 auto ifd = get_mpf_ifd(inputData);
956 REPORTER_ASSERT(r, ifd);
957 REPORTER_ASSERT(r, ifd->getNumEntries() == 3);
958 REPORTER_ASSERT(r, ifd->getEntryTag(0) == 0xB000);
959 REPORTER_ASSERT(r, ifd->getEntryTag(1) == 0xB001);
960 REPORTER_ASSERT(r, ifd->getEntryTag(2) == 0xB002);
961
962 // There is no attribute IFD.
963 REPORTER_ASSERT(r, !ifd->nextIfdOffset());
964 }
965
966 {
967 // The gainmap images have version and image count.
968 auto ifd = get_mpf_ifd(get_mp_image(inputData, 1));
969 REPORTER_ASSERT(r, ifd);
970
971 REPORTER_ASSERT(r, ifd->getNumEntries() == 2);
972 REPORTER_ASSERT(r, ifd->getEntryTag(0) == 0xB000);
973 REPORTER_ASSERT(r, ifd->getEntryTag(1) == 0xB001);
974 uint32_t value = 0;
975 REPORTER_ASSERT(r, ifd->getEntryUnsignedLong(1, 1, &value));
976 REPORTER_ASSERT(r, value == 3);
977
978 // There is no further IFD.
979 REPORTER_ASSERT(r, !ifd->nextIfdOffset());
980 }
981
982 // Replace |inputData| with its transcoded version.
983 {
984 SkBitmap baseBitmap;
985 SkBitmap gainmapBitmap;
986 SkGainmapInfo gainmapInfo;
987 decode_all(r,
988 std::make_unique<SkMemoryStream>(inputData),
989 baseBitmap,
990 gainmapBitmap,
991 gainmapInfo);
992 gainmapInfo.fType = SkGainmapInfo::Type::kDefault;
993 SkDynamicMemoryWStream encodeStream;
994 bool encodeResult = SkJpegGainmapEncoder::EncodeHDRGM(&encodeStream,
995 baseBitmap.pixmap(),
996 SkJpegEncoder::Options(),
997 gainmapBitmap.pixmap(),
998 SkJpegEncoder::Options(),
999 gainmapInfo);
1000 REPORTER_ASSERT(r, encodeResult);
1001 inputData = encodeStream.detachAsData();
1002 }
1003
1004 {
1005 // Exif should be present and valid.
1006 auto ifd = get_exif_ifd(inputData);
1007 REPORTER_ASSERT(r, ifd);
1008 REPORTER_ASSERT(r, ifd->getNumEntries() == 1);
1009 constexpr uint16_t kSubIFDOffsetTag = 0x8769;
1010 REPORTER_ASSERT(r, ifd->getEntryTag(0) == kSubIFDOffsetTag);
1011 }
1012
1013 {
1014 // The MPF in encoded images has 3 entries: version, image count, and the MP entries.
1015 auto ifd = get_mpf_ifd(inputData);
1016 REPORTER_ASSERT(r, ifd);
1017 REPORTER_ASSERT(r, ifd->getNumEntries() == 3);
1018 REPORTER_ASSERT(r, ifd->getEntryTag(0) == 0xB000);
1019 REPORTER_ASSERT(r, ifd->getEntryTag(1) == 0xB001);
1020 REPORTER_ASSERT(r, ifd->getEntryTag(2) == 0xB002);
1021
1022 // There is no attribute IFD.
1023 REPORTER_ASSERT(r, !ifd->nextIfdOffset());
1024 }
1025
1026 {
1027 // The MPF in encoded gainmap images has 2 entries: Version and number of images.
1028 auto ifd = get_mpf_ifd(get_mp_image(inputData, 1));
1029 REPORTER_ASSERT(r, ifd);
1030
1031 REPORTER_ASSERT(r, ifd->getNumEntries() == 1);
1032 REPORTER_ASSERT(r, ifd->getEntryTag(0) == 0xB000);
1033
1034 // Verify the version data (don't verify the version in the primary image, because if that
1035 // were broken all MPF images would be broken).
1036 sk_sp<SkData> versionData = ifd->getEntryUndefinedData(0);
1037 REPORTER_ASSERT(r, versionData);
1038 REPORTER_ASSERT(r, versionData->bytes()[0] == '0');
1039 REPORTER_ASSERT(r, versionData->bytes()[1] == '1');
1040 REPORTER_ASSERT(r, versionData->bytes()[2] == '0');
1041 REPORTER_ASSERT(r, versionData->bytes()[3] == '0');
1042
1043 // There is no further IFD.
1044 REPORTER_ASSERT(r, !ifd->nextIfdOffset());
1045 }
1046 }
1047
DEF_TEST(AndroidCodec_gainmapInfoParse,r)1048 DEF_TEST(AndroidCodec_gainmapInfoParse, r) {
1049 const uint8_t versionData[] = {
1050 0x00, // Minimum version
1051 0x00,
1052 0x00, // Writer version
1053 0x00,
1054 };
1055 const uint8_t data[] = {
1056 0x00, 0x00, // Minimum version
1057 0x00, 0x00, // Writer version
1058 0xc0, // Flags
1059 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // Base HDR headroom
1060 0x00, 0x01, 0x45, 0x3e, 0x00, 0x00, 0x80, 0x00, // Altr HDR headroom
1061 0xfc, 0x23, 0x05, 0x14, 0x40, 0x00, 0x00, 0x00, // Red: Gainmap min
1062 0x00, 0x01, 0x1f, 0xe1, 0x00, 0x00, 0x80, 0x00, // Red: Gainmap max
1063 0x10, 0x4b, 0x9f, 0x0a, 0x40, 0x00, 0x00, 0x00, // Red: Gamma
1064 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // Red: Base offset
1065 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // Red: Altr offset
1066 0xfd, 0xdb, 0x68, 0x04, 0x40, 0x00, 0x00, 0x00, // Green: Gainmap min
1067 0x00, 0x01, 0x11, 0x68, 0x00, 0x00, 0x80, 0x00, // Green: Gainmap max
1068 0x10, 0x28, 0xf9, 0x53, 0x40, 0x00, 0x00, 0x00, // Green: Gamma
1069 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // Green: Base offset
1070 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // Green: Altr offset
1071 0xf7, 0x16, 0x7b, 0x90, 0x40, 0x00, 0x00, 0x00, // Blue: Gainmap min
1072 0x00, 0x01, 0x0f, 0x9a, 0x00, 0x00, 0x80, 0x00, // Blue: Gainmap max
1073 0x12, 0x95, 0xa8, 0x3f, 0x40, 0x00, 0x00, 0x00, // Blue: Gamma
1074 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // Blue: Base offset
1075 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // Blue: Altr offset
1076 };
1077 SkGainmapInfo kExpectedInfo = {{0.959023f, 0.977058f, 0.907989f, 1.f},
1078 {4.753710f, 4.395375f, 4.352630f, 1.f},
1079 {3.927490f, 3.960382f, 3.443712f, 1.f},
1080 {0.015625f, 0.015625f, 0.015625f, 1.f},
1081 {0.015625f, 0.015625f, 0.015625f, 1.f},
1082 1.000000f,
1083 5.819739f,
1084 SkGainmapInfo::BaseImageType::kSDR,
1085 SkGainmapInfo::Type::kDefault,
1086 nullptr};
1087 SkGainmapInfo kSingleChannelInfo = {{0.1234567e-4f, 0.1234567e-4f, 0.1234567e-4f, 1.f},
1088 {-0.1234567e-4f, -0.1234567e-4f, -0.1234567e-4f, 1.f},
1089 {0.1234567e+0f, 0.1234567e+0f, 0.1234567e+0f, 1.f},
1090 {0.1234567e+4f, 0.1234567e+4f, 0.1234567e+4f, 1.f},
1091 {0.1234567e+4f, 0.1234567e+4f, 0.1234567e+4f, 1.f},
1092 1.,
1093 4.f,
1094 SkGainmapInfo::BaseImageType::kHDR,
1095 SkGainmapInfo::Type::kDefault,
1096 SkColorSpace::MakeSRGB()};
1097
1098 // Verify the version from data.
1099 REPORTER_ASSERT(r,
1100 SkGainmapInfo::ParseVersion(
1101 SkData::MakeWithoutCopy(versionData, sizeof(versionData)).get()));
1102
1103 // Verify the SkGainmapInfo from data.
1104 SkGainmapInfo info;
1105 REPORTER_ASSERT(r,
1106 SkGainmapInfo::Parse(SkData::MakeWithoutCopy(data, sizeof(data)).get(), info));
1107 expect_approx_eq_info(r, info, kExpectedInfo);
1108
1109 // Verify the parsed version.
1110 REPORTER_ASSERT(r, SkGainmapInfo::ParseVersion(SkGainmapInfo::SerializeVersion().get()));
1111
1112 // Verify the round-trip SkGainmapInfo.
1113 auto dataInfo = info.serialize();
1114 SkGainmapInfo infoRoundTrip;
1115 REPORTER_ASSERT(r, SkGainmapInfo::Parse(dataInfo.get(), infoRoundTrip));
1116 expect_approx_eq_info(r, info, infoRoundTrip);
1117
1118 // Serialize a single-channel SkGainmapInfo. The serialized data should be smaller.
1119 auto dataSingleChannelInfo = kSingleChannelInfo.serialize();
1120 REPORTER_ASSERT(r, dataSingleChannelInfo->size() < dataInfo->size());
1121 SkGainmapInfo singleChannelInfoRoundTrip;
1122 REPORTER_ASSERT(r,
1123 SkGainmapInfo::Parse(dataSingleChannelInfo.get(), singleChannelInfoRoundTrip));
1124 expect_approx_eq_info(r, singleChannelInfoRoundTrip, kSingleChannelInfo);
1125 }
1126
1127 #endif // !defined(SK_ENABLE_NDK_IMAGES)
1128