1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGIntegrationUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,721 @@ 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 "nsSVGIntegrationUtils.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "gfxDrawable.h" 1.14 +#include "nsCSSAnonBoxes.h" 1.15 +#include "nsDisplayList.h" 1.16 +#include "nsFilterInstance.h" 1.17 +#include "nsLayoutUtils.h" 1.18 +#include "nsRenderingContext.h" 1.19 +#include "nsSVGClipPathFrame.h" 1.20 +#include "nsSVGEffects.h" 1.21 +#include "nsSVGElement.h" 1.22 +#include "nsSVGFilterPaintCallback.h" 1.23 +#include "nsSVGMaskFrame.h" 1.24 +#include "nsSVGPaintServerFrame.h" 1.25 +#include "nsSVGUtils.h" 1.26 +#include "FrameLayerBuilder.h" 1.27 +#include "BasicLayers.h" 1.28 +#include "mozilla/gfx/Point.h" 1.29 + 1.30 +using namespace mozilla; 1.31 +using namespace mozilla::layers; 1.32 + 1.33 +// ---------------------------------------------------------------------- 1.34 + 1.35 +/** 1.36 + * This class is used to get the pre-effects visual overflow rect of a frame, 1.37 + * or, in the case of a frame with continuations, to collect the union of the 1.38 + * pre-effects visual overflow rects of all the continuations. The result is 1.39 + * relative to the origin (top left corner of the border box) of the frame, or, 1.40 + * if the frame has continuations, the origin of the _first_ continuation. 1.41 + */ 1.42 +class PreEffectsVisualOverflowCollector : public nsLayoutUtils::BoxCallback 1.43 +{ 1.44 +public: 1.45 + /** 1.46 + * If the pre-effects visual overflow rect of the frame being examined 1.47 + * happens to be known, it can be passed in as aCurrentFrame and its 1.48 + * pre-effects visual overflow rect can be passed in as 1.49 + * aCurrentFrameOverflowArea. This is just an optimization to save a 1.50 + * frame property lookup - these arguments are optional. 1.51 + */ 1.52 + PreEffectsVisualOverflowCollector(nsIFrame* aFirstContinuation, 1.53 + nsIFrame* aCurrentFrame, 1.54 + const nsRect& aCurrentFrameOverflowArea) 1.55 + : mFirstContinuation(aFirstContinuation) 1.56 + , mCurrentFrame(aCurrentFrame) 1.57 + , mCurrentFrameOverflowArea(aCurrentFrameOverflowArea) 1.58 + { 1.59 + NS_ASSERTION(!mFirstContinuation->GetPrevContinuation(), 1.60 + "We want the first continuation here"); 1.61 + } 1.62 + 1.63 + virtual void AddBox(nsIFrame* aFrame) MOZ_OVERRIDE { 1.64 + nsRect overflow = (aFrame == mCurrentFrame) ? 1.65 + mCurrentFrameOverflowArea : GetPreEffectsVisualOverflowRect(aFrame); 1.66 + mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mFirstContinuation)); 1.67 + } 1.68 + 1.69 + nsRect GetResult() const { 1.70 + return mResult; 1.71 + } 1.72 + 1.73 +private: 1.74 + 1.75 + static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame) { 1.76 + nsRect* r = static_cast<nsRect*> 1.77 + (aFrame->Properties().Get(nsIFrame::PreEffectsBBoxProperty())); 1.78 + if (r) { 1.79 + return *r; 1.80 + } 1.81 + // Despite the fact that we're invoked for frames with SVG effects applied, 1.82 + // we can actually get here. All continuations and IB split siblings of a 1.83 + // frame with SVG effects applied will have the PreEffectsBBoxProperty 1.84 + // property set on them. Therefore, the frames that are passed to us will 1.85 + // always have that property set...well, with one exception. If the frames 1.86 + // for an element with SVG effects applied have been subject to an "IB 1.87 + // split", then the block frame(s) that caused the split will have been 1.88 + // wrapped in anonymous, inline-block, nsBlockFrames of pseudo-type 1.89 + // nsCSSAnonBoxes::mozAnonymousBlock. These "IB split sibling" anonymous 1.90 + // blocks will have the PreEffectsBBoxProperty property set on them, but 1.91 + // they will never be passed to us. Instead, we'll be passed the block 1.92 + // children that they wrap, which don't have the PreEffectsBBoxProperty 1.93 + // property set on them. This is actually okay. What we care about is 1.94 + // collecting the _pre_ effects visual overflow rects of the frames to 1.95 + // which the SVG effects have been applied. Since the IB split results in 1.96 + // any overflow rect adjustments for transforms, effects, etc. taking 1.97 + // place on the anonymous block wrappers, the wrapped children are left 1.98 + // with their overflow rects unaffected. In other words, calling 1.99 + // GetVisualOverflowRect() on the children will return their pre-effects 1.100 + // visual overflow rects, just as we need. 1.101 + // 1.102 + // A couple of tests that demonstrate the IB split and cause us to get here 1.103 + // are: 1.104 + // 1.105 + // * reftests/svg/svg-integration/clipPath-html-06.xhtml 1.106 + // * reftests/svg/svg-integration/clipPath-html-06-extref.xhtml 1.107 + // 1.108 + // If we ever got passed a frame with the PreTransformOverflowAreasProperty 1.109 + // property set, that would be bad, since then our GetVisualOverflowRect() 1.110 + // call would give us the post-effects, and post-transform, overflow rect. 1.111 + // 1.112 + NS_ASSERTION(aFrame->GetParent()->StyleContext()->GetPseudo() == 1.113 + nsCSSAnonBoxes::mozAnonymousBlock, 1.114 + "How did we getting here, then?"); 1.115 + NS_ASSERTION(!aFrame->Properties().Get( 1.116 + aFrame->PreTransformOverflowAreasProperty()), 1.117 + "GetVisualOverflowRect() won't return the pre-effects rect!"); 1.118 + return aFrame->GetVisualOverflowRect(); 1.119 + } 1.120 + 1.121 + nsIFrame* mFirstContinuation; 1.122 + nsIFrame* mCurrentFrame; 1.123 + const nsRect& mCurrentFrameOverflowArea; 1.124 + nsRect mResult; 1.125 +}; 1.126 + 1.127 +/** 1.128 + * Gets the union of the pre-effects visual overflow rects of all of a frame's 1.129 + * continuations, in "user space". 1.130 + */ 1.131 +static nsRect 1.132 +GetPreEffectsVisualOverflowUnion(nsIFrame* aFirstContinuation, 1.133 + nsIFrame* aCurrentFrame, 1.134 + const nsRect& aCurrentFramePreEffectsOverflow, 1.135 + const nsPoint& aFirstContinuationToUserSpace) 1.136 +{ 1.137 + NS_ASSERTION(!aFirstContinuation->GetPrevContinuation(), 1.138 + "Need first continuation here"); 1.139 + PreEffectsVisualOverflowCollector collector(aFirstContinuation, 1.140 + aCurrentFrame, 1.141 + aCurrentFramePreEffectsOverflow); 1.142 + // Compute union of all overflow areas relative to aFirstContinuation: 1.143 + nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation, &collector); 1.144 + // Return the result in user space: 1.145 + return collector.GetResult() + aFirstContinuationToUserSpace; 1.146 +} 1.147 + 1.148 + 1.149 +bool 1.150 +nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) 1.151 +{ 1.152 + // Even when SVG display lists are disabled, returning true for SVG frames 1.153 + // does not adversely affect any of our callers. Therefore we don't bother 1.154 + // checking the SDL prefs here, since we don't know if we're being called for 1.155 + // painting or hit-testing anyway. 1.156 + const nsStyleSVGReset *style = aFrame->StyleSVGReset(); 1.157 + return (style->HasFilters() || style->mClipPath || style->mMask); 1.158 +} 1.159 + 1.160 +// For non-SVG frames, this gives the offset to the frame's "user space". 1.161 +// For SVG frames, this returns a zero offset. 1.162 +static nsPoint 1.163 +GetOffsetToBoundingBox(nsIFrame* aFrame) 1.164 +{ 1.165 + if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { 1.166 + // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the 1.167 + // covered region relative to the nsSVGOuterSVGFrame, which is absolutely 1.168 + // not what we want. SVG frames are always in user space, so they have 1.169 + // no offset adjustment to make. 1.170 + return nsPoint(); 1.171 + } 1.172 + // We could allow aFrame to be any continuation, but since that would require 1.173 + // a GetPrevContinuation() virtual call and conditional returns, and since 1.174 + // all our current consumers always pass in the first continuation, we don't 1.175 + // currently bother. 1.176 + NS_ASSERTION(!aFrame->GetPrevContinuation(), "Not first continuation"); 1.177 + 1.178 + // The GetAllInFlowRectsUnion() call gets the union of the frame border-box 1.179 + // rects over all continuations, relative to the origin (top-left of the 1.180 + // border box) of its second argument (here, aFrame, the first continuation). 1.181 + return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame).TopLeft(); 1.182 +} 1.183 + 1.184 +/* static */ nsSize 1.185 +nsSVGIntegrationUtils::GetContinuationUnionSize(nsIFrame* aNonSVGFrame) 1.186 +{ 1.187 + NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG), 1.188 + "SVG frames should not get here"); 1.189 + nsIFrame* firstFrame = 1.190 + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame); 1.191 + return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame).Size(); 1.192 +} 1.193 + 1.194 +/* static */ gfx::Size 1.195 +nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(nsIFrame* aNonSVGFrame) 1.196 +{ 1.197 + NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG), 1.198 + "SVG frames should not get here"); 1.199 + nsIFrame* firstFrame = 1.200 + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame); 1.201 + nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame); 1.202 + nsPresContext* presContext = firstFrame->PresContext(); 1.203 + return gfx::Size(presContext->AppUnitsToFloatCSSPixels(r.width), 1.204 + presContext->AppUnitsToFloatCSSPixels(r.height)); 1.205 +} 1.206 + 1.207 +gfxRect 1.208 +nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame) 1.209 +{ 1.210 + NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG), 1.211 + "SVG frames should not get here"); 1.212 + nsIFrame* firstFrame = 1.213 + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame); 1.214 + // 'r' is in "user space": 1.215 + nsRect r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(), 1.216 + GetOffsetToBoundingBox(firstFrame)); 1.217 + return nsLayoutUtils::RectToGfxRect(r, 1.218 + aNonSVGFrame->PresContext()->AppUnitsPerCSSPixel()); 1.219 +} 1.220 + 1.221 +// XXX Since we're called during reflow, this method is broken for frames with 1.222 +// continuations. When we're called for a frame with continuations, we're 1.223 +// called for each continuation in turn as it's reflowed. However, it isn't 1.224 +// until the last continuation is reflowed that this method's 1.225 +// GetOffsetToBoundingBox() and GetPreEffectsVisualOverflowUnion() calls will 1.226 +// obtain valid border boxes for all the continuations. As a result, we'll 1.227 +// end up returning bogus post-filter visual overflow rects for all the prior 1.228 +// continuations. Unfortunately, by the time the last continuation is 1.229 +// reflowed, it's too late to go back and set and propagate the overflow 1.230 +// rects on the previous continuations. 1.231 +// 1.232 +// The reason that we need to pass an override bbox to 1.233 +// GetPreEffectsVisualOverflowUnion rather than just letting it call into our 1.234 +// GetSVGBBoxForNonSVGFrame method is because we get called by 1.235 +// ComputeEffectsRect when it has been called with 1.236 +// aStoreRectProperties set to false. In this case the pre-effects visual 1.237 +// overflow rect that it has been passed may be different to that stored on 1.238 +// aFrame, resulting in a different bbox. 1.239 +// 1.240 +// XXXjwatt The pre-effects visual overflow rect passed to 1.241 +// ComputeEffectsRect won't include continuation overflows, so 1.242 +// for frames with continuation the following filter analysis will likely end 1.243 +// up being carried out with a bbox created as if the frame didn't have 1.244 +// continuations. 1.245 +// 1.246 +// XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right 1.247 +// for SVG frames, since for SVG frames the SVG spec defines the bbox to be 1.248 +// something quite different to the pre-effects visual overflow rect. However, 1.249 +// we're essentially calculating an invalidation area here, and using the 1.250 +// pre-effects overflow rect will actually overestimate that area which, while 1.251 +// being a bit wasteful, isn't otherwise a problem. 1.252 +// 1.253 +nsRect 1.254 + nsSVGIntegrationUtils:: 1.255 + ComputePostEffectsVisualOverflowRect(nsIFrame* aFrame, 1.256 + const nsRect& aPreEffectsOverflowRect) 1.257 +{ 1.258 + NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT), 1.259 + "Don't call this on SVG child frames"); 1.260 + 1.261 + nsIFrame* firstFrame = 1.262 + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); 1.263 + nsSVGEffects::EffectProperties effectProperties = 1.264 + nsSVGEffects::GetEffectProperties(firstFrame); 1.265 + if (!effectProperties.HasValidFilter()) { 1.266 + return aPreEffectsOverflowRect; 1.267 + } 1.268 + 1.269 + // Create an override bbox - see comment above: 1.270 + nsPoint firstFrameToBoundingBox = GetOffsetToBoundingBox(firstFrame); 1.271 + // overrideBBox is in "user space", in _CSS_ pixels: 1.272 + // XXX Why are we rounding out to pixel boundaries? We don't do that in 1.273 + // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary. 1.274 + gfxRect overrideBBox = 1.275 + nsLayoutUtils::RectToGfxRect( 1.276 + GetPreEffectsVisualOverflowUnion(firstFrame, aFrame, 1.277 + aPreEffectsOverflowRect, 1.278 + firstFrameToBoundingBox), 1.279 + aFrame->PresContext()->AppUnitsPerCSSPixel()); 1.280 + overrideBBox.RoundOut(); 1.281 + 1.282 + nsRect overflowRect = 1.283 + nsFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox); 1.284 + 1.285 + // Return overflowRect relative to aFrame, rather than "user space": 1.286 + return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToBoundingBox); 1.287 +} 1.288 + 1.289 +nsIntRegion 1.290 +nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame, 1.291 + const nsPoint& aToReferenceFrame, 1.292 + const nsIntRegion& aInvalidRegion) 1.293 +{ 1.294 + if (aInvalidRegion.IsEmpty()) { 1.295 + return nsIntRect(); 1.296 + } 1.297 + 1.298 + // Don't bother calling GetEffectProperties; the filter property should 1.299 + // already have been set up during reflow/ComputeFrameEffectsRect 1.300 + nsIFrame* firstFrame = 1.301 + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); 1.302 + nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame); 1.303 + if (!prop || !prop->IsInObserverLists()) { 1.304 + return aInvalidRegion; 1.305 + } 1.306 + 1.307 + int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.308 + 1.309 + if (!prop || !prop->ReferencesValidResources()) { 1.310 + // The frame is either not there or not currently available, 1.311 + // perhaps because we're in the middle of tearing stuff down. 1.312 + // Be conservative, return our visual overflow rect relative 1.313 + // to the reference frame. 1.314 + nsRect overflow = aFrame->GetVisualOverflowRect() + aToReferenceFrame; 1.315 + return overflow.ToOutsidePixels(appUnitsPerDevPixel); 1.316 + } 1.317 + 1.318 + // Convert aInvalidRegion into bounding box frame space in app units: 1.319 + nsPoint toBoundingBox = 1.320 + aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame); 1.321 + // The initial rect was relative to the reference frame, so we need to 1.322 + // remove that offset to get a rect relative to the current frame. 1.323 + toBoundingBox -= aToReferenceFrame; 1.324 + nsRegion preEffectsRegion = aInvalidRegion.ToAppUnits(appUnitsPerDevPixel).MovedBy(toBoundingBox); 1.325 + 1.326 + // Adjust the dirty area for effects, and shift it back to being relative to 1.327 + // the reference frame. 1.328 + nsRegion result = nsFilterInstance::GetPostFilterDirtyArea(firstFrame, 1.329 + preEffectsRegion).MovedBy(-toBoundingBox); 1.330 + // Return the result, in pixels relative to the reference frame. 1.331 + return result.ToOutsidePixels(appUnitsPerDevPixel); 1.332 +} 1.333 + 1.334 +nsRect 1.335 +nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame, 1.336 + const nsRect& aDirtyRect) 1.337 +{ 1.338 + // Don't bother calling GetEffectProperties; the filter property should 1.339 + // already have been set up during reflow/ComputeFrameEffectsRect 1.340 + nsIFrame* firstFrame = 1.341 + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); 1.342 + nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame); 1.343 + if (!prop || !prop->ReferencesValidResources()) { 1.344 + return aDirtyRect; 1.345 + } 1.346 + 1.347 + // Convert aDirtyRect into "user space" in app units: 1.348 + nsPoint toUserSpace = 1.349 + aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame); 1.350 + nsRect postEffectsRect = aDirtyRect + toUserSpace; 1.351 + 1.352 + // Return ther result, relative to aFrame, not in user space: 1.353 + return nsFilterInstance::GetPreFilterNeededArea(firstFrame, postEffectsRect).GetBounds() 1.354 + - toUserSpace; 1.355 +} 1.356 + 1.357 +bool 1.358 +nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt) 1.359 +{ 1.360 + nsIFrame* firstFrame = 1.361 + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); 1.362 + // Convert aPt to user space: 1.363 + nsPoint toUserSpace; 1.364 + if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { 1.365 + // XXXmstange Isn't this wrong for svg:use and innerSVG frames? 1.366 + toUserSpace = aFrame->GetPosition(); 1.367 + } else { 1.368 + toUserSpace = 1.369 + aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame); 1.370 + } 1.371 + nsPoint pt = aPt + toUserSpace; 1.372 + return nsSVGUtils::HitTestClip(firstFrame, pt); 1.373 +} 1.374 + 1.375 +class RegularFramePaintCallback : public nsSVGFilterPaintCallback 1.376 +{ 1.377 +public: 1.378 + RegularFramePaintCallback(nsDisplayListBuilder* aBuilder, 1.379 + LayerManager* aManager, 1.380 + const nsPoint& aOffset) 1.381 + : mBuilder(aBuilder), mLayerManager(aManager), 1.382 + mOffset(aOffset) {} 1.383 + 1.384 + virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget, 1.385 + const nsIntRect* aDirtyRect, 1.386 + nsIFrame* aTransformRoot) MOZ_OVERRIDE 1.387 + { 1.388 + BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager); 1.389 + basic->SetTarget(aContext->ThebesContext()); 1.390 + nsRenderingContext::AutoPushTranslation push(aContext, -mOffset); 1.391 + mLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, mBuilder); 1.392 + } 1.393 + 1.394 +private: 1.395 + nsDisplayListBuilder* mBuilder; 1.396 + LayerManager* mLayerManager; 1.397 + nsPoint mOffset; 1.398 +}; 1.399 + 1.400 +void 1.401 +nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx, 1.402 + nsIFrame* aFrame, 1.403 + const nsRect& aDirtyRect, 1.404 + nsDisplayListBuilder* aBuilder, 1.405 + LayerManager *aLayerManager) 1.406 +{ 1.407 +#ifdef DEBUG 1.408 + NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || 1.409 + (NS_SVGDisplayListPaintingEnabled() && 1.410 + !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)), 1.411 + "Should not use nsSVGIntegrationUtils on this SVG frame"); 1.412 +#endif 1.413 + 1.414 + /* SVG defines the following rendering model: 1.415 + * 1.416 + * 1. Render geometry 1.417 + * 2. Apply filter 1.418 + * 3. Apply clipping, masking, group opacity 1.419 + * 1.420 + * We follow this, but perform a couple of optimizations: 1.421 + * 1.422 + * + Use cairo's clipPath when representable natively (single object 1.423 + * clip region). 1.424 + * 1.425 + * + Merge opacity and masking if both used together. 1.426 + */ 1.427 + 1.428 + const nsIContent* content = aFrame->GetContent(); 1.429 + bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); 1.430 + if (hasSVGLayout) { 1.431 + nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame); 1.432 + if (!svgChildFrame || !aFrame->GetContent()->IsSVG()) { 1.433 + NS_ASSERTION(false, "why?"); 1.434 + return; 1.435 + } 1.436 + if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { 1.437 + return; // The SVG spec says not to draw _anything_ 1.438 + } 1.439 + } 1.440 + 1.441 + float opacity = aFrame->StyleDisplay()->mOpacity; 1.442 + if (opacity == 0.0f) { 1.443 + return; 1.444 + } 1.445 + if (opacity != 1.0f && 1.446 + hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) { 1.447 + opacity = 1.0f; 1.448 + } 1.449 + 1.450 + /* Properties are added lazily and may have been removed by a restyle, 1.451 + so make sure all applicable ones are set again. */ 1.452 + nsIFrame* firstFrame = 1.453 + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); 1.454 + nsSVGEffects::EffectProperties effectProperties = 1.455 + nsSVGEffects::GetEffectProperties(firstFrame); 1.456 + 1.457 + bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); 1.458 + nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); 1.459 + nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK); 1.460 + if (!isOK) { 1.461 + return; // Some resource is missing. We shouldn't paint anything. 1.462 + } 1.463 + 1.464 + bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true; 1.465 + 1.466 + gfxContext* gfx = aCtx->ThebesContext(); 1.467 + gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx); 1.468 + 1.469 + nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame); 1.470 + nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset; 1.471 + if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) { 1.472 + /* Snap the offset if the reference frame is not a SVG frame, 1.473 + * since other frames will be snapped to pixel when rendering. */ 1.474 + offsetToBoundingBox = nsPoint( 1.475 + aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x), 1.476 + aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y)); 1.477 + } 1.478 + 1.479 + // After applying only "offsetToBoundingBox", aCtx would have its origin at 1.480 + // the top left corner of aFrame's bounding box (over all continuations). 1.481 + // However, SVG painting needs the origin to be located at the origin of the 1.482 + // SVG frame's "user space", i.e. the space in which, for example, the 1.483 + // frame's BBox lives. 1.484 + // SVG geometry frames and foreignObject frames apply their own offsets, so 1.485 + // their position is relative to their user space. So for these frame types, 1.486 + // if we want aCtx to be in user space, we first need to subtract the 1.487 + // frame's position so that SVG painting can later add it again and the 1.488 + // frame is painted in the right place. 1.489 + 1.490 + gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame); 1.491 + nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)), 1.492 + nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y))); 1.493 + nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace; 1.494 + 1.495 + NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace, 1.496 + "For non-SVG frames there shouldn't be any additional offset"); 1.497 + 1.498 + aCtx->Translate(offsetToUserSpace); 1.499 + 1.500 + gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame); 1.501 + 1.502 + bool complexEffects = false; 1.503 + /* Check if we need to do additional operations on this child's 1.504 + * rendering, which necessitates rendering into another surface. */ 1.505 + if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip) 1.506 + || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { 1.507 + complexEffects = true; 1.508 + gfx->Save(); 1.509 + aCtx->IntersectClip(aFrame->GetVisualOverflowRectRelativeToSelf() + 1.510 + toUserSpace); 1.511 + gfx->PushGroup(gfxContentType::COLOR_ALPHA); 1.512 + } 1.513 + 1.514 + /* If this frame has only a trivial clipPath, set up cairo's clipping now so 1.515 + * we can just do normal painting and get it clipped appropriately. 1.516 + */ 1.517 + if (clipPathFrame && isTrivialClip) { 1.518 + gfx->Save(); 1.519 + clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix); 1.520 + } 1.521 + 1.522 + /* Paint the child */ 1.523 + if (effectProperties.HasValidFilter()) { 1.524 + RegularFramePaintCallback callback(aBuilder, aLayerManager, 1.525 + offsetToUserSpace); 1.526 + 1.527 + nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox; 1.528 + nsFilterInstance::PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRegion); 1.529 + } else { 1.530 + gfx->SetMatrix(matrixAutoSaveRestore.Matrix()); 1.531 + aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder); 1.532 + aCtx->Translate(offsetToUserSpace); 1.533 + } 1.534 + 1.535 + if (clipPathFrame && isTrivialClip) { 1.536 + gfx->Restore(); 1.537 + } 1.538 + 1.539 + /* No more effects, we're done. */ 1.540 + if (!complexEffects) { 1.541 + return; 1.542 + } 1.543 + 1.544 + gfx->PopGroupToSource(); 1.545 + 1.546 + nsRefPtr<gfxPattern> maskSurface = 1.547 + maskFrame ? maskFrame->ComputeMaskAlpha(aCtx, aFrame, 1.548 + cssPxToDevPxMatrix, opacity) : nullptr; 1.549 + 1.550 + nsRefPtr<gfxPattern> clipMaskSurface; 1.551 + if (clipPathFrame && !isTrivialClip) { 1.552 + gfx->PushGroup(gfxContentType::COLOR_ALPHA); 1.553 + 1.554 + nsresult rv = clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix); 1.555 + clipMaskSurface = gfx->PopGroup(); 1.556 + 1.557 + if (NS_SUCCEEDED(rv) && clipMaskSurface) { 1.558 + // Still more set after clipping, so clip to another surface 1.559 + if (maskSurface || opacity != 1.0f) { 1.560 + gfx->PushGroup(gfxContentType::COLOR_ALPHA); 1.561 + gfx->Mask(clipMaskSurface); 1.562 + gfx->PopGroupToSource(); 1.563 + } else { 1.564 + gfx->Mask(clipMaskSurface); 1.565 + } 1.566 + } 1.567 + } 1.568 + 1.569 + if (maskSurface) { 1.570 + gfx->Mask(maskSurface); 1.571 + } else if (opacity != 1.0f) { 1.572 + gfx->Paint(opacity); 1.573 + } 1.574 + 1.575 + gfx->Restore(); 1.576 +} 1.577 + 1.578 +gfxMatrix 1.579 +nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame) 1.580 +{ 1.581 + int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel(); 1.582 + float devPxPerCSSPx = 1.583 + 1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel); 1.584 + 1.585 + return gfxMatrix(devPxPerCSSPx, 0.0, 1.586 + 0.0, devPxPerCSSPx, 1.587 + 0.0, 0.0); 1.588 +} 1.589 + 1.590 +class PaintFrameCallback : public gfxDrawingCallback { 1.591 +public: 1.592 + PaintFrameCallback(nsIFrame* aFrame, 1.593 + const nsSize aPaintServerSize, 1.594 + const gfxIntSize aRenderSize, 1.595 + uint32_t aFlags) 1.596 + : mFrame(aFrame) 1.597 + , mPaintServerSize(aPaintServerSize) 1.598 + , mRenderSize(aRenderSize) 1.599 + , mFlags (aFlags) 1.600 + {} 1.601 + virtual bool operator()(gfxContext* aContext, 1.602 + const gfxRect& aFillRect, 1.603 + const GraphicsFilter& aFilter, 1.604 + const gfxMatrix& aTransform) MOZ_OVERRIDE; 1.605 +private: 1.606 + nsIFrame* mFrame; 1.607 + nsSize mPaintServerSize; 1.608 + gfxIntSize mRenderSize; 1.609 + uint32_t mFlags; 1.610 +}; 1.611 + 1.612 +bool 1.613 +PaintFrameCallback::operator()(gfxContext* aContext, 1.614 + const gfxRect& aFillRect, 1.615 + const GraphicsFilter& aFilter, 1.616 + const gfxMatrix& aTransform) 1.617 +{ 1.618 + if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER) 1.619 + return false; 1.620 + 1.621 + mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER); 1.622 + 1.623 + nsRefPtr<nsRenderingContext> context(new nsRenderingContext()); 1.624 + context->Init(mFrame->PresContext()->DeviceContext(), aContext); 1.625 + aContext->Save(); 1.626 + 1.627 + // Clip to aFillRect so that we don't paint outside. 1.628 + aContext->NewPath(); 1.629 + aContext->Rectangle(aFillRect); 1.630 + aContext->Clip(); 1.631 + 1.632 + aContext->Multiply(gfxMatrix(aTransform).Invert()); 1.633 + 1.634 + // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want 1.635 + // to have it anchored at the top left corner of the bounding box of all of 1.636 + // mFrame's continuations. So we add a translation transform. 1.637 + int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 1.638 + nsPoint offset = GetOffsetToBoundingBox(mFrame); 1.639 + gfxPoint devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel; 1.640 + aContext->Multiply(gfxMatrix().Translate(devPxOffset)); 1.641 + 1.642 + gfxSize paintServerSize = 1.643 + gfxSize(mPaintServerSize.width, mPaintServerSize.height) / 1.644 + mFrame->PresContext()->AppUnitsPerDevPixel(); 1.645 + 1.646 + // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we 1.647 + // want it to render with mRenderSize, so we need to set up a scale transform. 1.648 + gfxFloat scaleX = mRenderSize.width / paintServerSize.width; 1.649 + gfxFloat scaleY = mRenderSize.height / paintServerSize.height; 1.650 + gfxMatrix scaleMatrix = gfxMatrix().Scale(scaleX, scaleY); 1.651 + aContext->Multiply(scaleMatrix); 1.652 + 1.653 + // Draw. 1.654 + nsRect dirty(-offset.x, -offset.y, 1.655 + mPaintServerSize.width, mPaintServerSize.height); 1.656 + 1.657 + uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM | 1.658 + nsLayoutUtils::PAINT_ALL_CONTINUATIONS; 1.659 + if (mFlags & nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) { 1.660 + flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES; 1.661 + } 1.662 + nsLayoutUtils::PaintFrame(context, mFrame, 1.663 + dirty, NS_RGBA(0, 0, 0, 0), 1.664 + flags); 1.665 + 1.666 + aContext->Restore(); 1.667 + 1.668 + mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER); 1.669 + 1.670 + return true; 1.671 +} 1.672 + 1.673 +/* static */ already_AddRefed<gfxDrawable> 1.674 +nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame* aFrame, 1.675 + nsIFrame* aTarget, 1.676 + const nsSize& aPaintServerSize, 1.677 + const gfxIntSize& aRenderSize, 1.678 + const gfxMatrix& aContextMatrix, 1.679 + uint32_t aFlags) 1.680 +{ 1.681 + // aPaintServerSize is the size that would be filled when using 1.682 + // background-repeat:no-repeat and background-size:auto. For normal background 1.683 + // images, this would be the intrinsic size of the image; for gradients and 1.684 + // patterns this would be the whole target frame fill area. 1.685 + // aRenderSize is what we will be actually filling after accounting for 1.686 + // background-size. 1.687 + if (aFrame->IsFrameOfType(nsIFrame::eSVGPaintServer)) { 1.688 + // aFrame is either a pattern or a gradient. These fill the whole target 1.689 + // frame by default, so aPaintServerSize is the whole target background fill 1.690 + // area. 1.691 + nsSVGPaintServerFrame* server = 1.692 + static_cast<nsSVGPaintServerFrame*>(aFrame); 1.693 + 1.694 + gfxRect overrideBounds(0, 0, 1.695 + aPaintServerSize.width, aPaintServerSize.height); 1.696 + overrideBounds.ScaleInverse(aFrame->PresContext()->AppUnitsPerDevPixel()); 1.697 + nsRefPtr<gfxPattern> pattern = 1.698 + server->GetPaintServerPattern(aTarget, aContextMatrix, 1.699 + &nsStyleSVG::mFill, 1.0, &overrideBounds); 1.700 + 1.701 + if (!pattern) 1.702 + return nullptr; 1.703 + 1.704 + // pattern is now set up to fill aPaintServerSize. But we want it to 1.705 + // fill aRenderSize, so we need to add a scaling transform. 1.706 + // We couldn't just have set overrideBounds to aRenderSize - it would have 1.707 + // worked for gradients, but for patterns it would result in a different 1.708 + // pattern size. 1.709 + gfxFloat scaleX = overrideBounds.Width() / aRenderSize.width; 1.710 + gfxFloat scaleY = overrideBounds.Height() / aRenderSize.height; 1.711 + gfxMatrix scaleMatrix = gfxMatrix().Scale(scaleX, scaleY); 1.712 + pattern->SetMatrix(scaleMatrix.Multiply(pattern->GetMatrix())); 1.713 + nsRefPtr<gfxDrawable> drawable = 1.714 + new gfxPatternDrawable(pattern, aRenderSize); 1.715 + return drawable.forget(); 1.716 + } 1.717 + 1.718 + // We don't want to paint into a surface as long as we don't need to, so we 1.719 + // set up a drawing callback. 1.720 + nsRefPtr<gfxDrawingCallback> cb = 1.721 + new PaintFrameCallback(aFrame, aPaintServerSize, aRenderSize, aFlags); 1.722 + nsRefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, aRenderSize); 1.723 + return drawable.forget(); 1.724 +}