Bug 450340. Support SVG mask/clip-path/filter CSS properties applied to non-SVG content. r=longsonr,sr=mats

This commit is contained in:
Robert O'Callahan 2008-09-11 12:24:16 +12:00
parent 720062e552
commit b52d3794d5
63 changed files with 1918 additions and 472 deletions

View File

@ -1606,6 +1606,7 @@ GK_ATOM(IBSplitSpecialSibling, "IBSplitSpecialSibling") // nsIFrame*
GK_ATOM(lineCursorProperty, "LineCursorProperty") // nsLineBox*
GK_ATOM(rowCursorProperty, "RowCursorProperty") // nsTableRowGroupFrame::FrameCursorData*
GK_ATOM(maxElementWidthProperty, "MaxElementWidthProperty") // nscoord*
GK_ATOM(outlineInnerRectProperty, "OutlineInnerRectProperty") // nsRect*
GK_ATOM(outOfFlowDirtyRectProperty, "OutOfFlowDirtyRectProperty") // nsRect*
GK_ATOM(overflowAreaProperty, "OverflowArea") // nsRect*
GK_ATOM(overflowProperty, "OverflowProperty") // list of nsIFrame*
@ -1614,6 +1615,7 @@ GK_ATOM(excessOverflowContainersProperty, "ExcessOverflowContainersProperty") //
GK_ATOM(overflowLinesProperty, "OverflowLinesProperty") // list of nsLineBox*
GK_ATOM(overflowOutOfFlowsProperty, "OverflowOutOfFlowsProperty") // nsFrameList*
GK_ATOM(overflowPlaceholdersProperty, "OverflowPlaceholdersProperty") // nsFrameList*
GK_ATOM(preEffectsBBoxProperty, "PreEffectsBBoxProperty") // nsRect*
GK_ATOM(rowUnpaginatedHeightProperty, "RowUnpaginatedHeightProperty") // nscoord*
GK_ATOM(spaceManagerProperty, "SpaceManagerProperty") // the space manager for a block
GK_ATOM(tabWidthProperty, "TabWidthProperty") // nsTArray<TabSetting>* array of tab widths

View File

@ -54,6 +54,7 @@ class nsSVGFilterElement : public nsSVGFilterElementBase,
public nsIDOMSVGUnitTypes
{
friend class nsSVGFilterFrame;
friend class nsAutoFilterInstance;
protected:
friend nsresult NS_NewSVGFilterElement(nsIContent **aResult,

View File

@ -42,6 +42,7 @@
#include "nsIFrame.h"
#include "gfxRect.h"
#include "gfxImageSurface.h"
#include "nsIDOMSVGFilters.h"
class nsSVGFilterResource;
class nsSVGString;

View File

@ -40,6 +40,8 @@
#include "prdtoa.h"
#include "nsTextFormatter.h"
#include "nsSVGSVGElement.h"
#include "nsIFrame.h"
#include "nsSVGIntegrationUtils.h"
NS_IMPL_ADDREF(nsSVGLength2::DOMBaseVal)
NS_IMPL_RELEASE(nsSVGLength2::DOMBaseVal)
@ -179,20 +181,51 @@ nsSVGLength2::GetMMPerPixel(nsSVGSVGElement *aCtx) const
return mmPerPx;
}
float
nsSVGLength2::GetMMPerPixel(nsIFrame *aNonSVGFrame) const
{
nsPresContext* presContext = aNonSVGFrame->PresContext();
float pixelsPerInch =
presContext->AppUnitsToFloatCSSPixels(presContext->AppUnitsPerInch());
return 25.4f/pixelsPerInch;
}
static float
FixAxisLength(float aLength)
{
if (aLength == 0.0f) {
NS_WARNING("zero axis length");
return 1e-20f;
}
return aLength;
}
float
nsSVGLength2::GetAxisLength(nsSVGSVGElement *aCtx) const
{
if (!aCtx)
return 1;
float d = aCtx->GetLength(mCtxType);
return FixAxisLength(aCtx->GetLength(mCtxType));
}
if (d == 0.0f) {
NS_WARNING("zero axis length");
d = 1e-20f;
float
nsSVGLength2::GetAxisLength(nsIFrame *aNonSVGFrame) const
{
gfxRect rect = nsSVGIntegrationUtils::GetSVGRectForNonSVGFrame(aNonSVGFrame);
float length;
switch (mCtxType) {
case nsSVGUtils::X: length = rect.Width(); break;
case nsSVGUtils::Y: length = rect.Height(); break;
case nsSVGUtils::XY:
length = nsSVGUtils::ComputeNormalizedHypotenuse(rect.Width(), rect.Height());
break;
default:
NS_NOTREACHED("Unknown axis type");
length = 1;
break;
}
return d;
return FixAxisLength(length);
}
float
@ -240,6 +273,39 @@ nsSVGLength2::GetUnitScaleFactor(nsSVGSVGElement *aCtx) const
}
}
float
nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame) const
{
nsIContent* content = aFrame->GetContent();
if (content->IsNodeOfType(nsINode::eSVG))
return GetUnitScaleFactor(static_cast<nsSVGElement*>(content));
switch (mSpecifiedUnitType) {
case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER:
case nsIDOMSVGLength::SVG_LENGTHTYPE_PX:
return 1;
case nsIDOMSVGLength::SVG_LENGTHTYPE_MM:
return GetMMPerPixel(aFrame);
case nsIDOMSVGLength::SVG_LENGTHTYPE_CM:
return GetMMPerPixel(aFrame) / 10.0f;
case nsIDOMSVGLength::SVG_LENGTHTYPE_IN:
return GetMMPerPixel(aFrame) / 25.4f;
case nsIDOMSVGLength::SVG_LENGTHTYPE_PT:
return GetMMPerPixel(aFrame) * POINTS_PER_INCH_FLOAT / 25.4f;
case nsIDOMSVGLength::SVG_LENGTHTYPE_PC:
return GetMMPerPixel(aFrame) * POINTS_PER_INCH_FLOAT / 24.4f / 12.0f;
case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE:
return 100.0f / GetAxisLength(aFrame);
case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS:
return 1 / GetEmLength(aFrame);
case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS:
return 1 / GetExLength(aFrame);
default:
NS_NOTREACHED("Unknown unit type");
return 0;
}
}
void
nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue,
nsSVGElement *aSVGElement)

View File

@ -43,6 +43,8 @@
#include "nsSVGElement.h"
#include "nsDOMError.h"
class nsIFrame;
class nsSVGLength2
{
@ -64,10 +66,12 @@ public:
void GetBaseValueString(nsAString& aValue);
void GetAnimValueString(nsAString& aValue);
float GetBaseValue(nsSVGElement* aSVGElement)
float GetBaseValue(nsSVGElement* aSVGElement) const
{ return mBaseVal / GetUnitScaleFactor(aSVGElement); }
float GetAnimValue(nsSVGElement* aSVGElement)
float GetAnimValue(nsSVGElement* aSVGElement) const
{ return mAnimVal / GetUnitScaleFactor(aSVGElement); }
float GetAnimValue(nsIFrame* aFrame) const
{ return mAnimVal / GetUnitScaleFactor(aFrame); }
PRUint8 GetCtxType() const { return mCtxType; }
PRUint8 GetSpecifiedUnitType() const { return mSpecifiedUnitType; }
@ -76,9 +80,9 @@ public:
float GetAnimValInSpecifiedUnits() const { return mAnimVal; }
float GetBaseValInSpecifiedUnits() const { return mBaseVal; }
float GetBaseValue(nsSVGSVGElement* aCtx)
float GetBaseValue(nsSVGSVGElement* aCtx) const
{ return mBaseVal / GetUnitScaleFactor(aCtx); }
float GetAnimValue(nsSVGSVGElement* aCtx)
float GetAnimValue(nsSVGSVGElement* aCtx) const
{ return mAnimVal / GetUnitScaleFactor(aCtx); }
nsresult ToDOMAnimatedLength(nsIDOMSVGAnimatedLength **aResult,
@ -93,6 +97,14 @@ private:
PRUint8 mCtxType; // X, Y or Unspecified
PRPackedBool mIsAnimated;
float GetMMPerPixel(nsIFrame *aNonSVGFrame) const;
float GetAxisLength(nsIFrame *aNonSVGFrame) const;
float GetEmLength(nsIFrame *aFrame) const
{ return nsSVGUtils::GetFontSize(aFrame); }
float GetExLength(nsIFrame *aFrame) const
{ return nsSVGUtils::GetFontXHeight(aFrame); }
float GetUnitScaleFactor(nsIFrame *aFrame) const;
float GetMMPerPixel(nsSVGSVGElement *aCtx) const;
float GetAxisLength(nsSVGSVGElement *aCtx) const;
float GetEmLength(nsSVGElement *aSVGElement) const

View File

@ -489,7 +489,7 @@ static nsIFrame* GetSpecialSibling(nsIFrame* aFrame)
}
static nsIFrame*
GetIBSplitSpecialPrevSibling(nsIFrame* aFrame)
GetIBSplitSpecialPrevSiblingForAnonymousBlock(nsIFrame* aFrame)
{
NS_PRECONDITION(IsFrameSpecial(aFrame) && !IsInlineFrame(aFrame),
"Shouldn't call this");
@ -608,10 +608,7 @@ FindLastBlock(nsIFrame* aKid)
}
/*
* Unlike the special (next) sibling, the special previous sibling
* property points only from the anonymous block to the original
* inline that preceded it. DO NOT CHANGE THAT -- the
* GetParentStyleContextFrame code depends on it! It is useful for
* The special-prev-sibling is useful for
* finding the "special parent" of a frame (i.e., a frame from which a
* good parent style context can be obtained), one looks at the
* special previous sibling annotation of the real parent of the frame
@ -7837,7 +7834,8 @@ nsCSSFrameConstructor::AppendFrames(nsFrameConstructorState& aState,
nsIContent* content = nsnull;
nsStyleContext* styleContext = nsnull;
if (!inlineSibling) {
nsIFrame* firstInline = GetIBSplitSpecialPrevSibling(parentFrame);
nsIFrame* firstInline =
GetIBSplitSpecialPrevSiblingForAnonymousBlock(parentFrame);
NS_ASSERTION(firstInline, "How did that happen?");
content = firstInline->GetContent();
@ -12455,8 +12453,11 @@ nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState,
SetFrameIsSpecial(aNewFrame, blockFrame);
SetFrameIsSpecial(blockFrame, inlineFrame);
MarkIBSpecialPrevSibling(blockFrame, aNewFrame);
if (inlineFrame) {
MarkIBSpecialPrevSibling(inlineFrame, blockFrame);
}
#ifdef DEBUG
#ifdef DEBUG
if (gNoisyInlineConstruction) {
nsIFrameDebug* frameDebug;
@ -12725,8 +12726,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState,
// too.
nsIFrame* floatContainer = aFrame;
do {
floatContainer =
GetFloatContainingBlock(GetIBSplitSpecialPrevSibling(floatContainer));
floatContainer = GetFloatContainingBlock(
GetIBSplitSpecialPrevSiblingForAnonymousBlock(floatContainer));
if (!floatContainer) {
break;
}

View File

@ -642,6 +642,16 @@ nsCSSRendering::PaintBorder(nsPresContext* aPresContext,
SN();
}
static nsRect
GetOutlineInnerRect(nsIFrame* aFrame)
{
nsRect* savedOutlineInnerRect = static_cast<nsRect*>
(aFrame->GetProperty(nsGkAtoms::outlineInnerRectProperty));
if (savedOutlineInnerRect)
return *savedOutlineInnerRect;
return aFrame->GetOverflowRect();
}
void
nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@ -671,9 +681,6 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
// get the radius for our outline
GetBorderRadiusTwips(aOutlineStyle.mOutlineRadius, aBorderArea.width, twipsRadii);
nscoord offset;
aOutlineStyle.GetOutlineOffset(offset);
// When the outline property is set on :-moz-anonymous-block or
// :-moz-anonyomus-positioned-block pseudo-elements, it inherited that
// outline from the inline that was broken because it contained a
@ -690,47 +697,35 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
frameForArea = frameForArea->GetFirstChild(nsnull);
NS_ASSERTION(frameForArea, "anonymous block with no children?");
} while (frameForArea);
nsRect overflowArea;
nsRect innerRect; // relative to aBorderArea.TopLeft()
if (frameForArea == aForFrame) {
overflowArea = aForFrame->GetOverflowRect();
innerRect = GetOutlineInnerRect(aForFrame);
} else {
for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
// The outline has already been included in aForFrame's overflow
// area, but not in those of its descendants, so we have to
// include it. Otherwise we'll end up drawing the outline inside
// the border.
nsRect r(frameForArea->GetOverflowRect() +
nsRect r(GetOutlineInnerRect(frameForArea) +
frameForArea->GetOffsetTo(aForFrame));
nscoord delta = PR_MAX(offset + width, 0);
r.Inflate(delta, delta);
overflowArea.UnionRect(overflowArea, r);
innerRect.UnionRect(innerRect, r);
}
}
nsRect outerRect(overflowArea + aBorderArea.TopLeft());
nsRect innerRect(outerRect);
if (width + offset >= 0) {
// the overflow area is exactly the outside edge of the outline
innerRect.Deflate(width, width);
} else {
// the overflow area is exactly the rectangle containing the frame and its
// children; we can compute the outline directly
innerRect.Deflate(-offset, -offset);
if (innerRect.width < 0 || innerRect.height < 0) {
return; // Protect against negative outline sizes
}
outerRect = innerRect;
outerRect.Inflate(width, width);
}
innerRect += aBorderArea.TopLeft();
nscoord offset;
aOutlineStyle.GetOutlineOffset(offset);
innerRect.Inflate(offset, offset);
// If the dirty rect is completely inside the border area (e.g., only the
// content is being painted), then we can skip out now
// XXX this isn't exactly true for rounded borders, where the inside curves may
// encroach into the content area. A safer calculation would be to
// shorten insideRect by the radius one each side before performing this test.
if (innerRect.Contains(aDirtyRect)) {
if (innerRect.Contains(aDirtyRect))
return;
}
nsRect outerRect = innerRect;
outerRect.Inflate(width, width);
// Get our conversion values
nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);

View File

@ -51,6 +51,9 @@
#include "nsFrameManager.h"
#include "gfxContext.h"
#include "nsStyleStructInlines.h"
#ifdef MOZ_SVG
#include "nsSVGIntegrationUtils.h"
#endif
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
PRBool aIsForEvents, PRBool aBuildCaret)
@ -267,6 +270,15 @@ nsDisplayList::FlattenTo(nsTArray<nsDisplayItem*>* aElements) {
}
}
nsRect
nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const {
nsRect bounds;
for (nsDisplayItem* i = GetBottom(); i != nsnull; i = i->GetAbove()) {
bounds.UnionRect(bounds, i->GetBounds(aBuilder));
}
return bounds;
}
void
nsDisplayList::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion) {
@ -671,11 +683,7 @@ nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
nsRect
nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder) {
nsRect bounds;
for (nsDisplayItem* i = mList.GetBottom(); i != nsnull; i = i->GetAbove()) {
bounds.UnionRect(bounds, i->GetBounds(aBuilder));
}
return bounds;
return mList.GetBounds(aBuilder);
}
PRBool
@ -928,3 +936,72 @@ nsDisplayWrapList* nsDisplayClip::WrapWithClone(nsDisplayListBuilder* aBuilder,
return new (aBuilder)
nsDisplayClip(aItem->GetUnderlyingFrame(), mClippingFrame, aItem, mClip);
}
#ifdef MOZ_SVG
nsDisplaySVGEffects::nsDisplaySVGEffects(nsIFrame* aFrame, nsDisplayList* aList)
: nsDisplayWrapList(aFrame, aList), mEffectsFrame(aFrame),
mBounds(aFrame->GetOverflowRect())
{
MOZ_COUNT_CTOR(nsDisplaySVGEffects);
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplaySVGEffects::~nsDisplaySVGEffects()
{
MOZ_COUNT_DTOR(nsDisplaySVGEffects);
}
#endif
PRBool nsDisplaySVGEffects::IsOpaque(nsDisplayListBuilder* aBuilder)
{
return PR_FALSE;
}
nsIFrame*
nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
HitTestState* aState)
{
if (!nsSVGIntegrationUtils::HitTestFrameForEffects(mEffectsFrame,
aPt - aBuilder->ToReferenceFrame(mEffectsFrame)))
return nsnull;
return mList.HitTest(aBuilder, aPt, aState);
}
void nsDisplaySVGEffects::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
{
nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx,
mEffectsFrame, aDirtyRect, aBuilder, &mList);
}
PRBool nsDisplaySVGEffects::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion) {
nsRegion vis;
vis.And(*aVisibleRegion, GetBounds(aBuilder));
nsPoint offset = aBuilder->ToReferenceFrame(mEffectsFrame);
nsRect dirtyRect = nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mEffectsFrame,
vis.GetBounds() - offset) + offset;
// Our children may be translucent so we should not allow them to subtract
// area from aVisibleRegion.
nsRegion childrenVisibleRegion(dirtyRect);
nsDisplayWrapList::OptimizeVisibility(aBuilder, &childrenVisibleRegion);
return !vis.IsEmpty();
}
PRBool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem)
{
if (aItem->GetType() != TYPE_SVG_EFFECTS)
return PR_FALSE;
// items for the same content element should be merged into a single
// compositing group
// aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
if (aItem->GetUnderlyingFrame()->GetContent() != mFrame->GetContent())
return PR_FALSE;
nsDisplaySVGEffects* other = static_cast<nsDisplaySVGEffects*>(aItem);
mList.AppendToBottom(&other->mList);
mBounds.UnionRect(mBounds,
other->mBounds + other->mEffectsFrame->GetOffsetTo(mEffectsFrame));
return PR_TRUE;
}
#endif

View File

@ -392,6 +392,9 @@ public:
TYPE_OUTLINE,
TYPE_CLIP,
TYPE_OPACITY,
#ifdef MOZ_SVG
TYPE_SVG_EFFECTS,
#endif
TYPE_WRAPLIST
};
@ -694,6 +697,10 @@ public:
*/
void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
const nsRect& aDirtyRect) const;
/**
* Get the bounds. Takes the union of the bounds of all children.
*/
nsRect GetBounds(nsDisplayListBuilder* aBuilder) const;
/**
* Find the topmost display item that returns a non-null frame, and return
* the frame.
@ -1252,4 +1259,39 @@ private:
nsRect mClip;
};
#ifdef MOZ_SVG
/**
* A display item to paint a stacking context with effects
* set by the stacking context root frame's style.
*/
class nsDisplaySVGEffects : public nsDisplayWrapList {
public:
nsDisplaySVGEffects(nsIFrame* aFrame, nsDisplayList* aList);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplaySVGEffects();
#endif
virtual Type GetType() { return TYPE_SVG_EFFECTS; }
virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder);
virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
HitTestState* aState);
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
return mBounds + aBuilder->ToReferenceFrame(mEffectsFrame);
}
virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
const nsRect& aDirtyRect);
virtual PRBool OptimizeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion);
virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
NS_DISPLAY_DECL_NAME("SVGEffects")
nsIFrame* GetEffectsFrame() { return mEffectsFrame; }
private:
nsIFrame* mEffectsFrame;
// relative to mEffectsFrame
nsRect mBounds;
};
#endif
#endif /*NSDISPLAYLIST_H_*/

