Shader.cpp revision 18bd88534a5783e581ccdd25b1b0f81c237a14b4
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    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    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::MakeLinear(pts,
130            reinterpret_cast<const SkColor*>(colorValues), pos, count,
131            static_cast<SkShader::TileMode>(tileMode)).release();
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::MakeLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode).release();
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::MakeRadial(center, radius,
174            reinterpret_cast<const SkColor*>(colorValues), pos, count,
175            static_cast<SkShader::TileMode>(tileMode)).release();
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::MakeRadial(center, radius, colors, NULL, 2,
193            (SkShader::TileMode)tileMode).release();
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::MakeSweep(x, y,
213            reinterpret_cast<const SkColor*>(colors), pos, count).release();
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::MakeSweep(x, y, colors, NULL, 2).release();
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    SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
238    SkShader* shader = SkShader::MakeComposeShader(sk_ref_sp(shaderA),
239                                                   sk_ref_sp(shaderB),
240                                                   (SkXfermode::Mode)mode).release();
241    return reinterpret_cast<jlong>(shader);
242}
243
244///////////////////////////////////////////////////////////////////////////////////////////////
245
246static const JNINativeMethod gColorMethods[] = {
247    { "nativeRGBToHSV",     "(III[F)V", (void*)Color_RGBToHSV   },
248    { "nativeHSVToColor",   "(I[F)I",   (void*)Color_HSVToColor }
249};
250
251static const JNINativeMethod gShaderMethods[] = {
252    { "nativeDestructor",        "(J)V",    (void*)Shader_destructor        },
253    { "nativeSetLocalMatrix",    "(JJ)J",   (void*)Shader_setLocalMatrix    }
254};
255
256static const JNINativeMethod gBitmapShaderMethods[] = {
257    { "nativeCreate",     "(Landroid/graphics/Bitmap;II)J",  (void*)BitmapShader_constructor },
258};
259
260static const JNINativeMethod gLinearGradientMethods[] = {
261    { "nativeCreate1",     "(FFFF[I[FI)J",  (void*)LinearGradient_create1     },
262    { "nativeCreate2",     "(FFFFIII)J",    (void*)LinearGradient_create2     },
263};
264
265static const JNINativeMethod gRadialGradientMethods[] = {
266    { "nativeCreate1",     "(FFF[I[FI)J",  (void*)RadialGradient_create1     },
267    { "nativeCreate2",     "(FFFIII)J",    (void*)RadialGradient_create2     },
268};
269
270static const JNINativeMethod gSweepGradientMethods[] = {
271    { "nativeCreate1",     "(FF[I[F)J",  (void*)SweepGradient_create1     },
272    { "nativeCreate2",     "(FFII)J",    (void*)SweepGradient_create2     },
273};
274
275static const JNINativeMethod gComposeShaderMethods[] = {
276    { "nativeCreate",      "(JJI)J",   (void*)ComposeShader_create     },
277};
278
279int register_android_graphics_Shader(JNIEnv* env)
280{
281    android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
282                                  NELEM(gColorMethods));
283    android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
284                                  NELEM(gShaderMethods));
285    android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
286                                  NELEM(gBitmapShaderMethods));
287    android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
288                                  NELEM(gLinearGradientMethods));
289    android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
290                                  NELEM(gRadialGradientMethods));
291    android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
292                                  NELEM(gSweepGradientMethods));
293    android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
294                                  NELEM(gComposeShaderMethods));
295
296    return 0;
297}
298