OSDN Git Service

Initial text support in new reorderer/renderer
authorChris Craik <ccraik@google.com>
Thu, 19 Nov 2015 21:02:43 +0000 (13:02 -0800)
committerChris Craik <ccraik@google.com>
Fri, 20 Nov 2015 19:03:18 +0000 (11:03 -0800)
Removes obsolete drawPosText codepath, and unifies text decoration behavior.

Change-Id: I9c563249ab688a3394445a0e7fe1b9d0661f6f7c

26 files changed:
core/jni/android_graphics_Canvas.cpp
libs/hwui/Android.mk
libs/hwui/BakedOpRenderer.cpp
libs/hwui/Canvas.cpp [new file with mode: 0644]
libs/hwui/Canvas.h
libs/hwui/DisplayListCanvas.cpp
libs/hwui/DisplayListCanvas.h
libs/hwui/DisplayListOp.h
libs/hwui/FontRenderer.cpp
libs/hwui/FontRenderer.h
libs/hwui/OpReorderer.cpp
libs/hwui/OpenGLRenderer.cpp
libs/hwui/OpenGLRenderer.h
libs/hwui/RecordedOp.h
libs/hwui/RecordingCanvas.cpp
libs/hwui/RecordingCanvas.h
libs/hwui/Rect.h
libs/hwui/SkiaCanvas.cpp
libs/hwui/TextDropShadowCache.cpp
libs/hwui/TextDropShadowCache.h
libs/hwui/font/Font.cpp
libs/hwui/font/Font.h
libs/hwui/unit_tests/OpReordererTests.cpp
libs/hwui/unit_tests/RecordingCanvasTests.cpp
libs/hwui/utils/TestUtils.cpp
libs/hwui/utils/TestUtils.h

index 3d96fab..e4e73a4 100644 (file)
@@ -544,39 +544,6 @@ private:
     float totalAdvance;
 };
 
-// Same values used by Skia
-#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
-#define kStdUnderline_Offset    (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
-void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) {
-    uint32_t flags;
-    SkDrawFilter* drawFilter = canvas->getDrawFilter();
-    if (drawFilter) {
-        SkPaint paintCopy(paint);
-        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
-        flags = paintCopy.getFlags();
-    } else {
-        flags = paint.getFlags();
-    }
-    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
-        SkScalar left = x;
-        SkScalar right = x + length;
-        float textSize = paint.getTextSize();
-        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
-        if (flags & SkPaint::kUnderlineText_Flag) {
-            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
-            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
-            canvas->drawRect(left, top, right, bottom, paint);
-        }
-        if (flags & SkPaint::kStrikeThruText_Flag) {
-            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
-            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
-            canvas->drawRect(left, top, right, bottom, paint);
-        }
-    }
-}
-
 void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
              float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
     // minikin may modify the original paint
@@ -586,8 +553,8 @@ void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int co
     MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
 
     size_t nGlyphs = layout.nGlyphs();
-    uint16_t* glyphs = new uint16_t[nGlyphs];
-    float* pos = new float[nGlyphs * 2];
+    std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
+    std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);
 
     x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
 
@@ -597,13 +564,9 @@ void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int co
         bounds.offset(x, y);
     }
 
-    DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance());
+    DrawTextFunctor f(layout, canvas, glyphs.get(), pos.get(),
+            paint, x, y, bounds, layout.getAdvance());
     MinikinUtils::forFontRun(layout, &paint, f);
-
-    drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
-
-    delete[] glyphs;
-    delete[] pos;
 }
 
 static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
index 0a57d50..cc68fb2 100644 (file)
@@ -37,6 +37,7 @@ hwui_src_files := \
     AnimatorManager.cpp \
     AssetAtlas.cpp \
     Caches.cpp \
+    Canvas.cpp \
     CanvasState.cpp \
     ClipArea.cpp \
     DamageAccumulator.cpp \
index d2d3285..d13d7ef 100644 (file)
@@ -24,6 +24,9 @@
 #include "utils/GLUtils.h"
 #include "VertexBuffer.h"
 
+#include <algorithm>
+#include <math.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -183,6 +186,10 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op
     renderer.renderGlop(state, glop);
 }
 
+void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
+    LOG_ALWAYS_FATAL("todo");
+}
+
 void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
@@ -270,6 +277,91 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR
     renderer.renderGlop(state, glop);
 }
 
+static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
+        const TextOp& op, const BakedOpState& state) {
+    renderer.caches().textureState().activateTexture(0);
+
+    PaintUtils::TextShadow textShadow;
+    if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
+        LOG_ALWAYS_FATAL("failed to query shadow attributes");
+    }
+
+    renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
+    ShadowTexture* texture = renderer.caches().dropShadowCache.get(
+            op.paint, (const char*) op.glyphs,
+            op.glyphCount, textShadow.radius, op.positions);
+    // If the drop shadow exceeds the max texture size or couldn't be
+    // allocated, skip drawing
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+
+    const float sx = op.x - texture->left + textShadow.dx;
+    const float sy = op.y - texture->top + textShadow.dy;
+
+    Glop glop;
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+            .setRoundRectClipState(state.roundRectClipState)
+            .setMeshTexturedUnitQuad(nullptr)
+            .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
+            .setTransform(state.computedState.transform, TransformFlags::None)
+            .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
+            .build();
+    renderer.renderGlop(state, glop);
+}
+
+void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
+    FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
+
+    if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
+        fontRenderer.setFont(op.paint, SkMatrix::I());
+        renderTextShadow(renderer, fontRenderer, op, state);
+    }
+
+    float x = op.x;
+    float y = op.y;
+    const Matrix4& transform = state.computedState.transform;
+    const bool pureTranslate = transform.isPureTranslate();
+    if (CC_LIKELY(pureTranslate)) {
+        x = floorf(x + transform.getTranslateX() + 0.5f);
+        y = floorf(y + transform.getTranslateY() + 0.5f);
+        fontRenderer.setFont(op.paint, SkMatrix::I());
+        fontRenderer.setTextureFiltering(false);
+    } else if (CC_UNLIKELY(transform.isPerspective())) {
+        fontRenderer.setFont(op.paint, SkMatrix::I());
+        fontRenderer.setTextureFiltering(true);
+    } else {
+        // We only pass a partial transform to the font renderer. That partial
+        // matrix defines how glyphs are rasterized. Typically we want glyphs
+        // to be rasterized at their final size on screen, which means the partial
+        // matrix needs to take the scale factor into account.
+        // When a partial matrix is used to transform glyphs during rasterization,
+        // the mesh is generated with the inverse transform (in the case of scale,
+        // the mesh is generated at 1.0 / scale for instance.) This allows us to
+        // apply the full transform matrix at draw time in the vertex shader.
+        // Applying the full matrix in the shader is the easiest way to handle
+        // rotation and perspective and allows us to always generated quads in the
+        // font renderer which greatly simplifies the code, clipping in particular.
+        float sx, sy;
+        transform.decomposeScale(sx, sy);
+        fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
+                roundf(std::max(1.0f, sx)),
+                roundf(std::max(1.0f, sy))));
+        fontRenderer.setTextureFiltering(true);
+    }
+
+    // TODO: Implement better clipping for scaled/rotated text
+    const Rect* clip = !pureTranslate ? nullptr : &state.computedState.clipRect;
+    Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+
+    int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
+    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
+    TextDrawFunctor functor(&renderer, &state, x, y, pureTranslate, alpha, mode, op.paint);
+
+    bool hasActiveLayer = false; // TODO
+    fontRenderer.renderPosText(op.paint, clip, (const char*) op.glyphs, op.glyphCount, x, y,
+            op.positions, hasActiveLayer ? &layerBounds : nullptr, &functor, true); // TODO: merging
+}
+
 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
     OffscreenBuffer* buffer = *op.layerHandle;
 
