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 /**
 28  * <p>cc.TMXLayer represents the TMX layer. </p>
 29  *
 30  * <p>It is a subclass of cc.SpriteBatchNode. By default the tiles are rendered using a cc.TextureAtlas. <br />
 31  * If you modify a tile on runtime, then, that tile will become a cc.Sprite, otherwise no cc.Sprite objects are created. <br />
 32  * The benefits of using cc.Sprite objects as tiles are: <br />
 33  * - tiles (cc.Sprite) can be rotated/scaled/moved with a nice API </p>
 34  *
 35  * <p>If the layer contains a property named "cc.vertexz" with an integer (in can be positive or negative), <br />
 36  * then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth. </p>
 37  *
 38  * <p>On the other hand, if the "cc.vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value. <br />
 39  * Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be:  </p>
 40  *
 41  * glAlphaFunc( GL_GREATER, value ) <br />
 42  *
 43  * <p>"value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer. <br />
 44  * The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a different value, like 0.5.</p>
 45  * @class
 46  * @extends cc.SpriteBatchNode
 47  *
 48  * @property {Array}                tiles               - Tiles for layer
 49  * @property {cc.TMXTilesetInfo}    tileset             - Tileset for layer
 50  * @property {Number}               layerOrientation    - Layer orientation
 51  * @property {Array}                properties          - Properties from the layer. They can be added using tilemap editors
 52  * @property {String}               layerName           - Name of the layer
 53  * @property {Number}               layerWidth          - Width of the layer
 54  * @property {Number}               layerHeight         - Height of the layer
 55  * @property {Number}               tileWidth           - Width of a tile
 56  * @property {Number}               tileHeight          - Height of a tile
 57  */
 58 cc.TMXLayer = cc.SpriteBatchNode.extend(/** @lends cc.TMXLayer# */{
 59 	tiles: null,
 60 	tileset: null,
 61 	layerOrientation: null,
 62 	properties: null,
 63 	layerName: "",
 64 
 65     //size of the layer in tiles
 66     _layerSize: null,
 67     _mapTileSize: null,
 68     //TMX Layer supports opacity
 69     _opacity: 255,
 70     _minGID: null,
 71     _maxGID: null,
 72     //Only used when vertexZ is used
 73     _vertexZvalue: null,
 74     _useAutomaticVertexZ: null,
 75     _alphaFuncValue: null,
 76     //used for optimization
 77     _reusedTile: null,
 78     _atlasIndexArray: null,
 79     //used for retina display
 80     _contentScaleFactor: null,
 81 
 82     _cacheCanvas:null,
 83     _cacheContext:null,
 84     _cacheTexture:null,
 85     // Sub caches for avoid Chrome big image draw issue
 86     _subCacheCanvas:null,
 87     _subCacheContext:null,
 88     _subCacheCount:0,
 89     _subCacheWidth:0,
 90     // Maximum pixel number by cache, a little more than 3072*3072, real limit is 4096*4096
 91     _maxCachePixel:10000000,
 92     _className:"TMXLayer",
 93 
 94     /**
 95      * Creates a cc.TMXLayer with an tile set info, a layer info and a map info
 96      * @constructor
 97      * @param {cc.TMXTilesetInfo} tilesetInfo
 98      * @param {cc.TMXLayerInfo} layerInfo
 99      * @param {cc.TMXMapInfo} mapInfo
100      */
101     ctor:function (tilesetInfo, layerInfo, mapInfo) {
102         cc.SpriteBatchNode.prototype.ctor.call(this);
103         this._descendants = [];
104 
105         this._layerSize = cc.size(0, 0);
106         this._mapTileSize = cc.size(0, 0);
107 
108         if(cc._renderType === cc._RENDER_TYPE_CANVAS){
109             var locCanvas = cc._canvas;
110             var tmpCanvas = cc.newElement('canvas');
111             tmpCanvas.width = locCanvas.width;
112             tmpCanvas.height = locCanvas.height;
113             this._cacheCanvas = tmpCanvas;
114             this._cacheContext = this._cacheCanvas.getContext('2d');
115             var tempTexture = new cc.Texture2D();
116             tempTexture.initWithElement(tmpCanvas);
117             tempTexture.handleLoadedTexture();
118             this._cacheTexture = tempTexture;
119             this.width = locCanvas.width;
120 	        this.height = locCanvas.height;
121 	        // This class uses cache, so its default cachedParent should be himself
122 	        this._cachedParent = this;
123         }
124         if(mapInfo !== undefined)
125             this.initWithTilesetInfo(tilesetInfo, layerInfo, mapInfo);
126     },
127 
128     /**
129      * Sets the untransformed size of the TMXLayer.
130      * @override
131      * @param {cc.Size|Number} size The untransformed size of the TMXLayer or The untransformed size's width of the TMXLayer.
132      * @param {Number} [height] The untransformed size's height of the TMXLayer.
133      */
134     setContentSize:function (size, height) {
135         var locContentSize = this._contentSize;
136 	    cc.Node.prototype.setContentSize.call(this, size, height);
137 
138         if(cc._renderType === cc._RENDER_TYPE_CANVAS){
139             var locCanvas = this._cacheCanvas;
140             var scaleFactor = cc.contentScaleFactor();
141             locCanvas.width = 0 | (locContentSize.width * 1.5 * scaleFactor);
142             locCanvas.height = 0 | (locContentSize.height * 1.5 * scaleFactor);
143 
144             this._cacheContext.translate(0, locCanvas.height);
145             var locTexContentSize = this._cacheTexture._contentSize;
146             locTexContentSize.width = locCanvas.width;
147             locTexContentSize.height = locCanvas.height;
148 
149             // Init sub caches if needed
150             var totalPixel = locCanvas.width * locCanvas.height;
151             if(totalPixel > this._maxCachePixel) {
152                 if(!this._subCacheCanvas) this._subCacheCanvas = [];
153                 if(!this._subCacheContext) this._subCacheContext = [];
154 
155                 this._subCacheCount = Math.ceil( totalPixel / this._maxCachePixel );
156                 var locSubCacheCanvas = this._subCacheCanvas, i;
157                 for(i = 0; i < this._subCacheCount; i++) {
158                     if(!locSubCacheCanvas[i]) {
159                         locSubCacheCanvas[i] = document.createElement('canvas');
160                         this._subCacheContext[i] = locSubCacheCanvas[i].getContext('2d');
161                     }
162                     var tmpCanvas = locSubCacheCanvas[i];
163                     tmpCanvas.width = this._subCacheWidth = Math.round( locCanvas.width / this._subCacheCount );
164                     tmpCanvas.height = locCanvas.height;
165                 }
166                 // Clear wasted cache to release memory
167                 for(i = this._subCacheCount; i < locSubCacheCanvas.length; i++) {
168                     tmpCanvas.width = 0;
169                     tmpCanvas.height = 0;
170                 }
171             }
172             // Otherwise use count as a flag to disable sub caches
173             else this._subCacheCount = 0;
174         }
175     },
176 
177 
178     /**
179      * Return texture of cc.SpriteBatchNode
180      * @return {cc.Texture2D}
181      */
182 	getTexture: null,
183 
184     _getTextureForCanvas:function () {
185         return this._cacheTexture;
186     },
187 
188     /**
189      * don't call visit on it's children ( override visit of cc.Node )
190      * @override
191      * @param {CanvasRenderingContext2D} ctx
192      */
193     visit: null,
194 
195     _visitForCanvas: function (ctx) {
196         var context = ctx || cc._renderContext;
197         // quick return if not visible
198         if (!this._visible)
199             return;
200 
201         context.save();
202         this.transform(ctx);
203         var i, locChildren = this._children;
204 
205         if (this._cacheDirty) {
206             //
207             var eglViewer = cc.view;
208             eglViewer._setScaleXYForRenderTexture();
209             //add dirty region
210             var locCacheContext = this._cacheContext, locCacheCanvas = this._cacheCanvas;
211             locCacheContext.clearRect(0, 0, locCacheCanvas.width, -locCacheCanvas.height);
212             locCacheContext.save();
213             locCacheContext.translate(this._anchorPointInPoints.x, -(this._anchorPointInPoints.y));
214             if (locChildren) {
215                 this.sortAllChildren();
216                 for (i = 0; i < locChildren.length; i++) {
217                     if (locChildren[i])
218                         locChildren[i].visit(locCacheContext);
219                 }
220             }
221             locCacheContext.restore();
222             // Update sub caches if needed
223             if(this._subCacheCount > 0) {
224                 var subCacheW = this._subCacheWidth, subCacheH = locCacheCanvas.height;
225                 for(i = 0; i < this._subCacheCount; i++) {
226                     this._subCacheContext[i].drawImage(locCacheCanvas, i * subCacheW, 0, subCacheW, subCacheH, 0, 0, subCacheW, subCacheH);
227                 }
228             }
229 
230             //reset Scale
231             eglViewer._resetScale();
232             this._cacheDirty = false;
233         }
234         // draw RenderTexture
235         this.draw(ctx);
236         context.restore();
237     },
238 
239     /**
240      * draw cc.SpriteBatchNode (override draw of cc.Node)
241      * @param {CanvasRenderingContext2D} ctx
242      */
243     draw:null,
244 
245     _drawForCanvas:function (ctx) {
246         var context = ctx || cc._renderContext;
247         //context.globalAlpha = this._opacity / 255;
248         var posX = 0 | ( -this._anchorPointInPoints.x), posY = 0 | ( -this._anchorPointInPoints.y);
249         var eglViewer = cc.view;
250         var locCacheCanvas = this._cacheCanvas;
251         //direct draw image by canvas drawImage
252         if (locCacheCanvas) {
253             var locSubCacheCount = this._subCacheCount, locCanvasHeight = locCacheCanvas.height * eglViewer._scaleY;
254             if(locSubCacheCount > 0) {
255                 var locSubCacheCanvasArr = this._subCacheCanvas;
256                 for(var i = 0; i < locSubCacheCount; i++){
257                     var selSubCanvas = locSubCacheCanvasArr[i];
258                     context.drawImage(locSubCacheCanvasArr[i], 0, 0, selSubCanvas.width, selSubCanvas.height,
259                         posX + i * this._subCacheWidth, -(posY + locCanvasHeight), selSubCanvas.width * eglViewer._scaleX, locCanvasHeight);
260                 }
261             } else{
262                 //context.drawImage(locCacheCanvas, 0, 0, locCacheCanvas.width, locCacheCanvas.height,
263                 //    posX, -(posY + locCacheCanvas.height ), locCacheCanvas.width, locCacheCanvas.height );
264                 context.drawImage(locCacheCanvas, 0, 0, locCacheCanvas.width, locCacheCanvas.height,
265                     posX, -(posY + locCanvasHeight), locCacheCanvas.width * eglViewer._scaleX, locCanvasHeight);
266             }
267         }
268     },
269 
270     /**
271      * @return {cc.Size}
272      */
273     getLayerSize:function () {
274         return cc.size(this._layerSize.width, this._layerSize.height);
275     },
276 
277     /**
278      * @param {cc.Size} Var
279      */
280     setLayerSize:function (Var) {
281         this._layerSize.width = Var.width;
282         this._layerSize.height = Var.height;
283     },
284 
285 	_getLayerWidth: function () {
286 		return this._layerSize.width;
287 	},
288 	_setLayerWidth: function (width) {
289 		this._layerSize.width = width;
290 	},
291 	_getLayerHeight: function () {
292 		return this._layerSize.height;
293 	},
294 	_setLayerHeight: function (height) {
295 		this._layerSize.height = height;
296 	},
297 
298     /**
299      * Size of the map's tile (could be different from the tile's size)
300      * @return {cc.Size}
301      */
302     getMapTileSize:function () {
303         return cc.size(this._mapTileSize.width,this._mapTileSize.height);
304     },
305 
306     /**
307      * @param {cc.Size} Var
308      */
309     setMapTileSize:function (Var) {
310         this._mapTileSize.width = Var.width;
311         this._mapTileSize.height = Var.height;
312     },
313 
314 	_getTileWidth: function () {
315 		return this._mapTileSize.width;
316 	},
317 	_setTileWidth: function (width) {
318 		this._mapTileSize.width = width;
319 	},
320 	_getTileHeight: function () {
321 		return this._mapTileSize.height;
322 	},
323 	_setTileHeight: function (height) {
324 		this._mapTileSize.height = height;
325 	},
326 
327     /**
328      * Pointer to the map of tiles
329      * @return {Array}
330      */
331     getTiles:function () {
332         return this.tiles;
333     },
334 
335     /**
336      * @param {Array} Var
337      */
338     setTiles:function (Var) {
339         this.tiles = Var;
340     },
341 
342     /**
343      * Tile set information for the layer
344      * @return {cc.TMXTilesetInfo}
345      */
346     getTileset:function () {
347         return this.tileset;
348     },
349 
350     /**
351      * @param {cc.TMXTilesetInfo} Var
352      */
353     setTileset:function (Var) {
354         this.tileset = Var;
355     },
356 
357     /**
358      * Layer orientation, which is the same as the map orientation
359      * @return {Number}
360      */
361     getLayerOrientation:function () {
362         return this.layerOrientation;
363     },
364 
365     /**
366      * @param {Number} Var
367      */
368     setLayerOrientation:function (Var) {
369         this.layerOrientation = Var;
370     },
371 
372     /**
373      * properties from the layer. They can be added using Tiled
374      * @return {Array}
375      */
376     getProperties:function () {
377         return this.properties;
378     },
379 
380     /**
381      * @param {Array} Var
382      */
383     setProperties:function (Var) {
384         this.properties = Var;
385     },
386 
387     /**
388      * Initializes a cc.TMXLayer with a tileset info, a layer info and a map info
389      * @param {cc.TMXTilesetInfo} tilesetInfo
390      * @param {cc.TMXLayerInfo} layerInfo
391      * @param {cc.TMXMapInfo} mapInfo
392      * @return {Boolean}
393      */
394     initWithTilesetInfo:function (tilesetInfo, layerInfo, mapInfo) {
395         // XXX: is 35% a good estimate ?
396         var size = layerInfo._layerSize;
397         var totalNumberOfTiles = parseInt(size.width * size.height);
398         var capacity = totalNumberOfTiles * 0.35 + 1; // 35 percent is occupied ?
399         var texture;
400         if (tilesetInfo)
401             texture = cc.textureCache.addImage(tilesetInfo.sourceImage);
402 
403         if (this.initWithTexture(texture, capacity)) {
404             // layerInfo
405             this.layerName = layerInfo.name;
406             this._layerSize = size;
407             this.tiles = layerInfo._tiles;
408             this._minGID = layerInfo._minGID;
409             this._maxGID = layerInfo._maxGID;
410             this._opacity = layerInfo._opacity;
411             this.properties = layerInfo.properties;
412             this._contentScaleFactor = cc.director.getContentScaleFactor();
413 
414             // tilesetInfo
415             this.tileset = tilesetInfo;
416 
417             // mapInfo
418             this._mapTileSize = mapInfo.getTileSize();
419             this.layerOrientation = mapInfo.orientation;
420 
421             // offset (after layer orientation is set);
422             var offset = this._calculateLayerOffset(layerInfo.offset);
423             this.setPosition(cc.pointPixelsToPoints(offset));
424 
425             this._atlasIndexArray = [];
426             this.setContentSize(cc.sizePixelsToPoints(cc.size(this._layerSize.width * this._mapTileSize.width,
427                 this._layerSize.height * this._mapTileSize.height)));
428             this._useAutomaticVertexZ = false;
429             this._vertexZvalue = 0;
430             return true;
431         }
432         return false;
433     },
434 
435     /**
436      * <p>Dealloc the map that contains the tile position from memory. <br />
437      * Unless you want to know at runtime the tiles positions, you can safely call this method. <br />
438      * If you are going to call layer.getTileGIDAt() then, don't release the map</p>
439      */
440     releaseMap:function () {
441         if (this.tiles)
442             this.tiles = null;
443 
444         if (this._atlasIndexArray)
445             this._atlasIndexArray = null;
446     },
447 
448     /**
449      * <p>Returns the tile (cc.Sprite) at a given a tile coordinate. <br/>
450      * The returned cc.Sprite will be already added to the cc.TMXLayer. Don't add it again.<br/>
451      * The cc.Sprite can be treated like any other cc.Sprite: rotated, scaled, translated, opacity, color, etc. <br/>
452      * You can remove either by calling: <br/>
453      * - layer.removeChild(sprite, cleanup); <br/>
454      * - or layer.removeTileAt(ccp(x,y)); </p>
455      * @param {cc.Point} pos
456      * @return {cc.Sprite}
457      */
458     getTileAt: function (pos) {
459         if(!pos)
460             throw "cc.TMXLayer.getTileAt(): pos should be non-null";
461         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
462             throw "cc.TMXLayer.getTileAt(): invalid position";
463         if(!this.tiles || !this._atlasIndexArray){
464             cc.log("cc.TMXLayer.getTileAt(): TMXLayer: the tiles map has been released");
465             return null;
466         }
467 
468         var tile = null;
469         var gid = this.getTileGIDAt(pos);
470 
471         // if GID == 0, then no tile is present
472         if (gid === 0)
473             return tile;
474 
475         var z = 0 | (pos.x + pos.y * this._layerSize.width);
476         tile = this.getChildByTag(z);
477         // tile not created yet. create it
478         if (!tile) {
479             var rect = this.tileset.rectForGID(gid);
480             rect = cc.rectPixelsToPoints(rect);
481 
482             tile = new cc.Sprite();
483             tile.initWithTexture(this.texture, rect);
484             tile.batchNode = this;
485             tile.setPosition(this.getPositionAt(pos));
486             tile.vertexZ = this._vertexZForPos(pos);
487             tile.anchorX = 0;
488 	        tile.anchorY = 0;
489             tile.opacity = this._opacity;
490 
491             var indexForZ = this._atlasIndexForExistantZ(z);
492             this.addSpriteWithoutQuad(tile, indexForZ, z);
493         }
494         return tile;
495     },
496 
497     /**
498      * Returns the tile gid at a given tile coordinate. <br />
499      * if it returns 0, it means that the tile is empty. <br />
500      * This method requires the the tile map has not been previously released (eg. don't call layer.releaseMap())<br />
501      * @param {cc.Point} pos
502      * @return {Number}
503      */
504     getTileGIDAt:function (pos) {
505         if(!pos)
506             throw "cc.TMXLayer.getTileGIDAt(): pos should be non-null";
507         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
508             throw "cc.TMXLayer.getTileGIDAt(): invalid position";
509         if(!this.tiles || !this._atlasIndexArray){
510             cc.log("cc.TMXLayer.getTileGIDAt(): TMXLayer: the tiles map has been released");
511             return null;
512         }
513 
514         var idx = 0 | (pos.x + pos.y * this._layerSize.width);
515         // Bits on the far end of the 32-bit global tile ID are used for tile flags
516         var tile = this.tiles[idx];
517 
518         return (tile & cc.TMX_TILE_FLIPPED_MASK) >>> 0;
519     },
520     // XXX: deprecated
521     // tileGIDAt:getTileGIDAt,
522 
523     /**
524      *  lipped tiles can be changed dynamically
525      * @param {cc.Point} pos
526      * @return {Number}
527      */
528     getTileFlagsAt:function (pos) {
529         if(!pos)
530             throw "cc.TMXLayer.getTileFlagsAt(): pos should be non-null";
531         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
532             throw "cc.TMXLayer.getTileFlagsAt(): invalid position";
533         if(!this.tiles || !this._atlasIndexArray){
534             cc.log("cc.TMXLayer.getTileFlagsAt(): TMXLayer: the tiles map has been released");
535             return null;
536         }
537 
538         var idx = 0 | (pos.x + pos.y * this._layerSize.width);
539         // Bits on the far end of the 32-bit global tile ID are used for tile flags
540         var tile = this.tiles[idx];
541 
542         return (tile & cc.TMX_TILE_FLIPPED_ALL) >>> 0;
543     },
544     // XXX: deprecated
545     // tileFlagAt:getTileFlagsAt,
546 
547     /**
548      * <p>Sets the tile gid (gid = tile global id) at a given tile coordinate.<br />
549      * The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor . Tileset Mgr +1.<br />
550      * If a tile is already placed at that position, then it will be removed.</p>
551      * @param {Number} gid
552      * @param {cc.Point} pos
553      * @param {Number} flags
554      */
555     setTileGID:function (gid, pos, flags) {
556         if(!pos)
557             throw "cc.TMXLayer.setTileGID(): pos should be non-null";
558         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
559             throw "cc.TMXLayer.setTileGID(): invalid position";
560         if(!this.tiles || !this._atlasIndexArray){
561             cc.log("cc.TMXLayer.setTileGID(): TMXLayer: the tiles map has been released");
562             return null;
563         }
564         if(gid !== 0 && gid < this.tileset.firstGid){
565             cc.log( "cc.TMXLayer.setTileGID(): invalid gid:" + gid);
566             return null;
567         }
568 
569         flags = flags || 0;
570         this._setNodeDirtyForCache();
571 
572         var currentFlags = this.getTileFlagsAt(pos);
573         var currentGID = this.getTileGIDAt(pos);
574 
575         if (currentGID != gid || currentFlags != flags) {
576             var gidAndFlags = (gid | flags) >>> 0;
577             // setting gid=0 is equal to remove the tile
578             if (gid === 0)
579                 this.removeTileAt(pos);
580             else if (currentGID === 0)            // empty tile. create a new one
581                 this._insertTileForGID(gidAndFlags, pos);
582             else {                // modifying an existing tile with a non-empty tile
583                 var z = pos.x + pos.y * this._layerSize.width;
584                 var sprite = this.getChildByTag(z);
585                 if (sprite) {
586                     var rect = this.tileset.rectForGID(gid);
587                     rect = cc.rectPixelsToPoints(rect);
588 
589                     sprite.setTextureRect(rect, false);
590                     if (flags != null)
591                         this._setupTileSprite(sprite, pos, gidAndFlags);
592 
593                     this.tiles[z] = gidAndFlags;
594                 } else
595                     this._updateTileForGID(gidAndFlags, pos);
596             }
597         }
598     },
599 
600     /**
601      * Removes a tile at given tile coordinate
602      * @param {cc.Point} pos
603      */
604     removeTileAt:function (pos) {
605         if(!pos)
606             throw "cc.TMXLayer.removeTileAt(): pos should be non-null";
607         if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0)
608             throw "cc.TMXLayer.removeTileAt(): invalid position";
609         if(!this.tiles || !this._atlasIndexArray){
610             cc.log("cc.TMXLayer.removeTileAt(): TMXLayer: the tiles map has been released");
611             return null;
612         }
613 
614         var gid = this.getTileGIDAt(pos);
615         if (gid !== 0) {
616             if (cc._renderType === cc._RENDER_TYPE_CANVAS)
617                 this._setNodeDirtyForCache();
618             var z = 0 | (pos.x + pos.y * this._layerSize.width);
619             var atlasIndex = this._atlasIndexForExistantZ(z);
620             // remove tile from GID map
621             this.tiles[z] = 0;
622 
623             // remove tile from atlas position array
624             this._atlasIndexArray.splice(atlasIndex, 1);
625 
626             // remove it from sprites and/or texture atlas
627             var sprite = this.getChildByTag(z);
628 
629             if (sprite)
630                 cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, true);           //this.removeChild(sprite, true);
631             else {
632                 if(cc._renderType === cc._RENDER_TYPE_WEBGL)
633                     this.textureAtlas.removeQuadAtIndex(atlasIndex);
634 
635                 // update possible children
636                 if (this._children) {
637                     var locChildren = this._children;
638                     for (var i = 0, len = locChildren.length; i < len; i++) {
639                         var child = locChildren[i];
640                         if (child) {
641                             var ai = child.atlasIndex;
642                             if (ai >= atlasIndex)
643                                 child.atlasIndex = ai - 1;
644                         }
645                     }
646                 }
647             }
648         }
649     },
650 
651     /**
652      * Returns the position in pixels of a given tile coordinate
653      * @param {cc.Point} pos
654      * @return {cc.Point}
655      */
656     getPositionAt:function (pos) {
657         var ret = cc.p(0,0);
658         switch (this.layerOrientation) {
659             case cc.TMX_ORIENTATION_ORTHO:
660                 ret = this._positionForOrthoAt(pos);
661                 break;
662             case cc.TMX_ORIENTATION_ISO:
663                 ret = this._positionForIsoAt(pos);
664                 break;
665             case cc.TMX_ORIENTATION_HEX:
666                 ret = this._positionForHexAt(pos);
667                 break;
668         }
669         return cc.pointPixelsToPoints(ret);
670     },
671     // XXX: Deprecated. For backward compatibility only
672     // positionAt:getPositionAt,
673 
674     /**
675      * Return the value for the specific property name
676      * @param {String} propertyName
677      * @return {*}
678      */
679     getProperty:function (propertyName) {
680         return this.properties[propertyName];
681     },
682 
683     /**
684      * Creates the tiles
685      */
686     setupTiles:function () {
687         // Optimization: quick hack that sets the image size on the tileset
688         if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
689             this.tileset.imageSize = this._originalTexture.getContentSizeInPixels();
690         } else {
691             this.tileset.imageSize = this.textureAtlas.texture.getContentSizeInPixels();
692 
693             // By default all the tiles are aliased
694             // pros:
695             //  - easier to render
696             // cons:
697             //  - difficult to scale / rotate / etc.
698             this.textureAtlas.texture.setAliasTexParameters();
699         }
700 
701         // Parse cocos2d properties
702         this._parseInternalProperties();
703         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
704             this._setNodeDirtyForCache();
705 
706         var locLayerHeight = this._layerSize.height, locLayerWidth = this._layerSize.width;
707         for (var y = 0; y < locLayerHeight; y++) {
708             for (var x = 0; x < locLayerWidth; x++) {
709                 var pos = x + locLayerWidth * y;
710                 var gid = this.tiles[pos];
711 
712                 // XXX: gid == 0 -. empty tile
713                 if (gid !== 0) {
714                     this._appendTileForGID(gid, cc.p(x, y));
715                     // Optimization: update min and max GID rendered by the layer
716                     this._minGID = Math.min(gid, this._minGID);
717                     this._maxGID = Math.max(gid, this._maxGID);
718                 }
719             }
720         }
721 
722         if (!((this._maxGID >= this.tileset.firstGid) && (this._minGID >= this.tileset.firstGid))) {
723             cc.log("cocos2d:TMX: Only 1 tileset per layer is supported");
724         }
725     },
726 
727     /**
728      * cc.TMXLayer doesn't support adding a cc.Sprite manually.
729      * @warning addChild(child); is not supported on cc.TMXLayer. Instead of setTileGID.
730      * @param {cc.Node} child
731      * @param {number} zOrder
732      * @param {number} tag
733      */
734     addChild:function (child, zOrder, tag) {
735         cc.log("addChild: is not supported on cc.TMXLayer. Instead use setTileGID or tileAt.");
736     },
737 
738     /**
739      * Remove child
740      * @param  {cc.Sprite} sprite
741      * @param  {Boolean} cleanup
742      */
743     removeChild:function (sprite, cleanup) {
744         // allows removing nil objects
745         if (!sprite)
746             return;
747 
748         if(this._children.indexOf(sprite) === -1){
749             cc.log("cc.TMXLayer.removeChild(): Tile does not belong to TMXLayer");
750             return;
751         }
752 
753         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
754             this._setNodeDirtyForCache();
755         var atlasIndex = sprite.atlasIndex;
756         var zz = this._atlasIndexArray[atlasIndex];
757         this.tiles[zz] = 0;
758         this._atlasIndexArray.splice(atlasIndex, 1);
759         cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, cleanup);
760     },
761 
762     /**
763      * @return {String}
764      */
765     getLayerName:function () {
766         return this.layerName;
767     },
768 
769     /**
770      * @param {String} layerName
771      */
772     setLayerName:function (layerName) {
773         this.layerName = layerName;
774     },
775 
776     _positionForIsoAt:function (pos) {
777         return cc.p(this._mapTileSize.width / 2 * ( this._layerSize.width + pos.x - pos.y - 1),
778             this._mapTileSize.height / 2 * (( this._layerSize.height * 2 - pos.x - pos.y) - 2));
779     },
780 
781     _positionForOrthoAt:function (pos) {
782         return cc.p(pos.x * this._mapTileSize.width,
783             (this._layerSize.height - pos.y - 1) * this._mapTileSize.height);
784     },
785 
786     _positionForHexAt:function (pos) {
787         var diffY = (pos.x % 2 == 1) ? (-this._mapTileSize.height / 2) : 0;
788         return cc.p(pos.x * this._mapTileSize.width * 3 / 4,
789             (this._layerSize.height - pos.y - 1) * this._mapTileSize.height + diffY);
790     },
791 
792     _calculateLayerOffset:function (pos) {
793         var ret = cc.p(0,0);
794         switch (this.layerOrientation) {
795             case cc.TMX_ORIENTATION_ORTHO:
796                 ret = cc.p(pos.x * this._mapTileSize.width, -pos.y * this._mapTileSize.height);
797                 break;
798             case cc.TMX_ORIENTATION_ISO:
799                 ret = cc.p((this._mapTileSize.width / 2) * (pos.x - pos.y),
800                     (this._mapTileSize.height / 2 ) * (-pos.x - pos.y));
801                 break;
802             case cc.TMX_ORIENTATION_HEX:
803                 if(pos.x !== 0 || pos.y !== 0)
804                     cc.log("offset for hexagonal map not implemented yet");
805                 break;
806         }
807         return ret;
808     },
809 
810     _appendTileForGID:function (gid, pos) {
811         var rect = this.tileset.rectForGID(gid);
812         rect = cc.rectPixelsToPoints(rect);
813 
814         var z = 0 | (pos.x + pos.y * this._layerSize.width);
815         var tile = this._reusedTileWithRect(rect);
816         this._setupTileSprite(tile, pos, gid);
817 
818         // optimization:
819         // The difference between appendTileForGID and insertTileforGID is that append is faster, since
820         // it appends the tile at the end of the texture atlas
821         var indexForZ = this._atlasIndexArray.length;
822 
823         // don't add it using the "standard" way.
824         this.insertQuadFromSprite(tile, indexForZ);
825 
826         // append should be after addQuadFromSprite since it modifies the quantity values
827         this._atlasIndexArray.splice(indexForZ, 0, z);
828         return tile;
829     },
830 
831     _insertTileForGID:function (gid, pos) {
832         var rect = this.tileset.rectForGID(gid);
833         rect = cc.rectPixelsToPoints(rect);
834 
835         var z = 0 | (pos.x + pos.y * this._layerSize.width);
836         var tile = this._reusedTileWithRect(rect);
837         this._setupTileSprite(tile, pos, gid);
838 
839         // get atlas index
840         var indexForZ = this._atlasIndexForNewZ(z);
841 
842         // Optimization: add the quad without adding a child
843         this.insertQuadFromSprite(tile, indexForZ);
844 
845         // insert it into the local atlasindex array
846         this._atlasIndexArray.splice(indexForZ, 0, z);
847         // update possible children
848         if (this._children) {
849             var locChildren = this._children;
850             for (var i = 0, len = locChildren.length; i < len; i++) {
851                 var child = locChildren[i];
852                 if (child) {
853                     var ai = child.atlasIndex;
854                     if (ai >= indexForZ)
855                         child.atlasIndex = ai + 1;
856                 }
857             }
858         }
859         this.tiles[z] = gid;
860         return tile;
861     },
862 
863     _updateTileForGID:function (gid, pos) {
864         var rect = this.tileset.rectForGID(gid);
865         var locScaleFactor = this._contentScaleFactor;
866         rect = cc.rect(rect.x / locScaleFactor, rect.y / locScaleFactor,
867             rect.width / locScaleFactor, rect.height / locScaleFactor);
868         var z = pos.x + pos.y * this._layerSize.width;
869 
870         var tile = this._reusedTileWithRect(rect);
871         this._setupTileSprite(tile, pos, gid);
872 
873         // get atlas index
874         var indexForZ = this._atlasIndexForExistantZ(z);
875         tile.atlasIndex = indexForZ;
876         tile.dirty = true;
877         tile.updateTransform();
878         this.tiles[z] = gid;
879 
880         return tile;
881     },
882 
883     //The layer recognizes some special properties, like cc_vertez
884     _parseInternalProperties:function () {
885         // if cc_vertex=automatic, then tiles will be rendered using vertexz
886         var vertexz = this.getProperty("cc_vertexz");
887         if (vertexz) {
888             if (vertexz == "automatic") {
889                 this._useAutomaticVertexZ = true;
890                 var alphaFuncVal = this.getProperty("cc_alpha_func");
891                 var alphaFuncValue = 0;
892                 if (alphaFuncVal)
893                     alphaFuncValue = parseFloat(alphaFuncVal);
894 
895                 if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
896                     this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST);
897                     var alphaValueLocation = cc._renderContext.getUniformLocation(this.shaderProgram.getProgram(), cc.UNIFORM_ALPHA_TEST_VALUE_S);
898                     // NOTE: alpha test shader is hard-coded to use the equivalent of a glAlphaFunc(GL_GREATER) comparison
899                     this.shaderProgram.use();
900                     this.shaderProgram.setUniformLocationWith1f(alphaValueLocation, alphaFuncValue);
901                 }
902             } else
903                 this._vertexZvalue = parseInt(vertexz, 10);
904         }
905     },
906 
907     _setupTileSprite:function (sprite, pos, gid) {
908         var z = pos.x + pos.y * this._layerSize.width;
909         sprite.setPosition(this.getPositionAt(pos));
910         if (cc._renderType === cc._RENDER_TYPE_WEBGL)
911             sprite.vertexZ = this._vertexZForPos(pos);
912         else
913             sprite.tag = z;
914 
915         sprite.anchorX = 0;
916 	    sprite.anchorY = 0;
917         sprite.opacity = this._opacity;
918         if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
919             sprite.rotation = 0.0;
920         }
921 
922         sprite.setFlippedX(false);
923         sprite.setFlippedY(false);
924 
925         // Rotation in tiled is achieved using 3 flipped states, flipping across the horizontal, vertical, and diagonal axes of the tiles.
926         if ((gid & cc.TMX_TILE_DIAGONAL_FLAG) >>> 0) {
927             // put the anchor in the middle for ease of rotation.
928             sprite.anchorX = 0.5;
929 	        sprite.anchorY = 0.5;
930             sprite.x = this.getPositionAt(pos).x + sprite.width / 2;
931 	        sprite.y = this.getPositionAt(pos).y + sprite.height / 2;
932 
933             var flag = (gid & (cc.TMX_TILE_HORIZONTAL_FLAG | cc.TMX_TILE_VERTICAL_FLAG) >>> 0) >>> 0;
934             // handle the 4 diagonally flipped states.
935             if (flag == cc.TMX_TILE_HORIZONTAL_FLAG)
936                 sprite.rotation = 90;
937             else if (flag == cc.TMX_TILE_VERTICAL_FLAG)
938                 sprite.rotation = 270;
939             else if (flag == (cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) {
940                 sprite.rotation = 90;
941 	            sprite.setFlippedX(true);
942             } else {
943                 sprite.rotation = 270;
944 	            sprite.setFlippedX(true);
945             }
946         } else {
947             if ((gid & cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) {
948                 sprite.setFlippedX(true);
949             }
950 
951             if ((gid & cc.TMX_TILE_VERTICAL_FLAG) >>> 0) {
952                 sprite.setFlippedY(true);
953             }
954         }
955     },
956 
957     _reusedTileWithRect:function (rect) {
958         if(cc._renderType === cc._RENDER_TYPE_WEBGL){
959             if (!this._reusedTile) {
960                 this._reusedTile = new cc.Sprite();
961                 this._reusedTile.initWithTexture(this.texture, rect, false);
962                 this._reusedTile.batchNode = this;
963             } else {
964                 // XXX HACK: Needed because if "batch node" is nil,
965                 // then the Sprite'squad will be reset
966                 this._reusedTile.batchNode = null;
967 
968                 // Re-init the sprite
969                 this._reusedTile.setTextureRect(rect, false);
970 
971                 // restore the batch node
972                 this._reusedTile.batchNode = this;
973             }
974         } else {
975             this._reusedTile = new cc.Sprite();
976             this._reusedTile.initWithTexture(this._textureForCanvas, rect, false);
977             this._reusedTile.batchNode = this;
978             this._reusedTile.parent = this;
979         }
980         return this._reusedTile;
981     },
982 
983     _vertexZForPos:function (pos) {
984         var ret = 0;
985         var maxVal = 0;
986         if (this._useAutomaticVertexZ) {
987             switch (this.layerOrientation) {
988                 case cc.TMX_ORIENTATION_ISO:
989                     maxVal = this._layerSize.width + this._layerSize.height;
990                     ret = -(maxVal - (pos.x + pos.y));
991                     break;
992                 case cc.TMX_ORIENTATION_ORTHO:
993                     ret = -(this._layerSize.height - pos.y);
994                     break;
995                 case cc.TMX_ORIENTATION_HEX:
996                     cc.log("TMX Hexa zOrder not supported");
997                     break;
998                 default:
999                     cc.log("TMX invalid value");
1000                     break;
1001             }
1002         } else
1003             ret = this._vertexZvalue;
1004         return ret;
1005     },
1006 
1007     _atlasIndexForExistantZ:function (z) {
1008         var item;
1009         if (this._atlasIndexArray) {
1010             var locAtlasIndexArray = this._atlasIndexArray;
1011             for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) {
1012                 item = locAtlasIndexArray[i];
1013                 if (item == z)
1014                     break;
1015             }
1016         }
1017         if(typeof item != "number")
1018             cc.log("cc.TMXLayer._atlasIndexForExistantZ(): TMX atlas index not found. Shall not happen");
1019         return i;
1020     },
1021 
1022     _atlasIndexForNewZ:function (z) {
1023         var locAtlasIndexArray = this._atlasIndexArray;
1024         for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) {
1025             var val = locAtlasIndexArray[i];
1026             if (z < val)
1027                 break;
1028         }
1029         return i;
1030     }
1031 });
1032 
1033 var _p = cc.TMXLayer.prototype;
1034 
1035 if(cc._renderType == cc._RENDER_TYPE_WEBGL){
1036 	_p.draw = cc.SpriteBatchNode.prototype.draw;
1037     _p.visit = cc.SpriteBatchNode.prototype.visit;
1038 	_p.getTexture = cc.SpriteBatchNode.prototype.getTexture;
1039 }else{
1040     _p.draw = _p._drawForCanvas;
1041     _p.visit = _p._visitForCanvas;
1042 	_p.getTexture = _p._getTextureForCanvas;
1043 }
1044 
1045 /** @expose */
1046 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
1047 
1048 // Extended properties
1049 /** @expose */
1050 _p.layerWidth;
1051 cc.defineGetterSetter(_p, "layerWidth", _p._getLayerWidth, _p._setLayerWidth);
1052 /** @expose */
1053 _p.layerHeight;
1054 cc.defineGetterSetter(_p, "layerHeight", _p._getLayerHeight, _p._setLayerHeight);
1055 /** @expose */
1056 _p.tileWidth;
1057 cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth);
1058 /** @expose */
1059 _p.tileHeight;
1060 cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight);
1061 
1062 
1063 /**
1064  * Creates a cc.TMXLayer with an tile set info, a layer info and a map info
1065  * @param {cc.TMXTilesetInfo} tilesetInfo
1066  * @param {cc.TMXLayerInfo} layerInfo
1067  * @param {cc.TMXMapInfo} mapInfo
1068  * @return {cc.TMXLayer|Null}
1069  */
1070 cc.TMXLayer.create = function (tilesetInfo, layerInfo, mapInfo) {
1071     return new cc.TMXLayer(tilesetInfo, layerInfo, mapInfo);
1072 };
1073