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 IBM Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2004
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
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.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsIDOMSVGAnimatedRect.h"
38 : #include "nsIDOMSVGRect.h"
39 : #include "nsIDocument.h"
40 : #include "nsSVGMarkerFrame.h"
41 : #include "nsSVGPathGeometryFrame.h"
42 : #include "nsSVGEffects.h"
43 : #include "nsSVGMarkerElement.h"
44 : #include "nsSVGPathGeometryElement.h"
45 : #include "gfxContext.h"
46 :
47 : nsIFrame*
48 0 : NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
49 : {
50 0 : return new (aPresShell) nsSVGMarkerFrame(aContext);
51 : }
52 :
53 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame)
54 :
55 : //----------------------------------------------------------------------
56 : // nsIFrame methods:
57 :
58 : NS_IMETHODIMP
59 0 : nsSVGMarkerFrame::AttributeChanged(PRInt32 aNameSpaceID,
60 : nsIAtom* aAttribute,
61 : PRInt32 aModType)
62 : {
63 0 : if (aNameSpaceID == kNameSpaceID_None &&
64 : (aAttribute == nsGkAtoms::markerUnits ||
65 : aAttribute == nsGkAtoms::refX ||
66 : aAttribute == nsGkAtoms::refY ||
67 : aAttribute == nsGkAtoms::markerWidth ||
68 : aAttribute == nsGkAtoms::markerHeight ||
69 : aAttribute == nsGkAtoms::orient ||
70 : aAttribute == nsGkAtoms::preserveAspectRatio ||
71 : aAttribute == nsGkAtoms::viewBox)) {
72 0 : nsSVGEffects::InvalidateRenderingObservers(this);
73 : }
74 :
75 : return nsSVGMarkerFrameBase::AttributeChanged(aNameSpaceID,
76 0 : aAttribute, aModType);
77 : }
78 :
79 : #ifdef DEBUG
80 : NS_IMETHODIMP
81 0 : nsSVGMarkerFrame::Init(nsIContent* aContent,
82 : nsIFrame* aParent,
83 : nsIFrame* aPrevInFlow)
84 : {
85 0 : nsCOMPtr<nsIDOMSVGMarkerElement> marker = do_QueryInterface(aContent);
86 0 : NS_ASSERTION(marker, "Content is not an SVG marker");
87 :
88 0 : return nsSVGMarkerFrameBase::Init(aContent, aParent, aPrevInFlow);
89 : }
90 : #endif /* DEBUG */
91 :
92 : nsIAtom *
93 0 : nsSVGMarkerFrame::GetType() const
94 : {
95 0 : return nsGkAtoms::svgMarkerFrame;
96 : }
97 :
98 : //----------------------------------------------------------------------
99 : // nsSVGContainerFrame methods:
100 :
101 : gfxMatrix
102 0 : nsSVGMarkerFrame::GetCanvasTM()
103 : {
104 0 : NS_ASSERTION(mMarkedFrame, "null nsSVGPathGeometry frame");
105 :
106 0 : if (mInUse2) {
107 : // We're going to be bailing drawing the marker, so return an identity.
108 0 : return gfxMatrix();
109 : }
110 :
111 0 : nsSVGMarkerElement *content = static_cast<nsSVGMarkerElement*>(mContent);
112 :
113 0 : mInUse2 = true;
114 0 : gfxMatrix markedTM = mMarkedFrame->GetCanvasTM();
115 0 : mInUse2 = false;
116 :
117 0 : gfxMatrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle);
118 0 : gfxMatrix viewBoxTM = content->GetViewBoxTransform();
119 :
120 0 : return viewBoxTM * markerTM * markedTM;
121 : }
122 :
123 :
124 : nsresult
125 0 : nsSVGMarkerFrame::PaintMark(nsRenderingContext *aContext,
126 : nsSVGPathGeometryFrame *aMarkedFrame,
127 : nsSVGMark *aMark, float aStrokeWidth)
128 : {
129 : // If the flag is set when we get here, it means this marker frame
130 : // has already been used painting the current mark, and the document
131 : // has a marker reference loop.
132 0 : if (mInUse)
133 0 : return NS_OK;
134 :
135 0 : AutoMarkerReferencer markerRef(this, aMarkedFrame);
136 :
137 0 : nsSVGMarkerElement *marker = static_cast<nsSVGMarkerElement*>(mContent);
138 :
139 0 : const nsSVGViewBoxRect viewBox = marker->GetViewBoxRect();
140 :
141 0 : if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
142 : // We must disable rendering if the viewBox width or height are zero.
143 0 : return NS_OK;
144 : }
145 :
146 0 : mStrokeWidth = aStrokeWidth;
147 0 : mX = aMark->x;
148 0 : mY = aMark->y;
149 0 : mAutoAngle = aMark->angle;
150 :
151 0 : gfxContext *gfx = aContext->ThebesContext();
152 :
153 0 : if (GetStyleDisplay()->IsScrollableOverflow()) {
154 0 : gfx->Save();
155 : gfxRect clipRect =
156 : nsSVGUtils::GetClipRectForFrame(this, viewBox.x, viewBox.y,
157 0 : viewBox.width, viewBox.height);
158 0 : nsSVGUtils::SetClipRect(gfx, GetCanvasTM(), clipRect);
159 : }
160 :
161 0 : for (nsIFrame* kid = mFrames.FirstChild(); kid;
162 : kid = kid->GetNextSibling()) {
163 0 : nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
164 0 : if (SVGFrame) {
165 : // The CTM of each frame referencing us may be different.
166 : SVGFrame->NotifySVGChanged(
167 : nsISVGChildFrame::DO_NOT_NOTIFY_RENDERING_OBSERVERS |
168 0 : nsISVGChildFrame::TRANSFORM_CHANGED);
169 0 : nsSVGUtils::PaintFrameWithEffects(aContext, nsnull, kid);
170 : }
171 : }
172 :
173 0 : if (GetStyleDisplay()->IsScrollableOverflow())
174 0 : gfx->Restore();
175 :
176 0 : return NS_OK;
177 : }
178 :
179 : gfxRect
180 0 : nsSVGMarkerFrame::GetMarkBBoxContribution(const gfxMatrix &aToBBoxUserspace,
181 : PRUint32 aFlags,
182 : nsSVGPathGeometryFrame *aMarkedFrame,
183 : const nsSVGMark *aMark,
184 : float aStrokeWidth)
185 : {
186 : // If the flag is set when we get here, it means this marker frame
187 : // has already been used in calculating the current mark bbox, and
188 : // the document has a marker reference loop.
189 0 : if (mInUse)
190 0 : return gfxRect();
191 :
192 0 : AutoMarkerReferencer markerRef(this, aMarkedFrame);
193 :
194 0 : nsSVGMarkerElement *content = static_cast<nsSVGMarkerElement*>(mContent);
195 :
196 0 : const nsSVGViewBoxRect viewBox = content->GetViewBoxRect();
197 :
198 0 : if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
199 0 : return gfxRect();
200 : }
201 :
202 0 : mStrokeWidth = aStrokeWidth;
203 0 : mX = aMark->x;
204 0 : mY = aMark->y;
205 0 : mAutoAngle = aMark->angle;
206 :
207 0 : gfxRect bbox;
208 :
209 : gfxMatrix markerTM =
210 0 : content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle);
211 0 : gfxMatrix viewBoxTM = content->GetViewBoxTransform();
212 :
213 0 : gfxMatrix tm = viewBoxTM * markerTM * aToBBoxUserspace;
214 :
215 0 : for (nsIFrame* kid = mFrames.FirstChild();
216 : kid;
217 : kid = kid->GetNextSibling()) {
218 0 : nsISVGChildFrame* child = do_QueryFrame(kid);
219 0 : if (child) {
220 : // When we're being called to obtain the invalidation area, we need to
221 : // pass down all the flags so that stroke is included. However, once DOM
222 : // getBBox() accepts flags, maybe we should strip some of those here?
223 0 : bbox.UnionRect(bbox, child->GetBBoxContribution(tm, aFlags));
224 : }
225 : }
226 :
227 0 : return bbox;
228 : }
229 :
230 : void
231 0 : nsSVGMarkerFrame::SetParentCoordCtxProvider(nsSVGSVGElement *aContext)
232 : {
233 0 : nsSVGMarkerElement *marker = static_cast<nsSVGMarkerElement*>(mContent);
234 0 : marker->SetParentCoordCtxProvider(aContext);
235 0 : }
236 :
237 : //----------------------------------------------------------------------
238 : // helper class
239 :
240 0 : nsSVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer(
241 : nsSVGMarkerFrame *aFrame,
242 : nsSVGPathGeometryFrame *aMarkedFrame)
243 0 : : mFrame(aFrame)
244 : {
245 0 : mFrame->mInUse = true;
246 0 : mFrame->mMarkedFrame = aMarkedFrame;
247 :
248 : nsSVGSVGElement *ctx =
249 0 : static_cast<nsSVGElement*>(aMarkedFrame->GetContent())->GetCtx();
250 0 : mFrame->SetParentCoordCtxProvider(ctx);
251 0 : }
252 :
253 0 : nsSVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer()
254 : {
255 0 : mFrame->SetParentCoordCtxProvider(nsnull);
256 :
257 0 : mFrame->mMarkedFrame = nsnull;
258 0 : mFrame->mInUse = false;
259 0 : }
|