diff --git a/libs/hwui/Canvas.cpp b/libs/hwui/Canvas.cpp
new file mode 100644 (file)
index 0000000..bc88c81
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "Canvas.h"
+
+#include <SkDrawFilter.h>
+
+namespace android {
+
+void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
+    uint32_t flags;
+    SkDrawFilter* drawFilter = getDrawFilter();
+    if (drawFilter) {
+        SkPaint paintCopy(paint);
+        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+        flags = paintCopy.getFlags();
+    } else {
+        flags = paint.getFlags();
+    }
+    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+        // Same values used by Skia
+        const float kStdStrikeThru_Offset   = (-6.0f / 21.0f);
+        const float kStdUnderline_Offset    = (1.0f / 9.0f);
+        const float kStdUnderline_Thickness = (1.0f / 18.0f);
+
+        SkScalar left = x;
+        SkScalar right = x + length;
+        float textSize = paint.getTextSize();
+        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+        if (flags & SkPaint::kUnderlineText_Flag) {
+            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+            drawRect(left, top, right, bottom, paint);
+        }
+        if (flags & SkPaint::kStrikeThruText_Flag) {
+            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+            drawRect(left, top, right, bottom, paint);
+        }
+    }
+}
+
+} // namespace android
index 4bd4ac8..b585a27 100644 (file)
@@ -149,16 +149,12 @@ public:
     // Text
     /**
      * drawText: count is of glyphs
-     * totalAdvance is ignored in software renderering, used by hardware renderer for
-     * text decorations (underlines, strikethroughs).
+     * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
      */
     virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
             const SkPaint& paint, float x, float y,
             float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
             float totalAdvance) = 0;
-    /** drawPosText: count is of UTF16 characters, posCount is floats (2 * glyphs) */
-    virtual void drawPosText(const uint16_t* text, const float* positions, int count,
-            int posCount, const SkPaint& paint) = 0;
     /** drawTextOnPath: count is of glyphs */
     virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) = 0;
@@ -171,6 +167,9 @@ public:
      * to be added to each glyph's position to get its absolute position.
      */
     virtual bool drawTextAbsolutePos() const = 0;
+
+protected:
+    void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
 };
 
 }; // namespace android
index f5e5735..759c12a 100644 (file)
@@ -423,18 +423,6 @@ void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
     addDrawOp(op);
 }
 
-void DisplayListCanvas::drawPosText(const uint16_t* text, const float* positions,
-        int count, int posCount, const SkPaint& paint) {
-    if (!text || count <= 0) return;
-
-    int bytesCount = 2 * count;
-    positions = refBuffer<float>(positions, count * 2);
-
-    DrawOp* op = new (alloc()) DrawPosTextOp(refText((const char*) text, bytesCount),
-                                             bytesCount, count, positions, refPaint(&paint));
-    addDrawOp(op);
-}
-
 void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions,
         int count, const SkPaint& paint, float x, float y,
         float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
@@ -450,6 +438,7 @@ void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions,
     DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
             x, y, positions, refPaint(&paint), totalAdvance, bounds);
     addDrawOp(op);
+    drawTextDecorations(x, y, totalAdvance, paint);
 }
 
 void DisplayListCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
index 609103b..bf98f79 100644 (file)
@@ -212,8 +212,6 @@ public:
     virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
             float boundsRight, float boundsBottom, float totalAdvance) override;
-    virtual void drawPosText(const uint16_t* text, const float* positions, int count,
-            int posCount, const SkPaint& paint) override;
     virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) override;
     virtual bool drawTextAbsolutePos() const override { return false; }
index 772aa72..977b53c 100644 (file)
@@ -1278,24 +1278,6 @@ private:
     float mVOffset;
 };
 
-class DrawPosTextOp : public DrawSomeTextOp {
-public:
-    DrawPosTextOp(const char* text, int bytesCount, int count,
-            const float* positions, const SkPaint* paint)
-            : DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) {
-        /* TODO: inherit from DrawBounded and init mLocalBounds */
-    }
-
-    virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
-        renderer.drawPosText(mText, mBytesCount, mCount, mPositions, mPaint);
-    }
-
-    virtual const char* name() override { return "DrawPosText"; }
-
-private:
-    const float* mPositions;
-};
-
 class DrawTextOp : public DrawStrokableOp {
 public:
     DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
index ccf0b48..5f33cae 100644 (file)
 #include "Extensions.h"
 #include "Glop.h"
 #include "GlopBuilder.h"
-#include "OpenGLRenderer.h"
 #include "PixelBuffer.h"
 #include "Rect.h"
 #include "renderstate/RenderState.h"
 #include "utils/Blur.h"
 #include "utils/Timing.h"
 
+
+#if HWUI_NEW_OPS
+#include "BakedOpState.h"
+#include "BakedOpRenderer.h"
+#else
+#include "OpenGLRenderer.h"
+#endif
+
 #include <algorithm>
 #include <cutils/properties.h>
 #include <SkGlyph.h>
@@ -59,14 +66,25 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
     int transformFlags = pureTranslate
             ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
     Glop glop;
+#if HWUI_NEW_OPS
+    GlopBuilder(renderer->renderState(), renderer->caches(), &glop)
+            .setRoundRectClipState(bakedState->roundRectClipState)
+            .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
+            .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
+            .setTransform(bakedState->computedState.transform, transformFlags)
+            .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
+            .build();
+    renderer->renderGlop(*bakedState, glop);
+#else
     GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop)
