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