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