Take only the scale params into account to rasterize text

This change extracts the scale parameters of the current transform
to pass then to the font renderer. Rotation and perspective are
applied to the generated mesh inside the vertex shader. This limits
the number of glyphs we have to create in the font cache and thus
reduces memory churn.

Change-Id: Ic5b3bae2b2b0e0250a8ee723b071a1709725c749
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 4a72477..d231907 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1175,8 +1175,8 @@
         SkPaint* paint = getPaint(renderer);
         FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
         const bool pureTranslate = state.mMatrix.isPureTranslate();
-        fontRenderer.precache(paint, mText, mCount,
-                pureTranslate ? mat4::identity() : state.mMatrix);
+        const mat4 transform = renderer.findBestFontTransform(state.mMatrix);
+        fontRenderer.precache(paint, mText, mCount, transform);
     }
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 14c7c39..9aa9615 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -92,11 +92,11 @@
     // who will invoke OpenGLRenderer::resume()
 }
 
-GLint LayerRenderer::getTargetFbo() {
+GLint LayerRenderer::getTargetFbo() const {
     return mLayer->getFbo();
 }
 
-bool LayerRenderer::suppressErrorChecks() {
+bool LayerRenderer::suppressErrorChecks() const {
     return true;
 }
 
@@ -104,7 +104,7 @@
 // Layer support
 ///////////////////////////////////////////////////////////////////////////////
 
-bool LayerRenderer::hasLayer() {
+bool LayerRenderer::hasLayer() const {
     return true;
 }
 
@@ -116,7 +116,7 @@
 // Dirty region tracking
 ///////////////////////////////////////////////////////////////////////////////
 
-Region* LayerRenderer::getRegion() {
+Region* LayerRenderer::getRegion() const {
     if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
         return OpenGLRenderer::getRegion();
     }
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 7a8bdc5..5f86731 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -65,10 +65,10 @@
 
 protected:
     virtual void ensureStencilBuffer();
-    virtual bool hasLayer();
-    virtual Region* getRegion();
-    virtual GLint getTargetFbo();
-    virtual bool suppressErrorChecks();
+    virtual bool hasLayer() const;
+    virtual Region* getRegion() const;
+    virtual GLint getTargetFbo() const;
+    virtual bool suppressErrorChecks() const;
 
 private:
     void generateMesh();
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 204e8f5..6a5ea51 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -232,11 +232,11 @@
     memcpy(v, data, sizeof(data));
 }
 
-float Matrix4::getTranslateX() {
+float Matrix4::getTranslateX() const {
     return data[kTranslateX];
 }
 
-float Matrix4::getTranslateY() {
+float Matrix4::getTranslateY() const {
     return data[kTranslateY];
 }
 
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index be5bea7..7b7357e 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -161,8 +161,8 @@
     void mapRect(Rect& r) const;
     void mapPoint(float& x, float& y) const;
 
-    float getTranslateX();
-    float getTranslateY();
+    float getTranslateX() const;
+    float getTranslateY() const;
 
     void decomposeScale(float& sx, float& sy) const;
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 9c12419..22ec93b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2627,6 +2627,31 @@
     return DrawGlInfo::kStatusDrew;
 }
 
+mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
+    mat4 fontTransform;
+    if (CC_LIKELY(transform.isPureTranslate())) {
+        fontTransform = mat4::identity();
+    } else {
+        if (CC_UNLIKELY(transform.isPerspective())) {
+            // When the below condition is true, we are rendering text with a
+            // perspective transform inside a layer (either an inline layer
+            // created by Canvas.saveLayer() or a hardware layer.)
+            if (hasLayer() || getTargetFbo() != 0) {
+                float sx, sy;
+                currentTransform().decomposeScale(sx, sy);
+                fontTransform.loadScale(sx, sy, 1.0f);
+            } else {
+                fontTransform = mat4::identity();
+            }
+        } else {
+            float sx, sy;
+            currentTransform().decomposeScale(sx, sy);
+            fontTransform.loadScale(sx, sy, 1.0f);
+        }
+    }
+    return fontTransform;
+}
+
 status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, const float* positions, SkPaint* paint, float length) {
     if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
@@ -2653,12 +2678,13 @@
 
     const float oldX = x;
     const float oldY = y;
-    const bool pureTranslate = currentTransform().isPureTranslate();
-    const bool isPerspective = currentTransform().isPerspective();
+
+    const mat4& transform = currentTransform();
+    const bool pureTranslate = transform.isPureTranslate();
 
     if (CC_LIKELY(pureTranslate)) {
-        x = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
-        y = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
+        x = (int) floorf(x + transform.getTranslateX() + 0.5f);
+        y = (int) floorf(y + transform.getTranslateY() + 0.5f);
     }
 
     int alpha;
@@ -2675,23 +2701,18 @@
 
     const bool hasActiveLayer = hasLayer();
 
-    mat4 fontTransform;
-    if (CC_LIKELY(pureTranslate)) {
-        fontTransform = mat4::identity();
-    } else {
-        if (CC_UNLIKELY(isPerspective)) {
-            // When the below condition is true, we are rendering text with a
-            // perspective transform inside a layer (either an inline layer
-            // created by Canvas.saveLayer() or a hardware layer.)
-            if (hasActiveLayer || getTargetFbo() != 0) {
-                fontTransform = currentTransform();
-            } else {
-                fontTransform = mat4::identity();
-            }
-        } else {
-            fontTransform = currentTransform();
-        }
-    }
+    // 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.
+    mat4 fontTransform = findBestFontTransform(transform);
     fontRenderer.setFont(paint, fontTransform);
 
     // Pick the appropriate texture filtering
@@ -2708,16 +2729,17 @@
     setupDrawShader();
     setupDrawBlending(true, mode);
     setupDrawProgram();
-    setupDrawModelView(x, y, x, y, !isPerspective, true);
+    setupDrawModelView(x, y, x, y, pureTranslate, true);
     // See comment above; the font renderer must use texture unit 0
     // assert(mTextureUnit == 0)
     setupDrawTexture(fontRenderer.getTexture(linearFilter));
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms();
-    setupDrawShaderUniforms(!isPerspective);
+    setupDrawShaderUniforms(pureTranslate);
     setupDrawTextGammaUniforms();
 
-    const Rect* clip = isPerspective ? NULL : mSnapshot->clipRect;
+    // TODO: Implement better clipping for scaled/rotated text
+    const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
     Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
     bool status;
@@ -2732,8 +2754,8 @@
     }
 
     if (status && hasActiveLayer) {
-        if (isPerspective) {
-            currentTransform().mapRect(bounds);
+        if (!pureTranslate) {
+            transform.mapRect(bounds);
         }
         dirtyLayerUnchecked(bounds, getRegion());
     }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index d542315..e961af2 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -335,6 +335,12 @@
         }
     }
 
+    /**
+     * Return the best transform to use to rasterize text given a full
+     * transform matrix.
+     */
+    mat4 findBestFontTransform(const mat4& transform) const;
+
 protected:
     /**
      * Computes the projection matrix, initialize the first snapshot
@@ -384,28 +390,28 @@
     /**
      * Returns the current snapshot.
      */
-    sp<Snapshot> getSnapshot() {
+    sp<Snapshot> getSnapshot() const {
         return mSnapshot;
     }
 
     /**
      * Returns the region of the current layer.
      */
-    virtual Region* getRegion() {
+    virtual Region* getRegion() const {
         return mSnapshot->region;
     }
 
     /**
      * Indicates whether rendering is currently targeted at a layer.
      */
-    virtual bool hasLayer() {
+    virtual bool hasLayer() const {
         return (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region;
     }
 
     /**
      * Returns the name of the FBO this renderer is rendering into.
      */
-    virtual GLint getTargetFbo() {
+    virtual GLint getTargetFbo() const {
         return 0;
     }
 
@@ -442,7 +448,7 @@
     /**
      * Set to true to suppress error checks at the end of a frame.
      */
-    virtual bool suppressErrorChecks() {
+    virtual bool suppressErrorChecks() const {
         return false;
     }
 
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index bf522b7..9307f11 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -53,10 +53,8 @@
     mStrokeWidth = paint->getStrokeWidth();
     mAntiAliasing = paint->isAntiAlias();
     mLookupTransform.reset();
-    mLookupTransform[SkMatrix::kMScaleX] = matrix.data[mat4::kScaleX];
-    mLookupTransform[SkMatrix::kMScaleY] = matrix.data[mat4::kScaleY];
-    mLookupTransform[SkMatrix::kMSkewX] = matrix.data[mat4::kSkewX];
-    mLookupTransform[SkMatrix::kMSkewY] = matrix.data[mat4::kSkewY];
+    mLookupTransform[SkMatrix::kMScaleX] = matrix[mat4::kScaleX];
+    mLookupTransform[SkMatrix::kMScaleY] = matrix[mat4::kScaleY];
     if (!mLookupTransform.invert(&mInverseLookupTransform)) {
         ALOGW("Could not query the inverse lookup transform for this font");
     }
@@ -81,8 +79,6 @@
     hash = JenkinsHashMix(hash, int(mAntiAliasing));
     hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
     hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
-    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewX]));
-    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewY]));
     return JenkinsHashWhiten(hash);
 }
 
