Commit 087c12f9 authored by miletus@chromium.org's avatar miletus@chromium.org

Scale x/y coordinate of svg path separately for hit-test

We currently scale x/y coordinate of svg path using the same
scale factor for hit-test. It has the problem that when x/y
values have different magnitudes, e.g. when x has huge value
and y has small value, scaling down x and y using the same
scale factor will clamp the y values and make it lose the
resolution for hit-test. Scaling x/y separately can fix the
problem.

BUG=379320

Review URL: https://codereview.chromium.org/1300783005

git-svn-id: svn://svn.chromium.org/blink/trunk@201192 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent d07d1155
Test that hit-test works for a path whose x/y ranges have different magnitude.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS path contains point at (5, 104)
PASS path contains point at (5, 107)
PASS path contains point at (5, 110)
PASS path does not contain point at (5, 103)
PASS path does not contain point at (5, 111)
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<style>
svg {
margin: 0px;
padding: 0px;
position: absolute;
top: 0px;
left: 0px;
}
</style>
<body>
<svg x="0" y="0">
<svg x="-250000" width="500000" height="500">
<path id="path" d="M 250005 104 l 19 0 l 3 3 l -3 3 l -19 0 z"></path>
</svg>
</svg>
<script src="../../resources/js-test.js"></script>
<script>
description("Test that hit-test works for a path whose x/y ranges have different magnitude.");
if (window.testRunner)
testRunner.dumpAsText();
var resultString = "";
var pathElement = document.getElementById("path");
var pointsInPath = [
{x: 5, y: 104},
{x: 5, y: 107},
{x: 5, y: 110}
];
var pointsNotInPath = [
{x: 5, y: 103},
{x: 5, y: 111}
];
pointsInPath.forEach( function(point) {
var pass = (pathElement == document.elementFromPoint(point.x, point.y));
resultString += ((pass) ? "PASS" : "FAIL") + " path contains point at (" + point.x + ", " + point.y + ")\n";
});
pointsNotInPath.forEach( function(point) {
var pass = (pathElement != document.elementFromPoint(point.x, point.y));
resultString += ((pass) ? "PASS" : "FAIL") + " path does not contain point at (" + point.x + ", " + point.y + ")\n";
});
debug(resultString);
</script>
</body>
...@@ -188,13 +188,19 @@ bool SkPathContainsPoint(const SkPath& originalPath, const FloatPoint& point, Sk ...@@ -188,13 +188,19 @@ bool SkPathContainsPoint(const SkPath& originalPath, const FloatPoint& point, Sk
// 1) Skia has trouble with coordinates close to the max signed 16-bit values, so we scale larger paths down. // 1) Skia has trouble with coordinates close to the max signed 16-bit values, so we scale larger paths down.
// TODO: when Skia is patched to work properly with large values, this will not be necessary. // TODO: when Skia is patched to work properly with large values, this will not be necessary.
// 2) Skia does not support analytic hit testing, so we scale paths up to do raster hit testing with subpixel accuracy. // 2) Skia does not support analytic hit testing, so we scale paths up to do raster hit testing with subpixel accuracy.
SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); // 3) Scale the x/y axis separately so an extreme large/small scale factor on one axis won't
if (SkScalarNearlyZero(biggestCoord)) // ruin the resolution of the other axis.
SkScalar biggestCoordX = std::max(bounds.fRight, -bounds.fLeft);
SkScalar biggestCoordY = std::max(bounds.fBottom, -bounds.fTop);
if (SkScalarNearlyZero(biggestCoordX) || SkScalarNearlyZero(biggestCoordY))
return false; return false;
biggestCoord = std::max(std::max(biggestCoord, fX + 1), fY + 1);
biggestCoordX = std::max(biggestCoordX, std::fabs(fX) + 1);
biggestCoordY = std::max(biggestCoordY, std::fabs(fY) + 1);
const SkScalar kMaxCoordinate = SkIntToScalar(1 << 15); const SkScalar kMaxCoordinate = SkIntToScalar(1 << 15);
SkScalar scale = kMaxCoordinate / biggestCoord; SkScalar scaleX = kMaxCoordinate / biggestCoordX;
SkScalar scaleY = kMaxCoordinate / biggestCoordY;
SkRegion rgn; SkRegion rgn;
SkRegion clip; SkRegion clip;
...@@ -202,11 +208,11 @@ bool SkPathContainsPoint(const SkPath& originalPath, const FloatPoint& point, Sk ...@@ -202,11 +208,11 @@ bool SkPathContainsPoint(const SkPath& originalPath, const FloatPoint& point, Sk
SkPath scaledPath(originalPath); SkPath scaledPath(originalPath);
scaledPath.setFillType(ft); scaledPath.setFillType(ft);
m.setScale(scale, scale); m.setScale(scaleX, scaleY);
scaledPath.transform(m, 0); scaledPath.transform(m, 0);
int x = static_cast<int>(floorf(0.5f + point.x() * scale)); int x = static_cast<int>(floorf(0.5f + point.x() * scaleX));
int y = static_cast<int>(floorf(0.5f + point.y() * scale)); int y = static_cast<int>(floorf(0.5f + point.y() * scaleY));
clip.setRect(x - 1, y - 1, x + 1, y + 1); clip.setRect(x - 1, y - 1, x + 1, y + 1);
return rgn.setPath(scaledPath, clip); return rgn.setPath(scaledPath, clip);
......
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