Bug 999964 part 1 - Patch for SVG 2 getBBox method; r=longsonr, r=bz

This commit is contained in:
Shigeyuki Tsukihashi 2014-05-13 10:24:35 +09:00
parent 1021555d7c
commit 4a3b14fabe
8 changed files with 181 additions and 9 deletions

View File

@ -5,6 +5,7 @@
#include "gfx2DGlue.h"
#include "mozilla/dom/SVGAnimatedTransformList.h"
#include "mozilla/dom/SVGGraphicsElementBinding.h"
#include "mozilla/dom/SVGTransformableElement.h"
#include "mozilla/dom/SVGMatrix.h"
#include "mozilla/dom/SVGSVGElement.h"
@ -182,7 +183,8 @@ SVGTransformableElement::GetFarthestViewportElement()
}
already_AddRefed<SVGIRect>
SVGTransformableElement::GetBBox(ErrorResult& rv)
SVGTransformableElement::GetBBox(const SVGBoundingBoxOptions& aOptions,
ErrorResult& rv)
{
nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
@ -190,14 +192,37 @@ SVGTransformableElement::GetBBox(ErrorResult& rv)
rv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsISVGChildFrame* svgframe = do_QueryFrame(frame);
if (!svgframe) {
rv.Throw(NS_ERROR_NOT_IMPLEMENTED); // XXX: outer svg
return nullptr;
}
return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame)));
if (!NS_SVGNewGetBBoxEnabled()) {
return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame)));
} else {
uint32_t aFlags = 0;
if (aOptions.mFill) {
aFlags |= nsSVGUtils::eBBoxIncludeFill;
}
if (aOptions.mStroke) {
aFlags |= nsSVGUtils::eBBoxIncludeStroke;
}
if (aOptions.mMarkers) {
aFlags |= nsSVGUtils::eBBoxIncludeMarkers;
}
if (aOptions.mClipped) {
aFlags |= nsSVGUtils::eBBoxIncludeClipped;
}
if (aFlags == 0) {
return NS_NewSVGRect(this,0,0,0,0);
}
if (aFlags == nsSVGUtils::eBBoxIncludeMarkers ||
aFlags == nsSVGUtils::eBBoxIncludeClipped) {
aFlags |= nsSVGUtils::eBBoxIncludeFill;
}
return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame, aFlags)));
}
}
already_AddRefed<SVGMatrix>

View File

