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