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