support m_bGlyph_Adjust

Skia has no direct match for a different 2x3
matrix per glyph -- if the 2x3 is a square scale
with rotation, we can do that with RSX transforms.
The bug used the 2x3 to do a condensed face,
which we can do with SkPaint::setTextScaleX.
Otherwise, we draw a character at a time.

R=dsinclair@chromium.org,npm@chromium.org
Bug: 736703
Change-Id: I8f14fd63ffdaee886b63d1154b771f9de092a83f
Reviewed-on: https://pdfium-review.googlesource.com/7272
Commit-Queue: Cary Clark <caryclark@google.com>
Reviewed-by: dsinclair <dsinclair@chromium.org>
Reviewed-by: Nicolás Peña <npm@chromium.org>
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index 40b1ccb..ad3b85c 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -32,6 +32,7 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkRSXform.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/core/SkStream.h"
 #include "third_party/skia/include/core/SkTypeface.h"
@@ -664,6 +665,7 @@
       : m_pDriver(pDriver),
         m_pTypeFace(nullptr),
         m_fontSize(0),
+        m_scaleX(0),
         m_fillColor(0),
         m_strokeColor(0),
         m_blendType(0),
@@ -772,6 +774,38 @@
     m_type = Accumulator::kNone;
   }
 
+  bool HasRSX(int nChars,
+              const FXTEXT_CHARPOS* pCharPos,
+              float* scaleXPtr,
+              bool* oneAtATimePtr) {
+    bool useRSXform = false;
+    bool oneAtATime = false;
+    float scaleX = 1;
+    for (int index = 0; index < nChars; ++index) {
+      const FXTEXT_CHARPOS& cp = pCharPos[index];
+      if (!cp.m_bGlyphAdjust)
+        continue;
+      bool upright = 0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2];
+      if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3]) {
+        if (upright && 1 == cp.m_AdjustMatrix[3]) {
+          if (1 == scaleX)
+            scaleX = cp.m_AdjustMatrix[0];
+          else if (scaleX != cp.m_AdjustMatrix[0])
+            oneAtATime = true;
+        } else {
+          oneAtATime = true;
+        }
+      } else if (cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) {
+        oneAtATime = true;
+      } else {
+        useRSXform = true;
+      }
+    }
+    *oneAtATimePtr = oneAtATime;
+    *scaleXPtr = oneAtATime ? 1 : scaleX;
+    return oneAtATime ? false : useRSXform;
+  }
+
   bool DrawText(int nChars,
                 const FXTEXT_CHARPOS* pCharPos,
                 CFX_Font* pFont,
@@ -781,10 +815,18 @@
     if (m_debugDisable)
       return false;
     Dump(__func__);
+    float scaleX = 1;
+    bool oneAtATime = false;
+    bool hasRSX = HasRSX(nChars, pCharPos, &scaleX, &oneAtATime);
+    if (oneAtATime) {
+      Flush();
+      return false;
+    }
     int drawIndex = SkTMin(m_drawIndex, m_commands.count());
     if (Accumulator::kPath == m_type || drawIndex != m_commandIndex ||
         (Accumulator::kText == m_type &&
-         FontChanged(pFont, pMatrix, font_size, color))) {
+         (FontChanged(pFont, pMatrix, font_size, scaleX, color) ||
+          hasRSX == !m_rsxform.count()))) {
       Flush();
     }
     if (Accumulator::kText != m_type) {
@@ -792,6 +834,7 @@
       m_glyphs.setCount(0);
       m_pTypeFace = pFont->GetFace() ? pFont->GetDeviceCache() : nullptr;
       m_fontSize = font_size;
+      m_scaleX = scaleX;
       m_fillColor = color;
       m_drawMatrix = *pMatrix;
       m_drawIndex = m_commandIndex;
@@ -800,6 +843,9 @@
     int count = m_positions.count();
     m_positions.setCount(nChars + count);
     m_glyphs.setCount(nChars + count);
+    if (hasRSX) {
+      m_rsxform.setCount(nChars + count);
+    }
     SkScalar flip = m_fontSize < 0 ? -1 : 1;
     SkScalar vFlip = flip;
     if (pFont->IsVertical())
@@ -815,6 +861,23 @@
       for (int index = 0; index < nChars; ++index)
         m_positions[index + count].offset(delta.fX * flip, -delta.fY * flip);
     }
