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         //cc.Assert(texture != null, "TextureAtlas.initWithTexture():texture should not be null");
235         capacity = 0 | (capacity);
236         this._capacity = capacity;
237         this._totalQuads = 0;
238 
239         // retained in property
240         this._texture = texture;
241 
242         // Re-initialization is not allowed
243         cc.Assert(this._quads == null && this._indices == null, "TextureAtlas.initWithTexture():_quads and _indices should not be null");
244 
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         //cc.Assert(index >= 0 && index < this._capacity, "updateQuadWithTexture: Invalid index");
272         this._totalQuads = Math.max(index + 1, this._totalQuads);
273         this._setQuadToArray(quad, index);
274         this._dirty = true;
275     },
276 
277     /**
278      * <p>Inserts a Quad (texture, vertex and color) at a certain index<br />
279      * index must be between 0 and the atlas capacity - 1 </p>
280      * @param {cc.V2F_C4B_T2F_Quad} quad
281      * @param {Number} index
282      */
283     insertQuad:function (quad, index) {
284         cc.Assert(index < this._capacity, "insertQuadWithTexture: Invalid index");
285 
286         this._totalQuads++;
287         cc.Assert(this._totalQuads <= this._capacity, "invalid totalQuads");
288 
289         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
290         // issue #575. index can be > totalQuads
291         var remaining = (this._totalQuads-1) - index;
292         var startOffset = index * quadSize;
293         var moveLength = remaining * quadSize;
294         this._quads[this._totalQuads -1] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, (this._totalQuads -1) * quadSize);
295         this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset + quadSize);
296 
297         this._setQuadToArray(quad, index);
298         this._dirty = true;
299     },
300 
301     /**
302      * <p>
303      *      Inserts a c array of quads at a given index                                           <br />
304      *      index must be between 0 and the atlas capacity - 1                                    <br />
305      *      this method doesn't enlarge the array when amount + index > totalQuads                <br />
306      * </p>
307      * @param {Array} quads
308      * @param {Number} index
309      * @param {Number} amount
310      */
311     insertQuads:function (quads, index, amount) {
312         amount = amount || quads.length;
313         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
314 
315         cc.Assert(index + amount <= this._capacity, "insertQuadWithTexture: Invalid index + amount");
316         this._totalQuads += amount;
317         cc.Assert(this._totalQuads <= this._capacity, "invalid totalQuads");
318 
319         // issue #575. index can be > totalQuads
320         var remaining = (this._totalQuads-1) - index - amount;
321         var startOffset = index * quadSize;
322         var moveLength = remaining * quadSize;
323         var lastIndex = (this._totalQuads-1)  - amount;
324 
325         var i;
326         for(i = 0; i < amount;i++)
327             this._quads[lastIndex + i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, (this._totalQuads -1) * quadSize);
328         this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset + quadSize * amount);
329         for(i = 0; i < amount; i++)
330             this._setQuadToArray(quads[i], index + i);
331 
332         this._dirty = true;
333     },
334 
335     /**
336      * <p>Removes the quad that is located at a certain index and inserts it at a new index <br />
337      * This operation is faster than removing and inserting in a quad in 2 different steps</p>
338      * @param {Number} fromIndex
339      * @param {Number} newIndex
340      */
341     insertQuadFromIndex:function (fromIndex, newIndex) {
342         cc.Assert(newIndex >= 0 && newIndex < this._totalQuads, "insertQuadFromIndex:atIndex: Invalid index");
343         cc.Assert(fromIndex >= 0 && fromIndex < this._totalQuads, "insertQuadFromIndex:atIndex: Invalid index");
344         if (fromIndex === newIndex)
345             return;
346 
347         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
348         var locQuadsReader = this._quadsReader;
349         var sourceArr = locQuadsReader.subarray(fromIndex * quadSize,quadSize);
350         var startOffset, moveLength;
351         if(fromIndex > newIndex){
352             startOffset = newIndex * quadSize;
353             moveLength = (fromIndex - newIndex) * quadSize;
354             locQuadsReader.set(locQuadsReader.subarray(startOffset, startOffset + moveLength),startOffset + quadSize);
355             locQuadsReader.set(sourceArr,startOffset);
356         }else{
357             startOffset = (fromIndex + 1) * quadSize;
358             moveLength = (newIndex - fromIndex) * quadSize;
359             locQuadsReader.set(locQuadsReader.subarray(startOffset, startOffset + moveLength),startOffset - quadSize);
360             locQuadsReader.set(sourceArr, newIndex * quadSize);
361         }
362         this._dirty = true;
363     },
364 
365     /**
366      * <p>Removes a quad at a given index number.<br />
367      * The capacity remains the same, but the total number of quads to be drawn is reduced in 1 </p>
368      * @param {Number} index
369      */
370     removeQuadAtIndex:function (index) {
371         cc.Assert(index < this._totalQuads, "removeQuadAtIndex: Invalid index");
372         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
373         this._totalQuads--;
374         this._quads.length = this._totalQuads;
375         if(index !== this._totalQuads){
376             //move data
377             var startOffset = (index + 1) * quadSize;
378             var moveLength = (this._totalQuads - index) * quadSize;
379             this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset - quadSize);
380         }
381         this._dirty = true;
382     },
383 
384     removeQuadsAtIndex:function (index, amount) {
385         cc.Assert(index + amount <= this._totalQuads, "removeQuadAtIndex: index + amount out of bounds");
386         this._totalQuads -= amount;
387 
388         if(index !== this._totalQuads){
389             //move data
390             var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
391             var srcOffset = (index + amount) * quadSize;
392             var moveLength = (this._totalQuads - index) * quadSize;
393             var dstOffset = index * quadSize;
394             this._quadsReader.set(this._quadsReader.subarray(srcOffset,srcOffset + moveLength),dstOffset);
395         }
396         this._dirty = true;
397     },
398 
399     /**
400      * <p>Removes all Quads. <br />
401      * The TextureAtlas capacity remains untouched. No memory is freed.<br />
402      * The total number of quads to be drawn will be 0</p>
403      */
404     removeAllQuads:function () {
405         this._quads.length = 0;
406         this._totalQuads = 0;
407     },
408 
409     _setDirty:function(dirty){
410         this._dirty = dirty;
411     },
412 
413     /**
414      * <p>Resize the capacity of the CCTextureAtlas.<br />
415      * The new capacity can be lower or higher than the current one<br />
416      * It returns YES if the resize was successful. <br />
417      * If it fails to resize the capacity it will return NO with a new capacity of 0. <br />
418      * no used for js</p>
419      * @param {Number} newCapacity
420      * @return {Boolean}
421      */
422     resizeCapacity:function (newCapacity) {
423         if (newCapacity == this._capacity)
424             return true;
425 
426         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
427         var oldCapacity = this._capacity;
428         // update capacity and totolQuads
429         this._totalQuads = Math.min(this._totalQuads, newCapacity);
430         this._capacity = 0 | newCapacity;
431         var i, capacity = this._capacity, locTotalQuads = this._totalQuads;
432 
433         if (this._quads == null) {
434             this._quads = [];
435             this._quadsArrayBuffer = new ArrayBuffer(quadSize * capacity);
436             this._quadsReader = new Uint8Array(this._quadsArrayBuffer);
437             for(i = 0; i< capacity; i++)
438                 this._quads = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer,i * quadSize);
439         } else {
440             var newQuads, newArrayBuffer, quads = this._quads;
441             if (capacity > oldCapacity) {
442                 newQuads = [];
443                 newArrayBuffer = new ArrayBuffer(quadSize * capacity);
444                 for(i = 0; i < locTotalQuads;i++){
445                      newQuads[i] = new cc.V3F_C4B_T2F_Quad(quads[i].tl,quads[i].bl,quads[i].tr,quads[i].br,
446                          newArrayBuffer,i * quadSize);
447                 }
448                 for(;i<capacity; i ++)
449                     newQuads[i] = new cc.V3F_C4B_T2F_Quad(null,null,null,null, newArrayBuffer,i * quadSize);
450 
451                 this._quadsReader = new Uint8Array(newArrayBuffer);
452                 this._quads = newQuads;
453                 this._quadsArrayBuffer = newArrayBuffer;
454             } else {
455                 var count = Math.max(locTotalQuads, capacity);
456                 newQuads = [];
457                 newArrayBuffer = new ArrayBuffer(quadSize * capacity);
458                 for(i = 0; i < count;i++){
459                     newQuads[i] = new cc.V3F_C4B_T2F_Quad(quads[i].tl,quads[i].bl,quads[i].tr,quads[i].br,
460                         newArrayBuffer,i * quadSize);
461                 }
462                 this._quadsReader = new Uint8Array(newArrayBuffer);
463                 this._quads = newQuads;
464                 this._quadsArrayBuffer = newArrayBuffer;
465             }
466         }
467 
468         if (this._indices == null) {
469             this._indices = new Uint16Array(capacity * 6);
470         } else {
471             if (capacity > oldCapacity) {
472                 var tempIndices = new Uint16Array(capacity * 6);
473                 tempIndices.set(this._indices, 0);
474                 this._indices = tempIndices;
475             } else {
476                 this._indices = this._indices.subarray(0, capacity * 6);
477             }
478         }
479 
480         this._setupIndices();
481         this._mapBuffers();
482         this._dirty = true;
483         return true;
484     },
485 
486     /**
487      * Used internally by CCParticleBatchNode                                    <br/>
488      * don't use this unless you know what you're doing
489      * @param {Number} amount
490      */
491     increaseTotalQuadsWith:function (amount) {
492         this._totalQuads += amount;
493     },
494 
495     /**
496      * Moves an amount of quads from oldIndex at newIndex
497      * @param {Number} oldIndex
498      * @param {Number} amount
499      * @param {Number} newIndex
500      */
501     moveQuadsFromIndex: function (oldIndex, amount, newIndex) {
502         if (arguments.length == 2) {
503             newIndex = amount;
504             amount = this._totalQuads - oldIndex;
505             cc.Assert(newIndex + (this._totalQuads - oldIndex) <= this._capacity, "moveQuadsFromIndex move is out of bounds");
506             if(amount === 0)
507                 return;
508         }else{
509             cc.Assert(newIndex + amount <= this._totalQuads, "moveQuadsFromIndex:newIndex: Invalid index");
510             cc.Assert(oldIndex < this._totalQuads, "moveQuadsFromIndex:oldIndex: Invalid index");
511 
512             if (oldIndex == newIndex)
513                 return;
514         }
515 
516         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
517         var srcOffset = oldIndex * quadSize;
518         var srcLength = amount * quadSize;
519         var locQuadsReader = this._quadsReader;
520         var sourceArr = locQuadsReader.subarray(srcOffset, srcOffset + srcLength);
521         var dstOffset = newIndex * quadSize;
522         var moveLength, moveStart;
523         if (newIndex < oldIndex) {
524             moveLength = (oldIndex - newIndex) * quadSize;
525             moveStart = newIndex * quadSize;
526             locQuadsReader.set(locQuadsReader.subarray(moveStart, moveStart + moveLength), moveStart + srcLength)
527         } else {
528             moveLength = (newIndex - oldIndex) * quadSize;
529             moveStart = (oldIndex + amount) * quadSize;
530             locQuadsReader.set(locQuadsReader.subarray(moveStart, moveStart + moveLength), srcOffset);
531         }
532         locQuadsReader.set(sourceArr, dstOffset);
533         this._dirty = true;
534     },
535 
536     /**
537      * Ensures that after a realloc quads are still empty                                <br/>
538      * Used internally by CCParticleBatchNode
539      * @param {Number} index
540      * @param {Number} amount
541      */
542     fillWithEmptyQuadsFromIndex:function (index, amount) {
543         var count = amount * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
544         var clearReader = new Uint8Array(this._quadsArrayBuffer, index * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT, count);
545         for (var i = 0; i < count; i++)
546             clearReader[i] = 0;
547     },
548 
549     // TextureAtlas - Drawing
550 
551     /**
552      * <p>Draws n quads from an index (offset). <br />
553      * n + start can't be greater than the capacity of the atlas</p>
554      * @param {Number} n
555      * @param {Number} start
556      */
557     drawNumberOfQuads:function (n, start) {
558         start = start || 0;
559         if (0 === n || !this._texture || !this._texture.isLoaded())
560             return;
561 
562         var gl = cc.renderContext;
563         cc.glBindTexture2D(this._texture);
564 
565         //
566         // Using VBO without VAO
567         //
568         //vertices
569         //gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
570         // XXX: update is done in draw... perhaps it should be done in a timer
571         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSCOLORTEX);
572 
573         gl.bindBuffer(gl.ARRAY_BUFFER, this._quadsWebBuffer);
574         if (this._dirty)
575             gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
576 
577         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0);               // vertices
578         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12);          // colors
579         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16);            // tex coords
580 
581         if (this._dirty)
582             this._dirty = false;
583 
584         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
585 
586         if (cc.TEXTURE_ATLAS_USE_TRIANGLE_STRIP)
587             gl.drawElements(gl.TRIANGLE_STRIP, n * 6, gl.UNSIGNED_SHORT, start * 6 * this._indices.BYTES_PER_ELEMENT);
588         else
589             gl.drawElements(gl.TRIANGLES, n * 6, gl.UNSIGNED_SHORT, start * 6 * this._indices.BYTES_PER_ELEMENT);
590 
591         cc.g_NumberOfDraws++;
592         //cc.CHECK_GL_ERROR_DEBUG();
593     },
594 
595     /**
596      * Draws all the Atlas's Quads
597      */
598     drawQuads:function () {
599         this.drawNumberOfQuads(this._totalQuads, 0);
600     },
601 
602     _releaseBuffer: function () {
603         var gl = cc.renderContext;
604         if (this._buffersVBO) {
605             if (this._buffersVBO[0])
606                 gl.deleteBuffer(this._buffersVBO[0]);
607             if (this._buffersVBO[1])
608                 gl.deleteBuffer(this._buffersVBO[1])
609         }
610         if (this._quadsWebBuffer)
611             gl.deleteBuffer(this._quadsWebBuffer);
612     }
613 });
614 
615 /**
616  * <p>Creates a TextureAtlas with an filename and with an initial capacity for Quads. <br />
617  * The TextureAtlas capacity can be increased in runtime. </p>
618  * @param {String} file
619  * @param {Number} capacity
620  * @return {cc.TextureAtlas|Null}
621  * @example
622  * //example
623  * var textureAtlas = cc.TextureAtlas.create("hello.png", 3);
624  */
625 cc.TextureAtlas.create = function (file, capacity) {
626     var textureAtlas = new cc.TextureAtlas();
627     if (textureAtlas && textureAtlas.initWithFile(file, capacity))
628         return textureAtlas;
629     return null;
630 };
631 
632 /**
633  * <p>Creates a TextureAtlas with a previously initialized Texture2D object, and with an initial capacity for n Quads.
634  * The TextureAtlas capacity can be increased in runtime.</p>
635  * @param {Image|cc.Texture2D} texture
636  * @param {Number} capacity
637  * @return {cc.TextureAtlas}
638  * @example
639  * //example
640  * var texture = cc.TextureCache.getInstance().addImage("hello.png");
641  * var textureAtlas = cc.TextureAtlas.createWithTexture(texture, 3);
642  */
643 cc.TextureAtlas.createWithTexture = function (texture, capacity) {
644     var textureAtlas = new cc.TextureAtlas();
645     if (textureAtlas && textureAtlas.initWithTexture(texture, capacity))
646         return textureAtlas;
647     return null;
648 };
649