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