• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 stream.
505     std::unique_ptr<SkStream> gainmapStream;
506     REPORTER_ASSERT(r, androidCodec->getAndroidGainmap(&gainmapInfo, &gainmapStream));
507     REPORTER_ASSERT(r, gainmapStream);
508 
509     // Decode the gainmap bitmap.
510     std::unique_ptr<SkCodec> gainmapCodec = SkCodec::MakeFromStream(std::move(gainmapStream));
511     REPORTER_ASSERT(r, gainmapCodec);
512     SkBitmap bm;
513     bm.allocPixels(gainmapCodec->getInfo());
514     gainmapBitmap.allocPixels(gainmapCodec->getInfo());
515     REPORTER_ASSERT(r,
516                     SkCodec::kSuccess == gainmapCodec->getPixels(gainmapBitmap.info(),
517                                                                  gainmapBitmap.getPixels(),
518                                                                  gainmapBitmap.rowBytes()));
519 }
520 
DEF_TEST(AndroidCodec_jpegGainmapDecode,r)521 DEF_TEST(AndroidCodec_jpegGainmapDecode, r) {
522     const struct Rec {
523         const char* path;
524         SkISize dimensions;
525         SkColor originColor;
526         SkColor farCornerColor;
527         SkGainmapInfo info;
528     } recs[] = {
529             {"images/iphone_13_pro.jpeg",
530              SkISize::Make(1512, 2016),
531              0xFF3B3B3B,
532              0xFF101010,
533              {{1.f, 1.f, 1.f, 1.f},
534               {3.482202f, 3.482202f, 3.482202f, 1.f},
535               {1.f, 1.f, 1.f, 1.f},
536               {0.f, 0.f, 0.f, 1.f},
537               {0.f, 0.f, 0.f, 1.f},
538               1.f,
539               3.482202f,
540               SkGainmapInfo::BaseImageType::kSDR,
541               SkGainmapInfo::Type::kApple,
542               nullptr}},
543             {"images/iphone_15.jpeg",
544              SkISize::Make(2016, 1512),
545              0xFF5C5C5C,
546              0xFF656565,
547              {{1.f, 1.f, 1.f, 1.f},
548               {3.755272f, 3.755272f, 3.755272f, 1.f},
549               {1.f, 1.f, 1.f, 1.f},
550               {0.f, 0.f, 0.f, 1.f},
551               {0.f, 0.f, 0.f, 1.f},
552               1.f,
553               3.755272f,
554               SkGainmapInfo::BaseImageType::kSDR,
555               SkGainmapInfo::Type::kApple,
556               nullptr}},
557             {"images/gainmap_gcontainer_only.jpg",
558              SkISize::Make(32, 32),
559              0xffffffff,
560              0xffffffff,
561              {{25.f, 0.5f, 1.f, 1.f},
562               {2.f, 4.f, 8.f, 1.f},
563               {0.5, 1.f, 2.f, 1.f},
564               {0.01f, 0.001f, 0.0001f, 1.f},
565               {0.0001f, 0.001f, 0.01f, 1.f},
566               2.f,
567               4.f,
568               SkGainmapInfo::BaseImageType::kSDR,
569               SkGainmapInfo::Type::kDefault,
570               nullptr}},
571             {"images/gainmap_iso21496_1_adobe_gcontainer.jpg",
572              SkISize::Make(32, 32),
573              0xffffffff,
574              0xff000000,
575              {{25.f, 0.5f, 1.f, 1.f},
576               {2.f, 4.f, 8.f, 1.f},
577               {0.5, 1.f, 2.f, 1.f},
578               {0.01f, 0.001f, 0.0001f, 1.f},
579               {0.0001f, 0.001f, 0.01f, 1.f},
580               2.f,
581               4.f,
582               SkGainmapInfo::BaseImageType::kSDR,
583               SkGainmapInfo::Type::kDefault,
584               nullptr}},
585             {"images/gainmap_iso21496_1.jpg",
586              SkISize::Make(32, 32),
587              0xffffffff,
588              0xff000000,
589              {{25.f, 0.5f, 1.f, 1.f},
590               {2.f, 4.f, 8.f, 1.f},
591               {0.5, 1.f, 2.f, 1.f},
592               {0.01f, 0.001f, 0.0001f, 1.f},
593               {0.0001f, 0.001f, 0.01f, 1.f},
594               2.f,
595               4.f,
596               SkGainmapInfo::BaseImageType::kHDR,
597               SkGainmapInfo::Type::kDefault,
598               SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kRec2020)}},
599     };
600 
601     TestStream::Type kStreamTypes[] = {
602             TestStream::Type::kUnseekable,
603             TestStream::Type::kSeekable,
604             TestStream::Type::kMemoryMapped,
605     };
606     for (const auto& streamType : kStreamTypes) {
607         bool useFileStream = streamType != TestStream::Type::kMemoryMapped;
608         for (const auto& rec : recs) {
609             auto stream = GetResourceAsStream(rec.path, useFileStream);
610             REPORTER_ASSERT(r, stream);
611             auto testStream = std::make_unique<TestStream>(streamType, stream.get());
612 
613             SkBitmap baseBitmap;
614             SkBitmap gainmapBitmap;
615             SkGainmapInfo gainmapInfo;
616             decode_all(r, std::move(testStream), baseBitmap, gainmapBitmap, gainmapInfo);
617 
618             // Spot-check the image size and pixels.
619             REPORTER_ASSERT(r, gainmapBitmap.dimensions() == rec.dimensions);
620             REPORTER_ASSERT(r, gainmapBitmap.getColor(0, 0) == rec.originColor);
621             REPORTER_ASSERT(
622                     r,
623                     gainmapBitmap.getColor(rec.dimensions.fWidth - 1, rec.dimensions.fHeight - 1) ==
624                             rec.farCornerColor);
625 
626             // Verify the gainmap rendering parameters.
627             expect_approx_eq_info(r, rec.info, gainmapInfo);
628         }
629     }
630 }
631 
DEF_TEST(AndroidCodec_jpegNoGainmap,r)632 DEF_TEST(AndroidCodec_jpegNoGainmap, r) {
633     // This test image has a large APP16 segment that will stress the various SkJpegSourceMgrs'
634     // data skipping paths.
635     const char* path = "images/icc-v2-gbr.jpg";
636 
637     TestStream::Type kStreamTypes[] = {
638             TestStream::Type::kUnseekable,
639             TestStream::Type::kSeekable,
640             TestStream::Type::kMemoryMapped,
641     };
642     for (const auto& streamType : kStreamTypes) {
643         bool useFileStream = streamType != TestStream::Type::kMemoryMapped;
644         auto stream = GetResourceAsStream(path, useFileStream);
645         REPORTER_ASSERT(r, stream);
646         auto testStream = std::make_unique<TestStream>(streamType, stream.get());
647 
648         // Decode the base bitmap.
649         SkCodec::Result result = SkCodec::kSuccess;
650         std::unique_ptr<SkCodec> baseCodec =
651                 SkJpegCodec::MakeFromStream(std::move(testStream), &result);
652         REPORTER_ASSERT(r, baseCodec);
653         SkBitmap baseBitmap;
654         baseBitmap.allocPixels(baseCodec->getInfo());
655         REPORTER_ASSERT(r,
656                         SkCodec::kSuccess == baseCodec->getPixels(baseBitmap.info(),
657                                                                   baseBitmap.getPixels(),
658                                                                   baseBitmap.rowBytes()));
659 
660         std::unique_ptr<SkAndroidCodec> androidCodec =
661                 SkAndroidCodec::MakeFromCodec(std::move(baseCodec));
662         REPORTER_ASSERT(r, androidCodec);
663 
664         // Try to extract the gainmap info and stream. It should fail.
665         SkGainmapInfo gainmapInfo;
666         std::unique_ptr<SkStream> gainmapStream;
667         REPORTER_ASSERT(r, !androidCodec->getAndroidGainmap(&gainmapInfo, &gainmapStream));
668     }
669 }
670 
671 #if !defined(SK_ENABLE_NDK_IMAGES)
672 
DEF_TEST(AndroidCodec_gainmapInfoEncode,r)673 DEF_TEST(AndroidCodec_gainmapInfoEncode, r) {
674     SkDynamicMemoryWStream encodeStream;
675 
676     constexpr size_t kNumTests = 4;
677 
678     SkBitmap baseBitmap;
679     baseBitmap.allocPixels(SkImageInfo::MakeN32Premul(16, 16));
680 
681     SkBitmap gainmapBitmaps[kNumTests];
682     gainmapBitmaps[0].allocPixels(SkImageInfo::MakeN32Premul(16, 16));
683     gainmapBitmaps[1].allocPixels(SkImageInfo::MakeN32Premul(8, 8));
684     gainmapBitmaps[2].allocPixels(
685             SkImageInfo::Make(4, 4, kAlpha_8_SkColorType, kPremul_SkAlphaType));
686     gainmapBitmaps[3].allocPixels(
687             SkImageInfo::Make(8, 8, kGray_8_SkColorType, kPremul_SkAlphaType));
688 
689     SkGainmapInfo infos[kNumTests] = {
690             // Multi-channel, UltraHDR-compatible.
691             {{1.f, 2.f, 4.f, 1.f},
692              {8.f, 16.f, 32.f, 1.f},
693              {64.f, 128.f, 256.f, 1.f},
694              {1 / 10.f, 1 / 11.f, 1 / 12.f, 1.f},
695              {1 / 13.f, 1 / 14.f, 1 / 15.f, 1.f},
696              4.f,
697              32.f,
698              SkGainmapInfo::BaseImageType::kSDR,
699              SkGainmapInfo::Type::kDefault,
700              nullptr},
701             // Multi-channel, not UltraHDR-compatible.
702             {{1.f, 2.f, 4.f, 1.f},
703              {8.f, 16.f, 32.f, 1.f},
704              {64.f, 128.f, 256.f, 1.f},
705              {1 / 10.f, 1 / 11.f, 1 / 12.f, 1.f},
706              {1 / 13.f, 1 / 14.f, 1 / 15.f, 1.f},
707              4.f,
708              32.f,
709              SkGainmapInfo::BaseImageType::kSDR,
710              SkGainmapInfo::Type::kDefault,
711              SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3)},
712             // Single-channel, UltraHDR-compatible.
713             {{1.f, 1.f, 1.f, 1.f},
714              {8.f, 8.f, 8.f, 1.f},
715              {64.f, 64.f, 64.f, 1.f},
716              {1 / 128.f, 1 / 128.f, 1 / 128.f, 1.f},
717              {1 / 256.f, 1 / 256.f, 1 / 256.f, 1.f},
718              4.f,
719              32.f,
720              SkGainmapInfo::BaseImageType::kSDR,
721              SkGainmapInfo::Type::kDefault,
722              nullptr},
723             // Single-channel, not UltraHDR-compatible.
724             {{1.f, 1.f, 1.f, 1.f},
725              {8.f, 8.f, 8.f, 1.f},
726              {64.f, 64.f, 64.f, 1.f},
727              {1 / 128.f, 1 / 128.f, 1 / 128.f, 1.f},
728              {1 / 256.f, 1 / 256.f, 1 / 256.f, 1.f},
729              4.f,
730              32.f,
731              SkGainmapInfo::BaseImageType::kHDR,
732              SkGainmapInfo::Type::kDefault,
733              SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3)},
734     };
735 
736     for (size_t i = 0; i < kNumTests; ++i) {
737         // Encode |gainmapInfo|.
738         bool encodeResult = SkJpegGainmapEncoder::EncodeHDRGM(&encodeStream,
739                                                               baseBitmap.pixmap(),
740                                                               SkJpegEncoder::Options(),
741                                                               gainmapBitmaps[i].pixmap(),
742                                                               SkJpegEncoder::Options(),
743                                                               infos[i]);
744         REPORTER_ASSERT(r, encodeResult);
745 
746         // Decode into |decodedGainmapInfo|.
747         SkGainmapInfo decodedGainmapInfo;
748         SkBitmap decodedBaseBitmap;
749         SkBitmap decodedGainmapBitmap;
750         auto decodeStream = std::make_unique<SkMemoryStream>(encodeStream.detachAsData());
751         decode_all(r,
752                    std::move(decodeStream),
753                    decodedBaseBitmap,
754                    decodedGainmapBitmap,
755                    decodedGainmapInfo);
756 
757         // Verify that the decode reproducd the input.
758         expect_approx_eq_info(r, infos[i], decodedGainmapInfo);
759     }
760 }
761 
762 // Render an applied gainmap.
render_gainmap(const SkImageInfo & renderInfo,float renderHdrRatio,const SkBitmap & baseBitmap,const SkBitmap & gainmapBitmap,const SkGainmapInfo & gainmapInfo,int x,int y)763 static SkBitmap render_gainmap(const SkImageInfo& renderInfo,
764                                float renderHdrRatio,
765                                const SkBitmap& baseBitmap,
766                                const SkBitmap& gainmapBitmap,
767                                const SkGainmapInfo& gainmapInfo,
768                                int x,
769                                int y) {
770     SkRect baseRect = SkRect::MakeXYWH(x, y, renderInfo.width(), renderInfo.height());
771 
772     float scaleX = gainmapBitmap.width() / static_cast<float>(baseBitmap.width());
773     float scaleY = gainmapBitmap.height() / static_cast<float>(baseBitmap.height());
774     SkRect gainmapRect = SkRect::MakeXYWH(baseRect.x() * scaleX,
775                                           baseRect.y() * scaleY,
776                                           baseRect.width() * scaleX,
777                                           baseRect.height() * scaleY);
778 
779     SkRect dstRect = SkRect::Make(renderInfo.dimensions());
780 
781     sk_sp<SkImage> baseImage = SkImages::RasterFromBitmap(baseBitmap);
782     sk_sp<SkImage> gainmapImage = SkImages::RasterFromBitmap(gainmapBitmap);
783     sk_sp<SkShader> shader = SkGainmapShader::Make(baseImage,
784                                                    baseRect,
785                                                    SkSamplingOptions(),
786                                                    gainmapImage,
787                                                    gainmapRect,
788                                                    SkSamplingOptions(),
789                                                    gainmapInfo,
790                                                    dstRect,
791                                                    renderHdrRatio,
792                                                    renderInfo.refColorSpace());
793 
794     SkBitmap result;
795     result.allocPixels(renderInfo);
796     result.eraseColor(SK_ColorTRANSPARENT);
797     SkCanvas canvas(result);
798 
799     SkPaint paint;
800     paint.setShader(shader);
801     canvas.drawRect(dstRect, paint);
802 
803     return result;
804 }
805 
806 // 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)807 static SkColor4f render_gainmap_pixel(float renderHdrRatio,
808                                       const SkBitmap& baseBitmap,
809                                       const SkBitmap& gainmapBitmap,
810                                       const SkGainmapInfo& gainmapInfo,
811                                       int x,
812                                       int y) {
813     SkImageInfo testPixelInfo = SkImageInfo::Make(
814             /*width=*/1,
815             /*height=*/1,
816             kRGBA_F16_SkColorType,
817             kPremul_SkAlphaType,
818             SkColorSpace::MakeSRGB());
819     SkBitmap testPixelBitmap = render_gainmap(
820             testPixelInfo, renderHdrRatio, baseBitmap, gainmapBitmap, gainmapInfo, x, y);
821     return testPixelBitmap.getColor4f(0, 0);
822 }
823 
DEF_TEST(AndroidCodec_jpegGainmapTranscode,r)824 DEF_TEST(AndroidCodec_jpegGainmapTranscode, r) {
825     const char* path = "images/iphone_13_pro.jpeg";
826     SkBitmap baseBitmap[2];
827     SkBitmap gainmapBitmap[2];
828     SkGainmapInfo gainmapInfo[2];
829 
830     // Decode an MPF-based gainmap image.
831     decode_all(r, GetResourceAsStream(path), baseBitmap[0], gainmapBitmap[0], gainmapInfo[0]);
832 
833     // This test was written before SkGainmapShader added support for kApple type. Strip the
834     // type out.
835     gainmapInfo[0].fType = SkGainmapInfo::Type::kDefault;
836 
837     constexpr float kEpsilon = 1e-2f;
838     {
839         SkDynamicMemoryWStream encodeStream;
840 
841         // Transcode to UltraHDR.
842         bool encodeResult = SkJpegGainmapEncoder::EncodeHDRGM(&encodeStream,
843                                                               baseBitmap[0].pixmap(),
844                                                               SkJpegEncoder::Options(),
845                                                               gainmapBitmap[0].pixmap(),
846                                                               SkJpegEncoder::Options(),
847                                                               gainmapInfo[0]);
848         REPORTER_ASSERT(r, encodeResult);
849         auto encodeData = encodeStream.detachAsData();
850 
851         // Decode the just-encoded image.
852         auto decodeStream = std::make_unique<SkMemoryStream>(encodeData);
853         decode_all(r, std::move(decodeStream), baseBitmap[1], gainmapBitmap[1], gainmapInfo[1]);
854 
855         // HDRGM will have the same rendering parameters.
856         expect_approx_eq_info(r, gainmapInfo[0], gainmapInfo[1]);
857 
858         // Render a few pixels and verify that they come out the same. Rendering requires SkSL.
859         const struct Rec {
860             int x;
861             int y;
862             float hdrRatio;
863             SkColor4f expectedColor;
864             SkColorType forcedColorType;
865         } recs[] = {
866                 {1446, 1603, 1.05f, {0.984375f, 1.004883f, 1.008789f, 1.f}, kUnknown_SkColorType},
867                 {1446, 1603, 100.f, {1.147461f, 1.170898f, 1.174805f, 1.f}, kUnknown_SkColorType},
868                 {1446, 1603, 100.f, {1.147461f, 1.170898f, 1.174805f, 1.f}, kGray_8_SkColorType},
869                 {1446, 1603, 100.f, {1.147461f, 1.170898f, 1.174805f, 1.f}, kAlpha_8_SkColorType},
870                 {1446, 1603, 100.f, {1.147461f, 1.170898f, 1.174805f, 1.f}, kR8_unorm_SkColorType},
871         };
872 
873         for (const auto& rec : recs) {
874             SkBitmap gainmapBitmap0;
875             SkASSERT(gainmapBitmap[0].colorType() == kGray_8_SkColorType);
876 
877             // Force various different single-channel formats, to ensure that they all work. Note
878             // that when the color type is forced to kAlpha_8_SkColorType, the shader will always
879             // read (0,0,0,1) if the alpha type is kOpaque_SkAlphaType.
880             if (rec.forcedColorType == kUnknown_SkColorType) {
881                 gainmapBitmap0 = gainmapBitmap[0];
882             } else {
883                 gainmapBitmap0.installPixels(gainmapBitmap[0]
884                                                      .info()
885                                                      .makeColorType(rec.forcedColorType)
886                                                      .makeAlphaType(kPremul_SkAlphaType),
887                                              gainmapBitmap[0].getPixels(),
888                                              gainmapBitmap[0].rowBytes());
889             }
890             SkColor4f p0 = render_gainmap_pixel(
891                     rec.hdrRatio, baseBitmap[0], gainmapBitmap0, gainmapInfo[0], rec.x, rec.y);
892             SkColor4f p1 = render_gainmap_pixel(
893                     rec.hdrRatio, baseBitmap[1], gainmapBitmap[1], gainmapInfo[1], rec.x, rec.y);
894 
895             REPORTER_ASSERT(r, approx_eq(p0, p1, kEpsilon));
896         }
897     }
898 }
899 
get_mp_image(sk_sp<SkData> imageData,size_t imageNumber)900 static sk_sp<SkData> get_mp_image(sk_sp<SkData> imageData, size_t imageNumber) {
901     SkMemoryStream stream(imageData);
902     auto sourceMgr = SkJpegSourceMgr::Make(&stream);
903 
904     std::unique_ptr<SkJpegMultiPictureParameters> mpParams;
905     SkJpegSegment mpParamsSegment;
906     if (!find_mp_params_segment(&stream, &mpParams, &mpParamsSegment)) {
907         return nullptr;
908     }
909     return SkData::MakeSubset(
910             imageData.get(),
911             SkJpegMultiPictureParameters::GetImageAbsoluteOffset(
912                     mpParams->images[imageNumber].dataOffset, mpParamsSegment.offset),
913             mpParams->images[imageNumber].size);
914 }
915 
get_ifd(sk_sp<SkData> imageData,uint8_t marker,const void * sig,size_t sigSize,size_t pad)916 static std::unique_ptr<SkTiff::ImageFileDirectory> get_ifd(
917         sk_sp<SkData> imageData, uint8_t marker, const void* sig, size_t sigSize, size_t pad) {
918     SkMemoryStream stream(imageData);
919     auto sourceMgr = SkJpegSourceMgr::Make(&stream);
920     for (const auto& segment : sourceMgr->getAllSegments()) {
921         if (segment.marker != marker) {
922             continue;
923         }
924         auto parameterData = sourceMgr->getSegmentParameters(segment);
925         if (!parameterData) {
926             continue;
927         }
928         if (parameterData->size() < sigSize || memcmp(sig, parameterData->data(), sigSize) != 0) {
929             continue;
930         }
931         auto ifdData = SkData::MakeSubset(
932                 parameterData.get(), sigSize + pad, parameterData->size() - (sigSize + pad));
933 
934         bool littleEndian = false;
935         uint32_t ifdOffset = 0;
936         if (!SkTiff::ImageFileDirectory::ParseHeader(ifdData.get(), &littleEndian, &ifdOffset)) {
937             return nullptr;
938         }
939         return SkTiff::ImageFileDirectory::MakeFromOffset(ifdData, littleEndian, ifdOffset);
940     }
941     return nullptr;
942 }
943 
get_mpf_ifd(sk_sp<SkData> imageData)944 static std::unique_ptr<SkTiff::ImageFileDirectory> get_mpf_ifd(sk_sp<SkData> imageData) {
945     return get_ifd(std::move(imageData), kMpfMarker, kMpfSig, sizeof(kMpfSig), 0);
946 }
947 
get_exif_ifd(sk_sp<SkData> imageData)948 static std::unique_ptr<SkTiff::ImageFileDirectory> get_exif_ifd(sk_sp<SkData> imageData) {
949     return get_ifd(std::move(imageData), kExifMarker, kExifSig, sizeof(kExifSig), 1);
950 }
951 
DEF_TEST(AndroidCodec_mpfParse,r)952 DEF_TEST(AndroidCodec_mpfParse, r) {
953     sk_sp<SkData> inputData = GetResourceAsData("images/iphone_13_pro.jpeg");
954 
955     {
956         // The MPF in iPhone images has 3 entries: version, image count, and the MP entries.
957         auto ifd = get_mpf_ifd(inputData);
958         REPORTER_ASSERT(r, ifd);
959         REPORTER_ASSERT(r, ifd->getNumEntries() == 3);
960         REPORTER_ASSERT(r, ifd->getEntryTag(0) == 0xB000);
961         REPORTER_ASSERT(r, ifd->getEntryTag(1) == 0xB001);
962         REPORTER_ASSERT(r, ifd->getEntryTag(2) == 0xB002);
963 
964         // There is no attribute IFD.
965         REPORTER_ASSERT(r, !ifd->nextIfdOffset());
966     }
967 
968     {
969         // The gainmap images have version and image count.
970         auto ifd = get_mpf_ifd(get_mp_image(inputData, 1));
971         REPORTER_ASSERT(r, ifd);
972 
973         REPORTER_ASSERT(r, ifd->getNumEntries() == 2);
974         REPORTER_ASSERT(r, ifd->getEntryTag(0) == 0xB000);
975         REPORTER_ASSERT(r, ifd->getEntryTag(1) == 0xB001);
976         uint32_t value = 0;
977         REPORTER_ASSERT(r, ifd->getEntryUnsignedLong(1, 1, &value));
978         REPORTER_ASSERT(r, value == 3);
979 
980         // There is no further IFD.
981         REPORTER_ASSERT(r, !ifd->nextIfdOffset());
982     }
983 
984     // Replace |inputData| with its transcoded version.
985     {
986         SkBitmap baseBitmap;
987         SkBitmap gainmapBitmap;
988         SkGainmapInfo gainmapInfo;
989         decode_all(r,
990                    std::make_unique<SkMemoryStream>(inputData),
991                    baseBitmap,
992                    gainmapBitmap,
993                    gainmapInfo);
994         gainmapInfo.fType = SkGainmapInfo::Type::kDefault;
995         SkDynamicMemoryWStream encodeStream;
996         bool encodeResult = SkJpegGainmapEncoder::EncodeHDRGM(&encodeStream,
997                                                               baseBitmap.pixmap(),
998                                                               SkJpegEncoder::Options(),
999                                                               gainmapBitmap.pixmap(),
1000                                                               SkJpegEncoder::Options(),
1001                                                               gainmapInfo);
1002         REPORTER_ASSERT(r, encodeResult);
1003         inputData = encodeStream.detachAsData();
1004     }
1005 
1006     {
1007         // Exif should be present and valid.
1008         auto ifd = get_exif_ifd(inputData);
1009         REPORTER_ASSERT(r, ifd);
1010         REPORTER_ASSERT(r, ifd->getNumEntries() == 1);
1011         constexpr uint16_t kSubIFDOffsetTag = 0x8769;
1012         REPORTER_ASSERT(r, ifd->getEntryTag(0) == kSubIFDOffsetTag);
1013     }
1014 
1015     {
1016         // The MPF in encoded images has 3 entries: version, image count, and the MP entries.
1017         auto ifd = get_mpf_ifd(inputData);
1018         REPORTER_ASSERT(r, ifd);
1019         REPORTER_ASSERT(r, ifd->getNumEntries() == 3);
1020         REPORTER_ASSERT(r, ifd->getEntryTag(0) == 0xB000);
1021         REPORTER_ASSERT(r, ifd->getEntryTag(1) == 0xB001);
1022         REPORTER_ASSERT(r, ifd->getEntryTag(2) == 0xB002);
1023 
1024         // There is no attribute IFD.
1025         REPORTER_ASSERT(r, !ifd->nextIfdOffset());
1026     }
1027 
1028     {
1029         // The MPF in encoded gainmap images has 2 entries: Version and number of images.
1030         auto ifd = get_mpf_ifd(get_mp_image(inputData, 1));
1031         REPORTER_ASSERT(r, ifd);
1032 
1033         REPORTER_ASSERT(r, ifd->getNumEntries() == 1);
1034         REPORTER_ASSERT(r, ifd->getEntryTag(0) == 0xB000);
1035 
1036         // Verify the version data (don't verify the version in the primary image, because if that
1037         // were broken all MPF images would be broken).
1038         sk_sp<SkData> versionData = ifd->getEntryUndefinedData(0);
1039         REPORTER_ASSERT(r, versionData);
1040         REPORTER_ASSERT(r, versionData->bytes()[0] == '0');
1041         REPORTER_ASSERT(r, versionData->bytes()[1] == '1');
1042         REPORTER_ASSERT(r, versionData->bytes()[2] == '0');
1043         REPORTER_ASSERT(r, versionData->bytes()[3] == '0');
1044 
1045         // There is no further IFD.
1046         REPORTER_ASSERT(r, !ifd->nextIfdOffset());
1047     }
1048 }
1049 
DEF_TEST(AndroidCodec_gainmapInfoParse,r)1050 DEF_TEST(AndroidCodec_gainmapInfoParse, r) {
1051     const uint8_t versionData[] = {
1052             0x00,  // Minimum version
1053             0x00,
1054             0x00,  // Writer version
1055             0x00,
1056     };
1057     const uint8_t data[] = {
1058             0x00, 0x00,                                      // Minimum version
1059             0x00, 0x00,                                      // Writer version
1060             0xc0,                                            // Flags
1061             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,  // Base HDR headroom
1062             0x00, 0x01, 0x45, 0x3e, 0x00, 0x00, 0x80, 0x00,  // Altr HDR headroom
1063             0xfc, 0x23, 0x05, 0x14, 0x40, 0x00, 0x00, 0x00,  // Red: Gainmap min
1064             0x00, 0x01, 0x1f, 0xe1, 0x00, 0x00, 0x80, 0x00,  // Red: Gainmap max
1065             0x10, 0x4b, 0x9f, 0x0a, 0x40, 0x00, 0x00, 0x00,  // Red: Gamma
1066             0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,  // Red: Base offset
1067             0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,  // Red: Altr offset
1068             0xfd, 0xdb, 0x68, 0x04, 0x40, 0x00, 0x00, 0x00,  // Green: Gainmap min
1069             0x00, 0x01, 0x11, 0x68, 0x00, 0x00, 0x80, 0x00,  // Green: Gainmap max
1070             0x10, 0x28, 0xf9, 0x53, 0x40, 0x00, 0x00, 0x00,  // Green: Gamma
1071             0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,  // Green: Base offset
1072             0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,  // Green: Altr offset
1073             0xf7, 0x16, 0x7b, 0x90, 0x40, 0x00, 0x00, 0x00,  // Blue: Gainmap min
1074             0x00, 0x01, 0x0f, 0x9a, 0x00, 0x00, 0x80, 0x00,  // Blue: Gainmap max
1075             0x12, 0x95, 0xa8, 0x3f, 0x40, 0x00, 0x00, 0x00,  // Blue: Gamma
1076             0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,  // Blue: Base offset
1077             0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,  // Blue: Altr offset
1078     };
1079     SkGainmapInfo kExpectedInfo = {{0.959023f, 0.977058f, 0.907989f, 1.f},
1080                                    {4.753710f, 4.395375f, 4.352630f, 1.f},
1081                                    {3.927490f, 3.960382f, 3.443712f, 1.f},
1082                                    {0.015625f, 0.015625f, 0.015625f, 1.f},
1083                                    {0.015625f, 0.015625f, 0.015625f, 1.f},
1084                                    1.000000f,
1085                                    5.819739f,
1086                                    SkGainmapInfo::BaseImageType::kSDR,
1087                                    SkGainmapInfo::Type::kDefault,
1088                                    nullptr};
1089     SkGainmapInfo kSingleChannelInfo = {{0.1234567e-4f, 0.1234567e-4f, 0.1234567e-4f, 1.f},
1090                                         {-0.1234567e-4f, -0.1234567e-4f, -0.1234567e-4f, 1.f},
1091                                         {0.1234567e+0f, 0.1234567e+0f, 0.1234567e+0f, 1.f},
1092                                         {0.1234567e+4f, 0.1234567e+4f, 0.1234567e+4f, 1.f},
1093                                         {0.1234567e+4f, 0.1234567e+4f, 0.1234567e+4f, 1.f},
1094                                         1.,
1095                                         4.f,
1096                                         SkGainmapInfo::BaseImageType::kHDR,
1097                                         SkGainmapInfo::Type::kDefault,
1098                                         SkColorSpace::MakeSRGB()};
1099 
1100     // Verify the version from data.
1101     REPORTER_ASSERT(r,
1102                     SkGainmapInfo::ParseVersion(
1103                             SkData::MakeWithoutCopy(versionData, sizeof(versionData)).get()));
1104 
1105     // Verify the SkGainmapInfo from data.
1106     SkGainmapInfo info;
1107     REPORTER_ASSERT(r,
1108                     SkGainmapInfo::Parse(SkData::MakeWithoutCopy(data, sizeof(data)).get(), info));
1109     expect_approx_eq_info(r, info, kExpectedInfo);
1110 
1111     // Verify the parsed version.
1112     REPORTER_ASSERT(r, SkGainmapInfo::ParseVersion(SkGainmapInfo::SerializeVersion().get()));
1113 
1114     // Verify the round-trip SkGainmapInfo.
1115     auto dataInfo = info.serialize();
1116     SkGainmapInfo infoRoundTrip;
1117     REPORTER_ASSERT(r, SkGainmapInfo::Parse(dataInfo.get(), infoRoundTrip));
1118     expect_approx_eq_info(r, info, infoRoundTrip);
1119 
1120     // Serialize a single-channel SkGainmapInfo. The serialized data should be smaller.
1121     auto dataSingleChannelInfo = kSingleChannelInfo.serialize();
1122     REPORTER_ASSERT(r, dataSingleChannelInfo->size() < dataInfo->size());
1123     SkGainmapInfo singleChannelInfoRoundTrip;
1124     REPORTER_ASSERT(r,
1125                     SkGainmapInfo::Parse(dataSingleChannelInfo.get(), singleChannelInfoRoundTrip));
1126     expect_approx_eq_info(r, singleChannelInfoRoundTrip, kSingleChannelInfo);
1127 }
1128 
1129 #endif  // !defined(SK_ENABLE_NDK_IMAGES)
1130