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
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/
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
15 * The Original Code is the Mozilla SVG project.
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.
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.
35 * ***** END LICENSE BLOCK ***** */
37 #include "nsSVGFilterFrame.h"
38 #include "nsIDocument.h"
39 #include "nsISVGValueUtils.h"
40 #include "nsSVGMatrix.h"
41 #include "nsSVGOuterSVGFrame.h"
42 #include "nsGkAtoms.h"
43 #include "nsSVGUtils.h"
44 #include "nsSVGFilterElement.h"
45 #include "nsSVGFilters.h"
46 #include "gfxASurface.h"
47 #include "gfxContext.h"
48 #include "gfxImageSurface.h"
49 #include "nsSVGFilterPaintCallback.h"
50 #include "nsSVGRect.h"
51 #include "nsSVGFilterInstance.h"
54 NS_NewSVGFilterFrame(nsIPresShell
* aPresShell
, nsIContent
* aContent
, nsStyleContext
* aContext
)
56 nsCOMPtr
<nsIDOMSVGFilterElement
> filter
= do_QueryInterface(aContent
);
58 NS_ERROR("Can't create frame! Content is not an SVG filter");
62 return new (aPresShell
) nsSVGFilterFrame(aContext
);
66 MapDeviceRectToFilterSpace(const gfxMatrix
& aMatrix
,
67 const gfxIntSize
& aFilterSize
,
68 const nsIntRect
* aDeviceRect
)
70 nsIntRect
rect(0, 0, aFilterSize
.width
, aFilterSize
.height
);
72 gfxRect r
= aMatrix
.TransformBounds(gfxRect(aDeviceRect
->x
, aDeviceRect
->y
,
73 aDeviceRect
->width
, aDeviceRect
->height
));
76 if (NS_SUCCEEDED(nsSVGUtils::GfxRectToIntRect(r
, &intRect
))) {
83 class NS_STACK_CLASS nsAutoFilterInstance
{
85 nsAutoFilterInstance(nsIFrame
*aTarget
,
86 nsSVGFilterFrame
*aFilterFrame
,
87 nsSVGFilterPaintCallback
*aPaint
,
88 const nsIntRect
*aDirtyOutputRect
,
89 const nsIntRect
*aDirtyInputRect
,
90 const nsIntRect
*aOverrideSourceBBox
);
91 ~nsAutoFilterInstance();
93 // If this returns null, then draw nothing. Either the filter draws
94 // nothing or it is "in error".
95 nsSVGFilterInstance
* get() { return mInstance
; }
98 nsAutoPtr
<nsSVGFilterInstance
> mInstance
;
99 // Store mTarget separately even though mInstance has it, because if
100 // mInstance creation fails we still need to be able to clean up
101 nsISVGChildFrame
* mTarget
;
104 nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame
*aTarget
,
105 nsSVGFilterFrame
*aFilterFrame
,
106 nsSVGFilterPaintCallback
*aPaint
,
107 const nsIntRect
*aDirtyOutputRect
,
108 const nsIntRect
*aDirtyInputRect
,
109 const nsIntRect
*aOverrideSourceBBox
)
111 nsCOMPtr
<nsIDOMSVGMatrix
> ctm
= nsSVGUtils::GetCanvasTM(aTarget
);
113 CallQueryInterface(aTarget
, &mTarget
);
115 mTarget
->SetMatrixPropagation(PR_FALSE
);
116 mTarget
->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION
|
117 nsISVGChildFrame::TRANSFORM_CHANGED
);
120 nsSVGFilterElement
*filter
= static_cast<nsSVGFilterElement
*>(
121 aFilterFrame
->GetContent());
124 filter
->mEnumAttributes
[nsSVGFilterElement::FILTERUNITS
].GetAnimValue();
125 PRUint16 primitiveUnits
=
126 filter
->mEnumAttributes
[nsSVGFilterElement::PRIMITIVEUNITS
].GetAnimValue();
127 nsCOMPtr
<nsIDOMSVGRect
> bbox
;
128 if (aOverrideSourceBBox
) {
129 NS_NewSVGRect(getter_AddRefs(bbox
),
130 aOverrideSourceBBox
->x
, aOverrideSourceBBox
->y
,
131 aOverrideSourceBBox
->width
, aOverrideSourceBBox
->height
);
133 bbox
= nsSVGUtils::GetBBox(aTarget
);
135 if (!bbox
&& (units
== nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
||
136 primitiveUnits
== nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
))
139 gfxRect filterArea
= nsSVGUtils::GetRelativeRect(units
,
140 &filter
->mLengthAttributes
[nsSVGFilterElement::X
], bbox
, aTarget
);
141 filterArea
.RoundOut();
143 PRBool resultOverflows
;
144 gfxIntSize filterRes
;
146 // Compute size of filter buffer
147 if (filter
->HasAttr(kNameSpaceID_None
, nsGkAtoms::filterRes
)) {
148 PRInt32 filterResX
, filterResY
;
149 filter
->GetAnimatedIntegerValues(&filterResX
, &filterResY
, nsnull
);
152 nsSVGUtils::ConvertToSurfaceSize(gfxSize(filterResX
, filterResY
),
155 float scale
= nsSVGUtils::MaxExpansion(ctm
);
157 fprintf(stderr
, "scale: %f\n", scale
);
161 nsSVGUtils::ConvertToSurfaceSize(filterArea
.size
* scale
,
165 // 0 disables rendering, < 0 is error
166 if (filterRes
.width
<= 0 || filterRes
.height
<= 0)
169 // 'fini' is the matrix we will finally use to transform filter space
170 // to surface space for drawing
171 nsCOMPtr
<nsIDOMSVGMatrix
> scale
, fini
;
172 NS_NewSVGMatrix(getter_AddRefs(scale
),
173 filterArea
.Width() / filterRes
.width
, 0.0f
,
174 0.0f
, filterArea
.Height() / filterRes
.height
,
175 filterArea
.X(), filterArea
.Y());
176 ctm
->Multiply(scale
, getter_AddRefs(fini
));
178 gfxMatrix finiM
= nsSVGUtils::ConvertSVGMatrixToThebes(fini
);
179 // fini is always invertible.
182 nsIntRect dirtyOutputRect
=
183 MapDeviceRectToFilterSpace(finiM
, filterRes
, aDirtyOutputRect
);
184 nsIntRect dirtyInputRect
=
185 MapDeviceRectToFilterSpace(finiM
, filterRes
, aDirtyInputRect
);
187 // Setup instance data
188 mInstance
= new nsSVGFilterInstance(aTarget
, aPaint
, filter
, bbox
, filterArea
,
189 nsIntSize(filterRes
.width
, filterRes
.height
),
191 dirtyOutputRect
, dirtyInputRect
,
195 nsAutoFilterInstance::~nsAutoFilterInstance()
200 mTarget
->SetMatrixPropagation(PR_TRUE
);
201 mTarget
->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION
|
202 nsISVGChildFrame::TRANSFORM_CHANGED
);
206 nsSVGFilterFrame::FilterPaint(nsSVGRenderState
*aContext
,
208 nsSVGFilterPaintCallback
*aPaintCallback
,
209 const nsIntRect
*aDirtyRect
)
211 nsAutoFilterInstance
instance(aTarget
, this, aPaintCallback
,
212 aDirtyRect
, nsnull
, nsnull
);
216 nsRefPtr
<gfxASurface
> result
;
217 nsresult rv
= instance
.get()->Render(getter_AddRefs(result
));
218 if (NS_SUCCEEDED(rv
) && result
) {
219 nsSVGUtils::CompositeSurfaceMatrix(aContext
->GetGfxContext(),
220 result
, instance
.get()->GetFilterSpaceToDeviceSpaceTransform(), 1.0);
226 TransformFilterSpaceToDeviceSpace(nsSVGFilterInstance
*aInstance
, nsIntRect
*aRect
)
228 gfxMatrix m
= nsSVGUtils::ConvertSVGMatrixToThebes(
229 aInstance
->GetFilterSpaceToDeviceSpaceTransform());
230 gfxRect
r(aRect
->x
, aRect
->y
, aRect
->width
, aRect
->height
);
231 r
= m
.TransformBounds(r
);
233 nsIntRect deviceRect
;
234 nsresult rv
= nsSVGUtils::GfxRectToIntRect(r
, &deviceRect
);
242 nsSVGFilterFrame::GetInvalidationBBox(nsIFrame
*aTarget
, const nsIntRect
& aRect
)
244 nsAutoFilterInstance
instance(aTarget
, this, nsnull
, nsnull
, &aRect
, nsnull
);
248 // We've passed in the source's dirty area so the instance knows about it.
249 // Now we can ask the instance to compute the area of the filter output
252 nsresult rv
= instance
.get()->ComputeOutputDirtyRect(&dirtyRect
);
253 if (NS_SUCCEEDED(rv
)) {
254 rv
= TransformFilterSpaceToDeviceSpace(instance
.get(), &dirtyRect
);
255 if (NS_SUCCEEDED(rv
))
263 nsSVGFilterFrame::GetSourceForInvalidArea(nsIFrame
*aTarget
, const nsIntRect
& aRect
)
265 nsAutoFilterInstance
instance(aTarget
, this, nsnull
, &aRect
, nsnull
, nsnull
);
269 // Now we can ask the instance to compute the area of the source
271 nsIntRect neededRect
;
272 nsresult rv
= instance
.get()->ComputeSourceNeededRect(&neededRect
);
273 if (NS_SUCCEEDED(rv
)) {
274 rv
= TransformFilterSpaceToDeviceSpace(instance
.get(), &neededRect
);
275 if (NS_SUCCEEDED(rv
))
283 nsSVGFilterFrame::GetFilterBBox(nsIFrame
*aTarget
, const nsIntRect
*aSourceBBox
)
285 nsAutoFilterInstance
instance(aTarget
, this, nsnull
, nsnull
, nsnull
, aSourceBBox
);
289 // We've passed in the source's bounding box so the instance knows about
290 // it. Now we can ask the instance to compute the bounding box of
291 // the filter output.
293 nsresult rv
= instance
.get()->ComputeOutputBBox(&bbox
);
294 if (NS_SUCCEEDED(rv
)) {
295 rv
= TransformFilterSpaceToDeviceSpace(instance
.get(), &bbox
);
296 if (NS_SUCCEEDED(rv
))
304 nsSVGFilterFrame::GetType() const
306 return nsGkAtoms::svgFilterFrame
;