001: /*
002: * $Id: ShaderType2.java,v 1.2 2007/12/20 18:33:30 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.pattern;
023:
024: import java.awt.Paint;
025: import java.awt.PaintContext;
026: import java.awt.Rectangle;
027: import java.awt.RenderingHints;
028: import java.awt.Transparency;
029: import java.awt.color.ColorSpace;
030: import java.awt.geom.AffineTransform;
031: import java.awt.geom.Point2D;
032: import java.awt.geom.Rectangle2D;
033: import java.awt.image.ColorModel;
034: import java.awt.image.ComponentColorModel;
035: import java.awt.image.DataBuffer;
036: import java.awt.image.Raster;
037: import java.awt.image.WritableRaster;
038: import java.io.IOException;
039:
040: import com.sun.pdfview.PDFObject;
041: import com.sun.pdfview.PDFPaint;
042: import com.sun.pdfview.PDFParseException;
043: import com.sun.pdfview.function.PDFFunction;
044:
045: /**
046: * A shader that performs axial shader based on a function.
047: */
048: public class ShaderType2 extends PDFShader {
049: /** the start of the axis */
050: private Point2D axisStart;
051:
052: /** the end of the axis */
053: private Point2D axisEnd;
054:
055: /** the domain minimum */
056: private float minT = 0f;
057:
058: /** the domain maximum */
059: private float maxT = 1f;
060:
061: /** whether to extend the start of the axis */
062: private boolean extendStart = false;
063:
064: /** whether to extend the end of the axis */
065: private boolean extendEnd = false;
066:
067: /** functions, as an array of either 1 or n functions */
068: private PDFFunction[] functions;
069:
070: /** Creates a new instance of ShaderType2 */
071: public ShaderType2() {
072: super (2);
073: }
074:
075: /**
076: * Parse the shader-specific data
077: */
078: public void parse(PDFObject shaderObj) throws IOException {
079: // read the axis coordinates (required)
080: PDFObject coordsObj = shaderObj.getDictRef("Coords");
081: if (coordsObj == null) {
082: throw new PDFParseException("No coordinates found!");
083: }
084: PDFObject[] coords = coordsObj.getArray();
085: Point2D start = new Point2D.Float(coords[0].getFloatValue(),
086: coords[1].getFloatValue());
087: Point2D end = new Point2D.Float(coords[2].getFloatValue(),
088: coords[3].getFloatValue());
089: setAxisStart(start);
090: setAxisEnd(end);
091:
092: // read the domain (optional)
093: PDFObject domainObj = shaderObj.getDictRef("Domain");
094: if (domainObj != null) {
095: PDFObject[] domain = domainObj.getArray();
096: setMinT(domain[0].getFloatValue());
097: setMaxT(domain[1].getFloatValue());
098: }
099:
100: // read the functions (required)
101: PDFObject functionObj = shaderObj.getDictRef("Function");
102: if (functionObj == null) {
103: throw new PDFParseException(
104: "No function defined for shader!");
105: }
106: PDFObject[] functionArray = functionObj.getArray();
107: PDFFunction[] functions = new PDFFunction[functionArray.length];
108: for (int i = 0; i < functions.length; i++) {
109: functions[i] = PDFFunction.getFunction(functionArray[i]);
110: }
111: setFunctions(functions);
112:
113: // read the extend array (optional)
114: PDFObject extendObj = shaderObj.getDictRef("Extend");
115: if (extendObj != null) {
116: PDFObject[] extendArray = extendObj.getArray();
117: setExtendStart(extendArray[0].getBooleanValue());
118: setExtendEnd(extendArray[1].getBooleanValue());
119: }
120:
121: }
122:
123: /**
124: * Create a paint that paints this pattern
125: */
126: public PDFPaint getPaint() {
127: return PDFPaint.getPaint(new Type2Paint());
128: }
129:
130: /**
131: * Get the start of the axis
132: */
133: public Point2D getAxisStart() {
134: return axisStart;
135: }
136:
137: /**
138: * Set the start of the axis
139: */
140: protected void setAxisStart(Point2D axisStart) {
141: this .axisStart = axisStart;
142: }
143:
144: /**
145: * Get the end of the axis
146: */
147: public Point2D getAxisEnd() {
148: return axisEnd;
149: }
150:
151: /**
152: * Set the start of the axis
153: */
154: protected void setAxisEnd(Point2D axisEnd) {
155: this .axisEnd = axisEnd;
156: }
157:
158: /**
159: * Get the domain minimum
160: */
161: public float getMinT() {
162: return minT;
163: }
164:
165: /**
166: * Set the domain minimum
167: */
168: protected void setMinT(float minT) {
169: this .minT = minT;
170: }
171:
172: /**
173: * Get the domain maximum
174: */
175: public float getMaxT() {
176: return maxT;
177: }
178:
179: /**
180: * Set the domain maximum
181: */
182: protected void setMaxT(float maxT) {
183: this .maxT = maxT;
184: }
185:
186: /**
187: * Get whether to extend the start of the axis
188: */
189: public boolean getExtendStart() {
190: return extendStart;
191: }
192:
193: /**
194: * Set whether to extend the start of the axis
195: */
196: protected void setExtendStart(boolean extendStart) {
197: this .extendStart = extendStart;
198: }
199:
200: /**
201: * Get whether to extend the end of the axis
202: */
203: public boolean getExtendEnd() {
204: return extendEnd;
205: }
206:
207: /**
208: * Set whether to extend the end of the axis
209: */
210: protected void setExtendEnd(boolean extendEnd) {
211: this .extendEnd = extendEnd;
212: }
213:
214: /**
215: * Get the functions associated with this shader
216: */
217: public PDFFunction[] getFunctions() {
218: return functions;
219: }
220:
221: /**
222: * Set the functions associated with this shader
223: */
224: protected void setFunctions(PDFFunction[] functions) {
225: this .functions = functions;
226: }
227:
228: /**
229: * A subclass of paint that uses this shader to generate a paint
230: */
231: class Type2Paint implements Paint {
232: public Type2Paint() {
233: }
234:
235: /** create a paint context */
236: public PaintContext createContext(ColorModel cm,
237: Rectangle deviceBounds, Rectangle2D userBounds,
238: AffineTransform xform, RenderingHints hints) {
239: ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
240: ColorModel model = new ComponentColorModel(cs, true, false,
241: Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
242:
243: Point2D devStart = xform.transform(getAxisStart(), null);
244: Point2D devEnd = xform.transform(getAxisEnd(), null);
245:
246: return new Type2PaintContext(model, devStart, devEnd);
247: }
248:
249: public int getTransparency() {
250: return Transparency.TRANSLUCENT;
251: }
252: }
253:
254: /**
255: * A simple paint context that uses an existing raster in device
256: * space to generate pixels
257: */
258: class Type2PaintContext implements PaintContext {
259: /** the color model */
260: private ColorModel colorModel;
261:
262: /** the start of the axis */
263: private Point2D start;
264:
265: /** the end of the axis */
266: private Point2D end;
267:
268: /**
269: * Create a paint context
270: */
271: Type2PaintContext(ColorModel colorModel, Point2D start,
272: Point2D end) {
273: this .colorModel = colorModel;
274: this .start = start;
275: this .end = end;
276: }
277:
278: public void dispose() {
279: colorModel = null;
280: }
281:
282: public ColorModel getColorModel() {
283: return colorModel;
284: }
285:
286: public Raster getRaster(int x, int y, int w, int h) {
287: ColorSpace cs = getColorModel().getColorSpace();
288:
289: PDFFunction functions[] = getFunctions();
290: int numComponents = cs.getNumComponents();
291:
292: float x0 = (float) start.getX();
293: float x1 = (float) end.getX();
294: float y0 = (float) start.getY();
295: float y1 = (float) end.getY();
296:
297: float[] inputs = new float[1];
298: float[] outputs = new float[numComponents];
299:
300: // all the data, plus alpha channel
301: int[] data = new int[w * h * (numComponents + 1)];
302:
303: // for each device coordinate
304: for (int j = 0; j < h; j++) {
305: for (int i = 0; i < w + 8; i += 8) {
306: // find t for that user coordinate
307: float xp = getXPrime(i + x, j + y, x0, y0, x1, y1);
308: float t = getT(xp);
309:
310: // calculate the pixel values at t
311: inputs[0] = t;
312: if (functions.length == 1) {
313: functions[0].calculate(inputs, 0, outputs, 0);
314: } else {
315: for (int c = 0; c < functions.length; c++) {
316: functions[c].calculate(inputs, 0, outputs,
317: c);
318: }
319: }
320:
321: for (int q = i; q < i + 8 && q < w; q++) {
322: int base = (j * w + q) * (numComponents + 1);
323: for (int c = 0; c < numComponents; c++) {
324: data[base + c] = (int) (outputs[c] * 255);
325: }
326: data[base + numComponents] = 255;
327: }
328: }
329: }
330:
331: WritableRaster raster = getColorModel()
332: .createCompatibleWritableRaster(w, h);
333: raster.setPixels(0, 0, w, h, data);
334:
335: Raster child = raster.createTranslatedChild(x, y);
336: return child;
337: }
338:
339: /**
340: * x' = (x1 - x0) * (x - x0) + (y1 - y0) * (y - y0)
341: * -------------------------------------------
342: * (x1 - x0)^2 + (y1 - y0)^2
343: */
344: private float getXPrime(float x, float y, float x0, float y0,
345: float x1, float y1) {
346:
347: double tp = (((x1 - x0) * (x - x0)) + ((y1 - y0) * (y - y0)))
348: / (Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2));
349:
350: return (float) tp;
351: }
352:
353: /**
354: * t = t0 + (t1 - t0) x x'
355: */
356: private float getT(float xp) {
357: float t0 = getMinT();
358: float t1 = getMaxT();
359:
360: if (xp < 0) {
361: return t0;
362: } else if (xp > 1) {
363: return t1;
364: } else {
365: return t0 + ((t1 - t0) * xp);
366: }
367: }
368: }
369: }
|