1#include "GraphicsJNI.h"
2#include "SkGradientShader.h"
3#include "SkShader.h"
4#include "SkXfermode.h"
5#include "core_jni_helpers.h"
6
7#include <Caches.h>
8#include <jni.h>
9
10using namespace android::uirenderer;
11
12static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
13    if (NULL == ptr) {
14        doThrowIAE(env);
15    }
16}
17
18static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
19{
20    SkScalar hsv[3];
21    SkRGBToHSV(red, green, blue, hsv);
22
23    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
24    float* values = autoHSV.ptr();
25    for (int i = 0; i < 3; i++) {
26        values[i] = SkScalarToFloat(hsv[i]);
27    }
28}
29
30static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
31{
32    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
33#ifdef SK_SCALAR_IS_FLOAT
34    SkScalar*   hsv = autoHSV.ptr();
35#else
36    #error Need to convert float array to SkScalar array before calling the following function.
37#endif
38
39    return static_cast<jint>(SkHSVToColor(alpha, hsv));
40}
41
42///////////////////////////////////////////////////////////////////////////////////////////////
43
44static void Shader_destructor(JNIEnv* env, jobject o, jlong shaderHandle, jlong shaderWithLMHandle)
45{
46    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
47    SkSafeUnref(shader);
48}
49
50static jlong Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle, jlong matrixHandle)
51{
52    // ensure we have a valid matrix to use
53    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
54    if (NULL == matrix) {
55        matrix = &SkMatrix::I();
56    }
57
58    // The current shader will no longer need a direct reference owned by Shader.java
59    // as all the data needed is contained within the newly created LocalMatrixShader.
60    SkASSERT(shaderHandle);
61    SkAutoTUnref<SkShader> currentShader(reinterpret_cast<SkShader*>(shaderHandle));
62
63    // Attempt to peel off an existing proxy shader and get the proxy's matrix. If
64    // the proxy existed and it's matrix equals the desired matrix then just return
65    // the proxy, otherwise replace it with a new proxy containing the desired matrix.
66    //
67    // refAsALocalMatrixShader(): if the shader contains a proxy then it unwraps the proxy
68    //                            returning both the underlying shader and the proxy's matrix.
69    // newWithLocalMatrix(): will return a proxy shader that wraps the provided shader and
70    //                       concats the provided local matrix with the shader's matrix.
71    //
72    // WARNING: This proxy replacement only behaves like a setter because the Java
73    //          API enforces that all local matrices are set using this call and
74    //          not passed to the constructor of the Shader.
75    SkMatrix proxyMatrix;
76    SkAutoTUnref<SkShader> baseShader(currentShader->refAsALocalMatrixShader(&proxyMatrix));
77    if (baseShader.get()) {
78        if (proxyMatrix == *matrix) {
79            return reinterpret_cast<jlong>(currentShader.detach());
80        }
81        return reinterpret_cast<jlong>(baseShader->newWithLocalMatrix(*matrix));
82    }
83    return reinterpret_cast<jlong>(currentShader->newWithLocalMatrix(*matrix));
84}
85
86///////////////////////////////////////////////////////////////////////////////////////////////
87
88static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jobject jbitmap,
89                                      jint tileModeX, jint tileModeY)
90{
91    SkBitmap bitmap;
92    if (jbitmap) {
93        // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
94        // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
95        GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
96    }
97    SkShader* s = SkShader::CreateBitmapShader(bitmap,
98                                        (SkShader::TileMode)tileModeX,
99                                        (SkShader::TileMode)tileModeY);
100
101    ThrowIAE_IfNull(env, s);
102    return reinterpret_cast<jlong>(s);
103}
104
105///////////////////////////////////////////////////////////////////////////////////////////////
106
107static jlong LinearGradient_create1(JNIEnv* env, jobject o,
108                                    jfloat x0, jfloat y0, jfloat x1, jfloat y1,
109                                    jintArray colorArray, jfloatArray posArray, jint tileMode)
110{
111    SkPoint pts[2];
112    pts[0].set(x0, y0);
113    pts[1].set(x1, y1);
114
115    size_t count = env->GetArrayLength(colorArray);
116    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
117
118    AutoJavaFloatArray autoPos(env, posArray, count);
119#ifdef SK_SCALAR_IS_FLOAT
120    SkScalar* pos = autoPos.ptr();
121#else
122    #error Need to convert float array to SkScalar array before calling the following function.
123#endif
124
125    SkShader* shader = SkGradientShader::CreateLinear(pts,
126            reinterpret_cast<const SkColor*>(colorValues), pos, count,
127            static_cast<SkShader::TileMode>(tileMode));
128
129    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
130    ThrowIAE_IfNull(env, shader);
131    return reinterpret_cast<jlong>(shader);
132}
133
134static jlong LinearGradient_create2(JNIEnv* env, jobject o,
135                                    jfloat x0, jfloat y0, jfloat x1, jfloat y1,
136                                    jint color0, jint color1, jint tileMode)
137{
138    SkPoint pts[2];
139    pts[0].set(x0, y0);
140    pts[1].set(x1, y1);
141
142    SkColor colors[2];
143    colors[0] = color0;
144    colors[1] = color1;
145
146    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode);
147
148    ThrowIAE_IfNull(env, s);
149    return reinterpret_cast<jlong>(s);
150}
151
152///////////////////////////////////////////////////////////////////////////////////////////////
153
154static jlong RadialGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat radius,
155        jintArray colorArray, jfloatArray posArray, jint tileMode) {
156    SkPoint center;
157    center.set(x, y);
158
159    size_t      count = env->GetArrayLength(colorArray);
160    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
161
162    AutoJavaFloatArray autoPos(env, posArray, count);
163#ifdef SK_SCALAR_IS_FLOAT
164    SkScalar* pos = autoPos.ptr();
165#else
166    #error Need to convert float array to SkScalar array before calling the following function.
167#endif
168
169    SkShader* shader = SkGradientShader::CreateRadial(center, radius,
170            reinterpret_cast<const SkColor*>(colorValues), pos, count,
171            static_cast<SkShader::TileMode>(tileMode));
172    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
173                                 JNI_ABORT);
174
175    ThrowIAE_IfNull(env, shader);
176    return reinterpret_cast<jlong>(shader);
177}
178
179static jlong RadialGradient_create2(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat radius,
180        jint color0, jint color1, jint tileMode) {
181    SkPoint center;
182    center.set(x, y);
183
184    SkColor colors[2];
185    colors[0] = color0;
186    colors[1] = color1;
187
188    SkShader* s = SkGradientShader::CreateRadial(center, radius, colors, NULL, 2,
189            (SkShader::TileMode)tileMode);
190    ThrowIAE_IfNull(env, s);
191    return reinterpret_cast<jlong>(s);
192}
193
194///////////////////////////////////////////////////////////////////////////////
195
196static jlong SweepGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y,
197        jintArray jcolors, jfloatArray jpositions) {
198    size_t      count = env->GetArrayLength(jcolors);
199    const jint* colors = env->GetIntArrayElements(jcolors, NULL);
200
201    AutoJavaFloatArray autoPos(env, jpositions, count);
202#ifdef SK_SCALAR_IS_FLOAT
203    SkScalar* pos = autoPos.ptr();
204#else
205    #error Need to convert float array to SkScalar array before calling the following function.
206#endif
207
208    SkShader* shader = SkGradientShader::CreateSweep(x, y,
209            reinterpret_cast<const SkColor*>(colors), pos, count);
210    env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
211                                 JNI_ABORT);
212    ThrowIAE_IfNull(env, shader);
213    return reinterpret_cast<jlong>(shader);
214}
215
216static jlong SweepGradient_create2(JNIEnv* env, jobject, jfloat x, jfloat y,
217        int color0, int color1) {
218    SkColor colors[2];
219    colors[0] = color0;
220    colors[1] = color1;
221    SkShader* s = SkGradientShader::CreateSweep(x, y, colors, NULL, 2);
222    ThrowIAE_IfNull(env, s);
223    return reinterpret_cast<jlong>(s);
224}
225
226///////////////////////////////////////////////////////////////////////////////////////////////
227
228static jlong ComposeShader_create1(JNIEnv* env, jobject o,
229        jlong shaderAHandle, jlong shaderBHandle, jlong modeHandle)
230{
231    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
232    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
233    SkXfermode* mode = reinterpret_cast<SkXfermode *>(modeHandle);
234    SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, mode);
235    return reinterpret_cast<jlong>(shader);
236}
237
238static jlong ComposeShader_create2(JNIEnv* env, jobject o,
239        jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle)
240{
241    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
242    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
243    SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
244    SkAutoTUnref<SkXfermode> xfermode(SkXfermode::Create(mode));
245    SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, xfermode.get());
246    return reinterpret_cast<jlong>(shader);
247}
248
249///////////////////////////////////////////////////////////////////////////////////////////////
250
251static const JNINativeMethod gColorMethods[] = {
252    { "nativeRGBToHSV",     "(III[F)V", (void*)Color_RGBToHSV   },
253    { "nativeHSVToColor",   "(I[F)I",   (void*)Color_HSVToColor }
254};
255
256static const JNINativeMethod gShaderMethods[] = {
257    { "nativeDestructor",        "(J)V",    (void*)Shader_destructor        },
258    { "nativeSetLocalMatrix",    "(JJ)J",   (void*)Shader_setLocalMatrix    }
259};
260
261static const JNINativeMethod gBitmapShaderMethods[] = {
262    { "nativeCreate",     "(Landroid/graphics/Bitmap;II)J",  (void*)BitmapShader_constructor },
263};
264
265static const JNINativeMethod gLinearGradientMethods[] = {
266    { "nativeCreate1",     "(FFFF[I[FI)J",  (void*)LinearGradient_create1     },
267    { "nativeCreate2",     "(FFFFIII)J",    (void*)LinearGradient_create2     },
268};
269
270static const JNINativeMethod gRadialGradientMethods[] = {
271    { "nativeCreate1",     "(FFF[I[FI)J",  (void*)RadialGradient_create1     },
272    { "nativeCreate2",     "(FFFIII)J",    (void*)RadialGradient_create2     },
273};
274
275static const JNINativeMethod gSweepGradientMethods[] = {
276    { "nativeCreate1",     "(FF[I[F)J",  (void*)SweepGradient_create1     },
277    { "nativeCreate2",     "(FFII)J",    (void*)SweepGradient_create2     },
278};
279
280static const JNINativeMethod gComposeShaderMethods[] = {
281    { "nativeCreate1",      "(JJJ)J",   (void*)ComposeShader_create1     },
282    { "nativeCreate2",      "(JJI)J",   (void*)ComposeShader_create2     },
283};
284
285int register_android_graphics_Shader(JNIEnv* env)
286{
287    android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
288                                  NELEM(gColorMethods));
289    android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
290                                  NELEM(gShaderMethods));
291    android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
292                                  NELEM(gBitmapShaderMethods));
293    android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
294                                  NELEM(gLinearGradientMethods));
295    android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
296                                  NELEM(gRadialGradientMethods));
297    android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
298                                  NELEM(gSweepGradientMethods));
299    android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
300                                  NELEM(gComposeShaderMethods));
301
302    return 0;
303}
304