Shader.cpp revision 669b15a93548b82135c73196665bcb7f03d87795
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
13/**
14 * By default Skia gradients will interpolate their colors in unpremul space
15 * and then premultiply each of the results. We must set this flag to preserve
16 * backwards compatiblity by premultiplying the colors of the gradient first,
17 * and then interpolating between them.
18 */
19static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
20
21static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
22    if (NULL == ptr) {
23        doThrowIAE(env);
24    }
25}
26
27static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
28{
29    SkScalar hsv[3];
30    SkRGBToHSV(red, green, blue, hsv);
31
32    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
33    float* values = autoHSV.ptr();
34    for (int i = 0; i < 3; i++) {
35        values[i] = SkScalarToFloat(hsv[i]);
36    }
37}
38
39static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
40{
41    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
42#ifdef SK_SCALAR_IS_FLOAT
43    SkScalar*   hsv = autoHSV.ptr();
44#else
45    #error Need to convert float array to SkScalar array before calling the following function.
46#endif
47
48    return static_cast<jint>(SkHSVToColor(alpha, hsv));
49}
50
51///////////////////////////////////////////////////////////////////////////////////////////////
52
53static void Shader_safeUnref(JNIEnv* env, jobject o, jlong shaderHandle) {
54    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
55    SkSafeUnref(shader);
56}
57
58///////////////////////////////////////////////////////////////////////////////////////////////
59
60static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jobject jbitmap,
61        jint tileModeX, jint tileModeY) {
62    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
63    SkBitmap bitmap;
64    if (jbitmap) {
65        // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
66        // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
67        android::bitmap::toBitmap(env, jbitmap).getSkBitmapForShaders(&bitmap);
68    }
69
70    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
71    sk_sp<SkShader> shader = image->makeShader(
72            (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY, matrix);
73
74    ThrowIAE_IfNull(env, shader.get());
75    return reinterpret_cast<jlong>(shader.release());
76}
77
78///////////////////////////////////////////////////////////////////////////////////////////////
79
80static jlong LinearGradient_create1(JNIEnv* env, jobject o, jlong matrixPtr,
81        jfloat x0, jfloat y0, jfloat x1, jfloat y1,
82        jintArray colorArray, jfloatArray posArray, jint tileMode) {
83    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
84    SkPoint pts[2];
85    pts[0].set(x0, y0);
86    pts[1].set(x1, y1);
87
88    size_t count = env->GetArrayLength(colorArray);
89    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
90
91    AutoJavaFloatArray autoPos(env, posArray, count);
92#ifdef SK_SCALAR_IS_FLOAT
93    SkScalar* pos = autoPos.ptr();
94#else
95    #error Need to convert float array to SkScalar array before calling the following function.
96#endif
97
98    SkShader* shader = SkGradientShader::MakeLinear(pts,
99            reinterpret_cast<const SkColor*>(colorValues), pos, count,
100            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
101
102    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
103    ThrowIAE_IfNull(env, shader);
104    return reinterpret_cast<jlong>(shader);
105}
106
107static jlong LinearGradient_create2(JNIEnv* env, jobject o, jlong matrixPtr,
108        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jint color0, jint color1, jint tileMode) {
109    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
110
111    SkPoint pts[2];
112    pts[0].set(x0, y0);
113    pts[1].set(x1, y1);
114
115    SkColor colors[2];
116    colors[0] = color0;
117    colors[1] = color1;
118
119    SkShader* s = SkGradientShader::MakeLinear(pts, colors, NULL, 2,
120            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
121
122    ThrowIAE_IfNull(env, s);
123    return reinterpret_cast<jlong>(s);
124}
125
126///////////////////////////////////////////////////////////////////////////////////////////////
127
128static jlong RadialGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
129        jfloat radius, jintArray colorArray, jfloatArray posArray, jint tileMode) {
130    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
131    SkPoint center;
132    center.set(x, y);
133
134    size_t      count = env->GetArrayLength(colorArray);
135    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
136
137    AutoJavaFloatArray autoPos(env, posArray, count);
138#ifdef SK_SCALAR_IS_FLOAT
139    SkScalar* pos = autoPos.ptr();
140#else
141    #error Need to convert float array to SkScalar array before calling the following function.
142#endif
143
144    SkShader* shader = SkGradientShader::MakeRadial(center, radius,
145            reinterpret_cast<const SkColor*>(colorValues), pos, count,
146            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
147    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
148                                 JNI_ABORT);
149
150    ThrowIAE_IfNull(env, shader);
151    return reinterpret_cast<jlong>(shader);
152}
153
154static jlong RadialGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y, jfloat radius,
155        jint color0, jint color1, jint tileMode) {
156    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
157    SkPoint center;
158    center.set(x, y);
159
160    SkColor colors[2];
161    colors[0] = color0;
162    colors[1] = color1;
163
164    SkShader* s = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
165            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
166    ThrowIAE_IfNull(env, s);
167    return reinterpret_cast<jlong>(s);
168}
169
170///////////////////////////////////////////////////////////////////////////////
171
172static jlong SweepGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
173        jintArray jcolors, jfloatArray jpositions) {
174    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
175    size_t      count = env->GetArrayLength(jcolors);
176    const jint* colors = env->GetIntArrayElements(jcolors, NULL);
177
178    AutoJavaFloatArray autoPos(env, jpositions, count);
179#ifdef SK_SCALAR_IS_FLOAT
180    SkScalar* pos = autoPos.ptr();
181#else
182    #error Need to convert float array to SkScalar array before calling the following function.
183#endif
184
185    SkShader* shader = SkGradientShader::MakeSweep(x, y, reinterpret_cast<const SkColor*>(colors),
186            pos, count, sGradientShaderFlags, matrix).release();
187    env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
188                                 JNI_ABORT);
189    ThrowIAE_IfNull(env, shader);
190    return reinterpret_cast<jlong>(shader);
191}
192
193static jlong SweepGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
194        int color0, int color1) {
195    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
196    SkColor colors[2];
197    colors[0] = color0;
198    colors[1] = color1;
199    SkShader* s = SkGradientShader::MakeSweep(x, y, colors, NULL, 2,
200            sGradientShaderFlags, matrix).release();
201    ThrowIAE_IfNull(env, s);
202    return reinterpret_cast<jlong>(s);
203}
204
205///////////////////////////////////////////////////////////////////////////////////////////////
206
207static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
208        jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
209    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
210    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
211    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
212    SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
213    sk_sp<SkShader> baseShader(SkShader::MakeComposeShader(
214            sk_ref_sp(shaderA), sk_ref_sp(shaderB), mode));
215
216    SkShader* shader;
217
218    if (matrix) {
219        shader = baseShader->makeWithLocalMatrix(*matrix).release();
220    } else {
221        shader = baseShader.release();
222    }
223    return reinterpret_cast<jlong>(shader);
224}
225
226///////////////////////////////////////////////////////////////////////////////////////////////
227
228static const JNINativeMethod gColorMethods[] = {
229    { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
230    { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
231};
232
233static const JNINativeMethod gShaderMethods[] = {
234    { "nativeSafeUnref",   "(J)V",    (void*)Shader_safeUnref },
235};
236
237static const JNINativeMethod gBitmapShaderMethods[] = {
238    { "nativeCreate",      "(JLandroid/graphics/Bitmap;II)J",  (void*)BitmapShader_constructor },
239};
240
241static const JNINativeMethod gLinearGradientMethods[] = {
242    { "nativeCreate1",     "(JFFFF[I[FI)J",  (void*)LinearGradient_create1     },
243    { "nativeCreate2",     "(JFFFFIII)J",    (void*)LinearGradient_create2     },
244};
245
246static const JNINativeMethod gRadialGradientMethods[] = {
247    { "nativeCreate1",     "(JFFF[I[FI)J",  (void*)RadialGradient_create1     },
248    { "nativeCreate2",     "(JFFFIII)J",    (void*)RadialGradient_create2     },
249};
250
251static const JNINativeMethod gSweepGradientMethods[] = {
252    { "nativeCreate1",     "(JFF[I[F)J",  (void*)SweepGradient_create1     },
253    { "nativeCreate2",     "(JFFII)J",    (void*)SweepGradient_create2     },
254};
255
256static const JNINativeMethod gComposeShaderMethods[] = {
257    { "nativeCreate",      "(JJJI)J",   (void*)ComposeShader_create     },
258};
259
260int register_android_graphics_Shader(JNIEnv* env)
261{
262    android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
263                                  NELEM(gColorMethods));
264    android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
265                                  NELEM(gShaderMethods));
266    android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
267                                  NELEM(gBitmapShaderMethods));
268    android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
269                                  NELEM(gLinearGradientMethods));
270    android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
271                                  NELEM(gRadialGradientMethods));
272    android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
273                                  NELEM(gSweepGradientMethods));
274    android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
275                                  NELEM(gComposeShaderMethods));
276
277    return 0;
278}
279