• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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