Title: [293032] trunk/Source/WebCore
Revision
293032
Author
[email protected]
Date
2022-04-19 13:08:37 -0700 (Tue, 19 Apr 2022)

Log Message

[Cocoa] Add explanatory comments in fillVectorWithVerticalGlyphPositions() and simplify it somewhat
https://bugs.webkit.org/show_bug.cgi?id=239480

Reviewed by Alan Bujtas.

This adds a lot of explanatory text in fillVectorWithVerticalGlyphPositions(). It also rewrites some of it to
call shared functions (computeOverallTextMatrixInternal() and computeVerticalTextMatrixInternal()) instead of
reimplementing the contents of those functions.

The last thing this patch does is it stops adding in CTFontGetMatrix() to the text matrix, because:
1. It's always the identity, because we never create a font with one of these things
2. Even if it wasn't the identity, Core Text already applies it itself, so if we apply it, it would be double
       applied.

No new tests because there is no behavior change.

* platform/graphics/FontCascade.h:
* platform/graphics/coretext/FontCascadeCoreText.cpp:
(WebCore::computeBaseOverallTextMatrix):
(WebCore::computeOverallTextMatrix):
(WebCore::computeBaseVerticalTextMatrix):
(WebCore::computeVerticalTextMatrix):
(WebCore::fillVectorWithVerticalGlyphPositions):
(WebCore::showGlyphsWithAdvances):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (293031 => 293032)


--- trunk/Source/WebCore/ChangeLog	2022-04-19 19:24:15 UTC (rev 293031)
+++ trunk/Source/WebCore/ChangeLog	2022-04-19 20:08:37 UTC (rev 293032)
@@ -1,3 +1,30 @@
+2022-04-19  Myles C. Maxfield  <[email protected]>
+
+        [Cocoa] Add explanatory comments in fillVectorWithVerticalGlyphPositions() and simplify it somewhat
+        https://bugs.webkit.org/show_bug.cgi?id=239480
+
+        Reviewed by Alan Bujtas.
+
+        This adds a lot of explanatory text in fillVectorWithVerticalGlyphPositions(). It also rewrites some of it to
+        call shared functions (computeOverallTextMatrixInternal() and computeVerticalTextMatrixInternal()) instead of
+        reimplementing the contents of those functions.
+
+        The last thing this patch does is it stops adding in CTFontGetMatrix() to the text matrix, because:
+        1. It's always the identity, because we never create a font with one of these things
+        2. Even if it wasn't the identity, Core Text already applies it itself, so if we apply it, it would be double
+               applied.
+
+        No new tests because there is no behavior change.
+
+        * platform/graphics/FontCascade.h:
+        * platform/graphics/coretext/FontCascadeCoreText.cpp:
+        (WebCore::computeBaseOverallTextMatrix):
+        (WebCore::computeOverallTextMatrix):
+        (WebCore::computeBaseVerticalTextMatrix):
+        (WebCore::computeVerticalTextMatrix):
+        (WebCore::fillVectorWithVerticalGlyphPositions):
+        (WebCore::showGlyphsWithAdvances):
+
 2022-04-19  Wenson Hsieh  <[email protected]>
 
         [iOS] Text selection flickers when inserting text using dictation

Modified: trunk/Source/WebCore/platform/graphics/FontCascade.h (293031 => 293032)


--- trunk/Source/WebCore/platform/graphics/FontCascade.h	2022-04-19 19:24:15 UTC (rev 293031)
+++ trunk/Source/WebCore/platform/graphics/FontCascade.h	2022-04-19 20:08:37 UTC (rev 293032)
@@ -29,6 +29,7 @@
 #include "FontCascadeDescription.h"
 #include "FontCascadeFonts.h"
 #include "Path.h"
+#include <optional>
 #include <wtf/HashSet.h>
 #include <wtf/WeakPtr.h>
 #include <wtf/unicode/CharacterNames.h>
@@ -91,7 +92,9 @@
 };
 
 #if USE(CORE_TEXT)
+AffineTransform computeBaseOverallTextMatrix(const std::optional<AffineTransform>& syntheticOblique);
 AffineTransform computeOverallTextMatrix(const Font&);
