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 }