diff options
Diffstat (limited to 'WebCore/platform/graphics/cairo')
-rw-r--r-- | WebCore/platform/graphics/cairo/AffineTransformCairo.cpp | 281 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/CairoPath.h | 45 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/FontCairo.cpp | 105 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/GradientCairo.cpp | 74 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp | 1104 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h | 101 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/ImageBufferCairo.cpp | 224 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/ImageBufferData.h | 44 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/ImageCairo.cpp | 187 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/ImageSourceCairo.cpp | 229 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/PathCairo.cpp | 285 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/PatternCairo.cpp | 50 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/rgb24-hacks.txt | 32 | ||||
-rw-r--r-- | WebCore/platform/graphics/cairo/scale-removal.txt | 13 |
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; |