Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // Main header first:
7 #include "nsFilterInstance.h"
9 // Keep others in (case-insensitive) order:
10 #include "gfxPlatform.h"
11 #include "gfxUtils.h"
12 #include "nsISVGChildFrame.h"
13 #include "nsRenderingContext.h"
14 #include "nsSVGFilterInstance.h"
15 #include "nsSVGFilterPaintCallback.h"
16 #include "nsSVGUtils.h"
17 #include "SVGContentUtils.h"
18 #include "FilterSupport.h"
19 #include "gfx2DGlue.h"
21 using namespace mozilla;
22 using namespace mozilla::dom;
23 using namespace mozilla::gfx;
25 nsresult
26 nsFilterInstance::PaintFilteredFrame(nsRenderingContext *aContext,
27 nsIFrame *aFilteredFrame,
28 nsSVGFilterPaintCallback *aPaintCallback,
29 const nsRegion *aDirtyArea,
30 nsIFrame* aTransformRoot)
31 {
32 nsFilterInstance instance(aFilteredFrame, aPaintCallback, aDirtyArea,
33 nullptr, nullptr, nullptr,
34 aTransformRoot);
35 if (!instance.IsInitialized()) {
36 return NS_OK;
37 }
38 return instance.Render(aContext->ThebesContext());
39 }
41 nsRegion
42 nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
43 const nsRegion& aPreFilterDirtyRegion)
44 {
45 if (aPreFilterDirtyRegion.IsEmpty()) {
46 return nsRegion();
47 }
49 nsFilterInstance instance(aFilteredFrame, nullptr, nullptr,
50 &aPreFilterDirtyRegion);
51 if (!instance.IsInitialized()) {
52 return nsRegion();
53 }
54 // We've passed in the source's dirty area so the instance knows about it.
55 // Now we can ask the instance to compute the area of the filter output
56 // that's dirty.
57 nsRegion dirtyRegion;
58 nsresult rv = instance.ComputePostFilterDirtyRegion(&dirtyRegion);
59 if (NS_SUCCEEDED(rv)) {
60 return dirtyRegion;
61 }
62 return nsRegion();
63 }
65 nsRegion
66 nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
67 const nsRegion& aPostFilterDirtyRegion)
68 {
69 nsFilterInstance instance(aFilteredFrame, nullptr, &aPostFilterDirtyRegion);
70 if (!instance.IsInitialized()) {
71 return nsRect();
72 }
73 // Now we can ask the instance to compute the area of the source
74 // that's needed.
75 nsRect neededRect;
76 nsresult rv = instance.ComputeSourceNeededRect(&neededRect);
77 if (NS_SUCCEEDED(rv)) {
78 return neededRect;
79 }
80 return nsRegion();
81 }
83 nsRect
84 nsFilterInstance::GetPostFilterBounds(nsIFrame *aFilteredFrame,
85 const gfxRect *aOverrideBBox,
86 const nsRect *aPreFilterBounds)
87 {
88 MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
89 !(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
90 "Non-display SVG do not maintain visual overflow rects");
92 nsRegion preFilterRegion;
93 nsRegion* preFilterRegionPtr = nullptr;
94 if (aPreFilterBounds) {
95 preFilterRegion = *aPreFilterBounds;
96 preFilterRegionPtr = &preFilterRegion;
97 }
98 nsFilterInstance instance(aFilteredFrame, nullptr, nullptr,
99 preFilterRegionPtr, aPreFilterBounds,
100 aOverrideBBox);
101 if (!instance.IsInitialized()) {
102 return nsRect();
103 }
104 nsRect bbox;
105 nsresult rv = instance.ComputePostFilterExtents(&bbox);
106 if (NS_SUCCEEDED(rv)) {
107 return bbox;
108 }
109 return nsRect();
110 }
112 nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
113 nsSVGFilterPaintCallback *aPaintCallback,
114 const nsRegion *aPostFilterDirtyRegion,
115 const nsRegion *aPreFilterDirtyRegion,
116 const nsRect *aPreFilterVisualOverflowRectOverride,
117 const gfxRect *aOverrideBBox,
118 nsIFrame* aTransformRoot) :
119 mTargetFrame(aTargetFrame),
120 mPaintCallback(aPaintCallback),
121 mTransformRoot(aTransformRoot),
122 mInitialized(false) {
124 mTargetBBox = aOverrideBBox ?
125 *aOverrideBBox : nsSVGUtils::GetBBox(mTargetFrame);
127 nsresult rv = ComputeUserSpaceToFilterSpaceScale();
128 if (NS_FAILED(rv)) {
129 return;
130 }
132 rv = BuildPrimitives();
133 if (NS_FAILED(rv)) {
134 return;
135 }
137 if (mPrimitiveDescriptions.IsEmpty()) {
138 // Nothing should be rendered.
139 return;
140 }
142 // Get various transforms:
144 gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f,
145 0.0f, mFilterSpaceToUserSpaceScale.height,
146 0.0f, 0.0f);
148 // Only used (so only set) when we paint:
149 if (mPaintCallback) {
150 mFilterSpaceToDeviceSpaceTransform = filterToUserSpace *
151 nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING);
152 }
154 // Convert the passed in rects from frame to filter space:
156 mAppUnitsPerCSSPx = mTargetFrame->PresContext()->AppUnitsPerCSSPixel();
158 mFilterSpaceToFrameSpaceInCSSPxTransform =
159 filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
160 // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
161 mFrameSpaceInCSSPxToFilterSpaceTransform =
162 mFilterSpaceToFrameSpaceInCSSPxTransform;
163 mFrameSpaceInCSSPxToFilterSpaceTransform.Invert();
165 mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion);
166 mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
167 if (aPreFilterVisualOverflowRectOverride) {
168 mTargetBounds =
169 FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride);
170 } else {
171 nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect();
172 mTargetBounds = FrameSpaceToFilterSpace(&preFilterVOR);
173 }
175 mInitialized = true;
176 }
178 nsresult
179 nsFilterInstance::ComputeUserSpaceToFilterSpaceScale()
180 {
181 gfxMatrix canvasTransform =
182 nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
183 if (canvasTransform.IsSingular()) {
184 // Nothing should be rendered.
185 return NS_ERROR_FAILURE;
186 }
188 mUserSpaceToFilterSpaceScale = canvasTransform.ScaleFactors(true);
189 if (mUserSpaceToFilterSpaceScale.width <= 0.0f ||
190 mUserSpaceToFilterSpaceScale.height <= 0.0f) {
191 // Nothing should be rendered.
192 return NS_ERROR_FAILURE;
193 }
195 mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width,
196 1.0f / mUserSpaceToFilterSpaceScale.height);
197 return NS_OK;
198 }
200 gfxRect
201 nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
202 {
203 gfxRect filterSpaceRect = aUserSpaceRect;
204 filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
205 mUserSpaceToFilterSpaceScale.height);
206 return filterSpaceRect;
207 }
209 gfxRect
210 nsFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const
211 {
212 gfxRect userSpaceRect = aFilterSpaceRect;
213 userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width,
214 mFilterSpaceToUserSpaceScale.height);
215 return userSpaceRect;
216 }
218 nsresult
219 nsFilterInstance::BuildPrimitives()
220 {
221 NS_ASSERTION(!mPrimitiveDescriptions.Length(),
222 "expected to start building primitives from scratch");
224 const nsTArray<nsStyleFilter>& filters = mTargetFrame->StyleSVGReset()->mFilters;
225 for (uint32_t i = 0; i < filters.Length(); i++) {
226 nsresult rv = BuildPrimitivesForFilter(filters[i]);
227 if (NS_FAILED(rv)) {
228 return rv;
229 }
230 }
231 return NS_OK;
232 }
234 nsresult
235 nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter)
236 {
237 NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f &&
238 mFilterSpaceToUserSpaceScale.height > 0.0f,
239 "scale factors between spaces should be positive values");
241 if (aFilter.GetType() == NS_STYLE_FILTER_URL) {
242 // Build primitives for an SVG filter.
243 nsSVGFilterInstance svgFilterInstance(aFilter, mTargetFrame, mTargetBBox,
244 mUserSpaceToFilterSpaceScale,
245 mFilterSpaceToUserSpaceScale);
246 if (!svgFilterInstance.IsInitialized()) {
247 return NS_ERROR_FAILURE;
248 }
250 // For now, we use the last SVG filter region as the overall filter region
251 // for the filter chain. Eventually, we will compute the overall filter
252 // using all of the generated FilterPrimitiveDescriptions.
253 mUserSpaceBounds = svgFilterInstance.GetFilterRegion();
254 mFilterSpaceBounds = svgFilterInstance.GetFilterSpaceBounds();
256 // If this overflows, we can at least paint the maximum surface size.
257 bool overflow;
258 gfxIntSize surfaceSize =
259 nsSVGUtils::ConvertToSurfaceSize(mFilterSpaceBounds.Size(), &overflow);
260 mFilterSpaceBounds.SizeTo(surfaceSize);
262 return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages);
263 }
265 // Eventually, we will build primitives for CSS filters, too.
266 return NS_ERROR_FAILURE;
267 }
269 void
270 nsFilterInstance::ComputeNeededBoxes()
271 {
272 if (mPrimitiveDescriptions.IsEmpty())
273 return;
275 nsIntRegion sourceGraphicNeededRegion;
276 nsIntRegion fillPaintNeededRegion;
277 nsIntRegion strokePaintNeededRegion;
279 FilterDescription filter(mPrimitiveDescriptions, ToIntRect(mFilterSpaceBounds));
280 FilterSupport::ComputeSourceNeededRegions(
281 filter, mPostFilterDirtyRegion,
282 sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
284 nsIntRect sourceBoundsInt;
285 gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
286 sourceBounds.RoundOut();
287 // Detect possible float->int overflow
288 if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
289 return;
290 sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds);
292 sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, sourceBoundsInt);
294 mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds();
295 mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds();
296 mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds();
297 }
299 nsresult
300 nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
301 gfxASurface* aTargetSurface,
302 DrawTarget* aTargetDT)
303 {
304 nsIntRect neededRect = aSource->mNeededBounds;
306 RefPtr<DrawTarget> offscreenDT;
307 nsRefPtr<gfxASurface> offscreenSurface;
308 nsRefPtr<gfxContext> ctx;
309 if (aTargetSurface) {
310 offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
311 neededRect.Size().ToIntSize(), gfxContentType::COLOR_ALPHA);
312 if (!offscreenSurface || offscreenSurface->CairoStatus()) {
313 return NS_ERROR_OUT_OF_MEMORY;
314 }
315 ctx = new gfxContext(offscreenSurface);
316 } else {
317 offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
318 ToIntSize(neededRect.Size()), SurfaceFormat::B8G8R8A8);
319 if (!offscreenDT) {
320 return NS_ERROR_OUT_OF_MEMORY;
321 }
322 ctx = new gfxContext(offscreenDT);
323 }
325 ctx->Translate(-neededRect.TopLeft());
327 nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext());
328 tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
330 gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
331 gfxContext *gfx = tmpCtx->ThebesContext();
332 gfx->Multiply(deviceToFilterSpace);
334 gfx->Save();
336 gfxMatrix matrix =
337 nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING,
338 mTransformRoot);
339 if (!matrix.IsSingular()) {
340 gfx->Multiply(matrix);
341 gfx->Rectangle(mUserSpaceBounds);
342 if ((aSource == &mFillPaint &&
343 nsSVGUtils::SetupCairoFillPaint(mTargetFrame, gfx)) ||
344 (aSource == &mStrokePaint &&
345 nsSVGUtils::SetupCairoStrokePaint(mTargetFrame, gfx))) {
346 gfx->Fill();
347 }
348 }
349 gfx->Restore();
351 if (offscreenSurface) {
352 aSource->mSourceSurface =
353 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface);
354 } else {
355 aSource->mSourceSurface = offscreenDT->Snapshot();
356 }
357 aSource->mSurfaceRect = ToIntRect(neededRect);
359 return NS_OK;
360 }
362 nsresult
363 nsFilterInstance::BuildSourcePaints(gfxASurface* aTargetSurface,
364 DrawTarget* aTargetDT)
365 {
366 nsresult rv = NS_OK;
368 if (!mFillPaint.mNeededBounds.IsEmpty()) {
369 rv = BuildSourcePaint(&mFillPaint, aTargetSurface, aTargetDT);
370 NS_ENSURE_SUCCESS(rv, rv);
371 }
373 if (!mStrokePaint.mNeededBounds.IsEmpty()) {
374 rv = BuildSourcePaint(&mStrokePaint, aTargetSurface, aTargetDT);
375 NS_ENSURE_SUCCESS(rv, rv);
376 }
377 return rv;
378 }
380 nsresult
381 nsFilterInstance::BuildSourceImage(gfxASurface* aTargetSurface,
382 DrawTarget* aTargetDT)
383 {
384 nsIntRect neededRect = mSourceGraphic.mNeededBounds;
385 if (neededRect.IsEmpty()) {
386 return NS_OK;
387 }
389 RefPtr<DrawTarget> offscreenDT;
390 nsRefPtr<gfxASurface> offscreenSurface;
391 nsRefPtr<gfxContext> ctx;
392 if (aTargetSurface) {
393 offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
394 neededRect.Size().ToIntSize(), gfxContentType::COLOR_ALPHA);
395 if (!offscreenSurface || offscreenSurface->CairoStatus()) {
396 return NS_ERROR_OUT_OF_MEMORY;
397 }
398 ctx = new gfxContext(offscreenSurface);
399 } else {
400 offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
401 ToIntSize(neededRect.Size()), SurfaceFormat::B8G8R8A8);
402 if (!offscreenDT) {
403 return NS_ERROR_OUT_OF_MEMORY;
404 }
405 ctx = new gfxContext(offscreenDT);
406 }
408 ctx->Translate(-neededRect.TopLeft());
410 nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext());
411 tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
413 gfxRect r = FilterSpaceToUserSpace(neededRect);
414 r.RoundOut();
415 nsIntRect dirty;
416 if (!gfxUtils::GfxRectToIntRect(r, &dirty))
417 return NS_ERROR_FAILURE;
419 // SVG graphics paint to device space, so we need to set an initial device
420 // space to filter space transform on the gfxContext that SourceGraphic
421 // and SourceAlpha will paint to.
422 //
423 // (In theory it would be better to minimize error by having filtered SVG
424 // graphics temporarily paint to user space when painting the sources and
425 // only set a user space to filter space transform on the gfxContext
426 // (since that would eliminate the transform multiplications from user
427 // space to device space and back again). However, that would make the
428 // code more complex while being hard to get right without introducing
429 // subtle bugs, and in practice it probably makes no real difference.)
430 gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
431 tmpCtx->ThebesContext()->Multiply(deviceToFilterSpace);
432 mPaintCallback->Paint(tmpCtx, mTargetFrame, &dirty, mTransformRoot);
434 RefPtr<SourceSurface> sourceGraphicSource;
436 if (offscreenSurface) {
437 sourceGraphicSource =
438 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface);
439 } else {
440 sourceGraphicSource = offscreenDT->Snapshot();
441 }
443 mSourceGraphic.mSourceSurface = sourceGraphicSource;
444 mSourceGraphic.mSurfaceRect = ToIntRect(neededRect);
446 return NS_OK;
447 }
449 nsresult
450 nsFilterInstance::Render(gfxContext* aContext)
451 {
452 nsIntRect filterRect = mPostFilterDirtyRegion.GetBounds().Intersect(mFilterSpaceBounds);
453 gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform();
455 if (filterRect.IsEmpty() || ctm.IsSingular()) {
456 return NS_OK;
457 }
459 Matrix oldDTMatrix;
460 nsRefPtr<gfxASurface> resultImage;
461 RefPtr<DrawTarget> dt;
462 if (aContext->IsCairo()) {
463 resultImage =
464 gfxPlatform::GetPlatform()->CreateOffscreenSurface(filterRect.Size().ToIntSize(),
465 gfxContentType::COLOR_ALPHA);
466 if (!resultImage || resultImage->CairoStatus())
467 return NS_ERROR_OUT_OF_MEMORY;
469 // Create a Cairo DrawTarget around resultImage.
470 dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(
471 resultImage, ToIntSize(filterRect.Size()));
472 } else {
473 // When we have a DrawTarget-backed context, we can call DrawFilter
474 // directly on the target DrawTarget and don't need a temporary DT.
475 dt = aContext->GetDrawTarget();
476 oldDTMatrix = dt->GetTransform();
477 Matrix matrix = ToMatrix(ctm);
478 matrix.Translate(filterRect.x, filterRect.y);
479 dt->SetTransform(matrix * oldDTMatrix);
480 }
482 ComputeNeededBoxes();
484 nsresult rv = BuildSourceImage(resultImage, dt);
485 if (NS_FAILED(rv))
486 return rv;
487 rv = BuildSourcePaints(resultImage, dt);
488 if (NS_FAILED(rv))
489 return rv;
491 IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds);
492 FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
494 FilterSupport::RenderFilterDescription(
495 dt, filter, ToRect(filterRect),
496 mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
497 mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
498 mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
499 mInputImages);
501 if (resultImage) {
502 aContext->Save();
503 aContext->Multiply(ctm);
504 aContext->Translate(filterRect.TopLeft());
505 aContext->SetSource(resultImage);
506 aContext->Paint();
507 aContext->Restore();
508 } else {
509 dt->SetTransform(oldDTMatrix);
510 }
512 return NS_OK;
513 }
515 nsresult
516 nsFilterInstance::ComputePostFilterDirtyRegion(nsRegion* aPostFilterDirtyRegion)
517 {
518 *aPostFilterDirtyRegion = nsRegion();
519 if (mPreFilterDirtyRegion.IsEmpty()) {
520 return NS_OK;
521 }
523 IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds);
524 FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
525 nsIntRegion resultChangeRegion =
526 FilterSupport::ComputeResultChangeRegion(filter,
527 mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion());
528 *aPostFilterDirtyRegion =
529 FilterSpaceToFrameSpace(resultChangeRegion);
530 return NS_OK;
531 }
533 nsresult
534 nsFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents)
535 {
536 *aPostFilterExtents = nsRect();
538 nsIntRect sourceBoundsInt;
539 gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
540 sourceBounds.RoundOut();
541 // Detect possible float->int overflow
542 if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
543 return NS_ERROR_FAILURE;
544 sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds);
546 IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds);
547 FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
548 nsIntRegion postFilterExtents =
549 FilterSupport::ComputePostFilterExtents(filter, sourceBoundsInt);
550 *aPostFilterExtents = FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
551 return NS_OK;
552 }
554 nsresult
555 nsFilterInstance::ComputeSourceNeededRect(nsRect* aDirty)
556 {
557 ComputeNeededBoxes();
558 *aDirty = FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds);
560 return NS_OK;
561 }
563 nsIntRect
564 nsFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
565 {
566 nsIntRect rect = mFilterSpaceBounds;
567 if (aRect) {
568 if (aRect->IsEmpty()) {
569 return nsIntRect();
570 }
571 gfxRect rectInCSSPx =
572 nsLayoutUtils::RectToGfxRect(*aRect, mAppUnitsPerCSSPx);
573 gfxRect rectInFilterSpace =
574 mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx);
575 rectInFilterSpace.RoundOut();
576 nsIntRect intRect;
577 if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
578 rect = intRect;
579 }
580 }
581 return rect;
582 }
584 nsRect
585 nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const
586 {
587 if (aRect.IsEmpty()) {
588 return nsRect();
589 }
590 gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
591 r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
592 // nsLayoutUtils::RoundGfxRectToAppRect rounds out.
593 return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx);
594 }
596 nsIntRegion
597 nsFilterInstance::FrameSpaceToFilterSpace(const nsRegion* aRegion) const
598 {
599 if (!aRegion) {
600 return mFilterSpaceBounds;
601 }
602 nsIntRegion result;
603 nsRegionRectIterator it(*aRegion);
604 while (const nsRect* r = it.Next()) {
605 // FrameSpaceToFilterSpace rounds out, so this works.
606 result.Or(result, FrameSpaceToFilterSpace(r));
607 }
608 return result;
609 }
611 nsRegion
612 nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRegion& aRegion) const
613 {
614 nsRegion result;
615 nsIntRegionRectIterator it(aRegion);
616 while (const nsIntRect* r = it.Next()) {
617 // FilterSpaceToFrameSpace rounds out, so this works.
618 result.Or(result, FilterSpaceToFrameSpace(*r));
619 }
620 return result;
621 }
623 gfxMatrix
624 nsFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const
625 {
626 return gfxMatrix().Translate(-nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mTargetFrame));
627 }