Implement new NDK image decoding apis
Bug: 135133301 Test: Ib84462ea5fa8a7779eaa44494775e182e52ecaca Separate out the code for encodedFormatToString into a piece which returns a const char* that can be used by AImageDecoder (with its own header) and the part that the JNI code uses to convert that into a Java String. Change-Id: I4cf8bfb0aacfb8e22c3f9b1689bd614ed1253673
This commit is contained in:
@@ -53,42 +53,34 @@ jmethodID gBitmapConfig_nativeToConfigMethodID;
|
||||
|
||||
using namespace android;
|
||||
|
||||
jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format) {
|
||||
const char* mimeType;
|
||||
const char* getMimeType(SkEncodedImageFormat format) {
|
||||
switch (format) {
|
||||
case SkEncodedImageFormat::kBMP:
|
||||
mimeType = "image/bmp";
|
||||
break;
|
||||
return "image/bmp";
|
||||
case SkEncodedImageFormat::kGIF:
|
||||
mimeType = "image/gif";
|
||||
break;
|
||||
return "image/gif";
|
||||
case SkEncodedImageFormat::kICO:
|
||||
mimeType = "image/x-ico";
|
||||
break;
|
||||
return "image/x-ico";
|
||||
case SkEncodedImageFormat::kJPEG:
|
||||
mimeType = "image/jpeg";
|
||||
break;
|
||||
return "image/jpeg";
|
||||
case SkEncodedImageFormat::kPNG:
|
||||
mimeType = "image/png";
|
||||
break;
|
||||
return "image/png";
|
||||
case SkEncodedImageFormat::kWEBP:
|
||||
mimeType = "image/webp";
|
||||
break;
|
||||
return "image/webp";
|
||||
case SkEncodedImageFormat::kHEIF:
|
||||
mimeType = "image/heif";
|
||||
break;
|
||||
return "image/heif";
|
||||
case SkEncodedImageFormat::kWBMP:
|
||||
mimeType = "image/vnd.wap.wbmp";
|
||||
break;
|
||||
return "image/vnd.wap.wbmp";
|
||||
case SkEncodedImageFormat::kDNG:
|
||||
mimeType = "image/x-adobe-dng";
|
||||
break;
|
||||
return "image/x-adobe-dng";
|
||||
default:
|
||||
mimeType = nullptr;
|
||||
break;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
jstring getMimeTypeAsJavaString(JNIEnv* env, SkEncodedImageFormat format) {
|
||||
jstring jstr = nullptr;
|
||||
const char* mimeType = getMimeType(format);
|
||||
if (mimeType) {
|
||||
// NOTE: Caller should env->ExceptionCheck() for OOM
|
||||
// (can't check for nullptr as it's a valid return value)
|
||||
@@ -290,10 +282,9 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
|
||||
|
||||
// Set the options and return if the client only wants the size.
|
||||
if (options != NULL) {
|
||||
jstring mimeType = encodedFormatToString(
|
||||
env, (SkEncodedImageFormat)codec->getEncodedFormat());
|
||||
jstring mimeType = getMimeTypeAsJavaString(env, codec->getEncodedFormat());
|
||||
if (env->ExceptionCheck()) {
|
||||
return nullObjectReturn("OOM in encodedFormatToString()");
|
||||
return nullObjectReturn("OOM in getMimeTypeAsJavaString()");
|
||||
}
|
||||
env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
|
||||
env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
|
||||
|
||||
@@ -26,6 +26,6 @@ extern jfieldID gOptions_bitmapFieldID;
|
||||
extern jclass gBitmapConfig_class;
|
||||
extern jmethodID gBitmapConfig_nativeToConfigMethodID;
|
||||
|
||||
jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format);
|
||||
jstring getMimeTypeAsJavaString(JNIEnv*, SkEncodedImageFormat);
|
||||
|
||||
#endif // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
|
||||
|
||||
@@ -197,7 +197,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
|
||||
env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
|
||||
|
||||
env->SetObjectField(options, gOptions_mimeFieldID,
|
||||
encodedFormatToString(env, (SkEncodedImageFormat)brd->getEncodedFormat()));
|
||||
getMimeTypeAsJavaString(env, brd->getEncodedFormat()));
|
||||
if (env->ExceptionCheck()) {
|
||||
return nullObjectReturn("OOM in encodedFormatToString()");
|
||||
}
|
||||
|
||||
@@ -475,7 +475,7 @@ static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong native
|
||||
|
||||
static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
|
||||
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
|
||||
return encodedFormatToString(env, decoder->mCodec->getEncodedFormat());
|
||||
return getMimeTypeAsJavaString(env, decoder->mCodec->getEncodedFormat());
|
||||
}
|
||||
|
||||
static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
|
||||
|
||||
21
core/jni/android/graphics/MimeType.h
Normal file
21
core/jni/android/graphics/MimeType.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SkEncodedImageFormat.h"
|
||||
|
||||
const char* getMimeType(SkEncodedImageFormat);
|
||||
@@ -25,16 +25,20 @@ cc_library_shared {
|
||||
// our source files
|
||||
//
|
||||
srcs: [
|
||||
"bitmap.cpp",
|
||||
"aassetstreamadaptor.cpp",
|
||||
"bitmap.cpp",
|
||||
"imagedecoder.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libandroid",
|
||||
"libandroid_runtime",
|
||||
"libhwui",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
static_libs: ["libarect"],
|
||||
|
||||
arch: {
|
||||
arm: {
|
||||
// TODO: This is to work around b/24465209. Remove after root cause is fixed
|
||||
|
||||
320
native/graphics/jni/imagedecoder.cpp
Normal file
320
native/graphics/jni/imagedecoder.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
@@ -1,5 +1,22 @@
|
||||
LIBJNIGRAPHICS {
|
||||
global:
|
||||
AImageDecoder_createFromAAsset;
|
||||
AImageDecoder_createFromFd;
|
||||
AImageDecoder_createFromBuffer;
|
||||
AImageDecoder_delete;
|
||||
AImageDecoder_setAndroidBitmapFormat;
|
||||
AImageDecoder_setAlphaFlags;
|
||||
AImageDecoder_getHeaderInfo;
|
||||
AImageDecoder_getMinimumStride;
|
||||
AImageDecoder_decodeImage;
|
||||
AImageDecoder_setTargetSize;
|
||||
AImageDecoder_setCrop;
|
||||
AImageDecoderHeaderInfo_getWidth;
|
||||
AImageDecoderHeaderInfo_getHeight;
|
||||
AImageDecoderHeaderInfo_getMimeType;
|
||||
AImageDecoderHeaderInfo_getAlphaFlags;
|
||||
AImageDecoderHeaderInfo_isAnimated;
|
||||
AImageDecoderHeaderInfo_getAndroidBitmapFormat;
|
||||
AndroidBitmap_getInfo;
|
||||
AndroidBitmap_lockPixels;
|
||||
AndroidBitmap_unlockPixels;
|
||||
|
||||
Reference in New Issue
Block a user