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