1 /****************************************************************************
  2  Copyright (c) 2010-2011 cocos2d-x.org
  3  Copyright (c) 2010      Lam Pham
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24  ****************************************************************************/
 25 
 26 
 27 /**
 28  * Radial Counter-Clockwise
 29  * @type Number
 30  * @constant
 31  */
 32 cc.PROGRESS_TIMER_TYPE_RADIAL = 0;
 33 /**
 34  * Bar
 35  * @type Number
 36  * @constant
 37  */
 38 cc.PROGRESS_TIMER_TYPE_BAR = 1;
 39 
 40 /**
 41  * @constant
 42  * @type Number
 43  */
 44 cc.PROGRESS_TEXTURE_COORDS_COUNT = 4;
 45 
 46 /**
 47  * @constant
 48  * @type Number
 49  */
 50 cc.PROGRESS_TEXTURE_COORDS = 0x4b;
 51 
 52 /**
 53  * cc.Progresstimer is a subclass of cc.Node.   <br/>
 54  * It renders the inner sprite according to the percentage.<br/>
 55  * The progress can be Radial, Horizontal or vertical.
 56  * @class
 57  * @extends cc.NodeRGBA
 58  */
 59 cc.ProgressTimer = cc.NodeRGBA.extend(/** @lends cc.ProgressTimer# */{
 60     _type:null,
 61     _percentage:0.0,
 62     _sprite:null,
 63 
 64     _midPoint:null,
 65     _barChangeRate:null,
 66     _reverseDirection:false,
 67 
 68     /**
 69      *    Midpoint is used to modify the progress start position.
 70      *    If you're using radials type then the midpoint changes the center point
 71      *    If you're using bar type the the midpoint changes the bar growth
 72      *        it expands from the center but clamps to the sprites edge so:
 73      *        you want a left to right then set the midpoint all the way to cc.p(0,y)
 74      *        you want a right to left then set the midpoint all the way to cc.p(1,y)
 75      *        you want a bottom to top then set the midpoint all the way to cc.p(x,0)
 76      *        you want a top to bottom then set the midpoint all the way to cc.p(x,1)
 77      *  @return {cc.Point}
 78      */
 79     getMidpoint:function () {
 80         return cc.p(this._midPoint.x, this._midPoint);
 81     },
 82 
 83     /**
 84      * Midpoint setter
 85      * @param {cc.Point} mpoint
 86      */
 87     setMidpoint:function (mpoint) {
 88         this._midPoint = cc.pClamp(mpoint, cc.p(0, 0), cc.p(1, 1));
 89     },
 90 
 91     /**
 92      *    This allows the bar type to move the component at a specific rate
 93      *    Set the component to 0 to make sure it stays at 100%.
 94      *    For example you want a left to right bar but not have the height stay 100%
 95      *    Set the rate to be cc.p(0,1); and set the midpoint to = cc.p(0,.5f);
 96      *  @return {cc.Point}
 97      */
 98     getBarChangeRate:function () {
 99         return cc.p(this._barChangeRate.x, this._barChangeRate.y);
100     },
101 
102     /**
103      * @param {cc.Point} barChangeRate
104      */
105     setBarChangeRate:function (barChangeRate) {
106         this._barChangeRate = cc.pClamp(barChangeRate, cc.p(0, 0), cc.p(1, 1));
107     },
108 
109     /**
110      *  Change the percentage to change progress
111      * @return {cc.PROGRESS_TIMER_TYPE_RADIAL|cc.PROGRESS_TIMER_TYPE_BAR}
112      */
113     getType:function () {
114         return this._type;
115     },
116 
117     /**
118      * Percentages are from 0 to 100
119      * @return {Number}
120      */
121     getPercentage:function () {
122         return this._percentage;
123     },
124 
125     /**
126      * The image to show the progress percentage, retain
127      * @return {cc.Sprite}
128      */
129     getSprite:function () {
130         return this._sprite;
131     },
132 
133     /**
134      * from 0-100
135      * @param {Number} percentage
136      */
137     setPercentage:function (percentage) {
138         if (this._percentage != percentage) {
139             this._percentage = cc.clampf(percentage, 0, 100);
140             this._updateProgress();
141         }
142     },
143 
144     setOpacityModifyRGB:function (bValue) {
145     },
146 
147     isOpacityModifyRGB:function () {
148         return false;
149     },
150 
151     isReverseDirection:function () {
152         return this._reverseDirection;
153     },
154 
155     _boundaryTexCoord:function (index) {
156         if (index < cc.PROGRESS_TEXTURE_COORDS_COUNT) {
157             var locProTextCoords = cc.PROGRESS_TEXTURE_COORDS;
158             if (this._reverseDirection)
159                 return cc.p((locProTextCoords >> (7 - (index << 1))) & 1, (locProTextCoords >> (7 - ((index << 1) + 1))) & 1);
160             else
161                 return cc.p((locProTextCoords >> ((index << 1) + 1)) & 1, (locProTextCoords >> (index << 1)) & 1);
162         }
163         return cc.PointZero();
164     },
165 
166     _origin:null,
167     _startAngle:270,
168     _endAngle:270,
169     _radius:0,
170     _counterClockWise:false,
171     _barRect:null,
172 
173     _vertexDataCount:0,
174     _vertexData:null,
175     _vertexArrayBuffer:null,
176     _vertexWebGLBuffer:null,
177     _vertexDataDirty:false,
178 
179     ctor: null,
180 
181     _ctorForCanvas: function () {
182         cc.NodeRGBA.prototype.ctor.call(this);
183 
184         this._type = cc.PROGRESS_TIMER_TYPE_RADIAL;
185         this._percentage = 0.0;
186         this._midPoint = cc.p(0, 0);
187         this._barChangeRate = cc.p(0, 0);
188         this._reverseDirection = false;
189 
190         this._sprite = null;
191 
192         this._origin = cc.PointZero();
193         this._startAngle = 270;
194         this._endAngle = 270;
195         this._radius = 0;
196         this._counterClockWise = false;
197         this._barRect = cc.RectZero();
198     },
199 
200     _ctorForWebGL: function () {
201         cc.NodeRGBA.prototype.ctor.call(this);
202         this._type = cc.PROGRESS_TIMER_TYPE_RADIAL;
203         this._percentage = 0.0;
204         this._midPoint = cc.p(0, 0);
205         this._barChangeRate = cc.p(0, 0);
206         this._reverseDirection = false;
207 
208         this._sprite = null;
209 
210         this._vertexWebGLBuffer = cc.renderContext.createBuffer();
211         this._vertexDataCount = 0;
212         this._vertexData = null;
213         this._vertexArrayBuffer = null;
214         this._vertexDataDirty = false;
215     },
216 
217     /**
218      * set color of sprite
219      * @param {cc.Color3B} color
220      */
221     setColor:function (color) {
222         this._sprite.setColor(color);
223     },
224 
225     /**
226      * Opacity
227      * @param {Number} opacity
228      */
229     setOpacity:function (opacity) {
230         this._sprite.setOpacity(opacity);
231     },
232 
233     /**
234      * return color of sprite
235      * @return {cc.Color3B}
236      */
237     getColor:function () {
238         return this._sprite.getColor();
239     },
240 
241     /**
242      * return Opacity of sprite
243      * @return {Number}
244      */
245     getOpacity:function () {
246         return this._sprite.getOpacity();
247     },
248 
249     /**
250      * @param {Boolean} reverse
251      */
252     setReverseProgress:null,
253 
254     _setReverseProgressForCanvas:function (reverse) {
255         if (this._reverseDirection !== reverse)
256             this._reverseDirection = reverse;
257     },
258 
259     _setReverseProgressForWebGL:function (reverse) {
260         if (this._reverseDirection !== reverse) {
261             this._reverseDirection = reverse;
262 
263             //    release all previous information
264             this._vertexData = null;
265             this._vertexArrayBuffer = null;
266             this._vertexDataCount = 0;
267         }
268     },
269 
270     /**
271      * @param {cc.Sprite} sprite
272      */
273     setSprite:null,
274 
275     _setSpriteForCanvas:function (sprite) {
276         if (this._sprite != sprite) {
277             this._sprite = sprite;
278             this.setContentSize(this._sprite.getContentSize());
279         }
280     },
281 
282     _setSpriteForWebGL:function (sprite) {
283         if (sprite && this._sprite != sprite) {
284             this._sprite = sprite;
285             this.setContentSize(sprite.getContentSize());
286 
287             //	Everytime we set a new sprite, we free the current vertex data
288             if (this._vertexData) {
289                 this._vertexData = null;
290                 this._vertexArrayBuffer = null;
291                 this._vertexDataCount = 0;
292             }
293         }
294     },
295 
296     /**
297      * set Progress type of cc.ProgressTimer
298      * @param {cc.PROGRESS_TIMER_TYPE_RADIAL|cc.PROGRESS_TIMER_TYPE_BAR} type
299      */
300     setType:null,
301 
302     _setTypeForCanvas:function (type) {
303         if (type !== this._type)
304             this._type = type;
305     },
306 
307     _setTypeForWebGL:function (type) {
308         if (type !== this._type) {
309             //	release all previous information
310             if (this._vertexData) {
311                 this._vertexData = null;
312                 this._vertexArrayBuffer = null;
313                 this._vertexDataCount = 0;
314             }
315             this._type = type;
316         }
317     },
318 
319     /**
320      * Reverse Progress setter
321      * @param {Boolean} reverse
322      */
323     setReverseDirection: null,
324 
325     _setReverseDirectionForCanvas: function (reverse) {
326         if (this._reverseDirection !== reverse)
327             this._reverseDirection = reverse;
328     },
329 
330     _setReverseDirectionForWebGL: function (reverse) {
331         if (this._reverseDirection !== reverse) {
332             this._reverseDirection = reverse;
333             //release all previous information
334             this._vertexData = null;
335             this._vertexArrayBuffer = null;
336             this._vertexDataCount = 0;
337         }
338     },
339 
340     /**
341      * @param {cc.Point} alpha
342      * @return {cc.Vertex2F | Object} the vertex position from the texture coordinate
343      * @private
344      */
345     _textureCoordFromAlphaPoint:function (alpha) {
346         var locSprite = this._sprite;
347         if (!locSprite) {
348             return {u:0, v:0}; //new cc.Tex2F(0, 0);
349         }
350         var quad = locSprite.getQuad();
351         var min = cc.p(quad.bl.texCoords.u, quad.bl.texCoords.v);
352         var max = cc.p(quad.tr.texCoords.u, quad.tr.texCoords.v);
353 
354         //  Fix bug #1303 so that progress timer handles sprite frame texture rotation
355         if (locSprite.isTextureRectRotated()) {
356             var temp = alpha.x;
357             alpha.x = alpha.y;
358             alpha.y = temp;
359         }
360         return {u: min.x * (1 - alpha.x) + max.x * alpha.x, v: min.y * (1 - alpha.y) + max.y * alpha.y};
361     },
362 
363     _vertexFromAlphaPoint:function (alpha) {
364         if (!this._sprite) {
365             return {x: 0, y: 0};
366         }
367         var quad = this._sprite.getQuad();
368         var min = cc.p(quad.bl.vertices.x, quad.bl.vertices.y);
369         var max = cc.p(quad.tr.vertices.x, quad.tr.vertices.y);
370         return {x: min.x * (1 - alpha.x) + max.x * alpha.x, y: min.y * (1 - alpha.y) + max.y * alpha.y};
371     },
372 
373     /**
374      * Initializes a progress timer with the sprite as the shape the timer goes through
375      * @param {cc.Sprite} sprite
376      * @return {Boolean}
377      */
378     initWithSprite:null,
379 
380     _initWithSpriteForCanvas:function (sprite) {
381         this.setPercentage(0);
382         this.setAnchorPoint(cc.p(0.5, 0.5));
383 
384         this._type = cc.PROGRESS_TIMER_TYPE_RADIAL;
385         this._reverseDirection = false;
386         this.setMidpoint(cc.p(0.5, 0.5));
387         this.setBarChangeRate(cc.p(1, 1));
388         this.setSprite(sprite);
389 
390         return true;
391     },
392 
393     _initWithSpriteForWebGL:function (sprite) {
394         this.setPercentage(0);
395         this._vertexData = null;
396         this._vertexArrayBuffer = null;
397         this._vertexDataCount = 0;
398         this.setAnchorPoint(cc.p(0.5, 0.5));
399 
400         this._type = cc.PROGRESS_TIMER_TYPE_RADIAL;
401         this._reverseDirection = false;
402         this.setMidpoint(cc.p(0.5, 0.5));
403         this.setBarChangeRate(cc.p(1, 1));
404         this.setSprite(sprite);
405 
406         //shader program
407         this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.SHADER_POSITION_TEXTURECOLOR));
408         return true;
409     },
410 
411     /**
412      * stuff gets drawn here
413      * @param {CanvasRenderingContext2D} ctx
414      */
415     draw:null,
416 
417     _drawForCanvas:function (ctx) {
418         var context = ctx || cc.renderContext;
419 
420         var locSprite = this._sprite;
421         if (locSprite._isLighterMode)
422             context.globalCompositeOperation = 'lighter';
423 
424         var locEGL_ScaleX = cc.EGLView.getInstance().getScaleX(), locEGL_ScaleY = cc.EGLView.getInstance().getScaleY();
425 
426         context.globalAlpha = locSprite._displayedOpacity / 255;
427         var locRect = locSprite._rect, locContentSize = locSprite._contentSize, locOffsetPosition = locSprite._offsetPosition, locDrawSizeCanvas = locSprite._drawSize_Canvas;
428         var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height, locTextureCoord = locSprite._textureRect_Canvas;
429         locDrawSizeCanvas.width = locRect.width * locEGL_ScaleX;
430         locDrawSizeCanvas.height = locRect.height * locEGL_ScaleY;
431 
432         context.save();
433         if (locSprite._flippedX) {
434             flipXOffset = -locOffsetPosition.x - locRect.width;
435             context.scale(-1, 1);
436         }
437         if (locSprite._flippedY) {
438             flipYOffset = locOffsetPosition.y;
439             context.scale(1, -1);
440         }
441 
442         flipXOffset *= locEGL_ScaleX;
443         flipYOffset *= locEGL_ScaleY;
444 
445         //clip
446         if (this._type == cc.PROGRESS_TIMER_TYPE_BAR) {
447             var locBarRect = this._barRect;
448             context.beginPath();
449             context.rect(locBarRect.x * locEGL_ScaleX, locBarRect.y * locEGL_ScaleY, locBarRect.width * locEGL_ScaleX, locBarRect.height * locEGL_ScaleY);
450             context.clip();
451             context.closePath();
452         } else if (this._type == cc.PROGRESS_TIMER_TYPE_RADIAL) {
453             var locOriginX = this._origin.x * locEGL_ScaleX;
454             var locOriginY = this._origin.y * locEGL_ScaleY;
455             context.beginPath();
456             context.arc(locOriginX, locOriginY, this._radius * locEGL_ScaleY, (Math.PI / 180) * this._startAngle, (Math.PI / 180) * this._endAngle, this._counterClockWise);
457             context.lineTo(locOriginX, locOriginY);
458             context.clip();
459             context.closePath();
460         }
461 
462         //draw sprite
463         if (locSprite._texture && locRect.width > 0) {
464             var image = locSprite._texture.getHtmlElementObj();
465             if (this._colorized) {
466                 context.drawImage(image,
467                     0, 0, locTextureCoord.width, locTextureCoord.height,
468                     flipXOffset, flipYOffset, locDrawSizeCanvas.width, locDrawSizeCanvas.height);
469             } else {
470                 context.drawImage(image,
471                     locTextureCoord.x, locTextureCoord.y, locTextureCoord.width,  locTextureCoord.height,
472                     flipXOffset, flipYOffset, locDrawSizeCanvas.width , locDrawSizeCanvas.height);
473             }
474         } else if (locContentSize.width !== 0) {
475             var curColor = this.getColor();
476             context.fillStyle = "rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)";
477             context.fillRect(flipXOffset, flipYOffset, locContentSize.width * locEGL_ScaleX, locContentSize.height * locEGL_ScaleY);
478         }
479 
480         context.restore();
481         cc.INCREMENT_GL_DRAWS(1);
482     },
483 
484     _drawForWebGL:function (ctx) {
485         var context = ctx || cc.renderContext;
486         if (!this._vertexData || !this._sprite)
487             return;
488 
489         cc.NODE_DRAW_SETUP(this);
490 
491         var blendFunc = this._sprite.getBlendFunc();
492         cc.glBlendFunc(blendFunc.src, blendFunc.dst);
493         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSCOLORTEX);
494 
495         if (this._sprite.getTexture())
496             cc.glBindTexture2D(this._sprite.getTexture());
497         else
498             cc.glBindTexture2D(null);
499 
500         context.bindBuffer(context.ARRAY_BUFFER, this._vertexWebGLBuffer);
501         if(this._vertexDataDirty){
502             context.bufferData(context.ARRAY_BUFFER, this._vertexArrayBuffer, context.DYNAMIC_DRAW);
503             this._vertexDataDirty = false;
504         }
505         var locVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
506         context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, context.FLOAT, false, locVertexDataLen, 0);
507         context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, locVertexDataLen, 8);
508         context.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, context.FLOAT, false, locVertexDataLen, 12);
509 
510         if (this._type === cc.PROGRESS_TIMER_TYPE_RADIAL)
511             context.drawArrays(context.TRIANGLE_FAN, 0, this._vertexDataCount);
512         else if (this._type == cc.PROGRESS_TIMER_TYPE_BAR) {
513             if (!this._reverseDirection)
514                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount);
515             else {
516                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount / 2);
517                 context.drawArrays(context.TRIANGLE_STRIP, 4, this._vertexDataCount / 2);
518                 // 2 draw calls
519                 cc.g_NumberOfDraws++;
520             }
521         }
522         cc.g_NumberOfDraws++;
523     },
524 
525     /**
526      * <p>
527      *    Update does the work of mapping the texture onto the triangles            <br/>
528      *    It now doesn't occur the cost of free/alloc data every update cycle.      <br/>
529      *    It also only changes the percentage point but no other points if they have not been modified.       <br/>
530      *                                                                              <br/>
531      *    It now deals with flipped texture. If you run into this problem, just use the                       <br/>
532      *    sprite property and enable the methods flipX, flipY.                      <br/>
533      * </p>
534      * @private
535      */
536     _updateRadial:function () {
537         if (!this._sprite)
538             return;
539 
540         var i, locMidPoint = this._midPoint;
541         var alpha = this._percentage / 100;
542         var angle = 2 * (cc.PI) * ( this._reverseDirection ? alpha : 1.0 - alpha);
543 
544         //    We find the vector to do a hit detection based on the percentage
545         //    We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
546         //    from that by the progress angle around the m_tMidpoint pivot
547         var topMid = cc.p(locMidPoint.x, 1);
548         var percentagePt = cc.pRotateByAngle(topMid, locMidPoint, angle);
549 
550         var index = 0;
551         var hit;
552 
553         if (alpha == 0) {
554             //    More efficient since we don't always need to check intersection
555             //    If the alpha is zero then the hit point is top mid and the index is 0.
556             hit = topMid;
557             index = 0;
558         } else if (alpha == 1) {
559             //    More efficient since we don't always need to check intersection
560             //    If the alpha is one then the hit point is top mid and the index is 4.
561             hit = topMid;
562             index = 4;
563         } else {
564             //    We run a for loop checking the edges of the texture to find the
565             //    intersection point
566             //    We loop through five points since the top is split in half
567 
568             var min_t = cc.FLT_MAX;
569             var locProTextCoordsCount = cc.PROGRESS_TEXTURE_COORDS_COUNT;
570             for (i = 0; i <= locProTextCoordsCount; ++i) {
571                 var pIndex = (i + (locProTextCoordsCount - 1)) % locProTextCoordsCount;
572 
573                 var edgePtA = this._boundaryTexCoord(i % locProTextCoordsCount);
574                 var edgePtB = this._boundaryTexCoord(pIndex);
575 
576                 //    Remember that the top edge is split in half for the 12 o'clock position
577                 //    Let's deal with that here by finding the correct endpoints
578                 if (i == 0)
579                     edgePtB = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
580                 else if (i == 4)
581                     edgePtA = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
582 
583                 // retPoint are returned by ccpLineIntersect
584                 var retPoint = cc.p(0, 0);
585                 if (cc.pLineIntersect(edgePtA, edgePtB, locMidPoint, percentagePt, retPoint)) {
586                     //    Since our hit test is on rays we have to deal with the top edge
587                     //    being in split in half so we have to test as a segment
588                     if ((i == 0 || i == 4)) {
589                         //    s represents the point between edgePtA--edgePtB
590                         if (!(0 <= retPoint.x && retPoint.x <= 1))
591                             continue;
592                     }
593                     //    As long as our t isn't negative we are at least finding a
594                     //    correct hitpoint from m_tMidpoint to percentagePt.
595                     if (retPoint.y >= 0) {
596                         //    Because the percentage line and all the texture edges are
597                         //    rays we should only account for the shortest intersection
598                         if (retPoint.y < min_t) {
599                             min_t = retPoint.y;
600                             index = i;
601                         }
602                     }
603                 }
604             }
605 
606             //    Now that we have the minimum magnitude we can use that to find our intersection
607             hit = cc.pAdd(locMidPoint, cc.pMult(cc.pSub(percentagePt, locMidPoint), min_t));
608         }
609 
610         //    The size of the vertex data is the index from the hitpoint
611         //    the 3 is for the m_tMidpoint, 12 o'clock point and hitpoint position.
612         var sameIndexCount = true;
613         if (this._vertexDataCount != index + 3) {
614             sameIndexCount = false;
615             this._vertexData = null;
616             this._vertexArrayBuffer = null;
617             this._vertexDataCount = 0;
618         }
619 
620         if (!this._vertexData) {
621             this._vertexDataCount = index + 3;
622             var locCount = this._vertexDataCount, vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
623             this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
624             var locData = [];
625             for (i = 0; i < locCount; i++)
626                 locData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
627 
628             this._vertexData = locData;
629             if(!this._vertexData){
630                 cc.log( "cc.ProgressTimer._updateRadial() : Not enough memory");
631                 return;
632             }
633         }
634 
635         var locVertexData = this._vertexData;
636         if (!sameIndexCount) {
637             //    First we populate the array with the m_tMidpoint, then all
638             //    vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
639             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(locMidPoint);
640             locVertexData[0].vertices = this._vertexFromAlphaPoint(locMidPoint);
641 
642             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(topMid);
643             locVertexData[1].vertices = this._vertexFromAlphaPoint(topMid);
644 
645             for (i = 0; i < index; i++) {
646                 var alphaPoint = this._boundaryTexCoord(i);
647                 locVertexData[i + 2].texCoords = this._textureCoordFromAlphaPoint(alphaPoint);
648                 locVertexData[i + 2].vertices = this._vertexFromAlphaPoint(alphaPoint);
649             }
650         }
651 
652         //    hitpoint will go last
653         locVertexData[this._vertexDataCount - 1].texCoords = this._textureCoordFromAlphaPoint(hit);
654         locVertexData[this._vertexDataCount - 1].vertices = this._vertexFromAlphaPoint(hit);
655     },
656 
657     /**
658      * <p>
659      *    Update does the work of mapping the texture onto the triangles for the bar                            <br/>
660      *    It now doesn't occur the cost of free/alloc data every update cycle.                                  <br/>
661      *    It also only changes the percentage point but no other points if they have not been modified.         <br/>
662      *                                                                                                          <br/>
663      *    It now deals with flipped texture. If you run into this problem, just use the                         <br/>
664      *    sprite property and enable the methods flipX, flipY.                                                  <br/>
665      * </p>
666      * @private
667      */
668     _updateBar:function () {
669         if (!this._sprite)
670             return;
671 
672         var i;
673         var alpha = this._percentage / 100.0;
674         var locBarChangeRate = this._barChangeRate;
675         var alphaOffset = cc.pMult(cc.p((1.0 - locBarChangeRate.x) + alpha * locBarChangeRate.x,
676             (1.0 - locBarChangeRate.y) + alpha * locBarChangeRate.y), 0.5);
677         var min = cc.pSub(this._midPoint, alphaOffset);
678         var max = cc.pAdd(this._midPoint, alphaOffset);
679 
680         if (min.x < 0) {
681             max.x += -min.x;
682             min.x = 0;
683         }
684 
685         if (max.x > 1) {
686             min.x -= max.x - 1;
687             max.x = 1;
688         }
689 
690         if (min.y < 0) {
691             max.y += -min.y;
692             min.y = 0;
693         }
694 
695         if (max.y > 1) {
696             min.y -= max.y - 1;
697             max.y = 1;
698         }
699 
700         var locVertexData;
701         if (!this._reverseDirection) {
702             if (!this._vertexData) {
703                 this._vertexDataCount = 4;
704                 var vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, locCount = 4;
705                 this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
706                 this._vertexData = [];
707                 for (i = 0; i < locCount; i++)
708                     this._vertexData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
709             }
710 
711             locVertexData = this._vertexData;
712             //    TOPLEFT
713             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
714             locVertexData[0].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
715 
716             //    BOTLEFT
717             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
718             locVertexData[1].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
719 
720             //    TOPRIGHT
721             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
722             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
723 
724             //    BOTRIGHT
725             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
726             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
727         } else {
728             if (!this._vertexData) {
729                 this._vertexDataCount = 8;
730                 var rVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, rLocCount = 8;
731                 this._vertexArrayBuffer = new ArrayBuffer(rLocCount * rVertexDataLen);
732                 var rTempData = [];
733                 for (i = 0; i < rLocCount; i++)
734                     rTempData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * rVertexDataLen);
735                 //    TOPLEFT 1
736                 rTempData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 1));
737                 rTempData[0].vertices = this._vertexFromAlphaPoint(cc.p(0, 1));
738 
739                 //    BOTLEFT 1
740                 rTempData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 0));
741                 rTempData[1].vertices = this._vertexFromAlphaPoint(cc.p(0, 0));
742 
743                 //    TOPRIGHT 2
744                 rTempData[6].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 1));
745                 rTempData[6].vertices = this._vertexFromAlphaPoint(cc.p(1, 1));
746 
747                 //    BOTRIGHT 2
748                 rTempData[7].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 0));
749                 rTempData[7].vertices = this._vertexFromAlphaPoint(cc.p(1, 0));
750 
751                 this._vertexData = rTempData;
752             }
753 
754             locVertexData = this._vertexData;
755             //    TOPRIGHT 1
756             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
757             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
758 
759             //    BOTRIGHT 1
760             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
761             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
762 
763             //    TOPLEFT 2
764             locVertexData[4].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
765             locVertexData[4].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
766 
767             //    BOTLEFT 2
768             locVertexData[5].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
769             locVertexData[5].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
770         }
771     },
772 
773     _updateColor:function () {
774         if (!this._sprite || !this._vertexData)
775             return;
776 
777         var sc = this._sprite.getQuad().tl.colors;
778         var locVertexData = this._vertexData;
779         for (var i = 0, len = this._vertexDataCount; i < len; ++i)
780             locVertexData[i].colors = sc;
781         this._vertexDataDirty = true;
782     },
783 
784     _updateProgress:null,
785 
786     _updateProgressForCanvas:function () {
787         var locSprite = this._sprite;
788         var spriteSize = locSprite.getContentSize();
789         var locMidPoint = this._midPoint;
790 
791         if (this._type == cc.PROGRESS_TIMER_TYPE_RADIAL) {
792             this._radius = Math.round(Math.sqrt(spriteSize.width * spriteSize.width + spriteSize.height * spriteSize.height));
793             var locStartAngle = 270;
794             var locEndAngle = 270;
795             var locCounterClockWise = false;
796             var locOrigin = this._origin;
797 
798             locOrigin.x = spriteSize.width * locMidPoint.x;
799             locOrigin.y = -spriteSize.height * locMidPoint.y;
800 
801             if (this._reverseDirection) {
802                 locStartAngle = 270 - 3.6 * this._percentage;
803             } else {
804                 locEndAngle = 270 + 3.6 * this._percentage;
805             }
806 
807             if (locSprite._flippedX) {
808                 locOrigin.x -= spriteSize.width * (this._midPoint.x * 2);
809                 locStartAngle= -locStartAngle;
810                 locEndAngle= -locEndAngle;
811                 locStartAngle -= 180;
812                 locEndAngle -= 180;
813                 locCounterClockWise = !locCounterClockWise;
814             }
815             if (locSprite._flippedY) {
816                 locOrigin.y+=spriteSize.height*(this._midPoint.y*2);
817                 locCounterClockWise = !locCounterClockWise;
818                 locStartAngle= -locStartAngle;
819                 locEndAngle= -locEndAngle;
820             }
821 
822             this._startAngle = locStartAngle;
823             this._endAngle = locEndAngle;
824             this._counterClockWise = locCounterClockWise;
825         } else {
826             var locBarChangeRate = this._barChangeRate;
827             var percentageF = this._percentage / 100;
828             var locBarRect = this._barRect;
829 
830             var drawedSize = cc.size((spriteSize.width * (1 - locBarChangeRate.x)), (spriteSize.height * (1 - locBarChangeRate.y)));
831             var drawingSize = cc.size((spriteSize.width - drawedSize.width) * percentageF, (spriteSize.height - drawedSize.height) * percentageF);
832             var currentDrawSize = cc.size(drawedSize.width + drawingSize.width, drawedSize.height + drawingSize.height);
833 
834             var startPoint = cc.p(spriteSize.width * locMidPoint.x, spriteSize.height * locMidPoint.y);
835 
836             var needToLeft = startPoint.x - currentDrawSize.width / 2;
837             if (locMidPoint.x > 0.5) {
838                 if (currentDrawSize.width / 2 >= spriteSize.width - startPoint.x) {
839                     needToLeft = spriteSize.width - currentDrawSize.width;
840                 }
841             }
842 
843             var needToTop = startPoint.y - currentDrawSize.height / 2;
844             if (locMidPoint.y > 0.5) {
845                 if (currentDrawSize.height / 2 >= spriteSize.height - startPoint.y) {
846                     needToTop = spriteSize.height - currentDrawSize.height;
847                 }
848             }
849 
850             //left pos
851             locBarRect.x = 0;
852             var flipXNeed = 1;
853             if (locSprite._flippedX) {
854                 locBarRect.x -= currentDrawSize.width;
855                 flipXNeed = -1;
856             }
857 
858             if (needToLeft > 0)
859                 locBarRect.x += needToLeft * flipXNeed;
860 
861             //right pos
862             locBarRect.y = 0;
863             var flipYNeed = 1;
864             if (locSprite._flippedY) {
865                 locBarRect.y += currentDrawSize.height;
866                 flipYNeed = -1;
867             }
868 
869             if (needToTop > 0)
870                 locBarRect.y -= needToTop * flipYNeed;
871 
872             //clip width and clip height
873             locBarRect.width = currentDrawSize.width;
874             locBarRect.height = -currentDrawSize.height;
875         }
876     },
877 
878     _updateProgressForWebGL:function () {
879         var locType = this._type;
880         if(locType === cc.PROGRESS_TIMER_TYPE_RADIAL)
881             this._updateRadial();
882         else if(locType === cc.PROGRESS_TIMER_TYPE_BAR)
883             this._updateBar();
884         this._updateColor();
885         this._vertexDataDirty = true;
886     }
887 });
888 
889 if(cc.Browser.supportWebGL) {
890     cc.ProgressTimer.prototype.ctor = cc.ProgressTimer.prototype._ctorForWebGL;
891     cc.ProgressTimer.prototype.setReverseProgress = cc.ProgressTimer.prototype._setReverseProgressForWebGL;
892     cc.ProgressTimer.prototype.setSprite = cc.ProgressTimer.prototype._setSpriteForWebGL;
893     cc.ProgressTimer.prototype.setType = cc.ProgressTimer.prototype._setTypeForWebGL;
894     cc.ProgressTimer.prototype.setReverseDirection = cc.ProgressTimer.prototype._setReverseDirectionForWebGL;
895     cc.ProgressTimer.prototype.initWithSprite = cc.ProgressTimer.prototype._initWithSpriteForWebGL;
896     cc.ProgressTimer.prototype.draw = cc.ProgressTimer.prototype._drawForWebGL;
897     cc.ProgressTimer.prototype._updateProgress = cc.ProgressTimer.prototype._updateProgressForWebGL;
898 } else {
899     cc.ProgressTimer.prototype.ctor = cc.ProgressTimer.prototype._ctorForCanvas;
900     cc.ProgressTimer.prototype.setReverseProgress = cc.ProgressTimer.prototype._setReverseProgressForCanvas;
901     cc.ProgressTimer.prototype.setSprite = cc.ProgressTimer.prototype._setSpriteForCanvas;
902     cc.ProgressTimer.prototype.setType = cc.ProgressTimer.prototype._setTypeForCanvas;
903     cc.ProgressTimer.prototype.setReverseDirection = cc.ProgressTimer.prototype._setReverseDirectionForCanvas;
904     cc.ProgressTimer.prototype.initWithSprite = cc.ProgressTimer.prototype._initWithSpriteForCanvas;
905     cc.ProgressTimer.prototype.draw = cc.ProgressTimer.prototype._drawForCanvas;
906     cc.ProgressTimer.prototype._updateProgress = cc.ProgressTimer.prototype._updateProgressForCanvas;
907 }
908 
909 /**
910  * create a progress timer object with image file name that renders the inner sprite according to the percentage
911  * @param {cc.Sprite} sprite
912  * @return {cc.ProgressTimer}
913  * @example
914  * // Example
915  * var progress = cc.ProgressTimer.create('progress.png')
916  */
917 cc.ProgressTimer.create = function (sprite) {
918     var progressTimer = new cc.ProgressTimer();
919     if (progressTimer.initWithSprite(sprite))
920         return progressTimer;
921     return null;
922 };
923 
924 
925