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

Unified Diff: src/codec/SkScaledCodec.cpp

Issue 1321433002: Add subsetting to SkScaledCodec (Closed) Base URL: https://skia.googlesource.com/skia.git@gif-scan
Patch Set: 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
Index: src/codec/SkScaledCodec.cpp
diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp
index 27fbd59a89c18e6729bcc91327d4de85e9bf10c0..7fad945847cdcc583ee9f5f16c2e0ddde2ad6080 100644
--- a/src/codec/SkScaledCodec.cpp
+++ b/src/codec/SkScaledCodec.cpp
@@ -18,7 +18,7 @@ SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) {
}
if (isWebp) {
// Webp codec supports scaling and subsetting natively
- return SkWebpCodec::NewFromStream(stream);
+ return SkWebpCodec::NewFromStream(stream);
}
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream));
@@ -44,16 +44,28 @@ SkScaledCodec::SkScaledCodec(SkCodec* codec)
SkScaledCodec::~SkScaledCodec() {}
-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;
+static bool is_in_subset(int coord, int offset, int length) {
scroggo 2015/10/02 18:27:03 I thought we had something like this function, but
+ if (coord < offset || coord >= offset + length) {
scroggo 2015/10/02 18:27:03 Why not: return coord >= offset && coord < offset
+ return false;
}
+ return true;
+}
+
+static int get_sample_size(float scale) {
scroggo 2015/10/02 18:27:03 Don't you define this somewhere else?
+ return SkScalarRoundToInt(1.0f / scale);
+}
+
+static bool use_native_scaling(const SkISize& origDims, const SkISize& nativeDims,
+ const SkISize& sampledDims, float desiredScale) {
+ if (nativeDims == sampledDims) {
+ // If both options are the same, choose native scaling.
+ return true;
+ }
+
float idealWidth = origDims.width() * desiredScale;
float idealHeight = origDims.height() * desiredScale;
- // calculate difference between native dimensions and ideal dimensions
+ // Calculate difference between native scaling and ideal scaling.
float nativeWDiff = SkTAbs(idealWidth - nativeDims.width());
float nativeHDiff = SkTAbs(idealHeight - nativeDims.height());
float nativeDiff = nativeWDiff + nativeHDiff;
@@ -61,52 +73,77 @@ static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& na
// 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;
+ return true;
}
- // calculate difference between scaledCodec dimensions and ideal dimensions
- float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width());
- float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height());
- float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff;
+ // Calculate difference between native scaling and sampled scaling.
+ float sampledWDiff = SkTAbs(idealWidth - sampledDims.width());
+ float sampledHDiff = SkTAbs(idealHeight - sampledDims.height());
+ float sampledDiff = sampledWDiff + sampledHDiff;
- // return dimensions closest to ideal dimensions.
- // If the differences are equal, return nativeDims, as native scaling is more efficient.
- return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims;
+ // Use native scaling if it is closer to the ideal scale.
+ return nativeDiff <= sampledDiff;
}
/*
* 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();
+ SkISize nativeDims = fCodec->getScaledDimensions(desiredScale);
+ int sampleSize = get_sample_size(desiredScale);
+ SkISize sampledDims = SkISize::Make(
+ get_scaled_dimension(this->getInfo().width(), sampleSize),
+ get_scaled_dimension(this->getInfo().height(), sampleSize));
+
+ if (use_native_scaling(this->getInfo().dimensions(), nativeDims, sampledDims, desiredScale)) {
+ return nativeDims;
}
- // 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 sampledDims;
+}
- // Return the calculated output dimensions for the given scale
- scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight);
+bool SkScaledCodec::onGetScaledSubsetDimensions(float desiredScale, Options* options) const {
+
+ SkISize nativeDims = fCodec->getScaledDimensions(desiredScale);
+ int sampleSize = get_sample_size(desiredScale);
+ SkISize sampledDims = SkISize::Make(
+ get_scaled_dimension(this->getInfo().width(), sampleSize),
+ get_scaled_dimension(this->getInfo().height(), sampleSize));
+
+ if (use_native_scaling(this->getInfo().dimensions(), nativeDims, sampledDims, desiredScale)) {
+ // Set the scaled dimensions and calculate subset size using native scaling.
+ options->fScaledDimensions = nativeDims;
+ float widthScale = ((float) nativeDims.width()) / ((float) this->getInfo().width());
+ float heightScale = ((float) nativeDims.height()) / ((float) this->getInfo().height());
+ // Notice that we may round the size of the subset up to 1. This means that we must
+ // floor the left and top offsets to ensure that we do not suggest a subset that is
+ // off the edge of the image.
+ options->fScaledSubset = SkIRect::MakeXYWH(
+ int (((float) options->fSubset->left()) * widthScale),
+ int (((float) options->fSubset->top()) * heightScale),
+ SkTMax(1, SkScalarRoundToInt(((float) options->fSubset->width()) * widthScale)),
+ SkTMax(1, SkScalarRoundToInt(((float) options->fSubset->height()) * heightScale)));
+ return true;
+ }
- return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions,
- scaledCodecDimensions, desiredScale);
+ // Set the scaled dimensions and calculate subset size using sampling.
+ options->fScaledDimensions = sampledDims;
+ options->fScaledSubset = SkIRect::MakeXYWH(
+ options->fSubset->left() / sampleSize,
+ options->fSubset->top() / sampleSize,
+ get_scaled_dimension(options->fSubset->width(), sampleSize),
+ get_scaled_dimension(options->fSubset->height(), sampleSize));
+ return true;
}
// check if scaling to dstInfo size from srcInfo size using sampleSize is possible
-static bool scaling_supported(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo,
+static bool scaling_supported(const SkISize& dstSize, const SkISize& srcSize,
int* sampleX, int* sampleY) {
- SkScaledCodec::ComputeSampleSize(dstInfo, srcInfo, sampleX, sampleY);
- const int dstWidth = dstInfo.width();
- const int dstHeight = dstInfo.height();
- const int srcWidth = srcInfo.width();
- const int srcHeight = srcInfo.height();
+ SkScaledCodec::ComputeSampleSize(dstSize, srcSize, sampleX, sampleY);
+ const int dstWidth = dstSize.width();
+ const int dstHeight = dstSize.height();
+ const int srcWidth = srcSize.width();
+ const int srcHeight = srcSize.height();
// only support down sampling, not up sampling
if (dstWidth > srcWidth || dstHeight > srcHeight) {
return false;
@@ -131,12 +168,12 @@ static bool scaling_supported(const SkImageInfo& dstInfo, const SkImageInfo& src
}
// calculates sampleSize in x and y direction
-void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo,
+void SkScaledCodec::ComputeSampleSize(const SkISize& dstSize, const SkISize& srcSize,
int* sampleXPtr, int* sampleYPtr) {
- int srcWidth = srcInfo.width();
- int dstWidth = dstInfo.width();
- int srcHeight = srcInfo.height();
- int dstHeight = dstInfo.height();
+ int srcWidth = srcSize.width();
+ int dstWidth = dstSize.width();
+ int srcHeight = srcSize.height();
+ int dstHeight = dstSize.height();
int sampleX = srcWidth / dstWidth;
int sampleY = srcHeight / dstHeight;
@@ -163,6 +200,7 @@ void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageI
} else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) {
sampleY = sampleX;
}
+ // FIXME (msarett): Should this never be reached?
}
}
@@ -174,83 +212,173 @@ void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageI
}
}
-// 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) {
-
- if (options.fSubset) {
- // Subsets are not supported.
- return kUnimplemented;
- }
+SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& scaledSubsetInfo, void* dst,
+ size_t rowBytes, const Options& options, SkPMColor ctable[], int* ctableCount,
+ int* rowsDecoded) {
+
+ // There are various values of Rect, Size, and Info used in this function. I think it is
+ // useful to go ahead and define what they mean.
+ // orig_: Refers to the original image size.
+ // scaledSubset_: Refers to the size of the final output. This can match the original
+ // dimensions, be a subset of the original dimensions, be a scaled version
+ // of the original dimensions, or be a scaled subset of the original dimensions.
+ // subset_: Refers to the size of the unscaled subset in terms of the original image
+ // dimensions. If this is not a subset decode, this will match the original
+ // image dimensions.
+ // scaled_: Refers to the scaled size of the original image, ignoring any subsetting.
+ // If we are not scaling, this will match the original dimensions.
+ SkISize origSize = this->getInfo().dimensions();
+ SkIRect subsetRect;
+ SkISize scaledSize;
+ SkIRect scaledSubsetRect;
+ if (nullptr == options.fSubset) {
+ // This is not a subset decode.
+ SkASSERT(options.fScaledDimensions.isZero());
+ SkASSERT(options.fScaledSubset.isEmpty());
+
+ // Set the "subset" to the full image dimensions.
+ subsetRect = SkIRect::MakeSize(origSize);
+
+ // This may be scaled or unscaled, depending on if scaledSize matches origSize.
+ scaledSize = scaledSubsetInfo.dimensions();
+ scaledSubsetRect = SkIRect::MakeSize(scaledSize);
+ } else {
+ // This is a subset decode.
+ if (!is_valid_subset(options.fSubset, origSize)) {
+ return kInvalidParameters;
+ }
- // FIXME: If no scaling/subsets are requested, we can call fCodec->getPixels.
- Result result = fCodec->startScanlineDecode(requestedInfo, &options, ctable, ctableCount);
- if (kSuccess == result) {
- // native decode supported
- switch (fCodec->getScanlineOrder()) {
- case SkCodec::kTopDown_SkScanlineOrder:
- case SkCodec::kBottomUp_SkScanlineOrder:
- case SkCodec::kNone_SkScanlineOrder:
- if (fCodec->getScanlines(dst, requestedInfo.height(), rowBytes) !=
- requestedInfo.height()) {
- // fCodec has already handled filling uninitialized memory.
- *rowsDecoded = requestedInfo.height();
- return kIncompleteInput;
- }
- return kSuccess;
- case SkCodec::kOutOfOrder_SkScanlineOrder: {
- for (int y = 0; y < requestedInfo.height(); y++) {
- int dstY = fCodec->nextScanline();
- void* dstPtr = SkTAddOffset<void>(dst, rowBytes * dstY);
- if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) {
- *rowsDecoded = y + 1;
- return kIncompleteInput;
- }
- }
- return kSuccess;
+ subsetRect = *(options.fSubset);
+ if (options.fScaledDimensions.isZero()) {
+ // This is an unscaled subset decode.
+ SkASSERT(options.fScaledSubset.isEmpty());
+ SkASSERT(scaledSubsetInfo.dimensions() == subsetRect.size());
+
+ scaledSize = origSize;
+ scaledSubsetRect = subsetRect;
+ } else {
+ // This is a scaled subset decode.
+ SkASSERT(!options.fScaledSubset.isEmpty());
+ SkASSERT(scaledSubsetInfo.dimensions() == options.fScaledSubset.size());
+ if (!is_valid_subset(&options.fScaledSubset, options.fScaledDimensions)) {
+ return kInvalidParameters;
}
+
+ scaledSize = options.fScaledDimensions;
+ scaledSubsetRect = options.fScaledSubset;
}
}
- if (kInvalidScale != result) {
- // no scaling requested
- return result;
+ // Reset the options for use by fCodec. We will handle scaling and subsetting
+ // from this level, the native codec does not need to know about it.
+ Options newOptions = options;
+ newOptions.fSubset = nullptr;
+ newOptions.fScaledDimensions = SkISize::Make(0, 0);
+ newOptions.fScaledSubset = SkIRect::MakeEmpty();
+
+ // The native decoder needs the scaled size of the entire image to check if it can decode to
+ // the requested scale.
+ SkImageInfo scaledInfo = scaledSubsetInfo.makeWH(scaledSize.width(), scaledSize.height());
+ Result result = fCodec->startScanlineDecode(scaledInfo, &newOptions, ctable, ctableCount,
+ scaledSubsetRect.left(), scaledSubsetRect.width());
+ switch (result) {
+ case kSuccess:
+ return this->nativeDecode(scaledInfo, dst, rowBytes, scaledSubsetRect,
+ options.fZeroInitialized, rowsDecoded);
+ case kInvalidScale:
+ // We will attempt to scale by sampling below.
+ break;
+ default:
+ return result;
}
- // scaling requested
+ // Try to provide the scale using sampling.
int sampleX;
int sampleY;
- if (!scaling_supported(requestedInfo, fCodec->getInfo(), &sampleX, &sampleY)) {
+ if (!scaling_supported(scaledSubsetInfo.dimensions(), subsetRect.size(), &sampleX, &sampleY)) {
return kInvalidScale;
}
- // set first sample pixel in y direction
- int Y0 = get_start_coord(sampleY);
-
- int dstHeight = requestedInfo.height();
- int srcHeight = fCodec->getInfo().height();
-
- SkImageInfo info = requestedInfo;
- // use original height as codec does not support y sampling natively
- info = info.makeWH(requestedInfo.width(), srcHeight);
-
- // update codec with new info
- // FIXME: The previous call to start returned kInvalidScale. This call may
- // require a rewind. (skbug.com/4284)
- result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount);
+
+ // Create the image info that will be passed to fCodec. We support scaling in the
+ // x-dimension, but we will perform scaling in the y-dimension here, so we pass the scaled
+ // width and the original height.
+ // FIXME: This is a bit confusing. I think there is a plan to move scaling completely out
+ // of the native codec.
+ SkImageInfo decodeInfo = scaledSubsetInfo.makeWH(scaledSize.width(), this->getInfo().height());
+
+ // When starting the fCodec, we pass the left offset based on the original image
+ // dimensions, but need the scaled version of the subset width.
+ // FIXME: This is a bit confusing. I think there is a plan to move scaling completely out
+ // of the native codec.
+ result = fCodec->startScanlineDecode(decodeInfo, &newOptions, ctable, ctableCount,
+ subsetRect.left(), scaledSubsetInfo.width());
if (kSuccess != result) {
+ SkASSERT(kInvalidScale != result);
return result;
}
+ return this->sampledDecode(scaledSubsetInfo, dst, rowBytes, subsetRect, scaledSubsetRect,
+ sampleX, sampleY, options.fZeroInitialized, rowsDecoded);
+}
+
+SkCodec::Result SkScaledCodec::nativeDecode(const SkImageInfo& scaledInfo, void* dst,
+ size_t rowBytes, const SkIRect& scaledSubsetRect, ZeroInitialized zeroInit,
+ int* rowsDecoded) {
+
+ int scaledSubsetTop = scaledSubsetRect.top();
+ int scaledSubsetHeight = scaledSubsetRect.height();
+ switch (fCodec->getScanlineOrder()) {
+ case SkCodec::kTopDown_SkScanlineOrder:
+ case SkCodec::kBottomUp_SkScanlineOrder:
+ case SkCodec::kNone_SkScanlineOrder: {
+ if (!fCodec->skipScanlines(scaledSubsetTop)) {
+ *rowsDecoded = 0;
+ return kIncompleteInput;
+ }
+
+ uint32_t decodedLines = fCodec->getScanlines(dst, scaledSubsetHeight,
+ rowBytes);
+ if (decodedLines != scaledSubsetHeight) {
+ *rowsDecoded = decodedLines;
+ return kIncompleteInput;
+ }
+ return kSuccess;
+ }
+ case SkCodec::kOutOfOrder_SkScanlineOrder: {
+ for (int y = 0; y < scaledInfo.height(); y++) {
+ int dstY = fCodec->nextScanline();
+ if (is_in_subset(dstY, scaledSubsetTop, scaledSubsetHeight)) {
+ void* dstPtr = SkTAddOffset<void>(dst, rowBytes * (dstY - scaledSubsetTop));
+ if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) {
+ *rowsDecoded = y + 1;
+ return kIncompleteInput;
+ }
+ } else {
+ if (!fCodec->skipScanlines(1)) {
+ *rowsDecoded = y + 1;
+ return kIncompleteInput;
+ }
+ }
+ }
+ return kSuccess;
+ }
+ }
+}
+
+SkCodec::Result SkScaledCodec::sampledDecode(const SkImageInfo& scaledSubsetInfo, void* dst,
+ size_t rowBytes, const SkIRect& subsetRect, const SkIRect& scaledSubsetRect, int sampleX,
+ int sampleY, ZeroInitialized zeroInit, int* rowsDecoded) {
+
+ // Set first sample pixel in y direction.
+ int y0 = get_start_coord(sampleY);
+ int scaledSubsetHeight = scaledSubsetRect.height();
switch(fCodec->getScanlineOrder()) {
- case SkCodec::kTopDown_SkScanlineOrder: {
- if (!fCodec->skipScanlines(Y0)) {
+ case SkCodec::kTopDown_SkScanlineOrder:
+ if (!fCodec->skipScanlines(y0 + subsetRect.top())) {
*rowsDecoded = 0;
return kIncompleteInput;
}
- for (int y = 0; y < dstHeight; y++) {
+ for (int y = 0; y < scaledSubsetHeight; 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
@@ -258,7 +386,7 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
*rowsDecoded = y + 1;
return kIncompleteInput;
}
- if (y < dstHeight - 1) {
+ if (y < scaledSubsetHeight - 1) {
if (!fCodec->skipScanlines(sampleY - 1)) {
*rowsDecoded = y + 1;
return kIncompleteInput;
@@ -267,14 +395,13 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
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++) {
+ for (y = 0; y < this->getInfo().height(); y++) {
int srcY = fCodec->nextScanline();
- if (is_coord_necessary(srcY, sampleY, dstHeight)) {
+ if (is_coord_necessary(srcY, sampleY, scaledSubsetHeight, subsetRect.top())) {
void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY));
if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) {
result = kIncompleteInput;
@@ -291,39 +418,42 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
// 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 SkImageInfo fillInfo = requestedInfo.makeWH(requestedInfo.width(), 1);
+ const SkImageInfo fillInfo = scaledSubsetInfo.makeWH(scaledSubsetRect.width(), 1);
const uint32_t fillValue = fCodec->getFillValue(fillInfo.colorType(),
fillInfo.alphaType());
- for (; y < srcHeight; y++) {
+ for (; y < this->getInfo().height(); y++) {
int srcY = fCodec->outputScanline(y);
- if (is_coord_necessary(srcY, sampleY, dstHeight)) {
+ if (is_coord_necessary(srcY, sampleY, scaledSubsetHeight, subsetRect.top())) {
void* dstRow = SkTAddOffset<void>(dst,
rowBytes * get_dst_coord(srcY, sampleY));
- SkSwizzler::Fill(dstRow, fillInfo, rowBytes, fillValue,
- options.fZeroInitialized);
+ SkSwizzler::Fill(dstRow, fillInfo, rowBytes, fillValue, zeroInit);
}
}
- *rowsDecoded = dstHeight;
+ *rowsDecoded = scaledSubsetInfo.height();
}
return result;
}
case SkCodec::kNone_SkScanlineOrder: {
- SkAutoMalloc storage(srcHeight * rowBytes);
+ SkAutoMalloc storage(subsetRect.height() * rowBytes);
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
- int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes);
- storagePtr += Y0 * rowBytes;
- scanlines -= Y0;
+ if (!fCodec->skipScanlines(subsetRect.top())) {
+ *rowsDecoded = 0;
+ return kIncompleteInput;
+ }
+ int scanlines = fCodec->getScanlines(storagePtr, subsetRect.height(), rowBytes);
+ scanlines -= y0;
+ storagePtr += y0 * rowBytes;
int y = 0;
- while (y < dstHeight && scanlines > 0) {
- memcpy(dst, storagePtr, rowBytes);
+ while (y < scaledSubsetHeight && scanlines > 0) {
+ memcpy(dst, storagePtr, scaledSubsetInfo.minRowBytes());
storagePtr += sampleY * rowBytes;
dst = SkTAddOffset<void>(dst, rowBytes);
scanlines -= sampleY;
y++;
}
- if (y < dstHeight) {
+ if (y < scaledSubsetHeight) {
// fCodec has already handled filling uninitialized memory.
- *rowsDecoded = dstHeight;
+ *rowsDecoded = scaledSubsetInfo.height();
return kIncompleteInput;
}
return kSuccess;

Powered by Google App Engine
This is Rietveld 408576698