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) 2005
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Scooter Morris <scootermorris@comcast.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsSVGPatternFrame.h"
40 :
41 : #include "nsGkAtoms.h"
42 : #include "nsIDOMSVGAnimatedRect.h"
43 : #include "SVGAnimatedTransformList.h"
44 : #include "nsStyleContext.h"
45 : #include "nsINameSpaceManager.h"
46 : #include "nsISVGChildFrame.h"
47 : #include "nsSVGRect.h"
48 : #include "nsSVGUtils.h"
49 : #include "nsSVGEffects.h"
50 : #include "nsSVGOuterSVGFrame.h"
51 : #include "nsSVGPatternElement.h"
52 : #include "nsSVGGeometryFrame.h"
53 : #include "gfxContext.h"
54 : #include "gfxPlatform.h"
55 : #include "gfxPattern.h"
56 : #include "gfxMatrix.h"
57 : #include "nsContentUtils.h"
58 :
59 : using namespace mozilla;
60 :
61 : //----------------------------------------------------------------------
62 : // Helper classes
63 :
64 : class nsSVGPatternFrame::AutoPatternReferencer
65 : {
66 : public:
67 0 : AutoPatternReferencer(nsSVGPatternFrame *aFrame)
68 0 : : mFrame(aFrame)
69 : {
70 : // Reference loops should normally be detected in advance and handled, so
71 : // we're not expecting to encounter them here
72 0 : NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!");
73 0 : mFrame->mLoopFlag = true;
74 0 : }
75 0 : ~AutoPatternReferencer() {
76 0 : mFrame->mLoopFlag = false;
77 0 : }
78 : private:
79 : nsSVGPatternFrame *mFrame;
80 : };
81 :
82 : //----------------------------------------------------------------------
83 : // Implementation
84 :
85 0 : nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext) :
86 : nsSVGPatternFrameBase(aContext),
87 : mLoopFlag(false),
88 0 : mNoHRefURI(false)
89 : {
90 0 : }
91 :
92 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame)
93 :
94 : //----------------------------------------------------------------------
95 : // nsIFrame methods:
96 :
97 : /* virtual */ void
98 0 : nsSVGPatternFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
99 : {
100 0 : nsSVGEffects::InvalidateRenderingObservers(this);
101 0 : nsSVGPatternFrameBase::DidSetStyleContext(aOldStyleContext);
102 0 : }
103 :
104 : NS_IMETHODIMP
105 0 : nsSVGPatternFrame::AttributeChanged(PRInt32 aNameSpaceID,
106 : nsIAtom* aAttribute,
107 : PRInt32 aModType)
108 : {
109 0 : if (aNameSpaceID == kNameSpaceID_None &&
110 : (aAttribute == nsGkAtoms::patternUnits ||
111 : aAttribute == nsGkAtoms::patternContentUnits ||
112 : aAttribute == nsGkAtoms::patternTransform ||
113 : aAttribute == nsGkAtoms::x ||
114 : aAttribute == nsGkAtoms::y ||
115 : aAttribute == nsGkAtoms::width ||
116 : aAttribute == nsGkAtoms::height ||
117 : aAttribute == nsGkAtoms::preserveAspectRatio ||
118 : aAttribute == nsGkAtoms::viewBox)) {
119 0 : nsSVGEffects::InvalidateRenderingObservers(this);
120 : }
121 :
122 0 : if (aNameSpaceID == kNameSpaceID_XLink &&
123 : aAttribute == nsGkAtoms::href) {
124 : // Blow away our reference, if any
125 0 : Properties().Delete(nsSVGEffects::HrefProperty());
126 0 : mNoHRefURI = false;
127 : // And update whoever references us
128 0 : nsSVGEffects::InvalidateRenderingObservers(this);
129 : }
130 :
131 : return nsSVGPatternFrameBase::AttributeChanged(aNameSpaceID,
132 0 : aAttribute, aModType);
133 : }
134 :
135 : #ifdef DEBUG
136 : NS_IMETHODIMP
137 0 : nsSVGPatternFrame::Init(nsIContent* aContent,
138 : nsIFrame* aParent,
139 : nsIFrame* aPrevInFlow)
140 : {
141 0 : nsCOMPtr<nsIDOMSVGPatternElement> patternElement = do_QueryInterface(aContent);
142 0 : NS_ASSERTION(patternElement, "Content is not an SVG pattern");
143 :
144 0 : return nsSVGPatternFrameBase::Init(aContent, aParent, aPrevInFlow);
145 : }
146 : #endif /* DEBUG */
147 :
148 : nsIAtom*
149 0 : nsSVGPatternFrame::GetType() const
150 : {
151 0 : return nsGkAtoms::svgPatternFrame;
152 : }
153 :
154 : //----------------------------------------------------------------------
155 : // nsSVGContainerFrame methods:
156 :
157 : // If our GetCanvasTM is getting called, we
158 : // need to return *our current* transformation
159 : // matrix, which depends on our units parameters
160 : // and X, Y, Width, and Height
161 : gfxMatrix
162 0 : nsSVGPatternFrame::GetCanvasTM()
163 : {
164 0 : if (mCTM) {
165 0 : return *mCTM;
166 : }
167 :
168 : // Do we know our rendering parent?
169 0 : if (mSource) {
170 : // Yes, use it!
171 0 : return mSource->GetCanvasTM();
172 : }
173 :
174 : // We get here when geometry in the <pattern> container is updated
175 0 : return gfxMatrix();
176 : }
177 :
178 : nsresult
179 0 : nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
180 : gfxMatrix* patternMatrix,
181 : nsIFrame *aSource,
182 : float aGraphicOpacity,
183 : const gfxRect *aOverrideBounds)
184 : {
185 : /*
186 : * General approach:
187 : * Set the content geometry stuff
188 : * Calculate our bbox (using x,y,width,height & patternUnits &
189 : * patternTransform)
190 : * Create the surface
191 : * Calculate the content transformation matrix
192 : * Get our children (we may need to get them from another Pattern)
193 : * Call SVGPaint on all of our children
194 : * Return
195 : */
196 0 : *surface = nsnull;
197 :
198 : // Get the first child of the pattern data we will render
199 0 : nsIFrame* firstKid = GetPatternFirstChild();
200 0 : if (!firstKid)
201 0 : return NS_ERROR_FAILURE; // Either no kids or a bad reference
202 :
203 : /*
204 : * Get the content geometry information. This is a little tricky --
205 : * our parent is probably a <defs>, but we are rendering in the context
206 : * of some geometry source. Our content geometry information needs to
207 : * come from our rendering parent as opposed to our content parent. We
208 : * get that information from aSource, which is passed to us from the
209 : * backend renderer.
210 : *
211 : * There are three "geometries" that we need:
212 : * 1) The bounding box for the pattern. We use this to get the
213 : * width and height for the surface, and as the return to
214 : * GetBBox.
215 : * 2) The transformation matrix for the pattern. This is not *quite*
216 : * the same as the canvas transformation matrix that we will
217 : * provide to our rendering children since we "fudge" it a little
218 : * to get the renderer to handle the translations correctly for us.
219 : * 3) The CTM that we return to our children who make up the pattern.
220 : */
221 :
222 : // Get all of the information we need from our "caller" -- i.e.
223 : // the geometry that is being rendered with a pattern
224 0 : gfxRect callerBBox;
225 0 : gfxMatrix callerCTM;
226 0 : if (NS_FAILED(GetTargetGeometry(&callerCTM,
227 : &callerBBox,
228 : aSource,
229 : aOverrideBounds)))
230 0 : return NS_ERROR_FAILURE;
231 :
232 : // Construct the CTM that we will provide to our children when we
233 : // render them into the tile.
234 0 : gfxMatrix ctm = ConstructCTM(callerBBox, callerCTM, aSource);
235 0 : if (ctm.IsSingular()) {
236 0 : return NS_ERROR_FAILURE;
237 : }
238 :
239 : // Get the pattern we are going to render
240 : nsSVGPatternFrame *patternFrame =
241 0 : static_cast<nsSVGPatternFrame*>(firstKid->GetParent());
242 0 : if (patternFrame->mCTM) {
243 0 : *patternFrame->mCTM = ctm;
244 : } else {
245 0 : patternFrame->mCTM = new gfxMatrix(ctm);
246 : }
247 :
248 : // Get the bounding box of the pattern. This will be used to determine
249 : // the size of the surface, and will also be used to define the bounding
250 : // box for the pattern tile.
251 0 : gfxRect bbox = GetPatternRect(callerBBox, callerCTM, aSource);
252 :
253 : // Get the pattern transform
254 0 : gfxMatrix patternTransform = GetPatternTransform();
255 :
256 : // Get the transformation matrix that we will hand to the renderer's pattern
257 : // routine.
258 : *patternMatrix = GetPatternMatrix(patternTransform,
259 0 : bbox, callerBBox, callerCTM);
260 :
261 : // Now that we have all of the necessary geometries, we can
262 : // create our surface.
263 0 : gfxFloat patternWidth = bbox.Width();
264 0 : gfxFloat patternHeight = bbox.Height();
265 :
266 : bool resultOverflows;
267 : gfxIntSize surfaceSize =
268 : nsSVGUtils::ConvertToSurfaceSize(
269 0 : gfxSize(patternWidth * fabs(patternTransform.xx),
270 0 : patternHeight * fabs(patternTransform.yy)),
271 0 : &resultOverflows);
272 :
273 : // 0 disables rendering, < 0 is an error
274 0 : if (surfaceSize.width <= 0 || surfaceSize.height <= 0)
275 0 : return NS_ERROR_FAILURE;
276 :
277 0 : if (resultOverflows ||
278 : patternWidth != surfaceSize.width ||
279 : patternHeight != surfaceSize.height) {
280 : // scale drawing to pattern surface size
281 : gfxMatrix tempTM =
282 : gfxMatrix(surfaceSize.width / patternWidth, 0.0f,
283 : 0.0f, surfaceSize.height / patternHeight,
284 0 : 0.0f, 0.0f);
285 0 : patternFrame->mCTM->PreMultiply(tempTM);
286 :
287 : // and rescale pattern to compensate
288 : patternMatrix->Scale(patternWidth / surfaceSize.width,
289 0 : patternHeight / surfaceSize.height);
290 : }
291 :
292 : nsRefPtr<gfxASurface> tmpSurface =
293 0 : gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize,
294 0 : gfxASurface::CONTENT_COLOR_ALPHA);
295 0 : if (!tmpSurface || tmpSurface->CairoStatus())
296 0 : return NS_ERROR_FAILURE;
297 :
298 0 : nsRenderingContext context;
299 0 : context.Init(aSource->PresContext()->DeviceContext(), tmpSurface);
300 0 : gfxContext* gfx = context.ThebesContext();
301 :
302 : // Fill with transparent black
303 0 : gfx->SetOperator(gfxContext::OPERATOR_CLEAR);
304 0 : gfx->Paint();
305 0 : gfx->SetOperator(gfxContext::OPERATOR_OVER);
306 :
307 0 : if (aGraphicOpacity != 1.0f) {
308 0 : gfx->Save();
309 0 : gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
310 : }
311 :
312 : // OK, now render -- note that we use "firstKid", which
313 : // we got at the beginning because it takes care of the
314 : // referenced pattern situation for us
315 :
316 0 : if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
317 : // Set the geometrical parent of the pattern we are rendering
318 0 : patternFrame->mSource = static_cast<nsSVGGeometryFrame*>(aSource);
319 : }
320 :
321 : // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
322 : // give back a clear surface if there's a loop
323 0 : if (!(patternFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) {
324 0 : patternFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
325 0 : for (nsIFrame* kid = firstKid; kid;
326 : kid = kid->GetNextSibling()) {
327 : // The CTM of each frame referencing us can be different
328 0 : nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
329 0 : if (SVGFrame) {
330 : SVGFrame->NotifySVGChanged(
331 : nsISVGChildFrame::DO_NOT_NOTIFY_RENDERING_OBSERVERS |
332 0 : nsISVGChildFrame::TRANSFORM_CHANGED);
333 : }
334 0 : nsSVGUtils::PaintFrameWithEffects(&context, nsnull, kid);
335 : }
336 0 : patternFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
337 : }
338 :
339 0 : patternFrame->mSource = nsnull;
340 :
341 0 : if (aGraphicOpacity != 1.0f) {
342 0 : gfx->PopGroupToSource();
343 0 : gfx->Paint(aGraphicOpacity);
344 0 : gfx->Restore();
345 : }
346 :
347 : // caller now owns the surface
348 0 : tmpSurface.forget(surface);
349 0 : return NS_OK;
350 : }
351 :
352 : /* Will probably need something like this... */
353 : // How do we handle the insertion of a new frame?
354 : // We really don't want to rerender this every time,
355 : // do we?
356 : nsIFrame*
357 0 : nsSVGPatternFrame::GetPatternFirstChild()
358 : {
359 : // Do we have any children ourselves?
360 0 : nsIFrame* kid = mFrames.FirstChild();
361 0 : if (kid)
362 0 : return kid;
363 :
364 : // No, see if we chain to someone who does
365 0 : AutoPatternReferencer patternRef(this);
366 :
367 0 : nsSVGPatternFrame* next = GetReferencedPatternIfNotInUse();
368 0 : if (!next)
369 0 : return nsnull;
370 :
371 0 : return next->GetPatternFirstChild();
372 : }
373 :
374 : PRUint16
375 0 : nsSVGPatternFrame::GetEnumValue(PRUint32 aIndex, nsIContent *aDefault)
376 : {
377 : nsSVGEnum& thisEnum =
378 0 : static_cast<nsSVGPatternElement *>(mContent)->mEnumAttributes[aIndex];
379 :
380 0 : if (thisEnum.IsExplicitlySet())
381 0 : return thisEnum.GetAnimValue();
382 :
383 0 : AutoPatternReferencer patternRef(this);
384 :
385 0 : nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
386 : return next ? next->GetEnumValue(aIndex, aDefault) :
387 : static_cast<nsSVGPatternElement *>(aDefault)->
388 0 : mEnumAttributes[aIndex].GetAnimValue();
389 : }
390 :
391 : SVGAnimatedTransformList*
392 0 : nsSVGPatternFrame::GetPatternTransformList(nsIContent* aDefault)
393 : {
394 : SVGAnimatedTransformList *thisTransformList =
395 0 : static_cast<nsSVGPatternElement *>(mContent)->GetAnimatedTransformList();
396 :
397 0 : if (thisTransformList->IsExplicitlySet())
398 0 : return thisTransformList;
399 :
400 0 : AutoPatternReferencer patternRef(this);
401 :
402 0 : nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
403 : return next ? next->GetPatternTransformList(aDefault) :
404 0 : static_cast<nsSVGPatternElement *>(aDefault)->mPatternTransform.get();
405 : }
406 :
407 : gfxMatrix
408 0 : nsSVGPatternFrame::GetPatternTransform()
409 : {
410 : SVGAnimatedTransformList* animTransformList =
411 0 : GetPatternTransformList(mContent);
412 0 : if (!animTransformList)
413 0 : return gfxMatrix();
414 :
415 0 : return animTransformList->GetAnimValue().GetConsolidationMatrix();
416 : }
417 :
418 : const nsSVGViewBox &
419 0 : nsSVGPatternFrame::GetViewBox(nsIContent* aDefault)
420 : {
421 : const nsSVGViewBox &thisViewBox =
422 0 : static_cast<nsSVGPatternElement *>(mContent)->mViewBox;
423 :
424 0 : if (thisViewBox.IsValid())
425 0 : return thisViewBox;
426 :
427 0 : AutoPatternReferencer patternRef(this);
428 :
429 0 : nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
430 : return next ? next->GetViewBox(aDefault) :
431 0 : static_cast<nsSVGPatternElement *>(aDefault)->mViewBox;
432 : }
433 :
434 : const SVGAnimatedPreserveAspectRatio &
435 0 : nsSVGPatternFrame::GetPreserveAspectRatio(nsIContent *aDefault)
436 : {
437 : const SVGAnimatedPreserveAspectRatio &thisPar =
438 0 : static_cast<nsSVGPatternElement *>(mContent)->mPreserveAspectRatio;
439 :
440 0 : if (thisPar.IsExplicitlySet())
441 0 : return thisPar;
442 :
443 0 : AutoPatternReferencer patternRef(this);
444 :
445 0 : nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
446 : return next ? next->GetPreserveAspectRatio(aDefault) :
447 0 : static_cast<nsSVGPatternElement *>(aDefault)->mPreserveAspectRatio;
448 : }
449 :
450 : const nsSVGLength2 *
451 0 : nsSVGPatternFrame::GetLengthValue(PRUint32 aIndex, nsIContent *aDefault)
452 : {
453 : const nsSVGLength2 *thisLength =
454 0 : &static_cast<nsSVGPatternElement *>(mContent)->mLengthAttributes[aIndex];
455 :
456 0 : if (thisLength->IsExplicitlySet())
457 0 : return thisLength;
458 :
459 0 : AutoPatternReferencer patternRef(this);
460 :
461 0 : nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
462 : return next ? next->GetLengthValue(aIndex, aDefault) :
463 0 : &static_cast<nsSVGPatternElement *>(aDefault)->mLengthAttributes[aIndex];
464 : }
465 :
466 : // Private (helper) methods
467 : nsSVGPatternFrame *
468 0 : nsSVGPatternFrame::GetReferencedPattern()
469 : {
470 0 : if (mNoHRefURI)
471 0 : return nsnull;
472 :
473 : nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*>
474 0 : (Properties().Get(nsSVGEffects::HrefProperty()));
475 :
476 0 : if (!property) {
477 : // Fetch our pattern element's xlink:href attribute
478 0 : nsSVGPatternElement *pattern = static_cast<nsSVGPatternElement *>(mContent);
479 0 : nsAutoString href;
480 0 : pattern->mStringAttributes[nsSVGPatternElement::HREF].GetAnimValue(href, pattern);
481 0 : if (href.IsEmpty()) {
482 0 : mNoHRefURI = true;
483 0 : return nsnull; // no URL
484 : }
485 :
486 : // Convert href to an nsIURI
487 0 : nsCOMPtr<nsIURI> targetURI;
488 0 : nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
489 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
490 0 : mContent->GetCurrentDoc(), base);
491 :
492 : property =
493 0 : nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty());
494 0 : if (!property)
495 0 : return nsnull;
496 : }
497 :
498 0 : nsIFrame *result = property->GetReferencedFrame();
499 0 : if (!result)
500 0 : return nsnull;
501 :
502 0 : nsIAtom* frameType = result->GetType();
503 0 : if (frameType != nsGkAtoms::svgPatternFrame)
504 0 : return nsnull;
505 :
506 0 : return static_cast<nsSVGPatternFrame*>(result);
507 : }
508 :
509 : nsSVGPatternFrame *
510 0 : nsSVGPatternFrame::GetReferencedPatternIfNotInUse()
511 : {
512 0 : nsSVGPatternFrame *referenced = GetReferencedPattern();
513 0 : if (!referenced)
514 0 : return nsnull;
515 :
516 0 : if (referenced->mLoopFlag) {
517 : // XXXjwatt: we should really send an error to the JavaScript Console here:
518 0 : NS_WARNING("pattern reference loop detected while inheriting attribute!");
519 0 : return nsnull;
520 : }
521 :
522 0 : return referenced;
523 : }
524 :
525 : // -------------------------------------------------------------------------
526 : // Helper functions
527 : // -------------------------------------------------------------------------
528 :
529 : gfxRect
530 0 : nsSVGPatternFrame::GetPatternRect(const gfxRect &aTargetBBox,
531 : const gfxMatrix &aTargetCTM,
532 : nsIFrame *aTarget)
533 : {
534 : // Get our type
535 0 : PRUint16 type = GetEnumValue(nsSVGPatternElement::PATTERNUNITS);
536 :
537 : // We need to initialize our box
538 : float x,y,width,height;
539 :
540 : // Get the pattern x,y,width, and height
541 : const nsSVGLength2 *tmpX, *tmpY, *tmpHeight, *tmpWidth;
542 0 : tmpX = GetLengthValue(nsSVGPatternElement::X);
543 0 : tmpY = GetLengthValue(nsSVGPatternElement::Y);
544 0 : tmpHeight = GetLengthValue(nsSVGPatternElement::HEIGHT);
545 0 : tmpWidth = GetLengthValue(nsSVGPatternElement::WIDTH);
546 :
547 0 : if (type == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
548 0 : x = nsSVGUtils::ObjectSpace(aTargetBBox, tmpX);
549 0 : y = nsSVGUtils::ObjectSpace(aTargetBBox, tmpY);
550 0 : width = nsSVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
551 0 : height = nsSVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
552 : } else {
553 0 : float scale = nsSVGUtils::MaxExpansion(aTargetCTM);
554 0 : x = nsSVGUtils::UserSpace(aTarget, tmpX) * scale;
555 0 : y = nsSVGUtils::UserSpace(aTarget, tmpY) * scale;
556 0 : width = nsSVGUtils::UserSpace(aTarget, tmpWidth) * scale;
557 0 : height = nsSVGUtils::UserSpace(aTarget, tmpHeight) * scale;
558 : }
559 :
560 0 : return gfxRect(x, y, width, height);
561 : }
562 :
563 : gfxMatrix
564 0 : nsSVGPatternFrame::ConstructCTM(const gfxRect &callerBBox,
565 : const gfxMatrix &callerCTM,
566 : nsIFrame *aTarget)
567 : {
568 0 : gfxMatrix tCTM;
569 0 : nsSVGSVGElement *ctx = nsnull;
570 0 : nsIContent* targetContent = aTarget->GetContent();
571 :
572 : // The objectBoundingBox conversion must be handled in the CTM:
573 0 : if (GetEnumValue(nsSVGPatternElement::PATTERNCONTENTUNITS) ==
574 : nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
575 0 : tCTM.Scale(callerBBox.Width(), callerBBox.Height());
576 : } else {
577 0 : if (targetContent->IsSVG()) {
578 0 : ctx = static_cast<nsSVGElement*>(targetContent)->GetCtx();
579 : }
580 0 : float scale = nsSVGUtils::MaxExpansion(callerCTM);
581 0 : tCTM.Scale(scale, scale);
582 : }
583 :
584 0 : const nsSVGViewBoxRect viewBox = GetViewBox().GetAnimValue();
585 :
586 0 : if (viewBox.height <= 0.0f || viewBox.width <= 0.0f) {
587 0 : return tCTM;
588 : }
589 :
590 : float viewportWidth, viewportHeight;
591 0 : if (targetContent->IsSVG()) {
592 : // If we're dealing with an SVG target only retrieve the context once.
593 : // Calling the nsIFrame* variant of GetAnimValue would look it up on
594 : // every call.
595 : viewportWidth =
596 0 : GetLengthValue(nsSVGPatternElement::WIDTH)->GetAnimValue(ctx);
597 : viewportHeight =
598 0 : GetLengthValue(nsSVGPatternElement::HEIGHT)->GetAnimValue(ctx);
599 : } else {
600 : // No SVG target, call the nsIFrame* variant of GetAnimValue.
601 : viewportWidth =
602 0 : GetLengthValue(nsSVGPatternElement::WIDTH)->GetAnimValue(aTarget);
603 : viewportHeight =
604 0 : GetLengthValue(nsSVGPatternElement::HEIGHT)->GetAnimValue(aTarget);
605 : }
606 : gfxMatrix tm = nsSVGUtils::GetViewBoxTransform(
607 : static_cast<nsSVGPatternElement*>(mContent),
608 : viewportWidth, viewportHeight,
609 : viewBox.x, viewBox.y,
610 : viewBox.width, viewBox.height,
611 0 : GetPreserveAspectRatio());
612 :
613 0 : return tm * tCTM;
614 : }
615 :
616 : // Given the matrix for the pattern element's own transform, this returns a
617 : // combined matrix including the transforms applicable to its target.
618 : gfxMatrix
619 0 : nsSVGPatternFrame::GetPatternMatrix(const gfxMatrix &patternTransform,
620 : const gfxRect &bbox,
621 : const gfxRect &callerBBox,
622 : const gfxMatrix &callerCTM)
623 : {
624 : // We really want the pattern matrix to handle translations
625 0 : gfxFloat minx = bbox.X();
626 0 : gfxFloat miny = bbox.Y();
627 :
628 0 : PRUint16 type = GetEnumValue(nsSVGPatternElement::PATTERNCONTENTUNITS);
629 0 : if (type == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
630 0 : minx += callerBBox.X();
631 0 : miny += callerBBox.Y();
632 : }
633 :
634 0 : float scale = 1.0f / nsSVGUtils::MaxExpansion(callerCTM);
635 0 : gfxMatrix patternMatrix = patternTransform;
636 0 : patternMatrix.Scale(scale, scale);
637 0 : patternMatrix.Translate(gfxPoint(minx, miny));
638 :
639 : return patternMatrix;
640 : }
641 :
642 : nsresult
643 0 : nsSVGPatternFrame::GetTargetGeometry(gfxMatrix *aCTM,
644 : gfxRect *aBBox,
645 : nsIFrame *aTarget,
646 : const gfxRect *aOverrideBounds)
647 : {
648 0 : *aBBox = aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aTarget);
649 :
650 : // Sanity check
651 0 : PRUint16 type = GetEnumValue(nsSVGPatternElement::PATTERNUNITS);
652 0 : if (type == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
653 0 : if (aBBox->Width() <= 0 || aBBox->Height() <= 0) {
654 0 : return NS_ERROR_FAILURE;
655 : }
656 : }
657 :
658 : // Get the transformation matrix from our calling geometry
659 0 : *aCTM = nsSVGUtils::GetCanvasTM(aTarget);
660 :
661 : // OK, now fix up the bounding box to reflect user coordinates
662 : // We handle device unit scaling in pattern matrix
663 : {
664 0 : float scale = nsSVGUtils::MaxExpansion(*aCTM);
665 0 : if (scale <= 0) {
666 0 : return NS_ERROR_FAILURE;
667 : }
668 0 : aBBox->Scale(scale);
669 : }
670 0 : return NS_OK;
671 : }
672 :
673 : //----------------------------------------------------------------------
674 : // nsSVGPaintServerFrame methods:
675 :
676 : already_AddRefed<gfxPattern>
677 0 : nsSVGPatternFrame::GetPaintServerPattern(nsIFrame *aSource,
678 : float aGraphicOpacity,
679 : const gfxRect *aOverrideBounds)
680 : {
681 0 : if (aGraphicOpacity == 0.0f) {
682 0 : nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0));
683 0 : return pattern.forget();
684 : }
685 :
686 : // Paint it!
687 0 : nsRefPtr<gfxASurface> surface;
688 0 : gfxMatrix pMatrix;
689 : nsresult rv = PaintPattern(getter_AddRefs(surface), &pMatrix,
690 0 : aSource, aGraphicOpacity, aOverrideBounds);
691 :
692 0 : if (NS_FAILED(rv)) {
693 0 : return nsnull;
694 : }
695 :
696 0 : if (pMatrix.IsSingular()) {
697 0 : return nsnull;
698 : }
699 :
700 0 : pMatrix.Invert();
701 :
702 0 : nsRefPtr<gfxPattern> pattern = new gfxPattern(surface);
703 :
704 0 : if (!pattern || pattern->CairoStatus())
705 0 : return nsnull;
706 :
707 0 : pattern->SetMatrix(pMatrix);
708 0 : pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
709 0 : return pattern.forget();
710 : }
711 :
712 : // -------------------------------------------------------------------------
713 : // Public functions
714 : // -------------------------------------------------------------------------
715 :
716 0 : nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
717 : nsStyleContext* aContext)
718 : {
719 0 : return new (aPresShell) nsSVGPatternFrame(aContext);
720 : }
721 :
|