@@ -122,16 +118,6 @@
     if (lhs.mLookupTransform[SkMatrix::kMScaleY] >
             rhs.mLookupTransform[SkMatrix::kMScaleY]) return +1;
 
-    if (lhs.mLookupTransform[SkMatrix::kMSkewX] <
-            rhs.mLookupTransform[SkMatrix::kMSkewX]) return -1;
-    if (lhs.mLookupTransform[SkMatrix::kMSkewX] >
-            rhs.mLookupTransform[SkMatrix::kMSkewX]) return +1;
-
-    if (lhs.mLookupTransform[SkMatrix::kMSkewY] <
-            rhs.mLookupTransform[SkMatrix::kMSkewY]) return -1;
-    if (lhs.mLookupTransform[SkMatrix::kMSkewY] >
-            rhs.mLookupTransform[SkMatrix::kMSkewY]) return +1;
-
     return 0;
 }
 
@@ -185,7 +171,7 @@
             nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
 }
 
-void Font::drawCachedGlyphPerspective(CachedGlyphInfo* glyph, int x, int y,
+void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
         uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
     SkPoint p[4];
     p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + glyph->mBitmapHeight);
@@ -388,18 +374,17 @@
 
     static RenderGlyph gRenderGlyph[] = {
             &android::uirenderer::Font::drawCachedGlyph,
-            &android::uirenderer::Font::drawCachedGlyphPerspective,
+            &android::uirenderer::Font::drawCachedGlyphTransformed,
             &android::uirenderer::Font::drawCachedGlyphBitmap,
             &android::uirenderer::Font::drawCachedGlyphBitmap,
             &android::uirenderer::Font::measureCachedGlyph,
             &android::uirenderer::Font::measureCachedGlyph
     };
