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