1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "jni.h"
18 #include "GraphicsJNI.h"
19 #include "core_jni_helpers.h"
20 
21 #include <Canvas.h>
22 #include "SkDrawFilter.h"
23 #include "SkGraphics.h"
24 #include "Paint.h"
25 #include "TypefaceImpl.h"
26 
27 #include "MinikinUtils.h"
28 
29 namespace android {
30 
31 namespace CanvasJNI {
32 
get_canvas(jlong canvasHandle)33 static Canvas* get_canvas(jlong canvasHandle) {
34     return reinterpret_cast<Canvas*>(canvasHandle);
35 }
36 
finalizer(JNIEnv * env,jobject clazz,jlong canvasHandle)37 static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
38     delete get_canvas(canvasHandle);
39 }
40 
41 // Native wrapper constructor used by Canvas(Bitmap)
initRaster(JNIEnv * env,jobject,jobject jbitmap)42 static jlong initRaster(JNIEnv* env, jobject, jobject jbitmap) {
43     SkBitmap bitmap;
44     if (jbitmap != NULL) {
45         GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
46     }
47     return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
48 }
49 
50 // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
51 // optionally copying canvas matrix & clip state.
setBitmap(JNIEnv * env,jobject,jlong canvasHandle,jobject jbitmap)52 static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap) {
53     SkBitmap bitmap;
54     if (jbitmap != NULL) {
55         GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
56     }
57     get_canvas(canvasHandle)->setBitmap(bitmap);
58 }
59 
isOpaque(JNIEnv *,jobject,jlong canvasHandle)60 static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
61     return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
62 }
63 
getWidth(JNIEnv *,jobject,jlong canvasHandle)64 static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
65     return static_cast<jint>(get_canvas(canvasHandle)->width());
66 }
67 
getHeight(JNIEnv *,jobject,jlong canvasHandle)68 static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
69     return static_cast<jint>(get_canvas(canvasHandle)->height());
70 }
71 
getSaveCount(JNIEnv *,jobject,jlong canvasHandle)72 static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
73     return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
74 }
75 
save(JNIEnv *,jobject,jlong canvasHandle,jint flagsHandle)76 static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
77     SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
78     return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
79 }
80 
saveLayer(JNIEnv * env,jobject,jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jlong paintHandle,jint flagsHandle)81 static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
82                       jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
83     Paint* paint  = reinterpret_cast<Paint*>(paintHandle);
84     SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
85     return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
86 }
87 
saveLayerAlpha(JNIEnv * env,jobject,jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jint alpha,jint flagsHandle)88 static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
89                            jfloat r, jfloat b, jint alpha, jint flagsHandle) {
90     SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
91     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
92 }
93 
restore(JNIEnv * env,jobject,jlong canvasHandle,jboolean throwOnUnderflow)94 static void restore(JNIEnv* env, jobject, jlong canvasHandle, jboolean throwOnUnderflow) {
95     Canvas* canvas = get_canvas(canvasHandle);
96     if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
97         if (throwOnUnderflow) {
98             doThrowISE(env, "Underflow in restore - more restores than saves");
99         }
100         return; // compat behavior - return without throwing
101     }
102     canvas->restore();
103 }
104 
restoreToCount(JNIEnv * env,jobject,jlong canvasHandle,jint restoreCount,jboolean throwOnUnderflow)105 static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount,
106         jboolean throwOnUnderflow) {
107     Canvas* canvas = get_canvas(canvasHandle);
108     if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
109         if (throwOnUnderflow) {
110             doThrowIAE(env, "Underflow in restoreToCount - more restores than saves");
111             return;
112         }
113         restoreCount = 1; // compat behavior - restore as far as possible
114     }
115     canvas->restoreToCount(restoreCount);
116 }
117 
getCTM(JNIEnv * env,jobject,jlong canvasHandle,jlong matrixHandle)118 static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
119     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
120     get_canvas(canvasHandle)->getMatrix(matrix);
121 }
122 
setMatrix(JNIEnv * env,jobject,jlong canvasHandle,jlong matrixHandle)123 static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
124     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
125     get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
126 }
127 
concat(JNIEnv * env,jobject,jlong canvasHandle,jlong matrixHandle)128 static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
129     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
130     get_canvas(canvasHandle)->concat(*matrix);
131 }
132 
rotate(JNIEnv *,jobject,jlong canvasHandle,jfloat degrees)133 static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
134     get_canvas(canvasHandle)->rotate(degrees);
135 }
136 
scale(JNIEnv *,jobject,jlong canvasHandle,jfloat sx,jfloat sy)137 static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
138     get_canvas(canvasHandle)->scale(sx, sy);
139 }
140 
skew(JNIEnv *,jobject,jlong canvasHandle,jfloat sx,jfloat sy)141 static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
142     get_canvas(canvasHandle)->skew(sx, sy);
143 }
144 
translate(JNIEnv *,jobject,jlong canvasHandle,jfloat dx,jfloat dy)145 static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
146     get_canvas(canvasHandle)->translate(dx, dy);
147 }
148 
getClipBounds(JNIEnv * env,jobject,jlong canvasHandle,jobject bounds)149 static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
150     SkRect   r;
151     SkIRect ir;
152     bool result = get_canvas(canvasHandle)->getClipBounds(&r);
153 
154     if (!result) {
155         r.setEmpty();
156     }
157     r.round(&ir);
158 
159     (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
160     return result ? JNI_TRUE : JNI_FALSE;
161 }
162 
quickRejectRect(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom)163 static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
164                                 jfloat left, jfloat top, jfloat right, jfloat bottom) {
165     bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
166     return result ? JNI_TRUE : JNI_FALSE;
167 }
168 
quickRejectPath(JNIEnv * env,jobject,jlong canvasHandle,jlong pathHandle)169 static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
170     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
171     bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
172     return result ? JNI_TRUE : JNI_FALSE;
173 }
174 
clipRect(JNIEnv *,jobject,jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jint opHandle)175 static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
176                          jfloat r, jfloat b, jint opHandle) {
177     SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
178     bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
179     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
180 }
181 
clipPath(JNIEnv * env,jobject,jlong canvasHandle,jlong pathHandle,jint opHandle)182 static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
183                          jint opHandle) {
184     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
185     SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
186     bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, op);
187     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
188 }
189 
clipRegion(JNIEnv * env,jobject,jlong canvasHandle,jlong deviceRgnHandle,jint opHandle)190 static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
191                            jint opHandle) {
192     SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
193     SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
194     bool nonEmptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
195     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
196 }
197 
drawColor(JNIEnv * env,jobject,jlong canvasHandle,jint color,jint modeHandle)198 static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
199     SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle);
200     get_canvas(canvasHandle)->drawColor(color, mode);
201 }
202 
drawPaint(JNIEnv * env,jobject,jlong canvasHandle,jlong paintHandle)203 static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
204     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
205     get_canvas(canvasHandle)->drawPaint(*paint);
206 }
207 
drawPoint(JNIEnv *,jobject,jlong canvasHandle,jfloat x,jfloat y,jlong paintHandle)208 static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
209                       jlong paintHandle) {
210     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
211     get_canvas(canvasHandle)->drawPoint(x, y, *paint);
212 }
213 
drawPoints(JNIEnv * env,jobject,jlong canvasHandle,jfloatArray jptsArray,jint offset,jint count,jlong paintHandle)214 static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
215                        jint offset, jint count, jlong paintHandle) {
216     NPE_CHECK_RETURN_VOID(env, jptsArray);
217     AutoJavaFloatArray autoPts(env, jptsArray);
218     float* floats = autoPts.ptr();
219     const int length = autoPts.length();
220 
221     if ((offset | count) < 0 || offset + count > length) {
222         doThrowAIOOBE(env);
223         return;
224     }
225 
226     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
227     get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
228 }
229 
drawLine(JNIEnv * env,jobject,jlong canvasHandle,jfloat startX,jfloat startY,jfloat stopX,jfloat stopY,jlong paintHandle)230 static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
231                      jfloat stopX, jfloat stopY, jlong paintHandle) {
232     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
233     get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
234 }
235 
drawLines(JNIEnv * env,jobject,jlong canvasHandle,jfloatArray jptsArray,jint offset,jint count,jlong paintHandle)236 static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
237                       jint offset, jint count, jlong paintHandle) {
238     NPE_CHECK_RETURN_VOID(env, jptsArray);
239     AutoJavaFloatArray autoPts(env, jptsArray);
240     float* floats = autoPts.ptr();
241     const int length = autoPts.length();
242 
243     if ((offset | count) < 0 || offset + count > length) {
244         doThrowAIOOBE(env);
245         return;
246     }
247 
248     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
249     get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
250 }
251 
drawRect(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jlong paintHandle)252 static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
253                      jfloat right, jfloat bottom, jlong paintHandle) {
254     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
255     get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
256 }
257 
drawRoundRect(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat rx,jfloat ry,jlong paintHandle)258 static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
259                           jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
260     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
261     get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
262 }
263 
drawCircle(JNIEnv * env,jobject,jlong canvasHandle,jfloat cx,jfloat cy,jfloat radius,jlong paintHandle)264 static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
265                        jfloat radius, jlong paintHandle) {
266     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
267     get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
268 }
269 
drawOval(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jlong paintHandle)270 static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
271                      jfloat right, jfloat bottom, jlong paintHandle) {
272     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
273     get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
274 }
275 
drawArc(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat startAngle,jfloat sweepAngle,jboolean useCenter,jlong paintHandle)276 static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
277                     jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
278                     jboolean useCenter, jlong paintHandle) {
279     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
280     get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
281                                        useCenter, *paint);
282 }
283 
drawPath(JNIEnv * env,jobject,jlong canvasHandle,jlong pathHandle,jlong paintHandle)284 static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
285                      jlong paintHandle) {
286     const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
287     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
288     get_canvas(canvasHandle)->drawPath(*path, *paint);
289 }
290 
drawVertices(JNIEnv * env,jobject,jlong canvasHandle,jint modeHandle,jint vertexCount,jfloatArray jverts,jint vertIndex,jfloatArray jtexs,jint texIndex,jintArray jcolors,jint colorIndex,jshortArray jindices,jint indexIndex,jint indexCount,jlong paintHandle)291 static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
292                          jint modeHandle, jint vertexCount,
293                          jfloatArray jverts, jint vertIndex,
294                          jfloatArray jtexs, jint texIndex,
295                          jintArray jcolors, jint colorIndex,
296                          jshortArray jindices, jint indexIndex,
297                          jint indexCount, jlong paintHandle) {
298     AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
299     AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
300     AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
301     AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
302 
303     const float* verts = vertA.ptr() + vertIndex;
304     const float* texs = texA.ptr() + vertIndex;
305     const int* colors = NULL;
306     const uint16_t* indices = NULL;
307 
308     if (jcolors != NULL) {
309         colors = colorA.ptr() + colorIndex;
310     }
311     if (jindices != NULL) {
312         indices = (const uint16_t*)(indexA.ptr() + indexIndex);
313     }
314 
315     SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
316     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
317     get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
318                                            indices, indexCount, *paint);
319 }
320 
drawBitmap(JNIEnv * env,jobject jcanvas,jlong canvasHandle,jobject jbitmap,jfloat left,jfloat top,jlong paintHandle,jint canvasDensity,jint screenDensity,jint bitmapDensity)321 static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jobject jbitmap,
322                        jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
323                        jint screenDensity, jint bitmapDensity) {
324     Canvas* canvas = get_canvas(canvasHandle);
325     SkBitmap bitmap;
326     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
327     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
328 
329     if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
330         if (screenDensity != 0 && screenDensity != bitmapDensity) {
331             Paint filteredPaint;
332             if (paint) {
333                 filteredPaint = *paint;
334             }
335             filteredPaint.setFilterQuality(kLow_SkFilterQuality);
336             canvas->drawBitmap(bitmap, left, top, &filteredPaint);
337         } else {
338             canvas->drawBitmap(bitmap, left, top, paint);
339         }
340     } else {
341         canvas->save(SkCanvas::kMatrixClip_SaveFlag);
342         SkScalar scale = canvasDensity / (float)bitmapDensity;
343         canvas->translate(left, top);
344         canvas->scale(scale, scale);
345 
346         Paint filteredPaint;
347         if (paint) {
348             filteredPaint = *paint;
349         }
350         filteredPaint.setFilterQuality(kLow_SkFilterQuality);
351 
352         canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
353         canvas->restore();
354     }
355 }
356 
drawBitmapMatrix(JNIEnv * env,jobject,jlong canvasHandle,jobject jbitmap,jlong matrixHandle,jlong paintHandle)357 static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
358                              jlong matrixHandle, jlong paintHandle) {
359     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
360     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
361     SkBitmap bitmap;
362     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
363     get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint);
364 }
365 
drawBitmapRect(JNIEnv * env,jobject,jlong canvasHandle,jobject jbitmap,float srcLeft,float srcTop,float srcRight,float srcBottom,float dstLeft,float dstTop,float dstRight,float dstBottom,jlong paintHandle,jint screenDensity,jint bitmapDensity)366 static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
367                            float srcLeft, float srcTop, float srcRight, float srcBottom,
368                            float dstLeft, float dstTop, float dstRight, float dstBottom,
369                            jlong paintHandle, jint screenDensity, jint bitmapDensity) {
370     Canvas* canvas = get_canvas(canvasHandle);
371     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
372 
373     SkBitmap bitmap;
374     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
375     if (screenDensity != 0 && screenDensity != bitmapDensity) {
376         Paint filteredPaint;
377         if (paint) {
378             filteredPaint = *paint;
379         }
380         filteredPaint.setFilterQuality(kLow_SkFilterQuality);
381         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
382                            dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
383     } else {
384         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
385                            dstLeft, dstTop, dstRight, dstBottom, paint);
386     }
387 }
388 
drawBitmapArray(JNIEnv * env,jobject,jlong canvasHandle,jintArray jcolors,jint offset,jint stride,jfloat x,jfloat y,jint width,jint height,jboolean hasAlpha,jlong paintHandle)389 static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
390                             jintArray jcolors, jint offset, jint stride,
391                             jfloat x, jfloat y, jint width, jint height,
392                             jboolean hasAlpha, jlong paintHandle) {
393     // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
394     // correct the alphaType to kOpaque_SkAlphaType.
395     SkImageInfo info = SkImageInfo::Make(width, height,
396                            hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
397                            kPremul_SkAlphaType);
398     SkBitmap bitmap;
399     bitmap.setInfo(info);
400     if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) {
401         return;
402     }
403 
404     if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
405         return;
406     }
407 
408     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
409     get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
410 }
411 
drawBitmapMesh(JNIEnv * env,jobject,jlong canvasHandle,jobject jbitmap,jint meshWidth,jint meshHeight,jfloatArray jverts,jint vertIndex,jintArray jcolors,jint colorIndex,jlong paintHandle)412 static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
413                            jint meshWidth, jint meshHeight, jfloatArray jverts,
414                            jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
415     const int ptCount = (meshWidth + 1) * (meshHeight + 1);
416     AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
417     AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
418 
419     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
420     SkBitmap bitmap;
421     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
422     get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
423                                              vertA.ptr(), colorA.ptr(), paint);
424 }
425 
426 class DrawTextFunctor {
427 public:
DrawTextFunctor(const Layout & layout,Canvas * canvas,uint16_t * glyphs,float * pos,const SkPaint & paint,float x,float y,MinikinRect & bounds,float totalAdvance)428     DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
429                     const SkPaint& paint, float x, float y, MinikinRect& bounds,
430                     float totalAdvance)
431             : layout(layout), canvas(canvas), glyphs(glyphs), pos(pos), paint(paint),
432               x(x), y(y), bounds(bounds), totalAdvance(totalAdvance) { }
433 
operator ()(size_t start,size_t end)434     void operator()(size_t start, size_t end) {
435         if (canvas->drawTextAbsolutePos()) {
436             for (size_t i = start; i < end; i++) {
437                 glyphs[i] = layout.getGlyphId(i);
438                 pos[2 * i] = x + layout.getX(i);
439                 pos[2 * i + 1] = y + layout.getY(i);
440             }
441         } else {
442             for (size_t i = start; i < end; i++) {
443                 glyphs[i] = layout.getGlyphId(i);
444                 pos[2 * i] = layout.getX(i);
445                 pos[2 * i + 1] = layout.getY(i);
446             }
447         }
448 
449         size_t glyphCount = end - start;
450         canvas->drawText(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
451                          bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom,
452                          totalAdvance);
453     }
454 private:
455     const Layout& layout;
456     Canvas* canvas;
457     uint16_t* glyphs;
458     float* pos;
459     const SkPaint& paint;
460     float x;
461     float y;
462     MinikinRect& bounds;
463     float totalAdvance;
464 };
465 
466 // Same values used by Skia
467 #define kStdStrikeThru_Offset   (-6.0f / 21.0f)
468 #define kStdUnderline_Offset    (1.0f / 9.0f)
469 #define kStdUnderline_Thickness (1.0f / 18.0f)
470 
drawTextDecorations(Canvas * canvas,float x,float y,float length,const SkPaint & paint)471 void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) {
472     uint32_t flags;
473     SkDrawFilter* drawFilter = canvas->getDrawFilter();
474     if (drawFilter) {
475         SkPaint paintCopy(paint);
476         drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
477         flags = paintCopy.getFlags();
478     } else {
479         flags = paint.getFlags();
480     }
481     if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
482         SkScalar left = x;
483         SkScalar right = x + length;
484         float textSize = paint.getTextSize();
485         float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
486         if (flags & SkPaint::kUnderlineText_Flag) {
487             SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
488             SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
489             canvas->drawRect(left, top, right, bottom, paint);
490         }
491         if (flags & SkPaint::kStrikeThruText_Flag) {
492             SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
493             SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
494             canvas->drawRect(left, top, right, bottom, paint);
495         }
496     }
497 }
498 
drawText(Canvas * canvas,const uint16_t * text,int start,int count,int contextCount,float x,float y,int bidiFlags,const Paint & origPaint,TypefaceImpl * typeface)499 void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
500              float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
501     // minikin may modify the original paint
502     Paint paint(origPaint);
503 
504     Layout layout;
505     MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
506 
507     size_t nGlyphs = layout.nGlyphs();
508     uint16_t* glyphs = new uint16_t[nGlyphs];
509     float* pos = new float[nGlyphs * 2];
510 
511     x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
512 
513     MinikinRect bounds;
514     layout.getBounds(&bounds);
515     if (!canvas->drawTextAbsolutePos()) {
516         bounds.offset(x, y);
517     }
518 
519     DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance());
520     MinikinUtils::forFontRun(layout, &paint, f);
521 
522     drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
523 
524     delete[] glyphs;
525     delete[] pos;
526 }
527 
drawTextChars(JNIEnv * env,jobject,jlong canvasHandle,jcharArray text,jint index,jint count,jfloat x,jfloat y,jint bidiFlags,jlong paintHandle,jlong typefaceHandle)528 static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
529                           jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
530                           jlong paintHandle, jlong typefaceHandle) {
531     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
532     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
533     jchar* jchars = env->GetCharArrayElements(text, NULL);
534     drawText(get_canvas(canvasHandle), jchars + index, 0, count, count, x, y,
535                                        bidiFlags, *paint, typeface);
536     env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
537 }
538 
drawTextString(JNIEnv * env,jobject,jlong canvasHandle,jstring text,jint start,jint end,jfloat x,jfloat y,jint bidiFlags,jlong paintHandle,jlong typefaceHandle)539 static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
540                            jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
541                            jlong paintHandle, jlong typefaceHandle) {
542     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
543     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
544     const int count = end - start;
545     const jchar* jchars = env->GetStringChars(text, NULL);
546     drawText(get_canvas(canvasHandle), jchars + start, 0, count, count, x, y,
547                                        bidiFlags, *paint, typeface);
548     env->ReleaseStringChars(text, jchars);
549 }
550 
drawTextRunChars(JNIEnv * env,jobject,jlong canvasHandle,jcharArray text,jint index,jint count,jint contextIndex,jint contextCount,jfloat x,jfloat y,jboolean isRtl,jlong paintHandle,jlong typefaceHandle)551 static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
552                              jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
553                              jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
554     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
555     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
556 
557     const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
558     jchar* jchars = env->GetCharArrayElements(text, NULL);
559     drawText(get_canvas(canvasHandle), jchars + contextIndex, index - contextIndex, count,
560                                        contextCount, x, y, bidiFlags, *paint, typeface);
561     env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
562 }
563 
drawTextRunString(JNIEnv * env,jobject obj,jlong canvasHandle,jstring text,jint start,jint end,jint contextStart,jint contextEnd,jfloat x,jfloat y,jboolean isRtl,jlong paintHandle,jlong typefaceHandle)564 static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
565                               jint start, jint end, jint contextStart, jint contextEnd,
566                               jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
567                               jlong typefaceHandle) {
568     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
569     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
570 
571     int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
572     jint count = end - start;
573     jint contextCount = contextEnd - contextStart;
574     const jchar* jchars = env->GetStringChars(text, NULL);
575     drawText(get_canvas(canvasHandle), jchars + contextStart, start - contextStart, count,
576                                        contextCount, x, y, bidiFlags, *paint, typeface);
577     env->ReleaseStringChars(text, jchars);
578 }
579 
580 class DrawTextOnPathFunctor {
581 public:
DrawTextOnPathFunctor(const Layout & layout,Canvas * canvas,float hOffset,float vOffset,const Paint & paint,const SkPath & path)582     DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
583                 float vOffset, const Paint& paint, const SkPath& path)
584             : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
585                 paint(paint), path(path) {
586     }
operator ()(size_t start,size_t end)587     void operator()(size_t start, size_t end) {
588         uint16_t glyphs[1];
589         for (size_t i = start; i < end; i++) {
590             glyphs[0] = layout.getGlyphId(i);
591             float x = hOffset + layout.getX(i);
592             float y = vOffset + layout.getY(i);
593             canvas->drawTextOnPath(glyphs, 1, path, x, y, paint);
594         }
595     }
596 private:
597     const Layout& layout;
598     Canvas* canvas;
599     float hOffset;
600     float vOffset;
601     const Paint& paint;
602     const SkPath& path;
603 };
604 
drawTextOnPath(Canvas * canvas,const uint16_t * text,int count,int bidiFlags,const SkPath & path,float hOffset,float vOffset,const Paint & paint,TypefaceImpl * typeface)605 static void drawTextOnPath(Canvas* canvas, const uint16_t* text, int count, int bidiFlags,
606                            const SkPath& path, float hOffset, float vOffset,
607                            const Paint& paint, TypefaceImpl* typeface) {
608     Paint paintCopy(paint);
609     Layout layout;
610     MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
611     hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
612 
613     // Set align to left for drawing, as we don't want individual
614     // glyphs centered or right-aligned; the offset above takes
615     // care of all alignment.
616     paintCopy.setTextAlign(Paint::kLeft_Align);
617 
618     DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
619     MinikinUtils::forFontRun(layout, &paintCopy, f);
620 }
621 
drawTextOnPathChars(JNIEnv * env,jobject,jlong canvasHandle,jcharArray text,jint index,jint count,jlong pathHandle,jfloat hOffset,jfloat vOffset,jint bidiFlags,jlong paintHandle,jlong typefaceHandle)622 static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
623                                 jint index, jint count, jlong pathHandle, jfloat hOffset,
624                                 jfloat vOffset, jint bidiFlags, jlong paintHandle,
625                                 jlong typefaceHandle) {
626     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
627     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
628     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
629 
630     jchar* jchars = env->GetCharArrayElements(text, NULL);
631 
632     drawTextOnPath(get_canvas(canvasHandle), jchars + index, count, bidiFlags, *path,
633                    hOffset, vOffset, *paint, typeface);
634 
635     env->ReleaseCharArrayElements(text, jchars, 0);
636 }
637 
drawTextOnPathString(JNIEnv * env,jobject,jlong canvasHandle,jstring text,jlong pathHandle,jfloat hOffset,jfloat vOffset,jint bidiFlags,jlong paintHandle,jlong typefaceHandle)638 static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
639                                  jlong pathHandle, jfloat hOffset, jfloat vOffset,
640                                  jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
641     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
642     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
643     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
644 
645     const jchar* jchars = env->GetStringChars(text, NULL);
646     int count = env->GetStringLength(text);
647 
648     drawTextOnPath(get_canvas(canvasHandle), jchars, count, bidiFlags, *path,
649                    hOffset, vOffset, *paint, typeface);
650 
651     env->ReleaseStringChars(text, jchars);
652 }
653 
setDrawFilter(JNIEnv * env,jobject,jlong canvasHandle,jlong filterHandle)654 static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
655     get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
656 }
657 
freeCaches(JNIEnv * env,jobject)658 static void freeCaches(JNIEnv* env, jobject) {
659     SkGraphics::PurgeFontCache();
660 }
661 
freeTextLayoutCaches(JNIEnv * env,jobject)662 static void freeTextLayoutCaches(JNIEnv* env, jobject) {
663     Layout::purgeCaches();
664 }
665 
666 }; // namespace CanvasJNI
667 
668 static JNINativeMethod gMethods[] = {
669     {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
670     {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
671     {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
672     {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
673     {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
674     {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
675     {"native_save","(JI)I", (void*) CanvasJNI::save},
676     {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
677     {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
678     {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
679     {"native_restore","(JZ)V", (void*) CanvasJNI::restore},
680     {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount},
681     {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
682     {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
683     {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
684     {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
685     {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
686     {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
687     {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
688     {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
689     {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
690     {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
691     {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
692     {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
693     {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
694     {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
695     {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
696     {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
697     {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
698     {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
699     {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
700     {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
701     {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
702     {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
703     {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
704     {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
705     {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
706     {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
707     {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
708     {"nativeDrawBitmapMatrix", "(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
709     {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
710     {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
711     {"nativeDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
712     {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
713     {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
714     {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
715     {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
716     {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
717     {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
718     {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
719     {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
720     {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
721 };
722 
register_android_graphics_Canvas(JNIEnv * env)723 int register_android_graphics_Canvas(JNIEnv* env) {
724     return RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
725 }
726 
727 }; // namespace android
728