Introduce SkGlyphPositionRoundingSpec.

SkGlyphPositionRoundingSpec encapsulates the information needed for rounding
positions correctly. This allows test to avoid using an SkStrike in future
unit tests.

This is part of a major rewrite:
https://skia-review.googlesource.com/c/skia/+/238196

Change-Id: I18dd6fe394df0fcbda492cefe2484c9bf9bb40d1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/247338
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/core/SkGlyphRunPainter.cpp b/src/core/SkGlyphRunPainter.cpp
index d1366c2..c0178f3 100644
--- a/src/core/SkGlyphRunPainter.cpp
+++ b/src/core/SkGlyphRunPainter.cpp
@@ -34,27 +34,7 @@
 #include "src/core/SkStrikeSpec.h"
 #include "src/core/SkTraceEvent.h"
 
-#include <limits.h>
-
-// -- SkGlyphCacheCommon ---------------------------------------------------------------------------
-SkVector SkStrikeCommon::PixelRounding(bool isSubpixel, SkAxisAlignment axisAlignment) {
-    if (!isSubpixel) {
-        return {SK_ScalarHalf, SK_ScalarHalf};
-    } else {
-        static constexpr SkScalar kSubpixelRounding = SkFixedToScalar(SkGlyph::kSubpixelRound);
-        switch (axisAlignment) {
-            case kX_SkAxisAlignment:
-                return {kSubpixelRounding, SK_ScalarHalf};
-            case kY_SkAxisAlignment:
-                return {SK_ScalarHalf, kSubpixelRounding};
-            case kNone_SkAxisAlignment:
-                return {kSubpixelRounding, kSubpixelRounding};
-        }
-    }
-
-    // Some compilers need this.
-    return {0, 0};
-}
+#include <climits>
 
 // -- SkGlyphRunListPainter ------------------------------------------------------------------------
 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
