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