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:
52: public class PNGEncoder
53: {
54:
57: private static final int defaultChunkSize = 8192;
58:
59: private PNGHeader header;
60: private PNGPalette palette;
61: private int stride, bpp;
62: private byte[] rawData;
63: private PNGICCProfile profile;
64:
65: public PNGEncoder( BufferedImage bi ) throws PNGException
66: {
67: ColorModel c = bi.getColorModel();
68: int width = bi.getWidth();
69: int height = bi.getHeight();
70: int depth = 0;
71: int colorType;
72: boolean interlace = false;
73:
74: if( c instanceof IndexColorModel )
75: {
76: colorType = PNGHeader.INDEXED;
77: int n = ((IndexColorModel)c).getMapSize();
78: if( n <= 2 )
79: depth = 1;
80: else if( n <= 4 )
81: depth = 2;
82: else if( n <= 16 )
83: depth = 4;
84: else if( n <= 256 )
85: depth = 8;
86: else
87: throw new PNGException("Depth must be <= 8 bits for indexed color.");
88: palette = new PNGPalette( ((IndexColorModel)c) );
89: }
90: else
91: {
92: ColorSpace cs = c.getColorSpace();
93: ColorSpace grayCS = ColorSpace.getInstance( ColorSpace.CS_GRAY );
94: if( cs == grayCS || bi.getType() == BufferedImage.TYPE_BYTE_GRAY
95: || bi.getType() == BufferedImage.TYPE_USHORT_GRAY )
96: colorType = c.hasAlpha() ? PNGHeader.GRAYSCALE_WITH_ALPHA :
97: PNGHeader.GRAYSCALE;
98: else
99: colorType = c.hasAlpha() ? PNGHeader.RGB_WITH_ALPHA : PNGHeader.RGB;
100:
101: int[] bits = c.getComponentSize();
102: depth = bits[0];
103: for(int i = 1; i < bits.length; i++ )
104: if( bits[i] > depth ) depth = bits[i];
105: if( (cs != grayCS && !cs.isCS_sRGB()) && cs instanceof ICC_ColorSpace )
106: profile = new PNGICCProfile( ((ICC_ColorSpace)cs).getProfile() );
107: }
108:
109: header = new PNGHeader(width, height, depth, colorType, interlace);
110:
111: stride = header.getScanlineStride();
112: bpp = header.bytesPerPixel();
113: getRawData( bi );
114: }
115:
116:
119: public PNGHeader getHeader()
120: {
121: return header;
122: }
123:
124:
127: public PNGPalette getPalette()
128: {
129: return palette;
130: }
131:
132:
135: public PNGICCProfile getProfile()
136: {
137: return profile;
138: }
139:
140:
143: public Vector encodeImage()
144: {
145: Deflater deflater = new Deflater();
146: boolean useFilter = PNGFilter.useFilter( header );
147: byte[] lastScanline = new byte[ stride ];
148:
149: byte[] data = new byte[ rawData.length + header.getHeight() ];
150:
151: byte filterByte = PNGFilter.FILTER_NONE;
152: for( int i = 0; i < header.getHeight(); i++)
153: {
154: byte[] scanline = new byte[ stride ];
155: System.arraycopy(rawData, (i * stride), scanline, 0, stride);
156: if( useFilter && i > 0)
157: filterByte = PNGFilter.chooseFilter( scanline, lastScanline, bpp);
158:
159: byte[] filtered = PNGFilter.filterScanline( filterByte, scanline,
160: lastScanline, bpp );
161: data[i * (stride + 1)] = filterByte;
162: System.arraycopy(filtered, 0, data, 1 + (i * (stride + 1)), stride);
163:
164: lastScanline = scanline;
165: }
166:
167: deflater.setInput( data );
168: deflater.finish();
169:
170: PNGData chunk;
171: Vector chunks = new Vector();
172: do
173: {
174: chunk = new PNGData( defaultChunkSize );
175: chunk.deflateToChunk( deflater );
176: chunks.add( chunk );
177: }
178: while( chunk.chunkFull() );
179: chunk.shrink();
180: return chunks;
181: }
182:
183:
187: private void getRawData( BufferedImage bi ) throws PNGException
188: {
189: WritableRaster raster = bi.getRaster();
190: rawData = new byte[ stride * header.getHeight() ];
191: if( header.isIndexed() )
192: {
193: DataBuffer db = raster.getDataBuffer();
194: if( !( db instanceof DataBufferByte ) )
195: throw new PNGException("Unexpected DataBuffer for an IndexColorModel.");
196: byte[] data = ((DataBufferByte)db).getData();
197: for(int i = 0; i < header.getHeight(); i++ )
198: System.arraycopy( data, i * stride, rawData, i * stride, stride );
199: return;
200: }
201:
202: if( header.getDepth() == 16 )
203: {
204: DataBuffer db = raster.getDataBuffer();
205: if( !( db instanceof DataBufferUShort ) )
206: throw new PNGException("Unexpected DataBuffer for 16-bit.");
207: short[] data = ((DataBufferUShort)db).getData();
208: for(int i = 0; i < header.getHeight(); i++ )
209: for(int j = 0; j < ( stride >> 1); j++)
210: {
211: rawData[ j * 2 + i * stride ] = (byte)((data[j + i * (stride >> 1 )] & 0xFF00) >> 8);
212: rawData[ j * 2 + i * stride + 1 ] = (byte)(data[j + i * (stride >> 1 )] & 0xFF);
213: }
214: return;
215: }
216:
217: int size = ( header.getColorType() == PNGHeader.RGB_WITH_ALPHA ) ? 4 : 3;
218: int width = header.getWidth();
219: int height = header.getHeight();
220: int[] pixels = bi.getRGB( 0, 0, width, height, null, 0, width );
221:
222: for( int i = 0; i < width * height; i++ )
223: {
224: rawData[ i * size ] = (byte)((pixels[i] & 0xFF0000) >> 16);
225: rawData[ i * size + 1 ] = (byte)((pixels[i] & 0xFF00) >> 8);
226: rawData[ i * size + 2 ] = (byte)(pixels[i] & 0xFF);
227: }
228:
229: if( size == 4 )
230: for( int i = 0; i < width * height; i++ )
231: rawData[ i * size + 3 ] = (byte)((pixels[i] & 0xFF000000) >> 24);
232: }
233: }