Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Unified Diff: src/codec/SkScaledCodec.cpp

Issue 1406223002: Create an SkAndroidCodec API separate from SkCodec (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Win bot fix Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/codec/SkSampledCodec.h ('k') | src/codec/SkSwizzler.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/codec/SkScaledCodec.cpp
diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp
index 6b50a09f268c3889e99831098980486aef235af7..c2be2d6ede8a5525db44b21c84c2315c63fe8b99 100644
--- a/src/codec/SkScaledCodec.cpp
+++ b/src/codec/SkScaledCodec.cpp
@@ -5,344 +5,210 @@
* found in the LICENSE file.
*/
+#include "SkCodec.h"
#include "SkCodecPriv.h"
-#include "SkScaledCodec.h"
-#include "SkStream.h"
-#include "SkWebpCodec.h"
+#include "SkSampledCodec.h"
+// FIXME: Rename this file to SkSampledCodec.cpp
-SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) {
- SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream));
- if (nullptr == codec) {
- return nullptr;
- }
-
- switch (codec->getEncodedFormat()) {
- case kWEBP_SkEncodedFormat:
- // Webp codec supports scaling and subsetting natively
- return codec.detach();
- case kPNG_SkEncodedFormat:
- case kJPEG_SkEncodedFormat:
- // wrap in new SkScaledCodec
- return new SkScaledCodec(codec.detach());
- default:
- // FIXME: SkScaledCodec is temporarily disabled for other formats
- // while focusing on the formats that are supported by
- // BitmapRegionDecoder.
- return nullptr;
- }
-}
-
-SkCodec* SkScaledCodec::NewFromData(SkData* data) {
- if (!data) {
- return nullptr;
- }
- return NewFromStream(new SkMemoryStream(data));
-}
-
-SkScaledCodec::SkScaledCodec(SkCodec* codec)
- : INHERITED(codec->getInfo(), nullptr)
+SkSampledCodec::SkSampledCodec(SkCodec* codec)
+ : INHERITED(codec->getInfo())
, fCodec(codec)
{}
-SkScaledCodec::~SkScaledCodec() {}
-
-bool SkScaledCodec::onRewind() {
- return fCodec->onRewind();
-}
-
-static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& nativeDims,
- const SkISize& scaledCodecDims, float desiredScale) {
- if (nativeDims == scaledCodecDims) {
- // does not matter which to return if equal. Return here to skip below calculations
- return nativeDims;
+SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const {
+ // Fast path for when we are not scaling.
+ if (1 == sampleSize) {
+ return fCodec->getInfo().dimensions();
}
- float idealWidth = origDims.width() * desiredScale;
- float idealHeight = origDims.height() * desiredScale;
- // calculate difference between native dimensions and ideal dimensions
- float nativeWDiff = SkTAbs(idealWidth - nativeDims.width());
- float nativeHDiff = SkTAbs(idealHeight - nativeDims.height());
- float nativeDiff = nativeWDiff + nativeHDiff;
+ const int width = fCodec->getInfo().width();
+ const int height = fCodec->getInfo().height();
+ // Check if the codec can provide the scaling natively.
+ float scale = get_scale_from_sample_size(sampleSize);
+ SkSize idealSize = SkSize::Make(scale * (float) width, scale * (float) height);
+ SkISize nativeSize = fCodec->getScaledDimensions(scale);
+ float widthDiff = SkTAbs(((float) nativeSize.width()) - idealSize.width());
+ float heightDiff = SkTAbs(((float) nativeSize.height()) - idealSize.height());
// Native scaling is preferred to sampling. If we can scale natively to
// within one of the ideal value, we should choose to scale natively.
- if (nativeWDiff < 1.0f && nativeHDiff < 1.0f) {
- return nativeDims;
+ if (widthDiff < 1.0f && heightDiff < 1.0f) {
+ return nativeSize;
}
- // calculate difference between scaledCodec dimensions and ideal dimensions
- float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width());
- float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height());
- float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff;
-
- // return dimensions closest to ideal dimensions.
- // If the differences are equal, return nativeDims, as native scaling is more efficient.
- return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims;
+ // Provide the scaling by sampling.
+ return SkISize::Make(get_scaled_dimension(width, sampleSize),
+ get_scaled_dimension(height, sampleSize));
}
-/*
- * Return a valid set of output dimensions for this decoder, given an input scale
- */
-SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const {
- SkISize nativeDimensions = fCodec->getScaledDimensions(desiredScale);
- // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ...
- SkISize scaledCodecDimensions;
- if (desiredScale > 0.5f) {
- // sampleSize = 1
- scaledCodecDimensions = fCodec->getInfo().dimensions();
- }
- // sampleSize determines the step size between samples
- // Ex: sampleSize = 2, sample every second pixel in x and y directions
- int sampleSize = int ((1.0f / desiredScale) + 0.5f);
-
- int scaledWidth = get_scaled_dimension(this->getInfo().width(), sampleSize);
- int scaledHeight = get_scaled_dimension(this->getInfo().height(), sampleSize);
-
- // Return the calculated output dimensions for the given scale
- scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight);
-
- return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions,
- scaledCodecDimensions, desiredScale);
-}
-
-// check if scaling to dstInfo size from srcInfo size using sampleSize is possible
-static bool scaling_supported(const SkISize& dstDim, const SkISize& srcDim,
- int* sampleX, int* sampleY) {
- SkScaledCodec::ComputeSampleSize(dstDim, srcDim, sampleX, sampleY);
- const int dstWidth = dstDim.width();
- const int dstHeight = dstDim.height();
- const int srcWidth = srcDim.width();
- const int srcHeight = srcDim.height();
- // only support down sampling, not up sampling
- if (dstWidth > srcWidth || dstHeight > srcHeight) {
- return false;
- }
- // check that srcWidth is scaled down by an integer value
- if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) {
- return false;
- }
- // check that src height is scaled down by an integer value
- if (get_scaled_dimension(srcHeight, *sampleY) != dstHeight) {
- return false;
- }
- // sampleX and sampleY should be equal unless the original sampleSize requested was larger
- // than srcWidth or srcHeight. If so, the result of this is dstWidth or dstHeight = 1.
- // This functionality allows for tall thin images to still be scaled down by scaling factors.
- if (*sampleX != *sampleY){
- if (1 != dstWidth && 1 != dstHeight) {
- return false;
+SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
+ size_t rowBytes, AndroidOptions& options) {
+ // Create an Options struct for the codec.
+ SkCodec::Options codecOptions;
+ codecOptions.fZeroInitialized = options.fZeroInitialized;
+
+ SkIRect* subset = options.fSubset;
+ if (!subset || subset->size() == fCodec->getInfo().dimensions()) {
+ if (fCodec->dimensionsSupported(info.dimensions())) {
+ return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, options.fColorPtr,
+ options.fColorCount);
}
- }
- return true;
-}
-bool SkScaledCodec::onDimensionsSupported(const SkISize& dim) {
- // Check with fCodec first. No need to call the non-virtual version, which
- // just checks if it matches the original, since a match means this method
- // will not be called.
- if (fCodec->onDimensionsSupported(dim)) {
- return true;
+ // If the native codec does not support the requested scale, scale by sampling.
+ return this->sampledDecode(info, pixels, rowBytes, options);
}
- // FIXME: These variables are unused, but are needed by scaling_supported.
- // This class could also cache these values, and avoid calling this in
- // onGetPixels (since getPixels already calls it).
- int sampleX;
- int sampleY;
- return scaling_supported(dim, this->getInfo().dimensions(), &sampleX, &sampleY);
-}
-
-// calculates sampleSize in x and y direction
-void SkScaledCodec::ComputeSampleSize(const SkISize& dstDim, const SkISize& srcDim,
- int* sampleXPtr, int* sampleYPtr) {
- int srcWidth = srcDim.width();
- int dstWidth = dstDim.width();
- int srcHeight = srcDim.height();
- int dstHeight = dstDim.height();
-
- int sampleX = srcWidth / dstWidth;
- int sampleY = srcHeight / dstHeight;
-
- // only support down sampling, not up sampling
- SkASSERT(dstWidth <= srcWidth);
- SkASSERT(dstHeight <= srcHeight);
+ // We are performing a subset decode.
+ int sampleSize = options.fSampleSize;
+ SkISize scaledSize = this->onGetSampledDimensions(sampleSize);
+ if (!fCodec->dimensionsSupported(scaledSize)) {
+ // If the native codec does not support the requested scale, scale by sampling.
+ return this->sampledDecode(info, pixels, rowBytes, options);
+ }
- // sampleX and sampleY should be equal unless the original sampleSize requested was
- // larger than srcWidth or srcHeight.
- // If so, the result of this is dstWidth or dstHeight = 1. This functionality
- // allows for tall thin images to still be scaled down by scaling factors.
+ // Calculate the scaled subset bounds.
+ int scaledSubsetX = subset->x() / sampleSize;
+ int scaledSubsetY = subset->y() / sampleSize;
+ int scaledSubsetWidth = info.width();
+ int scaledSubsetHeight = info.height();
- if (sampleX != sampleY){
- if (1 != dstWidth && 1 != dstHeight) {
+ // Start the scanline decode.
+ SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
+ scaledSize.height());
+ codecOptions.fSubset = &scanlineSubset;
+ SkCodec::Result result = fCodec->startScanlineDecode(info.makeWH(scaledSize.width(),
+ scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount);
+ if (SkCodec::kSuccess != result) {
+ return result;
+ }
- // rounding during onGetScaledDimensions can cause different sampleSizes
- // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10
- // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2 = 10
- // correct for this rounding by comparing width to sampleY and height to sampleX
+ // At this point, we are only concerned with subsetting. Either no scale was
+ // requested, or the fCodec is handling the scale.
+ switch (fCodec->getScanlineOrder()) {
+ case SkCodec::kTopDown_SkScanlineOrder:
+ case SkCodec::kNone_SkScanlineOrder: {
+ if (!fCodec->skipScanlines(scaledSubsetY)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ scaledSubsetHeight, 0);
+ return SkCodec::kIncompleteInput;
+ }
- if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) {
- sampleX = sampleY;
- } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) {
- sampleY = sampleX;
+ int decodedLines = fCodec->getScanlines(pixels, scaledSubsetHeight, rowBytes);
+ if (decodedLines != scaledSubsetHeight) {
+ return SkCodec::kIncompleteInput;
}
+ return SkCodec::kSuccess;
}
- }
-
- if (sampleXPtr) {
- *sampleXPtr = sampleX;
- }
- if (sampleYPtr) {
- *sampleYPtr = sampleY;
+ default:
+ SkASSERT(false);
+ return SkCodec::kUnimplemented;
}
}
-// TODO: Implement subsetting in onGetPixels which works when and when not sampling
-SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
- size_t rowBytes, const Options& options,
- SkPMColor ctable[], int* ctableCount,
- int* rowsDecoded) {
+SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
+ size_t rowBytes, AndroidOptions& options) {
+ // Create options struct for the codec.
+ SkCodec::Options sampledOptions;
+ sampledOptions.fZeroInitialized = options.fZeroInitialized;
+ // Check if there is a subset.
+ SkIRect subset;
+ int subsetY = 0;
+ int subsetWidth = fCodec->getInfo().width();
+ int subsetHeight = fCodec->getInfo().height();
if (options.fSubset) {
- // Subsets are not supported.
- return kUnimplemented;
- }
-
- if (fCodec->dimensionsSupported(requestedInfo.dimensions())) {
- // Make sure that the parent class does not fill on an incomplete decode, since
- // fCodec will take care of filling the uninitialized lines.
- *rowsDecoded = requestedInfo.height();
- return fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable, ctableCount);
- }
-
- // scaling requested
- int sampleX;
- int sampleY;
- if (!scaling_supported(requestedInfo.dimensions(), fCodec->getInfo().dimensions(),
- &sampleX, &sampleY)) {
- // onDimensionsSupported would have returned false, meaning we should never reach here.
- SkASSERT(false);
- return kInvalidScale;
- }
-
- // set first sample pixel in y direction
- const int Y0 = get_start_coord(sampleY);
-
- const int dstHeight = requestedInfo.height();
- const int srcWidth = fCodec->getInfo().width();
- const int srcHeight = fCodec->getInfo().height();
-
- const SkImageInfo info = requestedInfo.makeWH(srcWidth, srcHeight);
-
- Result result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount);
-
- if (kSuccess != result) {
+ // We will need to know about subsetting in the y-dimension in order to use the
+ // scanline decoder.
+ SkIRect* subsetPtr = options.fSubset;
+ subsetY = subsetPtr->y();
+ subsetWidth = subsetPtr->width();
+ subsetHeight = subsetPtr->height();
+
+ // The scanline decoder only needs to be aware of subsetting in the x-dimension.
+ subset.setXYWH(subsetPtr->x(), 0, subsetWidth, fCodec->getInfo().height());
+ sampledOptions.fSubset = &subset;
+ }
+
+ // Start the scanline decode.
+ SkCodec::Result result = fCodec->startScanlineDecode(
+ info.makeWH(fCodec->getInfo().width(), fCodec->getInfo().height()), &sampledOptions,
+ options.fColorPtr, options.fColorCount);
+ if (SkCodec::kSuccess != result) {
return result;
}
SkSampler* sampler = fCodec->getSampler(true);
if (!sampler) {
- return kUnimplemented;
+ return SkCodec::kUnimplemented;
}
- if (sampler->setSampleX(sampleX) != requestedInfo.width()) {
- return kInvalidScale;
+ // Since we guarantee that output dimensions are always at least one (even if the sampleSize
+ // is greater than a given dimension), the input sampleSize is not always the sampleSize that
+ // we use in practice.
+ const int sampleX = subsetWidth / info.width();
+ const int sampleY = subsetHeight / info.height();
+ if (sampler->setSampleX(sampleX) != info.width()) {
+ return SkCodec::kInvalidScale;
+ }
+ if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
+ return SkCodec::kInvalidScale;
}
+ const int samplingOffsetY = get_start_coord(sampleY);
+ const int startY = samplingOffsetY + subsetY;
+ int dstHeight = info.height();
switch(fCodec->getScanlineOrder()) {
case SkCodec::kTopDown_SkScanlineOrder: {
- if (!fCodec->skipScanlines(Y0)) {
- *rowsDecoded = 0;
- return kIncompleteInput;
+ if (!fCodec->skipScanlines(startY)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ dstHeight, 0);
+ return SkCodec::kIncompleteInput;
}
+ void* pixelPtr = pixels;
for (int y = 0; y < dstHeight; y++) {
- if (1 != fCodec->getScanlines(dst, 1, rowBytes)) {
- // The failed call to getScanlines() will take care of
- // filling the failed row, so we indicate that we have
- // decoded (y + 1) rows.
- *rowsDecoded = y + 1;
- return kIncompleteInput;
- }
- if (y < dstHeight - 1) {
- if (!fCodec->skipScanlines(sampleY - 1)) {
- *rowsDecoded = y + 1;
- return kIncompleteInput;
- }
- }
- dst = SkTAddOffset<void>(dst, rowBytes);
- }
- return kSuccess;
- }
- case SkCodec::kBottomUp_SkScanlineOrder:
- case SkCodec::kOutOfOrder_SkScanlineOrder: {
- Result result = kSuccess;
- int y;
- for (y = 0; y < srcHeight; y++) {
- int srcY = fCodec->nextScanline();
- if (is_coord_necessary(srcY, sampleY, dstHeight)) {
- void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY));
- if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) {
- result = kIncompleteInput;
- break;
- }
- } else {
- if (!fCodec->skipScanlines(1)) {
- result = kIncompleteInput;
- break;
- }
+ if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ dstHeight, y + 1);
+ return SkCodec::kIncompleteInput;
}
- }
-
- // We handle filling uninitialized memory here instead of in the parent class.
- // The parent class does not know that we are sampling.
- if (kIncompleteInput == result) {
- const uint32_t fillValue = fCodec->getFillValue(requestedInfo.colorType(),
- requestedInfo.alphaType());
- for (; y < srcHeight; y++) {
- int srcY = fCodec->outputScanline(y);
- if (is_coord_necessary(srcY, sampleY, dstHeight)) {
- void* dstRow = SkTAddOffset<void>(dst,
- rowBytes * get_dst_coord(srcY, sampleY));
- SkSampler::Fill(requestedInfo.makeWH(requestedInfo.width(), 1), dstRow,
- rowBytes, fillValue, options.fZeroInitialized);
- }
+ int linesToSkip = SkTMin(sampleY - 1, dstHeight - y - 1);
+ if (!fCodec->skipScanlines(linesToSkip)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ dstHeight, y + 1);
+ return SkCodec::kIncompleteInput;
}
- *rowsDecoded = dstHeight;
+ pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
}
- return result;
+ return SkCodec::kSuccess;
}
case SkCodec::kNone_SkScanlineOrder: {
- SkAutoMalloc storage(srcHeight * rowBytes);
+ const int linesNeeded = subsetHeight - samplingOffsetY;
+ SkAutoMalloc storage(linesNeeded * rowBytes);
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
- int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes);
- storagePtr += Y0 * rowBytes;
- scanlines -= Y0;
- int y = 0;
- while (y < dstHeight && scanlines > 0) {
- memcpy(dst, storagePtr, rowBytes);
+
+ if (!fCodec->skipScanlines(startY)) {
+ fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
+ dstHeight, 0);
+ return SkCodec::kIncompleteInput;
+ }
+ int scanlines = fCodec->getScanlines(storagePtr, linesNeeded, rowBytes);
+
+ for (int y = 0; y < dstHeight; y++) {
+ memcpy(pixels, storagePtr, info.minRowBytes());
storagePtr += sampleY * rowBytes;
- dst = SkTAddOffset<void>(dst, rowBytes);
- scanlines -= sampleY;
- y++;
+ pixels = SkTAddOffset<void>(pixels, rowBytes);
}
- if (y < dstHeight) {
+
+ if (scanlines < dstHeight) {
// fCodec has already handled filling uninitialized memory.
- *rowsDecoded = dstHeight;
- return kIncompleteInput;
+ return SkCodec::kIncompleteInput;
}
- return kSuccess;
+ return SkCodec::kSuccess;
}
default:
SkASSERT(false);
- return kUnimplemented;
+ return SkCodec::kUnimplemented;
}
}
-
-uint32_t SkScaledCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
- return fCodec->onGetFillValue(colorType, alphaType);
-}
-
-SkCodec::SkScanlineOrder SkScaledCodec::onGetScanlineOrder() const {
- return fCodec->onGetScanlineOrder();
-}
« no previous file with comments | « src/codec/SkSampledCodec.h ('k') | src/codec/SkSwizzler.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698