860 lines
37 KiB
Java
860 lines
37 KiB
Java
/*
|
|
* 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.ColorLong;
|
|
import android.annotation.IntRange;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.Size;
|
|
import android.compat.annotation.UnsupportedAppUsage;
|
|
import android.graphics.Canvas.VertexMode;
|
|
import android.graphics.fonts.Font;
|
|
import android.graphics.text.MeasuredText;
|
|
import android.graphics.text.TextRunShaper;
|
|
import android.text.GraphicsOperations;
|
|
import android.text.MeasuredParagraph;
|
|
import android.text.PrecomputedText;
|
|
import android.text.SpannableString;
|
|
import android.text.SpannedString;
|
|
import android.text.TextShaper;
|
|
import android.text.TextUtils;
|
|
|
|
import com.android.internal.util.Preconditions;
|
|
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* This class is a base class for Canvas's drawing operations. Any modifications here
|
|
* should be accompanied by a similar modification to {@link BaseRecordingCanvas}.
|
|
*
|
|
* 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.
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage
|
|
protected long mNativeCanvasWrapper;
|
|
|
|
/**
|
|
* Used to determine when compatibility scaling is in effect.
|
|
* @hide
|
|
*/
|
|
protected int mScreenDensity = Bitmap.DENSITY_NONE;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
protected int mDensity = Bitmap.DENSITY_NONE;
|
|
private boolean mAllowHwFeaturesInSwMode = false;
|
|
|
|
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);
|
|
}
|
|
throwIfHwBitmapInSwMode(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 RecordingCanvas so that we can
|
|
// selectively apply on them
|
|
// Everything below here is copy/pasted from Canvas.java
|
|
// The JNI registration is handled by android_graphics_Canvas.cpp
|
|
// ---------------------------------------------------------------------------
|
|
|
|
public void drawArc(float left, float top, float right, float bottom, float startAngle,
|
|
float sweepAngle, boolean useCenter, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(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) {
|
|
throwIfHasHwFeaturesInSwMode(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);
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top,
|
|
paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity,
|
|
bitmap.mDensity);
|
|
}
|
|
|
|
public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), 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);
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
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.getNativeInstance(), 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);
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
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.getNativeInstance(), 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();
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
// 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();
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
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.getNativeInstance(), meshWidth, meshHeight,
|
|
verts, vertOffset, colors, colorOffset,
|
|
paint != null ? paint.getNativeInstance() : 0);
|
|
}
|
|
|
|
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawColor(@ColorInt int color) {
|
|
nDrawColor(mNativeCanvasWrapper, color, BlendMode.SRC_OVER.getXfermode().porterDuffMode);
|
|
}
|
|
|
|
public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
|
|
nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
|
|
}
|
|
|
|
/**
|
|
* Make lint happy.
|
|
* See {@link Canvas#drawColor(int, BlendMode)}
|
|
*/
|
|
public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
|
|
nDrawColor(mNativeCanvasWrapper, color, mode.getXfermode().porterDuffMode);
|
|
}
|
|
|
|
/**
|
|
* Make lint happy.
|
|
* See {@link Canvas#drawColor(long, BlendMode)}
|
|
*/
|
|
public void drawColor(@ColorLong long color, @NonNull BlendMode mode) {
|
|
ColorSpace cs = Color.colorSpace(color);
|
|
nDrawColor(mNativeCanvasWrapper, cs.getNativeInstance(), color,
|
|
mode.getXfermode().porterDuffMode);
|
|
}
|
|
|
|
public void drawLine(float startX, float startY, float stopX, float stopY,
|
|
@NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(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) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
drawLines(pts, 0, pts.length, paint);
|
|
}
|
|
|
|
public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawOval(@NonNull RectF oval, @NonNull Paint paint) {
|
|
if (oval == null) {
|
|
throw new NullPointerException();
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
|
|
}
|
|
|
|
public void drawPaint(@NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
|
|
Bitmap bitmap = patch.getBitmap();
|
|
throwIfCannotDraw(bitmap);
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
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);
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
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) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawRegion(@NonNull Region region, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawRegion(mNativeCanvasWrapper, region.mNativeRegion, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawPoint(float x, float y, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
|
|
@NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(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();
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
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) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
|
|
}
|
|
|
|
public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
drawRect(r.left, r.top, r.right, r.bottom, paint);
|
|
}
|
|
|
|
public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(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) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
|
|
paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
|
|
}
|
|
|
|
/**
|
|
* Make lint happy.
|
|
* See {@link Canvas#drawDoubleRoundRect(RectF, float, float, RectF, float, float, Paint)}
|
|
*/
|
|
public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
|
|
@NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
float outerLeft = outer.left;
|
|
float outerTop = outer.top;
|
|
float outerRight = outer.right;
|
|
float outerBottom = outer.bottom;
|
|
|
|
float innerLeft = inner.left;
|
|
float innerTop = inner.top;
|
|
float innerRight = inner.right;
|
|
float innerBottom = inner.bottom;
|
|
nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, outerBottom,
|
|
outerRx, outerRy, innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy,
|
|
paint.getNativeInstance());
|
|
}
|
|
|
|
/**
|
|
* Make lint happy.
|
|
* See {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}
|
|
*/
|
|
public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
|
|
@NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
if (innerRadii == null || outerRadii == null
|
|
|| innerRadii.length != 8 || outerRadii.length != 8) {
|
|
throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
|
|
+ "exactly 8 values");
|
|
}
|
|
float outerLeft = outer.left;
|
|
float outerTop = outer.top;
|
|
float outerRight = outer.right;
|
|
float outerBottom = outer.bottom;
|
|
|
|
float innerLeft = inner.left;
|
|
float innerTop = inner.top;
|
|
float innerRight = inner.right;
|
|
float innerBottom = inner.bottom;
|
|
nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight,
|
|
outerBottom, outerRadii, innerLeft, innerTop, innerRight, innerBottom, innerRadii,
|
|
paint.getNativeInstance());
|
|
}
|
|
|
|
/**
|
|
* Draw array of glyphs with specified font.
|
|
*
|
|
* @param glyphIds Array of glyph IDs. The length of array must be greater than or equal to
|
|
* {@code glyphStart + glyphCount}.
|
|
* @param glyphIdOffset Number of elements to skip before drawing in <code>glyphIds</code>
|
|
* array.
|
|
* @param positions A flattened X and Y position array. The first glyph X position must be
|
|
* stored at {@code positionOffset}. The first glyph Y position must be stored
|
|
* at {@code positionOffset + 1}, then the second glyph X position must be
|
|
* stored at {@code positionOffset + 2}.
|
|
* The length of array must be greater than or equal to
|
|
* {@code positionOffset + glyphCount * 2}.
|
|
* @param positionOffset Number of elements to skip before drawing in {@code positions}.
|
|
* The first glyph X position must be stored at {@code positionOffset}.
|
|
* The first glyph Y position must be stored at
|
|
* {@code positionOffset + 1}, then the second glyph X position must be
|
|
* stored at {@code positionOffset + 2}.
|
|
* @param glyphCount Number of glyphs to be drawn.
|
|
* @param font Font used for drawing.
|
|
* @param paint Paint used for drawing. The typeface set to this paint is ignored.
|
|
*
|
|
* @see TextRunShaper
|
|
* @see TextShaper
|
|
*/
|
|
public void drawGlyphs(
|
|
@NonNull int[] glyphIds,
|
|
@IntRange(from = 0) int glyphIdOffset,
|
|
@NonNull float[] positions,
|
|
@IntRange(from = 0) int positionOffset,
|
|
@IntRange(from = 0) int glyphCount,
|
|
@NonNull Font font,
|
|
@NonNull Paint paint) {
|
|
Objects.requireNonNull(glyphIds, "glyphIds must not be null.");
|
|
Objects.requireNonNull(positions, "positions must not be null.");
|
|
Objects.requireNonNull(font, "font must not be null.");
|
|
Objects.requireNonNull(paint, "paint must not be null.");
|
|
Preconditions.checkArgumentNonnegative(glyphCount);
|
|
|
|
if (glyphIdOffset < 0 || glyphIdOffset + glyphCount > glyphIds.length) {
|
|
throw new IndexOutOfBoundsException(
|
|
"glyphIds must have at least " + (glyphIdOffset + glyphCount) + " of elements");
|
|
}
|
|
if (positionOffset < 0 || positionOffset + glyphCount * 2 > positions.length) {
|
|
throw new IndexOutOfBoundsException(
|
|
"positions must have at least " + (positionOffset + glyphCount * 2)
|
|
+ " of elements");
|
|
}
|
|
nDrawGlyphs(mNativeCanvasWrapper, glyphIds, positions, glyphIdOffset, positionOffset,
|
|
glyphCount, font.getNativePtr(), paint.getNativeInstance());
|
|
}
|
|
|
|
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();
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
|
|
paint.getNativeInstance());
|
|
}
|
|
|
|
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();
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
if (text instanceof String || text instanceof SpannedString ||
|
|
text instanceof SpannableString) {
|
|
nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
|
|
paint.mBidiFlags, paint.getNativeInstance());
|
|
} 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());
|
|
TemporaryBuffer.recycle(buf);
|
|
}
|
|
}
|
|
|
|
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
|
|
paint.getNativeInstance());
|
|
}
|
|
|
|
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();
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
|
|
paint.getNativeInstance());
|
|
}
|
|
|
|
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();
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
|
|
path.readOnlyNI(), hOffset, vOffset,
|
|
paint.mBidiFlags, paint.getNativeInstance());
|
|
}
|
|
|
|
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
|
|
float vOffset, @NonNull Paint paint) {
|
|
if (text.length() > 0) {
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
|
|
paint.mBidiFlags, paint.getNativeInstance());
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
|
|
x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
if (text instanceof String || text instanceof SpannedString ||
|
|
text instanceof SpannableString) {
|
|
nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
|
|
contextEnd, x, y, isRtl, paint.getNativeInstance());
|
|
} else if (text instanceof GraphicsOperations) {
|
|
((GraphicsOperations) text).drawTextRun(this, start, end,
|
|
contextStart, contextEnd, x, y, isRtl, paint);
|
|
} else {
|
|
if (text instanceof PrecomputedText) {
|
|
final PrecomputedText pt = (PrecomputedText) text;
|
|
final int paraIndex = pt.findParaIndex(start);
|
|
if (end <= pt.getParagraphEnd(paraIndex)) {
|
|
final int paraStart = pt.getParagraphStart(paraIndex);
|
|
final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
|
|
// Only support the text in the same paragraph.
|
|
drawTextRun(mp.getMeasuredText(),
|
|
start - paraStart,
|
|
end - paraStart,
|
|
contextStart - paraStart,
|
|
contextEnd - paraStart,
|
|
x, y, isRtl, paint);
|
|
return;
|
|
}
|
|
}
|
|
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(),
|
|
0 /* measured paragraph pointer */);
|
|
TemporaryBuffer.recycle(buf);
|
|
}
|
|
}
|
|
|
|
public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end,
|
|
int contextStart, int contextEnd, float x, float y, boolean isRtl,
|
|
@NonNull Paint paint) {
|
|
nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start,
|
|
contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(),
|
|
measuredText.getNativePtr());
|
|
}
|
|
|
|
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 (texs != null) {
|
|
checkRange(texs.length, texOffset, vertexCount);
|
|
}
|
|
if (colors != null) {
|
|
checkRange(colors.length, colorOffset, vertexCount / 2);
|
|
}
|
|
if (indices != null) {
|
|
checkRange(indices.length, indexOffset, indexCount);
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(paint);
|
|
nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
|
|
vertOffset, texs, texOffset, colors, colorOffset,
|
|
indices, indexOffset, indexCount, paint.getNativeInstance());
|
|
}
|
|
|
|
/**
|
|
* Draws a mesh object to the screen.
|
|
*
|
|
* <p>Note: antialiasing is not supported, therefore {@link Paint#ANTI_ALIAS_FLAG} is
|
|
* ignored.</p>
|
|
*
|
|
* @param mesh {@link Mesh} object that will be drawn to the screen
|
|
* @param blendMode {@link BlendMode} used to blend mesh primitives as the destination color
|
|
* with the Paint color/shader as the source color. This defaults to
|
|
* {@link BlendMode#MODULATE} if null.
|
|
* @param paint {@link Paint} used to provide a color/shader/blend mode.
|
|
*/
|
|
public void drawMesh(@NonNull Mesh mesh, @Nullable BlendMode blendMode, @NonNull Paint paint) {
|
|
if (!isHardwareAccelerated() && onHwFeatureInSwMode()) {
|
|
throw new RuntimeException("software rendering doesn't support meshes");
|
|
}
|
|
if (blendMode == null) {
|
|
blendMode = BlendMode.MODULATE;
|
|
}
|
|
nDrawMesh(this.mNativeCanvasWrapper, mesh.getNativeWrapperInstance(),
|
|
blendMode.getXfermode().porterDuffMode, paint.getNativeInstance());
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public void punchHole(float left, float top, float right, float bottom, float rx, float ry,
|
|
float alpha) {
|
|
nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha);
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public void setHwFeaturesInSwModeEnabled(boolean enabled) {
|
|
mAllowHwFeaturesInSwMode = enabled;
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public boolean isHwFeaturesInSwModeEnabled() {
|
|
return mAllowHwFeaturesInSwMode;
|
|
}
|
|
|
|
/**
|
|
* If true throw an exception
|
|
* @hide
|
|
*/
|
|
protected boolean onHwFeatureInSwMode() {
|
|
return !mAllowHwFeaturesInSwMode;
|
|
}
|
|
|
|
private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
|
|
if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE
|
|
&& onHwFeatureInSwMode()) {
|
|
throw new IllegalArgumentException(
|
|
"Software rendering doesn't support hardware bitmaps");
|
|
}
|
|
}
|
|
|
|
private void throwIfHasHwFeaturesInSwMode(Paint p) {
|
|
if (isHardwareAccelerated() || p == null) {
|
|
return;
|
|
}
|
|
throwIfHasHwFeaturesInSwMode(p.getShader());
|
|
}
|
|
|
|
private void throwIfHasHwFeaturesInSwMode(Shader shader) {
|
|
if (shader == null) {
|
|
return;
|
|
}
|
|
if (shader instanceof BitmapShader) {
|
|
throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap);
|
|
} else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) {
|
|
throw new IllegalArgumentException(
|
|
"Software rendering doesn't support RuntimeShader");
|
|
} else if (shader instanceof ComposeShader) {
|
|
throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA);
|
|
throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB);
|
|
}
|
|
}
|
|
|
|
private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left,
|
|
float top, long nativePaintOrZero, int canvasDensity, int screenDensity,
|
|
int bitmapDensity);
|
|
|
|
private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float srcLeft,
|
|
float srcTop,
|
|
float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
|
|
float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity);
|
|
|
|
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 nDrawColor(long nativeCanvas, long nativeColorSpace,
|
|
@ColorLong long 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 nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
|
|
float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy,
|
|
float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx,
|
|
float innerRy, long nativePaint);
|
|
|
|
private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
|
|
float outerTop, float outerRight, float outerBottom, float[] outerRadii,
|
|
float innerLeft, float innerTop, float innerRight, float innerBottom,
|
|
float[] innerRadii, long nativePaint);
|
|
|
|
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, long bitmapHandle,
|
|
long nativeMatrix, long nativePaint);
|
|
|
|
private static native void nDrawBitmapMesh(long nativeCanvas, long bitmapHandle, 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 nDrawMesh(
|
|
long nativeCanvas, long nativeMesh, int mode, long nativePaint);
|
|
|
|
private static native void nDrawGlyphs(long nativeCanvas, int[] glyphIds, float[] positions,
|
|
int glyphIdStart, int positionStart, int glyphCount, long nativeFont, long nativePaint);
|
|
|
|
private static native void nDrawText(long nativeCanvas, char[] text, int index, int count,
|
|
float x, float y, int flags, long nativePaint);
|
|
|
|
private static native void nDrawText(long nativeCanvas, String text, int start, int end,
|
|
float x, float y, int flags, long nativePaint);
|
|
|
|
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);
|
|
|
|
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 nativePrecomputedText);
|
|
|
|
private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
|
|
long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
|
|
|
|
private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
|
|
float hOffset, float vOffset, int flags, long nativePaint);
|
|
|
|
private static native void nPunchHole(long renderer, float left, float top, float right,
|
|
float bottom, float rx, float ry, float alpha);
|
|
}
|