| /* | |
| * Copyright 2020 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkImageGenerator.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/ports/SkImageGeneratorNDK.h" | |
| #include "src/ports/SkNDKConversions.h" | |
| #include <android/bitmap.h> | |
| #include <android/data_space.h> | |
| #include <android/imagedecoder.h> | |
| namespace { | |
| class ImageGeneratorNDK : public SkImageGenerator { | |
| public: | |
| ImageGeneratorNDK(const SkImageInfo&, sk_sp<const SkData>, AImageDecoder*); | |
| ~ImageGeneratorNDK() override; | |
| protected: | |
| sk_sp<const SkData> onRefEncodedData() override; | |
| bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, | |
| const Options& opts) override; | |
| private: | |
| sk_sp<const SkData> fData; | |
| AImageDecoder* fDecoder; | |
| // Setting the ADataSpace is sticky - it is set for all future decodes | |
| // until it is set again. But as of R there is no way to reset it to | |
| // ADATASPACE_UNKNOWN to skip color correction. If the client requests | |
| // skipping correction after having set it to something else, we need | |
| // to recreate the AImageDecoder. | |
| bool fPreviouslySetADataSpace; | |
| using INHERITED = SkImageGenerator; | |
| }; | |
| } // anonymous namespace | |
| static bool ok(int result) { | |
| return result == ANDROID_IMAGE_DECODER_SUCCESS; | |
| } | |
| static bool set_android_bitmap_format(AImageDecoder* decoder, SkColorType colorType) { | |
| auto format = SkNDKConversions::toAndroidBitmapFormat(colorType); | |
| return ok(AImageDecoder_setAndroidBitmapFormat(decoder, format)); | |
| } | |
| static SkColorType colorType(AImageDecoder* decoder, const AImageDecoderHeaderInfo* headerInfo) { | |
| // AImageDecoder never defaults to gray, but allows setting it if the image is 8 bit gray. | |
| if (set_android_bitmap_format(decoder, kGray_8_SkColorType)) { | |
| return kGray_8_SkColorType; | |
| } | |
| auto format = static_cast<AndroidBitmapFormat>( | |
| AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo)); | |
| return SkNDKConversions::toColorType(format); | |
| } | |
| static sk_sp<SkColorSpace> get_default_colorSpace(const AImageDecoderHeaderInfo* headerInfo) { | |
| auto dataSpace = static_cast<ADataSpace>(AImageDecoderHeaderInfo_getDataSpace(headerInfo)); | |
| if (auto cs = SkNDKConversions::toColorSpace(dataSpace)) { | |
| return cs; | |
| } | |
| return SkColorSpace::MakeSRGB(); | |
| } | |
| std::unique_ptr<SkImageGenerator> SkImageGeneratorNDK::MakeFromEncodedNDK( | |
| sk_sp<const SkData> data) { | |
| if (!data) { | |
| return nullptr; | |
| } | |
| AImageDecoder* rawDecoder; | |
| if (!ok(AImageDecoder_createFromBuffer(data->data(), data->size(), &rawDecoder))) { | |
| return nullptr; | |
| } | |
| const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(rawDecoder); | |
| int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo); | |
| int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo); | |
| SkColorType ct = colorType(rawDecoder, headerInfo); | |
| // Although the encoded data stores unpremultiplied pixels, AImageDecoder defaults to premul | |
| // (if the image may have alpha). | |
| SkAlphaType at = AImageDecoderHeaderInfo_getAlphaFlags(headerInfo) | |
| == ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE ? kOpaque_SkAlphaType : kPremul_SkAlphaType; | |
| auto imageInfo = SkImageInfo::Make(width, height, ct, at, get_default_colorSpace(headerInfo)); | |
| return std::unique_ptr<SkImageGenerator>( | |
| new ImageGeneratorNDK(imageInfo, std::move(data), rawDecoder)); | |
| } | |
| ImageGeneratorNDK::ImageGeneratorNDK(const SkImageInfo& info, | |
| sk_sp<const SkData> data, | |
| AImageDecoder* decoder) | |
| : INHERITED(info) | |
| , fData(std::move(data)) | |
| , fDecoder(decoder) | |
| , fPreviouslySetADataSpace(false) { | |
| SkASSERT(fDecoder); | |
| } | |
| ImageGeneratorNDK::~ImageGeneratorNDK() { | |
| AImageDecoder_delete(fDecoder); | |
| } | |
| static bool set_target_size(AImageDecoder* decoder, const SkISize& size, const SkISize targetSize) { | |
| if (size != targetSize) { | |
| // AImageDecoder will scale to arbitrary sizes. Only support a size if it's supported by the | |
| // underlying library. | |
| const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder); | |
| const char* mimeType = AImageDecoderHeaderInfo_getMimeType(headerInfo); | |
| if (0 == strcmp(mimeType, "image/jpeg")) { | |
| bool supported = false; | |
| for (int sampleSize : { 2, 4, 8 }) { | |
| int32_t width; | |
| int32_t height; | |
| if (ok(AImageDecoder_computeSampledSize(decoder, sampleSize, &width, &height)) | |
| && targetSize == SkISize::Make(width, height)) { | |
| supported = true; | |
| break; | |
| } | |
| } | |
| if (!supported) return false; | |
| } else if (0 == strcmp(mimeType, "image/webp")) { | |
| // libwebp supports arbitrary downscaling. | |
| if (targetSize.width() > size.width() || targetSize.height() > size.height()) { | |
| return false; | |
| } | |
| } else { | |
| return false; | |
| } | |
| } | |
| return ok(AImageDecoder_setTargetSize(decoder, targetSize.width(), targetSize.height())); | |
| } | |
| bool ImageGeneratorNDK::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, | |
| const Options& opts) { | |
| if (auto* cs = info.colorSpace()) { | |
| if (!ok(AImageDecoder_setDataSpace(fDecoder, SkNDKConversions::toDataSpace(cs)))) { | |
| return false; | |
| } | |
| fPreviouslySetADataSpace = true; | |
| } else { | |
| // If the requested SkColorSpace is null, the client wants the "raw" colors, without color | |
| // space transformations applied. (This is primarily useful for a client that wants to do | |
| // their own color transformations.) This is AImageDecoder's default, but if a previous call | |
| // set an ADataSpace, AImageDecoder is no longer using its default, so we need to set it | |
| // back. | |
| if (fPreviouslySetADataSpace) { | |
| // AImageDecoderHeaderInfo_getDataSpace always returns the same value for the same | |
| // image, regardless of prior calls to AImageDecoder_setDataSpace. Check if it's | |
| // ADATASPACE_UNKNOWN, which needs to be handled specially. | |
| const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(fDecoder); | |
| const auto defaultDataSpace = AImageDecoderHeaderInfo_getDataSpace(headerInfo); | |
| if (defaultDataSpace == ADATASPACE_UNKNOWN) { | |
| // As of R, there's no way to reset AImageDecoder to ADATASPACE_UNKNOWN, so | |
| // create a new one. | |
| AImageDecoder* decoder; | |
| if (!ok(AImageDecoder_createFromBuffer(fData->data(), fData->size(), &decoder))) { | |
| return false; | |
| } | |
| AImageDecoder_delete(fDecoder); | |
| fDecoder = decoder; | |
| } else { | |
| if (!ok(AImageDecoder_setDataSpace(fDecoder, defaultDataSpace))) { | |
| return false; | |
| } | |
| } | |
| // Whether by recreating AImageDecoder or calling AImageDecoder_setDataSpace, the | |
| // AImageDecoder is back to its default, so if the next call has a null SkColorSpace, it | |
| // does not need to reset it again. | |
| fPreviouslySetADataSpace = false; | |
| } | |
| } | |
| if (!set_android_bitmap_format(fDecoder, info.colorType())) { | |
| return false; | |
| } | |
| switch (info.alphaType()) { | |
| case kUnknown_SkAlphaType: | |
| return false; | |
| case kOpaque_SkAlphaType: | |
| if (this->getInfo().alphaType() != kOpaque_SkAlphaType) { | |
| return false; | |
| } | |
| break; | |
| case kUnpremul_SkAlphaType: | |
| if (!ok(AImageDecoder_setUnpremultipliedRequired(fDecoder, true))) { | |
| return false; | |
| } | |
| break; | |
| case kPremul_SkAlphaType: | |
| break; | |
| } | |
| if (!set_target_size(fDecoder, getInfo().dimensions(), info.dimensions())) { | |
| return false; | |
| } | |
| auto byteSize = info.computeByteSize(rowBytes); | |
| switch (AImageDecoder_decodeImage(fDecoder, pixels, rowBytes, byteSize)) { | |
| case ANDROID_IMAGE_DECODER_INCOMPLETE: | |
| // The image was partially decoded, but the input was truncated. The client may be | |
| // happy with the partial image. | |
| case ANDROID_IMAGE_DECODER_ERROR: | |
| // Similarly, the image was partially decoded, but the input had an error. The client | |
| // may be happy with the partial image. | |
| case ANDROID_IMAGE_DECODER_SUCCESS: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| sk_sp<const SkData> ImageGeneratorNDK::onRefEncodedData() { return fData; } |