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