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