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