michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Main header first: michael@0: #include "nsFilterInstance.h" michael@0: michael@0: // Keep others in (case-insensitive) order: michael@0: #include "gfxPlatform.h" michael@0: #include "gfxUtils.h" michael@0: #include "nsISVGChildFrame.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsSVGFilterInstance.h" michael@0: #include "nsSVGFilterPaintCallback.h" michael@0: #include "nsSVGUtils.h" michael@0: #include "SVGContentUtils.h" michael@0: #include "FilterSupport.h" michael@0: #include "gfx2DGlue.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::gfx; michael@0: michael@0: nsresult michael@0: nsFilterInstance::PaintFilteredFrame(nsRenderingContext *aContext, michael@0: nsIFrame *aFilteredFrame, michael@0: nsSVGFilterPaintCallback *aPaintCallback, michael@0: const nsRegion *aDirtyArea, michael@0: nsIFrame* aTransformRoot) michael@0: { michael@0: nsFilterInstance instance(aFilteredFrame, aPaintCallback, aDirtyArea, michael@0: nullptr, nullptr, nullptr, michael@0: aTransformRoot); michael@0: if (!instance.IsInitialized()) { michael@0: return NS_OK; michael@0: } michael@0: return instance.Render(aContext->ThebesContext()); michael@0: } michael@0: michael@0: nsRegion michael@0: nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame, michael@0: const nsRegion& aPreFilterDirtyRegion) michael@0: { michael@0: if (aPreFilterDirtyRegion.IsEmpty()) { michael@0: return nsRegion(); michael@0: } michael@0: michael@0: nsFilterInstance instance(aFilteredFrame, nullptr, nullptr, michael@0: &aPreFilterDirtyRegion); michael@0: if (!instance.IsInitialized()) { michael@0: return nsRegion(); michael@0: } michael@0: // We've passed in the source's dirty area so the instance knows about it. michael@0: // Now we can ask the instance to compute the area of the filter output michael@0: // that's dirty. michael@0: nsRegion dirtyRegion; michael@0: nsresult rv = instance.ComputePostFilterDirtyRegion(&dirtyRegion); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return dirtyRegion; michael@0: } michael@0: return nsRegion(); michael@0: } michael@0: michael@0: nsRegion michael@0: nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame, michael@0: const nsRegion& aPostFilterDirtyRegion) michael@0: { michael@0: nsFilterInstance instance(aFilteredFrame, nullptr, &aPostFilterDirtyRegion); michael@0: if (!instance.IsInitialized()) { michael@0: return nsRect(); michael@0: } michael@0: // Now we can ask the instance to compute the area of the source michael@0: // that's needed. michael@0: nsRect neededRect; michael@0: nsresult rv = instance.ComputeSourceNeededRect(&neededRect); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return neededRect; michael@0: } michael@0: return nsRegion(); michael@0: } michael@0: michael@0: nsRect michael@0: nsFilterInstance::GetPostFilterBounds(nsIFrame *aFilteredFrame, michael@0: const gfxRect *aOverrideBBox, michael@0: const nsRect *aPreFilterBounds) michael@0: { michael@0: MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || michael@0: !(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY), michael@0: "Non-display SVG do not maintain visual overflow rects"); michael@0: michael@0: nsRegion preFilterRegion; michael@0: nsRegion* preFilterRegionPtr = nullptr; michael@0: if (aPreFilterBounds) { michael@0: preFilterRegion = *aPreFilterBounds; michael@0: preFilterRegionPtr = &preFilterRegion; michael@0: } michael@0: nsFilterInstance instance(aFilteredFrame, nullptr, nullptr, michael@0: preFilterRegionPtr, aPreFilterBounds, michael@0: aOverrideBBox); michael@0: if (!instance.IsInitialized()) { michael@0: return nsRect(); michael@0: } michael@0: nsRect bbox; michael@0: nsresult rv = instance.ComputePostFilterExtents(&bbox); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return bbox; michael@0: } michael@0: return nsRect(); michael@0: } michael@0: michael@0: nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame, michael@0: nsSVGFilterPaintCallback *aPaintCallback, michael@0: const nsRegion *aPostFilterDirtyRegion, michael@0: const nsRegion *aPreFilterDirtyRegion, michael@0: const nsRect *aPreFilterVisualOverflowRectOverride, michael@0: const gfxRect *aOverrideBBox, michael@0: nsIFrame* aTransformRoot) : michael@0: mTargetFrame(aTargetFrame), michael@0: mPaintCallback(aPaintCallback), michael@0: mTransformRoot(aTransformRoot), michael@0: mInitialized(false) { michael@0: michael@0: mTargetBBox = aOverrideBBox ? michael@0: *aOverrideBBox : nsSVGUtils::GetBBox(mTargetFrame); michael@0: michael@0: nsresult rv = ComputeUserSpaceToFilterSpaceScale(); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: rv = BuildPrimitives(); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: if (mPrimitiveDescriptions.IsEmpty()) { michael@0: // Nothing should be rendered. michael@0: return; michael@0: } michael@0: michael@0: // Get various transforms: michael@0: michael@0: gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f, michael@0: 0.0f, mFilterSpaceToUserSpaceScale.height, michael@0: 0.0f, 0.0f); michael@0: michael@0: // Only used (so only set) when we paint: michael@0: if (mPaintCallback) { michael@0: mFilterSpaceToDeviceSpaceTransform = filterToUserSpace * michael@0: nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING); michael@0: } michael@0: michael@0: // Convert the passed in rects from frame to filter space: michael@0: michael@0: mAppUnitsPerCSSPx = mTargetFrame->PresContext()->AppUnitsPerCSSPixel(); michael@0: michael@0: mFilterSpaceToFrameSpaceInCSSPxTransform = michael@0: filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform(); michael@0: // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible michael@0: mFrameSpaceInCSSPxToFilterSpaceTransform = michael@0: mFilterSpaceToFrameSpaceInCSSPxTransform; michael@0: mFrameSpaceInCSSPxToFilterSpaceTransform.Invert(); michael@0: michael@0: mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion); michael@0: mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion); michael@0: if (aPreFilterVisualOverflowRectOverride) { michael@0: mTargetBounds = michael@0: FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride); michael@0: } else { michael@0: nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect(); michael@0: mTargetBounds = FrameSpaceToFilterSpace(&preFilterVOR); michael@0: } michael@0: michael@0: mInitialized = true; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::ComputeUserSpaceToFilterSpaceScale() michael@0: { michael@0: gfxMatrix canvasTransform = michael@0: nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_OUTERSVG_TM); michael@0: if (canvasTransform.IsSingular()) { michael@0: // Nothing should be rendered. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mUserSpaceToFilterSpaceScale = canvasTransform.ScaleFactors(true); michael@0: if (mUserSpaceToFilterSpaceScale.width <= 0.0f || michael@0: mUserSpaceToFilterSpaceScale.height <= 0.0f) { michael@0: // Nothing should be rendered. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width, michael@0: 1.0f / mUserSpaceToFilterSpaceScale.height); michael@0: return NS_OK; michael@0: } michael@0: michael@0: gfxRect michael@0: nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const michael@0: { michael@0: gfxRect filterSpaceRect = aUserSpaceRect; michael@0: filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width, michael@0: mUserSpaceToFilterSpaceScale.height); michael@0: return filterSpaceRect; michael@0: } michael@0: michael@0: gfxRect michael@0: nsFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const michael@0: { michael@0: gfxRect userSpaceRect = aFilterSpaceRect; michael@0: userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width, michael@0: mFilterSpaceToUserSpaceScale.height); michael@0: return userSpaceRect; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::BuildPrimitives() michael@0: { michael@0: NS_ASSERTION(!mPrimitiveDescriptions.Length(), michael@0: "expected to start building primitives from scratch"); michael@0: michael@0: const nsTArray& filters = mTargetFrame->StyleSVGReset()->mFilters; michael@0: for (uint32_t i = 0; i < filters.Length(); i++) { michael@0: nsresult rv = BuildPrimitivesForFilter(filters[i]); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter) michael@0: { michael@0: NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f && michael@0: mFilterSpaceToUserSpaceScale.height > 0.0f, michael@0: "scale factors between spaces should be positive values"); michael@0: michael@0: if (aFilter.GetType() == NS_STYLE_FILTER_URL) { michael@0: // Build primitives for an SVG filter. michael@0: nsSVGFilterInstance svgFilterInstance(aFilter, mTargetFrame, mTargetBBox, michael@0: mUserSpaceToFilterSpaceScale, michael@0: mFilterSpaceToUserSpaceScale); michael@0: if (!svgFilterInstance.IsInitialized()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // For now, we use the last SVG filter region as the overall filter region michael@0: // for the filter chain. Eventually, we will compute the overall filter michael@0: // using all of the generated FilterPrimitiveDescriptions. michael@0: mUserSpaceBounds = svgFilterInstance.GetFilterRegion(); michael@0: mFilterSpaceBounds = svgFilterInstance.GetFilterSpaceBounds(); michael@0: michael@0: // If this overflows, we can at least paint the maximum surface size. michael@0: bool overflow; michael@0: gfxIntSize surfaceSize = michael@0: nsSVGUtils::ConvertToSurfaceSize(mFilterSpaceBounds.Size(), &overflow); michael@0: mFilterSpaceBounds.SizeTo(surfaceSize); michael@0: michael@0: return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages); michael@0: } michael@0: michael@0: // Eventually, we will build primitives for CSS filters, too. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: void michael@0: nsFilterInstance::ComputeNeededBoxes() michael@0: { michael@0: if (mPrimitiveDescriptions.IsEmpty()) michael@0: return; michael@0: michael@0: nsIntRegion sourceGraphicNeededRegion; michael@0: nsIntRegion fillPaintNeededRegion; michael@0: nsIntRegion strokePaintNeededRegion; michael@0: michael@0: FilterDescription filter(mPrimitiveDescriptions, ToIntRect(mFilterSpaceBounds)); michael@0: FilterSupport::ComputeSourceNeededRegions( michael@0: filter, mPostFilterDirtyRegion, michael@0: sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion); michael@0: michael@0: nsIntRect sourceBoundsInt; michael@0: gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox); michael@0: sourceBounds.RoundOut(); michael@0: // Detect possible float->int overflow michael@0: if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt)) michael@0: return; michael@0: sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds); michael@0: michael@0: sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, sourceBoundsInt); michael@0: michael@0: mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds(); michael@0: mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds(); michael@0: mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds(); michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::BuildSourcePaint(SourceInfo *aSource, michael@0: gfxASurface* aTargetSurface, michael@0: DrawTarget* aTargetDT) michael@0: { michael@0: nsIntRect neededRect = aSource->mNeededBounds; michael@0: michael@0: RefPtr offscreenDT; michael@0: nsRefPtr offscreenSurface; michael@0: nsRefPtr ctx; michael@0: if (aTargetSurface) { michael@0: offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface( michael@0: neededRect.Size().ToIntSize(), gfxContentType::COLOR_ALPHA); michael@0: if (!offscreenSurface || offscreenSurface->CairoStatus()) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: ctx = new gfxContext(offscreenSurface); michael@0: } else { michael@0: offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( michael@0: ToIntSize(neededRect.Size()), SurfaceFormat::B8G8R8A8); michael@0: if (!offscreenDT) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: ctx = new gfxContext(offscreenDT); michael@0: } michael@0: michael@0: ctx->Translate(-neededRect.TopLeft()); michael@0: michael@0: nsRefPtr tmpCtx(new nsRenderingContext()); michael@0: tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx); michael@0: michael@0: gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert(); michael@0: gfxContext *gfx = tmpCtx->ThebesContext(); michael@0: gfx->Multiply(deviceToFilterSpace); michael@0: michael@0: gfx->Save(); michael@0: michael@0: gfxMatrix matrix = michael@0: nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING, michael@0: mTransformRoot); michael@0: if (!matrix.IsSingular()) { michael@0: gfx->Multiply(matrix); michael@0: gfx->Rectangle(mUserSpaceBounds); michael@0: if ((aSource == &mFillPaint && michael@0: nsSVGUtils::SetupCairoFillPaint(mTargetFrame, gfx)) || michael@0: (aSource == &mStrokePaint && michael@0: nsSVGUtils::SetupCairoStrokePaint(mTargetFrame, gfx))) { michael@0: gfx->Fill(); michael@0: } michael@0: } michael@0: gfx->Restore(); michael@0: michael@0: if (offscreenSurface) { michael@0: aSource->mSourceSurface = michael@0: gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface); michael@0: } else { michael@0: aSource->mSourceSurface = offscreenDT->Snapshot(); michael@0: } michael@0: aSource->mSurfaceRect = ToIntRect(neededRect); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::BuildSourcePaints(gfxASurface* aTargetSurface, michael@0: DrawTarget* aTargetDT) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!mFillPaint.mNeededBounds.IsEmpty()) { michael@0: rv = BuildSourcePaint(&mFillPaint, aTargetSurface, aTargetDT); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (!mStrokePaint.mNeededBounds.IsEmpty()) { michael@0: rv = BuildSourcePaint(&mStrokePaint, aTargetSurface, aTargetDT); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::BuildSourceImage(gfxASurface* aTargetSurface, michael@0: DrawTarget* aTargetDT) michael@0: { michael@0: nsIntRect neededRect = mSourceGraphic.mNeededBounds; michael@0: if (neededRect.IsEmpty()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: RefPtr offscreenDT; michael@0: nsRefPtr offscreenSurface; michael@0: nsRefPtr ctx; michael@0: if (aTargetSurface) { michael@0: offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface( michael@0: neededRect.Size().ToIntSize(), gfxContentType::COLOR_ALPHA); michael@0: if (!offscreenSurface || offscreenSurface->CairoStatus()) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: ctx = new gfxContext(offscreenSurface); michael@0: } else { michael@0: offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( michael@0: ToIntSize(neededRect.Size()), SurfaceFormat::B8G8R8A8); michael@0: if (!offscreenDT) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: ctx = new gfxContext(offscreenDT); michael@0: } michael@0: michael@0: ctx->Translate(-neededRect.TopLeft()); michael@0: michael@0: nsRefPtr tmpCtx(new nsRenderingContext()); michael@0: tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx); michael@0: michael@0: gfxRect r = FilterSpaceToUserSpace(neededRect); michael@0: r.RoundOut(); michael@0: nsIntRect dirty; michael@0: if (!gfxUtils::GfxRectToIntRect(r, &dirty)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // SVG graphics paint to device space, so we need to set an initial device michael@0: // space to filter space transform on the gfxContext that SourceGraphic michael@0: // and SourceAlpha will paint to. michael@0: // michael@0: // (In theory it would be better to minimize error by having filtered SVG michael@0: // graphics temporarily paint to user space when painting the sources and michael@0: // only set a user space to filter space transform on the gfxContext michael@0: // (since that would eliminate the transform multiplications from user michael@0: // space to device space and back again). However, that would make the michael@0: // code more complex while being hard to get right without introducing michael@0: // subtle bugs, and in practice it probably makes no real difference.) michael@0: gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert(); michael@0: tmpCtx->ThebesContext()->Multiply(deviceToFilterSpace); michael@0: mPaintCallback->Paint(tmpCtx, mTargetFrame, &dirty, mTransformRoot); michael@0: michael@0: RefPtr sourceGraphicSource; michael@0: michael@0: if (offscreenSurface) { michael@0: sourceGraphicSource = michael@0: gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface); michael@0: } else { michael@0: sourceGraphicSource = offscreenDT->Snapshot(); michael@0: } michael@0: michael@0: mSourceGraphic.mSourceSurface = sourceGraphicSource; michael@0: mSourceGraphic.mSurfaceRect = ToIntRect(neededRect); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::Render(gfxContext* aContext) michael@0: { michael@0: nsIntRect filterRect = mPostFilterDirtyRegion.GetBounds().Intersect(mFilterSpaceBounds); michael@0: gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform(); michael@0: michael@0: if (filterRect.IsEmpty() || ctm.IsSingular()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: Matrix oldDTMatrix; michael@0: nsRefPtr resultImage; michael@0: RefPtr dt; michael@0: if (aContext->IsCairo()) { michael@0: resultImage = michael@0: gfxPlatform::GetPlatform()->CreateOffscreenSurface(filterRect.Size().ToIntSize(), michael@0: gfxContentType::COLOR_ALPHA); michael@0: if (!resultImage || resultImage->CairoStatus()) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // Create a Cairo DrawTarget around resultImage. michael@0: dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface( michael@0: resultImage, ToIntSize(filterRect.Size())); michael@0: } else { michael@0: // When we have a DrawTarget-backed context, we can call DrawFilter michael@0: // directly on the target DrawTarget and don't need a temporary DT. michael@0: dt = aContext->GetDrawTarget(); michael@0: oldDTMatrix = dt->GetTransform(); michael@0: Matrix matrix = ToMatrix(ctm); michael@0: matrix.Translate(filterRect.x, filterRect.y); michael@0: dt->SetTransform(matrix * oldDTMatrix); michael@0: } michael@0: michael@0: ComputeNeededBoxes(); michael@0: michael@0: nsresult rv = BuildSourceImage(resultImage, dt); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = BuildSourcePaints(resultImage, dt); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds); michael@0: FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds); michael@0: michael@0: FilterSupport::RenderFilterDescription( michael@0: dt, filter, ToRect(filterRect), michael@0: mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect, michael@0: mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect, michael@0: mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect, michael@0: mInputImages); michael@0: michael@0: if (resultImage) { michael@0: aContext->Save(); michael@0: aContext->Multiply(ctm); michael@0: aContext->Translate(filterRect.TopLeft()); michael@0: aContext->SetSource(resultImage); michael@0: aContext->Paint(); michael@0: aContext->Restore(); michael@0: } else { michael@0: dt->SetTransform(oldDTMatrix); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::ComputePostFilterDirtyRegion(nsRegion* aPostFilterDirtyRegion) michael@0: { michael@0: *aPostFilterDirtyRegion = nsRegion(); michael@0: if (mPreFilterDirtyRegion.IsEmpty()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds); michael@0: FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds); michael@0: nsIntRegion resultChangeRegion = michael@0: FilterSupport::ComputeResultChangeRegion(filter, michael@0: mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion()); michael@0: *aPostFilterDirtyRegion = michael@0: FilterSpaceToFrameSpace(resultChangeRegion); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents) michael@0: { michael@0: *aPostFilterExtents = nsRect(); michael@0: michael@0: nsIntRect sourceBoundsInt; michael@0: gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox); michael@0: sourceBounds.RoundOut(); michael@0: // Detect possible float->int overflow michael@0: if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt)) michael@0: return NS_ERROR_FAILURE; michael@0: sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds); michael@0: michael@0: IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds); michael@0: FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds); michael@0: nsIntRegion postFilterExtents = michael@0: FilterSupport::ComputePostFilterExtents(filter, sourceBoundsInt); michael@0: *aPostFilterExtents = FilterSpaceToFrameSpace(postFilterExtents.GetBounds()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFilterInstance::ComputeSourceNeededRect(nsRect* aDirty) michael@0: { michael@0: ComputeNeededBoxes(); michael@0: *aDirty = FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIntRect michael@0: nsFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const michael@0: { michael@0: nsIntRect rect = mFilterSpaceBounds; michael@0: if (aRect) { michael@0: if (aRect->IsEmpty()) { michael@0: return nsIntRect(); michael@0: } michael@0: gfxRect rectInCSSPx = michael@0: nsLayoutUtils::RectToGfxRect(*aRect, mAppUnitsPerCSSPx); michael@0: gfxRect rectInFilterSpace = michael@0: mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx); michael@0: rectInFilterSpace.RoundOut(); michael@0: nsIntRect intRect; michael@0: if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) { michael@0: rect = intRect; michael@0: } michael@0: } michael@0: return rect; michael@0: } michael@0: michael@0: nsRect michael@0: nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const michael@0: { michael@0: if (aRect.IsEmpty()) { michael@0: return nsRect(); michael@0: } michael@0: gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height); michael@0: r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r); michael@0: // nsLayoutUtils::RoundGfxRectToAppRect rounds out. michael@0: return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx); michael@0: } michael@0: michael@0: nsIntRegion michael@0: nsFilterInstance::FrameSpaceToFilterSpace(const nsRegion* aRegion) const michael@0: { michael@0: if (!aRegion) { michael@0: return mFilterSpaceBounds; michael@0: } michael@0: nsIntRegion result; michael@0: nsRegionRectIterator it(*aRegion); michael@0: while (const nsRect* r = it.Next()) { michael@0: // FrameSpaceToFilterSpace rounds out, so this works. michael@0: result.Or(result, FrameSpaceToFilterSpace(r)); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsRegion michael@0: nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRegion& aRegion) const michael@0: { michael@0: nsRegion result; michael@0: nsIntRegionRectIterator it(aRegion); michael@0: while (const nsIntRect* r = it.Next()) { michael@0: // FilterSpaceToFrameSpace rounds out, so this works. michael@0: result.Or(result, FilterSpaceToFrameSpace(*r)); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: gfxMatrix michael@0: nsFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const michael@0: { michael@0: return gfxMatrix().Translate(-nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mTargetFrame)); michael@0: }