Implement drawTextOnPath with Minikin
This patch contains an implementation of drawTextOnPath for both software and hardware Canvas using Minikin for text layout. One of the steps for switching all remaining text operations to Minikin so the old TextLayout and Skia fallback fonts mechanisms can be deleted. Bug: 11750374 Resolve TODO items for Minikin Change-Id: I06bfe74a101fa1dcdfc38f530f7194d71e522a85
This commit is contained in:
@@ -981,22 +981,24 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
}
|
||||
|
||||
nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
|
||||
paint.mBidiFlags, paint.mNativePaint);
|
||||
paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
|
||||
}
|
||||
|
||||
private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count,
|
||||
long path, float hOffset, float vOffset, int bidiFlags, long nativePaint);
|
||||
long path, float hOffset, float vOffset, int bidiFlags, long nativePaint,
|
||||
long typeface);
|
||||
|
||||
@Override
|
||||
public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
|
||||
if (text.length() == 0) return;
|
||||
|
||||
nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
|
||||
paint.mBidiFlags, paint.mNativePaint);
|
||||
paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
|
||||
}
|
||||
|
||||
private static native void nDrawTextOnPath(long renderer, String text, int start, int end,
|
||||
long path, float hOffset, float vOffset, int bidiFlags, long nativePaint);
|
||||
long path, float hOffset, float vOffset, int bidiFlags, long nativePaint,
|
||||
long typeface);
|
||||
|
||||
@Override
|
||||
public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
|
||||
|
||||
@@ -1123,29 +1123,82 @@ public:
|
||||
delete[] posPtr;
|
||||
}
|
||||
|
||||
#ifdef USE_MINIKIN
|
||||
class DrawTextOnPathFunctor {
|
||||
public:
|
||||
DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset,
|
||||
float vOffset, SkPaint* paint, SkPath* path)
|
||||
: layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
|
||||
paint(paint), path(path) {
|
||||
}
|
||||
void operator()(size_t start, size_t end) {
|
||||
uint16_t glyphs[1];
|
||||
for (size_t i = start; i < end; i++) {
|
||||
glyphs[0] = layout.getGlyphId(i);
|
||||
float x = hOffset + layout.getX(i);
|
||||
float y = vOffset + layout.getY(i);
|
||||
canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint);
|
||||
}
|
||||
}
|
||||
private:
|
||||
const Layout& layout;
|
||||
SkCanvas* canvas;
|
||||
float hOffset;
|
||||
float vOffset;
|
||||
SkPaint* paint;
|
||||
SkPath* path;
|
||||
};
|
||||
#endif
|
||||
|
||||
static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags,
|
||||
float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) {
|
||||
#ifdef USE_MINIKIN
|
||||
Layout layout;
|
||||
std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
|
||||
layout.doLayout(text, 0, count, count, css);
|
||||
hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
|
||||
// Set align to left for drawing, as we don't want individual
|
||||
// glyphs centered or right-aligned; the offset above takes
|
||||
// care of all alignment.
|
||||
SkPaint::Align align = paint->getTextAlign();
|
||||
paint->setTextAlign(SkPaint::kLeft_Align);
|
||||
|
||||
DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path);
|
||||
MinikinUtils::forFontRun(layout, paint, f);
|
||||
paint->setTextAlign(align);
|
||||
#else
|
||||
TextLayout::drawTextOnPath(paint, text, count, bidiFlags, hOffset, vOffset, path, canvas);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
|
||||
jlong canvasHandle, jcharArray text, jint index, jint count,
|
||||
jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
|
||||
jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
|
||||
jlong typefaceHandle) {
|
||||
SkCanvas* canvas = getNativeCanvas(canvasHandle);
|
||||
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
|
||||
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
|
||||
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
|
||||
|
||||
jchar* textArray = env->GetCharArrayElements(text, NULL);
|
||||
TextLayout::drawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
|
||||
path, canvas);
|
||||
doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
|
||||
path, canvas, typeface);
|
||||
env->ReleaseCharArrayElements(text, textArray, 0);
|
||||
}
|
||||
|
||||
static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
|
||||
jlong canvasHandle, jstring text, jlong pathHandle,
|
||||
jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
|
||||
jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
|
||||
jlong typefaceHandle) {
|
||||
SkCanvas* canvas = getNativeCanvas(canvasHandle);
|
||||
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
|
||||
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
|
||||
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
|
||||
|
||||
const jchar* text_ = env->GetStringChars(text, NULL);
|
||||
int count = env->GetStringLength(text);
|
||||
TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
|
||||
path, canvas);
|
||||
doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
|
||||
path, canvas, typeface);
|
||||
env->ReleaseStringChars(text, text_);
|
||||
}
|
||||
|
||||
@@ -1271,9 +1324,9 @@ static JNINativeMethod gCanvasMethods[] = {
|
||||
(void*) SkCanvasGlue::drawPosText___CII_FPaint},
|
||||
{"native_drawPosText","(JLjava/lang/String;[FJ)V",
|
||||
(void*) SkCanvasGlue::drawPosText__String_FPaint},
|
||||
{"native_drawTextOnPath","(J[CIIJFFIJ)V",
|
||||
{"native_drawTextOnPath","(J[CIIJFFIJJ)V",
|
||||
(void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
|
||||
{"native_drawTextOnPath","(JLjava/lang/String;JFFIJ)V",
|
||||
{"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V",
|
||||
(void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
|
||||
|
||||
{"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "SkPaint.h"
|
||||
#include "SkPathMeasure.h"
|
||||
#include "minikin/Layout.h"
|
||||
#include "TypefaceImpl.h"
|
||||
#include "MinikinSkia.h"
|
||||
@@ -76,4 +77,20 @@ float MinikinUtils::xOffsetForTextAlign(SkPaint* paint, const Layout& layout) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float MinikinUtils::hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path) {
|
||||
float align = 0;
|
||||
switch (paint->getTextAlign()) {
|
||||
case SkPaint::kCenter_Align:
|
||||
align = -0.5f;
|
||||
break;
|
||||
case SkPaint::kRight_Align:
|
||||
align = -1;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
SkPathMeasure measure(path, false);
|
||||
return align * (layout.getAdvance() - measure.getLength());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
|
||||
static float xOffsetForTextAlign(SkPaint* paint, const Layout& layout);
|
||||
|
||||
static float hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path);
|
||||
// f is a functor of type void f(size_t start, size_t end);
|
||||
template <typename F>
|
||||
static void forFontRun(const Layout& layout, SkPaint* paint, F& f) {
|
||||
|
||||
@@ -669,8 +669,48 @@ static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_MINIKIN
|
||||
class RenderTextOnPathFunctor {
|
||||
public:
|
||||
RenderTextOnPathFunctor(const Layout& layout, OpenGLRenderer* renderer, float hOffset,
|
||||
float vOffset, SkPaint* paint, SkPath* path)
|
||||
: layout(layout), renderer(renderer), hOffset(hOffset), vOffset(vOffset),
|
||||
paint(paint), path(path) {
|
||||
}
|
||||
void operator()(size_t start, size_t end) {
|
||||
uint16_t glyphs[1];
|
||||
for (size_t i = start; i < end; i++) {
|
||||
glyphs[0] = layout.getGlyphId(i);
|
||||
float x = hOffset + layout.getX(i);
|
||||
float y = vOffset + layout.getY(i);
|
||||
renderer->drawTextOnPath((const char*) glyphs, sizeof(glyphs), 1, path, x, y, paint);
|
||||
}
|
||||
}
|
||||
private:
|
||||
const Layout& layout;
|
||||
OpenGLRenderer* renderer;
|
||||
float hOffset;
|
||||
float vOffset;
|
||||
SkPaint* paint;
|
||||
SkPath* path;
|
||||
};
|
||||
#endif
|
||||
|
||||
static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count,
|
||||
SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint) {
|
||||
SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint,
|
||||
TypefaceImpl* typeface) {
|
||||
#ifdef USE_MINIKIN
|
||||
Layout layout;
|
||||
std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
|
||||
layout.doLayout(text, 0, count, count, css);
|
||||
hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
|
||||
SkPaint::Align align = paint->getTextAlign();
|
||||
paint->setTextAlign(SkPaint::kLeft_Align);
|
||||
|
||||
RenderTextOnPathFunctor f(layout, renderer, hOffset, vOffset, paint, path);
|
||||
MinikinUtils::forFontRun(layout, paint, f);
|
||||
paint->setTextAlign(align);
|
||||
#else
|
||||
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
|
||||
text, 0, count, count, bidiFlags);
|
||||
if (value == NULL) {
|
||||
@@ -681,6 +721,7 @@ static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int co
|
||||
int bytesCount = glyphsCount * sizeof(jchar);
|
||||
renderer->drawTextOnPath((const char*) glyphs, bytesCount, glyphsCount, path,
|
||||
hOffset, vOffset, paint);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
|
||||
@@ -739,27 +780,31 @@ static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject clazz,
|
||||
|
||||
static void android_view_GLES20Canvas_drawTextArrayOnPath(JNIEnv* env, jobject clazz,
|
||||
jlong rendererPtr, jcharArray text, jint index, jint count,
|
||||
jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) {
|
||||
jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr,
|
||||
jlong typefacePtr) {
|
||||
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
|
||||
jchar* textArray = env->GetCharArrayElements(text, NULL);
|
||||
SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
|
||||
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
|
||||
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
|
||||
|
||||
renderTextOnPath(renderer, textArray + index, count, path,
|
||||
hOffset, vOffset, bidiFlags, paint);
|
||||
hOffset, vOffset, bidiFlags, paint, typeface);
|
||||
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
|
||||
}
|
||||
|
||||
static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz,
|
||||
jlong rendererPtr, jstring text, jint start, jint end,
|
||||
jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) {
|
||||
jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr,
|
||||
jlong typefacePtr) {
|
||||
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
|
||||
const jchar* textArray = env->GetStringChars(text, NULL);
|
||||
SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
|
||||
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
|
||||
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
|
||||
|
||||
renderTextOnPath(renderer, textArray + start, end - start, path,
|
||||
hOffset, vOffset, bidiFlags, paint);
|
||||
hOffset, vOffset, bidiFlags, paint, typeface);
|
||||
env->ReleaseStringChars(text, textArray);
|
||||
}
|
||||
|
||||
@@ -986,8 +1031,8 @@ static JNINativeMethod gMethods[] = {
|
||||
{ "nDrawText", "(JLjava/lang/String;IIFFIJJ)V",
|
||||
(void*) android_view_GLES20Canvas_drawText },
|
||||
|
||||
{ "nDrawTextOnPath", "(J[CIIJFFIJ)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
|
||||
{ "nDrawTextOnPath", "(JLjava/lang/String;IIJFFIJ)V",
|
||||
{ "nDrawTextOnPath", "(J[CIIJFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
|
||||
{ "nDrawTextOnPath", "(JLjava/lang/String;IIJFFIJJ)V",
|
||||
(void*) android_view_GLES20Canvas_drawTextOnPath },
|
||||
|
||||
{ "nDrawTextRun", "(J[CIIIIFFZJJ)V", (void*) android_view_GLES20Canvas_drawTextRunArray },
|
||||
|
||||
@@ -1770,7 +1770,7 @@ public class Canvas {
|
||||
}
|
||||
native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
|
||||
path.ni(), hOffset, vOffset,
|
||||
paint.mBidiFlags, paint.mNativePaint);
|
||||
paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1790,7 +1790,7 @@ public class Canvas {
|
||||
float vOffset, @NonNull Paint paint) {
|
||||
if (text.length() > 0) {
|
||||
native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset,
|
||||
paint.mBidiFlags, paint.mNativePaint);
|
||||
paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2021,11 +2021,11 @@ public class Canvas {
|
||||
int count, long nativePath,
|
||||
float hOffset,
|
||||
float vOffset, int bidiFlags,
|
||||
long nativePaint);
|
||||
long nativePaint, long nativeTypeface);
|
||||
private static native void native_drawTextOnPath(long nativeCanvas,
|
||||
String text, long nativePath,
|
||||
float hOffset,
|
||||
float vOffset,
|
||||
int flags, long nativePaint);
|
||||
int flags, long nativePaint, long nativeTypeface);
|
||||
private static native void finalizer(long nativeCanvas);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user