Files
Mozilla/mozilla/layout/svg/base/src/nsSVGPatternFrame.cpp
jwatt%jwatt.org 4624a80f67 Fixing bug 328571. Change the type of PRBool SVG classes members to PRPackedBool. r+sr=roc@ocallahan.org
git-svn-id: svn://10.0.0.236/trunk@191478 18797224-902f-48f8-a5cc-f745e15eee43
2006-03-01 10:59:36 +00:00

1161 lines
36 KiB
C++

/* -*- 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
* Scooter Morris.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Scooter Morris <scootermorris@comcast.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 "nsIDOMSVGFitToViewBox.h"
#include "nsSVGPattern.h"
#include "nsWeakReference.h"
#include "nsIDOMDocument.h"
#include "nsIDocument.h"
#include "nsIDOMSVGStopElement.h"
#include "nsSVGAtoms.h"
#include "nsIDOMSVGLength.h"
#include "nsIDOMSVGAnimatedEnum.h"
#include "nsIDOMSVGAnimatedLength.h"
#include "nsIDOMSVGAnimatedNumber.h"
#include "nsIDOMSVGAnimatedRect.h"
#include "nsIDOMSVGAnimatedString.h"
#include "nsIDOMSVGAnimTransformList.h"
#include "nsIDOMSVGTransformList.h"
#include "nsSVGPreserveAspectRatio.h"
#include "nsSVGAnimatedPreserveAspectRatio.h"
#include "nsIDOMSVGNumber.h"
#include "nsIDOMSVGPatternElement.h"
#include "nsIDOMSVGURIReference.h"
#include "nsISVGValue.h"
#include "nsISVGValueUtils.h"
#include "nsStyleContext.h"
#include "nsISVGValueObserver.h"
#include "nsSVGValue.h"
#include "nsNetUtil.h"
#include "nsINameSpaceManager.h"
#include "nsISVGChildFrame.h"
#include "nsIDOMSVGRect.h"
#include "nsSVGMatrix.h"
#include "nsSVGRect.h"
#include "nsISVGGeometrySource.h"
#include "nsISVGPattern.h"
#include "nsIURI.h"
#include "nsIContent.h"
#include "nsContentUtils.h"
#include "nsSVGNumber.h"
#include "nsLayoutAtoms.h"
#include "nsISVGRenderer.h"
#include "nsISVGRendererCanvas.h"
#include "nsISVGRendererSurface.h"
#include "nsSVGUtils.h"
#include "nsISVGOuterSVGFrame.h"
#include "nsISVGContainerFrame.h"
#include "nsSVGDefsFrame.h"
typedef nsSVGDefsFrame nsSVGPatternFrameBase;
#ifdef DEBUG_scooter
static void printCTM(char *msg, nsIDOMSVGMatrix *aCTM);
static void printRect(char *msg, nsIDOMSVGRect *aRect);
#endif
class nsSVGPatternFrame : public nsSVGPatternFrameBase,
public nsSVGValue,
public nsISVGPattern
{
protected:
friend nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
nsIContent* aContent);
friend nsresult NS_GetSVGPatternFrame(nsIFrame** result,
nsIURI* aURI,
nsIContent* aContent,
nsIPresShell* aPresShell);
public:
// nsISupports interface:
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; }
NS_IMETHOD_(nsrefcnt) Release() { return NS_OK; }
// nsISVGPattern interface:
NS_DECL_NSISVGPATTERN
// nsISVGValue interface:
NS_IMETHOD SetValueString(const nsAString &aValue) { return NS_OK; }
NS_IMETHOD GetValueString(nsAString& aValue) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// nsISVGValueObserver interface:
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
// nsIFrame interface:
NS_IMETHOD DidSetStyleContext(nsPresContext* aPresContext);
// nsISVGContainerFrame interface:
already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
already_AddRefed<nsSVGCoordCtxProvider> GetCoordContextProvider();
NS_IMETHOD GetBBox(nsIDOMSVGRect **aRect);
/**
* Get the "type" of the frame
*
* @see nsLayoutAtoms::svgPatternFrame
*/
virtual nsIAtom* GetType() const;
virtual PRBool IsFrameOfType(PRUint32 aFlags) const;
#ifdef DEBUG
NS_IMETHOD GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("SVGPattern"), aResult);
}
#endif // DEBUG
protected:
virtual ~nsSVGPatternFrame();
// Internal methods for handling referenced patterns
PRBool checkURITarget(nsIAtom *);
PRBool checkURITarget();
//
NS_IMETHOD PrivateGetPatternUnits(nsIDOMSVGAnimatedEnumeration * *aEnum);
NS_IMETHOD PrivateGetPatternContentUnits(nsIDOMSVGAnimatedEnumeration * *aEnum);
NS_IMETHOD PrivateGetX(nsIDOMSVGLength * *aValue);
NS_IMETHOD PrivateGetY(nsIDOMSVGLength * *aValue);
NS_IMETHOD PrivateGetWidth(nsIDOMSVGLength * *aValue);
NS_IMETHOD PrivateGetHeight(nsIDOMSVGLength * *aValue);
NS_IMETHOD PrivateGetViewBox(nsIDOMSVGAnimatedRect * *aMatrix);
NS_IMETHOD PrivateGetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio **aPreserveAspectRatio);
NS_IMETHOD GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio **aPreserveAspectRatio);
NS_IMETHOD GetPatternFirstChild(nsIFrame **kid);
NS_IMETHOD GetViewBox(nsIDOMSVGRect * *aMatrix);
nsresult GetPatternRect(nsIDOMSVGRect **patternRect,
nsIDOMSVGRect *bbox, nsIContent *content);
nsresult GetPatternMatrix(nsIDOMSVGMatrix **ctm, nsIDOMSVGRect *bbox, nsIDOMSVGRect *aViewRect);
nsresult ConstructCTM(nsIDOMSVGMatrix **ctm, nsIDOMSVGMatrix *aPCTM, nsIDOMSVGRect *aViewRect);
private:
// Cached values
nsCOMPtr<nsIDOMSVGAnimatedEnumeration> mPatternUnits;
nsCOMPtr<nsIDOMSVGAnimatedEnumeration> mPatternContentUnits;
nsCOMPtr<nsIDOMSVGLength> mX;
nsCOMPtr<nsIDOMSVGLength> mY;
nsCOMPtr<nsIDOMSVGLength> mWidth;
nsCOMPtr<nsIDOMSVGLength> mHeight;
nsCOMPtr<nsIDOMSVGRect> mBBox;
nsCOMPtr<nsIDOMSVGAnimatedRect> mViewBox;
nsCOMPtr<nsIDOMSVGAnimatedPreserveAspectRatio> mPreserveAspectRatio;
// this is a *temporary* reference to the frame of the element currently
// referencing our pattern. This must be termporary because different
// referencing frames will all reference this one fram
nsISVGGeometrySource *mSource;
nsCOMPtr<nsIDOMSVGMatrix> mCTM;
nsAutoString mNextPatternStr;
protected:
nsSVGPatternFrame *mNextPattern;
PRPackedBool mLoopFlag;
};
//----------------------------------------------------------------------
// Implementation
nsSVGPatternFrame::~nsSVGPatternFrame()
{
WillModify();
// Notify the world that we're dying
DidModify(mod_die);
// Remove observers on pattern attributes
if (mPatternUnits) NS_REMOVE_SVGVALUE_OBSERVER(mPatternUnits);
if (mPatternContentUnits) NS_REMOVE_SVGVALUE_OBSERVER(mPatternContentUnits);
if (mX) NS_REMOVE_SVGVALUE_OBSERVER(mX);
if (mY) NS_REMOVE_SVGVALUE_OBSERVER(mY);
if (mWidth) NS_REMOVE_SVGVALUE_OBSERVER(mWidth);
if (mHeight) NS_REMOVE_SVGVALUE_OBSERVER(mHeight);
if (mViewBox) NS_REMOVE_SVGVALUE_OBSERVER(mViewBox);
if (mPreserveAspectRatio) NS_REMOVE_SVGVALUE_OBSERVER(mPreserveAspectRatio);
}
//----------------------------------------------------------------------
// nsISupports methods:
NS_INTERFACE_MAP_BEGIN(nsSVGPatternFrame)
NS_INTERFACE_MAP_ENTRY(nsISVGValue)
NS_INTERFACE_MAP_ENTRY(nsISVGPattern)
NS_INTERFACE_MAP_ENTRY(nsISVGContainerFrame)
NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
NS_INTERFACE_MAP_END_INHERITING(nsSVGPatternFrameBase)
//----------------------------------------------------------------------
// nsISVGValueObserver methods:
NS_IMETHODIMP
nsSVGPatternFrame::WillModifySVGObservable(nsISVGValue* observable,
modificationType aModType)
{
WillModify(aModType);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
nsCOMPtr<nsISVGPattern> pattern = do_QueryInterface(observable);
// Is this a pattern we are observing that is going away?
if (mNextPattern && aModType == nsISVGValue::mod_die && pattern) {
// Yes, we need to handle this differently
nsISVGPattern *patt;
CallQueryInterface(mNextPattern, &patt);
if (patt == pattern) {
mNextPattern = nsnull;
}
}
// Something we depend on was modified -- pass it on!
DidModify(aModType);
return NS_OK;
}
//----------------------------------------------------------------------
// nsIFrame methods:
NS_IMETHODIMP
nsSVGPatternFrame::DidSetStyleContext(nsPresContext* aPresContext)
{
WillModify(mod_other);
DidModify(mod_other);
return NS_OK;
}
nsIAtom*
nsSVGPatternFrame::GetType() const
{
return nsLayoutAtoms::svgPatternFrame;
}
PRBool
nsSVGPatternFrame::IsFrameOfType(PRUint32 aFlags) const
{
return !(aFlags & ~nsIFrame::eSVG);
}
//----------------------------------------------------------------------
// nsISVGContainerFrame interface:
// If our GetCanvasTM is getting called, we
// need to return *our current* transformation
// matrix, which depends on our units parameters
// and X, Y, Width, and Height
already_AddRefed<nsIDOMSVGMatrix>
nsSVGPatternFrame::GetCanvasTM() {
nsIDOMSVGMatrix *rCTM;
if (mCTM) {
rCTM = mCTM;
NS_IF_ADDREF(rCTM);
} else {
// Do we know our rendering parent?
if (mSource) {
// Yes, use it!
mSource->GetCanvasTM(&rCTM);
} else {
// No, we'll use our content parent, then
nsCOMPtr<nsISVGGeometrySource> aSource = do_QueryInterface(mParent);
if (aSource) {
aSource->GetCanvasTM(&rCTM);
} else {
// OK, we have no content parent, which means that we're
// not part of the document tree. Return an identity matrix?
NS_NewSVGMatrix(&rCTM, 1.0f,0.0f,0.0f,1.0f,0.0f,0.0f);
}
}
}
return rCTM;
}
already_AddRefed<nsSVGCoordCtxProvider>
nsSVGPatternFrame::GetCoordContextProvider() {
NS_ASSERTION(mParent, "null parent");
nsISVGContainerFrame *containerFrame;
mParent->QueryInterface(NS_GET_IID(nsISVGContainerFrame),
(void**)&containerFrame);
if (!containerFrame) {
NS_ERROR("invalid container");
return nsnull;
}
return containerFrame->GetCoordContextProvider();
}
NS_IMETHODIMP
nsSVGPatternFrame::GetBBox(nsIDOMSVGRect **aRect) {
*aRect = nsnull;
if (mBBox) {
*aRect = mBBox;
return NS_OK;
} else {
if (mSource) {
// Yes, use it!
nsCOMPtr<nsISVGChildFrame> callerSVGFrame = do_QueryInterface(mSource);
if (callerSVGFrame) {
return callerSVGFrame->GetBBox(aRect);
}
} else {
// No, we'll use our content parent, then
nsCOMPtr<nsISVGChildFrame> aFrame = do_QueryInterface(mParent);
if (aFrame) {
return aFrame->GetBBox(aRect);
}
}
}
return NS_ERROR_FAILURE;
}
//----------------------------------------------------------------------
// nsISVGPattern implementation
//----------------------------------------------------------------------
NS_IMETHODIMP
nsSVGPatternFrame::PaintPattern(nsISVGRendererCanvas* canvas,
nsISVGRendererSurface** surface,
nsIDOMSVGMatrix** patternMatrix,
nsISVGGeometrySource *aSource)
{
/*
* General approach:
* Set the content geometry stuff
* Calculate our bbox (using x,y,width,height & patternUnits &
* patternTransform)
* Create the surface
* Calculate the content transformation matrix
* Get our children (we may need to get them from another Pattern)
* Call SVGPaint on all of our children
* Return
*/
*surface = nsnull;
// Get our child
nsIFrame *firstKid;
if (NS_FAILED(GetPatternFirstChild(&firstKid)))
return NS_ERROR_FAILURE; // Either no kids or a bad reference
/*
* Get the content geometry information. This is a little tricky --
* our parent is probably a <defs>, but we are rendering in the context
* of some geometry source. Our content geometry information needs to
* come from our rendering parent as opposed to our content parent. We
* get that information from aSource, which is passed to us from the
* backend renderer.
*/
// Get the geometric parent CTM to get our scale
nsCOMPtr<nsISVGChildFrame> callerSVGFrame = do_QueryInterface(aSource);
NS_ASSERTION(callerSVGFrame,"Caller is not an nsISVGChildFrame!");
nsIFrame *callerFrame;
CallQueryInterface(callerSVGFrame, &callerFrame);
NS_ASSERTION(callerFrame,"Caller is not an nsIFrame!");
// Make sure the callerContent is an SVG element. If we are attempting
// to paint a pattern for text, then the content will be the #text, so we
// actually want the parent, which should be the <svg:text> or <svg:tspan>
// element.
nsIContent *callerContent;
nsIAtom *callerType = callerFrame->GetType();
if (callerType == nsLayoutAtoms::svgGlyphFrame) {
callerContent = callerFrame->GetContent()->GetParent();
} else {
callerContent = callerFrame->GetContent();
}
NS_ASSERTION(callerContent,"Caller does not have any content!");
nsISVGContainerFrame *container;
CallQueryInterface(callerFrame, &container);
nsCOMPtr<nsIDOMSVGMatrix> parentCTM;
if (container) {
parentCTM = container->GetCanvasTM();
} else {
aSource->GetCanvasTM(getter_AddRefs(parentCTM));
}
/*
* OK, we've got the content geometry in general. Now we need to get two
* different transformation matrices. First, we need to transform the
* x,y,width,and height units. This is done differently than the units of
* the actual elements we are going to paint. We also need to pass some
* of this information back to our caller (to get the x,y offset of the
* pattern within the painted area. So, we need to generate two matrices
*/
// Get the parent scale values. We need these to scale our width & height
// for the surface.
float xScale, yScale, xTranslate, yTranslate;
parentCTM->GetA(&xScale);
parentCTM->GetD(&yScale);
parentCTM->GetE(&xTranslate);
parentCTM->GetF(&yTranslate);
// Get our Viewbox (if we have one)
nsCOMPtr<nsIDOMSVGRect> viewRect;
GetViewBox(getter_AddRefs(viewRect));
// Construct a CTM for the parent that excludes the translation -- this
// will become the CanvasTM we provide. Note that mCTM must take into
// account the viewBox/Viewport transformations since these can not be
// handled by the renderer's pattern matrix
if (NS_FAILED(ConstructCTM(getter_AddRefs(mCTM), parentCTM, viewRect)))
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMSVGRect> bbox;
callerSVGFrame->GetBBox(getter_AddRefs(bbox));
// We don't want to even try if we have a degenerate path
{
float width, height;
bbox->GetWidth(&width);
bbox->GetHeight(&height);
if (width <= 0 || height <= 0) {
return NS_ERROR_FAILURE;
}
}
if (NS_FAILED(GetPatternRect(getter_AddRefs(mBBox), bbox, callerContent)))
return NS_ERROR_FAILURE;
// Create the new surface
float x, y, width, height;
mBBox->GetWidth(&width);
mBBox->GetHeight(&height);
mBBox->GetX(&x);
mBBox->GetY(&y);
mBBox->SetX(0.0f);
mBBox->SetY(0.0f);
nsISVGOuterSVGFrame* outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this);
if (!outerSVGFrame) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsISVGRenderer> renderer;
outerSVGFrame->GetRenderer(getter_AddRefs(renderer));
nsCOMPtr<nsISVGRendererSurface> patternSurface;
#ifdef DEBUG_scooter
printf("Creating %dX%d surface\n",(int)(xScale*width),(int)(yScale*height));
#endif
renderer->CreateSurface((int)(xScale*width), (int)(yScale*height),
getter_AddRefs(patternSurface));
// Push the surface
if (NS_FAILED(canvas->PushSurface(patternSurface)))
return NS_ERROR_FAILURE; //?
if (NS_FAILED(GetPatternMatrix(patternMatrix, bbox, viewRect)))
return NS_ERROR_FAILURE;
// We really need to have the renderer deal with the pattern offsets, so
// we sort of fudge things and add in our x and y offsets from our bounding
// box
float xTrans, yTrans;
(*patternMatrix)->GetE(&xTrans);
(*patternMatrix)->GetF(&yTrans);
xTrans += xTranslate + x * xScale;
yTrans += yTranslate + y * yScale;
(*patternMatrix)->SetE(xTrans);
(*patternMatrix)->SetF(yTrans);
#ifdef DEBUG_scooter
printRect("Bounding Rect: ",mBBox);
printCTM("Child TM ",*patternMatrix);
#endif
// OK, now render -- note that we use "firstKid", which
// we got at the beginning because it takes care of the
// referenced pattern situation for us
// Set our geometrical parent
mSource = aSource;
nsRect dummyRect;
for (nsIFrame* kid = firstKid; kid;
kid = kid->GetNextSibling()) {
nsISVGChildFrame* SVGFrame=nsnull;
kid->QueryInterface(NS_GET_IID(nsISVGChildFrame),(void**)&SVGFrame);
if (SVGFrame) {
SVGFrame->PaintSVG(canvas, dummyRect, PR_FALSE);
}
}
mSource = nsnull;
// The surface on the top of the stack is the surface
// that was passed to us, and has the pattern cell. We
// pop it off of the stack, but we don't destroy it so
// that the renderer can do what needs to be done to
// use it as a pattern
canvas->PopSurface();
*surface = patternSurface;
NS_IF_ADDREF(*surface);
return NS_OK;
}
/* Will probably need something like this... */
// How do we handle the insertion of a new frame?
// We really don't want to rerender this every time,
// do we?
NS_IMETHODIMP
nsSVGPatternFrame::GetPatternFirstChild(nsIFrame **kid)
{
nsresult rv = NS_OK;
// Do we have any children ourselves?
if ((*kid = mFrames.FirstChild()) == nsnull) {
// No, see if we chain to someone who does
if (checkURITarget())
rv = mNextPattern->GetPatternFirstChild(kid);
else
rv = NS_ERROR_FAILURE; // No children = error
}
mLoopFlag = PR_FALSE;
return rv;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetPatternUnits(PRUint16 *aUnits)
{
if (!mPatternUnits) {
PrivateGetPatternUnits(getter_AddRefs(mPatternUnits));
if (!mPatternUnits)
return NS_ERROR_FAILURE;
NS_ADD_SVGVALUE_OBSERVER(mPatternUnits);
}
mPatternUnits->GetAnimVal(aUnits);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetPatternContentUnits(PRUint16 *aUnits)
{
if (!mPatternContentUnits) {
PrivateGetPatternContentUnits(getter_AddRefs(mPatternContentUnits));
if (!mPatternContentUnits)
return NS_ERROR_FAILURE;
NS_ADD_SVGVALUE_OBSERVER(mPatternContentUnits);
}
mPatternContentUnits->GetAnimVal(aUnits);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetPatternTransform(nsIDOMSVGMatrix **aPatternTransform)
{
*aPatternTransform = nsnull;
nsCOMPtr<nsIDOMSVGAnimatedTransformList> trans;
nsCOMPtr<nsIDOMSVGPatternElement> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (!checkURITarget(nsSVGAtoms::patternTransform)) {
// No, return the values
pattern->GetPatternTransform(getter_AddRefs(trans));
nsCOMPtr<nsIDOMSVGTransformList> lTrans;
trans->GetAnimVal(getter_AddRefs(lTrans));
lTrans->GetConsolidationMatrix(aPatternTransform);
} else {
// Yes, get it from the target
mNextPattern->GetPatternTransform(aPatternTransform);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetViewBox(nsIDOMSVGRect **aViewBox)
{
if (!mViewBox) {
PrivateGetViewBox(getter_AddRefs(mViewBox));
if (!mViewBox)
return NS_ERROR_FAILURE;
NS_ADD_SVGVALUE_OBSERVER(mViewBox);
}
mViewBox->GetAnimVal(aViewBox);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
**aPreserveAspectRatio)
{
if (!mPreserveAspectRatio) {
PrivateGetPreserveAspectRatio(getter_AddRefs(mPreserveAspectRatio));
if (!mPreserveAspectRatio)
return NS_ERROR_FAILURE;
NS_ADD_SVGVALUE_OBSERVER(mPreserveAspectRatio);
}
*aPreserveAspectRatio = mPreserveAspectRatio;
NS_IF_ADDREF(*aPreserveAspectRatio);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetX(float *aX)
{
if (!mX) {
PrivateGetX(getter_AddRefs(mX));
if (!mX)
return NS_ERROR_FAILURE;
NS_ADD_SVGVALUE_OBSERVER(mX);
}
mX->GetValue(aX);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetY(float *aY)
{
if (!mY) {
PrivateGetY(getter_AddRefs(mY));
if (!mY)
return NS_ERROR_FAILURE;
NS_ADD_SVGVALUE_OBSERVER(mY);
}
mY->GetValue(aY);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetWidth(float *aWidth)
{
if (!mWidth) {
PrivateGetWidth(getter_AddRefs(mWidth));
if (!mWidth)
return NS_ERROR_FAILURE;
NS_ADD_SVGVALUE_OBSERVER(mWidth);
}
mWidth->GetValue(aWidth);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetHeight(float *aHeight)
{
if (!mHeight) {
PrivateGetHeight(getter_AddRefs(mHeight));
if (!mHeight)
return NS_ERROR_FAILURE;
NS_ADD_SVGVALUE_OBSERVER(mHeight);
}
mHeight->GetValue(aHeight);
return NS_OK;
}
// -------------------------------------------------------------
// Protected versions of the various "Get" routines. These need
// to be used to allow for the ability to delegate to referenced
// patterns
// -------------------------------------------------------------
NS_IMETHODIMP
nsSVGPatternFrame::PrivateGetPatternUnits(nsIDOMSVGAnimatedEnumeration * *aEnum)
{
nsCOMPtr<nsIDOMSVGPatternElement> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (!checkURITarget(nsSVGAtoms::patternUnits)) {
// No, return the values
pattern->GetPatternUnits(aEnum);
} else {
// Yes, get it from the target
mNextPattern->PrivateGetPatternUnits(aEnum);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::PrivateGetPatternContentUnits(nsIDOMSVGAnimatedEnumeration * *aEnum)
{
nsCOMPtr<nsIDOMSVGPatternElement> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (!checkURITarget(nsSVGAtoms::patternContentUnits)) {
// No, return the values
pattern->GetPatternContentUnits(aEnum);
} else {
// Yes, get it from the target
mNextPattern->PrivateGetPatternContentUnits(aEnum);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
// Private (helper) methods
PRBool
nsSVGPatternFrame::checkURITarget(nsIAtom *attr) {
// Was the attribute explicitly set?
if (mContent->HasAttr(kNameSpaceID_None, attr)) {
// Yes, just return
return PR_FALSE;
}
return checkURITarget();
}
PRBool
nsSVGPatternFrame::checkURITarget(void) {
nsIFrame *nextPattern;
mLoopFlag = PR_TRUE; // Set our loop detection flag
// Have we already figured out the next Pattern?
if (mNextPattern != nsnull) {
return PR_TRUE;
}
// Do we have URI?
if (mNextPatternStr.Length() == 0) {
return PR_FALSE; // No, return the default
}
nsCOMPtr<nsIURI> targetURI;
nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI),
mNextPatternStr, mContent->GetCurrentDoc(), base);
// Note that we are using *our* frame tree for this call,
// otherwise we're going to have to get the PresShell in each call
if (nsSVGUtils::GetReferencedFrame(&nextPattern, targetURI, mContent, GetPresContext()->PresShell()) == NS_OK) {
nsIAtom* frameType = nextPattern->GetType();
if (frameType != nsLayoutAtoms::svgPatternFrame)
return PR_FALSE;
mNextPattern = (nsSVGPatternFrame *)nextPattern;
// Are we looping?
if (mNextPattern->mLoopFlag) {
// Yes, remove the reference and return an error
NS_WARNING("Pattern loop detected!");
CopyUTF8toUTF16("", mNextPatternStr);
mNextPattern = nsnull;
return PR_FALSE;
}
// Add ourselves to the observer list
if (mNextPattern) {
// Can't use the NS_ADD macro here because of nsISupports ambiguity
mNextPattern->AddObserver(this);
}
return PR_TRUE;
}
return PR_FALSE;
}
nsresult
nsSVGPatternFrame::PrivateGetX(nsIDOMSVGLength * *aX)
{
nsCOMPtr<nsIDOMSVGPatternElement> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (checkURITarget(nsSVGAtoms::x)) {
// Yes, get it from the target
mNextPattern->PrivateGetX(aX);
} else {
// No, return the values
nsCOMPtr<nsIDOMSVGAnimatedLength> len;
pattern->GetX(getter_AddRefs(len));
len->GetAnimVal(aX);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
nsresult
nsSVGPatternFrame::PrivateGetY(nsIDOMSVGLength * *aY)
{
nsCOMPtr<nsIDOMSVGPatternElement> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (checkURITarget(nsSVGAtoms::y)) {
// Yes, get it from the target
mNextPattern->PrivateGetY(aY);
} else {
// No, return the values
nsCOMPtr<nsIDOMSVGAnimatedLength> len;
pattern->GetY(getter_AddRefs(len));
len->GetAnimVal(aY);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
nsresult
nsSVGPatternFrame::PrivateGetHeight(nsIDOMSVGLength * *aHeight)
{
nsCOMPtr<nsIDOMSVGPatternElement> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (checkURITarget(nsSVGAtoms::height)) {
// Yes, get it from the target
mNextPattern->PrivateGetHeight(aHeight);
} else {
// No, return the values
nsCOMPtr<nsIDOMSVGAnimatedLength> len;
pattern->GetHeight(getter_AddRefs(len));
len->GetAnimVal(aHeight);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
nsresult
nsSVGPatternFrame::PrivateGetWidth(nsIDOMSVGLength * *aWidth)
{
nsCOMPtr<nsIDOMSVGPatternElement> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (checkURITarget(nsSVGAtoms::width)) {
// Yes, get it from the target
mNextPattern->PrivateGetWidth(aWidth);
} else {
// No, return the values
nsCOMPtr<nsIDOMSVGAnimatedLength> len;
pattern->GetWidth(getter_AddRefs(len));
len->GetAnimVal(aWidth);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
nsresult
nsSVGPatternFrame::PrivateGetViewBox(nsIDOMSVGAnimatedRect * *aViewBox)
{
*aViewBox = nsnull;
nsCOMPtr<nsIDOMSVGFitToViewBox> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (!checkURITarget(nsSVGAtoms::viewBox)) {
// No, return the values
pattern->GetViewBox(aViewBox);
} else {
// Yes, get it from the target
mNextPattern->PrivateGetViewBox(aViewBox);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
nsresult
nsSVGPatternFrame::PrivateGetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio * *aPreserveAspectRatio)
{
*aPreserveAspectRatio = nsnull;
nsCOMPtr<nsIDOMSVGFitToViewBox> pattern = do_QueryInterface(mContent);
NS_ASSERTION(pattern, "Wrong content element (not pattern)");
if (pattern == nsnull) {
return NS_ERROR_FAILURE;
}
// See if we need to get the value from another pattern
if (!checkURITarget(nsSVGAtoms::preserveAspectRatio)) {
// No, return the values
pattern->GetPreserveAspectRatio(aPreserveAspectRatio);
} else {
// Yes, get it from the target
mNextPattern->PrivateGetPreserveAspectRatio(aPreserveAspectRatio);
}
mLoopFlag = PR_FALSE;
return NS_OK;
}
// -------------------------------------------------------------------------
// Helper functions
// -------------------------------------------------------------------------
nsresult
nsSVGPatternFrame::GetPatternRect(nsIDOMSVGRect **patternRect, nsIDOMSVGRect *bbox, nsIContent *content)
{
// Get our type
PRUint16 type;
GetPatternUnits(&type);
// We need to initialize our box
float x,y,width,height,tmp;
if (!mX)
GetX(&tmp);
if (!mY)
GetY(&tmp);
if (!mWidth)
GetWidth(&tmp);
if (!mHeight)
GetHeight(&tmp);
if (type == nsIDOMSVGPatternElement::SVG_PUNITS_OBJECTBOUNDINGBOX) {
bbox->GetX(&x);
x += nsSVGUtils::ObjectSpace(bbox, mX, nsSVGUtils::X);
bbox->GetY(&y);
y += nsSVGUtils::ObjectSpace(bbox, mY, nsSVGUtils::Y);
width = nsSVGUtils::ObjectSpace(bbox, mWidth, nsSVGUtils::X);
height = nsSVGUtils::ObjectSpace(bbox, mHeight, nsSVGUtils::Y);
} else {
x = nsSVGUtils::UserSpace(content, mX, nsSVGUtils::X);
y = nsSVGUtils::UserSpace(content, mY, nsSVGUtils::Y);
width = nsSVGUtils::UserSpace(content, mWidth, nsSVGUtils::X);
height = nsSVGUtils::UserSpace(content, mHeight, nsSVGUtils::Y);
}
return NS_NewSVGRect(patternRect, x, y, width, height);
}
nsresult
nsSVGPatternFrame::ConstructCTM(nsIDOMSVGMatrix **resultCTM, nsIDOMSVGMatrix *aPCTM, nsIDOMSVGRect *aViewRect)
{
// Create a matrix from the parent without any translations
nsCOMPtr<nsIDOMSVGMatrix> tCTM;
nsCOMPtr<nsIDOMSVGMatrix>tempTM;
float a, b, c, d, e, f;
aPCTM->GetA(&a);
aPCTM->GetB(&b);
aPCTM->GetC(&c);
aPCTM->GetD(&d);
aPCTM->GetE(&e);
aPCTM->GetF(&f);
NS_NewSVGMatrix(getter_AddRefs(tCTM),
a, b,
c, d,
0.0f, 0.0f);
// See if we really have something
float viewBoxX, viewBoxY, viewBoxHeight, viewBoxWidth;
aViewRect->GetX(&viewBoxX);
aViewRect->GetY(&viewBoxY);
aViewRect->GetHeight(&viewBoxHeight);
aViewRect->GetWidth(&viewBoxWidth);
if (viewBoxHeight != 0.0f && viewBoxWidth != 0.0f) {
float viewportWidth, viewportHeight;
GetWidth(&viewportWidth);
GetHeight(&viewportHeight);
float refX, refY;
GetX(&refX);
GetY(&refY);
nsCOMPtr<nsIDOMSVGAnimatedPreserveAspectRatio> par;
GetPreserveAspectRatio(getter_AddRefs(par));
tempTM = nsSVGUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
viewBoxX + refX, viewBoxY + refY,
viewBoxWidth, viewBoxHeight,
par,
PR_TRUE);
tempTM->Multiply(tCTM, resultCTM);
} else {
// No viewBox, construct from the (modified) parent matrix
NS_NewSVGMatrix(getter_AddRefs(tempTM),
1.0f, 0.0f,
0.0f, 1.0f,
0.0f, 0.0f);
tempTM->Multiply(tCTM, resultCTM);
}
return NS_OK;
}
nsresult
nsSVGPatternFrame::GetPatternMatrix(nsIDOMSVGMatrix **ctm, nsIDOMSVGRect *bbox, nsIDOMSVGRect *aViewRect)
{
// Determine the new CTM and set it
// this depends on the patternContentUnits & viewBox
nsCOMPtr<nsIDOMSVGMatrix> tCTM;
// Get the pattern transform
nsCOMPtr<nsIDOMSVGMatrix> patternTransform;
GetPatternTransform(getter_AddRefs(patternTransform));
// OK, now that we're dealing with the content units, we want to
// get rid of translations (since we're a pattern). The pattern
// matrix will translate the entire frame for us when we render
// the actual pattern
// See if we really have something
float viewBoxHeight, viewBoxWidth;
aViewRect->GetHeight(&viewBoxHeight);
aViewRect->GetWidth(&viewBoxWidth);
if (viewBoxHeight != 0.0f && viewBoxWidth != 0.0f) {
NS_NewSVGMatrix(getter_AddRefs(tCTM), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
} else {
// Use whatever patternContentUnits tells us
PRUint16 type;
GetPatternContentUnits(&type);
if (type == nsIDOMSVGPatternElement::SVG_PUNITS_OBJECTBOUNDINGBOX) {
// Use the bounding box
float bX,bY,bWidth,bHeight;
bbox->GetX(&bX);
bbox->GetY(&bY);
bbox->GetWidth(&bWidth);
bbox->GetHeight(&bHeight);
NS_NewSVGMatrix(getter_AddRefs(tCTM), bWidth, 0.0f, 0.0f, bHeight, bX, bY);
} else {
NS_NewSVGMatrix(getter_AddRefs(tCTM), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
}
}
tCTM->Multiply(patternTransform, ctm);
return NS_OK;
}
// -------------------------------------------------------------------------
// Public functions
// -------------------------------------------------------------------------
nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
nsIContent* aContent)
{
nsCOMPtr<nsIDOMSVGPatternElement> pattern = do_QueryInterface(aContent);
NS_ASSERTION(pattern,
"NS_NewSVGPatternFrame -- Content doesn't support nsIDOMSVGPattern");
if (!pattern)
return nsnull;
nsSVGPatternFrame* it = new (aPresShell) nsSVGPatternFrame;
if (nsnull == it)
return nsnull;
nsCOMPtr<nsIDOMSVGURIReference> ref = do_QueryInterface(aContent);
NS_ASSERTION(ref,
"NS_NewSVGPatternFrame -- Content doesn't support nsIDOMSVGURIReference");
if (!ref) {
it->mNextPattern = nsnull;
} else {
// Get the hRef
nsCOMPtr<nsIDOMSVGAnimatedString> hRef;
ref->GetHref(getter_AddRefs(hRef));
nsAutoString str;
hRef->GetAnimVal(str);
it->mNextPatternStr = str;
it->mNextPattern = nsnull;
#ifdef DEBUG_scooter
printf("NS_NewSVGPatternFrame\n");
#endif
}
return it;
}
// Public function to locate the SVGPatternFrame structure pointed to by a URI
// and return a nsISVGPattern
nsresult NS_GetSVGPattern(nsISVGPattern **aPattern, nsIURI *aURI,
nsIContent *aContent,
nsIPresShell *aPresShell)
{
*aPattern = nsnull;
#ifdef DEBUG_scooter
nsCAutoString uriSpec;
aURI->GetSpec(uriSpec);
printf("NS_GetSVGPattern: uri = %s\n",uriSpec.get());
#endif
nsIFrame *result;
if (!NS_SUCCEEDED(nsSVGUtils::GetReferencedFrame(&result, aURI,
aContent, aPresShell)))
return NS_ERROR_FAILURE;
#ifdef DEBUG_scooter
const char *contentString;
nsIAtom *tag = aContent->Tag();
tag->GetUTF8String(&contentString);
printf("NS_GetSVGPattern: checking content -- caller content tag is %s, ",
contentString);
tag = result->GetContent()->Tag();
tag->GetUTF8String(&contentString);
printf("target content tag is %s\n",
contentString);
#endif
// Check to see if we are referencing an ancestor (*not* a good thing)
if (result && nsContentUtils::ContentIsDescendantOf(aContent, result->GetContent())) {
NS_WARNING("Pattern child references pattern!");
return NS_ERROR_FAILURE;
}
return result->QueryInterface(NS_GET_IID(nsISVGPattern), (void **)aPattern);
}
#ifdef DEBUG_scooter
static void printCTM(char *msg, nsIDOMSVGMatrix *aCTM)
{
float a,b,c,d,e,f;
aCTM->GetA(&a);
aCTM->GetB(&b);
aCTM->GetC(&c);
aCTM->GetD(&d);
aCTM->GetE(&e);
aCTM->GetF(&f);
printf("%s {%f,%f,%f,%f,%f,%f}\n",msg,a,b,c,d,e,f);
}
static void printRect(char *msg, nsIDOMSVGRect *aRect)
{
float x,y,width,height;
aRect->GetX(&x);
aRect->GetY(&y);
aRect->GetWidth(&width);
aRect->GetHeight(&height);
printf("%s {%f,%f,%f,%f}\n",msg,x,y,width,height);
}
#endif