View File

@ -79,6 +79,7 @@
#ifdef MOZ_SVG
#include "nsSVGUtils.h"
#include "nsSVGIntegrationUtils.h"
#include "nsSVGForeignObjectFrame.h"
#include "nsSVGOuterSVGFrame.h"
#endif
@ -1037,7 +1038,19 @@ AddItemsToRegion(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
nsDisplayList* sublist = item->GetList();
if (sublist) {
if (item->GetType() == nsDisplayItem::TYPE_CLIP) {
nsDisplayItem::Type type = item->GetType();
#ifdef MOZ_SVG
if (type == nsDisplayItem::TYPE_SVG_EFFECTS) {
nsDisplaySVGEffects* effectsItem = static_cast<nsDisplaySVGEffects*>(item);
if (!aBuilder->IsMovingFrame(effectsItem->GetEffectsFrame())) {
// Invalidate the whole thing
nsRect r;
r.IntersectRect(aClipRect, effectsItem->GetBounds(aBuilder));
aRegion->Or(*aRegion, r);
}
} else
#endif
if (type == nsDisplayItem::TYPE_CLIP) {
nsDisplayClip* clipItem = static_cast<nsDisplayClip*>(item);
nsRect clip = aClipRect;
// If the clipping frame is moving, then it isn't clipping any
@ -1241,45 +1254,63 @@ nsLayoutUtils::BinarySearchForPosition(nsIRenderingContext* aRendContext,
}
static void
AddRectsForFrame(nsIFrame* aFrame, nsIFrame* aRelativeTo,
nsLayoutUtils::RectCallback* aCallback)
AddBoxesForFrame(nsIFrame* aFrame,
nsLayoutUtils::BoxCallback* aCallback)
{
nsIAtom* pseudoType = aFrame->GetStyleContext()->GetPseudoType();
if (pseudoType == nsCSSAnonBoxes::tableOuter) {
AddRectsForFrame(aFrame->GetFirstChild(nsnull), aRelativeTo,
aCallback);
AddBoxesForFrame(aFrame->GetFirstChild(nsnull), aCallback);
nsIFrame* kid = aFrame->GetFirstChild(nsGkAtoms::captionList);
if (kid) {
AddRectsForFrame(kid, aRelativeTo, aCallback);
AddBoxesForFrame(kid, aCallback);
}
} else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock ||
pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock ||
pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock ||
pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) {
for (nsIFrame* kid = aFrame->GetFirstChild(nsnull); kid; kid = kid->GetNextSibling()) {
AddRectsForFrame(kid, aRelativeTo, aCallback);
AddBoxesForFrame(kid, aCallback);
}
} else {
aCallback->AddBox(aFrame);
}
}
void
nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback)
{
while (aFrame) {
AddBoxesForFrame(aFrame, aCallback);
aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame);
}
}
struct BoxToBorderRect : public nsLayoutUtils::BoxCallback {
nsIFrame* mRelativeTo;
nsLayoutUtils::RectCallback* mCallback;
BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback)
: mCallback(aCallback), mRelativeTo(aRelativeTo) {}
virtual void AddBox(nsIFrame* aFrame) {
#ifdef MOZ_SVG
nsRect r;
nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r);
if (outer) {
aCallback->AddRect(r + outer->GetOffsetTo(aRelativeTo));
mCallback->AddRect(r + outer->GetOffsetTo(mRelativeTo));
} else
#endif
aCallback->AddRect(nsRect(aFrame->GetOffsetTo(aRelativeTo), aFrame->GetSize()));
mCallback->AddRect(nsRect(aFrame->GetOffsetTo(mRelativeTo), aFrame->GetSize()));
}
}
};
void
nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
RectCallback* aCallback)
{
while (aFrame) {
AddRectsForFrame(aFrame, aRelativeTo, aCallback);
aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame);
}
BoxToBorderRect converter(aRelativeTo, aCallback);
GetAllInFlowBoxes(aFrame, &converter);
}
struct RectAccumulator : public nsLayoutUtils::RectCallback {
@ -1475,6 +1506,23 @@ nsLayoutUtils::GetNextContinuationOrSpecialSibling(nsIFrame *aFrame)
return nsnull;
}
nsIFrame*
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(nsIFrame *aFrame)
{
nsIFrame *result = aFrame->GetFirstContinuation();
if (result->GetStateBits() & NS_FRAME_IS_SPECIAL) {
while (PR_TRUE) {
nsIFrame *f = static_cast<nsIFrame*>
(result->GetProperty(nsGkAtoms::IBSplitSpecialPrevSibling));
if (!f)
break;
result = f;
}
}
return result;
}
PRBool
nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame)
{

View File

@ -486,6 +486,19 @@ public:
PRInt32& aIndex,
PRInt32& aTextWidth);
class BoxCallback {
public:
virtual void AddBox(nsIFrame* aFrame) = 0;
};
/**
* Collect all CSS boxes associated with aFrame and its
* continuations, "drilling down" through outer table frames and
* some anonymous blocks since they're not real CSS boxes.
* If aFrame is null, no boxes are returned.
* SVG frames return a single box, themselves.
*/
static void GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback);
class RectCallback {
public:
virtual void AddRect(const nsRect& aRect) = 0;
@ -579,6 +592,13 @@ public:
static nsIFrame*
GetNextContinuationOrSpecialSibling(nsIFrame *aFrame);
/**
* Get the first frame in the continuation-plus-special-sibling chain
* containing aFrame.
*/
static nsIFrame*
GetFirstContinuationOrSpecialSibling(nsIFrame *aFrame);
/**
* Check whether aFrame is a part of the scrollbar or scrollcorner of
* the root content.

View File

@ -120,6 +120,10 @@
#include "nsBlockFrame.h"
#include "nsDisplayList.h"
#ifdef MOZ_SVG
#include "nsSVGIntegrationUtils.h"
#endif
#include "gfxContext.h"
static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
@ -1182,6 +1186,14 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
absPosClip - aBuilder->ToReferenceFrame(this));
}
#ifdef MOZ_SVG
PRBool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
if (usingSVGEffects) {
dirtyRect =
nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
}
#endif
nsDisplayListCollection set;
nsresult rv;
{
@ -1262,10 +1274,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
resultList.AppendToTop(item);
}
if (disp->mOpacity == 1.0f) {
aList->AppendToTop(&resultList);
} else {
#ifdef MOZ_SVG
if (usingSVGEffects) {
rv = aList->AppendNewToTop(new (aBuilder) nsDisplaySVGEffects(this, &resultList));
} else
#endif
if (disp->mOpacity < 1.0f) {
rv = aList->AppendNewToTop(new (aBuilder) nsDisplayOpacity(this, &resultList));
} else {
aList->AppendToTop(&resultList);
}
return rv;
@ -1406,7 +1423,11 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
!PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
return NS_OK;
PRBool isComposited = disp->mOpacity != 1.0f;
PRBool isComposited = disp->mOpacity != 1.0f
#ifdef MOZ_SVG
|| nsSVGIntegrationUtils::UsingEffectsForFrame(aChild)
#endif
;
PRBool isPositioned = disp->IsPositioned();
if (isComposited || isPositioned || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
// If you change this, also change IsPseudoStackingContextFromStyle()
@ -3591,6 +3612,15 @@ void
nsIFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY,
nsIFrame* aForChild, PRBool aImmediate)
{
#ifdef MOZ_SVG
if (nsSVGIntegrationUtils::UsingEffectsForFrame(this)) {
nsRect r = nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(this,
aDamageRect + nsPoint(aX, aY));
GetParent()->InvalidateInternal(r, mRect.x, mRect.y, this, aImmediate);
return;
}
#endif
GetParent()->
InvalidateInternal(aDamageRect, aX + mRect.x, aY + mRect.y, this, aImmediate);
}
@ -3620,12 +3650,53 @@ nsIFrame::InvalidateRoot(const nsRect& aDamageRect,
view->GetViewManager()->UpdateView(view, aDamageRect + nsPoint(aX, aY), flags);
}
static nsRect ComputeOutlineRect(const nsIFrame* aFrame, PRBool* aAnyOutline,
const nsRect& aOverflowRect) {
static void
DestroyRectFunc(void* aFrame,
nsIAtom* aPropertyName,
void* aPropertyValue,
void* aDtorData)
{
delete static_cast<nsRect*>(aPropertyValue);
}
static void
SetRectProperty(nsIFrame* aFrame, nsIAtom* aProp, const nsRect& aRect)
{
nsRect* r = new nsRect(aRect);
if (!r)
return;
aFrame->SetProperty(aProp, r, DestroyRectFunc);
}
static nsRect
ComputeOutlineAndEffectsRect(nsIFrame* aFrame, PRBool* aAnyOutlineOrEffects,
const nsRect& aOverflowRect,
PRBool aStoreRectProperties) {
nsRect r = aOverflowRect;
*aAnyOutlineOrEffects = PR_FALSE;
// box-shadow
nsCSSShadowArray* boxShadows = aFrame->GetStyleBorder()->mBoxShadow;
if (boxShadows) {
nsRect shadows;
for (PRUint32 i = 0; i < boxShadows->Length(); ++i) {
nsRect tmpRect = r;
nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
nscoord xOffset = shadow->mXOffset.GetCoordValue();
nscoord yOffset = shadow->mYOffset.GetCoordValue();
nscoord outsetRadius = shadow->mRadius.GetCoordValue() +
shadow->mSpread.GetCoordValue();
tmpRect.MoveBy(nsPoint(xOffset, yOffset));
tmpRect.Inflate(outsetRadius, outsetRadius);
shadows.UnionRect(shadows, tmpRect);
}
r.UnionRect(r, shadows);
}
const nsStyleOutline* outline = aFrame->GetStyleOutline();
PRUint8 outlineStyle = outline->GetOutlineStyle();
nsRect r = aOverflowRect;
*aAnyOutline = PR_FALSE;
if (outlineStyle != NS_STYLE_BORDER_STYLE_NONE) {
nscoord width;
#ifdef DEBUG
@ -3634,13 +3705,35 @@ static nsRect ComputeOutlineRect(const nsIFrame* aFrame, PRBool* aAnyOutline,
outline->GetOutlineWidth(width);
NS_ASSERTION(result, "GetOutlineWidth had no cached outline width");
if (width > 0) {
if (aStoreRectProperties) {
SetRectProperty(aFrame, nsGkAtoms::outlineInnerRectProperty, r);
}
nscoord offset;
outline->GetOutlineOffset(offset);
nscoord inflateBy = PR_MAX(width + offset, 0);
r.Inflate(inflateBy, inflateBy);
*aAnyOutline = PR_TRUE;
*aAnyOutlineOrEffects = PR_TRUE;
}
}
// Note that we don't remove the outlineInnerRect if a frame loses outline
// style. That would require an extra property lookup for every frame,
// or a new frame state bit to track whether a property had been stored,
// or something like that. It's not worth doing that here. At most it's
// only one heap-allocated rect per frame and it will be cleaned up when
// the frame dies.
#ifdef MOZ_SVG
if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
*aAnyOutlineOrEffects = PR_TRUE;
if (aStoreRectProperties) {
SetRectProperty(aFrame, nsGkAtoms::preEffectsBBoxProperty, r);
}
r = nsSVGIntegrationUtils::ComputeFrameEffectsRect(aFrame, r);
}
#endif
return r;
}
@ -3699,10 +3792,10 @@ nsIFrame::CheckInvalidateSizeChange(const nsRect& aOldRect,
// invalidated)
// Invalidate the entire old frame+outline if the frame has an outline
PRBool anyOutline;
nsRect r = ComputeOutlineRect(this, &anyOutline,
aNewDesiredSize.mOverflowArea);
if (anyOutline) {
PRBool anyOutlineOrEffects;
nsRect r = ComputeOutlineAndEffectsRect(this, &anyOutlineOrEffects,
aOldOverflowRect, PR_FALSE);
if (anyOutlineOrEffects) {
r.UnionRect(aOldOverflowRect, r);
Invalidate(r);
return;
@ -5261,16 +5354,6 @@ nsFrame::GetAccessible(nsIAccessible** aAccessible)
}
#endif
// Destructor function for the overflow area property
static void
DestroyRectFunc(void* aFrame,
nsIAtom* aPropertyName,
void* aPropertyValue,
void* aDtorData)
{
delete static_cast<nsRect*>(aPropertyValue);
}
/** Create or retrieve the previously stored overflow area, if the frame does
* not overflow and no creation is required return nsnull.
* @param aCreateIfNecessary create a new nsRect for the overflow area
@ -5314,29 +5397,11 @@ nsRect
nsIFrame::GetAdditionalOverflow(const nsRect& aOverflowArea,
const nsSize& aNewSize)
{
nsRect overflowRect;
// outline
PRBool hasOutline;
overflowRect = ComputeOutlineRect(this, &hasOutline, aOverflowArea);
// box-shadow
nsCSSShadowArray* boxShadows = GetStyleBorder()->mBoxShadow;
if (boxShadows) {
for (PRUint32 i = 0; i < boxShadows->Length(); ++i) {
nsRect tmpRect(nsPoint(0, 0), aNewSize);
nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
nscoord xOffset = shadow->mXOffset.GetCoordValue();
nscoord yOffset = shadow->mYOffset.GetCoordValue();
nscoord outsetRadius = shadow->mRadius.GetCoordValue() +
shadow->mSpread.GetCoordValue();
tmpRect.MoveBy(nsPoint(xOffset, yOffset));
tmpRect.Inflate(outsetRadius, outsetRadius);
overflowRect.UnionRect(overflowRect, tmpRect);
}
}
PRBool hasOutlineOrEffects;
nsRect overflowRect =
ComputeOutlineAndEffectsRect(this, &hasOutlineOrEffects,
aOverflowArea, PR_TRUE);
// Absolute position clipping
PRBool hasAbsPosClip;
@ -5437,11 +5502,10 @@ nsFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
* as aSpecialSibling. This is needed because the split inline's
* style context is the parent of the anonymous block's srtyle context.
*
* If aFrame is not the anonymous block, aSpecialSibling is not
* touched.
* If aFrame is not the anonymous block, aSpecialSibling is set to null.
*/
static nsresult
GetIBSpecialSibling(nsPresContext* aPresContext,
GetIBSpecialSiblingForAnonymousBlock(nsPresContext* aPresContext,
nsIFrame* aFrame,
nsIFrame** aSpecialSibling)
{
@ -5449,6 +5513,14 @@ GetIBSpecialSibling(nsPresContext* aPresContext,
NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL,
"GetIBSpecialSibling should not be called on a non-special frame");
nsIAtom* type = aFrame->GetStyleContext()->GetPseudoType();
if (type != nsCSSAnonBoxes::mozAnonymousBlock &&
type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) {
// it's not the anonymous block
*aSpecialSibling = nsnull;
return NS_OK;
}
// Find the first-in-flow of the frame. (Ugh. This ends up
// being O(N^2) when it is called O(N) times.)
aFrame = aFrame->GetFirstInFlow();
@ -5532,10 +5604,11 @@ nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
if (parent->GetStateBits() & NS_FRAME_IS_SPECIAL) {
nsIFrame* sibling;
nsresult rv =
GetIBSpecialSibling(parent->PresContext(), parent, &sibling);
GetIBSpecialSiblingForAnonymousBlock(parent->PresContext(), parent, &sibling);
if (NS_FAILED(rv)) {
// If GetIBSpecialSibling fails, then what? we used to return what is
// now |aProspectiveParent|, but maybe |parent| would make more sense?
// If GetIBSpecialSiblingForAnonymousBlock fails, then what?
// we used to return what is now |aProspectiveParent|, but maybe
// |parent| would make more sense?
NS_NOTREACHED("Shouldn't get here");
return aProspectiveParent;
}
@ -5594,10 +5667,11 @@ nsFrame::DoGetParentStyleContextFrame(nsPresContext* aPresContext,
* If this frame is the anonymous block created when an inline
* with a block inside it got split, then the parent style context
* is on the first of the three special frames. We can get to it
* using GetIBSpecialSibling
* using GetIBSpecialSiblingForAnonymousBlock
*/
if (mState & NS_FRAME_IS_SPECIAL) {
nsresult rv = GetIBSpecialSibling(aPresContext, this, aProviderFrame);
nsresult rv =
GetIBSpecialSiblingForAnonymousBlock(aPresContext, this, aProviderFrame);
if (NS_FAILED(rv)) {
NS_NOTREACHED("Shouldn't get here");
*aProviderFrame = nsnull;

View File

@ -84,6 +84,9 @@ include pagination/reftest.list
# svg/
include svg/reftest.list
# svg-integration/
include svg-integration/reftest.list
# table-background/
include table-background/reftest.list

View File

@ -0,0 +1,8 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="250" y="0" width="250" height="500" fill="lime"/>
</svg>

After

Width:  |  Height:  |  Size: 288 B

View File

@ -0,0 +1,17 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<body style="margin:0">
<div style="position:absolute; top:0; left:0; clip-path: url(#c1); width:500px; height:500px; background:lime;"></div>
<svg:svg height="0">
<svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
<svg:rect x="0.5" y="0" width="0.5" height="1"/>
</svg:clipPath>
</svg:svg>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="500" height="200" fill="lime"/>
<rect x="0" y="200" width="500" height="120" fill="blue"/>
</svg>

After

Width:  |  Height:  |  Size: 348 B

View File

@ -0,0 +1,20 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<body style="margin:0">
<div style="clip-path: url(#c1); width:500px; height:200px; background:lime;">
<div style="height:200px;"/>
<div style="height:200px; background:blue;"/>
</div>
<svg:svg height="0">
<svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
<svg:rect x="0" y="0" width="1" height="0.8"/>
</svg:clipPath>
</svg:svg>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="100" height="150" fill="lime"/>
<rect x="100" y="0" width="100" height="200" fill="lime"/>
<rect x="100" y="200" width="100" height="100" fill="blue"/>
</svg>

After

Width:  |  Height:  |  Size: 411 B

View File

@ -0,0 +1,21 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<body style="margin:0">
<div style="clip-path: url(#c1); width:500px; height:200px; background:lime;">
<div style="height:200px;"/>
<div style="height:200px; background:blue;"/>
</div>
<svg:svg height="0">
<svg:clipPath id="c1" clipPathUnits="userSpaceOnuse">
<svg:rect x="0" y="0" width="100" height="150"/>
<svg:rect x="100" y="0" width="100" height="300"/>
</svg:clipPath>
</svg:svg>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<body style="margin:0; line-height:100px;">
<span style="display:inline-block; width:200px;"
/><span style="background:lime;"><span style="display:inline-block; width:50px;"/></span><br/>
<span style="display:inline-block; width:50px;"
/><span style="background:lime;"><span style="display:inline-block; width:50px;"/></span>
</body>
</html>

View File

@ -0,0 +1,24 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<style>
.unit { display:inline-block; width:100px; height:1px; }
</style>
</head>
<body style="margin:0; width:350px; line-height:100px;">
<span class="unit"/><span class="unit"
/><span style="clip-path:url(#c1); background:lime;"><span class="unit"/><span class="unit"
/></span>
<svg:svg height="0">
<svg:clipPath id="c1" clipPathUnits="userSpaceOnuse">
<svg:rect x="50" y="0" width="200" height="200"/>
</svg:clipPath>
</svg:svg>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<body style="margin:0; line-height:100px;">
<span style="display:inline-block; width:200px;"
/><span style="background:lime;"><span style="display:inline-block; width:70px;"/></span><br/>
<span style="display:inline-block; width:30px;"
/><span style="background:lime;"><span style="display:inline-block; width:70px;"/></span>
</body>
</html>

View File

@ -0,0 +1,24 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<style>
.unit { display:inline-block; width:100px; height:1px; }
</style>
</head>
<body style="margin:0; width:350px; line-height:100px;">
<span class="unit"/><span class="unit"
/><span style="clip-path:url(#c1); background:lime;"><span class="unit"/><span class="unit"
/></span>
<svg:svg height="0">
<svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
<svg:rect x="0.1" y="0" width="0.8" height="1"/>
</svg:clipPath>
</svg:svg>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<style>
.unit { display:inline-block; width:50px; height:10px; }
</style>
</head>
<body style="margin:0">
<span>
<span class="unit"></span>
<div style="height:200px;"/>
<span class="unit" style="background:lime;"></span>
</span>
</body>
</html>

View File

@ -0,0 +1,26 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<style>
.unit { display:inline-block; width:100px; height:10px; }
</style>
</head>
<body style="margin:0">
<span style="clip-path: url(#c1);">
<span class="unit" style="background:lime;"></span>
<div style="height:200px; width:100px;"/>
<span class="unit" style="background:lime;"></span>
</span>
<svg:svg height="0">
<svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
<svg:rect x="0" y="0.5" width="0.5" height="0.5"/>
</svg:clipPath>
</svg:svg>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<filter id="f1">
<feFlood flood-color="black" result="black"/>
<feGaussianBlur in="SourceAlpha" stdDeviation="10"/>
<feComposite in="black" operator="in"/>
<feComposite in="SourceGraphic"/>
</filter>
<rect x="50" y="50" width="200" height="200" fill="lime" filter="url(#f1)"/>
</svg>

After

Width:  |  Height:  |  Size: 527 B

View File

@ -0,0 +1,20 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<body style="margin:0">
<div style="background:lime; width:200px; height:200px; margin:50px; filter:url(#f1)"/>
<svg:svg height="0">
<svg:filter id="f1">
<svg:feFlood flood-color="black" result="black"/>
<svg:feGaussianBlur in="SourceAlpha" stdDeviation="10"/>
<svg:feComposite in="black" operator="in"/>
<svg:feComposite in="SourceGraphic"/>
</svg:filter>
</svg:svg>
</body>
</html>

View File

@ -0,0 +1,13 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<linearGradient id="g" gradientUnits="objectBoundingBox" x2="0" y2="1">
<stop stop-color="lime" offset="0"/>
<stop stop-color="lime" stop-opacity="0" offset="1"/>
</linearGradient>
<circle cx="125" cy="125" r="125" id="circle" fill="lime"/>
<rect x="250" y="0" width="250" height="500" fill="url(#g)"/>
</svg>

After

Width:  |  Height:  |  Size: 547 B

View File

@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<body style="margin:0">
<div style="mask: url(#m1); width:500px; height:500px; background:lime;"></div>
<svg:svg height="0">
<svg:mask id="m1" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
<svg:linearGradient id="g" gradientUnits="objectBoundingBox" x2="0" y2="1">
<svg:stop stop-color="white" offset="0"/>
<svg:stop stop-color="white" stop-opacity="0" offset="1"/>
</svg:linearGradient>
<svg:circle cx="0.25" cy="0.25" r="0.25" id="circle" fill="white"/>
<svg:rect x="0.5" y="0" width="0.5" height="1" fill="url(#g)"/>
</svg:mask>
</svg:svg>
</body>
</html>

View File

@ -0,0 +1,9 @@
== clipPath-html-01.xhtml clipPath-html-01-ref.svg
== clipPath-html-02.xhtml clipPath-html-02-ref.svg
== clipPath-html-03.xhtml clipPath-html-03-ref.svg
== clipPath-html-04.xhtml clipPath-html-04-ref.xhtml
== clipPath-html-05.xhtml clipPath-html-05-ref.xhtml
== clipPath-html-06.xhtml clipPath-html-06-ref.xhtml
== filter-html-01.xhtml filter-html-01-ref.svg
== mask-html-01.xhtml mask-html-01-ref.svg

View File

@ -128,6 +128,9 @@
/* we currently inherit from the inline that is split */
outline: inherit;
-moz-outline-offset: inherit;
clip-path: inherit;
filter: inherit;
mask: inherit;
}
*|*::-moz-xul-anonymous-block {

View File

@ -80,6 +80,7 @@ CPPSRCS = \
nsSVGGradientFrame.cpp \
nsSVGImageFrame.cpp \
nsSVGInnerSVGFrame.cpp \
nsSVGIntegrationUtils.cpp \
nsSVGLeafFrame.cpp \
nsSVGMarkerFrame.cpp \
nsSVGMaskFrame.cpp \
@ -103,6 +104,7 @@ include $(topsrcdir)/config/config.mk
FORCE_STATIC_LIB = 1
EXPORTS = \
nsSVGIntegrationUtils.h \
nsSVGUtils.h \
nsSVGFilterInstance.h \
nsSVGForeignObjectFrame.h \

View File

@ -51,8 +51,8 @@ class nsIDOMSVGMatrix;
class nsSVGRenderState;
#define NS_ISVGCHILDFRAME_IID \
{ 0xe4ecddbf, 0xde7c, 0x4cd9, \
{ 0x92, 0x4a, 0xfa, 0x81, 0xba, 0x83, 0x26, 0x69 } }
{ 0x8b80b2a0, 0x2e1f, 0x4775, \
{ 0xab, 0x47, 0xbe, 0xeb, 0x4b, 0x81, 0x63, 0x6d } }
class nsISVGChildFrame : public nsISupports {
public:
@ -98,6 +98,7 @@ public:
// Set whether we should stop multiplying matrices when building up
// the current transformation matrix at this frame.
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate)=0;
virtual PRBool GetMatrixPropagation()=0;
// Set the current transformation matrix to a particular matrix.
// Value is only used if matrix propagation is prevented

View File

@ -61,7 +61,7 @@ NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleCo
nsresult
nsSVGClipPathFrame::ClipPaint(nsSVGRenderState* aContext,
nsISVGChildFrame* aParent,
nsIFrame* aParent,
nsIDOMSVGMatrix *aMatrix)
{
// If the flag is set when we get here, it means this clipPath frame
@ -103,7 +103,7 @@ nsSVGClipPathFrame::ClipPaint(nsSVGRenderState* aContext,
}
PRBool
nsSVGClipPathFrame::ClipHitTest(nsISVGChildFrame* aParent,
nsSVGClipPathFrame::ClipHitTest(nsIFrame* aParent,
nsIDOMSVGMatrix *aMatrix,
const nsPoint &aPoint)
{

View File

@ -54,10 +54,10 @@ protected:
public:
// nsSVGClipPathFrame methods:
nsresult ClipPaint(nsSVGRenderState* aContext,
nsISVGChildFrame* aParent,
nsIFrame* aParent,
nsIDOMSVGMatrix *aMatrix);
PRBool ClipHitTest(nsISVGChildFrame* aParent,
PRBool ClipHitTest(nsIFrame* aParent,
nsIDOMSVGMatrix *aMatrix,
const nsPoint &aPoint);
@ -100,7 +100,7 @@ public:
nsSVGClipPathFrame *mFrame;
};
nsISVGChildFrame *mClipParent;
nsIFrame *mClipParent;
nsCOMPtr<nsIDOMSVGMatrix> mClipParentMatrix;
// nsSVGContainerFrame methods:

View File

@ -88,7 +88,7 @@ nsSVGContainerFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD);
AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_PROPAGATE_TRANSFORM);
nsresult rv = nsSVGContainerFrameBase::Init(aContent, aParent, aPrevInFlow);
return rv;
}
@ -98,6 +98,7 @@ nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
}
@ -277,3 +278,20 @@ nsSVGDisplayContainerFrame::GetBBox(nsIDOMSVGRect **_retval)
{
return nsSVGUtils::GetBBox(&mFrames, _retval);
}
NS_IMETHODIMP
nsSVGDisplayContainerFrame::SetMatrixPropagation(PRBool aPropagate)
{
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGDisplayContainerFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}

View File

@ -109,7 +109,8 @@ public:
virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended();
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate) { return NS_ERROR_FAILURE; }
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
virtual PRBool GetMatrixPropagation();
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { return NS_ERROR_FAILURE; }
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM() { return nsnull; }
NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);

View File

@ -127,7 +127,7 @@ nsSVGFilterProperty::UpdateRect()
{
nsSVGFilterFrame *filter = GetFilterFrame(nsnull);
if (filter) {
mFilterRect = filter->GetInvalidationRegion(mFrame, mFrame->GetRect());
mFilterRect = filter->GetFilterBBox(mFrame, nsnull);
} else {
mFilterRect = nsRect();
}

View File

@ -42,11 +42,13 @@
#include "nsGkAtoms.h"
#include "nsSVGUtils.h"
#include "nsSVGFilterElement.h"
#include "nsSVGFilterInstance.h"
#include "nsSVGFilters.h"
#include "gfxASurface.h"
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "nsSVGFilterPaintCallback.h"
#include "nsSVGRect.h"
#include "nsSVGFilterInstance.h"
nsIFrame*
NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext)
@ -78,63 +80,68 @@ MapDeviceRectToFilterSpace(const gfxMatrix& aMatrix,
return rect;
}
nsresult
nsSVGFilterFrame::CreateInstance(nsISVGChildFrame *aTarget,
class NS_STACK_CLASS nsAutoFilterInstance {
public:
nsAutoFilterInstance(nsIFrame *aTarget,
nsSVGFilterFrame *aFilterFrame,
nsSVGFilterPaintCallback *aPaint,
const nsIntRect *aDirtyOutputRect,
const nsIntRect *aDirtyInputRect,
nsSVGFilterInstance **aInstance)
const nsIntRect *aOverrideSourceBBox);
~nsAutoFilterInstance();
// If this returns null, then draw nothing. Either the filter draws
// nothing or it is "in error".
nsSVGFilterInstance* get() { return mInstance; }
private:
nsAutoPtr<nsSVGFilterInstance> mInstance;
// Store mTarget separately even though mInstance has it, because if
// mInstance creation fails we still need to be able to clean up
nsISVGChildFrame* mTarget;
};
nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
nsSVGFilterFrame *aFilterFrame,
nsSVGFilterPaintCallback *aPaint,
const nsIntRect *aDirtyOutputRect,
const nsIntRect *aDirtyInputRect,
const nsIntRect *aOverrideSourceBBox)
{
*aInstance = nsnull;
nsCOMPtr<nsIDOMSVGMatrix> ctm = nsSVGUtils::GetCanvasTM(aTarget);
nsIFrame *frame;
CallQueryInterface(aTarget, &frame);
nsCOMPtr<nsIDOMSVGMatrix> ctm = nsSVGUtils::GetCanvasTM(frame);
nsSVGElement *target = static_cast<nsSVGElement*>(frame->GetContent());
aTarget->SetMatrixPropagation(PR_FALSE);
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
CallQueryInterface(aTarget, &mTarget);
if (mTarget) {
mTarget->SetMatrixPropagation(PR_FALSE);
mTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
}
nsSVGFilterElement *filter = static_cast<nsSVGFilterElement*>(mContent);
float x, y, width, height;
nsCOMPtr<nsIDOMSVGRect> bbox;
aTarget->GetBBox(getter_AddRefs(bbox));
nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight;
tmpX = &filter->mLengthAttributes[nsSVGFilterElement::X];
tmpY = &filter->mLengthAttributes[nsSVGFilterElement::Y];
tmpWidth = &filter->mLengthAttributes[nsSVGFilterElement::WIDTH];
tmpHeight = &filter->mLengthAttributes[nsSVGFilterElement::HEIGHT];
nsSVGFilterElement *filter = static_cast<nsSVGFilterElement*>(
aFilterFrame->GetContent());
PRUint16 units =
filter->mEnumAttributes[nsSVGFilterElement::FILTERUNITS].GetAnimValue();
// Compute filter effects region as per spec
if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
if (!bbox)
return NS_OK;
bbox->GetX(&x);
x += nsSVGUtils::ObjectSpace(bbox, tmpX);
bbox->GetY(&y);
y += nsSVGUtils::ObjectSpace(bbox, tmpY);
width = nsSVGUtils::ObjectSpace(bbox, tmpWidth);
height = nsSVGUtils::ObjectSpace(bbox, tmpHeight);
nsCOMPtr<nsIDOMSVGRect> bbox;
if (aOverrideSourceBBox) {
NS_NewSVGRect(getter_AddRefs(bbox),
aOverrideSourceBBox->x, aOverrideSourceBBox->y,
aOverrideSourceBBox->width, aOverrideSourceBBox->height);
} else {
x = nsSVGUtils::UserSpace(target, tmpX);
y = nsSVGUtils::UserSpace(target, tmpY);
width = nsSVGUtils::UserSpace(target, tmpWidth);
height = nsSVGUtils::UserSpace(target, tmpHeight);
bbox = nsSVGUtils::GetBBox(aTarget);
}
if (!bbox && units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
return;
gfxRect filterArea = nsSVGUtils::GetRelativeRect(units,
&filter->mLengthAttributes[nsSVGFilterElement::X], bbox, aTarget);
filterArea.RoundOut();
PRBool resultOverflows;
gfxIntSize filterRes;
// Compute size of filter buffer
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) {
if (filter->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) {
PRInt32 filterResX, filterResY;
filter->GetAnimatedIntegerValues(&filterResX, &filterResY, nsnull);
@ -148,26 +155,21 @@ nsSVGFilterFrame::CreateInstance(nsISVGChildFrame *aTarget,
#endif
filterRes =
nsSVGUtils::ConvertToSurfaceSize(gfxSize(width, height) * scale,
nsSVGUtils::ConvertToSurfaceSize(filterArea.size * scale,
&resultOverflows);
}
// 0 disables rendering, < 0 is error
if (filterRes.width <= 0 || filterRes.height <= 0)
return NS_OK;
#ifdef DEBUG_tor
fprintf(stderr, "filter bbox: %f,%f %fx%f\n", x, y, width, height);
fprintf(stderr, "filterRes: %u %u\n", filterRes.width, filterRes.height);
#endif
return;
// 'fini' is the matrix we will finally use to transform filter space
// to surface space for drawing
nsCOMPtr<nsIDOMSVGMatrix> scale, fini;
NS_NewSVGMatrix(getter_AddRefs(scale),
width / filterRes.width, 0.0f,
0.0f, height / filterRes.height,
x, y);
filterArea.Width() / filterRes.width, 0.0f,
0.0f, filterArea.Height() / filterRes.height,
filterArea.X(), filterArea.Y());
ctm->Multiply(scale, getter_AddRefs(fini));
gfxMatrix finiM = nsSVGUtils::ConvertSVGMatrixToThebes(fini);
@ -182,99 +184,120 @@ nsSVGFilterFrame::CreateInstance(nsISVGChildFrame *aTarget,
// Setup instance data
PRUint16 primitiveUnits =
filter->mEnumAttributes[nsSVGFilterElement::PRIMITIVEUNITS].GetAnimValue();
*aInstance = new nsSVGFilterInstance(aTarget, mContent, bbox,
gfxRect(x, y, width, height),
mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterArea,
nsIntSize(filterRes.width, filterRes.height),
fini,
dirtyOutputRect, dirtyInputRect,
primitiveUnits);
return *aInstance ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
static void
RestoreTargetState(nsISVGChildFrame *aTarget)
nsAutoFilterInstance::~nsAutoFilterInstance()
{
aTarget->SetOverrideCTM(nsnull);
aTarget->SetMatrixPropagation(PR_TRUE);
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
if (!mTarget)
return;
mTarget->SetOverrideCTM(nsnull);
mTarget->SetMatrixPropagation(PR_TRUE);
mTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
}
nsresult
nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
nsISVGChildFrame *aTarget,
nsIFrame *aTarget,
nsSVGFilterPaintCallback *aPaintCallback,
const nsIntRect *aDirtyRect)
{
nsAutoPtr<nsSVGFilterInstance> instance;
nsresult rv = CreateInstance(aTarget, aDirtyRect, nsnull, getter_Transfers(instance));
if (NS_SUCCEEDED(rv) && instance) {
// Transformation from user space to filter space
nsCOMPtr<nsIDOMSVGMatrix> filterTransform =
instance->GetUserSpaceToFilterSpaceTransform();
aTarget->SetOverrideCTM(filterTransform);
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
nsAutoFilterInstance instance(aTarget, this, aPaintCallback,
aDirtyRect, nsnull, nsnull);
if (!instance.get())
return NS_OK;
nsRefPtr<gfxASurface> result;
rv = instance->Render(getter_AddRefs(result));
nsresult rv = instance.get()->Render(getter_AddRefs(result));
if (NS_SUCCEEDED(rv) && result) {
nsSVGUtils::CompositeSurfaceMatrix(aContext->GetGfxContext(),
result, instance->GetFilterSpaceToDeviceSpaceTransform(), 1.0);
}
}
RestoreTargetState(aTarget);
if (NS_FAILED(rv)) {
aTarget->PaintSVG(aContext, nsnull);
result, instance.get()->GetFilterSpaceToDeviceSpaceTransform(), 1.0);
}
return rv;
}
nsRect
nsSVGFilterFrame::GetInvalidationRegion(nsIFrame *aTarget, const nsRect& aRect)
static nsresult
TransformFilterSpaceToDeviceSpace(nsSVGFilterInstance *aInstance, nsIntRect *aRect)
{
nsISVGChildFrame *svg;
CallQueryInterface(aTarget, &svg);
nscoord p2a = aTarget->PresContext()->AppUnitsPerDevPixel();
nsRect result = aRect;
nsIntRect rect = aRect;
rect.ScaleRoundOut(1.0/p2a);
nsAutoPtr<nsSVGFilterInstance> instance;
nsresult rv = CreateInstance(svg, nsnull, &rect, getter_Transfers(instance));
if (NS_SUCCEEDED(rv)) {
if (!instance) {
// The filter draws nothing, so nothing is dirty.
result = nsRect();
} else {
// We've passed in the source's dirty area so the instance knows about it.
// Now we can ask the instance to compute the area of the filter output
// that's dirty.
nsIntRect filterSpaceDirtyRect;
rv = instance->ComputeOutputDirtyRect(&filterSpaceDirtyRect);
if (NS_SUCCEEDED(rv)) {
gfxMatrix m = nsSVGUtils::ConvertSVGMatrixToThebes(
instance->GetFilterSpaceToDeviceSpaceTransform());
gfxRect r(filterSpaceDirtyRect.x, filterSpaceDirtyRect.y,
filterSpaceDirtyRect.width, filterSpaceDirtyRect.height);
aInstance->GetFilterSpaceToDeviceSpaceTransform());
gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
r = m.TransformBounds(r);
r.RoundOut();
nsIntRect deviceRect;
rv = nsSVGUtils::GfxRectToIntRect(r, &deviceRect);
nsresult rv = nsSVGUtils::GfxRectToIntRect(r, &deviceRect);
if (NS_FAILED(rv))
return rv;
*aRect = deviceRect;
return NS_OK;
}
nsIntRect
nsSVGFilterFrame::GetInvalidationBBox(nsIFrame *aTarget, const nsIntRect& aRect)
{
nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, &aRect, nsnull);
if (!instance.get())
return nsIntRect();
// We've passed in the source's dirty area so the instance knows about it.
// Now we can ask the instance to compute the area of the filter output
// that's dirty.
nsIntRect dirtyRect;
nsresult rv = instance.get()->ComputeOutputDirtyRect(&dirtyRect);
if (NS_SUCCEEDED(rv)) {
deviceRect.ScaleRoundOut(p2a);
result = deviceRect;
}
}
}
rv = TransformFilterSpaceToDeviceSpace(instance.get(), &dirtyRect);
if (NS_SUCCEEDED(rv))
return dirtyRect;
}
RestoreTargetState(svg);
return nsIntRect();
}
return result;
nsIntRect
nsSVGFilterFrame::GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aRect)
{
nsAutoFilterInstance instance(aTarget, this, nsnull, &aRect, nsnull, nsnull);
if (!instance.get())
return nsIntRect();
// Now we can ask the instance to compute the area of the source
// that's needed.
nsIntRect neededRect;
nsresult rv = instance.get()->ComputeSourceNeededRect(&neededRect);
if (NS_SUCCEEDED(rv)) {
rv = TransformFilterSpaceToDeviceSpace(instance.get(), &neededRect);
if (NS_SUCCEEDED(rv))
return neededRect;
}
return nsIntRect();
}
nsIntRect
nsSVGFilterFrame::GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox)
{
nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, nsnull, aSourceBBox);
if (!instance.get())
return nsIntRect();
// We've passed in the source's bounding box so the instance knows about
// it. Now we can ask the instance to compute the bounding box of
// the filter output.
nsIntRect bbox;
nsresult rv = instance.get()->ComputeOutputBBox(&bbox);
if (NS_SUCCEEDED(rv)) {
rv = TransformFilterSpaceToDeviceSpace(instance.get(), &bbox);
if (NS_SUCCEEDED(rv))
return bbox;
}
return nsIntRect();
}
nsIAtom *

