2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Trolltech ASA
4 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "CanvasRenderingContext2D.h"
31 #include "AffineTransform.h"
32 #include "CSSParser.h"
33 #include "CachedImage.h"
34 #include "CanvasGradient.h"
35 #include "CanvasPattern.h"
36 #include "CanvasStyle.h"
38 #include "ExceptionCode.h"
40 #include "GraphicsContext.h"
41 #include "HTMLCanvasElement.h"
42 #include "HTMLImageElement.h"
43 #include "HTMLNames.h"
44 #include "NotImplemented.h"
45 #include "RenderHTMLCanvas.h"
47 #include <wtf/MathExtras.h>
52 #include <QPainterPath>
54 #include "CairoPath.h"
60 using namespace HTMLNames;
62 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
63 : RefCounted<CanvasRenderingContext2D>(0)
69 void CanvasRenderingContext2D::reset()
71 m_stateStack.resize(1);
72 m_stateStack.first() = State();
75 CanvasRenderingContext2D::State::State()
76 : m_strokeStyle(new CanvasStyle("black"))
77 , m_fillStyle(new CanvasStyle("black"))
80 , m_lineJoin(MiterJoin)
83 , m_shadowColor("black")
85 , m_globalComposite(CompositeSourceOver)
86 , m_appliedStrokePattern(false)
87 , m_appliedFillPattern(false)
91 void CanvasRenderingContext2D::save()
93 ASSERT(m_stateStack.size() >= 1);
94 m_stateStack.append(state());
95 GraphicsContext* c = drawingContext();
101 void CanvasRenderingContext2D::restore()
103 ASSERT(m_stateStack.size() >= 1);
104 if (m_stateStack.size() <= 1)
106 m_stateStack.removeLast();
107 GraphicsContext* c = drawingContext();
113 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
115 return state().m_strokeStyle.get();
118 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
122 state().m_strokeStyle = style;
123 GraphicsContext* c = drawingContext();
126 state().m_strokeStyle->applyStrokeColor(c);
127 state().m_appliedStrokePattern = false;
130 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
132 return state().m_fillStyle.get();
135 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
139 state().m_fillStyle = style;
140 GraphicsContext* c = drawingContext();
144 // FIXME: hack to reduce code duplication in CanvasStyle.cpp
145 state().m_fillStyle->applyStrokeColor(c);
147 state().m_fillStyle->applyFillColor(c);
149 state().m_appliedFillPattern = false;
152 float CanvasRenderingContext2D::lineWidth() const
154 return state().m_lineWidth;
157 void CanvasRenderingContext2D::setLineWidth(float width)
161 state().m_lineWidth = width;
162 GraphicsContext* c = drawingContext();
165 c->setStrokeThickness(width);
168 String CanvasRenderingContext2D::lineCap() const
170 return lineCapName(state().m_lineCap);
173 void CanvasRenderingContext2D::setLineCap(const String& s)
176 if (!parseLineCap(s, cap))
178 state().m_lineCap = cap;
179 GraphicsContext* c = drawingContext();
185 String CanvasRenderingContext2D::lineJoin() const
187 return lineJoinName(state().m_lineJoin);
190 void CanvasRenderingContext2D::setLineJoin(const String& s)
193 if (!parseLineJoin(s, join))
195 state().m_lineJoin = join;
196 GraphicsContext* c = drawingContext();
199 c->setLineJoin(join);
202 float CanvasRenderingContext2D::miterLimit() const
204 return state().m_miterLimit;
207 void CanvasRenderingContext2D::setMiterLimit(float limit)
211 state().m_miterLimit = limit;
212 GraphicsContext* c = drawingContext();
215 c->setMiterLimit(limit);
218 float CanvasRenderingContext2D::shadowOffsetX() const
220 return state().m_shadowOffset.width();
223 void CanvasRenderingContext2D::setShadowOffsetX(float x)
225 state().m_shadowOffset.setWidth(x);
229 float CanvasRenderingContext2D::shadowOffsetY() const
231 return state().m_shadowOffset.height();
234 void CanvasRenderingContext2D::setShadowOffsetY(float y)
236 state().m_shadowOffset.setHeight(y);
240 float CanvasRenderingContext2D::shadowBlur() const
242 return state().m_shadowBlur;
245 void CanvasRenderingContext2D::setShadowBlur(float blur)
247 state().m_shadowBlur = blur;
251 String CanvasRenderingContext2D::shadowColor() const
253 // FIXME: What should this return if you called setShadow with a non-string color?
254 return state().m_shadowColor;
257 void CanvasRenderingContext2D::setShadowColor(const String& color)
259 state().m_shadowColor = color;
263 float CanvasRenderingContext2D::globalAlpha() const
265 return state().m_globalAlpha;
268 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
270 if (!(alpha >= 0 && alpha <= 1))
272 state().m_globalAlpha = alpha;
273 GraphicsContext* c = drawingContext();
279 String CanvasRenderingContext2D::globalCompositeOperation() const
281 return compositeOperatorName(state().m_globalComposite);
284 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
286 CompositeOperator op;
287 if (!parseCompositeOperator(operation, op))
289 state().m_globalComposite = op;
290 GraphicsContext* c = drawingContext();
293 c->setCompositeOperation(op);
296 void CanvasRenderingContext2D::scale(float sx, float sy)
298 GraphicsContext* c = drawingContext();
301 c->scale(FloatSize(sx, sy));
302 state().m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy));
305 void CanvasRenderingContext2D::rotate(float angleInRadians)
307 GraphicsContext* c = drawingContext();
310 c->rotate(angleInRadians);
311 state().m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
314 void CanvasRenderingContext2D::translate(float tx, float ty)
316 GraphicsContext* c = drawingContext();
319 c->translate(tx, ty);
320 state().m_path.transform(AffineTransform().translate(-tx, -ty));
323 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
325 GraphicsContext* c = drawingContext();
329 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
330 if (!isfinite(m11) || !isfinite(m21) || !isfinite(dx) ||
331 !isfinite(m12) || !isfinite(m22) || !isfinite(dy))
333 AffineTransform transform(m11, m12, m21, m22, dx, dy);
334 c->concatCTM(transform);
335 state().m_path.transform(transform.inverse());
338 void CanvasRenderingContext2D::setStrokeColor(const String& color)
340 setStrokeStyle(new CanvasStyle(color));
343 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
345 setStrokeStyle(new CanvasStyle(grayLevel, 1));
348 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
350 setStrokeStyle(new CanvasStyle(color, alpha));
353 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
355 setStrokeStyle(new CanvasStyle(grayLevel, alpha));
358 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
360 setStrokeStyle(new CanvasStyle(r, g, b, a));
363 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
365 setStrokeStyle(new CanvasStyle(c, m, y, k, a));
368 void CanvasRenderingContext2D::setFillColor(const String& color)
370 setFillStyle(new CanvasStyle(color));
373 void CanvasRenderingContext2D::setFillColor(float grayLevel)
375 setFillStyle(new CanvasStyle(grayLevel, 1));
378 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
380 setFillStyle(new CanvasStyle(color, 1));
383 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
385 setFillStyle(new CanvasStyle(grayLevel, alpha));
388 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
390 setFillStyle(new CanvasStyle(r, g, b, a));
393 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
395 setFillStyle(new CanvasStyle(c, m, y, k, a));
398 void CanvasRenderingContext2D::beginPath()
400 state().m_path.clear();
403 void CanvasRenderingContext2D::closePath()
405 state().m_path.closeSubpath();
408 void CanvasRenderingContext2D::moveTo(float x, float y)
410 state().m_path.moveTo(FloatPoint(x, y));
413 void CanvasRenderingContext2D::lineTo(float x, float y)
415 state().m_path.addLineTo(FloatPoint(x, y));
418 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
420 state().m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
423 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
425 state().m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
428 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
435 state().m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
438 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
445 state().m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
448 void CanvasRenderingContext2D::rect(float x, float y, float width, float height, ExceptionCode& ec)
451 if (!(width >= 0 && height >= 0)) {
455 state().m_path.addRect(FloatRect(x, y, width, height));
458 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
461 if (Settings* settings = m_canvas->document()->settings())
462 if (settings->usesDashboardBackwardCompatibilityMode())
463 state().m_path.clear();
466 void CanvasRenderingContext2D::fill()
468 GraphicsContext* c = drawingContext();
471 // FIXME: Do this through platform-independent GraphicsContext API.
473 CGContextBeginPath(c->platformContext());
474 CGContextAddPath(c->platformContext(), state().m_path.platformPath());
476 if (!state().m_path.isEmpty())
477 willDraw(CGContextGetPathBoundingBox(c->platformContext()));
479 if (state().m_fillStyle->gradient()) {
480 // Shading works on the entire clip region, so convert the current path to a clip.
482 CGContextClip(c->platformContext());
483 CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());
486 if (state().m_fillStyle->pattern())
488 CGContextFillPath(c->platformContext());
491 QPainterPath* path = state().m_path.platformPath();
492 QPainter* p = static_cast<QPainter*>(c->platformContext());
493 willDraw(path->controlPointRect());
494 if (state().m_fillStyle->gradient()) {
495 p->fillPath(*path, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
497 if (state().m_fillStyle->pattern())
499 p->fillPath(*path, p->brush());
501 #elif PLATFORM(CAIRO)
502 cairo_t* cr = c->platformContext();
504 willDraw(state().m_path.boundingRect());
505 if (state().m_fillStyle->gradient()) {
506 cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
507 c->addPath(state().m_path);
510 if (state().m_fillStyle->pattern())
512 c->addPath(state().m_path);
518 clearPathForDashboardBackwardCompatibilityMode();
521 void CanvasRenderingContext2D::stroke()
523 GraphicsContext* c = drawingContext();
526 // FIXME: Do this through platform-independent GraphicsContext API.
528 CGContextBeginPath(c->platformContext());
529 CGContextAddPath(c->platformContext(), state().m_path.platformPath());
531 if (!state().m_path.isEmpty()) {
532 float lineWidth = state().m_lineWidth;
533 float inset = -lineWidth / 2;
534 CGRect boundingRect = CGRectInset(CGContextGetPathBoundingBox(c->platformContext()), inset, inset);
535 willDraw(boundingRect);
538 if (state().m_strokeStyle->gradient()) {
539 // Shading works on the entire clip region, so convert the current path to a clip.
541 CGContextReplacePathWithStrokedPath(c->platformContext());
542 CGContextClip(c->platformContext());
543 CGContextDrawShading(c->platformContext(), state().m_strokeStyle->gradient()->platformShading());
546 if (state().m_strokeStyle->pattern())
547 applyStrokePattern();
548 CGContextStrokePath(c->platformContext());
551 QPainterPath* path = state().m_path.platformPath();
552 QPainter* p = static_cast<QPainter*>(c->platformContext());
553 willDraw(path->controlPointRect());
554 if (state().m_strokeStyle->gradient()) {
556 p->setBrush(*(state().m_strokeStyle->gradient()->platformShading()));
557 p->strokePath(*path, p->pen());
560 if (state().m_strokeStyle->pattern())
561 applyStrokePattern();
562 p->strokePath(*path, p->pen());
564 #elif PLATFORM(CAIRO)
565 cairo_t* cr = c->platformContext();
567 // FIXME: consider inset, as in CG
568 willDraw(state().m_path.boundingRect());
569 if (state().m_strokeStyle->gradient()) {
570 cairo_set_source(cr, state().m_strokeStyle->gradient()->platformShading());
571 c->addPath(state().m_path);
574 if (state().m_strokeStyle->pattern())
575 applyStrokePattern();
576 c->addPath(state().m_path);
582 clearPathForDashboardBackwardCompatibilityMode();
585 void CanvasRenderingContext2D::clip()
587 GraphicsContext* c = drawingContext();
590 c->clip(state().m_path);
591 clearPathForDashboardBackwardCompatibilityMode();
594 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
596 GraphicsContext* c = drawingContext();
599 FloatPoint point(x, y);
600 // We have to invert the current transform to ensure we correctly handle the
601 // transforms applied to the current path.
602 AffineTransform ctm = c->getCTM();
603 if (!ctm.isInvertible())
605 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
606 return state().m_path.contains(transformedPoint);
609 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height, ExceptionCode& ec)
612 if (!(width >= 0 && height >= 0)) {
616 GraphicsContext* c = drawingContext();
619 FloatRect rect(x, y, width, height);
624 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height, ExceptionCode& ec)
628 if (!(width >= 0 && height >= 0)) {
633 GraphicsContext* c = drawingContext();
636 // FIXME: Do this through platform-independent GraphicsContext API.
638 CGRect rect = CGRectMake(x, y, width, height);
642 if (state().m_fillStyle->gradient()) {
643 // Shading works on the entire clip region, so convert the rect to a clip.
645 CGContextClipToRect(c->platformContext(), rect);
646 CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());
649 if (state().m_fillStyle->pattern())
651 CGContextFillRect(c->platformContext(), rect);
654 QRectF rect(x, y, width, height);
656 QPainter* p = static_cast<QPainter*>(c->platformContext());
657 if (state().m_fillStyle->gradient()) {
658 p->fillRect(rect, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
660 if (state().m_fillStyle->pattern())
662 p->fillRect(rect, p->brush());
664 #elif PLATFORM(CAIRO)
665 FloatRect rect(x, y, width, height);
667 cairo_t* cr = c->platformContext();
669 if (state().m_fillStyle->gradient()) {
670 cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
672 if (state().m_fillStyle->pattern())
675 cairo_rectangle(cr, x, y, width, height);
681 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, ExceptionCode& ec)
683 strokeRect(x, y, width, height, state().m_lineWidth, ec);
686 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth, ExceptionCode& ec)
690 if (!(width >= 0 && height >= 0 && lineWidth >= 0)) {
695 GraphicsContext* c = drawingContext();
699 FloatRect rect(x, y, width, height);
701 FloatRect boundingRect = rect;
702 boundingRect.inflate(lineWidth / 2);
703 willDraw(boundingRect);
705 // FIXME: No support for gradients!
706 if (state().m_strokeStyle->pattern())
707 applyStrokePattern();
709 c->strokeRect(rect, lineWidth);
712 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
714 state().m_shadowOffset = FloatSize(width, height);
715 state().m_shadowBlur = blur;
716 state().m_shadowColor = "";
720 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
722 state().m_shadowOffset = FloatSize(width, height);
723 state().m_shadowBlur = blur;
724 state().m_shadowColor = color;
728 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
730 state().m_shadowOffset = FloatSize(width, height);
731 state().m_shadowBlur = blur;
732 state().m_shadowColor = "";
734 GraphicsContext* c = drawingContext();
737 // FIXME: Do this through platform-independent GraphicsContext API.
739 const CGFloat components[2] = { grayLevel, 1 };
740 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
741 CGColorRef color = CGColorCreate(colorSpace, components);
742 CGColorSpaceRelease(colorSpace);
743 CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
744 CGColorRelease(color);
748 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
750 state().m_shadowOffset = FloatSize(width, height);
751 state().m_shadowBlur = blur;
752 state().m_shadowColor = color;
754 GraphicsContext* c = drawingContext();
757 // FIXME: Do this through platform-independent GraphicsContext API.
759 RGBA32 rgba = 0; // default is transparent black
760 CSSParser::parseColor(rgba, color);
761 const CGFloat components[4] = {
762 ((rgba >> 16) & 0xFF) / 255.0f,
763 ((rgba >> 8) & 0xFF) / 255.0f,
764 (rgba & 0xFF) / 255.0f,
767 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
768 CGColorRef shadowColor = CGColorCreate(colorSpace, components);
769 CGColorSpaceRelease(colorSpace);
770 CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
771 CGColorRelease(shadowColor);
775 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
777 state().m_shadowOffset = FloatSize(width, height);
778 state().m_shadowBlur = blur;
779 state().m_shadowColor = "";
781 GraphicsContext* c = drawingContext();
784 // FIXME: Do this through platform-independent GraphicsContext API.
786 const CGFloat components[2] = { grayLevel, alpha };
787 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
788 CGColorRef color = CGColorCreate(colorSpace, components);
789 CGColorSpaceRelease(colorSpace);
790 CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
791 CGColorRelease(color);
795 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
797 state().m_shadowOffset = FloatSize(width, height);
798 state().m_shadowBlur = blur;
799 state().m_shadowColor = "";
801 GraphicsContext* c = drawingContext();
804 // FIXME: Do this through platform-independent GraphicsContext API.
806 const CGFloat components[4] = { r, g, b, a };
807 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
808 CGColorRef shadowColor = CGColorCreate(colorSpace, components);
809 CGColorSpaceRelease(colorSpace);
810 CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
811 CGColorRelease(shadowColor);
815 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
817 state().m_shadowOffset = FloatSize(width, height);
818 state().m_shadowBlur = blur;
819 state().m_shadowColor = "";
821 GraphicsContext* dc = drawingContext();
824 // FIXME: Do this through platform-independent GraphicsContext API.
826 const CGFloat components[5] = { c, m, y, k, a };
827 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
828 CGColorRef shadowColor = CGColorCreate(colorSpace, components);
829 CGColorSpaceRelease(colorSpace);
830 CGContextSetShadowWithColor(dc->platformContext(), CGSizeMake(width, height), blur, shadowColor);
831 CGColorRelease(shadowColor);
835 void CanvasRenderingContext2D::clearShadow()
837 state().m_shadowOffset = FloatSize();
838 state().m_shadowBlur = 0;
839 state().m_shadowColor = "";
843 void CanvasRenderingContext2D::applyShadow()
845 GraphicsContext* c = drawingContext();
848 // FIXME: Do this through platform-independent GraphicsContext API.
850 RGBA32 rgba = 0; // default is transparent black
851 if (!state().m_shadowColor.isEmpty())
852 CSSParser::parseColor(rgba, state().m_shadowColor);
853 const CGFloat components[4] = {
854 ((rgba >> 16) & 0xFF) / 255.0f,
855 ((rgba >> 8) & 0xFF) / 255.0f,
856 (rgba & 0xFF) / 255.0f,
857 ((rgba >> 24) & 0xFF) / 255.0f
859 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
860 CGColorRef color = CGColorCreate(colorSpace, components);
861 CGColorSpaceRelease(colorSpace);
862 CGContextSetShadowWithColor(c->platformContext(), state().m_shadowOffset, state().m_shadowBlur, color);
863 CGColorRelease(color);
867 static IntSize size(HTMLImageElement* image)
869 if (CachedImage* cachedImage = image->cachedImage())
870 return cachedImage->imageSize();
874 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
877 IntSize s = size(image);
879 drawImage(image, x, y, s.width(), s.height(), ec);
882 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
883 float x, float y, float width, float height, ExceptionCode& ec)
886 IntSize s = size(image);
887 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
890 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
897 FloatRect imageRect = FloatRect(FloatPoint(), size(image));
898 if (!(imageRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0
899 && dstRect.width() >= 0 && dstRect.height() >= 0)) {
904 if (srcRect.isEmpty() || dstRect.isEmpty())
907 GraphicsContext* c = drawingContext();
911 CachedImage* cachedImage = image->cachedImage();
915 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
916 FloatRect destRect = c->roundToDevicePixels(dstRect);
918 c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
921 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
925 drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
928 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
929 float x, float y, float width, float height, ExceptionCode& ec)
932 drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
935 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
936 const FloatRect& dstRect, ExceptionCode& ec)
942 FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
943 if (!(srcCanvasRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0
944 && dstRect.width() >= 0 && dstRect.height() >= 0)) {
949 if (srcRect.isEmpty() || dstRect.isEmpty())
952 GraphicsContext* c = drawingContext();
956 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
957 FloatRect destRect = c->roundToDevicePixels(dstRect);
959 // FIXME: Do this through platform-independent GraphicsContext API.
960 ImageBuffer* buffer = canvas->buffer();
965 c->drawImage(buffer, sourceRect, destRect);
968 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
969 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
970 float sx, float sy, float sw, float sh,
971 float dx, float dy, float dw, float dh,
972 const String& compositeOperation)
977 CachedImage* cachedImage = image->cachedImage();
981 GraphicsContext* c = drawingContext();
985 CompositeOperator op;
986 if (!parseCompositeOperator(compositeOperation, op))
987 op = CompositeSourceOver;
989 FloatRect destRect = FloatRect(dx, dy, dw, dh);
991 c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
994 void CanvasRenderingContext2D::setAlpha(float alpha)
996 setGlobalAlpha(alpha);
999 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1001 setGlobalCompositeOperation(operation);
1004 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
1006 return new CanvasGradient(FloatPoint(x0, y0), FloatPoint(x1, y1));
1009 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
1011 return new CanvasGradient(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1014 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1015 const String& repetitionType, ExceptionCode& ec)
1017 bool repeatX, repeatY;
1019 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1022 return new CanvasPattern(image ? image->cachedImage() : 0, repeatX, repeatY);
1025 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1026 const String& repetitionType, ExceptionCode& ec)
1028 bool repeatX, repeatY;
1030 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1033 // FIXME: Do this through platform-independent GraphicsContext API.
1035 CGImageRef image = canvas->createPlatformImage();
1038 PassRefPtr<CanvasPattern> pattern = new CanvasPattern(image, repeatX, repeatY);
1039 CGImageRelease(image);
1041 #elif PLATFORM(CAIRO)
1042 cairo_surface_t* surface = canvas->createPlatformImage();
1045 PassRefPtr<CanvasPattern> pattern = new CanvasPattern(surface, repeatX, repeatY);
1046 cairo_surface_destroy(surface);
1054 void CanvasRenderingContext2D::willDraw(const FloatRect& r)
1058 GraphicsContext* c = drawingContext();
1062 m_canvas->willDraw(c->getCTM().mapRect(r));
1065 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1069 return m_canvas->drawingContext();
1072 void CanvasRenderingContext2D::applyStrokePattern()
1074 GraphicsContext* c = drawingContext();
1079 // Check for case where the pattern is already set.
1080 CGAffineTransform m = CGContextGetCTM(c->platformContext());
1081 if (state().m_appliedStrokePattern
1082 && CGAffineTransformEqualToTransform(m, state().m_strokeStylePatternTransform))
1085 CanvasPattern* pattern = state().m_strokeStyle->pattern();
1089 CGPatternRef platformPattern = pattern->createPattern(m);
1090 if (!platformPattern)
1093 CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
1094 CGContextSetStrokeColorSpace(c->platformContext(), patternSpace);
1095 CGColorSpaceRelease(patternSpace);
1097 const CGFloat patternAlpha = 1;
1098 CGContextSetStrokePattern(c->platformContext(), platformPattern, &patternAlpha);
1099 CGPatternRelease(platformPattern);
1101 state().m_strokeStylePatternTransform = m;
1103 fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyStrokePattern\n");
1104 #elif PLATFORM(CAIRO)
1105 CanvasPattern* pattern = state().m_strokeStyle->pattern();
1109 cairo_t* cr = c->platformContext();
1111 cairo_get_matrix(cr, &m);
1113 cairo_pattern_t* platformPattern = pattern->createPattern(m);
1114 if (!platformPattern)
1117 cairo_set_source(cr, platformPattern);
1118 cairo_pattern_destroy(platformPattern);
1120 state().m_appliedStrokePattern = true;
1123 void CanvasRenderingContext2D::applyFillPattern()
1125 GraphicsContext* c = drawingContext();
1130 // Check for case where the pattern is already set.
1131 CGAffineTransform m = CGContextGetCTM(c->platformContext());
1132 if (state().m_appliedFillPattern
1133 && CGAffineTransformEqualToTransform(m, state().m_fillStylePatternTransform))
1136 CanvasPattern* pattern = state().m_fillStyle->pattern();
1140 CGPatternRef platformPattern = pattern->createPattern(m);
1141 if (!platformPattern)
1144 CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
1145 CGContextSetFillColorSpace(c->platformContext(), patternSpace);
1146 CGColorSpaceRelease(patternSpace);
1148 const CGFloat patternAlpha = 1;
1149 CGContextSetFillPattern(c->platformContext(), platformPattern, &patternAlpha);
1150 CGPatternRelease(platformPattern);
1152 state().m_fillStylePatternTransform = m;
1154 fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyFillPattern\n");
1155 #elif PLATFORM(CAIRO)
1156 CanvasPattern* pattern = state().m_fillStyle->pattern();
1160 cairo_t* cr = c->platformContext();
1162 cairo_get_matrix(cr, &m);
1164 cairo_pattern_t* platformPattern = pattern->createPattern(m);
1165 if (!platformPattern)
1168 cairo_set_source(cr, platformPattern);
1169 cairo_pattern_destroy(platformPattern);
1171 state().m_appliedFillPattern = true;
1174 } // namespace WebCore