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/. */
7 // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
8 #include "nsSVGUtils.h"
11 // Keep others in (case-insensitive) order:
12 #include "gfxContext.h"
13 #include "gfxImageSurface.h"
14 #include "gfxMatrix.h"
15 #include "gfxPlatform.h"
18 #include "mozilla/gfx/2D.h"
19 #include "mozilla/Preferences.h"
20 #include "nsCSSFrameConstructor.h"
21 #include "nsDisplayList.h"
22 #include "nsFrameList.h"
23 #include "nsGkAtoms.h"
24 #include "nsIContent.h"
25 #include "nsIDocument.h"
27 #include "nsINameSpaceManager.h"
28 #include "nsIPresShell.h"
29 #include "nsISVGChildFrame.h"
30 #include "nsPresContext.h"
31 #include "nsRenderingContext.h"
32 #include "nsStyleCoord.h"
33 #include "nsStyleStruct.h"
34 #include "nsSVGClipPathFrame.h"
35 #include "nsSVGContainerFrame.h"
36 #include "nsSVGEffects.h"
37 #include "nsSVGFilterFrame.h"
38 #include "nsSVGFilterPaintCallback.h"
39 #include "nsSVGForeignObjectFrame.h"
40 #include "nsSVGGeometryFrame.h"
41 #include "nsSVGGlyphFrame.h"
42 #include "nsSVGInnerSVGFrame.h"
43 #include "nsSVGIntegrationUtils.h"
44 #include "nsSVGLength2.h"
45 #include "nsSVGMaskFrame.h"
46 #include "nsSVGOuterSVGFrame.h"
47 #include "mozilla/dom/SVGPathElement.h"
48 #include "nsSVGPathGeometryElement.h"
49 #include "nsSVGPathGeometryFrame.h"
50 #include "nsSVGPaintServerFrame.h"
51 #include "mozilla/dom/SVGSVGElement.h"
52 #include "nsSVGTextContainerFrame.h"
53 #include "nsTextFrame.h"
54 #include "SVGContentUtils.h"
55 #include "mozilla/unused.h"
57 using namespace mozilla
;
58 using namespace mozilla::dom
;
59 using namespace mozilla::gfx
;
62 // (c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1 / 2.4) - 0.055) * 255 + 0.5
63 static const uint8_t glinearRGBTosRGBMap
[256] = {
64 0, 13, 22, 28, 34, 38, 42, 46,
65 50, 53, 56, 59, 61, 64, 66, 69,
66 71, 73, 75, 77, 79, 81, 83, 85,
67 86, 88, 90, 92, 93, 95, 96, 98,
68 99, 101, 102, 104, 105, 106, 108, 109,
69 110, 112, 113, 114, 115, 117, 118, 119,
70 120, 121, 122, 124, 125, 126, 127, 128,
71 129, 130, 131, 132, 133, 134, 135, 136,
72 137, 138, 139, 140, 141, 142, 143, 144,
73 145, 146, 147, 148, 148, 149, 150, 151,
74 152, 153, 154, 155, 155, 156, 157, 158,
75 159, 159, 160, 161, 162, 163, 163, 164,
76 165, 166, 167, 167, 168, 169, 170, 170,
77 171, 172, 173, 173, 174, 175, 175, 176,
78 177, 178, 178, 179, 180, 180, 181, 182,
79 182, 183, 184, 185, 185, 186, 187, 187,
80 188, 189, 189, 190, 190, 191, 192, 192,
81 193, 194, 194, 195, 196, 196, 197, 197,
82 198, 199, 199, 200, 200, 201, 202, 202,
83 203, 203, 204, 205, 205, 206, 206, 207,
84 208, 208, 209, 209, 210, 210, 211, 212,
85 212, 213, 213, 214, 214, 215, 215, 216,
86 216, 217, 218, 218, 219, 219, 220, 220,
87 221, 221, 222, 222, 223, 223, 224, 224,
88 225, 226, 226, 227, 227, 228, 228, 229,
89 229, 230, 230, 231, 231, 232, 232, 233,
90 233, 234, 234, 235, 235, 236, 236, 237,
91 237, 238, 238, 238, 239, 239, 240, 240,
92 241, 241, 242, 242, 243, 243, 244, 244,
93 245, 245, 246, 246, 246, 247, 247, 248,
94 248, 249, 249, 250, 250, 251, 251, 251,
95 252, 252, 253, 253, 254, 254, 255, 255
99 // c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5
100 static const uint8_t gsRGBToLinearRGBMap
[256] = {
101 0, 0, 0, 0, 0, 0, 0, 1,
102 1, 1, 1, 1, 1, 1, 1, 1,
103 1, 1, 2, 2, 2, 2, 2, 2,
104 2, 2, 3, 3, 3, 3, 3, 3,
105 4, 4, 4, 4, 4, 5, 5, 5,
106 5, 6, 6, 6, 6, 7, 7, 7,
107 8, 8, 8, 8, 9, 9, 9, 10,
108 10, 10, 11, 11, 12, 12, 12, 13,
109 13, 13, 14, 14, 15, 15, 16, 16,
110 17, 17, 17, 18, 18, 19, 19, 20,
111 20, 21, 22, 22, 23, 23, 24, 24,
112 25, 25, 26, 27, 27, 28, 29, 29,
113 30, 30, 31, 32, 32, 33, 34, 35,
114 35, 36, 37, 37, 38, 39, 40, 41,
115 41, 42, 43, 44, 45, 45, 46, 47,
116 48, 49, 50, 51, 51, 52, 53, 54,
117 55, 56, 57, 58, 59, 60, 61, 62,
118 63, 64, 65, 66, 67, 68, 69, 70,
119 71, 72, 73, 74, 76, 77, 78, 79,
120 80, 81, 82, 84, 85, 86, 87, 88,
121 90, 91, 92, 93, 95, 96, 97, 99,
122 100, 101, 103, 104, 105, 107, 108, 109,
123 111, 112, 114, 115, 116, 118, 119, 121,
124 122, 124, 125, 127, 128, 130, 131, 133,
125 134, 136, 138, 139, 141, 142, 144, 146,
126 147, 149, 151, 152, 154, 156, 157, 159,
127 161, 163, 164, 166, 168, 170, 171, 173,
128 175, 177, 179, 181, 183, 184, 186, 188,
129 190, 192, 194, 196, 198, 200, 202, 204,
130 206, 208, 210, 212, 214, 216, 218, 220,
131 222, 224, 226, 229, 231, 233, 235, 237,
132 239, 242, 244, 246, 248, 250, 253, 255
135 static bool sSVGDisplayListHitTestingEnabled
;
136 static bool sSVGDisplayListPaintingEnabled
;
137 static bool sSVGTextCSSFramesEnabled
;
140 NS_SVGDisplayListHitTestingEnabled()
142 return sSVGDisplayListHitTestingEnabled
;
146 NS_SVGDisplayListPaintingEnabled()
148 return sSVGDisplayListPaintingEnabled
;
152 NS_SVGTextCSSFramesEnabled()
154 return sSVGTextCSSFramesEnabled
;
157 // we only take the address of this:
158 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey
;
160 SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext
*aContext
,
163 , mOriginalRenderState(nullptr)
165 , mPaintingToWindow(false)
167 mOriginalRenderState
= aContext
->RemoveUserData(&sSVGAutoRenderStateKey
);
168 // We always remove ourselves from aContext before it dies, so
169 // passing nullptr as the destroy function is okay.
170 aContext
->AddUserData(&sSVGAutoRenderStateKey
, this, nullptr);
173 SVGAutoRenderState::~SVGAutoRenderState()
175 mContext
->RemoveUserData(&sSVGAutoRenderStateKey
);
176 if (mOriginalRenderState
) {
177 mContext
->AddUserData(&sSVGAutoRenderStateKey
, mOriginalRenderState
, nullptr);
182 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow
)
184 mPaintingToWindow
= aPaintingToWindow
;
187 /* static */ SVGAutoRenderState::RenderMode
188 SVGAutoRenderState::GetRenderMode(nsRenderingContext
*aContext
)
190 void *state
= aContext
->GetUserData(&sSVGAutoRenderStateKey
);
192 return static_cast<SVGAutoRenderState
*>(state
)->mMode
;
198 SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext
*aContext
)
200 void *state
= aContext
->GetUserData(&sSVGAutoRenderStateKey
);
202 return static_cast<SVGAutoRenderState
*>(state
)->mPaintingToWindow
;
210 Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled
,
211 "svg.display-lists.hit-testing.enabled");
213 Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled
,
214 "svg.display-lists.painting.enabled");
216 Preferences::AddBoolVarCache(&sSVGTextCSSFramesEnabled
,
217 "svg.text.css-frames.enabled");
221 nsSVGUtils::UnPremultiplyImageDataAlpha(uint8_t *data
,
223 const nsIntRect
&rect
)
225 for (int32_t y
= rect
.y
; y
< rect
.YMost(); y
++) {
226 for (int32_t x
= rect
.x
; x
< rect
.XMost(); x
++) {
227 uint8_t *pixel
= data
+ stride
* y
+ 4 * x
;
229 uint8_t a
= pixel
[GFX_ARGB32_OFFSET_A
];
234 pixel
[GFX_ARGB32_OFFSET_B
] = (255 * pixel
[GFX_ARGB32_OFFSET_B
]) / a
;
235 pixel
[GFX_ARGB32_OFFSET_G
] = (255 * pixel
[GFX_ARGB32_OFFSET_G
]) / a
;
236 pixel
[GFX_ARGB32_OFFSET_R
] = (255 * pixel
[GFX_ARGB32_OFFSET_R
]) / a
;
238 pixel
[GFX_ARGB32_OFFSET_B
] = 0;
239 pixel
[GFX_ARGB32_OFFSET_G
] = 0;
240 pixel
[GFX_ARGB32_OFFSET_R
] = 0;
247 nsSVGUtils::PremultiplyImageDataAlpha(uint8_t *data
,
249 const nsIntRect
&rect
)
251 for (int32_t y
= rect
.y
; y
< rect
.YMost(); y
++) {
252 for (int32_t x
= rect
.x
; x
< rect
.XMost(); x
++) {
253 uint8_t *pixel
= data
+ stride
* y
+ 4 * x
;
255 uint8_t a
= pixel
[GFX_ARGB32_OFFSET_A
];
259 FAST_DIVIDE_BY_255(pixel
[GFX_ARGB32_OFFSET_B
],
260 pixel
[GFX_ARGB32_OFFSET_B
] * a
);
261 FAST_DIVIDE_BY_255(pixel
[GFX_ARGB32_OFFSET_G
],
262 pixel
[GFX_ARGB32_OFFSET_G
] * a
);
263 FAST_DIVIDE_BY_255(pixel
[GFX_ARGB32_OFFSET_R
],
264 pixel
[GFX_ARGB32_OFFSET_R
] * a
);
270 nsSVGUtils::ConvertImageDataToLinearRGB(uint8_t *data
,
272 const nsIntRect
&rect
)
274 for (int32_t y
= rect
.y
; y
< rect
.YMost(); y
++) {
275 for (int32_t x
= rect
.x
; x
< rect
.XMost(); x
++) {
276 uint8_t *pixel
= data
+ stride
* y
+ 4 * x
;
278 pixel
[GFX_ARGB32_OFFSET_B
] =
279 gsRGBToLinearRGBMap
[pixel
[GFX_ARGB32_OFFSET_B
]];
280 pixel
[GFX_ARGB32_OFFSET_G
] =
281 gsRGBToLinearRGBMap
[pixel
[GFX_ARGB32_OFFSET_G
]];
282 pixel
[GFX_ARGB32_OFFSET_R
] =
283 gsRGBToLinearRGBMap
[pixel
[GFX_ARGB32_OFFSET_R
]];
289 nsSVGUtils::ConvertImageDataFromLinearRGB(uint8_t *data
,
291 const nsIntRect
&rect
)
293 for (int32_t y
= rect
.y
; y
< rect
.YMost(); y
++) {
294 for (int32_t x
= rect
.x
; x
< rect
.XMost(); x
++) {
295 uint8_t *pixel
= data
+ stride
* y
+ 4 * x
;
297 pixel
[GFX_ARGB32_OFFSET_B
] =
298 glinearRGBTosRGBMap
[pixel
[GFX_ARGB32_OFFSET_B
]];
299 pixel
[GFX_ARGB32_OFFSET_G
] =
300 glinearRGBTosRGBMap
[pixel
[GFX_ARGB32_OFFSET_G
]];
301 pixel
[GFX_ARGB32_OFFSET_R
] =
302 glinearRGBTosRGBMap
[pixel
[GFX_ARGB32_OFFSET_R
]];
308 nsSVGUtils::ComputesRGBLuminanceMask(uint8_t *aData
,
310 const nsIntRect
&aRect
,
313 for (int32_t y
= aRect
.y
; y
< aRect
.YMost(); y
++) {
314 for (int32_t x
= aRect
.x
; x
< aRect
.XMost(); x
++) {
315 uint8_t *pixel
= aData
+ aStride
* y
+ 4 * x
;
316 uint8_t a
= pixel
[GFX_ARGB32_OFFSET_A
];
320 /* sRGB -> intensity (unpremultiply cancels out the
321 * (a/255.0) multiplication with aOpacity */
324 ((pixel
[GFX_ARGB32_OFFSET_R
] * 0.2125 +
325 pixel
[GFX_ARGB32_OFFSET_G
] * 0.7154 +
326 pixel
[GFX_ARGB32_OFFSET_B
] * 0.0721) *
332 memset(pixel
, luminance
, 4);
338 nsSVGUtils::ComputeLinearRGBLuminanceMask(uint8_t *aData
,
340 const nsIntRect
&aRect
,
343 for (int32_t y
= aRect
.y
; y
< aRect
.YMost(); y
++) {
344 for (int32_t x
= aRect
.x
; x
< aRect
.XMost(); x
++) {
345 uint8_t *pixel
= aData
+ aStride
* y
+ 4 * x
;
346 uint8_t a
= pixel
[GFX_ARGB32_OFFSET_A
];
352 pixel
[GFX_ARGB32_OFFSET_B
] =
353 (255 * pixel
[GFX_ARGB32_OFFSET_B
]) / a
;
354 pixel
[GFX_ARGB32_OFFSET_G
] =
355 (255 * pixel
[GFX_ARGB32_OFFSET_G
]) / a
;
356 pixel
[GFX_ARGB32_OFFSET_R
] =
357 (255 * pixel
[GFX_ARGB32_OFFSET_R
]) / a
;
360 /* sRGB -> linearRGB -> intensity */
363 ((gsRGBToLinearRGBMap
[pixel
[GFX_ARGB32_OFFSET_R
]] *
365 gsRGBToLinearRGBMap
[pixel
[GFX_ARGB32_OFFSET_G
]] *
367 gsRGBToLinearRGBMap
[pixel
[GFX_ARGB32_OFFSET_B
]] *
368 0.0721) * (a
/ 255.0) * aOpacity
);
373 memset(pixel
, luminance
, 4);
379 nsSVGUtils::ComputeAlphaMask(uint8_t *aData
,
381 const nsIntRect
&aRect
,
384 for (int32_t y
= aRect
.y
; y
< aRect
.YMost(); y
++) {
385 for (int32_t x
= aRect
.x
; x
< aRect
.XMost(); x
++) {
386 uint8_t *pixel
= aData
+ aStride
* y
+ 4 * x
;
387 uint8_t luminance
= pixel
[GFX_ARGB32_OFFSET_A
] * aOpacity
;
388 memset(pixel
, luminance
, 4);
394 nsSVGUtils::CoordToFloat(nsPresContext
*aPresContext
,
395 nsSVGElement
*aContent
,
396 const nsStyleCoord
&aCoord
)
398 switch (aCoord
.GetUnit()) {
399 case eStyleUnit_Factor
:
401 return aCoord
.GetFactorValue();
403 case eStyleUnit_Coord
:
404 return nsPresContext::AppUnitsToFloatCSSPixels(aCoord
.GetCoordValue());
406 case eStyleUnit_Percent
: {
407 SVGSVGElement
* ctx
= aContent
->GetCtx();
408 return ctx
? aCoord
.GetPercentValue() * ctx
->GetLength(SVGContentUtils::XY
) : 0.0f
;
415 nsSVGDisplayContainerFrame
*
416 nsSVGUtils::GetNearestSVGViewport(nsIFrame
*aFrame
)
418 NS_ASSERTION(aFrame
->IsFrameOfType(nsIFrame::eSVG
), "SVG frame expected");
419 if (aFrame
->GetType() == nsGkAtoms::svgOuterSVGFrame
) {
422 while ((aFrame
= aFrame
->GetParent())) {
423 NS_ASSERTION(aFrame
->IsFrameOfType(nsIFrame::eSVG
), "SVG frame expected");
424 if (aFrame
->GetType() == nsGkAtoms::svgInnerSVGFrame
||
425 aFrame
->GetType() == nsGkAtoms::svgOuterSVGFrame
) {
426 return do_QueryFrame(aFrame
);
429 NS_NOTREACHED("This is not reached. It's only needed to compile.");
434 nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame
*aFrame
,
435 const nsRect
&aPreFilterRect
)
437 NS_ABORT_IF_FALSE(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
,
438 "Called on invalid frame type");
440 nsSVGFilterFrame
*filter
= nsSVGEffects::GetFilterFrame(aFrame
);
442 return aPreFilterRect
;
445 return filter
->GetPostFilterBounds(aFrame
, nullptr, &aPreFilterRect
);
449 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame
*aFrame
)
451 return GetOuterSVGFrame(aFrame
)->IsCallingReflowSVG();
455 nsSVGUtils::InvalidateBounds(nsIFrame
*aFrame
, bool aDuringUpdate
,
456 const nsRect
*aBoundsSubArea
, uint32_t aFlags
)
458 NS_ABORT_IF_FALSE(aFrame
->IsFrameOfType(nsIFrame::eSVG
) &&
459 !(aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
),
460 "Passed bad frame!");
462 NS_ASSERTION(aDuringUpdate
== OuterSVGIsCallingReflowSVG(aFrame
),
463 "aDuringUpdate lies!");
465 // Rendering observers must be notified about changes to the frames that they
466 // are observing _before_ ReflowSVG is called on the SVG frame tree, so we
467 // only need to notify observers if we're not under an ReflowSVG call.
468 // In fact, it would actually be wrong to notify observers while under
469 // ReflowSVG because the observers will try to mark themselves as dirty
470 // and, since ReflowSVG would be in the process of _removeing_ dirty bits
471 // from frames, that would mess things up.
472 if (!aDuringUpdate
) {
473 NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame
),
474 "Must not InvalidateRenderingObservers() under "
475 "nsISVGChildFrame::ReflowSVG!");
477 nsSVGEffects::InvalidateRenderingObservers(aFrame
);
480 // Must come after InvalidateRenderingObservers
481 if (aFrame
->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
) {
485 // XXXjwatt: can this come before InvalidateRenderingObservers?
486 if (aFrame
->GetStateBits() &
487 (NS_FRAME_IS_DIRTY
| NS_FRAME_FIRST_REFLOW
)) {
488 // Nothing to do if we're already dirty, or if the outer-<svg>
489 // hasn't yet had its initial reflow.
493 aFrame
->InvalidateFrameSubtree();
495 if ((aFrame
->GetType() == nsGkAtoms::svgPathGeometryFrame
||
496 aFrame
->GetType() == nsGkAtoms::svgGlyphFrame
) &&
497 NS_SVGDisplayListPaintingEnabled()) {
501 // Okay, so now we pass the area that needs to be invalidated up our parent
502 // chain, accounting for filter effects and transforms as we go, until we
503 // reach our nsSVGOuterSVGFrame where we can invalidate:
506 if (aBoundsSubArea
) {
507 invalidArea
= *aBoundsSubArea
;
509 invalidArea
= aFrame
->GetVisualOverflowRect();
510 // GetVisualOverflowRect() already includes filter effects and transforms,
511 // so advance to our parent before the loop below:
512 invalidArea
+= aFrame
->GetPosition();
513 aFrame
= aFrame
->GetParent();
516 int32_t appUnitsPerCSSPx
= aFrame
->PresContext()->AppUnitsPerCSSPixel();
519 if ((aFrame
->GetStateBits() & NS_FRAME_IS_DIRTY
)) {
520 // This ancestor frame has already been invalidated, so nothing to do.
523 if (aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
) {
526 if (aFrame
->GetType() == nsGkAtoms::svgInnerSVGFrame
&&
527 aFrame
->StyleDisplay()->IsScrollableOverflow()) {
528 // Clip rect to the viewport established by this inner-<svg>:
529 float x
, y
, width
, height
;
530 static_cast<SVGSVGElement
*>(aFrame
->GetContent())->
531 GetAnimatedLengthValues(&x
, &y
, &width
, &height
, nullptr);
532 if (width
<= 0.0f
|| height
<= 0.0f
) {
533 return; // Nothing to invalidate
535 nsRect viewportRect
=
536 nsLayoutUtils::RoundGfxRectToAppRect(gfxRect(0.0, 0.0, width
, height
),
538 invalidArea
= invalidArea
.Intersect(viewportRect
);
539 if (invalidArea
.IsEmpty()) {
540 return; // Nothing to invalidate
543 nsSVGFilterFrame
*filterFrame
= nsSVGEffects::GetFilterFrame(aFrame
);
546 filterFrame
->GetPostFilterDirtyArea(aFrame
, invalidArea
);
548 if (aFrame
->IsTransformed()) {
550 nsDisplayTransform::TransformRect(invalidArea
, aFrame
, nsPoint(0, 0));
552 invalidArea
+= aFrame
->GetPosition();
553 aFrame
= aFrame
->GetParent();
557 // We seem to be able to get here, even though SVG frames are never created
558 // without an ancestor nsSVGOuterSVGFrame. See bug 767996.
562 NS_ASSERTION(aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
,
563 "SVG frames must always have an nsSVGOuterSVGFrame ancestor!");
565 static_cast<nsSVGOuterSVGFrame
*>(aFrame
)->InvalidateSVG(invalidArea
);
569 nsSVGUtils::ScheduleReflowSVG(nsIFrame
*aFrame
)
571 NS_ABORT_IF_FALSE(aFrame
->IsFrameOfType(nsIFrame::eSVG
),
572 "Passed bad frame!");
574 // If this is triggered, the callers should be fixed to call us before
575 // ReflowSVG is called. If we try to mark dirty bits on frames while we're
576 // in the process of removing them, things will get messed up.
577 NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame
),
578 "Do not call under nsISVGChildFrame::ReflowSVG!");
580 // We don't call nsSVGEffects::InvalidateRenderingObservers here because
581 // we should only be called under InvalidateAndScheduleReflowSVG (which
582 // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
583 // (at which point the frame has no observers).
585 if (aFrame
->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
) {
589 if (aFrame
->GetStateBits() &
590 (NS_FRAME_IS_DIRTY
| NS_FRAME_FIRST_REFLOW
)) {
591 // Nothing to do if we're already dirty, or if the outer-<svg>
592 // hasn't yet had its initial reflow.
596 nsSVGOuterSVGFrame
*outerSVGFrame
= nullptr;
598 // We must not add dirty bits to the nsSVGOuterSVGFrame or else
599 // PresShell::FrameNeedsReflow won't work when we pass it in below.
600 if (aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
) {
601 outerSVGFrame
= static_cast<nsSVGOuterSVGFrame
*>(aFrame
);
603 aFrame
->AddStateBits(NS_FRAME_IS_DIRTY
);
605 nsIFrame
*f
= aFrame
->GetParent();
606 while (f
&& !(f
->GetStateBits() & NS_STATE_IS_OUTER_SVG
)) {
607 if (f
->GetStateBits() &
608 (NS_FRAME_IS_DIRTY
| NS_FRAME_HAS_DIRTY_CHILDREN
)) {
611 f
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
613 NS_ABORT_IF_FALSE(f
->IsFrameOfType(nsIFrame::eSVG
),
614 "NS_STATE_IS_OUTER_SVG check above not valid!");
617 outerSVGFrame
= static_cast<nsSVGOuterSVGFrame
*>(f
);
619 NS_ABORT_IF_FALSE(outerSVGFrame
&&
620 outerSVGFrame
->GetType() == nsGkAtoms::svgOuterSVGFrame
,
621 "Did not find nsSVGOuterSVGFrame!");
624 if (outerSVGFrame
->GetStateBits() & NS_FRAME_IN_REFLOW
) {
625 // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
626 // need to call PresShell::FrameNeedsReflow, since we have an
627 // nsSVGOuterSVGFrame::DidReflow call pending.
631 nsFrameState dirtyBit
=
632 (outerSVGFrame
== aFrame
? NS_FRAME_IS_DIRTY
: NS_FRAME_HAS_DIRTY_CHILDREN
);
634 aFrame
->PresContext()->PresShell()->FrameNeedsReflow(
635 outerSVGFrame
, nsIPresShell::eResize
, dirtyBit
);
639 nsSVGUtils::NeedsReflowSVG(nsIFrame
*aFrame
)
641 NS_ABORT_IF_FALSE(aFrame
->IsFrameOfType(nsIFrame::eSVG
),
642 "SVG uses bits differently!");
644 // The flags we test here may change, hence why we have this separate
646 return NS_SUBTREE_DIRTY(aFrame
);
650 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame
*aFrame
)
652 NS_ABORT_IF_FALSE(!(aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
),
653 "Not expecting to be called on the outer SVG Frame");
655 aFrame
= aFrame
->GetParent();
658 if (aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
)
661 nsSVGFilterProperty
*property
= nsSVGEffects::GetFilterProperty(aFrame
);
663 property
->Invalidate();
665 aFrame
= aFrame
->GetParent();
670 nsSVGUtils::ObjectSpace(const gfxRect
&aRect
, const nsSVGLength2
*aLength
)
674 switch (aLength
->GetCtxType()) {
675 case SVGContentUtils::X
:
676 axis
= aRect
.Width();
678 case SVGContentUtils::Y
:
679 axis
= aRect
.Height();
681 case SVGContentUtils::XY
:
682 axis
= float(SVGContentUtils::ComputeNormalizedHypotenuse(
683 aRect
.Width(), aRect
.Height()));
686 NS_NOTREACHED("unexpected ctx type");
690 if (aLength
->IsPercentage()) {
691 // Multiply first to avoid precision errors:
692 return axis
* aLength
->GetAnimValInSpecifiedUnits() / 100;
694 return aLength
->GetAnimValue(static_cast<SVGSVGElement
*>(nullptr)) * axis
;
698 nsSVGUtils::UserSpace(nsSVGElement
*aSVGElement
, const nsSVGLength2
*aLength
)
700 return aLength
->GetAnimValue(aSVGElement
);
704 nsSVGUtils::UserSpace(nsIFrame
*aNonSVGContext
, const nsSVGLength2
*aLength
)
706 return aLength
->GetAnimValue(aNonSVGContext
);
710 nsSVGUtils::GetOuterSVGFrame(nsIFrame
*aFrame
)
713 if (aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
) {
714 return static_cast<nsSVGOuterSVGFrame
*>(aFrame
);
716 aFrame
= aFrame
->GetParent();
723 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame
* aFrame
, nsRect
* aRect
)
725 nsISVGChildFrame
* svg
= do_QueryFrame(aFrame
);
728 *aRect
= (aFrame
->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
) ?
729 nsRect(0, 0, 0, 0) : svg
->GetCoveredRegion();
730 return GetOuterSVGFrame(aFrame
);
734 nsSVGUtils::GetCanvasTM(nsIFrame
*aFrame
, uint32_t aFor
)
736 // XXX yuck, we really need a common interface for GetCanvasTM
738 if (!aFrame
->IsFrameOfType(nsIFrame::eSVG
)) {
739 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame
);
742 if (!(aFrame
->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
)) {
743 if ((aFor
== nsISVGChildFrame::FOR_PAINTING
&&
744 NS_SVGDisplayListPaintingEnabled()) ||
745 (aFor
== nsISVGChildFrame::FOR_HIT_TESTING
&&
746 NS_SVGDisplayListHitTestingEnabled())) {
747 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame
);
751 nsIAtom
* type
= aFrame
->GetType();
752 if (type
== nsGkAtoms::svgForeignObjectFrame
) {
753 return static_cast<nsSVGForeignObjectFrame
*>(aFrame
)->GetCanvasTM(aFor
);
755 if (type
== nsGkAtoms::svgOuterSVGFrame
) {
756 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame
);
759 nsSVGContainerFrame
*containerFrame
= do_QueryFrame(aFrame
);
760 if (containerFrame
) {
761 return containerFrame
->GetCanvasTM(aFor
);
764 return static_cast<nsSVGGeometryFrame
*>(aFrame
)->GetCanvasTM(aFor
);
768 nsSVGUtils::GetUserToCanvasTM(nsIFrame
*aFrame
, uint32_t aFor
)
770 NS_ASSERTION(aFor
== nsISVGChildFrame::FOR_OUTERSVG_TM
,
773 nsISVGChildFrame
* svgFrame
= do_QueryFrame(aFrame
);
774 NS_ASSERTION(svgFrame
, "bad frame");
778 nsSVGElement
*content
= static_cast<nsSVGElement
*>(aFrame
->GetContent());
779 tm
= content
->PrependLocalTransformsTo(
780 GetCanvasTM(aFrame
->GetParent(), aFor
),
781 nsSVGElement::eUserSpaceToParent
);
787 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame
*aFrame
, uint32_t aFlags
)
789 nsIFrame
*kid
= aFrame
->GetFirstPrincipalChild();
792 nsISVGChildFrame
* SVGFrame
= do_QueryFrame(kid
);
794 SVGFrame
->NotifySVGChanged(aFlags
);
796 NS_ASSERTION(kid
->IsFrameOfType(nsIFrame::eSVG
) || kid
->IsSVGText(),
797 "SVG frame expected");
798 // recurse into the children of container frames e.g. <clipPath>, <mask>
799 // in case they have child frames with transformation matrices
800 if (kid
->IsFrameOfType(nsIFrame::eSVG
)) {
801 NotifyChildrenOfSVGChange(kid
, aFlags
);
804 kid
= kid
->GetNextSibling();
808 // ************************************************************
810 class SVGPaintCallback
: public nsSVGFilterPaintCallback
813 virtual void Paint(nsRenderingContext
*aContext
, nsIFrame
*aTarget
,
814 const nsIntRect
* aDirtyRect
)
816 nsISVGChildFrame
*svgChildFrame
= do_QueryFrame(aTarget
);
817 NS_ASSERTION(svgChildFrame
, "Expected SVG frame here");
819 nsIntRect
* dirtyRect
= nullptr;
820 nsIntRect tmpDirtyRect
;
822 // aDirtyRect is in user-space pixels, we need to convert to
823 // outer-SVG-frame-relative device pixels.
825 gfxMatrix userToDeviceSpace
=
826 nsSVGUtils::GetCanvasTM(aTarget
, nsISVGChildFrame::FOR_PAINTING
);
827 if (userToDeviceSpace
.IsSingular()) {
830 gfxRect dirtyBounds
= userToDeviceSpace
.TransformBounds(
831 gfxRect(aDirtyRect
->x
, aDirtyRect
->y
, aDirtyRect
->width
, aDirtyRect
->height
));
832 dirtyBounds
.RoundOut();
833 if (gfxUtils::GfxRectToIntRect(dirtyBounds
, &tmpDirtyRect
)) {
834 dirtyRect
= &tmpDirtyRect
;
838 svgChildFrame
->PaintSVG(aContext
, dirtyRect
);
843 nsSVGUtils::PaintFrameWithEffects(nsRenderingContext
*aContext
,
844 const nsIntRect
*aDirtyRect
,
847 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
848 (aFrame
->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
) ||
849 aFrame
->PresContext()->IsGlyph(),
850 "If display lists are enabled, only painting of non-display "
851 "SVG should take this code path");
853 nsISVGChildFrame
*svgChildFrame
= do_QueryFrame(aFrame
);
857 float opacity
= aFrame
->StyleDisplay()->mOpacity
;
861 const nsIContent
* content
= aFrame
->GetContent();
862 if (content
->IsSVG() &&
863 !static_cast<const nsSVGElement
*>(content
)->HasValidDimensions()) {
867 /* Properties are added lazily and may have been removed by a restyle,
868 so make sure all applicable ones are set again. */
870 nsSVGEffects::EffectProperties effectProperties
=
871 nsSVGEffects::GetEffectProperties(aFrame
);
874 nsSVGFilterFrame
*filterFrame
= effectProperties
.GetFilterFrame(&isOK
);
877 !(aFrame
->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
)) {
878 // Here we convert aFrame's paint bounds to outer-<svg> device space,
879 // compare it to aDirtyRect, and return early if they don't intersect.
880 // We don't do this optimization for nondisplay SVG since nondisplay
881 // SVG doesn't maintain bounds/overflow rects.
882 nsRect overflowRect
= aFrame
->GetVisualOverflowRectRelativeToSelf();
883 if (aFrame
->IsFrameOfType(nsIFrame::eSVGGeometry
) ||
884 aFrame
->IsSVGText()) {
885 // Unlike containers, leaf frames do not include GetPosition() in
887 overflowRect
= overflowRect
+ aFrame
->GetPosition();
889 int32_t appUnitsPerDevPx
= aFrame
->PresContext()->AppUnitsPerDevPixel();
890 gfxMatrix tm
= GetCanvasTM(aFrame
, nsISVGChildFrame::FOR_PAINTING
);
891 if (aFrame
->IsFrameOfType(nsIFrame::eSVG
| nsIFrame::eSVGContainer
)) {
892 gfxMatrix childrenOnlyTM
;
893 if (static_cast<nsSVGContainerFrame
*>(aFrame
)->
894 HasChildrenOnlyTransform(&childrenOnlyTM
)) {
895 // Undo the children-only transform:
896 tm
= childrenOnlyTM
.Invert() * tm
;
899 nsIntRect bounds
= TransformFrameRectToOuterSVG(overflowRect
,
900 tm
, aFrame
->PresContext()).
901 ToOutsidePixels(appUnitsPerDevPx
);
902 if (!aDirtyRect
->Intersects(bounds
)) {
907 /* SVG defines the following rendering model:
913 * 5. Apply clipping, masking, group opacity
915 * We follow this, but perform a couple of optimizations:
917 * + Use cairo's clipPath when representable natively (single object
920 * + Merge opacity and masking if both used together.
923 if (opacity
!= 1.0f
&& CanOptimizeOpacity(aFrame
))
926 gfxContext
*gfx
= aContext
->ThebesContext();
927 bool complexEffects
= false;
929 nsSVGClipPathFrame
*clipPathFrame
= effectProperties
.GetClipPathFrame(&isOK
);
930 nsSVGMaskFrame
*maskFrame
= effectProperties
.GetMaskFrame(&isOK
);
932 bool isTrivialClip
= clipPathFrame
? clipPathFrame
->IsTrivial() : true;
935 // Some resource is invalid. We shouldn't paint anything.
940 if (clipPathFrame
|| maskFrame
)
941 matrix
= GetCanvasTM(aFrame
, nsISVGChildFrame::FOR_PAINTING
);
943 /* Check if we need to do additional operations on this child's
944 * rendering, which necessitates rendering into another surface. */
945 if (opacity
!= 1.0f
|| maskFrame
|| (clipPathFrame
&& !isTrivialClip
)) {
946 complexEffects
= true;
948 if (!(aFrame
->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
)) {
949 // aFrame has a valid visual overflow rect, so clip to it before calling
950 // PushGroup() to minimize the size of the surfaces we'll composite:
951 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(gfx
);
952 gfx
->Multiply(GetCanvasTM(aFrame
, nsISVGChildFrame::FOR_PAINTING
));
953 nsRect overflowRect
= aFrame
->GetVisualOverflowRectRelativeToSelf();
954 if (aFrame
->IsFrameOfType(nsIFrame::eSVGGeometry
) ||
955 aFrame
->IsSVGText()) {
956 // Unlike containers, leaf frames do not include GetPosition() in
958 overflowRect
= overflowRect
+ aFrame
->GetPosition();
960 aContext
->IntersectClip(overflowRect
);
962 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
965 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
966 * we can just do normal painting and get it clipped appropriately.
968 if (clipPathFrame
&& isTrivialClip
) {
970 clipPathFrame
->ClipPaint(aContext
, aFrame
, matrix
);
973 /* Paint the child */
975 nsRect
* dirtyRect
= nullptr;
978 // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
979 // it in frame space.
980 gfxMatrix userToDeviceSpace
=
981 GetUserToCanvasTM(aFrame
, nsISVGChildFrame::FOR_OUTERSVG_TM
);
982 if (userToDeviceSpace
.IsSingular()) {
985 gfxMatrix deviceToUserSpace
= userToDeviceSpace
;
986 deviceToUserSpace
.Invert();
987 gfxRect dirtyBounds
= deviceToUserSpace
.TransformBounds(
988 gfxRect(aDirtyRect
->x
, aDirtyRect
->y
,
989 aDirtyRect
->width
, aDirtyRect
->height
));
991 nsLayoutUtils::RoundGfxRectToAppRect(
992 dirtyBounds
, aFrame
->PresContext()->AppUnitsPerCSSPixel()) -
993 aFrame
->GetPosition();
994 dirtyRect
= &tmpDirtyRect
;
996 SVGPaintCallback paintCallback
;
997 filterFrame
->PaintFilteredFrame(aContext
, aFrame
, &paintCallback
, dirtyRect
);
999 svgChildFrame
->PaintSVG(aContext
, aDirtyRect
);
1002 if (clipPathFrame
&& isTrivialClip
) {
1006 /* No more effects, we're done. */
1007 if (!complexEffects
)
1010 gfx
->PopGroupToSource();
1012 nsRefPtr
<gfxPattern
> maskSurface
=
1013 maskFrame
? maskFrame
->ComputeMaskAlpha(aContext
, aFrame
,
1014 matrix
, opacity
) : nullptr;
1016 nsRefPtr
<gfxPattern
> clipMaskSurface
;
1017 if (clipPathFrame
&& !isTrivialClip
) {
1018 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
1020 nsresult rv
= clipPathFrame
->ClipPaint(aContext
, aFrame
, matrix
);
1021 clipMaskSurface
= gfx
->PopGroup();
1023 if (NS_SUCCEEDED(rv
) && clipMaskSurface
) {
1024 // Still more set after clipping, so clip to another surface
1025 if (maskSurface
|| opacity
!= 1.0f
) {
1026 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
1027 gfx
->Mask(clipMaskSurface
);
1028 gfx
->PopGroupToSource();
1030 gfx
->Mask(clipMaskSurface
);
1036 gfx
->Mask(maskSurface
);
1037 } else if (opacity
!= 1.0f
) {
1038 gfx
->Paint(opacity
);
1045 nsSVGUtils::HitTestClip(nsIFrame
*aFrame
, const nsPoint
&aPoint
)
1047 nsSVGEffects::EffectProperties props
=
1048 nsSVGEffects::GetEffectProperties(aFrame
);
1049 if (!props
.mClipPath
)
1053 nsSVGClipPathFrame
*clipPathFrame
= props
.GetClipPathFrame(&isOK
);
1054 if (!clipPathFrame
|| !isOK
) {
1055 // clipPath is not a valid resource, so nothing gets painted, so
1056 // hit-testing must fail.
1060 return clipPathFrame
->ClipHitTest(aFrame
, GetCanvasTM(aFrame
,
1061 nsISVGChildFrame::FOR_HIT_TESTING
), aPoint
);
1065 nsSVGUtils::HitTestChildren(nsIFrame
*aFrame
, const nsPoint
&aPoint
)
1067 // Traverse the list in reverse order, so that if we get a hit we know that's
1068 // the topmost frame that intersects the point; then we can just return it.
1069 nsIFrame
* result
= nullptr;
1070 for (nsIFrame
* current
= aFrame
->PrincipalChildList().LastChild();
1072 current
= current
->GetPrevSibling()) {
1073 nsISVGChildFrame
* SVGFrame
= do_QueryFrame(current
);
1075 const nsIContent
* content
= current
->GetContent();
1076 if (content
->IsSVG() &&
1077 !static_cast<const nsSVGElement
*>(content
)->HasValidDimensions()) {
1080 result
= SVGFrame
->GetFrameForPoint(aPoint
);
1086 if (result
&& !HitTestClip(aFrame
, aPoint
))
1093 nsSVGUtils::GetCoveredRegion(const nsFrameList
&aFrames
)
1097 for (nsIFrame
* kid
= aFrames
.FirstChild();
1099 kid
= kid
->GetNextSibling()) {
1100 nsISVGChildFrame
* child
= do_QueryFrame(kid
);
1102 nsRect childRect
= child
->GetCoveredRegion();
1103 rect
.UnionRect(rect
, childRect
);
1111 nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint
,
1112 const gfxMatrix
& aFrameToCanvasTM
,
1113 nsPresContext
* aPresContext
)
1115 NS_ABORT_IF_FALSE(!aFrameToCanvasTM
.IsSingular(),
1116 "Callers must not pass a singular matrix");
1117 gfxMatrix canvasDevToFrameUserSpace
= aFrameToCanvasTM
;
1118 canvasDevToFrameUserSpace
.Invert();
1119 gfxPoint devPt
= gfxPoint(aPoint
.x
, aPoint
.y
) /
1120 aPresContext
->AppUnitsPerDevPixel();
1121 gfxPoint userPt
= canvasDevToFrameUserSpace
.Transform(devPt
);
1122 gfxPoint appPt
= (userPt
* aPresContext
->AppUnitsPerCSSPixel()).Round();
1123 userPt
.x
= clamped(appPt
.x
, gfxFloat(nscoord_MIN
), gfxFloat(nscoord_MAX
));
1124 userPt
.y
= clamped(appPt
.y
, gfxFloat(nscoord_MIN
), gfxFloat(nscoord_MAX
));
1125 // now guaranteed to be safe:
1126 return nsPoint(nscoord(userPt
.x
), nscoord(userPt
.y
));
1130 nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect
& aRect
,
1131 const gfxMatrix
& aMatrix
,
1132 nsPresContext
* aPresContext
)
1134 gfxRect
r(aRect
.x
, aRect
.y
, aRect
.width
, aRect
.height
);
1135 r
.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
1136 return nsLayoutUtils::RoundGfxRectToAppRect(
1137 aMatrix
.TransformBounds(r
), aPresContext
->AppUnitsPerDevPixel());
1141 nsSVGUtils::ConvertToSurfaceSize(const gfxSize
& aSize
,
1142 bool *aResultOverflows
)
1144 gfxIntSize
surfaceSize(ClampToInt(ceil(aSize
.width
)), ClampToInt(ceil(aSize
.height
)));
1146 *aResultOverflows
= surfaceSize
.width
!= ceil(aSize
.width
) ||
1147 surfaceSize
.height
!= ceil(aSize
.height
);
1149 if (!gfxASurface::CheckSurfaceSize(surfaceSize
)) {
1150 surfaceSize
.width
= std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION
,
1152 surfaceSize
.height
= std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION
,
1153 surfaceSize
.height
);
1154 *aResultOverflows
= true;
1161 nsSVGUtils::HitTestRect(const gfxMatrix
&aMatrix
,
1162 float aRX
, float aRY
, float aRWidth
, float aRHeight
,
1165 gfxRect
rect(aRX
, aRY
, aRWidth
, aRHeight
);
1166 if (rect
.IsEmpty() || aMatrix
.IsSingular()) {
1169 gfxMatrix toRectSpace
= aMatrix
;
1170 toRectSpace
.Invert();
1171 gfxPoint p
= toRectSpace
.Transform(gfxPoint(aX
, aY
));
1172 return rect
.x
<= p
.x
&& p
.x
<= rect
.XMost() &&
1173 rect
.y
<= p
.y
&& p
.y
<= rect
.YMost();
1177 nsSVGUtils::GetClipRectForFrame(nsIFrame
*aFrame
,
1178 float aX
, float aY
, float aWidth
, float aHeight
)
1180 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
1182 if (!(disp
->mClipFlags
& NS_STYLE_CLIP_RECT
)) {
1183 NS_ASSERTION(disp
->mClipFlags
== NS_STYLE_CLIP_AUTO
,
1184 "We don't know about this type of clip.");
1185 return gfxRect(aX
, aY
, aWidth
, aHeight
);
1188 if (disp
->mOverflowX
== NS_STYLE_OVERFLOW_HIDDEN
||
1189 disp
->mOverflowY
== NS_STYLE_OVERFLOW_HIDDEN
) {
1191 nsIntRect clipPxRect
=
1192 disp
->mClip
.ToOutsidePixels(aFrame
->PresContext()->AppUnitsPerDevPixel());
1194 gfxRect(clipPxRect
.x
, clipPxRect
.y
, clipPxRect
.width
, clipPxRect
.height
);
1196 if (NS_STYLE_CLIP_RIGHT_AUTO
& disp
->mClipFlags
) {
1197 clipRect
.width
= aWidth
- clipRect
.X();
1199 if (NS_STYLE_CLIP_BOTTOM_AUTO
& disp
->mClipFlags
) {
1200 clipRect
.height
= aHeight
- clipRect
.Y();
1203 if (disp
->mOverflowX
!= NS_STYLE_OVERFLOW_HIDDEN
) {
1205 clipRect
.width
= aWidth
;
1207 if (disp
->mOverflowY
!= NS_STYLE_OVERFLOW_HIDDEN
) {
1209 clipRect
.height
= aHeight
;
1214 return gfxRect(aX
, aY
, aWidth
, aHeight
);
1218 nsSVGUtils::CompositeSurfaceMatrix(gfxContext
*aContext
,
1219 gfxASurface
*aSurface
,
1220 const gfxMatrix
&aCTM
, float aOpacity
)
1222 if (aCTM
.IsSingular())
1225 if (aContext
->IsCairo()) {
1227 aContext
->Multiply(aCTM
);
1228 aContext
->SetSource(aSurface
);
1229 aContext
->Paint(aOpacity
);
1230 aContext
->Restore();
1232 DrawTarget
*dt
= aContext
->GetDrawTarget();
1233 Matrix oldMat
= dt
->GetTransform();
1234 RefPtr
<SourceSurface
> surf
=
1235 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt
, aSurface
);
1236 dt
->SetTransform(ToMatrix(aCTM
) * oldMat
);
1238 gfxSize size
= aSurface
->GetSize();
1239 NS_ASSERTION(size
.width
>= 0 && size
.height
>= 0, "Failure to get size for aSurface.");
1241 gfxPoint pt
= aSurface
->GetDeviceOffset();
1243 dt
->FillRect(Rect(-pt
.x
, -pt
.y
, size
.width
, size
.height
),
1244 SurfacePattern(surf
, EXTEND_CLAMP
,
1245 Matrix(1.0f
, 0, 0, 1.0f
, -pt
.x
, -pt
.y
)),
1246 DrawOptions(aOpacity
));
1248 dt
->SetTransform(oldMat
);
1253 nsSVGUtils::CompositePatternMatrix(gfxContext
*aContext
,
1254 gfxPattern
*aPattern
,
1255 const gfxMatrix
&aCTM
, float aWidth
, float aHeight
, float aOpacity
)
1257 if (aCTM
.IsSingular())
1261 SetClipRect(aContext
, aCTM
, gfxRect(0, 0, aWidth
, aHeight
));
1262 aContext
->Multiply(aCTM
);
1263 aContext
->SetPattern(aPattern
);
1264 aContext
->Paint(aOpacity
);
1265 aContext
->Restore();
1269 nsSVGUtils::SetClipRect(gfxContext
*aContext
,
1270 const gfxMatrix
&aCTM
,
1271 const gfxRect
&aRect
)
1273 if (aCTM
.IsSingular())
1276 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(aContext
);
1277 aContext
->Multiply(aCTM
);
1278 aContext
->Clip(aRect
);
1282 nsSVGUtils::ClipToGfxRect(nsIntRect
* aRect
, const gfxRect
& aGfxRect
)
1284 gfxRect r
= aGfxRect
;
1286 gfxRect
r2(aRect
->x
, aRect
->y
, aRect
->width
, aRect
->height
);
1287 r
= r
.Intersect(r2
);
1288 *aRect
= nsIntRect(int32_t(r
.X()), int32_t(r
.Y()),
1289 int32_t(r
.Width()), int32_t(r
.Height()));
1293 nsSVGUtils::GetBBox(nsIFrame
*aFrame
, uint32_t aFlags
)
1295 if (aFrame
->GetContent()->IsNodeOfType(nsINode::eTEXT
)) {
1296 aFrame
= aFrame
->GetParent();
1299 nsISVGChildFrame
*svg
= do_QueryFrame(aFrame
);
1300 if (svg
|| aFrame
->IsSVGText()) {
1301 // It is possible to apply a gradient, pattern, clipping path, mask or
1302 // filter to text. When one of these facilities is applied to text
1303 // the bounding box is the entire text element in all
1305 if (aFrame
->IsSVGText()) {
1306 nsIFrame
* ancestor
= GetFirstNonAAncestorFrame(aFrame
);
1307 if (ancestor
&& ancestor
->IsSVGText()) {
1308 while (ancestor
->GetType() != nsGkAtoms::svgTextFrame2
) {
1309 ancestor
= ancestor
->GetParent();
1312 svg
= do_QueryFrame(ancestor
);
1314 nsSVGTextContainerFrame
* metrics
= do_QueryFrame(
1315 GetFirstNonAAncestorFrame(aFrame
));
1317 while (aFrame
->GetType() != nsGkAtoms::svgTextFrame
) {
1318 aFrame
= aFrame
->GetParent();
1320 svg
= do_QueryFrame(aFrame
);
1323 nsIContent
* content
= aFrame
->GetContent();
1324 if (content
->IsSVG() &&
1325 !static_cast<const nsSVGElement
*>(content
)->HasValidDimensions()) {
1329 if (aFrame
->GetType() == nsGkAtoms::svgForeignObjectFrame
) {
1330 // The spec says getBBox "Returns the tight bounding box in *current user
1331 // space*". So we should really be doing this for all elements, but that
1332 // needs investigation to check that we won't break too much content.
1333 NS_ABORT_IF_FALSE(content
->IsSVG(), "bad cast");
1334 nsSVGElement
*element
= static_cast<nsSVGElement
*>(content
);
1335 matrix
= element
->PrependLocalTransformsTo(matrix
,
1336 nsSVGElement::eChildToUserSpace
);
1338 return svg
->GetBBoxContribution(matrix
, aFlags
);
1340 return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame
);
1344 nsSVGUtils::GetRelativeRect(uint16_t aUnits
, const nsSVGLength2
*aXYWH
,
1345 const gfxRect
&aBBox
, nsIFrame
*aFrame
)
1347 float x
, y
, width
, height
;
1348 if (aUnits
== SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
1349 x
= aBBox
.X() + ObjectSpace(aBBox
, &aXYWH
[0]);
1350 y
= aBBox
.Y() + ObjectSpace(aBBox
, &aXYWH
[1]);
1351 width
= ObjectSpace(aBBox
, &aXYWH
[2]);
1352 height
= ObjectSpace(aBBox
, &aXYWH
[3]);
1354 x
= UserSpace(aFrame
, &aXYWH
[0]);
1355 y
= UserSpace(aFrame
, &aXYWH
[1]);
1356 width
= UserSpace(aFrame
, &aXYWH
[2]);
1357 height
= UserSpace(aFrame
, &aXYWH
[3]);
1359 return gfxRect(x
, y
, width
, height
);
1363 nsSVGUtils::CanOptimizeOpacity(nsIFrame
*aFrame
)
1365 if (!(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
)) {
1368 nsIAtom
*type
= aFrame
->GetType();
1369 if (type
!= nsGkAtoms::svgImageFrame
&&
1370 type
!= nsGkAtoms::svgPathGeometryFrame
) {
1373 if (aFrame
->StyleSVGReset()->mFilter
) {
1376 // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
1377 if (type
== nsGkAtoms::svgImageFrame
) {
1380 const nsStyleSVG
*style
= aFrame
->StyleSVG();
1381 if (style
->mMarkerStart
|| style
->mMarkerMid
|| style
->mMarkerEnd
) {
1384 if (style
->mFill
.mType
== eStyleSVGPaintType_None
||
1385 style
->mFillOpacity
<= 0 ||
1386 !HasStroke(aFrame
)) {
1393 nsSVGUtils::MaxExpansion(const gfxMatrix
&aMatrix
)
1395 // maximum expansion derivation from
1396 // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
1397 // and also implemented in cairo_matrix_transformed_circle_major_axis
1398 double a
= aMatrix
.xx
;
1399 double b
= aMatrix
.yx
;
1400 double c
= aMatrix
.xy
;
1401 double d
= aMatrix
.yy
;
1402 double f
= (a
* a
+ b
* b
+ c
* c
+ d
* d
) / 2;
1403 double g
= (a
* a
+ b
* b
- c
* c
- d
* d
) / 2;
1404 double h
= a
* c
+ b
* d
;
1405 return sqrt(f
+ sqrt(g
* g
+ h
* h
));
1409 nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix
&aMatrix
,
1414 aUnits
->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
1415 gfxRect bbox
= GetBBox(aFrame
);
1416 return gfxMatrix().Scale(bbox
.Width(), bbox
.Height()) *
1417 gfxMatrix().Translate(gfxPoint(bbox
.X(), bbox
.Y())) *
1424 nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame
* aStartFrame
)
1426 for (nsIFrame
*ancestorFrame
= aStartFrame
; ancestorFrame
;
1427 ancestorFrame
= ancestorFrame
->GetParent()) {
1428 if (ancestorFrame
->GetType() != nsGkAtoms::svgAFrame
) {
1429 return ancestorFrame
;
1436 nsSVGUtils::GetStrokeTransform(nsIFrame
*aFrame
)
1438 if (aFrame
->StyleSVGReset()->mVectorEffect
==
1439 NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE
) {
1441 if (aFrame
->GetContent()->IsNodeOfType(nsINode::eTEXT
)) {
1442 aFrame
= aFrame
->GetParent();
1445 nsIContent
*content
= aFrame
->GetContent();
1446 NS_ABORT_IF_FALSE(content
->IsSVG(), "bad cast");
1448 // a non-scaling stroke is in the screen co-ordinate
1449 // space rather so we need to invert the transform
1450 // to the screen co-ordinate space to get there.
1451 // See http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke
1452 gfxMatrix transform
= SVGContentUtils::GetCTM(
1453 static_cast<nsSVGElement
*>(content
), true);
1454 if (!transform
.IsSingular()) {
1455 return transform
.Invert();
1461 // The logic here comes from _cairo_stroke_style_max_distance_from_path
1463 PathExtentsToMaxStrokeExtents(const gfxRect
& aPathExtents
,
1465 double aStyleExpansionFactor
,
1466 const gfxMatrix
& aMatrix
)
1468 double style_expansion
=
1469 aStyleExpansionFactor
* nsSVGUtils::GetStrokeWidth(aFrame
);
1471 gfxMatrix matrix
= aMatrix
;
1472 matrix
.Multiply(nsSVGUtils::GetStrokeTransform(aFrame
));
1474 double dx
= style_expansion
* (fabs(matrix
.xx
) + fabs(matrix
.xy
));
1475 double dy
= style_expansion
* (fabs(matrix
.yy
) + fabs(matrix
.yx
));
1477 gfxRect strokeExtents
= aPathExtents
;
1478 strokeExtents
.Inflate(dx
, dy
);
1479 return strokeExtents
;
1483 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect
& aPathExtents
,
1484 nsSVGGeometryFrame
* aFrame
,
1485 const gfxMatrix
& aMatrix
)
1487 return ::PathExtentsToMaxStrokeExtents(aPathExtents
, aFrame
, 0.5, aMatrix
);
1491 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect
& aPathExtents
,
1492 nsTextFrame
* aFrame
,
1493 const gfxMatrix
& aMatrix
)
1495 NS_ASSERTION(aFrame
->IsSVGText(), "expected an nsTextFrame for SVG text");
1496 return ::PathExtentsToMaxStrokeExtents(aPathExtents
, aFrame
, 0.5, aMatrix
);
1500 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect
& aPathExtents
,
1501 nsSVGPathGeometryFrame
* aFrame
,
1502 const gfxMatrix
& aMatrix
)
1504 double styleExpansionFactor
= 0.5;
1506 if (static_cast<nsSVGPathGeometryElement
*>(aFrame
->GetContent())->IsMarkable()) {
1507 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1509 if (style
->mStrokeLinecap
== NS_STYLE_STROKE_LINECAP_SQUARE
) {
1510 styleExpansionFactor
= M_SQRT1_2
;
1513 if (style
->mStrokeLinejoin
== NS_STYLE_STROKE_LINEJOIN_MITER
&&
1514 styleExpansionFactor
< style
->mStrokeMiterlimit
&&
1515 aFrame
->GetContent()->Tag() != nsGkAtoms::line
) {
1516 styleExpansionFactor
= style
->mStrokeMiterlimit
;
1520 return ::PathExtentsToMaxStrokeExtents(aPathExtents
,
1522 styleExpansionFactor
,
1526 // ----------------------------------------------------------------------
1528 /* static */ nscolor
1529 nsSVGUtils::GetFallbackOrPaintColor(gfxContext
*aContext
, nsStyleContext
*aStyleContext
,
1530 nsStyleSVGPaint
nsStyleSVG::*aFillOrStroke
)
1532 const nsStyleSVGPaint
&paint
= aStyleContext
->StyleSVG()->*aFillOrStroke
;
1533 nsStyleContext
*styleIfVisited
= aStyleContext
->GetStyleIfVisited();
1534 bool isServer
= paint
.mType
== eStyleSVGPaintType_Server
||
1535 paint
.mType
== eStyleSVGPaintType_ObjectFill
||
1536 paint
.mType
== eStyleSVGPaintType_ObjectStroke
;
1537 nscolor color
= isServer
? paint
.mFallbackColor
: paint
.mPaint
.mColor
;
1538 if (styleIfVisited
) {
1539 const nsStyleSVGPaint
&paintIfVisited
=
1540 styleIfVisited
->StyleSVG()->*aFillOrStroke
;
1541 // To prevent Web content from detecting if a user has visited a URL
1542 // (via URL loading triggered by paint servers or performance
1543 // differences between paint servers or between a paint server and a
1544 // color), we do not allow whether links are visited to change which
1545 // paint server is used or switch between paint servers and simple
1546 // colors. A :visited style may only override a simple color with
1547 // another simple color.
1548 if (paintIfVisited
.mType
== eStyleSVGPaintType_Color
&&
1549 paint
.mType
== eStyleSVGPaintType_Color
) {
1550 nscolor colors
[2] = { color
, paintIfVisited
.mPaint
.mColor
};
1551 return nsStyleContext::CombineVisitedColors(
1552 colors
, aStyleContext
->RelevantLinkVisited());
1559 SetupFallbackOrPaintColor(gfxContext
*aContext
, nsStyleContext
*aStyleContext
,
1560 nsStyleSVGPaint
nsStyleSVG::*aFillOrStroke
,
1563 nscolor color
= nsSVGUtils::GetFallbackOrPaintColor(
1564 aContext
, aStyleContext
, aFillOrStroke
);
1566 aContext
->SetColor(gfxRGBA(NS_GET_R(color
)/255.0,
1567 NS_GET_G(color
)/255.0,
1568 NS_GET_B(color
)/255.0,
1569 NS_GET_A(color
)/255.0 * aOpacity
));
1573 MaybeOptimizeOpacity(nsIFrame
*aFrame
, float aFillOrStrokeOpacity
)
1575 float opacity
= aFrame
->StyleDisplay()->mOpacity
;
1576 if (opacity
< 1 && nsSVGUtils::CanOptimizeOpacity(aFrame
)) {
1577 return aFillOrStrokeOpacity
* opacity
;
1579 return aFillOrStrokeOpacity
;
1583 nsSVGUtils::SetupObjectPaint(gfxContext
*aContext
,
1584 gfxTextObjectPaint
*aObjectPaint
,
1585 const nsStyleSVGPaint
&aPaint
,
1588 nsRefPtr
<gfxPattern
> pattern
;
1590 if (!aObjectPaint
) {
1594 switch (aPaint
.mType
) {
1595 case eStyleSVGPaintType_ObjectFill
:
1596 pattern
= aObjectPaint
->GetFillPattern(aOpacity
, aContext
->CurrentMatrix());
1598 case eStyleSVGPaintType_ObjectStroke
:
1599 pattern
= aObjectPaint
->GetStrokePattern(aOpacity
, aContext
->CurrentMatrix());
1609 aContext
->SetPattern(pattern
);
1615 nsSVGUtils::SetupCairoFillPaint(nsIFrame
*aFrame
, gfxContext
* aContext
,
1616 gfxTextObjectPaint
*aObjectPaint
)
1618 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1619 if (style
->mFill
.mType
== eStyleSVGPaintType_None
)
1622 if (style
->mFillRule
== NS_STYLE_FILL_RULE_EVENODD
)
1623 aContext
->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD
);
1625 aContext
->SetFillRule(gfxContext::FILL_RULE_WINDING
);
1627 float opacity
= MaybeOptimizeOpacity(aFrame
,
1628 GetOpacity(style
->mFillOpacitySource
,
1629 style
->mFillOpacity
,
1631 nsSVGPaintServerFrame
*ps
=
1632 nsSVGEffects::GetPaintServer(aFrame
, &style
->mFill
, nsSVGEffects::FillProperty());
1633 if (ps
&& ps
->SetupPaintServer(aContext
, aFrame
, &nsStyleSVG::mFill
, opacity
))
1636 if (SetupObjectPaint(aContext
, aObjectPaint
, style
->mFill
, opacity
)) {
1640 // On failure, use the fallback colour in case we have an
1641 // objectBoundingBox where the width or height of the object is zero.
1642 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
1643 SetupFallbackOrPaintColor(aContext
, aFrame
->StyleContext(),
1644 &nsStyleSVG::mFill
, opacity
);
1650 nsSVGUtils::SetupCairoStrokePaint(nsIFrame
*aFrame
, gfxContext
* aContext
,
1651 gfxTextObjectPaint
*aObjectPaint
)
1653 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1654 if (style
->mStroke
.mType
== eStyleSVGPaintType_None
)
1657 float opacity
= MaybeOptimizeOpacity(aFrame
,
1658 GetOpacity(style
->mStrokeOpacitySource
,
1659 style
->mStrokeOpacity
,
1662 nsSVGPaintServerFrame
*ps
=
1663 nsSVGEffects::GetPaintServer(aFrame
, &style
->mStroke
, nsSVGEffects::StrokeProperty());
1664 if (ps
&& ps
->SetupPaintServer(aContext
, aFrame
, &nsStyleSVG::mStroke
, opacity
))
1667 if (SetupObjectPaint(aContext
, aObjectPaint
, style
->mStroke
, opacity
)) {
1671 // On failure, use the fallback colour in case we have an
1672 // objectBoundingBox where the width or height of the object is zero.
1673 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
1674 SetupFallbackOrPaintColor(aContext
, aFrame
->StyleContext(),
1675 &nsStyleSVG::mStroke
, opacity
);
1681 nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType
,
1682 const float& aOpacity
,
1683 gfxTextObjectPaint
*aOuterObjectPaint
)
1685 float opacity
= 1.0f
;
1686 switch (aOpacityType
) {
1687 case eStyleSVGOpacitySource_Normal
:
1690 case eStyleSVGOpacitySource_ObjectFillOpacity
:
1691 if (aOuterObjectPaint
) {
1692 opacity
= aOuterObjectPaint
->GetFillOpacity();
1694 NS_WARNING("objectFillOpacity used outside of an SVG glyph");
1697 case eStyleSVGOpacitySource_ObjectStrokeOpacity
:
1698 if (aOuterObjectPaint
) {
1699 opacity
= aOuterObjectPaint
->GetStrokeOpacity();
1701 NS_WARNING("objectStrokeOpacity used outside of an SVG glyph");
1705 NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
1711 nsSVGUtils::HasStroke(nsIFrame
* aFrame
, gfxTextObjectPaint
*aObjectPaint
)
1713 const nsStyleSVG
*style
= aFrame
->StyleSVG();
1714 return style
->mStroke
.mType
!= eStyleSVGPaintType_None
&&
1715 style
->mStrokeOpacity
> 0 &&
1716 GetStrokeWidth(aFrame
, aObjectPaint
) > 0;
1720 nsSVGUtils::GetStrokeWidth(nsIFrame
* aFrame
, gfxTextObjectPaint
*aObjectPaint
)
1722 const nsStyleSVG
*style
= aFrame
->StyleSVG();
1723 if (aObjectPaint
&& style
->mStrokeWidthFromObject
) {
1724 return aObjectPaint
->GetStrokeWidth();
1727 nsIContent
* content
= aFrame
->GetContent();
1728 if (content
->IsNodeOfType(nsINode::eTEXT
)) {
1729 content
= content
->GetParent();
1732 nsSVGElement
*ctx
= static_cast<nsSVGElement
*>(content
);
1734 return CoordToFloat(aFrame
->PresContext(), ctx
,
1735 style
->mStrokeWidth
);
1739 nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame
* aFrame
, gfxContext
*aContext
,
1740 gfxTextObjectPaint
*aObjectPaint
)
1742 float width
= GetStrokeWidth(aFrame
, aObjectPaint
);
1745 aContext
->SetLineWidth(width
);
1747 // Apply any stroke-specific transform
1748 aContext
->Multiply(GetStrokeTransform(aFrame
));
1750 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1752 switch (style
->mStrokeLinecap
) {
1753 case NS_STYLE_STROKE_LINECAP_BUTT
:
1754 aContext
->SetLineCap(gfxContext::LINE_CAP_BUTT
);
1756 case NS_STYLE_STROKE_LINECAP_ROUND
:
1757 aContext
->SetLineCap(gfxContext::LINE_CAP_ROUND
);
1759 case NS_STYLE_STROKE_LINECAP_SQUARE
:
1760 aContext
->SetLineCap(gfxContext::LINE_CAP_SQUARE
);
1764 aContext
->SetMiterLimit(style
->mStrokeMiterlimit
);
1766 switch (style
->mStrokeLinejoin
) {
1767 case NS_STYLE_STROKE_LINEJOIN_MITER
:
1768 aContext
->SetLineJoin(gfxContext::LINE_JOIN_MITER
);
1770 case NS_STYLE_STROKE_LINEJOIN_ROUND
:
1771 aContext
->SetLineJoin(gfxContext::LINE_JOIN_ROUND
);
1773 case NS_STYLE_STROKE_LINEJOIN_BEVEL
:
1774 aContext
->SetLineJoin(gfxContext::LINE_JOIN_BEVEL
);
1780 GetStrokeDashData(nsIFrame
* aFrame
,
1781 FallibleTArray
<gfxFloat
>& aDashes
,
1782 gfxFloat
* aDashOffset
,
1783 gfxTextObjectPaint
*aObjectPaint
)
1785 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1786 nsPresContext
*presContext
= aFrame
->PresContext();
1787 nsIContent
*content
= aFrame
->GetContent();
1788 nsSVGElement
*ctx
= static_cast<nsSVGElement
*>
1789 (content
->IsNodeOfType(nsINode::eTEXT
) ?
1790 content
->GetParent() : content
);
1792 gfxFloat totalLength
= 0.0;
1793 if (aObjectPaint
&& style
->mStrokeDasharrayFromObject
) {
1794 aDashes
= aObjectPaint
->GetStrokeDashArray();
1796 for (uint32_t i
= 0; i
< aDashes
.Length(); i
++) {
1797 if (aDashes
[i
] < 0.0) {
1800 totalLength
+= aDashes
[i
];
1804 uint32_t count
= style
->mStrokeDasharrayLength
;
1805 if (!count
|| !aDashes
.SetLength(count
)) {
1809 gfxFloat pathScale
= 1.0;
1811 if (content
->Tag() == nsGkAtoms::path
) {
1812 pathScale
= static_cast<SVGPathElement
*>(content
)->
1813 GetPathLengthScale(SVGPathElement::eForStroking
);
1814 if (pathScale
<= 0) {
1819 const nsStyleCoord
*dasharray
= style
->mStrokeDasharray
;
1821 for (uint32_t i
= 0; i
< count
; i
++) {
1822 aDashes
[i
] = nsSVGUtils::CoordToFloat(presContext
,
1824 dasharray
[i
]) * pathScale
;
1825 if (aDashes
[i
] < 0.0) {
1828 totalLength
+= aDashes
[i
];
1832 if (aObjectPaint
&& style
->mStrokeDashoffsetFromObject
) {
1833 *aDashOffset
= aObjectPaint
->GetStrokeDashOffset();
1835 *aDashOffset
= nsSVGUtils::CoordToFloat(presContext
,
1837 style
->mStrokeDashoffset
);
1840 if (content
->IsNodeOfType(nsINode::eTEXT
)) {
1841 content
= content
->GetParent();
1844 return (totalLength
> 0.0);
1848 nsSVGUtils::SetupCairoStrokeHitGeometry(nsIFrame
* aFrame
, gfxContext
* aContext
,
1849 gfxTextObjectPaint
*aObjectPaint
)
1851 SetupCairoStrokeGeometry(aFrame
, aContext
, aObjectPaint
);
1853 AutoFallibleTArray
<gfxFloat
, 10> dashes
;
1854 gfxFloat dashOffset
;
1855 if (GetStrokeDashData(aFrame
, dashes
, &dashOffset
, aObjectPaint
)) {
1856 aContext
->SetDash(dashes
.Elements(), dashes
.Length(), dashOffset
);
1861 nsSVGUtils::GetGeometryHitTestFlags(nsIFrame
* aFrame
)
1865 switch(aFrame
->StyleVisibility()->mPointerEvents
) {
1866 case NS_STYLE_POINTER_EVENTS_NONE
:
1868 case NS_STYLE_POINTER_EVENTS_AUTO
:
1869 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED
:
1870 if (aFrame
->StyleVisibility()->IsVisible()) {
1871 if (aFrame
->StyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
)
1872 flags
|= SVG_HIT_TEST_FILL
;
1873 if (aFrame
->StyleSVG()->mStroke
.mType
!= eStyleSVGPaintType_None
)
1874 flags
|= SVG_HIT_TEST_STROKE
;
1875 if (aFrame
->StyleSVG()->mStrokeOpacity
> 0)
1876 flags
|= SVG_HIT_TEST_CHECK_MRECT
;
1879 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL
:
1880 if (aFrame
->StyleVisibility()->IsVisible()) {
1881 flags
|= SVG_HIT_TEST_FILL
;
1884 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE
:
1885 if (aFrame
->StyleVisibility()->IsVisible()) {
1886 flags
|= SVG_HIT_TEST_STROKE
;
1889 case NS_STYLE_POINTER_EVENTS_VISIBLE
:
1890 if (aFrame
->StyleVisibility()->IsVisible()) {
1891 flags
|= SVG_HIT_TEST_FILL
| SVG_HIT_TEST_STROKE
;
1894 case NS_STYLE_POINTER_EVENTS_PAINTED
:
1895 if (aFrame
->StyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
)
1896 flags
|= SVG_HIT_TEST_FILL
;
1897 if (aFrame
->StyleSVG()->mStroke
.mType
!= eStyleSVGPaintType_None
)
1898 flags
|= SVG_HIT_TEST_STROKE
;
1899 if (aFrame
->StyleSVG()->mStrokeOpacity
)
1900 flags
|= SVG_HIT_TEST_CHECK_MRECT
;
1902 case NS_STYLE_POINTER_EVENTS_FILL
:
1903 flags
|= SVG_HIT_TEST_FILL
;
1905 case NS_STYLE_POINTER_EVENTS_STROKE
:
1906 flags
|= SVG_HIT_TEST_STROKE
;
1908 case NS_STYLE_POINTER_EVENTS_ALL
:
1909 flags
|= SVG_HIT_TEST_FILL
| SVG_HIT_TEST_STROKE
;
1912 NS_ERROR("not reached");
1920 nsSVGUtils::SetupCairoStroke(nsIFrame
* aFrame
, gfxContext
* aContext
,
1921 gfxTextObjectPaint
*aObjectPaint
)
1923 if (!HasStroke(aFrame
, aObjectPaint
)) {
1926 SetupCairoStrokeHitGeometry(aFrame
, aContext
, aObjectPaint
);
1928 return SetupCairoStrokePaint(aFrame
, aContext
, aObjectPaint
);
1932 nsSVGUtils::PaintSVGGlyph(Element
* aElement
, gfxContext
* aContext
,
1933 gfxFont::DrawMode aDrawMode
,
1934 gfxTextObjectPaint
* aObjectPaint
)
1936 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
1937 nsISVGChildFrame
* svgFrame
= do_QueryFrame(frame
);
1938 MOZ_ASSERT(!frame
|| svgFrame
, "Non SVG frame for SVG glyph");
1940 nsRenderingContext context
;
1941 context
.Init(frame
->PresContext()->DeviceContext(), aContext
);
1942 context
.AddUserData(&gfxTextObjectPaint::sUserDataKey
, aObjectPaint
, nullptr);
1943 nsresult rv
= svgFrame
->PaintSVG(&context
, nullptr);
1944 if (NS_SUCCEEDED(rv
)) {
1952 nsSVGUtils::GetSVGGlyphExtents(Element
* aElement
,
1953 const gfxMatrix
& aSVGToAppSpace
,
1956 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
1957 nsISVGChildFrame
* svgFrame
= do_QueryFrame(frame
);
1958 MOZ_ASSERT(!frame
|| svgFrame
, "Non SVG frame for SVG glyph");
1960 *aResult
= svgFrame
->GetBBoxContribution(aSVGToAppSpace
,
1961 nsSVGUtils::eBBoxIncludeFill
| nsSVGUtils::eBBoxIncludeFillGeometry
|
1962 nsSVGUtils::eBBoxIncludeStroke
| nsSVGUtils::eBBoxIncludeStrokeGeometry
|
1963 nsSVGUtils::eBBoxIncludeMarkers
);
1970 nsSVGUtils::ToCanvasBounds(const gfxRect
&aUserspaceRect
,
1971 const gfxMatrix
&aToCanvas
,
1972 const nsPresContext
*presContext
)
1974 return nsLayoutUtils::RoundGfxRectToAppRect(
1975 aToCanvas
.TransformBounds(aUserspaceRect
),
1976 presContext
->AppUnitsPerDevPixel());