View File

@ -40,7 +40,8 @@
#include "nsRect.h"
#include "nsSVGContainerFrame.h"
class nsSVGFilterInstance;
class nsSVGRenderState;
class nsSVGFilterPaintCallback;
typedef nsSVGContainerFrame nsSVGFilterFrameBase;
class nsSVGFilterFrame : public nsSVGFilterFrameBase
@ -52,15 +53,31 @@ protected:
public:
nsresult FilterPaint(nsSVGRenderState *aContext,
nsISVGChildFrame *aTarget,
nsIFrame *aTarget, nsSVGFilterPaintCallback *aPaintCallback,
const nsIntRect* aDirtyRect);
// Returns invalidation region for filter (can be bigger than the
// referencing geometry to filter region sizing) in app units
// relative to the origin of the outer svg.
// aRect is the area that would be invalidated. Normally you'd just pass
// aTarget->GetRect() here.
nsRect GetInvalidationRegion(nsIFrame *aTarget, const nsRect& aRect);
/**
* Returns the area that could change when the given rect of the source changes.
* The rectangles are relative to the origin of the outer svg, if aTarget is SVG,
* relative to aTarget itself otherwise, in device pixels.
*/
nsIntRect GetInvalidationBBox(nsIFrame *aTarget, const nsIntRect& aRect);
/**
* Returns the area in device pixels that is needed from the source when
* the given area needs to be repainted.
* The rectangles are relative to the origin of the outer svg, if aTarget is SVG,
* relative to aTarget itself otherwise, in device pixels.
*/
nsIntRect GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aRect);
/**
* Returns the bounding box of the post-filter area of aTarget.
* The rectangles are relative to the origin of the outer svg, if aTarget is SVG,
* relative to aTarget itself otherwise, in device pixels.
* @param aSourceBBox overrides the normal bbox for the source, if non-null
*/
nsIntRect GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox);
/**
* Get the "type" of the frame
@ -68,13 +85,6 @@ public:
* @see nsGkAtoms::svgFilterFrame
*/
virtual nsIAtom* GetType() const;
private:
// implementation helpers
nsresult CreateInstance(nsISVGChildFrame *aTarget,
const nsIntRect *aDirtyOutputRect,
const nsIntRect *aDirtyInputRect,
nsSVGFilterInstance **aInstance);
};
#endif