+            .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState)
             .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
             .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha)
             .setTransform(*(renderer->currentSnapshot()), transformFlags)
             .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
-            .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState)
             .build();
     renderer->renderGlop(glop);
+#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -539,7 +557,7 @@ void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) {
 }
 
 FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
-        uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) {
+        int numGlyphs, float radius, const float* positions) {
     checkInit();
 
     DropShadow image;
@@ -558,7 +576,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
     mBounds = nullptr;
 
     Rect bounds;
-    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
+    mCurrentFont->measure(paint, text, numGlyphs, &bounds, positions);
 
     uint32_t intRadius = Blur::convertRadiusToInt(radius);
     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
@@ -590,7 +608,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
         // text has non-whitespace, so draw and blur to create the shadow
         // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
         // TODO: don't draw pure whitespace in the first place, and avoid needing this check
-        mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
+        mCurrentFont->render(paint, text, numGlyphs, penX, penY,
                 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
 
         // Unbind any PBO we might have used
@@ -635,15 +653,15 @@ void FontRenderer::endPrecaching() {
 }
 
 bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
-        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
-        const float* positions, Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
+        int numGlyphs, int x, int y, const float* positions,
+        Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
     if (!mCurrentFont) {
         ALOGE("No font set");
         return false;
     }
 
     initRender(clip, bounds, functor);
-    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
+    mCurrentFont->render(paint, text, numGlyphs, x, y, positions);
 
     if (forceFinish) {
         finishRender();
@@ -653,15 +671,15 @@ bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const c
 }
 
 bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
-        uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
-        float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor) {
+        int numGlyphs, const SkPath* path, float hOffset, float vOffset,
+        Rect* bounds, TextDrawFunctor* functor) {
     if (!mCurrentFont) {
         ALOGE("No font set");
         return false;
     }
 
     initRender(clip, bounds, functor);
-    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
+    mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset);
     finishRender();
 
     return mDrawn;
index 8172312..87cfe7f 100644 (file)
@@ -44,13 +44,28 @@ namespace RSC {
 namespace android {
 namespace uirenderer {
 
+#if HWUI_NEW_OPS
+class BakedOpState;
+class BakedOpRenderer;
+#else
 class OpenGLRenderer;
+#endif
 
 class TextDrawFunctor {
 public:
-    TextDrawFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
+    TextDrawFunctor(
+#if HWUI_NEW_OPS
+            BakedOpRenderer* renderer,
+            const BakedOpState* bakedState,
+#else
+            OpenGLRenderer* renderer,
+#endif
+            float x, float y, bool pureTranslate,
             int alpha, SkXfermode::Mode mode, const SkPaint* paint)
         : renderer(renderer)
+#if HWUI_NEW_OPS
+        , bakedState(bakedState)
+#endif
         , x(x)
         , y(y)
         , pureTranslate(pureTranslate)
@@ -61,7 +76,12 @@ public:
 
     void draw(CacheTexture& texture, bool linearFiltering);
 
+#if HWUI_NEW_OPS
+    BakedOpRenderer* renderer;
+    const BakedOpState* bakedState;
+#else
     OpenGLRenderer* renderer;
+#endif
     float x;
     float y;
     bool pureTranslate;
@@ -83,15 +103,13 @@ public:
     void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
     void endPrecaching();
 
-    // bounds is an out parameter
     bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
-            uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions,
-            Rect* bounds, TextDrawFunctor* functor, bool forceFinish = true);
+            int numGlyphs, int x, int y, const float* positions,
+            Rect* outBounds, TextDrawFunctor* functor, bool forceFinish = true);
 
-    // bounds is an out parameter
     bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
-            uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
-            float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor);
+            int numGlyphs, const SkPath* path,
+            float hOffset, float vOffset, Rect* outBounds, TextDrawFunctor* functor);
 
     struct DropShadow {
         uint32_t width;
@@ -103,8 +121,8 @@ public:
 
     // After renderDropShadow returns, the called owns the memory in DropShadow.image
     // and is responsible for releasing it when it's done with it
-    DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex,
-            uint32_t len, int numGlyphs, float radius, const float* positions);
+    DropShadow renderDropShadow(const SkPaint* paint, const char *text, int numGlyphs,
+            float radius, const float* positions);
 
     void setTextureFiltering(bool linearFiltering) {
         mLinearFiltering = linearFiltering;
index 96cac7e..5e954ae 100644 (file)
@@ -671,6 +671,13 @@ void OpReorderer::onBitmapOp(const BitmapOp& op) {
     currentLayer().deferMergeableOp(mAllocator, bakedStateOp, OpBatchType::Bitmap, mergeId);
 }
 
+void OpReorderer::onLinesOp(const LinesOp& op) {
+    BakedOpState* bakedStateOp = tryBakeOpState(op);
+    if (!bakedStateOp) return; // quick rejected
+    currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
+
+}
+
 void OpReorderer::onRectOp(const RectOp& op) {
     BakedOpState* bakedStateOp = tryBakeOpState(op);
     if (!bakedStateOp) return; // quick rejected
@@ -683,6 +690,17 @@ void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
     currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
 }
 
+void OpReorderer::onTextOp(const TextOp& op) {
+    BakedOpState* bakedStateOp = tryBakeOpState(op);
+    if (!bakedStateOp) return; // quick rejected
+
+    // TODO: better handling of shader (since we won't care about color then)
+    batchid_t batchId = op.paint->getColor() == SK_ColorBLACK
+            ? OpBatchType::Text : OpBatchType::ColorText;
+    mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
+    currentLayer().deferMergeableOp(mAllocator, bakedStateOp, batchId, mergeId);
+}
+
 void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
         float contentTranslateX, float contentTranslateY,
         const Rect& repaintRect,
index 12c4607..e386b1c 100644 (file)
@@ -1950,7 +1950,7 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
 }
 
 void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
-        int bytesCount, int count, const float* positions,
+        int count, const float* positions,
         FontRenderer& fontRenderer, int alpha, float x, float y) {
     mCaches.textureState().activateTexture(0);
 
@@ -1963,7 +1963,7 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
     //       if shader-based correction is enabled
     mCaches.dropShadowCache.setFontRenderer(fontRenderer);
     ShadowTexture* texture = mCaches.dropShadowCache.get(
-            paint, text, bytesCount, count, textShadow.radius, positions);
+            paint, text, count, textShadow.radius, positions);
     // If the drop shadow exceeds the max texture size or couldn't be
     // allocated, skip drawing
     if (!texture) return;
