Back to index

lightning-sunbird  0.9+nobinonly
nsSVGClipPathFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Mozilla SVG project.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2004
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "nsIDOMSVGTransformable.h"
00038 #include "nsIDOMDocument.h"
00039 #include "nsIDocument.h"
00040 #include "nsIDOMSVGClipPathElement.h"
00041 #include "nsSVGClipPathFrame.h"
00042 #include "nsISVGRendererCanvas.h"
00043 #include "nsIDOMSVGTransformList.h"
00044 #include "nsSVGAnimatedTransformList.h"
00045 #include "nsIDOMSVGAnimatedEnum.h"
00046 #include "nsSVGUtils.h"
00047 
00048 NS_IMETHODIMP_(nsrefcnt)
00049 nsSVGClipPathFrame::AddRef()
00050 {
00051   return NS_OK;
00052 }
00053 
00054 NS_IMETHODIMP_(nsrefcnt)
00055 nsSVGClipPathFrame::Release()
00056 {
00057   return NS_OK;
00058 }
00059 
00060 NS_IMETHODIMP
00061 nsSVGClipPathFrame::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00062 {
00063   if (nsnull == aInstancePtr) {
00064     return NS_ERROR_NULL_POINTER;
00065   }
00066   if (aIID.Equals(nsSVGClipPathFrame::GetCID())) {
00067     *aInstancePtr = (void*)(nsSVGClipPathFrame*)this;
00068     NS_ADDREF_THIS();
00069     return NS_OK;
00070   }
00071   return (nsSVGDefsFrame::QueryInterface(aIID, aInstancePtr));
00072 }
00073 
00074 //----------------------------------------------------------------------
00075 // Implementation
00076 
00077 nsresult
00078 NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame** aNewFrame)
00079 {
00080   *aNewFrame = nsnull;
00081   
00082   nsCOMPtr<nsIDOMSVGTransformable> transformable = do_QueryInterface(aContent);
00083   if (!transformable) {
00084 #ifdef DEBUG
00085     printf("warning: trying to construct an SVGClipPathFrame for a content element that doesn't support the right interfaces\n");
00086 #endif
00087     return NS_ERROR_FAILURE;
00088   }
00089   
00090   nsSVGClipPathFrame* it = new (aPresShell) nsSVGClipPathFrame;
00091   if (nsnull == it)
00092     return NS_ERROR_OUT_OF_MEMORY;
00093 
00094   *aNewFrame = it;
00095 
00096   return NS_OK;
00097 }
00098 
00099 nsresult
00100 NS_GetSVGClipPathFrame(nsSVGClipPathFrame **aResult, nsIURI *aURI, nsIContent *aContent)
00101 {
00102   *aResult = nsnull;
00103 
00104   // Get the PresShell
00105   nsIDocument *myDoc = aContent->GetCurrentDoc();
00106   if (!myDoc) {
00107     NS_WARNING("No document for this content!");
00108     return NS_ERROR_FAILURE;
00109   }
00110   nsIPresShell *aPresShell = myDoc->GetShellAt(0);
00111 
00112   // Get the URI Spec
00113   nsCAutoString uriSpec;
00114   aURI->GetSpec(uriSpec);
00115 
00116   // Find the referenced frame
00117   nsIFrame *cpframe;
00118   if (!NS_SUCCEEDED(nsSVGUtils::GetReferencedFrame(&cpframe, 
00119                                                    uriSpec, aContent, aPresShell)))
00120     return NS_ERROR_FAILURE;
00121 
00122   nsIAtom* frameType = cpframe->GetType();
00123   if (frameType != nsLayoutAtoms::svgClipPathFrame)
00124     return NS_ERROR_FAILURE;
00125 
00126   *aResult = (nsSVGClipPathFrame *)cpframe;
00127   return NS_OK;
00128 }
00129 
00130 nsSVGClipPathFrame::~nsSVGClipPathFrame()
00131 {
00132 }
00133 
00134 NS_IMETHODIMP
00135 nsSVGClipPathFrame::InitSVG()
00136 {
00137   nsresult rv = nsSVGDefsFrame::InitSVG();
00138   if (NS_FAILED(rv))
00139     return rv;
00140 
00141   mClipParentMatrix = NULL;
00142 
00143   return NS_OK;
00144 }
00145 
00146 NS_IMETHODIMP
00147 nsSVGClipPathFrame::ClipPaint(nsISVGRendererCanvas* canvas,
00148                               nsISVGChildFrame* aParent,
00149                               nsCOMPtr<nsIDOMSVGMatrix> aMatrix)
00150 {
00151   // If the flag is set when we get here, it means this clipPath frame
00152   // has already been used painting the current clip, and the document
00153   // has a clip reference loop.
00154   if (mInUse) {
00155     NS_WARNING("Clip loop detected!");
00156     return NS_OK;
00157   }
00158   mInUse = PR_TRUE;
00159 
00160   nsRect dirty;
00161   nsresult rv;
00162 
00163   mClipParent = aParent,
00164   mClipParentMatrix = aMatrix;
00165 
00166   NotifyCanvasTMChanged();
00167 
00168   rv = canvas->SetRenderMode(nsISVGRendererCanvas::SVG_RENDER_MODE_CLIP);
00169   if (NS_FAILED(rv))
00170     return NS_ERROR_FAILURE;
00171 
00172   for (nsIFrame* kid = mFrames.FirstChild(); kid;
00173        kid = kid->GetNextSibling()) {
00174     nsISVGChildFrame* SVGFrame=nsnull;
00175     kid->QueryInterface(NS_GET_IID(nsISVGChildFrame),(void**)&SVGFrame);
00176     if (SVGFrame) {
00177       SVGFrame->PaintSVG(canvas, dirty);
00178     }
00179   }
00180 
00181   canvas->SetRenderMode(nsISVGRendererCanvas::SVG_RENDER_MODE_NORMAL);
00182 
00183   mInUse = PR_FALSE;
00184 
00185   return NS_OK;
00186 }
00187 
00188 NS_IMETHODIMP
00189 nsSVGClipPathFrame::ClipHitTest(nsISVGChildFrame* aParent,
00190                                 nsCOMPtr<nsIDOMSVGMatrix> aMatrix,
00191                                 float aX, float aY, PRBool *aHit)
00192 {
00193   *aHit = PR_FALSE;
00194 
00195   // If the flag is set when we get here, it means this clipPath frame
00196   // has already been used in hit testing against the current clip,
00197   // and the document has a clip reference loop.
00198   if (mInUse) {
00199     NS_WARNING("Clip loop detected!");
00200     return NS_OK;
00201   }
00202   mInUse = PR_TRUE;
00203 
00204   nsRect dirty;
00205   mClipParent = aParent,
00206   mClipParentMatrix = aMatrix;
00207 
00208   for (nsIFrame* kid = mFrames.FirstChild(); kid;
00209        kid = kid->GetNextSibling()) {
00210     nsISVGChildFrame* SVGFrame=nsnull;
00211     kid->QueryInterface(NS_GET_IID(nsISVGChildFrame),(void**)&SVGFrame);
00212     if (SVGFrame) {
00213       // Notify the child frame that we may be working with a
00214       // different transform, so it can update its covered region
00215       // (used to shortcut hit testing).
00216       SVGFrame->NotifyCanvasTMChanged();
00217 
00218       nsIFrame *temp = nsnull;
00219       nsresult rv = SVGFrame->GetFrameForPointSVG(aX, aY, &temp);
00220       if (NS_SUCCEEDED(rv) && temp) {
00221         *aHit = PR_TRUE;
00222         mInUse = PR_FALSE;
00223         return NS_OK;
00224       }
00225     }
00226   }
00227 
00228   mInUse = PR_FALSE;
00229   return NS_OK;
00230 }
00231 
00232 nsIAtom *
00233 nsSVGClipPathFrame::GetType() const
00234 {
00235   return nsLayoutAtoms::svgClipPathFrame;
00236 }
00237 
00238 already_AddRefed<nsIDOMSVGMatrix>
00239 nsSVGClipPathFrame::GetCanvasTM()
00240 {
00241   // startup cycle
00242   if (!mClipParentMatrix) {
00243     NS_ASSERTION(mParent, "null parent");
00244     nsISVGContainerFrame *containerFrame;
00245     mParent->QueryInterface(NS_GET_IID(nsISVGContainerFrame), (void**)&containerFrame);
00246     if (!containerFrame) {
00247       NS_ERROR("invalid parent");
00248       return nsnull;
00249     }
00250     mClipParentMatrix = containerFrame->GetCanvasTM();
00251   }
00252 
00253   nsCOMPtr<nsIDOMSVGMatrix> localTM;
00254   {
00255     nsCOMPtr<nsIDOMSVGTransformable> transformable = do_QueryInterface(mContent);
00256     NS_ASSERTION(transformable, "wrong content element");
00257     nsCOMPtr<nsIDOMSVGAnimatedTransformList> atl;
00258     transformable->GetTransform(getter_AddRefs(atl));
00259     NS_ASSERTION(atl, "null animated transform list");
00260     nsCOMPtr<nsIDOMSVGTransformList> transforms;
00261     atl->GetAnimVal(getter_AddRefs(transforms));
00262     NS_ASSERTION(transforms, "null transform list");
00263     PRUint32 numberOfItems;
00264     transforms->GetNumberOfItems(&numberOfItems);
00265     if (numberOfItems>0)
00266       transforms->GetConsolidationMatrix(getter_AddRefs(localTM));
00267   }
00268   
00269   nsCOMPtr<nsIDOMSVGMatrix> canvasTM;
00270 
00271   if (localTM)
00272     mClipParentMatrix->Multiply(localTM, getter_AddRefs(canvasTM));
00273   else
00274     canvasTM = mClipParentMatrix;
00275 
00276   /* object bounding box? */
00277   PRUint16 units;
00278   nsCOMPtr<nsIDOMSVGClipPathElement> path = do_QueryInterface(mContent);
00279   nsCOMPtr<nsIDOMSVGAnimatedEnumeration> aEnum;
00280   path->GetClipPathUnits(getter_AddRefs(aEnum));
00281   aEnum->GetAnimVal(&units);
00282   
00283   if (mClipParent &&
00284       units == nsIDOMSVGClipPathElement::SVG_CPUNITS_OBJECTBOUNDINGBOX) {
00285     nsCOMPtr<nsIDOMSVGRect> rect;
00286     nsresult rv = mClipParent->GetBBox(getter_AddRefs(rect));
00287 
00288     if (NS_SUCCEEDED(rv)) {
00289       float minx, miny, width, height;
00290       rect->GetX(&minx);
00291       rect->GetY(&miny);
00292       rect->GetWidth(&width);
00293       rect->GetHeight(&height);
00294 
00295       nsCOMPtr<nsIDOMSVGMatrix> tmp, fini;
00296       canvasTM->Translate(minx, miny, getter_AddRefs(tmp));
00297       tmp->ScaleNonUniform(width, height, getter_AddRefs(fini));
00298       canvasTM = fini;
00299     }
00300   }
00301 
00302   nsIDOMSVGMatrix* retval = canvasTM.get();
00303   NS_IF_ADDREF(retval);
00304   return retval;
00305 }