1   package com.atlassian.core.util.thumbnail;
2   
3   import java.awt.Transparency;
4   import java.awt.image.BufferedImage;
5   import java.awt.image.ColorModel;
6   import java.awt.image.ImageConsumer;
7   import java.awt.image.WritableRaster;
8   import java.util.Hashtable;
9   
10  public class SimpleImageConsumer implements ImageConsumer
11  {
12      private final Object holder;
13  
14      private ColorModel colorModel;
15      private WritableRaster raster;
16      private int width;
17      private int height;
18  
19      private BufferedImage image;
20      private int[] intBuffer;
21      private volatile boolean loadComplete;
22  
23      public SimpleImageConsumer()
24      {
25          holder = new Object();
26          width = -1;
27          height = -1;
28          loadComplete = false;
29      }
30  
31      public void imageComplete(int status)
32      {
33          synchronized(holder)
34          {
35              loadComplete = true;
36              holder.notify();
37          }
38      }
39  
40      public void setColorModel(ColorModel model)
41      {
42          colorModel = model;
43          createImage();
44      }
45  
46      /**
47       * Notification of the dimensions of the source image.
48       *
49       * @param w The width of the source image
50       * @param h The height of the source image
51       */
52      public void setDimensions(int w, int h)
53      {
54          width = w;
55          height = h;
56          createImage();
57      }
58  
59      /**
60       * Notification of load hints that may be useful. Not used in this
61       * implementation.
62       *
63       * @param flags The hints
64       */
65      public void setHints(int flags)
66      {
67      }
68  
69      /**
70       * Notification of a bunch of pixel values in byte form. Used for
71       * 256 color or less images (eg GIF, greyscale etc).
72       *
73       * @param x The starting x position of the pixels
74       * @param y The starting y position of the pixels
75       * @param w The number of pixels in the width
76       * @param h The number of pixels in the height
77       * @param model The color model used with these pixel values
78       * @param offset The offset into the source array to copy from
79       * @param scansize The number of pixel values between rows
80       */
81      public void setPixels(int x,
82                            int y,
83                            int w,
84                            int h,
85                            ColorModel model,
86                            byte[] pixels,
87                            int offset,
88                            int scansize)
89      {
90          if((intBuffer == null) || (pixels.length > intBuffer.length))
91              intBuffer = new int[pixels.length];
92  
93          for(int i = pixels.length; --i >= 0 ; )
94              intBuffer[i] = (int)pixels[i] & 0xFF;
95  
96          raster.setPixels(x, y, w, h, intBuffer);
97      }
98  
99      /**
100      * Notification of a bunch of pixel values as ints. These will be
101      * full 3 or 4 component images.
102      *
103      * @param x The starting x position of the pixels
104      * @param y The starting y position of the pixels
105      * @param w The number of pixels in the width
106      * @param h The number of pixels in the height
107      * @param model The color model used with these pixel values
108      * @param offset The offset into the source array to copy from
109      * @param scansize The number of pixel values between rows
110      */
111     public void setPixels(int x,
112                           int y,
113                           int w,
114                           int h,
115                           ColorModel model,
116                           int[] pixels,
117                           int offset,
118                           int scansize)
119     {
120         image.setRGB(x, y, w, h, pixels, offset, scansize);
121     }
122 
123     /**
124      * Notification of the properties of the image to use. Not used in this implementation.
125      *
126      * @param props The map of properties for this image
127      */
128     public void setProperties(Hashtable props)
129     {
130         createImage();
131     }
132 
133     //------------------------------------------------------------------------
134     // Local methods
135     //------------------------------------------------------------------------
136 
137     /**
138      * Fetch the image. This image is not necessarily completely rendered
139      * although we do try to guarantee it.
140      *
141      * Torsten Römer: Changed to public
142      *
143      * @return The image that has been created for the current input
144      */
145     public BufferedImage getImage()
146     {
147         if(!loadComplete)
148         {
149             synchronized(holder)
150             {
151                 try
152                 {
153                     holder.wait();
154                 }
155                 catch(InterruptedException ie)
156                 {
157                 }
158             }
159         }
160 
161         return image;
162     }
163 
164     /**
165      * Convenience method used to create the output image based on the data
166      * that has been given to us so far. Will not create the image until all
167      * the necessary information is given, and once created, will not overwrite
168      * the current image.
169      *
170      * Torsten Römer: Use another constructor of BufferedImage. With the one used
171      * here the resulting jpg was extremely blueish.
172      */
173     private void createImage()
174     {
175         // meet the preconditions first.
176         if((image != null) ||
177            (width == -1) ||
178            (colorModel == null))
179             return;
180 
181         boolean hasAlpha = colorModel.hasAlpha() || colorModel.getTransparency() != Transparency.OPAQUE;
182         image = new BufferedImage(width, height, hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
183     }
184 }