Bug 867089 - Validate the playbackRate before using it. r=ehsan
[gecko.git] / layout / svg / nsSVGUtils.cpp
blobe296df7aa937c825304c97657199e89340e97a3c
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 // Main header first:
7 // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
8 #include "nsSVGUtils.h"
9 #include <algorithm>
11 // Keep others in (case-insensitive) order:
12 #include "gfxContext.h"
13 #include "gfxImageSurface.h"
14 #include "gfxMatrix.h"
15 #include "gfxPlatform.h"
16 #include "gfxRect.h"
17 #include "gfxUtils.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"
26 #include "nsIFrame.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;
61 // c = n / 255
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
98 // c = n / 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;
139 bool
140 NS_SVGDisplayListHitTestingEnabled()
142 return sSVGDisplayListHitTestingEnabled;
145 bool
146 NS_SVGDisplayListPaintingEnabled()
148 return sSVGDisplayListPaintingEnabled;
151 bool
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,
161 RenderMode aMode)
162 : mContext(aContext)
163 , mOriginalRenderState(nullptr)
164 , mMode(aMode)
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);
181 void
182 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
184 mPaintingToWindow = aPaintingToWindow;
187 /* static */ SVGAutoRenderState::RenderMode
188 SVGAutoRenderState::GetRenderMode(nsRenderingContext *aContext)
190 void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
191 if (state) {
192 return static_cast<SVGAutoRenderState*>(state)->mMode;
194 return NORMAL;
197 /* static */ bool
198 SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext)
200 void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
201 if (state) {
202 return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
204 return false;
207 void
208 nsSVGUtils::Init()
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");
220 void
221 nsSVGUtils::UnPremultiplyImageDataAlpha(uint8_t *data,
222 int32_t stride,
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];
230 if (a == 255)
231 continue;
233 if (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;
237 } else {
238 pixel[GFX_ARGB32_OFFSET_B] = 0;
239 pixel[GFX_ARGB32_OFFSET_G] = 0;
240 pixel[GFX_ARGB32_OFFSET_R] = 0;
246 void
247 nsSVGUtils::PremultiplyImageDataAlpha(uint8_t *data,
248 int32_t stride,
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];
256 if (a == 255)
257 continue;
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);
269 void
270 nsSVGUtils::ConvertImageDataToLinearRGB(uint8_t *data,
271 int32_t stride,
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]];
288 void
289 nsSVGUtils::ConvertImageDataFromLinearRGB(uint8_t *data,
290 int32_t stride,
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]];
307 void
308 nsSVGUtils::ComputesRGBLuminanceMask(uint8_t *aData,
309 int32_t aStride,
310 const nsIntRect &aRect,
311 float aOpacity)
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];
318 uint8_t luminance;
319 if (a) {
320 /* sRGB -> intensity (unpremultiply cancels out the
321 * (a/255.0) multiplication with aOpacity */
322 luminance =
323 static_cast<uint8_t>
324 ((pixel[GFX_ARGB32_OFFSET_R] * 0.2125 +
325 pixel[GFX_ARGB32_OFFSET_G] * 0.7154 +
326 pixel[GFX_ARGB32_OFFSET_B] * 0.0721) *
327 aOpacity);
328 } else {
329 luminance = 0;
332 memset(pixel, luminance, 4);
337 void
338 nsSVGUtils::ComputeLinearRGBLuminanceMask(uint8_t *aData,
339 int32_t aStride,
340 const nsIntRect &aRect,
341 float aOpacity)
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];
348 uint8_t luminance;
349 // unpremultiply
350 if (a) {
351 if (a != 255) {
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 */
361 luminance =
362 static_cast<uint8_t>
363 ((gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_R]] *
364 0.2125 +
365 gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_G]] *
366 0.7154 +
367 gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_B]] *
368 0.0721) * (a / 255.0) * aOpacity);
369 } else {
370 luminance = 0;
373 memset(pixel, luminance, 4);
378 void
379 nsSVGUtils::ComputeAlphaMask(uint8_t *aData,
380 int32_t aStride,
381 const nsIntRect &aRect,
382 float aOpacity)
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);
393 float
394 nsSVGUtils::CoordToFloat(nsPresContext *aPresContext,
395 nsSVGElement *aContent,
396 const nsStyleCoord &aCoord)
398 switch (aCoord.GetUnit()) {
399 case eStyleUnit_Factor:
400 // user units
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;
410 default:
411 return 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) {
420 return nullptr;
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.");
430 return nullptr;
433 nsRect
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);
441 if (!filter) {
442 return aPreFilterRect;
445 return filter->GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
448 bool
449 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
451 return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
454 void
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) {
482 return;
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.
490 return;
493 aFrame->InvalidateFrameSubtree();
495 if ((aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame ||
496 aFrame->GetType() == nsGkAtoms::svgGlyphFrame) &&
497 NS_SVGDisplayListPaintingEnabled()) {
498 return;
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:
505 nsRect invalidArea;
506 if (aBoundsSubArea) {
507 invalidArea = *aBoundsSubArea;
508 } else {
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();
518 while (aFrame) {
519 if ((aFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) {
520 // This ancestor frame has already been invalidated, so nothing to do.
521 return;
523 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
524 break;
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),
537 appUnitsPerCSSPx);
538 invalidArea = invalidArea.Intersect(viewportRect);
539 if (invalidArea.IsEmpty()) {
540 return; // Nothing to invalidate
543 nsSVGFilterFrame *filterFrame = nsSVGEffects::GetFilterFrame(aFrame);
544 if (filterFrame) {
545 invalidArea =
546 filterFrame->GetPostFilterDirtyArea(aFrame, invalidArea);
548 if (aFrame->IsTransformed()) {
549 invalidArea =
550 nsDisplayTransform::TransformRect(invalidArea, aFrame, nsPoint(0, 0));
552 invalidArea += aFrame->GetPosition();
553 aFrame = aFrame->GetParent();
556 if (!aFrame) {
557 // We seem to be able to get here, even though SVG frames are never created
558 // without an ancestor nsSVGOuterSVGFrame. See bug 767996.
559 return;
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);
568 void
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) {
586 return;
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.
593 return;
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);
602 } else {
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)) {
609 return;
611 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
612 f = f->GetParent();
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.
628 return;
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);
638 bool
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
645 // function.
646 return NS_SUBTREE_DIRTY(aFrame);
649 void
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();
657 while (aFrame) {
658 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
659 return;
661 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
662 if (property) {
663 property->Invalidate();
665 aFrame = aFrame->GetParent();
669 float
670 nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
672 float axis;
674 switch (aLength->GetCtxType()) {
675 case SVGContentUtils::X:
676 axis = aRect.Width();
677 break;
678 case SVGContentUtils::Y:
679 axis = aRect.Height();
680 break;
681 case SVGContentUtils::XY:
682 axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
683 aRect.Width(), aRect.Height()));
684 break;
685 default:
686 NS_NOTREACHED("unexpected ctx type");
687 axis = 0.0f;
688 break;
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;
697 float
698 nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
700 return aLength->GetAnimValue(aSVGElement);
703 float
704 nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
706 return aLength->GetAnimValue(aNonSVGContext);
709 nsSVGOuterSVGFrame *
710 nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
712 while (aFrame) {
713 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
714 return static_cast<nsSVGOuterSVGFrame*>(aFrame);
716 aFrame = aFrame->GetParent();
719 return nullptr;
722 nsIFrame*
723 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
725 nsISVGChildFrame* svg = do_QueryFrame(aFrame);
726 if (!svg)
727 return nullptr;
728 *aRect = (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) ?
729 nsRect(0, 0, 0, 0) : svg->GetCoveredRegion();
730 return GetOuterSVGFrame(aFrame);
733 gfxMatrix
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);
767 gfxMatrix
768 nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame, uint32_t aFor)
770 NS_ASSERTION(aFor == nsISVGChildFrame::FOR_OUTERSVG_TM,
771 "Unexpected aFor?");
773 nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
774 NS_ASSERTION(svgFrame, "bad frame");
776 gfxMatrix tm;
777 if (svgFrame) {
778 nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
779 tm = content->PrependLocalTransformsTo(
780 GetCanvasTM(aFrame->GetParent(), aFor),
781 nsSVGElement::eUserSpaceToParent);
783 return tm;
786 void
787 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags)
789 nsIFrame *kid = aFrame->GetFirstPrincipalChild();
791 while (kid) {
792 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
793 if (SVGFrame) {
794 SVGFrame->NotifySVGChanged(aFlags);
795 } else {
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
812 public:
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.
824 if (aDirtyRect) {
825 gfxMatrix userToDeviceSpace =
826 nsSVGUtils::GetCanvasTM(aTarget, nsISVGChildFrame::FOR_PAINTING);
827 if (userToDeviceSpace.IsSingular()) {
828 return;
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);
842 void
843 nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
844 const nsIntRect *aDirtyRect,
845 nsIFrame *aFrame)
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);
854 if (!svgChildFrame)
855 return;
857 float opacity = aFrame->StyleDisplay()->mOpacity;
858 if (opacity == 0.0f)
859 return;
861 const nsIContent* content = aFrame->GetContent();
862 if (content->IsSVG() &&
863 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
864 return;
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);
873 bool isOK = true;
874 nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
876 if (aDirtyRect &&
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
886 // GetCanvasTM().
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)) {
903 return;
907 /* SVG defines the following rendering model:
909 * 1. Render fill
910 * 2. Render stroke
911 * 3. Render markers
912 * 4. Apply filter
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
918 * clip region).
920 * + Merge opacity and masking if both used together.
923 if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
924 opacity = 1.0f;
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;
934 if (!isOK) {
935 // Some resource is invalid. We shouldn't paint anything.
936 return;
939 gfxMatrix matrix;
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;
947 gfx->Save();
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
957 // GetCanvasTM().
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) {
969 gfx->Save();
970 clipPathFrame->ClipPaint(aContext, aFrame, matrix);
973 /* Paint the child */
974 if (filterFrame) {
975 nsRect* dirtyRect = nullptr;
976 nsRect tmpDirtyRect;
977 if (aDirtyRect) {
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()) {
983 return;
985 gfxMatrix deviceToUserSpace = userToDeviceSpace;
986 deviceToUserSpace.Invert();
987 gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
988 gfxRect(aDirtyRect->x, aDirtyRect->y,
989 aDirtyRect->width, aDirtyRect->height));
990 tmpDirtyRect =
991 nsLayoutUtils::RoundGfxRectToAppRect(
992 dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
993 aFrame->GetPosition();
994 dirtyRect = &tmpDirtyRect;
996 SVGPaintCallback paintCallback;
997 filterFrame->PaintFilteredFrame(aContext, aFrame, &paintCallback, dirtyRect);
998 } else {
999 svgChildFrame->PaintSVG(aContext, aDirtyRect);
1002 if (clipPathFrame && isTrivialClip) {
1003 gfx->Restore();
1006 /* No more effects, we're done. */
1007 if (!complexEffects)
1008 return;
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();
1029 } else {
1030 gfx->Mask(clipMaskSurface);
1035 if (maskSurface) {
1036 gfx->Mask(maskSurface);
1037 } else if (opacity != 1.0f) {
1038 gfx->Paint(opacity);
1041 gfx->Restore();
1044 bool
1045 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
1047 nsSVGEffects::EffectProperties props =
1048 nsSVGEffects::GetEffectProperties(aFrame);
1049 if (!props.mClipPath)
1050 return true;
1052 bool isOK = true;
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.
1057 return false;
1060 return clipPathFrame->ClipHitTest(aFrame, GetCanvasTM(aFrame,
1061 nsISVGChildFrame::FOR_HIT_TESTING), aPoint);
1064 nsIFrame *
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();
1071 current;
1072 current = current->GetPrevSibling()) {
1073 nsISVGChildFrame* SVGFrame = do_QueryFrame(current);
1074 if (SVGFrame) {
1075 const nsIContent* content = current->GetContent();
1076 if (content->IsSVG() &&
1077 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
1078 continue;
1080 result = SVGFrame->GetFrameForPoint(aPoint);
1081 if (result)
1082 break;
1086 if (result && !HitTestClip(aFrame, aPoint))
1087 result = nullptr;
1089 return result;
1092 nsRect
1093 nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
1095 nsRect rect;
1097 for (nsIFrame* kid = aFrames.FirstChild();
1098 kid;
1099 kid = kid->GetNextSibling()) {
1100 nsISVGChildFrame* child = do_QueryFrame(kid);
1101 if (child) {
1102 nsRect childRect = child->GetCoveredRegion();
1103 rect.UnionRect(rect, childRect);
1107 return rect;
1110 nsPoint
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));
1129 nsRect
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());
1140 gfxIntSize
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,
1151 surfaceSize.width);
1152 surfaceSize.height = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
1153 surfaceSize.height);
1154 *aResultOverflows = true;
1157 return surfaceSize;
1160 bool
1161 nsSVGUtils::HitTestRect(const gfxMatrix &aMatrix,
1162 float aRX, float aRY, float aRWidth, float aRHeight,
1163 float aX, float aY)
1165 gfxRect rect(aRX, aRY, aRWidth, aRHeight);
1166 if (rect.IsEmpty() || aMatrix.IsSingular()) {
1167 return false;
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();
1176 gfxRect
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());
1193 gfxRect clipRect =
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) {
1204 clipRect.x = aX;
1205 clipRect.width = aWidth;
1207 if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
1208 clipRect.y = aY;
1209 clipRect.height = aHeight;
1212 return clipRect;
1214 return gfxRect(aX, aY, aWidth, aHeight);
1217 void
1218 nsSVGUtils::CompositeSurfaceMatrix(gfxContext *aContext,
1219 gfxASurface *aSurface,
1220 const gfxMatrix &aCTM, float aOpacity)
1222 if (aCTM.IsSingular())
1223 return;
1225 if (aContext->IsCairo()) {
1226 aContext->Save();
1227 aContext->Multiply(aCTM);
1228 aContext->SetSource(aSurface);
1229 aContext->Paint(aOpacity);
1230 aContext->Restore();
1231 } else {
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);
1252 void
1253 nsSVGUtils::CompositePatternMatrix(gfxContext *aContext,
1254 gfxPattern *aPattern,
1255 const gfxMatrix &aCTM, float aWidth, float aHeight, float aOpacity)
1257 if (aCTM.IsSingular())
1258 return;
1260 aContext->Save();
1261 SetClipRect(aContext, aCTM, gfxRect(0, 0, aWidth, aHeight));
1262 aContext->Multiply(aCTM);
1263 aContext->SetPattern(aPattern);
1264 aContext->Paint(aOpacity);
1265 aContext->Restore();
1268 void
1269 nsSVGUtils::SetClipRect(gfxContext *aContext,
1270 const gfxMatrix &aCTM,
1271 const gfxRect &aRect)
1273 if (aCTM.IsSingular())
1274 return;
1276 gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
1277 aContext->Multiply(aCTM);
1278 aContext->Clip(aRect);
1281 void
1282 nsSVGUtils::ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfxRect)
1284 gfxRect r = aGfxRect;
1285 r.RoundOut();
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()));
1292 gfxRect
1293 nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
1295 if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
1296 aFrame = aFrame->GetParent();
1298 gfxRect bbox;
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
1304 // cases.
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);
1313 } else {
1314 nsSVGTextContainerFrame* metrics = do_QueryFrame(
1315 GetFirstNonAAncestorFrame(aFrame));
1316 if (metrics) {
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()) {
1326 return bbox;
1328 gfxMatrix matrix;
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);
1343 gfxRect
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]);
1353 } else {
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);
1362 bool
1363 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
1365 if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
1366 return false;
1368 nsIAtom *type = aFrame->GetType();
1369 if (type != nsGkAtoms::svgImageFrame &&
1370 type != nsGkAtoms::svgPathGeometryFrame) {
1371 return false;
1373 if (aFrame->StyleSVGReset()->mFilter) {
1374 return false;
1376 // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
1377 if (type == nsGkAtoms::svgImageFrame) {
1378 return true;
1380 const nsStyleSVG *style = aFrame->StyleSVG();
1381 if (style->mMarkerStart || style->mMarkerMid || style->mMarkerEnd) {
1382 return false;
1384 if (style->mFill.mType == eStyleSVGPaintType_None ||
1385 style->mFillOpacity <= 0 ||
1386 !HasStroke(aFrame)) {
1387 return true;
1389 return false;
1392 float
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));
1408 gfxMatrix
1409 nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
1410 nsSVGEnum *aUnits,
1411 nsIFrame *aFrame)
1413 if (aFrame &&
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())) *
1418 aMatrix;
1420 return aMatrix;
1423 nsIFrame*
1424 nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
1426 for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame;
1427 ancestorFrame = ancestorFrame->GetParent()) {
1428 if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
1429 return ancestorFrame;
1432 return nullptr;
1435 gfxMatrix
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();
1458 return gfxMatrix();
1461 // The logic here comes from _cairo_stroke_style_max_distance_from_path
1462 static gfxRect
1463 PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
1464 nsIFrame* aFrame,
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;
1482 /*static*/ gfxRect
1483 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
1484 nsSVGGeometryFrame* aFrame,
1485 const gfxMatrix& aMatrix)
1487 return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
1490 /*static*/ gfxRect
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);
1499 /*static*/ gfxRect
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,
1521 aFrame,
1522 styleExpansionFactor,
1523 aMatrix);
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());
1555 return color;
1558 static void
1559 SetupFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
1560 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
1561 float aOpacity)
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));
1572 static float
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;
1582 /* static */ bool
1583 nsSVGUtils::SetupObjectPaint(gfxContext *aContext,
1584 gfxTextObjectPaint *aObjectPaint,
1585 const nsStyleSVGPaint &aPaint,
1586 float aOpacity)
1588 nsRefPtr<gfxPattern> pattern;
1590 if (!aObjectPaint) {
1591 return false;
1594 switch (aPaint.mType) {
1595 case eStyleSVGPaintType_ObjectFill:
1596 pattern = aObjectPaint->GetFillPattern(aOpacity, aContext->CurrentMatrix());
1597 break;
1598 case eStyleSVGPaintType_ObjectStroke:
1599 pattern = aObjectPaint->GetStrokePattern(aOpacity, aContext->CurrentMatrix());
1600 break;
1601 default:
1602 return false;
1605 if (!pattern) {
1606 return false;
1609 aContext->SetPattern(pattern);
1611 return true;
1614 bool
1615 nsSVGUtils::SetupCairoFillPaint(nsIFrame *aFrame, gfxContext* aContext,
1616 gfxTextObjectPaint *aObjectPaint)
1618 const nsStyleSVG* style = aFrame->StyleSVG();
1619 if (style->mFill.mType == eStyleSVGPaintType_None)
1620 return false;
1622 if (style->mFillRule == NS_STYLE_FILL_RULE_EVENODD)
1623 aContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
1624 else
1625 aContext->SetFillRule(gfxContext::FILL_RULE_WINDING);
1627 float opacity = MaybeOptimizeOpacity(aFrame,
1628 GetOpacity(style->mFillOpacitySource,
1629 style->mFillOpacity,
1630 aObjectPaint));
1631 nsSVGPaintServerFrame *ps =
1632 nsSVGEffects::GetPaintServer(aFrame, &style->mFill, nsSVGEffects::FillProperty());
1633 if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mFill, opacity))
1634 return true;
1636 if (SetupObjectPaint(aContext, aObjectPaint, style->mFill, opacity)) {
1637 return true;
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);
1646 return true;
1649 bool
1650 nsSVGUtils::SetupCairoStrokePaint(nsIFrame *aFrame, gfxContext* aContext,
1651 gfxTextObjectPaint *aObjectPaint)
1653 const nsStyleSVG* style = aFrame->StyleSVG();
1654 if (style->mStroke.mType == eStyleSVGPaintType_None)
1655 return false;
1657 float opacity = MaybeOptimizeOpacity(aFrame,
1658 GetOpacity(style->mStrokeOpacitySource,
1659 style->mStrokeOpacity,
1660 aObjectPaint));
1662 nsSVGPaintServerFrame *ps =
1663 nsSVGEffects::GetPaintServer(aFrame, &style->mStroke, nsSVGEffects::StrokeProperty());
1664 if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mStroke, opacity))
1665 return true;
1667 if (SetupObjectPaint(aContext, aObjectPaint, style->mStroke, opacity)) {
1668 return true;
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);
1677 return true;
1680 /* static */ float
1681 nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
1682 const float& aOpacity,
1683 gfxTextObjectPaint *aOuterObjectPaint)
1685 float opacity = 1.0f;
1686 switch (aOpacityType) {
1687 case eStyleSVGOpacitySource_Normal:
1688 opacity = aOpacity;
1689 break;
1690 case eStyleSVGOpacitySource_ObjectFillOpacity:
1691 if (aOuterObjectPaint) {
1692 opacity = aOuterObjectPaint->GetFillOpacity();
1693 } else {
1694 NS_WARNING("objectFillOpacity used outside of an SVG glyph");
1696 break;
1697 case eStyleSVGOpacitySource_ObjectStrokeOpacity:
1698 if (aOuterObjectPaint) {
1699 opacity = aOuterObjectPaint->GetStrokeOpacity();
1700 } else {
1701 NS_WARNING("objectStrokeOpacity used outside of an SVG glyph");
1703 break;
1704 default:
1705 NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
1707 return opacity;
1710 bool
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;
1719 float
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);
1738 void
1739 nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext *aContext,
1740 gfxTextObjectPaint *aObjectPaint)
1742 float width = GetStrokeWidth(aFrame, aObjectPaint);
1743 if (width <= 0)
1744 return;
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);
1755 break;
1756 case NS_STYLE_STROKE_LINECAP_ROUND:
1757 aContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
1758 break;
1759 case NS_STYLE_STROKE_LINECAP_SQUARE:
1760 aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
1761 break;
1764 aContext->SetMiterLimit(style->mStrokeMiterlimit);
1766 switch (style->mStrokeLinejoin) {
1767 case NS_STYLE_STROKE_LINEJOIN_MITER:
1768 aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
1769 break;
1770 case NS_STYLE_STROKE_LINEJOIN_ROUND:
1771 aContext->SetLineJoin(gfxContext::LINE_JOIN_ROUND);
1772 break;
1773 case NS_STYLE_STROKE_LINEJOIN_BEVEL:
1774 aContext->SetLineJoin(gfxContext::LINE_JOIN_BEVEL);
1775 break;
1779 static bool
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) {
1798 return false;
1800 totalLength += aDashes[i];
1803 } else {
1804 uint32_t count = style->mStrokeDasharrayLength;
1805 if (!count || !aDashes.SetLength(count)) {
1806 return false;
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) {
1815 return false;
1819 const nsStyleCoord *dasharray = style->mStrokeDasharray;
1821 for (uint32_t i = 0; i < count; i++) {
1822 aDashes[i] = nsSVGUtils::CoordToFloat(presContext,
1823 ctx,
1824 dasharray[i]) * pathScale;
1825 if (aDashes[i] < 0.0) {
1826 return false;
1828 totalLength += aDashes[i];
1832 if (aObjectPaint && style->mStrokeDashoffsetFromObject) {
1833 *aDashOffset = aObjectPaint->GetStrokeDashOffset();
1834 } else {
1835 *aDashOffset = nsSVGUtils::CoordToFloat(presContext,
1836 ctx,
1837 style->mStrokeDashoffset);
1840 if (content->IsNodeOfType(nsINode::eTEXT)) {
1841 content = content->GetParent();
1844 return (totalLength > 0.0);
1847 void
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);
1860 uint16_t
1861 nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame)
1863 uint16_t flags = 0;
1865 switch(aFrame->StyleVisibility()->mPointerEvents) {
1866 case NS_STYLE_POINTER_EVENTS_NONE:
1867 break;
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;
1878 break;
1879 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
1880 if (aFrame->StyleVisibility()->IsVisible()) {
1881 flags |= SVG_HIT_TEST_FILL;
1883 break;
1884 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
1885 if (aFrame->StyleVisibility()->IsVisible()) {
1886 flags |= SVG_HIT_TEST_STROKE;
1888 break;
1889 case NS_STYLE_POINTER_EVENTS_VISIBLE:
1890 if (aFrame->StyleVisibility()->IsVisible()) {
1891 flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
1893 break;
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;
1901 break;
1902 case NS_STYLE_POINTER_EVENTS_FILL:
1903 flags |= SVG_HIT_TEST_FILL;
1904 break;
1905 case NS_STYLE_POINTER_EVENTS_STROKE:
1906 flags |= SVG_HIT_TEST_STROKE;
1907 break;
1908 case NS_STYLE_POINTER_EVENTS_ALL:
1909 flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
1910 break;
1911 default:
1912 NS_ERROR("not reached");
1913 break;
1916 return flags;
1919 bool
1920 nsSVGUtils::SetupCairoStroke(nsIFrame* aFrame, gfxContext* aContext,
1921 gfxTextObjectPaint *aObjectPaint)
1923 if (!HasStroke(aFrame, aObjectPaint)) {
1924 return false;
1926 SetupCairoStrokeHitGeometry(aFrame, aContext, aObjectPaint);
1928 return SetupCairoStrokePaint(aFrame, aContext, aObjectPaint);
1931 bool
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");
1939 if (svgFrame) {
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)) {
1945 return true;
1948 return false;
1951 bool
1952 nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
1953 const gfxMatrix& aSVGToAppSpace,
1954 gfxRect* aResult)
1956 nsIFrame* frame = aElement->GetPrimaryFrame();
1957 nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
1958 MOZ_ASSERT(!frame || svgFrame, "Non SVG frame for SVG glyph");
1959 if (svgFrame) {
1960 *aResult = svgFrame->GetBBoxContribution(aSVGToAppSpace,
1961 nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry |
1962 nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry |
1963 nsSVGUtils::eBBoxIncludeMarkers);
1964 return true;
1966 return false;
1969 nsRect
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());