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>A class that implements a Texture Atlas. <br />
 29  * Supported features: <br />
 30  * The atlas file can be a PNG, JPG. <br />
 31  * Quads can be updated in runtime <br />
 32  * Quads can be added in runtime <br />
 33  * Quads can be removed in runtime <br />
 34  * Quads can be re-ordered in runtime <br />
 35  * The TextureAtlas capacity can be increased or decreased in runtime.</p>
 36  * @class
 37  * @extends cc.Class
 38  *
 39  * @property {Boolean}  dirty           - Indicates whether or not the array buffer of the VBO needs to be updated.
 40  * @property {Image}    texture         - Image texture for cc.TextureAtlas.
 41  * @property {Number}   capacity        - <@readonly> Quantity of quads that can be stored with the current texture atlas size.
 42  * @property {Number}   totalQuads      - <@readonly> Quantity of quads that are going to be drawn.
 43  * @property {Array}    quads           - <@readonly> Quads that are going to be rendered
 44  */
 45 cc.TextureAtlas = cc.Class.extend(/** @lends cc.TextureAtlas# */{
 46 	dirty:false,
 47 	texture:null,
 48 
 49     _indices:null,
 50     //0: vertex  1: indices
 51     _buffersVBO:null,
 52     _capacity:0,
 53 
 54     _quads:null,
 55     _quadsArrayBuffer:null,
 56     _quadsWebBuffer:null,
 57     _quadsReader:null,
 58 
 59     ctor:function () {
 60         this._buffersVBO = [];
 61     },
 62 
 63     /**
 64      * Quantity of quads that are going to be drawn.
 65      * @return {Number}
 66      */
 67     getTotalQuads:function () {
 68         //return this._quads.length;
 69         return this._totalQuads;
 70     },
 71 
 72     /**
 73      * Quantity of quads that can be stored with the current texture atlas size
 74      * @return {Number}
 75      */
 76     getCapacity:function () {
 77         return this._capacity;
 78     },
 79 
 80     /**
 81      * Texture of the texture atlas
 82      * @return {Image}
 83      */
 84     getTexture:function () {
 85         return this.texture;
 86     },
 87 
 88     /**
 89      * @param {Image} texture
 90      */
 91     setTexture:function (texture) {
 92         this.texture = texture;
 93     },
 94 
 95     /**
 96      * specify if the array buffer of the VBO needs to be updated
 97      * @param {Boolean} dirty
 98      */
 99     setDirty:function (dirty) {
100         this.dirty = dirty;
101     },
102 
103     /**
104      * whether or not the array buffer of the VBO needs to be updated
105      * @returns {boolean}
106      */
107     isDirty:function () {
108         return this.dirty;
109     },
110 
111     /**
112      * Quads that are going to be rendered
113      * @return {Array}
114      */
115     getQuads:function () {
116         return this._quads;
117     },
118 
119     /**
120      * @param {Array} quads
121      */
122     setQuads:function (quads) {
123         this._quads = quads;
124         //TODO need re-binding
125     },
126 
127     _copyQuadsToTextureAtlas:function(quads, index){
128         if(!quads)
129             return;
130 
131         for(var i = 0; i < quads.length ; i++)
132             this._setQuadToArray(quads[i], index + i);
133     },
134 
135     _setQuadToArray: function (quad, index) {
136         var locQuads = this._quads;
137         if (!locQuads[index]) {
138             locQuads[index] = new cc.V3F_C4B_T2F_Quad(quad.tl, quad.bl, quad.tr, quad.br, this._quadsArrayBuffer, index * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT);
139             return;
140         }
141         locQuads[index].bl = quad.bl;
142         locQuads[index].br = quad.br;
143         locQuads[index].tl = quad.tl;
144         locQuads[index].tr = quad.tr;
145     },
146 
147     /**
148      * Description
149      * @return {String}
150      */
151     description:function () {
152         return '<cc.TextureAtlas | totalQuads =' + this._totalQuads + '>';
153     },
154 
155     _setupIndices:function () {
156         if (this._capacity === 0)
157             return;
158         var locIndices = this._indices, locCapacity = this._capacity;
159         for (var i = 0; i < locCapacity; i++) {
160             if (cc.TEXTURE_ATLAS_USE_TRIANGLE_STRIP) {
161                 locIndices[i * 6 + 0] = i * 4 + 0;
162                 locIndices[i * 6 + 1] = i * 4 + 0;
163                 locIndices[i * 6 + 2] = i * 4 + 2;
164                 locIndices[i * 6 + 3] = i * 4 + 1;
165                 locIndices[i * 6 + 4] = i * 4 + 3;
166                 locIndices[i * 6 + 5] = i * 4 + 3;
167             } else {
168                 locIndices[i * 6 + 0] = i * 4 + 0;
169                 locIndices[i * 6 + 1] = i * 4 + 1;
170                 locIndices[i * 6 + 2] = i * 4 + 2;
171 
172                 // inverted index. issue #179
173                 locIndices[i * 6 + 3] = i * 4 + 3;
174                 locIndices[i * 6 + 4] = i * 4 + 2;
175                 locIndices[i * 6 + 5] = i * 4 + 1;
176             }
177         }
178     },
179 
180     _setupVBO:function () {
181         var gl = cc._renderContext;
182         //create WebGLBuffer
183         this._buffersVBO[0] = gl.createBuffer();
184         this._buffersVBO[1] = gl.createBuffer();
185 
186         this._quadsWebBuffer = gl.createBuffer();
187         this._mapBuffers();
188     },
189 
190     _mapBuffers:function () {
191         var gl = cc._renderContext;
192 
193         gl.bindBuffer(gl.ARRAY_BUFFER, this._quadsWebBuffer);
194         gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
195 
196         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
197         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW);
198 
199         //cc.CHECK_GL_ERROR_DEBUG();
200     },
201 
202     /**
203      * <p>Initializes a TextureAtlas with a filename and with a certain capacity for Quads.<br />
204      * The TextureAtlas capacity can be increased in runtime.<br />
205      * WARNING: Do not reinitialize the TextureAtlas because it will leak memory. </p>
206      * @param {String} file
207      * @param {Number} capacity
208      * @return {Boolean}
209      * @example
210      * //example
211      * var textureAtlas = new cc.TextureAtlas();
212      * textureAtlas.initWithTexture("hello.png", 3);
213      */
214     initWithFile:function (file, capacity) {
215         // retained in property
216         var texture = cc.textureCache.addImage(file);
217         if (texture)
218             return this.initWithTexture(texture, capacity);
219         else {
220             cc.log("cocos2d: Could not open file: " + file);
221             return false;
222         }
223     },
224 
225     /**
226      * <p>Initializes a TextureAtlas with a previously initialized Texture2D object, and<br />
227      * with an initial capacity for Quads.<br />
228      * The TextureAtlas capacity can be increased in runtime.<br />
229      * WARNING: Do not reinitialize the TextureAtlas because it will leak memory</p>
230      * @param {Image} texture
231      * @param {Number} capacity
232      * @return {Boolean}
233      * @example
234      * //example
235      * var texture = cc.textureCache.addImage("hello.png");
236      * var textureAtlas = new cc.TextureAtlas();
237      * textureAtlas.initWithTexture(texture, 3);
238      */
239     initWithTexture:function (texture, capacity) {
240         if(!texture)
241             throw "cc.TextureAtlas.initWithTexture():texture should be non-null";
242 
243         capacity = 0 | (capacity);
244         this._capacity = capacity;
245         this._totalQuads = 0;
246 
247         // retained in property
248         this.texture = texture;
249 
250         // Re-initialization is not allowed
251         this._quads = [];
252         this._indices = new Uint16Array(capacity * 6);
253         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
254         this._quadsArrayBuffer = new ArrayBuffer(quadSize * capacity);
255         this._quadsReader = new Uint8Array(this._quadsArrayBuffer);
256 
257         if (!( this._quads && this._indices) && capacity > 0)
258             return false;
259 
260         var locQuads = this._quads;
261         for(var i = 0; i< capacity; i++)
262             locQuads[i] =new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, i * quadSize);
263 
264         this._setupIndices();
265         this._setupVBO();
266         this.dirty = true;
267         return true;
268     },
269 
270     /**
271      * <p>Updates a Quad (texture, vertex and color) at a certain index <br />
272      * index must be between 0 and the atlas capacity - 1 </p>
273      * @param {cc.V2F_C4B_T2F_Quad} quad
274      * @param {Number} index
275      */
276     updateQuad:function (quad, index) {
277         if(!quad)
278             throw "cc.TextureAtlas.updateQuad(): quad should be non-null";
279         if((index < 0) || (index >= this._capacity))
280             throw "cc.TextureAtlas.updateQuad(): Invalid index";
281         this._totalQuads = Math.max(index + 1, this._totalQuads);
282         this._setQuadToArray(quad, index);
283         this.dirty = true;
284     },
285 
286     /**
287      * <p>Inserts a Quad (texture, vertex and color) at a certain index<br />
288      * index must be between 0 and the atlas capacity - 1 </p>
289      * @param {cc.V2F_C4B_T2F_Quad} quad
290      * @param {Number} index
291      */
292     insertQuad:function (quad, index) {
293         if(index >= this._capacity)
294             throw "cc.TextureAtlas.insertQuad(): Invalid index";
295 
296         this._totalQuads++;
297         if(this._totalQuads > this._capacity) {
298             cc.log("cc.TextureAtlas.insertQuad(): invalid totalQuads");
299             return;
300         }
301         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
302         // issue #575. index can be > totalQuads
303         var remaining = (this._totalQuads-1) - index;
304         var startOffset = index * quadSize;
305         var moveLength = remaining * quadSize;
306         this._quads[this._totalQuads -1] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, (this._totalQuads -1) * quadSize);
307         this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset + quadSize);
308 
309         this._setQuadToArray(quad, index);
310         this.dirty = true;
311     },
312 
313     /**
314      * <p>
315      *      Inserts a c array of quads at a given index                                           <br />
316      *      index must be between 0 and the atlas capacity - 1                                    <br />
317      *      this method doesn't enlarge the array when amount + index > totalQuads                <br />
318      * </p>
319      * @param {Array} quads
320      * @param {Number} index
321      * @param {Number} amount
322      */
323     insertQuads:function (quads, index, amount) {
324         amount = amount || quads.length;
325         if((index + amount) > this._capacity)
326             throw "cc.TextureAtlas.insertQuad(): Invalid index + amount";
327 
328         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
329         this._totalQuads += amount;
330         if(this._totalQuads > this._capacity) {
331             cc.log("cc.TextureAtlas.insertQuad(): invalid totalQuads");
332             return;
333         }
334 
335         // issue #575. index can be > totalQuads
336         var remaining = (this._totalQuads-1) - index - amount;
337         var startOffset = index * quadSize;
338         var moveLength = remaining * quadSize;
339         var lastIndex = (this._totalQuads-1)  - amount;
340 
341         var i;
342         for(i = 0; i < amount;i++)
343             this._quads[lastIndex + i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, (this._totalQuads -1) * quadSize);
344         this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset + quadSize * amount);
345         for(i = 0; i < amount; i++)
346             this._setQuadToArray(quads[i], index + i);
347 
348         this.dirty = true;
349     },
350 
351     /**
352      * <p>Removes the quad that is located at a certain index and inserts it at a new index <br />
353      * This operation is faster than removing and inserting in a quad in 2 different steps</p>
354      * @param {Number} fromIndex
355      * @param {Number} newIndex
356      */
357     insertQuadFromIndex:function (fromIndex, newIndex) {
358         if (fromIndex === newIndex)
359             return;
360 
361         if(newIndex < 0 && newIndex >= this._totalQuads)
362             throw "cc.TextureAtlas.insertQuadFromIndex(): Invalid newIndex";
363         if(fromIndex < 0 && fromIndex >= this._totalQuads)
364             throw "cc.TextureAtlas.insertQuadFromIndex(): Invalid fromIndex";
365 
366         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
367         var locQuadsReader = this._quadsReader;
368         var sourceArr = locQuadsReader.subarray(fromIndex * quadSize,quadSize);
369         var startOffset, moveLength;
370         if(fromIndex > newIndex){
371             startOffset = newIndex * quadSize;
372             moveLength = (fromIndex - newIndex) * quadSize;
373             locQuadsReader.set(locQuadsReader.subarray(startOffset, startOffset + moveLength),startOffset + quadSize);
374             locQuadsReader.set(sourceArr,startOffset);
375         }else{
376             startOffset = (fromIndex + 1) * quadSize;
377             moveLength = (newIndex - fromIndex) * quadSize;
378             locQuadsReader.set(locQuadsReader.subarray(startOffset, startOffset + moveLength),startOffset - quadSize);
379             locQuadsReader.set(sourceArr, newIndex * quadSize);
380         }
381         this.dirty = true;
382     },
383 
384     /**
385      * <p>Removes a quad at a given index number.<br />
386      * The capacity remains the same, but the total number of quads to be drawn is reduced in 1 </p>
387      * @param {Number} index
388      */
389     removeQuadAtIndex:function (index) {
390         if(index >= this._totalQuads)
391             throw "cc.TextureAtlas.removeQuadAtIndex(): Invalid index";
392         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
393         this._totalQuads--;
394         this._quads.length = this._totalQuads;
395         if(index !== this._totalQuads){
396             //move data
397             var startOffset = (index + 1) * quadSize;
398             var moveLength = (this._totalQuads - index) * quadSize;
399             this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset - quadSize);
400         }
401         this.dirty = true;
402     },
403 
404     removeQuadsAtIndex:function (index, amount) {
405         if(index + amount > this._totalQuads)
406             throw "cc.TextureAtlas.removeQuadsAtIndex(): index + amount out of bounds";
407         this._totalQuads -= amount;
408 
409         if(index !== this._totalQuads){
410             //move data
411             var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
412             var srcOffset = (index + amount) * quadSize;
413             var moveLength = (this._totalQuads - index) * quadSize;
414             var dstOffset = index * quadSize;
415             this._quadsReader.set(this._quadsReader.subarray(srcOffset,srcOffset + moveLength),dstOffset);
416         }
417         this.dirty = true;
418     },
419 
420     /**
421      * <p>Removes all Quads. <br />
422      * The TextureAtlas capacity remains untouched. No memory is freed.<br />
423      * The total number of quads to be drawn will be 0</p>
424      */
425     removeAllQuads:function () {
426         this._quads.length = 0;
427         this._totalQuads = 0;
428     },
429 
430     _setDirty:function(dirty){
431         this.dirty = dirty;
432     },
433 
434     /**
435      * <p>Resize the capacity of the CCTextureAtlas.<br />
436      * The new capacity can be lower or higher than the current one<br />
437      * It returns YES if the resize was successful. <br />
438      * If it fails to resize the capacity it will return NO with a new capacity of 0. <br />
439      * no used for js</p>
440      * @param {Number} newCapacity
441      * @return {Boolean}
442      */
443     resizeCapacity:function (newCapacity) {
444         if (newCapacity == this._capacity)
445             return true;
446 
447         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
448         var oldCapacity = this._capacity;
449         // update capacity and totolQuads
450         this._totalQuads = Math.min(this._totalQuads, newCapacity);
451         this._capacity = 0 | newCapacity;
452         var i, capacity = this._capacity, locTotalQuads = this._totalQuads;
453 
454         if (this._quads == null) {
455             this._quads = [];
456             this._quadsArrayBuffer = new ArrayBuffer(quadSize * capacity);
457             this._quadsReader = new Uint8Array(this._quadsArrayBuffer);
458             for(i = 0; i< capacity; i++)
459                 this._quads = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer,i * quadSize);
460         } else {
461             var newQuads, newArrayBuffer, quads = this._quads;
462             if (capacity > oldCapacity) {
463                 newQuads = [];
464                 newArrayBuffer = new ArrayBuffer(quadSize * capacity);
465                 for(i = 0; i < locTotalQuads;i++){
466                      newQuads[i] = new cc.V3F_C4B_T2F_Quad(quads[i].tl,quads[i].bl,quads[i].tr,quads[i].br,
467                          newArrayBuffer,i * quadSize);
468                 }
469                 for(;i<capacity; i ++)
470                     newQuads[i] = new cc.V3F_C4B_T2F_Quad(null,null,null,null, newArrayBuffer,i * quadSize);
471 
472                 this._quadsReader = new Uint8Array(newArrayBuffer);
473                 this._quads = newQuads;
474                 this._quadsArrayBuffer = newArrayBuffer;
475             } else {
476                 var count = Math.max(locTotalQuads, capacity);
477                 newQuads = [];
478                 newArrayBuffer = new ArrayBuffer(quadSize * capacity);
479                 for(i = 0; i < count;i++){
480                     newQuads[i] = new cc.V3F_C4B_T2F_Quad(quads[i].tl,quads[i].bl,quads[i].tr,quads[i].br,
481                         newArrayBuffer,i * quadSize);
482                 }
483                 this._quadsReader = new Uint8Array(newArrayBuffer);
484                 this._quads = newQuads;
485                 this._quadsArrayBuffer = newArrayBuffer;
486             }
487         }
488 
489         if (this._indices == null) {
490             this._indices = new Uint16Array(capacity * 6);
491         } else {
492             if (capacity > oldCapacity) {
493                 var tempIndices = new Uint16Array(capacity * 6);
494                 tempIndices.set(this._indices, 0);
495                 this._indices = tempIndices;
496             } else {
497                 this._indices = this._indices.subarray(0, capacity * 6);
498             }
499         }
500 
501         this._setupIndices();
502         this._mapBuffers();
503         this.dirty = true;
504         return true;
505     },
506 
507     /**
508      * Used internally by CCParticleBatchNode                                    <br/>
509      * don't use this unless you know what you're doing
510      * @param {Number} amount
511      */
512     increaseTotalQuadsWith:function (amount) {
513         this._totalQuads += amount;
514     },
515 
516     /**
517      * Moves an amount of quads from oldIndex at newIndex
518      * @param {Number} oldIndex
519      * @param {Number} amount
520      * @param {Number} newIndex
521      */
522     moveQuadsFromIndex: function (oldIndex, amount, newIndex) {
523         if (newIndex === undefined) {
524             newIndex = amount;
525             amount = this._totalQuads - oldIndex;
526             if((newIndex + (this._totalQuads - oldIndex)) > this._capacity)
527                 throw "cc.TextureAtlas.moveQuadsFromIndex(): move is out of bounds";
528             if(amount === 0)
529                 return;
530         }else{
531             if((newIndex + amount) > this._totalQuads)
532                 throw "cc.TextureAtlas.moveQuadsFromIndex(): Invalid newIndex";
533             if(oldIndex >= this._totalQuads)
534                 throw "cc.TextureAtlas.moveQuadsFromIndex(): Invalid oldIndex";
535 
536             if (oldIndex == newIndex)
537                 return;
538         }
539 
540         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
541         var srcOffset = oldIndex * quadSize;
542         var srcLength = amount * quadSize;
543         var locQuadsReader = this._quadsReader;
544         var sourceArr = locQuadsReader.subarray(srcOffset, srcOffset + srcLength);
545         var dstOffset = newIndex * quadSize;
546         var moveLength, moveStart;
547         if (newIndex < oldIndex) {
548             moveLength = (oldIndex - newIndex) * quadSize;
549             moveStart = newIndex * quadSize;
550             locQuadsReader.set(locQuadsReader.subarray(moveStart, moveStart + moveLength), moveStart + srcLength)
551         } else {
552             moveLength = (newIndex - oldIndex) * quadSize;
553             moveStart = (oldIndex + amount) * quadSize;
554             locQuadsReader.set(locQuadsReader.subarray(moveStart, moveStart + moveLength), srcOffset);
555         }
556         locQuadsReader.set(sourceArr, dstOffset);
557         this.dirty = true;
558     },
559 
560     /**
561      * Ensures that after a realloc quads are still empty                                <br/>
562      * Used internally by CCParticleBatchNode
563      * @param {Number} index
564      * @param {Number} amount
565      */
566     fillWithEmptyQuadsFromIndex:function (index, amount) {
567         var count = amount * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
568         var clearReader = new Uint8Array(this._quadsArrayBuffer, index * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT, count);
569         for (var i = 0; i < count; i++)
570             clearReader[i] = 0;
571     },
572 
573     // TextureAtlas - Drawing
574 
575     /**
576      * <p>Draws n quads from an index (offset). <br />
577      * n + start can't be greater than the capacity of the atlas</p>
578      * @param {Number} n
579      * @param {Number} start
580      */
581     drawNumberOfQuads:function (n, start) {
582         start = start || 0;
583         if (0 === n || !this.texture || !this.texture.isLoaded())
584             return;
585 
586         var gl = cc._renderContext;
587         cc.glBindTexture2D(this.texture);
588 
589         //
590         // Using VBO without VAO
591         //
592         //vertices
593         //gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
594         // XXX: update is done in draw... perhaps it should be done in a timer
595         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
596 
597         gl.bindBuffer(gl.ARRAY_BUFFER, this._quadsWebBuffer);
598         if (this.dirty)
599             gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
600 
601         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0);               // vertices
602         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12);          // colors
603         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16);            // tex coords
604 
605         if (this.dirty)
606             this.dirty = false;
607 
608         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
609 
610         if (cc.TEXTURE_ATLAS_USE_TRIANGLE_STRIP)
611             gl.drawElements(gl.TRIANGLE_STRIP, n * 6, gl.UNSIGNED_SHORT, start * 6 * this._indices.BYTES_PER_ELEMENT);
612         else
613             gl.drawElements(gl.TRIANGLES, n * 6, gl.UNSIGNED_SHORT, start * 6 * this._indices.BYTES_PER_ELEMENT);
614 
615         cc.g_NumberOfDraws++;
616         //cc.CHECK_GL_ERROR_DEBUG();
617     },
618 
619     /**
620      * Draws all the Atlas's Quads
621      */
622     drawQuads:function () {
623         this.drawNumberOfQuads(this._totalQuads, 0);
624     },
625 
626     _releaseBuffer: function () {
627         var gl = cc._renderContext;
628         if (this._buffersVBO) {
629             if (this._buffersVBO[0])
630                 gl.deleteBuffer(this._buffersVBO[0]);
631             if (this._buffersVBO[1])
632                 gl.deleteBuffer(this._buffersVBO[1])
633         }
634         if (this._quadsWebBuffer)
635             gl.deleteBuffer(this._quadsWebBuffer);
636     }
637 });
638 
639 window._p = cc.TextureAtlas.prototype;
640 
641 // Extended properties
642 /** @expose */
643 _p.totalQuads;
644 cc.defineGetterSetter(_p, "totalQuads", _p.getTotalQuads);
645 /** @expose */
646 _p.capacity;
647 cc.defineGetterSetter(_p, "capacity", _p.getCapacity);
648 /** @expose */
649 _p.quads;
650 cc.defineGetterSetter(_p, "quads", _p.getQuads, _p.setQuads);
651 
652 delete window._p;
653 
654 /**
655  * <p>Creates a TextureAtlas with an filename and with an initial capacity for Quads. <br />
656  * The TextureAtlas capacity can be increased in runtime. </p>
657  * @param {String|cc.Texture2D} fileName
658  * @param {Number} capacity
659  * @return {cc.TextureAtlas|Null}
660  * @example
661  * 1.
662  * //creates a TextureAtlas with  filename
663  * var textureAtlas = cc.TextureAtlas.create("res/hello.png", 3);
664  * 2.
665  * //creates a TextureAtlas with texture
666  * var texture = cc.textureCache.addImage("hello.png");
667  * var textureAtlas = cc.TextureAtlas.create(texture, 3);
668  */
669 cc.TextureAtlas.create = function (fileName, capacity) {
670     var textureAtlas = new cc.TextureAtlas();
671     if (typeof(fileName) == "string"){
672         if(textureAtlas.initWithFile(fileName, capacity))
673             return textureAtlas;
674     }
675     else if (fileName instanceof cc.Texture2D){
676         if(textureAtlas.initWithTexture(file, capacity))
677             return textureAtlas;
678     }
679 
680     return null;
681 };
682