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