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