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