xref: /aosp_15_r20/external/skia/tests/JpegGainmapTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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