@ -19,6 +19,7 @@ class SVGAnimatedTransformList;
class SVGGraphicsElement;
class SVGMatrix;
class SVGIRect;
class SVGBoundingBoxOptions;
class SVGTransformableElement : public nsSVGElement
{
@ -33,7 +34,8 @@ public:
already_AddRefed<SVGAnimatedTransformList> Transform();
nsSVGElement* GetNearestViewportElement();
nsSVGElement* GetFarthestViewportElement();
already_AddRefed<SVGIRect> GetBBox(ErrorResult& rv);
already_AddRefed<SVGIRect> GetBBox(const SVGBoundingBoxOptions& aOptions,
ErrorResult& rv);
already_AddRefed<SVGMatrix> GetCTM();
already_AddRefed<SVGMatrix> GetScreenCTM();
already_AddRefed<SVGMatrix> GetTransformToElement(SVGGraphicsElement& aElement,

View File

@ -10,6 +10,13 @@
* liability, trademark and document use rules apply.
*/
dictionary SVGBoundingBoxOptions {
boolean fill = true;
boolean stroke = false;
boolean markers = false;
boolean clipped = false;
};
interface SVGGraphicsElement : SVGElement {
readonly attribute SVGAnimatedTransformList transform;
@ -17,7 +24,7 @@ interface SVGGraphicsElement : SVGElement {
readonly attribute SVGElement? farthestViewportElement;
[NewObject, Throws]
SVGRect getBBox();
SVGRect getBBox(optional SVGBoundingBoxOptions aOptions);
// Not implemented
// SVGRect getStrokeBBox();
SVGMatrix? getCTM();

View File

@ -325,3 +325,45 @@ nsSVGClipPathFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
&content->mEnumAttributes[SVGClipPathElement::CLIPPATHUNITS],
mClipParent);
}
SVGBBox
nsSVGClipPathFrame::GetBBoxForClipPathFrame(const SVGBBox &aBBox,
const gfxMatrix &aMatrix)
{
nsIContent* node = GetContent()->GetFirstChild();
SVGBBox unionBBox, tmpBBox;
for (; node; node = node->GetNextSibling()) {
nsIFrame *frame =
static_cast<nsSVGElement*>(node)->GetPrimaryFrame();
if (frame) {
nsISVGChildFrame *svg = do_QueryFrame(frame);
if (svg) {
tmpBBox = svg->GetBBoxContribution(mozilla::gfx::ToMatrix(aMatrix),
nsSVGUtils::eBBoxIncludeFill);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(frame);
bool isOK = true;
nsSVGClipPathFrame *clipPathFrame =
effectProperties.GetClipPathFrame(&isOK);
if (clipPathFrame && isOK) {
tmpBBox = clipPathFrame->GetBBoxForClipPathFrame(tmpBBox, aMatrix);
}
tmpBBox.Intersect(aBBox);
unionBBox.UnionEdges(tmpBBox);
}
}
}
nsSVGEffects::EffectProperties props =
nsSVGEffects::GetEffectProperties(this);
if (props.mClipPath) {
bool isOK = true;
nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK);
if (clipPathFrame && isOK) {
tmpBBox = clipPathFrame->GetBBoxForClipPathFrame(aBBox, aMatrix);
unionBBox.Intersect(tmpBBox);
} else if (!isOK) {
unionBBox = SVGBBox();
}
}
return unionBBox;
}

View File

@ -75,6 +75,9 @@ public:
}
#endif
SVGBBox
GetBBoxForClipPathFrame(const SVGBBox &aBBox, const gfxMatrix &aMatrix);
private:
// A helper class to allow us to paint clip paths safely. The helper
// automatically sets and clears the mInUse flag on the clip path frame

View File

