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