Bug 473511. nsSVGInnerSVGFrame does not need to implement nsISVGValueObserver. r...
[mozilla-central.git] / layout / svg / base / src / nsSVGUtils.cpp
blob9534b51da2c23050d2b3f655724fa9d526ca5c93
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 "nsIDOMDocument.h"
38 #include "nsIDOMSVGElement.h"
39 #include "nsIDOMSVGSVGElement.h"
40 #include "nsStyleCoord.h"
41 #include "nsPresContext.h"
42 #include "nsSVGSVGElement.h"
43 #include "nsIContent.h"
44 #include "nsIDocument.h"
45 #include "nsIFrame.h"
46 #include "nsGkAtoms.h"
47 #include "nsIURI.h"
48 #include "nsStyleStruct.h"
49 #include "nsIPresShell.h"
50 #include "nsSVGUtils.h"
51 #include "nsISVGGlyphFragmentLeaf.h"
52 #include "nsNetUtil.h"
53 #include "nsIDOMSVGRect.h"
54 #include "nsFrameList.h"
55 #include "nsISVGChildFrame.h"
56 #include "nsContentDLF.h"
57 #include "nsContentUtils.h"
58 #include "nsSVGFilterFrame.h"
59 #include "nsINameSpaceManager.h"
60 #include "nsIDOMSVGPoint.h"
61 #include "nsSVGPoint.h"
62 #include "nsDOMError.h"
63 #include "nsSVGOuterSVGFrame.h"
64 #include "nsSVGPreserveAspectRatio.h"
65 #include "nsSVGMatrix.h"
66 #include "nsSVGClipPathFrame.h"
67 #include "nsSVGMaskFrame.h"
68 #include "nsSVGContainerFrame.h"
69 #include "nsSVGLength2.h"
70 #include "nsGenericElement.h"
71 #include "nsAttrValue.h"
72 #include "nsSVGGeometryFrame.h"
73 #include "nsIScriptError.h"
74 #include "gfxContext.h"
75 #include "gfxMatrix.h"
76 #include "gfxRect.h"
77 #include "gfxImageSurface.h"
78 #include "gfxPlatform.h"
79 #include "nsSVGForeignObjectFrame.h"
80 #include "nsIFontMetrics.h"
81 #include "nsIDOMSVGUnitTypes.h"
82 #include "nsSVGRect.h"
83 #include "nsSVGEffects.h"
84 #include "nsSVGIntegrationUtils.h"
85 #include "nsSVGFilterPaintCallback.h"
87 gfxASurface *nsSVGUtils::mThebesComputationalSurface = nsnull;
89 // c = n / 255
90 // (c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1 / 2.4) - 0.055) * 255 + 0.5
91 static const PRUint8 glinearRGBTosRGBMap[256] = {
92 0, 13, 22, 28, 34, 38, 42, 46,
93 50, 53, 56, 59, 61, 64, 66, 69,
94 71, 73, 75, 77, 79, 81, 83, 85,
95 86, 88, 90, 92, 93, 95, 96, 98,
96 99, 101, 102, 104, 105, 106, 108, 109,
97 110, 112, 113, 114, 115, 117, 118, 119,
98 120, 121, 122, 124, 125, 126, 127, 128,
99 129, 130, 131, 132, 133, 134, 135, 136,
100 137, 138, 139, 140, 141, 142, 143, 144,
101 145, 146, 147, 148, 148, 149, 150, 151,
102 152, 153, 154, 155, 155, 156, 157, 158,
103 159, 159, 160, 161, 162, 163, 163, 164,
104 165, 166, 167, 167, 168, 169, 170, 170,
105 171, 172, 173, 173, 174, 175, 175, 176,
106 177, 178, 178, 179, 180, 180, 181, 182,
107 182, 183, 184, 185, 185, 186, 187, 187,
108 188, 189, 189, 190, 190, 191, 192, 192,
109 193, 194, 194, 195, 196, 196, 197, 197,
110 198, 199, 199, 200, 200, 201, 202, 202,
111 203, 203, 204, 205, 205, 206, 206, 207,
112 208, 208, 209, 209, 210, 210, 211, 212,
113 212, 213, 213, 214, 214, 215, 215, 216,
114 216, 217, 218, 218, 219, 219, 220, 220,
115 221, 221, 222, 222, 223, 223, 224, 224,
116 225, 226, 226, 227, 227, 228, 228, 229,
117 229, 230, 230, 231, 231, 232, 232, 233,
118 233, 234, 234, 235, 235, 236, 236, 237,
119 237, 238, 238, 238, 239, 239, 240, 240,
120 241, 241, 242, 242, 243, 243, 244, 244,
121 245, 245, 246, 246, 246, 247, 247, 248,
122 248, 249, 249, 250, 250, 251, 251, 251,
123 252, 252, 253, 253, 254, 254, 255, 255
126 // c = n / 255
127 // c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5
128 static const PRUint8 gsRGBToLinearRGBMap[256] = {
129 0, 0, 0, 0, 0, 0, 0, 1,
130 1, 1, 1, 1, 1, 1, 1, 1,
131 1, 1, 2, 2, 2, 2, 2, 2,
132 2, 2, 3, 3, 3, 3, 3, 3,
133 4, 4, 4, 4, 4, 5, 5, 5,
134 5, 6, 6, 6, 6, 7, 7, 7,
135 8, 8, 8, 8, 9, 9, 9, 10,
136 10, 10, 11, 11, 12, 12, 12, 13,
137 13, 13, 14, 14, 15, 15, 16, 16,
138 17, 17, 17, 18, 18, 19, 19, 20,
139 20, 21, 22, 22, 23, 23, 24, 24,
140 25, 25, 26, 27, 27, 28, 29, 29,
141 30, 30, 31, 32, 32, 33, 34, 35,
142 35, 36, 37, 37, 38, 39, 40, 41,
143 41, 42, 43, 44, 45, 45, 46, 47,
144 48, 49, 50, 51, 51, 52, 53, 54,
145 55, 56, 57, 58, 59, 60, 61, 62,
146 63, 64, 65, 66, 67, 68, 69, 70,
147 71, 72, 73, 74, 76, 77, 78, 79,
148 80, 81, 82, 84, 85, 86, 87, 88,
149 90, 91, 92, 93, 95, 96, 97, 99,
150 100, 101, 103, 104, 105, 107, 108, 109,
151 111, 112, 114, 115, 116, 118, 119, 121,
152 122, 124, 125, 127, 128, 130, 131, 133,
153 134, 136, 138, 139, 141, 142, 144, 146,
154 147, 149, 151, 152, 154, 156, 157, 159,
155 161, 163, 164, 166, 168, 170, 171, 173,
156 175, 177, 179, 181, 183, 184, 186, 188,
157 190, 192, 194, 196, 198, 200, 202, 204,
158 206, 208, 210, 212, 214, 216, 218, 220,
159 222, 224, 226, 229, 231, 233, 235, 237,
160 239, 242, 244, 246, 248, 250, 253, 255
163 static PRBool gSVGEnabled;
164 static const char SVG_PREF_STR[] = "svg.enabled";
166 static int
167 SVGPrefChanged(const char *aPref, void *aClosure)
169 PRBool prefVal = nsContentUtils::GetBoolPref(SVG_PREF_STR);
170 if (prefVal == gSVGEnabled)
171 return 0;
173 gSVGEnabled = prefVal;
174 if (gSVGEnabled)
175 nsContentDLF::RegisterSVG();
176 else
177 nsContentDLF::UnregisterSVG();
179 return 0;
182 PRBool
183 NS_SVGEnabled()
185 static PRBool sInitialized = PR_FALSE;
187 if (!sInitialized) {
188 /* check and register ourselves with the pref */
189 gSVGEnabled = nsContentUtils::GetBoolPref(SVG_PREF_STR);
190 nsContentUtils::RegisterPrefCallback(SVG_PREF_STR, SVGPrefChanged, nsnull);
192 sInitialized = PR_TRUE;
195 return gSVGEnabled;
198 static nsIFrame*
199 GetFrameForContent(nsIContent* aContent)
201 if (!aContent)
202 return nsnull;
204 nsIDocument *doc = aContent->GetCurrentDoc();
205 if (!doc)
206 return nsnull;
208 return nsGenericElement::GetPrimaryFrameFor(aContent, doc);
211 float
212 nsSVGUtils::GetFontSize(nsIContent *aContent)
214 nsIFrame* frame = GetFrameForContent(aContent);
215 if (!frame) {
216 NS_WARNING("no frame in GetFontSize()");
217 return 1.0f;
220 return GetFontSize(frame);
223 float
224 nsSVGUtils::GetFontSize(nsIFrame *aFrame)
226 return nsPresContext::AppUnitsToFloatCSSPixels(aFrame->GetStyleFont()->mSize) /
227 aFrame->PresContext()->TextZoom();
230 float
231 nsSVGUtils::GetFontXHeight(nsIContent *aContent)
233 nsIFrame* frame = GetFrameForContent(aContent);
234 if (!frame) {
235 NS_WARNING("no frame in GetFontXHeight()");
236 return 1.0f;
239 return GetFontXHeight(frame);
242 float
243 nsSVGUtils::GetFontXHeight(nsIFrame *aFrame)
245 nsCOMPtr<nsIFontMetrics> fontMetrics;
246 nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fontMetrics));
248 if (!fontMetrics) {
249 NS_WARNING("no FontMetrics in GetFontXHeight()");
250 return 1.0f;
253 nscoord xHeight;
254 fontMetrics->GetXHeight(xHeight);
255 return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
256 aFrame->PresContext()->TextZoom();
259 void
260 nsSVGUtils::UnPremultiplyImageDataAlpha(PRUint8 *data,
261 PRInt32 stride,
262 const nsIntRect &rect)
264 for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
265 for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
266 PRUint8 *pixel = data + stride * y + 4 * x;
268 PRUint8 a = pixel[GFX_ARGB32_OFFSET_A];
269 if (a == 255)
270 continue;
272 if (a) {
273 pixel[GFX_ARGB32_OFFSET_B] = (255 * pixel[GFX_ARGB32_OFFSET_B]) / a;
274 pixel[GFX_ARGB32_OFFSET_G] = (255 * pixel[GFX_ARGB32_OFFSET_G]) / a;
275 pixel[GFX_ARGB32_OFFSET_R] = (255 * pixel[GFX_ARGB32_OFFSET_R]) / a;
276 } else {
277 pixel[GFX_ARGB32_OFFSET_B] = 0;
278 pixel[GFX_ARGB32_OFFSET_G] = 0;
279 pixel[GFX_ARGB32_OFFSET_R] = 0;
285 void
286 nsSVGUtils::PremultiplyImageDataAlpha(PRUint8 *data,
287 PRInt32 stride,
288 const nsIntRect &rect)
290 for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
291 for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
292 PRUint8 *pixel = data + stride * y + 4 * x;
294 PRUint8 a = pixel[GFX_ARGB32_OFFSET_A];
295 if (a == 255)
296 continue;
298 FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_B],
299 pixel[GFX_ARGB32_OFFSET_B] * a);
300 FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_G],
301 pixel[GFX_ARGB32_OFFSET_G] * a);
302 FAST_DIVIDE_BY_255(pixel[GFX_ARGB32_OFFSET_R],
303 pixel[GFX_ARGB32_OFFSET_R] * a);
308 void
309 nsSVGUtils::ConvertImageDataToLinearRGB(PRUint8 *data,
310 PRInt32 stride,
311 const nsIntRect &rect)
313 for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
314 for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
315 PRUint8 *pixel = data + stride * y + 4 * x;
317 pixel[GFX_ARGB32_OFFSET_B] =
318 gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_B]];
319 pixel[GFX_ARGB32_OFFSET_G] =
320 gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_G]];
321 pixel[GFX_ARGB32_OFFSET_R] =
322 gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_R]];
327 void
328 nsSVGUtils::ConvertImageDataFromLinearRGB(PRUint8 *data,
329 PRInt32 stride,
330 const nsIntRect &rect)
332 for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
333 for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
334 PRUint8 *pixel = data + stride * y + 4 * x;
336 pixel[GFX_ARGB32_OFFSET_B] =
337 glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_B]];
338 pixel[GFX_ARGB32_OFFSET_G] =
339 glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_G]];
340 pixel[GFX_ARGB32_OFFSET_R] =
341 glinearRGBTosRGBMap[pixel[GFX_ARGB32_OFFSET_R]];
346 nsresult
347 nsSVGUtils::ReportToConsole(nsIDocument* doc,
348 const char* aWarning,
349 const PRUnichar **aParams,
350 PRUint32 aParamsLength)
352 return nsContentUtils::ReportToConsole(nsContentUtils::eSVG_PROPERTIES,
353 aWarning,
354 aParams, aParamsLength,
355 doc ? doc->GetDocumentURI() : nsnull,
356 EmptyString(), 0, 0,
357 nsIScriptError::warningFlag,
358 "SVG");
361 float
362 nsSVGUtils::CoordToFloat(nsPresContext *aPresContext,
363 nsSVGElement *aContent,
364 const nsStyleCoord &aCoord)
366 switch (aCoord.GetUnit()) {
367 case eStyleUnit_Factor:
368 // user units
369 return aCoord.GetFactorValue();
371 case eStyleUnit_Coord:
372 return nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue());
374 case eStyleUnit_Percent: {
375 nsSVGSVGElement* ctx = aContent->GetCtx();
376 return ctx ? aCoord.GetPercentValue() * ctx->GetLength(nsSVGUtils::XY) : 0.0f;
378 default:
379 return 0.0f;
383 nsresult
384 nsSVGUtils::GetNearestViewportElement(nsIContent *aContent,
385 nsIDOMSVGElement * *aNearestViewportElement)
387 *aNearestViewportElement = nsnull;
389 nsBindingManager *bindingManager = nsnull;
390 // XXXbz I _think_ this is right. We want to be using the binding manager
391 // that would have attached the bindings that gives us our anonymous
392 // ancestors. That's the binding manager for the document we actually belong
393 // to, which is our owner doc.
394 nsIDocument* ownerDoc = aContent->GetOwnerDoc();
395 if (ownerDoc) {
396 bindingManager = ownerDoc->BindingManager();
399 nsCOMPtr<nsIContent> element = aContent;
400 nsCOMPtr<nsIContent> ancestor;
401 unsigned short ancestorCount = 0;
403 while (1) {
405 ancestor = nsnull;
406 if (bindingManager) {
407 // check for an anonymous ancestor first
408 ancestor = bindingManager->GetInsertionParent(element);
410 if (!ancestor) {
411 // if we didn't find an anonymous ancestor, use the explicit one
412 ancestor = element->GetParent();
415 nsCOMPtr<nsIDOMSVGFitToViewBox> fitToViewBox = do_QueryInterface(element);
417 if (fitToViewBox && (ancestor || ancestorCount)) {
418 // right interface and not the outermost SVG element
419 nsCOMPtr<nsIDOMSVGElement> SVGElement = do_QueryInterface(element);
420 SVGElement.swap(*aNearestViewportElement);
421 return NS_OK;
424 if (!ancestor) {
425 // reached the top of our parent chain
426 break;
429 element = ancestor;
430 ancestorCount++;
433 return NS_OK;
436 nsresult
437 nsSVGUtils::GetFarthestViewportElement(nsIContent *aContent,
438 nsIDOMSVGElement * *aFarthestViewportElement)
440 *aFarthestViewportElement = nsnull;
442 nsBindingManager *bindingManager = nsnull;
443 // XXXbz I _think_ this is right. We want to be using the binding manager
444 // that would have attached the bindings that gives us our anonymous
445 // ancestors. That's the binding manager for the document we actually belong
446 // to, which is our owner doc.
447 nsIDocument* ownerDoc = aContent->GetOwnerDoc();
448 if (ownerDoc) {
449 bindingManager = ownerDoc->BindingManager();
452 nsCOMPtr<nsIContent> element = aContent;
453 nsCOMPtr<nsIContent> ancestor;
454 nsCOMPtr<nsIDOMSVGElement> SVGElement;
455 unsigned short ancestorCount = 0;
457 while (1) {
459 ancestor = nsnull;
460 if (bindingManager) {
461 // check for an anonymous ancestor first
462 ancestor = bindingManager->GetInsertionParent(element);
464 if (!ancestor) {
465 // if we didn't find an anonymous ancestor, use the explicit one
466 ancestor = element->GetParent();
469 nsCOMPtr<nsIDOMSVGFitToViewBox> fitToViewBox = do_QueryInterface(element);
471 if (fitToViewBox) {
472 // right interface
473 SVGElement = do_QueryInterface(element);
476 if (!ancestor) {
477 // reached the top of our parent chain
478 break;
481 element = ancestor;
482 ancestorCount++;
485 if (ancestorCount == 0 || !SVGElement) {
486 // outermost SVG element or no viewport found
487 return NS_OK;
490 SVGElement.swap(*aFarthestViewportElement);
491 return NS_OK;
494 nsresult
495 nsSVGUtils::GetBBox(nsFrameList *aFrames, nsIDOMSVGRect **_retval)
497 *_retval = nsnull;
499 float minx, miny, maxx, maxy;
500 minx = miny = FLT_MAX;
501 maxx = maxy = -1.0 * FLT_MAX;
503 nsCOMPtr<nsIDOMSVGRect> unionRect;
505 nsIFrame* kid = aFrames->FirstChild();
506 while (kid) {
507 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
508 if (SVGFrame) {
509 nsCOMPtr<nsIDOMSVGRect> box;
510 SVGFrame->GetBBox(getter_AddRefs(box));
512 if (box) {
513 float bminx, bminy, bmaxx, bmaxy, width, height;
514 box->GetX(&bminx);
515 box->GetY(&bminy);
516 box->GetWidth(&width);
517 box->GetHeight(&height);
518 bmaxx = bminx+width;
519 bmaxy = bminy+height;
521 if (!unionRect)
522 unionRect = box;
523 minx = PR_MIN(minx, bminx);
524 miny = PR_MIN(miny, bminy);
525 maxx = PR_MAX(maxx, bmaxx);
526 maxy = PR_MAX(maxy, bmaxy);
529 kid = kid->GetNextSibling();
532 if (unionRect) {
533 unionRect->SetX(minx);
534 unionRect->SetY(miny);
535 unionRect->SetWidth(maxx - minx);
536 unionRect->SetHeight(maxy - miny);
537 *_retval = unionRect;
538 NS_ADDREF(*_retval);
539 return NS_OK;
542 return NS_ERROR_FAILURE;
545 nsRect
546 nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect)
548 PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
549 nsIntRect rect = nsRect::ToOutsidePixels(aRect, appUnitsPerDevPixel);
551 while (aFrame) {
552 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
553 break;
555 nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame);
556 if (filter) {
557 rect = filter->GetInvalidationBBox(aFrame, rect);
559 aFrame = aFrame->GetParent();
562 return nsIntRect::ToAppUnits(rect, appUnitsPerDevPixel);
565 void
566 nsSVGUtils::InvalidateCoveredRegion(nsIFrame *aFrame)
568 if (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
569 return;
571 nsSVGOuterSVGFrame* outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(aFrame);
572 NS_ASSERTION(outerSVGFrame, "no outer svg frame");
573 if (outerSVGFrame)
574 outerSVGFrame->InvalidateCoveredRegion(aFrame);
577 void
578 nsSVGUtils::UpdateGraphic(nsISVGChildFrame *aSVGFrame)
580 nsIFrame *frame = do_QueryFrame(aSVGFrame);
582 nsSVGEffects::InvalidateRenderingObservers(frame);
584 if (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
585 return;
587 nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(frame);
588 if (!outerSVGFrame) {
589 NS_ERROR("null outerSVGFrame");
590 return;
593 if (outerSVGFrame->IsRedrawSuspended()) {
594 frame->AddStateBits(NS_STATE_SVG_DIRTY);
595 } else {
596 frame->RemoveStateBits(NS_STATE_SVG_DIRTY);
598 PRBool changed = outerSVGFrame->UpdateAndInvalidateCoveredRegion(frame);
599 if (changed) {
600 NotifyAncestorsOfFilterRegionChange(frame);
605 void
606 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
608 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
609 // It would be better if we couldn't get here
610 return;
613 aFrame = aFrame->GetParent();
615 while (aFrame) {
616 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
617 return;
619 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
620 if (property) {
621 property->Invalidate();
623 aFrame = aFrame->GetParent();
627 double
628 nsSVGUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight)
630 return sqrt((aWidth*aWidth + aHeight*aHeight)/2);
633 float
634 nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, const nsSVGLength2 *aLength)
636 float fraction, axis;
638 switch (aLength->GetCtxType()) {
639 case X:
640 aRect->GetWidth(&axis);
641 break;
642 case Y:
643 aRect->GetHeight(&axis);
644 break;
645 case XY:
647 float width, height;
648 aRect->GetWidth(&width);
649 aRect->GetHeight(&height);
650 axis = float(ComputeNormalizedHypotenuse(width, height));
654 if (aLength->IsPercentage()) {
655 fraction = aLength->GetAnimValInSpecifiedUnits() / 100;
656 } else
657 fraction = aLength->GetAnimValue(static_cast<nsSVGSVGElement*>
658 (nsnull));
660 return fraction * axis;
663 float
664 nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
666 return aLength->GetAnimValue(aSVGElement);
669 float
670 nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
672 return aLength->GetAnimValue(aNonSVGContext);
675 void
676 nsSVGUtils::TransformPoint(nsIDOMSVGMatrix *matrix,
677 float *x, float *y)
679 nsCOMPtr<nsIDOMSVGPoint> point;
680 NS_NewSVGPoint(getter_AddRefs(point), *x, *y);
681 if (!point)
682 return;
684 nsCOMPtr<nsIDOMSVGPoint> xfpoint;
685 point->MatrixTransform(matrix, getter_AddRefs(xfpoint));
686 if (!xfpoint)
687 return;
689 xfpoint->GetX(x);
690 xfpoint->GetY(y);
693 float
694 nsSVGUtils::AngleBisect(float a1, float a2)
696 float delta = fmod(a2 - a1, static_cast<float>(2*M_PI));
697 if (delta < 0) {
698 delta += 2*M_PI;
700 /* delta is now the angle from a1 around to a2, in the range [0, 2*M_PI) */
701 float r = a1 + delta/2;
702 if (delta >= M_PI) {
703 /* the arc from a2 to a1 is smaller, so use the ray on that side */
704 r += M_PI;
706 return r;
709 nsSVGOuterSVGFrame *
710 nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
712 while (aFrame) {
713 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
714 return static_cast<nsSVGOuterSVGFrame*>(aFrame);
716 aFrame = aFrame->GetParent();
719 return nsnull;
722 nsIFrame*
723 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
725 nsISVGChildFrame* svg = do_QueryFrame(aFrame);
726 if (!svg)
727 return nsnull;
728 *aRect = svg->GetCoveredRegion();
729 return GetOuterSVGFrame(aFrame);
732 already_AddRefed<nsIDOMSVGMatrix>
733 nsSVGUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
734 float aViewboxX, float aViewboxY,
735 float aViewboxWidth, float aViewboxHeight,
736 const nsSVGPreserveAspectRatio &aPreserveAspectRatio,
737 PRBool aIgnoreAlign)
739 NS_ASSERTION(aViewboxWidth > 0, "viewBox width must be greater than zero!");
740 NS_ASSERTION(aViewboxHeight > 0, "viewBox height must be greater than zero!");
742 PRUint16 align = aPreserveAspectRatio.GetAnimValue().GetAlign();
743 PRUint16 meetOrSlice = aPreserveAspectRatio.GetAnimValue().GetMeetOrSlice();
745 // default to the defaults
746 if (align == nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_UNKNOWN)
747 align = nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID;
748 if (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN)
749 meetOrSlice = nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET;
751 // alignment disabled for this matrix setup
752 if (aIgnoreAlign)
753 align = nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMIN;
755 float a, d, e, f;
756 a = aViewportWidth / aViewboxWidth;
757 d = aViewportHeight / aViewboxHeight;
758 e = 0.0f;
759 f = 0.0f;
761 if (align != nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE &&
762 a != d) {
763 if ((meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET &&
764 a < d) ||
765 (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE &&
766 d < a)) {
767 d = a;
768 switch (align) {
769 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMIN:
770 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
771 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
772 break;
773 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
774 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
775 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
776 f = (aViewportHeight - a * aViewboxHeight) / 2.0f;
777 break;
778 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
779 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
780 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
781 f = aViewportHeight - a * aViewboxHeight;
782 break;
783 default:
784 NS_NOTREACHED("Unknown value for align");
787 else if (
788 (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET &&
789 d < a) ||
790 (meetOrSlice == nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE &&
791 a < d)) {
792 a = d;
793 switch (align) {
794 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMIN:
795 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
796 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
797 break;
798 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
799 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
800 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
801 e = (aViewportWidth - a * aViewboxWidth) / 2.0f;
802 break;
803 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
804 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
805 case nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
806 e = aViewportWidth - a * aViewboxWidth;
807 break;
808 default:
809 NS_NOTREACHED("Unknown value for align");
812 else NS_NOTREACHED("Unknown value for meetOrSlice");
815 if (aViewboxX) e += -a * aViewboxX;
816 if (aViewboxY) f += -d * aViewboxY;
818 nsIDOMSVGMatrix *retval;
819 NS_NewSVGMatrix(&retval, a, 0.0f, 0.0f, d, e, f);
820 return retval;
824 // This is ugly and roc will want to kill me...
826 already_AddRefed<nsIDOMSVGMatrix>
827 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
829 if (!aFrame->IsFrameOfType(nsIFrame::eSVG))
830 return nsSVGIntegrationUtils::GetInitialMatrix(aFrame);
832 if (!aFrame->IsLeaf()) {
833 // foreignObject is the one non-leaf svg frame that isn't a SVGContainer
834 if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
835 nsSVGForeignObjectFrame *foreignFrame =
836 static_cast<nsSVGForeignObjectFrame*>(aFrame);
837 return foreignFrame->GetCanvasTM();
839 nsSVGContainerFrame *containerFrame = static_cast<nsSVGContainerFrame*>
840 (aFrame);
841 return containerFrame->GetCanvasTM();
844 nsSVGGeometryFrame *geometryFrame = static_cast<nsSVGGeometryFrame*>
845 (aFrame);
846 nsCOMPtr<nsIDOMSVGMatrix> matrix;
847 nsIDOMSVGMatrix *retval;
848 geometryFrame->GetCanvasTM(getter_AddRefs(matrix));
849 retval = matrix.get();
850 NS_IF_ADDREF(retval);
851 return retval;
854 void
855 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, PRUint32 aFlags)
857 nsIFrame *aKid = aFrame->GetFirstChild(nsnull);
859 while (aKid) {
860 nsISVGChildFrame* SVGFrame = do_QueryFrame(aKid);
861 if (SVGFrame) {
862 SVGFrame->NotifySVGChanged(aFlags);
863 } else {
864 NS_ASSERTION(aKid->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
865 // recurse into the children of container frames e.g. <clipPath>, <mask>
866 // in case they have child frames with transformation matrices
867 nsSVGUtils::NotifyChildrenOfSVGChange(aKid, aFlags);
869 aKid = aKid->GetNextSibling();
873 // ************************************************************
875 class SVGPaintCallback : public nsSVGFilterPaintCallback
877 public:
878 virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget,
879 const nsIntRect* aDirtyRect)
881 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget);
882 NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
883 NS_ASSERTION(!svgChildFrame->GetMatrixPropagation(),
884 "This should have been set to false already");
886 nsIntRect* dirtyRect = nsnull;
887 nsIntRect tmpDirtyRect;
889 // aDirtyRect is in user-space pixels, we need to convert to
890 // outer-SVG-frame-relative device pixels.
891 if (aDirtyRect) {
892 // Temporarily set SetMatrixPropagation so we can find out what
893 // the actual CTM is.
894 svgChildFrame->SetMatrixPropagation(PR_TRUE);
895 nsCOMPtr<nsIDOMSVGMatrix> ctm = nsSVGUtils::GetCanvasTM(aTarget);
896 NS_ASSERTION(ctm, "graphic source didn't specify a ctm");
897 svgChildFrame->SetMatrixPropagation(PR_FALSE);
899 gfxMatrix matrix = nsSVGUtils::ConvertSVGMatrixToThebes(ctm);
900 gfxRect dirtyBounds = matrix.TransformBounds(
901 gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
902 dirtyBounds.RoundOut();
903 if (NS_SUCCEEDED(nsSVGUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect))) {
904 dirtyRect = &tmpDirtyRect;
908 svgChildFrame->PaintSVG(aContext, dirtyRect);
912 void
913 nsSVGUtils::PaintFrameWithEffects(nsSVGRenderState *aContext,
914 const nsIntRect *aDirtyRect,
915 nsIFrame *aFrame)
917 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
918 if (!svgChildFrame)
919 return;
921 float opacity = aFrame->GetStyleDisplay()->mOpacity;
922 if (opacity == 0.0f)
923 return;
925 /* Properties are added lazily and may have been removed by a restyle,
926 so make sure all applicable ones are set again. */
928 nsSVGEffects::EffectProperties effectProperties =
929 nsSVGEffects::GetEffectProperties(aFrame);
931 PRBool isOK = PR_TRUE;
932 nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
934 /* Check if we need to draw anything. HasValidCoveredRect only returns
935 * true for path geometry and glyphs, so basically we're traversing
936 * all containers and we can only skip leaves here.
938 if (aDirtyRect && svgChildFrame->HasValidCoveredRect()) {
939 if (filterFrame) {
940 if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull)))
941 return;
942 } else {
943 nsRect rect = nsIntRect::ToAppUnits(*aDirtyRect, aFrame->PresContext()->AppUnitsPerDevPixel());
944 if (!rect.Intersects(aFrame->GetRect()))
945 return;
949 /* SVG defines the following rendering model:
951 * 1. Render geometry
952 * 2. Apply filter
953 * 3. Apply clipping, masking, group opacity
955 * We follow this, but perform a couple of optimizations:
957 * + Use cairo's clipPath when representable natively (single object
958 * clip region).
960 * + Merge opacity and masking if both used together.
963 if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
964 opacity = 1.0f;
966 gfxContext *gfx = aContext->GetGfxContext();
967 PRBool complexEffects = PR_FALSE;
969 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
970 nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
972 PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;
974 if (!isOK) {
975 // Some resource is missing. We shouldn't paint anything.
976 return;
979 nsCOMPtr<nsIDOMSVGMatrix> matrix =
980 (clipPathFrame || maskFrame) ? GetCanvasTM(aFrame) : nsnull;
982 /* Check if we need to do additional operations on this child's
983 * rendering, which necessitates rendering into another surface. */
984 if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
985 complexEffects = PR_TRUE;
986 gfx->Save();
987 gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
990 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
991 * we can just do normal painting and get it clipped appropriately.
993 if (clipPathFrame && isTrivialClip) {
994 gfx->Save();
995 clipPathFrame->ClipPaint(aContext, aFrame, matrix);
998 /* Paint the child */
999 if (filterFrame) {
1000 SVGPaintCallback paintCallback;
1001 filterFrame->FilterPaint(aContext, aFrame, &paintCallback, aDirtyRect);
1002 } else {
1003 svgChildFrame->PaintSVG(aContext, aDirtyRect);
1006 if (clipPathFrame && isTrivialClip) {
1007 gfx->Restore();
1010 /* No more effects, we're done. */
1011 if (!complexEffects)
1012 return;
1014 gfx->PopGroupToSource();
1016 nsRefPtr<gfxPattern> maskSurface =
1017 maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
1018 matrix, opacity) : nsnull;
1020 nsRefPtr<gfxPattern> clipMaskSurface;
1021 if (clipPathFrame && !isTrivialClip) {
1022 gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1024 nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
1025 clipMaskSurface = gfx->PopGroup();
1027 if (NS_SUCCEEDED(rv) && clipMaskSurface) {
1028 // Still more set after clipping, so clip to another surface
1029 if (maskSurface || opacity != 1.0f) {
1030 gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1031 gfx->Mask(clipMaskSurface);
1032 gfx->PopGroupToSource();
1033 } else {
1034 gfx->Mask(clipMaskSurface);
1039 if (maskSurface) {
1040 gfx->Mask(maskSurface);
1041 } else if (opacity != 1.0f) {
1042 gfx->Paint(opacity);
1045 gfx->Restore();
1048 PRBool
1049 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
1051 nsSVGEffects::EffectProperties props =
1052 nsSVGEffects::GetEffectProperties(aFrame);
1053 if (!props.mClipPath)
1054 return PR_TRUE;
1056 nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(nsnull);
1057 if (!clipPathFrame) {
1058 // clipPath is not a valid resource, so nothing gets painted, so
1059 // hit-testing must fail.
1060 return PR_FALSE;
1063 nsCOMPtr<nsIDOMSVGMatrix> matrix = GetCanvasTM(aFrame);
1064 return clipPathFrame->ClipHitTest(aFrame, matrix, aPoint);
1067 nsIFrame *
1068 nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
1070 // XXX: The frame's children are linked in a singly-linked list in document
1071 // order. If we were to hit test the children in this order we would need to
1072 // hit test *every* SVG frame, since even if we get a hit, later SVG frames
1073 // may lie on top of the matching frame. We really want to traverse SVG
1074 // frames in reverse order so we can stop at the first match. Since we don't
1075 // have a doubly-linked list, for the time being we traverse the
1076 // singly-linked list backwards by first reversing the nextSibling pointers
1077 // in place, and then restoring them when done.
1079 // Note: While the child list pointers are reversed, any method which walks
1080 // the list would only encounter a single child!
1082 nsIFrame* current = nsnull;
1083 nsIFrame* next = aFrame->GetFirstChild(nsnull);
1085 nsIFrame* result = nsnull;
1087 // reverse sibling pointers
1088 while (next) {
1089 nsIFrame* temp = next->GetNextSibling();
1090 next->SetNextSibling(current);
1091 current = next;
1092 next = temp;
1095 // now do the backwards traversal
1096 while (current) {
1097 nsISVGChildFrame* SVGFrame = do_QueryFrame(current);
1098 if (SVGFrame) {
1099 result = SVGFrame->GetFrameForPoint(aPoint);
1100 if (result)
1101 break;
1103 // restore current frame's sibling pointer
1104 nsIFrame* temp = current->GetNextSibling();
1105 current->SetNextSibling(next);
1106 next = current;
1107 current = temp;
1110 // restore remaining pointers
1111 while (current) {
1112 nsIFrame* temp = current->GetNextSibling();
1113 current->SetNextSibling(next);
1114 next = current;
1115 current = temp;
1118 if (result && !HitTestClip(aFrame, aPoint))
1119 result = nsnull;
1121 return result;
1124 nsRect
1125 nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
1127 nsRect rect;
1129 for (nsIFrame* kid = aFrames.FirstChild();
1130 kid;
1131 kid = kid->GetNextSibling()) {
1132 nsISVGChildFrame* child = do_QueryFrame(kid);
1133 if (child) {
1134 nsRect childRect = child->GetCoveredRegion();
1135 rect.UnionRect(rect, childRect);
1139 return rect;
1142 nsRect
1143 nsSVGUtils::ToAppPixelRect(nsPresContext *aPresContext,
1144 double xmin, double ymin,
1145 double xmax, double ymax)
1147 return ToAppPixelRect(aPresContext,
1148 gfxRect(xmin, ymin, xmax - xmin, ymax - ymin));
1151 nsRect
1152 nsSVGUtils::ToAppPixelRect(nsPresContext *aPresContext, const gfxRect& rect)
1154 return nsRect(aPresContext->DevPixelsToAppUnits(NSToIntFloor(rect.X())),
1155 aPresContext->DevPixelsToAppUnits(NSToIntFloor(rect.Y())),
1156 aPresContext->DevPixelsToAppUnits(NSToIntCeil(rect.XMost()) - NSToIntFloor(rect.X())),
1157 aPresContext->DevPixelsToAppUnits(NSToIntCeil(rect.YMost()) - NSToIntFloor(rect.Y())));
1160 gfxIntSize
1161 nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize, PRBool *aResultOverflows)
1163 gfxIntSize surfaceSize =
1164 gfxIntSize(PRInt32(aSize.width + 0.5), PRInt32(aSize.height + 0.5));
1166 *aResultOverflows = (aSize.width >= PR_INT32_MAX + 0.5 ||
1167 aSize.height >= PR_INT32_MAX + 0.5 ||
1168 aSize.width <= PR_INT32_MIN - 0.5 ||
1169 aSize.height <= PR_INT32_MIN - 0.5);
1171 if (*aResultOverflows ||
1172 !gfxASurface::CheckSurfaceSize(surfaceSize)) {
1173 surfaceSize.width = PR_MIN(NS_SVG_OFFSCREEN_MAX_DIMENSION,
1174 surfaceSize.width);
1175 surfaceSize.height = PR_MIN(NS_SVG_OFFSCREEN_MAX_DIMENSION,
1176 surfaceSize.height);
1177 *aResultOverflows = PR_TRUE;
1179 return surfaceSize;
1182 gfxASurface *
1183 nsSVGUtils::GetThebesComputationalSurface()
1185 if (!mThebesComputationalSurface) {
1186 nsRefPtr<gfxImageSurface> surface =
1187 new gfxImageSurface(gfxIntSize(1, 1), gfxASurface::ImageFormatARGB32);
1188 NS_ASSERTION(surface && !surface->CairoStatus(),
1189 "Could not create offscreen surface");
1190 mThebesComputationalSurface = surface;
1191 // we want to keep this surface around
1192 NS_IF_ADDREF(mThebesComputationalSurface);
1195 return mThebesComputationalSurface;
1198 gfxMatrix
1199 nsSVGUtils::ConvertSVGMatrixToThebes(nsIDOMSVGMatrix *aMatrix)
1201 float A, B, C, D, E, F;
1202 aMatrix->GetA(&A);
1203 aMatrix->GetB(&B);
1204 aMatrix->GetC(&C);
1205 aMatrix->GetD(&D);
1206 aMatrix->GetE(&E);
1207 aMatrix->GetF(&F);
1208 return gfxMatrix(A, B, C, D, E, F);
1211 PRBool
1212 nsSVGUtils::HitTestRect(nsIDOMSVGMatrix *aMatrix,
1213 float aRX, float aRY, float aRWidth, float aRHeight,
1214 float aX, float aY)
1216 PRBool result = PR_TRUE;
1218 if (aMatrix) {
1219 gfxContext ctx(GetThebesComputationalSurface());
1220 ctx.SetMatrix(ConvertSVGMatrixToThebes(aMatrix));
1222 ctx.NewPath();
1223 ctx.Rectangle(gfxRect(aRX, aRY, aRWidth, aRHeight));
1224 ctx.IdentityMatrix();
1226 if (!ctx.PointInFill(gfxPoint(aX, aY)))
1227 result = PR_FALSE;
1230 return result;
1233 void
1234 nsSVGUtils::CompositeSurfaceMatrix(gfxContext *aContext,
1235 gfxASurface *aSurface,
1236 nsIDOMSVGMatrix *aCTM, float aOpacity)
1238 gfxMatrix matrix = ConvertSVGMatrixToThebes(aCTM);
1239 if (matrix.IsSingular())
1240 return;
1242 aContext->Save();
1244 aContext->Multiply(matrix);
1246 aContext->SetSource(aSurface);
1247 aContext->Paint(aOpacity);
1249 aContext->Restore();
1252 void
1253 nsSVGUtils::CompositePatternMatrix(gfxContext *aContext,
1254 gfxPattern *aPattern,
1255 nsIDOMSVGMatrix *aCTM, float aWidth, float aHeight, float aOpacity)
1257 gfxMatrix matrix = ConvertSVGMatrixToThebes(aCTM);
1258 if (matrix.IsSingular())
1259 return;
1261 aContext->Save();
1263 SetClipRect(aContext, aCTM, 0, 0, aWidth, aHeight);
1265 aContext->Multiply(matrix);
1267 aContext->SetPattern(aPattern);
1268 aContext->Paint(aOpacity);
1270 aContext->Restore();
1273 void
1274 nsSVGUtils::SetClipRect(gfxContext *aContext,
1275 nsIDOMSVGMatrix *aCTM, float aX, float aY,
1276 float aWidth, float aHeight)
1278 gfxMatrix matrix = ConvertSVGMatrixToThebes(aCTM);
1279 if (matrix.IsSingular())
1280 return;
1282 gfxMatrix oldMatrix = aContext->CurrentMatrix();
1283 aContext->Multiply(matrix);
1284 aContext->Clip(gfxRect(aX, aY, aWidth, aHeight));
1285 aContext->SetMatrix(oldMatrix);
1288 void
1289 nsSVGUtils::ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfxRect)
1291 gfxRect r = aGfxRect;
1292 r.RoundOut();
1293 gfxRect r2(aRect->x, aRect->y, aRect->width, aRect->height);
1294 r = r.Intersect(r2);
1295 *aRect = nsIntRect(PRInt32(r.X()), PRInt32(r.Y()),
1296 PRInt32(r.Width()), PRInt32(r.Height()));
1299 nsresult
1300 nsSVGUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
1302 *aOut = nsIntRect(PRInt32(aIn.X()), PRInt32(aIn.Y()),
1303 PRInt32(aIn.Width()), PRInt32(aIn.Height()));
1304 return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height) == aIn
1305 ? NS_OK : NS_ERROR_FAILURE;
1308 already_AddRefed<nsIDOMSVGRect>
1309 nsSVGUtils::GetBBox(nsIFrame *aFrame)
1311 nsISVGChildFrame *svg = do_QueryFrame(aFrame);
1312 if (!svg) {
1313 nsIDOMSVGRect *rect = nsnull;
1314 gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
1315 NS_NewSVGRect(&rect, r);
1316 return rect;
1319 PRBool needToDisablePropagation = svg->GetMatrixPropagation();
1320 if (needToDisablePropagation) {
1321 svg->SetMatrixPropagation(PR_FALSE);
1322 svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
1323 nsISVGChildFrame::TRANSFORM_CHANGED);
1326 nsCOMPtr<nsIDOMSVGRect> bbox;
1327 svg->GetBBox(getter_AddRefs(bbox));
1329 if (needToDisablePropagation) {
1330 svg->SetMatrixPropagation(PR_TRUE);
1331 svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
1332 nsISVGChildFrame::TRANSFORM_CHANGED);
1335 return bbox.forget();
1338 gfxRect
1339 nsSVGUtils::GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH,
1340 nsIDOMSVGRect *aBBox, nsIFrame *aFrame)
1342 float x, y, width, height;
1343 if (aUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
1344 aBBox->GetX(&x);
1345 x += ObjectSpace(aBBox, &aXYWH[0]);
1346 aBBox->GetY(&y);
1347 y += ObjectSpace(aBBox, &aXYWH[1]);
1348 width = ObjectSpace(aBBox, &aXYWH[2]);
1349 height = ObjectSpace(aBBox, &aXYWH[3]);
1350 } else {
1351 x = nsSVGUtils::UserSpace(aFrame, &aXYWH[0]);
1352 y = nsSVGUtils::UserSpace(aFrame, &aXYWH[1]);
1353 width = nsSVGUtils::UserSpace(aFrame, &aXYWH[2]);
1354 height = nsSVGUtils::UserSpace(aFrame, &aXYWH[3]);
1356 return gfxRect(x, y, width, height);
1359 PRBool
1360 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
1362 if (!aFrame->GetStyleSVGReset()->mFilter) {
1363 nsIAtom *type = aFrame->GetType();
1364 if (type == nsGkAtoms::svgImageFrame)
1365 return PR_TRUE;
1366 if (type == nsGkAtoms::svgPathGeometryFrame) {
1367 const nsStyleSVG *style = aFrame->GetStyleSVG();
1368 if (style->mFill.mType == eStyleSVGPaintType_None &&
1369 style->mStroke.mType == eStyleSVGPaintType_None)
1370 return PR_TRUE;
1373 return PR_FALSE;
1376 float
1377 nsSVGUtils::MaxExpansion(nsIDOMSVGMatrix *aMatrix)
1379 float a, b, c, d;
1380 aMatrix->GetA(&a);
1381 aMatrix->GetB(&b);
1382 aMatrix->GetC(&c);
1383 aMatrix->GetD(&d);
1385 // maximum expansion derivation from
1386 // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
1387 float f = (a * a + b * b + c * c + d * d) / 2;
1388 float g = (a * a + b * b - c * c - d * d) / 2;
1389 float h = a * c + b * d;
1390 return sqrt(f + sqrt(g * g + h * h));
1393 already_AddRefed<nsIDOMSVGMatrix>
1394 nsSVGUtils::AdjustMatrixForUnits(nsIDOMSVGMatrix *aMatrix,
1395 nsSVGEnum *aUnits,
1396 nsIFrame *aFrame)
1398 nsCOMPtr<nsIDOMSVGMatrix> fini = aMatrix;
1400 if (aFrame &&
1401 aUnits->GetAnimValue() == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
1402 float minx, miny, width, height;
1404 PRBool gotRect = PR_FALSE;
1405 if (aFrame->IsFrameOfType(nsIFrame::eSVG)) {
1406 nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame);
1407 nsCOMPtr<nsIDOMSVGRect> rect;
1408 svgFrame->GetBBox(getter_AddRefs(rect));
1409 if (rect) {
1410 gotRect = PR_TRUE;
1411 rect->GetX(&minx);
1412 rect->GetY(&miny);
1413 rect->GetWidth(&width);
1414 rect->GetHeight(&height);
1415 // Correct for scaling in outersvg CTM
1416 nsPresContext *presCtx = aFrame->PresContext();
1417 float scaleInv =
1418 presCtx->AppUnitsToGfxUnits(presCtx->AppUnitsPerCSSPixel());
1419 minx /= scaleInv;
1420 miny /= scaleInv;
1421 width /= scaleInv;
1422 height /= scaleInv;
1424 } else {
1425 gotRect = PR_TRUE;
1426 gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
1427 minx = r.X();
1428 miny = r.Y();
1429 width = r.Width();
1430 height = r.Height();
1433 if (gotRect) {
1434 nsCOMPtr<nsIDOMSVGMatrix> tmp;
1435 aMatrix->Translate(minx, miny, getter_AddRefs(tmp));
1436 tmp->ScaleNonUniform(width, height, getter_AddRefs(fini));
1440 nsIDOMSVGMatrix* retval = fini.get();
1441 NS_IF_ADDREF(retval);
1442 return retval;
1445 #ifdef DEBUG
1446 void
1447 nsSVGUtils::WritePPM(const char *fname, gfxImageSurface *aSurface)
1449 FILE *f = fopen(fname, "wb");
1450 if (!f)
1451 return;
1453 gfxIntSize size = aSurface->GetSize();
1454 fprintf(f, "P6\n%d %d\n255\n", size.width, size.height);
1455 unsigned char *data = aSurface->Data();
1456 PRInt32 stride = aSurface->Stride();
1457 for (int y=0; y<size.height; y++) {
1458 for (int x=0; x<size.width; x++) {
1459 fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_R, 1, 1, f);
1460 fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_G, 1, 1, f);
1461 fwrite(data + y * stride + 4 * x + GFX_ARGB32_OFFSET_B, 1, 1, f);
1464 fclose(f);
1466 #endif
1468 // ----------------------------------------------------------------------
1470 nsSVGRenderState::nsSVGRenderState(nsIRenderingContext *aContext) :
1471 mRenderMode(NORMAL), mRenderingContext(aContext)
1473 mGfxContext = aContext->ThebesContext();
1476 nsSVGRenderState::nsSVGRenderState(gfxASurface *aSurface) :
1477 mRenderMode(NORMAL)
1479 mGfxContext = new gfxContext(aSurface);
1482 nsIRenderingContext*
1483 nsSVGRenderState::GetRenderingContext(nsIFrame *aFrame)
1485 if (!mRenderingContext) {
1486 nsIDeviceContext* devCtx = aFrame->PresContext()->DeviceContext();
1487 devCtx->CreateRenderingContextInstance(*getter_AddRefs(mRenderingContext));
1488 if (!mRenderingContext)
1489 return nsnull;
1490 mRenderingContext->Init(devCtx, mGfxContext);
1492 return mRenderingContext;