1 /*
2  * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "config.h"
21 #include "JSCanvasRenderingContext2D.h"
22 
23 #include "CanvasGradient.h"
24 #include "CanvasPattern.h"
25 #include "CanvasRenderingContext2D.h"
26 #include "CanvasStyle.h"
27 #include "ExceptionCode.h"
28 #include "FloatRect.h"
29 #include "HTMLCanvasElement.h"
30 #include "HTMLImageElement.h"
31 #include "HTMLVideoElement.h"
32 #include "ImageData.h"
33 #include "JSCanvasGradient.h"
34 #include "JSCanvasPattern.h"
35 #include "JSHTMLCanvasElement.h"
36 #include "JSHTMLImageElement.h"
37 #include "JSHTMLVideoElement.h"
38 #include "JSImageData.h"
39 #include <runtime/Error.h>
40 
41 using namespace JSC;
42 
43 namespace WebCore {
44 
toJS(ExecState * exec,JSDOMGlobalObject * globalObject,CanvasStyle * style)45 static JSValue toJS(ExecState* exec, JSDOMGlobalObject* globalObject, CanvasStyle* style)
46 {
47     if (style->canvasGradient())
48         return toJS(exec, globalObject, style->canvasGradient());
49     if (style->canvasPattern())
50         return toJS(exec, globalObject, style->canvasPattern());
51     return jsString(exec, style->color());
52 }
53 
toHTMLCanvasStyle(ExecState *,JSValue value)54 static PassRefPtr<CanvasStyle> toHTMLCanvasStyle(ExecState*, JSValue value)
55 {
56     if (!value.isObject())
57         return 0;
58     JSObject* object = asObject(value);
59     if (object->inherits(&JSCanvasGradient::s_info))
60         return CanvasStyle::createFromGradient(static_cast<JSCanvasGradient*>(object)->impl());
61     if (object->inherits(&JSCanvasPattern::s_info))
62         return CanvasStyle::createFromPattern(static_cast<JSCanvasPattern*>(object)->impl());
63     return 0;
64 }
65 
strokeStyle(ExecState * exec) const66 JSValue JSCanvasRenderingContext2D::strokeStyle(ExecState* exec) const
67 {
68     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
69     return toJS(exec, globalObject(), context->strokeStyle());
70 }
71 
setStrokeStyle(ExecState * exec,JSValue value)72 void JSCanvasRenderingContext2D::setStrokeStyle(ExecState* exec, JSValue value)
73 {
74     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
75     if (value.isString()) {
76         context->setStrokeColor(ustringToString(asString(value)->value(exec)));
77         return;
78     }
79     context->setStrokeStyle(toHTMLCanvasStyle(exec, value));
80 }
81 
fillStyle(ExecState * exec) const82 JSValue JSCanvasRenderingContext2D::fillStyle(ExecState* exec) const
83 {
84     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
85     return toJS(exec, globalObject(), context->fillStyle());
86 }
87 
setFillStyle(ExecState * exec,JSValue value)88 void JSCanvasRenderingContext2D::setFillStyle(ExecState* exec, JSValue value)
89 {
90     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
91     if (value.isString()) {
92         context->setFillColor(ustringToString(asString(value)->value(exec)));
93         return;
94     }
95     context->setFillStyle(toHTMLCanvasStyle(exec, value));
96 }
97 
setFillColor(ExecState * exec)98 JSValue JSCanvasRenderingContext2D::setFillColor(ExecState* exec)
99 {
100     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
101 
102     // string arg = named color
103     // number arg = gray color
104     // string arg, number arg = named color, alpha
105     // number arg, number arg = gray color, alpha
106     // 4 args = r, g, b, a
107     // 5 args = c, m, y, k, a
108     switch (exec->argumentCount()) {
109         case 1:
110             if (exec->argument(0).isString())
111                 context->setFillColor(ustringToString(asString(exec->argument(0))->value(exec)));
112             else
113                 context->setFillColor(exec->argument(0).toFloat(exec));
114             break;
115         case 2:
116             if (exec->argument(0).isString())
117                 context->setFillColor(ustringToString(asString(exec->argument(0))->value(exec)), exec->argument(1).toFloat(exec));
118             else
119                 context->setFillColor(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec));
120             break;
121         case 4:
122             context->setFillColor(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
123                                   exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
124             break;
125         case 5:
126             context->setFillColor(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
127                                   exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec));
128             break;
129         default:
130             return throwSyntaxError(exec);
131     }
132     return jsUndefined();
133 }
134 
setStrokeColor(ExecState * exec)135 JSValue JSCanvasRenderingContext2D::setStrokeColor(ExecState* exec)
136 {
137     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
138 
139     // string arg = named color
140     // number arg = gray color
141     // string arg, number arg = named color, alpha
142     // number arg, number arg = gray color, alpha
143     // 4 args = r, g, b, a
144     // 5 args = c, m, y, k, a
145     switch (exec->argumentCount()) {
146         case 1:
147             if (exec->argument(0).isString())
148                 context->setStrokeColor(ustringToString(asString(exec->argument(0))->value(exec)));
149             else
150                 context->setStrokeColor(exec->argument(0).toFloat(exec));
151             break;
152         case 2:
153             if (exec->argument(0).isString())
154                 context->setStrokeColor(ustringToString(asString(exec->argument(0))->value(exec)), exec->argument(1).toFloat(exec));
155             else
156                 context->setStrokeColor(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec));
157             break;
158         case 4:
159             context->setStrokeColor(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
160                                     exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
161             break;
162         case 5:
163             context->setStrokeColor(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
164                                     exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec));
165             break;
166         default:
167             return throwSyntaxError(exec);
168     }
169 
170     return jsUndefined();
171 }
172 
setLineDash(ExecState * exec)173 JSValue JSCanvasRenderingContext2D::setLineDash(ExecState* exec)
174 {
175     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
176     JSValue value = exec->argument(0);
177     JSObject* array = asObject(value);
178     uint32_t length = array->get(exec, JSC::Identifier(exec, "length")).toInt32(exec);
179     DashArray lineDash;
180     for(uint32_t i = 0; i < length; i++) {
181         JSValue v = array->get(exec, i);
182         lineDash.append(v.toNumber(exec));
183     }
184     context->setLineDash(lineDash, lineDash[0]);
185     return jsUndefined();
186 }
187 
strokeRect(ExecState * exec)188 JSValue JSCanvasRenderingContext2D::strokeRect(ExecState* exec)
189 {
190     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
191 
192     if (exec->argumentCount() <= 4)
193         context->strokeRect(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
194                             exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
195     else
196         context->strokeRect(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
197                             exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec));
198 
199     return jsUndefined();
200 }
201 
drawImage(ExecState * exec)202 JSValue JSCanvasRenderingContext2D::drawImage(ExecState* exec)
203 {
204     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
205 
206     // DrawImage has three variants:
207     //     drawImage(img, dx, dy)
208     //     drawImage(img, dx, dy, dw, dh)
209     //     drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)
210     // Composite operation is specified with globalCompositeOperation.
211     // The img parameter can be a <img> or <canvas> element.
212     JSValue value = exec->argument(0);
213     if (value.isNull()) {
214         setDOMException(exec, TYPE_MISMATCH_ERR);
215         return jsUndefined();
216     }
217     if (!value.isObject())
218         return throwTypeError(exec);
219 
220     JSObject* o = asObject(value);
221     ExceptionCode ec = 0;
222     if (o->inherits(&JSHTMLImageElement::s_info)) {
223         HTMLImageElement* imgElt = static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl());
224         switch (exec->argumentCount()) {
225             case 3:
226                 context->drawImage(imgElt, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), ec);
227                 break;
228             case 5:
229                 context->drawImage(imgElt, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
230                                    exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec), ec);
231                 setDOMException(exec, ec);
232                 break;
233             case 9:
234                 context->drawImage(imgElt, FloatRect(exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
235                                    exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec)),
236                                    FloatRect(exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec),
237                                    exec->argument(7).toFloat(exec), exec->argument(8).toFloat(exec)), ec);
238                 setDOMException(exec, ec);
239                 break;
240             default:
241                 return throwSyntaxError(exec);
242         }
243     } else if (o->inherits(&JSHTMLCanvasElement::s_info)) {
244         HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(static_cast<JSHTMLElement*>(o)->impl());
245         switch (exec->argumentCount()) {
246             case 3:
247                 context->drawImage(canvas, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), ec);
248                 setDOMException(exec, ec);
249                 break;
250             case 5:
251                 context->drawImage(canvas, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
252                                    exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec), ec);
253                 setDOMException(exec, ec);
254                 break;
255             case 9:
256                 context->drawImage(canvas, FloatRect(exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
257                                    exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec)),
258                                    FloatRect(exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec),
259                                    exec->argument(7).toFloat(exec), exec->argument(8).toFloat(exec)), ec);
260                 setDOMException(exec, ec);
261                 break;
262             default:
263                 return throwSyntaxError(exec);
264         }
265 #if ENABLE(VIDEO)
266     } else if (o->inherits(&JSHTMLVideoElement::s_info)) {
267             HTMLVideoElement* video = static_cast<HTMLVideoElement*>(static_cast<JSHTMLElement*>(o)->impl());
268             switch (exec->argumentCount()) {
269                 case 3:
270                     context->drawImage(video, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), ec);
271                     break;
272                 case 5:
273                     context->drawImage(video, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
274                                        exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec), ec);
275                     setDOMException(exec, ec);
276                     break;
277                 case 9:
278                     context->drawImage(video, FloatRect(exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
279                                        exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec)),
280                                        FloatRect(exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec),
281                                        exec->argument(7).toFloat(exec), exec->argument(8).toFloat(exec)), ec);
282                     setDOMException(exec, ec);
283                     break;
284                 default:
285                     return throwSyntaxError(exec);
286         }
287 #endif
288     } else
289         return throwTypeError(exec);
290 
291     return jsUndefined();
292 }
293 
drawImageFromRect(ExecState * exec)294 JSValue JSCanvasRenderingContext2D::drawImageFromRect(ExecState* exec)
295 {
296     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
297 
298     JSValue value = exec->argument(0);
299     if (!value.isObject())
300         return throwTypeError(exec);
301     JSObject* o = asObject(value);
302 
303     if (!o->inherits(&JSHTMLImageElement::s_info))
304         return throwTypeError(exec);
305     context->drawImageFromRect(static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl()),
306                                exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
307                                exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec),
308                                exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec),
309                                exec->argument(7).toFloat(exec), exec->argument(8).toFloat(exec),
310                                ustringToString(exec->argument(9).toString(exec)));
311     return jsUndefined();
312 }
313 
setShadow(ExecState * exec)314 JSValue JSCanvasRenderingContext2D::setShadow(ExecState* exec)
315 {
316     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
317 
318     switch (exec->argumentCount()) {
319         case 3:
320             context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
321                                exec->argument(2).toFloat(exec));
322             break;
323         case 4:
324             if (exec->argument(3).isString())
325                 context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
326                                    exec->argument(2).toFloat(exec), ustringToString(asString(exec->argument(3))->value(exec)));
327             else
328                 context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
329                                    exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
330             break;
331         case 5:
332             if (exec->argument(3).isString())
333                 context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
334                                    exec->argument(2).toFloat(exec), ustringToString(asString(exec->argument(3))->value(exec)),
335                                    exec->argument(4).toFloat(exec));
336             else
337                 context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
338                                    exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec),
339                                    exec->argument(4).toFloat(exec));
340             break;
341         case 7:
342             context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
343                                exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec),
344                                exec->argument(4).toFloat(exec), exec->argument(5).toFloat(exec),
345                                exec->argument(6).toFloat(exec));
346             break;
347         case 8:
348             context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
349                                exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec),
350                                exec->argument(4).toFloat(exec), exec->argument(5).toFloat(exec),
351                                exec->argument(6).toFloat(exec), exec->argument(7).toFloat(exec));
352             break;
353         default:
354             return throwSyntaxError(exec);
355     }
356 
357     return jsUndefined();
358 }
359 
createPattern(ExecState * exec)360 JSValue JSCanvasRenderingContext2D::createPattern(ExecState* exec)
361 {
362     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
363 
364     JSValue value = exec->argument(0);
365     if (!value.isObject()) {
366         setDOMException(exec, TYPE_MISMATCH_ERR);
367         return jsUndefined();
368     }
369     JSObject* o = asObject(value);
370 
371     if (o->inherits(&JSHTMLImageElement::s_info)) {
372         ExceptionCode ec;
373         JSValue pattern = toJS(exec, globalObject(),
374             context->createPattern(static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl()),
375                                    valueToStringWithNullCheck(exec, exec->argument(1)), ec).get());
376         setDOMException(exec, ec);
377         return pattern;
378     }
379     if (o->inherits(&JSHTMLCanvasElement::s_info)) {
380         ExceptionCode ec;
381         JSValue pattern = toJS(exec, globalObject(),
382             context->createPattern(static_cast<HTMLCanvasElement*>(static_cast<JSHTMLElement*>(o)->impl()),
383                 valueToStringWithNullCheck(exec, exec->argument(1)), ec).get());
384         setDOMException(exec, ec);
385         return pattern;
386     }
387     setDOMException(exec, TYPE_MISMATCH_ERR);
388     return jsUndefined();
389 }
390 
createImageData(ExecState * exec)391 JSValue JSCanvasRenderingContext2D::createImageData(ExecState* exec)
392 {
393     // createImageData has two variants
394     // createImageData(ImageData)
395     // createImageData(width, height)
396     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
397     RefPtr<ImageData> imageData = 0;
398 
399     ExceptionCode ec = 0;
400     if (exec->argumentCount() == 1)
401         imageData = context->createImageData(toImageData(exec->argument(0)), ec);
402     else if (exec->argumentCount() == 2)
403         imageData = context->createImageData(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec), ec);
404 
405     setDOMException(exec, ec);
406     return toJS(exec, globalObject(), WTF::getPtr(imageData));
407 }
408 
putImageData(ExecState * exec)409 JSValue JSCanvasRenderingContext2D::putImageData(ExecState* exec)
410 {
411     // putImageData has two variants
412     // putImageData(ImageData, x, y)
413     // putImageData(ImageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight)
414     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
415 
416     ExceptionCode ec = 0;
417     if (exec->argumentCount() >= 7)
418         context->putImageData(toImageData(exec->argument(0)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
419                               exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec), exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec), ec);
420     else
421         context->putImageData(toImageData(exec->argument(0)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), ec);
422 
423     setDOMException(exec, ec);
424     return jsUndefined();
425 }
426 
fillText(ExecState * exec)427 JSValue JSCanvasRenderingContext2D::fillText(ExecState* exec)
428 {
429     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
430 
431     // string arg = text to draw
432     // number arg = x
433     // number arg = y
434     // optional number arg = maxWidth
435     if (exec->argumentCount() < 3 || exec->argumentCount() > 4)
436         return throwSyntaxError(exec);
437 
438     if (exec->argumentCount() == 4)
439         context->fillText(ustringToString(exec->argument(0).toString(exec)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
440     else
441         context->fillText(ustringToString(exec->argument(0).toString(exec)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec));
442     return jsUndefined();
443 }
444 
strokeText(ExecState * exec)445 JSValue JSCanvasRenderingContext2D::strokeText(ExecState* exec)
446 {
447     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
448 
449     // string arg = text to draw
450     // number arg = x
451     // number arg = y
452     // optional number arg = maxWidth
453     if (exec->argumentCount() < 3 || exec->argumentCount() > 4)
454         return throwSyntaxError(exec);
455 
456     if (exec->argumentCount() == 4)
457         context->strokeText(ustringToString(exec->argument(0).toString(exec)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
458     else
459         context->strokeText(ustringToString(exec->argument(0).toString(exec)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec));
460     return jsUndefined();
461 }
462 
463 } // namespace WebCore
464