LCOV - code coverage report
Current view: directory - layout/svg/base/src - nsSVGFilterFrame.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 122 0 0.0 %
Date: 2012-06-02 Functions: 15 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is the Mozilla SVG project.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is IBM Corporation.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2005
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *
      23                 :  * Alternatively, the contents of this file may be used under the terms of
      24                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      25                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      26                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      27                 :  * of those above. If you wish to allow use of your version of this file only
      28                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      29                 :  * use your version of this file under the terms of the MPL, indicate your
      30                 :  * decision by deleting the provisions above and replace them with the notice
      31                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      32                 :  * the provisions above, a recipient may use your version of this file under
      33                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      34                 :  *
      35                 :  * ***** END LICENSE BLOCK ***** */
      36                 : 
      37                 : #include "nsSVGFilterFrame.h"
      38                 : #include "nsIDocument.h"
      39                 : #include "nsSVGOuterSVGFrame.h"
      40                 : #include "nsGkAtoms.h"
      41                 : #include "nsSVGEffects.h"
      42                 : #include "nsSVGUtils.h"
      43                 : #include "nsSVGFilterElement.h"
      44                 : #include "nsSVGFilters.h"
      45                 : #include "gfxASurface.h"
      46                 : #include "gfxContext.h"
      47                 : #include "gfxImageSurface.h"
      48                 : #include "nsSVGFilterPaintCallback.h"
      49                 : #include "nsSVGRect.h"
      50                 : #include "nsSVGFilterInstance.h"
      51                 : #include "gfxUtils.h"
      52                 : 
      53                 : nsIFrame*
      54               0 : NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
      55                 : {
      56               0 :   return new (aPresShell) nsSVGFilterFrame(aContext);
      57                 : }
      58                 : 
      59               0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame)
      60                 : 
      61                 : static nsIntRect
      62               0 : MapDeviceRectToFilterSpace(const gfxMatrix& aMatrix,
      63                 :                            const gfxIntSize& aFilterSize,
      64                 :                            const nsIntRect* aDeviceRect)
      65                 : {
      66               0 :   nsIntRect rect(0, 0, aFilterSize.width, aFilterSize.height);
      67               0 :   if (aDeviceRect) {
      68                 :     gfxRect r = aMatrix.TransformBounds(gfxRect(aDeviceRect->x, aDeviceRect->y,
      69               0 :                                                 aDeviceRect->width, aDeviceRect->height));
      70               0 :     r.RoundOut();
      71               0 :     nsIntRect intRect;
      72               0 :     if (gfxUtils::GfxRectToIntRect(r, &intRect)) {
      73               0 :       rect = intRect;
      74                 :     }
      75                 :   }
      76                 :   return rect;
      77                 : }
      78                 : 
      79                 : class NS_STACK_CLASS nsAutoFilterInstance {
      80                 : public:
      81                 :   nsAutoFilterInstance(nsIFrame *aTarget,
      82                 :                        nsSVGFilterFrame *aFilterFrame,
      83                 :                        nsSVGFilterPaintCallback *aPaint,
      84                 :                        const nsIntRect *aDirtyOutputRect,
      85                 :                        const nsIntRect *aDirtyInputRect,
      86                 :                        const nsIntRect *aOverrideSourceBBox);
      87                 :   ~nsAutoFilterInstance();
      88                 : 
      89                 :   // If this returns null, then draw nothing. Either the filter draws
      90                 :   // nothing or it is "in error".
      91               0 :   nsSVGFilterInstance* get() { return mInstance; }
      92                 : 
      93                 : private:
      94                 :   nsAutoPtr<nsSVGFilterInstance> mInstance;
      95                 :   // Store mTarget separately even though mInstance has it, because if
      96                 :   // mInstance creation fails we still need to be able to clean up
      97                 :   nsISVGChildFrame*              mTarget;
      98                 : };
      99                 : 
     100               0 : nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
     101                 :                                            nsSVGFilterFrame *aFilterFrame,
     102                 :                                            nsSVGFilterPaintCallback *aPaint,
     103                 :                                            const nsIntRect *aDirtyOutputRect,
     104                 :                                            const nsIntRect *aDirtyInputRect,
     105               0 :                                            const nsIntRect *aOverrideSourceBBox)
     106                 : {
     107               0 :   mTarget = do_QueryFrame(aTarget);
     108                 : 
     109                 :   nsSVGFilterElement *filter =
     110               0 :     static_cast<nsSVGFilterElement*>(aFilterFrame->GetContent());
     111                 : 
     112                 :   PRUint16 filterUnits =
     113               0 :     filter->mEnumAttributes[nsSVGFilterElement::FILTERUNITS].GetAnimValue();
     114                 :   PRUint16 primitiveUnits =
     115               0 :     filter->mEnumAttributes[nsSVGFilterElement::PRIMITIVEUNITS].GetAnimValue();
     116                 : 
     117               0 :   gfxRect bbox;
     118               0 :   if (aOverrideSourceBBox) {
     119                 :     bbox = gfxRect(aOverrideSourceBBox->x, aOverrideSourceBBox->y,
     120               0 :                    aOverrideSourceBBox->width, aOverrideSourceBBox->height);
     121                 :   } else {
     122               0 :     bbox = nsSVGUtils::GetBBox(aTarget);
     123                 :   }
     124                 : 
     125                 :   // Get the filter region (in the filtered element's user space):
     126                 : 
     127                 :   // XXX if filterUnits is set (or has defaulted) to objectBoundingBox, we
     128                 :   // should send a warning to the error console if the author has used lengths
     129                 :   // with units. This is a common mistake and can result in filterRes being
     130                 :   // *massive* below (because we ignore the units and interpret the number as
     131                 :   // a factor of the bbox width/height). We should also send a warning if the
     132                 :   // user uses a number without units (a future SVG spec should really
     133                 :   // deprecate that, since it's too confusing for a bare number to be sometimes
     134                 :   // interpreted as a fraction of the bounding box and sometimes as user-space
     135                 :   // units). So really only percentage values should be used in this case.
     136                 :   
     137                 :   gfxRect filterRegion = nsSVGUtils::GetRelativeRect(filterUnits,
     138               0 :     filter->mLengthAttributes, bbox, aTarget);
     139                 : 
     140               0 :   if (filterRegion.Width() <= 0 || filterRegion.Height() <= 0) {
     141                 :     // 0 disables rendering, < 0 is error. dispatch error console warning
     142                 :     // or error as appropriate.
     143               0 :     return;
     144                 :   }
     145                 : 
     146               0 :   gfxMatrix userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
     147               0 :   if (userToDeviceSpace.IsSingular()) {
     148                 :     // nothing to draw
     149               0 :     return;
     150                 :   }
     151                 :   
     152                 :   // Calculate filterRes (the width and height of the pixel buffer of the
     153                 :   // temporary offscreen surface that we'll paint into):
     154                 : 
     155               0 :   gfxIntSize filterRes;
     156                 :   const nsSVGIntegerPair& filterResAttrs =
     157               0 :     filter->mIntegerPairAttributes[nsSVGFilterElement::FILTERRES];
     158               0 :   if (filterResAttrs.IsExplicitlySet()) {
     159               0 :     PRInt32 filterResX = filterResAttrs.GetAnimValue(nsSVGIntegerPair::eFirst);
     160               0 :     PRInt32 filterResY = filterResAttrs.GetAnimValue(nsSVGIntegerPair::eSecond);
     161               0 :     if (filterResX <= 0 || filterResY <= 0) {
     162                 :       // 0 disables rendering, < 0 is error. dispatch error console warning?
     163               0 :       return;
     164                 :     }
     165                 : 
     166               0 :     filterRegion.Scale(filterResX, filterResY);
     167               0 :     filterRegion.RoundOut();
     168               0 :     filterRegion.Scale(1.0 / filterResX, 1.0 / filterResY);
     169                 :     // We don't care if this overflows, because we can handle upscaling/
     170                 :     // downscaling to filterRes
     171                 :     bool overflow;
     172                 :     filterRes =
     173                 :       nsSVGUtils::ConvertToSurfaceSize(gfxSize(filterResX, filterResY),
     174               0 :                                        &overflow);
     175                 :     // XXX we could send a warning to the error console if the author specified
     176                 :     // filterRes doesn't align well with our outer 'svg' device space.
     177                 :   } else {
     178                 :     // Match filterRes as closely as possible to the pixel density of the nearest
     179                 :     // outer 'svg' device space:
     180               0 :     float scale = nsSVGUtils::MaxExpansion(userToDeviceSpace);
     181                 : 
     182               0 :     filterRegion.Scale(scale);
     183               0 :     filterRegion.RoundOut();
     184                 :     // We don't care if this overflows, because we can handle upscaling/
     185                 :     // downscaling to filterRes
     186                 :     bool overflow;
     187                 :     filterRes = nsSVGUtils::ConvertToSurfaceSize(filterRegion.Size(),
     188               0 :                                                  &overflow);
     189               0 :     filterRegion.Scale(1.0 / scale);
     190                 :   }
     191                 : 
     192                 :   // XXX we haven't taken account of the fact that filterRegion may be
     193                 :   // partially or entirely outside the current clip region. :-/
     194                 : 
     195                 :   // Convert the dirty rects to filter space, and create our nsSVGFilterInstance:
     196                 : 
     197               0 :   gfxMatrix filterToUserSpace(filterRegion.Width() / filterRes.width, 0.0f,
     198               0 :                               0.0f, filterRegion.Height() / filterRes.height,
     199               0 :                               filterRegion.X(), filterRegion.Y());
     200               0 :   gfxMatrix filterToDeviceSpace = filterToUserSpace * userToDeviceSpace;
     201                 :   
     202                 :   // filterToDeviceSpace is always invertible
     203               0 :   gfxMatrix deviceToFilterSpace = filterToDeviceSpace;
     204               0 :   deviceToFilterSpace.Invert();
     205                 : 
     206                 :   nsIntRect dirtyOutputRect =
     207               0 :     MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aDirtyOutputRect);
     208                 :   nsIntRect dirtyInputRect =
     209               0 :     MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aDirtyInputRect);
     210               0 :   nsIntRect targetBoundsDeviceSpace;
     211               0 :   nsISVGChildFrame* svgTarget = do_QueryFrame(aTarget);
     212               0 :   if (svgTarget) {
     213                 :     targetBoundsDeviceSpace.UnionRect(targetBoundsDeviceSpace,
     214               0 :       svgTarget->GetCoveredRegion().ToOutsidePixels(aTarget->PresContext()->AppUnitsPerDevPixel()));
     215                 :   }
     216                 :   nsIntRect targetBoundsFilterSpace =
     217               0 :     MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, &targetBoundsDeviceSpace);
     218                 : 
     219                 :   // Setup instance data
     220                 :   mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterRegion,
     221                 :                                       nsIntSize(filterRes.width, filterRes.height),
     222                 :                                       filterToDeviceSpace, targetBoundsFilterSpace,
     223                 :                                       dirtyOutputRect, dirtyInputRect,
     224               0 :                                       primitiveUnits);
     225                 : }
     226                 : 
     227               0 : nsAutoFilterInstance::~nsAutoFilterInstance()
     228                 : {
     229               0 : }
     230                 : 
     231                 : NS_IMETHODIMP
     232               0 : nsSVGFilterFrame::AttributeChanged(PRInt32  aNameSpaceID,
     233                 :                                    nsIAtom* aAttribute,
     234                 :                                    PRInt32  aModType)
     235                 : {
     236               0 :   if ((aNameSpaceID == kNameSpaceID_None &&
     237                 :        (aAttribute == nsGkAtoms::x ||
     238                 :         aAttribute == nsGkAtoms::y ||
     239                 :         aAttribute == nsGkAtoms::width ||
     240                 :         aAttribute == nsGkAtoms::height ||
     241                 :         aAttribute == nsGkAtoms::filterRes ||
     242                 :         aAttribute == nsGkAtoms::filterUnits ||
     243                 :         aAttribute == nsGkAtoms::primitiveUnits)) ||
     244                 :        (aNameSpaceID == kNameSpaceID_XLink &&
     245                 :         aAttribute == nsGkAtoms::href)) {
     246               0 :     nsSVGEffects::InvalidateRenderingObservers(this);
     247                 :   }
     248                 :   return nsSVGFilterFrameBase::AttributeChanged(aNameSpaceID,
     249               0 :                                                 aAttribute, aModType);
     250                 : }
     251                 : 
     252                 : nsresult
     253               0 : nsSVGFilterFrame::FilterPaint(nsRenderingContext *aContext,
     254                 :                               nsIFrame *aTarget,
     255                 :                               nsSVGFilterPaintCallback *aPaintCallback,
     256                 :                               const nsIntRect *aDirtyRect)
     257                 : {
     258                 :   nsAutoFilterInstance instance(aTarget, this, aPaintCallback,
     259               0 :     aDirtyRect, nsnull, nsnull);
     260               0 :   if (!instance.get())
     261               0 :     return NS_OK;
     262                 : 
     263               0 :   nsRefPtr<gfxASurface> result;
     264               0 :   nsresult rv = instance.get()->Render(getter_AddRefs(result));
     265               0 :   if (NS_SUCCEEDED(rv) && result) {
     266                 :     nsSVGUtils::CompositeSurfaceMatrix(aContext->ThebesContext(),
     267               0 :       result, instance.get()->GetFilterSpaceToDeviceSpaceTransform(), 1.0);
     268                 :   }
     269               0 :   return rv;
     270                 : }
     271                 : 
     272                 : static nsresult
     273               0 : TransformFilterSpaceToDeviceSpace(nsSVGFilterInstance *aInstance, nsIntRect *aRect)
     274                 : {
     275               0 :   gfxMatrix m = aInstance->GetFilterSpaceToDeviceSpaceTransform();
     276               0 :   gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
     277               0 :   r = m.TransformBounds(r);
     278               0 :   r.RoundOut();
     279               0 :   nsIntRect deviceRect;
     280               0 :   if (!gfxUtils::GfxRectToIntRect(r, &deviceRect))
     281               0 :     return NS_ERROR_FAILURE;
     282               0 :   *aRect = deviceRect;
     283               0 :   return NS_OK;
     284                 : }
     285                 : 
     286                 : nsIntRect
     287               0 : nsSVGFilterFrame::GetInvalidationBBox(nsIFrame *aTarget, const nsIntRect& aRect)
     288                 : {
     289               0 :   nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, &aRect, nsnull);
     290               0 :   if (!instance.get())
     291               0 :     return nsIntRect();
     292                 : 
     293                 :   // We've passed in the source's dirty area so the instance knows about it.
     294                 :   // Now we can ask the instance to compute the area of the filter output
     295                 :   // that's dirty.
     296               0 :   nsIntRect dirtyRect;
     297               0 :   nsresult rv = instance.get()->ComputeOutputDirtyRect(&dirtyRect);
     298               0 :   if (NS_SUCCEEDED(rv)) {
     299               0 :     rv = TransformFilterSpaceToDeviceSpace(instance.get(), &dirtyRect);
     300               0 :     if (NS_SUCCEEDED(rv))
     301               0 :       return dirtyRect;
     302                 :   }
     303                 : 
     304               0 :   return nsIntRect();
     305                 : }
     306                 : 
     307                 : nsIntRect
     308               0 : nsSVGFilterFrame::GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aRect)
     309                 : {
     310               0 :   nsAutoFilterInstance instance(aTarget, this, nsnull, &aRect, nsnull, nsnull);
     311               0 :   if (!instance.get())
     312               0 :     return nsIntRect();
     313                 : 
     314                 :   // Now we can ask the instance to compute the area of the source
     315                 :   // that's needed.
     316               0 :   nsIntRect neededRect;
     317               0 :   nsresult rv = instance.get()->ComputeSourceNeededRect(&neededRect);
     318               0 :   if (NS_SUCCEEDED(rv)) {
     319               0 :     rv = TransformFilterSpaceToDeviceSpace(instance.get(), &neededRect);
     320               0 :     if (NS_SUCCEEDED(rv))
     321               0 :       return neededRect;
     322                 :   }
     323                 : 
     324               0 :   return nsIntRect();
     325                 : }
     326                 : 
     327                 : nsIntRect
     328               0 : nsSVGFilterFrame::GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox)
     329                 : {
     330               0 :   nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, nsnull, aSourceBBox);
     331               0 :   if (!instance.get())
     332               0 :     return nsIntRect();
     333                 : 
     334                 :   // We've passed in the source's bounding box so the instance knows about
     335                 :   // it. Now we can ask the instance to compute the bounding box of
     336                 :   // the filter output.
     337               0 :   nsIntRect bbox;
     338               0 :   nsresult rv = instance.get()->ComputeOutputBBox(&bbox);
     339               0 :   if (NS_SUCCEEDED(rv)) {
     340               0 :     rv = TransformFilterSpaceToDeviceSpace(instance.get(), &bbox);
     341               0 :     if (NS_SUCCEEDED(rv))
     342               0 :       return bbox;
     343                 :   }
     344                 :   
     345               0 :   return nsIntRect();
     346                 : }
     347                 :   
     348                 : #ifdef DEBUG
     349                 : NS_IMETHODIMP
     350               0 : nsSVGFilterFrame::Init(nsIContent* aContent,
     351                 :                        nsIFrame* aParent,
     352                 :                        nsIFrame* aPrevInFlow)
     353                 : {
     354               0 :   nsCOMPtr<nsIDOMSVGFilterElement> filter = do_QueryInterface(aContent);
     355               0 :   NS_ASSERTION(filter, "Content is not an SVG filter");
     356                 : 
     357               0 :   return nsSVGFilterFrameBase::Init(aContent, aParent, aPrevInFlow);
     358                 : }
     359                 : #endif /* DEBUG */
     360                 : 
     361                 : nsIAtom *
     362               0 : nsSVGFilterFrame::GetType() const
     363                 : {
     364               0 :   return nsGkAtoms::svgFilterFrame;
     365                 : }

Generated by: LCOV version 1.7