xref: /aosp_15_r20/frameworks/base/libs/hwui/jni/Gainmap.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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