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