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