Changeset 282443 in webkit


Ignore:
Timestamp:
Sep 15, 2021 12:26:18 AM (10 months ago)
Author:
Said Abou-Hallawa
Message:

Linear gradient sometimes is drawn incorrectly and sometimes hangs
https://bugs.webkit.org/show_bug.cgi?id=230145
<rdar://82428383>

Reviewed by Simon Fraser.

Source/WebCore:

Gradient::paint() has a few flaws:

1) We have to use atan2() instead of acos() to get the angle of the vector

(p0, p1) in the correct quadrant. acos() returns an angle between [0, pi].

2) The order in the case of 'repeat' and 'reflect' should be the following:

-- Move the points forward towards the bounding box.
-- Draw gradient and move the points forward till they are not

intersecting the bounding box.

-- Move the points backward towards the bounding box.
-- Draw gradient and move the points backward till they are not

intersecting the bounding box.

Tests: svg/gradients/gradient-flipped-start-end-points-expected.svg

svg/gradients/gradient-flipped-start-end-points.svg

  • platform/graphics/cg/GradientCG.cpp:

(WebCore::Gradient::paint):

LayoutTests:

  • svg/gradients/gradient-flipped-start-end-points-expected.svg: Added.
  • svg/gradients/gradient-flipped-start-end-points.svg: Added.
