layout/svg/nsFilterInstance.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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 }

mercurial