OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "Resources.h" | 8 #include "Resources.h" |
| 9 #include "SkAndroidCodec.h" |
9 #include "SkBitmap.h" | 10 #include "SkBitmap.h" |
10 #include "SkCodec.h" | 11 #include "SkCodec.h" |
11 #include "SkData.h" | 12 #include "SkData.h" |
12 #include "SkMD5.h" | 13 #include "SkMD5.h" |
13 #include "SkRandom.h" | 14 #include "SkRandom.h" |
14 #include "SkScaledCodec.h" | |
15 #include "Test.h" | 15 #include "Test.h" |
16 | 16 |
17 static SkStreamAsset* resource(const char path[]) { | 17 static SkStreamAsset* resource(const char path[]) { |
18 SkString fullPath = GetResourcePath(path); | 18 SkString fullPath = GetResourcePath(path); |
19 return SkStream::NewFromFile(fullPath.c_str()); | 19 return SkStream::NewFromFile(fullPath.c_str()); |
20 } | 20 } |
21 | 21 |
22 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { | 22 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { |
23 SkAutoLockPixels autoLockPixels(bm); | 23 SkAutoLockPixels autoLockPixels(bm); |
24 SkASSERT(bm.getPixels()); | 24 SkASSERT(bm.getPixels()); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 SkAutoLockPixels autoLockPixels(bm); | 56 SkAutoLockPixels autoLockPixels(bm); |
57 | 57 |
58 SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(
)); | 58 SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(
)); |
59 REPORTER_ASSERT(r, result == expectedResult); | 59 REPORTER_ASSERT(r, result == expectedResult); |
60 | 60 |
61 if (goodDigest) { | 61 if (goodDigest) { |
62 compare_to_good_digest(r, *goodDigest, bm); | 62 compare_to_good_digest(r, *goodDigest, bm); |
63 } | 63 } |
64 } | 64 } |
65 | 65 |
| 66 static void test_android_info(skiatest::Reporter* r, SkAndroidCodec* codec, cons
t SkImageInfo& info, |
| 67 SkCodec::Result expectedResult, const SkMD5::Diges
t* goodDigest) { |
| 68 SkBitmap bm; |
| 69 bm.allocPixels(info); |
| 70 SkAutoLockPixels autoLockPixels(bm); |
| 71 |
| 72 SkCodec::Result result = codec->getAndroidPixels(info, bm.getPixels(), bm.ro
wBytes()); |
| 73 REPORTER_ASSERT(r, result == expectedResult); |
| 74 |
| 75 if (goodDigest) { |
| 76 compare_to_good_digest(r, *goodDigest, bm); |
| 77 } |
| 78 } |
| 79 |
66 SkIRect generate_random_subset(SkRandom* rand, int w, int h) { | 80 SkIRect generate_random_subset(SkRandom* rand, int w, int h) { |
67 SkIRect rect; | 81 SkIRect rect; |
68 do { | 82 do { |
69 rect.fLeft = rand->nextRangeU(0, w); | 83 rect.fLeft = rand->nextRangeU(0, w); |
70 rect.fTop = rand->nextRangeU(0, h); | 84 rect.fTop = rand->nextRangeU(0, h); |
71 rect.fRight = rand->nextRangeU(0, w); | 85 rect.fRight = rand->nextRangeU(0, w); |
72 rect.fBottom = rand->nextRangeU(0, h); | 86 rect.fBottom = rand->nextRangeU(0, h); |
73 rect.sort(); | 87 rect.sort(); |
74 } while (rect.isEmpty()); | 88 } while (rect.isEmpty()); |
75 return rect; | 89 return rect; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 otherAt = kUnpremul_SkAlphaType; | 135 otherAt = kUnpremul_SkAlphaType; |
122 } else { | 136 } else { |
123 otherAt = kPremul_SkAlphaType; | 137 otherAt = kPremul_SkAlphaType; |
124 } | 138 } |
125 // The other non-opaque alpha type should always succeed, but not ma
tch. | 139 // The other non-opaque alpha type should always succeed, but not ma
tch. |
126 test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nul
lptr); | 140 test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nul
lptr); |
127 } | 141 } |
128 } | 142 } |
129 } | 143 } |
130 | 144 |
| 145 static void test_android_codec(skiatest::Reporter* r, SkAndroidCodec* codec, SkB
itmap& bm, |
| 146 const SkImageInfo& info, const SkISize& size, bool supports565, |
| 147 SkCodec::Result expectedResult, SkMD5::Digest* digest, const SkMD5::Dige
st* goodDigest) { |
| 148 |
| 149 REPORTER_ASSERT(r, info.dimensions() == size); |
| 150 bm.allocPixels(info); |
| 151 SkAutoLockPixels autoLockPixels(bm); |
| 152 |
| 153 SkCodec::Result result = codec->getAndroidPixels(info, bm.getPixels(), bm.ro
wBytes()); |
| 154 REPORTER_ASSERT(r, result == expectedResult); |
| 155 |
| 156 md5(bm, digest); |
| 157 if (goodDigest) { |
| 158 REPORTER_ASSERT(r, *digest == *goodDigest); |
| 159 } |
| 160 |
| 161 { |
| 162 // Test decoding to 565 |
| 163 SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType); |
| 164 SkCodec::Result expected565 = (supports565 && info.alphaType() == kOpaqu
e_SkAlphaType) ? |
| 165 expectedResult : SkCodec::kInvalidConversion; |
| 166 test_android_info(r, codec, info565, expected565, nullptr); |
| 167 } |
| 168 |
| 169 // Verify that re-decoding gives the same result. It is interesting to chec
k this after |
| 170 // a decode to 565, since choosing to decode to 565 may result in some of th
e decode |
| 171 // options being modified. These options should return to their defaults on
another |
| 172 // decode to kN32, so the new digest should match the old digest. |
| 173 test_android_info(r, codec, info, expectedResult, digest); |
| 174 |
| 175 { |
| 176 // Check alpha type conversions |
| 177 if (info.alphaType() == kOpaque_SkAlphaType) { |
| 178 test_android_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType
), |
| 179 SkCodec::kInvalidConversion, nullptr); |
| 180 test_android_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType), |
| 181 SkCodec::kInvalidConversion, nullptr); |
| 182 } else { |
| 183 // Decoding to opaque should fail |
| 184 test_android_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType), |
| 185 SkCodec::kInvalidConversion, nullptr); |
| 186 SkAlphaType otherAt = info.alphaType(); |
| 187 if (kPremul_SkAlphaType == otherAt) { |
| 188 otherAt = kUnpremul_SkAlphaType; |
| 189 } else { |
| 190 otherAt = kPremul_SkAlphaType; |
| 191 } |
| 192 // The other non-opaque alpha type should always succeed, but not ma
tch. |
| 193 test_android_info(r, codec, info.makeAlphaType(otherAt), expectedRes
ult, nullptr); |
| 194 } |
| 195 } |
| 196 } |
| 197 |
131 // FIXME: SkScaledCodec is currently only supported for types used by BRD | 198 // FIXME: SkScaledCodec is currently only supported for types used by BRD |
132 // skbug.com/4428 | 199 // skbug.com/4428 |
133 static bool supports_scaled_codec(const char path[]) { | 200 static bool supports_scaled_codec(const char path[]) { |
134 static const char* const exts[] = { | 201 static const char* const exts[] = { |
135 "jpg", "jpeg", "png", "webp" | 202 "jpg", "jpeg", "png", "webp" |
136 "JPG", "JPEG", "PNG", "WEBP" | 203 "JPG", "JPEG", "PNG", "WEBP" |
137 }; | 204 }; |
138 | 205 |
139 for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { | 206 for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { |
140 if (SkStrEndsWith(path, exts[i])) { | 207 if (SkStrEndsWith(path, exts[i])) { |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
281 | 348 |
282 // SkScaledCodec tests | 349 // SkScaledCodec tests |
283 if ((supportsScanlineDecoding || supportsSubsetDecoding) && supports_scaled_
codec(path)) { | 350 if ((supportsScanlineDecoding || supportsSubsetDecoding) && supports_scaled_
codec(path)) { |
284 | 351 |
285 SkAutoTDelete<SkStream> stream(resource(path)); | 352 SkAutoTDelete<SkStream> stream(resource(path)); |
286 if (!stream) { | 353 if (!stream) { |
287 SkDebugf("Missing resource '%s'\n", path); | 354 SkDebugf("Missing resource '%s'\n", path); |
288 return; | 355 return; |
289 } | 356 } |
290 | 357 |
291 SkAutoTDelete<SkCodec> codec(nullptr); | 358 SkAutoTDelete<SkAndroidCodec> codec(nullptr); |
292 if (isIncomplete) { | 359 if (isIncomplete) { |
293 size_t size = stream->getLength(); | 360 size_t size = stream->getLength(); |
294 SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size /
3))); | 361 SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size /
3))); |
295 codec.reset(SkScaledCodec::NewFromData(data)); | 362 codec.reset(SkAndroidCodec::NewFromData(data)); |
296 } else { | 363 } else { |
297 codec.reset(SkScaledCodec::NewFromStream(stream.detach())); | 364 codec.reset(SkAndroidCodec::NewFromStream(stream.detach())); |
298 } | 365 } |
299 if (!codec) { | 366 if (!codec) { |
300 ERRORF(r, "Unable to decode '%s'", path); | 367 ERRORF(r, "Unable to decode '%s'", path); |
301 return; | 368 return; |
302 } | 369 } |
303 | 370 |
304 SkBitmap bm; | 371 SkBitmap bm; |
305 SkMD5::Digest scaledCodecDigest; | 372 SkMD5::Digest scaledCodecDigest; |
306 test_codec(r, codec, bm, info, size, supports565, expectedResult, &scale
dCodecDigest, | 373 test_android_codec(r, codec, bm, info, size, supports565, expectedResult
, |
307 &codecDigest); | 374 &scaledCodecDigest, &codecDigest); |
308 } | 375 } |
309 | 376 |
310 // If we've just tested incomplete decodes, let's run the same test again on
full decodes. | 377 // If we've just tested incomplete decodes, let's run the same test again on
full decodes. |
311 if (isIncomplete) { | 378 if (isIncomplete) { |
312 check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, s
upports565, false); | 379 check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, s
upports565, false); |
313 } | 380 } |
314 } | 381 } |
315 | 382 |
316 DEF_TEST(Codec, r) { | 383 DEF_TEST(Codec, r) { |
317 // WBMP | 384 // WBMP |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
456 } | 523 } |
457 | 524 |
458 compare_to_good_digest(r, digest, bm); | 525 compare_to_good_digest(r, digest, bm); |
459 } | 526 } |
460 | 527 |
461 static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_
t len) { | 528 static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_
t len) { |
462 // Neither of these calls should return a codec. Bots should catch us if we
leaked anything. | 529 // Neither of these calls should return a codec. Bots should catch us if we
leaked anything. |
463 SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, fals
e)); | 530 SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, fals
e)); |
464 REPORTER_ASSERT(r, !codec); | 531 REPORTER_ASSERT(r, !codec); |
465 | 532 |
466 codec = SkScaledCodec::NewFromStream(new SkMemoryStream(stream, len, false))
; | 533 SkAndroidCodec* androidCodec = |
467 REPORTER_ASSERT(r, !codec); | 534 SkAndroidCodec::NewFromStream(new SkMemoryStream(stream, len, false)
); |
| 535 REPORTER_ASSERT(r, !androidCodec); |
468 } | 536 } |
469 | 537 |
470 // Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream, | 538 // Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream, |
471 // even on failure. Test some bad streams. | 539 // even on failure. Test some bad streams. |
472 DEF_TEST(Codec_leaks, r) { | 540 DEF_TEST(Codec_leaks, r) { |
473 // No codec should claim this as their format, so this tests SkCodec::NewFro
mStream. | 541 // No codec should claim this as their format, so this tests SkCodec::NewFro
mStream. |
474 const char nonSupportedStream[] = "hello world"; | 542 const char nonSupportedStream[] = "hello world"; |
475 // The other strings should look like the beginning of a file type, so we'll
call some | 543 // The other strings should look like the beginning of a file type, so we'll
call some |
476 // internal version of NewFromStream, which must also delete the stream on f
ailure. | 544 // internal version of NewFromStream, which must also delete the stream on f
ailure. |
477 const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a,
0x0a }; | 545 const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a,
0x0a }; |
(...skipping 11 matching lines...) Expand all Loading... |
489 test_invalid_stream(r, emptyIco, sizeof(emptyIco)); | 557 test_invalid_stream(r, emptyIco, sizeof(emptyIco)); |
490 test_invalid_stream(r, emptyGif, sizeof(emptyGif)); | 558 test_invalid_stream(r, emptyGif, sizeof(emptyGif)); |
491 } | 559 } |
492 | 560 |
493 DEF_TEST(Codec_null, r) { | 561 DEF_TEST(Codec_null, r) { |
494 // Attempting to create an SkCodec or an SkScaledCodec with null should not | 562 // Attempting to create an SkCodec or an SkScaledCodec with null should not |
495 // crash. | 563 // crash. |
496 SkCodec* codec = SkCodec::NewFromStream(nullptr); | 564 SkCodec* codec = SkCodec::NewFromStream(nullptr); |
497 REPORTER_ASSERT(r, !codec); | 565 REPORTER_ASSERT(r, !codec); |
498 | 566 |
499 codec = SkScaledCodec::NewFromStream(nullptr); | 567 SkAndroidCodec* androidCodec = SkAndroidCodec::NewFromStream(nullptr); |
500 REPORTER_ASSERT(r, !codec); | 568 REPORTER_ASSERT(r, !androidCodec); |
501 } | 569 } |
502 | 570 |
503 static void test_dimensions(skiatest::Reporter* r, const char path[]) { | 571 static void test_dimensions(skiatest::Reporter* r, const char path[]) { |
504 // Create the codec from the resource file | 572 // Create the codec from the resource file |
505 SkAutoTDelete<SkStream> stream(resource(path)); | 573 SkAutoTDelete<SkStream> stream(resource(path)); |
506 if (!stream) { | 574 if (!stream) { |
507 SkDebugf("Missing resource '%s'\n", path); | 575 SkDebugf("Missing resource '%s'\n", path); |
508 return; | 576 return; |
509 } | 577 } |
510 SkAutoTDelete<SkCodec> codec(SkScaledCodec::NewFromStream(stream.detach())); | 578 SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.det
ach())); |
511 if (!codec) { | 579 if (!codec) { |
512 ERRORF(r, "Unable to create codec '%s'", path); | 580 ERRORF(r, "Unable to create codec '%s'", path); |
513 return; | 581 return; |
514 } | 582 } |
515 | 583 |
516 // Check that the decode is successful for a variety of scales | 584 // Check that the decode is successful for a variety of scales |
517 for (float scale = 0.05f; scale < 2.0f; scale += 0.05f) { | 585 for (int sampleSize = 1; sampleSize < 10; sampleSize++) { |
518 // Scale the output dimensions | 586 // Scale the output dimensions |
519 SkISize scaledDims = codec->getScaledDimensions(scale); | 587 SkISize scaledDims = codec->getSampledDimensions(sampleSize); |
520 SkImageInfo scaledInfo = codec->getInfo() | 588 SkImageInfo scaledInfo = codec->getInfo() |
521 .makeWH(scaledDims.width(), scaledDims.height()) | 589 .makeWH(scaledDims.width(), scaledDims.height()) |
522 .makeColorType(kN32_SkColorType); | 590 .makeColorType(kN32_SkColorType); |
523 | 591 |
524 // Set up for the decode | 592 // Set up for the decode |
525 size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); | 593 size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); |
526 size_t totalBytes = scaledInfo.getSafeSize(rowBytes); | 594 size_t totalBytes = scaledInfo.getSafeSize(rowBytes); |
527 SkAutoTMalloc<SkPMColor> pixels(totalBytes); | 595 SkAutoTMalloc<SkPMColor> pixels(totalBytes); |
528 | 596 |
| 597 SkAndroidCodec::AndroidOptions options; |
| 598 options.fSampleSize = sampleSize; |
529 SkCodec::Result result = | 599 SkCodec::Result result = |
530 codec->getPixels(scaledInfo, pixels.get(), rowBytes, nullptr, nu
llptr, nullptr); | 600 codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &opt
ions); |
531 REPORTER_ASSERT(r, SkCodec::kSuccess == result); | 601 REPORTER_ASSERT(r, SkCodec::kSuccess == result); |
532 } | 602 } |
533 } | 603 } |
534 | 604 |
535 // Ensure that onGetScaledDimensions returns valid image dimensions to use for d
ecodes | 605 // Ensure that onGetScaledDimensions returns valid image dimensions to use for d
ecodes |
536 DEF_TEST(Codec_Dimensions, r) { | 606 DEF_TEST(Codec_Dimensions, r) { |
537 // JPG | 607 // JPG |
538 test_dimensions(r, "CMYK.jpg"); | 608 test_dimensions(r, "CMYK.jpg"); |
539 test_dimensions(r, "color_wheel.jpg"); | 609 test_dimensions(r, "color_wheel.jpg"); |
540 test_dimensions(r, "grayscale.jpg"); | 610 test_dimensions(r, "grayscale.jpg"); |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
609 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); | 679 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); |
610 result = decoder->startScanlineDecode( | 680 result = decoder->startScanlineDecode( |
611 decoder->getInfo().makeColorType(kIndex_8_SkColorType)); | 681 decoder->getInfo().makeColorType(kIndex_8_SkColorType)); |
612 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); | 682 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); |
613 } | 683 } |
614 | 684 |
615 DEF_TEST(Codec_Params, r) { | 685 DEF_TEST(Codec_Params, r) { |
616 test_invalid_parameters(r, "index8.png"); | 686 test_invalid_parameters(r, "index8.png"); |
617 test_invalid_parameters(r, "mandrill.wbmp"); | 687 test_invalid_parameters(r, "mandrill.wbmp"); |
618 } | 688 } |
OLD | NEW |