557 lines
19 KiB
C++
Raw Normal View History

/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "aassetstreamadaptor.h"
#include <android/asset_manager.h>
#include <android/bitmap.h>
#include <android/data_space.h>
#include <android/imagedecoder.h>
#include <MimeType.h>
#include <android/rect.h>
#include <hwui/ImageDecoder.h>
#include <log/log.h>
#include <SkAndroidCodec.h>
#include <utils/Color.h>
#include <fcntl.h>
#include <limits>
#include <optional>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
using namespace android;
int ResultToErrorCode(SkCodec::Result result) {
switch (result) {
case SkCodec::kIncompleteInput:
return ANDROID_IMAGE_DECODER_INCOMPLETE;
case SkCodec::kErrorInInput:
return ANDROID_IMAGE_DECODER_ERROR;
case SkCodec::kInvalidInput:
return ANDROID_IMAGE_DECODER_INVALID_INPUT;
case SkCodec::kCouldNotRewind:
return ANDROID_IMAGE_DECODER_SEEK_ERROR;
case SkCodec::kUnimplemented:
return ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT;
case SkCodec::kInvalidConversion:
return ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
case SkCodec::kInvalidParameters:
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
case SkCodec::kSuccess:
return ANDROID_IMAGE_DECODER_SUCCESS;
case SkCodec::kInvalidScale:
return ANDROID_IMAGE_DECODER_INVALID_SCALE;
case SkCodec::kInternalError:
return ANDROID_IMAGE_DECODER_INTERNAL_ERROR;
}
}
const char* AImageDecoder_resultToString(int result) {
switch (result) {
case ANDROID_IMAGE_DECODER_SUCCESS:
return "ANDROID_IMAGE_DECODER_SUCCESS";
case ANDROID_IMAGE_DECODER_INCOMPLETE:
return "ANDROID_IMAGE_DECODER_INCOMPLETE";
case ANDROID_IMAGE_DECODER_ERROR:
return "ANDROID_IMAGE_DECODER_ERROR";
case ANDROID_IMAGE_DECODER_INVALID_CONVERSION:
return "ANDROID_IMAGE_DECODER_INVALID_CONVERSION";
case ANDROID_IMAGE_DECODER_INVALID_SCALE:
return "ANDROID_IMAGE_DECODER_INVALID_SCALE";
case ANDROID_IMAGE_DECODER_BAD_PARAMETER:
return "ANDROID_IMAGE_DECODER_BAD_PARAMETER";
case ANDROID_IMAGE_DECODER_INVALID_INPUT:
return "ANDROID_IMAGE_DECODER_INVALID_INPUT";
case ANDROID_IMAGE_DECODER_SEEK_ERROR:
return "ANDROID_IMAGE_DECODER_SEEK_ERROR";
case ANDROID_IMAGE_DECODER_INTERNAL_ERROR:
return "ANDROID_IMAGE_DECODER_INTERNAL_ERROR";
case ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT:
return "ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT";
case ANDROID_IMAGE_DECODER_FINISHED:
return "ANDROID_IMAGE_DECODER_FINISHED";
case ANDROID_IMAGE_DECODER_INVALID_STATE:
return "ANDROID_IMAGE_DECODER_INVALID_STATE";
default:
return nullptr;
}
}
static int createFromStream(std::unique_ptr<SkStreamRewindable> stream, AImageDecoder** outDecoder) {
SkCodec::Result result;
auto codec = SkCodec::MakeFromStream(std::move(stream), &result, nullptr,
SkCodec::SelectionPolicy::kPreferAnimation);
// These may be swapped due to the SkEncodedOrigin, but we're just checking
// them to make sure they fit in int32_t.
auto dimensions = codec->dimensions();
auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
if (!androidCodec) {
return ResultToErrorCode(result);
}
// AImageDecoderHeaderInfo_getWidth/Height return an int32_t. Ensure that
// the conversion is safe.
if (dimensions.width() > std::numeric_limits<int32_t>::max() ||
dimensions.height() > std::numeric_limits<int32_t>::max()) {
return ANDROID_IMAGE_DECODER_INVALID_INPUT;
}
*outDecoder = reinterpret_cast<AImageDecoder*>(new ImageDecoder(std::move(androidCodec)));
return ANDROID_IMAGE_DECODER_SUCCESS;
}
int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) {
if (!asset || !outDecoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
*outDecoder = nullptr;
Build libjnigraphics, imagedecoder_fuzzer on host Bug: 142252770 Bug: 169137236 Bug: 169139756 Test: SANITIZE_HOST=address make imagedecoder_fuzzer This will allow running the fuzzer much faster in order to find bugs. It also allows running with ASAN to help find memory leaks. Some symbols (e.g. ABitmap, AAssetStreamAdaptor) rely on Android-specific features, so disable them when building on the host. This still allows using the core features of AImageDecoder. This requires a change in frameworks/native (Iefdb4132d6f10f9164be85f4dc6e11883f6fd37e). Disable detecting ODR violations when building with ASAN (which is how host builds are built). Without this change, the host detects the following: ================================================================= ==4072183==ERROR: AddressSanitizer: odr-violation (0x7fb7d52b4100): [1] size=80 'vtable for android::VectorImpl' system/core/libutils/VectorImpl.cpp [2] size=80 'vtable for android::VectorImpl' system/core/libutils/VectorImpl.cpp These globals were registered at these points: [1]: #0 0x55fefda47e0d in __asan_register_globals /out/llvm-project/compiler-rt/lib/asan/asan_globals.cpp:360:3 #1 0x7fb7d43aa77b in asan.module_ctor (/usr/local/ssd/android/master/out/host/linux-x86/fuzz/x86_64/imagedecoder_fuzzer/../lib/libhw ui.so+0x17c677b) [2]: #0 0x55fefda47e0d in __asan_register_globals /out/llvm-project/compiler-rt/lib/asan/asan_globals.cpp:360:3 #1 0x7fb7d52a0e6b in asan.module_ctor (/usr/local/ssd/android/master/out/host/linux-x86/fuzz/x86_64/imagedecoder_fuzzer/../lib/libutils.so+0x3de6b) ==4072183==HINT: if you don't care about these errors you may set ASAN_OPTIONS=detect_odr_violation=0 SUMMARY: AddressSanitizer: odr-violation: global 'vtable for android::VectorImpl' at system/core/libutils/VectorImpl.cpp ==4072183==ABORTING Change-Id: I0ffc4e90b4083db79fbb12012e2e1284206b43e3
2020-12-30 16:38:23 -05:00
#ifdef __ANDROID__
auto stream = std::make_unique<AAssetStreamAdaptor>(asset);
return createFromStream(std::move(stream), outDecoder);
Build libjnigraphics, imagedecoder_fuzzer on host Bug: 142252770 Bug: 169137236 Bug: 169139756 Test: SANITIZE_HOST=address make imagedecoder_fuzzer This will allow running the fuzzer much faster in order to find bugs. It also allows running with ASAN to help find memory leaks. Some symbols (e.g. ABitmap, AAssetStreamAdaptor) rely on Android-specific features, so disable them when building on the host. This still allows using the core features of AImageDecoder. This requires a change in frameworks/native (Iefdb4132d6f10f9164be85f4dc6e11883f6fd37e). Disable detecting ODR violations when building with ASAN (which is how host builds are built). Without this change, the host detects the following: ================================================================= ==4072183==ERROR: AddressSanitizer: odr-violation (0x7fb7d52b4100): [1] size=80 'vtable for android::VectorImpl' system/core/libutils/VectorImpl.cpp [2] size=80 'vtable for android::VectorImpl' system/core/libutils/VectorImpl.cpp These globals were registered at these points: [1]: #0 0x55fefda47e0d in __asan_register_globals /out/llvm-project/compiler-rt/lib/asan/asan_globals.cpp:360:3 #1 0x7fb7d43aa77b in asan.module_ctor (/usr/local/ssd/android/master/out/host/linux-x86/fuzz/x86_64/imagedecoder_fuzzer/../lib/libhw ui.so+0x17c677b) [2]: #0 0x55fefda47e0d in __asan_register_globals /out/llvm-project/compiler-rt/lib/asan/asan_globals.cpp:360:3 #1 0x7fb7d52a0e6b in asan.module_ctor (/usr/local/ssd/android/master/out/host/linux-x86/fuzz/x86_64/imagedecoder_fuzzer/../lib/libutils.so+0x3de6b) ==4072183==HINT: if you don't care about these errors you may set ASAN_OPTIONS=detect_odr_violation=0 SUMMARY: AddressSanitizer: odr-violation: global 'vtable for android::VectorImpl' at system/core/libutils/VectorImpl.cpp ==4072183==ABORTING Change-Id: I0ffc4e90b4083db79fbb12012e2e1284206b43e3
2020-12-30 16:38:23 -05:00
#else
return ANDROID_IMAGE_DECODER_INTERNAL_ERROR;
#endif
}
static bool isSeekable(int descriptor) {
return ::lseek64(descriptor, 0, SEEK_CUR) != -1;
}
int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) {
if (fd <= 0 || !outDecoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
struct stat fdStat;
if (fstat(fd, &fdStat) == -1) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
if (!isSeekable(fd)) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
// SkFILEStream will close its descriptor. Duplicate it so the client will
// still be responsible for closing the original.
int dupDescriptor = fcntl(fd, F_DUPFD_CLOEXEC, 0);
FILE* file = fdopen(dupDescriptor, "r");
if (!file) {
close(dupDescriptor);
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
auto stream = std::unique_ptr<SkStreamRewindable>(new SkFILEStream(file));
return createFromStream(std::move(stream), outDecoder);
}
int AImageDecoder_createFromBuffer(const void* buffer, size_t length,
AImageDecoder** outDecoder) {
if (!buffer || !length || !outDecoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
*outDecoder = nullptr;
// The client is expected to keep the buffer alive as long as the
// AImageDecoder, so we do not need to copy the buffer.
auto stream = std::unique_ptr<SkStreamRewindable>(
new SkMemoryStream(buffer, length, false /* copyData */));
return createFromStream(std::move(stream), outDecoder);
}
static ImageDecoder* toDecoder(AImageDecoder* d) {
return reinterpret_cast<ImageDecoder*>(d);
}
static const ImageDecoder* toDecoder(const AImageDecoder* d) {
return reinterpret_cast<const ImageDecoder*>(d);
}
// Note: This differs from the version in android_bitmap.cpp in that this
// version returns kGray_8_SkColorType for ANDROID_BITMAP_FORMAT_A_8. SkCodec
// allows decoding single channel images to gray, which Android then treats
// as A_8/ALPHA_8.
static SkColorType getColorType(AndroidBitmapFormat format) {
switch (format) {
case ANDROID_BITMAP_FORMAT_RGBA_8888:
return kN32_SkColorType;
case ANDROID_BITMAP_FORMAT_RGB_565:
return kRGB_565_SkColorType;
case ANDROID_BITMAP_FORMAT_RGBA_4444:
return kARGB_4444_SkColorType;
case ANDROID_BITMAP_FORMAT_A_8:
return kGray_8_SkColorType;
case ANDROID_BITMAP_FORMAT_RGBA_F16:
return kRGBA_F16_SkColorType;
default:
return kUnknown_SkColorType;
}
}
int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* decoder, int32_t format) {
if (!decoder || format < ANDROID_BITMAP_FORMAT_NONE
|| format > ANDROID_BITMAP_FORMAT_RGBA_F16) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
auto* imageDecoder = toDecoder(decoder);
if (imageDecoder->currentFrame() != 0) {
return ANDROID_IMAGE_DECODER_INVALID_STATE;
}
return imageDecoder->setOutColorType(getColorType((AndroidBitmapFormat) format))
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
}
int AImageDecoder_setDataSpace(AImageDecoder* decoder, int32_t dataspace) {
sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace);
// 0 is ADATASPACE_UNKNOWN. We need an explicit request for an ADataSpace.
if (!decoder || !dataspace || !cs) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
ImageDecoder* imageDecoder = toDecoder(decoder);
if (imageDecoder->currentFrame() != 0) {
return ANDROID_IMAGE_DECODER_INVALID_STATE;
}
imageDecoder->setOutColorSpace(std::move(cs));
return ANDROID_IMAGE_DECODER_SUCCESS;
}
const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(const AImageDecoder* decoder) {
return reinterpret_cast<const AImageDecoderHeaderInfo*>(decoder);
}
static const ImageDecoder* toDecoder(const AImageDecoderHeaderInfo* info) {
return reinterpret_cast<const ImageDecoder*>(info);
}
int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo* info) {
if (!info) {
return 0;
}
return toDecoder(info)->width();
}
int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* info) {
if (!info) {
return 0;
}
return toDecoder(info)->height();
}
const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* info) {
if (!info) {
return nullptr;
}
return getMimeType(toDecoder(info)->mCodec->getEncodedFormat());
}
int32_t AImageDecoderHeaderInfo_getDataSpace(const AImageDecoderHeaderInfo* info) {
if (!info) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
// Note: This recomputes the color type because it's possible the client has
// changed the output color type, so we cannot rely on it. Alternatively,
// we could store the ADataSpace in the ImageDecoder.
const ImageDecoder* imageDecoder = toDecoder(info);
SkColorType colorType = imageDecoder->mCodec->computeOutputColorType(kN32_SkColorType);
sk_sp<SkColorSpace> colorSpace = imageDecoder->getDefaultColorSpace();
return uirenderer::ColorSpaceToADataSpace(colorSpace.get(), colorType);
}
// FIXME: Share with getFormat in android_bitmap.cpp?
static AndroidBitmapFormat getFormat(SkColorType colorType) {
switch (colorType) {
case kN32_SkColorType:
return ANDROID_BITMAP_FORMAT_RGBA_8888;
case kRGB_565_SkColorType:
return ANDROID_BITMAP_FORMAT_RGB_565;
case kARGB_4444_SkColorType:
return ANDROID_BITMAP_FORMAT_RGBA_4444;
case kAlpha_8_SkColorType:
return ANDROID_BITMAP_FORMAT_A_8;
case kRGBA_F16_SkColorType:
return ANDROID_BITMAP_FORMAT_RGBA_F16;
default:
return ANDROID_BITMAP_FORMAT_NONE;
}
}
int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(const AImageDecoderHeaderInfo* info) {
if (!info) {
return ANDROID_BITMAP_FORMAT_NONE;
}
return getFormat(toDecoder(info)->mCodec->computeOutputColorType(kN32_SkColorType));
}
int AImageDecoderHeaderInfo_getAlphaFlags(const AImageDecoderHeaderInfo* info) {
if (!info) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
switch (toDecoder(info)->mCodec->getInfo().alphaType()) {
case kUnknown_SkAlphaType:
LOG_ALWAYS_FATAL("Invalid alpha type");
return ANDROID_IMAGE_DECODER_INTERNAL_ERROR;
case kUnpremul_SkAlphaType:
// fall through. premul is the default.
case kPremul_SkAlphaType:
return ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
case kOpaque_SkAlphaType:
return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
}
}
int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* decoder, bool required) {
if (!decoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
auto* imageDecoder = toDecoder(decoder);
if (imageDecoder->currentFrame() != 0) {
return ANDROID_IMAGE_DECODER_INVALID_STATE;
}
return imageDecoder->setUnpremultipliedRequired(required)
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
}
int AImageDecoder_setTargetSize(AImageDecoder* decoder, int32_t width, int32_t height) {
if (!decoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
auto* imageDecoder = toDecoder(decoder);
if (imageDecoder->currentFrame() != 0) {
return ANDROID_IMAGE_DECODER_INVALID_STATE;
}
return imageDecoder->setTargetSize(width, height)
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_SCALE;
}
int AImageDecoder_computeSampledSize(const AImageDecoder* decoder, int sampleSize,
int32_t* width, int32_t* height) {
if (!decoder || !width || !height || sampleSize < 1) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
SkISize size = toDecoder(decoder)->getSampledDimensions(sampleSize);
*width = size.width();
*height = size.height();
return ANDROID_IMAGE_DECODER_SUCCESS;
}
int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) {
if (!decoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
auto* imageDecoder = toDecoder(decoder);
if (imageDecoder->currentFrame() != 0) {
return ANDROID_IMAGE_DECODER_INVALID_STATE;
}
SkIRect cropIRect;
cropIRect.setLTRB(crop.left, crop.top, crop.right, crop.bottom);
SkIRect* cropPtr = cropIRect == SkIRect::MakeEmpty() ? nullptr : &cropIRect;
return imageDecoder->setCropRect(cropPtr)
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
size_t AImageDecoder_getMinimumStride(AImageDecoder* decoder) {
if (!decoder) {
return 0;
}
SkImageInfo info = toDecoder(decoder)->getOutputInfo();
return info.minRowBytes();
}
int AImageDecoder_decodeImage(AImageDecoder* decoder,
void* pixels, size_t stride,
size_t size) {
if (!decoder || !pixels || !stride) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
ImageDecoder* imageDecoder = toDecoder(decoder);
SkImageInfo info = imageDecoder->getOutputInfo();
size_t minSize = info.computeByteSize(stride);
if (SkImageInfo::ByteSizeOverflowed(minSize) || size < minSize || !info.validRowBytes(stride)) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
if (imageDecoder->finished()) {
return ANDROID_IMAGE_DECODER_FINISHED;
}
return ResultToErrorCode(imageDecoder->decode(pixels, stride));
}
void AImageDecoder_delete(AImageDecoder* decoder) {
delete toDecoder(decoder);
}
bool AImageDecoder_isAnimated(AImageDecoder* decoder) {
if (!decoder) return false;
ImageDecoder* imageDecoder = toDecoder(decoder);
return imageDecoder->isAnimated();
}
int32_t AImageDecoder_getRepeatCount(AImageDecoder* decoder) {
if (!decoder) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
ImageDecoder* imageDecoder = toDecoder(decoder);
const int count = imageDecoder->mCodec->codec()->getRepetitionCount();
// Skia should not report anything out of range, but defensively treat
// negative and too big as INFINITE.
if (count == SkCodec::kRepetitionCountInfinite || count < 0
|| count > std::numeric_limits<int32_t>::max()) {
return ANDROID_IMAGE_DECODER_INFINITE;
}
return count;
}
int AImageDecoder_advanceFrame(AImageDecoder* decoder) {
if (!decoder) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
ImageDecoder* imageDecoder = toDecoder(decoder);
if (!imageDecoder->isAnimated()) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
const auto colorType = imageDecoder->getOutputInfo().colorType();
switch (colorType) {
case kN32_SkColorType:
case kRGBA_F16_SkColorType:
break;
default:
return ANDROID_IMAGE_DECODER_INVALID_STATE;
}
if (imageDecoder->advanceFrame()) {
return ANDROID_IMAGE_DECODER_SUCCESS;
}
if (imageDecoder->finished()) {
return ANDROID_IMAGE_DECODER_FINISHED;
}
return ANDROID_IMAGE_DECODER_INCOMPLETE;
}
int AImageDecoder_rewind(AImageDecoder* decoder) {
if (!decoder) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
ImageDecoder* imageDecoder = toDecoder(decoder);
if (!imageDecoder->isAnimated()) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
return imageDecoder->rewind() ? ANDROID_IMAGE_DECODER_SUCCESS
: ANDROID_IMAGE_DECODER_SEEK_ERROR;
}
AImageDecoderFrameInfo* AImageDecoderFrameInfo_create() {
return reinterpret_cast<AImageDecoderFrameInfo*>(new SkCodec::FrameInfo);
}
static SkCodec::FrameInfo* toFrameInfo(AImageDecoderFrameInfo* info) {
return reinterpret_cast<SkCodec::FrameInfo*>(info);
}
static const SkCodec::FrameInfo* toFrameInfo(const AImageDecoderFrameInfo* info) {
return reinterpret_cast<const SkCodec::FrameInfo*>(info);
}
void AImageDecoderFrameInfo_delete(AImageDecoderFrameInfo* info) {
delete toFrameInfo(info);
}
int AImageDecoder_getFrameInfo(AImageDecoder* decoder,
AImageDecoderFrameInfo* info) {
if (!decoder || !info) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
auto* imageDecoder = toDecoder(decoder);
if (imageDecoder->finished()) {
return ANDROID_IMAGE_DECODER_FINISHED;
}
*toFrameInfo(info) = imageDecoder->getCurrentFrameInfo();
return ANDROID_IMAGE_DECODER_SUCCESS;
}
int64_t AImageDecoderFrameInfo_getDuration(const AImageDecoderFrameInfo* info) {
if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
return toFrameInfo(info)->fDuration * 1'000'000;
}
ARect AImageDecoderFrameInfo_getFrameRect(const AImageDecoderFrameInfo* info) {
if (!info) {
return { 0, 0, 0, 0};
}
const SkIRect& r = toFrameInfo(info)->fFrameRect;
return { r.left(), r.top(), r.right(), r.bottom() };
}
bool AImageDecoderFrameInfo_hasAlphaWithinBounds(const AImageDecoderFrameInfo* info) {
if (!info) return false;
return toFrameInfo(info)->fHasAlphaWithinBounds;
}
int32_t AImageDecoderFrameInfo_getDisposeOp(const AImageDecoderFrameInfo* info) {
if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kKeep)
== ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE);
static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kRestoreBGColor)
== ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND);
static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kRestorePrevious)
== ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS);
return static_cast<int>(toFrameInfo(info)->fDisposalMethod);
}
int32_t AImageDecoderFrameInfo_getBlendOp(const AImageDecoderFrameInfo* info) {
if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
switch (toFrameInfo(info)->fBlend) {
case SkCodecAnimation::Blend::kSrc:
return ANDROID_IMAGE_DECODER_BLEND_OP_SRC;
case SkCodecAnimation::Blend::kSrcOver:
return ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER;
}
}
void AImageDecoder_setInternallyHandleDisposePrevious(AImageDecoder* decoder, bool handle) {
if (decoder) {
toDecoder(decoder)->setHandleRestorePrevious(handle);
}
}