Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c2ebc2bc authored by Leon Scroggins III's avatar Leon Scroggins III
Browse files

Implement AImageDecoder_setInternallyHandleDisposePrevious

Bug: 160984428
Test: I00682f201a52f894b0e1335c00c4368ce675a805

Also fix a bug caught by the new test. If the current frame is the first
in a series of one or more RestorePrevious frames, fPriorFrame should be
set to |currentFrame - 1|. Otherwise SkCodec will decode the required
frame. This is wasted work, since the prior frame should already be
prepared (either by AImageDecoder or by the client).

Change-Id: I1fb9f91dc66fd3121f187b9a91c15f625eb17f8d
parent 946f8d41
Loading
Loading
Loading
Loading
+36 −15
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChu
    , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
    , mUnpremultipliedRequired(false)
    , mOutColorSpace(getDefaultColorSpace())
    , mHandleRestorePrevious(true)
{
    mTargetSize = swapWidthHeight() ? SkISize { mDecodeSize.height(), mDecodeSize.width() }
                                    : mDecodeSize;
@@ -230,6 +231,13 @@ bool ImageDecoder::rewind() {
    return true;
}

void ImageDecoder::setHandleRestorePrevious(bool handle) {
    mHandleRestorePrevious = handle;
    if (!handle) {
        mRestoreFrame = nullptr;
    }
}

bool ImageDecoder::advanceFrame() {
    const int frameIndex = ++mOptions.fFrameIndex;
    const int frameCount = mCodec->codec()->getFrameCount();
@@ -255,6 +263,7 @@ bool ImageDecoder::advanceFrame() {
            case RestoreState::kDoNothing:
            case RestoreState::kNeedsRestore:
                mRestoreState = RestoreState::kFirstRPFrame;
                mOptions.fPriorFrame = frameIndex - 1;
                break;
            case RestoreState::kFirstRPFrame:
                mRestoreState = RestoreState::kRPFrame;
@@ -315,29 +324,19 @@ bool ImageDecoder::finished() const {
    return mOptions.fFrameIndex >= mCodec->codec()->getFrameCount();
}

SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
    // This was checked inside setTargetSize, but it's possible the first frame
    // was opaque, so that method succeeded, but after calling advanceFrame, the
    // current frame is not opaque.
    if (mUnpremultipliedRequired && !opaque()) {
        // Allow using a matrix to handle orientation, but not scaling.
        if (requires_matrix_scaling(swapWidthHeight(), mDecodeSize, mTargetSize)) {
            return SkCodec::kInvalidScale;
        }
bool ImageDecoder::handleRestorePrevious(const SkImageInfo& outputInfo, void* pixels,
                                         size_t rowBytes) {
    if (!mHandleRestorePrevious) {
        return true;
    }

    void* decodePixels = pixels;
    size_t decodeRowBytes = rowBytes;
    const auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(),
                                              getOutputColorSpace());
    const auto outputInfo = getOutputInfo();
    switch (mRestoreState) {
        case RestoreState::kFirstRPFrame:{
            // This frame is marked kRestorePrevious. The prior frame should be in
            // |pixels|, and it is what we'll restore after each consecutive
            // kRestorePrevious frame. Cache it now.
            if (!(mRestoreFrame = Bitmap::allocateHeapBitmap(outputInfo))) {
                return SkCodec::kInternalError;
                return false;
            }

            const uint8_t* srcRow = static_cast<uint8_t*>(pixels);
@@ -366,7 +365,29 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
        case RestoreState::kDoNothing:
            break;
    }
    return true;
}

SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
    // This was checked inside setTargetSize, but it's possible the first frame
    // was opaque, so that method succeeded, but after calling advanceFrame, the
    // current frame is not opaque.
    if (mUnpremultipliedRequired && !opaque()) {
        // Allow using a matrix to handle orientation, but not scaling.
        if (requires_matrix_scaling(swapWidthHeight(), mDecodeSize, mTargetSize)) {
            return SkCodec::kInvalidScale;
        }
    }

    const auto outputInfo = getOutputInfo();
    if (!handleRestorePrevious(outputInfo, pixels, rowBytes)) {
        return SkCodec::kInternalError;
    }

    void* decodePixels = pixels;
    size_t decodeRowBytes = rowBytes;
    const auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(),
                                              getOutputColorSpace());
    // Used if we need a temporary before scaling or subsetting.
    // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
    SkBitmap tmp;
+6 −0
Original line number Diff line number Diff line
@@ -73,6 +73,9 @@ public:

    SkCodec::FrameInfo getCurrentFrameInfo();

    // Set whether the ImageDecoder should handle RestorePrevious frames.
    void setHandleRestorePrevious(bool handle);

private:
    // State machine for keeping track of how to handle RestorePrevious (RP)
    // frames in decode().
@@ -105,6 +108,7 @@ private:
    SkAndroidCodec::AndroidOptions mOptions;
    bool mCurrentFrameIsIndependent;
    bool mCurrentFrameIsOpaque;
    bool mHandleRestorePrevious;
    RestoreState mRestoreState;
    sk_sp<Bitmap> mRestoreFrame;
    std::optional<SkIRect> mCropRect;
@@ -115,6 +119,8 @@ private:
    SkAlphaType getOutAlphaType() const;
    sk_sp<SkColorSpace> getOutputColorSpace() const;
    bool swapWidthHeight() const;
    // Store/restore a frame if necessary. Returns false on error.
    bool handleRestorePrevious(const SkImageInfo&, void* pixels, size_t rowBytes);
};

} // namespace android
+6 −0
Original line number Diff line number Diff line
@@ -535,3 +535,9 @@ int32_t AImageDecoderFrameInfo_getBlendOp(const AImageDecoderFrameInfo* info) {
            return ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER;
    }
}

void AImageDecoder_setInternallyHandleDisposePrevious(AImageDecoder* decoder, bool handle) {
    if (decoder) {
        toDecoder(decoder)->setHandleRestorePrevious(handle);
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ LIBJNIGRAPHICS {
    AImageDecoder_advanceFrame; # introduced=31
    AImageDecoder_rewind; # introduced=31
    AImageDecoder_getFrameInfo; # introduced = 31
    AImageDecoder_setInternallyHandleDisposePrevious; # introduced = 31
    AImageDecoderHeaderInfo_getWidth; # introduced=30
    AImageDecoderHeaderInfo_getHeight; # introduced=30
    AImageDecoderHeaderInfo_getMimeType; # introduced=30