Commit e4d2c607 authored by Fredrik Söderquist's avatar Fredrik Söderquist Committed by Commit Bot

Update isPointInFill/isPointInStroke to match spec adjustments

Rather than using the value of the 'pointer-events' property (and
indirectly, the 'visibility' property and potentially others not
explicitly listed in the spec), pass 'fill' and 'stroke' respectively to
better match spec [1].

Use AsPath() in isPointIn{Fill,Stroke} instead of going to the
LayoutSVGShape, to hopefully allow the LayoutSVGShape to be stricter
about invariants.

[1] https://svgwg.org/svg2-draft/types.html#InterfaceSVGGeometryElement

Bug: 896638
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I4f8ef5ae4e84d29187e68ca243e3344a9a27544c
Reviewed-on: https://chromium-review.googlesource.com/c/1288574
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602018}
parent a3216a1c
This is a testharness.js-based test.
FAIL SVGGeometryElement.prototype.isPointInFill, no arguments. Failed to execute 'isPointInFill' on 'SVGGeometryElement': 1 argument required, but only 0 present.
PASS SVGGeometryElement.prototype.isPointInFill, non-finite argument.
PASS SVGGeometryElement.prototype.isPointInFill, functional test.
PASS SVGGeometryElement.prototype.isPointInFill, 'fill-rule'.
PASS SVGGeometryElement.prototype.isPointInFill, 'visibility' and 'pointer-events' have no effect.
PASS SVGGeometryElement.prototype.isPointInFill, 'clip-rule' never overrides 'fill-rule'.
Harness: the test ran to completion.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400"
xmlns:h="http://www.w3.org/1999/xhtml">
<title>SVGGeometryElement.prototype.isPointInFill</title>
<metadata>
<h:link rel="help" href="https://svgwg.org/svg2-draft/types.html#InterfaceSVGGeometryElement"/>
<h:link rel="help" href="https://svgwg.org/svg2-draft/types.html#__svg__SVGGeometryElement__isPointInFill"/>
</metadata>
<h:script src="/resources/testharness.js"/>
<h:script src="/resources/testharnessreport.js"/>
<circle id="circle-at-origin" r="10" fill="blue"/>
<ellipse id="ellipse" cx="150" cy="150" rx="200" ry="100"
fill="blue" fill-opacity="0.1"
stroke-width="100" stroke="green" stroke-opacity="0.2"/>
<path id="rectangular-outline-evenodd" d="M200,200h100v100h-100z m20,20h60v60h-60z"
fill="lightblue" fill-rule="evenodd"/>
<path id="rectangular-outline-nonzero" d="M310,200h100v100h-100z m20,20h60v60h-60z"
fill="lightblue"/>
<clipPath>
<path id="rectangular-outline-nonzero-in-clip" d="M200,200h100v100h-100z m20,20h60v60h-60z"
fill="lightblue" clip-rule="evenodd"/>
</clipPath>
<script><![CDATA[
'use strict';
setup(function() {
window.myPoint = document.documentElement.createSVGPoint();
});
function adaptPoint(point) {
myPoint.x = point.x;
myPoint.y = point.y;
return myPoint;
}
const pointsToTest = [
{ x: 150, y: 150 },
{ x: 275, y: 150 },
{ x: 250, y: 225 },
{ x: 0, y: 0 },
{ x: 275, y: 250 },
];
function testPoints(element) {
const expected = [true, true, true, false, false];
pointsToTest.forEach(function(point, index) {
assert_equals(element.isPointInFill(adaptPoint(point)),
expected[index], "point at " + point.x + ", " + point.y);
});
}
function testResultVector(element) {
return pointsToTest.map(function(point) {
return element.isPointInFill(adaptPoint(point));
});
}
test(function() {
let circleAtOrigin = document.getElementById("circle-at-origin");
assert_true(circleAtOrigin.isPointInFill());
let ellipse = document.getElementById("ellipse");
assert_false(ellipse.isPointInFill());
}, document.title + ", no arguments.");
test(function() {
let circleAtOrigin = document.getElementById("circle-at-origin");
assert_false(circleAtOrigin.isPointInFill(adaptPoint({ x: NaN, y: 0 })), "x is NaN");
assert_false(circleAtOrigin.isPointInFill(adaptPoint({ x: Infinity, y: 0 })), "x is Infinity");
assert_false(circleAtOrigin.isPointInFill(adaptPoint({ x: -Infinity, y: 0 })), "x is -Infinity");
assert_false(circleAtOrigin.isPointInFill(adaptPoint({ x: 0, y: NaN })), "y is NaN");
assert_false(circleAtOrigin.isPointInFill(adaptPoint({ x: 0, y: Infinity })), "y is Infinity");
assert_false(circleAtOrigin.isPointInFill(adaptPoint({ x: 0, y: -Infinity })), "y is -Infinity");
}, document.title + ", non-finite argument.");
test(function() {
testPoints(document.getElementById("ellipse"));
}, document.title + ", functional test.");
test(function() {
let evenOdd = document.getElementById("rectangular-outline-evenodd");
assert_false(evenOdd.isPointInFill(adaptPoint({ x: 250, y: 250 })));
let nonZeroWinding = document.getElementById("rectangular-outline-nonzero");
assert_true(nonZeroWinding.isPointInFill(adaptPoint({ x: 360, y: 250 })));
}, document.title + ", 'fill-rule'.");
test(function() {
let ellipse = document.getElementById("ellipse");
// Results for 'pointer-events: visiblePainted' and 'visibility: visible'.
let expectedResults = testResultVector(ellipse);
const pointerEventValues = [ "bounding-box", "visibleFill",
"visibleStroke", "visiblePainted", "fill",
"stroke", "painted", "visible", "all",
"none" ];
ellipse.setAttribute("visibility", "visible");
for (let pointerEventValue of pointerEventValues) {
ellipse.setAttribute("pointer-events", pointerEventValue);
assert_array_equals(expectedResults, testResultVector(ellipse));
}
ellipse.setAttribute("visibility", "hidden");
for (let pointerEventValue of pointerEventValues) {
ellipse.setAttribute("pointer-events", pointerEventValue);
assert_array_equals(expectedResults, testResultVector(ellipse));
}
}, document.title + ", 'visibility' and 'pointer-events' have no effect.");
test(function() {
let nonZeroWinding = document.getElementById("rectangular-outline-nonzero-in-clip");
assert_true(nonZeroWinding.isPointInFill(adaptPoint({ x: 250, y: 250 })));
}, document.title + ", 'clip-rule' never overrides 'fill-rule'.");
]]></script>
</svg>
This is a testharness.js-based test.
FAIL SVGGeometryElement.prototype.isPointInStroke, no arguments. Failed to execute 'isPointInStroke' on 'SVGGeometryElement': 1 argument required, but only 0 present.
PASS SVGGeometryElement.prototype.isPointInStroke, non-finite argument.
PASS SVGGeometryElement.prototype.isPointInStroke, functional test.
PASS SVGGeometryElement.prototype.isPointInStroke, 'stroke-dasharray'.
PASS SVGGeometryElement.prototype.isPointInStroke, 'stroke-dashoffset'.
PASS SVGGeometryElement.prototype.isPointInStroke, 'stroke-miterlimit'.
PASS SVGGeometryElement.prototype.isPointInStroke, 'stroke-linejoin'.
PASS SVGGeometryElement.prototype.isPointInStroke, 'stroke-linecap'.
PASS SVGGeometryElement.prototype.isPointInStroke, 'pathLength'.
PASS SVGGeometryElement.prototype.isPointInStroke, 'vector-effect'.
PASS SVGGeometryElement.prototype.isPointInStroke, 'visibility' and 'pointer-events' have no effect.
Harness: the test ran to completion.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400"
xmlns:h="http://www.w3.org/1999/xhtml">
<title>SVGGeometryElement.prototype.isPointInStroke</title>
<metadata>
<h:link rel="help" href="https://svgwg.org/svg2-draft/types.html#InterfaceSVGGeometryElement"/>
<h:link rel="help" href="https://svgwg.org/svg2-draft/types.html#__svg__SVGGeometryElement__isPointInStroke"/>
</metadata>
<h:script src="/resources/testharness.js"/>
<h:script src="/resources/testharnessreport.js"/>
<circle cx="10" id="circle-with-stroke-intersecting-origin" r="10"
fill="none" stroke="blue" stroke-width="10"/>
<ellipse id="ellipse" cx="150" cy="150" rx="200" ry="100"
fill="blue" fill-opacity="0.1"
stroke-width="100" stroke="lightblue"
stroke-opacity="0.2"/>
<g transform="scale(10)">
<circle id="circle-with-non-scaling-stroke" cx="20" cy="20" r="10"
fill="none" stroke-width="1" stroke="blue"
vector-effect="non-scaling-stroke"/>
</g>
<rect id="rect-with-dash-array" width="100" height="100" x="200" y="50"
stroke-dasharray="100" stroke="blue" stroke-width="10" fill="none"/>
<rect id="rect-with-dash-offset" width="100" height="100" x="200" y="180"
stroke-dasharray="100" stroke-dashoffset="100"
stroke="blue" stroke-width="10" fill="none"/>
<rect id="rect-with-path-length" width="100" height="100" x="200" y="310"
stroke-dasharray="100" pathLength="200"
stroke="blue" stroke-width="10" fill="none"/>
<polygon id="poly-with-miter" points="10,300 110,320 10,340"
fill="none" stroke="yellow" stroke-width="10"/>
<polygon id="poly-with-miter-limit-10" points="120,300 220,320 120,340"
fill="none" stroke="yellow" stroke-width="10" stroke-miterlimit="10"/>
<polygon id="poly-with-linejoin-round" points="230,300 330,320 230,340"
fill="none" stroke="yellow" stroke-width="10" stroke-linejoin="round"
stroke-miterlimit="100"/>
<line id="line-with-round-linecap" x1="10" y1="300" x2="20" y2="300"
stroke="orange" stroke-width="20" stroke-linecap="round"/>
<script><![CDATA[
'use strict';
setup(function() {
window.myPoint = document.documentElement.createSVGPoint();
});
function adaptPoint(point) {
myPoint.x = point.x;
myPoint.y = point.y;
return myPoint;
}
const pointsToTest = [
{ x: 275, y: 250 }, // outer stroke
{ x: 300, y: 200 }, // inner stroke
{ x: 375, y: 375 }, // outside ellipse
{ x: 0, y: 0 }, // outside ellipse
{ x: 150, y: 150 }, // inside ellipse
];
function testPoints(element) {
const expected = [true, true, false, false, false];
pointsToTest.forEach(function(point, index) {
assert_equals(element.isPointInStroke(adaptPoint(point)),
expected[index], "point at " + point.x + ", " + point.y);
});
}
function testResultVector(element) {
return pointsToTest.map(function(point) {
return element.isPointInStroke(adaptPoint(point));
});
}
test(function() {
let strokeAtOrigin = document.getElementById("circle-with-stroke-intersecting-origin");
assert_true(strokeAtOrigin.isPointInStroke());
let ellipse = document.getElementById("ellipse");
assert_false(ellipse.isPointInStroke());
}, document.title + ", no arguments.");
test(function() {
let strokeAtOrigin = document.getElementById("circle-with-stroke-intersecting-origin");
assert_false(strokeAtOrigin.isPointInStroke(adaptPoint({ x: NaN, y: 0 }))), "x is NaN";
assert_false(strokeAtOrigin.isPointInStroke(adaptPoint({ x: Infinity, y: 0 })), "x is Infinity");
assert_false(strokeAtOrigin.isPointInStroke(adaptPoint({ x: -Infinity, y: 0 })), "x is -Infinity");
assert_false(strokeAtOrigin.isPointInStroke(adaptPoint({ x: 0, y: NaN })), "y is NaN");
assert_false(strokeAtOrigin.isPointInStroke(adaptPoint({ x: 0, y: Infinity })), "y is Infinity");
assert_false(strokeAtOrigin.isPointInStroke(adaptPoint({ x: 0, y: -Infinity })), "y is -Infinity");
}, document.title + ", non-finite argument.");
test(function() {
testPoints(document.getElementById("ellipse"));
}, document.title + ", functional test.");
test(function() {
let rectWithDashes = document.getElementById("rect-with-dash-array");
assert_true(rectWithDashes.isPointInStroke(adaptPoint({ x: 250, y: 48 })));
assert_false(rectWithDashes.isPointInStroke(adaptPoint({ x: 302, y: 100 })));
}, document.title + ", 'stroke-dasharray'.");
test(function() {
let rectWithOffsetDashes = document.getElementById("rect-with-dash-offset");
assert_false(rectWithOffsetDashes.isPointInStroke(adaptPoint({ x: 250, y: 178 })));
assert_true(rectWithOffsetDashes.isPointInStroke(adaptPoint({ x: 302, y: 230 })));
}, document.title + ", 'stroke-dashoffset'.");
test(function() {
let polyWithMiter = document.getElementById("poly-with-miter");
assert_true(polyWithMiter.isPointInStroke(adaptPoint({ x: 110, y: 320 })));
assert_false(polyWithMiter.isPointInStroke(adaptPoint({ x: 113, y: 320 })));
let polyWithMiterLimit10 = document.getElementById("poly-with-miter-limit-10");
assert_true(polyWithMiterLimit10.isPointInStroke(adaptPoint({ x: 223, y: 320 })));
}, document.title + ", 'stroke-miterlimit'.");
test(function() {
let polyWithRoundJoin = document.getElementById("poly-with-linejoin-round");
assert_true(polyWithRoundJoin.isPointInStroke(adaptPoint({ x: 334, y: 320 })));
assert_false(polyWithRoundJoin.isPointInStroke(adaptPoint({ x: 336, y: 320 })));
}, document.title + ", 'stroke-linejoin'.");
test(function() {
let lineWithRoundCap = document.getElementById("line-with-round-linecap");
assert_true(lineWithRoundCap.isPointInStroke(adaptPoint({ x: 25, y: 300 })));
}, document.title + ", 'stroke-linecap'.");
test(function() {
let rectWithPathLength = document.getElementById("rect-with-path-length");
assert_true(rectWithPathLength.isPointInStroke(adaptPoint({ x: 250, y: 308 })));
assert_false(rectWithPathLength.isPointInStroke(adaptPoint({ x: 250, y: 412 })));
}, document.title + ", 'pathLength'.");
test(function() {
let circleWithNSS = document.getElementById("circle-with-non-scaling-stroke");
assert_true(circleWithNSS.isPointInStroke(adaptPoint({ x: 9.975, y: 20 })));
assert_false(circleWithNSS.isPointInStroke(adaptPoint({ x: 9.9, y: 20 })));
}, document.title + ", 'vector-effect'.");
test(function() {
let ellipse = document.getElementById("ellipse");
// Results for 'pointer-events: visiblePainted' and 'visibility: visible'.
let expectedResults = testResultVector(ellipse);
const pointerEventValues = [ "bounding-box", "visibleFill",
"visibleStroke", "visiblePainted", "fill",
"stroke", "painted", "visible", "all",
"none" ];
ellipse.setAttribute("visibility", "visible");
for (let pointerEventValue of pointerEventValues) {
ellipse.setAttribute("pointer-events", pointerEventValue);
assert_array_equals(expectedResults, testResultVector(ellipse));
}
ellipse.setAttribute("visibility", "hidden");
for (let pointerEventValue of pointerEventValues) {
ellipse.setAttribute("pointer-events", pointerEventValue);
assert_array_equals(expectedResults, testResultVector(ellipse));
}
}, document.title + ", 'visibility' and 'pointer-events' have no effect.");
]]></script>
</svg>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<style>
#svgRoot {
margin: 0px;
padding: 0px;
position: absolute;
top: 0px;
left: 0px;
}
#ellipse {
fill: green;
fill-opacity: 0.1;
stroke-width: 100px;
stroke: green;
stroke-opacity: 0.2;
}
</style>
</head>
<body>
<p>Tests for WK80423 - Make sure hit testing works properly on stroked ellipses.</p>
<p>On success, you will see a series of "PASS" messages and no "FAIL" messages.</p>
<pre id="console"></pre>
<svg onload="runTest()" id="svgRoot" width="400px" height="400px" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<ellipse id="ellipse" cx="150" cy="150" rx="200" ry="100"/>
</svg>
<script><![CDATA[
if (window.testRunner)
testRunner.dumpAsText();
var pointsInEllipse = [
{x: 150, y: 150},
{x: 275, y: 150},
{x: 250, y: 225}
];
var pointsNotInEllipse = [
{x: 0, y: 0},
{x: 275, y: 250}
];
var pointsOnEllipseStroke = [
{x: 275, y: 250}, // outer stroke
{x: 300, y: 200} // inner stroke
];
var pointsNotOnEllipseStroke = [
{x: 375, y: 375}, // outside ellipse
{x: 0, y: 0}, // outside ellipse
{x: 150, y: 150} // inside ellipse
];
var resultString = "";
function testFill(ellipseElement, pointerEvents, visibility, expectedResultInEllipse, expectedResultNotInEllipse) {
var mySVGPoint = ellipseElement.ownerSVGElement.createSVGPoint();
ellipseElement.setAttribute("visibility", visibility);
resultString += "Testing fill with pointer event " + pointerEvents + " and visibility=" + visibility + "\n";
ellipseElement.setAttribute("pointer-events", pointerEvents);
var count = 0;
pointsInEllipse.forEach( function(point) {
mySVGPoint.x = point.x;
mySVGPoint.y = point.y;
var found = ellipseElement.isPointInFill(mySVGPoint);
var pass = found == expectedResultInEllipse[count++];
resultString += ((pass) ? "PASS, as expected" : "FAIL") + " ellipse " + (found ? "contains" : "does not contain") + " point at (" + point.x + ", " + point.y + ")\n";
});
count = 0;
pointsNotInEllipse.forEach( function(point) {
mySVGPoint.x = point.x;
mySVGPoint.y = point.y;
var found = ellipseElement.isPointInFill(mySVGPoint);
var pass = found == expectedResultNotInEllipse[count++];
resultString += ((pass) ? "PASS, as expected" : "FAIL") + " ellipse " + (!found ? "does not contain" : "contains") + " point at (" + point.x + ", " + point.y + ")\n";
});
resultString += "\n";
}
function testStroke(ellipseElement, pointerEvents, visibility, expectedResultInEllipse, expectedResultNotInEllipse) {
var mySVGPoint = ellipseElement.ownerSVGElement.createSVGPoint();
ellipseElement.setAttribute("visibility", visibility);
resultString += "Testing stroke with pointer event " + pointerEvents + " and visibility=" + visibility + "\n";
ellipseElement.setAttribute("pointer-events", pointerEvents);
var count = 0;
pointsOnEllipseStroke.forEach( function(point) {
mySVGPoint.x = point.x;
mySVGPoint.y = point.y;
var found = ellipseElement.isPointInStroke(mySVGPoint);
var pass = found == expectedResultInEllipse[count++];
resultString += ((pass) ? "PASS, as expected" : "FAIL") + " ellipse stroke " + (found ? "contains" : "does not contain") + " point at (" + point.x + ", " + point.y + ")\n";
});
count = 0;
pointsNotOnEllipseStroke.forEach( function(point) {
mySVGPoint.x = point.x;
mySVGPoint.y = point.y;
var found = ellipseElement.isPointInStroke(mySVGPoint);
var pass = found == expectedResultNotInEllipse[count++];
resultString += ((pass) ? "PASS, as expected" : "FAIL") + " ellipse stroke " + (!found ? "does not contain" : "contains") + " point at (" + point.x + ", " + point.y + ")\n";
});
resultString += "\n";
}
function runTest() {
var ellipseElement = document.getElementById("ellipse");
var mySVGPoint = ellipseElement.ownerSVGElement.createSVGPoint();
resultString += "Testing isPointInFill/isPointInStroke\n";
try {
ellipseElement.isPointInFill();
resultString += "isPointInFill() throws no exception\n";
} catch(ex) {
resultString += "isPointInFill() throws exception\n";
}
try {
ellipseElement.isPointInStroke();
resultString += "isPointInStroke() throws no exception\n";
} catch(ex) {
resultString += "isPointInStroke() throws exception\n";
}
testFill(ellipseElement, "visibleFill", "visible", [true, true, true], [false, false]);
testFill(ellipseElement, "visibleStroke", "visible", [false, false, false], [false, false]);
testFill(ellipseElement, "visiblePainted", "visible", [true, true, true], [false, false]);
testFill(ellipseElement, "fill", "visible", [true, true, true], [false, false]);
testFill(ellipseElement, "stroke", "visible", [false, false, false], [false, false]);
testFill(ellipseElement, "painted", "visible", [true, true, true], [false, false]);
testFill(ellipseElement, "visible", "visible", [true, true, true], [false, false]);
testFill(ellipseElement, "all", "visible", [true, true, true], [false, false]);
testFill(ellipseElement, "none", "visible", [false, false, false], [false, false]);
testFill(ellipseElement, "visibleFill", "hidden", [false, false, false], [false, false]);
testFill(ellipseElement, "visibleStroke", "hidden", [false, false, false], [false, false]);
testFill(ellipseElement, "visiblePainted", "hidden", [false, false, false], [false, false]);
testFill(ellipseElement, "fill", "hidden", [true, true, true], [false, false]);
testFill(ellipseElement, "stroke", "hidden", [false, false, false], [false, false]);
testFill(ellipseElement, "painted", "hidden", [true, true, true], [false, false]);
testFill(ellipseElement, "visible", "hidden", [false, false, false], [false, false]);
testFill(ellipseElement, "all", "hidden", [true, true, true], [false, false]);
testFill(ellipseElement, "none", "hidden", [false, false, false], [false, false]);
testStroke(ellipseElement, "visibleFill", "visible", [false, false], [false, false, false]);
testStroke(ellipseElement, "visibleStroke", "visible", [true, true], [false, false, false]);
testStroke(ellipseElement, "visiblePainted", "visible", [true, true], [false, false, false]);
testStroke(ellipseElement, "fill", "visible", [false, false], [false, false, false]);
testStroke(ellipseElement, "stroke", "visible", [true, true], [false, false, false]);
testStroke(ellipseElement, "painted", "visible", [true, true], [false, false, false]);
testStroke(ellipseElement, "visible", "visible", [true, true], [false, false, false]);
testStroke(ellipseElement, "all", "visible", [true, true], [false, false, false]);
testStroke(ellipseElement, "none", "visible", [false, false], [false, false, false]);
testStroke(ellipseElement, "visibleFill", "hidden", [false, false], [false, false, false]);
testStroke(ellipseElement, "visibleStroke", "hidden", [false, false], [false, false, false]);
testStroke(ellipseElement, "visiblePainted", "hidden", [false, false], [false, false, false]);
testStroke(ellipseElement, "fill", "hidden", [false, false], [false, false, false]);
testStroke(ellipseElement, "stroke", "hidden", [true, true], [false, false, false]);
testStroke(ellipseElement, "painted", "hidden", [true, true], [false, false, false]);
testStroke(ellipseElement, "visible", "hidden", [false, false], [false, false, false]);
testStroke(ellipseElement, "all", "hidden", [true, true], [false, false, false]);
testStroke(ellipseElement, "none", "hidden", [false, false], [false, false, false]);
document.getElementById("console").innerHTML = resultString;
}
]]></script>
</body>
</html>
......@@ -309,31 +309,35 @@ void LayoutSVGShape::UpdateLayout() {
ClearNeedsLayout();
}
void LayoutSVGShape::UpdateNonScalingStrokeData() {
DCHECK(HasNonScalingStroke());
AffineTransform LayoutSVGShape::ComputeNonScalingStrokeTransform() const {
// Compute the CTM to the SVG root. This should probably be the CTM all the
// way to the "canvas" of the page ("host" coordinate system), but with our
// current approach of applying/painting non-scaling-stroke, that can break in
// unpleasant ways (see crbug.com/747708 for an example.) Maybe it would be
// better to apply this effect during rasterization?
const LayoutSVGRoot* svg_root = SVGLayoutSupport::FindTreeRootObject(this);
AffineTransform t;
t.Scale(1 / StyleRef().EffectiveZoom())
AffineTransform host_transform;
host_transform.Scale(1 / StyleRef().EffectiveZoom())
.Multiply(LocalToAncestorTransform(svg_root).ToAffineTransform());
// Width of non-scaling stroke is independent of translation, so zero it out
// here.
t.SetE(0);
t.SetF(0);
host_transform.SetE(0);
host_transform.SetF(0);
return host_transform;
}
void LayoutSVGShape::UpdateNonScalingStrokeData() {
DCHECK(HasNonScalingStroke());
const AffineTransform transform = ComputeNonScalingStrokeTransform();
auto& rare_data = EnsureRareData();
if (rare_data.non_scaling_stroke_transform_ != t) {
if (rare_data.non_scaling_stroke_transform_ != transform) {
SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kStyle);
rare_data.non_scaling_stroke_transform_ = t;
rare_data.non_scaling_stroke_transform_ = transform;
}
rare_data.non_scaling_stroke_path_ = *path_;
rare_data.non_scaling_stroke_path_.Transform(t);
rare_data.non_scaling_stroke_path_.Transform(transform);
}
void LayoutSVGShape::Paint(const PaintInfo& paint_info) const {
......
......@@ -97,6 +97,7 @@ class LayoutSVGShape : public LayoutSVGModelObject {
return rare_data_->non_scaling_stroke_transform_;
}
AffineTransform ComputeNonScalingStrokeTransform() const;
AffineTransform LocalSVGTransform() const final { return local_transform_; }
virtual const Vector<MarkerPosition>* MarkerPositions() const {
......
......@@ -31,12 +31,12 @@
#include "third_party/blink/renderer/core/svg/svg_geometry_element.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/layout/hit_test_request.h"
#include "third_party/blink/renderer/core/layout/pointer_events_hit_rules.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_path.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_shape.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
#include "third_party/blink/renderer/core/svg/svg_point_tear_off.h"
#include "third_party/blink/renderer/core/svg_names.h"
#include "third_party/blink/renderer/platform/graphics/stroke_data.h"
namespace blink {
......@@ -89,17 +89,13 @@ bool SVGGeometryElement::isPointInFill(SVGPointTearOff* point) const {
// FIXME: Eventually we should support isPointInFill for display:none
// elements.
if (!GetLayoutObject() || !GetLayoutObject()->IsSVGShape())
const LayoutObject* layout_object = GetLayoutObject();
if (!layout_object)
return false;
HitTestRequest request(HitTestRequest::kReadOnly);
PointerEventsHitRules hit_rules(
PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request,
GetLayoutObject()->StyleRef().PointerEvents());
hit_rules.can_hit_stroke = false;
HitTestLocation location(point->Target()->Value());
return ToLayoutSVGShape(GetLayoutObject())
->NodeAtPointInternal(request, location, hit_rules);
// Path::Contains will reject points with a non-finite component.
WindRule fill_rule = layout_object->StyleRef().SvgStyle().FillRule();
return AsPath().Contains(point->Target()->Value(), fill_rule);
}
bool SVGGeometryElement::isPointInStroke(SVGPointTearOff* point) const {
......@@ -107,17 +103,26 @@ bool SVGGeometryElement::isPointInStroke(SVGPointTearOff* point) const {
// FIXME: Eventually we should support isPointInStroke for display:none
// elements.
if (!GetLayoutObject() || !GetLayoutObject()->IsSVGShape())
const LayoutObject* layout_object = GetLayoutObject();
if (!layout_object)
return false;
const LayoutSVGShape& layout_shape = ToLayoutSVGShape(*layout_object);
HitTestRequest request(HitTestRequest::kReadOnly);
PointerEventsHitRules hit_rules(
PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request,
GetLayoutObject()->StyleRef().PointerEvents());
hit_rules.can_hit_fill = false;
HitTestLocation location(point->Target()->Value());
return ToLayoutSVGShape(GetLayoutObject())
->NodeAtPointInternal(request, location, hit_rules);
StrokeData stroke_data;
SVGLayoutSupport::ApplyStrokeStyleToStrokeData(
stroke_data, layout_shape.StyleRef(), layout_shape,
PathLengthScaleFactor());
Path path = AsPath();
FloatPoint local_point(point->Target()->Value());
if (layout_shape.HasNonScalingStroke()) {
const AffineTransform transform =
layout_shape.ComputeNonScalingStrokeTransform();
path.Transform(transform);
local_point = transform.MapPoint(local_point);
}
// Path::StrokeContains will reject points with a non-finite component.
return path.StrokeContains(local_point, stroke_data);
}
Path SVGGeometryElement::ToClipPath() const {
......
......@@ -67,13 +67,16 @@ bool Path::operator==(const Path& other) const {
}
bool Path::Contains(const FloatPoint& point) const {
return path_.contains(WebCoreFloatToSkScalar(point.X()),
WebCoreFloatToSkScalar(point.Y()));
if (!std::isfinite(point.X()) || !std::isfinite(point.Y()))
return false;
return path_.contains(SkScalar(point.X()), SkScalar(point.Y()));
}
bool Path::Contains(const FloatPoint& point, WindRule rule) const {
SkScalar x = WebCoreFloatToSkScalar(point.X());
SkScalar y = WebCoreFloatToSkScalar(point.Y());
if (!std::isfinite(point.X()) || !std::isfinite(point.Y()))
return false;
SkScalar x = point.X();
SkScalar y = point.Y();
SkPath::FillType fill_type = WebCoreWindRuleToSkFillType(rule);
if (path_.getFillType() != fill_type) {
SkPath tmp(path_);
......@@ -101,9 +104,10 @@ SkPath Path::StrokePath(const StrokeData& stroke_data) const {
bool Path::StrokeContains(const FloatPoint& point,
const StrokeData& stroke_data) const {
if (!std::isfinite(point.X()) || !std::isfinite(point.Y()))
return false;
return StrokePath(stroke_data)
.contains(WebCoreFloatToSkScalar(point.X()),
WebCoreFloatToSkScalar(point.Y()));
.contains(SkScalar(point.X()), SkScalar(point.Y()));
}
FloatRect Path::BoundingRect() const {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment