KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > keypoint > PngEncoderB


1 package com.keypoint;
2
3 /**
4  * PngEncoderB takes a Java BufferedImage object and creates a byte string which can be saved as a PNG file.
5  * The encoder will accept BufferedImages with eight-bit samples
6  * or 4-byte ARGB samples.
7  *
8  * There is also code to handle 4-byte samples returned as
9  * one int per pixel, but that has not been tested.
10  *
11  * Thanks to Jay Denny at KeyPoint Software
12  * http://www.keypoint.com/
13  * who let me develop this code on company time.
14  *
15  * You may contact me with (probably very-much-needed) improvements,
16  * comments, and bug fixes at:
17  *
18  * david@catcode.com
19  *
20  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Lesser General Public
22  * License as published by the Free Software Foundation; either
23  * version 2.1 of the License, or (at your option) any later version.
24  *
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  * Lesser General Public License for more details.
29  *
30  * You should have received a copy of the GNU Lesser General Public
31  * License along with this library; if not, write to the Free Software
32  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33  * A copy of the GNU LGPL may be found at
34  * http://www.gnu.org/copyleft/lesser.html,
35  *
36  * @author J. David Eisenberg
37  * @version 1.4, 31 March 2000
38  */

39
40 import java.awt.image.BufferedImage JavaDoc;
41 import java.awt.image.DataBuffer JavaDoc;
42 import java.awt.image.IndexColorModel JavaDoc;
43 import java.awt.image.WritableRaster JavaDoc;
44 import java.io.ByteArrayOutputStream JavaDoc;
45 import java.io.IOException JavaDoc;
46 import java.util.zip.Deflater JavaDoc;
47 import java.util.zip.DeflaterOutputStream JavaDoc;
48
49 public class PngEncoderB extends PngEncoder {
50     protected BufferedImage JavaDoc image;
51     protected WritableRaster JavaDoc wRaster;
52     protected int tType;
53
54     /**
55      * Class constructor
56      */

57     public PngEncoderB() {
58         this(null, false, FILTER_NONE, 0);
59     }
60
61     /**
62      * Class constructor specifying BufferedImage to encode, with no alpha channel encoding.
63      *
64      * @param image A Java BufferedImage object
65      */

66     public PngEncoderB(BufferedImage JavaDoc image) {
67         this(image, false, FILTER_NONE, 0);
68     }
69
70     /**
71      * Class constructor specifying BufferedImage to encode, and whether to encode alpha.
72      *
73      * @param image A Java BufferedImage object
74      * @param encodeAlpha Encode the alpha channel? false=no; true=yes
75      */

76     public PngEncoderB(BufferedImage JavaDoc image, boolean encodeAlpha) {
77         this(image, encodeAlpha, FILTER_NONE, 0);
78     }
79
80     /**
81      * Class constructor specifying BufferedImage to encode, whether to encode alpha, and filter to use.
82      *
83      * @param image A Java BufferedImage object
84      * @param encodeAlpha Encode the alpha channel? false=no; true=yes
85      * @param whichFilter 0=none, 1=sub, 2=up
86      */

87     public PngEncoderB(BufferedImage JavaDoc image, boolean encodeAlpha,
88                        int whichFilter) {
89         this(image, encodeAlpha, whichFilter, 0);
90     }
91
92     /**
93      * Class constructor specifying BufferedImage source to encode, whether to encode alpha, filter to use, and compression level
94      *
95      * @param image A Java BufferedImage object
96      * @param encodeAlpha Encode the alpha channel? false=no; true=yes
97      * @param whichFilter 0=none, 1=sub, 2=up
98      * @param compLevel 0..9
99      */

100     public PngEncoderB(BufferedImage JavaDoc image, boolean encodeAlpha,
101                        int whichFilter, int compLevel) {
102         this.image = image;
103         this.encodeAlpha = encodeAlpha;
104         setFilter(whichFilter);
105         if (compLevel >= 0 && compLevel <= 9) {
106             this.compressionLevel = compLevel;
107         }
108     }
109
110     /**
111      * Set the BufferedImage to be encoded
112      *
113      * @param image A Java BufferedImage object
114      */

115     public void setImage(BufferedImage JavaDoc image) {
116         this.image = image;
117         pngBytes = null;
118     }
119
120     /**
121      * Creates an array of bytes that is the PNG equivalent of the current image, specifying whether to encode alpha or not.
122      *
123      * @param encodeAlpha boolean false=no alpha, true=encode alpha
124      * @return an array of bytes, or null if there was a problem
125      */

126     public byte[] pngEncode(boolean encodeAlpha) {
127         byte[] pngIdBytes = {-119, 80, 78, 71, 13, 10, 26, 10};
128         int i;
129
130         if (image == null) {
131             return null;
132         }
133         width = image.getWidth(null);
134         height = image.getHeight(null);
135 // this.image = image;
136

137         if (!establishStorageInfo()) {
138             return null;
139         }
140         
141         /*
142          * start with an array that is big enough to hold all the pixels
143          * (plus filter bytes), and an extra 200 bytes for header info
144          */

145         pngBytes = new byte[((width + 1) * height * 3) + 200];
146
147         /*
148          * keep track of largest byte written to the array
149          */

150         maxPos = 0;
151
152         bytePos = writeBytes(pngIdBytes, 0);
153         hdrPos = bytePos;
154         writeHeader();
155         dataPos = bytePos;
156         if (writeImageData()) {
157             writeEnd();
158             pngBytes = resizeByteArray(pngBytes, maxPos);
159         } else {
160             pngBytes = null;
161         }
162         return pngBytes;
163     }
164
165     /**
166      * Creates an array of bytes that is the PNG equivalent of the current image.
167      * Alpha encoding is determined by its setting in the constructor.
168      *
169      * @return an array of bytes, or null if there was a problem
170      */

171     public byte[] pngEncode() {
172         return pngEncode(encodeAlpha);
173     }
174
175     /**
176      * Get and set variables that determine how picture is stored.
177      * <p/>
178      * Retrieves the writable raster of the buffered image,
179      * as well its transfer type.
180      * <p/>
181      * Sets number of output bytes per pixel, and, if only
182      * eight-bit bytes, turns off alpha encoding.
183      *
184      * @return true if 1-byte or 4-byte data, false otherwise
185      */

186     protected boolean establishStorageInfo() {
187         int dataBytes;
188
189         wRaster = image.getRaster();
190         dataBytes = wRaster.getNumDataElements();
191         tType = wRaster.getTransferType();
192
193         if (((tType == DataBuffer.TYPE_BYTE) && (dataBytes == 4)) ||
194                 ((tType == DataBuffer.TYPE_INT) && (dataBytes == 1))) {
195             bytesPerPixel = (encodeAlpha) ? 4 : 3;
196         } else if ((tType == DataBuffer.TYPE_BYTE) && (dataBytes == 1)) {
197             bytesPerPixel = 1;
198             encodeAlpha = false; // one-byte samples
199
} else {
200             return false;
201         }
202         return true;
203     }
204
205     /**
206      * Write a PNG "IHDR" chunk into the pngBytes array.
207      */

208     protected void writeHeader() {
209         int startPos;
210
211         startPos = bytePos = writeInt4(13, bytePos);
212         bytePos = writeString("IHDR", bytePos);
213         width = image.getWidth(null);
214         height = image.getHeight(null);
215         bytePos = writeInt4(width, bytePos);
216         bytePos = writeInt4(height, bytePos);
217         bytePos = writeByte(8, bytePos); // bit depth
218
if (bytesPerPixel != 1) {
219             bytePos = writeByte((encodeAlpha) ? 6 : 2, bytePos); // direct model
220
} else {
221             bytePos = writeByte(3, bytePos); // indexed
222
}
223         bytePos = writeByte(0, bytePos); // compression method
224
bytePos = writeByte(0, bytePos); // filter method
225
bytePos = writeByte(0, bytePos); // no interlace
226
crc.reset();
227         crc.update(pngBytes, startPos, bytePos - startPos);
228         crcValue = crc.getValue();
229         bytePos = writeInt4((int) crcValue, bytePos);
230     }
231
232     protected void writePalette(IndexColorModel JavaDoc icm) {
233         byte[] redPal = new byte[256];
234         byte[] greenPal = new byte[256];
235         byte[] bluePal = new byte[256];
236         byte[] allPal = new byte[768];
237         int i;
238
239         icm.getReds(redPal);
240         icm.getGreens(greenPal);
241         icm.getBlues(bluePal);
242         for (i = 0; i < 256; i++) {
243             allPal[i * 3] = redPal[i];
244             allPal[i * 3 + 1] = greenPal[i];
245             allPal[i * 3 + 2] = bluePal[i];
246         }
247         bytePos = writeInt4(768, bytePos);
248         bytePos = writeString("PLTE", bytePos);
249         crc.reset();
250         crc.update("PLTE".getBytes());
251         bytePos = writeBytes(allPal, bytePos);
252         crc.update(allPal);
253         crcValue = crc.getValue();
254         bytePos = writeInt4((int) crcValue, bytePos);
255     }
256
257     /**
258      * Write the image data into the pngBytes array.
259      * This will write one or more PNG "IDAT" chunks. In order
260      * to conserve memory, this method grabs as many rows as will
261      * fit into 32K bytes, or the whole image; whichever is less.
262      *
263      * @return true if no errors; false if error grabbing pixels
264      */

265     protected boolean writeImageData() {
266         int rowsLeft = height; // number of rows remaining to write
267
int startRow = 0; // starting row to process this time through
268
int nRows; // how many rows to grab at a time
269

270         byte[] scanLines; // the scan lines to be compressed
271
int scanPos; // where we are in the scan lines
272
int startPos; // where this line's actual pixels start (used for filtering)
273
int readPos; // position from which source pixels are read
274

275         byte[] compressedLines; // the resultant compressed lines
276
int nCompressed; // how big is the compressed area?
277

278         byte[] pixels; // storage area for byte-sized pixels
279
int[] iPixels; // storage area for int-sized pixels
280

281         Deflater JavaDoc scrunch = new Deflater JavaDoc(compressionLevel);
282         ByteArrayOutputStream JavaDoc outBytes =
283                 new ByteArrayOutputStream JavaDoc(1024);
284
285         DeflaterOutputStream JavaDoc compBytes =
286                 new DeflaterOutputStream JavaDoc(outBytes, scrunch);
287
288         if (bytesPerPixel == 1) {
289             writePalette((IndexColorModel JavaDoc) image.getColorModel());
290         }
291
292         try {
293             while (rowsLeft > 0) {
294                 nRows = Math.min(32767 / (width * (bytesPerPixel + 1)), rowsLeft);
295                 // nRows = rowsLeft;
296

297                 /*
298                  * Create a data chunk. scanLines adds "nRows" for
299                  * the filter bytes.
300                  */

301                 scanLines = new byte[width * nRows * bytesPerPixel + nRows];
302
303                 if (filter == FILTER_SUB) {
304                     leftBytes = new byte[16];
305                 }
306                 if (filter == FILTER_UP) {
307                     priorRow = new byte[width * bytesPerPixel];
308                 }
309
310                 if (tType == DataBuffer.TYPE_BYTE) {
311                     pixels = (byte[]) wRaster.getDataElements(0, startRow, width, nRows, null);
312                     iPixels = null;
313                 } else {
314                     iPixels = (int[]) wRaster.getDataElements(0, startRow, width, nRows, null);
315                     pixels = null;
316                 }
317
318                 scanPos = 0;
319                 readPos = 0;
320                 startPos = 1;
321                 for (int i = 0; i < width * nRows; i++) {
322                     if (i % width == 0) {
323                         scanLines[scanPos++] = (byte) filter;
324                         startPos = scanPos;
325                     }
326
327                     if (bytesPerPixel == 1) {
328                         scanLines[scanPos++] = pixels[readPos++];
329                     } else if (tType == DataBuffer.TYPE_BYTE) {
330                         scanLines[scanPos++] = pixels[readPos++];
331                         scanLines[scanPos++] = pixels[readPos++];
332                         scanLines[scanPos++] = pixels[readPos++];
333                         if (encodeAlpha) {
334                             scanLines[scanPos++] = pixels[readPos++];
335                         } else {
336                             readPos++;
337                         }
338                     } else {
339                         scanLines[scanPos++] = (byte) ((iPixels[readPos] >> 16) & 0xff);
340                         scanLines[scanPos++] = (byte) ((iPixels[readPos] >> 8) & 0xff);
341                         scanLines[scanPos++] = (byte) ((iPixels[readPos]) & 0xff);
342                         if (encodeAlpha) {
343                             scanLines[scanPos++] = (byte) ((iPixels[readPos] >> 24) & 0xff);
344                         }
345                         readPos++;
346                     }
347                     if ((i % width == width - 1) && (filter != FILTER_NONE)) {
348                         if (filter == FILTER_SUB) {
349                             filterSub(scanLines, startPos, width);
350                         }
351                         if (filter == FILTER_UP) {
352                             filterUp(scanLines, startPos, width);
353                         }
354                     }
355                 }
356
357                 /*
358                  * Write these lines to the output area
359                  */

360                 compBytes.write(scanLines, 0, scanPos);
361
362                 startRow += nRows;
363                 rowsLeft -= nRows;
364             }
365             compBytes.close();
366
367             /*
368              * Write the compressed bytes
369              */

370             compressedLines = outBytes.toByteArray();
371             nCompressed = compressedLines.length;
372
373             crc.reset();
374             bytePos = writeInt4(nCompressed, bytePos);
375             bytePos = writeString("IDAT", bytePos);
376             crc.update("IDAT".getBytes());
377             bytePos = writeBytes(compressedLines, nCompressed, bytePos);
378             crc.update(compressedLines, 0, nCompressed);
379
380             crcValue = crc.getValue();
381             bytePos = writeInt4((int) crcValue, bytePos);
382             scrunch.finish();
383             return true;
384         } catch (IOException JavaDoc e) {
385             System.err.println(e.toString());
386             return false;
387         }
388     }
389
390 }
391
392
Popular Tags