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