Source code: com/anotherbigidea/flash/movie/ImageUtil.java
1 /****************************************************************
2 * Copyright (c) 2001, David N. Main, All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the
6 * following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 *
17 * 3. The name of the author may not be used to endorse or
18 * promote products derived from this software without specific
19 * prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 ****************************************************************/
34 package com.anotherbigidea.flash.movie;
35
36 import java.util.*;
37 import java.io.*;
38 import java.awt.*;
39 import java.awt.image.*;
40 import com.sun.image.codec.jpeg.*;
41 import com.anotherbigidea.flash.SWFConstants;
42
43 /**
44 * Utilities for dealing with images
45 */
46 public class ImageUtil
47 {
48 /**
49 * Normalize a JPEG to ensure that it is "Baseline" rather than
50 * "Progressive". The Flash player doesn't like "Progressive".
51 *
52 * @param size null or an int[2] to receive the (width,height) of the image
53 * @return the image data - with the necessary header for SWF
54 */
55 public static byte[] normalizeJPEG( InputStream jpegImage, int[] size )
56 throws IOException
57 {
58 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder( jpegImage );
59 BufferedImage buff = decoder.decodeAsBufferedImage();
60
61 if( size != null && size.length >= 2 )
62 {
63 size[0] = decoder.getJPEGDecodeParam().getWidth();
64 size[1] = decoder.getJPEGDecodeParam().getHeight();
65 }
66
67 ByteArrayOutputStream out = new ByteArrayOutputStream();
68
69 //--write stream headers/terminators
70 out.write( 0xff );
71 out.write( 0xd9 );
72 out.write( 0xff );
73 out.write( 0xd8 );
74
75 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder( out );
76
77 encoder.encode( buff );
78 out.flush();
79
80 return out.toByteArray();
81 }
82
83 /**
84 * Create a Shape for the image. The resulting shape uses the image as
85 * a clipped image fill. The shape is a rectangle just the right size
86 * to show the entire image and its origin is at the top left.
87 *
88 * Additional geometry vectors may be added to the shape if required.
89 *
90 * @param size null or an int[2] to receive the (width,height) of the image
91 */
92 public static Shape shapeForImage( InputStream jpegImage, int[] size )
93 throws IOException
94 {
95 if( size == null || size.length < 2 ) size = new int[2];
96 byte[] data = normalizeJPEG( jpegImage, size );
97
98 int width = size[0];
99 int height = size[1];
100
101 Image img = new Image.JPEG( data );
102
103 return shapeForImage( img, (double)width, (double)height );
104 }
105
106 /**
107 * Create a Shape for the image. The resulting shape uses the image as
108 * a clipped image fill. The shape is a rectangle just the right size
109 * to show the entire image and its origin is at the top left.
110 *
111 * Additional geometry vectors may be added to the shape if required.
112 */
113 public static Shape shapeForImage( Image image, double width, double height )
114 //throws IOException
115 {
116 Shape s = new Shape();
117
118 Transform matrix = new Transform( SWFConstants.TWIPS,
119 SWFConstants.TWIPS,
120 0.0, 0.0 );
121
122 s.defineFillStyle( image, matrix, true );
123 s.setRightFillStyle(1); //use image fill
124 s.setLineStyle(0); //no line
125 s.line(width,0);
126 s.line(width,height);
127 s.line(0,height);
128 s.line(0,0);
129
130 return s;
131 }
132
133 /**
134 * Create a lossless Image object from an AWT Image - the awt image
135 * must be fully loaded and ready.
136 *
137 * @param format one of: SWFConstants.BITMAP_FORMAT_8_BIT,
138 * SWFConstants.BITMAP_FORMAT_16_BIT,
139 * SWFConstants.BITMAP_FORMAT_32_BIT
140 */
141 public static Image.Lossless createLosslessImage(
142 java.awt.Image image,
143 int format,
144 boolean hasAlpha )
145 {
146 int width = image.getWidth(null);
147 int height = image.getHeight(null);
148
149 PixelGrabber grabber = new PixelGrabber( image, 0, 0, width, height, true );
150 grabber.startGrabbing();
151
152 try{ grabber.grabPixels(); } catch( InterruptedException ie ) {}
153
154 int[] pixelData = (int[])grabber.getPixels();
155
156 int size = 0;
157 switch( format )
158 {
159 case SWFConstants.BITMAP_FORMAT_8_BIT: size = 1; break;
160 case SWFConstants.BITMAP_FORMAT_16_BIT: size = 2; break;
161 case SWFConstants.BITMAP_FORMAT_32_BIT:
162 default: size = 4; break;
163 }
164
165 int rowBytes = width * size;
166 int modulo = rowBytes % 4;
167 if( modulo > 0 ) rowBytes += 4 - modulo; //pad up to next 32 bits
168 //>>>Padding bug spotted by Joachim Sauer
169
170 byte[] imageData = new byte[ height * rowBytes ];
171
172 //--color lookup table
173 Map table = ( format == SWFConstants.BITMAP_FORMAT_32_BIT ) ? null :
174 new HashMap();
175
176 for( int y = 0; y < height; y++ )
177 {
178 int rowStart = y * width;
179 int rowByte = y * rowBytes;
180
181 for( int x = 0; x < width; x++ )
182 {
183 int pix = pixelData[ rowStart + x ];
184 int bite = rowByte + ( x * size );
185
186 if( format == SWFConstants.BITMAP_FORMAT_32_BIT )
187 {
188 imageData[ bite ] = hasAlpha ? (byte)( pix >> 24 ) : -1;
189 imageData[ bite+1 ] = (byte)((pix >> 16) & 0xff );
190 imageData[ bite+2 ] = (byte)((pix >> 8 ) & 0xff );
191 imageData[ bite+3 ] = (byte)( pix & 0xff );
192 }
193 else
194 {
195 //--find the pixel color in the lookup table
196 Integer pixI = new Integer( pix );
197 Integer index = (Integer)table.get( pixI );
198
199 if( index == null )
200 {
201 index = new Integer( table.size() );
202 table.put( pixI, index );
203 }
204
205 int idx = index.intValue();
206
207 if( format == SWFConstants.BITMAP_FORMAT_8_BIT )
208 {
209 imageData[ bite ] = (byte)idx;
210 }
211 else
212 {
213 imageData[bite ] = (byte)(( idx >> 8 )& 0xff);
214 imageData[bite+1] = (byte)( idx & 0xff );
215 }
216 }
217 }
218 }
219
220 com.anotherbigidea.flash.structs.Color[] colors =
221 ( format == SWFConstants.BITMAP_FORMAT_32_BIT ) ? null :
222 new com.anotherbigidea.flash.structs.Color[ table.size() ];
223
224 if( colors != null )
225 {
226 for( Iterator it = table.keySet().iterator(); it.hasNext(); )
227 {
228 Integer key = (Integer)it.next();
229 int argb = key.intValue();
230
231 int index = ((Integer)table.get(key)).intValue();
232
233 int alpha = (argb >> 24) & 0xff;
234 int red = (argb >> 16) & 0xff;
235 int green = (argb >> 8 ) & 0xff;
236 int blue = argb & 0xff;
237
238 com.anotherbigidea.flash.structs.Color color = hasAlpha ?
239 new com.anotherbigidea.flash.structs.AlphaColor(red,green,blue,alpha):
240 new com.anotherbigidea.flash.structs.Color(red,green,blue);
241
242 colors[index] = color;
243
244 System.out.println( "Color " + index + " = " + color );
245 }
246 }
247
248 return new Image.Lossless( colors, imageData,
249 (double)width, (double)height,
250 hasAlpha, format );
251 }
252 }