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/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());
     }