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