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