Location:
trunk
Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r282442 r282443  
     12021-09-15  Said Abou-Hallawa  <said@apple.com>
     2
     3        Linear gradient sometimes is drawn incorrectly and sometimes hangs
     4        https://bugs.webkit.org/show_bug.cgi?id=230145
     5        <rdar://82428383>
     6
     7        Reviewed by Simon Fraser.
     8
     9        * svg/gradients/gradient-flipped-start-end-points-expected.svg: Added.
     10        * svg/gradients/gradient-flipped-start-end-points.svg: Added.
     11
    1122021-09-15  Myles C. Maxfield  <mmaxfield@apple.com>
    213
  • trunk/Source/WebCore/ChangeLog

    r282441 r282443  
     12021-09-15  Said Abou-Hallawa  <said@apple.com>
     2
     3        Linear gradient sometimes is drawn incorrectly and sometimes hangs
     4        https://bugs.webkit.org/show_bug.cgi?id=230145
     5        <rdar://82428383>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Gradient::paint() has a few flaws:
     10
     11        1) We have to use atan2() instead of acos() to get the angle of the vector
     12           (p0, p1) in the correct quadrant. acos() returns an angle between [0, pi].
     13
     14        2) The order in the case of 'repeat' and 'reflect' should be the following:
     15            -- Move the points forward towards the bounding box.
     16            -- Draw gradient and move the points forward till they are not
     17               intersecting the bounding box.
     18            -- Move the points backward towards the bounding box.
     19            -- Draw gradient and move the points backward till they are not
     20               intersecting the bounding box.
     21
     22        Tests: svg/gradients/gradient-flipped-start-end-points-expected.svg
     23               svg/gradients/gradient-flipped-start-end-points.svg
     24
     25        * platform/graphics/cg/GradientCG.cpp:
     26        (WebCore::Gradient::paint):
     27
    1282021-09-14  Simon Fraser  <simon.fraser@apple.com>
    229
  • trunk/Source/WebCore/platform/graphics/cg/GradientCG.cpp

    r281488 r282443  
    120120            case GradientSpreadMethod::Reflect: {
    121121                CGContextStateSaver saveState(platformContext);
     122                extendOptions = 0;
    122123
    123124                FloatPoint gradientVectorNorm(data.point1 - data.point0);
    124125                gradientVectorNorm.normalize();
    125                 CGFloat angle = acos(gradientVectorNorm.dot({ 1, 0 }));
     126                CGFloat angle = gradientVectorNorm.isZero() ? 0 : atan2(gradientVectorNorm.y(), gradientVectorNorm.x());
    126127                CGContextRotateCTM(platformContext, angle);
     128
     129                CGRect boundingBox = CGContextGetClipBoundingBox(platformContext);
     130                if (CGRectIsInfinite(boundingBox) || CGRectIsEmpty(boundingBox))
     131                    break;
    127132
    128133                CGAffineTransform transform = CGAffineTransformMakeRotation(-angle);
    129134                FloatPoint point0 = CGPointApplyAffineTransform(data.point0, transform);
    130135                FloatPoint point1 = CGPointApplyAffineTransform(data.point1, transform);
    131 
    132                 CGRect boundingBox = CGContextGetClipBoundingBox(platformContext);
    133                 CGFloat width = point1.x() - point0.x();
     136                CGFloat dx = point1.x() - point0.x();
     137
    134138                CGFloat pixelSize = CGFAbs(CGContextConvertSizeToUserSpace(platformContext, CGSizeMake(1, 1)).width);
    135 
    136                 if (width > 0 && !CGRectIsInfinite(boundingBox) && !CGRectIsEmpty(boundingBox)) {
    137                     extendOptions = 0;
    138                     if (width < pixelSize)
    139                         width = pixelSize;
    140 
    141                     CGFloat gradientStart = point0.x();
    142                     CGFloat gradientEnd = point1.x();
    143                     bool flip = m_spreadMethod == GradientSpreadMethod::Reflect;
    144 
    145                     // Find first gradient position to the left of the bounding box
    146                     int n = CGFloor((boundingBox.origin.x - gradientStart) / width);
    147                     gradientStart += n * width;
    148                     if (!(n % 2))
    149                         flip = false;
    150 
    151                     gradientEnd -= CGFloor((gradientEnd - CGRectGetMaxX(boundingBox)) / width) * width;
    152 
    153                     for (CGFloat start = gradientStart; start <= gradientEnd; start += width) {
    154                         CGPoint left = CGPointMake(flip ? start + width : start, boundingBox.origin.y);
    155                         CGPoint right = CGPointMake(flip ? start : start + width, boundingBox.origin.y);
    156 
    157                         CGContextDrawLinearGradient(platformContext, m_gradient.get(), left, right, extendOptions);
    158 
    159                         if (m_spreadMethod == GradientSpreadMethod::Reflect)
    160                             flip = !flip;
    161                     }
    162 
    163                     break;
    164                 }
    165 
    166                 FALLTHROUGH;
     139                if (CGFAbs(dx) < pixelSize)
     140                    dx = dx < 0 ? -pixelSize : pixelSize;
     141
     142                auto drawLinearGradient = [&](CGFloat start, CGFloat end, bool flip) {
     143                    CGPoint left = CGPointMake(flip ? end : start, 0);
     144                    CGPoint right = CGPointMake(flip ? start : end, 0);
     145
     146                    CGContextDrawLinearGradient(platformContext, m_gradient.get(), left, right, extendOptions);
     147                };
     148
     149                auto isLeftOf = [](CGFloat start, CGFloat end, CGRect boundingBox) -> bool {
     150                    return std::max(start, end) <= CGRectGetMinX(boundingBox);
     151                };
     152
     153                auto isRightOf = [](CGFloat start, CGFloat end, CGRect boundingBox) -> bool {
     154                    return std::min(start, end) >= CGRectGetMaxX(boundingBox);
     155                };
     156
     157                auto isIntersecting = [](CGFloat start, CGFloat end, CGRect boundingBox) -> bool {
     158                    return std::min(start, end) < CGRectGetMaxX(boundingBox) && CGRectGetMinX(boundingBox) < std::max(start, end);
     159                };
     160
     161                bool flip = false;
     162                CGFloat start = point0.x();
     163
     164                // Should the points be moved forward towards boundingBox?
     165                if ((dx > 0 && isLeftOf(start, start + dx, boundingBox)) || (dx < 0 && isRightOf(start, start + dx, boundingBox))) {
     166                    // Move the 'start' point towards boundingBox.
     167                    for (; !isIntersecting(start, start + dx, boundingBox); start += dx)
     168                        flip = !flip && m_spreadMethod == GradientSpreadMethod::Reflect;
     169                }
     170
     171                // Draw gradient forward till the points are outside boundingBox.
     172                for (; isIntersecting(start, start + dx, boundingBox); start += dx) {
     173                    drawLinearGradient(start, start + dx, flip);
     174                    flip = !flip && m_spreadMethod == GradientSpreadMethod::Reflect;
     175                }
     176
     177                flip = m_spreadMethod == GradientSpreadMethod::Reflect;
     178                CGFloat end = point0.x();
     179
     180                // Should the points be moved backward towards boundingBox?
     181                if ((dx < 0 && isLeftOf(end, end - dx, boundingBox)) || (dx > 0 && isRightOf(end, end - dx, boundingBox))) {
     182                    // Move the 'end' point towards boundingBox.
     183                    for (; !isIntersecting(end, end - dx, boundingBox); end -= dx)
     184                        flip = !flip && m_spreadMethod == GradientSpreadMethod::Reflect;
     185                }
     186
     187                // Draw gradient backward till the points are outside boundingBox.
     188                for (; isIntersecting(end, end - dx, boundingBox); end -= dx) {
     189                    drawLinearGradient(end - dx, end, flip);
     190                    flip = !flip && m_spreadMethod == GradientSpreadMethod::Reflect;
     191                }
     192
     193                break;
    167194            }
    168195            case GradientSpreadMethod::Pad:
Note: See TracChangeset for help on using the changeset viewer.