blob: a2c16097b2bf017cf87fa111381d0be889280cc9 [file] [log] [blame]
Derek Sollenberger8872b382014-06-23 14:13:53 -04001/*
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"
Andreas Gampeed6b9df2014-11-20 22:02:20 -080019#include "core_jni_helpers.h"
Derek Sollenberger8872b382014-06-23 14:13:53 -040020
John Reck849911a2015-01-20 07:51:14 -080021#include <Canvas.h>
Derek Sollenbergeracb40992014-07-21 15:22:10 -040022#include "SkDrawFilter.h"
Derek Sollenberger8872b382014-06-23 14:13:53 -040023#include "SkGraphics.h"
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -040024#include "Paint.h"
Derek Sollenberger8872b382014-06-23 14:13:53 -040025#include "TypefaceImpl.h"
26
Derek Sollenberger8872b382014-06-23 14:13:53 -040027#include "MinikinUtils.h"
28
29namespace android {
30
31namespace CanvasJNI {
32
33static Canvas* get_canvas(jlong canvasHandle) {
34 return reinterpret_cast<Canvas*>(canvasHandle);
35}
36
37static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
38 delete get_canvas(canvasHandle);
39}
40
41// Native wrapper constructor used by Canvas(Bitmap)
John Reck9d4efdf2015-04-17 20:45:40 +000042static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
43 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -040044 return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
45}
46
47// Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
48// optionally copying canvas matrix & clip state.
John Reck9d4efdf2015-04-17 20:45:40 +000049static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
50 jboolean copyState) {
51 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
52 get_canvas(canvasHandle)->setBitmap(bitmap, copyState);
Derek Sollenberger8872b382014-06-23 14:13:53 -040053}
54
55static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
56 return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
57}
58
59static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
60 return static_cast<jint>(get_canvas(canvasHandle)->width());
61}
62
63static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
64 return static_cast<jint>(get_canvas(canvasHandle)->height());
65}
66
67static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
68 return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
69}
70
71static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
72 SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
73 return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
74}
75
76static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
77 jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -040078 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -040079 SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
80 return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
81}
82
83static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
84 jfloat r, jfloat b, jint alpha, jint flagsHandle) {
85 SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
86 return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
87}
88
Chris Craik3891f3a2015-04-02 15:28:08 -070089static void restore(JNIEnv* env, jobject, jlong canvasHandle, jboolean throwOnUnderflow) {
Derek Sollenberger8872b382014-06-23 14:13:53 -040090 Canvas* canvas = get_canvas(canvasHandle);
91 if (canvas->getSaveCount() <= 1) { // cannot restore anymore
Chris Craik3891f3a2015-04-02 15:28:08 -070092 if (throwOnUnderflow) {
93 doThrowISE(env, "Underflow in restore - more restores than saves");
94 }
95 return; // compat behavior - return without throwing
Derek Sollenberger8872b382014-06-23 14:13:53 -040096 }
97 canvas->restore();
98}
99
Chris Craik3891f3a2015-04-02 15:28:08 -0700100static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount,
101 jboolean throwOnUnderflow) {
Derek Sollenberger8872b382014-06-23 14:13:53 -0400102 Canvas* canvas = get_canvas(canvasHandle);
103 if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
Chris Craik3891f3a2015-04-02 15:28:08 -0700104 if (throwOnUnderflow) {
105 doThrowIAE(env, "Underflow in restoreToCount - more restores than saves");
106 return;
107 }
108 restoreCount = 1; // compat behavior - restore as far as possible
Derek Sollenberger8872b382014-06-23 14:13:53 -0400109 }
110 canvas->restoreToCount(restoreCount);
111}
112
113static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
114 SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
115 get_canvas(canvasHandle)->getMatrix(matrix);
116}
117
118static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
119 const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
120 get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
121}
122
123static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
124 const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
125 get_canvas(canvasHandle)->concat(*matrix);
126}
127
128static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
129 get_canvas(canvasHandle)->rotate(degrees);
130}
131
132static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
133 get_canvas(canvasHandle)->scale(sx, sy);
134}
135
136static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
137 get_canvas(canvasHandle)->skew(sx, sy);
138}
139
140static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
141 get_canvas(canvasHandle)->translate(dx, dy);
142}
143
144static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
145 SkRect r;
146 SkIRect ir;
147 bool result = get_canvas(canvasHandle)->getClipBounds(&r);
148
149 if (!result) {
150 r.setEmpty();
151 }
152 r.round(&ir);
153
154 (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
155 return result ? JNI_TRUE : JNI_FALSE;
156}
157
158static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
159 jfloat left, jfloat top, jfloat right, jfloat bottom) {
160 bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
161 return result ? JNI_TRUE : JNI_FALSE;
162}
163
164static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
165 SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
166 bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
167 return result ? JNI_TRUE : JNI_FALSE;
168}
169
170static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
171 jfloat r, jfloat b, jint opHandle) {
172 SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
173 bool emptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
174 return emptyClip ? JNI_FALSE : JNI_TRUE;
175}
176
177static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
178 jint opHandle) {
179 SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
180 SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
181 bool emptyClip = get_canvas(canvasHandle)->clipPath(path, op);
182 return emptyClip ? JNI_FALSE : JNI_TRUE;
183}
184
185static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
186 jint opHandle) {
187 SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
188 SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
189 bool emptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
190 return emptyClip ? JNI_FALSE : JNI_TRUE;
191}
192
193static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
Chris Craik1526a452015-03-06 18:42:15 +0000194 SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle);
195 get_canvas(canvasHandle)->drawColor(color, mode);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400196}
197
198static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400199 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400200 get_canvas(canvasHandle)->drawPaint(*paint);
201}
202
203static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
204 jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400205 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400206 get_canvas(canvasHandle)->drawPoint(x, y, *paint);
207}
208
209static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
210 jint offset, jint count, jlong paintHandle) {
211 NPE_CHECK_RETURN_VOID(env, jptsArray);
212 AutoJavaFloatArray autoPts(env, jptsArray);
213 float* floats = autoPts.ptr();
214 const int length = autoPts.length();
215
216 if ((offset | count) < 0 || offset + count > length) {
217 doThrowAIOOBE(env);
218 return;
219 }
220
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400221 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400222 get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
223}
224
225static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
226 jfloat stopX, jfloat stopY, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400227 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400228 get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
229}
230
231static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
232 jint offset, jint count, jlong paintHandle) {
233 NPE_CHECK_RETURN_VOID(env, jptsArray);
234 AutoJavaFloatArray autoPts(env, jptsArray);
235 float* floats = autoPts.ptr();
236 const int length = autoPts.length();
237
238 if ((offset | count) < 0 || offset + count > length) {
239 doThrowAIOOBE(env);
240 return;
241 }
242
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400243 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400244 get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
245}
246
247static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
248 jfloat right, jfloat bottom, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400249 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400250 get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
251}
252
253static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
254 jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400255 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400256 get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
257}
258
259static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
260 jfloat radius, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400261 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400262 get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
263}
264
265static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
266 jfloat right, jfloat bottom, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400267 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400268 get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
269}
270
271static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
272 jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
273 jboolean useCenter, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400274 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400275 get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
276 useCenter, *paint);
277}
278
279static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
280 jlong paintHandle) {
281 const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400282 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400283 get_canvas(canvasHandle)->drawPath(*path, *paint);
284}
285
286static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
287 jint modeHandle, jint vertexCount,
288 jfloatArray jverts, jint vertIndex,
289 jfloatArray jtexs, jint texIndex,
290 jintArray jcolors, jint colorIndex,
291 jshortArray jindices, jint indexIndex,
292 jint indexCount, jlong paintHandle) {
293 AutoJavaFloatArray vertA(env, jverts, vertIndex + vertexCount);
294 AutoJavaFloatArray texA(env, jtexs, texIndex + vertexCount);
295 AutoJavaIntArray colorA(env, jcolors, colorIndex + vertexCount);
296 AutoJavaShortArray indexA(env, jindices, indexIndex + indexCount);
297
298 const float* verts = vertA.ptr() + vertIndex;
299 const float* texs = texA.ptr() + vertIndex;
300 const int* colors = NULL;
301 const uint16_t* indices = NULL;
302
303 if (jcolors != NULL) {
304 colors = colorA.ptr() + colorIndex;
305 }
306 if (jindices != NULL) {
307 indices = (const uint16_t*)(indexA.ptr() + indexIndex);
308 }
309
310 SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400311 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400312 get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
313 indices, indexCount, *paint);
314}
315
John Reck1ff961d2015-04-17 20:45:15 +0000316static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jlong bitmapHandle,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400317 jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
318 jint screenDensity, jint bitmapDensity) {
319 Canvas* canvas = get_canvas(canvasHandle);
John Reck1ff961d2015-04-17 20:45:15 +0000320 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400321 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400322
323 if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
324 if (screenDensity != 0 && screenDensity != bitmapDensity) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400325 Paint filteredPaint;
Derek Sollenberger8872b382014-06-23 14:13:53 -0400326 if (paint) {
327 filteredPaint = *paint;
328 }
Mike Reed2a1ce8a2015-03-16 11:16:09 -0400329 filteredPaint.setFilterQuality(kLow_SkFilterQuality);
John Reck1ff961d2015-04-17 20:45:15 +0000330 canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400331 } else {
John Reck1ff961d2015-04-17 20:45:15 +0000332 canvas->drawBitmap(*bitmap, left, top, paint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400333 }
334 } else {
335 canvas->save(SkCanvas::kMatrixClip_SaveFlag);
336 SkScalar scale = canvasDensity / (float)bitmapDensity;
337 canvas->translate(left, top);
338 canvas->scale(scale, scale);
339
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400340 Paint filteredPaint;
Derek Sollenberger8872b382014-06-23 14:13:53 -0400341 if (paint) {
342 filteredPaint = *paint;
343 }
Mike Reed2a1ce8a2015-03-16 11:16:09 -0400344 filteredPaint.setFilterQuality(kLow_SkFilterQuality);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400345
John Reck1ff961d2015-04-17 20:45:15 +0000346 canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400347 canvas->restore();
348 }
349}
350
John Reck1ff961d2015-04-17 20:45:15 +0000351static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400352 jlong matrixHandle, jlong paintHandle) {
John Reck1ff961d2015-04-17 20:45:15 +0000353 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400354 const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400355 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
John Reck1ff961d2015-04-17 20:45:15 +0000356 get_canvas(canvasHandle)->drawBitmap(*bitmap, *matrix, paint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400357}
358
John Reck1ff961d2015-04-17 20:45:15 +0000359static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400360 float srcLeft, float srcTop, float srcRight, float srcBottom,
361 float dstLeft, float dstTop, float dstRight, float dstBottom,
362 jlong paintHandle, jint screenDensity, jint bitmapDensity) {
363 Canvas* canvas = get_canvas(canvasHandle);
John Reck1ff961d2015-04-17 20:45:15 +0000364 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400365 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400366
367 if (screenDensity != 0 && screenDensity != bitmapDensity) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400368 Paint filteredPaint;
Derek Sollenberger8872b382014-06-23 14:13:53 -0400369 if (paint) {
370 filteredPaint = *paint;
371 }
Mike Reed2a1ce8a2015-03-16 11:16:09 -0400372 filteredPaint.setFilterQuality(kLow_SkFilterQuality);
John Reck1ff961d2015-04-17 20:45:15 +0000373 canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400374 dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
375 } else {
John Reck1ff961d2015-04-17 20:45:15 +0000376 canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400377 dstLeft, dstTop, dstRight, dstBottom, paint);
378 }
379}
380
381static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
382 jintArray jcolors, jint offset, jint stride,
383 jfloat x, jfloat y, jint width, jint height,
384 jboolean hasAlpha, jlong paintHandle) {
385 // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
386 // correct the alphaType to kOpaque_SkAlphaType.
387 SkImageInfo info = SkImageInfo::Make(width, height,
388 hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
389 kPremul_SkAlphaType);
390 SkBitmap bitmap;
Derek Sollenberger3d4eed72014-12-04 15:20:29 -0500391 bitmap.setInfo(info);
392 if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) {
Derek Sollenberger8872b382014-06-23 14:13:53 -0400393 return;
394 }
395
396 if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
397 return;
398 }
399
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400400 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400401 get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
402}
403
John Reck1ff961d2015-04-17 20:45:15 +0000404static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400405 jint meshWidth, jint meshHeight, jfloatArray jverts,
406 jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
407 const int ptCount = (meshWidth + 1) * (meshHeight + 1);
408 AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
409 AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
410
John Reck1ff961d2015-04-17 20:45:15 +0000411 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400412 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
John Reck1ff961d2015-04-17 20:45:15 +0000413 get_canvas(canvasHandle)->drawBitmapMesh(*bitmap, meshWidth, meshHeight,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400414 vertA.ptr(), colorA.ptr(), paint);
415}
416
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400417class DrawTextFunctor {
418public:
419 DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
Tom Hudson8dfaa492014-12-09 15:03:44 -0500420 const SkPaint& paint, float x, float y, MinikinRect& bounds,
421 float totalAdvance)
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400422 : layout(layout), canvas(canvas), glyphs(glyphs), pos(pos), paint(paint),
Tom Hudson8dfaa492014-12-09 15:03:44 -0500423 x(x), y(y), bounds(bounds), totalAdvance(totalAdvance) { }
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400424
425 void operator()(size_t start, size_t end) {
426 if (canvas->drawTextAbsolutePos()) {
427 for (size_t i = start; i < end; i++) {
428 glyphs[i] = layout.getGlyphId(i);
429 pos[2 * i] = x + layout.getX(i);
430 pos[2 * i + 1] = y + layout.getY(i);
431 }
432 } else {
433 for (size_t i = start; i < end; i++) {
434 glyphs[i] = layout.getGlyphId(i);
435 pos[2 * i] = layout.getX(i);
436 pos[2 * i + 1] = layout.getY(i);
437 }
438 }
439
440 size_t glyphCount = end - start;
441 canvas->drawText(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
Tom Hudson8dfaa492014-12-09 15:03:44 -0500442 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom,
443 totalAdvance);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400444 }
445private:
446 const Layout& layout;
447 Canvas* canvas;
448 uint16_t* glyphs;
449 float* pos;
450 const SkPaint& paint;
451 float x;
452 float y;
453 MinikinRect& bounds;
Tom Hudson8dfaa492014-12-09 15:03:44 -0500454 float totalAdvance;
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400455};
456
457// Same values used by Skia
458#define kStdStrikeThru_Offset (-6.0f / 21.0f)
459#define kStdUnderline_Offset (1.0f / 9.0f)
460#define kStdUnderline_Thickness (1.0f / 18.0f)
461
462void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) {
463 uint32_t flags;
464 SkDrawFilter* drawFilter = canvas->getDrawFilter();
465 if (drawFilter) {
466 SkPaint paintCopy(paint);
467 drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
468 flags = paintCopy.getFlags();
469 } else {
470 flags = paint.getFlags();
471 }
472 if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
473 SkScalar left = x;
474 SkScalar right = x + length;
475 float textSize = paint.getTextSize();
476 float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
477 if (flags & SkPaint::kUnderlineText_Flag) {
478 SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
479 SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
480 canvas->drawRect(left, top, right, bottom, paint);
481 }
482 if (flags & SkPaint::kStrikeThruText_Flag) {
483 SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
484 SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
485 canvas->drawRect(left, top, right, bottom, paint);
486 }
487 }
488}
489
490void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
491 float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
492 // minikin may modify the original paint
493 Paint paint(origPaint);
494
495 Layout layout;
Behdad Esfahbod63c5c782014-07-25 14:54:46 -0400496 MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400497
498 size_t nGlyphs = layout.nGlyphs();
499 uint16_t* glyphs = new uint16_t[nGlyphs];
500 float* pos = new float[nGlyphs * 2];
501
502 x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
503
504 MinikinRect bounds;
505 layout.getBounds(&bounds);
Tom Hudson8dfaa492014-12-09 15:03:44 -0500506 if (!canvas->drawTextAbsolutePos()) {
507 bounds.offset(x, y);
508 }
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400509
Tom Hudson8dfaa492014-12-09 15:03:44 -0500510 DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance());
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400511 MinikinUtils::forFontRun(layout, &paint, f);
512
513 drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
514
515 delete[] glyphs;
516 delete[] pos;
517}
518
Derek Sollenberger8872b382014-06-23 14:13:53 -0400519static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
520 jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
521 jlong paintHandle, jlong typefaceHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400522 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400523 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
524 jchar* jchars = env->GetCharArrayElements(text, NULL);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400525 drawText(get_canvas(canvasHandle), jchars + index, 0, count, count, x, y,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400526 bidiFlags, *paint, typeface);
527 env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
528}
529
530static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
531 jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
532 jlong paintHandle, jlong typefaceHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400533 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400534 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
535 const int count = end - start;
536 const jchar* jchars = env->GetStringChars(text, NULL);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400537 drawText(get_canvas(canvasHandle), jchars + start, 0, count, count, x, y,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400538 bidiFlags, *paint, typeface);
539 env->ReleaseStringChars(text, jchars);
540}
541
542static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
543 jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
544 jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400545 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400546 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
547
548 const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
549 jchar* jchars = env->GetCharArrayElements(text, NULL);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400550 drawText(get_canvas(canvasHandle), jchars + contextIndex, index - contextIndex, count,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400551 contextCount, x, y, bidiFlags, *paint, typeface);
552 env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
553}
554
555static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
556 jint start, jint end, jint contextStart, jint contextEnd,
557 jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
558 jlong typefaceHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400559 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400560 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
561
562 int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
563 jint count = end - start;
564 jint contextCount = contextEnd - contextStart;
565 const jchar* jchars = env->GetStringChars(text, NULL);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400566 drawText(get_canvas(canvasHandle), jchars + contextStart, start - contextStart, count,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400567 contextCount, x, y, bidiFlags, *paint, typeface);
568 env->ReleaseStringChars(text, jchars);
569}
570
Derek Sollenberger8872b382014-06-23 14:13:53 -0400571class DrawTextOnPathFunctor {
572public:
573 DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400574 float vOffset, const Paint& paint, const SkPath& path)
Derek Sollenberger8872b382014-06-23 14:13:53 -0400575 : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
576 paint(paint), path(path) {
577 }
578 void operator()(size_t start, size_t end) {
579 uint16_t glyphs[1];
580 for (size_t i = start; i < end; i++) {
581 glyphs[0] = layout.getGlyphId(i);
582 float x = hOffset + layout.getX(i);
583 float y = vOffset + layout.getY(i);
584 canvas->drawTextOnPath(glyphs, 1, path, x, y, paint);
585 }
586 }
587private:
588 const Layout& layout;
589 Canvas* canvas;
590 float hOffset;
591 float vOffset;
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400592 const Paint& paint;
Derek Sollenberger8872b382014-06-23 14:13:53 -0400593 const SkPath& path;
594};
595
596static void drawTextOnPath(Canvas* canvas, const uint16_t* text, int count, int bidiFlags,
597 const SkPath& path, float hOffset, float vOffset,
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400598 const Paint& paint, TypefaceImpl* typeface) {
599 Paint paintCopy(paint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400600 Layout layout;
Behdad Esfahbod63c5c782014-07-25 14:54:46 -0400601 MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400602 hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
603
604 // Set align to left for drawing, as we don't want individual
605 // glyphs centered or right-aligned; the offset above takes
606 // care of all alignment.
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400607 paintCopy.setTextAlign(Paint::kLeft_Align);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400608
609 DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
610 MinikinUtils::forFontRun(layout, &paintCopy, f);
611}
612
613static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
614 jint index, jint count, jlong pathHandle, jfloat hOffset,
615 jfloat vOffset, jint bidiFlags, jlong paintHandle,
616 jlong typefaceHandle) {
617 SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400618 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400619 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
620
621 jchar* jchars = env->GetCharArrayElements(text, NULL);
622
623 drawTextOnPath(get_canvas(canvasHandle), jchars + index, count, bidiFlags, *path,
624 hOffset, vOffset, *paint, typeface);
625
626 env->ReleaseCharArrayElements(text, jchars, 0);
627}
628
629static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
630 jlong pathHandle, jfloat hOffset, jfloat vOffset,
631 jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
632 SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400633 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400634 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
635
636 const jchar* jchars = env->GetStringChars(text, NULL);
637 int count = env->GetStringLength(text);
638
639 drawTextOnPath(get_canvas(canvasHandle), jchars, count, bidiFlags, *path,
640 hOffset, vOffset, *paint, typeface);
641
642 env->ReleaseStringChars(text, jchars);
643}
644
645static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
646 get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
647}
648
649static void freeCaches(JNIEnv* env, jobject) {
650 SkGraphics::PurgeFontCache();
651}
652
653static void freeTextLayoutCaches(JNIEnv* env, jobject) {
654 Layout::purgeCaches();
655}
656
657}; // namespace CanvasJNI
658
659static JNINativeMethod gMethods[] = {
660 {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
John Reck9d4efdf2015-04-17 20:45:40 +0000661 {"initRaster", "(J)J", (void*) CanvasJNI::initRaster},
662 {"native_setBitmap", "(JJZ)V", (void*) CanvasJNI::setBitmap},
Derek Sollenberger8872b382014-06-23 14:13:53 -0400663 {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
664 {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
665 {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
666 {"native_save","(JI)I", (void*) CanvasJNI::save},
667 {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
668 {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
669 {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
Chris Craik3891f3a2015-04-02 15:28:08 -0700670 {"native_restore","(JZ)V", (void*) CanvasJNI::restore},
671 {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount},
Derek Sollenberger8872b382014-06-23 14:13:53 -0400672 {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
673 {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
674 {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
675 {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
676 {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
677 {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
678 {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
679 {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
680 {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
681 {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
682 {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
683 {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
684 {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
685 {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
686 {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
687 {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
688 {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
689 {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
690 {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
691 {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
692 {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
693 {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
694 {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
695 {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
696 {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
697 {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
John Reck1ff961d2015-04-17 20:45:15 +0000698 {"native_drawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
699 {"nativeDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
700 {"native_drawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
Derek Sollenberger8872b382014-06-23 14:13:53 -0400701 {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
John Reck1ff961d2015-04-17 20:45:15 +0000702 {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
Derek Sollenberger8872b382014-06-23 14:13:53 -0400703 {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
704 {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
705 {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
706 {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
707 {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
708 {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
709 {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
710 {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
711 {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
712};
713
714int register_android_graphics_Canvas(JNIEnv* env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800715 return RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
Derek Sollenberger8872b382014-06-23 14:13:53 -0400716}
717
718}; // namespace android