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