layout/svg/nsSVGUtils.h

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:e6ed5ce1e3e8
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/. */
5
6 #ifndef NS_SVGUTILS_H
7 #define NS_SVGUTILS_H
8
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>
12
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>
29
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 nsSVGDisplayContainerFrame;
41 class nsSVGElement;
42 class nsSVGEnum;
43 class nsSVGLength2;
44 class nsSVGOuterSVGFrame;
45 class nsSVGPathGeometryFrame;
46 class nsTextFrame;
47 class gfxTextContextPaint;
48
49 struct nsStyleSVG;
50 struct nsStyleSVGPaint;
51 struct nsRect;
52 struct nsIntRect;
53 struct nsPoint;
54
55 namespace mozilla {
56 class SVGAnimatedPreserveAspectRatio;
57 class SVGPreserveAspectRatio;
58 namespace dom {
59 class Element;
60 } // namespace dom
61 namespace gfx {
62 class SourceSurface;
63 }
64 } // namespace mozilla
65
66 // maximum dimension of an offscreen surface - choose so that
67 // the surface size doesn't overflow a 32-bit signed int using
68 // 4 bytes per pixel; in line with gfxASurface::CheckSurfaceSize
69 // In fact Macs can't even manage that
70 #define NS_SVG_OFFSCREEN_MAX_DIMENSION 4096
71
72 #define SVG_HIT_TEST_FILL 0x01
73 #define SVG_HIT_TEST_STROKE 0x02
74 #define SVG_HIT_TEST_CHECK_MRECT 0x04
75
76
77 bool NS_SVGDisplayListHitTestingEnabled();
78 bool NS_SVGDisplayListPaintingEnabled();
79
80 /**
81 * Sometimes we need to distinguish between an empty box and a box
82 * that contains an element that has no size e.g. a point at the origin.
83 */
84 class SVGBBox {
85 typedef mozilla::gfx::Rect Rect;
86
87 public:
88 SVGBBox()
89 : mIsEmpty(true) {}
90
91 SVGBBox(const Rect& aRect)
92 : mBBox(aRect), mIsEmpty(false) {}
93
94 SVGBBox(const gfxRect& aRect)
95 : mBBox(ToRect(aRect)), mIsEmpty(false) {}
96
97 gfxRect ToThebesRect() const {
98 return ThebesRect(mBBox);
99 }
100
101 bool IsEmpty() const {
102 return mIsEmpty;
103 }
104
105 void UnionEdges(const SVGBBox& aSVGBBox) {
106 if (aSVGBBox.mIsEmpty) {
107 return;
108 }
109 mBBox = mIsEmpty ? aSVGBBox.mBBox : mBBox.UnionEdges(aSVGBBox.mBBox);
110 mIsEmpty = false;
111 }
112
113 private:
114 Rect mBBox;
115 bool mIsEmpty;
116 };
117
118 // GRRR WINDOWS HATE HATE HATE
119 #undef CLIP_MASK
120
121 class MOZ_STACK_CLASS SVGAutoRenderState
122 {
123 public:
124 enum RenderMode {
125 /**
126 * Used to inform SVG frames that they should paint as normal.
127 */
128 NORMAL,
129 /**
130 * Used to inform SVG frames when they are painting as the child of a
131 * simple clipPath. In this case they should only draw their basic geometry
132 * as a path. They should not fill, stroke, or paint anything else.
133 */
134 CLIP,
135 /**
136 * Used to inform SVG frames when they are painting as the child of a
137 * complex clipPath that requires the use of a clip mask. In this case they
138 * should only draw their basic geometry as a path and then fill it using
139 * fully opaque white. They should not stroke, or paint anything else.
140 */
141 CLIP_MASK
142 };
143
144 SVGAutoRenderState(nsRenderingContext *aContext, RenderMode aMode
145 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
146 ~SVGAutoRenderState();
147
148 void SetPaintingToWindow(bool aPaintingToWindow);
149
150 static RenderMode GetRenderMode(nsRenderingContext *aContext);
151 static bool IsPaintingToWindow(nsRenderingContext *aContext);
152
153 private:
154 nsRenderingContext *mContext;
155 void *mOriginalRenderState;
156 RenderMode mMode;
157 bool mPaintingToWindow;
158 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
159 };
160
161
162 #define NS_ISVGFILTERREFERENCE_IID \
163 { 0x9744ee20, 0x1bcf, 0x4c62, \
164 { 0x86, 0x7d, 0xd3, 0x7a, 0x91, 0x60, 0x3e, 0xef } }
165
166 class nsISVGFilterReference : public nsISupports
167 {
168 public:
169 NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISVGFILTERREFERENCE_IID)
170 virtual void Invalidate() = 0;
171 };
172
173 NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGFilterReference, NS_ISVGFILTERREFERENCE_IID)
174
175 /**
176 * General functions used by all of SVG layout and possibly content code.
177 * If a method is used by content and depends only on other content methods
178 * it should go in SVGContentUtils instead.
179 */
180 class nsSVGUtils
181 {
182 public:
183 typedef mozilla::dom::Element Element;
184
185 static void Init();
186
187 /**
188 * Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame
189 * must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame,
190 * returns nullptr.
191 */
192 static nsSVGDisplayContainerFrame* GetNearestSVGViewport(nsIFrame *aFrame);
193
194 /**
195 * Returns the frame's post-filter visual overflow rect when passed the
196 * frame's pre-filter visual overflow rect. If the frame is not currently
197 * being filtered, this function simply returns aUnfilteredRect.
198 */
199 static nsRect GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
200 const nsRect &aUnfilteredRect);
201
202 /**
203 * Schedules an update of the frame's bounds (which will in turn invalidate
204 * the new area that the frame should paint to).
205 *
206 * This does nothing when passed an NS_FRAME_IS_NONDISPLAY frame.
207 * In future we may want to allow ReflowSVG to be called on such frames,
208 * but that would be better implemented as a ForceReflowSVG function to
209 * be called synchronously while painting them without marking or paying
210 * attention to dirty bits like this function.
211 *
212 * This is very similar to PresShell::FrameNeedsReflow. The main reason that
213 * we have this function instead of using FrameNeedsReflow is because we need
214 * to be able to call it under nsSVGOuterSVGFrame::NotifyViewportChange when
215 * that function is called by nsSVGOuterSVGFrame::Reflow. FrameNeedsReflow
216 * is not suitable for calling during reflow though, and it asserts as much.
217 * The reason that we want to be callable under NotifyViewportChange is
218 * because we want to synchronously notify and dirty the nsSVGOuterSVGFrame's
219 * children so that when nsSVGOuterSVGFrame::DidReflow is called its children
220 * will be updated for the new size as appropriate. Otherwise we'd have to
221 * post an event to the event loop to mark dirty flags and request an update.
222 *
223 * Another reason that we don't currently want to call
224 * PresShell::FrameNeedsReflow is because passing eRestyle to it to get it to
225 * mark descendants dirty would cause it to descend through
226 * nsSVGForeignObjectFrame frames to mark their children dirty, but we want to
227 * handle nsSVGForeignObjectFrame specially. It would also do unnecessary work
228 * descending into NS_FRAME_IS_NONDISPLAY frames.
229 */
230 static void ScheduleReflowSVG(nsIFrame *aFrame);
231
232 /**
233 * Returns true if the frame or any of its children need ReflowSVG
234 * to be called on them.
235 */
236 static bool NeedsReflowSVG(nsIFrame *aFrame);
237
238 /*
239 * Update the filter invalidation region for ancestor frames, if relevant.
240 */
241 static void NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame);
242
243 /* Computes the input length in terms of object space coordinates.
244 Input: rect - bounding box
245 length - length to be converted
246 */
247 static float ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength);
248
249 /* Computes the input length in terms of user space coordinates.
250 Input: content - object to be used for determining user space
251 Input: length - length to be converted
252 */
253 static float UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength);
254
255 /* Computes the input length in terms of user space coordinates.
256 Input: aFrame - object to be used for determining user space
257 length - length to be converted
258 */
259 static float UserSpace(nsIFrame *aFrame, const nsSVGLength2 *aLength);
260
261 /* Find the outermost SVG frame of the passed frame */
262 static nsSVGOuterSVGFrame *
263 GetOuterSVGFrame(nsIFrame *aFrame);
264
265 /**
266 * Get the covered region for a frame. Return null if it's not an SVG frame.
267 * @param aRect gets a rectangle in app units
268 * @return the outer SVG frame which aRect is relative to
269 */
270 static nsIFrame*
271 GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect);
272
273 /* Paint SVG frame with SVG effects - aDirtyRect is the area being
274 * redrawn, in device pixel coordinates relative to the outer svg */
275 static void
276 PaintFrameWithEffects(nsRenderingContext *aContext,
277 const nsIntRect *aDirtyRect,
278 nsIFrame *aFrame,
279 nsIFrame* aTransformRoot = nullptr);
280
281 /* Hit testing - check if point hits the clipPath of indicated
282 * frame. Returns true if no clipPath set. */
283 static bool
284 HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint);
285
286 /* Hit testing - check if point hits any children of frame. */
287
288 static nsIFrame *
289 HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint);
290
291 /*
292 * Returns the CanvasTM of the indicated frame, whether it's a
293 * child SVG frame, container SVG frame, or a regular frame.
294 * For regular frames, we just return an identity matrix.
295 */
296 static gfxMatrix GetCanvasTM(nsIFrame* aFrame, uint32_t aFor,
297 nsIFrame* aTransformRoot = nullptr);
298
299 /**
300 * Returns the transform from aFrame's user space to canvas space. Only call
301 * with SVG frames. This is like GetCanvasTM, except that it only includes
302 * the transforms from aFrame's user space (i.e. the coordinate context
303 * established by its 'transform' attribute, or else the coordinate context
304 * that its _parent_ establishes for its children) to outer-<svg> device
305 * space. Specifically, it does not include any other transforms introduced
306 * by the frame such as x/y offsets and viewBox attributes.
307 */
308 static gfxMatrix GetUserToCanvasTM(nsIFrame* aFrame, uint32_t aFor);
309
310 /**
311 * Notify the descendants of aFrame of a change to one of their ancestors
312 * that might affect them.
313 */
314 static void
315 NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags);
316
317 /*
318 * Get frame's covered region by walking the children and doing union.
319 */
320 static nsRect
321 GetCoveredRegion(const nsFrameList &aFrames);
322
323 // Converts aPoint from an app unit point in outer-<svg> content rect space
324 // to an app unit point in a frame's SVG userspace.
325 // This is a temporary helper we should no longer need after bug 614732 is
326 // fixed.
327 static nsPoint
328 TransformOuterSVGPointToChildFrame(nsPoint aPoint,
329 const gfxMatrix& aFrameToCanvasTM,
330 nsPresContext* aPresContext);
331
332 static nsRect
333 TransformFrameRectToOuterSVG(const nsRect& aRect,
334 const gfxMatrix& aMatrix,
335 nsPresContext* aPresContext);
336
337 /*
338 * Convert a surface size to an integer for use by thebes
339 * possibly making it smaller in the process so the surface does not
340 * use excessive memory.
341 *
342 * @param aSize the desired surface size
343 * @param aResultOverflows true if the desired surface size is too big
344 * @return the surface size to use
345 */
346 static gfxIntSize ConvertToSurfaceSize(const gfxSize& aSize,
347 bool *aResultOverflows);
348
349 /*
350 * Hit test a given rectangle/matrix.
351 */
352 static bool
353 HitTestRect(const mozilla::gfx::Matrix &aMatrix,
354 float aRX, float aRY, float aRWidth, float aRHeight,
355 float aX, float aY);
356
357
358 /**
359 * Get the clip rect for the given frame, taking into account the CSS 'clip'
360 * property. See:
361 * http://www.w3.org/TR/SVG11/masking.html#OverflowAndClipProperties
362 * The arguments for aX, aY, aWidth and aHeight should be the dimensions of
363 * the viewport established by aFrame.
364 */
365 static gfxRect
366 GetClipRectForFrame(nsIFrame *aFrame,
367 float aX, float aY, float aWidth, float aHeight);
368
369 static void SetClipRect(gfxContext *aContext,
370 const gfxMatrix &aCTM,
371 const gfxRect &aRect);
372
373 /* Using group opacity instead of fill or stroke opacity on a
374 * geometry object seems to be a common authoring mistake. If we're
375 * not applying filters and not both stroking and filling, we can
376 * generate the same result without going through the overhead of a
377 * push/pop group. */
378 static bool
379 CanOptimizeOpacity(nsIFrame *aFrame);
380
381 /**
382 * Take the CTM to userspace for an element, and adjust it to a CTM to its
383 * object bounding box space if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX.
384 * (I.e. so that [0,0] is at the top left of its bbox, and [1,1] is at the
385 * bottom right of its bbox).
386 *
387 * If the bbox is empty, this will return a singular matrix.
388 */
389 static gfxMatrix
390 AdjustMatrixForUnits(const gfxMatrix &aMatrix,
391 nsSVGEnum *aUnits,
392 nsIFrame *aFrame);
393
394 enum BBoxFlags {
395 eBBoxIncludeFill = 1 << 0,
396 eBBoxIncludeFillGeometry = 1 << 1,
397 eBBoxIncludeStroke = 1 << 2,
398 eBBoxIncludeStrokeGeometry = 1 << 3,
399 eBBoxIncludeMarkers = 1 << 4
400 };
401 /**
402 * Get the SVG bbox (the SVG spec's simplified idea of bounds) of aFrame in
403 * aFrame's userspace.
404 */
405 static gfxRect GetBBox(nsIFrame *aFrame,
406 uint32_t aFlags = eBBoxIncludeFillGeometry);
407
408 /*
409 * "User space" is the space that the frame's BBox (as calculated by
410 * nsSVGUtils::GetBBox) is in. "Frame space" is the space that has its origin
411 * at the top left of the union of the frame's border-box rects over all
412 * continuations.
413 * This function returns the offset one needs to add to something in frame
414 * space in order to get its coordinates in user space.
415 */
416 static gfxPoint FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame);
417
418 /**
419 * Convert a userSpaceOnUse/objectBoundingBoxUnits rectangle that's specified
420 * using four nsSVGLength2 values into a user unit rectangle in user space.
421 *
422 * @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing
423 * the x, y, width and height values in that order
424 * @param aBBox the bounding box of the object the rect is relative to;
425 * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
426 * @param aFrame the object in which to interpret user-space units;
427 * may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
428 */
429 static gfxRect
430 GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
431 const gfxRect &aBBox, nsIFrame *aFrame);
432
433 /**
434 * Find the first frame, starting with aStartFrame and going up its
435 * parent chain, that is not an svgAFrame.
436 */
437 static nsIFrame* GetFirstNonAAncestorFrame(nsIFrame* aStartFrame);
438
439 static bool OuterSVGIsCallingReflowSVG(nsIFrame *aFrame);
440 static bool AnyOuterSVGIsCallingReflowSVG(nsIFrame *aFrame);
441
442 /*
443 * Get any additional transforms that apply only to stroking
444 * e.g. non-scaling-stroke
445 */
446 static gfxMatrix GetStrokeTransform(nsIFrame *aFrame);
447
448 /**
449 * Compute the maximum possible device space stroke extents of a path given
450 * the path's device space path extents, its stroke style and its ctm.
451 *
452 * This is a workaround for the lack of suitable cairo API for getting the
453 * tight device space stroke extents of a path. This basically gives us the
454 * tightest extents that we can guarantee fully enclose the inked stroke
455 * without doing the calculations for the actual tight extents. We exploit
456 * the fact that cairo does have an API for getting the tight device space
457 * fill/path extents.
458 *
459 * This should die once bug 478152 is fixed.
460 */
461 static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
462 nsTextFrame* aFrame,
463 const gfxMatrix& aMatrix);
464 static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
465 nsSVGPathGeometryFrame* aFrame,
466 const gfxMatrix& aMatrix);
467
468 /**
469 * Convert a floating-point value to a 32-bit integer value, clamping to
470 * the range of valid integers.
471 */
472 static int32_t ClampToInt(double aVal)
473 {
474 return NS_lround(std::max(double(INT32_MIN),
475 std::min(double(INT32_MAX), aVal)));
476 }
477
478 static nscolor GetFallbackOrPaintColor(gfxContext *aContext,
479 nsStyleContext *aStyleContext,
480 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke);
481
482 /**
483 * Set up cairo context with an object pattern
484 */
485 static bool SetupContextPaint(gfxContext *aContext,
486 gfxTextContextPaint *aContextPaint,
487 const nsStyleSVGPaint& aPaint,
488 float aOpacity);
489
490 /**
491 * Sets the current paint on the specified gfxContent to be the SVG 'fill'
492 * for the given frame.
493 */
494 static bool SetupCairoFillPaint(nsIFrame* aFrame, gfxContext* aContext,
495 gfxTextContextPaint *aContextPaint = nullptr);
496
497 /**
498 * Sets the current paint on the specified gfxContent to be the SVG 'stroke'
499 * for the given frame.
500 */
501 static bool SetupCairoStrokePaint(nsIFrame* aFrame, gfxContext* aContext,
502 gfxTextContextPaint *aContextPaint = nullptr);
503
504 static float GetOpacity(nsStyleSVGOpacitySource aOpacityType,
505 const float& aOpacity,
506 gfxTextContextPaint *aOuterContextPaint);
507
508 /*
509 * @return false if there is no stroke
510 */
511 static bool HasStroke(nsIFrame* aFrame,
512 gfxTextContextPaint *aContextPaint = nullptr);
513
514 static float GetStrokeWidth(nsIFrame* aFrame,
515 gfxTextContextPaint *aContextPaint = nullptr);
516
517 /*
518 * Set up a cairo context for measuring the bounding box of a stroked path.
519 */
520 static void SetupCairoStrokeBBoxGeometry(nsIFrame* aFrame,
521 gfxContext *aContext,
522 gfxTextContextPaint *aContextPaint = nullptr);
523
524 /*
525 * Set up a cairo context for a stroked path (including any dashing that
526 * applies).
527 */
528 static void SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext *aContext,
529 gfxTextContextPaint *aContextPaint = nullptr);
530
531 /*
532 * Set up a cairo context for stroking, including setting up any stroke-related
533 * properties such as dashing and setting the current paint on the gfxContext.
534 */
535 static bool SetupCairoStroke(nsIFrame* aFrame, gfxContext *aContext,
536 gfxTextContextPaint *aContextPaint = nullptr);
537
538 /**
539 * This function returns a set of bit flags indicating which parts of the
540 * element (fill, stroke, bounds) should intercept pointer events. It takes
541 * into account the type of element and the value of the 'pointer-events'
542 * property on the element.
543 */
544 static uint16_t GetGeometryHitTestFlags(nsIFrame* aFrame);
545
546 /**
547 * Render a SVG glyph.
548 * @param aElement the SVG glyph element to render
549 * @param aContext the thebes aContext to draw to
550 * @param aDrawMode fill or stroke or both (see DrawMode)
551 * @return true if rendering succeeded
552 */
553 static bool PaintSVGGlyph(Element* aElement, gfxContext* aContext,
554 DrawMode aDrawMode,
555 gfxTextContextPaint* aContextPaint);
556 /**
557 * Get the extents of a SVG glyph.
558 * @param aElement the SVG glyph element
559 * @param aSVGToAppSpace the matrix mapping the SVG glyph space to the
560 * target context space
561 * @param aResult the result (valid when true is returned)
562 * @return true if calculating the extents succeeded
563 */
564 static bool GetSVGGlyphExtents(Element* aElement,
565 const gfxMatrix& aSVGToAppSpace,
566 gfxRect* aResult);
567
568 /**
569 * Returns the app unit canvas bounds of a userspace rect.
570 *
571 * @param aToCanvas Transform from userspace to canvas device space.
572 */
573 static nsRect
574 ToCanvasBounds(const gfxRect &aUserspaceRect,
575 const gfxMatrix &aToCanvas,
576 const nsPresContext *presContext);
577 };
578
579 #endif

mercurial