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