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 : * rocallahan@mozilla.com
23 : *
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.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsSVGIntegrationUtils.h"
39 :
40 : #include "nsSVGUtils.h"
41 : #include "nsSVGEffects.h"
42 : #include "nsRegion.h"
43 : #include "nsLayoutUtils.h"
44 : #include "nsDisplayList.h"
45 : #include "nsSVGFilterPaintCallback.h"
46 : #include "nsSVGFilterFrame.h"
47 : #include "nsSVGClipPathFrame.h"
48 : #include "nsSVGMaskFrame.h"
49 : #include "gfxPlatform.h"
50 : #include "gfxDrawable.h"
51 : #include "nsSVGPaintServerFrame.h"
52 :
53 : // ----------------------------------------------------------------------
54 :
55 : bool
56 0 : nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
57 : {
58 0 : if (aFrame->IsFrameOfType(nsIFrame::eSVG)) {
59 0 : return false;
60 : }
61 0 : const nsStyleSVGReset *style = aFrame->GetStyleSVGReset();
62 0 : return (style->mFilter || style->mClipPath || style->mMask);
63 : }
64 :
65 : /* static */ nsRect
66 0 : nsSVGIntegrationUtils::GetNonSVGUserSpace(nsIFrame* aFirst)
67 : {
68 0 : NS_ASSERTION(!aFirst->GetPrevContinuation(), "Not first continuation");
69 0 : return nsLayoutUtils::GetAllInFlowRectsUnion(aFirst, aFirst);
70 : }
71 :
72 : static nsRect
73 0 : GetPreEffectsOverflowRect(nsIFrame* aFrame)
74 : {
75 : nsRect* r = static_cast<nsRect*>
76 0 : (aFrame->Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
77 0 : if (r)
78 0 : return *r;
79 0 : return aFrame->GetVisualOverflowRect();
80 : }
81 :
82 0 : struct BBoxCollector : public nsLayoutUtils::BoxCallback {
83 : nsIFrame* mReferenceFrame;
84 : nsIFrame* mCurrentFrame;
85 : const nsRect& mCurrentFrameOverflowArea;
86 : nsRect mResult;
87 :
88 0 : BBoxCollector(nsIFrame* aReferenceFrame, nsIFrame* aCurrentFrame,
89 : const nsRect& aCurrentFrameOverflowArea)
90 : : mReferenceFrame(aReferenceFrame), mCurrentFrame(aCurrentFrame),
91 0 : mCurrentFrameOverflowArea(aCurrentFrameOverflowArea) {}
92 :
93 0 : virtual void AddBox(nsIFrame* aFrame) {
94 : nsRect overflow = aFrame == mCurrentFrame ? mCurrentFrameOverflowArea
95 0 : : GetPreEffectsOverflowRect(aFrame);
96 0 : mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mReferenceFrame));
97 0 : }
98 : };
99 :
100 : static nsRect
101 0 : GetSVGBBox(nsIFrame* aNonSVGFrame, nsIFrame* aCurrentFrame,
102 : const nsRect& aCurrentOverflow, const nsRect& aUserSpaceRect)
103 : {
104 0 : NS_ASSERTION(!aNonSVGFrame->GetPrevContinuation(),
105 : "Need first continuation here");
106 : // Compute union of all overflow areas relative to 'first'.
107 0 : BBoxCollector collector(aNonSVGFrame, aCurrentFrame, aCurrentOverflow);
108 0 : nsLayoutUtils::GetAllInFlowBoxes(aNonSVGFrame, &collector);
109 : // Get it into "user space" for non-SVG frames
110 0 : return collector.mResult - aUserSpaceRect.TopLeft();
111 : }
112 :
113 : nsRect
114 0 : nsSVGIntegrationUtils::ComputeFrameEffectsRect(nsIFrame* aFrame,
115 : const nsRect& aOverflowRect)
116 : {
117 : nsIFrame* firstFrame =
118 0 : nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
119 : nsSVGEffects::EffectProperties effectProperties =
120 0 : nsSVGEffects::GetEffectProperties(firstFrame);
121 : nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
122 0 : effectProperties.mFilter->GetFilterFrame() : nsnull;
123 0 : if (!filterFrame)
124 0 : return aOverflowRect;
125 :
126 : // XXX this isn't really right. We can't compute the correct filter
127 : // bbox until all aFrame's continuations have been reflowed.
128 : // but then it's too late to set the overflow areas for the earlier frames.
129 0 : nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
130 0 : nsRect r = GetSVGBBox(firstFrame, aFrame, aOverflowRect, userSpaceRect);
131 : // r is relative to user space
132 0 : PRUint32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
133 0 : nsIntRect p = r.ToOutsidePixels(appUnitsPerDevPixel);
134 0 : p = filterFrame->GetFilterBBox(firstFrame, &p);
135 0 : r = p.ToAppUnits(appUnitsPerDevPixel);
136 : // Make it relative to aFrame again
137 0 : return r + userSpaceRect.TopLeft() - aFrame->GetOffsetTo(firstFrame);
138 : }
139 :
140 : nsRect
141 0 : nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(nsIFrame* aFrame,
142 : const nsRect& aInvalidRect)
143 : {
144 : // Don't bother calling GetEffectProperties; the filter property should
145 : // already have been set up during reflow/ComputeFrameEffectsRect
146 : nsIFrame* firstFrame =
147 0 : nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
148 : nsSVGEffects::EffectProperties effectProperties =
149 0 : nsSVGEffects::GetEffectProperties(firstFrame);
150 0 : if (!effectProperties.mFilter)
151 0 : return aInvalidRect;
152 :
153 0 : nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
154 0 : if (!prop || !prop->IsInObserverList()) {
155 0 : return aInvalidRect;
156 : }
157 :
158 0 : nsSVGFilterFrame* filterFrame = prop->GetFilterFrame();
159 0 : if (!filterFrame) {
160 : // The frame is either not there or not currently available,
161 : // perhaps because we're in the middle of tearing stuff down.
162 : // Be conservative.
163 0 : return aFrame->GetVisualOverflowRect();
164 : }
165 :
166 0 : PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
167 0 : nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
168 0 : nsPoint offset = aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
169 0 : nsRect r = aInvalidRect + offset;
170 0 : nsIntRect p = r.ToOutsidePixels(appUnitsPerDevPixel);
171 0 : p = filterFrame->GetInvalidationBBox(firstFrame, p);
172 0 : r = p.ToAppUnits(appUnitsPerDevPixel);
173 0 : return r - offset;
174 : }
175 :
176 : nsRect
177 0 : nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
178 : const nsRect& aDamageRect)
179 : {
180 : // Don't bother calling GetEffectProperties; the filter property should
181 : // already have been set up during reflow/ComputeFrameEffectsRect
182 : nsIFrame* firstFrame =
183 0 : nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
184 : nsSVGFilterFrame* filterFrame =
185 0 : nsSVGEffects::GetFilterFrame(firstFrame);
186 0 : if (!filterFrame)
187 0 : return aDamageRect;
188 :
189 0 : PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
190 0 : nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
191 0 : nsPoint offset = aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
192 0 : nsRect r = aDamageRect + offset;
193 0 : nsIntRect p = r.ToOutsidePixels(appUnitsPerDevPixel);
194 0 : p = filterFrame->GetSourceForInvalidArea(firstFrame, p);
195 0 : r = p.ToAppUnits(appUnitsPerDevPixel);
196 0 : return r - offset;
197 : }
198 :
199 : bool
200 0 : nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
201 : {
202 : nsIFrame* firstFrame =
203 0 : nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
204 0 : nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
205 : // get point relative to userSpaceRect
206 0 : nsPoint pt = aPt + aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
207 0 : return nsSVGUtils::HitTestClip(firstFrame, pt);
208 : }
209 :
210 : class RegularFramePaintCallback : public nsSVGFilterPaintCallback
211 : {
212 : public:
213 0 : RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
214 : nsDisplayList* aInnerList,
215 : nsIFrame* aFrame,
216 : const nsPoint& aOffset)
217 : : mBuilder(aBuilder), mInnerList(aInnerList), mFrame(aFrame),
218 0 : mOffset(aOffset) {}
219 :
220 0 : virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
221 : const nsIntRect* aDirtyRect)
222 : {
223 0 : nsRenderingContext::AutoPushTranslation push(aContext, -mOffset);
224 0 : mInnerList->PaintForFrame(mBuilder, aContext, mFrame, nsDisplayList::PAINT_DEFAULT);
225 0 : }
226 :
227 : private:
228 : nsDisplayListBuilder* mBuilder;
229 : nsDisplayList* mInnerList;
230 : nsIFrame* mFrame;
231 : nsPoint mOffset;
232 : };
233 :
234 : void
235 0 : nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
236 : nsIFrame* aEffectsFrame,
237 : const nsRect& aDirtyRect,
238 : nsDisplayListBuilder* aBuilder,
239 : nsDisplayList* aInnerList)
240 : {
241 : #ifdef DEBUG
242 0 : nsISVGChildFrame *svgChildFrame = do_QueryFrame(aEffectsFrame);
243 0 : NS_ASSERTION(!svgChildFrame, "Should never be called on an SVG frame");
244 : #endif
245 :
246 0 : float opacity = aEffectsFrame->GetStyleDisplay()->mOpacity;
247 0 : if (opacity == 0.0f)
248 0 : return;
249 :
250 : /* Properties are added lazily and may have been removed by a restyle,
251 : so make sure all applicable ones are set again. */
252 : nsIFrame* firstFrame =
253 0 : nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aEffectsFrame);
254 : nsSVGEffects::EffectProperties effectProperties =
255 0 : nsSVGEffects::GetEffectProperties(firstFrame);
256 :
257 : /* SVG defines the following rendering model:
258 : *
259 : * 1. Render geometry
260 : * 2. Apply filter
261 : * 3. Apply clipping, masking, group opacity
262 : *
263 : * We follow this, but perform a couple of optimizations:
264 : *
265 : * + Use cairo's clipPath when representable natively (single object
266 : * clip region).
267 : *
268 : * + Merge opacity and masking if both used together.
269 : */
270 :
271 0 : bool isOK = true;
272 0 : nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
273 0 : nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
274 0 : nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
275 :
276 0 : bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
277 :
278 0 : if (!isOK) {
279 : // Some resource is missing. We shouldn't paint anything.
280 0 : return;
281 : }
282 :
283 0 : gfxContext* gfx = aCtx->ThebesContext();
284 0 : gfxMatrix savedCTM = gfx->CurrentMatrix();
285 :
286 : //SVGAutoRenderState autoRenderState(aCtx, SVGAutoRenderState::NORMAL);
287 :
288 0 : nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame) + aBuilder->ToReferenceFrame(firstFrame);
289 0 : PRInt32 appUnitsPerDevPixel = aEffectsFrame->PresContext()->AppUnitsPerDevPixel();
290 0 : userSpaceRect = userSpaceRect.ToNearestPixels(appUnitsPerDevPixel).ToAppUnits(appUnitsPerDevPixel);
291 0 : aCtx->Translate(userSpaceRect.TopLeft());
292 :
293 0 : gfxMatrix matrix = GetInitialMatrix(aEffectsFrame);
294 :
295 0 : bool complexEffects = false;
296 : /* Check if we need to do additional operations on this child's
297 : * rendering, which necessitates rendering into another surface. */
298 0 : if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
299 0 : complexEffects = true;
300 0 : gfx->Save();
301 0 : aCtx->IntersectClip(aEffectsFrame->GetVisualOverflowRect());
302 0 : gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
303 : }
304 :
305 : /* If this frame has only a trivial clipPath, set up cairo's clipping now so
306 : * we can just do normal painting and get it clipped appropriately.
307 : */
308 0 : if (clipPathFrame && isTrivialClip) {
309 0 : gfx->Save();
310 0 : clipPathFrame->ClipPaint(aCtx, aEffectsFrame, matrix);
311 : }
312 :
313 : /* Paint the child */
314 0 : if (filterFrame) {
315 : RegularFramePaintCallback paint(aBuilder, aInnerList, aEffectsFrame,
316 0 : userSpaceRect.TopLeft());
317 0 : nsIntRect r = (aDirtyRect - userSpaceRect.TopLeft()).ToOutsidePixels(appUnitsPerDevPixel);
318 0 : filterFrame->FilterPaint(aCtx, aEffectsFrame, &paint, &r);
319 : } else {
320 0 : gfx->SetMatrix(savedCTM);
321 : aInnerList->PaintForFrame(aBuilder, aCtx, aEffectsFrame,
322 0 : nsDisplayList::PAINT_DEFAULT);
323 0 : aCtx->Translate(userSpaceRect.TopLeft());
324 : }
325 :
326 0 : if (clipPathFrame && isTrivialClip) {
327 0 : gfx->Restore();
328 : }
329 :
330 : /* No more effects, we're done. */
331 0 : if (!complexEffects) {
332 0 : gfx->SetMatrix(savedCTM);
333 : return;
334 : }
335 :
336 0 : gfx->PopGroupToSource();
337 :
338 : nsRefPtr<gfxPattern> maskSurface =
339 : maskFrame ? maskFrame->ComputeMaskAlpha(aCtx, aEffectsFrame,
340 0 : matrix, opacity) : nsnull;
341 :
342 0 : nsRefPtr<gfxPattern> clipMaskSurface;
343 0 : if (clipPathFrame && !isTrivialClip) {
344 0 : gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
345 :
346 0 : nsresult rv = clipPathFrame->ClipPaint(aCtx, aEffectsFrame, matrix);
347 0 : clipMaskSurface = gfx->PopGroup();
348 :
349 0 : if (NS_SUCCEEDED(rv) && clipMaskSurface) {
350 : // Still more set after clipping, so clip to another surface
351 0 : if (maskSurface || opacity != 1.0f) {
352 0 : gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
353 0 : gfx->Mask(clipMaskSurface);
354 0 : gfx->PopGroupToSource();
355 : } else {
356 0 : gfx->Mask(clipMaskSurface);
357 : }
358 : }
359 : }
360 :
361 0 : if (maskSurface) {
362 0 : gfx->Mask(maskSurface);
363 0 : } else if (opacity != 1.0f) {
364 0 : gfx->Paint(opacity);
365 : }
366 :
367 0 : gfx->Restore();
368 0 : gfx->SetMatrix(savedCTM);
369 : }
370 :
371 : gfxMatrix
372 0 : nsSVGIntegrationUtils::GetInitialMatrix(nsIFrame* aNonSVGFrame)
373 : {
374 0 : NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
375 : "SVG frames should not get here");
376 0 : PRInt32 appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
377 : float devPxPerCSSPx =
378 0 : 1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
379 :
380 : return gfxMatrix(devPxPerCSSPx, 0.0,
381 : 0.0, devPxPerCSSPx,
382 0 : 0.0, 0.0);
383 : }
384 :
385 : gfxRect
386 0 : nsSVGIntegrationUtils::GetSVGRectForNonSVGFrame(nsIFrame* aNonSVGFrame)
387 : {
388 0 : NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
389 : "SVG frames should not get here");
390 : nsIFrame* firstFrame =
391 0 : nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame);
392 0 : nsRect r = GetNonSVGUserSpace(firstFrame);
393 0 : nsPresContext* presContext = firstFrame->PresContext();
394 0 : return gfxRect(0, 0, presContext->AppUnitsToFloatCSSPixels(r.width),
395 0 : presContext->AppUnitsToFloatCSSPixels(r.height));
396 : }
397 :
398 : gfxRect
399 0 : nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
400 : {
401 0 : NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
402 : "SVG frames should not get here");
403 : nsIFrame* firstFrame =
404 0 : nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame);
405 0 : nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
406 0 : nsRect r = GetSVGBBox(firstFrame, nsnull, nsRect(), userSpaceRect);
407 0 : gfxRect result(r.x, r.y, r.width, r.height);
408 0 : nsPresContext* presContext = aNonSVGFrame->PresContext();
409 0 : result.ScaleInverse(presContext->AppUnitsPerCSSPixel());
410 : return result;
411 : }
412 :
413 0 : class PaintFrameCallback : public gfxDrawingCallback {
414 : public:
415 0 : PaintFrameCallback(nsIFrame* aFrame,
416 : nsIFrame* aTarget,
417 : const nsSize aPaintServerSize,
418 : const gfxIntSize aRenderSize)
419 : : mFrame(aFrame)
420 : , mTarget(aTarget)
421 : , mPaintServerSize(aPaintServerSize)
422 0 : , mRenderSize(aRenderSize)
423 0 : {}
424 : virtual bool operator()(gfxContext* aContext,
425 : const gfxRect& aFillRect,
426 : const gfxPattern::GraphicsFilter& aFilter,
427 : const gfxMatrix& aTransform);
428 : private:
429 : nsIFrame* mFrame;
430 : nsIFrame* mTarget;
431 : nsSize mPaintServerSize;
432 : gfxIntSize mRenderSize;
433 : };
434 :
435 : bool
436 0 : PaintFrameCallback::operator()(gfxContext* aContext,
437 : const gfxRect& aFillRect,
438 : const gfxPattern::GraphicsFilter& aFilter,
439 : const gfxMatrix& aTransform)
440 : {
441 0 : if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)
442 0 : return false;
443 :
444 0 : mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
445 :
446 0 : nsRenderingContext context;
447 0 : context.Init(mFrame->PresContext()->DeviceContext(), aContext);
448 0 : aContext->Save();
449 :
450 : // Clip to aFillRect so that we don't paint outside.
451 0 : aContext->NewPath();
452 0 : aContext->Rectangle(aFillRect);
453 0 : aContext->Clip();
454 0 : gfxMatrix savedMatrix(aContext->CurrentMatrix());
455 :
456 0 : aContext->Multiply(gfxMatrix(aTransform).Invert());
457 :
458 : // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
459 : // to have it anchored at the top left corner of the bounding box of all of
460 : // mFrame's continuations. So we add a translation transform.
461 0 : nsRect bbox = nsSVGIntegrationUtils::GetNonSVGUserSpace(mFrame);
462 0 : PRInt32 appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
463 0 : gfxPoint offset = gfxPoint(bbox.x, bbox.y) / appUnitsPerDevPixel;
464 0 : aContext->Multiply(gfxMatrix().Translate(-offset));
465 :
466 : gfxSize paintServerSize =
467 : gfxSize(mPaintServerSize.width, mPaintServerSize.height) /
468 0 : mFrame->PresContext()->AppUnitsPerDevPixel();
469 :
470 : // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
471 : // want it to render with mRenderSize, so we need to set up a scale transform.
472 0 : gfxFloat scaleX = mRenderSize.width / paintServerSize.width;
473 0 : gfxFloat scaleY = mRenderSize.height / paintServerSize.height;
474 0 : gfxMatrix scaleMatrix = gfxMatrix().Scale(scaleX, scaleY);
475 0 : aContext->Multiply(scaleMatrix);
476 :
477 : // Draw.
478 0 : nsRect dirty(bbox.x, bbox.y, mPaintServerSize.width, mPaintServerSize.height);
479 : nsLayoutUtils::PaintFrame(&context, mFrame,
480 : dirty, NS_RGBA(0, 0, 0, 0),
481 : nsLayoutUtils::PAINT_IN_TRANSFORM |
482 0 : nsLayoutUtils::PAINT_ALL_CONTINUATIONS);
483 :
484 0 : aContext->SetMatrix(savedMatrix);
485 0 : aContext->Restore();
486 :
487 0 : mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
488 :
489 0 : return true;
490 : }
491 :
492 : static already_AddRefed<gfxDrawable>
493 0 : DrawableFromPaintServer(nsIFrame* aFrame,
494 : nsIFrame* aTarget,
495 : const nsSize& aPaintServerSize,
496 : const gfxIntSize& aRenderSize)
497 : {
498 : // aPaintServerSize is the size that would be filled when using
499 : // background-repeat:no-repeat and background-size:auto. For normal background
500 : // images, this would be the intrinsic size of the image; for gradients and
501 : // patterns this would be the whole target frame fill area.
502 : // aRenderSize is what we will be actually filling after accounting for
503 : // background-size.
504 0 : if (aFrame->IsFrameOfType(nsIFrame::eSVGPaintServer)) {
505 : // aFrame is either a pattern or a gradient. These fill the whole target
506 : // frame by default, so aPaintServerSize is the whole target background fill
507 : // area.
508 : nsSVGPaintServerFrame* server =
509 0 : static_cast<nsSVGPaintServerFrame*>(aFrame);
510 :
511 : gfxRect overrideBounds(0, 0,
512 0 : aPaintServerSize.width, aPaintServerSize.height);
513 0 : overrideBounds.ScaleInverse(aFrame->PresContext()->AppUnitsPerDevPixel());
514 : nsRefPtr<gfxPattern> pattern =
515 0 : server->GetPaintServerPattern(aTarget, 1.0, &overrideBounds);
516 :
517 0 : if (!pattern)
518 0 : return nsnull;
519 :
520 : // pattern is now set up to fill aPaintServerSize. But we want it to
521 : // fill aRenderSize, so we need to add a scaling transform.
522 : // We couldn't just have set overrideBounds to aRenderSize - it would have
523 : // worked for gradients, but for patterns it would result in a different
524 : // pattern size.
525 0 : gfxFloat scaleX = overrideBounds.Width() / aRenderSize.width;
526 0 : gfxFloat scaleY = overrideBounds.Height() / aRenderSize.height;
527 0 : gfxMatrix scaleMatrix = gfxMatrix().Scale(scaleX, scaleY);
528 0 : pattern->SetMatrix(scaleMatrix.Multiply(pattern->GetMatrix()));
529 : nsRefPtr<gfxDrawable> drawable =
530 0 : new gfxPatternDrawable(pattern, aRenderSize);
531 0 : return drawable.forget();
532 : }
533 :
534 : // We don't want to paint into a surface as long as we don't need to, so we
535 : // set up a drawing callback.
536 : nsRefPtr<gfxDrawingCallback> cb =
537 0 : new PaintFrameCallback(aFrame, aTarget, aPaintServerSize, aRenderSize);
538 0 : nsRefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, aRenderSize);
539 0 : return drawable.forget();
540 : }
541 :
542 : /* static */ void
543 0 : nsSVGIntegrationUtils::DrawPaintServer(nsRenderingContext* aRenderingContext,
544 : nsIFrame* aTarget,
545 : nsIFrame* aPaintServer,
546 : gfxPattern::GraphicsFilter aFilter,
547 : const nsRect& aDest,
548 : const nsRect& aFill,
549 : const nsPoint& aAnchor,
550 : const nsRect& aDirty,
551 : const nsSize& aPaintServerSize)
552 : {
553 0 : if (aDest.IsEmpty() || aFill.IsEmpty())
554 0 : return;
555 :
556 0 : PRInt32 appUnitsPerDevPixel = aTarget->PresContext()->AppUnitsPerDevPixel();
557 0 : nsRect destSize = aDest - aDest.TopLeft();
558 0 : nsIntSize roundedOut = destSize.ToOutsidePixels(appUnitsPerDevPixel).Size();
559 0 : gfxIntSize imageSize(roundedOut.width, roundedOut.height);
560 : nsRefPtr<gfxDrawable> drawable =
561 0 : DrawableFromPaintServer(aPaintServer, aTarget, aPaintServerSize, imageSize);
562 :
563 0 : if (drawable) {
564 : nsLayoutUtils::DrawPixelSnapped(aRenderingContext, drawable, aFilter,
565 0 : aDest, aFill, aAnchor, aDirty);
566 : }
567 : }
|