diff options
Diffstat (limited to 'WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp')
-rw-r--r-- | WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp | 324 |
1 files changed, 267 insertions, 57 deletions
diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index 08e8616..c403f44 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -1,6 +1,8 @@ /* * 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 @@ -33,10 +35,13 @@ #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> @@ -48,6 +53,7 @@ #elif PLATFORM(WIN) #include <cairo-win32.h> #endif +#include "GraphicsContextPrivate.h" #include "GraphicsContextPlatformPrivateCairo.h" #ifndef M_PI @@ -72,6 +78,25 @@ static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const 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) @@ -102,11 +127,13 @@ cairo_t* GraphicsContext::platformContext() const 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. @@ -287,32 +314,88 @@ void GraphicsContext::drawEllipse(const IntRect& rect) cairo_new_path(cr); } -// FIXME: This function needs to be adjusted to match the functionality on the Mac side. void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) { - if (paintingDisabled()) - return; - - if (strokeStyle() == NoStroke) + if (paintingDisabled() || strokeStyle() == NoStroke) return; int x = rect.x(); int y = rect.y(); float w = rect.width(); -#if 0 // FIXME: unused so far float h = rect.height(); float scaleFactor = h / w; float reverseScaleFactor = w / h; -#endif - float r = w / 2; + + float hRadius = w / 2; + float vRadius = h / 2; float fa = startAngle; float falen = fa + angleSpan; cairo_t* cr = m_data->cr; cairo_save(cr); - cairo_arc_negative(cr, x + r, y + r, r, -fa * M_PI/180, -falen * M_PI/180); + + 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()); - cairo_set_line_width(cr, strokeThickness()); + + 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); } @@ -350,13 +433,96 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points cairo_restore(cr); } -void GraphicsContext::fillRect(const IntRect& rect, const Color& color) +void GraphicsContext::fillPath() { if (paintingDisabled()) return; - if (color.alpha()) - fillRectSourceOver(m_data->cr, rect, color); + 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) @@ -368,7 +534,7 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) fillRectSourceOver(m_data->cr, rect, color); } -void GraphicsContext::clip(const IntRect& rect) +void GraphicsContext::clip(const FloatRect& rect) { if (paintingDisabled()) return; @@ -379,6 +545,7 @@ void GraphicsContext::clip(const IntRect& rect) 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) @@ -386,27 +553,48 @@ void GraphicsContext::drawFocusRing(const Color& color) if (paintingDisabled()) return; - int radius = (focusRingWidth() - 1) / 2; - int offset = radius + focusRingOffset(); - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); - IntRect finalFocusRect; - for (unsigned i = 0; i < rectCount; i++) { - IntRect focusRect = rects[i]; - focusRect.inflate(offset); - finalFocusRect.unite(focusRect); - } cairo_t* cr = m_data->cr; cairo_save(cr); - // FIXME: These rects should be rounded - cairo_rectangle(cr, finalFocusRect.x(), finalFocusRect.y(), finalFocusRect.width(), finalFocusRect.height()); + 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_stroke(cr); + 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); } @@ -430,7 +618,6 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, if (paintingDisabled()) return; -#if PLATFORM(GTK) cairo_t* cr = m_data->cr; cairo_save(cr); @@ -441,13 +628,14 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, 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); - - cairo_restore(cr); #else notImplemented(); #endif + + cairo_restore(cr); } FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) @@ -480,6 +668,7 @@ void GraphicsContext::translate(float x, float y) cairo_t* cr = m_data->cr; cairo_translate(cr, x, y); + m_data->translate(x, y); } IntPoint GraphicsContext::origin() @@ -492,14 +681,14 @@ IntPoint GraphicsContext::origin() void GraphicsContext::setPlatformFillColor(const Color& col) { - // FIXME: this is probably a no-op but I'm not sure - // notImplemented(); // commented-out because it's chatty and clutters output + // 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) { - // FIXME: this is probably a no-op but I'm not sure - //notImplemented(); // commented-out because it's chatty and clutters output + // 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) @@ -532,9 +721,6 @@ void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle) case DashedStroke: cairo_set_dash(m_data->cr, dashPattern, 2, 0); break; - default: - notImplemented(); - break; } } @@ -543,9 +729,6 @@ void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) notImplemented(); } -#if PLATFORM(GTK) -// FIXME: This should be moved to something like GraphicsContextCairoGTK.cpp, -// as there is a Windows implementation in platform/graphics/win/GraphicsContextCairoWin.cpp void GraphicsContext::concatCTM(const AffineTransform& transform) { if (paintingDisabled()) @@ -554,8 +737,8 @@ void GraphicsContext::concatCTM(const AffineTransform& transform) 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); } -#endif void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) { @@ -580,13 +763,20 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness cairo_set_fill_rule(cr, savedFillRule); } +void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer* imageBuffer) +{ + if (paintingDisabled()) + return; -void GraphicsContext::setShadow(IntSize const&, int, Color const&) + notImplemented(); +} + +void GraphicsContext::setPlatformShadow(IntSize const&, int, Color const&) { notImplemented(); } -void GraphicsContext::clearShadow() +void GraphicsContext::clearPlatformShadow() { notImplemented(); } @@ -599,6 +789,7 @@ void GraphicsContext::beginTransparencyLayer(float opacity) cairo_t* cr = m_data->cr; cairo_push_group(cr); m_data->layers.append(opacity); + m_data->beginTransparencyLayer(); } void GraphicsContext::endTransparencyLayer() @@ -611,6 +802,7 @@ void GraphicsContext::endTransparencyLayer() 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) @@ -635,9 +827,8 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float width) cairo_t* cr = m_data->cr; cairo_save(cr); cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); - setColor(cr, strokeColor()); cairo_set_line_width(cr, width); - cairo_stroke(cr); + strokePath(); cairo_restore(cr); } @@ -661,6 +852,11 @@ void GraphicsContext::setLineCap(LineCap lineCap) 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()) @@ -689,9 +885,14 @@ void GraphicsContext::setMiterLimit(float miter) cairo_set_miter_limit(m_data->cr, miter); } -void GraphicsContext::setAlpha(float) +void GraphicsContext::setAlpha(float alpha) { - notImplemented(); + m_common->state.globalAlpha = alpha; +} + +float GraphicsContext::getAlpha() +{ + return m_common->state.globalAlpha; } static inline cairo_operator_t toCairoOperator(CompositeOperator op) @@ -772,6 +973,7 @@ void GraphicsContext::clip(const Path& path) 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) @@ -779,6 +981,7 @@ 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); @@ -789,6 +992,9 @@ void GraphicsContext::clipOut(const Path& path) 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) @@ -797,6 +1003,7 @@ void GraphicsContext::rotate(float radians) return; cairo_rotate(m_data->cr, radians); + m_data->rotate(radians); } void GraphicsContext::scale(const FloatSize& size) @@ -805,6 +1012,7 @@ void GraphicsContext::scale(const FloatSize& size) return; cairo_scale(m_data->cr, size.width(), size.height()); + m_data->scale(size); } void GraphicsContext::clipOut(const IntRect& r) @@ -812,6 +1020,7 @@ 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); @@ -821,6 +1030,9 @@ void GraphicsContext::clipOut(const IntRect& r) 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) @@ -865,17 +1077,6 @@ GdkDrawable* GraphicsContext::gdkDrawable() const return GDK_DRAWABLE(m_data->expose->window); } - -IntPoint GraphicsContext::translatePoint(const IntPoint& point) const -{ - cairo_matrix_t tm; - cairo_get_matrix(m_data->cr, &tm); - double x = point.x(); - double y = point.y(); - - cairo_matrix_transform_point(&tm, &x, &y); - return IntPoint(x, y); -} #endif void GraphicsContext::setUseAntialiasing(bool enable) @@ -889,6 +1090,15 @@ void GraphicsContext::setUseAntialiasing(bool enable) 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) |