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 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 this._barChangeRate;
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         context.globalAlpha = locSprite._displayedOpacity / 255;
422         var locRect = locSprite._rect,  locOffsetPosition = locSprite._offsetPosition;
423         var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height;
424 
425         context.save();
426         if (locSprite._flippedX) {
427             flipXOffset = -locOffsetPosition.x - locRect.width;
428             context.scale(-1, 1);
429         }
430         if (locSprite._flippedY) {
431             flipYOffset = locOffsetPosition.y;
432             context.scale(1, -1);
433         }
434 
435         //clip
436         if (this._type == cc.PROGRESS_TIMER_TYPE_BAR) {
437             var locBarRect = this._barRect;
438             context.beginPath();
439             context.rect(locBarRect.x,locBarRect.y,locBarRect.width,locBarRect.height);
440             context.clip();
441             context.closePath();
442         }else if(this._type == cc.PROGRESS_TIMER_TYPE_RADIAL){
443             var locOrigin = this._origin;
444             context.beginPath();
445             context.arc(locOrigin.x, locOrigin.y, this._radius, (Math.PI / 180) * this._startAngle, (Math.PI / 180) * this._endAngle, this._counterClockWise);
446             context.lineTo(locOrigin.x, locOrigin.y);
447             context.clip();
448             context.closePath();
449         }
450 
451         //draw sprite
452         if (locSprite._texture && locRect.width > 0) {
453             var image = locSprite._texture.getHtmlElementObj();
454             if (locSprite._colorized) {
455                 context.drawImage(image,
456                     0, 0, locRect.width, locRect.height,
457                     flipXOffset, flipYOffset, locRect.width, locRect.height);
458             } else {
459                 context.drawImage(image,
460                     locRect.x, locRect.y, locRect.width, locRect.height,
461                     flipXOffset, flipYOffset, locRect.width, locRect.height);
462             }
463         }
464 
465         context.restore();
466         cc.INCREMENT_GL_DRAWS(1);
467     },
468 
469     _drawForWebGL:function (ctx) {
470         var context = ctx || cc.renderContext;
471         if (!this._vertexData || !this._sprite)
472             return;
473 
474         cc.NODE_DRAW_SETUP(this);
475 
476         var blendFunc = this._sprite.getBlendFunc();
477         cc.glBlendFunc(blendFunc.src, blendFunc.dst);
478         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSCOLORTEX);
479 
480         if (this._sprite.getTexture())
481             cc.glBindTexture2D(this._sprite.getTexture());
482         else
483             cc.glBindTexture2D(null);
484 
485         context.bindBuffer(context.ARRAY_BUFFER, this._vertexWebGLBuffer);
486         if(this._vertexDataDirty){
487             context.bufferData(context.ARRAY_BUFFER, this._vertexArrayBuffer, context.DYNAMIC_DRAW);
488             this._vertexDataDirty = false;
489         }
490         var locVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
491         context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, context.FLOAT, false, locVertexDataLen, 0);
492         context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, locVertexDataLen, 8);
493         context.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, context.FLOAT, false, locVertexDataLen, 12);
494 
495         if (this._type === cc.PROGRESS_TIMER_TYPE_RADIAL)
496             context.drawArrays(context.TRIANGLE_FAN, 0, this._vertexDataCount);
497         else if (this._type == cc.PROGRESS_TIMER_TYPE_BAR) {
498             if (!this._reverseDirection)
499                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount);
500             else {
501                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount / 2);
502                 context.drawArrays(context.TRIANGLE_STRIP, 4, this._vertexDataCount / 2);
503                 // 2 draw calls
504                 cc.g_NumberOfDraws++;
505             }
506         }
507         cc.g_NumberOfDraws++;
508     },
509 
510     /**
511      * <p>
512      *    Update does the work of mapping the texture onto the triangles            <br/>
513      *    It now doesn't occur the cost of free/alloc data every update cycle.      <br/>
514      *    It also only changes the percentage point but no other points if they have not been modified.       <br/>
515      *                                                                              <br/>
516      *    It now deals with flipped texture. If you run into this problem, just use the                       <br/>
517      *    sprite property and enable the methods flipX, flipY.                      <br/>
518      * </p>
519      * @private
520      */
521     _updateRadial:function () {
522         if (!this._sprite)
523             return;
524 
525         var i, locMidPoint = this._midPoint;
526         var alpha = this._percentage / 100;
527         var angle = 2 * (cc.PI) * ( this._reverseDirection ? alpha : 1.0 - alpha);
528 
529         //    We find the vector to do a hit detection based on the percentage
530         //    We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
531         //    from that by the progress angle around the m_tMidpoint pivot
532         var topMid = cc.p(locMidPoint.x, 1);
533         var percentagePt = cc.pRotateByAngle(topMid, locMidPoint, angle);
534 
535         var index = 0;
536         var hit;
537 
538         if (alpha == 0) {
539             //    More efficient since we don't always need to check intersection
540             //    If the alpha is zero then the hit point is top mid and the index is 0.
541             hit = topMid;
542             index = 0;
543         } else if (alpha == 1) {
544             //    More efficient since we don't always need to check intersection
545             //    If the alpha is one then the hit point is top mid and the index is 4.
546             hit = topMid;
547             index = 4;
548         } else {
549             //    We run a for loop checking the edges of the texture to find the
550             //    intersection point
551             //    We loop through five points since the top is split in half
552 
553             var min_t = cc.FLT_MAX;
554             var locProTextCoordsCount = cc.PROGRESS_TEXTURE_COORDS_COUNT;
555             for (i = 0; i <= locProTextCoordsCount; ++i) {
556                 var pIndex = (i + (locProTextCoordsCount - 1)) % locProTextCoordsCount;
557 
558                 var edgePtA = this._boundaryTexCoord(i % locProTextCoordsCount);
559                 var edgePtB = this._boundaryTexCoord(pIndex);
560 
561                 //    Remember that the top edge is split in half for the 12 o'clock position
562                 //    Let's deal with that here by finding the correct endpoints
563                 if (i == 0)
564                     edgePtB = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
565                 else if (i == 4)
566                     edgePtA = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
567 
568                 // retPoint are returned by ccpLineIntersect
569                 var retPoint = cc.p(0, 0);
570                 if (cc.pLineIntersect(edgePtA, edgePtB, locMidPoint, percentagePt, retPoint)) {
571                     //    Since our hit test is on rays we have to deal with the top edge
572                     //    being in split in half so we have to test as a segment
573                     if ((i == 0 || i == 4)) {
574                         //    s represents the point between edgePtA--edgePtB
575                         if (!(0 <= retPoint.x && retPoint.x <= 1))
576                             continue;
577                     }
578                     //    As long as our t isn't negative we are at least finding a
579                     //    correct hitpoint from m_tMidpoint to percentagePt.
580                     if (retPoint.y >= 0) {
581                         //    Because the percentage line and all the texture edges are
582                         //    rays we should only account for the shortest intersection
583                         if (retPoint.y < min_t) {
584                             min_t = retPoint.y;
585                             index = i;
586                         }
587                     }
588                 }
589             }
590 
591             //    Now that we have the minimum magnitude we can use that to find our intersection
592             hit = cc.pAdd(locMidPoint, cc.pMult(cc.pSub(percentagePt, locMidPoint), min_t));
593         }
594 
595         //    The size of the vertex data is the index from the hitpoint
596         //    the 3 is for the m_tMidpoint, 12 o'clock point and hitpoint position.
597         var sameIndexCount = true;
598         if (this._vertexDataCount != index + 3) {
599             sameIndexCount = false;
600             this._vertexData = null;
601             this._vertexArrayBuffer = null;
602             this._vertexDataCount = 0;
603         }
604 
605         if (!this._vertexData) {
606             this._vertexDataCount = index + 3;
607             var locCount = this._vertexDataCount, vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
608             this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
609             var locData = [];
610             for (i = 0; i < locCount; i++)
611                 locData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
612 
613             this._vertexData = locData;
614             cc.Assert(this._vertexData, "cc.ProgressTimer. Not enough memory");
615         }
616 
617         var locVertexData = this._vertexData;
618         if (!sameIndexCount) {
619             //    First we populate the array with the m_tMidpoint, then all
620             //    vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
621             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(locMidPoint);
622             locVertexData[0].vertices = this._vertexFromAlphaPoint(locMidPoint);
623 
624             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(topMid);
625             locVertexData[1].vertices = this._vertexFromAlphaPoint(topMid);
626 
627             for (i = 0; i < index; i++) {
628                 var alphaPoint = this._boundaryTexCoord(i);
629                 locVertexData[i + 2].texCoords = this._textureCoordFromAlphaPoint(alphaPoint);
630                 locVertexData[i + 2].vertices = this._vertexFromAlphaPoint(alphaPoint);
631             }
632         }
633 
634         //    hitpoint will go last
635         locVertexData[this._vertexDataCount - 1].texCoords = this._textureCoordFromAlphaPoint(hit);
636         locVertexData[this._vertexDataCount - 1].vertices = this._vertexFromAlphaPoint(hit);
637     },
638 
639     /**
640      * <p>
641      *    Update does the work of mapping the texture onto the triangles for the bar                            <br/>
642      *    It now doesn't occur the cost of free/alloc data every update cycle.                                  <br/>
643      *    It also only changes the percentage point but no other points if they have not been modified.         <br/>
644      *                                                                                                          <br/>
645      *    It now deals with flipped texture. If you run into this problem, just use the                         <br/>
646      *    sprite property and enable the methods flipX, flipY.                                                  <br/>
647      * </p>
648      * @private
649      */
650     _updateBar:function () {
651         if (!this._sprite)
652             return;
653 
654         var i;
655         var alpha = this._percentage / 100.0;
656         var locBarChangeRate = this._barChangeRate;
657         var alphaOffset = cc.pMult(cc.p((1.0 - locBarChangeRate.x) + alpha * locBarChangeRate.x,
658             (1.0 - locBarChangeRate.y) + alpha * locBarChangeRate.y), 0.5);
659         var min = cc.pSub(this._midPoint, alphaOffset);
660         var max = cc.pAdd(this._midPoint, alphaOffset);
661 
662         if (min.x < 0) {
663             max.x += -min.x;
664             min.x = 0;
665         }
666 
667         if (max.x > 1) {
668             min.x -= max.x - 1;
669             max.x = 1;
670         }
671 
672         if (min.y < 0) {
673             max.y += -min.y;
674             min.y = 0;
675         }
676 
677         if (max.y > 1) {
678             min.y -= max.y - 1;
679             max.y = 1;
680         }
681 
682         var locVertexData;
683         if (!this._reverseDirection) {
684             if (!this._vertexData) {
685                 this._vertexDataCount = 4;
686                 var vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, locCount = 4;
687                 this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
688                 this._vertexData = [];
689                 for (i = 0; i < locCount; i++) {
690                     this._vertexData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
691                 }
692                 cc.Assert(this._vertexData, "cc.ProgressTimer. Not enough memory");
693             }
694 
695             locVertexData = this._vertexData;
696             //    TOPLEFT
697             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
698             locVertexData[0].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
699 
700             //    BOTLEFT
701             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
702             locVertexData[1].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
703 
704             //    TOPRIGHT
705             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
706             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
707 
708             //    BOTRIGHT
709             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
710             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
711         } else {
712             if (!this._vertexData) {
713                 this._vertexDataCount = 8;
714                 var rVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, rLocCount = 8;
715                 this._vertexArrayBuffer = new ArrayBuffer(rLocCount * rVertexDataLen);
716                 var rTempData = [];
717                 for (i = 0; i < rLocCount; i++)
718                     rTempData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * rVertexDataLen);
719 
720                 cc.Assert(rTempData, "cc.ProgressTimer. Not enough memory");
721                 //    TOPLEFT 1
722                 rTempData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 1));
723                 rTempData[0].vertices = this._vertexFromAlphaPoint(cc.p(0, 1));
724 
725                 //    BOTLEFT 1
726                 rTempData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 0));
727                 rTempData[1].vertices = this._vertexFromAlphaPoint(cc.p(0, 0));
728 
729                 //    TOPRIGHT 2
730                 rTempData[6].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 1));
731                 rTempData[6].vertices = this._vertexFromAlphaPoint(cc.p(1, 1));
732 
733                 //    BOTRIGHT 2
734                 rTempData[7].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 0));
735                 rTempData[7].vertices = this._vertexFromAlphaPoint(cc.p(1, 0));
736 
737                 this._vertexData = rTempData;
738             }
739 
740             locVertexData = this._vertexData;
741             //    TOPRIGHT 1
742             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
743             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
744 
745             //    BOTRIGHT 1
746             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
747             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
748 
749             //    TOPLEFT 2
750             locVertexData[4].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
751             locVertexData[4].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
752 
753             //    BOTLEFT 2
754             locVertexData[5].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
755             locVertexData[5].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
756         }
757     },
758 
759     _updateColor:function () {
760         if (!this._sprite || !this._vertexData)
761             return;
762 
763         var sc = this._sprite.getQuad().tl.colors;
764         var locVertexData = this._vertexData;
765         for (var i = 0, len = this._vertexDataCount; i < len; ++i)
766             locVertexData[i].colors = sc;
767         this._vertexDataDirty = true;
768     },
769 
770     _updateProgress:null,
771 
772     _updateProgressForCanvas:function () {
773         var locSprite = this._sprite;
774         var spriteSize = locSprite.getContentSize();
775         var locMidPoint = this._midPoint;
776 
777         if (this._type == cc.PROGRESS_TIMER_TYPE_RADIAL) {
778             this._radius = Math.round(Math.sqrt(spriteSize.width * spriteSize.width + spriteSize.height * spriteSize.height));
779             var locStartAngle = 270;
780             var locEndAngle = 270;
781             var locCounterClockWise = false;
782             var locOrigin = this._origin;
783 
784             locOrigin.x = spriteSize.width * locMidPoint.x;
785             locOrigin.y = -spriteSize.height * locMidPoint.y;
786 
787             if (this._reverseDirection) {
788                 locStartAngle = 270 - 3.6 * this._percentage;
789             } else {
790                 locEndAngle = 270 + 3.6 * this._percentage;
791             }
792 
793             if (locSprite._flippedX) {
794                 locOrigin.x -= spriteSize.width * (this._midPoint.x * 2);
795                 locStartAngle= -locStartAngle;
796                 locEndAngle= -locEndAngle;
797                 locStartAngle -= 180;
798                 locEndAngle -= 180;
799                 locCounterClockWise = !locCounterClockWise;
800             }
801             if (locSprite._flippedY) {
802                 locOrigin.y+=spriteSize.height*(this._midPoint.y*2);
803                 locCounterClockWise = !locCounterClockWise;
804                 locStartAngle= -locStartAngle;
805                 locEndAngle= -locEndAngle;
806             }
807 
808             this._startAngle = locStartAngle;
809             this._endAngle = locEndAngle;
810             this._counterClockWise = locCounterClockWise;
811         } else {
812             var locBarChangeRate = this._barChangeRate;
813             var percentageF = this._percentage / 100;
814             var locBarRect = this._barRect;
815 
816             var drawedSize = cc.size((spriteSize.width * (1 - locBarChangeRate.x)), (spriteSize.height * (1 - locBarChangeRate.y)));
817             var drawingSize = cc.size((spriteSize.width - drawedSize.width) * percentageF, (spriteSize.height - drawedSize.height) * percentageF);
818             var currentDrawSize = cc.size(drawedSize.width + drawingSize.width, drawedSize.height + drawingSize.height);
819 
820             var startPoint = cc.p(spriteSize.width * locMidPoint.x, spriteSize.height * locMidPoint.y);
821 
822             var needToLeft = startPoint.x - currentDrawSize.width / 2;
823             if (locMidPoint.x > 0.5) {
824                 if (currentDrawSize.width / 2 >= spriteSize.width - startPoint.x) {
825                     needToLeft = spriteSize.width - currentDrawSize.width;
826                 }
827             }
828 
829             var needToTop = startPoint.y - currentDrawSize.height / 2;
830             if (locMidPoint.y > 0.5) {
831                 if (currentDrawSize.height / 2 >= spriteSize.height - startPoint.y) {
832                     needToTop = spriteSize.height - currentDrawSize.height;
833                 }
834             }
835 
836             //left pos
837             locBarRect.x = 0;
838             var flipXNeed = 1;
839             if (locSprite._flippedX) {
840                 locBarRect.x -= currentDrawSize.width;
841                 flipXNeed = -1;
842             }
843 
844             if (needToLeft > 0)
845                 locBarRect.x += needToLeft * flipXNeed;
846 
847             //right pos
848             locBarRect.y = 0;
849             var flipYNeed = 1;
850             if (locSprite._flippedY) {
851                 locBarRect.y += currentDrawSize.height;
852                 flipYNeed = -1;
853             }
854 
855             if (needToTop > 0)
856                 locBarRect.y -= needToTop * flipYNeed;
857 
858             //clip width and clip height
859             locBarRect.width = currentDrawSize.width;
860             locBarRect.height = -currentDrawSize.height;
861         }
862     },
863 
864     _updateProgressForWebGL:function () {
865         var locType = this._type;
866         if(locType === cc.PROGRESS_TIMER_TYPE_RADIAL)
867             this._updateRadial();
868         else if(locType === cc.PROGRESS_TIMER_TYPE_BAR)
869             this._updateBar();
870         this._updateColor();
871         this._vertexDataDirty = true;
872     }
873 });
874 
875 if(cc.Browser.supportWebGL) {
876     cc.ProgressTimer.prototype.ctor = cc.ProgressTimer.prototype._ctorForWebGL;
877     cc.ProgressTimer.prototype.setReverseProgress = cc.ProgressTimer.prototype._setReverseProgressForWebGL;
878     cc.ProgressTimer.prototype.setSprite = cc.ProgressTimer.prototype._setSpriteForWebGL;
879     cc.ProgressTimer.prototype.setType = cc.ProgressTimer.prototype._setTypeForWebGL;
880     cc.ProgressTimer.prototype.setReverseDirection = cc.ProgressTimer.prototype._setReverseDirectionForWebGL;
881     cc.ProgressTimer.prototype.initWithSprite = cc.ProgressTimer.prototype._initWithSpriteForWebGL;
882     cc.ProgressTimer.prototype.draw = cc.ProgressTimer.prototype._drawForWebGL;
883     cc.ProgressTimer.prototype._updateProgress = cc.ProgressTimer.prototype._updateProgressForWebGL;
884 } else {
885     cc.ProgressTimer.prototype.ctor = cc.ProgressTimer.prototype._ctorForCanvas;
886     cc.ProgressTimer.prototype.setReverseProgress = cc.ProgressTimer.prototype._setReverseProgressForCanvas;
887     cc.ProgressTimer.prototype.setSprite = cc.ProgressTimer.prototype._setSpriteForCanvas;
888     cc.ProgressTimer.prototype.setType = cc.ProgressTimer.prototype._setTypeForCanvas;
889     cc.ProgressTimer.prototype.setReverseDirection = cc.ProgressTimer.prototype._setReverseDirectionForCanvas;
890     cc.ProgressTimer.prototype.initWithSprite = cc.ProgressTimer.prototype._initWithSpriteForCanvas;
891     cc.ProgressTimer.prototype.draw = cc.ProgressTimer.prototype._drawForCanvas;
892     cc.ProgressTimer.prototype._updateProgress = cc.ProgressTimer.prototype._updateProgressForCanvas;
893 }
894 
895 /**
896  * create a progress timer object with image file name that renders the inner sprite according to the percentage
897  * @param {cc.Sprite} sprite
898  * @return {cc.ProgressTimer}
899  * @example
900  * // Example
901  * var progress = cc.ProgressTimer.create('progress.png')
902  */
903 cc.ProgressTimer.create = function (sprite) {
904     var progressTimer = new cc.ProgressTimer();
905     if (progressTimer.initWithSprite(sprite))
906         return progressTimer;
907     return null;
908 };
909 
910 
911