mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 05:15:45 +00:00
Bug 589648 part 3. Work around lack of cairo support to display square stroke-linecaps for zero length paths. r=longsonr.
--HG-- extra : rebase_source : 5aaa65a2bc888b4a64884f5f2a87beb7803f646e
This commit is contained in:
parent
45ffe4244b
commit
3045bb10aa
@ -237,6 +237,50 @@ SVGPathData::GetPathSegAtLength(float aDistance) const
|
||||
return NS_MAX(0U, segIndex - 1); // -1 because while loop takes us 1 too far
|
||||
}
|
||||
|
||||
/**
|
||||
* The SVG spec says we have to paint stroke caps for zero length subpaths:
|
||||
*
|
||||
* http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
|
||||
*
|
||||
* Cairo only does this for |stroke-linecap: round| and not for
|
||||
* |stroke-linecap: square| (since that's what Adobe Acrobat has always done).
|
||||
*
|
||||
* To help us conform to the SVG spec we have this helper function to draw an
|
||||
* approximation of square caps for zero length subpaths. It does this by
|
||||
* inserting a subpath containing a single axis aligned straight line that is
|
||||
* as small as it can be without cairo throwing it away for being too small to
|
||||
* affect rendering. Cairo will then draw stroke caps for this axis aligned
|
||||
* line, creating an axis aligned rectangle (approximating the square that
|
||||
* would ideally be drawn).
|
||||
*
|
||||
* Note that this function inserts a subpath into the current gfx path that
|
||||
* will be present during both fill and stroke operations.
|
||||
*/
|
||||
static void
|
||||
ApproximateZeroLengthSubpathSquareCaps(const gfxPoint &aPoint, gfxContext *aCtx)
|
||||
{
|
||||
// Cairo's fixed point fractional part is 8 bits wide, so its device space
|
||||
// coordinate granularity is 1/256 pixels. However, to prevent user space
|
||||
// |aPoint| and |aPoint + tinyAdvance| being rounded to the same device
|
||||
// coordinates, we double this for |tinyAdvance|:
|
||||
|
||||
const gfxSize tinyAdvance = aCtx->DeviceToUser(gfxSize(2.0/256.0, 0.0));
|
||||
|
||||
aCtx->MoveTo(aPoint);
|
||||
aCtx->LineTo(aPoint + gfxPoint(tinyAdvance.width, tinyAdvance.height));
|
||||
aCtx->MoveTo(aPoint);
|
||||
}
|
||||
|
||||
#define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS \
|
||||
do { \
|
||||
if (capsAreSquare && !subpathHasLength && subpathContainsNonArc && \
|
||||
SVGPathSegUtils::IsValidType(prevSegType) && \
|
||||
(!IsMoveto(prevSegType) || \
|
||||
segType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH)) { \
|
||||
ApproximateZeroLengthSubpathSquareCaps(segStart, aCtx); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void
|
||||
SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
{
|
||||
@ -244,6 +288,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
return; // paths without an initial moveto are invalid
|
||||
}
|
||||
|
||||
PRBool capsAreSquare = aCtx->CurrentLineCap() == gfxContext::LINE_CAP_SQUARE;
|
||||
PRBool subpathHasLength = PR_FALSE; // visual length
|
||||
PRBool subpathContainsNonArc = PR_FALSE;
|
||||
|
||||
PRUint32 segType, prevSegType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
|
||||
gfxPoint pathStart(0.0, 0.0); // start point of [sub]path
|
||||
gfxPoint segStart(0.0, 0.0);
|
||||
@ -263,28 +311,45 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
switch (segType)
|
||||
{
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
|
||||
// set this early to allow drawing of square caps for "M{x},{y} Z":
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS;
|
||||
segEnd = pathStart;
|
||||
aCtx->ClosePath();
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
|
||||
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS;
|
||||
pathStart = segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
aCtx->MoveTo(segEnd);
|
||||
subpathHasLength = PR_FALSE;
|
||||
subpathContainsNonArc = PR_FALSE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
|
||||
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS;
|
||||
pathStart = segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
aCtx->MoveTo(segEnd);
|
||||
subpathHasLength = PR_FALSE;
|
||||
subpathContainsNonArc = PR_FALSE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
aCtx->LineTo(segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
|
||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
aCtx->LineTo(segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
|
||||
@ -292,6 +357,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
cp2 = gfxPoint(mData[i+2], mData[i+3]);
|
||||
segEnd = gfxPoint(mData[i+4], mData[i+5]);
|
||||
aCtx->CurveTo(cp1, cp2, segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
|
||||
@ -299,6 +368,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
cp2 = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
||||
segEnd = segStart + gfxPoint(mData[i+4], mData[i+5]);
|
||||
aCtx->CurveTo(cp1, cp2, segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||
@ -308,6 +381,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
segEnd = gfxPoint(mData[i+2], mData[i+3]); // set before setting tcp2!
|
||||
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||
aCtx->CurveTo(tcp1, tcp2, segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart || segEnd != cp1);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
|
||||
@ -317,6 +394,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]); // set before setting tcp2!
|
||||
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||
aCtx->CurveTo(tcp1, tcp2, segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart || segEnd != cp1);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
|
||||
@ -338,27 +419,46 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
|
||||
segEnd = gfxPoint(mData[i], segStart.y);
|
||||
aCtx->LineTo(segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
|
||||
segEnd = segStart + gfxPoint(mData[i], 0.0f);
|
||||
aCtx->LineTo(segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
|
||||
segEnd = gfxPoint(segStart.x, mData[i]);
|
||||
aCtx->LineTo(segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
|
||||
segEnd = segStart + gfxPoint(0.0f, mData[i]);
|
||||
aCtx->LineTo(segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||
@ -366,6 +466,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
cp2 = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = gfxPoint(mData[i+2], mData[i+3]);
|
||||
aCtx->CurveTo(cp1, cp2, segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
||||
@ -373,6 +477,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
cp2 = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
||||
aCtx->CurveTo(cp1, cp2, segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
||||
@ -382,6 +490,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]); // set before setting tcp2!
|
||||
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||
aCtx->CurveTo(tcp1, tcp2, segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart || segEnd != cp1);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
||||
@ -391,6 +503,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]); // changed before setting tcp2!
|
||||
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||
aCtx->CurveTo(tcp1, tcp2, segEnd);
|
||||
if (!subpathHasLength) {
|
||||
subpathHasLength = (segEnd != segStart || segEnd != cp1);
|
||||
}
|
||||
subpathContainsNonArc = PR_TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -401,7 +517,10 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
prevSegType = segType;
|
||||
segStart = segEnd;
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
|
||||
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS;
|
||||
}
|
||||
|
||||
already_AddRefed<gfxFlattenedPath>
|
||||
|
@ -79,6 +79,7 @@ _TEST_FILES = \
|
||||
test_SVGAnimatedImageSMILDisabled.html \
|
||||
animated-svg-image-helper.html \
|
||||
animated-svg-image-helper.svg \
|
||||
test_stroke-linecap-hit-testing.xhtml \
|
||||
test_SVGLengthList.xhtml \
|
||||
test_SVGLengthList-2.xhtml \
|
||||
test_SVGPathSegList.xhtml \
|
||||
|
@ -0,0 +1,48 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=589648
|
||||
-->
|
||||
<head>
|
||||
<title>Test hit-testing of line caps</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="run()">
|
||||
<script class="testbody" type="text/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function run()
|
||||
{
|
||||
var svg = document.getElementById('svg');
|
||||
var div = document.getElementById("div");
|
||||
var x = div.offsetLeft;
|
||||
var y = div.offsetTop;
|
||||
var got, expected;
|
||||
|
||||
got = document.elementFromPoint(5 + x, 5 + y);
|
||||
expected = document.getElementById('zero-length-square-caps');
|
||||
is(got, expected, 'Check hit on zero length subpath\'s square caps');
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
|
||||
<div width="100%" height="1" id="div"></div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="svg" width="400" height="300">
|
||||
<path id="zero-length-square-caps" stroke="blue" stroke-width="50"
|
||||
stroke-linecap="square" d="M25,25 L25,25"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -196,6 +196,8 @@ fails-if(Android) random-if(gtk2Widget) != text-language-01.xhtml text-language-
|
||||
== text-layout-03.svg text-layout-03-ref.svg
|
||||
== text-scale-01.svg text-scale-01-ref.svg
|
||||
== text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg
|
||||
== stroke-linecap-square-w-zero-length-segs-01.svg pass.svg
|
||||
== stroke-linecap-square-w-zero-length-segs-02.svg pass.svg
|
||||
== text-style-01a.svg text-style-01-ref.svg
|
||||
== text-style-01b.svg text-style-01-ref.svg
|
||||
== text-style-01c.svg text-style-01-ref.svg
|
||||
|
@ -0,0 +1,143 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/licenses/publicdomain/
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<title>Test 'stroke-linecap: square' with zero length path segments</title>
|
||||
|
||||
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=589648 -->
|
||||
|
||||
<style>
|
||||
|
||||
path {
|
||||
stroke-width: 20px;
|
||||
stroke-linecap: square;
|
||||
}
|
||||
|
||||
rect {
|
||||
fill: red;
|
||||
}
|
||||
|
||||
/* expect lime squares to cover red rects */
|
||||
path.squares-expected {
|
||||
stroke: lime;
|
||||
}
|
||||
|
||||
path.squares-not-expected {
|
||||
stroke: red;
|
||||
}
|
||||
|
||||
/* thicker stroke to cover squares-not-expected paths */
|
||||
path.coverer {
|
||||
stroke: lime;
|
||||
stroke-width: 24px;
|
||||
}
|
||||
|
||||
/* to show edges of shapes to help in debugging:
|
||||
g > rect {
|
||||
stroke: red;
|
||||
stroke-width: 5px;
|
||||
}
|
||||
path.coverer {
|
||||
stroke: lime;
|
||||
stroke-width: 18px;
|
||||
}
|
||||
*/
|
||||
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" style="fill:lime"/>
|
||||
|
||||
<!-- Column 1: test single segment zero-length subpaths: -->
|
||||
|
||||
<g transform="translate(25,25)">
|
||||
<rect x="-9" y="-9" width="18" height="18"/>
|
||||
<rect x="41" y="41" width="18" height="18"/>
|
||||
<rect x="91" y="91" width="18" height="18"/>
|
||||
<path class="squares-expected" d="M0,0 L0,0 M20,20 L30,30 M50,50 L50,50 M70,70 L80,80 M100,100 L100,100"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(25,75)">
|
||||
<rect x="-9" y="-9" width="18" height="18"/>
|
||||
<rect x="41" y="41" width="18" height="18"/>
|
||||
<rect x="91" y="91" width="18" height="18"/>
|
||||
<path class="squares-expected" d="M0,0 C0,0 0,0 0,0 M20,20 L30,30 M50,50 C50,50 50,50 50,50 M70,70 L80,80 M100,100 C100,100 100,100 100,100"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(25,125)">
|
||||
<path class="squares-not-expected" d="M0,0 A0,10 0 0 0 0,0 M20,20 L30,30 M50,50 A0,10 0 0 0 50,50 M70,70 L80,80 M100,100 A0,10 0 0 0 100,100"/>
|
||||
<path class="coverer" d="M20,20 L30,30 M70,70 L80,80"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(25,175)">
|
||||
<rect x="-9" y="-9" width="18" height="18"/>
|
||||
<rect x="41" y="41" width="18" height="18"/>
|
||||
<rect x="91" y="91" width="18" height="18"/>
|
||||
<path class="squares-expected" d="M0,0 Z M20,20 L30,30 M50,50 Z M70,70 L80,80 M100,100 Z"/>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- Column 2: test multi-segment zero-length subpaths: -->
|
||||
|
||||
<g transform="translate(175,25)">
|
||||
<rect x="-9" y="-9" width="18" height="18"/>
|
||||
<rect x="41" y="41" width="18" height="18"/>
|
||||
<rect x="91" y="91" width="18" height="18"/>
|
||||
<path class="squares-expected" d="M0,0 L0,0 M0,0 L0,0 M20,20 L30,30 M50,50 L50,50 L50,50 M70,70 L80,80 M100,100 L100,100 L100,100"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(177,75)">
|
||||
<rect x="-9" y="-9" width="18" height="18"/>
|
||||
<rect x="41" y="41" width="18" height="18"/>
|
||||
<rect x="91" y="91" width="18" height="18"/>
|
||||
<path class="squares-expected" d="M0,0 C0,0 0,0 0,0 C0,0 0,0 0,0 M20,20 L30,30 M50,50 C50,50 50,50 50,50 C50,50 50,50 50,50 M70,70 L80,80 M100,100 C100,100 100,100 100,100 C100,100 100,100 100,100"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(175,125)">
|
||||
<path class="squares-not-expected" d="M0,0 A0,10 0 0 0 0,0 A0,10 0 0 0 0,0 M20,20 L30,30 M50,50 A0,10 0 0 0 50,50 A0,10 0 0 0 50,50 M70,70 L80,80 M100,100 A0,10 0 0 0 100,100 A0,10 0 0 0 100,100"/>
|
||||
<path class="coverer" d="M20,20 L30,30 M70,70 L80,80"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(175,175)">
|
||||
<rect x="-9" y="-9" width="18" height="18"/>
|
||||
<rect x="41" y="41" width="18" height="18"/>
|
||||
<rect x="91" y="91" width="18" height="18"/>
|
||||
<path class="squares-expected" d="M0,0 Z Z M20,20 L30,30 M50,50 Z Z M70,70 L80,80 M100,100 Z Z"/>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- Column 3: test non-zero-length subpaths that begin, end and contain
|
||||
zero length segments: -->
|
||||
|
||||
<g transform="translate(325,25)">
|
||||
<path class="squares-not-expected" d="M20,20 L20,20 L30,30 L30,30 L40,40 L40,40"/>
|
||||
<path class="coverer" d="M20,20 L40,40"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(325,75)">
|
||||
<path class="squares-not-expected" d="M20,20 C20,20 20,20 20,20 C20,20 30,30 30,30 C30,30 30,30 30,30 C30,30 40,40 40,40 C40,40 40,40 40,40"/>
|
||||
<path class="coverer" d="M20,20 L40,40"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(325,125)">
|
||||
<path class="squares-not-expected" d="M20,20 A0,10 0 0 0 20,20 A0,10 0 0 0 30,30 A0,10 0 0 0 30,30 A0,10 0 0 0 40,40 A0,10 0 0 0 40,40"/>
|
||||
<path class="coverer" d="M20,20 L40,40"/>
|
||||
</g>
|
||||
|
||||
<!-- this one is shorter because the Z's mean we only have path end points
|
||||
at 20,20 -->
|
||||
<g transform="translate(325,175)">
|
||||
<rect x="11" y="11" width="18" height="18"/>
|
||||
<path class="squares-expected" d="M20,20 Z L30,30 Z L40,40 Z"/>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- Column 4: test loan movetos -->
|
||||
|
||||
<g transform="translate(425,25)">
|
||||
<path class="squares-not-expected" d="M0,0 M0,0 M20,20 L30,30 M50,50 M50,50 M70,70 L80,80 M100,100 M100,100"/>
|
||||
<path class="coverer" d="M20,20 L30,30 M70,70 L80,80"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 4.7 KiB |
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/licenses/publicdomain/
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
|
||||
|
||||
<title>Test 'stroke-linecap: square' with zero length path segments</title>
|
||||
|
||||
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=589648 -->
|
||||
|
||||
<script>
|
||||
|
||||
function run()
|
||||
{
|
||||
document.getElementById('path').setAttribute('stroke-linecap', 'butt');
|
||||
document.documentElement.removeAttribute('class');
|
||||
}
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", run, false);
|
||||
|
||||
</script>
|
||||
|
||||
<rect width="100%" height="100%" style="fill:lime"/>
|
||||
|
||||
<path id="path" stroke="red" stroke-width="200" stroke-linecap="square"
|
||||
d="M100,100 L100,100"/>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 735 B |
@ -483,6 +483,12 @@ nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
|
||||
|
||||
aContext->Multiply(matrix);
|
||||
|
||||
// Hack to let SVGPathData::ConstructPath know if we have square caps:
|
||||
const nsStyleSVG* style = GetStyleSVG();
|
||||
if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
|
||||
aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
|
||||
}
|
||||
|
||||
aContext->NewPath();
|
||||
static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user