| OLD | NEW |
| 1 /* | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc.
All rights reserved. | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) | 3 // found in the LICENSE file. |
| 4 * Copyright (C) 2007 Alp Toker <alp@atoker.com> | |
| 5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> | |
| 6 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> | |
| 7 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. | |
| 8 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved. | |
| 9 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. | |
| 10 * | |
| 11 * Redistribution and use in source and binary forms, with or without | |
| 12 * modification, are permitted provided that the following conditions | |
| 13 * are met: | |
| 14 * 1. Redistributions of source code must retain the above copyright | |
| 15 * notice, this list of conditions and the following disclaimer. | |
| 16 * 2. Redistributions in binary form must reproduce the above copyright | |
| 17 * notice, this list of conditions and the following disclaimer in the | |
| 18 * documentation and/or other materials provided with the distribution. | |
| 19 * | |
| 20 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
| 21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
| 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 31 */ | |
| 32 | 4 |
| 33 #include "modules/canvas2d/CanvasRenderingContext2D.h" | 5 #include "modules/canvas2d/BaseRenderingContext2D.h" |
| 34 | 6 |
| 35 #include "bindings/core/v8/ExceptionMessages.h" | 7 #include "bindings/core/v8/ExceptionMessages.h" |
| 36 #include "bindings/core/v8/ExceptionState.h" | 8 #include "bindings/core/v8/ExceptionState.h" |
| 37 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | 9 #include "bindings/core/v8/ExceptionStatePlaceholder.h" |
| 38 #include "core/CSSPropertyNames.h" | |
| 39 #include "core/css/StylePropertySet.h" | |
| 40 #include "core/css/parser/CSSParser.h" | 10 #include "core/css/parser/CSSParser.h" |
| 41 #include "core/css/resolver/StyleResolver.h" | |
| 42 #include "core/dom/AXObjectCache.h" | |
| 43 #include "core/dom/StyleEngine.h" | |
| 44 #include "core/events/Event.h" | |
| 45 #include "core/frame/ImageBitmap.h" | 11 #include "core/frame/ImageBitmap.h" |
| 46 #include "core/frame/Settings.h" | 12 #include "core/html/HTMLCanvasElement.h" |
| 13 #include "core/html/HTMLImageElement.h" |
| 47 #include "core/html/HTMLVideoElement.h" | 14 #include "core/html/HTMLVideoElement.h" |
| 48 #include "core/html/ImageData.h" | 15 #include "core/html/ImageData.h" |
| 49 #include "core/html/TextMetrics.h" | |
| 50 #include "core/html/canvas/CanvasFontCache.h" | |
| 51 #include "core/layout/LayoutBox.h" | |
| 52 #include "core/layout/LayoutTheme.h" | |
| 53 #include "modules/canvas2d/CanvasGradient.h" | 16 #include "modules/canvas2d/CanvasGradient.h" |
| 54 #include "modules/canvas2d/CanvasPattern.h" | 17 #include "modules/canvas2d/CanvasPattern.h" |
| 55 #include "modules/canvas2d/CanvasRenderingContext2DState.h" | |
| 56 #include "modules/canvas2d/CanvasStyle.h" | 18 #include "modules/canvas2d/CanvasStyle.h" |
| 57 #include "modules/canvas2d/HitRegion.h" | |
| 58 #include "modules/canvas2d/Path2D.h" | 19 #include "modules/canvas2d/Path2D.h" |
| 59 #include "platform/fonts/FontCache.h" | |
| 60 #include "platform/geometry/FloatQuad.h" | 20 #include "platform/geometry/FloatQuad.h" |
| 61 #include "platform/graphics/DrawLooperBuilder.h" | 21 #include "platform/graphics/Color.h" |
| 62 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h" | 22 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h" |
| 23 #include "platform/graphics/Image.h" |
| 63 #include "platform/graphics/ImageBuffer.h" | 24 #include "platform/graphics/ImageBuffer.h" |
| 64 #include "platform/graphics/StrokeData.h" | 25 #include "platform/graphics/StrokeData.h" |
| 65 #include "platform/graphics/skia/SkiaUtils.h" | 26 #include "platform/graphics/skia/SkiaUtils.h" |
| 66 #include "platform/text/BidiTextRun.h" | |
| 67 #include "public/platform/Platform.h" | |
| 68 #include "third_party/skia/include/core/SkCanvas.h" | |
| 69 #include "third_party/skia/include/core/SkImageFilter.h" | 27 #include "third_party/skia/include/core/SkImageFilter.h" |
| 70 #include "wtf/ArrayBufferContents.h" | |
| 71 #include "wtf/CheckedArithmetic.h" | |
| 72 #include "wtf/MathExtras.h" | |
| 73 #include "wtf/OwnPtr.h" | |
| 74 #include "wtf/text/StringBuilder.h" | |
| 75 | 28 |
| 76 namespace blink { | 29 namespace blink { |
| 77 | 30 |
| 78 static const char defaultFont[] = "10px sans-serif"; | 31 BaseRenderingContext2D::BaseRenderingContext2D() |
| 79 static const char inherit[] = "inherit"; | 32 : m_clipAntialiasing(NotAntiAliased) |
| 80 static const char rtl[] = "rtl"; | |
| 81 static const char ltr[] = "ltr"; | |
| 82 static const double TryRestoreContextInterval = 0.5; | |
| 83 static const unsigned MaxTryRestoreContextAttempts = 4; | |
| 84 static const double cDeviceScaleFactor = 1.0; // Canvas is device independent | |
| 85 | |
| 86 static bool contextLostRestoredEventsEnabled() | |
| 87 { | 33 { |
| 88 return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled(); | 34 m_stateStack.append(CanvasRenderingContext2DState::create()); |
| 89 } | 35 } |
| 90 | 36 |
| 91 // Drawing methods need to use this instead of SkAutoCanvasRestore in case overd
raw | 37 BaseRenderingContext2D::~BaseRenderingContext2D() |
| 92 // detection substitutes the recording canvas (to discard overdrawn draw calls). | |
| 93 class CanvasRenderingContext2DAutoRestoreSkCanvas { | |
| 94 STACK_ALLOCATED(); | |
| 95 public: | |
| 96 explicit CanvasRenderingContext2DAutoRestoreSkCanvas(CanvasRenderingContext2
D* context) | |
| 97 : m_context(context) | |
| 98 , m_saveCount(0) | |
| 99 { | |
| 100 ASSERT(m_context); | |
| 101 SkCanvas* c = m_context->drawingCanvas(); | |
| 102 if (c) { | |
| 103 m_saveCount = c->getSaveCount(); | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 ~CanvasRenderingContext2DAutoRestoreSkCanvas() | |
| 108 { | |
| 109 SkCanvas* c = m_context->drawingCanvas(); | |
| 110 if (c) | |
| 111 c->restoreToCount(m_saveCount); | |
| 112 m_context->validateStateStack(); | |
| 113 } | |
| 114 private: | |
| 115 RawPtrWillBeMember<CanvasRenderingContext2D> m_context; | |
| 116 int m_saveCount; | |
| 117 }; | |
| 118 | |
| 119 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, co
nst CanvasContextCreationAttributes& attrs, Document& document) | |
| 120 : CanvasRenderingContext(canvas) | |
| 121 , m_clipAntialiasing(NotAntiAliased) | |
| 122 , m_hasAlpha(attrs.alpha()) | |
| 123 , m_contextLostMode(NotLostContext) | |
| 124 , m_contextRestorable(true) | |
| 125 , m_tryRestoreContextAttemptCount(0) | |
| 126 , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchC
ontextLostEvent) | |
| 127 , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispa
tchContextRestoredEvent) | |
| 128 , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreC
ontextEvent) | |
| 129 , m_pruneLocalFontCacheScheduled(false) | |
| 130 { | 38 { |
| 131 if (document.settings() && document.settings()->antialiasedClips2dCanvasEnab
led()) | |
| 132 m_clipAntialiasing = AntiAliased; | |
| 133 m_stateStack.append(CanvasRenderingContext2DState::create()); | |
| 134 setShouldAntialias(true); | |
| 135 #if ENABLE(OILPAN) | |
| 136 ThreadState::current()->registerPreFinalizer(this); | |
| 137 #endif | |
| 138 } | 39 } |
| 139 | 40 |
| 140 void CanvasRenderingContext2D::unwindStateStack() | 41 CanvasRenderingContext2DState& BaseRenderingContext2D::modifiableState() |
| 141 { | |
| 142 if (size_t stackSize = m_stateStack.size()) { | |
| 143 if (SkCanvas* skCanvas = canvas()->existingDrawingCanvas()) { | |
| 144 while (--stackSize) | |
| 145 skCanvas->restore(); | |
| 146 } | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 CanvasRenderingContext2D::~CanvasRenderingContext2D() | |
| 151 { | |
| 152 if (m_pruneLocalFontCacheScheduled) { | |
| 153 Platform::current()->currentThread()->removeTaskObserver(this); | |
| 154 } | |
| 155 #if !ENABLE(OILPAN) | |
| 156 dispose(); | |
| 157 #endif | |
| 158 } | |
| 159 | |
| 160 void CanvasRenderingContext2D::dispose() | |
| 161 { | |
| 162 clearFilterReferences(); | |
| 163 } | |
| 164 | |
| 165 void CanvasRenderingContext2D::validateStateStack() | |
| 166 { | |
| 167 #if ENABLE(ASSERT) | |
| 168 SkCanvas* skCanvas = canvas()->existingDrawingCanvas(); | |
| 169 if (skCanvas && m_contextLostMode == NotLostContext) { | |
| 170 ASSERT(static_cast<size_t>(skCanvas->getSaveCount()) == m_stateStack.siz
e()); | |
| 171 } | |
| 172 #endif | |
| 173 } | |
| 174 | |
| 175 CanvasRenderingContext2DState& CanvasRenderingContext2D::modifiableState() | |
| 176 { | 42 { |
| 177 realizeSaves(); | 43 realizeSaves(); |
| 178 return *m_stateStack.last(); | 44 return *m_stateStack.last(); |
| 179 } | 45 } |
| 180 | 46 |
| 181 bool CanvasRenderingContext2D::isAccelerated() const | 47 void BaseRenderingContext2D::realizeSaves() |
| 182 { | |
| 183 if (!canvas()->hasImageBuffer()) | |
| 184 return false; | |
| 185 return canvas()->buffer()->isAccelerated(); | |
| 186 } | |
| 187 | |
| 188 void CanvasRenderingContext2D::stop() | |
| 189 { | |
| 190 if (!isContextLost()) { | |
| 191 // Never attempt to restore the context because the page is being torn d
own. | |
| 192 loseContext(SyntheticLostContext); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 bool CanvasRenderingContext2D::isContextLost() const | |
| 197 { | |
| 198 return m_contextLostMode != NotLostContext; | |
| 199 } | |
| 200 | |
| 201 void CanvasRenderingContext2D::loseContext(LostContextMode lostMode) | |
| 202 { | |
| 203 if (m_contextLostMode != NotLostContext) | |
| 204 return; | |
| 205 m_contextLostMode = lostMode; | |
| 206 if (m_contextLostMode == SyntheticLostContext) { | |
| 207 canvas()->discardImageBuffer(); | |
| 208 } | |
| 209 m_dispatchContextLostEventTimer.startOneShot(0, BLINK_FROM_HERE); | |
| 210 } | |
| 211 | |
| 212 void CanvasRenderingContext2D::didSetSurfaceSize() | |
| 213 { | |
| 214 if (!m_contextRestorable) | |
| 215 return; | |
| 216 // This code path is for restoring from an eviction | |
| 217 // Restoring from surface failure is handled internally | |
| 218 ASSERT(m_contextLostMode != NotLostContext && !canvas()->hasImageBuffer()); | |
| 219 | |
| 220 if (canvas()->buffer()) { | |
| 221 if (contextLostRestoredEventsEnabled()) { | |
| 222 m_dispatchContextRestoredEventTimer.startOneShot(0, BLINK_FROM_HERE)
; | |
| 223 } else { | |
| 224 // legacy synchronous context restoration. | |
| 225 reset(); | |
| 226 m_contextLostMode = NotLostContext; | |
| 227 } | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 DEFINE_TRACE(CanvasRenderingContext2D) | |
| 232 { | |
| 233 visitor->trace(m_stateStack); | |
| 234 visitor->trace(m_hitRegionManager); | |
| 235 CanvasRenderingContext::trace(visitor); | |
| 236 } | |
| 237 | |
| 238 void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingCon
text2D>*) | |
| 239 { | |
| 240 if (contextLostRestoredEventsEnabled()) { | |
| 241 RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames
::contextlost); | |
| 242 canvas()->dispatchEvent(event); | |
| 243 if (event->defaultPrevented()) { | |
| 244 m_contextRestorable = false; | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 // If RealLostContext, it means the context was not lost due to surface fail
ure | |
| 249 // but rather due to a an eviction, which means image buffer exists. | |
| 250 if (m_contextRestorable && m_contextLostMode == RealLostContext) { | |
| 251 m_tryRestoreContextAttemptCount = 0; | |
| 252 m_tryRestoreContextEventTimer.startRepeating(TryRestoreContextInterval,
BLINK_FROM_HERE); | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 void CanvasRenderingContext2D::tryRestoreContextEvent(Timer<CanvasRenderingConte
xt2D>* timer) | |
| 257 { | |
| 258 if (m_contextLostMode == NotLostContext) { | |
| 259 // Canvas was already restored (possibly thanks to a resize), so stop tr
ying. | |
| 260 m_tryRestoreContextEventTimer.stop(); | |
| 261 return; | |
| 262 } | |
| 263 | |
| 264 ASSERT(m_contextLostMode == RealLostContext); | |
| 265 if (canvas()->hasImageBuffer() && canvas()->buffer()->restoreSurface()) { | |
| 266 m_tryRestoreContextEventTimer.stop(); | |
| 267 dispatchContextRestoredEvent(nullptr); | |
| 268 } | |
| 269 | |
| 270 if (++m_tryRestoreContextAttemptCount > MaxTryRestoreContextAttempts) { | |
| 271 // final attempt: allocate a brand new image buffer instead of restoring | |
| 272 canvas()->discardImageBuffer(); | |
| 273 m_tryRestoreContextEventTimer.stop(); | |
| 274 if (canvas()->buffer()) | |
| 275 dispatchContextRestoredEvent(nullptr); | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 void CanvasRenderingContext2D::dispatchContextRestoredEvent(Timer<CanvasRenderin
gContext2D>*) | |
| 280 { | |
| 281 if (m_contextLostMode == NotLostContext) | |
| 282 return; | |
| 283 reset(); | |
| 284 m_contextLostMode = NotLostContext; | |
| 285 if (contextLostRestoredEventsEnabled()) { | |
| 286 RefPtrWillBeRawPtr<Event> event(Event::create(EventTypeNames::contextres
tored)); | |
| 287 canvas()->dispatchEvent(event); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 void CanvasRenderingContext2D::reset() | |
| 292 { | |
| 293 validateStateStack(); | |
| 294 unwindStateStack(); | |
| 295 m_stateStack.resize(1); | |
| 296 m_stateStack.first() = CanvasRenderingContext2DState::create(); | |
| 297 m_path.clear(); | |
| 298 SkCanvas* c = canvas()->existingDrawingCanvas(); | |
| 299 if (c) { | |
| 300 c->resetMatrix(); | |
| 301 c->clipRect(SkRect::MakeWH(canvas()->width(), canvas()->height()), SkReg
ion::kReplace_Op); | |
| 302 } | |
| 303 validateStateStack(); | |
| 304 } | |
| 305 | |
| 306 void CanvasRenderingContext2D::restoreCanvasMatrixClipStack(SkCanvas* c) const | |
| 307 { | |
| 308 if (!c) | |
| 309 return; | |
| 310 WillBeHeapVector<OwnPtrWillBeMember<CanvasRenderingContext2DState>>::const_i
terator currState; | |
| 311 ASSERT(m_stateStack.begin() < m_stateStack.end()); | |
| 312 for (currState = m_stateStack.begin(); currState < m_stateStack.end(); currS
tate++) { | |
| 313 c->setMatrix(SkMatrix::I()); | |
| 314 currState->get()->playbackClips(c); | |
| 315 c->setMatrix(affineTransformToSkMatrix(currState->get()->transform())); | |
| 316 c->save(); | |
| 317 } | |
| 318 c->restore(); | |
| 319 } | |
| 320 | |
| 321 void CanvasRenderingContext2D::realizeSaves() | |
| 322 { | 48 { |
| 323 validateStateStack(); | 49 validateStateStack(); |
| 324 if (state().hasUnrealizedSaves()) { | 50 if (state().hasUnrealizedSaves()) { |
| 325 ASSERT(m_stateStack.size() >= 1); | 51 ASSERT(m_stateStack.size() >= 1); |
| 326 // Reduce the current state's unrealized count by one now, | 52 // Reduce the current state's unrealized count by one now, |
| 327 // to reflect the fact we are saving one state. | 53 // to reflect the fact we are saving one state. |
| 328 m_stateStack.last()->restore(); | 54 m_stateStack.last()->restore(); |
| 329 m_stateStack.append(CanvasRenderingContext2DState::create(state(), Canva
sRenderingContext2DState::DontCopyClipList)); | 55 m_stateStack.append(CanvasRenderingContext2DState::create(state(), Canva
sRenderingContext2DState::DontCopyClipList)); |
| 330 // Set the new state's unrealized count to 0, because it has no outstand
ing saves. | 56 // Set the new state's unrealized count to 0, because it has no outstand
ing saves. |
| 331 // We need to do this explicitly because the copy constructor and operat
or= used | 57 // We need to do this explicitly because the copy constructor and operat
or= used |
| 332 // by the Vector operations copy the unrealized count from the previous
state (in | 58 // by the Vector operations copy the unrealized count from the previous
state (in |
| 333 // turn necessary to support correct resizing and unwinding of the stack
). | 59 // turn necessary to support correct resizing and unwinding of the stack
). |
| 334 m_stateStack.last()->resetUnrealizedSaveCount(); | 60 m_stateStack.last()->resetUnrealizedSaveCount(); |
| 335 SkCanvas* canvas = drawingCanvas(); | 61 SkCanvas* canvas = drawingCanvas(); |
| 336 if (canvas) | 62 if (canvas) |
| 337 canvas->save(); | 63 canvas->save(); |
| 338 validateStateStack(); | 64 validateStateStack(); |
| 339 } | 65 } |
| 340 } | 66 } |
| 341 | 67 |
| 342 void CanvasRenderingContext2D::save() | 68 void BaseRenderingContext2D::save() |
| 343 { | 69 { |
| 344 m_stateStack.last()->save(); | 70 m_stateStack.last()->save(); |
| 345 } | 71 } |
| 346 | 72 |
| 347 void CanvasRenderingContext2D::restore() | 73 void BaseRenderingContext2D::restore() |
| 348 { | 74 { |
| 349 validateStateStack(); | 75 validateStateStack(); |
| 350 if (state().hasUnrealizedSaves()) { | 76 if (state().hasUnrealizedSaves()) { |
| 351 // We never realized the save, so just record that it was unnecessary. | 77 // We never realized the save, so just record that it was unnecessary. |
| 352 m_stateStack.last()->restore(); | 78 m_stateStack.last()->restore(); |
| 353 return; | 79 return; |
| 354 } | 80 } |
| 355 ASSERT(m_stateStack.size() >= 1); | 81 ASSERT(m_stateStack.size() >= 1); |
| 356 if (m_stateStack.size() <= 1) | 82 if (m_stateStack.size() <= 1) |
| 357 return; | 83 return; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 372 returnValue.setCanvasGradient(gradient); | 98 returnValue.setCanvasGradient(gradient); |
| 373 return; | 99 return; |
| 374 } | 100 } |
| 375 if (CanvasPattern* pattern = style->canvasPattern()) { | 101 if (CanvasPattern* pattern = style->canvasPattern()) { |
| 376 returnValue.setCanvasPattern(pattern); | 102 returnValue.setCanvasPattern(pattern); |
| 377 return; | 103 return; |
| 378 } | 104 } |
| 379 returnValue.setString(style->color()); | 105 returnValue.setString(style->color()); |
| 380 } | 106 } |
| 381 | 107 |
| 382 void CanvasRenderingContext2D::strokeStyle(StringOrCanvasGradientOrCanvasPattern
& returnValue) const | 108 void BaseRenderingContext2D::strokeStyle(StringOrCanvasGradientOrCanvasPattern&
returnValue) const |
| 383 { | 109 { |
| 384 convertCanvasStyleToUnionType(state().strokeStyle(), returnValue); | 110 convertCanvasStyleToUnionType(state().strokeStyle(), returnValue); |
| 385 } | 111 } |
| 386 | 112 |
| 387 void CanvasRenderingContext2D::setStrokeStyle(const StringOrCanvasGradientOrCanv
asPattern& style) | 113 void BaseRenderingContext2D::setStrokeStyle(const StringOrCanvasGradientOrCanvas
Pattern& style) |
| 388 { | 114 { |
| 389 ASSERT(!style.isNull()); | 115 ASSERT(!style.isNull()); |
| 390 | 116 |
| 391 String colorString; | 117 String colorString; |
| 392 CanvasStyle* canvasStyle = nullptr; | 118 CanvasStyle* canvasStyle = nullptr; |
| 393 if (style.isString()) { | 119 if (style.isString()) { |
| 394 colorString = style.getAsString(); | 120 colorString = style.getAsString(); |
| 395 if (colorString == state().unparsedStrokeColor()) | 121 if (colorString == state().unparsedStrokeColor()) |
| 396 return; | 122 return; |
| 397 Color parsedColor = 0; | 123 Color parsedColor = 0; |
| 398 if (!parseColorOrCurrentColor(parsedColor, colorString, canvas())) | 124 if (!parseColorOrCurrentColor(parsedColor, colorString)) |
| 399 return; | 125 return; |
| 400 if (state().strokeStyle()->isEquivalentRGBA(parsedColor.rgb())) { | 126 if (state().strokeStyle()->isEquivalentRGBA(parsedColor.rgb())) { |
| 401 modifiableState().setUnparsedStrokeColor(colorString); | 127 modifiableState().setUnparsedStrokeColor(colorString); |
| 402 return; | 128 return; |
| 403 } | 129 } |
| 404 canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb()); | 130 canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb()); |
| 405 } else if (style.isCanvasGradient()) { | 131 } else if (style.isCanvasGradient()) { |
| 406 canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient(
)); | 132 canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient(
)); |
| 407 } else if (style.isCanvasPattern()) { | 133 } else if (style.isCanvasPattern()) { |
| 408 CanvasPattern* canvasPattern = style.getAsCanvasPattern(); | 134 CanvasPattern* canvasPattern = style.getAsCanvasPattern(); |
| 409 | 135 |
| 410 if (canvas()->originClean() && !canvasPattern->originClean()) | 136 if (originClean() && !canvasPattern->originClean()) |
| 411 canvas()->setOriginTainted(); | 137 setOriginTainted(); |
| 412 | 138 |
| 413 canvasStyle = CanvasStyle::createFromPattern(canvasPattern); | 139 canvasStyle = CanvasStyle::createFromPattern(canvasPattern); |
| 414 } | 140 } |
| 415 | 141 |
| 416 ASSERT(canvasStyle); | 142 ASSERT(canvasStyle); |
| 417 | 143 |
| 418 modifiableState().setStrokeStyle(canvasStyle); | 144 modifiableState().setStrokeStyle(canvasStyle); |
| 419 modifiableState().setUnparsedStrokeColor(colorString); | 145 modifiableState().setUnparsedStrokeColor(colorString); |
| 420 modifiableState().clearResolvedFilter(); | 146 modifiableState().clearResolvedFilter(); |
| 421 } | 147 } |
| 422 | 148 |
| 423 void CanvasRenderingContext2D::fillStyle(StringOrCanvasGradientOrCanvasPattern&
returnValue) const | 149 void BaseRenderingContext2D::fillStyle(StringOrCanvasGradientOrCanvasPattern& re
turnValue) const |
| 424 { | 150 { |
| 425 convertCanvasStyleToUnionType(state().fillStyle(), returnValue); | 151 convertCanvasStyleToUnionType(state().fillStyle(), returnValue); |
| 426 } | 152 } |
| 427 | 153 |
| 428 void CanvasRenderingContext2D::setFillStyle(const StringOrCanvasGradientOrCanvas
Pattern& style) | 154 void BaseRenderingContext2D::setFillStyle(const StringOrCanvasGradientOrCanvasPa
ttern& style) |
| 429 { | 155 { |
| 430 ASSERT(!style.isNull()); | 156 ASSERT(!style.isNull()); |
| 431 validateStateStack(); | 157 validateStateStack(); |
| 432 String colorString; | 158 String colorString; |
| 433 CanvasStyle* canvasStyle = nullptr; | 159 CanvasStyle* canvasStyle = nullptr; |
| 434 if (style.isString()) { | 160 if (style.isString()) { |
| 435 colorString = style.getAsString(); | 161 colorString = style.getAsString(); |
| 436 if (colorString == state().unparsedFillColor()) | 162 if (colorString == state().unparsedFillColor()) |
| 437 return; | 163 return; |
| 438 Color parsedColor = 0; | 164 Color parsedColor = 0; |
| 439 if (!parseColorOrCurrentColor(parsedColor, colorString, canvas())) | 165 if (!parseColorOrCurrentColor(parsedColor, colorString)) |
| 440 return; | 166 return; |
| 441 if (state().fillStyle()->isEquivalentRGBA(parsedColor.rgb())) { | 167 if (state().fillStyle()->isEquivalentRGBA(parsedColor.rgb())) { |
| 442 modifiableState().setUnparsedFillColor(colorString); | 168 modifiableState().setUnparsedFillColor(colorString); |
| 443 return; | 169 return; |
| 444 } | 170 } |
| 445 canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb()); | 171 canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb()); |
| 446 } else if (style.isCanvasGradient()) { | 172 } else if (style.isCanvasGradient()) { |
| 447 canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient(
)); | 173 canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient(
)); |
| 448 } else if (style.isCanvasPattern()) { | 174 } else if (style.isCanvasPattern()) { |
| 449 CanvasPattern* canvasPattern = style.getAsCanvasPattern(); | 175 CanvasPattern* canvasPattern = style.getAsCanvasPattern(); |
| 450 | 176 |
| 451 if (canvas()->originClean() && !canvasPattern->originClean()) | 177 if (originClean() && !canvasPattern->originClean()) |
| 452 canvas()->setOriginTainted(); | 178 setOriginTainted(); |
| 453 if (canvasPattern->pattern()->isTextureBacked()) | 179 if (canvasPattern->pattern()->isTextureBacked()) |
| 454 canvas()->disableDeferral(DisableDeferralReasonUsingTextureBackedPat
tern); | 180 disableDeferral(DisableDeferralReasonUsingTextureBackedPattern); |
| 455 canvasStyle = CanvasStyle::createFromPattern(canvasPattern); | 181 canvasStyle = CanvasStyle::createFromPattern(canvasPattern); |
| 456 } | 182 } |
| 457 | 183 |
| 458 ASSERT(canvasStyle); | 184 ASSERT(canvasStyle); |
| 459 modifiableState().setFillStyle(canvasStyle); | 185 modifiableState().setFillStyle(canvasStyle); |
| 460 modifiableState().setUnparsedFillColor(colorString); | 186 modifiableState().setUnparsedFillColor(colorString); |
| 461 modifiableState().clearResolvedFilter(); | 187 modifiableState().clearResolvedFilter(); |
| 462 } | 188 } |
| 463 | 189 |
| 464 double CanvasRenderingContext2D::lineWidth() const | 190 double BaseRenderingContext2D::lineWidth() const |
| 465 { | 191 { |
| 466 return state().lineWidth(); | 192 return state().lineWidth(); |
| 467 } | 193 } |
| 468 | 194 |
| 469 void CanvasRenderingContext2D::setLineWidth(double width) | 195 void BaseRenderingContext2D::setLineWidth(double width) |
| 470 { | 196 { |
| 471 if (!std::isfinite(width) || width <= 0) | 197 if (!std::isfinite(width) || width <= 0) |
| 472 return; | 198 return; |
| 473 if (state().lineWidth() == width) | 199 if (state().lineWidth() == width) |
| 474 return; | 200 return; |
| 475 modifiableState().setLineWidth(width); | 201 modifiableState().setLineWidth(width); |
| 476 } | 202 } |
| 477 | 203 |
| 478 String CanvasRenderingContext2D::lineCap() const | 204 String BaseRenderingContext2D::lineCap() const |
| 479 { | 205 { |
| 480 return lineCapName(state().lineCap()); | 206 return lineCapName(state().lineCap()); |
| 481 } | 207 } |
| 482 | 208 |
| 483 void CanvasRenderingContext2D::setLineCap(const String& s) | 209 void BaseRenderingContext2D::setLineCap(const String& s) |
| 484 { | 210 { |
| 485 LineCap cap; | 211 LineCap cap; |
| 486 if (!parseLineCap(s, cap)) | 212 if (!parseLineCap(s, cap)) |
| 487 return; | 213 return; |
| 488 if (state().lineCap() == cap) | 214 if (state().lineCap() == cap) |
| 489 return; | 215 return; |
| 490 modifiableState().setLineCap(cap); | 216 modifiableState().setLineCap(cap); |
| 491 } | 217 } |
| 492 | 218 |
| 493 String CanvasRenderingContext2D::lineJoin() const | 219 String BaseRenderingContext2D::lineJoin() const |
| 494 { | 220 { |
| 495 return lineJoinName(state().lineJoin()); | 221 return lineJoinName(state().lineJoin()); |
| 496 } | 222 } |
| 497 | 223 |
| 498 void CanvasRenderingContext2D::setLineJoin(const String& s) | 224 void BaseRenderingContext2D::setLineJoin(const String& s) |
| 499 { | 225 { |
| 500 LineJoin join; | 226 LineJoin join; |
| 501 if (!parseLineJoin(s, join)) | 227 if (!parseLineJoin(s, join)) |
| 502 return; | 228 return; |
| 503 if (state().lineJoin() == join) | 229 if (state().lineJoin() == join) |
| 504 return; | 230 return; |
| 505 modifiableState().setLineJoin(join); | 231 modifiableState().setLineJoin(join); |
| 506 } | 232 } |
| 507 | 233 |
| 508 double CanvasRenderingContext2D::miterLimit() const | 234 double BaseRenderingContext2D::miterLimit() const |
| 509 { | 235 { |
| 510 return state().miterLimit(); | 236 return state().miterLimit(); |
| 511 } | 237 } |
| 512 | 238 |
| 513 void CanvasRenderingContext2D::setMiterLimit(double limit) | 239 void BaseRenderingContext2D::setMiterLimit(double limit) |
| 514 { | 240 { |
| 515 if (!std::isfinite(limit) || limit <= 0) | 241 if (!std::isfinite(limit) || limit <= 0) |
| 516 return; | 242 return; |
| 517 if (state().miterLimit() == limit) | 243 if (state().miterLimit() == limit) |
| 518 return; | 244 return; |
| 519 modifiableState().setMiterLimit(limit); | 245 modifiableState().setMiterLimit(limit); |
| 520 } | 246 } |
| 521 | 247 |
| 522 double CanvasRenderingContext2D::shadowOffsetX() const | 248 double BaseRenderingContext2D::shadowOffsetX() const |
| 523 { | 249 { |
| 524 return state().shadowOffset().width(); | 250 return state().shadowOffset().width(); |
| 525 } | 251 } |
| 526 | 252 |
| 527 void CanvasRenderingContext2D::setShadowOffsetX(double x) | 253 void BaseRenderingContext2D::setShadowOffsetX(double x) |
| 528 { | 254 { |
| 529 if (!std::isfinite(x)) | 255 if (!std::isfinite(x)) |
| 530 return; | 256 return; |
| 531 if (state().shadowOffset().width() == x) | 257 if (state().shadowOffset().width() == x) |
| 532 return; | 258 return; |
| 533 modifiableState().setShadowOffsetX(x); | 259 modifiableState().setShadowOffsetX(x); |
| 534 } | 260 } |
| 535 | 261 |
| 536 double CanvasRenderingContext2D::shadowOffsetY() const | 262 double BaseRenderingContext2D::shadowOffsetY() const |
| 537 { | 263 { |
| 538 return state().shadowOffset().height(); | 264 return state().shadowOffset().height(); |
| 539 } | 265 } |
| 540 | 266 |
| 541 void CanvasRenderingContext2D::setShadowOffsetY(double y) | 267 void BaseRenderingContext2D::setShadowOffsetY(double y) |
| 542 { | 268 { |
| 543 if (!std::isfinite(y)) | 269 if (!std::isfinite(y)) |
| 544 return; | 270 return; |
| 545 if (state().shadowOffset().height() == y) | 271 if (state().shadowOffset().height() == y) |
| 546 return; | 272 return; |
| 547 modifiableState().setShadowOffsetY(y); | 273 modifiableState().setShadowOffsetY(y); |
| 548 } | 274 } |
| 549 | 275 |
| 550 double CanvasRenderingContext2D::shadowBlur() const | 276 double BaseRenderingContext2D::shadowBlur() const |
| 551 { | 277 { |
| 552 return state().shadowBlur(); | 278 return state().shadowBlur(); |
| 553 } | 279 } |
| 554 | 280 |
| 555 void CanvasRenderingContext2D::setShadowBlur(double blur) | 281 void BaseRenderingContext2D::setShadowBlur(double blur) |
| 556 { | 282 { |
| 557 if (!std::isfinite(blur) || blur < 0) | 283 if (!std::isfinite(blur) || blur < 0) |
| 558 return; | 284 return; |
| 559 if (state().shadowBlur() == blur) | 285 if (state().shadowBlur() == blur) |
| 560 return; | 286 return; |
| 561 modifiableState().setShadowBlur(blur); | 287 modifiableState().setShadowBlur(blur); |
| 562 } | 288 } |
| 563 | 289 |
| 564 String CanvasRenderingContext2D::shadowColor() const | 290 String BaseRenderingContext2D::shadowColor() const |
| 565 { | 291 { |
| 566 return Color(state().shadowColor()).serialized(); | 292 return Color(state().shadowColor()).serialized(); |
| 567 } | 293 } |
| 568 | 294 |
| 569 void CanvasRenderingContext2D::setShadowColor(const String& colorString) | 295 void BaseRenderingContext2D::setShadowColor(const String& colorString) |
| 570 { | 296 { |
| 571 Color color; | 297 Color color; |
| 572 if (!parseColorOrCurrentColor(color, colorString, canvas())) | 298 if (!parseColorOrCurrentColor(color, colorString)) |
| 573 return; | 299 return; |
| 574 if (state().shadowColor() == color) | 300 if (state().shadowColor() == color) |
| 575 return; | 301 return; |
| 576 modifiableState().setShadowColor(color.rgb()); | 302 modifiableState().setShadowColor(color.rgb()); |
| 577 } | 303 } |
| 578 | 304 |
| 579 const Vector<double>& CanvasRenderingContext2D::getLineDash() const | 305 const Vector<double>& BaseRenderingContext2D::getLineDash() const |
| 580 { | 306 { |
| 581 return state().lineDash(); | 307 return state().lineDash(); |
| 582 } | 308 } |
| 583 | 309 |
| 584 static bool lineDashSequenceIsValid(const Vector<double>& dash) | 310 static bool lineDashSequenceIsValid(const Vector<double>& dash) |
| 585 { | 311 { |
| 586 for (size_t i = 0; i < dash.size(); i++) { | 312 for (size_t i = 0; i < dash.size(); i++) { |
| 587 if (!std::isfinite(dash[i]) || dash[i] < 0) | 313 if (!std::isfinite(dash[i]) || dash[i] < 0) |
| 588 return false; | 314 return false; |
| 589 } | 315 } |
| 590 return true; | 316 return true; |
| 591 } | 317 } |
| 592 | 318 |
| 593 void CanvasRenderingContext2D::setLineDash(const Vector<double>& dash) | 319 void BaseRenderingContext2D::setLineDash(const Vector<double>& dash) |
| 594 { | 320 { |
| 595 if (!lineDashSequenceIsValid(dash)) | 321 if (!lineDashSequenceIsValid(dash)) |
| 596 return; | 322 return; |
| 597 modifiableState().setLineDash(dash); | 323 modifiableState().setLineDash(dash); |
| 598 } | 324 } |
| 599 | 325 |
| 600 double CanvasRenderingContext2D::lineDashOffset() const | 326 double BaseRenderingContext2D::lineDashOffset() const |
| 601 { | 327 { |
| 602 return state().lineDashOffset(); | 328 return state().lineDashOffset(); |
| 603 } | 329 } |
| 604 | 330 |
| 605 void CanvasRenderingContext2D::setLineDashOffset(double offset) | 331 void BaseRenderingContext2D::setLineDashOffset(double offset) |
| 606 { | 332 { |
| 607 if (!std::isfinite(offset) || state().lineDashOffset() == offset) | 333 if (!std::isfinite(offset) || state().lineDashOffset() == offset) |
| 608 return; | 334 return; |
| 609 modifiableState().setLineDashOffset(offset); | 335 modifiableState().setLineDashOffset(offset); |
| 610 } | 336 } |
| 611 | 337 |
| 612 double CanvasRenderingContext2D::globalAlpha() const | 338 double BaseRenderingContext2D::globalAlpha() const |
| 613 { | 339 { |
| 614 return state().globalAlpha(); | 340 return state().globalAlpha(); |
| 615 } | 341 } |
| 616 | 342 |
| 617 void CanvasRenderingContext2D::setGlobalAlpha(double alpha) | 343 void BaseRenderingContext2D::setGlobalAlpha(double alpha) |
| 618 { | 344 { |
| 619 if (!(alpha >= 0 && alpha <= 1)) | 345 if (!(alpha >= 0 && alpha <= 1)) |
| 620 return; | 346 return; |
| 621 if (state().globalAlpha() == alpha) | 347 if (state().globalAlpha() == alpha) |
| 622 return; | 348 return; |
| 623 modifiableState().setGlobalAlpha(alpha); | 349 modifiableState().setGlobalAlpha(alpha); |
| 624 } | 350 } |
| 625 | 351 |
| 626 bool CanvasRenderingContext2D::shouldAntialias() const | 352 String BaseRenderingContext2D::globalCompositeOperation() const |
| 627 { | |
| 628 return state().shouldAntialias(); | |
| 629 } | |
| 630 | |
| 631 void CanvasRenderingContext2D::setShouldAntialias(bool doAA) | |
| 632 { | |
| 633 modifiableState().setShouldAntialias(doAA); | |
| 634 } | |
| 635 | |
| 636 String CanvasRenderingContext2D::globalCompositeOperation() const | |
| 637 { | 353 { |
| 638 return compositeOperatorName(compositeOperatorFromSkia(state().globalComposi
te()), blendModeFromSkia(state().globalComposite())); | 354 return compositeOperatorName(compositeOperatorFromSkia(state().globalComposi
te()), blendModeFromSkia(state().globalComposite())); |
| 639 } | 355 } |
| 640 | 356 |
| 641 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operati
on) | 357 void BaseRenderingContext2D::setGlobalCompositeOperation(const String& operation
) |
| 642 { | 358 { |
| 643 CompositeOperator op = CompositeSourceOver; | 359 CompositeOperator op = CompositeSourceOver; |
| 644 WebBlendMode blendMode = WebBlendModeNormal; | 360 WebBlendMode blendMode = WebBlendModeNormal; |
| 645 if (!parseCompositeAndBlendOperator(operation, op, blendMode)) | 361 if (!parseCompositeAndBlendOperator(operation, op, blendMode)) |
| 646 return; | 362 return; |
| 647 SkXfermode::Mode xfermode = WebCoreCompositeToSkiaComposite(op, blendMode); | 363 SkXfermode::Mode xfermode = WebCoreCompositeToSkiaComposite(op, blendMode); |
| 648 if (state().globalComposite() == xfermode) | 364 if (state().globalComposite() == xfermode) |
| 649 return; | 365 return; |
| 650 modifiableState().setGlobalComposite(xfermode); | 366 modifiableState().setGlobalComposite(xfermode); |
| 651 } | 367 } |
| 652 | 368 |
| 653 String CanvasRenderingContext2D::filter() const | 369 String BaseRenderingContext2D::filter() const |
| 654 { | 370 { |
| 655 return state().unparsedFilter(); | 371 return state().unparsedFilter(); |
| 656 } | 372 } |
| 657 | 373 |
| 658 void CanvasRenderingContext2D::setFilter(const String& filterString) | 374 void BaseRenderingContext2D::setFilter(const String& filterString) |
| 659 { | 375 { |
| 660 if (filterString == state().unparsedFilter()) | 376 if (filterString == state().unparsedFilter()) |
| 661 return; | 377 return; |
| 662 | 378 |
| 663 RefPtrWillBeRawPtr<CSSValue> filterValue = CSSParser::parseSingleValue(CSSPr
opertyWebkitFilter, filterString, CSSParserContext(HTMLStandardMode, 0)); | 379 RefPtrWillBeRawPtr<CSSValue> filterValue = CSSParser::parseSingleValue(CSSPr
opertyWebkitFilter, filterString, CSSParserContext(HTMLStandardMode, 0)); |
| 664 | 380 |
| 665 if (!filterValue || filterValue->isInitialValue() || filterValue->isInherite
dValue()) | 381 if (!filterValue || filterValue->isInitialValue() || filterValue->isInherite
dValue()) |
| 666 return; | 382 return; |
| 667 | 383 |
| 668 modifiableState().setUnparsedFilter(filterString); | 384 modifiableState().setUnparsedFilter(filterString); |
| 669 modifiableState().setFilter(filterValue.release()); | 385 modifiableState().setFilter(filterValue.release()); |
| 670 } | 386 } |
| 671 | 387 |
| 672 PassRefPtrWillBeRawPtr<SVGMatrixTearOff> CanvasRenderingContext2D::currentTransf
orm() const | 388 PassRefPtrWillBeRawPtr<SVGMatrixTearOff> BaseRenderingContext2D::currentTransfor
m() const |
| 673 { | 389 { |
| 674 return SVGMatrixTearOff::create(state().transform()); | 390 return SVGMatrixTearOff::create(state().transform()); |
| 675 } | 391 } |
| 676 | 392 |
| 677 void CanvasRenderingContext2D::setCurrentTransform(PassRefPtrWillBeRawPtr<SVGMat
rixTearOff> passMatrixTearOff) | 393 void BaseRenderingContext2D::setCurrentTransform(PassRefPtrWillBeRawPtr<SVGMatri
xTearOff> passMatrixTearOff) |
| 678 { | 394 { |
| 679 RefPtrWillBeRawPtr<SVGMatrixTearOff> matrixTearOff = passMatrixTearOff; | 395 RefPtrWillBeRawPtr<SVGMatrixTearOff> matrixTearOff = passMatrixTearOff; |
| 680 const AffineTransform& transform = matrixTearOff->value(); | 396 const AffineTransform& transform = matrixTearOff->value(); |
| 681 setTransform(transform.a(), transform.b(), transform.c(), transform.d(), tra
nsform.e(), transform.f()); | 397 setTransform(transform.a(), transform.b(), transform.c(), transform.d(), tra
nsform.e(), transform.f()); |
| 682 } | 398 } |
| 683 | 399 |
| 684 void CanvasRenderingContext2D::scale(double sx, double sy) | 400 void BaseRenderingContext2D::scale(double sx, double sy) |
| 685 { | 401 { |
| 686 SkCanvas* c = drawingCanvas(); | 402 SkCanvas* c = drawingCanvas(); |
| 687 if (!c) | 403 if (!c) |
| 688 return; | 404 return; |
| 689 | 405 |
| 690 if (!std::isfinite(sx) || !std::isfinite(sy)) | 406 if (!std::isfinite(sx) || !std::isfinite(sy)) |
| 691 return; | 407 return; |
| 692 | 408 |
| 693 AffineTransform newTransform = state().transform(); | 409 AffineTransform newTransform = state().transform(); |
| 694 newTransform.scaleNonUniform(sx, sy); | 410 newTransform.scaleNonUniform(sx, sy); |
| 695 if (state().transform() == newTransform) | 411 if (state().transform() == newTransform) |
| 696 return; | 412 return; |
| 697 | 413 |
| 698 modifiableState().setTransform(newTransform); | 414 modifiableState().setTransform(newTransform); |
| 699 if (!state().isTransformInvertible()) | 415 if (!state().isTransformInvertible()) |
| 700 return; | 416 return; |
| 701 | 417 |
| 702 c->scale(sx, sy); | 418 c->scale(sx, sy); |
| 703 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy)); | 419 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy)); |
| 704 } | 420 } |
| 705 | 421 |
| 706 void CanvasRenderingContext2D::rotate(double angleInRadians) | 422 void BaseRenderingContext2D::rotate(double angleInRadians) |
| 707 { | 423 { |
| 708 SkCanvas* c = drawingCanvas(); | 424 SkCanvas* c = drawingCanvas(); |
| 709 if (!c) | 425 if (!c) |
| 710 return; | 426 return; |
| 711 | 427 |
| 712 if (!std::isfinite(angleInRadians)) | 428 if (!std::isfinite(angleInRadians)) |
| 713 return; | 429 return; |
| 714 | 430 |
| 715 AffineTransform newTransform = state().transform(); | 431 AffineTransform newTransform = state().transform(); |
| 716 newTransform.rotateRadians(angleInRadians); | 432 newTransform.rotateRadians(angleInRadians); |
| 717 if (state().transform() == newTransform) | 433 if (state().transform() == newTransform) |
| 718 return; | 434 return; |
| 719 | 435 |
| 720 modifiableState().setTransform(newTransform); | 436 modifiableState().setTransform(newTransform); |
| 721 if (!state().isTransformInvertible()) | 437 if (!state().isTransformInvertible()) |
| 722 return; | 438 return; |
| 723 c->rotate(angleInRadians * (180.0 / piFloat)); | 439 c->rotate(angleInRadians * (180.0 / piFloat)); |
| 724 m_path.transform(AffineTransform().rotateRadians(-angleInRadians)); | 440 m_path.transform(AffineTransform().rotateRadians(-angleInRadians)); |
| 725 } | 441 } |
| 726 | 442 |
| 727 void CanvasRenderingContext2D::translate(double tx, double ty) | 443 void BaseRenderingContext2D::translate(double tx, double ty) |
| 728 { | 444 { |
| 729 SkCanvas* c = drawingCanvas(); | 445 SkCanvas* c = drawingCanvas(); |
| 730 if (!c) | 446 if (!c) |
| 731 return; | 447 return; |
| 732 if (!state().isTransformInvertible()) | 448 if (!state().isTransformInvertible()) |
| 733 return; | 449 return; |
| 734 | 450 |
| 735 if (!std::isfinite(tx) || !std::isfinite(ty)) | 451 if (!std::isfinite(tx) || !std::isfinite(ty)) |
| 736 return; | 452 return; |
| 737 | 453 |
| 738 AffineTransform newTransform = state().transform(); | 454 AffineTransform newTransform = state().transform(); |
| 739 newTransform.translate(tx, ty); | 455 newTransform.translate(tx, ty); |
| 740 if (state().transform() == newTransform) | 456 if (state().transform() == newTransform) |
| 741 return; | 457 return; |
| 742 | 458 |
| 743 modifiableState().setTransform(newTransform); | 459 modifiableState().setTransform(newTransform); |
| 744 if (!state().isTransformInvertible()) | 460 if (!state().isTransformInvertible()) |
| 745 return; | 461 return; |
| 746 c->translate(tx, ty); | 462 c->translate(tx, ty); |
| 747 m_path.transform(AffineTransform().translate(-tx, -ty)); | 463 m_path.transform(AffineTransform().translate(-tx, -ty)); |
| 748 } | 464 } |
| 749 | 465 |
| 750 void CanvasRenderingContext2D::transform(double m11, double m12, double m21, dou
ble m22, double dx, double dy) | 466 void BaseRenderingContext2D::transform(double m11, double m12, double m21, doubl
e m22, double dx, double dy) |
| 751 { | 467 { |
| 752 SkCanvas* c = drawingCanvas(); | 468 SkCanvas* c = drawingCanvas(); |
| 753 if (!c) | 469 if (!c) |
| 754 return; | 470 return; |
| 755 | 471 |
| 756 if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std
::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy)) | 472 if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std
::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy)) |
| 757 return; | 473 return; |
| 758 | 474 |
| 759 AffineTransform transform(m11, m12, m21, m22, dx, dy); | 475 AffineTransform transform(m11, m12, m21, m22, dx, dy); |
| 760 AffineTransform newTransform = state().transform() * transform; | 476 AffineTransform newTransform = state().transform() * transform; |
| 761 if (state().transform() == newTransform) | 477 if (state().transform() == newTransform) |
| 762 return; | 478 return; |
| 763 | 479 |
| 764 modifiableState().setTransform(newTransform); | 480 modifiableState().setTransform(newTransform); |
| 765 if (!state().isTransformInvertible()) | 481 if (!state().isTransformInvertible()) |
| 766 return; | 482 return; |
| 767 | 483 |
| 768 c->concat(affineTransformToSkMatrix(transform)); | 484 c->concat(affineTransformToSkMatrix(transform)); |
| 769 m_path.transform(transform.inverse()); | 485 m_path.transform(transform.inverse()); |
| 770 } | 486 } |
| 771 | 487 |
| 772 void CanvasRenderingContext2D::resetTransform() | 488 void BaseRenderingContext2D::resetTransform() |
| 773 { | 489 { |
| 774 SkCanvas* c = drawingCanvas(); | 490 SkCanvas* c = drawingCanvas(); |
| 775 if (!c) | 491 if (!c) |
| 776 return; | 492 return; |
| 777 | 493 |
| 778 AffineTransform ctm = state().transform(); | 494 AffineTransform ctm = state().transform(); |
| 779 bool invertibleCTM = state().isTransformInvertible(); | 495 bool invertibleCTM = state().isTransformInvertible(); |
| 780 // It is possible that CTM is identity while CTM is not invertible. | 496 // It is possible that CTM is identity while CTM is not invertible. |
| 781 // When CTM becomes non-invertible, realizeSaves() can make CTM identity. | 497 // When CTM becomes non-invertible, realizeSaves() can make CTM identity. |
| 782 if (ctm.isIdentity() && invertibleCTM) | 498 if (ctm.isIdentity() && invertibleCTM) |
| 783 return; | 499 return; |
| 784 | 500 |
| 785 // resetTransform() resolves the non-invertible CTM state. | 501 // resetTransform() resolves the non-invertible CTM state. |
| 786 modifiableState().resetTransform(); | 502 modifiableState().resetTransform(); |
| 787 c->setMatrix(affineTransformToSkMatrix(canvas()->baseTransform())); | 503 c->setMatrix(affineTransformToSkMatrix(baseTransform())); |
| 788 | 504 |
| 789 if (invertibleCTM) | 505 if (invertibleCTM) |
| 790 m_path.transform(ctm); | 506 m_path.transform(ctm); |
| 791 // When else, do nothing because all transform methods didn't update m_path
when CTM became non-invertible. | 507 // When else, do nothing because all transform methods didn't update m_path
when CTM became non-invertible. |
| 792 // It means that resetTransform() restores m_path just before CTM became non
-invertible. | 508 // It means that resetTransform() restores m_path just before CTM became non
-invertible. |
| 793 } | 509 } |
| 794 | 510 |
| 795 void CanvasRenderingContext2D::setTransform(double m11, double m12, double m21,
double m22, double dx, double dy) | 511 void BaseRenderingContext2D::setTransform(double m11, double m12, double m21, do
uble m22, double dx, double dy) |
| 796 { | 512 { |
| 797 SkCanvas* c = drawingCanvas(); | 513 SkCanvas* c = drawingCanvas(); |
| 798 if (!c) | 514 if (!c) |
| 799 return; | 515 return; |
| 800 | 516 |
| 801 if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std
::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy)) | 517 if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std
::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy)) |
| 802 return; | 518 return; |
| 803 | 519 |
| 804 resetTransform(); | 520 resetTransform(); |
| 805 transform(m11, m12, m21, m22, dx, dy); | 521 transform(m11, m12, m21, m22, dx, dy); |
| 806 } | 522 } |
| 807 | 523 |
| 808 void CanvasRenderingContext2D::beginPath() | 524 void BaseRenderingContext2D::beginPath() |
| 809 { | 525 { |
| 810 m_path.clear(); | 526 m_path.clear(); |
| 811 } | 527 } |
| 812 | 528 |
| 813 static bool validateRectForCanvas(double& x, double& y, double& width, double& h
eight) | 529 static bool validateRectForCanvas(double& x, double& y, double& width, double& h
eight) |
| 814 { | 530 { |
| 815 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || !std:
:isfinite(height)) | 531 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || !std:
:isfinite(height)) |
| 816 return false; | 532 return false; |
| 817 | 533 |
| 818 if (!width && !height) | 534 if (!width && !height) |
| 819 return false; | 535 return false; |
| 820 | 536 |
| 821 if (width < 0) { | 537 if (width < 0) { |
| 822 width = -width; | 538 width = -width; |
| 823 x -= width; | 539 x -= width; |
| 824 } | 540 } |
| 825 | 541 |
| 826 if (height < 0) { | 542 if (height < 0) { |
| 827 height = -height; | 543 height = -height; |
| 828 y -= height; | 544 y -= height; |
| 829 } | 545 } |
| 830 | 546 |
| 831 return true; | 547 return true; |
| 832 } | 548 } |
| 833 | 549 |
| 834 static bool isFullCanvasCompositeMode(SkXfermode::Mode op) | 550 bool BaseRenderingContext2D::isFullCanvasCompositeMode(SkXfermode::Mode op) |
| 835 { | 551 { |
| 836 // See 4.8.11.1.3 Compositing | 552 // See 4.8.11.1.3 Compositing |
| 837 // CompositeSourceAtop and CompositeDestinationOut are not listed here as th
e platforms already | 553 // CompositeSourceAtop and CompositeDestinationOut are not listed here as th
e platforms already |
| 838 // implement the specification's behavior. | 554 // implement the specification's behavior. |
| 839 return op == SkXfermode::kSrcIn_Mode || op == SkXfermode::kSrcOut_Mode || op
== SkXfermode::kDstIn_Mode || op == SkXfermode::kDstATop_Mode; | 555 return op == SkXfermode::kSrcIn_Mode || op == SkXfermode::kSrcOut_Mode || op
== SkXfermode::kDstIn_Mode || op == SkXfermode::kDstATop_Mode; |
| 840 } | 556 } |
| 841 | 557 |
| 842 template<typename DrawFunc> | |
| 843 void CanvasRenderingContext2D::compositedDraw(const DrawFunc& drawFunc, SkCanvas
* c, CanvasRenderingContext2DState::PaintType paintType, CanvasRenderingContext2
DState::ImageType imageType) | |
| 844 { | |
| 845 SkImageFilter* filter = state().getFilter(canvas(), accessFont(), canvas()->
size(), this); | |
| 846 ASSERT(isFullCanvasCompositeMode(state().globalComposite()) || filter); | |
| 847 SkMatrix ctm = c->getTotalMatrix(); | |
| 848 c->resetMatrix(); | |
| 849 SkPaint compositePaint; | |
| 850 compositePaint.setXfermodeMode(state().globalComposite()); | |
| 851 if (state().shouldDrawShadows()) { | |
| 852 // unroll into two independently composited passes if drawing shadows | |
| 853 SkPaint shadowPaint = *state().getPaint(paintType, DrawShadowOnly, image
Type); | |
| 854 int saveCount = c->getSaveCount(); | |
| 855 if (filter) { | |
| 856 SkPaint filterPaint; | |
| 857 filterPaint.setImageFilter(filter); | |
| 858 // TODO(junov): crbug.com/502921 We could use primitive bounds if we
knew that the filter | |
| 859 // does not affect transparent black regions. | |
| 860 c->saveLayer(nullptr, &shadowPaint); | |
| 861 c->saveLayer(nullptr, &filterPaint); | |
| 862 SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroun
dOnly, imageType); | |
| 863 c->setMatrix(ctm); | |
| 864 drawFunc(c, &foregroundPaint); | |
| 865 } else { | |
| 866 ASSERT(isFullCanvasCompositeMode(state().globalComposite())); | |
| 867 c->saveLayer(nullptr, &compositePaint); | |
| 868 shadowPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); | |
| 869 c->setMatrix(ctm); | |
| 870 drawFunc(c, &shadowPaint); | |
| 871 } | |
| 872 c->restoreToCount(saveCount); | |
| 873 } | |
| 874 | |
| 875 compositePaint.setImageFilter(filter); | |
| 876 // TODO(junov): crbug.com/502921 We could use primitive bounds if we knew th
at the filter | |
| 877 // does not affect transparent black regions *and* !isFullCanvasCompositeMod
e | |
| 878 c->saveLayer(nullptr, &compositePaint); | |
| 879 SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroundOnly, i
mageType); | |
| 880 foregroundPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); | |
| 881 c->setMatrix(ctm); | |
| 882 drawFunc(c, &foregroundPaint); | |
| 883 c->restore(); | |
| 884 c->setMatrix(ctm); | |
| 885 } | |
| 886 | |
| 887 template<typename DrawFunc, typename ContainsFunc> | |
| 888 bool CanvasRenderingContext2D::draw(const DrawFunc& drawFunc, const ContainsFunc
& drawCoversClipBounds, const SkRect& bounds, CanvasRenderingContext2DState::Pai
ntType paintType, CanvasRenderingContext2DState::ImageType imageType) | |
| 889 { | |
| 890 if (!state().isTransformInvertible()) | |
| 891 return false; | |
| 892 | |
| 893 SkIRect clipBounds; | |
| 894 if (!drawingCanvas() || !drawingCanvas()->getClipDeviceBounds(&clipBounds)) | |
| 895 return false; | |
| 896 | |
| 897 // If gradient size is zero, then paint nothing. | |
| 898 CanvasStyle* style = state().style(paintType); | |
| 899 if (style) { | |
| 900 CanvasGradient* gradient = style->canvasGradient(); | |
| 901 if (gradient && gradient->gradient()->isZeroSize()) | |
| 902 return false; | |
| 903 } | |
| 904 | |
| 905 if (isFullCanvasCompositeMode(state().globalComposite()) || state().hasFilte
r(canvas(), accessFont(), canvas()->size(), this)) { | |
| 906 compositedDraw(drawFunc, drawingCanvas(), paintType, imageType); | |
| 907 didDraw(clipBounds); | |
| 908 } else if (state().globalComposite() == SkXfermode::kSrc_Mode) { | |
| 909 clearCanvas(); // takes care of checkOverdraw() | |
| 910 const SkPaint* paint = state().getPaint(paintType, DrawForegroundOnly, i
mageType); | |
| 911 drawFunc(drawingCanvas(), paint); | |
| 912 didDraw(clipBounds); | |
| 913 } else { | |
| 914 SkIRect dirtyRect; | |
| 915 if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) { | |
| 916 const SkPaint* paint = state().getPaint(paintType, DrawShadowAndFore
ground, imageType); | |
| 917 if (paintType != CanvasRenderingContext2DState::StrokePaintType && d
rawCoversClipBounds(clipBounds)) | |
| 918 checkOverdraw(bounds, paint, imageType, ClipFill); | |
| 919 drawFunc(drawingCanvas(), paint); | |
| 920 didDraw(dirtyRect); | |
| 921 } | |
| 922 } | |
| 923 return true; | |
| 924 } | |
| 925 | |
| 926 static bool isPathExpensive(const Path& path) | 558 static bool isPathExpensive(const Path& path) |
| 927 { | 559 { |
| 928 const SkPath& skPath = path.skPath(); | 560 const SkPath& skPath = path.skPath(); |
| 929 if (ExpensiveCanvasHeuristicParameters::ConcavePathsAreExpensive && !skPath.
isConvex()) | 561 if (ExpensiveCanvasHeuristicParameters::ConcavePathsAreExpensive && !skPath.
isConvex()) |
| 930 return true; | 562 return true; |
| 931 | 563 |
| 932 if (skPath.countPoints() > ExpensiveCanvasHeuristicParameters::ExpensivePath
PointCount) | 564 if (skPath.countPoints() > ExpensiveCanvasHeuristicParameters::ExpensivePath
PointCount) |
| 933 return true; | 565 return true; |
| 934 | 566 |
| 935 return false; | 567 return false; |
| 936 } | 568 } |
| 937 | 569 |
| 938 void CanvasRenderingContext2D::drawPathInternal(const Path& path, CanvasRenderin
gContext2DState::PaintType paintType, SkPath::FillType fillType) | 570 void BaseRenderingContext2D::drawPathInternal(const Path& path, CanvasRenderingC
ontext2DState::PaintType paintType, SkPath::FillType fillType) |
| 939 { | 571 { |
| 940 if (path.isEmpty()) | 572 if (path.isEmpty()) |
| 941 return; | 573 return; |
| 942 | 574 |
| 943 SkPath skPath = path.skPath(); | 575 SkPath skPath = path.skPath(); |
| 944 FloatRect bounds = path.boundingRect(); | 576 FloatRect bounds = path.boundingRect(); |
| 945 skPath.setFillType(fillType); | 577 skPath.setFillType(fillType); |
| 946 | 578 |
| 947 if (paintType == CanvasRenderingContext2DState::StrokePaintType) | 579 if (paintType == CanvasRenderingContext2DState::StrokePaintType) |
| 948 inflateStrokeRect(bounds); | 580 inflateStrokeRect(bounds); |
| 949 | 581 |
| 950 if (!drawingCanvas()) | 582 if (!drawingCanvas()) |
| 951 return; | 583 return; |
| 952 | 584 |
| 953 if (draw( | 585 if (draw( |
| 954 [&skPath, this](SkCanvas* c, const SkPaint* paint) // draw lambda | 586 [&skPath, this](SkCanvas* c, const SkPaint* paint) // draw lambda |
| 955 { | 587 { |
| 956 c->drawPath(skPath, *paint); | 588 c->drawPath(skPath, *paint); |
| 957 }, | 589 }, |
| 958 [](const SkIRect& rect) // overdraw test lambda | 590 [](const SkIRect& rect) // overdraw test lambda |
| 959 { | 591 { |
| 960 return false; | 592 return false; |
| 961 }, bounds, paintType)) { | 593 }, bounds, paintType)) { |
| 962 if (isPathExpensive(path)) { | 594 if (isPathExpensive(path)) { |
| 963 ImageBuffer* buffer = canvas()->buffer(); | 595 ImageBuffer* buffer = imageBuffer(); |
| 964 if (buffer) | 596 if (buffer) |
| 965 buffer->setHasExpensiveOp(); | 597 buffer->setHasExpensiveOp(); |
| 966 } | 598 } |
| 967 } | 599 } |
| 968 } | 600 } |
| 969 | 601 |
| 970 static SkPath::FillType parseWinding(const String& windingRuleString) | 602 static SkPath::FillType parseWinding(const String& windingRuleString) |
| 971 { | 603 { |
| 972 if (windingRuleString == "nonzero") | 604 if (windingRuleString == "nonzero") |
| 973 return SkPath::kWinding_FillType; | 605 return SkPath::kWinding_FillType; |
| 974 if (windingRuleString == "evenodd") | 606 if (windingRuleString == "evenodd") |
| 975 return SkPath::kEvenOdd_FillType; | 607 return SkPath::kEvenOdd_FillType; |
| 976 | 608 |
| 977 ASSERT_NOT_REACHED(); | 609 ASSERT_NOT_REACHED(); |
| 978 return SkPath::kEvenOdd_FillType; | 610 return SkPath::kEvenOdd_FillType; |
| 979 } | 611 } |
| 980 | 612 |
| 981 void CanvasRenderingContext2D::fill(const String& windingRuleString) | 613 void BaseRenderingContext2D::fill(const String& windingRuleString) |
| 982 { | 614 { |
| 983 drawPathInternal(m_path, CanvasRenderingContext2DState::FillPaintType, parse
Winding(windingRuleString)); | 615 drawPathInternal(m_path, CanvasRenderingContext2DState::FillPaintType, parse
Winding(windingRuleString)); |
| 984 } | 616 } |
| 985 | 617 |
| 986 void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleSt
ring) | 618 void BaseRenderingContext2D::fill(Path2D* domPath, const String& windingRuleStri
ng) |
| 987 { | 619 { |
| 988 drawPathInternal(domPath->path(), CanvasRenderingContext2DState::FillPaintTy
pe, parseWinding(windingRuleString)); | 620 drawPathInternal(domPath->path(), CanvasRenderingContext2DState::FillPaintTy
pe, parseWinding(windingRuleString)); |
| 989 } | 621 } |
| 990 | 622 |
| 991 void CanvasRenderingContext2D::stroke() | 623 void BaseRenderingContext2D::stroke() |
| 992 { | 624 { |
| 993 drawPathInternal(m_path, CanvasRenderingContext2DState::StrokePaintType); | 625 drawPathInternal(m_path, CanvasRenderingContext2DState::StrokePaintType); |
| 994 } | 626 } |
| 995 | 627 |
| 996 void CanvasRenderingContext2D::stroke(Path2D* domPath) | 628 void BaseRenderingContext2D::stroke(Path2D* domPath) |
| 997 { | 629 { |
| 998 drawPathInternal(domPath->path(), CanvasRenderingContext2DState::StrokePaint
Type); | 630 drawPathInternal(domPath->path(), CanvasRenderingContext2DState::StrokePaint
Type); |
| 999 } | 631 } |
| 1000 | 632 |
| 1001 void CanvasRenderingContext2D::fillRect(double x, double y, double width, double
height) | 633 void BaseRenderingContext2D::fillRect(double x, double y, double width, double h
eight) |
| 1002 { | 634 { |
| 1003 if (!validateRectForCanvas(x, y, width, height)) | 635 if (!validateRectForCanvas(x, y, width, height)) |
| 1004 return; | 636 return; |
| 1005 | 637 |
| 1006 if (!drawingCanvas()) | 638 if (!drawingCanvas()) |
| 1007 return; | 639 return; |
| 1008 | 640 |
| 1009 SkRect rect = SkRect::MakeXYWH(x, y, width, height); | 641 SkRect rect = SkRect::MakeXYWH(x, y, width, height); |
| 1010 draw( | 642 draw( |
| 1011 [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda | 643 [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1026 SkPath path; | 658 SkPath path; |
| 1027 path.moveTo(rect.x(), rect.y()); | 659 path.moveTo(rect.x(), rect.y()); |
| 1028 path.lineTo(rect.maxX(), rect.maxY()); | 660 path.lineTo(rect.maxX(), rect.maxY()); |
| 1029 path.close(); | 661 path.close(); |
| 1030 canvas->drawPath(path, *paint); | 662 canvas->drawPath(path, *paint); |
| 1031 return; | 663 return; |
| 1032 } | 664 } |
| 1033 canvas->drawRect(rect, *paint); | 665 canvas->drawRect(rect, *paint); |
| 1034 } | 666 } |
| 1035 | 667 |
| 1036 void CanvasRenderingContext2D::strokeRect(double x, double y, double width, doub
le height) | 668 void BaseRenderingContext2D::strokeRect(double x, double y, double width, double
height) |
| 1037 { | 669 { |
| 1038 if (!validateRectForCanvas(x, y, width, height)) | 670 if (!validateRectForCanvas(x, y, width, height)) |
| 1039 return; | 671 return; |
| 1040 | 672 |
| 1041 if (!drawingCanvas()) | 673 if (!drawingCanvas()) |
| 1042 return; | 674 return; |
| 1043 | 675 |
| 1044 SkRect rect = SkRect::MakeXYWH(x, y, width, height); | 676 SkRect rect = SkRect::MakeXYWH(x, y, width, height); |
| 1045 FloatRect bounds = rect; | 677 FloatRect bounds = rect; |
| 1046 inflateStrokeRect(bounds); | 678 inflateStrokeRect(bounds); |
| 1047 draw( | 679 draw( |
| 1048 [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda | 680 [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda |
| 1049 { | 681 { |
| 1050 strokeRectOnCanvas(rect, c, paint); | 682 strokeRectOnCanvas(rect, c, paint); |
| 1051 }, | 683 }, |
| 1052 [](const SkIRect& clipBounds) // overdraw test lambda | 684 [](const SkIRect& clipBounds) // overdraw test lambda |
| 1053 { | 685 { |
| 1054 return false; | 686 return false; |
| 1055 }, bounds, CanvasRenderingContext2DState::StrokePaintType); | 687 }, bounds, CanvasRenderingContext2DState::StrokePaintType); |
| 1056 } | 688 } |
| 1057 | 689 |
| 1058 void CanvasRenderingContext2D::clipInternal(const Path& path, const String& wind
ingRuleString) | 690 void BaseRenderingContext2D::clipInternal(const Path& path, const String& windin
gRuleString) |
| 1059 { | 691 { |
| 1060 SkCanvas* c = drawingCanvas(); | 692 SkCanvas* c = drawingCanvas(); |
| 1061 if (!c) { | 693 if (!c) { |
| 1062 return; | 694 return; |
| 1063 } | 695 } |
| 1064 if (!state().isTransformInvertible()) { | 696 if (!state().isTransformInvertible()) { |
| 1065 return; | 697 return; |
| 1066 } | 698 } |
| 1067 | 699 |
| 1068 SkPath skPath = path.skPath(); | 700 SkPath skPath = path.skPath(); |
| 1069 skPath.setFillType(parseWinding(windingRuleString)); | 701 skPath.setFillType(parseWinding(windingRuleString)); |
| 1070 modifiableState().clipPath(skPath, m_clipAntialiasing); | 702 modifiableState().clipPath(skPath, m_clipAntialiasing); |
| 1071 c->clipPath(skPath, SkRegion::kIntersect_Op, m_clipAntialiasing == AntiAlias
ed); | 703 c->clipPath(skPath, SkRegion::kIntersect_Op, m_clipAntialiasing == AntiAlias
ed); |
| 1072 if (ExpensiveCanvasHeuristicParameters::ComplexClipsAreExpensive && !skPath.
isRect(0) && canvas()->hasImageBuffer()) { | 704 if (ExpensiveCanvasHeuristicParameters::ComplexClipsAreExpensive && !skPath.
isRect(0) && hasImageBuffer()) { |
| 1073 canvas()->buffer()->setHasExpensiveOp(); | 705 imageBuffer()->setHasExpensiveOp(); |
| 1074 } | 706 } |
| 1075 } | 707 } |
| 1076 | 708 |
| 1077 void CanvasRenderingContext2D::clip(const String& windingRuleString) | 709 void BaseRenderingContext2D::clip(const String& windingRuleString) |
| 1078 { | 710 { |
| 1079 clipInternal(m_path, windingRuleString); | 711 clipInternal(m_path, windingRuleString); |
| 1080 } | 712 } |
| 1081 | 713 |
| 1082 void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleSt
ring) | 714 void BaseRenderingContext2D::clip(Path2D* domPath, const String& windingRuleStri
ng) |
| 1083 { | 715 { |
| 1084 clipInternal(domPath->path(), windingRuleString); | 716 clipInternal(domPath->path(), windingRuleString); |
| 1085 } | 717 } |
| 1086 | 718 |
| 1087 bool CanvasRenderingContext2D::isPointInPath(const double x, const double y, con
st String& windingRuleString) | 719 bool BaseRenderingContext2D::isPointInPath(const double x, const double y, const
String& windingRuleString) |
| 1088 { | 720 { |
| 1089 return isPointInPathInternal(m_path, x, y, windingRuleString); | 721 return isPointInPathInternal(m_path, x, y, windingRuleString); |
| 1090 } | 722 } |
| 1091 | 723 |
| 1092 bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const double x, co
nst double y, const String& windingRuleString) | 724 bool BaseRenderingContext2D::isPointInPath(Path2D* domPath, const double x, cons
t double y, const String& windingRuleString) |
| 1093 { | 725 { |
| 1094 return isPointInPathInternal(domPath->path(), x, y, windingRuleString); | 726 return isPointInPathInternal(domPath->path(), x, y, windingRuleString); |
| 1095 } | 727 } |
| 1096 | 728 |
| 1097 bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, const dou
ble x, const double y, const String& windingRuleString) | 729 bool BaseRenderingContext2D::isPointInPathInternal(const Path& path, const doubl
e x, const double y, const String& windingRuleString) |
| 1098 { | 730 { |
| 1099 SkCanvas* c = drawingCanvas(); | 731 SkCanvas* c = drawingCanvas(); |
| 1100 if (!c) | 732 if (!c) |
| 1101 return false; | 733 return false; |
| 1102 if (!state().isTransformInvertible()) | 734 if (!state().isTransformInvertible()) |
| 1103 return false; | 735 return false; |
| 1104 | 736 |
| 1105 FloatPoint point(x, y); | 737 FloatPoint point(x, y); |
| 1106 if (!std::isfinite(point.x()) || !std::isfinite(point.y())) | 738 if (!std::isfinite(point.x()) || !std::isfinite(point.y())) |
| 1107 return false; | 739 return false; |
| 1108 AffineTransform ctm = state().transform(); | 740 AffineTransform ctm = state().transform(); |
| 1109 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); | 741 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); |
| 1110 | 742 |
| 1111 return path.contains(transformedPoint, SkFillTypeToWindRule(parseWinding(win
dingRuleString))); | 743 return path.contains(transformedPoint, SkFillTypeToWindRule(parseWinding(win
dingRuleString))); |
| 1112 } | 744 } |
| 1113 | 745 |
| 1114 bool CanvasRenderingContext2D::isPointInStroke(const double x, const double y) | 746 bool BaseRenderingContext2D::isPointInStroke(const double x, const double y) |
| 1115 { | 747 { |
| 1116 return isPointInStrokeInternal(m_path, x, y); | 748 return isPointInStrokeInternal(m_path, x, y); |
| 1117 } | 749 } |
| 1118 | 750 |
| 1119 bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const double x,
const double y) | 751 bool BaseRenderingContext2D::isPointInStroke(Path2D* domPath, const double x, co
nst double y) |
| 1120 { | 752 { |
| 1121 return isPointInStrokeInternal(domPath->path(), x, y); | 753 return isPointInStrokeInternal(domPath->path(), x, y); |
| 1122 } | 754 } |
| 1123 | 755 |
| 1124 bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, const d
ouble x, const double y) | 756 bool BaseRenderingContext2D::isPointInStrokeInternal(const Path& path, const dou
ble x, const double y) |
| 1125 { | 757 { |
| 1126 SkCanvas* c = drawingCanvas(); | 758 SkCanvas* c = drawingCanvas(); |
| 1127 if (!c) | 759 if (!c) |
| 1128 return false; | 760 return false; |
| 1129 if (!state().isTransformInvertible()) | 761 if (!state().isTransformInvertible()) |
| 1130 return false; | 762 return false; |
| 1131 | 763 |
| 1132 FloatPoint point(x, y); | 764 FloatPoint point(x, y); |
| 1133 if (!std::isfinite(point.x()) || !std::isfinite(point.y())) | 765 if (!std::isfinite(point.x()) || !std::isfinite(point.y())) |
| 1134 return false; | 766 return false; |
| 1135 AffineTransform ctm = state().transform(); | 767 AffineTransform ctm = state().transform(); |
| 1136 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); | 768 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); |
| 1137 | 769 |
| 1138 StrokeData strokeData; | 770 StrokeData strokeData; |
| 1139 strokeData.setThickness(state().lineWidth()); | 771 strokeData.setThickness(state().lineWidth()); |
| 1140 strokeData.setLineCap(state().lineCap()); | 772 strokeData.setLineCap(state().lineCap()); |
| 1141 strokeData.setLineJoin(state().lineJoin()); | 773 strokeData.setLineJoin(state().lineJoin()); |
| 1142 strokeData.setMiterLimit(state().miterLimit()); | 774 strokeData.setMiterLimit(state().miterLimit()); |
| 1143 Vector<float> lineDash(state().lineDash().size()); | 775 Vector<float> lineDash(state().lineDash().size()); |
| 1144 std::copy(state().lineDash().begin(), state().lineDash().end(), lineDash.beg
in()); | 776 std::copy(state().lineDash().begin(), state().lineDash().end(), lineDash.beg
in()); |
| 1145 strokeData.setLineDash(lineDash, state().lineDashOffset()); | 777 strokeData.setLineDash(lineDash, state().lineDashOffset()); |
| 1146 return path.strokeContains(transformedPoint, strokeData); | 778 return path.strokeContains(transformedPoint, strokeData); |
| 1147 } | 779 } |
| 1148 | 780 |
| 1149 void CanvasRenderingContext2D::scrollPathIntoView() | 781 void BaseRenderingContext2D::clearRect(double x, double y, double width, double
height) |
| 1150 { | |
| 1151 scrollPathIntoViewInternal(m_path); | |
| 1152 } | |
| 1153 | |
| 1154 void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d) | |
| 1155 { | |
| 1156 scrollPathIntoViewInternal(path2d->path()); | |
| 1157 } | |
| 1158 | |
| 1159 void CanvasRenderingContext2D::scrollPathIntoViewInternal(const Path& path) | |
| 1160 { | |
| 1161 if (!state().isTransformInvertible() || path.isEmpty()) | |
| 1162 return; | |
| 1163 | |
| 1164 canvas()->document().updateLayoutIgnorePendingStylesheets(); | |
| 1165 | |
| 1166 LayoutObject* renderer = canvas()->layoutObject(); | |
| 1167 LayoutBox* layoutBox = canvas()->layoutBox(); | |
| 1168 if (!renderer || !layoutBox) | |
| 1169 return; | |
| 1170 | |
| 1171 // Apply transformation and get the bounding rect | |
| 1172 Path transformedPath = path; | |
| 1173 transformedPath.transform(state().transform()); | |
| 1174 FloatRect boundingRect = transformedPath.boundingRect(); | |
| 1175 | |
| 1176 // Offset by the canvas rect | |
| 1177 LayoutRect pathRect(boundingRect); | |
| 1178 IntRect canvasRect = layoutBox->absoluteContentBox(); | |
| 1179 pathRect.moveBy(canvasRect.location()); | |
| 1180 | |
| 1181 renderer->scrollRectToVisible( | |
| 1182 pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopA
lways); | |
| 1183 | |
| 1184 // TODO: should implement "inform the user" that the caret and/or | |
| 1185 // selection the specified rectangle of the canvas. See http://crbug.com/357
987 | |
| 1186 } | |
| 1187 | |
| 1188 void CanvasRenderingContext2D::clearRect(double x, double y, double width, doubl
e height) | |
| 1189 { | 782 { |
| 1190 if (!validateRectForCanvas(x, y, width, height)) | 783 if (!validateRectForCanvas(x, y, width, height)) |
| 1191 return; | 784 return; |
| 1192 | 785 |
| 1193 SkCanvas* c = drawingCanvas(); | 786 SkCanvas* c = drawingCanvas(); |
| 1194 if (!c) | 787 if (!c) |
| 1195 return; | 788 return; |
| 1196 if (!state().isTransformInvertible()) | 789 if (!state().isTransformInvertible()) |
| 1197 return; | 790 return; |
| 1198 | 791 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1210 if (drawingCanvas()) | 803 if (drawingCanvas()) |
| 1211 drawingCanvas()->drawRect(rect, clearPaint); | 804 drawingCanvas()->drawRect(rect, clearPaint); |
| 1212 didDraw(clipBounds); | 805 didDraw(clipBounds); |
| 1213 } else { | 806 } else { |
| 1214 SkIRect dirtyRect; | 807 SkIRect dirtyRect; |
| 1215 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) { | 808 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) { |
| 1216 c->drawRect(rect, clearPaint); | 809 c->drawRect(rect, clearPaint); |
| 1217 didDraw(dirtyRect); | 810 didDraw(dirtyRect); |
| 1218 } | 811 } |
| 1219 } | 812 } |
| 1220 | |
| 1221 if (m_hitRegionManager) { | |
| 1222 m_hitRegionManager->removeHitRegionsInRect(rect, state().transform()); | |
| 1223 } | |
| 1224 } | 813 } |
| 1225 | 814 |
| 1226 static inline FloatRect normalizeRect(const FloatRect& rect) | 815 static inline FloatRect normalizeRect(const FloatRect& rect) |
| 1227 { | 816 { |
| 1228 return FloatRect(std::min(rect.x(), rect.maxX()), | 817 return FloatRect(std::min(rect.x(), rect.maxX()), |
| 1229 std::min(rect.y(), rect.maxY()), | 818 std::min(rect.y(), rect.maxY()), |
| 1230 std::max(rect.width(), -rect.width()), | 819 std::max(rect.width(), -rect.width()), |
| 1231 std::max(rect.height(), -rect.height())); | 820 std::max(rect.height(), -rect.height())); |
| 1232 } | 821 } |
| 1233 | 822 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1257 if (value.isHTMLVideoElement()) | 846 if (value.isHTMLVideoElement()) |
| 1258 return value.getAsHTMLVideoElement().get(); | 847 return value.getAsHTMLVideoElement().get(); |
| 1259 if (value.isHTMLCanvasElement()) | 848 if (value.isHTMLCanvasElement()) |
| 1260 return value.getAsHTMLCanvasElement().get(); | 849 return value.getAsHTMLCanvasElement().get(); |
| 1261 if (value.isImageBitmap()) | 850 if (value.isImageBitmap()) |
| 1262 return value.getAsImageBitmap().get(); | 851 return value.getAsImageBitmap().get(); |
| 1263 ASSERT_NOT_REACHED(); | 852 ASSERT_NOT_REACHED(); |
| 1264 return nullptr; | 853 return nullptr; |
| 1265 } | 854 } |
| 1266 | 855 |
| 1267 void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSour
ce, double x, double y, ExceptionState& exceptionState) | 856 void BaseRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource
, double x, double y, ExceptionState& exceptionState) |
| 1268 { | 857 { |
| 1269 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); | 858 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); |
| 1270 FloatSize sourceRectSize = imageSourceInternal->elementSize(); | 859 FloatSize sourceRectSize = imageSourceInternal->elementSize(); |
| 1271 FloatSize destRectSize = imageSourceInternal->defaultDestinationSize(); | 860 FloatSize destRectSize = imageSourceInternal->defaultDestinationSize(); |
| 1272 drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize.
height(), x, y, destRectSize.width(), destRectSize.height(), exceptionState); | 861 drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize.
height(), x, y, destRectSize.width(), destRectSize.height(), exceptionState); |
| 1273 } | 862 } |
| 1274 | 863 |
| 1275 void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSour
ce, | 864 void BaseRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource
, |
| 1276 double x, double y, double width, double height, ExceptionState& exceptionSt
ate) | 865 double x, double y, double width, double height, ExceptionState& exceptionSt
ate) |
| 1277 { | 866 { |
| 1278 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); | 867 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); |
| 1279 FloatSize sourceRectSize = imageSourceInternal->elementSize(); | 868 FloatSize sourceRectSize = imageSourceInternal->elementSize(); |
| 1280 drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize.
height(), x, y, width, height, exceptionState); | 869 drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize.
height(), x, y, width, height, exceptionState); |
| 1281 } | 870 } |
| 1282 | 871 |
| 1283 void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSour
ce, | 872 void BaseRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource
, |
| 1284 double sx, double sy, double sw, double sh, | 873 double sx, double sy, double sw, double sh, |
| 1285 double dx, double dy, double dw, double dh, ExceptionState& exceptionState) | 874 double dx, double dy, double dw, double dh, ExceptionState& exceptionState) |
| 1286 { | 875 { |
| 1287 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); | 876 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); |
| 1288 drawImage(imageSourceInternal, sx, sy, sw, sh, dx, dy, dw, dh, exceptionStat
e); | 877 drawImage(imageSourceInternal, sx, sy, sw, sh, dx, dy, dw, dh, exceptionStat
e); |
| 1289 } | 878 } |
| 1290 | 879 |
| 1291 bool CanvasRenderingContext2D::shouldDrawImageAntialiased(const FloatRect& destR
ect) const | 880 bool BaseRenderingContext2D::shouldDrawImageAntialiased(const FloatRect& destRec
t) const |
| 1292 { | 881 { |
| 1293 if (!shouldAntialias()) | 882 if (!state().shouldAntialias()) |
| 1294 return false; | 883 return false; |
| 1295 SkCanvas* c = drawingCanvas(); | 884 SkCanvas* c = drawingCanvas(); |
| 1296 ASSERT(c); | 885 ASSERT(c); |
| 1297 | 886 |
| 1298 const SkMatrix &ctm = c->getTotalMatrix(); | 887 const SkMatrix &ctm = c->getTotalMatrix(); |
| 1299 // Don't disable anti-aliasing if we're rotated or skewed. | 888 // Don't disable anti-aliasing if we're rotated or skewed. |
| 1300 if (!ctm.rectStaysRect()) | 889 if (!ctm.rectStaysRect()) |
| 1301 return true; | 890 return true; |
| 1302 // Check if the dimensions of the destination are "small" (less than one | 891 // Check if the dimensions of the destination are "small" (less than one |
| 1303 // device pixel). To prevent sudden drop-outs. Since we know that | 892 // device pixel). To prevent sudden drop-outs. Since we know that |
| 1304 // kRectStaysRect_Mask is set, the matrix either has scale and no skew or | 893 // kRectStaysRect_Mask is set, the matrix either has scale and no skew or |
| 1305 // vice versa. We can query the kAffine_Mask flag to determine which case | 894 // vice versa. We can query the kAffine_Mask flag to determine which case |
| 1306 // it is. | 895 // it is. |
| 1307 // FIXME: This queries the CTM while drawing, which is generally | 896 // FIXME: This queries the CTM while drawing, which is generally |
| 1308 // discouraged. Always drawing with AA can negatively impact performance | 897 // discouraged. Always drawing with AA can negatively impact performance |
| 1309 // though - that's why it's not always on. | 898 // though - that's why it's not always on. |
| 1310 SkScalar widthExpansion, heightExpansion; | 899 SkScalar widthExpansion, heightExpansion; |
| 1311 if (ctm.getType() & SkMatrix::kAffine_Mask) | 900 if (ctm.getType() & SkMatrix::kAffine_Mask) |
| 1312 widthExpansion = ctm[SkMatrix::kMSkewY], heightExpansion = ctm[SkMatrix:
:kMSkewX]; | 901 widthExpansion = ctm[SkMatrix::kMSkewY], heightExpansion = ctm[SkMatrix:
:kMSkewX]; |
| 1313 else | 902 else |
| 1314 widthExpansion = ctm[SkMatrix::kMScaleX], heightExpansion = ctm[SkMatrix
::kMScaleY]; | 903 widthExpansion = ctm[SkMatrix::kMScaleX], heightExpansion = ctm[SkMatrix
::kMScaleY]; |
| 1315 return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fa
bs(heightExpansion) < 1; | 904 return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fa
bs(heightExpansion) < 1; |
| 1316 } | 905 } |
| 1317 | 906 |
| 1318 void CanvasRenderingContext2D::drawImageInternal(SkCanvas* c, CanvasImageSource*
imageSource, Image* image, const FloatRect& srcRect, const FloatRect& dstRect,
const SkPaint* paint) | 907 void BaseRenderingContext2D::drawImageInternal(SkCanvas* c, CanvasImageSource* i
mageSource, Image* image, const FloatRect& srcRect, const FloatRect& dstRect, co
nst SkPaint* paint) |
| 1319 { | 908 { |
| 1320 int initialSaveCount = c->getSaveCount(); | 909 int initialSaveCount = c->getSaveCount(); |
| 1321 SkPaint imagePaint = *paint; | 910 SkPaint imagePaint = *paint; |
| 1322 | 911 |
| 1323 if (paint->getImageFilter()) { | 912 if (paint->getImageFilter()) { |
| 1324 SkMatrix invCtm; | 913 SkMatrix invCtm; |
| 1325 if (!c->getTotalMatrix().invert(&invCtm)) { | 914 if (!c->getTotalMatrix().invert(&invCtm)) { |
| 1326 // There is an earlier check for invertibility, but the arithmetic | 915 // There is an earlier check for invertibility, but the arithmetic |
| 1327 // in AffineTransform is not exactly identical, so it is possible | 916 // in AffineTransform is not exactly identical, so it is possible |
| 1328 // for SkMatrix to find the transform to be non-invertible at this s
tage. | 917 // for SkMatrix to find the transform to be non-invertible at this s
tage. |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1367 if (imageSource->isCanvasElement()) { | 956 if (imageSource->isCanvasElement()) { |
| 1368 HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(imageSource)
; | 957 HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(imageSource)
; |
| 1369 if (canvas->isAnimated2D()) { | 958 if (canvas->isAnimated2D()) { |
| 1370 *reason = DisableDeferralReasonDrawImageOfAnimated2dCanvas; | 959 *reason = DisableDeferralReasonDrawImageOfAnimated2dCanvas; |
| 1371 return true; | 960 return true; |
| 1372 } | 961 } |
| 1373 } | 962 } |
| 1374 return false; | 963 return false; |
| 1375 } | 964 } |
| 1376 | 965 |
| 1377 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, | 966 void BaseRenderingContext2D::drawImage(CanvasImageSource* imageSource, |
| 1378 double sx, double sy, double sw, double sh, | 967 double sx, double sy, double sw, double sh, |
| 1379 double dx, double dy, double dw, double dh, ExceptionState& exceptionState) | 968 double dx, double dy, double dw, double dh, ExceptionState& exceptionState) |
| 1380 { | 969 { |
| 1381 if (!drawingCanvas()) | 970 if (!drawingCanvas()) |
| 1382 return; | 971 return; |
| 1383 | 972 |
| 1384 RefPtr<Image> image; | 973 RefPtr<Image> image; |
| 1385 SourceImageStatus sourceImageStatus = InvalidSourceImageStatus; | 974 SourceImageStatus sourceImageStatus = InvalidSourceImageStatus; |
| 1386 if (!imageSource->isVideoElement()) { | 975 if (!imageSource->isVideoElement()) { |
| 1387 AccelerationHint hint = canvas()->buffer()->isAccelerated() ? PreferAcce
leration : PreferNoAcceleration; | 976 AccelerationHint hint = imageBuffer()->isAccelerated() ? PreferAccelerat
ion : PreferNoAcceleration; |
| 1388 image = imageSource->getSourceImageForCanvas(&sourceImageStatus, hint, S
napshotReasonDrawImage); | 977 image = imageSource->getSourceImageForCanvas(&sourceImageStatus, hint, S
napshotReasonDrawImage); |
| 1389 if (sourceImageStatus == UndecodableSourceImageStatus) | 978 if (sourceImageStatus == UndecodableSourceImageStatus) |
| 1390 exceptionState.throwDOMException(InvalidStateError, "The HTMLImageEl
ement provided is in the 'broken' state."); | 979 exceptionState.throwDOMException(InvalidStateError, "The HTMLImageEl
ement provided is in the 'broken' state."); |
| 1391 if (!image || !image->width() || !image->height()) | 980 if (!image || !image->width() || !image->height()) |
| 1392 return; | 981 return; |
| 1393 } else { | 982 } else { |
| 1394 if (!static_cast<HTMLVideoElement*>(imageSource)->hasAvailableVideoFrame
()) | 983 if (!static_cast<HTMLVideoElement*>(imageSource)->hasAvailableVideoFrame
()) |
| 1395 return; | 984 return; |
| 1396 } | 985 } |
| 1397 | 986 |
| 1398 if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dw) || !std::
isfinite(dh) | 987 if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dw) || !std::
isfinite(dh) |
| 1399 || !std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !st
d::isfinite(sh) | 988 || !std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !st
d::isfinite(sh) |
| 1400 || !dw || !dh || !sw || !sh) | 989 || !dw || !dh || !sw || !sh) |
| 1401 return; | 990 return; |
| 1402 | 991 |
| 1403 FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh)); | 992 FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh)); |
| 1404 FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh)); | 993 FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh)); |
| 1405 | 994 |
| 1406 clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->elementSize()), &s
rcRect, &dstRect); | 995 clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->elementSize()), &s
rcRect, &dstRect); |
| 1407 | 996 |
| 1408 imageSource->adjustDrawRects(&srcRect, &dstRect); | 997 imageSource->adjustDrawRects(&srcRect, &dstRect); |
| 1409 | 998 |
| 1410 if (srcRect.isEmpty()) | 999 if (srcRect.isEmpty()) |
| 1411 return; | 1000 return; |
| 1412 | 1001 |
| 1413 DisableDeferralReason reason = DisableDeferralReasonUnknown; | 1002 DisableDeferralReason reason = DisableDeferralReasonUnknown; |
| 1414 if (shouldDisableDeferral(imageSource, &reason) || image->isTextureBacked()) | 1003 if (shouldDisableDeferral(imageSource, &reason) || image->isTextureBacked()) |
| 1415 canvas()->disableDeferral(reason); | 1004 disableDeferral(reason); |
| 1416 | 1005 |
| 1417 validateStateStack(); | 1006 validateStateStack(); |
| 1418 | 1007 |
| 1419 draw( | 1008 draw( |
| 1420 [this, &imageSource, &image, &srcRect, dstRect](SkCanvas* c, const SkPai
nt* paint) // draw lambda | 1009 [this, &imageSource, &image, &srcRect, dstRect](SkCanvas* c, const SkPai
nt* paint) // draw lambda |
| 1421 { | 1010 { |
| 1422 drawImageInternal(c, imageSource, image.get(), srcRect, dstRect, pai
nt); | 1011 drawImageInternal(c, imageSource, image.get(), srcRect, dstRect, pai
nt); |
| 1423 }, | 1012 }, |
| 1424 [this, &dstRect](const SkIRect& clipBounds) // overdraw test lambda | 1013 [this, &dstRect](const SkIRect& clipBounds) // overdraw test lambda |
| 1425 { | 1014 { |
| 1426 return rectContainsTransformedRect(dstRect, clipBounds); | 1015 return rectContainsTransformedRect(dstRect, clipBounds); |
| 1427 }, dstRect, CanvasRenderingContext2DState::ImagePaintType, | 1016 }, dstRect, CanvasRenderingContext2DState::ImagePaintType, |
| 1428 imageSource->isOpaque() ? CanvasRenderingContext2DState::OpaqueImage : C
anvasRenderingContext2DState::NonOpaqueImage); | 1017 imageSource->isOpaque() ? CanvasRenderingContext2DState::OpaqueImage : C
anvasRenderingContext2DState::NonOpaqueImage); |
| 1429 | 1018 |
| 1430 validateStateStack(); | 1019 validateStateStack(); |
| 1431 | 1020 |
| 1432 bool isExpensive = false; | 1021 bool isExpensive = false; |
| 1433 | 1022 |
| 1434 if (ExpensiveCanvasHeuristicParameters::SVGImageSourcesAreExpensive && image
Source->isSVGSource()) | 1023 if (ExpensiveCanvasHeuristicParameters::SVGImageSourcesAreExpensive && image
Source->isSVGSource()) |
| 1435 isExpensive = true; | 1024 isExpensive = true; |
| 1436 | 1025 |
| 1437 if (imageSource->elementSize().width() * imageSource->elementSize().height()
> canvas()->width() * canvas()->height() * ExpensiveCanvasHeuristicParameters::
ExpensiveImageSizeRatio) | 1026 if (imageSource->elementSize().width() * imageSource->elementSize().height()
> width() * height() * ExpensiveCanvasHeuristicParameters::ExpensiveImageSizeRa
tio) |
| 1438 isExpensive = true; | 1027 isExpensive = true; |
| 1439 | 1028 |
| 1440 if (isExpensive) { | 1029 if (isExpensive) { |
| 1441 ImageBuffer* buffer = canvas()->buffer(); | 1030 ImageBuffer* buffer = imageBuffer(); |
| 1442 if (buffer) | 1031 if (buffer) |
| 1443 buffer->setHasExpensiveOp(); | 1032 buffer->setHasExpensiveOp(); |
| 1444 } | 1033 } |
| 1445 | 1034 |
| 1446 if (imageSource->isCanvasElement() && static_cast<HTMLCanvasElement*>(imageS
ource)->is3D()) { | 1035 if (imageSource->isCanvasElement() && static_cast<HTMLCanvasElement*>(imageS
ource)->is3D()) { |
| 1447 // WebGL to 2D canvas: must flush graphics context to prevent a race | 1036 // WebGL to 2D canvas: must flush graphics context to prevent a race |
| 1448 // FIXME: crbug.com/516331 Fix the underlying synchronization issue so t
his flush can be eliminated. | 1037 // FIXME: crbug.com/516331 Fix the underlying synchronization issue so t
his flush can be eliminated. |
| 1449 canvas()->buffer()->flushGpu(FlushReasonDrawImageOfWebGL); | 1038 imageBuffer()->flushGpu(FlushReasonDrawImageOfWebGL); |
| 1450 } | 1039 } |
| 1451 | 1040 |
| 1452 if (canvas()->originClean() && wouldTaintOrigin(imageSource)) | 1041 if (originClean() && wouldTaintOrigin(imageSource)) |
| 1453 canvas()->setOriginTainted(); | 1042 setOriginTainted(); |
| 1454 } | 1043 } |
| 1455 | 1044 |
| 1456 void CanvasRenderingContext2D::clearCanvas() | 1045 void BaseRenderingContext2D::clearCanvas() |
| 1457 { | 1046 { |
| 1458 FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height()); | 1047 FloatRect canvasRect(0, 0, width(), height()); |
| 1459 checkOverdraw(canvasRect, 0, CanvasRenderingContext2DState::NoImage, ClipFil
l); | 1048 checkOverdraw(canvasRect, 0, CanvasRenderingContext2DState::NoImage, ClipFil
l); |
| 1460 SkCanvas* c = drawingCanvas(); | 1049 SkCanvas* c = drawingCanvas(); |
| 1461 if (c) | 1050 if (c) |
| 1462 c->clear(m_hasAlpha ? SK_ColorTRANSPARENT : SK_ColorBLACK); | 1051 c->clear(hasAlpha() ? SK_ColorTRANSPARENT : SK_ColorBLACK); |
| 1463 } | 1052 } |
| 1464 | 1053 |
| 1465 bool CanvasRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect
, const SkIRect& transformedRect) const | 1054 bool BaseRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect,
const SkIRect& transformedRect) const |
| 1466 { | 1055 { |
| 1467 FloatQuad quad(rect); | 1056 FloatQuad quad(rect); |
| 1468 FloatQuad transformedQuad(FloatRect(transformedRect.x(), transformedRect.y()
, transformedRect.width(), transformedRect.height())); | 1057 FloatQuad transformedQuad(FloatRect(transformedRect.x(), transformedRect.y()
, transformedRect.width(), transformedRect.height())); |
| 1469 return state().transform().mapQuad(quad).containsQuad(transformedQuad); | 1058 return state().transform().mapQuad(quad).containsQuad(transformedQuad); |
| 1470 } | 1059 } |
| 1471 | 1060 |
| 1472 CanvasGradient* CanvasRenderingContext2D::createLinearGradient(double x0, double
y0, double x1, double y1) | 1061 CanvasGradient* BaseRenderingContext2D::createLinearGradient(double x0, double y
0, double x1, double y1) |
| 1473 { | 1062 { |
| 1474 CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatP
oint(x1, y1)); | 1063 CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatP
oint(x1, y1)); |
| 1475 return gradient; | 1064 return gradient; |
| 1476 } | 1065 } |
| 1477 | 1066 |
| 1478 CanvasGradient* CanvasRenderingContext2D::createRadialGradient(double x0, double
y0, double r0, double x1, double y1, double r1, ExceptionState& exceptionState) | 1067 CanvasGradient* BaseRenderingContext2D::createRadialGradient(double x0, double y
0, double r0, double x1, double y1, double r1, ExceptionState& exceptionState) |
| 1479 { | 1068 { |
| 1480 if (r0 < 0 || r1 < 0) { | 1069 if (r0 < 0 || r1 < 0) { |
| 1481 exceptionState.throwDOMException(IndexSizeError, String::format("The %s
provided is less than 0.", r0 < 0 ? "r0" : "r1")); | 1070 exceptionState.throwDOMException(IndexSizeError, String::format("The %s
provided is less than 0.", r0 < 0 ? "r0" : "r1")); |
| 1482 return nullptr; | 1071 return nullptr; |
| 1483 } | 1072 } |
| 1484 | 1073 |
| 1485 CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, Fl
oatPoint(x1, y1), r1); | 1074 CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, Fl
oatPoint(x1, y1), r1); |
| 1486 return gradient; | 1075 return gradient; |
| 1487 } | 1076 } |
| 1488 | 1077 |
| 1489 CanvasPattern* CanvasRenderingContext2D::createPattern(const CanvasImageSourceUn
ion& imageSource, const String& repetitionType, ExceptionState& exceptionState) | 1078 CanvasPattern* BaseRenderingContext2D::createPattern(const CanvasImageSourceUnio
n& imageSource, const String& repetitionType, ExceptionState& exceptionState) |
| 1490 { | 1079 { |
| 1491 Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetiti
onType, exceptionState); | 1080 Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetiti
onType, exceptionState); |
| 1492 if (exceptionState.hadException()) | 1081 if (exceptionState.hadException()) |
| 1493 return nullptr; | 1082 return nullptr; |
| 1494 | 1083 |
| 1495 SourceImageStatus status; | 1084 SourceImageStatus status; |
| 1496 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); | 1085 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); |
| 1497 RefPtr<Image> imageForRendering = imageSourceInternal->getSourceImageForCanv
as(&status, PreferNoAcceleration, SnapshotReasonCreatePattern); | 1086 RefPtr<Image> imageForRendering = imageSourceInternal->getSourceImageForCanv
as(&status, PreferNoAcceleration, SnapshotReasonCreatePattern); |
| 1498 | 1087 |
| 1499 switch (status) { | 1088 switch (status) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1514 ASSERT_NOT_REACHED(); | 1103 ASSERT_NOT_REACHED(); |
| 1515 return nullptr; | 1104 return nullptr; |
| 1516 } | 1105 } |
| 1517 ASSERT(imageForRendering); | 1106 ASSERT(imageForRendering); |
| 1518 | 1107 |
| 1519 bool originClean = !wouldTaintOrigin(imageSourceInternal); | 1108 bool originClean = !wouldTaintOrigin(imageSourceInternal); |
| 1520 | 1109 |
| 1521 return CanvasPattern::create(imageForRendering.release(), repeatMode, origin
Clean); | 1110 return CanvasPattern::create(imageForRendering.release(), repeatMode, origin
Clean); |
| 1522 } | 1111 } |
| 1523 | 1112 |
| 1524 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, SkIR
ect* dirtyRect) | 1113 bool BaseRenderingContext2D::computeDirtyRect(const FloatRect& localRect, SkIRec
t* dirtyRect) |
| 1525 { | 1114 { |
| 1526 SkIRect clipBounds; | 1115 SkIRect clipBounds; |
| 1527 if (!drawingCanvas()->getClipDeviceBounds(&clipBounds)) | 1116 if (!drawingCanvas()->getClipDeviceBounds(&clipBounds)) |
| 1528 return false; | 1117 return false; |
| 1529 return computeDirtyRect(localRect, clipBounds, dirtyRect); | 1118 return computeDirtyRect(localRect, clipBounds, dirtyRect); |
| 1530 } | 1119 } |
| 1531 | 1120 |
| 1532 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, cons
t SkIRect& transformedClipBounds, SkIRect* dirtyRect) | 1121 bool BaseRenderingContext2D::computeDirtyRect(const FloatRect& localRect, const
SkIRect& transformedClipBounds, SkIRect* dirtyRect) |
| 1533 { | 1122 { |
| 1534 FloatRect canvasRect = state().transform().mapRect(localRect); | 1123 FloatRect canvasRect = state().transform().mapRect(localRect); |
| 1535 | 1124 |
| 1536 if (alphaChannel(state().shadowColor())) { | 1125 if (alphaChannel(state().shadowColor())) { |
| 1537 FloatRect shadowRect(canvasRect); | 1126 FloatRect shadowRect(canvasRect); |
| 1538 shadowRect.move(state().shadowOffset()); | 1127 shadowRect.move(state().shadowOffset()); |
| 1539 shadowRect.inflate(state().shadowBlur()); | 1128 shadowRect.inflate(state().shadowBlur()); |
| 1540 canvasRect.unite(shadowRect); | 1129 canvasRect.unite(shadowRect); |
| 1541 } | 1130 } |
| 1542 | 1131 |
| 1543 SkIRect canvasIRect; | 1132 SkIRect canvasIRect; |
| 1544 static_cast<SkRect>(canvasRect).roundOut(&canvasIRect); | 1133 static_cast<SkRect>(canvasRect).roundOut(&canvasIRect); |
| 1545 if (!canvasIRect.intersect(transformedClipBounds)) | 1134 if (!canvasIRect.intersect(transformedClipBounds)) |
| 1546 return false; | 1135 return false; |
| 1547 | 1136 |
| 1548 if (dirtyRect) | 1137 if (dirtyRect) |
| 1549 *dirtyRect = canvasIRect; | 1138 *dirtyRect = canvasIRect; |
| 1550 | 1139 |
| 1551 return true; | 1140 return true; |
| 1552 } | 1141 } |
| 1553 | 1142 |
| 1554 void CanvasRenderingContext2D::didDraw(const SkIRect& dirtyRect) | 1143 ImageData* BaseRenderingContext2D::createImageData(ImageData* imageData) const |
| 1555 { | |
| 1556 if (dirtyRect.isEmpty()) | |
| 1557 return; | |
| 1558 | |
| 1559 if (ExpensiveCanvasHeuristicParameters::BlurredShadowsAreExpensive && state(
).shouldDrawShadows() && state().shadowBlur() > 0) { | |
| 1560 ImageBuffer* buffer = canvas()->buffer(); | |
| 1561 if (buffer) | |
| 1562 buffer->setHasExpensiveOp(); | |
| 1563 } | |
| 1564 | |
| 1565 canvas()->didDraw(SkRect::Make(dirtyRect)); | |
| 1566 } | |
| 1567 | |
| 1568 SkCanvas* CanvasRenderingContext2D::drawingCanvas() const | |
| 1569 { | |
| 1570 if (isContextLost()) | |
| 1571 return nullptr; | |
| 1572 return canvas()->drawingCanvas(); | |
| 1573 } | |
| 1574 | |
| 1575 ImageData* CanvasRenderingContext2D::createImageData(ImageData* imageData) const | |
| 1576 { | 1144 { |
| 1577 return ImageData::create(imageData->size()); | 1145 return ImageData::create(imageData->size()); |
| 1578 } | 1146 } |
| 1579 | 1147 |
| 1580 ImageData* CanvasRenderingContext2D::createImageData(double sw, double sh, Excep
tionState& exceptionState) const | 1148 ImageData* BaseRenderingContext2D::createImageData(double sw, double sh, Excepti
onState& exceptionState) const |
| 1581 { | 1149 { |
| 1582 if (!sw || !sh) { | 1150 if (!sw || !sh) { |
| 1583 exceptionState.throwDOMException(IndexSizeError, String::format("The sou
rce %s is 0.", sw ? "height" : "width")); | 1151 exceptionState.throwDOMException(IndexSizeError, String::format("The sou
rce %s is 0.", sw ? "height" : "width")); |
| 1584 return nullptr; | 1152 return nullptr; |
| 1585 } | 1153 } |
| 1586 | 1154 |
| 1587 FloatSize logicalSize(fabs(sw), fabs(sh)); | 1155 FloatSize logicalSize(fabs(sw), fabs(sh)); |
| 1588 if (!logicalSize.isExpressibleAsIntSize()) | 1156 if (!logicalSize.isExpressibleAsIntSize()) |
| 1589 return nullptr; | 1157 return nullptr; |
| 1590 | 1158 |
| 1591 IntSize size = expandedIntSize(logicalSize); | 1159 IntSize size = expandedIntSize(logicalSize); |
| 1592 if (size.width() < 1) | 1160 if (size.width() < 1) |
| 1593 size.setWidth(1); | 1161 size.setWidth(1); |
| 1594 if (size.height() < 1) | 1162 if (size.height() < 1) |
| 1595 size.setHeight(1); | 1163 size.setHeight(1); |
| 1596 | 1164 |
| 1597 return ImageData::create(size); | 1165 return ImageData::create(size); |
| 1598 } | 1166 } |
| 1599 | 1167 |
| 1600 ImageData* CanvasRenderingContext2D::getImageData(double sx, double sy, double s
w, double sh, ExceptionState& exceptionState) const | 1168 ImageData* BaseRenderingContext2D::getImageData(double sx, double sy, double sw,
double sh, ExceptionState& exceptionState) const |
| 1601 { | 1169 { |
| 1602 if (!canvas()->originClean()) | 1170 if (!originClean()) |
| 1603 exceptionState.throwSecurityError("The canvas has been tainted by cross-
origin data."); | 1171 exceptionState.throwSecurityError("The canvas has been tainted by cross-
origin data."); |
| 1604 else if (!sw || !sh) | 1172 else if (!sw || !sh) |
| 1605 exceptionState.throwDOMException(IndexSizeError, String::format("The sou
rce %s is 0.", sw ? "height" : "width")); | 1173 exceptionState.throwDOMException(IndexSizeError, String::format("The sou
rce %s is 0.", sw ? "height" : "width")); |
| 1606 | 1174 |
| 1607 if (exceptionState.hadException()) | 1175 if (exceptionState.hadException()) |
| 1608 return nullptr; | 1176 return nullptr; |
| 1609 | 1177 |
| 1610 if (sw < 0) { | 1178 if (sw < 0) { |
| 1611 sx += sw; | 1179 sx += sw; |
| 1612 sw = -sw; | 1180 sw = -sw; |
| 1613 } | 1181 } |
| 1614 if (sh < 0) { | 1182 if (sh < 0) { |
| 1615 sy += sh; | 1183 sy += sh; |
| 1616 sh = -sh; | 1184 sh = -sh; |
| 1617 } | 1185 } |
| 1618 | 1186 |
| 1619 FloatRect logicalRect(sx, sy, sw, sh); | 1187 FloatRect logicalRect(sx, sy, sw, sh); |
| 1620 if (logicalRect.width() < 1) | 1188 if (logicalRect.width() < 1) |
| 1621 logicalRect.setWidth(1); | 1189 logicalRect.setWidth(1); |
| 1622 if (logicalRect.height() < 1) | 1190 if (logicalRect.height() < 1) |
| 1623 logicalRect.setHeight(1); | 1191 logicalRect.setHeight(1); |
| 1624 if (!logicalRect.isExpressibleAsIntRect()) | 1192 if (!logicalRect.isExpressibleAsIntRect()) |
| 1625 return nullptr; | 1193 return nullptr; |
| 1626 | 1194 |
| 1627 IntRect imageDataRect = enclosingIntRect(logicalRect); | 1195 IntRect imageDataRect = enclosingIntRect(logicalRect); |
| 1628 ImageBuffer* buffer = canvas()->buffer(); | 1196 ImageBuffer* buffer = imageBuffer(); |
| 1629 if (!buffer || isContextLost()) | 1197 if (!buffer || isContextLost()) |
| 1630 return ImageData::create(imageDataRect.size()); | 1198 return ImageData::create(imageDataRect.size()); |
| 1631 | 1199 |
| 1632 WTF::ArrayBufferContents contents; | 1200 WTF::ArrayBufferContents contents; |
| 1633 if (!buffer->getImageData(Unmultiplied, imageDataRect, contents)) | 1201 if (!buffer->getImageData(Unmultiplied, imageDataRect, contents)) |
| 1634 return nullptr; | 1202 return nullptr; |
| 1635 | 1203 |
| 1636 RefPtr<DOMArrayBuffer> arrayBuffer = DOMArrayBuffer::create(contents); | 1204 RefPtr<DOMArrayBuffer> arrayBuffer = DOMArrayBuffer::create(contents); |
| 1637 return ImageData::create( | 1205 return ImageData::create( |
| 1638 imageDataRect.size(), | 1206 imageDataRect.size(), |
| 1639 DOMUint8ClampedArray::create(arrayBuffer, 0, arrayBuffer->byteLength()))
; | 1207 DOMUint8ClampedArray::create(arrayBuffer, 0, arrayBuffer->byteLength()))
; |
| 1640 } | 1208 } |
| 1641 | 1209 |
| 1642 void CanvasRenderingContext2D::putImageData(ImageData* data, double dx, double d
y, ExceptionState& exceptionState) | 1210 void BaseRenderingContext2D::putImageData(ImageData* data, double dx, double dy,
ExceptionState& exceptionState) |
| 1643 { | 1211 { |
| 1644 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), exceptionSta
te); | 1212 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), exceptionSta
te); |
| 1645 } | 1213 } |
| 1646 | 1214 |
| 1647 void CanvasRenderingContext2D::putImageData(ImageData* data, double dx, double d
y, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight, Exceptio
nState& exceptionState) | 1215 void BaseRenderingContext2D::putImageData(ImageData* data, double dx, double dy,
double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight, ExceptionS
tate& exceptionState) |
| 1648 { | 1216 { |
| 1649 if (data->data()->bufferBase()->isNeutered()) { | 1217 if (data->data()->bufferBase()->isNeutered()) { |
| 1650 exceptionState.throwDOMException(InvalidStateError, "The source data has
been neutered."); | 1218 exceptionState.throwDOMException(InvalidStateError, "The source data has
been neutered."); |
| 1651 return; | 1219 return; |
| 1652 } | 1220 } |
| 1653 ImageBuffer* buffer = canvas()->buffer(); | 1221 ImageBuffer* buffer = imageBuffer(); |
| 1654 if (!buffer) | 1222 if (!buffer) |
| 1655 return; | 1223 return; |
| 1656 | 1224 |
| 1657 if (dirtyWidth < 0) { | 1225 if (dirtyWidth < 0) { |
| 1658 dirtyX += dirtyWidth; | 1226 dirtyX += dirtyWidth; |
| 1659 dirtyWidth = -dirtyWidth; | 1227 dirtyWidth = -dirtyWidth; |
| 1660 } | 1228 } |
| 1661 | 1229 |
| 1662 if (dirtyHeight < 0) { | 1230 if (dirtyHeight < 0) { |
| 1663 dirtyY += dirtyHeight; | 1231 dirtyY += dirtyHeight; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1675 IntRect sourceRect(destRect); | 1243 IntRect sourceRect(destRect); |
| 1676 sourceRect.move(-destOffset); | 1244 sourceRect.move(-destOffset); |
| 1677 | 1245 |
| 1678 checkOverdraw(destRect, 0, CanvasRenderingContext2DState::NoImage, Untransfo
rmedUnclippedFill); | 1246 checkOverdraw(destRect, 0, CanvasRenderingContext2DState::NoImage, Untransfo
rmedUnclippedFill); |
| 1679 | 1247 |
| 1680 buffer->putByteArray(Unmultiplied, data->data()->data(), IntSize(data->width
(), data->height()), sourceRect, IntPoint(destOffset)); | 1248 buffer->putByteArray(Unmultiplied, data->data()->data(), IntSize(data->width
(), data->height()), sourceRect, IntPoint(destOffset)); |
| 1681 | 1249 |
| 1682 didDraw(destRect); | 1250 didDraw(destRect); |
| 1683 } | 1251 } |
| 1684 | 1252 |
| 1685 String CanvasRenderingContext2D::font() const | 1253 void BaseRenderingContext2D::inflateStrokeRect(FloatRect& rect) const |
| 1686 { | |
| 1687 if (!state().hasRealizedFont()) | |
| 1688 return defaultFont; | |
| 1689 | |
| 1690 canvas()->document().canvasFontCache()->willUseCurrentFont(); | |
| 1691 StringBuilder serializedFont; | |
| 1692 const FontDescription& fontDescription = state().font().fontDescription(); | |
| 1693 | |
| 1694 if (fontDescription.style() == FontStyleItalic) | |
| 1695 serializedFont.appendLiteral("italic "); | |
| 1696 if (fontDescription.weight() == FontWeightBold) | |
| 1697 serializedFont.appendLiteral("bold "); | |
| 1698 if (fontDescription.variant() == FontVariantSmallCaps) | |
| 1699 serializedFont.appendLiteral("small-caps "); | |
| 1700 | |
| 1701 serializedFont.appendNumber(fontDescription.computedPixelSize()); | |
| 1702 serializedFont.appendLiteral("px"); | |
| 1703 | |
| 1704 const FontFamily& firstFontFamily = fontDescription.family(); | |
| 1705 for (const FontFamily* fontFamily = &firstFontFamily; fontFamily; fontFamily
= fontFamily->next()) { | |
| 1706 if (fontFamily != &firstFontFamily) | |
| 1707 serializedFont.append(','); | |
| 1708 | |
| 1709 // FIXME: We should append family directly to serializedFont rather than
building a temporary string. | |
| 1710 String family = fontFamily->family(); | |
| 1711 if (family.startsWith("-webkit-")) | |
| 1712 family = family.substring(8); | |
| 1713 if (family.contains(' ')) | |
| 1714 family = "\"" + family + "\""; | |
| 1715 | |
| 1716 serializedFont.append(' '); | |
| 1717 serializedFont.append(family); | |
| 1718 } | |
| 1719 | |
| 1720 return serializedFont.toString(); | |
| 1721 } | |
| 1722 | |
| 1723 void CanvasRenderingContext2D::setFont(const String& newFont) | |
| 1724 { | |
| 1725 // The style resolution required for rendering text is not available in fram
e-less documents. | |
| 1726 if (!canvas()->document().frame()) | |
| 1727 return; | |
| 1728 | |
| 1729 canvas()->document().updateLayoutTreeForNode(canvas()); | |
| 1730 | |
| 1731 // The following early exit is dependent on the cache not being empty | |
| 1732 // because an empty cache may indicate that a style change has occured | |
| 1733 // which would require that the font be re-resolved. This check has to | |
| 1734 // come after the layout tree update to flush pending style changes. | |
| 1735 if (newFont == state().unparsedFont() && state().hasRealizedFont() && m_font
sResolvedUsingCurrentStyle.size() > 0) | |
| 1736 return; | |
| 1737 | |
| 1738 CanvasFontCache* canvasFontCache = canvas()->document().canvasFontCache(); | |
| 1739 | |
| 1740 // Map the <canvas> font into the text style. If the font uses keywords like
larger/smaller, these will work | |
| 1741 // relative to the canvas. | |
| 1742 RefPtr<ComputedStyle> fontStyle; | |
| 1743 const ComputedStyle* computedStyle = canvas()->ensureComputedStyle(); | |
| 1744 if (computedStyle) { | |
| 1745 HashMap<String, Font>::iterator i = m_fontsResolvedUsingCurrentStyle.fin
d(newFont); | |
| 1746 if (i != m_fontsResolvedUsingCurrentStyle.end()) { | |
| 1747 ASSERT(m_fontLRUList.contains(newFont)); | |
| 1748 m_fontLRUList.remove(newFont); | |
| 1749 m_fontLRUList.add(newFont); | |
| 1750 modifiableState().setFont(i->value, canvas()->document().styleEngine
().fontSelector()); | |
| 1751 } else { | |
| 1752 MutableStylePropertySet* parsedStyle = canvasFontCache->parseFont(ne
wFont); | |
| 1753 if (!parsedStyle) | |
| 1754 return; | |
| 1755 fontStyle = ComputedStyle::create(); | |
| 1756 FontDescription elementFontDescription(computedStyle->fontDescriptio
n()); | |
| 1757 // Reset the computed size to avoid inheriting the zoom factor from
the <canvas> element. | |
| 1758 elementFontDescription.setComputedSize(elementFontDescription.specif
iedSize()); | |
| 1759 fontStyle->setFontDescription(elementFontDescription); | |
| 1760 fontStyle->font().update(fontStyle->font().fontSelector()); | |
| 1761 canvas()->document().ensureStyleResolver().computeFont(fontStyle.get
(), *parsedStyle); | |
| 1762 m_fontsResolvedUsingCurrentStyle.add(newFont, fontStyle->font()); | |
| 1763 ASSERT(!m_fontLRUList.contains(newFont)); | |
| 1764 m_fontLRUList.add(newFont); | |
| 1765 pruneLocalFontCache(canvasFontCache->hardMaxFonts()); // hard limit | |
| 1766 schedulePruneLocalFontCacheIfNeeded(); // soft limit | |
| 1767 modifiableState().setFont(fontStyle->font(), canvas()->document().st
yleEngine().fontSelector()); | |
| 1768 } | |
| 1769 } else { | |
| 1770 Font resolvedFont; | |
| 1771 if (!canvasFontCache->getFontUsingDefaultStyle(newFont, resolvedFont)) | |
| 1772 return; | |
| 1773 modifiableState().setFont(resolvedFont, canvas()->document().styleEngine
().fontSelector()); | |
| 1774 } | |
| 1775 | |
| 1776 // The parse succeeded. | |
| 1777 String newFontSafeCopy(newFont); // Create a string copy since newFont can b
e deleted inside realizeSaves. | |
| 1778 modifiableState().setUnparsedFont(newFontSafeCopy); | |
| 1779 } | |
| 1780 | |
| 1781 void CanvasRenderingContext2D::schedulePruneLocalFontCacheIfNeeded() | |
| 1782 { | |
| 1783 if (m_pruneLocalFontCacheScheduled) | |
| 1784 return; | |
| 1785 m_pruneLocalFontCacheScheduled = true; | |
| 1786 Platform::current()->currentThread()->addTaskObserver(this); | |
| 1787 } | |
| 1788 | |
| 1789 void CanvasRenderingContext2D::didProcessTask() | |
| 1790 { | |
| 1791 // The rendering surface needs to be prepared now because it will be too lat
e | |
| 1792 // to create a layer once we are in the paint invalidation phase. | |
| 1793 canvas()->prepareSurfaceForPaintingIfNeeded(); | |
| 1794 | |
| 1795 pruneLocalFontCache(canvas()->document().canvasFontCache()->maxFonts()); | |
| 1796 m_pruneLocalFontCacheScheduled = false; | |
| 1797 Platform::current()->currentThread()->removeTaskObserver(this); | |
| 1798 } | |
| 1799 | |
| 1800 void CanvasRenderingContext2D::pruneLocalFontCache(size_t targetSize) | |
| 1801 { | |
| 1802 if (targetSize == 0) { | |
| 1803 // Short cut: LRU does not matter when evicting everything | |
| 1804 m_fontLRUList.clear(); | |
| 1805 m_fontsResolvedUsingCurrentStyle.clear(); | |
| 1806 return; | |
| 1807 } | |
| 1808 while (m_fontLRUList.size() > targetSize) { | |
| 1809 m_fontsResolvedUsingCurrentStyle.remove(m_fontLRUList.first()); | |
| 1810 m_fontLRUList.removeFirst(); | |
| 1811 } | |
| 1812 } | |
| 1813 | |
| 1814 void CanvasRenderingContext2D::styleDidChange(const ComputedStyle* oldStyle, con
st ComputedStyle& newStyle) | |
| 1815 { | |
| 1816 if (oldStyle && oldStyle->font() == newStyle.font()) | |
| 1817 return; | |
| 1818 pruneLocalFontCache(0); | |
| 1819 } | |
| 1820 | |
| 1821 void CanvasRenderingContext2D::filterNeedsInvalidation() | |
| 1822 { | |
| 1823 state().clearResolvedFilter(); | |
| 1824 } | |
| 1825 | |
| 1826 String CanvasRenderingContext2D::textAlign() const | |
| 1827 { | |
| 1828 return textAlignName(state().textAlign()); | |
| 1829 } | |
| 1830 | |
| 1831 void CanvasRenderingContext2D::setTextAlign(const String& s) | |
| 1832 { | |
| 1833 TextAlign align; | |
| 1834 if (!parseTextAlign(s, align)) | |
| 1835 return; | |
| 1836 if (state().textAlign() == align) | |
| 1837 return; | |
| 1838 modifiableState().setTextAlign(align); | |
| 1839 } | |
| 1840 | |
| 1841 String CanvasRenderingContext2D::textBaseline() const | |
| 1842 { | |
| 1843 return textBaselineName(state().textBaseline()); | |
| 1844 } | |
| 1845 | |
| 1846 void CanvasRenderingContext2D::setTextBaseline(const String& s) | |
| 1847 { | |
| 1848 TextBaseline baseline; | |
| 1849 if (!parseTextBaseline(s, baseline)) | |
| 1850 return; | |
| 1851 if (state().textBaseline() == baseline) | |
| 1852 return; | |
| 1853 modifiableState().setTextBaseline(baseline); | |
| 1854 } | |
| 1855 | |
| 1856 static inline TextDirection toTextDirection(CanvasRenderingContext2DState::Direc
tion direction, HTMLCanvasElement* canvas, const ComputedStyle** computedStyle =
0) | |
| 1857 { | |
| 1858 const ComputedStyle* style = (computedStyle || direction == CanvasRenderingC
ontext2DState::DirectionInherit) ? canvas->ensureComputedStyle() : nullptr; | |
| 1859 if (computedStyle) | |
| 1860 *computedStyle = style; | |
| 1861 switch (direction) { | |
| 1862 case CanvasRenderingContext2DState::DirectionInherit: | |
| 1863 return style ? style->direction() : LTR; | |
| 1864 case CanvasRenderingContext2DState::DirectionRTL: | |
| 1865 return RTL; | |
| 1866 case CanvasRenderingContext2DState::DirectionLTR: | |
| 1867 return LTR; | |
| 1868 } | |
| 1869 ASSERT_NOT_REACHED(); | |
| 1870 return LTR; | |
| 1871 } | |
| 1872 | |
| 1873 String CanvasRenderingContext2D::direction() const | |
| 1874 { | |
| 1875 if (state().direction() == CanvasRenderingContext2DState::DirectionInherit) | |
| 1876 canvas()->document().updateLayoutTreeForNode(canvas()); | |
| 1877 return toTextDirection(state().direction(), canvas()) == RTL ? rtl : ltr; | |
| 1878 } | |
| 1879 | |
| 1880 void CanvasRenderingContext2D::setDirection(const String& directionString) | |
| 1881 { | |
| 1882 CanvasRenderingContext2DState::Direction direction; | |
| 1883 if (directionString == inherit) | |
| 1884 direction = CanvasRenderingContext2DState::DirectionInherit; | |
| 1885 else if (directionString == rtl) | |
| 1886 direction = CanvasRenderingContext2DState::DirectionRTL; | |
| 1887 else if (directionString == ltr) | |
| 1888 direction = CanvasRenderingContext2DState::DirectionLTR; | |
| 1889 else | |
| 1890 return; | |
| 1891 | |
| 1892 if (state().direction() == direction) | |
| 1893 return; | |
| 1894 | |
| 1895 modifiableState().setDirection(direction); | |
| 1896 } | |
| 1897 | |
| 1898 void CanvasRenderingContext2D::fillText(const String& text, double x, double y) | |
| 1899 { | |
| 1900 drawTextInternal(text, x, y, CanvasRenderingContext2DState::FillPaintType); | |
| 1901 } | |
| 1902 | |
| 1903 void CanvasRenderingContext2D::fillText(const String& text, double x, double y,
double maxWidth) | |
| 1904 { | |
| 1905 drawTextInternal(text, x, y, CanvasRenderingContext2DState::FillPaintType, &
maxWidth); | |
| 1906 } | |
| 1907 | |
| 1908 void CanvasRenderingContext2D::strokeText(const String& text, double x, double y
) | |
| 1909 { | |
| 1910 drawTextInternal(text, x, y, CanvasRenderingContext2DState::StrokePaintType)
; | |
| 1911 } | |
| 1912 | |
| 1913 void CanvasRenderingContext2D::strokeText(const String& text, double x, double y
, double maxWidth) | |
| 1914 { | |
| 1915 drawTextInternal(text, x, y, CanvasRenderingContext2DState::StrokePaintType,
&maxWidth); | |
| 1916 } | |
| 1917 | |
| 1918 TextMetrics* CanvasRenderingContext2D::measureText(const String& text) | |
| 1919 { | |
| 1920 TextMetrics* metrics = TextMetrics::create(); | |
| 1921 | |
| 1922 // The style resolution required for rendering text is not available in fram
e-less documents. | |
| 1923 if (!canvas()->document().frame()) | |
| 1924 return metrics; | |
| 1925 | |
| 1926 canvas()->document().updateLayoutTreeForNode(canvas()); | |
| 1927 const Font& font = accessFont(); | |
| 1928 | |
| 1929 TextDirection direction; | |
| 1930 if (state().direction() == CanvasRenderingContext2DState::DirectionInherit) | |
| 1931 direction = determineDirectionality(text); | |
| 1932 else | |
| 1933 direction = toTextDirection(state().direction(), canvas()); | |
| 1934 TextRun textRun(text, 0, 0, TextRun::AllowTrailingExpansion | TextRun::Forbi
dLeadingExpansion, direction, false); | |
| 1935 textRun.setNormalizeSpace(true); | |
| 1936 FloatRect textBounds = font.selectionRectForText(textRun, FloatPoint(), font
.fontDescription().computedSize(), 0, -1, true); | |
| 1937 | |
| 1938 // x direction | |
| 1939 metrics->setWidth(font.width(textRun)); | |
| 1940 metrics->setActualBoundingBoxLeft(-textBounds.x()); | |
| 1941 metrics->setActualBoundingBoxRight(textBounds.maxX()); | |
| 1942 | |
| 1943 // y direction | |
| 1944 const FontMetrics& fontMetrics = font.fontMetrics(); | |
| 1945 const float ascent = fontMetrics.floatAscent(); | |
| 1946 const float descent = fontMetrics.floatDescent(); | |
| 1947 const float baselineY = getFontBaseline(fontMetrics); | |
| 1948 | |
| 1949 metrics->setFontBoundingBoxAscent(ascent - baselineY); | |
| 1950 metrics->setFontBoundingBoxDescent(descent + baselineY); | |
| 1951 metrics->setActualBoundingBoxAscent(-textBounds.y() - baselineY); | |
| 1952 metrics->setActualBoundingBoxDescent(textBounds.maxY() + baselineY); | |
| 1953 | |
| 1954 // Note : top/bottom and ascend/descend are currently the same, so there's n
o difference | |
| 1955 // between the EM box's top and bottom and the font's ascend and desc
end | |
| 1956 metrics->setEmHeightAscent(0); | |
| 1957 metrics->setEmHeightDescent(0); | |
| 1958 | |
| 1959 metrics->setHangingBaseline(-0.8f * ascent + baselineY); | |
| 1960 metrics->setAlphabeticBaseline(baselineY); | |
| 1961 metrics->setIdeographicBaseline(descent + baselineY); | |
| 1962 return metrics; | |
| 1963 } | |
| 1964 | |
| 1965 void CanvasRenderingContext2D::drawTextInternal(const String& text, double x, do
uble y, CanvasRenderingContext2DState::PaintType paintType, double* maxWidth) | |
| 1966 { | |
| 1967 // The style resolution required for rendering text is not available in fram
e-less documents. | |
| 1968 if (!canvas()->document().frame()) | |
| 1969 return; | |
| 1970 | |
| 1971 // accessFont needs the style to be up to date, but updating style can cause
script to run, | |
| 1972 // (e.g. due to autofocus) which can free the canvas (set size to 0, for exa
mple), so update | |
| 1973 // style before grabbing the drawingCanvas. | |
| 1974 canvas()->document().updateLayoutTreeForNode(canvas()); | |
| 1975 | |
| 1976 SkCanvas* c = drawingCanvas(); | |
| 1977 if (!c) | |
| 1978 return; | |
| 1979 | |
| 1980 if (!std::isfinite(x) || !std::isfinite(y)) | |
| 1981 return; | |
| 1982 if (maxWidth && (!std::isfinite(*maxWidth) || *maxWidth <= 0)) | |
| 1983 return; | |
| 1984 | |
| 1985 // Currently, SkPictureImageFilter does not support subpixel text anti-alias
ing, which | |
| 1986 // is expected when !m_hasAlpha, so we need to fall out of display list mode
when | |
| 1987 // drawing text to an opaque canvas | |
| 1988 // crbug.com/583809 | |
| 1989 if (!m_hasAlpha && !isAccelerated()) | |
| 1990 canvas()->disableDeferral(DisableDeferralReasonSubPixelTextAntiAliasingS
upport); | |
| 1991 | |
| 1992 const Font& font = accessFont(); | |
| 1993 if (!font.primaryFont()) | |
| 1994 return; | |
| 1995 | |
| 1996 const FontMetrics& fontMetrics = font.fontMetrics(); | |
| 1997 | |
| 1998 // FIXME: Need to turn off font smoothing. | |
| 1999 | |
| 2000 const ComputedStyle* computedStyle = 0; | |
| 2001 TextDirection direction = toTextDirection(state().direction(), canvas(), &co
mputedStyle); | |
| 2002 bool isRTL = direction == RTL; | |
| 2003 bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : f
alse; | |
| 2004 | |
| 2005 TextRun textRun(text, 0, 0, TextRun::AllowTrailingExpansion, direction, over
ride); | |
| 2006 textRun.setNormalizeSpace(true); | |
| 2007 // Draw the item text at the correct point. | |
| 2008 FloatPoint location(x, y + getFontBaseline(fontMetrics)); | |
| 2009 double fontWidth = font.width(textRun); | |
| 2010 | |
| 2011 bool useMaxWidth = (maxWidth && *maxWidth < fontWidth); | |
| 2012 double width = useMaxWidth ? *maxWidth : fontWidth; | |
| 2013 | |
| 2014 TextAlign align = state().textAlign(); | |
| 2015 if (align == StartTextAlign) | |
| 2016 align = isRTL ? RightTextAlign : LeftTextAlign; | |
| 2017 else if (align == EndTextAlign) | |
| 2018 align = isRTL ? LeftTextAlign : RightTextAlign; | |
| 2019 | |
| 2020 switch (align) { | |
| 2021 case CenterTextAlign: | |
| 2022 location.setX(location.x() - width / 2); | |
| 2023 break; | |
| 2024 case RightTextAlign: | |
| 2025 location.setX(location.x() - width); | |
| 2026 break; | |
| 2027 default: | |
| 2028 break; | |
| 2029 } | |
| 2030 | |
| 2031 // The slop built in to this mask rect matches the heuristic used in FontCGW
in.cpp for GDI text. | |
| 2032 TextRunPaintInfo textRunPaintInfo(textRun); | |
| 2033 textRunPaintInfo.bounds = FloatRect(location.x() - fontMetrics.height() / 2, | |
| 2034 location.y() - fontMetrics.ascent() - fontMetrics.lineGap(), | |
| 2035 width + fontMetrics.height(), | |
| 2036 fontMetrics.lineSpacing()); | |
| 2037 if (paintType == CanvasRenderingContext2DState::StrokePaintType) | |
| 2038 inflateStrokeRect(textRunPaintInfo.bounds); | |
| 2039 | |
| 2040 CanvasRenderingContext2DAutoRestoreSkCanvas stateRestorer(this); | |
| 2041 if (useMaxWidth) { | |
| 2042 drawingCanvas()->save(); | |
| 2043 drawingCanvas()->translate(location.x(), location.y()); | |
| 2044 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" o
p) still work. | |
| 2045 drawingCanvas()->scale((fontWidth > 0 ? (width / fontWidth) : 0), 1); | |
| 2046 location = FloatPoint(); | |
| 2047 } | |
| 2048 | |
| 2049 draw( | |
| 2050 [&font, this, &textRunPaintInfo, &location](SkCanvas* c, const SkPaint*
paint) // draw lambda | |
| 2051 { | |
| 2052 font.drawBidiText(c, textRunPaintInfo, location, Font::UseFallbackIf
FontNotReady, cDeviceScaleFactor, *paint); | |
| 2053 }, | |
| 2054 [](const SkIRect& rect) // overdraw test lambda | |
| 2055 { | |
| 2056 return false; | |
| 2057 }, | |
| 2058 textRunPaintInfo.bounds, paintType); | |
| 2059 } | |
| 2060 | |
| 2061 void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const | |
| 2062 { | 1254 { |
| 2063 // Fast approximation of the stroke's bounding rect. | 1255 // Fast approximation of the stroke's bounding rect. |
| 2064 // This yields a slightly oversized rect but is very fast | 1256 // This yields a slightly oversized rect but is very fast |
| 2065 // compared to Path::strokeBoundingRect(). | 1257 // compared to Path::strokeBoundingRect(). |
| 2066 static const double root2 = sqrtf(2); | 1258 static const double root2 = sqrtf(2); |
| 2067 double delta = state().lineWidth() / 2; | 1259 double delta = state().lineWidth() / 2; |
| 2068 if (state().lineJoin() == MiterJoin) | 1260 if (state().lineJoin() == MiterJoin) |
| 2069 delta *= state().miterLimit(); | 1261 delta *= state().miterLimit(); |
| 2070 else if (state().lineCap() == SquareCap) | 1262 else if (state().lineCap() == SquareCap) |
| 2071 delta *= root2; | 1263 delta *= root2; |
| 2072 | 1264 |
| 2073 rect.inflate(delta); | 1265 rect.inflate(delta); |
| 2074 } | 1266 } |
| 2075 | 1267 |
| 2076 const Font& CanvasRenderingContext2D::accessFont() | 1268 bool BaseRenderingContext2D::imageSmoothingEnabled() const |
| 2077 { | |
| 2078 if (!state().hasRealizedFont()) | |
| 2079 setFont(state().unparsedFont()); | |
| 2080 canvas()->document().canvasFontCache()->willUseCurrentFont(); | |
| 2081 return state().font(); | |
| 2082 } | |
| 2083 | |
| 2084 int CanvasRenderingContext2D::getFontBaseline(const FontMetrics& fontMetrics) co
nst | |
| 2085 { | |
| 2086 switch (state().textBaseline()) { | |
| 2087 case TopTextBaseline: | |
| 2088 return fontMetrics.ascent(); | |
| 2089 case HangingTextBaseline: | |
| 2090 // According to http://wiki.apache.org/xmlgraphics-fop/LineLayout/Alignm
entHandling | |
| 2091 // "FOP (Formatting Objects Processor) puts the hanging baseline at 80%
of the ascender height" | |
| 2092 return (fontMetrics.ascent() * 4) / 5; | |
| 2093 case BottomTextBaseline: | |
| 2094 case IdeographicTextBaseline: | |
| 2095 return -fontMetrics.descent(); | |
| 2096 case MiddleTextBaseline: | |
| 2097 return -fontMetrics.descent() + fontMetrics.height() / 2; | |
| 2098 case AlphabeticTextBaseline: | |
| 2099 default: | |
| 2100 // Do nothing. | |
| 2101 break; | |
| 2102 } | |
| 2103 return 0; | |
| 2104 } | |
| 2105 | |
| 2106 void CanvasRenderingContext2D::setIsHidden(bool hidden) | |
| 2107 { | |
| 2108 if (canvas()->hasImageBuffer()) | |
| 2109 canvas()->buffer()->setIsHidden(hidden); | |
| 2110 if (hidden) { | |
| 2111 pruneLocalFontCache(0); | |
| 2112 } | |
| 2113 } | |
| 2114 | |
| 2115 bool CanvasRenderingContext2D::isTransformInvertible() const | |
| 2116 { | |
| 2117 return state().isTransformInvertible(); | |
| 2118 } | |
| 2119 | |
| 2120 WebLayer* CanvasRenderingContext2D::platformLayer() const | |
| 2121 { | |
| 2122 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0; | |
| 2123 } | |
| 2124 | |
| 2125 bool CanvasRenderingContext2D::imageSmoothingEnabled() const | |
| 2126 { | 1269 { |
| 2127 return state().imageSmoothingEnabled(); | 1270 return state().imageSmoothingEnabled(); |
| 2128 } | 1271 } |
| 2129 | 1272 |
| 2130 void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled) | 1273 void BaseRenderingContext2D::setImageSmoothingEnabled(bool enabled) |
| 2131 { | 1274 { |
| 2132 if (enabled == state().imageSmoothingEnabled()) | 1275 if (enabled == state().imageSmoothingEnabled()) |
| 2133 return; | 1276 return; |
| 2134 | 1277 |
| 2135 modifiableState().setImageSmoothingEnabled(enabled); | 1278 modifiableState().setImageSmoothingEnabled(enabled); |
| 2136 } | 1279 } |
| 2137 | 1280 |
| 2138 String CanvasRenderingContext2D::imageSmoothingQuality() const | 1281 String BaseRenderingContext2D::imageSmoothingQuality() const |
| 2139 { | 1282 { |
| 2140 return state().imageSmoothingQuality(); | 1283 return state().imageSmoothingQuality(); |
| 2141 } | 1284 } |
| 2142 | 1285 |
| 2143 void CanvasRenderingContext2D::setImageSmoothingQuality(const String& quality) | 1286 void BaseRenderingContext2D::setImageSmoothingQuality(const String& quality) |
| 2144 { | 1287 { |
| 2145 if (quality == state().imageSmoothingQuality()) | 1288 if (quality == state().imageSmoothingQuality()) |
| 2146 return; | 1289 return; |
| 2147 | 1290 |
| 2148 modifiableState().setImageSmoothingQuality(quality); | 1291 modifiableState().setImageSmoothingQuality(quality); |
| 2149 } | 1292 } |
| 2150 | 1293 |
| 2151 void CanvasRenderingContext2D::getContextAttributes(Canvas2DContextAttributes& a
ttrs) const | 1294 void BaseRenderingContext2D::checkOverdraw(const SkRect& rect, const SkPaint* pa
int, CanvasRenderingContext2DState::ImageType imageType, DrawType drawType) |
| 2152 { | |
| 2153 attrs.setAlpha(m_hasAlpha); | |
| 2154 } | |
| 2155 | |
| 2156 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element) | |
| 2157 { | |
| 2158 drawFocusIfNeededInternal(m_path, element); | |
| 2159 } | |
| 2160 | |
| 2161 void CanvasRenderingContext2D::drawFocusIfNeeded(Path2D* path2d, Element* elemen
t) | |
| 2162 { | |
| 2163 drawFocusIfNeededInternal(path2d->path(), element); | |
| 2164 } | |
| 2165 | |
| 2166 void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Eleme
nt* element) | |
| 2167 { | |
| 2168 if (!focusRingCallIsValid(path, element)) | |
| 2169 return; | |
| 2170 | |
| 2171 // Note: we need to check document->focusedElement() rather than just callin
g | |
| 2172 // element->focused(), because element->focused() isn't updated until after | |
| 2173 // focus events fire. | |
| 2174 if (element->document().focusedElement() == element) { | |
| 2175 scrollPathIntoViewInternal(path); | |
| 2176 drawFocusRing(path); | |
| 2177 } | |
| 2178 | |
| 2179 // Update its accessible bounds whether it's focused or not. | |
| 2180 updateElementAccessibility(path, element); | |
| 2181 } | |
| 2182 | |
| 2183 bool CanvasRenderingContext2D::focusRingCallIsValid(const Path& path, Element* e
lement) | |
| 2184 { | |
| 2185 ASSERT(element); | |
| 2186 if (!state().isTransformInvertible()) | |
| 2187 return false; | |
| 2188 if (path.isEmpty()) | |
| 2189 return false; | |
| 2190 if (!element->isDescendantOf(canvas())) | |
| 2191 return false; | |
| 2192 | |
| 2193 return true; | |
| 2194 } | |
| 2195 | |
| 2196 void CanvasRenderingContext2D::drawFocusRing(const Path& path) | |
| 2197 { | |
| 2198 if (!drawingCanvas()) | |
| 2199 return; | |
| 2200 | |
| 2201 SkColor color = LayoutTheme::theme().focusRingColor().rgb(); | |
| 2202 const int focusRingWidth = 5; | |
| 2203 | |
| 2204 drawPlatformFocusRing(path.skPath(), drawingCanvas(), color, focusRingWidth)
; | |
| 2205 | |
| 2206 // We need to add focusRingWidth to dirtyRect. | |
| 2207 StrokeData strokeData; | |
| 2208 strokeData.setThickness(focusRingWidth); | |
| 2209 | |
| 2210 SkIRect dirtyRect; | |
| 2211 if (!computeDirtyRect(path.strokeBoundingRect(strokeData), &dirtyRect)) | |
| 2212 return; | |
| 2213 | |
| 2214 didDraw(dirtyRect); | |
| 2215 } | |
| 2216 | |
| 2217 void CanvasRenderingContext2D::updateElementAccessibility(const Path& path, Elem
ent* element) | |
| 2218 { | |
| 2219 element->document().updateLayoutIgnorePendingStylesheets(); | |
| 2220 AXObjectCache* axObjectCache = element->document().existingAXObjectCache(); | |
| 2221 LayoutBoxModelObject* lbmo = canvas()->layoutBoxModelObject(); | |
| 2222 LayoutObject* renderer = canvas()->layoutObject(); | |
| 2223 if (!axObjectCache || !lbmo || !renderer) | |
| 2224 return; | |
| 2225 | |
| 2226 // Get the transformed path. | |
| 2227 Path transformedPath = path; | |
| 2228 transformedPath.transform(state().transform()); | |
| 2229 | |
| 2230 // Offset by the canvas rect, taking border and padding into account. | |
| 2231 IntRect canvasRect = renderer->absoluteBoundingBoxRect(); | |
| 2232 canvasRect.move(lbmo->borderLeft() + lbmo->paddingLeft(), lbmo->borderTop()
+ lbmo->paddingTop()); | |
| 2233 LayoutRect elementRect = enclosingLayoutRect(transformedPath.boundingRect())
; | |
| 2234 elementRect.moveBy(canvasRect.location()); | |
| 2235 axObjectCache->setCanvasObjectBounds(element, elementRect); | |
| 2236 } | |
| 2237 | |
| 2238 void CanvasRenderingContext2D::addHitRegion(const HitRegionOptions& options, Exc
eptionState& exceptionState) | |
| 2239 { | |
| 2240 if (options.id().isEmpty() && !options.control()) { | |
| 2241 exceptionState.throwDOMException(NotSupportedError, "Both id and control
are null."); | |
| 2242 return; | |
| 2243 } | |
| 2244 | |
| 2245 if (options.control() && !canvas()->isSupportedInteractiveCanvasFallback(*op
tions.control())) { | |
| 2246 exceptionState.throwDOMException(NotSupportedError, "The control is neit
her null nor a supported interactive canvas fallback element."); | |
| 2247 return; | |
| 2248 } | |
| 2249 | |
| 2250 Path hitRegionPath = options.hasPath() ? options.path()->path() : m_path; | |
| 2251 | |
| 2252 SkCanvas* c = drawingCanvas(); | |
| 2253 | |
| 2254 if (hitRegionPath.isEmpty() || !c || !state().isTransformInvertible() | |
| 2255 || !c->getClipDeviceBounds(0)) { | |
| 2256 exceptionState.throwDOMException(NotSupportedError, "The specified path
has no pixels."); | |
| 2257 return; | |
| 2258 } | |
| 2259 | |
| 2260 hitRegionPath.transform(state().transform()); | |
| 2261 | |
| 2262 if (state().hasClip()) { | |
| 2263 hitRegionPath.intersectPath(state().getCurrentClipPath()); | |
| 2264 if (hitRegionPath.isEmpty()) | |
| 2265 exceptionState.throwDOMException(NotSupportedError, "The specified p
ath has no pixels."); | |
| 2266 } | |
| 2267 | |
| 2268 if (!m_hitRegionManager) | |
| 2269 m_hitRegionManager = HitRegionManager::create(); | |
| 2270 | |
| 2271 // Remove previous region (with id or control) | |
| 2272 m_hitRegionManager->removeHitRegionById(options.id()); | |
| 2273 m_hitRegionManager->removeHitRegionByControl(options.control().get()); | |
| 2274 | |
| 2275 RefPtrWillBeRawPtr<HitRegion> hitRegion = HitRegion::create(hitRegionPath, o
ptions); | |
| 2276 Element* element = hitRegion->control(); | |
| 2277 if (element && element->isDescendantOf(canvas())) | |
| 2278 updateElementAccessibility(hitRegion->path(), hitRegion->control()); | |
| 2279 m_hitRegionManager->addHitRegion(hitRegion.release()); | |
| 2280 } | |
| 2281 | |
| 2282 void CanvasRenderingContext2D::removeHitRegion(const String& id) | |
| 2283 { | |
| 2284 if (m_hitRegionManager) | |
| 2285 m_hitRegionManager->removeHitRegionById(id); | |
| 2286 } | |
| 2287 | |
| 2288 void CanvasRenderingContext2D::clearHitRegions() | |
| 2289 { | |
| 2290 if (m_hitRegionManager) | |
| 2291 m_hitRegionManager->removeAllHitRegions(); | |
| 2292 } | |
| 2293 | |
| 2294 HitRegion* CanvasRenderingContext2D::hitRegionAtPoint(const FloatPoint& point) | |
| 2295 { | |
| 2296 if (m_hitRegionManager) | |
| 2297 return m_hitRegionManager->getHitRegionAtPoint(point); | |
| 2298 | |
| 2299 return nullptr; | |
| 2300 } | |
| 2301 | |
| 2302 unsigned CanvasRenderingContext2D::hitRegionsCount() const | |
| 2303 { | |
| 2304 if (m_hitRegionManager) | |
| 2305 return m_hitRegionManager->getHitRegionsCount(); | |
| 2306 | |
| 2307 return 0; | |
| 2308 } | |
| 2309 | |
| 2310 void CanvasRenderingContext2D::checkOverdraw(const SkRect& rect, const SkPaint*
paint, CanvasRenderingContext2DState::ImageType imageType, DrawType drawType) | |
| 2311 { | 1295 { |
| 2312 SkCanvas* c = drawingCanvas(); | 1296 SkCanvas* c = drawingCanvas(); |
| 2313 if (!c || !canvas()->buffer()->isRecording()) | 1297 if (!c || !imageBuffer()->isRecording()) |
| 2314 return; | 1298 return; |
| 2315 | 1299 |
| 2316 SkRect deviceRect; | 1300 SkRect deviceRect; |
| 2317 if (drawType == UntransformedUnclippedFill) { | 1301 if (drawType == UntransformedUnclippedFill) { |
| 2318 deviceRect = rect; | 1302 deviceRect = rect; |
| 2319 } else { | 1303 } else { |
| 2320 ASSERT(drawType == ClipFill); | 1304 ASSERT(drawType == ClipFill); |
| 2321 if (state().hasComplexClip()) | 1305 if (state().hasComplexClip()) |
| 2322 return; | 1306 return; |
| 2323 | 1307 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 2350 return; | 1334 return; |
| 2351 } | 1335 } |
| 2352 } | 1336 } |
| 2353 | 1337 |
| 2354 alpha = paint->getAlpha(); | 1338 alpha = paint->getAlpha(); |
| 2355 | 1339 |
| 2356 if (isSourceOver && imageType == CanvasRenderingContext2DState::NoImage)
{ | 1340 if (isSourceOver && imageType == CanvasRenderingContext2DState::NoImage)
{ |
| 2357 SkShader* shader = paint->getShader(); | 1341 SkShader* shader = paint->getShader(); |
| 2358 if (shader) { | 1342 if (shader) { |
| 2359 if (shader->isOpaque() && alpha == 0xFF) | 1343 if (shader->isOpaque() && alpha == 0xFF) |
| 2360 canvas()->buffer()->willOverwriteCanvas(); | 1344 imageBuffer()->willOverwriteCanvas(); |
| 2361 return; | 1345 return; |
| 2362 } | 1346 } |
| 2363 } | 1347 } |
| 2364 } | 1348 } |
| 2365 | 1349 |
| 2366 if (isSourceOver) { | 1350 if (isSourceOver) { |
| 2367 // With source over, we need to certify that alpha == 0xFF for all pixel
s | 1351 // With source over, we need to certify that alpha == 0xFF for all pixel
s |
| 2368 if (imageType == CanvasRenderingContext2DState::NonOpaqueImage) | 1352 if (imageType == CanvasRenderingContext2DState::NonOpaqueImage) |
| 2369 return; | 1353 return; |
| 2370 if (alpha < 0xFF) | 1354 if (alpha < 0xFF) |
| 2371 return; | 1355 return; |
| 2372 } | 1356 } |
| 2373 | 1357 |
| 2374 canvas()->buffer()->willOverwriteCanvas(); | 1358 imageBuffer()->willOverwriteCanvas(); |
| 1359 } |
| 1360 |
| 1361 DEFINE_TRACE(BaseRenderingContext2D) |
| 1362 { |
| 1363 visitor->trace(m_stateStack); |
| 2375 } | 1364 } |
| 2376 | 1365 |
| 2377 } // namespace blink | 1366 } // namespace blink |
| OLD | NEW |