2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 "CSSPropertyNames.h"
40 #include "CSSStyleSelector.h"
42 #include "ExceptionCode.h"
43 #include "FloatConversion.h"
45 #include "GraphicsContext.h"
46 #include "HTMLCanvasElement.h"
47 #include "HTMLImageElement.h"
48 #include "HTMLNames.h"
49 #include "ImageBuffer.h"
50 #include "ImageData.h"
52 #include "NotImplemented.h"
54 #include "RenderHTMLCanvas.h"
55 #include "SecurityOrigin.h"
57 #include "StrokeStyleApplier.h"
58 #include "TextMetrics.h"
61 #include <wtf/ByteArray.h>
62 #include <wtf/MathExtras.h>
68 using namespace HTMLNames;
70 static const char* const defaultFont = "10px sans-serif";
73 class CanvasStrokeStyleApplier : public StrokeStyleApplier {
75 CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
76 : m_canvasContext(canvasContext)
80 virtual void strokeStyle(GraphicsContext* c)
82 c->setStrokeThickness(m_canvasContext->lineWidth());
83 c->setLineCap(m_canvasContext->getLineCap());
84 c->setLineJoin(m_canvasContext->getLineJoin());
85 c->setMiterLimit(m_canvasContext->miterLimit());
89 CanvasRenderingContext2D* m_canvasContext;
94 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
100 void CanvasRenderingContext2D::ref()
105 void CanvasRenderingContext2D::deref()
110 void CanvasRenderingContext2D::reset()
112 m_stateStack.resize(1);
113 m_stateStack.first() = State();
116 CanvasRenderingContext2D::State::State()
117 : m_strokeStyle(CanvasStyle::create("black"))
118 , m_fillStyle(CanvasStyle::create("black"))
121 , m_lineJoin(MiterJoin)
124 , m_shadowColor("black")
126 , m_globalComposite(CompositeSourceOver)
127 , m_invertibleCTM(true)
128 , m_textAlign(StartTextAlign)
129 , m_textBaseline(AlphabeticTextBaseline)
130 , m_unparsedFont(defaultFont)
131 , m_realizedFont(false)
135 void CanvasRenderingContext2D::save()
137 ASSERT(m_stateStack.size() >= 1);
138 m_stateStack.append(state());
139 GraphicsContext* c = drawingContext();
145 void CanvasRenderingContext2D::restore()
147 ASSERT(m_stateStack.size() >= 1);
148 if (m_stateStack.size() <= 1)
150 m_path.transform(state().m_transform);
151 m_stateStack.removeLast();
152 m_path.transform(state().m_transform.inverse());
153 GraphicsContext* c = drawingContext();
159 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
161 return state().m_strokeStyle.get();
164 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
169 if (m_canvas->originClean()) {
170 if (CanvasPattern* pattern = style->canvasPattern()) {
171 if (!pattern->originClean())
172 m_canvas->setOriginTainted();
176 state().m_strokeStyle = style;
177 GraphicsContext* c = drawingContext();
180 state().m_strokeStyle->applyStrokeColor(c);
183 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
185 return state().m_fillStyle.get();
188 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
193 if (m_canvas->originClean()) {
194 if (CanvasPattern* pattern = style->canvasPattern()) {
195 if (!pattern->originClean())
196 m_canvas->setOriginTainted();
200 state().m_fillStyle = style;
201 GraphicsContext* c = drawingContext();
204 state().m_fillStyle->applyFillColor(c);
207 float CanvasRenderingContext2D::lineWidth() const
209 return state().m_lineWidth;
212 void CanvasRenderingContext2D::setLineWidth(float width)
216 state().m_lineWidth = width;
217 GraphicsContext* c = drawingContext();
220 c->setStrokeThickness(width);
223 String CanvasRenderingContext2D::lineCap() const
225 return lineCapName(state().m_lineCap);
228 void CanvasRenderingContext2D::setLineCap(const String& s)
231 if (!parseLineCap(s, cap))
233 state().m_lineCap = cap;
234 GraphicsContext* c = drawingContext();
240 String CanvasRenderingContext2D::lineJoin() const
242 return lineJoinName(state().m_lineJoin);
245 void CanvasRenderingContext2D::setLineJoin(const String& s)
248 if (!parseLineJoin(s, join))
250 state().m_lineJoin = join;
251 GraphicsContext* c = drawingContext();
254 c->setLineJoin(join);
257 float CanvasRenderingContext2D::miterLimit() const
259 return state().m_miterLimit;
262 void CanvasRenderingContext2D::setMiterLimit(float limit)
266 state().m_miterLimit = limit;
267 GraphicsContext* c = drawingContext();
270 c->setMiterLimit(limit);
273 float CanvasRenderingContext2D::shadowOffsetX() const
275 return state().m_shadowOffset.width();
278 void CanvasRenderingContext2D::setShadowOffsetX(float x)
280 state().m_shadowOffset.setWidth(x);
284 float CanvasRenderingContext2D::shadowOffsetY() const
286 return state().m_shadowOffset.height();
289 void CanvasRenderingContext2D::setShadowOffsetY(float y)
291 state().m_shadowOffset.setHeight(y);
295 float CanvasRenderingContext2D::shadowBlur() const
297 return state().m_shadowBlur;
300 void CanvasRenderingContext2D::setShadowBlur(float blur)
302 state().m_shadowBlur = blur;
306 String CanvasRenderingContext2D::shadowColor() const
308 // FIXME: What should this return if you called setShadow with a non-string color?
309 return state().m_shadowColor;
312 void CanvasRenderingContext2D::setShadowColor(const String& color)
314 state().m_shadowColor = color;
318 float CanvasRenderingContext2D::globalAlpha() const
320 return state().m_globalAlpha;
323 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
325 if (!(alpha >= 0 && alpha <= 1))
327 state().m_globalAlpha = alpha;
328 GraphicsContext* c = drawingContext();
334 String CanvasRenderingContext2D::globalCompositeOperation() const
336 return compositeOperatorName(state().m_globalComposite);
339 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
341 CompositeOperator op;
342 if (!parseCompositeOperator(operation, op))
344 state().m_globalComposite = op;
345 GraphicsContext* c = drawingContext();
348 c->setCompositeOperation(op);
351 void CanvasRenderingContext2D::scale(float sx, float sy)
353 GraphicsContext* c = drawingContext();
356 if (!state().m_invertibleCTM)
359 TransformationMatrix newTransform = state().m_transform;
360 newTransform.scale(sx, sy);
361 if (!newTransform.isInvertible()) {
362 state().m_invertibleCTM = false;
366 state().m_transform = newTransform;
367 c->scale(FloatSize(sx, sy));
368 m_path.transform(TransformationMatrix().scale(1.0/sx, 1.0/sy));
371 void CanvasRenderingContext2D::rotate(float angleInRadians)
373 GraphicsContext* c = drawingContext();
376 if (!state().m_invertibleCTM)
379 TransformationMatrix newTransform = state().m_transform;
380 newTransform.rotate(angleInRadians / piDouble * 180.0);
381 if (!newTransform.isInvertible()) {
382 state().m_invertibleCTM = false;
386 state().m_transform = newTransform;
387 c->rotate(angleInRadians);
388 m_path.transform(TransformationMatrix().rotate(-angleInRadians / piDouble * 180.0));
391 void CanvasRenderingContext2D::translate(float tx, float ty)
393 GraphicsContext* c = drawingContext();
396 if (!state().m_invertibleCTM)
399 TransformationMatrix newTransform = state().m_transform;
400 newTransform.translate(tx, ty);
401 if (!newTransform.isInvertible()) {
402 state().m_invertibleCTM = false;
406 state().m_transform = newTransform;
407 c->translate(tx, ty);
408 m_path.transform(TransformationMatrix().translate(-tx, -ty));
411 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
413 GraphicsContext* c = drawingContext();
416 if (!state().m_invertibleCTM)
419 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
420 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
421 !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
424 TransformationMatrix transform(m11, m12, m21, m22, dx, dy);
425 TransformationMatrix newTransform = transform * state().m_transform;
426 if (!newTransform.isInvertible()) {
427 state().m_invertibleCTM = false;
431 state().m_transform = newTransform;
432 c->concatCTM(transform);
433 m_path.transform(transform.inverse());
436 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
438 GraphicsContext* c = drawingContext();
442 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
443 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
444 !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
447 TransformationMatrix ctm = state().m_transform;
448 if (!ctm.isInvertible())
450 c->concatCTM(c->getCTM().inverse());
451 c->concatCTM(m_canvas->baseTransform());
452 state().m_transform.multiply(ctm.inverse());
453 m_path.transform(ctm);
455 state().m_invertibleCTM = true;
456 transform(m11, m12, m21, m22, dx, dy);
459 void CanvasRenderingContext2D::setStrokeColor(const String& color)
461 setStrokeStyle(CanvasStyle::create(color));
464 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
466 setStrokeStyle(CanvasStyle::create(grayLevel, 1));
469 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
471 setStrokeStyle(CanvasStyle::create(color, alpha));
474 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
476 setStrokeStyle(CanvasStyle::create(grayLevel, alpha));
479 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
481 setStrokeStyle(CanvasStyle::create(r, g, b, a));
484 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
486 setStrokeStyle(CanvasStyle::create(c, m, y, k, a));
489 void CanvasRenderingContext2D::setFillColor(const String& color)
491 setFillStyle(CanvasStyle::create(color));
494 void CanvasRenderingContext2D::setFillColor(float grayLevel)
496 setFillStyle(CanvasStyle::create(grayLevel, 1));
499 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
501 setFillStyle(CanvasStyle::create(color, alpha));
504 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
506 setFillStyle(CanvasStyle::create(grayLevel, alpha));
509 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
511 setFillStyle(CanvasStyle::create(r, g, b, a));
514 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
516 setFillStyle(CanvasStyle::create(c, m, y, k, a));
519 void CanvasRenderingContext2D::beginPath()
524 void CanvasRenderingContext2D::closePath()
526 m_path.closeSubpath();
529 void CanvasRenderingContext2D::moveTo(float x, float y)
531 if (!isfinite(x) | !isfinite(y))
533 if (!state().m_invertibleCTM)
535 m_path.moveTo(FloatPoint(x, y));
538 void CanvasRenderingContext2D::lineTo(float x, float y)
540 if (!isfinite(x) | !isfinite(y))
542 if (!state().m_invertibleCTM)
544 m_path.addLineTo(FloatPoint(x, y));
547 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
549 if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y))
551 if (!state().m_invertibleCTM)
553 m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
556 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
558 if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y))
560 if (!state().m_invertibleCTM)
562 m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
565 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
568 if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinite(r))
575 if (!state().m_invertibleCTM)
577 m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
580 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
583 if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea))
590 if (!state().m_invertibleCTM)
592 m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
595 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
597 if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height))
613 void CanvasRenderingContext2D::rect(float x, float y, float width, float height)
615 if (!validateRectForCanvas(x, y, width, height))
617 if (!state().m_invertibleCTM)
619 m_path.addRect(FloatRect(x, y, width, height));
622 #if ENABLE(DASHBOARD_SUPPORT)
623 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
625 if (Settings* settings = m_canvas->document()->settings())
626 if (settings->usesDashboardBackwardCompatibilityMode())
631 void CanvasRenderingContext2D::fill()
633 GraphicsContext* c = drawingContext();
636 if (!state().m_invertibleCTM)
639 if (!m_path.isEmpty()) {
642 willDraw(m_path.boundingRect());
646 #if ENABLE(DASHBOARD_SUPPORT)
647 clearPathForDashboardBackwardCompatibilityMode();
651 void CanvasRenderingContext2D::stroke()
653 GraphicsContext* c = drawingContext();
656 if (!state().m_invertibleCTM)
659 if (!m_path.isEmpty()) {
663 CanvasStrokeStyleApplier strokeApplier(this);
664 FloatRect boundingRect = m_path.strokeBoundingRect(&strokeApplier);
665 willDraw(boundingRect);
670 #if ENABLE(DASHBOARD_SUPPORT)
671 clearPathForDashboardBackwardCompatibilityMode();
675 void CanvasRenderingContext2D::clip()
677 GraphicsContext* c = drawingContext();
680 if (!state().m_invertibleCTM)
683 #if ENABLE(DASHBOARD_SUPPORT)
684 clearPathForDashboardBackwardCompatibilityMode();
688 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
690 GraphicsContext* c = drawingContext();
693 if (!state().m_invertibleCTM)
696 FloatPoint point(x, y);
697 TransformationMatrix ctm = state().m_transform;
698 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
699 return m_path.contains(transformedPoint);
702 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
704 if (!validateRectForCanvas(x, y, width, height))
706 GraphicsContext* c = drawingContext();
709 if (!state().m_invertibleCTM)
711 FloatRect rect(x, y, width, height);
716 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
718 if (!validateRectForCanvas(x, y, width, height))
721 GraphicsContext* c = drawingContext();
724 if (!state().m_invertibleCTM)
727 FloatRect rect(x, y, width, height);
735 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
737 if (!validateRectForCanvas(x, y, width, height))
739 strokeRect(x, y, width, height, state().m_lineWidth);
742 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth)
744 if (!validateRectForCanvas(x, y, width, height))
747 if (!(lineWidth >= 0))
750 GraphicsContext* c = drawingContext();
753 if (!state().m_invertibleCTM)
756 FloatRect rect(x, y, width, height);
758 FloatRect boundingRect = rect;
759 boundingRect.inflate(lineWidth / 2);
760 willDraw(boundingRect);
762 c->strokeRect(rect, lineWidth);
766 static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height)
768 // Work around <rdar://problem/5539388> by ensuring that shadow offsets will get truncated
769 // to the desired integer.
770 static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
772 width += extraShadowOffset;
774 width -= extraShadowOffset;
777 height += extraShadowOffset;
779 height -= extraShadowOffset;
781 return CGSizeMake(width, height);
785 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
787 state().m_shadowOffset = FloatSize(width, height);
788 state().m_shadowBlur = blur;
789 state().m_shadowColor = "";
793 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
795 state().m_shadowOffset = FloatSize(width, height);
796 state().m_shadowBlur = blur;
797 state().m_shadowColor = color;
801 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
803 state().m_shadowOffset = FloatSize(width, height);
804 state().m_shadowBlur = blur;
805 state().m_shadowColor = "";
807 GraphicsContext* c = drawingContext();
811 RGBA32 rgba = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f);
812 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
815 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
817 state().m_shadowOffset = FloatSize(width, height);
818 state().m_shadowBlur = blur;
819 state().m_shadowColor = color;
821 GraphicsContext* c = drawingContext();
825 RGBA32 rgba = 0; // default is transparent black
826 if (!state().m_shadowColor.isEmpty())
827 CSSParser::parseColor(rgba, state().m_shadowColor);
828 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(colorWithOverrideAlpha(rgba, alpha)));
831 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
833 state().m_shadowOffset = FloatSize(width, height);
834 state().m_shadowBlur = blur;
835 state().m_shadowColor = "";
837 GraphicsContext* c = drawingContext();
841 RGBA32 rgba = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha);
842 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
845 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
847 state().m_shadowOffset = FloatSize(width, height);
848 state().m_shadowBlur = blur;
849 state().m_shadowColor = "";
851 GraphicsContext* c = drawingContext();
855 RGBA32 rgba = makeRGBA32FromFloats(r, g, b, a); // default is transparent black
856 if (!state().m_shadowColor.isEmpty())
857 CSSParser::parseColor(rgba, state().m_shadowColor);
858 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
861 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
863 state().m_shadowOffset = FloatSize(width, height);
864 state().m_shadowBlur = blur;
865 state().m_shadowColor = "";
867 GraphicsContext* dc = drawingContext();
871 const CGFloat components[5] = { c, m, y, k, a };
872 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
873 CGColorRef shadowColor = CGColorCreate(colorSpace, components);
874 CGColorSpaceRelease(colorSpace);
875 CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
876 CGColorRelease(shadowColor);
878 dc->setShadow(IntSize(width, -height), blur, Color(c, m, y, k, a));
882 void CanvasRenderingContext2D::clearShadow()
884 state().m_shadowOffset = FloatSize();
885 state().m_shadowBlur = 0;
886 state().m_shadowColor = "";
890 void CanvasRenderingContext2D::applyShadow()
892 GraphicsContext* c = drawingContext();
896 RGBA32 rgba = 0; // default is transparent black
897 if (!state().m_shadowColor.isEmpty())
898 CSSParser::parseColor(rgba, state().m_shadowColor);
899 float width = state().m_shadowOffset.width();
900 float height = state().m_shadowOffset.height();
901 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
904 static IntSize size(HTMLImageElement* image)
906 if (CachedImage* cachedImage = image->cachedImage())
907 return cachedImage->imageSize(1.0f); // FIXME: Not sure about this.
911 static inline FloatRect normalizeRect(const FloatRect& rect)
913 return FloatRect(min(rect.x(), rect.right()),
914 min(rect.y(), rect.bottom()),
915 max(rect.width(), -rect.width()),
916 max(rect.height(), -rect.height()));
919 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
922 IntSize s = size(image);
924 drawImage(image, x, y, s.width(), s.height(), ec);
927 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
928 float x, float y, float width, float height, ExceptionCode& ec)
931 IntSize s = size(image);
932 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
935 void CanvasRenderingContext2D::checkOrigin(const KURL& url)
937 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
938 if (!m_canvas->document()->securityOrigin()->canAccess(origin.get()))
939 m_canvas->setOriginTainted();
942 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
949 FloatRect imageRect = FloatRect(FloatPoint(), size(image));
950 if (!imageRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
955 if (!dstRect.width() || !dstRect.height())
958 GraphicsContext* c = drawingContext();
961 if (!state().m_invertibleCTM)
964 CachedImage* cachedImage = image->cachedImage();
968 if (m_canvas->originClean())
969 checkOrigin(cachedImage->response().url());
971 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
972 m_canvas->setOriginTainted();
974 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
975 FloatRect destRect = c->roundToDevicePixels(dstRect);
977 c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
980 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
984 drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
987 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
988 float x, float y, float width, float height, ExceptionCode& ec)
991 drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
994 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
995 const FloatRect& dstRect, ExceptionCode& ec)
1001 FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
1002 if (!srcCanvasRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
1003 ec = INDEX_SIZE_ERR;
1007 if (!dstRect.width() || !dstRect.height())
1010 GraphicsContext* c = drawingContext();
1013 if (!state().m_invertibleCTM)
1016 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1017 FloatRect destRect = c->roundToDevicePixels(dstRect);
1019 // FIXME: Do this through platform-independent GraphicsContext API.
1020 ImageBuffer* buffer = canvas->buffer();
1024 if (!canvas->originClean())
1025 m_canvas->setOriginTainted();
1027 c->drawImage(buffer->image(), destRect, sourceRect, state().m_globalComposite);
1028 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.
1029 // FIXME: Arguably willDraw should become didDraw and occur after drawing calls and not before them to avoid problems like this.
1032 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
1033 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1034 float sx, float sy, float sw, float sh,
1035 float dx, float dy, float dw, float dh,
1036 const String& compositeOperation)
1041 CachedImage* cachedImage = image->cachedImage();
1045 if (m_canvas->originClean())
1046 checkOrigin(cachedImage->response().url());
1048 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
1049 m_canvas->setOriginTainted();
1051 GraphicsContext* c = drawingContext();
1054 if (!state().m_invertibleCTM)
1057 CompositeOperator op;
1058 if (!parseCompositeOperator(compositeOperation, op))
1059 op = CompositeSourceOver;
1061 FloatRect destRect = FloatRect(dx, dy, dw, dh);
1063 c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
1066 void CanvasRenderingContext2D::setAlpha(float alpha)
1068 setGlobalAlpha(alpha);
1071 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1073 setGlobalCompositeOperation(operation);
1076 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec)
1078 if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) {
1079 ec = NOT_SUPPORTED_ERR;
1083 return CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1086 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec)
1088 if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) ||
1089 !isfinite(x1) || !isfinite(y1) || !isfinite(r1)) {
1090 ec = NOT_SUPPORTED_ERR;
1093 return CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1096 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1097 const String& repetitionType, ExceptionCode& ec)
1099 bool repeatX, repeatY;
1101 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1105 if (!image->complete()) {
1106 ec = INVALID_STATE_ERR;
1110 CachedImage* cachedImage = image->cachedImage();
1111 if (!cachedImage || !image->cachedImage()->image())
1112 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
1114 RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromString(cachedImage->url());
1115 bool originClean = m_canvas->document()->securityOrigin()->canAccess(origin.get());
1116 return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originClean);
1119 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1120 const String& repetitionType, ExceptionCode& ec)
1122 if (!canvas->width() || !canvas->height()) {
1123 ec = INVALID_STATE_ERR;
1127 bool repeatX, repeatY;
1129 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1132 return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, canvas->originClean());
1135 void CanvasRenderingContext2D::willDraw(const FloatRect& r, unsigned options)
1137 GraphicsContext* c = drawingContext();
1140 if (!state().m_invertibleCTM)
1143 FloatRect dirtyRect = r;
1144 if (options & CanvasWillDrawApplyTransform) {
1145 TransformationMatrix ctm = state().m_transform;
1146 dirtyRect = ctm.mapRect(r);
1149 if (options & CanvasWillDrawApplyShadow) {
1150 // The shadow gets applied after transformation
1151 FloatRect shadowRect(dirtyRect);
1152 shadowRect.move(state().m_shadowOffset);
1153 shadowRect.inflate(state().m_shadowBlur);
1154 dirtyRect.unite(shadowRect);
1157 if (options & CanvasWillDrawApplyClip) {
1158 // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
1159 // back out of the GraphicsContext, so to take clip into account for incremental painting,
1160 // we'd have to keep the clip path around.
1163 m_canvas->willDraw(dirtyRect);
1166 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1168 return m_canvas->drawingContext();
1171 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1173 RefPtr<ImageData> data = ImageData::create(size.width(), size.height());
1174 memset(data->data()->data()->data(), 0, data->data()->data()->length());
1178 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh) const
1180 FloatSize unscaledSize(sw, sh);
1181 IntSize scaledSize = m_canvas->convertLogicalToDevice(unscaledSize);
1182 if (scaledSize.width() < 1)
1183 scaledSize.setWidth(1);
1184 if (scaledSize.height() < 1)
1185 scaledSize.setHeight(1);
1187 return createEmptyImageData(scaledSize);
1190 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1192 if (!m_canvas->originClean()) {
1197 FloatRect unscaledRect(sx, sy, sw, sh);
1198 IntRect scaledRect = m_canvas->convertLogicalToDevice(unscaledRect);
1199 if (scaledRect.width() < 1)
1200 scaledRect.setWidth(1);
1201 if (scaledRect.height() < 1)
1202 scaledRect.setHeight(1);
1203 ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0;
1205 return createEmptyImageData(scaledRect.size());
1206 return buffer->getImageData(scaledRect);
1209 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
1212 ec = TYPE_MISMATCH_ERR;
1215 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
1218 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
1219 float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
1222 ec = TYPE_MISMATCH_ERR;
1225 if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) ||
1226 !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
1227 ec = INDEX_SIZE_ERR;
1231 ImageBuffer* buffer = m_canvas->buffer();
1235 if (dirtyWidth < 0) {
1236 dirtyX += dirtyWidth;
1237 dirtyWidth = -dirtyWidth;
1240 if (dirtyHeight < 0) {
1241 dirtyY += dirtyHeight;
1242 dirtyHeight = -dirtyHeight;
1245 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1246 clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1247 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1248 IntRect sourceRect = enclosingIntRect(clipRect);
1249 sourceRect.move(destOffset);
1250 sourceRect.intersect(IntRect(IntPoint(), buffer->size()));
1251 if (sourceRect.isEmpty())
1253 willDraw(sourceRect, 0); // ignore transform, shadow and clip
1254 sourceRect.move(-destOffset);
1255 IntPoint destPoint(destOffset.width(), destOffset.height());
1257 buffer->putImageData(data, sourceRect, destPoint);
1260 String CanvasRenderingContext2D::font() const
1262 return state().m_unparsedFont;
1265 void CanvasRenderingContext2D::setFont(const String& newFont)
1267 RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create();
1268 CSSParser parser(!m_canvas->document()->inCompatMode()); // Use the parse mode of the canvas' document when parsing CSS.
1270 String declarationText("font: ");
1271 declarationText += newFont;
1272 parser.parseDeclaration(tempDecl.get(), declarationText);
1273 if (!tempDecl->length())
1276 // The parse succeeded.
1277 state().m_unparsedFont = newFont;
1279 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
1280 // relative to the canvas.
1281 RefPtr<RenderStyle> newStyle = RenderStyle::create();
1282 if (m_canvas->computedStyle())
1283 newStyle->setFontDescription(m_canvas->computedStyle()->fontDescription());
1285 // Now map the font property into the style.
1286 CSSStyleSelector* styleSelector = m_canvas->document()->styleSelector();
1287 styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCSSValue(CSSPropertyFont).get(), newStyle.get());
1289 state().m_font = newStyle->font();
1290 state().m_font.update(styleSelector->fontSelector());
1291 state().m_realizedFont = true;
1294 String CanvasRenderingContext2D::textAlign() const
1296 return textAlignName(state().m_textAlign);
1299 void CanvasRenderingContext2D::setTextAlign(const String& s)
1302 if (!parseTextAlign(s, align))
1304 state().m_textAlign = align;
1307 String CanvasRenderingContext2D::textBaseline() const
1309 return textBaselineName(state().m_textBaseline);
1312 void CanvasRenderingContext2D::setTextBaseline(const String& s)
1314 TextBaseline baseline;
1315 if (!parseTextBaseline(s, baseline))
1317 state().m_textBaseline = baseline;
1320 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
1322 drawTextInternal(text, x, y, true);
1325 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
1327 drawTextInternal(text, x, y, true, maxWidth, true);
1330 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
1332 drawTextInternal(text, x, y, false);
1335 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
1337 drawTextInternal(text, x, y, false, maxWidth, true);
1340 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
1342 RefPtr<TextMetrics> metrics = TextMetrics::create();
1343 metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length())));
1347 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float /*maxWidth*/, bool /*useMaxWidth*/)
1349 GraphicsContext* c = drawingContext();
1352 if (!state().m_invertibleCTM)
1355 const Font& font = accessFont();
1357 // FIXME: Handle maxWidth.
1358 // FIXME: Need to turn off font smoothing.
1360 bool rtl = m_canvas->computedStyle() ? m_canvas->computedStyle()->direction() == RTL : false;
1361 bool override = m_canvas->computedStyle() ? m_canvas->computedStyle()->unicodeBidi() == Override : false;
1363 unsigned length = text.length();
1364 const UChar* string = text.characters();
1365 TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false);
1367 // Draw the item text at the correct point.
1368 FloatPoint location(x, y);
1369 switch (state().m_textBaseline) {
1370 case TopTextBaseline:
1371 case HangingTextBaseline:
1372 location.setY(y + font.ascent());
1374 case BottomTextBaseline:
1375 case IdeographicTextBaseline:
1376 location.setY(y - font.descent());
1378 case MiddleTextBaseline:
1379 location.setY(y - font.descent() + font.height() / 2);
1381 case AlphabeticTextBaseline:
1387 float width = font.width(TextRun(text, false, 0, 0, rtl, override));
1389 TextAlign align = state().m_textAlign;
1390 if (align == StartTextAlign)
1391 align = rtl ? RightTextAlign : LeftTextAlign;
1392 else if (align == EndTextAlign)
1393 align = rtl ? LeftTextAlign : RightTextAlign;
1396 case CenterTextAlign:
1397 location.setX(location.x() - width / 2);
1399 case RightTextAlign:
1400 location.setX(location.x() - width);
1406 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
1407 FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y() - font.ascent() - font.lineGap(),
1408 width + font.height(), font.lineSpacing());
1410 textRect.inflate(c->strokeThickness() / 2);
1413 m_canvas->willDraw(textRect);
1415 // When stroking text, pointy miters can extend outside of textRect, so we
1416 // punt and dirty the whole canvas.
1417 m_canvas->willDraw(FloatRect(0, 0, m_canvas->width(), m_canvas->height()));
1420 CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
1421 if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
1422 // FIXME: The rect is not big enough for miters on stroked text.
1423 IntRect maskRect = enclosingIntRect(textRect);
1425 auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), false);
1427 GraphicsContext* maskImageContext = maskImage->context();
1430 maskImageContext->setFillColor(Color::black);
1432 maskImageContext->setStrokeColor(Color::black);
1433 maskImageContext->setStrokeThickness(c->strokeThickness());
1436 maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1437 maskImageContext->translate(-maskRect.x(), -maskRect.y());
1439 maskImageContext->drawBidiText(font, textRun, location);
1442 c->clipToImageBuffer(maskRect, maskImage.get());
1443 drawStyle->applyFillColor(c);
1444 c->fillRect(maskRect);
1450 c->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1451 c->drawBidiText(font, textRun, location);
1454 const Font& CanvasRenderingContext2D::accessFont()
1456 if (!state().m_realizedFont)
1457 setFont(state().m_unparsedFont);
1458 return state().m_font;
1461 } // namespace WebCore