@@ -108,7 +88,7 @@
 }
 
 SkSpan<const SkPackedGlyphID> SkGlyphRunListPainter::DeviceSpacePackedGlyphIDs(
-        SkStrikeForGPU* strike,
+        const SkGlyphPositionRoundingSpec& roundingSpec,
         const SkMatrix& viewMatrix,
         const SkPoint& origin,
         int n,
@@ -119,11 +99,11 @@
     // Add rounding and origin.
     SkMatrix matrix = viewMatrix;
     matrix.preTranslate(origin.x(), origin.y());
-    SkPoint rounding = strike->rounding();
+    SkPoint rounding = roundingSpec.halfAxisSampleFreq;
     matrix.postTranslate(rounding.x(), rounding.y());
     matrix.mapPoints(mappedPositions, positions, n);
 
-    SkIPoint mask = strike->subpixelMask();
+    SkIPoint mask = roundingSpec.ignorePositionMask;
 
     for (int i = 0; i < n; i++) {
         SkFixed subX = SkScalarToFixed(mappedPositions[i].x()) & mask.x(),
@@ -214,7 +194,7 @@
             auto strike = strikeSpec.findOrCreateExclusiveStrike();
 
             auto packedGlyphIDs = DeviceSpacePackedGlyphIDs(
-                    strike.get(),
+                    strike->roundingSpec(),
                     deviceMatrix,
                     origin,
                     runSize,
@@ -510,7 +490,7 @@
             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
             auto packedGlyphIDs = DeviceSpacePackedGlyphIDs(
-                    strike.get(),
+                    strike->roundingSpec(),
                     viewMatrix,
                     origin,
                     glyphRun.runSize(),
@@ -977,3 +957,34 @@
         fPainter->fARGBPositions.shrink_to_fit();
     }
 }
+
+SkVector SkGlyphPositionRoundingSpec::HalfAxisSampleFreq(bool isSubpixel, SkAxisAlignment axisAlignment) {
+    if (!isSubpixel) {
+        return {SK_ScalarHalf, SK_ScalarHalf};
+    } else {
+        static constexpr SkScalar kSubpixelRounding = SkFixedToScalar(SkGlyph::kSubpixelRound);
+        switch (axisAlignment) {
+            case kX_SkAxisAlignment:
+                return {kSubpixelRounding, SK_ScalarHalf};
+            case kY_SkAxisAlignment:
+                return {SK_ScalarHalf, kSubpixelRounding};
+            case kNone_SkAxisAlignment:
+                return {kSubpixelRounding, kSubpixelRounding};
+        }
+    }
+
+    // Some compilers need this.
+    return {0, 0};
+}
+
+SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionMask(
+        bool isSubpixel, SkAxisAlignment axisAlignment) {
+    return SkIPoint::Make((!isSubpixel || axisAlignment == kY_SkAxisAlignment) ? 0 : ~0,
+                          (!isSubpixel || axisAlignment == kX_SkAxisAlignment) ? 0 : ~0);
+}
+
+SkGlyphPositionRoundingSpec::SkGlyphPositionRoundingSpec(bool isSubpixel,
+                                                         SkAxisAlignment axisAlignment)
+        : halfAxisSampleFreq{HalfAxisSampleFreq(isSubpixel, axisAlignment)}
+        , ignorePositionMask{IgnorePositionMask(isSubpixel, axisAlignment)} {
+}
diff --git a/src/core/SkGlyphRunPainter.h b/src/core/SkGlyphRunPainter.h
index 48812c0..d16c290 100644
--- a/src/core/SkGlyphRunPainter.h
+++ b/src/core/SkGlyphRunPainter.h
@@ -23,10 +23,25 @@
 class SkGlyphRunPainterInterface;
 class SkStrikeSpec;
 
+// round and ignorePositionMask are used to calculate the subpixel position of a glyph.
+// The per component (x or y) calculation is:
+//
+//   subpixelOffset = (floor((viewportPosition + rounding) & mask) >> 14) & 3
+//
+// where mask is either 0 or ~0, and rounding is either
+// 1/2 for non-subpixel or 1/8 for subpixel.
+struct SkGlyphPositionRoundingSpec {
+    SkGlyphPositionRoundingSpec(bool isSubpixel, SkAxisAlignment axisAlignment);
+    const SkVector halfAxisSampleFreq;
+    const SkIPoint ignorePositionMask;
+
+private:
+    static SkVector HalfAxisSampleFreq(bool isSubpixel, SkAxisAlignment axisAlignment);
+    static SkIPoint IgnorePositionMask(bool isSubpixel, SkAxisAlignment axisAlignment);
+};
+
 class SkStrikeCommon {
 public:
-    static SkVector PixelRounding(bool isSubpixel, SkAxisAlignment axisAlignment);
-
     // An atlas consists of plots, and plots hold glyphs. The minimum a plot can be is 256x256.
     // This means that the maximum size a glyph can be is 256x256.
     static constexpr uint16_t kSkSideTooBigForAtlas = 256;
@@ -102,7 +117,7 @@
                              SkGlyphRunPainterInterface* process);
 
     static SkSpan<const SkPackedGlyphID> DeviceSpacePackedGlyphIDs(
-            SkStrikeForGPU* strike,
+            const SkGlyphPositionRoundingSpec& roundingSpec,
             const SkMatrix& viewMatrix,
             const SkPoint& origin,
             int n,
diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp
index d59419f..3f6a8ac 100644
--- a/src/core/SkRemoteGlyphCache.cpp
+++ b/src/core/SkRemoteGlyphCache.cpp
@@ -197,19 +197,14 @@
     void writePendingGlyphs(Serializer* serializer);
     SkDiscardableHandleId discardableHandleId() const { return fDiscardableHandleId; }
 
-    bool isSubpixel() const { return fIsSubpixel; }
-
     const SkDescriptor& getDescriptor() const override {
         return *fDescriptor.getDesc();
     }
 
     void setTypefaceAndEffects(const SkTypeface* typeface, SkScalerContextEffects effects);
 
-    SkVector rounding() const override;
-
-    SkIPoint subpixelMask() const override {
-        return SkIPoint::Make((!fIsSubpixel || fAxisAlignment == kY_SkAxisAlignment) ? 0 : ~0u,
-                              (!fIsSubpixel || fAxisAlignment == kX_SkAxisAlignment) ? 0 : ~0u);
+    const SkGlyphPositionRoundingSpec& roundingSpec() const override {
+        return fRoundingSpec;
     }
 
     SkSpan<const SkGlyphPos>
@@ -245,9 +240,7 @@
 
     const SkDiscardableHandleId fDiscardableHandleId;
 
-    // Values saved from the initial context.
-    const bool fIsSubpixel;
-    const SkAxisAlignment fAxisAlignment;
+    const SkGlyphPositionRoundingSpec fRoundingSpec;
 
     // The context built using fDescriptor
     std::unique_ptr<SkScalerContext> fContext;
@@ -282,8 +275,7 @@
         uint32_t discardableHandleId)
         : fDescriptor{descriptor}
         , fDiscardableHandleId(discardableHandleId)
-        , fIsSubpixel{context->isSubpixel()}
-        , fAxisAlignment{context->computeAxisAlignmentForHText()}
+        , fRoundingSpec{context->isSubpixel(), context->computeAxisAlignmentForHText()}
         // N.B. context must come last because it is used above.
         , fContext{std::move(context)} {
     SkASSERT(fDescriptor.getDesc() != nullptr);
@@ -650,10 +642,6 @@
     fEffects = effects;
 }
 
-SkVector SkStrikeServer::RemoteStrike::rounding() const {
-    return SkStrikeCommon::PixelRounding(fIsSubpixel, fAxisAlignment);
-}
-
 void SkStrikeServer::RemoteStrike::writeGlyphPath(const SkPackedGlyphID& glyphID,
                                                   Serializer* serializer) const {
     SkPath path;
diff --git a/src/core/SkStrike.cpp b/src/core/SkStrike.cpp
index 9c553ff..d8d4f57 100644
--- a/src/core/SkStrike.cpp
+++ b/src/core/SkStrike.cpp
@@ -20,12 +20,11 @@
     const SkDescriptor& desc,
     std::unique_ptr<SkScalerContext> scaler,
     const SkFontMetrics& fontMetrics)
-    : fDesc{desc}
-    , fScalerContext{std::move(scaler)}
-    , fFontMetrics{fontMetrics}
-    , fIsSubpixel{fScalerContext->isSubpixel()}
-    , fAxisAlignment{fScalerContext->computeAxisAlignmentForHText()}
-{
+        : fDesc{desc}
+        , fScalerContext{std::move(scaler)}
+        , fFontMetrics{fontMetrics}
+        , fRoundingSpec{fScalerContext->isSubpixel(),
+                        fScalerContext->computeAxisAlignmentForHText()} {
     SkASSERT(fScalerContext != nullptr);
     fMemoryUsed = sizeof(*this);
 }
@@ -59,10 +58,9 @@
 }
 
 SkGlyph* SkStrike::glyph(SkGlyphID glyphID, SkPoint position) {
-    const SkFixed maskX = (!fIsSubpixel || fAxisAlignment == kY_SkAxisAlignment) ? 0 : ~0;
-    const SkFixed maskY = (!fIsSubpixel || fAxisAlignment == kX_SkAxisAlignment) ? 0 : ~0;
-    SkFixed subX = SkScalarToFixed(position.x()) & maskX,
-            subY = SkScalarToFixed(position.y()) & maskY;
+    SkIPoint mask = fRoundingSpec.ignorePositionMask;
+    SkFixed subX = SkScalarToFixed(position.x()) & mask.x(),
+            subY = SkScalarToFixed(position.y()) & mask.y();
     return this->glyph(SkPackedGlyphID{glyphID, subX, subY});
 }
 
@@ -160,10 +158,6 @@
     return nullptr;
 }
 
-SkVector SkStrike::rounding() const {
-    return SkStrikeCommon::PixelRounding(fIsSubpixel, fAxisAlignment);
-}
-
 SkSpan<const SkGlyph*> SkStrike::metrics(SkSpan<const SkGlyphID> glyphIDs,
                                          const SkGlyph* results[]) {
     return this->internalPrepare(glyphIDs, kMetricsOnly, results);
diff --git a/src/core/SkStrike.h b/src/core/SkStrike.h
index 1c67986..dfb2b59 100644
--- a/src/core/SkStrike.h
+++ b/src/core/SkStrike.h
@@ -97,15 +97,8 @@
         return fScalerContext->getMaskFormat();
     }
 
-    bool isSubpixel() const {
-        return fIsSubpixel;
-    }
-
-    SkVector rounding() const override;
-
-    SkIPoint subpixelMask() const override {
-        return SkIPoint::Make((!fIsSubpixel || fAxisAlignment == kY_SkAxisAlignment) ? 0 : ~0,
-                              (!fIsSubpixel || fAxisAlignment == kX_SkAxisAlignment) ? 0 : ~0);
+    const SkGlyphPositionRoundingSpec& roundingSpec() const override {
+        return fRoundingSpec;
     }
 
     const SkDescriptor& getDescriptor() const override;
@@ -203,8 +196,7 @@
     // Tracks (approx) how much ram is tied-up in this strike.
     size_t                  fMemoryUsed;
 
-    const bool              fIsSubpixel;
-    const SkAxisAlignment   fAxisAlignment;
+    const SkGlyphPositionRoundingSpec fRoundingSpec;
 };
 
 #endif  // SkStrike_DEFINED
diff --git a/src/core/SkStrikeCache.cpp b/src/core/SkStrikeCache.cpp
index 2d070b5..1bcbdd4 100644
--- a/src/core/SkStrikeCache.cpp
+++ b/src/core/SkStrikeCache.cpp
@@ -28,12 +28,8 @@
             , fStrike{desc, std::move(scaler), metrics}
             , fPinner{std::move(pinner)} {}
 
-    SkVector rounding() const override {
-        return fStrike.rounding();
-    }
-
-    SkIPoint subpixelMask() const override {
-        return fStrike.subpixelMask();
+    const SkGlyphPositionRoundingSpec& roundingSpec() const override {
+        return fStrike.roundingSpec();
     }
 
     SkSpan<const SkGlyphPos>
diff --git a/src/core/SkStrikeForGPU.h b/src/core/SkStrikeForGPU.h
index 749390a..a72ea35 100644
--- a/src/core/SkStrikeForGPU.h
+++ b/src/core/SkStrikeForGPU.h
@@ -21,6 +21,7 @@
 class SkMaskFilter;
 class SkPathEffect;
 class SkTypeface;
+struct SkGlyphPositionRoundingSpec;
 struct SkScalerContextEffects;
 
 struct SkGlyphPos {
@@ -52,15 +53,7 @@
                                  int maxDimension,
                                  SkGlyphPos results[]) = 0;
 
-    // rounding() and subpixelMask are used to calculate the subpixel position of a glyph.
-    // The per component (x or y) calculation is:
-    //
-    //   subpixelOffset = (floor((viewportPosition + rounding) & mask) >> 14) & 3
-    //
-    // where mask is either 0 or ~0, and rounding is either
-    // 1/2 for non-subpixel or 1/8 for subpixel.
-    virtual SkVector rounding() const = 0;
-    virtual SkIPoint subpixelMask() const = 0;
+    virtual const SkGlyphPositionRoundingSpec& roundingSpec() const = 0;
 
     // Used with SkScopedStrikeForGPU to take action at the end of a scope.
     virtual void onAboutToExitScope() = 0;