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.
22 * rocallahan@mozilla.com
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsSVGIntegrationUtils.h"
40 #include "nsSVGUtils.h"
41 #include "nsSVGEffects.h"
43 #include "nsLayoutUtils.h"
44 #include "nsDisplayList.h"
45 #include "nsSVGMatrix.h"
46 #include "nsSVGFilterPaintCallback.h"
47 #include "nsSVGFilterFrame.h"
48 #include "nsSVGClipPathFrame.h"
49 #include "nsSVGMaskFrame.h"
51 // ----------------------------------------------------------------------
54 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame
* aFrame
)
56 const nsStyleSVGReset
*style
= aFrame
->GetStyleSVGReset();
57 return (style
->mFilter
|| style
->mClipPath
|| style
->mMask
) &&
58 !aFrame
->IsFrameOfType(nsIFrame::eSVG
);
61 // Get the union the frame border-box rects over all continuations,
62 // relative to aFirst. This defines "user space" for non-SVG frames.
63 static nsRect
GetNonSVGUserSpace(nsIFrame
* aFirst
)
65 NS_ASSERTION(!aFirst
->GetPrevContinuation(), "Not first continuation");
66 return nsLayoutUtils::GetAllInFlowRectsUnion(aFirst
, aFirst
);
70 GetPreEffectsOverflowRect(nsIFrame
* aFrame
)
72 nsRect
* r
= static_cast<nsRect
*>(aFrame
->GetProperty(nsGkAtoms::preEffectsBBoxProperty
));
75 return aFrame
->GetOverflowRect();
78 struct BBoxCollector
: public nsLayoutUtils::BoxCallback
{
79 nsIFrame
* mReferenceFrame
;
80 nsIFrame
* mCurrentFrame
;
81 const nsRect
& mCurrentFrameOverflowArea
;
84 BBoxCollector(nsIFrame
* aReferenceFrame
, nsIFrame
* aCurrentFrame
,
85 const nsRect
& aCurrentFrameOverflowArea
)
86 : mReferenceFrame(aReferenceFrame
), mCurrentFrame(aCurrentFrame
),
87 mCurrentFrameOverflowArea(aCurrentFrameOverflowArea
) {}
89 virtual void AddBox(nsIFrame
* aFrame
) {
90 nsRect overflow
= aFrame
== mCurrentFrame
? mCurrentFrameOverflowArea
91 : GetPreEffectsOverflowRect(aFrame
);
92 mResult
.UnionRect(mResult
, overflow
+ aFrame
->GetOffsetTo(mReferenceFrame
));
97 GetSVGBBox(nsIFrame
* aNonSVGFrame
, nsIFrame
* aCurrentFrame
,
98 const nsRect
& aCurrentOverflow
, const nsRect
& aUserSpaceRect
)
100 NS_ASSERTION(!aNonSVGFrame
->GetPrevContinuation(),
101 "Need first continuation here");
102 // Compute union of all overflow areas relative to 'first'.
103 BBoxCollector
collector(aNonSVGFrame
, aCurrentFrame
, aCurrentOverflow
);
104 nsLayoutUtils::GetAllInFlowBoxes(aNonSVGFrame
, &collector
);
105 // Get it into "user space" for non-SVG frames
106 return collector
.mResult
- aUserSpaceRect
.TopLeft();
110 nsSVGIntegrationUtils::ComputeFrameEffectsRect(nsIFrame
* aFrame
,
111 const nsRect
& aOverflowRect
)
113 nsIFrame
* firstFrame
=
114 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame
);
115 nsSVGEffects::EffectProperties effectProperties
=
116 nsSVGEffects::GetEffectProperties(firstFrame
);
117 nsSVGFilterFrame
*filterFrame
= effectProperties
.mFilter
?
118 effectProperties
.mFilter
->GetFilterFrame() : nsnull
;
120 return aOverflowRect
;
122 // XXX this isn't really right. We can't compute the correct filter
123 // bbox until all aFrame's continuations have been reflowed.
124 // but then it's too late to set the overflow areas for the earlier frames.
125 nsRect userSpaceRect
= GetNonSVGUserSpace(firstFrame
);
126 nsRect r
= GetSVGBBox(firstFrame
, aFrame
, aOverflowRect
, userSpaceRect
);
127 // r is relative to user space
128 PRUint32 appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
129 r
.ScaleRoundOutInverse(appUnitsPerDevPixel
);
130 r
= filterFrame
->GetFilterBBox(firstFrame
, &r
);
131 r
.ScaleRoundOut(appUnitsPerDevPixel
);
132 // Make it relative to aFrame again
133 return r
+ userSpaceRect
.TopLeft() - aFrame
->GetOffsetTo(firstFrame
);
137 nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(nsIFrame
* aFrame
,
138 const nsRect
& aInvalidRect
)
140 // Don't bother calling GetEffectProperties; the filter property should
141 // already have been set up during reflow/ComputeFrameEffectsRect
142 nsIFrame
* firstFrame
=
143 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame
);
144 nsSVGEffects::EffectProperties effectProperties
=
145 nsSVGEffects::GetEffectProperties(firstFrame
);
146 if (!effectProperties
.mFilter
)
148 nsSVGFilterFrame
* filterFrame
= nsSVGEffects::GetFilterFrame(firstFrame
);
150 // The frame is either not there or not currently available,
151 // perhaps because we're in the middle of tearing stuff down.
153 return aFrame
->GetOverflowRect();
156 PRInt32 appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
157 nsRect userSpaceRect
= GetNonSVGUserSpace(firstFrame
);
158 nsPoint offset
= aFrame
->GetOffsetTo(firstFrame
) - userSpaceRect
.TopLeft();
159 nsRect r
= aInvalidRect
+ offset
;
160 r
.ScaleRoundOutInverse(appUnitsPerDevPixel
);
161 r
= filterFrame
->GetInvalidationBBox(firstFrame
, r
);
162 r
.ScaleRoundOut(appUnitsPerDevPixel
);
167 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame
* aFrame
,
168 const nsRect
& aDamageRect
)
170 // Don't bother calling GetEffectProperties; the filter property should
171 // already have been set up during reflow/ComputeFrameEffectsRect
172 nsIFrame
* firstFrame
=
173 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame
);
174 nsSVGFilterFrame
* filterFrame
=
175 nsSVGEffects::GetFilterFrame(firstFrame
);
179 PRInt32 appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
180 nsRect userSpaceRect
= GetNonSVGUserSpace(firstFrame
);
181 nsPoint offset
= aFrame
->GetOffsetTo(firstFrame
) - userSpaceRect
.TopLeft();
182 nsRect r
= aDamageRect
+ offset
;
183 r
.ScaleRoundOutInverse(appUnitsPerDevPixel
);
184 r
= filterFrame
->GetSourceForInvalidArea(firstFrame
, r
);
185 r
.ScaleRoundOut(appUnitsPerDevPixel
);
190 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame
* aFrame
, const nsPoint
& aPt
)
192 nsIFrame
* firstFrame
=
193 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame
);
194 nsRect userSpaceRect
= GetNonSVGUserSpace(firstFrame
);
195 // get point relative to userSpaceRect
196 nsPoint pt
= aPt
+ aFrame
->GetOffsetTo(firstFrame
) - userSpaceRect
.TopLeft();
197 return nsSVGUtils::HitTestClip(firstFrame
, pt
);
200 class RegularFramePaintCallback
: public nsSVGFilterPaintCallback
203 RegularFramePaintCallback(nsDisplayListBuilder
* aBuilder
,
204 nsDisplayList
* aInnerList
,
205 const nsPoint
& aOffset
)
206 : mBuilder(aBuilder
), mInnerList(aInnerList
), mOffset(aOffset
) {}
208 virtual void Paint(nsSVGRenderState
*aContext
, nsIFrame
*aTarget
,
209 const nsIntRect
* aDirtyRect
)
211 nsIRenderingContext
* ctx
= aContext
->GetRenderingContext(aTarget
);
212 gfxContext
* gfxCtx
= aContext
->GetGfxContext();
214 // We're expected to paint with 1 unit equal to 1 CSS pixel. But
215 // mInnerList->Paint expects 1 unit to equal 1 device pixel. So
218 nsPresContext::AppUnitsToFloatCSSPixels(aTarget
->PresContext()->AppUnitsPerDevPixel());
219 gfxCtx
->Scale(scale
, scale
);
221 nsIRenderingContext::AutoPushTranslation
push(ctx
, -mOffset
.x
, -mOffset
.y
);
225 dirty
.ScaleRoundOut(nsIDeviceContext::AppUnitsPerCSSPixel());
228 dirty
= mInnerList
->GetBounds(mBuilder
);
230 mInnerList
->Paint(mBuilder
, ctx
, dirty
);
234 nsDisplayListBuilder
* mBuilder
;
235 nsDisplayList
* mInnerList
;
240 nsSVGIntegrationUtils::PaintFramesWithEffects(nsIRenderingContext
* aCtx
,
241 nsIFrame
* aEffectsFrame
,
242 const nsRect
& aDirtyRect
,
243 nsDisplayListBuilder
* aBuilder
,
244 nsDisplayList
* aInnerList
)
247 nsISVGChildFrame
*svgChildFrame
;
248 CallQueryInterface(aEffectsFrame
, &svgChildFrame
);
250 NS_ASSERTION(!svgChildFrame
, "Should never be called on an SVG frame");
252 float opacity
= aEffectsFrame
->GetStyleDisplay()->mOpacity
;
256 /* Properties are added lazily and may have been removed by a restyle,
257 so make sure all applicable ones are set again. */
258 nsIFrame
* firstFrame
=
259 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aEffectsFrame
);
260 nsSVGEffects::EffectProperties effectProperties
=
261 nsSVGEffects::GetEffectProperties(firstFrame
);
263 /* SVG defines the following rendering model:
267 * 3. Apply clipping, masking, group opacity
269 * We follow this, but perform a couple of optimizations:
271 * + Use cairo's clipPath when representable natively (single object
274 * + Merge opacity and masking if both used together.
277 PRBool isOK
= PR_TRUE
;
278 nsSVGClipPathFrame
*clipPathFrame
= effectProperties
.GetClipPathFrame(&isOK
);
279 nsSVGFilterFrame
*filterFrame
= effectProperties
.GetFilterFrame(&isOK
);
280 nsSVGMaskFrame
*maskFrame
= effectProperties
.GetMaskFrame(&isOK
);
282 PRBool isTrivialClip
= clipPathFrame
? clipPathFrame
->IsTrivial() : PR_TRUE
;
285 // Some resource is missing. We shouldn't paint anything.
289 gfxContext
* gfx
= aCtx
->ThebesContext();
290 gfxMatrix savedCTM
= gfx
->CurrentMatrix();
291 nsSVGRenderState
svgContext(aCtx
);
293 nsRect userSpaceRect
= GetNonSVGUserSpace(firstFrame
) + aBuilder
->ToReferenceFrame(firstFrame
);
294 PRInt32 appUnitsPerDevPixel
= aEffectsFrame
->PresContext()->AppUnitsPerDevPixel();
295 userSpaceRect
.ScaleRoundPreservingCentersInverse(appUnitsPerDevPixel
);
296 userSpaceRect
.ScaleRoundOut(appUnitsPerDevPixel
);
297 aCtx
->Translate(userSpaceRect
.x
, userSpaceRect
.y
);
299 nsCOMPtr
<nsIDOMSVGMatrix
> matrix
= GetInitialMatrix(aEffectsFrame
);
301 PRBool complexEffects
= PR_FALSE
;
302 /* Check if we need to do additional operations on this child's
303 * rendering, which necessitates rendering into another surface. */
304 if (opacity
!= 1.0f
|| maskFrame
|| (clipPathFrame
&& !isTrivialClip
)) {
305 complexEffects
= PR_TRUE
;
307 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
310 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
311 * we can just do normal painting and get it clipped appropriately.
313 if (clipPathFrame
&& isTrivialClip
) {
315 clipPathFrame
->ClipPaint(&svgContext
, aEffectsFrame
, matrix
);
318 /* Paint the child */
320 RegularFramePaintCallback
paint(aBuilder
, aInnerList
, userSpaceRect
.TopLeft());
321 nsRect r
= aDirtyRect
- userSpaceRect
.TopLeft();
322 r
.ScaleRoundOutInverse(appUnitsPerDevPixel
);
323 filterFrame
->FilterPaint(&svgContext
, aEffectsFrame
, &paint
, &r
);
325 gfx
->SetMatrix(savedCTM
);
326 aInnerList
->Paint(aBuilder
, aCtx
, aDirtyRect
);
327 aCtx
->Translate(userSpaceRect
.x
, userSpaceRect
.y
);
330 if (clipPathFrame
&& isTrivialClip
) {
334 /* No more effects, we're done. */
335 if (!complexEffects
) {
336 gfx
->SetMatrix(savedCTM
);
340 gfx
->PopGroupToSource();
342 nsRefPtr
<gfxPattern
> maskSurface
=
343 maskFrame
? maskFrame
->ComputeMaskAlpha(&svgContext
, aEffectsFrame
,
344 matrix
, opacity
) : nsnull
;
346 nsRefPtr
<gfxPattern
> clipMaskSurface
;
347 if (clipPathFrame
&& !isTrivialClip
) {
348 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
350 nsresult rv
= clipPathFrame
->ClipPaint(&svgContext
, aEffectsFrame
, matrix
);
351 clipMaskSurface
= gfx
->PopGroup();
353 if (NS_SUCCEEDED(rv
) && clipMaskSurface
) {
354 // Still more set after clipping, so clip to another surface
355 if (maskSurface
|| opacity
!= 1.0f
) {
356 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
357 gfx
->Mask(clipMaskSurface
);
358 gfx
->PopGroupToSource();
360 gfx
->Mask(clipMaskSurface
);
366 gfx
->Mask(maskSurface
);
367 } else if (opacity
!= 1.0f
) {
372 gfx
->SetMatrix(savedCTM
);
375 already_AddRefed
<nsIDOMSVGMatrix
>
376 nsSVGIntegrationUtils::GetInitialMatrix(nsIFrame
* aNonSVGFrame
)
378 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
379 "SVG frames should not get here");
380 PRInt32 appUnitsPerDevPixel
= aNonSVGFrame
->PresContext()->AppUnitsPerDevPixel();
381 nsCOMPtr
<nsIDOMSVGMatrix
> matrix
;
382 float devPxPerCSSPx
=
383 1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel
);
384 NS_NewSVGMatrix(getter_AddRefs(matrix
),
386 0.0f
, devPxPerCSSPx
);
387 return matrix
.forget();
391 nsSVGIntegrationUtils::GetSVGRectForNonSVGFrame(nsIFrame
* aNonSVGFrame
)
393 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
394 "SVG frames should not get here");
395 nsIFrame
* firstFrame
=
396 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame
);
397 nsRect r
= GetNonSVGUserSpace(firstFrame
);
398 nsPresContext
* presContext
= firstFrame
->PresContext();
399 return gfxRect(0, 0, presContext
->AppUnitsToFloatCSSPixels(r
.width
),
400 presContext
->AppUnitsToFloatCSSPixels(r
.height
));
404 nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame
* aNonSVGFrame
)
406 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
407 "SVG frames should not get here");
408 nsIFrame
* firstFrame
=
409 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame
);
410 nsRect userSpaceRect
= GetNonSVGUserSpace(firstFrame
);
411 nsRect r
= GetSVGBBox(firstFrame
, nsnull
, nsRect(), userSpaceRect
);
412 gfxRect
result(r
.x
, r
.y
, r
.width
, r
.height
);
413 nsPresContext
* presContext
= aNonSVGFrame
->PresContext();
414 result
.ScaleInverse(presContext
->AppUnitsPerCSSPixel());