+    if (hasRSX) {
+      for (int index = 0; index < nChars; ++index) {
+        const FXTEXT_CHARPOS& cp = pCharPos[index];
+        SkRSXform* rsxform = &m_rsxform[index + count];
+        if (cp.m_bGlyphAdjust) {
+          rsxform->fSCos = cp.m_AdjustMatrix[0];
+          rsxform->fSSin = cp.m_AdjustMatrix[1];
+          rsxform->fTx = cp.m_AdjustMatrix[0] * m_positions[index].fX;
+          rsxform->fTy = cp.m_AdjustMatrix[1] * m_positions[index].fY;
+        } else {
+          rsxform->fSCos = 1;
+          rsxform->fSSin = 0;
+          rsxform->fTx = m_positions[index].fX;
+          rsxform->fTy = m_positions[index].fY;
+        }
+      }
+    }
     return true;
   }
 
@@ -829,6 +892,7 @@
     }
     skPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
     skPaint.setHinting(SkPaint::kNo_Hinting);
+    skPaint.setTextScaleX(m_scaleX);
     skPaint.setTextSize(SkTAbs(m_fontSize));
     skPaint.setSubpixelText(true);
     SkCanvas* skCanvas = m_pDriver->SkiaCanvas();
@@ -847,8 +911,13 @@
       printf("%lc", m_glyphs[i]);
     printf("\n");
 #endif
-    skCanvas->drawPosText(m_glyphs.begin(), m_glyphs.count() * 2,
-                          m_positions.begin(), skPaint);
+    if (m_rsxform.count()) {
+      skCanvas->drawTextRSXform(m_glyphs.begin(), m_glyphs.count() * 2,
+                                m_rsxform.begin(), nullptr, skPaint);
+    } else {
+      skCanvas->drawPosText(m_glyphs.begin(), m_glyphs.count() * 2,
+                            m_positions.begin(), skPaint);
+    }
     skCanvas->restore();
     m_drawIndex = INT_MAX;
     m_type = Accumulator::kNone;
@@ -1006,11 +1075,13 @@
   bool FontChanged(CFX_Font* pFont,
                    const CFX_Matrix* pMatrix,
                    float font_size,
+                   float scaleX,
                    uint32_t color) const {
     CFX_TypeFace* typeface =
         pFont->GetFace() ? pFont->GetDeviceCache() : nullptr;
     return typeface != m_pTypeFace || MatrixChanged(pMatrix, m_drawMatrix) ||
-           font_size != m_fontSize || color != m_fillColor;
+           font_size != m_fontSize || scaleX != m_scaleX ||
+           color != m_fillColor;
   }
 
   bool MatrixChanged(const CFX_Matrix* pMatrix,
@@ -1301,6 +1372,7 @@
   SkTArray<SkPath> m_clips;        // stack of clips that may be reused
   SkTDArray<Clip> m_commands;      // stack of clip-related commands
   SkTDArray<SkPoint> m_positions;  // accumulator for text positions
+  SkTDArray<SkRSXform> m_rsxform;  // accumulator for txt rotate/scale/translate
   SkTDArray<uint16_t> m_glyphs;    // accumulator for text glyphs
   SkPath m_skPath;                 // accumulator for path contours
   SkPath m_skEmptyPath;            // used as placehold in the clips array
@@ -1311,6 +1383,7 @@
   CFX_UnownedPtr<CFX_SkiaDeviceDriver> m_pDriver;
   CFX_UnownedPtr<CFX_TypeFace> m_pTypeFace;
   float m_fontSize;
+  float m_scaleX;
   uint32_t m_fillColor;
   uint32_t m_strokeColor;
   int m_blendType;
@@ -1487,11 +1560,22 @@
   positions.setCount(nChars);
   SkTDArray<uint16_t> glyphs;
   glyphs.setCount(nChars);
+  bool useRSXform = false;
+  bool oneAtATime = false;
   for (int index = 0; index < nChars; ++index) {
     const FXTEXT_CHARPOS& cp = pCharPos[index];
     positions[index] = {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip};
+    if (cp.m_bGlyphAdjust) {
+      useRSXform = true;
+      if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3] ||
+          cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) {
+        oneAtATime = true;
+      }
+    }
     glyphs[index] = static_cast<uint16_t>(cp.m_GlyphIndex);
   }
