blob: 71972d01b94fafb4808bcd6cb4cfe5df9f5bcbe5 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <Gainmap.h>
#include "SkColorType.h"
#include "SkGainmapInfo.h"
#ifdef __ANDROID__
#include <binder/Parcel.h>
#endif
#include "Bitmap.h"
#include "GraphicsJNI.h"
#include "ScopedParcel.h"
#include "graphics_jni_helpers.h"
namespace android {
static jclass gGainmap_class;
static jmethodID gGainmap_constructorMethodID;
using namespace uirenderer;
static Gainmap* fromJava(jlong gainmap) {
return reinterpret_cast<Gainmap*>(gainmap);
}
static SkGainmapInfo::BaseImageType baseImageTypeFromJava(jint direction) {
switch (direction) {
case 0:
return SkGainmapInfo::BaseImageType::kSDR;
case 1:
return SkGainmapInfo::BaseImageType::kHDR;
default:
LOG_ALWAYS_FATAL("Unrecognized Gainmap direction: %d", direction);
}
}
static jint baseImageTypeToJava(SkGainmapInfo::BaseImageType type) {
switch (type) {
case SkGainmapInfo::BaseImageType::kSDR:
return 0;
case SkGainmapInfo::BaseImageType::kHDR:
return 1;
default:
LOG_ALWAYS_FATAL("Unrecognized base image: %d", type);
}
}
static int getCreateFlags(const sk_sp<Bitmap>& bitmap) {
int flags = 0;
if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
flags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
}
if (!bitmap->isImmutable()) {
flags |= android::bitmap::kBitmapCreateFlag_Mutable;
}
return flags;
}
jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap) {
auto gainmap = bitmap.gainmap();
jobject jGainmapImage;
{
// Scope to guard the release of nativeBitmap
auto nativeBitmap = gainmap->bitmap;
const int createFlags = getCreateFlags(nativeBitmap);
jGainmapImage = bitmap::createBitmap(env, nativeBitmap.release(), createFlags);
}
// Grab a ref for the jobject
gainmap->incStrong(0);
jobject obj = env->NewObject(gGainmap_class, gGainmap_constructorMethodID, jGainmapImage,
gainmap.get());
if (env->ExceptionCheck() != 0) {
// sadtrombone
gainmap->decStrong(0);
ALOGE("*** Uncaught exception returned from Java call!\n");
env->ExceptionDescribe();
}
return obj;
}
static void Gainmap_destructor(Gainmap* gainmap) {
gainmap->decStrong(0);
}
static jlong Gainmap_getNativeFinalizer(JNIEnv*, jobject) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Gainmap_destructor));
}
jlong Gainmap_createEmpty(JNIEnv*, jobject) {
Gainmap* gainmap = new Gainmap();
gainmap->incStrong(0);
return static_cast<jlong>(reinterpret_cast<uintptr_t>(gainmap));
}
jlong Gainmap_createCopy(JNIEnv*, jobject, jlong sourcePtr) {
Gainmap* gainmap = new Gainmap();
gainmap->incStrong(0);
if (sourcePtr) {
Gainmap* src = fromJava(sourcePtr);
gainmap->info = src->info;
}
return static_cast<jlong>(reinterpret_cast<uintptr_t>(gainmap));
}
static void Gainmap_setBitmap(JNIEnv* env, jobject, jlong gainmapPtr, jobject jBitmap) {
android::Bitmap* bitmap = GraphicsJNI::getNativeBitmap(env, jBitmap);
fromJava(gainmapPtr)->bitmap = sk_ref_sp(bitmap);
}
static void Gainmap_setRatioMin(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g, jfloat b) {
fromJava(gainmapPtr)->info.fGainmapRatioMin = {r, g, b, 1.f};
}
static void Gainmap_getRatioMin(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
const auto value = fromJava(gainmapPtr)->info.fGainmapRatioMin;
jfloat buf[3]{value.fR, value.fG, value.fB};
env->SetFloatArrayRegion(components, 0, 3, buf);
}
static void Gainmap_setRatioMax(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g, jfloat b) {
fromJava(gainmapPtr)->info.fGainmapRatioMax = {r, g, b, 1.f};
}
static void Gainmap_getRatioMax(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
const auto value = fromJava(gainmapPtr)->info.fGainmapRatioMax;
jfloat buf[3]{value.fR, value.fG, value.fB};
env->SetFloatArrayRegion(components, 0, 3, buf);
}
static void Gainmap_setGamma(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g, jfloat b) {
fromJava(gainmapPtr)->info.fGainmapGamma = {r, g, b, 1.f};
}
static void Gainmap_getGamma(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
const auto value = fromJava(gainmapPtr)->info.fGainmapGamma;
jfloat buf[3]{value.fR, value.fG, value.fB};
env->SetFloatArrayRegion(components, 0, 3, buf);
}
static void Gainmap_setEpsilonSdr(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g,
jfloat b) {
fromJava(gainmapPtr)->info.fEpsilonSdr = {r, g, b, 1.f};
}
static void Gainmap_getEpsilonSdr(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
const auto value = fromJava(gainmapPtr)->info.fEpsilonSdr;
jfloat buf[3]{value.fR, value.fG, value.fB};
env->SetFloatArrayRegion(components, 0, 3, buf);
}
static void Gainmap_setEpsilonHdr(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g,
jfloat b) {
fromJava(gainmapPtr)->info.fEpsilonHdr = {r, g, b, 1.f};
}
static void Gainmap_getEpsilonHdr(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
const auto value = fromJava(gainmapPtr)->info.fEpsilonHdr;
jfloat buf[3]{value.fR, value.fG, value.fB};
env->SetFloatArrayRegion(components, 0, 3, buf);
}
static void Gainmap_setDisplayRatioHdr(JNIEnv*, jobject, jlong gainmapPtr, jfloat max) {
fromJava(gainmapPtr)->info.fDisplayRatioHdr = max;
}
static jfloat Gainmap_getDisplayRatioHdr(JNIEnv*, jobject, jlong gainmapPtr) {
return fromJava(gainmapPtr)->info.fDisplayRatioHdr;
}
static void Gainmap_setDisplayRatioSdr(JNIEnv*, jobject, jlong gainmapPtr, jfloat min) {
fromJava(gainmapPtr)->info.fDisplayRatioSdr = min;
}
static jfloat Gainmap_getDisplayRatioSdr(JNIEnv*, jobject, jlong gainmapPtr) {
return fromJava(gainmapPtr)->info.fDisplayRatioSdr;
}
static void Gainmap_setAlternativeColorSpace(JNIEnv*, jobject, jlong gainmapPtr,
jlong colorSpacePtr) {
auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr);
fromJava(gainmapPtr)->info.fGainmapMathColorSpace = colorSpace;
}
static jobject Gainmap_getAlternativeColorSpace(JNIEnv* env, jobject, jlong gainmapPtr) {
const auto javaGainmap = fromJava(gainmapPtr);
auto colorSpace = javaGainmap->info.fGainmapMathColorSpace.get();
if (colorSpace == nullptr) {
return nullptr;
}
auto colorType = javaGainmap->bitmap->colorType();
// A8 bitmaps don't support colorspaces, but an alternative colorspace is
// still valid for configuring the gainmap math, so use RGBA8888 instead.
if (colorType == kAlpha_8_SkColorType) {
colorType = kRGBA_8888_SkColorType;
}
return GraphicsJNI::getColorSpace(env, colorSpace, colorType);
}
static void Gainmap_setDirection(JNIEnv*, jobject, jlong gainmapPtr, jint direction) {
fromJava(gainmapPtr)->info.fBaseImageType = baseImageTypeFromJava(direction);
}
static jint Gainmap_getDirection(JNIEnv* env, jobject, jlong gainmapPtr) {
return baseImageTypeToJava(fromJava(gainmapPtr)->info.fBaseImageType);
}
// ----------------------------------------------------------------------------
// Serialization
// ----------------------------------------------------------------------------
static void Gainmap_writeToParcel(JNIEnv* env, jobject, jlong nativeObject, jobject parcel) {
#ifdef __ANDROID__ // Layoutlib does not support parcel
if (parcel == NULL) {
ALOGD("write null parcel\n");
return;
}
ScopedParcel p(env, parcel);
SkGainmapInfo info = fromJava(nativeObject)->info;
// write gainmap to parcel
// ratio min
p.writeFloat(info.fGainmapRatioMin.fR);
p.writeFloat(info.fGainmapRatioMin.fG);
p.writeFloat(info.fGainmapRatioMin.fB);
// ratio max
p.writeFloat(info.fGainmapRatioMax.fR);
p.writeFloat(info.fGainmapRatioMax.fG);
p.writeFloat(info.fGainmapRatioMax.fB);
// gamma
p.writeFloat(info.fGainmapGamma.fR);
p.writeFloat(info.fGainmapGamma.fG);
p.writeFloat(info.fGainmapGamma.fB);
// epsilonsdr
p.writeFloat(info.fEpsilonSdr.fR);
p.writeFloat(info.fEpsilonSdr.fG);
p.writeFloat(info.fEpsilonSdr.fB);
// epsilonhdr
p.writeFloat(info.fEpsilonHdr.fR);
p.writeFloat(info.fEpsilonHdr.fG);
p.writeFloat(info.fEpsilonHdr.fB);
// display ratio sdr
p.writeFloat(info.fDisplayRatioSdr);
// display ratio hdr
p.writeFloat(info.fDisplayRatioHdr);
// base image type
p.writeInt32(static_cast<int32_t>(info.fBaseImageType));
#else
doThrowRE(env, "Cannot use parcels outside of Android!");
#endif
}
static void Gainmap_readFromParcel(JNIEnv* env, jobject, jlong nativeObject, jobject parcel) {
#ifdef __ANDROID__ // Layoutlib does not support parcel
if (parcel == NULL) {
jniThrowNullPointerException(env, "parcel cannot be null");
return;
}
ScopedParcel p(env, parcel);
SkGainmapInfo info;
info.fGainmapRatioMin = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
info.fGainmapRatioMax = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
info.fGainmapGamma = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
info.fEpsilonSdr = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
info.fEpsilonHdr = {p.readFloat(), p.readFloat(), p.readFloat(), 1.f};
info.fDisplayRatioSdr = p.readFloat();
info.fDisplayRatioHdr = p.readFloat();
info.fBaseImageType = static_cast<SkGainmapInfo::BaseImageType>(p.readInt32());
fromJava(nativeObject)->info = info;
#else
jniThrowRuntimeException(env, "Cannot use parcels outside of Android");
#endif
}
// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
static const JNINativeMethod gGainmapMethods[] = {
{"nGetFinalizer", "()J", (void*)Gainmap_getNativeFinalizer},
{"nCreateEmpty", "()J", (void*)Gainmap_createEmpty},
{"nCreateCopy", "(J)J", (void*)Gainmap_createCopy},
{"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*)Gainmap_setBitmap},
{"nSetRatioMin", "(JFFF)V", (void*)Gainmap_setRatioMin},
{"nGetRatioMin", "(J[F)V", (void*)Gainmap_getRatioMin},
{"nSetRatioMax", "(JFFF)V", (void*)Gainmap_setRatioMax},
{"nGetRatioMax", "(J[F)V", (void*)Gainmap_getRatioMax},
{"nSetGamma", "(JFFF)V", (void*)Gainmap_setGamma},
{"nGetGamma", "(J[F)V", (void*)Gainmap_getGamma},
{"nSetEpsilonSdr", "(JFFF)V", (void*)Gainmap_setEpsilonSdr},
{"nGetEpsilonSdr", "(J[F)V", (void*)Gainmap_getEpsilonSdr},
{"nSetEpsilonHdr", "(JFFF)V", (void*)Gainmap_setEpsilonHdr},
{"nGetEpsilonHdr", "(J[F)V", (void*)Gainmap_getEpsilonHdr},
{"nSetDisplayRatioHdr", "(JF)V", (void*)Gainmap_setDisplayRatioHdr},
{"nGetDisplayRatioHdr", "(J)F", (void*)Gainmap_getDisplayRatioHdr},
{"nSetDisplayRatioSdr", "(JF)V", (void*)Gainmap_setDisplayRatioSdr},
{"nGetDisplayRatioSdr", "(J)F", (void*)Gainmap_getDisplayRatioSdr},
{"nSetAlternativeColorSpace", "(JJ)V", (void*)Gainmap_setAlternativeColorSpace},
{"nGetAlternativeColorSpace", "(J)Landroid/graphics/ColorSpace;",
(void*)Gainmap_getAlternativeColorSpace},
{"nSetDirection", "(JI)V", (void*)Gainmap_setDirection},
{"nGetDirection", "(J)I", (void*)Gainmap_getDirection},
{"nWriteGainmapToParcel", "(JLandroid/os/Parcel;)V", (void*)Gainmap_writeToParcel},
{"nReadGainmapFromParcel", "(JLandroid/os/Parcel;)V", (void*)Gainmap_readFromParcel},
};
int register_android_graphics_Gainmap(JNIEnv* env) {
gGainmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Gainmap"));
gGainmap_constructorMethodID =
GetMethodIDOrDie(env, gGainmap_class, "<init>", "(Landroid/graphics/Bitmap;J)V");
return android::RegisterMethodsOrDie(env, "android/graphics/Gainmap", gGainmapMethods,
NELEM(gGainmapMethods));
}
} // namespace android