Changeset 273244 in webkit


Ignore:
Timestamp:
Feb 22, 2021 9:52:13 AM (3 years ago)
Author:
weinig@apple.com
Message:

Add experimental support for CSS Color 5 color-mix()
https://bugs.webkit.org/show_bug.cgi?id=222258

Reviewed by Antti Koivisto.

Source/WebCore:

Adds initial support for CSS Color 5 color-mix() - https://drafts.csswg.org/css-color-5/#color-mix

This feature is off by default and can be enabled via the CSSColorMixEnabled
experimental preference flag.

This implementation has the same restriction on it that the recently landed
Relative Color Syntax does in that it does support system colors or currentColor
as input, since those can't be resolved at parse time. Ultimately, we will need
to add a late binding version of this for those cases.

Test: fast/css/parsing-color-mix.html

  • css/CSSValueKeywords.in:

Add new keywords needed for color-mix().

  • css/parser/CSSParserContext.cpp:

(WebCore::operator==):

  • css/parser/CSSParserContext.h:

(WebCore::CSSParserContextHash::hash):
Add new setting for color-mix().

  • css/parser/CSSPropertyParserHelpers.cpp:

(WebCore::CSSPropertyParserHelpers::HueColorAdjuster::fixupAnglesForInterpolation):
(WebCore::CSSPropertyParserHelpers::HueColorAdjuster::HueColorAdjuster):
(WebCore::CSSPropertyParserHelpers::ColorAdjuster::ColorAdjuster):
(WebCore::CSSPropertyParserHelpers::consumeAdjuster):
(WebCore::CSSPropertyParserHelpers::consumeAndUpdateAdjusterAtIndex):
(WebCore::CSSPropertyParserHelpers::consumeAndUpdateAdjuster):
(WebCore::CSSPropertyParserHelpers::consumeAdjusters):
(WebCore::CSSPropertyParserHelpers::consumeMixComponents):
(WebCore::CSSPropertyParserHelpers::normalizeAdjusterValues):
(WebCore::CSSPropertyParserHelpers::remainingAdjustment):
(WebCore::CSSPropertyParserHelpers::mixComponent):
(WebCore::CSSPropertyParserHelpers::mixComponentAtIndex):
(WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix):
(WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<HWBA<float>>):
(WebCore::CSSPropertyParserHelpers::mix):
(WebCore::CSSPropertyParserHelpers::parseColorMixFunctionParametersUsingAdjusters):
(WebCore::CSSPropertyParserHelpers::parseColorMixFunctionParameters):
(WebCore::CSSPropertyParserHelpers::parseColorFunction):
The implementation uses a templatized ColorAdjuster struct to declartively map the
various mixing color spaces to their allowed adjusters and what type those adjusters
operate on (either double or the hue specific HueColorAdjuster). For example, for
LCHA we have:

using LCHColorAdjuster = ColorAdjuster<LCHA<float>, CSSValueLightness, double, CSSValueChroma, HueColorAdjuster, CSSValueHue, double, CSSValueAlpha, double>;

which indicates:

  • it creates a LCHA<float> and will operate on LCHA<float> values
  • its first channel is called "lightness" and is a double
  • its second channel is called "chroma" and is a double
  • its third channel is called "hue" and is a HueColorAdjuster
  • its fourth channel is called "alpha" and is a double

This data is then used by the parsing and mixing functions to implement mixing without
having to write specific implementations for each mixing color space and can be expanded
to more spaces if needed.

  • platform/graphics/Color.h:

(WebCore::Color::Color):
Add new overloaded constructor for a generic Optional<ColorType<float>> which parallels the existing
Optional<SRGBA<uint8_t>> allowing callers to convert WTF::nullopt to an invalid Color without checking
for nullopt themselves.

Source/WTF:

  • Scripts/Preferences/WebPreferencesExperimental.yaml:

Add new experimental preference for CSS Color 5 color-mix()
which is off by default.

LayoutTests:

  • fast/css/parsing-color-mix-expected.txt: Added.
  • fast/css/parsing-color-mix.html: Added.

Add parsing and computed style computation tests for color-mix().

