1. /*
  2. * @(#)BasicStroke.java 1.39 03/03/19
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt;
  8. import java.awt.geom.GeneralPath;
  9. import java.awt.geom.PathIterator;
  10. import sun.dc.path.FastPathProducer;
  11. import sun.dc.path.PathConsumer;
  12. import sun.dc.path.PathException;
  13. import sun.dc.pr.PathStroker;
  14. import sun.dc.pr.PathDasher;
  15. import sun.dc.pr.Rasterizer;
  16. /**
  17. * The <code>BasicStroke</code> class defines a basic set of rendering
  18. * attributes for the outlines of graphics primitives, which are rendered
  19. * with a {@link Graphics2D} object that has its Stroke attribute set to
  20. * this <code>BasicStroke</code>.
  21. * The rendering attributes defined by <code>BasicStroke</code> describe
  22. * the shape of the mark made by a pen drawn along the outline of a
  23. * {@link Shape} and the decorations applied at the ends and joins of
  24. * path segments of the <code>Shape</code>.
  25. * These rendering attributes include:
  26. * <dl compact>
  27. * <dt><i>width</i>
  28. * <dd>The pen width, measured perpendicularly to the pen trajectory.
  29. * <dt><i>end caps</i>
  30. * <dd>The decoration applied to the ends of unclosed subpaths and
  31. * dash segments. Subpaths that start and end on the same point are
  32. * still considered unclosed if they do not have a CLOSE segment.
  33. * See {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}
  34. * for more information on the CLOSE segment.
  35. * The three different decorations are: {@link #CAP_BUTT},
  36. * {@link #CAP_ROUND}, and {@link #CAP_SQUARE}.
  37. * <dt><i>line joins</i>
  38. * <dd>The decoration applied at the intersection of two path segments
  39. * and at the intersection of the endpoints of a subpath that is closed
  40. * using {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}.
  41. * The three different decorations are: {@link #JOIN_BEVEL},
  42. * {@link #JOIN_MITER}, and {@link #JOIN_ROUND}.
  43. * <dt><i>miter limit</i>
  44. * <dd>The limit to trim a line join that has a JOIN_MITER decoration.
  45. * A line join is trimmed when the ratio of miter length to stroke
  46. * width is greater than the miterlimit value. The miter length is
  47. * the diagonal length of the miter, which is the distance between
  48. * the inside corner and the outside corner of the intersection.
  49. * The smaller the angle formed by two line segments, the longer
  50. * the miter length and the sharper the angle of intersection. The
  51. * default miterlimit value of 10.0f causes all angles less than
  52. * 11 degrees to be trimmed. Trimming miters converts
  53. * the decoration of the line join to bevel.
  54. * <dt><i>dash attributes</i>
  55. * <dd>The definition of how to make a dash pattern by alternating
  56. * between opaque and transparent sections.
  57. * </dl>
  58. * All attributes that specify measurements and distances controlling
  59. * the shape of the returned outline are measured in the same
  60. * coordinate system as the original unstroked <code>Shape</code>
  61. * argument. When a <code>Graphics2D</code> object uses a
  62. * <code>Stroke</code> object to redefine a path during the execution
  63. * of one of its <code>draw</code> methods, the geometry is supplied
  64. * in its original form before the <code>Graphics2D</code> transform
  65. * attribute is applied. Therefore, attributes such as the pen width
  66. * are interpreted in the user space coordinate system of the
  67. * <code>Graphics2D</code> object and are subject to the scaling and
  68. * shearing effects of the user-space-to-device-space transform in that
  69. * particular <code>Graphics2D</code>.
  70. * For example, the width of a rendered shape's outline is determined
  71. * not only by the width attribute of this <code>BasicStroke</code>,
  72. * but also by the transform attribute of the
  73. * <code>Graphics2D</code> object. Consider this code:
  74. * <blockquote><tt>
  75. * // sets the Graphics2D object's Tranform attribute
  76. * g2d.scale(10, 10);
  77. * // sets the Graphics2D object's Stroke attribute
  78. * g2d.setStroke(new BasicStroke(1.5f));
  79. * </tt></blockquote>
  80. * Assuming there are no other scaling transforms added to the
  81. * <code>Graphics2D</code> object, the resulting line
  82. * will be approximately 15 pixels wide.
  83. * As the example code demonstrates, a floating-point line
  84. * offers better precision, especially when large transforms are
  85. * used with a <code>Graphics2D</code> object.
  86. * When a line is diagonal, the exact width depends on how the
  87. * rendering pipeline chooses which pixels to fill as it traces the
  88. * theoretical widened outline. The choice of which pixels to turn
  89. * on is affected by the antialiasing attribute because the
  90. * antialiasing rendering pipeline can choose to color
  91. * partially-covered pixels.
  92. * <p>
  93. * For more information on the user space coordinate system and the
  94. * rendering process, see the <code>Graphics2D</code> class comments.
  95. * @see Graphics2D
  96. * @version 1.39, 03/19/03
  97. * @author Jim Graham
  98. */
  99. public class BasicStroke implements Stroke {
  100. /**
  101. * Joins path segments by extending their outside edges until
  102. * they meet.
  103. */
  104. public final static int JOIN_MITER = 0;
  105. /**
  106. * Joins path segments by rounding off the corner at a radius
  107. * of half the line width.
  108. */
  109. public final static int JOIN_ROUND = 1;
  110. /**
  111. * Joins path segments by connecting the outer corners of their
  112. * wide outlines with a straight segment.
  113. */
  114. public final static int JOIN_BEVEL = 2;
  115. /**
  116. * Ends unclosed subpaths and dash segments with no added
  117. * decoration.
  118. */
  119. public final static int CAP_BUTT = 0;
  120. /**
  121. * Ends unclosed subpaths and dash segments with a round
  122. * decoration that has a radius equal to half of the width
  123. * of the pen.
  124. */
  125. public final static int CAP_ROUND = 1;
  126. /**
  127. * Ends unclosed subpaths and dash segments with a square
  128. * projection that extends beyond the end of the segment
  129. * to a distance equal to half of the line width.
  130. */
  131. public final static int CAP_SQUARE = 2;
  132. float width;
  133. int join;
  134. int cap;
  135. float miterlimit;
  136. float dash[];
  137. float dash_phase;
  138. /**
  139. * Constructs a new <code>BasicStroke</code> with the specified
  140. * attributes.
  141. * @param width the width of this <code>BasicStroke</code>. The
  142. * width must be greater than or equal to 0.0f. If width is
  143. * set to 0.0f, the stroke is rendered as the thinnest
  144. * possible line for the target device and the antialias
  145. * hint setting.
  146. * @param cap the decoration of the ends of a <code>BasicStroke</code>
  147. * @param join the decoration applied where path segments meet
  148. * @param miterlimit the limit to trim the miter join. The miterlimit
  149. * must be greater than or equal to 1.0f.
  150. * @param dash the array representing the dashing pattern
  151. * @param dash_phase the offset to start the dashing pattern
  152. * @throws IllegalArgumentException if <code>width</code> is negative
  153. * @throws IllegalArgumentException if <code>cap</code> is not either
  154. * CAP_BUTT, CAP_ROUND or CAP_SQUARE
  155. * @throws IllegalArgumentException if <code>miterlimit</code> is less
  156. * than 1 and <code>join</code> is JOIN_MITER
  157. * @throws IllegalArgumentException if <code>join</code> is not
  158. * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
  159. * @throws IllegalArgumentException if <code>dash_phase</code>
  160. * is negative and <code>dash</code> is not <code>null</code>
  161. * @throws IllegalArgumentException if the length of
  162. * <code>dash</code> is zero
  163. * @throws IllegalArgumentException if dash lengths are all zero.
  164. */
  165. public BasicStroke(float width, int cap, int join, float miterlimit,
  166. float dash[], float dash_phase) {
  167. if (width < 0.0f) {
  168. throw new IllegalArgumentException("negative width");
  169. }
  170. if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
  171. throw new IllegalArgumentException("illegal end cap value");
  172. }
  173. if (join == JOIN_MITER) {
  174. if (miterlimit < 1.0f) {
  175. throw new IllegalArgumentException("miter limit < 1");
  176. }
  177. } else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
  178. throw new IllegalArgumentException("illegal line join value");
  179. }
  180. if (dash != null) {
  181. if (dash_phase < 0.0f) {
  182. throw new IllegalArgumentException("negative dash phase");
  183. }
  184. boolean allzero = true;
  185. for (int i = 0; i < dash.length; i++) {
  186. float d = dash[i];
  187. if (d > 0.0) {
  188. allzero = false;
  189. } else if (d < 0.0) {
  190. throw new IllegalArgumentException("negative dash length");
  191. }
  192. }
  193. if (allzero) {
  194. throw new IllegalArgumentException("dash lengths all zero");
  195. }
  196. }
  197. this.width = width;
  198. this.cap = cap;
  199. this.join = join;
  200. this.miterlimit = miterlimit;
  201. if (dash != null) {
  202. this.dash = (float []) dash.clone();
  203. }
  204. this.dash_phase = dash_phase;
  205. }
  206. /**
  207. * Constructs a solid <code>BasicStroke</code> with the specified
  208. * attributes.
  209. * @param width the width of the <code>BasicStroke</code>
  210. * @param cap the decoration of the ends of a <code>BasicStroke</code>
  211. * @param join the decoration applied where path segments meet
  212. * @param miterlimit the limit to trim the miter join
  213. * @throws IllegalArgumentException if <code>width</code> is negative
  214. * @throws IllegalArgumentException if <code>cap</code> is not either
  215. * CAP_BUTT, CAP_ROUND or CAP_SQUARE
  216. * @throws IllegalArgumentException if <code>miterlimit</code> is less
  217. * than 1 and <code>join</code> is JOIN_MITER
  218. * @throws IllegalArgumentException if <code>join</code> is not
  219. * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
  220. */
  221. public BasicStroke(float width, int cap, int join, float miterlimit) {
  222. this(width, cap, join, miterlimit, null, 0.0f);
  223. }
  224. /**
  225. * Constructs a solid <code>BasicStroke</code> with the specified
  226. * attributes. The <code>miterlimit</code> parameter is
  227. * unnecessary in cases where the default is allowable or the
  228. * line joins are not specified as JOIN_MITER.
  229. * @param width the width of the <code>BasicStroke</code>
  230. * @param cap the decoration of the ends of a <code>BasicStroke</code>
  231. * @param join the decoration applied where path segments meet
  232. * @throws IllegalArgumentException if <code>width</code> is negative
  233. * @throws IllegalArgumentException if <code>cap</code> is not either
  234. * CAP_BUTT, CAP_ROUND or CAP_SQUARE
  235. * @throws IllegalArgumentException if <code>join</code> is not
  236. * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
  237. */
  238. public BasicStroke(float width, int cap, int join) {
  239. this(width, cap, join, 10.0f, null, 0.0f);
  240. }
  241. /**
  242. * Constructs a solid <code>BasicStroke</code> with the specified
  243. * line width and with default values for the cap and join
  244. * styles.
  245. * @param width the width of the <code>BasicStroke</code>
  246. * @throws IllegalArgumentException if <code>width</code> is negative
  247. */
  248. public BasicStroke(float width) {
  249. this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
  250. }
  251. /**
  252. * Constructs a new <code>BasicStroke</code> with defaults for all
  253. * attributes.
  254. * The default attributes are a solid line of width 1.0, CAP_SQUARE,
  255. * JOIN_MITER, a miter limit of 10.0.
  256. */
  257. public BasicStroke() {
  258. this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
  259. }
  260. /**
  261. * Returns a <code>Shape</code> whose interior defines the
  262. * stroked outline of a specified <code>Shape</code>.
  263. * @param s the <code>Shape</code> boundary be stroked
  264. * @return the <code>Shape</code> of the stroked outline.
  265. */
  266. public Shape createStrokedShape(Shape s) {
  267. FillAdapter filler = new FillAdapter();
  268. PathStroker stroker = new PathStroker(filler);
  269. PathConsumer consumer;
  270. stroker.setPenDiameter(width);
  271. stroker.setPenT4(null);
  272. stroker.setCaps(RasterizerCaps[cap]);
  273. stroker.setCorners(RasterizerCorners[join], miterlimit);
  274. if (dash != null) {
  275. PathDasher dasher = new PathDasher(stroker);
  276. dasher.setDash(dash, dash_phase);
  277. dasher.setDashT4(null);
  278. consumer = dasher;
  279. } else {
  280. consumer = stroker;
  281. }
  282. PathIterator pi = s.getPathIterator(null);
  283. try {
  284. consumer.beginPath();
  285. boolean pathClosed = false;
  286. float mx = 0.0f;
  287. float my = 0.0f;
  288. float point[] = new float[6];
  289. while (!pi.isDone()) {
  290. int type = pi.currentSegment(point);
  291. if (pathClosed == true) {
  292. pathClosed = false;
  293. if (type != PathIterator.SEG_MOVETO) {
  294. // Force current point back to last moveto point
  295. consumer.beginSubpath(mx, my);
  296. }
  297. }
  298. switch (type) {
  299. case PathIterator.SEG_MOVETO:
  300. mx = point[0];
  301. my = point[1];
  302. consumer.beginSubpath(point[0], point[1]);
  303. break;
  304. case PathIterator.SEG_LINETO:
  305. consumer.appendLine(point[0], point[1]);
  306. break;
  307. case PathIterator.SEG_QUADTO:
  308. // Quadratic curves take two points
  309. consumer.appendQuadratic(point[0], point[1],
  310. point[2], point[3]);
  311. break;
  312. case PathIterator.SEG_CUBICTO:
  313. // Cubic curves take three points
  314. consumer.appendCubic(point[0], point[1],
  315. point[2], point[3],
  316. point[4], point[5]);
  317. break;
  318. case PathIterator.SEG_CLOSE:
  319. consumer.closedSubpath();
  320. pathClosed = true;
  321. break;
  322. }
  323. pi.next();
  324. }
  325. consumer.endPath();
  326. } catch (PathException e) {
  327. throw new InternalError("Unable to Stroke shape ("+
  328. e.getMessage()+")");
  329. }
  330. return filler.getShape();
  331. }
  332. /**
  333. * Returns the line width. Line width is represented in user space,
  334. * which is the default-coordinate space used by Java 2D. See the
  335. * <code>Graphics2D</code> class comments for more information on
  336. * the user space coordinate system.
  337. * @return the line width of this <code>BasicStroke</code>.
  338. * @see Graphics2D
  339. */
  340. public float getLineWidth() {
  341. return width;
  342. }
  343. /**
  344. * Returns the end cap style.
  345. * @return the end cap style of this <code>BasicStroke</code> as one
  346. * of the static <code>int</code> values that define possible end cap
  347. * styles.
  348. */
  349. public int getEndCap() {
  350. return cap;
  351. }
  352. /**
  353. * Returns the line join style.
  354. * @return the line join style of the <code>BasicStroke</code> as one
  355. * of the static <code>int</code> values that define possible line
  356. * join styles.
  357. */
  358. public int getLineJoin() {
  359. return join;
  360. }
  361. /**
  362. * Returns the limit of miter joins.
  363. * @return the limit of miter joins of the <code>BasicStroke</code>.
  364. */
  365. public float getMiterLimit() {
  366. return miterlimit;
  367. }
  368. /**
  369. * Returns the array representing the lengths of the dash segments.
  370. * Alternate entries in the array represent the user space lengths
  371. * of the opaque and transparent segments of the dashes.
  372. * As the pen moves along the outline of the <code>Shape</code>
  373. * to be stroked, the user space
  374. * distance that the pen travels is accumulated. The distance
  375. * value is used to index into the dash array.
  376. * The pen is opaque when its current cumulative distance maps
  377. * to an even element of the dash array and transparent otherwise.
  378. * @return the dash array.
  379. */
  380. public float[] getDashArray() {
  381. if (dash == null) {
  382. return null;
  383. }
  384. return (float[]) dash.clone();
  385. }
  386. /**
  387. * Returns the current dash phase.
  388. * The dash phase is a distance specified in user coordinates that
  389. * represents an offset into the dashing pattern. In other words, the dash
  390. * phase defines the point in the dashing pattern that will correspond to
  391. * the beginning of the stroke.
  392. * @return the dash phase as a <code>float</code> value.
  393. */
  394. public float getDashPhase() {
  395. return dash_phase;
  396. }
  397. /**
  398. * Returns the hashcode for this stroke.
  399. * @return a hash code for this stroke.
  400. */
  401. public int hashCode() {
  402. int hash = Float.floatToIntBits(width);
  403. hash = hash * 31 + join;
  404. hash = hash * 31 + cap;
  405. hash = hash * 31 + Float.floatToIntBits(miterlimit);
  406. if (dash != null) {
  407. hash = hash * 31 + Float.floatToIntBits(dash_phase);
  408. for (int i = 0; i < dash.length; i++) {
  409. hash = hash * 31 + Float.floatToIntBits(dash[i]);
  410. }
  411. }
  412. return hash;
  413. }
  414. /**
  415. * Returns true if this BasicStroke represents the same
  416. * stroking operation as the given argument.
  417. */
  418. /**
  419. * Tests if a specified object is equal to this <code>BasicStroke</code>
  420. * by first testing if it is a <code>BasicStroke</code> and then comparing
  421. * its width, join, cap, miter limit, dash, and dash phase attributes with
  422. * those of this <code>BasicStroke</code>.
  423. * @param obj the specified object to compare to this
  424. * <code>BasicStroke</code>
  425. * @return <code>true</code> if the width, join, cap, miter limit, dash, and
  426. * dash phase are the same for both objects;
  427. * <code>false</code> otherwise.
  428. */
  429. public boolean equals(Object obj) {
  430. if (!(obj instanceof BasicStroke)) {
  431. return false;
  432. }
  433. BasicStroke bs = (BasicStroke) obj;
  434. if (width != bs.width) {
  435. return false;
  436. }
  437. if (join != bs.join) {
  438. return false;
  439. }
  440. if (cap != bs.cap) {
  441. return false;
  442. }
  443. if (miterlimit != bs.miterlimit) {
  444. return false;
  445. }
  446. if (dash != null) {
  447. if (dash_phase != bs.dash_phase) {
  448. return false;
  449. }
  450. if (!java.util.Arrays.equals(dash, bs.dash)) {
  451. return false;
  452. }
  453. }
  454. else if (bs.dash != null) {
  455. return false;
  456. }
  457. return true;
  458. }
  459. private static final int RasterizerCaps[] = {
  460. Rasterizer.BUTT, Rasterizer.ROUND, Rasterizer.SQUARE
  461. };
  462. private static final int RasterizerCorners[] = {
  463. Rasterizer.MITER, Rasterizer.ROUND, Rasterizer.BEVEL
  464. };
  465. private class FillAdapter implements PathConsumer {
  466. boolean closed;
  467. GeneralPath path;
  468. public FillAdapter() {
  469. path = new GeneralPath(GeneralPath.WIND_NON_ZERO);
  470. }
  471. public Shape getShape() {
  472. return path;
  473. }
  474. public void beginPath() {}
  475. public void beginSubpath(float x0, float y0) {
  476. if (closed) {
  477. path.closePath();
  478. closed = false;
  479. }
  480. path.moveTo(x0, y0);
  481. }
  482. public void appendLine(float x1, float y1) {
  483. path.lineTo(x1, y1);
  484. }
  485. public void appendQuadratic(float xm, float ym, float x1, float y1) {
  486. path.quadTo(xm, ym, x1, y1);
  487. }
  488. public void appendCubic(float xm, float ym,
  489. float xn, float yn,
  490. float x1, float y1) {
  491. path.curveTo(xm, ym, xn, yn, x1, y1);
  492. }
  493. public void closedSubpath() {
  494. closed = true;
  495. }
  496. public void endPath() {
  497. if (closed) {
  498. path.closePath();
  499. closed = false;
  500. }
  501. }
  502. public void useProxy(FastPathProducer proxy)
  503. throws PathException
  504. {
  505. proxy.sendTo(this);
  506. }
  507. public long getCPathConsumer() {
  508. return 0;
  509. }
  510. public void dispose() {
  511. }
  512. }
  513. }