001         package com.croftsoft.core.animation.icon;
002    
003         import java.awt.*;
004         import java.awt.image.*;
005         import java.net.URL;
006         import javax.swing.*;
007    
008         import com.croftsoft.core.awt.image.NullVolatileImage;
009         import com.croftsoft.core.lang.NullArgumentException;
010    
011         /*********************************************************************
012         * When contents lost, reloads the VolatileImage from the ClassLoader.
013         *
014         * <p>
015         * This class only works with opaque (non-transparent) images.
016         * </p>
017         *
018         * <p>
019         * Example code:
020         * </p>
021         * <pre>
022         * Icon  heroIcon = new ResourceImageIcon (
023         *   HERO_IMAGE_FILENAME,
024         *   getClass ( ).getClassLoader ( ),
025         *   new Dimension (
026         *     ( int ) scalingRatio * HERO_IMAGE_WIDTH,
027         *     ( int ) scalingRatio * HERO_IMAGE_HEIGHT ),
028         *   animatedComponent );
029         * </pre>
030         *
031         * @see
032         *   VolatileImage
033         *
034         * @version
035         *   2003-02-18
036         * @since
037         *   2003-02-18
038         * @author
039         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
040         *********************************************************************/
041    
042         public final class  ResourceImageIcon
043           implements Icon
044         //////////////////////////////////////////////////////////////////////
045         //////////////////////////////////////////////////////////////////////
046         {
047    
048         private final String       imageFilename;
049    
050         private final ClassLoader  classLoader;
051    
052         //
053    
054         private VolatileImage  volatileImage;
055    
056         private boolean        scaleImage;
057    
058         private int            width;
059    
060         private int            height;
061    
062         private boolean        resized;
063    
064         //////////////////////////////////////////////////////////////////////
065         //////////////////////////////////////////////////////////////////////
066    
067         /*********************************************************************
068         * Main constructor.
069         *
070         * @param  imageFilename
071         *
072         *   The image path and filename, usually pulled from a JAR.
073         *
074         * @param  classLoader
075         *
076         *   The ClassLoader to use to load the image as a resource file.
077         *
078         * @param  scaleDimension
079         *
080         *   If not null, used to pre-scale the VolatileImage.
081         *
082         * @param  component
083         *
084         *   If not null, prepareVolatileImage() is called during construction.
085         *********************************************************************/
086         public  ResourceImageIcon (
087           String       imageFilename,
088           ClassLoader  classLoader,
089           Dimension    scaleDimension,
090           Component    component )
091         //////////////////////////////////////////////////////////////////////
092         {
093           NullArgumentException.check ( this.imageFilename = imageFilename );
094    
095           NullArgumentException.check ( this.classLoader   = classLoader   );
096    
097           if ( scaleDimension != null )
098           {
099             setSize ( scaleDimension );
100           }
101    
102           if ( component == null )
103           {
104             volatileImage = NullVolatileImage.INSTANCE;
105           }
106           else
107           {
108             prepareVolatileImage ( component );
109           }
110         }
111    
112         public  ResourceImageIcon ( String  imageFilename )
113         //////////////////////////////////////////////////////////////////////
114         {
115           this (
116             imageFilename,
117             ResourceImageIcon.class.getClassLoader ( ),
118             ( Dimension ) null,
119             ( Component ) null );       
120         }
121    
122         //////////////////////////////////////////////////////////////////////
123         //////////////////////////////////////////////////////////////////////
124    
125         public int  getIconWidth  ( ) { return width;  }
126    
127         public int  getIconHeight ( ) { return height; }
128    
129         //////////////////////////////////////////////////////////////////////
130         //////////////////////////////////////////////////////////////////////
131    
132         /*********************************************************************
133         * If VolatileImage contents are lost, calls prepareVolatileImage().
134         *********************************************************************/
135         public void  paintIcon (
136           Component  component,
137           Graphics   graphics,
138           int        x,
139           int        y )
140         //////////////////////////////////////////////////////////////////////
141         {
142           if ( resized
143             || volatileImage.contentsLost ( ) )
144           {
145             prepareVolatileImage ( component );
146           }
147    
148           graphics.drawImage ( volatileImage, x, y, component );
149         }
150    
151         //////////////////////////////////////////////////////////////////////
152         //////////////////////////////////////////////////////////////////////
153    
154         /*********************************************************************
155         * Loads the VolatileImage.
156         *
157         * <p>
158         * Call this method to load the image after construction but before
159         * the first call to paint().  This is useful if you cannot perform
160         * this operation when the main constructor is called because the
161         * Component is not ready at the time and you do not want to wait
162         * until the first paint() because the load may stall animation.
163         * </p>
164         *
165         * <p>
166         * Example code:
167         * </p>
168         *
169         * <pre>
170         * ResourceImageIcon  heroIcon = new ResourceImageIcon (
171         *   HERO_IMAGE_FILENAME,
172         *   getClass ( ).getClassLoader ( ),
173         *   ( Dimension ) null, // no scaling
174         *   ( Component ) null );
175         *
176         * [...wait until Component is ready...]
177         *
178         * heroIcon.prepareVolatileImage ( animatedComponent );
179         * </pre>
180         *********************************************************************/
181         public void  prepareVolatileImage ( Component  component )
182         //////////////////////////////////////////////////////////////////////
183         {
184           URL  imageURL
185             = classLoader.getResource ( imageFilename );
186    
187           ImageIcon  imageIcon = new ImageIcon ( imageURL );
188    
189           Image  imageIconImage = imageIcon.getImage ( );
190    
191           if ( !scaleImage )
192           {
193             width  = imageIcon.getIconWidth  ( );
194    
195             height = imageIcon.getIconHeight ( );
196           }
197    
198           if ( volatileImage != null )
199           {
200             volatileImage.flush ( );
201           }
202    
203           volatileImage = component.createVolatileImage ( width, height );
204    
205           Graphics2D  graphics = volatileImage.createGraphics ( );
206    
207           if ( scaleImage )
208           {
209             graphics.drawImage (
210               imageIconImage, 0, 0, width, height, null );
211           }
212           else
213           {
214             graphics.drawImage ( imageIconImage, 0, 0, null );
215           }
216    
217           imageIconImage.flush ( );
218    
219           graphics.dispose ( );
220         }
221    
222         public void  setWidth ( int  width )
223         //////////////////////////////////////////////////////////////////////
224         {
225           if ( width < 1 )
226           {
227             throw new IllegalArgumentException ( "width < 1:  " + width );
228           }
229    
230           scaleImage = true;
231    
232           resized    = true;
233    
234           this.width = width;
235         }
236    
237         public void  setHeight ( int  height )
238         //////////////////////////////////////////////////////////////////////
239         {
240           if ( height < 1 )
241           {
242             throw new IllegalArgumentException ( "height < 1:  " + height );
243           }
244    
245           scaleImage = true;
246    
247           resized    = true;
248    
249           this.height = height;
250         }
251    
252         public void  setSize ( Dimension  size )
253         //////////////////////////////////////////////////////////////////////
254         {
255           setWidth  ( size.width  );
256    
257           setHeight ( size.height );
258         }
259    
260         //////////////////////////////////////////////////////////////////////
261         //////////////////////////////////////////////////////////////////////
262         }