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_view_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 if (path.isSimplePath && path.rects != null) { 334 nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance()); 335 } else { 336 nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance()); 337 } 338 } 339 drawPoint(float x, float y, @NonNull Paint paint)340 public void drawPoint(float x, float y, @NonNull Paint paint) { 341 throwIfHasHwFeaturesInSwMode(paint); 342 nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance()); 343 } 344 drawPoints(@izemultiple = 2) float[] pts, int offset, int count, @NonNull Paint paint)345 public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count, 346 @NonNull Paint paint) { 347 throwIfHasHwFeaturesInSwMode(paint); 348 nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); 349 } 350 drawPoints(@izemultiple = 2) @onNull float[] pts, @NonNull Paint paint)351 public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) { 352 throwIfHasHwFeaturesInSwMode(paint); 353 drawPoints(pts, 0, pts.length, paint); 354 } 355 356 @Deprecated drawPosText(@onNull char[] text, int index, int count, @NonNull @Size(multiple = 2) float[] pos, @NonNull Paint paint)357 public void drawPosText(@NonNull char[] text, int index, int count, 358 @NonNull @Size(multiple = 2) float[] pos, 359 @NonNull Paint paint) { 360 if (index < 0 || index + count > text.length || count * 2 > pos.length) { 361 throw new IndexOutOfBoundsException(); 362 } 363 throwIfHasHwFeaturesInSwMode(paint); 364 for (int i = 0; i < count; i++) { 365 drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint); 366 } 367 } 368 369 @Deprecated drawPosText(@onNull String text, @NonNull @Size(multiple = 2) float[] pos, @NonNull Paint paint)370 public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos, 371 @NonNull Paint paint) { 372 throwIfHasHwFeaturesInSwMode(paint); 373 drawPosText(text.toCharArray(), 0, text.length(), pos, paint); 374 } 375 drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)376 public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) { 377 throwIfHasHwFeaturesInSwMode(paint); 378 nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); 379 } 380 drawRect(@onNull Rect r, @NonNull Paint paint)381 public void drawRect(@NonNull Rect r, @NonNull Paint paint) { 382 throwIfHasHwFeaturesInSwMode(paint); 383 drawRect(r.left, r.top, r.right, r.bottom, paint); 384 } 385 drawRect(@onNull RectF rect, @NonNull Paint paint)386 public void drawRect(@NonNull RectF rect, @NonNull Paint paint) { 387 throwIfHasHwFeaturesInSwMode(paint); 388 nDrawRect(mNativeCanvasWrapper, 389 rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance()); 390 } 391 drawRGB(int r, int g, int b)392 public void drawRGB(int r, int g, int b) { 393 drawColor(Color.rgb(r, g, b)); 394 } 395 drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint)396 public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, 397 @NonNull Paint paint) { 398 throwIfHasHwFeaturesInSwMode(paint); 399 nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, 400 paint.getNativeInstance()); 401 } 402 drawRoundRect(@onNull RectF rect, float rx, float ry, @NonNull Paint paint)403 public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { 404 throwIfHasHwFeaturesInSwMode(paint); 405 drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint); 406 } 407 408 /** 409 * Make lint happy. 410 * See {@link Canvas#drawDoubleRoundRect(RectF, float, float, RectF, float, float, Paint)} 411 */ drawDoubleRoundRect(@onNull RectF outer, float outerRx, float outerRy, @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint)412 public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy, 413 @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) { 414 throwIfHasHwFeaturesInSwMode(paint); 415 float outerLeft = outer.left; 416 float outerTop = outer.top; 417 float outerRight = outer.right; 418 float outerBottom = outer.bottom; 419 420 float innerLeft = inner.left; 421 float innerTop = inner.top; 422 float innerRight = inner.right; 423 float innerBottom = inner.bottom; 424 nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, outerBottom, 425 outerRx, outerRy, innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, 426 paint.getNativeInstance()); 427 } 428 429 /** 430 * Make lint happy. 431 * See {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)} 432 */ drawDoubleRoundRect(@onNull RectF outer, @NonNull float[] outerRadii, @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint)433 public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii, 434 @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) { 435 throwIfHasHwFeaturesInSwMode(paint); 436 if (innerRadii == null || outerRadii == null 437 || innerRadii.length != 8 || outerRadii.length != 8) { 438 throw new IllegalArgumentException("Both inner and outer radii arrays must contain " 439 + "exactly 8 values"); 440 } 441 float outerLeft = outer.left; 442 float outerTop = outer.top; 443 float outerRight = outer.right; 444 float outerBottom = outer.bottom; 445 446 float innerLeft = inner.left; 447 float innerTop = inner.top; 448 float innerRight = inner.right; 449 float innerBottom = inner.bottom; 450 nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, 451 outerBottom, outerRadii, innerLeft, innerTop, innerRight, innerBottom, innerRadii, 452 paint.getNativeInstance()); 453 } 454 455 /** 456 * Draw array of glyphs with specified font. 457 * 458 * @param glyphIds Array of glyph IDs. The length of array must be greater than or equal to 459 * {@code glyphStart + glyphCount}. 460 * @param glyphIdOffset Number of elements to skip before drawing in <code>glyphIds</code> 461 * array. 462 * @param positions A flattened X and Y position array. The first glyph X position must be 463 * stored at {@code positionOffset}. The first glyph Y position must be stored 464 * at {@code positionOffset + 1}, then the second glyph X position must be 465 * stored at {@code positionOffset + 2}. 466 * The length of array must be greater than or equal to 467 * {@code positionOffset + glyphCount * 2}. 468 * @param positionOffset Number of elements to skip before drawing in {@code positions}. 469 * The first glyph X position must be stored at {@code positionOffset}. 470 * The first glyph Y position must be stored at 471 * {@code positionOffset + 1}, then the second glyph X position must be 472 * stored at {@code positionOffset + 2}. 473 * @param glyphCount Number of glyphs to be drawn. 474 * @param font Font used for drawing. 475 * @param paint Paint used for drawing. The typeface set to this paint is ignored. 476 * 477 * @see TextRunShaper 478 * @see TextShaper 479 */ 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)480 public void drawGlyphs( 481 @NonNull int[] glyphIds, 482 @IntRange(from = 0) int glyphIdOffset, 483 @NonNull float[] positions, 484 @IntRange(from = 0) int positionOffset, 485 @IntRange(from = 0) int glyphCount, 486 @NonNull Font font, 487 @NonNull Paint paint) { 488 Objects.requireNonNull(glyphIds, "glyphIds must not be null."); 489 Objects.requireNonNull(positions, "positions must not be null."); 490 Objects.requireNonNull(font, "font must not be null."); 491 Objects.requireNonNull(paint, "paint must not be null."); 492 Preconditions.checkArgumentNonnegative(glyphCount); 493 494 if (glyphIdOffset < 0 || glyphIdOffset + glyphCount > glyphIds.length) { 495 throw new IndexOutOfBoundsException( 496 "glyphIds must have at least " + (glyphIdOffset + glyphCount) + " of elements"); 497 } 498 if (positionOffset < 0 || positionOffset + glyphCount * 2 > positions.length) { 499 throw new IndexOutOfBoundsException( 500 "positions must have at least " + (positionOffset + glyphCount * 2) 501 + " of elements"); 502 } 503 nDrawGlyphs(mNativeCanvasWrapper, glyphIds, positions, glyphIdOffset, positionOffset, 504 glyphCount, font.getNativePtr(), paint.getNativeInstance()); 505 } 506 drawText(@onNull char[] text, int index, int count, float x, float y, @NonNull Paint paint)507 public void drawText(@NonNull char[] text, int index, int count, float x, float y, 508 @NonNull Paint paint) { 509 if ((index | count | (index + count) | 510 (text.length - index - count)) < 0) { 511 throw new IndexOutOfBoundsException(); 512 } 513 throwIfHasHwFeaturesInSwMode(paint); 514 nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags, 515 paint.getNativeInstance()); 516 } 517 drawText(@onNull CharSequence text, int start, int end, float x, float y, @NonNull Paint paint)518 public void drawText(@NonNull CharSequence text, int start, int end, float x, float y, 519 @NonNull Paint paint) { 520 if ((start | end | (end - start) | (text.length() - end)) < 0) { 521 throw new IndexOutOfBoundsException(); 522 } 523 throwIfHasHwFeaturesInSwMode(paint); 524 if (text instanceof String || text instanceof SpannedString || 525 text instanceof SpannableString) { 526 nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y, 527 paint.mBidiFlags, paint.getNativeInstance()); 528 } else if (text instanceof GraphicsOperations) { 529 ((GraphicsOperations) text).drawText(this, start, end, x, y, 530 paint); 531 } else { 532 char[] buf = TemporaryBuffer.obtain(end - start); 533 TextUtils.getChars(text, start, end, buf, 0); 534 nDrawText(mNativeCanvasWrapper, buf, 0, end - start, x, y, 535 paint.mBidiFlags, paint.getNativeInstance()); 536 TemporaryBuffer.recycle(buf); 537 } 538 } 539 drawText(@onNull String text, float x, float y, @NonNull Paint paint)540 public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { 541 throwIfHasHwFeaturesInSwMode(paint); 542 nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, 543 paint.getNativeInstance()); 544 } 545 drawText(@onNull String text, int start, int end, float x, float y, @NonNull Paint paint)546 public void drawText(@NonNull String text, int start, int end, float x, float y, 547 @NonNull Paint paint) { 548 if ((start | end | (end - start) | (text.length() - end)) < 0) { 549 throw new IndexOutOfBoundsException(); 550 } 551 throwIfHasHwFeaturesInSwMode(paint); 552 nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags, 553 paint.getNativeInstance()); 554 } 555 drawTextOnPath(@onNull char[] text, int index, int count, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint)556 public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path, 557 float hOffset, float vOffset, @NonNull Paint paint) { 558 if (index < 0 || index + count > text.length) { 559 throw new ArrayIndexOutOfBoundsException(); 560 } 561 throwIfHasHwFeaturesInSwMode(paint); 562 nDrawTextOnPath(mNativeCanvasWrapper, text, index, count, 563 path.readOnlyNI(), hOffset, vOffset, 564 paint.mBidiFlags, paint.getNativeInstance()); 565 } 566 drawTextOnPath(@onNull String text, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint)567 public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, 568 float vOffset, @NonNull Paint paint) { 569 if (text.length() > 0) { 570 throwIfHasHwFeaturesInSwMode(paint); 571 nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset, 572 paint.mBidiFlags, paint.getNativeInstance()); 573 } 574 } 575 drawTextRun(@onNull char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint)576 public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex, 577 int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) { 578 579 if (text == null) { 580 throw new NullPointerException("text is null"); 581 } 582 if (paint == null) { 583 throw new NullPointerException("paint is null"); 584 } 585 if ((index | count | contextIndex | contextCount | index - contextIndex 586 | (contextIndex + contextCount) - (index + count) 587 | text.length - (contextIndex + contextCount)) < 0) { 588 throw new IndexOutOfBoundsException(); 589 } 590 591 throwIfHasHwFeaturesInSwMode(paint); 592 nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount, 593 x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */); 594 } 595 drawTextRun(@onNull CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint)596 public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart, 597 int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) { 598 599 if (text == null) { 600 throw new NullPointerException("text is null"); 601 } 602 if (paint == null) { 603 throw new NullPointerException("paint is null"); 604 } 605 if ((start | end | contextStart | contextEnd | start - contextStart | end - start 606 | contextEnd - end | text.length() - contextEnd) < 0) { 607 throw new IndexOutOfBoundsException(); 608 } 609 610 throwIfHasHwFeaturesInSwMode(paint); 611 if (text instanceof String || text instanceof SpannedString || 612 text instanceof SpannableString) { 613 nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart, 614 contextEnd, x, y, isRtl, paint.getNativeInstance()); 615 } else if (text instanceof GraphicsOperations) { 616 ((GraphicsOperations) text).drawTextRun(this, start, end, 617 contextStart, contextEnd, x, y, isRtl, paint); 618 } else { 619 if (text instanceof PrecomputedText) { 620 final PrecomputedText pt = (PrecomputedText) text; 621 final int paraIndex = pt.findParaIndex(start); 622 if (end <= pt.getParagraphEnd(paraIndex)) { 623 final int paraStart = pt.getParagraphStart(paraIndex); 624 final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex); 625 // Only support the text in the same paragraph. 626 drawTextRun(mp.getMeasuredText(), 627 start - paraStart, 628 end - paraStart, 629 contextStart - paraStart, 630 contextEnd - paraStart, 631 x, y, isRtl, paint); 632 return; 633 } 634 } 635 int contextLen = contextEnd - contextStart; 636 int len = end - start; 637 char[] buf = TemporaryBuffer.obtain(contextLen); 638 TextUtils.getChars(text, contextStart, contextEnd, buf, 0); 639 nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len, 640 0, contextLen, x, y, isRtl, paint.getNativeInstance(), 641 0 /* measured paragraph pointer */); 642 TemporaryBuffer.recycle(buf); 643 } 644 } 645 drawTextRun(@onNull MeasuredText measuredText, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint)646 public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end, 647 int contextStart, int contextEnd, float x, float y, boolean isRtl, 648 @NonNull Paint paint) { 649 nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start, 650 contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(), 651 measuredText.getNativePtr()); 652 } 653 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)654 public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts, 655 int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors, 656 int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount, 657 @NonNull Paint paint) { 658 checkRange(verts.length, vertOffset, vertexCount); 659 if (texs != null) { 660 checkRange(texs.length, texOffset, vertexCount); 661 } 662 if (colors != null) { 663 checkRange(colors.length, colorOffset, vertexCount / 2); 664 } 665 if (indices != null) { 666 checkRange(indices.length, indexOffset, indexCount); 667 } 668 throwIfHasHwFeaturesInSwMode(paint); 669 nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts, 670 vertOffset, texs, texOffset, colors, colorOffset, 671 indices, indexOffset, indexCount, paint.getNativeInstance()); 672 } 673 674 /** 675 * @hide 676 */ punchHole(float left, float top, float right, float bottom, float rx, float ry)677 public void punchHole(float left, float top, float right, float bottom, float rx, float ry) { 678 nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry); 679 } 680 681 /** 682 * @hide 683 */ setHwFeaturesInSwModeEnabled(boolean enabled)684 public void setHwFeaturesInSwModeEnabled(boolean enabled) { 685 mAllowHwFeaturesInSwMode = enabled; 686 } 687 688 /** 689 * @hide 690 */ isHwFeaturesInSwModeEnabled()691 public boolean isHwFeaturesInSwModeEnabled() { 692 return mAllowHwFeaturesInSwMode; 693 } 694 695 /** 696 * If true throw an exception 697 * @hide 698 */ onHwFeatureInSwMode()699 protected boolean onHwFeatureInSwMode() { 700 return !mAllowHwFeaturesInSwMode; 701 } 702 throwIfHwBitmapInSwMode(Bitmap bitmap)703 private void throwIfHwBitmapInSwMode(Bitmap bitmap) { 704 if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE 705 && onHwFeatureInSwMode()) { 706 throw new IllegalArgumentException( 707 "Software rendering doesn't support hardware bitmaps"); 708 } 709 } 710 throwIfHasHwFeaturesInSwMode(Paint p)711 private void throwIfHasHwFeaturesInSwMode(Paint p) { 712 if (isHardwareAccelerated() || p == null) { 713 return; 714 } 715 throwIfHasHwFeaturesInSwMode(p.getShader()); 716 } 717 throwIfHasHwFeaturesInSwMode(Shader shader)718 private void throwIfHasHwFeaturesInSwMode(Shader shader) { 719 if (shader == null) { 720 return; 721 } 722 if (shader instanceof BitmapShader) { 723 throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap); 724 } else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) { 725 throw new IllegalArgumentException( 726 "Software rendering doesn't support RuntimeShader"); 727 } else if (shader instanceof ComposeShader) { 728 throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA); 729 throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB); 730 } 731 } 732 nDrawBitmap(long nativeCanvas, long bitmapHandle, float left, float top, long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity)733 private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left, 734 float top, long nativePaintOrZero, int canvasDensity, int screenDensity, 735 int bitmapDensity); 736 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)737 private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float srcLeft, 738 float srcTop, 739 float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, 740 float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity); 741 nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero)742 private static native void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride, 743 float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero); 744 nDrawColor(long nativeCanvas, int color, int mode)745 private static native void nDrawColor(long nativeCanvas, int color, int mode); 746 nDrawColor(long nativeCanvas, long nativeColorSpace, @ColorLong long color, int mode)747 private static native void nDrawColor(long nativeCanvas, long nativeColorSpace, 748 @ColorLong long color, int mode); 749 nDrawPaint(long nativeCanvas, long nativePaint)750 private static native void nDrawPaint(long nativeCanvas, long nativePaint); 751 nDrawPoint(long canvasHandle, float x, float y, long paintHandle)752 private static native void nDrawPoint(long canvasHandle, float x, float y, long paintHandle); 753 nDrawPoints(long canvasHandle, float[] pts, int offset, int count, long paintHandle)754 private static native void nDrawPoints(long canvasHandle, float[] pts, int offset, int count, 755 long paintHandle); 756 nDrawLine(long nativeCanvas, float startX, float startY, float stopX, float stopY, long nativePaint)757 private static native void nDrawLine(long nativeCanvas, float startX, float startY, float stopX, 758 float stopY, long nativePaint); 759 nDrawLines(long canvasHandle, float[] pts, int offset, int count, long paintHandle)760 private static native void nDrawLines(long canvasHandle, float[] pts, int offset, int count, 761 long paintHandle); 762 nDrawRect(long nativeCanvas, float left, float top, float right, float bottom, long nativePaint)763 private static native void nDrawRect(long nativeCanvas, float left, float top, float right, 764 float bottom, long nativePaint); 765 nDrawOval(long nativeCanvas, float left, float top, float right, float bottom, long nativePaint)766 private static native void nDrawOval(long nativeCanvas, float left, float top, float right, 767 float bottom, long nativePaint); 768 nDrawCircle(long nativeCanvas, float cx, float cy, float radius, long nativePaint)769 private static native void nDrawCircle(long nativeCanvas, float cx, float cy, float radius, 770 long nativePaint); 771 nDrawArc(long nativeCanvas, float left, float top, float right, float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint)772 private static native void nDrawArc(long nativeCanvas, float left, float top, float right, 773 float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint); 774 nDrawRoundRect(long nativeCanvas, float left, float top, float right, float bottom, float rx, float ry, long nativePaint)775 private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right, 776 float bottom, float rx, float ry, long nativePaint); 777 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)778 private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft, 779 float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, 780 float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, 781 float innerRy, long nativePaint); 782 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)783 private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft, 784 float outerTop, float outerRight, float outerBottom, float[] outerRadii, 785 float innerLeft, float innerTop, float innerRight, float innerBottom, 786 float[] innerRadii, long nativePaint); 787 nDrawPath(long nativeCanvas, long nativePath, long nativePaint)788 private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint); 789 nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint)790 private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint); 791 nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch, float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity)792 private static native void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch, 793 float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, 794 int screenDensity, int bitmapDensity); 795 nDrawBitmapMatrix(long nativeCanvas, long bitmapHandle, long nativeMatrix, long nativePaint)796 private static native void nDrawBitmapMatrix(long nativeCanvas, long bitmapHandle, 797 long nativeMatrix, long nativePaint); 798 nDrawBitmapMesh(long nativeCanvas, long bitmapHandle, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, long nativePaint)799 private static native void nDrawBitmapMesh(long nativeCanvas, long bitmapHandle, int meshWidth, 800 int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, 801 long nativePaint); 802 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)803 private static native void nDrawVertices(long nativeCanvas, int mode, int n, float[] verts, 804 int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, 805 short[] indices, int indexOffset, int indexCount, long nativePaint); 806 nDrawGlyphs(long nativeCanvas, int[] glyphIds, float[] positions, int glyphIdStart, int positionStart, int glyphCount, long nativeFont, long nativePaint)807 private static native void nDrawGlyphs(long nativeCanvas, int[] glyphIds, float[] positions, 808 int glyphIdStart, int positionStart, int glyphCount, long nativeFont, long nativePaint); 809 nDrawText(long nativeCanvas, char[] text, int index, int count, float x, float y, int flags, long nativePaint)810 private static native void nDrawText(long nativeCanvas, char[] text, int index, int count, 811 float x, float y, int flags, long nativePaint); 812 nDrawText(long nativeCanvas, String text, int start, int end, float x, float y, int flags, long nativePaint)813 private static native void nDrawText(long nativeCanvas, String text, int start, int end, 814 float x, float y, int flags, long nativePaint); 815 nDrawTextRun(long nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint)816 private static native void nDrawTextRun(long nativeCanvas, String text, int start, int end, 817 int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint); 818 nDrawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, long nativePrecomputedText)819 private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count, 820 int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, 821 long nativePrecomputedText); 822 nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint)823 private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, 824 long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint); 825 nDrawTextOnPath(long nativeCanvas, String text, long nativePath, float hOffset, float vOffset, int flags, long nativePaint)826 private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath, 827 float hOffset, float vOffset, int flags, long nativePaint); 828 nPunchHole(long renderer, float left, float top, float right, float bottom, float rx, float ry)829 private static native void nPunchHole(long renderer, float left, float top, float right, 830 float bottom, float rx, float ry); 831 } 832