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* exec, JSValue value)
     55 {
     56     if (value.isString())
     57         return CanvasStyle::create(asString(value)->value(exec));
     58     if (!value.isObject())
     59         return 0;
     60     JSObject* object = asObject(value);
     61     if (object->inherits(&JSCanvasGradient::s_info))
     62         return CanvasStyle::create(static_cast<JSCanvasGradient*>(object)->impl());
     63     if (object->inherits(&JSCanvasPattern::s_info))
     64         return CanvasStyle::create(static_cast<JSCanvasPattern*>(object)->impl());
     65     return 0;
     66 }
     67 
     68 JSValue JSCanvasRenderingContext2D::strokeStyle(ExecState* exec) const
     69 {
     70     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
     71     return toJS(exec, context->strokeStyle());
     72 }
     73 
     74 void JSCanvasRenderingContext2D::setStrokeStyle(ExecState* exec, JSValue value)
     75 {
     76     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
     77     context->setStrokeStyle(toHTMLCanvasStyle(exec, value));
     78 }
     79 
     80 JSValue JSCanvasRenderingContext2D::fillStyle(ExecState* exec) const
     81 {
     82     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
     83     return toJS(exec, context->fillStyle());
     84 }
     85 
     86 void JSCanvasRenderingContext2D::setFillStyle(ExecState* exec, JSValue value)
     87 {
     88     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
     89     context->setFillStyle(toHTMLCanvasStyle(exec, value));
     90 }
     91 
     92 JSValue JSCanvasRenderingContext2D::setFillColor(ExecState* exec, const ArgList& args)
     93 {
     94     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
     95 
     96     // string arg = named color
     97     // number arg = gray color
     98     // string arg, number arg = named color, alpha
     99     // number arg, number arg = gray color, alpha
    100     // 4 args = r, g, b, a
    101     // 5 args = c, m, y, k, a
    102     switch (args.size()) {
    103         case 1:
    104             if (args.at(0).isString())
    105                 context->setFillColor(asString(args.at(0))->value(exec));
    106             else
    107                 context->setFillColor(args.at(0).toFloat(exec));
    108             break;
    109         case 2:
    110             if (args.at(0).isString())
    111                 context->setFillColor(asString(args.at(0))->value(exec), args.at(1).toFloat(exec));
    112             else
    113                 context->setFillColor(args.at(0).toFloat(exec), args.at(1).toFloat(exec));
    114             break;
    115         case 4:
    116             context->setFillColor(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    117                                   args.at(2).toFloat(exec), args.at(3).toFloat(exec));
    118             break;
    119         case 5:
    120             context->setFillColor(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    121                                   args.at(2).toFloat(exec), args.at(3).toFloat(exec), args.at(4).toFloat(exec));
    122             break;
    123         default:
    124             return throwError(exec, SyntaxError);
    125     }
    126     return jsUndefined();
    127 }
    128 
    129 JSValue JSCanvasRenderingContext2D::setStrokeColor(ExecState* exec, const ArgList& args)
    130 {
    131     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    132 
    133     // string arg = named color
    134     // number arg = gray color
    135     // string arg, number arg = named color, alpha
    136     // number arg, number arg = gray color, alpha
    137     // 4 args = r, g, b, a
    138     // 5 args = c, m, y, k, a
    139     switch (args.size()) {
    140         case 1:
    141             if (args.at(0).isString())
    142                 context->setStrokeColor(asString(args.at(0))->value(exec));
    143             else
    144                 context->setStrokeColor(args.at(0).toFloat(exec));
    145             break;
    146         case 2:
    147             if (args.at(0).isString())
    148                 context->setStrokeColor(asString(args.at(0))->value(exec), args.at(1).toFloat(exec));
    149             else
    150                 context->setStrokeColor(args.at(0).toFloat(exec), args.at(1).toFloat(exec));
    151             break;
    152         case 4:
    153             context->setStrokeColor(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    154                                     args.at(2).toFloat(exec), args.at(3).toFloat(exec));
    155             break;
    156         case 5:
    157             context->setStrokeColor(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    158                                     args.at(2).toFloat(exec), args.at(3).toFloat(exec), args.at(4).toFloat(exec));
    159             break;
    160         default:
    161             return throwError(exec, SyntaxError);
    162     }
    163 
    164     return jsUndefined();
    165 }
    166 
    167 JSValue JSCanvasRenderingContext2D::strokeRect(ExecState* exec, const ArgList& args)
    168 {
    169     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    170 
    171     if (args.size() <= 4)
    172         context->strokeRect(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    173                             args.at(2).toFloat(exec), args.at(3).toFloat(exec));
    174     else
    175         context->strokeRect(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    176                             args.at(2).toFloat(exec), args.at(3).toFloat(exec), args.at(4).toFloat(exec));
    177 
    178     return jsUndefined();
    179 }
    180 
    181 JSValue JSCanvasRenderingContext2D::drawImage(ExecState* exec, const ArgList& args)
    182 {
    183     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    184 
    185     // DrawImage has three variants:
    186     //     drawImage(img, dx, dy)
    187     //     drawImage(img, dx, dy, dw, dh)
    188     //     drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)
    189     // Composite operation is specified with globalCompositeOperation.
    190     // The img parameter can be a <img> or <canvas> element.
    191     JSValue value = args.at(0);
    192     if (!value.isObject())
    193         return throwError(exec, TypeError);
    194     JSObject* o = asObject(value);
    195 
    196     ExceptionCode ec = 0;
    197     if (o->inherits(&JSHTMLImageElement::s_info)) {
    198         HTMLImageElement* imgElt = static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl());
    199         switch (args.size()) {
    200             case 3:
    201                 context->drawImage(imgElt, args.at(1).toFloat(exec), args.at(2).toFloat(exec));
    202                 break;
    203             case 5:
    204                 context->drawImage(imgElt, args.at(1).toFloat(exec), args.at(2).toFloat(exec),
    205                                    args.at(3).toFloat(exec), args.at(4).toFloat(exec), ec);
    206                 setDOMException(exec, ec);
    207                 break;
    208             case 9:
    209                 context->drawImage(imgElt, FloatRect(args.at(1).toFloat(exec), args.at(2).toFloat(exec),
    210                                    args.at(3).toFloat(exec), args.at(4).toFloat(exec)),
    211                                    FloatRect(args.at(5).toFloat(exec), args.at(6).toFloat(exec),
    212                                    args.at(7).toFloat(exec), args.at(8).toFloat(exec)), ec);
    213                 setDOMException(exec, ec);
    214                 break;
    215             default:
    216                 return throwError(exec, SyntaxError);
    217         }
    218     } else if (o->inherits(&JSHTMLCanvasElement::s_info)) {
    219         HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(static_cast<JSHTMLElement*>(o)->impl());
    220         switch (args.size()) {
    221             case 3:
    222                 context->drawImage(canvas, args.at(1).toFloat(exec), args.at(2).toFloat(exec));
    223                 break;
    224             case 5:
    225                 context->drawImage(canvas, args.at(1).toFloat(exec), args.at(2).toFloat(exec),
    226                                    args.at(3).toFloat(exec), args.at(4).toFloat(exec), ec);
    227                 setDOMException(exec, ec);
    228                 break;
    229             case 9:
    230                 context->drawImage(canvas, FloatRect(args.at(1).toFloat(exec), args.at(2).toFloat(exec),
    231                                    args.at(3).toFloat(exec), args.at(4).toFloat(exec)),
    232                                    FloatRect(args.at(5).toFloat(exec), args.at(6).toFloat(exec),
    233                                    args.at(7).toFloat(exec), args.at(8).toFloat(exec)), ec);
    234                 setDOMException(exec, ec);
    235                 break;
    236             default:
    237                 return throwError(exec, SyntaxError);
    238         }
    239 #if ENABLE(VIDEO)
    240     } else if (o->inherits(&JSHTMLVideoElement::s_info)) {
    241             HTMLVideoElement* video = static_cast<HTMLVideoElement*>(static_cast<JSHTMLElement*>(o)->impl());
    242             switch (args.size()) {
    243                 case 3:
    244                     context->drawImage(video, args.at(1).toFloat(exec), args.at(2).toFloat(exec));
    245                     break;
    246                 case 5:
    247                     context->drawImage(video, args.at(1).toFloat(exec), args.at(2).toFloat(exec),
    248                                        args.at(3).toFloat(exec), args.at(4).toFloat(exec), ec);
    249                     setDOMException(exec, ec);
    250                     break;
    251                 case 9:
    252                     context->drawImage(video, FloatRect(args.at(1).toFloat(exec), args.at(2).toFloat(exec),
    253                                        args.at(3).toFloat(exec), args.at(4).toFloat(exec)),
    254                                        FloatRect(args.at(5).toFloat(exec), args.at(6).toFloat(exec),
    255                                        args.at(7).toFloat(exec), args.at(8).toFloat(exec)), ec);
    256                     setDOMException(exec, ec);
    257                     break;
    258                 default:
    259                     return throwError(exec, SyntaxError);
    260         }
    261 #endif
    262     } else {
    263         setDOMException(exec, TYPE_MISMATCH_ERR);
    264     }
    265 
    266     return jsUndefined();
    267 }
    268 
    269 JSValue JSCanvasRenderingContext2D::drawImageFromRect(ExecState* exec, const ArgList& args)
    270 {
    271     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    272 
    273     JSValue value = args.at(0);
    274     if (!value.isObject())
    275         return throwError(exec, TypeError);
    276     JSObject* o = asObject(value);
    277 
    278     if (!o->inherits(&JSHTMLImageElement::s_info))
    279         return throwError(exec, TypeError);
    280     context->drawImageFromRect(static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl()),
    281                                args.at(1).toFloat(exec), args.at(2).toFloat(exec),
    282                                args.at(3).toFloat(exec), args.at(4).toFloat(exec),
    283                                args.at(5).toFloat(exec), args.at(6).toFloat(exec),
    284                                args.at(7).toFloat(exec), args.at(8).toFloat(exec),
    285                                args.at(9).toString(exec));
    286     return jsUndefined();
    287 }
    288 
    289 JSValue JSCanvasRenderingContext2D::setShadow(ExecState* exec, const ArgList& args)
    290 {
    291     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    292 
    293     switch (args.size()) {
    294         case 3:
    295             context->setShadow(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    296                                args.at(2).toFloat(exec));
    297             break;
    298         case 4:
    299             if (args.at(3).isString())
    300                 context->setShadow(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    301                                    args.at(2).toFloat(exec), asString(args.at(3))->value(exec));
    302             else
    303                 context->setShadow(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    304                                    args.at(2).toFloat(exec), args.at(3).toFloat(exec));
    305             break;
    306         case 5:
    307             if (args.at(3).isString())
    308                 context->setShadow(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    309                                    args.at(2).toFloat(exec), asString(args.at(3))->value(exec),
    310                                    args.at(4).toFloat(exec));
    311             else
    312                 context->setShadow(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    313                                    args.at(2).toFloat(exec), args.at(3).toFloat(exec),
    314                                    args.at(4).toFloat(exec));
    315             break;
    316         case 7:
    317             context->setShadow(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    318                                args.at(2).toFloat(exec), args.at(3).toFloat(exec),
    319                                args.at(4).toFloat(exec), args.at(5).toFloat(exec),
    320                                args.at(6).toFloat(exec));
    321             break;
    322         case 8:
    323             context->setShadow(args.at(0).toFloat(exec), args.at(1).toFloat(exec),
    324                                args.at(2).toFloat(exec), args.at(3).toFloat(exec),
    325                                args.at(4).toFloat(exec), args.at(5).toFloat(exec),
    326                                args.at(6).toFloat(exec), args.at(7).toFloat(exec));
    327             break;
    328         default:
    329             return throwError(exec, SyntaxError);
    330     }
    331 
    332     return jsUndefined();
    333 }
    334 
    335 JSValue JSCanvasRenderingContext2D::createPattern(ExecState* exec, const ArgList& args)
    336 {
    337     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    338 
    339     JSValue value = args.at(0);
    340     if (!value.isObject())
    341         return throwError(exec, TypeError);
    342     JSObject* o = asObject(value);
    343 
    344     if (o->inherits(&JSHTMLImageElement::s_info)) {
    345         ExceptionCode ec;
    346         JSValue pattern = toJS(exec,
    347             context->createPattern(static_cast<HTMLImageElement*>(static_cast<JSHTMLElement*>(o)->impl()),
    348                                    valueToStringWithNullCheck(exec, args.at(1)), ec).get());
    349         setDOMException(exec, ec);
    350         return pattern;
    351     }
    352     if (o->inherits(&JSHTMLCanvasElement::s_info)) {
    353         ExceptionCode ec;
    354         JSValue pattern = toJS(exec,
    355             context->createPattern(static_cast<HTMLCanvasElement*>(static_cast<JSHTMLElement*>(o)->impl()),
    356                 valueToStringWithNullCheck(exec, args.at(1)), ec).get());
    357         setDOMException(exec, ec);
    358         return pattern;
    359     }
    360     setDOMException(exec, TYPE_MISMATCH_ERR);
    361     return jsUndefined();
    362 }
    363 
    364 JSValue JSCanvasRenderingContext2D::putImageData(ExecState* exec, const ArgList& args)
    365 {
    366     // putImageData has two variants
    367     // putImageData(ImageData, x, y)
    368     // putImageData(ImageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight)
    369     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    370 
    371     ExceptionCode ec = 0;
    372     if (args.size() >= 7)
    373         context->putImageData(toImageData(args.at(0)), args.at(1).toFloat(exec), args.at(2).toFloat(exec),
    374                               args.at(3).toFloat(exec), args.at(4).toFloat(exec), args.at(5).toFloat(exec), args.at(6).toFloat(exec), ec);
    375     else
    376         context->putImageData(toImageData(args.at(0)), args.at(1).toFloat(exec), args.at(2).toFloat(exec), ec);
    377 
    378     setDOMException(exec, ec);
    379     return jsUndefined();
    380 }
    381 
    382 JSValue JSCanvasRenderingContext2D::fillText(ExecState* exec, const ArgList& args)
    383 {
    384     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    385 
    386     // string arg = text to draw
    387     // number arg = x
    388     // number arg = y
    389     // optional number arg = maxWidth
    390     if (args.size() < 3 || args.size() > 4)
    391         return throwError(exec, SyntaxError);
    392 
    393     if (args.size() == 4)
    394         context->fillText(args.at(0).toString(exec), args.at(1).toFloat(exec), args.at(2).toFloat(exec), args.at(3).toFloat(exec));
    395     else
    396         context->fillText(args.at(0).toString(exec), args.at(1).toFloat(exec), args.at(2).toFloat(exec));
    397     return jsUndefined();
    398 }
    399 
    400 JSValue JSCanvasRenderingContext2D::strokeText(ExecState* exec, const ArgList& args)
    401 {
    402     CanvasRenderingContext2D* context = static_cast<CanvasRenderingContext2D*>(impl());
    403 
    404     // string arg = text to draw
    405     // number arg = x
    406     // number arg = y
    407     // optional number arg = maxWidth
    408     if (args.size() < 3 || args.size() > 4)
    409         return throwError(exec, SyntaxError);
    410 
    411     if (args.size() == 4)
    412         context->strokeText(args.at(0).toString(exec), args.at(1).toFloat(exec), args.at(2).toFloat(exec), args.at(3).toFloat(exec));
    413     else
    414         context->strokeText(args.at(0).toString(exec), args.at(1).toFloat(exec), args.at(2).toFloat(exec));
    415     return jsUndefined();
    416 }
    417 
    418 } // namespace WebCore
    419