001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.vmd.game.model;
042:
043: import java.awt.Dimension;
044: import java.awt.Graphics2D;
045: import java.awt.image.BufferedImage;
046: import java.lang.ref.SoftReference;
047: import java.net.URL;
048: import java.util.ArrayList;
049: import java.util.Collection;
050: import java.util.Collections;
051: import java.util.HashMap;
052: import java.util.HashSet;
053: import java.util.Iterator;
054: import java.util.List;
055: import java.util.Map;
056: import java.util.Set;
057: import javax.swing.event.EventListenerList;
058:
059: public class ImageResource implements Identifiable {
060:
061: private long id = Identifiable.ID_UNKNOWN;
062:
063: public static final boolean DEBUG = false;
064:
065: private GlobalRepository gameDesign;
066:
067: EventListenerList listenerList = new EventListenerList();
068:
069: private URL imageURL;
070: private String relativeResourcePath;
071: private String name;
072:
073: private Map<Key, StaticTile> emptyTiles = new HashMap<Key, StaticTile>();
074: private Map<Key, StaticTile[][]> staticTileGrids = new HashMap<Key, StaticTile[][]>();
075: private Map<Integer, AnimatedTile> animatedTiles = new HashMap<Integer, AnimatedTile>();
076: private int animatedTileIndexKey = -1;
077:
078: private List<Sequence> sequences = new ArrayList();
079:
080: private static HashMap imgSoftReferences = new HashMap();
081:
082: private static BufferedImage softenImage(URL imageURL) {
083: BufferedImage img = null;
084: SoftReference softie = (SoftReference) imgSoftReferences
085: .get(imageURL);
086: if (softie != null)
087: img = (BufferedImage) (softie).get();
088: if (img == null) {
089: if (DEBUG)
090: System.out.println(">>>>> " + imageURL
091: + " NOT available - reloading"); // NOI18N
092: img = ImageUtils.loadImage(imageURL);
093: imgSoftReferences.put(imageURL, new SoftReference(img));
094: }
095: return img;
096: }
097:
098: /**
099: * Only called from GlobalRepository.getImageResource()
100: */
101: ImageResource(GlobalRepository gameDesign, URL imageURL,
102: String relativeResourcePath) {
103: assert (gameDesign != null);
104: this .gameDesign = gameDesign;
105: this .imageURL = imageURL;
106: this .relativeResourcePath = relativeResourcePath;
107: this .name = gameDesign.getNextAvailableComponentName(CodeUtils
108: .getIdealImageName(relativeResourcePath));
109: }
110:
111: public void addImageResourceListener(ImageResourceListener l) {
112: this .listenerList.add(ImageResourceListener.class, l);
113: }
114:
115: public void removeImageResourceListener(ImageResourceListener l) {
116: this .listenerList.remove(ImageResourceListener.class, l);
117: }
118:
119: public Sequence createSequence(String name, int numberFrames,
120: int frameWidth, int frameHeight, boolean zeroBasedIndex) {
121: if (!this .gameDesign.isComponentNameAvailable(name)) {
122: throw new IllegalArgumentException(
123: "Sequence cannot be created because component name '"
124: + name + "' already exists."); // NOI18N
125: }
126: Sequence sequence = new Sequence(name, this , numberFrames,
127: frameWidth, frameHeight, zeroBasedIndex);
128: this .sequences.add(sequence);
129: this .fireSequenceAdded(sequence);
130: return sequence;
131: }
132:
133: public Sequence createSequence(String name, Sequence sequence) {
134: if (!this .gameDesign.isComponentNameAvailable(name)) {
135: throw new IllegalArgumentException(
136: "Sequence cannot be created because component name '"
137: + name + "' already exists."); // NOI18N
138: }
139: Sequence newSequence = new Sequence(name, sequence);
140: this .sequences.add(newSequence);
141: this .fireSequenceAdded(newSequence);
142: return newSequence;
143: }
144:
145: public Sequence getSequenceByName(String name) {
146: for (Sequence sequence : this .sequences) {
147: if (sequence.getName().equals(name))
148: return sequence;
149: }
150: return null;
151: }
152:
153: public Sequence getSequence(long id) {
154: for (Sequence sequence : this .sequences) {
155: if (sequence.getId() == id)
156: return sequence;
157: }
158: return null;
159: }
160:
161: public void fireSequenceAdded(Sequence sequence) {
162: Object[] listeners = listenerList.getListenerList();
163: for (int i = listeners.length - 2; i >= 0; i -= 2) {
164: if (listeners[i] == ImageResourceListener.class) {
165: ((ImageResourceListener) listeners[i + 1])
166: .sequenceAdded(this , sequence);
167: }
168: }
169: }
170:
171: public void removeSequence(Sequence sequence) {
172: this .sequences.remove(sequence);
173: this .fireSequenceRemoved(sequence);
174: }
175:
176: public void fireSequenceRemoved(Sequence sequence) {
177: Object[] listeners = listenerList.getListenerList();
178: for (int i = listeners.length - 2; i >= 0; i -= 2) {
179: if (listeners[i] == ImageResourceListener.class) {
180: ((ImageResourceListener) listeners[i + 1])
181: .sequenceRemoved(this , sequence);
182: }
183: }
184: }
185:
186: public List getSequences() {
187: return Collections.unmodifiableList(sequences);
188: }
189:
190: public AnimatedTile createAnimatedTile(int index, String name,
191: int firstStaticTileIndex, int tileWidth, int tileHeight) {
192: assert (index < 0);
193: assert (this .animatedTiles.get(index) == null);
194:
195: if (!this .gameDesign.isComponentNameAvailable(name)) {
196: throw new IllegalArgumentException(
197: "AnimatedTile cannot be created because component name '"
198: + name + "' already exists."); // NOI18N
199: }
200:
201: if (this .animatedTileIndexKey >= index) {
202: this .animatedTileIndexKey = index - 1;
203: }
204: AnimatedTile animatedTile = new AnimatedTile(name, this , index,
205: tileWidth, tileHeight);
206: Sequence seq = animatedTile.getDefaultSequence();
207: seq.setFrame(new StaticTile(this , firstStaticTileIndex,
208: tileWidth, tileHeight, false), 0);
209: this .animatedTiles.put(index, animatedTile);
210: this .fireAnimatedTileAdded(animatedTile);
211: return animatedTile;
212: }
213:
214: public AnimatedTile createAnimatedTile(String name,
215: int firstStaticTileIndex, int tileWidth, int tileHeight) {
216: if (!this .gameDesign.isComponentNameAvailable(name)) {
217: throw new IllegalArgumentException(
218: "AnimatedTile cannot be created because component name '"
219: + name + "' already exists."); // NOI18N
220: }
221:
222: int index = this .animatedTileIndexKey--;
223: return this .createAnimatedTile(index, name,
224: firstStaticTileIndex, tileWidth, tileHeight);
225: }
226:
227: public AnimatedTile createAnimatedTile(int index, String name,
228: Sequence sequence) {
229: assert (index < 0);
230: assert (this .animatedTiles.get(index) == null);
231:
232: if (!this .gameDesign.isComponentNameAvailable(name)) {
233: throw new IllegalArgumentException(
234: "AnimatedTile cannot be created because component name '"
235: + name + "' already exists."); // NOI18N
236: }
237:
238: if (this .animatedTileIndexKey >= index) {
239: this .animatedTileIndexKey = index - 1;
240: }
241: AnimatedTile animatedTile = new AnimatedTile(name, this , index,
242: sequence, sequence.getFrameWidth(), sequence
243: .getFrameHeight());
244: this .animatedTiles.put(index, animatedTile);
245: this .fireAnimatedTileAdded(animatedTile);
246: return animatedTile;
247: }
248:
249: public AnimatedTile createAnimatedTile(String name,
250: Sequence sequence) {
251: if (!this .gameDesign.isComponentNameAvailable(name)) {
252: throw new IllegalArgumentException(
253: "AnimatedTile cannot be created because component name '"
254: + name + "' already exists."); // NOI18N
255: }
256: int index = this .animatedTileIndexKey--;
257: return this .createAnimatedTile(index, name, sequence);
258: }
259:
260: public void fireAnimatedTileAdded(AnimatedTile animatedTile) {
261: Object[] listeners = listenerList.getListenerList();
262: for (int i = listeners.length - 2; i >= 0; i -= 2) {
263: if (listeners[i] == ImageResourceListener.class) {
264: ((ImageResourceListener) listeners[i + 1])
265: .animatedTileAdded(this , animatedTile);
266: }
267: }
268: }
269:
270: public void removeAnimatedTile(int index) {
271: AnimatedTile removedTile = this .animatedTiles
272: .remove(new Integer(index));
273: this .fireAnimatedTileRemoved(removedTile);
274: }
275:
276: public void fireAnimatedTileRemoved(AnimatedTile animatedTile) {
277: Object[] listeners = listenerList.getListenerList();
278: for (int i = listeners.length - 2; i >= 0; i -= 2) {
279: if (listeners[i] == ImageResourceListener.class) {
280: ((ImageResourceListener) listeners[i + 1])
281: .animatedTileRemoved(this , animatedTile);
282: }
283: }
284: }
285:
286: public AnimatedTile getAnimatedTile(long id) {
287: Collection c = this .animatedTiles.values();
288: for (Iterator iter = c.iterator(); iter.hasNext();) {
289: AnimatedTile tile = (AnimatedTile) iter.next();
290: if (tile.getId() == (id))
291: return tile;
292: }
293: return null;
294: }
295:
296: public AnimatedTile getAnimatedTileByName(String name) {
297: Collection c = this .animatedTiles.values();
298: for (Iterator iter = c.iterator(); iter.hasNext();) {
299: AnimatedTile tile = (AnimatedTile) iter.next();
300: if (tile.getName().equals(name))
301: return tile;
302: }
303: return null;
304: }
305:
306: public StaticTile getEmptyTile(int cellWidth, int cellHeight) {
307: StaticTile emptyTile = this .emptyTiles.get(getGridKey(
308: cellWidth, cellHeight, false));
309: if (emptyTile == null) {
310: emptyTile = new StaticTile(this , Tile.EMPTY_TILE_INDEX,
311: cellWidth, cellHeight, false);
312: this .emptyTiles
313: .put(getGridKey(cellWidth, cellHeight, false),
314: emptyTile);
315: }
316: return emptyTile;
317: }
318:
319: private StaticTile[][] getStaticTileGrid(int tileWidth,
320: int tileHeight, boolean zeroBasedIndex) {
321: StaticTile[][] grid = this .staticTileGrids.get(getGridKey(
322: tileWidth, tileHeight, zeroBasedIndex));
323: if (grid == null) {
324: grid = this .initStaticTileGrid(tileWidth, tileHeight,
325: zeroBasedIndex);
326: }
327: return grid;
328: }
329:
330: public Set<Dimension> getTileResolutions() {
331: Set<Key> keys = this .staticTileGrids.keySet();
332: Set<Dimension> dimensions = new HashSet<Dimension>();
333: for (Key key : keys) {
334: dimensions.add(new Dimension(key.getTileWidth(), key
335: .getTileHeight()));
336: }
337: return Collections.unmodifiableSet(dimensions);
338: }
339:
340: private static Key getGridKey(int tileWidth, int tileHeight,
341: boolean zeroBasedIndex) {
342: return new Key(tileWidth, tileHeight, zeroBasedIndex);
343: }
344:
345: public int getStaticTileCount(int tileWidth, int tileHeight) {
346: return this .getRowCount(tileWidth, tileHeight)
347: * this .getColumnCount(tileWidth, tileHeight);
348: }
349:
350: public int getStaticTileIndex(int cellRow, int cellColumn,
351: int tileWidth, int tileHeight, boolean zeroBasedIndex) {
352: return this .getStaticTileAt(cellRow, cellColumn, tileWidth,
353: tileHeight, zeroBasedIndex).getIndex();
354: }
355:
356: public Tile getTile(int index, int tileWidth, int tileHeight,
357: boolean zeroBasedIndex) {
358: if (zeroBasedIndex) {
359: return new StaticTile(this , index, tileWidth, tileHeight,
360: zeroBasedIndex);
361: // int[] coordinates = this.translateIndex(index, tileWidth, tileHeight, zeroBasedIndex);
362: // return this.getStaticTileGrid(tileWidth, tileHeight, zeroBasedIndex)[coordinates[0]][coordinates[1]];
363: } else {
364: //zero is empty tile
365: if (index == Tile.EMPTY_TILE_INDEX)
366: return this .getEmptyTile(tileWidth, tileHeight);
367: //bigger than zero is a static tile
368: if (index > Tile.EMPTY_TILE_INDEX) {
369: //int[] coordinates = this.translateIndex(index, tileWidth, tileHeight, zeroBasedIndex);
370: //return this.getStaticTileGrid(tileWidth, tileHeight, zeroBasedIndex)[coordinates[0]][coordinates[1]];
371: return new StaticTile(this , index, tileWidth,
372: tileHeight, zeroBasedIndex);
373: }
374: //smaller than zero is an animated tile
375: else {
376: return (Tile) this .animatedTiles
377: .get(new Integer(index));
378: }
379: }
380: }
381:
382: public List<AnimatedTile> getAnimatedTiles() {
383: List<AnimatedTile> list = new ArrayList<AnimatedTile>(
384: this .animatedTiles.values());
385: Collections.sort(list);
386: return Collections.unmodifiableList(list);
387: }
388:
389: public List<AnimatedTile> getAnimatedTiles(int width, int height) {
390: List<AnimatedTile> list = new ArrayList<AnimatedTile>();
391: for (AnimatedTile at : this .animatedTiles.values()) {
392: if (at.getWidth() == width && at.getHeight() == height) {
393: list.add(at);
394: }
395: }
396: return list;
397: }
398:
399: public StaticTile getStaticTileAt(int row, int col, int tileWidth,
400: int tileHeight, boolean zeroBasedIndex) {
401: StaticTile[][] grid = this .getStaticTileGrid(tileWidth,
402: tileHeight, zeroBasedIndex);
403: return grid[row][col];
404: }
405:
406: /**
407: * Returns int[row, col].
408: */
409: private int[] translateIndex(int index, int tileWidth,
410: int tileHeight, boolean zeroBasedIndex) {
411: StaticTile[][] grid = this .getStaticTileGrid(tileWidth,
412: tileHeight, zeroBasedIndex);
413: int[] coordinates = new int[2];
414: coordinates[0] = (index - (zeroBasedIndex ? 0 : 1))
415: / grid[0].length;
416: coordinates[1] = (index - (zeroBasedIndex ? 0 : 1))
417: % grid[0].length;
418: return coordinates;
419: }
420:
421: private int getRowCount(int tileWidth, int tileHeight) {
422: StaticTile[][] grid = this .getStaticTileGrid(tileWidth,
423: tileHeight, true);
424: return grid.length;
425: }
426:
427: private int getColumnCount(int tileWidth, int tileHeight) {
428: StaticTile[][] grid = this .getStaticTileGrid(tileWidth,
429: tileHeight, true);
430: return grid[0].length;
431: }
432:
433: private StaticTile[][] initStaticTileGrid(int tileWidth,
434: int tileHeight, boolean zeroBasedIndex) {
435: BufferedImage img = this .softenImage(this .imageURL);
436: int rows = img.getHeight(null) / tileHeight;
437: int cols = img.getWidth(null) / tileWidth;
438:
439: StaticTile[][] staticTileGrid = new StaticTile[rows][cols];
440: int index = (zeroBasedIndex ? 0 : 1);
441: for (int i = 0; i < rows; i++) {
442: for (int j = 0; j < cols; j++) {
443: StaticTile staticTile = new StaticTile(this , index++,
444: tileWidth, tileHeight, zeroBasedIndex);
445: staticTileGrid[i][j] = staticTile;
446: }
447: }
448: this .staticTileGrids.put(getGridKey(tileWidth, tileHeight,
449: zeroBasedIndex), staticTileGrid);
450: return staticTileGrid;
451: }
452:
453: public void paint(int index, Graphics2D g, int x, int y,
454: int tileWidth, int tileHeight, boolean zeroBasedIndex) {
455: this .paint(index, g, x, y, tileWidth, tileHeight, tileWidth,
456: tileHeight, zeroBasedIndex);
457: }
458:
459: void paint(int index, Graphics2D g, int x, int y, int tileWidth,
460: int tileHeight, int scaledWidth, int scaledHeight,
461: boolean zeroBasedIndex) {
462: if (zeroBasedIndex) {
463: BufferedImage img = this .softenImage(this .imageURL);
464: int[] coordinates = this .translateIndex(index, tileWidth,
465: tileHeight, zeroBasedIndex);
466: int resX = coordinates[1] * tileWidth;
467: int resY = coordinates[0] * tileHeight;
468:
469: g.drawImage(img, x, y, x + scaledWidth, y + scaledHeight,
470: resX, resY, resX + tileWidth, resY + tileHeight,
471: null);
472: } else {
473: if (index == Tile.EMPTY_TILE_INDEX)
474: return;
475: //static tile
476: if (index > Tile.EMPTY_TILE_INDEX) {
477: BufferedImage img = this .softenImage(this .imageURL);
478: int[] coordinates = this .translateIndex(index,
479: tileWidth, tileHeight, zeroBasedIndex);
480: int resX = coordinates[1] * tileWidth;
481: int resY = coordinates[0] * tileHeight;
482:
483: g.drawImage(img, x, y, x + scaledWidth, y
484: + scaledHeight, resX, resY, resX + tileWidth,
485: resY + tileHeight, null);
486: }
487: //animated tile
488: else {
489: AnimatedTile a = (AnimatedTile) this .getTile(index,
490: tileWidth, tileHeight, zeroBasedIndex);
491: a.paint(g, x, y);
492: }
493: }
494: }
495:
496: public String getRelativeResourcePath() {
497: return this .relativeResourcePath;
498: }
499:
500: public String toString() {
501: return "ImageResource: " + this .imageURL; // NOI18N
502: }
503:
504: public GlobalRepository getGameDesign() {
505: return this .gameDesign;
506: }
507:
508: public URL getURL() {
509: return this .imageURL;
510: }
511:
512: public void setURL(URL imageURL) {
513: this .imageURL = imageURL;
514: }
515:
516: public void setName(String imgResName) {
517: this .name = imgResName;
518: }
519:
520: public String getName() {
521: return this .getName(false);
522: }
523:
524: public String getName(boolean createIfNull) {
525: if (this .name == null && createIfNull) {
526: String ideal = CodeUtils.getIdealImageName(this
527: .getRelativeResourcePath());
528: this .name = this .gameDesign
529: .getNextAvailableComponentName(ideal);
530: }
531: return this .name;
532: }
533:
534: private static class Key {
535: private int tileWidth;
536: private int tileHeight;
537: private boolean zeroBasedIndex;
538:
539: public Key(int tileWidth, int tileHeight, boolean zeroBasedIndex) {
540: this .tileWidth = tileWidth;
541: this .tileHeight = tileHeight;
542: this .zeroBasedIndex = zeroBasedIndex;
543: }
544:
545: public boolean isZeroBasedIndex() {
546: return zeroBasedIndex;
547: }
548:
549: public int getTileHeight() {
550: return tileHeight;
551: }
552:
553: public int getTileWidth() {
554: return tileWidth;
555: }
556:
557: @Override
558: public int hashCode() {
559: return this .toString().hashCode();
560: }
561:
562: @Override
563: public boolean equals(Object other) {
564: if (other == null) {
565: return false;
566: }
567: return this .toString().equals(other.toString());
568: }
569:
570: @Override
571: public String toString() {
572: return "" + tileWidth + tileHeight + zeroBasedIndex;
573: }
574:
575: }
576:
577: public long getId() {
578: return id;
579: }
580:
581: public void setId(long id) {
582: this.id = id;
583: }
584:
585: }
|