1var canvas;
2var ctx;
3var canvasGradients = {};
4
5function canvas_rbga(color) {
6    var a = canvas_opacity(color);
7    var r = (color >> 16) & 0xFF;
8    var g = (color >>  8) & 0xFF;
9    var b = (color >>  0) & 0xFF;
10    return "rgba(" + r + "," + g + "," + b + "," + a + ")";
11}
12
13function canvas_opacity(color) {
14    var a = (color >> 24) & 0xFF;
15    return a / 255.;
16}
17
18function displayCanvas(displayList) {
19    if (displayList.clear) {
20        ctx.clearRect(0, 0, canvas.width, canvas.height);
21    }
22    for (var index = 0; index < displayList.length; ++index) {
23        drawToCanvas(displayList[index]);
24    }
25}
26
27function drawToCanvas(action) {
28    ctx.save();
29    var paint = paintToCanvas(action.paint);
30    var draw = action.draw;
31    if ('string' == typeof(draw)) {
32        draw = (new Function("return " + draw))();
33    }
34    if (isArray(draw)) {
35        assert(draw.length > 0);
36        var picture = 'draw' in draw[0];
37        if (picture) {
38            for (var index = 0; index < draw.length; ++index) {
39                drawToCanvas(draw[index]);
40            }
41            return;
42        }
43        ctx.beginPath();
44        for (var index = 0; index < draw.length; ++index) {
45            for (var prop in draw[index]) {
46                var v = draw[index][prop];
47                switch (prop) {
48                    case 'arcTo':
49                        ctx.arcTo(v[0], v[1], v[2], v[3], v[4]);
50                        break;
51                    case 'close':
52                        ctx.closePath();
53                        break;
54                    case 'cubic':
55                        ctx.moveTo(v[0], v[1]);
56                        ctx.bezierCurveTo(v[2], v[3], v[4], v[5], v[6], v[7]);
57                        break;
58                    case 'line':
59                        ctx.moveTo(v[0], v[1]);
60                        ctx.lineTo(v[2], v[3]);
61                        break;
62                    case 'quad':
63                        ctx.moveTo(v[0], v[1]);
64                        ctx.quadraticCurveTo(v[2], v[3], v[4], v[5]);
65                        break;
66                    default:
67                        assert(0);
68                }
69            }
70        }
71        if ('fill' == paint.style) {
72            ctx.fill();
73        } else {
74            assert('stroke' == paint.style);
75            ctx.stroke();
76        }
77    } else {
78        assert('string' in draw);
79        if ('fill' == paint.style) {
80            ctx.fillText(draw.string, draw.x, draw.y);
81        } else {
82            assert('stroke' == paint.style);
83            ctx.strokeText(draw.string, draw.x, draw.y);
84        }
85    }
86    ctx.restore();
87}
88
89function keyframeCanvasInit(displayList, first) {
90    if ('canvas' in first && 'clear' == first.canvas) {
91        displayList.clear = true;
92    }
93}
94
95function paintToCanvas(paint) {
96    var color;
97    var inPicture = 'string' == typeof(paint);
98    if (inPicture) {
99        paint = (new Function("return " + paint))();
100        assert('object' == typeof(paint) && !isArray(paint));
101    }
102    if ('gradient' in paint) {
103        var gradient = paint.gradient.split('.');
104        var gradName = gradient[1];
105        if (!canvasGradients[gradName]) {
106            var g = window[gradient[0]][gradient[1]];
107            var grad = ctx.createRadialGradient(g.cx, g.cy, 0, g.cx, g.cy, g.r);
108            var stopLen = g.stops.length;
109            for (var index = 0; index < stopLen; ++index) {
110                var stop = g.stops[index];
111                var color = canvas_rbga(stop.color);
112                grad.addColorStop(index, color);
113            }
114            canvasGradients[gradName] = grad;
115        }
116        color = canvasGradients[gradName];
117        if (!inPicture) {
118            ctx.globalAlpha = canvas_opacity(paint.color);
119        }
120    } else {
121        color = canvas_rbga(paint.color);
122    }
123    if ('fill' == paint.style) {
124        ctx.fillStyle = color;
125    } else if ('stroke' == paint.style) {
126        ctx.strokeStyle = color;
127    } else {
128        ctx.globalAlpha = canvas_opacity(paint.color);
129    }
130    if ('strokeWidth' in paint) {
131        ctx.lineWidth = paint.strokeWidth;
132    }
133    if ('typeface' in paint) {
134        var typeface = typefaces[paint.typeface];
135        var font = typeface.style;
136        if ('textSize' in paint) {
137            font += " " + paint.textSize;
138        }
139        if ('family' in typeface) {
140            font += " " + typeface.family;
141        }
142        ctx.font = font;
143        if ('textAlign' in paint) {
144            ctx.textAlign = paint.textAlign;
145        }
146        if ('textBaseline' in paint) {
147            ctx.textBaseline = paint.textBaseline;
148        }
149    }
150    return paint;
151}
152
153function setupCanvas() {
154    canvas = document.getElementById("canvas");
155    ctx = canvas ? canvas.getContext("2d") : null;
156    assert(ctx);
157    var resScale = window.devicePixelRatio ? window.devicePixelRatio : 1;
158    var unscaledWidth = canvas.width;
159    var unscaledHeight = canvas.height;
160    canvas.width = unscaledWidth * resScale;
161    canvas.height = unscaledHeight * resScale;
162    canvas.style.width = unscaledWidth + 'px';
163    canvas.style.height = unscaledHeight + 'px';
164    if (resScale != 1) {
165        ctx.scale(resScale, resScale);
166    }
167}
168