xref: /aosp_15_r20/external/skia/src/ports/SkImageGeneratorNDK.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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