diff options
Diffstat (limited to 'WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp')
-rw-r--r-- | WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp | 1104 |
1 files changed, 1104 insertions, 0 deletions
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) |