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