source: josm/trunk/src/com/kitfox/svg/ShapeElement.java@ 13500

Last change on this file since 13500 was 11525, checked in by Don-vip, 7 years ago

see #14319 - update to latest version of svgSalamander (2017-01-07, patched)

  • Property svn:eol-style set to native
File size: 14.4 KB
Line 
1/*
2 * SVG Salamander
3 * Copyright (c) 2004, Mark McKay
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
9 *
10 * - Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
32 * projects can be found at http://www.kitfox.com
33 *
34 * Created on January 26, 2004, 5:21 PM
35 */
36
37package com.kitfox.svg;
38
39import com.kitfox.svg.Marker.MarkerLayout;
40import com.kitfox.svg.Marker.MarkerPos;
41import com.kitfox.svg.xml.StyleAttribute;
42import java.awt.AlphaComposite;
43import java.awt.BasicStroke;
44import java.awt.Color;
45import java.awt.Composite;
46import java.awt.Graphics2D;
47import java.awt.Paint;
48import java.awt.Shape;
49import java.awt.geom.AffineTransform;
50import java.awt.geom.Point2D;
51import java.awt.geom.Rectangle2D;
52import java.net.URI;
53import java.util.ArrayList;
54import java.util.List;
55
56
57
58/**
59 * Parent of shape objects
60 *
61 * @author Mark McKay
62 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
63 */
64abstract public class ShapeElement extends RenderableElement
65{
66
67 /**
68 * This is necessary to get text elements to render the stroke the correct
69 * width. It is an alternative to producing new font glyph sets at different
70 * sizes.
71 */
72 protected float strokeWidthScalar = 1f;
73
74 /** Creates a new instance of ShapeElement */
75 public ShapeElement() {
76 }
77
78 @Override
79 abstract public void render(java.awt.Graphics2D g) throws SVGException;
80
81 /*
82 protected void setStrokeWidthScalar(float strokeWidthScalar)
83 {
84 this.strokeWidthScalar = strokeWidthScalar;
85 }
86 */
87
88 @Override
89 void pick(Point2D point, boolean boundingBox, List<List<SVGElement>> retVec) throws SVGException
90 {
91// StyleAttribute styleAttrib = new StyleAttribute();
92// if (getStyle(styleAttrib.setName("fill")) && getShape().contains(point))
93 if ((boundingBox ? getBoundingBox() : getShape()).contains(point))
94 {
95 retVec.add(getPath(null));
96 }
97 }
98
99 @Override
100 void pick(Rectangle2D pickArea, AffineTransform ltw, boolean boundingBox, List<List<SVGElement>> retVec) throws SVGException
101 {
102// StyleAttribute styleAttrib = new StyleAttribute();
103// if (getStyle(styleAttrib.setName("fill")) && getShape().contains(point))
104 if (ltw.createTransformedShape((boundingBox ? getBoundingBox() : getShape())).intersects(pickArea))
105 {
106 retVec.add(getPath(null));
107 }
108 }
109
110 private Paint handleCurrentColor(StyleAttribute styleAttrib) throws SVGException
111 {
112 if (styleAttrib.getStringValue().equals("currentColor"))
113 {
114 StyleAttribute currentColorAttrib = new StyleAttribute();
115 if (getStyle(currentColorAttrib.setName("color")))
116 {
117 if (!currentColorAttrib.getStringValue().equals("none"))
118 {
119 return currentColorAttrib.getColorValue();
120 }
121 }
122 return null;
123 }
124 else
125 {
126 return styleAttrib.getColorValue();
127 }
128 }
129
130 protected void renderShape(Graphics2D g, Shape shape) throws SVGException
131 {
132//g.setColor(Color.green);
133
134 StyleAttribute styleAttrib = new StyleAttribute();
135
136 //Don't process if not visible
137 if (getStyle(styleAttrib.setName("visibility")))
138 {
139 if (!styleAttrib.getStringValue().equals("visible")) return;
140 }
141
142 if (getStyle(styleAttrib.setName("display")))
143 {
144 if (styleAttrib.getStringValue().equals("none")) return;
145 }
146
147 //None, solid color, gradient, pattern
148 Paint fillPaint = Color.black; //Default to black. Must be explicitly set to none for no fill.
149 if (getStyle(styleAttrib.setName("fill")))
150 {
151 if (styleAttrib.getStringValue().equals("none")) fillPaint = null;
152 else
153 {
154 fillPaint = handleCurrentColor(styleAttrib);
155 if (fillPaint == null)
156 {
157 URI uri = styleAttrib.getURIValue(getXMLBase());
158 if (uri != null)
159 {
160 Rectangle2D bounds = shape.getBounds2D();
161 AffineTransform xform = g.getTransform();
162
163 SVGElement ele = diagram.getUniverse().getElement(uri);
164 if (ele != null)
165 {
166 try {
167 fillPaint = ((FillElement)ele).getPaint(bounds, xform);
168 } catch (IllegalArgumentException e) {
169 throw new SVGException(e);
170 }
171 }
172 }
173 }
174 }
175 }
176
177 //Default opacity
178 float opacity = 1f;
179 if (getStyle(styleAttrib.setName("opacity")))
180 {
181 opacity = styleAttrib.getRatioValue();
182 }
183
184 float fillOpacity = opacity;
185 if (getStyle(styleAttrib.setName("fill-opacity")))
186 {
187 fillOpacity *= styleAttrib.getRatioValue();
188 }
189
190
191 Paint strokePaint = null; //Default is to stroke with none
192 if (getStyle(styleAttrib.setName("stroke")))
193 {
194 if (styleAttrib.getStringValue().equals("none")) strokePaint = null;
195 else
196 {
197 strokePaint = handleCurrentColor(styleAttrib);
198 if (strokePaint == null)
199 {
200 URI uri = styleAttrib.getURIValue(getXMLBase());
201 if (uri != null)
202 {
203 Rectangle2D bounds = shape.getBounds2D();
204 AffineTransform xform = g.getTransform();
205
206 SVGElement ele = diagram.getUniverse().getElement(uri);
207 if (ele != null)
208 {
209 strokePaint = ((FillElement)ele).getPaint(bounds, xform);
210 }
211 }
212 }
213 }
214 }
215
216 float[] strokeDashArray = null;
217 if (getStyle(styleAttrib.setName("stroke-dasharray")))
218 {
219 strokeDashArray = styleAttrib.getFloatList();
220 if (strokeDashArray.length == 0) strokeDashArray = null;
221 }
222
223 float strokeDashOffset = 0f;
224 if (getStyle(styleAttrib.setName("stroke-dashoffset")))
225 {
226 strokeDashOffset = styleAttrib.getFloatValueWithUnits();
227 }
228
229 int strokeLinecap = BasicStroke.CAP_BUTT;
230 if (getStyle(styleAttrib.setName("stroke-linecap")))
231 {
232 String val = styleAttrib.getStringValue();
233 if (val.equals("round"))
234 {
235 strokeLinecap = BasicStroke.CAP_ROUND;
236 }
237 else if (val.equals("square"))
238 {
239 strokeLinecap = BasicStroke.CAP_SQUARE;
240 }
241 }
242
243 int strokeLinejoin = BasicStroke.JOIN_MITER;
244 if (getStyle(styleAttrib.setName("stroke-linejoin")))
245 {
246 String val = styleAttrib.getStringValue();
247 if (val.equals("round"))
248 {
249 strokeLinejoin = BasicStroke.JOIN_ROUND;
250 }
251 else if (val.equals("bevel"))
252 {
253 strokeLinejoin = BasicStroke.JOIN_BEVEL;
254 }
255 }
256
257 float strokeMiterLimit = 4f;
258 if (getStyle(styleAttrib.setName("stroke-miterlimit")))
259 {
260 strokeMiterLimit = Math.max(styleAttrib.getFloatValueWithUnits(), 1);
261 }
262
263 float strokeOpacity = opacity;
264 if (getStyle(styleAttrib.setName("stroke-opacity")))
265 {
266 strokeOpacity *= styleAttrib.getRatioValue();
267 }
268
269 float strokeWidth = 1f;
270 if (getStyle(styleAttrib.setName("stroke-width")))
271 {
272 strokeWidth = styleAttrib.getFloatValueWithUnits();
273 }
274// if (strokeWidthScalar != 1f)
275// {
276 strokeWidth *= strokeWidthScalar;
277// }
278
279 Marker markerStart = null;
280 if (getStyle(styleAttrib.setName("marker-start")))
281 {
282 if (!styleAttrib.getStringValue().equals("none"))
283 {
284 URI uri = styleAttrib.getURIValue(getXMLBase());
285 markerStart = (Marker)diagram.getUniverse().getElement(uri);
286 }
287 }
288
289 Marker markerMid = null;
290 if (getStyle(styleAttrib.setName("marker-mid")))
291 {
292 if (!styleAttrib.getStringValue().equals("none"))
293 {
294 URI uri = styleAttrib.getURIValue(getXMLBase());
295 markerMid = (Marker)diagram.getUniverse().getElement(uri);
296 }
297 }
298
299 Marker markerEnd = null;
300 if (getStyle(styleAttrib.setName("marker-end")))
301 {
302 if (!styleAttrib.getStringValue().equals("none"))
303 {
304 URI uri = styleAttrib.getURIValue(getXMLBase());
305 markerEnd = (Marker)diagram.getUniverse().getElement(uri);
306 }
307 }
308
309
310 //Draw the shape
311 if (fillPaint != null && fillOpacity != 0f)
312 {
313 if (fillOpacity <= 0)
314 {
315 //Do nothing
316 }
317 else if (fillOpacity < 1f)
318 {
319 Composite cachedComposite = g.getComposite();
320 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, fillOpacity));
321
322 g.setPaint(fillPaint);
323 g.fill(shape);
324
325 g.setComposite(cachedComposite);
326 }
327 else
328 {
329 g.setPaint(fillPaint);
330 g.fill(shape);
331 }
332 }
333
334
335 if (strokePaint != null && strokeOpacity != 0f)
336 {
337 BasicStroke stroke;
338 if (strokeDashArray == null)
339 {
340 stroke = new BasicStroke(strokeWidth, strokeLinecap, strokeLinejoin, strokeMiterLimit);
341 }
342 else
343 {
344 stroke = new BasicStroke(strokeWidth, strokeLinecap, strokeLinejoin, strokeMiterLimit, strokeDashArray, strokeDashOffset);
345 }
346
347 Shape strokeShape;
348 AffineTransform cacheXform = g.getTransform();
349 if (vectorEffect == VECTOR_EFFECT_NON_SCALING_STROKE)
350 {
351 strokeShape = cacheXform.createTransformedShape(shape);
352 strokeShape = stroke.createStrokedShape(strokeShape);
353 }
354 else
355 {
356 strokeShape = stroke.createStrokedShape(shape);
357 }
358
359 if (strokeOpacity <= 0)
360 {
361 //Do nothing
362 }
363 else
364 {
365 Composite cachedComposite = g.getComposite();
366
367 if (strokeOpacity < 1f)
368 {
369 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, strokeOpacity));
370 }
371
372 if (vectorEffect == VECTOR_EFFECT_NON_SCALING_STROKE)
373 {
374 //Set to identity
375 g.setTransform(new AffineTransform());
376 }
377
378 g.setPaint(strokePaint);
379 g.fill(strokeShape);
380
381 if (vectorEffect == VECTOR_EFFECT_NON_SCALING_STROKE)
382 {
383 //Set to identity
384 g.setTransform(cacheXform);
385 }
386
387 if (strokeOpacity < 1f)
388 {
389 g.setComposite(cachedComposite);
390 }
391 }
392 }
393
394 if (markerStart != null || markerMid != null || markerEnd != null)
395 {
396 MarkerLayout layout = new MarkerLayout();
397 layout.layout(shape);
398
399 ArrayList<MarkerPos> list = layout.getMarkerList();
400 for (int i = 0; i < list.size(); ++i)
401 {
402 MarkerPos pos = list.get(i);
403
404 switch (pos.type)
405 {
406 case Marker.MARKER_START:
407 if (markerStart != null)
408 {
409 markerStart.render(g, pos, strokeWidth);
410 }
411 break;
412 case Marker.MARKER_MID:
413 if (markerMid != null)
414 {
415 markerMid.render(g, pos, strokeWidth);
416 }
417 break;
418 case Marker.MARKER_END:
419 if (markerEnd != null)
420 {
421 markerEnd.render(g, pos, strokeWidth);
422 }
423 break;
424 }
425 }
426 }
427 }
428
429 abstract public Shape getShape();
430
431 protected Rectangle2D includeStrokeInBounds(Rectangle2D rect) throws SVGException
432 {
433 StyleAttribute styleAttrib = new StyleAttribute();
434 if (!getStyle(styleAttrib.setName("stroke"))) return rect;
435
436 double strokeWidth = 1;
437 if (getStyle(styleAttrib.setName("stroke-width"))) strokeWidth = styleAttrib.getDoubleValue();
438
439 rect.setRect(
440 rect.getX() - strokeWidth / 2,
441 rect.getY() - strokeWidth / 2,
442 rect.getWidth() + strokeWidth,
443 rect.getHeight() + strokeWidth);
444
445 return rect;
446 }
447
448}
Note: See TracBrowser for help on using the repository browser.