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