-    RenderGlyph render = gRenderGlyph[(mode << 1) + mTransform.isPerspective()];
+    RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform];
 
     text += start;
     int glyphsCount = 0;
 
-    const bool applyTransform = !mTransform.isIdentity() && !mTransform.isPerspective();
     const SkPaint::Align align = paint->getTextAlign();
 
     while (glyphsCount < numGlyphs) {
@@ -418,10 +403,6 @@
             float penX = x + positions[(glyphsCount << 1)];
             float penY = y + positions[(glyphsCount << 1) + 1];
 
-            if (applyTransform) {
-                mTransform.mapPoint(penX, penY);
-            }
-
             (*this.*render)(cachedGlyph, roundf(penX), roundf(penY),
                     bitmap, bitmapW, bitmapH, bounds, positions);
         }
@@ -495,7 +476,7 @@
         font = new Font(state, description);
         state->mActiveFonts.put(description, font);
     }
-    font->mTransform.load(matrix);
+    font->mIdentityTransform = matrix.isIdentity();
 
     return font;
 }
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index b2382f4..52cca1c 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -125,7 +125,7 @@
     void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
             uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
             Rect* bounds, const float* pos);
-    void drawCachedGlyphPerspective(CachedGlyphInfo* glyph, int x, int y,
+    void drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
             uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
             Rect* bounds, const float* pos);
     void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
@@ -142,7 +142,7 @@
     // Cache of glyphs
     DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
 
-    mat4 mTransform;
+    bool mIdentityTransform;
 };
 
 inline int strictly_order_type(const Font::FontDescription& lhs,