blob: 9b5fb3a60119fccf25f8dba79c4fb7c0bd3d445c [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 Reckc294d122015-04-13 15:20:29 -070042static jlong initRaster(JNIEnv* env, jobject, jobject jbitmap) {
43 SkBitmap bitmap;
44 if (jbitmap != NULL) {
45 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
46 }
Derek Sollenberger8872b382014-06-23 14:13:53 -040047 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.
John Reckc294d122015-04-13 15:20:29 -070052static 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);
Derek Sollenberger8872b382014-06-23 14:13:53 -040058}
59
60static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
61 return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
62}
63
64static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
65 return static_cast<jint>(get_canvas(canvasHandle)->width());
66}
67
68static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
69 return static_cast<jint>(get_canvas(canvasHandle)->height());
70}
71
72static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
73 return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
74}
75
76static 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
81static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
82 jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -040083 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -040084 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
88static 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
Chris Craik3891f3a2015-04-02 15:28:08 -070094static void restore(JNIEnv* env, jobject, jlong canvasHandle, jboolean throwOnUnderflow) {
Derek Sollenberger8872b382014-06-23 14:13:53 -040095 Canvas* canvas = get_canvas(canvasHandle);
96 if (canvas->getSaveCount() <= 1) { // cannot restore anymore
Chris Craik3891f3a2015-04-02 15:28:08 -070097 if (throwOnUnderflow) {
98 doThrowISE(env, "Underflow in restore - more restores than saves");
99 }
100 return; // compat behavior - return without throwing
Derek Sollenberger8872b382014-06-23 14:13:53 -0400101 }
102 canvas->restore();
103}
104
Chris Craik3891f3a2015-04-02 15:28:08 -0700105static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount,
106 jboolean throwOnUnderflow) {
Derek Sollenberger8872b382014-06-23 14:13:53 -0400107 Canvas* canvas = get_canvas(canvasHandle);
108 if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
Chris Craik3891f3a2015-04-02 15:28:08 -0700109 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
Derek Sollenberger8872b382014-06-23 14:13:53 -0400114 }
115 canvas->restoreToCount(restoreCount);
116}
117
118static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
119 SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
120 get_canvas(canvasHandle)->getMatrix(matrix);
121}
122
123static 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
128static 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
133static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
134 get_canvas(canvasHandle)->rotate(degrees);
135}
136
137static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
138 get_canvas(canvasHandle)->scale(sx, sy);
139}
140
141static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
142 get_canvas(canvasHandle)->skew(sx, sy);
143}
144
145static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
146 get_canvas(canvasHandle)->translate(dx, dy);
147}
148
149static 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
163static 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
169static 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
175static 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 emptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
179 return emptyClip ? JNI_FALSE : JNI_TRUE;
180}
181
182static 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 emptyClip = get_canvas(canvasHandle)->clipPath(path, op);
187 return emptyClip ? JNI_FALSE : JNI_TRUE;
188}
189
190static 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 emptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
195 return emptyClip ? JNI_FALSE : JNI_TRUE;
196}
197
198static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
Chris Craik1526a452015-03-06 18:42:15 +0000199 SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle);
200 get_canvas(canvasHandle)->drawColor(color, mode);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400201}
202
203static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400204 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400205 get_canvas(canvasHandle)->drawPaint(*paint);
206}
207
208static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
209 jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400210 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400211 get_canvas(canvasHandle)->drawPoint(x, y, *paint);
212}
213
214static 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
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400226 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400227 get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
228}
229
230static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
231 jfloat stopX, jfloat stopY, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400232 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400233 get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
234}
235
236static 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
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400248 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400249 get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
250}
251
252static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
253 jfloat right, jfloat bottom, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400254 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400255 get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
256}
257
258static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
259 jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400260 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400261 get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
262}
263
264static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
265 jfloat radius, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400266 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400267 get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
268}
269
270static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
271 jfloat right, jfloat bottom, jlong paintHandle) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400272 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400273 get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
274}
275
276static 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) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400279 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400280 get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
281 useCenter, *paint);
282}
283
284static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
285 jlong paintHandle) {
286 const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400287 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400288 get_canvas(canvasHandle)->drawPath(*path, *paint);
289}
290
291static 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);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400316 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400317 get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
318 indices, indexCount, *paint);
319}
320
John Reck4bd981e2015-04-15 15:52:10 -0700321static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jobject jbitmap,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400322 jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
323 jint screenDensity, jint bitmapDensity) {
324 Canvas* canvas = get_canvas(canvasHandle);
John Reck4bd981e2015-04-15 15:52:10 -0700325 SkBitmap bitmap;
326 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400327 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400328
329 if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
330 if (screenDensity != 0 && screenDensity != bitmapDensity) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400331 Paint filteredPaint;
Derek Sollenberger8872b382014-06-23 14:13:53 -0400332 if (paint) {
333 filteredPaint = *paint;
334 }
Mike Reed2a1ce8a2015-03-16 11:16:09 -0400335 filteredPaint.setFilterQuality(kLow_SkFilterQuality);
John Reck4bd981e2015-04-15 15:52:10 -0700336 canvas->drawBitmap(bitmap, left, top, &filteredPaint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400337 } else {
John Reck4bd981e2015-04-15 15:52:10 -0700338 canvas->drawBitmap(bitmap, left, top, paint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400339 }
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
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400346 Paint filteredPaint;
Derek Sollenberger8872b382014-06-23 14:13:53 -0400347 if (paint) {
348 filteredPaint = *paint;
349 }
Mike Reed2a1ce8a2015-03-16 11:16:09 -0400350 filteredPaint.setFilterQuality(kLow_SkFilterQuality);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400351
John Reck4bd981e2015-04-15 15:52:10 -0700352 canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400353 canvas->restore();
354 }
355}
356
John Reck4bd981e2015-04-15 15:52:10 -0700357static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400358 jlong matrixHandle, jlong paintHandle) {
Derek Sollenberger8872b382014-06-23 14:13:53 -0400359 const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400360 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
John Reck4bd981e2015-04-15 15:52:10 -0700361 SkBitmap bitmap;
362 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
363 get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400364}
365
John Reck4bd981e2015-04-15 15:52:10 -0700366static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400367 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);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400371 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400372
John Reck4bd981e2015-04-15 15:52:10 -0700373 SkBitmap bitmap;
374 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400375 if (screenDensity != 0 && screenDensity != bitmapDensity) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400376 Paint filteredPaint;
Derek Sollenberger8872b382014-06-23 14:13:53 -0400377 if (paint) {
378 filteredPaint = *paint;
379 }
Mike Reed2a1ce8a2015-03-16 11:16:09 -0400380 filteredPaint.setFilterQuality(kLow_SkFilterQuality);
John Reck4bd981e2015-04-15 15:52:10 -0700381 canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400382 dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
383 } else {
John Reck4bd981e2015-04-15 15:52:10 -0700384 canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400385 dstLeft, dstTop, dstRight, dstBottom, paint);
386 }
387}
388
389static 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;
Derek Sollenberger3d4eed72014-12-04 15:20:29 -0500399 bitmap.setInfo(info);
400 if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) {
Derek Sollenberger8872b382014-06-23 14:13:53 -0400401 return;
402 }
403
404 if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
405 return;
406 }
407
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400408 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400409 get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
410}
411
John Reck4bd981e2015-04-15 15:52:10 -0700412static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400413 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
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400419 const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
John Reck4bd981e2015-04-15 15:52:10 -0700420 SkBitmap bitmap;
421 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
422 get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400423 vertA.ptr(), colorA.ptr(), paint);
424}
425
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400426class DrawTextFunctor {
427public:
428 DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
Tom Hudson8dfaa492014-12-09 15:03:44 -0500429 const SkPaint& paint, float x, float y, MinikinRect& bounds,
430 float totalAdvance)
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400431 : layout(layout), canvas(canvas), glyphs(glyphs), pos(pos), paint(paint),
Tom Hudson8dfaa492014-12-09 15:03:44 -0500432 x(x), y(y), bounds(bounds), totalAdvance(totalAdvance) { }
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400433
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,
Tom Hudson8dfaa492014-12-09 15:03:44 -0500451 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom,
452 totalAdvance);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400453 }
454private:
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;
Tom Hudson8dfaa492014-12-09 15:03:44 -0500463 float totalAdvance;
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400464};
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
471void 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
499void 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;
Behdad Esfahbod63c5c782014-07-25 14:54:46 -0400505 MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400506
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);
Tom Hudson8dfaa492014-12-09 15:03:44 -0500515 if (!canvas->drawTextAbsolutePos()) {
516 bounds.offset(x, y);
517 }
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400518
Tom Hudson8dfaa492014-12-09 15:03:44 -0500519 DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance());
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400520 MinikinUtils::forFontRun(layout, &paint, f);
521
522 drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
523
524 delete[] glyphs;
525 delete[] pos;
526}
527
Derek Sollenberger8872b382014-06-23 14:13:53 -0400528static 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) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400531 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400532 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
533 jchar* jchars = env->GetCharArrayElements(text, NULL);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400534 drawText(get_canvas(canvasHandle), jchars + index, 0, count, count, x, y,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400535 bidiFlags, *paint, typeface);
536 env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
537}
538
539static 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) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400542 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400543 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
544 const int count = end - start;
545 const jchar* jchars = env->GetStringChars(text, NULL);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400546 drawText(get_canvas(canvasHandle), jchars + start, 0, count, count, x, y,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400547 bidiFlags, *paint, typeface);
548 env->ReleaseStringChars(text, jchars);
549}
550
551static 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) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400554 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400555 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);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400559 drawText(get_canvas(canvasHandle), jchars + contextIndex, index - contextIndex, count,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400560 contextCount, x, y, bidiFlags, *paint, typeface);
561 env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
562}
563
564static 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) {
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400568 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400569 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);
Derek Sollenbergeracb40992014-07-21 15:22:10 -0400575 drawText(get_canvas(canvasHandle), jchars + contextStart, start - contextStart, count,
Derek Sollenberger8872b382014-06-23 14:13:53 -0400576 contextCount, x, y, bidiFlags, *paint, typeface);
577 env->ReleaseStringChars(text, jchars);
578}
579
Derek Sollenberger8872b382014-06-23 14:13:53 -0400580class DrawTextOnPathFunctor {
581public:
582 DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400583 float vOffset, const Paint& paint, const SkPath& path)
Derek Sollenberger8872b382014-06-23 14:13:53 -0400584 : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
585 paint(paint), path(path) {
586 }
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 }
596private:
597 const Layout& layout;
598 Canvas* canvas;
599 float hOffset;
600 float vOffset;
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400601 const Paint& paint;
Derek Sollenberger8872b382014-06-23 14:13:53 -0400602 const SkPath& path;
603};
604
605static void drawTextOnPath(Canvas* canvas, const uint16_t* text, int count, int bidiFlags,
606 const SkPath& path, float hOffset, float vOffset,
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400607 const Paint& paint, TypefaceImpl* typeface) {
608 Paint paintCopy(paint);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400609 Layout layout;
Behdad Esfahbod63c5c782014-07-25 14:54:46 -0400610 MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400611 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.
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400616 paintCopy.setTextAlign(Paint::kLeft_Align);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400617
618 DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
619 MinikinUtils::forFontRun(layout, &paintCopy, f);
620}
621
622static 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);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400627 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400628 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
638static 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);
Behdad Esfahbod6ba30b82014-07-15 16:22:32 -0400642 Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Derek Sollenberger8872b382014-06-23 14:13:53 -0400643 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
654static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
655 get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
656}
657
658static void freeCaches(JNIEnv* env, jobject) {
659 SkGraphics::PurgeFontCache();
660}
661
662static void freeTextLayoutCaches(JNIEnv* env, jobject) {
663 Layout::purgeCaches();
664}
665
666}; // namespace CanvasJNI
667
668static JNINativeMethod gMethods[] = {
669 {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
John Reckc294d122015-04-13 15:20:29 -0700670 {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
671 {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
Derek Sollenberger8872b382014-06-23 14:13:53 -0400672 {"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},
Chris Craik3891f3a2015-04-02 15:28:08 -0700679 {"native_restore","(JZ)V", (void*) CanvasJNI::restore},
680 {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount},
Derek Sollenberger8872b382014-06-23 14:13:53 -0400681 {"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},
John Reck4bd981e2015-04-15 15:52:10 -0700707 {"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},
Derek Sollenberger8872b382014-06-23 14:13:53 -0400710 {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
John Reck4bd981e2015-04-15 15:52:10 -0700711 {"nativeDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
Derek Sollenberger8872b382014-06-23 14:13:53 -0400712 {"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
723int register_android_graphics_Canvas(JNIEnv* env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800724 return RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
Derek Sollenberger8872b382014-06-23 14:13:53 -0400725}
726
727}; // namespace android