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" |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 c->restoreToCount(m_saveCount); | 103 c->restoreToCount(m_saveCount); |
112 m_context->validateStateStack(); | 104 m_context->validateStateStack(); |
113 } | 105 } |
114 private: | 106 private: |
115 RawPtrWillBeMember<CanvasRenderingContext2D> m_context; | 107 RawPtrWillBeMember<CanvasRenderingContext2D> m_context; |
116 int m_saveCount; | 108 int m_saveCount; |
117 }; | 109 }; |
118 | 110 |
119 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, co
nst CanvasContextCreationAttributes& attrs, Document& document) | 111 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, co
nst CanvasContextCreationAttributes& attrs, Document& document) |
120 : CanvasRenderingContext(canvas) | 112 : CanvasRenderingContext(canvas) |
121 , m_clipAntialiasing(NotAntiAliased) | |
122 , m_hasAlpha(attrs.alpha()) | 113 , m_hasAlpha(attrs.alpha()) |
123 , m_contextLostMode(NotLostContext) | 114 , m_contextLostMode(NotLostContext) |
124 , m_contextRestorable(true) | 115 , m_contextRestorable(true) |
125 , m_tryRestoreContextAttemptCount(0) | 116 , m_tryRestoreContextAttemptCount(0) |
126 , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchC
ontextLostEvent) | 117 , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchC
ontextLostEvent) |
127 , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispa
tchContextRestoredEvent) | 118 , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispa
tchContextRestoredEvent) |
128 , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreC
ontextEvent) | 119 , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreC
ontextEvent) |
129 , m_pruneLocalFontCacheScheduled(false) | 120 , m_pruneLocalFontCacheScheduled(false) |
130 { | 121 { |
131 if (document.settings() && document.settings()->antialiasedClips2dCanvasEnab
led()) | 122 if (document.settings() && document.settings()->antialiasedClips2dCanvasEnab
led()) |
132 m_clipAntialiasing = AntiAliased; | 123 m_clipAntialiasing = AntiAliased; |
133 m_stateStack.append(CanvasRenderingContext2DState::create()); | |
134 setShouldAntialias(true); | 124 setShouldAntialias(true); |
135 #if ENABLE(OILPAN) | 125 #if ENABLE(OILPAN) |
136 ThreadState::current()->registerPreFinalizer(this); | 126 ThreadState::current()->registerPreFinalizer(this); |
137 #endif | 127 #endif |
138 } | 128 } |
139 | 129 |
140 void CanvasRenderingContext2D::unwindStateStack() | 130 void CanvasRenderingContext2D::unwindStateStack() |
141 { | 131 { |
142 if (size_t stackSize = m_stateStack.size()) { | 132 if (size_t stackSize = m_stateStack.size()) { |
143 if (SkCanvas* skCanvas = canvas()->existingDrawingCanvas()) { | 133 if (SkCanvas* skCanvas = canvas()->existingDrawingCanvas()) { |
(...skipping 21 matching lines...) Expand all Loading... |
165 void CanvasRenderingContext2D::validateStateStack() | 155 void CanvasRenderingContext2D::validateStateStack() |
166 { | 156 { |
167 #if ENABLE(ASSERT) | 157 #if ENABLE(ASSERT) |
168 SkCanvas* skCanvas = canvas()->existingDrawingCanvas(); | 158 SkCanvas* skCanvas = canvas()->existingDrawingCanvas(); |
169 if (skCanvas && m_contextLostMode == NotLostContext) { | 159 if (skCanvas && m_contextLostMode == NotLostContext) { |
170 ASSERT(static_cast<size_t>(skCanvas->getSaveCount()) == m_stateStack.siz
e()); | 160 ASSERT(static_cast<size_t>(skCanvas->getSaveCount()) == m_stateStack.siz
e()); |
171 } | 161 } |
172 #endif | 162 #endif |
173 } | 163 } |
174 | 164 |
175 CanvasRenderingContext2DState& CanvasRenderingContext2D::modifiableState() | |
176 { | |
177 realizeSaves(); | |
178 return *m_stateStack.last(); | |
179 } | |
180 | |
181 bool CanvasRenderingContext2D::isAccelerated() const | 165 bool CanvasRenderingContext2D::isAccelerated() const |
182 { | 166 { |
183 if (!canvas()->hasImageBuffer()) | 167 if (!canvas()->hasImageBuffer()) |
184 return false; | 168 return false; |
185 return canvas()->buffer()->isAccelerated(); | 169 return canvas()->buffer()->isAccelerated(); |
186 } | 170 } |
187 | 171 |
188 void CanvasRenderingContext2D::stop() | 172 void CanvasRenderingContext2D::stop() |
189 { | 173 { |
190 if (!isContextLost()) { | 174 if (!isContextLost()) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 } else { | 207 } else { |
224 // legacy synchronous context restoration. | 208 // legacy synchronous context restoration. |
225 reset(); | 209 reset(); |
226 m_contextLostMode = NotLostContext; | 210 m_contextLostMode = NotLostContext; |
227 } | 211 } |
228 } | 212 } |
229 } | 213 } |
230 | 214 |
231 DEFINE_TRACE(CanvasRenderingContext2D) | 215 DEFINE_TRACE(CanvasRenderingContext2D) |
232 { | 216 { |
233 visitor->trace(m_stateStack); | |
234 visitor->trace(m_hitRegionManager); | 217 visitor->trace(m_hitRegionManager); |
235 CanvasRenderingContext::trace(visitor); | 218 CanvasRenderingContext::trace(visitor); |
| 219 BaseRenderingContext2D::trace(visitor); |
236 } | 220 } |
237 | 221 |
238 void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingCon
text2D>*) | 222 void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingCon
text2D>*) |
239 { | 223 { |
240 if (contextLostRestoredEventsEnabled()) { | 224 if (contextLostRestoredEventsEnabled()) { |
241 RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames
::contextlost); | 225 RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames
::contextlost); |
242 canvas()->dispatchEvent(event); | 226 canvas()->dispatchEvent(event); |
243 if (event->defaultPrevented()) { | 227 if (event->defaultPrevented()) { |
244 m_contextRestorable = false; | 228 m_contextRestorable = false; |
245 } | 229 } |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 ASSERT(m_stateStack.begin() < m_stateStack.end()); | 295 ASSERT(m_stateStack.begin() < m_stateStack.end()); |
312 for (currState = m_stateStack.begin(); currState < m_stateStack.end(); currS
tate++) { | 296 for (currState = m_stateStack.begin(); currState < m_stateStack.end(); currS
tate++) { |
313 c->setMatrix(SkMatrix::I()); | 297 c->setMatrix(SkMatrix::I()); |
314 currState->get()->playbackClips(c); | 298 currState->get()->playbackClips(c); |
315 c->setMatrix(affineTransformToSkMatrix(currState->get()->transform())); | 299 c->setMatrix(affineTransformToSkMatrix(currState->get()->transform())); |
316 c->save(); | 300 c->save(); |
317 } | 301 } |
318 c->restore(); | 302 c->restore(); |
319 } | 303 } |
320 | 304 |
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 | |
631 void CanvasRenderingContext2D::setShouldAntialias(bool doAA) | 305 void CanvasRenderingContext2D::setShouldAntialias(bool doAA) |
632 { | 306 { |
633 modifiableState().setShouldAntialias(doAA); | 307 modifiableState().setShouldAntialias(doAA); |
634 } | 308 } |
635 | 309 |
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 | |
1149 void CanvasRenderingContext2D::scrollPathIntoView() | 310 void CanvasRenderingContext2D::scrollPathIntoView() |
1150 { | 311 { |
1151 scrollPathIntoViewInternal(m_path); | 312 scrollPathIntoViewInternal(m_path); |
1152 } | 313 } |
1153 | 314 |
1154 void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d) | 315 void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d) |
1155 { | 316 { |
1156 scrollPathIntoViewInternal(path2d->path()); | 317 scrollPathIntoViewInternal(path2d->path()); |
1157 } | 318 } |
1158 | 319 |
(...skipping 21 matching lines...) Expand all Loading... |
1180 | 341 |
1181 renderer->scrollRectToVisible( | 342 renderer->scrollRectToVisible( |
1182 pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopA
lways); | 343 pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopA
lways); |
1183 | 344 |
1184 // TODO: should implement "inform the user" that the caret and/or | 345 // TODO: should implement "inform the user" that the caret and/or |
1185 // selection the specified rectangle of the canvas. See http://crbug.com/357
987 | 346 // selection the specified rectangle of the canvas. See http://crbug.com/357
987 |
1186 } | 347 } |
1187 | 348 |
1188 void CanvasRenderingContext2D::clearRect(double x, double y, double width, doubl
e height) | 349 void CanvasRenderingContext2D::clearRect(double x, double y, double width, doubl
e height) |
1189 { | 350 { |
1190 if (!validateRectForCanvas(x, y, width, height)) | 351 BaseRenderingContext2D::clearRect(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); | |
1206 FloatRect rect(x, y, width, height); | 352 FloatRect rect(x, y, width, height); |
1207 | 353 |
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 | |
1221 if (m_hitRegionManager) { | 354 if (m_hitRegionManager) { |
1222 m_hitRegionManager->removeHitRegionsInRect(rect, state().transform()); | 355 m_hitRegionManager->removeHitRegionsInRect(rect, state().transform()); |
1223 } | 356 } |
1224 } | 357 } |
1225 | 358 |
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 | |
1554 void CanvasRenderingContext2D::didDraw(const SkIRect& dirtyRect) | 359 void CanvasRenderingContext2D::didDraw(const SkIRect& dirtyRect) |
1555 { | 360 { |
1556 if (dirtyRect.isEmpty()) | 361 if (dirtyRect.isEmpty()) |
1557 return; | 362 return; |
1558 | 363 |
1559 if (ExpensiveCanvasHeuristicParameters::BlurredShadowsAreExpensive && state(
).shouldDrawShadows() && state().shadowBlur() > 0) { | 364 if (ExpensiveCanvasHeuristicParameters::BlurredShadowsAreExpensive && state(
).shouldDrawShadows() && state().shadowBlur() > 0) { |
1560 ImageBuffer* buffer = canvas()->buffer(); | 365 ImageBuffer* buffer = canvas()->buffer(); |
1561 if (buffer) | 366 if (buffer) |
1562 buffer->setHasExpensiveOp(); | 367 buffer->setHasExpensiveOp(); |
1563 } | 368 } |
1564 | 369 |
1565 canvas()->didDraw(SkRect::Make(dirtyRect)); | 370 canvas()->didDraw(SkRect::Make(dirtyRect)); |
1566 } | 371 } |
1567 | 372 |
| 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 |
1568 SkCanvas* CanvasRenderingContext2D::drawingCanvas() const | 383 SkCanvas* CanvasRenderingContext2D::drawingCanvas() const |
1569 { | 384 { |
1570 if (isContextLost()) | 385 if (isContextLost()) |
1571 return nullptr; | 386 return nullptr; |
1572 return canvas()->drawingCanvas(); | 387 return canvas()->drawingCanvas(); |
1573 } | 388 } |
1574 | 389 |
1575 ImageData* CanvasRenderingContext2D::createImageData(ImageData* imageData) const | 390 SkCanvas* CanvasRenderingContext2D::existingDrawingCanvas() const |
1576 { | 391 { |
1577 return ImageData::create(imageData->size()); | 392 return canvas()->existingDrawingCanvas(); |
1578 } | 393 } |
1579 | 394 |
1580 ImageData* CanvasRenderingContext2D::createImageData(double sw, double sh, Excep
tionState& exceptionState) const | 395 void CanvasRenderingContext2D::disableDeferral(DisableDeferralReason reason) |
1581 { | 396 { |
1582 if (!sw || !sh) { | 397 canvas()->disableDeferral(reason); |
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); | |
1598 } | 398 } |
1599 | 399 |
1600 ImageData* CanvasRenderingContext2D::getImageData(double sx, double sy, double s
w, double sh, ExceptionState& exceptionState) const | 400 AffineTransform CanvasRenderingContext2D::baseTransform() const |
1601 { | 401 { |
1602 if (!canvas()->originClean()) | 402 return canvas()->baseTransform(); |
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); | |
1683 } | 403 } |
1684 | 404 |
1685 String CanvasRenderingContext2D::font() const | 405 String CanvasRenderingContext2D::font() const |
1686 { | 406 { |
1687 if (!state().hasRealizedFont()) | 407 if (!state().hasRealizedFont()) |
1688 return defaultFont; | 408 return defaultFont; |
1689 | 409 |
1690 canvas()->document().canvasFontCache()->willUseCurrentFont(); | 410 canvas()->document().canvasFontCache()->willUseCurrentFont(); |
1691 StringBuilder serializedFont; | 411 StringBuilder serializedFont; |
1692 const FontDescription& fontDescription = state().font().fontDescription(); | 412 const FontDescription& fontDescription = state().font().fontDescription(); |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1816 if (oldStyle && oldStyle->font() == newStyle.font()) | 536 if (oldStyle && oldStyle->font() == newStyle.font()) |
1817 return; | 537 return; |
1818 pruneLocalFontCache(0); | 538 pruneLocalFontCache(0); |
1819 } | 539 } |
1820 | 540 |
1821 void CanvasRenderingContext2D::filterNeedsInvalidation() | 541 void CanvasRenderingContext2D::filterNeedsInvalidation() |
1822 { | 542 { |
1823 state().clearResolvedFilter(); | 543 state().clearResolvedFilter(); |
1824 } | 544 } |
1825 | 545 |
| 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 |
1826 String CanvasRenderingContext2D::textAlign() const | 581 String CanvasRenderingContext2D::textAlign() const |
1827 { | 582 { |
1828 return textAlignName(state().textAlign()); | 583 return textAlignName(state().textAlign()); |
1829 } | 584 } |
1830 | 585 |
1831 void CanvasRenderingContext2D::setTextAlign(const String& s) | 586 void CanvasRenderingContext2D::setTextAlign(const String& s) |
1832 { | 587 { |
1833 TextAlign align; | 588 TextAlign align; |
1834 if (!parseTextAlign(s, align)) | 589 if (!parseTextAlign(s, align)) |
1835 return; | 590 return; |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2051 { | 806 { |
2052 font.drawBidiText(c, textRunPaintInfo, location, Font::UseFallbackIf
FontNotReady, cDeviceScaleFactor, *paint); | 807 font.drawBidiText(c, textRunPaintInfo, location, Font::UseFallbackIf
FontNotReady, cDeviceScaleFactor, *paint); |
2053 }, | 808 }, |
2054 [](const SkIRect& rect) // overdraw test lambda | 809 [](const SkIRect& rect) // overdraw test lambda |
2055 { | 810 { |
2056 return false; | 811 return false; |
2057 }, | 812 }, |
2058 textRunPaintInfo.bounds, paintType); | 813 textRunPaintInfo.bounds, paintType); |
2059 } | 814 } |
2060 | 815 |
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 | |
2076 const Font& CanvasRenderingContext2D::accessFont() | 816 const Font& CanvasRenderingContext2D::accessFont() |
2077 { | 817 { |
2078 if (!state().hasRealizedFont()) | 818 if (!state().hasRealizedFont()) |
2079 setFont(state().unparsedFont()); | 819 setFont(state().unparsedFont()); |
2080 canvas()->document().canvasFontCache()->willUseCurrentFont(); | 820 canvas()->document().canvasFontCache()->willUseCurrentFont(); |
2081 return state().font(); | 821 return state().font(); |
2082 } | 822 } |
2083 | 823 |
2084 int CanvasRenderingContext2D::getFontBaseline(const FontMetrics& fontMetrics) co
nst | 824 int CanvasRenderingContext2D::getFontBaseline(const FontMetrics& fontMetrics) co
nst |
2085 { | 825 { |
(...skipping 29 matching lines...) Expand all Loading... |
2115 bool CanvasRenderingContext2D::isTransformInvertible() const | 855 bool CanvasRenderingContext2D::isTransformInvertible() const |
2116 { | 856 { |
2117 return state().isTransformInvertible(); | 857 return state().isTransformInvertible(); |
2118 } | 858 } |
2119 | 859 |
2120 WebLayer* CanvasRenderingContext2D::platformLayer() const | 860 WebLayer* CanvasRenderingContext2D::platformLayer() const |
2121 { | 861 { |
2122 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0; | 862 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0; |
2123 } | 863 } |
2124 | 864 |
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 | |
2151 void CanvasRenderingContext2D::getContextAttributes(Canvas2DContextAttributes& a
ttrs) const | 865 void CanvasRenderingContext2D::getContextAttributes(Canvas2DContextAttributes& a
ttrs) const |
2152 { | 866 { |
2153 attrs.setAlpha(m_hasAlpha); | 867 attrs.setAlpha(m_hasAlpha); |
2154 } | 868 } |
2155 | 869 |
2156 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element) | 870 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element) |
2157 { | 871 { |
2158 drawFocusIfNeededInternal(m_path, element); | 872 drawFocusIfNeededInternal(m_path, element); |
2159 } | 873 } |
2160 | 874 |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2300 } | 1014 } |
2301 | 1015 |
2302 unsigned CanvasRenderingContext2D::hitRegionsCount() const | 1016 unsigned CanvasRenderingContext2D::hitRegionsCount() const |
2303 { | 1017 { |
2304 if (m_hitRegionManager) | 1018 if (m_hitRegionManager) |
2305 return m_hitRegionManager->getHitRegionsCount(); | 1019 return m_hitRegionManager->getHitRegionsCount(); |
2306 | 1020 |
2307 return 0; | 1021 return 0; |
2308 } | 1022 } |
2309 | 1023 |
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 | |
2377 } // namespace blink | 1024 } // namespace blink |
OLD | NEW |