001: /*
002: * $Id: PDFShapeCmd.java,v 1.2 2007/12/20 18:17:41 rbair Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package com.sun.pdfview;
023:
024: import java.awt.BasicStroke;
025: import java.awt.geom.AffineTransform;
026: import java.awt.geom.GeneralPath;
027: import java.awt.geom.PathIterator;
028: import java.awt.geom.Rectangle2D;
029:
030: /**
031: * Encapsulates a path. Also contains extra fields and logic to check
032: * for consecutive abutting anti-aliased regions. We stroke the shared
033: * line between these regions again with a 1-pixel wide line so that
034: * the background doesn't show through between them.
035: *
036: * @author Mike Wessler
037: */
038: public class PDFShapeCmd extends PDFCmd {
039: /** stroke the outline of the path with the stroke paint */
040: public static final int STROKE = 1;
041:
042: /** fill the path with the fill paint */
043: public static final int FILL = 2;
044:
045: /** perform both stroke and fill */
046: public static final int BOTH = 3;
047:
048: /** set the clip region to the path */
049: public static final int CLIP = 4;
050:
051: /** base path */
052: private GeneralPath gp;
053:
054: /** the style */
055: private int style;
056:
057: /** the bounding box of the path */
058: private Rectangle2D bounds;
059:
060: /** the stroke style for the anti-antialias stroke */
061: BasicStroke againstroke = new BasicStroke(2, BasicStroke.CAP_BUTT,
062: BasicStroke.JOIN_BEVEL);
063:
064: /**
065: * create a new PDFShapeCmd and check it against the previous one
066: * to find any shared edges.
067: * @param gp the path
068: * @param style the style: an OR of STROKE, FILL, or CLIP. As a
069: * convenience, BOTH = STROKE | FILL.
070: */
071: public PDFShapeCmd(GeneralPath gp, int style) {
072: this .gp = new GeneralPath(gp);
073: this .style = style;
074: bounds = gp.getBounds2D();
075: }
076:
077: /**
078: * perform the stroke and record the dirty region
079: */
080: public Rectangle2D execute(PDFRenderer state) {
081: Rectangle2D rect = null;
082:
083: if ((style & FILL) != 0) {
084: rect = state.fill(gp);
085:
086: GeneralPath strokeagain = checkOverlap(state);
087: if (strokeagain != null) {
088: state.draw(strokeagain, againstroke);
089: }
090:
091: if (gp != null) {
092: state.setLastShape(gp);
093: }
094: }
095: if ((style & STROKE) != 0) {
096: Rectangle2D strokeRect = state.stroke(gp);
097: if (rect == null) {
098: rect = strokeRect;
099: } else {
100: rect = rect.createUnion(strokeRect);
101: }
102: }
103: if ((style & CLIP) != 0) {
104: state.clip(gp);
105: }
106:
107: return rect;
108: }
109:
110: /**
111: * Check for overlap with the previous shape to make anti-aliased shapes
112: * that are near each other look good
113: */
114: private GeneralPath checkOverlap(PDFRenderer state) {
115: if (style == FILL && gp != null && state.getLastShape() != null) {
116: float mypoints[] = new float[16];
117: float prevpoints[] = new float[16];
118:
119: int mycount = getPoints(gp, mypoints);
120: int prevcount = getPoints(state.getLastShape(), prevpoints);
121:
122: // now check mypoints against prevpoints for opposite pairs:
123: if (mypoints != null && prevpoints != null) {
124: for (int i = 0; i < prevcount; i += 4) {
125: for (int j = 0; j < mycount; j += 4) {
126: if ((Math.abs(mypoints[j + 2] - prevpoints[i]) < 0.01
127: && Math.abs(mypoints[j + 3]
128: - prevpoints[i + 1]) < 0.01
129: && Math.abs(mypoints[j]
130: - prevpoints[i + 2]) < 0.01 && Math
131: .abs(mypoints[j + 1]
132: - prevpoints[i + 3]) < 0.01)) {
133: GeneralPath strokeagain = new GeneralPath();
134: strokeagain.moveTo(mypoints[j],
135: mypoints[j + 1]);
136: strokeagain.lineTo(mypoints[j + 2],
137: mypoints[j + 3]);
138: return strokeagain;
139: }
140: }
141: }
142: }
143: }
144:
145: // no issues
146: return null;
147: }
148:
149: /**
150: * Get an array of 16 points from a path
151: * @return the number of points we actually got
152: */
153: private int getPoints(GeneralPath path, float[] mypoints) {
154: int count = 0;
155: float x = 0;
156: float y = 0;
157: float startx = 0;
158: float starty = 0;
159: float[] coords = new float[6];
160:
161: PathIterator pi = path.getPathIterator(new AffineTransform());
162: while (!pi.isDone()) {
163: if (count >= mypoints.length) {
164: mypoints = null;
165: break;
166: }
167:
168: int pathtype = pi.currentSegment(coords);
169: switch (pathtype) {
170: case PathIterator.SEG_MOVETO:
171: startx = x = coords[0];
172: starty = y = coords[1];
173: break;
174: case PathIterator.SEG_LINETO:
175: mypoints[count++] = x;
176: mypoints[count++] = y;
177: x = mypoints[count++] = coords[0];
178: y = mypoints[count++] = coords[1];
179: break;
180: case PathIterator.SEG_QUADTO:
181: x = coords[2];
182: y = coords[3];
183: break;
184: case PathIterator.SEG_CUBICTO:
185: x = mypoints[4];
186: y = mypoints[5];
187: break;
188: case PathIterator.SEG_CLOSE:
189: mypoints[count++] = x;
190: mypoints[count++] = y;
191: x = mypoints[count++] = startx;
192: y = mypoints[count++] = starty;
193: break;
194: }
195:
196: pi.next();
197: }
198:
199: return count;
200: }
201:
202: /** Get detailed information about this shape
203: */
204: @Override
205: public String getDetails() {
206: StringBuffer sb = new StringBuffer();
207:
208: Rectangle2D b = gp.getBounds2D();
209: sb.append("ShapeCommand at: " + b.getX() + ", " + b.getY()
210: + "\n");
211: sb.append("Size: " + b.getWidth() + " x " + b.getHeight()
212: + "\n");
213:
214: sb.append("Mode: ");
215: if ((style & FILL) != 0) {
216: sb.append("FILL ");
217: }
218: if ((style & STROKE) != 0) {
219: sb.append("STROKE ");
220: }
221: if ((style & CLIP) != 0) {
222: sb.append("CLIP");
223: }
224:
225: return sb.toString();
226: }
227: }
|