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