1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 /**
 27  * @constant
 28  * @type Number
 29  */
 30 cc.TGA_OK = 0;
 31 
 32 /**
 33  * @constant
 34  * @type Number
 35  */
 36 cc.TGA_ERROR_FILE_OPEN = 1;
 37 
 38 /**
 39  * @constant
 40  * @type Number
 41  */
 42 cc.TGA_ERROR_READING_FILE = 2;
 43 
 44 /**
 45  * @constant
 46  * @type Number
 47  */
 48 cc.TGA_ERROR_INDEXED_COLOR = 3;
 49 
 50 /**
 51  * @constant
 52  * @type Number
 53  */
 54 cc.TGA_ERROR_MEMORY = 4;
 55 
 56 /**
 57  * @constant
 58  * @type Number
 59  */
 60 cc.TGA_ERROR_COMPRESSED_FILE = 5;
 61 
 62 function cc.ImageTGA(status, type, pixelDepth, width, height, imageData, flipped) {
 63     this.status = status;
 64     this.type = type;
 65     this.pixelDepth = pixelDepth;
 66     /** map width */
 67     this.width = width;
 68     /** map height */
 69     this.height = height;
 70     /** raw data */
 71     this.imageData = imageData;
 72     this.flipped = flipped;
 73 }
 74 
 75 /**
 76  * <p>cc.TileMapAtlas is a subclass of cc.AtlasNode.</p>
 77  *
 78  * <p>It knows how to render a map based of tiles.<br/>
 79  * The tiles must be in a .PNG format while the map must be a .TGA file. </p>
 80  *
 81  * <p>For more information regarding the format, please see this post: <br/>
 82  * http://www.cocos2d-iphone.org/archives/27 </p>
 83  *
 84  * <p>All features from cc.AtlasNode are valid in cc.TileMapAtlas</p>
 85  *
 86  * <p>IMPORTANT: <br/>
 87  * This class is deprecated. It is maintained for compatibility reasons only.<br/>
 88  * You SHOULD not use this class. <br/>
 89  * Instead, use the newer TMX file format: cc.TMXTiledMap </p>
 90  * @class
 91  * @extends cc.AtlasNode
 92  */
 93 cc.TileMapAtlas = cc.AtlasNode.extend(/** @lends cc.TileMapAtlas# */{
 94     _GAInfo:null,
 95     indices:null,
 96     //numbers of tiles to render
 97     _itemsToRender:0,
 98     //x,y to altas dictionary
 99     _posToAtlasIndex:null,
100 
101     /**
102      * @return {cc.ImageTGA}
103      */
104     getTGAInfo:function () {
105         return this._GAInfo;
106     },
107 
108     /**
109      * @param  {cc.ImageTGA} Var
110      */
111     setTGAInfo:function (Var) {
112         this._GAInfo = Var;
113     },
114 
115     /**
116      * Initializes a cc.TileMap with a tile file (atlas) with a map file and the width and height of each tile in points.<br />
117      * The file will be loaded using the TextureMgr.
118      * @param {String} tile
119      * @param {String} mapFile
120      * @param {Number} tileWidth
121      * @param {Number} tileHeight
122      * @return {Boolean}
123      * @example
124      * //example
125      * var tmpAtlas = new cc.TileMapAtlas();
126      * tmpAtlas.initWithTileFile("hello.png", "hello.tga", 16, 16);
127      */
128     initWithTileFile:function (tile, mapFile, tileWidth, tileHeight) {
129         this._loadTGAfile(mapFile);
130         this._calculateItemsToRender();
131         if (this.initWithTileFile(tile, tileWidth, tileHeight, this._itemsToRender)) {
132             this._color = cc.white();
133             this._posToAtlasIndex = new Object();
134             this._updateAtlasValues();
135             this.setContentSize(cc.size((this._GAInfo.width * this._itemWidth),
136                 (this._GAInfo.height * this._itemHeight)));
137             return true;
138         }
139         return false;
140     },
141 
142     /**
143      * <p>Returns a tile from position x,y.<br />
144      * For the moment only channel R is used. </p>
145      * @param {cc.Point} position
146      * @return {cc.Sprite}
147      */
148     tileAt:function (position) {
149         cc.Assert(this._GAInfo != null, "tgaInfo must not be nil");
150         cc.Assert(position.x < this._GAInfo.width, "Invalid position.x");
151         cc.Assert(position.y < this._GAInfo.height, "Invalid position.y");
152 
153         var ptr = this._GAInfo.imageData;
154         var value = ptr[position.x + position.y * this._GAInfo.width];
155 
156         return value;
157     },
158 
159     /**
160      * Sets a tile at position x,y.
161      * For the moment only channel R is used
162      * @param {cc.Sprite} tile
163      * @param {cc.Point} position
164      */
165     setTile:function (tile, position) {
166         cc.Assert(this._GAInfo != null, "tgaInfo must not be nil");
167         cc.Assert(this._posToAtlasIndex != null, "posToAtlasIndex must not be nil");
168         cc.Assert(position.x < this._GAInfo.width, "Invalid position.x");
169         cc.Assert(position.y < this._GAInfo.height, "Invalid position.x");
170         cc.Assert(tile.r != 0, "R component must be non 0");
171 
172         var ptr = this._GAInfo.imageData;
173         var value = ptr[position.x + position.y * this._GAInfo.width];
174         if (value.r == 0) {
175             cc.log("cocos2d: Value.r must be non 0.");
176         } else {
177             ptr[position.x + position.y * this._GAInfo.width] = tile;
178 
179             var num = this._posToAtlasIndex[position.x + "" + position.y];
180             this._updateAtlasValueAt(position, tile, num);
181         }
182     },
183 
184     /**
185      * Dealloc the map from memory
186      */
187     releaseMap:function () {
188         if (this._GAInfo) {
189             cc.tgaDestroy(this._GAInfo);
190         }
191         this._GAInfo = null;
192     },
193 
194     _loadTGAfile:function (file) {
195         cc.Assert(file != null, "file must be non-nil");
196 
197         //	//Find the path of the file
198         //	NSBundle *mainBndl = [cc.Director sharedDirector].loadingBundle;
199         //	cc.String *resourcePath = [mainBndl resourcePath];
200         //	cc.String * path = [resourcePath stringByAppendingPathComponent:file];
201 
202         this._GAInfo = cc.tgaLoad(cc.FileUtils.getInstance().fullPathFromRelativePath(file));
203         if (this._GAInfo.status != cc.TGA_OK) {
204             cc.Assert(0, "TileMapAtlasLoadTGA : TileMapAtas cannot load TGA file");
205         }
206     },
207 
208     _calculateItemsToRender:function () {
209         cc.Assert(this._GAInfo != null, "tgaInfo must be non-nil");
210 
211         this._itemsToRender = 0;
212         for (var x = 0; x < this._GAInfo.width; x++) {
213             for (var y = 0; y < this._GAInfo.height; y++) {
214                 var ptr = this._GAInfo.imageData;
215                 var value = ptr[x + y * this._GAInfo.width];
216                 if (value.r) {
217                     ++this._itemsToRender;
218                 }
219             }
220         }
221     },
222 
223     _updateAtlasValueAt:function (pos, value, index) {
224         var quad = new cc.V3F_C4B_T2F_Quad();
225 
226         var x = pos.x;
227         var y = pos.y;
228         var row = (value.r % this._itemsPerRow);
229         var col = (value.r / this._itemsPerRow);
230 
231         var textureWide = this._textureAtlas.getTexture().getPixelsWide();
232         var textureHigh = this._textureAtlas.getTexture().getPixelsHigh();
233 
234         var itemWidthInPixels = this._itemWidth * cc.CONTENT_SCALE_FACTOR();
235         var itemHeightInPixels = this._itemHeight * cc.CONTENT_SCALE_FACTOR();
236 
237         if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
238             var left = (2 * row * itemWidthInPixels + 1) / (2 * textureWide);
239             var right = left + (itemWidthInPixels * 2 - 2) / (2 * textureWide);
240             var top = (2 * col * itemHeightInPixels + 1) / (2 * textureHigh);
241             var bottom = top + (itemHeightInPixels * 2 - 2) / (2 * textureHigh);
242         } else {
243             var left = (row * itemWidthInPixels) / textureWide;
244             var right = left + itemWidthInPixels / textureWide;
245             var top = (col * itemHeightInPixels) / textureHigh;
246             var bottom = top + itemHeightInPixels / textureHigh;
247         }
248 
249         quad.tl.texCoords.u = left;
250         quad.tl.texCoords.v = top;
251         quad.tr.texCoords.u = right;
252         quad.tr.texCoords.v = top;
253         quad.bl.texCoords.u = left;
254         quad.bl.texCoords.v = bottom;
255         quad.br.texCoords.u = right;
256         quad.br.texCoords.v = bottom;
257 
258         quad.bl.vertices.x = (x * this._itemWidth);
259         quad.bl.vertices.y = (y * this._itemHeight);
260         quad.bl.vertices.z = 0.0;
261         quad.br.vertices.x = (x * this._itemWidth + this._itemWidth);
262         quad.br.vertices.y = (y * this._itemHeight);
263         quad.br.vertices.z = 0.0;
264         quad.tl.vertices.x = (x * this._itemWidth);
265         quad.tl.vertices.y = (y * this._itemHeight + this._itemHeight);
266         quad.tl.vertices.z = 0.0;
267         quad.tr.vertices.x = (x * this._itemWidth + this._itemWidth);
268         quad.tr.vertices.y = (y * this._itemHeight + this._itemHeight);
269         quad.tr.vertices.z = 0.0;
270 
271         var color = new cc.Color4B(this._color.r, this._color.g, this._color.b, this._opacity);
272         quad.tr.colors = color;
273         quad.tl.colors = color;
274         quad.br.colors = color;
275         quad.bl.colors = color;
276 
277         this._textureAtlas.updateQuad(quad, index);
278     },
279 
280     _updateAtlasValues:function () {
281         cc.Assert(this._GAInfo != null, "tgaInfo must be non-nil");
282 
283         var total = 0;
284 
285         for (var x = 0; x < this._GAInfo.width; x++) {
286             for (var y = 0; y < this._GAInfo.height; y++) {
287                 if (total < this._itemsToRender) {
288                     var ptr = this._GAInfo.imageData;
289                     var value = ptr[x + y * this._GAInfo.width];
290 
291                     if (value.r != 0) {
292                         this._updateAtlasValueAt(cc.g(x, y), value, total);
293                         this._posToAtlasIndex[x + "" + y] = total;
294 
295                         total++;
296                     }
297                 }
298             }
299         }
300     }
301 });
302 
303 /**
304  * <p>Creates a cc.TileMap with a tile file (atlas) with a map file and the width and height of each tile in points.<br />
305  * The tile file will be loaded using the TextureMgr. </p>
306  * @param {String} tile
307  * @param {String} mapFile
308  * @param {Number} tileWidth
309  * @param {Number} tileHeight
310  * @return {Boolean|Null}
311  * @example
312  * //example
313  * var tmpAtlas = new cc.TileMapAtlas();
314  *  tmpAtlas.initWithTileFile("hello.png", "hello.tga", 16, 16);
315  */
316 cc.TileMapAtlas.create = function (tile, mapFile, tileWidth, tileHeight) {
317     var ret = new cc.TileMapAtlas();
318     if (ret.initWithTileFile(tile, mapFile, tileWidth, tileHeight)) {
319         return ret;
320     }
321     return null;
322 };
323