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