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