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
15 * The Original Code is the Mozilla SVG project.
17 * The Initial Developer of the Original Code is
18 * Crocodile Clips Ltd..
19 * Portions created by the Initial Developer are Copyright (C) 2002
20 * the Initial Developer. All Rights Reserved.
23 * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or 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.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsSVGPathGeometryFrame.h"
40 #include "nsGkAtoms.h"
41 #include "nsSVGMarkerFrame.h"
42 #include "nsSVGMatrix.h"
43 #include "nsSVGUtils.h"
44 #include "nsSVGEffects.h"
45 #include "nsSVGGraphicElement.h"
46 #include "nsSVGOuterSVGFrame.h"
47 #include "nsSVGRect.h"
48 #include "nsSVGPathGeometryElement.h"
49 #include "gfxContext.h"
51 //----------------------------------------------------------------------
55 NS_NewSVGPathGeometryFrame(nsIPresShell
* aPresShell
,
57 nsStyleContext
* aContext
)
59 return new (aPresShell
) nsSVGPathGeometryFrame(aContext
);
62 //----------------------------------------------------------------------
63 // nsISupports methods
65 NS_INTERFACE_MAP_BEGIN(nsSVGPathGeometryFrame
)
66 NS_INTERFACE_MAP_ENTRY(nsISVGChildFrame
)
67 NS_INTERFACE_MAP_END_INHERITING(nsSVGPathGeometryFrameBase
)
69 //----------------------------------------------------------------------
73 nsSVGPathGeometryFrame::AttributeChanged(PRInt32 aNameSpaceID
,
77 if (aNameSpaceID
== kNameSpaceID_None
&&
78 (static_cast<nsSVGPathGeometryElement
*>
79 (mContent
)->IsDependentAttribute(aAttribute
) ||
80 aAttribute
== nsGkAtoms::transform
))
81 nsSVGUtils::UpdateGraphic(this);
87 nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
89 nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext
);
91 nsSVGUtils::InvalidateCoveredRegion(this);
93 // XXX: we'd like to use the style_hint mechanism and the
94 // ContentStateChanged/AttributeChanged functions for style changes
95 // to get slightly finer granularity, but unfortunately the
96 // style_hints don't map very well onto svg. Here seems to be the
97 // best place to deal with style changes:
99 nsSVGUtils::UpdateGraphic(this);
103 nsSVGPathGeometryFrame::GetType() const
105 return nsGkAtoms::svgPathGeometryFrame
;
108 //----------------------------------------------------------------------
109 // nsISVGChildFrame methods
112 nsSVGPathGeometryFrame::PaintSVG(nsSVGRenderState
*aContext
,
113 const nsIntRect
*aDirtyRect
)
115 if (!GetStyleVisibility()->IsVisible())
121 if (static_cast<nsSVGPathGeometryElement
*>(mContent
)->IsMarkable()) {
122 MarkerProperties properties
= GetMarkerProperties(this);
124 if (properties
.MarkersExist()) {
125 float strokeWidth
= GetStrokeWidth();
127 nsTArray
<nsSVGMark
> marks
;
128 static_cast<nsSVGPathGeometryElement
*>
129 (mContent
)->GetMarkPoints(&marks
);
131 PRUint32 num
= marks
.Length();
134 nsSVGMarkerFrame
*frame
= properties
.GetMarkerStartFrame();
136 frame
->PaintMark(aContext
, this, &marks
[0], strokeWidth
);
138 frame
= properties
.GetMarkerMidFrame();
140 for (PRUint32 i
= 1; i
< num
- 1; i
++)
141 frame
->PaintMark(aContext
, this, &marks
[i
], strokeWidth
);
144 frame
= properties
.GetMarkerEndFrame();
146 frame
->PaintMark(aContext
, this, &marks
[num
-1], strokeWidth
);
154 NS_IMETHODIMP_(nsIFrame
*)
155 nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint
&aPoint
)
157 PRUint16 fillRule
, mask
;
158 // check if we're a clipPath - cheaper than IsClipChild(), and we shouldn't
159 // get in here for other nondisplay children
160 if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
) {
161 NS_ASSERTION(IsClipChild(), "should be in clipPath but we're not");
162 mask
= HITTEST_MASK_FILL
;
163 fillRule
= GetClipRule();
165 mask
= GetHittestMask();
166 if (!mask
|| (!(mask
& HITTEST_MASK_FORCE_TEST
) &&
167 !mRect
.Contains(aPoint
)))
169 fillRule
= GetStyleSVG()->mFillRule
;
172 PRBool isHit
= PR_FALSE
;
174 gfxContext
context(nsSVGUtils::GetThebesComputationalSurface());
176 GeneratePath(&context
);
177 gfxPoint userSpacePoint
=
178 context
.DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint
.x
),
179 PresContext()->AppUnitsToGfxUnits(aPoint
.y
)));
181 if (fillRule
== NS_STYLE_FILL_RULE_EVENODD
)
182 context
.SetFillRule(gfxContext::FILL_RULE_EVEN_ODD
);
184 context
.SetFillRule(gfxContext::FILL_RULE_WINDING
);
186 if (mask
& HITTEST_MASK_FILL
)
187 isHit
= context
.PointInFill(userSpacePoint
);
188 if (!isHit
&& (mask
& HITTEST_MASK_STROKE
) &&
189 SetupCairoStrokeHitGeometry(&context
)) {
190 isHit
= context
.PointInStroke(userSpacePoint
);
193 if (isHit
&& nsSVGUtils::HitTestClip(this, aPoint
))
199 NS_IMETHODIMP_(nsRect
)
200 nsSVGPathGeometryFrame::GetCoveredRegion()
202 if (static_cast<nsSVGPathGeometryElement
*>(mContent
)->IsMarkable()) {
203 MarkerProperties properties
= GetMarkerProperties(this);
205 if (!properties
.MarkersExist())
210 float strokeWidth
= GetStrokeWidth();
212 nsTArray
<nsSVGMark
> marks
;
213 static_cast<nsSVGPathGeometryElement
*>(mContent
)->GetMarkPoints(&marks
);
215 PRUint32 num
= marks
.Length();
218 nsSVGMarkerFrame
*frame
= properties
.GetMarkerStartFrame();
220 nsRect mark
= frame
->RegionMark(this, &marks
[0], strokeWidth
);
221 rect
.UnionRect(rect
, mark
);
224 frame
= properties
.GetMarkerMidFrame();
226 for (PRUint32 i
= 1; i
< num
- 1; i
++) {
227 nsRect mark
= frame
->RegionMark(this, &marks
[i
], strokeWidth
);
228 rect
.UnionRect(rect
, mark
);
232 frame
= properties
.GetMarkerEndFrame();
234 nsRect mark
= frame
->RegionMark(this, &marks
[num
-1], strokeWidth
);
235 rect
.UnionRect(rect
, mark
);
246 nsSVGPathGeometryFrame::UpdateCoveredRegion()
250 gfxContext
context(nsSVGUtils::GetThebesComputationalSurface());
252 static_cast<nsSVGPathGeometryElement
*>(mContent
)->ConstructPath(&context
);
254 gfxRect extent
= gfxRect(0, 0, 0, 0);
256 if (SetupCairoStrokeGeometry(&context
)) {
257 extent
= context
.GetUserStrokeExtent();
259 if (GetStyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
) {
260 extent
= extent
.Union(context
.GetUserPathExtent());
263 if (!extent
.IsEmpty()) {
264 nsCOMPtr
<nsIDOMSVGMatrix
> ctm
;
265 GetCanvasTM(getter_AddRefs(ctm
));
266 NS_ASSERTION(ctm
, "graphic source didn't specify a ctm");
268 gfxMatrix matrix
= nsSVGUtils::ConvertSVGMatrixToThebes(ctm
);
270 extent
= matrix
.TransformBounds(extent
);
271 mRect
= nsSVGUtils::ToAppPixelRect(PresContext(), extent
);
275 mRect
= GetCoveredRegion();
281 nsSVGPathGeometryFrame::InitialUpdate()
283 NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW
,
284 "Yikes! We've been called already! Hopefully we weren't called "
285 "before our nsSVGOuterSVGFrame's initial Reflow()!!!");
287 nsSVGUtils::UpdateGraphic(this);
289 NS_ASSERTION(!(mState
& NS_FRAME_IN_REFLOW
),
290 "We don't actually participate in reflow");
292 // Do unset the various reflow bits, though.
293 mState
&= ~(NS_FRAME_FIRST_REFLOW
| NS_FRAME_IS_DIRTY
|
294 NS_FRAME_HAS_DIRTY_CHILDREN
);
299 nsSVGPathGeometryFrame::NotifySVGChanged(PRUint32 aFlags
)
301 if (!(aFlags
& SUPPRESS_INVALIDATION
)) {
302 nsSVGUtils::UpdateGraphic(this);
307 nsSVGPathGeometryFrame::NotifyRedrawSuspended()
309 // XXX should we cache the fact that redraw is suspended?
314 nsSVGPathGeometryFrame::NotifyRedrawUnsuspended()
316 if (GetStateBits() & NS_STATE_SVG_DIRTY
)
317 nsSVGUtils::UpdateGraphic(this);
323 nsSVGPathGeometryFrame::SetMatrixPropagation(PRBool aPropagate
)
326 AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM
);
328 RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM
);
334 nsSVGPathGeometryFrame::GetMatrixPropagation()
336 return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM
) != 0;
340 nsSVGPathGeometryFrame::GetBBox(nsIDOMSVGRect
**_retval
)
342 gfxContext
context(nsSVGUtils::GetThebesComputationalSurface());
344 GeneratePath(&context
);
345 context
.IdentityMatrix();
347 return NS_NewSVGRect(_retval
, context
.GetUserPathExtent());
350 //----------------------------------------------------------------------
351 // nsSVGGeometryFrame methods:
353 /* readonly attribute nsIDOMSVGMatrix canvasTM; */
355 nsSVGPathGeometryFrame::GetCanvasTM(nsIDOMSVGMatrix
* *aCTM
)
359 if (!GetMatrixPropagation()) {
360 return NS_NewSVGMatrix(aCTM
);
363 nsSVGContainerFrame
*containerFrame
= static_cast<nsSVGContainerFrame
*>
365 nsCOMPtr
<nsIDOMSVGMatrix
> parentTM
= containerFrame
->GetCanvasTM();
366 NS_ASSERTION(parentTM
, "null TM");
368 // append our local transformations if we have any:
369 nsSVGGraphicElement
*element
=
370 static_cast<nsSVGGraphicElement
*>(mContent
);
371 nsCOMPtr
<nsIDOMSVGMatrix
> localTM
= element
->GetLocalTransformMatrix();
374 return parentTM
->Multiply(localTM
, aCTM
);
381 //----------------------------------------------------------------------
382 // nsSVGPathGeometryFrame methods:
384 nsSVGPathGeometryFrame::MarkerProperties
385 nsSVGPathGeometryFrame::GetMarkerProperties(nsSVGPathGeometryFrame
*aFrame
)
387 NS_ASSERTION(!aFrame
->GetPrevContinuation(), "aFrame should be first continuation");
389 MarkerProperties result
;
390 const nsStyleSVG
*style
= aFrame
->GetStyleSVG();
391 result
.mMarkerStart
= nsSVGEffects::GetMarkerProperty(
392 style
->mMarkerStart
, aFrame
, nsGkAtoms::marker_start
);
393 result
.mMarkerMid
= nsSVGEffects::GetMarkerProperty(
394 style
->mMarkerMid
, aFrame
, nsGkAtoms::marker_mid
);
395 result
.mMarkerEnd
= nsSVGEffects::GetMarkerProperty(
396 style
->mMarkerEnd
, aFrame
, nsGkAtoms::marker_end
);
401 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame()
405 return static_cast<nsSVGMarkerFrame
*>
406 (mMarkerStart
->GetReferencedFrame(nsGkAtoms::svgMarkerFrame
, nsnull
));
410 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame()
414 return static_cast<nsSVGMarkerFrame
*>
415 (mMarkerMid
->GetReferencedFrame(nsGkAtoms::svgMarkerFrame
, nsnull
));
419 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame()
423 return static_cast<nsSVGMarkerFrame
*>
424 (mMarkerEnd
->GetReferencedFrame(nsGkAtoms::svgMarkerFrame
, nsnull
));
428 nsSVGPathGeometryFrame::Render(nsSVGRenderState
*aContext
)
430 gfxContext
*gfx
= aContext
->GetGfxContext();
432 PRUint16 renderMode
= aContext
->GetRenderMode();
434 /* save/pop the state so we don't screw up the xform */
439 if (renderMode
!= nsSVGRenderState::NORMAL
) {
442 if (GetClipRule() == NS_STYLE_FILL_RULE_EVENODD
)
443 gfx
->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD
);
445 gfx
->SetFillRule(gfxContext::FILL_RULE_WINDING
);
447 if (renderMode
== nsSVGRenderState::CLIP_MASK
) {
448 gfx
->SetAntialiasMode(gfxContext::MODE_ALIASED
);
449 gfx
->SetColor(gfxRGBA(1.0f
, 1.0f
, 1.0f
, 1.0f
));
457 switch (GetStyleSVG()->mShapeRendering
) {
458 case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED
:
459 case NS_STYLE_SHAPE_RENDERING_CRISPEDGES
:
460 gfx
->SetAntialiasMode(gfxContext::MODE_ALIASED
);
463 gfx
->SetAntialiasMode(gfxContext::MODE_COVERAGE
);
467 if (SetupCairoFill(gfx
)) {
471 if (SetupCairoStroke(gfx
)) {
481 nsSVGPathGeometryFrame::GeneratePath(gfxContext
* aContext
)
483 nsCOMPtr
<nsIDOMSVGMatrix
> ctm
;
484 GetCanvasTM(getter_AddRefs(ctm
));
485 NS_ASSERTION(ctm
, "graphic source didn't specify a ctm");
487 gfxMatrix matrix
= nsSVGUtils::ConvertSVGMatrixToThebes(ctm
);
489 if (matrix
.IsSingular()) {
490 aContext
->IdentityMatrix();
495 aContext
->Multiply(matrix
);
498 static_cast<nsSVGPathGeometryElement
*>(mContent
)->ConstructPath(aContext
);
502 nsSVGPathGeometryFrame::GetHittestMask()
506 switch(GetStyleSVG()->mPointerEvents
) {
507 case NS_STYLE_POINTER_EVENTS_NONE
:
509 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED
:
510 if (GetStyleVisibility()->IsVisible()) {
511 if (GetStyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
)
512 mask
|= HITTEST_MASK_FILL
;
513 if (GetStyleSVG()->mStroke
.mType
!= eStyleSVGPaintType_None
)
514 mask
|= HITTEST_MASK_STROKE
;
517 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL
:
518 if (GetStyleVisibility()->IsVisible()) {
519 mask
|= HITTEST_MASK_FILL
| HITTEST_MASK_FORCE_TEST
;
522 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE
:
523 if (GetStyleVisibility()->IsVisible()) {
524 mask
|= HITTEST_MASK_STROKE
| HITTEST_MASK_FORCE_TEST
;
527 case NS_STYLE_POINTER_EVENTS_VISIBLE
:
528 if (GetStyleVisibility()->IsVisible()) {
531 HITTEST_MASK_STROKE
|
532 HITTEST_MASK_FORCE_TEST
;
535 case NS_STYLE_POINTER_EVENTS_PAINTED
:
536 if (GetStyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
)
537 mask
|= HITTEST_MASK_FILL
;
538 if (GetStyleSVG()->mStroke
.mType
!= eStyleSVGPaintType_None
)
539 mask
|= HITTEST_MASK_STROKE
;
541 case NS_STYLE_POINTER_EVENTS_FILL
:
542 mask
|= HITTEST_MASK_FILL
| HITTEST_MASK_FORCE_TEST
;
544 case NS_STYLE_POINTER_EVENTS_STROKE
:
545 mask
|= HITTEST_MASK_STROKE
| HITTEST_MASK_FORCE_TEST
;
547 case NS_STYLE_POINTER_EVENTS_ALL
:
550 HITTEST_MASK_STROKE
|
551 HITTEST_MASK_FORCE_TEST
;
554 NS_ERROR("not reached");