View File

@ -38,6 +38,9 @@
#include "nsSVGUtils.h"
#include "nsIDOMSVGUnitTypes.h"
#include "nsSVGMatrix.h"
#include "gfxPlatform.h"
#include "nsSVGFilterPaintCallback.h"
#include "nsSVGFilterElement.h"
static double Square(double aX)
{
@ -48,10 +51,11 @@ float
nsSVGFilterInstance::GetPrimitiveLength(nsSVGLength2 *aLength) const
{
float value;
if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
value = nsSVGUtils::ObjectSpace(mTargetBBox, aLength);
else
value = nsSVGUtils::UserSpace(TargetElement(), aLength);
} else {
value = nsSVGUtils::UserSpace(mTargetFrame, aLength);
}
switch (aLength->GetCtxType()) {
case nsSVGUtils::X:
@ -123,29 +127,9 @@ nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(PrimitiveInfo* aPrimitive)
gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
}
nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight;
tmpX = &fE->mLengthAttributes[nsSVGFE::X];
tmpY = &fE->mLengthAttributes[nsSVGFE::Y];
tmpWidth = &fE->mLengthAttributes[nsSVGFE::WIDTH];
tmpHeight = &fE->mLengthAttributes[nsSVGFE::HEIGHT];
float x, y, width, height;
if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
mTargetBBox->GetX(&x);
x += nsSVGUtils::ObjectSpace(mTargetBBox, tmpX);
mTargetBBox->GetY(&y);
y += nsSVGUtils::ObjectSpace(mTargetBBox, tmpY);
width = nsSVGUtils::ObjectSpace(mTargetBBox, tmpWidth);
height = nsSVGUtils::ObjectSpace(mTargetBBox, tmpHeight);
} else {
nsSVGElement* targetElement = TargetElement();
x = nsSVGUtils::UserSpace(targetElement, tmpX);
y = nsSVGUtils::UserSpace(targetElement, tmpY);
width = nsSVGUtils::UserSpace(targetElement, tmpWidth);
height = nsSVGUtils::UserSpace(targetElement, tmpHeight);
}
gfxRect region = UserSpaceToFilterSpace(gfxRect(x, y, width, height));
gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
&fE->mLengthAttributes[nsSVGFE::X], mTargetBBox, mTargetFrame);
gfxRect region = UserSpaceToFilterSpace(feArea);
if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::x))
region.pos.x = defaultFilterSubregion.X();
@ -340,18 +324,51 @@ nsSVGFilterInstance::ComputeUnionOfAllNeededBoxes()
nsresult
nsSVGFilterInstance::BuildSourceImages()
{
if (mSourceColorAlpha.mResultNeededBox.IsEmpty() &&
mSourceAlpha.mResultNeededBox.IsEmpty())
nsIntRect neededRect;
neededRect.UnionRect(mSourceColorAlpha.mResultNeededBox,
mSourceAlpha.mResultNeededBox);
if (neededRect.IsEmpty())
return NS_OK;
nsRefPtr<gfxImageSurface> sourceColorAlpha = CreateImage();
if (!sourceColorAlpha)
return NS_ERROR_OUT_OF_MEMORY;
nsSVGRenderState tmpState(sourceColorAlpha);
nsresult rv = mTargetFrame->PaintSVG(&tmpState, nsnull);
{
// Paint to an offscreen surface first, then copy it to an image
// surface. This can be faster especially when the stuff we're painting
// contains native themes.
nsRefPtr<gfxASurface> offscreen =
gfxPlatform::GetPlatform()->CreateOffscreenSurface(
gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
gfxASurface::ImageFormatARGB32);
if (!offscreen || offscreen->CairoStatus())
return NS_ERROR_OUT_OF_MEMORY;
offscreen->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
nsSVGRenderState tmpState(offscreen);
nsCOMPtr<nsIDOMSVGMatrix> userSpaceToFilterSpaceTransform
= GetUserSpaceToFilterSpaceTransform();
if (!userSpaceToFilterSpaceTransform)
return NS_ERROR_OUT_OF_MEMORY;
gfxMatrix m =
nsSVGUtils::ConvertSVGMatrixToThebes(userSpaceToFilterSpaceTransform);
gfxRect r(neededRect.x, neededRect.y, neededRect.width, neededRect.height);
m.Invert();
r = m.TransformBounds(r);
r.RoundOut();
nsIntRect dirty;
nsresult rv = nsSVGUtils::GfxRectToIntRect(r, &dirty);
if (NS_FAILED(rv))
return rv;
mPaintCallback->Paint(&tmpState, mTargetFrame, &dirty,
userSpaceToFilterSpaceTransform);
gfxContext copyContext(sourceColorAlpha);
copyContext.SetSource(offscreen);
copyContext.Paint();
}
if (!mSourceColorAlpha.mResultNeededBox.IsEmpty()) {
NS_ASSERTION(mSourceColorAlpha.mImageUsers > 0, "Some user must have needed this");
@ -528,3 +545,50 @@ nsSVGFilterInstance::ComputeOutputDirtyRect(nsIntRect* aDirty)
*aDirty = result->mResultChangeBox;
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
{
nsresult rv = BuildSources();
if (NS_FAILED(rv))
return rv;
rv = BuildPrimitives();
if (NS_FAILED(rv))
return rv;
if (mPrimitives.IsEmpty()) {
// Nothing should be rendered, so nothing is needed.
return NS_OK;
}
ComputeResultBoundingBoxes();
ComputeNeededBoxes();
aDirty->UnionRect(mSourceColorAlpha.mResultNeededBox,
mSourceAlpha.mResultNeededBox);
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputeOutputBBox(nsIntRect* aDirty)
{
nsresult rv = BuildSources();
if (NS_FAILED(rv))
return rv;
rv = BuildPrimitives();
if (NS_FAILED(rv))
return rv;
if (mPrimitives.IsEmpty()) {
// Nothing should be rendered.
*aDirty = nsIntRect();
return NS_OK;
}
ComputeResultBoundingBoxes();
PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
*aDirty = result->mResultBoundingBox;
return NS_OK;
}

View File

@ -51,6 +51,8 @@
class nsSVGLength2;
class nsSVGElement;
class nsSVGFilterElement;
class nsSVGFilterPaintCallback;
/**
* This class performs all filter processing.
@ -64,8 +66,9 @@ class NS_STACK_CLASS nsSVGFilterInstance
public:
float GetPrimitiveLength(nsSVGLength2 *aLength) const;
nsSVGFilterInstance(nsISVGChildFrame *aTargetFrame,
nsIContent* aFilterElement,
nsSVGFilterInstance(nsIFrame *aTargetFrame,
nsSVGFilterPaintCallback *aPaintCallback,
nsSVGFilterElement *aFilterElement,
nsIDOMSVGRect *aTargetBBox,
const gfxRect& aFilterRect,
const nsIntSize& aFilterSpaceSize,
@ -74,6 +77,7 @@ public:
const nsIntRect& aDirtyInputRect,
PRUint16 aPrimitiveUnits) :
mTargetFrame(aTargetFrame),
mPaintCallback(aPaintCallback),
mFilterElement(aFilterElement),
mTargetBBox(aTargetBBox),
mFilterSpaceToDeviceSpaceTransform(aFilterSpaceToDeviceSpaceTransform),
@ -100,6 +104,8 @@ public:
nsresult Render(gfxASurface** aOutput);
nsresult ComputeOutputDirtyRect(nsIntRect* aDirty);
nsresult ComputeSourceNeededRect(nsIntRect* aDirty);
nsresult ComputeOutputBBox(nsIntRect* aBBox);
already_AddRefed<nsIDOMSVGMatrix> GetUserSpaceToFilterSpaceTransform() const;
nsIDOMSVGMatrix* GetFilterSpaceToDeviceSpaceTransform() const {
@ -174,15 +180,10 @@ private:
aRect->IntersectRect(*aRect, filterSpace);
}
void ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfx) const;
nsSVGElement* TargetElement() const
{
nsIFrame* f;
CallQueryInterface(mTargetFrame, &f);
return static_cast<nsSVGElement*>(f->GetContent());
}
nsISVGChildFrame* mTargetFrame;
nsIContent* mFilterElement;
nsIFrame* mTargetFrame;
nsSVGFilterPaintCallback* mPaintCallback;
nsSVGFilterElement* mFilterElement;
nsCOMPtr<nsIDOMSVGRect> mTargetBBox;
nsCOMPtr<nsIDOMSVGMatrix> mFilterSpaceToDeviceSpaceTransform;
gfxRect mFilterRect;

View File

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Mozilla SVG project.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NS_SVGFILTERPAINTCALLBACK_H__
#define __NS_SVGFILTERPAINTCALLBACK_H__
#include "nsRect.h"
class nsIFrame;
class nsIDOMSVGMatrix;
class nsSVGRenderState;
class nsSVGFilterPaintCallback {
public:
/**
* Paint the frame contents. aTransform should be applied to aContext
* (either via SetOverrideCTM or by applying the transform to aContext
* directly).
* SVG frames will have had matrix propagation set to false already.
* frames have to do their own thing.
* The caller will do a Save()/Restore() as necessary so feel free
* to mess with context state.
* @param aDirtyRect the dirty rect *in user space pixels*
* @param aTransform the user-space-to-filter-space transform to apply.
* May be null if the identity matrix is requested.
*/
virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget,
const nsIntRect *aDirtyRect, nsIDOMSVGMatrix *aTransform) = 0;
};
#endif

View File

@ -97,6 +97,7 @@ nsSVGForeignObjectFrame::Init(nsIContent* aContent,
nsIFrame* aPrevInFlow)
{
nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow);
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
if (NS_SUCCEEDED(rv)) {
nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
}
@ -450,10 +451,20 @@ nsSVGForeignObjectFrame::NotifyRedrawUnsuspended()
NS_IMETHODIMP
nsSVGForeignObjectFrame::SetMatrixPropagation(PRBool aPropagate)
{
mPropagateTransform = aPropagate;
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGForeignObjectFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
{
@ -514,7 +525,7 @@ nsSVGForeignObjectFrame::GetTMIncludingOffset()
already_AddRefed<nsIDOMSVGMatrix>
nsSVGForeignObjectFrame::GetCanvasTM()
{
if (!mPropagateTransform) {
if (!GetMatrixPropagation()) {
nsIDOMSVGMatrix *retval;
if (mOverrideCTM) {
retval = mOverrideCTM;

View File

@ -114,6 +114,7 @@ public:
NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended();
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
virtual PRBool GetMatrixPropagation();
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);

View File

@ -80,13 +80,6 @@ nsSVGGFrame::NotifySVGChanged(PRUint32 aFlags)
nsSVGGFrameBase::NotifySVGChanged(aFlags);
}
NS_IMETHODIMP
nsSVGGFrame::SetMatrixPropagation(PRBool aPropagate)
{
mPropagateTransform = aPropagate;
return NS_OK;
}
NS_IMETHODIMP
nsSVGGFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
{
@ -105,7 +98,7 @@ nsSVGGFrame::GetOverrideCTM()
already_AddRefed<nsIDOMSVGMatrix>
nsSVGGFrame::GetCanvasTM()
{
if (!mPropagateTransform) {
if (!GetMatrixPropagation()) {
nsIDOMSVGMatrix *retval;
if (mOverrideCTM) {
retval = mOverrideCTM;

View File

@ -73,7 +73,6 @@ public:
// nsISVGChildFrame interface:
virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();

View File

@ -65,7 +65,8 @@ nsSVGGeometryFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
AddStateBits((aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) |
NS_STATE_SVG_PROPAGATE_TRANSFORM);
nsresult rv = nsSVGGeometryFrameBase::Init(aContent, aParent, aPrevInFlow);
return rv;
}

View File

@ -1284,6 +1284,23 @@ nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale,
return PR_TRUE;
}
NS_IMETHODIMP
nsSVGGlyphFrame::SetMatrixPropagation(PRBool aPropagate)
{
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGGlyphFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}
//----------------------------------------------------------------------
// helper class

View File

@ -125,7 +125,8 @@ public:
virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended();
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate) { return NS_OK; }
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
virtual PRBool GetMatrixPropagation();
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { return NS_ERROR_FAILURE; }
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM() { return nsnull; }
NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; }

View File

@ -85,7 +85,6 @@ public:
// nsISVGChildFrame interface:
NS_IMETHOD PaintSVG(nsSVGRenderState *aContext, nsIntRect *aDirtyRect);
virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
@ -228,13 +227,6 @@ nsSVGInnerSVGFrame::NotifySVGChanged(PRUint32 aFlags)
nsSVGInnerSVGFrameBase::NotifySVGChanged(aFlags);
}
NS_IMETHODIMP
nsSVGInnerSVGFrame::SetMatrixPropagation(PRBool aPropagate)
{
mPropagateTransform = aPropagate;
return NS_OK;
}
NS_IMETHODIMP
nsSVGInnerSVGFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
{

View File

@ -0,0 +1,414 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Mozilla SVG project.
*
* The Initial Developer of the Original Code is IBM Corporation.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* rocallahan@mozilla.com
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsSVGIntegrationUtils.h"
#include "nsSVGUtils.h"
#include "nsSVGEffects.h"
#include "nsRegion.h"
#include "nsLayoutUtils.h"
#include "nsDisplayList.h"
#include "nsSVGMatrix.h"
#include "nsSVGFilterPaintCallback.h"
// ----------------------------------------------------------------------
PRBool
nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
{
const nsStyleSVGReset *style = aFrame->GetStyleSVGReset();
return style->mFilter || style->mClipPath || style->mMask;
}
// Get the union the frame border-box rects over all continuations,
// relative to aFirst. This defines "user space" for non-SVG frames.
static nsRect GetNonSVGUserSpace(nsIFrame* aFirst)
{
NS_ASSERTION(!aFirst->GetPrevContinuation(), "Not first continuation");
return nsLayoutUtils::GetAllInFlowRectsUnion(aFirst, aFirst);
}
static nsRect
GetPreEffectsOverflowRect(nsIFrame* aFrame)
{
nsRect* r = static_cast<nsRect*>(aFrame->GetProperty(nsGkAtoms::preEffectsBBoxProperty));
if (r)
return *r;
return aFrame->GetOverflowRect();
}
struct BBoxCollector : public nsLayoutUtils::BoxCallback {
nsIFrame* mReferenceFrame;
nsIFrame* mCurrentFrame;
const nsRect& mCurrentFrameOverflowArea;
nsRect mResult;
BBoxCollector(nsIFrame* aReferenceFrame, nsIFrame* aCurrentFrame,
const nsRect& aCurrentFrameOverflowArea)
: mReferenceFrame(aReferenceFrame), mCurrentFrame(aCurrentFrame),
mCurrentFrameOverflowArea(aCurrentFrameOverflowArea) {}
virtual void AddBox(nsIFrame* aFrame) {
nsRect overflow = aFrame == mCurrentFrame ? mCurrentFrameOverflowArea
: GetPreEffectsOverflowRect(aFrame);
mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mReferenceFrame));
}
};
static nsRect
GetSVGBBox(nsIFrame* aNonSVGFrame, nsIFrame* aCurrentFrame,
const nsRect& aCurrentOverflow, const nsRect& aUserSpaceRect)
{
NS_ASSERTION(!aNonSVGFrame->GetPrevContinuation(),
"Need first continuation here");
// Compute union of all overflow areas relative to 'first'.
BBoxCollector collector(aNonSVGFrame, aCurrentFrame, aCurrentOverflow);
nsLayoutUtils::GetAllInFlowBoxes(aNonSVGFrame, &collector);
// Get it into "user space" for non-SVG frames
return collector.mResult - aUserSpaceRect.TopLeft();
}
nsRect
nsSVGIntegrationUtils::ComputeFrameEffectsRect(nsIFrame* aFrame,
const nsRect& aOverflowRect)
{
PRBool isOK;
nsIFrame* firstFrame =
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
if (!filterFrame)
return aOverflowRect;
// XXX this isn't really right. We can't compute the correct filter
// bbox until all aFrame's continuations have been reflowed.
// but then it's too late to set the overflow areas for the earlier frames.
nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
nsRect r = GetSVGBBox(firstFrame, aFrame, aOverflowRect, userSpaceRect);
// r is relative to user space
PRUint32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
r.ScaleRoundOutInverse(appUnitsPerDevPixel);
r = filterFrame->GetFilterBBox(firstFrame, &r);
r.ScaleRoundOut(appUnitsPerDevPixel);
// Make it relative to aFrame again
return r + userSpaceRect.TopLeft() - aFrame->GetOffsetTo(firstFrame);
}
nsRect
nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(nsIFrame* aFrame,
const nsRect& aInvalidRect)
{
// Don't bother calling GetEffectProperties; the filter property should
// already have been set up during reflow/ComputeFrameEffectsRect
nsIFrame* firstFrame =
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
nsSVGFilterFrame* filterFrame = nsSVGEffects::GetFilterFrame(firstFrame);
if (!filterFrame)
return aInvalidRect;
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
nsPoint offset = aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
nsRect r = aInvalidRect + offset;
r.ScaleRoundOutInverse(appUnitsPerDevPixel);
r = filterFrame->GetInvalidationBBox(firstFrame, r);
r.ScaleRoundOut(appUnitsPerDevPixel);
return r - offset;
}
nsRect
nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
const nsRect& aDamageRect)
{
// Don't bother calling GetEffectProperties; the filter property should
// already have been set up during reflow/ComputeFrameEffectsRect
nsIFrame* firstFrame =
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
nsSVGFilterFrame* filterFrame =
nsSVGEffects::GetFilterFrame(firstFrame);
if (!filterFrame)
return aDamageRect;
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
nsPoint offset = aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
nsRect r = aDamageRect + offset;
r.ScaleRoundOutInverse(appUnitsPerDevPixel);
r = filterFrame->GetSourceForInvalidArea(firstFrame, r);
r.ScaleRoundOut(appUnitsPerDevPixel);
return r - offset;
}
PRBool
nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
{
nsIFrame* firstFrame =
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
// get point relative to userSpaceRect
nsPoint pt = aPt + aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
return nsSVGUtils::HitTestClip(firstFrame, pt);
}
class RegularFramePaintCallback : public nsSVGFilterPaintCallback
{
public:
RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
nsDisplayList* aInnerList,
const nsPoint& aOffset)
: mBuilder(aBuilder), mInnerList(aInnerList), mOffset(aOffset) {}
virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget,
const nsIntRect* aDirtyRect, nsIDOMSVGMatrix *aTransform)
{
nsIRenderingContext* ctx = aContext->GetRenderingContext(aTarget);
gfxContext* gfxCtx = aContext->GetGfxContext();
if (aTransform) {
// Transform by aTransform first
gfxMatrix m = nsSVGUtils::ConvertSVGMatrixToThebes(aTransform);
gfxCtx->Multiply(m);
}
// We're expected to paint with 1 unit equal to 1 CSS pixel. But
// mInnerList->Paint expects 1 unit to equal 1 device pixel. So
// adjust.
gfxFloat scale =
nsPresContext::AppUnitsToFloatCSSPixels(aTarget->PresContext()->AppUnitsPerDevPixel());
gfxCtx->Scale(scale, scale);
nsIRenderingContext::AutoPushTranslation push(ctx, -mOffset.x, -mOffset.y);
nsRect dirty;
if (aDirtyRect) {
dirty = *aDirtyRect;
dirty.ScaleRoundOut(nsIDeviceContext::AppUnitsPerCSSPixel());
dirty += mOffset;
} else {
dirty = mInnerList->GetBounds(mBuilder);
}
mInnerList->Paint(mBuilder, ctx, dirty);
}
private:
nsDisplayListBuilder* mBuilder;
nsDisplayList* mInnerList;
nsPoint mOffset;
};
void
nsSVGIntegrationUtils::PaintFramesWithEffects(nsIRenderingContext* aCtx,
nsIFrame* aEffectsFrame,
const nsRect& aDirtyRect,
nsDisplayListBuilder* aBuilder,
nsDisplayList* aInnerList)
{
#ifdef DEBUG
nsISVGChildFrame *svgChildFrame;
CallQueryInterface(aEffectsFrame, &svgChildFrame);
#endif
NS_ASSERTION(!svgChildFrame, "Should never be called on an SVG frame");
float opacity = aEffectsFrame->GetStyleDisplay()->mOpacity;
if (opacity == 0.0f)
return;
/* Properties are added lazily and may have been removed by a restyle,
so make sure all applicable ones are set again. */
nsIFrame* firstFrame =
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aEffectsFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
/* SVG defines the following rendering model:
*
* 1. Render geometry
* 2. Apply filter
* 3. Apply clipping, masking, group opacity
*
* We follow this, but perform a couple of optimizations:
*
* + Use cairo's clipPath when representable natively (single object
* clip region).
*
* + Merge opacity and masking if both used together.
*/
PRBool isOK = PR_TRUE;
nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ?
effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull;
nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
nsSVGMaskFrame *maskFrame = effectProperties.mMask ?
effectProperties.mMask->GetMaskFrame(&isOK) : nsnull;
PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;
if (!isOK) {
// Some resource is missing. We shouldn't paint anything.
return;
}
gfxContext* gfx = aCtx->ThebesContext();
gfxMatrix savedCTM = gfx->CurrentMatrix();
nsSVGRenderState svgContext(aCtx);
nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame) + aBuilder->ToReferenceFrame(firstFrame);
PRInt32 appUnitsPerDevPixel = aEffectsFrame->PresContext()->AppUnitsPerDevPixel();
userSpaceRect.ScaleRoundPreservingCentersInverse(appUnitsPerDevPixel);
userSpaceRect.ScaleRoundOut(appUnitsPerDevPixel);
aCtx->Translate(userSpaceRect.x, userSpaceRect.y);
nsCOMPtr<nsIDOMSVGMatrix> matrix = GetInitialMatrix(aEffectsFrame);
PRBool complexEffects = PR_FALSE;
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
complexEffects = PR_TRUE;
gfx->Save();
gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
}
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
* we can just do normal painting and get it clipped appropriately.
*/
if (clipPathFrame && isTrivialClip) {
gfx->Save();
clipPathFrame->ClipPaint(&svgContext, aEffectsFrame, matrix);
}
/* Paint the child */
if (filterFrame) {
RegularFramePaintCallback paint(aBuilder, aInnerList, userSpaceRect.TopLeft());
nsRect r = aDirtyRect - userSpaceRect.TopLeft();
r.ScaleRoundOutInverse(appUnitsPerDevPixel);
filterFrame->FilterPaint(&svgContext, aEffectsFrame, &paint, &r);
} else {
gfx->SetMatrix(savedCTM);
aInnerList->Paint(aBuilder, aCtx, aDirtyRect);
aCtx->Translate(userSpaceRect.x, userSpaceRect.y);
}
if (clipPathFrame && isTrivialClip) {
gfx->Restore();
}
/* No more effects, we're done. */
if (!complexEffects) {
gfx->SetMatrix(savedCTM);
return;
}
gfx->PopGroupToSource();
nsRefPtr<gfxPattern> maskSurface =
maskFrame ? maskFrame->ComputeMaskAlpha(&svgContext, aEffectsFrame,
matrix, opacity) : nsnull;
nsRefPtr<gfxPattern> clipMaskSurface;
if (clipPathFrame && !isTrivialClip) {
gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
nsresult rv = clipPathFrame->ClipPaint(&svgContext, aEffectsFrame, matrix);
clipMaskSurface = gfx->PopGroup();
if (NS_SUCCEEDED(rv) && clipMaskSurface) {
// Still more set after clipping, so clip to another surface
if (maskSurface || opacity != 1.0f) {
gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
gfx->Mask(clipMaskSurface);
gfx->PopGroupToSource();
} else {
gfx->Mask(clipMaskSurface);
}
}
}
if (maskSurface) {
gfx->Mask(maskSurface);
} else if (opacity != 1.0f) {
gfx->Paint(opacity);
}
gfx->Restore();
gfx->SetMatrix(savedCTM);
}
already_AddRefed<nsIDOMSVGMatrix>
nsSVGIntegrationUtils::GetInitialMatrix(nsIFrame* aNonSVGFrame)
{
NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
"SVG frames should not get here");
PRInt32 appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
nsCOMPtr<nsIDOMSVGMatrix> matrix;
float devPxPerCSSPx =
1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
NS_NewSVGMatrix(getter_AddRefs(matrix),
devPxPerCSSPx, 0.0f,
0.0f, devPxPerCSSPx);
return matrix.forget();
}
gfxRect
nsSVGIntegrationUtils::GetSVGRectForNonSVGFrame(nsIFrame* aNonSVGFrame)
{
NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
"SVG frames should not get here");
nsIFrame* firstFrame =
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame);
nsRect r = GetNonSVGUserSpace(firstFrame);
nsPresContext* presContext = firstFrame->PresContext();
return gfxRect(0, 0, presContext->AppUnitsToFloatCSSPixels(r.width),
presContext->AppUnitsToFloatCSSPixels(r.height));
}
gfxRect
nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
{
NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
"SVG frames should not get here");
nsIFrame* firstFrame =
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame);
nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
nsRect r = GetSVGBBox(firstFrame, nsnull, nsRect(), userSpaceRect);
gfxRect result(r.x, r.y, r.width, r.height);
nsPresContext* presContext = aNonSVGFrame->PresContext();
result.ScaleInverse(presContext->AppUnitsPerCSSPixel());
return result;
}

