Chromium Code Reviews| Index: tests/ImageDecodingTest.cpp |
| diff --git a/tests/ImageDecodingTest.cpp b/tests/ImageDecodingTest.cpp |
| index f840129b1c41c56f43fd1b35e2f1bd938b865b8b..794f4dd02ac2f5829d8bff114ad4bd1051779880 100644 |
| --- a/tests/ImageDecodingTest.cpp |
| +++ b/tests/ImageDecodingTest.cpp |
| @@ -12,10 +12,13 @@ |
| #include "SkColor.h" |
| #include "SkColorPriv.h" |
| #include "SkData.h" |
| +#include "SkDecodingImageGenerator.h" |
| +#include "SkDiscardableMemoryPool.h" |
| #include "SkForceLinking.h" |
| #include "SkGradientShader.h" |
| #include "SkImageDecoder.h" |
| #include "SkImageEncoder.h" |
| +#include "SkImageGenerator.h" |
| #include "SkOSFile.h" |
| #include "SkPoint.h" |
| #include "SkShader.h" |
| @@ -152,7 +155,7 @@ static void test_unpremul(skiatest::Reporter* reporter) { |
| if (iter.next(&basename)) { |
| do { |
| SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str()); |
| - //SkDebugf("about to decode \"%s\"\n", filename.c_str()); |
| + // SkDebugf("about to decode \"%s\"\n", filename.c_str()); |
| compare_unpremul(reporter, filename); |
| } while (iter.next(&basename)); |
| } else { |
| @@ -200,7 +203,7 @@ static void test_stream_life() { |
| SkImageEncoder::kWEBP_Type, |
| }; |
| for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) { |
| - //SkDebugf("encoding to %i\n", i); |
| + // SkDebugf("encoding to %i\n", i); |
| SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i])); |
| if (NULL == stream.get()) { |
| SkDebugf("no stream\n"); |
| @@ -227,7 +230,7 @@ static void test_stream_life() { |
| // Test inside SkScaledBitmapSampler.cpp |
| extern void test_row_proc_choice(); |
| -#endif // SK_DEBUG |
| +#endif // SK_DEBUG |
| DEF_TEST(ImageDecoding, reporter) { |
| test_unpremul(reporter); |
| @@ -236,3 +239,212 @@ DEF_TEST(ImageDecoding, reporter) { |
| test_row_proc_choice(); |
| #endif |
| } |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| +// example of how Android will do this inside their BitmapFactory |
| +static SkPixelRef* install_pixel_ref(SkBitmap* bitmap, |
| + SkStreamRewindable* stream, |
| + int sampleSize, bool ditherImage) { |
| + SkASSERT(bitmap != NULL); |
| + SkASSERT(stream != NULL); |
| + SkASSERT(stream->rewind()); |
| + SkASSERT(stream->unique()); |
| + SkDecoderOptions opts(sampleSize, ditherImage, bitmap->config()); |
| + SkAutoTDelete<SkImageGenerator> gen( |
| + SkNewDecodingImageGenerator(stream, opts)); |
| + SkImageInfo info; |
| + if ((NULL == gen.get()) || !gen->getInfo(&info)) { |
| + return NULL; |
| + } |
| + SkDiscardableMemory::Factory* factory = NULL; |
| + if (SkBitmap::ComputeSize(info) < (32 * 1024)) { |
| + // only use ashmem for large images, since mmaps come at a price |
| + factory = SkGetGlobalDiscardableMemoryPool(); |
| + } |
| + if (SkInstallDiscardablePixelRef(gen.detach(), bitmap, factory)) { |
| + return bitmap->pixelRef(); |
| + } |
| + return NULL; |
| +} |
| +/** |
| + * A test for the SkNewDecodingImageGenerator and |
| + * SkInstallDiscardablePixelRef functions. |
| + */ |
| +DEF_TEST(ImprovedBitmapFactory, reporter) { |
| + SkString resourcePath = skiatest::Test::GetResourcePath(); |
| + SkString directory = SkOSPath::SkPathJoin(resourcePath.c_str(), "encoding"); |
| + SkString path = SkOSPath::SkPathJoin(directory.c_str(), "randPixels.png"); |
| + SkAutoTUnref<SkStreamRewindable> stream( |
| + SkStream::NewFromFile(path.c_str())); |
| + if (sk_exists(path.c_str())) { |
| + SkBitmap bm; |
| + SkAssertResult(bm.setConfig(SkBitmap::kARGB_8888_Config, 1, 1)); |
| + REPORTER_ASSERT(reporter, |
| + NULL != install_pixel_ref(&bm, stream.detach(), 1, true)); |
| + SkAutoLockPixels alp(bm); |
| + REPORTER_ASSERT(reporter, NULL != bm.getPixels()); |
| + } |
| +} |
| + |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| + |
| +static inline bool check_rounding(int value, int dividend, int divisor) { |
| + // returns true if (dividend/divisor) rounds up OR down to value |
| + return (((divisor * value) > (dividend - divisor)) |
| + && ((divisor * value) < (dividend + divisor))); |
| +} |
| +namespace { |
| +// expected output for 8x8 bitmap |
| +const int kExpectedWidth = 8; |
| +const int kExpectedHeight = 8; |
| +const SkColor kExpectedPixels[] = { |
| + 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666, |
| + 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093, |
| + 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4, |
| + 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3, |
| + 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e, |
| + 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689, |
| + 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d, |
| + 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46, |
| + 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67, |
| + 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4, |
| + 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283, |
| + 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4, |
| + 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4, |
| + 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a, |
| + 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738, |
| + 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df |
| +}; |
| +SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight) |
| + == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch); |
| +} // namespace |
| + |
| +/** |
| + * Given either a SkStream or a SkData, try to decode the endoded |
|
scroggo
2013/12/16 15:38:36
encoded*
hal.canary
2013/12/17 17:08:11
Done.
|
| + * image using the specified options and report errors. |
| + */ |
| +static void test_options(skiatest::Reporter* reporter, |
| + const SkDecoderOptions& opts, |
| + SkStreamRewindable* encodedStream, |
| + SkData* encodedData, |
| + bool useData, |
| + const SkString& path) { |
| + SkBitmap bm; |
| + bool success = false; |
| + if (useData) { |
| + if (NULL == encodedData) { |
| + return; |
| + } |
| + success = SkInstallDiscardablePixelRef( |
| + SkNewDecodingImageGenerator(encodedData, opts), &bm, NULL); |
| + } else { |
| + if (NULL == encodedStream) { |
| + return; |
| + } |
| + success = SkInstallDiscardablePixelRef( |
| + SkNewDecodingImageGenerator(encodedStream->duplicate(), opts), |
| + &bm, NULL); |
| + } |
| + |
| + REPORTER_ASSERT(reporter, success |
| + || (SkBitmap::kARGB_4444_Config == opts.fRequestedConfig)); |
| + if (!success) { |
| + return; |
| + } |
| + REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight, |
| + opts.fSampleSize)); |
| + REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth, |
| + opts.fSampleSize)); |
| + |
| + SkAutoLockPixels alp(bm); |
| + REPORTER_ASSERT(reporter, bm.getPixels() != NULL); |
| + |
| + REPORTER_ASSERT(reporter, |
| + (SkBitmap::kNo_Config == opts.fRequestedConfig) |
| + || (bm.config() == opts.fRequestedConfig)); |
| + |
| + // Condition under which we should check the decoding results: |
| + if ((SkBitmap::kARGB_8888_Config == bm.config()) |
| + && (NULL != bm.getPixels()) |
| + && (!path.endsWith(".jpg")) // lossy |
| + && (!path.endsWith(".webp")) // decoder error |
| + && (opts.fSampleSize == 1)) { // scaled |
| + bool pixelError = false; |
| + const SkColor* correctPixels = kExpectedPixels; |
| + SkASSERT(bm.height() == kExpectedHeight); |
| + SkASSERT(bm.width() == kExpectedWidth); |
| + for (int y = 0; y < bm.height(); ++y) { |
| + for (int x = 0; x < bm.width(); ++x) { |
| + pixelError |= (*correctPixels != bm.getColor(x, y)); |
| + ++correctPixels; |
| + } |
| + } |
| + REPORTER_ASSERT(reporter, !pixelError); |
| + } |
| +} |
| + |
| +/** |
| + * SkDecodingImageGenerator has an Options struct which lets the |
| + * client of the generator set sample size, dithering, and bitmap |
| + * config. This test loops through many possible options and tries |
| + * them on a set of 5 small encoded images (each in a different |
| + * format). We test both SkData and SkStreamRewindable decoding. |
| + */ |
| +DEF_TEST(ImageDecoderOptions, reporter) { |
| + const char* files[] = { |
| + "randPixels.bmp", |
| + "randPixels.jpg", |
| + "randPixels.png", |
| + "randPixels.webp", |
| + "randPixels.gif" |
| + }; |
| + |
| + SkString resourceDir = skiatest::Test::GetResourcePath(); |
| + SkString directory = SkOSPath::SkPathJoin(resourceDir.c_str(), "encoding"); |
| + if (!sk_exists(directory.c_str())) { |
| + return; |
| + } |
| + |
| + int scaleList[] = {1, 2, 3, 4}; |
| + bool ditherList[] = {true, false}; |
| + SkBitmap::Config configList[] = { |
| + SkBitmap::kNo_Config, // Use whatever the decoder prefers. |
| + SkBitmap::kARGB_8888_Config, |
| + SkBitmap::kRGB_565_Config, |
| + SkBitmap::kA8_Config, |
| + SkBitmap::kARGB_4444_Config, // Most decoders will fail on 4444. |
| + // Note that indexed color is left out of the list. Lazy |
| + // decoding doesn't do indexed color. |
| + }; |
| + const bool useDataList[] = {true, false}; |
| + |
| + for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) { |
| + SkString path = SkOSPath::SkPathJoin(directory.c_str(), files[fidx]); |
| + if (!sk_exists(path.c_str())) { |
| + continue; |
| + } |
| + |
| + SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str())); |
| + REPORTER_ASSERT(reporter, encodedData.get() != NULL); |
| + SkAutoTUnref<SkStreamRewindable> encodedStream( |
| + SkStream::NewFromFile(path.c_str())); |
| + REPORTER_ASSERT(reporter, encodedStream.get() != NULL); |
| + |
| + for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) { |
| + for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) { |
| + for (size_t k = 0; k < SK_ARRAY_COUNT(configList); ++k) { |
| + SkDecoderOptions opts(scaleList[i], ditherList[j], |
| + configList[k]); |
| + for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) { |
| + test_options(reporter, opts, encodedStream, encodedData, |
| + useDataList[m], path); |
| + } |
| + } |
| + } |
| + } |
| + } |
| +} |
| + |