1/* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. 3 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) 4 * Copyright (C) 2007 Alp Toker <alp@atoker.com> 5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> 6 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> 7 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. 8 * Copyright (C) 2012 Intel Corporation. All rights reserved. 9 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33#include "config.h" 34#include "CanvasRenderingContext2D.h" 35 36#include "AffineTransform.h" 37#include "CSSFontSelector.h" 38#include "CSSParser.h" 39#include "CSSPropertyNames.h" 40#include "CachedImage.h" 41#include "CanvasGradient.h" 42#include "CanvasPattern.h" 43#include "Console.h" 44#include "DOMPath.h" 45#include "ExceptionCode.h" 46#include "ExceptionCodePlaceholder.h" 47#include "FloatConversion.h" 48#include "FloatQuad.h" 49#include "FontCache.h" 50#include "GraphicsContext.h" 51#include "HTMLCanvasElement.h" 52#include "HTMLImageElement.h" 53#include "HTMLMediaElement.h" 54#include "HTMLNames.h" 55#include "HTMLVideoElement.h" 56#include "ImageData.h" 57#include "KURL.h" 58#include "Page.h" 59#include "RenderHTMLCanvas.h" 60#include "SecurityOrigin.h" 61#include "Settings.h" 62#include "StrokeStyleApplier.h" 63#include "StylePropertySet.h" 64#include "StyleResolver.h" 65#include "TextMetrics.h" 66#include "TextRun.h" 67 68#if USE(ACCELERATED_COMPOSITING) 69#include "RenderLayer.h" 70#endif 71 72#include <wtf/CheckedArithmetic.h> 73#include <wtf/MathExtras.h> 74#include <wtf/OwnPtr.h> 75#include <wtf/Uint8ClampedArray.h> 76#include <wtf/text/StringBuilder.h> 77 78#if USE(CG) 79#include <ApplicationServices/ApplicationServices.h> 80#endif 81 82using namespace std; 83 84namespace WebCore { 85 86using namespace HTMLNames; 87 88static const int defaultFontSize = 10; 89static const char* const defaultFontFamily = "sans-serif"; 90static const char* const defaultFont = "10px sans-serif"; 91 92static bool isOriginClean(CachedImage* cachedImage, SecurityOrigin* securityOrigin) 93{ 94 if (!cachedImage->image()->hasSingleSecurityOrigin()) 95 return false; 96 if (cachedImage->passesAccessControlCheck(securityOrigin)) 97 return true; 98 return !securityOrigin->taintsCanvas(cachedImage->response().url()); 99} 100 101class CanvasStrokeStyleApplier : public StrokeStyleApplier { 102public: 103 CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext) 104 : m_canvasContext(canvasContext) 105 { 106 } 107 108 virtual void strokeStyle(GraphicsContext* c) 109 { 110 c->setStrokeThickness(m_canvasContext->lineWidth()); 111 c->setLineCap(m_canvasContext->getLineCap()); 112 c->setLineJoin(m_canvasContext->getLineJoin()); 113 c->setMiterLimit(m_canvasContext->miterLimit()); 114 const Vector<float>& lineDash = m_canvasContext->getLineDash(); 115 DashArray convertedLineDash(lineDash.size()); 116 for (size_t i = 0; i < lineDash.size(); ++i) 117 convertedLineDash[i] = static_cast<DashArrayElement>(lineDash[i]); 118 c->setLineDash(convertedLineDash, m_canvasContext->lineDashOffset()); 119 } 120 121private: 122 CanvasRenderingContext2D* m_canvasContext; 123}; 124 125CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode) 126 : CanvasRenderingContext(canvas) 127 , m_stateStack(1) 128 , m_unrealizedSaveCount(0) 129 , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode) 130#if ENABLE(DASHBOARD_SUPPORT) 131 , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode) 132#endif 133{ 134#if !ENABLE(DASHBOARD_SUPPORT) 135 ASSERT_UNUSED(usesDashboardCompatibilityMode, !usesDashboardCompatibilityMode); 136#endif 137} 138 139void CanvasRenderingContext2D::unwindStateStack() 140{ 141 // Ensure that the state stack in the ImageBuffer's context 142 // is cleared before destruction, to avoid assertions in the 143 // GraphicsContext dtor. 144 if (size_t stackSize = m_stateStack.size()) { 145 if (GraphicsContext* context = canvas()->existingDrawingContext()) { 146 while (--stackSize) 147 context->restore(); 148 } 149 } 150} 151 152CanvasRenderingContext2D::~CanvasRenderingContext2D() 153{ 154#if !ASSERT_DISABLED 155 unwindStateStack(); 156#endif 157} 158 159bool CanvasRenderingContext2D::isAccelerated() const 160{ 161#if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS) 162 if (!canvas()->hasCreatedImageBuffer()) 163 return false; 164 GraphicsContext* context = drawingContext(); 165 return context && context->isAcceleratedContext(); 166#else 167 return false; 168#endif 169} 170 171void CanvasRenderingContext2D::reset() 172{ 173 unwindStateStack(); 174 m_stateStack.resize(1); 175 m_stateStack.first() = State(); 176 m_path.clear(); 177 m_unrealizedSaveCount = 0; 178} 179 180CanvasRenderingContext2D::State::State() 181 : m_strokeStyle(Color::black) 182 , m_fillStyle(Color::black) 183 , m_lineWidth(1) 184 , m_lineCap(ButtCap) 185 , m_lineJoin(MiterJoin) 186 , m_miterLimit(10) 187 , m_shadowBlur(0) 188 , m_shadowColor(Color::transparent) 189 , m_globalAlpha(1) 190 , m_globalComposite(CompositeSourceOver) 191 , m_globalBlend(BlendModeNormal) 192 , m_invertibleCTM(true) 193 , m_lineDashOffset(0) 194 , m_imageSmoothingEnabled(true) 195 , m_textAlign(StartTextAlign) 196 , m_textBaseline(AlphabeticTextBaseline) 197 , m_unparsedFont(defaultFont) 198 , m_realizedFont(false) 199{ 200} 201 202CanvasRenderingContext2D::State::State(const State& other) 203 : FontSelectorClient() 204 , m_unparsedStrokeColor(other.m_unparsedStrokeColor) 205 , m_unparsedFillColor(other.m_unparsedFillColor) 206 , m_strokeStyle(other.m_strokeStyle) 207 , m_fillStyle(other.m_fillStyle) 208 , m_lineWidth(other.m_lineWidth) 209 , m_lineCap(other.m_lineCap) 210 , m_lineJoin(other.m_lineJoin) 211 , m_miterLimit(other.m_miterLimit) 212 , m_shadowOffset(other.m_shadowOffset) 213 , m_shadowBlur(other.m_shadowBlur) 214 , m_shadowColor(other.m_shadowColor) 215 , m_globalAlpha(other.m_globalAlpha) 216 , m_globalComposite(other.m_globalComposite) 217 , m_globalBlend(other.m_globalBlend) 218 , m_transform(other.m_transform) 219 , m_invertibleCTM(other.m_invertibleCTM) 220 , m_lineDashOffset(other.m_lineDashOffset) 221 , m_imageSmoothingEnabled(other.m_imageSmoothingEnabled) 222 , m_textAlign(other.m_textAlign) 223 , m_textBaseline(other.m_textBaseline) 224 , m_unparsedFont(other.m_unparsedFont) 225 , m_font(other.m_font) 226 , m_realizedFont(other.m_realizedFont) 227{ 228 if (m_realizedFont) 229 m_font.fontSelector()->registerForInvalidationCallbacks(this); 230} 231 232CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other) 233{ 234 if (this == &other) 235 return *this; 236 237 if (m_realizedFont) 238 m_font.fontSelector()->unregisterForInvalidationCallbacks(this); 239 240 m_unparsedStrokeColor = other.m_unparsedStrokeColor; 241 m_unparsedFillColor = other.m_unparsedFillColor; 242 m_strokeStyle = other.m_strokeStyle; 243 m_fillStyle = other.m_fillStyle; 244 m_lineWidth = other.m_lineWidth; 245 m_lineCap = other.m_lineCap; 246 m_lineJoin = other.m_lineJoin; 247 m_miterLimit = other.m_miterLimit; 248 m_shadowOffset = other.m_shadowOffset; 249 m_shadowBlur = other.m_shadowBlur; 250 m_shadowColor = other.m_shadowColor; 251 m_globalAlpha = other.m_globalAlpha; 252 m_globalComposite = other.m_globalComposite; 253 m_globalBlend = other.m_globalBlend; 254 m_transform = other.m_transform; 255 m_invertibleCTM = other.m_invertibleCTM; 256 m_imageSmoothingEnabled = other.m_imageSmoothingEnabled; 257 m_textAlign = other.m_textAlign; 258 m_textBaseline = other.m_textBaseline; 259 m_unparsedFont = other.m_unparsedFont; 260 m_font = other.m_font; 261 m_realizedFont = other.m_realizedFont; 262 263 if (m_realizedFont) 264 m_font.fontSelector()->registerForInvalidationCallbacks(this); 265 266 return *this; 267} 268 269CanvasRenderingContext2D::State::~State() 270{ 271 if (m_realizedFont) 272 m_font.fontSelector()->unregisterForInvalidationCallbacks(this); 273} 274 275void CanvasRenderingContext2D::State::fontsNeedUpdate(FontSelector* fontSelector) 276{ 277 ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector()); 278 ASSERT(m_realizedFont); 279 280 m_font.update(fontSelector); 281} 282 283void CanvasRenderingContext2D::realizeSavesLoop() 284{ 285 ASSERT(m_unrealizedSaveCount); 286 ASSERT(m_stateStack.size() >= 1); 287 GraphicsContext* context = drawingContext(); 288 do { 289 m_stateStack.append(state()); 290 if (context) 291 context->save(); 292 } while (--m_unrealizedSaveCount); 293} 294 295void CanvasRenderingContext2D::restore() 296{ 297 if (m_unrealizedSaveCount) { 298 --m_unrealizedSaveCount; 299 return; 300 } 301 ASSERT(m_stateStack.size() >= 1); 302 if (m_stateStack.size() <= 1) 303 return; 304 m_path.transform(state().m_transform); 305 m_stateStack.removeLast(); 306 m_path.transform(state().m_transform.inverse()); 307 GraphicsContext* c = drawingContext(); 308 if (!c) 309 return; 310 c->restore(); 311} 312 313void CanvasRenderingContext2D::setStrokeStyle(CanvasStyle style) 314{ 315 if (!style.isValid()) 316 return; 317 318 if (state().m_strokeStyle.isValid() && state().m_strokeStyle.isEquivalentColor(style)) 319 return; 320 321 if (style.isCurrentColor()) { 322 if (style.hasOverrideAlpha()) 323 style = CanvasStyle(colorWithOverrideAlpha(currentColor(canvas()), style.overrideAlpha())); 324 else 325 style = CanvasStyle(currentColor(canvas())); 326 } else 327 checkOrigin(style.canvasPattern()); 328 329 realizeSaves(); 330 State& state = modifiableState(); 331 state.m_strokeStyle = style; 332 GraphicsContext* c = drawingContext(); 333 if (!c) 334 return; 335 state.m_strokeStyle.applyStrokeColor(c); 336 state.m_unparsedStrokeColor = String(); 337} 338 339void CanvasRenderingContext2D::setFillStyle(CanvasStyle style) 340{ 341 if (!style.isValid()) 342 return; 343 344 if (state().m_fillStyle.isValid() && state().m_fillStyle.isEquivalentColor(style)) 345 return; 346 347 if (style.isCurrentColor()) { 348 if (style.hasOverrideAlpha()) 349 style = CanvasStyle(colorWithOverrideAlpha(currentColor(canvas()), style.overrideAlpha())); 350 else 351 style = CanvasStyle(currentColor(canvas())); 352 } else 353 checkOrigin(style.canvasPattern()); 354 355 realizeSaves(); 356 State& state = modifiableState(); 357 state.m_fillStyle = style; 358 GraphicsContext* c = drawingContext(); 359 if (!c) 360 return; 361 state.m_fillStyle.applyFillColor(c); 362 state.m_unparsedFillColor = String(); 363} 364 365float CanvasRenderingContext2D::lineWidth() const 366{ 367 return state().m_lineWidth; 368} 369 370void CanvasRenderingContext2D::setLineWidth(float width) 371{ 372 if (!(std::isfinite(width) && width > 0)) 373 return; 374 if (state().m_lineWidth == width) 375 return; 376 realizeSaves(); 377 modifiableState().m_lineWidth = width; 378 GraphicsContext* c = drawingContext(); 379 if (!c) 380 return; 381 c->setStrokeThickness(width); 382} 383 384String CanvasRenderingContext2D::lineCap() const 385{ 386 return lineCapName(state().m_lineCap); 387} 388 389void CanvasRenderingContext2D::setLineCap(const String& s) 390{ 391 LineCap cap; 392 if (!parseLineCap(s, cap)) 393 return; 394 if (state().m_lineCap == cap) 395 return; 396 realizeSaves(); 397 modifiableState().m_lineCap = cap; 398 GraphicsContext* c = drawingContext(); 399 if (!c) 400 return; 401 c->setLineCap(cap); 402} 403 404String CanvasRenderingContext2D::lineJoin() const 405{ 406 return lineJoinName(state().m_lineJoin); 407} 408 409void CanvasRenderingContext2D::setLineJoin(const String& s) 410{ 411 LineJoin join; 412 if (!parseLineJoin(s, join)) 413 return; 414 if (state().m_lineJoin == join) 415 return; 416 realizeSaves(); 417 modifiableState().m_lineJoin = join; 418 GraphicsContext* c = drawingContext(); 419 if (!c) 420 return; 421 c->setLineJoin(join); 422} 423 424float CanvasRenderingContext2D::miterLimit() const 425{ 426 return state().m_miterLimit; 427} 428 429void CanvasRenderingContext2D::setMiterLimit(float limit) 430{ 431 if (!(std::isfinite(limit) && limit > 0)) 432 return; 433 if (state().m_miterLimit == limit) 434 return; 435 realizeSaves(); 436 modifiableState().m_miterLimit = limit; 437 GraphicsContext* c = drawingContext(); 438 if (!c) 439 return; 440 c->setMiterLimit(limit); 441} 442 443float CanvasRenderingContext2D::shadowOffsetX() const 444{ 445 return state().m_shadowOffset.width(); 446} 447 448void CanvasRenderingContext2D::setShadowOffsetX(float x) 449{ 450 if (!std::isfinite(x)) 451 return; 452 if (state().m_shadowOffset.width() == x) 453 return; 454 realizeSaves(); 455 modifiableState().m_shadowOffset.setWidth(x); 456 applyShadow(); 457} 458 459float CanvasRenderingContext2D::shadowOffsetY() const 460{ 461 return state().m_shadowOffset.height(); 462} 463 464void CanvasRenderingContext2D::setShadowOffsetY(float y) 465{ 466 if (!std::isfinite(y)) 467 return; 468 if (state().m_shadowOffset.height() == y) 469 return; 470 realizeSaves(); 471 modifiableState().m_shadowOffset.setHeight(y); 472 applyShadow(); 473} 474 475float CanvasRenderingContext2D::shadowBlur() const 476{ 477 return state().m_shadowBlur; 478} 479 480void CanvasRenderingContext2D::setShadowBlur(float blur) 481{ 482 if (!(std::isfinite(blur) && blur >= 0)) 483 return; 484 if (state().m_shadowBlur == blur) 485 return; 486 realizeSaves(); 487 modifiableState().m_shadowBlur = blur; 488 applyShadow(); 489} 490 491String CanvasRenderingContext2D::shadowColor() const 492{ 493 return Color(state().m_shadowColor).serialized(); 494} 495 496void CanvasRenderingContext2D::setShadowColor(const String& color) 497{ 498 RGBA32 rgba; 499 if (!parseColorOrCurrentColor(rgba, color, canvas())) 500 return; 501 if (state().m_shadowColor == rgba) 502 return; 503 realizeSaves(); 504 modifiableState().m_shadowColor = rgba; 505 applyShadow(); 506} 507 508const Vector<float>& CanvasRenderingContext2D::getLineDash() const 509{ 510 return state().m_lineDash; 511} 512 513static bool lineDashSequenceIsValid(const Vector<float>& dash) 514{ 515 for (size_t i = 0; i < dash.size(); i++) { 516 if (!std::isfinite(dash[i]) || dash[i] < 0) 517 return false; 518 } 519 return true; 520} 521 522void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash) 523{ 524 if (!lineDashSequenceIsValid(dash)) 525 return; 526 527 realizeSaves(); 528 modifiableState().m_lineDash = dash; 529 // Spec requires the concatenation of two copies the dash list when the 530 // number of elements is odd 531 if (dash.size() % 2) 532 modifiableState().m_lineDash.appendVector(dash); 533 534 applyLineDash(); 535} 536 537void CanvasRenderingContext2D::setWebkitLineDash(const Vector<float>& dash) 538{ 539 if (!lineDashSequenceIsValid(dash)) 540 return; 541 542 realizeSaves(); 543 modifiableState().m_lineDash = dash; 544 545 applyLineDash(); 546} 547 548float CanvasRenderingContext2D::lineDashOffset() const 549{ 550 return state().m_lineDashOffset; 551} 552 553void CanvasRenderingContext2D::setLineDashOffset(float offset) 554{ 555 if (!std::isfinite(offset) || state().m_lineDashOffset == offset) 556 return; 557 558 realizeSaves(); 559 modifiableState().m_lineDashOffset = offset; 560 applyLineDash(); 561} 562 563float CanvasRenderingContext2D::webkitLineDashOffset() const 564{ 565 return lineDashOffset(); 566} 567 568void CanvasRenderingContext2D::setWebkitLineDashOffset(float offset) 569{ 570 setLineDashOffset(offset); 571} 572 573void CanvasRenderingContext2D::applyLineDash() const 574{ 575 GraphicsContext* c = drawingContext(); 576 if (!c) 577 return; 578 DashArray convertedLineDash(state().m_lineDash.size()); 579 for (size_t i = 0; i < state().m_lineDash.size(); ++i) 580 convertedLineDash[i] = static_cast<DashArrayElement>(state().m_lineDash[i]); 581 c->setLineDash(convertedLineDash, state().m_lineDashOffset); 582} 583 584float CanvasRenderingContext2D::globalAlpha() const 585{ 586 return state().m_globalAlpha; 587} 588 589void CanvasRenderingContext2D::setGlobalAlpha(float alpha) 590{ 591 if (!(alpha >= 0 && alpha <= 1)) 592 return; 593 if (state().m_globalAlpha == alpha) 594 return; 595 realizeSaves(); 596 modifiableState().m_globalAlpha = alpha; 597 GraphicsContext* c = drawingContext(); 598 if (!c) 599 return; 600 c->setAlpha(alpha); 601} 602 603String CanvasRenderingContext2D::globalCompositeOperation() const 604{ 605 return compositeOperatorName(state().m_globalComposite, state().m_globalBlend); 606} 607 608void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation) 609{ 610 CompositeOperator op = CompositeSourceOver; 611 BlendMode blendMode = BlendModeNormal; 612 if (!parseCompositeAndBlendOperator(operation, op, blendMode)) 613 return; 614 if ((state().m_globalComposite == op) && (state().m_globalBlend == blendMode)) 615 return; 616 realizeSaves(); 617 modifiableState().m_globalComposite = op; 618 modifiableState().m_globalBlend = blendMode; 619 GraphicsContext* c = drawingContext(); 620 if (!c) 621 return; 622 c->setCompositeOperation(op, blendMode); 623} 624 625void CanvasRenderingContext2D::scale(float sx, float sy) 626{ 627 GraphicsContext* c = drawingContext(); 628 if (!c) 629 return; 630 if (!state().m_invertibleCTM) 631 return; 632 633 if (!std::isfinite(sx) | !std::isfinite(sy)) 634 return; 635 636 AffineTransform newTransform = state().m_transform; 637 newTransform.scaleNonUniform(sx, sy); 638 if (state().m_transform == newTransform) 639 return; 640 641 realizeSaves(); 642 643 if (!newTransform.isInvertible()) { 644 modifiableState().m_invertibleCTM = false; 645 return; 646 } 647 648 modifiableState().m_transform = newTransform; 649 c->scale(FloatSize(sx, sy)); 650 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy)); 651} 652 653void CanvasRenderingContext2D::rotate(float angleInRadians) 654{ 655 GraphicsContext* c = drawingContext(); 656 if (!c) 657 return; 658 if (!state().m_invertibleCTM) 659 return; 660 661 if (!std::isfinite(angleInRadians)) 662 return; 663 664 AffineTransform newTransform = state().m_transform; 665 newTransform.rotate(angleInRadians / piDouble * 180.0); 666 if (state().m_transform == newTransform) 667 return; 668 669 realizeSaves(); 670 671 if (!newTransform.isInvertible()) { 672 modifiableState().m_invertibleCTM = false; 673 return; 674 } 675 676 modifiableState().m_transform = newTransform; 677 c->rotate(angleInRadians); 678 m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0)); 679} 680 681void CanvasRenderingContext2D::translate(float tx, float ty) 682{ 683 GraphicsContext* c = drawingContext(); 684 if (!c) 685 return; 686 if (!state().m_invertibleCTM) 687 return; 688 689 if (!std::isfinite(tx) | !std::isfinite(ty)) 690 return; 691 692 AffineTransform newTransform = state().m_transform; 693 newTransform.translate(tx, ty); 694 if (state().m_transform == newTransform) 695 return; 696 697 realizeSaves(); 698 699 if (!newTransform.isInvertible()) { 700 modifiableState().m_invertibleCTM = false; 701 return; 702 } 703 704 modifiableState().m_transform = newTransform; 705 c->translate(tx, ty); 706 m_path.transform(AffineTransform().translate(-tx, -ty)); 707} 708 709void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy) 710{ 711 GraphicsContext* c = drawingContext(); 712 if (!c) 713 return; 714 if (!state().m_invertibleCTM) 715 return; 716 717 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy)) 718 return; 719 720 AffineTransform transform(m11, m12, m21, m22, dx, dy); 721 AffineTransform newTransform = state().m_transform * transform; 722 if (state().m_transform == newTransform) 723 return; 724 725 realizeSaves(); 726 727 if (!newTransform.isInvertible()) { 728 modifiableState().m_invertibleCTM = false; 729 return; 730 } 731 732 modifiableState().m_transform = newTransform; 733 c->concatCTM(transform); 734 m_path.transform(transform.inverse()); 735} 736 737void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy) 738{ 739 GraphicsContext* c = drawingContext(); 740 if (!c) 741 return; 742 743 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy)) 744 return; 745 746 AffineTransform ctm = state().m_transform; 747 if (!ctm.isInvertible()) 748 return; 749 750 realizeSaves(); 751 752 c->setCTM(canvas()->baseTransform()); 753 modifiableState().m_transform = AffineTransform(); 754 m_path.transform(ctm); 755 756 modifiableState().m_invertibleCTM = true; 757 transform(m11, m12, m21, m22, dx, dy); 758} 759 760void CanvasRenderingContext2D::setStrokeColor(const String& color) 761{ 762 if (color == state().m_unparsedStrokeColor) 763 return; 764 realizeSaves(); 765 setStrokeStyle(CanvasStyle::createFromString(color, canvas()->document())); 766 modifiableState().m_unparsedStrokeColor = color; 767} 768 769void CanvasRenderingContext2D::setStrokeColor(float grayLevel) 770{ 771 if (state().m_strokeStyle.isValid() && state().m_strokeStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) 772 return; 773 setStrokeStyle(CanvasStyle(grayLevel, 1.0f)); 774} 775 776void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha) 777{ 778 setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); 779} 780 781void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha) 782{ 783 if (state().m_strokeStyle.isValid() && state().m_strokeStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) 784 return; 785 setStrokeStyle(CanvasStyle(grayLevel, alpha)); 786} 787 788void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a) 789{ 790 if (state().m_strokeStyle.isValid() && state().m_strokeStyle.isEquivalentRGBA(r, g, b, a)) 791 return; 792 setStrokeStyle(CanvasStyle(r, g, b, a)); 793} 794 795void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a) 796{ 797 if (state().m_strokeStyle.isValid() && state().m_strokeStyle.isEquivalentCMYKA(c, m, y, k, a)) 798 return; 799 setStrokeStyle(CanvasStyle(c, m, y, k, a)); 800} 801 802void CanvasRenderingContext2D::setFillColor(const String& color) 803{ 804 if (color == state().m_unparsedFillColor) 805 return; 806 realizeSaves(); 807 setFillStyle(CanvasStyle::createFromString(color, canvas()->document())); 808 modifiableState().m_unparsedFillColor = color; 809} 810 811void CanvasRenderingContext2D::setFillColor(float grayLevel) 812{ 813 if (state().m_fillStyle.isValid() && state().m_fillStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) 814 return; 815 setFillStyle(CanvasStyle(grayLevel, 1.0f)); 816} 817 818void CanvasRenderingContext2D::setFillColor(const String& color, float alpha) 819{ 820 setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); 821} 822 823void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha) 824{ 825 if (state().m_fillStyle.isValid() && state().m_fillStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) 826 return; 827 setFillStyle(CanvasStyle(grayLevel, alpha)); 828} 829 830void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a) 831{ 832 if (state().m_fillStyle.isValid() && state().m_fillStyle.isEquivalentRGBA(r, g, b, a)) 833 return; 834 setFillStyle(CanvasStyle(r, g, b, a)); 835} 836 837void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a) 838{ 839 if (state().m_fillStyle.isValid() && state().m_fillStyle.isEquivalentCMYKA(c, m, y, k, a)) 840 return; 841 setFillStyle(CanvasStyle(c, m, y, k, a)); 842} 843 844void CanvasRenderingContext2D::beginPath() 845{ 846 m_path.clear(); 847} 848 849#if ENABLE(CANVAS_PATH) 850PassRefPtr<DOMPath> CanvasRenderingContext2D::currentPath() 851{ 852 return DOMPath::create(m_path); 853} 854 855void CanvasRenderingContext2D::setCurrentPath(DOMPath* path) 856{ 857 if (!path) 858 return; 859 m_path = path->path(); 860} 861#endif 862 863static bool validateRectForCanvas(float& x, float& y, float& width, float& height) 864{ 865 if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height)) 866 return false; 867 868 if (!width && !height) 869 return false; 870 871 if (width < 0) { 872 width = -width; 873 x -= width; 874 } 875 876 if (height < 0) { 877 height = -height; 878 y -= height; 879 } 880 881 return true; 882} 883 884#if ENABLE(DASHBOARD_SUPPORT) 885void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode() 886{ 887 if (m_usesDashboardCompatibilityMode) 888 m_path.clear(); 889} 890#endif 891 892static bool isFullCanvasCompositeMode(CompositeOperator op) 893{ 894 // See 4.8.11.1.3 Compositing 895 // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already 896 // implement the specification's behavior. 897 return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop; 898} 899 900static bool parseWinding(const String& windingRuleString, WindRule& windRule) 901{ 902 if (windingRuleString == "nonzero") 903 windRule = RULE_NONZERO; 904 else if (windingRuleString == "evenodd") 905 windRule = RULE_EVENODD; 906 else 907 return false; 908 909 return true; 910} 911 912void CanvasRenderingContext2D::fill(const String& windingRuleString) 913{ 914 GraphicsContext* c = drawingContext(); 915 if (!c) 916 return; 917 if (!state().m_invertibleCTM) 918 return; 919 920 // If gradient size is zero, then paint nothing. 921 Gradient* gradient = c->fillGradient(); 922 if (gradient && gradient->isZeroSize()) 923 return; 924 925 if (!m_path.isEmpty()) { 926 WindRule windRule = c->fillRule(); 927 WindRule newWindRule = RULE_NONZERO; 928 if (!parseWinding(windingRuleString, newWindRule)) 929 return; 930 c->setFillRule(newWindRule); 931 932 if (isFullCanvasCompositeMode(state().m_globalComposite)) { 933 fullCanvasCompositedFill(m_path); 934 didDrawEntireCanvas(); 935 } else if (state().m_globalComposite == CompositeCopy) { 936 clearCanvas(); 937 c->fillPath(m_path); 938 didDrawEntireCanvas(); 939 } else { 940 c->fillPath(m_path); 941 didDraw(m_path.fastBoundingRect()); 942 } 943 944 c->setFillRule(windRule); 945 } 946 947#if ENABLE(DASHBOARD_SUPPORT) 948 clearPathForDashboardBackwardCompatibilityMode(); 949#endif 950} 951 952void CanvasRenderingContext2D::stroke() 953{ 954 GraphicsContext* c = drawingContext(); 955 if (!c) 956 return; 957 if (!state().m_invertibleCTM) 958 return; 959 960 // If gradient size is zero, then paint nothing. 961 Gradient* gradient = c->strokeGradient(); 962 if (gradient && gradient->isZeroSize()) 963 return; 964 965 if (!m_path.isEmpty()) { 966 FloatRect dirtyRect = m_path.fastBoundingRect(); 967 inflateStrokeRect(dirtyRect); 968 969 c->strokePath(m_path); 970 didDraw(dirtyRect); 971 } 972 973#if ENABLE(DASHBOARD_SUPPORT) 974 clearPathForDashboardBackwardCompatibilityMode(); 975#endif 976} 977 978void CanvasRenderingContext2D::clip(const String& windingRuleString) 979{ 980 GraphicsContext* c = drawingContext(); 981 if (!c) 982 return; 983 if (!state().m_invertibleCTM) 984 return; 985 986 WindRule newWindRule = RULE_NONZERO; 987 if (!parseWinding(windingRuleString, newWindRule)) 988 return; 989 990 realizeSaves(); 991 c->canvasClip(m_path, newWindRule); 992 993#if ENABLE(DASHBOARD_SUPPORT) 994 clearPathForDashboardBackwardCompatibilityMode(); 995#endif 996} 997 998bool CanvasRenderingContext2D::isPointInPath(const float x, const float y, const String& windingRuleString) 999{ 1000 GraphicsContext* c = drawingContext(); 1001 if (!c) 1002 return false; 1003 if (!state().m_invertibleCTM) 1004 return false; 1005 1006 FloatPoint point(x, y); 1007 AffineTransform ctm = state().m_transform; 1008 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); 1009 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y())) 1010 return false; 1011 1012 WindRule windRule = RULE_NONZERO; 1013 if (!parseWinding(windingRuleString, windRule)) 1014 return false; 1015 1016 return m_path.contains(transformedPoint, windRule); 1017} 1018 1019 1020bool CanvasRenderingContext2D::isPointInStroke(const float x, const float y) 1021{ 1022 GraphicsContext* c = drawingContext(); 1023 if (!c) 1024 return false; 1025 if (!state().m_invertibleCTM) 1026 return false; 1027 1028 FloatPoint point(x, y); 1029 AffineTransform ctm = state().m_transform; 1030 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); 1031 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y())) 1032 return false; 1033 1034 CanvasStrokeStyleApplier applier(this); 1035 return m_path.strokeContains(&applier, transformedPoint); 1036} 1037 1038void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height) 1039{ 1040 if (!validateRectForCanvas(x, y, width, height)) 1041 return; 1042 GraphicsContext* context = drawingContext(); 1043 if (!context) 1044 return; 1045 if (!state().m_invertibleCTM) 1046 return; 1047 FloatRect rect(x, y, width, height); 1048 1049 bool saved = false; 1050 if (shouldDrawShadows()) { 1051 context->save(); 1052 saved = true; 1053 context->setLegacyShadow(FloatSize(), 0, Color::transparent, ColorSpaceDeviceRGB); 1054 } 1055 if (state().m_globalAlpha != 1) { 1056 if (!saved) { 1057 context->save(); 1058 saved = true; 1059 } 1060 context->setAlpha(1); 1061 } 1062 if (state().m_globalComposite != CompositeSourceOver) { 1063 if (!saved) { 1064 context->save(); 1065 saved = true; 1066 } 1067 context->setCompositeOperation(CompositeSourceOver); 1068 } 1069 context->clearRect(rect); 1070 if (saved) 1071 context->restore(); 1072 didDraw(rect); 1073} 1074 1075void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height) 1076{ 1077 if (!validateRectForCanvas(x, y, width, height)) 1078 return; 1079 1080 GraphicsContext* c = drawingContext(); 1081 if (!c) 1082 return; 1083 if (!state().m_invertibleCTM) 1084 return; 1085 1086 // from the HTML5 Canvas spec: 1087 // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing 1088 // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing 1089 Gradient* gradient = c->fillGradient(); 1090 if (gradient && gradient->isZeroSize()) 1091 return; 1092 1093 FloatRect rect(x, y, width, height); 1094 1095 if (rectContainsCanvas(rect)) { 1096 c->fillRect(rect); 1097 didDrawEntireCanvas(); 1098 } else if (isFullCanvasCompositeMode(state().m_globalComposite)) { 1099 fullCanvasCompositedFill(rect); 1100 didDrawEntireCanvas(); 1101 } else if (state().m_globalComposite == CompositeCopy) { 1102 clearCanvas(); 1103 c->fillRect(rect); 1104 didDrawEntireCanvas(); 1105 } else { 1106 c->fillRect(rect); 1107 didDraw(rect); 1108 } 1109} 1110 1111void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height) 1112{ 1113 if (!validateRectForCanvas(x, y, width, height)) 1114 return; 1115 1116 GraphicsContext* c = drawingContext(); 1117 if (!c) 1118 return; 1119 if (!state().m_invertibleCTM) 1120 return; 1121 if (!(state().m_lineWidth >= 0)) 1122 return; 1123 1124 // If gradient size is zero, then paint nothing. 1125 Gradient* gradient = c->strokeGradient(); 1126 if (gradient && gradient->isZeroSize()) 1127 return; 1128 1129 FloatRect rect(x, y, width, height); 1130 1131 FloatRect boundingRect = rect; 1132 boundingRect.inflate(state().m_lineWidth / 2); 1133 1134 c->strokeRect(rect, state().m_lineWidth); 1135 didDraw(boundingRect); 1136} 1137 1138void CanvasRenderingContext2D::setShadow(float width, float height, float blur) 1139{ 1140 setShadow(FloatSize(width, height), blur, Color::transparent); 1141} 1142 1143void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color) 1144{ 1145 RGBA32 rgba; 1146 if (!parseColorOrCurrentColor(rgba, color, canvas())) 1147 return; 1148 setShadow(FloatSize(width, height), blur, rgba); 1149} 1150 1151void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel) 1152{ 1153 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1)); 1154} 1155 1156void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha) 1157{ 1158 RGBA32 rgba; 1159 if (!parseColorOrCurrentColor(rgba, color, canvas())) 1160 return; 1161 setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(rgba, alpha)); 1162} 1163 1164void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha) 1165{ 1166 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha)); 1167} 1168 1169void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a) 1170{ 1171 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(r, g, b, a)); 1172} 1173 1174void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a) 1175{ 1176 setShadow(FloatSize(width, height), blur, makeRGBAFromCMYKA(c, m, y, k, a)); 1177} 1178 1179void CanvasRenderingContext2D::clearShadow() 1180{ 1181 setShadow(FloatSize(), 0, Color::transparent); 1182} 1183 1184void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, RGBA32 color) 1185{ 1186 if (state().m_shadowOffset == offset && state().m_shadowBlur == blur && state().m_shadowColor == color) 1187 return; 1188 bool wasDrawingShadows = shouldDrawShadows(); 1189 realizeSaves(); 1190 modifiableState().m_shadowOffset = offset; 1191 modifiableState().m_shadowBlur = blur; 1192 modifiableState().m_shadowColor = color; 1193 if (!wasDrawingShadows && !shouldDrawShadows()) 1194 return; 1195 applyShadow(); 1196} 1197 1198void CanvasRenderingContext2D::applyShadow() 1199{ 1200 GraphicsContext* c = drawingContext(); 1201 if (!c) 1202 return; 1203 1204 if (shouldDrawShadows()) { 1205 float width = state().m_shadowOffset.width(); 1206 float height = state().m_shadowOffset.height(); 1207 c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); 1208 } else 1209 c->setLegacyShadow(FloatSize(), 0, Color::transparent, ColorSpaceDeviceRGB); 1210} 1211 1212bool CanvasRenderingContext2D::shouldDrawShadows() const 1213{ 1214 return alphaChannel(state().m_shadowColor) && (state().m_shadowBlur || !state().m_shadowOffset.isZero()); 1215} 1216 1217static LayoutSize size(HTMLImageElement* image) 1218{ 1219 if (CachedImage* cachedImage = image->cachedImage()) 1220 return cachedImage->imageSizeForRenderer(image->renderer(), 1.0f); // FIXME: Not sure about this. 1221 return IntSize(); 1222} 1223 1224#if ENABLE(VIDEO) 1225static IntSize size(HTMLVideoElement* video) 1226{ 1227 if (MediaPlayer* player = video->player()) 1228 return player->naturalSize(); 1229 return IntSize(); 1230} 1231#endif 1232 1233static inline FloatRect normalizeRect(const FloatRect& rect) 1234{ 1235 return FloatRect(min(rect.x(), rect.maxX()), 1236 min(rect.y(), rect.maxY()), 1237 max(rect.width(), -rect.width()), 1238 max(rect.height(), -rect.height())); 1239} 1240 1241void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y, ExceptionCode& ec) 1242{ 1243 if (!image) { 1244 ec = TYPE_MISMATCH_ERR; 1245 return; 1246 } 1247 LayoutSize s = size(image); 1248 drawImage(image, x, y, s.width(), s.height(), ec); 1249} 1250 1251void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, 1252 float x, float y, float width, float height, ExceptionCode& ec) 1253{ 1254 if (!image) { 1255 ec = TYPE_MISMATCH_ERR; 1256 return; 1257 } 1258 LayoutSize s = size(image); 1259 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec); 1260} 1261 1262void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, 1263 float sx, float sy, float sw, float sh, 1264 float dx, float dy, float dw, float dh, ExceptionCode& ec) 1265{ 1266 drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); 1267} 1268 1269void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec) 1270{ 1271 drawImage(image, srcRect, dstRect, state().m_globalComposite, state().m_globalBlend, ec); 1272} 1273 1274void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode, ExceptionCode& ec) 1275{ 1276 if (!image) { 1277 ec = TYPE_MISMATCH_ERR; 1278 return; 1279 } 1280 1281 ec = 0; 1282 1283 if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height()) 1284 || !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height())) 1285 return; 1286 1287 if (!dstRect.width() || !dstRect.height()) 1288 return; 1289 1290 if (!image->complete()) 1291 return; 1292 1293 FloatRect normalizedSrcRect = normalizeRect(srcRect); 1294 FloatRect normalizedDstRect = normalizeRect(dstRect); 1295 1296 FloatRect imageRect = FloatRect(FloatPoint(), size(image)); 1297 if (!srcRect.width() || !srcRect.height()) { 1298 ec = INDEX_SIZE_ERR; 1299 return; 1300 } 1301 if (!imageRect.contains(normalizedSrcRect)) 1302 return; 1303 1304 GraphicsContext* c = drawingContext(); 1305 if (!c) 1306 return; 1307 if (!state().m_invertibleCTM) 1308 return; 1309 1310 CachedImage* cachedImage = image->cachedImage(); 1311 if (!cachedImage) 1312 return; 1313 1314 checkOrigin(image); 1315 1316 if (rectContainsCanvas(normalizedDstRect)) { 1317 c->drawImage(cachedImage->imageForRenderer(image->renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op, blendMode); 1318 didDrawEntireCanvas(); 1319 } else if (isFullCanvasCompositeMode(op)) { 1320 fullCanvasCompositedDrawImage(cachedImage->imageForRenderer(image->renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op); 1321 didDrawEntireCanvas(); 1322 } else if (op == CompositeCopy) { 1323 clearCanvas(); 1324 c->drawImage(cachedImage->imageForRenderer(image->renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op, blendMode); 1325 didDrawEntireCanvas(); 1326 } else { 1327 c->drawImage(cachedImage->imageForRenderer(image->renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op, blendMode); 1328 didDraw(normalizedDstRect); 1329 } 1330} 1331 1332void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, float x, float y, ExceptionCode& ec) 1333{ 1334 drawImage(sourceCanvas, 0, 0, sourceCanvas->width(), sourceCanvas->height(), x, y, sourceCanvas->width(), sourceCanvas->height(), ec); 1335} 1336 1337void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, 1338 float x, float y, float width, float height, ExceptionCode& ec) 1339{ 1340 drawImage(sourceCanvas, FloatRect(0, 0, sourceCanvas->width(), sourceCanvas->height()), FloatRect(x, y, width, height), ec); 1341} 1342 1343void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, 1344 float sx, float sy, float sw, float sh, 1345 float dx, float dy, float dw, float dh, ExceptionCode& ec) 1346{ 1347 drawImage(sourceCanvas, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); 1348} 1349 1350void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const FloatRect& srcRect, 1351 const FloatRect& dstRect, ExceptionCode& ec) 1352{ 1353 if (!sourceCanvas) { 1354 ec = TYPE_MISMATCH_ERR; 1355 return; 1356 } 1357 1358 FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas->size()); 1359 1360 if (!srcCanvasRect.width() || !srcCanvasRect.height()) { 1361 ec = INVALID_STATE_ERR; 1362 return; 1363 } 1364 1365 if (!srcRect.width() || !srcRect.height()) { 1366 ec = INDEX_SIZE_ERR; 1367 return; 1368 } 1369 1370 ec = 0; 1371 1372 if (!srcCanvasRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height()) 1373 return; 1374 1375 GraphicsContext* c = drawingContext(); 1376 if (!c) 1377 return; 1378 if (!state().m_invertibleCTM) 1379 return; 1380 1381 // FIXME: Do this through platform-independent GraphicsContext API. 1382 ImageBuffer* buffer = sourceCanvas->buffer(); 1383 if (!buffer) 1384 return; 1385 1386 checkOrigin(sourceCanvas); 1387 1388#if ENABLE(ACCELERATED_2D_CANVAS) 1389 // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas->makeRenderingResultsAvailable() 1390 // as that will do a readback to software. 1391 CanvasRenderingContext* sourceContext = sourceCanvas->renderingContext(); 1392 // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible. 1393 if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d()) 1394 sourceCanvas->makeRenderingResultsAvailable(); 1395#else 1396 sourceCanvas->makeRenderingResultsAvailable(); 1397#endif 1398 1399 if (rectContainsCanvas(dstRect)) { 1400 c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, dstRect, srcRect, state().m_globalComposite, state().m_globalBlend); 1401 didDrawEntireCanvas(); 1402 } else if (isFullCanvasCompositeMode(state().m_globalComposite)) { 1403 fullCanvasCompositedDrawImage(buffer, ColorSpaceDeviceRGB, dstRect, srcRect, state().m_globalComposite); 1404 didDrawEntireCanvas(); 1405 } else if (state().m_globalComposite == CompositeCopy) { 1406 clearCanvas(); 1407 c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, dstRect, srcRect, state().m_globalComposite, state().m_globalBlend); 1408 didDrawEntireCanvas(); 1409 } else { 1410 c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, dstRect, srcRect, state().m_globalComposite, state().m_globalBlend); 1411 didDraw(dstRect); 1412 } 1413} 1414 1415#if ENABLE(VIDEO) 1416void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, float x, float y, ExceptionCode& ec) 1417{ 1418 if (!video) { 1419 ec = TYPE_MISMATCH_ERR; 1420 return; 1421 } 1422 IntSize s = size(video); 1423 drawImage(video, x, y, s.width(), s.height(), ec); 1424} 1425 1426void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, 1427 float x, float y, float width, float height, ExceptionCode& ec) 1428{ 1429 if (!video) { 1430 ec = TYPE_MISMATCH_ERR; 1431 return; 1432 } 1433 IntSize s = size(video); 1434 drawImage(video, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec); 1435} 1436 1437void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, 1438 float sx, float sy, float sw, float sh, 1439 float dx, float dy, float dw, float dh, ExceptionCode& ec) 1440{ 1441 drawImage(video, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); 1442} 1443 1444void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, const FloatRect& srcRect, const FloatRect& dstRect, 1445 ExceptionCode& ec) 1446{ 1447 if (!video) { 1448 ec = TYPE_MISMATCH_ERR; 1449 return; 1450 } 1451 1452 ec = 0; 1453 1454 if (video->readyState() == HTMLMediaElement::HAVE_NOTHING || video->readyState() == HTMLMediaElement::HAVE_METADATA) 1455 return; 1456 1457 FloatRect videoRect = FloatRect(FloatPoint(), size(video)); 1458 if (!srcRect.width() || !srcRect.height()) { 1459 ec = INDEX_SIZE_ERR; 1460 return; 1461 } 1462 1463 if (!videoRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height()) 1464 return; 1465 1466 GraphicsContext* c = drawingContext(); 1467 if (!c) 1468 return; 1469 if (!state().m_invertibleCTM) 1470 return; 1471 1472 checkOrigin(video); 1473 1474 GraphicsContextStateSaver stateSaver(*c); 1475 c->clip(dstRect); 1476 c->translate(dstRect.x(), dstRect.y()); 1477 c->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height())); 1478 c->translate(-srcRect.x(), -srcRect.y()); 1479 video->paintCurrentFrameInContext(c, IntRect(IntPoint(), size(video))); 1480 stateSaver.restore(); 1481 didDraw(dstRect); 1482} 1483#endif 1484 1485void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image, 1486 float sx, float sy, float sw, float sh, 1487 float dx, float dy, float dw, float dh, 1488 const String& compositeOperation) 1489{ 1490 CompositeOperator op; 1491 BlendMode blendOp = BlendModeNormal; 1492 if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != BlendModeNormal) 1493 op = CompositeSourceOver; 1494 1495 drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), op, BlendModeNormal, IGNORE_EXCEPTION); 1496} 1497 1498void CanvasRenderingContext2D::setAlpha(float alpha) 1499{ 1500 setGlobalAlpha(alpha); 1501} 1502 1503void CanvasRenderingContext2D::setCompositeOperation(const String& operation) 1504{ 1505 setGlobalCompositeOperation(operation); 1506} 1507 1508void CanvasRenderingContext2D::clearCanvas() 1509{ 1510 FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height()); 1511 GraphicsContext* c = drawingContext(); 1512 if (!c) 1513 return; 1514 1515 c->save(); 1516 c->setCTM(canvas()->baseTransform()); 1517 c->clearRect(canvasRect); 1518 c->restore(); 1519} 1520 1521Path CanvasRenderingContext2D::transformAreaToDevice(const Path& path) const 1522{ 1523 Path transformed(path); 1524 transformed.transform(state().m_transform); 1525 transformed.transform(canvas()->baseTransform()); 1526 return transformed; 1527} 1528 1529Path CanvasRenderingContext2D::transformAreaToDevice(const FloatRect& rect) const 1530{ 1531 Path path; 1532 path.addRect(rect); 1533 return transformAreaToDevice(path); 1534} 1535 1536bool CanvasRenderingContext2D::rectContainsCanvas(const FloatRect& rect) const 1537{ 1538 FloatQuad quad(rect); 1539 FloatQuad canvasQuad(FloatRect(0, 0, canvas()->width(), canvas()->height())); 1540 return state().m_transform.mapQuad(quad).containsQuad(canvasQuad); 1541} 1542 1543template<class T> IntRect CanvasRenderingContext2D::calculateCompositingBufferRect(const T& area, IntSize* croppedOffset) 1544{ 1545 IntRect canvasRect(0, 0, canvas()->width(), canvas()->height()); 1546 canvasRect = canvas()->baseTransform().mapRect(canvasRect); 1547 Path path = transformAreaToDevice(area); 1548 IntRect bufferRect = enclosingIntRect(path.fastBoundingRect()); 1549 IntPoint originalLocation = bufferRect.location(); 1550 bufferRect.intersect(canvasRect); 1551 if (croppedOffset) 1552 *croppedOffset = originalLocation - bufferRect.location(); 1553 return bufferRect; 1554} 1555 1556PassOwnPtr<ImageBuffer> CanvasRenderingContext2D::createCompositingBuffer(const IntRect& bufferRect) 1557{ 1558 RenderingMode renderMode = isAccelerated() ? Accelerated : Unaccelerated; 1559 return ImageBuffer::create(bufferRect.size(), 1, ColorSpaceDeviceRGB, renderMode); 1560} 1561 1562void CanvasRenderingContext2D::compositeBuffer(ImageBuffer* buffer, const IntRect& bufferRect, CompositeOperator op) 1563{ 1564 IntRect canvasRect(0, 0, canvas()->width(), canvas()->height()); 1565 canvasRect = canvas()->baseTransform().mapRect(canvasRect); 1566 1567 GraphicsContext* c = drawingContext(); 1568 if (!c) 1569 return; 1570 1571 c->save(); 1572 c->setCTM(AffineTransform()); 1573 c->setCompositeOperation(op); 1574 1575 c->save(); 1576 c->clipOut(bufferRect); 1577 c->clearRect(canvasRect); 1578 c->restore(); 1579 1580 c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, bufferRect.location(), state().m_globalComposite); 1581 c->restore(); 1582} 1583 1584static void drawImageToContext(Image* image, GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op) 1585{ 1586 context->drawImage(image, styleColorSpace, dest, src, op); 1587} 1588 1589static void drawImageToContext(ImageBuffer* imageBuffer, GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op) 1590{ 1591 context->drawImageBuffer(imageBuffer, styleColorSpace, dest, src, op); 1592} 1593 1594template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op) 1595{ 1596 ASSERT(isFullCanvasCompositeMode(op)); 1597 1598 IntSize croppedOffset; 1599 IntRect bufferRect = calculateCompositingBufferRect(dest, &croppedOffset); 1600 if (bufferRect.isEmpty()) { 1601 clearCanvas(); 1602 return; 1603 } 1604 1605 OwnPtr<ImageBuffer> buffer = createCompositingBuffer(bufferRect); 1606 if (!buffer) 1607 return; 1608 1609 GraphicsContext* c = drawingContext(); 1610 if (!c) 1611 return; 1612 1613 FloatRect adjustedDest = dest; 1614 adjustedDest.setLocation(FloatPoint(0, 0)); 1615 AffineTransform effectiveTransform = c->getCTM(); 1616 IntRect transformedAdjustedRect = enclosingIntRect(effectiveTransform.mapRect(adjustedDest)); 1617 buffer->context()->translate(-transformedAdjustedRect.location().x(), -transformedAdjustedRect.location().y()); 1618 buffer->context()->translate(croppedOffset.width(), croppedOffset.height()); 1619 buffer->context()->concatCTM(effectiveTransform); 1620 drawImageToContext(image, buffer->context(), styleColorSpace, adjustedDest, src, CompositeSourceOver); 1621 1622 compositeBuffer(buffer.get(), bufferRect, op); 1623} 1624 1625template<class T> void CanvasRenderingContext2D::fullCanvasCompositedFill(const T& area) 1626{ 1627 ASSERT(isFullCanvasCompositeMode(state().m_globalComposite)); 1628 1629 IntRect bufferRect = calculateCompositingBufferRect(area, 0); 1630 if (bufferRect.isEmpty()) { 1631 clearCanvas(); 1632 return; 1633 } 1634 1635 OwnPtr<ImageBuffer> buffer = createCompositingBuffer(bufferRect); 1636 if (!buffer) 1637 return; 1638 1639 Path path = transformAreaToDevice(area); 1640 path.translate(FloatSize(-bufferRect.x(), -bufferRect.y())); 1641 1642 buffer->context()->setCompositeOperation(CompositeSourceOver); 1643 modifiableState().m_fillStyle.applyFillColor(buffer->context()); 1644 buffer->context()->fillPath(path); 1645 1646 compositeBuffer(buffer.get(), bufferRect, state().m_globalComposite); 1647} 1648 1649void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient* gradient) const 1650{ 1651#if ENABLE(DASHBOARD_SUPPORT) 1652 if (m_usesDashboardCompatibilityMode) 1653 gradient->setDashboardCompatibilityMode(); 1654#else 1655 UNUSED_PARAM(gradient); 1656#endif 1657} 1658 1659PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec) 1660{ 1661 if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(x1) || !std::isfinite(y1)) { 1662 ec = NOT_SUPPORTED_ERR; 1663 return 0; 1664 } 1665 1666 RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1)); 1667 prepareGradientForDashboard(gradient.get()); 1668 return gradient.release(); 1669} 1670 1671PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec) 1672{ 1673 if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(r0) || !std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(r1)) { 1674 ec = NOT_SUPPORTED_ERR; 1675 return 0; 1676 } 1677 1678 if (r0 < 0 || r1 < 0) { 1679 ec = INDEX_SIZE_ERR; 1680 return 0; 1681 } 1682 1683 RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1); 1684 prepareGradientForDashboard(gradient.get()); 1685 return gradient.release(); 1686} 1687 1688PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image, 1689 const String& repetitionType, ExceptionCode& ec) 1690{ 1691 if (!image) { 1692 ec = TYPE_MISMATCH_ERR; 1693 return 0; 1694 } 1695 bool repeatX, repeatY; 1696 ec = 0; 1697 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); 1698 if (ec) 1699 return 0; 1700 1701 if (!image->complete()) 1702 return 0; 1703 1704 CachedImage* cachedImage = image->cachedImage(); 1705 if (!cachedImage || !image->cachedImage()->imageForRenderer(image->renderer())) 1706 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true); 1707 1708 bool originClean = isOriginClean(cachedImage, canvas()->securityOrigin()); 1709 return CanvasPattern::create(cachedImage->imageForRenderer(image->renderer()), repeatX, repeatY, originClean); 1710} 1711 1712PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas, 1713 const String& repetitionType, ExceptionCode& ec) 1714{ 1715 if (!canvas) { 1716 ec = TYPE_MISMATCH_ERR; 1717 return 0; 1718 } 1719 if (!canvas->width() || !canvas->height()) { 1720 ec = INVALID_STATE_ERR; 1721 return 0; 1722 } 1723 1724 bool repeatX, repeatY; 1725 ec = 0; 1726 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); 1727 if (ec) 1728 return 0; 1729 return CanvasPattern::create(canvas->copiedImage(), repeatX, repeatY, canvas->originClean()); 1730} 1731 1732void CanvasRenderingContext2D::didDrawEntireCanvas() 1733{ 1734 didDraw(FloatRect(FloatPoint::zero(), canvas()->size()), CanvasDidDrawApplyClip); 1735} 1736 1737void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options) 1738{ 1739 GraphicsContext* c = drawingContext(); 1740 if (!c) 1741 return; 1742 if (!state().m_invertibleCTM) 1743 return; 1744 1745#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) 1746 // If we are drawing to hardware and we have a composited layer, just call contentChanged(). 1747 if (isAccelerated()) { 1748 RenderBox* renderBox = canvas()->renderBox(); 1749 if (renderBox && renderBox->hasAcceleratedCompositing()) { 1750 renderBox->contentChanged(CanvasPixelsChanged); 1751 canvas()->clearCopiedImage(); 1752 canvas()->notifyObserversCanvasChanged(r); 1753 return; 1754 } 1755 } 1756#endif 1757 1758 FloatRect dirtyRect = r; 1759 if (options & CanvasDidDrawApplyTransform) { 1760 AffineTransform ctm = state().m_transform; 1761 dirtyRect = ctm.mapRect(r); 1762 } 1763 1764 if (options & CanvasDidDrawApplyShadow && alphaChannel(state().m_shadowColor)) { 1765 // The shadow gets applied after transformation 1766 FloatRect shadowRect(dirtyRect); 1767 shadowRect.move(state().m_shadowOffset); 1768 shadowRect.inflate(state().m_shadowBlur); 1769 dirtyRect.unite(shadowRect); 1770 } 1771 1772 if (options & CanvasDidDrawApplyClip) { 1773 // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip 1774 // back out of the GraphicsContext, so to take clip into account for incremental painting, 1775 // we'd have to keep the clip path around. 1776 } 1777 1778 canvas()->didDraw(dirtyRect); 1779} 1780 1781GraphicsContext* CanvasRenderingContext2D::drawingContext() const 1782{ 1783 return canvas()->drawingContext(); 1784} 1785 1786static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size) 1787{ 1788 Checked<int, RecordOverflow> dataSize = 4; 1789 dataSize *= size.width(); 1790 dataSize *= size.height(); 1791 if (dataSize.hasOverflowed()) 1792 return 0; 1793 1794 RefPtr<ImageData> data = ImageData::create(size); 1795 data->data()->zeroFill(); 1796 return data.release(); 1797} 1798 1799PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtr<ImageData> imageData, ExceptionCode& ec) const 1800{ 1801 if (!imageData) { 1802 ec = NOT_SUPPORTED_ERR; 1803 return 0; 1804 } 1805 1806 return createEmptyImageData(imageData->size()); 1807} 1808 1809PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionCode& ec) const 1810{ 1811 ec = 0; 1812 if (!sw || !sh) { 1813 ec = INDEX_SIZE_ERR; 1814 return 0; 1815 } 1816 if (!std::isfinite(sw) || !std::isfinite(sh)) { 1817 ec = NOT_SUPPORTED_ERR; 1818 return 0; 1819 } 1820 1821 FloatSize logicalSize(fabs(sw), fabs(sh)); 1822 if (!logicalSize.isExpressibleAsIntSize()) 1823 return 0; 1824 1825 IntSize size = expandedIntSize(logicalSize); 1826 if (size.width() < 1) 1827 size.setWidth(1); 1828 if (size.height() < 1) 1829 size.setHeight(1); 1830 1831 return createEmptyImageData(size); 1832} 1833 1834PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const 1835{ 1836 return getImageData(ImageBuffer::LogicalCoordinateSystem, sx, sy, sw, sh, ec); 1837} 1838 1839PassRefPtr<ImageData> CanvasRenderingContext2D::webkitGetImageDataHD(float sx, float sy, float sw, float sh, ExceptionCode& ec) const 1840{ 1841 return getImageData(ImageBuffer::BackingStoreCoordinateSystem, sx, sy, sw, sh, ec); 1842} 1843 1844PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(ImageBuffer::CoordinateSystem coordinateSystem, float sx, float sy, float sw, float sh, ExceptionCode& ec) const 1845{ 1846 if (!canvas()->originClean()) { 1847 DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Unable to get image data from canvas because the canvas has been tainted by cross-origin data."))); 1848 canvas()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, consoleMessage); 1849 ec = SECURITY_ERR; 1850 return 0; 1851 } 1852 1853 if (!sw || !sh) { 1854 ec = INDEX_SIZE_ERR; 1855 return 0; 1856 } 1857 if (!std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !std::isfinite(sh)) { 1858 ec = NOT_SUPPORTED_ERR; 1859 return 0; 1860 } 1861 1862 if (sw < 0) { 1863 sx += sw; 1864 sw = -sw; 1865 } 1866 if (sh < 0) { 1867 sy += sh; 1868 sh = -sh; 1869 } 1870 1871 FloatRect logicalRect(sx, sy, sw, sh); 1872 if (logicalRect.width() < 1) 1873 logicalRect.setWidth(1); 1874 if (logicalRect.height() < 1) 1875 logicalRect.setHeight(1); 1876 if (!logicalRect.isExpressibleAsIntRect()) 1877 return 0; 1878 1879 IntRect imageDataRect = enclosingIntRect(logicalRect); 1880 ImageBuffer* buffer = canvas()->buffer(); 1881 if (!buffer) 1882 return createEmptyImageData(imageDataRect.size()); 1883 1884 RefPtr<Uint8ClampedArray> byteArray = buffer->getUnmultipliedImageData(imageDataRect, coordinateSystem); 1885 if (!byteArray) 1886 return 0; 1887 1888 return ImageData::create(imageDataRect.size(), byteArray.release()); 1889} 1890 1891void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec) 1892{ 1893 if (!data) { 1894 ec = TYPE_MISMATCH_ERR; 1895 return; 1896 } 1897 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec); 1898} 1899 1900void CanvasRenderingContext2D::webkitPutImageDataHD(ImageData* data, float dx, float dy, ExceptionCode& ec) 1901{ 1902 if (!data) { 1903 ec = TYPE_MISMATCH_ERR; 1904 return; 1905 } 1906 webkitPutImageDataHD(data, dx, dy, 0, 0, data->width(), data->height(), ec); 1907} 1908 1909void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, 1910 float dirtyWidth, float dirtyHeight, ExceptionCode& ec) 1911{ 1912 putImageData(data, ImageBuffer::LogicalCoordinateSystem, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight, ec); 1913} 1914 1915void CanvasRenderingContext2D::webkitPutImageDataHD(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight, ExceptionCode& ec) 1916{ 1917 putImageData(data, ImageBuffer::BackingStoreCoordinateSystem, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight, ec); 1918} 1919 1920void CanvasRenderingContext2D::putImageData(ImageData* data, ImageBuffer::CoordinateSystem coordinateSystem, float dx, float dy, float dirtyX, float dirtyY, 1921 float dirtyWidth, float dirtyHeight, ExceptionCode& ec) 1922{ 1923 if (!data) { 1924 ec = TYPE_MISMATCH_ERR; 1925 return; 1926 } 1927 if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dirtyX) || !std::isfinite(dirtyY) || !std::isfinite(dirtyWidth) || !std::isfinite(dirtyHeight)) { 1928 ec = NOT_SUPPORTED_ERR; 1929 return; 1930 } 1931 1932 ImageBuffer* buffer = canvas()->buffer(); 1933 if (!buffer) 1934 return; 1935 1936 if (dirtyWidth < 0) { 1937 dirtyX += dirtyWidth; 1938 dirtyWidth = -dirtyWidth; 1939 } 1940 1941 if (dirtyHeight < 0) { 1942 dirtyY += dirtyHeight; 1943 dirtyHeight = -dirtyHeight; 1944 } 1945 1946 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); 1947 clipRect.intersect(IntRect(0, 0, data->width(), data->height())); 1948 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy)); 1949 IntRect destRect = enclosingIntRect(clipRect); 1950 destRect.move(destOffset); 1951 destRect.intersect(IntRect(IntPoint(), coordinateSystem == ImageBuffer::LogicalCoordinateSystem ? buffer->logicalSize() : buffer->internalSize())); 1952 if (destRect.isEmpty()) 1953 return; 1954 IntRect sourceRect(destRect); 1955 sourceRect.move(-destOffset); 1956 1957 buffer->putByteArray(Unmultiplied, data->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset), coordinateSystem); 1958 1959 if (coordinateSystem == ImageBuffer::BackingStoreCoordinateSystem) { 1960 FloatRect dirtyRect = destRect; 1961 dirtyRect.scale(1 / canvas()->deviceScaleFactor()); 1962 destRect = enclosingIntRect(dirtyRect); 1963 } 1964 didDraw(destRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip 1965} 1966 1967String CanvasRenderingContext2D::font() const 1968{ 1969 if (!state().m_realizedFont) 1970 return defaultFont; 1971 1972 StringBuilder serializedFont; 1973 const FontDescription& fontDescription = state().m_font.fontDescription(); 1974 1975 if (fontDescription.italic()) 1976 serializedFont.appendLiteral("italic "); 1977 if (fontDescription.smallCaps() == FontSmallCapsOn) 1978 serializedFont.appendLiteral("small-caps "); 1979 1980 serializedFont.appendNumber(fontDescription.computedPixelSize()); 1981 serializedFont.appendLiteral("px"); 1982 1983 for (unsigned i = 0; i < state().m_font.familyCount(); ++i) { 1984 if (i) 1985 serializedFont.append(','); 1986 // FIXME: We should append family directly to serializedFont rather than building a temporary string. 1987 String family = state().m_font.familyAt(i); 1988 if (family.startsWith("-webkit-")) 1989 family = family.substring(8); 1990 if (family.contains(' ')) 1991 family = makeString('"', family, '"'); 1992 1993 serializedFont.append(' '); 1994 serializedFont.append(family); 1995 } 1996 1997 return serializedFont.toString(); 1998} 1999 2000void CanvasRenderingContext2D::setFont(const String& newFont) 2001{ 2002 if (newFont == state().m_unparsedFont && state().m_realizedFont) 2003 return; 2004 2005 RefPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create(); 2006 CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, newFont, true, strictToCSSParserMode(!m_usesCSSCompatibilityParseMode), 0); 2007 if (parsedStyle->isEmpty()) 2008 return; 2009 2010 String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont); 2011 2012 // According to http://lists.w3.org/Archives/Public/public-html/2009Jul/0947.html, 2013 // the "inherit" and "initial" values must be ignored. 2014 if (fontValue == "inherit" || fontValue == "initial") 2015 return; 2016 2017 // The parse succeeded. 2018 String newFontSafeCopy(newFont); // Create a string copy since newFont can be deleted inside realizeSaves. 2019 realizeSaves(); 2020 modifiableState().m_unparsedFont = newFontSafeCopy; 2021 2022 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work 2023 // relative to the canvas. 2024 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 2025 if (RenderStyle* computedStyle = canvas()->computedStyle()) 2026 newStyle->setFontDescription(computedStyle->fontDescription()); 2027 else { 2028 FontDescription defaultFontDescription; 2029 defaultFontDescription.setOneFamily(defaultFontFamily); 2030 defaultFontDescription.setSpecifiedSize(defaultFontSize); 2031 defaultFontDescription.setComputedSize(defaultFontSize); 2032 2033 newStyle->setFontDescription(defaultFontDescription); 2034 } 2035 2036 newStyle->font().update(newStyle->font().fontSelector()); 2037 2038 // Now map the font property longhands into the style. 2039 StyleResolver* styleResolver = canvas()->document()->ensureStyleResolver(); 2040 styleResolver->applyPropertyToStyle(CSSPropertyFontFamily, parsedStyle->getPropertyCSSValue(CSSPropertyFontFamily).get(), newStyle.get()); 2041 styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontStyle, parsedStyle->getPropertyCSSValue(CSSPropertyFontStyle).get()); 2042 styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontVariant, parsedStyle->getPropertyCSSValue(CSSPropertyFontVariant).get()); 2043 styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontWeight, parsedStyle->getPropertyCSSValue(CSSPropertyFontWeight).get()); 2044 2045 // As described in BUG66291, setting font-size and line-height on a font may entail a CSSPrimitiveValue::computeLengthDouble call, 2046 // which assumes the fontMetrics are available for the affected font, otherwise a crash occurs (see http://trac.webkit.org/changeset/96122). 2047 // The updateFont() calls below update the fontMetrics and ensure the proper setting of font-size and line-height. 2048 styleResolver->updateFont(); 2049 styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontSize, parsedStyle->getPropertyCSSValue(CSSPropertyFontSize).get()); 2050 styleResolver->updateFont(); 2051 styleResolver->applyPropertyToCurrentStyle(CSSPropertyLineHeight, parsedStyle->getPropertyCSSValue(CSSPropertyLineHeight).get()); 2052 2053 modifiableState().m_font = newStyle->font(); 2054 modifiableState().m_font.update(styleResolver->fontSelector()); 2055 modifiableState().m_realizedFont = true; 2056 styleResolver->fontSelector()->registerForInvalidationCallbacks(&modifiableState()); 2057} 2058 2059String CanvasRenderingContext2D::textAlign() const 2060{ 2061 return textAlignName(state().m_textAlign); 2062} 2063 2064void CanvasRenderingContext2D::setTextAlign(const String& s) 2065{ 2066 TextAlign align; 2067 if (!parseTextAlign(s, align)) 2068 return; 2069 if (state().m_textAlign == align) 2070 return; 2071 realizeSaves(); 2072 modifiableState().m_textAlign = align; 2073} 2074 2075String CanvasRenderingContext2D::textBaseline() const 2076{ 2077 return textBaselineName(state().m_textBaseline); 2078} 2079 2080void CanvasRenderingContext2D::setTextBaseline(const String& s) 2081{ 2082 TextBaseline baseline; 2083 if (!parseTextBaseline(s, baseline)) 2084 return; 2085 if (state().m_textBaseline == baseline) 2086 return; 2087 realizeSaves(); 2088 modifiableState().m_textBaseline = baseline; 2089} 2090 2091void CanvasRenderingContext2D::fillText(const String& text, float x, float y) 2092{ 2093 drawTextInternal(text, x, y, true); 2094} 2095 2096void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth) 2097{ 2098 drawTextInternal(text, x, y, true, maxWidth, true); 2099} 2100 2101void CanvasRenderingContext2D::strokeText(const String& text, float x, float y) 2102{ 2103 drawTextInternal(text, x, y, false); 2104} 2105 2106void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth) 2107{ 2108 drawTextInternal(text, x, y, false, maxWidth, true); 2109} 2110 2111PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text) 2112{ 2113 FontCachePurgePreventer fontCachePurgePreventer; 2114 2115 RefPtr<TextMetrics> metrics = TextMetrics::create(); 2116 2117#if PLATFORM(QT) 2118 // We always use complex text shaping since it can't be turned off for QPainterPath::addText(). 2119 Font::CodePath oldCodePath = Font::codePath(); 2120 Font::setCodePath(Font::Complex); 2121#endif 2122 2123 metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length()))); 2124 2125#if PLATFORM(QT) 2126 Font::setCodePath(oldCodePath); 2127#endif 2128 2129 return metrics.release(); 2130} 2131 2132static void replaceCharacterInString(String& text, WTF::CharacterMatchFunctionPtr matchFunction, const String& replacement) 2133{ 2134 const size_t replacementLength = replacement.length(); 2135 size_t index = 0; 2136 while ((index = text.find(matchFunction, index)) != notFound) { 2137 text.replace(index, 1, replacement); 2138 index += replacementLength; 2139 } 2140} 2141 2142void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth, bool useMaxWidth) 2143{ 2144 GraphicsContext* c = drawingContext(); 2145 if (!c) 2146 return; 2147 if (!state().m_invertibleCTM) 2148 return; 2149 if (!std::isfinite(x) | !std::isfinite(y)) 2150 return; 2151 if (useMaxWidth && (!std::isfinite(maxWidth) || maxWidth <= 0)) 2152 return; 2153 2154 // If gradient size is zero, then paint nothing. 2155 Gradient* gradient = c->strokeGradient(); 2156 if (!fill && gradient && gradient->isZeroSize()) 2157 return; 2158 2159 gradient = c->fillGradient(); 2160 if (fill && gradient && gradient->isZeroSize()) 2161 return; 2162 2163 FontCachePurgePreventer fontCachePurgePreventer; 2164 2165 const Font& font = accessFont(); 2166 const FontMetrics& fontMetrics = font.fontMetrics(); 2167 // According to spec, all the space characters must be replaced with U+0020 SPACE characters. 2168 String normalizedText = text; 2169 replaceCharacterInString(normalizedText, isSpaceOrNewline, " "); 2170 2171 // FIXME: Need to turn off font smoothing. 2172 2173 RenderStyle* computedStyle = canvas()->computedStyle(); 2174 TextDirection direction = computedStyle ? computedStyle->direction() : LTR; 2175 bool isRTL = direction == RTL; 2176 bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : false; 2177 2178 TextRun textRun(normalizedText, 0, 0, TextRun::AllowTrailingExpansion, direction, override, true, TextRun::NoRounding); 2179 // Draw the item text at the correct point. 2180 FloatPoint location(x, y); 2181 switch (state().m_textBaseline) { 2182 case TopTextBaseline: 2183 case HangingTextBaseline: 2184 location.setY(y + fontMetrics.ascent()); 2185 break; 2186 case BottomTextBaseline: 2187 case IdeographicTextBaseline: 2188 location.setY(y - fontMetrics.descent()); 2189 break; 2190 case MiddleTextBaseline: 2191 location.setY(y - fontMetrics.descent() + fontMetrics.height() / 2); 2192 break; 2193 case AlphabeticTextBaseline: 2194 default: 2195 // Do nothing. 2196 break; 2197 } 2198 2199 float fontWidth = font.width(TextRun(normalizedText, 0, 0, TextRun::AllowTrailingExpansion, direction, override)); 2200 2201 useMaxWidth = (useMaxWidth && maxWidth < fontWidth); 2202 float width = useMaxWidth ? maxWidth : fontWidth; 2203 2204 TextAlign align = state().m_textAlign; 2205 if (align == StartTextAlign) 2206 align = isRTL ? RightTextAlign : LeftTextAlign; 2207 else if (align == EndTextAlign) 2208 align = isRTL ? LeftTextAlign : RightTextAlign; 2209 2210 switch (align) { 2211 case CenterTextAlign: 2212 location.setX(location.x() - width / 2); 2213 break; 2214 case RightTextAlign: 2215 location.setX(location.x() - width); 2216 break; 2217 default: 2218 break; 2219 } 2220 2221 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text. 2222 FloatRect textRect = FloatRect(location.x() - fontMetrics.height() / 2, location.y() - fontMetrics.ascent() - fontMetrics.lineGap(), 2223 width + fontMetrics.height(), fontMetrics.lineSpacing()); 2224 if (!fill) 2225 inflateStrokeRect(textRect); 2226 2227#if USE(CG) 2228 const CanvasStyle& drawStyle = fill ? state().m_fillStyle : state().m_strokeStyle; 2229 if (drawStyle.canvasGradient() || drawStyle.canvasPattern()) { 2230 IntRect maskRect = enclosingIntRect(textRect); 2231 2232 OwnPtr<ImageBuffer> maskImage = c->createCompatibleBuffer(maskRect.size()); 2233 2234 GraphicsContext* maskImageContext = maskImage->context(); 2235 2236 if (fill) 2237 maskImageContext->setFillColor(Color::black, ColorSpaceDeviceRGB); 2238 else { 2239 maskImageContext->setStrokeColor(Color::black, ColorSpaceDeviceRGB); 2240 maskImageContext->setStrokeThickness(c->strokeThickness()); 2241 } 2242 2243 maskImageContext->setTextDrawingMode(fill ? TextModeFill : TextModeStroke); 2244 2245 if (useMaxWidth) { 2246 maskImageContext->translate(location.x() - maskRect.x(), location.y() - maskRect.y()); 2247 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work. 2248 maskImageContext->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1)); 2249 maskImageContext->drawBidiText(font, textRun, FloatPoint(0, 0), Font::UseFallbackIfFontNotReady); 2250 } else { 2251 maskImageContext->translate(-maskRect.x(), -maskRect.y()); 2252 maskImageContext->drawBidiText(font, textRun, location, Font::UseFallbackIfFontNotReady); 2253 } 2254 2255 GraphicsContextStateSaver stateSaver(*c); 2256 c->clipToImageBuffer(maskImage.get(), maskRect); 2257 drawStyle.applyFillColor(c); 2258 c->fillRect(maskRect); 2259 return; 2260 } 2261#endif 2262 2263 c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke); 2264 2265#if PLATFORM(QT) 2266 // We always use complex text shaping since it can't be turned off for QPainterPath::addText(). 2267 Font::CodePath oldCodePath = Font::codePath(); 2268 Font::setCodePath(Font::Complex); 2269#endif 2270 2271 if (useMaxWidth) { 2272 GraphicsContextStateSaver stateSaver(*c); 2273 c->translate(location.x(), location.y()); 2274 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work. 2275 c->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1)); 2276 c->drawBidiText(font, textRun, FloatPoint(0, 0), Font::UseFallbackIfFontNotReady); 2277 } else 2278 c->drawBidiText(font, textRun, location, Font::UseFallbackIfFontNotReady); 2279 2280 didDraw(textRect); 2281 2282#if PLATFORM(QT) 2283 Font::setCodePath(oldCodePath); 2284#endif 2285} 2286 2287void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const 2288{ 2289 // Fast approximation of the stroke's bounding rect. 2290 // This yields a slightly oversized rect but is very fast 2291 // compared to Path::strokeBoundingRect(). 2292 static const float root2 = sqrtf(2); 2293 float delta = state().m_lineWidth / 2; 2294 if (state().m_lineJoin == MiterJoin) 2295 delta *= state().m_miterLimit; 2296 else if (state().m_lineCap == SquareCap) 2297 delta *= root2; 2298 2299 rect.inflate(delta); 2300} 2301 2302const Font& CanvasRenderingContext2D::accessFont() 2303{ 2304 canvas()->document()->updateStyleIfNeeded(); 2305 2306 if (!state().m_realizedFont) 2307 setFont(state().m_unparsedFont); 2308 return state().m_font; 2309} 2310 2311#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) 2312PlatformLayer* CanvasRenderingContext2D::platformLayer() const 2313{ 2314 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0; 2315} 2316#endif 2317 2318bool CanvasRenderingContext2D::webkitImageSmoothingEnabled() const 2319{ 2320 return state().m_imageSmoothingEnabled; 2321} 2322 2323void CanvasRenderingContext2D::setWebkitImageSmoothingEnabled(bool enabled) 2324{ 2325 if (enabled == state().m_imageSmoothingEnabled) 2326 return; 2327 2328 realizeSaves(); 2329 modifiableState().m_imageSmoothingEnabled = enabled; 2330 GraphicsContext* c = drawingContext(); 2331 if (c) 2332 c->setImageInterpolationQuality(enabled ? DefaultInterpolationQuality : InterpolationNone); 2333} 2334 2335} // namespace WebCore 2336