View File

@ -0,0 +1,116 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Mozilla SVG project.
*
* The Initial Developer of the Original Code is IBM Corporation.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* rocallahan@mozilla.com
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef NSSVGINTEGRATIONUTILS_H_
#define NSSVGINTEGRATIONUTILS_H_
#include "nsPoint.h"
#include "nsRect.h"
#include "gfxRect.h"
class nsIFrame;
class nsDisplayListBuilder;
class nsDisplayList;
class nsIRenderingContext;
class nsIDOMSVGMatrix;
/***** Integration of SVG effects with regular frame painting *****/
class nsSVGIntegrationUtils
{
public:
static PRBool
UsingEffectsForFrame(const nsIFrame* aFrame);
/**
* Adjust overflow rect for effects.
* XXX this is a problem. We really need to compute the effects rect for
* a whole chain of frames for a given element at once. but we have no
* way to do this effectively with Gecko's current reflow architecture.
* See http://groups.google.com/group/mozilla.dev.tech.layout/msg/6b179066f3051f65
*/
static nsRect
ComputeFrameEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect);
/**
* Adjust the frame's invalidation area to cover effects
*/
static nsRect
GetInvalidAreaForChangedSource(nsIFrame* aFrame, const nsRect& aInvalidRect);
/**
* Figure out which area of the source is needed given an area to
* repaint
*/
static nsRect
GetRequiredSourceForInvalidArea(nsIFrame* aFrame, const nsRect& aDamageRect);
/**
* Returns true if the given point is not clipped out by effects.
* @param aPt in appunits relative to aFrame
*/
static PRBool
HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt);
/**
* Paint non-SVG frame with SVG effects.
* @param aOffset the offset in appunits where aFrame should be positioned
* in aCtx's coordinate system
*/
static void
PaintFramesWithEffects(nsIRenderingContext* aCtx,
nsIFrame* aEffectsFrame, const nsRect& aDirtyRect,
nsDisplayListBuilder* aBuilder,
nsDisplayList* aInnerList);
static already_AddRefed<nsIDOMSVGMatrix>
GetInitialMatrix(nsIFrame* aNonSVGFrame);
/**
* Returns aNonSVGFrame's rect in CSS pixel units. This is the union
* of all its continuations' rectangles. The top-left is always 0,0
* since "user space" origin for non-SVG frames is the top-left of the
* union of all the continuations' rectangles.
*/
static gfxRect
GetSVGRectForNonSVGFrame(nsIFrame* aNonSVGFrame);
/**
* Returns aNonSVGFrame's bounding box in CSS units. This is the union
* of all its continuations' overflow areas, relative to the top-left
* of all the continuations' rectangles.
*/
static gfxRect
GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame);
};
#endif /*NSSVGINTEGRATIONUTILS_H_*/

