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