1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla SVG project.
16 : *
17 : * The Initial Developer of the Original Code is IBM Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2005
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either of the GNU General Public License Version 2 or later (the "GPL"),
25 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : // include nsSVGUtils.h first to ensure definition of M_SQRT1_2 is picked up
38 : #include "nsSVGUtils.h"
39 : #include "nsIDOMDocument.h"
40 : #include "nsIDOMSVGElement.h"
41 : #include "nsIDOMSVGSVGElement.h"
42 : #include "nsStyleCoord.h"
43 : #include "nsPresContext.h"
44 : #include "nsSVGSVGElement.h"
45 : #include "nsIContent.h"
46 : #include "nsIDocument.h"
47 : #include "nsIFrame.h"
48 : #include "nsGkAtoms.h"
49 : #include "nsIURI.h"
50 : #include "nsStyleStruct.h"
51 : #include "nsIPresShell.h"
52 : #include "nsNetUtil.h"
53 : #include "nsFrameList.h"
54 : #include "nsISVGChildFrame.h"
55 : #include "nsContentDLF.h"
56 : #include "nsContentUtils.h"
57 : #include "nsSVGFilterFrame.h"
58 : #include "nsINameSpaceManager.h"
59 : #include "nsDOMError.h"
60 : #include "nsSVGOuterSVGFrame.h"
61 : #include "nsSVGInnerSVGFrame.h"
62 : #include "SVGAnimatedPreserveAspectRatio.h"
63 : #include "nsSVGClipPathFrame.h"
64 : #include "nsSVGMaskFrame.h"
65 : #include "nsSVGContainerFrame.h"
66 : #include "nsSVGTextContainerFrame.h"
67 : #include "nsSVGLength2.h"
68 : #include "nsGenericElement.h"
69 : #include "nsSVGGraphicElement.h"
70 : #include "nsAttrValue.h"
71 : #include "nsIScriptError.h"
72 : #include "gfxContext.h"
73 : #include "gfxMatrix.h"
74 : #include "gfxRect.h"
75 : #include "gfxImageSurface.h"
76 : #include "gfxPlatform.h"
77 : #include "nsSVGForeignObjectFrame.h"
78 : #include "nsIDOMSVGUnitTypes.h"
79 : #include "nsSVGEffects.h"
80 : #include "nsMathUtils.h"
81 : #include "nsSVGIntegrationUtils.h"
82 : #include "nsSVGFilterPaintCallback.h"
83 : #include "nsSVGGeometryFrame.h"
84 : #include "nsComputedDOMStyle.h"
85 : #include "nsSVGPathGeometryFrame.h"
86 : #include "nsSVGPathGeometryElement.h"
87 : #include "prdtoa.h"
88 : #include "mozilla/dom/Element.h"
89 : #include "gfxUtils.h"
90 : #include "mozilla/Preferences.h"
91 :
92 : #include "mozilla/gfx/2D.h"
93 :
94 : using namespace mozilla;
95 : using namespace mozilla::dom;
96 : using namespace mozilla::gfx;
97 :
98 : // c = n / 255
99 : // (c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1 / 2.4) - 0.055) * 255 + 0.5
100 : static const PRUint8 glinearRGBTosRGBMap[256] = {
101 : 0, 13, 22, 28, 34, 38, 42, 46,
102 : 50, 53, 56, 59, 61, 64, 66, 69,
103 : 71, 73, 75, 77, 79, 81, 83, 85,
104 : 86, 88, 90, 92, 93, 95, 96, 98,
105 : 99, 101, 102, 104, 105, 106, 108, 109,
106 : 110, 112, 113, 114, 115, 117, 118, 119,
107 : 120, 121, 122, 124, 125, 126, 127, 128,
108 : 129, 130, 131, 132, 133, 134, 135, 136,
109 : 137, 138, 139, 140, 141, 142, 143, 144,
110 : 145, 146, 147, 148, 148, 149, 150, 151,
111 : 152, 153, 154, 155, 155, 156, 157, 158,
112 : 159, 159, 160, 161, 162, 163, 163, 164,
113 : 165, 166, 167, 167, 168, 169, 170, 170,
114 : 171, 172, 173, 173, 174, 175, 175, 176,
115 : 177, 178, 178, 179, 180, 180, 181, 182,
116 : 182, 183, 184, 185, 185, 186, 187, 187,
117 : 188, 189, 189, 190, 190, 191, 192, 192,
118 : 193, 194, 194, 195, 196, 196, 197, 197,
119 : 198, 199, 199, 200, 200, 201, 202, 202,
120 : 203, 203, 204, 205, 205, 206, 206, 207,
121 : 208, 208, 209, 209, 210, 210, 211, 212,
122 : 212, 213, 213, 214, 214, 215, 215, 216,
123 : 216, 217, 218, 218, 219, 219, 220, 220,
124 : 221, 221, 222, 222, 223, 223, 224, 224,
125 : 225, 226, 226, 227, 227, 228, 228, 229,
126 : 229, 230, 230, 231, 231, 232, 232, 233,
127 : 233, 234, 234, 235, 235, 236, 236, 237,
128 : 237, 238, 238, 238, 239, 239, 240, 240,
129 : 241, 241, 242, 242, 243, 243, 244, 244,
130 : 245, 245, 246, 246, 246, 247, 247, 248,
131 : 248, 249, 249, 250, 250, 251, 251, 251,
132 : 252, 252, 253, 253, 254, 254, 255, 255
133 : };
134 :
135 : // c = n / 255
136 : // c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5
137 : static const PRUint8 gsRGBToLinearRGBMap[256] = {
138 : 0, 0, 0, 0, 0, 0, 0, 1,
139 : 1, 1, 1, 1, 1, 1, 1, 1,
140 : 1, 1, 2, 2, 2, 2, 2, 2,
141 : 2, 2, 3, 3, 3, 3, 3, 3,
142 : 4, 4, 4, 4, 4, 5, 5, 5,
143 : 5, 6, 6, 6, 6, 7, 7, 7,
144 : 8, 8, 8, 8, 9, 9, 9, 10,
145 : 10, 10, 11, 11, 12, 12, 12, 13,
146 : 13, 13, 14, 14, 15, 15, 16, 16,
147 : 17, 17, 17, 18, 18, 19, 19, 20,
148 : 20, 21, 22, 22, 23, 23, 24, 24,
149 : 25, 25, 26, 27, 27, 28, 29, 29,
150 : 30, 30, 31, 32, 32, 33, 34, 35,
151 : 35, 36, 37, 37, 38, 39, 40, 41,
152 : 41, 42, 43, 44, 45, 45, 46, 47,
153 : 48, 49, 50, 51, 51, 52, 53, 54,
154 : 55, 56, 57, 58, 59, 60, 61, 62,
155 : 63, 64, 65, 66, 67, 68, 69, 70,
156 : 71, 72, 73, 74, 76, 77, 78, 79,
157 : 80, 81, 82, 84, 85, 86, 87, 88,
158 : 90, 91, 92, 93, 95, 96, 97, 99,
159 : 100, 101, 103, 104, 105, 107, 108, 109,
160 : 111, 112, 114, 115, 116, 118, 119, 121,
161 : 122, 124, 125, 127, 128, 130, 131, 133,
162 : 134, 136, 138, 139, 141, 142, 144, 146,
163 : 147, 149, 151, 152, 154, 156, 157, 159,
164 : 161, 163, 164, 166, 168, 170, 171, 173,
165 : 175, 177, 179, 181, 183, 184, 186, 188,
166 : 190, 192, 194, 196, 198, 200, 202, 204,
167 : 206, 208, 210, 212, 214, 216, 218, 220,
168 : 222, 224, 226, 229, 231, 233, 235, 237,
169 : 239, 242, 244, 246, 248, 250, 253, 255
170 : };
171 :
172 : static bool gSMILEnabled;
173 : static const char SMIL_PREF_STR[] = "svg.smil.enabled";
174 :
175 : static int
176 0 : SMILPrefChanged(const char *aPref, void *aClosure)
177 : {
178 0 : bool prefVal = Preferences::GetBool(SMIL_PREF_STR);
179 0 : gSMILEnabled = prefVal;
180 0 : return 0;
181 : }
182 :
183 : bool
184 0 : NS_SMILEnabled()
185 : {
186 : static bool sInitialized = false;
187 :
188 0 : if (!sInitialized) {
189 : /* check and register ourselves with the pref */
190 0 : gSMILEnabled = Preferences::GetBool(SMIL_PREF_STR);
191 0 : Preferences::RegisterCallback(SMILPrefChanged, SMIL_PREF_STR);
192 :
193 0 : sInitialized = true;
194 : }
195 :
196 0 : return gSMILEnabled;
197 : }
198 :
199 : // we only take the address of this:
200 : static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
201 :
202 0 : SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext *aContext,
203 : RenderMode aMode)
204 : : mContext(aContext)
205 : , mOriginalRenderState(nsnull)
206 : , mMode(aMode)
207 0 : , mPaintingToWindow(false)
208 : {
209 0 : mOriginalRenderState = aContext->RemoveUserData(&sSVGAutoRenderStateKey);
210 : // We always remove ourselves from aContext before it dies, so
211 : // passing nsnull as the destroy function is okay.
212 0 : aContext->AddUserData(&sSVGAutoRenderStateKey, this, nsnull);
213 0 : }
214 :
215 0 : SVGAutoRenderState::~SVGAutoRenderState()
216 : {
217 0 : mContext->RemoveUserData(&sSVGAutoRenderStateKey);
218 0 : if (mOriginalRenderState) {
219 0 : mContext->AddUserData(&sSVGAutoRenderStateKey, mOriginalRenderState, nsnull);
220 : }
221 0 : }
222 :
223 : void
224 0 : SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
225 : {
226 0 : mPaintingToWindow = aPaintingToWindow;
227 0 : }
228 :
229 : /* static */ SVGAutoRenderState::RenderMode
230 0 : SVGAutoRenderState::GetRenderMode(nsRenderingContext *aContext)
231 : {
232 0 : void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
233 0 : if (state) {
234 0 : return static_cast<SVGAutoRenderState*>(state)->mMode;
235 : }
236 0 : return NORMAL;
237 : }
238 :
239 : /* static */ bool
240 0 : SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext)
241 : {
242 0 : void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
243 0 : if (state) {
244 0 : return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
245 : }
246 0 : return false;
247 : }
248 :
249 : nsSVGSVGElement*
250 0 : nsSVGUtils::GetOuterSVGElement(nsSVGElement *aSVGElement)
251 : {
252 0 : nsIContent *element = nsnull;
253 0 : nsIContent *ancestor = aSVGElement->GetFlattenedTreeParent();
254 :
255 0 : while (ancestor && ancestor->IsSVG() &&
256 0 : ancestor->Tag() != nsGkAtoms::foreignObject) {
257 0 : element = ancestor;
258 0 : ancestor = element->GetFlattenedTreeParent();
259 : }
260 :
261 0 : if (element && element->Tag() == nsGkAtoms::svg) {
262 0 : return static_cast<nsSVGSVGElement*>(element);
263 : }
264 0 : return nsnull;
265 : }
266 :
267 : float
268 0 : nsSVGUtils::GetFontSize(Element *aElement)
269 : {
270 0 : if (!aElement)
271 0 : return 1.0f;
272 :
273 : nsRefPtr<nsStyleContext> styleContext =
274 : nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
275 0 : nsnull, nsnull);
276 0 : if (!styleContext) {
277 : // ReportToConsole
278 0 : NS_WARNING("Couldn't get style context for content in GetFontStyle");
279 0 : return 1.0f;
280 : }
281 :
282 0 : return GetFontSize(styleContext);
283 : }
284 :
285 : float
286 0 : nsSVGUtils::GetFontSize(nsIFrame *aFrame)
287 : {
288 0 : NS_ABORT_IF_FALSE(aFrame, "NULL frame in GetFontSize");
289 0 : return GetFontSize(aFrame->GetStyleContext());
290 : }
291 :
292 : float
293 0 : nsSVGUtils::GetFontSize(nsStyleContext *aStyleContext)
294 : {
295 0 : NS_ABORT_IF_FALSE(aStyleContext, "NULL style context in GetFontSize");
296 :
297 0 : nsPresContext *presContext = aStyleContext->PresContext();
298 0 : NS_ABORT_IF_FALSE(presContext, "NULL pres context in GetFontSize");
299 :
300 0 : nscoord fontSize = aStyleContext->GetStyleFont()->mSize;
301 0 : return nsPresContext::AppUnitsToFloatCSSPixels(fontSize) /
302 0 : presContext->TextZoom();
303 : }
304 :
305 : float
306 0 : nsSVGUtils::GetFontXHeight(Element *aElement)
307 : {
308 0 : if (!aElement)
309 0 : return 1.0f;
310 :
311 : nsRefPtr<nsStyleContext> styleContext =
312 : nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
313 0 : nsnull, nsnull);
314 0 : if (!styleContext) {
315 : // ReportToConsole
316 0 : NS_WARNING("Couldn't get style context for content in GetFontStyle");
317 0 : return 1.0f;
318 : }
319 :
320 0 : return GetFontXHeight(styleContext);
321 : }
322 :
323 : float
324 0 : nsSVGUtils::GetFontXHeight(nsIFrame *aFrame)
325 : {
326 0 : NS_ABORT_IF_FALSE(aFrame, "NULL frame in GetFontXHeight");
327 0 : return GetFontXHeight(aFrame->GetStyleContext());
328 : }
329 :
330 : float
331 0 : nsSVGUtils::GetFontXHeight(nsStyleContext *aStyleContext)
332 : {
333 0 : NS_ABORT_IF_FALSE(aStyleContext, "NULL style context in GetFontXHeight");
334 :
335 0 : nsPresContext *presContext = aStyleContext->PresContext();
336 0 : NS_ABORT_IF_FALSE(presContext, "NULL pres context in GetFontXHeight");
337 :
338 0 : nsRefPtr<nsFontMetrics> fontMetrics;
339 : nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext,
340 0 : getter_AddRefs(fontMetrics));
341 :
342 0 : if (!fontMetrics) {
343 : // ReportToConsole
344 0 : NS_WARNING("no FontMetrics in GetFontXHeight()");
345 0 : return 1.0f;
346 : }
347 :
348 0 : nscoord xHeight = fontMetrics->XHeight();
349 0 : return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
350 0 : presContext->TextZoom();
351 : }
352 :
353 : void
354 0 : nsSVGUtils::UnPremultiplyImageDataAlpha(PRUint8 *data,
355 : PRInt32 stride,
356 : const nsIntRect &rect)
357 : {
358 0 : for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
359 0 : for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
360 0 : PRUint8 *pixel = data + stride * y + 4 * x;
361 :
362 0 : PRUint8 a = pixel[GFX_ARGB32_OFFSET_A];
363 0 : if (a == 255)
364 0 : continue;
365 :
366 0 : if (a) {
367 0 : pixel[GFX_ARGB32_OFFSET_B] = (255 * pixel[GFX_ARGB32_OFFSET_B]) / a;
368 0 : pixel[GFX_ARGB32_OFFSET_G] = (255 * pixel[GFX_ARGB32_OFFSET_G]) / a;
369 0 : pixel[GFX_ARGB32_OFFSET_R] = (255 * pixel[GFX_ARGB32_OFFSET_R]) / a;
370 : } else {
371 0 : pixel[GFX_ARGB32_OFFSET_B] = 0;
372 0 : pixel[GFX_ARGB32_OFFSET_G] = 0;
373 0 : pixel[GFX_ARGB32_OFFSET_R] = 0;
374 : }
375 : }
376 : }
377 0 : }
378 :
379 : void
380 0 : nsSVGUtils::PremultiplyImageDataAlpha(PRUint8 *data,
381 : PRInt32 stride,
382 : const nsIntRect &rect)
383 : {
384 0 : for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
385 0 : for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
386 0 : PRUint8 *pixel = data + stride * y + 4 * x;
387 :
388 0 : PRUint8 a = pixel[GFX_ARGB32_OFFSET_A];
389 0 : if (a == 255)
390 0 : continue;
391 :
392 0 : FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_B],
393 : pixel[GFX_ARGB32_OFFSET_B] * a);
394 0 : FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_G],
395 : pixel[GFX_ARGB32_OFFSET_G] * a);
396 0 : FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_R],
397 : pixel[GFX_ARGB32_OFFSET_R] * a);
398 : }
399 : }
400 0 : }
401 :
402 : void
403 0 : nsSVGUtils::ConvertImageDataToLinearRGB(PRUint8 *data,
404 : PRInt32 stride,
405 : const nsIntRect &rect)
406 : {
407 0 : for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
408 0 : for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
409 0 : PRUint8 *pixel = data + stride * y + 4 * x;
410 :
411 : pixel[GFX_ARGB32_OFFSET_B] =
412 0 : gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_B]];
413 0 : pixel[GFX_ARGB32_OFFSET_G] =
414 0 : gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_G]];
415 0 : pixel[GFX_ARGB32_OFFSET_R] =
416 0 : gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_R]];
417 : }
418 : }
419 0 : }
420 :
421 : void
422 0 : nsSVGUtils::ConvertImageDataFromLinearRGB(PRUint8 *data,
423 : PRInt32 stride,
424 : const nsIntRect &rect)
425 : {
426 0 : for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
427 0 : for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
428 0 : PRUint8 *pixel = data + stride * y + 4 * x;
429 :
430 : pixel[GFX_ARGB32_OFFSET_B] =
431 0 : glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_B]];
432 0 : pixel[GFX_ARGB32_OFFSET_G] =
433 0 : glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_G]];
434 0 : pixel[GFX_ARGB32_OFFSET_R] =
435 0 : glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_R]];
436 : }
437 : }
438 0 : }
439 :
440 : nsresult
441 0 : nsSVGUtils::ReportToConsole(nsIDocument* doc,
442 : const char* aWarning,
443 : const PRUnichar **aParams,
444 : PRUint32 aParamsLength)
445 : {
446 : return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
447 : "SVG", doc,
448 : nsContentUtils::eSVG_PROPERTIES,
449 : aWarning,
450 0 : aParams, aParamsLength);
451 : }
452 :
453 : float
454 0 : nsSVGUtils::CoordToFloat(nsPresContext *aPresContext,
455 : nsSVGElement *aContent,
456 : const nsStyleCoord &aCoord)
457 : {
458 0 : switch (aCoord.GetUnit()) {
459 : case eStyleUnit_Factor:
460 : // user units
461 0 : return aCoord.GetFactorValue();
462 :
463 : case eStyleUnit_Coord:
464 0 : return nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue());
465 :
466 : case eStyleUnit_Percent: {
467 0 : nsSVGSVGElement* ctx = aContent->GetCtx();
468 0 : return ctx ? aCoord.GetPercentValue() * ctx->GetLength(nsSVGUtils::XY) : 0.0f;
469 : }
470 : default:
471 0 : return 0.0f;
472 : }
473 : }
474 :
475 : bool
476 0 : nsSVGUtils::EstablishesViewport(nsIContent *aContent)
477 : {
478 : // Although SVG 1.1 states that <image> is an element that establishes a
479 : // viewport, this is really only for the document it references, not
480 : // for any child content, which is what this function is used for.
481 0 : return aContent && aContent->IsSVG() &&
482 0 : (aContent->Tag() == nsGkAtoms::svg ||
483 0 : aContent->Tag() == nsGkAtoms::foreignObject ||
484 0 : aContent->Tag() == nsGkAtoms::symbol);
485 : }
486 :
487 : already_AddRefed<nsIDOMSVGElement>
488 0 : nsSVGUtils::GetNearestViewportElement(nsIContent *aContent)
489 : {
490 0 : nsIContent *element = aContent->GetFlattenedTreeParent();
491 :
492 0 : while (element && element->IsSVG()) {
493 0 : if (EstablishesViewport(element)) {
494 0 : if (element->Tag() == nsGkAtoms::foreignObject) {
495 0 : return nsnull;
496 : }
497 0 : return nsCOMPtr<nsIDOMSVGElement>(do_QueryInterface(element)).forget();
498 : }
499 0 : element = element->GetFlattenedTreeParent();
500 : }
501 0 : return nsnull;
502 : }
503 :
504 : static gfxMatrix
505 0 : GetCTMInternal(nsSVGElement *aElement, bool aScreenCTM, bool aHaveRecursed)
506 : {
507 0 : nsIDocument* currentDoc = aElement->GetCurrentDoc();
508 :
509 : gfxMatrix matrix = aElement->PrependLocalTransformsTo(gfxMatrix(),
510 0 : aHaveRecursed ? nsSVGElement::eAllTransforms : nsSVGElement::eUserSpaceToParent);
511 0 : nsSVGElement *element = aElement;
512 0 : nsIContent *ancestor = aElement->GetFlattenedTreeParent();
513 :
514 0 : while (ancestor && ancestor->IsSVG() &&
515 0 : ancestor->Tag() != nsGkAtoms::foreignObject) {
516 0 : element = static_cast<nsSVGElement*>(ancestor);
517 0 : matrix *= element->PrependLocalTransformsTo(gfxMatrix()); // i.e. *A*ppend
518 0 : if (!aScreenCTM && nsSVGUtils::EstablishesViewport(element)) {
519 0 : if (!element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG) &&
520 0 : !element->NodeInfo()->Equals(nsGkAtoms::symbol, kNameSpaceID_SVG)) {
521 0 : NS_ERROR("New (SVG > 1.1) SVG viewport establishing element?");
522 0 : return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
523 : }
524 : // XXX spec seems to say x,y translation should be undone for IsInnerSVG
525 0 : return matrix;
526 : }
527 0 : ancestor = ancestor->GetFlattenedTreeParent();
528 : }
529 0 : if (!aScreenCTM) {
530 : // didn't find a nearestViewportElement
531 0 : return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
532 : }
533 0 : if (!ancestor || !ancestor->IsElement()) {
534 0 : return matrix;
535 : }
536 0 : if (ancestor->IsSVG()) {
537 0 : if (element->Tag() != nsGkAtoms::svg) {
538 0 : return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
539 : }
540 : return
541 0 : matrix * GetCTMInternal(static_cast<nsSVGElement*>(ancestor), true, true);
542 : }
543 : // XXX this does not take into account CSS transform, or that the non-SVG
544 : // content that we've hit may itself be inside an SVG foreignObject higher up
545 0 : float x = 0.0f, y = 0.0f;
546 0 : if (currentDoc && element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
547 0 : nsIPresShell *presShell = currentDoc->GetShell();
548 0 : if (presShell) {
549 0 : nsIFrame* frame = element->GetPrimaryFrame();
550 0 : nsIFrame* ancestorFrame = presShell->GetRootFrame();
551 0 : if (frame && ancestorFrame) {
552 0 : nsPoint point = frame->GetOffsetTo(ancestorFrame);
553 0 : x = nsPresContext::AppUnitsToFloatCSSPixels(point.x);
554 0 : y = nsPresContext::AppUnitsToFloatCSSPixels(point.y);
555 : }
556 : }
557 : }
558 0 : return matrix * gfxMatrix().Translate(gfxPoint(x, y));
559 : }
560 :
561 : gfxMatrix
562 0 : nsSVGUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM)
563 : {
564 0 : nsIDocument* currentDoc = aElement->GetCurrentDoc();
565 0 : if (currentDoc) {
566 : // Flush all pending notifications so that our frames are up to date
567 0 : currentDoc->FlushPendingNotifications(Flush_Layout);
568 : }
569 0 : return GetCTMInternal(aElement, aScreenCTM, false);
570 : }
571 :
572 : nsSVGDisplayContainerFrame*
573 0 : nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
574 : {
575 0 : NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
576 0 : if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
577 0 : return nsnull;
578 : }
579 0 : while ((aFrame = aFrame->GetParent())) {
580 0 : NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
581 0 : if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame ||
582 0 : aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
583 0 : return do_QueryFrame(aFrame);
584 : }
585 : }
586 0 : NS_NOTREACHED("This is not reached. It's only needed to compile.");
587 0 : return nsnull;
588 : }
589 :
590 : nsRect
591 0 : nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect)
592 : {
593 0 : PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
594 0 : nsIntRect rect = aRect.ToOutsidePixels(appUnitsPerDevPixel);
595 :
596 0 : while (aFrame) {
597 0 : if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
598 0 : break;
599 :
600 0 : nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame);
601 0 : if (filter) {
602 : // When we are under AttributeChanged, we can no longer get the old bbox
603 : // by calling GetBBox(), and we need that to set up the filter region
604 : // with the correct position. :-(
605 : //rect = filter->GetInvalidationBBox(aFrame, rect);
606 :
607 : // XXX [perf] As a horrible workaround, for now we just invalidate the
608 : // entire area of the nearest viewport establishing frame that doesnt
609 : // have overflow:visible. See bug 463939.
610 0 : nsSVGDisplayContainerFrame* viewportFrame = GetNearestSVGViewport(aFrame);
611 0 : while (viewportFrame && !viewportFrame->GetStyleDisplay()->IsScrollableOverflow()) {
612 0 : viewportFrame = GetNearestSVGViewport(viewportFrame);
613 : }
614 0 : if (!viewportFrame) {
615 0 : viewportFrame = GetOuterSVGFrame(aFrame);
616 : }
617 0 : if (viewportFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
618 0 : nsRect r = viewportFrame->GetVisualOverflowRect();
619 : // GetOverflowRect is relative to our border box, but we need it
620 : // relative to our content box.
621 0 : r.MoveBy(viewportFrame->GetPosition() - viewportFrame->GetContentRect().TopLeft());
622 0 : return r;
623 : }
624 0 : NS_ASSERTION(viewportFrame->GetType() == nsGkAtoms::svgInnerSVGFrame,
625 : "Wrong frame type");
626 0 : nsSVGInnerSVGFrame* innerSvg = do_QueryFrame(static_cast<nsIFrame*>(viewportFrame));
627 0 : nsSVGDisplayContainerFrame* innerSvgParent = do_QueryFrame(viewportFrame->GetParent());
628 : float x, y, width, height;
629 0 : static_cast<nsSVGSVGElement*>(innerSvg->GetContent())->
630 0 : GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
631 0 : gfxRect bounds = GetCanvasTM(innerSvgParent).
632 0 : TransformBounds(gfxRect(x, y, width, height));
633 0 : bounds.RoundOut();
634 0 : nsIntRect r;
635 0 : if (gfxUtils::GfxRectToIntRect(bounds, &r)) {
636 0 : rect = r;
637 : } else {
638 0 : NS_NOTREACHED("Not going to invalidate the correct area");
639 : }
640 0 : aFrame = viewportFrame;
641 : }
642 0 : aFrame = aFrame->GetParent();
643 : }
644 :
645 0 : nsRect r = rect.ToAppUnits(appUnitsPerDevPixel);
646 0 : if (aFrame) {
647 0 : NS_ASSERTION(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG,
648 : "outer SVG frame expected");
649 0 : r.MoveBy(aFrame->GetContentRect().TopLeft() - aFrame->GetPosition());
650 : }
651 0 : return r;
652 : }
653 :
654 : void
655 0 : nsSVGUtils::InvalidateCoveredRegion(nsIFrame *aFrame)
656 : {
657 0 : if (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
658 0 : return;
659 :
660 0 : if (aFrame->GetStateBits() & NS_STATE_SVG_REDRAW_SUSPENDED) {
661 0 : aFrame->AddStateBits(NS_STATE_SVG_DIRTY);
662 0 : return;
663 : }
664 :
665 0 : aFrame->RemoveStateBits(NS_STATE_SVG_DIRTY);
666 :
667 0 : nsSVGOuterSVGFrame* outerSVGFrame = GetOuterSVGFrame(aFrame);
668 0 : NS_ASSERTION(outerSVGFrame, "no outer svg frame");
669 0 : if (outerSVGFrame) {
670 0 : nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame);
671 0 : if (!svgFrame)
672 0 : return;
673 :
674 : // Make sure elements styled by :hover get updated if script/animation moves
675 : // them under or out from under the pointer:
676 0 : aFrame->PresContext()->PresShell()->SynthesizeMouseMove(false);
677 :
678 0 : nsRect rect = FindFilterInvalidation(aFrame, svgFrame->GetCoveredRegion());
679 0 : outerSVGFrame->Invalidate(rect);
680 : }
681 : }
682 :
683 : void
684 0 : nsSVGUtils::UpdateGraphic(nsIFrame *aFrame)
685 : {
686 0 : nsSVGEffects::InvalidateRenderingObservers(aFrame);
687 :
688 0 : if (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
689 0 : return;
690 :
691 0 : if (aFrame->GetStateBits() & NS_STATE_SVG_REDRAW_SUSPENDED) {
692 0 : aFrame->AddStateBits(NS_STATE_SVG_DIRTY);
693 0 : return;
694 : }
695 :
696 0 : aFrame->RemoveStateBits(NS_STATE_SVG_DIRTY);
697 :
698 0 : nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame);
699 0 : if (!svgFrame)
700 0 : return;
701 :
702 0 : nsSVGOuterSVGFrame *outerSVGFrame = GetOuterSVGFrame(aFrame);
703 0 : if (!outerSVGFrame) {
704 0 : NS_ERROR("null outerSVGFrame");
705 0 : return;
706 : }
707 :
708 : // Make sure elements styled by :hover get updated if script/animation moves
709 : // them under or out from under the pointer:
710 0 : aFrame->PresContext()->PresShell()->SynthesizeMouseMove(false);
711 :
712 0 : nsRect oldRegion = svgFrame->GetCoveredRegion();
713 0 : outerSVGFrame->Invalidate(FindFilterInvalidation(aFrame, oldRegion));
714 0 : svgFrame->UpdateCoveredRegion();
715 0 : nsRect newRegion = svgFrame->GetCoveredRegion();
716 0 : if (oldRegion.IsEqualInterior(newRegion))
717 : return;
718 :
719 0 : outerSVGFrame->Invalidate(FindFilterInvalidation(aFrame, newRegion));
720 0 : if (!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
721 0 : NotifyAncestorsOfFilterRegionChange(aFrame);
722 : }
723 : }
724 :
725 : void
726 0 : nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
727 : {
728 0 : NS_ABORT_IF_FALSE(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG),
729 : "Not expecting to be called on the outer SVG Frame");
730 :
731 0 : aFrame = aFrame->GetParent();
732 :
733 0 : while (aFrame) {
734 0 : if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
735 0 : return;
736 :
737 0 : nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
738 0 : if (property) {
739 0 : property->Invalidate();
740 : }
741 0 : aFrame = aFrame->GetParent();
742 : }
743 : }
744 :
745 : double
746 0 : nsSVGUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight)
747 : {
748 0 : return sqrt((aWidth*aWidth + aHeight*aHeight)/2);
749 : }
750 :
751 : float
752 0 : nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
753 : {
754 : float fraction, axis;
755 :
756 0 : switch (aLength->GetCtxType()) {
757 : case X:
758 0 : axis = aRect.Width();
759 0 : break;
760 : case Y:
761 0 : axis = aRect.Height();
762 0 : break;
763 : case XY:
764 0 : axis = float(ComputeNormalizedHypotenuse(aRect.Width(), aRect.Height()));
765 0 : break;
766 : default:
767 0 : NS_NOTREACHED("unexpected ctx type");
768 0 : axis = 0.0f;
769 0 : break;
770 : }
771 :
772 0 : if (aLength->IsPercentage()) {
773 0 : fraction = aLength->GetAnimValInSpecifiedUnits() / 100;
774 : } else {
775 : fraction = aLength->GetAnimValue(static_cast<nsSVGSVGElement*>
776 0 : (nsnull));
777 : }
778 :
779 0 : return fraction * axis;
780 : }
781 :
782 : float
783 0 : nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
784 : {
785 0 : return aLength->GetAnimValue(aSVGElement);
786 : }
787 :
788 : float
789 0 : nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
790 : {
791 0 : return aLength->GetAnimValue(aNonSVGContext);
792 : }
793 :
794 : float
795 0 : nsSVGUtils::AngleBisect(float a1, float a2)
796 : {
797 0 : float delta = fmod(a2 - a1, static_cast<float>(2*M_PI));
798 0 : if (delta < 0) {
799 0 : delta += 2*M_PI;
800 : }
801 : /* delta is now the angle from a1 around to a2, in the range [0, 2*M_PI) */
802 0 : float r = a1 + delta/2;
803 0 : if (delta >= M_PI) {
804 : /* the arc from a2 to a1 is smaller, so use the ray on that side */
805 0 : r += M_PI;
806 : }
807 0 : return r;
808 : }
809 :
810 : nsSVGOuterSVGFrame *
811 0 : nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
812 : {
813 0 : while (aFrame) {
814 0 : if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
815 0 : return static_cast<nsSVGOuterSVGFrame*>(aFrame);
816 : }
817 0 : aFrame = aFrame->GetParent();
818 : }
819 :
820 0 : return nsnull;
821 : }
822 :
823 : nsIFrame*
824 0 : nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
825 : {
826 0 : nsISVGChildFrame* svg = do_QueryFrame(aFrame);
827 0 : if (!svg)
828 0 : return nsnull;
829 0 : *aRect = (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) ?
830 0 : nsRect(0, 0, 0, 0) : svg->GetCoveredRegion();
831 0 : return GetOuterSVGFrame(aFrame);
832 : }
833 :
834 : gfxMatrix
835 0 : nsSVGUtils::GetViewBoxTransform(const nsSVGElement* aElement,
836 : float aViewportWidth, float aViewportHeight,
837 : float aViewboxX, float aViewboxY,
838 : float aViewboxWidth, float aViewboxHeight,
839 : const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio)
840 : {
841 : return GetViewBoxTransform(aElement,
842 : aViewportWidth, aViewportHeight,
843 : aViewboxX, aViewboxY,
844 : aViewboxWidth, aViewboxHeight,
845 0 : aPreserveAspectRatio.GetAnimValue());
846 : }
847 :
848 : gfxMatrix
849 0 : nsSVGUtils::GetViewBoxTransform(const nsSVGElement* aElement,
850 : float aViewportWidth, float aViewportHeight,
851 : float aViewboxX, float aViewboxY,
852 : float aViewboxWidth, float aViewboxHeight,
853 : const SVGPreserveAspectRatio &aPreserveAspectRatio)
854 : {
855 0 : NS_ASSERTION(aViewportWidth >= 0, "viewport width must be nonnegative!");
856 0 : NS_ASSERTION(aViewportHeight >= 0, "viewport height must be nonnegative!");
857 0 : NS_ASSERTION(aViewboxWidth > 0, "viewBox width must be greater than zero!");
858 0 : NS_ASSERTION(aViewboxHeight > 0, "viewBox height must be greater than zero!");
859 :
860 0 : PRUint16 align = aPreserveAspectRatio.GetAlign();
861 0 : PRUint16 meetOrSlice = aPreserveAspectRatio.GetMeetOrSlice();
862 :
863 : // default to the defaults
864 0 : if (align == nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_UNKNOWN)
865 0 : align = nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID;
866 0 : if (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN)
867 0 : meetOrSlice = nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET;
868 :
869 : float a, d, e, f;
870 0 : a = aViewportWidth / aViewboxWidth;
871 0 : d = aViewportHeight / aViewboxHeight;
872 0 : e = 0.0f;
873 0 : f = 0.0f;
874 :
875 0 : if (align != nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE &&
876 : a != d) {
877 0 : if ((meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET &&
878 : a < d) ||
879 : (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE &&
880 : d < a)) {
881 0 : d = a;
882 0 : switch (align) {
883 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMIN:
884 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
885 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
886 0 : break;
887 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
888 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
889 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
890 0 : f = (aViewportHeight - a * aViewboxHeight) / 2.0f;
891 0 : break;
892 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
893 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
894 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
895 0 : f = aViewportHeight - a * aViewboxHeight;
896 0 : break;
897 : default:
898 0 : NS_NOTREACHED("Unknown value for align");
899 : }
900 : }
901 0 : else if (
902 : (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET &&
903 : d < a) ||
904 : (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE &&
905 : a < d)) {
906 0 : a = d;
907 0 : switch (align) {
908 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMIN:
909 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
910 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
911 0 : break;
912 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
913 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
914 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
915 0 : e = (aViewportWidth - a * aViewboxWidth) / 2.0f;
916 0 : break;
917 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
918 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
919 : case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
920 0 : e = aViewportWidth - a * aViewboxWidth;
921 0 : break;
922 : default:
923 0 : NS_NOTREACHED("Unknown value for align");
924 : }
925 : }
926 0 : else NS_NOTREACHED("Unknown value for meetOrSlice");
927 : }
928 :
929 0 : if (aViewboxX) e += -a * aViewboxX;
930 0 : if (aViewboxY) f += -d * aViewboxY;
931 :
932 0 : return gfxMatrix(a, 0.0f, 0.0f, d, e, f);
933 : }
934 :
935 : gfxMatrix
936 0 : nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
937 : {
938 : // XXX yuck, we really need a common interface for GetCanvasTM
939 :
940 0 : if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
941 0 : return nsSVGIntegrationUtils::GetInitialMatrix(aFrame);
942 : }
943 :
944 0 : nsIAtom* type = aFrame->GetType();
945 0 : if (type == nsGkAtoms::svgForeignObjectFrame) {
946 0 : return static_cast<nsSVGForeignObjectFrame*>(aFrame)->GetCanvasTM();
947 : }
948 :
949 0 : nsSVGContainerFrame *containerFrame = do_QueryFrame(aFrame);
950 0 : if (containerFrame) {
951 0 : return containerFrame->GetCanvasTM();
952 : }
953 :
954 0 : return static_cast<nsSVGGeometryFrame*>(aFrame)->GetCanvasTM();
955 : }
956 :
957 : void
958 0 : nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, PRUint32 aFlags)
959 : {
960 0 : nsIFrame *kid = aFrame->GetFirstPrincipalChild();
961 :
962 0 : while (kid) {
963 0 : nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
964 0 : if (SVGFrame) {
965 0 : SVGFrame->NotifySVGChanged(aFlags);
966 : } else {
967 0 : NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
968 : // recurse into the children of container frames e.g. <clipPath>, <mask>
969 : // in case they have child frames with transformation matrices
970 0 : NotifyChildrenOfSVGChange(kid, aFlags);
971 : }
972 0 : kid = kid->GetNextSibling();
973 : }
974 0 : }
975 :
976 : void
977 0 : nsSVGUtils::NotifyRedrawSuspended(nsIFrame *aFrame)
978 : {
979 0 : aFrame->AddStateBits(NS_STATE_SVG_REDRAW_SUSPENDED);
980 :
981 0 : nsIFrame *kid = aFrame->GetFirstPrincipalChild();
982 :
983 0 : while (kid) {
984 0 : nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
985 0 : if (SVGFrame) {
986 0 : SVGFrame->NotifyRedrawSuspended();
987 : }
988 0 : kid = kid->GetNextSibling();
989 : }
990 0 : }
991 :
992 : void
993 0 : nsSVGUtils::NotifyRedrawUnsuspended(nsIFrame *aFrame)
994 : {
995 0 : aFrame->RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED);
996 :
997 0 : nsIFrame *kid = aFrame->GetFirstPrincipalChild();
998 :
999 0 : while (kid) {
1000 0 : nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
1001 0 : if (SVGFrame) {
1002 0 : SVGFrame->NotifyRedrawUnsuspended();
1003 : }
1004 0 : kid = kid->GetNextSibling();
1005 : }
1006 0 : }
1007 :
1008 : // ************************************************************
1009 :
1010 : class SVGPaintCallback : public nsSVGFilterPaintCallback
1011 0 : {
1012 : public:
1013 0 : virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
1014 : const nsIntRect* aDirtyRect)
1015 : {
1016 0 : nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget);
1017 0 : NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
1018 :
1019 0 : nsIntRect* dirtyRect = nsnull;
1020 0 : nsIntRect tmpDirtyRect;
1021 :
1022 : // aDirtyRect is in user-space pixels, we need to convert to
1023 : // outer-SVG-frame-relative device pixels.
1024 0 : if (aDirtyRect) {
1025 0 : gfxMatrix userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
1026 0 : if (userToDeviceSpace.IsSingular()) {
1027 0 : return;
1028 : }
1029 : gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
1030 0 : gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
1031 0 : dirtyBounds.RoundOut();
1032 0 : if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
1033 0 : dirtyRect = &tmpDirtyRect;
1034 : }
1035 : }
1036 :
1037 0 : svgChildFrame->PaintSVG(aContext, dirtyRect);
1038 : }
1039 : };
1040 :
1041 : void
1042 0 : nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
1043 : const nsIntRect *aDirtyRect,
1044 : nsIFrame *aFrame)
1045 : {
1046 0 : nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
1047 0 : if (!svgChildFrame)
1048 0 : return;
1049 :
1050 0 : float opacity = aFrame->GetStyleDisplay()->mOpacity;
1051 0 : if (opacity == 0.0f)
1052 0 : return;
1053 :
1054 0 : const nsIContent* content = aFrame->GetContent();
1055 0 : if (content->IsSVG() &&
1056 0 : !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
1057 0 : return;
1058 : }
1059 :
1060 : /* Properties are added lazily and may have been removed by a restyle,
1061 : so make sure all applicable ones are set again. */
1062 :
1063 : nsSVGEffects::EffectProperties effectProperties =
1064 0 : nsSVGEffects::GetEffectProperties(aFrame);
1065 :
1066 0 : bool isOK = true;
1067 0 : nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
1068 :
1069 : /* Check if we need to draw anything. HasValidCoveredRect only returns
1070 : * true for path geometry and glyphs, so basically we're traversing
1071 : * all containers and we can only skip leaves here.
1072 : */
1073 0 : if (aDirtyRect && svgChildFrame->HasValidCoveredRect()) {
1074 0 : if (filterFrame) {
1075 0 : if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull)))
1076 0 : return;
1077 : } else {
1078 : nsRect leafBounds = nsSVGUtils::TransformFrameRectToOuterSVG(
1079 0 : aFrame->GetRect(), GetCanvasTM(aFrame), aFrame->PresContext());
1080 0 : nsRect rect = aDirtyRect->ToAppUnits(aFrame->PresContext()->AppUnitsPerDevPixel());
1081 0 : if (!rect.Intersects(leafBounds))
1082 : return;
1083 : }
1084 : }
1085 :
1086 : /* SVG defines the following rendering model:
1087 : *
1088 : * 1. Render fill
1089 : * 2. Render stroke
1090 : * 3. Render markers
1091 : * 4. Apply filter
1092 : * 5. Apply clipping, masking, group opacity
1093 : *
1094 : * We follow this, but perform a couple of optimizations:
1095 : *
1096 : * + Use cairo's clipPath when representable natively (single object
1097 : * clip region).
1098 : *
1099 : * + Merge opacity and masking if both used together.
1100 : */
1101 :
1102 0 : if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
1103 0 : opacity = 1.0f;
1104 :
1105 0 : gfxContext *gfx = aContext->ThebesContext();
1106 0 : bool complexEffects = false;
1107 :
1108 0 : nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
1109 0 : nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
1110 :
1111 0 : bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
1112 :
1113 0 : if (!isOK) {
1114 : // Some resource is invalid. We shouldn't paint anything.
1115 0 : return;
1116 : }
1117 :
1118 0 : gfxMatrix matrix;
1119 0 : if (clipPathFrame || maskFrame)
1120 0 : matrix = GetCanvasTM(aFrame);
1121 :
1122 : /* Check if we need to do additional operations on this child's
1123 : * rendering, which necessitates rendering into another surface. */
1124 0 : if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
1125 0 : complexEffects = true;
1126 0 : gfx->Save();
1127 0 : gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1128 : }
1129 :
1130 : /* If this frame has only a trivial clipPath, set up cairo's clipping now so
1131 : * we can just do normal painting and get it clipped appropriately.
1132 : */
1133 0 : if (clipPathFrame && isTrivialClip) {
1134 0 : gfx->Save();
1135 0 : clipPathFrame->ClipPaint(aContext, aFrame, matrix);
1136 : }
1137 :
1138 : /* Paint the child */
1139 0 : if (filterFrame) {
1140 0 : SVGPaintCallback paintCallback;
1141 0 : filterFrame->FilterPaint(aContext, aFrame, &paintCallback, aDirtyRect);
1142 : } else {
1143 0 : svgChildFrame->PaintSVG(aContext, aDirtyRect);
1144 : }
1145 :
1146 0 : if (clipPathFrame && isTrivialClip) {
1147 0 : gfx->Restore();
1148 : }
1149 :
1150 : /* No more effects, we're done. */
1151 0 : if (!complexEffects)
1152 0 : return;
1153 :
1154 0 : gfx->PopGroupToSource();
1155 :
1156 : nsRefPtr<gfxPattern> maskSurface =
1157 : maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
1158 0 : matrix, opacity) : nsnull;
1159 :
1160 0 : nsRefPtr<gfxPattern> clipMaskSurface;
1161 0 : if (clipPathFrame && !isTrivialClip) {
1162 0 : gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1163 :
1164 0 : nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
1165 0 : clipMaskSurface = gfx->PopGroup();
1166 :
1167 0 : if (NS_SUCCEEDED(rv) && clipMaskSurface) {
1168 : // Still more set after clipping, so clip to another surface
1169 0 : if (maskSurface || opacity != 1.0f) {
1170 0 : gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1171 0 : gfx->Mask(clipMaskSurface);
1172 0 : gfx->PopGroupToSource();
1173 : } else {
1174 0 : gfx->Mask(clipMaskSurface);
1175 : }
1176 : }
1177 : }
1178 :
1179 0 : if (maskSurface) {
1180 0 : gfx->Mask(maskSurface);
1181 0 : } else if (opacity != 1.0f) {
1182 0 : gfx->Paint(opacity);
1183 : }
1184 :
1185 0 : gfx->Restore();
1186 : }
1187 :
1188 : bool
1189 0 : nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
1190 : {
1191 : nsSVGEffects::EffectProperties props =
1192 0 : nsSVGEffects::GetEffectProperties(aFrame);
1193 0 : if (!props.mClipPath)
1194 0 : return true;
1195 :
1196 0 : bool isOK = true;
1197 0 : nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK);
1198 0 : if (!clipPathFrame || !isOK) {
1199 : // clipPath is not a valid resource, so nothing gets painted, so
1200 : // hit-testing must fail.
1201 0 : return false;
1202 : }
1203 :
1204 0 : return clipPathFrame->ClipHitTest(aFrame, GetCanvasTM(aFrame), aPoint);
1205 : }
1206 :
1207 : nsIFrame *
1208 0 : nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
1209 : {
1210 : // Traverse the list in reverse order, so that if we get a hit we know that's
1211 : // the topmost frame that intersects the point; then we can just return it.
1212 0 : nsIFrame* result = nsnull;
1213 0 : for (nsIFrame* current = aFrame->PrincipalChildList().LastChild();
1214 : current;
1215 : current = current->GetPrevSibling()) {
1216 0 : nsISVGChildFrame* SVGFrame = do_QueryFrame(current);
1217 0 : if (SVGFrame) {
1218 0 : const nsIContent* content = current->GetContent();
1219 0 : if (content->IsSVG() &&
1220 0 : !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
1221 0 : continue;
1222 : }
1223 0 : result = SVGFrame->GetFrameForPoint(aPoint);
1224 0 : if (result)
1225 0 : break;
1226 : }
1227 : }
1228 :
1229 0 : if (result && !HitTestClip(aFrame, aPoint))
1230 0 : result = nsnull;
1231 :
1232 0 : return result;
1233 : }
1234 :
1235 : nsRect
1236 0 : nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
1237 : {
1238 0 : nsRect rect;
1239 :
1240 0 : for (nsIFrame* kid = aFrames.FirstChild();
1241 : kid;
1242 : kid = kid->GetNextSibling()) {
1243 0 : nsISVGChildFrame* child = do_QueryFrame(kid);
1244 0 : if (child) {
1245 0 : nsRect childRect = child->GetCoveredRegion();
1246 0 : rect.UnionRect(rect, childRect);
1247 : }
1248 : }
1249 :
1250 : return rect;
1251 : }
1252 :
1253 : nsPoint
1254 0 : nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint,
1255 : const gfxMatrix& aFrameToCanvasTM,
1256 : nsPresContext* aPresContext)
1257 : {
1258 0 : NS_ABORT_IF_FALSE(!aFrameToCanvasTM.IsSingular(),
1259 : "Callers must not pass a singular matrix");
1260 0 : gfxMatrix canvasDevToFrameUserSpace = aFrameToCanvasTM;
1261 0 : canvasDevToFrameUserSpace.Invert();
1262 : gfxPoint devPt = gfxPoint(aPoint.x, aPoint.y) /
1263 0 : aPresContext->AppUnitsPerDevPixel();
1264 0 : gfxPoint userPt = canvasDevToFrameUserSpace.Transform(devPt).Round();
1265 0 : gfxPoint appPt = userPt * aPresContext->AppUnitsPerCSSPixel();
1266 0 : userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
1267 0 : userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
1268 : // now guaranteed to be safe:
1269 0 : return nsPoint(nscoord(userPt.x), nscoord(userPt.y));
1270 : }
1271 :
1272 : nsRect
1273 0 : nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
1274 : const gfxMatrix& aMatrix,
1275 : nsPresContext* aPresContext)
1276 : {
1277 0 : gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
1278 0 : r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
1279 : return nsLayoutUtils::RoundGfxRectToAppRect(
1280 0 : aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
1281 : }
1282 :
1283 : gfxIntSize
1284 0 : nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
1285 : bool *aResultOverflows)
1286 : {
1287 0 : gfxIntSize surfaceSize(ClampToInt(ceil(aSize.width)), ClampToInt(ceil(aSize.height)));
1288 :
1289 0 : *aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
1290 0 : surfaceSize.height != ceil(aSize.height);
1291 :
1292 0 : if (!gfxASurface::CheckSurfaceSize(surfaceSize)) {
1293 : surfaceSize.width = NS_MIN(NS_SVG_OFFSCREEN_MAX_DIMENSION,
1294 0 : surfaceSize.width);
1295 : surfaceSize.height = NS_MIN(NS_SVG_OFFSCREEN_MAX_DIMENSION,
1296 0 : surfaceSize.height);
1297 0 : *aResultOverflows = true;
1298 : }
1299 :
1300 : return surfaceSize;
1301 : }
1302 :
1303 : bool
1304 0 : nsSVGUtils::HitTestRect(const gfxMatrix &aMatrix,
1305 : float aRX, float aRY, float aRWidth, float aRHeight,
1306 : float aX, float aY)
1307 : {
1308 0 : if (aMatrix.IsSingular()) {
1309 0 : return false;
1310 : }
1311 0 : gfxContext ctx(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
1312 0 : ctx.SetMatrix(aMatrix);
1313 0 : ctx.NewPath();
1314 0 : ctx.Rectangle(gfxRect(aRX, aRY, aRWidth, aRHeight));
1315 0 : ctx.IdentityMatrix();
1316 0 : return ctx.PointInFill(gfxPoint(aX, aY));
1317 : }
1318 :
1319 : gfxRect
1320 0 : nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame,
1321 : float aX, float aY, float aWidth, float aHeight)
1322 : {
1323 0 : const nsStyleDisplay* disp = aFrame->GetStyleDisplay();
1324 :
1325 0 : if (!(disp->mClipFlags & NS_STYLE_CLIP_RECT)) {
1326 0 : NS_ASSERTION(disp->mClipFlags == NS_STYLE_CLIP_AUTO,
1327 : "We don't know about this type of clip.");
1328 0 : return gfxRect(aX, aY, aWidth, aHeight);
1329 : }
1330 :
1331 0 : if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
1332 : disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
1333 :
1334 : nsIntRect clipPxRect =
1335 0 : disp->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel());
1336 : gfxRect clipRect =
1337 0 : gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height);
1338 :
1339 0 : if (NS_STYLE_CLIP_RIGHT_AUTO & disp->mClipFlags) {
1340 0 : clipRect.width = aWidth - clipRect.X();
1341 : }
1342 0 : if (NS_STYLE_CLIP_BOTTOM_AUTO & disp->mClipFlags) {
1343 0 : clipRect.height = aHeight - clipRect.Y();
1344 : }
1345 :
1346 0 : if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
1347 0 : clipRect.x = aX;
1348 0 : clipRect.width = aWidth;
1349 : }
1350 0 : if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
1351 0 : clipRect.y = aY;
1352 0 : clipRect.height = aHeight;
1353 : }
1354 :
1355 0 : return clipRect;
1356 : }
1357 0 : return gfxRect(aX, aY, aWidth, aHeight);
1358 : }
1359 :
1360 : void
1361 0 : nsSVGUtils::CompositeSurfaceMatrix(gfxContext *aContext,
1362 : gfxASurface *aSurface,
1363 : const gfxMatrix &aCTM, float aOpacity)
1364 : {
1365 0 : if (aCTM.IsSingular())
1366 0 : return;
1367 :
1368 0 : if (aContext->IsCairo()) {
1369 0 : aContext->Save();
1370 0 : aContext->Multiply(aCTM);
1371 0 : aContext->SetSource(aSurface);
1372 0 : aContext->Paint(aOpacity);
1373 0 : aContext->Restore();
1374 : } else {
1375 0 : DrawTarget *dt = aContext->GetDrawTarget();
1376 0 : Matrix oldMat = dt->GetTransform();
1377 : RefPtr<SourceSurface> surf =
1378 0 : gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
1379 0 : dt->SetTransform(oldMat * ToMatrix(aCTM));
1380 :
1381 0 : gfxSize size = aSurface->GetSize();
1382 0 : NS_ASSERTION(size.width >= 0 && size.height >= 0, "Failure to get size for aSurface.");
1383 :
1384 0 : gfxPoint pt = aSurface->GetDeviceOffset();
1385 :
1386 : dt->FillRect(Rect(-pt.x, -pt.y, size.width, size.height),
1387 : SurfacePattern(surf, EXTEND_CLAMP,
1388 0 : Matrix(1.0f, 0, 0, 1.0f, -pt.x, -pt.y)),
1389 0 : DrawOptions(aOpacity));
1390 :
1391 0 : dt->SetTransform(oldMat);
1392 : }
1393 : }
1394 :
1395 : void
1396 0 : nsSVGUtils::CompositePatternMatrix(gfxContext *aContext,
1397 : gfxPattern *aPattern,
1398 : const gfxMatrix &aCTM, float aWidth, float aHeight, float aOpacity)
1399 : {
1400 0 : if (aCTM.IsSingular())
1401 0 : return;
1402 :
1403 0 : aContext->Save();
1404 0 : SetClipRect(aContext, aCTM, gfxRect(0, 0, aWidth, aHeight));
1405 0 : aContext->Multiply(aCTM);
1406 0 : aContext->SetPattern(aPattern);
1407 0 : aContext->Paint(aOpacity);
1408 0 : aContext->Restore();
1409 : }
1410 :
1411 : void
1412 0 : nsSVGUtils::SetClipRect(gfxContext *aContext,
1413 : const gfxMatrix &aCTM,
1414 : const gfxRect &aRect)
1415 : {
1416 0 : if (aCTM.IsSingular())
1417 0 : return;
1418 :
1419 0 : gfxMatrix oldMatrix = aContext->CurrentMatrix();
1420 0 : aContext->Multiply(aCTM);
1421 0 : aContext->Clip(aRect);
1422 0 : aContext->SetMatrix(oldMatrix);
1423 : }
1424 :
1425 : void
1426 0 : nsSVGUtils::ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfxRect)
1427 : {
1428 0 : gfxRect r = aGfxRect;
1429 0 : r.RoundOut();
1430 0 : gfxRect r2(aRect->x, aRect->y, aRect->width, aRect->height);
1431 0 : r = r.Intersect(r2);
1432 0 : *aRect = nsIntRect(PRInt32(r.X()), PRInt32(r.Y()),
1433 0 : PRInt32(r.Width()), PRInt32(r.Height()));
1434 0 : }
1435 :
1436 : gfxRect
1437 0 : nsSVGUtils::GetBBox(nsIFrame *aFrame, PRUint32 aFlags)
1438 : {
1439 0 : if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
1440 0 : aFrame = aFrame->GetParent();
1441 : }
1442 0 : gfxRect bbox;
1443 0 : nsISVGChildFrame *svg = do_QueryFrame(aFrame);
1444 0 : if (svg) {
1445 : // It is possible to apply a gradient, pattern, clipping path, mask or
1446 : // filter to text. When one of these facilities is applied to text
1447 : // the bounding box is the entire text element in all
1448 : // cases.
1449 : nsSVGTextContainerFrame* metrics = do_QueryFrame(
1450 0 : GetFirstNonAAncestorFrame(aFrame));
1451 0 : if (metrics) {
1452 0 : while (aFrame->GetType() != nsGkAtoms::svgTextFrame) {
1453 0 : aFrame = aFrame->GetParent();
1454 : }
1455 0 : svg = do_QueryFrame(aFrame);
1456 : }
1457 0 : nsIContent* content = aFrame->GetContent();
1458 0 : if (content->IsSVG() &&
1459 0 : !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
1460 0 : return bbox;
1461 : }
1462 0 : gfxMatrix matrix;
1463 0 : if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
1464 : // The spec says getBBox "Returns the tight bounding box in *current user
1465 : // space*". So we should really be doing this for all elements, but that
1466 : // needs investigation to check that we won't break too much content.
1467 0 : NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
1468 0 : nsSVGElement *element = static_cast<nsSVGElement*>(content);
1469 : matrix = element->PrependLocalTransformsTo(matrix,
1470 0 : nsSVGElement::eChildToUserSpace);
1471 : }
1472 0 : bbox = svg->GetBBoxContribution(matrix, aFlags);
1473 : } else {
1474 0 : bbox = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
1475 : }
1476 0 : NS_ASSERTION(bbox.Width() >= 0.0 && bbox.Height() >= 0.0, "Invalid bbox!");
1477 0 : return bbox;
1478 : }
1479 :
1480 : gfxRect
1481 0 : nsSVGUtils::GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH,
1482 : const gfxRect &aBBox, nsIFrame *aFrame)
1483 : {
1484 : float x, y, width, height;
1485 0 : if (aUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
1486 0 : x = aBBox.X() + ObjectSpace(aBBox, &aXYWH[0]);
1487 0 : y = aBBox.Y() + ObjectSpace(aBBox, &aXYWH[1]);
1488 0 : width = ObjectSpace(aBBox, &aXYWH[2]);
1489 0 : height = ObjectSpace(aBBox, &aXYWH[3]);
1490 : } else {
1491 0 : x = UserSpace(aFrame, &aXYWH[0]);
1492 0 : y = UserSpace(aFrame, &aXYWH[1]);
1493 0 : width = UserSpace(aFrame, &aXYWH[2]);
1494 0 : height = UserSpace(aFrame, &aXYWH[3]);
1495 : }
1496 0 : return gfxRect(x, y, width, height);
1497 : }
1498 :
1499 : bool
1500 0 : nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
1501 : {
1502 0 : nsIAtom *type = aFrame->GetType();
1503 0 : if (type != nsGkAtoms::svgImageFrame &&
1504 : type != nsGkAtoms::svgPathGeometryFrame) {
1505 0 : return false;
1506 : }
1507 0 : if (aFrame->GetStyleSVGReset()->mFilter) {
1508 0 : return false;
1509 : }
1510 : // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
1511 0 : if (type == nsGkAtoms::svgImageFrame) {
1512 0 : return true;
1513 : }
1514 0 : const nsStyleSVG *style = aFrame->GetStyleSVG();
1515 0 : if (style->mMarkerStart || style->mMarkerMid || style->mMarkerEnd) {
1516 0 : return false;
1517 : }
1518 0 : if (style->mFill.mType == eStyleSVGPaintType_None ||
1519 : style->mFillOpacity <= 0 ||
1520 0 : !static_cast<nsSVGPathGeometryFrame*>(aFrame)->HasStroke()) {
1521 0 : return true;
1522 : }
1523 0 : return false;
1524 : }
1525 :
1526 : float
1527 0 : nsSVGUtils::MaxExpansion(const gfxMatrix &aMatrix)
1528 : {
1529 : // maximum expansion derivation from
1530 : // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
1531 : // and also implemented in cairo_matrix_transformed_circle_major_axis
1532 0 : double a = aMatrix.xx;
1533 0 : double b = aMatrix.yx;
1534 0 : double c = aMatrix.xy;
1535 0 : double d = aMatrix.yy;
1536 0 : double f = (a * a + b * b + c * c + d * d) / 2;
1537 0 : double g = (a * a + b * b - c * c - d * d) / 2;
1538 0 : double h = a * c + b * d;
1539 0 : return sqrt(f + sqrt(g * g + h * h));
1540 : }
1541 :
1542 : gfxMatrix
1543 0 : nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
1544 : nsSVGEnum *aUnits,
1545 : nsIFrame *aFrame)
1546 : {
1547 0 : if (aFrame &&
1548 0 : aUnits->GetAnimValue() ==
1549 : nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
1550 0 : gfxRect bbox = GetBBox(aFrame);
1551 0 : return gfxMatrix().Scale(bbox.Width(), bbox.Height()) *
1552 0 : gfxMatrix().Translate(gfxPoint(bbox.X(), bbox.Y())) *
1553 0 : aMatrix;
1554 : }
1555 0 : return aMatrix;
1556 : }
1557 :
1558 : nsIFrame*
1559 0 : nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
1560 : {
1561 0 : for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame;
1562 : ancestorFrame = ancestorFrame->GetParent()) {
1563 0 : if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
1564 0 : return ancestorFrame;
1565 : }
1566 : }
1567 0 : return nsnull;
1568 : }
1569 :
1570 : #ifdef DEBUG
1571 : void
1572 0 : nsSVGUtils::WritePPM(const char *fname, gfxImageSurface *aSurface)
1573 : {
1574 0 : FILE *f = fopen(fname, "wb");
1575 0 : if (!f)
1576 0 : return;
1577 :
1578 0 : gfxIntSize size = aSurface->GetSize();
1579 0 : fprintf(f, "P6\n%d %d\n255\n", size.width, size.height);
1580 0 : unsigned char *data = aSurface->Data();
1581 0 : PRInt32 stride = aSurface->Stride();
1582 0 : for (int y=0; y<size.height; y++) {
1583 0 : for (int x=0; x<size.width; x++) {
1584 0 : fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_R, 1, 1, f);
1585 0 : fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_G, 1, 1, f);
1586 0 : fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_B, 1, 1, f);
1587 : }
1588 : }
1589 0 : fclose(f);
1590 : }
1591 : #endif
1592 :
1593 : // The logic here comes from _cairo_stroke_style_max_distance_from_path
1594 : static gfxRect
1595 0 : PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
1596 : nsSVGGeometryFrame* aFrame,
1597 : double styleExpansionFactor,
1598 : const gfxMatrix& aMatrix)
1599 : {
1600 : double style_expansion =
1601 0 : styleExpansionFactor * aFrame->GetStrokeWidth();
1602 :
1603 0 : double dx = style_expansion * (fabs(aMatrix.xx) + fabs(aMatrix.xy));
1604 0 : double dy = style_expansion * (fabs(aMatrix.yy) + fabs(aMatrix.yx));
1605 :
1606 0 : gfxRect strokeExtents = aPathExtents;
1607 0 : strokeExtents.Inflate(dx, dy);
1608 : return strokeExtents;
1609 : }
1610 :
1611 : /*static*/ gfxRect
1612 0 : nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
1613 : nsSVGGeometryFrame* aFrame,
1614 : const gfxMatrix& aMatrix)
1615 : {
1616 0 : return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
1617 : }
1618 :
1619 : /*static*/ gfxRect
1620 0 : nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
1621 : nsSVGPathGeometryFrame* aFrame,
1622 : const gfxMatrix& aMatrix)
1623 : {
1624 0 : double styleExpansionFactor = 0.5;
1625 :
1626 0 : if (static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
1627 0 : const nsStyleSVG* style = aFrame->GetStyleSVG();
1628 :
1629 0 : if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
1630 0 : styleExpansionFactor = M_SQRT1_2;
1631 : }
1632 :
1633 0 : if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
1634 : styleExpansionFactor < style->mStrokeMiterlimit &&
1635 0 : aFrame->GetContent()->Tag() != nsGkAtoms::line) {
1636 0 : styleExpansionFactor = style->mStrokeMiterlimit;
1637 : }
1638 : }
1639 :
1640 : return ::PathExtentsToMaxStrokeExtents(aPathExtents,
1641 : aFrame,
1642 : styleExpansionFactor,
1643 0 : aMatrix);
1644 : }
1645 :
1646 : // ----------------------------------------------------------------------
1647 :
1648 : /* static */ bool
1649 0 : nsSVGUtils::RootSVGElementHasViewbox(const nsIContent *aRootSVGElem)
1650 : {
1651 0 : if (!aRootSVGElem->IsSVG(nsGkAtoms::svg)) {
1652 0 : NS_ABORT_IF_FALSE(false, "Expecting an SVG <svg> node");
1653 0 : return false;
1654 : }
1655 :
1656 : const nsSVGSVGElement *svgSvgElem =
1657 0 : static_cast<const nsSVGSVGElement*>(aRootSVGElem);
1658 :
1659 0 : return svgSvgElem->HasValidViewbox();
1660 : }
1661 :
1662 : /* static */ void
1663 0 : nsSVGUtils::GetFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
1664 : nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
1665 : float *aOpacity, nscolor *color)
1666 : {
1667 0 : const nsStyleSVGPaint &paint = aStyleContext->GetStyleSVG()->*aFillOrStroke;
1668 0 : nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
1669 0 : bool isServer = paint.mType == eStyleSVGPaintType_Server;
1670 0 : *color = isServer ? paint.mFallbackColor : paint.mPaint.mColor;
1671 0 : if (styleIfVisited) {
1672 : const nsStyleSVGPaint &paintIfVisited =
1673 0 : styleIfVisited->GetStyleSVG()->*aFillOrStroke;
1674 : // To prevent Web content from detecting if a user has visited a URL
1675 : // (via URL loading triggered by paint servers or performance
1676 : // differences between paint servers or between a paint server and a
1677 : // color), we do not allow whether links are visited to change which
1678 : // paint server is used or switch between paint servers and simple
1679 : // colors. A :visited style may only override a simple color with
1680 : // another simple color.
1681 0 : if (paintIfVisited.mType == eStyleSVGPaintType_Color &&
1682 : paint.mType == eStyleSVGPaintType_Color) {
1683 0 : nscolor colorIfVisited = paintIfVisited.mPaint.mColor;
1684 0 : nscolor colors[2] = { *color, colorIfVisited };
1685 : *color = nsStyleContext::CombineVisitedColors(colors,
1686 0 : aStyleContext->RelevantLinkVisited());
1687 : }
1688 : }
1689 0 : }
|