1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsFilterInstance.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,627 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Main header first: 1.10 +#include "nsFilterInstance.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "gfxPlatform.h" 1.14 +#include "gfxUtils.h" 1.15 +#include "nsISVGChildFrame.h" 1.16 +#include "nsRenderingContext.h" 1.17 +#include "nsSVGFilterInstance.h" 1.18 +#include "nsSVGFilterPaintCallback.h" 1.19 +#include "nsSVGUtils.h" 1.20 +#include "SVGContentUtils.h" 1.21 +#include "FilterSupport.h" 1.22 +#include "gfx2DGlue.h" 1.23 + 1.24 +using namespace mozilla; 1.25 +using namespace mozilla::dom; 1.26 +using namespace mozilla::gfx; 1.27 + 1.28 +nsresult 1.29 +nsFilterInstance::PaintFilteredFrame(nsRenderingContext *aContext, 1.30 + nsIFrame *aFilteredFrame, 1.31 + nsSVGFilterPaintCallback *aPaintCallback, 1.32 + const nsRegion *aDirtyArea, 1.33 + nsIFrame* aTransformRoot) 1.34 +{ 1.35 + nsFilterInstance instance(aFilteredFrame, aPaintCallback, aDirtyArea, 1.36 + nullptr, nullptr, nullptr, 1.37 + aTransformRoot); 1.38 + if (!instance.IsInitialized()) { 1.39 + return NS_OK; 1.40 + } 1.41 + return instance.Render(aContext->ThebesContext()); 1.42 +} 1.43 + 1.44 +nsRegion 1.45 +nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame, 1.46 + const nsRegion& aPreFilterDirtyRegion) 1.47 +{ 1.48 + if (aPreFilterDirtyRegion.IsEmpty()) { 1.49 + return nsRegion(); 1.50 + } 1.51 + 1.52 + nsFilterInstance instance(aFilteredFrame, nullptr, nullptr, 1.53 + &aPreFilterDirtyRegion); 1.54 + if (!instance.IsInitialized()) { 1.55 + return nsRegion(); 1.56 + } 1.57 + // We've passed in the source's dirty area so the instance knows about it. 1.58 + // Now we can ask the instance to compute the area of the filter output 1.59 + // that's dirty. 1.60 + nsRegion dirtyRegion; 1.61 + nsresult rv = instance.ComputePostFilterDirtyRegion(&dirtyRegion); 1.62 + if (NS_SUCCEEDED(rv)) { 1.63 + return dirtyRegion; 1.64 + } 1.65 + return nsRegion(); 1.66 +} 1.67 + 1.68 +nsRegion 1.69 +nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame, 1.70 + const nsRegion& aPostFilterDirtyRegion) 1.71 +{ 1.72 + nsFilterInstance instance(aFilteredFrame, nullptr, &aPostFilterDirtyRegion); 1.73 + if (!instance.IsInitialized()) { 1.74 + return nsRect(); 1.75 + } 1.76 + // Now we can ask the instance to compute the area of the source 1.77 + // that's needed. 1.78 + nsRect neededRect; 1.79 + nsresult rv = instance.ComputeSourceNeededRect(&neededRect); 1.80 + if (NS_SUCCEEDED(rv)) { 1.81 + return neededRect; 1.82 + } 1.83 + return nsRegion(); 1.84 +} 1.85 + 1.86 +nsRect 1.87 +nsFilterInstance::GetPostFilterBounds(nsIFrame *aFilteredFrame, 1.88 + const gfxRect *aOverrideBBox, 1.89 + const nsRect *aPreFilterBounds) 1.90 +{ 1.91 + MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || 1.92 + !(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY), 1.93 + "Non-display SVG do not maintain visual overflow rects"); 1.94 + 1.95 + nsRegion preFilterRegion; 1.96 + nsRegion* preFilterRegionPtr = nullptr; 1.97 + if (aPreFilterBounds) { 1.98 + preFilterRegion = *aPreFilterBounds; 1.99 + preFilterRegionPtr = &preFilterRegion; 1.100 + } 1.101 + nsFilterInstance instance(aFilteredFrame, nullptr, nullptr, 1.102 + preFilterRegionPtr, aPreFilterBounds, 1.103 + aOverrideBBox); 1.104 + if (!instance.IsInitialized()) { 1.105 + return nsRect(); 1.106 + } 1.107 + nsRect bbox; 1.108 + nsresult rv = instance.ComputePostFilterExtents(&bbox); 1.109 + if (NS_SUCCEEDED(rv)) { 1.110 + return bbox; 1.111 + } 1.112 + return nsRect(); 1.113 +} 1.114 + 1.115 +nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame, 1.116 + nsSVGFilterPaintCallback *aPaintCallback, 1.117 + const nsRegion *aPostFilterDirtyRegion, 1.118 + const nsRegion *aPreFilterDirtyRegion, 1.119 + const nsRect *aPreFilterVisualOverflowRectOverride, 1.120 + const gfxRect *aOverrideBBox, 1.121 + nsIFrame* aTransformRoot) : 1.122 + mTargetFrame(aTargetFrame), 1.123 + mPaintCallback(aPaintCallback), 1.124 + mTransformRoot(aTransformRoot), 1.125 + mInitialized(false) { 1.126 + 1.127 + mTargetBBox = aOverrideBBox ? 1.128 + *aOverrideBBox : nsSVGUtils::GetBBox(mTargetFrame); 1.129 + 1.130 + nsresult rv = ComputeUserSpaceToFilterSpaceScale(); 1.131 + if (NS_FAILED(rv)) { 1.132 + return; 1.133 + } 1.134 + 1.135 + rv = BuildPrimitives(); 1.136 + if (NS_FAILED(rv)) { 1.137 + return; 1.138 + } 1.139 + 1.140 + if (mPrimitiveDescriptions.IsEmpty()) { 1.141 + // Nothing should be rendered. 1.142 + return; 1.143 + } 1.144 + 1.145 + // Get various transforms: 1.146 + 1.147 + gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f, 1.148 + 0.0f, mFilterSpaceToUserSpaceScale.height, 1.149 + 0.0f, 0.0f); 1.150 + 1.151 + // Only used (so only set) when we paint: 1.152 + if (mPaintCallback) { 1.153 + mFilterSpaceToDeviceSpaceTransform = filterToUserSpace * 1.154 + nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING); 1.155 + } 1.156 + 1.157 + // Convert the passed in rects from frame to filter space: 1.158 + 1.159 + mAppUnitsPerCSSPx = mTargetFrame->PresContext()->AppUnitsPerCSSPixel(); 1.160 + 1.161 + mFilterSpaceToFrameSpaceInCSSPxTransform = 1.162 + filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform(); 1.163 + // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible 1.164 + mFrameSpaceInCSSPxToFilterSpaceTransform = 1.165 + mFilterSpaceToFrameSpaceInCSSPxTransform; 1.166 + mFrameSpaceInCSSPxToFilterSpaceTransform.Invert(); 1.167 + 1.168 + mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion); 1.169 + mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion); 1.170 + if (aPreFilterVisualOverflowRectOverride) { 1.171 + mTargetBounds = 1.172 + FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride); 1.173 + } else { 1.174 + nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect(); 1.175 + mTargetBounds = FrameSpaceToFilterSpace(&preFilterVOR); 1.176 + } 1.177 + 1.178 + mInitialized = true; 1.179 +} 1.180 + 1.181 +nsresult 1.182 +nsFilterInstance::ComputeUserSpaceToFilterSpaceScale() 1.183 +{ 1.184 + gfxMatrix canvasTransform = 1.185 + nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_OUTERSVG_TM); 1.186 + if (canvasTransform.IsSingular()) { 1.187 + // Nothing should be rendered. 1.188 + return NS_ERROR_FAILURE; 1.189 + } 1.190 + 1.191 + mUserSpaceToFilterSpaceScale = canvasTransform.ScaleFactors(true); 1.192 + if (mUserSpaceToFilterSpaceScale.width <= 0.0f || 1.193 + mUserSpaceToFilterSpaceScale.height <= 0.0f) { 1.194 + // Nothing should be rendered. 1.195 + return NS_ERROR_FAILURE; 1.196 + } 1.197 + 1.198 + mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width, 1.199 + 1.0f / mUserSpaceToFilterSpaceScale.height); 1.200 + return NS_OK; 1.201 +} 1.202 + 1.203 +gfxRect 1.204 +nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const 1.205 +{ 1.206 + gfxRect filterSpaceRect = aUserSpaceRect; 1.207 + filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width, 1.208 + mUserSpaceToFilterSpaceScale.height); 1.209 + return filterSpaceRect; 1.210 +} 1.211 + 1.212 +gfxRect 1.213 +nsFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const 1.214 +{ 1.215 + gfxRect userSpaceRect = aFilterSpaceRect; 1.216 + userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width, 1.217 + mFilterSpaceToUserSpaceScale.height); 1.218 + return userSpaceRect; 1.219 +} 1.220 + 1.221 +nsresult 1.222 +nsFilterInstance::BuildPrimitives() 1.223 +{ 1.224 + NS_ASSERTION(!mPrimitiveDescriptions.Length(), 1.225 + "expected to start building primitives from scratch"); 1.226 + 1.227 + const nsTArray<nsStyleFilter>& filters = mTargetFrame->StyleSVGReset()->mFilters; 1.228 + for (uint32_t i = 0; i < filters.Length(); i++) { 1.229 + nsresult rv = BuildPrimitivesForFilter(filters[i]); 1.230 + if (NS_FAILED(rv)) { 1.231 + return rv; 1.232 + } 1.233 + } 1.234 + return NS_OK; 1.235 +} 1.236 + 1.237 +nsresult 1.238 +nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter) 1.239 +{ 1.240 + NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f && 1.241 + mFilterSpaceToUserSpaceScale.height > 0.0f, 1.242 + "scale factors between spaces should be positive values"); 1.243 + 1.244 + if (aFilter.GetType() == NS_STYLE_FILTER_URL) { 1.245 + // Build primitives for an SVG filter. 1.246 + nsSVGFilterInstance svgFilterInstance(aFilter, mTargetFrame, mTargetBBox, 1.247 + mUserSpaceToFilterSpaceScale, 1.248 + mFilterSpaceToUserSpaceScale); 1.249 + if (!svgFilterInstance.IsInitialized()) { 1.250 + return NS_ERROR_FAILURE; 1.251 + } 1.252 + 1.253 + // For now, we use the last SVG filter region as the overall filter region 1.254 + // for the filter chain. Eventually, we will compute the overall filter 1.255 + // using all of the generated FilterPrimitiveDescriptions. 1.256 + mUserSpaceBounds = svgFilterInstance.GetFilterRegion(); 1.257 + mFilterSpaceBounds = svgFilterInstance.GetFilterSpaceBounds(); 1.258 + 1.259 + // If this overflows, we can at least paint the maximum surface size. 1.260 + bool overflow; 1.261 + gfxIntSize surfaceSize = 1.262 + nsSVGUtils::ConvertToSurfaceSize(mFilterSpaceBounds.Size(), &overflow); 1.263 + mFilterSpaceBounds.SizeTo(surfaceSize); 1.264 + 1.265 + return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages); 1.266 + } 1.267 + 1.268 + // Eventually, we will build primitives for CSS filters, too. 1.269 + return NS_ERROR_FAILURE; 1.270 +} 1.271 + 1.272 +void 1.273 +nsFilterInstance::ComputeNeededBoxes() 1.274 +{ 1.275 + if (mPrimitiveDescriptions.IsEmpty()) 1.276 + return; 1.277 + 1.278 + nsIntRegion sourceGraphicNeededRegion; 1.279 + nsIntRegion fillPaintNeededRegion; 1.280 + nsIntRegion strokePaintNeededRegion; 1.281 + 1.282 + FilterDescription filter(mPrimitiveDescriptions, ToIntRect(mFilterSpaceBounds)); 1.283 + FilterSupport::ComputeSourceNeededRegions( 1.284 + filter, mPostFilterDirtyRegion, 1.285 + sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion); 1.286 + 1.287 + nsIntRect sourceBoundsInt; 1.288 + gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox); 1.289 + sourceBounds.RoundOut(); 1.290 + // Detect possible float->int overflow 1.291 + if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt)) 1.292 + return; 1.293 + sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds); 1.294 + 1.295 + sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, sourceBoundsInt); 1.296 + 1.297 + mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds(); 1.298 + mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds(); 1.299 + mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds(); 1.300 +} 1.301 + 1.302 +nsresult 1.303 +nsFilterInstance::BuildSourcePaint(SourceInfo *aSource, 1.304 + gfxASurface* aTargetSurface, 1.305 + DrawTarget* aTargetDT) 1.306 +{ 1.307 + nsIntRect neededRect = aSource->mNeededBounds; 1.308 + 1.309 + RefPtr<DrawTarget> offscreenDT; 1.310 + nsRefPtr<gfxASurface> offscreenSurface; 1.311 + nsRefPtr<gfxContext> ctx; 1.312 + if (aTargetSurface) { 1.313 + offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface( 1.314 + neededRect.Size().ToIntSize(), gfxContentType::COLOR_ALPHA); 1.315 + if (!offscreenSurface || offscreenSurface->CairoStatus()) { 1.316 + return NS_ERROR_OUT_OF_MEMORY; 1.317 + } 1.318 + ctx = new gfxContext(offscreenSurface); 1.319 + } else { 1.320 + offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( 1.321 + ToIntSize(neededRect.Size()), SurfaceFormat::B8G8R8A8); 1.322 + if (!offscreenDT) { 1.323 + return NS_ERROR_OUT_OF_MEMORY; 1.324 + } 1.325 + ctx = new gfxContext(offscreenDT); 1.326 + } 1.327 + 1.328 + ctx->Translate(-neededRect.TopLeft()); 1.329 + 1.330 + nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext()); 1.331 + tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx); 1.332 + 1.333 + gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert(); 1.334 + gfxContext *gfx = tmpCtx->ThebesContext(); 1.335 + gfx->Multiply(deviceToFilterSpace); 1.336 + 1.337 + gfx->Save(); 1.338 + 1.339 + gfxMatrix matrix = 1.340 + nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING, 1.341 + mTransformRoot); 1.342 + if (!matrix.IsSingular()) { 1.343 + gfx->Multiply(matrix); 1.344 + gfx->Rectangle(mUserSpaceBounds); 1.345 + if ((aSource == &mFillPaint && 1.346 + nsSVGUtils::SetupCairoFillPaint(mTargetFrame, gfx)) || 1.347 + (aSource == &mStrokePaint && 1.348 + nsSVGUtils::SetupCairoStrokePaint(mTargetFrame, gfx))) { 1.349 + gfx->Fill(); 1.350 + } 1.351 + } 1.352 + gfx->Restore(); 1.353 + 1.354 + if (offscreenSurface) { 1.355 + aSource->mSourceSurface = 1.356 + gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface); 1.357 + } else { 1.358 + aSource->mSourceSurface = offscreenDT->Snapshot(); 1.359 + } 1.360 + aSource->mSurfaceRect = ToIntRect(neededRect); 1.361 + 1.362 + return NS_OK; 1.363 +} 1.364 + 1.365 +nsresult 1.366 +nsFilterInstance::BuildSourcePaints(gfxASurface* aTargetSurface, 1.367 + DrawTarget* aTargetDT) 1.368 +{ 1.369 + nsresult rv = NS_OK; 1.370 + 1.371 + if (!mFillPaint.mNeededBounds.IsEmpty()) { 1.372 + rv = BuildSourcePaint(&mFillPaint, aTargetSurface, aTargetDT); 1.373 + NS_ENSURE_SUCCESS(rv, rv); 1.374 + } 1.375 + 1.376 + if (!mStrokePaint.mNeededBounds.IsEmpty()) { 1.377 + rv = BuildSourcePaint(&mStrokePaint, aTargetSurface, aTargetDT); 1.378 + NS_ENSURE_SUCCESS(rv, rv); 1.379 + } 1.380 + return rv; 1.381 +} 1.382 + 1.383 +nsresult 1.384 +nsFilterInstance::BuildSourceImage(gfxASurface* aTargetSurface, 1.385 + DrawTarget* aTargetDT) 1.386 +{ 1.387 + nsIntRect neededRect = mSourceGraphic.mNeededBounds; 1.388 + if (neededRect.IsEmpty()) { 1.389 + return NS_OK; 1.390 + } 1.391 + 1.392 + RefPtr<DrawTarget> offscreenDT; 1.393 + nsRefPtr<gfxASurface> offscreenSurface; 1.394 + nsRefPtr<gfxContext> ctx; 1.395 + if (aTargetSurface) { 1.396 + offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface( 1.397 + neededRect.Size().ToIntSize(), gfxContentType::COLOR_ALPHA); 1.398 + if (!offscreenSurface || offscreenSurface->CairoStatus()) { 1.399 + return NS_ERROR_OUT_OF_MEMORY; 1.400 + } 1.401 + ctx = new gfxContext(offscreenSurface); 1.402 + } else { 1.403 + offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( 1.404 + ToIntSize(neededRect.Size()), SurfaceFormat::B8G8R8A8); 1.405 + if (!offscreenDT) { 1.406 + return NS_ERROR_OUT_OF_MEMORY; 1.407 + } 1.408 + ctx = new gfxContext(offscreenDT); 1.409 + } 1.410 + 1.411 + ctx->Translate(-neededRect.TopLeft()); 1.412 + 1.413 + nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext()); 1.414 + tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx); 1.415 + 1.416 + gfxRect r = FilterSpaceToUserSpace(neededRect); 1.417 + r.RoundOut(); 1.418 + nsIntRect dirty; 1.419 + if (!gfxUtils::GfxRectToIntRect(r, &dirty)) 1.420 + return NS_ERROR_FAILURE; 1.421 + 1.422 + // SVG graphics paint to device space, so we need to set an initial device 1.423 + // space to filter space transform on the gfxContext that SourceGraphic 1.424 + // and SourceAlpha will paint to. 1.425 + // 1.426 + // (In theory it would be better to minimize error by having filtered SVG 1.427 + // graphics temporarily paint to user space when painting the sources and 1.428 + // only set a user space to filter space transform on the gfxContext 1.429 + // (since that would eliminate the transform multiplications from user 1.430 + // space to device space and back again). However, that would make the 1.431 + // code more complex while being hard to get right without introducing 1.432 + // subtle bugs, and in practice it probably makes no real difference.) 1.433 + gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert(); 1.434 + tmpCtx->ThebesContext()->Multiply(deviceToFilterSpace); 1.435 + mPaintCallback->Paint(tmpCtx, mTargetFrame, &dirty, mTransformRoot); 1.436 + 1.437 + RefPtr<SourceSurface> sourceGraphicSource; 1.438 + 1.439 + if (offscreenSurface) { 1.440 + sourceGraphicSource = 1.441 + gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface); 1.442 + } else { 1.443 + sourceGraphicSource = offscreenDT->Snapshot(); 1.444 + } 1.445 + 1.446 + mSourceGraphic.mSourceSurface = sourceGraphicSource; 1.447 + mSourceGraphic.mSurfaceRect = ToIntRect(neededRect); 1.448 + 1.449 + return NS_OK; 1.450 +} 1.451 + 1.452 +nsresult 1.453 +nsFilterInstance::Render(gfxContext* aContext) 1.454 +{ 1.455 + nsIntRect filterRect = mPostFilterDirtyRegion.GetBounds().Intersect(mFilterSpaceBounds); 1.456 + gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform(); 1.457 + 1.458 + if (filterRect.IsEmpty() || ctm.IsSingular()) { 1.459 + return NS_OK; 1.460 + } 1.461 + 1.462 + Matrix oldDTMatrix; 1.463 + nsRefPtr<gfxASurface> resultImage; 1.464 + RefPtr<DrawTarget> dt; 1.465 + if (aContext->IsCairo()) { 1.466 + resultImage = 1.467 + gfxPlatform::GetPlatform()->CreateOffscreenSurface(filterRect.Size().ToIntSize(), 1.468 + gfxContentType::COLOR_ALPHA); 1.469 + if (!resultImage || resultImage->CairoStatus()) 1.470 + return NS_ERROR_OUT_OF_MEMORY; 1.471 + 1.472 + // Create a Cairo DrawTarget around resultImage. 1.473 + dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface( 1.474 + resultImage, ToIntSize(filterRect.Size())); 1.475 + } else { 1.476 + // When we have a DrawTarget-backed context, we can call DrawFilter 1.477 + // directly on the target DrawTarget and don't need a temporary DT. 1.478 + dt = aContext->GetDrawTarget(); 1.479 + oldDTMatrix = dt->GetTransform(); 1.480 + Matrix matrix = ToMatrix(ctm); 1.481 + matrix.Translate(filterRect.x, filterRect.y); 1.482 + dt->SetTransform(matrix * oldDTMatrix); 1.483 + } 1.484 + 1.485 + ComputeNeededBoxes(); 1.486 + 1.487 + nsresult rv = BuildSourceImage(resultImage, dt); 1.488 + if (NS_FAILED(rv)) 1.489 + return rv; 1.490 + rv = BuildSourcePaints(resultImage, dt); 1.491 + if (NS_FAILED(rv)) 1.492 + return rv; 1.493 + 1.494 + IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds); 1.495 + FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds); 1.496 + 1.497 + FilterSupport::RenderFilterDescription( 1.498 + dt, filter, ToRect(filterRect), 1.499 + mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect, 1.500 + mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect, 1.501 + mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect, 1.502 + mInputImages); 1.503 + 1.504 + if (resultImage) { 1.505 + aContext->Save(); 1.506 + aContext->Multiply(ctm); 1.507 + aContext->Translate(filterRect.TopLeft()); 1.508 + aContext->SetSource(resultImage); 1.509 + aContext->Paint(); 1.510 + aContext->Restore(); 1.511 + } else { 1.512 + dt->SetTransform(oldDTMatrix); 1.513 + } 1.514 + 1.515 + return NS_OK; 1.516 +} 1.517 + 1.518 +nsresult 1.519 +nsFilterInstance::ComputePostFilterDirtyRegion(nsRegion* aPostFilterDirtyRegion) 1.520 +{ 1.521 + *aPostFilterDirtyRegion = nsRegion(); 1.522 + if (mPreFilterDirtyRegion.IsEmpty()) { 1.523 + return NS_OK; 1.524 + } 1.525 + 1.526 + IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds); 1.527 + FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds); 1.528 + nsIntRegion resultChangeRegion = 1.529 + FilterSupport::ComputeResultChangeRegion(filter, 1.530 + mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion()); 1.531 + *aPostFilterDirtyRegion = 1.532 + FilterSpaceToFrameSpace(resultChangeRegion); 1.533 + return NS_OK; 1.534 +} 1.535 + 1.536 +nsresult 1.537 +nsFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents) 1.538 +{ 1.539 + *aPostFilterExtents = nsRect(); 1.540 + 1.541 + nsIntRect sourceBoundsInt; 1.542 + gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox); 1.543 + sourceBounds.RoundOut(); 1.544 + // Detect possible float->int overflow 1.545 + if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt)) 1.546 + return NS_ERROR_FAILURE; 1.547 + sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds); 1.548 + 1.549 + IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds); 1.550 + FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds); 1.551 + nsIntRegion postFilterExtents = 1.552 + FilterSupport::ComputePostFilterExtents(filter, sourceBoundsInt); 1.553 + *aPostFilterExtents = FilterSpaceToFrameSpace(postFilterExtents.GetBounds()); 1.554 + return NS_OK; 1.555 +} 1.556 + 1.557 +nsresult 1.558 +nsFilterInstance::ComputeSourceNeededRect(nsRect* aDirty) 1.559 +{ 1.560 + ComputeNeededBoxes(); 1.561 + *aDirty = FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds); 1.562 + 1.563 + return NS_OK; 1.564 +} 1.565 + 1.566 +nsIntRect 1.567 +nsFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const 1.568 +{ 1.569 + nsIntRect rect = mFilterSpaceBounds; 1.570 + if (aRect) { 1.571 + if (aRect->IsEmpty()) { 1.572 + return nsIntRect(); 1.573 + } 1.574 + gfxRect rectInCSSPx = 1.575 + nsLayoutUtils::RectToGfxRect(*aRect, mAppUnitsPerCSSPx); 1.576 + gfxRect rectInFilterSpace = 1.577 + mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx); 1.578 + rectInFilterSpace.RoundOut(); 1.579 + nsIntRect intRect; 1.580 + if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) { 1.581 + rect = intRect; 1.582 + } 1.583 + } 1.584 + return rect; 1.585 +} 1.586 + 1.587 +nsRect 1.588 +nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const 1.589 +{ 1.590 + if (aRect.IsEmpty()) { 1.591 + return nsRect(); 1.592 + } 1.593 + gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height); 1.594 + r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r); 1.595 + // nsLayoutUtils::RoundGfxRectToAppRect rounds out. 1.596 + return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx); 1.597 +} 1.598 + 1.599 +nsIntRegion 1.600 +nsFilterInstance::FrameSpaceToFilterSpace(const nsRegion* aRegion) const 1.601 +{ 1.602 + if (!aRegion) { 1.603 + return mFilterSpaceBounds; 1.604 + } 1.605 + nsIntRegion result; 1.606 + nsRegionRectIterator it(*aRegion); 1.607 + while (const nsRect* r = it.Next()) { 1.608 + // FrameSpaceToFilterSpace rounds out, so this works. 1.609 + result.Or(result, FrameSpaceToFilterSpace(r)); 1.610 + } 1.611 + return result; 1.612 +} 1.613 + 1.614 +nsRegion 1.615 +nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRegion& aRegion) const 1.616 +{ 1.617 + nsRegion result; 1.618 + nsIntRegionRectIterator it(aRegion); 1.619 + while (const nsIntRect* r = it.Next()) { 1.620 + // FilterSpaceToFrameSpace rounds out, so this works. 1.621 + result.Or(result, FilterSpaceToFrameSpace(*r)); 1.622 + } 1.623 + return result; 1.624 +} 1.625 + 1.626 +gfxMatrix 1.627 +nsFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const 1.628 +{ 1.629 + return gfxMatrix().Translate(-nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mTargetFrame)); 1.630 +}