+AffineTransform computeBaseVerticalTextMatrix(const AffineTransform& previousTextMatrix);
 AffineTransform computeVerticalTextMatrix(const Font&, const AffineTransform& previousTextMatrix);
 #endif
 

Modified: trunk/Source/WebCore/platform/graphics/coretext/FontCascadeCoreText.cpp (293031 => 293032)


--- trunk/Source/WebCore/platform/graphics/coretext/FontCascadeCoreText.cpp	2022-04-19 19:24:15 UTC (rev 293031)
+++ trunk/Source/WebCore/platform/graphics/coretext/FontCascadeCoreText.cpp	2022-04-19 20:08:37 UTC (rev 293032)
@@ -60,44 +60,57 @@
     return result;
 }
 
-AffineTransform computeOverallTextMatrix(const Font& font)
+AffineTransform computeBaseOverallTextMatrix(const std::optional<AffineTransform>& syntheticOblique)
 {
-    auto& platformData = font.platformData();
     AffineTransform result;
-    if (!platformData.isColorBitmapFont())
-        result = CTFontGetMatrix(platformData.ctFont());
+
+    // This is a Y-flip, because text's coordinate system is increasing-Y-goes-up,
+    // but WebKit's coordinate system is increasing-Y-goes-down.
     result.setB(-result.b());
     result.setD(-result.d());
+
+    if (syntheticOblique)
+        result = *syntheticOblique * result;
+
+    return result;
+}
+
+AffineTransform computeOverallTextMatrix(const Font& font)
+{
+    std::optional<AffineTransform> syntheticOblique;
+    auto& platformData = font.platformData();
     if (platformData.syntheticOblique()) {
         float obliqueSkew = tanf(deg2rad(FontCascade::syntheticObliqueAngle()));
         if (platformData.orientation() == FontOrientation::Vertical) {
             if (font.isTextOrientationFallback())
-                result = AffineTransform(1, obliqueSkew, 0, 1, 0, 0) * result;
+                syntheticOblique = AffineTransform(1, obliqueSkew, 0, 1, 0, 0);
             else
-                result = AffineTransform(1, -obliqueSkew, 0, 1, 0, 0) * result;
+                syntheticOblique = AffineTransform(1, -obliqueSkew, 0, 1, 0, 0);
         } else
-            result = AffineTransform(1, 0, -obliqueSkew, 1, 0, 0) * result;
+            syntheticOblique = AffineTransform(1, 0, -obliqueSkew, 1, 0, 0);
     }
 
-    // We're emulating the behavior of CGContextSetTextPosition() by adding constant amounts to each glyph's position
-    // (see fillVectorWithHorizontalGlyphPositions() and fillVectorWithVerticalGlyphPositions()).
-    // CGContextSetTextPosition() has the side effect of clobbering the E and F fields of the text matrix,
-    // so we do that explicitly here.
-    result.setE(0);
-    result.setF(0);
-    return result;
+    return computeBaseOverallTextMatrix(syntheticOblique);
 }
 
-AffineTransform computeVerticalTextMatrix(const Font& font, const AffineTransform& previousTextMatrix)
+AffineTransform computeBaseVerticalTextMatrix(const AffineTransform& previousTextMatrix)
 {
-    ASSERT_UNUSED(font, font.platformData().orientation() == FontOrientation::Vertical);
     // The translation here ("e" and "f" fields) are irrelevant, because
     // this matrix is inverted in fillVectorWithVerticalGlyphPositions to place the glyphs in the CTM's coordinate system.
     // All we're trying to do here is rotate the text matrix so glyphs appear visually upright.
     // We have to include the previous text matrix because it includes things like synthetic oblique.
+    //
+    // Because this is a left-multiply, we're taking the points from user coordinates, which are increasing-Y-goes-down,
+    // and we're rotating the points to the left in that coordinate system, to put them physically upright.
     return rotateLeftTransform() * previousTextMatrix;
 }
 
+AffineTransform computeVerticalTextMatrix(const Font& font, const AffineTransform& previousTextMatrix)
+{
+    ASSERT_UNUSED(font, font.platformData().orientation() == FontOrientation::Vertical);
+    return computeBaseVerticalTextMatrix(previousTextMatrix);
+}
+
 #if !PLATFORM(WIN)
 
 // Confusingly, even when CGFontRenderingGetFontSmoothingDisabled() returns true, CGContextSetShouldSmoothFonts() still impacts text
