Add Canvas#drawGlyphs

Bug: 168048923
Test: atest CanvasDrawGlyphsTest
Change-Id: I013335fa82883146044ea03fbbe67e1e26a35f6d
This commit is contained in:
Seigo Nonaka
2020-09-26 23:53:02 -07:00
parent c730d2f604
commit 39c482f21f
8 changed files with 181 additions and 0 deletions

View File

@@ -14356,6 +14356,7 @@ package android.graphics {
method public void drawColor(@ColorLong long, @NonNull android.graphics.BlendMode);
method public void drawDoubleRoundRect(@NonNull android.graphics.RectF, float, float, @NonNull android.graphics.RectF, float, float, @NonNull android.graphics.Paint);
method public void drawDoubleRoundRect(@NonNull android.graphics.RectF, @NonNull float[], @NonNull android.graphics.RectF, @NonNull float[], @NonNull android.graphics.Paint);
method public void drawGlyphs(@NonNull int[], @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.fonts.Font, @NonNull android.graphics.Paint);
method public void drawLine(float, float, float, float, @NonNull android.graphics.Paint);
method public void drawLines(@NonNull @Size(multiple=4) float[], int, int, @NonNull android.graphics.Paint);
method public void drawLines(@NonNull @Size(multiple=4) float[], @NonNull android.graphics.Paint);

View File

@@ -18,11 +18,13 @@ 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.text.GraphicsOperations;
import android.text.MeasuredParagraph;
@@ -31,6 +33,10 @@ import android.text.SpannableString;
import android.text.SpannedString;
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}.
@@ -443,6 +449,58 @@ public abstract class BaseCanvas {
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 android.graphics.text.TextShaper
* @see android.text.StyledTextShaper
*/
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) |
@@ -734,6 +792,9 @@ public abstract class BaseCanvas {
int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset,
short[] indices, int indexOffset, int indexCount, 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);

View File

@@ -18,9 +18,11 @@ 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.graphics.fonts.Font;
import android.graphics.text.MeasuredText;
import android.text.GraphicsOperations;
import android.text.MeasuredParagraph;
@@ -29,8 +31,12 @@ import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.FastNative;
import java.util.Objects;
/**
* 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
@@ -408,6 +414,34 @@ public class BaseRecordingCanvas extends Canvas {
paint.getNativeInstance());
}
@Override
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());
}
@Override
public final void drawText(@NonNull char[] text, int index, int count, float x, float y,
@NonNull Paint paint) {
@@ -673,6 +707,10 @@ public class BaseRecordingCanvas extends Canvas {
int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset,
short[] indices, int indexOffset, int indexCount, long nativePaint);
@FastNative
private static native void nDrawGlyphs(long nativeCanvas, int[] glyphIds, float[] positions,
int glyphIdStart, int positionStart, int glyphCount, long nativeFont, long nativePaint);
@FastNative
private static native void nDrawText(long nativeCanvas, char[] text, int index, int count,
float x, float y, int flags, long nativePaint);

View File

@@ -19,10 +19,12 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.fonts.Font;
import android.graphics.text.MeasuredText;
import android.os.Build;
@@ -2047,6 +2049,43 @@ public class Canvas extends BaseCanvas {
super.drawDoubleRoundRect(outer, outerRadii, inner, innerRadii, paint);
}
/**
* 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 android.graphics.text.TextShaper
* @see android.text.StyledTextShaper
*/
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) {
super.drawGlyphs(glyphIds, glyphIdOffset, positions, positionOffset, glyphCount, font,
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.

View File

@@ -135,6 +135,26 @@ private:
float totalAdvance;
};
void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions,
int glyphCount, const Paint& paint) {
// Minikin modify skFont for auto-fakebold/auto-fakeitalic.
Paint copied(paint);
auto glyphFunc = [&](uint16_t* outGlyphIds, float* outPositions) {
for (uint32_t i = 0; i < glyphCount; ++i) {
outGlyphIds[i] = static_cast<uint16_t>(glyphIds[i]);
}
memcpy(outPositions, positions, sizeof(float) * 2 * glyphCount);
};
const minikin::MinikinFont* minikinFont = font.typeface().get();
SkFont* skfont = &copied.getSkFont();
MinikinFontSkia::populateSkFont(skfont, minikinFont, minikin::FontFakery());
// total advance is used for drawing underline. We do not support underlyine by glyph drawing.
drawGlyphs(glyphFunc, glyphCount, copied, 0 /* x */, 0 /* y */, 0 /* total Advance */);
}
void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, int contextStart,
int contextCount, float x, float y, minikin::Bidi bidiFlags,
const Paint& origPaint, const Typeface* typeface, minikin::MeasuredText* mt) {

View File

@@ -32,6 +32,7 @@ class SkCanvasState;
class SkVertices;
namespace minikin {
class Font;
class Layout;
class MeasuredText;
enum class Bidi : uint8_t;
@@ -255,6 +256,9 @@ public:
*/
virtual void drawVectorDrawable(VectorDrawableRoot* tree) = 0;
void drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions,
int glyphCount, const Paint& paint);
/**
* Converts utf16 text to glyphs, calculating position and boundary,
* and delegating the final draw to virtual drawGlyphs method.

View File

@@ -30,6 +30,7 @@
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedStringChars.h>
#include "FontUtils.h"
#include "Bitmap.h"
#include "SkGraphics.h"
#include "SkRegion.h"
@@ -540,6 +541,21 @@ static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitma
colorA.ptr() + colorIndex, paint);
}
static void drawGlyphs(JNIEnv* env, jobject, jlong canvasHandle, jintArray glyphIds,
jfloatArray positions, jint glyphOffset, jint positionOffset,
jint glyphCount, jlong fontHandle, jlong paintHandle) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
AutoJavaIntArray glyphIdArray(env, glyphIds);
AutoJavaFloatArray positionArray(env, positions);
get_canvas(canvasHandle)->drawGlyphs(
*font->font.get(),
glyphIdArray.ptr() + glyphOffset,
positionArray.ptr() + positionOffset,
glyphCount,
*paint);
}
static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray,
jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
jlong paintHandle) {
@@ -719,6 +735,7 @@ static const JNINativeMethod gDrawMethods[] = {
{"nDrawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
{"nDrawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
{"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
{"nDrawGlyphs", "(J[I[FIIIJJ)V", (void*)CanvasJNI::drawGlyphs},
{"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars},
{"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString},
{"nDrawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},

View File

@@ -14356,6 +14356,7 @@ package android.graphics {
method public void drawColor(@ColorLong long, @NonNull android.graphics.BlendMode);
method public void drawDoubleRoundRect(@NonNull android.graphics.RectF, float, float, @NonNull android.graphics.RectF, float, float, @NonNull android.graphics.Paint);
method public void drawDoubleRoundRect(@NonNull android.graphics.RectF, @NonNull float[], @NonNull android.graphics.RectF, @NonNull float[], @NonNull android.graphics.Paint);
method public void drawGlyphs(@NonNull int[], @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.fonts.Font, @NonNull android.graphics.Paint);
method public void drawLine(float, float, float, float, @NonNull android.graphics.Paint);
method public void drawLines(@NonNull @Size(multiple=4) float[], int, int, @NonNull android.graphics.Paint);
method public void drawLines(@NonNull @Size(multiple=4) float[], @NonNull android.graphics.Paint);