source: Dev/branches/rest-dojo-ui/client/dojox/gfx/canvas.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 25.1 KB
Line 
1define(["./_base", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "dojo/_base/window", "dojo/dom-geometry",
2                "dojo/dom", "./_base", "./shape", "./path", "./arc", "./matrix", "./decompose"],
3  function(g, lang, arr, declare, win, domGeom, dom, gfxBase, gs, pathLib, ga, m, decompose ){
4/*=====
5        dojox.gfx.canvas = {
6        // module:
7        //              dojox/gfx/canvas
8        // summary:
9        //              This the graphics rendering bridge for W3C Canvas compliant browsers.
10        //              Since Canvas is an immediate mode graphics api, with no object graph or
11        //              eventing capabilities, use of this module alone will only add in drawing support.
12        //              The additional module, canvasWithEvents extends this module with additional support
13        //              for handling events on Canvas.  By default, the support for events is now included
14        //              however, if only drawing capabilities are needed, canvas event module can be disabled
15        //              using the dojoConfig option, canvasEvents:true|false.
16        //              The id of the Canvas renderer is 'canvas'.  This id can be used when switch Dojo's
17        //              graphics context between renderer implementations.  See dojox.gfx._base switchRenderer
18        //              API.
19        };
20        g = dojox.gfx;
21        gs = dojox.gfx.shape;
22        pathLib.Path = dojox.gfx.path.Path;
23        pathLib.TextPath = dojox.gfx.path.TextPath;
24        canvas = dojox.gfx.canvas;
25        canvas.Shape = dojox.gfx.canvas.Shape;
26        gs.Shape = dojox.gfx.shape.Shape;
27        gs.Rect = dojox.gfx.shape.Rect;
28        gs.Ellipse = dojox.gfx.shape.Ellipse;
29        gs.Circle = dojox.gfx.shape.Circle;
30        gs.Line = dojox.gfx.shape.Line;
31        gs.PolyLine = dojox.gfx.shape.PolyLine;
32        gs.Image = dojox.gfx.shape.Image;
33        gs.Text = dojox.gfx.shape.Text;
34        gs.Surface = dojox.gfx.shape.Surface;
35  =====*/
36
37        var canvas = g.canvas = {};
38        var pattrnbuffer = null,
39                mp = m.multiplyPoint,
40                pi = Math.PI,
41                twoPI = 2 * pi,
42                halfPI = pi /2,
43                extend = lang.extend;
44
45        declare("dojox.gfx.canvas.Shape", gs.Shape, {
46                _render: function(/* Object */ ctx){
47                        // summary: render the shape
48                        ctx.save();
49                        this._renderTransform(ctx);
50                        this._renderShape(ctx);
51                        this._renderFill(ctx, true);
52                        this._renderStroke(ctx, true);
53                        ctx.restore();
54                },
55                _renderTransform: function(/* Object */ ctx){
56                        if("canvasTransform" in this){
57                                var t = this.canvasTransform;
58                                ctx.translate(t.dx, t.dy);
59                                ctx.rotate(t.angle2);
60                                ctx.scale(t.sx, t.sy);
61                                ctx.rotate(t.angle1);
62                                // The future implementation when vendors catch up with the spec:
63                                // var t = this.matrix;
64                                // ctx.transform(t.xx, t.yx, t.xy, t.yy, t.dx, t.dy);
65                        }
66                },
67                _renderShape: function(/* Object */ ctx){
68                        // nothing
69                },
70                _renderFill: function(/* Object */ ctx, /* Boolean */ apply){
71                        if("canvasFill" in this){
72                                var fs = this.fillStyle;
73                                if("canvasFillImage" in this){
74                                        var w = fs.width, h = fs.height,
75                                                iw = this.canvasFillImage.width, ih = this.canvasFillImage.height,
76                                                // let's match the svg default behavior wrt. aspect ratio: xMidYMid meet
77                                                sx = w == iw ? 1 : w / iw,
78                                                sy = h == ih ? 1 : h / ih,
79                                                s = Math.min(sx,sy), //meet->math.min , slice->math.max
80                                                dx = (w - s * iw)/2,
81                                                dy = (h - s * ih)/2;
82                                        // the buffer used to scaled the image
83                                        pattrnbuffer.width = w; pattrnbuffer.height = h;
84                                        var copyctx = pattrnbuffer.getContext("2d");
85                                        copyctx.clearRect(0, 0, w, h);
86                                        copyctx.drawImage(this.canvasFillImage, 0, 0, iw, ih, dx, dy, s*iw, s*ih);
87                                        this.canvasFill = ctx.createPattern(pattrnbuffer, "repeat");
88                                        delete this.canvasFillImage;
89                                }
90                                ctx.fillStyle = this.canvasFill;
91                                if(apply){
92                                        // offset the pattern
93                                        if (fs.type==="pattern" && (fs.x !== 0 || fs.y !== 0)) {
94                                                ctx.translate(fs.x,fs.y);
95                                        }
96                                        ctx.fill();
97                                }
98                        }else{
99                                ctx.fillStyle = "rgba(0,0,0,0.0)";
100                        }
101                },
102                _renderStroke: function(/* Object */ ctx, /* Boolean */ apply){
103                        var s = this.strokeStyle;
104                        if(s){
105                                ctx.strokeStyle = s.color.toString();
106                                ctx.lineWidth = s.width;
107                                ctx.lineCap = s.cap;
108                                if(typeof s.join == "number"){
109                                        ctx.lineJoin = "miter";
110                                        ctx.miterLimit = s.join;
111                                }else{
112                                        ctx.lineJoin = s.join;
113                                }
114                                if(apply){ ctx.stroke(); }
115                        }else if(!apply){
116                                ctx.strokeStyle = "rgba(0,0,0,0.0)";
117                        }
118                },
119
120                // events are not implemented
121                getEventSource: function(){ return null; },
122                connect:                function(){},
123                disconnect:             function(){}
124        });
125
126        var modifyMethod = function(shape, method, extra){
127                        var old = shape.prototype[method];
128                        shape.prototype[method] = extra ?
129                                function(){
130                                        this.surface.makeDirty();
131                                        old.apply(this, arguments);
132                                        extra.call(this);
133                                        return this;
134                                } :
135                                function(){
136                                        this.surface.makeDirty();
137                                        return old.apply(this, arguments);
138                                };
139                };
140
141        modifyMethod(canvas.Shape, "setTransform",
142                function(){
143                        // prepare Canvas-specific structures
144                        if(this.matrix){
145                                this.canvasTransform = g.decompose(this.matrix);
146                        }else{
147                                delete this.canvasTransform;
148                        }
149                });
150
151        modifyMethod(canvas.Shape, "setFill",
152                function(){
153                        // prepare Canvas-specific structures
154                        var fs = this.fillStyle, f;
155                        if(fs){
156                                if(typeof(fs) == "object" && "type" in fs){
157                                        var ctx = this.surface.rawNode.getContext("2d");
158                                        switch(fs.type){
159                                                case "linear":
160                                                case "radial":
161                                                        f = fs.type == "linear" ?
162                                                                ctx.createLinearGradient(fs.x1, fs.y1, fs.x2, fs.y2) :
163                                                                ctx.createRadialGradient(fs.cx, fs.cy, 0, fs.cx, fs.cy, fs.r);
164                                                        arr.forEach(fs.colors, function(step){
165                                                                f.addColorStop(step.offset, g.normalizeColor(step.color).toString());
166                                                        });
167                                                        break;
168                                                case "pattern":
169                                                        if (!pattrnbuffer) {
170                                                                pattrnbuffer = document.createElement("canvas");
171                                                        }
172                                                        // no need to scale the image since the canvas.createPattern uses
173                                                        // the original image data and not the scaled ones (see spec.)
174                                                        // the scaling needs to be done at rendering time in a context buffer
175                                                        var img =new Image();
176                                                        this.surface.downloadImage(img, fs.src);
177                                                        this.canvasFillImage = img;
178                                        }
179                                }else{
180                                        // Set fill color using CSS RGBA func style
181                                        f = fs.toString();
182                                }
183                                this.canvasFill = f;
184                        }else{
185                                delete this.canvasFill;
186                        }
187                });
188
189        modifyMethod(canvas.Shape, "setStroke");
190        modifyMethod(canvas.Shape, "setShape");
191
192        declare("dojox.gfx.canvas.Group", canvas.Shape, {
193                // summary: a group shape (Canvas), which can be used
194                //      to logically group shapes (e.g, to propagate matricies)
195                constructor: function(){
196                        gs.Container._init.call(this);
197                },
198                _render: function(/* Object */ ctx){
199                        // summary: render the group
200                        ctx.save();
201                        this._renderTransform(ctx);
202                        for(var i = 0; i < this.children.length; ++i){
203                                this.children[i]._render(ctx);
204                        }
205                        ctx.restore();
206                }
207        });
208
209        declare("dojox.gfx.canvas.Rect", [canvas.Shape, gs.Rect], {
210                // summary: a rectangle shape (Canvas)
211                _renderShape: function(/* Object */ ctx){
212                        var s = this.shape, r = Math.min(s.r, s.height / 2, s.width / 2),
213                                xl = s.x, xr = xl + s.width, yt = s.y, yb = yt + s.height,
214                                xl2 = xl + r, xr2 = xr - r, yt2 = yt + r, yb2 = yb - r;
215                        ctx.beginPath();
216                        ctx.moveTo(xl2, yt);
217                        if(r){
218                                ctx.arc(xr2, yt2, r, -halfPI, 0, false);
219                                ctx.arc(xr2, yb2, r, 0, halfPI, false);
220                                ctx.arc(xl2, yb2, r, halfPI, pi, false);
221                                ctx.arc(xl2, yt2, r, pi, pi + halfPI, false);
222                        }else{
223                                ctx.lineTo(xr2, yt);
224                                ctx.lineTo(xr, yb2);
225                                ctx.lineTo(xl2, yb);
226                                ctx.lineTo(xl, yt2);
227                        }
228                        ctx.closePath();
229                }
230        });
231
232        var bezierCircle = [];
233        (function(){
234                var u = ga.curvePI4;
235                bezierCircle.push(u.s, u.c1, u.c2, u.e);
236                for(var a = 45; a < 360; a += 45){
237                        var r = m.rotateg(a);
238                        bezierCircle.push(mp(r, u.c1), mp(r, u.c2), mp(r, u.e));
239                }
240        })();
241
242        declare("dojox.gfx.canvas.Ellipse", [canvas.Shape, gs.Ellipse], {
243                // summary: an ellipse shape (Canvas)
244                setShape: function(){
245                        this.inherited(arguments);
246                        // prepare Canvas-specific structures
247                        var s = this.shape, t, c1, c2, r = [],
248                                M = m.normalize([m.translate(s.cx, s.cy), m.scale(s.rx, s.ry)]);
249                        t = mp(M, bezierCircle[0]);
250                        r.push([t.x, t.y]);
251                        for(var i = 1; i < bezierCircle.length; i += 3){
252                                c1 = mp(M, bezierCircle[i]);
253                                c2 = mp(M, bezierCircle[i + 1]);
254                                t  = mp(M, bezierCircle[i + 2]);
255                                r.push([c1.x, c1.y, c2.x, c2.y, t.x, t.y]);
256                        }
257                        this.canvasEllipse = r;
258                        return this;
259                },
260                _renderShape: function(/* Object */ ctx){
261                        var r = this.canvasEllipse;
262                        ctx.beginPath();
263                        ctx.moveTo.apply(ctx, r[0]);
264                        for(var i = 1; i < r.length; ++i){
265                                ctx.bezierCurveTo.apply(ctx, r[i]);
266                        }
267                        ctx.closePath();
268                }
269        });
270
271        declare("dojox.gfx.canvas.Circle", [canvas.Shape, gs.Circle], {
272                // summary: a circle shape (Canvas)
273                _renderShape: function(/* Object */ ctx){
274                        var s = this.shape;
275                        ctx.beginPath();
276                        ctx.arc(s.cx, s.cy, s.r, 0, twoPI, 1);
277                }
278        });
279
280        declare("dojox.gfx.canvas.Line", [canvas.Shape, gs.Line], {
281                // summary: a line shape (Canvas)
282                _renderShape: function(/* Object */ ctx){
283                        var s = this.shape;
284                        ctx.beginPath();
285                        ctx.moveTo(s.x1, s.y1);
286                        ctx.lineTo(s.x2, s.y2);
287                }
288        });
289
290        declare("dojox.gfx.canvas.Polyline", [canvas.Shape, gs.Polyline], {
291                // summary: a polyline/polygon shape (Canvas)
292                setShape: function(){
293                        this.inherited(arguments);
294                        var p = this.shape.points, f = p[0], r, c, i;
295                        this.bbox = null;
296                        // normalize this.shape.points as array of points: [{x,y}, {x,y}, ...]
297                        this._normalizePoints();                       
298                        // after _normalizePoints, if shape.points was [x1,y1,x2,y2,..], shape.points references a new array
299                        // and p references the original points array
300                        // prepare Canvas-specific structures, if needed
301                        if(p.length){ 
302                                if(typeof f == "number"){ // already in the canvas format [x1,y1,x2,y2,...]
303                                        r = p;
304                                }else{ // convert into canvas-specific format
305                                        r = [];
306                                        for(i=0; i < p.length; ++i){
307                                                c = p[i];
308                                                r.push(c.x, c.y);
309                                        }
310                                }
311                        }else{
312                                r = [];
313                        }
314                        this.canvasPolyline = r;
315                        return this;
316                },
317                _renderShape: function(/* Object */ ctx){
318                        var p = this.canvasPolyline;
319                        if(p.length){
320                                ctx.beginPath();
321                                ctx.moveTo(p[0], p[1]);
322                                for(var i = 2; i < p.length; i += 2){
323                                        ctx.lineTo(p[i], p[i + 1]);
324                                }
325                        }
326                }
327        });
328
329        declare("dojox.gfx.canvas.Image", [canvas.Shape, gs.Image], {
330                // summary: an image shape (Canvas)
331                setShape: function(){
332                        this.inherited(arguments);
333                        // prepare Canvas-specific structures
334                        var img = new Image();
335                        this.surface.downloadImage(img, this.shape.src);
336                        this.canvasImage = img;
337                        return this;
338                },
339                _renderShape: function(/* Object */ ctx){
340                        var s = this.shape;
341                        ctx.drawImage(this.canvasImage, s.x, s.y, s.width, s.height);
342                }
343        });
344       
345        declare("dojox.gfx.canvas.Text", [canvas.Shape, gs.Text], {
346                _setFont:function(){
347                        if (this.fontStyle){
348                                this.canvasFont = g.makeFontString(this.fontStyle);
349                        } else {
350                                delete this.canvasFont;
351                        }
352                },
353               
354                getTextWidth: function(){
355                        // summary: get the text width in pixels
356                        var s = this.shape, w = 0, ctx;
357                        if(s.text && s.text.length > 0){
358                                ctx = this.surface.rawNode.getContext("2d");
359                                ctx.save();
360                                this._renderTransform(ctx);
361                                this._renderFill(ctx, false);
362                                this._renderStroke(ctx, false);
363                                if (this.canvasFont)
364                                        ctx.font = this.canvasFont;
365                                w = ctx.measureText(s.text).width;
366                                ctx.restore();
367                        }
368                        return w;
369                },
370               
371                // override to apply first fill and stroke (
372                // the base implementation is for path-based shape that needs to first define the path then to fill/stroke it.
373                // Here, we need the fillstyle or strokestyle to be set before calling fillText/strokeText.
374                _render: function(/* Object */ctx){
375                        // summary: render the shape
376                        // ctx : Object: the drawing context.
377                        ctx.save();
378                        this._renderTransform(ctx);
379                        this._renderFill(ctx, false);
380                        this._renderStroke(ctx, false);
381                        this._renderShape(ctx);
382                        ctx.restore();
383                },
384               
385                _renderShape: function(ctx){
386                        // summary: a text shape (Canvas)
387                        // ctx : Object: the drawing context.
388                        var ta, s = this.shape;
389                        if(!s.text || s.text.length == 0){
390                                return;
391                        }
392                        // text align
393                        ta = s.align === 'middle' ? 'center' : s.align;
394                        ctx.textAlign = ta;
395                        if(this.canvasFont){
396                                ctx.font = this.canvasFont;
397                        }
398                        if(this.canvasFill){
399                                ctx.fillText(s.text, s.x, s.y);
400                        }
401                        if(this.strokeStyle){
402                                ctx.beginPath(); // fix bug in FF3.6. Fixed in FF4b8
403                                ctx.strokeText(s.text, s.x, s.y);
404                                ctx.closePath();
405                        }
406                }
407        });
408        modifyMethod(canvas.Text, "setFont");
409       
410        // the next test is from https://github.com/phiggins42/has.js
411        if(win.global.CanvasRenderingContext2D){
412                // need to doublecheck canvas is supported since module can be loaded if building layers (ticket 14288)
413                var ctx2d = win.doc.createElement("canvas").getContext("2d");
414                if(ctx2d && typeof ctx2d.fillText != "function"){
415                        canvas.Text.extend({
416                                getTextWidth: function(){
417                                        return 0;
418                                },
419                                _renderShape: function(){
420                                }
421                        });
422                }
423        }
424       
425
426        var pathRenderers = {
427                        M: "_moveToA", m: "_moveToR",
428                        L: "_lineToA", l: "_lineToR",
429                        H: "_hLineToA", h: "_hLineToR",
430                        V: "_vLineToA", v: "_vLineToR",
431                        C: "_curveToA", c: "_curveToR",
432                        S: "_smoothCurveToA", s: "_smoothCurveToR",
433                        Q: "_qCurveToA", q: "_qCurveToR",
434                        T: "_qSmoothCurveToA", t: "_qSmoothCurveToR",
435                        A: "_arcTo", a: "_arcTo",
436                        Z: "_closePath", z: "_closePath"
437                };
438
439        declare("dojox.gfx.canvas.Path", [canvas.Shape, pathLib.Path], {
440                // summary: a path shape (Canvas)
441                constructor: function(){
442                        this.lastControl = {};
443                },
444                setShape: function(){
445                        this.canvasPath = [];
446                        return this.inherited(arguments);
447                },
448                _updateWithSegment: function(segment){
449                        var last = lang.clone(this.last);
450                        this[pathRenderers[segment.action]](this.canvasPath, segment.action, segment.args);
451                        this.last = last;
452                        this.inherited(arguments);
453                },
454                _renderShape: function(/* Object */ ctx){
455                        var r = this.canvasPath;
456                        ctx.beginPath();
457                        for(var i = 0; i < r.length; i += 2){
458                                ctx[r[i]].apply(ctx, r[i + 1]);
459                        }
460                },
461                _moveToA: function(result, action, args){
462                        result.push("moveTo", [args[0], args[1]]);
463                        for(var i = 2; i < args.length; i += 2){
464                                result.push("lineTo", [args[i], args[i + 1]]);
465                        }
466                        this.last.x = args[args.length - 2];
467                        this.last.y = args[args.length - 1];
468                        this.lastControl = {};
469                },
470                _moveToR: function(result, action, args){
471                        if("x" in this.last){
472                                result.push("moveTo", [this.last.x += args[0], this.last.y += args[1]]);
473                        }else{
474                                result.push("moveTo", [this.last.x = args[0], this.last.y = args[1]]);
475                        }
476                        for(var i = 2; i < args.length; i += 2){
477                                result.push("lineTo", [this.last.x += args[i], this.last.y += args[i + 1]]);
478                        }
479                        this.lastControl = {};
480                },
481                _lineToA: function(result, action, args){
482                        for(var i = 0; i < args.length; i += 2){
483                                result.push("lineTo", [args[i], args[i + 1]]);
484                        }
485                        this.last.x = args[args.length - 2];
486                        this.last.y = args[args.length - 1];
487                        this.lastControl = {};
488                },
489                _lineToR: function(result, action, args){
490                        for(var i = 0; i < args.length; i += 2){
491                                result.push("lineTo", [this.last.x += args[i], this.last.y += args[i + 1]]);
492                        }
493                        this.lastControl = {};
494                },
495                _hLineToA: function(result, action, args){
496                        for(var i = 0; i < args.length; ++i){
497                                result.push("lineTo", [args[i], this.last.y]);
498                        }
499                        this.last.x = args[args.length - 1];
500                        this.lastControl = {};
501                },
502                _hLineToR: function(result, action, args){
503                        for(var i = 0; i < args.length; ++i){
504                                result.push("lineTo", [this.last.x += args[i], this.last.y]);
505                        }
506                        this.lastControl = {};
507                },
508                _vLineToA: function(result, action, args){
509                        for(var i = 0; i < args.length; ++i){
510                                result.push("lineTo", [this.last.x, args[i]]);
511                        }
512                        this.last.y = args[args.length - 1];
513                        this.lastControl = {};
514                },
515                _vLineToR: function(result, action, args){
516                        for(var i = 0; i < args.length; ++i){
517                                result.push("lineTo", [this.last.x, this.last.y += args[i]]);
518                        }
519                        this.lastControl = {};
520                },
521                _curveToA: function(result, action, args){
522                        for(var i = 0; i < args.length; i += 6){
523                                result.push("bezierCurveTo", args.slice(i, i + 6));
524                        }
525                        this.last.x = args[args.length - 2];
526                        this.last.y = args[args.length - 1];
527                        this.lastControl.x = args[args.length - 4];
528                        this.lastControl.y = args[args.length - 3];
529                        this.lastControl.type = "C";
530                },
531                _curveToR: function(result, action, args){
532                        for(var i = 0; i < args.length; i += 6){
533                                result.push("bezierCurveTo", [
534                                        this.last.x + args[i],
535                                        this.last.y + args[i + 1],
536                                        this.lastControl.x = this.last.x + args[i + 2],
537                                        this.lastControl.y = this.last.y + args[i + 3],
538                                        this.last.x + args[i + 4],
539                                        this.last.y + args[i + 5]
540                                ]);
541                                this.last.x += args[i + 4];
542                                this.last.y += args[i + 5];
543                        }
544                        this.lastControl.type = "C";
545                },
546                _smoothCurveToA: function(result, action, args){
547                        for(var i = 0; i < args.length; i += 4){
548                                var valid = this.lastControl.type == "C";
549                                result.push("bezierCurveTo", [
550                                        valid ? 2 * this.last.x - this.lastControl.x : this.last.x,
551                                        valid ? 2 * this.last.y - this.lastControl.y : this.last.y,
552                                        args[i],
553                                        args[i + 1],
554                                        args[i + 2],
555                                        args[i + 3]
556                                ]);
557                                this.lastControl.x = args[i];
558                                this.lastControl.y = args[i + 1];
559                                this.lastControl.type = "C";
560                        }
561                        this.last.x = args[args.length - 2];
562                        this.last.y = args[args.length - 1];
563                },
564                _smoothCurveToR: function(result, action, args){
565                        for(var i = 0; i < args.length; i += 4){
566                                var valid = this.lastControl.type == "C";
567                                result.push("bezierCurveTo", [
568                                        valid ? 2 * this.last.x - this.lastControl.x : this.last.x,
569                                        valid ? 2 * this.last.y - this.lastControl.y : this.last.y,
570                                        this.last.x + args[i],
571                                        this.last.y + args[i + 1],
572                                        this.last.x + args[i + 2],
573                                        this.last.y + args[i + 3]
574                                ]);
575                                this.lastControl.x = this.last.x + args[i];
576                                this.lastControl.y = this.last.y + args[i + 1];
577                                this.lastControl.type = "C";
578                                this.last.x += args[i + 2];
579                                this.last.y += args[i + 3];
580                        }
581                },
582                _qCurveToA: function(result, action, args){
583                        for(var i = 0; i < args.length; i += 4){
584                                result.push("quadraticCurveTo", args.slice(i, i + 4));
585                        }
586                        this.last.x = args[args.length - 2];
587                        this.last.y = args[args.length - 1];
588                        this.lastControl.x = args[args.length - 4];
589                        this.lastControl.y = args[args.length - 3];
590                        this.lastControl.type = "Q";
591                },
592                _qCurveToR: function(result, action, args){
593                        for(var i = 0; i < args.length; i += 4){
594                                result.push("quadraticCurveTo", [
595                                        this.lastControl.x = this.last.x + args[i],
596                                        this.lastControl.y = this.last.y + args[i + 1],
597                                        this.last.x + args[i + 2],
598                                        this.last.y + args[i + 3]
599                                ]);
600                                this.last.x += args[i + 2];
601                                this.last.y += args[i + 3];
602                        }
603                        this.lastControl.type = "Q";
604                },
605                _qSmoothCurveToA: function(result, action, args){
606                        for(var i = 0; i < args.length; i += 2){
607                                var valid = this.lastControl.type == "Q";
608                                result.push("quadraticCurveTo", [
609                                        this.lastControl.x = valid ? 2 * this.last.x - this.lastControl.x : this.last.x,
610                                        this.lastControl.y = valid ? 2 * this.last.y - this.lastControl.y : this.last.y,
611                                        args[i],
612                                        args[i + 1]
613                                ]);
614                                this.lastControl.type = "Q";
615                        }
616                        this.last.x = args[args.length - 2];
617                        this.last.y = args[args.length - 1];
618                },
619                _qSmoothCurveToR: function(result, action, args){
620                        for(var i = 0; i < args.length; i += 2){
621                                var valid = this.lastControl.type == "Q";
622                                result.push("quadraticCurveTo", [
623                                        this.lastControl.x = valid ? 2 * this.last.x - this.lastControl.x : this.last.x,
624                                        this.lastControl.y = valid ? 2 * this.last.y - this.lastControl.y : this.last.y,
625                                        this.last.x + args[i],
626                                        this.last.y + args[i + 1]
627                                ]);
628                                this.lastControl.type = "Q";
629                                this.last.x += args[i];
630                                this.last.y += args[i + 1];
631                        }
632                },
633                _arcTo: function(result, action, args){
634                        var relative = action == "a";
635                        for(var i = 0; i < args.length; i += 7){
636                                var x1 = args[i + 5], y1 = args[i + 6];
637                                if(relative){
638                                        x1 += this.last.x;
639                                        y1 += this.last.y;
640                                }
641                                var arcs = ga.arcAsBezier(
642                                        this.last, args[i], args[i + 1], args[i + 2],
643                                        args[i + 3] ? 1 : 0, args[i + 4] ? 1 : 0,
644                                        x1, y1
645                                );
646                                arr.forEach(arcs, function(p){
647                                        result.push("bezierCurveTo", p);
648                                });
649                                this.last.x = x1;
650                                this.last.y = y1;
651                        }
652                        this.lastControl = {};
653                },
654                _closePath: function(result, action, args){
655                        result.push("closePath", []);
656                        this.lastControl = {};
657                }
658        });
659        arr.forEach(["moveTo", "lineTo", "hLineTo", "vLineTo", "curveTo",
660                "smoothCurveTo", "qCurveTo", "qSmoothCurveTo", "arcTo", "closePath"],
661                function(method){ modifyMethod(canvas.Path, method); }
662        );
663
664        declare("dojox.gfx.canvas.TextPath", [canvas.Shape, pathLib.TextPath], {
665                // summary: a text shape (Canvas)
666                _renderShape: function(/* Object */ ctx){
667                        var s = this.shape;
668                        // nothing for the moment
669                },
670                _setText: function(){
671                        // not implemented
672                },
673                _setFont: function(){
674                        // not implemented
675                }
676        });
677
678        declare("dojox.gfx.canvas.Surface", gs.Surface, {
679                // summary: a surface object to be used for drawings (Canvas)
680                constructor: function(){
681                        gs.Container._init.call(this);
682                        this.pendingImageCount = 0;
683                        this.makeDirty();
684                },
685                setDimensions: function(width, height){
686                        // summary: sets the width and height of the rawNode
687                        // width: String: width of surface, e.g., "100px"
688                        // height: String: height of surface, e.g., "100px"
689                        this.width  = g.normalizedLength(width);        // in pixels
690                        this.height = g.normalizedLength(height);       // in pixels
691                        if(!this.rawNode) return this;
692                        var dirty = false;
693                        if (this.rawNode.width != this.width){
694                                this.rawNode.width = this.width;
695                                dirty = true;
696                        }
697                        if (this.rawNode.height != this.height){
698                                this.rawNode.height = this.height;
699                                dirty = true;
700                        }
701                        if (dirty)
702                                this.makeDirty();
703                        return this;    // self
704                },
705                getDimensions: function(){
706                        // summary: returns an object with properties "width" and "height"
707                        return this.rawNode ? {width:  this.rawNode.width, height: this.rawNode.height} : null; // Object
708                },
709                _render: function(){
710                        // summary: render the all shapes
711                        if(this.pendingImageCount){ return; }
712                        var ctx = this.rawNode.getContext("2d");
713                        ctx.save();
714                        ctx.clearRect(0, 0, this.rawNode.width, this.rawNode.height);
715                        for(var i = 0; i < this.children.length; ++i){
716                                this.children[i]._render(ctx);
717                        }
718                        ctx.restore();
719                        if("pendingRender" in this){
720                                clearTimeout(this.pendingRender);
721                                delete this.pendingRender;
722                        }
723                },
724                makeDirty: function(){
725                        // summary: internal method, which is called when we may need to redraw
726                        if(!this.pendingImagesCount && !("pendingRender" in this)){
727                                this.pendingRender = setTimeout(lang.hitch(this, this._render), 0);
728                        }
729                },
730                downloadImage: function(img, url){
731                        // summary:
732                        //              internal method, which starts an image download and renders, when it is ready
733                        // img: Image:
734                        //              the image object
735                        // url: String:
736                        //              the url of the image
737                        var handler = lang.hitch(this, this.onImageLoad);
738                        if(!this.pendingImageCount++ && "pendingRender" in this){
739                                clearTimeout(this.pendingRender);
740                                delete this.pendingRender;
741                        }
742                        img.onload  = handler;
743                        img.onerror = handler;
744                        img.onabort = handler;
745                        img.src = url;
746                },
747                onImageLoad: function(){
748                        if(!--this.pendingImageCount){ this._render(); }
749                },
750
751                // events are not implemented
752                getEventSource: function(){ return null; },
753                connect:                function(){},
754                disconnect:             function(){}
755        });
756
757        canvas.createSurface = function(parentNode, width, height){
758                // summary: creates a surface (Canvas)
759                // parentNode: Node: a parent node
760                // width: String: width of surface, e.g., "100px"
761                // height: String: height of surface, e.g., "100px"
762
763                if(!width && !height){
764                        var pos = domGeom.position(parentNode);
765                        width  = width  || pos.w;
766                        height = height || pos.h;
767                }
768                if(typeof width == "number"){
769                        width = width + "px";
770                }
771                if(typeof height == "number"){
772                        height = height + "px";
773                }
774
775                var s = new canvas.Surface(),
776                        p = dom.byId(parentNode),
777                        c = p.ownerDocument.createElement("canvas");
778
779                c.width  = g.normalizedLength(width);   // in pixels
780                c.height = g.normalizedLength(height);  // in pixels
781
782                p.appendChild(c);
783                s.rawNode = c;
784                s._parent = p;
785                s.surface = s;
786                return s;       // dojox.gfx.Surface
787        };
788
789        // Extenders
790
791        var C = gs.Container, Container = {
792                add: function(shape){
793                        this.surface.makeDirty();
794                        return C.add.apply(this, arguments);
795                },
796                remove: function(shape, silently){
797                        this.surface.makeDirty();
798                        return C.remove.apply(this, arguments);
799                },
800                clear: function(){
801                        this.surface.makeDirty();
802                        return C.clear.apply(this, arguments);
803                },
804                _moveChildToFront: function(shape){
805                        this.surface.makeDirty();
806                        return C._moveChildToFront.apply(this, arguments);
807                },
808                _moveChildToBack: function(shape){
809                        this.surface.makeDirty();
810                        return C._moveChildToBack.apply(this, arguments);
811                }
812        };
813
814        var Creator = {
815                // summary: Canvas shape creators
816                createObject: function(shapeType, rawShape) {
817                        // summary: creates an instance of the passed shapeType class
818                        // shapeType: Function: a class constructor to create an instance of
819                        // rawShape: Object: properties to be passed in to the classes "setShape" method
820                        // overrideSize: Boolean: set the size explicitly, if true
821                        var shape = new shapeType();
822                        shape.surface = this.surface;
823                        shape.setShape(rawShape);
824                        this.add(shape);
825                        return shape;   // dojox.gfx.Shape
826                }
827        };
828
829        extend(canvas.Group, Container);
830        extend(canvas.Group, gs.Creator);
831        extend(canvas.Group, Creator);
832
833        extend(canvas.Surface, Container);
834        extend(canvas.Surface, gs.Creator);
835        extend(canvas.Surface, Creator);
836       
837        // no event support -> nothing to fix.
838        canvas.fixTarget = function(event, gfxElement){
839                return true;
840        };
841         
842        return canvas;
843});
Note: See TracBrowser for help on using the repository browser.