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