Changeset 117709 in webkit


Ignore:
Timestamp:
May 20, 2012 12:49:45 PM (12 years ago)
Author:
pdr@google.com
Message:

Fix hit testing on non-scaling strokes
https://bugs.webkit.org/show_bug.cgi?id=82628

Reviewed by Nikolas Zimmermann.

Source/WebCore:

This change fixes hit testing on non-scaling strokes. It contains fixes for 3 bugs:
1) RenderSVGRect::shapeDependentStrokeContains was not falling back to shape-based hit testing.
2) m_strokeAndMarkerBoundingBox did not account for non-scaling strokes.
3) RenderSVGShape::shapeDependentStrokeContains did not have any support for non-scaling strokes.

This change also contains some refactoring/cleanup of the non-scale-stroke codepaths.

Test: svg/hittest/svg-shapes-non-scale-stroke.html

  • rendering/svg/RenderSVGEllipse.cpp:

(WebCore::RenderSVGEllipse::createShape):

  • rendering/svg/RenderSVGRect.cpp:

(WebCore::RenderSVGRect::createShape):
(WebCore::RenderSVGRect::shapeDependentStrokeContains):

  • rendering/svg/RenderSVGShape.cpp:

(WebCore::RenderSVGShape::shapeDependentStrokeContains):
(WebCore::RenderSVGShape::shapeDependentFillContains):
(WebCore::RenderSVGShape::nonScalingStrokePath):
(WebCore):
(WebCore::RenderSVGShape::setupNonScalingStrokeContext):
(WebCore::RenderSVGShape::nonScalingStrokeTransform):
(WebCore::RenderSVGShape::strokePath):
(WebCore::RenderSVGShape::fillAndStrokePath):
(WebCore::RenderSVGShape::updateCachedBoundaries):
(WebCore::RenderSVGShape::inflateWithStrokeAndMarkerBounds):

  • rendering/svg/RenderSVGShape.h:

(WebCore::RenderSVGShape::hasNonScalingStroke):
(RenderSVGShape):

LayoutTests:

  • platform/chromium/test_expectations.txt:
  • platform/mac/Skipped:
  • svg/custom/non-scaling-stroke-expected.txt:
  • svg/hittest/svg-shapes-non-scale-stroke-expected.txt: Added.
  • svg/hittest/svg-shapes-non-scale-stroke.html: Added.
