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 "CSSPropertyNames.h"
37 #include "bindings/v8/ExceptionMessages.h"
38 #include "bindings/v8/ExceptionState.h"
39 #include "bindings/v8/ExceptionStatePlaceholder.h"
40 #include "core/accessibility/AXObjectCache.h"
41 #include "core/css/CSSFontSelector.h"
42 #include "core/css/parser/BisonCSSParser.h"
43 #include "core/css/StylePropertySet.h"
44 #include "core/css/resolver/StyleResolver.h"
45 #include "core/dom/ExceptionCode.h"
46 #include "core/fetch/ImageResource.h"
47 #include "core/html/HTMLCanvasElement.h"
48 #include "core/html/HTMLImageElement.h"
49 #include "core/html/HTMLMediaElement.h"
50 #include "core/html/HTMLVideoElement.h"
51 #include "core/html/ImageData.h"
52 #include "core/html/TextMetrics.h"
53 #include "core/html/canvas/CanvasGradient.h"
54 #include "core/html/canvas/CanvasPattern.h"
55 #include "core/html/canvas/CanvasStyle.h"
56 #include "core/html/canvas/DOMPath.h"
57 #include "core/frame/ImageBitmap.h"
58 #include "core/rendering/RenderImage.h"
59 #include "core/rendering/RenderLayer.h"
60 #include "core/rendering/RenderTheme.h"
61 #include "platform/fonts/FontCache.h"
62 #include "platform/geometry/FloatQuad.h"
63 #include "platform/graphics/GraphicsContextStateSaver.h"
64 #include "platform/graphics/DrawLooper.h"
65 #include "platform/text/TextRun.h"
66 #include "platform/weborigin/SecurityOrigin.h"
67 #include "wtf/CheckedArithmetic.h"
68 #include "wtf/MathExtras.h"
69 #include "wtf/OwnPtr.h"
70 #include "wtf/Uint8ClampedArray.h"
71 #include "wtf/text/StringBuilder.h"
77 static const int defaultFontSize = 10;
78 static const char defaultFontFamily[] = "sans-serif";
79 static const char defaultFont[] = "10px sans-serif";
81 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, const Canvas2DContextAttributes* attrs, bool usesCSSCompatibilityParseMode)
82 : CanvasRenderingContext(canvas)
84 , m_unrealizedSaveCount(0)
85 , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
86 , m_hasAlpha(!attrs || attrs->alpha())
88 ScriptWrappable::init(this);
91 void CanvasRenderingContext2D::unwindStateStack()
93 // Ensure that the state stack in the ImageBuffer's context
94 // is cleared before destruction, to avoid assertions in the
95 // GraphicsContext dtor.
96 if (size_t stackSize = m_stateStack.size()) {
97 if (GraphicsContext* context = canvas()->existingDrawingContext()) {
104 CanvasRenderingContext2D::~CanvasRenderingContext2D()
111 bool CanvasRenderingContext2D::isAccelerated() const
113 if (!canvas()->hasImageBuffer())
115 GraphicsContext* context = drawingContext();
116 return context && context->isAccelerated();
119 void CanvasRenderingContext2D::reset()
122 m_stateStack.resize(1);
123 m_stateStack.first() = State();
125 m_unrealizedSaveCount = 0;
128 // Important: Several of these properties are also stored in GraphicsContext's
129 // StrokeData. The default values that StrokeData uses may not the same values
130 // that the canvas 2d spec specifies. Make sure to sync the initial state of the
131 // GraphicsContext in HTMLCanvasElement::createImageBuffer()!
132 CanvasRenderingContext2D::State::State()
133 : m_strokeStyle(CanvasStyle::createFromRGBA(Color::black))
134 , m_fillStyle(CanvasStyle::createFromRGBA(Color::black))
137 , m_lineJoin(MiterJoin)
140 , m_shadowColor(Color::transparent)
142 , m_globalComposite(CompositeSourceOver)
143 , m_globalBlend(blink::WebBlendModeNormal)
144 , m_invertibleCTM(true)
145 , m_lineDashOffset(0)
146 , m_imageSmoothingEnabled(true)
147 , m_textAlign(StartTextAlign)
148 , m_textBaseline(AlphabeticTextBaseline)
149 , m_unparsedFont(defaultFont)
150 , m_realizedFont(false)
154 CanvasRenderingContext2D::State::State(const State& other)
155 : CSSFontSelectorClient()
156 , m_unparsedStrokeColor(other.m_unparsedStrokeColor)
157 , m_unparsedFillColor(other.m_unparsedFillColor)
158 , m_strokeStyle(other.m_strokeStyle)
159 , m_fillStyle(other.m_fillStyle)
160 , m_lineWidth(other.m_lineWidth)
161 , m_lineCap(other.m_lineCap)
162 , m_lineJoin(other.m_lineJoin)
163 , m_miterLimit(other.m_miterLimit)
164 , m_shadowOffset(other.m_shadowOffset)
165 , m_shadowBlur(other.m_shadowBlur)
166 , m_shadowColor(other.m_shadowColor)
167 , m_globalAlpha(other.m_globalAlpha)
168 , m_globalComposite(other.m_globalComposite)
169 , m_globalBlend(other.m_globalBlend)
170 , m_transform(other.m_transform)
171 , m_invertibleCTM(other.m_invertibleCTM)
172 , m_lineDashOffset(other.m_lineDashOffset)
173 , m_imageSmoothingEnabled(other.m_imageSmoothingEnabled)
174 , m_textAlign(other.m_textAlign)
175 , m_textBaseline(other.m_textBaseline)
176 , m_unparsedFont(other.m_unparsedFont)
177 , m_font(other.m_font)
178 , m_realizedFont(other.m_realizedFont)
181 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalidationCallbacks(this);
184 CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other)
190 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInvalidationCallbacks(this);
192 m_unparsedStrokeColor = other.m_unparsedStrokeColor;
193 m_unparsedFillColor = other.m_unparsedFillColor;
194 m_strokeStyle = other.m_strokeStyle;
195 m_fillStyle = other.m_fillStyle;
196 m_lineWidth = other.m_lineWidth;
197 m_lineCap = other.m_lineCap;
198 m_lineJoin = other.m_lineJoin;
199 m_miterLimit = other.m_miterLimit;
200 m_shadowOffset = other.m_shadowOffset;
201 m_shadowBlur = other.m_shadowBlur;
202 m_shadowColor = other.m_shadowColor;
203 m_globalAlpha = other.m_globalAlpha;
204 m_globalComposite = other.m_globalComposite;
205 m_globalBlend = other.m_globalBlend;
206 m_transform = other.m_transform;
207 m_invertibleCTM = other.m_invertibleCTM;
208 m_imageSmoothingEnabled = other.m_imageSmoothingEnabled;
209 m_textAlign = other.m_textAlign;
210 m_textBaseline = other.m_textBaseline;
211 m_unparsedFont = other.m_unparsedFont;
212 m_font = other.m_font;
213 m_realizedFont = other.m_realizedFont;
216 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalidationCallbacks(this);
221 CanvasRenderingContext2D::State::~State()
224 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInvalidationCallbacks(this);
227 void CanvasRenderingContext2D::State::fontsNeedUpdate(CSSFontSelector* fontSelector)
229 ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector());
230 ASSERT(m_realizedFont);
232 m_font.update(fontSelector);
235 void CanvasRenderingContext2D::realizeSavesLoop()
237 ASSERT(m_unrealizedSaveCount);
238 ASSERT(m_stateStack.size() >= 1);
239 GraphicsContext* context = drawingContext();
241 m_stateStack.append(state());
244 } while (--m_unrealizedSaveCount);
247 void CanvasRenderingContext2D::restore()
249 if (m_unrealizedSaveCount) {
250 --m_unrealizedSaveCount;
253 ASSERT(m_stateStack.size() >= 1);
254 if (m_stateStack.size() <= 1)
256 m_path.transform(state().m_transform);
257 m_stateStack.removeLast();
258 m_path.transform(state().m_transform.inverse());
259 GraphicsContext* c = drawingContext();
265 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
267 return state().m_strokeStyle.get();
270 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> prpStyle)
272 RefPtr<CanvasStyle> style = prpStyle;
277 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style))
280 if (style->isCurrentColor()) {
281 if (style->hasOverrideAlpha())
282 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha()));
284 style = CanvasStyle::createFromRGBA(currentColor(canvas()));
286 checkOrigin(style->canvasPattern());
289 modifiableState().m_strokeStyle = style.release();
290 GraphicsContext* c = drawingContext();
293 state().m_strokeStyle->applyStrokeColor(c);
294 modifiableState().m_unparsedStrokeColor = String();
297 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
299 return state().m_fillStyle.get();
302 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> prpStyle)
304 RefPtr<CanvasStyle> style = prpStyle;
309 if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style))
312 if (style->isCurrentColor()) {
313 if (style->hasOverrideAlpha())
314 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha()));
316 style = CanvasStyle::createFromRGBA(currentColor(canvas()));
318 checkOrigin(style->canvasPattern());
321 modifiableState().m_fillStyle = style.release();
322 GraphicsContext* c = drawingContext();
325 state().m_fillStyle->applyFillColor(c);
326 modifiableState().m_unparsedFillColor = String();
329 float CanvasRenderingContext2D::lineWidth() const
331 return state().m_lineWidth;
334 void CanvasRenderingContext2D::setLineWidth(float width)
336 if (!(std::isfinite(width) && width > 0))
338 if (state().m_lineWidth == width)
341 modifiableState().m_lineWidth = width;
342 GraphicsContext* c = drawingContext();
345 c->setStrokeThickness(width);
348 String CanvasRenderingContext2D::lineCap() const
350 return lineCapName(state().m_lineCap);
353 void CanvasRenderingContext2D::setLineCap(const String& s)
356 if (!parseLineCap(s, cap))
358 if (state().m_lineCap == cap)
361 modifiableState().m_lineCap = cap;
362 GraphicsContext* c = drawingContext();
368 String CanvasRenderingContext2D::lineJoin() const
370 return lineJoinName(state().m_lineJoin);
373 void CanvasRenderingContext2D::setLineJoin(const String& s)
376 if (!parseLineJoin(s, join))
378 if (state().m_lineJoin == join)
381 modifiableState().m_lineJoin = join;
382 GraphicsContext* c = drawingContext();
385 c->setLineJoin(join);
388 float CanvasRenderingContext2D::miterLimit() const
390 return state().m_miterLimit;
393 void CanvasRenderingContext2D::setMiterLimit(float limit)
395 if (!(std::isfinite(limit) && limit > 0))
397 if (state().m_miterLimit == limit)
400 modifiableState().m_miterLimit = limit;
401 GraphicsContext* c = drawingContext();
404 c->setMiterLimit(limit);
407 float CanvasRenderingContext2D::shadowOffsetX() const
409 return state().m_shadowOffset.width();
412 void CanvasRenderingContext2D::setShadowOffsetX(float x)
414 if (!std::isfinite(x))
416 if (state().m_shadowOffset.width() == x)
419 modifiableState().m_shadowOffset.setWidth(x);
423 float CanvasRenderingContext2D::shadowOffsetY() const
425 return state().m_shadowOffset.height();
428 void CanvasRenderingContext2D::setShadowOffsetY(float y)
430 if (!std::isfinite(y))
432 if (state().m_shadowOffset.height() == y)
435 modifiableState().m_shadowOffset.setHeight(y);
439 float CanvasRenderingContext2D::shadowBlur() const
441 return state().m_shadowBlur;
444 void CanvasRenderingContext2D::setShadowBlur(float blur)
446 if (!(std::isfinite(blur) && blur >= 0))
448 if (state().m_shadowBlur == blur)
451 modifiableState().m_shadowBlur = blur;
455 String CanvasRenderingContext2D::shadowColor() const
457 return Color(state().m_shadowColor).serialized();
460 void CanvasRenderingContext2D::setShadowColor(const String& color)
463 if (!parseColorOrCurrentColor(rgba, color, canvas()))
465 if (state().m_shadowColor == rgba)
468 modifiableState().m_shadowColor = rgba;
472 const Vector<float>& CanvasRenderingContext2D::getLineDash() const
474 return state().m_lineDash;
477 static bool lineDashSequenceIsValid(const Vector<float>& dash)
479 for (size_t i = 0; i < dash.size(); i++) {
480 if (!std::isfinite(dash[i]) || dash[i] < 0)
486 void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash)
488 if (!lineDashSequenceIsValid(dash))
492 modifiableState().m_lineDash = dash;
493 // Spec requires the concatenation of two copies the dash list when the
494 // number of elements is odd
496 modifiableState().m_lineDash.append(dash);
501 void CanvasRenderingContext2D::setWebkitLineDash(const Vector<float>& dash)
503 if (!lineDashSequenceIsValid(dash))
507 modifiableState().m_lineDash = dash;
512 float CanvasRenderingContext2D::lineDashOffset() const
514 return state().m_lineDashOffset;
517 void CanvasRenderingContext2D::setLineDashOffset(float offset)
519 if (!std::isfinite(offset) || state().m_lineDashOffset == offset)
523 modifiableState().m_lineDashOffset = offset;
527 float CanvasRenderingContext2D::webkitLineDashOffset() const
529 return lineDashOffset();
532 void CanvasRenderingContext2D::setWebkitLineDashOffset(float offset)
534 setLineDashOffset(offset);
537 void CanvasRenderingContext2D::applyLineDash() const
539 GraphicsContext* c = drawingContext();
542 DashArray convertedLineDash(state().m_lineDash.size());
543 for (size_t i = 0; i < state().m_lineDash.size(); ++i)
544 convertedLineDash[i] = static_cast<DashArrayElement>(state().m_lineDash[i]);
545 c->setLineDash(convertedLineDash, state().m_lineDashOffset);
548 float CanvasRenderingContext2D::globalAlpha() const
550 return state().m_globalAlpha;
553 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
555 if (!(alpha >= 0 && alpha <= 1))
557 if (state().m_globalAlpha == alpha)
560 modifiableState().m_globalAlpha = alpha;
561 GraphicsContext* c = drawingContext();
567 String CanvasRenderingContext2D::globalCompositeOperation() const
569 return compositeOperatorName(state().m_globalComposite, state().m_globalBlend);
572 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
574 CompositeOperator op = CompositeSourceOver;
575 blink::WebBlendMode blendMode = blink::WebBlendModeNormal;
576 if (!parseCompositeAndBlendOperator(operation, op, blendMode))
578 if ((state().m_globalComposite == op) && (state().m_globalBlend == blendMode))
581 modifiableState().m_globalComposite = op;
582 modifiableState().m_globalBlend = blendMode;
583 GraphicsContext* c = drawingContext();
586 c->setCompositeOperation(op, blendMode);
589 void CanvasRenderingContext2D::setCurrentTransform(const SVGMatrix& matrix)
591 setTransform(matrix.a(), matrix.b(), matrix.c(), matrix.d(), matrix.e(), matrix.f());
594 void CanvasRenderingContext2D::scale(float sx, float sy)
596 GraphicsContext* c = drawingContext();
599 if (!state().m_invertibleCTM)
602 if (!std::isfinite(sx) | !std::isfinite(sy))
605 AffineTransform newTransform = state().m_transform;
606 newTransform.scaleNonUniform(sx, sy);
607 if (state().m_transform == newTransform)
612 if (!newTransform.isInvertible()) {
613 modifiableState().m_invertibleCTM = false;
617 modifiableState().m_transform = newTransform;
618 c->scale(FloatSize(sx, sy));
619 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
622 void CanvasRenderingContext2D::rotate(float angleInRadians)
624 GraphicsContext* c = drawingContext();
627 if (!state().m_invertibleCTM)
630 if (!std::isfinite(angleInRadians))
633 AffineTransform newTransform = state().m_transform;
634 newTransform.rotate(angleInRadians / piDouble * 180.0);
635 if (state().m_transform == newTransform)
640 if (!newTransform.isInvertible()) {
641 modifiableState().m_invertibleCTM = false;
645 modifiableState().m_transform = newTransform;
646 c->rotate(angleInRadians);
647 m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
650 void CanvasRenderingContext2D::translate(float tx, float ty)
652 GraphicsContext* c = drawingContext();
655 if (!state().m_invertibleCTM)
658 if (!std::isfinite(tx) | !std::isfinite(ty))
661 AffineTransform newTransform = state().m_transform;
662 newTransform.translate(tx, ty);
663 if (state().m_transform == newTransform)
668 if (!newTransform.isInvertible()) {
669 modifiableState().m_invertibleCTM = false;
673 modifiableState().m_transform = newTransform;
674 c->translate(tx, ty);
675 m_path.transform(AffineTransform().translate(-tx, -ty));
678 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
680 GraphicsContext* c = drawingContext();
683 if (!state().m_invertibleCTM)
686 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
689 AffineTransform transform(m11, m12, m21, m22, dx, dy);
690 AffineTransform newTransform = state().m_transform * transform;
691 if (state().m_transform == newTransform)
696 modifiableState().m_transform = newTransform;
697 if (!newTransform.isInvertible()) {
698 modifiableState().m_invertibleCTM = false;
702 c->concatCTM(transform);
703 m_path.transform(transform.inverse());
706 void CanvasRenderingContext2D::resetTransform()
708 GraphicsContext* c = drawingContext();
712 AffineTransform ctm = state().m_transform;
713 bool invertibleCTM = state().m_invertibleCTM;
714 // It is possible that CTM is identity while CTM is not invertible.
715 // When CTM becomes non-invertible, realizeSaves() can make CTM identity.
716 if (ctm.isIdentity() && invertibleCTM)
720 // resetTransform() resolves the non-invertible CTM state.
721 modifiableState().m_transform.makeIdentity();
722 modifiableState().m_invertibleCTM = true;
723 c->setCTM(canvas()->baseTransform());
726 m_path.transform(ctm);
727 // When else, do nothing because all transform methods didn't update m_path when CTM became non-invertible.
728 // It means that resetTransform() restores m_path just before CTM became non-invertible.
731 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
733 GraphicsContext* c = drawingContext();
737 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
741 transform(m11, m12, m21, m22, dx, dy);
744 void CanvasRenderingContext2D::setStrokeColor(const String& color)
746 if (color == state().m_unparsedStrokeColor)
749 setStrokeStyle(CanvasStyle::createFromString(color, &canvas()->document()));
750 modifiableState().m_unparsedStrokeColor = color;
753 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
755 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
757 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
760 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
762 setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
765 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
767 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
769 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
772 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
774 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b, a))
776 setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
779 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
781 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a))
783 setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
786 void CanvasRenderingContext2D::setFillColor(const String& color)
788 if (color == state().m_unparsedFillColor)
791 setFillStyle(CanvasStyle::createFromString(color, &canvas()->document()));
792 modifiableState().m_unparsedFillColor = color;
795 void CanvasRenderingContext2D::setFillColor(float grayLevel)
797 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
799 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
802 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
804 setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
807 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
809 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
811 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
814 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
816 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a))
818 setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
821 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
823 if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k, a))
825 setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
828 void CanvasRenderingContext2D::beginPath()
833 PassRefPtr<DOMPath> CanvasRenderingContext2D::currentPath()
835 return DOMPath::create(m_path);
838 void CanvasRenderingContext2D::setCurrentPath(DOMPath* path)
842 m_path = path->path();
845 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
847 if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height))
850 if (!width && !height)
866 static bool isFullCanvasCompositeMode(CompositeOperator op)
868 // See 4.8.11.1.3 Compositing
869 // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already
870 // implement the specification's behavior.
871 return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop;
874 static bool parseWinding(const String& windingRuleString, WindRule& windRule)
876 if (windingRuleString == "nonzero")
877 windRule = RULE_NONZERO;
878 else if (windingRuleString == "evenodd")
879 windRule = RULE_EVENODD;
886 void CanvasRenderingContext2D::fill(const String& windingRuleString)
888 GraphicsContext* c = drawingContext();
891 if (!state().m_invertibleCTM)
893 FloatRect clipBounds;
894 if (!drawingContext()->getTransformedClipBounds(&clipBounds))
897 // If gradient size is zero, then paint nothing.
898 Gradient* gradient = c->fillGradient();
899 if (gradient && gradient->isZeroSize())
902 if (!m_path.isEmpty()) {
903 WindRule windRule = c->fillRule();
904 WindRule newWindRule = RULE_NONZERO;
905 if (!parseWinding(windingRuleString, newWindRule))
907 c->setFillRule(newWindRule);
909 if (isFullCanvasCompositeMode(state().m_globalComposite)) {
910 fullCanvasCompositedFill(m_path);
912 } else if (state().m_globalComposite == CompositeCopy) {
918 if (computeDirtyRect(m_path.boundingRect(), clipBounds, &dirtyRect)) {
924 c->setFillRule(windRule);
928 void CanvasRenderingContext2D::stroke()
930 GraphicsContext* c = drawingContext();
933 if (!state().m_invertibleCTM)
936 // If gradient size is zero, then paint nothing.
937 Gradient* gradient = c->strokeGradient();
938 if (gradient && gradient->isZeroSize())
941 if (!m_path.isEmpty()) {
942 FloatRect bounds = m_path.boundingRect();
943 inflateStrokeRect(bounds);
945 if (computeDirtyRect(bounds, &dirtyRect)) {
946 c->strokePath(m_path);
952 void CanvasRenderingContext2D::clip(const String& windingRuleString)
954 GraphicsContext* c = drawingContext();
957 if (!state().m_invertibleCTM)
960 WindRule newWindRule = RULE_NONZERO;
961 if (!parseWinding(windingRuleString, newWindRule))
965 c->canvasClip(m_path, newWindRule);
968 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y, const String& windingRuleString)
970 GraphicsContext* c = drawingContext();
973 if (!state().m_invertibleCTM)
976 FloatPoint point(x, y);
977 AffineTransform ctm = state().m_transform;
978 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
979 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
982 WindRule windRule = RULE_NONZERO;
983 if (!parseWinding(windingRuleString, windRule))
986 return m_path.contains(transformedPoint, windRule);
990 bool CanvasRenderingContext2D::isPointInStroke(const float x, const float y)
992 GraphicsContext* c = drawingContext();
995 if (!state().m_invertibleCTM)
998 FloatPoint point(x, y);
999 AffineTransform ctm = state().m_transform;
1000 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
1001 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
1004 StrokeData strokeData;
1005 strokeData.setThickness(lineWidth());
1006 strokeData.setLineCap(getLineCap());
1007 strokeData.setLineJoin(getLineJoin());
1008 strokeData.setMiterLimit(miterLimit());
1009 strokeData.setLineDash(getLineDash(), lineDashOffset());
1010 return m_path.strokeContains(transformedPoint, strokeData);
1013 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
1015 if (!validateRectForCanvas(x, y, width, height))
1017 GraphicsContext* context = drawingContext();
1020 if (!state().m_invertibleCTM)
1022 FloatRect rect(x, y, width, height);
1024 FloatRect dirtyRect;
1025 if (!computeDirtyRect(rect, &dirtyRect))
1029 if (shouldDrawShadows()) {
1032 context->clearShadow();
1034 if (state().m_globalAlpha != 1) {
1039 context->setAlpha(1);
1041 if (state().m_globalComposite != CompositeSourceOver) {
1046 context->setCompositeOperation(CompositeSourceOver);
1048 context->clearRect(rect);
1055 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
1057 if (!validateRectForCanvas(x, y, width, height))
1060 GraphicsContext* c = drawingContext();
1063 if (!state().m_invertibleCTM)
1065 FloatRect clipBounds;
1066 if (!drawingContext()->getTransformedClipBounds(&clipBounds))
1069 // from the HTML5 Canvas spec:
1070 // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing
1071 // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing
1072 Gradient* gradient = c->fillGradient();
1073 if (gradient && gradient->isZeroSize())
1076 FloatRect rect(x, y, width, height);
1077 if (rectContainsTransformedRect(rect, clipBounds)) {
1079 didDraw(clipBounds);
1080 } else if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1081 fullCanvasCompositedFill(rect);
1082 didDraw(clipBounds);
1083 } else if (state().m_globalComposite == CompositeCopy) {
1086 didDraw(clipBounds);
1088 FloatRect dirtyRect;
1089 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) {
1096 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
1098 if (!validateRectForCanvas(x, y, width, height))
1101 if (!(state().m_lineWidth >= 0))
1104 GraphicsContext* c = drawingContext();
1107 if (!state().m_invertibleCTM)
1110 // If gradient size is zero, then paint nothing.
1111 Gradient* gradient = c->strokeGradient();
1112 if (gradient && gradient->isZeroSize())
1115 FloatRect rect(x, y, width, height);
1117 FloatRect boundingRect = rect;
1118 boundingRect.inflate(state().m_lineWidth / 2);
1119 FloatRect dirtyRect;
1120 if (computeDirtyRect(boundingRect, &dirtyRect)) {
1121 c->strokeRect(rect, state().m_lineWidth);
1126 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
1128 setShadow(FloatSize(width, height), blur, Color::transparent);
1131 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
1134 if (!parseColorOrCurrentColor(rgba, color, canvas()))
1136 setShadow(FloatSize(width, height), blur, rgba);
1139 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
1141 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1));
1144 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
1147 if (!parseColorOrCurrentColor(rgba, color, canvas()))
1149 setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(rgba, alpha));
1152 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
1154 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha));
1157 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
1159 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(r, g, b, a));
1162 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
1164 setShadow(FloatSize(width, height), blur, makeRGBAFromCMYKA(c, m, y, k, a));
1167 void CanvasRenderingContext2D::clearShadow()
1169 setShadow(FloatSize(), 0, Color::transparent);
1172 void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, RGBA32 color)
1174 if (state().m_shadowOffset == offset && state().m_shadowBlur == blur && state().m_shadowColor == color)
1176 bool wasDrawingShadows = shouldDrawShadows();
1178 modifiableState().m_shadowOffset = offset;
1179 modifiableState().m_shadowBlur = blur;
1180 modifiableState().m_shadowColor = color;
1181 if (!wasDrawingShadows && !shouldDrawShadows())
1186 void CanvasRenderingContext2D::applyShadow()
1188 GraphicsContext* c = drawingContext();
1192 if (shouldDrawShadows()) {
1193 c->setShadow(state().m_shadowOffset, state().m_shadowBlur, state().m_shadowColor,
1194 DrawLooper::ShadowIgnoresTransforms);
1200 bool CanvasRenderingContext2D::shouldDrawShadows() const
1202 return alphaChannel(state().m_shadowColor) && (state().m_shadowBlur || !state().m_shadowOffset.isZero());
1205 enum ImageSizeType {
1206 ImageSizeAfterDevicePixelRatio,
1207 ImageSizeBeforeDevicePixelRatio
1210 static LayoutSize sizeFor(HTMLImageElement* image, ImageSizeType sizeType)
1213 ImageResource* cachedImage = image->cachedImage();
1215 size = cachedImage->imageSizeForRenderer(image->renderer(), 1.0f); // FIXME: Not sure about this.
1217 if (sizeType == ImageSizeAfterDevicePixelRatio && image->renderer() && image->renderer()->isRenderImage() && cachedImage->image() && !cachedImage->image()->hasRelativeWidth())
1218 size.scale(toRenderImage(image->renderer())->imageDevicePixelRatio());
1223 static IntSize sizeFor(HTMLVideoElement* video)
1225 if (MediaPlayer* player = video->player())
1226 return player->naturalSize();
1230 static inline FloatRect normalizeRect(const FloatRect& rect)
1232 return FloatRect(min(rect.x(), rect.maxX()),
1233 min(rect.y(), rect.maxY()),
1234 max(rect.width(), -rect.width()),
1235 max(rect.height(), -rect.height()));
1238 static inline void clipRectsToImageRect(const FloatRect& imageRect, FloatRect* srcRect, FloatRect* dstRect)
1240 if (imageRect.contains(*srcRect))
1243 // Compute the src to dst transform
1244 FloatSize scale(dstRect->size().width() / srcRect->size().width(), dstRect->size().height() / srcRect->size().height());
1245 FloatPoint scaledSrcLocation = srcRect->location();
1246 scaledSrcLocation.scale(scale.width(), scale.height());
1247 FloatSize offset = dstRect->location() - scaledSrcLocation;
1249 srcRect->intersect(imageRect);
1251 // To clip the destination rectangle in the same proportion, transform the clipped src rect
1252 *dstRect = *srcRect;
1253 dstRect->scale(scale.width(), scale.height());
1254 dstRect->move(offset);
1257 void CanvasRenderingContext2D::drawImageInternal(Image* image, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const blink::WebBlendMode& blendMode)
1262 GraphicsContext* c = drawingContext();
1265 if (!state().m_invertibleCTM)
1267 FloatRect clipBounds;
1268 if (!drawingContext()->getTransformedClipBounds(&clipBounds))
1271 if (rectContainsTransformedRect(dstRect, clipBounds)) {
1272 c->drawImage(image, dstRect, srcRect, op, blendMode);
1273 didDraw(clipBounds);
1274 } else if (isFullCanvasCompositeMode(op)) {
1275 fullCanvasCompositedDrawImage(image, dstRect, srcRect, op);
1276 didDraw(clipBounds);
1277 } else if (op == CompositeCopy) {
1279 c->drawImage(image, dstRect, srcRect, op, blendMode);
1280 didDraw(clipBounds);
1282 FloatRect dirtyRect;
1283 if (computeDirtyRect(dstRect, &dirtyRect)) {
1284 c->drawImage(image, dstRect, srcRect, op, blendMode);
1290 void CanvasRenderingContext2D::drawImage(ImageBitmap* bitmap, float x, float y, ExceptionState& exceptionState)
1293 exceptionState.throwDOMException(TypeMismatchError, "The element provided is invalid.");
1296 drawImage(bitmap, x, y, bitmap->width(), bitmap->height(), exceptionState);
1299 void CanvasRenderingContext2D::drawImage(ImageBitmap* bitmap,
1300 float x, float y, float width, float height, ExceptionState& exceptionState)
1303 exceptionState.throwDOMException(TypeMismatchError, "The element provided is invalid.");
1306 if (!bitmap->bitmapRect().width() || !bitmap->bitmapRect().height())
1309 drawImage(bitmap, 0, 0, bitmap->width(), bitmap->height(), x, y, width, height, exceptionState);
1312 void CanvasRenderingContext2D::drawImage(ImageBitmap* bitmap,
1313 float sx, float sy, float sw, float sh,
1314 float dx, float dy, float dw, float dh, ExceptionState& exceptionState)
1317 exceptionState.throwDOMException(TypeMismatchError, "The element provided is invalid.");
1321 FloatRect srcRect(sx, sy, sw, sh);
1322 FloatRect dstRect(dx, dy, dw, dh);
1323 FloatRect bitmapRect = bitmap->bitmapRect();
1325 if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height())
1326 || !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height()))
1329 if (!dstRect.width() || !dstRect.height())
1331 if (!srcRect.width() || !srcRect.height()) {
1332 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", srcRect.width() ? "height" : "width"));
1336 ASSERT(bitmap->height() && bitmap->width());
1337 FloatRect normalizedSrcRect = normalizeRect(srcRect);
1338 FloatRect normalizedDstRect = normalizeRect(dstRect);
1340 // Clip the rects to where the user thinks that the image is situated.
1341 clipRectsToImageRect(IntRect(IntPoint(), bitmap->size()), &normalizedSrcRect, &normalizedDstRect);
1343 FloatRect intersectRect = intersection(bitmapRect, normalizedSrcRect);
1344 FloatRect actualSrcRect(intersectRect);
1346 IntPoint bitmapOffset = bitmap->bitmapOffset();
1347 actualSrcRect.move(bitmapOffset - bitmapRect.location());
1348 FloatRect imageRect = FloatRect(bitmapOffset, bitmapRect.size());
1350 FloatRect actualDstRect(FloatPoint(intersectRect.location() - normalizedSrcRect.location()), bitmapRect.size());
1351 actualDstRect.scale(normalizedDstRect.width() / normalizedSrcRect.width() * intersectRect.width() / bitmapRect.width(),
1352 normalizedDstRect.height() / normalizedSrcRect.height() * intersectRect.height() / bitmapRect.height());
1353 actualDstRect.moveBy(normalizedDstRect.location());
1355 if (!imageRect.intersects(actualSrcRect))
1358 RefPtr<Image> imageForRendering = bitmap->bitmapImage();
1359 if (!imageForRendering)
1362 drawImageInternal(imageForRendering.get(), actualSrcRect, actualDstRect, state().m_globalComposite, state().m_globalBlend);
1365 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y, ExceptionState& exceptionState)
1368 exceptionState.throwDOMException(TypeMismatchError, "The image element provided is invalid.");
1371 LayoutSize destRectSize = sizeFor(image, ImageSizeAfterDevicePixelRatio);
1372 drawImage(image, x, y, destRectSize.width(), destRectSize.height(), exceptionState);
1375 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
1376 float x, float y, float width, float height, ExceptionState& exceptionState)
1379 exceptionState.throwDOMException(TypeMismatchError, "The image element provided is invalid.");
1382 LayoutSize sourceRectSize = sizeFor(image, ImageSizeBeforeDevicePixelRatio);
1383 drawImage(image, FloatRect(0, 0, sourceRectSize.width(), sourceRectSize.height()), FloatRect(x, y, width, height), exceptionState);
1386 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
1387 float sx, float sy, float sw, float sh,
1388 float dx, float dy, float dw, float dh, ExceptionState& exceptionState)
1390 drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), exceptionState);
1393 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionState& exceptionState)
1395 drawImage(image, srcRect, dstRect, state().m_globalComposite, state().m_globalBlend, exceptionState);
1398 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const blink::WebBlendMode& blendMode, ExceptionState& exceptionState)
1401 exceptionState.throwDOMException(TypeMismatchError, "The image element provided is invalid.");
1405 if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height())
1406 || !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height()))
1409 ImageResource* cachedImage = image->cachedImage();
1410 if (!cachedImage || !image->complete())
1413 LayoutSize size = sizeFor(image, ImageSizeBeforeDevicePixelRatio);
1414 if (!size.width() || !size.height()) {
1415 exceptionState.throwDOMException(InvalidStateError, String::format("The source %s is 0.", size.width() ? "height" : "width"));
1419 if (!dstRect.width() || !dstRect.height())
1422 FloatRect normalizedSrcRect = normalizeRect(srcRect);
1423 FloatRect normalizedDstRect = normalizeRect(dstRect);
1425 FloatRect imageRect = FloatRect(FloatPoint(), size);
1426 if (!srcRect.width() || !srcRect.height()) {
1427 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", srcRect.width() ? "height" : "width"));
1430 if (!imageRect.intersects(normalizedSrcRect))
1433 clipRectsToImageRect(imageRect, &normalizedSrcRect, &normalizedDstRect);
1437 Image* imageForRendering = cachedImage->imageForRenderer(image->renderer());
1439 // For images that depend on an unavailable container size, we need to fall back to the intrinsic
1440 // object size. http://www.w3.org/TR/2dcontext2/#dom-context-2d-drawimage
1441 // FIXME: Without a specified image size this should resolve against the canvas element's size, see: crbug.com/230163.
1442 if (!image->renderer() && imageForRendering->usesContainerSize())
1443 imageForRendering->setContainerSize(imageForRendering->size());
1445 drawImageInternal(imageForRendering, normalizedSrcRect, normalizedDstRect, op, blendMode);
1448 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, float x, float y, ExceptionState& exceptionState)
1450 drawImage(sourceCanvas, 0, 0, sourceCanvas->width(), sourceCanvas->height(), x, y, sourceCanvas->width(), sourceCanvas->height(), exceptionState);
1453 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas,
1454 float x, float y, float width, float height, ExceptionState& exceptionState)
1456 drawImage(sourceCanvas, FloatRect(0, 0, sourceCanvas->width(), sourceCanvas->height()), FloatRect(x, y, width, height), exceptionState);
1459 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas,
1460 float sx, float sy, float sw, float sh,
1461 float dx, float dy, float dw, float dh, ExceptionState& exceptionState)
1463 drawImage(sourceCanvas, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), exceptionState);
1466 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const FloatRect& srcRect,
1467 const FloatRect& dstRect, ExceptionState& exceptionState)
1469 if (!sourceCanvas) {
1470 exceptionState.throwDOMException(TypeMismatchError, "The canvas element provided is invalid.");
1474 FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas->size());
1476 if (!srcCanvasRect.width() || !srcCanvasRect.height()) {
1477 exceptionState.throwDOMException(InvalidStateError, String::format("The source canvas %s is 0.", srcCanvasRect.width() ? "height" : "width"));
1481 if (!srcRect.width() || !srcRect.height()) {
1482 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", srcRect.width() ? "height" : "width"));
1486 FloatRect normalizedSrcRect = normalizeRect(srcRect);
1487 FloatRect normalizedDstRect = normalizeRect(dstRect);
1489 if (!srcCanvasRect.intersects(normalizedSrcRect) || !normalizedDstRect.width() || !normalizedDstRect.height())
1492 clipRectsToImageRect(srcCanvasRect, &normalizedSrcRect, &normalizedDstRect);
1494 GraphicsContext* c = drawingContext();
1497 if (!state().m_invertibleCTM)
1500 // FIXME: Do this through platform-independent GraphicsContext API.
1501 ImageBuffer* buffer = sourceCanvas->buffer();
1505 FloatRect clipBounds;
1506 if (!drawingContext()->getTransformedClipBounds(&clipBounds))
1509 checkOrigin(sourceCanvas);
1511 // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas->makeRenderingResultsAvailable()
1512 // as that will do a readback to software.
1513 CanvasRenderingContext* sourceContext = sourceCanvas->renderingContext();
1514 // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible.
1515 if (sourceContext && sourceContext->is3d())
1516 sourceContext->paintRenderingResultsToCanvas();
1518 if (rectContainsTransformedRect(normalizedDstRect, clipBounds)) {
1519 c->drawImageBuffer(buffer, normalizedDstRect, normalizedSrcRect, state().m_globalComposite, state().m_globalBlend);
1520 didDraw(clipBounds);
1521 } else if (isFullCanvasCompositeMode(state().m_globalComposite)) {
1522 fullCanvasCompositedDrawImage(buffer, normalizedDstRect, normalizedSrcRect, state().m_globalComposite);
1523 didDraw(clipBounds);
1524 } else if (state().m_globalComposite == CompositeCopy) {
1526 c->drawImageBuffer(buffer, normalizedDstRect, normalizedSrcRect, state().m_globalComposite, state().m_globalBlend);
1527 didDraw(clipBounds);
1529 FloatRect dirtyRect;
1530 if (computeDirtyRect(normalizedDstRect, clipBounds, &dirtyRect)) {
1531 c->drawImageBuffer(buffer, normalizedDstRect, normalizedSrcRect, state().m_globalComposite, state().m_globalBlend);
1536 // Flush canvas's ImageBuffer when drawImage from WebGL to HW accelerated 2d canvas
1537 if (sourceContext && sourceContext->is3d() && is2d() && isAccelerated() && canvas()->buffer())
1538 canvas()->buffer()->flush();
1541 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, float x, float y, ExceptionState& exceptionState)
1544 exceptionState.throwDOMException(TypeMismatchError, "The video element provided is invalid.");
1547 IntSize size = sizeFor(video);
1548 drawImage(video, x, y, size.width(), size.height(), exceptionState);
1551 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video,
1552 float x, float y, float width, float height, ExceptionState& exceptionState)
1555 exceptionState.throwDOMException(TypeMismatchError, "The video element provided is invalid.");
1558 IntSize size = sizeFor(video);
1559 drawImage(video, FloatRect(0, 0, size.width(), size.height()), FloatRect(x, y, width, height), exceptionState);
1562 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video,
1563 float sx, float sy, float sw, float sh,
1564 float dx, float dy, float dw, float dh, ExceptionState& exceptionState)
1566 drawImage(video, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), exceptionState);
1569 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionState& exceptionState)
1572 exceptionState.throwDOMException(TypeMismatchError, "The video element provided is invalid.");
1576 if (video->readyState() == HTMLMediaElement::HAVE_NOTHING || video->readyState() == HTMLMediaElement::HAVE_METADATA)
1579 FloatRect videoRect = FloatRect(FloatPoint(), sizeFor(video));
1580 if (!srcRect.width() || !srcRect.height()) {
1581 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", srcRect.width() ? "height" : "width"));
1585 FloatRect normalizedSrcRect = normalizeRect(srcRect);
1586 FloatRect normalizedDstRect = normalizeRect(dstRect);
1588 if (!videoRect.intersects(normalizedSrcRect) || !normalizedDstRect.width() || !normalizedDstRect.height())
1591 clipRectsToImageRect(videoRect, &normalizedSrcRect, &normalizedDstRect);
1593 GraphicsContext* c = drawingContext();
1596 if (!state().m_invertibleCTM)
1601 FloatRect dirtyRect;
1602 if (!computeDirtyRect(normalizedDstRect, &dirtyRect))
1605 GraphicsContextStateSaver stateSaver(*c);
1606 c->clip(normalizedDstRect);
1607 c->translate(normalizedDstRect.x(), normalizedDstRect.y());
1608 c->scale(FloatSize(normalizedDstRect.width() / normalizedSrcRect.width(), normalizedDstRect.height() / normalizedSrcRect.height()));
1609 c->translate(-normalizedSrcRect.x(), -normalizedSrcRect.y());
1610 video->paintCurrentFrameInContext(c, IntRect(IntPoint(), sizeFor(video)));
1611 stateSaver.restore();
1616 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1617 float sx, float sy, float sw, float sh,
1618 float dx, float dy, float dw, float dh,
1619 const String& compositeOperation)
1621 CompositeOperator op;
1622 blink::WebBlendMode blendOp = blink::WebBlendModeNormal;
1623 if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != blink::WebBlendModeNormal)
1624 op = CompositeSourceOver;
1626 drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), op, blink::WebBlendModeNormal, IGNORE_EXCEPTION);
1629 void CanvasRenderingContext2D::setAlpha(float alpha)
1631 setGlobalAlpha(alpha);
1634 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1636 setGlobalCompositeOperation(operation);
1639 void CanvasRenderingContext2D::clearCanvas()
1641 FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height());
1642 GraphicsContext* c = drawingContext();
1647 c->setCTM(canvas()->baseTransform());
1648 c->clearRect(canvasRect);
1652 bool CanvasRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect, const FloatRect& transformedRect) const
1654 FloatQuad quad(rect);
1655 FloatQuad transformedQuad(transformedRect);
1656 return state().m_transform.mapQuad(quad).containsQuad(transformedQuad);
1659 static void drawImageToContext(Image* image, GraphicsContext* context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1661 context->drawImage(image, dest, src, op);
1664 static void drawImageToContext(ImageBuffer* imageBuffer, GraphicsContext* context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1666 context->drawImageBuffer(imageBuffer, dest, src, op);
1669 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1671 ASSERT(isFullCanvasCompositeMode(op));
1673 drawingContext()->beginLayer(1, op);
1674 drawImageToContext(image, drawingContext(), dest, src, CompositeSourceOver);
1675 drawingContext()->endLayer();
1678 static void fillPrimitive(const FloatRect& rect, GraphicsContext* context)
1680 context->fillRect(rect);
1683 static void fillPrimitive(const Path& path, GraphicsContext* context)
1685 context->fillPath(path);
1688 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedFill(const T& area)
1690 ASSERT(isFullCanvasCompositeMode(state().m_globalComposite));
1692 GraphicsContext* c = drawingContext();
1694 c->beginLayer(1, state().m_globalComposite);
1695 CompositeOperator previousOperator = c->compositeOperation();
1696 c->setCompositeOperation(CompositeSourceOver);
1697 fillPrimitive(area, c);
1698 c->setCompositeOperation(previousOperator);
1702 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionState& exceptionState)
1704 if (!std::isfinite(x0))
1705 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(x0, "x0"));
1706 else if (!std::isfinite(y0))
1707 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(y0, "y0"));
1708 else if (!std::isfinite(x1))
1709 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(x1, "x1"));
1710 else if (!std::isfinite(y1))
1711 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(y1, "y1"));
1713 if (exceptionState.hadException())
1716 RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1717 return gradient.release();
1720 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionState& exceptionState)
1722 if (!std::isfinite(x0))
1723 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(x0, "x0"));
1724 else if (!std::isfinite(y0))
1725 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(y0, "y0"));
1726 else if (!std::isfinite(r0))
1727 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(r0, "r0"));
1728 else if (!std::isfinite(x1))
1729 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(x1, "x1"));
1730 else if (!std::isfinite(y1))
1731 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(y1, "y1"));
1732 else if (!std::isfinite(r1))
1733 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(r1, "r1"));
1734 else if (r0 < 0 || r1 < 0)
1735 exceptionState.throwDOMException(IndexSizeError, String::format("The %s provided is less than 0.", r0 < 0 ? "r0" : "r1"));
1737 if (exceptionState.hadException())
1740 RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1741 return gradient.release();
1744 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1745 const String& repetitionType, ExceptionState& exceptionState)
1748 exceptionState.throwDOMException(TypeMismatchError, "The image element provided is invalid.");
1751 bool repeatX, repeatY;
1752 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, exceptionState);
1753 if (exceptionState.hadException())
1756 if (!image->complete())
1759 ImageResource* cachedImage = image->cachedImage();
1760 Image* imageForRendering = cachedImage ? cachedImage->imageForRenderer(image->renderer()) : 0;
1761 if (!imageForRendering)
1762 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
1764 // We need to synthesize a container size if a renderer is not available to provide one.
1765 if (!image->renderer() && imageForRendering->usesContainerSize())
1766 imageForRendering->setContainerSize(imageForRendering->size());
1768 bool originClean = cachedImage->isAccessAllowed(canvas()->securityOrigin());
1769 return CanvasPattern::create(imageForRendering, repeatX, repeatY, originClean);
1772 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1773 const String& repetitionType, ExceptionState& exceptionState)
1776 exceptionState.throwDOMException(TypeMismatchError, "The canvas element provided is invalid.");
1777 else if (!canvas->width() || !canvas->height())
1778 exceptionState.throwDOMException(InvalidStateError, String::format("The canvas %s is 0.", canvas->width() ? "height" : "width"));
1780 if (exceptionState.hadException())
1783 bool repeatX, repeatY;
1784 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, exceptionState);
1785 if (exceptionState.hadException())
1787 return CanvasPattern::create(canvas->copiedImage(), repeatX, repeatY, canvas->originClean());
1790 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, FloatRect* dirtyRect)
1792 FloatRect clipBounds;
1793 if (!drawingContext()->getTransformedClipBounds(&clipBounds))
1795 return computeDirtyRect(localRect, clipBounds, dirtyRect);
1798 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, const FloatRect& transformedClipBounds, FloatRect* dirtyRect)
1800 FloatRect canvasRect = state().m_transform.mapRect(localRect);
1802 if (alphaChannel(state().m_shadowColor)) {
1803 FloatRect shadowRect(canvasRect);
1804 shadowRect.move(state().m_shadowOffset);
1805 shadowRect.inflate(state().m_shadowBlur);
1806 canvasRect.unite(shadowRect);
1809 canvasRect.intersect(transformedClipBounds);
1810 if (canvasRect.isEmpty())
1814 *dirtyRect = canvasRect;
1819 void CanvasRenderingContext2D::didDraw(const FloatRect& dirtyRect)
1821 if (dirtyRect.isEmpty())
1824 // If we are drawing to hardware and we have a composited layer, just call contentChanged().
1825 if (isAccelerated()) {
1826 RenderBox* renderBox = canvas()->renderBox();
1827 if (renderBox && renderBox->hasAcceleratedCompositing()) {
1828 renderBox->contentChanged(CanvasPixelsChanged);
1829 canvas()->clearCopiedImage();
1830 canvas()->notifyObserversCanvasChanged(dirtyRect);
1835 canvas()->didDraw(dirtyRect);
1838 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1840 return canvas()->drawingContext();
1843 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1845 Checked<int, RecordOverflow> dataSize = 4;
1846 dataSize *= size.width();
1847 dataSize *= size.height();
1848 if (dataSize.hasOverflowed())
1851 RefPtr<ImageData> data = ImageData::create(size);
1852 data->data()->zeroFill();
1853 return data.release();
1856 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtr<ImageData> imageData, ExceptionState& exceptionState) const
1859 exceptionState.throwDOMException(NotSupportedError, "The ImageData provided is invalid.");
1863 return createEmptyImageData(imageData->size());
1866 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionState& exceptionState) const
1869 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
1870 else if (!std::isfinite(sw))
1871 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sw, "source width"));
1872 else if (!std::isfinite(sh))
1873 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sh, "source height"));
1875 if (exceptionState.hadException())
1878 FloatSize logicalSize(fabs(sw), fabs(sh));
1879 if (!logicalSize.isExpressibleAsIntSize())
1882 IntSize size = expandedIntSize(logicalSize);
1883 if (size.width() < 1)
1885 if (size.height() < 1)
1888 return createEmptyImageData(size);
1891 PassRefPtr<ImageData> CanvasRenderingContext2D::webkitGetImageDataHD(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const
1893 return getImageData(sx, sy, sw, sh, exceptionState);
1896 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const
1898 if (!canvas()->originClean())
1899 exceptionState.throwSecurityError("The canvas has been tainted by cross-origin data.");
1900 else if (!sw || !sh)
1901 exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
1902 else if (!std::isfinite(sx))
1903 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sx, "source X"));
1904 else if (!std::isfinite(sy))
1905 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sy, "source Y"));
1906 else if (!std::isfinite(sw))
1907 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sw, "source width"));
1908 else if (!std::isfinite(sh))
1909 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sh, "source height"));
1911 if (exceptionState.hadException())
1923 FloatRect logicalRect(sx, sy, sw, sh);
1924 if (logicalRect.width() < 1)
1925 logicalRect.setWidth(1);
1926 if (logicalRect.height() < 1)
1927 logicalRect.setHeight(1);
1928 if (!logicalRect.isExpressibleAsIntRect())
1931 IntRect imageDataRect = enclosingIntRect(logicalRect);
1932 ImageBuffer* buffer = canvas()->buffer();
1934 return createEmptyImageData(imageDataRect.size());
1936 RefPtr<Uint8ClampedArray> byteArray = buffer->getUnmultipliedImageData(imageDataRect);
1940 return ImageData::create(imageDataRect.size(), byteArray.release());
1943 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionState& exceptionState)
1946 exceptionState.throwDOMException(TypeMismatchError, "The ImageData provided is invalid.");
1949 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), exceptionState);
1952 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
1953 float dirtyWidth, float dirtyHeight, ExceptionState& exceptionState)
1956 exceptionState.throwDOMException(TypeMismatchError, "The ImageData provided is invalid.");
1957 else if (!std::isfinite(dx))
1958 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dx, "dx"));
1959 else if (!std::isfinite(dy))
1960 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dy, "dy"));
1961 else if (!std::isfinite(dirtyX))
1962 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dirtyX, "dirtyX"));
1963 else if (!std::isfinite(dirtyY))
1964 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dirtyY, "dirtyY"));
1965 else if (!std::isfinite(dirtyWidth))
1966 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dirtyWidth, "dirtyWidth"));
1967 else if (!std::isfinite(dirtyHeight))
1968 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dirtyHeight, "dirtyHeight"));
1970 if (exceptionState.hadException())
1973 ImageBuffer* buffer = canvas()->buffer();
1977 if (dirtyWidth < 0) {
1978 dirtyX += dirtyWidth;
1979 dirtyWidth = -dirtyWidth;
1982 if (dirtyHeight < 0) {
1983 dirtyY += dirtyHeight;
1984 dirtyHeight = -dirtyHeight;
1987 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1988 clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1989 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1990 IntRect destRect = enclosingIntRect(clipRect);
1991 destRect.move(destOffset);
1992 destRect.intersect(IntRect(IntPoint(), buffer->size()));
1993 if (destRect.isEmpty())
1995 IntRect sourceRect(destRect);
1996 sourceRect.move(-destOffset);
1998 buffer->putByteArray(Unmultiplied, data->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset));
2003 String CanvasRenderingContext2D::font() const
2005 if (!state().m_realizedFont)
2008 StringBuilder serializedFont;
2009 const FontDescription& fontDescription = state().m_font.fontDescription();
2011 if (fontDescription.italic())
2012 serializedFont.appendLiteral("italic ");
2013 if (fontDescription.weight() == FontWeightBold)
2014 serializedFont.appendLiteral("bold ");
2015 if (fontDescription.smallCaps() == FontSmallCapsOn)
2016 serializedFont.appendLiteral("small-caps ");
2018 serializedFont.appendNumber(fontDescription.computedPixelSize());
2019 serializedFont.appendLiteral("px");
2021 const FontFamily& firstFontFamily = fontDescription.family();
2022 for (const FontFamily* fontFamily = &firstFontFamily; fontFamily; fontFamily = fontFamily->next()) {
2023 if (fontFamily != &firstFontFamily)
2024 serializedFont.append(',');
2026 // FIXME: We should append family directly to serializedFont rather than building a temporary string.
2027 String family = fontFamily->family();
2028 if (family.startsWith("-webkit-"))
2029 family = family.substring(8);
2030 if (family.contains(' '))
2031 family = "\"" + family + "\"";
2033 serializedFont.append(' ');
2034 serializedFont.append(family);
2037 return serializedFont.toString();
2040 void CanvasRenderingContext2D::setFont(const String& newFont)
2042 MutableStylePropertyMap::iterator i = m_fetchedFonts.find(newFont);
2043 RefPtr<MutableStylePropertySet> parsedStyle = i != m_fetchedFonts.end() ? i->value : 0;
2046 parsedStyle = MutableStylePropertySet::create();
2047 CSSParserMode mode = m_usesCSSCompatibilityParseMode ? HTMLQuirksMode : HTMLStandardMode;
2048 BisonCSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, newFont, true, mode, 0);
2049 m_fetchedFonts.add(newFont, parsedStyle);
2051 if (parsedStyle->isEmpty())
2054 String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
2056 // According to http://lists.w3.org/Archives/Public/public-html/2009Jul/0947.html,
2057 // the "inherit" and "initial" values must be ignored.
2058 if (fontValue == "inherit" || fontValue == "initial")
2061 // The parse succeeded.
2062 String newFontSafeCopy(newFont); // Create a string copy since newFont can be deleted inside realizeSaves.
2064 modifiableState().m_unparsedFont = newFontSafeCopy;
2066 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
2067 // relative to the canvas.
2068 RefPtr<RenderStyle> newStyle = RenderStyle::create();
2069 if (RenderStyle* computedStyle = canvas()->computedStyle())
2070 newStyle->setFontDescription(computedStyle->fontDescription());
2072 FontFamily fontFamily;
2073 fontFamily.setFamily(defaultFontFamily);
2075 FontDescription defaultFontDescription;
2076 defaultFontDescription.setFamily(fontFamily);
2077 defaultFontDescription.setSpecifiedSize(defaultFontSize);
2078 defaultFontDescription.setComputedSize(defaultFontSize);
2080 newStyle->setFontDescription(defaultFontDescription);
2083 newStyle->font().update(newStyle->font().fontSelector());
2085 // Now map the font property longhands into the style.
2086 CSSPropertyValue properties[] = {
2087 CSSPropertyValue(CSSPropertyFontFamily, *parsedStyle),
2088 CSSPropertyValue(CSSPropertyFontStyle, *parsedStyle),
2089 CSSPropertyValue(CSSPropertyFontVariant, *parsedStyle),
2090 CSSPropertyValue(CSSPropertyFontWeight, *parsedStyle),
2091 CSSPropertyValue(CSSPropertyFontSize, *parsedStyle),
2092 CSSPropertyValue(CSSPropertyLineHeight, *parsedStyle),
2095 StyleResolver& styleResolver = canvas()->document().ensureStyleResolver();
2096 styleResolver.applyPropertiesToStyle(properties, WTF_ARRAY_LENGTH(properties), newStyle.get());
2098 if (state().m_realizedFont)
2099 static_cast<CSSFontSelector*>(state().m_font.fontSelector())->unregisterForInvalidationCallbacks(&modifiableState());
2100 modifiableState().m_font = newStyle->font();
2101 modifiableState().m_font.update(canvas()->document().styleEngine()->fontSelector());
2102 modifiableState().m_realizedFont = true;
2103 canvas()->document().styleEngine()->fontSelector()->registerForInvalidationCallbacks(&modifiableState());
2106 String CanvasRenderingContext2D::textAlign() const
2108 return textAlignName(state().m_textAlign);
2111 void CanvasRenderingContext2D::setTextAlign(const String& s)
2114 if (!parseTextAlign(s, align))
2116 if (state().m_textAlign == align)
2119 modifiableState().m_textAlign = align;
2122 String CanvasRenderingContext2D::textBaseline() const
2124 return textBaselineName(state().m_textBaseline);
2127 void CanvasRenderingContext2D::setTextBaseline(const String& s)
2129 TextBaseline baseline;
2130 if (!parseTextBaseline(s, baseline))
2132 if (state().m_textBaseline == baseline)
2135 modifiableState().m_textBaseline = baseline;
2138 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
2140 drawTextInternal(text, x, y, true);
2143 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
2145 drawTextInternal(text, x, y, true, maxWidth, true);
2148 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
2150 drawTextInternal(text, x, y, false);
2153 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
2155 drawTextInternal(text, x, y, false, maxWidth, true);
2158 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
2160 FontCachePurgePreventer fontCachePurgePreventer;
2161 RefPtr<TextMetrics> metrics = TextMetrics::create();
2162 canvas()->document().updateStyleIfNeeded();
2163 metrics->setWidth(accessFont().width(TextRun(text)));
2164 return metrics.release();
2167 static void replaceCharacterInString(String& text, WTF::CharacterMatchFunctionPtr matchFunction, const String& replacement)
2169 const size_t replacementLength = replacement.length();
2171 while ((index = text.find(matchFunction, index)) != kNotFound) {
2172 text.replace(index, 1, replacement);
2173 index += replacementLength;
2177 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth, bool useMaxWidth)
2179 // accessFont needs the style to be up to date, but updating style can cause script to run,
2180 // (e.g. due to autofocus) which can free the GraphicsContext, so update style before grabbing
2181 // the GraphicsContext.
2182 canvas()->document().updateStyleIfNeeded();
2184 GraphicsContext* c = drawingContext();
2187 if (!state().m_invertibleCTM)
2189 if (!std::isfinite(x) | !std::isfinite(y))
2191 if (useMaxWidth && (!std::isfinite(maxWidth) || maxWidth <= 0))
2194 // If gradient size is zero, then paint nothing.
2195 Gradient* gradient = c->strokeGradient();
2196 if (!fill && gradient && gradient->isZeroSize())
2199 gradient = c->fillGradient();
2200 if (fill && gradient && gradient->isZeroSize())
2203 FontCachePurgePreventer fontCachePurgePreventer;
2205 const Font& font = accessFont();
2206 const FontMetrics& fontMetrics = font.fontMetrics();
2207 // According to spec, all the space characters must be replaced with U+0020 SPACE characters.
2208 String normalizedText = text;
2209 replaceCharacterInString(normalizedText, isSpaceOrNewline, " ");
2211 // FIXME: Need to turn off font smoothing.
2213 RenderStyle* computedStyle = canvas()->computedStyle();
2214 TextDirection direction = computedStyle ? computedStyle->direction() : LTR;
2215 bool isRTL = direction == RTL;
2216 bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : false;
2218 TextRun textRun(normalizedText, 0, 0, TextRun::AllowTrailingExpansion, direction, override, true, TextRun::NoRounding);
2219 // Draw the item text at the correct point.
2220 FloatPoint location(x, y);
2221 switch (state().m_textBaseline) {
2222 case TopTextBaseline:
2223 case HangingTextBaseline:
2224 location.setY(y + fontMetrics.ascent());
2226 case BottomTextBaseline:
2227 case IdeographicTextBaseline:
2228 location.setY(y - fontMetrics.descent());
2230 case MiddleTextBaseline:
2231 location.setY(y - fontMetrics.descent() + fontMetrics.height() / 2);
2233 case AlphabeticTextBaseline:
2239 float fontWidth = font.width(TextRun(normalizedText, 0, 0, TextRun::AllowTrailingExpansion, direction, override));
2241 useMaxWidth = (useMaxWidth && maxWidth < fontWidth);
2242 float width = useMaxWidth ? maxWidth : fontWidth;
2244 TextAlign align = state().m_textAlign;
2245 if (align == StartTextAlign)
2246 align = isRTL ? RightTextAlign : LeftTextAlign;
2247 else if (align == EndTextAlign)
2248 align = isRTL ? LeftTextAlign : RightTextAlign;
2251 case CenterTextAlign:
2252 location.setX(location.x() - width / 2);
2254 case RightTextAlign:
2255 location.setX(location.x() - width);
2261 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
2262 TextRunPaintInfo textRunPaintInfo(textRun);
2263 textRunPaintInfo.bounds = FloatRect(location.x() - fontMetrics.height() / 2,
2264 location.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
2265 width + fontMetrics.height(),
2266 fontMetrics.lineSpacing());
2268 inflateStrokeRect(textRunPaintInfo.bounds);
2270 FloatRect dirtyRect;
2271 if (!computeDirtyRect(textRunPaintInfo.bounds, &dirtyRect))
2274 c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
2276 GraphicsContextStateSaver stateSaver(*c);
2277 c->translate(location.x(), location.y());
2278 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work.
2279 c->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1));
2280 c->drawBidiText(font, textRunPaintInfo, FloatPoint(0, 0), Font::UseFallbackIfFontNotReady);
2282 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
2287 void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
2289 // Fast approximation of the stroke's bounding rect.
2290 // This yields a slightly oversized rect but is very fast
2291 // compared to Path::strokeBoundingRect().
2292 static const float root2 = sqrtf(2);
2293 float delta = state().m_lineWidth / 2;
2294 if (state().m_lineJoin == MiterJoin)
2295 delta *= state().m_miterLimit;
2296 else if (state().m_lineCap == SquareCap)
2299 rect.inflate(delta);
2302 const Font& CanvasRenderingContext2D::accessFont()
2304 // This needs style to be up to date, but can't assert so because drawTextInternal
2305 // can invalidate style before this is called (e.g. drawingContext invalidates style).
2306 if (!state().m_realizedFont)
2307 setFont(state().m_unparsedFont);
2308 return state().m_font;
2311 blink::WebLayer* CanvasRenderingContext2D::platformLayer() const
2313 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0;
2316 bool CanvasRenderingContext2D::imageSmoothingEnabled() const
2318 return state().m_imageSmoothingEnabled;
2321 void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled)
2323 if (enabled == state().m_imageSmoothingEnabled)
2327 modifiableState().m_imageSmoothingEnabled = enabled;
2328 GraphicsContext* c = drawingContext();
2330 c->setImageInterpolationQuality(enabled ? DefaultInterpolationQuality : InterpolationNone);
2333 PassRefPtr<Canvas2DContextAttributes> CanvasRenderingContext2D::getContextAttributes() const
2335 RefPtr<Canvas2DContextAttributes> attributes = Canvas2DContextAttributes::create();
2336 attributes->setAlpha(m_hasAlpha);
2337 return attributes.release();
2340 void CanvasRenderingContext2D::drawSystemFocusRing(Element* element)
2342 if (!focusRingCallIsValid(m_path, element))
2345 updateFocusRingAccessibility(m_path, element);
2346 // Note: we need to check document->focusedElement() rather than just calling
2347 // element->focused(), because element->focused() isn't updated until after
2348 // focus events fire.
2349 if (element->document().focusedElement() == element)
2350 drawFocusRing(m_path);
2353 bool CanvasRenderingContext2D::drawCustomFocusRing(Element* element)
2355 if (!focusRingCallIsValid(m_path, element))
2358 updateFocusRingAccessibility(m_path, element);
2360 // Return true if the application should draw the focus ring. The spec allows us to
2361 // override this for accessibility, but currently Blink doesn't take advantage of this.
2362 return element->focused();
2365 bool CanvasRenderingContext2D::focusRingCallIsValid(const Path& path, Element* element)
2367 if (!state().m_invertibleCTM)
2371 if (!element->isDescendantOf(canvas()))
2377 void CanvasRenderingContext2D::updateFocusRingAccessibility(const Path& path, Element* element)
2379 if (!canvas()->renderer())
2382 // If accessibility is already enabled in this frame, associate this path's
2383 // bounding box with the accessible object. Do this even if the element
2384 // isn't focused because assistive technology might try to explore the object's
2385 // location before it gets focus.
2386 if (AXObjectCache* axObjectCache = element->document().existingAXObjectCache()) {
2387 if (AXObject* obj = axObjectCache->getOrCreate(element)) {
2388 // Get the bounding rect and apply transformations.
2389 FloatRect bounds = m_path.boundingRect();
2390 AffineTransform ctm = state().m_transform;
2391 FloatRect transformedBounds = ctm.mapRect(bounds);
2392 LayoutRect elementRect = LayoutRect(transformedBounds);
2394 // Offset by the canvas rect and set the bounds of the accessible element.
2395 IntRect canvasRect = canvas()->renderer()->absoluteBoundingBoxRect();
2396 elementRect.moveBy(canvasRect.location());
2397 obj->setElementRect(elementRect);
2399 // Set the bounds of any ancestor accessible elements, up to the canvas element,
2400 // otherwise this element will appear to not be within its parent element.
2401 obj = obj->parentObject();
2402 while (obj && obj->node() != canvas()) {
2403 obj->setElementRect(elementRect);
2404 obj = obj->parentObject();
2410 void CanvasRenderingContext2D::drawFocusRing(const Path& path)
2412 GraphicsContext* c = drawingContext();
2416 FloatRect dirtyRect;
2417 if (!computeDirtyRect(path.boundingRect(), &dirtyRect))
2423 c->setCompositeOperation(CompositeSourceOver, blink::WebBlendModeNormal);
2425 // These should match the style defined in html.css.
2426 Color focusRingColor = RenderTheme::theme().focusRingColor();
2427 const int focusRingWidth = 5;
2428 const int focusRingOutline = 0;
2429 c->drawFocusRing(path, focusRingWidth, focusRingOutline, focusRingColor);
2436 } // namespace WebCore