Bumping manifests a=b2g-bump
[gecko.git] / layout / svg / nsSVGUtils.h
blobdb81e4d5883a6552993fa03ebe60e5ab082d3bdd
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef NS_SVGUTILS_H
7 #define NS_SVGUTILS_H
9 // include math.h to pick up definition of M_ maths defines e.g. M_PI
10 #define _USE_MATH_DEFINES
11 #include <math.h>
13 #include "DrawMode.h"
14 #include "gfx2DGlue.h"
15 #include "gfxMatrix.h"
16 #include "gfxPoint.h"
17 #include "gfxRect.h"
18 #include "mozilla/gfx/Rect.h"
19 #include "nsAlgorithm.h"
20 #include "nsChangeHint.h"
21 #include "nsColor.h"
22 #include "nsCOMPtr.h"
23 #include "nsID.h"
24 #include "nsISupportsBase.h"
25 #include "nsMathUtils.h"
26 #include "nsStyleStruct.h"
27 #include "mozilla/Constants.h"
28 #include <algorithm>
30 class gfxContext;
31 class gfxPattern;
32 class nsFrameList;
33 class nsIContent;
34 class nsIDocument;
35 class nsIFrame;
36 class nsPresContext;
37 class nsRenderingContext;
38 class nsStyleContext;
39 class nsStyleCoord;
40 class nsSVGClipPathFrame;
41 class nsSVGDisplayContainerFrame;
42 class nsSVGElement;
43 class nsSVGEnum;
44 class nsSVGLength2;
45 class nsSVGOuterSVGFrame;
46 class nsSVGPathGeometryFrame;
47 class nsTextFrame;
48 class gfxTextContextPaint;
50 struct nsStyleSVG;
51 struct nsStyleSVGPaint;
52 struct nsRect;
53 struct nsIntRect;
54 struct nsPoint;
56 namespace mozilla {
57 class SVGAnimatedPreserveAspectRatio;
58 class SVGPreserveAspectRatio;
59 namespace dom {
60 class Element;
61 } // namespace dom
62 namespace gfx {
63 class SourceSurface;
65 } // namespace mozilla
67 // maximum dimension of an offscreen surface - choose so that
68 // the surface size doesn't overflow a 32-bit signed int using
69 // 4 bytes per pixel; in line with gfxASurface::CheckSurfaceSize
70 // In fact Macs can't even manage that
71 #define NS_SVG_OFFSCREEN_MAX_DIMENSION 4096
73 #define SVG_HIT_TEST_FILL 0x01
74 #define SVG_HIT_TEST_STROKE 0x02
75 #define SVG_HIT_TEST_CHECK_MRECT 0x04
78 bool NS_SVGDisplayListHitTestingEnabled();
79 bool NS_SVGDisplayListPaintingEnabled();
80 bool NS_SVGNewGetBBoxEnabled();
82 /**
83 * Sometimes we need to distinguish between an empty box and a box
84 * that contains an element that has no size e.g. a point at the origin.
86 class SVGBBox {
87 typedef mozilla::gfx::Rect Rect;
89 public:
90 SVGBBox()
91 : mIsEmpty(true) {}
93 MOZ_IMPLICIT SVGBBox(const Rect& aRect)
94 : mBBox(aRect), mIsEmpty(false) {}
96 MOZ_IMPLICIT SVGBBox(const gfxRect& aRect)
97 : mBBox(ToRect(aRect)), mIsEmpty(false) {}
99 gfxRect ToThebesRect() const {
100 return ThebesRect(mBBox);
103 bool IsEmpty() const {
104 return mIsEmpty;
107 bool IsFinite() const {
108 return mBBox.IsFinite();
111 void Scale(float aScale) {
112 mBBox.Scale(aScale);
115 void UnionEdges(const SVGBBox& aSVGBBox) {
116 if (aSVGBBox.mIsEmpty) {
117 return;
119 mBBox = mIsEmpty ? aSVGBBox.mBBox : mBBox.UnionEdges(aSVGBBox.mBBox);
120 mIsEmpty = false;
123 void Intersect(const SVGBBox& aSVGBBox) {
124 if (!mIsEmpty && !aSVGBBox.mIsEmpty) {
125 mBBox = mBBox.Intersect(aSVGBBox.mBBox);
126 if (mBBox.IsEmpty()) {
127 mIsEmpty = true;
128 mBBox = Rect(0, 0, 0, 0);
130 } else {
131 mIsEmpty = true;
132 mBBox = Rect(0, 0, 0, 0);
136 private:
137 Rect mBBox;
138 bool mIsEmpty;
141 // GRRR WINDOWS HATE HATE HATE
142 #undef CLIP_MASK
144 class MOZ_STACK_CLASS SVGAutoRenderState
146 public:
147 enum RenderMode {
149 * Used to inform SVG frames that they should paint as normal.
151 NORMAL,
152 /**
153 * Used to inform SVG frames when they are painting as the child of a
154 * simple clipPath. In this case they should only draw their basic geometry
155 * as a path. They should not fill, stroke, or paint anything else.
157 CLIP,
158 /**
159 * Used to inform SVG frames when they are painting as the child of a
160 * complex clipPath that requires the use of a clip mask. In this case they
161 * should only draw their basic geometry as a path and then fill it using
162 * fully opaque white. They should not stroke, or paint anything else.
164 CLIP_MASK
167 SVGAutoRenderState(nsRenderingContext *aContext, RenderMode aMode
168 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
169 ~SVGAutoRenderState();
171 void SetPaintingToWindow(bool aPaintingToWindow);
173 static RenderMode GetRenderMode(nsRenderingContext *aContext);
174 static bool IsPaintingToWindow(nsRenderingContext *aContext);
176 private:
177 nsRenderingContext *mContext;
178 void *mOriginalRenderState;
179 RenderMode mMode;
180 bool mPaintingToWindow;
181 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
185 #define NS_ISVGFILTERREFERENCE_IID \
186 { 0x9744ee20, 0x1bcf, 0x4c62, \
187 { 0x86, 0x7d, 0xd3, 0x7a, 0x91, 0x60, 0x3e, 0xef } }
189 class nsISVGFilterReference : public nsISupports
191 public:
192 NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISVGFILTERREFERENCE_IID)
193 virtual void Invalidate() = 0;
196 NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGFilterReference, NS_ISVGFILTERREFERENCE_IID)
199 * General functions used by all of SVG layout and possibly content code.
200 * If a method is used by content and depends only on other content methods
201 * it should go in SVGContentUtils instead.
203 class nsSVGUtils
205 public:
206 typedef mozilla::dom::Element Element;
208 static void Init();
211 * Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame
212 * must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame,
213 * returns nullptr.
215 static nsSVGDisplayContainerFrame* GetNearestSVGViewport(nsIFrame *aFrame);
218 * Returns the frame's post-filter visual overflow rect when passed the
219 * frame's pre-filter visual overflow rect. If the frame is not currently
220 * being filtered, this function simply returns aUnfilteredRect.
222 static nsRect GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
223 const nsRect &aUnfilteredRect);
226 * Schedules an update of the frame's bounds (which will in turn invalidate
227 * the new area that the frame should paint to).
229 * This does nothing when passed an NS_FRAME_IS_NONDISPLAY frame.
230 * In future we may want to allow ReflowSVG to be called on such frames,
231 * but that would be better implemented as a ForceReflowSVG function to
232 * be called synchronously while painting them without marking or paying
233 * attention to dirty bits like this function.
235 * This is very similar to PresShell::FrameNeedsReflow. The main reason that
236 * we have this function instead of using FrameNeedsReflow is because we need
237 * to be able to call it under nsSVGOuterSVGFrame::NotifyViewportChange when
238 * that function is called by nsSVGOuterSVGFrame::Reflow. FrameNeedsReflow
239 * is not suitable for calling during reflow though, and it asserts as much.
240 * The reason that we want to be callable under NotifyViewportChange is
241 * because we want to synchronously notify and dirty the nsSVGOuterSVGFrame's
242 * children so that when nsSVGOuterSVGFrame::DidReflow is called its children
243 * will be updated for the new size as appropriate. Otherwise we'd have to
244 * post an event to the event loop to mark dirty flags and request an update.
246 * Another reason that we don't currently want to call
247 * PresShell::FrameNeedsReflow is because passing eRestyle to it to get it to
248 * mark descendants dirty would cause it to descend through
249 * nsSVGForeignObjectFrame frames to mark their children dirty, but we want to
250 * handle nsSVGForeignObjectFrame specially. It would also do unnecessary work
251 * descending into NS_FRAME_IS_NONDISPLAY frames.
253 static void ScheduleReflowSVG(nsIFrame *aFrame);
256 * Returns true if the frame or any of its children need ReflowSVG
257 * to be called on them.
259 static bool NeedsReflowSVG(nsIFrame *aFrame);
262 * Update the filter invalidation region for ancestor frames, if relevant.
264 static void NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame);
266 /* Computes the input length in terms of object space coordinates.
267 Input: rect - bounding box
268 length - length to be converted
270 static float ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength);
272 /* Computes the input length in terms of user space coordinates.
273 Input: content - object to be used for determining user space
274 Input: length - length to be converted
276 static float UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength);
278 /* Computes the input length in terms of user space coordinates.
279 Input: aFrame - object to be used for determining user space
280 length - length to be converted
282 static float UserSpace(nsIFrame *aFrame, const nsSVGLength2 *aLength);
284 /* Find the outermost SVG frame of the passed frame */
285 static nsSVGOuterSVGFrame *
286 GetOuterSVGFrame(nsIFrame *aFrame);
289 * Get the covered region for a frame. Return null if it's not an SVG frame.
290 * @param aRect gets a rectangle in app units
291 * @return the outer SVG frame which aRect is relative to
293 static nsIFrame*
294 GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect);
296 /* Paint SVG frame with SVG effects - aDirtyRect is the area being
297 * redrawn, in device pixel coordinates relative to the outer svg */
298 static void
299 PaintFrameWithEffects(nsRenderingContext *aContext,
300 const nsIntRect *aDirtyRect,
301 nsIFrame *aFrame,
302 nsIFrame* aTransformRoot = nullptr);
304 /* Hit testing - check if point hits the clipPath of indicated
305 * frame. Returns true if no clipPath set. */
306 static bool
307 HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint);
310 * Hit testing - check if point hits any children of aFrame. aPoint is
311 * expected to be in the coordinate space established by aFrame for its
312 * children (e.g. the space established by the 'viewBox' attribute on <svg>).
314 static nsIFrame *
315 HitTestChildren(nsSVGDisplayContainerFrame *aFrame, const gfxPoint &aPoint);
318 * Returns the CanvasTM of the indicated frame, whether it's a
319 * child SVG frame, container SVG frame, or a regular frame.
320 * For regular frames, we just return an identity matrix.
322 static gfxMatrix GetCanvasTM(nsIFrame* aFrame, uint32_t aFor,
323 nsIFrame* aTransformRoot = nullptr);
326 * Returns the transform from aFrame's user space to canvas space. Only call
327 * with SVG frames. This is like GetCanvasTM, except that it only includes
328 * the transforms from aFrame's user space (i.e. the coordinate context
329 * established by its 'transform' attribute, or else the coordinate context
330 * that its _parent_ establishes for its children) to outer-<svg> device
331 * space. Specifically, it does not include any other transforms introduced
332 * by the frame such as x/y offsets and viewBox attributes.
334 static gfxMatrix GetUserToCanvasTM(nsIFrame* aFrame, uint32_t aFor);
337 * Notify the descendants of aFrame of a change to one of their ancestors
338 * that might affect them.
340 static void
341 NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags);
344 * Get frame's covered region by walking the children and doing union.
346 static nsRect
347 GetCoveredRegion(const nsFrameList &aFrames);
349 // Converts aPoint from an app unit point in outer-<svg> content rect space
350 // to an app unit point in a frame's SVG userspace.
351 // This is a temporary helper we should no longer need after bug 614732 is
352 // fixed.
353 static nsPoint
354 TransformOuterSVGPointToChildFrame(nsPoint aPoint,
355 const gfxMatrix& aFrameToCanvasTM,
356 nsPresContext* aPresContext);
358 static nsRect
359 TransformFrameRectToOuterSVG(const nsRect& aRect,
360 const gfxMatrix& aMatrix,
361 nsPresContext* aPresContext);
364 * Convert a surface size to an integer for use by thebes
365 * possibly making it smaller in the process so the surface does not
366 * use excessive memory.
368 * @param aSize the desired surface size
369 * @param aResultOverflows true if the desired surface size is too big
370 * @return the surface size to use
372 static gfxIntSize ConvertToSurfaceSize(const gfxSize& aSize,
373 bool *aResultOverflows);
376 * Hit test a given rectangle/matrix.
378 static bool
379 HitTestRect(const mozilla::gfx::Matrix &aMatrix,
380 float aRX, float aRY, float aRWidth, float aRHeight,
381 float aX, float aY);
385 * Get the clip rect for the given frame, taking into account the CSS 'clip'
386 * property. See:
387 * http://www.w3.org/TR/SVG11/masking.html#OverflowAndClipProperties
388 * The arguments for aX, aY, aWidth and aHeight should be the dimensions of
389 * the viewport established by aFrame.
391 static gfxRect
392 GetClipRectForFrame(nsIFrame *aFrame,
393 float aX, float aY, float aWidth, float aHeight);
395 static void SetClipRect(gfxContext *aContext,
396 const gfxMatrix &aCTM,
397 const gfxRect &aRect);
399 /* Using group opacity instead of fill or stroke opacity on a
400 * geometry object seems to be a common authoring mistake. If we're
401 * not applying filters and not both stroking and filling, we can
402 * generate the same result without going through the overhead of a
403 * push/pop group. */
404 static bool
405 CanOptimizeOpacity(nsIFrame *aFrame);
408 * Take the CTM to userspace for an element, and adjust it to a CTM to its
409 * object bounding box space if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX.
410 * (I.e. so that [0,0] is at the top left of its bbox, and [1,1] is at the
411 * bottom right of its bbox).
413 * If the bbox is empty, this will return a singular matrix.
415 static gfxMatrix
416 AdjustMatrixForUnits(const gfxMatrix &aMatrix,
417 nsSVGEnum *aUnits,
418 nsIFrame *aFrame);
420 enum BBoxFlags {
421 eBBoxIncludeFill = 1 << 0,
422 eBBoxIncludeFillGeometry = 1 << 1,
423 eBBoxIncludeStroke = 1 << 2,
424 eBBoxIncludeStrokeGeometry = 1 << 3,
425 eBBoxIncludeMarkers = 1 << 4,
426 eBBoxIncludeClipped = 1 << 5
429 * Get the SVG bbox (the SVG spec's simplified idea of bounds) of aFrame in
430 * aFrame's userspace.
432 static gfxRect GetBBox(nsIFrame *aFrame,
433 uint32_t aFlags = eBBoxIncludeFillGeometry);
436 * "User space" is the space that the frame's BBox (as calculated by
437 * nsSVGUtils::GetBBox) is in. "Frame space" is the space that has its origin
438 * at the top left of the union of the frame's border-box rects over all
439 * continuations.
440 * This function returns the offset one needs to add to something in frame
441 * space in order to get its coordinates in user space.
443 static gfxPoint FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame);
446 * Convert a userSpaceOnUse/objectBoundingBoxUnits rectangle that's specified
447 * using four nsSVGLength2 values into a user unit rectangle in user space.
449 * @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing
450 * the x, y, width and height values in that order
451 * @param aBBox the bounding box of the object the rect is relative to;
452 * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
453 * @param aFrame the object in which to interpret user-space units;
454 * may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
456 static gfxRect
457 GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
458 const gfxRect &aBBox, nsIFrame *aFrame);
461 * Find the first frame, starting with aStartFrame and going up its
462 * parent chain, that is not an svgAFrame.
464 static nsIFrame* GetFirstNonAAncestorFrame(nsIFrame* aStartFrame);
466 static bool OuterSVGIsCallingReflowSVG(nsIFrame *aFrame);
467 static bool AnyOuterSVGIsCallingReflowSVG(nsIFrame *aFrame);
470 * Get any additional transforms that apply only to stroking
471 * e.g. non-scaling-stroke
473 static gfxMatrix GetStrokeTransform(nsIFrame *aFrame);
476 * Compute the maximum possible device space stroke extents of a path given
477 * the path's device space path extents, its stroke style and its ctm.
479 * This is a workaround for the lack of suitable cairo API for getting the
480 * tight device space stroke extents of a path. This basically gives us the
481 * tightest extents that we can guarantee fully enclose the inked stroke
482 * without doing the calculations for the actual tight extents. We exploit
483 * the fact that cairo does have an API for getting the tight device space
484 * fill/path extents.
486 * This should die once bug 478152 is fixed.
488 static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
489 nsTextFrame* aFrame,
490 const gfxMatrix& aMatrix);
491 static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
492 nsSVGPathGeometryFrame* aFrame,
493 const gfxMatrix& aMatrix);
496 * Convert a floating-point value to a 32-bit integer value, clamping to
497 * the range of valid integers.
499 static int32_t ClampToInt(double aVal)
501 return NS_lround(std::max(double(INT32_MIN),
502 std::min(double(INT32_MAX), aVal)));
505 static nscolor GetFallbackOrPaintColor(gfxContext *aContext,
506 nsStyleContext *aStyleContext,
507 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke);
510 * Set up cairo context with an object pattern
512 static bool SetupContextPaint(gfxContext *aContext,
513 gfxTextContextPaint *aContextPaint,
514 const nsStyleSVGPaint& aPaint,
515 float aOpacity);
518 * Sets the current paint on the specified gfxContent to be the SVG 'fill'
519 * for the given frame.
521 static bool SetupCairoFillPaint(nsIFrame* aFrame, gfxContext* aContext,
522 gfxTextContextPaint *aContextPaint = nullptr);
525 * Sets the current paint on the specified gfxContent to be the SVG 'stroke'
526 * for the given frame.
528 static bool SetupCairoStrokePaint(nsIFrame* aFrame, gfxContext* aContext,
529 gfxTextContextPaint *aContextPaint = nullptr);
531 static float GetOpacity(nsStyleSVGOpacitySource aOpacityType,
532 const float& aOpacity,
533 gfxTextContextPaint *aOuterContextPaint);
536 * @return false if there is no stroke
538 static bool HasStroke(nsIFrame* aFrame,
539 gfxTextContextPaint *aContextPaint = nullptr);
541 static float GetStrokeWidth(nsIFrame* aFrame,
542 gfxTextContextPaint *aContextPaint = nullptr);
545 * Set up a cairo context for measuring the bounding box of a stroked path.
547 static void SetupCairoStrokeBBoxGeometry(nsIFrame* aFrame,
548 gfxContext *aContext,
549 gfxTextContextPaint *aContextPaint = nullptr);
552 * Set up a cairo context for a stroked path (including any dashing that
553 * applies).
555 static void SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext *aContext,
556 gfxTextContextPaint *aContextPaint = nullptr);
559 * Set up a cairo context for stroking, including setting up any stroke-related
560 * properties such as dashing and setting the current paint on the gfxContext.
562 static bool SetupCairoStroke(nsIFrame* aFrame, gfxContext *aContext,
563 gfxTextContextPaint *aContextPaint = nullptr);
566 * This function returns a set of bit flags indicating which parts of the
567 * element (fill, stroke, bounds) should intercept pointer events. It takes
568 * into account the type of element and the value of the 'pointer-events'
569 * property on the element.
571 static uint16_t GetGeometryHitTestFlags(nsIFrame* aFrame);
574 * Render a SVG glyph.
575 * @param aElement the SVG glyph element to render
576 * @param aContext the thebes aContext to draw to
577 * @param aDrawMode fill or stroke or both (see DrawMode)
578 * @return true if rendering succeeded
580 static bool PaintSVGGlyph(Element* aElement, gfxContext* aContext,
581 DrawMode aDrawMode,
582 gfxTextContextPaint* aContextPaint);
584 * Get the extents of a SVG glyph.
585 * @param aElement the SVG glyph element
586 * @param aSVGToAppSpace the matrix mapping the SVG glyph space to the
587 * target context space
588 * @param aResult the result (valid when true is returned)
589 * @return true if calculating the extents succeeded
591 static bool GetSVGGlyphExtents(Element* aElement,
592 const gfxMatrix& aSVGToAppSpace,
593 gfxRect* aResult);
596 * Returns the app unit canvas bounds of a userspace rect.
598 * @param aToCanvas Transform from userspace to canvas device space.
600 static nsRect
601 ToCanvasBounds(const gfxRect &aUserspaceRect,
602 const gfxMatrix &aToCanvas,
603 const nsPresContext *presContext);
606 #endif