View File

@ -61,7 +61,7 @@ NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContex
already_AddRefed<gfxPattern>
nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRenderState *aContext,
nsISVGChildFrame* aParent,
nsIFrame* aParent,
nsIDOMSVGMatrix* aMatrix,
float aOpacity)
{
@ -79,67 +79,23 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRenderState *aContext,
gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
{
nsIFrame *frame;
CallQueryInterface(aParent, &frame);
nsSVGElement *parent = static_cast<nsSVGElement*>(frame->GetContent());
float x, y, width, height;
nsSVGMaskElement *mask = static_cast<nsSVGMaskElement*>(mContent);
nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight;
tmpX = &mask->mLengthAttributes[nsSVGMaskElement::X];
tmpY = &mask->mLengthAttributes[nsSVGMaskElement::Y];
tmpWidth = &mask->mLengthAttributes[nsSVGMaskElement::WIDTH];
tmpHeight = &mask->mLengthAttributes[nsSVGMaskElement::HEIGHT];
PRUint16 units =
mask->mEnumAttributes[nsSVGMaskElement::MASKUNITS].GetAnimValue();
if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
aParent->SetMatrixPropagation(PR_FALSE);
aParent->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
nsCOMPtr<nsIDOMSVGRect> bbox;
aParent->GetBBox(getter_AddRefs(bbox));
aParent->SetMatrixPropagation(PR_TRUE);
aParent->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
bbox = nsSVGUtils::GetBBox(aParent);
if (!bbox)
return nsnull;
#ifdef DEBUG_tor
bbox->GetX(&x);
bbox->GetY(&y);
bbox->GetWidth(&width);
bbox->GetHeight(&height);
fprintf(stderr, "mask bbox: %f,%f %fx%f\n", x, y, width, height);
#endif
bbox->GetX(&x);
x += nsSVGUtils::ObjectSpace(bbox, tmpX);
bbox->GetY(&y);
y += nsSVGUtils::ObjectSpace(bbox, tmpY);
width = nsSVGUtils::ObjectSpace(bbox, tmpWidth);
height = nsSVGUtils::ObjectSpace(bbox, tmpHeight);
} else {
x = nsSVGUtils::UserSpace(parent, tmpX);
y = nsSVGUtils::UserSpace(parent, tmpY);
width = nsSVGUtils::UserSpace(parent, tmpWidth);
height = nsSVGUtils::UserSpace(parent, tmpHeight);
}
#ifdef DEBUG_tor
fprintf(stderr, "mask clip: %f,%f %fx%f\n", x, y, width, height);
#endif
gfxRect maskArea = nsSVGUtils::GetRelativeRect(units,
&mask->mLengthAttributes[nsSVGMaskElement::X], bbox, aParent);
gfx->Save();
nsSVGUtils::SetClipRect(gfx, aMatrix, x, y, width, height);
nsSVGUtils::SetClipRect(gfx, aMatrix, maskArea.X(), maskArea.Y(),
maskArea.Width(), maskArea.Height());
}
mMaskParent = aParent;