@@ -138,15 +151,132 @@
     }
 }
 
-static void fillVectorWithVerticalGlyphPositions(Vector<CGPoint, 256>& positions, CGContextRef context, const CGSize* translations, const CGSize* advances, unsigned count, const FloatPoint& point, float ascentDelta)
+static void fillVectorWithVerticalGlyphPositions(Vector<CGPoint, 256>& positions, const CGSize translations[], const CGSize advances[], unsigned count, const FloatPoint& point, float ascentDelta, CGAffineTransform textMatrix)
 {
-    // Keep this in sync as the inverse of `DrawGlyphsRecorder::recordDrawGlyphs`.
-    CGAffineTransform transform = CGAffineTransformInvert(CGContextGetTextMatrix(context));
+    // Keep this function in sync as the inverse of `DrawGlyphsRecorder::recordDrawGlyphs`.
 
+    // It's important to realize we're dealing with 4 coordinate systems here:
+    // 1. Physical coordinate system. This is what the user sees.
+    // 2. User coordinate system. This is the coordinate system of just the CTM. For vertical text, this is just like the normal WebKit increasing-Y-down
+    //        coordinate system, except we're rotated right, so logical right is physical down. (We do this so logical inline progression proceeds in the
+    //        logically increasing-X dimension, just as it would if we weren't doing vertical stuff.)
+    // 3. Text coordinate system. This is the coordinate of the text matrix concatenated with the CTM. For vertical text, this is rotated such that
+    //        increasing Y goes physically up, and increasing X goes physically right. The control points in font contours are authored according to this
+    //        increasing-Y-up coordinate system.
+    // 4. Synthetic-oblique-less text coordinate system. This would be identical to the text coordinate system if synthetic oblique was not in effect. This
+    //        is useful because, when we're moving glyphs around, we usually don't want to consider synthetic oblique. Instead, synthetic oblique is just
+    //        a rasterization-time effect, and not used for glyph positioning/layout.
+    //        FIXME: Does this mean that synthetic oblique should always be applied on the result of rotateLeftTransform() in computeVerticalTextMatrix(),
+    //        rather than the other way around?
+
+    // Imagine an vertical upright glyph:
+    // +--------------------------+
+    // |      ___       ___       |
+    // |      \  \     /  /       |
+    // |       \  \   /  /        |
+    // |        \  \ /  /         |
+    // |         \  V  /          |
+    // |          \   /           |
+    // |          |   |           |
+    // |          |   |           |
+    // |          |   |           |
+    // |          |   |           |
+    // |          |___|           |
+    // |                          |
+    // +--------------------------+
+    //
+    // The ideographic baseline lies in the center of the glyph, and the alphabetic baseline lies to the left of it:
+    //     |        |
+    // +---|--------|-------------+
+    // |   |  ___   |   ___       |
+    // |   |  \  \  |  /  /       |
+    // |   |   \  \ | /  /        |
+    // |   |    \  \|/  /         |
+    // |   |     \  |  /          |
+    // |   |      \ | /           |
+    // |   |      | | |           |
+    // |   |      | | |           |
+    // |   |      | | |           |
+    // |   |      | | |           |
+    // |   |      |_|_|           |
+    // |   |        |             |
+    // +---|--------|-------------+
+    //     |        | <== ideographic baseline
+    //     | <== alphabetic baseline
+    //
+    // The glyph itself has a local origin, which is the position sent to Core Text. The control points of the contours are defined relative to this point.
+    // +--------------------------+
+    // |      ___       ___       |
+    // |      \  \     /  /       |
+    // |       \  \   /  /        |
+    // |        \  \ /  /         |
+    // |         \  V  /          |
+    // |          \   /           |
+    // |          |   |           |
+    // |          |   |           |
+    // |          |   |           |
+    // * <= here  |   |           |
+    // |          |___|           |
+    // |                          |
+    // +--------------------------+
+    //
+    // Now, for horizontal text, we can do the simple thing of just:
+    // 1. Place the pen at a position. Record this position as the local origin of the first glyph
+    // 2. Move the pen according to the glyph's advance
+    // 3. Record a new position as the local origin of the next glyph
+    // 4. Go to 2
+    // However, for vertical text, we can't get away with this because the glyph origins are not on the baseline.
+    // This is what the "vertical translation for a glyph" is. It contains this vector:
+    // +---A--------B-------------+
+    // |           /              |
+    // |          /               |
+    // |        --                |
+    // |       /                  |
+    // |      /                   |
+    // |    --                    |
+    // |   /                      |
+    // |  /                       |
+    // ||_                        |
+    // C                          |
+    // |                          |
+    // |                          |
+    // +--------------------------+
+    // It points from the pen position on the ideographic baseline to the glyph's local origin. This is (usually) physically
+    // down-and-to-the-left. Core Text gives us these vectors in the text coordinate system, and so therefore these vectors (usually) have
+    // both X and Y components negative.
+
+    // The goal of this function is to produce glyph origins in the text coordinate system, because that's what Core Text expects. The "advances"
+    // and "point" parameters to this function are in the user coordinate system. The "translations" parameter is in the "synthetic-oblique-less
+    // text coordinate system."
+
+    // CGContextGetTextMatrix() transforms points from text coordinates to user coordinates. However, we're trying to produce text coordinates from
+    // user coordinates, so we invert it.
+    CGAffineTransform transform = CGAffineTransformInvert(textMatrix);
+
+    // Because the "vertical translation for a glyph" vector starts at the ideographic baseline (the point B in the above diagram), we have to
+    // adjust the pen position to start there. WebKit's text routines start out using the alphabetic baseline (point A in the diagram above) so we
+    // adjust the start position here, which has the effect of shifting the whole run altogether.
+    //
+    // ascentDelta is (usually) a negative number, and represents the distance between the ideographic baseline to the alphabetic baseline.
+    // In user coordinates, we want to adjust the Y component to make a horizontal physical change. And, because the user coordinate system is
+    // logically increasing-Y-down, we add the value, which is negative, to move us logically up, which is physically to the right. Now our position
+    // is at the point labeled B in the above diagram, in user coordinates.
     auto position = CGPointMake(point.x(), point.y() + ascentDelta);
+
     for (unsigned i = 0; i < count; ++i) {
-        CGSize translation = CGSizeApplyAffineTransform(translations[i], rotateLeftTransform());
-        positions[i] = CGPointApplyAffineTransform(CGPointMake(position.x - translation.width, position.y + translation.height), transform);
+        // The "translations" parameter is in the "synthetic-oblique-less text coordinate system" and we want to add it to the position in the user
+        // coordinate system. Luckily, the text matrix (or, at least the version of the text matrix that doesn't include synthetic oblique) does exactly
+        // this. So, we just create the synthetic-oblique-less text matrix, and run the translation through it. This gives us the translation in user
+        // coordinates.
+        auto translation = CGSizeApplyAffineTransform(translations[i], computeBaseVerticalTextMatrix(computeBaseOverallTextMatrix(std::nullopt)));
+
+        // Now we can add the position in user coordinates with the translation in user coordinates.
+        auto positionInUserCoordinates = CGPointMake(position.x + translation.width, position.y + translation.height);
+
+        // And then put it back in font coordinates for submission to Core Text. Yay!
+        positions[i] = CGPointApplyAffineTransform(positionInUserCoordinates, transform);
+
+        // Advance the position to the next position in user coordinates. Both the advances and position are in user coordinates.
         position.x += advances[i].width;
         position.y += advances[i].height;
     }
@@ -166,7 +296,7 @@
         CTFontGetVerticalTranslationsForGlyphs(platformData.ctFont(), glyphs, translations.data(), count);
 
         auto ascentDelta = font.fontMetrics().floatAscent(IdeographicBaseline) - font.fontMetrics().floatAscent();
-        fillVectorWithVerticalGlyphPositions(positions, context, translations.data(), advances, count, point, ascentDelta);
+        fillVectorWithVerticalGlyphPositions(positions, translations.data(), advances, count, point, ascentDelta, CGContextGetTextMatrix(context));
         CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context);
     } else {
         fillVectorWithHorizontalGlyphPositions(positions, context, advances, count, point);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to