/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package javax.microedition.lcdui.game;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
public class TiledLayer extends Layer {
// Animated tile array increment
private static final int ANIM_ARRAY_INCREMENT = 4;
private int rows;
private int cols;
private int[][] cells;
private int[] animArray;
private int animCount;
public TiledLayer(int cols, int rows, Image tileImage, int tileWidth, int tileHeight) {
super(tileImage, tileWidth, tileHeight);
if (cols < 1 || rows < 1) {
throw new IllegalArgumentException(
MsgRepository.TILEDLAYER_EXCEPTION_INVALID_DIMENSIONS);
}
this.cols = cols;
this.rows = rows;
cells = new int[rows][cols];
animArray = new int[ANIM_ARRAY_INCREMENT];
animCount = 0;
}
public void setStaticTileSet(Image tileImage, int tileWidth, int tileHeight) {
final int oldTileCount = tileCount;
setTileImage(tileImage, tileWidth, tileHeight);
// If the new static tile set has as many or more tiles, then the
// animated tiles and cell contents will be preserved.
// If not, the contents of the grid will be cleared (all cells will
// contain index 0) and all animated tiles will be deleted.
//
if (tileCount < oldTileCount) {
fillCells(0, 0, cols, rows, 0);
animCount = 0;
}
}
public void setCell(int col, int row, int aTileIndex) {
validateTileIndex(aTileIndex);
cells[row][col] = aTileIndex;
}
public int getCell(int col, int row) {
return cells[row][col];
}
public void fillCells(int col, int row, int numCols, int numRows, int tileIndex) {
if (numCols < 0) {
throw new IllegalArgumentException(
MsgRepository.TILEDLAYER_EXCEPTION_INVALID_NUMBER_OF_COLUMNS);
}
if (numRows < 0) {
throw new IllegalArgumentException(
MsgRepository.TILEDLAYER_EXCEPTION_INVALID_NUMBER_OF_ROWS);
}
validateTileIndex(tileIndex);
for (int i = col + numCols; --i >= col;) {
cells[row][i] = tileIndex;
}
for (int i = row + numRows; --i > row;) {
System.arraycopy(cells[row], col, cells[i], col, numCols);
}
}
public int createAnimatedTile(int tileIndex) {
if (tileIndex < 0 || tileIndex > tileCount) {
throw new IndexOutOfBoundsException(
MsgRepository.TILEDLAYER_EXCEPTION_INVALID_TILE_INDEX);
}
final int index = animCount;
checkCapacity(index + 1);
animArray[index] = tileIndex;
animCount++;
return ~index;
}
/**
* Grows the animated tile array if required.
*/
private void checkCapacity(int requiredLength) {
int length = animArray.length;
if (requiredLength > length) {
int[] array = new int[length + ANIM_ARRAY_INCREMENT];
System.arraycopy(animArray, 0, array, 0, length);
animArray = array;
}
}
public void setAnimatedTile(int animIndex, int tileIndex) {
final int index = ~animIndex;
if (index >= animCount) {
throw new IndexOutOfBoundsException(
MsgRepository.TILEDLAYER_EXCEPTION_INVALID_ANIMTILE_INDEX);
}
if (tileIndex < 0 || tileIndex > tileCount) {
throw new IndexOutOfBoundsException(
MsgRepository.TILEDLAYER_EXCEPTION_INVALID_TILE_INDEX);
}
animArray[index] = tileIndex;
}
public int getAnimatedTile(int animIndex) {
final int index = ~animIndex;
if (index >= animCount) {
throw new IndexOutOfBoundsException(
MsgRepository.TILEDLAYER_EXCEPTION_INVALID_ANIMTILE_INDEX);
}
return animArray[index];
}
public final int getCellWidth() {
return tileWidth;
}
public final int getCellHeight() {
return tileHeight;
}
public final int getColumns() {
return cols;
}
public final int getRows() {
return rows;
}
public final void paint(Graphics aGraphics) {
if (visible) {
aGraphics.translate(x, y);
int clipX1 = aGraphics.getClipX();
int clipY1 = aGraphics.getClipY();
int clipX2 = clipX1 + aGraphics.getClipWidth();
int clipY2 = clipY1 + aGraphics.getClipHeight();
final int tw = tileWidth;
final int th = tileHeight;
final int lw = getWidth(); // layer width
final int lh = getHeight(); // layer height
clipX1 = (clipX1 < 0 ? 0 : clipX1);
clipY1 = (clipY1 < 0 ? 0 : clipY1);
clipX2 = (clipX2 > lw ? lw : clipX2);
clipY2 = (clipY2 > lh ? lh : clipY2);
final int begRow = clipY1 / tileHeight;
final int begCol = clipX1 / tileWidth;
final int endRow = (clipY2 - 1) / tileHeight;
final int endCol = (clipX2 - 1) / tileWidth;
int sx = begRow * th;
int sy = begCol * tw;
int dx;
int dy;
for (int row = begRow; row <= endRow; row++) {
for (int col = begCol; col <= endCol; col++) {
final int index = validateTileIndex(cells[row][col]);
if (index >= 0) {
// Future performace improvement suggestion: accumulate
// region
//
// We have a visible tile in the clip rect, if its
// bounding rect is disjoint from our accumulated rect,
// we draw what we have accumulated so far and reset our
// accumulation rect, else we just add this tile to the
// accumulation rect.
//
// This helps with the not totally uncommon case of
// cells in the tiled layer containing adjacent tiles
// from the tile image. Accumulating the tiles turns
// multiple bitblt's into a single bitblt, saving a bit
// on setup/teardown/data transfer.
dx = col * tw;
dy = row * th;
int tr = index / tileColumns;
int tc = index % tileColumns;
sx = tc * tw;
sy = tr * th;
aGraphics.drawRegion(tileImage,
sx, sy,
tw, th,
Sprite.TRANS_NONE,
dx, dy, 0);
}
}
}
aGraphics.translate(-x, -y);
}
}
private int validateTileIndex(int tileIndex) {
if (tileIndex > tileCount) {
throw new IndexOutOfBoundsException(
MsgRepository.TILEDLAYER_EXCEPTION_INVALID_TILE_INDEX);
}
// check createAnimatedTile has been called, getAnimatedTile will throw
// exception if hasn't.
if (tileIndex < 0) {
tileIndex = getAnimatedTile(tileIndex);
}
return tileIndex - 1;
}
boolean collidesCell(int aX1, int aY1, int aX2, int aY2) {
// tiled layer rect in painter's coordinates
int tlX1 = x;
int tlY1 = y;
int tlX2 = x + getWidth();
int tlY2 = y + getHeight();
// compute intersection of TiledLayer bounds and sprite collision
// rectangle.
tlX1 = Math.max(tlX1, aX1);
tlY1 = Math.max(tlY1, aY1);
tlX2 = Math.min(tlX2, aX2);
tlY2 = Math.min(tlY2, aY2);
// return false if empty intersection.
if (!((tlX1 < tlX2) && (tlY1 < tlY2))) {
return false;
}
// transform to tiled layer coordinates
tlX1 -= x;
tlY1 -= y;
tlX2 -= x;
tlY2 -= y;
int startRow = (tlY1 / tileHeight);
int startCol = (tlX1 / tileWidth);
int endRow = (tlY2 - 1) / tileHeight;
int endCol = (tlX2 - 1) / tileWidth;
for (int row = startRow; row <= endRow; row++) {
for (int col = startCol; col <= endCol; col++) {
if (cells[row][col] != 0) {
// at least one cell is not empty
return true;
}
}
}
// all cells in overlap are empty
return false;
}
int getLayerWidth() {
return cols * tileWidth;
}
int getLayerHeight() {
return rows * tileHeight;
}
}