@@ -1991,57 +1991,6 @@ bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
             && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
 }
 
-void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
-        const float* positions, const SkPaint* paint) {
-    if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
-        return;
-    }
-
-    // NOTE: Skia does not support perspective transform on drawPosText yet
-    if (!currentTransform()->isSimple()) {
-        return;
-    }
-
-    mRenderState.scissor().setEnabled(true);
-
-    float x = 0.0f;
-    float y = 0.0f;
-    const bool pureTranslate = currentTransform()->isPureTranslate();
-    if (pureTranslate) {
-        x = floorf(x + currentTransform()->getTranslateX() + 0.5f);
-        y = floorf(y + currentTransform()->getTranslateY() + 0.5f);
-    }
-
-    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
-    fontRenderer.setFont(paint, SkMatrix::I());
-
-    int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
-    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
-
-    if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
-        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
-                alpha, 0.0f, 0.0f);
-    }
-
-    // Pick the appropriate texture filtering
-    bool linearFilter = currentTransform()->changesBounds();
-    if (pureTranslate && !linearFilter) {
-        linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
-    }
-    fontRenderer.setTextureFiltering(linearFilter);
-
-    const Rect& clip(pureTranslate ? writableSnapshot()->getRenderTargetClip() : writableSnapshot()->getLocalClip());
-    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-
-    TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
-    if (fontRenderer.renderPosText(paint, &clip, text, 0, bytesCount, count, x, y,
-            positions, hasLayer() ? &bounds : nullptr, &functor)) {
-        dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
-        mDirty = true;
-    }
-
-}
-
 bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const {
     if (CC_LIKELY(transform.isPureTranslate())) {
         outMatrix->setIdentity();
@@ -2166,7 +2115,7 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float
 
     if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
         fontRenderer.setFont(paint, SkMatrix::I());
-        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
+        drawTextShadow(paint, text, count, positions, fontRenderer,
                 alpha, oldX, oldY);
     }
 
@@ -2195,17 +2144,22 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float
     Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
     bool status;
+#if HWUI_NEW_OPS
+    LOG_ALWAYS_FATAL("unsupported");
+    TextDrawFunctor functor(nullptr, nullptr, x, y, pureTranslate, alpha, mode, paint);
+#else
     TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
+#endif
 
     // don't call issuedrawcommand, do it at end of batch
     bool forceFinish = (drawOpMode != DrawOpMode::kDefer);
     if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
         SkPaint paintCopy(*paint);
         paintCopy.setTextAlign(SkPaint::kLeft_Align);
-        status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
+        status = fontRenderer.renderPosText(&paintCopy, clip, text, count, x, y,
                 positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
     } else {
-        status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
+        status = fontRenderer.renderPosText(paint, clip, text, count, x, y,
                 positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
     }
 
@@ -2216,8 +2170,6 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float
         dirtyLayerUnchecked(layerBounds, getRegion());
     }
 
-    drawTextDecorations(totalAdvance, oldX, oldY, paint);
-
     mDirty = true;
 }
 
@@ -2236,12 +2188,17 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
 
     int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
     SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
+#if HWUI_NEW_OPS
+    LOG_ALWAYS_FATAL("unsupported");
+    TextDrawFunctor functor(nullptr, nullptr, 0.0f, 0.0f, false, alpha, mode, paint);
+#else
     TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
+#endif
 
     const Rect* clip = &writableSnapshot()->getLocalClip();
     Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
-    if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
+    if (fontRenderer.renderTextOnPath(paint, clip, text, count, path,
             hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
         dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
         mDirty = true;
@@ -2375,56 +2332,6 @@ void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y,
     renderGlop(glop);
 }
 
-// Same values used by Skia
-#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
-#define kStdUnderline_Offset    (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
-void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y,
-        const SkPaint* paint) {
-    // Handle underline and strike-through
-    uint32_t flags = paint->getFlags();
-    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
-        SkPaint paintCopy(*paint);
-
-        if (CC_LIKELY(underlineWidth > 0.0f)) {
-            const float textSize = paintCopy.getTextSize();
-            const float strokeWidth = std::max(textSize * kStdUnderline_Thickness, 1.0f);
-
-            const float left = x;
-            float top = 0.0f;
-
-            int linesCount = 0;
-            if (flags & SkPaint::kUnderlineText_Flag) linesCount++;
-            if (flags & SkPaint::kStrikeThruText_Flag) linesCount++;
-
-            const int pointsCount = 4 * linesCount;
-            float points[pointsCount];
-            int currentPoint = 0;
-
-            if (flags & SkPaint::kUnderlineText_Flag) {
-                top = y + textSize * kStdUnderline_Offset;
-                points[currentPoint++] = left;
-                points[currentPoint++] = top;
-                points[currentPoint++] = left + underlineWidth;
-                points[currentPoint++] = top;
-            }
-
-            if (flags & SkPaint::kStrikeThruText_Flag) {
-                top = y + textSize * kStdStrikeThru_Offset;
-                points[currentPoint++] = left;
-                points[currentPoint++] = top;
-                points[currentPoint++] = left + underlineWidth;
-                points[currentPoint++] = top;
-            }
-
-            paintCopy.setStrokeWidth(strokeWidth);
-
-            drawLines(&points[0], pointsCount, &paintCopy);
-        }
-    }
-}
-
 void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
     if (mState.currentlyIgnored()) {
         return;
index 400c225..84bc9b0 100755 (executable)
@@ -193,8 +193,6 @@ public:
     void drawPoints(const float* points, int count, const SkPaint* paint);
     void drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
             float hOffset, float vOffset, const SkPaint* paint);
-    void drawPosText(const char* text, int bytesCount, int count,
-            const float* positions, const SkPaint* paint);
     void drawText(const char* text, int bytesCount, int count, float x, float y,
             const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
             DrawOpMode drawOpMode = DrawOpMode::kImmediate);
@@ -637,24 +635,11 @@ private:
      */
     void drawConvexPath(const SkPath& path, const SkPaint* paint);
 
