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