001         package com.croftsoft.apps.sprite;
002    
003         import java.awt.*;
004         import java.awt.image.*;
005         import java.io.*;
006         import java.util.*;
007         import javax.swing.*;
008    
009         import com.croftsoft.core.animation.*;
010         import com.croftsoft.core.animation.animator.*;
011         import com.croftsoft.core.animation.clock.*;
012         import com.croftsoft.core.animation.collector.*;
013         import com.croftsoft.core.animation.component.*;
014         import com.croftsoft.core.animation.factory.DefaultAnimationFactory;
015         import com.croftsoft.core.animation.painter.*;
016         import com.croftsoft.core.animation.sprite.*;
017         import com.croftsoft.core.animation.updater.*;
018         import com.croftsoft.core.awt.image.ImageLib;
019         import com.croftsoft.core.util.loop.FixedDelayLoopGovernor;
020         import com.croftsoft.core.util.loop.NanoTimeLoopGovernor;
021    
022    // Direct control of imageCache when sprite removed for last time?
023    
024    // MemoryCacheInputStream
025    // Java 1.4 upgrade:  Component.createVolatileImage(int,int)
026    
027    // garbage collection settings?
028    
029    // transparent background Color for fun
030    
031         /*********************************************************************
032         * Directs the AnimatedComponent.
033         *
034         * @version
035         *   $Date: 2008/04/19 21:31:00 $
036         * @since
037         *   2002-02-15
038         * @author
039         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
040         *********************************************************************/
041    
042         public final class  SpriteAnimator
043           implements Clock, ComponentAnimator, SpriteConstants
044         //////////////////////////////////////////////////////////////////////
045         //////////////////////////////////////////////////////////////////////
046         {
047    
048         private final AnimatedComponent  animatedComponent;
049    
050         private final Map                iconMap;
051    
052    // get rid of Random
053         private final Random             random;
054    
055         private final Rectangle          bounds;
056    
057         private final Rectangle          spriteBounds;
058    
059         //
060    
061         private Clock  clock;
062    
063         private long   updateTimeNanos;
064    
065         // Animators
066    
067         private TileAnimator       brickTileAnimator;
068    
069         private Sprite [ ]         headSprites;
070    
071         private Sprite [ ]         rateSprites;
072    
073         private Sprite [ ]         sprites;
074    
075         private TileAnimator       cloudTileAnimator;
076    
077         // Updaters
078    
079         private FogNightUpdater    fogNightUpdater;
080    
081         private ComponentUpdater   updateRateSampler;
082    
083         // Painters
084    
085         private ColorPainter       brickColorPainter;
086    
087         private CursorAnimator     cursorAnimator;
088    
089         private ColorPainter       fogNightColorPainter;
090    
091         private ComponentPainter   paintRateSampler;
092    
093         //
094    
095         private boolean  initialized;
096    
097         private boolean  paintBackground;
098    
099         private boolean  paintBricks;
100    
101         private boolean  paintSprites;
102    
103         private boolean  paintClouds;
104    
105         private boolean  paintFogNight;
106    
107         private boolean  onlySpriteUpdates;
108    
109         private boolean  onlySpriteRegions;
110    
111         //////////////////////////////////////////////////////////////////////
112         //////////////////////////////////////////////////////////////////////
113    
114         public  SpriteAnimator ( BufferStrategy  bufferStrategy )
115         //////////////////////////////////////////////////////////////////////
116         {
117    //javax.swing.RepaintManager.setCurrentManager ( new SpriteRepaintManager ( ) );
118    
119           iconMap      = new HashMap   ( );
120    
121           random       = new Random    ( );
122    
123           bounds       = new Rectangle ( );
124    
125           spriteBounds = new Rectangle ( );
126    
127           if ( bufferStrategy == null )
128           {
129             animatedComponent = new AnimatedComponent (
130               this,
131               new CoalescingRepaintCollector ( ),
132               new NanoTimeLoopGovernor ( DEFAULT_FRAME_RATE ) );
133           }
134           else
135           {
136             animatedComponent = new BufferStrategyAnimatedComponent (
137               this,
138               new CoalescingRepaintCollector ( ),
139               new NanoTimeLoopGovernor ( DEFAULT_FRAME_RATE ),
140               bufferStrategy );
141           }
142    
143           animatedComponent.setBackground ( Color.MAGENTA );
144    
145           animatedComponent.setForeground ( Color.GREEN );
146    
147           animatedComponent.setFont (
148             new Font ( "Times New Roman", Font.BOLD, 12 ) );
149    
150           paintBackground = true;
151    
152           paintBricks     = true;
153    
154           paintSprites    = true;
155    
156           paintClouds     = true;
157    
158           paintFogNight   = false;
159    
160           clock = new HiResClock ( );
161         }
162    
163         public void  initialize ( )
164           throws IOException
165         //////////////////////////////////////////////////////////////////////
166         {
167           IconSprite  sprite1
168             = new IconSprite ( validateIcon ( NORMAL_IMAGE_FILENAME ) );
169    
170           sprite1.setComponentUpdater (
171             new ArrayComponentUpdater (
172               new ComponentUpdater [ ] {
173                 new BounceUpdater ( sprite1, bounds, this ),
174                 new ImpactUpdater (
175                   sprite1,
176                   validateIcon ( NORMAL_IMAGE_FILENAME ),
177                   validateIcon ( IMPACT_IMAGE_FILENAME ),
178                   250000000,
179                   this ) } ) );
180    
181           sprite1.setHeading ( random.nextDouble ( ) * 2.0 * Math.PI );
182    
183           IconSprite  sprite2
184             = new IconSprite ( validateIcon ( LOOK_LEFT_IMAGE_FILENAME ) );
185    
186           sprite2.setY ( 10.0 );
187    
188           sprite2.setComponentUpdater (
189             new ArrayComponentUpdater (
190               new ComponentUpdater [ ] {
191                 new BounceUpdater ( sprite2, bounds, this ),
192                 new LeftRightUpdater (
193                   sprite2,
194                   validateIcon ( LOOK_LEFT_IMAGE_FILENAME  ),
195                   validateIcon ( LOOK_RIGHT_IMAGE_FILENAME ) ) } ) );
196    
197           TextSprite  updateRateTextSprite = new TextSprite ( "*" );
198    
199           TextSprite  paintRateTextSprite  = new TextSprite ( "*" );
200    
201           updateRateTextSprite.setColor ( Color.GREEN );
202    
203           paintRateTextSprite .setColor ( Color.BLUE  );
204    
205           updateRateTextSprite.setComponentUpdater (
206             new TextWrapUpdater ( updateRateTextSprite, -1,  1, bounds ) );
207    
208           paintRateTextSprite .setComponentUpdater (
209             new TextWrapUpdater ( paintRateTextSprite , -1, -1, bounds ) );
210    
211           updateRateSampler = new FrameRateSampler (
212             updateRateTextSprite, 3000, "Updates per second:  " );
213    
214           paintRateSampler  = new FrameRateSampler (
215             paintRateTextSprite , 3000, "Paints per second:  " );
216    
217           headSprites = new Sprite [ ] {
218             sprite1,
219             sprite2 };
220    
221           rateSprites = new Sprite [ ] {
222             paintRateTextSprite,
223             updateRateTextSprite };
224    
225           sprites = new Sprite [ ] {
226             sprite1,
227             sprite2,
228             paintRateTextSprite,
229             updateRateTextSprite };
230    
231           setSpriteVelocity ( DEFAULT_SPRITE_VELOCITY );
232    
233           // We need a background color since the background image contains
234           // some transparent pixels.
235    
236           brickColorPainter = new ColorPainter ( Color.RED, bounds );
237    
238           brickTileAnimator = new TileAnimator (
239             0, 0, validateIcon ( BACKGROUND_IMAGE_FILENAME ), null,  1, 1 );
240    
241           cloudTileAnimator = new TileAnimator (
242             0, 0, validateIcon ( CLOUD_IMAGE_FILENAME      ), null, -1, 1 );
243    
244           fogNightColorPainter = new ColorPainter ( Color.BLACK, bounds );
245    
246           fogNightUpdater = new FogNightUpdater (
247             fogNightColorPainter, brickColorPainter, 60, this );
248    
249    // show a sprite with more than one updater (multimage animation)
250    
251    // also show alpha transparency baloons
252    
253    // show orientation and mirroring
254    
255    // show animated GIFs and other animation
256    
257           cursorAnimator = new CursorAnimator (
258             validateIcon ( MOUSE_IMAGE_FILENAME ),
259             validateIcon ( MOUSE_PRESSED_IMAGE_FILENAME ),
260             ( Point ) null, // hotSpot
261             animatedComponent );
262    
263           initialized = true;
264         }
265    
266         //////////////////////////////////////////////////////////////////////
267         // accessor methods
268         //////////////////////////////////////////////////////////////////////
269    
270         public AnimatedComponent  getAnimatedComponent ( )
271         //////////////////////////////////////////////////////////////////////
272         {
273           return animatedComponent;
274         }
275    
276         //////////////////////////////////////////////////////////////////////
277         // mutator methods
278         //////////////////////////////////////////////////////////////////////
279    
280         public void  setSpriteVelocity ( double  spriteVelocity )
281         //////////////////////////////////////////////////////////////////////
282         {
283           for ( int  i = 0; i < sprites.length; i++ )
284           {
285             sprites [ i ].setVelocity ( spriteVelocity );
286           }
287         }
288    
289         public void  setPaintBackground ( boolean  paintBackground )
290         //////////////////////////////////////////////////////////////////////
291         {
292           this.paintBackground = paintBackground;
293         }
294    
295         public void  setPaintBricks ( boolean  paintBricks )
296         //////////////////////////////////////////////////////////////////////
297         {
298           this.paintBricks = paintBricks;
299         }
300    
301         public void  setPaintSprites ( boolean  paintSprites )
302         //////////////////////////////////////////////////////////////////////
303         {
304           this.paintSprites = paintSprites;
305         }
306    
307         public void  setPaintClouds ( boolean  paintClouds )
308         //////////////////////////////////////////////////////////////////////
309         {
310           this.paintClouds = paintClouds;
311         }
312    
313         public void  setPaintFogNight ( boolean  paintFogNight )
314         //////////////////////////////////////////////////////////////////////
315         {
316           this.paintFogNight = paintFogNight;
317         }
318    
319         public void  setOnlySpriteUpdates ( boolean  onlySpriteUpdates )
320         //////////////////////////////////////////////////////////////////////
321         {
322           this.onlySpriteUpdates = onlySpriteUpdates;
323         }
324    
325         public void  setOnlySpriteRegions ( boolean  onlySpriteRegions )
326         //////////////////////////////////////////////////////////////////////
327         {
328           this.onlySpriteRegions = onlySpriteRegions;
329         }
330    
331         public void  setUseSystemClock ( boolean  useSystemClock )
332         //////////////////////////////////////////////////////////////////////
333         {
334           if ( useSystemClock )
335           {
336             clock = SystemClock.INSTANCE;
337           }
338           else
339           {
340             clock = new HiResClock ( );
341           }
342         }
343    
344         public void  setLoopGovernor (
345           boolean  useFixedDelay,
346           double   frequency )
347         //////////////////////////////////////////////////////////////////////
348         {
349           if ( useFixedDelay )
350           {
351             animatedComponent.setLoopGovernor (
352               new FixedDelayLoopGovernor ( frequency ) );
353           }
354           else
355           {
356             animatedComponent.setLoopGovernor (
357               new NanoTimeLoopGovernor ( frequency ) );
358           }
359         }
360    
361         public void  setUseSwingRepaintCollector (
362           boolean  useSwingRepaintCollector )
363         //////////////////////////////////////////////////////////////////////
364         {
365           if ( useSwingRepaintCollector )
366           {
367             animatedComponent.setRepaintCollector (
368               new SwingRepaintCollector ( animatedComponent ) );
369           }
370           else
371           {
372             animatedComponent.setRepaintCollector (
373               DefaultAnimationFactory.INSTANCE.createRepaintCollector ( ) );
374           }
375         }
376    
377         public void  setUseDoubleBuffering ( boolean  useDoubleBuffering )
378         //////////////////////////////////////////////////////////////////////
379         {
380           RepaintManager.currentManager ( animatedComponent )
381             .setDoubleBufferingEnabled ( useDoubleBuffering );
382         }
383    
384         //////////////////////////////////////////////////////////////////////
385         // interface Clock method
386         //////////////////////////////////////////////////////////////////////
387    
388         public long  currentTimeNanos ( )
389         //////////////////////////////////////////////////////////////////////
390         {
391           return updateTimeNanos;
392         }
393    
394         //////////////////////////////////////////////////////////////////////
395         // interface ComponentAnimator methods
396         //////////////////////////////////////////////////////////////////////
397    
398         public void  update ( JComponent  component )
399         //////////////////////////////////////////////////////////////////////
400         {
401           if ( !initialized )
402           {
403             return;
404           }
405    
406           updateTimeNanos = clock.currentTimeNanos ( );
407    
408           // Set the bounds here to prevent them from changing during the
409           // update.
410    
411           component.getBounds ( bounds );
412    
413           if ( paintSprites )
414           {
415             for ( int  i = 0; i < headSprites.length; i++ )
416             {
417               headSprites [ i ].update ( component );
418             }
419           }
420    
421           if ( !onlySpriteUpdates )
422           {
423             RepaintCollector  oldRepaintCollector
424               = animatedComponent.setRepaintCollector (
425               NullRepaintCollector.INSTANCE );
426    
427             if ( paintBricks )
428             {
429               brickTileAnimator.update ( component );
430             }
431    
432             if ( paintClouds )
433             {
434               cloudTileAnimator.update ( component );
435             }
436    
437             if ( paintFogNight )
438             {
439               fogNightUpdater  .update ( component );
440             }
441    
442             animatedComponent.setRepaintCollector ( oldRepaintCollector );
443           }
444    
445           cursorAnimator.update ( component );
446    
447           for ( int  i = 0; i < rateSprites.length; i++ )
448           {
449             rateSprites [ i ].update ( component );
450           }
451    
452           updateRateSampler.update ( component );
453    
454           if ( !onlySpriteRegions )
455           {
456             component.repaint ( );
457           }
458         }
459    
460         public void  paint (
461           JComponent  component,
462           Graphics2D  graphics )
463         //////////////////////////////////////////////////////////////////////
464         {
465           if ( !initialized )
466           {
467             try
468             {
469               initialize ( );
470             }
471             catch ( IOException  ex )
472             {
473               ex.printStackTrace ( );
474             }
475           }
476    
477           if ( paintBackground )
478           {
479             brickColorPainter.paint ( component, graphics );
480           }
481    
482           if ( paintBricks )
483           {
484             brickTileAnimator.paint ( component, graphics );
485           }
486    
487           if ( paintSprites )
488           {
489             for ( int  i = 0; i < headSprites.length; i++ )
490             {
491               headSprites [ i ].paint ( component, graphics );
492             }
493           }
494    
495           cursorAnimator.paint ( component, graphics );
496    
497           if ( paintClouds )
498           {
499             cloudTileAnimator.paint ( component, graphics );
500           }
501    
502           if ( paintFogNight )
503           {
504             fogNightColorPainter.paint ( component, graphics );
505           }
506    
507           paintRateSampler.paint ( component, graphics );
508    
509           for ( int  i = 0; i < rateSprites.length; i++ )
510           {
511             rateSprites [ i ].paint ( component, graphics );
512           }
513         }
514    
515         //////////////////////////////////////////////////////////////////////
516         // private methods
517         //////////////////////////////////////////////////////////////////////
518    
519         private Icon  validateIcon ( String  imageFilename )
520           throws IOException
521         //////////////////////////////////////////////////////////////////////
522         {
523           Icon  icon = ( Icon ) iconMap.get ( imageFilename );
524    
525           if ( icon == null )
526           {
527             Image  image
528               = loadAutomaticImage ( imageFilename, Transparency.BITMASK );
529    
530             if ( image == null )
531             {
532               return null;
533             }
534    
535             icon = new ImageIcon ( image );
536    
537             iconMap.put ( imageFilename, icon );
538           }
539    
540           return icon;
541         }
542    
543         private BufferedImage  loadAutomaticImage (
544           String  imageFilename,
545           int     transparency )
546           throws IOException
547         //////////////////////////////////////////////////////////////////////
548         {
549           return ImageLib.loadAutomaticImage (
550             imageFilename,
551             transparency,
552             animatedComponent,
553             getClass ( ).getClassLoader ( ),
554             null );
555         }
556    
557         //////////////////////////////////////////////////////////////////////
558         //////////////////////////////////////////////////////////////////////
559         }