blob: 2ef203dd466f2e84322e564fe22b263cb4470245 [file] [log] [blame]
/*
* 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/imagedecoder.h>
#include <android/graphics/MimeType.h>
#include <android/rect.h>
#include <hwui/ImageDecoder.h>
#include <log/log.h>
#include <SkAndroidCodec.h>
#include <fcntl.h>
#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;
}
}
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);
auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
SkAndroidCodec::ExifOrientationBehavior::kRespect);
if (!androidCodec) {
return ResultToErrorCode(result);
}
*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;
auto stream = std::make_unique<AAssetStreamAdaptor>(asset);
return createFromStream(std::move(stream), outDecoder);
}
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);
}
// 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;
}
return toDecoder(decoder)->setOutColorType(getColorType((AndroidBitmapFormat) format))
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
}
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)->mCodec->getInfo().width();
}
int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* info) {
if (!info) {
return 0;
}
return toDecoder(info)->mCodec->getInfo().height();
}
const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* info) {
if (!info) {
return nullptr;
}
return getMimeType(toDecoder(info)->mCodec->getEncodedFormat());
}
bool AImageDecoderHeaderInfo_isAnimated(const AImageDecoderHeaderInfo* info) {
if (!info) {
return false;
}
return toDecoder(info)->mCodec->codec()->getFrameCount() > 1;
}
// 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;
}
}
AndroidBitmapFormat 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) {
// FIXME: Better invalid?
return -1;
}
switch (toDecoder(info)->mCodec->getInfo().alphaType()) {
case kUnknown_SkAlphaType:
LOG_ALWAYS_FATAL("Invalid alpha type");
return -1;
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;
}
}
SkAlphaType toAlphaType(int androidBitmapFlags) {
switch (androidBitmapFlags) {
case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL:
return kPremul_SkAlphaType;
case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL:
return kUnpremul_SkAlphaType;
case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE:
return kOpaque_SkAlphaType;
default:
return kUnknown_SkAlphaType;
}
}
int AImageDecoder_setAlphaFlags(AImageDecoder* decoder, int alphaFlag) {
if (!decoder || alphaFlag < ANDROID_BITMAP_FLAGS_ALPHA_PREMUL
|| alphaFlag > ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
return toDecoder(decoder)->setOutAlphaType(toAlphaType(alphaFlag))
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
}
int AImageDecoder_setTargetSize(AImageDecoder* decoder, int width, int height) {
if (!decoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
return toDecoder(decoder)->setTargetSize(width, height)
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_SCALE;
}
int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) {
if (!decoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
SkIRect cropIRect;
cropIRect.setLTRB(crop.left, crop.top, crop.right, crop.bottom);
SkIRect* cropPtr = cropIRect == SkIRect::MakeEmpty() ? nullptr : &cropIRect;
return toDecoder(decoder)->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);
const int height = imageDecoder->getOutputInfo().height();
const size_t minStride = AImageDecoder_getMinimumStride(decoder);
// If this calculation were to overflow, it would have been caught in
// setTargetSize.
if (stride < minStride || size < stride * (height - 1) + minStride) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
return ResultToErrorCode(imageDecoder->decode(pixels, stride));
}
void AImageDecoder_delete(AImageDecoder* decoder) {
delete toDecoder(decoder);
}