gecko-dev/layout/svg/SVGIntegrationUtils.h
Emilio Cobos Álvarez bc989db245 Bug 1787623 - Make invalid and unsupported filters paint the unfiltered frame. r=botond
https://drafts.fxtf.org/filter-effects/#typedef-filter-url:

> A filter reference to a filter element. For example
> url(commonfilters.svg#filter). If the filter references a non-existent
> object or the referenced object is not a filter element, then the
> whole filter chain is ignored. No filter is applied to the object.

Differential Revision: https://phabricator.services.mozilla.com/D156074
2022-09-05 10:24:14 +00:00

270 lines
10 KiB
C++

/* -*- 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/. */
#ifndef LAYOUT_SVG_SVGINTEGRATIONUTILS_H_
#define LAYOUT_SVG_SVGINTEGRATIONUTILS_H_
#include "ImgDrawResult.h"
#include "gfxMatrix.h"
#include "gfxRect.h"
#include "nsRegionFwd.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/ServoStyleConsts.h"
#include "mozilla/webrender/WebRenderTypes.h"
class gfxContext;
class gfxDrawable;
class nsIFrame;
struct nsPoint;
struct nsRect;
struct nsSize;
struct WrFiltersHolder {
nsTArray<mozilla::wr::FilterOp> filters;
nsTArray<mozilla::wr::WrFilterData> filter_datas;
mozilla::Maybe<nsRect> post_filters_clip;
// This exists just to own the values long enough for them to be copied into
// rust.
nsTArray<nsTArray<float>> values;
};
namespace mozilla {
class nsDisplayList;
class nsDisplayListBuilder;
namespace gfx {
class DrawTarget;
} // namespace gfx
/**
* Integration of SVG effects (clipPath clipping, masking and filters) into
* regular display list based painting and hit-testing.
*/
class SVGIntegrationUtils final {
using DrawTarget = gfx::DrawTarget;
using IntRect = gfx::IntRect;
using imgDrawingParams = image::imgDrawingParams;
public:
/**
* Returns true if SVG effects that affect the overflow of the given frame
* are currently applied to the frame.
*/
static bool UsingOverflowAffectingEffects(const nsIFrame* aFrame);
/**
* Returns true if SVG effects are currently applied to this frame.
*/
static bool UsingEffectsForFrame(const nsIFrame* aFrame);
/**
* Returns true if mask or clippath are currently applied to this frame.
*/
static bool UsingMaskOrClipPathForFrame(const nsIFrame* aFrame);
/**
* Returns true if the element has a clippath that is simple enough to
* be represented without a mask in WebRender.
*/
static bool UsingSimpleClipPathForFrame(const nsIFrame* aFrame);
/**
* Returns the size of the union of the border-box rects of all of
* aNonSVGFrame's continuations.
*/
static nsSize GetContinuationUnionSize(nsIFrame* aNonSVGFrame);
/**
* When SVG effects need to resolve percentage, userSpaceOnUse lengths, they
* need a coordinate context to resolve them against. This method provides
* that coordinate context for non-SVG frames with SVG effects applied to
* them. The gfxSize returned is the size of the union of all of the given
* frame's continuations' border boxes, converted to SVG user units (equal to
* CSS px units), as required by the SVG code.
*/
static gfx::Size GetSVGCoordContextForNonSVGFrame(nsIFrame* aNonSVGFrame);
/**
* SVG effects such as SVG filters, masking and clipPath may require an SVG
* "bbox" for the element they're being applied to in order to make decisions
* about positioning, and to resolve various lengths against. This method
* provides the "bbox" for non-SVG frames. The bbox returned is in CSS px
* units, and aUnionContinuations decide whether bbox contains the area of
* current frame only or the union of all aNonSVGFrame's continuations'
* overflow areas, relative to the top-left of the union of all aNonSVGFrame's
* continuations' border box rects.
*/
static gfxRect GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame,
bool aUnionContinuations);
/**
* Used to adjust a frame's pre-effects ink overflow rect to take account
* of SVG effects.
*
* XXX This method will not do the right thing for frames with continuations.
* It really needs all the continuations to have been reflowed before being
* called, but we currently call it on each continuation as its overflow
* rects are set during the reflow of each particular continuation. Gecko's
* current reflow architecture does not allow us to set the overflow rects
* for a whole chain of continuations for a given element at the point when
* the last continuation is reflowed. See:
* http://groups.google.com/group/mozilla.dev.tech.layout/msg/6b179066f3051f65
*/
static nsRect ComputePostEffectsInkOverflowRect(
nsIFrame* aFrame, const nsRect& aPreEffectsOverflowRect);
/**
* Figure out which area of the source is needed given an area to
* repaint
*/
static nsRect GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
const nsRect& aDirtyRect);
/**
* Returns true if the given point is not clipped out by effects.
* @param aPt in appunits relative to aFrame
*/
static bool HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt);
struct MOZ_STACK_CLASS PaintFramesParams {
gfxContext& ctx;
nsIFrame* frame;
nsRect dirtyRect;
nsRect borderArea;
nsDisplayListBuilder* builder;
bool handleOpacity; // If true, PaintMaskAndClipPath/ PaintFilter should
// apply css opacity.
Maybe<LayoutDeviceRect> maskRect;
imgDrawingParams& imgParams;
explicit PaintFramesParams(gfxContext& aCtx, nsIFrame* aFrame,
const nsRect& aDirtyRect,
const nsRect& aBorderArea,
nsDisplayListBuilder* aBuilder,
bool aHandleOpacity,
imgDrawingParams& aImgParams)
: ctx(aCtx),
frame(aFrame),
dirtyRect(aDirtyRect),
borderArea(aBorderArea),
builder(aBuilder),
handleOpacity(aHandleOpacity),
imgParams(aImgParams) {}
};
// This should use FunctionRef instead of std::function because we don't need
// to take ownership of the function. See bug 1490781.
static void PaintMaskAndClipPath(const PaintFramesParams& aParams,
const std::function<void()>& aPaintChild);
/**
* Paint mask of frame onto a given context, aParams.ctx.
* aParams.ctx must contain an A8 surface. Returns false if the mask
* didn't get painted and should be ignored at the call site.
* isMaskComplete is an outparameter returning whether the mask is complete.
* Incomplete masks should not be drawn and the proper fallback behaviour
* depends on if the masked element is html or svg.
*/
static bool PaintMask(const PaintFramesParams& aParams,
bool& aOutIsMaskComplete);
/**
* Paint the frame contents.
* SVG frames will have had matrix propagation set to false already.
* Non-SVG frames have to do their own thing.
* The caller will do a Save()/Restore() as necessary so feel free
* to mess with context state.
* The context will be configured to use the "user space" coordinate
* system if passing aTransform/aDirtyRect, or untouched otherwise.
* @param aImgParams the params to draw with.
* @param aTransform the user-to-device space matrix, if painting with
* filters.
* @param aDirtyRect the dirty rect *in user space pixels*
*/
using SVGFilterPaintCallback = std::function<void(
gfxContext& aContext, imgDrawingParams&, const gfxMatrix* aTransform,
const nsIntRect* aDirtyRect)>;
/**
* Paint non-SVG frame with filter and opacity effect.
*/
static void PaintFilter(const PaintFramesParams& aParams,
Span<const StyleFilter> aFilters,
const SVGFilterPaintCallback& aCallback);
/**
* Build WebRender filters for a frame with CSS filters applied to it.
*/
static bool CreateWebRenderCSSFilters(Span<const StyleFilter> aFilters,
nsIFrame* aFrame,
WrFiltersHolder& aWrFilters);
/**
* Try to build WebRender filters for a frame with SVG filters applied to it
* if the filters are supported.
*/
static bool BuildWebRenderFilters(nsIFrame* aFilteredFrame,
Span<const StyleFilter> aFilters,
WrFiltersHolder& aWrFilters,
bool& aInitialized);
/**
* Check if the filters present on |aFrame| are supported by WebRender.
*/
static bool CanCreateWebRenderFiltersForFrame(nsIFrame* aFrame);
/**
* Check if |aFrame| uses any SVG effects that cannot be rendered in the
* compositor.
*/
static bool UsesSVGEffectsNotSupportedInCompositor(nsIFrame* aFrame);
/**
* @param aRenderingContext the target rendering context in which the paint
* server will be rendered
* @param aTarget the target frame onto which the paint server will be
* rendered
* @param aPaintServer a first-continuation frame to use as the source
* @param aFilter a filter to be applied when scaling
* @param aDest the area the paint server image should be mapped to
* @param aFill the area to be filled with copies of the paint server image
* @param aAnchor a point in aFill which we will ensure is pixel-aligned in
* the output
* @param aDirty pixels outside this area may be skipped
* @param aPaintServerSize 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.
* @param aFlags pass FLAG_SYNC_DECODE_IMAGES and any images in the paint
* server will be decoding synchronously if they are not decoded already.
*/
enum {
FLAG_SYNC_DECODE_IMAGES = 0x01,
};
static already_AddRefed<gfxDrawable> DrawableFromPaintServer(
nsIFrame* aFrame, nsIFrame* aTarget, const nsSize& aPaintServerSize,
const gfx::IntSize& aRenderSize, const DrawTarget* aDrawTarget,
const gfxMatrix& aContextMatrix, uint32_t aFlags);
/**
* For non-SVG frames, this gives the offset to the frame's "user space".
* For SVG frames, this returns a zero offset.
*/
static nsPoint GetOffsetToBoundingBox(nsIFrame* aFrame);
/**
* The offset between the reference frame and the bounding box of the
* target frame in device units.
*/
static gfxPoint GetOffsetToUserSpaceInDevPx(nsIFrame* aFrame,
const PaintFramesParams& aParams);
};
} // namespace mozilla
#endif // LAYOUT_SVG_SVGINTEGRATIONUTILS_H_