@ -42,6 +42,7 @@
#include "nsSVGLength2.h"
#include "nsSVGMaskFrame.h"
#include "nsSVGOuterSVGFrame.h"
#include "mozilla/dom/SVGClipPathElement.h"
#include "mozilla/dom/SVGPathElement.h"
#include "nsSVGPathGeometryElement.h"
#include "nsSVGPathGeometryFrame.h"
@ -57,6 +58,7 @@ using namespace mozilla::gfx;
static bool sSVGDisplayListHitTestingEnabled;
static bool sSVGDisplayListPaintingEnabled;
static bool sSVGNewGetBBoxEnabled;
bool
NS_SVGDisplayListHitTestingEnabled()
@ -70,6 +72,13 @@ NS_SVGDisplayListPaintingEnabled()
return sSVGDisplayListPaintingEnabled;
}
bool
NS_SVGNewGetBBoxEnabled()
{
return sSVGNewGetBBoxEnabled;
}
// we only take the address of this:
static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
@ -130,6 +139,9 @@ nsSVGUtils::Init()
Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
"svg.display-lists.painting.enabled");
Preferences::AddBoolVarCache(&sSVGNewGetBBoxEnabled,
"svg.new-getBBox.enabled");
}
nsSVGDisplayContainerFrame*
@ -885,7 +897,8 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
return bbox;
}
gfxMatrix matrix;
if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame ||
aFrame->GetType() == nsGkAtoms::svgUseFrame) {
// The spec says getBBox "Returns the tight bounding box in *current user
// space*". So we should really be doing this for all elements, but that
// needs investigation to check that we won't break too much content.
@ -896,7 +909,63 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
matrix = element->PrependLocalTransformsTo(matrix,
nsSVGElement::eChildToUserSpace);
}
return svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
bbox = svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
// Account for 'clipped'.
if (aFlags & nsSVGUtils::eBBoxIncludeClipped) {
gfxRect clipRect(0, 0, 0, 0);
float x, y, width, height;
gfxMatrix tm;
gfxRect fillBBox =
svg->GetBBoxContribution(ToMatrix(tm),
nsSVGUtils::eBBoxIncludeFill).ToThebesRect();
x = fillBBox.x;
y = fillBBox.y;
width = fillBBox.width;
height = fillBBox.height;
bool hasClip = aFrame->StyleDisplay()->IsScrollableOverflow();
if (hasClip) {
clipRect =
nsSVGUtils::GetClipRectForFrame(aFrame, x, y, width, height);
if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame ||
aFrame->GetType() == nsGkAtoms::svgUseFrame) {
clipRect = matrix.TransformBounds(clipRect);
}
}
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(aFrame);
bool isOK = true;
nsSVGClipPathFrame *clipPathFrame =
effectProperties.GetClipPathFrame(&isOK);
if (clipPathFrame && isOK) {
SVGClipPathElement *clipContent =
static_cast<SVGClipPathElement*>(clipPathFrame->GetContent());
nsRefPtr<SVGAnimatedEnumeration> units = clipContent->ClipPathUnits();
if (units->AnimVal() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
matrix = gfxMatrix().Scale(width, height) *
gfxMatrix().Translate(gfxPoint(x, y)) *
matrix;
} else if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
matrix.Reset();
}
bbox =
clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix).ToThebesRect();
if (hasClip) {
bbox = bbox.Intersect(clipRect);
}
} else {
if (!isOK) {
bbox = gfxRect(0, 0, 0, 0);
} else {
if (hasClip) {
bbox = bbox.Intersect(clipRect);
}
}
}
if (bbox.IsEmpty()) {
bbox = gfxRect(0, 0, 0, 0);
}
}
return bbox;
}
return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
}
@ -919,7 +988,8 @@ nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
// For foreignObject frames, nsSVGUtils::GetBBox applies their local
// transform, so we need to do the same here.
if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame ||
aFrame->GetType() == nsGkAtoms::svgUseFrame) {
gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
PrependLocalTransformsTo(gfxMatrix(),
nsSVGElement::eChildToUserSpace);

View File

@ -37,6 +37,7 @@ class nsPresContext;
class nsRenderingContext;
class nsStyleContext;
class nsStyleCoord;
class nsSVGClipPathFrame;
class nsSVGDisplayContainerFrame;
class nsSVGElement;
class nsSVGEnum;
@ -76,6 +77,7 @@ class SourceSurface;
bool NS_SVGDisplayListHitTestingEnabled();
bool NS_SVGDisplayListPaintingEnabled();
bool NS_SVGNewGetBBoxEnabled();
/**
* Sometimes we need to distinguish between an empty box and a box
@ -110,6 +112,19 @@ public:
mIsEmpty = false;
}
void Intersect(const SVGBBox& aSVGBBox) {
if (!mIsEmpty && !aSVGBBox.mIsEmpty) {
mBBox = mBBox.Intersect(aSVGBBox.mBBox);
if (mBBox.IsEmpty()) {
mIsEmpty = true;
mBBox = Rect(0, 0, 0, 0);
}
} else {
mIsEmpty = true;
mBBox = Rect(0, 0, 0, 0);
}
}
private:
Rect mBBox;
bool mIsEmpty;
@ -396,7 +411,8 @@ public:
eBBoxIncludeFillGeometry = 1 << 1,
eBBoxIncludeStroke = 1 << 2,
eBBoxIncludeStrokeGeometry = 1 << 3,
eBBoxIncludeMarkers = 1 << 4
eBBoxIncludeMarkers = 1 << 4,
eBBoxIncludeClipped = 1 << 5
};
/**
* Get the SVG bbox (the SVG spec's simplified idea of bounds) of aFrame in

View File

@ -2088,6 +2088,13 @@ pref("svg.svg-iframe.enabled", false);
pref("svg.svg-iframe.enabled", false);
#endif
// Is support for the new getBBox method from SVG 2 enabled?
// See https://svgwg.org/svg2-draft/single-page.html#types-SVGBoundingBoxOptions
#ifdef RELEASE_BUILD
pref("svg.new-getBBox.enabled", false);
#else
pref("svg.new-getBBox.enabled", true);
#endif
// Default font types and sizes by locale
pref("font.default.ar", "sans-serif");