2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 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>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "CanvasRenderingContext2D.h"
33 #include "TransformationMatrix.h"
34 #include "CSSParser.h"
35 #include "CachedImage.h"
36 #include "CanvasGradient.h"
37 #include "CanvasPattern.h"
38 #include "CanvasStyle.h"
39 #include "CSSMutableStyleDeclaration.h"
40 #include "CSSPropertyNames.h"
41 #include "CSSStyleSelector.h"
43 #include "ExceptionCode.h"
44 #include "FloatConversion.h"
46 #include "GraphicsContext.h"
47 #include "HTMLCanvasElement.h"
48 #include "HTMLImageElement.h"
49 #include "HTMLNames.h"
50 #include "ImageBuffer.h"
51 #include "ImageData.h"
54 #include "RenderHTMLCanvas.h"
55 #include "SecurityOrigin.h"
57 #include "StrokeStyleApplier.h"
58 #include "TextMetrics.h"
59 #include "HTMLVideoElement.h"
61 #include <wtf/ByteArray.h>
62 #include <wtf/MathExtras.h>
63 #include <wtf/OwnPtr.h>
64 #include <wtf/UnusedParam.h>
70 using namespace HTMLNames;
72 static const char* const defaultFont = "10px sans-serif";
75 class CanvasStrokeStyleApplier : public StrokeStyleApplier {
77 CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
78 : m_canvasContext(canvasContext)
82 virtual void strokeStyle(GraphicsContext* c)
84 c->setStrokeThickness(m_canvasContext->lineWidth());
85 c->setLineCap(m_canvasContext->getLineCap());
86 c->setLineJoin(m_canvasContext->getLineJoin());
87 c->setMiterLimit(m_canvasContext->miterLimit());
91 CanvasRenderingContext2D* m_canvasContext;
96 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
100 // Make sure that even if the drawingContext() has a different default
101 // thickness, it is in sync with the canvas thickness.
102 setLineWidth(lineWidth());
105 void CanvasRenderingContext2D::ref()
110 void CanvasRenderingContext2D::deref()
115 void CanvasRenderingContext2D::reset()
117 m_stateStack.resize(1);
118 m_stateStack.first() = State();
121 CanvasRenderingContext2D::State::State()
122 : m_strokeStyle(CanvasStyle::create("black"))
123 , m_fillStyle(CanvasStyle::create("black"))
126 , m_lineJoin(MiterJoin)
129 , m_shadowColor("black")
131 , m_globalComposite(CompositeSourceOver)
132 , m_invertibleCTM(true)
133 , m_textAlign(StartTextAlign)
134 , m_textBaseline(AlphabeticTextBaseline)
135 , m_unparsedFont(defaultFont)
136 , m_realizedFont(false)
140 void CanvasRenderingContext2D::save()
142 ASSERT(m_stateStack.size() >= 1);
143 m_stateStack.append(state());
144 GraphicsContext* c = drawingContext();
150 void CanvasRenderingContext2D::restore()
152 ASSERT(m_stateStack.size() >= 1);
153 if (m_stateStack.size() <= 1)
155 m_path.transform(state().m_transform);
156 m_stateStack.removeLast();
157 m_path.transform(state().m_transform.inverse());
158 GraphicsContext* c = drawingContext();
164 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
166 return state().m_strokeStyle.get();
169 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
174 if (m_canvas->originClean()) {
175 if (CanvasPattern* pattern = style->canvasPattern()) {
176 if (!pattern->originClean())
177 m_canvas->setOriginTainted();
181 state().m_strokeStyle = style;
182 GraphicsContext* c = drawingContext();
185 state().m_strokeStyle->applyStrokeColor(c);
188 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
190 return state().m_fillStyle.get();
193 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
198 if (m_canvas->originClean()) {
199 if (CanvasPattern* pattern = style->canvasPattern()) {
200 if (!pattern->originClean())
201 m_canvas->setOriginTainted();
205 state().m_fillStyle = style;
206 GraphicsContext* c = drawingContext();
209 state().m_fillStyle->applyFillColor(c);
212 float CanvasRenderingContext2D::lineWidth() const
214 return state().m_lineWidth;
217 void CanvasRenderingContext2D::setLineWidth(float width)
221 state().m_lineWidth = width;
222 GraphicsContext* c = drawingContext();
225 c->setStrokeThickness(width);
228 String CanvasRenderingContext2D::lineCap() const
230 return lineCapName(state().m_lineCap);
233 void CanvasRenderingContext2D::setLineCap(const String& s)
236 if (!parseLineCap(s, cap))
238 state().m_lineCap = cap;
239 GraphicsContext* c = drawingContext();
245 String CanvasRenderingContext2D::lineJoin() const
247 return lineJoinName(state().m_lineJoin);
250 void CanvasRenderingContext2D::setLineJoin(const String& s)
253 if (!parseLineJoin(s, join))
255 state().m_lineJoin = join;
256 GraphicsContext* c = drawingContext();
259 c->setLineJoin(join);
262 float CanvasRenderingContext2D::miterLimit() const
264 return state().m_miterLimit;
267 void CanvasRenderingContext2D::setMiterLimit(float limit)
271 state().m_miterLimit = limit;
272 GraphicsContext* c = drawingContext();
275 c->setMiterLimit(limit);
278 float CanvasRenderingContext2D::shadowOffsetX() const
280 return state().m_shadowOffset.width();
283 void CanvasRenderingContext2D::setShadowOffsetX(float x)
285 state().m_shadowOffset.setWidth(x);
289 float CanvasRenderingContext2D::shadowOffsetY() const
291 return state().m_shadowOffset.height();
294 void CanvasRenderingContext2D::setShadowOffsetY(float y)
296 state().m_shadowOffset.setHeight(y);
300 float CanvasRenderingContext2D::shadowBlur() const
302 return state().m_shadowBlur;
305 void CanvasRenderingContext2D::setShadowBlur(float blur)
307 state().m_shadowBlur = blur;
311 String CanvasRenderingContext2D::shadowColor() const
313 // FIXME: What should this return if you called setShadow with a non-string color?
314 return state().m_shadowColor;
317 void CanvasRenderingContext2D::setShadowColor(const String& color)
319 state().m_shadowColor = color;
323 float CanvasRenderingContext2D::globalAlpha() const
325 return state().m_globalAlpha;
328 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
330 if (!(alpha >= 0 && alpha <= 1))
332 state().m_globalAlpha = alpha;
333 GraphicsContext* c = drawingContext();
339 String CanvasRenderingContext2D::globalCompositeOperation() const
341 return compositeOperatorName(state().m_globalComposite);
344 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
346 CompositeOperator op;
347 if (!parseCompositeOperator(operation, op))
349 state().m_globalComposite = op;
350 GraphicsContext* c = drawingContext();
353 c->setCompositeOperation(op);
356 void CanvasRenderingContext2D::scale(float sx, float sy)
358 GraphicsContext* c = drawingContext();
361 if (!state().m_invertibleCTM)
364 TransformationMatrix newTransform = state().m_transform;
365 newTransform.scaleNonUniform(sx, sy);
366 if (!newTransform.isInvertible()) {
367 state().m_invertibleCTM = false;
371 state().m_transform = newTransform;
372 c->scale(FloatSize(sx, sy));
373 m_path.transform(TransformationMatrix().scaleNonUniform(1.0/sx, 1.0/sy));
376 void CanvasRenderingContext2D::rotate(float angleInRadians)
378 GraphicsContext* c = drawingContext();
381 if (!state().m_invertibleCTM)
384 TransformationMatrix newTransform = state().m_transform;
385 newTransform.rotate(angleInRadians / piDouble * 180.0);
386 if (!newTransform.isInvertible()) {
387 state().m_invertibleCTM = false;
391 state().m_transform = newTransform;
392 c->rotate(angleInRadians);
393 m_path.transform(TransformationMatrix().rotate(-angleInRadians / piDouble * 180.0));
396 void CanvasRenderingContext2D::translate(float tx, float ty)
398 GraphicsContext* c = drawingContext();
401 if (!state().m_invertibleCTM)
404 TransformationMatrix newTransform = state().m_transform;
405 newTransform.translate(tx, ty);
406 if (!newTransform.isInvertible()) {
407 state().m_invertibleCTM = false;
411 state().m_transform = newTransform;
412 c->translate(tx, ty);
413 m_path.transform(TransformationMatrix().translate(-tx, -ty));
416 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
418 GraphicsContext* c = drawingContext();
421 if (!state().m_invertibleCTM)
424 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
425 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
426 !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
429 TransformationMatrix transform(m11, m12, m21, m22, dx, dy);
430 TransformationMatrix newTransform = transform * state().m_transform;
431 if (!newTransform.isInvertible()) {
432 state().m_invertibleCTM = false;
436 state().m_transform = newTransform;
437 c->concatCTM(transform);
438 m_path.transform(transform.inverse());
441 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
443 GraphicsContext* c = drawingContext();
447 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
448 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
449 !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
452 TransformationMatrix ctm = state().m_transform;
453 if (!ctm.isInvertible())
455 c->concatCTM(c->getCTM().inverse());
456 c->concatCTM(m_canvas->baseTransform());
457 state().m_transform.multiply(ctm.inverse());
458 m_path.transform(ctm);
460 state().m_invertibleCTM = true;
461 transform(m11, m12, m21, m22, dx, dy);
464 void CanvasRenderingContext2D::setStrokeColor(const String& color)
466 setStrokeStyle(CanvasStyle::create(color));
469 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
471 setStrokeStyle(CanvasStyle::create(grayLevel, 1));
474 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
476 setStrokeStyle(CanvasStyle::create(color, alpha));
479 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
481 setStrokeStyle(CanvasStyle::create(grayLevel, alpha));
484 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
486 setStrokeStyle(CanvasStyle::create(r, g, b, a));
489 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
491 setStrokeStyle(CanvasStyle::create(c, m, y, k, a));
494 void CanvasRenderingContext2D::setFillColor(const String& color)
496 setFillStyle(CanvasStyle::create(color));
499 void CanvasRenderingContext2D::setFillColor(float grayLevel)
501 setFillStyle(CanvasStyle::create(grayLevel, 1));
504 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
506 setFillStyle(CanvasStyle::create(color, alpha));
509 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
511 setFillStyle(CanvasStyle::create(grayLevel, alpha));
514 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
516 setFillStyle(CanvasStyle::create(r, g, b, a));
519 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
521 setFillStyle(CanvasStyle::create(c, m, y, k, a));
524 void CanvasRenderingContext2D::beginPath()
529 void CanvasRenderingContext2D::closePath()
531 m_path.closeSubpath();
534 void CanvasRenderingContext2D::moveTo(float x, float y)
536 if (!isfinite(x) | !isfinite(y))
538 if (!state().m_invertibleCTM)
540 m_path.moveTo(FloatPoint(x, y));
543 void CanvasRenderingContext2D::lineTo(float x, float y)
545 if (!isfinite(x) | !isfinite(y))
547 if (!state().m_invertibleCTM)
549 if (!m_path.hasCurrentPoint())
550 m_path.moveTo(FloatPoint(x, y));
551 m_path.addLineTo(FloatPoint(x, y));
554 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
556 if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y))
558 if (!state().m_invertibleCTM)
560 if (!m_path.hasCurrentPoint())
561 m_path.moveTo(FloatPoint(cpx, cpy));
562 m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
565 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
567 if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y))
569 if (!state().m_invertibleCTM)
571 if (!m_path.hasCurrentPoint())
572 m_path.moveTo(FloatPoint(cp1x, cp1y));
573 m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
576 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
579 if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinite(r))
586 if (!state().m_invertibleCTM)
588 m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
591 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
594 if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea))
601 if (!state().m_invertibleCTM)
603 m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
606 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
608 if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height))
624 void CanvasRenderingContext2D::rect(float x, float y, float width, float height)
626 if (!validateRectForCanvas(x, y, width, height))
628 if (!state().m_invertibleCTM)
630 m_path.addRect(FloatRect(x, y, width, height));
633 #if ENABLE(DASHBOARD_SUPPORT)
634 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
636 if (Settings* settings = m_canvas->document()->settings())
637 if (settings->usesDashboardBackwardCompatibilityMode())
642 void CanvasRenderingContext2D::fill()
644 GraphicsContext* c = drawingContext();
647 if (!state().m_invertibleCTM)
650 if (!m_path.isEmpty()) {
653 willDraw(m_path.boundingRect());
657 #if ENABLE(DASHBOARD_SUPPORT)
658 clearPathForDashboardBackwardCompatibilityMode();
662 void CanvasRenderingContext2D::stroke()
664 GraphicsContext* c = drawingContext();
667 if (!state().m_invertibleCTM)
670 if (!m_path.isEmpty()) {
674 CanvasStrokeStyleApplier strokeApplier(this);
675 FloatRect boundingRect = m_path.strokeBoundingRect(&strokeApplier);
676 willDraw(boundingRect);
681 #if ENABLE(DASHBOARD_SUPPORT)
682 clearPathForDashboardBackwardCompatibilityMode();
686 void CanvasRenderingContext2D::clip()
688 GraphicsContext* c = drawingContext();
691 if (!state().m_invertibleCTM)
694 #if ENABLE(DASHBOARD_SUPPORT)
695 clearPathForDashboardBackwardCompatibilityMode();
699 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
701 GraphicsContext* c = drawingContext();
704 if (!state().m_invertibleCTM)
707 FloatPoint point(x, y);
708 TransformationMatrix ctm = state().m_transform;
709 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
710 return m_path.contains(transformedPoint);
713 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
715 if (!validateRectForCanvas(x, y, width, height))
717 GraphicsContext* c = drawingContext();
720 if (!state().m_invertibleCTM)
722 FloatRect rect(x, y, width, height);
727 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
729 if (!validateRectForCanvas(x, y, width, height))
732 GraphicsContext* c = drawingContext();
735 if (!state().m_invertibleCTM)
738 FloatRect rect(x, y, width, height);
746 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
748 if (!validateRectForCanvas(x, y, width, height))
750 strokeRect(x, y, width, height, state().m_lineWidth);
753 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth)
755 if (!validateRectForCanvas(x, y, width, height))
758 if (!(lineWidth >= 0))
761 GraphicsContext* c = drawingContext();
764 if (!state().m_invertibleCTM)
767 FloatRect rect(x, y, width, height);
769 FloatRect boundingRect = rect;
770 boundingRect.inflate(lineWidth / 2);
771 willDraw(boundingRect);
773 c->strokeRect(rect, lineWidth);
777 static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height)
779 // Work around <rdar://problem/5539388> by ensuring that shadow offsets will get truncated
780 // to the desired integer.
781 static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
783 width += extraShadowOffset;
785 width -= extraShadowOffset;
788 height += extraShadowOffset;
790 height -= extraShadowOffset;
792 return CGSizeMake(width, height);
796 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
798 state().m_shadowOffset = FloatSize(width, height);
799 state().m_shadowBlur = blur;
800 state().m_shadowColor = "";
804 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
806 state().m_shadowOffset = FloatSize(width, height);
807 state().m_shadowBlur = blur;
808 state().m_shadowColor = color;
812 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
814 state().m_shadowOffset = FloatSize(width, height);
815 state().m_shadowBlur = blur;
816 state().m_shadowColor = "";
818 GraphicsContext* c = drawingContext();
822 RGBA32 rgba = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f);
823 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
826 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
828 state().m_shadowOffset = FloatSize(width, height);
829 state().m_shadowBlur = blur;
830 state().m_shadowColor = color;
832 GraphicsContext* c = drawingContext();
836 RGBA32 rgba = 0; // default is transparent black
837 if (!state().m_shadowColor.isEmpty())
838 CSSParser::parseColor(rgba, state().m_shadowColor);
839 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(colorWithOverrideAlpha(rgba, alpha)));
842 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
844 state().m_shadowOffset = FloatSize(width, height);
845 state().m_shadowBlur = blur;
846 state().m_shadowColor = "";
848 GraphicsContext* c = drawingContext();
852 RGBA32 rgba = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha);
853 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
856 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
858 state().m_shadowOffset = FloatSize(width, height);
859 state().m_shadowBlur = blur;
860 state().m_shadowColor = "";
862 GraphicsContext* c = drawingContext();
866 RGBA32 rgba = makeRGBA32FromFloats(r, g, b, a); // default is transparent black
867 if (!state().m_shadowColor.isEmpty())
868 CSSParser::parseColor(rgba, state().m_shadowColor);
869 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
872 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
874 state().m_shadowOffset = FloatSize(width, height);
875 state().m_shadowBlur = blur;
876 state().m_shadowColor = "";
878 GraphicsContext* dc = drawingContext();
882 const CGFloat components[5] = { c, m, y, k, a };
883 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
884 CGColorRef shadowColor = CGColorCreate(colorSpace, components);
885 CGColorSpaceRelease(colorSpace);
886 CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
887 CGColorRelease(shadowColor);
889 dc->setShadow(IntSize(width, -height), blur, Color(c, m, y, k, a));
893 void CanvasRenderingContext2D::clearShadow()
895 state().m_shadowOffset = FloatSize();
896 state().m_shadowBlur = 0;
897 state().m_shadowColor = "";
901 void CanvasRenderingContext2D::applyShadow()
903 GraphicsContext* c = drawingContext();
907 RGBA32 rgba = 0; // default is transparent black
908 if (!state().m_shadowColor.isEmpty())
909 CSSParser::parseColor(rgba, state().m_shadowColor);
910 float width = state().m_shadowOffset.width();
911 float height = state().m_shadowOffset.height();
912 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
915 static IntSize size(HTMLImageElement* image)
917 if (CachedImage* cachedImage = image->cachedImage())
918 return cachedImage->imageSize(1.0f); // FIXME: Not sure about this.
923 static IntSize size(HTMLVideoElement* video)
925 if (MediaPlayer* player = video->player())
926 return player->naturalSize();
931 static inline FloatRect normalizeRect(const FloatRect& rect)
933 return FloatRect(min(rect.x(), rect.right()),
934 min(rect.y(), rect.bottom()),
935 max(rect.width(), -rect.width()),
936 max(rect.height(), -rect.height()));
939 void CanvasRenderingContext2D::checkOrigin(const KURL& url)
941 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
942 if (!m_canvas->document()->securityOrigin()->canAccess(origin.get()))
943 m_canvas->setOriginTainted();
946 void CanvasRenderingContext2D::checkOrigin(const String& url)
948 RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromString(url);
949 if (!m_canvas->document()->securityOrigin()->canAccess(origin.get()))
950 m_canvas->setOriginTainted();
953 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
956 IntSize s = size(image);
958 drawImage(image, x, y, s.width(), s.height(), ec);
961 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
962 float x, float y, float width, float height, ExceptionCode& ec)
965 IntSize s = size(image);
966 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
969 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
976 FloatRect imageRect = FloatRect(FloatPoint(), size(image));
977 if (!imageRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
982 if (!dstRect.width() || !dstRect.height())
985 GraphicsContext* c = drawingContext();
988 if (!state().m_invertibleCTM)
991 CachedImage* cachedImage = image->cachedImage();
995 if (m_canvas->originClean())
996 checkOrigin(cachedImage->response().url());
998 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
999 m_canvas->setOriginTainted();
1001 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1002 FloatRect destRect = c->roundToDevicePixels(dstRect);
1004 c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
1007 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
1011 drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
1014 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
1015 float x, float y, float width, float height, ExceptionCode& ec)
1018 drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
1021 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
1022 const FloatRect& dstRect, ExceptionCode& ec)
1028 FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
1029 if (!srcCanvasRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
1030 ec = INDEX_SIZE_ERR;
1034 if (!dstRect.width() || !dstRect.height())
1037 GraphicsContext* c = drawingContext();
1040 if (!state().m_invertibleCTM)
1043 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1044 FloatRect destRect = c->roundToDevicePixels(dstRect);
1046 // FIXME: Do this through platform-independent GraphicsContext API.
1047 ImageBuffer* buffer = canvas->buffer();
1051 if (!canvas->originClean())
1052 m_canvas->setOriginTainted();
1054 c->drawImage(buffer->image(), destRect, sourceRect, state().m_globalComposite);
1055 willDraw(destRect); // This call comes after drawImage, since the buffer we draw into may be our own, and we need to make sure it is dirty.
1056 // FIXME: Arguably willDraw should become didDraw and occur after drawing calls and not before them to avoid problems like this.
1060 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, float x, float y)
1063 IntSize s = size(video);
1065 drawImage(video, x, y, s.width(), s.height(), ec);
1068 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video,
1069 float x, float y, float width, float height, ExceptionCode& ec)
1072 IntSize s = size(video);
1073 drawImage(video, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
1076 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, const FloatRect& srcRect, const FloatRect& dstRect,
1082 FloatRect videoRect = FloatRect(FloatPoint(), size(video));
1083 if (!videoRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
1084 ec = INDEX_SIZE_ERR;
1088 if (!dstRect.width() || !dstRect.height())
1091 GraphicsContext* c = drawingContext();
1094 if (!state().m_invertibleCTM)
1097 if (m_canvas->originClean())
1098 checkOrigin(video->currentSrc());
1100 if (m_canvas->originClean() && !video->hasSingleSecurityOrigin())
1101 m_canvas->setOriginTainted();
1103 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1104 FloatRect destRect = c->roundToDevicePixels(dstRect);
1109 c->translate(destRect.x(), destRect.y());
1110 c->scale(FloatSize(destRect.width()/sourceRect.width(), destRect.height()/sourceRect.height()));
1111 c->translate(-sourceRect.x(), -sourceRect.y());
1112 video->paintCurrentFrameInContext(c, IntRect(IntPoint(), size(video)));
1117 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
1118 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1119 float sx, float sy, float sw, float sh,
1120 float dx, float dy, float dw, float dh,
1121 const String& compositeOperation)
1126 CachedImage* cachedImage = image->cachedImage();
1130 if (m_canvas->originClean())
1131 checkOrigin(cachedImage->response().url());
1133 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
1134 m_canvas->setOriginTainted();
1136 GraphicsContext* c = drawingContext();
1139 if (!state().m_invertibleCTM)
1142 CompositeOperator op;
1143 if (!parseCompositeOperator(compositeOperation, op))
1144 op = CompositeSourceOver;
1146 FloatRect destRect = FloatRect(dx, dy, dw, dh);
1148 c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
1151 void CanvasRenderingContext2D::setAlpha(float alpha)
1153 setGlobalAlpha(alpha);
1156 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1158 setGlobalCompositeOperation(operation);
1161 void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient* gradient) const
1163 #if ENABLE(DASHBOARD_SUPPORT)
1164 if (Settings* settings = m_canvas->document()->settings())
1165 if (settings->usesDashboardBackwardCompatibilityMode())
1166 gradient->setDashboardCompatibilityMode();
1168 UNUSED_PARAM(gradient);
1172 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec)
1174 if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) {
1175 ec = NOT_SUPPORTED_ERR;
1179 PassRefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1180 prepareGradientForDashboard(gradient.get());
1184 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec)
1186 if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) ||
1187 !isfinite(x1) || !isfinite(y1) || !isfinite(r1)) {
1188 ec = NOT_SUPPORTED_ERR;
1191 PassRefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1192 prepareGradientForDashboard(gradient.get());
1196 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1197 const String& repetitionType, ExceptionCode& ec)
1199 bool repeatX, repeatY;
1201 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1205 if (!image->complete()) {
1206 ec = INVALID_STATE_ERR;
1210 CachedImage* cachedImage = image->cachedImage();
1211 if (!cachedImage || !image->cachedImage()->image())
1212 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
1214 RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromString(cachedImage->url());
1215 bool originClean = m_canvas->document()->securityOrigin()->canAccess(origin.get());
1216 return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originClean);
1219 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1220 const String& repetitionType, ExceptionCode& ec)
1222 if (!canvas->width() || !canvas->height()) {
1223 ec = INVALID_STATE_ERR;
1227 bool repeatX, repeatY;
1229 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1232 return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, canvas->originClean());
1235 void CanvasRenderingContext2D::willDraw(const FloatRect& r, unsigned options)
1237 GraphicsContext* c = drawingContext();
1240 if (!state().m_invertibleCTM)
1243 FloatRect dirtyRect = r;
1244 if (options & CanvasWillDrawApplyTransform) {
1245 TransformationMatrix ctm = state().m_transform;
1246 dirtyRect = ctm.mapRect(r);
1249 if (options & CanvasWillDrawApplyShadow) {
1250 // The shadow gets applied after transformation
1251 FloatRect shadowRect(dirtyRect);
1252 shadowRect.move(state().m_shadowOffset);
1253 shadowRect.inflate(state().m_shadowBlur);
1254 dirtyRect.unite(shadowRect);
1257 if (options & CanvasWillDrawApplyClip) {
1258 // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
1259 // back out of the GraphicsContext, so to take clip into account for incremental painting,
1260 // we'd have to keep the clip path around.
1263 m_canvas->willDraw(dirtyRect);
1266 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1268 return m_canvas->drawingContext();
1271 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1273 RefPtr<ImageData> data = ImageData::create(size.width(), size.height());
1274 memset(data->data()->data()->data(), 0, data->data()->data()->length());
1278 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh) const
1280 FloatSize unscaledSize(sw, sh);
1281 IntSize scaledSize = m_canvas->convertLogicalToDevice(unscaledSize);
1282 if (scaledSize.width() < 1)
1283 scaledSize.setWidth(1);
1284 if (scaledSize.height() < 1)
1285 scaledSize.setHeight(1);
1287 return createEmptyImageData(scaledSize);
1290 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1292 if (!m_canvas->originClean()) {
1297 FloatRect unscaledRect(sx, sy, sw, sh);
1298 IntRect scaledRect = m_canvas->convertLogicalToDevice(unscaledRect);
1299 if (scaledRect.width() < 1)
1300 scaledRect.setWidth(1);
1301 if (scaledRect.height() < 1)
1302 scaledRect.setHeight(1);
1303 ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0;
1305 return createEmptyImageData(scaledRect.size());
1306 return buffer->getImageData(scaledRect);
1309 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
1312 ec = TYPE_MISMATCH_ERR;
1315 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
1318 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
1319 float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
1322 ec = TYPE_MISMATCH_ERR;
1325 if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) ||
1326 !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
1327 ec = INDEX_SIZE_ERR;
1331 ImageBuffer* buffer = m_canvas->buffer();
1335 if (dirtyWidth < 0) {
1336 dirtyX += dirtyWidth;
1337 dirtyWidth = -dirtyWidth;
1340 if (dirtyHeight < 0) {
1341 dirtyY += dirtyHeight;
1342 dirtyHeight = -dirtyHeight;
1345 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1346 clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1347 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1348 IntRect sourceRect = enclosingIntRect(clipRect);
1349 sourceRect.move(destOffset);
1350 sourceRect.intersect(IntRect(IntPoint(), buffer->size()));
1351 if (sourceRect.isEmpty())
1353 willDraw(sourceRect, 0); // ignore transform, shadow and clip
1354 sourceRect.move(-destOffset);
1355 IntPoint destPoint(destOffset.width(), destOffset.height());
1357 buffer->putImageData(data, sourceRect, destPoint);
1360 String CanvasRenderingContext2D::font() const
1362 return state().m_unparsedFont;
1365 void CanvasRenderingContext2D::setFont(const String& newFont)
1367 RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create();
1368 CSSParser parser(!m_canvas->document()->inCompatMode()); // Use the parse mode of the canvas' document when parsing CSS.
1370 String declarationText("font: ");
1371 declarationText += newFont;
1372 parser.parseDeclaration(tempDecl.get(), declarationText);
1373 if (!tempDecl->length())
1376 // The parse succeeded.
1377 state().m_unparsedFont = newFont;
1379 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
1380 // relative to the canvas.
1381 RefPtr<RenderStyle> newStyle = RenderStyle::create();
1382 if (m_canvas->computedStyle())
1383 newStyle->setFontDescription(m_canvas->computedStyle()->fontDescription());
1385 // Now map the font property into the style.
1386 CSSStyleSelector* styleSelector = m_canvas->document()->styleSelector();
1387 styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCSSValue(CSSPropertyFont).get(), newStyle.get());
1389 state().m_font = newStyle->font();
1390 state().m_font.update(styleSelector->fontSelector());
1391 state().m_realizedFont = true;
1394 String CanvasRenderingContext2D::textAlign() const
1396 return textAlignName(state().m_textAlign);
1399 void CanvasRenderingContext2D::setTextAlign(const String& s)
1402 if (!parseTextAlign(s, align))
1404 state().m_textAlign = align;
1407 String CanvasRenderingContext2D::textBaseline() const
1409 return textBaselineName(state().m_textBaseline);
1412 void CanvasRenderingContext2D::setTextBaseline(const String& s)
1414 TextBaseline baseline;
1415 if (!parseTextBaseline(s, baseline))
1417 state().m_textBaseline = baseline;
1420 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
1422 drawTextInternal(text, x, y, true);
1425 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
1427 drawTextInternal(text, x, y, true, maxWidth, true);
1430 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
1432 drawTextInternal(text, x, y, false);
1435 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
1437 drawTextInternal(text, x, y, false, maxWidth, true);
1440 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
1442 RefPtr<TextMetrics> metrics = TextMetrics::create();
1443 metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length())));
1447 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float /*maxWidth*/, bool /*useMaxWidth*/)
1449 GraphicsContext* c = drawingContext();
1452 if (!state().m_invertibleCTM)
1455 const Font& font = accessFont();
1457 // FIXME: Handle maxWidth.
1458 // FIXME: Need to turn off font smoothing.
1460 bool rtl = m_canvas->computedStyle() ? m_canvas->computedStyle()->direction() == RTL : false;
1461 bool override = m_canvas->computedStyle() ? m_canvas->computedStyle()->unicodeBidi() == Override : false;
1463 unsigned length = text.length();
1464 const UChar* string = text.characters();
1465 TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false);
1467 // Draw the item text at the correct point.
1468 FloatPoint location(x, y);
1469 switch (state().m_textBaseline) {
1470 case TopTextBaseline:
1471 case HangingTextBaseline:
1472 location.setY(y + font.ascent());
1474 case BottomTextBaseline:
1475 case IdeographicTextBaseline:
1476 location.setY(y - font.descent());
1478 case MiddleTextBaseline:
1479 location.setY(y - font.descent() + font.height() / 2);
1481 case AlphabeticTextBaseline:
1487 float width = font.width(TextRun(text, false, 0, 0, rtl, override));
1489 TextAlign align = state().m_textAlign;
1490 if (align == StartTextAlign)
1491 align = rtl ? RightTextAlign : LeftTextAlign;
1492 else if (align == EndTextAlign)
1493 align = rtl ? LeftTextAlign : RightTextAlign;
1496 case CenterTextAlign:
1497 location.setX(location.x() - width / 2);
1499 case RightTextAlign:
1500 location.setX(location.x() - width);
1506 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
1507 FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y() - font.ascent() - font.lineGap(),
1508 width + font.height(), font.lineSpacing());
1510 textRect.inflate(c->strokeThickness() / 2);
1513 m_canvas->willDraw(textRect);
1515 // When stroking text, pointy miters can extend outside of textRect, so we
1516 // punt and dirty the whole canvas.
1517 m_canvas->willDraw(FloatRect(0, 0, m_canvas->width(), m_canvas->height()));
1521 CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
1522 if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
1523 // FIXME: The rect is not big enough for miters on stroked text.
1524 IntRect maskRect = enclosingIntRect(textRect);
1526 OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), false);
1528 GraphicsContext* maskImageContext = maskImage->context();
1531 maskImageContext->setFillColor(Color::black);
1533 maskImageContext->setStrokeColor(Color::black);
1534 maskImageContext->setStrokeThickness(c->strokeThickness());
1537 maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1538 maskImageContext->translate(-maskRect.x(), -maskRect.y());
1540 maskImageContext->drawBidiText(font, textRun, location);
1543 c->clipToImageBuffer(maskRect, maskImage.get());
1544 drawStyle->applyFillColor(c);
1545 c->fillRect(maskRect);
1552 c->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1553 c->drawBidiText(font, textRun, location);
1556 const Font& CanvasRenderingContext2D::accessFont()
1558 if (!state().m_realizedFont)
1559 setFont(state().m_unparsedFont);
1560 return state().m_font;
1563 } // namespace WebCore