1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2020 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageGenerator.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkImageGeneratorNDK.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/ports/SkNDKConversions.h"
12*c8dee2aaSAndroid Build Coastguard Worker
13*c8dee2aaSAndroid Build Coastguard Worker #include <android/bitmap.h>
14*c8dee2aaSAndroid Build Coastguard Worker #include <android/data_space.h>
15*c8dee2aaSAndroid Build Coastguard Worker #include <android/imagedecoder.h>
16*c8dee2aaSAndroid Build Coastguard Worker
17*c8dee2aaSAndroid Build Coastguard Worker namespace {
18*c8dee2aaSAndroid Build Coastguard Worker class ImageGeneratorNDK : public SkImageGenerator {
19*c8dee2aaSAndroid Build Coastguard Worker public:
20*c8dee2aaSAndroid Build Coastguard Worker ImageGeneratorNDK(const SkImageInfo&, sk_sp<SkData>, AImageDecoder*);
21*c8dee2aaSAndroid Build Coastguard Worker ~ImageGeneratorNDK() override;
22*c8dee2aaSAndroid Build Coastguard Worker
23*c8dee2aaSAndroid Build Coastguard Worker protected:
24*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> onRefEncodedData() override;
25*c8dee2aaSAndroid Build Coastguard Worker
26*c8dee2aaSAndroid Build Coastguard Worker bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
27*c8dee2aaSAndroid Build Coastguard Worker const Options& opts) override;
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker private:
30*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> fData;
31*c8dee2aaSAndroid Build Coastguard Worker AImageDecoder* fDecoder;
32*c8dee2aaSAndroid Build Coastguard Worker // Setting the ADataSpace is sticky - it is set for all future decodes
33*c8dee2aaSAndroid Build Coastguard Worker // until it is set again. But as of R there is no way to reset it to
34*c8dee2aaSAndroid Build Coastguard Worker // ADATASPACE_UNKNOWN to skip color correction. If the client requests
35*c8dee2aaSAndroid Build Coastguard Worker // skipping correction after having set it to something else, we need
36*c8dee2aaSAndroid Build Coastguard Worker // to recreate the AImageDecoder.
37*c8dee2aaSAndroid Build Coastguard Worker bool fPreviouslySetADataSpace;
38*c8dee2aaSAndroid Build Coastguard Worker
39*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = SkImageGenerator;
40*c8dee2aaSAndroid Build Coastguard Worker };
41*c8dee2aaSAndroid Build Coastguard Worker
42*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
43*c8dee2aaSAndroid Build Coastguard Worker
ok(int result)44*c8dee2aaSAndroid Build Coastguard Worker static bool ok(int result) {
45*c8dee2aaSAndroid Build Coastguard Worker return result == ANDROID_IMAGE_DECODER_SUCCESS;
46*c8dee2aaSAndroid Build Coastguard Worker }
47*c8dee2aaSAndroid Build Coastguard Worker
set_android_bitmap_format(AImageDecoder * decoder,SkColorType colorType)48*c8dee2aaSAndroid Build Coastguard Worker static bool set_android_bitmap_format(AImageDecoder* decoder, SkColorType colorType) {
49*c8dee2aaSAndroid Build Coastguard Worker auto format = SkNDKConversions::toAndroidBitmapFormat(colorType);
50*c8dee2aaSAndroid Build Coastguard Worker return ok(AImageDecoder_setAndroidBitmapFormat(decoder, format));
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker
colorType(AImageDecoder * decoder,const AImageDecoderHeaderInfo * headerInfo)53*c8dee2aaSAndroid Build Coastguard Worker static SkColorType colorType(AImageDecoder* decoder, const AImageDecoderHeaderInfo* headerInfo) {
54*c8dee2aaSAndroid Build Coastguard Worker // AImageDecoder never defaults to gray, but allows setting it if the image is 8 bit gray.
55*c8dee2aaSAndroid Build Coastguard Worker if (set_android_bitmap_format(decoder, kGray_8_SkColorType)) {
56*c8dee2aaSAndroid Build Coastguard Worker return kGray_8_SkColorType;
57*c8dee2aaSAndroid Build Coastguard Worker }
58*c8dee2aaSAndroid Build Coastguard Worker
59*c8dee2aaSAndroid Build Coastguard Worker auto format = static_cast<AndroidBitmapFormat>(
60*c8dee2aaSAndroid Build Coastguard Worker AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo));
61*c8dee2aaSAndroid Build Coastguard Worker return SkNDKConversions::toColorType(format);
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker
get_default_colorSpace(const AImageDecoderHeaderInfo * headerInfo)64*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkColorSpace> get_default_colorSpace(const AImageDecoderHeaderInfo* headerInfo) {
65*c8dee2aaSAndroid Build Coastguard Worker auto dataSpace = static_cast<ADataSpace>(AImageDecoderHeaderInfo_getDataSpace(headerInfo));
66*c8dee2aaSAndroid Build Coastguard Worker if (auto cs = SkNDKConversions::toColorSpace(dataSpace)) {
67*c8dee2aaSAndroid Build Coastguard Worker return cs;
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker
70*c8dee2aaSAndroid Build Coastguard Worker return SkColorSpace::MakeSRGB();
71*c8dee2aaSAndroid Build Coastguard Worker }
72*c8dee2aaSAndroid Build Coastguard Worker
MakeFromEncodedNDK(sk_sp<SkData> data)73*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkImageGenerator> SkImageGeneratorNDK::MakeFromEncodedNDK(sk_sp<SkData> data) {
74*c8dee2aaSAndroid Build Coastguard Worker if (!data) return nullptr;
75*c8dee2aaSAndroid Build Coastguard Worker
76*c8dee2aaSAndroid Build Coastguard Worker AImageDecoder* rawDecoder;
77*c8dee2aaSAndroid Build Coastguard Worker if (!ok(AImageDecoder_createFromBuffer(data->data(), data->size(), &rawDecoder))) {
78*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
79*c8dee2aaSAndroid Build Coastguard Worker }
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(rawDecoder);
82*c8dee2aaSAndroid Build Coastguard Worker int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo);
83*c8dee2aaSAndroid Build Coastguard Worker int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo);
84*c8dee2aaSAndroid Build Coastguard Worker SkColorType ct = colorType(rawDecoder, headerInfo);
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker // Although the encoded data stores unpremultiplied pixels, AImageDecoder defaults to premul
87*c8dee2aaSAndroid Build Coastguard Worker // (if the image may have alpha).
88*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType at = AImageDecoderHeaderInfo_getAlphaFlags(headerInfo)
89*c8dee2aaSAndroid Build Coastguard Worker == ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
90*c8dee2aaSAndroid Build Coastguard Worker auto imageInfo = SkImageInfo::Make(width, height, ct, at, get_default_colorSpace(headerInfo));
91*c8dee2aaSAndroid Build Coastguard Worker return std::unique_ptr<SkImageGenerator>(
92*c8dee2aaSAndroid Build Coastguard Worker new ImageGeneratorNDK(imageInfo, std::move(data), rawDecoder));
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker
ImageGeneratorNDK(const SkImageInfo & info,sk_sp<SkData> data,AImageDecoder * decoder)95*c8dee2aaSAndroid Build Coastguard Worker ImageGeneratorNDK::ImageGeneratorNDK(const SkImageInfo& info, sk_sp<SkData> data,
96*c8dee2aaSAndroid Build Coastguard Worker AImageDecoder* decoder)
97*c8dee2aaSAndroid Build Coastguard Worker : INHERITED(info)
98*c8dee2aaSAndroid Build Coastguard Worker , fData(std::move(data))
99*c8dee2aaSAndroid Build Coastguard Worker , fDecoder(decoder)
100*c8dee2aaSAndroid Build Coastguard Worker , fPreviouslySetADataSpace(false)
101*c8dee2aaSAndroid Build Coastguard Worker {
102*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDecoder);
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker
~ImageGeneratorNDK()105*c8dee2aaSAndroid Build Coastguard Worker ImageGeneratorNDK::~ImageGeneratorNDK() {
106*c8dee2aaSAndroid Build Coastguard Worker AImageDecoder_delete(fDecoder);
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker
set_target_size(AImageDecoder * decoder,const SkISize & size,const SkISize targetSize)109*c8dee2aaSAndroid Build Coastguard Worker static bool set_target_size(AImageDecoder* decoder, const SkISize& size, const SkISize targetSize) {
110*c8dee2aaSAndroid Build Coastguard Worker if (size != targetSize) {
111*c8dee2aaSAndroid Build Coastguard Worker // AImageDecoder will scale to arbitrary sizes. Only support a size if it's supported by the
112*c8dee2aaSAndroid Build Coastguard Worker // underlying library.
113*c8dee2aaSAndroid Build Coastguard Worker const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder);
114*c8dee2aaSAndroid Build Coastguard Worker const char* mimeType = AImageDecoderHeaderInfo_getMimeType(headerInfo);
115*c8dee2aaSAndroid Build Coastguard Worker if (0 == strcmp(mimeType, "image/jpeg")) {
116*c8dee2aaSAndroid Build Coastguard Worker bool supported = false;
117*c8dee2aaSAndroid Build Coastguard Worker for (int sampleSize : { 2, 4, 8 }) {
118*c8dee2aaSAndroid Build Coastguard Worker int32_t width;
119*c8dee2aaSAndroid Build Coastguard Worker int32_t height;
120*c8dee2aaSAndroid Build Coastguard Worker if (ok(AImageDecoder_computeSampledSize(decoder, sampleSize, &width, &height))
121*c8dee2aaSAndroid Build Coastguard Worker && targetSize == SkISize::Make(width, height)) {
122*c8dee2aaSAndroid Build Coastguard Worker supported = true;
123*c8dee2aaSAndroid Build Coastguard Worker break;
124*c8dee2aaSAndroid Build Coastguard Worker }
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker if (!supported) return false;
127*c8dee2aaSAndroid Build Coastguard Worker } else if (0 == strcmp(mimeType, "image/webp")) {
128*c8dee2aaSAndroid Build Coastguard Worker // libwebp supports arbitrary downscaling.
129*c8dee2aaSAndroid Build Coastguard Worker if (targetSize.width() > size.width() || targetSize.height() > size.height()) {
130*c8dee2aaSAndroid Build Coastguard Worker return false;
131*c8dee2aaSAndroid Build Coastguard Worker }
132*c8dee2aaSAndroid Build Coastguard Worker } else {
133*c8dee2aaSAndroid Build Coastguard Worker return false;
134*c8dee2aaSAndroid Build Coastguard Worker }
135*c8dee2aaSAndroid Build Coastguard Worker }
136*c8dee2aaSAndroid Build Coastguard Worker return ok(AImageDecoder_setTargetSize(decoder, targetSize.width(), targetSize.height()));
137*c8dee2aaSAndroid Build Coastguard Worker }
138*c8dee2aaSAndroid Build Coastguard Worker
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options & opts)139*c8dee2aaSAndroid Build Coastguard Worker bool ImageGeneratorNDK::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
140*c8dee2aaSAndroid Build Coastguard Worker const Options& opts) {
141*c8dee2aaSAndroid Build Coastguard Worker if (auto* cs = info.colorSpace()) {
142*c8dee2aaSAndroid Build Coastguard Worker if (!ok(AImageDecoder_setDataSpace(fDecoder, SkNDKConversions::toDataSpace(cs)))) {
143*c8dee2aaSAndroid Build Coastguard Worker return false;
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker fPreviouslySetADataSpace = true;
146*c8dee2aaSAndroid Build Coastguard Worker } else {
147*c8dee2aaSAndroid Build Coastguard Worker // If the requested SkColorSpace is null, the client wants the "raw" colors, without color
148*c8dee2aaSAndroid Build Coastguard Worker // space transformations applied. (This is primarily useful for a client that wants to do
149*c8dee2aaSAndroid Build Coastguard Worker // their own color transformations.) This is AImageDecoder's default, but if a previous call
150*c8dee2aaSAndroid Build Coastguard Worker // set an ADataSpace, AImageDecoder is no longer using its default, so we need to set it
151*c8dee2aaSAndroid Build Coastguard Worker // back.
152*c8dee2aaSAndroid Build Coastguard Worker if (fPreviouslySetADataSpace) {
153*c8dee2aaSAndroid Build Coastguard Worker // AImageDecoderHeaderInfo_getDataSpace always returns the same value for the same
154*c8dee2aaSAndroid Build Coastguard Worker // image, regardless of prior calls to AImageDecoder_setDataSpace. Check if it's
155*c8dee2aaSAndroid Build Coastguard Worker // ADATASPACE_UNKNOWN, which needs to be handled specially.
156*c8dee2aaSAndroid Build Coastguard Worker const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(fDecoder);
157*c8dee2aaSAndroid Build Coastguard Worker const auto defaultDataSpace = AImageDecoderHeaderInfo_getDataSpace(headerInfo);
158*c8dee2aaSAndroid Build Coastguard Worker if (defaultDataSpace == ADATASPACE_UNKNOWN) {
159*c8dee2aaSAndroid Build Coastguard Worker // As of R, there's no way to reset AImageDecoder to ADATASPACE_UNKNOWN, so
160*c8dee2aaSAndroid Build Coastguard Worker // create a new one.
161*c8dee2aaSAndroid Build Coastguard Worker AImageDecoder* decoder;
162*c8dee2aaSAndroid Build Coastguard Worker if (!ok(AImageDecoder_createFromBuffer(fData->data(), fData->size(), &decoder))) {
163*c8dee2aaSAndroid Build Coastguard Worker return false;
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker AImageDecoder_delete(fDecoder);
166*c8dee2aaSAndroid Build Coastguard Worker fDecoder = decoder;
167*c8dee2aaSAndroid Build Coastguard Worker } else {
168*c8dee2aaSAndroid Build Coastguard Worker if (!ok(AImageDecoder_setDataSpace(fDecoder, defaultDataSpace))) {
169*c8dee2aaSAndroid Build Coastguard Worker return false;
170*c8dee2aaSAndroid Build Coastguard Worker }
171*c8dee2aaSAndroid Build Coastguard Worker }
172*c8dee2aaSAndroid Build Coastguard Worker
173*c8dee2aaSAndroid Build Coastguard Worker // Whether by recreating AImageDecoder or calling AImageDecoder_setDataSpace, the
174*c8dee2aaSAndroid Build Coastguard Worker // AImageDecoder is back to its default, so if the next call has a null SkColorSpace, it
175*c8dee2aaSAndroid Build Coastguard Worker // does not need to reset it again.
176*c8dee2aaSAndroid Build Coastguard Worker fPreviouslySetADataSpace = false;
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker }
179*c8dee2aaSAndroid Build Coastguard Worker
180*c8dee2aaSAndroid Build Coastguard Worker if (!set_android_bitmap_format(fDecoder, info.colorType())) {
181*c8dee2aaSAndroid Build Coastguard Worker return false;
182*c8dee2aaSAndroid Build Coastguard Worker }
183*c8dee2aaSAndroid Build Coastguard Worker
184*c8dee2aaSAndroid Build Coastguard Worker switch (info.alphaType()) {
185*c8dee2aaSAndroid Build Coastguard Worker case kUnknown_SkAlphaType:
186*c8dee2aaSAndroid Build Coastguard Worker return false;
187*c8dee2aaSAndroid Build Coastguard Worker case kOpaque_SkAlphaType:
188*c8dee2aaSAndroid Build Coastguard Worker if (this->getInfo().alphaType() != kOpaque_SkAlphaType) {
189*c8dee2aaSAndroid Build Coastguard Worker return false;
190*c8dee2aaSAndroid Build Coastguard Worker }
191*c8dee2aaSAndroid Build Coastguard Worker break;
192*c8dee2aaSAndroid Build Coastguard Worker case kUnpremul_SkAlphaType:
193*c8dee2aaSAndroid Build Coastguard Worker if (!ok(AImageDecoder_setUnpremultipliedRequired(fDecoder, true))) {
194*c8dee2aaSAndroid Build Coastguard Worker return false;
195*c8dee2aaSAndroid Build Coastguard Worker }
196*c8dee2aaSAndroid Build Coastguard Worker break;
197*c8dee2aaSAndroid Build Coastguard Worker case kPremul_SkAlphaType:
198*c8dee2aaSAndroid Build Coastguard Worker break;
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker if (!set_target_size(fDecoder, getInfo().dimensions(), info.dimensions())) {
202*c8dee2aaSAndroid Build Coastguard Worker return false;
203*c8dee2aaSAndroid Build Coastguard Worker }
204*c8dee2aaSAndroid Build Coastguard Worker
205*c8dee2aaSAndroid Build Coastguard Worker auto byteSize = info.computeByteSize(rowBytes);
206*c8dee2aaSAndroid Build Coastguard Worker switch (AImageDecoder_decodeImage(fDecoder, pixels, rowBytes, byteSize)) {
207*c8dee2aaSAndroid Build Coastguard Worker case ANDROID_IMAGE_DECODER_INCOMPLETE:
208*c8dee2aaSAndroid Build Coastguard Worker // The image was partially decoded, but the input was truncated. The client may be
209*c8dee2aaSAndroid Build Coastguard Worker // happy with the partial image.
210*c8dee2aaSAndroid Build Coastguard Worker case ANDROID_IMAGE_DECODER_ERROR:
211*c8dee2aaSAndroid Build Coastguard Worker // Similarly, the image was partially decoded, but the input had an error. The client
212*c8dee2aaSAndroid Build Coastguard Worker // may be happy with the partial image.
213*c8dee2aaSAndroid Build Coastguard Worker case ANDROID_IMAGE_DECODER_SUCCESS:
214*c8dee2aaSAndroid Build Coastguard Worker return true;
215*c8dee2aaSAndroid Build Coastguard Worker default:
216*c8dee2aaSAndroid Build Coastguard Worker return false;
217*c8dee2aaSAndroid Build Coastguard Worker }
218*c8dee2aaSAndroid Build Coastguard Worker }
219*c8dee2aaSAndroid Build Coastguard Worker
onRefEncodedData()220*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> ImageGeneratorNDK::onRefEncodedData() {
221*c8dee2aaSAndroid Build Coastguard Worker return fData;
222*c8dee2aaSAndroid Build Coastguard Worker }
223