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         this._updateColor();
224     },
225 
226     /**
227      * Opacity
228      * @param {Number} opacity
229      */
230     setOpacity:function (opacity) {
231         this._sprite.setOpacity(opacity);
232         this._updateColor();
233     },
234 
235     /**
236      * return color of sprite
237      * @return {cc.Color3B}
238      */
239     getColor:function () {
240         return this._sprite.getColor();
241     },
242 
243     /**
244      * return Opacity of sprite
245      * @return {Number}
246      */
247     getOpacity:function () {
248         return this._sprite.getOpacity();
249     },
250 
251     /**
252      * @param {Boolean} reverse
253      */
254     setReverseProgress:null,
255 
256     _setReverseProgressForCanvas:function (reverse) {
257         if (this._reverseDirection !== reverse)
258             this._reverseDirection = reverse;
259     },
260 
261     _setReverseProgressForWebGL:function (reverse) {
262         if (this._reverseDirection !== reverse) {
263             this._reverseDirection = reverse;
264 
265             //    release all previous information
266             this._vertexData = null;
267             this._vertexArrayBuffer = null;
268             this._vertexDataCount = 0;
269         }
270     },
271 
272     /**
273      * @param {cc.Sprite} sprite
274      */
275     setSprite:null,
276 
277     _setSpriteForCanvas:function (sprite) {
278         if (this._sprite != sprite) {
279             this._sprite = sprite;
280             this.setContentSize(this._sprite.getContentSize());
281         }
282     },
283 
284     _setSpriteForWebGL:function (sprite) {
285         if (sprite && this._sprite != sprite) {
286             this._sprite = sprite;
287             this.setContentSize(sprite.getContentSize());
288 
289             //	Everytime we set a new sprite, we free the current vertex data
290             if (this._vertexData) {
291                 this._vertexData = null;
292                 this._vertexArrayBuffer = null;
293                 this._vertexDataCount = 0;
294             }
295         }
296     },
297 
298     /**
299      * set Progress type of cc.ProgressTimer
300      * @param {cc.PROGRESS_TIMER_TYPE_RADIAL|cc.PROGRESS_TIMER_TYPE_BAR} type
301      */
302     setType:null,
303 
304     _setTypeForCanvas:function (type) {
305         if (type !== this._type)
306             this._type = type;
307     },
308 
309     _setTypeForWebGL:function (type) {
310         if (type !== this._type) {
311             //	release all previous information
312             if (this._vertexData) {
313                 this._vertexData = null;
314                 this._vertexArrayBuffer = null;
315                 this._vertexDataCount = 0;
316             }
317             this._type = type;
318         }
319     },
320 
321     /**
322      * Reverse Progress setter
323      * @param {Boolean} reverse
324      */
325     setReverseDirection: null,
326 
327     _setReverseDirectionForCanvas: function (reverse) {
328         if (this._reverseDirection !== reverse)
329             this._reverseDirection = reverse;
330     },
331 
332     _setReverseDirectionForWebGL: function (reverse) {
333         if (this._reverseDirection !== reverse) {
334             this._reverseDirection = reverse;
335             //release all previous information
336             this._vertexData = null;
337             this._vertexArrayBuffer = null;
338             this._vertexDataCount = 0;
339         }
340     },
341 
342     /**
343      * @param {cc.Point} alpha
344      * @return {cc.Vertex2F | Object} the vertex position from the texture coordinate
345      * @private
346      */
347     _textureCoordFromAlphaPoint:function (alpha) {
348         var locSprite = this._sprite;
349         if (!locSprite) {
350             return {u:0, v:0}; //new cc.Tex2F(0, 0);
351         }
352         var quad = locSprite.getQuad();
353         var min = cc.p(quad.bl.texCoords.u, quad.bl.texCoords.v);
354         var max = cc.p(quad.tr.texCoords.u, quad.tr.texCoords.v);
355 
356         //  Fix bug #1303 so that progress timer handles sprite frame texture rotation
357         if (locSprite.isTextureRectRotated()) {
358             var temp = alpha.x;
359             alpha.x = alpha.y;
360             alpha.y = temp;
361         }
362         return {u: min.x * (1 - alpha.x) + max.x * alpha.x, v: min.y * (1 - alpha.y) + max.y * alpha.y};
363     },
364 
365     _vertexFromAlphaPoint:function (alpha) {
366         if (!this._sprite) {
367             return {x: 0, y: 0};
368         }
369         var quad = this._sprite.getQuad();
370         var min = cc.p(quad.bl.vertices.x, quad.bl.vertices.y);
371         var max = cc.p(quad.tr.vertices.x, quad.tr.vertices.y);
372         return {x: min.x * (1 - alpha.x) + max.x * alpha.x, y: min.y * (1 - alpha.y) + max.y * alpha.y};
373     },
374 
375     /**
376      * Initializes a progress timer with the sprite as the shape the timer goes through
377      * @param {cc.Sprite} sprite
378      * @return {Boolean}
379      */
380     initWithSprite:null,
381 
382     _initWithSpriteForCanvas:function (sprite) {
383         this.setPercentage(0);
384         this.setAnchorPoint(0.5, 0.5);
385 
386         this._type = cc.PROGRESS_TIMER_TYPE_RADIAL;
387         this._reverseDirection = false;
388         this.setMidpoint(cc.p(0.5, 0.5));
389         this.setBarChangeRate(cc.p(1, 1));
390         this.setSprite(sprite);
391 
392         return true;
393     },
394 
395     _initWithSpriteForWebGL:function (sprite) {
396         this.setPercentage(0);
397         this._vertexData = null;
398         this._vertexArrayBuffer = null;
399         this._vertexDataCount = 0;
400         this.setAnchorPoint(0.5, 0.5);
401 
402         this._type = cc.PROGRESS_TIMER_TYPE_RADIAL;
403         this._reverseDirection = false;
404         this.setMidpoint(cc.p(0.5, 0.5));
405         this.setBarChangeRate(cc.p(1, 1));
406         this.setSprite(sprite);
407 
408         //shader program
409         this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.SHADER_POSITION_TEXTURECOLOR));
410         return true;
411     },
412 
413     /**
414      * stuff gets drawn here
415      * @param {CanvasRenderingContext2D} ctx
416      */
417     draw:null,
418 
419     _drawForCanvas:function (ctx) {
420         var context = ctx || cc.renderContext;
421 
422         var locSprite = this._sprite;
423         if (locSprite._isLighterMode)
424             context.globalCompositeOperation = 'lighter';
425 
426         var locEGL_ScaleX = cc.EGLView.getInstance().getScaleX(), locEGL_ScaleY = cc.EGLView.getInstance().getScaleY();
427 
428         context.globalAlpha = locSprite._displayedOpacity / 255;
429         var locRect = locSprite._rect, locContentSize = locSprite._contentSize, locOffsetPosition = locSprite._offsetPosition, locDrawSizeCanvas = locSprite._drawSize_Canvas;
430         var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height, locTextureCoord = locSprite._textureRect_Canvas;
431         locDrawSizeCanvas.width = locRect.width * locEGL_ScaleX;
432         locDrawSizeCanvas.height = locRect.height * locEGL_ScaleY;
433 
434         context.save();
435         if (locSprite._flippedX) {
436             flipXOffset = -locOffsetPosition.x - locRect.width;
437             context.scale(-1, 1);
438         }
439         if (locSprite._flippedY) {
440             flipYOffset = locOffsetPosition.y;
441             context.scale(1, -1);
442         }
443 
444         flipXOffset *= locEGL_ScaleX;
445         flipYOffset *= locEGL_ScaleY;
446 
447         //clip
448         if (this._type == cc.PROGRESS_TIMER_TYPE_BAR) {
449             var locBarRect = this._barRect;
450             context.beginPath();
451             context.rect(locBarRect.x * locEGL_ScaleX, locBarRect.y * locEGL_ScaleY, locBarRect.width * locEGL_ScaleX, locBarRect.height * locEGL_ScaleY);
452             context.clip();
453             context.closePath();
454         } else if (this._type == cc.PROGRESS_TIMER_TYPE_RADIAL) {
455             var locOriginX = this._origin.x * locEGL_ScaleX;
456             var locOriginY = this._origin.y * locEGL_ScaleY;
457             context.beginPath();
458             context.arc(locOriginX, locOriginY, this._radius * locEGL_ScaleY, (Math.PI / 180) * this._startAngle, (Math.PI / 180) * this._endAngle, this._counterClockWise);
459             context.lineTo(locOriginX, locOriginY);
460             context.clip();
461             context.closePath();
462         }
463 
464         //draw sprite
465         if (locSprite._texture && locTextureCoord.validRect) {
466             var image = locSprite._texture.getHtmlElementObj();
467             if (this._colorized) {
468                 context.drawImage(image,
469                     0, 0, locTextureCoord.width, locTextureCoord.height,
470                     flipXOffset, flipYOffset, locDrawSizeCanvas.width, locDrawSizeCanvas.height);
471             } else {
472                 context.drawImage(image,
473                     locTextureCoord.x, locTextureCoord.y, locTextureCoord.width,  locTextureCoord.height,
474                     flipXOffset, flipYOffset, locDrawSizeCanvas.width , locDrawSizeCanvas.height);
475             }
476         } else if (locContentSize.width !== 0) {
477             var curColor = this.getColor();
478             context.fillStyle = "rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)";
479             context.fillRect(flipXOffset, flipYOffset, locContentSize.width * locEGL_ScaleX, locContentSize.height * locEGL_ScaleY);
480         }
481 
482         context.restore();
483         cc.INCREMENT_GL_DRAWS(1);
484     },
485 
486     _drawForWebGL:function (ctx) {
487         var context = ctx || cc.renderContext;
488         if (!this._vertexData || !this._sprite)
489             return;
490 
491         cc.NODE_DRAW_SETUP(this);
492 
493         var blendFunc = this._sprite.getBlendFunc();
494         cc.glBlendFunc(blendFunc.src, blendFunc.dst);
495         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSCOLORTEX);
496 
497         if (this._sprite.getTexture())
498             cc.glBindTexture2D(this._sprite.getTexture());
499         else
500             cc.glBindTexture2D(null);
501 
502         context.bindBuffer(context.ARRAY_BUFFER, this._vertexWebGLBuffer);
503         if(this._vertexDataDirty){
504             context.bufferData(context.ARRAY_BUFFER, this._vertexArrayBuffer, context.DYNAMIC_DRAW);
505             this._vertexDataDirty = false;
506         }
507         var locVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
508         context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, context.FLOAT, false, locVertexDataLen, 0);
509         context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, locVertexDataLen, 8);
510         context.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, context.FLOAT, false, locVertexDataLen, 12);
511 
512         if (this._type === cc.PROGRESS_TIMER_TYPE_RADIAL)
513             context.drawArrays(context.TRIANGLE_FAN, 0, this._vertexDataCount);
514         else if (this._type == cc.PROGRESS_TIMER_TYPE_BAR) {
515             if (!this._reverseDirection)
516                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount);
517             else {
518                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount / 2);
519                 context.drawArrays(context.TRIANGLE_STRIP, 4, this._vertexDataCount / 2);
520                 // 2 draw calls
521                 cc.g_NumberOfDraws++;
522             }
523         }
524         cc.g_NumberOfDraws++;
525     },
526 
527     /**
528      * <p>
529      *    Update does the work of mapping the texture onto the triangles            <br/>
530      *    It now doesn't occur the cost of free/alloc data every update cycle.      <br/>
531      *    It also only changes the percentage point but no other points if they have not been modified.       <br/>
532      *                                                                              <br/>
533      *    It now deals with flipped texture. If you run into this problem, just use the                       <br/>
534      *    sprite property and enable the methods flipX, flipY.                      <br/>
535      * </p>
536      * @private
537      */
538     _updateRadial:function () {
539         if (!this._sprite)
540             return;
541 
542         var i, locMidPoint = this._midPoint;
543         var alpha = this._percentage / 100;
544         var angle = 2 * (cc.PI) * ( this._reverseDirection ? alpha : 1.0 - alpha);
545 
546         //    We find the vector to do a hit detection based on the percentage
547         //    We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
548         //    from that by the progress angle around the m_tMidpoint pivot
549         var topMid = cc.p(locMidPoint.x, 1);
550         var percentagePt = cc.pRotateByAngle(topMid, locMidPoint, angle);
551 
552         var index = 0;
553         var hit;
554 
555         if (alpha == 0) {
556             //    More efficient since we don't always need to check intersection
557             //    If the alpha is zero then the hit point is top mid and the index is 0.
558             hit = topMid;
559             index = 0;
560         } else if (alpha == 1) {
561             //    More efficient since we don't always need to check intersection
562             //    If the alpha is one then the hit point is top mid and the index is 4.
563             hit = topMid;
564             index = 4;
565         } else {
566             //    We run a for loop checking the edges of the texture to find the
567             //    intersection point
568             //    We loop through five points since the top is split in half
569 
570             var min_t = cc.FLT_MAX;
571             var locProTextCoordsCount = cc.PROGRESS_TEXTURE_COORDS_COUNT;
572             for (i = 0; i <= locProTextCoordsCount; ++i) {
573                 var pIndex = (i + (locProTextCoordsCount - 1)) % locProTextCoordsCount;
574 
575                 var edgePtA = this._boundaryTexCoord(i % locProTextCoordsCount);
576                 var edgePtB = this._boundaryTexCoord(pIndex);
577 
578                 //    Remember that the top edge is split in half for the 12 o'clock position
579                 //    Let's deal with that here by finding the correct endpoints
580                 if (i == 0)
581                     edgePtB = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
582                 else if (i == 4)
583                     edgePtA = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
584 
585                 // retPoint are returned by ccpLineIntersect
586                 var retPoint = cc.p(0, 0);
587                 if (cc.pLineIntersect(edgePtA, edgePtB, locMidPoint, percentagePt, retPoint)) {
588                     //    Since our hit test is on rays we have to deal with the top edge
589                     //    being in split in half so we have to test as a segment
590                     if ((i == 0 || i == 4)) {
591                         //    s represents the point between edgePtA--edgePtB
592                         if (!(0 <= retPoint.x && retPoint.x <= 1))
593                             continue;
594                     }
595                     //    As long as our t isn't negative we are at least finding a
596                     //    correct hitpoint from m_tMidpoint to percentagePt.
597                     if (retPoint.y >= 0) {
598                         //    Because the percentage line and all the texture edges are
599                         //    rays we should only account for the shortest intersection
600                         if (retPoint.y < min_t) {
601                             min_t = retPoint.y;
602                             index = i;
603                         }
604                     }
605                 }
606             }
607 
608             //    Now that we have the minimum magnitude we can use that to find our intersection
609             hit = cc.pAdd(locMidPoint, cc.pMult(cc.pSub(percentagePt, locMidPoint), min_t));
610         }
611 
612         //    The size of the vertex data is the index from the hitpoint
613         //    the 3 is for the m_tMidpoint, 12 o'clock point and hitpoint position.
614         var sameIndexCount = true;
615         if (this._vertexDataCount != index + 3) {
616             sameIndexCount = false;
617             this._vertexData = null;
618             this._vertexArrayBuffer = null;
619             this._vertexDataCount = 0;
620         }
621 
622         if (!this._vertexData) {
623             this._vertexDataCount = index + 3;
624             var locCount = this._vertexDataCount, vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
625             this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
626             var locData = [];
627             for (i = 0; i < locCount; i++)
628                 locData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
629 
630             this._vertexData = locData;
631             if(!this._vertexData){
632                 cc.log( "cc.ProgressTimer._updateRadial() : Not enough memory");
633                 return;
634             }
635         }
636         this._updateColor();
637 
638         var locVertexData = this._vertexData;
639         if (!sameIndexCount) {
640             //    First we populate the array with the m_tMidpoint, then all
641             //    vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
642             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(locMidPoint);
643             locVertexData[0].vertices = this._vertexFromAlphaPoint(locMidPoint);
644 
645             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(topMid);
646             locVertexData[1].vertices = this._vertexFromAlphaPoint(topMid);
647 
648             for (i = 0; i < index; i++) {
649                 var alphaPoint = this._boundaryTexCoord(i);
650                 locVertexData[i + 2].texCoords = this._textureCoordFromAlphaPoint(alphaPoint);
651                 locVertexData[i + 2].vertices = this._vertexFromAlphaPoint(alphaPoint);
652             }
653         }
654 
655         //    hitpoint will go last
656         locVertexData[this._vertexDataCount - 1].texCoords = this._textureCoordFromAlphaPoint(hit);
657         locVertexData[this._vertexDataCount - 1].vertices = this._vertexFromAlphaPoint(hit);
658     },
659 
660     /**
661      * <p>
662      *    Update does the work of mapping the texture onto the triangles for the bar                            <br/>
663      *    It now doesn't occur the cost of free/alloc data every update cycle.                                  <br/>
664      *    It also only changes the percentage point but no other points if they have not been modified.         <br/>
665      *                                                                                                          <br/>
666      *    It now deals with flipped texture. If you run into this problem, just use the                         <br/>
667      *    sprite property and enable the methods flipX, flipY.                                                  <br/>
668      * </p>
669      * @private
670      */
671     _updateBar:function () {
672         if (!this._sprite)
673             return;
674 
675         var i;
676         var alpha = this._percentage / 100.0;
677         var locBarChangeRate = this._barChangeRate;
678         var alphaOffset = cc.pMult(cc.p((1.0 - locBarChangeRate.x) + alpha * locBarChangeRate.x,
679             (1.0 - locBarChangeRate.y) + alpha * locBarChangeRate.y), 0.5);
680         var min = cc.pSub(this._midPoint, alphaOffset);
681         var max = cc.pAdd(this._midPoint, alphaOffset);
682 
683         if (min.x < 0) {
684             max.x += -min.x;
685             min.x = 0;
686         }
687 
688         if (max.x > 1) {
689             min.x -= max.x - 1;
690             max.x = 1;
691         }
692 
693         if (min.y < 0) {
694             max.y += -min.y;
695             min.y = 0;
696         }
697 
698         if (max.y > 1) {
699             min.y -= max.y - 1;
700             max.y = 1;
701         }
702 
703         var locVertexData;
704         if (!this._reverseDirection) {
705             if (!this._vertexData) {
706                 this._vertexDataCount = 4;
707                 var vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, locCount = 4;
708                 this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
709                 this._vertexData = [];
710                 for (i = 0; i < locCount; i++)
711                     this._vertexData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
712             }
713 
714             locVertexData = this._vertexData;
715             //    TOPLEFT
716             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
717             locVertexData[0].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
718 
719             //    BOTLEFT
720             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
721             locVertexData[1].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
722 
723             //    TOPRIGHT
724             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
725             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
726 
727             //    BOTRIGHT
728             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
729             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
730         } else {
731             if (!this._vertexData) {
732                 this._vertexDataCount = 8;
733                 var rVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, rLocCount = 8;
734                 this._vertexArrayBuffer = new ArrayBuffer(rLocCount * rVertexDataLen);
735                 var rTempData = [];
736                 for (i = 0; i < rLocCount; i++)
737                     rTempData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * rVertexDataLen);
738                 //    TOPLEFT 1
739                 rTempData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 1));
740                 rTempData[0].vertices = this._vertexFromAlphaPoint(cc.p(0, 1));
741 
742                 //    BOTLEFT 1
743                 rTempData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 0));
744                 rTempData[1].vertices = this._vertexFromAlphaPoint(cc.p(0, 0));
745 
746                 //    TOPRIGHT 2
747                 rTempData[6].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 1));
748                 rTempData[6].vertices = this._vertexFromAlphaPoint(cc.p(1, 1));
749 
750                 //    BOTRIGHT 2
751                 rTempData[7].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 0));
752                 rTempData[7].vertices = this._vertexFromAlphaPoint(cc.p(1, 0));
753 
754                 this._vertexData = rTempData;
755             }
756 
757             locVertexData = this._vertexData;
758             //    TOPRIGHT 1
759             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
760             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
761 
762             //    BOTRIGHT 1
763             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
764             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
765 
766             //    TOPLEFT 2
767             locVertexData[4].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
768             locVertexData[4].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
769 
770             //    BOTLEFT 2
771             locVertexData[5].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
772             locVertexData[5].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
773         }
774         this._updateColor();
775     },
776 
777     _updateColor:function () {
778         if (!this._sprite || !this._vertexData)
779             return;
780 
781         var sc = this._sprite.getQuad().tl.colors;
782         var locVertexData = this._vertexData;
783         for (var i = 0, len = this._vertexDataCount; i < len; ++i)
784             locVertexData[i].colors = sc;
785         this._vertexDataDirty = true;
786     },
787 
788     _updateProgress:null,
789 
790     _updateProgressForCanvas:function () {
791         var locSprite = this._sprite;
792         var spriteSize = locSprite.getContentSize();
793         var locMidPoint = this._midPoint;
794 
795         if (this._type == cc.PROGRESS_TIMER_TYPE_RADIAL) {
796             this._radius = Math.round(Math.sqrt(spriteSize.width * spriteSize.width + spriteSize.height * spriteSize.height));
797             var locStartAngle, locEndAngle, locCounterClockWise = false, locOrigin = this._origin;
798             locOrigin.x = spriteSize.width * locMidPoint.x;
799             locOrigin.y = -spriteSize.height * locMidPoint.y;
800 
801             if (this._reverseDirection) {
802                 locEndAngle = 270;
803                 locStartAngle = 270 - 3.6 * this._percentage;
804             } else {
805                 locStartAngle = -90;
806                 locEndAngle = -90 + 3.6 * this._percentage;
807             }
808 
809             if (locSprite._flippedX) {
810                 locOrigin.x -= spriteSize.width * (this._midPoint.x * 2);
811                 locStartAngle= -locStartAngle;
812                 locEndAngle= -locEndAngle;
813                 locStartAngle -= 180;
814                 locEndAngle -= 180;
815                 locCounterClockWise = !locCounterClockWise;
816             }
817             if (locSprite._flippedY) {
818                 locOrigin.y+=spriteSize.height*(this._midPoint.y*2);
819                 locCounterClockWise = !locCounterClockWise;
820                 locStartAngle= -locStartAngle;
821                 locEndAngle= -locEndAngle;
822             }
823 
824             this._startAngle = locStartAngle;
825             this._endAngle = locEndAngle;
826             this._counterClockWise = locCounterClockWise;
827         } else {
828             var locBarChangeRate = this._barChangeRate;
829             var percentageF = this._percentage / 100;
830             var locBarRect = this._barRect;
831 
832             var drawedSize = cc.size((spriteSize.width * (1 - locBarChangeRate.x)), (spriteSize.height * (1 - locBarChangeRate.y)));
833             var drawingSize = cc.size((spriteSize.width - drawedSize.width) * percentageF, (spriteSize.height - drawedSize.height) * percentageF);
834             var currentDrawSize = cc.size(drawedSize.width + drawingSize.width, drawedSize.height + drawingSize.height);
835 
836             var startPoint = cc.p(spriteSize.width * locMidPoint.x, spriteSize.height * locMidPoint.y);
837 
838             var needToLeft = startPoint.x - currentDrawSize.width / 2;
839             if (locMidPoint.x > 0.5) {
840                 if (currentDrawSize.width / 2 >= spriteSize.width - startPoint.x) {
841                     needToLeft = spriteSize.width - currentDrawSize.width;
842                 }
843             }
844 
845             var needToTop = startPoint.y - currentDrawSize.height / 2;
846             if (locMidPoint.y > 0.5) {
847                 if (currentDrawSize.height / 2 >= spriteSize.height - startPoint.y) {
848                     needToTop = spriteSize.height - currentDrawSize.height;
849                 }
850             }
851 
852             //left pos
853             locBarRect.x = 0;
854             var flipXNeed = 1;
855             if (locSprite._flippedX) {
856                 locBarRect.x -= currentDrawSize.width;
857                 flipXNeed = -1;
858             }
859 
860             if (needToLeft > 0)
861                 locBarRect.x += needToLeft * flipXNeed;
862 
863             //right pos
864             locBarRect.y = 0;
865             var flipYNeed = 1;
866             if (locSprite._flippedY) {
867                 locBarRect.y += currentDrawSize.height;
868                 flipYNeed = -1;
869             }
870 
871             if (needToTop > 0)
872                 locBarRect.y -= needToTop * flipYNeed;
873 
874             //clip width and clip height
875             locBarRect.width = currentDrawSize.width;
876             locBarRect.height = -currentDrawSize.height;
877         }
878     },
879 
880     _updateProgressForWebGL:function () {
881         var locType = this._type;
882         if(locType === cc.PROGRESS_TIMER_TYPE_RADIAL)
883             this._updateRadial();
884         else if(locType === cc.PROGRESS_TIMER_TYPE_BAR)
885             this._updateBar();
886         this._vertexDataDirty = true;
887     }
888 });
889 
890 if(cc.Browser.supportWebGL) {
891     cc.ProgressTimer.prototype.ctor = cc.ProgressTimer.prototype._ctorForWebGL;
892     cc.ProgressTimer.prototype.setReverseProgress = cc.ProgressTimer.prototype._setReverseProgressForWebGL;
893     cc.ProgressTimer.prototype.setSprite = cc.ProgressTimer.prototype._setSpriteForWebGL;
894     cc.ProgressTimer.prototype.setType = cc.ProgressTimer.prototype._setTypeForWebGL;
895     cc.ProgressTimer.prototype.setReverseDirection = cc.ProgressTimer.prototype._setReverseDirectionForWebGL;
896     cc.ProgressTimer.prototype.initWithSprite = cc.ProgressTimer.prototype._initWithSpriteForWebGL;
897     cc.ProgressTimer.prototype.draw = cc.ProgressTimer.prototype._drawForWebGL;
898     cc.ProgressTimer.prototype._updateProgress = cc.ProgressTimer.prototype._updateProgressForWebGL;
899 } else {
900     cc.ProgressTimer.prototype.ctor = cc.ProgressTimer.prototype._ctorForCanvas;
901     cc.ProgressTimer.prototype.setReverseProgress = cc.ProgressTimer.prototype._setReverseProgressForCanvas;
902     cc.ProgressTimer.prototype.setSprite = cc.ProgressTimer.prototype._setSpriteForCanvas;
903     cc.ProgressTimer.prototype.setType = cc.ProgressTimer.prototype._setTypeForCanvas;
904     cc.ProgressTimer.prototype.setReverseDirection = cc.ProgressTimer.prototype._setReverseDirectionForCanvas;
905     cc.ProgressTimer.prototype.initWithSprite = cc.ProgressTimer.prototype._initWithSpriteForCanvas;
906     cc.ProgressTimer.prototype.draw = cc.ProgressTimer.prototype._drawForCanvas;
907     cc.ProgressTimer.prototype._updateProgress = cc.ProgressTimer.prototype._updateProgressForCanvas;
908 }
909 
910 /**
911  * create a progress timer object with image file name that renders the inner sprite according to the percentage
912  * @param {cc.Sprite} sprite
913  * @return {cc.ProgressTimer}
914  * @example
915  * // Example
916  * var progress = cc.ProgressTimer.create('progress.png')
917  */
918 cc.ProgressTimer.create = function (sprite) {
919     var progressTimer = new cc.ProgressTimer();
920     if (progressTimer.initWithSprite(sprite))
921         return progressTimer;
922     return null;
923 };
924 
925 
926