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