+  if (oneAtATime)
+    useRSXform = false;
 #if SHOW_TEXT_GLYPHS
   SkTDArray<SkUnichar> text;
   text.setCount(glyphs.count());
@@ -1503,7 +1587,58 @@
 #ifdef _SKIA_SUPPORT_PATHS_
   m_pBitmap->PreMultiply();
 #endif  // _SKIA_SUPPORT_PATHS_
-  m_pCanvas->drawPosText(glyphs.begin(), nChars * 2, positions.begin(), paint);
+  if (useRSXform) {
+    SkTDArray<SkRSXform> xforms;
+    xforms.setCount(nChars);
+    for (int index = 0; index < nChars; ++index) {
+      const FXTEXT_CHARPOS& cp = pCharPos[index];
+      SkRSXform* rsxform = &xforms[index];
+      if (cp.m_bGlyphAdjust) {
+        rsxform->fSCos = cp.m_AdjustMatrix[0];
+        rsxform->fSSin = cp.m_AdjustMatrix[1];
+        rsxform->fTx = cp.m_AdjustMatrix[0] * positions[index].fX;
+        rsxform->fTy = cp.m_AdjustMatrix[1] * positions[index].fY;
+      } else {
+        rsxform->fSCos = 1;
+        rsxform->fSSin = 0;
+        rsxform->fTx = positions[index].fX;
+        rsxform->fTy = positions[index].fY;
+      }
+    }
+    m_pCanvas->drawTextRSXform(glyphs.begin(), nChars * 2, xforms.begin(),
+                               nullptr, paint);
+  } else if (oneAtATime) {
+    for (int index = 0; index < nChars; ++index) {
+      const FXTEXT_CHARPOS& cp = pCharPos[index];
+      if (cp.m_bGlyphAdjust) {
+        if (0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2] &&
+            1 == cp.m_AdjustMatrix[3]) {
+          paint.setTextScaleX(cp.m_AdjustMatrix[0]);
+          m_pCanvas->drawText(&glyphs[index], 1, positions[index].fX,
+                              positions[index].fY, paint);
+          paint.setTextScaleX(1);
+        } else {
+          m_pCanvas->save();
+          SkMatrix adjust;
+          adjust.reset();
+          adjust.setScaleX(cp.m_AdjustMatrix[0]);
+          adjust.setSkewX(cp.m_AdjustMatrix[1]);
+          adjust.setSkewY(cp.m_AdjustMatrix[2]);
+          adjust.setScaleY(cp.m_AdjustMatrix[3]);
+          adjust.preTranslate(positions[index].fX, positions[index].fY);
+          m_pCanvas->concat(adjust);
+          m_pCanvas->drawText(&glyphs[index], 1, 0, 0, paint);
+          m_pCanvas->restore();
+        }
+      } else {
+        m_pCanvas->drawText(&glyphs[index], 1, positions[index].fX,
+                            positions[index].fY, paint);
+      }
+    }
+  } else {
+    m_pCanvas->drawPosText(glyphs.begin(), nChars * 2, positions.begin(),
+                           paint);
+  }
   m_pCanvas->restore();
 
   return true;