Location:
trunk
Files:
2 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r273243 r273244  
     12021-02-22  Sam Weinig  <weinig@apple.com>
     2
     3        Add experimental support for CSS Color 5 color-mix()
     4        https://bugs.webkit.org/show_bug.cgi?id=222258
     5
     6        Reviewed by Antti Koivisto.
     7
     8        * fast/css/parsing-color-mix-expected.txt: Added.
     9        * fast/css/parsing-color-mix.html: Added.
     10        Add parsing and computed style computation tests for color-mix().
     11
    1122021-02-22  Sam Weinig  <weinig@apple.com>
    213
  • trunk/Source/WTF/ChangeLog

    r273236 r273244  
     12021-02-22  Sam Weinig  <weinig@apple.com>
     2
     3        Add experimental support for CSS Color 5 color-mix()
     4        https://bugs.webkit.org/show_bug.cgi?id=222258
     5
     6        Reviewed by Antti Koivisto.
     7
     8        * Scripts/Preferences/WebPreferencesExperimental.yaml:
     9        Add new experimental preference for CSS Color 5 color-mix()
     10        which is off by default.
     11
    1122021-02-22  Carlos Garcia Campos  <cgarcia@igalia.com>
    213
  • trunk/Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml

    r273221 r273244  
    114114      default: false
    115115
     116CSSColorMixEnabled:
     117  type: bool
     118  humanReadableName: "CSS color-mix()"
     119  humanReadableDescription: "Enable support for CSS color-mix() defined in CSS Color 5"
     120  defaultValue:
     121    WebKitLegacy:
     122      default: false
     123    WebKit:
     124      default: false
     125    WebCore:
     126      default: false
     127
    116128CSSCustomPropertiesAndValuesEnabled:
    117129  type: bool
  • trunk/Source/WebCore/ChangeLog

    r273242 r273244  
     12021-02-22  Sam Weinig  <weinig@apple.com>
     2
     3        Add experimental support for CSS Color 5 color-mix()
     4        https://bugs.webkit.org/show_bug.cgi?id=222258
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Adds initial support for CSS Color 5 color-mix() - https://drafts.csswg.org/css-color-5/#color-mix
     9
     10        This feature is off by default and can be enabled via the CSSColorMixEnabled
     11        experimental preference flag.
     12
     13        This implementation has the same restriction on it that the recently landed
     14        Relative Color Syntax does in that it does support system colors or currentColor
     15        as input, since those can't be resolved at parse time. Ultimately, we will need
     16        to add a late binding version of this for those cases.
     17
     18        Test: fast/css/parsing-color-mix.html
     19
     20        * css/CSSValueKeywords.in:
     21        Add new keywords needed for color-mix().
     22
     23        * css/parser/CSSParserContext.cpp:
     24        (WebCore::operator==):
     25        * css/parser/CSSParserContext.h:
     26        (WebCore::CSSParserContextHash::hash):
     27        Add new setting for color-mix().
     28
     29        * css/parser/CSSPropertyParserHelpers.cpp:
     30        (WebCore::CSSPropertyParserHelpers::HueColorAdjuster::fixupAnglesForInterpolation):
     31        (WebCore::CSSPropertyParserHelpers::HueColorAdjuster::HueColorAdjuster):
     32        (WebCore::CSSPropertyParserHelpers::ColorAdjuster::ColorAdjuster):
     33        (WebCore::CSSPropertyParserHelpers::consumeAdjuster):
     34        (WebCore::CSSPropertyParserHelpers::consumeAndUpdateAdjusterAtIndex):
     35        (WebCore::CSSPropertyParserHelpers::consumeAndUpdateAdjuster):
     36        (WebCore::CSSPropertyParserHelpers::consumeAdjusters):
     37        (WebCore::CSSPropertyParserHelpers::consumeMixComponents):
     38        (WebCore::CSSPropertyParserHelpers::normalizeAdjusterValues):
     39        (WebCore::CSSPropertyParserHelpers::remainingAdjustment):
     40        (WebCore::CSSPropertyParserHelpers::mixComponent):
     41        (WebCore::CSSPropertyParserHelpers::mixComponentAtIndex):
     42        (WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix):
     43        (WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<HWBA<float>>):
     44        (WebCore::CSSPropertyParserHelpers::mix):
     45        (WebCore::CSSPropertyParserHelpers::parseColorMixFunctionParametersUsingAdjusters):
     46        (WebCore::CSSPropertyParserHelpers::parseColorMixFunctionParameters):
     47        (WebCore::CSSPropertyParserHelpers::parseColorFunction):
     48        The implementation uses a templatized ColorAdjuster struct to declartively map the
     49        various mixing color spaces to their allowed adjusters and what type those adjusters
     50        operate on (either double or the hue specific HueColorAdjuster). For example, for
     51        LCHA we have:
     52       
     53            using LCHColorAdjuster = ColorAdjuster<LCHA<float>, CSSValueLightness, double, CSSValueChroma, HueColorAdjuster, CSSValueHue, double, CSSValueAlpha, double>;
     54
     55        which indicates:
     56           
     57            - it creates a LCHA<float> and will operate on LCHA<float> values
     58            - its first channel is called "lightness" and is a double
     59            - its second channel is called "chroma" and is a double
     60            - its third channel is called "hue" and is a HueColorAdjuster
     61            - its fourth channel is called "alpha" and is a double
     62
     63        This data is then used by the parsing and mixing functions to implement mixing without
     64        having to write specific implementations for each mixing color space and can be expanded
     65        to more spaces if needed.
     66
     67        * platform/graphics/Color.h:
     68        (WebCore::Color::Color):
     69        Add new overloaded constructor for a generic Optional<ColorType<float>> which parallels the existing
     70        Optional<SRGBA<uint8_t>> allowing callers to convert WTF::nullopt to an invalid Color without checking
     71        for nullopt themselves.
     72
    1732021-02-19  Sergio Villar Senin  <svillar@igalia.com>
    274
  • trunk/Source/WebCore/css/CSSValueKeywords.in

    r273127 r273244  
    14351435xyz
    14361436
     1437// color-mix()
     1438color-mix
     1439shorter
     1440longer
     1441increasing
     1442decreasing
     1443specified
     1444lightness
     1445chroma
     1446whiteness
     1447blackness
     1448
    14371449// prefers-default-appearance
    14381450prefers
  • trunk/Source/WebCore/css/parser/CSSParserContext.cpp

    r273127 r273244  
    7070    , aspectRatioEnabled { document.settings().aspectRatioEnabled() }
    7171    , colorFilterEnabled { document.settings().colorFilterEnabled() }
     72    , colorMixEnabled { document.settings().cssColorMixEnabled() }
    7273    , constantPropertiesEnabled { document.settings().constantPropertiesEnabled() }
    7374    , deferredCSSParserEnabled { document.settings().deferredCSSParserEnabled() }
     
    107108        && a.aspectRatioEnabled == b.aspectRatioEnabled
    108109        && a.colorFilterEnabled == b.colorFilterEnabled
     110        && a.colorMixEnabled == b.colorMixEnabled
    109111        && a.constantPropertiesEnabled == b.constantPropertiesEnabled
    110112        && a.deferredCSSParserEnabled == b.deferredCSSParserEnabled
  • trunk/Source/WebCore/css/parser/CSSParserContext.h

    r273127 r273244  
    6060    bool aspectRatioEnabled { false };
    6161    bool colorFilterEnabled { false };
     62    bool colorMixEnabled { false };
    6263    bool constantPropertiesEnabled { false };
    6364    bool deferredCSSParserEnabled { false };
     
    103104        if (!key.charset.isEmpty())
    104105            hash ^= StringHash::hash(key.charset);
     106       
    105107        unsigned bits = key.isHTMLDocument                  << 0
    106108            & key.hasDocumentSecurityOrigin                 << 1
     
    109111            & key.aspectRatioEnabled                        << 4
    110112            & key.colorFilterEnabled                        << 5
    111             & key.constantPropertiesEnabled                 << 6
    112             & key.deferredCSSParserEnabled                  << 7
    113             & key.enforcesCSSMIMETypeInNoQuirksMode         << 8
    114             & key.individualTransformPropertiesEnabled      << 9
     113            & key.colorMixEnabled                           << 6
     114            & key.constantPropertiesEnabled                 << 7
     115            & key.deferredCSSParserEnabled                  << 8
     116            & key.enforcesCSSMIMETypeInNoQuirksMode         << 9
     117            & key.individualTransformPropertiesEnabled      << 10
    115118#if ENABLE(OVERFLOW_SCROLLING_TOUCH)
    116             & key.legacyOverflowScrollingTouchEnabled       << 10
     119            & key.legacyOverflowScrollingTouchEnabled       << 11
    117120#endif
    118             & key.overscrollBehaviorEnabled                 << 11
    119             & key.relativeColorSyntaxEnabled                << 12
    120             & key.scrollBehaviorEnabled                     << 13
    121             & key.springTimingFunctionEnabled               << 14
     121            & key.overscrollBehaviorEnabled                 << 12
     122            & key.relativeColorSyntaxEnabled                << 13
     123            & key.scrollBehaviorEnabled                     << 14
     124            & key.springTimingFunctionEnabled               << 15
    122125#if ENABLE(TEXT_AUTOSIZING)
    123             & key.textAutosizingEnabled                     << 15
     126            & key.textAutosizingEnabled                     << 16
    124127#endif
    125128#if ENABLE(CSS_TRANSFORM_STYLE_OPTIMIZED_3D)
    126             & key.transformStyleOptimized3DEnabled          << 16
     129            & key.transformStyleOptimized3DEnabled          << 17
    127130#endif
    128             & key.useLegacyBackgroundSizeShorthandBehavior  << 17
    129             & key.focusVisibleEnabled                       << 18
     131            & key.useLegacyBackgroundSizeShorthandBehavior  << 18
     132            & key.focusVisibleEnabled                       << 19
    130133#if ENABLE(ATTACHMENT_ELEMENT)
    131             & key.attachmentEnabled                         << 19
     134            & key.attachmentEnabled                         << 20
    132135#endif
    133             & key.mode                                      << 20; // Keep this last.
     136            & key.mode                                      << 21; // Keep this last.
    134137        hash ^= WTF::intHash(bits);
    135138        return hash;
  • trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp

    r273211 r273244  
    14811481}
    14821482
     1483struct HueColorAdjuster {
     1484    enum class Type {
     1485        Shorter,
     1486        Longer,
     1487        Increasing,
     1488        Decreasing,
     1489        Specified
     1490    };
     1491
     1492    static std::pair<double, double> fixupAnglesForInterpolation(double theta1, double theta2, Type type)
     1493    {
     1494        ASSERT(theta1 >= 0.0);
     1495        ASSERT(theta1 <= 360.0);
     1496        ASSERT(theta2 >= 0.0);
     1497        ASSERT(theta2 <= 360.0);
     1498
     1499        switch (type) {
     1500        case Type::Shorter: {
     1501            auto difference = theta2 - theta1;
     1502            if (difference > 180.0)
     1503                return { theta1 + 360.0, theta2 };
     1504            if (difference < -180.0)
     1505                return { theta1, theta2 + 360.0 };
     1506            return { theta1, theta2 };
     1507        }
     1508        case Type::Longer: {
     1509            auto difference = theta2 - theta1;
     1510            if (difference >= 0.0 && difference < 180.0)
     1511                return { theta1 + 360.0, theta2 };
     1512            if (difference >= -180.0  && difference < 0)
     1513                return { theta1, theta2 + 360.0 };
     1514            return { theta1, theta2 };
     1515        }
     1516        case Type::Increasing: {
     1517            if (theta2 < theta1)
     1518                return { theta1, theta2 + 360.0 };
     1519            return { theta1, theta2 };
     1520        }
     1521        case Type::Decreasing: {
     1522            if (theta1 < theta2)
     1523                return { theta1 + 360.0, theta2 };
     1524            return { theta1, theta2 };
     1525        }
     1526        case Type::Specified:
     1527            return { theta1, theta2 };
     1528        }
     1529    }
     1530
     1531    HueColorAdjuster(double value = 0.0, Type type = Type::Shorter)
     1532        : type { type }
     1533        , value { value }
     1534    {
     1535    }
     1536
     1537    Type type;
     1538    double value;
     1539};
     1540
     1541template<typename C, CSSValueID ID0, typename Channel0, CSSValueID ID1, typename Channel1, CSSValueID ID2, typename Channel2, CSSValueID ID3, typename Channel3>
     1542struct ColorAdjuster {
     1543    using ColorType = C;
     1544    static constexpr auto channelCSSValueIDs = std::make_tuple(ID0, ID1, ID2, ID3);
     1545
     1546    ColorAdjuster() = default;
     1547    explicit ColorAdjuster(double percentage)
     1548        : channels { percentage, percentage, percentage, percentage }
     1549    {
     1550    }
     1551
     1552    std::tuple<Optional<Channel0>, Optional<Channel1>, Optional<Channel2>, Optional<Channel3>> channels;
     1553};
     1554
     1555using HSLColorAdjuster = ColorAdjuster<HSLA<float>, CSSValueHue, HueColorAdjuster, CSSValueSaturation, double, CSSValueLightness, double, CSSValueAlpha, double>;
     1556using HWBColorAdjuster = ColorAdjuster<HWBA<float>, CSSValueHue, HueColorAdjuster, CSSValueWhiteness, double, CSSValueBlackness, double, CSSValueAlpha, double>;
     1557using LCHColorAdjuster = ColorAdjuster<LCHA<float>, CSSValueLightness, double, CSSValueChroma, HueColorAdjuster, CSSValueHue, double, CSSValueAlpha, double>;
     1558using LabColorAdjuster = ColorAdjuster<Lab<float>, CSSValueLightness, double, CSSValueA, double, CSSValueB, double, CSSValueAlpha, double>;
     1559using SRGBColorAdjuster = ColorAdjuster<SRGBA<float>, CSSValueRed, double, CSSValueGreen, double, CSSValueBlue, double, CSSValueAlpha, double>;
     1560using XYZColorAdjuster = ColorAdjuster<XYZA<float, WhitePoint::D50>, CSSValueX, double, CSSValueY, double, CSSValueZ, double, CSSValueAlpha, double>;
     1561
     1562template<typename Adjuster> struct ColorMixComponent {
     1563    Color color;
     1564    Adjuster adjuster;
     1565};
     1566
     1567template<CSSValueID Ident, typename T> struct AdjusterConsumer;
     1568
     1569template<CSSValueID Ident> struct AdjusterConsumer<Ident, HueColorAdjuster> {
     1570    static Optional<HueColorAdjuster> consume(CSSParserTokenRange& args)
     1571    {
     1572        if (!consumeIdentRaw<Ident>(args))
     1573            return WTF::nullopt;
     1574
     1575        HueColorAdjuster result;
     1576        if (auto hueAdjustmentType = consumeHueAdjustmentType(args))
     1577            result.type = *hueAdjustmentType;
     1578
     1579        // FIXME: Is clamping to 0 for negative percentages the right thing to do?
     1580        if (auto percentage = consumePercentRaw(args))
     1581            result.value = std::max(0.0, *percentage);
     1582
     1583        // FIXME: Should an adjuster without a percetange be allowed?
     1584        //  e.g color-mix(hsl, teal hue, red);
     1585
     1586        return result;
     1587    }
     1588
     1589    static Optional<HueColorAdjuster::Type> consumeHueAdjustmentType(CSSParserTokenRange& args)
     1590    {
     1591        switch (args.peek().id()) {
     1592        case CSSValueShorter:
     1593            consumeIdentRaw(args);
     1594            return HueColorAdjuster::Type::Shorter;
     1595        case CSSValueLonger:
     1596            consumeIdentRaw(args);
     1597            return HueColorAdjuster::Type::Longer;
     1598        case CSSValueIncreasing:
     1599            consumeIdentRaw(args);
     1600            return HueColorAdjuster::Type::Increasing;
     1601        case CSSValueDecreasing:
     1602            consumeIdentRaw(args);
     1603            return HueColorAdjuster::Type::Decreasing;
     1604        case CSSValueSpecified:
     1605            consumeIdentRaw(args);
     1606            return HueColorAdjuster::Type::Specified;
     1607        default:
     1608            return WTF::nullopt;
     1609        }
     1610    }
     1611};
     1612
     1613template<CSSValueID Ident> struct AdjusterConsumer<Ident, double> {
     1614    static Optional<double> consume(CSSParserTokenRange& args)
     1615    {
     1616        if (!consumeIdentRaw<Ident>(args))
     1617            return WTF::nullopt;
     1618       
     1619        // FIXME: Is clamping to 0 for negative percentages the right thing to do?
     1620        if (auto percentage = consumePercentRaw(args))
     1621            return std::max(0.0, *percentage);
     1622
     1623        // FIXME: Should an adjuster without a percetange be allowed?
     1624        //  e.g color-mix(hsl, teal saturation, red);
     1625
     1626        return 0;
     1627    }
     1628};
     1629
     1630template<CSSValueID Ident, typename T> inline decltype(auto) consumeAdjuster(CSSParserTokenRange& args)
     1631{
     1632    return AdjusterConsumer<Ident, T>::consume(args);
     1633}
     1634
     1635template<std::size_t I, typename Adjuster> static bool consumeAndUpdateAdjusterAtIndex(CSSParserTokenRange& args, Adjuster& adjuster)
     1636{
     1637    using AdjusterType = std::decay_t<decltype(std::get<I>(adjuster.channels).value())>;
     1638    static constexpr CSSValueID Ident = std::get<I>(Adjuster::channelCSSValueIDs);
     1639
     1640    if (auto adjustment = consumeAdjuster<Ident, AdjusterType>(args)) {
     1641        std::get<I>(adjuster.channels) = *adjustment;
     1642        return true;
     1643    }
     1644    return false;
     1645}
     1646
     1647template<typename Adjuster> static bool consumeAndUpdateAdjuster(CSSParserTokenRange& args, Adjuster& adjuster)
     1648{
     1649    if (consumeAndUpdateAdjusterAtIndex<0>(args, adjuster))
     1650        return true;
     1651    if (consumeAndUpdateAdjusterAtIndex<1>(args, adjuster))
     1652        return true;
     1653    if (consumeAndUpdateAdjusterAtIndex<2>(args, adjuster))
     1654        return true;
     1655    if (consumeAndUpdateAdjusterAtIndex<3>(args, adjuster))
     1656        return true;
     1657    return false;
     1658}
     1659
     1660template<typename Adjuster> static Adjuster consumeAdjusters(CSSParserTokenRange& args)
     1661{
     1662    Adjuster adjuster;
     1663    while (consumeAndUpdateAdjuster(args, adjuster)) {
     1664        // Keep consuming until there are no more adjusters.
     1665    }
     1666   
     1667    return adjuster;
     1668}
     1669
     1670template<typename Adjuster> static Optional<ColorMixComponent<Adjuster>> consumeMixComponents(CSSParserTokenRange& args, const CSSParserContext& context)
     1671{
     1672    auto originColor = consumeOriginColor(args, context);
     1673    if (!originColor.isValid())
     1674        return WTF::nullopt;
     1675
     1676    // FIXME: Is clamping to 0 for negative percentages the right thing to do?
     1677    if (auto percentage = consumePercentRaw(args))
     1678        return { { WTFMove(originColor), Adjuster { std::max(0.0, *percentage) } } };
     1679
     1680    return { { WTFMove(originColor), consumeAdjusters<Adjuster>(args) } };
     1681}
     1682
     1683static std::pair<HueColorAdjuster, HueColorAdjuster> normalizeAdjusterValues(HueColorAdjuster adjuster1, HueColorAdjuster adjuster2)
     1684{
     1685    if (auto sum = adjuster1.value + adjuster2.value; sum != 100.0) {
     1686        adjuster1.value *= 100.0 / sum;
     1687        adjuster2.value *= 100.0 / sum;
     1688    }
     1689
     1690    return { adjuster1, adjuster2 };
     1691}
     1692
     1693static std::pair<double, double> normalizeAdjusterValues(double adjuster1, double adjuster2)
     1694{
     1695    if (auto sum = adjuster1 + adjuster2; sum != 100.0) {
     1696        adjuster1 *= 100.0 / sum;
     1697        adjuster2 *= 100.0 / sum;
     1698    }
     1699
     1700    return { adjuster1, adjuster2 };
     1701}
     1702
     1703static HueColorAdjuster remainingAdjustment(HueColorAdjuster adjuster)
     1704{
     1705    return { 100.0 - adjuster.value, adjuster.type };
     1706}
     1707
     1708static double remainingAdjustment(double adjuster)
     1709{
     1710    return 100.0 - adjuster;
     1711}
     1712
     1713template<typename AdjusterType> static auto normalizeAdjusterValues(Optional<AdjusterType> adjuster1, Optional<AdjusterType> adjuster2) -> std::pair<AdjusterType, AdjusterType>
     1714{
     1715    if (adjuster1 && adjuster2)
     1716        return normalizeAdjusterValues(*adjuster1, *adjuster2);
     1717    if (!adjuster1 && adjuster2)
     1718        return { remainingAdjustment(*adjuster2), *adjuster2 };
     1719    if (adjuster1 && !adjuster2)
     1720        return { *adjuster1, remainingAdjustment(*adjuster1) };
     1721    // When neigher mix component provides an adjuster, the result is the non-modified
     1722    // channel from from the first color.
     1723    ASSERT(!adjuster1 && !adjuster2);
     1724    return { 100.0, 0.0 };
     1725}
     1726
     1727static double mixComponent(float component1, HueColorAdjuster adjustment1, float component2, HueColorAdjuster adjustment2)
     1728{
     1729    // FIXME: The spec does not indicate what to do if two different hue types are specified. We always use the first one for now,
     1730    // though we probably should take into account whether it was actually specified or is the default value. That normalization
     1731    // should happen in normalizeAdjusterValues().
     1732
     1733    auto [fixedUpComponent1, fixedUpComponent2] = HueColorAdjuster::fixupAnglesForInterpolation(component1, component2, adjustment1.type);
     1734    auto result = (fixedUpComponent1 * (adjustment1.value / 100.0)) + (fixedUpComponent2 * (adjustment2.value / 100.0));
     1735    // FIXME: Check if this full normalization is needed.
     1736    return normalizeHue(result);
     1737}
     1738
     1739static double mixComponent(float component1, double adjustment1, float component2, double adjustment2)
     1740{
     1741    return (component1 * (adjustment1 / 100.0)) + (component2 * (adjustment2 / 100.0));
     1742}
     1743
     1744template<std::size_t I, typename Adjuster> static double mixComponentAtIndex(const ColorComponents<float>& color1, const Adjuster& adjuster1, const ColorComponents<float>& color2, const Adjuster& adjuster2)
     1745{
     1746    auto [normalizedAdjuster1Value, normalizedAdjuster2Value] = normalizeAdjusterValues(std::get<I>(adjuster1.channels), std::get<I>(adjuster2.channels));
     1747    return mixComponent(color1[I], normalizedAdjuster1Value, color2[I], normalizedAdjuster2Value);
     1748}
     1749
     1750template<typename ColorType> inline ColorType makeColorTypeByNormalizingComponentsAfterMix(double channel0, double channel1, double channel2, double channel3)
     1751{
     1752    return { static_cast<float>(channel0), static_cast<float>(channel1), static_cast<float>(channel2), static_cast<float>(channel3) };
     1753}
     1754
     1755template<> inline HWBA<float> makeColorTypeByNormalizingComponentsAfterMix<HWBA<float>>(double hue, double whiteness, double blackness, double alpha)
     1756{
     1757    auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(whiteness, blackness);
     1758    return { static_cast<float>(hue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(alpha) };
     1759}
     1760
     1761template<typename Adjuster> static typename Adjuster::ColorType mix(const ColorMixComponent<Adjuster>& mixComponents1, const ColorMixComponent<Adjuster>& mixComponents2)
     1762{
     1763    using ColorType = typename Adjuster::ColorType;
     1764
     1765    auto color1 = asColorComponents(mixComponents1.color.template toColorTypeLossy<ColorType>());
     1766    auto color2 = asColorComponents(mixComponents2.color.template toColorTypeLossy<ColorType>());
     1767
     1768    auto adjuster1 = mixComponents1.adjuster;
     1769    auto adjuster2 = mixComponents2.adjuster;
     1770
     1771    if (!std::get<0>(adjuster1.channels) && !std::get<1>(adjuster1.channels) && !std::get<2>(adjuster1.channels) && !std::get<3>(adjuster1.channels) && !std::get<0>(adjuster2.channels) && !std::get<1>(adjuster2.channels) && !std::get<2>(adjuster2.channels) && !std::get<3>(adjuster2.channels)) {
     1772        // No adjusters being specified at all is special cased to mean mix 50-50.
     1773        adjuster1 = Adjuster { 50.0 };
     1774        adjuster2 = Adjuster { 50.0 };
     1775    }
     1776
     1777    auto channel0 = mixComponentAtIndex<0>(color1, adjuster1, color2, adjuster2);
     1778    auto channel1 = mixComponentAtIndex<1>(color1, adjuster1, color2, adjuster2);
     1779    auto channel2 = mixComponentAtIndex<2>(color1, adjuster1, color2, adjuster2);
     1780    auto channel3 = mixComponentAtIndex<3>(color1, adjuster1, color2, adjuster2);
     1781
     1782    return makeColorTypeByNormalizingComponentsAfterMix<ColorType>(channel0, channel1, channel2, channel3);
     1783}
     1784
     1785template<typename Adjuster> static Optional<typename Adjuster::ColorType> parseColorMixFunctionParametersUsingAdjusters(CSSParserTokenRange& args, const CSSParserContext& context)
     1786{
     1787    auto mixComponents1 = consumeMixComponents<Adjuster>(args, context);
     1788    if (!mixComponents1)
     1789        return WTF::nullopt;
     1790
     1791    // FIXME: This comma is not in the grammar, but is in all the examples.
     1792    if (!consumeCommaIncludingWhitespace(args))
     1793        return WTF::nullopt;
     1794
     1795    auto mixComponents2 = consumeMixComponents<Adjuster>(args, context);
     1796    if (!mixComponents2)
     1797        return WTF::nullopt;
     1798
     1799    return mix(*mixComponents1, *mixComponents2);
     1800}
     1801
     1802static Color parseColorMixFunctionParameters(CSSParserTokenRange& range, const CSSParserContext& context)
     1803{
     1804    ASSERT(range.peek().functionId() == CSSValueColorMix);
     1805
     1806    if (!context.colorMixEnabled)
     1807        return { };
     1808
     1809    auto args = consumeFunction(range);
     1810
     1811    auto consumeIdentAndComma = [](CSSParserTokenRange& args) {
     1812        consumeIdentRaw(args);
     1813        // FIXME: This comma is not in the grammar, but is in all the examples.
     1814        return consumeCommaIncludingWhitespace(args);
     1815    };
     1816
     1817    switch (args.peek().id()) {
     1818    case CSSValueHsl: {
     1819        if (!consumeIdentAndComma(args))
     1820            return { };
     1821        auto hsl = parseColorMixFunctionParametersUsingAdjusters<HSLColorAdjuster>(args, context);
     1822        if (!hsl)
     1823            return { };
     1824        return convertColor<SRGBA<uint8_t>>(*hsl);
     1825    }
     1826    case CSSValueHwb: {
     1827        if (!consumeIdentAndComma(args))
     1828            return { };
     1829        auto hwb = parseColorMixFunctionParametersUsingAdjusters<HWBColorAdjuster>(args, context);
     1830        if (!hwb)
     1831            return { };
     1832        return convertColor<SRGBA<uint8_t>>(*hwb);
     1833    }
     1834    case CSSValueLch:
     1835        if (!consumeIdentAndComma(args))
     1836            return { };
     1837        return parseColorMixFunctionParametersUsingAdjusters<LCHColorAdjuster>(args, context);
     1838    case CSSValueLab:
     1839        if (!consumeIdentAndComma(args))
     1840            return { };
     1841        return parseColorMixFunctionParametersUsingAdjusters<LabColorAdjuster>(args, context);
     1842    case CSSValueXyz:
     1843        if (!consumeIdentAndComma(args))
     1844            return { };
     1845        return parseColorMixFunctionParametersUsingAdjusters<XYZColorAdjuster>(args, context);
     1846    case CSSValueSRGB:
     1847        if (!consumeIdentAndComma(args))
     1848            return { };
     1849        return parseColorMixFunctionParametersUsingAdjusters<SRGBColorAdjuster>(args, context);
     1850    default:
     1851        // Default to using LCH if no color space is provided as per the spec.
     1852        // FIXME: This behavior is unnecessarily confusing, we should remove the default from the spec.
     1853        return parseColorMixFunctionParametersUsingAdjusters<LCHColorAdjuster>(args, context);
     1854    }
     1855}
     1856
    14831857static Optional<SRGBA<uint8_t>> parseHexColor(CSSParserTokenRange& range, bool acceptQuirkyColors)
    14841858{
     
    15471921    case CSSValueColor:
    15481922        color = parseColorFunctionParameters(colorRange);
     1923        break;
     1924    case CSSValueColorMix:
     1925        color = parseColorMixFunctionParameters(colorRange, context);
    15491926        break;
    15501927    default:
  • trunk/Source/WebCore/platform/graphics/Color.h

    r273211 r273244  
    7474
    7575    Color(ColorComponents<float>, ColorSpace, OptionSet<Flags> = { });
     76   
    7677    template<typename ColorType, typename std::enable_if_t<IsColorTypeWithComponentType<ColorType, float>>* = nullptr>
    7778    Color(const ColorType&, OptionSet<Flags> = { });
     79   
     80    template<typename ColorType, typename std::enable_if_t<IsColorTypeWithComponentType<ColorType, float>>* = nullptr>
     81    Color(const Optional<ColorType>&, OptionSet<Flags> = { });
    7882
    7983    explicit Color(WTF::HashTableEmptyValueType);
     
    299303{
    300304    setExtendedColor(ExtendedColor::create(color), toFlagsIncludingPrivate(flags));
     305}
     306
     307template<typename ColorType, typename std::enable_if_t<IsColorTypeWithComponentType<ColorType, float>>*>
     308inline Color::Color(const Optional<ColorType>& color, OptionSet<Flags> flags)
     309{
     310    if (color)
     311        setExtendedColor(ExtendedColor::create(*color), toFlagsIncludingPrivate(flags));
    301312}
    302313
Note: See TracChangeset for help on using the changeset viewer.