summaryrefslogtreecommitdiffstats
path: root/webkit/pending/CanvasRenderingContext2D.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/pending/CanvasRenderingContext2D.cpp')
-rw-r--r--webkit/pending/CanvasRenderingContext2D.cpp936
1 files changed, 504 insertions, 432 deletions
diff --git a/webkit/pending/CanvasRenderingContext2D.cpp b/webkit/pending/CanvasRenderingContext2D.cpp
index 1d28a2f..12cdcd4 100644
--- a/webkit/pending/CanvasRenderingContext2D.cpp
+++ b/webkit/pending/CanvasRenderingContext2D.cpp
@@ -2,6 +2,7 @@
* Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2007 Trolltech ASA
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,56 +34,56 @@
#include "CachedImage.h"
#include "CanvasGradient.h"
#include "CanvasPattern.h"
+#include "CanvasPixelArray.h"
#include "CanvasStyle.h"
+#include "CSSPropertyNames.h"
+#include "CSSStyleSelector.h"
#include "Document.h"
#include "ExceptionCode.h"
+#include "FloatConversion.h"
#include "Frame.h"
#include "GraphicsContext.h"
#include "HTMLCanvasElement.h"
#include "HTMLImageElement.h"
#include "HTMLNames.h"
+#include "ImageBuffer.h"
+#include "ImageData.h"
+#include "KURL.h"
#include "NotImplemented.h"
+#include "Page.h"
#include "RenderHTMLCanvas.h"
+#include "SecurityOrigin.h"
#include "Settings.h"
+#include "TextMetrics.h"
+#include <kjs/interpreter.h>
+#include <stdio.h>
#include <wtf/MathExtras.h>
-// Use Android canvas implementation instead
-#undef WTF_PLATFORM_CAIRO
-
-#if PLATFORM(QT)
-#include <QPainter>
-#include <QPixmap>
-#include <QPainterPath>
-#elif PLATFORM(CAIRO)
-#include "CairoPath.h"
-#include <cairo.h>
-#elif defined(ANDROID_CANVAS_IMPL)
-#include "PlatformContextSkia.h"
-#include "base/gfx/platform_canvas_win.h"
-#endif
+using namespace std;
namespace WebCore {
using namespace HTMLNames;
-#ifdef ANDROID_CANVAS_IMPL
-static PlatformGradient* extractGradient(CanvasStyle* style)
+const char* defaultFont = "10px sans-serif";
+
+CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
+ : m_canvas(canvas)
+ , m_stateStack(1)
+#if USE(V8)
+ , m_peer(0)
+#endif
{
- CanvasGradient* grad = style->gradient();
- return grad ? grad->platformGradient() : NULL;
}
-static PlatformPattern* extractPattern(CanvasStyle* style)
+void CanvasRenderingContext2D::ref()
{
- CanvasPattern* pat = style->pattern();
- return pat ? pat->platformPattern() : NULL;
+ m_canvas->ref();
}
-#endif
-CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
- : m_canvas(canvas)
- , m_stateStack(1)
+void CanvasRenderingContext2D::deref()
{
+ m_canvas->deref();
}
void CanvasRenderingContext2D::reset()
@@ -92,8 +93,8 @@ void CanvasRenderingContext2D::reset()
}
CanvasRenderingContext2D::State::State()
- : m_strokeStyle(new CanvasStyle("black"))
- , m_fillStyle(new CanvasStyle("black"))
+ : m_strokeStyle(CanvasStyle::create("black"))
+ , m_fillStyle(CanvasStyle::create("black"))
, m_lineWidth(1)
, m_lineCap(ButtCap)
, m_lineJoin(MiterJoin)
@@ -102,8 +103,10 @@ CanvasRenderingContext2D::State::State()
, m_shadowColor("black")
, m_globalAlpha(1)
, m_globalComposite(CompositeSourceOver)
- , m_appliedStrokePattern(false)
- , m_appliedFillPattern(false)
+ , m_textAlign(StartTextAlign)
+ , m_textBaseline(AlphabeticTextBaseline)
+ , m_unparsedFont(defaultFont)
+ , m_realizedFont(false)
{
}
@@ -122,7 +125,9 @@ void CanvasRenderingContext2D::restore()
ASSERT(m_stateStack.size() >= 1);
if (m_stateStack.size() <= 1)
return;
+ m_path.transform(state().m_transform);
m_stateStack.removeLast();
+ m_path.transform(state().m_transform.inverse());
GraphicsContext* c = drawingContext();
if (!c)
return;
@@ -138,12 +143,19 @@ void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
{
if (!style)
return;
+
+ if (m_canvas->originClean()) {
+ if (CanvasPattern* pattern = style->canvasPattern()) {
+ if (!pattern->originClean())
+ m_canvas->setOriginTainted();
+ }
+ }
+
state().m_strokeStyle = style;
GraphicsContext* c = drawingContext();
if (!c)
return;
state().m_strokeStyle->applyStrokeColor(c);
- state().m_appliedStrokePattern = false;
}
CanvasStyle* CanvasRenderingContext2D::fillStyle() const
@@ -155,17 +167,19 @@ void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
{
if (!style)
return;
+
+ if (m_canvas->originClean()) {
+ if (CanvasPattern* pattern = style->canvasPattern()) {
+ if (!pattern->originClean())
+ m_canvas->setOriginTainted();
+ }
+ }
+
state().m_fillStyle = style;
GraphicsContext* c = drawingContext();
if (!c)
return;
-#if PLATFORM(CAIRO)
- // FIXME: hack to reduce code duplication in CanvasStyle.cpp
- state().m_fillStyle->applyStrokeColor(c);
-#else
state().m_fillStyle->applyFillColor(c);
-#endif
- state().m_appliedFillPattern = false;
}
float CanvasRenderingContext2D::lineWidth() const
@@ -318,7 +332,8 @@ void CanvasRenderingContext2D::scale(float sx, float sy)
if (!c)
return;
c->scale(FloatSize(sx, sy));
- state().m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy));
+ state().m_transform.scale(sx, sy);
+ m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy));
}
void CanvasRenderingContext2D::rotate(float angleInRadians)
@@ -327,7 +342,8 @@ void CanvasRenderingContext2D::rotate(float angleInRadians)
if (!c)
return;
c->rotate(angleInRadians);
- state().m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
+ state().m_transform.rotate(angleInRadians / piDouble * 180.0);
+ m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
}
void CanvasRenderingContext2D::translate(float tx, float ty)
@@ -336,7 +352,8 @@ void CanvasRenderingContext2D::translate(float tx, float ty)
if (!c)
return;
c->translate(tx, ty);
- state().m_path.transform(AffineTransform().translate(-tx, -ty));
+ state().m_transform.translate(tx, ty);
+ m_path.transform(AffineTransform().translate(-tx, -ty));
}
void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
@@ -346,202 +363,192 @@ void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float
return;
// HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
- if (!isfinite(m11) || !isfinite(m21) || !isfinite(dx) ||
- !isfinite(m12) || !isfinite(m22) || !isfinite(dy))
+ if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
+ !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
return;
AffineTransform transform(m11, m12, m21, m22, dx, dy);
c->concatCTM(transform);
- state().m_path.transform(transform.inverse());
+ state().m_transform.multiply(transform);
+ m_path.transform(transform.inverse());
}
void CanvasRenderingContext2D::setStrokeColor(const String& color)
{
- setStrokeStyle(new CanvasStyle(color));
+ setStrokeStyle(CanvasStyle::create(color));
}
void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
{
- setStrokeStyle(new CanvasStyle(grayLevel, 1));
+ setStrokeStyle(CanvasStyle::create(grayLevel, 1));
}
void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
{
- setStrokeStyle(new CanvasStyle(color, alpha));
+ setStrokeStyle(CanvasStyle::create(color, alpha));
}
void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
{
- setStrokeStyle(new CanvasStyle(grayLevel, alpha));
+ setStrokeStyle(CanvasStyle::create(grayLevel, alpha));
}
void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
{
- setStrokeStyle(new CanvasStyle(r, g, b, a));
+ setStrokeStyle(CanvasStyle::create(r, g, b, a));
}
void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
{
- setStrokeStyle(new CanvasStyle(c, m, y, k, a));
+ setStrokeStyle(CanvasStyle::create(c, m, y, k, a));
}
void CanvasRenderingContext2D::setFillColor(const String& color)
{
- setFillStyle(new CanvasStyle(color));
+ setFillStyle(CanvasStyle::create(color));
}
void CanvasRenderingContext2D::setFillColor(float grayLevel)
{
- setFillStyle(new CanvasStyle(grayLevel, 1));
+ setFillStyle(CanvasStyle::create(grayLevel, 1));
}
void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
{
- setFillStyle(new CanvasStyle(color, 1));
+ setFillStyle(CanvasStyle::create(color, 1));
}
void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
{
- setFillStyle(new CanvasStyle(grayLevel, alpha));
+ setFillStyle(CanvasStyle::create(grayLevel, alpha));
}
void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
{
- setFillStyle(new CanvasStyle(r, g, b, a));
+ setFillStyle(CanvasStyle::create(r, g, b, a));
}
void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
{
- setFillStyle(new CanvasStyle(c, m, y, k, a));
+ setFillStyle(CanvasStyle::create(c, m, y, k, a));
}
void CanvasRenderingContext2D::beginPath()
{
- state().m_path.clear();
+ m_path.clear();
}
void CanvasRenderingContext2D::closePath()
{
- state().m_path.closeSubpath();
+ m_path.closeSubpath();
}
void CanvasRenderingContext2D::moveTo(float x, float y)
{
- state().m_path.moveTo(FloatPoint(x, y));
+ if (!isfinite(x) | !isfinite(y))
+ return;
+ m_path.moveTo(FloatPoint(x, y));
}
void CanvasRenderingContext2D::lineTo(float x, float y)
{
- state().m_path.addLineTo(FloatPoint(x, y));
+ if (!isfinite(x) | !isfinite(y))
+ return;
+ m_path.addLineTo(FloatPoint(x, y));
}
void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
{
- state().m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
+ if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y))
+ return;
+ m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
}
void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
{
- state().m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
+ if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y))
+ return;
+ m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
}
void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
{
ec = 0;
- if (!(r > 0)) {
+ if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinite(r))
+ return;
+
+ if (r < 0) {
ec = INDEX_SIZE_ERR;
return;
}
- state().m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
+
+ m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
}
void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
{
ec = 0;
- if (!(r > 0)) {
+ if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea))
+ return;
+
+ if (r < 0) {
ec = INDEX_SIZE_ERR;
return;
}
- state().m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
+
+ m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
+}
+
+static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
+{
+ if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height))
+ return false;
+
+ if (width < 0) {
+ width = -width;
+ x -= width;
+ }
+
+ if (height < 0) {
+ height = -height;
+ y -= height;
+ }
+
+ return true;
}
-void CanvasRenderingContext2D::rect(float x, float y, float width, float height, ExceptionCode& ec)
+void CanvasRenderingContext2D::rect(float x, float y, float width, float height)
{
- ec = 0;
- if (!(width >= 0 && height >= 0)) {
- ec = INDEX_SIZE_ERR;
+ if (!validateRectForCanvas(x, y, width, height))
return;
- }
- state().m_path.addRect(FloatRect(x, y, width, height));
+
+ m_path.addRect(FloatRect(x, y, width, height));
}
+#if ENABLE(DASHBOARD_SUPPORT)
void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
{
- if (m_canvas)
- if (Settings* settings = m_canvas->document()->settings())
- if (settings->usesDashboardBackwardCompatibilityMode())
- state().m_path.clear();
+ if (Settings* settings = m_canvas->document()->settings())
+ if (settings->usesDashboardBackwardCompatibilityMode())
+ m_path.clear();
}
+#endif
void CanvasRenderingContext2D::fill()
{
GraphicsContext* c = drawingContext();
if (!c)
return;
- // FIXME: Do this through platform-independent GraphicsContext API.
-#if PLATFORM(CG)
- CGContextBeginPath(c->platformContext());
- CGContextAddPath(c->platformContext(), state().m_path.platformPath());
- if (!state().m_path.isEmpty())
- willDraw(CGContextGetPathBoundingBox(c->platformContext()));
+ c->beginPath();
+ c->addPath(m_path);
+ if (!m_path.isEmpty())
+ willDraw(m_path.boundingRect());
- if (state().m_fillStyle->gradient()) {
- // Shading works on the entire clip region, so convert the current path to a clip.
- c->save();
- CGContextClip(c->platformContext());
- CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());
- c->restore();
- } else {
- if (state().m_fillStyle->pattern())
- applyFillPattern();
- CGContextFillPath(c->platformContext());
- }
-#elif PLATFORM(QT)
- QPainterPath* path = state().m_path.platformPath();
- QPainter* p = static_cast<QPainter*>(c->platformContext());
- willDraw(path->controlPointRect());
- if (state().m_fillStyle->gradient()) {
- p->fillPath(*path, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
- } else {
- if (state().m_fillStyle->pattern())
- applyFillPattern();
- p->fillPath(*path, p->brush());
- }
-#elif PLATFORM(CAIRO)
- cairo_t* cr = c->platformContext();
- cairo_save(cr);
- willDraw(state().m_path.boundingRect());
- if (state().m_fillStyle->gradient()) {
- cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
- c->addPath(state().m_path);
- cairo_fill(cr);
- } else {
- if (state().m_fillStyle->pattern())
- applyFillPattern();
- c->addPath(state().m_path);
- cairo_fill(cr);
- }
- cairo_restore(cr);
-#elif defined(ANDROID_CANVAS_IMPL)
- CanvasStyle* s = state().m_fillStyle.get();
- const Path& path = state().m_path;
- if (!path.isEmpty()) {
- willDraw(path.boundingRect());
- c->fillPath(path, extractGradient(s), extractPattern(s));
- }
-#endif
+ c->fillPath();
+#if ENABLE(DASHBOARD_SUPPORT)
clearPathForDashboardBackwardCompatibilityMode();
+#endif
}
void CanvasRenderingContext2D::stroke()
@@ -549,76 +556,23 @@ void CanvasRenderingContext2D::stroke()
GraphicsContext* c = drawingContext();
if (!c)
return;
- // FIXME: Do this through platform-independent GraphicsContext API.
-#if PLATFORM(CG)
- CGContextBeginPath(c->platformContext());
- CGContextAddPath(c->platformContext(), state().m_path.platformPath());
+ c->beginPath();
+ c->addPath(m_path);
- if (!state().m_path.isEmpty()) {
+ if (!m_path.isEmpty()) {
+ // FIXME: This is insufficient, need to use CGContextReplacePathWithStrokedPath to expand to required bounds
float lineWidth = state().m_lineWidth;
- float inset = -lineWidth / 2;
- CGRect boundingRect = CGRectInset(CGContextGetPathBoundingBox(c->platformContext()), inset, inset);
+ float inset = lineWidth / 2;
+ FloatRect boundingRect = m_path.boundingRect();
+ boundingRect.inflate(inset);
willDraw(boundingRect);
}
- if (state().m_strokeStyle->gradient()) {
- // Shading works on the entire clip region, so convert the current path to a clip.
- c->save();
- CGContextReplacePathWithStrokedPath(c->platformContext());
- CGContextClip(c->platformContext());
- CGContextDrawShading(c->platformContext(), state().m_strokeStyle->gradient()->platformShading());
- c->restore();
- } else {
- if (state().m_strokeStyle->pattern())
- applyStrokePattern();
- CGContextStrokePath(c->platformContext());
- }
-#elif PLATFORM(QT)
- QPainterPath* path = state().m_path.platformPath();
- QPainter* p = static_cast<QPainter*>(c->platformContext());
- willDraw(path->controlPointRect());
- if (state().m_strokeStyle->gradient()) {
- p->save();
- p->setBrush(*(state().m_strokeStyle->gradient()->platformShading()));
- p->strokePath(*path, p->pen());
- p->restore();
- } else {
- if (state().m_strokeStyle->pattern())
- applyStrokePattern();
- p->strokePath(*path, p->pen());
- }
-#elif PLATFORM(CAIRO)
- cairo_t* cr = c->platformContext();
- cairo_save(cr);
- // FIXME: consider inset, as in CG
- willDraw(state().m_path.boundingRect());
- if (state().m_strokeStyle->gradient()) {
- cairo_set_source(cr, state().m_strokeStyle->gradient()->platformShading());
- c->addPath(state().m_path);
- cairo_stroke(cr);
- } else {
- if (state().m_strokeStyle->pattern())
- applyStrokePattern();
- c->addPath(state().m_path);
- cairo_stroke(cr);
- }
- cairo_restore(cr);
-#elif defined(ANDROID_CANVAS_IMPL)
- // TODO(pamg): Someone familiar with this code should determine whether
- // this check is needed. It was outside the top #if in the previous
- // version of this file, so I'm copying it down here to keep the behavior
- // unchanged.
- if (state().m_path.isEmpty())
- return;
- CanvasStyle* s = state().m_strokeStyle.get();
- const Path& path = state().m_path;
- FloatRect bounds = path.boundingRect();
- bounds.inflate(state().m_lineWidth * 0.5f);
- willDraw(bounds);
- c->strokePath(path, extractGradient(s), extractPattern(s));
-#endif
+ c->strokePath();
+#if ENABLE(DASHBOARD_SUPPORT)
clearPathForDashboardBackwardCompatibilityMode();
+#endif
}
void CanvasRenderingContext2D::clip()
@@ -626,8 +580,10 @@ void CanvasRenderingContext2D::clip()
GraphicsContext* c = drawingContext();
if (!c)
return;
- c->clip(state().m_path);
+ c->clip(m_path);
+#if ENABLE(DASHBOARD_SUPPORT)
clearPathForDashboardBackwardCompatibilityMode();
+#endif
}
bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
@@ -638,20 +594,17 @@ bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
FloatPoint point(x, y);
// We have to invert the current transform to ensure we correctly handle the
// transforms applied to the current path.
- AffineTransform ctm = c->getCTM();
+ AffineTransform ctm = state().m_transform;
if (!ctm.isInvertible())
return false;
FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
- return state().m_path.contains(transformedPoint);
+ return m_path.contains(transformedPoint);
}
-void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height, ExceptionCode& ec)
+void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
{
- ec = 0;
- if (!(width >= 0 && height >= 0)) {
- ec = INDEX_SIZE_ERR;
+ if (!validateRectForCanvas(x, y, width, height))
return;
- }
GraphicsContext* c = drawingContext();
if (!c)
return;
@@ -660,81 +613,37 @@ void CanvasRenderingContext2D::clearRect(float x, float y, float width, float he
c->clearRect(rect);
}
-void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height, ExceptionCode& ec)
+void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
{
- ec = 0;
-
- if (!(width >= 0 && height >= 0)) {
- ec = INDEX_SIZE_ERR;
+ if (!validateRectForCanvas(x, y, width, height))
return;
- }
GraphicsContext* c = drawingContext();
if (!c)
return;
- // FIXME: Do this through platform-independent GraphicsContext API.
-#if PLATFORM(CG)
- CGRect rect = CGRectMake(x, y, width, height);
- willDraw(rect);
-
- if (state().m_fillStyle->gradient()) {
- // Shading works on the entire clip region, so convert the rect to a clip.
- c->save();
- CGContextClipToRect(c->platformContext(), rect);
- CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());
- c->restore();
- } else {
- if (state().m_fillStyle->pattern())
- applyFillPattern();
- CGContextFillRect(c->platformContext(), rect);
- }
-#elif PLATFORM(QT)
- QRectF rect(x, y, width, height);
- willDraw(rect);
- QPainter* p = static_cast<QPainter*>(c->platformContext());
- if (state().m_fillStyle->gradient()) {
- p->fillRect(rect, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
- } else {
- if (state().m_fillStyle->pattern())
- applyFillPattern();
- p->fillRect(rect, p->brush());
- }
-#elif PLATFORM(CAIRO)
FloatRect rect(x, y, width, height);
willDraw(rect);
- cairo_t* cr = c->platformContext();
- cairo_save(cr);
- if (state().m_fillStyle->gradient()) {
- cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
- } else {
- if (state().m_fillStyle->pattern())
- applyFillPattern();
- }
- cairo_rectangle(cr, x, y, width, height);
- cairo_fill(cr);
- cairo_restore(cr);
-#elif defined(ANDROID_CANVAS_IMPL)
- CanvasStyle* s = state().m_fillStyle.get();
- FloatRect rect = FloatRect(x, y, width, height);
- willDraw(rect);
- c->fillRect(rect, extractGradient(s), extractPattern(s));
-#endif
+
+ c->save();
+ c->fillRect(rect);
+ c->restore();
}
-void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, ExceptionCode& ec)
+void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
{
- strokeRect(x, y, width, height, state().m_lineWidth, ec);
+ if (!validateRectForCanvas(x, y, width, height))
+ return;
+ strokeRect(x, y, width, height, state().m_lineWidth);
}
-void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth, ExceptionCode& ec)
+void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth)
{
- ec = 0;
-
- if (!(width >= 0 && height >= 0 && lineWidth >= 0)) {
- ec = INDEX_SIZE_ERR;
+ if (!validateRectForCanvas(x, y, width, height))
+ return;
+
+ if (!(lineWidth >= 0))
return;
- }
GraphicsContext* c = drawingContext();
if (!c)
@@ -746,18 +655,28 @@ void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float h
boundingRect.inflate(lineWidth / 2);
willDraw(boundingRect);
-#ifndef ANDROID_CANVAS_IMPL
- // FIXME: No support for gradients!
- if (state().m_strokeStyle->pattern())
- applyStrokePattern();
-
c->strokeRect(rect, lineWidth);
-#else
- CanvasStyle* s = state().m_strokeStyle.get();
- c->strokeRect(rect, lineWidth, extractGradient(s), extractPattern(s));
-#endif
+}
+
+#if PLATFORM(CG)
+static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height)
+{
+ // Work around <rdar://problem/5539388> by ensuring that shadow offsets will get truncated
+ // to the desired integer.
+ static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
+ if (width > 0)
+ width += extraShadowOffset;
+ else if (width < 0)
+ width -= extraShadowOffset;
+
+ if (height > 0)
+ height += extraShadowOffset;
+ else if (height < 0)
+ height -= extraShadowOffset;
+ return CGSizeMake(width, height);
}
+#endif
void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
{
@@ -790,7 +709,7 @@ void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGColorRef color = CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
- CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
+ CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width, -height), blur, color);
CGColorRelease(color);
#endif
}
@@ -817,7 +736,7 @@ void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef shadowColor = CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
- CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
+ CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
CGColorRelease(shadowColor);
#endif
}
@@ -837,7 +756,7 @@ void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGColorRef color = CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
- CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
+ CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width, -height), blur, color);
CGColorRelease(color);
#endif
}
@@ -857,7 +776,7 @@ void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef shadowColor = CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
- CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
+ CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
CGColorRelease(shadowColor);
#endif
}
@@ -877,7 +796,7 @@ void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
CGColorRef shadowColor = CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
- CGContextSetShadowWithColor(dc->platformContext(), CGSizeMake(width, height), blur, shadowColor);
+ CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
CGColorRelease(shadowColor);
#endif
}
@@ -909,7 +828,8 @@ void CanvasRenderingContext2D::applyShadow()
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef color = CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
- CGContextSetShadowWithColor(c->platformContext(), state().m_shadowOffset, state().m_shadowBlur, color);
+
+ CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(state().m_shadowOffset.width(), -state().m_shadowOffset.height()), state().m_shadowBlur, color);
CGColorRelease(color);
#endif
}
@@ -917,10 +837,18 @@ void CanvasRenderingContext2D::applyShadow()
static IntSize size(HTMLImageElement* image)
{
if (CachedImage* cachedImage = image->cachedImage())
- return cachedImage->imageSize();
+ return cachedImage->imageSize(1.0f); // FIXME: Not sure about this.
return IntSize();
}
+static inline FloatRect normalizeRect(const FloatRect& rect)
+{
+ return FloatRect(min(rect.x(), rect.right()),
+ min(rect.y(), rect.bottom()),
+ max(rect.width(), -rect.width()),
+ max(rect.height(), -rect.height()));
+}
+
void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
{
ASSERT(image);
@@ -937,6 +865,13 @@ void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
}
+void CanvasRenderingContext2D::checkOrigin(const KURL& url)
+{
+ RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
+ if (!m_canvas->document()->securityOrigin()->canAccess(origin.get()))
+ m_canvas->setOriginTainted();
+}
+
void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
ExceptionCode& ec)
{
@@ -945,13 +880,12 @@ void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRec
ec = 0;
FloatRect imageRect = FloatRect(FloatPoint(), size(image));
- if (!(imageRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0
- && dstRect.width() >= 0 && dstRect.height() >= 0)) {
+ if (!imageRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
ec = INDEX_SIZE_ERR;
return;
}
- if (srcRect.isEmpty() || dstRect.isEmpty())
+ if (!dstRect.width() || !dstRect.height())
return;
GraphicsContext* c = drawingContext();
@@ -962,6 +896,12 @@ void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRec
if (!cachedImage)
return;
+ if (m_canvas->originClean())
+ checkOrigin(cachedImage->response().url());
+
+ if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
+ m_canvas->setOriginTainted();
+
FloatRect sourceRect = c->roundToDevicePixels(srcRect);
FloatRect destRect = c->roundToDevicePixels(dstRect);
willDraw(destRect);
@@ -990,13 +930,12 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatR
ec = 0;
FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
- if (!(srcCanvasRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0
- && dstRect.width() >= 0 && dstRect.height() >= 0)) {
+ if (!srcCanvasRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
ec = INDEX_SIZE_ERR;
return;
}
- if (srcRect.isEmpty() || dstRect.isEmpty())
+ if (!dstRect.width() || !dstRect.height())
return;
GraphicsContext* c = drawingContext();
@@ -1007,68 +946,16 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatR
FloatRect destRect = c->roundToDevicePixels(dstRect);
// FIXME: Do this through platform-independent GraphicsContext API.
-#if PLATFORM(CG)
- CGImageRef platformImage = canvas->createPlatformImage();
- if (!platformImage)
+ ImageBuffer* buffer = canvas->buffer();
+ if (!buffer)
return;
- willDraw(destRect);
-
- float iw = CGImageGetWidth(platformImage);
- float ih = CGImageGetHeight(platformImage);
- if (sourceRect.x() == 0 && sourceRect.y() == 0 && iw == sourceRect.width() && ih == sourceRect.height()) {
- // Fast path, yay!
- CGContextDrawImage(c->platformContext(), destRect, platformImage);
- } else {
- // Slow path, boo!
- // Create a new bitmap of the appropriate size and then draw that into our context.
-
- size_t csw = static_cast<size_t>(ceilf(sourceRect.width()));
- size_t csh = static_cast<size_t>(ceilf(sourceRect.height()));
-
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- size_t bytesPerRow = csw * 4;
- void* buffer = fastMalloc(csh * bytesPerRow);
-
- CGContextRef clippedSourceContext = CGBitmapContextCreate(buffer, csw, csh,
- 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
- CGColorSpaceRelease(colorSpace);
- CGContextTranslateCTM(clippedSourceContext, -sourceRect.x(), -sourceRect.y());
- CGContextDrawImage(clippedSourceContext, CGRectMake(0, 0, iw, ih), platformImage);
-
- CGImageRef clippedSourceImage = CGBitmapContextCreateImage(clippedSourceContext);
- CGContextRelease(clippedSourceContext);
-
- CGContextDrawImage(c->platformContext(), destRect, clippedSourceImage);
- CGImageRelease(clippedSourceImage);
-
- fastFree(buffer);
- }
+ if (!canvas->originClean())
+ m_canvas->setOriginTainted();
- CGImageRelease(platformImage);
-#elif PLATFORM(QT)
- QImage px = canvas->createPlatformImage();
- if (px.isNull())
- return;
- willDraw(dstRect);
- QPainter* painter = static_cast<QPainter*>(c->platformContext());
- painter->drawImage(dstRect, px, srcRect);
-#elif PLATFORM(CAIRO)
- cairo_surface_t* image = canvas->createPlatformImage();
- if (!image)
- return;
- willDraw(dstRect);
- cairo_t* cr = c->platformContext();
- cairo_save(cr);
- cairo_set_source_surface(cr, image, srcRect.x(), srcRect.y());
- cairo_surface_destroy(image);
- cairo_rectangle(cr, dstRect.x(), dstRect.y(), dstRect.width(), dstRect.height());
- cairo_fill(cr);
- cairo_restore(cr);
-#elif defined(ANDROID_CANVAS_IMPL)
- willDraw(destRect);
- c->drawOffscreenContext(canvas->drawingContext(), &srcRect, dstRect);
-#endif
+ c->drawImage(buffer->image(), destRect, sourceRect, state().m_globalComposite);
+ 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.
+ // FIXME: Arguably willDraw should become didDraw and occur after drawing calls and not before them to avoid problems like this.
}
// FIXME: Why isn't this just another overload of drawImage? Why have a different name?
@@ -1084,6 +971,12 @@ void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
if (!cachedImage)
return;
+ if (m_canvas->originClean())
+ checkOrigin(cachedImage->response().url());
+
+ if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
+ m_canvas->setOriginTainted();
+
GraphicsContext* c = drawingContext();
if (!c)
return;
@@ -1109,12 +1002,12 @@ void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
{
- return new CanvasGradient(FloatPoint(x0, y0), FloatPoint(x1, y1));
+ return CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
}
PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
{
- return new CanvasGradient(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
+ return CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
}
PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
@@ -1125,7 +1018,20 @@ PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageEleme
CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
if (ec)
return 0;
- return new CanvasPattern(image ? image->cachedImage() : 0, repeatX, repeatY);
+
+ if (!image->complete()) {
+ ec = INVALID_STATE_ERR;
+ return 0;
+ }
+
+ CachedImage* cachedImage = image->cachedImage();
+ if (!cachedImage || !image->cachedImage()->image())
+ return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
+
+ KURL url(cachedImage->url());
+ RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
+ bool originClean = m_canvas->document()->securityOrigin()->canAccess(origin.get());
+ return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originClean);
}
PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
@@ -1136,31 +1042,11 @@ PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElem
CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
if (ec)
return 0;
- // FIXME: Do this through platform-independent GraphicsContext API.
-#if PLATFORM(CG)
- CGImageRef image = canvas->createPlatformImage();
- if (!image)
- return 0;
- PassRefPtr<CanvasPattern> pattern = new CanvasPattern(image, repeatX, repeatY);
- CGImageRelease(image);
- return pattern;
-#elif PLATFORM(CAIRO)
- cairo_surface_t* surface = canvas->createPlatformImage();
- if (!surface)
- return 0;
- PassRefPtr<CanvasPattern> pattern = new CanvasPattern(surface, repeatX, repeatY);
- cairo_surface_destroy(surface);
- return pattern;
-#else
- notImplemented();
- return 0;
-#endif
+ return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, canvas->originClean());
}
void CanvasRenderingContext2D::willDraw(const FloatRect& r)
{
- if (!m_canvas)
- return;
GraphicsContext* c = drawingContext();
if (!c)
return;
@@ -1170,111 +1056,297 @@ void CanvasRenderingContext2D::willDraw(const FloatRect& r)
GraphicsContext* CanvasRenderingContext2D::drawingContext() const
{
- if (!m_canvas)
- return 0;
return m_canvas->drawingContext();
}
-void CanvasRenderingContext2D::applyStrokePattern()
+static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
{
- GraphicsContext* c = drawingContext();
- if (!c)
- return;
+ PassRefPtr<ImageData> data = ImageData::create(size.width(), size.height());
+ memset(data->data()->data().data(), 0, data->data()->length());
+ return data;
+}
-#if PLATFORM(CG)
- // Check for case where the pattern is already set.
- CGAffineTransform m = CGContextGetCTM(c->platformContext());
- if (state().m_appliedStrokePattern
- && CGAffineTransformEqualToTransform(m, state().m_strokeStylePatternTransform))
- return;
+PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh) const
+{
+ FloatSize unscaledSize(sw, sh);
+ IntSize scaledSize = m_canvas->convertLogicalToDevice(unscaledSize);
+ if (scaledSize.width() < 1)
+ scaledSize.setWidth(1);
+ if (scaledSize.height() < 1)
+ scaledSize.setHeight(1);
+
+ return createEmptyImageData(scaledSize);
+}
- CanvasPattern* pattern = state().m_strokeStyle->pattern();
- if (!pattern)
- return;
+PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
+{
+ if (!m_canvas->originClean()) {
+ ec = SECURITY_ERR;
+ return 0;
+ }
+
+ FloatRect unscaledRect(sx, sy, sw, sh);
+ IntRect scaledRect = m_canvas->convertLogicalToDevice(unscaledRect);
+ if (scaledRect.width() < 1)
+ scaledRect.setWidth(1);
+ if (scaledRect.height() < 1)
+ scaledRect.setHeight(1);
+ ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0;
+ if (!buffer)
+ return createEmptyImageData(scaledRect.size());
+ return buffer->getImageData(scaledRect);
+}
- CGPatternRef platformPattern = pattern->createPattern(m);
- if (!platformPattern)
+void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
+{
+ if (!data) {
+ ec = TYPE_MISMATCH_ERR;
return;
+ }
+ putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
+}
- CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
- CGContextSetStrokeColorSpace(c->platformContext(), patternSpace);
- CGColorSpaceRelease(patternSpace);
-
- const CGFloat patternAlpha = 1;
- CGContextSetStrokePattern(c->platformContext(), platformPattern, &patternAlpha);
- CGPatternRelease(platformPattern);
+void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
+ float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
+{
+ if (!data) {
+ ec = TYPE_MISMATCH_ERR;
+ return;
+ }
+ if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) ||
+ !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
- state().m_strokeStylePatternTransform = m;
-#elif PLATFORM(QT)
- fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyStrokePattern\n");
-#elif PLATFORM(CAIRO)
- CanvasPattern* pattern = state().m_strokeStyle->pattern();
- if (!pattern)
+ ImageBuffer* buffer = m_canvas->buffer();
+ if (!buffer)
return;
- cairo_t* cr = c->platformContext();
- cairo_matrix_t m;
- cairo_get_matrix(cr, &m);
+ if (dirtyWidth < 0) {
+ dirtyX += dirtyWidth;
+ dirtyWidth = -dirtyWidth;
+ }
- cairo_pattern_t* platformPattern = pattern->createPattern(m);
- if (!platformPattern)
- return;
+ if (dirtyHeight < 0) {
+ dirtyY += dirtyHeight;
+ dirtyHeight = -dirtyHeight;
+ }
- cairo_set_source(cr, platformPattern);
- cairo_pattern_destroy(platformPattern);
-#endif
- state().m_appliedStrokePattern = true;
+ FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+ clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
+ IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
+ IntRect sourceRect = enclosingIntRect(clipRect);
+ sourceRect.move(destOffset);
+ sourceRect.intersect(IntRect(IntPoint(), buffer->size()));
+ if (sourceRect.isEmpty())
+ return;
+ willDraw(sourceRect);
+ sourceRect.move(-destOffset);
+ IntPoint destPoint(destOffset.width(), destOffset.height());
+
+ buffer->putImageData(data, sourceRect, destPoint);
}
-void CanvasRenderingContext2D::applyFillPattern()
+String CanvasRenderingContext2D::font() const
{
+ return state().m_unparsedFont;
+}
+
+void CanvasRenderingContext2D::setFont(const String& newFont)
+{
+ RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create();
+ CSSParser parser(!m_canvas->document()->inCompatMode()); // Use the parse mode of the canvas' document when parsing CSS.
+
+ String declarationText("font: ");
+ declarationText += newFont;
+ parser.parseDeclaration(tempDecl.get(), declarationText);
+ if (!tempDecl->length())
+ return;
+
+ // The parse succeeded.
+ state().m_unparsedFont = newFont;
+
+ // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
+ // relative to the canvas.
+ RenderArena* arena = m_canvas->document()->renderArena();
+ RenderStyle* newStyle = new (arena) RenderStyle();
+ newStyle->ref();
+ if (m_canvas->computedStyle())
+ newStyle->setFontDescription(m_canvas->computedStyle()->fontDescription());
+
+ // Now map the font property into the style.
+ CSSStyleSelector* styleSelector = m_canvas->document()->styleSelector();
+ styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCSSValue(CSSPropertyFont).get(), newStyle);
+
+ state().m_font = newStyle->font();
+ state().m_font.update(styleSelector->fontSelector());
+ state().m_realizedFont = true;
+
+ newStyle->deref(arena);
+
+ // Set the font in the graphics context.
GraphicsContext* c = drawingContext();
if (!c)
return;
+ c->setFont(state().m_font);
+}
+
+String CanvasRenderingContext2D::textAlign() const
+{
+ return textAlignName(state().m_textAlign);
+}
-#if PLATFORM(CG)
- // Check for case where the pattern is already set.
- CGAffineTransform m = CGContextGetCTM(c->platformContext());
- if (state().m_appliedFillPattern
- && CGAffineTransformEqualToTransform(m, state().m_fillStylePatternTransform))
+void CanvasRenderingContext2D::setTextAlign(const String& s)
+{
+ TextAlign align;
+ if (!parseTextAlign(s, align))
return;
+ state().m_textAlign = align;
+}
+
+String CanvasRenderingContext2D::textBaseline() const
+{
+ return textBaselineName(state().m_textBaseline);
+}
- CanvasPattern* pattern = state().m_fillStyle->pattern();
- if (!pattern)
+void CanvasRenderingContext2D::setTextBaseline(const String& s)
+{
+ TextBaseline baseline;
+ if (!parseTextBaseline(s, baseline))
return;
+ state().m_textBaseline = baseline;
+}
- CGPatternRef platformPattern = pattern->createPattern(m);
- if (!platformPattern)
- return;
+void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
+{
+ drawTextInternal(text, x, y, true);
+}
+
+void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
+{
+ drawTextInternal(text, x, y, true, maxWidth, true);
+}
+
+void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
+{
+ drawTextInternal(text, x, y, false);
+}
- CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
- CGContextSetFillColorSpace(c->platformContext(), patternSpace);
- CGColorSpaceRelease(patternSpace);
+void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
+{
+ drawTextInternal(text, x, y, false, maxWidth, true);
+}
- const CGFloat patternAlpha = 1;
- CGContextSetFillPattern(c->platformContext(), platformPattern, &patternAlpha);
- CGPatternRelease(platformPattern);
+PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
+{
+ RefPtr<TextMetrics> metrics = TextMetrics::create();
+ metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length())));
+ return metrics;
+}
- state().m_fillStylePatternTransform = m;
-#elif PLATFORM(QT)
- fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyFillPattern\n");
-#elif PLATFORM(CAIRO)
- CanvasPattern* pattern = state().m_fillStyle->pattern();
- if (!pattern)
+void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth, bool useMaxWidth)
+{
+ GraphicsContext* c = drawingContext();
+ if (!c)
return;
+
+ const Font& font = accessFont();
+
+ // FIXME: Handle maxWidth.
+ // FIXME: Need to turn off font smoothing.
+
+ bool rtl = m_canvas->computedStyle() ? m_canvas->computedStyle()->direction() == RTL : false;
+ bool override = m_canvas->computedStyle() ? m_canvas->computedStyle()->unicodeBidi() == Override : false;
+
+ unsigned length = text.length();
+ const UChar* string = text.characters();
+ TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false);
+
+ // Draw the item text at the correct point.
+ FloatPoint location(x, y);
+ switch (state().m_textBaseline) {
+ case TopTextBaseline:
+ case HangingTextBaseline:
+ location.setY(y + font.ascent());
+ break;
+ case BottomTextBaseline:
+ case IdeographicTextBaseline:
+ location.setY(y - font.descent());
+ break;
+ case MiddleTextBaseline:
+ location.setY(y - font.descent() + font.height() / 2);
+ break;
+ case AlphabeticTextBaseline:
+ default:
+ // Do nothing.
+ break;
+ }
+
+ float width = font.width(TextRun(text, false, 0, 0, rtl, override));
- cairo_t* cr = c->platformContext();
- cairo_matrix_t m;
- cairo_get_matrix(cr, &m);
+ TextAlign align = state().m_textAlign;
+ if (align == StartTextAlign)
+ align = rtl ? RightTextAlign : LeftTextAlign;
+ else if (align == EndTextAlign)
+ align = rtl ? LeftTextAlign : RightTextAlign;
+
+ switch (align) {
+ case CenterTextAlign:
+ location.setX(location.x() - width / 2);
+ break;
+ case RightTextAlign:
+ location.setX(location.x() - width);
+ break;
+ default:
+ break;
+ }
+
+ // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
+ FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y() - font.ascent() - font.lineGap(),
+ width + font.height(), font.lineSpacing());
+ if (!fill)
+ textRect.inflate(c->strokeThickness() / 2);
+
+ CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
+ if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
+ IntRect maskRect = enclosingIntRect(textRect);
+
+ auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), false);
+
+ GraphicsContext* maskImageContext = maskImage->context();
+
+ if (fill)
+ maskImageContext->setFillColor(Color::black);
+ else {
+ maskImageContext->setStrokeColor(Color::black);
+ maskImageContext->setStrokeThickness(c->strokeThickness());
+ }
+
+ maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke);
+ maskImageContext->translate(-maskRect.x(), -maskRect.y());
+
+ maskImageContext->setFont(font);
+ maskImageContext->drawBidiText(textRun, location);
+
+ c->save();
+ c->clipToImageBuffer(maskRect, maskImage.get());
+ drawStyle->applyFillColor(c);
+ c->fillRect(maskRect);
+ c->restore();
- cairo_pattern_t* platformPattern = pattern->createPattern(m);
- if (!platformPattern)
return;
+ }
- cairo_set_source(cr, platformPattern);
- cairo_pattern_destroy(platformPattern);
-#endif
- state().m_appliedFillPattern = true;
+ c->setTextDrawingMode(fill ? cTextFill : cTextStroke);
+ c->drawBidiText(textRun, location);
+}
+
+const Font& CanvasRenderingContext2D::accessFont()
+{
+ if (!state().m_realizedFont)
+ setFont(state().m_unparsedFont);
+ return state().m_font;
}
} // namespace WebCore