Bug 684479 - Bounding boxes for strokes are unnecessarily big for many shapes. r=roc

This commit is contained in:
Robert Longson 2011-09-05 18:53:34 +01:00
Родитель e47cab333b
Коммит af30ad46e4
5 изменённых файлов: 130 добавлений и 88 удалений

Просмотреть файл

@ -60,6 +60,7 @@ _TEST_FILES = \
test_animLengthUnits.xhtml \
test_bbox.xhtml \
test_bbox-with-invalid-viewBox.xhtml \
test_bounds.html \
bbox-helper.svg \
bounds-helper.svg \
test_dataTypes.html \

Просмотреть файл

@ -7,24 +7,23 @@ text { font: 20px monospace; }
<g id="g">
<text id="text1" x="25" y="25">abc</text>
<text id="text1a" x="85" y="25" stroke="black" stroke-width="4">abc</text>
<rect id="rect1" x="50" y="50" width="50" height="50" fill="green"/>
<rect id="rect1a" x="50" y="50" width="50" height="50" fill="none" stroke-width="2" stroke="yellow"/>
<rect id="rect1a" x="50" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="yellow"/>
<text id="text2" x="125" y="25">abc</text>
<text id="text2a" x="185" y="25" stroke="black" stroke-width="10">abc</text>
<g transform="rotate(45 175 75)">
<rect id="rect2" x="150" y="50" width="50" height="50" fill="yellow"/>
<rect id="rect2a" x="150" y="50" width="50" height="50" fill="none" stroke-width="2" stroke="blue"/>
<rect id="rect2a" x="150" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/>
<text id="text3" x="150" y="50" text-anchor="middle">abc</text>
</g>
<g transform="scale(2)">
<rect id="rect3" x="25" y="80" width="50" height="50" fill="green"/>
<rect id="rect3a" x="25" y="80" width="50" height="50" fill="none" stroke-width="2" stroke="blue"/>
<rect id="rect3a" x="25" y="80" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/>
</g>
<g transform="scale(2) rotate(45 175 75)">
<rect id="rect4" x="150" y="50" width="50" height="50" fill="yellow"/>
<rect id="rect4a" x="150" y="50" width="50" height="50" fill="none" stroke-width="2" stroke="blue"/>
<text id="text4" x="125" y="125">abc</text>
<rect id="rect4a" x="150" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/>
</g>
<text id="text1a" x="85" y="25" stroke="black" stroke-width="1">M</text>
<text id="text2a" x="185" y="25" stroke="black" stroke-width="10">M</text>
</g>
</svg>

До

Ширина:  |  Высота:  |  Размер: 1.4 KiB

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Просмотреть файл

