1
39
40 import java.io.*;
41 import java.awt.*;
42 import java.awt.image.*;
43 import java.util.*;
44 import java.util.zip.*;
45 import java.io.*;
46 import javax.swing.*;
47
48 public class PngEncoder extends Object
49 {
50
51 public static final boolean ENCODE_ALPHA=true;
52
53 public static final boolean NO_ALPHA=false;
54
55 public static final int FILTER_NONE = 0;
56 public static final int FILTER_SUB = 1;
57 public static final int FILTER_UP = 2;
58 public static final int FILTER_LAST = 2;
59
60 public static int compress = 5;
61 public static int filter = FILTER_SUB | FILTER_UP;
62 public static int chunksize;
63
64 protected OutputStream out;
65 protected int width, height;
66 protected CRC32 crc = new CRC32();
67 protected long crcValue;
68
69 public boolean encodeAlpha = true;
70
71 public JLabel status = null;
72 public String message = "";
73
74
75
76
83 public PngEncoder( int width,int height,OutputStream out) {
84 this.width = width;
85 this.height = height;
86 this.out = out;
87 }
88
89
96 protected void writeBytes( byte[] data) throws IOException {
97 out.write(data);
98 crc.update(data);
99 }
100
101
109 protected void writeBytes( byte[] data, int nBytes ) throws IOException {
110 out.write(data,0,nBytes);
111 crc.update(data,0,nBytes);
112 }
113
114
119 protected void writeInt2( int n ) throws IOException {
120 byte[] temp = { (byte)((n >> 8) & 0xff),
121 (byte) (n & 0xff) };
122 writeBytes( temp );
123 }
124
125
130 protected void writeInt4( int n ) throws IOException {
131 byte[] temp = { (byte)((n >> 24) & 0xff),
132 (byte) ((n >> 16) & 0xff ),
133 (byte) ((n >> 8) & 0xff ),
134 (byte) ( n & 0xff ) };
135 out.write( temp );
136 crc.update(temp);
137 }
138
139
145 protected void writeInt4nocrc( int n ) throws IOException {
146 byte[] temp = { (byte)((n >> 24) & 0xff),
147 (byte) ((n >> 16) & 0xff ),
148 (byte) ((n >> 8) & 0xff ),
149 (byte) ( n & 0xff ) };
150 out.write( temp );
151 }
152
153
158 protected void writeByte( int b ) throws IOException {
159 out.write(b);
160 crc.update(b);
161 }
162
163
171 protected void writeString( String s ) throws IOException {
172 writeBytes( s.getBytes() );
173 }
174
175
178 protected void writeHeader() throws IOException {
179 crc.reset();
180 writeInt4nocrc( 13 );
181 writeString( "IHDR");
182
183
184 writeInt4( width );
185 writeInt4( height );
186 writeByte( 8 );
187 writeByte( (encodeAlpha) ? 6 : 2 );
188 writeByte( 0 );
189 writeByte( 0 );
190 writeByte( 0 );
191 crcValue = crc.getValue();
192 writeInt4nocrc( (int) crcValue );
193 }
194
195 int bytesPerPixel = 4;
196 byte[] lastline = null;
197 byte[] raw = null;
198 byte[] line = null;
199 int[] pixels = null;
200 byte[] deflatebuff;
201 int defpos = 0;
202 Deflater scrunch;
203
204
209 public void start() throws IOException {
210
211 byte[] pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10 };
212
213 writeBytes( pngIdBytes );
214 writeHeader();
215
216 bytesPerPixel = (encodeAlpha) ? 4 : 3;
217
218 lastline = new byte[bytesPerPixel*width];
219 line = new byte[bytesPerPixel*width+1];
220 raw = new byte[bytesPerPixel*width];
221 pixels = new int[width];
222 for (int j=0;j<width*bytesPerPixel;j++) lastline[j] = (byte)0;
223
224 scrunch = new Deflater( compress );
225
226 if (chunksize==0) chunksize = 10000;
227
228 deflatebuff = new byte[chunksize];
229 defpos = 0;
230
231 }
232
233
237 public void addSlice(BufferedImage image,
238 JLabel status,String message) throws IOException,CookKillException {
239 int sliceheight = image.getHeight();
240 int progress = -1;
241
242 for (int row=0; row<sliceheight; row++) {
243
244 if (Carto.kill==Carto.PRINTKILL) throw new CookKillException();
245
246 if (row*100/sliceheight > progress && status!=null) {
247 status.setText(message+" "+(progress=row*100/sliceheight)+"%");
248 }
249
250
251 pixels = (int[]) image.getRaster().getDataElements(0,row,width,1,pixels);
252
253 line[0] = (byte) filter;
254
255
256 for (int j=0;j<width;j++) {
257 for (int k=0;k<3;k++)
258 raw[j*bytesPerPixel+k] = (byte)((pixels[j] >> (8*(2-k))) & 0xff);
259 if (encodeAlpha) raw[j*bytesPerPixel+3] = (byte) ((pixels[j] >> 24) & 0xff );
260 }
261
262
263 for (int j = 0;j<bytesPerPixel*width;j++) {
264 if (filter == FILTER_SUB && j>=bytesPerPixel)
265 line[j+1] = (byte)(raw[j] - raw[j-bytesPerPixel]);
266 else if (filter == FILTER_UP)
267 line[j+1] = (byte)(raw[j] - lastline[j]);
268 else line[j+1] = raw[j];
269 }
270 byte[] tmp = lastline;
271 lastline = raw;
272 raw = tmp;
273
274 while (!scrunch.needsInput()) {
275 defpos += scrunch.deflate(deflatebuff,defpos,chunksize-defpos);
276 if (defpos==chunksize) {
277 writeDat(deflatebuff,defpos);
278 defpos = 0;
279 }
280 }
281
282 scrunch.setInput(line);
283 }
284 }
285
286 public void finish() throws IOException{
287 try {
288 scrunch.finish();
289
290 while (!scrunch.finished()) {
291 defpos += scrunch.deflate(deflatebuff,defpos,chunksize-defpos);
292 if (defpos==chunksize || scrunch.finished()) {
293 writeDat(deflatebuff,defpos);
294 defpos = 0;
295 }
296 }
297 writeEnd();
298 } finally {clean();}
299 }
300
301 public void clean() {
302 if (scrunch!=null) scrunch.end();
303 deflatebuff=null;
304 lastline=null;
305 line=null;
306 raw=null;
307 pixels=null;
308 if (out!=null) try {out.close();}catch(IOException e){}
309 out=null;
310 }
311
312
317 protected void writeDat(byte[] data,int size) throws IOException {
318 crc.reset();
319 writeInt4nocrc(size);
320 writeString("IDAT");
321
322 writeBytes(data,size);
323
324 crcValue = crc.getValue();
325 writeInt4nocrc( (int) crcValue );
326 }
327
328
331 protected void writeEnd() throws IOException {
332 writeInt4nocrc( 0 );
333 crc.reset();
334 writeString( "IEND" );
335 crcValue = crc.getValue();
336 writeInt4nocrc( (int) crcValue );
337 }
338 }
339
340
341
342