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 cc.PI2 = Math.PI * 2;
 28 
 29 /**
 30  * Canvas of DrawingPrimitive implement version
 31  * @class
 32  * @extends cc.Class
 33  */
 34 cc.DrawingPrimitiveCanvas = cc.Class.extend(/** @lends cc.DrawingPrimitiveCanvas# */{
 35     _cacheArray:[],
 36     _renderContext:null,
 37     /**
 38      * Constructor
 39      * @param {CanvasRenderingContext2D} renderContext
 40      */
 41     ctor:function (renderContext) {
 42         this._renderContext = renderContext;
 43     },
 44 
 45     /**
 46      * draws a point given x and y coordinate measured in points
 47      * @override
 48      * @param {cc.Point} point
 49      * @param {Number} size
 50      */
 51     drawPoint:function (point, size) {
 52         if (!size) {
 53             size = 1;
 54         }
 55         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
 56         var newPoint = cc.p(point.x  * locScaleX, point.y * locScaleY);
 57         this._renderContext.beginPath();
 58         this._renderContext.arc(newPoint.x, -newPoint.y, size * locScaleX, 0, Math.PI * 2, false);
 59         this._renderContext.closePath();
 60         this._renderContext.fill();
 61     },
 62 
 63     /**
 64      * draws an array of points.
 65      * @override
 66      * @param {Array} points point of array
 67      * @param {Number} numberOfPoints
 68      * @param {Number} size
 69      */
 70     drawPoints:function (points, numberOfPoints, size) {
 71         if (points == null) {
 72             return;
 73         }
 74         if (!size) {
 75             size = 1;
 76         }
 77         var locContext = this._renderContext,locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
 78 
 79         locContext.beginPath();
 80         for (var i = 0, len = points.length; i < len; i++)
 81             locContext.arc(points[i].x * locScaleX, -points[i].y * locScaleY, size * locScaleX, 0, Math.PI * 2, false);
 82         locContext.closePath();
 83         locContext.fill();
 84     },
 85 
 86     /**
 87      * draws a line given the origin and destination point measured in points
 88      * @override
 89      * @param {cc.Point} origin
 90      * @param {cc.Point} destination
 91      */
 92     drawLine:function (origin, destination) {
 93         var locContext = this._renderContext, locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
 94         locContext.beginPath();
 95         locContext.moveTo(origin.x * locScaleX, -origin.y * locScaleY);
 96         locContext.lineTo(destination.x * locScaleX, -destination.y * locScaleY);
 97         locContext.closePath();
 98         locContext.stroke();
 99     },
100 
101     /**
102      * draws a rectangle given the origin and destination point measured in points.
103      * @param {cc.Point} origin
104      * @param {cc.Point} destination
105      */
106     drawRect:function (origin, destination) {
107         this.drawLine(cc.p(origin.x, origin.y), cc.p(destination.x, origin.y));
108         this.drawLine(cc.p(destination.x, origin.y), cc.p(destination.x, destination.y));
109         this.drawLine(cc.p(destination.x, destination.y), cc.p(origin.x, destination.y));
110         this.drawLine(cc.p(origin.x, destination.y), cc.p(origin.x, origin.y));
111     },
112 
113     /**
114      * draws a solid rectangle given the origin and destination point measured in points.
115      * @param {cc.Point} origin
116      * @param {cc.Point} destination
117      * @param {cc.Color} color
118      */
119     drawSolidRect:function (origin, destination, color) {
120         var vertices = [
121             origin,
122             cc.p(destination.x, origin.y),
123             destination,
124             cc.p(origin.x, destination.y)
125         ];
126 
127         this.drawSolidPoly(vertices, 4, color);
128     },
129 
130     /**
131      * draws a polygon given a pointer to cc.Point coordinates and the number of vertices measured in points.
132      * @override
133      * @param {Array} vertices a pointer to cc.Point coordinates
134      * @param {Number} numOfVertices the number of vertices measured in points
135      * @param {Boolean} closePolygon The polygon can be closed or open
136      * @param {Boolean} [fill=] The polygon can be closed or open and optionally filled with current color
137      */
138     drawPoly:function (vertices, numOfVertices, closePolygon, fill) {
139         fill = fill || false;
140 
141         if (vertices == null)
142             return;
143 
144         if (vertices.length < 3)
145             throw new Error("Polygon's point must greater than 2");
146 
147         var firstPoint = vertices[0], locContext = this._renderContext;
148         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
149         locContext.beginPath();
150         locContext.moveTo(firstPoint.x * locScaleX, -firstPoint.y * locScaleY);
151         for (var i = 1, len = vertices.length; i < len; i++)
152             locContext.lineTo(vertices[i].x * locScaleX, -vertices[i].y * locScaleY);
153 
154         if (closePolygon)
155             locContext.closePath();
156 
157         if (fill)
158             locContext.fill();
159         else
160             locContext.stroke();
161     },
162 
163     /**
164      * draws a solid polygon given a pointer to CGPoint coordinates, the number of vertices measured in points, and a color.
165      * @param {Array} polygons
166      * @param {Number} numberOfPoints
167      * @param {cc.Color} color
168      */
169     drawSolidPoly:function (polygons, numberOfPoints, color) {
170         this.setDrawColor(color.r, color.g, color.b, color.a);
171         this.drawPoly(polygons, numberOfPoints, true, true);
172     },
173 
174     /**
175      * draws a circle given the center, radius and number of segments.
176      * @override
177      * @param {cc.Point} center center of circle
178      * @param {Number} radius
179      * @param {Number} angle angle in radians
180      * @param {Number} segments
181      * @param {Boolean} [drawLineToCenter=]
182      */
183     drawCircle: function (center, radius, angle, segments, drawLineToCenter) {
184         drawLineToCenter = drawLineToCenter || false;
185         var locContext = this._renderContext;
186         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
187         locContext.beginPath();
188         var endAngle = angle - Math.PI * 2;
189         locContext.arc(0 | (center.x * locScaleX), 0 | -(center.y * locScaleY), radius * locScaleX, -angle, -endAngle, false);
190         if (drawLineToCenter) {
191             locContext.lineTo(0 | (center.x * locScaleX), 0 | -(center.y * locScaleY));
192         }
193         locContext.stroke();
194     },
195 
196     /**
197      * draws a quad bezier path
198      * @override
199      * @param {cc.Point} origin
200      * @param {cc.Point} control
201      * @param {cc.Point} destination
202      * @param {Number} segments
203      */
204     drawQuadBezier:function (origin, control, destination, segments) {
205         //this is OpenGL Algorithm
206         var vertices = this._cacheArray;
207         vertices.length =0;
208 
209         var t = 0.0;
210         for (var i = 0; i < segments; i++) {
211             var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
212             var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
213             vertices.push(cc.p(x, y));
214             t += 1.0 / segments;
215         }
216         vertices.push(cc.p(destination.x, destination.y));
217 
218         this.drawPoly(vertices, segments + 1, false, false);
219     },
220 
221     /**
222      * draws a cubic bezier path
223      * @override
224      * @param {cc.Point} origin
225      * @param {cc.Point} control1
226      * @param {cc.Point} control2
227      * @param {cc.Point} destination
228      * @param {Number} segments
229      */
230     drawCubicBezier:function (origin, control1, control2, destination, segments) {
231         //this is OpenGL Algorithm
232         var vertices = this._cacheArray;
233         vertices.length =0;
234 
235         var t = 0;
236         for (var i = 0; i < segments; i++) {
237             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;
238             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;
239             vertices.push(cc.p(x , y ));
240             t += 1.0 / segments;
241         }
242         vertices.push(cc.p(destination.x , destination.y));
243 
244         this.drawPoly(vertices, segments + 1, false, false);
245     },
246 
247     /**
248      * draw a CatmullRom curve
249      * @override
250      * @param {Array} points
251      * @param {Number} segments
252      */
253     drawCatmullRom:function (points, segments) {
254         this.drawCardinalSpline(points, 0.5, segments);
255     },
256 
257     /**
258      * draw a cardinal spline path
259      * @override
260      * @param {Array} config
261      * @param {Number} tension
262      * @param {Number} segments
263      */
264     drawCardinalSpline:function (config, tension, segments) {
265         //lazy_init();
266         cc._renderContext.strokeStyle = "rgba(255,255,255,1)";
267         var points = this._cacheArray;
268         points.length = 0;
269         var p, lt;
270         var deltaT = 1.0 / config.length;
271 
272         for (var i = 0; i < segments + 1; i++) {
273             var dt = i / segments;
274 
275             // border
276             if (dt == 1) {
277                 p = config.length - 1;
278                 lt = 1;
279             } else {
280                 p = 0 | (dt / deltaT);
281                 lt = (dt - deltaT * p) / deltaT;
282             }
283 
284             // Interpolate
285             var newPos = cc.CardinalSplineAt(
286                 cc.getControlPointAt(config, p - 1),
287                 cc.getControlPointAt(config, p - 0),
288                 cc.getControlPointAt(config, p + 1),
289                 cc.getControlPointAt(config, p + 2),
290                 tension, lt);
291             points.push(newPos);
292         }
293         this.drawPoly(points, segments + 1, false, false);
294     },
295 
296     /**
297      * draw an image
298      * @override
299      * @param {HTMLImageElement|HTMLCanvasElement} image
300      * @param {cc.Point} sourcePoint
301      * @param {cc.Size} sourceSize
302      * @param {cc.Point} destPoint
303      * @param {cc.Size} destSize
304      */
305     drawImage:function (image, sourcePoint, sourceSize, destPoint, destSize) {
306         var len = arguments.length;
307         switch (len) {
308             case 2:
309                 var height = image.height;
310                 this._renderContext.drawImage(image, sourcePoint.x, -(sourcePoint.y + height));
311                 break;
312             case 3:
313                 this._renderContext.drawImage(image, sourcePoint.x, -(sourcePoint.y + sourceSize.height), sourceSize.width, sourceSize.height);
314                 break;
315             case 5:
316                 this._renderContext.drawImage(image, sourcePoint.x, sourcePoint.y, sourceSize.width, sourceSize.height, destPoint.x, -(destPoint.y + destSize.height),
317                     destSize.width, destSize.height);
318                 break;
319             default:
320                 throw new Error("Argument must be non-nil");
321                 break;
322         }
323     },
324 
325     /**
326      * draw a star
327      * @param {CanvasRenderingContext2D} ctx canvas context
328      * @param {Number} radius
329      * @param {cc.Color} color
330      */
331     drawStar:function (ctx, radius, color) {
332         var context = ctx || this._renderContext;
333         radius *= cc.view.getScaleX();
334         var colorStr = "rgba(" + (0 | color.r) + "," + (0 | color.g) + "," + (0 | color.b);
335         context.fillStyle = colorStr + ",1)";
336         var subRadius = radius / 10;
337 
338         context.beginPath();
339         context.moveTo(-radius, radius);
340         context.lineTo(0, subRadius);
341         context.lineTo(radius, radius);
342         context.lineTo(subRadius, 0);
343         context.lineTo(radius, -radius);
344         context.lineTo(0, -subRadius);
345         context.lineTo(-radius, -radius);
346         context.lineTo(-subRadius, 0);
347         context.lineTo(-radius, radius);
348         context.closePath();
349         context.fill();
350 
351         var g1 = context.createRadialGradient(0, 0, subRadius, 0, 0, radius);
352         g1.addColorStop(0, colorStr + ", 1)");
353         g1.addColorStop(0.3, colorStr + ", 0.8)");
354         g1.addColorStop(1.0, colorStr + ", 0.0)");
355         context.fillStyle = g1;
356         context.beginPath();
357         var startAngle_1 = 0;
358         var endAngle_1 = cc.PI2;
359         context.arc(0, 0, radius - subRadius, startAngle_1, endAngle_1, false);
360         context.closePath();
361         context.fill();
362     },
363 
364     /**
365      * draw a color ball
366      * @param {CanvasRenderingContext2D} ctx canvas context
367      * @param {Number} radius
368      * @param {cc.Color} color
369      */
370     drawColorBall:function (ctx, radius, color) {
371         var context = ctx || this._renderContext;
372         radius *= cc.view.getScaleX();
373         var colorStr = "rgba(" +(0|color.r) + "," + (0|color.g) + "," + (0|color.b);
374         var subRadius = radius / 10;
375 
376         var g1 = context.createRadialGradient(0, 0, subRadius, 0, 0, radius);
377         g1.addColorStop(0, colorStr + ", 1)");
378         g1.addColorStop(0.3, colorStr + ", 0.8)");
379         g1.addColorStop(0.6, colorStr + ", 0.4)");
380         g1.addColorStop(1.0, colorStr + ", 0.0)");
381         context.fillStyle = g1;
382         context.beginPath();
383         var startAngle_1 = 0;
384         var endAngle_1 = cc.PI2;
385         context.arc(0, 0, radius, startAngle_1, endAngle_1, false);
386         context.closePath();
387         context.fill();
388     },
389 
390     /**
391      * fill text
392      * @param {String} strText
393      * @param {Number} x
394      * @param {Number} y
395      */
396     fillText:function (strText, x, y) {
397         this._renderContext.fillText(strText, x, -y);
398     },
399 
400     /**
401      * set the drawing color with 4 unsigned bytes
402      * @param {Number} r red value (0 to 255)
403      * @param {Number} g green value (0 to 255)
404      * @param {Number} b blue value (0 to 255)
405      * @param {Number} a Alpha value (0 to 255)
406      */
407     setDrawColor:function (r, g, b, a) {
408         this._renderContext.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a / 255 + ")";
409         this._renderContext.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a / 255 + ")";
410     },
411 
412     /**
413      * set the point size in points. Default 1.
414      * @param {Number} pointSize
415      */
416     setPointSize:function (pointSize) {
417     },
418 
419     /**
420      * set the line width. Default 1.
421      * @param {Number} width
422      */
423     setLineWidth:function (width) {
424         this._renderContext.lineWidth = width * cc.view.getScaleX();
425     }
426 });