1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 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 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 26 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "CanvasRenderingContext2D.h" 33 34 #include "AffineTransform.h" 35 #include "CSSMutableStyleDeclaration.h" 36 #include "CSSParser.h" 37 #include "CSSPropertyNames.h" 38 #include "CSSStyleSelector.h" 39 #include "CachedImage.h" 40 #include "CanvasGradient.h" 41 #include "CanvasPattern.h" 42 #include "CanvasStyle.h" 43 #include "ExceptionCode.h" 44 #include "FloatConversion.h" 45 #include "GraphicsContext.h" 46 #include "HTMLCanvasElement.h" 47 #include "HTMLImageElement.h" 48 #include "HTMLMediaElement.h" 49 #include "HTMLNames.h" 50 #include "HTMLVideoElement.h" 51 #include "ImageBuffer.h" 52 #include "ImageData.h" 53 #include "KURL.h" 54 #include "Page.h" 55 #include "RenderHTMLCanvas.h" 56 #include "SecurityOrigin.h" 57 #include "Settings.h" 58 #include "StrokeStyleApplier.h" 59 #include "TextMetrics.h" 60 #include "TextRun.h" 61 62 #if ENABLE(ACCELERATED_2D_CANVAS) 63 #include "Chrome.h" 64 #include "ChromeClient.h" 65 #include "DrawingBuffer.h" 66 #include "FrameView.h" 67 #include "GraphicsContext3D.h" 68 #include "SharedGraphicsContext3D.h" 69 #if USE(ACCELERATED_COMPOSITING) 70 #include "RenderLayer.h" 71 #endif 72 #endif 73 74 #include <wtf/ByteArray.h> 75 #include <wtf/MathExtras.h> 76 #include <wtf/OwnPtr.h> 77 #include <wtf/UnusedParam.h> 78 79 using namespace std; 80 81 namespace WebCore { 82 83 using namespace HTMLNames; 84 85 static const char* const defaultFont = "10px sans-serif"; 86 87 88 class CanvasStrokeStyleApplier : public StrokeStyleApplier { 89 public: 90 CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext) 91 : m_canvasContext(canvasContext) 92 { 93 } 94 95 virtual void strokeStyle(GraphicsContext* c) 96 { 97 c->setStrokeThickness(m_canvasContext->lineWidth()); 98 c->setLineCap(m_canvasContext->getLineCap()); 99 c->setLineJoin(m_canvasContext->getLineJoin()); 100 c->setMiterLimit(m_canvasContext->miterLimit()); 101 } 102 103 private: 104 CanvasRenderingContext2D* m_canvasContext; 105 }; 106 107 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode) 108 : CanvasRenderingContext(canvas) 109 , m_stateStack(1) 110 , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode) 111 #if ENABLE(DASHBOARD_SUPPORT) 112 , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode) 113 #endif 114 #if ENABLE(ACCELERATED_2D_CANVAS) 115 , m_context3D(0) 116 #endif 117 { 118 #if !ENABLE(DASHBOARD_SUPPORT) 119 ASSERT_UNUSED(usesDashboardCompatibilityMode, !usesDashboardCompatibilityMode); 120 #endif 121 122 // Make sure that even if the drawingContext() has a different default 123 // thickness, it is in sync with the canvas thickness. 124 setLineWidth(lineWidth()); 125 126 #if ENABLE(ACCELERATED_2D_CANVAS) 127 Page* p = canvas->document()->page(); 128 if (!p) 129 return; 130 if (!p->settings()->accelerated2dCanvasEnabled()) 131 return; 132 if (GraphicsContext* c = drawingContext()) { 133 m_context3D = p->sharedGraphicsContext3D(); 134 if (m_context3D) { 135 m_drawingBuffer = m_context3D->graphicsContext3D()->createDrawingBuffer(IntSize(canvas->width(), canvas->height())); 136 if (!m_drawingBuffer) { 137 c->setSharedGraphicsContext3D(0, 0, IntSize()); 138 m_context3D.clear(); 139 } else 140 c->setSharedGraphicsContext3D(m_context3D.get(), m_drawingBuffer.get(), IntSize(canvas->width(), canvas->height())); 141 } 142 } 143 #endif 144 } 145 146 CanvasRenderingContext2D::~CanvasRenderingContext2D() 147 { 148 } 149 150 bool CanvasRenderingContext2D::isAccelerated() const 151 { 152 #if USE(IOSURFACE_CANVAS_BACKING_STORE) 153 ImageBuffer* buffer = canvas()->buffer(); 154 return buffer ? buffer->isAccelerated() : false; 155 #elif ENABLE(ACCELERATED_2D_CANVAS) 156 return m_context3D; 157 #else 158 return false; 159 #endif 160 } 161 162 bool CanvasRenderingContext2D::paintsIntoCanvasBuffer() const 163 { 164 #if ENABLE(ACCELERATED_2D_CANVAS) 165 if (m_context3D) 166 return m_context3D->paintsIntoCanvasBuffer(); 167 #endif 168 return true; 169 } 170 171 172 void CanvasRenderingContext2D::reset() 173 { 174 m_stateStack.resize(1); 175 m_stateStack.first() = State(); 176 m_path.clear(); 177 #if ENABLE(ACCELERATED_2D_CANVAS) 178 if (GraphicsContext* c = drawingContext()) { 179 if (m_context3D && m_drawingBuffer) { 180 if (m_drawingBuffer->reset(IntSize(canvas()->width(), canvas()->height()))) { 181 c->setSharedGraphicsContext3D(m_context3D.get(), m_drawingBuffer.get(), IntSize(canvas()->width(), canvas()->height())); 182 #if USE(ACCELERATED_COMPOSITING) 183 RenderBox* renderBox = canvas()->renderBox(); 184 if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) 185 renderBox->layer()->contentChanged(RenderLayer::CanvasChanged); 186 #endif 187 } else { 188 c->setSharedGraphicsContext3D(0, 0, IntSize()); 189 m_drawingBuffer.clear(); 190 m_context3D.clear(); 191 } 192 } 193 } 194 #endif 195 } 196 197 CanvasRenderingContext2D::State::State() 198 : m_strokeStyle(CanvasStyle::createFromRGBA(Color::black)) 199 , m_fillStyle(CanvasStyle::createFromRGBA(Color::black)) 200 , m_lineWidth(1) 201 , m_lineCap(ButtCap) 202 , m_lineJoin(MiterJoin) 203 , m_miterLimit(10) 204 , m_shadowBlur(0) 205 , m_shadowColor(Color::transparent) 206 , m_globalAlpha(1) 207 , m_globalComposite(CompositeSourceOver) 208 , m_invertibleCTM(true) 209 , m_textAlign(StartTextAlign) 210 , m_textBaseline(AlphabeticTextBaseline) 211 , m_unparsedFont(defaultFont) 212 , m_realizedFont(false) 213 { 214 } 215 216 CanvasRenderingContext2D::State::State(const State& other) 217 : FontSelectorClient() 218 { 219 m_unparsedStrokeColor = other.m_unparsedStrokeColor; 220 m_unparsedFillColor = other.m_unparsedFillColor; 221 m_strokeStyle = other.m_strokeStyle; 222 m_fillStyle = other.m_fillStyle; 223 m_lineWidth = other.m_lineWidth; 224 m_lineCap = other.m_lineCap; 225 m_lineJoin = other.m_lineJoin; 226 m_miterLimit = other.m_miterLimit; 227 m_shadowOffset = other.m_shadowOffset; 228 m_shadowBlur = other.m_shadowBlur; 229 m_shadowColor = other.m_shadowColor; 230 m_globalAlpha = other.m_globalAlpha; 231 m_globalComposite = other.m_globalComposite; 232 m_transform = other.m_transform; 233 m_invertibleCTM = other.m_invertibleCTM; 234 m_textAlign = other.m_textAlign; 235 m_textBaseline = other.m_textBaseline; 236 m_unparsedFont = other.m_unparsedFont; 237 m_font = other.m_font; 238 m_realizedFont = other.m_realizedFont; 239 240 if (m_realizedFont) 241 m_font.fontSelector()->registerForInvalidationCallbacks(this); 242 } 243 244 CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other) 245 { 246 if (this == &other) 247 return *this; 248 249 if (m_realizedFont) 250 m_font.fontSelector()->unregisterForInvalidationCallbacks(this); 251 252 m_unparsedStrokeColor = other.m_unparsedStrokeColor; 253 m_unparsedFillColor = other.m_unparsedFillColor; 254 m_strokeStyle = other.m_strokeStyle; 255 m_fillStyle = other.m_fillStyle; 256 m_lineWidth = other.m_lineWidth; 257 m_lineCap = other.m_lineCap; 258 m_lineJoin = other.m_lineJoin; 259 m_miterLimit = other.m_miterLimit; 260 m_shadowOffset = other.m_shadowOffset; 261 m_shadowBlur = other.m_shadowBlur; 262 m_shadowColor = other.m_shadowColor; 263 m_globalAlpha = other.m_globalAlpha; 264 m_globalComposite = other.m_globalComposite; 265 m_transform = other.m_transform; 266 m_invertibleCTM = other.m_invertibleCTM; 267 m_textAlign = other.m_textAlign; 268 m_textBaseline = other.m_textBaseline; 269 m_unparsedFont = other.m_unparsedFont; 270 m_font = other.m_font; 271 m_realizedFont = other.m_realizedFont; 272 273 if (m_realizedFont) 274 m_font.fontSelector()->registerForInvalidationCallbacks(this); 275 276 return *this; 277 } 278 279 CanvasRenderingContext2D::State::~State() 280 { 281 if (m_realizedFont) 282 m_font.fontSelector()->unregisterForInvalidationCallbacks(this); 283 } 284 285 void CanvasRenderingContext2D::State::fontsNeedUpdate(FontSelector* fontSelector) 286 { 287 ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector()); 288 ASSERT(m_realizedFont); 289 290 m_font.update(fontSelector); 291 } 292 293 void CanvasRenderingContext2D::save() 294 { 295 ASSERT(m_stateStack.size() >= 1); 296 m_stateStack.append(state()); 297 GraphicsContext* c = drawingContext(); 298 if (!c) 299 return; 300 c->save(); 301 } 302 303 void CanvasRenderingContext2D::restore() 304 { 305 ASSERT(m_stateStack.size() >= 1); 306 if (m_stateStack.size() <= 1) 307 return; 308 m_path.transform(state().m_transform); 309 m_stateStack.removeLast(); 310 m_path.transform(state().m_transform.inverse()); 311 GraphicsContext* c = drawingContext(); 312 if (!c) 313 return; 314 c->restore(); 315 } 316 317 void CanvasRenderingContext2D::setAllAttributesToDefault() 318 { 319 state().m_globalAlpha = 1; 320 state().m_shadowOffset = FloatSize(); 321 state().m_shadowBlur = 0; 322 state().m_shadowColor = Color::transparent; 323 state().m_globalComposite = CompositeSourceOver; 324 325 GraphicsContext* context = drawingContext(); 326 if (!context) 327 return; 328 329 context->setLegacyShadow(FloatSize(), 0, Color::transparent, ColorSpaceDeviceRGB); 330 context->setAlpha(1); 331 context->setCompositeOperation(CompositeSourceOver); 332 } 333 334 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const 335 { 336 return state().m_strokeStyle.get(); 337 } 338 339 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style) 340 { 341 if (!style) 342 return; 343 344 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style)) 345 return; 346 347 if (style->isCurrentColor()) { 348 if (style->hasOverrideAlpha()) 349 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha())); 350 else 351 style = CanvasStyle::createFromRGBA(currentColor(canvas())); 352 } else 353 checkOrigin(style->canvasPattern()); 354 355 state().m_strokeStyle = style; 356 GraphicsContext* c = drawingContext(); 357 if (!c) 358 return; 359 state().m_strokeStyle->applyStrokeColor(c); 360 state().m_unparsedStrokeColor = String(); 361 } 362 363 CanvasStyle* CanvasRenderingContext2D::fillStyle() const 364 { 365 return state().m_fillStyle.get(); 366 } 367 368 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style) 369 { 370 if (!style) 371 return; 372 373 if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style)) 374 return; 375 376 if (style->isCurrentColor()) { 377 if (style->hasOverrideAlpha()) 378 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha())); 379 else 380 style = CanvasStyle::createFromRGBA(currentColor(canvas())); 381 } else 382 checkOrigin(style->canvasPattern()); 383 384 state().m_fillStyle = style; 385 GraphicsContext* c = drawingContext(); 386 if (!c) 387 return; 388 state().m_fillStyle->applyFillColor(c); 389 state().m_unparsedFillColor = String(); 390 } 391 392 float CanvasRenderingContext2D::lineWidth() const 393 { 394 return state().m_lineWidth; 395 } 396 397 void CanvasRenderingContext2D::setLineWidth(float width) 398 { 399 if (!(isfinite(width) && width > 0)) 400 return; 401 state().m_lineWidth = width; 402 GraphicsContext* c = drawingContext(); 403 if (!c) 404 return; 405 c->setStrokeThickness(width); 406 } 407 408 String CanvasRenderingContext2D::lineCap() const 409 { 410 return lineCapName(state().m_lineCap); 411 } 412 413 void CanvasRenderingContext2D::setLineCap(const String& s) 414 { 415 LineCap cap; 416 if (!parseLineCap(s, cap)) 417 return; 418 state().m_lineCap = cap; 419 GraphicsContext* c = drawingContext(); 420 if (!c) 421 return; 422 c->setLineCap(cap); 423 } 424 425 String CanvasRenderingContext2D::lineJoin() const 426 { 427 return lineJoinName(state().m_lineJoin); 428 } 429 430 void CanvasRenderingContext2D::setLineJoin(const String& s) 431 { 432 LineJoin join; 433 if (!parseLineJoin(s, join)) 434 return; 435 state().m_lineJoin = join; 436 GraphicsContext* c = drawingContext(); 437 if (!c) 438 return; 439 c->setLineJoin(join); 440 } 441 442 float CanvasRenderingContext2D::miterLimit() const 443 { 444 return state().m_miterLimit; 445 } 446 447 void CanvasRenderingContext2D::setMiterLimit(float limit) 448 { 449 if (!(isfinite(limit) && limit > 0)) 450 return; 451 state().m_miterLimit = limit; 452 GraphicsContext* c = drawingContext(); 453 if (!c) 454 return; 455 c->setMiterLimit(limit); 456 } 457 458 float CanvasRenderingContext2D::shadowOffsetX() const 459 { 460 return state().m_shadowOffset.width(); 461 } 462 463 void CanvasRenderingContext2D::setShadowOffsetX(float x) 464 { 465 if (!isfinite(x)) 466 return; 467 state().m_shadowOffset.setWidth(x); 468 applyShadow(); 469 } 470 471 float CanvasRenderingContext2D::shadowOffsetY() const 472 { 473 return state().m_shadowOffset.height(); 474 } 475 476 void CanvasRenderingContext2D::setShadowOffsetY(float y) 477 { 478 if (!isfinite(y)) 479 return; 480 state().m_shadowOffset.setHeight(y); 481 applyShadow(); 482 } 483 484 float CanvasRenderingContext2D::shadowBlur() const 485 { 486 return state().m_shadowBlur; 487 } 488 489 void CanvasRenderingContext2D::setShadowBlur(float blur) 490 { 491 if (!(isfinite(blur) && blur >= 0)) 492 return; 493 state().m_shadowBlur = blur; 494 applyShadow(); 495 } 496 497 String CanvasRenderingContext2D::shadowColor() const 498 { 499 return Color(state().m_shadowColor).serialized(); 500 } 501 502 void CanvasRenderingContext2D::setShadowColor(const String& color) 503 { 504 if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas())) 505 return; 506 507 applyShadow(); 508 } 509 510 float CanvasRenderingContext2D::globalAlpha() const 511 { 512 return state().m_globalAlpha; 513 } 514 515 void CanvasRenderingContext2D::setGlobalAlpha(float alpha) 516 { 517 if (!(alpha >= 0 && alpha <= 1)) 518 return; 519 state().m_globalAlpha = alpha; 520 GraphicsContext* c = drawingContext(); 521 if (!c) 522 return; 523 c->setAlpha(alpha); 524 } 525 526 String CanvasRenderingContext2D::globalCompositeOperation() const 527 { 528 return compositeOperatorName(state().m_globalComposite); 529 } 530 531 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation) 532 { 533 CompositeOperator op; 534 if (!parseCompositeOperator(operation, op)) 535 return; 536 state().m_globalComposite = op; 537 GraphicsContext* c = drawingContext(); 538 if (!c) 539 return; 540 c->setCompositeOperation(op); 541 #if ENABLE(ACCELERATED_2D_CANVAS) && !ENABLE(SKIA_GPU) 542 if (isAccelerated() && op != CompositeSourceOver) { 543 c->setSharedGraphicsContext3D(0, 0, IntSize()); 544 m_drawingBuffer.clear(); 545 m_context3D.clear(); 546 // Mark as needing a style recalc so our compositing layer can be removed. 547 canvas()->setNeedsStyleRecalc(SyntheticStyleChange); 548 } 549 #endif 550 } 551 552 void CanvasRenderingContext2D::scale(float sx, float sy) 553 { 554 GraphicsContext* c = drawingContext(); 555 if (!c) 556 return; 557 if (!state().m_invertibleCTM) 558 return; 559 560 if (!isfinite(sx) | !isfinite(sy)) 561 return; 562 563 AffineTransform newTransform = state().m_transform; 564 newTransform.scaleNonUniform(sx, sy); 565 if (!newTransform.isInvertible()) { 566 state().m_invertibleCTM = false; 567 return; 568 } 569 570 state().m_transform = newTransform; 571 c->scale(FloatSize(sx, sy)); 572 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy)); 573 } 574 575 void CanvasRenderingContext2D::rotate(float angleInRadians) 576 { 577 GraphicsContext* c = drawingContext(); 578 if (!c) 579 return; 580 if (!state().m_invertibleCTM) 581 return; 582 583 if (!isfinite(angleInRadians)) 584 return; 585 586 AffineTransform newTransform = state().m_transform; 587 newTransform.rotate(angleInRadians / piDouble * 180.0); 588 if (!newTransform.isInvertible()) { 589 state().m_invertibleCTM = false; 590 return; 591 } 592 593 state().m_transform = newTransform; 594 c->rotate(angleInRadians); 595 m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0)); 596 } 597 598 void CanvasRenderingContext2D::translate(float tx, float ty) 599 { 600 GraphicsContext* c = drawingContext(); 601 if (!c) 602 return; 603 if (!state().m_invertibleCTM) 604 return; 605 606 if (!isfinite(tx) | !isfinite(ty)) 607 return; 608 609 AffineTransform newTransform = state().m_transform; 610 newTransform.translate(tx, ty); 611 if (!newTransform.isInvertible()) { 612 state().m_invertibleCTM = false; 613 return; 614 } 615 616 state().m_transform = newTransform; 617 c->translate(tx, ty); 618 m_path.transform(AffineTransform().translate(-tx, -ty)); 619 } 620 621 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy) 622 { 623 GraphicsContext* c = drawingContext(); 624 if (!c) 625 return; 626 if (!state().m_invertibleCTM) 627 return; 628 629 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) | !isfinite(m12) | !isfinite(m22) | !isfinite(dy)) 630 return; 631 632 AffineTransform transform(m11, m12, m21, m22, dx, dy); 633 AffineTransform newTransform = state().m_transform * transform; 634 if (!newTransform.isInvertible()) { 635 state().m_invertibleCTM = false; 636 return; 637 } 638 639 state().m_transform = newTransform; 640 c->concatCTM(transform); 641 m_path.transform(transform.inverse()); 642 } 643 644 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy) 645 { 646 GraphicsContext* c = drawingContext(); 647 if (!c) 648 return; 649 650 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) | !isfinite(m12) | !isfinite(m22) | !isfinite(dy)) 651 return; 652 653 AffineTransform ctm = state().m_transform; 654 if (!ctm.isInvertible()) 655 return; 656 c->concatCTM(c->getCTM().inverse()); 657 c->concatCTM(canvas()->baseTransform()); 658 state().m_transform = ctm.inverse() * state().m_transform; 659 m_path.transform(ctm); 660 661 state().m_invertibleCTM = true; 662 transform(m11, m12, m21, m22, dx, dy); 663 } 664 665 void CanvasRenderingContext2D::setStrokeColor(const String& color) 666 { 667 if (color == state().m_unparsedStrokeColor) 668 return; 669 setStrokeStyle(CanvasStyle::createFromString(color, canvas()->document())); 670 state().m_unparsedStrokeColor = color; 671 } 672 673 void CanvasRenderingContext2D::setStrokeColor(float grayLevel) 674 { 675 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) 676 return; 677 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f)); 678 } 679 680 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha) 681 { 682 setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); 683 } 684 685 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha) 686 { 687 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) 688 return; 689 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha)); 690 } 691 692 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a) 693 { 694 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b, a)) 695 return; 696 setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a)); 697 } 698 699 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a) 700 { 701 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a)) 702 return; 703 setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a)); 704 } 705 706 void CanvasRenderingContext2D::setFillColor(const String& color) 707 { 708 if (color == state().m_unparsedFillColor) 709 return; 710 setFillStyle(CanvasStyle::createFromString(color, canvas()->document())); 711 state().m_unparsedFillColor = color; 712 } 713 714 void CanvasRenderingContext2D::setFillColor(float grayLevel) 715 { 716 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) 717 return; 718 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f)); 719 } 720 721 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha) 722 { 723 setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); 724 } 725 726 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha) 727 { 728 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) 729 return; 730 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha)); 731 } 732 733 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a) 734 { 735 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a)) 736 return; 737 setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a)); 738 } 739 740 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a) 741 { 742 if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k, a)) 743 return; 744 setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a)); 745 } 746 747 void CanvasRenderingContext2D::beginPath() 748 { 749 m_path.clear(); 750 } 751 752 void CanvasRenderingContext2D::closePath() 753 { 754 if (m_path.isEmpty()) 755 return; 756 757 FloatRect boundRect = m_path.boundingRect(); 758 if (boundRect.width() || boundRect.height()) 759 m_path.closeSubpath(); 760 } 761 762 void CanvasRenderingContext2D::moveTo(float x, float y) 763 { 764 if (!isfinite(x) | !isfinite(y)) 765 return; 766 if (!state().m_invertibleCTM) 767 return; 768 m_path.moveTo(FloatPoint(x, y)); 769 } 770 771 void CanvasRenderingContext2D::lineTo(float x, float y) 772 { 773 if (!isfinite(x) | !isfinite(y)) 774 return; 775 if (!state().m_invertibleCTM) 776 return; 777 778 FloatPoint p1 = FloatPoint(x, y); 779 if (!m_path.hasCurrentPoint()) 780 m_path.moveTo(p1); 781 else if (p1 != m_path.currentPoint()) 782 m_path.addLineTo(FloatPoint(x, y)); 783 } 784 785 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y) 786 { 787 if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y)) 788 return; 789 if (!state().m_invertibleCTM) 790 return; 791 if (!m_path.hasCurrentPoint()) 792 m_path.moveTo(FloatPoint(cpx, cpy)); 793 794 FloatPoint p1 = FloatPoint(x, y); 795 if (p1 != m_path.currentPoint()) 796 m_path.addQuadCurveTo(FloatPoint(cpx, cpy), p1); 797 } 798 799 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) 800 { 801 if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y)) 802 return; 803 if (!state().m_invertibleCTM) 804 return; 805 if (!m_path.hasCurrentPoint()) 806 m_path.moveTo(FloatPoint(cp1x, cp1y)); 807 808 FloatPoint p1 = FloatPoint(x, y); 809 if (p1 != m_path.currentPoint()) 810 m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), p1); 811 } 812 813 void CanvasRenderingContext2D::arcTo(float x1, float y1, float x2, float y2, float r, ExceptionCode& ec) 814 { 815 ec = 0; 816 if (!isfinite(x1) | !isfinite(y1) | !isfinite(x2) | !isfinite(y2) | !isfinite(r)) 817 return; 818 819 if (r < 0) { 820 ec = INDEX_SIZE_ERR; 821 return; 822 } 823 824 if (!state().m_invertibleCTM) 825 return; 826 827 FloatPoint p1 = FloatPoint(x1, y1); 828 FloatPoint p2 = FloatPoint(x2, y2); 829 830 if (!m_path.hasCurrentPoint()) 831 m_path.moveTo(p1); 832 else if (p1 == m_path.currentPoint() || p1 == p2 || !r) 833 lineTo(x1, y1); 834 else 835 m_path.addArcTo(p1, p2, r); 836 } 837 838 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec) 839 { 840 ec = 0; 841 if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea)) 842 return; 843 844 if (r < 0) { 845 ec = INDEX_SIZE_ERR; 846 return; 847 } 848 849 if (sa == ea) 850 return; 851 852 if (!state().m_invertibleCTM) 853 return; 854 m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise); 855 } 856 857 static bool validateRectForCanvas(float& x, float& y, float& width, float& height) 858 { 859 if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height)) 860 return false; 861 862 if (!width && !height) 863 return false; 864 865 if (width < 0) { 866 width = -width; 867 x -= width; 868 } 869 870 if (height < 0) { 871 height = -height; 872 y -= height; 873 } 874 875 return true; 876 } 877 878 void CanvasRenderingContext2D::rect(float x, float y, float width, float height) 879 { 880 if (!state().m_invertibleCTM) 881 return; 882 883 if (!isfinite(x) || !isfinite(y) || !isfinite(width) || !isfinite(height)) 884 return; 885 886 if (!width && !height) { 887 m_path.moveTo(FloatPoint(x, y)); 888 return; 889 } 890 891 m_path.addRect(FloatRect(x, y, width, height)); 892 } 893 894 #if ENABLE(DASHBOARD_SUPPORT) 895 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode() 896 { 897 if (m_usesDashboardCompatibilityMode) 898 m_path.clear(); 899 } 900 #endif 901 902 void CanvasRenderingContext2D::fill() 903 { 904 GraphicsContext* c = drawingContext(); 905 if (!c) 906 return; 907 if (!state().m_invertibleCTM) 908 return; 909 910 if (!m_path.isEmpty()) { 911 c->fillPath(m_path); 912 didDraw(m_path.boundingRect()); 913 } 914 915 #if ENABLE(DASHBOARD_SUPPORT) 916 clearPathForDashboardBackwardCompatibilityMode(); 917 #endif 918 } 919 920 void CanvasRenderingContext2D::stroke() 921 { 922 GraphicsContext* c = drawingContext(); 923 if (!c) 924 return; 925 if (!state().m_invertibleCTM) 926 return; 927 928 if (!m_path.isEmpty()) { 929 #if PLATFORM(QT) 930 // Fast approximation of the stroke's bounding rect. 931 // This yields a slightly oversized rect but is very fast 932 // compared to Path::strokeBoundingRect(). 933 FloatRect boundingRect = m_path.platformPath().controlPointRect(); 934 boundingRect.inflate(state().m_miterLimit + state().m_lineWidth); 935 #else 936 CanvasStrokeStyleApplier strokeApplier(this); 937 FloatRect boundingRect = m_path.strokeBoundingRect(&strokeApplier); 938 #endif 939 c->strokePath(m_path); 940 didDraw(boundingRect); 941 } 942 943 #if ENABLE(DASHBOARD_SUPPORT) 944 clearPathForDashboardBackwardCompatibilityMode(); 945 #endif 946 } 947 948 void CanvasRenderingContext2D::clip() 949 { 950 GraphicsContext* c = drawingContext(); 951 if (!c) 952 return; 953 if (!state().m_invertibleCTM) 954 return; 955 c->canvasClip(m_path); 956 #if ENABLE(DASHBOARD_SUPPORT) 957 clearPathForDashboardBackwardCompatibilityMode(); 958 #endif 959 } 960 961 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y) 962 { 963 GraphicsContext* c = drawingContext(); 964 if (!c) 965 return false; 966 if (!state().m_invertibleCTM) 967 return false; 968 969 FloatPoint point(x, y); 970 AffineTransform ctm = state().m_transform; 971 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); 972 if (!isfinite(transformedPoint.x()) || !isfinite(transformedPoint.y())) 973 return false; 974 return m_path.contains(transformedPoint); 975 } 976 977 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height) 978 { 979 if (!validateRectForCanvas(x, y, width, height)) 980 return; 981 GraphicsContext* context = drawingContext(); 982 if (!context) 983 return; 984 if (!state().m_invertibleCTM) 985 return; 986 FloatRect rect(x, y, width, height); 987 988 save(); 989 setAllAttributesToDefault(); 990 context->clearRect(rect); 991 didDraw(rect); 992 restore(); 993 } 994 995 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height) 996 { 997 if (!validateRectForCanvas(x, y, width, height)) 998 return; 999 1000 GraphicsContext* c = drawingContext(); 1001 if (!c) 1002 return; 1003 if (!state().m_invertibleCTM) 1004 return; 1005 1006 // from the HTML5 Canvas spec: 1007 // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing 1008 // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing 1009 Gradient* gradient = c->fillGradient(); 1010 if (gradient && gradient->isZeroSize()) 1011 return; 1012 1013 FloatRect rect(x, y, width, height); 1014 1015 c->fillRect(rect); 1016 didDraw(rect); 1017 } 1018 1019 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height) 1020 { 1021 if (!validateRectForCanvas(x, y, width, height)) 1022 return; 1023 strokeRect(x, y, width, height, state().m_lineWidth); 1024 } 1025 1026 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth) 1027 { 1028 if (!validateRectForCanvas(x, y, width, height)) 1029 return; 1030 1031 if (!(lineWidth >= 0)) 1032 return; 1033 1034 GraphicsContext* c = drawingContext(); 1035 if (!c) 1036 return; 1037 if (!state().m_invertibleCTM) 1038 return; 1039 1040 FloatRect rect(x, y, width, height); 1041 1042 FloatRect boundingRect = rect; 1043 boundingRect.inflate(lineWidth / 2); 1044 1045 c->strokeRect(rect, lineWidth); 1046 didDraw(boundingRect); 1047 } 1048 1049 #if USE(CG) 1050 static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height) 1051 { 1052 // Work around <rdar://problem/5539388> by ensuring that shadow offsets will get truncated 1053 // to the desired integer. 1054 static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128); 1055 if (width > 0) 1056 width += extraShadowOffset; 1057 else if (width < 0) 1058 width -= extraShadowOffset; 1059 1060 if (height > 0) 1061 height += extraShadowOffset; 1062 else if (height < 0) 1063 height -= extraShadowOffset; 1064 1065 return CGSizeMake(width, height); 1066 } 1067 #endif 1068 1069 void CanvasRenderingContext2D::setShadow(float width, float height, float blur) 1070 { 1071 state().m_shadowOffset = FloatSize(width, height); 1072 state().m_shadowBlur = blur; 1073 state().m_shadowColor = Color::transparent; 1074 applyShadow(); 1075 } 1076 1077 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color) 1078 { 1079 if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas())) 1080 return; 1081 1082 state().m_shadowOffset = FloatSize(width, height); 1083 state().m_shadowBlur = blur; 1084 applyShadow(); 1085 } 1086 1087 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel) 1088 { 1089 state().m_shadowOffset = FloatSize(width, height); 1090 state().m_shadowBlur = blur; 1091 state().m_shadowColor = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f); 1092 1093 GraphicsContext* c = drawingContext(); 1094 if (!c) 1095 return; 1096 1097 c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); 1098 } 1099 1100 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha) 1101 { 1102 RGBA32 rgba; 1103 1104 if (!parseColorOrCurrentColor(rgba, color, canvas())) 1105 return; 1106 1107 state().m_shadowColor = colorWithOverrideAlpha(rgba, alpha); 1108 state().m_shadowOffset = FloatSize(width, height); 1109 state().m_shadowBlur = blur; 1110 1111 GraphicsContext* c = drawingContext(); 1112 if (!c) 1113 return; 1114 1115 c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); 1116 } 1117 1118 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha) 1119 { 1120 state().m_shadowOffset = FloatSize(width, height); 1121 state().m_shadowBlur = blur; 1122 state().m_shadowColor = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha); 1123 1124 GraphicsContext* c = drawingContext(); 1125 if (!c) 1126 return; 1127 1128 c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); 1129 } 1130 1131 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a) 1132 { 1133 state().m_shadowOffset = FloatSize(width, height); 1134 state().m_shadowBlur = blur; 1135 state().m_shadowColor = makeRGBA32FromFloats(r, g, b, a); 1136 1137 GraphicsContext* c = drawingContext(); 1138 if (!c) 1139 return; 1140 1141 c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); 1142 } 1143 1144 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a) 1145 { 1146 state().m_shadowOffset = FloatSize(width, height); 1147 state().m_shadowBlur = blur; 1148 state().m_shadowColor = makeRGBAFromCMYKA(c, m, y, k, a); 1149 1150 GraphicsContext* dc = drawingContext(); 1151 if (!dc) 1152 return; 1153 #if USE(CG) 1154 const CGFloat components[5] = { c, m, y, k, a }; 1155 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK(); 1156 CGColorRef shadowColor = CGColorCreate(colorSpace, components); 1157 CGColorSpaceRelease(colorSpace); 1158 CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor); 1159 CGColorRelease(shadowColor); 1160 #else 1161 dc->setLegacyShadow(FloatSize(width, -height), blur, state().m_shadowColor, ColorSpaceDeviceRGB); 1162 #endif 1163 } 1164 1165 void CanvasRenderingContext2D::clearShadow() 1166 { 1167 state().m_shadowOffset = FloatSize(); 1168 state().m_shadowBlur = 0; 1169 state().m_shadowColor = Color::transparent; 1170 applyShadow(); 1171 } 1172 1173 void CanvasRenderingContext2D::applyShadow() 1174 { 1175 GraphicsContext* c = drawingContext(); 1176 if (!c) 1177 return; 1178 1179 float width = state().m_shadowOffset.width(); 1180 float height = state().m_shadowOffset.height(); 1181 c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); 1182 } 1183 1184 static IntSize size(HTMLImageElement* image) 1185 { 1186 if (CachedImage* cachedImage = image->cachedImage()) 1187 return cachedImage->imageSize(1.0f); // FIXME: Not sure about this. 1188 return IntSize(); 1189 } 1190 1191 #if ENABLE(VIDEO) 1192 static IntSize size(HTMLVideoElement* video) 1193 { 1194 if (MediaPlayer* player = video->player()) 1195 return player->naturalSize(); 1196 return IntSize(); 1197 } 1198 #endif 1199 1200 static inline FloatRect normalizeRect(const FloatRect& rect) 1201 { 1202 return FloatRect(min(rect.x(), rect.maxX()), 1203 min(rect.y(), rect.maxY()), 1204 max(rect.width(), -rect.width()), 1205 max(rect.height(), -rect.height())); 1206 } 1207 1208 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y, ExceptionCode& ec) 1209 { 1210 if (!image) { 1211 ec = TYPE_MISMATCH_ERR; 1212 return; 1213 } 1214 IntSize s = size(image); 1215 drawImage(image, x, y, s.width(), s.height(), ec); 1216 } 1217 1218 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, 1219 float x, float y, float width, float height, ExceptionCode& ec) 1220 { 1221 if (!image) { 1222 ec = TYPE_MISMATCH_ERR; 1223 return; 1224 } 1225 IntSize s = size(image); 1226 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec); 1227 } 1228 1229 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, 1230 float sx, float sy, float sw, float sh, 1231 float dx, float dy, float dw, float dh, ExceptionCode& ec) 1232 { 1233 if (!image) { 1234 ec = TYPE_MISMATCH_ERR; 1235 return; 1236 } 1237 drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); 1238 } 1239 1240 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec) 1241 { 1242 drawImage(image, srcRect, dstRect, state().m_globalComposite, ec); 1243 } 1244 1245 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, ExceptionCode& ec) 1246 { 1247 if (!image) { 1248 ec = TYPE_MISMATCH_ERR; 1249 return; 1250 } 1251 1252 ec = 0; 1253 1254 if (!isfinite(dstRect.x()) || !isfinite(dstRect.y()) || !isfinite(dstRect.width()) || !isfinite(dstRect.height()) 1255 || !isfinite(srcRect.x()) || !isfinite(srcRect.y()) || !isfinite(srcRect.width()) || !isfinite(srcRect.height())) 1256 return; 1257 1258 if (!dstRect.width() || !dstRect.height()) 1259 return; 1260 1261 if (!image->complete()) 1262 return; 1263 1264 FloatRect normalizedSrcRect = normalizeRect(srcRect); 1265 FloatRect normalizedDstRect = normalizeRect(dstRect); 1266 1267 FloatRect imageRect = FloatRect(FloatPoint(), size(image)); 1268 if (!imageRect.contains(normalizedSrcRect) || !srcRect.width() || !srcRect.height()) { 1269 ec = INDEX_SIZE_ERR; 1270 return; 1271 } 1272 1273 GraphicsContext* c = drawingContext(); 1274 if (!c) 1275 return; 1276 if (!state().m_invertibleCTM) 1277 return; 1278 1279 CachedImage* cachedImage = image->cachedImage(); 1280 if (!cachedImage) 1281 return; 1282 1283 checkOrigin(image); 1284 1285 FloatRect sourceRect = c->roundToDevicePixels(normalizedSrcRect); 1286 FloatRect destRect = c->roundToDevicePixels(normalizedDstRect); 1287 c->drawImage(cachedImage->image(), ColorSpaceDeviceRGB, destRect, sourceRect, op); 1288 didDraw(destRect); 1289 } 1290 1291 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y, ExceptionCode& ec) 1292 { 1293 if (!canvas) { 1294 ec = TYPE_MISMATCH_ERR; 1295 return; 1296 } 1297 drawImage(canvas, x, y, canvas->width(), canvas->height(), ec); 1298 } 1299 1300 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, 1301 float x, float y, float width, float height, ExceptionCode& ec) 1302 { 1303 if (!canvas) { 1304 ec = TYPE_MISMATCH_ERR; 1305 return; 1306 } 1307 drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec); 1308 } 1309 1310 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, 1311 float sx, float sy, float sw, float sh, 1312 float dx, float dy, float dw, float dh, ExceptionCode& ec) 1313 { 1314 drawImage(canvas, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); 1315 } 1316 1317 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const FloatRect& srcRect, 1318 const FloatRect& dstRect, ExceptionCode& ec) 1319 { 1320 if (!sourceCanvas) { 1321 ec = TYPE_MISMATCH_ERR; 1322 return; 1323 } 1324 1325 FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas->size()); 1326 1327 if (!srcCanvasRect.width() || !srcCanvasRect.height()) { 1328 ec = INVALID_STATE_ERR; 1329 return; 1330 } 1331 1332 if (!srcCanvasRect.contains(normalizeRect(srcRect)) || !srcRect.width() || !srcRect.height()) { 1333 ec = INDEX_SIZE_ERR; 1334 return; 1335 } 1336 1337 ec = 0; 1338 1339 if (!dstRect.width() || !dstRect.height()) 1340 return; 1341 1342 GraphicsContext* c = drawingContext(); 1343 if (!c) 1344 return; 1345 if (!state().m_invertibleCTM) 1346 return; 1347 1348 FloatRect sourceRect = c->roundToDevicePixels(srcRect); 1349 FloatRect destRect = c->roundToDevicePixels(dstRect); 1350 1351 // FIXME: Do this through platform-independent GraphicsContext API. 1352 ImageBuffer* buffer = sourceCanvas->buffer(); 1353 if (!buffer) 1354 return; 1355 1356 checkOrigin(sourceCanvas); 1357 1358 #if ENABLE(ACCELERATED_2D_CANVAS) 1359 // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas->makeRenderingResultsAvailable() 1360 // as that will do a readback to software. 1361 CanvasRenderingContext* sourceContext = sourceCanvas->renderingContext(); 1362 // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible. 1363 if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d()) 1364 sourceCanvas->makeRenderingResultsAvailable(); 1365 #else 1366 sourceCanvas->makeRenderingResultsAvailable(); 1367 #endif 1368 1369 c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, destRect, sourceRect, state().m_globalComposite); 1370 didDraw(destRect); 1371 } 1372 1373 #if ENABLE(VIDEO) 1374 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, float x, float y, ExceptionCode& ec) 1375 { 1376 if (!video) { 1377 ec = TYPE_MISMATCH_ERR; 1378 return; 1379 } 1380 IntSize s = size(video); 1381 drawImage(video, x, y, s.width(), s.height(), ec); 1382 } 1383 1384 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, 1385 float x, float y, float width, float height, ExceptionCode& ec) 1386 { 1387 if (!video) { 1388 ec = TYPE_MISMATCH_ERR; 1389 return; 1390 } 1391 IntSize s = size(video); 1392 drawImage(video, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec); 1393 } 1394 1395 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, 1396 float sx, float sy, float sw, float sh, 1397 float dx, float dy, float dw, float dh, ExceptionCode& ec) 1398 { 1399 drawImage(video, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); 1400 } 1401 1402 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, const FloatRect& srcRect, const FloatRect& dstRect, 1403 ExceptionCode& ec) 1404 { 1405 if (!video) { 1406 ec = TYPE_MISMATCH_ERR; 1407 return; 1408 } 1409 1410 ec = 0; 1411 1412 if (video->readyState() == HTMLMediaElement::HAVE_NOTHING || video->readyState() == HTMLMediaElement::HAVE_METADATA) 1413 return; 1414 1415 FloatRect videoRect = FloatRect(FloatPoint(), size(video)); 1416 if (!videoRect.contains(normalizeRect(srcRect)) || !srcRect.width() || !srcRect.height()) { 1417 ec = INDEX_SIZE_ERR; 1418 return; 1419 } 1420 1421 if (!dstRect.width() || !dstRect.height()) 1422 return; 1423 1424 GraphicsContext* c = drawingContext(); 1425 if (!c) 1426 return; 1427 if (!state().m_invertibleCTM) 1428 return; 1429 1430 checkOrigin(video); 1431 1432 FloatRect sourceRect = c->roundToDevicePixels(srcRect); 1433 FloatRect destRect = c->roundToDevicePixels(dstRect); 1434 1435 c->save(); 1436 c->clip(destRect); 1437 c->translate(destRect.x(), destRect.y()); 1438 c->scale(FloatSize(destRect.width() / sourceRect.width(), destRect.height() / sourceRect.height())); 1439 c->translate(-sourceRect.x(), -sourceRect.y()); 1440 video->paintCurrentFrameInContext(c, IntRect(IntPoint(), size(video))); 1441 c->restore(); 1442 didDraw(destRect); 1443 } 1444 #endif 1445 1446 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image, 1447 float sx, float sy, float sw, float sh, 1448 float dx, float dy, float dw, float dh, 1449 const String& compositeOperation) 1450 { 1451 CompositeOperator op; 1452 if (!parseCompositeOperator(compositeOperation, op)) 1453 op = CompositeSourceOver; 1454 1455 ExceptionCode ec; 1456 drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), op, ec); 1457 } 1458 1459 void CanvasRenderingContext2D::setAlpha(float alpha) 1460 { 1461 setGlobalAlpha(alpha); 1462 } 1463 1464 void CanvasRenderingContext2D::setCompositeOperation(const String& operation) 1465 { 1466 setGlobalCompositeOperation(operation); 1467 } 1468 1469 void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient* gradient) const 1470 { 1471 #if ENABLE(DASHBOARD_SUPPORT) 1472 if (m_usesDashboardCompatibilityMode) 1473 gradient->setDashboardCompatibilityMode(); 1474 #else 1475 UNUSED_PARAM(gradient); 1476 #endif 1477 } 1478 1479 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec) 1480 { 1481 if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) { 1482 ec = NOT_SUPPORTED_ERR; 1483 return 0; 1484 } 1485 1486 RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1)); 1487 prepareGradientForDashboard(gradient.get()); 1488 return gradient.release(); 1489 } 1490 1491 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec) 1492 { 1493 if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) || !isfinite(x1) || !isfinite(y1) || !isfinite(r1)) { 1494 ec = NOT_SUPPORTED_ERR; 1495 return 0; 1496 } 1497 1498 if (r0 < 0 || r1 < 0) { 1499 ec = INDEX_SIZE_ERR; 1500 return 0; 1501 } 1502 1503 RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1); 1504 prepareGradientForDashboard(gradient.get()); 1505 return gradient.release(); 1506 } 1507 1508 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image, 1509 const String& repetitionType, ExceptionCode& ec) 1510 { 1511 if (!image) { 1512 ec = TYPE_MISMATCH_ERR; 1513 return 0; 1514 } 1515 bool repeatX, repeatY; 1516 ec = 0; 1517 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); 1518 if (ec) 1519 return 0; 1520 1521 if (!image->complete()) 1522 return 0; 1523 1524 CachedImage* cachedImage = image->cachedImage(); 1525 if (!cachedImage || !image->cachedImage()->image()) 1526 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true); 1527 1528 bool originClean = !canvas()->securityOrigin().taintsCanvas(KURL(KURL(), cachedImage->response().url())) && cachedImage->image()->hasSingleSecurityOrigin(); 1529 return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originClean); 1530 } 1531 1532 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas, 1533 const String& repetitionType, ExceptionCode& ec) 1534 { 1535 if (!canvas) { 1536 ec = TYPE_MISMATCH_ERR; 1537 return 0; 1538 } 1539 if (!canvas->width() || !canvas->height()) { 1540 ec = INVALID_STATE_ERR; 1541 return 0; 1542 } 1543 1544 bool repeatX, repeatY; 1545 ec = 0; 1546 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); 1547 if (ec) 1548 return 0; 1549 return CanvasPattern::create(canvas->copiedImage(), repeatX, repeatY, canvas->originClean()); 1550 } 1551 1552 void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options) 1553 { 1554 GraphicsContext* c = drawingContext(); 1555 if (!c) 1556 return; 1557 if (!state().m_invertibleCTM) 1558 return; 1559 1560 FloatRect dirtyRect = r; 1561 if (options & CanvasDidDrawApplyTransform) { 1562 AffineTransform ctm = state().m_transform; 1563 dirtyRect = ctm.mapRect(r); 1564 } 1565 1566 if (options & CanvasDidDrawApplyShadow && alphaChannel(state().m_shadowColor)) { 1567 // The shadow gets applied after transformation 1568 FloatRect shadowRect(dirtyRect); 1569 shadowRect.move(state().m_shadowOffset); 1570 shadowRect.inflate(state().m_shadowBlur); 1571 dirtyRect.unite(shadowRect); 1572 } 1573 1574 if (options & CanvasDidDrawApplyClip) { 1575 // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip 1576 // back out of the GraphicsContext, so to take clip into account for incremental painting, 1577 // we'd have to keep the clip path around. 1578 } 1579 1580 #if ENABLE(ACCELERATED_2D_CANVAS) 1581 if (isAccelerated()) 1582 drawingContext()->markDirtyRect(enclosingIntRect(dirtyRect)); 1583 #endif 1584 #if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) 1585 // If we are drawing to hardware and we have a composited layer, just call contentChanged(). 1586 RenderBox* renderBox = canvas()->renderBox(); 1587 if (isAccelerated() && renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) 1588 renderBox->layer()->contentChanged(RenderLayer::CanvasChanged); 1589 else 1590 #endif 1591 canvas()->didDraw(dirtyRect); 1592 } 1593 1594 GraphicsContext* CanvasRenderingContext2D::drawingContext() const 1595 { 1596 return canvas()->drawingContext(); 1597 } 1598 1599 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size) 1600 { 1601 RefPtr<ImageData> data = ImageData::create(size); 1602 memset(data->data()->data()->data(), 0, data->data()->data()->length()); 1603 return data.release(); 1604 } 1605 1606 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtr<ImageData> imageData, ExceptionCode& ec) const 1607 { 1608 if (!imageData) { 1609 ec = NOT_SUPPORTED_ERR; 1610 return 0; 1611 } 1612 1613 return createEmptyImageData(imageData->size()); 1614 } 1615 1616 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionCode& ec) const 1617 { 1618 ec = 0; 1619 if (!sw || !sh) { 1620 ec = INDEX_SIZE_ERR; 1621 return 0; 1622 } 1623 if (!isfinite(sw) || !isfinite(sh)) { 1624 ec = NOT_SUPPORTED_ERR; 1625 return 0; 1626 } 1627 1628 FloatSize unscaledSize(fabs(sw), fabs(sh)); 1629 IntSize scaledSize = canvas()->convertLogicalToDevice(unscaledSize); 1630 if (scaledSize.width() < 1) 1631 scaledSize.setWidth(1); 1632 if (scaledSize.height() < 1) 1633 scaledSize.setHeight(1); 1634 1635 float area = 4.0f * scaledSize.width() * scaledSize.height(); 1636 if (area > static_cast<float>(std::numeric_limits<int>::max())) 1637 return 0; 1638 1639 return createEmptyImageData(scaledSize); 1640 } 1641 1642 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const 1643 { 1644 if (!canvas()->originClean()) { 1645 ec = SECURITY_ERR; 1646 return 0; 1647 } 1648 if (!sw || !sh) { 1649 ec = INDEX_SIZE_ERR; 1650 return 0; 1651 } 1652 if (!isfinite(sx) || !isfinite(sy) || !isfinite(sw) || !isfinite(sh)) { 1653 ec = NOT_SUPPORTED_ERR; 1654 return 0; 1655 } 1656 1657 if (sw < 0) { 1658 sx += sw; 1659 sw = -sw; 1660 } 1661 if (sh < 0) { 1662 sy += sh; 1663 sh = -sh; 1664 } 1665 1666 FloatRect unscaledRect(sx, sy, sw, sh); 1667 IntRect scaledRect = canvas()->convertLogicalToDevice(unscaledRect); 1668 if (scaledRect.width() < 1) 1669 scaledRect.setWidth(1); 1670 if (scaledRect.height() < 1) 1671 scaledRect.setHeight(1); 1672 ImageBuffer* buffer = canvas()->buffer(); 1673 if (!buffer) 1674 return createEmptyImageData(scaledRect.size()); 1675 1676 RefPtr<ByteArray> byteArray = buffer->getUnmultipliedImageData(scaledRect); 1677 if (!byteArray) 1678 return 0; 1679 1680 return ImageData::create(scaledRect.size(), byteArray.release()); 1681 } 1682 1683 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec) 1684 { 1685 if (!data) { 1686 ec = TYPE_MISMATCH_ERR; 1687 return; 1688 } 1689 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec); 1690 } 1691 1692 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, 1693 float dirtyWidth, float dirtyHeight, ExceptionCode& ec) 1694 { 1695 if (!data) { 1696 ec = TYPE_MISMATCH_ERR; 1697 return; 1698 } 1699 if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) || !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) { 1700 ec = NOT_SUPPORTED_ERR; 1701 return; 1702 } 1703 1704 ImageBuffer* buffer = canvas()->buffer(); 1705 if (!buffer) 1706 return; 1707 1708 if (dirtyWidth < 0) { 1709 dirtyX += dirtyWidth; 1710 dirtyWidth = -dirtyWidth; 1711 } 1712 1713 if (dirtyHeight < 0) { 1714 dirtyY += dirtyHeight; 1715 dirtyHeight = -dirtyHeight; 1716 } 1717 1718 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); 1719 clipRect.intersect(IntRect(0, 0, data->width(), data->height())); 1720 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy)); 1721 IntRect destRect = enclosingIntRect(clipRect); 1722 destRect.move(destOffset); 1723 destRect.intersect(IntRect(IntPoint(), buffer->size())); 1724 if (destRect.isEmpty()) 1725 return; 1726 IntRect sourceRect(destRect); 1727 sourceRect.move(-destOffset); 1728 1729 buffer->putUnmultipliedImageData(data->data()->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset)); 1730 didDraw(destRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip 1731 } 1732 1733 String CanvasRenderingContext2D::font() const 1734 { 1735 return state().m_unparsedFont; 1736 } 1737 1738 void CanvasRenderingContext2D::setFont(const String& newFont) 1739 { 1740 RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create(); 1741 CSSParser parser(!m_usesCSSCompatibilityParseMode); 1742 1743 String declarationText("font: "); 1744 declarationText += newFont; 1745 parser.parseDeclaration(tempDecl.get(), declarationText); 1746 if (!tempDecl->length()) 1747 return; 1748 1749 // The parse succeeded. 1750 state().m_unparsedFont = newFont; 1751 1752 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work 1753 // relative to the canvas. 1754 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 1755 if (RenderStyle* computedStyle = canvas()->computedStyle()) 1756 newStyle->setFontDescription(computedStyle->fontDescription()); 1757 newStyle->font().update(newStyle->font().fontSelector()); 1758 1759 // Now map the font property into the style. 1760 CSSStyleSelector* styleSelector = canvas()->styleSelector(); 1761 styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCSSValue(CSSPropertyFont).get(), newStyle.get()); 1762 1763 state().m_font = newStyle->font(); 1764 state().m_font.update(styleSelector->fontSelector()); 1765 state().m_realizedFont = true; 1766 styleSelector->fontSelector()->registerForInvalidationCallbacks(&state()); 1767 } 1768 1769 String CanvasRenderingContext2D::textAlign() const 1770 { 1771 return textAlignName(state().m_textAlign); 1772 } 1773 1774 void CanvasRenderingContext2D::setTextAlign(const String& s) 1775 { 1776 TextAlign align; 1777 if (!parseTextAlign(s, align)) 1778 return; 1779 state().m_textAlign = align; 1780 } 1781 1782 String CanvasRenderingContext2D::textBaseline() const 1783 { 1784 return textBaselineName(state().m_textBaseline); 1785 } 1786 1787 void CanvasRenderingContext2D::setTextBaseline(const String& s) 1788 { 1789 TextBaseline baseline; 1790 if (!parseTextBaseline(s, baseline)) 1791 return; 1792 state().m_textBaseline = baseline; 1793 } 1794 1795 void CanvasRenderingContext2D::fillText(const String& text, float x, float y) 1796 { 1797 drawTextInternal(text, x, y, true); 1798 } 1799 1800 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth) 1801 { 1802 drawTextInternal(text, x, y, true, maxWidth, true); 1803 } 1804 1805 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y) 1806 { 1807 drawTextInternal(text, x, y, false); 1808 } 1809 1810 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth) 1811 { 1812 drawTextInternal(text, x, y, false, maxWidth, true); 1813 } 1814 1815 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text) 1816 { 1817 RefPtr<TextMetrics> metrics = TextMetrics::create(); 1818 1819 #if PLATFORM(QT) 1820 // We always use complex text shaping since it can't be turned off for QPainterPath::addText(). 1821 Font::CodePath oldCodePath = Font::codePath(); 1822 Font::setCodePath(Font::Complex); 1823 #endif 1824 1825 metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length()))); 1826 1827 #if PLATFORM(QT) 1828 Font::setCodePath(oldCodePath); 1829 #endif 1830 1831 return metrics.release(); 1832 } 1833 1834 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float /*maxWidth*/, bool /*useMaxWidth*/) 1835 { 1836 GraphicsContext* c = drawingContext(); 1837 if (!c) 1838 return; 1839 if (!state().m_invertibleCTM) 1840 return; 1841 if (!isfinite(x) | !isfinite(y)) 1842 return; 1843 1844 const Font& font = accessFont(); 1845 const FontMetrics& fontMetrics = font.fontMetrics(); 1846 1847 // FIXME: Handle maxWidth. 1848 // FIXME: Need to turn off font smoothing. 1849 1850 RenderStyle* computedStyle = canvas()->computedStyle(); 1851 bool rtl = computedStyle ? !computedStyle->isLeftToRightDirection() : false; 1852 bool override = computedStyle ? computedStyle->unicodeBidi() == Override : false; 1853 1854 unsigned length = text.length(); 1855 const UChar* string = text.characters(); 1856 TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, rtl, override); 1857 1858 // Draw the item text at the correct point. 1859 FloatPoint location(x, y); 1860 switch (state().m_textBaseline) { 1861 case TopTextBaseline: 1862 case HangingTextBaseline: 1863 location.setY(y + fontMetrics.ascent()); 1864 break; 1865 case BottomTextBaseline: 1866 case IdeographicTextBaseline: 1867 location.setY(y - fontMetrics.descent()); 1868 break; 1869 case MiddleTextBaseline: 1870 location.setY(y - fontMetrics.descent() + fontMetrics.height() / 2); 1871 break; 1872 case AlphabeticTextBaseline: 1873 default: 1874 // Do nothing. 1875 break; 1876 } 1877 1878 float width = font.width(TextRun(text, false, 0, 0, TextRun::AllowTrailingExpansion, rtl, override)); 1879 1880 TextAlign align = state().m_textAlign; 1881 if (align == StartTextAlign) 1882 align = rtl ? RightTextAlign : LeftTextAlign; 1883 else if (align == EndTextAlign) 1884 align = rtl ? LeftTextAlign : RightTextAlign; 1885 1886 switch (align) { 1887 case CenterTextAlign: 1888 location.setX(location.x() - width / 2); 1889 break; 1890 case RightTextAlign: 1891 location.setX(location.x() - width); 1892 break; 1893 default: 1894 break; 1895 } 1896 1897 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text. 1898 FloatRect textRect = FloatRect(location.x() - fontMetrics.height() / 2, location.y() - fontMetrics.ascent() - fontMetrics.lineGap(), 1899 width + fontMetrics.height(), fontMetrics.lineSpacing()); 1900 if (!fill) 1901 textRect.inflate(c->strokeThickness() / 2); 1902 1903 #if USE(CG) 1904 CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get(); 1905 if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) { 1906 // FIXME: The rect is not big enough for miters on stroked text. 1907 IntRect maskRect = enclosingIntRect(textRect); 1908 1909 #if USE(IOSURFACE_CANVAS_BACKING_STORE) 1910 OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), ColorSpaceDeviceRGB, Accelerated); 1911 #else 1912 OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size()); 1913 #endif 1914 1915 GraphicsContext* maskImageContext = maskImage->context(); 1916 1917 if (fill) 1918 maskImageContext->setFillColor(Color::black, ColorSpaceDeviceRGB); 1919 else { 1920 maskImageContext->setStrokeColor(Color::black, ColorSpaceDeviceRGB); 1921 maskImageContext->setStrokeThickness(c->strokeThickness()); 1922 } 1923 1924 maskImageContext->setTextDrawingMode(fill ? TextModeFill : TextModeStroke); 1925 maskImageContext->translate(-maskRect.x(), -maskRect.y()); 1926 1927 maskImageContext->drawBidiText(font, textRun, location); 1928 1929 c->save(); 1930 c->clipToImageBuffer(maskImage.get(), maskRect); 1931 drawStyle->applyFillColor(c); 1932 c->fillRect(maskRect); 1933 c->restore(); 1934 1935 return; 1936 } 1937 #endif 1938 1939 c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke); 1940 1941 #if PLATFORM(QT) 1942 // We always use complex text shaping since it can't be turned off for QPainterPath::addText(). 1943 Font::CodePath oldCodePath = Font::codePath(); 1944 Font::setCodePath(Font::Complex); 1945 #endif 1946 1947 c->drawBidiText(font, textRun, location); 1948 1949 if (fill) 1950 didDraw(textRect); 1951 else { 1952 // When stroking text, pointy miters can extend outside of textRect, so we 1953 // punt and dirty the whole canvas. 1954 didDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); 1955 } 1956 1957 #if PLATFORM(QT) 1958 Font::setCodePath(oldCodePath); 1959 #endif 1960 } 1961 1962 const Font& CanvasRenderingContext2D::accessFont() 1963 { 1964 canvas()->document()->updateStyleIfNeeded(); 1965 1966 if (!state().m_realizedFont) 1967 setFont(state().m_unparsedFont); 1968 return state().m_font; 1969 } 1970 1971 void CanvasRenderingContext2D::paintRenderingResultsToCanvas() 1972 { 1973 #if ENABLE(ACCELERATED_2D_CANVAS) 1974 if (GraphicsContext* c = drawingContext()) 1975 c->syncSoftwareCanvas(); 1976 #endif 1977 } 1978 1979 #if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) 1980 PlatformLayer* CanvasRenderingContext2D::platformLayer() const 1981 { 1982 return m_drawingBuffer ? m_drawingBuffer->platformLayer() : 0; 1983 } 1984 #endif 1985 1986 } // namespace WebCore 1987