Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / svg / base / src / nsSVGUtils.cpp
blobe0f391223fd92b86963d499b0e790ea75b578497
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
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/
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.
15 * The Original Code is the Mozilla SVG project.
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.
21 * Contributor(s):
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.
35 * ***** END LICENSE BLOCK ***** */
37 #include "nsSVGLength.h"
38 #include "nsIDOMDocument.h"
39 #include "nsIDOMSVGElement.h"
40 #include "nsIDOMSVGSVGElement.h"
41 #include "nsStyleCoord.h"
42 #include "nsPresContext.h"
43 #include "nsSVGSVGElement.h"
44 #include "nsIContent.h"
45 #include "nsIDocument.h"
46 #include "nsIFrame.h"
47 #include "nsGkAtoms.h"
48 #include "nsIURI.h"
49 #include "nsStyleStruct.h"
50 #include "nsIPresShell.h"
51 #include "nsSVGUtils.h"
52 #include "nsISVGGlyphFragmentLeaf.h"
53 #include "nsNetUtil.h"
54 #include "nsIDOMSVGRect.h"
55 #include "nsFrameList.h"
56 #include "nsISVGChildFrame.h"
57 #include "nsContentDLF.h"
58 #include "nsContentUtils.h"
59 #include "nsSVGFilterFrame.h"
60 #include "nsINameSpaceManager.h"
61 #include "nsIDOMSVGPoint.h"
62 #include "nsSVGPoint.h"
63 #include "nsDOMError.h"
64 #include "nsSVGOuterSVGFrame.h"
65 #include "nsIDOMSVGAnimPresAspRatio.h"
66 #include "nsIDOMSVGPresAspectRatio.h"
67 #include "nsSVGMatrix.h"
68 #include "nsSVGClipPathFrame.h"
69 #include "nsSVGMaskFrame.h"
70 #include "nsSVGContainerFrame.h"
71 #include "nsSVGLength2.h"
72 #include "nsGenericElement.h"
73 #include "nsAttrValue.h"
74 #include "nsSVGGeometryFrame.h"
75 #include "nsIScriptError.h"
76 #include "gfxContext.h"
77 #include "gfxMatrix.h"
78 #include "gfxRect.h"
79 #include "gfxImageSurface.h"
80 #include "gfxPlatform.h"
81 #include "nsSVGForeignObjectFrame.h"
82 #include "nsIFontMetrics.h"
83 #include "nsIDOMSVGUnitTypes.h"
84 #include "nsSVGRect.h"
85 #include "nsSVGEffects.h"
86 #include "nsSVGIntegrationUtils.h"
87 #include "nsSVGFilterPaintCallback.h"
89 gfxASurface *nsSVGUtils::mThebesComputationalSurface = nsnull;
91 // c = n / 255
92 // (c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1 / 2.4) - 0.055) * 255 + 0.5
93 static const PRUint8 glinearRGBTosRGBMap[256] = {
94 0, 13, 22, 28, 34, 38, 42, 46,
95 50, 53, 56, 59, 61, 64, 66, 69,
96 71, 73, 75, 77, 79, 81, 83, 85,
97 86, 88, 90, 92, 93, 95, 96, 98,
98 99, 101, 102, 104, 105, 106, 108, 109,
99 110, 112, 113, 114, 115, 117, 118, 119,
100 120, 121, 122, 124, 125, 126, 127, 128,
101 129, 130, 131, 132, 133, 134, 135, 136,
102 137, 138, 139, 140, 141, 142, 143, 144,
103 145, 146, 147, 148, 148, 149, 150, 151,
104 152, 153, 154, 155, 155, 156, 157, 158,
105 159, 159, 160, 161, 162, 163, 163, 164,
106 165, 166, 167, 167, 168, 169, 170, 170,
107 171, 172, 173, 173, 174, 175, 175, 176,
108 177, 178, 178, 179, 180, 180, 181, 182,
109 182, 183, 184, 185, 185, 186, 187, 187,
110 188, 189, 189, 190, 190, 191, 192, 192,
111 193, 194, 194, 195, 196, 196, 197, 197,
112 198, 199, 199, 200, 200, 201, 202, 202,
113 203, 203, 204, 205, 205, 206, 206, 207,
114 208, 208, 209, 209, 210, 210, 211, 212,
115 212, 213, 213, 214, 214, 215, 215, 216,
116 216, 217, 218, 218, 219, 219, 220, 220,
117 221, 221, 222, 222, 223, 223, 224, 224,
118 225, 226, 226, 227, 227, 228, 228, 229,
119 229, 230, 230, 231, 231, 232, 232, 233,
120 233, 234, 234, 235, 235, 236, 236, 237,
121 237, 238, 238, 238, 239, 239, 240, 240,
122 241, 241, 242, 242, 243, 243, 244, 244,
123 245, 245, 246, 246, 246, 247, 247, 248,
124 248, 249, 249, 250, 250, 251, 251, 251,
125 252, 252, 253, 253, 254, 254, 255, 255
128 // c = n / 255
129 // c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5
130 static const PRUint8 gsRGBToLinearRGBMap[256] = {
131 0, 0, 0, 0, 0, 0, 0, 1,
132 1, 1, 1, 1, 1, 1, 1, 1,
133 1, 1, 2, 2, 2, 2, 2, 2,
134 2, 2, 3, 3, 3, 3, 3, 3,
135 4, 4, 4, 4, 4, 5, 5, 5,
136 5, 6, 6, 6, 6, 7, 7, 7,
137 8, 8, 8, 8, 9, 9, 9, 10,
138 10, 10, 11, 11, 12, 12, 12, 13,
139 13, 13, 14, 14, 15, 15, 16, 16,
140 17, 17, 17, 18, 18, 19, 19, 20,
141 20, 21, 22, 22, 23, 23, 24, 24,
142 25, 25, 26, 27, 27, 28, 29, 29,
143 30, 30, 31, 32, 32, 33, 34, 35,
144 35, 36, 37, 37, 38, 39, 40, 41,
145 41, 42, 43, 44, 45, 45, 46, 47,
146 48, 49, 50, 51, 51, 52, 53, 54,
147 55, 56, 57, 58, 59, 60, 61, 62,
148 63, 64, 65, 66, 67, 68, 69, 70,
149 71, 72, 73, 74, 76, 77, 78, 79,
150 80, 81, 82, 84, 85, 86, 87, 88,
151 90, 91, 92, 93, 95, 96, 97, 99,
152 100, 101, 103, 104, 105, 107, 108, 109,
153 111, 112, 114, 115, 116, 118, 119, 121,
154 122, 124, 125, 127, 128, 130, 131, 133,
155 134, 136, 138, 139, 141, 142, 144, 146,
156 147, 149, 151, 152, 154, 156, 157, 159,
157 161, 163, 164, 166, 168, 170, 171, 173,
158 175, 177, 179, 181, 183, 184, 186, 188,
159 190, 192, 194, 196, 198, 200, 202, 204,
160 206, 208, 210, 212, 214, 216, 218, 220,
161 222, 224, 226, 229, 231, 233, 235, 237,
162 239, 242, 244, 246, 248, 250, 253, 255
165 static PRBool gSVGEnabled;
166 static const char SVG_PREF_STR[] = "svg.enabled";
168 static int
169 SVGPrefChanged(const char *aPref, void *aClosure)
171 PRBool prefVal = nsContentUtils::GetBoolPref(SVG_PREF_STR);
172 if (prefVal == gSVGEnabled)
173 return 0;
175 gSVGEnabled = prefVal;
176 if (gSVGEnabled)
177 nsContentDLF::RegisterSVG();
178 else
179 nsContentDLF::UnregisterSVG();
181 return 0;
184 PRBool
185 NS_SVGEnabled()
187 static PRBool sInitialized = PR_FALSE;
189 if (!sInitialized) {
190 /* check and register ourselves with the pref */
191 gSVGEnabled = nsContentUtils::GetBoolPref(SVG_PREF_STR);
192 nsContentUtils::RegisterPrefCallback(SVG_PREF_STR, SVGPrefChanged, nsnull);
194 sInitialized = PR_TRUE;
197 return gSVGEnabled;
200 static nsIFrame*
201 GetFrameForContent(nsIContent* aContent)
203 if (!aContent)
204 return nsnull;
206 nsIDocument *doc = aContent->GetCurrentDoc();
207 if (!doc)
208 return nsnull;
210 return nsGenericElement::GetPrimaryFrameFor(aContent, doc);
213 float
214 nsSVGUtils::GetFontSize(nsIContent *aContent)
216 nsIFrame* frame = GetFrameForContent(aContent);
217 if (!frame) {
218 NS_WARNING("no frame in GetFontSize()");
219 return 1.0f;
222 return GetFontSize(frame);
225 float
226 nsSVGUtils::GetFontSize(nsIFrame *aFrame)
228 return nsPresContext::AppUnitsToFloatCSSPixels(aFrame->GetStyleFont()->mSize) /
229 aFrame->PresContext()->TextZoom();
232 float
233 nsSVGUtils::GetFontXHeight(nsIContent *aContent)
235 nsIFrame* frame = GetFrameForContent(aContent);
236 if (!frame) {
237 NS_WARNING("no frame in GetFontXHeight()");
238 return 1.0f;
241 return GetFontXHeight(frame);
244 float
245 nsSVGUtils::GetFontXHeight(nsIFrame *aFrame)
247 nsCOMPtr<nsIFontMetrics> fontMetrics;
248 nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fontMetrics));
250 if (!fontMetrics) {
251 NS_WARNING("no FontMetrics in GetFontXHeight()");
252 return 1.0f;
255 nscoord xHeight;
256 fontMetrics->GetXHeight(xHeight);
257 return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
258 aFrame->PresContext()->TextZoom();
261 void
262 nsSVGUtils::UnPremultiplyImageDataAlpha(PRUint8 *data,
263 PRInt32 stride,
264 const nsIntRect &rect)
266 for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
267 for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
268 PRUint8 *pixel = data + stride * y + 4 * x;
270 PRUint8 a = pixel[GFX_ARGB32_OFFSET_A];
271 if (a == 255)
272 continue;
274 if (a) {
275 pixel[GFX_ARGB32_OFFSET_B] = (255 * pixel[GFX_ARGB32_OFFSET_B]) / a;
276 pixel[GFX_ARGB32_OFFSET_G] = (255 * pixel[GFX_ARGB32_OFFSET_G]) / a;
277 pixel[GFX_ARGB32_OFFSET_R] = (255 * pixel[GFX_ARGB32_OFFSET_R]) / a;
278 } else {
279 pixel[GFX_ARGB32_OFFSET_B] = 0;
280 pixel[GFX_ARGB32_OFFSET_G] = 0;
281 pixel[GFX_ARGB32_OFFSET_R] = 0;
287 void
288 nsSVGUtils::PremultiplyImageDataAlpha(PRUint8 *data,
289 PRInt32 stride,
290 const nsIntRect &rect)
292 for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
293 for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
294 PRUint8 *pixel = data + stride * y + 4 * x;
296 PRUint8 a = pixel[GFX_ARGB32_OFFSET_A];
297 if (a == 255)
298 continue;
300 FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_B],
301 pixel[GFX_ARGB32_OFFSET_B] * a);
302 FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_G],
303 pixel[GFX_ARGB32_OFFSET_G] * a);
304 FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_R],
305 pixel[GFX_ARGB32_OFFSET_R] * a);
310 void
311 nsSVGUtils::ConvertImageDataToLinearRGB(PRUint8 *data,
312 PRInt32 stride,
313 const nsIntRect &rect)
315 for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
316 for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
317 PRUint8 *pixel = data + stride * y + 4 * x;
319 pixel[GFX_ARGB32_OFFSET_B] =
320 gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_B]];
321 pixel[GFX_ARGB32_OFFSET_G] =
322 gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_G]];
323 pixel[GFX_ARGB32_OFFSET_R] =
324 gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_R]];
329 void
330 nsSVGUtils::ConvertImageDataFromLinearRGB(PRUint8 *data,
331 PRInt32 stride,
332 const nsIntRect &rect)
334 for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
335 for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
336 PRUint8 *pixel = data + stride * y + 4 * x;
338 pixel[GFX_ARGB32_OFFSET_B] =
339 glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_B]];
340 pixel[GFX_ARGB32_OFFSET_G] =
341 glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_G]];
342 pixel[GFX_ARGB32_OFFSET_R] =
343 glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_R]];
348 nsresult
349 nsSVGUtils::ReportToConsole(nsIDocument* doc,
350 const char* aWarning,
351 const PRUnichar **aParams,
352 PRUint32 aParamsLength)
354 return nsContentUtils::ReportToConsole(nsContentUtils::eSVG_PROPERTIES,
355 aWarning,
356 aParams, aParamsLength,
357 doc ? doc->GetDocumentURI() : nsnull,
358 EmptyString(), 0, 0,
359 nsIScriptError::warningFlag,
360 "SVG");
363 float
364 nsSVGUtils::CoordToFloat(nsPresContext *aPresContext,
365 nsSVGElement *aContent,
366 const nsStyleCoord &aCoord)
368 float val = 0.0f;
370 switch (aCoord.GetUnit()) {
371 case eStyleUnit_Factor:
372 // user units
373 val = aCoord.GetFactorValue();
374 break;
376 case eStyleUnit_Coord:
377 val = nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue());
378 break;
380 case eStyleUnit_Percent: {
381 nsCOMPtr<nsISVGLength> length;
382 NS_NewSVGLength(getter_AddRefs(length),
383 aCoord.GetPercentValue() * 100.0f,
384 nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE);
386 if (!length)
387 break;
389 nsWeakPtr weakCtx =
390 do_GetWeakReference(static_cast<nsGenericElement*>(aContent));
391 length->SetContext(weakCtx, nsSVGUtils::XY);
392 length->GetValue(&val);
393 break;
395 default:
396 break;
399 return val;
402 nsresult
403 nsSVGUtils::GetNearestViewportElement(nsIContent *aContent,
404 nsIDOMSVGElement * *aNearestViewportElement)
406 *aNearestViewportElement = nsnull;
408 nsBindingManager *bindingManager = nsnull;
409 // XXXbz I _think_ this is right. We want to be using the binding manager
410 // that would have attached the bindings that gives us our anonymous
411 // ancestors. That's the binding manager for the document we actually belong
412 // to, which is our owner doc.
413 nsIDocument* ownerDoc = aContent->GetOwnerDoc();
414 if (ownerDoc) {
415 bindingManager = ownerDoc->BindingManager();
418 nsCOMPtr<nsIContent> element = aContent;
419 nsCOMPtr<nsIContent> ancestor;
420 unsigned short ancestorCount = 0;
422 while (1) {
424 ancestor = nsnull;
425 if (bindingManager) {
426 // check for an anonymous ancestor first
427 ancestor = bindingManager->GetInsertionParent(element);
429 if (!ancestor) {
430 // if we didn't find an anonymous ancestor, use the explicit one
431 ancestor = element->GetParent();
434 nsCOMPtr<nsIDOMSVGFitToViewBox> fitToViewBox = do_QueryInterface(element);
436 if (fitToViewBox && (ancestor || ancestorCount)) {
437 // right interface and not the outermost SVG element
438 nsCOMPtr<nsIDOMSVGElement> SVGElement = do_QueryInterface(element);
439 SVGElement.swap(*aNearestViewportElement);
440 return NS_OK;
443 if (!ancestor) {
444 // reached the top of our parent chain
445 break;
448 element = ancestor;
449 ancestorCount++;
452 return NS_OK;
455 nsresult
456 nsSVGUtils::GetFarthestViewportElement(nsIContent *aContent,
457 nsIDOMSVGElement * *aFarthestViewportElement)
459 *aFarthestViewportElement = nsnull;
461 nsBindingManager *bindingManager = nsnull;
462 // XXXbz I _think_ this is right. We want to be using the binding manager
463 // that would have attached the bindings that gives us our anonymous
464 // ancestors. That's the binding manager for the document we actually belong
465 // to, which is our owner doc.
466 nsIDocument* ownerDoc = aContent->GetOwnerDoc();
467 if (ownerDoc) {
468 bindingManager = ownerDoc->BindingManager();
471 nsCOMPtr<nsIContent> element = aContent;
472 nsCOMPtr<nsIContent> ancestor;
473 nsCOMPtr<nsIDOMSVGElement> SVGElement;
474 unsigned short ancestorCount = 0;
476 while (1) {
478 ancestor = nsnull;
479 if (bindingManager) {
480 // check for an anonymous ancestor first
481 ancestor = bindingManager->GetInsertionParent(element);
483 if (!ancestor) {
484 // if we didn't find an anonymous ancestor, use the explicit one
485 ancestor = element->GetParent();
488 nsCOMPtr<nsIDOMSVGFitToViewBox> fitToViewBox = do_QueryInterface(element);
490 if (fitToViewBox) {
491 // right interface
492 SVGElement = do_QueryInterface(element);
495 if (!ancestor) {
496 // reached the top of our parent chain
497 break;
500 element = ancestor;
501 ancestorCount++;
504 if (ancestorCount == 0 || !SVGElement) {
505 // outermost SVG element or no viewport found
506 return NS_OK;
509 SVGElement.swap(*aFarthestViewportElement);
510 return NS_OK;
513 nsresult
514 nsSVGUtils::GetBBox(nsFrameList *aFrames, nsIDOMSVGRect **_retval)
516 *_retval = nsnull;
518 float minx, miny, maxx, maxy;
519 minx = miny = FLT_MAX;
520 maxx = maxy = -1.0 * FLT_MAX;
522 nsCOMPtr<nsIDOMSVGRect> unionRect;
524 nsIFrame* kid = aFrames->FirstChild();
525 while (kid) {
526 nsISVGChildFrame* SVGFrame = nsnull;
527 CallQueryInterface(kid, &SVGFrame);
528 if (SVGFrame) {
529 nsCOMPtr<nsIDOMSVGRect> box;
530 SVGFrame->GetBBox(getter_AddRefs(box));
532 if (box) {
533 float bminx, bminy, bmaxx, bmaxy, width, height;
534 box->GetX(&bminx);
535 box->GetY(&bminy);
536 box->GetWidth(&width);
537 box->GetHeight(&height);
538 bmaxx = bminx+width;
539 bmaxy = bminy+height;
541 if (!unionRect)
542 unionRect = box;
543 minx = PR_MIN(minx, bminx);
544 miny = PR_MIN(miny, bminy);
545 maxx = PR_MAX(maxx, bmaxx);
546 maxy = PR_MAX(maxy, bmaxy);
549 kid = kid->GetNextSibling();
552 if (unionRect) {
553 unionRect->SetX(minx);
554 unionRect->SetY(miny);
555 unionRect->SetWidth(maxx - minx);
556 unionRect->SetHeight(maxy - miny);
557 *_retval = unionRect;
558 NS_ADDREF(*_retval);
559 return NS_OK;
562 return NS_ERROR_FAILURE;
565 nsRect
566 nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect)
568 PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
569 nsRect rect = aRect;
570 rect.ScaleRoundOutInverse(appUnitsPerDevPixel);
572 while (aFrame) {
573 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
574 break;
576 nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame);
577 if (filter) {
578 rect = filter->GetInvalidationBBox(aFrame, rect);
580 aFrame = aFrame->GetParent();
583 rect.ScaleRoundOut(appUnitsPerDevPixel);
584 return rect;
587 void
588 nsSVGUtils::InvalidateCoveredRegion(nsIFrame *aFrame)
590 if (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
591 return;
593 nsSVGOuterSVGFrame* outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(aFrame);
594 NS_ASSERTION(outerSVGFrame, "no outer svg frame");
595 if (outerSVGFrame)
596 outerSVGFrame->InvalidateCoveredRegion(aFrame);
599 void
600 nsSVGUtils::UpdateGraphic(nsISVGChildFrame *aSVGFrame)
602 nsIFrame *frame;
603 CallQueryInterface(aSVGFrame, &frame);
605 nsSVGEffects::InvalidateRenderingObservers(frame);
607 if (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
608 return;
610 nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(frame);
611 if (!outerSVGFrame) {
612 NS_ERROR("null outerSVGFrame");
613 return;
616 if (outerSVGFrame->IsRedrawSuspended()) {
617 frame->AddStateBits(NS_STATE_SVG_DIRTY);
618 } else {
619 frame->RemoveStateBits(NS_STATE_SVG_DIRTY);
621 PRBool changed = outerSVGFrame->UpdateAndInvalidateCoveredRegion(frame);
622 if (changed) {
623 NotifyAncestorsOfFilterRegionChange(frame);
628 void
629 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
631 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
632 // It would be better if we couldn't get here
633 return;
636 aFrame = aFrame->GetParent();
638 while (aFrame) {
639 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
640 return;
642 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
643 if (property) {
644 property->Invalidate();
646 aFrame = aFrame->GetParent();
650 double
651 nsSVGUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight)
653 return sqrt((aWidth*aWidth + aHeight*aHeight)/2);
656 float
657 nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, const nsSVGLength2 *aLength)
659 float fraction, axis;
661 switch (aLength->GetCtxType()) {
662 case X:
663 aRect->GetWidth(&axis);
664 break;
665 case Y:
666 aRect->GetHeight(&axis);
667 break;
668 case XY:
670 float width, height;
671 aRect->GetWidth(&width);
672 aRect->GetHeight(&height);
673 axis = float(ComputeNormalizedHypotenuse(width, height));
677 if (aLength->IsPercentage()) {
678 fraction = aLength->GetAnimValInSpecifiedUnits() / 100;
679 } else
680 fraction = aLength->GetAnimValue(static_cast<nsSVGSVGElement*>
681 (nsnull));
683 return fraction * axis;
686 float
687 nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
689 return aLength->GetAnimValue(aSVGElement);
692 float
693 nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
695 return aLength->GetAnimValue(aNonSVGContext);
698 void
699 nsSVGUtils::TransformPoint(nsIDOMSVGMatrix *matrix,
700 float *x, float *y)
702 nsCOMPtr<nsIDOMSVGPoint> point;
703 NS_NewSVGPoint(getter_AddRefs(point), *x, *y);
704 if (!point)
705 return;
707 nsCOMPtr<nsIDOMSVGPoint> xfpoint;
708 point->MatrixTransform(matrix, getter_AddRefs(xfpoint));
709 if (!xfpoint)
710 return;
712 xfpoint->GetX(x);
713 xfpoint->GetY(y);
716 float
717 nsSVGUtils::AngleBisect(float a1, float a2)
719 float delta = fmod(a2 - a1, static_cast<float>(2*M_PI));
720 if (delta < 0) {
721 delta += 2*M_PI;
723 /* delta is now the angle from a1 around to a2, in the range [0, 2*M_PI) */
724 float r = a1 + delta/2;
725 if (delta >= M_PI) {
726 /* the arc from a2 to a1 is smaller, so use the ray on that side */
727 r += M_PI;
729 return r;
732 nsSVGOuterSVGFrame *
733 nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
735 while (aFrame) {
736 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
737 return static_cast<nsSVGOuterSVGFrame*>(aFrame);
739 aFrame = aFrame->GetParent();
742 return nsnull;
745 nsIFrame*
746 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
748 nsISVGChildFrame* svg;
749 CallQueryInterface(aFrame, &svg);
750 if (!svg)
751 return nsnull;
752 *aRect = svg->GetCoveredRegion();
753 return GetOuterSVGFrame(aFrame);
756 already_AddRefed<nsIDOMSVGMatrix>
757 nsSVGUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
758 float aViewboxX, float aViewboxY,
759 float aViewboxWidth, float aViewboxHeight,
760 nsIDOMSVGAnimatedPreserveAspectRatio *aPreserveAspectRatio,
761 PRBool aIgnoreAlign)
763 NS_ASSERTION(aViewboxWidth > 0, "viewBox width must be greater than zero!");
764 NS_ASSERTION(aViewboxHeight > 0, "viewBox height must be greater than zero!");
766 PRUint16 align, meetOrSlice;
768 nsCOMPtr<nsIDOMSVGPreserveAspectRatio> par;
769 aPreserveAspectRatio->GetAnimVal(getter_AddRefs(par));
770 NS_ASSERTION(par, "could not get preserveAspectRatio");
771 par->GetAlign(&align);
772 par->GetMeetOrSlice(&meetOrSlice);
775 // default to the defaults
776 if (align == nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_UNKNOWN)
777 align = nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID;
778 if (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN)
779 meetOrSlice = nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET;
781 // alignment disabled for this matrix setup
782 if (aIgnoreAlign)
783 align = nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMIN;
785 float a, d, e, f;
786 a = aViewportWidth / aViewboxWidth;
787 d = aViewportHeight / aViewboxHeight;
788 e = 0.0f;
789 f = 0.0f;
791 if (align != nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE &&
792 a != d) {
793 if ((meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET &&
794 a < d) ||
795 (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE &&
796 d < a)) {
797 d = a;
798 switch (align) {
799 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMIN:
800 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
801 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
802 break;
803 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
804 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
805 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
806 f = (aViewportHeight - a * aViewboxHeight) / 2.0f;
807 break;
808 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
809 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
810 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
811 f = aViewportHeight - a * aViewboxHeight;
812 break;
813 default:
814 NS_NOTREACHED("Unknown value for align");
817 else if (
818 (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET &&
819 d < a) ||
820 (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE &&
821 a < d)) {
822 a = d;
823 switch (align) {
824 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMIN:
825 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
826 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
827 break;
828 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
829 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
830 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
831 e = (aViewportWidth - a * aViewboxWidth) / 2.0f;
832 break;
833 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
834 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
835 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
836 e = aViewportWidth - a * aViewboxWidth;
837 break;
838 default:
839 NS_NOTREACHED("Unknown value for align");
842 else NS_NOTREACHED("Unknown value for meetOrSlice");
845 if (aViewboxX) e += -a * aViewboxX;
846 if (aViewboxY) f += -d * aViewboxY;
848 nsIDOMSVGMatrix *retval;
849 NS_NewSVGMatrix(&retval, a, 0.0f, 0.0f, d, e, f);
850 return retval;
854 // This is ugly and roc will want to kill me...
856 already_AddRefed<nsIDOMSVGMatrix>
857 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
859 if (!aFrame->IsFrameOfType(nsIFrame::eSVG))
860 return nsSVGIntegrationUtils::GetInitialMatrix(aFrame);
862 if (!aFrame->IsLeaf()) {
863 // foreignObject is the one non-leaf svg frame that isn't a SVGContainer
864 if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
865 nsSVGForeignObjectFrame *foreignFrame =
866 static_cast<nsSVGForeignObjectFrame*>(aFrame);
867 return foreignFrame->GetCanvasTM();
869 nsSVGContainerFrame *containerFrame = static_cast<nsSVGContainerFrame*>
870 (aFrame);
871 return containerFrame->GetCanvasTM();
874 nsSVGGeometryFrame *geometryFrame = static_cast<nsSVGGeometryFrame*>
875 (aFrame);
876 nsCOMPtr<nsIDOMSVGMatrix> matrix;
877 nsIDOMSVGMatrix *retval;
878 geometryFrame->GetCanvasTM(getter_AddRefs(matrix));
879 retval = matrix.get();
880 NS_IF_ADDREF(retval);
881 return retval;
884 void
885 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, PRUint32 aFlags)
887 nsIFrame *aKid = aFrame->GetFirstChild(nsnull);
889 while (aKid) {
890 nsISVGChildFrame* SVGFrame = nsnull;
891 CallQueryInterface(aKid, &SVGFrame);
892 if (SVGFrame) {
893 SVGFrame->NotifySVGChanged(aFlags);
894 } else {
895 NS_ASSERTION(aKid->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
896 // recurse into the children of container frames e.g. <clipPath>, <mask>
897 // in case they have child frames with transformation matrices
898 nsSVGUtils::NotifyChildrenOfSVGChange(aKid, aFlags);
900 aKid = aKid->GetNextSibling();
904 void
905 nsSVGUtils::AddObserver(nsISupports *aObserver, nsISupports *aTarget)
907 nsISVGValueObserver *observer = nsnull;
908 nsISVGValue *v = nsnull;
909 CallQueryInterface(aObserver, &observer);
910 CallQueryInterface(aTarget, &v);
911 if (observer && v)
912 v->AddObserver(observer);
915 void
916 nsSVGUtils::RemoveObserver(nsISupports *aObserver, nsISupports *aTarget)
918 nsISVGValueObserver *observer = nsnull;
919 nsISVGValue *v = nsnull;
920 CallQueryInterface(aObserver, &observer);
921 CallQueryInterface(aTarget, &v);
922 if (observer && v)
923 v->RemoveObserver(observer);
926 // ************************************************************
928 class SVGPaintCallback : public nsSVGFilterPaintCallback
930 public:
931 virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget,
932 const nsIntRect* aDirtyRect)
934 nsISVGChildFrame *svgChildFrame;
935 CallQueryInterface(aTarget, &svgChildFrame);
936 NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
937 NS_ASSERTION(!svgChildFrame->GetMatrixPropagation(),
938 "This should have been set to false already");
940 nsIntRect* dirtyRect = nsnull;
941 nsIntRect tmpDirtyRect;
943 // aDirtyRect is in user-space pixels, we need to convert to
944 // outer-SVG-frame-relative device pixels.
945 if (aDirtyRect) {
946 // Temporarily set SetMatrixPropagation so we can find out what
947 // the actual CTM is.
948 svgChildFrame->SetMatrixPropagation(PR_TRUE);
949 nsCOMPtr<nsIDOMSVGMatrix> ctm = nsSVGUtils::GetCanvasTM(aTarget);
950 NS_ASSERTION(ctm, "graphic source didn't specify a ctm");
951 svgChildFrame->SetMatrixPropagation(PR_FALSE);
953 gfxMatrix matrix = nsSVGUtils::ConvertSVGMatrixToThebes(ctm);
954 gfxRect dirtyBounds = matrix.TransformBounds(
955 gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
956 dirtyBounds.RoundOut();
957 if (NS_SUCCEEDED(nsSVGUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect))) {
958 dirtyRect = &tmpDirtyRect;
962 svgChildFrame->PaintSVG(aContext, dirtyRect);
966 void
967 nsSVGUtils::PaintFrameWithEffects(nsSVGRenderState *aContext,
968 const nsIntRect *aDirtyRect,
969 nsIFrame *aFrame)
971 nsISVGChildFrame *svgChildFrame;
972 CallQueryInterface(aFrame, &svgChildFrame);
974 if (!svgChildFrame)
975 return;
977 float opacity = aFrame->GetStyleDisplay()->mOpacity;
978 if (opacity == 0.0f)
979 return;
981 /* Properties are added lazily and may have been removed by a restyle,
982 so make sure all applicable ones are set again. */
984 nsSVGEffects::EffectProperties effectProperties =
985 nsSVGEffects::GetEffectProperties(aFrame);
987 PRBool isOK = PR_TRUE;
988 nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
990 /* Check if we need to draw anything. HasValidCoveredRect only returns
991 * true for path geometry and glyphs, so basically we're traversing
992 * all containers and we can only skip leaves here.
994 if (aDirtyRect && svgChildFrame->HasValidCoveredRect()) {
995 if (filterFrame) {
996 if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull)))
997 return;
998 } else {
999 nsRect rect = *aDirtyRect;
1000 rect.ScaleRoundOut(aFrame->PresContext()->AppUnitsPerDevPixel());
1001 if (!rect.Intersects(aFrame->GetRect()))
1002 return;
1006 /* SVG defines the following rendering model:
1008 * 1. Render geometry
1009 * 2. Apply filter
1010 * 3. Apply clipping, masking, group opacity
1012 * We follow this, but perform a couple of optimizations:
1014 * + Use cairo's clipPath when representable natively (single object
1015 * clip region).
1017 * + Merge opacity and masking if both used together.
1020 if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
1021 opacity = 1.0f;
1023 gfxContext *gfx = aContext->GetGfxContext();
1024 PRBool complexEffects = PR_FALSE;
1026 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
1027 nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
1029 PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;
1031 if (!isOK) {
1032 // Some resource is missing. We shouldn't paint anything.
1033 return;
1036 nsCOMPtr<nsIDOMSVGMatrix> matrix =
1037 (clipPathFrame || maskFrame) ? GetCanvasTM(aFrame) : nsnull;
1039 /* Check if we need to do additional operations on this child's
1040 * rendering, which necessitates rendering into another surface. */
1041 if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
1042 complexEffects = PR_TRUE;
1043 gfx->Save();
1044 gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1047 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
1048 * we can just do normal painting and get it clipped appropriately.
1050 if (clipPathFrame && isTrivialClip) {
1051 gfx->Save();
1052 clipPathFrame->ClipPaint(aContext, aFrame, matrix);
1055 /* Paint the child */
1056 if (filterFrame) {
1057 SVGPaintCallback paintCallback;
1058 filterFrame->FilterPaint(aContext, aFrame, &paintCallback, aDirtyRect);
1059 } else {
1060 svgChildFrame->PaintSVG(aContext, aDirtyRect);
1063 if (clipPathFrame && isTrivialClip) {
1064 gfx->Restore();
1067 /* No more effects, we're done. */
1068 if (!complexEffects)
1069 return;
1071 gfx->PopGroupToSource();
1073 nsRefPtr<gfxPattern> maskSurface =
1074 maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
1075 matrix, opacity) : nsnull;
1077 nsRefPtr<gfxPattern> clipMaskSurface;
1078 if (clipPathFrame && !isTrivialClip) {
1079 gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1081 nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
1082 clipMaskSurface = gfx->PopGroup();
1084 if (NS_SUCCEEDED(rv) && clipMaskSurface) {
1085 // Still more set after clipping, so clip to another surface
1086 if (maskSurface || opacity != 1.0f) {
1087 gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1088 gfx->Mask(clipMaskSurface);
1089 gfx->PopGroupToSource();
1090 } else {
1091 gfx->Mask(clipMaskSurface);
1096 if (maskSurface) {
1097 gfx->Mask(maskSurface);
1098 } else if (opacity != 1.0f) {
1099 gfx->Paint(opacity);
1102 gfx->Restore();
1105 PRBool
1106 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
1108 nsSVGEffects::EffectProperties props =
1109 nsSVGEffects::GetEffectProperties(aFrame);
1110 if (!props.mClipPath)
1111 return PR_TRUE;
1113 nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(nsnull);
1114 if (!clipPathFrame) {
1115 // clipPath is not a valid resource, so nothing gets painted, so
1116 // hit-testing must fail.
1117 return PR_FALSE;
1120 nsCOMPtr<nsIDOMSVGMatrix> matrix = GetCanvasTM(aFrame);
1121 return clipPathFrame->ClipHitTest(aFrame, matrix, aPoint);
1124 nsIFrame *
1125 nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
1127 // XXX: The frame's children are linked in a singly-linked list in document
1128 // order. If we were to hit test the children in this order we would need to
1129 // hit test *every* SVG frame, since even if we get a hit, later SVG frames
1130 // may lie on top of the matching frame. We really want to traverse SVG
1131 // frames in reverse order so we can stop at the first match. Since we don't
1132 // have a doubly-linked list, for the time being we traverse the
1133 // singly-linked list backwards by first reversing the nextSibling pointers
1134 // in place, and then restoring them when done.
1136 // Note: While the child list pointers are reversed, any method which walks
1137 // the list would only encounter a single child!
1139 nsIFrame* current = nsnull;
1140 nsIFrame* next = aFrame->GetFirstChild(nsnull);
1142 nsIFrame* result = nsnull;
1144 // reverse sibling pointers
1145 while (next) {
1146 nsIFrame* temp = next->GetNextSibling();
1147 next->SetNextSibling(current);
1148 current = next;
1149 next = temp;
1152 // now do the backwards traversal
1153 while (current) {
1154 nsISVGChildFrame* SVGFrame;
1155 CallQueryInterface(current, &SVGFrame);
1156 if (SVGFrame) {
1157 result = SVGFrame->GetFrameForPoint(aPoint);
1158 if (result)
1159 break;
1161 // restore current frame's sibling pointer
1162 nsIFrame* temp = current->GetNextSibling();
1163 current->SetNextSibling(next);
1164 next = current;
1165 current = temp;
1168 // restore remaining pointers
1169 while (current) {
1170 nsIFrame* temp = current->GetNextSibling();
1171 current->SetNextSibling(next);
1172 next = current;
1173 current = temp;
1176 if (result && !HitTestClip(aFrame, aPoint))
1177 result = nsnull;
1179 return result;
1182 nsRect
1183 nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
1185 nsRect rect;
1187 for (nsIFrame* kid = aFrames.FirstChild();
1188 kid;
1189 kid = kid->GetNextSibling()) {
1190 nsISVGChildFrame* child = nsnull;
1191 CallQueryInterface(kid, &child);
1192 if (child) {
1193 nsRect childRect = child->GetCoveredRegion();
1194 rect.UnionRect(rect, childRect);
1198 return rect;
1201 nsRect
1202 nsSVGUtils::ToAppPixelRect(nsPresContext *aPresContext,
1203 double xmin, double ymin,
1204 double xmax, double ymax)
1206 return ToAppPixelRect(aPresContext,
1207 gfxRect(xmin, ymin, xmax - xmin, ymax - ymin));
1210 nsRect
1211 nsSVGUtils::ToAppPixelRect(nsPresContext *aPresContext, const gfxRect& rect)
1213 return nsRect(aPresContext->DevPixelsToAppUnits(NSToIntFloor(rect.X())),
1214 aPresContext->DevPixelsToAppUnits(NSToIntFloor(rect.Y())),
1215 aPresContext->DevPixelsToAppUnits(NSToIntCeil(rect.XMost()) - NSToIntFloor(rect.X())),
1216 aPresContext->DevPixelsToAppUnits(NSToIntCeil(rect.YMost()) - NSToIntFloor(rect.Y())));
1219 gfxIntSize
1220 nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize, PRBool *aResultOverflows)
1222 gfxIntSize surfaceSize =
1223 gfxIntSize(PRInt32(aSize.width + 0.5), PRInt32(aSize.height + 0.5));
1225 *aResultOverflows = (aSize.width >= PR_INT32_MAX + 0.5 ||
1226 aSize.height >= PR_INT32_MAX + 0.5 ||
1227 aSize.width <= PR_INT32_MIN - 0.5 ||
1228 aSize.height <= PR_INT32_MIN - 0.5);
1230 if (*aResultOverflows ||
1231 !gfxASurface::CheckSurfaceSize(surfaceSize)) {
1232 surfaceSize.width = PR_MIN(NS_SVG_OFFSCREEN_MAX_DIMENSION,
1233 surfaceSize.width);
1234 surfaceSize.height = PR_MIN(NS_SVG_OFFSCREEN_MAX_DIMENSION,
1235 surfaceSize.height);
1236 *aResultOverflows = PR_TRUE;
1238 return surfaceSize;
1241 gfxASurface *
1242 nsSVGUtils::GetThebesComputationalSurface()
1244 if (!mThebesComputationalSurface) {
1245 nsRefPtr<gfxImageSurface> surface =
1246 new gfxImageSurface(gfxIntSize(1, 1), gfxASurface::ImageFormatARGB32);
1247 NS_ASSERTION(surface && !surface->CairoStatus(),
1248 "Could not create offscreen surface");
1249 mThebesComputationalSurface = surface;
1250 // we want to keep this surface around
1251 NS_IF_ADDREF(mThebesComputationalSurface);
1254 return mThebesComputationalSurface;
1257 gfxMatrix
1258 nsSVGUtils::ConvertSVGMatrixToThebes(nsIDOMSVGMatrix *aMatrix)
1260 float A, B, C, D, E, F;
1261 aMatrix->GetA(&A);
1262 aMatrix->GetB(&B);
1263 aMatrix->GetC(&C);
1264 aMatrix->GetD(&D);
1265 aMatrix->GetE(&E);
1266 aMatrix->GetF(&F);
1267 return gfxMatrix(A, B, C, D, E, F);
1270 PRBool
1271 nsSVGUtils::HitTestRect(nsIDOMSVGMatrix *aMatrix,
1272 float aRX, float aRY, float aRWidth, float aRHeight,
1273 float aX, float aY)
1275 PRBool result = PR_TRUE;
1277 if (aMatrix) {
1278 gfxContext ctx(GetThebesComputationalSurface());
1279 ctx.SetMatrix(ConvertSVGMatrixToThebes(aMatrix));
1281 ctx.NewPath();
1282 ctx.Rectangle(gfxRect(aRX, aRY, aRWidth, aRHeight));
1283 ctx.IdentityMatrix();
1285 if (!ctx.PointInFill(gfxPoint(aX, aY)))
1286 result = PR_FALSE;
1289 return result;
1292 void
1293 nsSVGUtils::CompositeSurfaceMatrix(gfxContext *aContext,
1294 gfxASurface *aSurface,
1295 nsIDOMSVGMatrix *aCTM, float aOpacity)
1297 gfxMatrix matrix = ConvertSVGMatrixToThebes(aCTM);
1298 if (matrix.IsSingular())
1299 return;
1301 aContext->Save();
1303 aContext->Multiply(matrix);
1305 aContext->SetSource(aSurface);
1306 aContext->Paint(aOpacity);
1308 aContext->Restore();
1311 void
1312 nsSVGUtils::CompositePatternMatrix(gfxContext *aContext,
1313 gfxPattern *aPattern,
1314 nsIDOMSVGMatrix *aCTM, float aWidth, float aHeight, float aOpacity)
1316 gfxMatrix matrix = ConvertSVGMatrixToThebes(aCTM);
1317 if (matrix.IsSingular())
1318 return;
1320 aContext->Save();
1322 SetClipRect(aContext, aCTM, 0, 0, aWidth, aHeight);
1324 aContext->Multiply(matrix);
1326 aContext->SetPattern(aPattern);
1327 aContext->Paint(aOpacity);
1329 aContext->Restore();
1332 void
1333 nsSVGUtils::SetClipRect(gfxContext *aContext,
1334 nsIDOMSVGMatrix *aCTM, float aX, float aY,
1335 float aWidth, float aHeight)
1337 gfxMatrix matrix = ConvertSVGMatrixToThebes(aCTM);
1338 if (matrix.IsSingular())
1339 return;
1341 gfxMatrix oldMatrix = aContext->CurrentMatrix();
1342 aContext->Multiply(matrix);
1343 aContext->Clip(gfxRect(aX, aY, aWidth, aHeight));
1344 aContext->SetMatrix(oldMatrix);
1347 void
1348 nsSVGUtils::ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfxRect)
1350 gfxRect r = aGfxRect;
1351 r.RoundOut();
1352 gfxRect r2(aRect->x, aRect->y, aRect->width, aRect->height);
1353 r = r.Intersect(r2);
1354 *aRect = nsIntRect(PRInt32(r.X()), PRInt32(r.Y()),
1355 PRInt32(r.Width()), PRInt32(r.Height()));
1358 nsresult
1359 nsSVGUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
1361 *aOut = nsIntRect(PRInt32(aIn.X()), PRInt32(aIn.Y()),
1362 PRInt32(aIn.Width()), PRInt32(aIn.Height()));
1363 return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height) == aIn
1364 ? NS_OK : NS_ERROR_FAILURE;
1367 already_AddRefed<nsIDOMSVGRect>
1368 nsSVGUtils::GetBBox(nsIFrame *aFrame)
1370 nsISVGChildFrame *svg;
1371 CallQueryInterface(aFrame, &svg);
1372 if (!svg) {
1373 nsIDOMSVGRect *rect = nsnull;
1374 gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
1375 NS_NewSVGRect(&rect, r);
1376 return rect;
1379 PRBool needToDisablePropagation = svg->GetMatrixPropagation();
1380 if (needToDisablePropagation) {
1381 svg->SetMatrixPropagation(PR_FALSE);
1382 svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
1383 nsISVGChildFrame::TRANSFORM_CHANGED);
1386 nsCOMPtr<nsIDOMSVGRect> bbox;
1387 svg->GetBBox(getter_AddRefs(bbox));
1389 if (needToDisablePropagation) {
1390 svg->SetMatrixPropagation(PR_TRUE);
1391 svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
1392 nsISVGChildFrame::TRANSFORM_CHANGED);
1395 return bbox.forget();
1398 gfxRect
1399 nsSVGUtils::GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH,
1400 nsIDOMSVGRect *aBBox, nsIFrame *aFrame)
1402 float x, y, width, height;
1403 if (aUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
1404 aBBox->GetX(&x);
1405 x += ObjectSpace(aBBox, &aXYWH[0]);
1406 aBBox->GetY(&y);
1407 y += ObjectSpace(aBBox, &aXYWH[1]);
1408 width = ObjectSpace(aBBox, &aXYWH[2]);
1409 height = ObjectSpace(aBBox, &aXYWH[3]);
1410 } else {
1411 x = nsSVGUtils::UserSpace(aFrame, &aXYWH[0]);
1412 y = nsSVGUtils::UserSpace(aFrame, &aXYWH[1]);
1413 width = nsSVGUtils::UserSpace(aFrame, &aXYWH[2]);
1414 height = nsSVGUtils::UserSpace(aFrame, &aXYWH[3]);
1416 return gfxRect(x, y, width, height);
1419 PRBool
1420 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
1422 if (!aFrame->GetStyleSVGReset()->mFilter) {
1423 nsIAtom *type = aFrame->GetType();
1424 if (type == nsGkAtoms::svgImageFrame)
1425 return PR_TRUE;
1426 if (type == nsGkAtoms::svgPathGeometryFrame) {
1427 const nsStyleSVG *style = aFrame->GetStyleSVG();
1428 if (style->mFill.mType == eStyleSVGPaintType_None &&
1429 style->mStroke.mType == eStyleSVGPaintType_None)
1430 return PR_TRUE;
1433 return PR_FALSE;
1436 float
1437 nsSVGUtils::MaxExpansion(nsIDOMSVGMatrix *aMatrix)
1439 float a, b, c, d;
1440 aMatrix->GetA(&a);
1441 aMatrix->GetB(&b);
1442 aMatrix->GetC(&c);
1443 aMatrix->GetD(&d);
1445 // maximum expansion derivation from
1446 // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
1447 float f = (a * a + b * b + c * c + d * d) / 2;
1448 float g = (a * a + b * b - c * c - d * d) / 2;
1449 float h = a * c + b * d;
1450 return sqrt(f + sqrt(g * g + h * h));
1453 already_AddRefed<nsIDOMSVGMatrix>
1454 nsSVGUtils::AdjustMatrixForUnits(nsIDOMSVGMatrix *aMatrix,
1455 nsSVGEnum *aUnits,
1456 nsIFrame *aFrame)
1458 nsCOMPtr<nsIDOMSVGMatrix> fini = aMatrix;
1460 if (aFrame &&
1461 aUnits->GetAnimValue() == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
1462 float minx, miny, width, height;
1464 PRBool gotRect = PR_FALSE;
1465 if (aFrame->IsFrameOfType(nsIFrame::eSVG)) {
1466 nsISVGChildFrame *svgFrame;
1467 CallQueryInterface(aFrame, &svgFrame);
1468 nsCOMPtr<nsIDOMSVGRect> rect;
1469 svgFrame->GetBBox(getter_AddRefs(rect));
1470 if (rect) {
1471 gotRect = PR_TRUE;
1472 rect->GetX(&minx);
1473 rect->GetY(&miny);
1474 rect->GetWidth(&width);
1475 rect->GetHeight(&height);
1476 // Correct for scaling in outersvg CTM
1477 nsPresContext *presCtx = aFrame->PresContext();
1478 float scaleInv =
1479 presCtx->AppUnitsToGfxUnits(presCtx->AppUnitsPerCSSPixel());
1480 minx /= scaleInv;
1481 miny /= scaleInv;
1482 width /= scaleInv;
1483 height /= scaleInv;
1485 } else {
1486 gotRect = PR_TRUE;
1487 gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
1488 minx = r.X();
1489 miny = r.Y();
1490 width = r.Width();
1491 height = r.Height();
1494 if (gotRect) {
1495 nsCOMPtr<nsIDOMSVGMatrix> tmp;
1496 aMatrix->Translate(minx, miny, getter_AddRefs(tmp));
1497 tmp->ScaleNonUniform(width, height, getter_AddRefs(fini));
1501 nsIDOMSVGMatrix* retval = fini.get();
1502 NS_IF_ADDREF(retval);
1503 return retval;
1506 #ifdef DEBUG
1507 void
1508 nsSVGUtils::WritePPM(const char *fname, gfxImageSurface *aSurface)
1510 FILE *f = fopen(fname, "wb");
1511 if (!f)
1512 return;
1514 gfxIntSize size = aSurface->GetSize();
1515 fprintf(f, "P6\n%d %d\n255\n", size.width, size.height);
1516 unsigned char *data = aSurface->Data();
1517 PRInt32 stride = aSurface->Stride();
1518 for (int y=0; y<size.height; y++) {
1519 for (int x=0; x<size.width; x++) {
1520 fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_R, 1, 1, f);
1521 fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_G, 1, 1, f);
1522 fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_B, 1, 1, f);
1525 fclose(f);
1527 #endif
1529 // ----------------------------------------------------------------------
1531 nsSVGRenderState::nsSVGRenderState(nsIRenderingContext *aContext) :
1532 mRenderMode(NORMAL), mRenderingContext(aContext)
1534 mGfxContext = aContext->ThebesContext();
1537 nsSVGRenderState::nsSVGRenderState(gfxASurface *aSurface) :
1538 mRenderMode(NORMAL)
1540 mGfxContext = new gfxContext(aSurface);
1543 nsIRenderingContext*
1544 nsSVGRenderState::GetRenderingContext(nsIFrame *aFrame)
1546 if (!mRenderingContext) {
1547 nsIDeviceContext* devCtx = aFrame->PresContext()->DeviceContext();
1548 devCtx->CreateRenderingContextInstance(*getter_AddRefs(mRenderingContext));
1549 if (!mRenderingContext)
1550 return nsnull;
1551 mRenderingContext->Init(devCtx, mGfxContext);
1553 return mRenderingContext;