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