summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp')
-rw-r--r--WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp281
1 files changed, 208 insertions, 73 deletions
diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
index 8741c5e..14034fd 100644
--- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
+++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
@@ -1,8 +1,9 @@
/*
* 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, 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) 2008 Nuanti Ltd.
+ * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,14 +33,17 @@
#if PLATFORM(CAIRO)
#include "CairoPath.h"
+#include "FEGaussianBlur.h"
#include "FloatRect.h"
#include "Font.h"
#include "ImageBuffer.h"
+#include "ImageBufferFilter.h"
#include "IntRect.h"
#include "NotImplemented.h"
#include "Path.h"
#include "Pattern.h"
#include "SimpleFontData.h"
+#include "SourceGraphic.h"
#include "TransformationMatrix.h"
#include <cairo.h>
@@ -69,6 +73,42 @@ static inline void setColor(cairo_t* cr, const Color& col)
cairo_set_source_rgba(cr, red, green, blue, alpha);
}
+static inline void setPlatformFill(GraphicsContext* context, cairo_t* cr, GraphicsContextPrivate* gcp)
+{
+ cairo_save(cr);
+ if (gcp->state.fillPattern) {
+ TransformationMatrix affine;
+ cairo_set_source(cr, gcp->state.fillPattern->createPlatformPattern(affine));
+ } else if (gcp->state.fillGradient)
+ cairo_set_source(cr, gcp->state.fillGradient->platformGradient());
+ else
+ setColor(cr, context->fillColor());
+ cairo_clip_preserve(cr);
+ cairo_paint_with_alpha(cr, gcp->state.globalAlpha);
+ cairo_restore(cr);
+}
+
+static inline void setPlatformStroke(GraphicsContext* context, cairo_t* cr, GraphicsContextPrivate* gcp)
+{
+ cairo_save(cr);
+ if (gcp->state.strokePattern) {
+ TransformationMatrix affine;
+ cairo_set_source(cr, gcp->state.strokePattern->createPlatformPattern(affine));
+ } else if (gcp->state.strokeGradient)
+ cairo_set_source(cr, gcp->state.strokeGradient->platformGradient());
+ else {
+ Color strokeColor = colorWithOverrideAlpha(context->strokeColor().rgb(), context->strokeColor().alpha() / 255.f * gcp->state.globalAlpha);
+ setColor(cr, strokeColor);
+ }
+ if (gcp->state.globalAlpha < 1.0f && (gcp->state.strokePattern || gcp->state.strokeGradient)) {
+ cairo_push_group(cr);
+ cairo_paint_with_alpha(cr, gcp->state.globalAlpha);
+ cairo_pop_group_to_source(cr);
+ }
+ cairo_stroke_preserve(cr);
+ cairo_restore(cr);
+}
+
// A fillRect helper
static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const Color& col)
{
@@ -78,6 +118,81 @@ static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const
cairo_fill(cr);
}
+static inline void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr)
+{
+ cairo_set_antialias(dstCr, cairo_get_antialias(srcCr));
+
+ size_t dashCount = cairo_get_dash_count(srcCr);
+ Vector<double> dashes(dashCount);
+
+ double offset;
+ cairo_get_dash(srcCr, dashes.data(), &offset);
+ cairo_set_dash(dstCr, dashes.data(), dashCount, offset);
+ cairo_set_line_cap(dstCr, cairo_get_line_cap(srcCr));
+ cairo_set_line_join(dstCr, cairo_get_line_join(srcCr));
+ cairo_set_line_width(dstCr, cairo_get_line_width(srcCr));
+ cairo_set_miter_limit(dstCr, cairo_get_miter_limit(srcCr));
+ cairo_set_fill_rule(dstCr, cairo_get_fill_rule(srcCr));
+}
+
+void GraphicsContext::calculateShadowBufferDimensions(IntSize& shadowBufferSize, FloatRect& shadowRect, float& kernelSize, const FloatRect& sourceRect, const IntSize& shadowSize, int shadowBlur)
+{
+#if ENABLE(FILTERS)
+ // calculate the kernel size according to the HTML5 canvas shadow specification
+ kernelSize = (shadowBlur < 8 ? shadowBlur / 2.f : sqrt(shadowBlur * 2.f));
+ int blurRadius = ceil(kernelSize);
+
+ shadowBufferSize = IntSize(sourceRect.width() + blurRadius * 2, sourceRect.height() + blurRadius * 2);
+
+ // determine dimensions of shadow rect
+ shadowRect = FloatRect(sourceRect.location(), shadowBufferSize);
+ shadowRect.move(shadowSize.width() - kernelSize, shadowSize.height() - kernelSize);
+#endif
+}
+
+static inline void drawPathShadow(GraphicsContext* context, GraphicsContextPrivate* gcp, bool fillShadow, bool strokeShadow)
+{
+#if ENABLE(FILTERS)
+ IntSize shadowSize;
+ int shadowBlur;
+ Color shadowColor;
+ if (!context->getShadow(shadowSize, shadowBlur, shadowColor))
+ return;
+
+ // Calculate filter values to create appropriate shadow.
+ cairo_t* cr = context->platformContext();
+ cairo_path_t* path = cairo_copy_path(cr);
+ double x0, x1, y0, y1;
+ if (strokeShadow)
+ cairo_stroke_extents(cr, &x0, &y0, &x1, &y1);
+ else
+ cairo_fill_extents(cr, &x0, &y0, &x1, &y1);
+ FloatRect rect(x0, y0, x1 - x0, y1 - y0);
+
+ IntSize shadowBufferSize;
+ FloatRect shadowRect;
+ float kernelSize = 0;
+ GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, kernelSize, rect, shadowSize, shadowBlur);
+
+ // Create suitably-sized ImageBuffer to hold the shadow.
+ OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize);
+
+ // Draw shadow into a new ImageBuffer.
+ cairo_t* shadowContext = shadowBuffer->context()->platformContext();
+ copyContextProperties(cr, shadowContext);
+ cairo_translate(shadowContext, -rect.x() + kernelSize, -rect.y() + kernelSize);
+ cairo_new_path(shadowContext);
+ cairo_append_path(shadowContext, path);
+
+ if (fillShadow)
+ setPlatformFill(context, shadowContext, gcp);
+ if (strokeShadow)
+ setPlatformStroke(context, shadowContext, gcp);
+
+ context->createPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, kernelSize);
+#endif
+}
+
GraphicsContext::GraphicsContext(PlatformGraphicsContext* cr)
: m_common(createGraphicsContextPrivate())
, m_data(new GraphicsContextPlatformPrivate)
@@ -387,30 +502,12 @@ void GraphicsContext::fillPath()
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:
- setColor(cr, fillColor());
- cairo_clip(cr);
- cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
- break;
- case PatternColorSpace: {
- TransformationMatrix affine;
- cairo_set_source(cr, m_common->state.fillPattern->createPlatformPattern(affine));
- cairo_clip(cr);
- cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
- break;
- }
- case GradientColorSpace:
- cairo_pattern_t* pattern = m_common->state.fillGradient->platformGradient();
- cairo_set_source(cr, pattern);
- cairo_clip(cr);
- cairo_paint_with_alpha(cr, m_common->state.globalAlpha);
- break;
- }
- cairo_restore(cr);
+ drawPathShadow(this, m_common, true, false);
+
+ setPlatformFill(this, cr, m_common);
+ cairo_new_path(cr);
}
void GraphicsContext::strokePath()
@@ -419,45 +516,26 @@ void GraphicsContext::strokePath()
return;
cairo_t* cr = m_data->cr;
- cairo_save(cr);
- switch (m_common->state.strokeColorSpace) {
- case SolidColorSpace:
- float red, green, blue, alpha;
- strokeColor().getRGBA(red, green, blue, alpha);
- if (m_common->state.globalAlpha < 1.0f)
- alpha *= m_common->state.globalAlpha;
- cairo_set_source_rgba(cr, red, green, blue, alpha);
- cairo_stroke(cr);
- break;
- case PatternColorSpace: {
- TransformationMatrix affine;
- cairo_set_source(cr, m_common->state.strokePattern->createPlatformPattern(affine));
- 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->platformGradient();
- 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);
+ drawPathShadow(this, m_common, false, true);
+
+ setPlatformStroke(this, cr, m_common);
+ cairo_new_path(cr);
+
}
void GraphicsContext::drawPath()
{
- fillPath();
- strokePath();
+ if (paintingDisabled())
+ return;
+
+ cairo_t* cr = m_data->cr;
+
+ cairo_set_fill_rule(cr, fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
+ drawPathShadow(this, m_common, true, true);
+
+ setPlatformFill(this, cr, m_common);
+ setPlatformStroke(this, cr, m_common);
+ cairo_new_path(cr);
}
void GraphicsContext::fillRect(const FloatRect& rect)
@@ -470,11 +548,36 @@ void GraphicsContext::fillRect(const FloatRect& rect)
fillPath();
}
-void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
+static void drawBorderlessRectShadow(GraphicsContext* context, const FloatRect& rect, const Color& rectColor)
+{
+#if ENABLE(FILTERS)
+ IntSize shadowSize;
+ int shadowBlur;
+ Color shadowColor;
+
+ if (!context->getShadow(shadowSize, shadowBlur, shadowColor))
+ return;
+
+ IntSize shadowBufferSize;
+ FloatRect shadowRect;
+ float kernelSize = 0;
+ GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, kernelSize, rect, shadowSize, shadowBlur);
+
+ // Draw shadow into a new ImageBuffer
+ OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize);
+ GraphicsContext* shadowContext = shadowBuffer->context();
+ shadowContext->fillRect(FloatRect(FloatPoint(kernelSize, kernelSize), rect.size()), rectColor, DeviceColorSpace);
+
+ context->createPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, kernelSize);
+#endif
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled())
return;
+ drawBorderlessRectShadow(this, rect, color);
if (color.alpha())
fillRectSourceOver(m_data->cr, rect, color);
}
@@ -634,13 +737,13 @@ IntPoint GraphicsContext::origin()
return IntPoint(static_cast<int>(matrix.x0), static_cast<int>(matrix.y0));
}
-void GraphicsContext::setPlatformFillColor(const Color& col)
+void GraphicsContext::setPlatformFillColor(const Color& col, ColorSpace colorSpace)
{
// 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)
+void GraphicsContext::setPlatformStrokeColor(const Color& col, ColorSpace colorSpace)
{
// Cairo contexts can't hold separate fill and stroke colors
// so we set them just before we actually fill or stroke
@@ -726,9 +829,48 @@ void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer
notImplemented();
}
-void GraphicsContext::setPlatformShadow(IntSize const&, int, Color const&)
+void GraphicsContext::setPlatformShadow(IntSize const& size, int, Color const&, ColorSpace)
{
- notImplemented();
+ // Cairo doesn't support shadows natively, they are drawn manually in the draw*
+ // functions
+
+ if (m_common->state.shadowsIgnoreTransforms) {
+ // Meaning that this graphics context is associated with a CanvasRenderingContext
+ // We flip the height since CG and HTML5 Canvas have opposite Y axis
+ m_common->state.shadowSize = IntSize(size.width(), -size.height());
+ }
+}
+
+void GraphicsContext::createPlatformShadow(PassOwnPtr<ImageBuffer> buffer, const Color& shadowColor, const FloatRect& shadowRect, float kernelSize)
+{
+#if ENABLE(FILTERS)
+ cairo_t* cr = m_data->cr;
+
+ // draw the shadow without blurring, if kernelSize is zero
+ if (!kernelSize) {
+ setColor(cr, shadowColor);
+ cairo_mask_surface(cr, buffer->image()->nativeImageForCurrentFrame(), shadowRect.x(), shadowRect.y());
+ return;
+ }
+
+ // limit kernel size to 1000, this is what CG is doing.
+ kernelSize = std::min(1000.f, kernelSize);
+
+ // create filter
+ RefPtr<Filter> filter = ImageBufferFilter::create();
+ filter->setSourceImage(buffer.release());
+ RefPtr<FilterEffect> source = SourceGraphic::create();
+ source->setScaledSubRegion(FloatRect(FloatPoint(), shadowRect.size()));
+ source->setIsAlphaImage(true);
+ RefPtr<FilterEffect> blur = FEGaussianBlur::create(source.get(), kernelSize, kernelSize);
+ blur->setScaledSubRegion(FloatRect(FloatPoint(), shadowRect.size()));
+ blur->apply(filter.get());
+
+ // Mask the filter with the shadow color and draw it to the context.
+ // Masking makes it possible to just blur the alpha channel.
+ setColor(cr, shadowColor);
+ cairo_mask_surface(cr, blur->resultImage()->image()->nativeImageForCurrentFrame(), shadowRect.x(), shadowRect.y());
+#endif
}
void GraphicsContext::clearPlatformShadow()
@@ -941,7 +1083,6 @@ 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);
@@ -952,9 +1093,6 @@ 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)
@@ -980,19 +1118,15 @@ 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, x1, y1, 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)
@@ -1005,7 +1139,7 @@ void GraphicsContext::clipOutEllipseInRect(const IntRect& r)
clipOut(p);
}
-void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
+void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled())
return;
@@ -1015,6 +1149,7 @@ void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft,
beginPath();
addPath(Path::createRoundedRectangle(r, topLeft, topRight, bottomLeft, bottomRight));
setColor(cr, color);
+ drawPathShadow(this, m_common, true, false);
cairo_fill(cr);
cairo_restore(cr);
}