1c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown/* 2c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. 3c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) 4c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Copyright (C) 2007 Alp Toker <alp@atoker.com> 5c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Copyright (C) 2008 Eric Seidel <eric@webkit.org> 6c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> 7c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. 8c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved. 9c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. 10c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * 11c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * Redistribution and use in source and binary forms, with or without 12c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * modification, are permitted provided that the following conditions 13c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * are met: 14c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * 1. Redistributions of source code must retain the above copyright 15c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * notice, this list of conditions and the following disclaimer. 16c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * 2. Redistributions in binary form must reproduce the above copyright 17c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * notice, this list of conditions and the following disclaimer in the 18c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * documentation and/or other materials provided with the distribution. 196ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown * 20c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 21c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2232cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2332cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 24c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 28692065128e66de77470de2c50ead2bef0452952aJeff Brown * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 296ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 306ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 316ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown */ 3232cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown 3332cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "config.h" 3432cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "core/html/canvas/CanvasRenderingContext2D.h" 3532cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown 3632cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "bindings/core/v8/ExceptionMessages.h" 3732cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "bindings/core/v8/ExceptionState.h" 3832cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "bindings/core/v8/ExceptionStatePlaceholder.h" 3932cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "core/CSSPropertyNames.h" 4032cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "core/css/CSSFontSelector.h" 4132cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "core/css/StylePropertySet.h" 4232cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "core/css/parser/CSSParser.h" 4332cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "core/css/resolver/StyleResolver.h" 4432cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "core/dom/ExceptionCode.h" 45c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/dom/StyleEngine.h" 4632cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#include "core/events/Event.h" 47c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/fetch/ImageResource.h" 48c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/frame/ImageBitmap.h" 49c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/HTMLCanvasElement.h" 50c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/HTMLImageElement.h" 51c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/HTMLMediaElement.h" 52c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/HTMLVideoElement.h" 53c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/ImageData.h" 54c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/TextMetrics.h" 55c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/canvas/CanvasGradient.h" 56c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/canvas/CanvasPattern.h" 57c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/html/canvas/CanvasStyle.h" 5891c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brown#include "core/html/canvas/HitRegionOptions.h" 5991c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brown#include "core/html/canvas/Path2D.h" 60c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "core/rendering/RenderImage.h" 61e33348ba54cd68d6936cffd4507037c14d4b10c2Jeff Brown#include "core/rendering/RenderLayer.h" 62e33348ba54cd68d6936cffd4507037c14d4b10c2Jeff Brown#include "core/rendering/RenderTheme.h" 63e33348ba54cd68d6936cffd4507037c14d4b10c2Jeff Brown#include "platform/fonts/FontCache.h" 64e33348ba54cd68d6936cffd4507037c14d4b10c2Jeff Brown#include "platform/geometry/FloatQuad.h" 65e33348ba54cd68d6936cffd4507037c14d4b10c2Jeff Brown#include "platform/graphics/DrawLooperBuilder.h" 6691c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brown#include "platform/graphics/GraphicsContextStateSaver.h" 67e33348ba54cd68d6936cffd4507037c14d4b10c2Jeff Brown#include "platform/text/TextRun.h" 6891c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brown#include "wtf/CheckedArithmetic.h" 69e33348ba54cd68d6936cffd4507037c14d4b10c2Jeff Brown#include "wtf/MathExtras.h" 70c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "wtf/OwnPtr.h" 71c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown#include "wtf/Uint8ClampedArray.h" 72e33348ba54cd68d6936cffd4507037c14d4b10c2Jeff Brown#include "wtf/text/StringBuilder.h" 73600cba973fa6889728cd7ee9938ede12c80c005aJohn Spurlock 74c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brownnamespace blink { 7591c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brown 7691c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brownstatic const int defaultFontSize = 10; 77c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brownstatic const char defaultFontFamily[] = "sans-serif"; 78c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brownstatic const char defaultFont[] = "10px sans-serif"; 7991c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brownstatic const char inherit[] = "inherit"; 8091c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brownstatic const char rtl[] = "rtl"; 81c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brownstatic const char ltr[] = "ltr"; 82c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brownstatic const double TryRestoreContextInterval = 0.5; 8391c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brownstatic const unsigned MaxTryRestoreContextAttempts = 4; 8491c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brown 850029c66203ab9ded4342976bf7a17bb63af8c44aJeff Brownstatic bool contextLostRestoredEventsEnabled() 8674e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright{ 8774e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled(); 8874e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright} 8974e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright 9074e4156e5c62392c37f4a70358de30dcfff4956fMichael WrightCanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, const Canvas2DContextAttributes* attrs, bool usesCSSCompatibilityParseMode) 9174e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright : CanvasRenderingContext(canvas) 9274e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode) 9374e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright , m_hasAlpha(!attrs || attrs->alpha()) 9474e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright , m_isContextLost(false) 9574e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright , m_contextRestorable(true) 9674e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright , m_storageMode(!attrs ? PersistentStorage : attrs->parsedStorage()) 9774e4156e5c62392c37f4a70358de30dcfff4956fMichael Wright , m_tryRestoreContextAttemptCount(0) 9821bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchContextLostEvent) 9921bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispatchContextRestoredEvent) 10021bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreContextEvent) 10121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown{ 10221bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown m_stateStack.append(adoptPtrWillBeNoop(new State())); 10321bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown} 10421bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 10521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brownvoid CanvasRenderingContext2D::unwindStateStack() 1060029c66203ab9ded4342976bf7a17bb63af8c44aJeff Brown{ 1070029c66203ab9ded4342976bf7a17bb63af8c44aJeff Brown if (size_t stackSize = m_stateStack.size()) { 1080029c66203ab9ded4342976bf7a17bb63af8c44aJeff Brown if (GraphicsContext* context = canvas()->existingDrawingContext()) { 1090029c66203ab9ded4342976bf7a17bb63af8c44aJeff Brown while (--stackSize) 1100029c66203ab9ded4342976bf7a17bb63af8c44aJeff Brown context->restore(); 1110029c66203ab9ded4342976bf7a17bb63af8c44aJeff Brown } 11232cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown } 11332cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown} 11432cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown 11532cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff BrownCanvasRenderingContext2D::~CanvasRenderingContext2D() 11632cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown{ 11732cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown} 11832cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown 11932cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brownvoid CanvasRenderingContext2D::validateStateStack() 12032cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown{ 12132cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#if ENABLE(ASSERT) 12232cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown GraphicsContext* context = canvas()->existingDrawingContext(); 12332cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown if (context && !context->contextDisabled()) 12432cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown ASSERT(context->saveCount() == m_stateStack.size()); 12532cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown#endif 12632cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown} 12792cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown 12892cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brownbool CanvasRenderingContext2D::isAccelerated() const 12992cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown{ 13092cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown if (!canvas()->hasImageBuffer()) 13192cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown return false; 13292cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown GraphicsContext* context = drawingContext(); 13392cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown return context && context->isAccelerated(); 13492cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown} 13592cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown 13692cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brownbool CanvasRenderingContext2D::isContextLost() const 13792cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown{ 13892cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown return m_isContextLost; 13992cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown} 14092cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brown 14192cc2d8dc35f2bdd1bb95ab24787066371064899Jeff Brownvoid CanvasRenderingContext2D::loseContext() 14232cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown{ 14332cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown if (m_isContextLost) 14432cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown return; 14532cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown m_isContextLost = true; 14632cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown m_dispatchContextLostEventTimer.startOneShot(0, FROM_HERE); 14732cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown} 14832cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown 14932cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brownvoid CanvasRenderingContext2D::restoreContext() 1500029c66203ab9ded4342976bf7a17bb63af8c44aJeff Brown{ 15121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown if (!m_contextRestorable) 15221bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown return; 15321bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown // This code path is for restoring from an eviction 15421bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown // Restoring from surface failure is handled internally 15521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown ASSERT(m_isContextLost && !canvas()->hasImageBuffer()); 15621bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 15721bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown if (canvas()->buffer()) { 15821bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown if (contextLostRestoredEventsEnabled()) { 15921bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown m_dispatchContextRestoredEventTimer.startOneShot(0, FROM_HERE); 16021bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown } else { 16121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown // legacy synchronous context restoration. 16221bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown reset(); 16321bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown m_isContextLost = false; 16421bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown } 16521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown } 16621bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown} 16721bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 16821bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brownvoid CanvasRenderingContext2D::trace(Visitor* visitor) 16921bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown{ 17021bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown#if ENABLE(OILPAN) 17121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown visitor->trace(m_stateStack); 17221bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown visitor->trace(m_fetchedFonts); 1734e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown visitor->trace(m_hitRegionManager); 174b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown#endif 175b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown CanvasRenderingContext::trace(visitor); 176b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown} 177b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown 178b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brownvoid CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingContext2D>*) 179b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown{ 180b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown if (contextLostRestoredEventsEnabled()) { 181b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames::contextlost); 182b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown canvas()->dispatchEvent(event); 183b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown if (event->defaultPrevented()) { 184b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown m_contextRestorable = false; 185b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown } 186b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown } 1874e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown 188b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown // If an image buffer is present, it means the context was not lost due to 189b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown // an eviction, but rather due to a surface failure (gpu context lost?) 190b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown if (m_contextRestorable && canvas()->hasImageBuffer()) { 191b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown m_tryRestoreContextAttemptCount = 0; 192b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown m_tryRestoreContextEventTimer.startRepeating(TryRestoreContextInterval, FROM_HERE); 193b11499d2db0ba9782363ec6bf714b583e8585212Jeff Brown } 1944e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown} 1954e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown 1964e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brownvoid CanvasRenderingContext2D::tryRestoreContextEvent(Timer<CanvasRenderingContext2D>* timer) 1974e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown{ 19832cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown if (!m_isContextLost) { 199c3672cd3f7e2bd87d6de9dada499de82b62fae84Wale Ogunwale // Canvas was already restored (possibly thanks to a resize), so stop trying. 200c3672cd3f7e2bd87d6de9dada499de82b62fae84Wale Ogunwale m_tryRestoreContextEventTimer.stop(); 201c3672cd3f7e2bd87d6de9dada499de82b62fae84Wale Ogunwale return; 202c3672cd3f7e2bd87d6de9dada499de82b62fae84Wale Ogunwale } 203c3672cd3f7e2bd87d6de9dada499de82b62fae84Wale Ogunwale if (canvas()->hasImageBuffer() && canvas()->buffer()->restoreSurface()) { 204c3672cd3f7e2bd87d6de9dada499de82b62fae84Wale Ogunwale m_tryRestoreContextEventTimer.stop(); 205c3672cd3f7e2bd87d6de9dada499de82b62fae84Wale Ogunwale dispatchContextRestoredEvent(0); 20632cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown } 20732cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown 20832cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown if (++m_tryRestoreContextAttemptCount > MaxTryRestoreContextAttempts) 20932cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown canvas()->discardImageBuffer(); 21032cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown 21132cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown if (!canvas()->hasImageBuffer()) { 21232cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown // final attempt: allocate a brand new image buffer instead of restoring 21332cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown timer->stop(); 21432cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown if (canvas()->buffer()) 21532cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown dispatchContextRestoredEvent(0); 21632cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown } 21732cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown} 21832cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown 21932cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brownvoid CanvasRenderingContext2D::dispatchContextRestoredEvent(Timer<CanvasRenderingContext2D>*) 22032cbc3855c2a971aa5a801fd339fb6a37db91a1aJeff Brown{ 221692065128e66de77470de2c50ead2bef0452952aJeff Brown if (!m_isContextLost) 2226ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown return; 2236ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown reset(); 22491c69ab01539f7ba28708f41ec1835cc2920d0a0Jeff Brown m_isContextLost = false; 2256ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown if (contextLostRestoredEventsEnabled()) { 2266ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown RefPtrWillBeRawPtr<Event> event(Event::create(EventTypeNames::contextrestored)); 2276ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown canvas()->dispatchEvent(event); 2286ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown } 2296ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown} 2306ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown 2316ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brownvoid CanvasRenderingContext2D::reset() 2326ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown{ 2336ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown validateStateStack(); 2346ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown unwindStateStack(); 2356ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown m_stateStack.resize(1); 2366ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown m_stateStack.first() = adoptPtrWillBeNoop(new State()); 2376ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown m_path.clear(); 2386ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown validateStateStack(); 2396ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown} 2406ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown 2416ec402b5ae33c8927694d8522b4cc6a5c8ba974eJeff Brown// Important: Several of these properties are also stored in GraphicsContext's 242c5ed5910c9ef066cec6a13bbb404ec57b1e92637Jeff Brown// StrokeData. The default values that StrokeData uses may not the same values 243// that the canvas 2d spec specifies. Make sure to sync the initial state of the 244// GraphicsContext in HTMLCanvasElement::createImageBuffer()! 245CanvasRenderingContext2D::State::State() 246 : m_unrealizedSaveCount(0) 247 , m_strokeStyle(CanvasStyle::createFromRGBA(Color::black)) 248 , m_fillStyle(CanvasStyle::createFromRGBA(Color::black)) 249 , m_lineWidth(1) 250 , m_lineCap(ButtCap) 251 , m_lineJoin(MiterJoin) 252 , m_miterLimit(10) 253 , m_shadowBlur(0) 254 , m_shadowColor(Color::transparent) 255 , m_globalAlpha(1) 256 , m_globalComposite(CompositeSourceOver) 257 , m_globalBlend(blink::WebBlendModeNormal) 258 , m_invertibleCTM(true) 259 , m_lineDashOffset(0) 260 , m_imageSmoothingEnabled(true) 261 , m_textAlign(StartTextAlign) 262 , m_textBaseline(AlphabeticTextBaseline) 263 , m_direction(DirectionInherit) 264 , m_unparsedFont(defaultFont) 265 , m_realizedFont(false) 266 , m_hasClip(false) 267{ 268} 269 270CanvasRenderingContext2D::State::State(const State& other) 271 : CSSFontSelectorClient() 272 , m_unrealizedSaveCount(other.m_unrealizedSaveCount) 273 , m_unparsedStrokeColor(other.m_unparsedStrokeColor) 274 , m_unparsedFillColor(other.m_unparsedFillColor) 275 , m_strokeStyle(other.m_strokeStyle) 276 , m_fillStyle(other.m_fillStyle) 277 , m_lineWidth(other.m_lineWidth) 278 , m_lineCap(other.m_lineCap) 279 , m_lineJoin(other.m_lineJoin) 280 , m_miterLimit(other.m_miterLimit) 281 , m_shadowOffset(other.m_shadowOffset) 282 , m_shadowBlur(other.m_shadowBlur) 283 , m_shadowColor(other.m_shadowColor) 284 , m_globalAlpha(other.m_globalAlpha) 285 , m_globalComposite(other.m_globalComposite) 286 , m_globalBlend(other.m_globalBlend) 287 , m_transform(other.m_transform) 288 , m_invertibleCTM(other.m_invertibleCTM) 289 , m_lineDashOffset(other.m_lineDashOffset) 290 , m_imageSmoothingEnabled(other.m_imageSmoothingEnabled) 291 , m_textAlign(other.m_textAlign) 292 , m_textBaseline(other.m_textBaseline) 293 , m_direction(other.m_direction) 294 , m_unparsedFont(other.m_unparsedFont) 295 , m_font(other.m_font) 296 , m_realizedFont(other.m_realizedFont) 297 , m_hasClip(other.m_hasClip) 298{ 299 if (m_realizedFont) 300 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalidationCallbacks(this); 301} 302 303CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other) 304{ 305 if (this == &other) 306 return *this; 307 308#if !ENABLE(OILPAN) 309 if (m_realizedFont) 310 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInvalidationCallbacks(this); 311#endif 312 313 m_unrealizedSaveCount = other.m_unrealizedSaveCount; 314 m_unparsedStrokeColor = other.m_unparsedStrokeColor; 315 m_unparsedFillColor = other.m_unparsedFillColor; 316 m_strokeStyle = other.m_strokeStyle; 317 m_fillStyle = other.m_fillStyle; 318 m_lineWidth = other.m_lineWidth; 319 m_lineCap = other.m_lineCap; 320 m_lineJoin = other.m_lineJoin; 321 m_miterLimit = other.m_miterLimit; 322 m_shadowOffset = other.m_shadowOffset; 323 m_shadowBlur = other.m_shadowBlur; 324 m_shadowColor = other.m_shadowColor; 325 m_globalAlpha = other.m_globalAlpha; 326 m_globalComposite = other.m_globalComposite; 327 m_globalBlend = other.m_globalBlend; 328 m_transform = other.m_transform; 329 m_invertibleCTM = other.m_invertibleCTM; 330 m_imageSmoothingEnabled = other.m_imageSmoothingEnabled; 331 m_textAlign = other.m_textAlign; 332 m_textBaseline = other.m_textBaseline; 333 m_direction = other.m_direction; 334 m_unparsedFont = other.m_unparsedFont; 335 m_font = other.m_font; 336 m_realizedFont = other.m_realizedFont; 337 m_hasClip = other.m_hasClip; 338 339 if (m_realizedFont) 340 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalidationCallbacks(this); 341 342 return *this; 343} 344 345CanvasRenderingContext2D::State::~State() 346{ 347#if !ENABLE(OILPAN) 348 if (m_realizedFont) 349 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInvalidationCallbacks(this); 350#endif 351} 352 353void CanvasRenderingContext2D::State::fontsNeedUpdate(CSSFontSelector* fontSelector) 354{ 355 ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector()); 356 ASSERT(m_realizedFont); 357 358 m_font.update(fontSelector); 359} 360 361void CanvasRenderingContext2D::State::trace(Visitor* visitor) 362{ 363 visitor->trace(m_strokeStyle); 364 visitor->trace(m_fillStyle); 365 CSSFontSelectorClient::trace(visitor); 366} 367 368void CanvasRenderingContext2D::realizeSaves(GraphicsContext* context) 369{ 370 validateStateStack(); 371 if (state().m_unrealizedSaveCount) { 372 ASSERT(m_stateStack.size() >= 1); 373 // Reduce the current state's unrealized count by one now, 374 // to reflect the fact we are saving one state. 375 m_stateStack.last()->m_unrealizedSaveCount--; 376 m_stateStack.append(adoptPtrWillBeNoop(new State(state()))); 377 // Set the new state's unrealized count to 0, because it has no outstanding saves. 378 // We need to do this explicitly because the copy constructor and operator= used 379 // by the Vector operations copy the unrealized count from the previous state (in 380 // turn necessary to support correct resizing and unwinding of the stack). 381 m_stateStack.last()->m_unrealizedSaveCount = 0; 382 if (!context) 383 context = drawingContext(); 384 if (context) 385 context->save(); 386 validateStateStack(); 387 } 388} 389 390void CanvasRenderingContext2D::restore() 391{ 392 validateStateStack(); 393 if (state().m_unrealizedSaveCount) { 394 // We never realized the save, so just record that it was unnecessary. 395 --m_stateStack.last()->m_unrealizedSaveCount; 396 return; 397 } 398 ASSERT(m_stateStack.size() >= 1); 399 if (m_stateStack.size() <= 1) 400 return; 401 m_path.transform(state().m_transform); 402 m_stateStack.removeLast(); 403 m_path.transform(state().m_transform.inverse()); 404 GraphicsContext* c = drawingContext(); 405 if (c) 406 c->restore(); 407 validateStateStack(); 408} 409 410CanvasStyle* CanvasRenderingContext2D::strokeStyle() const 411{ 412 return state().m_strokeStyle.get(); 413} 414 415void CanvasRenderingContext2D::setStrokeStyle(PassRefPtrWillBeRawPtr<CanvasStyle> prpStyle) 416{ 417 RefPtrWillBeRawPtr<CanvasStyle> style = prpStyle; 418 419 if (!style) 420 return; 421 422 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style)) 423 return; 424 425 if (style->isCurrentColor()) { 426 if (style->hasOverrideAlpha()) 427 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha())); 428 else 429 style = CanvasStyle::createFromRGBA(currentColor(canvas())); 430 } else if (canvas()->originClean() && style->canvasPattern() && !style->canvasPattern()->originClean()) { 431 canvas()->setOriginTainted(); 432 } 433 434 GraphicsContext* c = drawingContext(); 435 realizeSaves(c); 436 modifiableState().m_strokeStyle = style.release(); 437 if (!c) 438 return; 439 state().m_strokeStyle->applyStrokeColor(c); 440 modifiableState().m_unparsedStrokeColor = String(); 441} 442 443CanvasStyle* CanvasRenderingContext2D::fillStyle() const 444{ 445 return state().m_fillStyle.get(); 446} 447 448void CanvasRenderingContext2D::setFillStyle(PassRefPtrWillBeRawPtr<CanvasStyle> prpStyle) 449{ 450 RefPtrWillBeRawPtr<CanvasStyle> style = prpStyle; 451 452 if (!style) 453 return; 454 455 if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style)) 456 return; 457 458 if (style->isCurrentColor()) { 459 if (style->hasOverrideAlpha()) 460 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha())); 461 else 462 style = CanvasStyle::createFromRGBA(currentColor(canvas())); 463 } else if (canvas()->originClean() && style->canvasPattern() && !style->canvasPattern()->originClean()) { 464 canvas()->setOriginTainted(); 465 } 466 467 GraphicsContext* c = drawingContext(); 468 realizeSaves(c); 469 modifiableState().m_fillStyle = style.release(); 470 if (!c) 471 return; 472 state().m_fillStyle->applyFillColor(c); 473 modifiableState().m_unparsedFillColor = String(); 474} 475 476float CanvasRenderingContext2D::lineWidth() const 477{ 478 return state().m_lineWidth; 479} 480 481void CanvasRenderingContext2D::setLineWidth(float width) 482{ 483 if (!(std::isfinite(width) && width > 0)) 484 return; 485 if (state().m_lineWidth == width) 486 return; 487 GraphicsContext* c = drawingContext(); 488 realizeSaves(c); 489 modifiableState().m_lineWidth = width; 490 if (!c) 491 return; 492 c->setStrokeThickness(width); 493} 494 495String CanvasRenderingContext2D::lineCap() const 496{ 497 return lineCapName(state().m_lineCap); 498} 499 500void CanvasRenderingContext2D::setLineCap(const String& s) 501{ 502 LineCap cap; 503 if (!parseLineCap(s, cap)) 504 return; 505 if (state().m_lineCap == cap) 506 return; 507 GraphicsContext* c = drawingContext(); 508 realizeSaves(c); 509 modifiableState().m_lineCap = cap; 510 if (!c) 511 return; 512 c->setLineCap(cap); 513} 514 515String CanvasRenderingContext2D::lineJoin() const 516{ 517 return lineJoinName(state().m_lineJoin); 518} 519 520void CanvasRenderingContext2D::setLineJoin(const String& s) 521{ 522 LineJoin join; 523 if (!parseLineJoin(s, join)) 524 return; 525 if (state().m_lineJoin == join) 526 return; 527 GraphicsContext* c = drawingContext(); 528 realizeSaves(c); 529 modifiableState().m_lineJoin = join; 530 if (!c) 531 return; 532 c->setLineJoin(join); 533} 534 535float CanvasRenderingContext2D::miterLimit() const 536{ 537 return state().m_miterLimit; 538} 539 540void CanvasRenderingContext2D::setMiterLimit(float limit) 541{ 542 if (!(std::isfinite(limit) && limit > 0)) 543 return; 544 if (state().m_miterLimit == limit) 545 return; 546 GraphicsContext* c = drawingContext(); 547 realizeSaves(c); 548 modifiableState().m_miterLimit = limit; 549 if (!c) 550 return; 551 c->setMiterLimit(limit); 552} 553 554float CanvasRenderingContext2D::shadowOffsetX() const 555{ 556 return state().m_shadowOffset.width(); 557} 558 559void CanvasRenderingContext2D::setShadowOffsetX(float x) 560{ 561 if (!std::isfinite(x)) 562 return; 563 if (state().m_shadowOffset.width() == x) 564 return; 565 realizeSaves(0); 566 modifiableState().m_shadowOffset.setWidth(x); 567 applyShadow(); 568} 569 570float CanvasRenderingContext2D::shadowOffsetY() const 571{ 572 return state().m_shadowOffset.height(); 573} 574 575void CanvasRenderingContext2D::setShadowOffsetY(float y) 576{ 577 if (!std::isfinite(y)) 578 return; 579 if (state().m_shadowOffset.height() == y) 580 return; 581 realizeSaves(0); 582 modifiableState().m_shadowOffset.setHeight(y); 583 applyShadow(); 584} 585 586float CanvasRenderingContext2D::shadowBlur() const 587{ 588 return state().m_shadowBlur; 589} 590 591void CanvasRenderingContext2D::setShadowBlur(float blur) 592{ 593 if (!(std::isfinite(blur) && blur >= 0)) 594 return; 595 if (state().m_shadowBlur == blur) 596 return; 597 realizeSaves(0); 598 modifiableState().m_shadowBlur = blur; 599 applyShadow(); 600} 601 602String CanvasRenderingContext2D::shadowColor() const 603{ 604 return Color(state().m_shadowColor).serialized(); 605} 606 607void CanvasRenderingContext2D::setShadowColor(const String& color) 608{ 609 RGBA32 rgba; 610 if (!parseColorOrCurrentColor(rgba, color, canvas())) 611 return; 612 if (state().m_shadowColor == rgba) 613 return; 614 realizeSaves(0); 615 modifiableState().m_shadowColor = rgba; 616 applyShadow(); 617} 618 619const Vector<float>& CanvasRenderingContext2D::getLineDash() const 620{ 621 return state().m_lineDash; 622} 623 624static bool lineDashSequenceIsValid(const Vector<float>& dash) 625{ 626 for (size_t i = 0; i < dash.size(); i++) { 627 if (!std::isfinite(dash[i]) || dash[i] < 0) 628 return false; 629 } 630 return true; 631} 632 633void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash) 634{ 635 if (!lineDashSequenceIsValid(dash)) 636 return; 637 638 realizeSaves(0); 639 modifiableState().m_lineDash = dash; 640 // Spec requires the concatenation of two copies the dash list when the 641 // number of elements is odd 642 if (dash.size() % 2) 643 modifiableState().m_lineDash.appendVector(dash); 644 645 applyLineDash(); 646} 647 648float CanvasRenderingContext2D::lineDashOffset() const 649{ 650 return state().m_lineDashOffset; 651} 652 653void CanvasRenderingContext2D::setLineDashOffset(float offset) 654{ 655 if (!std::isfinite(offset) || state().m_lineDashOffset == offset) 656 return; 657 658 realizeSaves(0); 659 modifiableState().m_lineDashOffset = offset; 660 applyLineDash(); 661} 662 663void CanvasRenderingContext2D::applyLineDash() const 664{ 665 GraphicsContext* c = drawingContext(); 666 if (!c) 667 return; 668 DashArray convertedLineDash(state().m_lineDash.size()); 669 for (size_t i = 0; i < state().m_lineDash.size(); ++i) 670 convertedLineDash[i] = static_cast<DashArrayElement>(state().m_lineDash[i]); 671 c->setLineDash(convertedLineDash, state().m_lineDashOffset); 672} 673 674float CanvasRenderingContext2D::globalAlpha() const 675{ 676 return state().m_globalAlpha; 677} 678 679void CanvasRenderingContext2D::setGlobalAlpha(float alpha) 680{ 681 if (!(alpha >= 0 && alpha <= 1)) 682 return; 683 if (state().m_globalAlpha == alpha) 684 return; 685 GraphicsContext* c = drawingContext(); 686 realizeSaves(c); 687 modifiableState().m_globalAlpha = alpha; 688 if (!c) 689 return; 690 c->setAlphaAsFloat(alpha); 691} 692 693String CanvasRenderingContext2D::globalCompositeOperation() const 694{ 695 return compositeOperatorName(state().m_globalComposite, state().m_globalBlend); 696} 697 698void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation) 699{ 700 CompositeOperator op = CompositeSourceOver; 701 blink::WebBlendMode blendMode = blink::WebBlendModeNormal; 702 if (!parseCompositeAndBlendOperator(operation, op, blendMode)) 703 return; 704 if ((state().m_globalComposite == op) && (state().m_globalBlend == blendMode)) 705 return; 706 GraphicsContext* c = drawingContext(); 707 realizeSaves(c); 708 modifiableState().m_globalComposite = op; 709 modifiableState().m_globalBlend = blendMode; 710 if (!c) 711 return; 712 c->setCompositeOperation(op, blendMode); 713} 714 715void CanvasRenderingContext2D::setCurrentTransform(PassRefPtr<SVGMatrixTearOff> passMatrixTearOff) 716{ 717 RefPtr<SVGMatrixTearOff> matrixTearOff = passMatrixTearOff; 718 const AffineTransform& transform = matrixTearOff->value(); 719 setTransform(transform.a(), transform.b(), transform.c(), transform.d(), transform.e(), transform.f()); 720} 721 722void CanvasRenderingContext2D::scale(float sx, float sy) 723{ 724 GraphicsContext* c = drawingContext(); 725 if (!c) 726 return; 727 if (!state().m_invertibleCTM) 728 return; 729 730 if (!std::isfinite(sx) | !std::isfinite(sy)) 731 return; 732 733 AffineTransform newTransform = state().m_transform; 734 newTransform.scaleNonUniform(sx, sy); 735 if (state().m_transform == newTransform) 736 return; 737 738 realizeSaves(c); 739 740 if (!newTransform.isInvertible()) { 741 modifiableState().m_invertibleCTM = false; 742 return; 743 } 744 745 modifiableState().m_transform = newTransform; 746 c->scale(sx, sy); 747 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy)); 748} 749 750void CanvasRenderingContext2D::rotate(float angleInRadians) 751{ 752 GraphicsContext* c = drawingContext(); 753 if (!c) 754 return; 755 if (!state().m_invertibleCTM) 756 return; 757 758 if (!std::isfinite(angleInRadians)) 759 return; 760 761 AffineTransform newTransform = state().m_transform; 762 newTransform.rotateRadians(angleInRadians); 763 if (state().m_transform == newTransform) 764 return; 765 766 realizeSaves(c); 767 768 if (!newTransform.isInvertible()) { 769 modifiableState().m_invertibleCTM = false; 770 return; 771 } 772 773 modifiableState().m_transform = newTransform; 774 c->rotate(angleInRadians); 775 m_path.transform(AffineTransform().rotateRadians(-angleInRadians)); 776} 777 778void CanvasRenderingContext2D::translate(float tx, float ty) 779{ 780 GraphicsContext* c = drawingContext(); 781 if (!c) 782 return; 783 if (!state().m_invertibleCTM) 784 return; 785 786 if (!std::isfinite(tx) | !std::isfinite(ty)) 787 return; 788 789 AffineTransform newTransform = state().m_transform; 790 newTransform.translate(tx, ty); 791 if (state().m_transform == newTransform) 792 return; 793 794 realizeSaves(c); 795 796 if (!newTransform.isInvertible()) { 797 modifiableState().m_invertibleCTM = false; 798 return; 799 } 800 801 modifiableState().m_transform = newTransform; 802 c->translate(tx, ty); 803 m_path.transform(AffineTransform().translate(-tx, -ty)); 804} 805 806void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy) 807{ 808 GraphicsContext* c = drawingContext(); 809 if (!c) 810 return; 811 if (!state().m_invertibleCTM) 812 return; 813 814 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy)) 815 return; 816 817 AffineTransform transform(m11, m12, m21, m22, dx, dy); 818 AffineTransform newTransform = state().m_transform * transform; 819 if (state().m_transform == newTransform) 820 return; 821 822 realizeSaves(c); 823 824 modifiableState().m_transform = newTransform; 825 if (!newTransform.isInvertible()) { 826 modifiableState().m_invertibleCTM = false; 827 return; 828 } 829 830 c->concatCTM(transform); 831 m_path.transform(transform.inverse()); 832} 833 834void CanvasRenderingContext2D::resetTransform() 835{ 836 GraphicsContext* c = drawingContext(); 837 if (!c) 838 return; 839 840 AffineTransform ctm = state().m_transform; 841 bool invertibleCTM = state().m_invertibleCTM; 842 // It is possible that CTM is identity while CTM is not invertible. 843 // When CTM becomes non-invertible, realizeSaves() can make CTM identity. 844 if (ctm.isIdentity() && invertibleCTM) 845 return; 846 847 realizeSaves(c); 848 // resetTransform() resolves the non-invertible CTM state. 849 modifiableState().m_transform.makeIdentity(); 850 modifiableState().m_invertibleCTM = true; 851 c->setCTM(canvas()->baseTransform()); 852 853 if (invertibleCTM) 854 m_path.transform(ctm); 855 // When else, do nothing because all transform methods didn't update m_path when CTM became non-invertible. 856 // It means that resetTransform() restores m_path just before CTM became non-invertible. 857} 858 859void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy) 860{ 861 GraphicsContext* c = drawingContext(); 862 if (!c) 863 return; 864 865 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy)) 866 return; 867 868 resetTransform(); 869 transform(m11, m12, m21, m22, dx, dy); 870} 871 872void CanvasRenderingContext2D::setStrokeColor(const String& color) 873{ 874 if (color == state().m_unparsedStrokeColor) 875 return; 876 realizeSaves(0); 877 setStrokeStyle(CanvasStyle::createFromString(color)); 878 modifiableState().m_unparsedStrokeColor = color; 879} 880 881void CanvasRenderingContext2D::setStrokeColor(float grayLevel) 882{ 883 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) 884 return; 885 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f)); 886} 887 888void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha) 889{ 890 setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); 891} 892 893void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha) 894{ 895 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) 896 return; 897 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha)); 898} 899 900void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a) 901{ 902 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b, a)) 903 return; 904 setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a)); 905} 906 907void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a) 908{ 909 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a)) 910 return; 911 setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a)); 912} 913 914void CanvasRenderingContext2D::setFillColor(const String& color) 915{ 916 if (color == state().m_unparsedFillColor) 917 return; 918 realizeSaves(0); 919 setFillStyle(CanvasStyle::createFromString(color)); 920 modifiableState().m_unparsedFillColor = color; 921} 922 923void CanvasRenderingContext2D::setFillColor(float grayLevel) 924{ 925 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) 926 return; 927 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f)); 928} 929 930void CanvasRenderingContext2D::setFillColor(const String& color, float alpha) 931{ 932 setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); 933} 934 935void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha) 936{ 937 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) 938 return; 939 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha)); 940} 941 942void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a) 943{ 944 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a)) 945 return; 946 setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a)); 947} 948 949void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a) 950{ 951 if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k, a)) 952 return; 953 setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a)); 954} 955 956void CanvasRenderingContext2D::beginPath() 957{ 958 m_path.clear(); 959} 960 961static bool validateRectForCanvas(float& x, float& y, float& width, float& height) 962{ 963 if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height)) 964 return false; 965 966 if (!width && !height) 967 return false; 968 969 if (width < 0) { 970 width = -width; 971 x -= width; 972 } 973 974 if (height < 0) { 975 height = -height; 976 y -= height; 977 } 978 979 return true; 980} 981 982static bool isFullCanvasCompositeMode(CompositeOperator op) 983{ 984 // See 4.8.11.1.3 Compositing 985 // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already 986 // implement the specification's behavior. 987 return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop; 988} 989 990static WindRule parseWinding(const String& windingRuleString) 991{ 992 if (windingRuleString == "nonzero") 993 return RULE_NONZERO; 994 if (windingRuleString == "evenodd") 995 return RULE_EVENODD; 996 997 ASSERT_NOT_REACHED(); 998 return RULE_EVENODD; 999} 1000 1001void CanvasRenderingContext2D::fillInternal(const Path& path, const String& windingRuleString) 1002{ 1003 if (path.isEmpty()) { 1004 return; 1005 } 1006 GraphicsContext* c = drawingContext(); 1007 if (!c) { 1008 return; 1009 } 1010 if (!state().m_invertibleCTM) { 1011 return; 1012 } 1013 FloatRect clipBounds; 1014 if (!c->getTransformedClipBounds(&clipBounds)) { 1015 return; 1016 } 1017 1018 // If gradient size is zero, then paint nothing. 1019 Gradient* gradient = c->fillGradient(); 1020 if (gradient && gradient->isZeroSize()) { 1021 return; 1022 } 1023 1024 WindRule windRule = c->fillRule(); 1025 c->setFillRule(parseWinding(windingRuleString)); 1026 1027 if (isFullCanvasCompositeMode(state().m_globalComposite)) { 1028 fullCanvasCompositedFill(path); 1029 didDraw(clipBounds); 1030 } else if (state().m_globalComposite == CompositeCopy) { 1031 clearCanvas(); 1032 c->fillPath(path); 1033 didDraw(clipBounds); 1034 } else { 1035 FloatRect dirtyRect; 1036 if (computeDirtyRect(path.boundingRect(), clipBounds, &dirtyRect)) { 1037 c->fillPath(path); 1038 didDraw(dirtyRect); 1039 } 1040 } 1041 1042 c->setFillRule(windRule); 1043} 1044 1045void CanvasRenderingContext2D::fill(const String& windingRuleString) 1046{ 1047 fillInternal(m_path, windingRuleString); 1048} 1049 1050void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleString) 1051{ 1052 fillInternal(domPath->path(), windingRuleString); 1053} 1054 1055void CanvasRenderingContext2D::strokeInternal(const Path& path) 1056{ 1057 if (path.isEmpty()) { 1058 return; 1059 } 1060 GraphicsContext* c = drawingContext(); 1061 if (!c) { 1062 return; 1063 } 1064 if (!state().m_invertibleCTM) { 1065 return; 1066 } 1067 FloatRect clipBounds; 1068 if (!c->getTransformedClipBounds(&clipBounds)) 1069 return; 1070 1071 // If gradient size is zero, then paint nothing. 1072 Gradient* gradient = c->strokeGradient(); 1073 if (gradient && gradient->isZeroSize()) { 1074 return; 1075 } 1076 1077 if (isFullCanvasCompositeMode(state().m_globalComposite)) { 1078 fullCanvasCompositedStroke(path); 1079 didDraw(clipBounds); 1080 } else if (state().m_globalComposite == CompositeCopy) { 1081 clearCanvas(); 1082 c->strokePath(path); 1083 didDraw(clipBounds); 1084 } else { 1085 FloatRect bounds = path.boundingRect(); 1086 inflateStrokeRect(bounds); 1087 FloatRect dirtyRect; 1088 if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) { 1089 c->strokePath(path); 1090 didDraw(dirtyRect); 1091 } 1092 } 1093} 1094 1095void CanvasRenderingContext2D::stroke() 1096{ 1097 strokeInternal(m_path); 1098} 1099 1100void CanvasRenderingContext2D::stroke(Path2D* domPath) 1101{ 1102 strokeInternal(domPath->path()); 1103} 1104 1105void CanvasRenderingContext2D::clipInternal(const Path& path, const String& windingRuleString) 1106{ 1107 GraphicsContext* c = drawingContext(); 1108 if (!c) { 1109 return; 1110 } 1111 if (!state().m_invertibleCTM) { 1112 return; 1113 } 1114 1115 realizeSaves(c); 1116 c->canvasClip(path, parseWinding(windingRuleString)); 1117 modifiableState().m_hasClip = true; 1118} 1119 1120void CanvasRenderingContext2D::clip(const String& windingRuleString) 1121{ 1122 clipInternal(m_path, windingRuleString); 1123} 1124 1125void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleString) 1126{ 1127 clipInternal(domPath->path(), windingRuleString); 1128} 1129 1130bool CanvasRenderingContext2D::isPointInPath(const float x, const float y, const String& windingRuleString) 1131{ 1132 return isPointInPathInternal(m_path, x, y, windingRuleString); 1133} 1134 1135bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const float x, const float y, const String& windingRuleString) 1136{ 1137 return isPointInPathInternal(domPath->path(), x, y, windingRuleString); 1138} 1139 1140bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, const float x, const float y, const String& windingRuleString) 1141{ 1142 GraphicsContext* c = drawingContext(); 1143 if (!c) 1144 return false; 1145 if (!state().m_invertibleCTM) 1146 return false; 1147 1148 FloatPoint point(x, y); 1149 AffineTransform ctm = state().m_transform; 1150 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); 1151 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y())) 1152 return false; 1153 1154 return path.contains(transformedPoint, parseWinding(windingRuleString)); 1155} 1156 1157bool CanvasRenderingContext2D::isPointInStroke(const float x, const float y) 1158{ 1159 return isPointInStrokeInternal(m_path, x, y); 1160} 1161 1162bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const float x, const float y) 1163{ 1164 return isPointInStrokeInternal(domPath->path(), x, y); 1165} 1166 1167bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, const float x, const float y) 1168{ 1169 GraphicsContext* c = drawingContext(); 1170 if (!c) 1171 return false; 1172 if (!state().m_invertibleCTM) 1173 return false; 1174 1175 FloatPoint point(x, y); 1176 AffineTransform ctm = state().m_transform; 1177 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); 1178 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y())) 1179 return false; 1180 1181 StrokeData strokeData; 1182 strokeData.setThickness(lineWidth()); 1183 strokeData.setLineCap(getLineCap()); 1184 strokeData.setLineJoin(getLineJoin()); 1185 strokeData.setMiterLimit(miterLimit()); 1186 strokeData.setLineDash(getLineDash(), lineDashOffset()); 1187 return path.strokeContains(transformedPoint, strokeData); 1188} 1189 1190void CanvasRenderingContext2D::scrollPathIntoView() 1191{ 1192 scrollPathIntoViewInternal(m_path); 1193} 1194 1195void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d) 1196{ 1197 scrollPathIntoViewInternal(path2d->path()); 1198} 1199 1200void CanvasRenderingContext2D::scrollPathIntoViewInternal(const Path& path) 1201{ 1202 RenderObject* renderer = canvas()->renderer(); 1203 RenderBox* renderBox = canvas()->renderBox(); 1204 if (!renderer || !renderBox || !state().m_invertibleCTM || path.isEmpty()) 1205 return; 1206 1207 canvas()->document().updateLayoutIgnorePendingStylesheets(); 1208 1209 // Apply transformation and get the bounding rect 1210 Path transformedPath = path; 1211 transformedPath.transform(state().m_transform); 1212 FloatRect boundingRect = transformedPath.boundingRect(); 1213 1214 // Offset by the canvas rect 1215 LayoutRect pathRect(boundingRect); 1216 IntRect canvasRect = renderBox->absoluteContentBox(); 1217 pathRect.move(canvasRect.x(), canvasRect.y()); 1218 1219 renderer->scrollRectToVisible( 1220 pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopAlways); 1221 1222 // TODO: should implement "inform the user" that the caret and/or 1223 // selection the specified rectangle of the canvas. See http://crbug.com/357987 1224} 1225 1226void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height) 1227{ 1228 if (!validateRectForCanvas(x, y, width, height)) 1229 return; 1230 GraphicsContext* context = drawingContext(); 1231 if (!context) 1232 return; 1233 if (!state().m_invertibleCTM) 1234 return; 1235 FloatRect rect(x, y, width, height); 1236 1237 FloatRect dirtyRect; 1238 if (!computeDirtyRect(rect, &dirtyRect)) 1239 return; 1240 1241 bool saved = false; 1242 if (shouldDrawShadows()) { 1243 context->save(); 1244 saved = true; 1245 context->clearShadow(); 1246 } 1247 if (state().m_globalAlpha != 1) { 1248 if (!saved) { 1249 context->save(); 1250 saved = true; 1251 } 1252 context->setAlphaAsFloat(1); 1253 } 1254 if (state().m_globalComposite != CompositeSourceOver) { 1255 if (!saved) { 1256 context->save(); 1257 saved = true; 1258 } 1259 context->setCompositeOperation(CompositeSourceOver); 1260 } 1261 context->clearRect(rect); 1262 if (m_hitRegionManager) 1263 m_hitRegionManager->removeHitRegionsInRect(rect, state().m_transform); 1264 if (saved) 1265 context->restore(); 1266 1267 validateStateStack(); 1268 didDraw(dirtyRect); 1269} 1270 1271void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height) 1272{ 1273 if (!validateRectForCanvas(x, y, width, height)) 1274 return; 1275 1276 GraphicsContext* c = drawingContext(); 1277 if (!c) 1278 return; 1279 if (!state().m_invertibleCTM) 1280 return; 1281 FloatRect clipBounds; 1282 if (!c->getTransformedClipBounds(&clipBounds)) 1283 return; 1284 1285 // from the HTML5 Canvas spec: 1286 // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing 1287 // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing 1288 Gradient* gradient = c->fillGradient(); 1289 if (gradient && gradient->isZeroSize()) 1290 return; 1291 1292 FloatRect rect(x, y, width, height); 1293 if (rectContainsTransformedRect(rect, clipBounds)) { 1294 c->fillRect(rect); 1295 didDraw(clipBounds); 1296 } else if (isFullCanvasCompositeMode(state().m_globalComposite)) { 1297 fullCanvasCompositedFill(rect); 1298 didDraw(clipBounds); 1299 } else if (state().m_globalComposite == CompositeCopy) { 1300 clearCanvas(); 1301 c->fillRect(rect); 1302 didDraw(clipBounds); 1303 } else { 1304 FloatRect dirtyRect; 1305 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) { 1306 c->fillRect(rect); 1307 didDraw(dirtyRect); 1308 } 1309 } 1310} 1311 1312void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height) 1313{ 1314 if (!validateRectForCanvas(x, y, width, height)) 1315 return; 1316 1317 if (!(state().m_lineWidth >= 0)) 1318 return; 1319 1320 GraphicsContext* c = drawingContext(); 1321 if (!c) 1322 return; 1323 if (!state().m_invertibleCTM) 1324 return; 1325 FloatRect clipBounds; 1326 if (!c->getTransformedClipBounds(&clipBounds)) 1327 return; 1328 1329 // If gradient size is zero, then paint nothing. 1330 Gradient* gradient = c->strokeGradient(); 1331 if (gradient && gradient->isZeroSize()) 1332 return; 1333 1334 FloatRect rect(x, y, width, height); 1335 1336 if (isFullCanvasCompositeMode(state().m_globalComposite)) { 1337 fullCanvasCompositedStroke(rect); 1338 didDraw(clipBounds); 1339 } else if (state().m_globalComposite == CompositeCopy) { 1340 clearCanvas(); 1341 c->strokeRect(rect); 1342 didDraw(clipBounds); 1343 } else { 1344 FloatRect boundingRect = rect; 1345 boundingRect.inflate(state().m_lineWidth / 2); 1346 FloatRect dirtyRect; 1347 if (computeDirtyRect(boundingRect, clipBounds, &dirtyRect)) { 1348 c->strokeRect(rect); 1349 didDraw(dirtyRect); 1350 } 1351 } 1352} 1353 1354void CanvasRenderingContext2D::setShadow(float width, float height, float blur) 1355{ 1356 setShadow(FloatSize(width, height), blur, Color::transparent); 1357} 1358 1359void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color) 1360{ 1361 RGBA32 rgba; 1362 if (!parseColorOrCurrentColor(rgba, color, canvas())) 1363 return; 1364 setShadow(FloatSize(width, height), blur, rgba); 1365} 1366 1367void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel) 1368{ 1369 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1)); 1370} 1371 1372void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha) 1373{ 1374 RGBA32 rgba; 1375 if (!parseColorOrCurrentColor(rgba, color, canvas())) 1376 return; 1377 setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(rgba, alpha)); 1378} 1379 1380void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha) 1381{ 1382 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha)); 1383} 1384 1385void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a) 1386{ 1387 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(r, g, b, a)); 1388} 1389 1390void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a) 1391{ 1392 setShadow(FloatSize(width, height), blur, makeRGBAFromCMYKA(c, m, y, k, a)); 1393} 1394 1395void CanvasRenderingContext2D::clearShadow() 1396{ 1397 setShadow(FloatSize(), 0, Color::transparent); 1398} 1399 1400void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, RGBA32 color) 1401{ 1402 if (state().m_shadowOffset == offset && state().m_shadowBlur == blur && state().m_shadowColor == color) 1403 return; 1404 bool wasDrawingShadows = shouldDrawShadows(); 1405 realizeSaves(0); 1406 modifiableState().m_shadowOffset = offset; 1407 modifiableState().m_shadowBlur = blur; 1408 modifiableState().m_shadowColor = color; 1409 if (!wasDrawingShadows && !shouldDrawShadows()) 1410 return; 1411 applyShadow(); 1412} 1413 1414void CanvasRenderingContext2D::applyShadow() 1415{ 1416 GraphicsContext* c = drawingContext(); 1417 if (!c) 1418 return; 1419 1420 if (shouldDrawShadows()) { 1421 c->setShadow(state().m_shadowOffset, state().m_shadowBlur, state().m_shadowColor, 1422 DrawLooperBuilder::ShadowIgnoresTransforms); 1423 } else { 1424 c->clearShadow(); 1425 } 1426} 1427 1428bool CanvasRenderingContext2D::shouldDrawShadows() const 1429{ 1430 return alphaChannel(state().m_shadowColor) && (state().m_shadowBlur || !state().m_shadowOffset.isZero()); 1431} 1432 1433static inline FloatRect normalizeRect(const FloatRect& rect) 1434{ 1435 return FloatRect(std::min(rect.x(), rect.maxX()), 1436 std::min(rect.y(), rect.maxY()), 1437 std::max(rect.width(), -rect.width()), 1438 std::max(rect.height(), -rect.height())); 1439} 1440 1441static inline void clipRectsToImageRect(const FloatRect& imageRect, FloatRect* srcRect, FloatRect* dstRect) 1442{ 1443 if (imageRect.contains(*srcRect)) 1444 return; 1445 1446 // Compute the src to dst transform 1447 FloatSize scale(dstRect->size().width() / srcRect->size().width(), dstRect->size().height() / srcRect->size().height()); 1448 FloatPoint scaledSrcLocation = srcRect->location(); 1449 scaledSrcLocation.scale(scale.width(), scale.height()); 1450 FloatSize offset = dstRect->location() - scaledSrcLocation; 1451 1452 srcRect->intersect(imageRect); 1453 1454 // To clip the destination rectangle in the same proportion, transform the clipped src rect 1455 *dstRect = *srcRect; 1456 dstRect->scale(scale.width(), scale.height()); 1457 dstRect->move(offset); 1458} 1459 1460void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, float x, float y, ExceptionState& exceptionState) 1461{ 1462 FloatSize destRectSize = imageSource->defaultDestinationSize(); 1463 drawImage(imageSource, x, y, destRectSize.width(), destRectSize.height(), exceptionState); 1464} 1465 1466void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, 1467 float x, float y, float width, float height, ExceptionState& exceptionState) 1468{ 1469 FloatSize sourceRectSize = imageSource->sourceSize(); 1470 drawImage(imageSource, 0, 0, sourceRectSize.width(), sourceRectSize.height(), x, y, width, height, exceptionState); 1471} 1472 1473void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, 1474 float sx, float sy, float sw, float sh, 1475 float dx, float dy, float dw, float dh, ExceptionState& exceptionState) 1476{ 1477 GraphicsContext* c = drawingContext(); // Do not exit yet if !c because we may need to throw exceptions first 1478 CompositeOperator op = c ? c->compositeOperation() : CompositeSourceOver; 1479 blink::WebBlendMode blendMode = c ? c->blendModeOperation() : blink::WebBlendModeNormal; 1480 drawImageInternal(imageSource, sx, sy, sw, sh, dx, dy, dw, dh, exceptionState, op, blendMode, c); 1481} 1482 1483void CanvasRenderingContext2D::drawImageInternal(CanvasImageSource* imageSource, 1484 float sx, float sy, float sw, float sh, 1485 float dx, float dy, float dw, float dh, ExceptionState& exceptionState, 1486 CompositeOperator op, blink::WebBlendMode blendMode, GraphicsContext* c) 1487{ 1488 RefPtr<Image> image; 1489 SourceImageStatus sourceImageStatus = InvalidSourceImageStatus; 1490 if (!imageSource->isVideoElement()) { 1491 SourceImageMode mode = canvas() == imageSource ? CopySourceImageIfVolatile : DontCopySourceImage; // Thunking for == 1492 image = imageSource->getSourceImageForCanvas(mode, &sourceImageStatus); 1493 if (sourceImageStatus == UndecodableSourceImageStatus) 1494 exceptionState.throwDOMException(InvalidStateError, "The HTMLImageElement provided is in the 'broken' state."); 1495 if (!image || !image->width() || !image->height()) 1496 return; 1497 } 1498 1499 if (!c) 1500 c = drawingContext(); 1501 if (!c) 1502 return; 1503 1504 if (!state().m_invertibleCTM) 1505 return; 1506 1507 if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dw) || !std::isfinite(dh) 1508 || !std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !std::isfinite(sh) 1509 || !dw || !dh || !sw || !sh) 1510 return; 1511 1512 FloatRect clipBounds; 1513 if (!c->getTransformedClipBounds(&clipBounds)) 1514 return; 1515 1516 FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh)); 1517 FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh)); 1518 1519 clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->sourceSize()), &srcRect, &dstRect); 1520 1521 imageSource->adjustDrawRects(&srcRect, &dstRect); 1522 1523 if (srcRect.isEmpty()) 1524 return; 1525 1526 FloatRect dirtyRect = clipBounds; 1527 if (imageSource->isVideoElement()) { 1528 // TODO(dshwang): unify video code into below code to composite correctly; crbug.com/407079 1529 drawVideo(static_cast<HTMLVideoElement*>(imageSource), srcRect, dstRect); 1530 computeDirtyRect(dstRect, clipBounds, &dirtyRect); 1531 } else { 1532 if (rectContainsTransformedRect(dstRect, clipBounds)) { 1533 c->drawImage(image.get(), dstRect, srcRect, op, blendMode); 1534 } else if (isFullCanvasCompositeMode(op)) { 1535 fullCanvasCompositedDrawImage(image.get(), dstRect, srcRect, op); 1536 } else if (op == CompositeCopy) { 1537 clearCanvas(); 1538 c->drawImage(image.get(), dstRect, srcRect, op, blendMode); 1539 } else { 1540 FloatRect dirtyRect; 1541 computeDirtyRect(dstRect, clipBounds, &dirtyRect); 1542 c->drawImage(image.get(), dstRect, srcRect, op, blendMode); 1543 } 1544 1545 if (sourceImageStatus == ExternalSourceImageStatus && isAccelerated() && canvas()->buffer()) 1546 canvas()->buffer()->flush(); 1547 } 1548 1549 if (canvas()->originClean() && wouldTaintOrigin(imageSource)) 1550 canvas()->setOriginTainted(); 1551 1552 didDraw(dirtyRect); 1553} 1554 1555void CanvasRenderingContext2D::drawVideo(HTMLVideoElement* video, FloatRect srcRect, FloatRect dstRect) 1556{ 1557 GraphicsContext* c = drawingContext(); 1558 GraphicsContextStateSaver stateSaver(*c); 1559 c->clip(dstRect); 1560 c->translate(dstRect.x(), dstRect.y()); 1561 c->scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()); 1562 c->translate(-srcRect.x(), -srcRect.y()); 1563 video->paintCurrentFrameInContext(c, IntRect(IntPoint(), IntSize(video->videoWidth(), video->videoHeight()))); 1564 stateSaver.restore(); 1565 validateStateStack(); 1566} 1567 1568void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image, 1569 float sx, float sy, float sw, float sh, 1570 float dx, float dy, float dw, float dh, 1571 const String& compositeOperation) 1572{ 1573 if (!image) 1574 return; 1575 CompositeOperator op; 1576 blink::WebBlendMode blendOp = blink::WebBlendModeNormal; 1577 if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != blink::WebBlendModeNormal) 1578 op = CompositeSourceOver; 1579 1580 drawImageInternal(image, sx, sy, sw, sh, dx, dy, dw, dh, IGNORE_EXCEPTION, op, blendOp); 1581} 1582 1583void CanvasRenderingContext2D::setAlpha(float alpha) 1584{ 1585 setGlobalAlpha(alpha); 1586} 1587 1588void CanvasRenderingContext2D::setCompositeOperation(const String& operation) 1589{ 1590 setGlobalCompositeOperation(operation); 1591} 1592 1593void CanvasRenderingContext2D::clearCanvas() 1594{ 1595 FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height()); 1596 GraphicsContext* c = drawingContext(); 1597 if (!c) 1598 return; 1599 1600 c->save(); 1601 c->setCTM(canvas()->baseTransform()); 1602 c->clearRect(canvasRect); 1603 c->restore(); 1604} 1605 1606bool CanvasRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect, const FloatRect& transformedRect) const 1607{ 1608 FloatQuad quad(rect); 1609 FloatQuad transformedQuad(transformedRect); 1610 return state().m_transform.mapQuad(quad).containsQuad(transformedQuad); 1611} 1612 1613static void drawImageToContext(Image* image, GraphicsContext* context, const FloatRect& dest, const FloatRect& src, CompositeOperator op) 1614{ 1615 context->drawImage(image, dest, src, op); 1616} 1617 1618template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op) 1619{ 1620 ASSERT(isFullCanvasCompositeMode(op)); 1621 1622 GraphicsContext* c = drawingContext(); 1623 c->beginLayer(1, op); 1624 drawImageToContext(image, c, dest, src, CompositeSourceOver); 1625 c->endLayer(); 1626} 1627 1628static void fillPrimitive(const FloatRect& rect, GraphicsContext* context) 1629{ 1630 context->fillRect(rect); 1631} 1632 1633static void fillPrimitive(const Path& path, GraphicsContext* context) 1634{ 1635 context->fillPath(path); 1636} 1637 1638template<class T> void CanvasRenderingContext2D::fullCanvasCompositedFill(const T& area) 1639{ 1640 ASSERT(isFullCanvasCompositeMode(state().m_globalComposite)); 1641 1642 GraphicsContext* c = drawingContext(); 1643 ASSERT(c); 1644 c->beginLayer(1, state().m_globalComposite); 1645 CompositeOperator previousOperator = c->compositeOperation(); 1646 c->setCompositeOperation(CompositeSourceOver); 1647 fillPrimitive(area, c); 1648 c->setCompositeOperation(previousOperator); 1649 c->endLayer(); 1650} 1651 1652static void strokePrimitive(const FloatRect& rect, GraphicsContext* context) 1653{ 1654 context->strokeRect(rect); 1655} 1656 1657static void strokePrimitive(const Path& path, GraphicsContext* context) 1658{ 1659 context->strokePath(path); 1660} 1661 1662template<class T> void CanvasRenderingContext2D::fullCanvasCompositedStroke(const T& area) 1663{ 1664 ASSERT(isFullCanvasCompositeMode(state().m_globalComposite)); 1665 1666 GraphicsContext* c = drawingContext(); 1667 ASSERT(c); 1668 c->beginLayer(1, state().m_globalComposite); 1669 CompositeOperator previousOperator = c->compositeOperation(); 1670 c->setCompositeOperation(CompositeSourceOver); 1671 strokePrimitive(area, c); 1672 c->setCompositeOperation(previousOperator); 1673 c->endLayer(); 1674} 1675 1676PassRefPtrWillBeRawPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1) 1677{ 1678 RefPtrWillBeRawPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1)); 1679 return gradient.release(); 1680} 1681 1682PassRefPtrWillBeRawPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionState& exceptionState) 1683{ 1684 if (r0 < 0 || r1 < 0) { 1685 exceptionState.throwDOMException(IndexSizeError, String::format("The %s provided is less than 0.", r0 < 0 ? "r0" : "r1")); 1686 return nullptr; 1687 } 1688 1689 RefPtrWillBeRawPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1); 1690 return gradient.release(); 1691} 1692 1693PassRefPtrWillBeRawPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(CanvasImageSource* imageSource, 1694 const String& repetitionType, ExceptionState& exceptionState) 1695{ 1696 Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetitionType, exceptionState); 1697 if (exceptionState.hadException()) 1698 return nullptr; 1699 1700 SourceImageStatus status; 1701 RefPtr<Image> imageForRendering = imageSource->getSourceImageForCanvas(CopySourceImageIfVolatile, &status); 1702 1703 switch (status) { 1704 case NormalSourceImageStatus: 1705 break; 1706 case ZeroSizeCanvasSourceImageStatus: 1707 exceptionState.throwDOMException(InvalidStateError, String::format("The canvas %s is 0.", imageSource->sourceSize().width() ? "height" : "width")); 1708 return nullptr; 1709 case UndecodableSourceImageStatus: 1710 exceptionState.throwDOMException(InvalidStateError, "Source image is in the 'broken' state."); 1711 return nullptr; 1712 case InvalidSourceImageStatus: 1713 imageForRendering = Image::nullImage(); 1714 break; 1715 case IncompleteSourceImageStatus: 1716 return nullptr; 1717 default: 1718 case ExternalSourceImageStatus: // should not happen when mode is CopySourceImageIfVolatile 1719 ASSERT_NOT_REACHED(); 1720 return nullptr; 1721 } 1722 ASSERT(imageForRendering); 1723 1724 bool originClean = !wouldTaintOrigin(imageSource); 1725 1726 return CanvasPattern::create(imageForRendering.release(), repeatMode, originClean); 1727} 1728 1729bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, FloatRect* dirtyRect) 1730{ 1731 FloatRect clipBounds; 1732 if (!drawingContext()->getTransformedClipBounds(&clipBounds)) 1733 return false; 1734 return computeDirtyRect(localRect, clipBounds, dirtyRect); 1735} 1736 1737bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, const FloatRect& transformedClipBounds, FloatRect* dirtyRect) 1738{ 1739 FloatRect canvasRect = state().m_transform.mapRect(localRect); 1740 1741 if (alphaChannel(state().m_shadowColor)) { 1742 FloatRect shadowRect(canvasRect); 1743 shadowRect.move(state().m_shadowOffset); 1744 shadowRect.inflate(state().m_shadowBlur); 1745 canvasRect.unite(shadowRect); 1746 } 1747 1748 canvasRect.intersect(transformedClipBounds); 1749 if (canvasRect.isEmpty()) 1750 return false; 1751 1752 if (dirtyRect) 1753 *dirtyRect = canvasRect; 1754 1755 return true; 1756} 1757 1758void CanvasRenderingContext2D::didDraw(const FloatRect& dirtyRect) 1759{ 1760 if (dirtyRect.isEmpty()) 1761 return; 1762 1763 canvas()->didDraw(dirtyRect); 1764} 1765 1766GraphicsContext* CanvasRenderingContext2D::drawingContext() const 1767{ 1768 if (isContextLost()) 1769 return 0; 1770 return canvas()->drawingContext(); 1771} 1772 1773static PassRefPtrWillBeRawPtr<ImageData> createEmptyImageData(const IntSize& size) 1774{ 1775 if (RefPtrWillBeRawPtr<ImageData> data = ImageData::create(size)) { 1776 data->data()->zeroFill(); 1777 return data.release(); 1778 } 1779 1780 return nullptr; 1781} 1782 1783PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtrWillBeRawPtr<ImageData> imageData) const 1784{ 1785 return createEmptyImageData(imageData->size()); 1786} 1787 1788PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionState& exceptionState) const 1789{ 1790 if (!sw || !sh) { 1791 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width")); 1792 return nullptr; 1793 } 1794 1795 FloatSize logicalSize(fabs(sw), fabs(sh)); 1796 if (!logicalSize.isExpressibleAsIntSize()) 1797 return nullptr; 1798 1799 IntSize size = expandedIntSize(logicalSize); 1800 if (size.width() < 1) 1801 size.setWidth(1); 1802 if (size.height() < 1) 1803 size.setHeight(1); 1804 1805 return createEmptyImageData(size); 1806} 1807 1808PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const 1809{ 1810 if (!canvas()->originClean()) 1811 exceptionState.throwSecurityError("The canvas has been tainted by cross-origin data."); 1812 else if (!sw || !sh) 1813 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width")); 1814 1815 if (exceptionState.hadException()) 1816 return nullptr; 1817 1818 if (sw < 0) { 1819 sx += sw; 1820 sw = -sw; 1821 } 1822 if (sh < 0) { 1823 sy += sh; 1824 sh = -sh; 1825 } 1826 1827 FloatRect logicalRect(sx, sy, sw, sh); 1828 if (logicalRect.width() < 1) 1829 logicalRect.setWidth(1); 1830 if (logicalRect.height() < 1) 1831 logicalRect.setHeight(1); 1832 if (!logicalRect.isExpressibleAsIntRect()) 1833 return nullptr; 1834 1835 IntRect imageDataRect = enclosingIntRect(logicalRect); 1836 ImageBuffer* buffer = canvas()->buffer(); 1837 if (!buffer || isContextLost()) 1838 return createEmptyImageData(imageDataRect.size()); 1839 1840 RefPtr<Uint8ClampedArray> byteArray = buffer->getImageData(Unmultiplied, imageDataRect); 1841 if (!byteArray) 1842 return nullptr; 1843 1844 return ImageData::create(imageDataRect.size(), byteArray.release()); 1845} 1846 1847void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy) 1848{ 1849 putImageData(data, dx, dy, 0, 0, data->width(), data->height()); 1850} 1851 1852void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight) 1853{ 1854 ImageBuffer* buffer = canvas()->buffer(); 1855 if (!buffer) 1856 return; 1857 1858 if (dirtyWidth < 0) { 1859 dirtyX += dirtyWidth; 1860 dirtyWidth = -dirtyWidth; 1861 } 1862 1863 if (dirtyHeight < 0) { 1864 dirtyY += dirtyHeight; 1865 dirtyHeight = -dirtyHeight; 1866 } 1867 1868 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); 1869 clipRect.intersect(IntRect(0, 0, data->width(), data->height())); 1870 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy)); 1871 IntRect destRect = enclosingIntRect(clipRect); 1872 destRect.move(destOffset); 1873 destRect.intersect(IntRect(IntPoint(), buffer->size())); 1874 if (destRect.isEmpty()) 1875 return; 1876 IntRect sourceRect(destRect); 1877 sourceRect.move(-destOffset); 1878 1879 buffer->putByteArray(Unmultiplied, data->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset)); 1880 1881 didDraw(destRect); 1882} 1883 1884String CanvasRenderingContext2D::font() const 1885{ 1886 if (!state().m_realizedFont) 1887 return defaultFont; 1888 1889 StringBuilder serializedFont; 1890 const FontDescription& fontDescription = state().m_font.fontDescription(); 1891 1892 if (fontDescription.style() == FontStyleItalic) 1893 serializedFont.appendLiteral("italic "); 1894 if (fontDescription.weight() == FontWeightBold) 1895 serializedFont.appendLiteral("bold "); 1896 if (fontDescription.variant() == FontVariantSmallCaps) 1897 serializedFont.appendLiteral("small-caps "); 1898 1899 serializedFont.appendNumber(fontDescription.computedPixelSize()); 1900 serializedFont.appendLiteral("px"); 1901 1902 const FontFamily& firstFontFamily = fontDescription.family(); 1903 for (const FontFamily* fontFamily = &firstFontFamily; fontFamily; fontFamily = fontFamily->next()) { 1904 if (fontFamily != &firstFontFamily) 1905 serializedFont.append(','); 1906 1907 // FIXME: We should append family directly to serializedFont rather than building a temporary string. 1908 String family = fontFamily->family(); 1909 if (family.startsWith("-webkit-")) 1910 family = family.substring(8); 1911 if (family.contains(' ')) 1912 family = "\"" + family + "\""; 1913 1914 serializedFont.append(' '); 1915 serializedFont.append(family); 1916 } 1917 1918 return serializedFont.toString(); 1919} 1920 1921void CanvasRenderingContext2D::setFont(const String& newFont) 1922{ 1923 // The style resolution required for rendering text is not available in frame-less documents. 1924 if (!canvas()->document().frame()) 1925 return; 1926 1927 MutableStylePropertyMap::iterator i = m_fetchedFonts.find(newFont); 1928 RefPtrWillBeRawPtr<MutableStylePropertySet> parsedStyle = i != m_fetchedFonts.end() ? i->value : nullptr; 1929 1930 if (!parsedStyle) { 1931 parsedStyle = MutableStylePropertySet::create(); 1932 CSSParserMode mode = m_usesCSSCompatibilityParseMode ? HTMLQuirksMode : HTMLStandardMode; 1933 CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, newFont, true, mode, 0); 1934 m_fetchedFonts.add(newFont, parsedStyle); 1935 } 1936 if (parsedStyle->isEmpty()) 1937 return; 1938 1939 String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont); 1940 1941 // According to http://lists.w3.org/Archives/Public/public-html/2009Jul/0947.html, 1942 // the "inherit" and "initial" values must be ignored. 1943 if (fontValue == "inherit" || fontValue == "initial") 1944 return; 1945 1946 // The parse succeeded. 1947 String newFontSafeCopy(newFont); // Create a string copy since newFont can be deleted inside realizeSaves. 1948 realizeSaves(0); 1949 modifiableState().m_unparsedFont = newFontSafeCopy; 1950 1951 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work 1952 // relative to the canvas. 1953 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 1954 canvas()->document().updateRenderTreeIfNeeded(); 1955 if (RenderStyle* computedStyle = canvas()->computedStyle()) { 1956 FontDescription elementFontDescription(computedStyle->fontDescription()); 1957 // Reset the computed size to avoid inheriting the zoom factor from the <canvas> element. 1958 elementFontDescription.setComputedSize(elementFontDescription.specifiedSize()); 1959 newStyle->setFontDescription(elementFontDescription); 1960 } else { 1961 FontFamily fontFamily; 1962 fontFamily.setFamily(defaultFontFamily); 1963 1964 FontDescription defaultFontDescription; 1965 defaultFontDescription.setFamily(fontFamily); 1966 defaultFontDescription.setSpecifiedSize(defaultFontSize); 1967 defaultFontDescription.setComputedSize(defaultFontSize); 1968 1969 newStyle->setFontDescription(defaultFontDescription); 1970 } 1971 1972 newStyle->font().update(newStyle->font().fontSelector()); 1973 1974 // Now map the font property longhands into the style. 1975 CSSPropertyValue properties[] = { 1976 CSSPropertyValue(CSSPropertyFontFamily, *parsedStyle), 1977 CSSPropertyValue(CSSPropertyFontStyle, *parsedStyle), 1978 CSSPropertyValue(CSSPropertyFontVariant, *parsedStyle), 1979 CSSPropertyValue(CSSPropertyFontWeight, *parsedStyle), 1980 CSSPropertyValue(CSSPropertyFontSize, *parsedStyle), 1981 CSSPropertyValue(CSSPropertyLineHeight, *parsedStyle), 1982 }; 1983 1984 StyleResolver& styleResolver = canvas()->document().ensureStyleResolver(); 1985 styleResolver.applyPropertiesToStyle(properties, WTF_ARRAY_LENGTH(properties), newStyle.get()); 1986 1987#if !ENABLE(OILPAN) 1988 if (state().m_realizedFont) 1989 static_cast<CSSFontSelector*>(state().m_font.fontSelector())->unregisterForInvalidationCallbacks(&modifiableState()); 1990#endif 1991 modifiableState().m_font = newStyle->font(); 1992 modifiableState().m_font.update(canvas()->document().styleEngine()->fontSelector()); 1993 modifiableState().m_realizedFont = true; 1994 canvas()->document().styleEngine()->fontSelector()->registerForInvalidationCallbacks(&modifiableState()); 1995} 1996 1997String CanvasRenderingContext2D::textAlign() const 1998{ 1999 return textAlignName(state().m_textAlign); 2000} 2001 2002void CanvasRenderingContext2D::setTextAlign(const String& s) 2003{ 2004 TextAlign align; 2005 if (!parseTextAlign(s, align)) 2006 return; 2007 if (state().m_textAlign == align) 2008 return; 2009 realizeSaves(0); 2010 modifiableState().m_textAlign = align; 2011} 2012 2013String CanvasRenderingContext2D::textBaseline() const 2014{ 2015 return textBaselineName(state().m_textBaseline); 2016} 2017 2018void CanvasRenderingContext2D::setTextBaseline(const String& s) 2019{ 2020 TextBaseline baseline; 2021 if (!parseTextBaseline(s, baseline)) 2022 return; 2023 if (state().m_textBaseline == baseline) 2024 return; 2025 realizeSaves(0); 2026 modifiableState().m_textBaseline = baseline; 2027} 2028 2029inline TextDirection CanvasRenderingContext2D::toTextDirection(Direction direction, RenderStyle** computedStyle) const 2030{ 2031 RenderStyle* style = (computedStyle || direction == DirectionInherit) ? canvas()->computedStyle() : nullptr; 2032 if (computedStyle) 2033 *computedStyle = style; 2034 switch (direction) { 2035 case DirectionInherit: 2036 return style ? style->direction() : LTR; 2037 case DirectionRTL: 2038 return RTL; 2039 case DirectionLTR: 2040 return LTR; 2041 } 2042 ASSERT_NOT_REACHED(); 2043 return LTR; 2044} 2045 2046String CanvasRenderingContext2D::direction() const 2047{ 2048 if (state().m_direction == DirectionInherit) 2049 canvas()->document().updateRenderTreeIfNeeded(); 2050 return toTextDirection(state().m_direction) == RTL ? rtl : ltr; 2051} 2052 2053void CanvasRenderingContext2D::setDirection(const String& directionString) 2054{ 2055 Direction direction; 2056 if (directionString == inherit) 2057 direction = DirectionInherit; 2058 else if (directionString == rtl) 2059 direction = DirectionRTL; 2060 else if (directionString == ltr) 2061 direction = DirectionLTR; 2062 else 2063 return; 2064 2065 if (state().m_direction == direction) 2066 return; 2067 2068 realizeSaves(0); 2069 modifiableState().m_direction = direction; 2070} 2071 2072void CanvasRenderingContext2D::fillText(const String& text, float x, float y) 2073{ 2074 drawTextInternal(text, x, y, true); 2075} 2076 2077void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth) 2078{ 2079 drawTextInternal(text, x, y, true, maxWidth, true); 2080} 2081 2082void CanvasRenderingContext2D::strokeText(const String& text, float x, float y) 2083{ 2084 drawTextInternal(text, x, y, false); 2085} 2086 2087void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth) 2088{ 2089 drawTextInternal(text, x, y, false, maxWidth, true); 2090} 2091 2092PassRefPtrWillBeRawPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text) 2093{ 2094 RefPtrWillBeRawPtr<TextMetrics> metrics = TextMetrics::create(); 2095 2096 // The style resolution required for rendering text is not available in frame-less documents. 2097 if (!canvas()->document().frame()) 2098 return metrics.release(); 2099 2100 FontCachePurgePreventer fontCachePurgePreventer; 2101 canvas()->document().updateRenderTreeIfNeeded(); 2102 const Font& font = accessFont(); 2103 const TextRun textRun(text, 0, 0, TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, LTR, false, true, true); 2104 FloatRect textBounds = font.selectionRectForText(textRun, FloatPoint(), font.fontDescription().computedSize(), 0, -1, true); 2105 2106 // x direction 2107 metrics->setWidth(font.width(textRun)); 2108 metrics->setActualBoundingBoxLeft(-textBounds.x()); 2109 metrics->setActualBoundingBoxRight(textBounds.maxX()); 2110 2111 // y direction 2112 const FontMetrics& fontMetrics = font.fontMetrics(); 2113 const float ascent = fontMetrics.floatAscent(); 2114 const float descent = fontMetrics.floatDescent(); 2115 const float baselineY = getFontBaseline(fontMetrics); 2116 2117 metrics->setFontBoundingBoxAscent(ascent - baselineY); 2118 metrics->setFontBoundingBoxDescent(descent + baselineY); 2119 metrics->setActualBoundingBoxAscent(-textBounds.y() - baselineY); 2120 metrics->setActualBoundingBoxDescent(textBounds.maxY() + baselineY); 2121 2122 // Note : top/bottom and ascend/descend are currently the same, so there's no difference 2123 // between the EM box's top and bottom and the font's ascend and descend 2124 metrics->setEmHeightAscent(0); 2125 metrics->setEmHeightDescent(0); 2126 2127 metrics->setHangingBaseline(-0.8f * ascent + baselineY); 2128 metrics->setAlphabeticBaseline(baselineY); 2129 metrics->setIdeographicBaseline(descent + baselineY); 2130 return metrics.release(); 2131} 2132 2133void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth, bool useMaxWidth) 2134{ 2135 // The style resolution required for rendering text is not available in frame-less documents. 2136 if (!canvas()->document().frame()) 2137 return; 2138 2139 // accessFont needs the style to be up to date, but updating style can cause script to run, 2140 // (e.g. due to autofocus) which can free the GraphicsContext, so update style before grabbing 2141 // the GraphicsContext. 2142 canvas()->document().updateRenderTreeIfNeeded(); 2143 2144 GraphicsContext* c = drawingContext(); 2145 if (!c) 2146 return; 2147 if (!state().m_invertibleCTM) 2148 return; 2149 if (!std::isfinite(x) | !std::isfinite(y)) 2150 return; 2151 if (useMaxWidth && (!std::isfinite(maxWidth) || maxWidth <= 0)) 2152 return; 2153 2154 // If gradient size is zero, then paint nothing. 2155 Gradient* gradient = c->strokeGradient(); 2156 if (!fill && gradient && gradient->isZeroSize()) 2157 return; 2158 2159 gradient = c->fillGradient(); 2160 if (fill && gradient && gradient->isZeroSize()) 2161 return; 2162 2163 FontCachePurgePreventer fontCachePurgePreventer; 2164 2165 const Font& font = accessFont(); 2166 const FontMetrics& fontMetrics = font.fontMetrics(); 2167 2168 // FIXME: Need to turn off font smoothing. 2169 2170 RenderStyle* computedStyle; 2171 TextDirection direction = toTextDirection(state().m_direction, &computedStyle); 2172 bool isRTL = direction == RTL; 2173 bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : false; 2174 2175 TextRun textRun(text, 0, 0, TextRun::AllowTrailingExpansion, direction, override, true, true); 2176 // Draw the item text at the correct point. 2177 FloatPoint location(x, y + getFontBaseline(fontMetrics)); 2178 float fontWidth = font.width(textRun); 2179 2180 useMaxWidth = (useMaxWidth && maxWidth < fontWidth); 2181 float width = useMaxWidth ? maxWidth : fontWidth; 2182 2183 TextAlign align = state().m_textAlign; 2184 if (align == StartTextAlign) 2185 align = isRTL ? RightTextAlign : LeftTextAlign; 2186 else if (align == EndTextAlign) 2187 align = isRTL ? LeftTextAlign : RightTextAlign; 2188 2189 switch (align) { 2190 case CenterTextAlign: 2191 location.setX(location.x() - width / 2); 2192 break; 2193 case RightTextAlign: 2194 location.setX(location.x() - width); 2195 break; 2196 default: 2197 break; 2198 } 2199 2200 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text. 2201 TextRunPaintInfo textRunPaintInfo(textRun); 2202 textRunPaintInfo.bounds = FloatRect(location.x() - fontMetrics.height() / 2, 2203 location.y() - fontMetrics.ascent() - fontMetrics.lineGap(), 2204 width + fontMetrics.height(), 2205 fontMetrics.lineSpacing()); 2206 if (!fill) 2207 inflateStrokeRect(textRunPaintInfo.bounds); 2208 2209 c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke); 2210 2211 GraphicsContextStateSaver stateSaver(*c); 2212 if (useMaxWidth) { 2213 c->translate(location.x(), location.y()); 2214 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work. 2215 c->scale((fontWidth > 0 ? (width / fontWidth) : 0), 1); 2216 location = FloatPoint(); 2217 } 2218 2219 FloatRect clipBounds; 2220 if (!c->getTransformedClipBounds(&clipBounds)) { 2221 return; 2222 } 2223 2224 if (isFullCanvasCompositeMode(state().m_globalComposite)) { 2225 c->beginLayer(1, state().m_globalComposite); 2226 CompositeOperator previousOperator = c->compositeOperation(); 2227 c->setCompositeOperation(CompositeSourceOver); 2228 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady); 2229 c->setCompositeOperation(previousOperator); 2230 c->endLayer(); 2231 didDraw(clipBounds); 2232 } else if (state().m_globalComposite == CompositeCopy) { 2233 clearCanvas(); 2234 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady); 2235 didDraw(clipBounds); 2236 } else { 2237 FloatRect dirtyRect; 2238 if (computeDirtyRect(textRunPaintInfo.bounds, clipBounds, &dirtyRect)) { 2239 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady); 2240 didDraw(dirtyRect); 2241 } 2242 } 2243} 2244 2245void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const 2246{ 2247 // Fast approximation of the stroke's bounding rect. 2248 // This yields a slightly oversized rect but is very fast 2249 // compared to Path::strokeBoundingRect(). 2250 static const float root2 = sqrtf(2); 2251 float delta = state().m_lineWidth / 2; 2252 if (state().m_lineJoin == MiterJoin) 2253 delta *= state().m_miterLimit; 2254 else if (state().m_lineCap == SquareCap) 2255 delta *= root2; 2256 2257 rect.inflate(delta); 2258} 2259 2260const Font& CanvasRenderingContext2D::accessFont() 2261{ 2262 // This needs style to be up to date, but can't assert so because drawTextInternal 2263 // can invalidate style before this is called (e.g. drawingContext invalidates style). 2264 if (!state().m_realizedFont) 2265 setFont(state().m_unparsedFont); 2266 return state().m_font; 2267} 2268 2269int CanvasRenderingContext2D::getFontBaseline(const FontMetrics& fontMetrics) const 2270{ 2271 switch (state().m_textBaseline) { 2272 case TopTextBaseline: 2273 return fontMetrics.ascent(); 2274 case HangingTextBaseline: 2275 // According to http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling 2276 // "FOP (Formatting Objects Processor) puts the hanging baseline at 80% of the ascender height" 2277 return (fontMetrics.ascent() * 4) / 5; 2278 case BottomTextBaseline: 2279 case IdeographicTextBaseline: 2280 return -fontMetrics.descent(); 2281 case MiddleTextBaseline: 2282 return -fontMetrics.descent() + fontMetrics.height() / 2; 2283 case AlphabeticTextBaseline: 2284 default: 2285 // Do nothing. 2286 break; 2287 } 2288 return 0; 2289} 2290 2291void CanvasRenderingContext2D::setIsHidden(bool hidden) 2292{ 2293 ImageBuffer* buffer = canvas()->buffer(); 2294 if (buffer) 2295 buffer->setIsHidden(hidden); 2296} 2297 2298blink::WebLayer* CanvasRenderingContext2D::platformLayer() const 2299{ 2300 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0; 2301} 2302 2303bool CanvasRenderingContext2D::imageSmoothingEnabled() const 2304{ 2305 return state().m_imageSmoothingEnabled; 2306} 2307 2308void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled) 2309{ 2310 if (enabled == state().m_imageSmoothingEnabled) 2311 return; 2312 2313 GraphicsContext* c = drawingContext(); 2314 realizeSaves(c); 2315 modifiableState().m_imageSmoothingEnabled = enabled; 2316 if (c) 2317 c->setImageInterpolationQuality(enabled ? CanvasDefaultInterpolationQuality : InterpolationNone); 2318} 2319 2320PassRefPtrWillBeRawPtr<Canvas2DContextAttributes> CanvasRenderingContext2D::getContextAttributes() const 2321{ 2322 RefPtrWillBeRawPtr<Canvas2DContextAttributes> attributes = Canvas2DContextAttributes::create(); 2323 attributes->setAlpha(m_hasAlpha); 2324 return attributes.release(); 2325} 2326 2327void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element) 2328{ 2329 drawFocusIfNeededInternal(m_path, element); 2330} 2331 2332void CanvasRenderingContext2D::drawFocusIfNeeded(Path2D* path2d, Element* element) 2333{ 2334 drawFocusIfNeededInternal(path2d->path(), element); 2335} 2336 2337void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Element* element) 2338{ 2339 if (!focusRingCallIsValid(path, element)) 2340 return; 2341 2342 // Note: we need to check document->focusedElement() rather than just calling 2343 // element->focused(), because element->focused() isn't updated until after 2344 // focus events fire. 2345 if (element->document().focusedElement() == element) 2346 drawFocusRing(path); 2347} 2348 2349bool CanvasRenderingContext2D::focusRingCallIsValid(const Path& path, Element* element) 2350{ 2351 ASSERT(element); 2352 if (!state().m_invertibleCTM) 2353 return false; 2354 if (path.isEmpty()) 2355 return false; 2356 if (!element->isDescendantOf(canvas())) 2357 return false; 2358 2359 return true; 2360} 2361 2362void CanvasRenderingContext2D::drawFocusRing(const Path& path) 2363{ 2364 GraphicsContext* c = drawingContext(); 2365 if (!c) 2366 return; 2367 2368 // These should match the style defined in html.css. 2369 Color focusRingColor = RenderTheme::theme().focusRingColor(); 2370 const int focusRingWidth = 5; 2371 const int focusRingOutline = 0; 2372 2373 // We need to add focusRingWidth to dirtyRect. 2374 StrokeData strokeData; 2375 strokeData.setThickness(focusRingWidth); 2376 2377 FloatRect dirtyRect; 2378 if (!computeDirtyRect(path.strokeBoundingRect(strokeData), &dirtyRect)) 2379 return; 2380 2381 c->save(); 2382 c->setAlphaAsFloat(1.0); 2383 c->clearShadow(); 2384 c->setCompositeOperation(CompositeSourceOver, blink::WebBlendModeNormal); 2385 c->drawFocusRing(path, focusRingWidth, focusRingOutline, focusRingColor); 2386 c->restore(); 2387 validateStateStack(); 2388 didDraw(dirtyRect); 2389} 2390 2391void CanvasRenderingContext2D::addHitRegion(const HitRegionOptions& options, ExceptionState& exceptionState) 2392{ 2393 HitRegionOptionsInternal passOptions; 2394 passOptions.id = options.id(); 2395 passOptions.control = options.control(); 2396 if (passOptions.id.isEmpty() && !passOptions.control) { 2397 exceptionState.throwDOMException(NotSupportedError, "Both id and control are null."); 2398 return; 2399 } 2400 2401 Path hitRegionPath = options.hasPath() ? options.path()->path() : m_path; 2402 2403 FloatRect clipBounds; 2404 GraphicsContext* context = drawingContext(); 2405 2406 if (hitRegionPath.isEmpty() || !context || !state().m_invertibleCTM 2407 || !context->getTransformedClipBounds(&clipBounds)) { 2408 exceptionState.throwDOMException(NotSupportedError, "The specified path has no pixels."); 2409 return; 2410 } 2411 2412 hitRegionPath.transform(state().m_transform); 2413 2414 if (hasClip()) { 2415 // FIXME: The hit regions should take clipping region into account. 2416 // However, we have no way to get the region from canvas state stack by now. 2417 // See http://crbug.com/387057 2418 exceptionState.throwDOMException(NotSupportedError, "The specified path has no pixels."); 2419 return; 2420 } 2421 2422 passOptions.path = hitRegionPath; 2423 2424 if (options.fillRule() != "evenodd") 2425 passOptions.fillRule = RULE_NONZERO; 2426 else 2427 passOptions.fillRule = RULE_EVENODD; 2428 2429 addHitRegionInternal(passOptions, exceptionState); 2430} 2431 2432void CanvasRenderingContext2D::addHitRegionInternal(const HitRegionOptionsInternal& options, ExceptionState& exceptionState) 2433{ 2434 if (!m_hitRegionManager) 2435 m_hitRegionManager = HitRegionManager::create(); 2436 2437 // Remove previous region (with id or control) 2438 m_hitRegionManager->removeHitRegionById(options.id); 2439 m_hitRegionManager->removeHitRegionByControl(options.control.get()); 2440 2441 RefPtrWillBeRawPtr<HitRegion> hitRegion = HitRegion::create(options); 2442 hitRegion->updateAccessibility(canvas()); 2443 m_hitRegionManager->addHitRegion(hitRegion.release()); 2444} 2445 2446void CanvasRenderingContext2D::removeHitRegion(const String& id) 2447{ 2448 if (m_hitRegionManager) 2449 m_hitRegionManager->removeHitRegionById(id); 2450} 2451 2452void CanvasRenderingContext2D::clearHitRegions() 2453{ 2454 if (m_hitRegionManager) 2455 m_hitRegionManager->removeAllHitRegions(); 2456} 2457 2458HitRegion* CanvasRenderingContext2D::hitRegionAtPoint(const LayoutPoint& point) 2459{ 2460 if (m_hitRegionManager) 2461 return m_hitRegionManager->getHitRegionAtPoint(point); 2462 2463 return 0; 2464} 2465 2466unsigned CanvasRenderingContext2D::hitRegionsCount() const 2467{ 2468 if (m_hitRegionManager) 2469 return m_hitRegionManager->getHitRegionsCount(); 2470 2471 return 0; 2472} 2473 2474} // namespace blink 2475