Reland "SkAndroidCodec: Support decoding all frames"
This is a reland of fc4fdc5b25f448dd9c2cd4e445561a840ce8514b
Original change's description:
> SkAndroidCodec: Support decoding all frames
>
> Bug: b/160984428
> Bug: b/163595585
>
> Add support to SkAndroidCodec for decoding all frames with an
> fSampleSize, so that an entire animation can be decoded to a smaller
> size.
>
> dm/:
> - Test scaled + animated decodes
>
> SkAndroidCodec:
> - Make AndroidOptions inherit from SkCodec::Options. This allows
> SkAndroidCodec to use fFrameIndex. (It also combines the two versions
> of fSubset, which is now const for both.)
> - Respect fFrameIndex, and call SkCodec::handleFrameIndex to decode
> the required frame.
> - Disallow decoding with kRespect + fFrameIndex > 0 if there is a
> non-default orientation. As currently written (except without
> disabling this combination), SkPixmapPriv::Orient would draw the new
> portion of the frame on top of uninitialized pixels, instead of the
> prior frame. This could be fixed by
> - If SkAndroidCodec needs to decode the required frame, it could do so
> without applying the orientation, then decode fFrameIndex, and then
> apply the orientation.
> - If the client provided the required frame, SkAndroidCodec would need
> to un-apply the orientation to get the proper starting state, then
> decode and apply.
> I think it is simpler to force the client to handle the orientation
> externally.
>
> SkCodec:
> - Allow SkAndroidCodec to call its private method handleFrameIndex. This
> method handles decoding a required frame, if necessary. When called by
> SkAndroidCodec, it now uses the SkAndroidCodec to check for/decode the
> required frame, so that it will scale properly.
> - Call rewindIfNeeded inside handleFrameIndex. handleFrameIndex calls a
> virtual method which may set some state (e.g. in SkJpegCodec). Without
> this change, that state would be reset by rewindIfNeeded.
> - Simplify handling a kRestoreBGColor frame. Whether provided or not,
> take the same path to calling zero_rect.
> - Updates to zero_rect:
> - Intersect after scaling, which will also check for empty.
> - Round out instead of in - this ensures we don't under-erase
> - Use kFill_ScaleToFit, which better matches the intent.
>
> Change-Id: Ibe1951980a0dca8f5b7b1f20192432d395681683
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/333225
> Commit-Queue: Leon Scroggins <scroggo@google.com>
> Reviewed-by: Derek Sollenberger <djsollen@google.com>
Bug: b/160984428
Bug: b/163595585
Change-Id: I7c1e79e0f92c75b4840eef65c8fc2b8497189e81
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/334842
Auto-Submit: Leon Scroggins <scroggo@google.com>
Commit-Queue: Derek Sollenberger <djsollen@google.com>
Reviewed-by: Derek Sollenberger <djsollen@google.com>
diff --git a/src/codec/SkSampledCodec.cpp b/src/codec/SkSampledCodec.cpp
index b7ac0eb..4371e69 100644
--- a/src/codec/SkSampledCodec.cpp
+++ b/src/codec/SkSampledCodec.cpp
@@ -73,14 +73,10 @@
SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
size_t rowBytes, const AndroidOptions& options) {
- // Create an Options struct for the codec.
- SkCodec::Options codecOptions;
- codecOptions.fZeroInitialized = options.fZeroInitialized;
-
- SkIRect* subset = options.fSubset;
+ const SkIRect* subset = options.fSubset;
if (!subset || subset->size() == this->codec()->dimensions()) {
if (this->codec()->dimensionsSupported(info.dimensions())) {
- return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions);
+ return this->codec()->getPixels(info, pixels, rowBytes, &options);
}
// If the native codec does not support the requested scale, scale by sampling.
@@ -103,15 +99,17 @@
const SkImageInfo scaledInfo = info.makeDimensions(scaledSize);
+ // Copy so we can use a different fSubset.
+ AndroidOptions subsetOptions = options;
{
// Although startScanlineDecode expects the bottom and top to match the
// SkImageInfo, startIncrementalDecode uses them to determine which rows to
// decode.
SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY,
scaledSubsetWidth, scaledSubsetHeight);
- codecOptions.fSubset = &incrementalSubset;
+ subsetOptions.fSubset = &incrementalSubset;
const SkCodec::Result startResult = this->codec()->startIncrementalDecode(
- scaledInfo, pixels, rowBytes, &codecOptions);
+ scaledInfo, pixels, rowBytes, &subsetOptions);
if (SkCodec::kSuccess == startResult) {
int rowsDecoded = 0;
const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
@@ -128,17 +126,17 @@
return startResult;
}
// Otherwise fall down to use the old scanline decoder.
- // codecOptions.fSubset will be reset below, so it will not continue to
+ // subsetOptions.fSubset will be reset below, so it will not continue to
// point to the object that is no longer on the stack.
}
// Start the scanline decode.
SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
scaledSize.height());
- codecOptions.fSubset = &scanlineSubset;
+ subsetOptions.fSubset = &scanlineSubset;
SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo,
- &codecOptions);
+ &subsetOptions);
if (SkCodec::kSuccess != result) {
return result;
}
@@ -167,10 +165,6 @@
// We should only call this function when sampling.
SkASSERT(options.fSampleSize > 1);
- // Create options struct for the codec.
- SkCodec::Options sampledOptions;
- sampledOptions.fZeroInitialized = options.fZeroInitialized;
-
// FIXME: This was already called by onGetAndroidPixels. Can we reduce that?
int sampleSize = options.fSampleSize;
int nativeSampleSize;
@@ -198,7 +192,6 @@
// The scanline decoder only needs to be aware of subsetting in the x-dimension.
subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height());
- sampledOptions.fSubset = ⊂
}
// Since we guarantee that output dimensions are always at least one (even if the sampleSize
@@ -217,13 +210,13 @@
// Although startScanlineDecode expects the bottom and top to match the
// SkImageInfo, startIncrementalDecode uses them to determine which rows to
// decode.
- SkCodec::Options incrementalOptions = sampledOptions;
+ AndroidOptions incrementalOptions = options;
SkIRect incrementalSubset;
- if (sampledOptions.fSubset) {
- incrementalSubset.fTop = subsetY;
- incrementalSubset.fBottom = subsetY + subsetHeight;
- incrementalSubset.fLeft = sampledOptions.fSubset->fLeft;
- incrementalSubset.fRight = sampledOptions.fSubset->fRight;
+ if (options.fSubset) {
+ incrementalSubset.fTop = subsetY;
+ incrementalSubset.fBottom = subsetY + subsetHeight;
+ incrementalSubset.fLeft = subset.fLeft;
+ incrementalSubset.fRight = subset.fRight;
incrementalOptions.fSubset = &incrementalSubset;
}
const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo,
@@ -263,6 +256,10 @@
}
// Start the scanline decode.
+ AndroidOptions sampledOptions = options;
+ if (options.fSubset) {
+ sampledOptions.fSubset = ⊂
+ }
SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo,
&sampledOptions);
if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) {