Shader.cpp revision d4babda3aa0af4d9a060b588f082e2f29192fd60
1#include "GraphicsJNI.h"
2#include "SkGradientShader.h"
3#include "SkImagePriv.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    sk_sp<SkShader> s = SkMakeBitmapShader(bitmap,
99                                           (SkShader::TileMode)tileModeX,
100                                           (SkShader::TileMode)tileModeY,
101                                           nullptr,
102                                           kNever_SkCopyPixelsMode,
103                                           nullptr);
104
105    ThrowIAE_IfNull(env, s.get());
106    return reinterpret_cast<jlong>(s.release());
107}
108
109///////////////////////////////////////////////////////////////////////////////////////////////
110
111static jlong LinearGradient_create1(JNIEnv* env, jobject o,
112                                    jfloat x0, jfloat y0, jfloat x1, jfloat y1,
113                                    jintArray colorArray, jfloatArray posArray, jint tileMode)
114{
115    SkPoint pts[2];
116    pts[0].set(x0, y0);
117    pts[1].set(x1, y1);
118
119    size_t count = env->GetArrayLength(colorArray);
120    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
121
122    AutoJavaFloatArray autoPos(env, posArray, count);
123#ifdef SK_SCALAR_IS_FLOAT
124    SkScalar* pos = autoPos.ptr();
125#else
126    #error Need to convert float array to SkScalar array before calling the following function.
127#endif
128
129    SkShader* shader = SkGradientShader::CreateLinear(pts,
130            reinterpret_cast<const SkColor*>(colorValues), pos, count,
131            static_cast<SkShader::TileMode>(tileMode));
132
133    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
134    ThrowIAE_IfNull(env, shader);
135    return reinterpret_cast<jlong>(shader);
136}
137
138static jlong LinearGradient_create2(JNIEnv* env, jobject o,
139                                    jfloat x0, jfloat y0, jfloat x1, jfloat y1,
140                                    jint color0, jint color1, jint tileMode)
141{
142    SkPoint pts[2];
143    pts[0].set(x0, y0);
144    pts[1].set(x1, y1);
145
146    SkColor colors[2];
147    colors[0] = color0;
148    colors[1] = color1;
149
150    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode);
151
152    ThrowIAE_IfNull(env, s);
153    return reinterpret_cast<jlong>(s);
154}
155
156///////////////////////////////////////////////////////////////////////////////////////////////
157
158static jlong RadialGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat radius,
159        jintArray colorArray, jfloatArray posArray, jint tileMode) {
160    SkPoint center;
161    center.set(x, y);
162
163    size_t      count = env->GetArrayLength(colorArray);
164    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
165
166    AutoJavaFloatArray autoPos(env, posArray, count);
167#ifdef SK_SCALAR_IS_FLOAT
168    SkScalar* pos = autoPos.ptr();
169#else
170    #error Need to convert float array to SkScalar array before calling the following function.
171#endif
172
173    SkShader* shader = SkGradientShader::CreateRadial(center, radius,
174            reinterpret_cast<const SkColor*>(colorValues), pos, count,
175            static_cast<SkShader::TileMode>(tileMode));
176    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
177                                 JNI_ABORT);
178
179    ThrowIAE_IfNull(env, shader);
180    return reinterpret_cast<jlong>(shader);
181}
182
183static jlong RadialGradient_create2(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat radius,
184        jint color0, jint color1, jint tileMode) {
185    SkPoint center;
186    center.set(x, y);
187
188    SkColor colors[2];
189    colors[0] = color0;
190    colors[1] = color1;
191
192    SkShader* s = SkGradientShader::CreateRadial(center, radius, colors, NULL, 2,
193            (SkShader::TileMode)tileMode);
194    ThrowIAE_IfNull(env, s);
195    return reinterpret_cast<jlong>(s);
196}
197
198///////////////////////////////////////////////////////////////////////////////
199
200static jlong SweepGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y,
201        jintArray jcolors, jfloatArray jpositions) {
202    size_t      count = env->GetArrayLength(jcolors);
203    const jint* colors = env->GetIntArrayElements(jcolors, NULL);
204
205    AutoJavaFloatArray autoPos(env, jpositions, count);
206#ifdef SK_SCALAR_IS_FLOAT
207    SkScalar* pos = autoPos.ptr();
208#else
209    #error Need to convert float array to SkScalar array before calling the following function.
210#endif
211
212    SkShader* shader = SkGradientShader::CreateSweep(x, y,
213            reinterpret_cast<const SkColor*>(colors), pos, count);
214    env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
215                                 JNI_ABORT);
216    ThrowIAE_IfNull(env, shader);
217    return reinterpret_cast<jlong>(shader);
218}
219
220static jlong SweepGradient_create2(JNIEnv* env, jobject, jfloat x, jfloat y,
221        int color0, int color1) {
222    SkColor colors[2];
223    colors[0] = color0;
224    colors[1] = color1;
225    SkShader* s = SkGradientShader::CreateSweep(x, y, colors, NULL, 2);
226    ThrowIAE_IfNull(env, s);
227    return reinterpret_cast<jlong>(s);
228}
229
230///////////////////////////////////////////////////////////////////////////////////////////////
231
232static jlong ComposeShader_create(JNIEnv* env, jobject o,
233        jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle)
234{
235    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
236    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
237    SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
238    SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, mode);
239    return reinterpret_cast<jlong>(shader);
240}
241
242///////////////////////////////////////////////////////////////////////////////////////////////
243
244static const JNINativeMethod gColorMethods[] = {
245    { "nativeRGBToHSV",     "(III[F)V", (void*)Color_RGBToHSV   },
246    { "nativeHSVToColor",   "(I[F)I",   (void*)Color_HSVToColor }
247};
248
249static const JNINativeMethod gShaderMethods[] = {
250    { "nativeDestructor",        "(J)V",    (void*)Shader_destructor        },
251    { "nativeSetLocalMatrix",    "(JJ)J",   (void*)Shader_setLocalMatrix    }
252};
253
254static const JNINativeMethod gBitmapShaderMethods[] = {
255    { "nativeCreate",     "(Landroid/graphics/Bitmap;II)J",  (void*)BitmapShader_constructor },
256};
257
258static const JNINativeMethod gLinearGradientMethods[] = {
259    { "nativeCreate1",     "(FFFF[I[FI)J",  (void*)LinearGradient_create1     },
260    { "nativeCreate2",     "(FFFFIII)J",    (void*)LinearGradient_create2     },
261};
262
263static const JNINativeMethod gRadialGradientMethods[] = {
264    { "nativeCreate1",     "(FFF[I[FI)J",  (void*)RadialGradient_create1     },
265    { "nativeCreate2",     "(FFFIII)J",    (void*)RadialGradient_create2     },
266};
267
268static const JNINativeMethod gSweepGradientMethods[] = {
269    { "nativeCreate1",     "(FF[I[F)J",  (void*)SweepGradient_create1     },
270    { "nativeCreate2",     "(FFII)J",    (void*)SweepGradient_create2     },
271};
272
273static const JNINativeMethod gComposeShaderMethods[] = {
274    { "nativeCreate",      "(JJI)J",   (void*)ComposeShader_create     },
275};
276
277int register_android_graphics_Shader(JNIEnv* env)
278{
279    android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
280                                  NELEM(gColorMethods));
281    android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
282                                  NELEM(gShaderMethods));
283    android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
284                                  NELEM(gBitmapShaderMethods));
285    android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
286                                  NELEM(gLinearGradientMethods));
287    android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
288                                  NELEM(gRadialGradientMethods));
289    android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
290                                  NELEM(gSweepGradientMethods));
291    android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
292                                  NELEM(gComposeShaderMethods));
293
294    return 0;
295}
296