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