From 9d2b5e1930bfc4b1da1c865843c247c708ea1565 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sun, 15 Jun 2014 23:53:57 -0700 Subject: [PATCH] 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 --- core/java/android/view/GLES20Canvas.java | 10 ++-- core/jni/android/graphics/Canvas.cpp | 69 +++++++++++++++++++--- core/jni/android/graphics/MinikinUtils.cpp | 17 ++++++ core/jni/android/graphics/MinikinUtils.h | 1 + core/jni/android_view_GLES20Canvas.cpp | 59 +++++++++++++++--- graphics/java/android/graphics/Canvas.java | 8 +-- 6 files changed, 141 insertions(+), 23 deletions(-) diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 446bbc9732a0b..b65af152532ed 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -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, diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 4584c464ffa0e..69e149e94974c 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -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(pathHandle); SkPaint* paint = reinterpret_cast(paintHandle); + TypefaceImpl* typeface = reinterpret_cast(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(pathHandle); SkPaint* paint = reinterpret_cast(paintHandle); + TypefaceImpl* typeface = reinterpret_cast(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}, diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp index a9360ea1ecbb9..fc92d53193c07 100644 --- a/core/jni/android/graphics/MinikinUtils.cpp +++ b/core/jni/android/graphics/MinikinUtils.cpp @@ -19,6 +19,7 @@ #include #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()); +} + } diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h index a96c6b19eca97..b2662a15cebfd 100644 --- a/core/jni/android/graphics/MinikinUtils.h +++ b/core/jni/android/graphics/MinikinUtils.h @@ -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 static void forFontRun(const Layout& layout, SkPaint* paint, F& f) { diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 4a6e117a051b5..de00e594ccda9 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -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 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(rendererPtr); jchar* textArray = env->GetCharArrayElements(text, NULL); SkPath* path = reinterpret_cast(pathPtr); SkPaint* paint = reinterpret_cast(paintPtr); + TypefaceImpl* typeface = reinterpret_cast(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(rendererPtr); const jchar* textArray = env->GetStringChars(text, NULL); SkPath* path = reinterpret_cast(pathPtr); SkPaint* paint = reinterpret_cast(paintPtr); + TypefaceImpl* typeface = reinterpret_cast(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 }, diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 158801c34c717..13421aa1dea0d 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -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); }