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