diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java index 8674c66c90d2d..6edf845978c9c 100644 --- a/core/java/android/text/GraphicsOperations.java +++ b/core/java/android/text/GraphicsOperations.java @@ -16,31 +16,30 @@ package android.text; +import android.graphics.BaseCanvas; import android.graphics.Canvas; import android.graphics.Paint; /** - * Please implement this interface if your CharSequence can do quick - * draw/measure/widths calculations from an internal array. - * {@hide} + * Please implement this interface if your CharSequence can do quick draw/measure/widths + * calculations from an internal array. + * + * @hide */ -public interface GraphicsOperations -extends CharSequence -{ +public interface GraphicsOperations extends CharSequence { /** * Just like {@link Canvas#drawText}. */ - void drawText(Canvas c, int start, int end, - float x, float y, Paint p); + void drawText(BaseCanvas c, int start, int end, + float x, float y, Paint p); /** * Just like {@link Canvas#drawTextRun}. - * {@hide} */ - void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd, + void drawTextRun(BaseCanvas c, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint p); - /** + /** * Just like {@link Paint#measureText}. */ float measureText(int start, int end, Paint p); @@ -52,14 +51,12 @@ extends CharSequence /** * Just like {@link Paint#getTextRunAdvances}. - * @hide */ float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, boolean isRtl, float[] advances, int advancesIndex, Paint paint); /** * Just like {@link Paint#getTextRunCursor}. - * @hide */ int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset, int cursorOpt, Paint p); diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index dc8e4b93e0939..186d96bce8e1f 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -17,7 +17,7 @@ package android.text; import android.annotation.Nullable; -import android.graphics.Canvas; +import android.graphics.BaseCanvas; import android.graphics.Paint; import android.util.Log; @@ -1357,7 +1357,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * Don't call this yourself -- exists for Canvas to use internally. * {@hide} */ - public void drawText(Canvas c, int start, int end, float x, float y, Paint p) { + @Override + public void drawText(BaseCanvas c, int start, int end, float x, float y, Paint p) { checkRange("drawText", start, end); if (end <= mGapStart) { @@ -1378,7 +1379,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * Don't call this yourself -- exists for Canvas to use internally. * {@hide} */ - public void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd, + @Override + public void drawTextRun(BaseCanvas c, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint p) { checkRange("drawTextRun", start, end); diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index 872303a69a506..eb41f9ebcf7fb 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -19,7 +19,6 @@ package android.view; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; import android.util.Pools.SynchronizedPool; @@ -34,7 +33,7 @@ import dalvik.annotation.optimization.FastNative; * * @hide */ -public class DisplayListCanvas extends Canvas { +public final class DisplayListCanvas extends RecordingCanvas { // The recording canvas pool should be large enough to handle a deeply nested // view hierarchy because display lists are generated recursively. private static final int POOL_LIMIT = 25; @@ -42,7 +41,7 @@ public class DisplayListCanvas extends Canvas { private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB private static final SynchronizedPool sPool = - new SynchronizedPool(POOL_LIMIT); + new SynchronizedPool<>(POOL_LIMIT); RenderNode mNode; private int mWidth; diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java new file mode 100644 index 0000000000000..e4b91b75e95ae --- /dev/null +++ b/core/java/android/view/RecordingCanvas.java @@ -0,0 +1,641 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.ColorInt; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.Size; +import android.graphics.BaseCanvas; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.NinePatch; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Picture; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.TemporaryBuffer; +import android.text.GraphicsOperations; +import android.text.SpannableString; +import android.text.SpannedString; +import android.text.TextUtils; + +import dalvik.annotation.optimization.FastNative; + +/** + * This class is a base class for canvases that defer drawing operations, so all + * the draw operations can be marked @FastNative. It contains a re-implementation of + * all the methods in {@link BaseCanvas}. + * + * @hide + */ +public class RecordingCanvas extends Canvas { + + public RecordingCanvas(long nativeCanvas) { + super(nativeCanvas); + } + + @Override + public final void drawArc(float left, float top, float right, float bottom, float startAngle, + float sweepAngle, boolean useCenter, @NonNull Paint paint) { + nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle, + useCenter, paint.getNativeInstance()); + } + + @Override + public final void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, + boolean useCenter, @NonNull Paint paint) { + drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter, + paint); + } + + @Override + public final void drawARGB(int a, int r, int g, int b) { + drawColor(Color.argb(a, r, g, b)); + } + + @Override + public final void drawBitmap(@NonNull Bitmap bitmap, float left, float top, + @Nullable Paint paint) { + throwIfCannotDraw(bitmap); + nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top, + paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity, + bitmap.mDensity); + } + + @Override + public final void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, + @Nullable Paint paint) { + nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap, matrix.ni(), + paint != null ? paint.getNativeInstance() : 0); + } + + @Override + public final void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, + @Nullable Paint paint) { + if (dst == null) { + throw new NullPointerException(); + } + throwIfCannotDraw(bitmap); + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); + + int left, top, right, bottom; + if (src == null) { + left = top = 0; + right = bitmap.getWidth(); + bottom = bitmap.getHeight(); + } else { + left = src.left; + right = src.right; + top = src.top; + bottom = src.bottom; + } + + nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity, + bitmap.mDensity); + } + + @Override + public final void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, + @Nullable Paint paint) { + if (dst == null) { + throw new NullPointerException(); + } + throwIfCannotDraw(bitmap); + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); + + float left, top, right, bottom; + if (src == null) { + left = top = 0; + right = bitmap.getWidth(); + bottom = bitmap.getHeight(); + } else { + left = src.left; + right = src.right; + top = src.top; + bottom = src.bottom; + } + + nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity, + bitmap.mDensity); + } + + /** @deprecated checkstyle */ + @Override + @Deprecated + public final void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y, + int width, int height, boolean hasAlpha, @Nullable Paint paint) { + // check for valid input + if (width < 0) { + throw new IllegalArgumentException("width must be >= 0"); + } + if (height < 0) { + throw new IllegalArgumentException("height must be >= 0"); + } + if (Math.abs(stride) < width) { + throw new IllegalArgumentException("abs(stride) must be >= width"); + } + int lastScanline = offset + (height - 1) * stride; + int length = colors.length; + if (offset < 0 || (offset + width > length) || lastScanline < 0 + || (lastScanline + width > length)) { + throw new ArrayIndexOutOfBoundsException(); + } + // quick escape if there's nothing to draw + if (width == 0 || height == 0) { + return; + } + // punch down to native for the actual draw + nDrawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha, + paint != null ? paint.getNativeInstance() : 0); + } + + /** @deprecated checkstyle */ + @Override + @Deprecated + public final void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y, + int width, int height, boolean hasAlpha, @Nullable Paint paint) { + // call through to the common float version + drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, + hasAlpha, paint); + } + + @Override + public final void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight, + @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset, + @Nullable Paint paint) { + if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) { + throw new ArrayIndexOutOfBoundsException(); + } + if (meshWidth == 0 || meshHeight == 0) { + return; + } + int count = (meshWidth + 1) * (meshHeight + 1); + // we mul by 2 since we need two floats per vertex + checkRange(verts.length, vertOffset, count * 2); + if (colors != null) { + // no mul by 2, since we need only 1 color per vertex + checkRange(colors.length, colorOffset, count); + } + nDrawBitmapMesh(mNativeCanvasWrapper, bitmap, meshWidth, meshHeight, + verts, vertOffset, colors, colorOffset, + paint != null ? paint.getNativeInstance() : 0); + } + + @Override + public final void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) { + nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance()); + } + + @Override + public final void drawColor(@ColorInt int color) { + nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt); + } + + @Override + public final void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) { + nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt); + } + + @Override + public final void drawLine(float startX, float startY, float stopX, float stopY, + @NonNull Paint paint) { + nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance()); + } + + @Override + public final void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count, + @NonNull Paint paint) { + nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); + } + + @Override + public final void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) { + drawLines(pts, 0, pts.length, paint); + } + + @Override + public final void drawOval(float left, float top, float right, float bottom, + @NonNull Paint paint) { + nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); + } + + @Override + public final void drawOval(@NonNull RectF oval, @NonNull Paint paint) { + if (oval == null) { + throw new NullPointerException(); + } + drawOval(oval.left, oval.top, oval.right, oval.bottom, paint); + } + + @Override + public final void drawPaint(@NonNull Paint paint) { + nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance()); + } + + @Override + public final void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, + @Nullable Paint paint) { + Bitmap bitmap = patch.getBitmap(); + throwIfCannotDraw(bitmap); + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); + nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, + mDensity, patch.getDensity()); + } + + @Override + public final void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, + @Nullable Paint paint) { + Bitmap bitmap = patch.getBitmap(); + throwIfCannotDraw(bitmap); + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); + nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, + mDensity, patch.getDensity()); + } + + @Override + public final void drawPath(@NonNull Path path, @NonNull Paint paint) { + if (path.isSimplePath && path.rects != null) { + nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance()); + } else { + nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance()); + } + } + + @Override + public final void drawPicture(@NonNull Picture picture) { + picture.endRecording(); + int restoreCount = save(); + picture.draw(this); + restoreToCount(restoreCount); + } + + @Override + public final void drawPicture(@NonNull Picture picture, @NonNull Rect dst) { + save(); + translate(dst.left, dst.top); + if (picture.getWidth() > 0 && picture.getHeight() > 0) { + scale((float) dst.width() / picture.getWidth(), + (float) dst.height() / picture.getHeight()); + } + drawPicture(picture); + restore(); + } + + @Override + public final void drawPicture(@NonNull Picture picture, @NonNull RectF dst) { + save(); + translate(dst.left, dst.top); + if (picture.getWidth() > 0 && picture.getHeight() > 0) { + scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight()); + } + drawPicture(picture); + restore(); + } + + @Override + public final void drawPoint(float x, float y, @NonNull Paint paint) { + nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance()); + } + + @Override + public final void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count, + @NonNull Paint paint) { + nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); + } + + @Override + public final void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) { + drawPoints(pts, 0, pts.length, paint); + } + + /** @deprecated checkstyle */ + @Override + @Deprecated + public final void drawPosText(@NonNull char[] text, int index, int count, + @NonNull @Size(multiple = 2) float[] pos, + @NonNull Paint paint) { + if (index < 0 || index + count > text.length || count * 2 > pos.length) { + throw new IndexOutOfBoundsException(); + } + for (int i = 0; i < count; i++) { + drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint); + } + } + + /** @deprecated checkstyle */ + @Override + @Deprecated + public final void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos, + @NonNull Paint paint) { + drawPosText(text.toCharArray(), 0, text.length(), pos, paint); + } + + @Override + public final void drawRect(float left, float top, float right, float bottom, + @NonNull Paint paint) { + nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); + } + + @Override + public final void drawRect(@NonNull Rect r, @NonNull Paint paint) { + drawRect(r.left, r.top, r.right, r.bottom, paint); + } + + @Override + public final void drawRect(@NonNull RectF rect, @NonNull Paint paint) { + nDrawRect(mNativeCanvasWrapper, + rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance()); + } + + @Override + public final void drawRGB(int r, int g, int b) { + drawColor(Color.rgb(r, g, b)); + } + + @Override + public final void drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, @NonNull Paint paint) { + nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, + paint.getNativeInstance()); + } + + @Override + public final void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { + drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint); + } + + @Override + public final void drawText(@NonNull char[] text, int index, int count, float x, float y, + @NonNull Paint paint) { + if ((index | count | (index + count) + | (text.length - index - count)) < 0) { + throw new IndexOutOfBoundsException(); + } + nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags, + paint.getNativeInstance(), paint.mNativeTypeface); + } + + @Override + public final void drawText(@NonNull CharSequence text, int start, int end, float x, float y, + @NonNull Paint paint) { + if ((start | end | (end - start) | (text.length() - end)) < 0) { + throw new IndexOutOfBoundsException(); + } + if (text instanceof String || text instanceof SpannedString + || text instanceof SpannableString) { + nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y, + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); + } else if (text instanceof GraphicsOperations) { + ((GraphicsOperations) text).drawText(this, start, end, x, y, + paint); + } else { + char[] buf = TemporaryBuffer.obtain(end - start); + TextUtils.getChars(text, start, end, buf, 0); + nDrawText(mNativeCanvasWrapper, buf, 0, end - start, x, y, + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); + TemporaryBuffer.recycle(buf); + } + } + + @Override + public final void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { + nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, + paint.getNativeInstance(), paint.mNativeTypeface); + } + + @Override + public final void drawText(@NonNull String text, int start, int end, float x, float y, + @NonNull Paint paint) { + if ((start | end | (end - start) | (text.length() - end)) < 0) { + throw new IndexOutOfBoundsException(); + } + nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags, + paint.getNativeInstance(), paint.mNativeTypeface); + } + + @Override + public final void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path, + float hOffset, float vOffset, @NonNull Paint paint) { + if (index < 0 || index + count > text.length) { + throw new ArrayIndexOutOfBoundsException(); + } + nDrawTextOnPath(mNativeCanvasWrapper, text, index, count, + path.readOnlyNI(), hOffset, vOffset, + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); + } + + @Override + public final void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, + float vOffset, @NonNull Paint paint) { + if (text.length() > 0) { + nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset, + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); + } + } + + @Override + public final void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex, + int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) { + + if (text == null) { + throw new NullPointerException("text is null"); + } + if (paint == null) { + throw new NullPointerException("paint is null"); + } + if ((index | count | contextIndex | contextCount | index - contextIndex + | (contextIndex + contextCount) - (index + count) + | text.length - (contextIndex + contextCount)) < 0) { + throw new IndexOutOfBoundsException(); + } + + nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount, + x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); + } + + @Override + public final void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart, + int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) { + + if (text == null) { + throw new NullPointerException("text is null"); + } + if (paint == null) { + throw new NullPointerException("paint is null"); + } + if ((start | end | contextStart | contextEnd | start - contextStart | end - start + | contextEnd - end | text.length() - contextEnd) < 0) { + throw new IndexOutOfBoundsException(); + } + + if (text instanceof String || text instanceof SpannedString + || text instanceof SpannableString) { + nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart, + contextEnd, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); + } else if (text instanceof GraphicsOperations) { + ((GraphicsOperations) text).drawTextRun(this, start, end, + contextStart, contextEnd, x, y, isRtl, paint); + } else { + int contextLen = contextEnd - contextStart; + int len = end - start; + char[] buf = TemporaryBuffer.obtain(contextLen); + TextUtils.getChars(text, contextStart, contextEnd, buf, 0); + nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len, + 0, contextLen, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); + TemporaryBuffer.recycle(buf); + } + } + + @Override + public final void drawVertices(@NonNull 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) { + checkRange(verts.length, vertOffset, vertexCount); + if (isHardwareAccelerated()) { + return; + } + if (texs != null) { + checkRange(texs.length, texOffset, vertexCount); + } + if (colors != null) { + checkRange(colors.length, colorOffset, vertexCount / 2); + } + if (indices != null) { + checkRange(indices.length, indexOffset, indexCount); + } + nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts, + vertOffset, texs, texOffset, colors, colorOffset, + indices, indexOffset, indexCount, paint.getNativeInstance()); + } + + @FastNative + private static native void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top, + long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity); + + @FastNative + private static native void 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); + + @FastNative + private static native void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride, + float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero); + + @FastNative + private static native void nDrawColor(long nativeCanvas, int color, int mode); + + @FastNative + private static native void nDrawPaint(long nativeCanvas, long nativePaint); + + @FastNative + private static native void nDrawPoint(long canvasHandle, float x, float y, long paintHandle); + + @FastNative + private static native void nDrawPoints(long canvasHandle, float[] pts, int offset, int count, + long paintHandle); + + @FastNative + private static native void nDrawLine(long nativeCanvas, float startX, float startY, float stopX, + float stopY, long nativePaint); + + @FastNative + private static native void nDrawLines(long canvasHandle, float[] pts, int offset, int count, + long paintHandle); + + @FastNative + private static native void nDrawRect(long nativeCanvas, float left, float top, float right, + float bottom, long nativePaint); + + @FastNative + private static native void nDrawOval(long nativeCanvas, float left, float top, float right, + float bottom, long nativePaint); + + @FastNative + private static native void nDrawCircle(long nativeCanvas, float cx, float cy, float radius, + long nativePaint); + + @FastNative + private static native void nDrawArc(long nativeCanvas, float left, float top, float right, + float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint); + + @FastNative + private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right, + float bottom, float rx, float ry, long nativePaint); + + @FastNative + private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint); + + @FastNative + private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint); + + @FastNative + private static native void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch, + float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, + int screenDensity, int bitmapDensity); + + @FastNative + private static native void nDrawBitmapMatrix(long nativeCanvas, Bitmap bitmap, + long nativeMatrix, long nativePaint); + + @FastNative + private static native void nDrawBitmapMesh(long nativeCanvas, Bitmap bitmap, int meshWidth, + int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, + long nativePaint); + + @FastNative + private static native void 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); + + @FastNative + private static native void nDrawText(long nativeCanvas, char[] text, int index, int count, + float x, float y, int flags, long nativePaint, long nativeTypeface); + + @FastNative + private static native void nDrawText(long nativeCanvas, String text, int start, int end, + float x, float y, int flags, long nativePaint, long nativeTypeface); + + @FastNative + private static native void nDrawTextRun(long nativeCanvas, String text, int start, int end, + int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint, + long nativeTypeface); + + @FastNative + private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count, + int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, + long nativeTypeface); + + @FastNative + private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, + long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint, + long nativeTypeface); + + @FastNative + private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath, + float hOffset, float vOffset, int flags, long nativePaint, long nativeTypeface); +} diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5264d5c95dbca..f1c3079349062 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -41,6 +41,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.graphics.BaseCanvas; import android.graphics.Canvas; import android.graphics.Insets; import android.graphics.Paint; @@ -10222,12 +10223,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener System.arraycopy(mChars, start + mStart, buf, off, end - start); } - public void drawText(Canvas c, int start, int end, + @Override + public void drawText(BaseCanvas c, int start, int end, float x, float y, Paint p) { c.drawText(mChars, start + mStart, end - start, x, y, p); } - public void drawTextRun(Canvas c, int start, int end, + @Override + public void drawTextRun(BaseCanvas c, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint p) { int count = end - start; int contextCount = contextEnd - contextStart; diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index 43f7ca5c12371..10645e841bf89 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -368,7 +368,7 @@ static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmap } } -static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jobject jbitmap, +static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap, jfloat left, jfloat top, jlong paintHandle, jint canvasDensity, jint screenDensity, jint bitmapDensity) { Canvas* canvas = get_canvas(canvasHandle); @@ -571,67 +571,77 @@ static void freeTextLayoutCaches(JNIEnv* env, jobject) { }; // namespace CanvasJNI static const JNINativeMethod gMethods[] = { - {"getNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer}, - {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster}, - {"freeCaches", "()V", (void*) CanvasJNI::freeCaches}, - {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}, - {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap}, - {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, - {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, + {"nGetNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer}, + {"nInitRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster}, + {"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches}, + {"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}, // ------------ @FastNative ---------------- - {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap}, - {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque}, - {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth}, - {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight}, - {"native_setHighContrastText","(JZ)V", (void*) CanvasJNI::setHighContrastText}, - {"native_save","(JI)I", (void*) CanvasJNI::save}, - {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer}, - {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha}, - {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount}, - {"native_restore","(JZ)V", (void*) CanvasJNI::restore}, - {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount}, - {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM}, - {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix}, - {"native_concat","(JJ)V", (void*) CanvasJNI::concat}, - {"native_rotate","(JF)V", (void*) CanvasJNI::rotate}, - {"native_scale","(JFF)V", (void*) CanvasJNI::scale}, - {"native_skew","(JFF)V", (void*) CanvasJNI::skew}, - {"native_translate","(JFF)V", (void*) CanvasJNI::translate}, - {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds}, - {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath}, - {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect}, - {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect}, - {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath}, - {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion}, - {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor}, - {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint}, - {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint}, - {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints}, - {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine}, - {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines}, - {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect}, - {"native_drawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion }, - {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect}, - {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle}, - {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval}, - {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc}, - {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath}, - {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices}, - {"native_drawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch}, - {"nativeDrawBitmapMatrix", "(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix}, - {"nativeDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, - {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars}, - {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString}, - {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars}, - {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString}, - {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars}, - {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString}, - {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter}, + {"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap}, + {"nIsOpaque","(J)Z", (void*) CanvasJNI::isOpaque}, + {"nGetWidth","(J)I", (void*) CanvasJNI::getWidth}, + {"nGetHeight","(J)I", (void*) CanvasJNI::getHeight}, + {"nSetHighContrastText","(JZ)V", (void*) CanvasJNI::setHighContrastText}, + {"nSave","(JI)I", (void*) CanvasJNI::save}, + {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer}, + {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha}, + {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount}, + {"nRestore","(JZ)V", (void*) CanvasJNI::restore}, + {"nRestoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount}, + {"nGetCTM", "(JJ)V", (void*)CanvasJNI::getCTM}, + {"nSetMatrix","(JJ)V", (void*) CanvasJNI::setMatrix}, + {"nConcat","(JJ)V", (void*) CanvasJNI::concat}, + {"nRotate","(JF)V", (void*) CanvasJNI::rotate}, + {"nScale","(JFF)V", (void*) CanvasJNI::scale}, + {"nSkew","(JFF)V", (void*) CanvasJNI::skew}, + {"nTranslate","(JFF)V", (void*) CanvasJNI::translate}, + {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds}, + {"nQuickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath}, + {"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect}, + {"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect}, + {"nClipPath","(JJI)Z", (void*) CanvasJNI::clipPath}, + {"nClipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion}, + {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter}, +}; + +// If called from Canvas these are regular JNI +// If called from DisplayListCanvas they are @FastNative +static const JNINativeMethod gDrawMethods[] = { + {"nDrawColor","(JII)V", (void*) CanvasJNI::drawColor}, + {"nDrawPaint","(JJ)V", (void*) CanvasJNI::drawPaint}, + {"nDrawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint}, + {"nDrawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints}, + {"nDrawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine}, + {"nDrawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines}, + {"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect}, + {"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion }, + {"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect}, + {"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle}, + {"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval}, + {"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc}, + {"nDrawPath","(JJJ)V", (void*) CanvasJNI::drawPath}, + {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices}, + {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch}, + {"nDrawBitmapMatrix", "(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix}, + {"nDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, + {"nDrawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap}, + {"nDrawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, + {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, + {"nDrawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars}, + {"nDrawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString}, + {"nDrawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars}, + {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString}, + {"nDrawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars}, + {"nDrawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString}, }; int register_android_graphics_Canvas(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods)); + int ret = 0; + ret |= RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods)); + ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods)); + ret |= RegisterMethodsOrDie(env, "android/view/RecordingCanvas", gDrawMethods, NELEM(gDrawMethods)); + return ret; + } }; // namespace android diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java new file mode 100644 index 0000000000000..f135484adcc04 --- /dev/null +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import android.annotation.ColorInt; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.Size; +import android.graphics.Canvas.VertexMode; +import android.text.GraphicsOperations; +import android.text.SpannableString; +import android.text.SpannedString; +import android.text.TextUtils; +import android.view.RecordingCanvas; + +/** + * This class is a base class for Canvas's drawing operations. Any modifications here + * should be accompanied by a similar modification to {@link RecordingCanvas}. + * + * The purpose of this class is to minimize the cost of deciding between regular JNI + * and @FastNative JNI to just the virtual call that Canvas already has. + * + * @hide + */ +public abstract class BaseCanvas { + /** + * Should only be assigned in constructors (or setBitmap if software canvas), + * freed by NativeAllocation. + */ + protected long mNativeCanvasWrapper; + + /** + * Used to determine when compatibility scaling is in effect. + */ + protected int mScreenDensity = Bitmap.DENSITY_NONE; + protected int mDensity = Bitmap.DENSITY_NONE; + + protected void throwIfCannotDraw(Bitmap bitmap) { + if (bitmap.isRecycled()) { + throw new RuntimeException("Canvas: trying to use a recycled bitmap " + bitmap); + } + if (!bitmap.isPremultiplied() && bitmap.getConfig() == Bitmap.Config.ARGB_8888 && + bitmap.hasAlpha()) { + throw new RuntimeException("Canvas: trying to use a non-premultiplied bitmap " + + bitmap); + } + } + + protected final static void checkRange(int length, int offset, int count) { + if ((offset | count) < 0 || offset + count > length) { + throw new ArrayIndexOutOfBoundsException(); + } + } + + public boolean isHardwareAccelerated() { + return false; + } + + // --------------------------------------------------------------------------- + // Drawing methods + // These are also implemented in DisplayListCanvas so that we can + // selectively apply on them + // Everything below here is copy/pasted from Canvas.java + // The JNI registration is handled by android_view_Canvas.cpp + // --------------------------------------------------------------------------- + + public void drawArc(float left, float top, float right, float bottom, float startAngle, + float sweepAngle, boolean useCenter, @NonNull Paint paint) { + nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle, + useCenter, paint.getNativeInstance()); + } + + public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, + @NonNull Paint paint) { + drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter, + paint); + } + + public void drawARGB(int a, int r, int g, int b) { + drawColor(Color.argb(a, r, g, b)); + } + + public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) { + throwIfCannotDraw(bitmap); + nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top, + paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity, + bitmap.mDensity); + } + + public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) { + nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap, matrix.ni(), + paint != null ? paint.getNativeInstance() : 0); + } + + public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, + @Nullable Paint paint) { + if (dst == null) { + throw new NullPointerException(); + } + throwIfCannotDraw(bitmap); + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); + + int left, top, right, bottom; + if (src == null) { + left = top = 0; + right = bitmap.getWidth(); + bottom = bitmap.getHeight(); + } else { + left = src.left; + right = src.right; + top = src.top; + bottom = src.bottom; + } + + nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity, + bitmap.mDensity); + } + + public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, + @Nullable Paint paint) { + if (dst == null) { + throw new NullPointerException(); + } + throwIfCannotDraw(bitmap); + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); + + float left, top, right, bottom; + if (src == null) { + left = top = 0; + right = bitmap.getWidth(); + bottom = bitmap.getHeight(); + } else { + left = src.left; + right = src.right; + top = src.top; + bottom = src.bottom; + } + + nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity, + bitmap.mDensity); + } + + @Deprecated + public void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y, + int width, int height, boolean hasAlpha, @Nullable Paint paint) { + // check for valid input + if (width < 0) { + throw new IllegalArgumentException("width must be >= 0"); + } + if (height < 0) { + throw new IllegalArgumentException("height must be >= 0"); + } + if (Math.abs(stride) < width) { + throw new IllegalArgumentException("abs(stride) must be >= width"); + } + int lastScanline = offset + (height - 1) * stride; + int length = colors.length; + if (offset < 0 || (offset + width > length) || lastScanline < 0 + || (lastScanline + width > length)) { + throw new ArrayIndexOutOfBoundsException(); + } + // quick escape if there's nothing to draw + if (width == 0 || height == 0) { + return; + } + // punch down to native for the actual draw + nDrawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha, + paint != null ? paint.getNativeInstance() : 0); + } + + @Deprecated + public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y, + int width, int height, boolean hasAlpha, @Nullable Paint paint) { + // call through to the common float version + drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, + hasAlpha, paint); + } + + public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight, + @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset, + @Nullable Paint paint) { + if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) { + throw new ArrayIndexOutOfBoundsException(); + } + if (meshWidth == 0 || meshHeight == 0) { + return; + } + int count = (meshWidth + 1) * (meshHeight + 1); + // we mul by 2 since we need two floats per vertex + checkRange(verts.length, vertOffset, count * 2); + if (colors != null) { + // no mul by 2, since we need only 1 color per vertex + checkRange(colors.length, colorOffset, count); + } + nDrawBitmapMesh(mNativeCanvasWrapper, bitmap, meshWidth, meshHeight, + verts, vertOffset, colors, colorOffset, + paint != null ? paint.getNativeInstance() : 0); + } + + public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) { + nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance()); + } + + public void drawColor(@ColorInt int color) { + nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt); + } + + public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) { + nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt); + } + + public void drawLine(float startX, float startY, float stopX, float stopY, + @NonNull Paint paint) { + nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance()); + } + + public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count, + @NonNull Paint paint) { + nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); + } + + public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) { + drawLines(pts, 0, pts.length, paint); + } + + public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) { + nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); + } + + public void drawOval(@NonNull RectF oval, @NonNull Paint paint) { + if (oval == null) { + throw new NullPointerException(); + } + drawOval(oval.left, oval.top, oval.right, oval.bottom, paint); + } + + public void drawPaint(@NonNull Paint paint) { + nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance()); + } + + public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) { + Bitmap bitmap = patch.getBitmap(); + throwIfCannotDraw(bitmap); + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); + nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, + mDensity, patch.getDensity()); + } + + public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) { + Bitmap bitmap = patch.getBitmap(); + throwIfCannotDraw(bitmap); + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); + nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, + mDensity, patch.getDensity()); + } + + public void drawPath(@NonNull Path path, @NonNull Paint paint) { + if (path.isSimplePath && path.rects != null) { + nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance()); + } else { + nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance()); + } + } + + public void drawPoint(float x, float y, @NonNull Paint paint) { + nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance()); + } + + public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count, + @NonNull Paint paint) { + nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); + } + + public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) { + drawPoints(pts, 0, pts.length, paint); + } + + @Deprecated + public void drawPosText(@NonNull char[] text, int index, int count, + @NonNull @Size(multiple = 2) float[] pos, + @NonNull Paint paint) { + if (index < 0 || index + count > text.length || count * 2 > pos.length) { + throw new IndexOutOfBoundsException(); + } + for (int i = 0; i < count; i++) { + drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint); + } + } + + @Deprecated + public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos, + @NonNull Paint paint) { + drawPosText(text.toCharArray(), 0, text.length(), pos, paint); + } + + public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) { + nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); + } + + public void drawRect(@NonNull Rect r, @NonNull Paint paint) { + drawRect(r.left, r.top, r.right, r.bottom, paint); + } + + public void drawRect(@NonNull RectF rect, @NonNull Paint paint) { + nDrawRect(mNativeCanvasWrapper, + rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance()); + } + + public void drawRGB(int r, int g, int b) { + drawColor(Color.rgb(r, g, b)); + } + + public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, + @NonNull Paint paint) { + nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, + paint.getNativeInstance()); + } + + public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { + drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint); + } + + public void drawText(@NonNull char[] text, int index, int count, float x, float y, + @NonNull Paint paint) { + if ((index | count | (index + count) | + (text.length - index - count)) < 0) { + throw new IndexOutOfBoundsException(); + } + nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags, + paint.getNativeInstance(), paint.mNativeTypeface); + } + + public void drawText(@NonNull CharSequence text, int start, int end, float x, float y, + @NonNull Paint paint) { + if ((start | end | (end - start) | (text.length() - end)) < 0) { + throw new IndexOutOfBoundsException(); + } + if (text instanceof String || text instanceof SpannedString || + text instanceof SpannableString) { + nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y, + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); + } else if (text instanceof GraphicsOperations) { + ((GraphicsOperations) text).drawText(this, start, end, x, y, + paint); + } else { + char[] buf = TemporaryBuffer.obtain(end - start); + TextUtils.getChars(text, start, end, buf, 0); + nDrawText(mNativeCanvasWrapper, buf, 0, end - start, x, y, + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); + TemporaryBuffer.recycle(buf); + } + } + + public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { + nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, + paint.getNativeInstance(), paint.mNativeTypeface); + } + + public void drawText(@NonNull String text, int start, int end, float x, float y, + @NonNull Paint paint) { + if ((start | end | (end - start) | (text.length() - end)) < 0) { + throw new IndexOutOfBoundsException(); + } + nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags, + paint.getNativeInstance(), paint.mNativeTypeface); + } + + public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path, + float hOffset, float vOffset, @NonNull Paint paint) { + if (index < 0 || index + count > text.length) { + throw new ArrayIndexOutOfBoundsException(); + } + nDrawTextOnPath(mNativeCanvasWrapper, text, index, count, + path.readOnlyNI(), hOffset, vOffset, + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); + } + + public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, + float vOffset, @NonNull Paint paint) { + if (text.length() > 0) { + nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset, + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); + } + } + + public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex, + int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) { + + if (text == null) { + throw new NullPointerException("text is null"); + } + if (paint == null) { + throw new NullPointerException("paint is null"); + } + if ((index | count | contextIndex | contextCount | index - contextIndex + | (contextIndex + contextCount) - (index + count) + | text.length - (contextIndex + contextCount)) < 0) { + throw new IndexOutOfBoundsException(); + } + + nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount, + x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); + } + + public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart, + int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) { + + if (text == null) { + throw new NullPointerException("text is null"); + } + if (paint == null) { + throw new NullPointerException("paint is null"); + } + if ((start | end | contextStart | contextEnd | start - contextStart | end - start + | contextEnd - end | text.length() - contextEnd) < 0) { + throw new IndexOutOfBoundsException(); + } + + if (text instanceof String || text instanceof SpannedString || + text instanceof SpannableString) { + nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart, + contextEnd, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); + } else if (text instanceof GraphicsOperations) { + ((GraphicsOperations) text).drawTextRun(this, start, end, + contextStart, contextEnd, x, y, isRtl, paint); + } else { + int contextLen = contextEnd - contextStart; + int len = end - start; + char[] buf = TemporaryBuffer.obtain(contextLen); + TextUtils.getChars(text, contextStart, contextEnd, buf, 0); + nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len, + 0, contextLen, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); + TemporaryBuffer.recycle(buf); + } + } + + public void drawVertices(@NonNull 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) { + checkRange(verts.length, vertOffset, vertexCount); + if (isHardwareAccelerated()) { + return; + } + if (texs != null) { + checkRange(texs.length, texOffset, vertexCount); + } + if (colors != null) { + checkRange(colors.length, colorOffset, vertexCount / 2); + } + if (indices != null) { + checkRange(indices.length, indexOffset, indexCount); + } + nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts, + vertOffset, texs, texOffset, colors, colorOffset, + indices, indexOffset, indexCount, paint.getNativeInstance()); + } + + private static native void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top, + long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity); + + private static native void 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); + + private static native void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride, + float x, float y, int width, int height, boolean hasAlpha, long nativePaintOrZero); + + private static native void nDrawColor(long nativeCanvas, int color, int mode); + + private static native void nDrawPaint(long nativeCanvas, long nativePaint); + + private static native void nDrawPoint(long canvasHandle, float x, float y, long paintHandle); + + private static native void nDrawPoints(long canvasHandle, float[] pts, int offset, int count, + long paintHandle); + + private static native void nDrawLine(long nativeCanvas, float startX, float startY, float stopX, + float stopY, long nativePaint); + + private static native void nDrawLines(long canvasHandle, float[] pts, int offset, int count, + long paintHandle); + + private static native void nDrawRect(long nativeCanvas, float left, float top, float right, + float bottom, long nativePaint); + + private static native void nDrawOval(long nativeCanvas, float left, float top, float right, + float bottom, long nativePaint); + + private static native void nDrawCircle(long nativeCanvas, float cx, float cy, float radius, + long nativePaint); + + private static native void nDrawArc(long nativeCanvas, float left, float top, float right, + float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint); + + private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right, + float bottom, float rx, float ry, long nativePaint); + + private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint); + + private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint); + + private static native void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch, + float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, + int screenDensity, int bitmapDensity); + + private static native void nDrawBitmapMatrix(long nativeCanvas, Bitmap bitmap, + long nativeMatrix, long nativePaint); + + private static native void nDrawBitmapMesh(long nativeCanvas, Bitmap bitmap, int meshWidth, + int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, + long nativePaint); + + private static native void 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); + + private static native void nDrawText(long nativeCanvas, char[] text, int index, int count, + float x, float y, int flags, long nativePaint, long nativeTypeface); + + private static native void nDrawText(long nativeCanvas, String text, int start, int end, + float x, float y, int flags, long nativePaint, long nativeTypeface); + + private static native void nDrawTextRun(long nativeCanvas, String text, int start, int end, + int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint, + long nativeTypeface); + + private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count, + int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, + long nativeTypeface); + + private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, + long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint, + long nativeTypeface); + + private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath, + float hOffset, float vOffset, int flags, long nativePaint, long nativeTypeface); +} diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 7ce750d6e0001..fd2f705bfd452 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -73,8 +73,8 @@ public final class Bitmap implements Parcelable { private int mHeight; private boolean mRecycled; - // Package-scoped for fast access. - int mDensity = getDefaultDensity(); + /** @hide */ + public int mDensity = getDefaultDensity(); private static volatile Matrix sScaleMatrix; @@ -130,8 +130,9 @@ public final class Bitmap implements Parcelable { /** * Return the pointer to the native object. + * @hide */ - long getNativeInstance() { + public long getNativeInstance() { return mNativePtr; } diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 0692a090426ec..3f0bed86e531b 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -48,17 +48,10 @@ import javax.microedition.khronos.opengles.GL; * * Canvas and Drawables developer guide.

*/ -public class Canvas { +public class Canvas extends BaseCanvas { /** @hide */ public static boolean sCompatibilityRestore = false; - /** - * Should only be assigned in constructors (or setBitmap if software canvas), - * freed by NativeAllocation. - * @hide - */ - protected long mNativeCanvasWrapper; - /** @hide */ public long getNativeCanvasWrapper() { return mNativeCanvasWrapper; @@ -73,18 +66,6 @@ public class Canvas { // optional field set by the caller private DrawFilter mDrawFilter; - /** - * @hide - */ - protected int mDensity = Bitmap.DENSITY_NONE; - - /** - * Used to determine when compatibility scaling is in effect. - * - * @hide - */ - protected int mScreenDensity = Bitmap.DENSITY_NONE; - // Maximum bitmap size as defined in Skia's native code // (see SkCanvas.cpp, SkDraw.cpp) private static final int MAXMIMUM_BITMAP_SIZE = 32766; @@ -96,7 +77,7 @@ public class Canvas { // Use a Holder to allow static initialization of Canvas in the boot image. private static class NoImagePreloadHolder { public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( - Canvas.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE); + Canvas.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_ALLOCATION_SIZE); } // This field is used to finalize the native Canvas properly @@ -111,7 +92,7 @@ public class Canvas { public Canvas() { if (!isHardwareAccelerated()) { // 0 means no native bitmap - mNativeCanvasWrapper = initRaster(null); + mNativeCanvasWrapper = nInitRaster(null); mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation( this, mNativeCanvasWrapper); } else { @@ -133,7 +114,7 @@ public class Canvas { throw new IllegalStateException("Immutable bitmap passed to Canvas constructor"); } throwIfCannotDraw(bitmap); - mNativeCanvasWrapper = initRaster(bitmap); + mNativeCanvasWrapper = nInitRaster(bitmap); mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation( this, mNativeCanvasWrapper); mBitmap = bitmap; @@ -192,7 +173,7 @@ public class Canvas { } if (bitmap == null) { - native_setBitmap(mNativeCanvasWrapper, null); + nSetBitmap(mNativeCanvasWrapper, null); mDensity = Bitmap.DENSITY_NONE; } else { if (!bitmap.isMutable()) { @@ -200,7 +181,7 @@ public class Canvas { } throwIfCannotDraw(bitmap); - native_setBitmap(mNativeCanvasWrapper, bitmap); + nSetBitmap(mNativeCanvasWrapper, bitmap); mDensity = bitmap.mDensity; } @@ -209,7 +190,7 @@ public class Canvas { /** @hide */ public void setHighContrastText(boolean highContrastText) { - native_setHighContrastText(mNativeCanvasWrapper, highContrastText); + nSetHighContrastText(mNativeCanvasWrapper, highContrastText); } /** @hide */ @@ -225,7 +206,7 @@ public class Canvas { * @return true if the device that the current layer draws into is opaque */ public boolean isOpaque() { - return native_isOpaque(mNativeCanvasWrapper); + return nIsOpaque(mNativeCanvasWrapper); } /** @@ -234,7 +215,7 @@ public class Canvas { * @return the width of the current drawing layer */ public int getWidth() { - return native_getWidth(mNativeCanvasWrapper); + return nGetWidth(mNativeCanvasWrapper); } /** @@ -243,7 +224,7 @@ public class Canvas { * @return the height of the current drawing layer */ public int getHeight() { - return native_getHeight(mNativeCanvasWrapper); + return nGetHeight(mNativeCanvasWrapper); } /** @@ -371,7 +352,7 @@ public class Canvas { * @return The value to pass to restoreToCount() to balance this save() */ public int save() { - return native_save(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG); + return nSave(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG); } /** @@ -391,7 +372,7 @@ public class Canvas { * @return The value to pass to restoreToCount() to balance this save() */ public int save(@Saveflags int saveFlags) { - return native_save(mNativeCanvasWrapper, saveFlags); + return nSave(mNativeCanvasWrapper, saveFlags); } /** @@ -443,7 +424,7 @@ public class Canvas { */ public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint, @Saveflags int saveFlags) { - return native_saveLayer(mNativeCanvasWrapper, left, top, right, bottom, + return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom, paint != null ? paint.getNativeInstance() : 0, saveFlags); } @@ -503,7 +484,7 @@ public class Canvas { public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, @Saveflags int saveFlags) { alpha = Math.min(255, Math.max(0, alpha)); - return native_saveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom, + return nSaveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom, alpha, saveFlags); } @@ -521,7 +502,7 @@ public class Canvas { */ public void restore() { boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated(); - native_restore(mNativeCanvasWrapper, throwOnUnderflow); + nRestore(mNativeCanvasWrapper, throwOnUnderflow); } /** @@ -529,7 +510,7 @@ public class Canvas { * This will equal # save() calls - # restore() calls. */ public int getSaveCount() { - return native_getSaveCount(mNativeCanvasWrapper); + return nGetSaveCount(mNativeCanvasWrapper); } /** @@ -547,7 +528,7 @@ public class Canvas { */ public void restoreToCount(int saveCount) { boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated(); - native_restoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow); + nRestoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow); } /** @@ -558,7 +539,7 @@ public class Canvas { */ public void translate(float dx, float dy) { if (dx == 0.0f && dy == 0.0f) return; - native_translate(mNativeCanvasWrapper, dx, dy); + nTranslate(mNativeCanvasWrapper, dx, dy); } /** @@ -569,7 +550,7 @@ public class Canvas { */ public void scale(float sx, float sy) { if (sx == 1.0f && sy == 1.0f) return; - native_scale(mNativeCanvasWrapper, sx, sy); + nScale(mNativeCanvasWrapper, sx, sy); } /** @@ -594,7 +575,7 @@ public class Canvas { */ public void rotate(float degrees) { if (degrees == 0.0f) return; - native_rotate(mNativeCanvasWrapper, degrees); + nRotate(mNativeCanvasWrapper, degrees); } /** @@ -619,7 +600,7 @@ public class Canvas { */ public void skew(float sx, float sy) { if (sx == 0.0f && sy == 0.0f) return; - native_skew(mNativeCanvasWrapper, sx, sy); + nSkew(mNativeCanvasWrapper, sx, sy); } /** @@ -629,7 +610,7 @@ public class Canvas { * @param matrix The matrix to preconcatenate with the current matrix */ public void concat(@Nullable Matrix matrix) { - if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance); + if (matrix != null) nConcat(mNativeCanvasWrapper, matrix.native_instance); } /** @@ -646,7 +627,7 @@ public class Canvas { * @see #concat(Matrix) */ public void setMatrix(@Nullable Matrix matrix) { - native_setMatrix(mNativeCanvasWrapper, + nSetMatrix(mNativeCanvasWrapper, matrix == null ? 0 : matrix.native_instance); } @@ -662,7 +643,7 @@ public class Canvas { */ @Deprecated public void getMatrix(@NonNull Matrix ctm) { - native_getCTM(mNativeCanvasWrapper, ctm.native_instance); + nGetCTM(mNativeCanvasWrapper, ctm.native_instance); } /** @@ -691,7 +672,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) { - return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, + return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt); } @@ -704,7 +685,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(@NonNull Rect rect, @NonNull Region.Op op) { - return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, + return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt); } @@ -716,7 +697,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(@NonNull RectF rect) { - return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, + return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, Region.Op.INTERSECT.nativeInt); } @@ -728,7 +709,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(@NonNull Rect rect) { - return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, + return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, Region.Op.INTERSECT.nativeInt); } @@ -749,7 +730,7 @@ public class Canvas { */ public boolean clipRect(float left, float top, float right, float bottom, @NonNull Region.Op op) { - return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt); + return nClipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt); } /** @@ -766,7 +747,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(float left, float top, float right, float bottom) { - return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, + return nClipRect(mNativeCanvasWrapper, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } @@ -784,7 +765,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(int left, int top, int right, int bottom) { - return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, + return nClipRect(mNativeCanvasWrapper, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } @@ -796,7 +777,7 @@ public class Canvas { * @return true if the resulting is non-empty */ public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) { - return native_clipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt); + return nClipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt); } /** @@ -825,7 +806,7 @@ public class Canvas { */ @Deprecated public boolean clipRegion(@NonNull Region region, @NonNull Region.Op op) { - return native_clipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt); + return nClipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt); } /** @@ -856,7 +837,7 @@ public class Canvas { nativeFilter = filter.mNativeInt; } mDrawFilter = filter; - nativeSetDrawFilter(mNativeCanvasWrapper, nativeFilter); + nSetDrawFilter(mNativeCanvasWrapper, nativeFilter); } /** @@ -904,7 +885,7 @@ public class Canvas { * does not intersect with the canvas' clip */ public boolean quickReject(@NonNull RectF rect, @NonNull EdgeType type) { - return native_quickReject(mNativeCanvasWrapper, + return nQuickReject(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom); } @@ -924,7 +905,7 @@ public class Canvas { * does not intersect with the canvas' clip */ public boolean quickReject(@NonNull Path path, @NonNull EdgeType type) { - return native_quickReject(mNativeCanvasWrapper, path.readOnlyNI()); + return nQuickReject(mNativeCanvasWrapper, path.readOnlyNI()); } /** @@ -949,7 +930,7 @@ public class Canvas { */ public boolean quickReject(float left, float top, float right, float bottom, @NonNull EdgeType type) { - return native_quickReject(mNativeCanvasWrapper, left, top, right, bottom); + return nQuickReject(mNativeCanvasWrapper, left, top, right, bottom); } /** @@ -963,7 +944,7 @@ public class Canvas { * @return true if the current clip is non-empty. */ public boolean getClipBounds(@Nullable Rect bounds) { - return native_getClipBounds(mNativeCanvasWrapper, bounds); + return nGetClipBounds(mNativeCanvasWrapper, bounds); } /** @@ -978,209 +959,206 @@ public class Canvas { } /** - * Fill the entire canvas' bitmap (restricted to the current clip) with the - * specified RGB color, using srcover porterduff mode. + * Save the canvas state, draw the picture, and restore the canvas state. + * This differs from picture.draw(canvas), which does not perform any + * save/restore. * - * @param r red component (0..255) of the color to draw onto the canvas - * @param g green component (0..255) of the color to draw onto the canvas - * @param b blue component (0..255) of the color to draw onto the canvas + *

+ * Note: This forces the picture to internally call + * {@link Picture#endRecording} in order to prepare for playback. + * + * @param picture The picture to be drawn */ - public void drawRGB(int r, int g, int b) { - drawColor(Color.rgb(r, g, b)); + public void drawPicture(@NonNull Picture picture) { + picture.endRecording(); + int restoreCount = save(); + picture.draw(this); + restoreToCount(restoreCount); } /** - * Fill the entire canvas' bitmap (restricted to the current clip) with the - * specified ARGB color, using srcover porterduff mode. - * - * @param a alpha component (0..255) of the color to draw onto the canvas - * @param r red component (0..255) of the color to draw onto the canvas - * @param g green component (0..255) of the color to draw onto the canvas - * @param b blue component (0..255) of the color to draw onto the canvas + * Draw the picture, stretched to fit into the dst rectangle. */ - public void drawARGB(int a, int r, int g, int b) { - drawColor(Color.argb(a, r, g, b)); - } - - /** - * Fill the entire canvas' bitmap (restricted to the current clip) with the - * specified color, using srcover porterduff mode. - * - * @param color the color to draw onto the canvas - */ - public void drawColor(@ColorInt int color) { - native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt); - } - - /** - * Fill the entire canvas' bitmap (restricted to the current clip) with the - * specified color and porter-duff xfermode. - * - * @param color the color to draw with - * @param mode the porter-duff mode to apply to the color - */ - public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) { - native_drawColor(mNativeCanvasWrapper, color, mode.nativeInt); - } - - /** - * Fill the entire canvas' bitmap (restricted to the current clip) with - * the specified paint. This is equivalent (but faster) to drawing an - * infinitely large rectangle with the specified paint. - * - * @param paint The paint used to draw onto the canvas - */ - public void drawPaint(@NonNull Paint paint) { - native_drawPaint(mNativeCanvasWrapper, paint.getNativeInstance()); - } - - /** - * Draw a series of points. Each point is centered at the coordinate - * specified by pts[], and its diameter is specified by the paint's stroke - * width (as transformed by the canvas' CTM), with special treatment for - * a stroke width of 0, which always draws exactly 1 pixel (or at most 4 - * if antialiasing is enabled). The shape of the point is controlled by - * the paint's Cap type. The shape is a square, unless the cap type is - * Round, in which case the shape is a circle. - * - * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...] - * @param offset Number of values to skip before starting to draw. - * @param count The number of values to process, after skipping offset - * of them. Since one point uses two values, the number of - * "points" that are drawn is really (count >> 1). - * @param paint The paint used to draw the points - */ - public void drawPoints(@Size(multiple=2) float[] pts, int offset, int count, - @NonNull Paint paint) { - native_drawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); - } - - /** - * Helper for drawPoints() that assumes you want to draw the entire array - */ - public void drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint) { - drawPoints(pts, 0, pts.length, paint); - } - - /** - * Helper for drawPoints() for drawing a single point. - */ - public void drawPoint(float x, float y, @NonNull Paint paint) { - native_drawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance()); - } - - /** - * Draw a line segment with the specified start and stop x,y coordinates, - * using the specified paint. - * - *

Note that since a line is always "framed", the Style is ignored in the paint.

- * - *

Degenerate lines (length is 0) will not be drawn.

- * - * @param startX The x-coordinate of the start point of the line - * @param startY The y-coordinate of the start point of the line - * @param paint The paint used to draw the line - */ - public void drawLine(float startX, float startY, float stopX, float stopY, - @NonNull Paint paint) { - native_drawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance()); - } - - /** - * Draw a series of lines. Each line is taken from 4 consecutive values - * in the pts array. Thus to draw 1 line, the array must contain at least 4 - * values. This is logically the same as drawing the array as follows: - * drawLine(pts[0], pts[1], pts[2], pts[3]) followed by - * drawLine(pts[4], pts[5], pts[6], pts[7]) and so on. - * - * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...] - * @param offset Number of values in the array to skip before drawing. - * @param count The number of values in the array to process, after - * skipping "offset" of them. Since each line uses 4 values, - * the number of "lines" that are drawn is really - * (count >> 2). - * @param paint The paint used to draw the points - */ - public void drawLines(@Size(multiple=4) @NonNull float[] pts, int offset, int count, - @NonNull Paint paint) { - native_drawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); - } - - public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint) { - drawLines(pts, 0, pts.length, paint); - } - - /** - * Draw the specified Rect using the specified paint. The rectangle will - * be filled or framed based on the Style in the paint. - * - * @param rect The rect to be drawn - * @param paint The paint used to draw the rect - */ - public void drawRect(@NonNull RectF rect, @NonNull Paint paint) { - native_drawRect(mNativeCanvasWrapper, - rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance()); - } - - /** - * Draw the specified Rect using the specified Paint. The rectangle - * will be filled or framed based on the Style in the paint. - * - * @param r The rectangle to be drawn. - * @param paint The paint used to draw the rectangle - */ - public void drawRect(@NonNull Rect r, @NonNull Paint paint) { - drawRect(r.left, r.top, r.right, r.bottom, paint); - } - - - /** - * Draw the specified Rect using the specified paint. The rectangle will - * be filled or framed based on the Style in the paint. - * - * @param left The left side of the rectangle to be drawn - * @param top The top side of the rectangle to be drawn - * @param right The right side of the rectangle to be drawn - * @param bottom The bottom side of the rectangle to be drawn - * @param paint The paint used to draw the rect - */ - public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) { - native_drawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); - } - - /** - * Draw the specified oval using the specified paint. The oval will be - * filled or framed based on the Style in the paint. - * - * @param oval The rectangle bounds of the oval to be drawn - */ - public void drawOval(@NonNull RectF oval, @NonNull Paint paint) { - if (oval == null) { - throw new NullPointerException(); + public void drawPicture(@NonNull Picture picture, @NonNull Rect dst) { + save(); + translate(dst.left, dst.top); + if (picture.getWidth() > 0 && picture.getHeight() > 0) { + scale((float) dst.width() / picture.getWidth(), + (float) dst.height() / picture.getHeight()); } - drawOval(oval.left, oval.top, oval.right, oval.bottom, paint); + drawPicture(picture); + restore(); } /** - * Draw the specified oval using the specified paint. The oval will be - * filled or framed based on the Style in the paint. + * Draw the picture, stretched to fit into the dst rectangle. */ - public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) { - native_drawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); + public void drawPicture(@NonNull Picture picture, @NonNull RectF dst) { + save(); + translate(dst.left, dst.top); + if (picture.getWidth() > 0 && picture.getHeight() > 0) { + scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight()); + } + drawPicture(picture); + restore(); + } + + public enum VertexMode { + TRIANGLES(0), + TRIANGLE_STRIP(1), + TRIANGLE_FAN(2); + + VertexMode(int nativeInt) { + this.nativeInt = nativeInt; + } + + /** + * @hide + */ + public final int nativeInt; } /** - * Draw the specified circle using the specified paint. If radius is <= 0, - * then nothing will be drawn. The circle will be filled or framed based - * on the Style in the paint. + * Releases the resources associated with this canvas. * - * @param cx The x-coordinate of the center of the cirle to be drawn - * @param cy The y-coordinate of the center of the cirle to be drawn - * @param radius The radius of the cirle to be drawn - * @param paint The paint used to draw the circle + * @hide */ - public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) { - native_drawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance()); + public void release() { + mNativeCanvasWrapper = 0; + if (mFinalizer != null) { + mFinalizer.run(); + mFinalizer = null; + } + } + + /** + * Free up as much memory as possible from private caches (e.g. fonts, images) + * + * @hide + */ + public static void freeCaches() { + nFreeCaches(); + } + + /** + * Free up text layout caches + * + * @hide + */ + public static void freeTextLayoutCaches() { + nFreeTextLayoutCaches(); + } + + private static native void nFreeCaches(); + private static native void nFreeTextLayoutCaches(); + private static native long nInitRaster(Bitmap bitmap); + private static native long nGetNativeFinalizer(); + + // ---------------- @FastNative ------------------- + + @FastNative + private static native void nSetBitmap(long canvasHandle, + Bitmap bitmap); + @FastNative + private static native boolean nIsOpaque(long canvasHandle); + @FastNative + private static native void nSetHighContrastText(long renderer, boolean highContrastText); + @FastNative + private static native int nGetWidth(long canvasHandle); + @FastNative + private static native int nGetHeight(long canvasHandle); + + @FastNative + private static native int nSave(long canvasHandle, int saveFlags); + @FastNative + private static native int nSaveLayer(long nativeCanvas, float l, + float t, float r, float b, + long nativePaint, + int layerFlags); + @FastNative + private static native int nSaveLayerAlpha(long nativeCanvas, float l, + float t, float r, float b, + int alpha, int layerFlags); + @FastNative + private static native void nRestore(long canvasHandle, boolean tolerateUnderflow); + @FastNative + private static native void nRestoreToCount(long canvasHandle, + int saveCount, + boolean tolerateUnderflow); + @FastNative + private static native int nGetSaveCount(long canvasHandle); + + @FastNative + private static native void nTranslate(long canvasHandle, + float dx, float dy); + @FastNative + private static native void nScale(long canvasHandle, + float sx, float sy); + @FastNative + private static native void nRotate(long canvasHandle, float degrees); + @FastNative + private static native void nSkew(long canvasHandle, + float sx, float sy); + @FastNative + private static native void nConcat(long nativeCanvas, + long nativeMatrix); + @FastNative + private static native void nSetMatrix(long nativeCanvas, + long nativeMatrix); + @FastNative + private static native boolean nClipRect(long nativeCanvas, + float left, float top, + float right, float bottom, + int regionOp); + @FastNative + private static native boolean nClipPath(long nativeCanvas, + long nativePath, + int regionOp); + @FastNative + private static native boolean nClipRegion(long nativeCanvas, + long nativeRegion, + int regionOp); + @FastNative + private static native void nSetDrawFilter(long nativeCanvas, + long nativeFilter); + @FastNative + private static native boolean nGetClipBounds(long nativeCanvas, + Rect bounds); + @FastNative + private static native void nGetCTM(long nativeCanvas, + long nativeMatrix); + @FastNative + private static native boolean nQuickReject(long nativeCanvas, + long nativePath); + @FastNative + private static native boolean nQuickReject(long nativeCanvas, + float left, float top, + float right, float bottom); + + /** + *

Draw the specified arc, which will be scaled to fit inside the + * specified oval.

+ * + *

If the start angle is negative or >= 360, the start angle is treated + * as start angle modulo 360.

+ * + *

If the sweep angle is >= 360, then the oval is drawn + * completely. Note that this differs slightly from SkPath::arcTo, which + * treats the sweep angle modulo 360. If the sweep angle is negative, + * the sweep angle is treated as sweep angle modulo 360

+ * + *

The arc is drawn clockwise. An angle of 0 degrees correspond to the + * geometric angle of 0 degrees (3 o'clock on a watch.)

+ * + * @param startAngle Starting angle (in degrees) where the arc begins + * @param sweepAngle Sweep angle (in degrees) measured clockwise + * @param useCenter If true, include the center of the oval in the arc, and + close it if it is being stroked. This will draw a wedge + * @param paint The paint used to draw the arc + */ + public void drawArc(float left, float top, float right, float bottom, float startAngle, + float sweepAngle, boolean useCenter, @NonNull Paint paint) { + super.drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint); } /** @@ -1208,126 +1186,20 @@ public class Canvas { */ public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint) { - drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter, - paint); + super.drawArc(oval, startAngle, sweepAngle, useCenter, paint); } /** - *

Draw the specified arc, which will be scaled to fit inside the - * specified oval.

+ * Fill the entire canvas' bitmap (restricted to the current clip) with the + * specified ARGB color, using srcover porterduff mode. * - *

If the start angle is negative or >= 360, the start angle is treated - * as start angle modulo 360.

- * - *

If the sweep angle is >= 360, then the oval is drawn - * completely. Note that this differs slightly from SkPath::arcTo, which - * treats the sweep angle modulo 360. If the sweep angle is negative, - * the sweep angle is treated as sweep angle modulo 360

- * - *

The arc is drawn clockwise. An angle of 0 degrees correspond to the - * geometric angle of 0 degrees (3 o'clock on a watch.)

- * - * @param startAngle Starting angle (in degrees) where the arc begins - * @param sweepAngle Sweep angle (in degrees) measured clockwise - * @param useCenter If true, include the center of the oval in the arc, and - close it if it is being stroked. This will draw a wedge - * @param paint The paint used to draw the arc + * @param a alpha component (0..255) of the color to draw onto the canvas + * @param r red component (0..255) of the color to draw onto the canvas + * @param g green component (0..255) of the color to draw onto the canvas + * @param b blue component (0..255) of the color to draw onto the canvas */ - public void drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, boolean useCenter, @NonNull Paint paint) { - native_drawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle, - useCenter, paint.getNativeInstance()); - } - - /** - * Draw the specified round-rect using the specified paint. The roundrect - * will be filled or framed based on the Style in the paint. - * - * @param rect The rectangular bounds of the roundRect to be drawn - * @param rx The x-radius of the oval used to round the corners - * @param ry The y-radius of the oval used to round the corners - * @param paint The paint used to draw the roundRect - */ - public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { - drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint); - } - - /** - * Draw the specified round-rect using the specified paint. The roundrect - * will be filled or framed based on the Style in the paint. - * - * @param rx The x-radius of the oval used to round the corners - * @param ry The y-radius of the oval used to round the corners - * @param paint The paint used to draw the roundRect - */ - public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - @NonNull Paint paint) { - native_drawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, paint.getNativeInstance()); - } - - /** - * Draw the specified path using the specified paint. The path will be - * filled or framed based on the Style in the paint. - * - * @param path The path to be drawn - * @param paint The paint used to draw the path - */ - public void drawPath(@NonNull Path path, @NonNull Paint paint) { - if (path.isSimplePath && path.rects != null) { - native_drawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance()); - } else { - native_drawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance()); - } - } - - /** - * @hide - */ - protected void throwIfCannotDraw(Bitmap bitmap) { - if (bitmap.isRecycled()) { - throw new RuntimeException("Canvas: trying to use a recycled bitmap " + bitmap); - } - if (!bitmap.isPremultiplied() && bitmap.getConfig() == Bitmap.Config.ARGB_8888 && - bitmap.hasAlpha()) { - throw new RuntimeException("Canvas: trying to use a non-premultiplied bitmap " - + bitmap); - } - } - - /** - * Draws the specified bitmap as an N-patch (most often, a 9-patches.) - * - * @param patch The ninepatch object to render - * @param dst The destination rectangle. - * @param paint The paint to draw the bitmap with. may be null - * - * @hide - */ - public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) { - Bitmap bitmap = patch.getBitmap(); - throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); - native_drawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, - dst.left, dst.top, dst.right, dst.bottom, nativePaint, - mDensity, patch.getDensity()); - } - - /** - * Draws the specified bitmap as an N-patch (most often, a 9-patches.) - * - * @param patch The ninepatch object to render - * @param dst The destination rectangle. - * @param paint The paint to draw the bitmap with. may be null - * - * @hide - */ - public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) { - Bitmap bitmap = patch.getBitmap(); - throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); - native_drawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, - dst.left, dst.top, dst.right, dst.bottom, nativePaint, - mDensity, patch.getDensity()); + public void drawARGB(int a, int r, int g, int b) { + super.drawARGB(a, r, g, b); } /** @@ -1350,57 +1222,19 @@ public class Canvas { * @param paint The paint used to draw the bitmap (may be null) */ public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) { - throwIfCannotDraw(bitmap); - native_drawBitmap(mNativeCanvasWrapper, bitmap, left, top, - paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity, bitmap.mDensity); + super.drawBitmap(bitmap, left, top, paint); } /** - * Draw the specified bitmap, scaling/translating automatically to fill - * the destination rectangle. If the source rectangle is not null, it - * specifies the subset of the bitmap to draw. + * Draw the bitmap using the specified matrix. * - *

Note: if the paint contains a maskfilter that generates a mask which - * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), - * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. - * Thus the color outside of the original width/height will be the edge - * color replicated. - * - *

This function ignores the density associated with the bitmap. - * This is because the source and destination rectangle coordinate - * spaces are in their respective densities, so must already have the - * appropriate scaling factor applied. - * - * @param bitmap The bitmap to be drawn - * @param src May be null. The subset of the bitmap to be drawn - * @param dst The rectangle that the bitmap will be scaled/translated - * to fit into + * @param bitmap The bitmap to draw + * @param matrix The matrix used to transform the bitmap when it is drawn * @param paint May be null. The paint used to draw the bitmap */ - public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, - @Nullable Paint paint) { - if (dst == null) { - throw new NullPointerException(); - } - throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); - - float left, top, right, bottom; - if (src == null) { - left = top = 0; - right = bitmap.getWidth(); - bottom = bitmap.getHeight(); - } else { - left = src.left; - right = src.right; - top = src.top; - bottom = src.bottom; - } - - native_drawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom, - dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity, - bitmap.mDensity); - } + public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) { + super.drawBitmap(bitmap, matrix, paint); + } /** * Draw the specified bitmap, scaling/translating automatically to fill @@ -1426,29 +1260,36 @@ public class Canvas { */ public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, @Nullable Paint paint) { - if (dst == null) { - throw new NullPointerException(); - } - throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); - - int left, top, right, bottom; - if (src == null) { - left = top = 0; - right = bitmap.getWidth(); - bottom = bitmap.getHeight(); - } else { - left = src.left; - right = src.right; - top = src.top; - bottom = src.bottom; - } - - native_drawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom, - dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity, - bitmap.mDensity); + super.drawBitmap(bitmap, src, dst, paint); } + /** + * Draw the specified bitmap, scaling/translating automatically to fill + * the destination rectangle. If the source rectangle is not null, it + * specifies the subset of the bitmap to draw. + * + *

Note: if the paint contains a maskfilter that generates a mask which + * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), + * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. + * Thus the color outside of the original width/height will be the edge + * color replicated. + * + *

This function ignores the density associated with the bitmap. + * This is because the source and destination rectangle coordinate + * spaces are in their respective densities, so must already have the + * appropriate scaling factor applied. + * + * @param bitmap The bitmap to be drawn + * @param src May be null. The subset of the bitmap to be drawn + * @param dst The rectangle that the bitmap will be scaled/translated + * to fit into + * @param paint May be null. The paint used to draw the bitmap + */ + public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, + @Nullable Paint paint) { + super.drawBitmap(bitmap, src, dst, paint); + } + /** * Treat the specified array of colors as a bitmap, and draw it. This gives * the same result as first creating a bitmap from the array, and then @@ -1476,29 +1317,7 @@ public class Canvas { @Deprecated public void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, @Nullable Paint paint) { - // check for valid input - if (width < 0) { - throw new IllegalArgumentException("width must be >= 0"); - } - if (height < 0) { - throw new IllegalArgumentException("height must be >= 0"); - } - if (Math.abs(stride) < width) { - throw new IllegalArgumentException("abs(stride) must be >= width"); - } - int lastScanline = offset + (height - 1) * stride; - int length = colors.length; - if (offset < 0 || (offset + width > length) || lastScanline < 0 - || (lastScanline + width > length)) { - throw new ArrayIndexOutOfBoundsException(); - } - // quick escape if there's nothing to draw - if (width == 0 || height == 0) { - return; - } - // punch down to native for the actual draw - native_drawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha, - paint != null ? paint.getNativeInstance() : 0); + super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint); } /** @@ -1512,30 +1331,7 @@ public class Canvas { @Deprecated public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasAlpha, @Nullable Paint paint) { - // call through to the common float version - drawBitmap(colors, offset, stride, (float)x, (float)y, width, height, - hasAlpha, paint); - } - - /** - * Draw the bitmap using the specified matrix. - * - * @param bitmap The bitmap to draw - * @param matrix The matrix used to transform the bitmap when it is drawn - * @param paint May be null. The paint used to draw the bitmap - */ - public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) { - nativeDrawBitmapMatrix(mNativeCanvasWrapper, bitmap, matrix.ni(), - paint != null ? paint.getNativeInstance() : 0); - } - - /** - * @hide - */ - protected static void checkRange(int length, int offset, int count) { - if ((offset | count) < 0 || offset + count > length) { - throw new ArrayIndexOutOfBoundsException(); - } + super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint); } /** @@ -1567,89 +1363,299 @@ public class Canvas { public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight, @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset, @Nullable Paint paint) { - if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) { - throw new ArrayIndexOutOfBoundsException(); - } - if (meshWidth == 0 || meshHeight == 0) { - return; - } - int count = (meshWidth + 1) * (meshHeight + 1); - // we mul by 2 since we need two floats per vertex - checkRange(verts.length, vertOffset, count * 2); - if (colors != null) { - // no mul by 2, since we need only 1 color per vertex - checkRange(colors.length, colorOffset, count); - } - nativeDrawBitmapMesh(mNativeCanvasWrapper, bitmap, meshWidth, meshHeight, - verts, vertOffset, colors, colorOffset, - paint != null ? paint.getNativeInstance() : 0); - } - - public enum VertexMode { - TRIANGLES(0), - TRIANGLE_STRIP(1), - TRIANGLE_FAN(2); - - VertexMode(int nativeInt) { - this.nativeInt = nativeInt; - } - - /** - * @hide - */ - public final int nativeInt; + super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, + paint); } /** - * Draw the array of vertices, interpreted as triangles (based on mode). The - * verts array is required, and specifies the x,y pairs for each vertex. If - * texs is non-null, then it is used to specify the coordinate in shader - * coordinates to use at each vertex (the paint must have a shader in this - * case). If there is no texs array, but there is a color array, then each - * color is interpolated across its corresponding triangle in a gradient. If - * both texs and colors arrays are present, then they behave as before, but - * the resulting color at each pixels is the result of multiplying the - * colors from the shader and the color-gradient together. The indices array - * is optional, but if it is present, then it is used to specify the index - * of each triangle, rather than just walking through the arrays in order. + * Draw the specified circle using the specified paint. If radius is <= 0, + * then nothing will be drawn. The circle will be filled or framed based + * on the Style in the paint. * - * @param mode How to interpret the array of vertices - * @param vertexCount The number of values in the vertices array (and - * corresponding texs and colors arrays if non-null). Each logical - * vertex is two values (x, y), vertexCount must be a multiple of 2. - * @param verts Array of vertices for the mesh - * @param vertOffset Number of values in the verts to skip before drawing. - * @param texs May be null. If not null, specifies the coordinates to sample - * into the current shader (e.g. bitmap tile or gradient) - * @param texOffset Number of values in texs to skip before drawing. - * @param colors May be null. If not null, specifies a color for each - * vertex, to be interpolated across the triangle. - * @param colorOffset Number of values in colors to skip before drawing. - * @param indices If not null, array of indices to reference into the - * vertex (texs, colors) array. - * @param indexCount number of entries in the indices array (if not null). - * @param paint Specifies the shader to use if the texs array is non-null. + * @param cx The x-coordinate of the center of the cirle to be drawn + * @param cy The y-coordinate of the center of the cirle to be drawn + * @param radius The radius of the cirle to be drawn + * @param paint The paint used to draw the circle */ - public void drawVertices(@NonNull 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, + public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) { + super.drawCircle(cx, cy, radius, paint); + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with the + * specified color, using srcover porterduff mode. + * + * @param color the color to draw onto the canvas + */ + public void drawColor(@ColorInt int color) { + super.drawColor(color); + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with the + * specified color and porter-duff xfermode. + * + * @param color the color to draw with + * @param mode the porter-duff mode to apply to the color + */ + public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) { + super.drawColor(color, mode); + } + + /** + * Draw a line segment with the specified start and stop x,y coordinates, + * using the specified paint. + * + *

Note that since a line is always "framed", the Style is ignored in the paint.

+ * + *

Degenerate lines (length is 0) will not be drawn.

+ * + * @param startX The x-coordinate of the start point of the line + * @param startY The y-coordinate of the start point of the line + * @param paint The paint used to draw the line + */ + public void drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint) { - checkRange(verts.length, vertOffset, vertexCount); - if (isHardwareAccelerated()) { - return; - } - if (texs != null) { - checkRange(texs.length, texOffset, vertexCount); - } - if (colors != null) { - checkRange(colors.length, colorOffset, vertexCount / 2); - } - if (indices != null) { - checkRange(indices.length, indexOffset, indexCount); - } - nativeDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts, - vertOffset, texs, texOffset, colors, colorOffset, - indices, indexOffset, indexCount, paint.getNativeInstance()); + super.drawLine(startX, startY, stopX, stopY, paint); + } + + /** + * Draw a series of lines. Each line is taken from 4 consecutive values + * in the pts array. Thus to draw 1 line, the array must contain at least 4 + * values. This is logically the same as drawing the array as follows: + * drawLine(pts[0], pts[1], pts[2], pts[3]) followed by + * drawLine(pts[4], pts[5], pts[6], pts[7]) and so on. + * + * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...] + * @param offset Number of values in the array to skip before drawing. + * @param count The number of values in the array to process, after + * skipping "offset" of them. Since each line uses 4 values, + * the number of "lines" that are drawn is really + * (count >> 2). + * @param paint The paint used to draw the points + */ + public void drawLines(@Size(multiple=4) @NonNull float[] pts, int offset, int count, + @NonNull Paint paint) { + super.drawLines(pts, offset, count, paint); + } + + public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint) { + super.drawLines(pts, paint); + } + + /** + * Draw the specified oval using the specified paint. The oval will be + * filled or framed based on the Style in the paint. + */ + public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) { + super.drawOval(left, top, right, bottom, paint); + } + + /** + * Draw the specified oval using the specified paint. The oval will be + * filled or framed based on the Style in the paint. + * + * @param oval The rectangle bounds of the oval to be drawn + */ + public void drawOval(@NonNull RectF oval, @NonNull Paint paint) { + super.drawOval(oval, paint); + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with + * the specified paint. This is equivalent (but faster) to drawing an + * infinitely large rectangle with the specified paint. + * + * @param paint The paint used to draw onto the canvas + */ + public void drawPaint(@NonNull Paint paint) { + super.drawPaint(paint); + } + + /** + * Draws the specified bitmap as an N-patch (most often, a 9-patches.) + * + * @param patch The ninepatch object to render + * @param dst The destination rectangle. + * @param paint The paint to draw the bitmap with. may be null + * + * @hide + */ + public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) { + super.drawPatch(patch, dst, paint); + } + + /** + * Draws the specified bitmap as an N-patch (most often, a 9-patches.) + * + * @param patch The ninepatch object to render + * @param dst The destination rectangle. + * @param paint The paint to draw the bitmap with. may be null + * + * @hide + */ + public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) { + super.drawPatch(patch, dst, paint); + } + + /** + * Draw the specified path using the specified paint. The path will be + * filled or framed based on the Style in the paint. + * + * @param path The path to be drawn + * @param paint The paint used to draw the path + */ + public void drawPath(@NonNull Path path, @NonNull Paint paint) { + super.drawPath(path, paint); + } + + /** + * Helper for drawPoints() for drawing a single point. + */ + public void drawPoint(float x, float y, @NonNull Paint paint) { + super.drawPoint(x, y, paint); + } + + /** + * Draw a series of points. Each point is centered at the coordinate + * specified by pts[], and its diameter is specified by the paint's stroke + * width (as transformed by the canvas' CTM), with special treatment for + * a stroke width of 0, which always draws exactly 1 pixel (or at most 4 + * if antialiasing is enabled). The shape of the point is controlled by + * the paint's Cap type. The shape is a square, unless the cap type is + * Round, in which case the shape is a circle. + * + * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...] + * @param offset Number of values to skip before starting to draw. + * @param count The number of values to process, after skipping offset + * of them. Since one point uses two values, the number of + * "points" that are drawn is really (count >> 1). + * @param paint The paint used to draw the points + */ + public void drawPoints(@Size(multiple=2) float[] pts, int offset, int count, + @NonNull Paint paint) { + super.drawPoints(pts, offset, count, paint); + } + + /** + * Helper for drawPoints() that assumes you want to draw the entire array + */ + public void drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint) { + super.drawPoints(pts, paint); + } + + /** + * Draw the text in the array, with each character's origin specified by + * the pos array. + * + * @param text The text to be drawn + * @param index The index of the first character to draw + * @param count The number of characters to draw, starting from index. + * @param pos Array of [x,y] positions, used to position each + * character + * @param paint The paint used for the text (e.g. color, size, style) + * + * @deprecated This method does not support glyph composition and decomposition and + * should therefore not be used to render complex scripts. It also doesn't + * handle supplementary characters (eg emoji). + */ + @Deprecated + public void drawPosText(@NonNull char[] text, int index, int count, + @NonNull @Size(multiple=2) float[] pos, + @NonNull Paint paint) { + super.drawPosText(text, index, count, pos, paint); + } + + /** + * Draw the text in the array, with each character's origin specified by + * the pos array. + * + * @param text The text to be drawn + * @param pos Array of [x,y] positions, used to position each character + * @param paint The paint used for the text (e.g. color, size, style) + * + * @deprecated This method does not support glyph composition and decomposition and + * should therefore not be used to render complex scripts. It also doesn't + * handle supplementary characters (eg emoji). + */ + @Deprecated + public void drawPosText(@NonNull String text, @NonNull @Size(multiple=2) float[] pos, + @NonNull Paint paint) { + super.drawPosText(text, pos, paint); + } + + /** + * Draw the specified Rect using the specified paint. The rectangle will + * be filled or framed based on the Style in the paint. + * + * @param left The left side of the rectangle to be drawn + * @param top The top side of the rectangle to be drawn + * @param right The right side of the rectangle to be drawn + * @param bottom The bottom side of the rectangle to be drawn + * @param paint The paint used to draw the rect + */ + public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) { + super.drawRect(left, top, right, bottom, paint); + } + + /** + * Draw the specified Rect using the specified Paint. The rectangle + * will be filled or framed based on the Style in the paint. + * + * @param r The rectangle to be drawn. + * @param paint The paint used to draw the rectangle + */ + public void drawRect(@NonNull Rect r, @NonNull Paint paint) { + super.drawRect(r, paint); + } + + /** + * Draw the specified Rect using the specified paint. The rectangle will + * be filled or framed based on the Style in the paint. + * + * @param rect The rect to be drawn + * @param paint The paint used to draw the rect + */ + public void drawRect(@NonNull RectF rect, @NonNull Paint paint) { + super.drawRect(rect, paint); + } + + /** + * Fill the entire canvas' bitmap (restricted to the current clip) with the + * specified RGB color, using srcover porterduff mode. + * + * @param r red component (0..255) of the color to draw onto the canvas + * @param g green component (0..255) of the color to draw onto the canvas + * @param b blue component (0..255) of the color to draw onto the canvas + */ + public void drawRGB(int r, int g, int b) { + super.drawRGB(r, g, b); + } + + /** + * Draw the specified round-rect using the specified paint. The roundrect + * will be filled or framed based on the Style in the paint. + * + * @param rx The x-radius of the oval used to round the corners + * @param ry The y-radius of the oval used to round the corners + * @param paint The paint used to draw the roundRect + */ + public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, + @NonNull Paint paint) { + super.drawRoundRect(left, top, right, bottom, rx, ry, paint); + } + + /** + * Draw the specified round-rect using the specified paint. The roundrect + * will be filled or framed based on the Style in the paint. + * + * @param rect The rectangular bounds of the roundRect to be drawn + * @param rx The x-radius of the oval used to round the corners + * @param ry The y-radius of the oval used to round the corners + * @param paint The paint used to draw the roundRect + */ + public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { + super.drawRoundRect(rect, rx, ry, paint); } /** @@ -1663,46 +1669,7 @@ public class Canvas { */ public void drawText(@NonNull char[] text, int index, int count, float x, float y, @NonNull Paint paint) { - if ((index | count | (index + count) | - (text.length - index - count)) < 0) { - throw new IndexOutOfBoundsException(); - } - native_drawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags, - paint.getNativeInstance(), paint.mNativeTypeface); - } - - /** - * Draw the text, with origin at (x,y), using the specified paint. The - * origin is interpreted based on the Align setting in the paint. - * - * @param text The text to be drawn - * @param x The x-coordinate of the origin of the text being drawn - * @param y The y-coordinate of the baseline of the text being drawn - * @param paint The paint used for the text (e.g. color, size, style) - */ - public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { - native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, - paint.getNativeInstance(), paint.mNativeTypeface); - } - - /** - * Draw the text, with origin at (x,y), using the specified paint. - * The origin is interpreted based on the Align setting in the paint. - * - * @param text The text to be drawn - * @param start The index of the first character in text to draw - * @param end (end - 1) is the index of the last character in text to draw - * @param x The x-coordinate of the origin of the text being drawn - * @param y The y-coordinate of the baseline of the text being drawn - * @param paint The paint used for the text (e.g. color, size, style) - */ - public void drawText(@NonNull String text, int start, int end, float x, float y, - @NonNull Paint paint) { - if ((start | end | (end - start) | (text.length() - end)) < 0) { - throw new IndexOutOfBoundsException(); - } - native_drawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags, - paint.getNativeInstance(), paint.mNativeTypeface); + super.drawText(text, index, count, x, y, paint); } /** @@ -1720,23 +1687,72 @@ public class Canvas { */ public void drawText(@NonNull CharSequence text, int start, int end, float x, float y, @NonNull Paint paint) { - if ((start | end | (end - start) | (text.length() - end)) < 0) { - throw new IndexOutOfBoundsException(); - } - if (text instanceof String || text instanceof SpannedString || - text instanceof SpannableString) { - native_drawText(mNativeCanvasWrapper, text.toString(), start, end, x, y, - paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); - } else if (text instanceof GraphicsOperations) { - ((GraphicsOperations) text).drawText(this, start, end, x, y, - paint); - } else { - char[] buf = TemporaryBuffer.obtain(end - start); - TextUtils.getChars(text, start, end, buf, 0); - native_drawText(mNativeCanvasWrapper, buf, 0, end - start, x, y, - paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); - TemporaryBuffer.recycle(buf); - } + super.drawText(text, start, end, x, y, paint); + } + + /** + * Draw the text, with origin at (x,y), using the specified paint. The + * origin is interpreted based on the Align setting in the paint. + * + * @param text The text to be drawn + * @param x The x-coordinate of the origin of the text being drawn + * @param y The y-coordinate of the baseline of the text being drawn + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { + super.drawText(text, x, y, paint); + } + + /** + * Draw the text, with origin at (x,y), using the specified paint. + * The origin is interpreted based on the Align setting in the paint. + * + * @param text The text to be drawn + * @param start The index of the first character in text to draw + * @param end (end - 1) is the index of the last character in text to draw + * @param x The x-coordinate of the origin of the text being drawn + * @param y The y-coordinate of the baseline of the text being drawn + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawText(@NonNull String text, int start, int end, float x, float y, + @NonNull Paint paint) { + super.drawText(text, start, end, x, y, paint); + } + + /** + * Draw the text, with origin at (x,y), using the specified paint, along + * the specified path. The paint's Align setting determins where along the + * path to start the text. + * + * @param text The text to be drawn + * @param path The path the text should follow for its baseline + * @param hOffset The distance along the path to add to the text's + * starting position + * @param vOffset The distance above(-) or below(+) the path to position + * the text + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path, + float hOffset, float vOffset, @NonNull Paint paint) { + super.drawTextOnPath(text, index, count, path, hOffset, vOffset, paint); + } + + /** + * Draw the text, with origin at (x,y), using the specified paint, along + * the specified path. The paint's Align setting determins where along the + * path to start the text. + * + * @param text The text to be drawn + * @param path The path the text should follow for its baseline + * @param hOffset The distance along the path to add to the text's + * starting position + * @param vOffset The distance above(-) or below(+) the path to position + * the text + * @param paint The paint used for the text (e.g. color, size, style) + */ + public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, + float vOffset, @NonNull Paint paint) { + super.drawTextOnPath(text, path, hOffset, vOffset, paint); } /** @@ -1764,21 +1780,7 @@ public class Canvas { */ public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) { - - if (text == null) { - throw new NullPointerException("text is null"); - } - if (paint == null) { - throw new NullPointerException("paint is null"); - } - if ((index | count | contextIndex | contextCount | index - contextIndex - | (contextIndex + contextCount) - (index + count) - | text.length - (contextIndex + contextCount)) < 0) { - throw new IndexOutOfBoundsException(); - } - - native_drawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount, - x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); + super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, isRtl, paint); } /** @@ -1818,393 +1820,44 @@ public class Canvas { */ public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) { - - if (text == null) { - throw new NullPointerException("text is null"); - } - if (paint == null) { - throw new NullPointerException("paint is null"); - } - if ((start | end | contextStart | contextEnd | start - contextStart | end - start - | contextEnd - end | text.length() - contextEnd) < 0) { - throw new IndexOutOfBoundsException(); - } - - if (text instanceof String || text instanceof SpannedString || - text instanceof SpannableString) { - native_drawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart, - contextEnd, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); - } else if (text instanceof GraphicsOperations) { - ((GraphicsOperations) text).drawTextRun(this, start, end, - contextStart, contextEnd, x, y, isRtl, paint); - } else { - int contextLen = contextEnd - contextStart; - int len = end - start; - char[] buf = TemporaryBuffer.obtain(contextLen); - TextUtils.getChars(text, contextStart, contextEnd, buf, 0); - native_drawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len, - 0, contextLen, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); - TemporaryBuffer.recycle(buf); - } + super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, isRtl, paint); } /** - * Draw the text in the array, with each character's origin specified by - * the pos array. + * Draw the array of vertices, interpreted as triangles (based on mode). The + * verts array is required, and specifies the x,y pairs for each vertex. If + * texs is non-null, then it is used to specify the coordinate in shader + * coordinates to use at each vertex (the paint must have a shader in this + * case). If there is no texs array, but there is a color array, then each + * color is interpolated across its corresponding triangle in a gradient. If + * both texs and colors arrays are present, then they behave as before, but + * the resulting color at each pixels is the result of multiplying the + * colors from the shader and the color-gradient together. The indices array + * is optional, but if it is present, then it is used to specify the index + * of each triangle, rather than just walking through the arrays in order. * - * @param text The text to be drawn - * @param index The index of the first character to draw - * @param count The number of characters to draw, starting from index. - * @param pos Array of [x,y] positions, used to position each - * character - * @param paint The paint used for the text (e.g. color, size, style) - * - * @deprecated This method does not support glyph composition and decomposition and - * should therefore not be used to render complex scripts. It also doesn't - * handle supplementary characters (eg emoji). + * @param mode How to interpret the array of vertices + * @param vertexCount The number of values in the vertices array (and + * corresponding texs and colors arrays if non-null). Each logical + * vertex is two values (x, y), vertexCount must be a multiple of 2. + * @param verts Array of vertices for the mesh + * @param vertOffset Number of values in the verts to skip before drawing. + * @param texs May be null. If not null, specifies the coordinates to sample + * into the current shader (e.g. bitmap tile or gradient) + * @param texOffset Number of values in texs to skip before drawing. + * @param colors May be null. If not null, specifies a color for each + * vertex, to be interpolated across the triangle. + * @param colorOffset Number of values in colors to skip before drawing. + * @param indices If not null, array of indices to reference into the + * vertex (texs, colors) array. + * @param indexCount number of entries in the indices array (if not null). + * @param paint Specifies the shader to use if the texs array is non-null. */ - @Deprecated - public void drawPosText(@NonNull char[] text, int index, int count, - @NonNull @Size(multiple=2) float[] pos, + public void drawVertices(@NonNull 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) { - if (index < 0 || index + count > text.length || count*2 > pos.length) { - throw new IndexOutOfBoundsException(); - } - for (int i = 0; i < count; i++) { - drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint); - } + super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, + colors, colorOffset, indices, indexOffset, indexCount, paint); } - - /** - * Draw the text in the array, with each character's origin specified by - * the pos array. - * - * @param text The text to be drawn - * @param pos Array of [x,y] positions, used to position each character - * @param paint The paint used for the text (e.g. color, size, style) - * - * @deprecated This method does not support glyph composition and decomposition and - * should therefore not be used to render complex scripts. It also doesn't - * handle supplementary characters (eg emoji). - */ - @Deprecated - public void drawPosText(@NonNull String text, @NonNull @Size(multiple=2) float[] pos, - @NonNull Paint paint) { - drawPosText(text.toCharArray(), 0, text.length(), pos, paint); - } - - /** - * Draw the text, with origin at (x,y), using the specified paint, along - * the specified path. The paint's Align setting determins where along the - * path to start the text. - * - * @param text The text to be drawn - * @param path The path the text should follow for its baseline - * @param hOffset The distance along the path to add to the text's - * starting position - * @param vOffset The distance above(-) or below(+) the path to position - * the text - * @param paint The paint used for the text (e.g. color, size, style) - */ - public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path, - float hOffset, float vOffset, @NonNull Paint paint) { - if (index < 0 || index + count > text.length) { - throw new ArrayIndexOutOfBoundsException(); - } - native_drawTextOnPath(mNativeCanvasWrapper, text, index, count, - path.readOnlyNI(), hOffset, vOffset, - paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); - } - - /** - * Draw the text, with origin at (x,y), using the specified paint, along - * the specified path. The paint's Align setting determins where along the - * path to start the text. - * - * @param text The text to be drawn - * @param path The path the text should follow for its baseline - * @param hOffset The distance along the path to add to the text's - * starting position - * @param vOffset The distance above(-) or below(+) the path to position - * the text - * @param paint The paint used for the text (e.g. color, size, style) - */ - public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, - float vOffset, @NonNull Paint paint) { - if (text.length() > 0) { - native_drawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset, - paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); - } - } - - /** - * Save the canvas state, draw the picture, and restore the canvas state. - * This differs from picture.draw(canvas), which does not perform any - * save/restore. - * - *

- * Note: This forces the picture to internally call - * {@link Picture#endRecording} in order to prepare for playback. - * - * @param picture The picture to be drawn - */ - public void drawPicture(@NonNull Picture picture) { - picture.endRecording(); - int restoreCount = save(); - picture.draw(this); - restoreToCount(restoreCount); - } - - /** - * Draw the picture, stretched to fit into the dst rectangle. - */ - public void drawPicture(@NonNull Picture picture, @NonNull RectF dst) { - save(); - translate(dst.left, dst.top); - if (picture.getWidth() > 0 && picture.getHeight() > 0) { - scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight()); - } - drawPicture(picture); - restore(); - } - - /** - * Draw the picture, stretched to fit into the dst rectangle. - */ - public void drawPicture(@NonNull Picture picture, @NonNull Rect dst) { - save(); - translate(dst.left, dst.top); - if (picture.getWidth() > 0 && picture.getHeight() > 0) { - scale((float) dst.width() / picture.getWidth(), - (float) dst.height() / picture.getHeight()); - } - drawPicture(picture); - restore(); - } - - /** - * Releases the resources associated with this canvas. - * - * @hide - */ - public void release() { - mNativeCanvasWrapper = 0; - if (mFinalizer != null) { - mFinalizer.run(); - mFinalizer = null; - } - } - - /** - * Free up as much memory as possible from private caches (e.g. fonts, images) - * - * @hide - */ - public static native void freeCaches(); - - /** - * Free up text layout caches - * - * @hide - */ - public static native void freeTextLayoutCaches(); - - private static native long initRaster(Bitmap bitmap); - private static native long getNativeFinalizer(); - - private native void native_drawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top, - long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity); - private native void native_drawBitmap(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); - private static native void native_drawBitmap(long nativeCanvas, int[] colors, - int offset, int stride, float x, float y, int width, int height, - boolean hasAlpha, long nativePaintOrZero); - - // ---------------- @FastNative ------------------- - - @FastNative - private static native void native_setBitmap(long canvasHandle, - Bitmap bitmap); - @FastNative - private static native boolean native_isOpaque(long canvasHandle); - @FastNative - private static native void native_setHighContrastText(long renderer, boolean highContrastText); - @FastNative - private static native int native_getWidth(long canvasHandle); - @FastNative - private static native int native_getHeight(long canvasHandle); - - @FastNative - private static native int native_save(long canvasHandle, int saveFlags); - @FastNative - private static native int native_saveLayer(long nativeCanvas, float l, - float t, float r, float b, - long nativePaint, - int layerFlags); - @FastNative - private static native int native_saveLayerAlpha(long nativeCanvas, float l, - float t, float r, float b, - int alpha, int layerFlags); - @FastNative - private static native void native_restore(long canvasHandle, boolean tolerateUnderflow); - @FastNative - private static native void native_restoreToCount(long canvasHandle, - int saveCount, - boolean tolerateUnderflow); - @FastNative - private static native int native_getSaveCount(long canvasHandle); - - @FastNative - private static native void native_translate(long canvasHandle, - float dx, float dy); - @FastNative - private static native void native_scale(long canvasHandle, - float sx, float sy); - @FastNative - private static native void native_rotate(long canvasHandle, float degrees); - @FastNative - private static native void native_skew(long canvasHandle, - float sx, float sy); - @FastNative - private static native void native_concat(long nativeCanvas, - long nativeMatrix); - @FastNative - private static native void native_setMatrix(long nativeCanvas, - long nativeMatrix); - @FastNative - private static native boolean native_clipRect(long nativeCanvas, - float left, float top, - float right, float bottom, - int regionOp); - @FastNative - private static native boolean native_clipPath(long nativeCanvas, - long nativePath, - int regionOp); - @FastNative - private static native boolean native_clipRegion(long nativeCanvas, - long nativeRegion, - int regionOp); - @FastNative - private static native void nativeSetDrawFilter(long nativeCanvas, - long nativeFilter); - @FastNative - private static native boolean native_getClipBounds(long nativeCanvas, - Rect bounds); - @FastNative - private static native void native_getCTM(long nativeCanvas, - long nativeMatrix); - @FastNative - private static native boolean native_quickReject(long nativeCanvas, - long nativePath); - @FastNative - private static native boolean native_quickReject(long nativeCanvas, - float left, float top, - float right, float bottom); - @FastNative - private static native void native_drawColor(long nativeCanvas, int color, - int mode); - @FastNative - private static native void native_drawPaint(long nativeCanvas, - long nativePaint); - @FastNative - private static native void native_drawPoint(long canvasHandle, float x, float y, - long paintHandle); - @FastNative - private static native void native_drawPoints(long canvasHandle, float[] pts, - int offset, int count, - long paintHandle); - @FastNative - private static native void native_drawLine(long nativeCanvas, float startX, - float startY, float stopX, - float stopY, long nativePaint); - @FastNative - private static native void native_drawLines(long canvasHandle, float[] pts, - int offset, int count, - long paintHandle); - @FastNative - private static native void native_drawRect(long nativeCanvas, float left, - float top, float right, - float bottom, - long nativePaint); - @FastNative - private static native void native_drawOval(long nativeCanvas, float left, float top, - float right, float bottom, long nativePaint); - @FastNative - private static native void native_drawCircle(long nativeCanvas, float cx, - float cy, float radius, - long nativePaint); - @FastNative - private static native void native_drawArc(long nativeCanvas, float left, float top, - float right, float bottom, - float startAngle, float sweep, boolean useCenter, - long nativePaint); - @FastNative - private static native void native_drawRoundRect(long nativeCanvas, - float left, float top, float right, float bottom, - float rx, float ry, long nativePaint); - @FastNative - private static native void native_drawPath(long nativeCanvas, - long nativePath, - long nativePaint); - @FastNative - private static native void native_drawRegion(long nativeCanvas, - long nativeRegion, long nativePaint); - @FastNative - private static native void native_drawNinePatch(long nativeCanvas, long nativeBitmap, - long ninePatch, float dstLeft, float dstTop, float dstRight, float dstBottom, - long nativePaintOrZero, int screenDensity, int bitmapDensity); - @FastNative - private static native void nativeDrawBitmapMatrix(long nativeCanvas, - Bitmap bitmap, - long nativeMatrix, - long nativePaint); - @FastNative - private static native void nativeDrawBitmapMesh(long nativeCanvas, - Bitmap bitmap, - int meshWidth, int meshHeight, - float[] verts, int vertOffset, - int[] colors, int colorOffset, - long nativePaint); - @FastNative - private static native void nativeDrawVertices(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); - - @FastNative - private static native void native_drawText(long nativeCanvas, char[] text, - int index, int count, float x, - float y, int flags, long nativePaint, - long nativeTypeface); - @FastNative - private static native void native_drawText(long nativeCanvas, String text, - int start, int end, float x, - float y, int flags, long nativePaint, - long nativeTypeface); - - @FastNative - private static native void native_drawTextRun(long nativeCanvas, String text, - int start, int end, int contextStart, int contextEnd, - float x, float y, boolean isRtl, long nativePaint, long nativeTypeface); - - @FastNative - private static native void native_drawTextRun(long nativeCanvas, char[] text, - int start, int count, int contextStart, int contextCount, - float x, float y, boolean isRtl, long nativePaint, long nativeTypeface); - - @FastNative - private static native void native_drawTextOnPath(long nativeCanvas, - char[] text, int index, - int count, long nativePath, - float hOffset, - float vOffset, int bidiFlags, - long nativePaint, long nativeTypeface); - @FastNative - private static native void native_drawTextOnPath(long nativeCanvas, - String text, long nativePath, - float hOffset, - float vOffset, - int flags, long nativePaint, long nativeTypeface); } diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java index 35cedae1e49a1..486070c99e3fd 100644 --- a/graphics/java/android/graphics/Matrix.java +++ b/graphics/java/android/graphics/Matrix.java @@ -838,7 +838,8 @@ public class Matrix { } - /* package */ final long ni() { + /** @hide */ + public final long ni() { return native_instance; } diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java index 2294b862192da..4ed2581f2b329 100644 --- a/graphics/java/android/graphics/Path.java +++ b/graphics/java/android/graphics/Path.java @@ -775,7 +775,8 @@ public class Path { } } - final long readOnlyNI() { + /** @hide */ + public final long readOnlyNI() { return mNativePath; }