CanvasRenderingContext2D.cpp revision d0147a863b872ecaa451ab0dce2a348760e99e2c
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@atoker.com> 5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> 6 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> 7 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. 8 * 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 79using namespace std; 80 81namespace WebCore { 82 83using namespace HTMLNames; 84 85static const char* const defaultFont = "10px sans-serif"; 86 87 88class CanvasStrokeStyleApplier : public StrokeStyleApplier { 89public: 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 103private: 104 CanvasRenderingContext2D* m_canvasContext; 105}; 106 107CanvasRenderingContext2D::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 146CanvasRenderingContext2D::~CanvasRenderingContext2D() 147{ 148} 149 150bool 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 162bool 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 172void 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 197CanvasRenderingContext2D::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 216CanvasRenderingContext2D::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 244CanvasRenderingContext2D::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 279CanvasRenderingContext2D::State::~State() 280{ 281 if (m_realizedFont) 282 m_font.fontSelector()->unregisterForInvalidationCallbacks(this); 283} 284 285void 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 293void 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 303void 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 317void 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 334CanvasStyle* CanvasRenderingContext2D::strokeStyle() const 335{ 336 return state().m_strokeStyle.get(); 337} 338 339void 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 363CanvasStyle* CanvasRenderingContext2D::fillStyle() const 364{ 365 return state().m_fillStyle.get(); 366} 367 368void 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 392float CanvasRenderingContext2D::lineWidth() const 393{ 394 return state().m_lineWidth; 395} 396 397void 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 408String CanvasRenderingContext2D::lineCap() const 409{ 410 return lineCapName(state().m_lineCap); 411} 412 413void 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 425String CanvasRenderingContext2D::lineJoin() const 426{ 427 return lineJoinName(state().m_lineJoin); 428} 429 430void 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 442float CanvasRenderingContext2D::miterLimit() const 443{ 444 return state().m_miterLimit; 445} 446 447void 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 458float CanvasRenderingContext2D::shadowOffsetX() const 459{ 460 return state().m_shadowOffset.width(); 461} 462 463void CanvasRenderingContext2D::setShadowOffsetX(float x) 464{ 465 if (!isfinite(x)) 466 return; 467 state().m_shadowOffset.setWidth(x); 468 applyShadow(); 469} 470 471float CanvasRenderingContext2D::shadowOffsetY() const 472{ 473 return state().m_shadowOffset.height(); 474} 475 476void CanvasRenderingContext2D::setShadowOffsetY(float y) 477{ 478 if (!isfinite(y)) 479 return; 480 state().m_shadowOffset.setHeight(y); 481 applyShadow(); 482} 483 484float CanvasRenderingContext2D::shadowBlur() const 485{ 486 return state().m_shadowBlur; 487} 488 489void CanvasRenderingContext2D::setShadowBlur(float blur) 490{ 491 if (!(isfinite(blur) && blur >= 0)) 492 return; 493 state().m_shadowBlur = blur; 494 applyShadow(); 495} 496 497String CanvasRenderingContext2D::shadowColor() const 498{ 499 return Color(state().m_shadowColor).serialized(); 500} 501 502void CanvasRenderingContext2D::setShadowColor(const String& color) 503{ 504 if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas())) 505 return; 506 507 applyShadow(); 508} 509 510float CanvasRenderingContext2D::globalAlpha() const 511{ 512 return state().m_globalAlpha; 513} 514 515void 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 526String CanvasRenderingContext2D::globalCompositeOperation() const 527{ 528 return compositeOperatorName(state().m_globalComposite); 529} 530 531void 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 552void 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 575void 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 598void 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 621void 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 644void 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 665void 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 673void 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 680void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha) 681{ 682 setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); 683} 684 685void 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 692void 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 699void 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 706void 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 714void 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 721void CanvasRenderingContext2D::setFillColor(const String& color, float alpha) 722{ 723 setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); 724} 725 726void 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 733void 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 740void 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 747void CanvasRenderingContext2D::beginPath() 748{ 749 m_path.clear(); 750} 751 752void 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 762void 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 771void 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 785void 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 799void 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 813void 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 838void 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 857static 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 878void 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) 895void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode() 896{ 897 if (m_usesDashboardCompatibilityMode) 898 m_path.clear(); 899} 900#endif 901 902void 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 920void 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 948void 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 961bool 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 977void 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 995void 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 1019void 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 1026void 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) 1050static 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 1069void 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 1077void 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 1087void 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 1100void 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 1118void 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 1131void 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 1144void 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 1165void CanvasRenderingContext2D::clearShadow() 1166{ 1167 state().m_shadowOffset = FloatSize(); 1168 state().m_shadowBlur = 0; 1169 state().m_shadowColor = Color::transparent; 1170 applyShadow(); 1171} 1172 1173void 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 1184static 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) 1192static IntSize size(HTMLVideoElement* video) 1193{ 1194 if (MediaPlayer* player = video->player()) 1195 return player->naturalSize(); 1196 return IntSize(); 1197} 1198#endif 1199 1200static 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 1208void 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 1218void 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 1229void 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 1240void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec) 1241{ 1242 drawImage(image, srcRect, dstRect, state().m_globalComposite, ec); 1243} 1244 1245void 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 1291void 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 1300void 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 1310void 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 1317void 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) 1374void 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 1384void 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 1395void 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 1402void 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 1446void 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 1459void CanvasRenderingContext2D::setAlpha(float alpha) 1460{ 1461 setGlobalAlpha(alpha); 1462} 1463 1464void CanvasRenderingContext2D::setCompositeOperation(const String& operation) 1465{ 1466 setGlobalCompositeOperation(operation); 1467} 1468 1469void 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 1479PassRefPtr<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 1491PassRefPtr<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 1508PassRefPtr<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 1532PassRefPtr<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 1552void 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 1594GraphicsContext* CanvasRenderingContext2D::drawingContext() const 1595{ 1596 return canvas()->drawingContext(); 1597} 1598 1599static 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 1606PassRefPtr<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 1616PassRefPtr<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 1642PassRefPtr<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 1683void 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 1692void 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 1733String CanvasRenderingContext2D::font() const 1734{ 1735 return state().m_unparsedFont; 1736} 1737 1738void 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 1769String CanvasRenderingContext2D::textAlign() const 1770{ 1771 return textAlignName(state().m_textAlign); 1772} 1773 1774void CanvasRenderingContext2D::setTextAlign(const String& s) 1775{ 1776 TextAlign align; 1777 if (!parseTextAlign(s, align)) 1778 return; 1779 state().m_textAlign = align; 1780} 1781 1782String CanvasRenderingContext2D::textBaseline() const 1783{ 1784 return textBaselineName(state().m_textBaseline); 1785} 1786 1787void CanvasRenderingContext2D::setTextBaseline(const String& s) 1788{ 1789 TextBaseline baseline; 1790 if (!parseTextBaseline(s, baseline)) 1791 return; 1792 state().m_textBaseline = baseline; 1793} 1794 1795void CanvasRenderingContext2D::fillText(const String& text, float x, float y) 1796{ 1797 drawTextInternal(text, x, y, true); 1798} 1799 1800void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth) 1801{ 1802 drawTextInternal(text, x, y, true, maxWidth, true); 1803} 1804 1805void CanvasRenderingContext2D::strokeText(const String& text, float x, float y) 1806{ 1807 drawTextInternal(text, x, y, false); 1808} 1809 1810void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth) 1811{ 1812 drawTextInternal(text, x, y, false, maxWidth, true); 1813} 1814 1815PassRefPtr<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 1834void 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 1962const 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 1971void 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) 1980PlatformLayer* CanvasRenderingContext2D::platformLayer() const 1981{ 1982 return m_drawingBuffer ? m_drawingBuffer->platformLayer() : 0; 1983} 1984#endif 1985 1986} // namespace WebCore 1987