Index: tests/ImageDecodingTest.cpp |
diff --git a/tests/ImageDecodingTest.cpp b/tests/ImageDecodingTest.cpp |
index bbc31cc81fed21663885bccc497bee0c3534f72c..d39a51a134f413a5680012f9d4bb84b6e8dd9aad 100644 |
--- a/tests/ImageDecodingTest.cpp |
+++ b/tests/ImageDecodingTest.cpp |
@@ -13,10 +13,13 @@ |
#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 "SkImagePriv.h" |
#include "SkOSFile.h" |
#include "SkPoint.h" |
#include "SkShader.h" |
@@ -153,7 +156,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 { |
@@ -201,7 +204,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"); |
@@ -228,7 +231,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); |
@@ -239,6 +242,31 @@ DEF_TEST(ImageDecoding, reporter) { |
} |
//////////////////////////////////////////////////////////////////////////////// |
+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 |
DEF_TEST(WebP, reporter) { |
const unsigned char encodedWebP[] = { |
@@ -269,38 +297,26 @@ DEF_TEST(WebP, reporter) { |
0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0, |
0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b |
}; |
- const SkColor thePixels[] = { |
- 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 |
- }; |
SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP, |
sizeof(encodedWebP))); |
SkBitmap bm; |
- bool success = SkDecodingImageGenerator::Install(encoded, &bm, NULL); |
+ |
+ bool success = SkInstallDiscardablePixelRef( |
+ SkDecodingImageGenerator::Create(encoded, |
+ SkDecodingImageGenerator::Options()), &bm, NULL); |
+ |
REPORTER_ASSERT(reporter, success); |
if (!success) { |
return; |
} |
SkAutoLockPixels alp(bm); |
- bool rightSize = SK_ARRAY_COUNT(thePixels) == bm.width() * bm.height(); |
+ |
+ bool rightSize = ((kExpectedWidth == bm.width()) |
+ && (kExpectedHeight == bm.height())); |
REPORTER_ASSERT(reporter, rightSize); |
if (rightSize) { |
bool error = false; |
- const SkColor* correctPixel = thePixels; |
+ const SkColor* correctPixel = kExpectedPixels; |
for (int y = 0; y < bm.height(); ++y) { |
for (int x = 0; x < bm.width(); ++x) { |
error |= (*correctPixel != bm.getColor(x, y)); |
@@ -310,3 +326,256 @@ DEF_TEST(WebP, reporter) { |
REPORTER_ASSERT(reporter, !error); |
} |
} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+// 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()); |
+ SkColorType colorType; |
+ if (!SkBitmapConfigToColorType(bitmap->config(), &colorType)) { |
+ return NULL; |
+ } |
+ SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType); |
+ SkAutoTDelete<SkImageGenerator> gen( |
+ SkDecodingImageGenerator::Create(stream, opts)); |
+ SkImageInfo info; |
+ if ((NULL == gen.get()) || !gen->getInfo(&info)) { |
+ return NULL; |
+ } |
+ SkDiscardableMemory::Factory* factory = NULL; |
+ if (info.getSafeSize(info.minRowBytes()) < (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 SkDecodingImageGenerator::Create 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()); |
+ } |
+} |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) |
+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))); |
+} |
+#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX |
+ |
+ |
+#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) |
+ #define kBackwards_SkColorType kRGBA_8888_SkColorType |
+#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) |
+ #define kBackwards_SkColorType kBGRA_8888_SkColorType |
+#else |
+ #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order" |
+#endif |
+ |
+static inline const char* SkColorType_to_string(SkColorType colorType) { |
+ switch(colorType) { |
+ case kAlpha_8_SkColorType: return "Alpha_8"; |
+ case kRGB_565_SkColorType: return "RGB_565"; |
+ case kARGB_4444_SkColorType: return "ARGB_4444"; |
+ case kPMColor_SkColorType: return "PMColor"; |
+ case kBackwards_SkColorType: return "Backwards"; |
+ case kIndex_8_SkColorType: return "Index_8"; |
+ default: return "ERROR"; |
+ } |
+} |
+ |
+/** |
+ * Given either a SkStream or a SkData, try to decode the encoded |
+ * image using the specified options and report errors. |
+ */ |
+static void test_options(skiatest::Reporter* reporter, |
+ const SkDecodingImageGenerator::Options& opts, |
+ SkStreamRewindable* encodedStream, |
+ SkData* encodedData, |
+ bool useData, |
+ const SkString& path) { |
+ SkBitmap bm; |
+ bool success = false; |
+ if (useData) { |
+ if (NULL == encodedData) { |
+ return; |
+ } |
+ success = SkInstallDiscardablePixelRef( |
+ SkDecodingImageGenerator::Create(encodedData, opts), &bm, NULL); |
+ } else { |
+ if (NULL == encodedStream) { |
+ return; |
+ } |
+ success = SkInstallDiscardablePixelRef( |
+ SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), |
+ &bm, NULL); |
+ } |
+ if (!success) { |
+ if (opts.fUseRequestedColorType |
+ && (kARGB_4444_SkColorType == opts.fRequestedColorType)) { |
+ return; // Ignore known conversion inabilities. |
+ } |
+ // If we get here, it's a failure and we will need more |
+ // information about why it failed. |
+ reporter->reportFailed(SkStringPrintf( |
+ "Bounds decode failed " |
+ "[sampleSize=%d dither=%s colorType=%s %s] %s:%d", |
+ opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"), |
+ (opts.fUseRequestedColorType |
+ ? SkColorType_to_string(opts.fRequestedColorType) : "(none)"), |
+ path.c_str(), __FILE__, __LINE__)); |
+ return; |
+ } |
+ #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) |
+ // Android is the only system that use Skia's image decoders in |
+ // production. For now, we'll only verify that samplesize works |
+ // on systems where it already is known to work. |
+ REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight, |
+ opts.fSampleSize)); |
+ REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth, |
+ opts.fSampleSize)); |
+ #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX |
+ SkAutoLockPixels alp(bm); |
+ if (bm.getPixels() == NULL) { |
+ reporter->reportFailed(SkStringPrintf( |
+ "Pixel decode failed " |
+ "[sampleSize=%d dither=%s colorType=%s %s] %s:%d", |
+ opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"), |
+ (opts.fUseRequestedColorType |
+ ? SkColorType_to_string(opts.fRequestedColorType) : "(none)"), |
+ path.c_str(), __FILE__, __LINE__)); |
+ return; |
+ } |
+ |
+ SkBitmap::Config requestedConfig |
+ = SkColorTypeToBitmapConfig(opts.fRequestedColorType); |
+ REPORTER_ASSERT(reporter, |
+ (!opts.fUseRequestedColorType) |
+ || (bm.config() == requestedConfig)); |
+ |
+ // Condition under which we should check the decoding results: |
+ if ((SkBitmap::kARGB_8888_Config == bm.config()) |
+ && (!path.endsWith(".jpg")) // lossy |
+ && (opts.fSampleSize == 1)) { // scaled |
+ const SkColor* correctPixels = kExpectedPixels; |
+ SkASSERT(bm.height() == kExpectedHeight); |
+ SkASSERT(bm.width() == kExpectedWidth); |
+ int pixelErrors = 0; |
+ for (int y = 0; y < bm.height(); ++y) { |
+ for (int x = 0; x < bm.width(); ++x) { |
+ if (*correctPixels != bm.getColor(x, y)) { |
+ ++pixelErrors; |
+ } |
+ ++correctPixels; |
+ } |
+ } |
+ if (pixelErrors != 0) { |
+ reporter->reportFailed(SkStringPrintf( |
+ "Pixel-level mismatch (%d of %d) [sampleSize=%d " |
+ "dither=%s colorType=%s %s] %s:%d", |
+ pixelErrors, kExpectedHeight * kExpectedWidth, |
+ opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"), |
+ (opts.fUseRequestedColorType |
+ ? SkColorType_to_string(opts.fRequestedColorType) |
+ : "(none)"), path.c_str(), __FILE__, __LINE__)); |
+ } |
+ } |
+} |
+ |
+/** |
+ * 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", |
+ #if !defined(SK_BUILD_FOR_WIN) |
+ // TODO(halcanary): Find out why this fails sometimes. |
+ "randPixels.gif", |
+ #endif |
+ }; |
+ |
+ 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}; |
+ SkColorType colorList[] = { |
+ kAlpha_8_SkColorType, |
+ kRGB_565_SkColorType, |
+ kARGB_4444_SkColorType, // Most decoders will fail on 4444. |
+ kPMColor_SkColorType |
+ // 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 m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) { |
+ for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) { |
+ SkDecodingImageGenerator::Options opts(scaleList[i], |
+ ditherList[j], |
+ colorList[k]); |
+ test_options(reporter, opts, encodedStream, encodedData, |
+ useDataList[m], path); |
+ |
+ } |
+ SkDecodingImageGenerator::Options options(scaleList[i], |
+ ditherList[j]); |
+ test_options(reporter, options, encodedStream, encodedData, |
+ useDataList[m], path); |
+ } |
+ } |
+ } |
+ } |
+} |
+//////////////////////////////////////////////////////////////////////////////// |
+ |