1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55:
56: public class PNGDecoder
57: {
58: private PNGHeader header;
59: private byte[] raster;
60: private byte[] scanline, lastScanline;
61: private byte[] filterType;
62: private int offset, length;
63: private int currentScanline;
64: private final int stride;
65: private Inflater inflater;
66: private boolean readFilter;
67: private int bpp;
68:
69:
72: public PNGDecoder(PNGHeader header)
73: {
74: this.header = header;
75: offset = 0;
76: inflater = new Inflater();
77: stride = header.getScanlineStride();
78: length = stride * header.getHeight();
79:
80:
81: raster = new byte[ length ];
82: scanline = new byte[ stride ];
83: lastScanline = new byte[ stride ];
84: currentScanline = 0;
85: readFilter = true;
86: bpp = header.bytesPerPixel();
87: filterType = new byte[1];
88: inflater = new Inflater();
89: }
90:
91: private int getBytes( byte[] buf, int offset ) throws PNGException
92: {
93: try
94: {
95: return inflater.inflate( buf, offset, buf.length - offset);
96: }
97: catch(DataFormatException dfe)
98: {
99: throw new PNGException("Error inflating data.");
100: }
101: }
102:
103:
106: public void addData( PNGData chunk ) throws PNGException
107: {
108: int n = 0;
109: if( isFinished() )
110: return;
111: chunk.feedToInflater( inflater );
112: do
113: {
114: if( readFilter )
115: if( getBytes( filterType, 0 ) < 1 )
116: return;
117:
118: n = getBytes( scanline, offset );
119:
120: if( offset + n < stride )
121: {
122: offset += n;
123: readFilter = false;
124: }
125: else
126: {
127: scanline = PNGFilter.unFilterScanline( filterType[0], scanline,
128: lastScanline, bpp );
129: System.arraycopy( scanline, 0,
130: raster, currentScanline * stride, stride );
131: lastScanline = scanline;
132: scanline = new byte[scanline.length];
133: currentScanline++;
134: readFilter = true;
135: offset = 0;
136: }
137: }
138: while( n > 0 && currentScanline < header.getHeight() );
139: }
140:
141:
145: public WritableRaster getRaster( PNGHeader header )
146: {
147: SampleModel sm = null;
148: DataBuffer db = null;
149: int t;
150: int width = header.getWidth();
151: int height = header.getHeight();
152: int depth = header.getDepth();
153:
154: switch( header.getColorType() )
155: {
156: case PNGHeader.GRAYSCALE_WITH_ALPHA:
157: if( depth == 8 )
158: {
159: t = DataBuffer.TYPE_BYTE;
160: db = getByteBuffer();
161: }
162: else
163: {
164: t = DataBuffer.TYPE_USHORT;
165: db = getShortBuffer();
166: }
167: sm = new ComponentSampleModel(t, width, height, 2, width * 2,
168: new int[]{0, 1});
169: break;
170:
171: case PNGHeader.GRAYSCALE:
172: switch( depth )
173: {
174: case 16:
175: sm = new ComponentSampleModel(DataBuffer.TYPE_USHORT,
176: width, height, 1, width,
177: new int[]{ 0 });
178: db = getShortBuffer();
179: break;
180:
181: case 8:
182: sm = new ComponentSampleModel(DataBuffer.TYPE_BYTE,
183: width, height, 1, width,
184: new int[]{ 0 });
185: db = getByteBuffer();
186: break;
187:
188: default:
189: sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
190: width, height, depth);
191: db = getByteBuffer();
192: break;
193: }
194: break;
195:
196: case PNGHeader.RGB:
197: if( depth == 8 )
198: {
199: t = DataBuffer.TYPE_BYTE;
200: db = getByteBuffer();
201: }
202: else
203: {
204: t = DataBuffer.TYPE_USHORT;
205: db = getShortBuffer();
206: }
207: sm = new ComponentSampleModel(t, width, height, 3, 3 * width,
208: new int[]{0, 1, 2});
209: break;
210:
211: case PNGHeader.RGB_WITH_ALPHA:
212: if( depth == 8 )
213: {
214: t = DataBuffer.TYPE_BYTE;
215: db = getByteBuffer();
216: }
217: else
218: {
219: t = DataBuffer.TYPE_USHORT;
220: db = getShortBuffer();
221: }
222:
223: sm = new ComponentSampleModel(t, width, height, 4, width * 4,
224: new int[]{0, 1, 2, 3});
225: break;
226:
227: case PNGHeader.INDEXED:
228: if( depth == 8 )
229: sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_BYTE,
230: width, height,
231: new int[] {0xFF});
232: else
233: sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
234: width, height, depth);
235: db = getByteBuffer();
236: break;
237: }
238:
239: return Raster.createWritableRaster(sm, db, null);
240: }
241:
242:
246: private DataBuffer getShortBuffer()
247: {
248: short[] data = new short[(raster.length >> 1)];
249: for( int i = 0; i < data.length; i++ )
250: data[i] = (short)(((raster[i * 2] & 0xFF) << 8) |
251: (raster[i * 2 + 1] & 0xFF));
252: return new DataBufferUShort( data, data.length );
253: }
254:
255:
258: private DataBuffer getByteBuffer()
259: {
260: return new DataBufferByte( raster, raster.length );
261: }
262:
263: public ColorModel getColorModel( ColorSpace cs,
264: int colorType, int depth )
265: {
266: int[] bits;
267: boolean hasAlpha = false;
268: int transferType;
269:
270: switch( colorType )
271: {
272: case PNGHeader.GRAYSCALE_WITH_ALPHA:
273: if( cs == null )
274: cs = ColorSpace.getInstance( ColorSpace.CS_GRAY );
275: hasAlpha = true;
276: bits = new int[]{ depth, depth };
277: break;
278:
279: case PNGHeader.RGB:
280: bits = new int[]{ depth, depth, depth };
281: break;
282:
283: case PNGHeader.RGB_WITH_ALPHA:
284: hasAlpha = true;
285: bits = new int[]{ depth, depth, depth, depth };
286: break;
287:
288: case PNGHeader.GRAYSCALE:
289: if( depth < 8 )
290: return grayPalette( depth );
291:
292: if( cs == null )
293: cs = ColorSpace.getInstance( ColorSpace.CS_GRAY );
294: bits = new int[]{ depth };
295: break;
296:
297: default:
298: case PNGHeader.INDEXED:
299: return null;
300: }
301:
302: if( cs == null )
303: cs = ColorSpace.getInstance( ColorSpace.CS_sRGB );
304:
305:
306: return new ComponentColorModel(cs, bits, hasAlpha, false,
307: (hasAlpha ?
308: ComponentColorModel.TRANSLUCENT :
309: ComponentColorModel.OPAQUE),
310: ((depth == 16) ? DataBuffer.TYPE_USHORT :
311: DataBuffer.TYPE_BYTE));
312: }
313:
314: private IndexColorModel grayPalette(int depth)
315: {
316: byte[] c = new byte[ (1 << depth) ];
317: for(int i = 0; i < c.length; i++)
318: c[i] = (byte)(255.0 * (((double)i) / ((double)c.length - 1.0)));
319: return new IndexColorModel(8, c.length, c, c, c);
320: }
321:
322: public byte[] getRaster()
323: {
324: return raster;
325: }
326:
327: public boolean isFinished()
328: {
329: return currentScanline >= header.getHeight();
330: }
331: }