1 /*
2 * Copyright (C) 2019 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 "GraphicsJNI.h"
18
19 #include "SkColor.h"
20 #include "SkColorSpace.h"
21
22 using namespace android;
23
getNativeXYZMatrix(JNIEnv * env,jfloatArray xyzD50)24 static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
25 skcms_Matrix3x3 xyzMatrix;
26 jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
27 xyzMatrix.vals[0][0] = array[0];
28 xyzMatrix.vals[1][0] = array[1];
29 xyzMatrix.vals[2][0] = array[2];
30 xyzMatrix.vals[0][1] = array[3];
31 xyzMatrix.vals[1][1] = array[4];
32 xyzMatrix.vals[2][1] = array[5];
33 xyzMatrix.vals[0][2] = array[6];
34 xyzMatrix.vals[1][2] = array[7];
35 xyzMatrix.vals[2][2] = array[8];
36 env->ReleaseFloatArrayElements(xyzD50, array, 0);
37 return xyzMatrix;
38 }
39
40 ///////////////////////////////////////////////////////////////////////////////
41
42 #if defined(__ANDROID__) // __fp16 is not defined on non-Android builds
halfToFloat(uint16_t bits)43 static float halfToFloat(uint16_t bits) {
44 __fp16 h;
45 memcpy(&h, &bits, 2);
46 return (float)h;
47 }
48 #else
49 // This is Skia's implementation of SkHalfToFloat, which is
50 // based on Fabien Giesen's half_to_float_fast2()
51 // see https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
halfMantissa(uint16_t h)52 static uint16_t halfMantissa(uint16_t h) {
53 return h & 0x03ff;
54 }
55
halfExponent(uint16_t h)56 static uint16_t halfExponent(uint16_t h) {
57 return (h >> 10) & 0x001f;
58 }
59
halfSign(uint16_t h)60 static uint16_t halfSign(uint16_t h) {
61 return h >> 15;
62 }
63
64 union FloatUIntUnion {
65 uint32_t mUInt; // this must come first for the initializations below to work
66 float mFloat;
67 };
68
halfToFloat(uint16_t bits)69 static float halfToFloat(uint16_t bits) {
70 static const FloatUIntUnion magic = { 126 << 23 };
71 FloatUIntUnion o;
72
73 if (halfExponent(bits) == 0) {
74 // Zero / Denormal
75 o.mUInt = magic.mUInt + halfMantissa(bits);
76 o.mFloat -= magic.mFloat;
77 } else {
78 // Set mantissa
79 o.mUInt = halfMantissa(bits) << 13;
80 // Set exponent
81 if (halfExponent(bits) == 0x1f) {
82 // Inf/NaN
83 o.mUInt |= (255 << 23);
84 } else {
85 o.mUInt |= ((127 - 15 + halfExponent(bits)) << 23);
86 }
87 }
88
89 // Set sign
90 o.mUInt |= (halfSign(bits) << 31);
91 return o.mFloat;
92 }
93 #endif // defined(__ANDROID__)
94
convertColorLong(jlong color)95 SkColor4f GraphicsJNI::convertColorLong(jlong color) {
96 if ((color & 0x3f) == 0) {
97 // This corresponds to sRGB, which is treated differently than the rest.
98 uint8_t a = color >> 56 & 0xff;
99 uint8_t r = color >> 48 & 0xff;
100 uint8_t g = color >> 40 & 0xff;
101 uint8_t b = color >> 32 & 0xff;
102 SkColor c = SkColorSetARGB(a, r, g, b);
103 return SkColor4f::FromColor(c);
104 }
105
106 // These match the implementation of android.graphics.Color#red(long) etc.
107 float r = halfToFloat((uint16_t)(color >> 48 & 0xffff));
108 float g = halfToFloat((uint16_t)(color >> 32 & 0xffff));
109 float b = halfToFloat((uint16_t)(color >> 16 & 0xffff));
110 float a = (color >> 6 & 0x3ff) / 1023.0f;
111
112 return SkColor4f{r, g, b, a};
113 }
114
getNativeColorSpace(jlong colorSpaceHandle)115 sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(jlong colorSpaceHandle) {
116 if (colorSpaceHandle == 0) return nullptr;
117 return sk_ref_sp(reinterpret_cast<SkColorSpace*>(colorSpaceHandle));
118 }
119
unref_colorSpace(SkColorSpace * cs)120 static void unref_colorSpace(SkColorSpace* cs) {
121 SkSafeUnref(cs);
122 }
123
ColorSpace_getNativeFinalizer(JNIEnv *,jobject)124 static jlong ColorSpace_getNativeFinalizer(JNIEnv*, jobject) {
125 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&unref_colorSpace));
126 }
127
ColorSpace_creator(JNIEnv * env,jobject,jfloat a,jfloat b,jfloat c,jfloat d,jfloat e,jfloat f,jfloat g,jfloatArray xyzD50)128 static jlong ColorSpace_creator(JNIEnv* env, jobject, jfloat a, jfloat b, jfloat c,
129 jfloat d, jfloat e, jfloat f, jfloat g, jfloatArray xyzD50) {
130 skcms_TransferFunction p;
131 p.a = a;
132 p.b = b;
133 p.c = c;
134 p.d = d;
135 p.e = e;
136 p.f = f;
137 p.g = g;
138 skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
139
140 return reinterpret_cast<jlong>(SkColorSpace::MakeRGB(p, xyzMatrix).release());
141 }
142
143 static const JNINativeMethod gColorSpaceRgbMethods[] = {
144 { "nativeGetNativeFinalizer", "()J", (void*)ColorSpace_getNativeFinalizer },
145 { "nativeCreate", "(FFFFFFF[F)J", (void*)ColorSpace_creator }
146 };
147
148 namespace android {
149
register_android_graphics_ColorSpace(JNIEnv * env)150 int register_android_graphics_ColorSpace(JNIEnv* env) {
151 return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb$Native",
152 gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods));
153 }
154
155 }; // namespace android
156