/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Main header first: #include"SVGIntegrationUtils.h"
// Keep others in (case-insensitive) order: #include"gfxDrawable.h"
/** * This class is used to get the pre-effects ink overflow rect of a frame, * or, in the case of a frame with continuations, to collect the union of the * pre-effects ink overflow rects of all the continuations. The result is * relative to the origin (top left corner of the border box) of the frame, or, * if the frame has continuations, the origin of the _first_ continuation.
*/ class PreEffectsInkOverflowCollector : public nsLayoutUtils::BoxCallback { public: /** * If the pre-effects ink overflow rect of the frame being examined * happens to be known, it can be passed in as aCurrentFrame and its * pre-effects ink overflow rect can be passed in as * aCurrentFrameOverflowArea. This is just an optimization to save a * frame property lookup - these arguments are optional.
*/
PreEffectsInkOverflowCollector(nsIFrame* aFirstContinuation,
nsIFrame* aCurrentFrame, const nsRect& aCurrentFrameOverflowArea, bool aInReflow)
: mFirstContinuation(aFirstContinuation),
mCurrentFrame(aCurrentFrame),
mCurrentFrameOverflowArea(aCurrentFrameOverflowArea),
mInReflow(aInReflow) {
NS_ASSERTION(!mFirstContinuation->GetPrevContinuation(), "We want the first continuation here");
}
private: static nsRect PreEffectsInkOverflowRect(nsIFrame* aFrame, bool aInReflow) {
nsRect* r = aFrame->GetProperty(nsIFrame::PreEffectsBBoxProperty()); if (r) { return *r;
}
#ifdef DEBUG // Having PreTransformOverflowAreasProperty cached means // InkOverflowRect() will return post-effect rect, which is not what // we want. This function intentional reports pre-effect rect. But it does // not matter if there is no SVG effect on this frame, since no effect // means post-effect rect matches pre-effect rect. // // This function may be called during reflow or painting. We should only // do this check in painting process since the PreEffectsBBoxProperty of // continuations are not set correctly while reflowing. if (SVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame) &&
!aInReflow) {
OverflowAreas* preTransformOverflows =
aFrame->GetProperty(nsIFrame::PreTransformOverflowAreasProperty());
/** * Gets the union of the pre-effects ink overflow rects of all of a frame's * continuations, in "user space".
*/ static nsRect GetPreEffectsInkOverflowUnion(
nsIFrame* aFirstContinuation, nsIFrame* aCurrentFrame, const nsRect& aCurrentFramePreEffectsOverflow, const nsPoint& aFirstContinuationToUserSpace, bool aInReflow) {
NS_ASSERTION(!aFirstContinuation->GetPrevContinuation(), "Need first continuation here");
PreEffectsInkOverflowCollector collector(aFirstContinuation, aCurrentFrame,
aCurrentFramePreEffectsOverflow,
aInReflow); // Compute union of all overflow areas relative to aFirstContinuation:
nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation, &collector); // Return the result in user space: return collector.GetResult() + aFirstContinuationToUserSpace;
}
/** * Gets the pre-effects ink overflow rect of aCurrentFrame in "user space".
*/ static nsRect GetPreEffectsInkOverflow(
nsIFrame* aFirstContinuation, nsIFrame* aCurrentFrame, const nsPoint& aFirstContinuationToUserSpace) {
NS_ASSERTION(!aFirstContinuation->GetPrevContinuation(), "Need first continuation here");
PreEffectsInkOverflowCollector collector(aFirstContinuation, nullptr,
nsRect(), false); // Compute overflow areas of current frame relative to aFirstContinuation:
nsLayoutUtils::AddBoxesForFrame(aCurrentFrame, &collector); // Return the result in user space: return collector.GetResult() + aFirstContinuationToUserSpace;
}
bool SVGIntegrationUtils::UsingOverflowAffectingEffects( const nsIFrame* aFrame) { // Currently overflow don't take account of SVG or other non-absolute // positioned clipping, or masking. return aFrame->StyleEffects()->HasFilters();
}
bool SVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) { // Even when SVG display lists are disabled, returning true for SVG frames // does not adversely affect any of our callers. Therefore we don't bother // checking the SDL prefs here, since we don't know if we're being called for // painting or hit-testing anyway. const nsStyleSVGReset* style = aFrame->StyleSVGReset(); const nsStyleEffects* effects = aFrame->StyleEffects(); // TODO(cbrewster): remove backdrop-filter from this list once it is supported // in preserve-3d cases. return effects->HasFilters() || effects->HasBackdropFilters() ||
style->HasClipPath() || style->HasMask();
}
nsPoint SVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame* aFrame) { if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the // covered region relative to the SVGOuterSVGFrame, which is absolutely // not what we want. SVG frames are always in user space, so they have // no offset adjustment to make. return nsPoint();
}
// The GetAllInFlowRectsUnion() call gets the union of the frame border-box // rects over all continuations, relative to the origin (top-left of the // border box) of its second argument (here, aFrame, the first continuation). return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame).TopLeft();
}
struct EffectOffsets { // The offset between the reference frame and the bounding box of the // target frame in app unit.
nsPoint offsetToBoundingBox; // The offset between the reference frame and the bounding box of the // target frame in app unit.
nsPoint offsetToUserSpace; // The offset between the reference frame and the bounding box of the // target frame in device unit.
gfxPoint offsetToUserSpaceInDevPx;
};
result.offsetToBoundingBox =
aParams.builder->ToReferenceFrame(aFrame) -
SVGIntegrationUtils::GetOffsetToBoundingBox(aFrame); if (!aFrame->IsSVGFrame()) { /* Snap the offset if the reference frame is not a SVG frame,
* since other frames will be snapped to pixel when rendering. */
result.offsetToBoundingBox =
nsPoint(aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(
result.offsetToBoundingBox.x),
aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(
result.offsetToBoundingBox.y));
}
// After applying only "aOffsetToBoundingBox", aParams.ctx would have its // origin at the top left corner of frame's bounding box (over all // continuations). // However, SVG painting needs the origin to be located at the origin of the // SVG frame's "user space", i.e. the space in which, for example, the // frame's BBox lives. // SVG geometry frames and foreignObject frames apply their own offsets, so // their position is relative to their user space. So for these frame types, // if we want aParams.ctx to be in user space, we first need to subtract the // frame's position so that SVG painting can later add it again and the // frame is painted in the right place.
gfxPoint toUserSpaceGfx =
SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
nsPoint toUserSpace =
nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
/** * Setup transform matrix of a gfx context by a specific frame. Move the * origin of aParams.ctx to the user space of aFrame.
*/ static EffectOffsets MoveContextOriginToUserSpace(
nsIFrame* aFrame, const SVGIntegrationUtils::PaintFramesParams& aParams) {
EffectOffsets offset = ComputeEffectOffset(aFrame, aParams);
/* static */
nsSize SVGIntegrationUtils::GetContinuationUnionSize(nsIFrame* aNonSVGFrame) {
NS_ASSERTION(!aNonSVGFrame->IsSVGFrame(), "SVG frames should not get here");
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame); return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame).Size();
}
/* static */ gfx::Size SVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(
nsIFrame* aNonSVGFrame) {
NS_ASSERTION(!aNonSVGFrame->IsSVGFrame(), "SVG frames should not get here");
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame); return gfx::Size(nsPresContext::AppUnitsToFloatCSSPixels(r.width),
nsPresContext::AppUnitsToFloatCSSPixels(r.height));
}
gfxRect SVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(
nsIFrame* aNonSVGFrame, bool aUnionContinuations) { // Except for SVGOuterSVGFrame, we shouldn't be getting here with SVG // frames at all. This function is for elements that are laid out using the // CSS box model rules.
NS_ASSERTION(!aNonSVGFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT), "Frames with SVG layout should not get here");
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame); // 'r' is in "user space":
nsRect r = (aUnionContinuations)
? GetPreEffectsInkOverflowUnion(
firstFrame, nullptr, nsRect(),
GetOffsetToBoundingBox(firstFrame), false)
: GetPreEffectsInkOverflow(firstFrame, aNonSVGFrame,
GetOffsetToBoundingBox(firstFrame));
// XXX Since we're called during reflow, this method is broken for frames with // continuations. When we're called for a frame with continuations, we're // called for each continuation in turn as it's reflowed. However, it isn't // until the last continuation is reflowed that this method's // GetOffsetToBoundingBox() and GetPreEffectsInkOverflowUnion() calls will // obtain valid border boxes for all the continuations. As a result, we'll // end up returning bogus post-filter ink overflow rects for all the prior // continuations. Unfortunately, by the time the last continuation is // reflowed, it's too late to go back and set and propagate the overflow // rects on the previous continuations. // // The reason that we need to pass an override bbox to // GetPreEffectsInkOverflowUnion rather than just letting it call into our // GetSVGBBoxForNonSVGFrame method is because we get called by // ComputeEffectsRect when it has been called with // aStoreRectProperties set to false. In this case the pre-effects visual // overflow rect that it has been passed may be different to that stored on // aFrame, resulting in a different bbox. // // XXXjwatt The pre-effects ink overflow rect passed to // ComputeEffectsRect won't include continuation overflows, so // for frames with continuation the following filter analysis will likely end // up being carried out with a bbox created as if the frame didn't have // continuations. // // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right // for SVG frames, since for SVG frames the SVG spec defines the bbox to be // something quite different to the pre-effects ink overflow rect. However, // we're essentially calculating an invalidation area here, and using the // pre-effects overflow rect will actually overestimate that area which, while // being a bit wasteful, isn't otherwise a problem. //
nsRect SVGIntegrationUtils::ComputePostEffectsInkOverflowRect(
nsIFrame* aFrame, const nsRect& aPreEffectsOverflowRect) {
MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT), "Don't call this on SVG child frames");
MOZ_ASSERT(aFrame->StyleEffects()->HasFilters(), "We should only be called if the frame is filtered, since filters " "are the only effect that affects overflow.");
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); // Note: we do not return here for eHasNoRefs since we must still handle any // CSS filter functions. // TODO: we should really return an empty rect for eHasRefsSomeInvalid since // in that case we disable painting of the element.
nsTArray<SVGFilterFrame*> filterFrames; if (SVGObserverUtils::GetAndObserveFilters(firstFrame, &filterFrames) ==
SVGObserverUtils::eHasRefsSomeInvalid) { return aPreEffectsOverflowRect;
}
// Create an override bbox - see comment above:
nsPoint firstFrameToBoundingBox = GetOffsetToBoundingBox(firstFrame); // overrideBBox is in "user space", in _CSS_ pixels: // XXX Why are we rounding out to pixel boundaries? We don't do that in // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
gfxRect overrideBBox = nsLayoutUtils::RectToGfxRect(
GetPreEffectsInkOverflowUnion(firstFrame, aFrame, aPreEffectsOverflowRect,
firstFrameToBoundingBox, true),
AppUnitsPerCSSPixel());
overrideBBox.RoundOut();
// If we have any filters to observe then we should have started doing that // during reflow/ComputeFrameEffectsRect, so we use GetFiltersIfObserving // here to avoid needless work (or masking bugs by setting up observers at // the wrong time).
nsTArray<SVGFilterFrame*> filterFrames; if (!aFrame->StyleEffects()->HasFilters() ||
SVGObserverUtils::GetFiltersIfObserving(firstFrame, &filterFrames) ==
SVGObserverUtils::eHasRefsSomeInvalid) { return aDirtyRect;
}
// Return ther result, relative to aFrame, not in user space: return FilterInstance::GetPreFilterNeededArea(firstFrame, filterFrames,
postEffectsRect)
.GetBounds() -
toUserSpace;
}
// We can paint mask along with opacity only if // 1. There is only one mask, or // 2. No overlap among masks. // Collision detect in #2 is not that trivial, we only accept #1 here.
paintResult.opacityApplied = (aMaskFrames.Length() == 1);
// Set context's matrix on maskContext, offset by the maskSurfaceRect's // position. This makes sure that we combine the masks in device space.
Matrix maskSurfaceMatrix = ctx.CurrentMatrix();
if (!isMaskComplete ||
(aParams.imgParams.result != ImgDrawResult::SUCCESS &&
aParams.imgParams.result != ImgDrawResult::SUCCESS_NOT_COMPLETE &&
aParams.imgParams.result != ImgDrawResult::WRONG_SIZE)) { // Now we know the status of mask resource since we used it while painting. // According to the return value of PaintMaskSurface, we know whether mask // resource is resolvable or not. // // For a HTML doc: // According to css-masking spec, always create a mask surface when // we have any item in maskFrame even if all of those items are // non-resolvable <mask-sources> or <images>. // Set paintResult.transparentBlackMask as true, the caller should stop // painting masked content as if this mask is a transparent black one. // For a SVG doc: // SVG 1.1 say that if we fail to resolve a mask, we should draw the // object unmasked. // Left paintResult.maskSurface empty, the caller should paint all // masked content as if this mask is an opaque white one(no mask).
paintResult.transparentBlackMask =
!aParams.frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT);
staticbool ValidateSVGFrame(nsIFrame* aFrame) {
NS_ASSERTION(
!aFrame->HasAllStateBits(NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY), "Should not use SVGIntegrationUtils on this SVG frame");
if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { #ifdef DEBUG
ISVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
MOZ_ASSERT(svgFrame && aFrame->GetContent()->IsSVGElement(), "A non-SVG frame carries NS_FRAME_SVG_LAYOUT flag?"); #endif
const nsIContent* content = aFrame->GetContent(); if (!static_cast<const SVGElement*>(content)->HasValidDimensions()) { // The SVG spec says not to draw _anything_ returnfalse;
}
}
if (maskUsage.ShouldGenerateMaskLayer() && maskUsage.HasSVGClip()) { // We will paint both mask of positioned mask and clip-path into // maskTarget. // // Create one extra draw target for drawing positioned mask, so that we do // not have to copy the content of maskTarget before painting // clip-path into it.
maskTarget = maskTarget->CreateClippedDrawTarget(Rect(), SurfaceFormat::A8);
}
basicShapeSR.SetContext(&ctx);
gfxMatrix mat = SVGUtils::GetCSSPxToDevPxMatrix(frame); if (!maskUsage.ShouldGenerateMaskLayer()) { // Only have basic-shape clip-path effect. Fill clipped region by // opaque white.
ctx.SetDeviceColor(DeviceColor::MaskOpaqueWhite());
RefPtr<Path> path = CSSClipPathInstance::CreateClipPathForFrame(
ctx.GetDrawTarget(), frame, mat); if (path) {
ctx.SetPath(path);
ctx.Fill();
}
template <class T> void PaintMaskAndClipPathInternal(const PaintFramesParams& aParams, const T& aPaintChild) { #ifdef DEBUG const nsStyleSVGReset* style = aParams.frame->StyleSVGReset();
MOZ_ASSERT(style->HasClipPath() || style->HasMask(), "Should not use this method when no mask or clipPath effect" "on this frame"); #endif
/* SVG defines the following rendering model: * * 1. Render geometry * 2. Apply filter * 3. Apply clipping, masking, group opacity * * We handle #3 here and perform a couple of optimizations: * * + Use cairo's clipPath when representable natively (single object * clip region). * * + Merge opacity and masking if both used together.
*/
nsIFrame* frame = aParams.frame; if (!ValidateSVGFrame(frame)) { return;
}
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */ if (maskUsage.ShouldGenerateMask()) {
gfxContextMatrixAutoSaveRestore matSR;
if (maskUsage.ShouldGenerateMaskLayer()) {
matSR.SetContext(&context);
// For css-mask, we want to generate a mask for each continuation frame, // so we setup context matrix by the position of the current frame, // instead of the first continuation frame.
EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
MaskPaintResult paintResult = CreateAndPaintMaskSurface(
aParams, maskUsage.Opacity(), firstFrame->Style(), maskFrames,
offsets.offsetToUserSpace);
if (paintResult.transparentBlackMask) { return;
}
maskSurface = paintResult.maskSurface; if (maskSurface) {
shouldPushMask = true;
opacityApplied = paintResult.opacityApplied;
}
}
if (maskUsage.ShouldGenerateClipMaskLayer()) {
matSR.Restore();
matSR.SetContext(&context);
if (shouldPushMask) { // We want the mask to be untransformed so use the inverse of the // current transform as the maskTransform to compensate.
Matrix maskTransform = context.CurrentMatrix();
maskTransform.Invert();
/* If this frame has only a trivial clipPath, set up cairo's clipping now so * we can just do normal painting and get it clipped appropriately.
*/ if (maskUsage.ShouldApplyClipPath() ||
maskUsage.ShouldApplyBasicShapeOrPath()) {
gfxContextMatrixAutoSaveRestore matSR(&context);
void SVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams,
Span<const StyleFilter> aFilters, const SVGFilterPaintCallback& aCallback) {
MOZ_ASSERT(!aParams.builder->IsForGenerateGlyphMask(), "Filter effect is discarded while generating glyph mask.");
MOZ_ASSERT(!aFilters.IsEmpty(), "Should not use this method when no filter effect on this frame");
nsIFrame* frame = aParams.frame; if (!ValidateSVGFrame(frame)) { return;
}
// Properties are added lazily and may have been removed by a restyle, so make // sure all applicable ones are set again.
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame); // Note: we do not return here for eHasNoRefs since we must still handle any // CSS filter functions. // XXX: Do we need to check for eHasRefsSomeInvalid here given that // nsDisplayFilter::BuildLayer returns nullptr for eHasRefsSomeInvalid? // Or can we just assert !eHasRefsSomeInvalid?
nsTArray<SVGFilterFrame*> filterFrames; if (SVGObserverUtils::GetAndObserveFilters(firstFrame, &filterFrames) ==
SVGObserverUtils::eHasRefsSomeInvalid) {
aCallback(aParams.ctx, aParams.imgParams, nullptr, nullptr); return;
}
WrFiltersStatus SVGIntegrationUtils::CreateWebRenderCSSFilters(
Span<const StyleFilter> aFilters, nsIFrame* aFrame,
WrFiltersHolder& aWrFilters) { // Check if prefs are set to convert the CSS filters to SVG filters and use // the new WebRender SVG filter rendering, rather than the existing CSS filter // support if (StaticPrefs::gfx_webrender_svg_filter_effects() &&
StaticPrefs::
gfx_webrender_svg_filter_effects_also_convert_css_filters()) { return WrFiltersStatus::BLOB_FALLBACK;
} // All CSS filters are supported by WebRender. SVG filters are not fully // supported, those use NS_STYLE_FILTER_URL and are handled separately.
// If there are too many filters to render, then just pretend that we // succeeded, and don't render any of them. if (aFilters.Length() >
StaticPrefs::gfx_webrender_max_filter_ops_per_chain()) { return WrFiltersStatus::DISABLED_FOR_PERFORMANCE;
} // Track status so we can do cleanup if unsupported filters are found.
WrFiltersStatus status = WrFiltersStatus::CHAIN;
aWrFilters.filters.SetCapacity(aFilters.Length()); auto& wrFilters = aWrFilters.filters; for (const StyleFilter& filter : aFilters) { switch (filter.tag) { case StyleFilter::Tag::Brightness:
wrFilters.AppendElement(
wr::FilterOp::Brightness(filter.AsBrightness())); break; case StyleFilter::Tag::Contrast:
wrFilters.AppendElement(wr::FilterOp::Contrast(filter.AsContrast())); break; case StyleFilter::Tag::Grayscale:
wrFilters.AppendElement(wr::FilterOp::Grayscale(filter.AsGrayscale())); break; case StyleFilter::Tag::Invert:
wrFilters.AppendElement(wr::FilterOp::Invert(filter.AsInvert())); break; case StyleFilter::Tag::Opacity: { float opacity = filter.AsOpacity();
wrFilters.AppendElement(wr::FilterOp::Opacity(
wr::PropertyBinding<float>::Value(opacity), opacity)); break;
} case StyleFilter::Tag::Saturate:
wrFilters.AppendElement(wr::FilterOp::Saturate(filter.AsSaturate())); break; case StyleFilter::Tag::Sepia:
wrFilters.AppendElement(wr::FilterOp::Sepia(filter.AsSepia())); break; case StyleFilter::Tag::HueRotate: {
wrFilters.AppendElement(
wr::FilterOp::HueRotate(filter.AsHueRotate().ToDegrees())); break;
} case StyleFilter::Tag::Blur: { // TODO(emilio): we should go directly from css pixels -> device pixels. float appUnitsPerDevPixel =
aFrame->PresContext()->AppUnitsPerDevPixel(); float radius = NSAppUnitsToFloatPixels(filter.AsBlur().ToAppUnits(),
appUnitsPerDevPixel);
wrFilters.AppendElement(wr::FilterOp::Blur(radius, radius)); break;
} case StyleFilter::Tag::DropShadow: { float appUnitsPerDevPixel =
aFrame->PresContext()->AppUnitsPerDevPixel(); const StyleSimpleShadow& shadow = filter.AsDropShadow();
nscolor color = shadow.color.CalcColor(aFrame);
wr::Shadow wrShadow;
wrShadow.offset = {
NSAppUnitsToFloatPixels(shadow.horizontal.ToAppUnits(),
appUnitsPerDevPixel),
NSAppUnitsToFloatPixels(shadow.vertical.ToAppUnits(),
appUnitsPerDevPixel)};
wrShadow.blur_radius = NSAppUnitsToFloatPixels(shadow.blur.ToAppUnits(),
appUnitsPerDevPixel);
wrShadow.color = {NS_GET_R(color) / 255.0f, NS_GET_G(color) / 255.0f,
NS_GET_B(color) / 255.0f, NS_GET_A(color) / 255.0f};
wrFilters.AppendElement(wr::FilterOp::DropShadow(wrShadow)); break;
} default:
status = WrFiltersStatus::BLOB_FALLBACK; break;
} if (status != WrFiltersStatus::CHAIN) { break;
}
} if (status != WrFiltersStatus::CHAIN) { // Clean up the filters holder if we can't render filters this way.
aWrFilters = {};
} return status;
}
bool SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(nsIFrame* aFrame) {
WrFiltersHolder wrFilters; auto filterChain = aFrame->StyleEffects()->mFilters.AsSpan();
WrFiltersStatus status =
CreateWebRenderCSSFilters(filterChain, aFrame, wrFilters); if (status == WrFiltersStatus::BLOB_FALLBACK) {
status = BuildWebRenderFilters(aFrame, filterChain, StyleFilterType::Filter,
wrFilters, nsPoint());
} return status == WrFiltersStatus::CHAIN || status == WrFiltersStatus::SVGFE;
}
bool SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(
nsIFrame* aFrame) { // WebRender supports masks / clip-paths and some filters in the compositor. if (aFrame->StyleEffects()->HasFilters()) { return !SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(aFrame);
} returnfalse;
}
// Clip to aFillRect so that we don't paint outside.
aContext->Clip(aFillRect);
gfxMatrix invmatrix = aTransform; if (!invmatrix.Invert()) { returnfalse;
}
aContext->Multiply(invmatrix);
// nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want // to have it anchored at the top left corner of the bounding box of all of // mFrame's continuations. So we add a translation transform.
int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
nsPoint offset = SVGIntegrationUtils::GetOffsetToBoundingBox(mFrame);
gfxPoint devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel;
aContext->Multiply(gfxMatrix::Translation(devPxOffset));
// nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we // want it to render with mRenderSize, so we need to set up a scale transform.
gfxFloat scaleX = mRenderSize.width / paintServerSize.width;
gfxFloat scaleY = mRenderSize.height / paintServerSize.height;
aContext->Multiply(gfxMatrix::Scaling(scaleX, scaleY));
/* static */
already_AddRefed<gfxDrawable> SVGIntegrationUtils::DrawableFromPaintServer(
nsIFrame* aFrame, nsIFrame* aTarget, const nsSize& aPaintServerSize, const IntSize& aRenderSize, const DrawTarget* aDrawTarget, const gfxMatrix& aContextMatrix, uint32_t aFlags) { // aPaintServerSize is the size that would be filled when using // background-repeat:no-repeat and background-size:auto. For normal background // images, this would be the intrinsic size of the image; for gradients and // patterns this would be the whole target frame fill area. // aRenderSize is what we will be actually filling after accounting for // background-size. if (SVGPaintServerFrame* server = do_QueryFrame(aFrame)) { // aFrame is either a pattern or a gradient. These fill the whole target // frame by default, so aPaintServerSize is the whole target background fill // area.
gfxRect overrideBounds(0, 0, aPaintServerSize.width,
aPaintServerSize.height);
overrideBounds.Scale(1.0 / aFrame->PresContext()->AppUnitsPerDevPixel());
uint32_t imgFlags = imgIContainer::FLAG_ASYNC_NOTIFY; if (aFlags & SVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) {
imgFlags |= imgIContainer::FLAG_SYNC_DECODE;
}
imgDrawingParams imgParams(imgFlags);
RefPtr<gfxPattern> pattern = server->GetPaintServerPattern(
aTarget, aDrawTarget, aContextMatrix, &nsStyleSVG::mFill, 1.0,
imgParams, &overrideBounds);
if (!pattern) { return nullptr;
}
// pattern is now set up to fill aPaintServerSize. But we want it to // fill aRenderSize, so we need to add a scaling transform. // We couldn't just have set overrideBounds to aRenderSize - it would have // worked for gradients, but for patterns it would result in a different // pattern size.
gfxFloat scaleX = overrideBounds.Width() / aRenderSize.width;
gfxFloat scaleY = overrideBounds.Height() / aRenderSize.height;
gfxMatrix scaleMatrix = gfxMatrix::Scaling(scaleX, scaleY);
pattern->SetMatrix(scaleMatrix * pattern->GetMatrix()); return do_AddRef(new gfxPatternDrawable(pattern, aRenderSize));
}
if (aFrame->IsSVGFrame() &&
!static_cast<ISVGDisplayableFrame*>(do_QueryFrame(aFrame))) {
MOZ_ASSERT_UNREACHABLE( "We should prevent painting of unpaintable SVG " "before we get here"); return nullptr;
}
// We don't want to paint into a surface as long as we don't need to, so we // set up a drawing callback.
RefPtr<gfxDrawingCallback> cb = new PaintFrameCallback(aFrame, aPaintServerSize, aRenderSize, aFlags); return do_AddRef(new gfxCallbackDrawable(cb, aRenderSize));
}
} // namespace mozilla
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.