1 /*
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)
4 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
6 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
7 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
8 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved.
9 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "config.h"
34 #include "core/html/canvas/CanvasRenderingContext2D.h"
35
36 #include "bindings/core/v8/ExceptionMessages.h"
37 #include "bindings/core/v8/ExceptionState.h"
38 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
39 #include "core/CSSPropertyNames.h"
40 #include "core/css/CSSFontSelector.h"
41 #include "core/css/StylePropertySet.h"
42 #include "core/css/parser/CSSParser.h"
43 #include "core/css/resolver/StyleResolver.h"
44 #include "core/dom/ExceptionCode.h"
45 #include "core/dom/StyleEngine.h"
46 #include "core/events/Event.h"
47 #include "core/fetch/ImageResource.h"
48 #include "core/frame/ImageBitmap.h"
49 #include "core/html/HTMLCanvasElement.h"
50 #include "core/html/HTMLImageElement.h"
51 #include "core/html/HTMLMediaElement.h"
52 #include "core/html/HTMLVideoElement.h"
53 #include "core/html/ImageData.h"
54 #include "core/html/TextMetrics.h"
55 #include "core/html/canvas/CanvasGradient.h"
56 #include "core/html/canvas/CanvasPattern.h"
57 #include "core/html/canvas/CanvasStyle.h"
58 #include "core/html/canvas/HitRegionOptions.h"
59 #include "core/html/canvas/Path2D.h"
60 #include "core/rendering/RenderImage.h"
61 #include "core/rendering/RenderLayer.h"
62 #include "core/rendering/RenderTheme.h"
63 #include "platform/fonts/FontCache.h"
64 #include "platform/geometry/FloatQuad.h"
65 #include "platform/graphics/DrawLooperBuilder.h"
66 #include "platform/graphics/GraphicsContextStateSaver.h"
67 #include "platform/text/TextRun.h"
68 #include "wtf/CheckedArithmetic.h"
69 #include "wtf/MathExtras.h"
70 #include "wtf/OwnPtr.h"
71 #include "wtf/Uint8ClampedArray.h"
72 #include "wtf/text/StringBuilder.h"
73
74 namespace blink {
75
76 static const int defaultFontSize = 10;
77 static const char defaultFontFamily[] = "sans-serif";
78 static const char defaultFont[] = "10px sans-serif";
79 static const char inherit[] = "inherit";
80 static const char rtl[] = "rtl";
81 static const char ltr[] = "ltr";
82 static const double TryRestoreContextInterval = 0.5;
83 static const unsigned MaxTryRestoreContextAttempts = 4;
84
contextLostRestoredEventsEnabled()85 static bool contextLostRestoredEventsEnabled()
86 {
87 return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled();
88 }
89
CanvasRenderingContext2D(HTMLCanvasElement * canvas,const Canvas2DContextAttributes * attrs,bool usesCSSCompatibilityParseMode)90 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, const Canvas2DContextAttributes* attrs, bool usesCSSCompatibilityParseMode)
91 : CanvasRenderingContext(canvas)
92 , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
93 , m_hasAlpha(!attrs || attrs->alpha())
94 , m_isContextLost(false)
95 , m_contextRestorable(true)
96 , m_storageMode(!attrs ? PersistentStorage : attrs->parsedStorage())
97 , m_tryRestoreContextAttemptCount(0)
98 , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchContextLostEvent)
99 , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispatchContextRestoredEvent)
100 , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreContextEvent)
101 {
102 m_stateStack.append(adoptPtrWillBeNoop(new State()));
103 }
104
unwindStateStack()105 void CanvasRenderingContext2D::unwindStateStack()
106 {
107 if (size_t stackSize = m_stateStack.size()) {
108 if (GraphicsContext* context = canvas()->existingDrawingContext()) {
109 while (--stackSize)
110 context->restore();
111 }
112 }
113 }
114
~CanvasRenderingContext2D()115 CanvasRenderingContext2D::~CanvasRenderingContext2D()
116 {
117 }
118
validateStateStack()119 void CanvasRenderingContext2D::validateStateStack()
120 {
121 #if ENABLE(ASSERT)
122 GraphicsContext* context = canvas()->existingDrawingContext();
123 if (context && !context->contextDisabled())
124 ASSERT(context->saveCount() == m_stateStack.size());
125 #endif
126 }
127
isAccelerated() const128 bool CanvasRenderingContext2D::isAccelerated() const
129 {
130 if (!canvas()->hasImageBuffer())
131 return false;
132 GraphicsContext* context = drawingContext();
133 return context && context->isAccelerated();
134 }
135
isContextLost() const136 bool CanvasRenderingContext2D::isContextLost() const
137 {
138 return m_isContextLost;
139 }
140
loseContext()141 void CanvasRenderingContext2D::loseContext()
142 {
143 if (m_isContextLost)
144 return;
145 m_isContextLost = true;
146 m_dispatchContextLostEventTimer.startOneShot(0, FROM_HERE);
147 }
148
restoreContext()149 void CanvasRenderingContext2D::restoreContext()
150 {
151 if (!m_contextRestorable)
152 return;
153 // This code path is for restoring from an eviction
154 // Restoring from surface failure is handled internally
155 ASSERT(m_isContextLost && !canvas()->hasImageBuffer());
156
157 if (canvas()->buffer()) {
158 if (contextLostRestoredEventsEnabled()) {
159 m_dispatchContextRestoredEventTimer.startOneShot(0, FROM_HERE);
160 } else {
161 // legacy synchronous context restoration.
162 reset();
163 m_isContextLost = false;
164 }
165 }
166 }
167
trace(Visitor * visitor)168 void CanvasRenderingContext2D::trace(Visitor* visitor)
169 {
170 #if ENABLE(OILPAN)
171 visitor->trace(m_stateStack);
172 visitor->trace(m_fetchedFonts);
173 visitor->trace(m_hitRegionManager);
174 #endif
175 CanvasRenderingContext::trace(visitor);
176 }
177
dispatchContextLostEvent(Timer<CanvasRenderingContext2D> *)178 void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingContext2D>*)
179 {
180 if (contextLostRestoredEventsEnabled()) {
181 RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames::contextlost);
182 canvas()->dispatchEvent(event);
183 if (event->defaultPrevented()) {
184 m_contextRestorable = false;
185 }
186 }
187
188 // If an image buffer is present, it means the context was not lost due to
189 // an eviction, but rather due to a surface failure (gpu context lost?)
190 if (m_contextRestorable && canvas()->hasImageBuffer()) {
191 m_tryRestoreContextAttemptCount = 0;
192 m_tryRestoreContextEventTimer.startRepeating(TryRestoreContextInterval, FROM_HERE);
193 }
194 }
195
tryRestoreContextEvent(Timer<CanvasRenderingContext2D> * timer)196 void CanvasRenderingContext2D::tryRestoreContextEvent(Timer<CanvasRenderingContext2D>* timer)
197 {
198 if (!m_isContextLost) {
199 // Canvas was already restored (possibly thanks to a resize), so stop trying.
200 m_tryRestoreContextEventTimer.stop();
201 return;
202 }
203 if (canvas()->hasImageBuffer() && canvas()->buffer()->restoreSurface()) {
204 m_tryRestoreContextEventTimer.stop();
205 dispatchContextRestoredEvent(0);
206 }
207
208 if (++m_tryRestoreContextAttemptCount > MaxTryRestoreContextAttempts)
209 canvas()->discardImageBuffer();
210
211 if (!canvas()->hasImageBuffer()) {
212 // final attempt: allocate a brand new image buffer instead of restoring
213 timer->stop();
214 if (canvas()->buffer())
215 dispatchContextRestoredEvent(0);
216 }
217 }
218
dispatchContextRestoredEvent(Timer<CanvasRenderingContext2D> *)219 void CanvasRenderingContext2D::dispatchContextRestoredEvent(Timer<CanvasRenderingContext2D>*)
220 {
221 if (!m_isContextLost)
222 return;
223 reset();
224 m_isContextLost = false;
225 if (contextLostRestoredEventsEnabled()) {
226 RefPtrWillBeRawPtr<Event> event(Event::create(EventTypeNames::contextrestored));
227 canvas()->dispatchEvent(event);
228 }
229 }
230
reset()231 void CanvasRenderingContext2D::reset()
232 {
233 validateStateStack();
234 unwindStateStack();
235 m_stateStack.resize(1);
236 m_stateStack.first() = adoptPtrWillBeNoop(new State());
237 m_path.clear();
238 validateStateStack();
239 }
240
241 // Important: Several of these properties are also stored in GraphicsContext's
242 // StrokeData. The default values that StrokeData uses may not the same values
243 // that the canvas 2d spec specifies. Make sure to sync the initial state of the
244 // GraphicsContext in HTMLCanvasElement::createImageBuffer()!
State()245 CanvasRenderingContext2D::State::State()
246 : m_unrealizedSaveCount(0)
247 , m_strokeStyle(CanvasStyle::createFromRGBA(Color::black))
248 , m_fillStyle(CanvasStyle::createFromRGBA(Color::black))
249 , m_lineWidth(1)
250 , m_lineCap(ButtCap)
251 , m_lineJoin(MiterJoin)
252 , m_miterLimit(10)
253 , m_shadowBlur(0)
254 , m_shadowColor(Color::transparent)
255 , m_globalAlpha(1)
256 , m_globalComposite(CompositeSourceOver)
257 , m_globalBlend(blink::WebBlendModeNormal)
258 , m_invertibleCTM(true)
259 , m_lineDashOffset(0)
260 , m_imageSmoothingEnabled(true)
261 , m_textAlign(StartTextAlign)
262 , m_textBaseline(AlphabeticTextBaseline)
263 , m_direction(DirectionInherit)
264 , m_unparsedFont(defaultFont)
265 , m_realizedFont(false)
266 , m_hasClip(false)
267 {
268 }
269
State(const State & other)270 CanvasRenderingContext2D::State::State(const State& other)
271 : CSSFontSelectorClient()
272 , m_unrealizedSaveCount(other.m_unrealizedSaveCount)
273 , m_unparsedStrokeColor(other.m_unparsedStrokeColor)
274 , m_unparsedFillColor(other.m_unparsedFillColor)
275 , m_strokeStyle(other.m_strokeStyle)
276 , m_fillStyle(other.m_fillStyle)
277 , m_lineWidth(other.m_lineWidth)
278 , m_lineCap(other.m_lineCap)
279 , m_lineJoin(other.m_lineJoin)
280 , m_miterLimit(other.m_miterLimit)
281 , m_shadowOffset(other.m_shadowOffset)
282 , m_shadowBlur(other.m_shadowBlur)
283 , m_shadowColor(other.m_shadowColor)
284 , m_globalAlpha(other.m_globalAlpha)
285 , m_globalComposite(other.m_globalComposite)
286 , m_globalBlend(other.m_globalBlend)
287 , m_transform(other.m_transform)
288 , m_invertibleCTM(other.m_invertibleCTM)
289 , m_lineDashOffset(other.m_lineDashOffset)
290 , m_imageSmoothingEnabled(other.m_imageSmoothingEnabled)
291 , m_textAlign(other.m_textAlign)
292 , m_textBaseline(other.m_textBaseline)
293 , m_direction(other.m_direction)
294 , m_unparsedFont(other.m_unparsedFont)
295 , m_font(other.m_font)
296 , m_realizedFont(other.m_realizedFont)
297 , m_hasClip(other.m_hasClip)
298 {
299 if (m_realizedFont)
300 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalidationCallbacks(this);
301 }
302
operator =(const State & other)303 CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other)
304 {
305 if (this == &other)
306 return *this;
307
308 #if !ENABLE(OILPAN)
309 if (m_realizedFont)
310 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInvalidationCallbacks(this);
311 #endif
312
313 m_unrealizedSaveCount = other.m_unrealizedSaveCount;
314 m_unparsedStrokeColor = other.m_unparsedStrokeColor;
315 m_unparsedFillColor = other.m_unparsedFillColor;
316 m_strokeStyle = other.m_strokeStyle;
317 m_fillStyle = other.m_fillStyle;
318 m_lineWidth = other.m_lineWidth;
319 m_lineCap = other.m_lineCap;
320 m_lineJoin = other.m_lineJoin;
321 m_miterLimit = other.m_miterLimit;
322 m_shadowOffset = other.m_shadowOffset;
323 m_shadowBlur = other.m_shadowBlur;
324 m_shadowColor = other.m_shadowColor;
325 m_globalAlpha = other.m_globalAlpha;
326 m_globalComposite = other.m_globalComposite;
327 m_globalBlend = other.m_globalBlend;
328 m_transform = other.m_transform;
329 m_invertibleCTM = other.m_invertibleCTM;
330 m_imageSmoothingEnabled = other.m_imageSmoothingEnabled;
331 m_textAlign = other.m_textAlign;
332 m_textBaseline = other.m_textBaseline;
333 m_direction = other.m_direction;
334 m_unparsedFont = other.m_unparsedFont;
335 m_font = other.m_font;
336 m_realizedFont = other.m_realizedFont;
337 m_hasClip = other.m_hasClip;
338
339 if (m_realizedFont)
340 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalidationCallbacks(this);
341
342 return *this;
343 }
344
~State()345 CanvasRenderingContext2D::State::~State()
346 {
347 #if !ENABLE(OILPAN)
348 if (m_realizedFont)
349 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInvalidationCallbacks(this);
350 #endif
351 }
352
fontsNeedUpdate(CSSFontSelector * fontSelector)353 void CanvasRenderingContext2D::State::fontsNeedUpdate(CSSFontSelector* fontSelector)
354 {
355 ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector());
356 ASSERT(m_realizedFont);
357
358 m_font.update(fontSelector);
359 }
360
trace(Visitor * visitor)361 void CanvasRenderingContext2D::State::trace(Visitor* visitor)
362 {
363 visitor->trace(m_strokeStyle);
364 visitor->trace(m_fillStyle);
365 CSSFontSelectorClient::trace(visitor);
366 }
367
realizeSaves(GraphicsContext * context)368 void CanvasRenderingContext2D::realizeSaves(GraphicsContext* context)
369 {
370 validateStateStack();
371 if (state().m_unrealizedSaveCount) {
372 ASSERT(m_stateStack.size() >= 1);
373 // Reduce the current state's unrealized count by one now,
374 // to reflect the fact we are saving one state.
375 m_stateStack.last()->m_unrealizedSaveCount--;
376 m_stateStack.append(adoptPtrWillBeNoop(new State(state())));
377 // Set the new state's unrealized count to 0, because it has no outstanding saves.
378 // We need to do this explicitly because the copy constructor and operator= used
379 // by the Vector operations copy the unrealized count from the previous state (in
380 // turn necessary to support correct resizing and unwinding of the stack).
381 m_stateStack.last()->m_unrealizedSaveCount = 0;
382 if (!context)
383 context = drawingContext();
384 if (context)
385 context->save();
386 validateStateStack();
387 }
388 }
389
restore()390 void CanvasRenderingContext2D::restore()
391 {
392 validateStateStack();
393 if (state().m_unrealizedSaveCount) {
394 // We never realized the save, so just record that it was unnecessary.
395 --m_stateStack.last()->m_unrealizedSaveCount;
396 return;
397 }
398 ASSERT(m_stateStack.size() >= 1);
399 if (m_stateStack.size() <= 1)
400 return;
401 m_path.transform(state().m_transform);
402 m_stateStack.removeLast();
403 m_path.transform(state().m_transform.inverse());
404 GraphicsContext* c = drawingContext();
405 if (c)
406 c->restore();
407 validateStateStack();
408 }
409
strokeStyle() const410 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
411 {
412 return state().m_strokeStyle.get();
413 }
414
setStrokeStyle(PassRefPtrWillBeRawPtr<CanvasStyle> prpStyle)415 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtrWillBeRawPtr<CanvasStyle> prpStyle)
416 {
417 RefPtrWillBeRawPtr<CanvasStyle> style = prpStyle;
418
419 if (!style)
420 return;
421
422 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style))
423 return;
424
425 if (style->isCurrentColor()) {
426 if (style->hasOverrideAlpha())
427 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha()));
428 else
429 style = CanvasStyle::createFromRGBA(currentColor(canvas()));
430 } else if (canvas()->originClean() && style->canvasPattern() && !style->canvasPattern()->originClean()) {
431 canvas()->setOriginTainted();
432 }
433
434 GraphicsContext* c = drawingContext();
435 realizeSaves(c);
436 modifiableState().m_strokeStyle = style.release();
437 if (!c)
438 return;
439 state().m_strokeStyle->applyStrokeColor(c);
440 modifiableState().m_unparsedStrokeColor = String();
441 }
442
fillStyle() const443 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
444 {
445 return state().m_fillStyle.get();
446 }
447
setFillStyle(PassRefPtrWillBeRawPtr<CanvasStyle> prpStyle)448 void CanvasRenderingContext2D::setFillStyle(PassRefPtrWillBeRawPtr<CanvasStyle> prpStyle)
449 {
450 RefPtrWillBeRawPtr<CanvasStyle> style = prpStyle;
451
452 if (!style)
453 return;
454
455 if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style))
456 return;
457
458 if (style->isCurrentColor()) {
459 if (style->hasOverrideAlpha())
460 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha()));
461 else
462 style = CanvasStyle::createFromRGBA(currentColor(canvas()));
463 } else if (canvas()->originClean() && style->canvasPattern() && !style->canvasPattern()->originClean()) {
464 canvas()->setOriginTainted();
465 }
466
467 GraphicsContext* c = drawingContext();
468 realizeSaves(c);
469 modifiableState().m_fillStyle = style.release();
470 if (!c)
471 return;
472 state().m_fillStyle->applyFillColor(c);
473 modifiableState().m_unparsedFillColor = String();
474 }
475
lineWidth() const476 float CanvasRenderingContext2D::lineWidth() const
477 {
478 return state().m_lineWidth;
479 }
480
setLineWidth(float width)481 void CanvasRenderingContext2D::setLineWidth(float width)
482 {
483 if (!(std::isfinite(width) && width > 0))
484 return;
485 if (state().m_lineWidth == width)
486 return;
487 GraphicsContext* c = drawingContext();
488 realizeSaves(c);
489 modifiableState().m_lineWidth = width;
490 if (!c)
491 return;
492 c->setStrokeThickness(width);
493 }
494
lineCap() const495 String CanvasRenderingContext2D::lineCap() const
496 {
497 return lineCapName(state().m_lineCap);
498 }
499
setLineCap(const String & s)500 void CanvasRenderingContext2D::setLineCap(const String& s)
501 {
502 LineCap cap;
503 if (!parseLineCap(s, cap))
504 return;
505 if (state().m_lineCap == cap)
506 return;
507 GraphicsContext* c = drawingContext();
508 realizeSaves(c);
509 modifiableState().m_lineCap = cap;
510 if (!c)
511 return;
512 c->setLineCap(cap);
513 }
514
lineJoin() const515 String CanvasRenderingContext2D::lineJoin() const
516 {
517 return lineJoinName(state().m_lineJoin);
518 }
519
setLineJoin(const String & s)520 void CanvasRenderingContext2D::setLineJoin(const String& s)
521 {
522 LineJoin join;
523 if (!parseLineJoin(s, join))
524 return;
525 if (state().m_lineJoin == join)
526 return;
527 GraphicsContext* c = drawingContext();
528 realizeSaves(c);
529 modifiableState().m_lineJoin = join;
530 if (!c)
531 return;
532 c->setLineJoin(join);
533 }
534
miterLimit() const535 float CanvasRenderingContext2D::miterLimit() const
536 {
537 return state().m_miterLimit;
538 }
539
setMiterLimit(float limit)540 void CanvasRenderingContext2D::setMiterLimit(float limit)
541 {
542 if (!(std::isfinite(limit) && limit > 0))
543 return;
544 if (state().m_miterLimit == limit)
545 return;
546 GraphicsContext* c = drawingContext();
547 realizeSaves(c);
548 modifiableState().m_miterLimit = limit;
549 if (!c)
550 return;
551 c->setMiterLimit(limit);
552 }
553
shadowOffsetX() const554 float CanvasRenderingContext2D::shadowOffsetX() const
555 {
556 return state().m_shadowOffset.width();
557 }
558
setShadowOffsetX(float x)559 void CanvasRenderingContext2D::setShadowOffsetX(float x)
560 {
561 if (!std::isfinite(x))
562 return;
563 if (state().m_shadowOffset.width() == x)
564 return;
565 realizeSaves(0);
566 modifiableState().m_shadowOffset.setWidth(x);
567 applyShadow();
568 }
569
shadowOffsetY() const570 float CanvasRenderingContext2D::shadowOffsetY() const
571 {
572 return state().m_shadowOffset.height();
573 }
574
setShadowOffsetY(float y)575 void CanvasRenderingContext2D::setShadowOffsetY(float y)
576 {
577 if (!std::isfinite(y))
578 return;
579 if (state().m_shadowOffset.height() == y)
580 return;
581 realizeSaves(0);
582 modifiableState().m_shadowOffset.setHeight(y);
583 applyShadow();
584 }
585
shadowBlur() const586 float CanvasRenderingContext2D::shadowBlur() const
587 {
588 return state().m_shadowBlur;
589 }
590
setShadowBlur(float blur)591 void CanvasRenderingContext2D::setShadowBlur(float blur)
592 {
593 if (!(std::isfinite(blur) && blur >= 0))
594 return;
595 if (state().m_shadowBlur == blur)
596 return;
597 realizeSaves(0);
598 modifiableState().m_shadowBlur = blur;
599 applyShadow();
600 }
601
shadowColor() const602 String CanvasRenderingContext2D::shadowColor() const
603 {
604 return Color(state().m_shadowColor).serialized();
605 }
606
setShadowColor(const String & color)607 void CanvasRenderingContext2D::setShadowColor(const String& color)
608 {
609 RGBA32 rgba;
610 if (!parseColorOrCurrentColor(rgba, color, canvas()))
611 return;
612 if (state().m_shadowColor == rgba)
613 return;
614 realizeSaves(0);
615 modifiableState().m_shadowColor = rgba;
616 applyShadow();
617 }
618
getLineDash() const619 const Vector<float>& CanvasRenderingContext2D::getLineDash() const
620 {
621 return state().m_lineDash;
622 }
623
lineDashSequenceIsValid(const Vector<float> & dash)624 static bool lineDashSequenceIsValid(const Vector<float>& dash)
625 {
626 for (size_t i = 0; i < dash.size(); i++) {
627 if (!std::isfinite(dash[i]) || dash[i] < 0)
628 return false;
629 }
630 return true;
631 }
632
setLineDash(const Vector<float> & dash)633 void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash)
634 {
635 if (!lineDashSequenceIsValid(dash))
636 return;
637
638 realizeSaves(0);
639 modifiableState().m_lineDash = dash;
640 // Spec requires the concatenation of two copies the dash list when the
641 // number of elements is odd
642 if (dash.size() % 2)
643 modifiableState().m_lineDash.appendVector(dash);
644
645 applyLineDash();
646 }
647
lineDashOffset() const648 float CanvasRenderingContext2D::lineDashOffset() const
649 {
650 return state().m_lineDashOffset;
651 }
652
setLineDashOffset(float offset)653 void CanvasRenderingContext2D::setLineDashOffset(float offset)
654 {
655 if (!std::isfinite(offset) || state().m_lineDashOffset == offset)
656 return;
657
658 realizeSaves(0);
659 modifiableState().m_lineDashOffset = offset;
660 applyLineDash();
661 }
662
applyLineDash() const663 void CanvasRenderingContext2D::applyLineDash() const
664 {
665 GraphicsContext* c = drawingContext();
666 if (!c)
667 return;
668 DashArray convertedLineDash(state().m_lineDash.size());
669 for (size_t i = 0; i < state().m_lineDash.size(); ++i)
670 convertedLineDash[i] = static_cast<DashArrayElement>(state().m_lineDash[i]);
671 c->setLineDash(convertedLineDash, state().m_lineDashOffset);
672 }
673
globalAlpha() const674 float CanvasRenderingContext2D::globalAlpha() const
675 {
676 return state().m_globalAlpha;
677 }
678
setGlobalAlpha(float alpha)679 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
680 {
681 if (!(alpha >= 0 && alpha <= 1))
682 return;
683 if (state().m_globalAlpha == alpha)
684 return;
685 GraphicsContext* c = drawingContext();
686 realizeSaves(c);
687 modifiableState().m_globalAlpha = alpha;
688 if (!c)
689 return;
690 c->setAlphaAsFloat(alpha);
691 }
692
globalCompositeOperation() const693 String CanvasRenderingContext2D::globalCompositeOperation() const
694 {
695 return compositeOperatorName(state().m_globalComposite, state().m_globalBlend);
696 }
697
setGlobalCompositeOperation(const String & operation)698 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
699 {
700 CompositeOperator op = CompositeSourceOver;
701 blink::WebBlendMode blendMode = blink::WebBlendModeNormal;
702 if (!parseCompositeAndBlendOperator(operation, op, blendMode))
703 return;
704 if ((state().m_globalComposite == op) && (state().m_globalBlend == blendMode))
705 return;
706 GraphicsContext* c = drawingContext();
707 realizeSaves(c);
708 modifiableState().m_globalComposite = op;
709 modifiableState().m_globalBlend = blendMode;
710 if (!c)
711 return;
712 c->setCompositeOperation(op, blendMode);
713 }
714
setCurrentTransform(PassRefPtr<SVGMatrixTearOff> passMatrixTearOff)715 void CanvasRenderingContext2D::setCurrentTransform(PassRefPtr<SVGMatrixTearOff> passMatrixTearOff)
716 {
717 RefPtr<SVGMatrixTearOff> matrixTearOff = passMatrixTearOff;
718 const AffineTransform& transform = matrixTearOff->value();
719 setTransform(transform.a(), transform.b(), transform.c(), transform.d(), transform.e(), transform.f());
720 }
721
scale(float sx,float sy)722 void CanvasRenderingContext2D::scale(float sx, float sy)
723 {
724 GraphicsContext* c = drawingContext();
725 if (!c)
726 return;
727 if (!state().m_invertibleCTM)
728 return;
729
730 if (!std::isfinite(sx) | !std::isfinite(sy))
731 return;
732
733 AffineTransform newTransform = state().m_transform;
734 newTransform.scaleNonUniform(sx, sy);
735 if (state().m_transform == newTransform)
736 return;
737
738 realizeSaves(c);
739
740 if (!newTransform.isInvertible()) {
741 modifiableState().m_invertibleCTM = false;
742 return;
743 }
744
745 modifiableState().m_transform = newTransform;
746 c->scale(sx, sy);
747 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
748 }
749
rotate(float angleInRadians)750 void CanvasRenderingContext2D::rotate(float angleInRadians)
751 {
752 GraphicsContext* c = drawingContext();
753 if (!c)
754 return;
755 if (!state().m_invertibleCTM)
756 return;
757
758 if (!std::isfinite(angleInRadians))
759 return;
760
761 AffineTransform newTransform = state().m_transform;
762 newTransform.rotateRadians(angleInRadians);
763 if (state().m_transform == newTransform)
764 return;
765
766 realizeSaves(c);
767
768 if (!newTransform.isInvertible()) {
769 modifiableState().m_invertibleCTM = false;
770 return;
771 }
772
773 modifiableState().m_transform = newTransform;
774 c->rotate(angleInRadians);
775 m_path.transform(AffineTransform().rotateRadians(-angleInRadians));
776 }
777
translate(float tx,float ty)778 void CanvasRenderingContext2D::translate(float tx, float ty)
779 {
780 GraphicsContext* c = drawingContext();
781 if (!c)
782 return;
783 if (!state().m_invertibleCTM)
784 return;
785
786 if (!std::isfinite(tx) | !std::isfinite(ty))
787 return;
788
789 AffineTransform newTransform = state().m_transform;
790 newTransform.translate(tx, ty);
791 if (state().m_transform == newTransform)
792 return;
793
794 realizeSaves(c);
795
796 if (!newTransform.isInvertible()) {
797 modifiableState().m_invertibleCTM = false;
798 return;
799 }
800
801 modifiableState().m_transform = newTransform;
802 c->translate(tx, ty);
803 m_path.transform(AffineTransform().translate(-tx, -ty));
804 }
805
transform(float m11,float m12,float m21,float m22,float dx,float dy)806 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
807 {
808 GraphicsContext* c = drawingContext();
809 if (!c)
810 return;
811 if (!state().m_invertibleCTM)
812 return;
813
814 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
815 return;
816
817 AffineTransform transform(m11, m12, m21, m22, dx, dy);
818 AffineTransform newTransform = state().m_transform * transform;
819 if (state().m_transform == newTransform)
820 return;
821
822 realizeSaves(c);
823
824 modifiableState().m_transform = newTransform;
825 if (!newTransform.isInvertible()) {
826 modifiableState().m_invertibleCTM = false;
827 return;
828 }
829
830 c->concatCTM(transform);
831 m_path.transform(transform.inverse());
832 }
833
resetTransform()834 void CanvasRenderingContext2D::resetTransform()
835 {
836 GraphicsContext* c = drawingContext();
837 if (!c)
838 return;
839
840 AffineTransform ctm = state().m_transform;
841 bool invertibleCTM = state().m_invertibleCTM;
842 // It is possible that CTM is identity while CTM is not invertible.
843 // When CTM becomes non-invertible, realizeSaves() can make CTM identity.
844 if (ctm.isIdentity() && invertibleCTM)
845 return;
846
847 realizeSaves(c);
848 // resetTransform() resolves the non-invertible CTM state.
849 modifiableState().m_transform.makeIdentity();
850 modifiableState().m_invertibleCTM = true;
851 c->setCTM(canvas()->baseTransform());
852
853 if (invertibleCTM)
854 m_path.transform(ctm);
855 // When else, do nothing because all transform methods didn't update m_path when CTM became non-invertible.
856 // It means that resetTransform() restores m_path just before CTM became non-invertible.
857 }
858
setTransform(float m11,float m12,float m21,float m22,float dx,float dy)859 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
860 {
861 GraphicsContext* c = drawingContext();
862 if (!c)
863 return;
864
865 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
866 return;
867
868 resetTransform();
869 transform(m11, m12, m21, m22, dx, dy);
870 }
871
setStrokeColor(const String & color)872 void CanvasRenderingContext2D::setStrokeColor(const String& color)
873 {
874 if (color == state().m_unparsedStrokeColor)
875 return;
876 realizeSaves(0);
877 setStrokeStyle(CanvasStyle::createFromString(color));
878 modifiableState().m_unparsedStrokeColor = color;
879 }
880
setStrokeColor(float grayLevel)881 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
882 {
883 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
884 return;
885 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
886 }
887
setStrokeColor(const String & color,float alpha)888 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
889 {
890 setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
891 }
892
setStrokeColor(float grayLevel,float alpha)893 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
894 {
895 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
896 return;
897 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
898 }
899
setStrokeColor(float r,float g,float b,float a)900 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
901 {
902 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b, a))
903 return;
904 setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
905 }
906
setStrokeColor(float c,float m,float y,float k,float a)907 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
908 {
909 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a))
910 return;
911 setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
912 }
913
setFillColor(const String & color)914 void CanvasRenderingContext2D::setFillColor(const String& color)
915 {
916 if (color == state().m_unparsedFillColor)
917 return;
918 realizeSaves(0);
919 setFillStyle(CanvasStyle::createFromString(color));
920 modifiableState().m_unparsedFillColor = color;
921 }
922
setFillColor(float grayLevel)923 void CanvasRenderingContext2D::setFillColor(float grayLevel)
924 {
925 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
926 return;
927 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
928 }
929
setFillColor(const String & color,float alpha)930 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
931 {
932 setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
933 }
934
setFillColor(float grayLevel,float alpha)935 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
936 {
937 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
938 return;
939 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
940 }
941
setFillColor(float r,float g,float b,float a)942 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
943 {
944 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a))
945 return;
946 setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
947 }
948
setFillColor(float c,float m,float y,float k,float a)949 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
950 {
951 if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k, a))
952 return;
953 setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
954 }
955
beginPath()956 void CanvasRenderingContext2D::beginPath()
957 {
958 m_path.clear();
959 }
960
validateRectForCanvas(float & x,float & y,float & width,float & height)961 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
962 {
963 if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height))
964 return false;
965
966 if (!width && !height)
967 return false;
968
969 if (width < 0) {
970 width = -width;
971 x -= width;
972 }
973
974 if (height < 0) {
975 height = -height;
976 y -= height;
977 }
978
979 return true;
980 }
981
isFullCanvasCompositeMode(CompositeOperator op)982 static bool isFullCanvasCompositeMode(CompositeOperator op)
983 {
984 // See 4.8.11.1.3 Compositing
985 // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already
986 // implement the specification's behavior.
987 return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop;
988 }
989
parseWinding(const String & windingRuleString)990 static WindRule parseWinding(const String& windingRuleString)
991 {
992 if (windingRuleString == "nonzero")
993 return RULE_NONZERO;
994 if (windingRuleString == "evenodd")
995 return RULE_EVENODD;
996
997 ASSERT_NOT_REACHED();
998 return RULE_EVENODD;
999 }
1000
fillInternal(const Path & path,const String & windingRuleString)1001 void CanvasRenderingContext2D::fillInternal(const Path& path, const String& windingRuleString)
1002 {
1003 if (path.isEmpty()) {
1004 return;
1005 }
1006 GraphicsContext* c = drawingContext();
1007 if (!c) {
1008 return;
1009 }
1010 if (!state().m_invertibleCTM) {
1011 return;
1012 }
1013 FloatRect clipBounds;
1014 if (!c->getTransformedClipBounds(&clipBounds)) {
1015 return;
1016 }
1017
1018 // If gradient size is zero, then paint nothing.
1019 Gradient* gradient = c->fillGradient();
1020 if (gradient && gradient->isZeroSize()) {
1021 return;
1022 }
1023
1024 WindRule windRule = c->fillRule();
1025 c->setFillRule(parseWinding(windingRuleString));
1026
1027 if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1028 fullCanvasCompositedFill(path);
1029 didDraw(clipBounds);
1030 } else if (state().m_globalComposite == CompositeCopy) {
1031 clearCanvas();
1032 c->fillPath(path);
1033 didDraw(clipBounds);
1034 } else {
1035 FloatRect dirtyRect;
1036 if (computeDirtyRect(path.boundingRect(), clipBounds, &dirtyRect)) {
1037 c->fillPath(path);
1038 didDraw(dirtyRect);
1039 }
1040 }
1041
1042 c->setFillRule(windRule);
1043 }
1044
fill(const String & windingRuleString)1045 void CanvasRenderingContext2D::fill(const String& windingRuleString)
1046 {
1047 fillInternal(m_path, windingRuleString);
1048 }
1049
fill(Path2D * domPath,const String & windingRuleString)1050 void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleString)
1051 {
1052 fillInternal(domPath->path(), windingRuleString);
1053 }
1054
strokeInternal(const Path & path)1055 void CanvasRenderingContext2D::strokeInternal(const Path& path)
1056 {
1057 if (path.isEmpty()) {
1058 return;
1059 }
1060 GraphicsContext* c = drawingContext();
1061 if (!c) {
1062 return;
1063 }
1064 if (!state().m_invertibleCTM) {
1065 return;
1066 }
1067 FloatRect clipBounds;
1068 if (!c->getTransformedClipBounds(&clipBounds))
1069 return;
1070
1071 // If gradient size is zero, then paint nothing.
1072 Gradient* gradient = c->strokeGradient();
1073 if (gradient && gradient->isZeroSize()) {
1074 return;
1075 }
1076
1077 if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1078 fullCanvasCompositedStroke(path);
1079 didDraw(clipBounds);
1080 } else if (state().m_globalComposite == CompositeCopy) {
1081 clearCanvas();
1082 c->strokePath(path);
1083 didDraw(clipBounds);
1084 } else {
1085 FloatRect bounds = path.boundingRect();
1086 inflateStrokeRect(bounds);
1087 FloatRect dirtyRect;
1088 if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) {
1089 c->strokePath(path);
1090 didDraw(dirtyRect);
1091 }
1092 }
1093 }
1094
stroke()1095 void CanvasRenderingContext2D::stroke()
1096 {
1097 strokeInternal(m_path);
1098 }
1099
stroke(Path2D * domPath)1100 void CanvasRenderingContext2D::stroke(Path2D* domPath)
1101 {
1102 strokeInternal(domPath->path());
1103 }
1104
clipInternal(const Path & path,const String & windingRuleString)1105 void CanvasRenderingContext2D::clipInternal(const Path& path, const String& windingRuleString)
1106 {
1107 GraphicsContext* c = drawingContext();
1108 if (!c) {
1109 return;
1110 }
1111 if (!state().m_invertibleCTM) {
1112 return;
1113 }
1114
1115 realizeSaves(c);
1116 c->canvasClip(path, parseWinding(windingRuleString));
1117 modifiableState().m_hasClip = true;
1118 }
1119
clip(const String & windingRuleString)1120 void CanvasRenderingContext2D::clip(const String& windingRuleString)
1121 {
1122 clipInternal(m_path, windingRuleString);
1123 }
1124
clip(Path2D * domPath,const String & windingRuleString)1125 void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleString)
1126 {
1127 clipInternal(domPath->path(), windingRuleString);
1128 }
1129
isPointInPath(const float x,const float y,const String & windingRuleString)1130 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y, const String& windingRuleString)
1131 {
1132 return isPointInPathInternal(m_path, x, y, windingRuleString);
1133 }
1134
isPointInPath(Path2D * domPath,const float x,const float y,const String & windingRuleString)1135 bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const float x, const float y, const String& windingRuleString)
1136 {
1137 return isPointInPathInternal(domPath->path(), x, y, windingRuleString);
1138 }
1139
isPointInPathInternal(const Path & path,const float x,const float y,const String & windingRuleString)1140 bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, const float x, const float y, const String& windingRuleString)
1141 {
1142 GraphicsContext* c = drawingContext();
1143 if (!c)
1144 return false;
1145 if (!state().m_invertibleCTM)
1146 return false;
1147
1148 FloatPoint point(x, y);
1149 AffineTransform ctm = state().m_transform;
1150 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
1151 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
1152 return false;
1153
1154 return path.contains(transformedPoint, parseWinding(windingRuleString));
1155 }
1156
isPointInStroke(const float x,const float y)1157 bool CanvasRenderingContext2D::isPointInStroke(const float x, const float y)
1158 {
1159 return isPointInStrokeInternal(m_path, x, y);
1160 }
1161
isPointInStroke(Path2D * domPath,const float x,const float y)1162 bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const float x, const float y)
1163 {
1164 return isPointInStrokeInternal(domPath->path(), x, y);
1165 }
1166
isPointInStrokeInternal(const Path & path,const float x,const float y)1167 bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, const float x, const float y)
1168 {
1169 GraphicsContext* c = drawingContext();
1170 if (!c)
1171 return false;
1172 if (!state().m_invertibleCTM)
1173 return false;
1174
1175 FloatPoint point(x, y);
1176 AffineTransform ctm = state().m_transform;
1177 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
1178 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
1179 return false;
1180
1181 StrokeData strokeData;
1182 strokeData.setThickness(lineWidth());
1183 strokeData.setLineCap(getLineCap());
1184 strokeData.setLineJoin(getLineJoin());
1185 strokeData.setMiterLimit(miterLimit());
1186 strokeData.setLineDash(getLineDash(), lineDashOffset());
1187 return path.strokeContains(transformedPoint, strokeData);
1188 }
1189
scrollPathIntoView()1190 void CanvasRenderingContext2D::scrollPathIntoView()
1191 {
1192 scrollPathIntoViewInternal(m_path);
1193 }
1194
scrollPathIntoView(Path2D * path2d)1195 void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d)
1196 {
1197 scrollPathIntoViewInternal(path2d->path());
1198 }
1199
scrollPathIntoViewInternal(const Path & path)1200 void CanvasRenderingContext2D::scrollPathIntoViewInternal(const Path& path)
1201 {
1202 RenderObject* renderer = canvas()->renderer();
1203 RenderBox* renderBox = canvas()->renderBox();
1204 if (!renderer || !renderBox || !state().m_invertibleCTM || path.isEmpty())
1205 return;
1206
1207 canvas()->document().updateLayoutIgnorePendingStylesheets();
1208
1209 // Apply transformation and get the bounding rect
1210 Path transformedPath = path;
1211 transformedPath.transform(state().m_transform);
1212 FloatRect boundingRect = transformedPath.boundingRect();
1213
1214 // Offset by the canvas rect
1215 LayoutRect pathRect(boundingRect);
1216 IntRect canvasRect = renderBox->absoluteContentBox();
1217 pathRect.move(canvasRect.x(), canvasRect.y());
1218
1219 renderer->scrollRectToVisible(
1220 pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopAlways);
1221
1222 // TODO: should implement "inform the user" that the caret and/or
1223 // selection the specified rectangle of the canvas. See http://crbug.com/357987
1224 }
1225
clearRect(float x,float y,float width,float height)1226 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
1227 {
1228 if (!validateRectForCanvas(x, y, width, height))
1229 return;
1230 GraphicsContext* context = drawingContext();
1231 if (!context)
1232 return;
1233 if (!state().m_invertibleCTM)
1234 return;
1235 FloatRect rect(x, y, width, height);
1236
1237 FloatRect dirtyRect;
1238 if (!computeDirtyRect(rect, &dirtyRect))
1239 return;
1240
1241 bool saved = false;
1242 if (shouldDrawShadows()) {
1243 context->save();
1244 saved = true;
1245 context->clearShadow();
1246 }
1247 if (state().m_globalAlpha != 1) {
1248 if (!saved) {
1249 context->save();
1250 saved = true;
1251 }
1252 context->setAlphaAsFloat(1);
1253 }
1254 if (state().m_globalComposite != CompositeSourceOver) {
1255 if (!saved) {
1256 context->save();
1257 saved = true;
1258 }
1259 context->setCompositeOperation(CompositeSourceOver);
1260 }
1261 context->clearRect(rect);
1262 if (m_hitRegionManager)
1263 m_hitRegionManager->removeHitRegionsInRect(rect, state().m_transform);
1264 if (saved)
1265 context->restore();
1266
1267 validateStateStack();
1268 didDraw(dirtyRect);
1269 }
1270
fillRect(float x,float y,float width,float height)1271 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
1272 {
1273 if (!validateRectForCanvas(x, y, width, height))
1274 return;
1275
1276 GraphicsContext* c = drawingContext();
1277 if (!c)
1278 return;
1279 if (!state().m_invertibleCTM)
1280 return;
1281 FloatRect clipBounds;
1282 if (!c->getTransformedClipBounds(&clipBounds))
1283 return;
1284
1285 // from the HTML5 Canvas spec:
1286 // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing
1287 // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing
1288 Gradient* gradient = c->fillGradient();
1289 if (gradient && gradient->isZeroSize())
1290 return;
1291
1292 FloatRect rect(x, y, width, height);
1293 if (rectContainsTransformedRect(rect, clipBounds)) {
1294 c->fillRect(rect);
1295 didDraw(clipBounds);
1296 } else if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1297 fullCanvasCompositedFill(rect);
1298 didDraw(clipBounds);
1299 } else if (state().m_globalComposite == CompositeCopy) {
1300 clearCanvas();
1301 c->fillRect(rect);
1302 didDraw(clipBounds);
1303 } else {
1304 FloatRect dirtyRect;
1305 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) {
1306 c->fillRect(rect);
1307 didDraw(dirtyRect);
1308 }
1309 }
1310 }
1311
strokeRect(float x,float y,float width,float height)1312 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
1313 {
1314 if (!validateRectForCanvas(x, y, width, height))
1315 return;
1316
1317 if (!(state().m_lineWidth >= 0))
1318 return;
1319
1320 GraphicsContext* c = drawingContext();
1321 if (!c)
1322 return;
1323 if (!state().m_invertibleCTM)
1324 return;
1325 FloatRect clipBounds;
1326 if (!c->getTransformedClipBounds(&clipBounds))
1327 return;
1328
1329 // If gradient size is zero, then paint nothing.
1330 Gradient* gradient = c->strokeGradient();
1331 if (gradient && gradient->isZeroSize())
1332 return;
1333
1334 FloatRect rect(x, y, width, height);
1335
1336 if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1337 fullCanvasCompositedStroke(rect);
1338 didDraw(clipBounds);
1339 } else if (state().m_globalComposite == CompositeCopy) {
1340 clearCanvas();
1341 c->strokeRect(rect);
1342 didDraw(clipBounds);
1343 } else {
1344 FloatRect boundingRect = rect;
1345 boundingRect.inflate(state().m_lineWidth / 2);
1346 FloatRect dirtyRect;
1347 if (computeDirtyRect(boundingRect, clipBounds, &dirtyRect)) {
1348 c->strokeRect(rect);
1349 didDraw(dirtyRect);
1350 }
1351 }
1352 }
1353
setShadow(float width,float height,float blur)1354 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
1355 {
1356 setShadow(FloatSize(width, height), blur, Color::transparent);
1357 }
1358
setShadow(float width,float height,float blur,const String & color)1359 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
1360 {
1361 RGBA32 rgba;
1362 if (!parseColorOrCurrentColor(rgba, color, canvas()))
1363 return;
1364 setShadow(FloatSize(width, height), blur, rgba);
1365 }
1366
setShadow(float width,float height,float blur,float grayLevel)1367 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
1368 {
1369 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1));
1370 }
1371
setShadow(float width,float height,float blur,const String & color,float alpha)1372 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
1373 {
1374 RGBA32 rgba;
1375 if (!parseColorOrCurrentColor(rgba, color, canvas()))
1376 return;
1377 setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(rgba, alpha));
1378 }
1379
setShadow(float width,float height,float blur,float grayLevel,float alpha)1380 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
1381 {
1382 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha));
1383 }
1384
setShadow(float width,float height,float blur,float r,float g,float b,float a)1385 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
1386 {
1387 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(r, g, b, a));
1388 }
1389
setShadow(float width,float height,float blur,float c,float m,float y,float k,float a)1390 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
1391 {
1392 setShadow(FloatSize(width, height), blur, makeRGBAFromCMYKA(c, m, y, k, a));
1393 }
1394
clearShadow()1395 void CanvasRenderingContext2D::clearShadow()
1396 {
1397 setShadow(FloatSize(), 0, Color::transparent);
1398 }
1399
setShadow(const FloatSize & offset,float blur,RGBA32 color)1400 void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, RGBA32 color)
1401 {
1402 if (state().m_shadowOffset == offset && state().m_shadowBlur == blur && state().m_shadowColor == color)
1403 return;
1404 bool wasDrawingShadows = shouldDrawShadows();
1405 realizeSaves(0);
1406 modifiableState().m_shadowOffset = offset;
1407 modifiableState().m_shadowBlur = blur;
1408 modifiableState().m_shadowColor = color;
1409 if (!wasDrawingShadows && !shouldDrawShadows())
1410 return;
1411 applyShadow();
1412 }
1413
applyShadow()1414 void CanvasRenderingContext2D::applyShadow()
1415 {
1416 GraphicsContext* c = drawingContext();
1417 if (!c)
1418 return;
1419
1420 if (shouldDrawShadows()) {
1421 c->setShadow(state().m_shadowOffset, state().m_shadowBlur, state().m_shadowColor,
1422 DrawLooperBuilder::ShadowIgnoresTransforms);
1423 } else {
1424 c->clearShadow();
1425 }
1426 }
1427
shouldDrawShadows() const1428 bool CanvasRenderingContext2D::shouldDrawShadows() const
1429 {
1430 return alphaChannel(state().m_shadowColor) && (state().m_shadowBlur || !state().m_shadowOffset.isZero());
1431 }
1432
normalizeRect(const FloatRect & rect)1433 static inline FloatRect normalizeRect(const FloatRect& rect)
1434 {
1435 return FloatRect(std::min(rect.x(), rect.maxX()),
1436 std::min(rect.y(), rect.maxY()),
1437 std::max(rect.width(), -rect.width()),
1438 std::max(rect.height(), -rect.height()));
1439 }
1440
clipRectsToImageRect(const FloatRect & imageRect,FloatRect * srcRect,FloatRect * dstRect)1441 static inline void clipRectsToImageRect(const FloatRect& imageRect, FloatRect* srcRect, FloatRect* dstRect)
1442 {
1443 if (imageRect.contains(*srcRect))
1444 return;
1445
1446 // Compute the src to dst transform
1447 FloatSize scale(dstRect->size().width() / srcRect->size().width(), dstRect->size().height() / srcRect->size().height());
1448 FloatPoint scaledSrcLocation = srcRect->location();
1449 scaledSrcLocation.scale(scale.width(), scale.height());
1450 FloatSize offset = dstRect->location() - scaledSrcLocation;
1451
1452 srcRect->intersect(imageRect);
1453
1454 // To clip the destination rectangle in the same proportion, transform the clipped src rect
1455 *dstRect = *srcRect;
1456 dstRect->scale(scale.width(), scale.height());
1457 dstRect->move(offset);
1458 }
1459
drawImage(CanvasImageSource * imageSource,float x,float y,ExceptionState & exceptionState)1460 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, float x, float y, ExceptionState& exceptionState)
1461 {
1462 FloatSize destRectSize = imageSource->defaultDestinationSize();
1463 drawImage(imageSource, x, y, destRectSize.width(), destRectSize.height(), exceptionState);
1464 }
1465
drawImage(CanvasImageSource * imageSource,float x,float y,float width,float height,ExceptionState & exceptionState)1466 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource,
1467 float x, float y, float width, float height, ExceptionState& exceptionState)
1468 {
1469 FloatSize sourceRectSize = imageSource->sourceSize();
1470 drawImage(imageSource, 0, 0, sourceRectSize.width(), sourceRectSize.height(), x, y, width, height, exceptionState);
1471 }
1472
drawImage(CanvasImageSource * imageSource,float sx,float sy,float sw,float sh,float dx,float dy,float dw,float dh,ExceptionState & exceptionState)1473 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource,
1474 float sx, float sy, float sw, float sh,
1475 float dx, float dy, float dw, float dh, ExceptionState& exceptionState)
1476 {
1477 GraphicsContext* c = drawingContext(); // Do not exit yet if !c because we may need to throw exceptions first
1478 CompositeOperator op = c ? c->compositeOperation() : CompositeSourceOver;
1479 blink::WebBlendMode blendMode = c ? c->blendModeOperation() : blink::WebBlendModeNormal;
1480 drawImageInternal(imageSource, sx, sy, sw, sh, dx, dy, dw, dh, exceptionState, op, blendMode, c);
1481 }
1482
drawImageInternal(CanvasImageSource * imageSource,float sx,float sy,float sw,float sh,float dx,float dy,float dw,float dh,ExceptionState & exceptionState,CompositeOperator op,blink::WebBlendMode blendMode,GraphicsContext * c)1483 void CanvasRenderingContext2D::drawImageInternal(CanvasImageSource* imageSource,
1484 float sx, float sy, float sw, float sh,
1485 float dx, float dy, float dw, float dh, ExceptionState& exceptionState,
1486 CompositeOperator op, blink::WebBlendMode blendMode, GraphicsContext* c)
1487 {
1488 RefPtr<Image> image;
1489 SourceImageStatus sourceImageStatus = InvalidSourceImageStatus;
1490 if (!imageSource->isVideoElement()) {
1491 SourceImageMode mode = canvas() == imageSource ? CopySourceImageIfVolatile : DontCopySourceImage; // Thunking for ==
1492 image = imageSource->getSourceImageForCanvas(mode, &sourceImageStatus);
1493 if (sourceImageStatus == UndecodableSourceImageStatus)
1494 exceptionState.throwDOMException(InvalidStateError, "The HTMLImageElement provided is in the 'broken' state.");
1495 if (!image || !image->width() || !image->height())
1496 return;
1497 }
1498
1499 if (!c)
1500 c = drawingContext();
1501 if (!c)
1502 return;
1503
1504 if (!state().m_invertibleCTM)
1505 return;
1506
1507 if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dw) || !std::isfinite(dh)
1508 || !std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !std::isfinite(sh)
1509 || !dw || !dh || !sw || !sh)
1510 return;
1511
1512 FloatRect clipBounds;
1513 if (!c->getTransformedClipBounds(&clipBounds))
1514 return;
1515
1516 FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh));
1517 FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh));
1518
1519 clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->sourceSize()), &srcRect, &dstRect);
1520
1521 imageSource->adjustDrawRects(&srcRect, &dstRect);
1522
1523 if (srcRect.isEmpty())
1524 return;
1525
1526 FloatRect dirtyRect = clipBounds;
1527 if (imageSource->isVideoElement()) {
1528 // TODO(dshwang): unify video code into below code to composite correctly; crbug.com/407079
1529 drawVideo(static_cast<HTMLVideoElement*>(imageSource), srcRect, dstRect);
1530 computeDirtyRect(dstRect, clipBounds, &dirtyRect);
1531 } else {
1532 if (rectContainsTransformedRect(dstRect, clipBounds)) {
1533 c->drawImage(image.get(), dstRect, srcRect, op, blendMode);
1534 } else if (isFullCanvasCompositeMode(op)) {
1535 fullCanvasCompositedDrawImage(image.get(), dstRect, srcRect, op);
1536 } else if (op == CompositeCopy) {
1537 clearCanvas();
1538 c->drawImage(image.get(), dstRect, srcRect, op, blendMode);
1539 } else {
1540 FloatRect dirtyRect;
1541 computeDirtyRect(dstRect, clipBounds, &dirtyRect);
1542 c->drawImage(image.get(), dstRect, srcRect, op, blendMode);
1543 }
1544
1545 if (sourceImageStatus == ExternalSourceImageStatus && isAccelerated() && canvas()->buffer())
1546 canvas()->buffer()->flush();
1547 }
1548
1549 if (canvas()->originClean() && wouldTaintOrigin(imageSource))
1550 canvas()->setOriginTainted();
1551
1552 didDraw(dirtyRect);
1553 }
1554
drawVideo(HTMLVideoElement * video,FloatRect srcRect,FloatRect dstRect)1555 void CanvasRenderingContext2D::drawVideo(HTMLVideoElement* video, FloatRect srcRect, FloatRect dstRect)
1556 {
1557 GraphicsContext* c = drawingContext();
1558 GraphicsContextStateSaver stateSaver(*c);
1559 c->clip(dstRect);
1560 c->translate(dstRect.x(), dstRect.y());
1561 c->scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height());
1562 c->translate(-srcRect.x(), -srcRect.y());
1563 video->paintCurrentFrameInContext(c, IntRect(IntPoint(), IntSize(video->videoWidth(), video->videoHeight())));
1564 stateSaver.restore();
1565 validateStateStack();
1566 }
1567
drawImageFromRect(HTMLImageElement * image,float sx,float sy,float sw,float sh,float dx,float dy,float dw,float dh,const String & compositeOperation)1568 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1569 float sx, float sy, float sw, float sh,
1570 float dx, float dy, float dw, float dh,
1571 const String& compositeOperation)
1572 {
1573 if (!image)
1574 return;
1575 CompositeOperator op;
1576 blink::WebBlendMode blendOp = blink::WebBlendModeNormal;
1577 if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != blink::WebBlendModeNormal)
1578 op = CompositeSourceOver;
1579
1580 drawImageInternal(image, sx, sy, sw, sh, dx, dy, dw, dh, IGNORE_EXCEPTION, op, blendOp);
1581 }
1582
setAlpha(float alpha)1583 void CanvasRenderingContext2D::setAlpha(float alpha)
1584 {
1585 setGlobalAlpha(alpha);
1586 }
1587
setCompositeOperation(const String & operation)1588 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1589 {
1590 setGlobalCompositeOperation(operation);
1591 }
1592
clearCanvas()1593 void CanvasRenderingContext2D::clearCanvas()
1594 {
1595 FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height());
1596 GraphicsContext* c = drawingContext();
1597 if (!c)
1598 return;
1599
1600 c->save();
1601 c->setCTM(canvas()->baseTransform());
1602 c->clearRect(canvasRect);
1603 c->restore();
1604 }
1605
rectContainsTransformedRect(const FloatRect & rect,const FloatRect & transformedRect) const1606 bool CanvasRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect, const FloatRect& transformedRect) const
1607 {
1608 FloatQuad quad(rect);
1609 FloatQuad transformedQuad(transformedRect);
1610 return state().m_transform.mapQuad(quad).containsQuad(transformedQuad);
1611 }
1612
drawImageToContext(Image * image,GraphicsContext * context,const FloatRect & dest,const FloatRect & src,CompositeOperator op)1613 static void drawImageToContext(Image* image, GraphicsContext* context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1614 {
1615 context->drawImage(image, dest, src, op);
1616 }
1617
fullCanvasCompositedDrawImage(T * image,const FloatRect & dest,const FloatRect & src,CompositeOperator op)1618 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1619 {
1620 ASSERT(isFullCanvasCompositeMode(op));
1621
1622 GraphicsContext* c = drawingContext();
1623 c->beginLayer(1, op);
1624 drawImageToContext(image, c, dest, src, CompositeSourceOver);
1625 c->endLayer();
1626 }
1627
fillPrimitive(const FloatRect & rect,GraphicsContext * context)1628 static void fillPrimitive(const FloatRect& rect, GraphicsContext* context)
1629 {
1630 context->fillRect(rect);
1631 }
1632
fillPrimitive(const Path & path,GraphicsContext * context)1633 static void fillPrimitive(const Path& path, GraphicsContext* context)
1634 {
1635 context->fillPath(path);
1636 }
1637
fullCanvasCompositedFill(const T & area)1638 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedFill(const T& area)
1639 {
1640 ASSERT(isFullCanvasCompositeMode(state().m_globalComposite));
1641
1642 GraphicsContext* c = drawingContext();
1643 ASSERT(c);
1644 c->beginLayer(1, state().m_globalComposite);
1645 CompositeOperator previousOperator = c->compositeOperation();
1646 c->setCompositeOperation(CompositeSourceOver);
1647 fillPrimitive(area, c);
1648 c->setCompositeOperation(previousOperator);
1649 c->endLayer();
1650 }
1651
strokePrimitive(const FloatRect & rect,GraphicsContext * context)1652 static void strokePrimitive(const FloatRect& rect, GraphicsContext* context)
1653 {
1654 context->strokeRect(rect);
1655 }
1656
strokePrimitive(const Path & path,GraphicsContext * context)1657 static void strokePrimitive(const Path& path, GraphicsContext* context)
1658 {
1659 context->strokePath(path);
1660 }
1661
fullCanvasCompositedStroke(const T & area)1662 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedStroke(const T& area)
1663 {
1664 ASSERT(isFullCanvasCompositeMode(state().m_globalComposite));
1665
1666 GraphicsContext* c = drawingContext();
1667 ASSERT(c);
1668 c->beginLayer(1, state().m_globalComposite);
1669 CompositeOperator previousOperator = c->compositeOperation();
1670 c->setCompositeOperation(CompositeSourceOver);
1671 strokePrimitive(area, c);
1672 c->setCompositeOperation(previousOperator);
1673 c->endLayer();
1674 }
1675
createLinearGradient(float x0,float y0,float x1,float y1)1676 PassRefPtrWillBeRawPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
1677 {
1678 RefPtrWillBeRawPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1679 return gradient.release();
1680 }
1681
createRadialGradient(float x0,float y0,float r0,float x1,float y1,float r1,ExceptionState & exceptionState)1682 PassRefPtrWillBeRawPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionState& exceptionState)
1683 {
1684 if (r0 < 0 || r1 < 0) {
1685 exceptionState.throwDOMException(IndexSizeError, String::format("The %s provided is less than 0.", r0 < 0 ? "r0" : "r1"));
1686 return nullptr;
1687 }
1688
1689 RefPtrWillBeRawPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1690 return gradient.release();
1691 }
1692
createPattern(CanvasImageSource * imageSource,const String & repetitionType,ExceptionState & exceptionState)1693 PassRefPtrWillBeRawPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(CanvasImageSource* imageSource,
1694 const String& repetitionType, ExceptionState& exceptionState)
1695 {
1696 Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetitionType, exceptionState);
1697 if (exceptionState.hadException())
1698 return nullptr;
1699
1700 SourceImageStatus status;
1701 RefPtr<Image> imageForRendering = imageSource->getSourceImageForCanvas(CopySourceImageIfVolatile, &status);
1702
1703 switch (status) {
1704 case NormalSourceImageStatus:
1705 break;
1706 case ZeroSizeCanvasSourceImageStatus:
1707 exceptionState.throwDOMException(InvalidStateError, String::format("The canvas %s is 0.", imageSource->sourceSize().width() ? "height" : "width"));
1708 return nullptr;
1709 case UndecodableSourceImageStatus:
1710 exceptionState.throwDOMException(InvalidStateError, "Source image is in the 'broken' state.");
1711 return nullptr;
1712 case InvalidSourceImageStatus:
1713 imageForRendering = Image::nullImage();
1714 break;
1715 case IncompleteSourceImageStatus:
1716 return nullptr;
1717 default:
1718 case ExternalSourceImageStatus: // should not happen when mode is CopySourceImageIfVolatile
1719 ASSERT_NOT_REACHED();
1720 return nullptr;
1721 }
1722 ASSERT(imageForRendering);
1723
1724 bool originClean = !wouldTaintOrigin(imageSource);
1725
1726 return CanvasPattern::create(imageForRendering.release(), repeatMode, originClean);
1727 }
1728
computeDirtyRect(const FloatRect & localRect,FloatRect * dirtyRect)1729 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, FloatRect* dirtyRect)
1730 {
1731 FloatRect clipBounds;
1732 if (!drawingContext()->getTransformedClipBounds(&clipBounds))
1733 return false;
1734 return computeDirtyRect(localRect, clipBounds, dirtyRect);
1735 }
1736
computeDirtyRect(const FloatRect & localRect,const FloatRect & transformedClipBounds,FloatRect * dirtyRect)1737 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, const FloatRect& transformedClipBounds, FloatRect* dirtyRect)
1738 {
1739 FloatRect canvasRect = state().m_transform.mapRect(localRect);
1740
1741 if (alphaChannel(state().m_shadowColor)) {
1742 FloatRect shadowRect(canvasRect);
1743 shadowRect.move(state().m_shadowOffset);
1744 shadowRect.inflate(state().m_shadowBlur);
1745 canvasRect.unite(shadowRect);
1746 }
1747
1748 canvasRect.intersect(transformedClipBounds);
1749 if (canvasRect.isEmpty())
1750 return false;
1751
1752 if (dirtyRect)
1753 *dirtyRect = canvasRect;
1754
1755 return true;
1756 }
1757
didDraw(const FloatRect & dirtyRect)1758 void CanvasRenderingContext2D::didDraw(const FloatRect& dirtyRect)
1759 {
1760 if (dirtyRect.isEmpty())
1761 return;
1762
1763 canvas()->didDraw(dirtyRect);
1764 }
1765
drawingContext() const1766 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1767 {
1768 if (isContextLost())
1769 return 0;
1770 return canvas()->drawingContext();
1771 }
1772
createEmptyImageData(const IntSize & size)1773 static PassRefPtrWillBeRawPtr<ImageData> createEmptyImageData(const IntSize& size)
1774 {
1775 if (RefPtrWillBeRawPtr<ImageData> data = ImageData::create(size)) {
1776 data->data()->zeroFill();
1777 return data.release();
1778 }
1779
1780 return nullptr;
1781 }
1782
createImageData(PassRefPtrWillBeRawPtr<ImageData> imageData) const1783 PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtrWillBeRawPtr<ImageData> imageData) const
1784 {
1785 return createEmptyImageData(imageData->size());
1786 }
1787
createImageData(float sw,float sh,ExceptionState & exceptionState) const1788 PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionState& exceptionState) const
1789 {
1790 if (!sw || !sh) {
1791 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
1792 return nullptr;
1793 }
1794
1795 FloatSize logicalSize(fabs(sw), fabs(sh));
1796 if (!logicalSize.isExpressibleAsIntSize())
1797 return nullptr;
1798
1799 IntSize size = expandedIntSize(logicalSize);
1800 if (size.width() < 1)
1801 size.setWidth(1);
1802 if (size.height() < 1)
1803 size.setHeight(1);
1804
1805 return createEmptyImageData(size);
1806 }
1807
getImageData(float sx,float sy,float sw,float sh,ExceptionState & exceptionState) const1808 PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const
1809 {
1810 if (!canvas()->originClean())
1811 exceptionState.throwSecurityError("The canvas has been tainted by cross-origin data.");
1812 else if (!sw || !sh)
1813 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
1814
1815 if (exceptionState.hadException())
1816 return nullptr;
1817
1818 if (sw < 0) {
1819 sx += sw;
1820 sw = -sw;
1821 }
1822 if (sh < 0) {
1823 sy += sh;
1824 sh = -sh;
1825 }
1826
1827 FloatRect logicalRect(sx, sy, sw, sh);
1828 if (logicalRect.width() < 1)
1829 logicalRect.setWidth(1);
1830 if (logicalRect.height() < 1)
1831 logicalRect.setHeight(1);
1832 if (!logicalRect.isExpressibleAsIntRect())
1833 return nullptr;
1834
1835 IntRect imageDataRect = enclosingIntRect(logicalRect);
1836 ImageBuffer* buffer = canvas()->buffer();
1837 if (!buffer || isContextLost())
1838 return createEmptyImageData(imageDataRect.size());
1839
1840 RefPtr<Uint8ClampedArray> byteArray = buffer->getImageData(Unmultiplied, imageDataRect);
1841 if (!byteArray)
1842 return nullptr;
1843
1844 return ImageData::create(imageDataRect.size(), byteArray.release());
1845 }
1846
putImageData(ImageData * data,float dx,float dy)1847 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy)
1848 {
1849 putImageData(data, dx, dy, 0, 0, data->width(), data->height());
1850 }
1851
putImageData(ImageData * data,float dx,float dy,float dirtyX,float dirtyY,float dirtyWidth,float dirtyHeight)1852 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
1853 {
1854 ImageBuffer* buffer = canvas()->buffer();
1855 if (!buffer)
1856 return;
1857
1858 if (dirtyWidth < 0) {
1859 dirtyX += dirtyWidth;
1860 dirtyWidth = -dirtyWidth;
1861 }
1862
1863 if (dirtyHeight < 0) {
1864 dirtyY += dirtyHeight;
1865 dirtyHeight = -dirtyHeight;
1866 }
1867
1868 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1869 clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1870 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1871 IntRect destRect = enclosingIntRect(clipRect);
1872 destRect.move(destOffset);
1873 destRect.intersect(IntRect(IntPoint(), buffer->size()));
1874 if (destRect.isEmpty())
1875 return;
1876 IntRect sourceRect(destRect);
1877 sourceRect.move(-destOffset);
1878
1879 buffer->putByteArray(Unmultiplied, data->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset));
1880
1881 didDraw(destRect);
1882 }
1883
font() const1884 String CanvasRenderingContext2D::font() const
1885 {
1886 if (!state().m_realizedFont)
1887 return defaultFont;
1888
1889 StringBuilder serializedFont;
1890 const FontDescription& fontDescription = state().m_font.fontDescription();
1891
1892 if (fontDescription.style() == FontStyleItalic)
1893 serializedFont.appendLiteral("italic ");
1894 if (fontDescription.weight() == FontWeightBold)
1895 serializedFont.appendLiteral("bold ");
1896 if (fontDescription.variant() == FontVariantSmallCaps)
1897 serializedFont.appendLiteral("small-caps ");
1898
1899 serializedFont.appendNumber(fontDescription.computedPixelSize());
1900 serializedFont.appendLiteral("px");
1901
1902 const FontFamily& firstFontFamily = fontDescription.family();
1903 for (const FontFamily* fontFamily = &firstFontFamily; fontFamily; fontFamily = fontFamily->next()) {
1904 if (fontFamily != &firstFontFamily)
1905 serializedFont.append(',');
1906
1907 // FIXME: We should append family directly to serializedFont rather than building a temporary string.
1908 String family = fontFamily->family();
1909 if (family.startsWith("-webkit-"))
1910 family = family.substring(8);
1911 if (family.contains(' '))
1912 family = "\"" + family + "\"";
1913
1914 serializedFont.append(' ');
1915 serializedFont.append(family);
1916 }
1917
1918 return serializedFont.toString();
1919 }
1920
setFont(const String & newFont)1921 void CanvasRenderingContext2D::setFont(const String& newFont)
1922 {
1923 // The style resolution required for rendering text is not available in frame-less documents.
1924 if (!canvas()->document().frame())
1925 return;
1926
1927 MutableStylePropertyMap::iterator i = m_fetchedFonts.find(newFont);
1928 RefPtrWillBeRawPtr<MutableStylePropertySet> parsedStyle = i != m_fetchedFonts.end() ? i->value : nullptr;
1929
1930 if (!parsedStyle) {
1931 parsedStyle = MutableStylePropertySet::create();
1932 CSSParserMode mode = m_usesCSSCompatibilityParseMode ? HTMLQuirksMode : HTMLStandardMode;
1933 CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, newFont, true, mode, 0);
1934 m_fetchedFonts.add(newFont, parsedStyle);
1935 }
1936 if (parsedStyle->isEmpty())
1937 return;
1938
1939 String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
1940
1941 // According to http://lists.w3.org/Archives/Public/public-html/2009Jul/0947.html,
1942 // the "inherit" and "initial" values must be ignored.
1943 if (fontValue == "inherit" || fontValue == "initial")
1944 return;
1945
1946 // The parse succeeded.
1947 String newFontSafeCopy(newFont); // Create a string copy since newFont can be deleted inside realizeSaves.
1948 realizeSaves(0);
1949 modifiableState().m_unparsedFont = newFontSafeCopy;
1950
1951 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
1952 // relative to the canvas.
1953 RefPtr<RenderStyle> newStyle = RenderStyle::create();
1954 canvas()->document().updateRenderTreeIfNeeded();
1955 if (RenderStyle* computedStyle = canvas()->computedStyle()) {
1956 FontDescription elementFontDescription(computedStyle->fontDescription());
1957 // Reset the computed size to avoid inheriting the zoom factor from the <canvas> element.
1958 elementFontDescription.setComputedSize(elementFontDescription.specifiedSize());
1959 newStyle->setFontDescription(elementFontDescription);
1960 } else {
1961 FontFamily fontFamily;
1962 fontFamily.setFamily(defaultFontFamily);
1963
1964 FontDescription defaultFontDescription;
1965 defaultFontDescription.setFamily(fontFamily);
1966 defaultFontDescription.setSpecifiedSize(defaultFontSize);
1967 defaultFontDescription.setComputedSize(defaultFontSize);
1968
1969 newStyle->setFontDescription(defaultFontDescription);
1970 }
1971
1972 newStyle->font().update(newStyle->font().fontSelector());
1973
1974 // Now map the font property longhands into the style.
1975 CSSPropertyValue properties[] = {
1976 CSSPropertyValue(CSSPropertyFontFamily, *parsedStyle),
1977 CSSPropertyValue(CSSPropertyFontStyle, *parsedStyle),
1978 CSSPropertyValue(CSSPropertyFontVariant, *parsedStyle),
1979 CSSPropertyValue(CSSPropertyFontWeight, *parsedStyle),
1980 CSSPropertyValue(CSSPropertyFontSize, *parsedStyle),
1981 CSSPropertyValue(CSSPropertyLineHeight, *parsedStyle),
1982 };
1983
1984 StyleResolver& styleResolver = canvas()->document().ensureStyleResolver();
1985 styleResolver.applyPropertiesToStyle(properties, WTF_ARRAY_LENGTH(properties), newStyle.get());
1986
1987 #if !ENABLE(OILPAN)
1988 if (state().m_realizedFont)
1989 static_cast<CSSFontSelector*>(state().m_font.fontSelector())->unregisterForInvalidationCallbacks(&modifiableState());
1990 #endif
1991 modifiableState().m_font = newStyle->font();
1992 modifiableState().m_font.update(canvas()->document().styleEngine()->fontSelector());
1993 modifiableState().m_realizedFont = true;
1994 canvas()->document().styleEngine()->fontSelector()->registerForInvalidationCallbacks(&modifiableState());
1995 }
1996
textAlign() const1997 String CanvasRenderingContext2D::textAlign() const
1998 {
1999 return textAlignName(state().m_textAlign);
2000 }
2001
setTextAlign(const String & s)2002 void CanvasRenderingContext2D::setTextAlign(const String& s)
2003 {
2004 TextAlign align;
2005 if (!parseTextAlign(s, align))
2006 return;
2007 if (state().m_textAlign == align)
2008 return;
2009 realizeSaves(0);
2010 modifiableState().m_textAlign = align;
2011 }
2012
textBaseline() const2013 String CanvasRenderingContext2D::textBaseline() const
2014 {
2015 return textBaselineName(state().m_textBaseline);
2016 }
2017
setTextBaseline(const String & s)2018 void CanvasRenderingContext2D::setTextBaseline(const String& s)
2019 {
2020 TextBaseline baseline;
2021 if (!parseTextBaseline(s, baseline))
2022 return;
2023 if (state().m_textBaseline == baseline)
2024 return;
2025 realizeSaves(0);
2026 modifiableState().m_textBaseline = baseline;
2027 }
2028
toTextDirection(Direction direction,RenderStyle ** computedStyle) const2029 inline TextDirection CanvasRenderingContext2D::toTextDirection(Direction direction, RenderStyle** computedStyle) const
2030 {
2031 RenderStyle* style = (computedStyle || direction == DirectionInherit) ? canvas()->computedStyle() : nullptr;
2032 if (computedStyle)
2033 *computedStyle = style;
2034 switch (direction) {
2035 case DirectionInherit:
2036 return style ? style->direction() : LTR;
2037 case DirectionRTL:
2038 return RTL;
2039 case DirectionLTR:
2040 return LTR;
2041 }
2042 ASSERT_NOT_REACHED();
2043 return LTR;
2044 }
2045
direction() const2046 String CanvasRenderingContext2D::direction() const
2047 {
2048 if (state().m_direction == DirectionInherit)
2049 canvas()->document().updateRenderTreeIfNeeded();
2050 return toTextDirection(state().m_direction) == RTL ? rtl : ltr;
2051 }
2052
setDirection(const String & directionString)2053 void CanvasRenderingContext2D::setDirection(const String& directionString)
2054 {
2055 Direction direction;
2056 if (directionString == inherit)
2057 direction = DirectionInherit;
2058 else if (directionString == rtl)
2059 direction = DirectionRTL;
2060 else if (directionString == ltr)
2061 direction = DirectionLTR;
2062 else
2063 return;
2064
2065 if (state().m_direction == direction)
2066 return;
2067
2068 realizeSaves(0);
2069 modifiableState().m_direction = direction;
2070 }
2071
fillText(const String & text,float x,float y)2072 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
2073 {
2074 drawTextInternal(text, x, y, true);
2075 }
2076
fillText(const String & text,float x,float y,float maxWidth)2077 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
2078 {
2079 drawTextInternal(text, x, y, true, maxWidth, true);
2080 }
2081
strokeText(const String & text,float x,float y)2082 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
2083 {
2084 drawTextInternal(text, x, y, false);
2085 }
2086
strokeText(const String & text,float x,float y,float maxWidth)2087 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
2088 {
2089 drawTextInternal(text, x, y, false, maxWidth, true);
2090 }
2091
measureText(const String & text)2092 PassRefPtrWillBeRawPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
2093 {
2094 RefPtrWillBeRawPtr<TextMetrics> metrics = TextMetrics::create();
2095
2096 // The style resolution required for rendering text is not available in frame-less documents.
2097 if (!canvas()->document().frame())
2098 return metrics.release();
2099
2100 FontCachePurgePreventer fontCachePurgePreventer;
2101 canvas()->document().updateRenderTreeIfNeeded();
2102 const Font& font = accessFont();
2103 const TextRun textRun(text, 0, 0, TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, LTR, false, true, true);
2104 FloatRect textBounds = font.selectionRectForText(textRun, FloatPoint(), font.fontDescription().computedSize(), 0, -1, true);
2105
2106 // x direction
2107 metrics->setWidth(font.width(textRun));
2108 metrics->setActualBoundingBoxLeft(-textBounds.x());
2109 metrics->setActualBoundingBoxRight(textBounds.maxX());
2110
2111 // y direction
2112 const FontMetrics& fontMetrics = font.fontMetrics();
2113 const float ascent = fontMetrics.floatAscent();
2114 const float descent = fontMetrics.floatDescent();
2115 const float baselineY = getFontBaseline(fontMetrics);
2116
2117 metrics->setFontBoundingBoxAscent(ascent - baselineY);
2118 metrics->setFontBoundingBoxDescent(descent + baselineY);
2119 metrics->setActualBoundingBoxAscent(-textBounds.y() - baselineY);
2120 metrics->setActualBoundingBoxDescent(textBounds.maxY() + baselineY);
2121
2122 // Note : top/bottom and ascend/descend are currently the same, so there's no difference
2123 // between the EM box's top and bottom and the font's ascend and descend
2124 metrics->setEmHeightAscent(0);
2125 metrics->setEmHeightDescent(0);
2126
2127 metrics->setHangingBaseline(-0.8f * ascent + baselineY);
2128 metrics->setAlphabeticBaseline(baselineY);
2129 metrics->setIdeographicBaseline(descent + baselineY);
2130 return metrics.release();
2131 }
2132
drawTextInternal(const String & text,float x,float y,bool fill,float maxWidth,bool useMaxWidth)2133 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth, bool useMaxWidth)
2134 {
2135 // The style resolution required for rendering text is not available in frame-less documents.
2136 if (!canvas()->document().frame())
2137 return;
2138
2139 // accessFont needs the style to be up to date, but updating style can cause script to run,
2140 // (e.g. due to autofocus) which can free the GraphicsContext, so update style before grabbing
2141 // the GraphicsContext.
2142 canvas()->document().updateRenderTreeIfNeeded();
2143
2144 GraphicsContext* c = drawingContext();
2145 if (!c)
2146 return;
2147 if (!state().m_invertibleCTM)
2148 return;
2149 if (!std::isfinite(x) | !std::isfinite(y))
2150 return;
2151 if (useMaxWidth && (!std::isfinite(maxWidth) || maxWidth <= 0))
2152 return;
2153
2154 // If gradient size is zero, then paint nothing.
2155 Gradient* gradient = c->strokeGradient();
2156 if (!fill && gradient && gradient->isZeroSize())
2157 return;
2158
2159 gradient = c->fillGradient();
2160 if (fill && gradient && gradient->isZeroSize())
2161 return;
2162
2163 FontCachePurgePreventer fontCachePurgePreventer;
2164
2165 const Font& font = accessFont();
2166 const FontMetrics& fontMetrics = font.fontMetrics();
2167
2168 // FIXME: Need to turn off font smoothing.
2169
2170 RenderStyle* computedStyle;
2171 TextDirection direction = toTextDirection(state().m_direction, &computedStyle);
2172 bool isRTL = direction == RTL;
2173 bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : false;
2174
2175 TextRun textRun(text, 0, 0, TextRun::AllowTrailingExpansion, direction, override, true, true);
2176 // Draw the item text at the correct point.
2177 FloatPoint location(x, y + getFontBaseline(fontMetrics));
2178 float fontWidth = font.width(textRun);
2179
2180 useMaxWidth = (useMaxWidth && maxWidth < fontWidth);
2181 float width = useMaxWidth ? maxWidth : fontWidth;
2182
2183 TextAlign align = state().m_textAlign;
2184 if (align == StartTextAlign)
2185 align = isRTL ? RightTextAlign : LeftTextAlign;
2186 else if (align == EndTextAlign)
2187 align = isRTL ? LeftTextAlign : RightTextAlign;
2188
2189 switch (align) {
2190 case CenterTextAlign:
2191 location.setX(location.x() - width / 2);
2192 break;
2193 case RightTextAlign:
2194 location.setX(location.x() - width);
2195 break;
2196 default:
2197 break;
2198 }
2199
2200 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
2201 TextRunPaintInfo textRunPaintInfo(textRun);
2202 textRunPaintInfo.bounds = FloatRect(location.x() - fontMetrics.height() / 2,
2203 location.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
2204 width + fontMetrics.height(),
2205 fontMetrics.lineSpacing());
2206 if (!fill)
2207 inflateStrokeRect(textRunPaintInfo.bounds);
2208
2209 c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
2210
2211 GraphicsContextStateSaver stateSaver(*c);
2212 if (useMaxWidth) {
2213 c->translate(location.x(), location.y());
2214 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work.
2215 c->scale((fontWidth > 0 ? (width / fontWidth) : 0), 1);
2216 location = FloatPoint();
2217 }
2218
2219 FloatRect clipBounds;
2220 if (!c->getTransformedClipBounds(&clipBounds)) {
2221 return;
2222 }
2223
2224 if (isFullCanvasCompositeMode(state().m_globalComposite)) {
2225 c->beginLayer(1, state().m_globalComposite);
2226 CompositeOperator previousOperator = c->compositeOperation();
2227 c->setCompositeOperation(CompositeSourceOver);
2228 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
2229 c->setCompositeOperation(previousOperator);
2230 c->endLayer();
2231 didDraw(clipBounds);
2232 } else if (state().m_globalComposite == CompositeCopy) {
2233 clearCanvas();
2234 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
2235 didDraw(clipBounds);
2236 } else {
2237 FloatRect dirtyRect;
2238 if (computeDirtyRect(textRunPaintInfo.bounds, clipBounds, &dirtyRect)) {
2239 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
2240 didDraw(dirtyRect);
2241 }
2242 }
2243 }
2244
inflateStrokeRect(FloatRect & rect) const2245 void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
2246 {
2247 // Fast approximation of the stroke's bounding rect.
2248 // This yields a slightly oversized rect but is very fast
2249 // compared to Path::strokeBoundingRect().
2250 static const float root2 = sqrtf(2);
2251 float delta = state().m_lineWidth / 2;
2252 if (state().m_lineJoin == MiterJoin)
2253 delta *= state().m_miterLimit;
2254 else if (state().m_lineCap == SquareCap)
2255 delta *= root2;
2256
2257 rect.inflate(delta);
2258 }
2259
accessFont()2260 const Font& CanvasRenderingContext2D::accessFont()
2261 {
2262 // This needs style to be up to date, but can't assert so because drawTextInternal
2263 // can invalidate style before this is called (e.g. drawingContext invalidates style).
2264 if (!state().m_realizedFont)
2265 setFont(state().m_unparsedFont);
2266 return state().m_font;
2267 }
2268
getFontBaseline(const FontMetrics & fontMetrics) const2269 int CanvasRenderingContext2D::getFontBaseline(const FontMetrics& fontMetrics) const
2270 {
2271 switch (state().m_textBaseline) {
2272 case TopTextBaseline:
2273 return fontMetrics.ascent();
2274 case HangingTextBaseline:
2275 // According to http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
2276 // "FOP (Formatting Objects Processor) puts the hanging baseline at 80% of the ascender height"
2277 return (fontMetrics.ascent() * 4) / 5;
2278 case BottomTextBaseline:
2279 case IdeographicTextBaseline:
2280 return -fontMetrics.descent();
2281 case MiddleTextBaseline:
2282 return -fontMetrics.descent() + fontMetrics.height() / 2;
2283 case AlphabeticTextBaseline:
2284 default:
2285 // Do nothing.
2286 break;
2287 }
2288 return 0;
2289 }
2290
setIsHidden(bool hidden)2291 void CanvasRenderingContext2D::setIsHidden(bool hidden)
2292 {
2293 ImageBuffer* buffer = canvas()->buffer();
2294 if (buffer)
2295 buffer->setIsHidden(hidden);
2296 }
2297
platformLayer() const2298 blink::WebLayer* CanvasRenderingContext2D::platformLayer() const
2299 {
2300 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0;
2301 }
2302
imageSmoothingEnabled() const2303 bool CanvasRenderingContext2D::imageSmoothingEnabled() const
2304 {
2305 return state().m_imageSmoothingEnabled;
2306 }
2307
setImageSmoothingEnabled(bool enabled)2308 void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled)
2309 {
2310 if (enabled == state().m_imageSmoothingEnabled)
2311 return;
2312
2313 GraphicsContext* c = drawingContext();
2314 realizeSaves(c);
2315 modifiableState().m_imageSmoothingEnabled = enabled;
2316 if (c)
2317 c->setImageInterpolationQuality(enabled ? CanvasDefaultInterpolationQuality : InterpolationNone);
2318 }
2319
getContextAttributes() const2320 PassRefPtrWillBeRawPtr<Canvas2DContextAttributes> CanvasRenderingContext2D::getContextAttributes() const
2321 {
2322 RefPtrWillBeRawPtr<Canvas2DContextAttributes> attributes = Canvas2DContextAttributes::create();
2323 attributes->setAlpha(m_hasAlpha);
2324 return attributes.release();
2325 }
2326
drawFocusIfNeeded(Element * element)2327 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element)
2328 {
2329 drawFocusIfNeededInternal(m_path, element);
2330 }
2331
drawFocusIfNeeded(Path2D * path2d,Element * element)2332 void CanvasRenderingContext2D::drawFocusIfNeeded(Path2D* path2d, Element* element)
2333 {
2334 drawFocusIfNeededInternal(path2d->path(), element);
2335 }
2336
drawFocusIfNeededInternal(const Path & path,Element * element)2337 void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Element* element)
2338 {
2339 if (!focusRingCallIsValid(path, element))
2340 return;
2341
2342 // Note: we need to check document->focusedElement() rather than just calling
2343 // element->focused(), because element->focused() isn't updated until after
2344 // focus events fire.
2345 if (element->document().focusedElement() == element)
2346 drawFocusRing(path);
2347 }
2348
focusRingCallIsValid(const Path & path,Element * element)2349 bool CanvasRenderingContext2D::focusRingCallIsValid(const Path& path, Element* element)
2350 {
2351 ASSERT(element);
2352 if (!state().m_invertibleCTM)
2353 return false;
2354 if (path.isEmpty())
2355 return false;
2356 if (!element->isDescendantOf(canvas()))
2357 return false;
2358
2359 return true;
2360 }
2361
drawFocusRing(const Path & path)2362 void CanvasRenderingContext2D::drawFocusRing(const Path& path)
2363 {
2364 GraphicsContext* c = drawingContext();
2365 if (!c)
2366 return;
2367
2368 // These should match the style defined in html.css.
2369 Color focusRingColor = RenderTheme::theme().focusRingColor();
2370 const int focusRingWidth = 5;
2371 const int focusRingOutline = 0;
2372
2373 // We need to add focusRingWidth to dirtyRect.
2374 StrokeData strokeData;
2375 strokeData.setThickness(focusRingWidth);
2376
2377 FloatRect dirtyRect;
2378 if (!computeDirtyRect(path.strokeBoundingRect(strokeData), &dirtyRect))
2379 return;
2380
2381 c->save();
2382 c->setAlphaAsFloat(1.0);
2383 c->clearShadow();
2384 c->setCompositeOperation(CompositeSourceOver, blink::WebBlendModeNormal);
2385 c->drawFocusRing(path, focusRingWidth, focusRingOutline, focusRingColor);
2386 c->restore();
2387 validateStateStack();
2388 didDraw(dirtyRect);
2389 }
2390
addHitRegion(const HitRegionOptions & options,ExceptionState & exceptionState)2391 void CanvasRenderingContext2D::addHitRegion(const HitRegionOptions& options, ExceptionState& exceptionState)
2392 {
2393 HitRegionOptionsInternal passOptions;
2394 passOptions.id = options.id();
2395 passOptions.control = options.control();
2396 if (passOptions.id.isEmpty() && !passOptions.control) {
2397 exceptionState.throwDOMException(NotSupportedError, "Both id and control are null.");
2398 return;
2399 }
2400
2401 Path hitRegionPath = options.hasPath() ? options.path()->path() : m_path;
2402
2403 FloatRect clipBounds;
2404 GraphicsContext* context = drawingContext();
2405
2406 if (hitRegionPath.isEmpty() || !context || !state().m_invertibleCTM
2407 || !context->getTransformedClipBounds(&clipBounds)) {
2408 exceptionState.throwDOMException(NotSupportedError, "The specified path has no pixels.");
2409 return;
2410 }
2411
2412 hitRegionPath.transform(state().m_transform);
2413
2414 if (hasClip()) {
2415 // FIXME: The hit regions should take clipping region into account.
2416 // However, we have no way to get the region from canvas state stack by now.
2417 // See http://crbug.com/387057
2418 exceptionState.throwDOMException(NotSupportedError, "The specified path has no pixels.");
2419 return;
2420 }
2421
2422 passOptions.path = hitRegionPath;
2423
2424 if (options.fillRule() != "evenodd")
2425 passOptions.fillRule = RULE_NONZERO;
2426 else
2427 passOptions.fillRule = RULE_EVENODD;
2428
2429 addHitRegionInternal(passOptions, exceptionState);
2430 }
2431
addHitRegionInternal(const HitRegionOptionsInternal & options,ExceptionState & exceptionState)2432 void CanvasRenderingContext2D::addHitRegionInternal(const HitRegionOptionsInternal& options, ExceptionState& exceptionState)
2433 {
2434 if (!m_hitRegionManager)
2435 m_hitRegionManager = HitRegionManager::create();
2436
2437 // Remove previous region (with id or control)
2438 m_hitRegionManager->removeHitRegionById(options.id);
2439 m_hitRegionManager->removeHitRegionByControl(options.control.get());
2440
2441 RefPtrWillBeRawPtr<HitRegion> hitRegion = HitRegion::create(options);
2442 hitRegion->updateAccessibility(canvas());
2443 m_hitRegionManager->addHitRegion(hitRegion.release());
2444 }
2445
removeHitRegion(const String & id)2446 void CanvasRenderingContext2D::removeHitRegion(const String& id)
2447 {
2448 if (m_hitRegionManager)
2449 m_hitRegionManager->removeHitRegionById(id);
2450 }
2451
clearHitRegions()2452 void CanvasRenderingContext2D::clearHitRegions()
2453 {
2454 if (m_hitRegionManager)
2455 m_hitRegionManager->removeAllHitRegions();
2456 }
2457
hitRegionAtPoint(const LayoutPoint & point)2458 HitRegion* CanvasRenderingContext2D::hitRegionAtPoint(const LayoutPoint& point)
2459 {
2460 if (m_hitRegionManager)
2461 return m_hitRegionManager->getHitRegionAtPoint(point);
2462
2463 return 0;
2464 }
2465
hitRegionsCount() const2466 unsigned CanvasRenderingContext2D::hitRegionsCount() const
2467 {
2468 if (m_hitRegionManager)
2469 return m_hitRegionManager->getHitRegionsCount();
2470
2471 return 0;
2472 }
2473
2474 } // namespace blink
2475