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