Home | History | Annotate | Download | only in js
      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 
     45 static JSValue toJS(ExecState* exec, CanvasStyle* style)
     46 {
     47     if (style->canvasGradient())
     48         return toJS(exec, style->canvasGradient());
     49     if (style->canvasPattern())
     50         return toJS(exec, style->canvasPattern());
     51     return jsString(exec, style->color());
     52 }
     53 
     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 
     66 JSValue JSCanvasRenderingContext2D::strokeStyle(ExecState* exec) const
     67 {
     68     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
     69     return toJS(exec, context->strokeStyle());
     70 }
     71 
     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 
     82 JSValue JSCanvasRenderingContext2D::fillStyle(ExecState* exec) const
     83 {
     84     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
     85     return toJS(exec, context->fillStyle());
     86 }
     87 
     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 
     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 
    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 
    173 JSValue JSCanvasRenderingContext2D::strokeRect(ExecState* exec)
    174 {
    175     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    176 
    177     if (exec->argumentCount() <= 4)
    178         context->strokeRect(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    179                             exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
    180     else
    181         context->strokeRect(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    182                             exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec));
    183 
    184     return jsUndefined();
    185 }
    186 
    187 JSValue JSCanvasRenderingContext2D::drawImage(ExecState* exec)
    188 {
    189     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    190 
    191     // DrawImage has three variants:
    192     //     drawImage(img, dx, dy)
    193     //     drawImage(img, dx, dy, dw, dh)
    194     //     drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)
    195     // Composite operation is specified with globalCompositeOperation.
    196     // The img parameter can be a <img> or <canvas> element.
    197     JSValue value = exec->argument(0);
    198     if (value.isNull()) {
    199         setDOMException(exec, TYPE_MISMATCH_ERR);
    200         return jsUndefined();
    201     }
    202     if (!value.isObject())
    203         return throwTypeError(exec);
    204 
    205     JSObject* o = asObject(value);
    206     ExceptionCode ec = 0;
    207     if (o->inherits(&JSHTMLImageElement::s_info)) {
    208         HTMLImageElement* imgElt = static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl());
    209         switch (exec->argumentCount()) {
    210             case 3:
    211                 context->drawImage(imgElt, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), ec);
    212                 break;
    213             case 5:
    214                 context->drawImage(imgElt, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
    215                                    exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec), ec);
    216                 setDOMException(exec, ec);
    217                 break;
    218             case 9:
    219                 context->drawImage(imgElt, FloatRect(exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
    220                                    exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec)),
    221                                    FloatRect(exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec),
    222                                    exec->argument(7).toFloat(exec), exec->argument(8).toFloat(exec)), ec);
    223                 setDOMException(exec, ec);
    224                 break;
    225             default:
    226                 return throwSyntaxError(exec);
    227         }
    228     } else if (o->inherits(&JSHTMLCanvasElement::s_info)) {
    229         HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(static_cast<JSHTMLElement*>(o)->impl());
    230         switch (exec->argumentCount()) {
    231             case 3:
    232                 context->drawImage(canvas, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), ec);
    233                 setDOMException(exec, ec);
    234                 break;
    235             case 5:
    236                 context->drawImage(canvas, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
    237                                    exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec), ec);
    238                 setDOMException(exec, ec);
    239                 break;
    240             case 9:
    241                 context->drawImage(canvas, FloatRect(exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
    242                                    exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec)),
    243                                    FloatRect(exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec),
    244                                    exec->argument(7).toFloat(exec), exec->argument(8).toFloat(exec)), ec);
    245                 setDOMException(exec, ec);
    246                 break;
    247             default:
    248                 return throwSyntaxError(exec);
    249         }
    250 #if ENABLE(VIDEO)
    251     } else if (o->inherits(&JSHTMLVideoElement::s_info)) {
    252             HTMLVideoElement* video = static_cast<HTMLVideoElement*>(static_cast<JSHTMLElement*>(o)->impl());
    253             switch (exec->argumentCount()) {
    254                 case 3:
    255                     context->drawImage(video, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), ec);
    256                     break;
    257                 case 5:
    258                     context->drawImage(video, exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
    259                                        exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec), ec);
    260                     setDOMException(exec, ec);
    261                     break;
    262                 case 9:
    263                     context->drawImage(video, FloatRect(exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
    264                                        exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec)),
    265                                        FloatRect(exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec),
    266                                        exec->argument(7).toFloat(exec), exec->argument(8).toFloat(exec)), ec);
    267                     setDOMException(exec, ec);
    268                     break;
    269                 default:
    270                     return throwSyntaxError(exec);
    271         }
    272 #endif
    273     } else
    274         return throwTypeError(exec);
    275 
    276     return jsUndefined();
    277 }
    278 
    279 JSValue JSCanvasRenderingContext2D::drawImageFromRect(ExecState* exec)
    280 {
    281     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    282 
    283     JSValue value = exec->argument(0);
    284     if (!value.isObject())
    285         return throwTypeError(exec);
    286     JSObject* o = asObject(value);
    287 
    288     if (!o->inherits(&JSHTMLImageElement::s_info))
    289         return throwTypeError(exec);
    290     context->drawImageFromRect(static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl()),
    291                                exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
    292                                exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec),
    293                                exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec),
    294                                exec->argument(7).toFloat(exec), exec->argument(8).toFloat(exec),
    295                                ustringToString(exec->argument(9).toString(exec)));
    296     return jsUndefined();
    297 }
    298 
    299 JSValue JSCanvasRenderingContext2D::setShadow(ExecState* exec)
    300 {
    301     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    302 
    303     switch (exec->argumentCount()) {
    304         case 3:
    305             context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    306                                exec->argument(2).toFloat(exec));
    307             break;
    308         case 4:
    309             if (exec->argument(3).isString())
    310                 context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    311                                    exec->argument(2).toFloat(exec), ustringToString(asString(exec->argument(3))->value(exec)));
    312             else
    313                 context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    314                                    exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
    315             break;
    316         case 5:
    317             if (exec->argument(3).isString())
    318                 context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    319                                    exec->argument(2).toFloat(exec), ustringToString(asString(exec->argument(3))->value(exec)),
    320                                    exec->argument(4).toFloat(exec));
    321             else
    322                 context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    323                                    exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec),
    324                                    exec->argument(4).toFloat(exec));
    325             break;
    326         case 7:
    327             context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    328                                exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec),
    329                                exec->argument(4).toFloat(exec), exec->argument(5).toFloat(exec),
    330                                exec->argument(6).toFloat(exec));
    331             break;
    332         case 8:
    333             context->setShadow(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec),
    334                                exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec),
    335                                exec->argument(4).toFloat(exec), exec->argument(5).toFloat(exec),
    336                                exec->argument(6).toFloat(exec), exec->argument(7).toFloat(exec));
    337             break;
    338         default:
    339             return throwSyntaxError(exec);
    340     }
    341 
    342     return jsUndefined();
    343 }
    344 
    345 JSValue JSCanvasRenderingContext2D::createPattern(ExecState* exec)
    346 {
    347     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    348 
    349     JSValue value = exec->argument(0);
    350     if (!value.isObject()) {
    351         setDOMException(exec, TYPE_MISMATCH_ERR);
    352         return jsUndefined();
    353     }
    354     JSObject* o = asObject(value);
    355 
    356     if (o->inherits(&JSHTMLImageElement::s_info)) {
    357         ExceptionCode ec;
    358         JSValue pattern = toJS(exec,
    359             context->createPattern(static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl()),
    360                                    valueToStringWithNullCheck(exec, exec->argument(1)), ec).get());
    361         setDOMException(exec, ec);
    362         return pattern;
    363     }
    364     if (o->inherits(&JSHTMLCanvasElement::s_info)) {
    365         ExceptionCode ec;
    366         JSValue pattern = toJS(exec,
    367             context->createPattern(static_cast<HTMLCanvasElement*>(static_cast<JSHTMLElement*>(o)->impl()),
    368                 valueToStringWithNullCheck(exec, exec->argument(1)), ec).get());
    369         setDOMException(exec, ec);
    370         return pattern;
    371     }
    372     setDOMException(exec, TYPE_MISMATCH_ERR);
    373     return jsUndefined();
    374 }
    375 
    376 JSValue JSCanvasRenderingContext2D::createImageData(ExecState* exec)
    377 {
    378     // createImageData has two variants
    379     // createImageData(ImageData)
    380     // createImageData(width, height)
    381     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    382     RefPtr<ImageData> imageData = 0;
    383 
    384     ExceptionCode ec = 0;
    385     if (exec->argumentCount() == 1)
    386         imageData = context->createImageData(toImageData(exec->argument(0)), ec);
    387     else if (exec->argumentCount() == 2)
    388         imageData = context->createImageData(exec->argument(0).toFloat(exec), exec->argument(1).toFloat(exec), ec);
    389 
    390     setDOMException(exec, ec);
    391     return toJS(exec, globalObject(), WTF::getPtr(imageData));
    392 }
    393 
    394 JSValue JSCanvasRenderingContext2D::putImageData(ExecState* exec)
    395 {
    396     // putImageData has two variants
    397     // putImageData(ImageData, x, y)
    398     // putImageData(ImageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight)
    399     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    400 
    401     ExceptionCode ec = 0;
    402     if (exec->argumentCount() >= 7)
    403         context->putImageData(toImageData(exec->argument(0)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec),
    404                               exec->argument(3).toFloat(exec), exec->argument(4).toFloat(exec), exec->argument(5).toFloat(exec), exec->argument(6).toFloat(exec), ec);
    405     else
    406         context->putImageData(toImageData(exec->argument(0)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), ec);
    407 
    408     setDOMException(exec, ec);
    409     return jsUndefined();
    410 }
    411 
    412 JSValue JSCanvasRenderingContext2D::fillText(ExecState* exec)
    413 {
    414     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    415 
    416     // string arg = text to draw
    417     // number arg = x
    418     // number arg = y
    419     // optional number arg = maxWidth
    420     if (exec->argumentCount() < 3 || exec->argumentCount() > 4)
    421         return throwSyntaxError(exec);
    422 
    423     if (exec->argumentCount() == 4)
    424         context->fillText(ustringToString(exec->argument(0).toString(exec)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
    425     else
    426         context->fillText(ustringToString(exec->argument(0).toString(exec)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec));
    427     return jsUndefined();
    428 }
    429 
    430 JSValue JSCanvasRenderingContext2D::strokeText(ExecState* exec)
    431 {
    432     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    433 
    434     // string arg = text to draw
    435     // number arg = x
    436     // number arg = y
    437     // optional number arg = maxWidth
    438     if (exec->argumentCount() < 3 || exec->argumentCount() > 4)
    439         return throwSyntaxError(exec);
    440 
    441     if (exec->argumentCount() == 4)
    442         context->strokeText(ustringToString(exec->argument(0).toString(exec)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec), exec->argument(3).toFloat(exec));
    443     else
    444         context->strokeText(ustringToString(exec->argument(0).toString(exec)), exec->argument(1).toFloat(exec), exec->argument(2).toFloat(exec));
    445     return jsUndefined();
    446 }
    447 
    448 } // namespace WebCore
    449