|
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/. */ |
|
5 |
|
6 // Main header first: |
|
7 #include "nsFilterInstance.h" |
|
8 |
|
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" |
|
20 |
|
21 using namespace mozilla; |
|
22 using namespace mozilla::dom; |
|
23 using namespace mozilla::gfx; |
|
24 |
|
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 } |
|
40 |
|
41 nsRegion |
|
42 nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame, |
|
43 const nsRegion& aPreFilterDirtyRegion) |
|
44 { |
|
45 if (aPreFilterDirtyRegion.IsEmpty()) { |
|
46 return nsRegion(); |
|
47 } |
|
48 |
|
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 } |
|
64 |
|
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 } |
|
82 |
|
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"); |
|
91 |
|
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 } |
|
111 |
|
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) { |
|
123 |
|
124 mTargetBBox = aOverrideBBox ? |
|
125 *aOverrideBBox : nsSVGUtils::GetBBox(mTargetFrame); |
|
126 |
|
127 nsresult rv = ComputeUserSpaceToFilterSpaceScale(); |
|
128 if (NS_FAILED(rv)) { |
|
129 return; |
|
130 } |
|
131 |
|
132 rv = BuildPrimitives(); |
|
133 if (NS_FAILED(rv)) { |
|
134 return; |
|
135 } |
|
136 |
|
137 if (mPrimitiveDescriptions.IsEmpty()) { |
|
138 // Nothing should be rendered. |
|
139 return; |
|
140 } |
|
141 |
|
142 // Get various transforms: |
|
143 |
|
144 gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f, |
|
145 0.0f, mFilterSpaceToUserSpaceScale.height, |
|
146 0.0f, 0.0f); |
|
147 |
|
148 // Only used (so only set) when we paint: |
|
149 if (mPaintCallback) { |
|
150 mFilterSpaceToDeviceSpaceTransform = filterToUserSpace * |
|
151 nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING); |
|
152 } |
|
153 |
|
154 // Convert the passed in rects from frame to filter space: |
|
155 |
|
156 mAppUnitsPerCSSPx = mTargetFrame->PresContext()->AppUnitsPerCSSPixel(); |
|
157 |
|
158 mFilterSpaceToFrameSpaceInCSSPxTransform = |
|
159 filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform(); |
|
160 // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible |
|
161 mFrameSpaceInCSSPxToFilterSpaceTransform = |
|
162 mFilterSpaceToFrameSpaceInCSSPxTransform; |
|
163 mFrameSpaceInCSSPxToFilterSpaceTransform.Invert(); |
|
164 |
|
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 } |
|
174 |
|
175 mInitialized = true; |
|
176 } |
|
177 |
|
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 } |
|
187 |
|
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 } |
|
194 |
|
195 mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width, |
|
196 1.0f / mUserSpaceToFilterSpaceScale.height); |
|
197 return NS_OK; |
|
198 } |
|
199 |
|
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 } |
|
208 |
|
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 } |
|
217 |
|
218 nsresult |
|
219 nsFilterInstance::BuildPrimitives() |
|
220 { |
|
221 NS_ASSERTION(!mPrimitiveDescriptions.Length(), |
|
222 "expected to start building primitives from scratch"); |
|
223 |
|
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 } |
|
233 |
|
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"); |
|
240 |
|
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 } |
|
249 |
|
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(); |
|
255 |
|
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); |
|
261 |
|
262 return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages); |
|
263 } |
|
264 |
|
265 // Eventually, we will build primitives for CSS filters, too. |
|
266 return NS_ERROR_FAILURE; |
|
267 } |
|
268 |
|
269 void |
|
270 nsFilterInstance::ComputeNeededBoxes() |
|
271 { |
|
272 if (mPrimitiveDescriptions.IsEmpty()) |
|
273 return; |
|
274 |
|
275 nsIntRegion sourceGraphicNeededRegion; |
|
276 nsIntRegion fillPaintNeededRegion; |
|
277 nsIntRegion strokePaintNeededRegion; |
|
278 |
|
279 FilterDescription filter(mPrimitiveDescriptions, ToIntRect(mFilterSpaceBounds)); |
|
280 FilterSupport::ComputeSourceNeededRegions( |
|
281 filter, mPostFilterDirtyRegion, |
|
282 sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion); |
|
283 |
|
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); |
|
291 |
|
292 sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, sourceBoundsInt); |
|
293 |
|
294 mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds(); |
|
295 mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds(); |
|
296 mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds(); |
|
297 } |
|
298 |
|
299 nsresult |
|
300 nsFilterInstance::BuildSourcePaint(SourceInfo *aSource, |
|
301 gfxASurface* aTargetSurface, |
|
302 DrawTarget* aTargetDT) |
|
303 { |
|
304 nsIntRect neededRect = aSource->mNeededBounds; |
|
305 |
|
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 } |
|
324 |
|
325 ctx->Translate(-neededRect.TopLeft()); |
|
326 |
|
327 nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext()); |
|
328 tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx); |
|
329 |
|
330 gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert(); |
|
331 gfxContext *gfx = tmpCtx->ThebesContext(); |
|
332 gfx->Multiply(deviceToFilterSpace); |
|
333 |
|
334 gfx->Save(); |
|
335 |
|
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(); |
|
350 |
|
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); |
|
358 |
|
359 return NS_OK; |
|
360 } |
|
361 |
|
362 nsresult |
|
363 nsFilterInstance::BuildSourcePaints(gfxASurface* aTargetSurface, |
|
364 DrawTarget* aTargetDT) |
|
365 { |
|
366 nsresult rv = NS_OK; |
|
367 |
|
368 if (!mFillPaint.mNeededBounds.IsEmpty()) { |
|
369 rv = BuildSourcePaint(&mFillPaint, aTargetSurface, aTargetDT); |
|
370 NS_ENSURE_SUCCESS(rv, rv); |
|
371 } |
|
372 |
|
373 if (!mStrokePaint.mNeededBounds.IsEmpty()) { |
|
374 rv = BuildSourcePaint(&mStrokePaint, aTargetSurface, aTargetDT); |
|
375 NS_ENSURE_SUCCESS(rv, rv); |
|
376 } |
|
377 return rv; |
|
378 } |
|
379 |
|
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 } |
|
388 |
|
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 } |
|
407 |
|
408 ctx->Translate(-neededRect.TopLeft()); |
|
409 |
|
410 nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext()); |
|
411 tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx); |
|
412 |
|
413 gfxRect r = FilterSpaceToUserSpace(neededRect); |
|
414 r.RoundOut(); |
|
415 nsIntRect dirty; |
|
416 if (!gfxUtils::GfxRectToIntRect(r, &dirty)) |
|
417 return NS_ERROR_FAILURE; |
|
418 |
|
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); |
|
433 |
|
434 RefPtr<SourceSurface> sourceGraphicSource; |
|
435 |
|
436 if (offscreenSurface) { |
|
437 sourceGraphicSource = |
|
438 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface); |
|
439 } else { |
|
440 sourceGraphicSource = offscreenDT->Snapshot(); |
|
441 } |
|
442 |
|
443 mSourceGraphic.mSourceSurface = sourceGraphicSource; |
|
444 mSourceGraphic.mSurfaceRect = ToIntRect(neededRect); |
|
445 |
|
446 return NS_OK; |
|
447 } |
|
448 |
|
449 nsresult |
|
450 nsFilterInstance::Render(gfxContext* aContext) |
|
451 { |
|
452 nsIntRect filterRect = mPostFilterDirtyRegion.GetBounds().Intersect(mFilterSpaceBounds); |
|
453 gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform(); |
|
454 |
|
455 if (filterRect.IsEmpty() || ctm.IsSingular()) { |
|
456 return NS_OK; |
|
457 } |
|
458 |
|
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; |
|
468 |
|
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 } |
|
481 |
|
482 ComputeNeededBoxes(); |
|
483 |
|
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; |
|
490 |
|
491 IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds); |
|
492 FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds); |
|
493 |
|
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); |
|
500 |
|
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 } |
|
511 |
|
512 return NS_OK; |
|
513 } |
|
514 |
|
515 nsresult |
|
516 nsFilterInstance::ComputePostFilterDirtyRegion(nsRegion* aPostFilterDirtyRegion) |
|
517 { |
|
518 *aPostFilterDirtyRegion = nsRegion(); |
|
519 if (mPreFilterDirtyRegion.IsEmpty()) { |
|
520 return NS_OK; |
|
521 } |
|
522 |
|
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 } |
|
532 |
|
533 nsresult |
|
534 nsFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents) |
|
535 { |
|
536 *aPostFilterExtents = nsRect(); |
|
537 |
|
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); |
|
545 |
|
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 } |
|
553 |
|
554 nsresult |
|
555 nsFilterInstance::ComputeSourceNeededRect(nsRect* aDirty) |
|
556 { |
|
557 ComputeNeededBoxes(); |
|
558 *aDirty = FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds); |
|
559 |
|
560 return NS_OK; |
|
561 } |
|
562 |
|
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 } |
|
583 |
|
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 } |
|
595 |
|
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 } |
|
610 |
|
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 } |
|
622 |
|
623 gfxMatrix |
|
624 nsFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const |
|
625 { |
|
626 return gfxMatrix().Translate(-nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mTargetFrame)); |
|
627 } |