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