• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import android.annotation.ColorInt;
20 import android.annotation.ColorLong;
21 import android.annotation.IntRange;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.Size;
25 import android.compat.annotation.UnsupportedAppUsage;
26 import android.graphics.Canvas.VertexMode;
27 import android.graphics.fonts.Font;
28 import android.graphics.text.MeasuredText;
29 import android.graphics.text.TextRunShaper;
30 import android.text.GraphicsOperations;
31 import android.text.MeasuredParagraph;
32 import android.text.PrecomputedText;
33 import android.text.SpannableString;
34 import android.text.SpannedString;
35 import android.text.TextShaper;
36 import android.text.TextUtils;
37 
38 import com.android.internal.util.Preconditions;
39 
40 import java.util.Objects;
41 
42 /**
43  * This class is a base class for Canvas's drawing operations. Any modifications here
44  * should be accompanied by a similar modification to {@link BaseRecordingCanvas}.
45  *
46  * The purpose of this class is to minimize the cost of deciding between regular JNI
47  * and @FastNative JNI to just the virtual call that Canvas already has.
48  *
49  * @hide
50  */
51 public abstract class BaseCanvas {
52     /**
53      * Should only be assigned in constructors (or setBitmap if software canvas),
54      * freed by NativeAllocation.
55      * @hide
56      */
57     @UnsupportedAppUsage
58     protected long mNativeCanvasWrapper;
59 
60     /**
61      * Used to determine when compatibility scaling is in effect.
62      * @hide
63      */
64     protected int mScreenDensity = Bitmap.DENSITY_NONE;
65 
66     /**
67      * @hide
68      */
69     protected int mDensity = Bitmap.DENSITY_NONE;
70     private boolean 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_view_Canvas.cpp
100     // ---------------------------------------------------------------------------
101 
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)102     public void drawArc(float left, float top, float right, float bottom, float startAngle,
103             float sweepAngle, boolean useCenter, @NonNull Paint paint) {
104         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         if (path.isSimplePath && path.rects != null) {
334             nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
335         } else {
336             nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
337         }
338     }
339 
drawPoint(float x, float y, @NonNull Paint paint)340     public void drawPoint(float x, float y, @NonNull Paint paint) {
341         throwIfHasHwFeaturesInSwMode(paint);
342         nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
343     }
344 
drawPoints(@izemultiple = 2) float[] pts, int offset, int count, @NonNull Paint paint)345     public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
346             @NonNull Paint paint) {
347         throwIfHasHwFeaturesInSwMode(paint);
348         nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
349     }
350 
drawPoints(@izemultiple = 2) @onNull float[] pts, @NonNull Paint paint)351     public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
352         throwIfHasHwFeaturesInSwMode(paint);
353         drawPoints(pts, 0, pts.length, paint);
354     }
355 
356     @Deprecated
drawPosText(@onNull char[] text, int index, int count, @NonNull @Size(multiple = 2) float[] pos, @NonNull Paint paint)357     public void drawPosText(@NonNull char[] text, int index, int count,
358             @NonNull @Size(multiple = 2) float[] pos,
359             @NonNull Paint paint) {
360         if (index < 0 || index + count > text.length || count * 2 > pos.length) {
361             throw new IndexOutOfBoundsException();
362         }
363         throwIfHasHwFeaturesInSwMode(paint);
364         for (int i = 0; i < count; i++) {
365             drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
366         }
367     }
368 
369     @Deprecated
drawPosText(@onNull String text, @NonNull @Size(multiple = 2) float[] pos, @NonNull Paint paint)370     public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
371             @NonNull Paint paint) {
372         throwIfHasHwFeaturesInSwMode(paint);
373         drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
374     }
375 
drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)376     public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
377         throwIfHasHwFeaturesInSwMode(paint);
378         nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
379     }
380 
drawRect(@onNull Rect r, @NonNull Paint paint)381     public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
382         throwIfHasHwFeaturesInSwMode(paint);
383         drawRect(r.left, r.top, r.right, r.bottom, paint);
384     }
385 
drawRect(@onNull RectF rect, @NonNull Paint paint)386     public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
387         throwIfHasHwFeaturesInSwMode(paint);
388         nDrawRect(mNativeCanvasWrapper,
389                 rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
390     }
391 
drawRGB(int r, int g, int b)392     public void drawRGB(int r, int g, int b) {
393         drawColor(Color.rgb(r, g, b));
394     }
395 
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint)396     public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
397             @NonNull Paint paint) {
398         throwIfHasHwFeaturesInSwMode(paint);
399         nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
400                 paint.getNativeInstance());
401     }
402 
drawRoundRect(@onNull RectF rect, float rx, float ry, @NonNull Paint paint)403     public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
404         throwIfHasHwFeaturesInSwMode(paint);
405         drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
406     }
407 
408     /**
409      * Make lint happy.
410      * See {@link Canvas#drawDoubleRoundRect(RectF, float, float, RectF, float, float, Paint)}
411      */
drawDoubleRoundRect(@onNull RectF outer, float outerRx, float outerRy, @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint)412     public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
413             @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
414         throwIfHasHwFeaturesInSwMode(paint);
415         float outerLeft = outer.left;
416         float outerTop = outer.top;
417         float outerRight = outer.right;
418         float outerBottom = outer.bottom;
419 
420         float innerLeft = inner.left;
421         float innerTop = inner.top;
422         float innerRight = inner.right;
423         float innerBottom = inner.bottom;
424         nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, outerBottom,
425                 outerRx, outerRy, innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy,
426                 paint.getNativeInstance());
427     }
428 
429     /**
430      * Make lint happy.
431      * See {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}
432      */
drawDoubleRoundRect(@onNull RectF outer, @NonNull float[] outerRadii, @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint)433     public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
434             @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
435         throwIfHasHwFeaturesInSwMode(paint);
436         if (innerRadii == null || outerRadii == null
437                 || innerRadii.length != 8 || outerRadii.length != 8) {
438             throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
439                     + "exactly 8 values");
440         }
441         float outerLeft = outer.left;
442         float outerTop = outer.top;
443         float outerRight = outer.right;
444         float outerBottom = outer.bottom;
445 
446         float innerLeft = inner.left;
447         float innerTop = inner.top;
448         float innerRight = inner.right;
449         float innerBottom = inner.bottom;
450         nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight,
451                 outerBottom, outerRadii, innerLeft, innerTop, innerRight, innerBottom, innerRadii,
452                 paint.getNativeInstance());
453     }
454 
455     /**
456      * Draw array of glyphs with specified font.
457      *
458      * @param glyphIds Array of glyph IDs. The length of array must be greater than or equal to
459      *                 {@code glyphStart + glyphCount}.
460      * @param glyphIdOffset Number of elements to skip before drawing in <code>glyphIds</code>
461      *                     array.
462      * @param positions A flattened X and Y position array. The first glyph X position must be
463      *                  stored at {@code positionOffset}. The first glyph Y position must be stored
464      *                  at {@code positionOffset + 1}, then the second glyph X position must be
465      *                  stored at {@code positionOffset + 2}.
466      *                 The length of array must be greater than or equal to
467      *                 {@code positionOffset + glyphCount * 2}.
468      * @param positionOffset Number of elements to skip before drawing in {@code positions}.
469      *                       The first glyph X position must be stored at {@code positionOffset}.
470      *                       The first glyph Y position must be stored at
471      *                       {@code positionOffset + 1}, then the second glyph X position must be
472      *                       stored at {@code positionOffset + 2}.
473      * @param glyphCount Number of glyphs to be drawn.
474      * @param font Font used for drawing.
475      * @param paint Paint used for drawing. The typeface set to this paint is ignored.
476      *
477      * @see TextRunShaper
478      * @see TextShaper
479      */
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)480     public void drawGlyphs(
481             @NonNull int[] glyphIds,
482             @IntRange(from = 0) int glyphIdOffset,
483             @NonNull float[] positions,
484             @IntRange(from = 0) int positionOffset,
485             @IntRange(from = 0) int glyphCount,
486             @NonNull Font font,
487             @NonNull Paint paint) {
488         Objects.requireNonNull(glyphIds, "glyphIds must not be null.");
489         Objects.requireNonNull(positions, "positions must not be null.");
490         Objects.requireNonNull(font, "font must not be null.");
491         Objects.requireNonNull(paint, "paint must not be null.");
492         Preconditions.checkArgumentNonnegative(glyphCount);
493 
494         if (glyphIdOffset < 0 || glyphIdOffset + glyphCount > glyphIds.length) {
495             throw new IndexOutOfBoundsException(
496                     "glyphIds must have at least " + (glyphIdOffset + glyphCount) + " of elements");
497         }
498         if (positionOffset < 0 || positionOffset + glyphCount * 2 > positions.length) {
499             throw new IndexOutOfBoundsException(
500                     "positions must have at least " + (positionOffset + glyphCount * 2)
501                             + " of elements");
502         }
503         nDrawGlyphs(mNativeCanvasWrapper, glyphIds, positions, glyphIdOffset, positionOffset,
504                 glyphCount, font.getNativePtr(), paint.getNativeInstance());
505     }
506 
drawText(@onNull char[] text, int index, int count, float x, float y, @NonNull Paint paint)507     public void drawText(@NonNull char[] text, int index, int count, float x, float y,
508             @NonNull Paint paint) {
509         if ((index | count | (index + count) |
510                 (text.length - index - count)) < 0) {
511             throw new IndexOutOfBoundsException();
512         }
513         throwIfHasHwFeaturesInSwMode(paint);
514         nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
515                 paint.getNativeInstance());
516     }
517 
drawText(@onNull CharSequence text, int start, int end, float x, float y, @NonNull Paint paint)518     public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
519             @NonNull Paint paint) {
520         if ((start | end | (end - start) | (text.length() - end)) < 0) {
521             throw new IndexOutOfBoundsException();
522         }
523         throwIfHasHwFeaturesInSwMode(paint);
524         if (text instanceof String || text instanceof SpannedString ||
525                 text instanceof SpannableString) {
526             nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
527                     paint.mBidiFlags, paint.getNativeInstance());
528         } else if (text instanceof GraphicsOperations) {
529             ((GraphicsOperations) text).drawText(this, start, end, x, y,
530                     paint);
531         } else {
532             char[] buf = TemporaryBuffer.obtain(end - start);
533             TextUtils.getChars(text, start, end, buf, 0);
534             nDrawText(mNativeCanvasWrapper, buf, 0, end - start, x, y,
535                     paint.mBidiFlags, paint.getNativeInstance());
536             TemporaryBuffer.recycle(buf);
537         }
538     }
539 
drawText(@onNull String text, float x, float y, @NonNull Paint paint)540     public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
541         throwIfHasHwFeaturesInSwMode(paint);
542         nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
543                 paint.getNativeInstance());
544     }
545 
drawText(@onNull String text, int start, int end, float x, float y, @NonNull Paint paint)546     public void drawText(@NonNull String text, int start, int end, float x, float y,
547             @NonNull Paint paint) {
548         if ((start | end | (end - start) | (text.length() - end)) < 0) {
549             throw new IndexOutOfBoundsException();
550         }
551         throwIfHasHwFeaturesInSwMode(paint);
552         nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
553                 paint.getNativeInstance());
554     }
555 
drawTextOnPath(@onNull char[] text, int index, int count, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint)556     public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path,
557             float hOffset, float vOffset, @NonNull Paint paint) {
558         if (index < 0 || index + count > text.length) {
559             throw new ArrayIndexOutOfBoundsException();
560         }
561         throwIfHasHwFeaturesInSwMode(paint);
562         nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
563                 path.readOnlyNI(), hOffset, vOffset,
564                 paint.mBidiFlags, paint.getNativeInstance());
565     }
566 
drawTextOnPath(@onNull String text, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint)567     public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
568             float vOffset, @NonNull Paint paint) {
569         if (text.length() > 0) {
570             throwIfHasHwFeaturesInSwMode(paint);
571             nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
572                     paint.mBidiFlags, paint.getNativeInstance());
573         }
574     }
575 
drawTextRun(@onNull char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint)576     public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex,
577             int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) {
578 
579         if (text == null) {
580             throw new NullPointerException("text is null");
581         }
582         if (paint == null) {
583             throw new NullPointerException("paint is null");
584         }
585         if ((index | count | contextIndex | contextCount | index - contextIndex
586                 | (contextIndex + contextCount) - (index + count)
587                 | text.length - (contextIndex + contextCount)) < 0) {
588             throw new IndexOutOfBoundsException();
589         }
590 
591         throwIfHasHwFeaturesInSwMode(paint);
592         nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
593                 x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
594     }
595 
drawTextRun(@onNull CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint)596     public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
597             int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) {
598 
599         if (text == null) {
600             throw new NullPointerException("text is null");
601         }
602         if (paint == null) {
603             throw new NullPointerException("paint is null");
604         }
605         if ((start | end | contextStart | contextEnd | start - contextStart | end - start
606                 | contextEnd - end | text.length() - contextEnd) < 0) {
607             throw new IndexOutOfBoundsException();
608         }
609 
610         throwIfHasHwFeaturesInSwMode(paint);
611         if (text instanceof String || text instanceof SpannedString ||
612                 text instanceof SpannableString) {
613             nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
614                     contextEnd, x, y, isRtl, paint.getNativeInstance());
615         } else if (text instanceof GraphicsOperations) {
616             ((GraphicsOperations) text).drawTextRun(this, start, end,
617                     contextStart, contextEnd, x, y, isRtl, paint);
618         } else {
619             if (text instanceof PrecomputedText) {
620                 final PrecomputedText pt = (PrecomputedText) text;
621                 final int paraIndex = pt.findParaIndex(start);
622                 if (end <= pt.getParagraphEnd(paraIndex)) {
623                     final int paraStart = pt.getParagraphStart(paraIndex);
624                     final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
625                     // Only support the text in the same paragraph.
626                     drawTextRun(mp.getMeasuredText(),
627                                 start - paraStart,
628                                 end - paraStart,
629                                 contextStart - paraStart,
630                                 contextEnd - paraStart,
631                                 x, y, isRtl, paint);
632                     return;
633                 }
634             }
635             int contextLen = contextEnd - contextStart;
636             int len = end - start;
637             char[] buf = TemporaryBuffer.obtain(contextLen);
638             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
639             nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
640                     0, contextLen, x, y, isRtl, paint.getNativeInstance(),
641                     0 /* measured paragraph pointer */);
642             TemporaryBuffer.recycle(buf);
643         }
644     }
645 
drawTextRun(@onNull MeasuredText measuredText, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint)646     public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end,
647             int contextStart, int contextEnd, float x, float y, boolean isRtl,
648             @NonNull Paint paint) {
649         nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start,
650                 contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(),
651                 measuredText.getNativePtr());
652     }
653 
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)654     public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
655             int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
656             int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
657             @NonNull Paint paint) {
658         checkRange(verts.length, vertOffset, vertexCount);
659         if (texs != null) {
660             checkRange(texs.length, texOffset, vertexCount);
661         }
662         if (colors != null) {
663             checkRange(colors.length, colorOffset, vertexCount / 2);
664         }
665         if (indices != null) {
666             checkRange(indices.length, indexOffset, indexCount);
667         }
668         throwIfHasHwFeaturesInSwMode(paint);
669         nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
670                 vertOffset, texs, texOffset, colors, colorOffset,
671                 indices, indexOffset, indexCount, paint.getNativeInstance());
672     }
673 
674     /**
675      * @hide
676      */
punchHole(float left, float top, float right, float bottom, float rx, float ry)677     public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
678         nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
679     }
680 
681     /**
682      * @hide
683      */
setHwFeaturesInSwModeEnabled(boolean enabled)684     public void setHwFeaturesInSwModeEnabled(boolean enabled) {
685         mAllowHwFeaturesInSwMode = enabled;
686     }
687 
688     /**
689      * @hide
690      */
isHwFeaturesInSwModeEnabled()691     public boolean isHwFeaturesInSwModeEnabled() {
692         return mAllowHwFeaturesInSwMode;
693     }
694 
695     /**
696      * If true throw an exception
697      * @hide
698      */
onHwFeatureInSwMode()699     protected boolean onHwFeatureInSwMode() {
700         return !mAllowHwFeaturesInSwMode;
701     }
702 
throwIfHwBitmapInSwMode(Bitmap bitmap)703     private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
704         if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE
705                 && onHwFeatureInSwMode()) {
706             throw new IllegalArgumentException(
707                     "Software rendering doesn't support hardware bitmaps");
708         }
709     }
710 
throwIfHasHwFeaturesInSwMode(Paint p)711     private void throwIfHasHwFeaturesInSwMode(Paint p) {
712         if (isHardwareAccelerated() || p == null) {
713             return;
714         }
715         throwIfHasHwFeaturesInSwMode(p.getShader());
716     }
717 
throwIfHasHwFeaturesInSwMode(Shader shader)718     private void throwIfHasHwFeaturesInSwMode(Shader shader) {
719         if (shader == null) {
720             return;
721         }
722         if (shader instanceof BitmapShader) {
723             throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap);
724         } else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) {
725             throw new IllegalArgumentException(
726                     "Software rendering doesn't support RuntimeShader");
727         } else if (shader instanceof ComposeShader) {
728             throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA);
729             throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB);
730         }
731     }
732 
nDrawBitmap(long nativeCanvas, long bitmapHandle, float left, float top, long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity)733     private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left,
734             float top, long nativePaintOrZero, int canvasDensity, int screenDensity,
735             int bitmapDensity);
736 
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)737     private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float srcLeft,
738             float srcTop,
739             float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
740             float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity);
741 
nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero)742     private static native void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride,
743             float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero);
744 
nDrawColor(long nativeCanvas, int color, int mode)745     private static native void nDrawColor(long nativeCanvas, int color, int mode);
746 
nDrawColor(long nativeCanvas, long nativeColorSpace, @ColorLong long color, int mode)747     private static native void nDrawColor(long nativeCanvas, long nativeColorSpace,
748             @ColorLong long color, int mode);
749 
nDrawPaint(long nativeCanvas, long nativePaint)750     private static native void nDrawPaint(long nativeCanvas, long nativePaint);
751 
nDrawPoint(long canvasHandle, float x, float y, long paintHandle)752     private static native void nDrawPoint(long canvasHandle, float x, float y, long paintHandle);
753 
nDrawPoints(long canvasHandle, float[] pts, int offset, int count, long paintHandle)754     private static native void nDrawPoints(long canvasHandle, float[] pts, int offset, int count,
755             long paintHandle);
756 
nDrawLine(long nativeCanvas, float startX, float startY, float stopX, float stopY, long nativePaint)757     private static native void nDrawLine(long nativeCanvas, float startX, float startY, float stopX,
758             float stopY, long nativePaint);
759 
nDrawLines(long canvasHandle, float[] pts, int offset, int count, long paintHandle)760     private static native void nDrawLines(long canvasHandle, float[] pts, int offset, int count,
761             long paintHandle);
762 
nDrawRect(long nativeCanvas, float left, float top, float right, float bottom, long nativePaint)763     private static native void nDrawRect(long nativeCanvas, float left, float top, float right,
764             float bottom, long nativePaint);
765 
nDrawOval(long nativeCanvas, float left, float top, float right, float bottom, long nativePaint)766     private static native void nDrawOval(long nativeCanvas, float left, float top, float right,
767             float bottom, long nativePaint);
768 
nDrawCircle(long nativeCanvas, float cx, float cy, float radius, long nativePaint)769     private static native void nDrawCircle(long nativeCanvas, float cx, float cy, float radius,
770             long nativePaint);
771 
nDrawArc(long nativeCanvas, float left, float top, float right, float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint)772     private static native void nDrawArc(long nativeCanvas, float left, float top, float right,
773             float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint);
774 
nDrawRoundRect(long nativeCanvas, float left, float top, float right, float bottom, float rx, float ry, long nativePaint)775     private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right,
776             float bottom, float rx, float ry, long nativePaint);
777 
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)778     private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
779             float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy,
780             float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx,
781             float innerRy, long nativePaint);
782 
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)783     private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
784             float outerTop, float outerRight, float outerBottom, float[] outerRadii,
785             float innerLeft, float innerTop, float innerRight, float innerBottom,
786             float[] innerRadii, long nativePaint);
787 
nDrawPath(long nativeCanvas, long nativePath, long nativePaint)788     private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
789 
nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint)790     private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint);
791 
nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch, float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity)792     private static native void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch,
793             float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero,
794             int screenDensity, int bitmapDensity);
795 
nDrawBitmapMatrix(long nativeCanvas, long bitmapHandle, long nativeMatrix, long nativePaint)796     private static native void nDrawBitmapMatrix(long nativeCanvas, long bitmapHandle,
797             long nativeMatrix, long nativePaint);
798 
nDrawBitmapMesh(long nativeCanvas, long bitmapHandle, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, long nativePaint)799     private static native void nDrawBitmapMesh(long nativeCanvas, long bitmapHandle, int meshWidth,
800             int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset,
801             long nativePaint);
802 
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)803     private static native void nDrawVertices(long nativeCanvas, int mode, int n, float[] verts,
804             int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset,
805             short[] indices, int indexOffset, int indexCount, long nativePaint);
806 
nDrawGlyphs(long nativeCanvas, int[] glyphIds, float[] positions, int glyphIdStart, int positionStart, int glyphCount, long nativeFont, long nativePaint)807     private static native void nDrawGlyphs(long nativeCanvas, int[] glyphIds, float[] positions,
808             int glyphIdStart, int positionStart, int glyphCount, long nativeFont, long nativePaint);
809 
nDrawText(long nativeCanvas, char[] text, int index, int count, float x, float y, int flags, long nativePaint)810     private static native void nDrawText(long nativeCanvas, char[] text, int index, int count,
811             float x, float y, int flags, long nativePaint);
812 
nDrawText(long nativeCanvas, String text, int start, int end, float x, float y, int flags, long nativePaint)813     private static native void nDrawText(long nativeCanvas, String text, int start, int end,
814             float x, float y, int flags, long nativePaint);
815 
nDrawTextRun(long nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint)816     private static native void nDrawTextRun(long nativeCanvas, String text, int start, int end,
817             int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint);
818 
nDrawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, long nativePrecomputedText)819     private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
820             int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
821             long nativePrecomputedText);
822 
nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint)823     private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
824             long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
825 
nDrawTextOnPath(long nativeCanvas, String text, long nativePath, float hOffset, float vOffset, int flags, long nativePaint)826     private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
827             float hOffset, float vOffset, int flags, long nativePaint);
828 
nPunchHole(long renderer, float left, float top, float right, float bottom, float rx, float ry)829     private static native void nPunchHole(long renderer, float left, float top, float right,
830             float bottom, float rx, float ry);
831 }
832