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
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
new file mode 100644
index 0000000..2ef203d
--- /dev/null
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -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);
+}