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.cpp287
1 files changed, 254 insertions, 33 deletions
diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
index 3a667ac..19cc518 100644
--- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
+++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
@@ -44,6 +44,7 @@
#include "NotImplemented.h"
#include "Path.h"
#include "Pattern.h"
+#include "PlatformRefPtrCairo.h"
#include "SimpleFontData.h"
#include "SourceGraphic.h"
@@ -175,7 +176,7 @@ static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const
cairo_close_path(context);
}
-void GraphicsContext::calculateShadowBufferDimensions(IntSize& shadowBufferSize, FloatRect& shadowRect, float& radius, const FloatRect& sourceRect, const FloatSize& shadowSize, float shadowBlur)
+void GraphicsContext::calculateShadowBufferDimensions(IntSize& shadowBufferSize, FloatRect& shadowRect, float& radius, const FloatRect& sourceRect, const FloatSize& shadowOffset, float shadowBlur)
{
#if ENABLE(FILTERS)
// limit radius to 128
@@ -185,17 +186,17 @@ void GraphicsContext::calculateShadowBufferDimensions(IntSize& shadowBufferSize,
// determine dimensions of shadow rect
shadowRect = FloatRect(sourceRect.location(), shadowBufferSize);
- shadowRect.move(shadowSize.width() - radius, shadowSize.height() - radius);
+ shadowRect.move(shadowOffset.width() - radius, shadowOffset.height() - radius);
#endif
}
static inline void drawPathShadow(GraphicsContext* context, GraphicsContextPrivate* gcp, bool fillShadow, bool strokeShadow)
{
#if ENABLE(FILTERS)
- FloatSize shadowSize;
+ FloatSize shadowOffset;
float shadowBlur;
Color shadowColor;
- if (!context->getShadow(shadowSize, shadowBlur, shadowColor))
+ if (!context->getShadow(shadowOffset, shadowBlur, shadowColor))
return;
// Calculate filter values to create appropriate shadow.
@@ -211,7 +212,7 @@ static inline void drawPathShadow(GraphicsContext* context, GraphicsContextPriva
IntSize shadowBufferSize;
FloatRect shadowRect;
float radius = 0;
- GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowSize, shadowBlur);
+ GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowOffset, shadowBlur);
// Create suitably-sized ImageBuffer to hold the shadow.
OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize);
@@ -228,7 +229,7 @@ static inline void drawPathShadow(GraphicsContext* context, GraphicsContextPriva
if (strokeShadow)
setPlatformStroke(context, shadowContext, gcp);
- context->createPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius);
+ context->applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius);
#endif
}
@@ -623,24 +624,38 @@ void GraphicsContext::fillRect(const FloatRect& rect)
static void drawBorderlessRectShadow(GraphicsContext* context, const FloatRect& rect, const Color& rectColor)
{
#if ENABLE(FILTERS)
- FloatSize shadowSize;
+ AffineTransform transform = context->getCTM();
+ // drawTiledShadow still does not work with rotations.
+ if ((transform.isIdentityOrTranslationOrFlipped())) {
+ cairo_t* cr = context->platformContext();
+ cairo_save(cr);
+ appendWebCorePathToCairoContext(cr, Path::createRectangle(rect));
+ FloatSize corner;
+ IntRect shadowRect(rect);
+ context->drawTiledShadow(shadowRect, corner, corner, corner, corner, DeviceColorSpace);
+ cairo_restore(cr);
+
+ return;
+ }
+
+ FloatSize shadowOffset;
float shadowBlur;
Color shadowColor;
- if (!context->getShadow(shadowSize, shadowBlur, shadowColor))
+ if (!context->getShadow(shadowOffset, shadowBlur, shadowColor))
return;
IntSize shadowBufferSize;
FloatRect shadowRect;
float radius = 0;
- GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowSize, shadowBlur);
+ GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowOffset, shadowBlur);
// Draw shadow into a new ImageBuffer
OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize);
GraphicsContext* shadowContext = shadowBuffer->context();
shadowContext->fillRect(FloatRect(FloatPoint(radius, radius), rect.size()), rectColor, DeviceColorSpace);
- context->createPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius);
+ context->applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius);
#endif
}
@@ -759,7 +774,7 @@ void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool pr
#include "DrawErrorUnderline.h"
#endif
-void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, int width, bool grammar)
+void GraphicsContext::drawLineForTextChecking(const IntPoint& origin, int width, TextCheckingLineStyle style)
{
if (paintingDisabled())
return;
@@ -767,12 +782,17 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin,
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
+ switch (style) {
+ case TextCheckingSpellingLineStyle:
cairo_set_source_rgb(cr, 1, 0, 0);
+ break;
+ case TextCheckingGrammarLineStyle:
+ cairo_set_source_rgb(cr, 0, 1, 0);
+ break;
+ default:
+ cairo_restore(cr);
+ return;
+ }
#if PLATFORM(GTK)
// We ignore most of the provided constants in favour of the platform style
@@ -917,24 +937,29 @@ void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color cons
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 = FloatSize(size.width(), -size.height());
+ m_common->state.shadowOffset = FloatSize(size.width(), -size.height());
}
}
-void GraphicsContext::createPlatformShadow(PassOwnPtr<ImageBuffer> buffer, const Color& shadowColor, const FloatRect& shadowRect, float radius)
+void GraphicsContext::applyPlatformShadow(PassOwnPtr<ImageBuffer> buffer, const Color& shadowColor, const FloatRect& shadowRect, float radius)
{
#if ENABLE(FILTERS)
- cairo_t* cr = m_data->cr;
+ setColor(m_data->cr, shadowColor);
+ PlatformRefPtr<cairo_surface_t> shadowMask(createShadowMask(buffer, shadowRect, radius));
+ cairo_mask_surface(m_data->cr, shadowMask.get(), shadowRect.x(), shadowRect.y());
+#endif
+}
- // calculate the standard deviation
- float sd = FEGaussianBlur::calculateStdDeviation(radius);
+PlatformRefPtr<cairo_surface_t> GraphicsContext::createShadowMask(PassOwnPtr<ImageBuffer> buffer, const FloatRect& shadowRect, float radius)
+{
+#if ENABLE(FILTERS)
+ if (!radius)
+ return buffer->m_data.m_surface;
- // draw the shadow without blurring, if radius is zero
- if (!radius || !sd) {
- setColor(cr, shadowColor);
- cairo_mask_surface(cr, buffer->m_data.m_surface, shadowRect.x(), shadowRect.y());
- return;
- }
+ FloatPoint blurRadius = FloatPoint(radius, radius);
+ float sd = FEGaussianBlur::calculateStdDeviation(radius);
+ if (!sd)
+ return buffer->m_data.m_surface;
// create filter
RefPtr<Filter> filter = ImageBufferFilter::create();
@@ -945,14 +970,11 @@ void GraphicsContext::createPlatformShadow(PassOwnPtr<ImageBuffer> buffer, const
RefPtr<FilterEffect> blur = FEGaussianBlur::create(source.get(), sd, sd);
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()->m_data.m_surface, shadowRect.x(), shadowRect.y());
+ return blur->resultImage()->m_data.m_surface;
#endif
}
+
void GraphicsContext::clearPlatformShadow()
{
notImplemented();
@@ -1218,6 +1240,200 @@ void GraphicsContext::clipOutEllipseInRect(const IntRect& r)
clipOut(p);
}
+static inline FloatPoint getPhase(const FloatRect& dest, const FloatRect& tile)
+{
+ FloatPoint phase = dest.location();
+ phase.move(-tile.x(), -tile.y());
+
+ return phase;
+}
+
+/*
+ This function uses tiling to improve the performance of the shadow
+ drawing of rounded rectangles. The code basically does the following
+ steps:
+
+ 1. Calculate the minimum rectangle size required to create the
+ tiles
+
+ 2. If that size is smaller than the real rectangle render the new
+ small rectangle and its shadow in a new surface, in other case
+ render the shadow of the real rectangle in the destination
+ surface.
+
+ 3. Calculate the sizes and positions of the tiles and their
+ destinations and use drawPattern to render the final shadow. The
+ code divides the rendering in 8 tiles:
+
+ 1 | 2 | 3
+ -----------
+ 4 | | 5
+ -----------
+ 6 | 7 | 8
+
+ The corners are directly copied from the small rectangle to the
+ real one and the side tiles are 1 pixel width, we use them as
+
+ tiles to cover the destination side. The corner tiles are bigger
+ than just the side of the rounded corner, we need to increase it
+ because the modifications caused by the corner over the blur
+ effect. We fill the central part with solid color to complete the
+ shadow.
+ */
+void GraphicsContext::drawTiledShadow(const IntRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, ColorSpace colorSpace)
+{
+#if ENABLE(FILTERS)
+ FloatSize shadowSize;
+ float shadowBlur;
+ Color shadowColor;
+ if (!getShadow(shadowSize, shadowBlur, shadowColor))
+ return;
+
+ // Calculate filter values to create appropriate shadow.
+ cairo_t* cr = m_data->cr;
+
+ IntSize shadowBufferSize;
+ FloatRect shadowRect;
+ float blurRadius = 0;
+ GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, blurRadius, rect, shadowSize, shadowBlur);
+
+ // Size of the tiling side.
+ int sideTileWidth = 1;
+ float radiusTwice = blurRadius * 2;
+
+ // Find the extra space needed from the curve of the corners.
+ int extraWidthFromCornerRadii = radiusTwice + max(topLeftRadius.width(), bottomLeftRadius.width()) +
+ radiusTwice + max(topRightRadius.width(), bottomRightRadius.width());
+ int extraHeightFromCornerRadii = radiusTwice + max(topLeftRadius.height(), topRightRadius.height()) +
+ radiusTwice + max(bottomLeftRadius.height(), bottomRightRadius.height());
+
+ // The length of a side of the buffer is the enough space for four blur radii,
+ // the radii of the corners, and then 1 pixel to draw the side tiles.
+ IntSize smallBufferSize = IntSize(sideTileWidth + extraWidthFromCornerRadii,
+ sideTileWidth + extraHeightFromCornerRadii);
+
+ if ((smallBufferSize.width() > shadowBufferSize.width()) || (smallBufferSize.height() > shadowBufferSize.height()) || (blurRadius <= 0)) {
+ // Create suitably-sized ImageBuffer to hold the shadow.
+ OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize);
+ if (!shadowBuffer)
+ return;
+
+ // Draw shadow into a new ImageBuffer.
+ cairo_t* shadowContext = shadowBuffer->context()->platformContext();
+ copyContextProperties(cr, shadowContext);
+ cairo_translate(shadowContext, -rect.x() + blurRadius, -rect.y() + blurRadius);
+ cairo_new_path(shadowContext);
+ cairo_path_t* path = cairo_copy_path(cr);
+ cairo_append_path(shadowContext, path);
+ cairo_path_destroy(path);
+
+ setPlatformFill(this, shadowContext, m_common);
+
+ applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, blurRadius);
+
+ return;
+ }
+
+ OwnPtr<ImageBuffer> smallBuffer = ImageBuffer::create(smallBufferSize);
+ if (!smallBuffer)
+ return;
+
+ IntRect smallRect = IntRect(blurRadius, blurRadius, smallBufferSize.width() - radiusTwice, smallBufferSize.height() - radiusTwice);
+
+ // Draw shadow into a new ImageBuffer.
+ cairo_t* smallBufferContext = smallBuffer->context()->platformContext();
+ copyContextProperties(cr, smallBufferContext);
+ appendWebCorePathToCairoContext(smallBuffer->context()->platformContext(), Path::createRoundedRectangle(smallRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius));
+ setPlatformFill(this, smallBufferContext, m_common);
+
+ OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(smallBufferSize);
+ if (!shadowBuffer)
+ return;
+
+ smallRect.setSize(smallBufferSize);
+
+ PlatformRefPtr<cairo_surface_t> shadowMask(createShadowMask(smallBuffer.release(), smallRect, blurRadius));
+
+ cairo_t* shadowContext = shadowBuffer->context()->platformContext();
+ setColor(shadowContext, shadowColor);
+ cairo_mask_surface(shadowContext, shadowMask.get(), 0, 0);
+
+ // Fill the internal part of the shadow.
+ shadowRect.inflate(-radiusTwice);
+ if (!shadowRect.isEmpty()) {
+ cairo_save(cr);
+ appendWebCorePathToCairoContext(cr, Path::createRoundedRectangle(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius));
+ setColor(cr, shadowColor);
+ cairo_fill(cr);
+ cairo_restore(cr);
+ }
+ shadowRect.inflate(radiusTwice);
+
+ // Draw top side.
+ FloatRect tileRect = FloatRect(radiusTwice + topLeftRadius.width(), 0, sideTileWidth, radiusTwice);
+ FloatRect destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y());
+ destRect.setWidth(shadowRect.width() - topLeftRadius.width() - topRightRadius.width() - blurRadius * 4);
+ FloatPoint phase = getPhase(destRect, tileRect);
+ AffineTransform patternTransform;
+ patternTransform.makeIdentity();
+ shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect);
+
+ // Draw the bottom side.
+ tileRect = FloatRect(radiusTwice + bottomLeftRadius.width(), smallBufferSize.height() - radiusTwice, sideTileWidth, radiusTwice);
+ destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y() + radiusTwice + rect.height() - smallBufferSize.height());
+ destRect.setWidth(shadowRect.width() - bottomLeftRadius.width() - bottomRightRadius.width() - blurRadius * 4);
+ phase = getPhase(destRect, tileRect);
+ shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect);
+
+ // Draw the right side.
+ tileRect = FloatRect(smallBufferSize.width() - radiusTwice, radiusTwice + topRightRadius.height(), radiusTwice, sideTileWidth);
+ destRect = tileRect;
+ destRect.move(shadowRect.x() + radiusTwice + rect.width() - smallBufferSize.width(), shadowRect.y());
+ destRect.setHeight(shadowRect.height() - topRightRadius.height() - bottomRightRadius.height() - blurRadius * 4);
+ phase = getPhase(destRect, tileRect);
+ shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect);
+
+ // Draw the left side.
+ tileRect = FloatRect(0, radiusTwice + topLeftRadius.height(), radiusTwice, sideTileWidth);
+ destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y());
+ destRect.setHeight(shadowRect.height() - topLeftRadius.height() - bottomLeftRadius.height() - blurRadius * 4);
+ phase = FloatPoint(destRect.x() - tileRect.x(), destRect.y() - tileRect.y());
+ shadowBuffer->drawPattern(this, tileRect, patternTransform,
+ phase, colorSpace, CompositeSourceOver, destRect);
+
+ // Draw the top left corner.
+ tileRect = FloatRect(0, 0, radiusTwice + topLeftRadius.width(), radiusTwice + topLeftRadius.height());
+ destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y());
+ phase = getPhase(destRect, tileRect);
+ shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect);
+
+ // Draw the top right corner.
+ tileRect = FloatRect(smallBufferSize.width() - radiusTwice - topRightRadius.width(), 0, radiusTwice + topRightRadius.width(), radiusTwice + topRightRadius.height());
+ destRect = tileRect;
+ destRect.move(shadowRect.x() + rect.width() - smallBufferSize.width() + radiusTwice, shadowRect.y());
+ phase = getPhase(destRect, tileRect);
+ shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect);
+
+ // Draw the bottom right corner.
+ tileRect = FloatRect(smallBufferSize.width() - radiusTwice - bottomRightRadius.width(), smallBufferSize.height() - radiusTwice - bottomRightRadius.height(), radiusTwice + bottomRightRadius.width(), radiusTwice + bottomRightRadius.height());
+ destRect = tileRect;
+ destRect.move(shadowRect.x() + rect.width() - smallBufferSize.width() + radiusTwice, shadowRect.y() + rect.height() - smallBufferSize.height() + radiusTwice);
+ phase = getPhase(destRect, tileRect);
+ shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect);
+
+ // Draw the bottom left corner.
+ tileRect = FloatRect(0, smallBufferSize.height() - radiusTwice - bottomLeftRadius.height(), radiusTwice + bottomLeftRadius.width(), radiusTwice + bottomLeftRadius.height());
+ destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y() + rect.height() - smallBufferSize.height() + radiusTwice);
+ phase = getPhase(destRect, tileRect);
+ shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect);
+#endif
+}
+
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())
@@ -1227,7 +1443,12 @@ void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft,
cairo_save(cr);
appendWebCorePathToCairoContext(cr, Path::createRoundedRectangle(r, topLeft, topRight, bottomLeft, bottomRight));
setColor(cr, color);
- drawPathShadow(this, m_common, true, false);
+ AffineTransform transform = this->getCTM();
+ // drawTiledShadow still does not work with rotations.
+ if (transform.isIdentityOrTranslationOrFlipped())
+ drawTiledShadow(r, topLeft, topRight, bottomLeft, bottomRight, colorSpace);
+ else
+ drawPathShadow(this, m_common, true, false);
cairo_fill(cr);
cairo_restore(cr);
}