1 /*
2 * Copyright (C) 2023 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 <Gainmap.h>
18
19 #include "SkColorType.h"
20 #include "SkGainmapInfo.h"
21
22 #ifdef __ANDROID__
23 #include <binder/Parcel.h>
24 #endif
25
26 #include "Bitmap.h"
27 #include "GraphicsJNI.h"
28 #include "ScopedParcel.h"
29 #include "graphics_jni_helpers.h"
30
31 namespace android {
32
33 static jclass gGainmap_class;
34 static jmethodID gGainmap_constructorMethodID;
35
36 using namespace uirenderer;
37
fromJava(jlong gainmap)38 static Gainmap* fromJava(jlong gainmap) {
39 return reinterpret_cast<Gainmap*>(gainmap);
40 }
41
baseImageTypeFromJava(jint direction)42 static SkGainmapInfo::BaseImageType baseImageTypeFromJava(jint direction) {
43 switch (direction) {
44 case 0:
45 return SkGainmapInfo::BaseImageType::kSDR;
46 case 1:
47 return SkGainmapInfo::BaseImageType::kHDR;
48 default:
49 LOG_ALWAYS_FATAL("Unrecognized Gainmap direction: %d", direction);
50 }
51 }
52
baseImageTypeToJava(SkGainmapInfo::BaseImageType type)53 static jint baseImageTypeToJava(SkGainmapInfo::BaseImageType type) {
54 switch (type) {
55 case SkGainmapInfo::BaseImageType::kSDR:
56 return 0;
57 case SkGainmapInfo::BaseImageType::kHDR:
58 return 1;
59 default:
60 LOG_ALWAYS_FATAL("Unrecognized base image: %d", type);
61 }
62 }
63
getCreateFlags(const sk_sp<Bitmap> & bitmap)64 static int getCreateFlags(const sk_sp<Bitmap>& bitmap) {
65 int flags = 0;
66 if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
67 flags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
68 }
69 if (!bitmap->isImmutable()) {
70 flags |= android::bitmap::kBitmapCreateFlag_Mutable;
71 }
72 return flags;
73 }
74
Gainmap_extractFromBitmap(JNIEnv * env,const Bitmap & bitmap)75 jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap) {
76 auto gainmap = bitmap.gainmap();
77 jobject jGainmapImage;
78
79 {
80 // Scope to guard the release of nativeBitmap
81 auto nativeBitmap = gainmap->bitmap;
82 const int createFlags = getCreateFlags(nativeBitmap);
83 jGainmapImage = bitmap::createBitmap(env, nativeBitmap.release(), createFlags);
84 }
85
86 // Grab a ref for the jobject
87 gainmap->incStrong(0);
88 jobject obj = env->NewObject(gGainmap_class, gGainmap_constructorMethodID, jGainmapImage,
89 gainmap.get());
90
91 if (env->ExceptionCheck() != 0) {
92 // sadtrombone
93 gainmap->decStrong(0);
94 ALOGE("*** Uncaught exception returned from Java call!\n");
95 env->ExceptionDescribe();
96 }
97 return obj;
98 }
99
Gainmap_destructor(Gainmap * gainmap)100 static void Gainmap_destructor(Gainmap* gainmap) {
101 gainmap->decStrong(0);
102 }
103
Gainmap_getNativeFinalizer(JNIEnv *,jobject)104 static jlong Gainmap_getNativeFinalizer(JNIEnv*, jobject) {
105 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Gainmap_destructor));
106 }
107
Gainmap_createEmpty(JNIEnv *,jobject)108 jlong Gainmap_createEmpty(JNIEnv*, jobject) {
109 Gainmap* gainmap = new Gainmap();
110 gainmap->incStrong(0);
111 return static_cast<jlong>(reinterpret_cast<uintptr_t>(gainmap));
112 }
113
Gainmap_createCopy(JNIEnv *,jobject,jlong sourcePtr)114 jlong Gainmap_createCopy(JNIEnv*, jobject, jlong sourcePtr) {
115 Gainmap* gainmap = new Gainmap();
116 gainmap->incStrong(0);
117 if (sourcePtr) {
118 Gainmap* src = fromJava(sourcePtr);
119 gainmap->info = src->info;
120 }
121 return static_cast<jlong>(reinterpret_cast<uintptr_t>(gainmap));
122 }
123
Gainmap_setBitmap(JNIEnv * env,jobject,jlong gainmapPtr,jobject jBitmap)124 static void Gainmap_setBitmap(JNIEnv* env, jobject, jlong gainmapPtr, jobject jBitmap) {
125 android::Bitmap* bitmap = GraphicsJNI::getNativeBitmap(env, jBitmap);
126 fromJava(gainmapPtr)->bitmap = sk_ref_sp(bitmap);
127 }
128
Gainmap_setRatioMin(JNIEnv *,jobject,jlong gainmapPtr,jfloat r,jfloat g,jfloat b)129 static void Gainmap_setRatioMin(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g, jfloat b) {
130 fromJava(gainmapPtr)->info.fGainmapRatioMin = {r, g, b, 1.f};
131 }
132
Gainmap_getRatioMin(JNIEnv * env,jobject,jlong gainmapPtr,jfloatArray components)133 static void Gainmap_getRatioMin(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
134 const auto value = fromJava(gainmapPtr)->info.fGainmapRatioMin;
135 jfloat buf[3]{value.fR, value.fG, value.fB};
136 env->SetFloatArrayRegion(components, 0, 3, buf);
137 }
138
Gainmap_setRatioMax(JNIEnv *,jobject,jlong gainmapPtr,jfloat r,jfloat g,jfloat b)139 static void Gainmap_setRatioMax(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g, jfloat b) {
140 fromJava(gainmapPtr)->info.fGainmapRatioMax = {r, g, b, 1.f};
141 }
142
Gainmap_getRatioMax(JNIEnv * env,jobject,jlong gainmapPtr,jfloatArray components)143 static void Gainmap_getRatioMax(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
144 const auto value = fromJava(gainmapPtr)->info.fGainmapRatioMax;
145 jfloat buf[3]{value.fR, value.fG, value.fB};
146 env->SetFloatArrayRegion(components, 0, 3, buf);
147 }
148
Gainmap_setGamma(JNIEnv *,jobject,jlong gainmapPtr,jfloat r,jfloat g,jfloat b)149 static void Gainmap_setGamma(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g, jfloat b) {
150 fromJava(gainmapPtr)->info.fGainmapGamma = {r, g, b, 1.f};
151 }
152
Gainmap_getGamma(JNIEnv * env,jobject,jlong gainmapPtr,jfloatArray components)153 static void Gainmap_getGamma(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
154 const auto value = fromJava(gainmapPtr)->info.fGainmapGamma;
155 jfloat buf[3]{value.fR, value.fG, value.fB};
156 env->SetFloatArrayRegion(components, 0, 3, buf);
157 }
158
Gainmap_setEpsilonSdr(JNIEnv *,jobject,jlong gainmapPtr,jfloat r,jfloat g,jfloat b)159 static void Gainmap_setEpsilonSdr(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g,
160 jfloat b) {
161 fromJava(gainmapPtr)->info.fEpsilonSdr = {r, g, b, 1.f};
162 }
163
Gainmap_getEpsilonSdr(JNIEnv * env,jobject,jlong gainmapPtr,jfloatArray components)164 static void Gainmap_getEpsilonSdr(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
165 const auto value = fromJava(gainmapPtr)->info.fEpsilonSdr;
166 jfloat buf[3]{value.fR, value.fG, value.fB};
167 env->SetFloatArrayRegion(components, 0, 3, buf);
168 }
169
Gainmap_setEpsilonHdr(JNIEnv *,jobject,jlong gainmapPtr,jfloat r,jfloat g,jfloat b)170 static void Gainmap_setEpsilonHdr(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g,
171 jfloat b) {
172 fromJava(gainmapPtr)->info.fEpsilonHdr = {r, g, b, 1.f};
173 }
174
Gainmap_getEpsilonHdr(JNIEnv * env,jobject,jlong gainmapPtr,jfloatArray components)175 static void Gainmap_getEpsilonHdr(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
176 const auto value = fromJava(gainmapPtr)->info.fEpsilonHdr;
177 jfloat buf[3]{value.fR, value.fG, value.fB};
178 env->SetFloatArrayRegion(components, 0, 3, buf);
179 }
180
Gainmap_setDisplayRatioHdr(JNIEnv *,jobject,jlong gainmapPtr,jfloat max)181 static void Gainmap_setDisplayRatioHdr(JNIEnv*, jobject, jlong gainmapPtr, jfloat max) {
182 fromJava(gainmapPtr)->info.fDisplayRatioHdr = max;
183 }
184
Gainmap_getDisplayRatioHdr(JNIEnv *,jobject,jlong gainmapPtr)185 static jfloat Gainmap_getDisplayRatioHdr(JNIEnv*, jobject, jlong gainmapPtr) {
186 return fromJava(gainmapPtr)->info.fDisplayRatioHdr;
187 }
188
Gainmap_setDisplayRatioSdr(JNIEnv *,jobject,jlong gainmapPtr,jfloat min)189 static void Gainmap_setDisplayRatioSdr(JNIEnv*, jobject, jlong gainmapPtr, jfloat min) {
190 fromJava(gainmapPtr)->info.fDisplayRatioSdr = min;
191 }
192
Gainmap_getDisplayRatioSdr(JNIEnv *,jobject,jlong gainmapPtr)193 static jfloat Gainmap_getDisplayRatioSdr(JNIEnv*, jobject, jlong gainmapPtr) {
194 return fromJava(gainmapPtr)->info.fDisplayRatioSdr;
195 }
196
Gainmap_setAlternativeColorSpace(JNIEnv *,jobject,jlong gainmapPtr,jlong colorSpacePtr)197 static void Gainmap_setAlternativeColorSpace(JNIEnv*, jobject, jlong gainmapPtr,
198 jlong colorSpacePtr) {
199 auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr);
200 fromJava(gainmapPtr)->info.fGainmapMathColorSpace = colorSpace;
201 }
202
Gainmap_getAlternativeColorSpace(JNIEnv * env,jobject,jlong gainmapPtr)203 static jobject Gainmap_getAlternativeColorSpace(JNIEnv* env, jobject, jlong gainmapPtr) {
204 const auto javaGainmap = fromJava(gainmapPtr);
205 auto colorSpace = javaGainmap->info.fGainmapMathColorSpace.get();
206 if (colorSpace == nullptr) {
207 return nullptr;
208 }
209
210 auto colorType = javaGainmap->bitmap->colorType();
211 // A8 bitmaps don't support colorspaces, but an alternative colorspace is
212 // still valid for configuring the gainmap math, so use RGBA8888 instead.
213 if (colorType == kAlpha_8_SkColorType) {
214 colorType = kRGBA_8888_SkColorType;
215 }
216 return GraphicsJNI::getColorSpace(env, colorSpace, colorType);
217 }
218
Gainmap_setDirection(JNIEnv *,jobject,jlong gainmapPtr,jint direction)219 static void Gainmap_setDirection(JNIEnv*, jobject, jlong gainmapPtr, jint direction) {
220 fromJava(gainmapPtr)->info.fBaseImageType = baseImageTypeFromJava(direction);
221 }
222
Gainmap_getDirection(JNIEnv * env,jobject,jlong gainmapPtr)223 static jint Gainmap_getDirection(JNIEnv* env, jobject, jlong gainmapPtr) {
224 return baseImageTypeToJava(fromJava(gainmapPtr)->info.fBaseImageType);
225 }
226
227 // ----------------------------------------------------------------------------
228 // Serialization
229 // ----------------------------------------------------------------------------
230
Gainmap_writeToParcel(JNIEnv * env,jobject,jlong nativeObject,jobject parcel)231 static void Gainmap_writeToParcel(JNIEnv* env, jobject, jlong nativeObject, jobject parcel) {
232 #ifdef __ANDROID__ // Layoutlib does not support parcel
233 if (parcel == NULL) {
234 ALOGD("write null parcel\n");
235 return;
236 }
237 ScopedParcel p(env, parcel);
238 SkGainmapInfo info = fromJava(nativeObject)->info;
239 // write gainmap to parcel
240 // ratio min
241 p.writeFloat(info.fGainmapRatioMin.fR);
242 p.writeFloat(info.fGainmapRatioMin.fG);
243 p.writeFloat(info.fGainmapRatioMin.fB);
244 // ratio max
245 p.writeFloat(info.fGainmapRatioMax.fR);
246 p.writeFloat(info.fGainmapRatioMax.fG);
247 p.writeFloat(info.fGainmapRatioMax.fB);
248 // gamma
249 p.writeFloat(info.fGainmapGamma.fR);
250 p.writeFloat(info.fGainmapGamma.fG);
251 p.writeFloat(info.fGainmapGamma.fB);
252 // epsilonsdr
253 p.writeFloat(info.fEpsilonSdr.fR);
254 p.writeFloat(info.fEpsilonSdr.fG);
255 p.writeFloat(info.fEpsilonSdr.fB);
256 // epsilonhdr
257 p.writeFloat(info.fEpsilonHdr.fR);
258 p.writeFloat(info.fEpsilonHdr.fG);
259 p.writeFloat(info.fEpsilonHdr.fB);
260 // display ratio sdr
261 p.writeFloat(info.fDisplayRatioSdr);
262 // display ratio hdr
263 p.writeFloat(info.fDisplayRatioHdr);
264 // base image type
265 p.writeInt32(static_cast<int32_t>(info.fBaseImageType));
266 #else
267 doThrowRE(env, "Cannot use parcels outside of Android!");
268 #endif
269 }
270
Gainmap_readFromParcel(JNIEnv * env,jobject,jlong nativeObject,jobject parcel)271 static void Gainmap_readFromParcel(JNIEnv* env, jobject, jlong nativeObject, jobject parcel) {
272 #ifdef __ANDROID__ // Layoutlib does not support parcel
273 if (parcel == NULL) {
274 jniThrowNullPointerException(env, "parcel cannot be null");
275 return;
276 }
277 ScopedParcel p(env, parcel);
278
279 SkGainmapInfo info;
280 info.fGainmapRatioMin = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
281 info.fGainmapRatioMax = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
282 info.fGainmapGamma = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
283 info.fEpsilonSdr = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
284 info.fEpsilonHdr = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
285 info.fDisplayRatioSdr = p.readFloat();
286 info.fDisplayRatioHdr = p.readFloat();
287 info.fBaseImageType = static_cast<SkGainmapInfo::BaseImageType>(p.readInt32());
288
289 fromJava(nativeObject)->info = info;
290 #else
291 jniThrowRuntimeException(env, "Cannot use parcels outside of Android");
292 #endif
293 }
294
295 // ----------------------------------------------------------------------------
296 // JNI Glue
297 // ----------------------------------------------------------------------------
298
299 static const JNINativeMethod gGainmapMethods[] = {
300 {"nGetFinalizer", "()J", (void*)Gainmap_getNativeFinalizer},
301 {"nCreateEmpty", "()J", (void*)Gainmap_createEmpty},
302 {"nCreateCopy", "(J)J", (void*)Gainmap_createCopy},
303 {"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*)Gainmap_setBitmap},
304 {"nSetRatioMin", "(JFFF)V", (void*)Gainmap_setRatioMin},
305 {"nGetRatioMin", "(J[F)V", (void*)Gainmap_getRatioMin},
306 {"nSetRatioMax", "(JFFF)V", (void*)Gainmap_setRatioMax},
307 {"nGetRatioMax", "(J[F)V", (void*)Gainmap_getRatioMax},
308 {"nSetGamma", "(JFFF)V", (void*)Gainmap_setGamma},
309 {"nGetGamma", "(J[F)V", (void*)Gainmap_getGamma},
310 {"nSetEpsilonSdr", "(JFFF)V", (void*)Gainmap_setEpsilonSdr},
311 {"nGetEpsilonSdr", "(J[F)V", (void*)Gainmap_getEpsilonSdr},
312 {"nSetEpsilonHdr", "(JFFF)V", (void*)Gainmap_setEpsilonHdr},
313 {"nGetEpsilonHdr", "(J[F)V", (void*)Gainmap_getEpsilonHdr},
314 {"nSetDisplayRatioHdr", "(JF)V", (void*)Gainmap_setDisplayRatioHdr},
315 {"nGetDisplayRatioHdr", "(J)F", (void*)Gainmap_getDisplayRatioHdr},
316 {"nSetDisplayRatioSdr", "(JF)V", (void*)Gainmap_setDisplayRatioSdr},
317 {"nGetDisplayRatioSdr", "(J)F", (void*)Gainmap_getDisplayRatioSdr},
318 {"nSetAlternativeColorSpace", "(JJ)V", (void*)Gainmap_setAlternativeColorSpace},
319 {"nGetAlternativeColorSpace", "(J)Landroid/graphics/ColorSpace;",
320 (void*)Gainmap_getAlternativeColorSpace},
321 {"nSetDirection", "(JI)V", (void*)Gainmap_setDirection},
322 {"nGetDirection", "(J)I", (void*)Gainmap_getDirection},
323 {"nWriteGainmapToParcel", "(JLandroid/os/Parcel;)V", (void*)Gainmap_writeToParcel},
324 {"nReadGainmapFromParcel", "(JLandroid/os/Parcel;)V", (void*)Gainmap_readFromParcel},
325 };
326
register_android_graphics_Gainmap(JNIEnv * env)327 int register_android_graphics_Gainmap(JNIEnv* env) {
328 gGainmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Gainmap"));
329 gGainmap_constructorMethodID =
330 GetMethodIDOrDie(env, gGainmap_class, "<init>", "(Landroid/graphics/Bitmap;J)V");
331 return android::RegisterMethodsOrDie(env, "android/graphics/Gainmap", gGainmapMethods,
332 NELEM(gGainmapMethods));
333 }
334
335 } // namespace android
336