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