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 "AffineTransform.h"
34 #include "CSSParser.h"
35 #include "CachedImage.h"
36 #include "CanvasGradient.h"
37 #include "CanvasPattern.h"
38 #include "CanvasPixelArray.h"
39 #include "CanvasStyle.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"
53 #include "NotImplemented.h"
55 #include "RenderHTMLCanvas.h"
56 #include "SecurityOrigin.h"
58 #include "TextMetrics.h"
60 #include <wtf/MathExtras.h>
66 using namespace HTMLNames;
68 const char* defaultFont = "10px sans-serif";
70 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
76 void CanvasRenderingContext2D::ref()
81 void CanvasRenderingContext2D::deref()
86 void CanvasRenderingContext2D::reset()
88 m_stateStack.resize(1);
89 m_stateStack.first() = State();
92 CanvasRenderingContext2D::State::State()
93 : m_strokeStyle(CanvasStyle::create("black"))
94 , m_fillStyle(CanvasStyle::create("black"))
97 , m_lineJoin(MiterJoin)
100 , m_shadowColor("black")
102 , m_globalComposite(CompositeSourceOver)
103 , m_invertibleCTM(true)
104 , m_textAlign(StartTextAlign)
105 , m_textBaseline(AlphabeticTextBaseline)
106 , m_unparsedFont(defaultFont)
107 , m_realizedFont(false)
111 void CanvasRenderingContext2D::save()
113 ASSERT(m_stateStack.size() >= 1);
114 m_stateStack.append(state());
115 GraphicsContext* c = drawingContext();
121 void CanvasRenderingContext2D::restore()
123 ASSERT(m_stateStack.size() >= 1);
124 if (m_stateStack.size() <= 1)
126 m_path.transform(state().m_transform);
127 m_stateStack.removeLast();
128 m_path.transform(state().m_transform.inverse());
129 GraphicsContext* c = drawingContext();
135 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
137 return state().m_strokeStyle.get();
140 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
145 if (m_canvas->originClean()) {
146 if (CanvasPattern* pattern = style->canvasPattern()) {
147 if (!pattern->originClean())
148 m_canvas->setOriginTainted();
152 state().m_strokeStyle = style;
153 GraphicsContext* c = drawingContext();
156 state().m_strokeStyle->applyStrokeColor(c);
159 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
161 return state().m_fillStyle.get();
164 void CanvasRenderingContext2D::setFillStyle(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_fillStyle = style;
177 GraphicsContext* c = drawingContext();
180 state().m_fillStyle->applyFillColor(c);
183 float CanvasRenderingContext2D::lineWidth() const
185 return state().m_lineWidth;
188 void CanvasRenderingContext2D::setLineWidth(float width)
192 state().m_lineWidth = width;
193 GraphicsContext* c = drawingContext();
196 c->setStrokeThickness(width);
199 String CanvasRenderingContext2D::lineCap() const
201 return lineCapName(state().m_lineCap);
204 void CanvasRenderingContext2D::setLineCap(const String& s)
207 if (!parseLineCap(s, cap))
209 state().m_lineCap = cap;
210 GraphicsContext* c = drawingContext();
216 String CanvasRenderingContext2D::lineJoin() const
218 return lineJoinName(state().m_lineJoin);
221 void CanvasRenderingContext2D::setLineJoin(const String& s)
224 if (!parseLineJoin(s, join))
226 state().m_lineJoin = join;
227 GraphicsContext* c = drawingContext();
230 c->setLineJoin(join);
233 float CanvasRenderingContext2D::miterLimit() const
235 return state().m_miterLimit;
238 void CanvasRenderingContext2D::setMiterLimit(float limit)
242 state().m_miterLimit = limit;
243 GraphicsContext* c = drawingContext();
246 c->setMiterLimit(limit);
249 float CanvasRenderingContext2D::shadowOffsetX() const
251 return state().m_shadowOffset.width();
254 void CanvasRenderingContext2D::setShadowOffsetX(float x)
256 state().m_shadowOffset.setWidth(x);
260 float CanvasRenderingContext2D::shadowOffsetY() const
262 return state().m_shadowOffset.height();
265 void CanvasRenderingContext2D::setShadowOffsetY(float y)
267 state().m_shadowOffset.setHeight(y);
271 float CanvasRenderingContext2D::shadowBlur() const
273 return state().m_shadowBlur;
276 void CanvasRenderingContext2D::setShadowBlur(float blur)
278 state().m_shadowBlur = blur;
282 String CanvasRenderingContext2D::shadowColor() const
284 // FIXME: What should this return if you called setShadow with a non-string color?
285 return state().m_shadowColor;
288 void CanvasRenderingContext2D::setShadowColor(const String& color)
290 state().m_shadowColor = color;
294 float CanvasRenderingContext2D::globalAlpha() const
296 return state().m_globalAlpha;
299 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
301 if (!(alpha >= 0 && alpha <= 1))
303 state().m_globalAlpha = alpha;
304 GraphicsContext* c = drawingContext();
310 String CanvasRenderingContext2D::globalCompositeOperation() const
312 return compositeOperatorName(state().m_globalComposite);
315 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
317 CompositeOperator op;
318 if (!parseCompositeOperator(operation, op))
320 state().m_globalComposite = op;
321 GraphicsContext* c = drawingContext();
324 c->setCompositeOperation(op);
327 void CanvasRenderingContext2D::scale(float sx, float sy)
329 GraphicsContext* c = drawingContext();
332 if (!state().m_invertibleCTM)
335 AffineTransform newTransform = state().m_transform;
336 newTransform.scale(sx, sy);
337 if (!newTransform.isInvertible()) {
338 state().m_invertibleCTM = false;
342 state().m_transform = newTransform;
343 c->scale(FloatSize(sx, sy));
344 m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy));
347 void CanvasRenderingContext2D::rotate(float angleInRadians)
349 GraphicsContext* c = drawingContext();
352 if (!state().m_invertibleCTM)
355 AffineTransform newTransform = state().m_transform;
356 newTransform.rotate(angleInRadians / piDouble * 180.0);
357 if (!newTransform.isInvertible()) {
358 state().m_invertibleCTM = false;
362 state().m_transform = newTransform;
363 c->rotate(angleInRadians);
364 m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
367 void CanvasRenderingContext2D::translate(float tx, float ty)
369 GraphicsContext* c = drawingContext();
372 if (!state().m_invertibleCTM)
375 AffineTransform newTransform = state().m_transform;
376 newTransform.translate(tx, ty);
377 if (!newTransform.isInvertible()) {
378 state().m_invertibleCTM = false;
382 state().m_transform = newTransform;
383 c->translate(tx, ty);
384 m_path.transform(AffineTransform().translate(-tx, -ty));
387 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
389 GraphicsContext* c = drawingContext();
392 if (!state().m_invertibleCTM)
395 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
396 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
397 !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
400 AffineTransform transform(m11, m12, m21, m22, dx, dy);
402 AffineTransform newTransform = state().m_transform;
403 newTransform.multiply(transform);
404 if (!newTransform.isInvertible()) {
405 state().m_invertibleCTM = false;
409 state().m_transform = newTransform;
410 c->concatCTM(transform);
411 m_path.transform(transform.inverse());
414 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
416 GraphicsContext* c = drawingContext();
420 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
421 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
422 !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
425 AffineTransform ctm = state().m_transform;
426 if (!ctm.isInvertible())
428 c->concatCTM(c->getCTM().inverse());
429 c->concatCTM(m_canvas->baseTransform());
430 state().m_transform.multiply(ctm.inverse());
431 m_path.transform(ctm);
433 state().m_invertibleCTM = true;
434 transform(m11, m12, m21, m22, dx, dy);
437 void CanvasRenderingContext2D::setStrokeColor(const String& color)
439 setStrokeStyle(CanvasStyle::create(color));
442 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
444 setStrokeStyle(CanvasStyle::create(grayLevel, 1));
447 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
449 setStrokeStyle(CanvasStyle::create(color, alpha));
452 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
454 setStrokeStyle(CanvasStyle::create(grayLevel, alpha));
457 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
459 setStrokeStyle(CanvasStyle::create(r, g, b, a));
462 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
464 setStrokeStyle(CanvasStyle::create(c, m, y, k, a));
467 void CanvasRenderingContext2D::setFillColor(const String& color)
469 setFillStyle(CanvasStyle::create(color));
472 void CanvasRenderingContext2D::setFillColor(float grayLevel)
474 setFillStyle(CanvasStyle::create(grayLevel, 1));
477 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
479 setFillStyle(CanvasStyle::create(color, 1));
482 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
484 setFillStyle(CanvasStyle::create(grayLevel, alpha));
487 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
489 setFillStyle(CanvasStyle::create(r, g, b, a));
492 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
494 setFillStyle(CanvasStyle::create(c, m, y, k, a));
497 void CanvasRenderingContext2D::beginPath()
502 void CanvasRenderingContext2D::closePath()
504 m_path.closeSubpath();
507 void CanvasRenderingContext2D::moveTo(float x, float y)
509 if (!isfinite(x) | !isfinite(y))
511 if (!state().m_invertibleCTM)
513 m_path.moveTo(FloatPoint(x, y));
516 void CanvasRenderingContext2D::lineTo(float x, float y)
518 if (!isfinite(x) | !isfinite(y))
520 if (!state().m_invertibleCTM)
522 m_path.addLineTo(FloatPoint(x, y));
525 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
527 if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y))
529 if (!state().m_invertibleCTM)
531 m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
534 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
536 if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y))
538 if (!state().m_invertibleCTM)
540 m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
543 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
546 if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinite(r))
553 if (!state().m_invertibleCTM)
555 m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
558 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
561 if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea))
568 if (!state().m_invertibleCTM)
570 m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
573 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
575 if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height))
591 void CanvasRenderingContext2D::rect(float x, float y, float width, float height)
593 if (!validateRectForCanvas(x, y, width, height))
595 if (!state().m_invertibleCTM)
597 m_path.addRect(FloatRect(x, y, width, height));
600 #if ENABLE(DASHBOARD_SUPPORT)
601 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
603 if (Settings* settings = m_canvas->document()->settings())
604 if (settings->usesDashboardBackwardCompatibilityMode())
609 void CanvasRenderingContext2D::fill()
611 GraphicsContext* c = drawingContext();
614 if (!state().m_invertibleCTM)
617 if (!m_path.isEmpty()) {
620 willDraw(m_path.boundingRect());
624 #if ENABLE(DASHBOARD_SUPPORT)
625 clearPathForDashboardBackwardCompatibilityMode();
629 void CanvasRenderingContext2D::stroke()
631 GraphicsContext* c = drawingContext();
634 if (!state().m_invertibleCTM)
637 if (!m_path.isEmpty()) {
641 // FIXME: This is insufficient, need to use CGContextReplacePathWithStrokedPath to expand to required bounds
642 float lineWidth = state().m_lineWidth;
643 float inset = lineWidth / 2;
644 FloatRect boundingRect = m_path.boundingRect();
645 boundingRect.inflate(inset);
646 willDraw(boundingRect);
651 #if ENABLE(DASHBOARD_SUPPORT)
652 clearPathForDashboardBackwardCompatibilityMode();
656 void CanvasRenderingContext2D::clip()
658 GraphicsContext* c = drawingContext();
661 if (!state().m_invertibleCTM)
664 #if ENABLE(DASHBOARD_SUPPORT)
665 clearPathForDashboardBackwardCompatibilityMode();
669 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
671 GraphicsContext* c = drawingContext();
674 if (!state().m_invertibleCTM)
677 FloatPoint point(x, y);
678 AffineTransform ctm = state().m_transform;
679 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
680 return m_path.contains(transformedPoint);
683 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
685 if (!validateRectForCanvas(x, y, width, height))
687 GraphicsContext* c = drawingContext();
690 if (!state().m_invertibleCTM)
692 FloatRect rect(x, y, width, height);
697 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
699 if (!validateRectForCanvas(x, y, width, height))
702 GraphicsContext* c = drawingContext();
705 if (!state().m_invertibleCTM)
708 FloatRect rect(x, y, width, height);
716 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
718 if (!validateRectForCanvas(x, y, width, height))
720 strokeRect(x, y, width, height, state().m_lineWidth);
723 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth)
725 if (!validateRectForCanvas(x, y, width, height))
728 if (!(lineWidth >= 0))
731 GraphicsContext* c = drawingContext();
734 if (!state().m_invertibleCTM)
737 FloatRect rect(x, y, width, height);
739 FloatRect boundingRect = rect;
740 boundingRect.inflate(lineWidth / 2);
741 willDraw(boundingRect);
743 c->strokeRect(rect, lineWidth);
747 static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height)
749 // Work around <rdar://problem/5539388> by ensuring that shadow offsets will get truncated
750 // to the desired integer.
751 static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
753 width += extraShadowOffset;
755 width -= extraShadowOffset;
758 height += extraShadowOffset;
760 height -= extraShadowOffset;
762 return CGSizeMake(width, height);
766 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
768 state().m_shadowOffset = FloatSize(width, height);
769 state().m_shadowBlur = blur;
770 state().m_shadowColor = "";
774 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
776 state().m_shadowOffset = FloatSize(width, height);
777 state().m_shadowBlur = blur;
778 state().m_shadowColor = color;
782 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
784 state().m_shadowOffset = FloatSize(width, height);
785 state().m_shadowBlur = blur;
786 state().m_shadowColor = "";
788 GraphicsContext* c = drawingContext();
792 RGBA32 rgba = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f);
793 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
796 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
798 state().m_shadowOffset = FloatSize(width, height);
799 state().m_shadowBlur = blur;
800 state().m_shadowColor = color;
802 GraphicsContext* c = drawingContext();
806 RGBA32 rgba = 0; // default is transparent black
807 if (!state().m_shadowColor.isEmpty())
808 CSSParser::parseColor(rgba, state().m_shadowColor);
809 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(colorWithOverrideAlpha(rgba, alpha)));
812 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
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, alpha);
823 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
826 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
828 state().m_shadowOffset = FloatSize(width, height);
829 state().m_shadowBlur = blur;
830 state().m_shadowColor = "";
832 GraphicsContext* c = drawingContext();
836 RGBA32 rgba = makeRGBA32FromFloats(r, g, b, a); // 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(rgba));
842 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
844 state().m_shadowOffset = FloatSize(width, height);
845 state().m_shadowBlur = blur;
846 state().m_shadowColor = "";
848 GraphicsContext* dc = drawingContext();
851 // FIXME: Do this through platform-independent GraphicsContext API.
853 const CGFloat components[5] = { c, m, y, k, a };
854 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
855 CGColorRef shadowColor = CGColorCreate(colorSpace, components);
856 CGColorSpaceRelease(colorSpace);
857 CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
858 CGColorRelease(shadowColor);
862 void CanvasRenderingContext2D::clearShadow()
864 state().m_shadowOffset = FloatSize();
865 state().m_shadowBlur = 0;
866 state().m_shadowColor = "";
870 void CanvasRenderingContext2D::applyShadow()
872 GraphicsContext* c = drawingContext();
876 RGBA32 rgba = 0; // default is transparent black
877 if (!state().m_shadowColor.isEmpty())
878 CSSParser::parseColor(rgba, state().m_shadowColor);
879 float width = state().m_shadowOffset.width();
880 float height = state().m_shadowOffset.height();
881 c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba));
884 static IntSize size(HTMLImageElement* image)
886 if (CachedImage* cachedImage = image->cachedImage())
887 return cachedImage->imageSize(1.0f); // FIXME: Not sure about this.
891 static inline FloatRect normalizeRect(const FloatRect& rect)
893 return FloatRect(min(rect.x(), rect.right()),
894 min(rect.y(), rect.bottom()),
895 max(rect.width(), -rect.width()),
896 max(rect.height(), -rect.height()));
899 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
902 IntSize s = size(image);
904 drawImage(image, x, y, s.width(), s.height(), ec);
907 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
908 float x, float y, float width, float height, ExceptionCode& ec)
911 IntSize s = size(image);
912 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
915 void CanvasRenderingContext2D::checkOrigin(const KURL& url)
917 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
918 if (!m_canvas->document()->securityOrigin()->canAccess(origin.get()))
919 m_canvas->setOriginTainted();
922 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
929 FloatRect imageRect = FloatRect(FloatPoint(), size(image));
930 if (!imageRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
935 if (!dstRect.width() || !dstRect.height())
938 GraphicsContext* c = drawingContext();
941 if (!state().m_invertibleCTM)
944 CachedImage* cachedImage = image->cachedImage();
948 if (m_canvas->originClean())
949 checkOrigin(cachedImage->response().url());
951 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
952 m_canvas->setOriginTainted();
954 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
955 FloatRect destRect = c->roundToDevicePixels(dstRect);
957 c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
960 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
964 drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
967 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
968 float x, float y, float width, float height, ExceptionCode& ec)
971 drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
974 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
975 const FloatRect& dstRect, ExceptionCode& ec)
981 FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
982 if (!srcCanvasRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
987 if (!dstRect.width() || !dstRect.height())
990 GraphicsContext* c = drawingContext();
993 if (!state().m_invertibleCTM)
996 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
997 FloatRect destRect = c->roundToDevicePixels(dstRect);
999 // FIXME: Do this through platform-independent GraphicsContext API.
1000 ImageBuffer* buffer = canvas->buffer();
1004 if (!canvas->originClean())
1005 m_canvas->setOriginTainted();
1007 c->drawImage(buffer->image(), destRect, sourceRect, state().m_globalComposite);
1008 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.
1009 // FIXME: Arguably willDraw should become didDraw and occur after drawing calls and not before them to avoid problems like this.
1012 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
1013 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1014 float sx, float sy, float sw, float sh,
1015 float dx, float dy, float dw, float dh,
1016 const String& compositeOperation)
1021 CachedImage* cachedImage = image->cachedImage();
1025 if (m_canvas->originClean())
1026 checkOrigin(cachedImage->response().url());
1028 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
1029 m_canvas->setOriginTainted();
1031 GraphicsContext* c = drawingContext();
1034 if (!state().m_invertibleCTM)
1037 CompositeOperator op;
1038 if (!parseCompositeOperator(compositeOperation, op))
1039 op = CompositeSourceOver;
1041 FloatRect destRect = FloatRect(dx, dy, dw, dh);
1043 c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
1046 void CanvasRenderingContext2D::setAlpha(float alpha)
1048 setGlobalAlpha(alpha);
1051 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1053 setGlobalCompositeOperation(operation);
1056 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec)
1058 if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) {
1059 ec = NOT_SUPPORTED_ERR;
1063 return CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1066 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec)
1068 if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) ||
1069 !isfinite(x1) || !isfinite(y1) || !isfinite(r1)) {
1070 ec = NOT_SUPPORTED_ERR;
1073 return CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1076 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1077 const String& repetitionType, ExceptionCode& ec)
1079 bool repeatX, repeatY;
1081 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1085 if (!image->complete()) {
1086 ec = INVALID_STATE_ERR;
1090 CachedImage* cachedImage = image->cachedImage();
1091 if (!cachedImage || !image->cachedImage()->image())
1092 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
1094 KURL url(cachedImage->url());
1095 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
1096 bool originClean = m_canvas->document()->securityOrigin()->canAccess(origin.get());
1097 return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originClean);
1100 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1101 const String& repetitionType, ExceptionCode& ec)
1103 if (!canvas->width() || !canvas->height()) {
1104 ec = INVALID_STATE_ERR;
1108 bool repeatX, repeatY;
1110 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1113 return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, canvas->originClean());
1116 void CanvasRenderingContext2D::willDraw(const FloatRect& r)
1118 GraphicsContext* c = drawingContext();
1121 if (!state().m_invertibleCTM)
1124 m_canvas->willDraw(c->getCTM().mapRect(r));
1127 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1129 return m_canvas->drawingContext();
1132 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1134 PassRefPtr<ImageData> data = ImageData::create(size.width(), size.height());
1135 memset(data->data()->data().data(), 0, data->data()->length());
1139 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh) const
1141 FloatSize unscaledSize(sw, sh);
1142 IntSize scaledSize = m_canvas->convertLogicalToDevice(unscaledSize);
1143 if (scaledSize.width() < 1)
1144 scaledSize.setWidth(1);
1145 if (scaledSize.height() < 1)
1146 scaledSize.setHeight(1);
1148 return createEmptyImageData(scaledSize);
1151 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1153 if (!m_canvas->originClean()) {
1158 FloatRect unscaledRect(sx, sy, sw, sh);
1159 IntRect scaledRect = m_canvas->convertLogicalToDevice(unscaledRect);
1160 if (scaledRect.width() < 1)
1161 scaledRect.setWidth(1);
1162 if (scaledRect.height() < 1)
1163 scaledRect.setHeight(1);
1164 ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0;
1166 return createEmptyImageData(scaledRect.size());
1167 return buffer->getImageData(scaledRect);
1170 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
1173 ec = TYPE_MISMATCH_ERR;
1176 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
1179 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
1180 float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
1183 ec = TYPE_MISMATCH_ERR;
1186 if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) ||
1187 !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
1188 ec = INDEX_SIZE_ERR;
1192 ImageBuffer* buffer = m_canvas->buffer();
1196 if (dirtyWidth < 0) {
1197 dirtyX += dirtyWidth;
1198 dirtyWidth = -dirtyWidth;
1201 if (dirtyHeight < 0) {
1202 dirtyY += dirtyHeight;
1203 dirtyHeight = -dirtyHeight;
1206 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1207 clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1208 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1209 IntRect sourceRect = enclosingIntRect(clipRect);
1210 sourceRect.move(destOffset);
1211 sourceRect.intersect(IntRect(IntPoint(), buffer->size()));
1212 if (sourceRect.isEmpty())
1214 willDraw(sourceRect);
1215 sourceRect.move(-destOffset);
1216 IntPoint destPoint(destOffset.width(), destOffset.height());
1218 buffer->putImageData(data, sourceRect, destPoint);
1221 String CanvasRenderingContext2D::font() const
1223 return state().m_unparsedFont;
1226 void CanvasRenderingContext2D::setFont(const String& newFont)
1228 RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create();
1229 CSSParser parser(!m_canvas->document()->inCompatMode()); // Use the parse mode of the canvas' document when parsing CSS.
1231 String declarationText("font: ");
1232 declarationText += newFont;
1233 parser.parseDeclaration(tempDecl.get(), declarationText);
1234 if (!tempDecl->length())
1237 // The parse succeeded.
1238 state().m_unparsedFont = newFont;
1240 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
1241 // relative to the canvas.
1242 RefPtr<RenderStyle> newStyle = RenderStyle::create();
1243 if (m_canvas->computedStyle())
1244 newStyle->setFontDescription(m_canvas->computedStyle()->fontDescription());
1246 // Now map the font property into the style.
1247 CSSStyleSelector* styleSelector = m_canvas->document()->styleSelector();
1248 styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCSSValue(CSSPropertyFont).get(), newStyle.get());
1250 state().m_font = newStyle->font();
1251 state().m_font.update(styleSelector->fontSelector());
1252 state().m_realizedFont = true;
1254 // Set the font in the graphics context.
1255 GraphicsContext* c = drawingContext();
1258 c->setFont(state().m_font);
1261 String CanvasRenderingContext2D::textAlign() const
1263 return textAlignName(state().m_textAlign);
1266 void CanvasRenderingContext2D::setTextAlign(const String& s)
1269 if (!parseTextAlign(s, align))
1271 state().m_textAlign = align;
1274 String CanvasRenderingContext2D::textBaseline() const
1276 return textBaselineName(state().m_textBaseline);
1279 void CanvasRenderingContext2D::setTextBaseline(const String& s)
1281 TextBaseline baseline;
1282 if (!parseTextBaseline(s, baseline))
1284 state().m_textBaseline = baseline;
1287 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
1289 drawTextInternal(text, x, y, true);
1292 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
1294 drawTextInternal(text, x, y, true, maxWidth, true);
1297 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
1299 drawTextInternal(text, x, y, false);
1302 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
1304 drawTextInternal(text, x, y, false, maxWidth, true);
1307 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
1309 RefPtr<TextMetrics> metrics = TextMetrics::create();
1310 metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length())));
1314 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth, bool useMaxWidth)
1316 GraphicsContext* c = drawingContext();
1319 if (!state().m_invertibleCTM)
1322 const Font& font = accessFont();
1324 // FIXME: Handle maxWidth.
1325 // FIXME: Need to turn off font smoothing.
1327 bool rtl = m_canvas->computedStyle() ? m_canvas->computedStyle()->direction() == RTL : false;
1328 bool override = m_canvas->computedStyle() ? m_canvas->computedStyle()->unicodeBidi() == Override : false;
1330 unsigned length = text.length();
1331 const UChar* string = text.characters();
1332 TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false);
1334 // Draw the item text at the correct point.
1335 FloatPoint location(x, y);
1336 switch (state().m_textBaseline) {
1337 case TopTextBaseline:
1338 case HangingTextBaseline:
1339 location.setY(y + font.ascent());
1341 case BottomTextBaseline:
1342 case IdeographicTextBaseline:
1343 location.setY(y - font.descent());
1345 case MiddleTextBaseline:
1346 location.setY(y - font.descent() + font.height() / 2);
1348 case AlphabeticTextBaseline:
1354 float width = font.width(TextRun(text, false, 0, 0, rtl, override));
1356 TextAlign align = state().m_textAlign;
1357 if (align == StartTextAlign)
1358 align = rtl ? RightTextAlign : LeftTextAlign;
1359 else if (align == EndTextAlign)
1360 align = rtl ? LeftTextAlign : RightTextAlign;
1363 case CenterTextAlign:
1364 location.setX(location.x() - width / 2);
1366 case RightTextAlign:
1367 location.setX(location.x() - width);
1373 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
1374 FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y() - font.ascent() - font.lineGap(),
1375 width + font.height(), font.lineSpacing());
1377 textRect.inflate(c->strokeThickness() / 2);
1379 CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
1380 if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
1381 IntRect maskRect = enclosingIntRect(textRect);
1383 auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), false);
1385 GraphicsContext* maskImageContext = maskImage->context();
1388 maskImageContext->setFillColor(Color::black);
1390 maskImageContext->setStrokeColor(Color::black);
1391 maskImageContext->setStrokeThickness(c->strokeThickness());
1394 maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1395 maskImageContext->translate(-maskRect.x(), -maskRect.y());
1397 maskImageContext->setFont(font);
1398 maskImageContext->drawBidiText(textRun, location);
1401 c->clipToImageBuffer(maskRect, maskImage.get());
1402 drawStyle->applyFillColor(c);
1403 c->fillRect(maskRect);
1409 c->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1410 c->drawBidiText(textRun, location);
1413 const Font& CanvasRenderingContext2D::accessFont()
1415 if (!state().m_realizedFont)
1416 setFont(state().m_unparsedFont);
1417 return state().m_font;
1420 } // namespace WebCore