1 /* 2 * Copyright (C) 2016 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 package android.graphics; 18 19 import android.annotation.ColorInt; 20 import android.annotation.ColorLong; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.Size; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.graphics.Canvas.VertexMode; 26 import android.graphics.text.MeasuredText; 27 import android.text.GraphicsOperations; 28 import android.text.MeasuredParagraph; 29 import android.text.PrecomputedText; 30 import android.text.SpannableString; 31 import android.text.SpannedString; 32 import android.text.TextUtils; 33 34 /** 35 * This class is a base class for Canvas's drawing operations. Any modifications here 36 * should be accompanied by a similar modification to {@link BaseRecordingCanvas}. 37 * 38 * The purpose of this class is to minimize the cost of deciding between regular JNI 39 * and @FastNative JNI to just the virtual call that Canvas already has. 40 * 41 * @hide 42 */ 43 public abstract class BaseCanvas { 44 /** 45 * Should only be assigned in constructors (or setBitmap if software canvas), 46 * freed by NativeAllocation. 47 * @hide 48 */ 49 @UnsupportedAppUsage 50 protected long mNativeCanvasWrapper; 51 52 /** 53 * Used to determine when compatibility scaling is in effect. 54 * @hide 55 */ 56 protected int mScreenDensity = Bitmap.DENSITY_NONE; 57 58 /** 59 * @hide 60 */ 61 protected int mDensity = Bitmap.DENSITY_NONE; 62 private boolean mAllowHwBitmapsInSwMode = false; 63 throwIfCannotDraw(Bitmap bitmap)64 protected void throwIfCannotDraw(Bitmap bitmap) { 65 if (bitmap.isRecycled()) { 66 throw new RuntimeException("Canvas: trying to use a recycled bitmap " + bitmap); 67 } 68 if (!bitmap.isPremultiplied() && bitmap.getConfig() == Bitmap.Config.ARGB_8888 && 69 bitmap.hasAlpha()) { 70 throw new RuntimeException("Canvas: trying to use a non-premultiplied bitmap " 71 + bitmap); 72 } 73 throwIfHwBitmapInSwMode(bitmap); 74 } 75 checkRange(int length, int offset, int count)76 protected final static void checkRange(int length, int offset, int count) { 77 if ((offset | count) < 0 || offset + count > length) { 78 throw new ArrayIndexOutOfBoundsException(); 79 } 80 } 81 isHardwareAccelerated()82 public boolean isHardwareAccelerated() { 83 return false; 84 } 85 86 // --------------------------------------------------------------------------- 87 // Drawing methods 88 // These are also implemented in RecordingCanvas so that we can 89 // selectively apply on them 90 // Everything below here is copy/pasted from Canvas.java 91 // The JNI registration is handled by android_view_Canvas.cpp 92 // --------------------------------------------------------------------------- 93 drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)94 public void drawArc(float left, float top, float right, float bottom, float startAngle, 95 float sweepAngle, boolean useCenter, @NonNull Paint paint) { 96 throwIfHasHwBitmapInSwMode(paint); 97 nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle, 98 useCenter, paint.getNativeInstance()); 99 } 100 drawArc(@onNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)101 public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, 102 @NonNull Paint paint) { 103 throwIfHasHwBitmapInSwMode(paint); 104 drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter, 105 paint); 106 } 107 drawARGB(int a, int r, int g, int b)108 public void drawARGB(int a, int r, int g, int b) { 109 drawColor(Color.argb(a, r, g, b)); 110 } 111 drawBitmap(@onNull Bitmap bitmap, float left, float top, @Nullable Paint paint)112 public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) { 113 throwIfCannotDraw(bitmap); 114 throwIfHasHwBitmapInSwMode(paint); 115 nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top, 116 paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity, 117 bitmap.mDensity); 118 } 119 drawBitmap(@onNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint)120 public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) { 121 throwIfHasHwBitmapInSwMode(paint); 122 nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), matrix.ni(), 123 paint != null ? paint.getNativeInstance() : 0); 124 } 125 drawBitmap(@onNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, @Nullable Paint paint)126 public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, 127 @Nullable Paint paint) { 128 if (dst == null) { 129 throw new NullPointerException(); 130 } 131 throwIfCannotDraw(bitmap); 132 throwIfHasHwBitmapInSwMode(paint); 133 final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); 134 135 int left, top, right, bottom; 136 if (src == null) { 137 left = top = 0; 138 right = bitmap.getWidth(); 139 bottom = bitmap.getHeight(); 140 } else { 141 left = src.left; 142 right = src.right; 143 top = src.top; 144 bottom = src.bottom; 145 } 146 147 nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top, right, bottom, 148 dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity, 149 bitmap.mDensity); 150 } 151 drawBitmap(@onNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, @Nullable Paint paint)152 public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, 153 @Nullable Paint paint) { 154 if (dst == null) { 155 throw new NullPointerException(); 156 } 157 throwIfCannotDraw(bitmap); 158 throwIfHasHwBitmapInSwMode(paint); 159 final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); 160 161 float left, top, right, bottom; 162 if (src == null) { 163 left = top = 0; 164 right = bitmap.getWidth(); 165 bottom = bitmap.getHeight(); 166 } else { 167 left = src.left; 168 right = src.right; 169 top = src.top; 170 bottom = src.bottom; 171 } 172 173 nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top, right, bottom, 174 dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity, 175 bitmap.mDensity); 176 } 177 178 @Deprecated drawBitmap(@onNull int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, @Nullable Paint paint)179 public void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y, 180 int width, int height, boolean hasAlpha, @Nullable Paint paint) { 181 // check for valid input 182 if (width < 0) { 183 throw new IllegalArgumentException("width must be >= 0"); 184 } 185 if (height < 0) { 186 throw new IllegalArgumentException("height must be >= 0"); 187 } 188 if (Math.abs(stride) < width) { 189 throw new IllegalArgumentException("abs(stride) must be >= width"); 190 } 191 int lastScanline = offset + (height - 1) * stride; 192 int length = colors.length; 193 if (offset < 0 || (offset + width > length) || lastScanline < 0 194 || (lastScanline + width > length)) { 195 throw new ArrayIndexOutOfBoundsException(); 196 } 197 throwIfHasHwBitmapInSwMode(paint); 198 // quick escape if there's nothing to draw 199 if (width == 0 || height == 0) { 200 return; 201 } 202 // punch down to native for the actual draw 203 nDrawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha, 204 paint != null ? paint.getNativeInstance() : 0); 205 } 206 207 @Deprecated drawBitmap(@onNull int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasAlpha, @Nullable Paint paint)208 public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y, 209 int width, int height, boolean hasAlpha, @Nullable Paint paint) { 210 // call through to the common float version 211 drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, 212 hasAlpha, paint); 213 } 214 drawBitmapMesh(@onNull Bitmap bitmap, int meshWidth, int meshHeight, @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset, @Nullable Paint paint)215 public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight, 216 @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset, 217 @Nullable Paint paint) { 218 if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) { 219 throw new ArrayIndexOutOfBoundsException(); 220 } 221 throwIfHasHwBitmapInSwMode(paint); 222 if (meshWidth == 0 || meshHeight == 0) { 223 return; 224 } 225 int count = (meshWidth + 1) * (meshHeight + 1); 226 // we mul by 2 since we need two floats per vertex 227 checkRange(verts.length, vertOffset, count * 2); 228 if (colors != null) { 229 // no mul by 2, since we need only 1 color per vertex 230 checkRange(colors.length, colorOffset, count); 231 } 232 nDrawBitmapMesh(mNativeCanvasWrapper, bitmap.getNativeInstance(), meshWidth, meshHeight, 233 verts, vertOffset, colors, colorOffset, 234 paint != null ? paint.getNativeInstance() : 0); 235 } 236 drawCircle(float cx, float cy, float radius, @NonNull Paint paint)237 public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) { 238 throwIfHasHwBitmapInSwMode(paint); 239 nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance()); 240 } 241 drawColor(@olorInt int color)242 public void drawColor(@ColorInt int color) { 243 nDrawColor(mNativeCanvasWrapper, color, BlendMode.SRC_OVER.getXfermode().porterDuffMode); 244 } 245 drawColor(@olorInt int color, @NonNull PorterDuff.Mode mode)246 public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) { 247 nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt); 248 } 249 250 /** 251 * Make lint happy. 252 * See {@link Canvas#drawColor(int, BlendMode)} 253 */ drawColor(@olorInt int color, @NonNull BlendMode mode)254 public void drawColor(@ColorInt int color, @NonNull BlendMode mode) { 255 nDrawColor(mNativeCanvasWrapper, color, mode.getXfermode().porterDuffMode); 256 } 257 258 /** 259 * Make lint happy. 260 * See {@link Canvas#drawColor(long, BlendMode)} 261 */ drawColor(@olorLong long color, @NonNull BlendMode mode)262 public void drawColor(@ColorLong long color, @NonNull BlendMode mode) { 263 ColorSpace cs = Color.colorSpace(color); 264 nDrawColor(mNativeCanvasWrapper, cs.getNativeInstance(), color, 265 mode.getXfermode().porterDuffMode); 266 } 267 drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint)268 public void drawLine(float startX, float startY, float stopX, float stopY, 269 @NonNull Paint paint) { 270 throwIfHasHwBitmapInSwMode(paint); 271 nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance()); 272 } 273 drawLines(@izemultiple = 4) @onNull float[] pts, int offset, int count, @NonNull Paint paint)274 public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count, 275 @NonNull Paint paint) { 276 throwIfHasHwBitmapInSwMode(paint); 277 nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); 278 } 279 drawLines(@izemultiple = 4) @onNull float[] pts, @NonNull Paint paint)280 public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) { 281 throwIfHasHwBitmapInSwMode(paint); 282 drawLines(pts, 0, pts.length, paint); 283 } 284 drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)285 public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) { 286 throwIfHasHwBitmapInSwMode(paint); 287 nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); 288 } 289 drawOval(@onNull RectF oval, @NonNull Paint paint)290 public void drawOval(@NonNull RectF oval, @NonNull Paint paint) { 291 if (oval == null) { 292 throw new NullPointerException(); 293 } 294 throwIfHasHwBitmapInSwMode(paint); 295 drawOval(oval.left, oval.top, oval.right, oval.bottom, paint); 296 } 297 drawPaint(@onNull Paint paint)298 public void drawPaint(@NonNull Paint paint) { 299 nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance()); 300 } 301 drawPatch(@onNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint)302 public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) { 303 Bitmap bitmap = patch.getBitmap(); 304 throwIfCannotDraw(bitmap); 305 throwIfHasHwBitmapInSwMode(paint); 306 final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); 307 nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, 308 dst.left, dst.top, dst.right, dst.bottom, nativePaint, 309 mDensity, patch.getDensity()); 310 } 311 drawPatch(@onNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint)312 public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) { 313 Bitmap bitmap = patch.getBitmap(); 314 throwIfCannotDraw(bitmap); 315 throwIfHasHwBitmapInSwMode(paint); 316 final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); 317 nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, 318 dst.left, dst.top, dst.right, dst.bottom, nativePaint, 319 mDensity, patch.getDensity()); 320 } 321 drawPath(@onNull Path path, @NonNull Paint paint)322 public void drawPath(@NonNull Path path, @NonNull Paint paint) { 323 throwIfHasHwBitmapInSwMode(paint); 324 if (path.isSimplePath && path.rects != null) { 325 nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance()); 326 } else { 327 nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance()); 328 } 329 } 330 drawPoint(float x, float y, @NonNull Paint paint)331 public void drawPoint(float x, float y, @NonNull Paint paint) { 332 throwIfHasHwBitmapInSwMode(paint); 333 nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance()); 334 } 335 drawPoints(@izemultiple = 2) float[] pts, int offset, int count, @NonNull Paint paint)336 public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count, 337 @NonNull Paint paint) { 338 throwIfHasHwBitmapInSwMode(paint); 339 nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); 340 } 341 drawPoints(@izemultiple = 2) @onNull float[] pts, @NonNull Paint paint)342 public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) { 343 throwIfHasHwBitmapInSwMode(paint); 344 drawPoints(pts, 0, pts.length, paint); 345 } 346 347 @Deprecated drawPosText(@onNull char[] text, int index, int count, @NonNull @Size(multiple = 2) float[] pos, @NonNull Paint paint)348 public void drawPosText(@NonNull char[] text, int index, int count, 349 @NonNull @Size(multiple = 2) float[] pos, 350 @NonNull Paint paint) { 351 if (index < 0 || index + count > text.length || count * 2 > pos.length) { 352 throw new IndexOutOfBoundsException(); 353 } 354 throwIfHasHwBitmapInSwMode(paint); 355 for (int i = 0; i < count; i++) { 356 drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint); 357 } 358 } 359 360 @Deprecated drawPosText(@onNull String text, @NonNull @Size(multiple = 2) float[] pos, @NonNull Paint paint)361 public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos, 362 @NonNull Paint paint) { 363 throwIfHasHwBitmapInSwMode(paint); 364 drawPosText(text.toCharArray(), 0, text.length(), pos, paint); 365 } 366 drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)367 public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) { 368 throwIfHasHwBitmapInSwMode(paint); 369 nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); 370 } 371 drawRect(@onNull Rect r, @NonNull Paint paint)372 public void drawRect(@NonNull Rect r, @NonNull Paint paint) { 373 throwIfHasHwBitmapInSwMode(paint); 374 drawRect(r.left, r.top, r.right, r.bottom, paint); 375 } 376 drawRect(@onNull RectF rect, @NonNull Paint paint)377 public void drawRect(@NonNull RectF rect, @NonNull Paint paint) { 378 throwIfHasHwBitmapInSwMode(paint); 379 nDrawRect(mNativeCanvasWrapper, 380 rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance()); 381 } 382 drawRGB(int r, int g, int b)383 public void drawRGB(int r, int g, int b) { 384 drawColor(Color.rgb(r, g, b)); 385 } 386 drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint)387 public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, 388 @NonNull Paint paint) { 389 throwIfHasHwBitmapInSwMode(paint); 390 nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, 391 paint.getNativeInstance()); 392 } 393 drawRoundRect(@onNull RectF rect, float rx, float ry, @NonNull Paint paint)394 public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { 395 throwIfHasHwBitmapInSwMode(paint); 396 drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint); 397 } 398 399 /** 400 * Make lint happy. 401 * See {@link Canvas#drawDoubleRoundRect(RectF, float, float, RectF, float, float, Paint)} 402 */ drawDoubleRoundRect(@onNull RectF outer, float outerRx, float outerRy, @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint)403 public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy, 404 @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) { 405 throwIfHasHwBitmapInSwMode(paint); 406 float outerLeft = outer.left; 407 float outerTop = outer.top; 408 float outerRight = outer.right; 409 float outerBottom = outer.bottom; 410 411 float innerLeft = inner.left; 412 float innerTop = inner.top; 413 float innerRight = inner.right; 414 float innerBottom = inner.bottom; 415 nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, outerBottom, 416 outerRx, outerRy, innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, 417 paint.getNativeInstance()); 418 } 419 420 /** 421 * Make lint happy. 422 * See {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)} 423 */ drawDoubleRoundRect(@onNull RectF outer, @NonNull float[] outerRadii, @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint)424 public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii, 425 @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) { 426 throwIfHasHwBitmapInSwMode(paint); 427 if (innerRadii == null || outerRadii == null 428 || innerRadii.length != 8 || outerRadii.length != 8) { 429 throw new IllegalArgumentException("Both inner and outer radii arrays must contain " 430 + "exactly 8 values"); 431 } 432 float outerLeft = outer.left; 433 float outerTop = outer.top; 434 float outerRight = outer.right; 435 float outerBottom = outer.bottom; 436 437 float innerLeft = inner.left; 438 float innerTop = inner.top; 439 float innerRight = inner.right; 440 float innerBottom = inner.bottom; 441 nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, 442 outerBottom, outerRadii, innerLeft, innerTop, innerRight, innerBottom, innerRadii, 443 paint.getNativeInstance()); 444 } 445 drawText(@onNull char[] text, int index, int count, float x, float y, @NonNull Paint paint)446 public void drawText(@NonNull char[] text, int index, int count, float x, float y, 447 @NonNull Paint paint) { 448 if ((index | count | (index + count) | 449 (text.length - index - count)) < 0) { 450 throw new IndexOutOfBoundsException(); 451 } 452 throwIfHasHwBitmapInSwMode(paint); 453 nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags, 454 paint.getNativeInstance()); 455 } 456 drawText(@onNull CharSequence text, int start, int end, float x, float y, @NonNull Paint paint)457 public void drawText(@NonNull CharSequence text, int start, int end, float x, float y, 458 @NonNull Paint paint) { 459 if ((start | end | (end - start) | (text.length() - end)) < 0) { 460 throw new IndexOutOfBoundsException(); 461 } 462 throwIfHasHwBitmapInSwMode(paint); 463 if (text instanceof String || text instanceof SpannedString || 464 text instanceof SpannableString) { 465 nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y, 466 paint.mBidiFlags, paint.getNativeInstance()); 467 } else if (text instanceof GraphicsOperations) { 468 ((GraphicsOperations) text).drawText(this, start, end, x, y, 469 paint); 470 } else { 471 char[] buf = TemporaryBuffer.obtain(end - start); 472 TextUtils.getChars(text, start, end, buf, 0); 473 nDrawText(mNativeCanvasWrapper, buf, 0, end - start, x, y, 474 paint.mBidiFlags, paint.getNativeInstance()); 475 TemporaryBuffer.recycle(buf); 476 } 477 } 478 drawText(@onNull String text, float x, float y, @NonNull Paint paint)479 public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { 480 throwIfHasHwBitmapInSwMode(paint); 481 nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, 482 paint.getNativeInstance()); 483 } 484 drawText(@onNull String text, int start, int end, float x, float y, @NonNull Paint paint)485 public void drawText(@NonNull String text, int start, int end, float x, float y, 486 @NonNull Paint paint) { 487 if ((start | end | (end - start) | (text.length() - end)) < 0) { 488 throw new IndexOutOfBoundsException(); 489 } 490 throwIfHasHwBitmapInSwMode(paint); 491 nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags, 492 paint.getNativeInstance()); 493 } 494 drawTextOnPath(@onNull char[] text, int index, int count, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint)495 public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path, 496 float hOffset, float vOffset, @NonNull Paint paint) { 497 if (index < 0 || index + count > text.length) { 498 throw new ArrayIndexOutOfBoundsException(); 499 } 500 throwIfHasHwBitmapInSwMode(paint); 501 nDrawTextOnPath(mNativeCanvasWrapper, text, index, count, 502 path.readOnlyNI(), hOffset, vOffset, 503 paint.mBidiFlags, paint.getNativeInstance()); 504 } 505 drawTextOnPath(@onNull String text, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint)506 public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, 507 float vOffset, @NonNull Paint paint) { 508 if (text.length() > 0) { 509 throwIfHasHwBitmapInSwMode(paint); 510 nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset, 511 paint.mBidiFlags, paint.getNativeInstance()); 512 } 513 } 514 drawTextRun(@onNull char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint)515 public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex, 516 int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) { 517 518 if (text == null) { 519 throw new NullPointerException("text is null"); 520 } 521 if (paint == null) { 522 throw new NullPointerException("paint is null"); 523 } 524 if ((index | count | contextIndex | contextCount | index - contextIndex 525 | (contextIndex + contextCount) - (index + count) 526 | text.length - (contextIndex + contextCount)) < 0) { 527 throw new IndexOutOfBoundsException(); 528 } 529 530 throwIfHasHwBitmapInSwMode(paint); 531 nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount, 532 x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */); 533 } 534 drawTextRun(@onNull CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint)535 public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart, 536 int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) { 537 538 if (text == null) { 539 throw new NullPointerException("text is null"); 540 } 541 if (paint == null) { 542 throw new NullPointerException("paint is null"); 543 } 544 if ((start | end | contextStart | contextEnd | start - contextStart | end - start 545 | contextEnd - end | text.length() - contextEnd) < 0) { 546 throw new IndexOutOfBoundsException(); 547 } 548 549 throwIfHasHwBitmapInSwMode(paint); 550 if (text instanceof String || text instanceof SpannedString || 551 text instanceof SpannableString) { 552 nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart, 553 contextEnd, x, y, isRtl, paint.getNativeInstance()); 554 } else if (text instanceof GraphicsOperations) { 555 ((GraphicsOperations) text).drawTextRun(this, start, end, 556 contextStart, contextEnd, x, y, isRtl, paint); 557 } else { 558 if (text instanceof PrecomputedText) { 559 final PrecomputedText pt = (PrecomputedText) text; 560 final int paraIndex = pt.findParaIndex(start); 561 if (end <= pt.getParagraphEnd(paraIndex)) { 562 final int paraStart = pt.getParagraphStart(paraIndex); 563 final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex); 564 // Only support the text in the same paragraph. 565 drawTextRun(mp.getMeasuredText(), 566 start - paraStart, 567 end - paraStart, 568 contextStart - paraStart, 569 contextEnd - paraStart, 570 x, y, isRtl, paint); 571 return; 572 } 573 } 574 int contextLen = contextEnd - contextStart; 575 int len = end - start; 576 char[] buf = TemporaryBuffer.obtain(contextLen); 577 TextUtils.getChars(text, contextStart, contextEnd, buf, 0); 578 nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len, 579 0, contextLen, x, y, isRtl, paint.getNativeInstance(), 580 0 /* measured paragraph pointer */); 581 TemporaryBuffer.recycle(buf); 582 } 583 } 584 drawTextRun(@onNull MeasuredText measuredText, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint)585 public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end, 586 int contextStart, int contextEnd, float x, float y, boolean isRtl, 587 @NonNull Paint paint) { 588 nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start, 589 contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(), 590 measuredText.getNativePtr()); 591 } 592 drawVertices(@onNull VertexMode mode, int vertexCount, @NonNull float[] verts, int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors, int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount, @NonNull Paint paint)593 public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts, 594 int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors, 595 int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount, 596 @NonNull Paint paint) { 597 checkRange(verts.length, vertOffset, vertexCount); 598 if (texs != null) { 599 checkRange(texs.length, texOffset, vertexCount); 600 } 601 if (colors != null) { 602 checkRange(colors.length, colorOffset, vertexCount / 2); 603 } 604 if (indices != null) { 605 checkRange(indices.length, indexOffset, indexCount); 606 } 607 throwIfHasHwBitmapInSwMode(paint); 608 nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts, 609 vertOffset, texs, texOffset, colors, colorOffset, 610 indices, indexOffset, indexCount, paint.getNativeInstance()); 611 } 612 613 /** 614 * @hide 615 */ setHwBitmapsInSwModeEnabled(boolean enabled)616 public void setHwBitmapsInSwModeEnabled(boolean enabled) { 617 mAllowHwBitmapsInSwMode = enabled; 618 } 619 620 /** 621 * @hide 622 */ isHwBitmapsInSwModeEnabled()623 public boolean isHwBitmapsInSwModeEnabled() { 624 return mAllowHwBitmapsInSwMode; 625 } 626 627 /** 628 * @hide 629 */ onHwBitmapInSwMode()630 protected void onHwBitmapInSwMode() { 631 if (!mAllowHwBitmapsInSwMode) { 632 throw new IllegalArgumentException( 633 "Software rendering doesn't support hardware bitmaps"); 634 } 635 } 636 throwIfHwBitmapInSwMode(Bitmap bitmap)637 private void throwIfHwBitmapInSwMode(Bitmap bitmap) { 638 if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) { 639 onHwBitmapInSwMode(); 640 } 641 } 642 throwIfHasHwBitmapInSwMode(Paint p)643 private void throwIfHasHwBitmapInSwMode(Paint p) { 644 if (isHardwareAccelerated() || p == null) { 645 return; 646 } 647 throwIfHasHwBitmapInSwMode(p.getShader()); 648 } 649 throwIfHasHwBitmapInSwMode(Shader shader)650 private void throwIfHasHwBitmapInSwMode(Shader shader) { 651 if (shader == null) { 652 return; 653 } 654 if (shader instanceof BitmapShader) { 655 throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap); 656 } 657 if (shader instanceof ComposeShader) { 658 throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderA); 659 throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderB); 660 } 661 } 662 nDrawBitmap(long nativeCanvas, long bitmapHandle, float left, float top, long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity)663 private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left, 664 float top, long nativePaintOrZero, int canvasDensity, int screenDensity, 665 int bitmapDensity); 666 nDrawBitmap(long nativeCanvas, long bitmapHandle, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity)667 private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float srcLeft, 668 float srcTop, 669 float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, 670 float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity); 671 nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero)672 private static native void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride, 673 float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero); 674 nDrawColor(long nativeCanvas, int color, int mode)675 private static native void nDrawColor(long nativeCanvas, int color, int mode); 676 nDrawColor(long nativeCanvas, long nativeColorSpace, @ColorLong long color, int mode)677 private static native void nDrawColor(long nativeCanvas, long nativeColorSpace, 678 @ColorLong long color, int mode); 679 nDrawPaint(long nativeCanvas, long nativePaint)680 private static native void nDrawPaint(long nativeCanvas, long nativePaint); 681 nDrawPoint(long canvasHandle, float x, float y, long paintHandle)682 private static native void nDrawPoint(long canvasHandle, float x, float y, long paintHandle); 683 nDrawPoints(long canvasHandle, float[] pts, int offset, int count, long paintHandle)684 private static native void nDrawPoints(long canvasHandle, float[] pts, int offset, int count, 685 long paintHandle); 686 nDrawLine(long nativeCanvas, float startX, float startY, float stopX, float stopY, long nativePaint)687 private static native void nDrawLine(long nativeCanvas, float startX, float startY, float stopX, 688 float stopY, long nativePaint); 689 nDrawLines(long canvasHandle, float[] pts, int offset, int count, long paintHandle)690 private static native void nDrawLines(long canvasHandle, float[] pts, int offset, int count, 691 long paintHandle); 692 nDrawRect(long nativeCanvas, float left, float top, float right, float bottom, long nativePaint)693 private static native void nDrawRect(long nativeCanvas, float left, float top, float right, 694 float bottom, long nativePaint); 695 nDrawOval(long nativeCanvas, float left, float top, float right, float bottom, long nativePaint)696 private static native void nDrawOval(long nativeCanvas, float left, float top, float right, 697 float bottom, long nativePaint); 698 nDrawCircle(long nativeCanvas, float cx, float cy, float radius, long nativePaint)699 private static native void nDrawCircle(long nativeCanvas, float cx, float cy, float radius, 700 long nativePaint); 701 nDrawArc(long nativeCanvas, float left, float top, float right, float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint)702 private static native void nDrawArc(long nativeCanvas, float left, float top, float right, 703 float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint); 704 nDrawRoundRect(long nativeCanvas, float left, float top, float right, float bottom, float rx, float ry, long nativePaint)705 private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right, 706 float bottom, float rx, float ry, long nativePaint); 707 nDrawDoubleRoundRect(long nativeCanvas, float outerLeft, float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, float innerRy, long nativePaint)708 private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft, 709 float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, 710 float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, 711 float innerRy, long nativePaint); 712 nDrawDoubleRoundRect(long nativeCanvas, float outerLeft, float outerTop, float outerRight, float outerBottom, float[] outerRadii, float innerLeft, float innerTop, float innerRight, float innerBottom, float[] innerRadii, long nativePaint)713 private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft, 714 float outerTop, float outerRight, float outerBottom, float[] outerRadii, 715 float innerLeft, float innerTop, float innerRight, float innerBottom, 716 float[] innerRadii, long nativePaint); 717 nDrawPath(long nativeCanvas, long nativePath, long nativePaint)718 private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint); 719 nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint)720 private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint); 721 nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch, float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity)722 private static native void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch, 723 float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, 724 int screenDensity, int bitmapDensity); 725 nDrawBitmapMatrix(long nativeCanvas, long bitmapHandle, long nativeMatrix, long nativePaint)726 private static native void nDrawBitmapMatrix(long nativeCanvas, long bitmapHandle, 727 long nativeMatrix, long nativePaint); 728 nDrawBitmapMesh(long nativeCanvas, long bitmapHandle, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, long nativePaint)729 private static native void nDrawBitmapMesh(long nativeCanvas, long bitmapHandle, int meshWidth, 730 int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, 731 long nativePaint); 732 nDrawVertices(long nativeCanvas, int mode, int n, float[] verts, int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices, int indexOffset, int indexCount, long nativePaint)733 private static native void nDrawVertices(long nativeCanvas, int mode, int n, float[] verts, 734 int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, 735 short[] indices, int indexOffset, int indexCount, long nativePaint); 736 nDrawText(long nativeCanvas, char[] text, int index, int count, float x, float y, int flags, long nativePaint)737 private static native void nDrawText(long nativeCanvas, char[] text, int index, int count, 738 float x, float y, int flags, long nativePaint); 739 nDrawText(long nativeCanvas, String text, int start, int end, float x, float y, int flags, long nativePaint)740 private static native void nDrawText(long nativeCanvas, String text, int start, int end, 741 float x, float y, int flags, long nativePaint); 742 nDrawTextRun(long nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint)743 private static native void nDrawTextRun(long nativeCanvas, String text, int start, int end, 744 int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint); 745 nDrawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, long nativePrecomputedText)746 private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count, 747 int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, 748 long nativePrecomputedText); 749 nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint)750 private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, 751 long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint); 752 nDrawTextOnPath(long nativeCanvas, String text, long nativePath, float hOffset, float vOffset, int flags, long nativePaint)753 private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath, 754 float hOffset, float vOffset, int flags, long nativePaint); 755 } 756