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.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
34 #include "core/html/canvas/CanvasRenderingContext2D.h"
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"
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;
85 static bool contextLostRestoredEventsEnabled()
87 return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled();
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)
102 m_stateStack.append(adoptPtrWillBeNoop(new State()));
105 void CanvasRenderingContext2D::unwindStateStack()
107 if (size_t stackSize = m_stateStack.size()) {
108 if (GraphicsContext* context = canvas()->existingDrawingContext()) {
115 CanvasRenderingContext2D::~CanvasRenderingContext2D()
119 void CanvasRenderingContext2D::validateStateStack()
122 GraphicsContext* context = canvas()->existingDrawingContext();
123 if (context && !context->contextDisabled())
124 ASSERT(context->saveCount() == m_stateStack.size());
128 bool CanvasRenderingContext2D::isAccelerated() const
130 if (!canvas()->hasImageBuffer())
132 GraphicsContext* context = drawingContext();
133 return context && context->isAccelerated();
136 bool CanvasRenderingContext2D::isContextLost() const
138 return m_isContextLost;
141 void CanvasRenderingContext2D::loseContext()
145 m_isContextLost = true;
146 m_dispatchContextLostEventTimer.startOneShot(0, FROM_HERE);
149 void CanvasRenderingContext2D::restoreContext()
151 if (!m_contextRestorable)
153 // This code path is for restoring from an eviction
154 // Restoring from surface failure is handled internally
155 ASSERT(m_isContextLost && !canvas()->hasImageBuffer());
157 if (canvas()->buffer()) {
158 if (contextLostRestoredEventsEnabled()) {
159 m_dispatchContextRestoredEventTimer.startOneShot(0, FROM_HERE);
161 // legacy synchronous context restoration.
163 m_isContextLost = false;
168 void CanvasRenderingContext2D::trace(Visitor* visitor)
171 visitor->trace(m_stateStack);
172 visitor->trace(m_fetchedFonts);
173 visitor->trace(m_hitRegionManager);
175 CanvasRenderingContext::trace(visitor);
178 void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingContext2D>*)
180 if (contextLostRestoredEventsEnabled()) {
181 RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames::contextlost);
182 canvas()->dispatchEvent(event);
183 if (event->defaultPrevented()) {
184 m_contextRestorable = false;
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);
196 void CanvasRenderingContext2D::tryRestoreContextEvent(Timer<CanvasRenderingContext2D>* timer)
198 if (!m_isContextLost) {
199 // Canvas was already restored (possibly thanks to a resize), so stop trying.
200 m_tryRestoreContextEventTimer.stop();
203 if (canvas()->hasImageBuffer() && canvas()->buffer()->restoreSurface()) {
204 m_tryRestoreContextEventTimer.stop();
205 dispatchContextRestoredEvent(0);
208 if (++m_tryRestoreContextAttemptCount > MaxTryRestoreContextAttempts)
209 canvas()->discardImageBuffer();
211 if (!canvas()->hasImageBuffer()) {
212 // final attempt: allocate a brand new image buffer instead of restoring
214 if (canvas()->buffer())
215 dispatchContextRestoredEvent(0);
219 void CanvasRenderingContext2D::dispatchContextRestoredEvent(Timer<CanvasRenderingContext2D>*)
221 if (!m_isContextLost)
224 m_isContextLost = false;
225 if (contextLostRestoredEventsEnabled()) {
226 RefPtrWillBeRawPtr<Event> event(Event::create(EventTypeNames::contextrestored));
227 canvas()->dispatchEvent(event);
231 void CanvasRenderingContext2D::reset()
233 validateStateStack();
235 m_stateStack.resize(1);
236 m_stateStack.first() = adoptPtrWillBeNoop(new State());
238 validateStateStack();
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()!
245 CanvasRenderingContext2D::State::State()
246 : m_unrealizedSaveCount(0)
247 , m_strokeStyle(CanvasStyle::createFromRGBA(Color::black))
248 , m_fillStyle(CanvasStyle::createFromRGBA(Color::black))
251 , m_lineJoin(MiterJoin)
254 , m_shadowColor(Color::transparent)
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)
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)
300 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalidationCallbacks(this);
303 CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other)
310 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInvalidationCallbacks(this);
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;
340 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalidationCallbacks(this);
345 CanvasRenderingContext2D::State::~State()
349 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInvalidationCallbacks(this);
353 void CanvasRenderingContext2D::State::fontsNeedUpdate(CSSFontSelector* fontSelector)
355 ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector());
356 ASSERT(m_realizedFont);
358 m_font.update(fontSelector);
361 void CanvasRenderingContext2D::State::trace(Visitor* visitor)
363 visitor->trace(m_strokeStyle);
364 visitor->trace(m_fillStyle);
365 CSSFontSelectorClient::trace(visitor);
368 void CanvasRenderingContext2D::realizeSaves(GraphicsContext* context)
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;
383 context = drawingContext();
386 validateStateStack();
390 void CanvasRenderingContext2D::restore()
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;
398 ASSERT(m_stateStack.size() >= 1);
399 if (m_stateStack.size() <= 1)
401 m_path.transform(state().m_transform);
402 m_stateStack.removeLast();
403 m_path.transform(state().m_transform.inverse());
404 GraphicsContext* c = drawingContext();
407 validateStateStack();
410 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
412 return state().m_strokeStyle.get();
415 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtrWillBeRawPtr<CanvasStyle> prpStyle)
417 RefPtrWillBeRawPtr<CanvasStyle> style = prpStyle;
422 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style))
425 if (style->isCurrentColor()) {
426 if (style->hasOverrideAlpha())
427 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha()));
429 style = CanvasStyle::createFromRGBA(currentColor(canvas()));
430 } else if (canvas()->originClean() && style->canvasPattern() && !style->canvasPattern()->originClean()) {
431 canvas()->setOriginTainted();
434 GraphicsContext* c = drawingContext();
436 modifiableState().m_strokeStyle = style.release();
439 state().m_strokeStyle->applyStrokeColor(c);
440 modifiableState().m_unparsedStrokeColor = String();
443 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
445 return state().m_fillStyle.get();
448 void CanvasRenderingContext2D::setFillStyle(PassRefPtrWillBeRawPtr<CanvasStyle> prpStyle)
450 RefPtrWillBeRawPtr<CanvasStyle> style = prpStyle;
455 if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style))
458 if (style->isCurrentColor()) {
459 if (style->hasOverrideAlpha())
460 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha()));
462 style = CanvasStyle::createFromRGBA(currentColor(canvas()));
463 } else if (canvas()->originClean() && style->canvasPattern() && !style->canvasPattern()->originClean()) {
464 canvas()->setOriginTainted();
467 GraphicsContext* c = drawingContext();
469 modifiableState().m_fillStyle = style.release();
472 state().m_fillStyle->applyFillColor(c);
473 modifiableState().m_unparsedFillColor = String();
476 float CanvasRenderingContext2D::lineWidth() const
478 return state().m_lineWidth;
481 void CanvasRenderingContext2D::setLineWidth(float width)
483 if (!(std::isfinite(width) && width > 0))
485 if (state().m_lineWidth == width)
487 GraphicsContext* c = drawingContext();
489 modifiableState().m_lineWidth = width;
492 c->setStrokeThickness(width);
495 String CanvasRenderingContext2D::lineCap() const
497 return lineCapName(state().m_lineCap);
500 void CanvasRenderingContext2D::setLineCap(const String& s)
503 if (!parseLineCap(s, cap))
505 if (state().m_lineCap == cap)
507 GraphicsContext* c = drawingContext();
509 modifiableState().m_lineCap = cap;
515 String CanvasRenderingContext2D::lineJoin() const
517 return lineJoinName(state().m_lineJoin);
520 void CanvasRenderingContext2D::setLineJoin(const String& s)
523 if (!parseLineJoin(s, join))
525 if (state().m_lineJoin == join)
527 GraphicsContext* c = drawingContext();
529 modifiableState().m_lineJoin = join;
532 c->setLineJoin(join);
535 float CanvasRenderingContext2D::miterLimit() const
537 return state().m_miterLimit;
540 void CanvasRenderingContext2D::setMiterLimit(float limit)
542 if (!(std::isfinite(limit) && limit > 0))
544 if (state().m_miterLimit == limit)
546 GraphicsContext* c = drawingContext();
548 modifiableState().m_miterLimit = limit;
551 c->setMiterLimit(limit);
554 float CanvasRenderingContext2D::shadowOffsetX() const
556 return state().m_shadowOffset.width();
559 void CanvasRenderingContext2D::setShadowOffsetX(float x)
561 if (!std::isfinite(x))
563 if (state().m_shadowOffset.width() == x)
566 modifiableState().m_shadowOffset.setWidth(x);
570 float CanvasRenderingContext2D::shadowOffsetY() const
572 return state().m_shadowOffset.height();
575 void CanvasRenderingContext2D::setShadowOffsetY(float y)
577 if (!std::isfinite(y))
579 if (state().m_shadowOffset.height() == y)
582 modifiableState().m_shadowOffset.setHeight(y);
586 float CanvasRenderingContext2D::shadowBlur() const
588 return state().m_shadowBlur;
591 void CanvasRenderingContext2D::setShadowBlur(float blur)
593 if (!(std::isfinite(blur) && blur >= 0))
595 if (state().m_shadowBlur == blur)
598 modifiableState().m_shadowBlur = blur;
602 String CanvasRenderingContext2D::shadowColor() const
604 return Color(state().m_shadowColor).serialized();
607 void CanvasRenderingContext2D::setShadowColor(const String& color)
610 if (!parseColorOrCurrentColor(rgba, color, canvas()))
612 if (state().m_shadowColor == rgba)
615 modifiableState().m_shadowColor = rgba;
619 const Vector<float>& CanvasRenderingContext2D::getLineDash() const
621 return state().m_lineDash;
624 static bool lineDashSequenceIsValid(const Vector<float>& dash)
626 for (size_t i = 0; i < dash.size(); i++) {
627 if (!std::isfinite(dash[i]) || dash[i] < 0)
633 void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash)
635 if (!lineDashSequenceIsValid(dash))
639 modifiableState().m_lineDash = dash;
640 // Spec requires the concatenation of two copies the dash list when the
641 // number of elements is odd
643 modifiableState().m_lineDash.appendVector(dash);
648 float CanvasRenderingContext2D::lineDashOffset() const
650 return state().m_lineDashOffset;
653 void CanvasRenderingContext2D::setLineDashOffset(float offset)
655 if (!std::isfinite(offset) || state().m_lineDashOffset == offset)
659 modifiableState().m_lineDashOffset = offset;
663 void CanvasRenderingContext2D::applyLineDash() const
665 GraphicsContext* c = drawingContext();
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);
674 float CanvasRenderingContext2D::globalAlpha() const
676 return state().m_globalAlpha;
679 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
681 if (!(alpha >= 0 && alpha <= 1))
683 if (state().m_globalAlpha == alpha)
685 GraphicsContext* c = drawingContext();
687 modifiableState().m_globalAlpha = alpha;
690 c->setAlphaAsFloat(alpha);
693 String CanvasRenderingContext2D::globalCompositeOperation() const
695 return compositeOperatorName(state().m_globalComposite, state().m_globalBlend);
698 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
700 CompositeOperator op = CompositeSourceOver;
701 blink::WebBlendMode blendMode = blink::WebBlendModeNormal;
702 if (!parseCompositeAndBlendOperator(operation, op, blendMode))
704 if ((state().m_globalComposite == op) && (state().m_globalBlend == blendMode))
706 GraphicsContext* c = drawingContext();
708 modifiableState().m_globalComposite = op;
709 modifiableState().m_globalBlend = blendMode;
712 c->setCompositeOperation(op, blendMode);
715 void CanvasRenderingContext2D::setCurrentTransform(PassRefPtr<SVGMatrixTearOff> passMatrixTearOff)
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());
722 void CanvasRenderingContext2D::scale(float sx, float sy)
724 GraphicsContext* c = drawingContext();
727 if (!state().m_invertibleCTM)
730 if (!std::isfinite(sx) | !std::isfinite(sy))
733 AffineTransform newTransform = state().m_transform;
734 newTransform.scaleNonUniform(sx, sy);
735 if (state().m_transform == newTransform)
740 if (!newTransform.isInvertible()) {
741 modifiableState().m_invertibleCTM = false;
745 modifiableState().m_transform = newTransform;
747 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
750 void CanvasRenderingContext2D::rotate(float angleInRadians)
752 GraphicsContext* c = drawingContext();
755 if (!state().m_invertibleCTM)
758 if (!std::isfinite(angleInRadians))
761 AffineTransform newTransform = state().m_transform;
762 newTransform.rotateRadians(angleInRadians);
763 if (state().m_transform == newTransform)
768 if (!newTransform.isInvertible()) {
769 modifiableState().m_invertibleCTM = false;
773 modifiableState().m_transform = newTransform;
774 c->rotate(angleInRadians);
775 m_path.transform(AffineTransform().rotateRadians(-angleInRadians));
778 void CanvasRenderingContext2D::translate(float tx, float ty)
780 GraphicsContext* c = drawingContext();
783 if (!state().m_invertibleCTM)
786 if (!std::isfinite(tx) | !std::isfinite(ty))
789 AffineTransform newTransform = state().m_transform;
790 newTransform.translate(tx, ty);
791 if (state().m_transform == newTransform)
796 if (!newTransform.isInvertible()) {
797 modifiableState().m_invertibleCTM = false;
801 modifiableState().m_transform = newTransform;
802 c->translate(tx, ty);
803 m_path.transform(AffineTransform().translate(-tx, -ty));
806 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
808 GraphicsContext* c = drawingContext();
811 if (!state().m_invertibleCTM)
814 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
817 AffineTransform transform(m11, m12, m21, m22, dx, dy);
818 AffineTransform newTransform = state().m_transform * transform;
819 if (state().m_transform == newTransform)
824 modifiableState().m_transform = newTransform;
825 if (!newTransform.isInvertible()) {
826 modifiableState().m_invertibleCTM = false;
830 c->concatCTM(transform);
831 m_path.transform(transform.inverse());
834 void CanvasRenderingContext2D::resetTransform()
836 GraphicsContext* c = drawingContext();
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)
848 // resetTransform() resolves the non-invertible CTM state.
849 modifiableState().m_transform.makeIdentity();
850 modifiableState().m_invertibleCTM = true;
851 c->setCTM(canvas()->baseTransform());
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.
859 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
861 GraphicsContext* c = drawingContext();
865 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
869 transform(m11, m12, m21, m22, dx, dy);
872 void CanvasRenderingContext2D::setStrokeColor(const String& color)
874 if (color == state().m_unparsedStrokeColor)
877 setStrokeStyle(CanvasStyle::createFromString(color));
878 modifiableState().m_unparsedStrokeColor = color;
881 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
883 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
885 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
888 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
890 setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
893 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
895 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
897 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
900 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
902 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b, a))
904 setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
907 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
909 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a))
911 setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
914 void CanvasRenderingContext2D::setFillColor(const String& color)
916 if (color == state().m_unparsedFillColor)
919 setFillStyle(CanvasStyle::createFromString(color));
920 modifiableState().m_unparsedFillColor = color;
923 void CanvasRenderingContext2D::setFillColor(float grayLevel)
925 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
927 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
930 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
932 setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
935 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
937 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
939 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
942 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
944 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a))
946 setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
949 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
951 if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k, a))
953 setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
956 void CanvasRenderingContext2D::beginPath()
961 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
963 if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height))
966 if (!width && !height)
982 static bool isFullCanvasCompositeMode(CompositeOperator op)
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;
990 static WindRule parseWinding(const String& windingRuleString)
992 if (windingRuleString == "nonzero")
994 if (windingRuleString == "evenodd")
997 ASSERT_NOT_REACHED();
1001 void CanvasRenderingContext2D::fillInternal(const Path& path, const String& windingRuleString)
1003 if (path.isEmpty()) {
1006 GraphicsContext* c = drawingContext();
1010 if (!state().m_invertibleCTM) {
1013 FloatRect clipBounds;
1014 if (!c->getTransformedClipBounds(&clipBounds)) {
1018 // If gradient size is zero, then paint nothing.
1019 Gradient* gradient = c->fillGradient();
1020 if (gradient && gradient->isZeroSize()) {
1024 WindRule windRule = c->fillRule();
1025 c->setFillRule(parseWinding(windingRuleString));
1027 if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1028 fullCanvasCompositedFill(path);
1029 didDraw(clipBounds);
1030 } else if (state().m_globalComposite == CompositeCopy) {
1033 didDraw(clipBounds);
1035 FloatRect dirtyRect;
1036 if (computeDirtyRect(path.boundingRect(), clipBounds, &dirtyRect)) {
1042 c->setFillRule(windRule);
1045 void CanvasRenderingContext2D::fill(const String& windingRuleString)
1047 fillInternal(m_path, windingRuleString);
1050 void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleString)
1052 fillInternal(domPath->path(), windingRuleString);
1055 void CanvasRenderingContext2D::strokeInternal(const Path& path)
1057 if (path.isEmpty()) {
1060 GraphicsContext* c = drawingContext();
1064 if (!state().m_invertibleCTM) {
1067 FloatRect clipBounds;
1068 if (!c->getTransformedClipBounds(&clipBounds))
1071 // If gradient size is zero, then paint nothing.
1072 Gradient* gradient = c->strokeGradient();
1073 if (gradient && gradient->isZeroSize()) {
1077 if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1078 fullCanvasCompositedStroke(path);
1079 didDraw(clipBounds);
1080 } else if (state().m_globalComposite == CompositeCopy) {
1082 c->strokePath(path);
1083 didDraw(clipBounds);
1085 FloatRect bounds = path.boundingRect();
1086 inflateStrokeRect(bounds);
1087 FloatRect dirtyRect;
1088 if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) {
1089 c->strokePath(path);
1095 void CanvasRenderingContext2D::stroke()
1097 strokeInternal(m_path);
1100 void CanvasRenderingContext2D::stroke(Path2D* domPath)
1102 strokeInternal(domPath->path());
1105 void CanvasRenderingContext2D::clipInternal(const Path& path, const String& windingRuleString)
1107 GraphicsContext* c = drawingContext();
1111 if (!state().m_invertibleCTM) {
1116 c->canvasClip(path, parseWinding(windingRuleString));
1117 modifiableState().m_hasClip = true;
1120 void CanvasRenderingContext2D::clip(const String& windingRuleString)
1122 clipInternal(m_path, windingRuleString);
1125 void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleString)
1127 clipInternal(domPath->path(), windingRuleString);
1130 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y, const String& windingRuleString)
1132 return isPointInPathInternal(m_path, x, y, windingRuleString);
1135 bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const float x, const float y, const String& windingRuleString)
1137 return isPointInPathInternal(domPath->path(), x, y, windingRuleString);
1140 bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, const float x, const float y, const String& windingRuleString)
1142 GraphicsContext* c = drawingContext();
1145 if (!state().m_invertibleCTM)
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()))
1154 return path.contains(transformedPoint, parseWinding(windingRuleString));
1157 bool CanvasRenderingContext2D::isPointInStroke(const float x, const float y)
1159 return isPointInStrokeInternal(m_path, x, y);
1162 bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const float x, const float y)
1164 return isPointInStrokeInternal(domPath->path(), x, y);
1167 bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, const float x, const float y)
1169 GraphicsContext* c = drawingContext();
1172 if (!state().m_invertibleCTM)
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()))
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);
1190 void CanvasRenderingContext2D::scrollPathIntoView()
1192 scrollPathIntoViewInternal(m_path);
1195 void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d)
1197 scrollPathIntoViewInternal(path2d->path());
1200 void CanvasRenderingContext2D::scrollPathIntoViewInternal(const Path& path)
1202 RenderObject* renderer = canvas()->renderer();
1203 RenderBox* renderBox = canvas()->renderBox();
1204 if (!renderer || !renderBox || !state().m_invertibleCTM || path.isEmpty())
1207 canvas()->document().updateLayoutIgnorePendingStylesheets();
1209 // Apply transformation and get the bounding rect
1210 Path transformedPath = path;
1211 transformedPath.transform(state().m_transform);
1212 FloatRect boundingRect = transformedPath.boundingRect();
1214 // Offset by the canvas rect
1215 LayoutRect pathRect(boundingRect);
1216 IntRect canvasRect = renderBox->absoluteContentBox();
1217 pathRect.move(canvasRect.x(), canvasRect.y());
1219 renderer->scrollRectToVisible(
1220 pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopAlways);
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
1226 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
1228 if (!validateRectForCanvas(x, y, width, height))
1230 GraphicsContext* context = drawingContext();
1233 if (!state().m_invertibleCTM)
1235 FloatRect rect(x, y, width, height);
1237 FloatRect dirtyRect;
1238 if (!computeDirtyRect(rect, &dirtyRect))
1242 if (shouldDrawShadows()) {
1245 context->clearShadow();
1247 if (state().m_globalAlpha != 1) {
1252 context->setAlphaAsFloat(1);
1254 if (state().m_globalComposite != CompositeSourceOver) {
1259 context->setCompositeOperation(CompositeSourceOver);
1261 context->clearRect(rect);
1262 if (m_hitRegionManager)
1263 m_hitRegionManager->removeHitRegionsInRect(rect, state().m_transform);
1267 validateStateStack();
1271 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
1273 if (!validateRectForCanvas(x, y, width, height))
1276 GraphicsContext* c = drawingContext();
1279 if (!state().m_invertibleCTM)
1281 FloatRect clipBounds;
1282 if (!c->getTransformedClipBounds(&clipBounds))
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())
1292 FloatRect rect(x, y, width, height);
1293 if (rectContainsTransformedRect(rect, clipBounds)) {
1295 didDraw(clipBounds);
1296 } else if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1297 fullCanvasCompositedFill(rect);
1298 didDraw(clipBounds);
1299 } else if (state().m_globalComposite == CompositeCopy) {
1302 didDraw(clipBounds);
1304 FloatRect dirtyRect;
1305 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) {
1312 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
1314 if (!validateRectForCanvas(x, y, width, height))
1317 if (!(state().m_lineWidth >= 0))
1320 GraphicsContext* c = drawingContext();
1323 if (!state().m_invertibleCTM)
1325 FloatRect clipBounds;
1326 if (!c->getTransformedClipBounds(&clipBounds))
1329 // If gradient size is zero, then paint nothing.
1330 Gradient* gradient = c->strokeGradient();
1331 if (gradient && gradient->isZeroSize())
1334 FloatRect rect(x, y, width, height);
1336 if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1337 fullCanvasCompositedStroke(rect);
1338 didDraw(clipBounds);
1339 } else if (state().m_globalComposite == CompositeCopy) {
1341 c->strokeRect(rect);
1342 didDraw(clipBounds);
1344 FloatRect boundingRect = rect;
1345 boundingRect.inflate(state().m_lineWidth / 2);
1346 FloatRect dirtyRect;
1347 if (computeDirtyRect(boundingRect, clipBounds, &dirtyRect)) {
1348 c->strokeRect(rect);
1354 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
1356 setShadow(FloatSize(width, height), blur, Color::transparent);
1359 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
1362 if (!parseColorOrCurrentColor(rgba, color, canvas()))
1364 setShadow(FloatSize(width, height), blur, rgba);
1367 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
1369 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1));
1372 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
1375 if (!parseColorOrCurrentColor(rgba, color, canvas()))
1377 setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(rgba, alpha));
1380 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
1382 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha));
1385 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
1387 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(r, g, b, a));
1390 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
1392 setShadow(FloatSize(width, height), blur, makeRGBAFromCMYKA(c, m, y, k, a));
1395 void CanvasRenderingContext2D::clearShadow()
1397 setShadow(FloatSize(), 0, Color::transparent);
1400 void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, RGBA32 color)
1402 if (state().m_shadowOffset == offset && state().m_shadowBlur == blur && state().m_shadowColor == color)
1404 bool wasDrawingShadows = shouldDrawShadows();
1406 modifiableState().m_shadowOffset = offset;
1407 modifiableState().m_shadowBlur = blur;
1408 modifiableState().m_shadowColor = color;
1409 if (!wasDrawingShadows && !shouldDrawShadows())
1414 void CanvasRenderingContext2D::applyShadow()
1416 GraphicsContext* c = drawingContext();
1420 if (shouldDrawShadows()) {
1421 c->setShadow(state().m_shadowOffset, state().m_shadowBlur, state().m_shadowColor,
1422 DrawLooperBuilder::ShadowIgnoresTransforms);
1428 bool CanvasRenderingContext2D::shouldDrawShadows() const
1430 return alphaChannel(state().m_shadowColor) && (state().m_shadowBlur || !state().m_shadowOffset.isZero());
1433 static inline FloatRect normalizeRect(const FloatRect& rect)
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()));
1441 static inline void clipRectsToImageRect(const FloatRect& imageRect, FloatRect* srcRect, FloatRect* dstRect)
1443 if (imageRect.contains(*srcRect))
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;
1452 srcRect->intersect(imageRect);
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);
1460 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, float x, float y, ExceptionState& exceptionState)
1462 FloatSize destRectSize = imageSource->defaultDestinationSize();
1463 drawImage(imageSource, x, y, destRectSize.width(), destRectSize.height(), exceptionState);
1466 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource,
1467 float x, float y, float width, float height, ExceptionState& exceptionState)
1469 FloatSize sourceRectSize = imageSource->sourceSize();
1470 drawImage(imageSource, 0, 0, sourceRectSize.width(), sourceRectSize.height(), x, y, width, height, 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)
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);
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)
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())
1500 c = drawingContext();
1504 if (!state().m_invertibleCTM)
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)
1512 FloatRect clipBounds;
1513 if (!c->getTransformedClipBounds(&clipBounds))
1516 FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh));
1517 FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh));
1519 clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->sourceSize()), &srcRect, &dstRect);
1521 imageSource->adjustDrawRects(&srcRect, &dstRect);
1523 if (srcRect.isEmpty())
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);
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) {
1538 c->drawImage(image.get(), dstRect, srcRect, op, blendMode);
1540 FloatRect dirtyRect;
1541 computeDirtyRect(dstRect, clipBounds, &dirtyRect);
1542 c->drawImage(image.get(), dstRect, srcRect, op, blendMode);
1545 if (sourceImageStatus == ExternalSourceImageStatus && isAccelerated() && canvas()->buffer())
1546 canvas()->buffer()->flush();
1549 if (canvas()->originClean() && wouldTaintOrigin(imageSource))
1550 canvas()->setOriginTainted();
1555 void CanvasRenderingContext2D::drawVideo(HTMLVideoElement* video, FloatRect srcRect, FloatRect dstRect)
1557 GraphicsContext* c = drawingContext();
1558 GraphicsContextStateSaver stateSaver(*c);
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();
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)
1575 CompositeOperator op;
1576 blink::WebBlendMode blendOp = blink::WebBlendModeNormal;
1577 if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != blink::WebBlendModeNormal)
1578 op = CompositeSourceOver;
1580 drawImageInternal(image, sx, sy, sw, sh, dx, dy, dw, dh, IGNORE_EXCEPTION, op, blendOp);
1583 void CanvasRenderingContext2D::setAlpha(float alpha)
1585 setGlobalAlpha(alpha);
1588 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1590 setGlobalCompositeOperation(operation);
1593 void CanvasRenderingContext2D::clearCanvas()
1595 FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height());
1596 GraphicsContext* c = drawingContext();
1601 c->setCTM(canvas()->baseTransform());
1602 c->clearRect(canvasRect);
1606 bool CanvasRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect, const FloatRect& transformedRect) const
1608 FloatQuad quad(rect);
1609 FloatQuad transformedQuad(transformedRect);
1610 return state().m_transform.mapQuad(quad).containsQuad(transformedQuad);
1613 static void drawImageToContext(Image* image, GraphicsContext* context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1615 context->drawImage(image, dest, src, op);
1618 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1620 ASSERT(isFullCanvasCompositeMode(op));
1622 GraphicsContext* c = drawingContext();
1623 c->beginLayer(1, op);
1624 drawImageToContext(image, c, dest, src, CompositeSourceOver);
1628 static void fillPrimitive(const FloatRect& rect, GraphicsContext* context)
1630 context->fillRect(rect);
1633 static void fillPrimitive(const Path& path, GraphicsContext* context)
1635 context->fillPath(path);
1638 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedFill(const T& area)
1640 ASSERT(isFullCanvasCompositeMode(state().m_globalComposite));
1642 GraphicsContext* c = drawingContext();
1644 c->beginLayer(1, state().m_globalComposite);
1645 CompositeOperator previousOperator = c->compositeOperation();
1646 c->setCompositeOperation(CompositeSourceOver);
1647 fillPrimitive(area, c);
1648 c->setCompositeOperation(previousOperator);
1652 static void strokePrimitive(const FloatRect& rect, GraphicsContext* context)
1654 context->strokeRect(rect);
1657 static void strokePrimitive(const Path& path, GraphicsContext* context)
1659 context->strokePath(path);
1662 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedStroke(const T& area)
1664 ASSERT(isFullCanvasCompositeMode(state().m_globalComposite));
1666 GraphicsContext* c = drawingContext();
1668 c->beginLayer(1, state().m_globalComposite);
1669 CompositeOperator previousOperator = c->compositeOperation();
1670 c->setCompositeOperation(CompositeSourceOver);
1671 strokePrimitive(area, c);
1672 c->setCompositeOperation(previousOperator);
1676 PassRefPtrWillBeRawPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
1678 RefPtrWillBeRawPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1679 return gradient.release();
1682 PassRefPtrWillBeRawPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionState& exceptionState)
1684 if (r0 < 0 || r1 < 0) {
1685 exceptionState.throwDOMException(IndexSizeError, String::format("The %s provided is less than 0.", r0 < 0 ? "r0" : "r1"));
1689 RefPtrWillBeRawPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1690 return gradient.release();
1693 PassRefPtrWillBeRawPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(CanvasImageSource* imageSource,
1694 const String& repetitionType, ExceptionState& exceptionState)
1696 Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetitionType, exceptionState);
1697 if (exceptionState.hadException())
1700 SourceImageStatus status;
1701 RefPtr<Image> imageForRendering = imageSource->getSourceImageForCanvas(CopySourceImageIfVolatile, &status);
1704 case NormalSourceImageStatus:
1706 case ZeroSizeCanvasSourceImageStatus:
1707 exceptionState.throwDOMException(InvalidStateError, String::format("The canvas %s is 0.", imageSource->sourceSize().width() ? "height" : "width"));
1709 case UndecodableSourceImageStatus:
1710 exceptionState.throwDOMException(InvalidStateError, "Source image is in the 'broken' state.");
1712 case InvalidSourceImageStatus:
1713 imageForRendering = Image::nullImage();
1715 case IncompleteSourceImageStatus:
1718 case ExternalSourceImageStatus: // should not happen when mode is CopySourceImageIfVolatile
1719 ASSERT_NOT_REACHED();
1722 ASSERT(imageForRendering);
1724 bool originClean = !wouldTaintOrigin(imageSource);
1726 return CanvasPattern::create(imageForRendering.release(), repeatMode, originClean);
1729 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, FloatRect* dirtyRect)
1731 FloatRect clipBounds;
1732 if (!drawingContext()->getTransformedClipBounds(&clipBounds))
1734 return computeDirtyRect(localRect, clipBounds, dirtyRect);
1737 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, const FloatRect& transformedClipBounds, FloatRect* dirtyRect)
1739 FloatRect canvasRect = state().m_transform.mapRect(localRect);
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);
1748 canvasRect.intersect(transformedClipBounds);
1749 if (canvasRect.isEmpty())
1753 *dirtyRect = canvasRect;
1758 void CanvasRenderingContext2D::didDraw(const FloatRect& dirtyRect)
1760 if (dirtyRect.isEmpty())
1763 canvas()->didDraw(dirtyRect);
1766 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1768 if (isContextLost())
1770 return canvas()->drawingContext();
1773 static PassRefPtrWillBeRawPtr<ImageData> createEmptyImageData(const IntSize& size)
1775 if (RefPtrWillBeRawPtr<ImageData> data = ImageData::create(size)) {
1776 data->data()->zeroFill();
1777 return data.release();
1783 PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtrWillBeRawPtr<ImageData> imageData) const
1785 return createEmptyImageData(imageData->size());
1788 PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionState& exceptionState) const
1791 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
1795 FloatSize logicalSize(fabs(sw), fabs(sh));
1796 if (!logicalSize.isExpressibleAsIntSize())
1799 IntSize size = expandedIntSize(logicalSize);
1800 if (size.width() < 1)
1802 if (size.height() < 1)
1805 return createEmptyImageData(size);
1808 PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const
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"));
1815 if (exceptionState.hadException())
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())
1835 IntRect imageDataRect = enclosingIntRect(logicalRect);
1836 ImageBuffer* buffer = canvas()->buffer();
1837 if (!buffer || isContextLost())
1838 return createEmptyImageData(imageDataRect.size());
1840 RefPtr<Uint8ClampedArray> byteArray = buffer->getImageData(Unmultiplied, imageDataRect);
1844 return ImageData::create(imageDataRect.size(), byteArray.release());
1847 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy)
1849 putImageData(data, dx, dy, 0, 0, data->width(), data->height());
1852 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
1854 ImageBuffer* buffer = canvas()->buffer();
1858 if (dirtyWidth < 0) {
1859 dirtyX += dirtyWidth;
1860 dirtyWidth = -dirtyWidth;
1863 if (dirtyHeight < 0) {
1864 dirtyY += dirtyHeight;
1865 dirtyHeight = -dirtyHeight;
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())
1876 IntRect sourceRect(destRect);
1877 sourceRect.move(-destOffset);
1879 buffer->putByteArray(Unmultiplied, data->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset));
1884 String CanvasRenderingContext2D::font() const
1886 if (!state().m_realizedFont)
1889 StringBuilder serializedFont;
1890 const FontDescription& fontDescription = state().m_font.fontDescription();
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 ");
1899 serializedFont.appendNumber(fontDescription.computedPixelSize());
1900 serializedFont.appendLiteral("px");
1902 const FontFamily& firstFontFamily = fontDescription.family();
1903 for (const FontFamily* fontFamily = &firstFontFamily; fontFamily; fontFamily = fontFamily->next()) {
1904 if (fontFamily != &firstFontFamily)
1905 serializedFont.append(',');
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 + "\"";
1914 serializedFont.append(' ');
1915 serializedFont.append(family);
1918 return serializedFont.toString();
1921 void CanvasRenderingContext2D::setFont(const String& newFont)
1923 // The style resolution required for rendering text is not available in frame-less documents.
1924 if (!canvas()->document().frame())
1927 MutableStylePropertyMap::iterator i = m_fetchedFonts.find(newFont);
1928 RefPtrWillBeRawPtr<MutableStylePropertySet> parsedStyle = i != m_fetchedFonts.end() ? i->value : nullptr;
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);
1936 if (parsedStyle->isEmpty())
1939 String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
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")
1946 // The parse succeeded.
1947 String newFontSafeCopy(newFont); // Create a string copy since newFont can be deleted inside realizeSaves.
1949 modifiableState().m_unparsedFont = newFontSafeCopy;
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);
1961 FontFamily fontFamily;
1962 fontFamily.setFamily(defaultFontFamily);
1964 FontDescription defaultFontDescription;
1965 defaultFontDescription.setFamily(fontFamily);
1966 defaultFontDescription.setSpecifiedSize(defaultFontSize);
1967 defaultFontDescription.setComputedSize(defaultFontSize);
1969 newStyle->setFontDescription(defaultFontDescription);
1972 newStyle->font().update(newStyle->font().fontSelector());
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),
1984 StyleResolver& styleResolver = canvas()->document().ensureStyleResolver();
1985 styleResolver.applyPropertiesToStyle(properties, WTF_ARRAY_LENGTH(properties), newStyle.get());
1988 if (state().m_realizedFont)
1989 static_cast<CSSFontSelector*>(state().m_font.fontSelector())->unregisterForInvalidationCallbacks(&modifiableState());
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());
1997 String CanvasRenderingContext2D::textAlign() const
1999 return textAlignName(state().m_textAlign);
2002 void CanvasRenderingContext2D::setTextAlign(const String& s)
2005 if (!parseTextAlign(s, align))
2007 if (state().m_textAlign == align)
2010 modifiableState().m_textAlign = align;
2013 String CanvasRenderingContext2D::textBaseline() const
2015 return textBaselineName(state().m_textBaseline);
2018 void CanvasRenderingContext2D::setTextBaseline(const String& s)
2020 TextBaseline baseline;
2021 if (!parseTextBaseline(s, baseline))
2023 if (state().m_textBaseline == baseline)
2026 modifiableState().m_textBaseline = baseline;
2029 inline TextDirection CanvasRenderingContext2D::toTextDirection(Direction direction, RenderStyle** computedStyle) const
2031 RenderStyle* style = (computedStyle || direction == DirectionInherit) ? canvas()->computedStyle() : nullptr;
2033 *computedStyle = style;
2034 switch (direction) {
2035 case DirectionInherit:
2036 return style ? style->direction() : LTR;
2042 ASSERT_NOT_REACHED();
2046 String CanvasRenderingContext2D::direction() const
2048 if (state().m_direction == DirectionInherit)
2049 canvas()->document().updateRenderTreeIfNeeded();
2050 return toTextDirection(state().m_direction) == RTL ? rtl : ltr;
2053 void CanvasRenderingContext2D::setDirection(const String& directionString)
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;
2065 if (state().m_direction == direction)
2069 modifiableState().m_direction = direction;
2072 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
2074 drawTextInternal(text, x, y, true);
2077 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
2079 drawTextInternal(text, x, y, true, maxWidth, true);
2082 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
2084 drawTextInternal(text, x, y, false);
2087 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
2089 drawTextInternal(text, x, y, false, maxWidth, true);
2092 PassRefPtrWillBeRawPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
2094 RefPtrWillBeRawPtr<TextMetrics> metrics = TextMetrics::create();
2096 // The style resolution required for rendering text is not available in frame-less documents.
2097 if (!canvas()->document().frame())
2098 return metrics.release();
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);
2107 metrics->setWidth(font.width(textRun));
2108 metrics->setActualBoundingBoxLeft(-textBounds.x());
2109 metrics->setActualBoundingBoxRight(textBounds.maxX());
2112 const FontMetrics& fontMetrics = font.fontMetrics();
2113 const float ascent = fontMetrics.floatAscent();
2114 const float descent = fontMetrics.floatDescent();
2115 const float baselineY = getFontBaseline(fontMetrics);
2117 metrics->setFontBoundingBoxAscent(ascent - baselineY);
2118 metrics->setFontBoundingBoxDescent(descent + baselineY);
2119 metrics->setActualBoundingBoxAscent(-textBounds.y() - baselineY);
2120 metrics->setActualBoundingBoxDescent(textBounds.maxY() + baselineY);
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);
2127 metrics->setHangingBaseline(-0.8f * ascent + baselineY);
2128 metrics->setAlphabeticBaseline(baselineY);
2129 metrics->setIdeographicBaseline(descent + baselineY);
2130 return metrics.release();
2133 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth, bool useMaxWidth)
2135 // The style resolution required for rendering text is not available in frame-less documents.
2136 if (!canvas()->document().frame())
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();
2144 GraphicsContext* c = drawingContext();
2147 if (!state().m_invertibleCTM)
2149 if (!std::isfinite(x) | !std::isfinite(y))
2151 if (useMaxWidth && (!std::isfinite(maxWidth) || maxWidth <= 0))
2154 // If gradient size is zero, then paint nothing.
2155 Gradient* gradient = c->strokeGradient();
2156 if (!fill && gradient && gradient->isZeroSize())
2159 gradient = c->fillGradient();
2160 if (fill && gradient && gradient->isZeroSize())
2163 FontCachePurgePreventer fontCachePurgePreventer;
2165 const Font& font = accessFont();
2166 const FontMetrics& fontMetrics = font.fontMetrics();
2168 // FIXME: Need to turn off font smoothing.
2170 RenderStyle* computedStyle;
2171 TextDirection direction = toTextDirection(state().m_direction, &computedStyle);
2172 bool isRTL = direction == RTL;
2173 bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : false;
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);
2180 useMaxWidth = (useMaxWidth && maxWidth < fontWidth);
2181 float width = useMaxWidth ? maxWidth : fontWidth;
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;
2190 case CenterTextAlign:
2191 location.setX(location.x() - width / 2);
2193 case RightTextAlign:
2194 location.setX(location.x() - width);
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());
2207 inflateStrokeRect(textRunPaintInfo.bounds);
2209 c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
2211 GraphicsContextStateSaver stateSaver(*c);
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();
2219 FloatRect clipBounds;
2220 if (!c->getTransformedClipBounds(&clipBounds)) {
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);
2231 didDraw(clipBounds);
2232 } else if (state().m_globalComposite == CompositeCopy) {
2234 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
2235 didDraw(clipBounds);
2237 FloatRect dirtyRect;
2238 if (computeDirtyRect(textRunPaintInfo.bounds, clipBounds, &dirtyRect)) {
2239 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
2245 void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
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)
2257 rect.inflate(delta);
2260 const Font& CanvasRenderingContext2D::accessFont()
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;
2269 int CanvasRenderingContext2D::getFontBaseline(const FontMetrics& fontMetrics) const
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:
2291 void CanvasRenderingContext2D::setIsHidden(bool hidden)
2293 ImageBuffer* buffer = canvas()->buffer();
2295 buffer->setIsHidden(hidden);
2298 blink::WebLayer* CanvasRenderingContext2D::platformLayer() const
2300 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0;
2303 bool CanvasRenderingContext2D::imageSmoothingEnabled() const
2305 return state().m_imageSmoothingEnabled;
2308 void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled)
2310 if (enabled == state().m_imageSmoothingEnabled)
2313 GraphicsContext* c = drawingContext();
2315 modifiableState().m_imageSmoothingEnabled = enabled;
2317 c->setImageInterpolationQuality(enabled ? CanvasDefaultInterpolationQuality : InterpolationNone);
2320 PassRefPtrWillBeRawPtr<Canvas2DContextAttributes> CanvasRenderingContext2D::getContextAttributes() const
2322 RefPtrWillBeRawPtr<Canvas2DContextAttributes> attributes = Canvas2DContextAttributes::create();
2323 attributes->setAlpha(m_hasAlpha);
2324 return attributes.release();
2327 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element)
2329 drawFocusIfNeededInternal(m_path, element);
2332 void CanvasRenderingContext2D::drawFocusIfNeeded(Path2D* path2d, Element* element)
2334 drawFocusIfNeededInternal(path2d->path(), element);
2337 void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Element* element)
2339 if (!focusRingCallIsValid(path, element))
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);
2349 bool CanvasRenderingContext2D::focusRingCallIsValid(const Path& path, Element* element)
2352 if (!state().m_invertibleCTM)
2356 if (!element->isDescendantOf(canvas()))
2362 void CanvasRenderingContext2D::drawFocusRing(const Path& path)
2364 GraphicsContext* c = drawingContext();
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;
2373 // We need to add focusRingWidth to dirtyRect.
2374 StrokeData strokeData;
2375 strokeData.setThickness(focusRingWidth);
2377 FloatRect dirtyRect;
2378 if (!computeDirtyRect(path.strokeBoundingRect(strokeData), &dirtyRect))
2382 c->setAlphaAsFloat(1.0);
2384 c->setCompositeOperation(CompositeSourceOver, blink::WebBlendModeNormal);
2385 c->drawFocusRing(path, focusRingWidth, focusRingOutline, focusRingColor);
2387 validateStateStack();
2391 void CanvasRenderingContext2D::addHitRegion(const HitRegionOptions& options, ExceptionState& exceptionState)
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.");
2401 Path hitRegionPath = options.hasPath() ? options.path()->path() : m_path;
2403 FloatRect clipBounds;
2404 GraphicsContext* context = drawingContext();
2406 if (hitRegionPath.isEmpty() || !context || !state().m_invertibleCTM
2407 || !context->getTransformedClipBounds(&clipBounds)) {
2408 exceptionState.throwDOMException(NotSupportedError, "The specified path has no pixels.");
2412 hitRegionPath.transform(state().m_transform);
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.");
2422 passOptions.path = hitRegionPath;
2424 if (options.fillRule() != "evenodd")
2425 passOptions.fillRule = RULE_NONZERO;
2427 passOptions.fillRule = RULE_EVENODD;
2429 addHitRegionInternal(passOptions, exceptionState);
2432 void CanvasRenderingContext2D::addHitRegionInternal(const HitRegionOptionsInternal& options, ExceptionState& exceptionState)
2434 if (!m_hitRegionManager)
2435 m_hitRegionManager = HitRegionManager::create();
2437 // Remove previous region (with id or control)
2438 m_hitRegionManager->removeHitRegionById(options.id);
2439 m_hitRegionManager->removeHitRegionByControl(options.control.get());
2441 RefPtrWillBeRawPtr<HitRegion> hitRegion = HitRegion::create(options);
2442 hitRegion->updateAccessibility(canvas());
2443 m_hitRegionManager->addHitRegion(hitRegion.release());
2446 void CanvasRenderingContext2D::removeHitRegion(const String& id)
2448 if (m_hitRegionManager)
2449 m_hitRegionManager->removeHitRegionById(id);
2452 void CanvasRenderingContext2D::clearHitRegions()
2454 if (m_hitRegionManager)
2455 m_hitRegionManager->removeAllHitRegions();
2458 HitRegion* CanvasRenderingContext2D::hitRegionAtPoint(const LayoutPoint& point)
2460 if (m_hitRegionManager)
2461 return m_hitRegionManager->getHitRegionAtPoint(point);
2466 unsigned CanvasRenderingContext2D::hitRegionsCount() const
2468 if (m_hitRegionManager)
2469 return m_hitRegionManager->getHitRegionsCount();
2474 } // namespace blink