1 /* ImageConverter.java -- Loads images asynchronously
2    Copyright (C) 2008 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package gnu.java.awt.image;
40 
41 import gnu.java.awt.image.AsyncImage;
42 
43 import java.awt.GraphicsEnvironment;
44 import java.awt.Image;
45 import java.awt.Transparency;
46 import java.awt.image.BufferedImage;
47 import java.awt.image.ColorModel;
48 import java.awt.image.DataBuffer;
49 import java.awt.image.ImageConsumer;
50 import java.awt.image.IndexColorModel;
51 import java.awt.image.ImageObserver;
52 import java.awt.image.SinglePixelPackedSampleModel;
53 import java.awt.image.WritableRaster;
54 import java.util.Hashtable;
55 
56 /**
57  * Convert an Image to a BufferedImage.
58  *
59  * @author Roman Kennke (kennke@aicas.com)
60  */
61 public class ImageConverter implements ImageConsumer
62 {
63 
64   public static final String IMAGE_TRANSPARENCY_PROPERTY =
65     "gnu.awt.image.transparency";
66 
67   public static final String IMAGE_PROPERTIES_PROPERTY =
68     "gnu.awt.image.properties";
69 
70   private AsyncImage image;
71   private BufferedImage bImage;
72   private Hashtable imageProperties;
73   private int width, height;
74   private ColorModel colorModel;
75   private ColorModel targetColorModel;
76 
ImageConverter()77   public ImageConverter()
78   {
79     width = 0;
80     height = 0;
81     image = new AsyncImage();
82   }
83 
setDimensions(int w, int h)84   public void setDimensions(int w, int h)
85   {
86     width = w;
87     height = h;
88   }
89 
setProperties(Hashtable props)90   public void setProperties(Hashtable props)
91   {
92     // Ignore for now.
93   }
94 
setColorModel(ColorModel model)95   public void setColorModel(ColorModel model)
96   {
97     colorModel = model;
98   }
99 
setHints(int flags)100   public void setHints(int flags)
101   {
102     // Ignore for now.
103   }
104 
setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int offset, int scansize)105   public void setPixels(int x, int y, int w, int h, ColorModel model,
106                         byte[] pixels, int offset, int scansize)
107   {
108     model = setupColorModel(model);
109 
110     if (bImage == null)
111       {
112         createImage();
113       }
114 
115     Integer t = (Integer) imageProperties.get("gnu.awt.image.transparency");
116     int transparency = t.intValue();
117 
118     if(targetColorModel.equals(model))
119       {
120         transparency = transferPixels(x, y, w, h, model, pixels, offset,
121                                       scansize, transparency);
122       }
123     else if (model instanceof IndexColorModel
124              && targetColorModel.equals(ColorModel.getRGBdefault()))
125       {
126         transparency = convertIndexColorModelToSRGB(x, y, w, h,
127                                                     (IndexColorModel) model,
128                                                     pixels, offset, scansize,
129                                                     transparency);
130       }
131     else
132       {
133         transparency = convertPixels(x, y, w, h, model, pixels, offset,
134                                      scansize, transparency);
135       }
136 
137     imageProperties.put("gnu.awt.image.transparency",
138                         Integer.valueOf(transparency));
139   }
140 
setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int offset, int scansize)141   public void setPixels(int x, int y, int w, int h, ColorModel model,
142                         int[] pixels, int offset, int scansize)
143   {
144     model = setupColorModel(model);
145     if (bImage == null)
146       {
147         createImage();
148       }
149 
150     Integer t = (Integer) imageProperties.get(IMAGE_TRANSPARENCY_PROPERTY);
151     int transparency= t.intValue();
152 
153     if (targetColorModel.equals(model))
154       {
155         transparency = transferPixels(x, y, w, h, model, pixels, offset,
156                                       scansize, transparency);
157       }
158     else if (model instanceof IndexColorModel
159              && targetColorModel.equals(ColorModel.getRGBdefault()))
160       {
161         transparency = convertIndexColorModelToSRGB(x, y, w, h,
162                                                     (IndexColorModel) model,
163                                                     pixels, offset, scansize,
164                                                     transparency);
165       }
166     else
167       {
168         transparency = convertPixels(x, y, w, h, model, pixels, offset,
169                                      scansize, transparency);
170       }
171 
172     imageProperties.put(IMAGE_TRANSPARENCY_PROPERTY,
173                         Integer.valueOf(transparency));
174 
175   }
176 
177   /**
178    * Initialize the color model for this setPixels run: <br/>
179    * 1. if no color model was given use the hinted color model <br/>
180    * 2. if no color model was given and non was hinted use the default sRGB color model. <br/>
181    * Also:<br/>
182    * If no target color model was set use the color model of the given pixels.
183    * @param model
184    * @return
185    */
setupColorModel(ColorModel model)186   private ColorModel setupColorModel(ColorModel model)
187   {
188     // If the given color model is null use the previously hinted color model.
189     if (model == null)
190       model = colorModel;
191 
192     // If no color model was given or hinted use default sRGB.
193     if (model == null)
194       model = ColorModel.getRGBdefault();
195 
196     // If no specific color model was requested for the target use the current
197     // pixels model.
198     if (targetColorModel == null)
199       targetColorModel = model;
200     targetColorModel = ColorModel.getRGBdefault();
201     return model;
202   }
203 
204   /**
205    * Creates the image instance into which the pixel data is converted.
206    */
createImage()207   private void createImage()
208   {
209     if (imageProperties == null)
210       {
211         imageProperties = new Hashtable();
212       }
213 
214     imageProperties.put(IMAGE_TRANSPARENCY_PROPERTY,
215                         Integer.valueOf(Transparency.OPAQUE));
216     imageProperties.put(IMAGE_PROPERTIES_PROPERTY, imageProperties);
217 
218     // For the sRGB case let the GraphicsEnvironment create an image for us.
219     if (ColorModel.getRGBdefault().equals(targetColorModel))
220       {
221         bImage = GraphicsEnvironment.getLocalGraphicsEnvironment()
222                                     .getDefaultScreenDevice()
223                                     .getDefaultConfiguration()
224              .createCompatibleImage(width, height, Transparency.TRANSLUCENT);
225       }
226     else
227       {
228         WritableRaster raster =
229           targetColorModel.createCompatibleWritableRaster(width, height);
230         bImage = new BufferedImage(targetColorModel, raster, false,
231                                    imageProperties);
232       }
233     image.setRealImage(bImage);
234     return;
235   }
236 
237   /**
238    * Transfers pixels into a raster of the same color model.
239    *
240    * @param x the X coordinate of the source pixel rectangle
241    * @param y the Y coordinate of the source pixel rectangle
242    * @param w the width of the source pixel rectangle
243    * @param h the height of the source pixel rectangle
244    * @param model the color model of the source pixels
245    * @param pixels the pixel data
246    * @param offset the offset in the pixel array
247    * @param scansize the scanline size
248    * @param transparency the assumed transparency
249    *
250    * @return the determined transparency
251    */
transferPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int offset, int scansize, int transparency)252   private int transferPixels(int x, int y, int w, int h, ColorModel model,
253                              byte[] pixels, int offset, int scansize,
254                              int transparency)
255   {
256     // If we have the same color model, then we can simply drop
257     // the pixel value into the target raster.
258     bImage.getRaster().setDataElements(x, y, w, h, pixels);
259 
260     for (int yy = 0; yy < h; yy++)
261       {
262         for (int xx = 0; xx < w; xx++)
263           {
264             int pixel = 0xFF & pixels[yy * scansize + xx + offset];
265             int alpha = model.getAlpha(pixel);
266             transparency = updateTransparency(alpha, transparency);
267           }
268       }
269     return transparency;
270   }
271 
272   /**
273    * Transfers pixels into a raster of the same color model.
274    *
275    * @param x the X coordinate of the source pixel rectangle
276    * @param y the Y coordinate of the source pixel rectangle
277    * @param w the width of the source pixel rectangle
278    * @param h the height of the source pixel rectangle
279    * @param model the color model of the source pixels
280    * @param pixels the pixel data
281    * @param offset the offset in the pixel array
282    * @param scansize the scanline size
283    * @param transparency the assumed transparency
284    *
285    * @return the determined transparency
286    */
transferPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int offset, int scansize, int transparency)287   private int transferPixels(int x, int y, int w, int h, ColorModel model,
288                              int[] pixels, int offset, int scansize,
289                              int transparency)
290   {
291     // If we have the same color model, then we can simply drop
292     // the pixel value into the target raster.
293     bImage.getRaster().setDataElements(x, y, w, h, pixels);
294 
295     for (int yy = 0; yy < h; yy++)
296       {
297         for (int xx = 0; xx < w; xx++)
298           {
299             int pixel = pixels[yy * scansize + xx + offset];
300             int alpha = model.getAlpha(pixel);
301             transparency = updateTransparency(alpha, transparency);
302           }
303       }
304     return transparency;
305   }
306 
307   /**
308    * Converts pixel from one color model to another, and stores them in the
309    * target image.
310    *
311    * @param x the X coordinate of the source pixel rectangle
312    * @param y the Y coordinate of the source pixel rectangle
313    * @param w the width of the source pixel rectangle
314    * @param h the height of the source pixel rectangle
315    * @param model the color model of the source pixels
316    * @param pixels the pixel data
317    * @param offset the offset in the pixel array
318    * @param scansize the scanline size
319    * @param transparency the assumed transparency
320    *
321    * @return the determined transparency
322    */
convertPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int offset, int scansize, int transparency)323   private int convertPixels(int x, int y, int w, int h, ColorModel model,
324                             byte[] pixels, int offset, int scansize,
325                             int transparency)
326   {
327     // If the color models are not the same, we must convert the
328     // pixel values from one model to the other.
329     Object dataEl = null;
330     // Convert pixels to the destination color model.
331     for (int yy = 0; yy < h; yy++)
332       {
333         for (int xx = 0; xx < w; xx++)
334           {
335             int pixel = 0xFF & pixels[yy * scansize + xx + offset];
336             int rgb = model.getRGB(pixel);
337             int alpha = model.getAlpha(pixel);
338             transparency = updateTransparency(alpha, transparency);
339             dataEl = targetColorModel.getDataElements(rgb, dataEl);
340             bImage.getRaster().setDataElements(x + xx, y + yy, dataEl);
341           }
342       }
343     return transparency;
344   }
345 
346   /**
347    * Converts pixel from one color model to another, and stores them in the
348    * target image.
349    *
350    * @param x the X coordinate of the source pixel rectangle
351    * @param y the Y coordinate of the source pixel rectangle
352    * @param w the width of the source pixel rectangle
353    * @param h the height of the source pixel rectangle
354    * @param model the color model of the source pixels
355    * @param pixels the pixel data
356    * @param offset the offset in the pixel array
357    * @param scansize the scanline size
358    * @param transparency the assumed transparency
359    *
360    * @return the determined transparency
361    */
convertPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int offset, int scansize, int transparency)362   private int convertPixels(int x, int y, int w, int h, ColorModel model,
363                             int[] pixels, int offset, int scansize,
364                             int transparency)
365   {
366     // If the color models are not the same, we must convert the
367     // pixel values from one model to the other.
368     Object dataEl = null;
369     // Convert pixels to the destination color model.
370     for (int yy = 0; yy < h; yy++)
371       {
372         for (int xx = 0; xx < w; xx++)
373           {
374             int pixel = pixels[yy * scansize + xx + offset];
375             int rgb = model.getRGB(pixel);
376             int alpha = model.getAlpha(pixel);
377             transparency = updateTransparency(alpha, transparency);
378             dataEl = targetColorModel.getDataElements(rgb, dataEl);
379             bImage.getRaster().setDataElements(x + xx, y + yy, dataEl);
380           }
381       }
382     return transparency;
383   }
384 
385   /**
386    * Converts pixels from an index color model to the target image.
387    *
388    * @param x the X coordinate of the source pixel rectangle
389    * @param y the Y coordinate of the source pixel rectangle
390    * @param w the width of the source pixel rectangle
391    * @param h the height of the source pixel rectangle
392    * @param model the color model of the source pixels
393    * @param pixels the pixel data
394    * @param offset the offset in the pixel array
395    * @param scansize the scanline size
396    * @param transparency the assumed transparency
397    *
398    * @return the determined transparency
399    */
convertIndexColorModelToSRGB(int x, int y, int w, int h, IndexColorModel model, byte[] pixels, int offset, int scansize, int transparency)400   private int convertIndexColorModelToSRGB(int x, int y, int w, int h,
401                                            IndexColorModel model,
402                                            byte[] pixels, int offset,
403                                            int scansize, int transparency)
404   {
405 
406     int mapSize = model.getMapSize();
407     int[] colorMap = new int[mapSize];
408     for(int i=0; i < mapSize; i++)
409       {
410         colorMap[i] = model.getRGB(i);
411       }
412 
413     WritableRaster raster = bImage.getRaster();
414     SinglePixelPackedSampleModel sampleMode =
415       (SinglePixelPackedSampleModel) raster.getSampleModel();
416     DataBuffer dataBuffer = (DataBuffer) raster.getDataBuffer();
417 
418     int rasterOffset = sampleMode.getOffset(x,y)+dataBuffer.getOffset();
419     int rasterScanline = sampleMode.getScanlineStride();
420 
421     for (int yy = 0; yy < h; yy++)
422       {
423         int xoffset = offset;
424         for (int xx = 0; xx < w; xx++)
425           {
426             int argb  = colorMap[(pixels[xoffset++] & 0xFF)];
427             dataBuffer.setElem(rasterOffset+xx, argb);
428             int alpha = (argb >>> 24);
429             transparency = updateTransparency(alpha, transparency);
430           }
431         offset += scansize;
432         rasterOffset += rasterScanline;
433       }
434 
435     return transparency;
436   }
437 
438   /**
439    * Converts pixels from an index color model to the target image.
440    *
441    * @param x the X coordinate of the source pixel rectangle
442    * @param y the Y coordinate of the source pixel rectangle
443    * @param w the width of the source pixel rectangle
444    * @param h the height of the source pixel rectangle
445    * @param model the color model of the source pixels
446    * @param pixels the pixel data
447    * @param offset the offset in the pixel array
448    * @param scansize the scanline size
449    * @param transparency the assumed transparency
450    *
451    * @return the determined transparency
452    */
convertIndexColorModelToSRGB(int x, int y, int w, int h, IndexColorModel model, int[] pixels, int offset, int scansize, int transparency)453   private int convertIndexColorModelToSRGB(int x, int y, int w, int h,
454                                            IndexColorModel model, int[] pixels,
455                                            int offset, int scansize,
456                                            int transparency)
457   {
458     int mapSize = model.getMapSize();
459     int[] colorMap = new int[mapSize];
460     for(int i=0; i < mapSize; i++)
461       {
462         colorMap[i] = model.getRGB(i);
463       }
464 
465     WritableRaster raster = bImage.getRaster();
466     SinglePixelPackedSampleModel sampleMode =
467       (SinglePixelPackedSampleModel) raster.getSampleModel();
468     DataBuffer dataBuffer = (DataBuffer)raster.getDataBuffer();
469 
470     int rasterOffset = sampleMode.getOffset(x, y) + dataBuffer.getOffset();
471     int rasterScanline = sampleMode.getScanlineStride();
472 
473     for (int yy = 0; yy < h; yy++)
474       {
475         int xoffset = offset;
476         for (int xx = 0; xx < w; xx++)
477           {
478             int argb  = colorMap[pixels[xoffset++]];
479             dataBuffer.setElem(rasterOffset + xx, argb);
480             int alpha = (argb >>> 24);
481             transparency = updateTransparency(alpha, transparency);
482           }
483         offset += scansize;
484         rasterOffset += rasterScanline;
485       }
486 
487     return transparency;
488   }
489 
490   /**
491    * Updates the transparency information according to the alpha pixel value.
492    *
493    * @param alpha the alpha pixel value
494    * @param transparency the old transparency
495    *
496    * @return the updated transparency
497    */
updateTransparency(int alpha, int transparency)498   private int updateTransparency(int alpha, int transparency)
499   {
500     if (alpha != 0xFF)
501       {
502         if (alpha == 0x00 && transparency <= Transparency.BITMASK)
503           {
504             transparency = Transparency.BITMASK;
505           }
506         else if (transparency < Transparency.TRANSLUCENT)
507           {
508             transparency = Transparency.TRANSLUCENT;
509           }
510       }
511     return transparency;
512   }
513 
imageComplete(int status)514   public void imageComplete(int status)
515   {
516     image.notifyObservers(ImageObserver.ALLBITS, 0, 0, width, height);
517   }
518 
setTargetColorModel(ColorModel model)519   public void setTargetColorModel(ColorModel model)
520   {
521     targetColorModel = model;
522   }
523 
getImage()524   public Image getImage()
525   {
526     return image;
527   }
528 }
529