@ -19,101 +19,120 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=463934
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
function Rect(left, top, width, height)
{
this.left = left;
this.top = top;
this.width = width;
this.height = height;
}
Rect.prototype.roundOut = function()
{
this.width = Math.ceil(this.left + this.width) - Math.floor(this.left);
this.height = Math.ceil(this.top + this.height) - Math.floor(this.top);
this.left = Math.floor(this.left);
this.top = Math.floor(this.top);
}
var delta = 1;
function isApproximately(a, b, message)
{
ok(delta >= Math.abs(a - b), message + " - got " + a + ", expected " + b + " ± " + delta);
}
function runTest()
{
function isRounded(a, b, message) {
is (Math.round(a), Math.round(b), message);
}
var doc = $("svg").contentWindow.document;
var text1 = doc.getElementById("text1");
var len = text1.getComputedTextLength();
var text1Bounds = text1.getBoundingClientRect();
var text2Bounds = doc.getElementById("text2").getBoundingClientRect();
var text3Bounds = doc.getElementById("text3").getBoundingClientRect();
var text4Bounds = doc.getElementById("text4").getBoundingClientRect();
var sin45 = Math.sin(Math.PI / 4);
isRounded(text1Bounds.left, 25, "text1.getBoundingClientRect().left");
isRounded(text1Bounds.width, len, "text1.getBoundingClientRect().width");
isApproximately(text1Bounds.left, 24, "text1.getBoundingClientRect().left");
isRounded(text2Bounds.left, text1Bounds.left + 100, "text2.getBoundingClientRect().left");
isRounded(text2Bounds.top, text1Bounds.top, "text2.getBoundingClientRect().top");
isRounded(text2Bounds.width, text1Bounds.width, "text2.getBoundingClientRect().width");
isRounded(text2Bounds.height, text1Bounds.height, "text2.getBoundingClientRect().height");
is(text2Bounds.left, text1Bounds.left + 100, "text2.getBoundingClientRect().left");
is(text2Bounds.top, text1Bounds.top, "text2.getBoundingClientRect().top");
is(text2Bounds.width, text1Bounds.width, "text2.getBoundingClientRect().width");
is(text2Bounds.height, text1Bounds.height, "text2.getBoundingClientRect().height");
isRounded(text3Bounds.width, (text1Bounds.width + text1Bounds.height) * sin45 + .5, "text3.getBoundingClientRect().width");
isRounded(text3Bounds.height, (text1Bounds.height + text1Bounds.width) * sin45 + .5, "text3.getBoundingClientRect().height");
isRounded(text4Bounds.width, 2 * (text1Bounds.width + text1Bounds.height) * sin45, "text4.getBoundingClientRect().width");
isRounded(text4Bounds.height, 2 * ((text1Bounds.height + text1Bounds.width) * sin45 - .5), "text4.getBoundingClientRect().height");
var r = (text1Bounds.width + text1Bounds.height) * sin45;
isApproximately(text3Bounds.width, Math.ceil(r), "text3.getBoundingClientRect().width");
isApproximately(text3Bounds.height, Math.ceil(r), "text3.getBoundingClientRect().height");
var rect1Bounds = doc.getElementById("rect1").getBoundingClientRect();
var rect2Bounds = doc.getElementById("rect2").getBoundingClientRect();
var rect3Bounds = doc.getElementById("rect3").getBoundingClientRect();
var rect4Bounds = doc.getElementById("rect4").getBoundingClientRect();
isRounded(rect1Bounds.left, 50, "rect1.getBoundingClientRect().left");
isRounded(rect1Bounds.top, 50, "rect1.getBoundingClientRect().top");
isRounded(rect1Bounds.width, 50, "rect1.getBoundingClientRect().width");
isRounded(rect1Bounds.height, 50, "rect1.getBoundingClientRect().height");
is(rect1Bounds.left, 50, "rect1.getBoundingClientRect().left");
is(rect1Bounds.top, 50, "rect1.getBoundingClientRect().top");
is(rect1Bounds.width, 50, "rect1.getBoundingClientRect().width");
is(rect1Bounds.height, 50, "rect1.getBoundingClientRect().height");
isRounded(rect2Bounds.left, 175 - 50 * sin45 - .5, "rect2.getBoundingClientRect().left");
isRounded(rect2Bounds.top, 75 - 50 * sin45 - .5, "rect2.getBoundingClientRect().top");
isRounded(rect2Bounds.width, (50 * sin45 + .5) * 2, "rect2.getBoundingClientRect().width");
isRounded(rect2Bounds.height, (50 * sin45 + .5) * 2, "rect2.getBoundingClientRect().height");
rect = new Rect(175 - 50 * sin45, 75 - 50 * sin45, 50 * sin45 * 2, 50 * sin45 * 2);
rect.roundOut();
is(rect2Bounds.left, rect.left, "rect2.getBoundingClientRect().left");
is(rect2Bounds.top, rect.top, "rect2.getBoundingClientRect().top");
is(rect2Bounds.width, rect.width, "rect2.getBoundingClientRect().width");
is(rect2Bounds.height, rect.height, "rect2.getBoundingClientRect().height");
isRounded(rect3Bounds.left, 50, "rect3.getBoundingClientRect().left");
isRounded(rect3Bounds.top, 160, "rect3.getBoundingClientRect().top");
isRounded(rect3Bounds.width, 100, "rect3.getBoundingClientRect().width");
isRounded(rect3Bounds.height, 100, "rect3.getBoundingClientRect().height");
is(rect3Bounds.left, 50, "rect3.getBoundingClientRect().left");
is(rect3Bounds.top, 160, "rect3.getBoundingClientRect().top");
is(rect3Bounds.width, 100, "rect3.getBoundingClientRect().width");
is(rect3Bounds.height, 100, "rect3.getBoundingClientRect().height");
isRounded(rect4Bounds.left, 350 - 100 * sin45 - .5, "rect4.getBoundingClientRect().left");
isRounded(rect4Bounds.top, 150 - 100 * sin45 - .5, "rect4.getBoundingClientRect().top");
isRounded(rect4Bounds.width, (100 * sin45 + .5) * 2, "rect4.getBoundingClientRect().width");
isRounded(rect4Bounds.height, (100 * sin45 + .5) * 2, "rect4.getBoundingClientRect().height");
rect = new Rect(350 - 100 * sin45, 150 - 100 * sin45, 100 * sin45 * 2, 100 * sin45 * 2);
rect.roundOut();
is(rect4Bounds.left, rect.left, "rect4.getBoundingClientRect().left");
is(rect4Bounds.top, rect.top, "rect4.getBoundingClientRect().top");
is(rect4Bounds.width, rect.width, "rect4.getBoundingClientRect().width");
is(rect4Bounds.height, rect.height, "rect4.getBoundingClientRect().height");
var rect1aBounds = doc.getElementById("rect1a").getBoundingClientRect();
var rect2aBounds = doc.getElementById("rect2a").getBoundingClientRect();
var rect3aBounds = doc.getElementById("rect3a").getBoundingClientRect();
var rect4aBounds = doc.getElementById("rect4a").getBoundingClientRect();
isRounded(rect1aBounds.left, 49, "rect1a.getBoundingClientRect().left");
isRounded(rect1aBounds.top, 49, "rect1a.getBoundingClientRect().top");
isRounded(rect1aBounds.width, 52, "rect1a.getBoundingClientRect().width");
isRounded(rect1aBounds.height, 52, "rect1a.getBoundingClientRect().height");
is(rect1aBounds.left, 48, "rect1a.getBoundingClientRect().left");
is(rect1aBounds.top, 48, "rect1a.getBoundingClientRect().top");
is(rect1aBounds.width, 54, "rect1a.getBoundingClientRect().width");
is(rect1aBounds.height, 54, "rect1a.getBoundingClientRect().height");
isRounded(rect2aBounds.left, 175 - 52 * sin45 - .5, "rect2a.getBoundingClientRect().left");
isRounded(rect2aBounds.top, 75 - 52 * sin45 - .5, "rect2a.getBoundingClientRect().top");
isRounded(rect2aBounds.width, 52 * sin45 * 2, "rect2a.getBoundingClientRect().width");
isRounded(rect2aBounds.height, 52 * sin45 * 2, "rect2a.getBoundingClientRect().height");
rect = new Rect(175 - 54 * sin45, 75 - 54 * sin45, 54 * sin45 * 2, 54 * sin45 * 2);
rect.roundOut();
is(rect2aBounds.left, rect.left, "rect2a.getBoundingClientRect().left");
is(rect2aBounds.top, rect.top, "rect2a.getBoundingClientRect().top");
is(rect2aBounds.width, rect.width, "rect2a.getBoundingClientRect().width");
is(rect2aBounds.height, rect.height, "rect2a.getBoundingClientRect().height");
isRounded(rect3aBounds.left, 48, "rect3a.getBoundingClientRect().left");
isRounded(rect3aBounds.top, 158, "rect3a.getBoundingClientRect().top");
isRounded(rect3aBounds.width, 104, "rect3a.getBoundingClientRect().width");
isRounded(rect3aBounds.height, 104, "rect3a.getBoundingClientRect().height");
is(rect3aBounds.left, 46, "rect3a.getBoundingClientRect().left");
is(rect3aBounds.top, 156, "rect3a.getBoundingClientRect().top");
is(rect3aBounds.width, 108, "rect3a.getBoundingClientRect().width");
is(rect3aBounds.height, 108, "rect3a.getBoundingClientRect().height");
isRounded(rect4aBounds.left, 350 - 104 * sin45 - .5, "rect4a.getBoundingClientRect().left");
isRounded(rect4aBounds.top, 150 - 104 * sin45 - .5, "rect4a.getBoundingClientRect().top");
isRounded(rect4aBounds.width, (104 * sin45 + .5) * 2, "rect4a.getBoundingClientRect().width");
isRounded(rect4aBounds.height, (104 * sin45 + .5) * 2, "rect4a.getBoundingClientRect().height");
rect = new Rect(350 - 108 * sin45, 150 - 108 * sin45, 108 * sin45 * 2, 108 * sin45 * 2);
rect.roundOut();
is(rect4aBounds.left, rect.left, "rect4a.getBoundingClientRect().left");
is(rect4aBounds.top, rect.top, "rect4a.getBoundingClientRect().top");
is(rect4aBounds.width, rect.width, "rect4a.getBoundingClientRect().width");
is(rect4aBounds.height, rect.height, "rect4a.getBoundingClientRect().height");
var text1a = doc.getElementById("text1a");
var text1aBounds = text1a.getBoundingClientRect();
var text2aBounds = doc.getElementById("text2a").getBoundingClientRect();
var len = text1a.getComputedTextLength();
isApproximately(text1aBounds.left, 82, "text1a.getBoundingClientRect().left");
is(text1aBounds.width, text1Bounds.width + 4, "text1a.getBoundingClientRect().width");
isRounded(text1aBounds.left, 85 - 1, "text1a.getBoundingClientRect().left");
isRounded(text1aBounds.width, len + 1, "text1a.getBoundingClientRect().width");
isRounded(text2aBounds.left, text1aBounds.left + 100 - 4, "text2a.getBoundingClientRect().left");
isRounded(text2aBounds.width, text1aBounds.width + 9, "text2a.getBoundingClientRect().width");
is(text2aBounds.left, text1aBounds.left + 100 - 3, "text2a.getBoundingClientRect().left");
is(text2aBounds.width, text1aBounds.width + 6, "text2a.getBoundingClientRect().width");
SimpleTest.finish();
}

