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