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
18 : * Scooter Morris.
19 : * Portions created by the Initial Developer are Copyright (C) 2004
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Scooter Morris <scootermorris@comcast.net>
24 : * Jonathan Watt <jwatt@jwatt.org>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsIDOMSVGAnimatedNumber.h"
41 : #include "nsIDOMSVGAnimTransformList.h"
42 : #include "SVGAnimatedTransformList.h"
43 : #include "nsSVGEffects.h"
44 : #include "nsIDOMSVGStopElement.h"
45 : #include "nsSVGGradientElement.h"
46 : #include "nsSVGGeometryFrame.h"
47 : #include "nsSVGGradientFrame.h"
48 : #include "gfxContext.h"
49 : #include "gfxPattern.h"
50 : #include "nsContentUtils.h"
51 :
52 : using mozilla::SVGAnimatedTransformList;
53 :
54 : //----------------------------------------------------------------------
55 : // Helper classes
56 :
57 : class nsSVGGradientFrame::AutoGradientReferencer
58 : {
59 : public:
60 0 : AutoGradientReferencer(nsSVGGradientFrame *aFrame)
61 0 : : mFrame(aFrame)
62 : {
63 : // Reference loops should normally be detected in advance and handled, so
64 : // we're not expecting to encounter them here
65 0 : NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!");
66 0 : mFrame->mLoopFlag = true;
67 0 : }
68 0 : ~AutoGradientReferencer() {
69 0 : mFrame->mLoopFlag = false;
70 0 : }
71 : private:
72 : nsSVGGradientFrame *mFrame;
73 : };
74 :
75 : //----------------------------------------------------------------------
76 : // Implementation
77 :
78 0 : nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext) :
79 : nsSVGGradientFrameBase(aContext),
80 : mLoopFlag(false),
81 0 : mNoHRefURI(false)
82 : {
83 0 : }
84 :
85 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGGradientFrame)
86 :
87 : //----------------------------------------------------------------------
88 : // nsIFrame methods:
89 :
90 : /* virtual */ void
91 0 : nsSVGGradientFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
92 : {
93 0 : nsSVGEffects::InvalidateRenderingObservers(this);
94 0 : nsSVGGradientFrameBase::DidSetStyleContext(aOldStyleContext);
95 0 : }
96 :
97 : NS_IMETHODIMP
98 0 : nsSVGGradientFrame::AttributeChanged(PRInt32 aNameSpaceID,
99 : nsIAtom* aAttribute,
100 : PRInt32 aModType)
101 : {
102 0 : if (aNameSpaceID == kNameSpaceID_None &&
103 : (aAttribute == nsGkAtoms::gradientUnits ||
104 : aAttribute == nsGkAtoms::gradientTransform ||
105 : aAttribute == nsGkAtoms::spreadMethod)) {
106 0 : nsSVGEffects::InvalidateRenderingObservers(this);
107 0 : } else if (aNameSpaceID == kNameSpaceID_XLink &&
108 : aAttribute == nsGkAtoms::href) {
109 : // Blow away our reference, if any
110 0 : Properties().Delete(nsSVGEffects::HrefProperty());
111 0 : mNoHRefURI = false;
112 : // And update whoever references us
113 0 : nsSVGEffects::InvalidateRenderingObservers(this);
114 : }
115 :
116 : return nsSVGGradientFrameBase::AttributeChanged(aNameSpaceID,
117 0 : aAttribute, aModType);
118 : }
119 :
120 : //----------------------------------------------------------------------
121 :
122 : PRUint32
123 0 : nsSVGGradientFrame::GetStopCount()
124 : {
125 0 : return GetStopFrame(-1, nsnull);
126 : }
127 :
128 : void
129 0 : nsSVGGradientFrame::GetStopInformation(PRInt32 aIndex,
130 : float *aOffset,
131 : nscolor *aStopColor,
132 : float *aStopOpacity)
133 : {
134 0 : *aOffset = 0.0f;
135 0 : *aStopColor = NS_RGBA(0, 0, 0, 0);
136 0 : *aStopOpacity = 1.0f;
137 :
138 0 : nsIFrame *stopFrame = nsnull;
139 0 : GetStopFrame(aIndex, &stopFrame);
140 : nsCOMPtr<nsIDOMSVGStopElement> stopElement =
141 0 : do_QueryInterface(stopFrame->GetContent());
142 :
143 0 : if (stopElement) {
144 0 : nsCOMPtr<nsIDOMSVGAnimatedNumber> aNum;
145 0 : stopElement->GetOffset(getter_AddRefs(aNum));
146 :
147 0 : aNum->GetAnimVal(aOffset);
148 0 : if (*aOffset < 0.0f)
149 0 : *aOffset = 0.0f;
150 0 : else if (*aOffset > 1.0f)
151 0 : *aOffset = 1.0f;
152 : }
153 :
154 0 : *aStopColor = stopFrame->GetStyleSVGReset()->mStopColor;
155 0 : *aStopOpacity = stopFrame->GetStyleSVGReset()->mStopOpacity;
156 0 : }
157 :
158 : PRUint16
159 0 : nsSVGGradientFrame::GetEnumValue(PRUint32 aIndex, nsIContent *aDefault)
160 : {
161 : const nsSVGEnum& thisEnum =
162 0 : static_cast<nsSVGGradientElement *>(mContent)->mEnumAttributes[aIndex];
163 :
164 0 : if (thisEnum.IsExplicitlySet())
165 0 : return thisEnum.GetAnimValue();
166 :
167 0 : AutoGradientReferencer gradientRef(this);
168 :
169 0 : nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
170 : return next ? next->GetEnumValue(aIndex, aDefault) :
171 : static_cast<nsSVGGradientElement *>(aDefault)->
172 0 : mEnumAttributes[aIndex].GetAnimValue();
173 : }
174 :
175 : const SVGAnimatedTransformList*
176 0 : nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault)
177 : {
178 : SVGAnimatedTransformList *thisTransformList =
179 0 : static_cast<nsSVGGradientElement *>(mContent)->GetAnimatedTransformList();
180 :
181 0 : if (thisTransformList->IsExplicitlySet())
182 0 : return thisTransformList;
183 :
184 0 : AutoGradientReferencer gradientRef(this);
185 :
186 0 : nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
187 : return next ? next->GetGradientTransformList(aDefault) :
188 : static_cast<const nsSVGGradientElement *>(aDefault)
189 0 : ->mGradientTransform.get();
190 : }
191 :
192 : gfxMatrix
193 0 : nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource,
194 : const gfxRect *aOverrideBounds)
195 : {
196 0 : gfxMatrix bboxMatrix;
197 :
198 0 : PRUint16 gradientUnits = GetGradientUnits();
199 0 : if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
200 : // If this gradient is applied to text, our caller
201 : // will be the glyph, which is not a container, so we
202 : // need to get the parent
203 0 : if (aSource->GetContent()->IsNodeOfType(nsINode::eTEXT))
204 0 : mSource = aSource->GetParent();
205 : else
206 0 : mSource = aSource;
207 : } else {
208 0 : NS_ASSERTION(
209 : gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
210 : "Unknown gradientUnits type");
211 : // objectBoundingBox is the default anyway
212 :
213 : gfxRect bbox =
214 0 : aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource);
215 : bboxMatrix =
216 0 : gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
217 : }
218 :
219 : const SVGAnimatedTransformList* animTransformList =
220 0 : GetGradientTransformList(mContent);
221 0 : if (!animTransformList)
222 0 : return bboxMatrix;
223 :
224 : gfxMatrix gradientTransform =
225 0 : animTransformList->GetAnimValue().GetConsolidationMatrix();
226 0 : return bboxMatrix.PreMultiply(gradientTransform);
227 : }
228 :
229 : nsSVGLinearGradientElement *
230 0 : nsSVGGradientFrame::GetLinearGradientWithLength(PRUint32 aIndex,
231 : nsSVGLinearGradientElement* aDefault)
232 : {
233 : // If this was a linear gradient with the required length, we would have
234 : // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength.
235 : // Since we didn't find the length, continue looking down the chain.
236 :
237 0 : AutoGradientReferencer gradientRef(this);
238 :
239 0 : nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
240 0 : return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault;
241 : }
242 :
243 : nsSVGRadialGradientElement *
244 0 : nsSVGGradientFrame::GetRadialGradientWithLength(PRUint32 aIndex,
245 : nsSVGRadialGradientElement* aDefault)
246 : {
247 : // If this was a radial gradient with the required length, we would have
248 : // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength.
249 : // Since we didn't find the length, continue looking down the chain.
250 :
251 0 : AutoGradientReferencer gradientRef(this);
252 :
253 0 : nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
254 0 : return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault;
255 : }
256 :
257 : //----------------------------------------------------------------------
258 : // nsSVGPaintServerFrame methods:
259 :
260 : already_AddRefed<gfxPattern>
261 0 : nsSVGGradientFrame::GetPaintServerPattern(nsIFrame *aSource,
262 : float aGraphicOpacity,
263 : const gfxRect *aOverrideBounds)
264 : {
265 : // Get the transform list (if there is one)
266 0 : gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
267 :
268 0 : if (patternMatrix.IsSingular())
269 0 : return nsnull;
270 :
271 0 : PRUint32 nStops = GetStopCount();
272 :
273 : // SVG specification says that no stops should be treated like
274 : // the corresponding fill or stroke had "none" specified.
275 0 : if (nStops == 0) {
276 0 : nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0));
277 0 : return pattern.forget();
278 : }
279 :
280 0 : patternMatrix.Invert();
281 :
282 0 : nsRefPtr<gfxPattern> gradient = CreateGradient();
283 0 : if (!gradient || gradient->CairoStatus())
284 0 : return nsnull;
285 :
286 0 : PRUint16 aSpread = GetSpreadMethod();
287 0 : if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_PAD)
288 0 : gradient->SetExtend(gfxPattern::EXTEND_PAD);
289 0 : else if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_REFLECT)
290 0 : gradient->SetExtend(gfxPattern::EXTEND_REFLECT);
291 0 : else if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_REPEAT)
292 0 : gradient->SetExtend(gfxPattern::EXTEND_REPEAT);
293 :
294 0 : gradient->SetMatrix(patternMatrix);
295 :
296 : // setup stops
297 0 : float lastOffset = 0.0f;
298 :
299 0 : for (PRUint32 i = 0; i < nStops; i++) {
300 : float offset, stopOpacity;
301 : nscolor stopColor;
302 :
303 0 : GetStopInformation(i, &offset, &stopColor, &stopOpacity);
304 :
305 0 : if (offset < lastOffset)
306 0 : offset = lastOffset;
307 : else
308 0 : lastOffset = offset;
309 :
310 : gradient->AddColorStop(offset,
311 : gfxRGBA(NS_GET_R(stopColor)/255.0,
312 : NS_GET_G(stopColor)/255.0,
313 : NS_GET_B(stopColor)/255.0,
314 : NS_GET_A(stopColor)/255.0 *
315 0 : stopOpacity * aGraphicOpacity));
316 : }
317 :
318 0 : return gradient.forget();
319 : }
320 :
321 : // Private (helper) methods
322 :
323 : nsSVGGradientFrame *
324 0 : nsSVGGradientFrame::GetReferencedGradient()
325 : {
326 0 : if (mNoHRefURI)
327 0 : return nsnull;
328 :
329 : nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*>
330 0 : (Properties().Get(nsSVGEffects::HrefProperty()));
331 :
332 0 : if (!property) {
333 : // Fetch our gradient element's xlink:href attribute
334 0 : nsSVGGradientElement *grad = static_cast<nsSVGGradientElement *>(mContent);
335 0 : nsAutoString href;
336 0 : grad->mStringAttributes[nsSVGGradientElement::HREF].GetAnimValue(href, grad);
337 0 : if (href.IsEmpty()) {
338 0 : mNoHRefURI = true;
339 0 : return nsnull; // no URL
340 : }
341 :
342 : // Convert href to an nsIURI
343 0 : nsCOMPtr<nsIURI> targetURI;
344 0 : nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
345 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
346 0 : mContent->GetCurrentDoc(), base);
347 :
348 : property =
349 0 : nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty());
350 0 : if (!property)
351 0 : return nsnull;
352 : }
353 :
354 0 : nsIFrame *result = property->GetReferencedFrame();
355 0 : if (!result)
356 0 : return nsnull;
357 :
358 0 : nsIAtom* frameType = result->GetType();
359 0 : if (frameType != nsGkAtoms::svgLinearGradientFrame &&
360 : frameType != nsGkAtoms::svgRadialGradientFrame)
361 0 : return nsnull;
362 :
363 0 : return static_cast<nsSVGGradientFrame*>(result);
364 : }
365 :
366 : nsSVGGradientFrame *
367 0 : nsSVGGradientFrame::GetReferencedGradientIfNotInUse()
368 : {
369 0 : nsSVGGradientFrame *referenced = GetReferencedGradient();
370 0 : if (!referenced)
371 0 : return nsnull;
372 :
373 0 : if (referenced->mLoopFlag) {
374 : // XXXjwatt: we should really send an error to the JavaScript Console here:
375 0 : NS_WARNING("gradient reference loop detected while inheriting attribute!");
376 0 : return nsnull;
377 : }
378 :
379 0 : return referenced;
380 : }
381 :
382 : PRInt32
383 0 : nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame)
384 : {
385 0 : PRInt32 stopCount = 0;
386 0 : nsIFrame *stopFrame = nsnull;
387 0 : for (stopFrame = mFrames.FirstChild(); stopFrame;
388 : stopFrame = stopFrame->GetNextSibling()) {
389 0 : if (stopFrame->GetType() == nsGkAtoms::svgStopFrame) {
390 : // Is this the one we're looking for?
391 0 : if (stopCount++ == aIndex)
392 0 : break; // Yes, break out of the loop
393 : }
394 : }
395 0 : if (stopCount > 0) {
396 0 : if (aStopFrame)
397 0 : *aStopFrame = stopFrame;
398 0 : return stopCount;
399 : }
400 :
401 : // Our gradient element doesn't have stops - try to "inherit" them
402 :
403 0 : AutoGradientReferencer gradientRef(this);
404 0 : nsSVGGradientFrame* next = GetReferencedGradientIfNotInUse();
405 0 : if (!next)
406 0 : return nsnull;
407 :
408 0 : return next->GetStopFrame(aIndex, aStopFrame);
409 : }
410 :
411 : // -------------------------------------------------------------------------
412 : // Linear Gradients
413 : // -------------------------------------------------------------------------
414 :
415 : #ifdef DEBUG
416 : NS_IMETHODIMP
417 0 : nsSVGLinearGradientFrame::Init(nsIContent* aContent,
418 : nsIFrame* aParent,
419 : nsIFrame* aPrevInFlow)
420 : {
421 0 : nsCOMPtr<nsIDOMSVGLinearGradientElement> grad = do_QueryInterface(aContent);
422 0 : NS_ASSERTION(grad, "Content is not an SVG linearGradient");
423 :
424 0 : return nsSVGLinearGradientFrameBase::Init(aContent, aParent, aPrevInFlow);
425 : }
426 : #endif /* DEBUG */
427 :
428 : nsIAtom*
429 0 : nsSVGLinearGradientFrame::GetType() const
430 : {
431 0 : return nsGkAtoms::svgLinearGradientFrame;
432 : }
433 :
434 : NS_IMETHODIMP
435 0 : nsSVGLinearGradientFrame::AttributeChanged(PRInt32 aNameSpaceID,
436 : nsIAtom* aAttribute,
437 : PRInt32 aModType)
438 : {
439 0 : if (aNameSpaceID == kNameSpaceID_None &&
440 : (aAttribute == nsGkAtoms::x1 ||
441 : aAttribute == nsGkAtoms::y1 ||
442 : aAttribute == nsGkAtoms::x2 ||
443 : aAttribute == nsGkAtoms::y2)) {
444 0 : nsSVGEffects::InvalidateRenderingObservers(this);
445 : }
446 :
447 : return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
448 0 : aAttribute, aModType);
449 : }
450 :
451 : //----------------------------------------------------------------------
452 :
453 : float
454 0 : nsSVGLinearGradientFrame::GetLengthValue(PRUint32 aIndex)
455 : {
456 : nsSVGLinearGradientElement* lengthElement =
457 : GetLinearGradientWithLength(aIndex,
458 0 : static_cast<nsSVGLinearGradientElement *>(mContent));
459 : // We passed in mContent as a fallback, so, assuming mContent is non-null, the
460 : // return value should also be non-null.
461 0 : NS_ABORT_IF_FALSE(lengthElement,
462 : "Got unexpected null element from GetLinearGradientWithLength");
463 0 : const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex];
464 :
465 : // Object bounding box units are handled by setting the appropriate
466 : // transform in GetGradientTransform, but we need to handle user
467 : // space units as part of the individual Get* routines. Fixes 323669.
468 :
469 0 : PRUint16 gradientUnits = GetGradientUnits();
470 0 : if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
471 0 : return nsSVGUtils::UserSpace(mSource, &length);
472 : }
473 :
474 0 : NS_ASSERTION(
475 : gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
476 : "Unknown gradientUnits type");
477 :
478 0 : return length.GetAnimValue(static_cast<nsSVGSVGElement*>(nsnull));
479 : }
480 :
481 : nsSVGLinearGradientElement *
482 0 : nsSVGLinearGradientFrame::GetLinearGradientWithLength(PRUint32 aIndex,
483 : nsSVGLinearGradientElement* aDefault)
484 : {
485 : nsSVGLinearGradientElement* thisElement =
486 0 : static_cast<nsSVGLinearGradientElement *>(mContent);
487 0 : const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
488 :
489 0 : if (length.IsExplicitlySet()) {
490 0 : return thisElement;
491 : }
492 :
493 : return nsSVGLinearGradientFrameBase::GetLinearGradientWithLength(aIndex,
494 0 : aDefault);
495 : }
496 :
497 : already_AddRefed<gfxPattern>
498 0 : nsSVGLinearGradientFrame::CreateGradient()
499 : {
500 : float x1, y1, x2, y2;
501 :
502 0 : x1 = GetLengthValue(nsSVGLinearGradientElement::X1);
503 0 : y1 = GetLengthValue(nsSVGLinearGradientElement::Y1);
504 0 : x2 = GetLengthValue(nsSVGLinearGradientElement::X2);
505 0 : y2 = GetLengthValue(nsSVGLinearGradientElement::Y2);
506 :
507 0 : gfxPattern *pattern = new gfxPattern(x1, y1, x2, y2);
508 0 : NS_IF_ADDREF(pattern);
509 0 : return pattern;
510 : }
511 :
512 : // -------------------------------------------------------------------------
513 : // Radial Gradients
514 : // -------------------------------------------------------------------------
515 :
516 : #ifdef DEBUG
517 : NS_IMETHODIMP
518 0 : nsSVGRadialGradientFrame::Init(nsIContent* aContent,
519 : nsIFrame* aParent,
520 : nsIFrame* aPrevInFlow)
521 : {
522 0 : nsCOMPtr<nsIDOMSVGRadialGradientElement> grad = do_QueryInterface(aContent);
523 0 : NS_ASSERTION(grad, "Content is not an SVG radialGradient");
524 :
525 0 : return nsSVGRadialGradientFrameBase::Init(aContent, aParent, aPrevInFlow);
526 : }
527 : #endif /* DEBUG */
528 :
529 : nsIAtom*
530 0 : nsSVGRadialGradientFrame::GetType() const
531 : {
532 0 : return nsGkAtoms::svgRadialGradientFrame;
533 : }
534 :
535 : NS_IMETHODIMP
536 0 : nsSVGRadialGradientFrame::AttributeChanged(PRInt32 aNameSpaceID,
537 : nsIAtom* aAttribute,
538 : PRInt32 aModType)
539 : {
540 0 : if (aNameSpaceID == kNameSpaceID_None &&
541 : (aAttribute == nsGkAtoms::r ||
542 : aAttribute == nsGkAtoms::cx ||
543 : aAttribute == nsGkAtoms::cy ||
544 : aAttribute == nsGkAtoms::fx ||
545 : aAttribute == nsGkAtoms::fy)) {
546 0 : nsSVGEffects::InvalidateRenderingObservers(this);
547 : }
548 :
549 : return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
550 0 : aAttribute, aModType);
551 : }
552 :
553 : //----------------------------------------------------------------------
554 :
555 : float
556 0 : nsSVGRadialGradientFrame::GetLengthValue(PRUint32 aIndex)
557 : {
558 : nsSVGRadialGradientElement* lengthElement =
559 : GetRadialGradientWithLength(aIndex,
560 0 : static_cast<nsSVGRadialGradientElement *>(mContent));
561 : // We passed in mContent as a fallback, so, assuming mContent is non-null,
562 : // the return value should also be non-null.
563 0 : NS_ABORT_IF_FALSE(lengthElement,
564 : "Got unexpected null element from GetRadialGradientWithLength");
565 0 : return GetLengthValueFromElement(aIndex, *lengthElement);
566 : }
567 :
568 : float
569 0 : nsSVGRadialGradientFrame::GetLengthValue(PRUint32 aIndex, float aDefaultValue)
570 : {
571 : nsSVGRadialGradientElement* lengthElement =
572 0 : GetRadialGradientWithLength(aIndex, nsnull);
573 :
574 : return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement)
575 0 : : aDefaultValue;
576 : }
577 :
578 : float
579 0 : nsSVGRadialGradientFrame::GetLengthValueFromElement(PRUint32 aIndex,
580 : nsSVGRadialGradientElement& aElement)
581 : {
582 0 : const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex];
583 :
584 : // Object bounding box units are handled by setting the appropriate
585 : // transform in GetGradientTransform, but we need to handle user
586 : // space units as part of the individual Get* routines. Fixes 323669.
587 :
588 0 : PRUint16 gradientUnits = GetGradientUnits();
589 0 : if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
590 0 : return nsSVGUtils::UserSpace(mSource, &length);
591 : }
592 :
593 0 : NS_ASSERTION(
594 : gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
595 : "Unknown gradientUnits type");
596 :
597 0 : return length.GetAnimValue(static_cast<nsSVGSVGElement*>(nsnull));
598 : }
599 :
600 : nsSVGRadialGradientElement *
601 0 : nsSVGRadialGradientFrame::GetRadialGradientWithLength(PRUint32 aIndex,
602 : nsSVGRadialGradientElement* aDefault)
603 : {
604 : nsSVGRadialGradientElement* thisElement =
605 0 : static_cast<nsSVGRadialGradientElement *>(mContent);
606 0 : const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
607 :
608 0 : if (length.IsExplicitlySet()) {
609 0 : return thisElement;
610 : }
611 :
612 : return nsSVGRadialGradientFrameBase::GetRadialGradientWithLength(aIndex,
613 0 : aDefault);
614 : }
615 :
616 : already_AddRefed<gfxPattern>
617 0 : nsSVGRadialGradientFrame::CreateGradient()
618 : {
619 : float cx, cy, r, fx, fy;
620 :
621 0 : cx = GetLengthValue(nsSVGRadialGradientElement::CX);
622 0 : cy = GetLengthValue(nsSVGRadialGradientElement::CY);
623 0 : r = GetLengthValue(nsSVGRadialGradientElement::R);
624 : // If fx or fy are not set, use cx/cy instead
625 0 : fx = GetLengthValue(nsSVGRadialGradientElement::FX, cx);
626 0 : fy = GetLengthValue(nsSVGRadialGradientElement::FY, cy);
627 :
628 0 : if (fx != cx || fy != cy) {
629 : // The focal point (fFx and fFy) must be clamped to be *inside* - not on -
630 : // the circumference of the gradient or we'll get rendering anomalies. We
631 : // calculate the distance from the focal point to the gradient center and
632 : // make sure it is *less* than the gradient radius.
633 : // 1/128 is the limit of the fractional part of cairo's 24.8 fixed point
634 : // representation divided by 2 to ensure that we get different cairo
635 : // fractions
636 0 : double dMax = NS_MAX(0.0, r - 1.0/128);
637 0 : float dx = fx - cx;
638 0 : float dy = fy - cy;
639 0 : double d = sqrt((dx * dx) + (dy * dy));
640 0 : if (d > dMax) {
641 0 : double angle = atan2(dy, dx);
642 0 : fx = (float)(dMax * cos(angle)) + cx;
643 0 : fy = (float)(dMax * sin(angle)) + cy;
644 : }
645 : }
646 :
647 0 : gfxPattern *pattern = new gfxPattern(fx, fy, 0, cx, cy, r);
648 0 : NS_IF_ADDREF(pattern);
649 0 : return pattern;
650 : }
651 :
652 : // -------------------------------------------------------------------------
653 : // Public functions
654 : // -------------------------------------------------------------------------
655 :
656 : nsIFrame*
657 0 : NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell,
658 : nsStyleContext* aContext)
659 : {
660 0 : return new (aPresShell) nsSVGLinearGradientFrame(aContext);
661 : }
662 :
663 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame)
664 :
665 : nsIFrame*
666 0 : NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell,
667 : nsStyleContext* aContext)
668 : {
669 0 : return new (aPresShell) nsSVGRadialGradientFrame(aContext);
670 : }
671 :
672 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame)
|