001         package com.croftsoft.apps.tile;
002    
003         import java.awt.image.BufferedImage;
004         import java.io.*;
005         import java.net.URL;
006         import java.util.Random;
007    
008         import com.croftsoft.core.awt.image.ImageLib;
009         import com.croftsoft.core.lang.NullArgumentException;
010         import com.croftsoft.core.util.ArrayLib;
011         
012         /*********************************************************************
013         * 2D tile data.
014         *
015         * @version
016         *   2003-08-11
017         * @since
018         *   2003-03-08
019         * @author
020         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
021         *********************************************************************/
022    
023         public final class  TileData
024           implements Serializable
025         //////////////////////////////////////////////////////////////////////
026         //////////////////////////////////////////////////////////////////////
027         {
028    
029         private static final long  serialVersionUID = 1L;
030    
031         //
032    
033         private final int  [ ]      palette;
034    
035         private final byte [ ] [ ]  tileMap;
036    
037         //////////////////////////////////////////////////////////////////////
038         //////////////////////////////////////////////////////////////////////
039    
040         public static void  check (
041           int  [ ]      palette,
042           byte [ ] [ ]  tileMap )
043           throws IllegalArgumentException
044         //////////////////////////////////////////////////////////////////////
045         {
046           NullArgumentException.check ( palette );
047    
048           NullArgumentException.check ( tileMap );
049    
050           if ( palette.length < 1 )
051           {
052             throw new IllegalArgumentException ( "palette length < 1" );
053           }
054    
055           if ( palette.length > 256 )
056           {
057             throw new IllegalArgumentException ( "palentte length > 256" );
058           }
059    
060           int  rows = tileMap.length;
061    
062           if ( rows < 1 )
063           {
064             throw new IllegalArgumentException ( "rows < 1" );
065           }
066    
067           int  columns = tileMap [ 0 ].length;
068    
069           if ( columns < 1 )
070           {
071             throw new IllegalArgumentException ( "columns < 1" );
072           }
073    
074           for ( int  row = 1; row < rows; row++ )
075           {
076             if ( tileMap [ row ].length != columns )
077             {
078               throw new IllegalArgumentException ( "tileMap not square" );
079             }
080           }
081         }
082    
083         public static int [ ]  generateRandomPalette (
084           Random   random,
085           int      paletteLength,
086           boolean  permitTransparent )
087         //////////////////////////////////////////////////////////////////////
088         {
089           NullArgumentException.check ( random );
090    
091           if ( paletteLength < 0 )
092           {
093             throw new IllegalArgumentException ( "paletteLength < 0" );
094           }
095    
096           if ( paletteLength > 256 )
097           {
098             throw new IllegalArgumentException ( "paletteLength > 256" );
099           }
100    
101           int [ ]  palette = new int [ paletteLength ];
102    
103           if ( permitTransparent )
104           {
105             for ( int  i = 0; i < palette.length; i++ )
106             {
107               palette [ i ] = random.nextInt ( );
108             }
109           }
110           else
111           {
112             for ( int  i = 0; i < palette.length; i++ )
113             {
114               palette [ i ] = 0xFF000000 | random.nextInt ( );
115             }
116           }
117    
118           return palette;
119         }
120    
121         public static int [ ]  generateRandomPalette (
122           Random   random,
123           int      paletteLength )
124         //////////////////////////////////////////////////////////////////////
125         {
126           return generateRandomPalette ( random, paletteLength, false );
127         }
128    
129         public static byte [ ] [ ]  generateRandomTileMap (
130           Random   random,
131           int [ ]  palette,
132           int      rows,
133           int      columns,
134           int      smoothingLoops )
135         //////////////////////////////////////////////////////////////////////
136         {
137           byte [ ] [ ]  tileMap = new byte [ rows ] [ columns ];
138    
139           for ( int  row = 0; row < rows; row++ )
140           {
141             for ( int  column = 0; column < columns; column++ )
142             {
143               int  paletteIndex = random.nextInt ( palette.length );
144    
145               tileMap [ row ] [ column ] = ( byte ) paletteIndex;
146             }
147           }
148    
149           for ( int  i = 0; i < smoothingLoops; i++ )
150           {
151             for ( int  row = 0; row < rows; row++ )
152             {
153               for ( int  column = 0; column < columns; column++ )
154               {
155                 int  left  = column > 0 ? column - 1 : columns - 1;
156    
157                 int  right = column < columns - 1 ? column + 1 : 0;
158    
159                 int  up    = row > 0 ? row - 1 : rows - 1;
160    
161                 int  down  = row < rows - 1 ? row + 1 : 0;
162    
163                 byte [ ]  neighbors = {
164                   tileMap [ row  ] [ column ],   // center
165                   tileMap [ row  ] [ left   ],   // west
166                   tileMap [ row  ] [ right  ],   // east
167                   tileMap [ up   ] [ column ],   // north
168                   tileMap [ up   ] [ left   ],   // north west
169                   tileMap [ up   ] [ right  ],   // north east
170                   tileMap [ down ] [ column ],   // south
171                   tileMap [ down ] [ left   ],   // south west
172                   tileMap [ down ] [ right  ] }; // south east
173    
174                 tileMap [ row ] [ column ]
175                   = neighbors [ random.nextInt ( neighbors.length ) ];
176               }
177             }
178           }
179    
180           return tileMap;
181         }
182    
183         public static void  remapToPalette (
184           int  [ ]      palette,
185           byte [ ] [ ]  tileMap,
186           int           defaultPaletteIndex )
187         //////////////////////////////////////////////////////////////////////
188         {
189           check ( palette, tileMap );
190    
191           for ( int  row = 0; row < tileMap.length; row++ )
192           {
193             for ( int  column = 0; column < tileMap [ 0 ].length; column++ )
194             {
195               int  paletteIndex = 0xFF & tileMap [ row ] [ column ];
196    
197               if ( ( paletteIndex < 0               )
198                 || ( paletteIndex >= palette.length ) )
199               {
200                 tileMap [ row ] [ column ] = ( byte ) defaultPaletteIndex;
201               }
202             }
203           }
204         }
205           
206         public static TileData  loadTileDataFromImage (
207           String       filename,
208           ClassLoader  classLoader )
209           throws IOException
210         //////////////////////////////////////////////////////////////////////
211         {
212           BufferedImage  bufferedImage
213             = ImageLib.loadBufferedImage ( filename, classLoader );
214    
215           int  rows    = bufferedImage.getHeight ( );
216    
217           int  columns = bufferedImage.getWidth  ( );
218    
219           int [ ]  palette = new int [ 0 ];
220    
221           byte [ ] [ ]  tileMap = new byte [ rows ] [ columns ];
222    
223           for ( int  row = 0; row < rows; row++ )
224           {
225             for ( int  column = 0; column < columns; column++ )
226             {
227               int  rgb = bufferedImage.getRGB ( column, row );
228    
229               boolean  inPalette = false;
230    
231               for ( int  i = 0; i < palette.length; i++ )
232               {
233                 if ( palette [ i ] == rgb )
234                 {
235                   tileMap [ row ] [ column ] = ( byte ) i;
236    
237                   inPalette = true;
238    
239                   break;
240                 }
241               }
242    
243               if ( !inPalette )
244               {
245                 tileMap [ row ] [ column ] = ( byte ) palette.length;
246    
247                 palette = ( int [ ] ) ArrayLib.append ( palette, rgb );
248               }         
249             }
250           }
251    
252           return new TileData ( palette, tileMap );
253         }
254    
255         //////////////////////////////////////////////////////////////////////
256         //////////////////////////////////////////////////////////////////////
257    
258         public  TileData (
259           int  [ ]      palette,
260           byte [ ] [ ]  tileMap )
261         //////////////////////////////////////////////////////////////////////
262         {
263           check ( palette, tileMap );
264    
265           this.palette = palette;
266    
267           this.tileMap = tileMap;
268         }
269    
270         //////////////////////////////////////////////////////////////////////
271         // accessor methods
272         //////////////////////////////////////////////////////////////////////
273    
274         public int  [ ]      getPalette ( ) { return palette; }
275    
276         public byte [ ] [ ]  getTileMap ( ) { return tileMap; }
277    
278         //////////////////////////////////////////////////////////////////////
279         //////////////////////////////////////////////////////////////////////
280         }