xref: /aosp_15_r20/frameworks/base/libs/hwui/jni/ImageDecoder.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2017 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "ImageDecoder.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <FrontBufferedStream.h>
20*d57664e9SAndroid Build Coastguard Worker #include <HardwareBitmapUploader.h>
21*d57664e9SAndroid Build Coastguard Worker #include <SkAlphaType.h>
22*d57664e9SAndroid Build Coastguard Worker #include <SkAndroidCodec.h>
23*d57664e9SAndroid Build Coastguard Worker #include <SkBitmap.h>
24*d57664e9SAndroid Build Coastguard Worker #include <SkCodec.h>
25*d57664e9SAndroid Build Coastguard Worker #include <SkCodecAnimation.h>
26*d57664e9SAndroid Build Coastguard Worker #include <SkColorSpace.h>
27*d57664e9SAndroid Build Coastguard Worker #include <SkColorType.h>
28*d57664e9SAndroid Build Coastguard Worker #include <SkEncodedImageFormat.h>
29*d57664e9SAndroid Build Coastguard Worker #include <SkImageInfo.h>
30*d57664e9SAndroid Build Coastguard Worker #include <SkRect.h>
31*d57664e9SAndroid Build Coastguard Worker #include <SkSize.h>
32*d57664e9SAndroid Build Coastguard Worker #include <SkStream.h>
33*d57664e9SAndroid Build Coastguard Worker #include <SkString.h>
34*d57664e9SAndroid Build Coastguard Worker #include <androidfw/Asset.h>
35*d57664e9SAndroid Build Coastguard Worker #include <fcntl.h>
36*d57664e9SAndroid Build Coastguard Worker #include <gui/TraceUtils.h>
37*d57664e9SAndroid Build Coastguard Worker #include <hwui/Bitmap.h>
38*d57664e9SAndroid Build Coastguard Worker #include <hwui/ImageDecoder.h>
39*d57664e9SAndroid Build Coastguard Worker #include <sys/stat.h>
40*d57664e9SAndroid Build Coastguard Worker #include <utils/StatsUtils.h>
41*d57664e9SAndroid Build Coastguard Worker 
42*d57664e9SAndroid Build Coastguard Worker #include "Bitmap.h"
43*d57664e9SAndroid Build Coastguard Worker #include "BitmapFactory.h"
44*d57664e9SAndroid Build Coastguard Worker #include "ByteBufferStreamAdaptor.h"
45*d57664e9SAndroid Build Coastguard Worker #include "CreateJavaOutputStreamAdaptor.h"
46*d57664e9SAndroid Build Coastguard Worker #include "Gainmap.h"
47*d57664e9SAndroid Build Coastguard Worker #include "GraphicsJNI.h"
48*d57664e9SAndroid Build Coastguard Worker #include "NinePatchPeeker.h"
49*d57664e9SAndroid Build Coastguard Worker #include "Utils.h"
50*d57664e9SAndroid Build Coastguard Worker 
51*d57664e9SAndroid Build Coastguard Worker using namespace android;
52*d57664e9SAndroid Build Coastguard Worker 
53*d57664e9SAndroid Build Coastguard Worker jclass gImageDecoder_class;
54*d57664e9SAndroid Build Coastguard Worker jmethodID gImageDecoder_isP010SupportedForHEVCMethodID;
55*d57664e9SAndroid Build Coastguard Worker static jclass    gSize_class;
56*d57664e9SAndroid Build Coastguard Worker static jclass    gDecodeException_class;
57*d57664e9SAndroid Build Coastguard Worker static jclass    gCanvas_class;
58*d57664e9SAndroid Build Coastguard Worker static jmethodID gImageDecoder_constructorMethodID;
59*d57664e9SAndroid Build Coastguard Worker static jmethodID gImageDecoder_postProcessMethodID;
60*d57664e9SAndroid Build Coastguard Worker static jmethodID gSize_constructorMethodID;
61*d57664e9SAndroid Build Coastguard Worker static jmethodID gDecodeException_constructorMethodID;
62*d57664e9SAndroid Build Coastguard Worker static jmethodID gCallback_onPartialImageMethodID;
63*d57664e9SAndroid Build Coastguard Worker static jmethodID gCanvas_constructorMethodID;
64*d57664e9SAndroid Build Coastguard Worker static jmethodID gCanvas_releaseMethodID;
65*d57664e9SAndroid Build Coastguard Worker 
66*d57664e9SAndroid Build Coastguard Worker // These need to stay in sync with ImageDecoder.java's Allocator constants.
67*d57664e9SAndroid Build Coastguard Worker enum Allocator {
68*d57664e9SAndroid Build Coastguard Worker     kDefault_Allocator      = 0,
69*d57664e9SAndroid Build Coastguard Worker     kSoftware_Allocator     = 1,
70*d57664e9SAndroid Build Coastguard Worker     kSharedMemory_Allocator = 2,
71*d57664e9SAndroid Build Coastguard Worker     kHardware_Allocator     = 3,
72*d57664e9SAndroid Build Coastguard Worker };
73*d57664e9SAndroid Build Coastguard Worker 
74*d57664e9SAndroid Build Coastguard Worker // These need to stay in sync with ImageDecoder.java's Error constants.
75*d57664e9SAndroid Build Coastguard Worker enum Error {
76*d57664e9SAndroid Build Coastguard Worker     kSourceException     = 1,
77*d57664e9SAndroid Build Coastguard Worker     kSourceIncomplete    = 2,
78*d57664e9SAndroid Build Coastguard Worker     kSourceMalformedData = 3,
79*d57664e9SAndroid Build Coastguard Worker };
80*d57664e9SAndroid Build Coastguard Worker 
81*d57664e9SAndroid Build Coastguard Worker // These need to stay in sync with PixelFormat.java's Format constants.
82*d57664e9SAndroid Build Coastguard Worker enum PixelFormat {
83*d57664e9SAndroid Build Coastguard Worker     kUnknown     =  0,
84*d57664e9SAndroid Build Coastguard Worker     kTranslucent = -3,
85*d57664e9SAndroid Build Coastguard Worker     kOpaque      = -1,
86*d57664e9SAndroid Build Coastguard Worker };
87*d57664e9SAndroid Build Coastguard Worker 
88*d57664e9SAndroid Build Coastguard Worker // Clear and return any pending exception for handling other than throwing directly.
get_and_clear_exception(JNIEnv * env)89*d57664e9SAndroid Build Coastguard Worker static jthrowable get_and_clear_exception(JNIEnv* env) {
90*d57664e9SAndroid Build Coastguard Worker     jthrowable jexception = env->ExceptionOccurred();
91*d57664e9SAndroid Build Coastguard Worker     if (jexception) {
92*d57664e9SAndroid Build Coastguard Worker         env->ExceptionClear();
93*d57664e9SAndroid Build Coastguard Worker     }
94*d57664e9SAndroid Build Coastguard Worker     return jexception;
95*d57664e9SAndroid Build Coastguard Worker }
96*d57664e9SAndroid Build Coastguard Worker 
97*d57664e9SAndroid Build Coastguard Worker // Throw a new ImageDecoder.DecodeException. Returns null for convenience.
throw_exception(JNIEnv * env,Error error,const char * msg,jthrowable cause,jobject source)98*d57664e9SAndroid Build Coastguard Worker static jobject throw_exception(JNIEnv* env, Error error, const char* msg,
99*d57664e9SAndroid Build Coastguard Worker                                jthrowable cause, jobject source) {
100*d57664e9SAndroid Build Coastguard Worker     jstring jstr = nullptr;
101*d57664e9SAndroid Build Coastguard Worker     if (msg) {
102*d57664e9SAndroid Build Coastguard Worker         jstr = env->NewStringUTF(msg);
103*d57664e9SAndroid Build Coastguard Worker         if (!jstr) {
104*d57664e9SAndroid Build Coastguard Worker             // Out of memory.
105*d57664e9SAndroid Build Coastguard Worker             return nullptr;
106*d57664e9SAndroid Build Coastguard Worker         }
107*d57664e9SAndroid Build Coastguard Worker     }
108*d57664e9SAndroid Build Coastguard Worker     jthrowable exception = (jthrowable) env->NewObject(gDecodeException_class,
109*d57664e9SAndroid Build Coastguard Worker             gDecodeException_constructorMethodID, error, jstr, cause, source);
110*d57664e9SAndroid Build Coastguard Worker     // Only throw if not out of memory.
111*d57664e9SAndroid Build Coastguard Worker     if (exception) {
112*d57664e9SAndroid Build Coastguard Worker         env->Throw(exception);
113*d57664e9SAndroid Build Coastguard Worker     }
114*d57664e9SAndroid Build Coastguard Worker     return nullptr;
115*d57664e9SAndroid Build Coastguard Worker }
116*d57664e9SAndroid Build Coastguard Worker 
native_create(JNIEnv * env,std::unique_ptr<SkStream> stream,jobject source,jboolean preferAnimation)117*d57664e9SAndroid Build Coastguard Worker static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
118*d57664e9SAndroid Build Coastguard Worker         jobject source, jboolean preferAnimation) {
119*d57664e9SAndroid Build Coastguard Worker     if (!stream.get()) {
120*d57664e9SAndroid Build Coastguard Worker         return throw_exception(env, kSourceMalformedData, "Failed to create a stream",
121*d57664e9SAndroid Build Coastguard Worker                                nullptr, source);
122*d57664e9SAndroid Build Coastguard Worker     }
123*d57664e9SAndroid Build Coastguard Worker     sk_sp<NinePatchPeeker> peeker(new NinePatchPeeker);
124*d57664e9SAndroid Build Coastguard Worker     SkCodec::Result result;
125*d57664e9SAndroid Build Coastguard Worker     auto codec = SkCodec::MakeFromStream(
126*d57664e9SAndroid Build Coastguard Worker             std::move(stream), &result, peeker.get(),
127*d57664e9SAndroid Build Coastguard Worker             preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
128*d57664e9SAndroid Build Coastguard Worker                             : SkCodec::SelectionPolicy::kPreferStillImage);
129*d57664e9SAndroid Build Coastguard Worker     if (jthrowable jexception = get_and_clear_exception(env)) {
130*d57664e9SAndroid Build Coastguard Worker         return throw_exception(env, kSourceException, "", jexception, source);
131*d57664e9SAndroid Build Coastguard Worker     }
132*d57664e9SAndroid Build Coastguard Worker     if (!codec) {
133*d57664e9SAndroid Build Coastguard Worker         switch (result) {
134*d57664e9SAndroid Build Coastguard Worker             case SkCodec::kIncompleteInput:
135*d57664e9SAndroid Build Coastguard Worker                 return throw_exception(env, kSourceIncomplete, "", nullptr, source);
136*d57664e9SAndroid Build Coastguard Worker             default:
137*d57664e9SAndroid Build Coastguard Worker                 SkString msg;
138*d57664e9SAndroid Build Coastguard Worker                 msg.printf("Failed to create image decoder with message '%s'",
139*d57664e9SAndroid Build Coastguard Worker                            SkCodec::ResultToString(result));
140*d57664e9SAndroid Build Coastguard Worker                 return throw_exception(env, kSourceMalformedData,  msg.c_str(),
141*d57664e9SAndroid Build Coastguard Worker                                        nullptr, source);
142*d57664e9SAndroid Build Coastguard Worker 
143*d57664e9SAndroid Build Coastguard Worker         }
144*d57664e9SAndroid Build Coastguard Worker     }
145*d57664e9SAndroid Build Coastguard Worker 
146*d57664e9SAndroid Build Coastguard Worker     const bool animated = codec->getFrameCount() > 1;
147*d57664e9SAndroid Build Coastguard Worker     if (jthrowable jexception = get_and_clear_exception(env)) {
148*d57664e9SAndroid Build Coastguard Worker         return throw_exception(env, kSourceException, "", jexception, source);
149*d57664e9SAndroid Build Coastguard Worker     }
150*d57664e9SAndroid Build Coastguard Worker 
151*d57664e9SAndroid Build Coastguard Worker     auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
152*d57664e9SAndroid Build Coastguard Worker     if (!androidCodec.get()) {
153*d57664e9SAndroid Build Coastguard Worker         return throw_exception(env, kSourceMalformedData, "", nullptr, source);
154*d57664e9SAndroid Build Coastguard Worker     }
155*d57664e9SAndroid Build Coastguard Worker 
156*d57664e9SAndroid Build Coastguard Worker     const bool isNinePatch = peeker->mPatch != nullptr;
157*d57664e9SAndroid Build Coastguard Worker     ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker),
158*d57664e9SAndroid Build Coastguard Worker                                              SkCodec::kYes_ZeroInitialized);
159*d57664e9SAndroid Build Coastguard Worker     return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
160*d57664e9SAndroid Build Coastguard Worker                           reinterpret_cast<jlong>(decoder), decoder->width(), decoder->height(),
161*d57664e9SAndroid Build Coastguard Worker                           animated, isNinePatch);
162*d57664e9SAndroid Build Coastguard Worker }
163*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nCreateFd(JNIEnv * env,jobject,jobject fileDescriptor,jlong length,jboolean preferAnimation,jobject source)164*d57664e9SAndroid Build Coastguard Worker static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
165*d57664e9SAndroid Build Coastguard Worker         jobject fileDescriptor, jlong length, jboolean preferAnimation, jobject source) {
166*d57664e9SAndroid Build Coastguard Worker #ifdef _WIN32  // LayoutLib for Windows does not support F_DUPFD_CLOEXEC
167*d57664e9SAndroid Build Coastguard Worker     return throw_exception(env, kSourceException, "Not supported on Windows", nullptr, source);
168*d57664e9SAndroid Build Coastguard Worker #else
169*d57664e9SAndroid Build Coastguard Worker     int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
170*d57664e9SAndroid Build Coastguard Worker 
171*d57664e9SAndroid Build Coastguard Worker     struct stat fdStat;
172*d57664e9SAndroid Build Coastguard Worker     if (fstat(descriptor, &fdStat) == -1) {
173*d57664e9SAndroid Build Coastguard Worker         return throw_exception(env, kSourceMalformedData,
174*d57664e9SAndroid Build Coastguard Worker                                "broken file descriptor; fstat returned -1", nullptr, source);
175*d57664e9SAndroid Build Coastguard Worker     }
176*d57664e9SAndroid Build Coastguard Worker 
177*d57664e9SAndroid Build Coastguard Worker     int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0);
178*d57664e9SAndroid Build Coastguard Worker     FILE* file = fdopen(dupDescriptor, "r");
179*d57664e9SAndroid Build Coastguard Worker     if (file == NULL) {
180*d57664e9SAndroid Build Coastguard Worker         close(dupDescriptor);
181*d57664e9SAndroid Build Coastguard Worker         return throw_exception(env, kSourceMalformedData, "Could not open file",
182*d57664e9SAndroid Build Coastguard Worker                                nullptr, source);
183*d57664e9SAndroid Build Coastguard Worker     }
184*d57664e9SAndroid Build Coastguard Worker 
185*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<SkFILEStream> fileStream;
186*d57664e9SAndroid Build Coastguard Worker     if (length == -1) {
187*d57664e9SAndroid Build Coastguard Worker         // -1 corresponds to AssetFileDescriptor.UNKNOWN_LENGTH. Pass no length
188*d57664e9SAndroid Build Coastguard Worker         // so SkFILEStream will figure out the size of the file on its own.
189*d57664e9SAndroid Build Coastguard Worker         fileStream.reset(new SkFILEStream(file));
190*d57664e9SAndroid Build Coastguard Worker     } else {
191*d57664e9SAndroid Build Coastguard Worker         fileStream.reset(new SkFILEStream(file, length));
192*d57664e9SAndroid Build Coastguard Worker     }
193*d57664e9SAndroid Build Coastguard Worker     return native_create(env, std::move(fileStream), source, preferAnimation);
194*d57664e9SAndroid Build Coastguard Worker #endif
195*d57664e9SAndroid Build Coastguard Worker }
196*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nCreateInputStream(JNIEnv * env,jobject,jobject is,jbyteArray storage,jboolean preferAnimation,jobject source)197*d57664e9SAndroid Build Coastguard Worker static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
198*d57664e9SAndroid Build Coastguard Worker         jobject is, jbyteArray storage, jboolean preferAnimation, jobject source) {
199*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
200*d57664e9SAndroid Build Coastguard Worker 
201*d57664e9SAndroid Build Coastguard Worker     if (!stream.get()) {
202*d57664e9SAndroid Build Coastguard Worker         return throw_exception(env, kSourceMalformedData, "Failed to create a stream",
203*d57664e9SAndroid Build Coastguard Worker                                nullptr, source);
204*d57664e9SAndroid Build Coastguard Worker     }
205*d57664e9SAndroid Build Coastguard Worker 
206*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<SkStream> bufferedStream(
207*d57664e9SAndroid Build Coastguard Worker             skia::FrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded()));
208*d57664e9SAndroid Build Coastguard Worker     return native_create(env, std::move(bufferedStream), source, preferAnimation);
209*d57664e9SAndroid Build Coastguard Worker }
210*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nCreateAsset(JNIEnv * env,jobject,jlong assetPtr,jboolean preferAnimation,jobject source)211*d57664e9SAndroid Build Coastguard Worker static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/,
212*d57664e9SAndroid Build Coastguard Worker         jlong assetPtr, jboolean preferAnimation, jobject source) {
213*d57664e9SAndroid Build Coastguard Worker     Asset* asset = reinterpret_cast<Asset*>(assetPtr);
214*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
215*d57664e9SAndroid Build Coastguard Worker     return native_create(env, std::move(stream), source, preferAnimation);
216*d57664e9SAndroid Build Coastguard Worker }
217*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nCreateByteBuffer(JNIEnv * env,jobject,jobject jbyteBuffer,jint initialPosition,jint limit,jboolean preferAnimation,jobject source)218*d57664e9SAndroid Build Coastguard Worker static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/,
219*d57664e9SAndroid Build Coastguard Worker         jobject jbyteBuffer, jint initialPosition, jint limit,
220*d57664e9SAndroid Build Coastguard Worker         jboolean preferAnimation, jobject source) {
221*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
222*d57664e9SAndroid Build Coastguard Worker                                                                      initialPosition, limit);
223*d57664e9SAndroid Build Coastguard Worker     if (!stream) {
224*d57664e9SAndroid Build Coastguard Worker         return throw_exception(env, kSourceMalformedData, "Failed to read ByteBuffer",
225*d57664e9SAndroid Build Coastguard Worker                                nullptr, source);
226*d57664e9SAndroid Build Coastguard Worker     }
227*d57664e9SAndroid Build Coastguard Worker     return native_create(env, std::move(stream), source, preferAnimation);
228*d57664e9SAndroid Build Coastguard Worker }
229*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nCreateByteArray(JNIEnv * env,jobject,jbyteArray byteArray,jint offset,jint length,jboolean preferAnimation,jobject source)230*d57664e9SAndroid Build Coastguard Worker static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/,
231*d57664e9SAndroid Build Coastguard Worker         jbyteArray byteArray, jint offset, jint length,
232*d57664e9SAndroid Build Coastguard Worker         jboolean preferAnimation, jobject source) {
233*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
234*d57664e9SAndroid Build Coastguard Worker     return native_create(env, std::move(stream), source, preferAnimation);
235*d57664e9SAndroid Build Coastguard Worker }
236*d57664e9SAndroid Build Coastguard Worker 
postProcessAndRelease(JNIEnv * env,jobject jimageDecoder,std::unique_ptr<Canvas> canvas)237*d57664e9SAndroid Build Coastguard Worker jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
238*d57664e9SAndroid Build Coastguard Worker     jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
239*d57664e9SAndroid Build Coastguard Worker                                      reinterpret_cast<jlong>(canvas.get()));
240*d57664e9SAndroid Build Coastguard Worker     if (!jcanvas) {
241*d57664e9SAndroid Build Coastguard Worker         doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
242*d57664e9SAndroid Build Coastguard Worker         return kUnknown;
243*d57664e9SAndroid Build Coastguard Worker     }
244*d57664e9SAndroid Build Coastguard Worker 
245*d57664e9SAndroid Build Coastguard Worker     // jcanvas now owns canvas.
246*d57664e9SAndroid Build Coastguard Worker     canvas.release();
247*d57664e9SAndroid Build Coastguard Worker 
248*d57664e9SAndroid Build Coastguard Worker     return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas);
249*d57664e9SAndroid Build Coastguard Worker }
250*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nDecodeBitmap(JNIEnv * env,jobject,jlong nativePtr,jobject jdecoder,jboolean jpostProcess,jint targetWidth,jint targetHeight,jobject jsubset,jboolean requireMutable,jint allocator,jboolean requireUnpremul,jboolean preferRamOverQuality,jboolean asAlphaMask,jlong colorSpaceHandle,jboolean extended)251*d57664e9SAndroid Build Coastguard Worker static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
252*d57664e9SAndroid Build Coastguard Worker                                           jobject jdecoder, jboolean jpostProcess,
253*d57664e9SAndroid Build Coastguard Worker                                           jint targetWidth, jint targetHeight, jobject jsubset,
254*d57664e9SAndroid Build Coastguard Worker                                           jboolean requireMutable, jint allocator,
255*d57664e9SAndroid Build Coastguard Worker                                           jboolean requireUnpremul, jboolean preferRamOverQuality,
256*d57664e9SAndroid Build Coastguard Worker                                           jboolean asAlphaMask, jlong colorSpaceHandle,
257*d57664e9SAndroid Build Coastguard Worker                                           jboolean extended) {
258*d57664e9SAndroid Build Coastguard Worker     ATRACE_CALL();
259*d57664e9SAndroid Build Coastguard Worker     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
260*d57664e9SAndroid Build Coastguard Worker     if (!decoder->setTargetSize(targetWidth, targetHeight)) {
261*d57664e9SAndroid Build Coastguard Worker         doThrowISE(env, "Could not scale to target size!");
262*d57664e9SAndroid Build Coastguard Worker         return nullptr;
263*d57664e9SAndroid Build Coastguard Worker     }
264*d57664e9SAndroid Build Coastguard Worker     if (requireUnpremul && !decoder->setUnpremultipliedRequired(true)) {
265*d57664e9SAndroid Build Coastguard Worker         doThrowISE(env, "Cannot scale unpremultiplied pixels!");
266*d57664e9SAndroid Build Coastguard Worker         return nullptr;
267*d57664e9SAndroid Build Coastguard Worker     }
268*d57664e9SAndroid Build Coastguard Worker 
269*d57664e9SAndroid Build Coastguard Worker     SkColorType colorType = kN32_SkColorType;
270*d57664e9SAndroid Build Coastguard Worker     if (asAlphaMask && decoder->gray()) {
271*d57664e9SAndroid Build Coastguard Worker         // We have to trick Skia to decode this to a single channel.
272*d57664e9SAndroid Build Coastguard Worker         colorType = kGray_8_SkColorType;
273*d57664e9SAndroid Build Coastguard Worker     } else if (preferRamOverQuality) {
274*d57664e9SAndroid Build Coastguard Worker         // FIXME: The post-process might add alpha, which would make a 565
275*d57664e9SAndroid Build Coastguard Worker         // result incorrect. If we call the postProcess before now and record
276*d57664e9SAndroid Build Coastguard Worker         // to a picture, we can know whether alpha was added, and if not, we
277*d57664e9SAndroid Build Coastguard Worker         // can still use 565.
278*d57664e9SAndroid Build Coastguard Worker         if (decoder->opaque() && !jpostProcess) {
279*d57664e9SAndroid Build Coastguard Worker             // If the final result will be hardware, decoding to 565 and then
280*d57664e9SAndroid Build Coastguard Worker             // uploading to the gpu as 8888 will not save memory. This still
281*d57664e9SAndroid Build Coastguard Worker             // may save us from using F16, but do not go down to 565.
282*d57664e9SAndroid Build Coastguard Worker             if (allocator != kHardware_Allocator &&
283*d57664e9SAndroid Build Coastguard Worker                (allocator != kDefault_Allocator || requireMutable)) {
284*d57664e9SAndroid Build Coastguard Worker                 colorType = kRGB_565_SkColorType;
285*d57664e9SAndroid Build Coastguard Worker             }
286*d57664e9SAndroid Build Coastguard Worker         }
287*d57664e9SAndroid Build Coastguard Worker         // Otherwise, stick with N32
288*d57664e9SAndroid Build Coastguard Worker     } else if (extended) {
289*d57664e9SAndroid Build Coastguard Worker         colorType = kRGBA_F16_SkColorType;
290*d57664e9SAndroid Build Coastguard Worker     } else {
291*d57664e9SAndroid Build Coastguard Worker         colorType = decoder->mCodec->computeOutputColorType(colorType);
292*d57664e9SAndroid Build Coastguard Worker     }
293*d57664e9SAndroid Build Coastguard Worker 
294*d57664e9SAndroid Build Coastguard Worker     const bool isHardware = !requireMutable
295*d57664e9SAndroid Build Coastguard Worker         && (allocator == kDefault_Allocator ||
296*d57664e9SAndroid Build Coastguard Worker             allocator == kHardware_Allocator)
297*d57664e9SAndroid Build Coastguard Worker         && colorType != kGray_8_SkColorType;
298*d57664e9SAndroid Build Coastguard Worker 
299*d57664e9SAndroid Build Coastguard Worker     if (colorType == kRGBA_F16_SkColorType && isHardware &&
300*d57664e9SAndroid Build Coastguard Worker             !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
301*d57664e9SAndroid Build Coastguard Worker         colorType = kN32_SkColorType;
302*d57664e9SAndroid Build Coastguard Worker     }
303*d57664e9SAndroid Build Coastguard Worker 
304*d57664e9SAndroid Build Coastguard Worker     // b/276879147, fallback to RGBA_8888 when decoding HEIF and P010 is not supported.
305*d57664e9SAndroid Build Coastguard Worker     if (colorType == kRGBA_1010102_SkColorType &&
306*d57664e9SAndroid Build Coastguard Worker         decoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kHEIF &&
307*d57664e9SAndroid Build Coastguard Worker         env->CallStaticBooleanMethod(gImageDecoder_class,
308*d57664e9SAndroid Build Coastguard Worker                                      gImageDecoder_isP010SupportedForHEVCMethodID) == JNI_FALSE) {
309*d57664e9SAndroid Build Coastguard Worker         colorType = kN32_SkColorType;
310*d57664e9SAndroid Build Coastguard Worker     }
311*d57664e9SAndroid Build Coastguard Worker 
312*d57664e9SAndroid Build Coastguard Worker     if (!decoder->setOutColorType(colorType)) {
313*d57664e9SAndroid Build Coastguard Worker         doThrowISE(env, "Failed to set out color type!");
314*d57664e9SAndroid Build Coastguard Worker         return nullptr;
315*d57664e9SAndroid Build Coastguard Worker     }
316*d57664e9SAndroid Build Coastguard Worker 
317*d57664e9SAndroid Build Coastguard Worker     {
318*d57664e9SAndroid Build Coastguard Worker         sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
319*d57664e9SAndroid Build Coastguard Worker         colorSpace = decoder->mCodec->computeOutputColorSpace(colorType, colorSpace);
320*d57664e9SAndroid Build Coastguard Worker         decoder->setOutColorSpace(std::move(colorSpace));
321*d57664e9SAndroid Build Coastguard Worker     }
322*d57664e9SAndroid Build Coastguard Worker 
323*d57664e9SAndroid Build Coastguard Worker     if (jsubset) {
324*d57664e9SAndroid Build Coastguard Worker         SkIRect subset;
325*d57664e9SAndroid Build Coastguard Worker         GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
326*d57664e9SAndroid Build Coastguard Worker         if (!decoder->setCropRect(&subset)) {
327*d57664e9SAndroid Build Coastguard Worker             doThrowISE(env, "Invalid crop rect!");
328*d57664e9SAndroid Build Coastguard Worker             return nullptr;
329*d57664e9SAndroid Build Coastguard Worker         }
330*d57664e9SAndroid Build Coastguard Worker     }
331*d57664e9SAndroid Build Coastguard Worker 
332*d57664e9SAndroid Build Coastguard Worker     SkImageInfo bitmapInfo = decoder->getOutputInfo();
333*d57664e9SAndroid Build Coastguard Worker     if (asAlphaMask && colorType == kGray_8_SkColorType) {
334*d57664e9SAndroid Build Coastguard Worker         bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType);
335*d57664e9SAndroid Build Coastguard Worker     }
336*d57664e9SAndroid Build Coastguard Worker 
337*d57664e9SAndroid Build Coastguard Worker     SkBitmap bm;
338*d57664e9SAndroid Build Coastguard Worker     if (!bm.setInfo(bitmapInfo)) {
339*d57664e9SAndroid Build Coastguard Worker         doThrowIOE(env, "Failed to setInfo properly");
340*d57664e9SAndroid Build Coastguard Worker         return nullptr;
341*d57664e9SAndroid Build Coastguard Worker     }
342*d57664e9SAndroid Build Coastguard Worker 
343*d57664e9SAndroid Build Coastguard Worker     sk_sp<Bitmap> nativeBitmap;
344*d57664e9SAndroid Build Coastguard Worker     if (allocator == kSharedMemory_Allocator) {
345*d57664e9SAndroid Build Coastguard Worker         nativeBitmap = Bitmap::allocateAshmemBitmap(&bm);
346*d57664e9SAndroid Build Coastguard Worker     } else {
347*d57664e9SAndroid Build Coastguard Worker         nativeBitmap = Bitmap::allocateHeapBitmap(&bm);
348*d57664e9SAndroid Build Coastguard Worker     }
349*d57664e9SAndroid Build Coastguard Worker     if (!nativeBitmap) {
350*d57664e9SAndroid Build Coastguard Worker         SkString msg;
351*d57664e9SAndroid Build Coastguard Worker         msg.printf("OOM allocating Bitmap with dimensions %i x %i",
352*d57664e9SAndroid Build Coastguard Worker                 bitmapInfo.width(), bitmapInfo.height());
353*d57664e9SAndroid Build Coastguard Worker         doThrowOOME(env, msg.c_str());
354*d57664e9SAndroid Build Coastguard Worker         return nullptr;
355*d57664e9SAndroid Build Coastguard Worker     }
356*d57664e9SAndroid Build Coastguard Worker 
357*d57664e9SAndroid Build Coastguard Worker     ATRACE_FORMAT("Decoding %dx%d bitmap", bitmapInfo.width(), bitmapInfo.height());
358*d57664e9SAndroid Build Coastguard Worker     SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes());
359*d57664e9SAndroid Build Coastguard Worker     jthrowable jexception = get_and_clear_exception(env);
360*d57664e9SAndroid Build Coastguard Worker     int onPartialImageError = jexception ? kSourceException : 0;  // No error.
361*d57664e9SAndroid Build Coastguard Worker 
362*d57664e9SAndroid Build Coastguard Worker     // Only attempt to extract the gainmap if we're not post-processing, as we can't automatically
363*d57664e9SAndroid Build Coastguard Worker     // mimic that to the gainmap and expect it to be meaningful. And also don't extract the gainmap
364*d57664e9SAndroid Build Coastguard Worker     // if we're prioritizing RAM over quality, since the gainmap improves quality at the
365*d57664e9SAndroid Build Coastguard Worker     // cost of RAM
366*d57664e9SAndroid Build Coastguard Worker     if (result == SkCodec::kSuccess && !jpostProcess && !preferRamOverQuality) {
367*d57664e9SAndroid Build Coastguard Worker         // The gainmap costs RAM to improve quality, so skip this if we're prioritizing RAM instead
368*d57664e9SAndroid Build Coastguard Worker         result = decoder->extractGainmap(nativeBitmap.get(),
369*d57664e9SAndroid Build Coastguard Worker                                          allocator == kSharedMemory_Allocator ? true : false);
370*d57664e9SAndroid Build Coastguard Worker         jexception = get_and_clear_exception(env);
371*d57664e9SAndroid Build Coastguard Worker     }
372*d57664e9SAndroid Build Coastguard Worker 
373*d57664e9SAndroid Build Coastguard Worker     switch (result) {
374*d57664e9SAndroid Build Coastguard Worker         case SkCodec::kSuccess:
375*d57664e9SAndroid Build Coastguard Worker             // Ignore the exception, since the decode was successful anyway.
376*d57664e9SAndroid Build Coastguard Worker             jexception = nullptr;
377*d57664e9SAndroid Build Coastguard Worker             onPartialImageError = 0;
378*d57664e9SAndroid Build Coastguard Worker             break;
379*d57664e9SAndroid Build Coastguard Worker         case SkCodec::kIncompleteInput:
380*d57664e9SAndroid Build Coastguard Worker             if (!jexception) {
381*d57664e9SAndroid Build Coastguard Worker                 onPartialImageError = kSourceIncomplete;
382*d57664e9SAndroid Build Coastguard Worker             }
383*d57664e9SAndroid Build Coastguard Worker             break;
384*d57664e9SAndroid Build Coastguard Worker         case SkCodec::kErrorInInput:
385*d57664e9SAndroid Build Coastguard Worker             if (!jexception) {
386*d57664e9SAndroid Build Coastguard Worker                 onPartialImageError = kSourceMalformedData;
387*d57664e9SAndroid Build Coastguard Worker             }
388*d57664e9SAndroid Build Coastguard Worker             break;
389*d57664e9SAndroid Build Coastguard Worker         default:
390*d57664e9SAndroid Build Coastguard Worker             SkString msg;
391*d57664e9SAndroid Build Coastguard Worker             msg.printf("getPixels failed with error %s", SkCodec::ResultToString(result));
392*d57664e9SAndroid Build Coastguard Worker             doThrowIOE(env, msg.c_str());
393*d57664e9SAndroid Build Coastguard Worker             return nullptr;
394*d57664e9SAndroid Build Coastguard Worker     }
395*d57664e9SAndroid Build Coastguard Worker 
396*d57664e9SAndroid Build Coastguard Worker     if (onPartialImageError) {
397*d57664e9SAndroid Build Coastguard Worker         env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError,
398*d57664e9SAndroid Build Coastguard Worker                 jexception);
399*d57664e9SAndroid Build Coastguard Worker         if (env->ExceptionCheck()) {
400*d57664e9SAndroid Build Coastguard Worker             return nullptr;
401*d57664e9SAndroid Build Coastguard Worker         }
402*d57664e9SAndroid Build Coastguard Worker     }
403*d57664e9SAndroid Build Coastguard Worker 
404*d57664e9SAndroid Build Coastguard Worker     jbyteArray ninePatchChunk = nullptr;
405*d57664e9SAndroid Build Coastguard Worker     jobject ninePatchInsets = nullptr;
406*d57664e9SAndroid Build Coastguard Worker 
407*d57664e9SAndroid Build Coastguard Worker     // Ignore ninepatch when post-processing.
408*d57664e9SAndroid Build Coastguard Worker     if (!jpostProcess) {
409*d57664e9SAndroid Build Coastguard Worker         // FIXME: Share more code with BitmapFactory.cpp.
410*d57664e9SAndroid Build Coastguard Worker         auto* peeker = reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get());
411*d57664e9SAndroid Build Coastguard Worker         if (peeker->mPatch != nullptr) {
412*d57664e9SAndroid Build Coastguard Worker             size_t ninePatchArraySize = peeker->mPatch->serializedSize();
413*d57664e9SAndroid Build Coastguard Worker             ninePatchChunk = env->NewByteArray(ninePatchArraySize);
414*d57664e9SAndroid Build Coastguard Worker             if (ninePatchChunk == nullptr) {
415*d57664e9SAndroid Build Coastguard Worker                 doThrowOOME(env, "Failed to allocate nine patch chunk.");
416*d57664e9SAndroid Build Coastguard Worker                 return nullptr;
417*d57664e9SAndroid Build Coastguard Worker             }
418*d57664e9SAndroid Build Coastguard Worker 
419*d57664e9SAndroid Build Coastguard Worker             env->SetByteArrayRegion(ninePatchChunk, 0, peeker->mPatchSize,
420*d57664e9SAndroid Build Coastguard Worker                                     reinterpret_cast<jbyte*>(peeker->mPatch));
421*d57664e9SAndroid Build Coastguard Worker         }
422*d57664e9SAndroid Build Coastguard Worker 
423*d57664e9SAndroid Build Coastguard Worker         if (peeker->mHasInsets) {
424*d57664e9SAndroid Build Coastguard Worker             ninePatchInsets = peeker->createNinePatchInsets(env, 1.0f);
425*d57664e9SAndroid Build Coastguard Worker             if (ninePatchInsets == nullptr) {
426*d57664e9SAndroid Build Coastguard Worker                 doThrowOOME(env, "Failed to allocate nine patch insets.");
427*d57664e9SAndroid Build Coastguard Worker                 return nullptr;
428*d57664e9SAndroid Build Coastguard Worker             }
429*d57664e9SAndroid Build Coastguard Worker         }
430*d57664e9SAndroid Build Coastguard Worker     }
431*d57664e9SAndroid Build Coastguard Worker 
432*d57664e9SAndroid Build Coastguard Worker     if (jpostProcess) {
433*d57664e9SAndroid Build Coastguard Worker         std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
434*d57664e9SAndroid Build Coastguard Worker 
435*d57664e9SAndroid Build Coastguard Worker         jint pixelFormat = postProcessAndRelease(env, jdecoder, std::move(canvas));
436*d57664e9SAndroid Build Coastguard Worker         if (env->ExceptionCheck()) {
437*d57664e9SAndroid Build Coastguard Worker             return nullptr;
438*d57664e9SAndroid Build Coastguard Worker         }
439*d57664e9SAndroid Build Coastguard Worker 
440*d57664e9SAndroid Build Coastguard Worker         SkAlphaType newAlphaType = bm.alphaType();
441*d57664e9SAndroid Build Coastguard Worker         switch (pixelFormat) {
442*d57664e9SAndroid Build Coastguard Worker             case kUnknown:
443*d57664e9SAndroid Build Coastguard Worker                 break;
444*d57664e9SAndroid Build Coastguard Worker             case kTranslucent:
445*d57664e9SAndroid Build Coastguard Worker                 newAlphaType = kPremul_SkAlphaType;
446*d57664e9SAndroid Build Coastguard Worker                 break;
447*d57664e9SAndroid Build Coastguard Worker             case kOpaque:
448*d57664e9SAndroid Build Coastguard Worker                 newAlphaType = kOpaque_SkAlphaType;
449*d57664e9SAndroid Build Coastguard Worker                 break;
450*d57664e9SAndroid Build Coastguard Worker             default:
451*d57664e9SAndroid Build Coastguard Worker                 SkString msg;
452*d57664e9SAndroid Build Coastguard Worker                 msg.printf("invalid return from postProcess: %i", pixelFormat);
453*d57664e9SAndroid Build Coastguard Worker                 doThrowIAE(env, msg.c_str());
454*d57664e9SAndroid Build Coastguard Worker                 return nullptr;
455*d57664e9SAndroid Build Coastguard Worker         }
456*d57664e9SAndroid Build Coastguard Worker 
457*d57664e9SAndroid Build Coastguard Worker         if (newAlphaType != bm.alphaType()) {
458*d57664e9SAndroid Build Coastguard Worker             if (!bm.setAlphaType(newAlphaType)) {
459*d57664e9SAndroid Build Coastguard Worker                 SkString msg;
460*d57664e9SAndroid Build Coastguard Worker                 msg.printf("incompatible return from postProcess: %i", pixelFormat);
461*d57664e9SAndroid Build Coastguard Worker                 doThrowIAE(env, msg.c_str());
462*d57664e9SAndroid Build Coastguard Worker                 return nullptr;
463*d57664e9SAndroid Build Coastguard Worker             }
464*d57664e9SAndroid Build Coastguard Worker             nativeBitmap->setAlphaType(newAlphaType);
465*d57664e9SAndroid Build Coastguard Worker         }
466*d57664e9SAndroid Build Coastguard Worker     }
467*d57664e9SAndroid Build Coastguard Worker 
468*d57664e9SAndroid Build Coastguard Worker     int bitmapCreateFlags = 0x0;
469*d57664e9SAndroid Build Coastguard Worker     if (!requireUnpremul) {
470*d57664e9SAndroid Build Coastguard Worker         // Even if the image is opaque, setting this flag means that
471*d57664e9SAndroid Build Coastguard Worker         // if alpha is added (e.g. by PostProcess), it will be marked as
472*d57664e9SAndroid Build Coastguard Worker         // premultiplied.
473*d57664e9SAndroid Build Coastguard Worker         bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Premultiplied;
474*d57664e9SAndroid Build Coastguard Worker     }
475*d57664e9SAndroid Build Coastguard Worker 
476*d57664e9SAndroid Build Coastguard Worker     if (requireMutable) {
477*d57664e9SAndroid Build Coastguard Worker         bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Mutable;
478*d57664e9SAndroid Build Coastguard Worker     } else {
479*d57664e9SAndroid Build Coastguard Worker         if (isHardware) {
480*d57664e9SAndroid Build Coastguard Worker             sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm);
481*d57664e9SAndroid Build Coastguard Worker             if (hwBitmap) {
482*d57664e9SAndroid Build Coastguard Worker                 hwBitmap->setImmutable();
483*d57664e9SAndroid Build Coastguard Worker                 if (nativeBitmap->hasGainmap()) {
484*d57664e9SAndroid Build Coastguard Worker                     auto gm = uirenderer::Gainmap::allocateHardwareGainmap(nativeBitmap->gainmap());
485*d57664e9SAndroid Build Coastguard Worker                     if (gm) {
486*d57664e9SAndroid Build Coastguard Worker                         hwBitmap->setGainmap(std::move(gm));
487*d57664e9SAndroid Build Coastguard Worker                     }
488*d57664e9SAndroid Build Coastguard Worker                 }
489*d57664e9SAndroid Build Coastguard Worker                 uirenderer::logBitmapDecode(*hwBitmap);
490*d57664e9SAndroid Build Coastguard Worker                 return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
491*d57664e9SAndroid Build Coastguard Worker                                             ninePatchChunk, ninePatchInsets);
492*d57664e9SAndroid Build Coastguard Worker             }
493*d57664e9SAndroid Build Coastguard Worker             if (allocator == kHardware_Allocator) {
494*d57664e9SAndroid Build Coastguard Worker                 doThrowOOME(env, "failed to allocate hardware Bitmap!");
495*d57664e9SAndroid Build Coastguard Worker                 return nullptr;
496*d57664e9SAndroid Build Coastguard Worker             }
497*d57664e9SAndroid Build Coastguard Worker             // If we failed to create a hardware bitmap, go ahead and create a
498*d57664e9SAndroid Build Coastguard Worker             // software one.
499*d57664e9SAndroid Build Coastguard Worker         }
500*d57664e9SAndroid Build Coastguard Worker 
501*d57664e9SAndroid Build Coastguard Worker         nativeBitmap->setImmutable();
502*d57664e9SAndroid Build Coastguard Worker     }
503*d57664e9SAndroid Build Coastguard Worker 
504*d57664e9SAndroid Build Coastguard Worker     uirenderer::logBitmapDecode(*nativeBitmap);
505*d57664e9SAndroid Build Coastguard Worker     return bitmap::createBitmap(env, nativeBitmap.release(), bitmapCreateFlags, ninePatchChunk,
506*d57664e9SAndroid Build Coastguard Worker                                 ninePatchInsets);
507*d57664e9SAndroid Build Coastguard Worker }
508*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nGetSampledSize(JNIEnv * env,jobject,jlong nativePtr,jint sampleSize)509*d57664e9SAndroid Build Coastguard Worker static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
510*d57664e9SAndroid Build Coastguard Worker                                             jint sampleSize) {
511*d57664e9SAndroid Build Coastguard Worker     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
512*d57664e9SAndroid Build Coastguard Worker     SkISize size = decoder->getSampledDimensions(sampleSize);
513*d57664e9SAndroid Build Coastguard Worker     return env->NewObject(gSize_class, gSize_constructorMethodID, size.width(), size.height());
514*d57664e9SAndroid Build Coastguard Worker }
515*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nGetPadding(JNIEnv * env,jobject,jlong nativePtr,jobject outPadding)516*d57664e9SAndroid Build Coastguard Worker static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
517*d57664e9SAndroid Build Coastguard Worker                                      jobject outPadding) {
518*d57664e9SAndroid Build Coastguard Worker     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
519*d57664e9SAndroid Build Coastguard Worker     reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get())->getPadding(env, outPadding);
520*d57664e9SAndroid Build Coastguard Worker }
521*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nClose(JNIEnv *,jobject,jlong nativePtr)522*d57664e9SAndroid Build Coastguard Worker static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) {
523*d57664e9SAndroid Build Coastguard Worker     delete reinterpret_cast<ImageDecoder*>(nativePtr);
524*d57664e9SAndroid Build Coastguard Worker }
525*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nGetMimeType(JNIEnv * env,jobject,jlong nativePtr)526*d57664e9SAndroid Build Coastguard Worker static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
527*d57664e9SAndroid Build Coastguard Worker     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
528*d57664e9SAndroid Build Coastguard Worker     return getMimeTypeAsJavaString(env, decoder->mCodec->getEncodedFormat());
529*d57664e9SAndroid Build Coastguard Worker }
530*d57664e9SAndroid Build Coastguard Worker 
ImageDecoder_nGetColorSpace(JNIEnv * env,jobject,jlong nativePtr)531*d57664e9SAndroid Build Coastguard Worker static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
532*d57664e9SAndroid Build Coastguard Worker     auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get();
533*d57664e9SAndroid Build Coastguard Worker     auto colorType = codec->computeOutputColorType(kN32_SkColorType);
534*d57664e9SAndroid Build Coastguard Worker     sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
535*d57664e9SAndroid Build Coastguard Worker     return GraphicsJNI::getColorSpace(env, colorSpace.get(), colorType);
536*d57664e9SAndroid Build Coastguard Worker }
537*d57664e9SAndroid Build Coastguard Worker 
538*d57664e9SAndroid Build Coastguard Worker static const JNINativeMethod gImageDecoderMethods[] = {
539*d57664e9SAndroid Build Coastguard Worker     { "nCreate",        "(JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
540*d57664e9SAndroid Build Coastguard Worker     { "nCreate",        "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
541*d57664e9SAndroid Build Coastguard Worker     { "nCreate",        "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
542*d57664e9SAndroid Build Coastguard Worker     { "nCreate",        "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
543*d57664e9SAndroid Build Coastguard Worker     { "nCreate",        "(Ljava/io/FileDescriptor;JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
544*d57664e9SAndroid Build Coastguard Worker     { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;",
545*d57664e9SAndroid Build Coastguard Worker                                                                  (void*) ImageDecoder_nDecodeBitmap },
546*d57664e9SAndroid Build Coastguard Worker     { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
547*d57664e9SAndroid Build Coastguard Worker     { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
548*d57664e9SAndroid Build Coastguard Worker     { "nClose",         "(J)V",                                  (void*) ImageDecoder_nClose},
549*d57664e9SAndroid Build Coastguard Worker     { "nGetMimeType",   "(J)Ljava/lang/String;",                 (void*) ImageDecoder_nGetMimeType },
550*d57664e9SAndroid Build Coastguard Worker     { "nGetColorSpace", "(J)Landroid/graphics/ColorSpace;",      (void*) ImageDecoder_nGetColorSpace },
551*d57664e9SAndroid Build Coastguard Worker };
552*d57664e9SAndroid Build Coastguard Worker 
register_android_graphics_ImageDecoder(JNIEnv * env)553*d57664e9SAndroid Build Coastguard Worker int register_android_graphics_ImageDecoder(JNIEnv* env) {
554*d57664e9SAndroid Build Coastguard Worker     gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
555*d57664e9SAndroid Build Coastguard Worker     gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZZ)V");
556*d57664e9SAndroid Build Coastguard Worker     gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I");
557*d57664e9SAndroid Build Coastguard Worker     gImageDecoder_isP010SupportedForHEVCMethodID =
558*d57664e9SAndroid Build Coastguard Worker             GetStaticMethodIDOrDie(env, gImageDecoder_class, "isP010SupportedForHEVC", "()Z");
559*d57664e9SAndroid Build Coastguard Worker 
560*d57664e9SAndroid Build Coastguard Worker     gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
561*d57664e9SAndroid Build Coastguard Worker     gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V");
562*d57664e9SAndroid Build Coastguard Worker 
563*d57664e9SAndroid Build Coastguard Worker     gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException"));
564*d57664e9SAndroid Build Coastguard Worker     gDecodeException_constructorMethodID = GetMethodIDOrDie(env, gDecodeException_class, "<init>", "(ILjava/lang/String;Ljava/lang/Throwable;Landroid/graphics/ImageDecoder$Source;)V");
565*d57664e9SAndroid Build Coastguard Worker 
566*d57664e9SAndroid Build Coastguard Worker     gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(ILjava/lang/Throwable;)V");
567*d57664e9SAndroid Build Coastguard Worker 
568*d57664e9SAndroid Build Coastguard Worker     gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
569*d57664e9SAndroid Build Coastguard Worker     gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
570*d57664e9SAndroid Build Coastguard Worker     gCanvas_releaseMethodID = GetMethodIDOrDie(env, gCanvas_class, "release", "()V");
571*d57664e9SAndroid Build Coastguard Worker 
572*d57664e9SAndroid Build Coastguard Worker     return android::RegisterMethodsOrDie(env, "android/graphics/ImageDecoder", gImageDecoderMethods,
573*d57664e9SAndroid Build Coastguard Worker                                          NELEM(gImageDecoderMethods));
574*d57664e9SAndroid Build Coastguard Worker }
575