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