View Javadoc

1   /*
2    *  MicroEmulator
3    *  Copyright (C) 2005 Andres Navarro
4    *
5    *  This library is free software; you can redistribute it and/or
6    *  modify it under the terms of the GNU Lesser General Public
7    *  License as published by the Free Software Foundation; either
8    *  version 2.1 of the License, or (at your option) any later version.
9    *
10   *  This library is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   *  Lesser General Public License for more details.
14   *
15   *  You should have received a copy of the GNU Lesser General Public
16   *  License along with this library; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package javax.microedition.lcdui.game;
21  
22  import javax.microedition.lcdui.Graphics;
23  import javax.microedition.lcdui.Image;
24  
25  /**
26   *
27   * @author Andres Navarro
28   */
29  
30  
31  // Synchronization is important because of two things:
32  // first setStaticTileSet can change the whole Object
33  // so any function could be running at the same time as a setStaticTileSet
34  // and have inconsistent behaviour
35  // second is the animated tiles, their indexes should be consecutive and 
36  // two simultaneus createAnimatedTile methods could blow that up.
37  // One would expect only one thread accessing this class at the same time
38  // but it becomes a little tricky when you consider that repaints access this
39  // class and you have no control or knowledge of the repaint thread on most
40  // systems
41  
42  public class TiledLayer extends Layer {
43      private final int rows, cols;
44      // package access for collision detection
45      Image img;
46  
47      private int tileHeight, tileWidth, numStaticTiles;
48      
49      
50      // the matrix for storing the tiles
51      private int [][]tiles;
52      
53      // the list of anmated tiles
54      // NOTE the first animatedTile (index -1) goes
55      // into the first position in the array (index 0)
56      // so to access the correct tile use animatedTiles[-n-1]
57      int []animatedTiles;
58      // the ammount of animated tiles
59      int numAnimatedTiles;
60      
61      public TiledLayer(int cols, int rows, Image img, int tileWidth, int tileHeight) {
62          // the specification doesn't states if the TiledLayer is visible on creation
63          // we assume it is
64          super(0, 0, cols * tileWidth, rows * tileHeight, true);
65          
66          if (img == null)
67              throw new NullPointerException();
68          if (cols <= 0 || rows <= 0 || tileHeight <= 0 || tileWidth <= 0)
69              throw new IllegalArgumentException();
70          if (img.getWidth() % tileWidth != 0 || img.getHeight() % tileHeight != 0)
71              throw new IllegalArgumentException();
72          
73          this.img = img;
74          this.cols = cols;
75          this.rows = rows;
76          this.tileWidth = tileWidth;
77          this.tileHeight = tileHeight;
78          this.numStaticTiles = (img.getWidth() / tileWidth) * (img.getHeight() / tileHeight);
79          this.tiles = new int[rows][cols];
80          this.animatedTiles = new int[5];
81          this.numAnimatedTiles = 0;
82      }
83      
84      // it is synchronized to avoid problems with the animatedTiles array and count
85      public int createAnimatedTile(int staticTileIndex) {
86      	synchronized (this) {
87  	        if (staticTileIndex < 0 || staticTileIndex > numStaticTiles)
88  	            throw new IndexOutOfBoundsException();
89  	        
90  	        if (numAnimatedTiles == animatedTiles.length) {
91  	            int [] temp = new int [numAnimatedTiles + 6];
92  	            System.arraycopy(animatedTiles, 0, temp, 0, numAnimatedTiles);
93  	            animatedTiles = temp;
94  	        }
95  	        
96  	        animatedTiles[numAnimatedTiles] = staticTileIndex; 
97  	        numAnimatedTiles++;
98  	        return -numAnimatedTiles;
99      	}
100     }
101     
102     public int getAnimatedTile(int index) {
103     	synchronized (this) {
104 	        index = -index-1;
105 	        if (index < 0 || index >= numAnimatedTiles)
106 	            throw new IndexOutOfBoundsException();
107 	        return animatedTiles[index];
108     	}
109     }
110     
111     public void setAnimatedTile(int index, int staticTileIndex) {
112     	synchronized (this) {
113 	        index = -index-1;
114 	        if (index < 0 || index >= numAnimatedTiles)
115 	            throw new IndexOutOfBoundsException();
116 	        if (staticTileIndex < 0 || staticTileIndex > numStaticTiles)
117 	            throw new IndexOutOfBoundsException();
118 	        
119 	        animatedTiles[index] = staticTileIndex;
120     	}
121     }
122     
123     public int getCell(int col, int row) {
124         return this.tiles[row][col];
125     }
126 
127     public void setCell(int col, int row, int index) {
128     	synchronized (this) {
129 	        if (-index-1 >= numAnimatedTiles || index > numStaticTiles)
130 	            throw new IndexOutOfBoundsException();
131 	        tiles[row][col] = index;
132     	}
133     }
134     
135     public void setStaticTileSet(Image image, int tileWidth, int tileHeight) {
136     	synchronized (this) {
137 	        if (img == null)
138 	            throw new NullPointerException();
139 	        if (tileHeight <= 0 || tileWidth <= 0)
140 	            throw new IllegalArgumentException();
141 	        if (img.getWidth() % tileWidth != 0 || img.getHeight() % tileHeight != 0)
142 	            throw new IllegalArgumentException();
143 	
144 	        int newNumStaticTiles = (img.getWidth() / getCellWidth()) * 
145 	                                    (img.getHeight() / getCellHeight());
146 	        
147 	        
148 	        // recalculate size
149 	        int w = cols * tileWidth;
150 	        int h = rows * tileHeight;
151 	        
152 	        setSize(w, h);
153 	        
154 	        this.img = img;
155 	        this.tileWidth = tileWidth;
156 	        this.tileHeight = tileHeight;
157 	        
158 	        if (newNumStaticTiles >= numStaticTiles) {
159 	            this.numStaticTiles = newNumStaticTiles;
160 	            return;
161 	        }
162 	        // if there are less static tiles
163 	        // all animated tiles are discarded and
164 	        // the tiledLayer is filled with tiles with index 0
165 	
166 	        this.numStaticTiles = newNumStaticTiles;
167 	        this.animatedTiles = new int[5];
168 	        this.numAnimatedTiles = 0;
169 	        this.fillCells(0, 0, getColumns(), getRows(), 0);
170     	}
171     }
172 
173     public void fillCells(int col, int row, int numCols, int numRows, int index) {
174     	synchronized (this) {
175 	        if (numCols < 0 || numRows < 0)
176 	            throw new IllegalArgumentException();
177 	        if (row < 0 || col < 0 || col + numCols > this.cols || row + numRows > this.rows)    
178 	            throw new IndexOutOfBoundsException();
179 	        if (-index-1 >= numAnimatedTiles || index > numStaticTiles)
180 	            throw new IndexOutOfBoundsException();
181 	        
182 	        int rMax = row + numRows;
183 	        int cMax = col + numCols;
184 	        for (int r = row; r < rMax; r++) {
185 	            for (int c = col; c < cMax; c++) {
186 	                tiles[r][c] = index; 
187 	            }
188 	        }
189     	}
190     }
191     
192     // dont need for synch here as columns are a constant
193     // after creation
194     public final int getColumns() {
195         return cols;
196     }
197     
198     // dont need for synch here as rows are a constant
199     // after creation
200     public final int getRows() {
201         return rows;
202     }
203     
204     public final int getCellWidth() {
205         return tileWidth;
206     }
207     
208     public final int getCellHeight() {
209         return tileHeight;
210     }
211     
212     public final void paint(Graphics g) {
213     	synchronized (this) {
214 	        if (!this.isVisible())
215 	            return;
216 	        
217 	        int x = getX();
218 	        int y = getY();
219 	
220 	        int c0 = 0;
221 	        int r0 = 0;
222 	        int cMax = getColumns();
223 	        int rMax = getRows();
224 	        
225 	        int tW = getCellWidth();
226 	        int tH = getCellHeight();
227 	        
228 	        int cX = g.getClipX();
229 	        int cY = g.getClipY();
230 	        int cW = g.getClipWidth();
231 	        int cH = g.getClipHeight();
232 	        
233 	        // take out the columns and rows that are outside of
234 	        // the clip area, this should speed things up a bit
235 /*	        
236 	        int diff = cX - x;
237 	        if (diff > 0)
238 	            c0 += diff / tW;
239 	        
240 	        diff = cX + cW - (x + cMax*tW);
241 	        if (diff > 0)
242 	            cMax -= diff / tW;
243 	
244 	        diff = cY - y;
245 	        if (diff > 0)
246 	            r0 += diff / tH;
247 	        
248 	        diff = cY + cH - (x + rMax*tH);
249 	        if (diff > 0)
250 	            rMax -= diff / tH;
251 */	        
252 	        int x0 = x;
253 	        int anchor = Graphics.LEFT | Graphics.TOP;
254 	        
255 	        int imgCols = img.getWidth() / tW;
256 	        int imgRows = img.getHeight() / tH;
257 	                
258 	        for (int r = r0; r < rMax; r++, y += tH) {
259 	            x = x0;
260 	            for (int c = c0; c < cMax; c++, x += tW) {
261 	                int tile = getCell(c, r);
262 	                if (tile < 0)
263 	                    tile = getAnimatedTile(tile);
264 	                if (tile == 0)
265 	                    continue;
266 	                
267 	                tile--;
268 	                
269 	                int xSrc = tW * (tile % imgCols);
270 	                int ySrc = (tile / imgCols) * tH;
271 	                
272 	                g.drawRegion(img, xSrc, ySrc, tW, tH, Sprite.TRANS_NONE, x, y, anchor);
273 	            }
274 	        }
275     	}
276     }
277 }