-    /**
-     * Draws text underline and strike-through if needed.
-     *
-     * @param text The text to decor
-     * @param bytesCount The number of bytes in the text
-     * @param totalAdvance The total advance in pixels, defines underline/strikethrough length
-     * @param x The x coordinate where the text will be drawn
-     * @param y The y coordinate where the text will be drawn
-     * @param paint The paint to draw the text with
-     */
-    void drawTextDecorations(float totalAdvance, float x, float y, const SkPaint* paint);
-
    /**
      * Draws shadow layer on text (with optional positions).
      *
      * @param paint The paint to draw the shadow with
      * @param text The text to draw
-     * @param bytesCount The number of bytes in the text
      * @param count The number of glyphs in the text
      * @param positions The x, y positions of individual glyphs (or NULL)
      * @param fontRenderer The font renderer object
@@ -662,7 +647,7 @@ private:
      * @param x The x coordinate where the shadow will be drawn
      * @param y The y coordinate where the shadow will be drawn
      */
-    void drawTextShadow(const SkPaint* paint, const char* text, int bytesCount, int count,
+    void drawTextShadow(const SkPaint* paint, const char* text, int count,
             const float* positions, FontRenderer& fontRenderer, int alpha,
             float x, float y);
 
index ef05367..127dca5 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef ANDROID_HWUI_RECORDED_OP_H
 #define ANDROID_HWUI_RECORDED_OP_H
 
+#include "font/FontUtil.h"
 #include "Matrix.h"
 #include "Rect.h"
 #include "RenderNode.h"
@@ -42,10 +43,12 @@ struct Vertex;
  */
 #define MAP_OPS(OP_FN) \
         OP_FN(BitmapOp) \
+        OP_FN(LinesOp) \
         OP_FN(RectOp) \
         OP_FN(RenderNodeOp) \
         OP_FN(ShadowOp) \
         OP_FN(SimpleRectsOp) \
+        OP_FN(TextOp) \
         OP_FN(BeginLayerOp) \
         OP_FN(EndLayerOp) \
         OP_FN(LayerOp)
@@ -98,6 +101,10 @@ struct RenderNodeOp : RecordedOp {
     bool skipInOrderDraw = false;
 };
 
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Standard Ops
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
 struct BitmapOp : RecordedOp {
     BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
             : SUPER(BitmapOp)
@@ -106,6 +113,15 @@ struct BitmapOp : RecordedOp {
     // TODO: asset atlas/texture id lookup?
 };
 
+struct LinesOp : RecordedOp {
+    LinesOp(BASE_PARAMS, const float* points, const int floatCount)
+            : SUPER(LinesOp)
+            , points(points)
+            , floatCount(floatCount) {}
+    const float* points;
+    const int floatCount;
+};
+
 struct RectOp : RecordedOp {
     RectOp(BASE_PARAMS)
             : SUPER(RectOp) {}
@@ -148,6 +164,27 @@ struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?)
     const size_t vertexCount;
 };
 
+struct TextOp : RecordedOp {
+    TextOp(BASE_PARAMS, const glyph_t* glyphs, const float* positions, int glyphCount,
+            float x, float y)
+            : SUPER(TextOp)
+            , glyphs(glyphs)
+            , positions(positions)
+            , glyphCount(glyphCount)
+            , x(x)
+            , y(y) {}
+    const glyph_t* glyphs;
+    const float* positions;
+    const int glyphCount;
+    const float x;
+    const float y;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Layers
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
 /**
  * Stateful operation! denotes the creation of an off-screen layer,
  * and that commands following will render into it.
index 6ab253c..61fa384 100644 (file)
@@ -230,12 +230,9 @@ void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) {
 
 void RecordingCanvas::drawPaint(const SkPaint& paint) {
     // TODO: more efficient recording?
-    Matrix4 identity;
-    identity.loadIdentity();
-
     addOp(new (alloc()) RectOp(
             mState.getRenderTargetClipBounds(),
-            identity,
+            Matrix4::identity(),
             mState.getRenderTargetClipBounds(),
             refPaint(&paint)));
 }
@@ -244,9 +241,30 @@ void RecordingCanvas::drawPaint(const SkPaint& paint) {
 void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
     LOG_ALWAYS_FATAL("TODO!");
 }
-void RecordingCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+
+void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
+    if (floatCount < 4) return;
+    floatCount &= ~0x3; // round down to nearest four
+
+    Rect unmappedBounds(points[0], points[1], points[0], points[1]);
+    for (int i = 2; i < floatCount; i += 2) {
+        unmappedBounds.left = std::min(unmappedBounds.left, points[i]);
+        unmappedBounds.right = std::max(unmappedBounds.right, points[i]);
+        unmappedBounds.top = std::min(unmappedBounds.top, points[i + 1]);
+        unmappedBounds.bottom = std::max(unmappedBounds.bottom, points[i + 1]);
+    }
+
+    // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced
+    // 1.0 stroke, treat 1.0 as minimum.
+    unmappedBounds.outset(std::max(paint.getStrokeWidth(), 1.0f) * 0.5f);
+
+    addOp(new (alloc()) LinesOp(
+            unmappedBounds,
+            *mState.currentSnapshot()->transform,
+            mState.getRenderTargetClipBounds(),
+            refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
 }
+
 void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
     addOp(new (alloc()) RectOp(
             Rect(left, top, right, bottom),
@@ -388,17 +406,24 @@ void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_p
 }
 
 // Text
-void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int count,
+void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int glyphCount,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
             float boundsRight, float boundsBottom, float totalAdvance) {
-    LOG_ALWAYS_FATAL("TODO!");
-}
-void RecordingCanvas::drawPosText(const uint16_t* text, const float* positions, int count,
-            int posCount, const SkPaint& paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+    if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
+    glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
+    positions = refBuffer<float>(positions, glyphCount * 2);
+
+    addOp(new (alloc()) TextOp(
+            Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            refPaint(&paint), glyphs, positions, glyphCount, x, y));
+    drawTextDecorations(x, y, totalAdvance, paint);
 }
+
 void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) {
+    // NOTE: can't use refPaint() directly, since it forces left alignment
     LOG_ALWAYS_FATAL("TODO!");
 }
 
index f26b0c8..736cc9e 100644 (file)
@@ -178,8 +178,6 @@ public:
     virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
             float boundsRight, float boundsBottom, float totalAdvance) override;
-    virtual void drawPosText(const uint16_t* text, const float* positions, int count,
-            int posCount, const SkPaint& paint) override;
     virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) override;
     virtual bool drawTextAbsolutePos() const override { return false; }
@@ -221,6 +219,15 @@ private:
         return cachedPath;
     }
 
+    /**
+     * Returns a RenderThread-safe, const copy of the SkPaint parameter passed in (with deduping
+     * based on paint generation ID)
+     *
+     * Note that this forces Left_Align, since drawText glyph rendering expects left alignment,
+     * since alignment offsetting has been done at a higher level. This is done to essentially all
+     * copied paints, since the deduping can mean a paint is shared by drawText commands and other
+     * types (which wouldn't care about alignment).
+     */
     inline const SkPaint* refPaint(const SkPaint* paint) {
         if (!paint) return nullptr;
 
@@ -239,10 +246,11 @@ private:
         // In the unlikely event that 2 unique paints have the same hash we do a
         // object equality check to ensure we don't erroneously dedup them.
         if (cachedPaint == nullptr || *cachedPaint != *paint) {
-            cachedPaint = new SkPaint(*paint);
-            std::unique_ptr<const SkPaint> copy(cachedPaint);
-            mDisplayList->paints.push_back(std::move(copy));
+            SkPaint* copy = new SkPaint(*paint);
+            copy->setTextAlign(SkPaint::kLeft_Align);
 
+            cachedPaint = copy;
+            mDisplayList->paints.emplace_back(copy);
             // replaceValueFor() performs an add if the entry doesn't exist
             mPaintMap.replaceValueFor(key, cachedPaint);
             refBitmapsInShader(cachedPaint->getShader());
index 0736a10..472aad7 100644 (file)
@@ -260,13 +260,6 @@ public:
         bottom = std::max(bottom, y);
     }
 
-    void expandToCoverRect(float otherLeft, float otherTop, float otherRight, float otherBottom) {
-        left = std::min(left, otherLeft);
-        top = std::min(top, otherTop);
-        right = std::max(right, otherRight);
-        bottom = std::max(bottom, otherBottom);
-    }
-
     SkRect toSkRect() const {
         return SkRect::MakeLTRB(left, top, right, bottom);
     }
index 6d3dfac..96c1a7c 100644 (file)
@@ -131,8 +131,6 @@ public:
             const SkPaint& paint, float x, float y,
             float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
             float totalAdvance) override;
-    virtual void drawPosText(const uint16_t* text, const float* positions, int count,
-            int posCount, const SkPaint& paint) override;
     virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) override;
 
@@ -152,7 +150,6 @@ private:
 
     void drawPoints(const float* points, int count, const SkPaint& paint,
                     SkCanvas::PointMode mode);
-    void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
 
     SkAutoTUnref<SkCanvas> mCanvas;
     std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
@@ -712,22 +709,7 @@ void SkiaCanvas::drawText(const uint16_t* text, const float* positions, int coun
 
     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
     mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy);
-}
-
-void SkiaCanvas::drawPosText(const uint16_t* text, const float* positions, int count, int posCount,
-        const SkPaint& paint) {
-    SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
-    int indx;
-    for (indx = 0; indx < posCount; indx++) {
-        posPtr[indx].fX = positions[indx << 1];
-        posPtr[indx].fY = positions[(indx << 1) + 1];
-    }
-
-    SkPaint paintCopy(paint);
-    paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding);
-    mCanvas->drawPosText(text, count, posPtr, paintCopy);
-
-    delete[] posPtr;
+    drawTextDecorations(x, y, totalAdvance, paint);
 }
 
 void SkiaCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
