| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | |
| 3 * Copyright (C) 2007 Trolltech ASA | |
| 4 * Copyright (C) 2007 Alp Toker <alp@atoker.com> | |
| 5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> | |
| 6 * | |
| 7 * Redistribution and use in source and binary forms, with or without | |
| 8 * modification, are permitted provided that the following conditions | |
| 9 * are met: | |
| 10 * 1. Redistributions of source code must retain the above copyright | |
| 11 * notice, this list of conditions and the following disclaimer. | |
| 12 * 2. Redistributions in binary form must reproduce the above copyright | |
| 13 * notice, this list of conditions and the following disclaimer in the | |
| 14 * documentation and/or other materials provided with the distribution. | |
| 15 * | |
| 16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
| 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
| 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 27 */ | |
| 28 | |
| 29 #include "config.h" | |
| 30 #include "CanvasRenderingContext2D.h" | |
| 31 | |
| 32 #include "AffineTransform.h" | |
| 33 #include "CSSParser.h" | |
| 34 #include "CachedImage.h" | |
| 35 #include "CanvasGradient.h" | |
| 36 #include "CanvasPattern.h" | |
| 37 #include "CanvasPixelArray.h" | |
| 38 #include "CanvasStyle.h" | |
| 39 #include "CSSPropertyNames.h" | |
| 40 #include "CSSStyleSelector.h" | |
| 41 #include "Document.h" | |
| 42 #include "ExceptionCode.h" | |
| 43 #include "FloatConversion.h" | |
| 44 #include "Frame.h" | |
| 45 #include "GraphicsContext.h" | |
| 46 #include "HTMLCanvasElement.h" | |
| 47 #include "HTMLImageElement.h" | |
| 48 #include "HTMLNames.h" | |
| 49 #include "ImageBuffer.h" | |
| 50 #include "ImageData.h" | |
| 51 #include "KURL.h" | |
| 52 #include "NotImplemented.h" | |
| 53 #include "Page.h" | |
| 54 #include "RenderHTMLCanvas.h" | |
| 55 #include "SecurityOrigin.h" | |
| 56 #include "Settings.h" | |
| 57 #include "TextMetrics.h" | |
| 58 #include <kjs/interpreter.h> | |
| 59 #include <stdio.h> | |
| 60 #include <wtf/MathExtras.h> | |
| 61 | |
| 62 using namespace std; | |
| 63 | |
| 64 namespace WebCore { | |
| 65 | |
| 66 using namespace HTMLNames; | |
| 67 | |
| 68 const char* defaultFont = "10px sans-serif"; | |
| 69 | |
| 70 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas) | |
| 71 : m_canvas(canvas) | |
| 72 , m_stateStack(1) | |
| 73 #if USE(V8) | |
| 74 , m_peer(0) | |
| 75 #endif | |
| 76 { | |
| 77 } | |
| 78 | |
| 79 void CanvasRenderingContext2D::ref() | |
| 80 { | |
| 81 m_canvas->ref(); | |
| 82 } | |
| 83 | |
| 84 void CanvasRenderingContext2D::deref() | |
| 85 { | |
| 86 m_canvas->deref(); | |
| 87 } | |
| 88 | |
| 89 void CanvasRenderingContext2D::reset() | |
| 90 { | |
| 91 m_stateStack.resize(1); | |
| 92 m_stateStack.first() = State(); | |
| 93 } | |
| 94 | |
| 95 CanvasRenderingContext2D::State::State() | |
| 96 : m_strokeStyle(CanvasStyle::create("black")) | |
| 97 , m_fillStyle(CanvasStyle::create("black")) | |
| 98 , m_lineWidth(1) | |
| 99 , m_lineCap(ButtCap) | |
| 100 , m_lineJoin(MiterJoin) | |
| 101 , m_miterLimit(10) | |
| 102 , m_shadowBlur(0) | |
| 103 , m_shadowColor("black") | |
| 104 , m_globalAlpha(1) | |
| 105 , m_globalComposite(CompositeSourceOver) | |
| 106 , m_textAlign(StartTextAlign) | |
| 107 , m_textBaseline(AlphabeticTextBaseline) | |
| 108 , m_unparsedFont(defaultFont) | |
| 109 , m_realizedFont(false) | |
| 110 { | |
| 111 } | |
| 112 | |
| 113 void CanvasRenderingContext2D::save() | |
| 114 { | |
| 115 ASSERT(m_stateStack.size() >= 1); | |
| 116 m_stateStack.append(state()); | |
| 117 GraphicsContext* c = drawingContext(); | |
| 118 if (!c) | |
| 119 return; | |
| 120 c->save(); | |
| 121 } | |
| 122 | |
| 123 void CanvasRenderingContext2D::restore() | |
| 124 { | |
| 125 ASSERT(m_stateStack.size() >= 1); | |
| 126 if (m_stateStack.size() <= 1) | |
| 127 return; | |
| 128 m_path.transform(state().m_transform); | |
| 129 m_stateStack.removeLast(); | |
| 130 m_path.transform(state().m_transform.inverse()); | |
| 131 GraphicsContext* c = drawingContext(); | |
| 132 if (!c) | |
| 133 return; | |
| 134 c->restore(); | |
| 135 } | |
| 136 | |
| 137 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const | |
| 138 { | |
| 139 return state().m_strokeStyle.get(); | |
| 140 } | |
| 141 | |
| 142 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style) | |
| 143 { | |
| 144 if (!style) | |
| 145 return; | |
| 146 | |
| 147 if (m_canvas->originClean()) { | |
| 148 if (CanvasPattern* pattern = style->canvasPattern()) { | |
| 149 if (!pattern->originClean()) | |
| 150 m_canvas->setOriginTainted(); | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 state().m_strokeStyle = style; | |
| 155 GraphicsContext* c = drawingContext(); | |
| 156 if (!c) | |
| 157 return; | |
| 158 state().m_strokeStyle->applyStrokeColor(c); | |
| 159 } | |
| 160 | |
| 161 CanvasStyle* CanvasRenderingContext2D::fillStyle() const | |
| 162 { | |
| 163 return state().m_fillStyle.get(); | |
| 164 } | |
| 165 | |
| 166 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style) | |
| 167 { | |
| 168 if (!style) | |
| 169 return; | |
| 170 | |
| 171 if (m_canvas->originClean()) { | |
| 172 if (CanvasPattern* pattern = style->canvasPattern()) { | |
| 173 if (!pattern->originClean()) | |
| 174 m_canvas->setOriginTainted(); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 state().m_fillStyle = style; | |
| 179 GraphicsContext* c = drawingContext(); | |
| 180 if (!c) | |
| 181 return; | |
| 182 state().m_fillStyle->applyFillColor(c); | |
| 183 } | |
| 184 | |
| 185 float CanvasRenderingContext2D::lineWidth() const | |
| 186 { | |
| 187 return state().m_lineWidth; | |
| 188 } | |
| 189 | |
| 190 void CanvasRenderingContext2D::setLineWidth(float width) | |
| 191 { | |
| 192 if (!(width > 0)) | |
| 193 return; | |
| 194 state().m_lineWidth = width; | |
| 195 GraphicsContext* c = drawingContext(); | |
| 196 if (!c) | |
| 197 return; | |
| 198 c->setStrokeThickness(width); | |
| 199 } | |
| 200 | |
| 201 String CanvasRenderingContext2D::lineCap() const | |
| 202 { | |
| 203 return lineCapName(state().m_lineCap); | |
| 204 } | |
| 205 | |
| 206 void CanvasRenderingContext2D::setLineCap(const String& s) | |
| 207 { | |
| 208 LineCap cap; | |
| 209 if (!parseLineCap(s, cap)) | |
| 210 return; | |
| 211 state().m_lineCap = cap; | |
| 212 GraphicsContext* c = drawingContext(); | |
| 213 if (!c) | |
| 214 return; | |
| 215 c->setLineCap(cap); | |
| 216 } | |
| 217 | |
| 218 String CanvasRenderingContext2D::lineJoin() const | |
| 219 { | |
| 220 return lineJoinName(state().m_lineJoin); | |
| 221 } | |
| 222 | |
| 223 void CanvasRenderingContext2D::setLineJoin(const String& s) | |
| 224 { | |
| 225 LineJoin join; | |
| 226 if (!parseLineJoin(s, join)) | |
| 227 return; | |
| 228 state().m_lineJoin = join; | |
| 229 GraphicsContext* c = drawingContext(); | |
| 230 if (!c) | |
| 231 return; | |
| 232 c->setLineJoin(join); | |
| 233 } | |
| 234 | |
| 235 float CanvasRenderingContext2D::miterLimit() const | |
| 236 { | |
| 237 return state().m_miterLimit; | |
| 238 } | |
| 239 | |
| 240 void CanvasRenderingContext2D::setMiterLimit(float limit) | |
| 241 { | |
| 242 if (!(limit > 0)) | |
| 243 return; | |
| 244 state().m_miterLimit = limit; | |
| 245 GraphicsContext* c = drawingContext(); | |
| 246 if (!c) | |
| 247 return; | |
| 248 c->setMiterLimit(limit); | |
| 249 } | |
| 250 | |
| 251 float CanvasRenderingContext2D::shadowOffsetX() const | |
| 252 { | |
| 253 return state().m_shadowOffset.width(); | |
| 254 } | |
| 255 | |
| 256 void CanvasRenderingContext2D::setShadowOffsetX(float x) | |
| 257 { | |
| 258 state().m_shadowOffset.setWidth(x); | |
| 259 applyShadow(); | |
| 260 } | |
| 261 | |
| 262 float CanvasRenderingContext2D::shadowOffsetY() const | |
| 263 { | |
| 264 return state().m_shadowOffset.height(); | |
| 265 } | |
| 266 | |
| 267 void CanvasRenderingContext2D::setShadowOffsetY(float y) | |
| 268 { | |
| 269 state().m_shadowOffset.setHeight(y); | |
| 270 applyShadow(); | |
| 271 } | |
| 272 | |
| 273 float CanvasRenderingContext2D::shadowBlur() const | |
| 274 { | |
| 275 return state().m_shadowBlur; | |
| 276 } | |
| 277 | |
| 278 void CanvasRenderingContext2D::setShadowBlur(float blur) | |
| 279 { | |
| 280 state().m_shadowBlur = blur; | |
| 281 applyShadow(); | |
| 282 } | |
| 283 | |
| 284 String CanvasRenderingContext2D::shadowColor() const | |
| 285 { | |
| 286 // FIXME: What should this return if you called setShadow with a non-string
color? | |
| 287 return state().m_shadowColor; | |
| 288 } | |
| 289 | |
| 290 void CanvasRenderingContext2D::setShadowColor(const String& color) | |
| 291 { | |
| 292 state().m_shadowColor = color; | |
| 293 applyShadow(); | |
| 294 } | |
| 295 | |
| 296 float CanvasRenderingContext2D::globalAlpha() const | |
| 297 { | |
| 298 return state().m_globalAlpha; | |
| 299 } | |
| 300 | |
| 301 void CanvasRenderingContext2D::setGlobalAlpha(float alpha) | |
| 302 { | |
| 303 if (!(alpha >= 0 && alpha <= 1)) | |
| 304 return; | |
| 305 state().m_globalAlpha = alpha; | |
| 306 GraphicsContext* c = drawingContext(); | |
| 307 if (!c) | |
| 308 return; | |
| 309 c->setAlpha(alpha); | |
| 310 } | |
| 311 | |
| 312 String CanvasRenderingContext2D::globalCompositeOperation() const | |
| 313 { | |
| 314 return compositeOperatorName(state().m_globalComposite); | |
| 315 } | |
| 316 | |
| 317 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operati
on) | |
| 318 { | |
| 319 CompositeOperator op; | |
| 320 if (!parseCompositeOperator(operation, op)) | |
| 321 return; | |
| 322 state().m_globalComposite = op; | |
| 323 GraphicsContext* c = drawingContext(); | |
| 324 if (!c) | |
| 325 return; | |
| 326 c->setCompositeOperation(op); | |
| 327 } | |
| 328 | |
| 329 void CanvasRenderingContext2D::scale(float sx, float sy) | |
| 330 { | |
| 331 GraphicsContext* c = drawingContext(); | |
| 332 if (!c) | |
| 333 return; | |
| 334 c->scale(FloatSize(sx, sy)); | |
| 335 state().m_transform.scale(sx, sy); | |
| 336 m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy)); | |
| 337 } | |
| 338 | |
| 339 void CanvasRenderingContext2D::rotate(float angleInRadians) | |
| 340 { | |
| 341 GraphicsContext* c = drawingContext(); | |
| 342 if (!c) | |
| 343 return; | |
| 344 c->rotate(angleInRadians); | |
| 345 state().m_transform.rotate(angleInRadians / piDouble * 180.0); | |
| 346 m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0
)); | |
| 347 } | |
| 348 | |
| 349 void CanvasRenderingContext2D::translate(float tx, float ty) | |
| 350 { | |
| 351 GraphicsContext* c = drawingContext(); | |
| 352 if (!c) | |
| 353 return; | |
| 354 c->translate(tx, ty); | |
| 355 state().m_transform.translate(tx, ty); | |
| 356 m_path.transform(AffineTransform().translate(-tx, -ty)); | |
| 357 } | |
| 358 | |
| 359 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float
m22, float dx, float dy) | |
| 360 { | |
| 361 GraphicsContext* c = drawingContext(); | |
| 362 if (!c) | |
| 363 return; | |
| 364 | |
| 365 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers | |
| 366 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) | | |
| 367 !isfinite(m12) | !isfinite(m22) | !isfinite(dy)) | |
| 368 return; | |
| 369 AffineTransform transform(m11, m12, m21, m22, dx, dy); | |
| 370 c->concatCTM(transform); | |
| 371 state().m_transform.multiply(transform); | |
| 372 m_path.transform(transform.inverse()); | |
| 373 } | |
| 374 | |
| 375 void CanvasRenderingContext2D::setStrokeColor(const String& color) | |
| 376 { | |
| 377 setStrokeStyle(CanvasStyle::create(color)); | |
| 378 } | |
| 379 | |
| 380 void CanvasRenderingContext2D::setStrokeColor(float grayLevel) | |
| 381 { | |
| 382 setStrokeStyle(CanvasStyle::create(grayLevel, 1)); | |
| 383 } | |
| 384 | |
| 385 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha) | |
| 386 { | |
| 387 setStrokeStyle(CanvasStyle::create(color, alpha)); | |
| 388 } | |
| 389 | |
| 390 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha) | |
| 391 { | |
| 392 setStrokeStyle(CanvasStyle::create(grayLevel, alpha)); | |
| 393 } | |
| 394 | |
| 395 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a
) | |
| 396 { | |
| 397 setStrokeStyle(CanvasStyle::create(r, g, b, a)); | |
| 398 } | |
| 399 | |
| 400 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k
, float a) | |
| 401 { | |
| 402 setStrokeStyle(CanvasStyle::create(c, m, y, k, a)); | |
| 403 } | |
| 404 | |
| 405 void CanvasRenderingContext2D::setFillColor(const String& color) | |
| 406 { | |
| 407 setFillStyle(CanvasStyle::create(color)); | |
| 408 } | |
| 409 | |
| 410 void CanvasRenderingContext2D::setFillColor(float grayLevel) | |
| 411 { | |
| 412 setFillStyle(CanvasStyle::create(grayLevel, 1)); | |
| 413 } | |
| 414 | |
| 415 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha) | |
| 416 { | |
| 417 setFillStyle(CanvasStyle::create(color, 1)); | |
| 418 } | |
| 419 | |
| 420 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha) | |
| 421 { | |
| 422 setFillStyle(CanvasStyle::create(grayLevel, alpha)); | |
| 423 } | |
| 424 | |
| 425 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a) | |
| 426 { | |
| 427 setFillStyle(CanvasStyle::create(r, g, b, a)); | |
| 428 } | |
| 429 | |
| 430 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k,
float a) | |
| 431 { | |
| 432 setFillStyle(CanvasStyle::create(c, m, y, k, a)); | |
| 433 } | |
| 434 | |
| 435 void CanvasRenderingContext2D::beginPath() | |
| 436 { | |
| 437 m_path.clear(); | |
| 438 } | |
| 439 | |
| 440 void CanvasRenderingContext2D::closePath() | |
| 441 { | |
| 442 m_path.closeSubpath(); | |
| 443 } | |
| 444 | |
| 445 void CanvasRenderingContext2D::moveTo(float x, float y) | |
| 446 { | |
| 447 if (!isfinite(x) | !isfinite(y)) | |
| 448 return; | |
| 449 m_path.moveTo(FloatPoint(x, y)); | |
| 450 } | |
| 451 | |
| 452 void CanvasRenderingContext2D::lineTo(float x, float y) | |
| 453 { | |
| 454 if (!isfinite(x) | !isfinite(y)) | |
| 455 return; | |
| 456 m_path.addLineTo(FloatPoint(x, y)); | |
| 457 } | |
| 458 | |
| 459 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, f
loat y) | |
| 460 { | |
| 461 if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y)) | |
| 462 return; | |
| 463 m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y)); | |
| 464 } | |
| 465 | |
| 466 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x,
float cp2y, float x, float y) | |
| 467 { | |
| 468 if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) |
!isfinite(x) | !isfinite(y)) | |
| 469 return; | |
| 470 m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), Floa
tPoint(x, y)); | |
| 471 } | |
| 472 | |
| 473 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, flo
at r, ExceptionCode& ec) | |
| 474 { | |
| 475 ec = 0; | |
| 476 if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinit
e(r)) | |
| 477 return; | |
| 478 | |
| 479 if (r < 0) { | |
| 480 ec = INDEX_SIZE_ERR; | |
| 481 return; | |
| 482 } | |
| 483 | |
| 484 m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r); | |
| 485 } | |
| 486 | |
| 487 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea
, bool anticlockwise, ExceptionCode& ec) | |
| 488 { | |
| 489 ec = 0; | |
| 490 if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(e
a)) | |
| 491 return; | |
| 492 | |
| 493 if (r < 0) { | |
| 494 ec = INDEX_SIZE_ERR; | |
| 495 return; | |
| 496 } | |
| 497 | |
| 498 m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise); | |
| 499 } | |
| 500 | |
| 501 static bool validateRectForCanvas(float& x, float& y, float& width, float& heigh
t) | |
| 502 { | |
| 503 if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height)) | |
| 504 return false; | |
| 505 | |
| 506 if (width < 0) { | |
| 507 width = -width; | |
| 508 x -= width; | |
| 509 } | |
| 510 | |
| 511 if (height < 0) { | |
| 512 height = -height; | |
| 513 y -= height; | |
| 514 } | |
| 515 | |
| 516 return true; | |
| 517 } | |
| 518 | |
| 519 void CanvasRenderingContext2D::rect(float x, float y, float width, float height) | |
| 520 { | |
| 521 if (!validateRectForCanvas(x, y, width, height)) | |
| 522 return; | |
| 523 | |
| 524 m_path.addRect(FloatRect(x, y, width, height)); | |
| 525 } | |
| 526 | |
| 527 #if ENABLE(DASHBOARD_SUPPORT) | |
| 528 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode() | |
| 529 { | |
| 530 if (Settings* settings = m_canvas->document()->settings()) | |
| 531 if (settings->usesDashboardBackwardCompatibilityMode()) | |
| 532 m_path.clear(); | |
| 533 } | |
| 534 #endif | |
| 535 | |
| 536 void CanvasRenderingContext2D::fill() | |
| 537 { | |
| 538 GraphicsContext* c = drawingContext(); | |
| 539 if (!c) | |
| 540 return; | |
| 541 | |
| 542 c->beginPath(); | |
| 543 c->addPath(m_path); | |
| 544 if (!m_path.isEmpty()) | |
| 545 willDraw(m_path.boundingRect()); | |
| 546 | |
| 547 c->fillPath(); | |
| 548 | |
| 549 #if ENABLE(DASHBOARD_SUPPORT) | |
| 550 clearPathForDashboardBackwardCompatibilityMode(); | |
| 551 #endif | |
| 552 } | |
| 553 | |
| 554 void CanvasRenderingContext2D::stroke() | |
| 555 { | |
| 556 GraphicsContext* c = drawingContext(); | |
| 557 if (!c) | |
| 558 return; | |
| 559 c->beginPath(); | |
| 560 c->addPath(m_path); | |
| 561 | |
| 562 if (!m_path.isEmpty()) { | |
| 563 // FIXME: This is insufficient, need to use CGContextReplacePathWithStro
kedPath to expand to required bounds | |
| 564 float lineWidth = state().m_lineWidth; | |
| 565 float inset = lineWidth / 2; | |
| 566 FloatRect boundingRect = m_path.boundingRect(); | |
| 567 boundingRect.inflate(inset); | |
| 568 willDraw(boundingRect); | |
| 569 } | |
| 570 | |
| 571 c->strokePath(); | |
| 572 | |
| 573 #if ENABLE(DASHBOARD_SUPPORT) | |
| 574 clearPathForDashboardBackwardCompatibilityMode(); | |
| 575 #endif | |
| 576 } | |
| 577 | |
| 578 void CanvasRenderingContext2D::clip() | |
| 579 { | |
| 580 GraphicsContext* c = drawingContext(); | |
| 581 if (!c) | |
| 582 return; | |
| 583 c->clip(m_path); | |
| 584 #if ENABLE(DASHBOARD_SUPPORT) | |
| 585 clearPathForDashboardBackwardCompatibilityMode(); | |
| 586 #endif | |
| 587 } | |
| 588 | |
| 589 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y) | |
| 590 { | |
| 591 GraphicsContext* c = drawingContext(); | |
| 592 if (!c) | |
| 593 return false; | |
| 594 FloatPoint point(x, y); | |
| 595 // We have to invert the current transform to ensure we correctly handle the | |
| 596 // transforms applied to the current path. | |
| 597 AffineTransform ctm = state().m_transform; | |
| 598 if (!ctm.isInvertible()) | |
| 599 return false; | |
| 600 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); | |
| 601 return m_path.contains(transformedPoint); | |
| 602 } | |
| 603 | |
| 604 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float he
ight) | |
| 605 { | |
| 606 if (!validateRectForCanvas(x, y, width, height)) | |
| 607 return; | |
| 608 GraphicsContext* c = drawingContext(); | |
| 609 if (!c) | |
| 610 return; | |
| 611 FloatRect rect(x, y, width, height); | |
| 612 willDraw(rect); | |
| 613 c->clearRect(rect); | |
| 614 } | |
| 615 | |
| 616 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float hei
ght) | |
| 617 { | |
| 618 if (!validateRectForCanvas(x, y, width, height)) | |
| 619 return; | |
| 620 | |
| 621 GraphicsContext* c = drawingContext(); | |
| 622 if (!c) | |
| 623 return; | |
| 624 | |
| 625 FloatRect rect(x, y, width, height); | |
| 626 willDraw(rect); | |
| 627 | |
| 628 c->save(); | |
| 629 c->fillRect(rect); | |
| 630 c->restore(); | |
| 631 } | |
| 632 | |
| 633 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float h
eight) | |
| 634 { | |
| 635 if (!validateRectForCanvas(x, y, width, height)) | |
| 636 return; | |
| 637 strokeRect(x, y, width, height, state().m_lineWidth); | |
| 638 } | |
| 639 | |
| 640 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float h
eight, float lineWidth) | |
| 641 { | |
| 642 if (!validateRectForCanvas(x, y, width, height)) | |
| 643 return; | |
| 644 | |
| 645 if (!(lineWidth >= 0)) | |
| 646 return; | |
| 647 | |
| 648 GraphicsContext* c = drawingContext(); | |
| 649 if (!c) | |
| 650 return; | |
| 651 | |
| 652 FloatRect rect(x, y, width, height); | |
| 653 | |
| 654 FloatRect boundingRect = rect; | |
| 655 boundingRect.inflate(lineWidth / 2); | |
| 656 willDraw(boundingRect); | |
| 657 | |
| 658 c->strokeRect(rect, lineWidth); | |
| 659 } | |
| 660 | |
| 661 #if PLATFORM(CG) | |
| 662 static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height) | |
| 663 { | |
| 664 // Work around <rdar://problem/5539388> by ensuring that shadow offsets will
get truncated | |
| 665 // to the desired integer. | |
| 666 static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128)
; | |
| 667 if (width > 0) | |
| 668 width += extraShadowOffset; | |
| 669 else if (width < 0) | |
| 670 width -= extraShadowOffset; | |
| 671 | |
| 672 if (height > 0) | |
| 673 height += extraShadowOffset; | |
| 674 else if (height < 0) | |
| 675 height -= extraShadowOffset; | |
| 676 | |
| 677 return CGSizeMake(width, height); | |
| 678 } | |
| 679 #endif | |
| 680 | |
| 681 void CanvasRenderingContext2D::setShadow(float width, float height, float blur) | |
| 682 { | |
| 683 state().m_shadowOffset = FloatSize(width, height); | |
| 684 state().m_shadowBlur = blur; | |
| 685 state().m_shadowColor = ""; | |
| 686 applyShadow(); | |
| 687 } | |
| 688 | |
| 689 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
const String& color) | |
| 690 { | |
| 691 state().m_shadowOffset = FloatSize(width, height); | |
| 692 state().m_shadowBlur = blur; | |
| 693 state().m_shadowColor = color; | |
| 694 applyShadow(); | |
| 695 } | |
| 696 | |
| 697 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
float grayLevel) | |
| 698 { | |
| 699 state().m_shadowOffset = FloatSize(width, height); | |
| 700 state().m_shadowBlur = blur; | |
| 701 state().m_shadowColor = ""; | |
| 702 | |
| 703 GraphicsContext* c = drawingContext(); | |
| 704 if (!c) | |
| 705 return; | |
| 706 // FIXME: Do this through platform-independent GraphicsContext API. | |
| 707 #if PLATFORM(CG) | |
| 708 const CGFloat components[2] = { grayLevel, 1 }; | |
| 709 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); | |
| 710 CGColorRef color = CGColorCreate(colorSpace, components); | |
| 711 CGColorSpaceRelease(colorSpace); | |
| 712 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width,
-height), blur, color); | |
| 713 CGColorRelease(color); | |
| 714 #endif | |
| 715 } | |
| 716 | |
| 717 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
const String& color, float alpha) | |
| 718 { | |
| 719 state().m_shadowOffset = FloatSize(width, height); | |
| 720 state().m_shadowBlur = blur; | |
| 721 state().m_shadowColor = color; | |
| 722 | |
| 723 GraphicsContext* c = drawingContext(); | |
| 724 if (!c) | |
| 725 return; | |
| 726 // FIXME: Do this through platform-independent GraphicsContext API. | |
| 727 #if PLATFORM(CG) | |
| 728 RGBA32 rgba = 0; // default is transparent black | |
| 729 CSSParser::parseColor(rgba, color); | |
| 730 const CGFloat components[4] = { | |
| 731 ((rgba >> 16) & 0xFF) / 255.0f, | |
| 732 ((rgba >> 8) & 0xFF) / 255.0f, | |
| 733 (rgba & 0xFF) / 255.0f, | |
| 734 alpha | |
| 735 }; | |
| 736 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | |
| 737 CGColorRef shadowColor = CGColorCreate(colorSpace, components); | |
| 738 CGColorSpaceRelease(colorSpace); | |
| 739 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width,
-height), blur, shadowColor); | |
| 740 CGColorRelease(shadowColor); | |
| 741 #endif | |
| 742 } | |
| 743 | |
| 744 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
float grayLevel, float alpha) | |
| 745 { | |
| 746 state().m_shadowOffset = FloatSize(width, height); | |
| 747 state().m_shadowBlur = blur; | |
| 748 state().m_shadowColor = ""; | |
| 749 | |
| 750 GraphicsContext* c = drawingContext(); | |
| 751 if (!c) | |
| 752 return; | |
| 753 // FIXME: Do this through platform-independent GraphicsContext API. | |
| 754 #if PLATFORM(CG) | |
| 755 const CGFloat components[2] = { grayLevel, alpha }; | |
| 756 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); | |
| 757 CGColorRef color = CGColorCreate(colorSpace, components); | |
| 758 CGColorSpaceRelease(colorSpace); | |
| 759 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width,
-height), blur, color); | |
| 760 CGColorRelease(color); | |
| 761 #endif | |
| 762 } | |
| 763 | |
| 764 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
float r, float g, float b, float a) | |
| 765 { | |
| 766 state().m_shadowOffset = FloatSize(width, height); | |
| 767 state().m_shadowBlur = blur; | |
| 768 state().m_shadowColor = ""; | |
| 769 | |
| 770 GraphicsContext* c = drawingContext(); | |
| 771 if (!c) | |
| 772 return; | |
| 773 // FIXME: Do this through platform-independent GraphicsContext API. | |
| 774 #if PLATFORM(CG) | |
| 775 const CGFloat components[4] = { r, g, b, a }; | |
| 776 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | |
| 777 CGColorRef shadowColor = CGColorCreate(colorSpace, components); | |
| 778 CGColorSpaceRelease(colorSpace); | |
| 779 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width,
-height), blur, shadowColor); | |
| 780 CGColorRelease(shadowColor); | |
| 781 #endif | |
| 782 } | |
| 783 | |
| 784 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
float c, float m, float y, float k, float a) | |
| 785 { | |
| 786 state().m_shadowOffset = FloatSize(width, height); | |
| 787 state().m_shadowBlur = blur; | |
| 788 state().m_shadowColor = ""; | |
| 789 | |
| 790 GraphicsContext* dc = drawingContext(); | |
| 791 if (!dc) | |
| 792 return; | |
| 793 // FIXME: Do this through platform-independent GraphicsContext API. | |
| 794 #if PLATFORM(CG) | |
| 795 const CGFloat components[5] = { c, m, y, k, a }; | |
| 796 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK(); | |
| 797 CGColorRef shadowColor = CGColorCreate(colorSpace, components); | |
| 798 CGColorSpaceRelease(colorSpace); | |
| 799 CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width,
-height), blur, shadowColor); | |
| 800 CGColorRelease(shadowColor); | |
| 801 #endif | |
| 802 } | |
| 803 | |
| 804 void CanvasRenderingContext2D::clearShadow() | |
| 805 { | |
| 806 state().m_shadowOffset = FloatSize(); | |
| 807 state().m_shadowBlur = 0; | |
| 808 state().m_shadowColor = ""; | |
| 809 applyShadow(); | |
| 810 } | |
| 811 | |
| 812 void CanvasRenderingContext2D::applyShadow() | |
| 813 { | |
| 814 GraphicsContext* c = drawingContext(); | |
| 815 if (!c) | |
| 816 return; | |
| 817 // FIXME: Do this through platform-independent GraphicsContext API. | |
| 818 #if PLATFORM(CG) | |
| 819 RGBA32 rgba = 0; // default is transparent black | |
| 820 if (!state().m_shadowColor.isEmpty()) | |
| 821 CSSParser::parseColor(rgba, state().m_shadowColor); | |
| 822 const CGFloat components[4] = { | |
| 823 ((rgba >> 16) & 0xFF) / 255.0f, | |
| 824 ((rgba >> 8) & 0xFF) / 255.0f, | |
| 825 (rgba & 0xFF) / 255.0f, | |
| 826 ((rgba >> 24) & 0xFF) / 255.0f | |
| 827 }; | |
| 828 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | |
| 829 CGColorRef color = CGColorCreate(colorSpace, components); | |
| 830 CGColorSpaceRelease(colorSpace); | |
| 831 | |
| 832 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(state()
.m_shadowOffset.width(), -state().m_shadowOffset.height()), state().m_shadowBlur
, color); | |
| 833 CGColorRelease(color); | |
| 834 #endif | |
| 835 } | |
| 836 | |
| 837 static IntSize size(HTMLImageElement* image) | |
| 838 { | |
| 839 if (CachedImage* cachedImage = image->cachedImage()) | |
| 840 return cachedImage->imageSize(1.0f); // FIXME: Not sure about this. | |
| 841 return IntSize(); | |
| 842 } | |
| 843 | |
| 844 static inline FloatRect normalizeRect(const FloatRect& rect) | |
| 845 { | |
| 846 return FloatRect(min(rect.x(), rect.right()), | |
| 847 min(rect.y(), rect.bottom()), | |
| 848 max(rect.width(), -rect.width()), | |
| 849 max(rect.height(), -rect.height())); | |
| 850 } | |
| 851 | |
| 852 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float
y) | |
| 853 { | |
| 854 ASSERT(image); | |
| 855 IntSize s = size(image); | |
| 856 ExceptionCode ec; | |
| 857 drawImage(image, x, y, s.width(), s.height(), ec); | |
| 858 } | |
| 859 | |
| 860 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, | |
| 861 float x, float y, float width, float height, ExceptionCode& ec) | |
| 862 { | |
| 863 ASSERT(image); | |
| 864 IntSize s = size(image); | |
| 865 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, wid
th, height), ec); | |
| 866 } | |
| 867 | |
| 868 void CanvasRenderingContext2D::checkOrigin(const KURL& url) | |
| 869 { | |
| 870 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); | |
| 871 if (!m_canvas->document()->securityOrigin()->canAccess(origin.get())) | |
| 872 m_canvas->setOriginTainted(); | |
| 873 } | |
| 874 | |
| 875 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRec
t& srcRect, const FloatRect& dstRect, | |
| 876 ExceptionCode& ec) | |
| 877 { | |
| 878 ASSERT(image); | |
| 879 | |
| 880 ec = 0; | |
| 881 | |
| 882 FloatRect imageRect = FloatRect(FloatPoint(), size(image)); | |
| 883 if (!imageRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || s
rcRect.height() == 0) { | |
| 884 ec = INDEX_SIZE_ERR; | |
| 885 return; | |
| 886 } | |
| 887 | |
| 888 if (!dstRect.width() || !dstRect.height()) | |
| 889 return; | |
| 890 | |
| 891 GraphicsContext* c = drawingContext(); | |
| 892 if (!c) | |
| 893 return; | |
| 894 | |
| 895 CachedImage* cachedImage = image->cachedImage(); | |
| 896 if (!cachedImage) | |
| 897 return; | |
| 898 | |
| 899 if (m_canvas->originClean()) | |
| 900 checkOrigin(cachedImage->response().url()); | |
| 901 | |
| 902 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigi
n()) | |
| 903 m_canvas->setOriginTainted(); | |
| 904 | |
| 905 FloatRect sourceRect = c->roundToDevicePixels(srcRect); | |
| 906 FloatRect destRect = c->roundToDevicePixels(dstRect); | |
| 907 willDraw(destRect); | |
| 908 c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalCom
posite); | |
| 909 } | |
| 910 | |
| 911 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, flo
at y) | |
| 912 { | |
| 913 ASSERT(canvas); | |
| 914 ExceptionCode ec; | |
| 915 drawImage(canvas, x, y, canvas->width(), canvas->height(), ec); | |
| 916 } | |
| 917 | |
| 918 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, | |
| 919 float x, float y, float width, float height, ExceptionCode& ec) | |
| 920 { | |
| 921 ASSERT(canvas); | |
| 922 drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatR
ect(x, y, width, height), ec); | |
| 923 } | |
| 924 | |
| 925 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatR
ect& srcRect, | |
| 926 const FloatRect& dstRect, ExceptionCode& ec) | |
| 927 { | |
| 928 ASSERT(canvas); | |
| 929 | |
| 930 ec = 0; | |
| 931 | |
| 932 FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size()); | |
| 933 if (!srcCanvasRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0
|| srcRect.height() == 0) { | |
| 934 ec = INDEX_SIZE_ERR; | |
| 935 return; | |
| 936 } | |
| 937 | |
| 938 if (!dstRect.width() || !dstRect.height()) | |
| 939 return; | |
| 940 | |
| 941 GraphicsContext* c = drawingContext(); | |
| 942 if (!c) | |
| 943 return; | |
| 944 | |
| 945 FloatRect sourceRect = c->roundToDevicePixels(srcRect); | |
| 946 FloatRect destRect = c->roundToDevicePixels(dstRect); | |
| 947 | |
| 948 // FIXME: Do this through platform-independent GraphicsContext API. | |
| 949 ImageBuffer* buffer = canvas->buffer(); | |
| 950 if (!buffer) | |
| 951 return; | |
| 952 | |
| 953 if (!canvas->originClean()) | |
| 954 m_canvas->setOriginTainted(); | |
| 955 | |
| 956 c->drawImage(buffer->image(), destRect, sourceRect, state().m_globalComposit
e); | |
| 957 willDraw(destRect); // This call comes after drawImage, since the buffer we
draw into may be our own, and we need to make sure it is dirty. | |
| 958 // FIXME: Arguably willDraw should become didDraw and oc
cur after drawing calls and not before them to avoid problems like this. | |
| 959 } | |
| 960 | |
| 961 // FIXME: Why isn't this just another overload of drawImage? Why have a differen
t name? | |
| 962 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image, | |
| 963 float sx, float sy, float sw, float sh, | |
| 964 float dx, float dy, float dw, float dh, | |
| 965 const String& compositeOperation) | |
| 966 { | |
| 967 if (!image) | |
| 968 return; | |
| 969 | |
| 970 CachedImage* cachedImage = image->cachedImage(); | |
| 971 if (!cachedImage) | |
| 972 return; | |
| 973 | |
| 974 if (m_canvas->originClean()) | |
| 975 checkOrigin(cachedImage->response().url()); | |
| 976 | |
| 977 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigi
n()) | |
| 978 m_canvas->setOriginTainted(); | |
| 979 | |
| 980 GraphicsContext* c = drawingContext(); | |
| 981 if (!c) | |
| 982 return; | |
| 983 | |
| 984 CompositeOperator op; | |
| 985 if (!parseCompositeOperator(compositeOperation, op)) | |
| 986 op = CompositeSourceOver; | |
| 987 | |
| 988 FloatRect destRect = FloatRect(dx, dy, dw, dh); | |
| 989 willDraw(destRect); | |
| 990 c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op); | |
| 991 } | |
| 992 | |
| 993 void CanvasRenderingContext2D::setAlpha(float alpha) | |
| 994 { | |
| 995 setGlobalAlpha(alpha); | |
| 996 } | |
| 997 | |
| 998 void CanvasRenderingContext2D::setCompositeOperation(const String& operation) | |
| 999 { | |
| 1000 setGlobalCompositeOperation(operation); | |
| 1001 } | |
| 1002 | |
| 1003 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float
x0, float y0, float x1, float y1) | |
| 1004 { | |
| 1005 return CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1)); | |
| 1006 } | |
| 1007 | |
| 1008 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float
x0, float y0, float r0, float x1, float y1, float r1) | |
| 1009 { | |
| 1010 return CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1
); | |
| 1011 } | |
| 1012 | |
| 1013 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageEleme
nt* image, | |
| 1014 const String& repetitionType, ExceptionCode& ec) | |
| 1015 { | |
| 1016 bool repeatX, repeatY; | |
| 1017 ec = 0; | |
| 1018 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); | |
| 1019 if (ec) | |
| 1020 return 0; | |
| 1021 | |
| 1022 if (!image->complete()) { | |
| 1023 ec = INVALID_STATE_ERR; | |
| 1024 return 0; | |
| 1025 } | |
| 1026 | |
| 1027 CachedImage* cachedImage = image->cachedImage(); | |
| 1028 if (!cachedImage || !image->cachedImage()->image()) | |
| 1029 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true)
; | |
| 1030 | |
| 1031 KURL url(cachedImage->url()); | |
| 1032 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); | |
| 1033 bool originClean = m_canvas->document()->securityOrigin()->canAccess(origin.
get()); | |
| 1034 return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originC
lean); | |
| 1035 } | |
| 1036 | |
| 1037 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElem
ent* canvas, | |
| 1038 const String& repetitionType, ExceptionCode& ec) | |
| 1039 { | |
| 1040 bool repeatX, repeatY; | |
| 1041 ec = 0; | |
| 1042 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); | |
| 1043 if (ec) | |
| 1044 return 0; | |
| 1045 return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, ca
nvas->originClean()); | |
| 1046 } | |
| 1047 | |
| 1048 void CanvasRenderingContext2D::willDraw(const FloatRect& r) | |
| 1049 { | |
| 1050 GraphicsContext* c = drawingContext(); | |
| 1051 if (!c) | |
| 1052 return; | |
| 1053 | |
| 1054 m_canvas->willDraw(c->getCTM().mapRect(r)); | |
| 1055 } | |
| 1056 | |
| 1057 GraphicsContext* CanvasRenderingContext2D::drawingContext() const | |
| 1058 { | |
| 1059 return m_canvas->drawingContext(); | |
| 1060 } | |
| 1061 | |
| 1062 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size) | |
| 1063 { | |
| 1064 PassRefPtr<ImageData> data = ImageData::create(size.width(), size.height()); | |
| 1065 memset(data->data()->data().data(), 0, data->data()->length()); | |
| 1066 return data; | |
| 1067 } | |
| 1068 | |
| 1069 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float
sh) const | |
| 1070 { | |
| 1071 FloatSize unscaledSize(sw, sh); | |
| 1072 IntSize scaledSize = m_canvas->convertLogicalToDevice(unscaledSize); | |
| 1073 if (scaledSize.width() < 1) | |
| 1074 scaledSize.setWidth(1); | |
| 1075 if (scaledSize.height() < 1) | |
| 1076 scaledSize.setHeight(1); | |
| 1077 | |
| 1078 return createEmptyImageData(scaledSize); | |
| 1079 } | |
| 1080 | |
| 1081 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy,
float sw, float sh, ExceptionCode& ec) const | |
| 1082 { | |
| 1083 if (!m_canvas->originClean()) { | |
| 1084 ec = SECURITY_ERR; | |
| 1085 return 0; | |
| 1086 } | |
| 1087 | |
| 1088 FloatRect unscaledRect(sx, sy, sw, sh); | |
| 1089 IntRect scaledRect = m_canvas->convertLogicalToDevice(unscaledRect); | |
| 1090 if (scaledRect.width() < 1) | |
| 1091 scaledRect.setWidth(1); | |
| 1092 if (scaledRect.height() < 1) | |
| 1093 scaledRect.setHeight(1); | |
| 1094 ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0; | |
| 1095 if (!buffer) | |
| 1096 return createEmptyImageData(scaledRect.size()); | |
| 1097 return buffer->getImageData(scaledRect); | |
| 1098 } | |
| 1099 | |
| 1100 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy,
ExceptionCode& ec) | |
| 1101 { | |
| 1102 if (!data) { | |
| 1103 ec = TYPE_MISMATCH_ERR; | |
| 1104 return; | |
| 1105 } | |
| 1106 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec); | |
| 1107 } | |
| 1108 | |
| 1109 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy,
float dirtyX, float dirtyY, | |
| 1110 float dirtyWidth, float dirtyHeight,
ExceptionCode& ec) | |
| 1111 { | |
| 1112 if (!data) { | |
| 1113 ec = TYPE_MISMATCH_ERR; | |
| 1114 return; | |
| 1115 } | |
| 1116 if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) || | |
| 1117 !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) { | |
| 1118 ec = INDEX_SIZE_ERR; | |
| 1119 return; | |
| 1120 } | |
| 1121 | |
| 1122 ImageBuffer* buffer = m_canvas->buffer(); | |
| 1123 if (!buffer) | |
| 1124 return; | |
| 1125 | |
| 1126 if (dirtyWidth < 0) { | |
| 1127 dirtyX += dirtyWidth; | |
| 1128 dirtyWidth = -dirtyWidth; | |
| 1129 } | |
| 1130 | |
| 1131 if (dirtyHeight < 0) { | |
| 1132 dirtyY += dirtyHeight; | |
| 1133 dirtyHeight = -dirtyHeight; | |
| 1134 } | |
| 1135 | |
| 1136 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); | |
| 1137 clipRect.intersect(IntRect(0, 0, data->width(), data->height())); | |
| 1138 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy)); | |
| 1139 IntRect sourceRect = enclosingIntRect(clipRect); | |
| 1140 sourceRect.move(destOffset); | |
| 1141 sourceRect.intersect(IntRect(IntPoint(), buffer->size())); | |
| 1142 if (sourceRect.isEmpty()) | |
| 1143 return; | |
| 1144 willDraw(sourceRect); | |
| 1145 sourceRect.move(-destOffset); | |
| 1146 IntPoint destPoint(destOffset.width(), destOffset.height()); | |
| 1147 | |
| 1148 buffer->putImageData(data, sourceRect, destPoint); | |
| 1149 } | |
| 1150 | |
| 1151 String CanvasRenderingContext2D::font() const | |
| 1152 { | |
| 1153 return state().m_unparsedFont; | |
| 1154 } | |
| 1155 | |
| 1156 void CanvasRenderingContext2D::setFont(const String& newFont) | |
| 1157 { | |
| 1158 RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::cr
eate(); | |
| 1159 CSSParser parser(!m_canvas->document()->inCompatMode()); // Use the parse mo
de of the canvas' document when parsing CSS. | |
| 1160 | |
| 1161 String declarationText("font: "); | |
| 1162 declarationText += newFont; | |
| 1163 parser.parseDeclaration(tempDecl.get(), declarationText); | |
| 1164 if (!tempDecl->length()) | |
| 1165 return; | |
| 1166 | |
| 1167 // The parse succeeded. | |
| 1168 state().m_unparsedFont = newFont; | |
| 1169 | |
| 1170 // Map the <canvas> font into the text style. If the font uses keywords like
larger/smaller, these will work | |
| 1171 // relative to the canvas. | |
| 1172 RenderArena* arena = m_canvas->document()->renderArena(); | |
| 1173 RenderStyle* newStyle = new (arena) RenderStyle(); | |
| 1174 newStyle->ref(); | |
| 1175 if (m_canvas->computedStyle()) | |
| 1176 newStyle->setFontDescription(m_canvas->computedStyle()->fontDescription(
)); | |
| 1177 | |
| 1178 // Now map the font property into the style. | |
| 1179 CSSStyleSelector* styleSelector = m_canvas->document()->styleSelector(); | |
| 1180 styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCS
SValue(CSSPropertyFont).get(), newStyle); | |
| 1181 | |
| 1182 state().m_font = newStyle->font(); | |
| 1183 state().m_font.update(styleSelector->fontSelector()); | |
| 1184 state().m_realizedFont = true; | |
| 1185 | |
| 1186 newStyle->deref(arena); | |
| 1187 | |
| 1188 // Set the font in the graphics context. | |
| 1189 GraphicsContext* c = drawingContext(); | |
| 1190 if (!c) | |
| 1191 return; | |
| 1192 c->setFont(state().m_font); | |
| 1193 } | |
| 1194 | |
| 1195 String CanvasRenderingContext2D::textAlign() const | |
| 1196 { | |
| 1197 return textAlignName(state().m_textAlign); | |
| 1198 } | |
| 1199 | |
| 1200 void CanvasRenderingContext2D::setTextAlign(const String& s) | |
| 1201 { | |
| 1202 TextAlign align; | |
| 1203 if (!parseTextAlign(s, align)) | |
| 1204 return; | |
| 1205 state().m_textAlign = align; | |
| 1206 } | |
| 1207 | |
| 1208 String CanvasRenderingContext2D::textBaseline() const | |
| 1209 { | |
| 1210 return textBaselineName(state().m_textBaseline); | |
| 1211 } | |
| 1212 | |
| 1213 void CanvasRenderingContext2D::setTextBaseline(const String& s) | |
| 1214 { | |
| 1215 TextBaseline baseline; | |
| 1216 if (!parseTextBaseline(s, baseline)) | |
| 1217 return; | |
| 1218 state().m_textBaseline = baseline; | |
| 1219 } | |
| 1220 | |
| 1221 void CanvasRenderingContext2D::fillText(const String& text, float x, float y) | |
| 1222 { | |
| 1223 drawTextInternal(text, x, y, true); | |
| 1224 } | |
| 1225 | |
| 1226 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, fl
oat maxWidth) | |
| 1227 { | |
| 1228 drawTextInternal(text, x, y, true, maxWidth, true); | |
| 1229 } | |
| 1230 | |
| 1231 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y) | |
| 1232 { | |
| 1233 drawTextInternal(text, x, y, false); | |
| 1234 } | |
| 1235 | |
| 1236 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y,
float maxWidth) | |
| 1237 { | |
| 1238 drawTextInternal(text, x, y, false, maxWidth, true); | |
| 1239 } | |
| 1240 | |
| 1241 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text
) | |
| 1242 { | |
| 1243 RefPtr<TextMetrics> metrics = TextMetrics::create(); | |
| 1244 metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length(
)))); | |
| 1245 return metrics; | |
| 1246 } | |
| 1247 | |
| 1248 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo
at y, bool fill, float maxWidth, bool useMaxWidth) | |
| 1249 { | |
| 1250 GraphicsContext* c = drawingContext(); | |
| 1251 if (!c) | |
| 1252 return; | |
| 1253 | |
| 1254 const Font& font = accessFont(); | |
| 1255 | |
| 1256 // FIXME: Handle maxWidth. | |
| 1257 // FIXME: Need to turn off font smoothing. | |
| 1258 | |
| 1259 bool rtl = m_canvas->computedStyle() ? m_canvas->computedStyle()->direction(
) == RTL : false; | |
| 1260 bool override = m_canvas->computedStyle() ? m_canvas->computedStyle()->unico
deBidi() == Override : false; | |
| 1261 | |
| 1262 unsigned length = text.length(); | |
| 1263 const UChar* string = text.characters(); | |
| 1264 TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false); | |
| 1265 | |
| 1266 // Draw the item text at the correct point. | |
| 1267 FloatPoint location(x, y); | |
| 1268 switch (state().m_textBaseline) { | |
| 1269 case TopTextBaseline: | |
| 1270 case HangingTextBaseline: | |
| 1271 location.setY(y + font.ascent()); | |
| 1272 break; | |
| 1273 case BottomTextBaseline: | |
| 1274 case IdeographicTextBaseline: | |
| 1275 location.setY(y - font.descent()); | |
| 1276 break; | |
| 1277 case MiddleTextBaseline: | |
| 1278 location.setY(y - font.descent() + font.height() / 2); | |
| 1279 break; | |
| 1280 case AlphabeticTextBaseline: | |
| 1281 default: | |
| 1282 // Do nothing. | |
| 1283 break; | |
| 1284 } | |
| 1285 | |
| 1286 float width = font.width(TextRun(text, false, 0, 0, rtl, override)); | |
| 1287 | |
| 1288 TextAlign align = state().m_textAlign; | |
| 1289 if (align == StartTextAlign) | |
| 1290 align = rtl ? RightTextAlign : LeftTextAlign; | |
| 1291 else if (align == EndTextAlign) | |
| 1292 align = rtl ? LeftTextAlign : RightTextAlign; | |
| 1293 | |
| 1294 switch (align) { | |
| 1295 case CenterTextAlign: | |
| 1296 location.setX(location.x() - width / 2); | |
| 1297 break; | |
| 1298 case RightTextAlign: | |
| 1299 location.setX(location.x() - width); | |
| 1300 break; | |
| 1301 default: | |
| 1302 break; | |
| 1303 } | |
| 1304 | |
| 1305 // The slop built in to this mask rect matches the heuristic used in FontCGW
in.cpp for GDI text. | |
| 1306 FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y(
) - font.ascent() - font.lineGap(), | |
| 1307 width + font.height(), font.lineSpacing()); | |
| 1308 if (!fill) | |
| 1309 textRect.inflate(c->strokeThickness() / 2); | |
| 1310 | |
| 1311 CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_stroke
Style.get(); | |
| 1312 if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) { | |
| 1313 IntRect maskRect = enclosingIntRect(textRect); | |
| 1314 | |
| 1315 auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), f
alse); | |
| 1316 | |
| 1317 GraphicsContext* maskImageContext = maskImage->context(); | |
| 1318 | |
| 1319 if (fill) | |
| 1320 maskImageContext->setFillColor(Color::black); | |
| 1321 else { | |
| 1322 maskImageContext->setStrokeColor(Color::black); | |
| 1323 maskImageContext->setStrokeThickness(c->strokeThickness()); | |
| 1324 } | |
| 1325 | |
| 1326 maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke); | |
| 1327 maskImageContext->translate(-maskRect.x(), -maskRect.y()); | |
| 1328 | |
| 1329 maskImageContext->setFont(font); | |
| 1330 maskImageContext->drawBidiText(textRun, location); | |
| 1331 | |
| 1332 c->save(); | |
| 1333 c->clipToImageBuffer(maskRect, maskImage.get()); | |
| 1334 drawStyle->applyFillColor(c); | |
| 1335 c->fillRect(maskRect); | |
| 1336 c->restore(); | |
| 1337 | |
| 1338 return; | |
| 1339 } | |
| 1340 | |
| 1341 c->setTextDrawingMode(fill ? cTextFill : cTextStroke); | |
| 1342 c->drawBidiText(textRun, location); | |
| 1343 } | |
| 1344 | |
| 1345 const Font& CanvasRenderingContext2D::accessFont() | |
| 1346 { | |
| 1347 if (!state().m_realizedFont) | |
| 1348 setFont(state().m_unparsedFont); | |
| 1349 return state().m_font; | |
| 1350 } | |
| 1351 | |
| 1352 } // namespace WebCore | |
| OLD | NEW |