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