1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 /**
 27  * @const
 28  * @type {number}
 29  */
 30 cc.PI2 = Math.PI * 2;
 31 
 32 /**
 33  * Canvas of DrawingPrimitive implement version use for canvasMode
 34  * @class
 35  * @extends cc.Class
 36  * @param {CanvasRenderingContext2D} renderContext
 37  */
 38 cc.DrawingPrimitiveCanvas = cc.Class.extend(/** @lends cc.DrawingPrimitiveCanvas# */{
 39     _cacheArray:[],
 40     _renderContext:null,
 41     /**
 42      * Constructor of cc.DrawingPrimitiveCanvas
 43      * @param {cc.CanvasContextWrapper} renderContext
 44      */
 45     ctor:function (renderContext) {
 46         this._renderContext = renderContext;
 47     },
 48 
 49     /**
 50      * draws a point given x and y coordinate measured in points
 51      * @override
 52      * @param {cc.Point} point
 53      * @param {Number} size
 54      */
 55     drawPoint:function (point, size) {
 56         if (!size) {
 57             size = 1;
 58         }
 59         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
 60         var newPoint = cc.p(point.x  * locScaleX, point.y * locScaleY);
 61         var ctx = this._renderContext.getContext();
 62         ctx.beginPath();
 63         ctx.arc(newPoint.x, -newPoint.y, size * locScaleX, 0, Math.PI * 2, false);
 64         ctx.closePath();
 65         ctx.fill();
 66     },
 67 
 68     /**
 69      * draws an array of points.
 70      * @override
 71      * @param {Array} points point of array
 72      * @param {Number} numberOfPoints
 73      * @param {Number} size
 74      */
 75     drawPoints:function (points, numberOfPoints, size) {
 76         if (points == null)
 77             return;
 78 
 79         if (!size) {
 80             size = 1;
 81         }
 82         var locContext = this._renderContext.getContext(),locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
 83 
 84         locContext.beginPath();
 85         for (var i = 0, len = points.length; i < len; i++)
 86             locContext.arc(points[i].x * locScaleX, -points[i].y * locScaleY, size * locScaleX, 0, Math.PI * 2, false);
 87         locContext.closePath();
 88         locContext.fill();
 89     },
 90 
 91     /**
 92      * draws a line given the origin and destination point measured in points
 93      * @override
 94      * @param {cc.Point} origin
 95      * @param {cc.Point} destination
 96      */
 97     drawLine:function (origin, destination) {
 98         var locContext = this._renderContext.getContext(), locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
 99         locContext.beginPath();
100         locContext.moveTo(origin.x * locScaleX, -origin.y * locScaleY);
101         locContext.lineTo(destination.x * locScaleX, -destination.y * locScaleY);
102         locContext.closePath();
103         locContext.stroke();
104     },
105 
106     /**
107      * draws a rectangle given the origin and destination point measured in points.
108      * @param {cc.Point} origin
109      * @param {cc.Point} destination
110      */
111     drawRect:function (origin, destination) {
112         //todo need optimize for performance
113         this.drawLine(cc.p(origin.x, origin.y), cc.p(destination.x, origin.y));
114         this.drawLine(cc.p(destination.x, origin.y), cc.p(destination.x, destination.y));
115         this.drawLine(cc.p(destination.x, destination.y), cc.p(origin.x, destination.y));
116         this.drawLine(cc.p(origin.x, destination.y), cc.p(origin.x, origin.y));
117     },
118 
119     /**
120      * draws a solid rectangle given the origin and destination point measured in points.
121      * @param {cc.Point} origin
122      * @param {cc.Point} destination
123      * @param {cc.Color} color
124      */
125     drawSolidRect:function (origin, destination, color) {
126         var vertices = [
127             origin,
128             cc.p(destination.x, origin.y),
129             destination,
130             cc.p(origin.x, destination.y)
131         ];
132 
133         this.drawSolidPoly(vertices, 4, color);
134     },
135 
136     /**
137      * draws a polygon given a pointer to cc.Point coordinates and the number of vertices measured in points.
138      * @override
139      * @param {Array} vertices a pointer to cc.Point coordinates
140      * @param {Number} numOfVertices the number of vertices measured in points
141      * @param {Boolean} closePolygon The polygon can be closed or open
142      * @param {Boolean} [fill=] The polygon can be closed or open and optionally filled with current color
143      */
144     drawPoly:function (vertices, numOfVertices, closePolygon, fill) {
145         fill = fill || false;
146 
147         if (vertices == null)
148             return;
149 
150         if (vertices.length < 3)
151             throw new Error("Polygon's point must greater than 2");
152 
153         var firstPoint = vertices[0], locContext = this._renderContext.getContext();
154         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
155         locContext.beginPath();
156         locContext.moveTo(firstPoint.x * locScaleX, -firstPoint.y * locScaleY);
157         for (var i = 1, len = vertices.length; i < len; i++)
158             locContext.lineTo(vertices[i].x * locScaleX, -vertices[i].y * locScaleY);
159 
160         if (closePolygon)
161             locContext.closePath();
162 
163         if (fill)
164             locContext.fill();
165         else
166             locContext.stroke();
167     },
168 
169     /**
170      * draws a solid polygon given a pointer to CGPoint coordinates, the number of vertices measured in points, and a color.
171      * @param {Array} polygons
172      * @param {Number} numberOfPoints
173      * @param {cc.Color} color
174      */
175     drawSolidPoly:function (polygons, numberOfPoints, color) {
176         this.setDrawColor(color.r, color.g, color.b, color.a);
177         this.drawPoly(polygons, numberOfPoints, true, true);
178     },
179 
180     /**
181      * draws a circle given the center, radius and number of segments.
182      * @override
183      * @param {cc.Point} center center of circle
184      * @param {Number} radius
185      * @param {Number} angle angle in radians
186      * @param {Number} segments
187      * @param {Boolean} [drawLineToCenter=]
188      */
189     drawCircle: function (center, radius, angle, segments, drawLineToCenter) {
190         drawLineToCenter = drawLineToCenter || false;
191         var locContext = this._renderContext.getContext();
192         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
193         locContext.beginPath();
194         var endAngle = angle - Math.PI * 2;
195         locContext.arc(0 | (center.x * locScaleX), 0 | -(center.y * locScaleY), radius * locScaleX, -angle, -endAngle, false);
196         if (drawLineToCenter) {
197             locContext.lineTo(0 | (center.x * locScaleX), 0 | -(center.y * locScaleY));
198         }
199         locContext.stroke();
200     },
201 
202     /**
203      * draws a quad bezier path
204      * @override
205      * @param {cc.Point} origin
206      * @param {cc.Point} control
207      * @param {cc.Point} destination
208      * @param {Number} segments
209      */
210     drawQuadBezier:function (origin, control, destination, segments) {
211         //this is OpenGL Algorithm
212         var vertices = this._cacheArray;
213         vertices.length =0;
214 
215         var t = 0.0;
216         for (var i = 0; i < segments; i++) {
217             var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
218             var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
219             vertices.push(cc.p(x, y));
220             t += 1.0 / segments;
221         }
222         vertices.push(cc.p(destination.x, destination.y));
223 
224         this.drawPoly(vertices, segments + 1, false, false);
225     },
226 
227     /**
228      * draws a cubic bezier path
229      * @override
230      * @param {cc.Point} origin
231      * @param {cc.Point} control1
232      * @param {cc.Point} control2
233      * @param {cc.Point} destination
234      * @param {Number} segments
235      */
236     drawCubicBezier:function (origin, control1, control2, destination, segments) {
237         //this is OpenGL Algorithm
238         var vertices = this._cacheArray;
239         vertices.length =0;
240 
241         var t = 0;
242         for (var i = 0; i < segments; i++) {
243             var x = Math.pow(1 - t, 3) * origin.x + 3.0 * Math.pow(1 - t, 2) * t * control1.x + 3.0 * (1 - t) * t * t * control2.x + t * t * t * destination.x;
244             var y = Math.pow(1 - t, 3) * origin.y + 3.0 * Math.pow(1 - t, 2) * t * control1.y + 3.0 * (1 - t) * t * t * control2.y + t * t * t * destination.y;
245             vertices.push(cc.p(x , y ));
246             t += 1.0 / segments;
247         }
248         vertices.push(cc.p(destination.x , destination.y));
249 
250         this.drawPoly(vertices, segments + 1, false, false);
251     },
252 
253     /**
254      * draw a CatmullRom curve
255      * @override
256      * @param {Array} points
257      * @param {Number} segments
258      */
259     drawCatmullRom:function (points, segments) {
260         this.drawCardinalSpline(points, 0.5, segments);
261     },
262 
263     /**
264      * draw a cardinal spline path
265      * @override
266      * @param {Array} config
267      * @param {Number} tension
268      * @param {Number} segments
269      */
270     drawCardinalSpline:function (config, tension, segments) {
271         //lazy_init();
272         cc._renderContext.setStrokeStyle("rgba(255,255,255,1)");
273         var points = this._cacheArray;
274         points.length = 0;
275         var p, lt;
276         var deltaT = 1.0 / config.length;
277 
278         for (var i = 0; i < segments + 1; i++) {
279             var dt = i / segments;
280 
281             // border
282             if (dt === 1) {
283                 p = config.length - 1;
284                 lt = 1;
285             } else {
286                 p = 0 | (dt / deltaT);
287                 lt = (dt - deltaT * p) / deltaT;
288             }
289 
290             // Interpolate
291             var newPos = cc.CardinalSplineAt(
292                 cc.getControlPointAt(config, p - 1),
293                 cc.getControlPointAt(config, p - 0),
294                 cc.getControlPointAt(config, p + 1),
295                 cc.getControlPointAt(config, p + 2),
296                 tension, lt);
297             points.push(newPos);
298         }
299         this.drawPoly(points, segments + 1, false, false);
300     },
301 
302     /**
303      * draw an image
304      * @override
305      * @param {HTMLImageElement|HTMLCanvasElement} image
306      * @param {cc.Point} sourcePoint
307      * @param {cc.Size} sourceSize
308      * @param {cc.Point} destPoint
309      * @param {cc.Size} destSize
310      */
311     drawImage:function (image, sourcePoint, sourceSize, destPoint, destSize) {
312         var len = arguments.length;
313         var ctx = this._renderContext.getContext();
314         switch (len) {
315             case 2:
316                 var height = image.height;
317                 ctx.drawImage(image, sourcePoint.x, -(sourcePoint.y + height));
318                 break;
319             case 3:
320                 ctx.drawImage(image, sourcePoint.x, -(sourcePoint.y + sourceSize.height), sourceSize.width, sourceSize.height);
321                 break;
322             case 5:
323                 ctx.drawImage(image, sourcePoint.x, sourcePoint.y, sourceSize.width, sourceSize.height, destPoint.x, -(destPoint.y + destSize.height),
324                     destSize.width, destSize.height);
325                 break;
326             default:
327                 throw new Error("Argument must be non-nil");
328                 break;
329         }
330     },
331 
332     /**
333      * draw a star
334      * @param {cc.CanvasContextWrapper} ctx canvas context
335      * @param {Number} radius
336      * @param {cc.Color} color
337      */
338     drawStar:function (ctx, radius, color) {
339         var wrapper = ctx || this._renderContext;
340         var context = wrapper.getContext();
341         radius *= cc.view.getScaleX();
342         var colorStr = "rgba(" + (0 | color.r) + "," + (0 | color.g) + "," + (0 | color.b);
343         wrapper.setFillStyle(colorStr + ",1)");
344         //context.fillStyle = colorStr + ",1)";
345         var subRadius = radius / 10;
346 
347         context.beginPath();
348         context.moveTo(-radius, radius);
349         context.lineTo(0, subRadius);
350         context.lineTo(radius, radius);
351         context.lineTo(subRadius, 0);
352         context.lineTo(radius, -radius);
353         context.lineTo(0, -subRadius);
354         context.lineTo(-radius, -radius);
355         context.lineTo(-subRadius, 0);
356         context.lineTo(-radius, radius);
357         context.closePath();
358         context.fill();
359 
360         var rg = context.createRadialGradient(0, 0, subRadius, 0, 0, radius);
361         rg.addColorStop(0, colorStr + ", 1)");
362         rg.addColorStop(0.3, colorStr + ", 0.8)");
363         rg.addColorStop(1.0, colorStr + ", 0.0)");
364         wrapper.setFillStyle(rg);
365         //context.fillStyle = g1;
366         context.beginPath();
367         var startAngle_1 = 0;
368         var endAngle_1 = cc.PI2;
369         context.arc(0, 0, radius - subRadius, startAngle_1, endAngle_1, false);
370         context.closePath();
371         context.fill();
372     },
373 
374     /**
375      * draw a color ball
376      * @param {cc.CanvasContextWrapper} ctx canvas context
377      * @param {Number} radius
378      * @param {cc.Color} color
379      */
380     drawColorBall:function (ctx, radius, color) {
381         var wrapper = ctx || this._renderContext;
382         var context = wrapper.getContext();
383         radius *= cc.view.getScaleX();
384         var colorStr = "rgba(" +(0|color.r) + "," + (0|color.g) + "," + (0|color.b);
385         var subRadius = radius / 10;
386 
387         var g1 = context.createRadialGradient(0, 0, subRadius, 0, 0, radius);
388         g1.addColorStop(0, colorStr + ", 1)");
389         g1.addColorStop(0.3, colorStr + ", 0.8)");
390         g1.addColorStop(0.6, colorStr + ", 0.4)");
391         g1.addColorStop(1.0, colorStr + ", 0.0)");
392         wrapper.setFillStyle(g1);
393         //context.fillStyle = g1;
394         context.beginPath();
395         var startAngle_1 = 0;
396         var endAngle_1 = cc.PI2;
397         context.arc(0, 0, radius, startAngle_1, endAngle_1, false);
398         context.closePath();
399         context.fill();
400     },
401 
402     /**
403      * fill text
404      * @param {String} strText
405      * @param {Number} x
406      * @param {Number} y
407      */
408     fillText:function (strText, x, y) {
409         this._renderContext.getContext().fillText(strText, x, -y);
410     },
411 
412     /**
413      * set the drawing color with 4 unsigned bytes
414      * @param {Number} r red value (0 to 255)
415      * @param {Number} g green value (0 to 255)
416      * @param {Number} b blue value (0 to 255)
417      * @param {Number} a Alpha value (0 to 255)
418      */
419     setDrawColor:function (r, g, b, a) {
420         this._renderContext.setFillStyle("rgba(" + r + "," + g + "," + b + "," + a / 255 + ")");
421         this._renderContext.setStrokeStyle("rgba(" + r + "," + g + "," + b + "," + a / 255 + ")");
422     },
423 
424     /**
425      * set the point size in points. Default 1.
426      * @param {Number} pointSize
427      */
428     setPointSize:function (pointSize) {
429     },
430 
431     /**
432      * set the line width. Default 1.
433      * @param {Number} width
434      */
435     setLineWidth:function (width) {
436         this._renderContext.getContext().lineWidth = width * cc.view.getScaleX();
437     }
438 });