Просмотреть файл

@ -69,7 +69,6 @@
#include "nsGenericElement.h"
#include "nsSVGGraphicElement.h"
#include "nsAttrValue.h"
#include "nsSVGGeometryFrame.h"
#include "nsIScriptError.h"
#include "gfxContext.h"
#include "gfxMatrix.h"
@ -85,6 +84,7 @@
#include "nsSVGGeometryFrame.h"
#include "nsComputedDOMStyle.h"
#include "nsSVGPathGeometryFrame.h"
#include "nsSVGPathGeometryElement.h"
#include "prdtoa.h"
#include "mozilla/dom/Element.h"
#include "gfxUtils.h"
@ -1431,26 +1431,14 @@ nsSVGUtils::WritePPM(const char *fname, gfxImageSurface *aSurface)
}
#endif
/*static*/ gfxRect
nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
nsSVGGeometryFrame* aFrame)
// The logic here comes from _cairo_stroke_style_max_distance_from_path
static gfxRect
PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
nsSVGGeometryFrame* aFrame,
double styleExpansionFactor)
{
// The logic here comes from _cairo_stroke_style_max_distance_from_path
double style_expansion = 0.5;
const nsStyleSVG* style = aFrame->GetStyleSVG();
if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
style_expansion = M_SQRT1_2;
}
if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
style_expansion < style->mStrokeMiterlimit) {
style_expansion = style->mStrokeMiterlimit;
}
style_expansion *= aFrame->GetStrokeWidth();
double style_expansion =
styleExpansionFactor * aFrame->GetStrokeWidth();
gfxMatrix ctm = aFrame->GetCanvasTM();
@ -1462,6 +1450,38 @@ nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
return strokeExtents;
}
/*static*/ gfxRect
nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
nsSVGGeometryFrame* aFrame)
{
return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5);
}
/*static*/ gfxRect
nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
nsSVGPathGeometryFrame* aFrame)
{
double styleExpansionFactor = 0.5;
if (static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
const nsStyleSVG* style = aFrame->GetStyleSVG();
if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
styleExpansionFactor = M_SQRT1_2;
}
if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
styleExpansionFactor < style->mStrokeMiterlimit &&
aFrame->GetContent()->Tag() != nsGkAtoms::line) {
styleExpansionFactor = style->mStrokeMiterlimit;
}
}
return ::PathExtentsToMaxStrokeExtents(aPathExtents,
aFrame,
styleExpansionFactor);
}
// ----------------------------------------------------------------------
nsSVGRenderState::nsSVGRenderState(nsRenderingContext *aContext) :

Просмотреть файл

@ -76,6 +76,7 @@ struct nsStyleFont;
class nsSVGEnum;
class nsISVGChildFrame;
class nsSVGGeometryFrame;
class nsSVGPathGeometryFrame;
class nsSVGDisplayContainerFrame;
namespace mozilla {
@ -564,6 +565,8 @@ public:
*/
static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
nsSVGGeometryFrame* aFrame);
static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
nsSVGPathGeometryFrame* aFrame);
/**
* Convert a floating-point value to a 32-bit integer value, clamping to