index b7a76ba..996ac8e 100644 (file)
@@ -30,8 +30,7 @@ namespace uirenderer {
 ///////////////////////////////////////////////////////////////////////////////
 
 hash_t ShadowText::hash() const {
-    uint32_t charCount = len / sizeof(char16_t);
-    uint32_t hash = JenkinsHashMix(0, len);
+    uint32_t hash = JenkinsHashMix(0, glyphCount);
     hash = JenkinsHashMix(hash, android::hash_type(radius));
     hash = JenkinsHashMix(hash, android::hash_type(textSize));
     hash = JenkinsHashMix(hash, android::hash_type(typeface));
@@ -40,10 +39,10 @@ hash_t ShadowText::hash() const {
     hash = JenkinsHashMix(hash, android::hash_type(scaleX));
     if (text) {
         hash = JenkinsHashMixShorts(
-            hash, reinterpret_cast<const uint16_t*>(text), charCount);
+            hash, reinterpret_cast<const uint16_t*>(text), glyphCount);
     }
     if (positions) {
-        for (uint32_t i = 0; i < charCount * 2; i++) {
+        for (uint32_t i = 0; i < glyphCount * 2; i++) {
             hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
         }
     }
@@ -51,7 +50,7 @@ hash_t ShadowText::hash() const {
 }
 
 int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) {
-    int deltaInt = int(lhs.len) - int(rhs.len);
+    int deltaInt = int(lhs.glyphCount) - int(rhs.glyphCount);
     if (deltaInt != 0) return deltaInt;
 
     deltaInt = lhs.flags - rhs.flags;
@@ -76,7 +75,7 @@ int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) {
         if (!lhs.text) return -1;
         if (!rhs.text) return +1;
 
-        deltaInt = memcmp(lhs.text, rhs.text, lhs.len);
+        deltaInt = memcmp(lhs.text, rhs.text, lhs.glyphCount * sizeof(glyph_t));
         if (deltaInt != 0) return deltaInt;
     }
 
@@ -84,7 +83,7 @@ int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) {
         if (!lhs.positions) return -1;
         if (!rhs.positions) return +1;
 
-        return memcmp(lhs.positions, rhs.positions, lhs.len << 2);
+        return memcmp(lhs.positions, rhs.positions, lhs.glyphCount << 1);
     }
 
     return 0;
@@ -168,16 +167,16 @@ void TextDropShadowCache::clear() {
     mCache.clear();
 }
 
-ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, uint32_t len,
-        int numGlyphs, float radius, const float* positions) {
-    ShadowText entry(paint, radius, len, text, positions);
+ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs, int numGlyphs,
+        float radius, const float* positions) {
+    ShadowText entry(paint, radius, numGlyphs * 2, glyphs, positions);
     ShadowTexture* texture = mCache.get(entry);
 
     if (!texture) {
         SkPaint paintCopy(*paint);
         paintCopy.setTextAlign(SkPaint::kLeft_Align);
-        FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, text, 0,
-                len, numGlyphs, radius, positions);
+        FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs,
+                radius, positions);
 
         if (!shadow.image) {
             return nullptr;
index caf089f..c4f3c5d 100644 (file)
@@ -34,14 +34,14 @@ class Caches;
 class FontRenderer;
 
 struct ShadowText {
-    ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(nullptr),
+    ShadowText(): glyphCount(0), radius(0.0f), textSize(0.0f), typeface(nullptr),
             flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) {
     }
 
     // len is the number of bytes in text
-    ShadowText(const SkPaint* paint, float radius, uint32_t len, const char* srcText,
+    ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const char* srcText,
             const float* positions):
-            len(len), radius(radius), positions(positions) {
+            glyphCount(glyphCount), radius(radius), positions(positions) {
         // TODO: Propagate this through the API, we should not cast here
         text = (const char16_t*) srcText;
 
@@ -73,17 +73,16 @@ struct ShadowText {
     }
 
     void copyTextLocally() {
-        uint32_t charCount = len / sizeof(char16_t);
-        str.setTo((const char16_t*) text, charCount);
+        str.setTo((const char16_t*) text, glyphCount);
         text = str.string();
         if (positions != nullptr) {
             positionsCopy.clear();
-            positionsCopy.appendArray(positions, charCount * 2);
+            positionsCopy.appendArray(positions, glyphCount * 2);
             positions = positionsCopy.array();
         }
     }
 
-    uint32_t len;
+    uint32_t glyphCount;
     float radius;
     float textSize;
     SkTypeface* typeface;
@@ -136,7 +135,7 @@ public:
      */
     void operator()(ShadowText& text, ShadowTexture*& texture) override;
 
-    ShadowTexture* get(const SkPaint* paint, const char* text, uint32_t len,
+    ShadowTexture* get(const SkPaint* paint, const char* text,
             int numGlyphs, float radius, const float* positions);
 
     /**
index d680f99..dc82041 100644 (file)
@@ -291,20 +291,18 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo
     return cachedGlyph;
 }
 
-void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+void Font::render(const SkPaint* paint, const char *text,
             int numGlyphs, int x, int y, const float* positions) {
-    render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, nullptr,
+    render(paint, text, numGlyphs, x, y, FRAMEBUFFER, nullptr,
             0, 0, nullptr, positions);
 }
 
-void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
-        int numGlyphs, const SkPath* path, float hOffset, float vOffset) {
-    if (numGlyphs == 0 || text == nullptr || len == 0) {
+void Font::render(const SkPaint* paint, const char *text, int numGlyphs,
+        const SkPath* path, float hOffset, float vOffset) {
+    if (numGlyphs == 0 || text == nullptr) {
         return;
     }
 
-    text += start;
-
     int glyphsCount = 0;
     SkFixed prevRsbDelta = 0;
 
@@ -317,7 +315,7 @@ void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32
     float pathLength = SkScalarToFloat(measure.getLength());
 
     if (paint->getTextAlign() != SkPaint::kLeft_Align) {
-        float textWidth = SkScalarToFloat(paint->measureText(text, len));
+        float textWidth = SkScalarToFloat(paint->measureText(text, numGlyphs * 2));
         float pathOffset = pathLength;
         if (paint->getTextAlign() == SkPaint::kCenter_Align) {
             textWidth *= 0.5f;
@@ -347,14 +345,14 @@ void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32
     }
 }
 
-void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::measure(const SkPaint* paint, const char* text,
         int numGlyphs, Rect *bounds, const float* positions) {
     if (bounds == nullptr) {
         ALOGE("No return rectangle provided to measure text");
         return;
     }
     bounds->set(1e6, -1e6, -1e6, 1e6);
-    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
+    render(paint, text, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
 }
 
 void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
@@ -378,10 +376,10 @@ void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
     }
 }
 
-void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::render(const SkPaint* paint, const char* text,
         int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
         uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
-    if (numGlyphs == 0 || text == nullptr || len == 0) {
+    if (numGlyphs == 0 || text == nullptr) {
         return;
     }
 
@@ -395,7 +393,6 @@ void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32
     };
     RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform];
 
-    text += start;
     int glyphsCount = 0;
 
     while (glyphsCount < numGlyphs) {
index 3119d73..59518a1 100644 (file)
@@ -82,10 +82,10 @@ public:
 
     ~Font();
 
-    void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+    void render(const SkPaint* paint, const char* text,
             int numGlyphs, int x, int y, const float* positions);
 
-    void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+    void render(const SkPaint* paint, const char* text,
             int numGlyphs, const SkPath* path, float hOffset, float vOffset);
 
     const Font::FontDescription& getDescription() const {
@@ -113,11 +113,11 @@ private:
 
     void precache(const SkPaint* paint, const char* text, int numGlyphs);
 
-    void render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+    void render(const SkPaint* paint, const char *text,
             int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
             uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
 
-    void measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+    void measure(const SkPaint* paint, const char* text,
             int numGlyphs, Rect *bounds, const float* positions);
 
     void invalidateTextureCache(CacheTexture* cacheTexture = nullptr);
index ec8048d..d76086c 100644 (file)
@@ -136,14 +136,14 @@ TEST(OpReorderer, simpleRejection) {
 }
 
 TEST(OpReorderer, simpleBatching) {
-    static int SIMPLE_BATCHING_LOOPS = 5;
+    const int LOOPS = 5;
     class SimpleBatchingTestRenderer : public TestRendererBase {
     public:
         void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
-            EXPECT_TRUE(mIndex++ >= SIMPLE_BATCHING_LOOPS);
+            EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
         }
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
-            EXPECT_TRUE(mIndex++ < SIMPLE_BATCHING_LOOPS);
+            EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
         }
     };
 
@@ -153,7 +153,7 @@ TEST(OpReorderer, simpleBatching) {
         // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
         // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-        for (int i = 0; i < SIMPLE_BATCHING_LOOPS; i++) {
+        for (int i = 0; i < LOOPS; i++) {
             canvas.translate(0, 10);
             canvas.drawRect(0, 0, 10, 10, SkPaint());
             canvas.drawBitmap(bitmap, 5, 0, nullptr);
@@ -164,7 +164,35 @@ TEST(OpReorderer, simpleBatching) {
     OpReorderer reorderer(200, 200, *dl, sLightCenter);
     SimpleBatchingTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
-    EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, renderer.getIndex()); // 2 x loops ops, because no merging (TODO: force no merging)
+    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
+            << "Expect number of ops = 2 * loop count"; // TODO: force no merging
+}
+
+TEST(OpReorderer, textStrikethroughBatching) {
+    const int LOOPS = 5;
+    class TextStrikethroughTestRenderer : public TestRendererBase {
+    public:
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
+        }
+        void onTextOp(const TextOp& op, const BakedOpState& state) override {
+            EXPECT_TRUE(mIndex++ < LOOPS) << "Text should be beneath all strikethrough rects";
+        }
+    };
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 2000, [](RecordingCanvas& canvas) {
+        SkPaint textPaint;
+        textPaint.setAntiAlias(true);
+        textPaint.setTextSize(20);
+        textPaint.setStrikeThruText(true);
+        for (int i = 0; i < LOOPS; i++) {
+            TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
+        }
+    });
+    OpReorderer reorderer(200, 2000, *dl, sLightCenter);
+    TextStrikethroughTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
+            << "Expect number of ops = 2 * loop count"; // TODO: force no merging
 }
 
 TEST(OpReorderer, renderNode) {
index 22190f5..c23d47e 100644 (file)
@@ -41,21 +41,110 @@ TEST(RecordingCanvas, emptyPlayback) {
     playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
 }
 
-TEST(RecordingCanvas, testSimpleRectRecord) {
+TEST(RecordingCanvas, drawLines) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+        SkPaint paint;
+        paint.setStrokeWidth(20);
+        float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
+        canvas.drawLines(&points[0], 7, paint);
+    });
+
+    ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
+    auto op = dl->getOps()[0];
+    ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
+    EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
+            << "float count must be rounded down to closest multiple of 4";
+    EXPECT_EQ(Rect(-10, -10, 30, 20), op->unmappedBounds)
+            << "unmapped bounds must be size of line, outset by 1/2 stroke width";
+}
+
+TEST(RecordingCanvas, drawRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.drawRect(10, 20, 90, 180, SkPaint());
     });
 
+    ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
+    auto op = *(dl->getOps()[0]);
+    ASSERT_EQ(RecordedOpId::RectOp, op.opId);
+    EXPECT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+    EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
+}
+
+TEST(RecordingCanvas, drawText) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(20);
+        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+    });
+
     int count = 0;
     playbackOps(*dl, [&count](const RecordedOp& op) {
         count++;
-        ASSERT_EQ(RecordedOpId::RectOp, op.opId);
-        ASSERT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
-        ASSERT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
+        ASSERT_EQ(RecordedOpId::TextOp, op.opId);
+        EXPECT_EQ(Rect(0, 0, 200, 200), op.localClipRect);
+        EXPECT_TRUE(op.localMatrix.isIdentity());
+        EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
+                << "Op expected to be 25+ pixels wide, 10+ pixels tall";
     });
     ASSERT_EQ(1, count);
 }
 
+TEST(RecordingCanvas, drawText_strikeThruAndUnderline) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(20);
+        for (int i = 0; i < 2; i++) {
+            for (int j = 0; j < 2; j++) {
+                paint.setUnderlineText(i != 0);
+                paint.setStrikeThruText(j != 0);
+                TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+            }
+        }
+    });
+
+    auto ops = dl->getOps();
+    ASSERT_EQ(8u, ops.size());
+
+    int index = 0;
+    EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
+
+    EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
+    EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
+
+    EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
+    EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
+
+    EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
+    EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
+    EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
+}
+
+TEST(RecordingCanvas, drawText_forceAlignLeft) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(20);
+        paint.setTextAlign(SkPaint::kLeft_Align);
+        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+        paint.setTextAlign(SkPaint::kCenter_Align);
+        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+        paint.setTextAlign(SkPaint::kRight_Align);
+        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+    });
+
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        count++;
+        ASSERT_EQ(RecordedOpId::TextOp, op.opId);
+        EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
+                << "recorded drawText commands must force kLeft_Align on their paint";
+        EXPECT_EQ(SkPaint::kGlyphID_TextEncoding, op.paint->getTextEncoding()); // verify TestUtils
+    });
+    ASSERT_EQ(3, count);
+}
+
 TEST(RecordingCanvas, backgroundAndImage) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         SkBitmap bitmap;
@@ -109,7 +198,7 @@ TEST(RecordingCanvas, backgroundAndImage) {
     ASSERT_EQ(2, count);
 }
 
-TEST(RecordingCanvas, saveLayerSimple) {
+TEST(RecordingCanvas, saveLayer_simple) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
         canvas.drawRect(10, 20, 190, 180, SkPaint());
@@ -143,7 +232,7 @@ TEST(RecordingCanvas, saveLayerSimple) {
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayerViewportCrop) {
+TEST(RecordingCanvas, saveLayer_viewportCrop) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // shouldn't matter, since saveLayer will clip to its bounds
         canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
@@ -167,7 +256,7 @@ TEST(RecordingCanvas, saveLayerViewportCrop) {
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayerRotateUnclipped) {
+TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
         canvas.translate(100, 100);
@@ -193,7 +282,7 @@ TEST(RecordingCanvas, saveLayerRotateUnclipped) {
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayerRotateClipped) {
+TEST(RecordingCanvas, saveLayer_rotateClipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
         canvas.translate(100, 100);
@@ -224,7 +313,7 @@ TEST(RecordingCanvas, saveLayerRotateClipped) {
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, testReorderBarrier) {
+TEST(RecordingCanvas, insertReorderBarrier) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.insertReorderBarrier(true);
index 84230a7..dd6fc36 100644 (file)
@@ -36,5 +36,46 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end)
             | (int)((startB + (int)(fraction * (endB - startB))));
 }
 
+void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
+        const SkPaint& inPaint, float x, float y) {
+   // copy to force TextEncoding (which JNI layer would have done)
+   SkPaint paint(inPaint);
+   paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+   SkMatrix identity;
+   identity.setIdentity();
+   SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
+   SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &identity);
+
+   float totalAdvance = 0;
+   std::vector<glyph_t> glyphs;
+   std::vector<float> positions;
+   Rect bounds;
+   while (*text != '\0') {
+       SkUnichar unichar = SkUTF8_NextUnichar(&text);
+       glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
+       autoCache.getCache()->unicharToGlyph(unichar);
+
+       // push glyph and its relative position
+       glyphs.push_back(glyph);
+       positions.push_back(totalAdvance);
+       positions.push_back(0);
+
+       // compute bounds
+       SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
+       Rect glyphBounds(skGlyph.fWidth, skGlyph.fHeight);
+       glyphBounds.translate(totalAdvance + skGlyph.fLeft, skGlyph.fTop);
+       bounds.unionWith(glyphBounds);
+
+       // advance next character
+       SkScalar skWidth;
+       paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
+       totalAdvance += skWidth;
+   }
+   bounds.translate(x, y);
+   canvas->drawText(glyphs.data(), positions.data(), glyphs.size(), paint, x, y,
+           bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
index f7f4f2d..f9fa242 100644 (file)
@@ -196,6 +196,9 @@ public:
 
     static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
 
+    static void drawTextToCanvas(TestCanvas* canvas, const char* text,
+            const SkPaint& inPaint, float x, float y);
+
 private:
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
         node->syncProperties();