/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkBitmap.h" #include "SkCachingPixelRef.h" #include "SkCanvas.h" #include "SkData.h" #include "SkDecodingImageGenerator.h" #include "SkDiscardableMemoryPool.h" #include "SkImageDecoder.h" #include "SkImageGeneratorPriv.h" #include "SkScaledImageCache.h" #include "SkStream.h" #include "SkUtils.h" #include "Test.h" /** * Fill this bitmap with some color. */ static void make_test_image(SkBitmap* bm) { const int W = 50, H = 50; bm->allocN32Pixels(W, H); bm->eraseColor(SK_ColorBLACK); SkCanvas canvas(*bm); SkPaint paint; paint.setColor(SK_ColorBLUE); canvas.drawRectCoords(0, 0, SkIntToScalar(W/2), SkIntToScalar(H/2), paint); paint.setColor(SK_ColorWHITE); canvas.drawRectCoords(SkIntToScalar(W/2), SkIntToScalar(H/2), SkIntToScalar(W), SkIntToScalar(H), paint); } /** * encode this bitmap into some data via SkImageEncoder */ static SkData* create_data_from_bitmap(const SkBitmap& bm, SkImageEncoder::Type type) { SkDynamicMemoryWStream stream; if (SkImageEncoder::EncodeStream(&stream, bm, type, 100)) { return stream.copyToData(); } return NULL; } //////////////////////////////////////////////////////////////////////////////// static void compare_bitmaps(skiatest::Reporter* reporter, const SkBitmap& b1, const SkBitmap& b2, bool pixelPerfect = true) { REPORTER_ASSERT(reporter, b1.empty() == b2.empty()); REPORTER_ASSERT(reporter, b1.width() == b2.width()); REPORTER_ASSERT(reporter, b1.height() == b2.height()); REPORTER_ASSERT(reporter, b1.isNull() == b2.isNull()); SkAutoLockPixels autoLockPixels1(b1); SkAutoLockPixels autoLockPixels2(b2); REPORTER_ASSERT(reporter, b1.isNull() == b2.isNull()); if (b1.isNull() || b1.empty()) { return; } REPORTER_ASSERT(reporter, NULL != b1.getPixels()); REPORTER_ASSERT(reporter, NULL != b2.getPixels()); if ((!(b1.getPixels())) || (!(b2.getPixels()))) { return; } if ((b1.width() != b2.width()) || (b1.height() != b2.height())) { return; } if (!pixelPerfect) { return; } int pixelErrors = 0; for (int y = 0; y < b2.height(); ++y) { for (int x = 0; x < b2.width(); ++x) { if (b1.getColor(x, y) != b2.getColor(x, y)) { ++pixelErrors; } } } REPORTER_ASSERT(reporter, 0 == pixelErrors); } typedef bool (*InstallEncoded)(SkData* encoded, SkBitmap* dst); /** This function tests three differently encoded images against the original bitmap */ static void test_three_encodings(skiatest::Reporter* reporter, InstallEncoded install) { SkBitmap original; make_test_image(&original); REPORTER_ASSERT(reporter, !original.empty()); REPORTER_ASSERT(reporter, !original.isNull()); if (original.empty() || original.isNull()) { return; } static const SkImageEncoder::Type types[] = { SkImageEncoder::kPNG_Type, SkImageEncoder::kJPEG_Type, SkImageEncoder::kWEBP_Type }; for (size_t i = 0; i < SK_ARRAY_COUNT(types); i++) { SkImageEncoder::Type type = types[i]; SkAutoDataUnref encoded(create_data_from_bitmap(original, type)); REPORTER_ASSERT(reporter, encoded.get() != NULL); if (NULL == encoded.get()) { continue; } SkBitmap lazy; bool installSuccess = install(encoded.get(), &lazy); REPORTER_ASSERT(reporter, installSuccess); if (!installSuccess) { continue; } REPORTER_ASSERT(reporter, NULL == lazy.getPixels()); { SkAutoLockPixels autoLockPixels(lazy); // now pixels are good. REPORTER_ASSERT(reporter, NULL != lazy.getPixels()); if (NULL == lazy.getPixels()) { continue; } } // pixels should be gone! REPORTER_ASSERT(reporter, NULL == lazy.getPixels()); { SkAutoLockPixels autoLockPixels(lazy); // now pixels are good. REPORTER_ASSERT(reporter, NULL != lazy.getPixels()); if (NULL == lazy.getPixels()) { continue; } } bool comparePixels = (SkImageEncoder::kPNG_Type == type); compare_bitmaps(reporter, original, lazy, comparePixels); } } //////////////////////////////////////////////////////////////////////////////// static bool install_skCachingPixelRef(SkData* encoded, SkBitmap* dst) { return SkCachingPixelRef::Install( SkDecodingImageGenerator::Create( encoded, SkDecodingImageGenerator::Options()), dst); } static bool install_skDiscardablePixelRef(SkData* encoded, SkBitmap* dst) { // Use system-default discardable memory. return SkInstallDiscardablePixelRef( SkDecodingImageGenerator::Create( encoded, SkDecodingImageGenerator::Options()), dst); } //////////////////////////////////////////////////////////////////////////////// /** * This checks to see that a SkCachingPixelRef and a * SkDiscardablePixelRef works as advertised with a * SkDecodingImageGenerator. */ DEF_TEST(DecodingImageGenerator, reporter) { test_three_encodings(reporter, install_skCachingPixelRef); test_three_encodings(reporter, install_skDiscardablePixelRef); } class TestImageGenerator : public SkImageGenerator { public: enum TestType { kFailGetInfo_TestType, kFailGetPixels_TestType, kSucceedGetPixels_TestType, kLast_TestType = kSucceedGetPixels_TestType }; static int Width() { return 10; } static int Height() { return 10; } static SkColor Color() { return SK_ColorCYAN; } TestImageGenerator(TestType type, skiatest::Reporter* reporter) : fType(type), fReporter(reporter) { SkASSERT((fType <= kLast_TestType) && (fType >= 0)); } virtual ~TestImageGenerator() { } protected: virtual bool onGetInfo(SkImageInfo* info) SK_OVERRIDE { REPORTER_ASSERT(fReporter, NULL != info); if ((NULL == info) || (kFailGetInfo_TestType == fType)) { return false; } info->fWidth = TestImageGenerator::Width(); info->fHeight = TestImageGenerator::Height(); info->fColorType = kN32_SkColorType; info->fAlphaType = kOpaque_SkAlphaType; return true; } virtual bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) SK_OVERRIDE { REPORTER_ASSERT(fReporter, pixels != NULL); size_t minRowBytes = static_cast(info.fWidth * info.bytesPerPixel()); REPORTER_ASSERT(fReporter, rowBytes >= minRowBytes); if ((NULL == pixels) || (fType != kSucceedGetPixels_TestType) || (info.fColorType != kN32_SkColorType)) { return false; } char* bytePtr = static_cast(pixels); for (int y = 0; y < info.fHeight; ++y) { sk_memset32(reinterpret_cast(bytePtr), TestImageGenerator::Color(), info.fWidth); bytePtr += rowBytes; } return true; } private: const TestType fType; skiatest::Reporter* const fReporter; }; static void check_test_image_generator_bitmap(skiatest::Reporter* reporter, const SkBitmap& bm) { REPORTER_ASSERT(reporter, TestImageGenerator::Width() == bm.width()); REPORTER_ASSERT(reporter, TestImageGenerator::Height() == bm.height()); SkAutoLockPixels autoLockPixels(bm); REPORTER_ASSERT(reporter, NULL != bm.getPixels()); if (NULL == bm.getPixels()) { return; } int errors = 0; for (int y = 0; y < bm.height(); ++y) { for (int x = 0; x < bm.width(); ++x) { if (TestImageGenerator::Color() != *bm.getAddr32(x, y)) { ++errors; } } } REPORTER_ASSERT(reporter, 0 == errors); } enum PixelRefType { kSkCaching_PixelRefType, kSkDiscardable_PixelRefType, kLast_PixelRefType = kSkDiscardable_PixelRefType }; static void check_pixelref(TestImageGenerator::TestType type, skiatest::Reporter* reporter, PixelRefType pixelRefType, SkDiscardableMemory::Factory* factory) { SkASSERT((pixelRefType >= 0) && (pixelRefType <= kLast_PixelRefType)); SkAutoTDelete gen(SkNEW_ARGS(TestImageGenerator, (type, reporter))); REPORTER_ASSERT(reporter, gen.get() != NULL); SkBitmap lazy; bool success; if (kSkCaching_PixelRefType == pixelRefType) { // Ignore factory; use global SkScaledImageCache. success = SkCachingPixelRef::Install(gen.detach(), &lazy); } else { success = SkInstallDiscardablePixelRef(gen.detach(), &lazy, factory); } REPORTER_ASSERT(reporter, success == (TestImageGenerator::kFailGetInfo_TestType != type)); if (TestImageGenerator::kSucceedGetPixels_TestType == type) { check_test_image_generator_bitmap(reporter, lazy); } else if (TestImageGenerator::kFailGetPixels_TestType == type) { SkAutoLockPixels autoLockPixels(lazy); REPORTER_ASSERT(reporter, NULL == lazy.getPixels()); } } // new/lock/delete is an odd pattern for a pixelref, but it needs to not assert static void test_newlockdelete(skiatest::Reporter* reporter) { SkBitmap bm; SkImageGenerator* ig = new TestImageGenerator( TestImageGenerator::kSucceedGetPixels_TestType, reporter); SkInstallDiscardablePixelRef(ig, &bm); bm.pixelRef()->lockPixels(); } /** * This tests the basic functionality of SkDiscardablePixelRef with a * basic SkImageGenerator implementation and several * SkDiscardableMemory::Factory choices. */ DEF_TEST(DiscardableAndCachingPixelRef, reporter) { test_newlockdelete(reporter); check_pixelref(TestImageGenerator::kFailGetInfo_TestType, reporter, kSkCaching_PixelRefType, NULL); check_pixelref(TestImageGenerator::kFailGetPixels_TestType, reporter, kSkCaching_PixelRefType, NULL); check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType, reporter, kSkCaching_PixelRefType, NULL); check_pixelref(TestImageGenerator::kFailGetInfo_TestType, reporter, kSkDiscardable_PixelRefType, NULL); check_pixelref(TestImageGenerator::kFailGetPixels_TestType, reporter, kSkDiscardable_PixelRefType, NULL); check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType, reporter, kSkDiscardable_PixelRefType, NULL); SkAutoTUnref pool( SkDiscardableMemoryPool::Create(1, NULL)); REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); check_pixelref(TestImageGenerator::kFailGetPixels_TestType, reporter, kSkDiscardable_PixelRefType, pool); REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType, reporter, kSkDiscardable_PixelRefType, pool); REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); SkDiscardableMemoryPool* globalPool = SkGetGlobalDiscardableMemoryPool(); // Only acts differently from NULL on a platform that has a // default discardable memory implementation that differs from the // global DM pool. check_pixelref(TestImageGenerator::kFailGetPixels_TestType, reporter, kSkDiscardable_PixelRefType, globalPool); check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType, reporter, kSkDiscardable_PixelRefType, globalPool); }