Flutter Engine
The Flutter Engine
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
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
204 SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType);
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[],
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,
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[],
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[],
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();
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[]) {
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";
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";
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.
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
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) {
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) {
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";
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
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
1692DEF_TEST(Codec_EncodeICC, r) {
1696}
1697
1698DEF_TEST(Codec_webp_rowsDecoded, r) {
1699 const char* path = "images/baby_tux.webp";
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));
1854 SkImageInfo info = codec->getInfo().makeColorType(kRGBA_F16_SkColorType)
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);
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";
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";
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";
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";
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
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
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[])
Definition: CodecTest.cpp:704
static void test_dimensions(skiatest::Reporter *r, const char path[])
Definition: CodecTest.cpp:636
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)
Definition: CodecTest.cpp:294
SkIRect generate_random_subset(SkRandom *rand, int w, int h)
Definition: CodecTest.cpp:119
static void check_round_trip(skiatest::Reporter *r, SkCodec *origCodec, const SkImageInfo &info)
Definition: CodecTest.cpp:1126
static void test_conversion_possible(skiatest::Reporter *r, const char *path, bool supportsScanlineDecoder, bool supportsIncrementalDecoder)
Definition: CodecTest.cpp:1189
static SkMD5::Digest md5(const SkBitmap &bm)
Definition: CodecTest.cpp:77
static bool supports_partial_scanlines(const char path[])
Definition: CodecTest.cpp:280
static void check_codec_image_generator(skiatest::Reporter *r, const SkMD5::Digest &codecDigest, const SkImageInfo &info, const char path[], bool supportsIncomplete)
Definition: CodecTest.cpp:452
static void test_incremental_decode(skiatest::Reporter *r, SkCodec *codec, const SkImageInfo &info, const SkMD5::Digest &goodDigest)
Definition: CodecTest.cpp:131
static void decode_frame(skiatest::Reporter *r, SkCodec *codec, size_t frame)
Definition: CodecTest.cpp:1250
DEF_TEST(Codec_wbmp, r)
Definition: CodecTest.cpp:529
static void test_in_stripes(skiatest::Reporter *r, SkCodec *codec, const SkImageInfo &info, const SkMD5::Digest &goodDigest)
Definition: CodecTest.cpp:145
static void check_color_xform(skiatest::Reporter *r, const char *path)
Definition: CodecTest.cpp:1076
static void test_invalid_header(skiatest::Reporter *r, const char *path)
Definition: CodecTest.cpp:1515
static bool alpha_type_match(SkAlphaType origAlphaType, SkAlphaType codecAlphaType)
Definition: CodecTest.cpp:1115
static void check_subset_decode(skiatest::Reporter *r, SkCodec *codec, const SkImageInfo &info, SkISize size, bool supportsSubsetDecoding, bool supportsIncomplete)
Definition: CodecTest.cpp:384
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)
Definition: CodecTest.cpp:1104
static void encode_format(SkDynamicMemoryWStream *stream, const SkPixmap &pixmap, SkEncodedImageFormat format)
Definition: CodecTest.cpp:1639
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)
Definition: CodecTest.cpp:187
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)
Definition: CodecTest.cpp:426
static void test_encode_icc(skiatest::Reporter *r, SkEncodedImageFormat format)
Definition: CodecTest.cpp:1657
static void test_invalid_images(skiatest::Reporter *r, const char *path, SkCodec::Result expectedResult)
Definition: CodecTest.cpp:1480
static void seek_and_decode(const char *file, std::unique_ptr< SkStream > stream, skiatest::Reporter *r)
Definition: CodecTest.cpp:1383
static void test_invalid_stream(skiatest::Reporter *r, const void *stream, size_t len)
Definition: CodecTest.cpp:598
static void check(skiatest::Reporter *r, const char path[], SkISize size, bool supportsScanlineDecoding, bool supportsSubsetDecoding, bool supportsIncomplete, bool supportsNewScanlineDecoding=false)
Definition: CodecTest.cpp:482
static void test_info(skiatest::Reporter *r, Codec *codec, const SkImageInfo &info, SkCodec::Result expectedResult, const SkMD5::Digest *goodDigest)
Definition: CodecTest.cpp:106
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
m reset()
sk_bzero(glyphs, sizeof(glyphs))
kUnpremul_SkAlphaType
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)
sk_sp< SkData > SkCopyStreamToData(SkStream *stream)
Definition: SkStream.cpp:937
bool SkStrEndsWith(const char string[], const char suffixStr[])
Definition: SkString.cpp:68
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
bool rewind() override
Definition: CodecTest.cpp:917
size_t peek(void *buf, size_t bytes) const override
Definition: CodecTest.cpp:911
bool isAtEnd() const override
Definition: CodecTest.cpp:920
LimitedPeekingMemStream(sk_sp< SkData > data, size_t limit)
Definition: CodecTest.cpp:907
size_t read(void *buf, size_t bytes) override
Definition: CodecTest.cpp:914
bool isAtEnd() const override
Definition: CodecTest.cpp:1313
size_t read(void *buffer, size_t size) override
Definition: CodecTest.cpp:1307
static std::unique_ptr< SkStream > Make(const char path[], size_t limit)
Definition: CodecTest.cpp:1299
bool rewind() override
Definition: CodecTest.cpp:1317
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()
Definition: SkAutoMalloc.h:64
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
Result
Definition: SkCodec.h:76
@ 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
bool toXYZD50(skcms_Matrix3x3 *toXYZD50) const
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 &)
const uint8_t * bytes() const
Definition: SkData.h:43
const void * data() const
Definition: SkData.h:37
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
size_t size() const
Definition: SkData.h:30
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)
DlColor color
double frame
Definition: examples.cpp:31
GAsyncResult * result
uint32_t uint32_t * format
static float min(float r, float g, float b)
Definition: hsl.cpp:48
size_t length
double y
SK_API sk_sp< SkImage > DeferredImage(std::unique_ptr< SkCodec > codec, std::optional< SkAlphaType > alphaType=std::nullopt)
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
Definition: SkColorSpace.h:77
static constexpr skcms_Matrix3x3 kDisplayP3
Definition: SkColorSpace.h:87
static constexpr skcms_TransferFunction k2Dot2
Definition: SkColorSpace.h:48
static constexpr skcms_TransferFunction kSRGB
Definition: SkColorSpace.h:45
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
bool copy_to(SkBitmap *dst, SkColorType dstColorType, const SkBitmap &src)
Definition: ToolUtils.cpp:394
sk_sp< SkImage > GetResourceAsImage(const char *resource)
Definition: DecodeUtils.h:25
def gen()
Definition: dom.py:77
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
Definition: switches.h:32
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
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
Definition: SkRect.h:32
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
Definition: SkSize.h:16
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
Definition: SkImageInfo.h:444
SkImageInfo makeAlphaType(SkAlphaType newAlphaType) const
Definition: SkImageInfo.h:466
static SkImageInfo MakeN32Premul(int width, int height)
size_t minRowBytes() const
Definition: SkImageInfo.h:517
static SkImageInfo MakeS32(int width, int height, SkAlphaType at)
size_t computeByteSize(size_t rowBytes) const
SkImageInfo makeDimensions(SkISize newSize) const
Definition: SkImageInfo.h:454
SkColorSpace * colorSpace() const
int bytesPerPixel() const
Definition: SkImageInfo.h:492
SkImageInfo makeColorSpace(sk_sp< SkColorSpace > cs) const
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkImageInfo makeColorType(SkColorType newColorType) const
Definition: SkImageInfo.h:475
float vals[3][3]
Definition: skcms_public.h:27
constexpr size_t kHeight
constexpr size_t kWidth
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63