/* * 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 #include #include namespace { class ImageGeneratorNDK : public SkImageGenerator { public: ImageGeneratorNDK(const SkImageInfo&, sk_sp, AImageDecoder*); ~ImageGeneratorNDK() override; protected: sk_sp onRefEncodedData() override; bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options& opts) override; private: sk_sp 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( AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo)); return SkNDKConversions::toColorType(format); } static sk_sp get_default_colorSpace(const AImageDecoderHeaderInfo* headerInfo) { auto dataSpace = static_cast(AImageDecoderHeaderInfo_getDataSpace(headerInfo)); if (auto cs = SkNDKConversions::toColorSpace(dataSpace)) { return cs; } return SkColorSpace::MakeSRGB(); } std::unique_ptr SkImageGeneratorNDK::MakeFromEncodedNDK(sk_sp 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( new ImageGeneratorNDK(imageInfo, std::move(data), rawDecoder)); } ImageGeneratorNDK::ImageGeneratorNDK(const SkImageInfo& info, sk_sp 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 ImageGeneratorNDK::onRefEncodedData() { return fData; }