| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc.
All rights reserved. | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc.
All rights reserved. |
| 3 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) | 3 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) |
| 4 * Copyright (C) 2007 Alp Toker <alp@atoker.com> | 4 * Copyright (C) 2007 Alp Toker <alp@atoker.com> |
| 5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> | 5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> |
| 6 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> | 6 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> |
| 7 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. | 7 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. |
| 8 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved. | 8 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved. |
| 9 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. | 9 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. |
| 10 * | 10 * |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 */ | 31 */ |
| 32 | 32 |
| 33 #include "modules/canvas2d/CanvasRenderingContext2D.h" | 33 #include "modules/canvas2d/CanvasRenderingContext2D.h" |
| 34 | 34 |
| 35 #include "bindings/core/v8/ExceptionMessages.h" | 35 #include "bindings/core/v8/ExceptionMessages.h" |
| 36 #include "bindings/core/v8/ExceptionState.h" | 36 #include "bindings/core/v8/ExceptionState.h" |
| 37 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | 37 #include "bindings/core/v8/ExceptionStatePlaceholder.h" |
| 38 #include "core/CSSPropertyNames.h" | 38 #include "core/CSSPropertyNames.h" |
| 39 #include "core/css/StylePropertySet.h" | 39 #include "core/css/StylePropertySet.h" |
| 40 #include "core/css/parser/CSSParser.h" | |
| 41 #include "core/css/resolver/StyleResolver.h" | 40 #include "core/css/resolver/StyleResolver.h" |
| 42 #include "core/dom/AXObjectCache.h" | 41 #include "core/dom/AXObjectCache.h" |
| 43 #include "core/dom/StyleEngine.h" | 42 #include "core/dom/StyleEngine.h" |
| 44 #include "core/events/Event.h" | 43 #include "core/events/Event.h" |
| 45 #include "core/frame/ImageBitmap.h" | |
| 46 #include "core/frame/Settings.h" | 44 #include "core/frame/Settings.h" |
| 47 #include "core/html/HTMLVideoElement.h" | |
| 48 #include "core/html/ImageData.h" | |
| 49 #include "core/html/TextMetrics.h" | 45 #include "core/html/TextMetrics.h" |
| 50 #include "core/html/canvas/CanvasFontCache.h" | 46 #include "core/html/canvas/CanvasFontCache.h" |
| 51 #include "core/layout/LayoutBox.h" | 47 #include "core/layout/LayoutBox.h" |
| 52 #include "core/layout/LayoutTheme.h" | 48 #include "core/layout/LayoutTheme.h" |
| 53 #include "modules/canvas2d/CanvasGradient.h" | |
| 54 #include "modules/canvas2d/CanvasPattern.h" | |
| 55 #include "modules/canvas2d/CanvasRenderingContext2DState.h" | |
| 56 #include "modules/canvas2d/CanvasStyle.h" | 49 #include "modules/canvas2d/CanvasStyle.h" |
| 57 #include "modules/canvas2d/HitRegion.h" | 50 #include "modules/canvas2d/HitRegion.h" |
| 58 #include "modules/canvas2d/Path2D.h" | 51 #include "modules/canvas2d/Path2D.h" |
| 59 #include "platform/fonts/FontCache.h" | 52 #include "platform/fonts/FontCache.h" |
| 60 #include "platform/geometry/FloatQuad.h" | |
| 61 #include "platform/graphics/DrawLooperBuilder.h" | 53 #include "platform/graphics/DrawLooperBuilder.h" |
| 62 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h" | 54 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h" |
| 63 #include "platform/graphics/ImageBuffer.h" | 55 #include "platform/graphics/ImageBuffer.h" |
| 64 #include "platform/graphics/StrokeData.h" | 56 #include "platform/graphics/StrokeData.h" |
| 65 #include "platform/graphics/skia/SkiaUtils.h" | 57 #include "platform/graphics/skia/SkiaUtils.h" |
| 66 #include "platform/text/BidiTextRun.h" | 58 #include "platform/text/BidiTextRun.h" |
| 67 #include "public/platform/Platform.h" | 59 #include "public/platform/Platform.h" |
| 68 #include "third_party/skia/include/core/SkCanvas.h" | 60 #include "third_party/skia/include/core/SkCanvas.h" |
| 69 #include "third_party/skia/include/core/SkImageFilter.h" | 61 #include "third_party/skia/include/core/SkImageFilter.h" |
| 70 #include "wtf/ArrayBufferContents.h" | 62 #include "wtf/ArrayBufferContents.h" |
| 71 #include "wtf/CheckedArithmetic.h" | 63 #include "wtf/CheckedArithmetic.h" |
| 72 #include "wtf/MathExtras.h" | 64 #include "wtf/MathExtras.h" |
| 73 #include "wtf/OwnPtr.h" | 65 #include "wtf/OwnPtr.h" |
| 74 #include "wtf/text/StringBuilder.h" | 66 #include "wtf/text/StringBuilder.h" |
| 75 | 67 |
| 76 namespace blink { | 68 namespace blink { |
| 77 | 69 |
| 78 static const char defaultFont[] = "10px sans-serif"; | 70 static const char defaultFont[] = "10px sans-serif"; |
| 79 static const char inherit[] = "inherit"; | 71 static const char inherit[] = "inherit"; |
| 80 static const char rtl[] = "rtl"; | 72 static const char rtl[] = "rtl"; |
| 81 static const char ltr[] = "ltr"; | 73 static const char ltr[] = "ltr"; |
| 82 static const double TryRestoreContextInterval = 0.5; | 74 static const double TryRestoreContextInterval = 0.5; |
| 83 static const unsigned MaxTryRestoreContextAttempts = 4; | 75 static const unsigned MaxTryRestoreContextAttempts = 4; |
| 84 static const double cDeviceScaleFactor = 1.0; // Canvas is device independent | |
| 85 | 76 |
| 86 static bool contextLostRestoredEventsEnabled() | 77 static bool contextLostRestoredEventsEnabled() |
| 87 { | 78 { |
| 88 return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled(); | 79 return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled(); |
| 89 } | 80 } |
| 90 | 81 |
| 91 // Drawing methods need to use this instead of SkAutoCanvasRestore in case overd
raw | 82 // Drawing methods need to use this instead of SkAutoCanvasRestore in case overd
raw |
| 92 // detection substitutes the recording canvas (to discard overdrawn draw calls). | 83 // detection substitutes the recording canvas (to discard overdrawn draw calls). |
| 93 class CanvasRenderingContext2DAutoRestoreSkCanvas { | 84 class CanvasRenderingContext2DAutoRestoreSkCanvas { |
| 94 STACK_ALLOCATED(); | 85 STACK_ALLOCATED(); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 111 c->restoreToCount(m_saveCount); | 102 c->restoreToCount(m_saveCount); |
| 112 m_context->validateStateStack(); | 103 m_context->validateStateStack(); |
| 113 } | 104 } |
| 114 private: | 105 private: |
| 115 RawPtrWillBeMember<CanvasRenderingContext2D> m_context; | 106 RawPtrWillBeMember<CanvasRenderingContext2D> m_context; |
| 116 int m_saveCount; | 107 int m_saveCount; |
| 117 }; | 108 }; |
| 118 | 109 |
| 119 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, co
nst CanvasContextCreationAttributes& attrs, Document& document) | 110 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, co
nst CanvasContextCreationAttributes& attrs, Document& document) |
| 120 : CanvasRenderingContext(canvas) | 111 : CanvasRenderingContext(canvas) |
| 121 , m_clipAntialiasing(NotAntiAliased) | |
| 122 , m_hasAlpha(attrs.alpha()) | 112 , m_hasAlpha(attrs.alpha()) |
| 123 , m_contextLostMode(NotLostContext) | 113 , m_contextLostMode(NotLostContext) |
| 124 , m_contextRestorable(true) | 114 , m_contextRestorable(true) |
| 125 , m_tryRestoreContextAttemptCount(0) | 115 , m_tryRestoreContextAttemptCount(0) |
| 126 , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchC
ontextLostEvent) | 116 , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchC
ontextLostEvent) |
| 127 , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispa
tchContextRestoredEvent) | 117 , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispa
tchContextRestoredEvent) |
| 128 , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreC
ontextEvent) | 118 , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreC
ontextEvent) |
| 129 , m_pruneLocalFontCacheScheduled(false) | 119 , m_pruneLocalFontCacheScheduled(false) |
| 130 { | 120 { |
| 131 if (document.settings() && document.settings()->antialiasedClips2dCanvasEnab
led()) | 121 if (document.settings() && document.settings()->antialiasedClips2dCanvasEnab
led()) |
| 132 m_clipAntialiasing = AntiAliased; | 122 m_clipAntialiasing = AntiAliased; |
| 133 m_stateStack.append(CanvasRenderingContext2DState::create()); | |
| 134 setShouldAntialias(true); | 123 setShouldAntialias(true); |
| 135 } | 124 } |
| 136 | 125 |
| 137 void CanvasRenderingContext2D::unwindStateStack() | 126 void CanvasRenderingContext2D::unwindStateStack() |
| 138 { | 127 { |
| 139 if (size_t stackSize = m_stateStack.size()) { | 128 if (size_t stackSize = m_stateStack.size()) { |
| 140 if (SkCanvas* skCanvas = canvas()->existingDrawingCanvas()) { | 129 if (SkCanvas* skCanvas = canvas()->existingDrawingCanvas()) { |
| 141 while (--stackSize) | 130 while (--stackSize) |
| 142 skCanvas->restore(); | 131 skCanvas->restore(); |
| 143 } | 132 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 162 void CanvasRenderingContext2D::validateStateStack() | 151 void CanvasRenderingContext2D::validateStateStack() |
| 163 { | 152 { |
| 164 #if ENABLE(ASSERT) | 153 #if ENABLE(ASSERT) |
| 165 SkCanvas* skCanvas = canvas()->existingDrawingCanvas(); | 154 SkCanvas* skCanvas = canvas()->existingDrawingCanvas(); |
| 166 if (skCanvas && m_contextLostMode == NotLostContext) { | 155 if (skCanvas && m_contextLostMode == NotLostContext) { |
| 167 ASSERT(static_cast<size_t>(skCanvas->getSaveCount()) == m_stateStack.siz
e()); | 156 ASSERT(static_cast<size_t>(skCanvas->getSaveCount()) == m_stateStack.siz
e()); |
| 168 } | 157 } |
| 169 #endif | 158 #endif |
| 170 } | 159 } |
| 171 | 160 |
| 172 CanvasRenderingContext2DState& CanvasRenderingContext2D::modifiableState() | |
| 173 { | |
| 174 realizeSaves(); | |
| 175 return *m_stateStack.last(); | |
| 176 } | |
| 177 | |
| 178 bool CanvasRenderingContext2D::isAccelerated() const | 161 bool CanvasRenderingContext2D::isAccelerated() const |
| 179 { | 162 { |
| 180 if (!canvas()->hasImageBuffer()) | 163 if (!canvas()->hasImageBuffer()) |
| 181 return false; | 164 return false; |
| 182 return canvas()->buffer()->isAccelerated(); | 165 return canvas()->buffer()->isAccelerated(); |
| 183 } | 166 } |
| 184 | 167 |
| 185 void CanvasRenderingContext2D::stop() | 168 void CanvasRenderingContext2D::stop() |
| 186 { | 169 { |
| 187 if (!isContextLost()) { | 170 if (!isContextLost()) { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 } else { | 203 } else { |
| 221 // legacy synchronous context restoration. | 204 // legacy synchronous context restoration. |
| 222 reset(); | 205 reset(); |
| 223 m_contextLostMode = NotLostContext; | 206 m_contextLostMode = NotLostContext; |
| 224 } | 207 } |
| 225 } | 208 } |
| 226 } | 209 } |
| 227 | 210 |
| 228 DEFINE_TRACE(CanvasRenderingContext2D) | 211 DEFINE_TRACE(CanvasRenderingContext2D) |
| 229 { | 212 { |
| 230 visitor->trace(m_stateStack); | |
| 231 visitor->trace(m_hitRegionManager); | 213 visitor->trace(m_hitRegionManager); |
| 232 CanvasRenderingContext::trace(visitor); | 214 CanvasRenderingContext::trace(visitor); |
| 215 BaseRenderingContext2D::trace(visitor); |
| 233 } | 216 } |
| 234 | 217 |
| 235 void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingCon
text2D>*) | 218 void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingCon
text2D>*) |
| 236 { | 219 { |
| 237 if (contextLostRestoredEventsEnabled()) { | 220 if (contextLostRestoredEventsEnabled()) { |
| 238 RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames
::contextlost); | 221 RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames
::contextlost); |
| 239 canvas()->dispatchEvent(event); | 222 canvas()->dispatchEvent(event); |
| 240 if (event->defaultPrevented()) { | 223 if (event->defaultPrevented()) { |
| 241 m_contextRestorable = false; | 224 m_contextRestorable = false; |
| 242 } | 225 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 ASSERT(m_stateStack.begin() < m_stateStack.end()); | 291 ASSERT(m_stateStack.begin() < m_stateStack.end()); |
| 309 for (currState = m_stateStack.begin(); currState < m_stateStack.end(); currS
tate++) { | 292 for (currState = m_stateStack.begin(); currState < m_stateStack.end(); currS
tate++) { |
| 310 c->setMatrix(SkMatrix::I()); | 293 c->setMatrix(SkMatrix::I()); |
| 311 currState->get()->playbackClips(c); | 294 currState->get()->playbackClips(c); |
| 312 c->setMatrix(affineTransformToSkMatrix(currState->get()->transform())); | 295 c->setMatrix(affineTransformToSkMatrix(currState->get()->transform())); |
| 313 c->save(); | 296 c->save(); |
| 314 } | 297 } |
| 315 c->restore(); | 298 c->restore(); |
| 316 } | 299 } |
| 317 | 300 |
| 318 void CanvasRenderingContext2D::realizeSaves() | |
| 319 { | |
| 320 validateStateStack(); | |
| 321 if (state().hasUnrealizedSaves()) { | |
| 322 ASSERT(m_stateStack.size() >= 1); | |
| 323 // Reduce the current state's unrealized count by one now, | |
| 324 // to reflect the fact we are saving one state. | |
| 325 m_stateStack.last()->restore(); | |
| 326 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. | |
| 328 // 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 | |
| 330 // turn necessary to support correct resizing and unwinding of the stack
). | |
| 331 m_stateStack.last()->resetUnrealizedSaveCount(); | |
| 332 SkCanvas* canvas = drawingCanvas(); | |
| 333 if (canvas) | |
| 334 canvas->save(); | |
| 335 validateStateStack(); | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 void CanvasRenderingContext2D::save() | |
| 340 { | |
| 341 m_stateStack.last()->save(); | |
| 342 } | |
| 343 | |
| 344 void CanvasRenderingContext2D::restore() | |
| 345 { | |
| 346 validateStateStack(); | |
| 347 if (state().hasUnrealizedSaves()) { | |
| 348 // We never realized the save, so just record that it was unnecessary. | |
| 349 m_stateStack.last()->restore(); | |
| 350 return; | |
| 351 } | |
| 352 ASSERT(m_stateStack.size() >= 1); | |
| 353 if (m_stateStack.size() <= 1) | |
| 354 return; | |
| 355 m_path.transform(state().transform()); | |
| 356 m_stateStack.removeLast(); | |
| 357 m_stateStack.last()->clearResolvedFilter(); | |
| 358 m_path.transform(state().transform().inverse()); | |
| 359 SkCanvas* c = drawingCanvas(); | |
| 360 if (c) | |
| 361 c->restore(); | |
| 362 | |
| 363 validateStateStack(); | |
| 364 } | |
| 365 | |
| 366 static inline void convertCanvasStyleToUnionType(CanvasStyle* style, StringOrCan
vasGradientOrCanvasPattern& returnValue) | |
| 367 { | |
| 368 if (CanvasGradient* gradient = style->canvasGradient()) { | |
| 369 returnValue.setCanvasGradient(gradient); | |
| 370 return; | |
| 371 } | |
| 372 if (CanvasPattern* pattern = style->canvasPattern()) { | |
| 373 returnValue.setCanvasPattern(pattern); | |
| 374 return; | |
| 375 } | |
| 376 returnValue.setString(style->color()); | |
| 377 } | |
| 378 | |
| 379 void CanvasRenderingContext2D::strokeStyle(StringOrCanvasGradientOrCanvasPattern
& returnValue) const | |
| 380 { | |
| 381 convertCanvasStyleToUnionType(state().strokeStyle(), returnValue); | |
| 382 } | |
| 383 | |
| 384 void CanvasRenderingContext2D::setStrokeStyle(const StringOrCanvasGradientOrCanv
asPattern& style) | |
| 385 { | |
| 386 ASSERT(!style.isNull()); | |
| 387 | |
| 388 String colorString; | |
| 389 CanvasStyle* canvasStyle = nullptr; | |
| 390 if (style.isString()) { | |
| 391 colorString = style.getAsString(); | |
| 392 if (colorString == state().unparsedStrokeColor()) | |
| 393 return; | |
| 394 Color parsedColor = 0; | |
| 395 if (!parseColorOrCurrentColor(parsedColor, colorString, canvas())) | |
| 396 return; | |
| 397 if (state().strokeStyle()->isEquivalentRGBA(parsedColor.rgb())) { | |
| 398 modifiableState().setUnparsedStrokeColor(colorString); | |
| 399 return; | |
| 400 } | |
| 401 canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb()); | |
| 402 } else if (style.isCanvasGradient()) { | |
| 403 canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient(
)); | |
| 404 } else if (style.isCanvasPattern()) { | |
| 405 CanvasPattern* canvasPattern = style.getAsCanvasPattern(); | |
| 406 | |
| 407 if (canvas()->originClean() && !canvasPattern->originClean()) | |
| 408 canvas()->setOriginTainted(); | |
| 409 | |
| 410 canvasStyle = CanvasStyle::createFromPattern(canvasPattern); | |
| 411 } | |
| 412 | |
| 413 ASSERT(canvasStyle); | |
| 414 | |
| 415 modifiableState().setStrokeStyle(canvasStyle); | |
| 416 modifiableState().setUnparsedStrokeColor(colorString); | |
| 417 modifiableState().clearResolvedFilter(); | |
| 418 } | |
| 419 | |
| 420 void CanvasRenderingContext2D::fillStyle(StringOrCanvasGradientOrCanvasPattern&
returnValue) const | |
| 421 { | |
| 422 convertCanvasStyleToUnionType(state().fillStyle(), returnValue); | |
| 423 } | |
| 424 | |
| 425 void CanvasRenderingContext2D::setFillStyle(const StringOrCanvasGradientOrCanvas
Pattern& style) | |
| 426 { | |
| 427 ASSERT(!style.isNull()); | |
| 428 validateStateStack(); | |
| 429 String colorString; | |
| 430 CanvasStyle* canvasStyle = nullptr; | |
| 431 if (style.isString()) { | |
| 432 colorString = style.getAsString(); | |
| 433 if (colorString == state().unparsedFillColor()) | |
| 434 return; | |
| 435 Color parsedColor = 0; | |
| 436 if (!parseColorOrCurrentColor(parsedColor, colorString, canvas())) | |
| 437 return; | |
| 438 if (state().fillStyle()->isEquivalentRGBA(parsedColor.rgb())) { | |
| 439 modifiableState().setUnparsedFillColor(colorString); | |
| 440 return; | |
| 441 } | |
| 442 canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb()); | |
| 443 } else if (style.isCanvasGradient()) { | |
| 444 canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient(
)); | |
| 445 } else if (style.isCanvasPattern()) { | |
| 446 CanvasPattern* canvasPattern = style.getAsCanvasPattern(); | |
| 447 | |
| 448 if (canvas()->originClean() && !canvasPattern->originClean()) | |
| 449 canvas()->setOriginTainted(); | |
| 450 if (canvasPattern->pattern()->isTextureBacked()) | |
| 451 canvas()->disableDeferral(DisableDeferralReasonUsingTextureBackedPat
tern); | |
| 452 canvasStyle = CanvasStyle::createFromPattern(canvasPattern); | |
| 453 } | |
| 454 | |
| 455 ASSERT(canvasStyle); | |
| 456 modifiableState().setFillStyle(canvasStyle); | |
| 457 modifiableState().setUnparsedFillColor(colorString); | |
| 458 modifiableState().clearResolvedFilter(); | |
| 459 } | |
| 460 | |
| 461 double CanvasRenderingContext2D::lineWidth() const | |
| 462 { | |
| 463 return state().lineWidth(); | |
| 464 } | |
| 465 | |
| 466 void CanvasRenderingContext2D::setLineWidth(double width) | |
| 467 { | |
| 468 if (!std::isfinite(width) || width <= 0) | |
| 469 return; | |
| 470 if (state().lineWidth() == width) | |
| 471 return; | |
| 472 modifiableState().setLineWidth(width); | |
| 473 } | |
| 474 | |
| 475 String CanvasRenderingContext2D::lineCap() const | |
| 476 { | |
| 477 return lineCapName(state().lineCap()); | |
| 478 } | |
| 479 | |
| 480 void CanvasRenderingContext2D::setLineCap(const String& s) | |
| 481 { | |
| 482 LineCap cap; | |
| 483 if (!parseLineCap(s, cap)) | |
| 484 return; | |
| 485 if (state().lineCap() == cap) | |
| 486 return; | |
| 487 modifiableState().setLineCap(cap); | |
| 488 } | |
| 489 | |
| 490 String CanvasRenderingContext2D::lineJoin() const | |
| 491 { | |
| 492 return lineJoinName(state().lineJoin()); | |
| 493 } | |
| 494 | |
| 495 void CanvasRenderingContext2D::setLineJoin(const String& s) | |
| 496 { | |
| 497 LineJoin join; | |
| 498 if (!parseLineJoin(s, join)) | |
| 499 return; | |
| 500 if (state().lineJoin() == join) | |
| 501 return; | |
| 502 modifiableState().setLineJoin(join); | |
| 503 } | |
| 504 | |
| 505 double CanvasRenderingContext2D::miterLimit() const | |
| 506 { | |
| 507 return state().miterLimit(); | |
| 508 } | |
| 509 | |
| 510 void CanvasRenderingContext2D::setMiterLimit(double limit) | |
| 511 { | |
| 512 if (!std::isfinite(limit) || limit <= 0) | |
| 513 return; | |
| 514 if (state().miterLimit() == limit) | |
| 515 return; | |
| 516 modifiableState().setMiterLimit(limit); | |
| 517 } | |
| 518 | |
| 519 double CanvasRenderingContext2D::shadowOffsetX() const | |
| 520 { | |
| 521 return state().shadowOffset().width(); | |
| 522 } | |
| 523 | |
| 524 void CanvasRenderingContext2D::setShadowOffsetX(double x) | |
| 525 { | |
| 526 if (!std::isfinite(x)) | |
| 527 return; | |
| 528 if (state().shadowOffset().width() == x) | |
| 529 return; | |
| 530 modifiableState().setShadowOffsetX(x); | |
| 531 } | |
| 532 | |
| 533 double CanvasRenderingContext2D::shadowOffsetY() const | |
| 534 { | |
| 535 return state().shadowOffset().height(); | |
| 536 } | |
| 537 | |
| 538 void CanvasRenderingContext2D::setShadowOffsetY(double y) | |
| 539 { | |
| 540 if (!std::isfinite(y)) | |
| 541 return; | |
| 542 if (state().shadowOffset().height() == y) | |
| 543 return; | |
| 544 modifiableState().setShadowOffsetY(y); | |
| 545 } | |
| 546 | |
| 547 double CanvasRenderingContext2D::shadowBlur() const | |
| 548 { | |
| 549 return state().shadowBlur(); | |
| 550 } | |
| 551 | |
| 552 void CanvasRenderingContext2D::setShadowBlur(double blur) | |
| 553 { | |
| 554 if (!std::isfinite(blur) || blur < 0) | |
| 555 return; | |
| 556 if (state().shadowBlur() == blur) | |
| 557 return; | |
| 558 modifiableState().setShadowBlur(blur); | |
| 559 } | |
| 560 | |
| 561 String CanvasRenderingContext2D::shadowColor() const | |
| 562 { | |
| 563 return Color(state().shadowColor()).serialized(); | |
| 564 } | |
| 565 | |
| 566 void CanvasRenderingContext2D::setShadowColor(const String& colorString) | |
| 567 { | |
| 568 Color color; | |
| 569 if (!parseColorOrCurrentColor(color, colorString, canvas())) | |
| 570 return; | |
| 571 if (state().shadowColor() == color) | |
| 572 return; | |
| 573 modifiableState().setShadowColor(color.rgb()); | |
| 574 } | |
| 575 | |
| 576 const Vector<double>& CanvasRenderingContext2D::getLineDash() const | |
| 577 { | |
| 578 return state().lineDash(); | |
| 579 } | |
| 580 | |
| 581 static bool lineDashSequenceIsValid(const Vector<double>& dash) | |
| 582 { | |
| 583 for (size_t i = 0; i < dash.size(); i++) { | |
| 584 if (!std::isfinite(dash[i]) || dash[i] < 0) | |
| 585 return false; | |
| 586 } | |
| 587 return true; | |
| 588 } | |
| 589 | |
| 590 void CanvasRenderingContext2D::setLineDash(const Vector<double>& dash) | |
| 591 { | |
| 592 if (!lineDashSequenceIsValid(dash)) | |
| 593 return; | |
| 594 modifiableState().setLineDash(dash); | |
| 595 } | |
| 596 | |
| 597 double CanvasRenderingContext2D::lineDashOffset() const | |
| 598 { | |
| 599 return state().lineDashOffset(); | |
| 600 } | |
| 601 | |
| 602 void CanvasRenderingContext2D::setLineDashOffset(double offset) | |
| 603 { | |
| 604 if (!std::isfinite(offset) || state().lineDashOffset() == offset) | |
| 605 return; | |
| 606 modifiableState().setLineDashOffset(offset); | |
| 607 } | |
| 608 | |
| 609 double CanvasRenderingContext2D::globalAlpha() const | |
| 610 { | |
| 611 return state().globalAlpha(); | |
| 612 } | |
| 613 | |
| 614 void CanvasRenderingContext2D::setGlobalAlpha(double alpha) | |
| 615 { | |
| 616 if (!(alpha >= 0 && alpha <= 1)) | |
| 617 return; | |
| 618 if (state().globalAlpha() == alpha) | |
| 619 return; | |
| 620 modifiableState().setGlobalAlpha(alpha); | |
| 621 } | |
| 622 | |
| 623 bool CanvasRenderingContext2D::shouldAntialias() const | |
| 624 { | |
| 625 return state().shouldAntialias(); | |
| 626 } | |
| 627 | |
| 628 void CanvasRenderingContext2D::setShouldAntialias(bool doAA) | 301 void CanvasRenderingContext2D::setShouldAntialias(bool doAA) |
| 629 { | 302 { |
| 630 modifiableState().setShouldAntialias(doAA); | 303 modifiableState().setShouldAntialias(doAA); |
| 631 } | 304 } |
| 632 | 305 |
| 633 String CanvasRenderingContext2D::globalCompositeOperation() const | |
| 634 { | |
| 635 return compositeOperatorName(compositeOperatorFromSkia(state().globalComposi
te()), blendModeFromSkia(state().globalComposite())); | |
| 636 } | |
| 637 | |
| 638 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operati
on) | |
| 639 { | |
| 640 CompositeOperator op = CompositeSourceOver; | |
| 641 WebBlendMode blendMode = WebBlendModeNormal; | |
| 642 if (!parseCompositeAndBlendOperator(operation, op, blendMode)) | |
| 643 return; | |
| 644 SkXfermode::Mode xfermode = WebCoreCompositeToSkiaComposite(op, blendMode); | |
| 645 if (state().globalComposite() == xfermode) | |
| 646 return; | |
| 647 modifiableState().setGlobalComposite(xfermode); | |
| 648 } | |
| 649 | |
| 650 String CanvasRenderingContext2D::filter() const | |
| 651 { | |
| 652 return state().unparsedFilter(); | |
| 653 } | |
| 654 | |
| 655 void CanvasRenderingContext2D::setFilter(const String& filterString) | |
| 656 { | |
| 657 if (filterString == state().unparsedFilter()) | |
| 658 return; | |
| 659 | |
| 660 RefPtrWillBeRawPtr<CSSValue> filterValue = CSSParser::parseSingleValue(CSSPr
opertyWebkitFilter, filterString, CSSParserContext(HTMLStandardMode, 0)); | |
| 661 | |
| 662 if (!filterValue || filterValue->isInitialValue() || filterValue->isInherite
dValue()) | |
| 663 return; | |
| 664 | |
| 665 modifiableState().setUnparsedFilter(filterString); | |
| 666 modifiableState().setFilter(filterValue.release()); | |
| 667 } | |
| 668 | |
| 669 PassRefPtrWillBeRawPtr<SVGMatrixTearOff> CanvasRenderingContext2D::currentTransf
orm() const | |
| 670 { | |
| 671 return SVGMatrixTearOff::create(state().transform()); | |
| 672 } | |
| 673 | |
| 674 void CanvasRenderingContext2D::setCurrentTransform(PassRefPtrWillBeRawPtr<SVGMat
rixTearOff> passMatrixTearOff) | |
| 675 { | |
| 676 RefPtrWillBeRawPtr<SVGMatrixTearOff> matrixTearOff = passMatrixTearOff; | |
| 677 const AffineTransform& transform = matrixTearOff->value(); | |
| 678 setTransform(transform.a(), transform.b(), transform.c(), transform.d(), tra
nsform.e(), transform.f()); | |
| 679 } | |
| 680 | |
| 681 void CanvasRenderingContext2D::scale(double sx, double sy) | |
| 682 { | |
| 683 SkCanvas* c = drawingCanvas(); | |
| 684 if (!c) | |
| 685 return; | |
| 686 | |
| 687 if (!std::isfinite(sx) || !std::isfinite(sy)) | |
| 688 return; | |
| 689 | |
| 690 AffineTransform newTransform = state().transform(); | |
| 691 newTransform.scaleNonUniform(sx, sy); | |
| 692 if (state().transform() == newTransform) | |
| 693 return; | |
| 694 | |
| 695 modifiableState().setTransform(newTransform); | |
| 696 if (!state().isTransformInvertible()) | |
| 697 return; | |
| 698 | |
| 699 c->scale(sx, sy); | |
| 700 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy)); | |
| 701 } | |
| 702 | |
| 703 void CanvasRenderingContext2D::rotate(double angleInRadians) | |
| 704 { | |
| 705 SkCanvas* c = drawingCanvas(); | |
| 706 if (!c) | |
| 707 return; | |
| 708 | |
| 709 if (!std::isfinite(angleInRadians)) | |
| 710 return; | |
| 711 | |
| 712 AffineTransform newTransform = state().transform(); | |
| 713 newTransform.rotateRadians(angleInRadians); | |
| 714 if (state().transform() == newTransform) | |
| 715 return; | |
| 716 | |
| 717 modifiableState().setTransform(newTransform); | |
| 718 if (!state().isTransformInvertible()) | |
| 719 return; | |
| 720 c->rotate(angleInRadians * (180.0 / piFloat)); | |
| 721 m_path.transform(AffineTransform().rotateRadians(-angleInRadians)); | |
| 722 } | |
| 723 | |
| 724 void CanvasRenderingContext2D::translate(double tx, double ty) | |
| 725 { | |
| 726 SkCanvas* c = drawingCanvas(); | |
| 727 if (!c) | |
| 728 return; | |
| 729 if (!state().isTransformInvertible()) | |
| 730 return; | |
| 731 | |
| 732 if (!std::isfinite(tx) || !std::isfinite(ty)) | |
| 733 return; | |
| 734 | |
| 735 AffineTransform newTransform = state().transform(); | |
| 736 newTransform.translate(tx, ty); | |
| 737 if (state().transform() == newTransform) | |
| 738 return; | |
| 739 | |
| 740 modifiableState().setTransform(newTransform); | |
| 741 if (!state().isTransformInvertible()) | |
| 742 return; | |
| 743 c->translate(tx, ty); | |
| 744 m_path.transform(AffineTransform().translate(-tx, -ty)); | |
| 745 } | |
| 746 | |
| 747 void CanvasRenderingContext2D::transform(double m11, double m12, double m21, dou
ble m22, double dx, double dy) | |
| 748 { | |
| 749 SkCanvas* c = drawingCanvas(); | |
| 750 if (!c) | |
| 751 return; | |
| 752 | |
| 753 if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std
::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy)) | |
| 754 return; | |
| 755 | |
| 756 AffineTransform transform(m11, m12, m21, m22, dx, dy); | |
| 757 AffineTransform newTransform = state().transform() * transform; | |
| 758 if (state().transform() == newTransform) | |
| 759 return; | |
| 760 | |
| 761 modifiableState().setTransform(newTransform); | |
| 762 if (!state().isTransformInvertible()) | |
| 763 return; | |
| 764 | |
| 765 c->concat(affineTransformToSkMatrix(transform)); | |
| 766 m_path.transform(transform.inverse()); | |
| 767 } | |
| 768 | |
| 769 void CanvasRenderingContext2D::resetTransform() | |
| 770 { | |
| 771 SkCanvas* c = drawingCanvas(); | |
| 772 if (!c) | |
| 773 return; | |
| 774 | |
| 775 AffineTransform ctm = state().transform(); | |
| 776 bool invertibleCTM = state().isTransformInvertible(); | |
| 777 // It is possible that CTM is identity while CTM is not invertible. | |
| 778 // When CTM becomes non-invertible, realizeSaves() can make CTM identity. | |
| 779 if (ctm.isIdentity() && invertibleCTM) | |
| 780 return; | |
| 781 | |
| 782 // resetTransform() resolves the non-invertible CTM state. | |
| 783 modifiableState().resetTransform(); | |
| 784 c->setMatrix(affineTransformToSkMatrix(canvas()->baseTransform())); | |
| 785 | |
| 786 if (invertibleCTM) | |
| 787 m_path.transform(ctm); | |
| 788 // 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. | |
| 790 } | |
| 791 | |
| 792 void CanvasRenderingContext2D::setTransform(double m11, double m12, double m21,
double m22, double dx, double dy) | |
| 793 { | |
| 794 SkCanvas* c = drawingCanvas(); | |
| 795 if (!c) | |
| 796 return; | |
| 797 | |
| 798 if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std
::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy)) | |
| 799 return; | |
| 800 | |
| 801 resetTransform(); | |
| 802 transform(m11, m12, m21, m22, dx, dy); | |
| 803 } | |
| 804 | |
| 805 void CanvasRenderingContext2D::beginPath() | |
| 806 { | |
| 807 m_path.clear(); | |
| 808 } | |
| 809 | |
| 810 static bool validateRectForCanvas(double& x, double& y, double& width, double& h
eight) | |
| 811 { | |
| 812 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || !std:
:isfinite(height)) | |
| 813 return false; | |
| 814 | |
| 815 if (!width && !height) | |
| 816 return false; | |
| 817 | |
| 818 if (width < 0) { | |
| 819 width = -width; | |
| 820 x -= width; | |
| 821 } | |
| 822 | |
| 823 if (height < 0) { | |
| 824 height = -height; | |
| 825 y -= height; | |
| 826 } | |
| 827 | |
| 828 return true; | |
| 829 } | |
| 830 | |
| 831 static bool isFullCanvasCompositeMode(SkXfermode::Mode op) | |
| 832 { | |
| 833 // See 4.8.11.1.3 Compositing | |
| 834 // CompositeSourceAtop and CompositeDestinationOut are not listed here as th
e platforms already | |
| 835 // implement the specification's behavior. | |
| 836 return op == SkXfermode::kSrcIn_Mode || op == SkXfermode::kSrcOut_Mode || op
== SkXfermode::kDstIn_Mode || op == SkXfermode::kDstATop_Mode; | |
| 837 } | |
| 838 | |
| 839 template<typename DrawFunc> | |
| 840 void CanvasRenderingContext2D::compositedDraw(const DrawFunc& drawFunc, SkCanvas
* c, CanvasRenderingContext2DState::PaintType paintType, CanvasRenderingContext2
DState::ImageType imageType) | |
| 841 { | |
| 842 SkImageFilter* filter = state().getFilter(canvas(), accessFont(), canvas()->
size(), this); | |
| 843 ASSERT(isFullCanvasCompositeMode(state().globalComposite()) || filter); | |
| 844 SkMatrix ctm = c->getTotalMatrix(); | |
| 845 c->resetMatrix(); | |
| 846 SkPaint compositePaint; | |
| 847 compositePaint.setXfermodeMode(state().globalComposite()); | |
| 848 if (state().shouldDrawShadows()) { | |
| 849 // unroll into two independently composited passes if drawing shadows | |
| 850 SkPaint shadowPaint = *state().getPaint(paintType, DrawShadowOnly, image
Type); | |
| 851 int saveCount = c->getSaveCount(); | |
| 852 if (filter) { | |
| 853 SkPaint filterPaint; | |
| 854 filterPaint.setImageFilter(filter); | |
| 855 // TODO(junov): crbug.com/502921 We could use primitive bounds if we
knew that the filter | |
| 856 // does not affect transparent black regions. | |
| 857 c->saveLayer(nullptr, &shadowPaint); | |
| 858 c->saveLayer(nullptr, &filterPaint); | |
| 859 SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroun
dOnly, imageType); | |
| 860 c->setMatrix(ctm); | |
| 861 drawFunc(c, &foregroundPaint); | |
| 862 } else { | |
| 863 ASSERT(isFullCanvasCompositeMode(state().globalComposite())); | |
| 864 c->saveLayer(nullptr, &compositePaint); | |
| 865 shadowPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); | |
| 866 c->setMatrix(ctm); | |
| 867 drawFunc(c, &shadowPaint); | |
| 868 } | |
| 869 c->restoreToCount(saveCount); | |
| 870 } | |
| 871 | |
| 872 compositePaint.setImageFilter(filter); | |
| 873 // TODO(junov): crbug.com/502921 We could use primitive bounds if we knew th
at the filter | |
| 874 // does not affect transparent black regions *and* !isFullCanvasCompositeMod
e | |
| 875 c->saveLayer(nullptr, &compositePaint); | |
| 876 SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroundOnly, i
mageType); | |
| 877 foregroundPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); | |
| 878 c->setMatrix(ctm); | |
| 879 drawFunc(c, &foregroundPaint); | |
| 880 c->restore(); | |
| 881 c->setMatrix(ctm); | |
| 882 } | |
| 883 | |
| 884 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) | |
| 886 { | |
| 887 if (!state().isTransformInvertible()) | |
| 888 return false; | |
| 889 | |
| 890 SkIRect clipBounds; | |
| 891 if (!drawingCanvas() || !drawingCanvas()->getClipDeviceBounds(&clipBounds)) | |
| 892 return false; | |
| 893 | |
| 894 // If gradient size is zero, then paint nothing. | |
| 895 CanvasStyle* style = state().style(paintType); | |
| 896 if (style) { | |
| 897 CanvasGradient* gradient = style->canvasGradient(); | |
| 898 if (gradient && gradient->gradient()->isZeroSize()) | |
| 899 return false; | |
| 900 } | |
| 901 | |
| 902 if (isFullCanvasCompositeMode(state().globalComposite()) || state().hasFilte
r(canvas(), accessFont(), canvas()->size(), this)) { | |
| 903 compositedDraw(drawFunc, drawingCanvas(), paintType, imageType); | |
| 904 didDraw(clipBounds); | |
| 905 } else if (state().globalComposite() == SkXfermode::kSrc_Mode) { | |
| 906 clearCanvas(); // takes care of checkOverdraw() | |
| 907 const SkPaint* paint = state().getPaint(paintType, DrawForegroundOnly, i
mageType); | |
| 908 drawFunc(drawingCanvas(), paint); | |
| 909 didDraw(clipBounds); | |
| 910 } else { | |
| 911 SkIRect dirtyRect; | |
| 912 if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) { | |
| 913 const SkPaint* paint = state().getPaint(paintType, DrawShadowAndFore
ground, imageType); | |
| 914 if (paintType != CanvasRenderingContext2DState::StrokePaintType && d
rawCoversClipBounds(clipBounds)) | |
| 915 checkOverdraw(bounds, paint, imageType, ClipFill); | |
| 916 drawFunc(drawingCanvas(), paint); | |
| 917 didDraw(dirtyRect); | |
| 918 } | |
| 919 } | |
| 920 return true; | |
| 921 } | |
| 922 | |
| 923 static bool isPathExpensive(const Path& path) | |
| 924 { | |
| 925 const SkPath& skPath = path.skPath(); | |
| 926 if (ExpensiveCanvasHeuristicParameters::ConcavePathsAreExpensive && !skPath.
isConvex()) | |
| 927 return true; | |
| 928 | |
| 929 if (skPath.countPoints() > ExpensiveCanvasHeuristicParameters::ExpensivePath
PointCount) | |
| 930 return true; | |
| 931 | |
| 932 return false; | |
| 933 } | |
| 934 | |
| 935 void CanvasRenderingContext2D::drawPathInternal(const Path& path, CanvasRenderin
gContext2DState::PaintType paintType, SkPath::FillType fillType) | |
| 936 { | |
| 937 if (path.isEmpty()) | |
| 938 return; | |
| 939 | |
| 940 SkPath skPath = path.skPath(); | |
| 941 FloatRect bounds = path.boundingRect(); | |
| 942 skPath.setFillType(fillType); | |
| 943 | |
| 944 if (paintType == CanvasRenderingContext2DState::StrokePaintType) | |
| 945 inflateStrokeRect(bounds); | |
| 946 | |
| 947 if (!drawingCanvas()) | |
| 948 return; | |
| 949 | |
| 950 if (draw( | |
| 951 [&skPath, this](SkCanvas* c, const SkPaint* paint) // draw lambda | |
| 952 { | |
| 953 c->drawPath(skPath, *paint); | |
| 954 }, | |
| 955 [](const SkIRect& rect) // overdraw test lambda | |
| 956 { | |
| 957 return false; | |
| 958 }, bounds, paintType)) { | |
| 959 if (isPathExpensive(path)) { | |
| 960 ImageBuffer* buffer = canvas()->buffer(); | |
| 961 if (buffer) | |
| 962 buffer->setHasExpensiveOp(); | |
| 963 } | |
| 964 } | |
| 965 } | |
| 966 | |
| 967 static SkPath::FillType parseWinding(const String& windingRuleString) | |
| 968 { | |
| 969 if (windingRuleString == "nonzero") | |
| 970 return SkPath::kWinding_FillType; | |
| 971 if (windingRuleString == "evenodd") | |
| 972 return SkPath::kEvenOdd_FillType; | |
| 973 | |
| 974 ASSERT_NOT_REACHED(); | |
| 975 return SkPath::kEvenOdd_FillType; | |
| 976 } | |
| 977 | |
| 978 void CanvasRenderingContext2D::fill(const String& windingRuleString) | |
| 979 { | |
| 980 drawPathInternal(m_path, CanvasRenderingContext2DState::FillPaintType, parse
Winding(windingRuleString)); | |
| 981 } | |
| 982 | |
| 983 void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleSt
ring) | |
| 984 { | |
| 985 drawPathInternal(domPath->path(), CanvasRenderingContext2DState::FillPaintTy
pe, parseWinding(windingRuleString)); | |
| 986 } | |
| 987 | |
| 988 void CanvasRenderingContext2D::stroke() | |
| 989 { | |
| 990 drawPathInternal(m_path, CanvasRenderingContext2DState::StrokePaintType); | |
| 991 } | |
| 992 | |
| 993 void CanvasRenderingContext2D::stroke(Path2D* domPath) | |
| 994 { | |
| 995 drawPathInternal(domPath->path(), CanvasRenderingContext2DState::StrokePaint
Type); | |
| 996 } | |
| 997 | |
| 998 void CanvasRenderingContext2D::fillRect(double x, double y, double width, double
height) | |
| 999 { | |
| 1000 if (!validateRectForCanvas(x, y, width, height)) | |
| 1001 return; | |
| 1002 | |
| 1003 if (!drawingCanvas()) | |
| 1004 return; | |
| 1005 | |
| 1006 SkRect rect = SkRect::MakeXYWH(x, y, width, height); | |
| 1007 draw( | |
| 1008 [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda | |
| 1009 { | |
| 1010 c->drawRect(rect, *paint); | |
| 1011 }, | |
| 1012 [&rect, this](const SkIRect& clipBounds) // overdraw test lambda | |
| 1013 { | |
| 1014 return rectContainsTransformedRect(rect, clipBounds); | |
| 1015 }, rect, CanvasRenderingContext2DState::FillPaintType); | |
| 1016 } | |
| 1017 | |
| 1018 static void strokeRectOnCanvas(const FloatRect& rect, SkCanvas* canvas, const Sk
Paint* paint) | |
| 1019 { | |
| 1020 ASSERT(paint->getStyle() == SkPaint::kStroke_Style); | |
| 1021 if ((rect.width() > 0) != (rect.height() > 0)) { | |
| 1022 // When stroking, we must skip the zero-dimension segments | |
| 1023 SkPath path; | |
| 1024 path.moveTo(rect.x(), rect.y()); | |
| 1025 path.lineTo(rect.maxX(), rect.maxY()); | |
| 1026 path.close(); | |
| 1027 canvas->drawPath(path, *paint); | |
| 1028 return; | |
| 1029 } | |
| 1030 canvas->drawRect(rect, *paint); | |
| 1031 } | |
| 1032 | |
| 1033 void CanvasRenderingContext2D::strokeRect(double x, double y, double width, doub
le height) | |
| 1034 { | |
| 1035 if (!validateRectForCanvas(x, y, width, height)) | |
| 1036 return; | |
| 1037 | |
| 1038 if (!drawingCanvas()) | |
| 1039 return; | |
| 1040 | |
| 1041 SkRect rect = SkRect::MakeXYWH(x, y, width, height); | |
| 1042 FloatRect bounds = rect; | |
| 1043 inflateStrokeRect(bounds); | |
| 1044 draw( | |
| 1045 [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda | |
| 1046 { | |
| 1047 strokeRectOnCanvas(rect, c, paint); | |
| 1048 }, | |
| 1049 [](const SkIRect& clipBounds) // overdraw test lambda | |
| 1050 { | |
| 1051 return false; | |
| 1052 }, bounds, CanvasRenderingContext2DState::StrokePaintType); | |
| 1053 } | |
| 1054 | |
| 1055 void CanvasRenderingContext2D::clipInternal(const Path& path, const String& wind
ingRuleString) | |
| 1056 { | |
| 1057 SkCanvas* c = drawingCanvas(); | |
| 1058 if (!c) { | |
| 1059 return; | |
| 1060 } | |
| 1061 if (!state().isTransformInvertible()) { | |
| 1062 return; | |
| 1063 } | |
| 1064 | |
| 1065 SkPath skPath = path.skPath(); | |
| 1066 skPath.setFillType(parseWinding(windingRuleString)); | |
| 1067 modifiableState().clipPath(skPath, m_clipAntialiasing); | |
| 1068 c->clipPath(skPath, SkRegion::kIntersect_Op, m_clipAntialiasing == AntiAlias
ed); | |
| 1069 if (ExpensiveCanvasHeuristicParameters::ComplexClipsAreExpensive && !skPath.
isRect(0) && canvas()->hasImageBuffer()) { | |
| 1070 canvas()->buffer()->setHasExpensiveOp(); | |
| 1071 } | |
| 1072 } | |
| 1073 | |
| 1074 void CanvasRenderingContext2D::clip(const String& windingRuleString) | |
| 1075 { | |
| 1076 clipInternal(m_path, windingRuleString); | |
| 1077 } | |
| 1078 | |
| 1079 void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleSt
ring) | |
| 1080 { | |
| 1081 clipInternal(domPath->path(), windingRuleString); | |
| 1082 } | |
| 1083 | |
| 1084 bool CanvasRenderingContext2D::isPointInPath(const double x, const double y, con
st String& windingRuleString) | |
| 1085 { | |
| 1086 return isPointInPathInternal(m_path, x, y, windingRuleString); | |
| 1087 } | |
| 1088 | |
| 1089 bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const double x, co
nst double y, const String& windingRuleString) | |
| 1090 { | |
| 1091 return isPointInPathInternal(domPath->path(), x, y, windingRuleString); | |
| 1092 } | |
| 1093 | |
| 1094 bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, const dou
ble x, const double y, const String& windingRuleString) | |
| 1095 { | |
| 1096 SkCanvas* c = drawingCanvas(); | |
| 1097 if (!c) | |
| 1098 return false; | |
| 1099 if (!state().isTransformInvertible()) | |
| 1100 return false; | |
| 1101 | |
| 1102 FloatPoint point(x, y); | |
| 1103 if (!std::isfinite(point.x()) || !std::isfinite(point.y())) | |
| 1104 return false; | |
| 1105 AffineTransform ctm = state().transform(); | |
| 1106 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); | |
| 1107 | |
| 1108 return path.contains(transformedPoint, SkFillTypeToWindRule(parseWinding(win
dingRuleString))); | |
| 1109 } | |
| 1110 | |
| 1111 bool CanvasRenderingContext2D::isPointInStroke(const double x, const double y) | |
| 1112 { | |
| 1113 return isPointInStrokeInternal(m_path, x, y); | |
| 1114 } | |
| 1115 | |
| 1116 bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const double x,
const double y) | |
| 1117 { | |
| 1118 return isPointInStrokeInternal(domPath->path(), x, y); | |
| 1119 } | |
| 1120 | |
| 1121 bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, const d
ouble x, const double y) | |
| 1122 { | |
| 1123 SkCanvas* c = drawingCanvas(); | |
| 1124 if (!c) | |
| 1125 return false; | |
| 1126 if (!state().isTransformInvertible()) | |
| 1127 return false; | |
| 1128 | |
| 1129 FloatPoint point(x, y); | |
| 1130 if (!std::isfinite(point.x()) || !std::isfinite(point.y())) | |
| 1131 return false; | |
| 1132 AffineTransform ctm = state().transform(); | |
| 1133 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); | |
| 1134 | |
| 1135 StrokeData strokeData; | |
| 1136 strokeData.setThickness(state().lineWidth()); | |
| 1137 strokeData.setLineCap(state().lineCap()); | |
| 1138 strokeData.setLineJoin(state().lineJoin()); | |
| 1139 strokeData.setMiterLimit(state().miterLimit()); | |
| 1140 Vector<float> lineDash(state().lineDash().size()); | |
| 1141 std::copy(state().lineDash().begin(), state().lineDash().end(), lineDash.beg
in()); | |
| 1142 strokeData.setLineDash(lineDash, state().lineDashOffset()); | |
| 1143 return path.strokeContains(transformedPoint, strokeData); | |
| 1144 } | |
| 1145 | |
| 1146 void CanvasRenderingContext2D::scrollPathIntoView() | 306 void CanvasRenderingContext2D::scrollPathIntoView() |
| 1147 { | 307 { |
| 1148 scrollPathIntoViewInternal(m_path); | 308 scrollPathIntoViewInternal(m_path); |
| 1149 } | 309 } |
| 1150 | 310 |
| 1151 void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d) | 311 void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d) |
| 1152 { | 312 { |
| 1153 scrollPathIntoViewInternal(path2d->path()); | 313 scrollPathIntoViewInternal(path2d->path()); |
| 1154 } | 314 } |
| 1155 | 315 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1177 | 337 |
| 1178 renderer->scrollRectToVisible( | 338 renderer->scrollRectToVisible( |
| 1179 pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopA
lways); | 339 pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopA
lways); |
| 1180 | 340 |
| 1181 // TODO: should implement "inform the user" that the caret and/or | 341 // 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 | 342 // selection the specified rectangle of the canvas. See http://crbug.com/357
987 |
| 1183 } | 343 } |
| 1184 | 344 |
| 1185 void CanvasRenderingContext2D::clearRect(double x, double y, double width, doubl
e height) | 345 void CanvasRenderingContext2D::clearRect(double x, double y, double width, doubl
e height) |
| 1186 { | 346 { |
| 1187 if (!validateRectForCanvas(x, y, width, height)) | 347 BaseRenderingContext2D::clearRect(x, y, width, height); |
| 1188 return; | |
| 1189 | |
| 1190 SkCanvas* c = drawingCanvas(); | |
| 1191 if (!c) | |
| 1192 return; | |
| 1193 if (!state().isTransformInvertible()) | |
| 1194 return; | |
| 1195 | |
| 1196 SkIRect clipBounds; | |
| 1197 if (!c->getClipDeviceBounds(&clipBounds)) | |
| 1198 return; | |
| 1199 | |
| 1200 SkPaint clearPaint; | |
| 1201 clearPaint.setXfermodeMode(SkXfermode::kClear_Mode); | |
| 1202 clearPaint.setStyle(SkPaint::kFill_Style); | |
| 1203 FloatRect rect(x, y, width, height); | 348 FloatRect rect(x, y, width, height); |
| 1204 | 349 |
| 1205 if (rectContainsTransformedRect(rect, clipBounds)) { | |
| 1206 checkOverdraw(rect, &clearPaint, CanvasRenderingContext2DState::NoImage,
ClipFill); | |
| 1207 if (drawingCanvas()) | |
| 1208 drawingCanvas()->drawRect(rect, clearPaint); | |
| 1209 didDraw(clipBounds); | |
| 1210 } else { | |
| 1211 SkIRect dirtyRect; | |
| 1212 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) { | |
| 1213 c->drawRect(rect, clearPaint); | |
| 1214 didDraw(dirtyRect); | |
| 1215 } | |
| 1216 } | |
| 1217 | |
| 1218 if (m_hitRegionManager) { | 350 if (m_hitRegionManager) { |
| 1219 m_hitRegionManager->removeHitRegionsInRect(rect, state().transform()); | 351 m_hitRegionManager->removeHitRegionsInRect(rect, state().transform()); |
| 1220 } | 352 } |
| 1221 } | 353 } |
| 1222 | 354 |
| 1223 static inline FloatRect normalizeRect(const FloatRect& rect) | |
| 1224 { | |
| 1225 return FloatRect(std::min(rect.x(), rect.maxX()), | |
| 1226 std::min(rect.y(), rect.maxY()), | |
| 1227 std::max(rect.width(), -rect.width()), | |
| 1228 std::max(rect.height(), -rect.height())); | |
| 1229 } | |
| 1230 | |
| 1231 static inline void clipRectsToImageRect(const FloatRect& imageRect, FloatRect* s
rcRect, FloatRect* dstRect) | |
| 1232 { | |
| 1233 if (imageRect.contains(*srcRect)) | |
| 1234 return; | |
| 1235 | |
| 1236 // Compute the src to dst transform | |
| 1237 FloatSize scale(dstRect->size().width() / srcRect->size().width(), dstRect->
size().height() / srcRect->size().height()); | |
| 1238 FloatPoint scaledSrcLocation = srcRect->location(); | |
| 1239 scaledSrcLocation.scale(scale.width(), scale.height()); | |
| 1240 FloatSize offset = dstRect->location() - scaledSrcLocation; | |
| 1241 | |
| 1242 srcRect->intersect(imageRect); | |
| 1243 | |
| 1244 // To clip the destination rectangle in the same proportion, transform the c
lipped src rect | |
| 1245 *dstRect = *srcRect; | |
| 1246 dstRect->scale(scale.width(), scale.height()); | |
| 1247 dstRect->move(offset); | |
| 1248 } | |
| 1249 | |
| 1250 static inline CanvasImageSource* toImageSourceInternal(const CanvasImageSourceUn
ion& value) | |
| 1251 { | |
| 1252 if (value.isHTMLImageElement()) | |
| 1253 return value.getAsHTMLImageElement().get(); | |
| 1254 if (value.isHTMLVideoElement()) | |
| 1255 return value.getAsHTMLVideoElement().get(); | |
| 1256 if (value.isHTMLCanvasElement()) | |
| 1257 return value.getAsHTMLCanvasElement().get(); | |
| 1258 if (value.isImageBitmap()) | |
| 1259 return value.getAsImageBitmap().get(); | |
| 1260 ASSERT_NOT_REACHED(); | |
| 1261 return nullptr; | |
| 1262 } | |
| 1263 | |
| 1264 void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSour
ce, double x, double y, ExceptionState& exceptionState) | |
| 1265 { | |
| 1266 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); | |
| 1267 FloatSize sourceRectSize = imageSourceInternal->elementSize(); | |
| 1268 FloatSize destRectSize = imageSourceInternal->defaultDestinationSize(); | |
| 1269 drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize.
height(), x, y, destRectSize.width(), destRectSize.height(), exceptionState); | |
| 1270 } | |
| 1271 | |
| 1272 void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSour
ce, | |
| 1273 double x, double y, double width, double height, ExceptionState& exceptionSt
ate) | |
| 1274 { | |
| 1275 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); | |
| 1276 FloatSize sourceRectSize = imageSourceInternal->elementSize(); | |
| 1277 drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize.
height(), x, y, width, height, exceptionState); | |
| 1278 } | |
| 1279 | |
| 1280 void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSour
ce, | |
| 1281 double sx, double sy, double sw, double sh, | |
| 1282 double dx, double dy, double dw, double dh, ExceptionState& exceptionState) | |
| 1283 { | |
| 1284 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); | |
| 1285 drawImage(imageSourceInternal, sx, sy, sw, sh, dx, dy, dw, dh, exceptionStat
e); | |
| 1286 } | |
| 1287 | |
| 1288 bool CanvasRenderingContext2D::shouldDrawImageAntialiased(const FloatRect& destR
ect) const | |
| 1289 { | |
| 1290 if (!shouldAntialias()) | |
| 1291 return false; | |
| 1292 SkCanvas* c = drawingCanvas(); | |
| 1293 ASSERT(c); | |
| 1294 | |
| 1295 const SkMatrix &ctm = c->getTotalMatrix(); | |
| 1296 // Don't disable anti-aliasing if we're rotated or skewed. | |
| 1297 if (!ctm.rectStaysRect()) | |
| 1298 return true; | |
| 1299 // Check if the dimensions of the destination are "small" (less than one | |
| 1300 // 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 | |
| 1302 // vice versa. We can query the kAffine_Mask flag to determine which case | |
| 1303 // it is. | |
| 1304 // FIXME: This queries the CTM while drawing, which is generally | |
| 1305 // discouraged. Always drawing with AA can negatively impact performance | |
| 1306 // though - that's why it's not always on. | |
| 1307 SkScalar widthExpansion, heightExpansion; | |
| 1308 if (ctm.getType() & SkMatrix::kAffine_Mask) | |
| 1309 widthExpansion = ctm[SkMatrix::kMSkewY], heightExpansion = ctm[SkMatrix:
:kMSkewX]; | |
| 1310 else | |
| 1311 widthExpansion = ctm[SkMatrix::kMScaleX], heightExpansion = ctm[SkMatrix
::kMScaleY]; | |
| 1312 return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fa
bs(heightExpansion) < 1; | |
| 1313 } | |
| 1314 | |
| 1315 void CanvasRenderingContext2D::drawImageInternal(SkCanvas* c, CanvasImageSource*
imageSource, Image* image, const FloatRect& srcRect, const FloatRect& dstRect,
const SkPaint* paint) | |
| 1316 { | |
| 1317 int initialSaveCount = c->getSaveCount(); | |
| 1318 SkPaint imagePaint = *paint; | |
| 1319 | |
| 1320 if (paint->getImageFilter()) { | |
| 1321 SkMatrix invCtm; | |
| 1322 if (!c->getTotalMatrix().invert(&invCtm)) { | |
| 1323 // There is an earlier check for invertibility, but the arithmetic | |
| 1324 // 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. | |
| 1326 // crbug.com/504687 | |
| 1327 return; | |
| 1328 } | |
| 1329 SkRect bounds = dstRect; | |
| 1330 SkPaint layerPaint; | |
| 1331 layerPaint.setXfermode(paint->getXfermode()); | |
| 1332 SkAutoTUnref<SkImageFilter> localFilter(paint->getImageFilter()->newWith
LocalMatrix(invCtm)); | |
| 1333 layerPaint.setImageFilter(localFilter); | |
| 1334 c->saveLayer(&bounds, &layerPaint); | |
| 1335 imagePaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); | |
| 1336 imagePaint.setImageFilter(nullptr); | |
| 1337 } | |
| 1338 | |
| 1339 if (!imageSource->isVideoElement()) { | |
| 1340 imagePaint.setAntiAlias(shouldDrawImageAntialiased(dstRect)); | |
| 1341 image->draw(c, imagePaint, dstRect, srcRect, DoNotRespectImageOrientatio
n, Image::DoNotClampImageToSourceRect); | |
| 1342 } else { | |
| 1343 c->save(); | |
| 1344 c->clipRect(dstRect); | |
| 1345 c->translate(dstRect.x(), dstRect.y()); | |
| 1346 c->scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.h
eight()); | |
| 1347 c->translate(-srcRect.x(), -srcRect.y()); | |
| 1348 HTMLVideoElement* video = static_cast<HTMLVideoElement*>(imageSource); | |
| 1349 video->paintCurrentFrame(c, IntRect(IntPoint(), IntSize(video->videoWidt
h(), video->videoHeight())), &imagePaint); | |
| 1350 } | |
| 1351 | |
| 1352 c->restoreToCount(initialSaveCount); | |
| 1353 } | |
| 1354 | |
| 1355 bool shouldDisableDeferral(CanvasImageSource* imageSource, DisableDeferralReason
* reason) | |
| 1356 { | |
| 1357 ASSERT(reason); | |
| 1358 ASSERT(*reason == DisableDeferralReasonUnknown); | |
| 1359 | |
| 1360 if (imageSource->isVideoElement()) { | |
| 1361 *reason = DisableDeferralReasonDrawImageOfVideo; | |
| 1362 return true; | |
| 1363 } | |
| 1364 if (imageSource->isCanvasElement()) { | |
| 1365 HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(imageSource)
; | |
| 1366 if (canvas->isAnimated2D()) { | |
| 1367 *reason = DisableDeferralReasonDrawImageOfAnimated2dCanvas; | |
| 1368 return true; | |
| 1369 } | |
| 1370 } | |
| 1371 return false; | |
| 1372 } | |
| 1373 | |
| 1374 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, | |
| 1375 double sx, double sy, double sw, double sh, | |
| 1376 double dx, double dy, double dw, double dh, ExceptionState& exceptionState) | |
| 1377 { | |
| 1378 if (!drawingCanvas()) | |
| 1379 return; | |
| 1380 | |
| 1381 RefPtr<Image> image; | |
| 1382 SourceImageStatus sourceImageStatus = InvalidSourceImageStatus; | |
| 1383 if (!imageSource->isVideoElement()) { | |
| 1384 AccelerationHint hint = canvas()->buffer()->isAccelerated() ? PreferAcce
leration : PreferNoAcceleration; | |
| 1385 image = imageSource->getSourceImageForCanvas(&sourceImageStatus, hint, S
napshotReasonDrawImage); | |
| 1386 if (sourceImageStatus == UndecodableSourceImageStatus) | |
| 1387 exceptionState.throwDOMException(InvalidStateError, "The HTMLImageEl
ement provided is in the 'broken' state."); | |
| 1388 if (!image || !image->width() || !image->height()) | |
| 1389 return; | |
| 1390 } else { | |
| 1391 if (!static_cast<HTMLVideoElement*>(imageSource)->hasAvailableVideoFrame
()) | |
| 1392 return; | |
| 1393 } | |
| 1394 | |
| 1395 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) | |
| 1397 || !dw || !dh || !sw || !sh) | |
| 1398 return; | |
| 1399 | |
| 1400 FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh)); | |
| 1401 FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh)); | |
| 1402 | |
| 1403 clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->elementSize()), &s
rcRect, &dstRect); | |
| 1404 | |
| 1405 imageSource->adjustDrawRects(&srcRect, &dstRect); | |
| 1406 | |
| 1407 if (srcRect.isEmpty()) | |
| 1408 return; | |
| 1409 | |
| 1410 DisableDeferralReason reason = DisableDeferralReasonUnknown; | |
| 1411 if (shouldDisableDeferral(imageSource, &reason) || image->isTextureBacked()) | |
| 1412 canvas()->disableDeferral(reason); | |
| 1413 | |
| 1414 validateStateStack(); | |
| 1415 | |
| 1416 draw( | |
| 1417 [this, &imageSource, &image, &srcRect, dstRect](SkCanvas* c, const SkPai
nt* paint) // draw lambda | |
| 1418 { | |
| 1419 drawImageInternal(c, imageSource, image.get(), srcRect, dstRect, pai
nt); | |
| 1420 }, | |
| 1421 [this, &dstRect](const SkIRect& clipBounds) // overdraw test lambda | |
| 1422 { | |
| 1423 return rectContainsTransformedRect(dstRect, clipBounds); | |
| 1424 }, dstRect, CanvasRenderingContext2DState::ImagePaintType, | |
| 1425 imageSource->isOpaque() ? CanvasRenderingContext2DState::OpaqueImage : C
anvasRenderingContext2DState::NonOpaqueImage); | |
| 1426 | |
| 1427 validateStateStack(); | |
| 1428 | |
| 1429 bool isExpensive = false; | |
| 1430 | |
| 1431 if (ExpensiveCanvasHeuristicParameters::SVGImageSourcesAreExpensive && image
Source->isSVGSource()) | |
| 1432 isExpensive = true; | |
| 1433 | |
| 1434 if (imageSource->elementSize().width() * imageSource->elementSize().height()
> canvas()->width() * canvas()->height() * ExpensiveCanvasHeuristicParameters::
ExpensiveImageSizeRatio) | |
| 1435 isExpensive = true; | |
| 1436 | |
| 1437 if (isExpensive) { | |
| 1438 ImageBuffer* buffer = canvas()->buffer(); | |
| 1439 if (buffer) | |
| 1440 buffer->setHasExpensiveOp(); | |
| 1441 } | |
| 1442 | |
| 1443 if (imageSource->isCanvasElement() && static_cast<HTMLCanvasElement*>(imageS
ource)->is3D()) { | |
| 1444 // 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. | |
| 1446 canvas()->buffer()->flushGpu(FlushReasonDrawImageOfWebGL); | |
| 1447 } | |
| 1448 | |
| 1449 if (canvas()->originClean() && wouldTaintOrigin(imageSource)) | |
| 1450 canvas()->setOriginTainted(); | |
| 1451 } | |
| 1452 | |
| 1453 void CanvasRenderingContext2D::clearCanvas() | |
| 1454 { | |
| 1455 FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height()); | |
| 1456 checkOverdraw(canvasRect, 0, CanvasRenderingContext2DState::NoImage, ClipFil
l); | |
| 1457 SkCanvas* c = drawingCanvas(); | |
| 1458 if (c) | |
| 1459 c->clear(m_hasAlpha ? SK_ColorTRANSPARENT : SK_ColorBLACK); | |
| 1460 } | |
| 1461 | |
| 1462 bool CanvasRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect
, const SkIRect& transformedRect) const | |
| 1463 { | |
| 1464 FloatQuad quad(rect); | |
| 1465 FloatQuad transformedQuad(FloatRect(transformedRect.x(), transformedRect.y()
, transformedRect.width(), transformedRect.height())); | |
| 1466 return state().transform().mapQuad(quad).containsQuad(transformedQuad); | |
| 1467 } | |
| 1468 | |
| 1469 CanvasGradient* CanvasRenderingContext2D::createLinearGradient(double x0, double
y0, double x1, double y1) | |
| 1470 { | |
| 1471 CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatP
oint(x1, y1)); | |
| 1472 return gradient; | |
| 1473 } | |
| 1474 | |
| 1475 CanvasGradient* CanvasRenderingContext2D::createRadialGradient(double x0, double
y0, double r0, double x1, double y1, double r1, ExceptionState& exceptionState) | |
| 1476 { | |
| 1477 if (r0 < 0 || r1 < 0) { | |
| 1478 exceptionState.throwDOMException(IndexSizeError, String::format("The %s
provided is less than 0.", r0 < 0 ? "r0" : "r1")); | |
| 1479 return nullptr; | |
| 1480 } | |
| 1481 | |
| 1482 CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, Fl
oatPoint(x1, y1), r1); | |
| 1483 return gradient; | |
| 1484 } | |
| 1485 | |
| 1486 CanvasPattern* CanvasRenderingContext2D::createPattern(const CanvasImageSourceUn
ion& imageSource, const String& repetitionType, ExceptionState& exceptionState) | |
| 1487 { | |
| 1488 Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetiti
onType, exceptionState); | |
| 1489 if (exceptionState.hadException()) | |
| 1490 return nullptr; | |
| 1491 | |
| 1492 SourceImageStatus status; | |
| 1493 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); | |
| 1494 RefPtr<Image> imageForRendering = imageSourceInternal->getSourceImageForCanv
as(&status, PreferNoAcceleration, SnapshotReasonCreatePattern); | |
| 1495 | |
| 1496 switch (status) { | |
| 1497 case NormalSourceImageStatus: | |
| 1498 break; | |
| 1499 case ZeroSizeCanvasSourceImageStatus: | |
| 1500 exceptionState.throwDOMException(InvalidStateError, String::format("The
canvas %s is 0.", imageSourceInternal->elementSize().width() ? "height" : "width
")); | |
| 1501 return nullptr; | |
| 1502 case UndecodableSourceImageStatus: | |
| 1503 exceptionState.throwDOMException(InvalidStateError, "Source image is in
the 'broken' state."); | |
| 1504 return nullptr; | |
| 1505 case InvalidSourceImageStatus: | |
| 1506 imageForRendering = Image::nullImage(); | |
| 1507 break; | |
| 1508 case IncompleteSourceImageStatus: | |
| 1509 return nullptr; | |
| 1510 default: | |
| 1511 ASSERT_NOT_REACHED(); | |
| 1512 return nullptr; | |
| 1513 } | |
| 1514 ASSERT(imageForRendering); | |
| 1515 | |
| 1516 bool originClean = !wouldTaintOrigin(imageSourceInternal); | |
| 1517 | |
| 1518 return CanvasPattern::create(imageForRendering.release(), repeatMode, origin
Clean); | |
| 1519 } | |
| 1520 | |
| 1521 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, SkIR
ect* dirtyRect) | |
| 1522 { | |
| 1523 SkIRect clipBounds; | |
| 1524 if (!drawingCanvas()->getClipDeviceBounds(&clipBounds)) | |
| 1525 return false; | |
| 1526 return computeDirtyRect(localRect, clipBounds, dirtyRect); | |
| 1527 } | |
| 1528 | |
| 1529 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, cons
t SkIRect& transformedClipBounds, SkIRect* dirtyRect) | |
| 1530 { | |
| 1531 FloatRect canvasRect = state().transform().mapRect(localRect); | |
| 1532 | |
| 1533 if (alphaChannel(state().shadowColor())) { | |
| 1534 FloatRect shadowRect(canvasRect); | |
| 1535 shadowRect.move(state().shadowOffset()); | |
| 1536 shadowRect.inflate(state().shadowBlur()); | |
| 1537 canvasRect.unite(shadowRect); | |
| 1538 } | |
| 1539 | |
| 1540 SkIRect canvasIRect; | |
| 1541 static_cast<SkRect>(canvasRect).roundOut(&canvasIRect); | |
| 1542 if (!canvasIRect.intersect(transformedClipBounds)) | |
| 1543 return false; | |
| 1544 | |
| 1545 if (dirtyRect) | |
| 1546 *dirtyRect = canvasIRect; | |
| 1547 | |
| 1548 return true; | |
| 1549 } | |
| 1550 | |
| 1551 void CanvasRenderingContext2D::didDraw(const SkIRect& dirtyRect) | 355 void CanvasRenderingContext2D::didDraw(const SkIRect& dirtyRect) |
| 1552 { | 356 { |
| 1553 if (dirtyRect.isEmpty()) | 357 if (dirtyRect.isEmpty()) |
| 1554 return; | 358 return; |
| 1555 | 359 |
| 1556 if (ExpensiveCanvasHeuristicParameters::BlurredShadowsAreExpensive && state(
).shouldDrawShadows() && state().shadowBlur() > 0) { | 360 if (ExpensiveCanvasHeuristicParameters::BlurredShadowsAreExpensive && state(
).shouldDrawShadows() && state().shadowBlur() > 0) { |
| 1557 ImageBuffer* buffer = canvas()->buffer(); | 361 ImageBuffer* buffer = canvas()->buffer(); |
| 1558 if (buffer) | 362 if (buffer) |
| 1559 buffer->setHasExpensiveOp(); | 363 buffer->setHasExpensiveOp(); |
| 1560 } | 364 } |
| 1561 | 365 |
| 1562 canvas()->didDraw(SkRect::Make(dirtyRect)); | 366 canvas()->didDraw(SkRect::Make(dirtyRect)); |
| 1563 } | 367 } |
| 1564 | 368 |
| 369 bool CanvasRenderingContext2D::stateHasFilter() |
| 370 { |
| 371 return state().hasFilter(canvas(), accessFont(), canvas()->size(), this); |
| 372 } |
| 373 |
| 374 SkImageFilter* CanvasRenderingContext2D::stateGetFilter() |
| 375 { |
| 376 return state().getFilter(canvas(), accessFont(), canvas()->size(), this); |
| 377 } |
| 378 |
| 1565 SkCanvas* CanvasRenderingContext2D::drawingCanvas() const | 379 SkCanvas* CanvasRenderingContext2D::drawingCanvas() const |
| 1566 { | 380 { |
| 1567 if (isContextLost()) | 381 if (isContextLost()) |
| 1568 return nullptr; | 382 return nullptr; |
| 1569 return canvas()->drawingCanvas(); | 383 return canvas()->drawingCanvas(); |
| 1570 } | 384 } |
| 1571 | 385 |
| 1572 ImageData* CanvasRenderingContext2D::createImageData(ImageData* imageData) const | 386 SkCanvas* CanvasRenderingContext2D::existingDrawingCanvas() const |
| 1573 { | 387 { |
| 1574 return ImageData::create(imageData->size()); | 388 return canvas()->existingDrawingCanvas(); |
| 1575 } | 389 } |
| 1576 | 390 |
| 1577 ImageData* CanvasRenderingContext2D::createImageData(double sw, double sh, Excep
tionState& exceptionState) const | 391 void CanvasRenderingContext2D::disableDeferral(DisableDeferralReason reason) |
| 1578 { | 392 { |
| 1579 if (!sw || !sh) { | 393 canvas()->disableDeferral(reason); |
| 1580 exceptionState.throwDOMException(IndexSizeError, String::format("The sou
rce %s is 0.", sw ? "height" : "width")); | |
| 1581 return nullptr; | |
| 1582 } | |
| 1583 | |
| 1584 FloatSize logicalSize(fabs(sw), fabs(sh)); | |
| 1585 if (!logicalSize.isExpressibleAsIntSize()) | |
| 1586 return nullptr; | |
| 1587 | |
| 1588 IntSize size = expandedIntSize(logicalSize); | |
| 1589 if (size.width() < 1) | |
| 1590 size.setWidth(1); | |
| 1591 if (size.height() < 1) | |
| 1592 size.setHeight(1); | |
| 1593 | |
| 1594 return ImageData::create(size); | |
| 1595 } | 394 } |
| 1596 | 395 |
| 1597 ImageData* CanvasRenderingContext2D::getImageData(double sx, double sy, double s
w, double sh, ExceptionState& exceptionState) const | 396 AffineTransform CanvasRenderingContext2D::baseTransform() const |
| 1598 { | 397 { |
| 1599 if (!canvas()->originClean()) | 398 return canvas()->baseTransform(); |
| 1600 exceptionState.throwSecurityError("The canvas has been tainted by cross-
origin data."); | |
| 1601 else if (!sw || !sh) | |
| 1602 exceptionState.throwDOMException(IndexSizeError, String::format("The sou
rce %s is 0.", sw ? "height" : "width")); | |
| 1603 | |
| 1604 if (exceptionState.hadException()) | |
| 1605 return nullptr; | |
| 1606 | |
| 1607 if (sw < 0) { | |
| 1608 sx += sw; | |
| 1609 sw = -sw; | |
| 1610 } | |
| 1611 if (sh < 0) { | |
| 1612 sy += sh; | |
| 1613 sh = -sh; | |
| 1614 } | |
| 1615 | |
| 1616 FloatRect logicalRect(sx, sy, sw, sh); | |
| 1617 if (logicalRect.width() < 1) | |
| 1618 logicalRect.setWidth(1); | |
| 1619 if (logicalRect.height() < 1) | |
| 1620 logicalRect.setHeight(1); | |
| 1621 if (!logicalRect.isExpressibleAsIntRect()) | |
| 1622 return nullptr; | |
| 1623 | |
| 1624 IntRect imageDataRect = enclosingIntRect(logicalRect); | |
| 1625 ImageBuffer* buffer = canvas()->buffer(); | |
| 1626 if (!buffer || isContextLost()) | |
| 1627 return ImageData::create(imageDataRect.size()); | |
| 1628 | |
| 1629 WTF::ArrayBufferContents contents; | |
| 1630 if (!buffer->getImageData(Unmultiplied, imageDataRect, contents)) | |
| 1631 return nullptr; | |
| 1632 | |
| 1633 RefPtr<DOMArrayBuffer> arrayBuffer = DOMArrayBuffer::create(contents); | |
| 1634 return ImageData::create( | |
| 1635 imageDataRect.size(), | |
| 1636 DOMUint8ClampedArray::create(arrayBuffer, 0, arrayBuffer->byteLength()))
; | |
| 1637 } | |
| 1638 | |
| 1639 void CanvasRenderingContext2D::putImageData(ImageData* data, double dx, double d
y, ExceptionState& exceptionState) | |
| 1640 { | |
| 1641 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), exceptionSta
te); | |
| 1642 } | |
| 1643 | |
| 1644 void CanvasRenderingContext2D::putImageData(ImageData* data, double dx, double d
y, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight, Exceptio
nState& exceptionState) | |
| 1645 { | |
| 1646 if (data->data()->bufferBase()->isNeutered()) { | |
| 1647 exceptionState.throwDOMException(InvalidStateError, "The source data has
been neutered."); | |
| 1648 return; | |
| 1649 } | |
| 1650 ImageBuffer* buffer = canvas()->buffer(); | |
| 1651 if (!buffer) | |
| 1652 return; | |
| 1653 | |
| 1654 if (dirtyWidth < 0) { | |
| 1655 dirtyX += dirtyWidth; | |
| 1656 dirtyWidth = -dirtyWidth; | |
| 1657 } | |
| 1658 | |
| 1659 if (dirtyHeight < 0) { | |
| 1660 dirtyY += dirtyHeight; | |
| 1661 dirtyHeight = -dirtyHeight; | |
| 1662 } | |
| 1663 | |
| 1664 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); | |
| 1665 clipRect.intersect(IntRect(0, 0, data->width(), data->height())); | |
| 1666 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy)); | |
| 1667 IntRect destRect = enclosingIntRect(clipRect); | |
| 1668 destRect.move(destOffset); | |
| 1669 destRect.intersect(IntRect(IntPoint(), buffer->size())); | |
| 1670 if (destRect.isEmpty()) | |
| 1671 return; | |
| 1672 IntRect sourceRect(destRect); | |
| 1673 sourceRect.move(-destOffset); | |
| 1674 | |
| 1675 checkOverdraw(destRect, 0, CanvasRenderingContext2DState::NoImage, Untransfo
rmedUnclippedFill); | |
| 1676 | |
| 1677 buffer->putByteArray(Unmultiplied, data->data()->data(), IntSize(data->width
(), data->height()), sourceRect, IntPoint(destOffset)); | |
| 1678 | |
| 1679 didDraw(destRect); | |
| 1680 } | 399 } |
| 1681 | 400 |
| 1682 String CanvasRenderingContext2D::font() const | 401 String CanvasRenderingContext2D::font() const |
| 1683 { | 402 { |
| 1684 if (!state().hasRealizedFont()) | 403 if (!state().hasRealizedFont()) |
| 1685 return defaultFont; | 404 return defaultFont; |
| 1686 | 405 |
| 1687 canvas()->document().canvasFontCache()->willUseCurrentFont(); | 406 canvas()->document().canvasFontCache()->willUseCurrentFont(); |
| 1688 StringBuilder serializedFont; | 407 StringBuilder serializedFont; |
| 1689 const FontDescription& fontDescription = state().font().fontDescription(); | 408 const FontDescription& fontDescription = state().font().fontDescription(); |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1813 if (oldStyle && oldStyle->font() == newStyle.font()) | 532 if (oldStyle && oldStyle->font() == newStyle.font()) |
| 1814 return; | 533 return; |
| 1815 pruneLocalFontCache(0); | 534 pruneLocalFontCache(0); |
| 1816 } | 535 } |
| 1817 | 536 |
| 1818 void CanvasRenderingContext2D::filterNeedsInvalidation() | 537 void CanvasRenderingContext2D::filterNeedsInvalidation() |
| 1819 { | 538 { |
| 1820 state().clearResolvedFilter(); | 539 state().clearResolvedFilter(); |
| 1821 } | 540 } |
| 1822 | 541 |
| 542 bool CanvasRenderingContext2D::originClean() const |
| 543 { |
| 544 return canvas()->originClean(); |
| 545 } |
| 546 |
| 547 void CanvasRenderingContext2D::setOriginTainted() |
| 548 { |
| 549 return canvas()->setOriginTainted(); |
| 550 } |
| 551 |
| 552 int CanvasRenderingContext2D::width() const |
| 553 { |
| 554 return canvas()->width(); |
| 555 } |
| 556 |
| 557 int CanvasRenderingContext2D::height() const |
| 558 { |
| 559 return canvas()->height(); |
| 560 } |
| 561 |
| 562 bool CanvasRenderingContext2D::hasImageBuffer() const |
| 563 { |
| 564 return canvas()->hasImageBuffer(); |
| 565 } |
| 566 |
| 567 ImageBuffer* CanvasRenderingContext2D::imageBuffer() const |
| 568 { |
| 569 return canvas()->buffer(); |
| 570 } |
| 571 |
| 572 bool CanvasRenderingContext2D::parseColorOrCurrentColor(Color& color, const Stri
ng& colorString) const |
| 573 { |
| 574 return ::blink::parseColorOrCurrentColor(color, colorString, canvas()); |
| 575 } |
| 576 |
| 1823 String CanvasRenderingContext2D::textAlign() const | 577 String CanvasRenderingContext2D::textAlign() const |
| 1824 { | 578 { |
| 1825 return textAlignName(state().textAlign()); | 579 return textAlignName(state().textAlign()); |
| 1826 } | 580 } |
| 1827 | 581 |
| 1828 void CanvasRenderingContext2D::setTextAlign(const String& s) | 582 void CanvasRenderingContext2D::setTextAlign(const String& s) |
| 1829 { | 583 { |
| 1830 TextAlign align; | 584 TextAlign align; |
| 1831 if (!parseTextAlign(s, align)) | 585 if (!parseTextAlign(s, align)) |
| 1832 return; | 586 return; |
| (...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2036 | 790 |
| 2037 CanvasRenderingContext2DAutoRestoreSkCanvas stateRestorer(this); | 791 CanvasRenderingContext2DAutoRestoreSkCanvas stateRestorer(this); |
| 2038 if (useMaxWidth) { | 792 if (useMaxWidth) { |
| 2039 drawingCanvas()->save(); | 793 drawingCanvas()->save(); |
| 2040 drawingCanvas()->translate(location.x(), location.y()); | 794 drawingCanvas()->translate(location.x(), location.y()); |
| 2041 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" o
p) still work. | 795 // 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); | 796 drawingCanvas()->scale((fontWidth > 0 ? (width / fontWidth) : 0), 1); |
| 2043 location = FloatPoint(); | 797 location = FloatPoint(); |
| 2044 } | 798 } |
| 2045 | 799 |
| 2046 draw( | 800 drawForText(font, textRunPaintInfo, location, paintType); |
| 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 { | |
| 2060 // Fast approximation of the stroke's bounding rect. | |
| 2061 // This yields a slightly oversized rect but is very fast | |
| 2062 // compared to Path::strokeBoundingRect(). | |
| 2063 static const double root2 = sqrtf(2); | |
| 2064 double delta = state().lineWidth() / 2; | |
| 2065 if (state().lineJoin() == MiterJoin) | |
| 2066 delta *= state().miterLimit(); | |
| 2067 else if (state().lineCap() == SquareCap) | |
| 2068 delta *= root2; | |
| 2069 | |
| 2070 rect.inflate(delta); | |
| 2071 } | 801 } |
| 2072 | 802 |
| 2073 const Font& CanvasRenderingContext2D::accessFont() | 803 const Font& CanvasRenderingContext2D::accessFont() |
| 2074 { | 804 { |
| 2075 if (!state().hasRealizedFont()) | 805 if (!state().hasRealizedFont()) |
| 2076 setFont(state().unparsedFont()); | 806 setFont(state().unparsedFont()); |
| 2077 canvas()->document().canvasFontCache()->willUseCurrentFont(); | 807 canvas()->document().canvasFontCache()->willUseCurrentFont(); |
| 2078 return state().font(); | 808 return state().font(); |
| 2079 } | 809 } |
| 2080 | 810 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2112 bool CanvasRenderingContext2D::isTransformInvertible() const | 842 bool CanvasRenderingContext2D::isTransformInvertible() const |
| 2113 { | 843 { |
| 2114 return state().isTransformInvertible(); | 844 return state().isTransformInvertible(); |
| 2115 } | 845 } |
| 2116 | 846 |
| 2117 WebLayer* CanvasRenderingContext2D::platformLayer() const | 847 WebLayer* CanvasRenderingContext2D::platformLayer() const |
| 2118 { | 848 { |
| 2119 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0; | 849 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0; |
| 2120 } | 850 } |
| 2121 | 851 |
| 2122 bool CanvasRenderingContext2D::imageSmoothingEnabled() const | |
| 2123 { | |
| 2124 return state().imageSmoothingEnabled(); | |
| 2125 } | |
| 2126 | |
| 2127 void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled) | |
| 2128 { | |
| 2129 if (enabled == state().imageSmoothingEnabled()) | |
| 2130 return; | |
| 2131 | |
| 2132 modifiableState().setImageSmoothingEnabled(enabled); | |
| 2133 } | |
| 2134 | |
| 2135 String CanvasRenderingContext2D::imageSmoothingQuality() const | |
| 2136 { | |
| 2137 return state().imageSmoothingQuality(); | |
| 2138 } | |
| 2139 | |
| 2140 void CanvasRenderingContext2D::setImageSmoothingQuality(const String& quality) | |
| 2141 { | |
| 2142 if (quality == state().imageSmoothingQuality()) | |
| 2143 return; | |
| 2144 | |
| 2145 modifiableState().setImageSmoothingQuality(quality); | |
| 2146 } | |
| 2147 | |
| 2148 void CanvasRenderingContext2D::getContextAttributes(Canvas2DContextAttributes& a
ttrs) const | 852 void CanvasRenderingContext2D::getContextAttributes(Canvas2DContextAttributes& a
ttrs) const |
| 2149 { | 853 { |
| 2150 attrs.setAlpha(m_hasAlpha); | 854 attrs.setAlpha(m_hasAlpha); |
| 2151 } | 855 } |
| 2152 | 856 |
| 2153 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element) | 857 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element) |
| 2154 { | 858 { |
| 2155 drawFocusIfNeededInternal(m_path, element); | 859 drawFocusIfNeededInternal(m_path, element); |
| 2156 } | 860 } |
| 2157 | 861 |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2297 } | 1001 } |
| 2298 | 1002 |
| 2299 unsigned CanvasRenderingContext2D::hitRegionsCount() const | 1003 unsigned CanvasRenderingContext2D::hitRegionsCount() const |
| 2300 { | 1004 { |
| 2301 if (m_hitRegionManager) | 1005 if (m_hitRegionManager) |
| 2302 return m_hitRegionManager->getHitRegionsCount(); | 1006 return m_hitRegionManager->getHitRegionsCount(); |
| 2303 | 1007 |
| 2304 return 0; | 1008 return 0; |
| 2305 } | 1009 } |
| 2306 | 1010 |
| 2307 void CanvasRenderingContext2D::checkOverdraw(const SkRect& rect, const SkPaint*
paint, CanvasRenderingContext2DState::ImageType imageType, DrawType drawType) | |
| 2308 { | |
| 2309 SkCanvas* c = drawingCanvas(); | |
| 2310 if (!c || !canvas()->buffer()->isRecording()) | |
| 2311 return; | |
| 2312 | |
| 2313 SkRect deviceRect; | |
| 2314 if (drawType == UntransformedUnclippedFill) { | |
| 2315 deviceRect = rect; | |
| 2316 } else { | |
| 2317 ASSERT(drawType == ClipFill); | |
| 2318 if (state().hasComplexClip()) | |
| 2319 return; | |
| 2320 | |
| 2321 SkIRect skIBounds; | |
| 2322 if (!c->getClipDeviceBounds(&skIBounds)) | |
| 2323 return; | |
| 2324 deviceRect = SkRect::Make(skIBounds); | |
| 2325 } | |
| 2326 | |
| 2327 const SkImageInfo& imageInfo = c->imageInfo(); | |
| 2328 if (!deviceRect.contains(SkRect::MakeWH(imageInfo.width(), imageInfo.height(
)))) | |
| 2329 return; | |
| 2330 | |
| 2331 bool isSourceOver = true; | |
| 2332 unsigned alpha = 0xFF; | |
| 2333 if (paint) { | |
| 2334 if (paint->getLooper() || paint->getImageFilter() || paint->getMaskFilte
r()) | |
| 2335 return; | |
| 2336 | |
| 2337 SkXfermode* xfermode = paint->getXfermode(); | |
| 2338 if (xfermode) { | |
| 2339 SkXfermode::Mode mode; | |
| 2340 if (xfermode->asMode(&mode)) { | |
| 2341 isSourceOver = mode == SkXfermode::kSrcOver_Mode; | |
| 2342 if (!isSourceOver && mode != SkXfermode::kSrc_Mode && mode != Sk
Xfermode::kClear_Mode) | |
| 2343 return; // The code below only knows how to handle Src, SrcO
ver, and Clear | |
| 2344 } else { | |
| 2345 // unknown xfermode | |
| 2346 ASSERT_NOT_REACHED(); | |
| 2347 return; | |
| 2348 } | |
| 2349 } | |
| 2350 | |
| 2351 alpha = paint->getAlpha(); | |
| 2352 | |
| 2353 if (isSourceOver && imageType == CanvasRenderingContext2DState::NoImage)
{ | |
| 2354 SkShader* shader = paint->getShader(); | |
| 2355 if (shader) { | |
| 2356 if (shader->isOpaque() && alpha == 0xFF) | |
| 2357 canvas()->buffer()->willOverwriteCanvas(); | |
| 2358 return; | |
| 2359 } | |
| 2360 } | |
| 2361 } | |
| 2362 | |
| 2363 if (isSourceOver) { | |
| 2364 // With source over, we need to certify that alpha == 0xFF for all pixel
s | |
| 2365 if (imageType == CanvasRenderingContext2DState::NonOpaqueImage) | |
| 2366 return; | |
| 2367 if (alpha < 0xFF) | |
| 2368 return; | |
| 2369 } | |
| 2370 | |
| 2371 canvas()->buffer()->willOverwriteCanvas(); | |
| 2372 } | |
| 2373 | |
| 2374 } // namespace blink | 1011 } // namespace blink |
| OLD | NEW |