xref: /aosp_15_r20/external/skia/tests/CodecTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2015 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/codec/SkEncodedImageFormat.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkGifDecoder.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkJpegDecoder.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkPngChunkReader.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkDataTable.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageGenerator.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixmap.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkJpegEncoder.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkPngEncoder.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkWebpEncoder.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAlign.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skcms/skcms.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkAutoMalloc.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkCodecImageGenerator.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkColorSpacePriv.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMD5.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkStreamPriv.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "tests/FakeStreams.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "tools/DecodeUtils.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Resources.h"
50*c8dee2aaSAndroid Build Coastguard Worker #include "tools/ToolUtils.h"
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_ENABLE_ANDROID_UTILS
53*c8dee2aaSAndroid Build Coastguard Worker #include "client_utils/android/FrontBufferedStream.h"
54*c8dee2aaSAndroid Build Coastguard Worker #endif
55*c8dee2aaSAndroid Build Coastguard Worker 
56*c8dee2aaSAndroid Build Coastguard Worker #include <png.h>
57*c8dee2aaSAndroid Build Coastguard Worker #include <pngconf.h>
58*c8dee2aaSAndroid Build Coastguard Worker #include <setjmp.h>
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
61*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
62*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
63*c8dee2aaSAndroid Build Coastguard Worker #include <initializer_list>
64*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
65*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
66*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker #if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 5
71*c8dee2aaSAndroid Build Coastguard Worker     // FIXME (scroggo): Google3 needs to be updated to use a newer version of libpng. In
72*c8dee2aaSAndroid Build Coastguard Worker     // the meantime, we had to break some pieces of SkPngCodec in order to support Google3.
73*c8dee2aaSAndroid Build Coastguard Worker     // The parts that are broken are likely not used by Google3.
74*c8dee2aaSAndroid Build Coastguard Worker     #define SK_PNG_DISABLE_TESTS
75*c8dee2aaSAndroid Build Coastguard Worker #endif
76*c8dee2aaSAndroid Build Coastguard Worker 
md5(const SkBitmap & bm)77*c8dee2aaSAndroid Build Coastguard Worker static SkMD5::Digest md5(const SkBitmap& bm) {
78*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(bm.getPixels());
79*c8dee2aaSAndroid Build Coastguard Worker     SkMD5 md5;
80*c8dee2aaSAndroid Build Coastguard Worker     size_t rowLen = bm.info().bytesPerPixel() * bm.width();
81*c8dee2aaSAndroid Build Coastguard Worker     for (int y = 0; y < bm.height(); ++y) {
82*c8dee2aaSAndroid Build Coastguard Worker         md5.write(bm.getAddr(0, y), rowLen);
83*c8dee2aaSAndroid Build Coastguard Worker     }
84*c8dee2aaSAndroid Build Coastguard Worker     return md5.finish();
85*c8dee2aaSAndroid Build Coastguard Worker }
86*c8dee2aaSAndroid Build Coastguard Worker 
87*c8dee2aaSAndroid Build Coastguard Worker /**
88*c8dee2aaSAndroid Build Coastguard Worker  *  Compute the digest for bm and compare it to a known good digest.
89*c8dee2aaSAndroid Build Coastguard Worker  *  @param r Reporter to assert that bm's digest matches goodDigest.
90*c8dee2aaSAndroid Build Coastguard Worker  *  @param goodDigest The known good digest to compare to.
91*c8dee2aaSAndroid Build Coastguard Worker  *  @param bm The bitmap to test.
92*c8dee2aaSAndroid Build Coastguard Worker  */
compare_to_good_digest(skiatest::Reporter * r,const SkMD5::Digest & goodDigest,const SkBitmap & bm)93*c8dee2aaSAndroid Build Coastguard Worker static void compare_to_good_digest(skiatest::Reporter* r, const SkMD5::Digest& goodDigest,
94*c8dee2aaSAndroid Build Coastguard Worker                            const SkBitmap& bm) {
95*c8dee2aaSAndroid Build Coastguard Worker     SkMD5::Digest digest = md5(bm);
96*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, digest == goodDigest);
97*c8dee2aaSAndroid Build Coastguard Worker }
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker /**
100*c8dee2aaSAndroid Build Coastguard Worker  *  Test decoding an SkCodec to a particular SkImageInfo.
101*c8dee2aaSAndroid Build Coastguard Worker  *
102*c8dee2aaSAndroid Build Coastguard Worker  *  Calling getPixels(info) should return expectedResult, and if goodDigest is non nullptr,
103*c8dee2aaSAndroid Build Coastguard Worker  *  the resulting decode should match.
104*c8dee2aaSAndroid Build Coastguard Worker  */
105*c8dee2aaSAndroid Build Coastguard Worker template<typename Codec>
test_info(skiatest::Reporter * r,Codec * codec,const SkImageInfo & info,SkCodec::Result expectedResult,const SkMD5::Digest * goodDigest)106*c8dee2aaSAndroid Build Coastguard Worker static void test_info(skiatest::Reporter* r, Codec* codec, const SkImageInfo& info,
107*c8dee2aaSAndroid Build Coastguard Worker                       SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) {
108*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
109*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
112*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == expectedResult);
113*c8dee2aaSAndroid Build Coastguard Worker 
114*c8dee2aaSAndroid Build Coastguard Worker     if (goodDigest) {
115*c8dee2aaSAndroid Build Coastguard Worker         compare_to_good_digest(r, *goodDigest, bm);
116*c8dee2aaSAndroid Build Coastguard Worker     }
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker 
generate_random_subset(SkRandom * rand,int w,int h)119*c8dee2aaSAndroid Build Coastguard Worker SkIRect generate_random_subset(SkRandom* rand, int w, int h) {
120*c8dee2aaSAndroid Build Coastguard Worker     SkIRect rect;
121*c8dee2aaSAndroid Build Coastguard Worker     do {
122*c8dee2aaSAndroid Build Coastguard Worker         rect.fLeft = rand->nextRangeU(0, w);
123*c8dee2aaSAndroid Build Coastguard Worker         rect.fTop = rand->nextRangeU(0, h);
124*c8dee2aaSAndroid Build Coastguard Worker         rect.fRight = rand->nextRangeU(0, w);
125*c8dee2aaSAndroid Build Coastguard Worker         rect.fBottom = rand->nextRangeU(0, h);
126*c8dee2aaSAndroid Build Coastguard Worker         rect.sort();
127*c8dee2aaSAndroid Build Coastguard Worker     } while (rect.isEmpty());
128*c8dee2aaSAndroid Build Coastguard Worker     return rect;
129*c8dee2aaSAndroid Build Coastguard Worker }
130*c8dee2aaSAndroid Build Coastguard Worker 
test_incremental_decode(skiatest::Reporter * r,SkCodec * codec,const SkImageInfo & info,const SkMD5::Digest & goodDigest)131*c8dee2aaSAndroid Build Coastguard Worker static void test_incremental_decode(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info,
132*c8dee2aaSAndroid Build Coastguard Worker         const SkMD5::Digest& goodDigest) {
133*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
134*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
135*c8dee2aaSAndroid Build Coastguard Worker 
136*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == codec->startIncrementalDecode(info, bm.getPixels(),
137*c8dee2aaSAndroid Build Coastguard Worker                                                                           bm.rowBytes()));
138*c8dee2aaSAndroid Build Coastguard Worker 
139*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == codec->incrementalDecode());
140*c8dee2aaSAndroid Build Coastguard Worker 
141*c8dee2aaSAndroid Build Coastguard Worker     compare_to_good_digest(r, goodDigest, bm);
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker // Test in stripes, similar to DM's kStripe_Mode
test_in_stripes(skiatest::Reporter * r,SkCodec * codec,const SkImageInfo & info,const SkMD5::Digest & goodDigest)145*c8dee2aaSAndroid Build Coastguard Worker static void test_in_stripes(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info,
146*c8dee2aaSAndroid Build Coastguard Worker                             const SkMD5::Digest& goodDigest) {
147*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
148*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
149*c8dee2aaSAndroid Build Coastguard Worker     bm.eraseColor(SK_ColorYELLOW);
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker     const int height = info.height();
152*c8dee2aaSAndroid Build Coastguard Worker     // Note that if numStripes does not evenly divide height there will be an extra
153*c8dee2aaSAndroid Build Coastguard Worker     // stripe.
154*c8dee2aaSAndroid Build Coastguard Worker     const int numStripes = 4;
155*c8dee2aaSAndroid Build Coastguard Worker 
156*c8dee2aaSAndroid Build Coastguard Worker     if (numStripes > height) {
157*c8dee2aaSAndroid Build Coastguard Worker         // Image is too small.
158*c8dee2aaSAndroid Build Coastguard Worker         return;
159*c8dee2aaSAndroid Build Coastguard Worker     }
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker     const int stripeHeight = height / numStripes;
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker     // Iterate through the image twice. Once to decode odd stripes, and once for even.
164*c8dee2aaSAndroid Build Coastguard Worker     for (int oddEven = 1; oddEven >= 0; oddEven--) {
165*c8dee2aaSAndroid Build Coastguard Worker         for (int y = oddEven * stripeHeight; y < height; y += 2 * stripeHeight) {
166*c8dee2aaSAndroid Build Coastguard Worker             SkIRect subset = SkIRect::MakeLTRB(0, y, info.width(),
167*c8dee2aaSAndroid Build Coastguard Worker                                                std::min(y + stripeHeight, height));
168*c8dee2aaSAndroid Build Coastguard Worker             SkCodec::Options options;
169*c8dee2aaSAndroid Build Coastguard Worker             options.fSubset = &subset;
170*c8dee2aaSAndroid Build Coastguard Worker             if (SkCodec::kSuccess != codec->startIncrementalDecode(info, bm.getAddr(0, y),
171*c8dee2aaSAndroid Build Coastguard Worker                         bm.rowBytes(), &options)) {
172*c8dee2aaSAndroid Build Coastguard Worker                 ERRORF(r, "failed to start incremental decode!\ttop: %i\tbottom%i\n",
173*c8dee2aaSAndroid Build Coastguard Worker                        subset.top(), subset.bottom());
174*c8dee2aaSAndroid Build Coastguard Worker                 return;
175*c8dee2aaSAndroid Build Coastguard Worker             }
176*c8dee2aaSAndroid Build Coastguard Worker             if (SkCodec::kSuccess != codec->incrementalDecode()) {
177*c8dee2aaSAndroid Build Coastguard Worker                 ERRORF(r, "failed incremental decode starting from line %i\n", y);
178*c8dee2aaSAndroid Build Coastguard Worker                 return;
179*c8dee2aaSAndroid Build Coastguard Worker             }
180*c8dee2aaSAndroid Build Coastguard Worker         }
181*c8dee2aaSAndroid Build Coastguard Worker     }
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker     compare_to_good_digest(r, goodDigest, bm);
184*c8dee2aaSAndroid Build Coastguard Worker }
185*c8dee2aaSAndroid Build Coastguard Worker 
186*c8dee2aaSAndroid Build Coastguard Worker template<typename Codec>
test_codec(skiatest::Reporter * r,const char * path,Codec * codec,SkBitmap & bm,const SkImageInfo & info,const SkISize & size,SkCodec::Result expectedResult,SkMD5::Digest * digest,const SkMD5::Digest * goodDigest)187*c8dee2aaSAndroid Build Coastguard Worker static void test_codec(skiatest::Reporter* r, const char* path, Codec* codec, SkBitmap& bm,
188*c8dee2aaSAndroid Build Coastguard Worker         const SkImageInfo& info, const SkISize& size, SkCodec::Result expectedResult,
189*c8dee2aaSAndroid Build Coastguard Worker         SkMD5::Digest* digest, const SkMD5::Digest* goodDigest) {
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, info.dimensions() == size);
192*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
195*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == expectedResult);
196*c8dee2aaSAndroid Build Coastguard Worker 
197*c8dee2aaSAndroid Build Coastguard Worker     *digest = md5(bm);
198*c8dee2aaSAndroid Build Coastguard Worker     if (goodDigest) {
199*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, *digest == *goodDigest);
200*c8dee2aaSAndroid Build Coastguard Worker     }
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker     {
203*c8dee2aaSAndroid Build Coastguard Worker         // Test decoding to 565
204*c8dee2aaSAndroid Build Coastguard Worker         SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType);
205*c8dee2aaSAndroid Build Coastguard Worker         if (info.alphaType() == kOpaque_SkAlphaType) {
206*c8dee2aaSAndroid Build Coastguard Worker             // Decoding to 565 should succeed.
207*c8dee2aaSAndroid Build Coastguard Worker             SkBitmap bm565;
208*c8dee2aaSAndroid Build Coastguard Worker             bm565.allocPixels(info565);
209*c8dee2aaSAndroid Build Coastguard Worker 
210*c8dee2aaSAndroid Build Coastguard Worker             // This will allow comparison even if the image is incomplete.
211*c8dee2aaSAndroid Build Coastguard Worker             bm565.eraseColor(SK_ColorBLACK);
212*c8dee2aaSAndroid Build Coastguard Worker 
213*c8dee2aaSAndroid Build Coastguard Worker             auto actualResult = codec->getPixels(info565, bm565.getPixels(), bm565.rowBytes());
214*c8dee2aaSAndroid Build Coastguard Worker             if (actualResult == expectedResult) {
215*c8dee2aaSAndroid Build Coastguard Worker                 SkMD5::Digest digest565 = md5(bm565);
216*c8dee2aaSAndroid Build Coastguard Worker 
217*c8dee2aaSAndroid Build Coastguard Worker                 // A request for non-opaque should also succeed.
218*c8dee2aaSAndroid Build Coastguard Worker                 for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
219*c8dee2aaSAndroid Build Coastguard Worker                     info565 = info565.makeAlphaType(alpha);
220*c8dee2aaSAndroid Build Coastguard Worker                     test_info(r, codec, info565, expectedResult, &digest565);
221*c8dee2aaSAndroid Build Coastguard Worker                 }
222*c8dee2aaSAndroid Build Coastguard Worker             } else {
223*c8dee2aaSAndroid Build Coastguard Worker                 ERRORF(r, "Decoding %s to 565 failed with result \"%s\"\n\t\t\t\texpected:\"%s\"",
224*c8dee2aaSAndroid Build Coastguard Worker                           path,
225*c8dee2aaSAndroid Build Coastguard Worker                           SkCodec::ResultToString(actualResult),
226*c8dee2aaSAndroid Build Coastguard Worker                           SkCodec::ResultToString(expectedResult));
227*c8dee2aaSAndroid Build Coastguard Worker             }
228*c8dee2aaSAndroid Build Coastguard Worker         } else {
229*c8dee2aaSAndroid Build Coastguard Worker             test_info(r, codec, info565, SkCodec::kInvalidConversion, nullptr);
230*c8dee2aaSAndroid Build Coastguard Worker         }
231*c8dee2aaSAndroid Build Coastguard Worker     }
232*c8dee2aaSAndroid Build Coastguard Worker 
233*c8dee2aaSAndroid Build Coastguard Worker     if (codec->getInfo().colorType() == kGray_8_SkColorType) {
234*c8dee2aaSAndroid Build Coastguard Worker         SkImageInfo grayInfo = codec->getInfo();
235*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap grayBm;
236*c8dee2aaSAndroid Build Coastguard Worker         grayBm.allocPixels(grayInfo);
237*c8dee2aaSAndroid Build Coastguard Worker 
238*c8dee2aaSAndroid Build Coastguard Worker         grayBm.eraseColor(SK_ColorBLACK);
239*c8dee2aaSAndroid Build Coastguard Worker 
240*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, expectedResult == codec->getPixels(grayInfo,
241*c8dee2aaSAndroid Build Coastguard Worker                 grayBm.getPixels(), grayBm.rowBytes()));
242*c8dee2aaSAndroid Build Coastguard Worker 
243*c8dee2aaSAndroid Build Coastguard Worker         SkMD5::Digest grayDigest = md5(grayBm);
244*c8dee2aaSAndroid Build Coastguard Worker 
245*c8dee2aaSAndroid Build Coastguard Worker         for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
246*c8dee2aaSAndroid Build Coastguard Worker             grayInfo = grayInfo.makeAlphaType(alpha);
247*c8dee2aaSAndroid Build Coastguard Worker             test_info(r, codec, grayInfo, expectedResult, &grayDigest);
248*c8dee2aaSAndroid Build Coastguard Worker         }
249*c8dee2aaSAndroid Build Coastguard Worker     }
250*c8dee2aaSAndroid Build Coastguard Worker 
251*c8dee2aaSAndroid Build Coastguard Worker     // Verify that re-decoding gives the same result.  It is interesting to check this after
252*c8dee2aaSAndroid Build Coastguard Worker     // a decode to 565, since choosing to decode to 565 may result in some of the decode
253*c8dee2aaSAndroid Build Coastguard Worker     // options being modified.  These options should return to their defaults on another
254*c8dee2aaSAndroid Build Coastguard Worker     // decode to kN32, so the new digest should match the old digest.
255*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec, info, expectedResult, digest);
256*c8dee2aaSAndroid Build Coastguard Worker 
257*c8dee2aaSAndroid Build Coastguard Worker     {
258*c8dee2aaSAndroid Build Coastguard Worker         // Check alpha type conversions
259*c8dee2aaSAndroid Build Coastguard Worker         if (info.alphaType() == kOpaque_SkAlphaType) {
260*c8dee2aaSAndroid Build Coastguard Worker             test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType),
261*c8dee2aaSAndroid Build Coastguard Worker                       expectedResult, digest);
262*c8dee2aaSAndroid Build Coastguard Worker             test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType),
263*c8dee2aaSAndroid Build Coastguard Worker                       expectedResult, digest);
264*c8dee2aaSAndroid Build Coastguard Worker         } else {
265*c8dee2aaSAndroid Build Coastguard Worker             // Decoding to opaque should fail
266*c8dee2aaSAndroid Build Coastguard Worker             test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType),
267*c8dee2aaSAndroid Build Coastguard Worker                       SkCodec::kInvalidConversion, nullptr);
268*c8dee2aaSAndroid Build Coastguard Worker             SkAlphaType otherAt = info.alphaType();
269*c8dee2aaSAndroid Build Coastguard Worker             if (kPremul_SkAlphaType == otherAt) {
270*c8dee2aaSAndroid Build Coastguard Worker                 otherAt = kUnpremul_SkAlphaType;
271*c8dee2aaSAndroid Build Coastguard Worker             } else {
272*c8dee2aaSAndroid Build Coastguard Worker                 otherAt = kPremul_SkAlphaType;
273*c8dee2aaSAndroid Build Coastguard Worker             }
274*c8dee2aaSAndroid Build Coastguard Worker             // The other non-opaque alpha type should always succeed, but not match.
275*c8dee2aaSAndroid Build Coastguard Worker             test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr);
276*c8dee2aaSAndroid Build Coastguard Worker         }
277*c8dee2aaSAndroid Build Coastguard Worker     }
278*c8dee2aaSAndroid Build Coastguard Worker }
279*c8dee2aaSAndroid Build Coastguard Worker 
supports_partial_scanlines(const char path[])280*c8dee2aaSAndroid Build Coastguard Worker static bool supports_partial_scanlines(const char path[]) {
281*c8dee2aaSAndroid Build Coastguard Worker     static const char* const exts[] = {
282*c8dee2aaSAndroid Build Coastguard Worker         "jpg", "jpeg", "png", "webp",
283*c8dee2aaSAndroid Build Coastguard Worker         "JPG", "JPEG", "PNG", "WEBP"
284*c8dee2aaSAndroid Build Coastguard Worker     };
285*c8dee2aaSAndroid Build Coastguard Worker 
286*c8dee2aaSAndroid Build Coastguard Worker     for (uint32_t i = 0; i < std::size(exts); i++) {
287*c8dee2aaSAndroid Build Coastguard Worker         if (SkStrEndsWith(path, exts[i])) {
288*c8dee2aaSAndroid Build Coastguard Worker             return true;
289*c8dee2aaSAndroid Build Coastguard Worker         }
290*c8dee2aaSAndroid Build Coastguard Worker     }
291*c8dee2aaSAndroid Build Coastguard Worker     return false;
292*c8dee2aaSAndroid Build Coastguard Worker }
293*c8dee2aaSAndroid Build Coastguard Worker 
check_scanline_decode(skiatest::Reporter * r,SkCodec * codec,SkMD5::Digest * codecDigest,const SkImageInfo & info,const char path[],SkISize size,bool supportsScanlineDecoding,bool supportsIncomplete,bool supportsNewScanlineDecoding)294*c8dee2aaSAndroid Build Coastguard Worker static void check_scanline_decode(skiatest::Reporter* r,
295*c8dee2aaSAndroid Build Coastguard Worker                                   SkCodec* codec,
296*c8dee2aaSAndroid Build Coastguard Worker                                   SkMD5::Digest* codecDigest,
297*c8dee2aaSAndroid Build Coastguard Worker                                   const SkImageInfo& info,
298*c8dee2aaSAndroid Build Coastguard Worker                                   const char path[],
299*c8dee2aaSAndroid Build Coastguard Worker                                   SkISize size,
300*c8dee2aaSAndroid Build Coastguard Worker                                   bool supportsScanlineDecoding,
301*c8dee2aaSAndroid Build Coastguard Worker                                   bool supportsIncomplete,
302*c8dee2aaSAndroid Build Coastguard Worker                                   bool supportsNewScanlineDecoding) {
303*c8dee2aaSAndroid Build Coastguard Worker 
304*c8dee2aaSAndroid Build Coastguard Worker     // Test full image decodes with SkCodec
305*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
306*c8dee2aaSAndroid Build Coastguard Worker     const SkCodec::Result expectedResult = supportsIncomplete ? SkCodec::kIncompleteInput
307*c8dee2aaSAndroid Build Coastguard Worker                                                               : SkCodec::kSuccess;
308*c8dee2aaSAndroid Build Coastguard Worker     test_codec(r, path, codec, bm, info, size, expectedResult, codecDigest, nullptr);
309*c8dee2aaSAndroid Build Coastguard Worker 
310*c8dee2aaSAndroid Build Coastguard Worker     // Scanline decoding follows.
311*c8dee2aaSAndroid Build Coastguard Worker     if (supportsNewScanlineDecoding && !supportsIncomplete) {
312*c8dee2aaSAndroid Build Coastguard Worker         test_incremental_decode(r, codec, info, *codecDigest);
313*c8dee2aaSAndroid Build Coastguard Worker         // This is only supported by codecs that use incremental decoding to
314*c8dee2aaSAndroid Build Coastguard Worker         // support subset decodes - png and jpeg (once SkJpegCodec is
315*c8dee2aaSAndroid Build Coastguard Worker         // converted).
316*c8dee2aaSAndroid Build Coastguard Worker         if (SkStrEndsWith(path, "png") || SkStrEndsWith(path, "PNG")) {
317*c8dee2aaSAndroid Build Coastguard Worker             test_in_stripes(r, codec, info, *codecDigest);
318*c8dee2aaSAndroid Build Coastguard Worker         }
319*c8dee2aaSAndroid Build Coastguard Worker     }
320*c8dee2aaSAndroid Build Coastguard Worker 
321*c8dee2aaSAndroid Build Coastguard Worker     // Need to call startScanlineDecode() first.
322*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0);
323*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !codec->skipScanlines(1));
324*c8dee2aaSAndroid Build Coastguard Worker     const SkCodec::Result startResult = codec->startScanlineDecode(info);
325*c8dee2aaSAndroid Build Coastguard Worker     if (supportsScanlineDecoding) {
326*c8dee2aaSAndroid Build Coastguard Worker         bm.eraseColor(SK_ColorYELLOW);
327*c8dee2aaSAndroid Build Coastguard Worker 
328*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, startResult == SkCodec::kSuccess);
329*c8dee2aaSAndroid Build Coastguard Worker 
330*c8dee2aaSAndroid Build Coastguard Worker         for (int y = 0; y < info.height(); y++) {
331*c8dee2aaSAndroid Build Coastguard Worker             const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
332*c8dee2aaSAndroid Build Coastguard Worker             if (!supportsIncomplete) {
333*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, 1 == lines);
334*c8dee2aaSAndroid Build Coastguard Worker             }
335*c8dee2aaSAndroid Build Coastguard Worker         }
336*c8dee2aaSAndroid Build Coastguard Worker         // verify that scanline decoding gives the same result.
337*c8dee2aaSAndroid Build Coastguard Worker         if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) {
338*c8dee2aaSAndroid Build Coastguard Worker             compare_to_good_digest(r, *codecDigest, bm);
339*c8dee2aaSAndroid Build Coastguard Worker         }
340*c8dee2aaSAndroid Build Coastguard Worker 
341*c8dee2aaSAndroid Build Coastguard Worker         // Cannot continue to decode scanlines beyond the end
342*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
343*c8dee2aaSAndroid Build Coastguard Worker                 == 0);
344*c8dee2aaSAndroid Build Coastguard Worker 
345*c8dee2aaSAndroid Build Coastguard Worker         // Interrupting a scanline decode with a full decode starts from
346*c8dee2aaSAndroid Build Coastguard Worker         // scratch
347*c8dee2aaSAndroid Build Coastguard Worker         {
348*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess);
349*c8dee2aaSAndroid Build Coastguard Worker             const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0);
350*c8dee2aaSAndroid Build Coastguard Worker             if (!supportsIncomplete) {
351*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, lines == 1);
352*c8dee2aaSAndroid Build Coastguard Worker             }
353*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())
354*c8dee2aaSAndroid Build Coastguard Worker                     == expectedResult);
355*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
356*c8dee2aaSAndroid Build Coastguard Worker                     == 0);
357*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, codec->skipScanlines(1)
358*c8dee2aaSAndroid Build Coastguard Worker                     == 0);
359*c8dee2aaSAndroid Build Coastguard Worker         }
360*c8dee2aaSAndroid Build Coastguard Worker 
361*c8dee2aaSAndroid Build Coastguard Worker         // Test partial scanline decodes
362*c8dee2aaSAndroid Build Coastguard Worker         if (supports_partial_scanlines(path) && info.width() >= 3) {
363*c8dee2aaSAndroid Build Coastguard Worker             SkCodec::Options options;
364*c8dee2aaSAndroid Build Coastguard Worker             int width = info.width();
365*c8dee2aaSAndroid Build Coastguard Worker             int height = info.height();
366*c8dee2aaSAndroid Build Coastguard Worker             SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height);
367*c8dee2aaSAndroid Build Coastguard Worker             options.fSubset = &subset;
368*c8dee2aaSAndroid Build Coastguard Worker 
369*c8dee2aaSAndroid Build Coastguard Worker             const auto partialStartResult = codec->startScanlineDecode(info, &options);
370*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess);
371*c8dee2aaSAndroid Build Coastguard Worker 
372*c8dee2aaSAndroid Build Coastguard Worker             for (int y = 0; y < height; y++) {
373*c8dee2aaSAndroid Build Coastguard Worker                 const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
374*c8dee2aaSAndroid Build Coastguard Worker                 if (!supportsIncomplete) {
375*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, 1 == lines);
376*c8dee2aaSAndroid Build Coastguard Worker                 }
377*c8dee2aaSAndroid Build Coastguard Worker             }
378*c8dee2aaSAndroid Build Coastguard Worker         }
379*c8dee2aaSAndroid Build Coastguard Worker     } else {
380*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented);
381*c8dee2aaSAndroid Build Coastguard Worker     }
382*c8dee2aaSAndroid Build Coastguard Worker }
383*c8dee2aaSAndroid Build Coastguard Worker 
check_subset_decode(skiatest::Reporter * r,SkCodec * codec,const SkImageInfo & info,SkISize size,bool supportsSubsetDecoding,bool supportsIncomplete)384*c8dee2aaSAndroid Build Coastguard Worker static void check_subset_decode(skiatest::Reporter* r,
385*c8dee2aaSAndroid Build Coastguard Worker                                 SkCodec* codec,
386*c8dee2aaSAndroid Build Coastguard Worker                                 const SkImageInfo& info,
387*c8dee2aaSAndroid Build Coastguard Worker                                 SkISize size,
388*c8dee2aaSAndroid Build Coastguard Worker                                 bool supportsSubsetDecoding,
389*c8dee2aaSAndroid Build Coastguard Worker                                 bool supportsIncomplete) {
390*c8dee2aaSAndroid Build Coastguard Worker     // This function tests decoding subsets, and will decode a handful of randomly-sized subsets.
391*c8dee2aaSAndroid Build Coastguard Worker     // Do not attempt to decode subsets of an image of only one pixel, since there is no
392*c8dee2aaSAndroid Build Coastguard Worker     // meaningful subset.
393*c8dee2aaSAndroid Build Coastguard Worker     if (size.width() * size.height() == 1) {
394*c8dee2aaSAndroid Build Coastguard Worker         return;
395*c8dee2aaSAndroid Build Coastguard Worker     }
396*c8dee2aaSAndroid Build Coastguard Worker 
397*c8dee2aaSAndroid Build Coastguard Worker     SkRandom rand;
398*c8dee2aaSAndroid Build Coastguard Worker     SkIRect subset;
399*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Options opts;
400*c8dee2aaSAndroid Build Coastguard Worker     opts.fSubset = &subset;
401*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < 5; i++) {
402*c8dee2aaSAndroid Build Coastguard Worker         subset = generate_random_subset(&rand, size.width(), size.height());
403*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!subset.isEmpty());
404*c8dee2aaSAndroid Build Coastguard Worker         const bool supported = codec->getValidSubset(&subset);
405*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, supported == supportsSubsetDecoding);
406*c8dee2aaSAndroid Build Coastguard Worker 
407*c8dee2aaSAndroid Build Coastguard Worker         SkImageInfo subsetInfo = info.makeDimensions(subset.size());
408*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bm;
409*c8dee2aaSAndroid Build Coastguard Worker         bm.allocPixels(subsetInfo);
410*c8dee2aaSAndroid Build Coastguard Worker         const auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts);
411*c8dee2aaSAndroid Build Coastguard Worker 
412*c8dee2aaSAndroid Build Coastguard Worker         if (supportsSubsetDecoding) {
413*c8dee2aaSAndroid Build Coastguard Worker             if (!supportsIncomplete) {
414*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
415*c8dee2aaSAndroid Build Coastguard Worker             }
416*c8dee2aaSAndroid Build Coastguard Worker             // Webp is the only codec that supports subsets, and it will have modified the subset
417*c8dee2aaSAndroid Build Coastguard Worker             // to have even left/top.
418*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
419*c8dee2aaSAndroid Build Coastguard Worker         } else {
420*c8dee2aaSAndroid Build Coastguard Worker             // No subsets will work.
421*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, result == SkCodec::kUnimplemented);
422*c8dee2aaSAndroid Build Coastguard Worker         }
423*c8dee2aaSAndroid Build Coastguard Worker     }
424*c8dee2aaSAndroid Build Coastguard Worker }
425*c8dee2aaSAndroid Build Coastguard Worker 
check_android_codec(skiatest::Reporter * r,std::unique_ptr<SkCodec> codec,const SkMD5::Digest & codecDigest,const SkImageInfo & info,const char path[],SkISize size,bool supportsScanlineDecoding,bool supportsSubsetDecoding,bool supportsIncomplete,bool supportsNewScanlineDecoding)426*c8dee2aaSAndroid Build Coastguard Worker static void check_android_codec(skiatest::Reporter* r,
427*c8dee2aaSAndroid Build Coastguard Worker                                 std::unique_ptr<SkCodec> codec,
428*c8dee2aaSAndroid Build Coastguard Worker                                 const SkMD5::Digest& codecDigest,
429*c8dee2aaSAndroid Build Coastguard Worker                                 const SkImageInfo& info,
430*c8dee2aaSAndroid Build Coastguard Worker                                 const char path[],
431*c8dee2aaSAndroid Build Coastguard Worker                                 SkISize size,
432*c8dee2aaSAndroid Build Coastguard Worker                                 bool supportsScanlineDecoding,
433*c8dee2aaSAndroid Build Coastguard Worker                                 bool supportsSubsetDecoding,
434*c8dee2aaSAndroid Build Coastguard Worker                                 bool supportsIncomplete,
435*c8dee2aaSAndroid Build Coastguard Worker                                 bool supportsNewScanlineDecoding) {
436*c8dee2aaSAndroid Build Coastguard Worker     if (supportsScanlineDecoding || supportsSubsetDecoding || supportsNewScanlineDecoding) {
437*c8dee2aaSAndroid Build Coastguard Worker         auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
438*c8dee2aaSAndroid Build Coastguard Worker         if (!androidCodec) {
439*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Unable to decode '%s'", path);
440*c8dee2aaSAndroid Build Coastguard Worker             return;
441*c8dee2aaSAndroid Build Coastguard Worker         }
442*c8dee2aaSAndroid Build Coastguard Worker 
443*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bm;
444*c8dee2aaSAndroid Build Coastguard Worker         SkMD5::Digest androidCodecDigest;
445*c8dee2aaSAndroid Build Coastguard Worker         const SkCodec::Result expectedResult = supportsIncomplete ? SkCodec::kIncompleteInput
446*c8dee2aaSAndroid Build Coastguard Worker                                                                   : SkCodec::kSuccess;
447*c8dee2aaSAndroid Build Coastguard Worker         test_codec(r, path, androidCodec.get(), bm, info, size, expectedResult, &androidCodecDigest,
448*c8dee2aaSAndroid Build Coastguard Worker                    &codecDigest);
449*c8dee2aaSAndroid Build Coastguard Worker     }
450*c8dee2aaSAndroid Build Coastguard Worker }
451*c8dee2aaSAndroid Build Coastguard Worker 
check_codec_image_generator(skiatest::Reporter * r,const SkMD5::Digest & codecDigest,const SkImageInfo & info,const char path[],bool supportsIncomplete)452*c8dee2aaSAndroid Build Coastguard Worker static void check_codec_image_generator(skiatest::Reporter* r,
453*c8dee2aaSAndroid Build Coastguard Worker                                         const SkMD5::Digest& codecDigest,
454*c8dee2aaSAndroid Build Coastguard Worker                                         const SkImageInfo& info,
455*c8dee2aaSAndroid Build Coastguard Worker                                         const char path[],
456*c8dee2aaSAndroid Build Coastguard Worker                                         bool supportsIncomplete) {
457*c8dee2aaSAndroid Build Coastguard Worker     // Test SkCodecImageGenerator
458*c8dee2aaSAndroid Build Coastguard Worker     if (!supportsIncomplete) {
459*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
460*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkData> fullData(SkData::MakeFromStream(stream.get(), stream->getLength()));
461*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkImageGenerator> gen(
462*c8dee2aaSAndroid Build Coastguard Worker                 SkCodecImageGenerator::MakeFromEncodedCodec(fullData));
463*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bm;
464*c8dee2aaSAndroid Build Coastguard Worker         bm.allocPixels(info);
465*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes()));
466*c8dee2aaSAndroid Build Coastguard Worker         compare_to_good_digest(r, codecDigest, bm);
467*c8dee2aaSAndroid Build Coastguard Worker 
468*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_PNG_DISABLE_TESTS) && defined(SK_ENABLE_ANDROID_UTILS)
469*c8dee2aaSAndroid Build Coastguard Worker         // Test using FrontBufferedStream, as Android does
470*c8dee2aaSAndroid Build Coastguard Worker         auto bufferedStream = android::skia::FrontBufferedStream::Make(
471*c8dee2aaSAndroid Build Coastguard Worker                       SkMemoryStream::Make(std::move(fullData)), SkCodec::MinBufferedBytesNeeded());
472*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, bufferedStream);
473*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(bufferedStream));
474*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, codec);
475*c8dee2aaSAndroid Build Coastguard Worker         if (codec) {
476*c8dee2aaSAndroid Build Coastguard Worker             test_info(r, codec.get(), info, SkCodec::kSuccess, &codecDigest);
477*c8dee2aaSAndroid Build Coastguard Worker         }
478*c8dee2aaSAndroid Build Coastguard Worker #endif
479*c8dee2aaSAndroid Build Coastguard Worker     }
480*c8dee2aaSAndroid Build Coastguard Worker }
481*c8dee2aaSAndroid Build Coastguard Worker 
check(skiatest::Reporter * r,const char path[],SkISize size,bool supportsScanlineDecoding,bool supportsSubsetDecoding,bool supportsIncomplete,bool supportsNewScanlineDecoding=false)482*c8dee2aaSAndroid Build Coastguard Worker static void check(skiatest::Reporter* r,
483*c8dee2aaSAndroid Build Coastguard Worker                   const char path[],
484*c8dee2aaSAndroid Build Coastguard Worker                   SkISize size,
485*c8dee2aaSAndroid Build Coastguard Worker                   bool supportsScanlineDecoding,
486*c8dee2aaSAndroid Build Coastguard Worker                   bool supportsSubsetDecoding,
487*c8dee2aaSAndroid Build Coastguard Worker                   bool supportsIncomplete,
488*c8dee2aaSAndroid Build Coastguard Worker                   bool supportsNewScanlineDecoding = false) {
489*c8dee2aaSAndroid Build Coastguard Worker     // If we're testing incomplete decodes, let's run the same test on full decodes.
490*c8dee2aaSAndroid Build Coastguard Worker     if (supportsIncomplete) {
491*c8dee2aaSAndroid Build Coastguard Worker         check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding,
492*c8dee2aaSAndroid Build Coastguard Worker               /*supportsIncomplete=*/false, supportsNewScanlineDecoding);
493*c8dee2aaSAndroid Build Coastguard Worker     }
494*c8dee2aaSAndroid Build Coastguard Worker 
495*c8dee2aaSAndroid Build Coastguard Worker     // Initialize a codec with a data stream.
496*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
497*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
498*c8dee2aaSAndroid Build Coastguard Worker         return;
499*c8dee2aaSAndroid Build Coastguard Worker     }
500*c8dee2aaSAndroid Build Coastguard Worker 
501*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec;
502*c8dee2aaSAndroid Build Coastguard Worker     if (supportsIncomplete) {
503*c8dee2aaSAndroid Build Coastguard Worker         size_t length = stream->getLength();
504*c8dee2aaSAndroid Build Coastguard Worker         codec = SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 2 * length / 3));
505*c8dee2aaSAndroid Build Coastguard Worker     } else {
506*c8dee2aaSAndroid Build Coastguard Worker         codec = SkCodec::MakeFromStream(std::move(stream));
507*c8dee2aaSAndroid Build Coastguard Worker     }
508*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
509*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Unable to decode '%s'", path);
510*c8dee2aaSAndroid Build Coastguard Worker         return;
511*c8dee2aaSAndroid Build Coastguard Worker     }
512*c8dee2aaSAndroid Build Coastguard Worker 
513*c8dee2aaSAndroid Build Coastguard Worker     const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
514*c8dee2aaSAndroid Build Coastguard Worker 
515*c8dee2aaSAndroid Build Coastguard Worker     // Run tests with this codec.
516*c8dee2aaSAndroid Build Coastguard Worker     SkMD5::Digest codecDigest;
517*c8dee2aaSAndroid Build Coastguard Worker     check_scanline_decode(r, codec.get(), &codecDigest, info, path, size, supportsScanlineDecoding,
518*c8dee2aaSAndroid Build Coastguard Worker                           supportsIncomplete, supportsNewScanlineDecoding);
519*c8dee2aaSAndroid Build Coastguard Worker 
520*c8dee2aaSAndroid Build Coastguard Worker     check_subset_decode(r, codec.get(), info, size, supportsSubsetDecoding, supportsIncomplete);
521*c8dee2aaSAndroid Build Coastguard Worker 
522*c8dee2aaSAndroid Build Coastguard Worker     check_android_codec(r, std::move(codec), codecDigest, info, path, size,
523*c8dee2aaSAndroid Build Coastguard Worker                         supportsScanlineDecoding, supportsSubsetDecoding, supportsIncomplete,
524*c8dee2aaSAndroid Build Coastguard Worker                         supportsNewScanlineDecoding);
525*c8dee2aaSAndroid Build Coastguard Worker 
526*c8dee2aaSAndroid Build Coastguard Worker     check_codec_image_generator(r, codecDigest, info, path, supportsIncomplete);
527*c8dee2aaSAndroid Build Coastguard Worker }
528*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_wbmp,r)529*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_wbmp, r) {
530*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/mandrill.wbmp", SkISize::Make(512, 512), true, false, true);
531*c8dee2aaSAndroid Build Coastguard Worker }
532*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_webp,r)533*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_webp, r) {
534*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/baby_tux.webp", SkISize::Make(386, 395), false, true, true);
535*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/color_wheel.webp", SkISize::Make(128, 128), false, true, true);
536*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/yellow_rose.webp", SkISize::Make(400, 301), false, true, true);
537*c8dee2aaSAndroid Build Coastguard Worker }
538*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_bmp,r)539*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_bmp, r) {
540*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/randPixels.bmp", SkISize::Make(8, 8), true, false, true);
541*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/rle.bmp", SkISize::Make(320, 240), true, false, true);
542*c8dee2aaSAndroid Build Coastguard Worker }
543*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_ico,r)544*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_ico, r) {
545*c8dee2aaSAndroid Build Coastguard Worker     // FIXME: We are not ready to test incomplete ICOs
546*c8dee2aaSAndroid Build Coastguard Worker     // These two tests examine interestingly different behavior:
547*c8dee2aaSAndroid Build Coastguard Worker     // Decodes an embedded BMP image
548*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/color_wheel.ico", SkISize::Make(128, 128), true, false, false);
549*c8dee2aaSAndroid Build Coastguard Worker     // Decodes an embedded PNG image
550*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/google_chrome.ico", SkISize::Make(256, 256), false, false, false, true);
551*c8dee2aaSAndroid Build Coastguard Worker }
552*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_gif,r)553*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_gif, r) {
554*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/box.gif", SkISize::Make(200, 55), false, false, true, true);
555*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/color_wheel.gif", SkISize::Make(128, 128), false, false, true, true);
556*c8dee2aaSAndroid Build Coastguard Worker     // randPixels.gif is too small to test incomplete
557*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/randPixels.gif", SkISize::Make(8, 8), false, false, false, true);
558*c8dee2aaSAndroid Build Coastguard Worker }
559*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_jpg,r)560*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_jpg, r) {
561*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/CMYK.jpg", SkISize::Make(642, 516), true, false, true);
562*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/color_wheel.jpg", SkISize::Make(128, 128), true, false, true);
563*c8dee2aaSAndroid Build Coastguard Worker     // grayscale.jpg is too small to test incomplete
564*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/grayscale.jpg", SkISize::Make(128, 128), true, false, false);
565*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/mandrill_512_q075.jpg", SkISize::Make(512, 512), true, false, true);
566*c8dee2aaSAndroid Build Coastguard Worker     // randPixels.jpg is too small to test incomplete
567*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/randPixels.jpg", SkISize::Make(8, 8), true, false, false);
568*c8dee2aaSAndroid Build Coastguard Worker }
569*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_png,r)570*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_png, r) {
571*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/arrow.png", SkISize::Make(187, 312), false, false, true, true);
572*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/baby_tux.png", SkISize::Make(240, 246), false, false, true, true);
573*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/color_wheel.png", SkISize::Make(128, 128), false, false, true, true);
574*c8dee2aaSAndroid Build Coastguard Worker     // half-transparent-white-pixel.png is too small to test incomplete
575*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/half-transparent-white-pixel.png", SkISize::Make(1, 1), false, false, false, true);
576*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/mandrill_128.png", SkISize::Make(128, 128), false, false, true, true);
577*c8dee2aaSAndroid Build Coastguard Worker     // mandrill_16.png is too small (relative to embedded sRGB profile) to test incomplete
578*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/mandrill_16.png", SkISize::Make(16, 16), false, false, false, true);
579*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/mandrill_256.png", SkISize::Make(256, 256), false, false, true, true);
580*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/mandrill_32.png", SkISize::Make(32, 32), false, false, true, true);
581*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/mandrill_512.png", SkISize::Make(512, 512), false, false, true, true);
582*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/mandrill_64.png", SkISize::Make(64, 64), false, false, true, true);
583*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/plane.png", SkISize::Make(250, 126), false, false, true, true);
584*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/plane_interlaced.png", SkISize::Make(250, 126), false, false, true, true);
585*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/randPixels.png", SkISize::Make(8, 8), false, false, true, true);
586*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/yellow_rose.png", SkISize::Make(400, 301), false, false, true, true);
587*c8dee2aaSAndroid Build Coastguard Worker }
588*c8dee2aaSAndroid Build Coastguard Worker 
589*c8dee2aaSAndroid Build Coastguard Worker // Disable RAW tests for Win32.
590*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
DEF_TEST(Codec_raw,r)591*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_raw, r) {
592*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/sample_1mp.dng", SkISize::Make(600, 338), false, false, false);
593*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/sample_1mp_rotated.dng", SkISize::Make(600, 338), false, false, false);
594*c8dee2aaSAndroid Build Coastguard Worker     check(r, "images/dng_with_preview.dng", SkISize::Make(600, 338), true, false, false);
595*c8dee2aaSAndroid Build Coastguard Worker }
596*c8dee2aaSAndroid Build Coastguard Worker #endif
597*c8dee2aaSAndroid Build Coastguard Worker 
test_invalid_stream(skiatest::Reporter * r,const void * stream,size_t len)598*c8dee2aaSAndroid Build Coastguard Worker static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) {
599*c8dee2aaSAndroid Build Coastguard Worker     // Neither of these calls should return a codec. Bots should catch us if we leaked anything.
600*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !SkCodec::MakeFromStream(
601*c8dee2aaSAndroid Build Coastguard Worker                                         std::make_unique<SkMemoryStream>(stream, len, false)));
602*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !SkAndroidCodec::MakeFromStream(
603*c8dee2aaSAndroid Build Coastguard Worker                                         std::make_unique<SkMemoryStream>(stream, len, false)));
604*c8dee2aaSAndroid Build Coastguard Worker }
605*c8dee2aaSAndroid Build Coastguard Worker 
606*c8dee2aaSAndroid Build Coastguard Worker // Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream,
607*c8dee2aaSAndroid Build Coastguard Worker // even on failure. Test some bad streams.
DEF_TEST(Codec_leaks,r)608*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_leaks, r) {
609*c8dee2aaSAndroid Build Coastguard Worker     // No codec should claim this as their format, so this tests SkCodec::NewFromStream.
610*c8dee2aaSAndroid Build Coastguard Worker     const char nonSupportedStream[] = "hello world";
611*c8dee2aaSAndroid Build Coastguard Worker     // The other strings should look like the beginning of a file type, so we'll call some
612*c8dee2aaSAndroid Build Coastguard Worker     // internal version of NewFromStream, which must also delete the stream on failure.
613*c8dee2aaSAndroid Build Coastguard Worker     const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
614*c8dee2aaSAndroid Build Coastguard Worker     const unsigned char emptyJpeg[] = { 0xFF, 0xD8, 0xFF };
615*c8dee2aaSAndroid Build Coastguard Worker     const char emptyWebp[] = "RIFF1234WEBPVP";
616*c8dee2aaSAndroid Build Coastguard Worker     const char emptyBmp[] = { 'B', 'M' };
617*c8dee2aaSAndroid Build Coastguard Worker     const char emptyIco[] = { '\x00', '\x00', '\x01', '\x00' };
618*c8dee2aaSAndroid Build Coastguard Worker     const char emptyGif[] = "GIFVER";
619*c8dee2aaSAndroid Build Coastguard Worker 
620*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_stream(r, nonSupportedStream, sizeof(nonSupportedStream));
621*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_stream(r, emptyPng, sizeof(emptyPng));
622*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_stream(r, emptyJpeg, sizeof(emptyJpeg));
623*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_stream(r, emptyWebp, sizeof(emptyWebp));
624*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_stream(r, emptyBmp, sizeof(emptyBmp));
625*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_stream(r, emptyIco, sizeof(emptyIco));
626*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_stream(r, emptyGif, sizeof(emptyGif));
627*c8dee2aaSAndroid Build Coastguard Worker }
628*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_null,r)629*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_null, r) {
630*c8dee2aaSAndroid Build Coastguard Worker     // Attempting to create an SkCodec or an SkAndroidCodec with null should not
631*c8dee2aaSAndroid Build Coastguard Worker     // crash.
632*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !SkCodec::MakeFromStream(nullptr));
633*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !SkAndroidCodec::MakeFromStream(nullptr));
634*c8dee2aaSAndroid Build Coastguard Worker }
635*c8dee2aaSAndroid Build Coastguard Worker 
test_dimensions(skiatest::Reporter * r,const char path[])636*c8dee2aaSAndroid Build Coastguard Worker static void test_dimensions(skiatest::Reporter* r, const char path[]) {
637*c8dee2aaSAndroid Build Coastguard Worker     // Create the codec from the resource file
638*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
639*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
640*c8dee2aaSAndroid Build Coastguard Worker         return;
641*c8dee2aaSAndroid Build Coastguard Worker     }
642*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream)));
643*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
644*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Unable to create codec '%s'", path);
645*c8dee2aaSAndroid Build Coastguard Worker         return;
646*c8dee2aaSAndroid Build Coastguard Worker     }
647*c8dee2aaSAndroid Build Coastguard Worker 
648*c8dee2aaSAndroid Build Coastguard Worker     // Check that the decode is successful for a variety of scales
649*c8dee2aaSAndroid Build Coastguard Worker     for (int sampleSize = 1; sampleSize < 32; sampleSize++) {
650*c8dee2aaSAndroid Build Coastguard Worker         // Scale the output dimensions
651*c8dee2aaSAndroid Build Coastguard Worker         SkISize scaledDims = codec->getSampledDimensions(sampleSize);
652*c8dee2aaSAndroid Build Coastguard Worker         SkImageInfo scaledInfo = codec->getInfo()
653*c8dee2aaSAndroid Build Coastguard Worker                 .makeDimensions(scaledDims)
654*c8dee2aaSAndroid Build Coastguard Worker                 .makeColorType(kN32_SkColorType);
655*c8dee2aaSAndroid Build Coastguard Worker 
656*c8dee2aaSAndroid Build Coastguard Worker         // Set up for the decode
657*c8dee2aaSAndroid Build Coastguard Worker         size_t rowBytes = scaledDims.width() * sizeof(SkPMColor);
658*c8dee2aaSAndroid Build Coastguard Worker         size_t totalBytes = scaledInfo.computeByteSize(rowBytes);
659*c8dee2aaSAndroid Build Coastguard Worker         AutoTMalloc<SkPMColor> pixels(totalBytes);
660*c8dee2aaSAndroid Build Coastguard Worker 
661*c8dee2aaSAndroid Build Coastguard Worker         SkAndroidCodec::AndroidOptions options;
662*c8dee2aaSAndroid Build Coastguard Worker         options.fSampleSize = sampleSize;
663*c8dee2aaSAndroid Build Coastguard Worker         SkCodec::Result result =
664*c8dee2aaSAndroid Build Coastguard Worker                 codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options);
665*c8dee2aaSAndroid Build Coastguard Worker         if (result != SkCodec::kSuccess) {
666*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Failed to decode %s with sample size %i; error: %s", path, sampleSize,
667*c8dee2aaSAndroid Build Coastguard Worker                     SkCodec::ResultToString(result));
668*c8dee2aaSAndroid Build Coastguard Worker         }
669*c8dee2aaSAndroid Build Coastguard Worker     }
670*c8dee2aaSAndroid Build Coastguard Worker }
671*c8dee2aaSAndroid Build Coastguard Worker 
672*c8dee2aaSAndroid Build Coastguard Worker // Ensure that onGetScaledDimensions returns valid image dimensions to use for decodes
DEF_TEST(Codec_Dimensions,r)673*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_Dimensions, r) {
674*c8dee2aaSAndroid Build Coastguard Worker     // JPG
675*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/CMYK.jpg");
676*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/color_wheel.jpg");
677*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/grayscale.jpg");
678*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/mandrill_512_q075.jpg");
679*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/randPixels.jpg");
680*c8dee2aaSAndroid Build Coastguard Worker 
681*c8dee2aaSAndroid Build Coastguard Worker     // Decoding small images with very large scaling factors is a potential
682*c8dee2aaSAndroid Build Coastguard Worker     // source of bugs and crashes.  We disable these tests in Gold because
683*c8dee2aaSAndroid Build Coastguard Worker     // tiny images are not very useful to look at.
684*c8dee2aaSAndroid Build Coastguard Worker     // Here we make sure that we do not crash or access illegal memory when
685*c8dee2aaSAndroid Build Coastguard Worker     // performing scaled decodes on small images.
686*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/1x1.png");
687*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/2x2.png");
688*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/3x3.png");
689*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/3x1.png");
690*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/1x1.png");
691*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/16x1.png");
692*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/1x16.png");
693*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/mandrill_16.png");
694*c8dee2aaSAndroid Build Coastguard Worker 
695*c8dee2aaSAndroid Build Coastguard Worker     // RAW
696*c8dee2aaSAndroid Build Coastguard Worker // Disable RAW tests for Win32.
697*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
698*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/sample_1mp.dng");
699*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/sample_1mp_rotated.dng");
700*c8dee2aaSAndroid Build Coastguard Worker     test_dimensions(r, "images/dng_with_preview.dng");
701*c8dee2aaSAndroid Build Coastguard Worker #endif
702*c8dee2aaSAndroid Build Coastguard Worker }
703*c8dee2aaSAndroid Build Coastguard Worker 
test_invalid(skiatest::Reporter * r,const char path[])704*c8dee2aaSAndroid Build Coastguard Worker static void test_invalid(skiatest::Reporter* r, const char path[]) {
705*c8dee2aaSAndroid Build Coastguard Worker     auto data = GetResourceAsData(path);
706*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
707*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Failed to get resource %s", path);
708*c8dee2aaSAndroid Build Coastguard Worker         return;
709*c8dee2aaSAndroid Build Coastguard Worker     }
710*c8dee2aaSAndroid Build Coastguard Worker 
711*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !SkCodec::MakeFromData(data));
712*c8dee2aaSAndroid Build Coastguard Worker }
713*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_Empty,r)714*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_Empty, r) {
715*c8dee2aaSAndroid Build Coastguard Worker     if (GetResourcePath().isEmpty()) {
716*c8dee2aaSAndroid Build Coastguard Worker         return;
717*c8dee2aaSAndroid Build Coastguard Worker     }
718*c8dee2aaSAndroid Build Coastguard Worker 
719*c8dee2aaSAndroid Build Coastguard Worker     // Test images that should not be able to create a codec
720*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-dims.gif");
721*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-embedded.ico");
722*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-width.bmp");
723*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-height.bmp");
724*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-width.jpg");
725*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-height.jpg");
726*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-width.png");
727*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-height.png");
728*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-width.wbmp");
729*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero-height.wbmp");
730*c8dee2aaSAndroid Build Coastguard Worker     // This image is an ico with an embedded mask-bmp.  This is illegal.
731*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "invalid_images/mask-bmp-ico.ico");
732*c8dee2aaSAndroid Build Coastguard Worker     // It is illegal for a webp frame to not be fully contained by the canvas.
733*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "invalid_images/invalid-offset.webp");
734*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
735*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "empty_images/zero_height.tiff");
736*c8dee2aaSAndroid Build Coastguard Worker #endif
737*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "invalid_images/b37623797.ico");
738*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "invalid_images/osfuzz6295.webp");
739*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "invalid_images/osfuzz6288.bmp");
740*c8dee2aaSAndroid Build Coastguard Worker     test_invalid(r, "invalid_images/ossfuzz6347");
741*c8dee2aaSAndroid Build Coastguard Worker }
742*c8dee2aaSAndroid Build Coastguard Worker 
743*c8dee2aaSAndroid Build Coastguard Worker #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
744*c8dee2aaSAndroid Build Coastguard Worker 
745*c8dee2aaSAndroid Build Coastguard Worker #ifndef SK_PNG_DISABLE_TESTS   // reading chunks does not work properly with older versions.
746*c8dee2aaSAndroid Build Coastguard Worker                                // It does not appear that anyone in Google3 is reading chunks.
747*c8dee2aaSAndroid Build Coastguard Worker 
codex_test_write_fn(png_structp png_ptr,png_bytep data,png_size_t len)748*c8dee2aaSAndroid Build Coastguard Worker static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
749*c8dee2aaSAndroid Build Coastguard Worker     SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
750*c8dee2aaSAndroid Build Coastguard Worker     if (!sk_stream->write(data, len)) {
751*c8dee2aaSAndroid Build Coastguard Worker         png_error(png_ptr, "sk_write_fn Error!");
752*c8dee2aaSAndroid Build Coastguard Worker     }
753*c8dee2aaSAndroid Build Coastguard Worker }
754*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_pngChunkReader,r)755*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_pngChunkReader, r) {
756*c8dee2aaSAndroid Build Coastguard Worker     // Create a bitmap for hashing. Use unpremul RGBA for libpng.
757*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
758*c8dee2aaSAndroid Build Coastguard Worker     const int w = 1;
759*c8dee2aaSAndroid Build Coastguard Worker     const int h = 1;
760*c8dee2aaSAndroid Build Coastguard Worker     const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType,
761*c8dee2aaSAndroid Build Coastguard Worker                                                  kUnpremul_SkAlphaType);
762*c8dee2aaSAndroid Build Coastguard Worker     bm.setInfo(bmInfo);
763*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels();
764*c8dee2aaSAndroid Build Coastguard Worker     bm.eraseColor(SK_ColorBLUE);
765*c8dee2aaSAndroid Build Coastguard Worker     SkMD5::Digest goodDigest = md5(bm);
766*c8dee2aaSAndroid Build Coastguard Worker 
767*c8dee2aaSAndroid Build Coastguard Worker     // Write to a png file.
768*c8dee2aaSAndroid Build Coastguard Worker     png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
769*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, png);
770*c8dee2aaSAndroid Build Coastguard Worker     if (!png) {
771*c8dee2aaSAndroid Build Coastguard Worker         return;
772*c8dee2aaSAndroid Build Coastguard Worker     }
773*c8dee2aaSAndroid Build Coastguard Worker 
774*c8dee2aaSAndroid Build Coastguard Worker     png_infop info = png_create_info_struct(png);
775*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, info);
776*c8dee2aaSAndroid Build Coastguard Worker     if (!info) {
777*c8dee2aaSAndroid Build Coastguard Worker         png_destroy_write_struct(&png, nullptr);
778*c8dee2aaSAndroid Build Coastguard Worker         return;
779*c8dee2aaSAndroid Build Coastguard Worker     }
780*c8dee2aaSAndroid Build Coastguard Worker 
781*c8dee2aaSAndroid Build Coastguard Worker     if (setjmp(png_jmpbuf(png))) {
782*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "failed writing png");
783*c8dee2aaSAndroid Build Coastguard Worker         png_destroy_write_struct(&png, &info);
784*c8dee2aaSAndroid Build Coastguard Worker         return;
785*c8dee2aaSAndroid Build Coastguard Worker     }
786*c8dee2aaSAndroid Build Coastguard Worker 
787*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream wStream;
788*c8dee2aaSAndroid Build Coastguard Worker     png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr);
789*c8dee2aaSAndroid Build Coastguard Worker 
790*c8dee2aaSAndroid Build Coastguard Worker     png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8,
791*c8dee2aaSAndroid Build Coastguard Worker                  PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
792*c8dee2aaSAndroid Build Coastguard Worker                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
793*c8dee2aaSAndroid Build Coastguard Worker 
794*c8dee2aaSAndroid Build Coastguard Worker #define PNG_BYTES(str) reinterpret_cast<png_byte*>(const_cast<char*>(str))
795*c8dee2aaSAndroid Build Coastguard Worker 
796*c8dee2aaSAndroid Build Coastguard Worker     // Create some chunks that match the Android framework's use.
797*c8dee2aaSAndroid Build Coastguard Worker     static png_unknown_chunk gUnknowns[] = {
798*c8dee2aaSAndroid Build Coastguard Worker         { "npOl", PNG_BYTES("outline"), sizeof("outline"), PNG_HAVE_IHDR },
799*c8dee2aaSAndroid Build Coastguard Worker         { "npLb", PNG_BYTES("layoutBounds"), sizeof("layoutBounds"), PNG_HAVE_IHDR },
800*c8dee2aaSAndroid Build Coastguard Worker         { "npTc", PNG_BYTES("ninePatchData"), sizeof("ninePatchData"), PNG_HAVE_IHDR },
801*c8dee2aaSAndroid Build Coastguard Worker     };
802*c8dee2aaSAndroid Build Coastguard Worker 
803*c8dee2aaSAndroid Build Coastguard Worker     png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, PNG_BYTES("npOl\0npLb\0npTc\0"), 3);
804*c8dee2aaSAndroid Build Coastguard Worker     png_set_unknown_chunks(png, info, gUnknowns, std::size(gUnknowns));
805*c8dee2aaSAndroid Build Coastguard Worker #if PNG_LIBPNG_VER < 10600
806*c8dee2aaSAndroid Build Coastguard Worker     /* Deal with unknown chunk location bug in 1.5.x and earlier */
807*c8dee2aaSAndroid Build Coastguard Worker     png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR);
808*c8dee2aaSAndroid Build Coastguard Worker     png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR);
809*c8dee2aaSAndroid Build Coastguard Worker #endif
810*c8dee2aaSAndroid Build Coastguard Worker 
811*c8dee2aaSAndroid Build Coastguard Worker     png_write_info(png, info);
812*c8dee2aaSAndroid Build Coastguard Worker 
813*c8dee2aaSAndroid Build Coastguard Worker     for (int j = 0; j < h; j++) {
814*c8dee2aaSAndroid Build Coastguard Worker         png_bytep row = (png_bytep)(bm.getAddr(0, j));
815*c8dee2aaSAndroid Build Coastguard Worker         png_write_rows(png, &row, 1);
816*c8dee2aaSAndroid Build Coastguard Worker     }
817*c8dee2aaSAndroid Build Coastguard Worker     png_write_end(png, info);
818*c8dee2aaSAndroid Build Coastguard Worker     png_destroy_write_struct(&png, &info);
819*c8dee2aaSAndroid Build Coastguard Worker 
820*c8dee2aaSAndroid Build Coastguard Worker     class ChunkReader : public SkPngChunkReader {
821*c8dee2aaSAndroid Build Coastguard Worker     public:
822*c8dee2aaSAndroid Build Coastguard Worker         ChunkReader(skiatest::Reporter* r)
823*c8dee2aaSAndroid Build Coastguard Worker             : fReporter(r)
824*c8dee2aaSAndroid Build Coastguard Worker         {
825*c8dee2aaSAndroid Build Coastguard Worker             this->reset();
826*c8dee2aaSAndroid Build Coastguard Worker         }
827*c8dee2aaSAndroid Build Coastguard Worker 
828*c8dee2aaSAndroid Build Coastguard Worker         bool readChunk(const char tag[], const void* data, size_t length) override {
829*c8dee2aaSAndroid Build Coastguard Worker             for (size_t i = 0; i < std::size(gUnknowns); ++i) {
830*c8dee2aaSAndroid Build Coastguard Worker                 if (!strcmp(tag, (const char*) gUnknowns[i].name)) {
831*c8dee2aaSAndroid Build Coastguard Worker                     // Tag matches. This should have been the first time we see it.
832*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(fReporter, !fSeen[i]);
833*c8dee2aaSAndroid Build Coastguard Worker                     fSeen[i] = true;
834*c8dee2aaSAndroid Build Coastguard Worker 
835*c8dee2aaSAndroid Build Coastguard Worker                     // Data and length should match
836*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(fReporter, length == gUnknowns[i].size);
837*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(fReporter, !strcmp((const char*) data,
838*c8dee2aaSAndroid Build Coastguard Worker                                                        (const char*) gUnknowns[i].data));
839*c8dee2aaSAndroid Build Coastguard Worker                     return true;
840*c8dee2aaSAndroid Build Coastguard Worker                 }
841*c8dee2aaSAndroid Build Coastguard Worker             }
842*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(fReporter, "Saw an unexpected unknown chunk.");
843*c8dee2aaSAndroid Build Coastguard Worker             return true;
844*c8dee2aaSAndroid Build Coastguard Worker         }
845*c8dee2aaSAndroid Build Coastguard Worker 
846*c8dee2aaSAndroid Build Coastguard Worker         bool allHaveBeenSeen() {
847*c8dee2aaSAndroid Build Coastguard Worker             bool ret = true;
848*c8dee2aaSAndroid Build Coastguard Worker             for (auto seen : fSeen) {
849*c8dee2aaSAndroid Build Coastguard Worker                 ret &= seen;
850*c8dee2aaSAndroid Build Coastguard Worker             }
851*c8dee2aaSAndroid Build Coastguard Worker             return ret;
852*c8dee2aaSAndroid Build Coastguard Worker         }
853*c8dee2aaSAndroid Build Coastguard Worker 
854*c8dee2aaSAndroid Build Coastguard Worker         void reset() {
855*c8dee2aaSAndroid Build Coastguard Worker             sk_bzero(fSeen, sizeof(fSeen));
856*c8dee2aaSAndroid Build Coastguard Worker         }
857*c8dee2aaSAndroid Build Coastguard Worker 
858*c8dee2aaSAndroid Build Coastguard Worker     private:
859*c8dee2aaSAndroid Build Coastguard Worker         skiatest::Reporter* fReporter;  // Unowned
860*c8dee2aaSAndroid Build Coastguard Worker         bool fSeen[3];
861*c8dee2aaSAndroid Build Coastguard Worker     };
862*c8dee2aaSAndroid Build Coastguard Worker 
863*c8dee2aaSAndroid Build Coastguard Worker     ChunkReader chunkReader(r);
864*c8dee2aaSAndroid Build Coastguard Worker 
865*c8dee2aaSAndroid Build Coastguard Worker     // Now read the file with SkCodec.
866*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(wStream.detachAsData(), &chunkReader));
867*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
868*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
869*c8dee2aaSAndroid Build Coastguard Worker         return;
870*c8dee2aaSAndroid Build Coastguard Worker     }
871*c8dee2aaSAndroid Build Coastguard Worker 
872*c8dee2aaSAndroid Build Coastguard Worker     // Now compare to the original.
873*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap decodedBm;
874*c8dee2aaSAndroid Build Coastguard Worker     decodedBm.setInfo(codec->getInfo());
875*c8dee2aaSAndroid Build Coastguard Worker     decodedBm.allocPixels();
876*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(),
877*c8dee2aaSAndroid Build Coastguard Worker                                               decodedBm.rowBytes());
878*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
879*c8dee2aaSAndroid Build Coastguard Worker 
880*c8dee2aaSAndroid Build Coastguard Worker     if (decodedBm.colorType() != bm.colorType()) {
881*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap tmp;
882*c8dee2aaSAndroid Build Coastguard Worker         bool     success = ToolUtils::copy_to(&tmp, bm.colorType(), decodedBm);
883*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, success);
884*c8dee2aaSAndroid Build Coastguard Worker         if (!success) {
885*c8dee2aaSAndroid Build Coastguard Worker             return;
886*c8dee2aaSAndroid Build Coastguard Worker         }
887*c8dee2aaSAndroid Build Coastguard Worker 
888*c8dee2aaSAndroid Build Coastguard Worker         tmp.swap(decodedBm);
889*c8dee2aaSAndroid Build Coastguard Worker     }
890*c8dee2aaSAndroid Build Coastguard Worker 
891*c8dee2aaSAndroid Build Coastguard Worker     compare_to_good_digest(r, goodDigest, decodedBm);
892*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen());
893*c8dee2aaSAndroid Build Coastguard Worker 
894*c8dee2aaSAndroid Build Coastguard Worker     // Decoding again will read the chunks again.
895*c8dee2aaSAndroid Build Coastguard Worker     chunkReader.reset();
896*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen());
897*c8dee2aaSAndroid Build Coastguard Worker     result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes());
898*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
899*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen());
900*c8dee2aaSAndroid Build Coastguard Worker }
901*c8dee2aaSAndroid Build Coastguard Worker #endif // SK_PNG_DISABLE_TESTS
902*c8dee2aaSAndroid Build Coastguard Worker #endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
903*c8dee2aaSAndroid Build Coastguard Worker 
904*c8dee2aaSAndroid Build Coastguard Worker // Stream that can only peek up to a limit
905*c8dee2aaSAndroid Build Coastguard Worker class LimitedPeekingMemStream : public SkStream {
906*c8dee2aaSAndroid Build Coastguard Worker public:
LimitedPeekingMemStream(sk_sp<SkData> data,size_t limit)907*c8dee2aaSAndroid Build Coastguard Worker     LimitedPeekingMemStream(sk_sp<SkData> data, size_t limit)
908*c8dee2aaSAndroid Build Coastguard Worker         : fStream(std::move(data))
909*c8dee2aaSAndroid Build Coastguard Worker         , fLimit(limit) {}
910*c8dee2aaSAndroid Build Coastguard Worker 
peek(void * buf,size_t bytes) const911*c8dee2aaSAndroid Build Coastguard Worker     size_t peek(void* buf, size_t bytes) const override {
912*c8dee2aaSAndroid Build Coastguard Worker         return fStream.peek(buf, std::min(bytes, fLimit));
913*c8dee2aaSAndroid Build Coastguard Worker     }
read(void * buf,size_t bytes)914*c8dee2aaSAndroid Build Coastguard Worker     size_t read(void* buf, size_t bytes) override {
915*c8dee2aaSAndroid Build Coastguard Worker         return fStream.read(buf, bytes);
916*c8dee2aaSAndroid Build Coastguard Worker     }
rewind()917*c8dee2aaSAndroid Build Coastguard Worker     bool rewind() override {
918*c8dee2aaSAndroid Build Coastguard Worker         return fStream.rewind();
919*c8dee2aaSAndroid Build Coastguard Worker     }
isAtEnd() const920*c8dee2aaSAndroid Build Coastguard Worker     bool isAtEnd() const override {
921*c8dee2aaSAndroid Build Coastguard Worker         return fStream.isAtEnd();
922*c8dee2aaSAndroid Build Coastguard Worker     }
923*c8dee2aaSAndroid Build Coastguard Worker private:
924*c8dee2aaSAndroid Build Coastguard Worker     SkMemoryStream fStream;
925*c8dee2aaSAndroid Build Coastguard Worker     const size_t   fLimit;
926*c8dee2aaSAndroid Build Coastguard Worker };
927*c8dee2aaSAndroid Build Coastguard Worker 
928*c8dee2aaSAndroid Build Coastguard Worker // Disable RAW tests for Win32.
929*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
930*c8dee2aaSAndroid Build Coastguard Worker // Test that the RawCodec works also for not asset stream. This will test the code path using
931*c8dee2aaSAndroid Build Coastguard Worker // SkRawBufferedStream instead of SkRawAssetStream.
DEF_TEST(Codec_raw_notseekable,r)932*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_raw_notseekable, r) {
933*c8dee2aaSAndroid Build Coastguard Worker     constexpr char path[] = "images/dng_with_preview.dng";
934*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data(GetResourceAsData(path));
935*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
936*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing resource '%s'\n", path);
937*c8dee2aaSAndroid Build Coastguard Worker         return;
938*c8dee2aaSAndroid Build Coastguard Worker     }
939*c8dee2aaSAndroid Build Coastguard Worker 
940*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
941*c8dee2aaSAndroid Build Coastguard Worker                                            std::make_unique<NotAssetMemStream>(std::move(data))));
942*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
943*c8dee2aaSAndroid Build Coastguard Worker 
944*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
945*c8dee2aaSAndroid Build Coastguard Worker }
946*c8dee2aaSAndroid Build Coastguard Worker #endif
947*c8dee2aaSAndroid Build Coastguard Worker 
948*c8dee2aaSAndroid Build Coastguard Worker // Test that even if webp_parse_header fails to peek enough, it will fall back to read()
949*c8dee2aaSAndroid Build Coastguard Worker // + rewind() and succeed.
DEF_TEST(Codec_webp_peek,r)950*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_webp_peek, r) {
951*c8dee2aaSAndroid Build Coastguard Worker     constexpr char path[] = "images/baby_tux.webp";
952*c8dee2aaSAndroid Build Coastguard Worker     auto data = GetResourceAsData(path);
953*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
954*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing resource '%s'\n", path);
955*c8dee2aaSAndroid Build Coastguard Worker         return;
956*c8dee2aaSAndroid Build Coastguard Worker     }
957*c8dee2aaSAndroid Build Coastguard Worker 
958*c8dee2aaSAndroid Build Coastguard Worker     // The limit is less than webp needs to peek or read.
959*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
960*c8dee2aaSAndroid Build Coastguard Worker                                            std::make_unique<LimitedPeekingMemStream>(data, 25)));
961*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
962*c8dee2aaSAndroid Build Coastguard Worker 
963*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
964*c8dee2aaSAndroid Build Coastguard Worker 
965*c8dee2aaSAndroid Build Coastguard Worker     // Similarly, a stream which does not peek should still succeed.
966*c8dee2aaSAndroid Build Coastguard Worker     codec = SkCodec::MakeFromStream(std::make_unique<LimitedPeekingMemStream>(data, 0));
967*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
968*c8dee2aaSAndroid Build Coastguard Worker 
969*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
970*c8dee2aaSAndroid Build Coastguard Worker }
971*c8dee2aaSAndroid Build Coastguard Worker 
972*c8dee2aaSAndroid Build Coastguard Worker // SkCodec's wbmp decoder was initially unnecessarily restrictive.
973*c8dee2aaSAndroid Build Coastguard Worker // It required the second byte to be zero. The wbmp specification allows
974*c8dee2aaSAndroid Build Coastguard Worker // a couple of bits to be 1 (so long as they do not overlap with 0x9F).
975*c8dee2aaSAndroid Build Coastguard Worker // Test that SkCodec now supports an image with these bits set.
DEF_TEST(Codec_wbmp_restrictive,r)976*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_wbmp_restrictive, r) {
977*c8dee2aaSAndroid Build Coastguard Worker     const char* path = "images/mandrill.wbmp";
978*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
979*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
980*c8dee2aaSAndroid Build Coastguard Worker         return;
981*c8dee2aaSAndroid Build Coastguard Worker     }
982*c8dee2aaSAndroid Build Coastguard Worker 
983*c8dee2aaSAndroid Build Coastguard Worker     // Modify the stream to contain a second byte with some bits set.
984*c8dee2aaSAndroid Build Coastguard Worker     auto data = SkCopyStreamToData(stream.get());
985*c8dee2aaSAndroid Build Coastguard Worker     uint8_t* writeableData = static_cast<uint8_t*>(data->writable_data());
986*c8dee2aaSAndroid Build Coastguard Worker     writeableData[1] = static_cast<uint8_t>(~0x9F);
987*c8dee2aaSAndroid Build Coastguard Worker 
988*c8dee2aaSAndroid Build Coastguard Worker     // SkCodec should support this.
989*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
990*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
991*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
992*c8dee2aaSAndroid Build Coastguard Worker         return;
993*c8dee2aaSAndroid Build Coastguard Worker     }
994*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
995*c8dee2aaSAndroid Build Coastguard Worker }
996*c8dee2aaSAndroid Build Coastguard Worker 
997*c8dee2aaSAndroid Build Coastguard Worker // wbmp images have a header that can be arbitrarily large, depending on the
998*c8dee2aaSAndroid Build Coastguard Worker // size of the image. We cap the size at 65535, meaning we only need to look at
999*c8dee2aaSAndroid Build Coastguard Worker // 8 bytes to determine whether we can read the image. This is important
1000*c8dee2aaSAndroid Build Coastguard Worker // because SkCodec only passes a limited number of bytes to SkWbmpCodec to
1001*c8dee2aaSAndroid Build Coastguard Worker // determine whether the image is a wbmp.
DEF_TEST(Codec_wbmp_max_size,r)1002*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_wbmp_max_size, r) {
1003*c8dee2aaSAndroid Build Coastguard Worker     const unsigned char maxSizeWbmp[] = { 0x00, 0x00,           // Header
1004*c8dee2aaSAndroid Build Coastguard Worker                                           0x83, 0xFF, 0x7F,     // W: 65535
1005*c8dee2aaSAndroid Build Coastguard Worker                                           0x83, 0xFF, 0x7F };   // H: 65535
1006*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(new SkMemoryStream(maxSizeWbmp, sizeof(maxSizeWbmp), false));
1007*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1008*c8dee2aaSAndroid Build Coastguard Worker 
1009*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
1010*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) return;
1011*c8dee2aaSAndroid Build Coastguard Worker 
1012*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec->getInfo().width() == 65535);
1013*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec->getInfo().height() == 65535);
1014*c8dee2aaSAndroid Build Coastguard Worker 
1015*c8dee2aaSAndroid Build Coastguard Worker     // Now test an image which is too big. Any image with a larger header (i.e.
1016*c8dee2aaSAndroid Build Coastguard Worker     // has bigger width/height) is also too big.
1017*c8dee2aaSAndroid Build Coastguard Worker     const unsigned char tooBigWbmp[] = { 0x00, 0x00,           // Header
1018*c8dee2aaSAndroid Build Coastguard Worker                                          0x84, 0x80, 0x00,     // W: 65536
1019*c8dee2aaSAndroid Build Coastguard Worker                                          0x84, 0x80, 0x00 };   // H: 65536
1020*c8dee2aaSAndroid Build Coastguard Worker     stream = std::make_unique<SkMemoryStream>(tooBigWbmp, sizeof(tooBigWbmp), false);
1021*c8dee2aaSAndroid Build Coastguard Worker     codec = SkCodec::MakeFromStream(std::move(stream));
1022*c8dee2aaSAndroid Build Coastguard Worker 
1023*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !codec);
1024*c8dee2aaSAndroid Build Coastguard Worker }
1025*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_jpeg_rewind,r)1026*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_jpeg_rewind, r) {
1027*c8dee2aaSAndroid Build Coastguard Worker     const char* path = "images/mandrill_512_q075.jpg";
1028*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data(GetResourceAsData(path));
1029*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
1030*c8dee2aaSAndroid Build Coastguard Worker         return;
1031*c8dee2aaSAndroid Build Coastguard Worker     }
1032*c8dee2aaSAndroid Build Coastguard Worker 
1033*c8dee2aaSAndroid Build Coastguard Worker     data = SkData::MakeSubset(data.get(), 0, data->size() / 2);
1034*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(data));
1035*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1036*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Unable to create codec '%s'.", path);
1037*c8dee2aaSAndroid Build Coastguard Worker         return;
1038*c8dee2aaSAndroid Build Coastguard Worker     }
1039*c8dee2aaSAndroid Build Coastguard Worker 
1040*c8dee2aaSAndroid Build Coastguard Worker     const int width = codec->getInfo().width();
1041*c8dee2aaSAndroid Build Coastguard Worker     const int height = codec->getInfo().height();
1042*c8dee2aaSAndroid Build Coastguard Worker     size_t rowBytes = sizeof(SkPMColor) * width;
1043*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMalloc pixelStorage(height * rowBytes);
1044*c8dee2aaSAndroid Build Coastguard Worker 
1045*c8dee2aaSAndroid Build Coastguard Worker     // Perform a sampled decode.
1046*c8dee2aaSAndroid Build Coastguard Worker     SkAndroidCodec::AndroidOptions opts;
1047*c8dee2aaSAndroid Build Coastguard Worker     opts.fSampleSize = 12;
1048*c8dee2aaSAndroid Build Coastguard Worker     auto sampledInfo = codec->getInfo().makeWH(width / 12, height / 12);
1049*c8dee2aaSAndroid Build Coastguard Worker     auto result = codec->getAndroidPixels(sampledInfo, pixelStorage.get(), rowBytes, &opts);
1050*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
1051*c8dee2aaSAndroid Build Coastguard Worker 
1052*c8dee2aaSAndroid Build Coastguard Worker     // Rewind the codec and perform a full image decode.
1053*c8dee2aaSAndroid Build Coastguard Worker     result = codec->getPixels(codec->getInfo(), pixelStorage.get(), rowBytes);
1054*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
1055*c8dee2aaSAndroid Build Coastguard Worker 
1056*c8dee2aaSAndroid Build Coastguard Worker     // Now perform a subset decode.
1057*c8dee2aaSAndroid Build Coastguard Worker     {
1058*c8dee2aaSAndroid Build Coastguard Worker         opts.fSampleSize = 1;
1059*c8dee2aaSAndroid Build Coastguard Worker         SkIRect subset = SkIRect::MakeWH(100, 100);
1060*c8dee2aaSAndroid Build Coastguard Worker         opts.fSubset = &subset;
1061*c8dee2aaSAndroid Build Coastguard Worker         result = codec->getAndroidPixels(codec->getInfo().makeWH(100, 100), pixelStorage.get(),
1062*c8dee2aaSAndroid Build Coastguard Worker                                          rowBytes, &opts);
1063*c8dee2aaSAndroid Build Coastguard Worker         // Though we only have half the data, it is enough to decode this subset.
1064*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1065*c8dee2aaSAndroid Build Coastguard Worker     }
1066*c8dee2aaSAndroid Build Coastguard Worker 
1067*c8dee2aaSAndroid Build Coastguard Worker     // Perform another full image decode.  ASAN will detect if we look at the subset when it is
1068*c8dee2aaSAndroid Build Coastguard Worker     // out of scope.  This would happen if we depend on the old state in the codec.
1069*c8dee2aaSAndroid Build Coastguard Worker     // This tests two layers of bugs: both SkJpegCodec::readRows and SkCodec::fillIncompleteImage
1070*c8dee2aaSAndroid Build Coastguard Worker     // used to look at the old subset.
1071*c8dee2aaSAndroid Build Coastguard Worker     opts.fSubset = nullptr;
1072*c8dee2aaSAndroid Build Coastguard Worker     result = codec->getAndroidPixels(codec->getInfo(), pixelStorage.get(), rowBytes, &opts);
1073*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
1074*c8dee2aaSAndroid Build Coastguard Worker }
1075*c8dee2aaSAndroid Build Coastguard Worker 
check_color_xform(skiatest::Reporter * r,const char * path)1076*c8dee2aaSAndroid Build Coastguard Worker static void check_color_xform(skiatest::Reporter* r, const char* path) {
1077*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(GetResourceAsStream(path)));
1078*c8dee2aaSAndroid Build Coastguard Worker 
1079*c8dee2aaSAndroid Build Coastguard Worker     SkAndroidCodec::AndroidOptions opts;
1080*c8dee2aaSAndroid Build Coastguard Worker     opts.fSampleSize = 3;
1081*c8dee2aaSAndroid Build Coastguard Worker     const int subsetWidth = codec->getInfo().width() / 2;
1082*c8dee2aaSAndroid Build Coastguard Worker     const int subsetHeight = codec->getInfo().height() / 2;
1083*c8dee2aaSAndroid Build Coastguard Worker     SkIRect subset = SkIRect::MakeWH(subsetWidth, subsetHeight);
1084*c8dee2aaSAndroid Build Coastguard Worker     opts.fSubset = &subset;
1085*c8dee2aaSAndroid Build Coastguard Worker 
1086*c8dee2aaSAndroid Build Coastguard Worker     const int dstWidth = subsetWidth / opts.fSampleSize;
1087*c8dee2aaSAndroid Build Coastguard Worker     const int dstHeight = subsetHeight / opts.fSampleSize;
1088*c8dee2aaSAndroid Build Coastguard Worker     auto colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
1089*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo dstInfo = codec->getInfo().makeWH(dstWidth, dstHeight)
1090*c8dee2aaSAndroid Build Coastguard Worker                                           .makeColorType(kN32_SkColorType)
1091*c8dee2aaSAndroid Build Coastguard Worker                                           .makeColorSpace(colorSpace);
1092*c8dee2aaSAndroid Build Coastguard Worker 
1093*c8dee2aaSAndroid Build Coastguard Worker     size_t rowBytes = dstInfo.minRowBytes();
1094*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMalloc pixelStorage(dstInfo.computeByteSize(rowBytes));
1095*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result = codec->getAndroidPixels(dstInfo, pixelStorage.get(), rowBytes, &opts);
1096*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1097*c8dee2aaSAndroid Build Coastguard Worker }
1098*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_ColorXform,r)1099*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_ColorXform, r) {
1100*c8dee2aaSAndroid Build Coastguard Worker     check_color_xform(r, "images/mandrill_512_q075.jpg");
1101*c8dee2aaSAndroid Build Coastguard Worker     check_color_xform(r, "images/mandrill_512.png");
1102*c8dee2aaSAndroid Build Coastguard Worker }
1103*c8dee2aaSAndroid Build Coastguard Worker 
color_type_match(SkColorType origColorType,SkColorType codecColorType)1104*c8dee2aaSAndroid Build Coastguard Worker static bool color_type_match(SkColorType origColorType, SkColorType codecColorType) {
1105*c8dee2aaSAndroid Build Coastguard Worker     switch (origColorType) {
1106*c8dee2aaSAndroid Build Coastguard Worker         case kRGBA_8888_SkColorType:
1107*c8dee2aaSAndroid Build Coastguard Worker         case kBGRA_8888_SkColorType:
1108*c8dee2aaSAndroid Build Coastguard Worker             return kRGBA_8888_SkColorType == codecColorType ||
1109*c8dee2aaSAndroid Build Coastguard Worker                    kBGRA_8888_SkColorType == codecColorType;
1110*c8dee2aaSAndroid Build Coastguard Worker         default:
1111*c8dee2aaSAndroid Build Coastguard Worker             return origColorType == codecColorType;
1112*c8dee2aaSAndroid Build Coastguard Worker     }
1113*c8dee2aaSAndroid Build Coastguard Worker }
1114*c8dee2aaSAndroid Build Coastguard Worker 
alpha_type_match(SkAlphaType origAlphaType,SkAlphaType codecAlphaType)1115*c8dee2aaSAndroid Build Coastguard Worker static bool alpha_type_match(SkAlphaType origAlphaType, SkAlphaType codecAlphaType) {
1116*c8dee2aaSAndroid Build Coastguard Worker     switch (origAlphaType) {
1117*c8dee2aaSAndroid Build Coastguard Worker         case kUnpremul_SkAlphaType:
1118*c8dee2aaSAndroid Build Coastguard Worker         case kPremul_SkAlphaType:
1119*c8dee2aaSAndroid Build Coastguard Worker             return kUnpremul_SkAlphaType == codecAlphaType ||
1120*c8dee2aaSAndroid Build Coastguard Worker                     kPremul_SkAlphaType == codecAlphaType;
1121*c8dee2aaSAndroid Build Coastguard Worker         default:
1122*c8dee2aaSAndroid Build Coastguard Worker             return origAlphaType == codecAlphaType;
1123*c8dee2aaSAndroid Build Coastguard Worker     }
1124*c8dee2aaSAndroid Build Coastguard Worker }
1125*c8dee2aaSAndroid Build Coastguard Worker 
check_round_trip(skiatest::Reporter * r,SkCodec * origCodec,const SkImageInfo & info)1126*c8dee2aaSAndroid Build Coastguard Worker static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const SkImageInfo& info) {
1127*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm1;
1128*c8dee2aaSAndroid Build Coastguard Worker     bm1.allocPixels(info);
1129*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes());
1130*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1131*c8dee2aaSAndroid Build Coastguard Worker 
1132*c8dee2aaSAndroid Build Coastguard Worker     // Encode the image to png.
1133*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream stream;
1134*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT_RELEASE(SkPngEncoder::Encode(&stream, bm1.pixmap(), {}));
1135*c8dee2aaSAndroid Build Coastguard Worker 
1136*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(stream.detachAsData()));
1137*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, color_type_match(info.colorType(), codec->getInfo().colorType()));
1138*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, alpha_type_match(info.alphaType(), codec->getInfo().alphaType()));
1139*c8dee2aaSAndroid Build Coastguard Worker 
1140*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm2;
1141*c8dee2aaSAndroid Build Coastguard Worker     bm2.allocPixels(info);
1142*c8dee2aaSAndroid Build Coastguard Worker     result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes());
1143*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1144*c8dee2aaSAndroid Build Coastguard Worker 
1145*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, md5(bm1) == md5(bm2));
1146*c8dee2aaSAndroid Build Coastguard Worker }
1147*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_PngRoundTrip,r)1148*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_PngRoundTrip, r) {
1149*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkCodec::MakeFromStream(GetResourceAsStream("images/mandrill_512_q075.jpg"));
1150*c8dee2aaSAndroid Build Coastguard Worker 
1151*c8dee2aaSAndroid Build Coastguard Worker     SkColorType colorTypesOpaque[] = {
1152*c8dee2aaSAndroid Build Coastguard Worker             kRGB_565_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType
1153*c8dee2aaSAndroid Build Coastguard Worker     };
1154*c8dee2aaSAndroid Build Coastguard Worker     for (SkColorType colorType : colorTypesOpaque) {
1155*c8dee2aaSAndroid Build Coastguard Worker         SkImageInfo newInfo = codec->getInfo().makeColorType(colorType);
1156*c8dee2aaSAndroid Build Coastguard Worker         check_round_trip(r, codec.get(), newInfo);
1157*c8dee2aaSAndroid Build Coastguard Worker     }
1158*c8dee2aaSAndroid Build Coastguard Worker 
1159*c8dee2aaSAndroid Build Coastguard Worker     codec = SkCodec::MakeFromStream(GetResourceAsStream("images/grayscale.jpg"));
1160*c8dee2aaSAndroid Build Coastguard Worker     check_round_trip(r, codec.get(), codec->getInfo());
1161*c8dee2aaSAndroid Build Coastguard Worker 
1162*c8dee2aaSAndroid Build Coastguard Worker     codec = SkCodec::MakeFromStream(GetResourceAsStream("images/yellow_rose.png"));
1163*c8dee2aaSAndroid Build Coastguard Worker 
1164*c8dee2aaSAndroid Build Coastguard Worker     SkColorType colorTypesWithAlpha[] = {
1165*c8dee2aaSAndroid Build Coastguard Worker             kRGBA_8888_SkColorType, kBGRA_8888_SkColorType
1166*c8dee2aaSAndroid Build Coastguard Worker     };
1167*c8dee2aaSAndroid Build Coastguard Worker     SkAlphaType alphaTypes[] = {
1168*c8dee2aaSAndroid Build Coastguard Worker             kUnpremul_SkAlphaType, kPremul_SkAlphaType
1169*c8dee2aaSAndroid Build Coastguard Worker     };
1170*c8dee2aaSAndroid Build Coastguard Worker     for (SkColorType colorType : colorTypesWithAlpha) {
1171*c8dee2aaSAndroid Build Coastguard Worker         for (SkAlphaType alphaType : alphaTypes) {
1172*c8dee2aaSAndroid Build Coastguard Worker             // Set color space to nullptr because color correct premultiplies do not round trip.
1173*c8dee2aaSAndroid Build Coastguard Worker             SkImageInfo newInfo = codec->getInfo().makeColorType(colorType)
1174*c8dee2aaSAndroid Build Coastguard Worker                                                   .makeAlphaType(alphaType)
1175*c8dee2aaSAndroid Build Coastguard Worker                                                   .makeColorSpace(nullptr);
1176*c8dee2aaSAndroid Build Coastguard Worker             check_round_trip(r, codec.get(), newInfo);
1177*c8dee2aaSAndroid Build Coastguard Worker         }
1178*c8dee2aaSAndroid Build Coastguard Worker     }
1179*c8dee2aaSAndroid Build Coastguard Worker 
1180*c8dee2aaSAndroid Build Coastguard Worker     codec = SkCodec::MakeFromStream(GetResourceAsStream("images/index8.png"));
1181*c8dee2aaSAndroid Build Coastguard Worker 
1182*c8dee2aaSAndroid Build Coastguard Worker     for (SkAlphaType alphaType : alphaTypes) {
1183*c8dee2aaSAndroid Build Coastguard Worker         SkImageInfo newInfo = codec->getInfo().makeAlphaType(alphaType)
1184*c8dee2aaSAndroid Build Coastguard Worker                                               .makeColorSpace(nullptr);
1185*c8dee2aaSAndroid Build Coastguard Worker         check_round_trip(r, codec.get(), newInfo);
1186*c8dee2aaSAndroid Build Coastguard Worker     }
1187*c8dee2aaSAndroid Build Coastguard Worker }
1188*c8dee2aaSAndroid Build Coastguard Worker 
test_conversion_possible(skiatest::Reporter * r,const char * path,bool supportsScanlineDecoder,bool supportsIncrementalDecoder)1189*c8dee2aaSAndroid Build Coastguard Worker static void test_conversion_possible(skiatest::Reporter* r, const char* path,
1190*c8dee2aaSAndroid Build Coastguard Worker                                      bool supportsScanlineDecoder,
1191*c8dee2aaSAndroid Build Coastguard Worker                                      bool supportsIncrementalDecoder) {
1192*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
1193*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
1194*c8dee2aaSAndroid Build Coastguard Worker         return;
1195*c8dee2aaSAndroid Build Coastguard Worker     }
1196*c8dee2aaSAndroid Build Coastguard Worker 
1197*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1198*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1199*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "failed to create a codec for %s", path);
1200*c8dee2aaSAndroid Build Coastguard Worker         return;
1201*c8dee2aaSAndroid Build Coastguard Worker     }
1202*c8dee2aaSAndroid Build Coastguard Worker 
1203*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo infoF16 = codec->getInfo().makeColorType(kRGBA_F16_SkColorType);
1204*c8dee2aaSAndroid Build Coastguard Worker 
1205*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1206*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(infoF16);
1207*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
1208*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1209*c8dee2aaSAndroid Build Coastguard Worker 
1210*c8dee2aaSAndroid Build Coastguard Worker     result = codec->startScanlineDecode(infoF16);
1211*c8dee2aaSAndroid Build Coastguard Worker     if (supportsScanlineDecoder) {
1212*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1213*c8dee2aaSAndroid Build Coastguard Worker     } else {
1214*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kUnimplemented == result
1215*c8dee2aaSAndroid Build Coastguard Worker                         || SkCodec::kSuccess == result);
1216*c8dee2aaSAndroid Build Coastguard Worker     }
1217*c8dee2aaSAndroid Build Coastguard Worker 
1218*c8dee2aaSAndroid Build Coastguard Worker     result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
1219*c8dee2aaSAndroid Build Coastguard Worker     if (supportsIncrementalDecoder) {
1220*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1221*c8dee2aaSAndroid Build Coastguard Worker     } else {
1222*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kUnimplemented == result
1223*c8dee2aaSAndroid Build Coastguard Worker                         || SkCodec::kSuccess == result);
1224*c8dee2aaSAndroid Build Coastguard Worker     }
1225*c8dee2aaSAndroid Build Coastguard Worker 
1226*c8dee2aaSAndroid Build Coastguard Worker     infoF16 = infoF16.makeColorSpace(infoF16.colorSpace()->makeLinearGamma());
1227*c8dee2aaSAndroid Build Coastguard Worker     result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
1228*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1229*c8dee2aaSAndroid Build Coastguard Worker     result = codec->startScanlineDecode(infoF16);
1230*c8dee2aaSAndroid Build Coastguard Worker     if (supportsScanlineDecoder) {
1231*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1232*c8dee2aaSAndroid Build Coastguard Worker     } else {
1233*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
1234*c8dee2aaSAndroid Build Coastguard Worker     }
1235*c8dee2aaSAndroid Build Coastguard Worker 
1236*c8dee2aaSAndroid Build Coastguard Worker     result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
1237*c8dee2aaSAndroid Build Coastguard Worker     if (supportsIncrementalDecoder) {
1238*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1239*c8dee2aaSAndroid Build Coastguard Worker     } else {
1240*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
1241*c8dee2aaSAndroid Build Coastguard Worker     }
1242*c8dee2aaSAndroid Build Coastguard Worker }
1243*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_F16ConversionPossible,r)1244*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_F16ConversionPossible, r) {
1245*c8dee2aaSAndroid Build Coastguard Worker     test_conversion_possible(r, "images/color_wheel.webp", false, false);
1246*c8dee2aaSAndroid Build Coastguard Worker     test_conversion_possible(r, "images/mandrill_512_q075.jpg", true, false);
1247*c8dee2aaSAndroid Build Coastguard Worker     test_conversion_possible(r, "images/yellow_rose.png", false, true);
1248*c8dee2aaSAndroid Build Coastguard Worker }
1249*c8dee2aaSAndroid Build Coastguard Worker 
decode_frame(skiatest::Reporter * r,SkCodec * codec,size_t frame)1250*c8dee2aaSAndroid Build Coastguard Worker static void decode_frame(skiatest::Reporter* r, SkCodec* codec, size_t frame) {
1251*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1252*c8dee2aaSAndroid Build Coastguard Worker     auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1253*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
1254*c8dee2aaSAndroid Build Coastguard Worker 
1255*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Options opts;
1256*c8dee2aaSAndroid Build Coastguard Worker     opts.fFrameIndex = frame;
1257*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(info,
1258*c8dee2aaSAndroid Build Coastguard Worker             bm.getPixels(), bm.rowBytes(), &opts));
1259*c8dee2aaSAndroid Build Coastguard Worker }
1260*c8dee2aaSAndroid Build Coastguard Worker 
1261*c8dee2aaSAndroid Build Coastguard Worker // For an animated GIF, we should only read enough to decode frame 0 if the
1262*c8dee2aaSAndroid Build Coastguard Worker // client never calls getFrameInfo and only decodes frame 0.
DEF_TEST(Codec_skipFullParse,r)1263*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_skipFullParse, r) {
1264*c8dee2aaSAndroid Build Coastguard Worker     auto path = "images/test640x479.gif";
1265*c8dee2aaSAndroid Build Coastguard Worker     auto streamObj = GetResourceAsStream(path);
1266*c8dee2aaSAndroid Build Coastguard Worker     if (!streamObj) {
1267*c8dee2aaSAndroid Build Coastguard Worker         return;
1268*c8dee2aaSAndroid Build Coastguard Worker     }
1269*c8dee2aaSAndroid Build Coastguard Worker     SkStream* stream = streamObj.get();
1270*c8dee2aaSAndroid Build Coastguard Worker 
1271*c8dee2aaSAndroid Build Coastguard Worker     // Note that we cheat and hold on to the stream pointer, but SkCodec will
1272*c8dee2aaSAndroid Build Coastguard Worker     // take ownership. We will not refer to the stream after the SkCodec
1273*c8dee2aaSAndroid Build Coastguard Worker     // deletes it.
1274*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(streamObj)));
1275*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1276*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Failed to create codec for %s", path);
1277*c8dee2aaSAndroid Build Coastguard Worker         return;
1278*c8dee2aaSAndroid Build Coastguard Worker     }
1279*c8dee2aaSAndroid Build Coastguard Worker 
1280*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, stream->hasPosition());
1281*c8dee2aaSAndroid Build Coastguard Worker     const size_t sizePosition = stream->getPosition();
1282*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, stream->hasLength() && sizePosition < stream->getLength());
1283*c8dee2aaSAndroid Build Coastguard Worker 
1284*c8dee2aaSAndroid Build Coastguard Worker     // This should read more of the stream, but not the whole stream.
1285*c8dee2aaSAndroid Build Coastguard Worker     decode_frame(r, codec.get(), 0);
1286*c8dee2aaSAndroid Build Coastguard Worker     const size_t positionAfterFirstFrame = stream->getPosition();
1287*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, positionAfterFirstFrame > sizePosition
1288*c8dee2aaSAndroid Build Coastguard Worker                        && positionAfterFirstFrame < stream->getLength());
1289*c8dee2aaSAndroid Build Coastguard Worker 
1290*c8dee2aaSAndroid Build Coastguard Worker     // There is more data in the stream.
1291*c8dee2aaSAndroid Build Coastguard Worker     auto frameInfo = codec->getFrameInfo();
1292*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, frameInfo.size() == 4);
1293*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, stream->getPosition() > positionAfterFirstFrame);
1294*c8dee2aaSAndroid Build Coastguard Worker }
1295*c8dee2aaSAndroid Build Coastguard Worker 
1296*c8dee2aaSAndroid Build Coastguard Worker // Only rewinds up to a limit.
1297*c8dee2aaSAndroid Build Coastguard Worker class LimitedRewindingStream : public SkStream {
1298*c8dee2aaSAndroid Build Coastguard Worker public:
Make(const char path[],size_t limit)1299*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<SkStream> Make(const char path[], size_t limit) {
1300*c8dee2aaSAndroid Build Coastguard Worker         auto stream = GetResourceAsStream(path);
1301*c8dee2aaSAndroid Build Coastguard Worker         if (!stream) {
1302*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
1303*c8dee2aaSAndroid Build Coastguard Worker         }
1304*c8dee2aaSAndroid Build Coastguard Worker         return std::unique_ptr<SkStream>(new LimitedRewindingStream(std::move(stream), limit));
1305*c8dee2aaSAndroid Build Coastguard Worker     }
1306*c8dee2aaSAndroid Build Coastguard Worker 
read(void * buffer,size_t size)1307*c8dee2aaSAndroid Build Coastguard Worker     size_t read(void* buffer, size_t size) override {
1308*c8dee2aaSAndroid Build Coastguard Worker         const size_t bytes = fStream->read(buffer, size);
1309*c8dee2aaSAndroid Build Coastguard Worker         fPosition += bytes;
1310*c8dee2aaSAndroid Build Coastguard Worker         return bytes;
1311*c8dee2aaSAndroid Build Coastguard Worker     }
1312*c8dee2aaSAndroid Build Coastguard Worker 
isAtEnd() const1313*c8dee2aaSAndroid Build Coastguard Worker     bool isAtEnd() const override {
1314*c8dee2aaSAndroid Build Coastguard Worker         return fStream->isAtEnd();
1315*c8dee2aaSAndroid Build Coastguard Worker     }
1316*c8dee2aaSAndroid Build Coastguard Worker 
rewind()1317*c8dee2aaSAndroid Build Coastguard Worker     bool rewind() override {
1318*c8dee2aaSAndroid Build Coastguard Worker         if (fPosition <= fLimit && fStream->rewind()) {
1319*c8dee2aaSAndroid Build Coastguard Worker             fPosition = 0;
1320*c8dee2aaSAndroid Build Coastguard Worker             return true;
1321*c8dee2aaSAndroid Build Coastguard Worker         }
1322*c8dee2aaSAndroid Build Coastguard Worker 
1323*c8dee2aaSAndroid Build Coastguard Worker         return false;
1324*c8dee2aaSAndroid Build Coastguard Worker     }
1325*c8dee2aaSAndroid Build Coastguard Worker 
1326*c8dee2aaSAndroid Build Coastguard Worker private:
1327*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStream> fStream;
1328*c8dee2aaSAndroid Build Coastguard Worker     const size_t              fLimit;
1329*c8dee2aaSAndroid Build Coastguard Worker     size_t                    fPosition;
1330*c8dee2aaSAndroid Build Coastguard Worker 
LimitedRewindingStream(std::unique_ptr<SkStream> stream,size_t limit)1331*c8dee2aaSAndroid Build Coastguard Worker     LimitedRewindingStream(std::unique_ptr<SkStream> stream, size_t limit)
1332*c8dee2aaSAndroid Build Coastguard Worker         : fStream(std::move(stream))
1333*c8dee2aaSAndroid Build Coastguard Worker         , fLimit(limit)
1334*c8dee2aaSAndroid Build Coastguard Worker         , fPosition(0)
1335*c8dee2aaSAndroid Build Coastguard Worker     {
1336*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fStream);
1337*c8dee2aaSAndroid Build Coastguard Worker     }
1338*c8dee2aaSAndroid Build Coastguard Worker };
1339*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_fallBack,r)1340*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_fallBack, r) {
1341*c8dee2aaSAndroid Build Coastguard Worker     // SkAndroidCodec needs to be able to fall back to scanline decoding
1342*c8dee2aaSAndroid Build Coastguard Worker     // if incremental decoding does not work. Make sure this does not
1343*c8dee2aaSAndroid Build Coastguard Worker     // require a rewind.
1344*c8dee2aaSAndroid Build Coastguard Worker 
1345*c8dee2aaSAndroid Build Coastguard Worker     // Formats that currently do not support incremental decoding
1346*c8dee2aaSAndroid Build Coastguard Worker     auto files = {
1347*c8dee2aaSAndroid Build Coastguard Worker             "images/CMYK.jpg",
1348*c8dee2aaSAndroid Build Coastguard Worker             "images/color_wheel.ico",
1349*c8dee2aaSAndroid Build Coastguard Worker             "images/mandrill.wbmp",
1350*c8dee2aaSAndroid Build Coastguard Worker             "images/randPixels.bmp",
1351*c8dee2aaSAndroid Build Coastguard Worker             };
1352*c8dee2aaSAndroid Build Coastguard Worker     for (auto file : files) {
1353*c8dee2aaSAndroid Build Coastguard Worker         auto stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded());
1354*c8dee2aaSAndroid Build Coastguard Worker         if (!stream) {
1355*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
1356*c8dee2aaSAndroid Build Coastguard Worker             return;
1357*c8dee2aaSAndroid Build Coastguard Worker         }
1358*c8dee2aaSAndroid Build Coastguard Worker 
1359*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1360*c8dee2aaSAndroid Build Coastguard Worker         if (!codec) {
1361*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Failed to create codec for %s,", file);
1362*c8dee2aaSAndroid Build Coastguard Worker             continue;
1363*c8dee2aaSAndroid Build Coastguard Worker         }
1364*c8dee2aaSAndroid Build Coastguard Worker 
1365*c8dee2aaSAndroid Build Coastguard Worker         SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
1366*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bm;
1367*c8dee2aaSAndroid Build Coastguard Worker         bm.allocPixels(info);
1368*c8dee2aaSAndroid Build Coastguard Worker 
1369*c8dee2aaSAndroid Build Coastguard Worker         if (SkCodec::kUnimplemented != codec->startIncrementalDecode(info, bm.getPixels(),
1370*c8dee2aaSAndroid Build Coastguard Worker                 bm.rowBytes())) {
1371*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Is scanline decoding now implemented for %s?", file);
1372*c8dee2aaSAndroid Build Coastguard Worker             continue;
1373*c8dee2aaSAndroid Build Coastguard Worker         }
1374*c8dee2aaSAndroid Build Coastguard Worker 
1375*c8dee2aaSAndroid Build Coastguard Worker         // Scanline decoding should not require a rewind.
1376*c8dee2aaSAndroid Build Coastguard Worker         SkCodec::Result result = codec->startScanlineDecode(info);
1377*c8dee2aaSAndroid Build Coastguard Worker         if (SkCodec::kSuccess != result) {
1378*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Scanline decoding failed for %s with %i", file, result);
1379*c8dee2aaSAndroid Build Coastguard Worker         }
1380*c8dee2aaSAndroid Build Coastguard Worker     }
1381*c8dee2aaSAndroid Build Coastguard Worker }
1382*c8dee2aaSAndroid Build Coastguard Worker 
seek_and_decode(const char * file,std::unique_ptr<SkStream> stream,skiatest::Reporter * r)1383*c8dee2aaSAndroid Build Coastguard Worker static void seek_and_decode(const char* file, std::unique_ptr<SkStream> stream,
1384*c8dee2aaSAndroid Build Coastguard Worker                             skiatest::Reporter* r) {
1385*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
1386*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
1387*c8dee2aaSAndroid Build Coastguard Worker         return;
1388*c8dee2aaSAndroid Build Coastguard Worker     }
1389*c8dee2aaSAndroid Build Coastguard Worker 
1390*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1391*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1392*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Failed to create codec for %s,", file);
1393*c8dee2aaSAndroid Build Coastguard Worker         return;
1394*c8dee2aaSAndroid Build Coastguard Worker     }
1395*c8dee2aaSAndroid Build Coastguard Worker 
1396*c8dee2aaSAndroid Build Coastguard Worker     // Trigger reading through the stream, so that decoding the first frame will
1397*c8dee2aaSAndroid Build Coastguard Worker     // require a rewind.
1398*c8dee2aaSAndroid Build Coastguard Worker     (void) codec->getFrameCount();
1399*c8dee2aaSAndroid Build Coastguard Worker 
1400*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
1401*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1402*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
1403*c8dee2aaSAndroid Build Coastguard Worker     auto result = codec->getPixels(bm.pixmap());
1404*c8dee2aaSAndroid Build Coastguard Worker     if (result != SkCodec::kSuccess) {
1405*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Failed to decode %s with error %s", file, SkCodec::ResultToString(result));
1406*c8dee2aaSAndroid Build Coastguard Worker     }
1407*c8dee2aaSAndroid Build Coastguard Worker }
1408*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Wuffs_seek_and_decode,r)1409*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Wuffs_seek_and_decode, r) {
1410*c8dee2aaSAndroid Build Coastguard Worker     const char* file = "images/flightAnim.gif";
1411*c8dee2aaSAndroid Build Coastguard Worker     auto stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded());
1412*c8dee2aaSAndroid Build Coastguard Worker     seek_and_decode(file, std::move(stream), r);
1413*c8dee2aaSAndroid Build Coastguard Worker 
1414*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_ENABLE_ANDROID_UTILS)
1415*c8dee2aaSAndroid Build Coastguard Worker     // Test using FrontBufferedStream, as Android does
1416*c8dee2aaSAndroid Build Coastguard Worker     auto bufferedStream = android::skia::FrontBufferedStream::Make(
1417*c8dee2aaSAndroid Build Coastguard Worker             GetResourceAsStream(file), SkCodec::MinBufferedBytesNeeded());
1418*c8dee2aaSAndroid Build Coastguard Worker     seek_and_decode(file, std::move(bufferedStream), r);
1419*c8dee2aaSAndroid Build Coastguard Worker #endif
1420*c8dee2aaSAndroid Build Coastguard Worker }
1421*c8dee2aaSAndroid Build Coastguard Worker 
1422*c8dee2aaSAndroid Build Coastguard Worker // This test verifies that we fixed an assert statement that fired when reusing a png codec
1423*c8dee2aaSAndroid Build Coastguard Worker // after scaling.
DEF_TEST(Codec_reusePng,r)1424*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_reusePng, r) {
1425*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(GetResourceAsStream("images/plane.png"));
1426*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
1427*c8dee2aaSAndroid Build Coastguard Worker         return;
1428*c8dee2aaSAndroid Build Coastguard Worker     }
1429*c8dee2aaSAndroid Build Coastguard Worker 
1430*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream)));
1431*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1432*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Failed to create codec\n");
1433*c8dee2aaSAndroid Build Coastguard Worker         return;
1434*c8dee2aaSAndroid Build Coastguard Worker     }
1435*c8dee2aaSAndroid Build Coastguard Worker 
1436*c8dee2aaSAndroid Build Coastguard Worker     SkAndroidCodec::AndroidOptions opts;
1437*c8dee2aaSAndroid Build Coastguard Worker     opts.fSampleSize = 5;
1438*c8dee2aaSAndroid Build Coastguard Worker     auto size = codec->getSampledDimensions(opts.fSampleSize);
1439*c8dee2aaSAndroid Build Coastguard Worker     auto info = codec->getInfo().makeDimensions(size).makeColorType(kN32_SkColorType);
1440*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1441*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
1442*c8dee2aaSAndroid Build Coastguard Worker     auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
1443*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1444*c8dee2aaSAndroid Build Coastguard Worker 
1445*c8dee2aaSAndroid Build Coastguard Worker     info = codec->getInfo().makeColorType(kN32_SkColorType);
1446*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
1447*c8dee2aaSAndroid Build Coastguard Worker     opts.fSampleSize = 1;
1448*c8dee2aaSAndroid Build Coastguard Worker     result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
1449*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1450*c8dee2aaSAndroid Build Coastguard Worker }
1451*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_rowsDecoded,r)1452*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_rowsDecoded, r) {
1453*c8dee2aaSAndroid Build Coastguard Worker     auto file = "images/plane_interlaced.png";
1454*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(GetResourceAsStream(file));
1455*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
1456*c8dee2aaSAndroid Build Coastguard Worker         return;
1457*c8dee2aaSAndroid Build Coastguard Worker     }
1458*c8dee2aaSAndroid Build Coastguard Worker 
1459*c8dee2aaSAndroid Build Coastguard Worker     // This is enough to read the header etc, but no rows.
1460*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 99)));
1461*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1462*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Failed to create codec\n");
1463*c8dee2aaSAndroid Build Coastguard Worker         return;
1464*c8dee2aaSAndroid Build Coastguard Worker     }
1465*c8dee2aaSAndroid Build Coastguard Worker 
1466*c8dee2aaSAndroid Build Coastguard Worker     auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1467*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1468*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
1469*c8dee2aaSAndroid Build Coastguard Worker     auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
1470*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1471*c8dee2aaSAndroid Build Coastguard Worker 
1472*c8dee2aaSAndroid Build Coastguard Worker     // This is an arbitrary value. The important fact is that it is not zero, and rowsDecoded
1473*c8dee2aaSAndroid Build Coastguard Worker     // should get set to zero by incrementalDecode.
1474*c8dee2aaSAndroid Build Coastguard Worker     int rowsDecoded = 77;
1475*c8dee2aaSAndroid Build Coastguard Worker     result = codec->incrementalDecode(&rowsDecoded);
1476*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
1477*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, rowsDecoded == 0);
1478*c8dee2aaSAndroid Build Coastguard Worker }
1479*c8dee2aaSAndroid Build Coastguard Worker 
test_invalid_images(skiatest::Reporter * r,const char * path,SkCodec::Result expectedResult)1480*c8dee2aaSAndroid Build Coastguard Worker static void test_invalid_images(skiatest::Reporter* r, const char* path,
1481*c8dee2aaSAndroid Build Coastguard Worker                                 SkCodec::Result expectedResult) {
1482*c8dee2aaSAndroid Build Coastguard Worker     auto stream = GetResourceAsStream(path);
1483*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
1484*c8dee2aaSAndroid Build Coastguard Worker         return;
1485*c8dee2aaSAndroid Build Coastguard Worker     }
1486*c8dee2aaSAndroid Build Coastguard Worker 
1487*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1488*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
1489*c8dee2aaSAndroid Build Coastguard Worker 
1490*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec.get(), codec->getInfo().makeColorType(kN32_SkColorType), expectedResult,
1491*c8dee2aaSAndroid Build Coastguard Worker               nullptr);
1492*c8dee2aaSAndroid Build Coastguard Worker }
1493*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_InvalidImages,r)1494*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_InvalidImages, r) {
1495*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_images(r, "invalid_images/b33251605.bmp", SkCodec::kIncompleteInput);
1496*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_images(r, "invalid_images/bad_palette.png", SkCodec::kInvalidInput);
1497*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_images(r, "invalid_images/many-progressive-scans.jpg", SkCodec::kInvalidInput);
1498*c8dee2aaSAndroid Build Coastguard Worker 
1499*c8dee2aaSAndroid Build Coastguard Worker     // An earlier revision of this test case passed kErrorInInput (instead of
1500*c8dee2aaSAndroid Build Coastguard Worker     // kSuccess) as the third argument (expectedResult). However, after
1501*c8dee2aaSAndroid Build Coastguard Worker     // https://skia-review.googlesource.com/c/skia/+/414417 `SkWuffsCodec:
1502*c8dee2aaSAndroid Build Coastguard Worker     // ignore too much pixel data` combined with
1503*c8dee2aaSAndroid Build Coastguard Worker     // https://github.com/google/wuffs/commit/e44920d3 `Let gif "ignore too
1504*c8dee2aaSAndroid Build Coastguard Worker     // much" quirk skip lzw errors`, the codec silently accepts skbug5887.gif
1505*c8dee2aaSAndroid Build Coastguard Worker     // (without the ASAN buffer-overflow violation that lead to that test case
1506*c8dee2aaSAndroid Build Coastguard Worker     // in the first place), even though it's technically an invalid GIF.
1507*c8dee2aaSAndroid Build Coastguard Worker     //
1508*c8dee2aaSAndroid Build Coastguard Worker     // Note that, in practice, real world GIF decoders already diverge (in
1509*c8dee2aaSAndroid Build Coastguard Worker     // different ways) from the GIF specification. For compatibility, (ad hoc)
1510*c8dee2aaSAndroid Build Coastguard Worker     // implementation often trumps specification.
1511*c8dee2aaSAndroid Build Coastguard Worker     // https://github.com/google/wuffs/blob/e44920d3/test/data/artificial-gif/frame-out-of-bounds.gif.make-artificial.txt#L30-L31
1512*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_images(r, "invalid_images/skbug5887.gif", SkCodec::kSuccess);
1513*c8dee2aaSAndroid Build Coastguard Worker }
1514*c8dee2aaSAndroid Build Coastguard Worker 
test_invalid_header(skiatest::Reporter * r,const char * path)1515*c8dee2aaSAndroid Build Coastguard Worker static void test_invalid_header(skiatest::Reporter* r, const char* path) {
1516*c8dee2aaSAndroid Build Coastguard Worker     auto data = GetResourceAsData(path);
1517*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
1518*c8dee2aaSAndroid Build Coastguard Worker         return;
1519*c8dee2aaSAndroid Build Coastguard Worker     }
1520*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(std::move(data)));
1521*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
1522*c8dee2aaSAndroid Build Coastguard Worker         return;
1523*c8dee2aaSAndroid Build Coastguard Worker     }
1524*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1525*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !codec);
1526*c8dee2aaSAndroid Build Coastguard Worker }
1527*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_InvalidHeader,r)1528*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_InvalidHeader, r) {
1529*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_header(r, "invalid_images/int_overflow.ico");
1530*c8dee2aaSAndroid Build Coastguard Worker 
1531*c8dee2aaSAndroid Build Coastguard Worker     // These files report values that have caused problems with SkFILEStreams.
1532*c8dee2aaSAndroid Build Coastguard Worker     // They are invalid, and should not create SkCodecs.
1533*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_header(r, "invalid_images/b33651913.bmp");
1534*c8dee2aaSAndroid Build Coastguard Worker     test_invalid_header(r, "invalid_images/b34778578.bmp");
1535*c8dee2aaSAndroid Build Coastguard Worker }
1536*c8dee2aaSAndroid Build Coastguard Worker 
1537*c8dee2aaSAndroid Build Coastguard Worker /*
1538*c8dee2aaSAndroid Build Coastguard Worker For the Codec_InvalidAnimated test, immediately below,
1539*c8dee2aaSAndroid Build Coastguard Worker resources/invalid_images/skbug6046.gif is:
1540*c8dee2aaSAndroid Build Coastguard Worker 
1541*c8dee2aaSAndroid Build Coastguard Worker 00000000: 4749 4638 3961 2000 0000 0000 002c ff00  GIF89a ......,..
1542*c8dee2aaSAndroid Build Coastguard Worker 00000010: 7400 0600 0000 4001 0021 f904 0a00 0000  t.....@..!......
1543*c8dee2aaSAndroid Build Coastguard Worker 00000020: 002c ff00 0000 ff00 7400 0606 0606 0601  .,......t.......
1544*c8dee2aaSAndroid Build Coastguard Worker 00000030: 0021 f904 0000 0000 002c ff00 0000 ffcc  .!.......,......
1545*c8dee2aaSAndroid Build Coastguard Worker 00000040: 1b36 5266 deba 543d                      .6Rf..T=
1546*c8dee2aaSAndroid Build Coastguard Worker 
1547*c8dee2aaSAndroid Build Coastguard Worker It nominally contains 3 frames, but only the first one is valid. It came from a
1548*c8dee2aaSAndroid Build Coastguard Worker fuzzer doing random mutations and copies. The breakdown:
1549*c8dee2aaSAndroid Build Coastguard Worker 
1550*c8dee2aaSAndroid Build Coastguard Worker @000  6 bytes magic "GIF89a"
1551*c8dee2aaSAndroid Build Coastguard Worker @006  7 bytes Logical Screen Descriptor: 0x20 0x00 ... 0x00
1552*c8dee2aaSAndroid Build Coastguard Worker    - width     =    32
1553*c8dee2aaSAndroid Build Coastguard Worker    - height    =     0
1554*c8dee2aaSAndroid Build Coastguard Worker    - flags     =  0x00
1555*c8dee2aaSAndroid Build Coastguard Worker    - background color index, pixel aspect ratio bytes ignored
1556*c8dee2aaSAndroid Build Coastguard Worker @00D 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x40
1557*c8dee2aaSAndroid Build Coastguard Worker    - origin_x  =   255
1558*c8dee2aaSAndroid Build Coastguard Worker    - origin_y  =   116
1559*c8dee2aaSAndroid Build Coastguard Worker    - width     =     6
1560*c8dee2aaSAndroid Build Coastguard Worker    - height    =     0
1561*c8dee2aaSAndroid Build Coastguard Worker    - flags     =  0x40, interlaced
1562*c8dee2aaSAndroid Build Coastguard Worker @017  2 bytes Image Descriptor body (pixel data): 0x01 0x00
1563*c8dee2aaSAndroid Build Coastguard Worker    - lit_width =     1
1564*c8dee2aaSAndroid Build Coastguard Worker    - 0x00 byte means "end of data" for this frame
1565*c8dee2aaSAndroid Build Coastguard Worker @019  8 bytes Graphic Control Extension: 0x21 0xF9 ... 0x00
1566*c8dee2aaSAndroid Build Coastguard Worker    - valid, but irrelevant here.
1567*c8dee2aaSAndroid Build Coastguard Worker @021 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x06
1568*c8dee2aaSAndroid Build Coastguard Worker    - origin_x  =   255
1569*c8dee2aaSAndroid Build Coastguard Worker    - origin_y  =     0
1570*c8dee2aaSAndroid Build Coastguard Worker    - width     =   255
1571*c8dee2aaSAndroid Build Coastguard Worker    - height    =   116
1572*c8dee2aaSAndroid Build Coastguard Worker    - flags     =  0x06, INVALID, 0x80 BIT ZERO IMPLIES 0x07 BITS SHOULD BE ZERO
1573*c8dee2aaSAndroid Build Coastguard Worker @02B 14 bytes Image Descriptor body (pixel data): 0x06 0x06 ... 0x00
1574*c8dee2aaSAndroid Build Coastguard Worker    - lit_width =     6
1575*c8dee2aaSAndroid Build Coastguard Worker    - 0x06 precedes a 6 byte block of data
1576*c8dee2aaSAndroid Build Coastguard Worker    - 0x04 precedes a 4 byte block of data
1577*c8dee2aaSAndroid Build Coastguard Worker    - 0x00 byte means "end of data" for this frame
1578*c8dee2aaSAndroid Build Coastguard Worker @039 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x06
1579*c8dee2aaSAndroid Build Coastguard Worker    - origin_x  =   255
1580*c8dee2aaSAndroid Build Coastguard Worker    - origin_y  =     0
1581*c8dee2aaSAndroid Build Coastguard Worker    - width     = 52479
1582*c8dee2aaSAndroid Build Coastguard Worker    - height    = 13851
1583*c8dee2aaSAndroid Build Coastguard Worker    - flags     =  0x52, INVALID, 0x80 BIT ZERO IMPLIES 0x07 BITS SHOULD BE ZERO
1584*c8dee2aaSAndroid Build Coastguard Worker @043  5 bytes Image Descriptor body (pixel data): 0x66 0xDE ... unexpected-EOF
1585*c8dee2aaSAndroid Build Coastguard Worker    - lit_width =   102, INVALID, GREATER THAN 8
1586*c8dee2aaSAndroid Build Coastguard Worker    - 0xDE precedes a 222 byte block of data, INVALIDLY TRUNCATED
1587*c8dee2aaSAndroid Build Coastguard Worker 
1588*c8dee2aaSAndroid Build Coastguard Worker On Image Descriptor flags INVALIDITY,
1589*c8dee2aaSAndroid Build Coastguard Worker https://www.w3.org/Graphics/GIF/spec-gif89a.txt section 20.c says that "Size of
1590*c8dee2aaSAndroid Build Coastguard Worker Local Color Table [the low 3 bits]... should be 0 if there is no Local Color
1591*c8dee2aaSAndroid Build Coastguard Worker Table specified [the high bit]."
1592*c8dee2aaSAndroid Build Coastguard Worker 
1593*c8dee2aaSAndroid Build Coastguard Worker On LZW literal width (also known as Minimum Code Size) INVALIDITY,
1594*c8dee2aaSAndroid Build Coastguard Worker https://www.w3.org/Graphics/GIF/spec-gif89a.txt Appendix F says that "Normally
1595*c8dee2aaSAndroid Build Coastguard Worker this will be the same as the number of [palette index] bits. Because of some
1596*c8dee2aaSAndroid Build Coastguard Worker algorithmic constraints however, black & white images which have one color bit
1597*c8dee2aaSAndroid Build Coastguard Worker must be indicated as having a code size of 2." In practice, some GIF decoders,
1598*c8dee2aaSAndroid Build Coastguard Worker including both the old third_party/gif code and the Wuffs GIF decoder, don't
1599*c8dee2aaSAndroid Build Coastguard Worker enforce this "at least 2" constraint. Nonetheless, any width greater than 8 is
1600*c8dee2aaSAndroid Build Coastguard Worker invalid, as there are only 8 bits in a byte.
1601*c8dee2aaSAndroid Build Coastguard Worker */
1602*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_InvalidAnimated,r)1603*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_InvalidAnimated, r) {
1604*c8dee2aaSAndroid Build Coastguard Worker     // ASAN will complain if there is an issue.
1605*c8dee2aaSAndroid Build Coastguard Worker     auto path = "invalid_images/skbug6046.gif";
1606*c8dee2aaSAndroid Build Coastguard Worker     auto stream = GetResourceAsStream(path);
1607*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
1608*c8dee2aaSAndroid Build Coastguard Worker         return;
1609*c8dee2aaSAndroid Build Coastguard Worker     }
1610*c8dee2aaSAndroid Build Coastguard Worker 
1611*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1612*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
1613*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1614*c8dee2aaSAndroid Build Coastguard Worker         return;
1615*c8dee2aaSAndroid Build Coastguard Worker     }
1616*c8dee2aaSAndroid Build Coastguard Worker 
1617*c8dee2aaSAndroid Build Coastguard Worker     const auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1618*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1619*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
1620*c8dee2aaSAndroid Build Coastguard Worker 
1621*c8dee2aaSAndroid Build Coastguard Worker     auto frameInfos = codec->getFrameInfo();
1622*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Options opts;
1623*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
1624*c8dee2aaSAndroid Build Coastguard Worker         opts.fFrameIndex = i;
1625*c8dee2aaSAndroid Build Coastguard Worker         const auto reqFrame = frameInfos[i].fRequiredFrame;
1626*c8dee2aaSAndroid Build Coastguard Worker         opts.fPriorFrame = reqFrame == i - 1 ? reqFrame : SkCodec::kNoFrame;
1627*c8dee2aaSAndroid Build Coastguard Worker         auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), &opts);
1628*c8dee2aaSAndroid Build Coastguard Worker 
1629*c8dee2aaSAndroid Build Coastguard Worker         if (result != SkCodec::kSuccess) {
1630*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Failed to start decoding frame %i (out of %zu) with error %i\n", i,
1631*c8dee2aaSAndroid Build Coastguard Worker                    frameInfos.size(), result);
1632*c8dee2aaSAndroid Build Coastguard Worker             continue;
1633*c8dee2aaSAndroid Build Coastguard Worker         }
1634*c8dee2aaSAndroid Build Coastguard Worker 
1635*c8dee2aaSAndroid Build Coastguard Worker         codec->incrementalDecode();
1636*c8dee2aaSAndroid Build Coastguard Worker     }
1637*c8dee2aaSAndroid Build Coastguard Worker }
1638*c8dee2aaSAndroid Build Coastguard Worker 
encode_format(SkDynamicMemoryWStream * stream,const SkPixmap & pixmap,SkEncodedImageFormat format)1639*c8dee2aaSAndroid Build Coastguard Worker static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap,
1640*c8dee2aaSAndroid Build Coastguard Worker                           SkEncodedImageFormat format) {
1641*c8dee2aaSAndroid Build Coastguard Worker     switch (format) {
1642*c8dee2aaSAndroid Build Coastguard Worker         case SkEncodedImageFormat::kPNG:
1643*c8dee2aaSAndroid Build Coastguard Worker             SkPngEncoder::Encode(stream, pixmap, SkPngEncoder::Options());
1644*c8dee2aaSAndroid Build Coastguard Worker             break;
1645*c8dee2aaSAndroid Build Coastguard Worker         case SkEncodedImageFormat::kJPEG:
1646*c8dee2aaSAndroid Build Coastguard Worker             SkJpegEncoder::Encode(stream, pixmap, SkJpegEncoder::Options());
1647*c8dee2aaSAndroid Build Coastguard Worker             break;
1648*c8dee2aaSAndroid Build Coastguard Worker         case SkEncodedImageFormat::kWEBP:
1649*c8dee2aaSAndroid Build Coastguard Worker             SkWebpEncoder::Encode(stream, pixmap, SkWebpEncoder::Options());
1650*c8dee2aaSAndroid Build Coastguard Worker             break;
1651*c8dee2aaSAndroid Build Coastguard Worker         default:
1652*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(false);
1653*c8dee2aaSAndroid Build Coastguard Worker             break;
1654*c8dee2aaSAndroid Build Coastguard Worker     }
1655*c8dee2aaSAndroid Build Coastguard Worker }
1656*c8dee2aaSAndroid Build Coastguard Worker 
test_encode_icc(skiatest::Reporter * r,SkEncodedImageFormat format)1657*c8dee2aaSAndroid Build Coastguard Worker static void test_encode_icc(skiatest::Reporter* r, SkEncodedImageFormat format) {
1658*c8dee2aaSAndroid Build Coastguard Worker     // Test with sRGB color space.
1659*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap srgbBitmap;
1660*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo srgbInfo = SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType);
1661*c8dee2aaSAndroid Build Coastguard Worker     srgbBitmap.allocPixels(srgbInfo);
1662*c8dee2aaSAndroid Build Coastguard Worker     *srgbBitmap.getAddr32(0, 0) = 0;
1663*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pixmap;
1664*c8dee2aaSAndroid Build Coastguard Worker     srgbBitmap.peekPixels(&pixmap);
1665*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream srgbBuf;
1666*c8dee2aaSAndroid Build Coastguard Worker     encode_format(&srgbBuf, pixmap, format);
1667*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> srgbData = srgbBuf.detachAsData();
1668*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> srgbCodec(SkCodec::MakeFromData(srgbData));
1669*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, srgbCodec->getInfo().colorSpace() == sk_srgb_singleton());
1670*c8dee2aaSAndroid Build Coastguard Worker 
1671*c8dee2aaSAndroid Build Coastguard Worker     // Test with P3 color space.
1672*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream p3Buf;
1673*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
1674*c8dee2aaSAndroid Build Coastguard Worker     pixmap.setColorSpace(p3);
1675*c8dee2aaSAndroid Build Coastguard Worker     encode_format(&p3Buf, pixmap, format);
1676*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> p3Data = p3Buf.detachAsData();
1677*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> p3Codec(SkCodec::MakeFromData(p3Data));
1678*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB());
1679*c8dee2aaSAndroid Build Coastguard Worker     skcms_Matrix3x3 mat0, mat1;
1680*c8dee2aaSAndroid Build Coastguard Worker     bool success = p3->toXYZD50(&mat0);
1681*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, success);
1682*c8dee2aaSAndroid Build Coastguard Worker     success = p3Codec->getInfo().colorSpace()->toXYZD50(&mat1);
1683*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, success);
1684*c8dee2aaSAndroid Build Coastguard Worker 
1685*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < 3; i++) {
1686*c8dee2aaSAndroid Build Coastguard Worker         for (int j = 0; j < 3; j++) {
1687*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, color_space_almost_equal(mat0.vals[i][j], mat1.vals[i][j]));
1688*c8dee2aaSAndroid Build Coastguard Worker         }
1689*c8dee2aaSAndroid Build Coastguard Worker     }
1690*c8dee2aaSAndroid Build Coastguard Worker }
1691*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_EncodeICC,r)1692*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_EncodeICC, r) {
1693*c8dee2aaSAndroid Build Coastguard Worker     test_encode_icc(r, SkEncodedImageFormat::kPNG);
1694*c8dee2aaSAndroid Build Coastguard Worker     test_encode_icc(r, SkEncodedImageFormat::kJPEG);
1695*c8dee2aaSAndroid Build Coastguard Worker     test_encode_icc(r, SkEncodedImageFormat::kWEBP);
1696*c8dee2aaSAndroid Build Coastguard Worker }
1697*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_webp_rowsDecoded,r)1698*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_webp_rowsDecoded, r) {
1699*c8dee2aaSAndroid Build Coastguard Worker     const char* path = "images/baby_tux.webp";
1700*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data(GetResourceAsData(path));
1701*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
1702*c8dee2aaSAndroid Build Coastguard Worker         return;
1703*c8dee2aaSAndroid Build Coastguard Worker     }
1704*c8dee2aaSAndroid Build Coastguard Worker 
1705*c8dee2aaSAndroid Build Coastguard Worker     // Truncate this file so that the header is available but no rows can be
1706*c8dee2aaSAndroid Build Coastguard Worker     // decoded. This should create a codec but fail to decode.
1707*c8dee2aaSAndroid Build Coastguard Worker     size_t truncatedSize = 5000;
1708*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> subset = SkData::MakeSubset(data.get(), 0, truncatedSize);
1709*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(std::move(subset));
1710*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1711*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Failed to create a codec for %s truncated to only %zu bytes",
1712*c8dee2aaSAndroid Build Coastguard Worker                path, truncatedSize);
1713*c8dee2aaSAndroid Build Coastguard Worker         return;
1714*c8dee2aaSAndroid Build Coastguard Worker     }
1715*c8dee2aaSAndroid Build Coastguard Worker 
1716*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec.get(), codec->getInfo(), SkCodec::kInvalidInput, nullptr);
1717*c8dee2aaSAndroid Build Coastguard Worker }
1718*c8dee2aaSAndroid Build Coastguard Worker 
1719*c8dee2aaSAndroid Build Coastguard Worker /*
1720*c8dee2aaSAndroid Build Coastguard Worker For the Codec_ossfuzz6274 test, immediately below,
1721*c8dee2aaSAndroid Build Coastguard Worker resources/invalid_images/ossfuzz6274.gif is:
1722*c8dee2aaSAndroid Build Coastguard Worker 
1723*c8dee2aaSAndroid Build Coastguard Worker 00000000: 4749 4638 3961 2000 2000 f120 2020 2020  GIF89a . ..
1724*c8dee2aaSAndroid Build Coastguard Worker 00000010: 2020 2020 2020 2020 2021 f903 ff20 2020           !...
1725*c8dee2aaSAndroid Build Coastguard Worker 00000020: 002c 0000 0000 2000 2000 2000 00         .,.... . . ..
1726*c8dee2aaSAndroid Build Coastguard Worker 
1727*c8dee2aaSAndroid Build Coastguard Worker @000  6 bytes magic "GIF89a"
1728*c8dee2aaSAndroid Build Coastguard Worker @006  7 bytes Logical Screen Descriptor: 0x20 0x00 ... 0x00
1729*c8dee2aaSAndroid Build Coastguard Worker    - width     =    32
1730*c8dee2aaSAndroid Build Coastguard Worker    - height    =    32
1731*c8dee2aaSAndroid Build Coastguard Worker    - flags     =  0xF1, global color table, 4 RGB entries
1732*c8dee2aaSAndroid Build Coastguard Worker    - background color index, pixel aspect ratio bytes ignored
1733*c8dee2aaSAndroid Build Coastguard Worker @00D 12 bytes Color Table: 0x20 0x20 ... 0x20
1734*c8dee2aaSAndroid Build Coastguard Worker @019 20 bytes Graphic Control Extension: 0x21 0xF9 ... unexpected-EOF
1735*c8dee2aaSAndroid Build Coastguard Worker    - 0x03 precedes a 3 byte block of data, INVALID, MUST BE 4
1736*c8dee2aaSAndroid Build Coastguard Worker    - 0x20 precedes a 32 byte block of data, INVALIDly truncated
1737*c8dee2aaSAndroid Build Coastguard Worker 
1738*c8dee2aaSAndroid Build Coastguard Worker https://www.w3.org/Graphics/GIF/spec-gif89a.txt section 23.c says that the
1739*c8dee2aaSAndroid Build Coastguard Worker block size (for an 0x21 0xF9 Graphic Control Extension) must be "the fixed
1740*c8dee2aaSAndroid Build Coastguard Worker value 4".
1741*c8dee2aaSAndroid Build Coastguard Worker */
1742*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_ossfuzz6274,r)1743*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_ossfuzz6274, r) {
1744*c8dee2aaSAndroid Build Coastguard Worker     if (GetResourcePath().isEmpty()) {
1745*c8dee2aaSAndroid Build Coastguard Worker         return;
1746*c8dee2aaSAndroid Build Coastguard Worker     }
1747*c8dee2aaSAndroid Build Coastguard Worker 
1748*c8dee2aaSAndroid Build Coastguard Worker     const char* file = "invalid_images/ossfuzz6274.gif";
1749*c8dee2aaSAndroid Build Coastguard Worker     auto image = ToolUtils::GetResourceAsImage(file);
1750*c8dee2aaSAndroid Build Coastguard Worker 
1751*c8dee2aaSAndroid Build Coastguard Worker     if (image) {
1752*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Invalid data gave non-nullptr image");
1753*c8dee2aaSAndroid Build Coastguard Worker     }
1754*c8dee2aaSAndroid Build Coastguard Worker }
1755*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_78329453,r)1756*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_78329453, r) {
1757*c8dee2aaSAndroid Build Coastguard Worker     if (GetResourcePath().isEmpty()) {
1758*c8dee2aaSAndroid Build Coastguard Worker         return;
1759*c8dee2aaSAndroid Build Coastguard Worker     }
1760*c8dee2aaSAndroid Build Coastguard Worker 
1761*c8dee2aaSAndroid Build Coastguard Worker     const char* file = "images/b78329453.jpeg";
1762*c8dee2aaSAndroid Build Coastguard Worker     auto data = GetResourceAsData(file);
1763*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
1764*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Missing %s", file);
1765*c8dee2aaSAndroid Build Coastguard Worker         return;
1766*c8dee2aaSAndroid Build Coastguard Worker     }
1767*c8dee2aaSAndroid Build Coastguard Worker 
1768*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data));
1769*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
1770*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "failed to create codec from %s", file);
1771*c8dee2aaSAndroid Build Coastguard Worker         return;
1772*c8dee2aaSAndroid Build Coastguard Worker     }
1773*c8dee2aaSAndroid Build Coastguard Worker 
1774*c8dee2aaSAndroid Build Coastguard Worker     // A bug in jpeg_skip_scanlines resulted in an infinite loop for this specific
1775*c8dee2aaSAndroid Build Coastguard Worker     // sample size on this image. Other sample sizes could have had the same result,
1776*c8dee2aaSAndroid Build Coastguard Worker     // but the ones tested by DM happen to not.
1777*c8dee2aaSAndroid Build Coastguard Worker     constexpr int kSampleSize = 19;
1778*c8dee2aaSAndroid Build Coastguard Worker     const auto size = codec->getSampledDimensions(kSampleSize);
1779*c8dee2aaSAndroid Build Coastguard Worker     auto info = codec->getInfo().makeDimensions(size);
1780*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1781*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
1782*c8dee2aaSAndroid Build Coastguard Worker     bm.eraseColor(SK_ColorTRANSPARENT);
1783*c8dee2aaSAndroid Build Coastguard Worker 
1784*c8dee2aaSAndroid Build Coastguard Worker     SkAndroidCodec::AndroidOptions options;
1785*c8dee2aaSAndroid Build Coastguard Worker     options.fSampleSize = kSampleSize;
1786*c8dee2aaSAndroid Build Coastguard Worker     auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &options);
1787*c8dee2aaSAndroid Build Coastguard Worker     if (result != SkCodec::kSuccess) {
1788*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "failed to decode with error %s", SkCodec::ResultToString(result));
1789*c8dee2aaSAndroid Build Coastguard Worker     }
1790*c8dee2aaSAndroid Build Coastguard Worker }
1791*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_A8,r)1792*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_A8, r) {
1793*c8dee2aaSAndroid Build Coastguard Worker     if (GetResourcePath().isEmpty()) {
1794*c8dee2aaSAndroid Build Coastguard Worker         return;
1795*c8dee2aaSAndroid Build Coastguard Worker     }
1796*c8dee2aaSAndroid Build Coastguard Worker 
1797*c8dee2aaSAndroid Build Coastguard Worker     const char* file = "images/mandrill_cmyk.jpg";
1798*c8dee2aaSAndroid Build Coastguard Worker     auto data = GetResourceAsData(file);
1799*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
1800*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "missing %s", file);
1801*c8dee2aaSAndroid Build Coastguard Worker         return;
1802*c8dee2aaSAndroid Build Coastguard Worker     }
1803*c8dee2aaSAndroid Build Coastguard Worker 
1804*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkCodec::MakeFromData(std::move(data));
1805*c8dee2aaSAndroid Build Coastguard Worker     auto info = codec->getInfo().makeColorType(kAlpha_8_SkColorType);
1806*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1807*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(info);
1808*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec->getPixels(bm.pixmap()) == SkCodec::kInvalidConversion);
1809*c8dee2aaSAndroid Build Coastguard Worker }
1810*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_crbug807324,r)1811*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_crbug807324, r) {
1812*c8dee2aaSAndroid Build Coastguard Worker     if (GetResourcePath().isEmpty()) {
1813*c8dee2aaSAndroid Build Coastguard Worker         return;
1814*c8dee2aaSAndroid Build Coastguard Worker     }
1815*c8dee2aaSAndroid Build Coastguard Worker 
1816*c8dee2aaSAndroid Build Coastguard Worker     const char* file = "images/crbug807324.png";
1817*c8dee2aaSAndroid Build Coastguard Worker     auto image = ToolUtils::GetResourceAsImage(file);
1818*c8dee2aaSAndroid Build Coastguard Worker     if (!image) {
1819*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Missing %s", file);
1820*c8dee2aaSAndroid Build Coastguard Worker         return;
1821*c8dee2aaSAndroid Build Coastguard Worker     }
1822*c8dee2aaSAndroid Build Coastguard Worker 
1823*c8dee2aaSAndroid Build Coastguard Worker     const int kWidth = image->width();
1824*c8dee2aaSAndroid Build Coastguard Worker     const int kHeight = image->height();
1825*c8dee2aaSAndroid Build Coastguard Worker 
1826*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1827*c8dee2aaSAndroid Build Coastguard Worker     if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(kWidth, kHeight))) {
1828*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(r, "Could not allocate pixels (%i x %i)", kWidth, kHeight);
1829*c8dee2aaSAndroid Build Coastguard Worker         return;
1830*c8dee2aaSAndroid Build Coastguard Worker     }
1831*c8dee2aaSAndroid Build Coastguard Worker 
1832*c8dee2aaSAndroid Build Coastguard Worker     bm.eraseColor(SK_ColorTRANSPARENT);
1833*c8dee2aaSAndroid Build Coastguard Worker 
1834*c8dee2aaSAndroid Build Coastguard Worker     SkCanvas canvas(bm);
1835*c8dee2aaSAndroid Build Coastguard Worker     canvas.drawImage(image, 0, 0);
1836*c8dee2aaSAndroid Build Coastguard Worker 
1837*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < kWidth;  ++i)
1838*c8dee2aaSAndroid Build Coastguard Worker     for (int j = 0; j < kHeight; ++j) {
1839*c8dee2aaSAndroid Build Coastguard Worker         if (*bm.getAddr32(i, j) == SK_ColorTRANSPARENT) {
1840*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "image should not be transparent! %i, %i is 0", i, j);
1841*c8dee2aaSAndroid Build Coastguard Worker             return;
1842*c8dee2aaSAndroid Build Coastguard Worker         }
1843*c8dee2aaSAndroid Build Coastguard Worker     }
1844*c8dee2aaSAndroid Build Coastguard Worker }
1845*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_F16_noColorSpace,r)1846*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_F16_noColorSpace, r) {
1847*c8dee2aaSAndroid Build Coastguard Worker     const char* path = "images/color_wheel.png";
1848*c8dee2aaSAndroid Build Coastguard Worker     auto data = GetResourceAsData(path);
1849*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
1850*c8dee2aaSAndroid Build Coastguard Worker         return;
1851*c8dee2aaSAndroid Build Coastguard Worker     }
1852*c8dee2aaSAndroid Build Coastguard Worker 
1853*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkCodec::MakeFromData(std::move(data));
1854*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo info = codec->getInfo().makeColorType(kRGBA_F16_SkColorType)
1855*c8dee2aaSAndroid Build Coastguard Worker                                        .makeColorSpace(nullptr);
1856*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec.get(), info, SkCodec::kSuccess, nullptr);
1857*c8dee2aaSAndroid Build Coastguard Worker }
1858*c8dee2aaSAndroid Build Coastguard Worker 
1859*c8dee2aaSAndroid Build Coastguard Worker // These test images have ICC profiles that do not map to an SkColorSpace.
1860*c8dee2aaSAndroid Build Coastguard Worker // Verify that decoding them with a null destination space does not perform
1861*c8dee2aaSAndroid Build Coastguard Worker // color space transformations.
DEF_TEST(Codec_noConversion,r)1862*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_noConversion, r) {
1863*c8dee2aaSAndroid Build Coastguard Worker     const struct Rec {
1864*c8dee2aaSAndroid Build Coastguard Worker         const char* name;
1865*c8dee2aaSAndroid Build Coastguard Worker         SkColor color;
1866*c8dee2aaSAndroid Build Coastguard Worker     } recs[] = {
1867*c8dee2aaSAndroid Build Coastguard Worker       { "images/cmyk_yellow_224_224_32.jpg", 0xFFD8FC04 },
1868*c8dee2aaSAndroid Build Coastguard Worker       { "images/wide_gamut_yellow_224_224_64.jpeg",0xFFE0E040 },
1869*c8dee2aaSAndroid Build Coastguard Worker     };
1870*c8dee2aaSAndroid Build Coastguard Worker 
1871*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& rec : recs) {
1872*c8dee2aaSAndroid Build Coastguard Worker         auto data = GetResourceAsData(rec.name);
1873*c8dee2aaSAndroid Build Coastguard Worker         if (!data) {
1874*c8dee2aaSAndroid Build Coastguard Worker             continue;
1875*c8dee2aaSAndroid Build Coastguard Worker         }
1876*c8dee2aaSAndroid Build Coastguard Worker 
1877*c8dee2aaSAndroid Build Coastguard Worker         auto codec = SkCodec::MakeFromData(std::move(data));
1878*c8dee2aaSAndroid Build Coastguard Worker         if (!codec) {
1879*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Failed to create a codec from %s", rec.name);
1880*c8dee2aaSAndroid Build Coastguard Worker             continue;
1881*c8dee2aaSAndroid Build Coastguard Worker         }
1882*c8dee2aaSAndroid Build Coastguard Worker 
1883*c8dee2aaSAndroid Build Coastguard Worker         const auto* profile = codec->getICCProfile();
1884*c8dee2aaSAndroid Build Coastguard Worker         if (!profile) {
1885*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Expected %s to have a profile", rec.name);
1886*c8dee2aaSAndroid Build Coastguard Worker             continue;
1887*c8dee2aaSAndroid Build Coastguard Worker         }
1888*c8dee2aaSAndroid Build Coastguard Worker 
1889*c8dee2aaSAndroid Build Coastguard Worker         auto cs = SkColorSpace::Make(*profile);
1890*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, !cs.get());
1891*c8dee2aaSAndroid Build Coastguard Worker 
1892*c8dee2aaSAndroid Build Coastguard Worker         SkImageInfo info = codec->getInfo().makeColorSpace(nullptr);
1893*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bm;
1894*c8dee2aaSAndroid Build Coastguard Worker         bm.allocPixels(info);
1895*c8dee2aaSAndroid Build Coastguard Worker         if (codec->getPixels(info, bm.getPixels(), bm.rowBytes()) != SkCodec::kSuccess) {
1896*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "Failed to decode %s", rec.name);
1897*c8dee2aaSAndroid Build Coastguard Worker             continue;
1898*c8dee2aaSAndroid Build Coastguard Worker         }
1899*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, bm.getColor(0, 0) == rec.color);
1900*c8dee2aaSAndroid Build Coastguard Worker     }
1901*c8dee2aaSAndroid Build Coastguard Worker }
1902*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_kBGR_101010x_XR_SkColorType_supported,r)1903*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_kBGR_101010x_XR_SkColorType_supported, r) {
1904*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap srcBm;
1905*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo srcInfo = SkImageInfo()
1906*c8dee2aaSAndroid Build Coastguard Worker             .makeWH(100, 100)
1907*c8dee2aaSAndroid Build Coastguard Worker             .makeColorSpace(SkColorSpace::MakeSRGB())
1908*c8dee2aaSAndroid Build Coastguard Worker             .makeColorType(kBGRA_8888_SkColorType)
1909*c8dee2aaSAndroid Build Coastguard Worker             .makeAlphaType(kOpaque_SkAlphaType);
1910*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo dstInfo = srcInfo.makeColorType(kBGR_101010x_XR_SkColorType);
1911*c8dee2aaSAndroid Build Coastguard Worker     srcBm.allocPixels(srcInfo);
1912*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream stream;
1913*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT_RELEASE(SkPngEncoder::Encode(&stream, srcBm.pixmap(), {}));
1914*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(stream.detachAsData()));
1915*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap dstBm;
1916*c8dee2aaSAndroid Build Coastguard Worker     dstBm.allocPixels(dstInfo);
1917*c8dee2aaSAndroid Build Coastguard Worker     bool success = codec->getPixels(dstInfo, dstBm.getPixels(), dstBm.rowBytes());
1918*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkCodec::kSuccess == success);
1919*c8dee2aaSAndroid Build Coastguard Worker }
1920*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_gif_notseekable,r)1921*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_gif_notseekable, r) {
1922*c8dee2aaSAndroid Build Coastguard Worker     constexpr char path[] = "images/flightAnim.gif";
1923*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data(GetResourceAsData(path));
1924*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
1925*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing resource '%s'\n", path);
1926*c8dee2aaSAndroid Build Coastguard Worker         return;
1927*c8dee2aaSAndroid Build Coastguard Worker     }
1928*c8dee2aaSAndroid Build Coastguard Worker 
1929*c8dee2aaSAndroid Build Coastguard Worker     // Verify that using a non-seekable stream works the same as a seekable one for
1930*c8dee2aaSAndroid Build Coastguard Worker     // decoding the first frame.
1931*c8dee2aaSAndroid Build Coastguard Worker     const SkMD5::Digest goodDigest = [data, &r]() {
1932*c8dee2aaSAndroid Build Coastguard Worker         auto codec = SkCodec::MakeFromStream(std::make_unique<SkMemoryStream>(data),
1933*c8dee2aaSAndroid Build Coastguard Worker                                              nullptr, nullptr,
1934*c8dee2aaSAndroid Build Coastguard Worker                                              SkCodec::SelectionPolicy::kPreferAnimation);
1935*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, codec->getFrameCount() == 60);
1936*c8dee2aaSAndroid Build Coastguard Worker         const auto info = codec->getInfo();
1937*c8dee2aaSAndroid Build Coastguard Worker 
1938*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bm;
1939*c8dee2aaSAndroid Build Coastguard Worker         bm.allocPixels(info);
1940*c8dee2aaSAndroid Build Coastguard Worker 
1941*c8dee2aaSAndroid Build Coastguard Worker         SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
1942*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1943*c8dee2aaSAndroid Build Coastguard Worker         return md5(bm);
1944*c8dee2aaSAndroid Build Coastguard Worker     }();
1945*c8dee2aaSAndroid Build Coastguard Worker 
1946*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkCodec::MakeFromStream(std::make_unique<NonseekableStream>(std::move(data)),
1947*c8dee2aaSAndroid Build Coastguard Worker                                          nullptr, nullptr,
1948*c8dee2aaSAndroid Build Coastguard Worker                                          SkCodec::SelectionPolicy::kPreferStillImage);
1949*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec->getFrameCount() == 1);
1950*c8dee2aaSAndroid Build Coastguard Worker 
1951*c8dee2aaSAndroid Build Coastguard Worker     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, &goodDigest);
1952*c8dee2aaSAndroid Build Coastguard Worker }
1953*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_gif_notseekable2,r)1954*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_gif_notseekable2, r) {
1955*c8dee2aaSAndroid Build Coastguard Worker     constexpr char path[] = "images/flightAnim.gif";
1956*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data(GetResourceAsData(path));
1957*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
1958*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing resource '%s'\n", path);
1959*c8dee2aaSAndroid Build Coastguard Worker         return;
1960*c8dee2aaSAndroid Build Coastguard Worker     }
1961*c8dee2aaSAndroid Build Coastguard Worker 
1962*c8dee2aaSAndroid Build Coastguard Worker     // Verify that using a non-seekable stream works the same as a seekable one for
1963*c8dee2aaSAndroid Build Coastguard Worker     // decoding a later frame.
1964*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Options options;
1965*c8dee2aaSAndroid Build Coastguard Worker     options.fFrameIndex = 5;
1966*c8dee2aaSAndroid Build Coastguard Worker 
1967*c8dee2aaSAndroid Build Coastguard Worker     const SkMD5::Digest goodDigest = [data, &r, &options]() {
1968*c8dee2aaSAndroid Build Coastguard Worker         auto codec = SkCodec::MakeFromStream(std::make_unique<SkMemoryStream>(data),
1969*c8dee2aaSAndroid Build Coastguard Worker                                              nullptr, nullptr,
1970*c8dee2aaSAndroid Build Coastguard Worker                                              SkCodec::SelectionPolicy::kPreferAnimation);
1971*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, codec->getFrameCount() == 60);
1972*c8dee2aaSAndroid Build Coastguard Worker         const auto info = codec->getInfo();
1973*c8dee2aaSAndroid Build Coastguard Worker 
1974*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bm;
1975*c8dee2aaSAndroid Build Coastguard Worker         bm.allocPixels(info);
1976*c8dee2aaSAndroid Build Coastguard Worker 
1977*c8dee2aaSAndroid Build Coastguard Worker         SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), &options);
1978*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1979*c8dee2aaSAndroid Build Coastguard Worker         return md5(bm);
1980*c8dee2aaSAndroid Build Coastguard Worker     }();
1981*c8dee2aaSAndroid Build Coastguard Worker 
1982*c8dee2aaSAndroid Build Coastguard Worker     // This should copy the non seekable stream.
1983*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkCodec::MakeFromStream(std::make_unique<NonseekableStream>(std::move(data)),
1984*c8dee2aaSAndroid Build Coastguard Worker                                          nullptr, nullptr,
1985*c8dee2aaSAndroid Build Coastguard Worker                                          SkCodec::SelectionPolicy::kPreferAnimation);
1986*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec->getFrameCount() == 60);
1987*c8dee2aaSAndroid Build Coastguard Worker 
1988*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
1989*c8dee2aaSAndroid Build Coastguard Worker     bm.allocPixels(codec->getInfo());
1990*c8dee2aaSAndroid Build Coastguard Worker 
1991*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result = codec->getPixels(codec->getInfo(), bm.getPixels(), bm.rowBytes(),
1992*c8dee2aaSAndroid Build Coastguard Worker                                               &options);
1993*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1994*c8dee2aaSAndroid Build Coastguard Worker     compare_to_good_digest(r, goodDigest, bm);
1995*c8dee2aaSAndroid Build Coastguard Worker }
1996*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_gif_null_param,r)1997*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_gif_null_param, r) {
1998*c8dee2aaSAndroid Build Coastguard Worker     constexpr char path[] = "images/flightAnim.gif";
1999*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data(GetResourceAsData(path));
2000*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
2001*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing resource '%s'\n", path);
2002*c8dee2aaSAndroid Build Coastguard Worker         return;
2003*c8dee2aaSAndroid Build Coastguard Worker     }
2004*c8dee2aaSAndroid Build Coastguard Worker 
2005*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result;
2006*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkGifDecoder::Decode(std::make_unique<SkMemoryStream>(std::move(data)),
2007*c8dee2aaSAndroid Build Coastguard Worker                                       &result, nullptr);
2008*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2009*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
2010*c8dee2aaSAndroid Build Coastguard Worker 
2011*c8dee2aaSAndroid Build Coastguard Worker     auto [image, res] = codec->getImage();
2012*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, res == SkCodec::kSuccess);
2013*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image);
2014*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image->width() == 320, "width %d != 320", image->width());
2015*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image->height() == 240, "height %d != 240", image->height());
2016*c8dee2aaSAndroid Build Coastguard Worker 
2017*c8dee2aaSAndroid Build Coastguard Worker     // Decoding the image this way loses the original data.
2018*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !image->refEncodedData());
2019*c8dee2aaSAndroid Build Coastguard Worker }
2020*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_gif_can_preserve_original_data,r)2021*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_gif_can_preserve_original_data, r) {
2022*c8dee2aaSAndroid Build Coastguard Worker     constexpr char path[] = "images/flightAnim.gif";
2023*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data(GetResourceAsData(path));
2024*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
2025*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing resource '%s'\n", path);
2026*c8dee2aaSAndroid Build Coastguard Worker         return;
2027*c8dee2aaSAndroid Build Coastguard Worker     }
2028*c8dee2aaSAndroid Build Coastguard Worker 
2029*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result;
2030*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkGifDecoder::Decode(data, &result, nullptr);
2031*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2032*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
2033*c8dee2aaSAndroid Build Coastguard Worker 
2034*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> image = SkCodecs::DeferredImage(std::move(codec), kPremul_SkAlphaType);
2035*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image);
2036*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image->width() == 320, "width %d != 320", image->width());
2037*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image->height() == 240, "height %d != 240", image->height());
2038*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r,
2039*c8dee2aaSAndroid Build Coastguard Worker                     image->alphaType() == kPremul_SkAlphaType,
2040*c8dee2aaSAndroid Build Coastguard Worker                     "AlphaType is wrong %d",
2041*c8dee2aaSAndroid Build Coastguard Worker                     image->alphaType());
2042*c8dee2aaSAndroid Build Coastguard Worker 
2043*c8dee2aaSAndroid Build Coastguard Worker     // The whole point of DeferredFromCodec is that it allows the client
2044*c8dee2aaSAndroid Build Coastguard Worker     // to hold onto the original image data for later.
2045*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> encodedData = image->refEncodedData();
2046*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, encodedData != nullptr);
2047*c8dee2aaSAndroid Build Coastguard Worker     // The returned data should the same as what went in.
2048*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, encodedData->size() == data->size());
2049*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, encodedData->bytes() == data->bytes());
2050*c8dee2aaSAndroid Build Coastguard Worker }
2051*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Codec_jpeg_can_return_data_from_original_stream,r)2052*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_jpeg_can_return_data_from_original_stream, r) {
2053*c8dee2aaSAndroid Build Coastguard Worker     constexpr char path[] = "images/dog.jpg";
2054*c8dee2aaSAndroid Build Coastguard Worker     // stream will be an SkFILEStream, not a SkMemoryStream (exercised above)
2055*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStreamAsset> stream = GetResourceAsStream(path, true);
2056*c8dee2aaSAndroid Build Coastguard Worker     if (!stream) {
2057*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing resource '%s'\n", path);
2058*c8dee2aaSAndroid Build Coastguard Worker         return;
2059*c8dee2aaSAndroid Build Coastguard Worker     }
2060*c8dee2aaSAndroid Build Coastguard Worker     size_t expectedBytes = stream->getLength();
2061*c8dee2aaSAndroid Build Coastguard Worker 
2062*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::Result result;
2063*c8dee2aaSAndroid Build Coastguard Worker     auto codec = SkJpegDecoder::Decode(std::move(stream), &result, nullptr);
2064*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2065*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, codec);
2066*c8dee2aaSAndroid Build Coastguard Worker 
2067*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> image = SkCodecs::DeferredImage(std::move(codec), kUnpremul_SkAlphaType);
2068*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image);
2069*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image->width() == 180, "width %d != 180", image->width());
2070*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, image->height() == 180, "height %d != 180", image->height());
2071*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r,
2072*c8dee2aaSAndroid Build Coastguard Worker                     image->alphaType() == kUnpremul_SkAlphaType,
2073*c8dee2aaSAndroid Build Coastguard Worker                     "AlphaType is wrong %d",
2074*c8dee2aaSAndroid Build Coastguard Worker                     image->alphaType());
2075*c8dee2aaSAndroid Build Coastguard Worker 
2076*c8dee2aaSAndroid Build Coastguard Worker     // The whole point of DeferredFromCodec is that it allows the client
2077*c8dee2aaSAndroid Build Coastguard Worker     // to hold onto the original image data for later.
2078*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> encodedData = image->refEncodedData();
2079*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, encodedData != nullptr);
2080*c8dee2aaSAndroid Build Coastguard Worker     // The returned data should the same as what went in.
2081*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, encodedData->size() == expectedBytes);
2082*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, SkJpegDecoder::IsJpeg(encodedData->data(), encodedData->size()));
2083*c8dee2aaSAndroid Build Coastguard Worker }
2084