Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / svg / base / src / nsSVGIntegrationUtils.cpp
blob8842b32d82c65bcd532a00af2d8e1bd188ab180c
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
13 * License.
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.
21 * Contributor(s):
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"
42 #include "nsRegion.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 // ----------------------------------------------------------------------
53 PRBool
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);
69 static nsRect
70 GetPreEffectsOverflowRect(nsIFrame* aFrame)
72 nsRect* r = static_cast<nsRect*>(aFrame->GetProperty(nsGkAtoms::preEffectsBBoxProperty));
73 if (r)
74 return *r;
75 return aFrame->GetOverflowRect();
78 struct BBoxCollector : public nsLayoutUtils::BoxCallback {
79 nsIFrame* mReferenceFrame;
80 nsIFrame* mCurrentFrame;
81 const nsRect& mCurrentFrameOverflowArea;
82 nsRect mResult;
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));
96 static nsRect
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();
109 nsRect
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;
119 if (!filterFrame)
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);
136 nsRect
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)
147 return aInvalidRect;
148 nsSVGFilterFrame* filterFrame = nsSVGEffects::GetFilterFrame(firstFrame);
149 if (!filterFrame) {
150 // The frame is either not there or not currently available,
151 // perhaps because we're in the middle of tearing stuff down.
152 // Be conservative.
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);
163 return r - offset;
166 nsRect
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);
176 if (!filterFrame)
177 return aDamageRect;
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);
186 return r - offset;
189 PRBool
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
202 public:
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
216 // adjust.
217 gfxFloat scale =
218 nsPresContext::AppUnitsToFloatCSSPixels(aTarget->PresContext()->AppUnitsPerDevPixel());
219 gfxCtx->Scale(scale, scale);
221 nsIRenderingContext::AutoPushTranslation push(ctx, -mOffset.x, -mOffset.y);
222 nsRect dirty;
223 if (aDirtyRect) {
224 dirty = *aDirtyRect;
225 dirty.ScaleRoundOut(nsIDeviceContext::AppUnitsPerCSSPixel());
226 dirty += mOffset;
227 } else {
228 dirty = mInnerList->GetBounds(mBuilder);
230 mInnerList->Paint(mBuilder, ctx, dirty);
233 private:
234 nsDisplayListBuilder* mBuilder;
235 nsDisplayList* mInnerList;
236 nsPoint mOffset;
239 void
240 nsSVGIntegrationUtils::PaintFramesWithEffects(nsIRenderingContext* aCtx,
241 nsIFrame* aEffectsFrame,
242 const nsRect& aDirtyRect,
243 nsDisplayListBuilder* aBuilder,
244 nsDisplayList* aInnerList)
246 #ifdef DEBUG
247 nsISVGChildFrame *svgChildFrame;
248 CallQueryInterface(aEffectsFrame, &svgChildFrame);
249 #endif
250 NS_ASSERTION(!svgChildFrame, "Should never be called on an SVG frame");
252 float opacity = aEffectsFrame->GetStyleDisplay()->mOpacity;
253 if (opacity == 0.0f)
254 return;
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:
265 * 1. Render geometry
266 * 2. Apply filter
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
272 * clip region).
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;
284 if (!isOK) {
285 // Some resource is missing. We shouldn't paint anything.
286 return;
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;
306 gfx->Save();
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) {
314 gfx->Save();
315 clipPathFrame->ClipPaint(&svgContext, aEffectsFrame, matrix);
318 /* Paint the child */
319 if (filterFrame) {
320 RegularFramePaintCallback paint(aBuilder, aInnerList, userSpaceRect.TopLeft());
321 nsRect r = aDirtyRect - userSpaceRect.TopLeft();
322 r.ScaleRoundOutInverse(appUnitsPerDevPixel);
323 filterFrame->FilterPaint(&svgContext, aEffectsFrame, &paint, &r);
324 } else {
325 gfx->SetMatrix(savedCTM);
326 aInnerList->Paint(aBuilder, aCtx, aDirtyRect);
327 aCtx->Translate(userSpaceRect.x, userSpaceRect.y);
330 if (clipPathFrame && isTrivialClip) {
331 gfx->Restore();
334 /* No more effects, we're done. */
335 if (!complexEffects) {
336 gfx->SetMatrix(savedCTM);
337 return;
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();
359 } else {
360 gfx->Mask(clipMaskSurface);
365 if (maskSurface) {
366 gfx->Mask(maskSurface);
367 } else if (opacity != 1.0f) {
368 gfx->Paint(opacity);
371 gfx->Restore();
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),
385 devPxPerCSSPx, 0.0f,
386 0.0f, devPxPerCSSPx);
387 return matrix.forget();
390 gfxRect
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));
403 gfxRect
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());
415 return result;