Changeset 237845 in webkit


Ignore:
Timestamp:
Nov 5, 2018 8:35:50 PM (5 years ago)
Author:
mmaxfield@apple.com
Message:

Cache glyph paths and share underline skipping code between all the ports
https://bugs.webkit.org/show_bug.cgi?id=191239

Reviewed by Alex Christensen.

PerformanceTests:

Measures the performance of drawing a whole lot of underlines

  • Layout/underline.html: Added.

Source/WebCore:

I was hoping that caching the glyph paths was going to be a performance progression,
but it turns out that the additional overhead of WebCore::Path compensated for it.
In total, the performance is the same (my testing says that this patch is a 1%
progression, but that's within the noise).

Because the ink skipping logic is now shared among all ports, Windows now gets it for
free.

Test: PerformanceTests/Layout/underline.html

  • platform/graphics/Font.cpp:

(WebCore::Font::pathForGlyph const):

  • platform/graphics/Font.h:
  • platform/graphics/FontCascade.cpp:

(WebCore::computeUnderlineType):
(WebCore::GlyphIterationState::GlyphIterationState):
(WebCore::findIntersectionPoint):
(WebCore::updateX):
(WebCore::findPathIntersections):
(WebCore::GlyphToPathTranslator::GlyphToPathTranslator):
(WebCore::GlyphToPathTranslator::containsMorePaths):
(WebCore::GlyphToPathTranslator::path):
(WebCore::GlyphToPathTranslator::extents):
(WebCore::GlyphToPathTranslator::underlineType):
(WebCore::GlyphToPathTranslator::advance):
(WebCore::FontCascade::dashesForIntersectionsWithRect const):

  • platform/graphics/FontCascade.h:
  • platform/graphics/GlyphMetricsMap.h:

(WebCore::GlyphMetricsMap::existingMetricsForGlyph):
(WebCore::GlyphMetricsMap::GlyphMetricsPage::existingMetricsForGlyph const):
(WebCore::GlyphMetricsMap<std::optional<Path>>::unknownMetrics):

  • platform/graphics/TextRun.h:
  • platform/graphics/cairo/FontCairo.cpp:

(WebCore::Font::platformPathForGlyph const):
(WebCore::GlyphIterationState::GlyphIterationState): Deleted.
(WebCore::findIntersectionPoint): Deleted.
(WebCore::updateX): Deleted.
(WebCore::findPathIntersections): Deleted.
(): Deleted.
(WebCore::CairoGlyphToPathTranslator::path): Deleted.
(WebCore::CairoGlyphToPathTranslator::extents): Deleted.
(WebCore::CairoGlyphToPathTranslator::underlineType): Deleted.
(WebCore::CairoGlyphToPathTranslator::advance): Deleted.
(WebCore::FontCascade::dashesForIntersectionsWithRect const): Deleted.

  • platform/graphics/cocoa/FontCascadeCocoa.mm:

(WebCore::GlyphIterationState::GlyphIterationState): Deleted.
(WebCore::findIntersectionPoint): Deleted.
(WebCore::updateX): Deleted.
(WebCore::findPathIntersections): Deleted.
(): Deleted.
(WebCore::MacGlyphToPathTranslator::path): Deleted.
(WebCore::MacGlyphToPathTranslator::extents): Deleted.
(WebCore::MacGlyphToPathTranslator::underlineType): Deleted.
(WebCore::MacGlyphToPathTranslator::advance): Deleted.
(WebCore::FontCascade::dashesForIntersectionsWithRect const): Deleted.

  • platform/graphics/cocoa/FontCocoa.mm:

(WebCore::Font::platformPathForGlyph const):

  • rendering/TextDecorationPainter.cpp:

(WebCore::drawSkipInkUnderline):
(WebCore::TextDecorationPainter::paintTextDecoration):

Source/WTF:

Remove CSS3_TEXT_DECORATION_SKIP_INK. It's now interoperable and part of the Web Platform.

  • wtf/Platform.h:
Location:
trunk
Files:
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/PerformanceTests/ChangeLog

    r237442 r237845  
     12018-11-05  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Cache glyph paths and share underline skipping code between all the ports
     4        https://bugs.webkit.org/show_bug.cgi?id=191239
     5
     6        Reviewed by Alex Christensen.
     7
     8        Measures the performance of drawing a whole lot of underlines
     9
     10        * Layout/underline.html: Added.
     11
    1122018-10-25  Saam Barati  <sbarati@apple.com>
    213
  • trunk/Source/WTF/ChangeLog

    r237803 r237845  
     12018-11-05  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Cache glyph paths and share underline skipping code between all the ports
     4        https://bugs.webkit.org/show_bug.cgi?id=191239
     5
     6        Reviewed by Alex Christensen.
     7
     8        Remove CSS3_TEXT_DECORATION_SKIP_INK. It's now interoperable and part of the Web Platform.
     9
     10        * wtf/Platform.h:
     11
    1122018-11-05  Dominik Infuehr  <dinfuehr@igalia.com>
    213
  • trunk/Source/WTF/wtf/Platform.h

    r237803 r237845  
    12641264#undef ENABLE_OPENTYPE_VERTICAL
    12651265#define ENABLE_OPENTYPE_VERTICAL 1
    1266 #define ENABLE_CSS3_TEXT_DECORATION_SKIP_INK 1
    1267 #endif
    1268 
    1269 #if PLATFORM(COCOA)
    1270 #define ENABLE_CSS3_TEXT_DECORATION_SKIP_INK 1
    12711266#endif
    12721267
  • trunk/Source/WebCore/ChangeLog

    r237844 r237845  
     12018-11-05  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Cache glyph paths and share underline skipping code between all the ports
     4        https://bugs.webkit.org/show_bug.cgi?id=191239
     5
     6        Reviewed by Alex Christensen.
     7
     8        I was hoping that caching the glyph paths was going to be a performance progression,
     9        but it turns out that the additional overhead of WebCore::Path compensated for it.
     10        In total, the performance is the same (my testing says that this patch is a 1%
     11        progression, but that's within the noise).
     12
     13        Because the ink skipping logic is now shared among all ports, Windows now gets it for
     14        free.
     15
     16        Test: PerformanceTests/Layout/underline.html
     17
     18        * platform/graphics/Font.cpp:
     19        (WebCore::Font::pathForGlyph const):
     20        * platform/graphics/Font.h:
     21        * platform/graphics/FontCascade.cpp:
     22        (WebCore::computeUnderlineType):
     23        (WebCore::GlyphIterationState::GlyphIterationState):
     24        (WebCore::findIntersectionPoint):
     25        (WebCore::updateX):
     26        (WebCore::findPathIntersections):
     27        (WebCore::GlyphToPathTranslator::GlyphToPathTranslator):
     28        (WebCore::GlyphToPathTranslator::containsMorePaths):
     29        (WebCore::GlyphToPathTranslator::path):
     30        (WebCore::GlyphToPathTranslator::extents):
     31        (WebCore::GlyphToPathTranslator::underlineType):
     32        (WebCore::GlyphToPathTranslator::advance):
     33        (WebCore::FontCascade::dashesForIntersectionsWithRect const):
     34        * platform/graphics/FontCascade.h:
     35        * platform/graphics/GlyphMetricsMap.h:
     36        (WebCore::GlyphMetricsMap::existingMetricsForGlyph):
     37        (WebCore::GlyphMetricsMap::GlyphMetricsPage::existingMetricsForGlyph const):
     38        (WebCore::GlyphMetricsMap<std::optional<Path>>::unknownMetrics):
     39        * platform/graphics/TextRun.h:
     40        * platform/graphics/cairo/FontCairo.cpp:
     41        (WebCore::Font::platformPathForGlyph const):
     42        (WebCore::GlyphIterationState::GlyphIterationState): Deleted.
     43        (WebCore::findIntersectionPoint): Deleted.
     44        (WebCore::updateX): Deleted.
     45        (WebCore::findPathIntersections): Deleted.
     46        (): Deleted.
     47        (WebCore::CairoGlyphToPathTranslator::path): Deleted.
     48        (WebCore::CairoGlyphToPathTranslator::extents): Deleted.
     49        (WebCore::CairoGlyphToPathTranslator::underlineType): Deleted.
     50        (WebCore::CairoGlyphToPathTranslator::advance): Deleted.
     51        (WebCore::FontCascade::dashesForIntersectionsWithRect const): Deleted.
     52        * platform/graphics/cocoa/FontCascadeCocoa.mm:
     53        (WebCore::GlyphIterationState::GlyphIterationState): Deleted.
     54        (WebCore::findIntersectionPoint): Deleted.
     55        (WebCore::updateX): Deleted.
     56        (WebCore::findPathIntersections): Deleted.
     57        (): Deleted.
     58        (WebCore::MacGlyphToPathTranslator::path): Deleted.
     59        (WebCore::MacGlyphToPathTranslator::extents): Deleted.
     60        (WebCore::MacGlyphToPathTranslator::underlineType): Deleted.
     61        (WebCore::MacGlyphToPathTranslator::advance): Deleted.
     62        (WebCore::FontCascade::dashesForIntersectionsWithRect const): Deleted.
     63        * platform/graphics/cocoa/FontCocoa.mm:
     64        (WebCore::Font::platformPathForGlyph const):
     65        * rendering/TextDecorationPainter.cpp:
     66        (WebCore::drawSkipInkUnderline):
     67        (WebCore::TextDecorationPainter::paintTextDecoration):
     68
    1692018-11-05  Myles C. Maxfield  <mmaxfield@apple.com>
    270
  • trunk/Source/WebCore/PAL/pal/spi/win/CoreTextSPIWin.h

    r220809 r237845  
    5858CFDataRef CTFontCopyTable(CTFontRef, CTFontTableTag, CTFontTableOptions);
    5959CFArrayRef CTFontCopyAvailableTables(CTFontRef, CTFontTableOptions);
     60CGPathRef CTFontCreatePathForGlyph(CTFontRef, CGGlyph, const CGAffineTransform*);
    6061
    6162WTF_EXTERN_C_END
  • trunk/Source/WebCore/platform/graphics/Font.cpp

    r237487 r237845  
    664664    return true;
    665665}
     666
     667// Don't store the result of this! The hash map is free to rehash at any point, leaving this reference dangling.
     668const Path& Font::pathForGlyph(Glyph glyph) const
     669{
     670    if (const auto& path = m_glyphPathMap.existingMetricsForGlyph(glyph))
     671        return *path;
     672    auto path = platformPathForGlyph(glyph);
     673    m_glyphPathMap.setMetricsForGlyph(glyph, path);
     674    return *m_glyphPathMap.existingMetricsForGlyph(glyph);
     675}
     676
    666677} // namespace WebCore
  • trunk/Source/WebCore/platform/graphics/Font.h

    r237487 r237845  
    147147    FloatRect boundsForGlyph(Glyph) const;
    148148    float widthForGlyph(Glyph) const;
     149    const Path& pathForGlyph(Glyph) const; // Don't store the result of this! The hash map is free to rehash at any point, leaving this reference dangling.
    149150    FloatRect platformBoundsForGlyph(Glyph) const;
    150151    float platformWidthForGlyph(Glyph) const;
     152    Path platformPathForGlyph(Glyph) const;
    151153
    152154    float spaceWidth() const { return m_spaceWidth; }
     
    254256    mutable std::unique_ptr<GlyphMetricsMap<FloatRect>> m_glyphToBoundsMap;
    255257    mutable GlyphMetricsMap<float> m_glyphToWidthMap;
     258    mutable GlyphMetricsMap<std::optional<Path>> m_glyphPathMap;
    256259    mutable BitVector m_codePointSupport;
    257260
  • trunk/Source/WebCore/platform/graphics/FontCascade.cpp

    r237463 r237845  
    12381238    return m_fonts && m_fonts->isLoadingCustomFonts();
    12391239}
     1240
     1241enum class GlyphUnderlineType : uint8_t {
     1242    SkipDescenders,
     1243    SkipGlyph,
     1244    DrawOverGlyph
     1245};
    12401246   
    1241 GlyphToPathTranslator::GlyphUnderlineType computeUnderlineType(const TextRun& textRun, const GlyphBuffer& glyphBuffer, unsigned index)
     1247static GlyphUnderlineType computeUnderlineType(const TextRun& textRun, const GlyphBuffer& glyphBuffer, unsigned index)
    12421248{
    12431249    // In general, we want to skip descenders. However, skipping descenders on CJK characters leads to undesirable renderings,
    12441250    // so we want to draw through CJK characters (on a character-by-character basis).
     1251    // FIXME: The CSS spec says this should instead be done by the user-agent stylesheet using the lang= attribute.
    12451252    UChar32 baseCharacter;
    12461253    unsigned offsetInString = glyphBuffer.offsetInString(index);
     
    12491256        // We have no idea which character spawned this glyph. Bail.
    12501257        ASSERT_WITH_SECURITY_IMPLICATION(offsetInString < textRun.length());
    1251         return GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph;
     1258        return GlyphUnderlineType::DrawOverGlyph;
    12521259    }
    12531260   
     
    12861293    case UBLOCK_HANGUL_JAMO_EXTENDED_A:
    12871294    case UBLOCK_HANGUL_JAMO_EXTENDED_B:
    1288         return GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph;
     1295        return GlyphUnderlineType::DrawOverGlyph;
    12891296    default:
    1290         return GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders;
     1297        return GlyphUnderlineType::SkipDescenders;
    12911298    }
    12921299}
     
    16591666}
    16601667
    1661 }
     1668struct GlyphIterationState {
     1669    FloatPoint startingPoint;
     1670    FloatPoint currentPoint;
     1671    float y1;
     1672    float y2;
     1673    float minX;
     1674    float maxX;
     1675};
     1676
     1677static std::optional<float> findIntersectionPoint(float y, FloatPoint p1, FloatPoint p2)
     1678{
     1679    if ((p1.y() < y && p2.y() > y) || (p1.y() > y && p2.y() < y))
     1680        return p1.x() + (y - p1.y()) * (p2.x() - p1.x()) / (p2.y() - p1.y());
     1681    return std::nullopt;
     1682}
     1683
     1684static void updateX(GlyphIterationState& state, float x)
     1685{
     1686    state.minX = std::min(state.minX, x);
     1687    state.maxX = std::max(state.maxX, x);
     1688}
     1689
     1690// This function is called by CGPathApply and is therefore invoked for each
     1691// contour in a glyph. This function models each contours as a straight line
     1692// and calculates the intersections between each pseudo-contour and
     1693// two horizontal lines (the upper and lower bounds of an underline) found in
     1694// GlyphIterationState::y1 and GlyphIterationState::y2. It keeps track of the
     1695// leftmost and rightmost intersection in GlyphIterationState::minX and
     1696// GlyphIterationState::maxX.
     1697static void findPathIntersections(GlyphIterationState& state, const PathElement& element)
     1698{
     1699    bool doIntersection = false;
     1700    FloatPoint point = FloatPoint();
     1701    switch (element.type) {
     1702    case PathElementMoveToPoint:
     1703        state.startingPoint = element.points[0];
     1704        state.currentPoint = element.points[0];
     1705        break;
     1706    case PathElementAddLineToPoint:
     1707        doIntersection = true;
     1708        point = element.points[0];
     1709        break;
     1710    case PathElementAddQuadCurveToPoint:
     1711        doIntersection = true;
     1712        point = element.points[1];
     1713        break;
     1714    case PathElementAddCurveToPoint:
     1715        doIntersection = true;
     1716        point = element.points[2];
     1717        break;
     1718    case PathElementCloseSubpath:
     1719        doIntersection = true;
     1720        point = state.startingPoint;
     1721        break;
     1722    }
     1723    if (!doIntersection)
     1724        return;
     1725    if (auto intersectionPoint = findIntersectionPoint(state.y1, state.currentPoint, point))
     1726        updateX(state, *intersectionPoint);
     1727    if (auto intersectionPoint = findIntersectionPoint(state.y2, state.currentPoint, point))
     1728        updateX(state, *intersectionPoint);
     1729    if ((state.currentPoint.y() >= state.y1 && state.currentPoint.y() <= state.y2)
     1730        || (state.currentPoint.y() <= state.y1 && state.currentPoint.y() >= state.y2))
     1731        updateX(state, state.currentPoint.x());
     1732    state.currentPoint = point;
     1733}
     1734
     1735class GlyphToPathTranslator {
     1736public:
     1737    GlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
     1738        : m_index(0)
     1739        , m_textRun(textRun)
     1740        , m_glyphBuffer(glyphBuffer)
     1741        , m_fontData(glyphBuffer.fontAt(m_index))
     1742        , m_translation(AffineTransform::translation(textOrigin.x(), textOrigin.y()).scale(1, -1))
     1743    {
     1744    }
     1745
     1746    bool containsMorePaths() { return m_index != m_glyphBuffer.size(); }
     1747    Path path();
     1748    std::pair<float, float> extents();
     1749    GlyphUnderlineType underlineType();
     1750    void advance();
     1751
     1752private:
     1753    unsigned m_index;
     1754    const TextRun& m_textRun;
     1755    const GlyphBuffer& m_glyphBuffer;
     1756    const Font* m_fontData;
     1757    AffineTransform m_translation;
     1758};
     1759
     1760Path GlyphToPathTranslator::path()
     1761{
     1762    Path path = m_fontData->pathForGlyph(m_glyphBuffer.glyphAt(m_index));
     1763    path.transform(m_translation);
     1764    return path;
     1765}
     1766
     1767std::pair<float, float> GlyphToPathTranslator::extents()
     1768{
     1769    auto beginning = m_translation.mapPoint(FloatPoint(0, 0));
     1770    auto advance = m_glyphBuffer.advanceAt(m_index);
     1771    auto end = m_translation.mapSize(FloatSize(advance.width(), advance.height()));
     1772    return std::make_pair(beginning.x(), beginning.x() + end.width());
     1773}
     1774
     1775auto GlyphToPathTranslator::underlineType() -> GlyphUnderlineType
     1776{
     1777    return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
     1778}
     1779
     1780void GlyphToPathTranslator::advance()
     1781{
     1782    GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
     1783    m_translation.translate(FloatSize(advance.width(), advance.height()));
     1784    ++m_index;
     1785    if (m_index < m_glyphBuffer.size())
     1786        m_fontData = m_glyphBuffer.fontAt(m_index);
     1787}
     1788
     1789DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
     1790{
     1791    if (isLoadingCustomFonts())
     1792        return DashArray();
     1793
     1794    GlyphBuffer glyphBuffer;
     1795    glyphBuffer.saveOffsetsInString();
     1796    float deltaX;
     1797    if (codePath(run) != FontCascade::Complex)
     1798        deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
     1799    else
     1800        deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
     1801
     1802    if (!glyphBuffer.size())
     1803        return DashArray();
     1804
     1805    FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
     1806    GlyphToPathTranslator translator(run, glyphBuffer, origin);
     1807    DashArray result;
     1808    for (unsigned index = 0; translator.containsMorePaths(); ++index, translator.advance()) {
     1809        GlyphIterationState info = { FloatPoint(0, 0), FloatPoint(0, 0), lineExtents.y(), lineExtents.y() + lineExtents.height(), lineExtents.x() + lineExtents.width(), lineExtents.x() };
     1810        const Font* localFont = glyphBuffer.fontAt(index);
     1811        if (!localFont) {
     1812            // The advances will get all messed up if we do anything other than bail here.
     1813            result.clear();
     1814            break;
     1815        }
     1816        switch (translator.underlineType()) {
     1817        case GlyphUnderlineType::SkipDescenders: {
     1818            Path path = translator.path();
     1819            path.apply([&](const PathElement& element) {
     1820                findPathIntersections(info, element);
     1821            });
     1822            if (info.minX < info.maxX) {
     1823                result.append(info.minX - lineExtents.x());
     1824                result.append(info.maxX - lineExtents.x());
     1825            }
     1826            break;
     1827        }
     1828        case GlyphUnderlineType::SkipGlyph: {
     1829            std::pair<float, float> extents = translator.extents();
     1830            result.append(extents.first - lineExtents.x());
     1831            result.append(extents.second - lineExtents.x());
     1832            break;
     1833        }
     1834        case GlyphUnderlineType::DrawOverGlyph:
     1835            // Nothing to do
     1836            break;
     1837        }
     1838    }
     1839    return result;
     1840}
     1841
     1842}
  • trunk/Source/WebCore/platform/graphics/FontCascade.h

    r237813 r237845  
    8282};
    8383
    84 class GlyphToPathTranslator {
    85 public:
    86     enum class GlyphUnderlineType {SkipDescenders, SkipGlyph, DrawOverGlyph};
    87     virtual bool containsMorePaths() = 0;
    88     virtual Path path() = 0;
    89     virtual std::pair<float, float> extents() = 0;
    90     virtual GlyphUnderlineType underlineType() = 0;
    91     virtual void advance() = 0;
    92     virtual ~GlyphToPathTranslator() = default;
    93 };
    94 GlyphToPathTranslator::GlyphUnderlineType computeUnderlineType(const TextRun&, const GlyphBuffer&, unsigned index);
    95 
    9684class TextLayoutDeleter {
    9785public:
  • trunk/Source/WebCore/platform/graphics/GlyphMetricsMap.h

    r205703 r237845  
    3131
    3232#include "Glyph.h"
     33#include "Path.h"
    3334#include <array>
    3435#include <wtf/HashMap.h>
     
    4445    {
    4546        return locatePage(glyph / GlyphMetricsPage::size).metricsForGlyph(glyph);
     47    }
     48
     49    const T& existingMetricsForGlyph(Glyph glyph)
     50    {
     51        return locatePage(glyph / GlyphMetricsPage::size).existingMetricsForGlyph(glyph);
    4652    }
    4753
     
    6975
    7076        T metricsForGlyph(Glyph glyph) const { return m_metrics[glyph % size]; }
     77        const T& existingMetricsForGlyph(Glyph glyph) const { return m_metrics[glyph % size]; }
    7178        void setMetricsForGlyph(Glyph glyph, const T& metrics)
    7279        {
     
    109116}
    110117
     118template<> inline std::optional<Path> GlyphMetricsMap<std::optional<Path>>::unknownMetrics()
     119{
     120    return std::nullopt;
     121}
     122
    111123template<class T> typename GlyphMetricsMap<T>::GlyphMetricsPage& GlyphMetricsMap<T>::locatePageSlowCase(unsigned pageNumber)
    112124{
  • trunk/Source/WebCore/platform/graphics/TextRun.h

    r235721 r237845  
    3636class GraphicsContext;
    3737class GlyphBuffer;
    38 class GlyphToPathTranslator;
    3938class Font;
    4039
  • trunk/Source/WebCore/platform/graphics/cairo/FontCairo.cpp

    r235521 r237845  
    7979}
    8080
    81 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
    82 struct GlyphIterationState {
    83     GlyphIterationState(FloatPoint startingPoint, FloatPoint currentPoint, float centerOfLine, float minX, float maxX)
    84         : startingPoint(startingPoint)
    85         , currentPoint(currentPoint)
    86         , centerOfLine(centerOfLine)
    87         , minX(minX)
    88         , maxX(maxX)
    89     {
    90     }
    91     FloatPoint startingPoint;
    92     FloatPoint currentPoint;
    93     float centerOfLine;
    94     float minX;
    95     float maxX;
    96 };
    97 
    98 static bool findIntersectionPoint(float y, FloatPoint p1, FloatPoint p2, float& x)
    99 {
    100     x = p1.x() + (y - p1.y()) * (p2.x() - p1.x()) / (p2.y() - p1.y());
    101     return (p1.y() < y && p2.y() > y) || (p1.y() > y && p2.y() < y);
    102 }
    103 
    104 static void updateX(GlyphIterationState& state, float x)
    105 {
    106     state.minX = std::min(state.minX, x);
    107     state.maxX = std::max(state.maxX, x);
    108 }
    109 
    110 // This function is called by Path::apply and is therefore invoked for each contour in a glyph. This
    111 // function models each contours as a straight line and calculates the intersections between each
    112 // pseudo-contour and the vertical center of the underline found in GlyphIterationState::centerOfLine.
    113 // It keeps track of the leftmost and rightmost intersection in  GlyphIterationState::minX and
    114 // GlyphIterationState::maxX.
    115 static void findPathIntersections(GlyphIterationState& state, const PathElement& element)
    116 {
    117     bool doIntersection = false;
    118     FloatPoint point = FloatPoint();
    119     switch (element.type) {
    120     case PathElementMoveToPoint:
    121         state.startingPoint = element.points[0];
    122         state.currentPoint = element.points[0];
    123         break;
    124     case PathElementAddLineToPoint:
    125         doIntersection = true;
    126         point = element.points[0];
    127         break;
    128     case PathElementAddQuadCurveToPoint:
    129         doIntersection = true;
    130         point = element.points[1];
    131         break;
    132     case PathElementAddCurveToPoint:
    133         doIntersection = true;
    134         point = element.points[2];
    135         break;
    136     case PathElementCloseSubpath:
    137         doIntersection = true;
    138         point = state.startingPoint;
    139         break;
    140     }
    141 
    142     if (!doIntersection)
    143         return;
    144 
    145     float x;
    146     if (findIntersectionPoint(state.centerOfLine, state.currentPoint, point, x))
    147         updateX(state, x);
    148 
    149     state.currentPoint = point;
    150 }
    151 
    152 class CairoGlyphToPathTranslator final : public GlyphToPathTranslator {
    153 public:
    154     CairoGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
    155         : m_index(0)
    156         , m_textRun(textRun)
    157         , m_glyphBuffer(glyphBuffer)
    158         , m_fontData(glyphBuffer.fontAt(m_index))
    159         , m_translation(AffineTransform().translate(textOrigin.x(), textOrigin.y()))
    160     {
    161     }
    162 
    163     bool containsMorePaths() final { return m_index != m_glyphBuffer.size(); }
    164     Path path() final;
    165     std::pair<float, float> extents() final;
    166     GlyphUnderlineType underlineType() final;
    167     void advance() final;
    168 
    169 private:
    170     unsigned m_index;
    171     const TextRun& m_textRun;
    172     const GlyphBuffer& m_glyphBuffer;
    173     const Font* m_fontData;
    174     AffineTransform m_translation;
    175 };
    176 
    177 Path CairoGlyphToPathTranslator::path()
     81Path Font::platformPathForGlyph(Glyph glyph) const
    17882{
    17983    Path path;
    18084    path.ensurePlatformPath();
    18185
    182     cairo_glyph_t cairoGlyph = { m_glyphBuffer.glyphAt(m_index), 0, 0 };
    183     cairo_set_scaled_font(path.platformPath()->context(), m_fontData->platformData().scaledFont());
     86    cairo_glyph_t cairoGlyph = { glyph, 0, 0 };
     87    cairo_set_scaled_font(path.platformPath()->context(), platformData().scaledFont());
    18488    cairo_glyph_path(path.platformPath()->context(), &cairoGlyph, 1);
    18589
    186     float syntheticBoldOffset = m_fontData->syntheticBoldOffset();
     90    float syntheticBoldOffset = this->syntheticBoldOffset();
    18791    if (syntheticBoldOffset) {
    18892        cairo_translate(path.platformPath()->context(), syntheticBoldOffset, 0);
    18993        cairo_glyph_path(path.platformPath()->context(), &cairoGlyph, 1);
    19094    }
    191 
    192     path.transform(m_translation);
    19395    return path;
    19496}
    195 
    196 std::pair<float, float> CairoGlyphToPathTranslator::extents()
    197 {
    198     FloatPoint beginning = m_translation.mapPoint(FloatPoint());
    199     FloatSize end = m_translation.mapSize(m_glyphBuffer.advanceAt(m_index));
    200     return std::make_pair(static_cast<float>(beginning.x()), static_cast<float>(beginning.x() + end.width()));
    201 }
    202 
    203 GlyphToPathTranslator::GlyphUnderlineType CairoGlyphToPathTranslator::underlineType()
    204 {
    205     return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
    206 }
    207 
    208 void CairoGlyphToPathTranslator::advance()
    209 {
    210     GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
    211     m_translation = m_translation.translate(advance.width(), advance.height());
    212     ++m_index;
    213     if (m_index < m_glyphBuffer.size())
    214         m_fontData = m_glyphBuffer.fontAt(m_index);
    215 }
    216 
    217 DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
    218 {
    219     if (isLoadingCustomFonts())
    220         return DashArray();
    221 
    222     GlyphBuffer glyphBuffer;
    223     glyphBuffer.saveOffsetsInString();
    224     float deltaX;
    225     if (codePath(run) != FontCascade::Complex)
    226         deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
    227     else
    228         deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
    229 
    230     if (!glyphBuffer.size())
    231         return DashArray();
    232 
    233     // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778
    234     FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
    235     CairoGlyphToPathTranslator translator(run, glyphBuffer, origin);
    236     DashArray result;
    237     for (int index = 0; translator.containsMorePaths(); ++index, translator.advance()) {
    238         float centerOfLine = lineExtents.y() + (lineExtents.height() / 2);
    239         GlyphIterationState info = GlyphIterationState(FloatPoint(), FloatPoint(), centerOfLine, lineExtents.x() + lineExtents.width(), lineExtents.x());
    240         const Font* localFontData = glyphBuffer.fontAt(index);
    241         if (!localFontData) {
    242             // The advances will get all messed up if we do anything other than bail here.
    243             result.clear();
    244             break;
    245         }
    246         switch (translator.underlineType()) {
    247         case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: {
    248             Path path = translator.path();
    249             path.apply([&info](const PathElement& pathElement) {
    250                 findPathIntersections(info, pathElement);
    251             });
    252             if (info.minX < info.maxX) {
    253                 result.append(info.minX - lineExtents.x());
    254                 result.append(info.maxX - lineExtents.x());
    255             }
    256             break;
    257         }
    258         case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: {
    259             std::pair<float, float> extents = translator.extents();
    260             result.append(extents.first - lineExtents.x());
    261             result.append(extents.second - lineExtents.x());
    262             break;
    263         }
    264         case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph:
    265             // Nothing to do
    266             break;
    267         }
    268     }
    269     return result;
    270 }
    271 #endif // ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
    27297
    27398} // namespace WebCore
  • trunk/Source/WebCore/platform/graphics/cocoa/FontCascadeCocoa.mm

    r237765 r237845  
    304304}
    305305
    306 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
    307 struct GlyphIterationState {
    308     GlyphIterationState(CGPoint startingPoint, CGPoint currentPoint, CGFloat y1, CGFloat y2, CGFloat minX, CGFloat maxX)
    309         : startingPoint(startingPoint)
    310         , currentPoint(currentPoint)
    311         , y1(y1)
    312         , y2(y2)
    313         , minX(minX)
    314         , maxX(maxX)
    315     {
    316     }
    317     CGPoint startingPoint;
    318     CGPoint currentPoint;
    319     CGFloat y1;
    320     CGFloat y2;
    321     CGFloat minX;
    322     CGFloat maxX;
    323 };
    324 
    325 static bool findIntersectionPoint(float y, CGPoint p1, CGPoint p2, CGFloat& x)
    326 {
    327     x = p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
    328     return (p1.y < y && p2.y > y) || (p1.y > y && p2.y < y);
    329 }
    330 
    331 static void updateX(GlyphIterationState& state, CGFloat x)
    332 {
    333     state.minX = std::min(state.minX, x);
    334     state.maxX = std::max(state.maxX, x);
    335 }
    336 
    337 // This function is called by CGPathApply and is therefore invoked for each
    338 // contour in a glyph. This function models each contours as a straight line
    339 // and calculates the intersections between each pseudo-contour and
    340 // two horizontal lines (the upper and lower bounds of an underline) found in
    341 // GlyphIterationState::y1 and GlyphIterationState::y2. It keeps track of the
    342 // leftmost and rightmost intersection in GlyphIterationState::minX and
    343 // GlyphIterationState::maxX.
    344 static void findPathIntersections(void* stateAsVoidPointer, const CGPathElement* e)
    345 {
    346     auto& state = *static_cast<GlyphIterationState*>(stateAsVoidPointer);
    347     bool doIntersection = false;
    348     CGPoint point = CGPointZero;
    349     switch (e->type) {
    350     case kCGPathElementMoveToPoint:
    351         state.startingPoint = e->points[0];
    352         state.currentPoint = e->points[0];
    353         break;
    354     case kCGPathElementAddLineToPoint:
    355         doIntersection = true;
    356         point = e->points[0];
    357         break;
    358     case kCGPathElementAddQuadCurveToPoint:
    359         doIntersection = true;
    360         point = e->points[1];
    361         break;
    362     case kCGPathElementAddCurveToPoint:
    363         doIntersection = true;
    364         point = e->points[2];
    365         break;
    366     case kCGPathElementCloseSubpath:
    367         doIntersection = true;
    368         point = state.startingPoint;
    369         break;
    370     }
    371     if (!doIntersection)
    372         return;
    373     CGFloat x;
    374     if (findIntersectionPoint(state.y1, state.currentPoint, point, x))
    375         updateX(state, x);
    376     if (findIntersectionPoint(state.y2, state.currentPoint, point, x))
    377         updateX(state, x);
    378     if ((state.currentPoint.y >= state.y1 && state.currentPoint.y <= state.y2)
    379         || (state.currentPoint.y <= state.y1 && state.currentPoint.y >= state.y2))
    380         updateX(state, state.currentPoint.x);
    381     state.currentPoint = point;
    382 }
    383 
    384 class MacGlyphToPathTranslator final : public GlyphToPathTranslator {
    385 public:
    386     MacGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
    387         : m_index(0)
    388         , m_textRun(textRun)
    389         , m_glyphBuffer(glyphBuffer)
    390         , m_fontData(glyphBuffer.fontAt(m_index))
    391         , m_translation(CGAffineTransformScale(CGAffineTransformMakeTranslation(textOrigin.x(), textOrigin.y()), 1, -1))
    392     {
    393     }
    394     bool containsMorePaths() final { return m_index != m_glyphBuffer.size(); }
    395     Path path() final;
    396     std::pair<float, float> extents() final;
    397     GlyphUnderlineType underlineType() final;
    398     void advance() final;
    399 
    400 private:
    401     unsigned m_index;
    402     const TextRun& m_textRun;
    403     const GlyphBuffer& m_glyphBuffer;
    404     const Font* m_fontData;
    405     CGAffineTransform m_translation;
    406 };
    407 
    408 Path MacGlyphToPathTranslator::path()
    409 {
    410     RetainPtr<CGPathRef> result = adoptCF(CTFontCreatePathForGlyph(m_fontData->platformData().ctFont(), m_glyphBuffer.glyphAt(m_index), &m_translation));
    411     return adoptCF(CGPathCreateMutableCopy(result.get()));
    412 }
    413 
    414 std::pair<float, float> MacGlyphToPathTranslator::extents()
    415 {
    416     CGPoint beginning = CGPointApplyAffineTransform(CGPointMake(0, 0), m_translation);
    417     CGSize end = CGSizeApplyAffineTransform(m_glyphBuffer.advanceAt(m_index), m_translation);
    418     return std::make_pair(static_cast<float>(beginning.x), static_cast<float>(beginning.x + end.width));
    419 }
    420 
    421 auto MacGlyphToPathTranslator::underlineType() -> GlyphUnderlineType
    422 {
    423     return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
    424 }
    425 
    426 void MacGlyphToPathTranslator::advance()
    427 {
    428     GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
    429     m_translation = CGAffineTransformTranslate(m_translation, advance.width(), advance.height());
    430     ++m_index;
    431     if (m_index < m_glyphBuffer.size())
    432         m_fontData = m_glyphBuffer.fontAt(m_index);
    433 }
    434 
    435 DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
    436 {
    437     if (isLoadingCustomFonts())
    438         return DashArray();
    439 
    440     GlyphBuffer glyphBuffer;
    441     glyphBuffer.saveOffsetsInString();
    442     float deltaX;
    443     if (codePath(run) != FontCascade::Complex)
    444         deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
    445     else
    446         deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
    447 
    448     if (!glyphBuffer.size())
    449         return DashArray();
    450    
    451     // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778
    452     FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
    453     MacGlyphToPathTranslator translator(run, glyphBuffer, origin);
    454     DashArray result;
    455     for (unsigned index = 0; translator.containsMorePaths(); ++index, translator.advance()) {
    456         GlyphIterationState info = GlyphIterationState(CGPointMake(0, 0), CGPointMake(0, 0), lineExtents.y(), lineExtents.y() + lineExtents.height(), lineExtents.x() + lineExtents.width(), lineExtents.x());
    457         const Font* localFont = glyphBuffer.fontAt(index);
    458         if (!localFont) {
    459             // The advances will get all messed up if we do anything other than bail here.
    460             result.clear();
    461             break;
    462         }
    463         switch (translator.underlineType()) {
    464         case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: {
    465             Path path = translator.path();
    466             CGPathApply(path.platformPath(), &info, &findPathIntersections);
    467             if (info.minX < info.maxX) {
    468                 result.append(info.minX - lineExtents.x());
    469                 result.append(info.maxX - lineExtents.x());
    470             }
    471             break;
    472         }
    473         case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: {
    474             std::pair<float, float> extents = translator.extents();
    475             result.append(extents.first - lineExtents.x());
    476             result.append(extents.second - lineExtents.x());
    477             break;
    478         }
    479         case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph:
    480             // Nothing to do
    481             break;
    482         }
    483     }
    484     return result;
    485 }
    486 #endif
    487 
    488306bool FontCascade::primaryFontIsSystemFont() const
    489307{
  • trunk/Source/WebCore/platform/graphics/cocoa/FontCocoa.mm

    r237266 r237845  
    598598}
    599599
     600Path Font::platformPathForGlyph(Glyph glyph) const
     601{
     602    auto result = adoptCF(CTFontCreatePathForGlyph(platformData().ctFont(), glyph, nullptr));
     603    auto syntheticBoldOffset = this->syntheticBoldOffset();
     604    if (syntheticBoldOffset) {
     605        auto newPath = adoptCF(CGPathCreateMutable());
     606        CGPathAddPath(newPath.get(), nullptr, result.get());
     607        auto translation = CGAffineTransformMakeTranslation(syntheticBoldOffset, 0);
     608        CGPathAddPath(newPath.get(), &translation, result.get());
     609        return newPath;
     610    }
     611    return adoptCF(CGPathCreateMutableCopy(result.get()));
     612}
     613
    600614bool Font::platformSupportsCodePoint(UChar32 character) const
    601615{
  • trunk/Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp

    r229400 r237845  
    153153}
    154154
     155Path Font::platformPathForGlyph(Glyph glyph) const
     156{
     157    auto ctFont = adoptCF(CTFontCreateWithGraphicsFont(platformData().cgFont(), platformData().size(), nullptr, nullptr));
     158    auto result = adoptCF(CTFontCreatePathForGlyph(ctFont.get(), glyph, nullptr));
     159    auto syntheticBoldOffset = this->syntheticBoldOffset();
     160    if (syntheticBoldOffset) {
     161        auto newPath = adoptCF(CGPathCreateMutable());
     162        CGPathAddPath(newPath.get(), nullptr, result.get());
     163        auto translation = CGAffineTransformMakeTranslation(syntheticBoldOffset, 0);
     164        CGPathAddPath(newPath.get(), &translation, result.get());
     165        return newPath;
     166    }
     167    return adoptCF(CGPathCreateMutableCopy(result.get()));
     168}
     169
    155170}
    156171
  • trunk/Source/WebCore/platform/graphics/win/SimpleFontDataDirect2D.cpp

    r233851 r237845  
    3535#include "GraphicsContext.h"
    3636#include "HWndDC.h"
     37#include "NotImplemented.h"
    3738#include <comutil.h>
    3839#include <dwrite.h>
     
    231232}
    232233
     234Path Font::platformPathForGlyph(Glyph) const
     235{
     236    notImplemented();
     237    return Path();
     238}
     239
    233240}
    234241
  • trunk/Source/WebCore/rendering/TextDecorationPainter.cpp

    r237844 r237845  
    117117}
    118118
    119 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
    120119static bool compareTuples(std::pair<float, float> l, std::pair<float, float> r)
    121120{
     
    168167    return result;
    169168}
    170 #endif
    171169
    172170static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorationStyle)
     
    213211void TextDecorationPainter::paintTextDecoration(const TextRun& textRun, const FloatPoint& textOrigin, const FloatPoint& boxOrigin)
    214212{
    215 #if !ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
    216     UNUSED_PARAM(textRun);
    217     UNUSED_PARAM(textOrigin);
    218 #endif
    219213    const auto& fontMetrics = m_lineStyle.fontMetrics();
    220214    float textDecorationThickness = textDecorationStrokeThickness(m_lineStyle.computedFontPixelSize());
     
    229223            strokeWavyTextDecoration(m_context, rect, m_lineStyle.computedFontPixelSize());
    230224        else if (decoration == TextDecoration::Underline || decoration == TextDecoration::Overline) {
    231 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
    232225            if ((m_lineStyle.textDecorationSkip() == TextDecorationSkip::Ink || m_lineStyle.textDecorationSkip() == TextDecorationSkip::Auto) && m_isHorizontal) {
    233226                if (!m_context.paintingDisabled()) {
     
    239232                    m_context.drawLinesForText(rect.location(), rect.height(), boundaries, m_isPrinting, style == TextDecorationStyle::Double, strokeStyle);
    240233                }
    241             } else
     234            } else {
    242235                // FIXME: Need to support text-decoration-skip: none.
    243 #endif
    244236                m_context.drawLineForText(rect, m_isPrinting, style == TextDecorationStyle::Double, strokeStyle);
    245            
     237            }
    246238        } else {
    247239            ASSERT(decoration == TextDecoration::LineThrough);
Note: See TracChangeset for help on using the changeset viewer.