summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/cairo
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/cairo')
-rw-r--r--WebCore/platform/graphics/cairo/AffineTransformCairo.cpp281
-rw-r--r--WebCore/platform/graphics/cairo/CairoPath.h45
-rw-r--r--WebCore/platform/graphics/cairo/FontCairo.cpp105
-rw-r--r--WebCore/platform/graphics/cairo/GradientCairo.cpp74
-rw-r--r--WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp1104
-rw-r--r--WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h101
-rw-r--r--WebCore/platform/graphics/cairo/ImageBufferCairo.cpp224
-rw-r--r--WebCore/platform/graphics/cairo/ImageBufferData.h44
-rw-r--r--WebCore/platform/graphics/cairo/ImageCairo.cpp187
-rw-r--r--WebCore/platform/graphics/cairo/ImageSourceCairo.cpp229
-rw-r--r--WebCore/platform/graphics/cairo/PathCairo.cpp285
-rw-r--r--WebCore/platform/graphics/cairo/PatternCairo.cpp50
-rw-r--r--WebCore/platform/graphics/cairo/rgb24-hacks.txt32
-rw-r--r--WebCore/platform/graphics/cairo/scale-removal.txt13
14 files changed, 2774 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp b/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp
new file mode 100644
index 0000000..0f2fccd
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp
@@ -0,0 +1,281 @@
+/*
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AffineTransform.h"
+
+#include "IntRect.h"
+#include "FloatRect.h"
+
+#include <cairo.h>
+
+namespace WebCore {
+
+static const double deg2rad = 0.017453292519943295769; // pi/180
+
+AffineTransform::AffineTransform()
+{
+ cairo_matrix_init_identity(&m_transform);
+}
+
+AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty)
+{
+ cairo_matrix_init(&m_transform, a, b, c, d, tx, ty);
+}
+
+AffineTransform::AffineTransform(const PlatformAffineTransform& matrix)
+{
+ m_transform = matrix;
+}
+
+void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty)
+{
+ cairo_matrix_init(&m_transform, a, b, c, d, tx, ty);
+}
+
+void AffineTransform::map(double x, double y, double* x2, double* y2) const
+{
+ *x2 = x;
+ *y2 = y;
+ cairo_matrix_transform_point(&m_transform, x2, y2);
+}
+
+IntRect AffineTransform::mapRect(const IntRect &rect) const
+{
+ FloatRect floatRect(rect);
+ FloatRect enclosingFloatRect = this->mapRect(floatRect);
+
+ return enclosingIntRect(enclosingFloatRect);
+}
+
+FloatRect AffineTransform::mapRect(const FloatRect &rect) const
+{
+ double rectMinX = rect.x();
+ double rectMaxX = rect.x() + rect.width();
+ double rectMinY = rect.y();
+ double rectMaxY = rect.y() + rect.height();
+
+ double px = rectMinX;
+ double py = rectMinY;
+ cairo_matrix_transform_point(&m_transform, &px, &py);
+
+ double enclosingRectMinX = px;
+ double enclosingRectMinY = py;
+ double enclosingRectMaxX = px;
+ double enclosingRectMaxY = py;
+
+ px = rectMaxX;
+ py = rectMinY;
+ cairo_matrix_transform_point(&m_transform, &px, &py);
+ if (px < enclosingRectMinX)
+ enclosingRectMinX = px;
+ else if (px > enclosingRectMaxX)
+ enclosingRectMaxX = px;
+ if (py < enclosingRectMinY)
+ enclosingRectMinY = py;
+ else if (py > enclosingRectMaxY)
+ enclosingRectMaxY = py;
+
+ px = rectMaxX;
+ py = rectMaxY;
+ cairo_matrix_transform_point(&m_transform, &px, &py);
+ if (px < enclosingRectMinX)
+ enclosingRectMinX = px;
+ else if (px > enclosingRectMaxX)
+ enclosingRectMaxX = px;
+ if (py < enclosingRectMinY)
+ enclosingRectMinY = py;
+ else if (py > enclosingRectMaxY)
+ enclosingRectMaxY = py;
+
+ px = rectMinX;
+ py = rectMaxY;
+ cairo_matrix_transform_point(&m_transform, &px, &py);
+ if (px < enclosingRectMinX)
+ enclosingRectMinX = px;
+ else if (px > enclosingRectMaxX)
+ enclosingRectMaxX = px;
+ if (py < enclosingRectMinY)
+ enclosingRectMinY = py;
+ else if (py > enclosingRectMaxY)
+ enclosingRectMaxY = py;
+
+
+ double enclosingRectWidth = enclosingRectMaxX - enclosingRectMinX;
+ double enclosingRectHeight = enclosingRectMaxY - enclosingRectMinY;
+
+ return FloatRect(enclosingRectMinX, enclosingRectMinY, enclosingRectWidth, enclosingRectHeight);
+}
+
+bool AffineTransform::isIdentity() const
+{
+ return ((m_transform.xx == 1) && (m_transform.yy == 1)
+ && (m_transform.xy == 0) && (m_transform.yx == 0)
+ && (m_transform.x0 == 0) && (m_transform.y0 == 0));
+}
+
+double AffineTransform::a() const
+{
+ return m_transform.xx;
+}
+
+void AffineTransform::setA(double a)
+{
+ m_transform.xx = a;
+}
+
+double AffineTransform::b() const
+{
+ return m_transform.yx;
+}
+
+void AffineTransform::setB(double b)
+{
+ m_transform.yx = b;
+}
+
+double AffineTransform::c() const
+{
+ return m_transform.xy;
+}
+
+void AffineTransform::setC(double c)
+{
+ m_transform.xy = c;
+}
+
+double AffineTransform::d() const
+{
+ return m_transform.yy;
+}
+
+void AffineTransform::setD(double d)
+{
+ m_transform.yy = d;
+}
+
+double AffineTransform::e() const
+{
+ return m_transform.x0;
+}
+
+void AffineTransform::setE(double e)
+{
+ m_transform.x0 = e;
+}
+
+double AffineTransform::f() const
+{
+ return m_transform.y0;
+}
+
+void AffineTransform::setF(double f)
+{
+ m_transform.y0 = f;
+}
+
+void AffineTransform::reset()
+{
+ cairo_matrix_init_identity(&m_transform);
+}
+
+AffineTransform &AffineTransform::scale(double sx, double sy)
+{
+ cairo_matrix_scale(&m_transform, sx, sy);
+ return *this;
+}
+
+AffineTransform &AffineTransform::rotate(double d)
+{
+ cairo_matrix_rotate(&m_transform, d * deg2rad);
+ return *this;
+}
+
+AffineTransform &AffineTransform::translate(double tx, double ty)
+{
+ cairo_matrix_translate(&m_transform, tx, ty);
+ return *this;
+}
+
+AffineTransform &AffineTransform::shear(double sx, double sy)
+{
+ cairo_matrix_t shear;
+ cairo_matrix_init(&shear, 1, sy, sx, 1, 0, 0);
+
+ cairo_matrix_t result;
+ cairo_matrix_multiply(&result, &shear, &m_transform);
+ m_transform = result;
+
+ return *this;
+}
+
+double AffineTransform::det() const
+{
+ return m_transform.xx * m_transform.yy - m_transform.xy * m_transform.yx;
+}
+
+AffineTransform AffineTransform::inverse() const
+{
+ if (!isInvertible()) return AffineTransform();
+
+ cairo_matrix_t result = m_transform;
+ cairo_matrix_invert(&result);
+ return AffineTransform(result);
+}
+
+AffineTransform::operator cairo_matrix_t() const
+{
+ return m_transform;
+}
+
+bool AffineTransform::operator== (const AffineTransform &m2) const
+{
+ return ((m_transform.xx == m2.m_transform.xx)
+ && (m_transform.yy == m2.m_transform.yy)
+ && (m_transform.xy == m2.m_transform.xy)
+ && (m_transform.yx == m2.m_transform.yx)
+ && (m_transform.x0 == m2.m_transform.x0)
+ && (m_transform.y0 == m2.m_transform.y0));
+
+}
+
+AffineTransform &AffineTransform::operator*= (const AffineTransform &m2)
+{
+ cairo_matrix_t result;
+ cairo_matrix_multiply(&result, &m_transform, &m2.m_transform);
+ m_transform = result;
+
+ return *this;
+}
+
+AffineTransform AffineTransform::operator* (const AffineTransform &m2)
+{
+ cairo_matrix_t result;
+ cairo_matrix_multiply(&result, &m_transform, &m2.m_transform);
+ return result;
+}
+
+}
+
+// vim: ts=4 sw=4 et
diff --git a/WebCore/platform/graphics/cairo/CairoPath.h b/WebCore/platform/graphics/cairo/CairoPath.h
new file mode 100644
index 0000000..b761ce6
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/CairoPath.h
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef CairoPath_h
+#define CairoPath_h
+
+#include <cairo.h>
+
+namespace WebCore {
+
+ // This is necessary since cairo_path_fixed_t isn't exposed in Cairo's public API.
+ struct CairoPath {
+ cairo_t* m_cr;
+
+ CairoPath()
+ {
+ static cairo_surface_t* pathSurface = cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1);
+ m_cr = cairo_create(pathSurface);
+ }
+
+ ~CairoPath()
+ {
+ cairo_destroy(m_cr);
+ }
+ };
+
+} // namespace WebCore
+
+#endif // CairoPath_h
diff --git a/WebCore/platform/graphics/cairo/FontCairo.cpp b/WebCore/platform/graphics/cairo/FontCairo.cpp
new file mode 100644
index 0000000..9da9426
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/FontCairo.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
+ * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Font.h"
+
+#include "GlyphBuffer.h"
+#include "GraphicsContext.h"
+#include "SimpleFontData.h"
+
+namespace WebCore {
+
+void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
+ int from, int numGlyphs, const FloatPoint& point) const
+{
+ cairo_t* cr = context->platformContext();
+ cairo_save(cr);
+
+ font->setFont(cr);
+
+ GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from);
+
+ float offset = point.x();
+ for (int i = 0; i < numGlyphs; i++) {
+ glyphs[i].x = offset;
+ glyphs[i].y = point.y();
+ offset += glyphBuffer.advanceAt(from + i);
+ }
+
+ Color fillColor = context->fillColor();
+
+ // Text shadow, inspired by FontMac
+ IntSize shadowSize;
+ int shadowBlur = 0;
+ Color shadowColor;
+ bool hasShadow = context->textDrawingMode() == cTextFill &&
+ context->getShadow(shadowSize, shadowBlur, shadowColor);
+
+ // TODO: Blur support
+ if (hasShadow) {
+ // Disable graphics context shadows (not yet implemented) and paint them manually
+ context->clearShadow();
+ Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
+ cairo_save(cr);
+
+ float red, green, blue, alpha;
+ shadowFillColor.getRGBA(red, green, blue, alpha);
+ cairo_set_source_rgba(cr, red, green, blue, alpha);
+
+ cairo_translate(cr, shadowSize.width(), shadowSize.height());
+ cairo_show_glyphs(cr, glyphs, numGlyphs);
+
+ cairo_restore(cr);
+ }
+
+ if (context->textDrawingMode() & cTextFill) {
+ float red, green, blue, alpha;
+ fillColor.getRGBA(red, green, blue, alpha);
+ cairo_set_source_rgba(cr, red, green, blue, alpha);
+
+ cairo_show_glyphs(cr, glyphs, numGlyphs);
+ }
+
+ if (context->textDrawingMode() & cTextStroke) {
+ Color strokeColor = context->strokeColor();
+ float red, green, blue, alpha;
+ strokeColor.getRGBA(red, green, blue, alpha);
+ cairo_set_source_rgba(cr, red, green, blue, alpha);
+ cairo_glyph_path(cr, glyphs, numGlyphs);
+ cairo_set_line_width(cr, context->strokeThickness());
+ cairo_stroke(cr);
+ }
+
+ // Re-enable the platform shadow we disabled earlier
+ if (hasShadow)
+ context->setShadow(shadowSize, shadowBlur, shadowColor);
+
+ cairo_restore(cr);
+}
+
+}
diff --git a/WebCore/platform/graphics/cairo/GradientCairo.cpp b/WebCore/platform/graphics/cairo/GradientCairo.cpp
new file mode 100644
index 0000000..7776424
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/GradientCairo.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Gradient.h"
+
+#include "CSSParser.h"
+#include "GraphicsContext.h"
+#include <cairo.h>
+
+namespace WebCore {
+
+void Gradient::platformDestroy()
+{
+ if (m_gradient) {
+ cairo_pattern_destroy(m_gradient);
+ m_gradient = 0;
+ }
+}
+
+cairo_pattern_t* Gradient::platformGradient()
+{
+ if (m_gradient)
+ return m_gradient;
+
+ if (m_radial)
+ m_gradient = cairo_pattern_create_radial(m_p0.x(), m_p0.y(), m_r0, m_p1.x(), m_p1.y(), m_r1);
+ else
+ m_gradient = cairo_pattern_create_linear(m_p0.x(), m_p0.y(), m_p1.x(), m_p1.y());
+
+ Vector<ColorStop>::iterator stopIterator = m_stops.begin();
+ while (stopIterator != m_stops.end()) {
+ cairo_pattern_add_color_stop_rgba(m_gradient, stopIterator->stop, stopIterator->red, stopIterator->green, stopIterator->blue, stopIterator->alpha);
+ ++stopIterator;
+ }
+
+ return m_gradient;
+}
+
+void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
+{
+ cairo_t* cr = context->platformContext();
+
+ cairo_save(cr);
+ cairo_set_source(cr, platformGradient());
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+ cairo_fill(cr);
+ cairo_restore(cr);
+}
+
+} //namespace
diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
new file mode 100644
index 0000000..c403f44
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
@@ -0,0 +1,1104 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de>
+ * Copyright (C) 2008 Nuanti Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "GraphicsContext.h"
+
+#if PLATFORM(CAIRO)
+
+#include "AffineTransform.h"
+#include "CairoPath.h"
+#include "FloatRect.h"
+#include "Font.h"
+#include "ImageBuffer.h"
+#include "IntRect.h"
+#include "NotImplemented.h"
+#include "Path.h"
+#include "Pattern.h"
+#include "SimpleFontData.h"
+
+#include <cairo.h>
+#include <math.h>
+#include <stdio.h>
+#include <wtf/MathExtras.h>
+
+#if PLATFORM(GTK)
+#include <gdk/gdk.h>
+#include <pango/pango.h>
+#elif PLATFORM(WIN)
+#include <cairo-win32.h>
+#endif
+#include "GraphicsContextPrivate.h"
+#include "GraphicsContextPlatformPrivateCairo.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace WebCore {
+
+static inline void setColor(cairo_t* cr, const Color& col)
+{
+ float red, green, blue, alpha;
+ col.getRGBA(red, green, blue, alpha);
+ cairo_set_source_rgba(cr, red, green, blue, alpha);
+}
+
+// A fillRect helper
+static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const Color& col)
+{
+ setColor(cr, col);
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_fill(cr);
+}
+
+static inline cairo_pattern_t* applySpreadMethod(cairo_pattern_t* pattern, GradientSpreadMethod spreadMethod)
+{
+ switch (spreadMethod) {
+ case SpreadMethodPad:
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
+ break;
+ case SpreadMethodReflect:
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
+ break;
+ case SpreadMethodRepeat:
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ break;
+ default:
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
+ break;
+ }
+ return pattern;
+}
+
+GraphicsContext::GraphicsContext(PlatformGraphicsContext* cr)
+ : m_common(createGraphicsContextPrivate())
+ , m_data(new GraphicsContextPlatformPrivate)
+{
+ m_data->cr = cairo_reference(cr);
+ setPaintingDisabled(!cr);
+}
+
+GraphicsContext::~GraphicsContext()
+{
+ destroyGraphicsContextPrivate(m_common);
+ delete m_data;
+}
+
+AffineTransform GraphicsContext::getCTM() const
+{
+ cairo_t* cr = platformContext();
+ cairo_matrix_t m;
+ cairo_get_matrix(cr, &m);
+ return m;
+}
+
+cairo_t* GraphicsContext::platformContext() const
+{
+ return m_data->cr;
+}
+
+void GraphicsContext::savePlatformState()
+{
+ cairo_save(m_data->cr);
+ m_data->save();
+}
+
+void GraphicsContext::restorePlatformState()
+{
+ cairo_restore(m_data->cr);
+ m_data->restore();
+}
+
+// Draws a filled rectangle with a stroked border.
+void GraphicsContext::drawRect(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+
+ if (fillColor().alpha())
+ fillRectSourceOver(cr, rect, fillColor());
+
+ if (strokeStyle() != NoStroke) {
+ setColor(cr, strokeColor());
+ FloatRect r(rect);
+ r.inflate(-.5f);
+ cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
+ cairo_set_line_width(cr, 1.0);
+ cairo_stroke(cr);
+ }
+
+ cairo_restore(cr);
+}
+
+// FIXME: Now that this is refactored, it should be shared by all contexts.
+static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle style)
+{
+ // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
+ // works out. For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
+ // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
+ // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
+ if (style == DottedStroke || style == DashedStroke) {
+ if (p1.x() == p2.x()) {
+ p1.setY(p1.y() + strokeWidth);
+ p2.setY(p2.y() - strokeWidth);
+ }
+ else {
+ p1.setX(p1.x() + strokeWidth);
+ p2.setX(p2.x() - strokeWidth);
+ }
+ }
+
+ if (static_cast<int>(strokeWidth) % 2) {
+ if (p1.x() == p2.x()) {
+ // We're a vertical line. Adjust our x.
+ p1.setX(p1.x() + 0.5);
+ p2.setX(p2.x() + 0.5);
+ }
+ else {
+ // We're a horizontal line. Adjust our y.
+ p1.setY(p1.y() + 0.5);
+ p2.setY(p2.y() + 0.5);
+ }
+ }
+}
+
+// This is only used to draw borders.
+void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
+{
+ if (paintingDisabled())
+ return;
+
+ StrokeStyle style = strokeStyle();
+ if (style == NoStroke)
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+
+ float width = strokeThickness();
+ if (width < 1)
+ width = 1;
+
+ FloatPoint p1 = point1;
+ FloatPoint p2 = point2;
+ bool isVerticalLine = (p1.x() == p2.x());
+
+ adjustLineToPixelBoundaries(p1, p2, width, style);
+ cairo_set_line_width(cr, width);
+
+ int patWidth = 0;
+ switch (style) {
+ case NoStroke:
+ case SolidStroke:
+ break;
+ case DottedStroke:
+ patWidth = static_cast<int>(width);
+ break;
+ case DashedStroke:
+ patWidth = 3*static_cast<int>(width);
+ break;
+ }
+
+ setColor(cr, strokeColor());
+
+ cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+
+ if (patWidth) {
+ // Do a rect fill of our endpoints. This ensures we always have the
+ // appearance of being a border. We then draw the actual dotted/dashed line.
+ if (isVerticalLine) {
+ fillRectSourceOver(cr, FloatRect(p1.x() - width/2, p1.y() - width, width, width), strokeColor());
+ fillRectSourceOver(cr, FloatRect(p2.x() - width/2, p2.y(), width, width), strokeColor());
+ } else {
+ fillRectSourceOver(cr, FloatRect(p1.x() - width, p1.y() - width/2, width, width), strokeColor());
+ fillRectSourceOver(cr, FloatRect(p2.x(), p2.y() - width/2, width, width), strokeColor());
+ }
+
+ // Example: 80 pixels with a width of 30 pixels.
+ // Remainder is 20. The maximum pixels of line we could paint
+ // will be 50 pixels.
+ int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*static_cast<int>(width);
+ int remainder = distance%patWidth;
+ int coverage = distance-remainder;
+ int numSegments = coverage/patWidth;
+
+ float patternOffset = 0;
+ // Special case 1px dotted borders for speed.
+ if (patWidth == 1)
+ patternOffset = 1.0;
+ else {
+ bool evenNumberOfSegments = numSegments%2 == 0;
+ if (remainder)
+ evenNumberOfSegments = !evenNumberOfSegments;
+ if (evenNumberOfSegments) {
+ if (remainder) {
+ patternOffset += patWidth - remainder;
+ patternOffset += remainder/2;
+ }
+ else
+ patternOffset = patWidth/2;
+ }
+ else if (!evenNumberOfSegments) {
+ if (remainder)
+ patternOffset = (patWidth - remainder)/2;
+ }
+ }
+
+ double dash = patWidth;
+ cairo_set_dash(cr, &dash, 1, patternOffset);
+ }
+
+ cairo_move_to(cr, p1.x(), p1.y());
+ cairo_line_to(cr, p2.x(), p2.y());
+
+ cairo_stroke(cr);
+ cairo_restore(cr);
+}
+
+// This method is only used to draw the little circles used in lists.
+void GraphicsContext::drawEllipse(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+ float yRadius = .5 * rect.height();
+ float xRadius = .5 * rect.width();
+ cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
+ cairo_scale(cr, xRadius, yRadius);
+ cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI);
+ cairo_restore(cr);
+
+ if (fillColor().alpha()) {
+ setColor(cr, fillColor());
+ cairo_fill_preserve(cr);
+ }
+
+ if (strokeStyle() != NoStroke) {
+ setColor(cr, strokeColor());
+ cairo_set_line_width(cr, strokeThickness());
+ cairo_stroke(cr);
+ }
+
+ cairo_new_path(cr);
+}
+
+void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
+{
+ if (paintingDisabled() || strokeStyle() == NoStroke)
+ return;
+
+ int x = rect.x();
+ int y = rect.y();
+ float w = rect.width();
+ float h = rect.height();
+ float scaleFactor = h / w;
+ float reverseScaleFactor = w / h;
+
+ float hRadius = w / 2;
+ float vRadius = h / 2;
+ float fa = startAngle;
+ float falen = fa + angleSpan;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+
+ if (w != h)
+ cairo_scale(cr, 1., scaleFactor);
+
+ cairo_arc_negative(cr, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, -fa * M_PI/180, -falen * M_PI/180);
+
+ if (w != h)
+ cairo_scale(cr, 1., reverseScaleFactor);
+
+ float width = strokeThickness();
+ int patWidth = 0;
+
+ switch (strokeStyle()) {
+ case DottedStroke:
+ patWidth = static_cast<int>(width / 2);
+ break;
+ case DashedStroke:
+ patWidth = 3 * static_cast<int>(width / 2);
+ break;
+ default:
+ break;
+ }
+
+ setColor(cr, strokeColor());
+
+ if (patWidth) {
+ // Example: 80 pixels with a width of 30 pixels.
+ // Remainder is 20. The maximum pixels of line we could paint
+ // will be 50 pixels.
+ int distance;
+ if (hRadius == vRadius)
+ distance = static_cast<int>((M_PI * hRadius) / 2.0);
+ else // We are elliptical and will have to estimate the distance
+ distance = static_cast<int>((M_PI * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0)) / 2.0);
+
+ int remainder = distance % patWidth;
+ int coverage = distance - remainder;
+ int numSegments = coverage / patWidth;
+
+ float patternOffset = 0.0;
+ // Special case 1px dotted borders for speed.
+ if (patWidth == 1)
+ patternOffset = 1.0;
+ else {
+ bool evenNumberOfSegments = numSegments % 2 == 0;
+ if (remainder)
+ evenNumberOfSegments = !evenNumberOfSegments;
+ if (evenNumberOfSegments) {
+ if (remainder) {
+ patternOffset += patWidth - remainder;
+ patternOffset += remainder / 2.0;
+ } else
+ patternOffset = patWidth / 2.0;
+ } else {
+ if (remainder)
+ patternOffset = (patWidth - remainder) / 2.0;
+ }
+ }
+
+ double dash = patWidth;
+ cairo_set_dash(cr, &dash, 1, patternOffset);
+ }
+
+ cairo_stroke(cr);
+ cairo_restore(cr);
+}
+
+void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
+{
+ if (paintingDisabled())
+ return;
+
+ if (npoints <= 1)
+ return;
+
+ cairo_t* cr = m_data->cr;
+
+ cairo_save(cr);
+ cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
+ cairo_move_to(cr, points[0].x(), points[0].y());
+ for (size_t i = 1; i < npoints; i++)
+ cairo_line_to(cr, points[i].x(), points[i].y());
+ cairo_close_path(cr);
+
+ if (fillColor().alpha()) {
+ setColor(cr, fillColor());
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+ cairo_fill_preserve(cr);
+ }
+
+ if (strokeStyle() != NoStroke) {
+ setColor(cr, strokeColor());
+ cairo_set_line_width(cr, strokeThickness());
+ cairo_stroke(cr);
+ }
+
+ cairo_new_path(cr);
+ cairo_restore(cr);
+}
+
+void GraphicsContext::fillPath()
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+
+ cairo_set_fill_rule(cr, fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
+ switch (m_common->state.fillColorSpace) {
+ case SolidColorSpace:
+ if (fillColor().alpha()) {
+ setColor(cr, fillColor());
+ cairo_clip(cr);
+ cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
+ }
+ break;
+ case PatternColorSpace:
+ cairo_set_source(cr, m_common->state.fillPattern.get()->createPlatformPattern(getCTM()));
+ cairo_clip(cr);
+ cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
+ break;
+ case GradientColorSpace:
+ cairo_pattern_t* pattern = m_common->state.fillGradient.get()->platformGradient();
+ pattern = applySpreadMethod(pattern, spreadMethod());
+ cairo_set_source(cr, pattern);
+ cairo_clip(cr);
+ cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
+ break;
+ }
+ cairo_restore(cr);
+}
+
+void GraphicsContext::strokePath()
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+ switch (m_common->state.strokeColorSpace) {
+ case SolidColorSpace:
+ if (strokeColor().alpha()) {
+ setColor(cr, strokeColor());
+ if (m_common->state.globalAlpha < 1.0f) {
+ cairo_push_group(cr);
+ cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
+ cairo_pop_group_to_source(cr);
+ }
+ cairo_stroke(cr);
+ }
+ break;
+ case PatternColorSpace:
+ cairo_set_source(cr, m_common->state.strokePattern.get()->createPlatformPattern(getCTM()));
+ if (m_common->state.globalAlpha < 1.0f) {
+ cairo_push_group(cr);
+ cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
+ cairo_pop_group_to_source(cr);
+ }
+ cairo_stroke(cr);
+ break;
+ case GradientColorSpace:
+ cairo_pattern_t* pattern = m_common->state.strokeGradient.get()->platformGradient();
+ pattern = applySpreadMethod(pattern, spreadMethod());
+ cairo_set_source(cr, pattern);
+ if (m_common->state.globalAlpha < 1.0f) {
+ cairo_push_group(cr);
+ cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
+ cairo_pop_group_to_source(cr);
+ }
+ cairo_stroke(cr);
+ break;
+ }
+ cairo_restore(cr);
+}
+
+void GraphicsContext::drawPath()
+{
+ fillPath();
+ strokePath();
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+ fillPath();
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ if (color.alpha())
+ fillRectSourceOver(m_data->cr, rect, color);
+}
+
+void GraphicsContext::clip(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+ cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+ cairo_clip(cr);
+ cairo_set_fill_rule(cr, savedFillRule);
+ m_data->clip(rect);
+}
+
+void GraphicsContext::drawFocusRing(const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ const Vector<IntRect>& rects = focusRingRects();
+ unsigned rectCount = rects.size();
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+ cairo_push_group(cr);
+ cairo_new_path(cr);
+
+#if PLATFORM(GTK)
+ GdkRegion* reg = gdk_region_new();
+ for (unsigned i = 0; i < rectCount; i++) {
+ GdkRectangle rect = rects[i];
+ gdk_region_union_with_rect(reg, &rect);
+ }
+ gdk_cairo_region(cr, reg);
+ gdk_region_destroy(reg);
+
+ setColor(cr, color);
+ cairo_set_line_width(cr, 2.0f);
+ setPlatformStrokeStyle(DottedStroke);
+#else
+ int radius = (focusRingWidth() - 1) / 2;
+ for (unsigned i = 0; i < rectCount; i++)
+ addPath(Path::createRoundedRectangle(rects[i], FloatSize(radius, radius)));
+
+ // Force the alpha to 50%. This matches what the Mac does with outline rings.
+ Color ringColor(color.red(), color.green(), color.blue(), 127);
+ setColor(cr, ringColor);
+ cairo_set_line_width(cr, focusRingWidth());
+ setPlatformStrokeStyle(SolidStroke);
+#endif
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_stroke_preserve(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+ cairo_fill(cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_paint(cr);
+ cairo_restore(cr);
+}
+
+void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
+{
+ if (paintingDisabled())
+ return;
+
+ // This is a workaround for http://bugs.webkit.org/show_bug.cgi?id=15659
+ StrokeStyle savedStrokeStyle = strokeStyle();
+ setStrokeStyle(SolidStroke);
+
+ IntPoint endPoint = origin + IntSize(width, 0);
+ drawLine(origin, endPoint);
+
+ setStrokeStyle(savedStrokeStyle);
+}
+
+void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, int width, bool grammar)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+
+ // Convention is green for grammar, red for spelling
+ // These need to become configurable
+ if (grammar)
+ cairo_set_source_rgb(cr, 0, 1, 0);
+ else
+ cairo_set_source_rgb(cr, 1, 0, 0);
+
+#if PLATFORM(GTK)
+ // We ignore most of the provided constants in favour of the platform style
+ pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
+#else
+ notImplemented();
+#endif
+
+ cairo_restore(cr);
+}
+
+FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
+{
+ FloatRect result;
+ double x = frect.x();
+ double y = frect.y();
+ cairo_t* cr = m_data->cr;
+ cairo_user_to_device(cr, &x, &y);
+ x = round(x);
+ y = round(y);
+ cairo_device_to_user(cr, &x, &y);
+ result.setX(static_cast<float>(x));
+ result.setY(static_cast<float>(y));
+ x = frect.width();
+ y = frect.height();
+ cairo_user_to_device_distance(cr, &x, &y);
+ x = round(x);
+ y = round(y);
+ cairo_device_to_user_distance(cr, &x, &y);
+ result.setWidth(static_cast<float>(x));
+ result.setHeight(static_cast<float>(y));
+ return result;
+}
+
+void GraphicsContext::translate(float x, float y)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_translate(cr, x, y);
+ m_data->translate(x, y);
+}
+
+IntPoint GraphicsContext::origin()
+{
+ cairo_matrix_t matrix;
+ cairo_t* cr = m_data->cr;
+ cairo_get_matrix(cr, &matrix);
+ return IntPoint(static_cast<int>(matrix.x0), static_cast<int>(matrix.y0));
+}
+
+void GraphicsContext::setPlatformFillColor(const Color& col)
+{
+ // Cairo contexts can't hold separate fill and stroke colors
+ // so we set them just before we actually fill or stroke
+}
+
+void GraphicsContext::setPlatformStrokeColor(const Color& col)
+{
+ // Cairo contexts can't hold separate fill and stroke colors
+ // so we set them just before we actually fill or stroke
+}
+
+void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_set_line_width(m_data->cr, strokeThickness);
+}
+
+void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
+{
+ static double dashPattern[] = {5.0, 5.0};
+ static double dotPattern[] = {1.0, 1.0};
+
+ if (paintingDisabled())
+ return;
+
+ switch (strokeStyle) {
+ case NoStroke:
+ // FIXME: is it the right way to emulate NoStroke?
+ cairo_set_line_width(m_data->cr, 0);
+ break;
+ case SolidStroke:
+ cairo_set_dash(m_data->cr, 0, 0, 0);
+ break;
+ case DottedStroke:
+ cairo_set_dash(m_data->cr, dotPattern, 2, 0);
+ break;
+ case DashedStroke:
+ cairo_set_dash(m_data->cr, dashPattern, 2, 0);
+ break;
+ }
+}
+
+void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
+{
+ notImplemented();
+}
+
+void GraphicsContext::concatCTM(const AffineTransform& transform)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform);
+ cairo_transform(cr, matrix);
+ m_data->concatCTM(transform);
+}
+
+void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
+{
+ if (paintingDisabled())
+ return;
+
+ clip(rect);
+
+ Path p;
+ FloatRect r(rect);
+ // Add outer ellipse
+ p.addEllipse(r);
+ // Add inner ellipse
+ r.inflate(-thickness);
+ p.addEllipse(r);
+ addPath(p);
+
+ cairo_t* cr = m_data->cr;
+ cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+ cairo_clip(cr);
+ cairo_set_fill_rule(cr, savedFillRule);
+}
+
+void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer* imageBuffer)
+{
+ if (paintingDisabled())
+ return;
+
+ notImplemented();
+}
+
+void GraphicsContext::setPlatformShadow(IntSize const&, int, Color const&)
+{
+ notImplemented();
+}
+
+void GraphicsContext::clearPlatformShadow()
+{
+ notImplemented();
+}
+
+void GraphicsContext::beginTransparencyLayer(float opacity)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_push_group(cr);
+ m_data->layers.append(opacity);
+ m_data->beginTransparencyLayer();
+}
+
+void GraphicsContext::endTransparencyLayer()
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint_with_alpha(cr, m_data->layers.last());
+ m_data->layers.removeLast();
+ m_data->endTransparencyLayer();
+}
+
+void GraphicsContext::clearRect(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+
+ cairo_save(cr);
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_fill(cr);
+ cairo_restore(cr);
+}
+
+void GraphicsContext::strokeRect(const FloatRect& rect, float width)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+ cairo_set_line_width(cr, width);
+ strokePath();
+ cairo_restore(cr);
+}
+
+void GraphicsContext::setLineCap(LineCap lineCap)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT;
+ switch (lineCap) {
+ case ButtCap:
+ // no-op
+ break;
+ case RoundCap:
+ cairoCap = CAIRO_LINE_CAP_ROUND;
+ break;
+ case SquareCap:
+ cairoCap = CAIRO_LINE_CAP_SQUARE;
+ break;
+ }
+ cairo_set_line_cap(m_data->cr, cairoCap);
+}
+
+void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
+{
+ cairo_set_dash(m_data->cr, dashes.data(), dashes.size(), dashOffset);
+}
+
+void GraphicsContext::setLineJoin(LineJoin lineJoin)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER;
+ switch (lineJoin) {
+ case MiterJoin:
+ // no-op
+ break;
+ case RoundJoin:
+ cairoJoin = CAIRO_LINE_JOIN_ROUND;
+ break;
+ case BevelJoin:
+ cairoJoin = CAIRO_LINE_JOIN_BEVEL;
+ break;
+ }
+ cairo_set_line_join(m_data->cr, cairoJoin);
+}
+
+void GraphicsContext::setMiterLimit(float miter)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_set_miter_limit(m_data->cr, miter);
+}
+
+void GraphicsContext::setAlpha(float alpha)
+{
+ m_common->state.globalAlpha = alpha;
+}
+
+float GraphicsContext::getAlpha()
+{
+ return m_common->state.globalAlpha;
+}
+
+static inline cairo_operator_t toCairoOperator(CompositeOperator op)
+{
+ switch (op) {
+ case CompositeClear:
+ return CAIRO_OPERATOR_CLEAR;
+ case CompositeCopy:
+ return CAIRO_OPERATOR_SOURCE;
+ case CompositeSourceOver:
+ return CAIRO_OPERATOR_OVER;
+ case CompositeSourceIn:
+ return CAIRO_OPERATOR_IN;
+ case CompositeSourceOut:
+ return CAIRO_OPERATOR_OUT;
+ case CompositeSourceAtop:
+ return CAIRO_OPERATOR_ATOP;
+ case CompositeDestinationOver:
+ return CAIRO_OPERATOR_DEST_OVER;
+ case CompositeDestinationIn:
+ return CAIRO_OPERATOR_DEST_IN;
+ case CompositeDestinationOut:
+ return CAIRO_OPERATOR_DEST_OUT;
+ case CompositeDestinationAtop:
+ return CAIRO_OPERATOR_DEST_ATOP;
+ case CompositeXOR:
+ return CAIRO_OPERATOR_XOR;
+ case CompositePlusDarker:
+ return CAIRO_OPERATOR_SATURATE;
+ case CompositeHighlight:
+ // There is no Cairo equivalent for CompositeHighlight.
+ return CAIRO_OPERATOR_OVER;
+ case CompositePlusLighter:
+ return CAIRO_OPERATOR_ADD;
+ default:
+ return CAIRO_OPERATOR_SOURCE;
+ }
+}
+
+void GraphicsContext::setCompositeOperation(CompositeOperator op)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_set_operator(m_data->cr, toCairoOperator(op));
+}
+
+void GraphicsContext::beginPath()
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_new_path(cr);
+}
+
+void GraphicsContext::addPath(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_path_t* p = cairo_copy_path(path.platformPath()->m_cr);
+ cairo_append_path(cr, p);
+ cairo_path_destroy(p);
+}
+
+void GraphicsContext::clip(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_path_t* p = cairo_copy_path(path.platformPath()->m_cr);
+ cairo_append_path(cr, p);
+ cairo_path_destroy(p);
+ cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+ cairo_clip(cr);
+ cairo_set_fill_rule(cr, savedFillRule);
+ m_data->clip(path);
+}
+
+void GraphicsContext::clipOut(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,4,0)
+ cairo_t* cr = m_data->cr;
+ double x1, y1, x2, y2;
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+ cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
+ addPath(path);
+
+ cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+ cairo_clip(cr);
+ cairo_set_fill_rule(cr, savedFillRule);
+#else
+ notImplemented();
+#endif
+}
+
+void GraphicsContext::rotate(float radians)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_rotate(m_data->cr, radians);
+ m_data->rotate(radians);
+}
+
+void GraphicsContext::scale(const FloatSize& size)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_scale(m_data->cr, size.width(), size.height());
+ m_data->scale(size);
+}
+
+void GraphicsContext::clipOut(const IntRect& r)
+{
+ if (paintingDisabled())
+ return;
+
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,4,0)
+ cairo_t* cr = m_data->cr;
+ double x1, y1, x2, y2;
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+ cairo_rectangle(cr, x1, x2, x2 - x1, y2 - y1);
+ cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
+ cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+ cairo_clip(cr);
+ cairo_set_fill_rule(cr, savedFillRule);
+#else
+ notImplemented();
+#endif
+}
+
+void GraphicsContext::clipOutEllipseInRect(const IntRect& r)
+{
+ if (paintingDisabled())
+ return;
+
+ Path p;
+ p.addEllipse(r);
+ clipOut(p);
+}
+
+void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+ cairo_save(cr);
+ beginPath();
+ addPath(Path::createRoundedRectangle(r, topLeft, topRight, bottomLeft, bottomRight));
+ setColor(cr, color);
+ cairo_fill(cr);
+ cairo_restore(cr);
+}
+
+#if PLATFORM(GTK)
+void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose)
+{
+ m_data->expose = expose;
+}
+
+GdkEventExpose* GraphicsContext::gdkExposeEvent() const
+{
+ return m_data->expose;
+}
+
+GdkDrawable* GraphicsContext::gdkDrawable() const
+{
+ if (!m_data->expose)
+ return 0;
+
+ return GDK_DRAWABLE(m_data->expose->window);
+}
+#endif
+
+void GraphicsContext::setUseAntialiasing(bool enable)
+{
+ if (paintingDisabled())
+ return;
+
+ // When true, use the default Cairo backend antialias mode (usually this
+ // enables standard 'grayscale' antialiasing); false to explicitly disable
+ // antialiasing. This is the same strategy as used in drawConvexPolygon().
+ cairo_set_antialias(m_data->cr, enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
+}
+
+void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
+{
+}
+
+InterpolationQuality GraphicsContext::imageInterpolationQuality() const
+{
+ return InterpolationDefault;
+}
+
+} // namespace WebCore
+
+#endif // PLATFORM(CAIRO)
diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h
new file mode 100644
index 0000000..9a14555
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Brent Fulgham <bfulgham@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "GraphicsContext.h"
+
+#include <cairo.h>
+#include <math.h>
+#include <stdio.h>
+#include <wtf/MathExtras.h>
+
+#if PLATFORM(GTK)
+#include <gdk/gdk.h>
+#include <pango/pango.h>
+#elif PLATFORM(WIN)
+#include <cairo-win32.h>
+#endif
+
+namespace WebCore {
+
+class GraphicsContextPlatformPrivate {
+public:
+ GraphicsContextPlatformPrivate()
+ : cr(0)
+#if PLATFORM(GTK)
+ , expose(0)
+#elif PLATFORM(WIN)
+ // NOTE: These may note be needed: review and remove once Cairo implementation is complete
+ , m_hdc(0)
+ , m_transparencyCount(0)
+#endif
+ {
+ }
+
+ ~GraphicsContextPlatformPrivate()
+ {
+ cairo_destroy(cr);
+ }
+
+#if PLATFORM(WIN)
+ // On Windows, we need to update the HDC for form controls to draw in the right place.
+ void save();
+ void restore();
+ void clip(const FloatRect&);
+ void clip(const Path&);
+ void scale(const FloatSize&);
+ void rotate(float);
+ void translate(float, float);
+ void concatCTM(const AffineTransform&);
+ void beginTransparencyLayer() { m_transparencyCount++; }
+ void endTransparencyLayer() { m_transparencyCount--; }
+#else
+ // On everything else, we do nothing.
+ void save() {}
+ void restore() {}
+ void clip(const FloatRect&) {}
+ void clip(const Path&) {}
+ void scale(const FloatSize&) {}
+ void rotate(float) {}
+ void translate(float, float) {}
+ void concatCTM(const AffineTransform&) {}
+ void beginTransparencyLayer() {}
+ void endTransparencyLayer() {}
+#endif
+
+ cairo_t* cr;
+ Vector<float> layers;
+
+#if PLATFORM(GTK)
+ GdkEventExpose* expose;
+#elif PLATFORM(WIN)
+ HDC m_hdc;
+ unsigned m_transparencyCount;
+#endif
+};
+
+} // namespace WebCore
+
diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp
new file mode 100644
index 0000000..5f65ed2
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2007 Holger Hans Peter Freyther <zecke@selfish.org>
+ * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ImageBuffer.h"
+
+#include "Base64.h"
+#include "BitmapImage.h"
+#include "GraphicsContext.h"
+#include "ImageData.h"
+#include "MIMETypeRegistry.h"
+#include "NotImplemented.h"
+#include "Pattern.h"
+#include "PlatformString.h"
+
+#include <cairo.h>
+#include <wtf/Vector.h>
+
+using namespace std;
+
+namespace WebCore {
+
+ImageBufferData::ImageBufferData(const IntSize& size)
+ : m_surface(0)
+{
+}
+
+ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success)
+ : m_data(size)
+ , m_size(size)
+{
+ success = false; // Make early return mean error.
+ m_data.m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ size.width(),
+ size.height());
+ if (cairo_surface_status(m_data.m_surface) != CAIRO_STATUS_SUCCESS)
+ return; // create will notice we didn't set m_initialized and fail.
+
+ cairo_t* cr = cairo_create(m_data.m_surface);
+ m_context.set(new GraphicsContext(cr));
+ cairo_destroy(cr); // The context is now owned by the GraphicsContext.
+ success = true;
+}
+
+ImageBuffer::~ImageBuffer()
+{
+ cairo_surface_destroy(m_data.m_surface);
+}
+
+GraphicsContext* ImageBuffer::context() const
+{
+ return m_context.get();
+}
+
+Image* ImageBuffer::image() const
+{
+ if (!m_image) {
+ // It's assumed that if image() is called, the actual rendering to the
+ // GraphicsContext must be done.
+ ASSERT(context());
+ // BitmapImage will release the passed in surface on destruction
+ m_image = BitmapImage::create(cairo_surface_reference(m_data.m_surface));
+ }
+ return m_image.get();
+}
+
+PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const
+{
+ ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+ PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
+ unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface);
+ unsigned char* dataDst = result->data()->data().data();
+
+ if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height())
+ memset(dataSrc, 0, result->data()->length());
+
+ int originx = rect.x();
+ int destx = 0;
+ if (originx < 0) {
+ destx = -originx;
+ originx = 0;
+ }
+ int endx = rect.x() + rect.width();
+ if (endx > m_size.width())
+ endx = m_size.width();
+ int numColumns = endx - originx;
+
+ int originy = rect.y();
+ int desty = 0;
+ if (originy < 0) {
+ desty = -originy;
+ originy = 0;
+ }
+ int endy = rect.y() + rect.height();
+ if (endy > m_size.height())
+ endy = m_size.height();
+ int numRows = endy - originy;
+
+ int stride = cairo_image_surface_get_stride(m_data.m_surface);
+ unsigned destBytesPerRow = 4 * rect.width();
+
+ unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4;
+ for (int y = 0; y < numRows; ++y) {
+ unsigned char *row = dataSrc + stride * (y + originy);
+ for (int x = 0; x < numColumns; x++) {
+ uint32_t *pixel = (uint32_t *) row + x + originx;
+ int basex = x * 4;
+ if (unsigned int alpha = (*pixel & 0xff000000) >> 24) {
+ destRows[basex] = (*pixel & 0x00ff0000) >> 16;
+ destRows[basex + 1] = (*pixel & 0x0000ff00) >> 8;
+ destRows[basex + 2] = (*pixel & 0x000000ff);
+ destRows[basex + 3] = alpha;
+ } else
+ reinterpret_cast<uint32_t*>(destRows + basex)[0] = pixel[0];
+ }
+ destRows += destBytesPerRow;
+ }
+
+ return result;
+}
+
+void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
+{
+ ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+ unsigned char* dataDst = cairo_image_surface_get_data(m_data.m_surface);
+
+ ASSERT(sourceRect.width() > 0);
+ ASSERT(sourceRect.height() > 0);
+
+ int originx = sourceRect.x();
+ int destx = destPoint.x() + sourceRect.x();
+ ASSERT(destx >= 0);
+ ASSERT(destx < m_size.width());
+ ASSERT(originx >= 0);
+ ASSERT(originx <= sourceRect.right());
+
+ int endx = destPoint.x() + sourceRect.right();
+ ASSERT(endx <= m_size.width());
+
+ int numColumns = endx - destx;
+
+ int originy = sourceRect.y();
+ int desty = destPoint.y() + sourceRect.y();
+ ASSERT(desty >= 0);
+ ASSERT(desty < m_size.height());
+ ASSERT(originy >= 0);
+ ASSERT(originy <= sourceRect.bottom());
+
+ int endy = destPoint.y() + sourceRect.bottom();
+ ASSERT(endy <= m_size.height());
+ int numRows = endy - desty;
+
+ unsigned srcBytesPerRow = 4 * source->width();
+ int stride = cairo_image_surface_get_stride(m_data.m_surface);
+
+ unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4;
+ for (int y = 0; y < numRows; ++y) {
+ unsigned char *row = dataDst + stride * (y + desty);
+ for (int x = 0; x < numColumns; x++) {
+ uint32_t *pixel = (uint32_t *) row + x + destx;
+ int basex = x * 4;
+ if (unsigned int alpha = srcRows[basex + 3]) {
+ *pixel = alpha << 24 | srcRows[basex] << 16 | srcRows[basex + 1] << 8 | srcRows[basex + 2];
+ } else
+ pixel[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
+ }
+ srcRows += srcBytesPerRow;
+ }
+}
+
+static cairo_status_t writeFunction(void* closure, const unsigned char* data, unsigned int length)
+{
+ Vector<char>* in = reinterpret_cast<Vector<char>*>(closure);
+ in->append(data, length);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+String ImageBuffer::toDataURL(const String& mimeType) const
+{
+ cairo_surface_t* image = cairo_get_target(context()->platformContext());
+ if (!image)
+ return "data:,";
+
+ String actualMimeType("image/png");
+ if (MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType))
+ actualMimeType = mimeType;
+
+ Vector<char> in;
+ // Only PNG output is supported for now.
+ cairo_surface_write_to_png_stream(image, writeFunction, &in);
+
+ Vector<char> out;
+ base64Encode(in, out);
+
+ return "data:" + actualMimeType + ";base64," + String(out.data(), out.size());
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/cairo/ImageBufferData.h b/WebCore/platform/graphics/cairo/ImageBufferData.h
new file mode 100644
index 0000000..49f15df
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/ImageBufferData.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ImageBufferData_h
+#define ImageBufferData_h
+
+#include "cairo.h"
+
+namespace WebCore {
+
+class IntSize;
+
+class ImageBufferData {
+public:
+ ImageBufferData(const IntSize&);
+
+ cairo_surface_t* m_surface;
+};
+
+} // namespace WebCore
+
+#endif // ImageBufferData_h
diff --git a/WebCore/platform/graphics/cairo/ImageCairo.cpp b/WebCore/platform/graphics/cairo/ImageCairo.cpp
new file mode 100644
index 0000000..0a35cf2
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/ImageCairo.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "BitmapImage.h"
+
+#if PLATFORM(CAIRO)
+
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "ImageObserver.h"
+#include <cairo.h>
+#include <math.h>
+
+namespace WebCore {
+
+void FrameData::clear()
+{
+ if (m_frame) {
+ cairo_surface_destroy(m_frame);
+ m_frame = 0;
+ // NOTE: We purposefully don't reset metadata here, so that even if we
+ // throw away previously-decoded data, animation loops can still access
+ // properties like frame durations without re-decoding.
+ }
+}
+
+BitmapImage::BitmapImage(cairo_surface_t* surface, ImageObserver* observer)
+ : Image(observer)
+ , m_currentFrame(0)
+ , m_frames(0)
+ , m_frameTimer(0)
+ , m_repetitionCount(cAnimationNone)
+ , m_repetitionCountStatus(Unknown)
+ , m_repetitionsComplete(0)
+ , m_isSolidColor(false)
+ , m_animationFinished(true)
+ , m_allDataReceived(true)
+ , m_haveSize(true)
+ , m_sizeAvailable(true)
+ , m_decodedSize(0)
+ , m_haveFrameCount(true)
+ , m_frameCount(1)
+{
+ initPlatformData();
+
+ // TODO: check to be sure this is an image surface
+
+ int width = cairo_image_surface_get_width(surface);
+ int height = cairo_image_surface_get_height(surface);
+ m_decodedSize = width * height * 4;
+ m_size = IntSize(width, height);
+
+ m_frames.grow(1);
+ m_frames[0].m_frame = surface;
+ m_frames[0].m_hasAlpha = cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR;
+ m_frames[0].m_haveMetadata = true;
+ checkForSolidColor();
+}
+
+void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, CompositeOperator op)
+{
+ startAnimation();
+
+ cairo_surface_t* image = frameAtIndex(m_currentFrame);
+ if (!image) // If it's too early we won't have an image yet.
+ return;
+
+ FloatRect srcRect(src);
+ FloatRect dstRect(dst);
+
+ if (mayFillWithSolidColor()) {
+ fillWithSolidColor(context, dstRect, solidColor(), op);
+ return;
+ }
+
+ IntSize selfSize = size();
+
+ cairo_t* cr = context->platformContext();
+ cairo_save(cr);
+
+ // Set the compositing operation.
+ if (op == CompositeSourceOver && !frameHasAlphaAtIndex(m_currentFrame))
+ context->setCompositeOperation(CompositeCopy);
+ else
+ context->setCompositeOperation(op);
+
+ // If we're drawing a sub portion of the image or scaling then create
+ // a pattern transformation on the image and draw the transformed pattern.
+ // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html
+ cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
+
+ // To avoid the unwanted gradient effect (#14017) we use
+ // CAIRO_FILTER_NEAREST now, but the real fix will be to have
+ // CAIRO_EXTEND_PAD implemented for surfaces in Cairo allowing us to still
+ // use bilinear filtering
+ cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
+
+ float scaleX = srcRect.width() / dstRect.width();
+ float scaleY = srcRect.height() / dstRect.height();
+ cairo_matrix_t matrix = { scaleX, 0, 0, scaleY, srcRect.x(), srcRect.y() };
+ cairo_pattern_set_matrix(pattern, &matrix);
+
+ // Draw the image.
+ cairo_translate(cr, dstRect.x(), dstRect.y());
+ cairo_set_source(cr, pattern);
+ cairo_pattern_destroy(pattern);
+ cairo_rectangle(cr, 0, 0, dstRect.width(), dstRect.height());
+ cairo_clip(cr);
+ cairo_paint_with_alpha(cr, context->getAlpha());
+
+ cairo_restore(cr);
+
+ if (imageObserver())
+ imageObserver()->didDraw(this);
+}
+
+void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const AffineTransform& patternTransform,
+ const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect)
+{
+ cairo_surface_t* image = nativeImageForCurrentFrame();
+ if (!image) // If it's too early we won't have an image yet.
+ return;
+
+ cairo_t* cr = context->platformContext();
+ context->save();
+
+ // TODO: Make use of tileRect.
+
+ cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+
+ // Workaround to avoid the unwanted gradient effect (#14017)
+ cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
+
+ cairo_matrix_t pattern_matrix = cairo_matrix_t(patternTransform);
+ cairo_matrix_t phase_matrix = {1, 0, 0, 1, phase.x(), phase.y()};
+ cairo_matrix_t combined;
+ cairo_matrix_multiply(&combined, &pattern_matrix, &phase_matrix);
+ cairo_matrix_invert(&combined);
+ cairo_pattern_set_matrix(pattern, &combined);
+
+ context->setCompositeOperation(op);
+ cairo_set_source(cr, pattern);
+ cairo_pattern_destroy(pattern);
+ cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height());
+ cairo_fill(cr);
+
+ context->restore();
+
+ if (imageObserver())
+ imageObserver()->didDraw(this);
+}
+
+void BitmapImage::checkForSolidColor()
+{
+ // FIXME: It's easy to implement this optimization. Just need to check the RGBA32 buffer to see if it is 1x1.
+ m_isSolidColor = false;
+}
+
+}
+
+#endif // PLATFORM(CAIRO)
diff --git a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp
new file mode 100644
index 0000000..b7a4cbb
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ImageSource.h"
+
+#if PLATFORM(CAIRO)
+
+#include "BMPImageDecoder.h"
+#include "GIFImageDecoder.h"
+#include "ICOImageDecoder.h"
+#include "JPEGImageDecoder.h"
+#include "PNGImageDecoder.h"
+#include "SharedBuffer.h"
+#include <cairo.h>
+
+#if !PLATFORM(WIN)
+#include "XBMImageDecoder.h"
+#endif
+
+namespace WebCore {
+
+ImageDecoder* createDecoder(const Vector<char>& data)
+{
+ // We need at least 4 bytes to figure out what kind of image we're dealing with.
+ int length = data.size();
+ if (length < 4)
+ return 0;
+
+ const unsigned char* uContents = (const unsigned char*)data.data();
+ const char* contents = data.data();
+
+ // GIFs begin with GIF8(7 or 9).
+ if (strncmp(contents, "GIF8", 4) == 0)
+ return new GIFImageDecoder();
+
+ // Test for PNG.
+ if (uContents[0]==0x89 &&
+ uContents[1]==0x50 &&
+ uContents[2]==0x4E &&
+ uContents[3]==0x47)
+ return new PNGImageDecoder();
+
+ // JPEG
+ if (uContents[0]==0xFF &&
+ uContents[1]==0xD8 &&
+ uContents[2]==0xFF)
+ return new JPEGImageDecoder();
+
+ // BMP
+ if (strncmp(contents, "BM", 2) == 0)
+ return new BMPImageDecoder();
+
+ // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
+ // CURs begin with 2-byte 0 followed by 2-byte 2.
+ if (!memcmp(contents, "\000\000\001\000", 4) ||
+ !memcmp(contents, "\000\000\002\000", 4))
+ return new ICOImageDecoder();
+
+#if !PLATFORM(WIN)
+ // XBMs require 8 bytes of info.
+ if (length >= 8 && strncmp(contents, "#define ", 8) == 0)
+ return new XBMImageDecoder();
+#endif
+
+ // Give up. We don't know what the heck this is.
+ return 0;
+}
+
+ImageSource::ImageSource()
+ : m_decoder(0)
+{
+}
+
+ImageSource::~ImageSource()
+{
+ clear();
+}
+
+void ImageSource::clear()
+{
+ delete m_decoder;
+ m_decoder = 0;
+}
+
+bool ImageSource::initialized() const
+{
+ return m_decoder;
+}
+
+void ImageSource::setData(SharedBuffer* data, bool allDataReceived)
+{
+ // Make the decoder by sniffing the bytes.
+ // This method will examine the data and instantiate an instance of the appropriate decoder plugin.
+ // If insufficient bytes are available to determine the image type, no decoder plugin will be
+ // made.
+ if (!m_decoder)
+ m_decoder = createDecoder(data->buffer());
+
+ if (!m_decoder)
+ return;
+
+ m_decoder->setData(data, allDataReceived);
+}
+
+bool ImageSource::isSizeAvailable()
+{
+ if (!m_decoder)
+ return false;
+
+ return m_decoder->isSizeAvailable();
+}
+
+IntSize ImageSource::size() const
+{
+ if (!m_decoder)
+ return IntSize();
+
+ return m_decoder->size();
+}
+
+IntSize ImageSource::frameSizeAtIndex(size_t) const
+{
+ return size();
+}
+
+int ImageSource::repetitionCount()
+{
+ if (!m_decoder)
+ return cAnimationNone;
+
+ return m_decoder->repetitionCount();
+}
+
+size_t ImageSource::frameCount() const
+{
+ return m_decoder ? m_decoder->frameCount() : 0;
+}
+
+NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
+{
+ if (!initialized())
+ return 0;
+
+ if (!m_decoder)
+ return 0;
+
+ RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
+ if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
+ return 0;
+
+ // Cairo does not like zero height images.
+ // If we have a zero height image, just pretend we don't have enough data yet.
+ if (!buffer->height())
+ return 0;
+
+ return cairo_image_surface_create_for_data((unsigned char*)buffer->bytes().data(),
+ CAIRO_FORMAT_ARGB32,
+ size().width(),
+ buffer->height(),
+ size().width()*4);
+}
+
+bool ImageSource::frameIsCompleteAtIndex(size_t index)
+{
+ if (!m_decoder)
+ return false;
+
+ RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
+ return buffer && buffer->status() == RGBA32Buffer::FrameComplete;
+}
+
+float ImageSource::frameDurationAtIndex(size_t index)
+{
+ if (!m_decoder)
+ return 0;
+
+ RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
+ if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
+ return 0;
+
+ // Many annoying ads specify a 0 duration to make an image flash as quickly
+ // as possible. We follow WinIE's behavior and use a duration of 100 ms
+ // for any frames that specify a duration of <= 50 ms. See
+ // <http://bugs.webkit.org/show_bug.cgi?id=14413> or Radar 4051389 for
+ // more.
+ const float duration = buffer->duration() / 1000.0f;
+ return (duration < 0.051f) ? 0.100f : duration;
+}
+
+bool ImageSource::frameHasAlphaAtIndex(size_t index)
+{
+ // When a frame has not finished decoding, always mark it as having alpha,
+ // so we don't get a black background for the undecoded sections.
+ // TODO: A better solution is probably to have the underlying buffer's
+ // hasAlpha() return true in these cases, since it is, in fact, technically
+ // true.
+ if (!frameIsCompleteAtIndex(index))
+ return true;
+
+ return m_decoder->frameBufferAtIndex(index)->hasAlpha();
+}
+
+}
+
+#endif // PLATFORM(CAIRO)
diff --git a/WebCore/platform/graphics/cairo/PathCairo.cpp b/WebCore/platform/graphics/cairo/PathCairo.cpp
new file mode 100644
index 0000000..3f8d588
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/PathCairo.cpp
@@ -0,0 +1,285 @@
+/*
+ Copyright (C) 2007 Krzysztof Kowalczyk <kkowalczyk@gmail.com>
+ Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <wildfox@kde.org>
+ 2004, 2005, 2006 Rob Buis <buis@kde.org>
+ 2005, 2007 Apple Inc. All Rights reserved.
+ 2007 Alp Toker <alp@atoker.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "Path.h"
+
+#include "AffineTransform.h"
+#include "CairoPath.h"
+#include "FloatRect.h"
+#include "NotImplemented.h"
+#include "PlatformString.h"
+
+#include <cairo.h>
+#include <math.h>
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+Path::Path()
+ : m_path(new CairoPath())
+{
+}
+
+Path::~Path()
+{
+ delete m_path;
+}
+
+Path::Path(const Path& other)
+ : m_path(new CairoPath())
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_path_t* p = cairo_copy_path(other.platformPath()->m_cr);
+ cairo_append_path(cr, p);
+ cairo_path_destroy(p);
+}
+
+Path& Path::operator=(const Path& other)
+{
+ if (&other == this)
+ return *this;
+
+ clear();
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_path_t* p = cairo_copy_path(other.platformPath()->m_cr);
+ cairo_append_path(cr, p);
+ cairo_path_destroy(p);
+ return *this;
+}
+
+void Path::clear()
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_new_path(cr);
+}
+
+bool Path::isEmpty() const
+{
+ cairo_t* cr = platformPath()->m_cr;
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,5,10)
+ return !cairo_has_current_point(cr);
+#else
+ cairo_path_t* p = cairo_copy_path(cr);
+ bool hasData = p->num_data;
+ cairo_path_destroy(p);
+ return !hasData;
+#endif
+}
+
+void Path::translate(const FloatSize& p)
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_translate(cr, p.width(), p.height());
+}
+
+void Path::moveTo(const FloatPoint& p)
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_move_to(cr, p.x(), p.y());
+}
+
+void Path::addLineTo(const FloatPoint& p)
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_line_to(cr, p.x(), p.y());
+}
+
+void Path::addRect(const FloatRect& rect)
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+/*
+ * inspired by libsvg-cairo
+ */
+void Path::addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& point)
+{
+ cairo_t* cr = platformPath()->m_cr;
+ double x, y;
+ double x1 = controlPoint.x();
+ double y1 = controlPoint.y();
+ double x2 = point.x();
+ double y2 = point.y();
+ cairo_get_current_point(cr, &x, &y);
+ cairo_curve_to(cr,
+ x + 2.0 / 3.0 * (x1 - x), y + 2.0 / 3.0 * (y1 - y),
+ x2 + 2.0 / 3.0 * (x1 - x2), y2 + 2.0 / 3.0 * (y1 - y2),
+ x2, y2);
+}
+
+void Path::addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& controlPoint3)
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_curve_to(cr, controlPoint1.x(), controlPoint1.y(),
+ controlPoint2.x(), controlPoint2.y(),
+ controlPoint3.x(), controlPoint3.y());
+}
+
+void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlockwise)
+{
+ // http://bugs.webkit.org/show_bug.cgi?id=16449
+ // cairo_arc() functions hang or crash when passed inf as radius or start/end angle
+ if (!isfinite(r) || !isfinite(sa) || !isfinite(ea))
+ return;
+
+ cairo_t* cr = platformPath()->m_cr;
+ if (anticlockwise)
+ cairo_arc_negative(cr, p.x(), p.y(), r, sa, ea);
+ else
+ cairo_arc(cr, p.x(), p.y(), r, sa, ea);
+}
+
+void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
+{
+ // FIXME: cairo_arc_to not yet in cairo see cairo.h
+ // cairo_arc_to(m_cr, p1.x(), p1.y(), p2.x(), p2.y());
+ notImplemented();
+}
+
+void Path::addEllipse(const FloatRect& rect)
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_save(cr);
+ float yRadius = .5 * rect.height();
+ float xRadius = .5 * rect.width();
+ cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
+ cairo_scale(cr, xRadius, yRadius);
+ cairo_arc(cr, 0., 0., 1., 0., 2 * piDouble);
+ cairo_restore(cr);
+}
+
+void Path::closeSubpath()
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_close_path(cr);
+}
+
+FloatRect Path::boundingRect() const
+{
+ cairo_t* cr = platformPath()->m_cr;
+ double x0, x1, y0, y1;
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 6, 0)
+ cairo_path_extents(cr, &x0, &y0, &x1, &y1);
+#else
+ cairo_stroke_extents(cr, &x0, &y0, &x1, &y1);
+#endif
+ return FloatRect(x0, y0, x1 - x0, y1 - y0);
+}
+
+bool Path::contains(const FloatPoint& point, WindRule rule) const
+{
+ if (!boundingRect().contains(point))
+ return false;
+
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_fill_rule_t cur = cairo_get_fill_rule(cr);
+ cairo_set_fill_rule(cr, rule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
+ bool contains = cairo_in_fill(cr, point.x(), point.y());
+ cairo_set_fill_rule(cr, cur);
+ return contains;
+}
+
+void Path::apply(void* info, PathApplierFunction function) const
+{
+ cairo_t* cr = platformPath()->m_cr;
+ cairo_path_t* path = cairo_copy_path(cr);
+ cairo_path_data_t* data;
+ PathElement pelement;
+ FloatPoint points[3];
+ pelement.points = points;
+
+ for (int i = 0; i < path->num_data; i += path->data[i].header.length) {
+ data = &path->data[i];
+ switch (data->header.type) {
+ case CAIRO_PATH_MOVE_TO:
+ pelement.type = PathElementMoveToPoint;
+ pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
+ function(info, &pelement);
+ break;
+ case CAIRO_PATH_LINE_TO:
+ pelement.type = PathElementAddLineToPoint;
+ pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
+ function(info, &pelement);
+ break;
+ case CAIRO_PATH_CURVE_TO:
+ pelement.type = PathElementAddCurveToPoint;
+ pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
+ pelement.points[1] = FloatPoint(data[2].point.x,data[2].point.y);
+ pelement.points[2] = FloatPoint(data[3].point.x,data[3].point.y);
+ function(info, &pelement);
+ break;
+ case CAIRO_PATH_CLOSE_PATH:
+ pelement.type = PathElementCloseSubpath;
+ function(info, &pelement);
+ break;
+ }
+ }
+ cairo_path_destroy(path);
+}
+
+void Path::transform(const AffineTransform& trans)
+{
+ cairo_t* m_cr = platformPath()->m_cr;
+ cairo_matrix_t c_matrix = cairo_matrix_t(trans);
+ cairo_matrix_invert(&c_matrix);
+ cairo_transform(m_cr, &c_matrix);
+}
+
+String Path::debugString() const
+{
+ String string = "";
+ cairo_path_t* path = cairo_copy_path(platformPath()->m_cr);
+ cairo_path_data_t* data;
+
+ if (!path->num_data )
+ string = "EMPTY";
+
+ for (int i = 0; i < path->num_data; i += path->data[i].header.length) {
+ data = &path->data[i];
+ switch (data->header.type) {
+ case CAIRO_PATH_MOVE_TO:
+ string += String::format("M %.2f,%.2f",
+ data[1].point.x, data[1].point.y);
+ break;
+ case CAIRO_PATH_LINE_TO:
+ string += String::format("L %.2f,%.2f",
+ data[1].point.x, data[1].point.y);
+ break;
+ case CAIRO_PATH_CURVE_TO:
+ string += String::format("C %.2f,%.2f,%.2f,%.2f,%.2f,%.2f",
+ data[1].point.x, data[1].point.y,
+ data[2].point.x, data[2].point.y,
+ data[3].point.x, data[3].point.y);
+ break;
+ case CAIRO_PATH_CLOSE_PATH:
+ string += "X";
+ break;
+ }
+ }
+ cairo_path_destroy(path);
+ return string;
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/cairo/PatternCairo.cpp b/WebCore/platform/graphics/cairo/PatternCairo.cpp
new file mode 100644
index 0000000..16cebf8
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/PatternCairo.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Pattern.h"
+
+#include "AffineTransform.h"
+#include "GraphicsContext.h"
+
+#include <cairo.h>
+
+namespace WebCore {
+
+cairo_pattern_t* Pattern::createPlatformPattern(const AffineTransform& patternTransform) const
+{
+ cairo_surface_t* surface = tileImage()->nativeImageForCurrentFrame();
+ if (!surface)
+ return 0;
+
+ cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface);
+ const cairo_matrix_t* pattern_matrix = reinterpret_cast<const cairo_matrix_t*>(&patternTransform);
+ cairo_pattern_set_matrix(pattern, pattern_matrix);
+ if (m_repeatX || m_repeatY)
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ return pattern;
+}
+
+}
diff --git a/WebCore/platform/graphics/cairo/rgb24-hacks.txt b/WebCore/platform/graphics/cairo/rgb24-hacks.txt
new file mode 100644
index 0000000..59f8070
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/rgb24-hacks.txt
@@ -0,0 +1,32 @@
+Index: cairo/src/cairo-win32-surface.c
+===================================================================
+--- cairo/src/cairo-win32-surface.c (revision 14498)
++++ cairo/src/cairo-win32-surface.c (working copy)
+@@ -824,8 +824,13 @@
+ * to figure out when we can use GDI. We don't have that checking
+ * anywhere at the moment, so just bail and use the fallback
+ * paths. */
+- if (surface->format != CAIRO_FORMAT_RGB24)
+- return CAIRO_INT_STATUS_UNSUPPORTED;
++ //if (surface->format != CAIRO_FORMAT_RGB24)
++ // return CAIRO_INT_STATUS_UNSUPPORTED;
++ // FIXME: We'll go ahead and optimize this now and just assume we're ok if
++ // the color has no alpha. Probably need to check various composite operators to
++ // get this exactly right.
++ if (color->alpha != 1.0)
++ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all
+ * surfaces with alpha.)
+@@ -1016,8 +1021,9 @@
+
+ /* We can only handle operator SOURCE or OVER with the destination
+ * having no alpha */
+- if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) ||
+- (dst->format != CAIRO_FORMAT_RGB24))
++ if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER))
++ // FIXME: It's not clear why ExtTextOut can't be called when the
++ // destination has alpha. Remove the RGB24 restriction. || (dst->format != CAIRO_FORMAT_RGB24))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* If we have a fallback mask clip set on the dst, we have
diff --git a/WebCore/platform/graphics/cairo/scale-removal.txt b/WebCore/platform/graphics/cairo/scale-removal.txt
new file mode 100644
index 0000000..47c0d70
--- /dev/null
+++ b/WebCore/platform/graphics/cairo/scale-removal.txt
@@ -0,0 +1,13 @@
+Index: cairo/src/cairo-win32-private.h
+===================================================================
+--- cairo/src/cairo-win32-private.h (revision 14582)
++++ cairo/src/cairo-win32-private.h (working copy)
+@@ -39,7 +39,7 @@
+ #include <cairo-win32.h>
+ #include <cairoint.h>
+
+-#define WIN32_FONT_LOGICAL_SCALE 32
++#define WIN32_FONT_LOGICAL_SCALE 1
+
+ typedef struct _cairo_win32_surface {
+ cairo_surface_t base;