SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { bool isWebp = SkWebpCodec::IsWebp(stream); if (!stream->rewind()) { return NULL; } if (isWebp) { // Webp codec supports scaling and subsetting natively return SkWebpCodec::NewFromStream(stream); } SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromStream(stream)); if (NULL == scanlineDecoder) { return NULL; } // wrap in new SkScaledCodec return SkNEW_ARGS(SkScaledCodec, (scanlineDecoder.detach())); }
static void check(skiatest::Reporter* r, const char path[], SkISize size, bool supportsScanlineDecoding, bool supportsSubsetDecoding, bool supports565 = true) { SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); if (!codec) { ERRORF(r, "Unable to decode '%s'", path); return; } // This test is used primarily to verify rewinding works properly. Using kN32 allows // us to test this without the added overhead of creating different bitmaps depending // on the color type (ex: building a color table for kIndex8). DM is where we test // decodes to all possible destination color types. SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); REPORTER_ASSERT(r, info.dimensions() == size); { // Test decoding to 565 SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType); SkCodec::Result expected = (supports565 && info.alphaType() == kOpaque_SkAlphaType) ? SkCodec::kSuccess : SkCodec::kInvalidConversion; test_info(r, codec, info565, expected, NULL); } SkBitmap bm; bm.allocPixels(info); SkAutoLockPixels autoLockPixels(bm); SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), NULL, NULL, NULL); REPORTER_ASSERT(r, result == SkCodec::kSuccess); SkMD5::Digest digest; md5(bm, &digest); // verify that re-decoding gives the same result. test_info(r, codec, info, SkCodec::kSuccess, &digest); { // Check alpha type conversions if (info.alphaType() == kOpaque_SkAlphaType) { test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType), SkCodec::kInvalidConversion, NULL); test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType), SkCodec::kInvalidConversion, NULL); } else { // Decoding to opaque should fail test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType), SkCodec::kInvalidConversion, NULL); SkAlphaType otherAt = info.alphaType(); if (kPremul_SkAlphaType == otherAt) { otherAt = kUnpremul_SkAlphaType; } else { otherAt = kPremul_SkAlphaType; } // The other non-opaque alpha type should always succeed, but not match. test_info(r, codec, info.makeAlphaType(otherAt), SkCodec::kSuccess, NULL); } } // Scanline decoding follows. stream.reset(resource(path)); SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( SkScanlineDecoder::NewFromStream(stream.detach())); if (supportsScanlineDecoding) { bm.eraseColor(SK_ColorYELLOW); REPORTER_ASSERT(r, scanlineDecoder); REPORTER_ASSERT(r, scanlineDecoder->start(info) == SkCodec::kSuccess); for (int y = 0; y < info.height(); y++) { result = scanlineDecoder->getScanlines(bm.getAddr(0, y), 1, 0); REPORTER_ASSERT(r, result == SkCodec::kSuccess); } // verify that scanline decoding gives the same result. compare_to_good_digest(r, digest, bm); } else { REPORTER_ASSERT(r, !scanlineDecoder); } // The rest of this function tests decoding subsets, and will decode an arbitrary number of // random subsets. // Do not attempt to decode subsets of an image of only once pixel, since there is no // meaningful subset. if (size.width() * size.height() == 1) { return; } SkRandom rand; SkIRect subset; SkCodec::Options opts; opts.fSubset = ⊂ for (int i = 0; i < 5; i++) { subset = generate_random_subset(&rand, size.width(), size.height()); SkASSERT(!subset.isEmpty()); const bool supported = codec->getValidSubset(&subset); REPORTER_ASSERT(r, supported == supportsSubsetDecoding); SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height()); SkBitmap bm; bm.allocPixels(subsetInfo); const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts, NULL, NULL); if (supportsSubsetDecoding) { REPORTER_ASSERT(r, result == SkCodec::kSuccess); // Webp is the only codec that supports subsets, and it will have modified the subset // to have even left/top. REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); } else { // No subsets will work. REPORTER_ASSERT(r, result == SkCodec::kUnimplemented); } } }
void SubsetZoomBench::onDraw(const int n, SkCanvas* canvas) { // When the color type is kIndex8, we will need to store the color table. If it is // used, it will be initialized by the codec. int colorCount; SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( SkScanlineDecoder::NewFromStream(fStream->duplicate())); const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); scanlineDecoder->start(info, nullptr, colors, &colorCount); const int centerX = info.width() / 2; const int centerY = info.height() / 2; int w = fSubsetWidth; int h = fSubsetHeight; do { const int subsetStartX = SkTMax(0, centerX - w / 2); const int subsetStartY = SkTMax(0, centerY - h / 2); const int subsetWidth = SkTMin(w, info.width() - subsetStartX); const int subsetHeight = SkTMin(h, info.height() - subsetStartY); // Note that if we subsetted and scaled in a single step, we could use the // same bitmap - as is often done in actual use cases. SkBitmap bitmap; SkImageInfo subsetInfo = info.makeWH(subsetWidth, subsetHeight); alloc_pixels(&bitmap, subsetInfo, colors, colorCount); uint32_t bpp = info.bytesPerPixel(); scanlineDecoder->skipScanlines(subsetStartY); for (int y = 0; y < subsetHeight; y++) { scanlineDecoder->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + subsetStartX * bpp, subsetWidth * bpp); } w <<= 1; h <<= 1; } while (w < 2 * info.width() || h < 2 * info.height()); } } else { for (int count = 0; count < n; count++) { int width, height; SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); decoder->buildTileIndex(fStream->duplicate(), &width, &height); const int centerX = width / 2; const int centerY = height / 2; int w = fSubsetWidth; int h = fSubsetHeight; do { const int subsetStartX = SkTMax(0, centerX - w / 2); const int subsetStartY = SkTMax(0, centerY - h / 2); const int subsetWidth = SkTMin(w, width - subsetStartX); const int subsetHeight = SkTMin(h, height - subsetStartY); SkBitmap bitmap; SkIRect rect = SkIRect::MakeXYWH(subsetStartX, subsetStartY, subsetWidth, subsetHeight); decoder->decodeSubset(&bitmap, rect, fColorType); w <<= 1; h <<= 1; } while (w < 2 * width || h < 2 * height); } } }
void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) { // When the color type is kIndex8, we will need to store the color table. If it is // used, it will be initialized by the codec. int colorCount; SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( SkScanlineDecoder::NewFromStream(fStream->duplicate())); const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); scanlineDecoder->start(info, nullptr, colors, &colorCount); SkBitmap bitmap; // Note that we use the same bitmap for all of the subsets. // It might be larger than necessary for the end subsets. SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight); alloc_pixels(&bitmap, subsetInfo, colors, colorCount); for (int x = 0; x < info.width(); x += fSubsetWidth) { for (int y = 0; y < info.height(); y += fSubsetHeight) { scanlineDecoder->skipScanlines(y); const uint32_t currSubsetWidth = x + (int) fSubsetWidth > info.width() ? info.width() - x : fSubsetWidth; const uint32_t currSubsetHeight = y + (int) fSubsetHeight > info.height() ? info.height() - y : fSubsetHeight; const uint32_t bpp = info.bytesPerPixel(); for (uint32_t y = 0; y < currSubsetHeight; y++) { scanlineDecoder->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + x * bpp, currSubsetWidth * bpp); } } } } } else { // We create a color table here to satisfy allocPixels() when the output // type is kIndex8. It's okay that this is uninitialized since we never // use it. SkColorTable* colorTable = new SkColorTable(colors, 0); for (int count = 0; count < n; count++) { int width, height; SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); decoder->buildTileIndex(fStream->duplicate(), &width, &height); SkBitmap bitmap; // Note that we use the same bitmap for all of the subsets. // It might be larger than necessary for the end subsets. // If we do not include this step, decodeSubset() would allocate space // for the pixels automatically, but this would not allow us to reuse the // same bitmap as the other subsets. We want to reuse the same bitmap // because it gives a more fair comparison with SkCodec and is a common // use case of BitmapRegionDecoder. bitmap.allocPixels(SkImageInfo::Make(fSubsetWidth, fSubsetHeight, fColorType, kOpaque_SkAlphaType), nullptr, colorTable); for (int x = 0; x < width; x += fSubsetWidth) { for (int y = 0; y < height; y += fSubsetHeight) { const uint32_t currSubsetWidth = x + (int) fSubsetWidth > width ? width - x : fSubsetWidth; const uint32_t currSubsetHeight = y + (int) fSubsetHeight > height ? height - y : fSubsetHeight; SkIRect rect = SkIRect::MakeXYWH(x, y, currSubsetWidth, currSubsetHeight); decoder->decodeSubset(&bitmap, rect, fColorType); } } } } }