Location:
trunk
Files:
2 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r117702 r117709  
     12012-05-20  Philip Rogers  <pdr@google.com>
     2
     3        Fix hit testing on non-scaling strokes
     4        https://bugs.webkit.org/show_bug.cgi?id=82628
     5
     6        Reviewed by Nikolas Zimmermann.
     7
     8        * platform/chromium/test_expectations.txt:
     9        * platform/mac/Skipped:
     10        * svg/custom/non-scaling-stroke-expected.txt:
     11        * svg/hittest/svg-shapes-non-scale-stroke-expected.txt: Added.
     12        * svg/hittest/svg-shapes-non-scale-stroke.html: Added.
     13
    1142012-05-19  Joshua Bell  <jsbell@chromium.org>
    215
  • trunk/LayoutTests/platform/chromium/test_expectations.txt

    r117677 r117709  
    12811281BUGWK83875 LINUX : svg/text/font-size-below-point-five.svg = PASS TEXT
    12821282
    1283 BUGWK82628 : svg/hittest/svg-ellipse-non-scale-stroke.xhtml = FAIL
    1284 
    12851283// It is very hard to see why some of these are failing. The diff must be tiny in some cases.
    12861284// Other fail intermittently in a way that is odd - clipping and masking should be stable.
  • trunk/LayoutTests/platform/mac/Skipped

    r117602 r117709  
    643643fast/table/cell-pref-width-invalidation.html
    644644
    645 # https://bugs.webkit.org/show_bug.cgi?id=82628
    646 svg/hittest/svg-ellipse-non-scale-stroke.xhtml
    647 
    648645# The inner <input> should now grow to take the full cell's height.
    649646fast/table/colspanMinWidth-vertical.html
  • trunk/LayoutTests/svg/custom/non-scaling-stroke-expected.txt

    r109097 r117709  
    22  RenderView at (0,0) size 800x600
    33layer at (0,0) size 800x600
    4   RenderSVGRoot {svg} at (18,12) size 259x306
     4  RenderSVGRoot {svg} at (12,0) size 287x488
    55    RenderSVGHiddenContainer {defs} at (0,0) size 0x0
    66      RenderSVGResourceLinearGradient {linearGradient} [id="grad1"] [gradientUnits=objectBoundingBox] [start=(0,0)] [end=(1,1)]
     
    1616        RenderSVGRect {rect} at (10,10) size 10x10 [fill={[type=SOLID] [color=#FFFF00]}] [x=10.00] [y=10.00] [width=10.00] [height=10.00]
    1717      RenderSVGRect {rect} at (0,0) size 400x50 [x=0.00] [y=0.00] [width=400.00] [height=50.00]
    18     RenderSVGContainer {g} at (18,12) size 104x66 [transform={m=((1.00,0.00)(0.00,1.00)) t=(20.00,20.00)}]
    19       RenderSVGRect {rect} at (18,12) size 104x66 [transform={m=((0.25,0.00)(0.00,1.00)) t=(0.00,0.00)}] [stroke={[type=LINEAR-GRADIENT] [id="grad1"] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
    20     RenderSVGRect {rect} at (18,92) size 104x66 [transform={m=((0.25,0.00)(0.00,1.00)) t=(20.00,100.00)}] [stroke={[type=LINEAR-GRADIENT] [id="grad2"] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
    21     RenderSVGContainer {use} at (18,172) size 104x66 [transform={m=((0.25,0.00)(0.00,1.00)) t=(20.00,180.00)}]
    22       RenderSVGRect {rect} at (18,172) size 104x66 [stroke={[type=PATTERN] [id="pattern"] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
    23     RenderSVGContainer {use} at (18,252) size 104x66 [transform={m=((0.25,0.00)(0.00,1.00)) t=(20.00,260.00)}]
    24       RenderSVGRect {rect} at (18,252) size 104x66 [stroke={[type=SOLID] [color=#008000] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
    25     RenderSVGContainer {use} at (156,12) size 121x66 [transform={m=((0.25,0.00)(0.25,1.00)) t=(160.00,20.00)}]
    26       RenderSVGRect {rect} at (156,12) size 121x66 [stroke={[type=SOLID] [color=#008000] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
    27     RenderSVGContainer {use} at (158,89) size 104x217 [transform={m=((0.25,0.36)(0.00,1.00)) t=(160.00,100.00)}]
    28       RenderSVGRect {rect} at (158,89) size 104x217 [stroke={[type=SOLID] [color=#008000] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
     18    RenderSVGContainer {g} at (12,12) size 116x66 [transform={m=((1.00,0.00)(0.00,1.00)) t=(20.00,20.00)}]
     19      RenderSVGRect {rect} at (12,12) size 116x66 [transform={m=((0.25,0.00)(0.00,1.00)) t=(0.00,0.00)}] [stroke={[type=LINEAR-GRADIENT] [id="grad1"] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
     20    RenderSVGRect {rect} at (12,92) size 116x66 [transform={m=((0.25,0.00)(0.00,1.00)) t=(20.00,100.00)}] [stroke={[type=LINEAR-GRADIENT] [id="grad2"] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
     21    RenderSVGContainer {use} at (12,172) size 116x66 [transform={m=((0.25,0.00)(0.00,1.00)) t=(20.00,180.00)}]
     22      RenderSVGRect {rect} at (12,172) size 116x66 [stroke={[type=PATTERN] [id="pattern"] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
     23    RenderSVGContainer {use} at (12,252) size 116x66 [transform={m=((0.25,0.00)(0.00,1.00)) t=(20.00,260.00)}]
     24      RenderSVGRect {rect} at (12,252) size 116x66 [stroke={[type=SOLID] [color=#008000] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
     25    RenderSVGContainer {use} at (134,12) size 165x66 [transform={m=((0.25,0.00)(0.25,1.00)) t=(160.00,20.00)}]
     26      RenderSVGRect {rect} at (134,12) size 165x66 [stroke={[type=SOLID] [color=#008000] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
     27    RenderSVGContainer {use} at (152,0) size 116x488 [transform={m=((0.25,0.36)(0.00,1.00)) t=(160.00,100.00)}]
     28      RenderSVGRect {rect} at (152,0) size 116x488 [stroke={[type=SOLID] [color=#008000] [stroke width=15.00]}] [x=0.00] [y=0.00] [width=400.00] [height=50.00]
  • trunk/Source/WebCore/ChangeLog

    r117697 r117709  
     12012-05-20  Philip Rogers  <pdr@google.com>
     2
     3        Fix hit testing on non-scaling strokes
     4        https://bugs.webkit.org/show_bug.cgi?id=82628
     5
     6        Reviewed by Nikolas Zimmermann.
     7
     8        This change fixes hit testing on non-scaling strokes. It contains fixes for 3 bugs:
     9        1) RenderSVGRect::shapeDependentStrokeContains was not falling back to shape-based hit testing.
     10        2) m_strokeAndMarkerBoundingBox did not account for non-scaling strokes.
     11        3) RenderSVGShape::shapeDependentStrokeContains did not have any support for non-scaling strokes.
     12
     13        This change also contains some refactoring/cleanup of the non-scale-stroke codepaths.
     14
     15        Test: svg/hittest/svg-shapes-non-scale-stroke.html
     16
     17        * rendering/svg/RenderSVGEllipse.cpp:
     18        (WebCore::RenderSVGEllipse::createShape):
     19        * rendering/svg/RenderSVGRect.cpp:
     20        (WebCore::RenderSVGRect::createShape):
     21        (WebCore::RenderSVGRect::shapeDependentStrokeContains):
     22        * rendering/svg/RenderSVGShape.cpp:
     23        (WebCore::RenderSVGShape::shapeDependentStrokeContains):
     24        (WebCore::RenderSVGShape::shapeDependentFillContains):
     25        (WebCore::RenderSVGShape::nonScalingStrokePath):
     26        (WebCore):
     27        (WebCore::RenderSVGShape::setupNonScalingStrokeContext):
     28        (WebCore::RenderSVGShape::nonScalingStrokeTransform):
     29        (WebCore::RenderSVGShape::strokePath):
     30        (WebCore::RenderSVGShape::fillAndStrokePath):
     31        (WebCore::RenderSVGShape::updateCachedBoundaries):
     32        (WebCore::RenderSVGShape::inflateWithStrokeAndMarkerBounds):
     33        * rendering/svg/RenderSVGShape.h:
     34        (WebCore::RenderSVGShape::hasNonScalingStroke):
     35        (RenderSVGShape):
     36
    1372012-05-19  Emil A Eklund  <eae@chromium.org>
    238
  • trunk/Source/WebCore/rendering/svg/RenderSVGEllipse.cpp

    r115201 r117709  
    5656
    5757    // Fallback to RenderSVGShape if shape has a non scaling stroke.
    58     if (style()->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE) {
     58    if (hasNonScalingStroke()) {
    5959        RenderSVGShape::createShape();
    6060        setIsPaintingFallback(true);
  • trunk/Source/WebCore/rendering/svg/RenderSVGRect.cpp

    r115201 r117709  
    5656    ASSERT(rect);
    5757
    58     bool nonScalingStroke = style()->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE;
    5958    // Fallback to RenderSVGShape if rect has rounded corners.
    60     if (rect->hasAttribute(SVGNames::rxAttr) || rect->hasAttribute(SVGNames::ryAttr) || nonScalingStroke) {
     59    if (rect->hasAttribute(SVGNames::rxAttr) || rect->hasAttribute(SVGNames::ryAttr) || hasNonScalingStroke()) {
    6160       RenderSVGShape::createShape();
    6261       setIsPaintingFallback(true);
     
    139138bool RenderSVGRect::shapeDependentStrokeContains(const FloatPoint& point) const
    140139{
     140    if (isPaintingFallback())
     141        return RenderSVGShape::shapeDependentStrokeContains(point);
     142
    141143    return m_outerStrokeRect.contains(point, FloatRect::InsideOrOnStroke) && !m_innerStrokeRect.contains(point, FloatRect::InsideButNotOnStroke);
    142144}
  • trunk/Source/WebCore/rendering/svg/RenderSVGShape.cpp

    r115201 r117709  
    101101bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point) const
    102102{
     103    ASSERT(m_path);
    103104    BoundingRectStrokeStyleApplier applier(this, style());
     105
     106    if (hasNonScalingStroke()) {
     107        AffineTransform nonScalingTransform = nonScalingStrokeTransform();
     108        Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
     109
     110        return usePath->strokeContains(&applier, nonScalingTransform.mapPoint(point));
     111    }
     112
    104113    return m_path->strokeContains(&applier, point);
    105114}
     
    107116bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
    108117{
     118    ASSERT(m_path);
    109119    return m_path->contains(point, fillRule);
    110120}
     
    198208}
    199209
     210Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
     211{
     212    DEFINE_STATIC_LOCAL(Path, tempPath, ());
     213
     214    tempPath = *path;
     215    tempPath.transform(strokeTransform);
     216
     217    return &tempPath;
     218}
     219
     220bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
     221{
     222    if (!strokeTransform.isInvertible())
     223        return false;
     224
     225    stateSaver.save();
     226    stateSaver.context()->concatCTM(strokeTransform.inverse());
     227    return true;
     228}
     229
     230AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
     231{
     232    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
     233    return element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
     234}
     235
    200236bool RenderSVGShape::shouldStrokeZeroLengthSubpath() const
    201237{
     
    224260
    225261    return &tempPath;
    226 }
    227 
    228 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform)
    229 {
    230     DEFINE_STATIC_LOCAL(Path, tempPath, ());
    231 
    232     tempPath = *path;
    233     tempPath.transform(strokeTransform);
    234 
    235     return &tempPath;
    236 }
    237 
    238 bool RenderSVGShape::setupNonScalingStrokeTransform(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
    239 {
    240     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
    241     strokeTransform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
    242     if (!strokeTransform.isInvertible())
    243         return false;
    244 
    245     stateSaver.save();
    246     stateSaver.context()->concatCTM(strokeTransform.inverse());
    247     return true;
    248262}
    249263
     
    264278
    265279void RenderSVGShape::strokePath(RenderStyle* style, GraphicsContext* context, Path* path, RenderSVGResource* strokePaintingResource,
    266                                 const Color& fallbackColor, bool nonScalingStroke, const AffineTransform& nonScalingStrokeTransform,
    267                                 int applyMode)
     280                                const Color& fallbackColor, int applyMode)
    268281{
    269282    if (!style->svgStyle()->hasVisibleStroke())
    270283        return;
    271     Path* usePath = path;
    272     if (nonScalingStroke) {
    273         usePath = nonScalingStrokePath(path, nonScalingStrokeTransform);
    274     }
     284
    275285    if (strokePaintingResource->applyResource(this, style, context, applyMode)) {
    276         strokePaintingResource->postApplyResource(this, context, applyMode, usePath, this);
    277         return;
    278     }
     286        strokePaintingResource->postApplyResource(this, context, applyMode, path, this);
     287        return;
     288    }
     289
    279290    if (!fallbackColor.isValid())
    280291        return;
     292
    281293    RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
    282294    fallbackResource->setColor(fallbackColor);
    283295    if (fallbackResource->applyResource(this, style, context, applyMode))
    284         fallbackResource->postApplyResource(this, context, applyMode, usePath, this);
     296        fallbackResource->postApplyResource(this, context, applyMode, path, this);
    285297}
    286298
     
    296308        return;
    297309
     310    Path* usePath = m_path.get();
    298311    GraphicsContextStateSaver stateSaver(*context, false);
    299     AffineTransform nonScalingStrokeTransform;
    300     bool nonScalingStroke = style->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE;
    301     if (nonScalingStroke) {
    302         if (!setupNonScalingStrokeTransform(nonScalingStrokeTransform, stateSaver))
     312    AffineTransform nonScalingTransform;
     313
     314    if (hasNonScalingStroke()) {
     315        nonScalingTransform = nonScalingStrokeTransform();
     316        if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
    303317            return;
    304     }
    305 
    306     strokePath(style, context, m_path.get(), strokePaintingResource, fallbackColor, nonScalingStroke, nonScalingStrokeTransform, ApplyToStrokeMode);
     318        usePath = nonScalingStrokePath(usePath, nonScalingTransform);
     319    }
     320
     321    strokePath(style, context, usePath, strokePaintingResource, fallbackColor, ApplyToStrokeMode);
    307322
    308323    // Spec(11.4): Any zero length subpath shall not be stroked if the "stroke-linecap" property has a value of butt
     
    310325    for (size_t i = 0; i < m_zeroLengthLinecapLocations.size(); ++i) {
    311326        Path* usePath = zeroLengthLinecapPath(m_zeroLengthLinecapLocations[i]);
    312         strokePath(style, context, usePath, strokePaintingResource, fallbackColor, nonScalingStroke, nonScalingStrokeTransform, ApplyToFillMode);
    313     }
    314 
     327        if (hasNonScalingStroke())
     328            usePath = nonScalingStrokePath(usePath, nonScalingTransform);
     329        strokePath(style, context, usePath, strokePaintingResource, fallbackColor, ApplyToFillMode);
     330    }
    315331}
    316332
     
    424440
    425441    // Add zero-length sub-path linecaps to the fill box
     442    // FIXME: zero-length subpaths do not respect vector-effect = non-scaling-stroke.
    426443    float strokeWidth = this->strokeWidth();
    427444    for (size_t i = 0; i < m_zeroLengthLinecapLocations.size(); ++i)
     
    450467    if (svgStyle->hasStroke()) {
    451468        BoundingRectStrokeStyleApplier strokeStyle(this, style());
    452         m_strokeAndMarkerBoundingBox.unite(path().strokeBoundingRect(&strokeStyle));
     469
     470        // SVG1.2 Tiny only defines non scaling stroke for the stroke but not markers.
     471        if (hasNonScalingStroke()) {
     472            AffineTransform nonScalingTransform = nonScalingStrokeTransform();
     473            if (nonScalingTransform.isInvertible()) {
     474                Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
     475                FloatRect strokeBoundingRect = usePath->strokeBoundingRect(&strokeStyle);
     476                strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
     477                m_strokeAndMarkerBoundingBox.unite(strokeBoundingRect);
     478            }
     479        } else
     480            m_strokeAndMarkerBoundingBox.unite(path().strokeBoundingRect(&strokeStyle));
    453481    }
    454482    if (svgStyle->hasMarkers()) {
  • trunk/Source/WebCore/rendering/svg/RenderSVGShape.h

    r116557 r117709  
    9898
    9999    bool hasPath() const { return m_path.get(); }
     100    bool hasNonScalingStroke() const { return style()->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE; }
    100101
    101102private:
     
    119120    void updateCachedBoundaries();
    120121
     122    AffineTransform nonScalingStrokeTransform() const;
     123    bool setupNonScalingStrokeContext(AffineTransform&, GraphicsContextStateSaver&);
     124    Path* nonScalingStrokePath(const Path*, const AffineTransform&) const;
     125
    121126    Path* zeroLengthLinecapPath(const FloatPoint&);
    122     bool setupNonScalingStrokeTransform(AffineTransform&, GraphicsContextStateSaver&);
    123     Path* nonScalingStrokePath(const Path*, const AffineTransform&);
    124127    bool shouldStrokeZeroLengthSubpath() const;
    125128    FloatRect zeroLengthSubpathRect(const FloatPoint&, float) const;
     
    127130    void fillShape(RenderStyle*, GraphicsContext*, Path*, RenderSVGShape*);
    128131    void strokePath(RenderStyle*, GraphicsContext*, Path*, RenderSVGResource*,
    129                     const Color&, bool, const AffineTransform&, int);
     132                    const Color&, int);
    130133    void fillAndStrokePath(GraphicsContext*);
    131134    void inflateWithStrokeAndMarkerBounds();
Note: See TracChangeset for help on using the changeset viewer.