Shader.cpp revision 450756ab69a77b9f198f133c275b11d5f1731be6
1#include "GraphicsJNI.h"
2#include "SkGradientShader.h"
3#include "SkImagePriv.h"
4#include "SkShader.h"
5#include "SkBlendMode.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    sk_sp<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    sk_sp<SkShader> baseShader = currentShader->makeAsALocalMatrixShader(&proxyMatrix);
78    if (baseShader.get()) {
79        if (proxyMatrix == *matrix) {
80            return reinterpret_cast<jlong>(currentShader.release());
81        }
82        return reinterpret_cast<jlong>(baseShader->makeWithLocalMatrix(*matrix).release());
83    }
84    return reinterpret_cast<jlong>(currentShader->makeWithLocalMatrix(*matrix).release());
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
99    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
100    sk_sp<SkShader> shader = image->makeShader((SkShader::TileMode)tileModeX,
101                                               (SkShader::TileMode)tileModeY);
102
103    ThrowIAE_IfNull(env, shader.get());
104    return reinterpret_cast<jlong>(shader.release());
105}
106
107///////////////////////////////////////////////////////////////////////////////////////////////
108
109static jlong LinearGradient_create1(JNIEnv* env, jobject o,
110                                    jfloat x0, jfloat y0, jfloat x1, jfloat y1,
111                                    jintArray colorArray, jfloatArray posArray, jint tileMode)
112{
113    SkPoint pts[2];
114    pts[0].set(x0, y0);
115    pts[1].set(x1, y1);
116
117    size_t count = env->GetArrayLength(colorArray);
118    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
119
120    AutoJavaFloatArray autoPos(env, posArray, count);
121#ifdef SK_SCALAR_IS_FLOAT
122    SkScalar* pos = autoPos.ptr();
123#else
124    #error Need to convert float array to SkScalar array before calling the following function.
125#endif
126
127    SkShader* shader = SkGradientShader::MakeLinear(pts,
128            reinterpret_cast<const SkColor*>(colorValues), pos, count,
129            static_cast<SkShader::TileMode>(tileMode)).release();
130
131    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
132    ThrowIAE_IfNull(env, shader);
133    return reinterpret_cast<jlong>(shader);
134}
135
136static jlong LinearGradient_create2(JNIEnv* env, jobject o,
137                                    jfloat x0, jfloat y0, jfloat x1, jfloat y1,
138                                    jint color0, jint color1, jint tileMode)
139{
140    SkPoint pts[2];
141    pts[0].set(x0, y0);
142    pts[1].set(x1, y1);
143
144    SkColor colors[2];
145    colors[0] = color0;
146    colors[1] = color1;
147
148    SkShader* s = SkGradientShader::MakeLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode).release();
149
150    ThrowIAE_IfNull(env, s);
151    return reinterpret_cast<jlong>(s);
152}
153
154///////////////////////////////////////////////////////////////////////////////////////////////
155
156static jlong RadialGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat radius,
157        jintArray colorArray, jfloatArray posArray, jint tileMode) {
158    SkPoint center;
159    center.set(x, y);
160
161    size_t      count = env->GetArrayLength(colorArray);
162    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
163
164    AutoJavaFloatArray autoPos(env, posArray, count);
165#ifdef SK_SCALAR_IS_FLOAT
166    SkScalar* pos = autoPos.ptr();
167#else
168    #error Need to convert float array to SkScalar array before calling the following function.
169#endif
170
171    SkShader* shader = SkGradientShader::MakeRadial(center, radius,
172            reinterpret_cast<const SkColor*>(colorValues), pos, count,
173            static_cast<SkShader::TileMode>(tileMode)).release();
174    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
175                                 JNI_ABORT);
176
177    ThrowIAE_IfNull(env, shader);
178    return reinterpret_cast<jlong>(shader);
179}
180
181static jlong RadialGradient_create2(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat radius,
182        jint color0, jint color1, jint tileMode) {
183    SkPoint center;
184    center.set(x, y);
185
186    SkColor colors[2];
187    colors[0] = color0;
188    colors[1] = color1;
189
190    SkShader* s = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
191            (SkShader::TileMode)tileMode).release();
192    ThrowIAE_IfNull(env, s);
193    return reinterpret_cast<jlong>(s);
194}
195
196///////////////////////////////////////////////////////////////////////////////
197
198static jlong SweepGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y,
199        jintArray jcolors, jfloatArray jpositions) {
200    size_t      count = env->GetArrayLength(jcolors);
201    const jint* colors = env->GetIntArrayElements(jcolors, NULL);
202
203    AutoJavaFloatArray autoPos(env, jpositions, count);
204#ifdef SK_SCALAR_IS_FLOAT
205    SkScalar* pos = autoPos.ptr();
206#else
207    #error Need to convert float array to SkScalar array before calling the following function.
208#endif
209
210    SkShader* shader = SkGradientShader::MakeSweep(x, y,
211            reinterpret_cast<const SkColor*>(colors), pos, count).release();
212    env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
213                                 JNI_ABORT);
214    ThrowIAE_IfNull(env, shader);
215    return reinterpret_cast<jlong>(shader);
216}
217
218static jlong SweepGradient_create2(JNIEnv* env, jobject, jfloat x, jfloat y,
219        int color0, int color1) {
220    SkColor colors[2];
221    colors[0] = color0;
222    colors[1] = color1;
223    SkShader* s = SkGradientShader::MakeSweep(x, y, colors, NULL, 2).release();
224    ThrowIAE_IfNull(env, s);
225    return reinterpret_cast<jlong>(s);
226}
227
228///////////////////////////////////////////////////////////////////////////////////////////////
229
230static jlong ComposeShader_create(JNIEnv* env, jobject o,
231        jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle)
232{
233    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
234    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
235    SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
236    SkShader* shader = SkShader::MakeComposeShader(sk_ref_sp(shaderA),
237                                                   sk_ref_sp(shaderB),
238                                                   mode).release();
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