View File

@ -57,7 +57,7 @@ protected:
public:
// nsSVGMaskFrame method:
already_AddRefed<gfxPattern> ComputeMaskAlpha(nsSVGRenderState *aContext,
nsISVGChildFrame* aParent,
nsIFrame* aParent,
nsIDOMSVGMatrix* aMatrix,
float aOpacity = 1.0f);
@ -95,7 +95,7 @@ private:
nsSVGMaskFrame *mFrame;
};
nsISVGChildFrame *mMaskParent;
nsIFrame *mMaskParent;
nsCOMPtr<nsIDOMSVGMatrix> mMaskParentMatrix;
// recursion prevention flag
PRPackedBool mInUse;

View File

@ -499,10 +499,20 @@ nsSVGPathGeometryFrame::NotifyRedrawUnsuspended()
NS_IMETHODIMP
nsSVGPathGeometryFrame::SetMatrixPropagation(PRBool aPropagate)
{
mPropagateTransform = aPropagate;
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGPathGeometryFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}
NS_IMETHODIMP
nsSVGPathGeometryFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
{
@ -538,7 +548,7 @@ nsSVGPathGeometryFrame::GetCanvasTM(nsIDOMSVGMatrix * *aCTM)
{
*aCTM = nsnull;
if (!mPropagateTransform) {
if (!GetMatrixPropagation()) {
if (mOverrideCTM) {
*aCTM = mOverrideCTM;
NS_ADDREF(*aCTM);

View File

@ -65,8 +65,7 @@ class nsSVGPathGeometryFrame : public nsSVGPathGeometryFrameBase,
nsStyleContext* aContext);
protected:
nsSVGPathGeometryFrame(nsStyleContext* aContext) :
nsSVGPathGeometryFrameBase(aContext),
mPropagateTransform(PR_TRUE) {}
nsSVGPathGeometryFrameBase(aContext) {}
public:
// nsISupports interface:
@ -112,6 +111,7 @@ protected:
NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended();
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
virtual PRBool GetMatrixPropagation();
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
@ -143,7 +143,6 @@ private:
void RemovePathProperties();
nsCOMPtr<nsIDOMSVGMatrix> mOverrideCTM;
PRPackedBool mPropagateTransform;
};
#endif // __NS_SVGPATHGEOMETRYFRAME_H__

View File

@ -102,13 +102,6 @@ nsSVGTSpanFrame::AttributeChanged(PRInt32 aNameSpaceID,
//----------------------------------------------------------------------
// nsISVGChildFrame methods
NS_IMETHODIMP
nsSVGTSpanFrame::SetMatrixPropagation(PRBool aPropagate)
{
mPropagateTransform = aPropagate;
return NS_OK;
}
NS_IMETHODIMP
nsSVGTSpanFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
{

View File

@ -81,7 +81,6 @@ public:
}
#endif
// nsISVGChildFrame interface:
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();

View File

@ -212,13 +212,6 @@ nsSVGTextFrame::NotifyRedrawUnsuspended()
return nsSVGTextFrameBase::NotifyRedrawUnsuspended();
}
NS_IMETHODIMP
nsSVGTextFrame::SetMatrixPropagation(PRBool aPropagate)
{
mPropagateTransform = aPropagate;
return NS_OK;
}
NS_IMETHODIMP
nsSVGTextFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
{
@ -282,7 +275,7 @@ nsSVGTextFrame::GetBBox(nsIDOMSVGRect **_retval)
already_AddRefed<nsIDOMSVGMatrix>
nsSVGTextFrame::GetCanvasTM()
{
if (!mPropagateTransform) {
if (!GetMatrixPropagation()) {
nsIDOMSVGMatrix *retval;
if (mOverrideCTM) {
retval = mOverrideCTM;

View File

@ -51,7 +51,6 @@ protected:
nsSVGTextFrame(nsStyleContext* aContext)
: nsSVGTextFrameBase(aContext),
mMetricsState(unsuspended),
mPropagateTransform(PR_TRUE),
mPositioningDirty(PR_TRUE) {}
public:
@ -75,7 +74,6 @@ public:
#endif
// nsISVGChildFrame interface:
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
virtual void NotifySVGChanged(PRUint32 aFlags);
@ -119,7 +117,6 @@ private:
enum UpdateState { unsuspended, suspended };
UpdateState mMetricsState;
PRPackedBool mPropagateTransform;
PRPackedBool mPositioningDirty;
};

View File

@ -83,6 +83,8 @@
#include "nsIDOMSVGUnitTypes.h"
#include "nsSVGRect.h"
#include "nsSVGEffects.h"
#include "nsSVGIntegrationUtils.h"
#include "nsSVGFilterPaintCallback.h"
gfxASurface *nsSVGUtils::mThebesComputationalSurface = nsnull;
@ -217,8 +219,14 @@ nsSVGUtils::GetFontSize(nsIContent *aContent)
return 1.0f;
}
return nsPresContext::AppUnitsToFloatCSSPixels(frame->GetStyleFont()->mSize) /
frame->PresContext()->TextZoom();
return GetFontSize(frame);
}
float
nsSVGUtils::GetFontSize(nsIFrame *aFrame)
{
return nsPresContext::AppUnitsToFloatCSSPixels(aFrame->GetStyleFont()->mSize) /
aFrame->PresContext()->TextZoom();
}
float
@ -230,8 +238,14 @@ nsSVGUtils::GetFontXHeight(nsIContent *aContent)
return 1.0f;
}
return GetFontXHeight(frame);
}
float
nsSVGUtils::GetFontXHeight(nsIFrame *aFrame)
{
nsCOMPtr<nsIFontMetrics> fontMetrics;
nsLayoutUtils::GetFontMetricsForFrame(frame, getter_AddRefs(fontMetrics));
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fontMetrics));
if (!fontMetrics) {
NS_WARNING("no FontMetrics in GetFontXHeight()");
@ -241,7 +255,7 @@ nsSVGUtils::GetFontXHeight(nsIContent *aContent)
nscoord xHeight;
fontMetrics->GetXHeight(xHeight);
return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
frame->PresContext()->TextZoom();
aFrame->PresContext()->TextZoom();
}
void
@ -580,7 +594,7 @@ nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect)
if (property) {
nsSVGFilterFrame *filter = property->GetFilterFrame(nsnull);
if (filter) {
rect = filter->GetInvalidationRegion(aFrame, rect);
rect = filter->GetInvalidationBBox(aFrame, rect);
}
}
aFrame = aFrame->GetParent();
@ -655,7 +669,7 @@ nsSVGUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight)
}
float
nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, nsSVGLength2 *aLength)
nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, const nsSVGLength2 *aLength)
{
float fraction, axis;
@ -685,11 +699,17 @@ nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, nsSVGLength2 *aLength)
}
float
nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, nsSVGLength2 *aLength)
nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
{
return aLength->GetAnimValue(aSVGElement);
}
float
nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
{
return aLength->GetAnimValue(aNonSVGContext);
}
void
nsSVGUtils::TransformPoint(nsIDOMSVGMatrix *matrix,
float *x, float *y)
@ -851,6 +871,9 @@ nsSVGUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
already_AddRefed<nsIDOMSVGMatrix>
nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
{
if (!aFrame->IsFrameOfType(nsIFrame::eSVG))
return nsSVGIntegrationUtils::GetInitialMatrix(aFrame);
if (!aFrame->IsLeaf()) {
// foreignObject is the one non-leaf svg frame that isn't a SVGContainer
if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
@ -917,6 +940,26 @@ nsSVGUtils::RemoveObserver(nsISupports *aObserver, nsISupports *aTarget)
// ************************************************************
class SVGPaintCallback : public nsSVGFilterPaintCallback
{
public:
virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget,
const nsIntRect* aDirtyRect, nsIDOMSVGMatrix *aTransform)
{
nsISVGChildFrame *svgChildFrame;
CallQueryInterface(aTarget, &svgChildFrame);
NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
if (aTransform) {
svgChildFrame->SetOverrideCTM(aTransform);
svgChildFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
}
svgChildFrame->PaintSVG(aContext, const_cast<nsIntRect*>(aDirtyRect));
}
};
void
nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
nsIntRect *aDirtyRect,
@ -938,14 +981,21 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(aFrame);
/* Check if we need to draw anything */
PRBool isOK = PR_TRUE;
nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
/* Check if we need to draw anything. HasValidCoveredRect only returns
* true for path geometry and glyphs, so basically we're traversing
* all containers and we can only skip leaves here.
*/
if (aDirtyRect && svgChildFrame->HasValidCoveredRect()) {
nsRect rect = *aDirtyRect;
rect.ScaleRoundOut(aFrame->PresContext()->AppUnitsPerDevPixel());
if (effectProperties.mFilter) {
if (!rect.Intersects(FindFilterInvalidation(aFrame, aFrame->GetRect())))
if (filterFrame) {
if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull)))
return;
} else {
nsRect rect = *aDirtyRect;
rect.ScaleRoundOut(aFrame->PresContext()->AppUnitsPerDevPixel());
if (!rect.Intersects(aFrame->GetRect()))
return;
}
@ -971,11 +1021,8 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
gfxContext *gfx = aContext->GetGfxContext();
PRBool complexEffects = PR_FALSE;
PRBool isOK = PR_TRUE;
nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ?
effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull;
nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
nsSVGMaskFrame *maskFrame = effectProperties.mMask ?
effectProperties.mMask->GetMaskFrame(&isOK) : nsnull;
@ -1002,12 +1049,13 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
*/
if (clipPathFrame && isTrivialClip) {
gfx->Save();
clipPathFrame->ClipPaint(aContext, svgChildFrame, matrix);
clipPathFrame->ClipPaint(aContext, aFrame, matrix);
}
/* Paint the child */
if (filterFrame) {
filterFrame->FilterPaint(aContext, svgChildFrame, aDirtyRect);
SVGPaintCallback paintCallback;
filterFrame->FilterPaint(aContext, aFrame, &paintCallback, aDirtyRect);
} else {
svgChildFrame->PaintSVG(aContext, aDirtyRect);
}
@ -1023,14 +1071,14 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
gfx->PopGroupToSource();
nsRefPtr<gfxPattern> maskSurface =
maskFrame ? maskFrame->ComputeMaskAlpha(aContext, svgChildFrame,
maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
matrix, opacity) : nsnull;
nsRefPtr<gfxPattern> clipMaskSurface;
if (clipPathFrame && !isTrivialClip) {
gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
nsresult rv = clipPathFrame->ClipPaint(aContext, svgChildFrame, matrix);
nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
clipMaskSurface = gfx->PopGroup();
if (NS_SUCCEEDED(rv) && clipMaskSurface) {
@ -1077,11 +1125,8 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
return PR_FALSE;
}
nsISVGChildFrame* SVGFrame;
CallQueryInterface(aFrame, &SVGFrame);
nsCOMPtr<nsIDOMSVGMatrix> matrix = GetCanvasTM(aFrame);
return clipPathFrame->ClipHitTest(SVGFrame, matrix, aPoint);
return clipPathFrame->ClipHitTest(aFrame, matrix, aPoint);
}
nsIFrame *
@ -1327,6 +1372,58 @@ nsSVGUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
? NS_OK : NS_ERROR_FAILURE;
}
already_AddRefed<nsIDOMSVGRect>
nsSVGUtils::GetBBox(nsIFrame *aFrame)
{
nsISVGChildFrame *svg;
CallQueryInterface(aFrame, &svg);
if (!svg) {
nsIDOMSVGRect *rect = nsnull;
gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
NS_NewSVGRect(&rect, r);
return rect;
}
PRBool needToDisablePropagation = svg->GetMatrixPropagation();
if (needToDisablePropagation) {
svg->SetMatrixPropagation(PR_FALSE);
svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
}
nsCOMPtr<nsIDOMSVGRect> bbox;
svg->GetBBox(getter_AddRefs(bbox));
if (needToDisablePropagation) {
svg->SetMatrixPropagation(PR_TRUE);
svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
}
return bbox.forget();
}
gfxRect
nsSVGUtils::GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH,
nsIDOMSVGRect *aBBox, nsIFrame *aFrame)
{
float x, y, width, height;
if (aUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
aBBox->GetX(&x);
x += ObjectSpace(aBBox, &aXYWH[0]);
aBBox->GetY(&y);
y += ObjectSpace(aBBox, &aXYWH[1]);
width = ObjectSpace(aBBox, &aXYWH[2]);
height = ObjectSpace(aBBox, &aXYWH[3]);
} else {
x = nsSVGUtils::UserSpace(aFrame, &aXYWH[0]);
y = nsSVGUtils::UserSpace(aFrame, &aXYWH[1]);
width = nsSVGUtils::UserSpace(aFrame, &aXYWH[2]);
height = nsSVGUtils::UserSpace(aFrame, &aXYWH[3]);
}
return gfxRect(x, y, width, height);
}
PRBool
nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
{
@ -1363,33 +1460,45 @@ nsSVGUtils::MaxExpansion(nsIDOMSVGMatrix *aMatrix)
already_AddRefed<nsIDOMSVGMatrix>
nsSVGUtils::AdjustMatrixForUnits(nsIDOMSVGMatrix *aMatrix,
nsSVGEnum *aUnits,
nsISVGChildFrame *aFrame)
nsIFrame *aFrame)
{
nsCOMPtr<nsIDOMSVGMatrix> fini = aMatrix;
if (aFrame &&
aUnits->GetAnimValue() == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
nsCOMPtr<nsIDOMSVGRect> rect;
nsresult rv = aFrame->GetBBox(getter_AddRefs(rect));
if (NS_SUCCEEDED(rv)) {
float minx, miny, width, height;
PRBool gotRect = PR_FALSE;
if (aFrame->IsFrameOfType(nsIFrame::eSVG)) {
nsISVGChildFrame *svgFrame;
CallQueryInterface(aFrame, &svgFrame);
nsCOMPtr<nsIDOMSVGRect> rect;
svgFrame->GetBBox(getter_AddRefs(rect));
if (rect) {
gotRect = PR_TRUE;
rect->GetX(&minx);
rect->GetY(&miny);
rect->GetWidth(&width);
rect->GetHeight(&height);
// Correct for scaling in outersvg CTM
nsIFrame *frame;
CallQueryInterface(aFrame, &frame);
nsPresContext *presCtx = frame->PresContext();
float cssPxPerDevPx =
presCtx->AppUnitsToFloatCSSPixels(presCtx->AppUnitsPerDevPixel());
minx *= cssPxPerDevPx;
miny *= cssPxPerDevPx;
width *= cssPxPerDevPx;
height *= cssPxPerDevPx;
nsPresContext *presCtx = aFrame->PresContext();
float scaleInv =
presCtx->AppUnitsToGfxUnits(presCtx->AppUnitsPerCSSPixel());
minx /= scaleInv;
miny /= scaleInv;
width /= scaleInv;
height /= scaleInv;
}
} else {
gotRect = PR_TRUE;
gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
minx = r.X();
miny = r.Y();
width = r.Width();
height = r.Height();
}
if (gotRect) {
nsCOMPtr<nsIDOMSVGMatrix> tmp;
aMatrix->Translate(minx, miny, getter_AddRefs(tmp));
tmp->ScaleNonUniform(width, height, getter_AddRefs(fini));
@ -1444,6 +1553,8 @@ nsSVGRenderState::GetRenderingContext(nsIFrame *aFrame)
if (!mRenderingContext) {
nsIDeviceContext* devCtx = aFrame->PresContext()->DeviceContext();
devCtx->CreateRenderingContextInstance(*getter_AddRefs(mRenderingContext));
if (!mRenderingContext)
return nsnull;
mRenderingContext->Init(devCtx, mGfxContext);
}
return mRenderingContext;

View File

@ -100,6 +100,8 @@ class nsISVGChildFrame;
/* are we the child of a non-display container? */
#define NS_STATE_SVG_NONDISPLAY_CHILD 0x02000000
#define NS_STATE_SVG_PROPAGATE_TRANSFORM 0x04000000
/**
* Byte offsets of channels in a native packed gfxColor or cairo image surface.
*/
@ -192,11 +194,12 @@ public:
* Get a font-size (em) of an nsIContent
*/
static float GetFontSize(nsIContent *aContent);
static float GetFontSize(nsIFrame *aFrame);
/*
* Get an x-height of of an nsIContent
*/
static float GetFontXHeight(nsIContent *aContent);
static float GetFontXHeight(nsIFrame *aFrame);
/*
* Converts image data from premultipled to unpremultiplied alpha
@ -299,13 +302,19 @@ public:
Input: rect - bounding box
length - length to be converted
*/
static float ObjectSpace(nsIDOMSVGRect *aRect, nsSVGLength2 *aLength);
static float ObjectSpace(nsIDOMSVGRect *aRect, const nsSVGLength2 *aLength);
/* Computes the input length in terms of user space coordinates.
Input: content - object to be used for determining user space
Input: length - length to be converted
*/
static float UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength);
/* Computes the input length in terms of user space coordinates.
Input: aFrame - object to be used for determining user space
length - length to be converted
*/
static float UserSpace(nsSVGElement *aSVGElement, nsSVGLength2 *aLength);
static float UserSpace(nsIFrame *aFrame, const nsSVGLength2 *aLength);
/* Tranforms point by the matrix. In/out: x,y */
static void
@ -337,8 +346,8 @@ public:
nsIDOMSVGAnimatedPreserveAspectRatio *aPreserveAspectRatio,
PRBool aIgnoreAlign = PR_FALSE);
/* Paint frame with SVG effects - aDirtyRect is the area being
* redrawn, in frame offset pixel coordinates */
/* Paint SVG frame with SVG effects - aDirtyRect is the area being
* redrawn, in device pixel coordinates relative to the outer svg */
static void
PaintChildWithEffects(nsSVGRenderState *aContext,
nsIntRect *aDirtyRect,
@ -371,7 +380,8 @@ public:
/*
* Returns the CanvasTM of the indicated frame, whether it's a
* child or container SVG frame.
* child SVG frame, container SVG frame, or a regular frame.
* For regular frames, we just return an identity matrix.
*/
static already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM(nsIFrame *aFrame);
@ -469,7 +479,26 @@ public:
static already_AddRefed<nsIDOMSVGMatrix>
AdjustMatrixForUnits(nsIDOMSVGMatrix *aMatrix,
nsSVGEnum *aUnits,
nsISVGChildFrame *aFrame);
nsIFrame *aFrame);
/**
* Get bounding-box for aFrame. Matrix propagation is disabled so the
* bounding box is computed in terms of aFrame's own user space.
*/
static already_AddRefed<nsIDOMSVGRect>
GetBBox(nsIFrame *aFrame);
/**
* Compute a rectangle in userSpaceOnUse or objectBoundingBoxUnits.
* @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing
* the x, y, width and height values in that order
* @param aBBox the bounding box of the object the rect is relative to;
* may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
* @param aFrame the object in which to interpret user-space units;
* may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
*/
static gfxRect
GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH, nsIDOMSVGRect *aBBox,
nsIFrame *aFrame);
#ifdef DEBUG
static void