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