diff -r 000000000000 -r 6474c204b198 layout/svg/nsSVGUtils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/svg/nsSVGUtils.h Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,579 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 NS_SVGUTILS_H +#define NS_SVGUTILS_H + +// include math.h to pick up definition of M_ maths defines e.g. M_PI +#define _USE_MATH_DEFINES +#include + +#include "DrawMode.h" +#include "gfx2DGlue.h" +#include "gfxMatrix.h" +#include "gfxPoint.h" +#include "gfxRect.h" +#include "mozilla/gfx/Rect.h" +#include "nsAlgorithm.h" +#include "nsChangeHint.h" +#include "nsColor.h" +#include "nsCOMPtr.h" +#include "nsID.h" +#include "nsISupportsBase.h" +#include "nsMathUtils.h" +#include "nsStyleStruct.h" +#include "mozilla/Constants.h" +#include + +class gfxContext; +class gfxPattern; +class nsFrameList; +class nsIContent; +class nsIDocument; +class nsIFrame; +class nsPresContext; +class nsRenderingContext; +class nsStyleContext; +class nsStyleCoord; +class nsSVGDisplayContainerFrame; +class nsSVGElement; +class nsSVGEnum; +class nsSVGLength2; +class nsSVGOuterSVGFrame; +class nsSVGPathGeometryFrame; +class nsTextFrame; +class gfxTextContextPaint; + +struct nsStyleSVG; +struct nsStyleSVGPaint; +struct nsRect; +struct nsIntRect; +struct nsPoint; + +namespace mozilla { +class SVGAnimatedPreserveAspectRatio; +class SVGPreserveAspectRatio; +namespace dom { +class Element; +} // namespace dom +namespace gfx { +class SourceSurface; +} +} // namespace mozilla + +// maximum dimension of an offscreen surface - choose so that +// the surface size doesn't overflow a 32-bit signed int using +// 4 bytes per pixel; in line with gfxASurface::CheckSurfaceSize +// In fact Macs can't even manage that +#define NS_SVG_OFFSCREEN_MAX_DIMENSION 4096 + +#define SVG_HIT_TEST_FILL 0x01 +#define SVG_HIT_TEST_STROKE 0x02 +#define SVG_HIT_TEST_CHECK_MRECT 0x04 + + +bool NS_SVGDisplayListHitTestingEnabled(); +bool NS_SVGDisplayListPaintingEnabled(); + +/** + * Sometimes we need to distinguish between an empty box and a box + * that contains an element that has no size e.g. a point at the origin. + */ +class SVGBBox { + typedef mozilla::gfx::Rect Rect; + +public: + SVGBBox() + : mIsEmpty(true) {} + + SVGBBox(const Rect& aRect) + : mBBox(aRect), mIsEmpty(false) {} + + SVGBBox(const gfxRect& aRect) + : mBBox(ToRect(aRect)), mIsEmpty(false) {} + + gfxRect ToThebesRect() const { + return ThebesRect(mBBox); + } + + bool IsEmpty() const { + return mIsEmpty; + } + + void UnionEdges(const SVGBBox& aSVGBBox) { + if (aSVGBBox.mIsEmpty) { + return; + } + mBBox = mIsEmpty ? aSVGBBox.mBBox : mBBox.UnionEdges(aSVGBBox.mBBox); + mIsEmpty = false; + } + +private: + Rect mBBox; + bool mIsEmpty; +}; + +// GRRR WINDOWS HATE HATE HATE +#undef CLIP_MASK + +class MOZ_STACK_CLASS SVGAutoRenderState +{ +public: + enum RenderMode { + /** + * Used to inform SVG frames that they should paint as normal. + */ + NORMAL, + /** + * Used to inform SVG frames when they are painting as the child of a + * simple clipPath. In this case they should only draw their basic geometry + * as a path. They should not fill, stroke, or paint anything else. + */ + CLIP, + /** + * Used to inform SVG frames when they are painting as the child of a + * complex clipPath that requires the use of a clip mask. In this case they + * should only draw their basic geometry as a path and then fill it using + * fully opaque white. They should not stroke, or paint anything else. + */ + CLIP_MASK + }; + + SVGAutoRenderState(nsRenderingContext *aContext, RenderMode aMode + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + ~SVGAutoRenderState(); + + void SetPaintingToWindow(bool aPaintingToWindow); + + static RenderMode GetRenderMode(nsRenderingContext *aContext); + static bool IsPaintingToWindow(nsRenderingContext *aContext); + +private: + nsRenderingContext *mContext; + void *mOriginalRenderState; + RenderMode mMode; + bool mPaintingToWindow; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + + +#define NS_ISVGFILTERREFERENCE_IID \ +{ 0x9744ee20, 0x1bcf, 0x4c62, \ + { 0x86, 0x7d, 0xd3, 0x7a, 0x91, 0x60, 0x3e, 0xef } } + +class nsISVGFilterReference : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISVGFILTERREFERENCE_IID) + virtual void Invalidate() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGFilterReference, NS_ISVGFILTERREFERENCE_IID) + +/** + * General functions used by all of SVG layout and possibly content code. + * If a method is used by content and depends only on other content methods + * it should go in SVGContentUtils instead. + */ +class nsSVGUtils +{ +public: + typedef mozilla::dom::Element Element; + + static void Init(); + + /** + * Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame + * must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame, + * returns nullptr. + */ + static nsSVGDisplayContainerFrame* GetNearestSVGViewport(nsIFrame *aFrame); + + /** + * Returns the frame's post-filter visual overflow rect when passed the + * frame's pre-filter visual overflow rect. If the frame is not currently + * being filtered, this function simply returns aUnfilteredRect. + */ + static nsRect GetPostFilterVisualOverflowRect(nsIFrame *aFrame, + const nsRect &aUnfilteredRect); + + /** + * Schedules an update of the frame's bounds (which will in turn invalidate + * the new area that the frame should paint to). + * + * This does nothing when passed an NS_FRAME_IS_NONDISPLAY frame. + * In future we may want to allow ReflowSVG to be called on such frames, + * but that would be better implemented as a ForceReflowSVG function to + * be called synchronously while painting them without marking or paying + * attention to dirty bits like this function. + * + * This is very similar to PresShell::FrameNeedsReflow. The main reason that + * we have this function instead of using FrameNeedsReflow is because we need + * to be able to call it under nsSVGOuterSVGFrame::NotifyViewportChange when + * that function is called by nsSVGOuterSVGFrame::Reflow. FrameNeedsReflow + * is not suitable for calling during reflow though, and it asserts as much. + * The reason that we want to be callable under NotifyViewportChange is + * because we want to synchronously notify and dirty the nsSVGOuterSVGFrame's + * children so that when nsSVGOuterSVGFrame::DidReflow is called its children + * will be updated for the new size as appropriate. Otherwise we'd have to + * post an event to the event loop to mark dirty flags and request an update. + * + * Another reason that we don't currently want to call + * PresShell::FrameNeedsReflow is because passing eRestyle to it to get it to + * mark descendants dirty would cause it to descend through + * nsSVGForeignObjectFrame frames to mark their children dirty, but we want to + * handle nsSVGForeignObjectFrame specially. It would also do unnecessary work + * descending into NS_FRAME_IS_NONDISPLAY frames. + */ + static void ScheduleReflowSVG(nsIFrame *aFrame); + + /** + * Returns true if the frame or any of its children need ReflowSVG + * to be called on them. + */ + static bool NeedsReflowSVG(nsIFrame *aFrame); + + /* + * Update the filter invalidation region for ancestor frames, if relevant. + */ + static void NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame); + + /* Computes the input length in terms of object space coordinates. + Input: rect - bounding box + length - length to be converted + */ + static float ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength); + + /* Computes the input length in terms of user space coordinates. + Input: content - object to be used for determining user space + Input: length - length to be converted + */ + static float UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength); + + /* Computes the input length in terms of user space coordinates. + Input: aFrame - object to be used for determining user space + length - length to be converted + */ + static float UserSpace(nsIFrame *aFrame, const nsSVGLength2 *aLength); + + /* Find the outermost SVG frame of the passed frame */ + static nsSVGOuterSVGFrame * + GetOuterSVGFrame(nsIFrame *aFrame); + + /** + * Get the covered region for a frame. Return null if it's not an SVG frame. + * @param aRect gets a rectangle in app units + * @return the outer SVG frame which aRect is relative to + */ + static nsIFrame* + GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect); + + /* Paint SVG frame with SVG effects - aDirtyRect is the area being + * redrawn, in device pixel coordinates relative to the outer svg */ + static void + PaintFrameWithEffects(nsRenderingContext *aContext, + const nsIntRect *aDirtyRect, + nsIFrame *aFrame, + nsIFrame* aTransformRoot = nullptr); + + /* Hit testing - check if point hits the clipPath of indicated + * frame. Returns true if no clipPath set. */ + static bool + HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint); + + /* Hit testing - check if point hits any children of frame. */ + + static nsIFrame * + HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint); + + /* + * Returns the CanvasTM of the indicated frame, whether it's a + * child SVG frame, container SVG frame, or a regular frame. + * For regular frames, we just return an identity matrix. + */ + static gfxMatrix GetCanvasTM(nsIFrame* aFrame, uint32_t aFor, + nsIFrame* aTransformRoot = nullptr); + + /** + * Returns the transform from aFrame's user space to canvas space. Only call + * with SVG frames. This is like GetCanvasTM, except that it only includes + * the transforms from aFrame's user space (i.e. the coordinate context + * established by its 'transform' attribute, or else the coordinate context + * that its _parent_ establishes for its children) to outer- device + * space. Specifically, it does not include any other transforms introduced + * by the frame such as x/y offsets and viewBox attributes. + */ + static gfxMatrix GetUserToCanvasTM(nsIFrame* aFrame, uint32_t aFor); + + /** + * Notify the descendants of aFrame of a change to one of their ancestors + * that might affect them. + */ + static void + NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags); + + /* + * Get frame's covered region by walking the children and doing union. + */ + static nsRect + GetCoveredRegion(const nsFrameList &aFrames); + + // Converts aPoint from an app unit point in outer- content rect space + // to an app unit point in a frame's SVG userspace. + // This is a temporary helper we should no longer need after bug 614732 is + // fixed. + static nsPoint + TransformOuterSVGPointToChildFrame(nsPoint aPoint, + const gfxMatrix& aFrameToCanvasTM, + nsPresContext* aPresContext); + + static nsRect + TransformFrameRectToOuterSVG(const nsRect& aRect, + const gfxMatrix& aMatrix, + nsPresContext* aPresContext); + + /* + * Convert a surface size to an integer for use by thebes + * possibly making it smaller in the process so the surface does not + * use excessive memory. + * + * @param aSize the desired surface size + * @param aResultOverflows true if the desired surface size is too big + * @return the surface size to use + */ + static gfxIntSize ConvertToSurfaceSize(const gfxSize& aSize, + bool *aResultOverflows); + + /* + * Hit test a given rectangle/matrix. + */ + static bool + HitTestRect(const mozilla::gfx::Matrix &aMatrix, + float aRX, float aRY, float aRWidth, float aRHeight, + float aX, float aY); + + + /** + * Get the clip rect for the given frame, taking into account the CSS 'clip' + * property. See: + * http://www.w3.org/TR/SVG11/masking.html#OverflowAndClipProperties + * The arguments for aX, aY, aWidth and aHeight should be the dimensions of + * the viewport established by aFrame. + */ + static gfxRect + GetClipRectForFrame(nsIFrame *aFrame, + float aX, float aY, float aWidth, float aHeight); + + static void SetClipRect(gfxContext *aContext, + const gfxMatrix &aCTM, + const gfxRect &aRect); + + /* Using group opacity instead of fill or stroke opacity on a + * geometry object seems to be a common authoring mistake. If we're + * not applying filters and not both stroking and filling, we can + * generate the same result without going through the overhead of a + * push/pop group. */ + static bool + CanOptimizeOpacity(nsIFrame *aFrame); + + /** + * Take the CTM to userspace for an element, and adjust it to a CTM to its + * object bounding box space if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX. + * (I.e. so that [0,0] is at the top left of its bbox, and [1,1] is at the + * bottom right of its bbox). + * + * If the bbox is empty, this will return a singular matrix. + */ + static gfxMatrix + AdjustMatrixForUnits(const gfxMatrix &aMatrix, + nsSVGEnum *aUnits, + nsIFrame *aFrame); + + enum BBoxFlags { + eBBoxIncludeFill = 1 << 0, + eBBoxIncludeFillGeometry = 1 << 1, + eBBoxIncludeStroke = 1 << 2, + eBBoxIncludeStrokeGeometry = 1 << 3, + eBBoxIncludeMarkers = 1 << 4 + }; + /** + * Get the SVG bbox (the SVG spec's simplified idea of bounds) of aFrame in + * aFrame's userspace. + */ + static gfxRect GetBBox(nsIFrame *aFrame, + uint32_t aFlags = eBBoxIncludeFillGeometry); + + /* + * "User space" is the space that the frame's BBox (as calculated by + * nsSVGUtils::GetBBox) is in. "Frame space" is the space that has its origin + * at the top left of the union of the frame's border-box rects over all + * continuations. + * This function returns the offset one needs to add to something in frame + * space in order to get its coordinates in user space. + */ + static gfxPoint FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame); + + /** + * Convert a userSpaceOnUse/objectBoundingBoxUnits rectangle that's specified + * using four nsSVGLength2 values into a user unit rectangle in user space. + * + * @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing + * the x, y, width and height values in that order + * @param aBBox the bounding box of the object the rect is relative to; + * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX + * @param aFrame the object in which to interpret user-space units; + * may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX + */ + static gfxRect + GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH, + const gfxRect &aBBox, nsIFrame *aFrame); + + /** + * Find the first frame, starting with aStartFrame and going up its + * parent chain, that is not an svgAFrame. + */ + static nsIFrame* GetFirstNonAAncestorFrame(nsIFrame* aStartFrame); + + static bool OuterSVGIsCallingReflowSVG(nsIFrame *aFrame); + static bool AnyOuterSVGIsCallingReflowSVG(nsIFrame *aFrame); + + /* + * Get any additional transforms that apply only to stroking + * e.g. non-scaling-stroke + */ + static gfxMatrix GetStrokeTransform(nsIFrame *aFrame); + + /** + * Compute the maximum possible device space stroke extents of a path given + * the path's device space path extents, its stroke style and its ctm. + * + * This is a workaround for the lack of suitable cairo API for getting the + * tight device space stroke extents of a path. This basically gives us the + * tightest extents that we can guarantee fully enclose the inked stroke + * without doing the calculations for the actual tight extents. We exploit + * the fact that cairo does have an API for getting the tight device space + * fill/path extents. + * + * This should die once bug 478152 is fixed. + */ + static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, + nsTextFrame* aFrame, + const gfxMatrix& aMatrix); + static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, + nsSVGPathGeometryFrame* aFrame, + const gfxMatrix& aMatrix); + + /** + * Convert a floating-point value to a 32-bit integer value, clamping to + * the range of valid integers. + */ + static int32_t ClampToInt(double aVal) + { + return NS_lround(std::max(double(INT32_MIN), + std::min(double(INT32_MAX), aVal))); + } + + static nscolor GetFallbackOrPaintColor(gfxContext *aContext, + nsStyleContext *aStyleContext, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke); + + /** + * Set up cairo context with an object pattern + */ + static bool SetupContextPaint(gfxContext *aContext, + gfxTextContextPaint *aContextPaint, + const nsStyleSVGPaint& aPaint, + float aOpacity); + + /** + * Sets the current paint on the specified gfxContent to be the SVG 'fill' + * for the given frame. + */ + static bool SetupCairoFillPaint(nsIFrame* aFrame, gfxContext* aContext, + gfxTextContextPaint *aContextPaint = nullptr); + + /** + * Sets the current paint on the specified gfxContent to be the SVG 'stroke' + * for the given frame. + */ + static bool SetupCairoStrokePaint(nsIFrame* aFrame, gfxContext* aContext, + gfxTextContextPaint *aContextPaint = nullptr); + + static float GetOpacity(nsStyleSVGOpacitySource aOpacityType, + const float& aOpacity, + gfxTextContextPaint *aOuterContextPaint); + + /* + * @return false if there is no stroke + */ + static bool HasStroke(nsIFrame* aFrame, + gfxTextContextPaint *aContextPaint = nullptr); + + static float GetStrokeWidth(nsIFrame* aFrame, + gfxTextContextPaint *aContextPaint = nullptr); + + /* + * Set up a cairo context for measuring the bounding box of a stroked path. + */ + static void SetupCairoStrokeBBoxGeometry(nsIFrame* aFrame, + gfxContext *aContext, + gfxTextContextPaint *aContextPaint = nullptr); + + /* + * Set up a cairo context for a stroked path (including any dashing that + * applies). + */ + static void SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext *aContext, + gfxTextContextPaint *aContextPaint = nullptr); + + /* + * Set up a cairo context for stroking, including setting up any stroke-related + * properties such as dashing and setting the current paint on the gfxContext. + */ + static bool SetupCairoStroke(nsIFrame* aFrame, gfxContext *aContext, + gfxTextContextPaint *aContextPaint = nullptr); + + /** + * This function returns a set of bit flags indicating which parts of the + * element (fill, stroke, bounds) should intercept pointer events. It takes + * into account the type of element and the value of the 'pointer-events' + * property on the element. + */ + static uint16_t GetGeometryHitTestFlags(nsIFrame* aFrame); + + /** + * Render a SVG glyph. + * @param aElement the SVG glyph element to render + * @param aContext the thebes aContext to draw to + * @param aDrawMode fill or stroke or both (see DrawMode) + * @return true if rendering succeeded + */ + static bool PaintSVGGlyph(Element* aElement, gfxContext* aContext, + DrawMode aDrawMode, + gfxTextContextPaint* aContextPaint); + /** + * Get the extents of a SVG glyph. + * @param aElement the SVG glyph element + * @param aSVGToAppSpace the matrix mapping the SVG glyph space to the + * target context space + * @param aResult the result (valid when true is returned) + * @return true if calculating the extents succeeded + */ + static bool GetSVGGlyphExtents(Element* aElement, + const gfxMatrix& aSVGToAppSpace, + gfxRect* aResult); + + /** + * Returns the app unit canvas bounds of a userspace rect. + * + * @param aToCanvas Transform from userspace to canvas device space. + */ + static nsRect + ToCanvasBounds(const gfxRect &aUserspaceRect, + const gfxMatrix &aToCanvas, + const nsPresContext *presContext); +}; + +#endif