diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/graphics/cairo | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/graphics/cairo')
23 files changed, 3685 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/cairo/CairoPath.h b/Source/WebCore/platform/graphics/cairo/CairoPath.h new file mode 100644 index 0000000..da7affb --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/CairoPath.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk> + Copyright (C) 2010 Igalia S.L. + + 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. +class CairoPath { +public: + 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); + } + + cairo_t* context() { return m_cr; } + +private: + cairo_t* m_cr; +}; + +} // namespace WebCore + +#endif // CairoPath_h diff --git a/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp b/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp new file mode 100644 index 0000000..013a4af --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * 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 "CairoUtilities.h" + +#include "AffineTransform.h" +#include "CairoPath.h" +#include "Color.h" +#include "FloatPoint.h" +#include "FloatRect.h" +#include "IntRect.h" +#include "OwnPtrCairo.h" +#include "Path.h" +#include "RefPtrCairo.h" +#include <wtf/Vector.h> + +namespace WebCore { + +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 setSourceRGBAFromColor(cairo_t* context, const Color& color) +{ + float red, green, blue, alpha; + color.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(context, red, green, blue, alpha); +} + +void appendPathToCairoContext(cairo_t* to, cairo_t* from) +{ + OwnPtr<cairo_path_t> cairoPath(cairo_copy_path(from)); + cairo_append_path(to, cairoPath.get()); +} + +void setPathOnCairoContext(cairo_t* to, cairo_t* from) +{ + cairo_new_path(to); + appendPathToCairoContext(to, from); +} + +void appendWebCorePathToCairoContext(cairo_t* context, const Path& path) +{ + appendPathToCairoContext(context, path.platformPath()->context()); +} + +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 drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect, + const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect) +{ + // Avoid NaN + if (!isfinite(phase.x()) || !isfinite(phase.y())) + return; + + cairo_save(cr); + + RefPtr<cairo_surface_t> clippedImageSurface = 0; + if (tileRect.size() != imageSize) { + IntRect imageRect = enclosingIntRect(tileRect); + clippedImageSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, imageRect.width(), imageRect.height())); + RefPtr<cairo_t> clippedImageContext = adoptRef(cairo_create(clippedImageSurface.get())); + cairo_set_source_surface(clippedImageContext.get(), image, -tileRect.x(), -tileRect.y()); + cairo_paint(clippedImageContext.get()); + image = clippedImageSurface.get(); + } + + cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + cairo_matrix_t patternMatrix = cairo_matrix_t(patternTransform); + cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()}; + cairo_matrix_t combined; + cairo_matrix_multiply(&combined, &patternMatrix, &phaseMatrix); + cairo_matrix_invert(&combined); + cairo_pattern_set_matrix(pattern, &combined); + + cairo_set_operator(cr, op); + cairo_set_source(cr, pattern); + cairo_pattern_destroy(pattern); + cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height()); + cairo_fill(cr); + + cairo_restore(cr); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/cairo/CairoUtilities.h b/Source/WebCore/platform/graphics/cairo/CairoUtilities.h new file mode 100644 index 0000000..d8fff8d --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/CairoUtilities.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * 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 CairoUtilities_h +#define CairoUtilities_h + +#include <GraphicsTypes.h> +#include <cairo.h> + +namespace WebCore { +class AffineTransform; +class Color; +class FloatRect; +class FloatPoint; +class IntSize; +class Path; + +void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr); +void setSourceRGBAFromColor(cairo_t*, const Color&); +void appendPathToCairoContext(cairo_t* to, cairo_t* from); +void setPathOnCairoContext(cairo_t* to, cairo_t* from); +void appendWebCorePathToCairoContext(cairo_t* context, const Path& path); +cairo_operator_t toCairoOperator(CompositeOperator op); +void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect, + const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect); + +} // namespace WebCore + +#endif // CairoUtilities_h diff --git a/Source/WebCore/platform/graphics/cairo/ContextShadowCairo.cpp b/Source/WebCore/platform/graphics/cairo/ContextShadowCairo.cpp new file mode 100644 index 0000000..b0588d6 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/ContextShadowCairo.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2010 Sencha, Inc. + * Copyright (C) 2010 Igalia S.L. + * + * 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. + */ + +#include "config.h" +#include "ContextShadow.h" + +#include "AffineTransform.h" +#include "CairoUtilities.h" +#include "GraphicsContext.h" +#include "OwnPtrCairo.h" +#include "Path.h" +#include "Timer.h" +#include <cairo.h> + +using WTF::max; + +namespace WebCore { + +static cairo_surface_t* scratchBuffer = 0; +static void purgeScratchBuffer() +{ + cairo_surface_destroy(scratchBuffer); + scratchBuffer = 0; +} + +// ContextShadow needs a scratch image as the buffer for the blur filter. +// Instead of creating and destroying the buffer for every operation, +// we create a buffer which will be automatically purged via a timer. +class PurgeScratchBufferTimer : public TimerBase { +private: + virtual void fired() { purgeScratchBuffer(); } +}; +static PurgeScratchBufferTimer purgeScratchBufferTimer; +static void scheduleScratchBufferPurge() +{ + if (purgeScratchBufferTimer.isActive()) + purgeScratchBufferTimer.stop(); + purgeScratchBufferTimer.startOneShot(2); +} + +static cairo_surface_t* getScratchBuffer(const IntSize& size) +{ + int width = size.width(); + int height = size.height(); + int scratchWidth = scratchBuffer ? cairo_image_surface_get_width(scratchBuffer) : 0; + int scratchHeight = scratchBuffer ? cairo_image_surface_get_height(scratchBuffer) : 0; + + // We do not need to recreate the buffer if the current buffer is large enough. + if (scratchBuffer && scratchWidth >= width && scratchHeight >= height) + return scratchBuffer; + + purgeScratchBuffer(); + + // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests. + width = (1 + (width >> 5)) << 5; + height = (1 + (height >> 5)) << 5; + scratchBuffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + return scratchBuffer; +} + +PlatformContext ContextShadow::beginShadowLayer(GraphicsContext* context, const FloatRect& layerArea) +{ + adjustBlurDistance(context); + + double x1, x2, y1, y2; + cairo_clip_extents(context->platformContext(), &x1, &y1, &x2, &y2); + IntRect layerRect = calculateLayerBoundingRect(context, layerArea, IntRect(x1, y1, x2 - x1, y2 - y1)); + + // Don't paint if we are totally outside the clip region. + if (layerRect.isEmpty()) + return 0; + + m_layerImage = getScratchBuffer(layerRect.size()); + m_layerContext = cairo_create(m_layerImage); + + // Always clear the surface first. + cairo_set_operator(m_layerContext, CAIRO_OPERATOR_CLEAR); + cairo_paint(m_layerContext); + cairo_set_operator(m_layerContext, CAIRO_OPERATOR_OVER); + + cairo_translate(m_layerContext, m_layerContextTranslation.x(), m_layerContextTranslation.y()); + return m_layerContext; +} + +void ContextShadow::endShadowLayer(GraphicsContext* context) +{ + cairo_destroy(m_layerContext); + m_layerContext = 0; + + if (m_type == BlurShadow) { + cairo_surface_flush(m_layerImage); + blurLayerImage(cairo_image_surface_get_data(m_layerImage), + IntSize(cairo_image_surface_get_width(m_layerImage), cairo_image_surface_get_height(m_layerImage)), + cairo_image_surface_get_stride(m_layerImage)); + cairo_surface_mark_dirty(m_layerImage); + } + + cairo_t* cr = context->platformContext(); + cairo_save(cr); + setSourceRGBAFromColor(cr, m_color); + cairo_mask_surface(cr, m_layerImage, m_layerOrigin.x(), m_layerOrigin.y()); + cairo_restore(cr); + + // Schedule a purge of the scratch buffer. We do not need to destroy the surface. + scheduleScratchBufferPurge(); +} + +void ContextShadow::drawRectShadowWithoutTiling(GraphicsContext* context, const IntRect& shadowRect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius, float alpha) +{ + beginShadowLayer(context, shadowRect); + + if (!m_layerContext) + return; + + Path path; + path.addRoundedRect(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + appendWebCorePathToCairoContext(m_layerContext, path); + cairo_set_source_rgba(m_layerContext, 0, 0, 0, alpha); + cairo_fill(m_layerContext); + + endShadowLayer(context); +} + +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 size of the shadow template, a rectangle that + contains all the necessary tiles to draw the complete shadow. + + 2. If that size is smaller than the real rectangle render the new + template 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 template 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 ContextShadow::drawRectShadow(GraphicsContext* context, const IntRect& rect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius) +{ + + float radiusTwice = m_blurDistance * 2; + + // Find the space the corners need inside the rect for its shadows. + int internalShadowWidth = radiusTwice + max(topLeftRadius.width(), bottomLeftRadius.width()) + + max(topRightRadius.width(), bottomRightRadius.width()); + int internalShadowHeight = radiusTwice + max(topLeftRadius.height(), topRightRadius.height()) + + max(bottomLeftRadius.height(), bottomRightRadius.height()); + + cairo_t* cr = context->platformContext(); + + // drawShadowedRect still does not work with rotations. + // https://bugs.webkit.org/show_bug.cgi?id=45042 + if ((!context->getCTM().isIdentityOrTranslationOrFlipped()) || (internalShadowWidth > rect.width()) + || (internalShadowHeight > rect.height()) || (m_type != BlurShadow)) { + drawRectShadowWithoutTiling(context, rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, context->getAlpha()); + return; + } + + // Calculate size of the template shadow buffer. + IntSize shadowBufferSize = IntSize(rect.width() + radiusTwice, rect.height() + radiusTwice); + + // Determine dimensions of shadow rect. + FloatRect shadowRect = FloatRect(rect.location(), shadowBufferSize); + shadowRect.move(- m_blurDistance, - m_blurDistance); + + // Size of the tiling side. + int sideTileWidth = 1; + + // 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 shadowTemplateSize = IntSize(sideTileWidth + radiusTwice + internalShadowWidth, + sideTileWidth + radiusTwice + internalShadowHeight); + + // Reduce the size of what we have to draw with the clip area. + double x1, x2, y1, y2; + cairo_clip_extents(cr, &x1, &y1, &x2, &y2); + calculateLayerBoundingRect(context, shadowRect, IntRect(x1, y1, x2 - x1, y2 - y1)); + + if ((shadowTemplateSize.width() * shadowTemplateSize.height() > m_sourceRect.width() * m_sourceRect.height())) { + drawRectShadowWithoutTiling(context, rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, context->getAlpha()); + return; + } + + shadowRect.move(m_offset.width(), m_offset.height()); + + m_layerImage = getScratchBuffer(shadowTemplateSize); + + // Draw shadow into a new ImageBuffer. + m_layerContext = cairo_create(m_layerImage); + + // Clear the surface first. + cairo_set_operator(m_layerContext, CAIRO_OPERATOR_CLEAR); + cairo_paint(m_layerContext); + cairo_set_operator(m_layerContext, CAIRO_OPERATOR_OVER); + + // Draw the rectangle. + IntRect templateRect = IntRect(m_blurDistance, m_blurDistance, shadowTemplateSize.width() - radiusTwice, shadowTemplateSize.height() - radiusTwice); + Path path; + path.addRoundedRect(templateRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + appendWebCorePathToCairoContext(m_layerContext, path); + + cairo_set_source_rgba(m_layerContext, 0, 0, 0, context->getAlpha()); + cairo_fill(m_layerContext); + + // Blur the image. + cairo_surface_flush(m_layerImage); + blurLayerImage(cairo_image_surface_get_data(m_layerImage), shadowTemplateSize, cairo_image_surface_get_stride(m_layerImage)); + cairo_surface_mark_dirty(m_layerImage); + + // Mask the image with the shadow color. + cairo_set_operator(m_layerContext, CAIRO_OPERATOR_IN); + setSourceRGBAFromColor(m_layerContext, m_color); + cairo_paint(m_layerContext); + + cairo_destroy(m_layerContext); + m_layerContext = 0; + + // Fill the internal part of the shadow. + shadowRect.inflate(-radiusTwice); + if (!shadowRect.isEmpty()) { + cairo_save(cr); + path.clear(); + path.addRoundedRect(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + appendWebCorePathToCairoContext(cr, path); + setSourceRGBAFromColor(cr, m_color); + 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() - m_blurDistance * 4); + FloatPoint phase = getPhase(destRect, tileRect); + AffineTransform patternTransform; + patternTransform.makeIdentity(); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the bottom side. + tileRect = FloatRect(radiusTwice + bottomLeftRadius.width(), shadowTemplateSize.height() - radiusTwice, sideTileWidth, radiusTwice); + destRect = tileRect; + destRect.move(shadowRect.x(), shadowRect.y() + radiusTwice + rect.height() - shadowTemplateSize.height()); + destRect.setWidth(shadowRect.width() - bottomLeftRadius.width() - bottomRightRadius.width() - m_blurDistance * 4); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the right side. + tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice, radiusTwice + topRightRadius.height(), radiusTwice, sideTileWidth); + destRect = tileRect; + destRect.move(shadowRect.x() + radiusTwice + rect.width() - shadowTemplateSize.width(), shadowRect.y()); + destRect.setHeight(shadowRect.height() - topRightRadius.height() - bottomRightRadius.height() - m_blurDistance * 4); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, 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() - m_blurDistance * 4); + phase = FloatPoint(destRect.x() - tileRect.x(), destRect.y() - tileRect.y()); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, 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); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the top right corner. + tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice - topRightRadius.width(), 0, radiusTwice + topRightRadius.width(), + radiusTwice + topRightRadius.height()); + destRect = tileRect; + destRect.move(shadowRect.x() + rect.width() - shadowTemplateSize.width() + radiusTwice, shadowRect.y()); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the bottom right corner. + tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice - bottomRightRadius.width(), + shadowTemplateSize.height() - radiusTwice - bottomRightRadius.height(), + radiusTwice + bottomRightRadius.width(), radiusTwice + bottomRightRadius.height()); + destRect = tileRect; + destRect.move(shadowRect.x() + rect.width() - shadowTemplateSize.width() + radiusTwice, + shadowRect.y() + rect.height() - shadowTemplateSize.height() + radiusTwice); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the bottom left corner. + tileRect = FloatRect(0, shadowTemplateSize.height() - radiusTwice - bottomLeftRadius.height(), + radiusTwice + bottomLeftRadius.width(), radiusTwice + bottomLeftRadius.height()); + destRect = tileRect; + destRect.move(shadowRect.x(), shadowRect.y() + rect.height() - shadowTemplateSize.height() + radiusTwice); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Schedule a purge of the scratch buffer. + scheduleScratchBufferPurge(); +} + +} diff --git a/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h b/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h new file mode 100644 index 0000000..1e0a846 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2004 Red Hat, Inc. + * Copyright (C) 2010 Brent Fulgham <bfulgham@webkit.org> + * + * Based on Pango sources (see pangocairo-render.c) + * + * 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 along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#if PLATFORM(CAIRO) + +#include <cairo.h> + +// +// Draws an error underline that looks like one of: +// +// H E H +// /\ /\ /\ /\ /\ - +// A/ \ / \ / \ A/ \ / \ | +// \ \ / \ / /D \ \ / \ | +// \ \/ C \/ / \ \/ C \ | height = heightSquares * square +// \ /\ F / \ F /\ \ | +// \ / \ / \ / \ \G | +// \ / \ / \ / \ / | +// \/ \/ \/ \/ - +// B B +// |---| +// unitWidth = (heightSquares - 1) * square +// +// The x, y, width, height passed in give the desired bounding box; +// x/width are adjusted to make the underline a integer number of units +// wide. +// +static inline void drawErrorUnderline(cairo_t* cr, double x, double y, double width, double height) +{ + static const double heightSquares = 2.5; + + double square = height / heightSquares; + double halfSquare = 0.5 * square; + + double unitWidth = (heightSquares - 1.0) * square; + int widthUnits = static_cast<int>((width + 0.5 * unitWidth) / unitWidth); + + x += 0.5 * (width - widthUnits * unitWidth); + width = widthUnits * unitWidth; + + double bottom = y + height; + double top = y; + + // Bottom of squiggle + cairo_move_to(cr, x - halfSquare, top + halfSquare); // A + + int i = 0; + for (i = 0; i < widthUnits; i += 2) { + double middle = x + (i + 1) * unitWidth; + double right = x + (i + 2) * unitWidth; + + cairo_line_to(cr, middle, bottom); // B + + if (i + 2 == widthUnits) + cairo_line_to(cr, right + halfSquare, top + halfSquare); // D + else if (i + 1 != widthUnits) + cairo_line_to(cr, right, top + square); // C + } + + // Top of squiggle + for (i -= 2; i >= 0; i -= 2) { + double left = x + i * unitWidth; + double middle = x + (i + 1) * unitWidth; + double right = x + (i + 2) * unitWidth; + + if (i + 1 == widthUnits) + cairo_line_to(cr, middle + halfSquare, bottom - halfSquare); // G + else { + if (i + 2 == widthUnits) + cairo_line_to(cr, right, top); // E + + cairo_line_to(cr, middle, bottom - halfSquare); // F + } + + cairo_line_to(cr, left, top); // H + } +} + +#endif diff --git a/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp b/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp new file mode 100644 index 0000000..9f86f74 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * 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 "FloatRect.h" + +#include <cairo.h> + +namespace WebCore { + +FloatRect::FloatRect(const cairo_rectangle_t& r) + : m_location(r.x, r.y) + , m_size(r.width, r.height) +{ +} + +FloatRect::operator cairo_rectangle_t() const +{ + cairo_rectangle_t r = { x(), y(), width(), height() }; + return r; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/cairo/FontCairo.cpp b/Source/WebCore/platform/graphics/cairo/FontCairo.cpp new file mode 100644 index 0000000..2d79499 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/FontCairo.cpp @@ -0,0 +1,175 @@ +/* + * 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> + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2010 Holger Hans Peter Freyther + * + * 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 "AffineTransform.h" +#include "CairoUtilities.h" +#include "ContextShadow.h" +#include "GlyphBuffer.h" +#include "Gradient.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "Pattern.h" +#include "SimpleFontData.h" + +namespace WebCore { + +static void prepareContextForGlyphDrawing(cairo_t* context, const SimpleFontData* font, const FloatPoint& point) +{ + static const float syntheticObliqueSkew = -tanf(14 * acosf(0) / 90); + cairo_set_scaled_font(context, font->platformData().scaledFont()); + if (font->platformData().syntheticOblique()) { + cairo_matrix_t mat = {1, 0, syntheticObliqueSkew, 1, point.x(), point.y()}; + cairo_transform(context, &mat); + } else + cairo_translate(context, point.x(), point.y()); +} + +static void drawGlyphsToContext(cairo_t* context, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs) +{ + cairo_show_glyphs(context, glyphs, numGlyphs); + if (font->syntheticBoldOffset()) { + // We could use cairo_save/cairo_restore here, but two translations are likely faster. + cairo_translate(context, font->syntheticBoldOffset(), 0); + cairo_show_glyphs(context, glyphs, numGlyphs); + cairo_translate(context, -font->syntheticBoldOffset(), 0); + } +} + +static void drawGlyphsShadow(GraphicsContext* graphicsContext, cairo_t* context, const FloatPoint& point, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs) +{ + ContextShadow* shadow = graphicsContext->contextShadow(); + ASSERT(shadow); + + if (!(graphicsContext->textDrawingMode() & TextModeFill) || shadow->m_type == ContextShadow::NoShadow) + return; + + if (!shadow->mustUseContextShadow(graphicsContext)) { + // Optimize non-blurry shadows, by just drawing text without the ContextShadow. + cairo_save(context); + cairo_translate(context, shadow->m_offset.width(), shadow->m_offset.height()); + setSourceRGBAFromColor(context, shadow->m_color); + prepareContextForGlyphDrawing(context, font, point); + cairo_show_glyphs(context, glyphs, numGlyphs); + cairo_restore(context); + return; + } + + cairo_text_extents_t extents; + cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents); + FloatRect fontExtentsRect(point.x(), point.y() - extents.height, extents.width, extents.height); + cairo_t* shadowContext = shadow->beginShadowLayer(graphicsContext, fontExtentsRect); + if (shadowContext) { + prepareContextForGlyphDrawing(shadowContext, font, point); + drawGlyphsToContext(shadowContext, font, glyphs, numGlyphs); + shadow->endShadowLayer(graphicsContext); + } +} + +void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, const FloatPoint& point) const +{ + GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); + + float offset = 0.0f; + for (int i = 0; i < numGlyphs; i++) { + glyphs[i].x = offset; + glyphs[i].y = 0.0f; + offset += glyphBuffer.advanceAt(from + i); + } + + cairo_t* cr = context->platformContext(); + drawGlyphsShadow(context, cr, point, font, glyphs, numGlyphs); + + cairo_save(cr); + prepareContextForGlyphDrawing(cr, font, point); + if (context->textDrawingMode() & TextModeFill) { + if (context->fillGradient()) { + cairo_set_source(cr, context->fillGradient()->platformGradient()); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else if (context->fillPattern()) { + AffineTransform affine; + cairo_pattern_t* pattern = context->fillPattern()->createPlatformPattern(affine); + cairo_set_source(cr, pattern); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + cairo_pattern_destroy(pattern); + } else { + float red, green, blue, alpha; + context->fillColor().getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); + } + drawGlyphsToContext(cr, font, glyphs, numGlyphs); + } + + // Prevent running into a long computation within cairo. If the stroke width is + // twice the size of the width of the text we will not ask cairo to stroke + // the text as even one single stroke would cover the full wdth of the text. + // See https://bugs.webkit.org/show_bug.cgi?id=33759. + if (context->textDrawingMode() & TextModeStroke && context->strokeThickness() < 2 * offset) { + if (context->strokeGradient()) { + cairo_set_source(cr, context->strokeGradient()->platformGradient()); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else if (context->strokePattern()) { + AffineTransform affine; + cairo_pattern_t* pattern = context->strokePattern()->createPlatformPattern(affine); + cairo_set_source(cr, pattern); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + cairo_pattern_destroy(pattern); + } else { + float red, green, blue, alpha; + context->strokeColor().getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); + } + cairo_glyph_path(cr, glyphs, numGlyphs); + cairo_set_line_width(cr, context->strokeThickness()); + cairo_stroke(cr); + } + + cairo_restore(cr); +} + +} diff --git a/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h new file mode 100644 index 0000000..dac31f8 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Alp Toker <alp@atoker.com> + * Copyright (C) 2010 Igalia S.L. + * + * 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 + * along 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 FontCustomPlatformData_h +#define FontCustomPlatformData_h + +#include "FontOrientation.h" +#include "FontRenderingMode.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +typedef struct FT_FaceRec_* FT_Face; +typedef struct _cairo_font_face cairo_font_face_t; + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformData : Noncopyable { +public: + FontCustomPlatformData(FT_Face, SharedBuffer*); + ~FontCustomPlatformData(); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + static bool supportsFormat(const String&); + +private: + FT_Face m_freeTypeFace; + cairo_font_face_t* m_fontFace; +}; + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer); + +} + +#endif diff --git a/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp b/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp new file mode 100644 index 0000000..4e6ed07 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp @@ -0,0 +1,99 @@ +/* + * 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; + } + + switch (m_spreadMethod) { + case SpreadMethodPad: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_PAD); + break; + case SpreadMethodReflect: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_REFLECT); + break; + case SpreadMethodRepeat: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_REPEAT); + break; + } + + cairo_matrix_t matrix = m_gradientSpaceTransformation; + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(m_gradient, &matrix); + + return m_gradient; +} + +void Gradient::setPlatformGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation) +{ + if (m_gradient) { + cairo_matrix_t matrix = gradientSpaceTransformation; + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(m_gradient, &matrix); + } +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + cairo_t* cr = context->platformContext(); + + context->save(); + cairo_set_source(cr, platformGradient()); + cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); + cairo_fill(cr); + context->restore(); +} + +} //namespace diff --git a/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp new file mode 100644 index 0000000..cdbfc57 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -0,0 +1,1149 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org> + * Copyright (C) 2010 Igalia S.L. + * Copyright (C) Research In Motion Limited 2010. 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. + */ + +#include "config.h" +#include "GraphicsContext.h" + +#if PLATFORM(CAIRO) + +#include "AffineTransform.h" +#include "CairoPath.h" +#include "CairoUtilities.h" +#include "ContextShadow.h" +#include "FloatRect.h" +#include "Font.h" +#include "GraphicsContextPlatformPrivateCairo.h" +#include "OwnPtrCairo.h" +#include "IntRect.h" +#include "NotImplemented.h" +#include "Path.h" +#include "Pattern.h" +#include "RefPtrCairo.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 + +using namespace std; + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +namespace WebCore { + +static inline void setPlatformFill(GraphicsContext* context, cairo_t* cr) +{ + cairo_pattern_t* pattern = 0; + cairo_save(cr); + + const GraphicsContextState& state = context->state(); + if (state.fillPattern) { + AffineTransform affine; + pattern = state.fillPattern->createPlatformPattern(affine); + cairo_set_source(cr, pattern); + } else if (state.fillGradient) + cairo_set_source(cr, state.fillGradient->platformGradient()); + else + setSourceRGBAFromColor(cr, context->fillColor()); + cairo_clip_preserve(cr); + cairo_paint_with_alpha(cr, state.globalAlpha); + cairo_restore(cr); + if (pattern) + cairo_pattern_destroy(pattern); +} + +static inline void setPlatformStroke(GraphicsContext* context, cairo_t* cr) +{ + cairo_pattern_t* pattern = 0; + cairo_save(cr); + + const GraphicsContextState& state = context->state(); + if (state.strokePattern) { + AffineTransform affine; + pattern = state.strokePattern->createPlatformPattern(affine); + cairo_set_source(cr, pattern); + } else if (state.strokeGradient) + cairo_set_source(cr, state.strokeGradient->platformGradient()); + else { + Color strokeColor = colorWithOverrideAlpha(context->strokeColor().rgb(), context->strokeColor().alpha() / 255.f * state.globalAlpha); + setSourceRGBAFromColor(cr, strokeColor); + } + if (state.globalAlpha < 1.0f && (state.strokePattern || state.strokeGradient)) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, state.globalAlpha); + cairo_pop_group_to_source(cr); + } + cairo_stroke_preserve(cr); + cairo_restore(cr); + if (pattern) + cairo_pattern_destroy(pattern); +} + +// A fillRect helper +static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const Color& col) +{ + setSourceRGBAFromColor(cr, col); + cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_fill(cr); +} + +static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const FloatPoint* points) +{ + cairo_move_to(context, points[0].x(), points[0].y()); + for (size_t i = 1; i < numPoints; i++) + cairo_line_to(context, points[i].x(), points[i].y()); + cairo_close_path(context); +} + +enum PathDrawingStyle { + Fill = 1, + Stroke = 2, + FillAndStroke = Fill + Stroke +}; + +static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle drawingStyle) +{ + ContextShadow* shadow = context->contextShadow(); + ASSERT(shadow); + if (shadow->m_type == ContextShadow::NoShadow) + return; + + // Calculate the extents of the rendered solid paths. + cairo_t* cairoContext = context->platformContext(); + OwnPtr<cairo_path_t> path(cairo_copy_path(cairoContext)); + + FloatRect solidFigureExtents; + double x0 = 0; + double x1 = 0; + double y0 = 0; + double y1 = 0; + if (drawingStyle & Stroke) { + cairo_stroke_extents(cairoContext, &x0, &y0, &x1, &y1); + solidFigureExtents = FloatRect(x0, y0, x1 - x0, y1 - y0); + } + if (drawingStyle & Fill) { + cairo_fill_extents(cairoContext, &x0, &y0, &x1, &y1); + FloatRect fillExtents(x0, y0, x1 - x0, y1 - y0); + solidFigureExtents.unite(fillExtents); + } + + cairo_t* shadowContext = shadow->beginShadowLayer(context, solidFigureExtents); + if (!shadowContext) + return; + + // It's important to copy the context properties to the new shadow + // context to preserve things such as the fill rule and stroke width. + copyContextProperties(cairoContext, shadowContext); + cairo_append_path(shadowContext, path.get()); + + if (drawingStyle & Fill) + setPlatformFill(context, shadowContext); + if (drawingStyle & Stroke) + setPlatformStroke(context, shadowContext); + + shadow->endShadowLayer(context); +} + +static void fillCurrentCairoPath(GraphicsContext* context, cairo_t* cairoContext) +{ + cairo_set_fill_rule(cairoContext, context->fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); + drawPathShadow(context, Fill); + + setPlatformFill(context, cairoContext); + cairo_new_path(cairoContext); +} + +static void strokeCurrentCairoPath(GraphicsContext* context, cairo_t* cairoContext) +{ + drawPathShadow(context, Stroke); + setPlatformStroke(context, cairoContext); + cairo_new_path(cairoContext); +} + +void GraphicsContext::platformInit(PlatformGraphicsContext* cr) +{ + m_data = new GraphicsContextPlatformPrivate; + m_data->cr = cairo_reference(cr); + m_data->syncContext(cr); + setPaintingDisabled(!cr); +} + +void GraphicsContext::platformDestroy() +{ + delete m_data; +} + +AffineTransform GraphicsContext::getCTM() const +{ + cairo_t* cr = platformContext(); + cairo_matrix_t m; + cairo_get_matrix(cr, &m); + return AffineTransform(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); +} + +cairo_t* GraphicsContext::platformContext() const +{ + return m_data->cr; +} + +void GraphicsContext::savePlatformState() +{ + cairo_save(m_data->cr); + m_data->save(); + m_data->shadowStack.append(m_data->shadow); +} + +void GraphicsContext::restorePlatformState() +{ + cairo_restore(m_data->cr); + m_data->restore(); + + if (m_data->shadowStack.isEmpty()) + m_data->shadow = ContextShadow(); + else { + m_data->shadow = m_data->shadowStack.last(); + m_data->shadowStack.removeLast(); + } +} + +// 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) { + setSourceRGBAFromColor(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); +} + +// 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; + } + + setSourceRGBAFromColor(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); + 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()) { + setSourceRGBAFromColor(cr, fillColor()); + cairo_fill_preserve(cr); + } + + if (strokeStyle() != NoStroke) { + setSourceRGBAFromColor(cr, strokeColor()); + cairo_set_line_width(cr, strokeThickness()); + cairo_stroke(cr); + } else + 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; + } + + setSourceRGBAFromColor(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); + 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); + addConvexPolygonToContext(cr, npoints, points); + + if (fillColor().alpha()) { + setSourceRGBAFromColor(cr, fillColor()); + cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_fill_preserve(cr); + } + + if (strokeStyle() != NoStroke) { + setSourceRGBAFromColor(cr, strokeColor()); + cairo_set_line_width(cr, strokeThickness()); + cairo_stroke(cr); + } else + cairo_new_path(cr); + + cairo_restore(cr); +} + +void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) +{ + if (paintingDisabled()) + return; + + if (numPoints <= 1) + return; + + cairo_t* cr = m_data->cr; + + cairo_new_path(cr); + cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); + cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr); + + cairo_set_antialias(cr, antialiased ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); + cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); + addConvexPolygonToContext(cr, numPoints, points); + cairo_clip(cr); + + cairo_set_antialias(cr, savedAntialiasRule); + cairo_set_fill_rule(cr, savedFillRule); +} + +void GraphicsContext::fillPath(const Path& path) +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + setPathOnCairoContext(cr, path.platformPath()->context()); + fillCurrentCairoPath(this, cr); +} + +void GraphicsContext::strokePath(const Path& path) +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + setPathOnCairoContext(cr, path.platformPath()->context()); + strokeCurrentCairoPath(this, cr); +} + +void GraphicsContext::fillRect(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()); + fillCurrentCairoPath(this, cr); + cairo_restore(cr); +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace) +{ + if (paintingDisabled()) + return; + + if (hasShadow()) + m_data->shadow.drawRectShadow(this, enclosingIntRect(rect)); + + 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::clipPath(const Path& path, WindRule clipRule) +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + setPathOnCairoContext(cr, path.platformPath()->context()); + cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); + cairo_clip(cr); +} + +static inline void adjustFocusRingColor(Color& color) +{ +#if !PLATFORM(GTK) + // Force the alpha to 50%. This matches what the Mac does with outline rings. + color.setRGB(makeRGBA(color.red(), color.green(), color.blue(), 127)); +#endif +} + +static inline void adjustFocusRingLineWidth(int& width) +{ +#if PLATFORM(GTK) + width = 2; +#endif +} + +static inline StrokeStyle focusRingStrokeStyle() +{ +#if PLATFORM(GTK) + return DottedStroke; +#else + return SolidStroke; +#endif +} + +void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset */, const Color& color) +{ + // FIXME: We should draw paths that describe a rectangle with rounded corners + // so as to be consistent with how we draw rectangular focus rings. + Color ringColor = color; + adjustFocusRingColor(ringColor); + adjustFocusRingLineWidth(width); + + cairo_t* cr = m_data->cr; + cairo_save(cr); + appendWebCorePathToCairoContext(cr, path); + setSourceRGBAFromColor(cr, ringColor); + cairo_set_line_width(cr, width); + setPlatformStrokeStyle(focusRingStrokeStyle()); + cairo_stroke(cr); + cairo_restore(cr); +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color) +{ + if (paintingDisabled()) + return; + + unsigned rectCount = rects.size(); + + cairo_t* cr = m_data->cr; + cairo_save(cr); + cairo_push_group(cr); + cairo_new_path(cr); + +#if PLATFORM(GTK) +#ifdef GTK_API_VERSION_2 + GdkRegion* reg = gdk_region_new(); +#else + cairo_region_t* reg = cairo_region_create(); +#endif + + for (unsigned i = 0; i < rectCount; i++) { +#ifdef GTK_API_VERSION_2 + GdkRectangle rect = rects[i]; + gdk_region_union_with_rect(reg, &rect); +#else + cairo_rectangle_int_t rect = rects[i]; + cairo_region_union_rectangle(reg, &rect); +#endif + } + gdk_cairo_region(cr, reg); +#ifdef GTK_API_VERSION_2 + gdk_region_destroy(reg); +#else + cairo_region_destroy(reg); +#endif +#else + int radius = (width - 1) / 2; + Path path; + for (unsigned i = 0; i < rectCount; ++i) { + if (i > 0) + path.clear(); + path.addRoundedRect(rects[i], FloatSize(radius, radius)); + appendWebCorePathToCairoContext(cr, path); + } +#endif + Color ringColor = color; + adjustFocusRingColor(ringColor); + adjustFocusRingLineWidth(width); + setSourceRGBAFromColor(cr, ringColor); + cairo_set_line_width(cr, width); + setPlatformStrokeStyle(focusRingStrokeStyle()); + + 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; + + IntPoint endPoint = origin + IntSize(width, 0); + drawLine(origin, endPoint); +} + +#if !PLATFORM(GTK) +#include "DrawErrorUnderline.h" +#endif + +void GraphicsContext::drawLineForTextChecking(const IntPoint& origin, int width, TextCheckingLineStyle style) +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + cairo_save(cr); + + 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 + pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness); +#else + drawErrorUnderline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness); +#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); +} + +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, 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::setPlatformStrokeThickness(float strokeThickness) +{ + if (paintingDisabled()) + return; + + cairo_set_line_width(m_data->cr, strokeThickness); +} + +void GraphicsContext::setPlatformStrokeStyle(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 = cairo_matrix_t(transform); + cairo_transform(cr, &matrix); + m_data->concatCTM(transform); +} + +void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + clip(rect); + + Path p; + FloatRect r(rect); + // Add outer ellipse + p.addEllipse(r); + // Add inner ellipse + r.inflate(-thickness); + p.addEllipse(r); + appendWebCorePathToCairoContext(cr, p); + + 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::setPlatformShadow(FloatSize const& size, float blur, Color const& color, ColorSpace) +{ + // Cairo doesn't support shadows natively, they are drawn manually in the draw* functions + if (m_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_state.shadowOffset = FloatSize(size.width(), -size.height()); + m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), -size.height())); + } else + m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), size.height())); + + m_data->shadow.setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms); +} + +ContextShadow* GraphicsContext::contextShadow() +{ + return &m_data->shadow; +} + +void GraphicsContext::clearPlatformShadow() +{ + m_data->shadow.clear(); +} + +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); + strokeCurrentCairoPath(this, cr); + 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_state.globalAlpha = alpha; +} + +float GraphicsContext::getAlpha() +{ + return m_state.globalAlpha; +} + +void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) +{ + if (paintingDisabled()) + return; + + cairo_set_operator(m_data->cr, toCairoOperator(op)); +} + +void GraphicsContext::clip(const Path& path) +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + OwnPtr<cairo_path_t> p(cairo_copy_path(path.platformPath()->context())); + cairo_append_path(cr, p.get()); + 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::canvasClip(const Path& path) +{ + clip(path); +} + +void GraphicsContext::clipOut(const Path& path) +{ + if (paintingDisabled()) + return; + + 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); + appendWebCorePathToCairoContext(cr, 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); +} + +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; + + 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); + 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); +} + +static inline FloatPoint getPhase(const FloatRect& dest, const FloatRect& tile) +{ + FloatPoint phase = dest.location(); + phase.move(-tile.x(), -tile.y()); + + return phase; +} + +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; + + if (hasShadow()) + m_data->shadow.drawRectShadow(this, r, topLeft, topRight, bottomLeft, bottomRight); + + cairo_t* cr = m_data->cr; + cairo_save(cr); + Path path; + path.addRoundedRect(r, topLeft, topRight, bottomLeft, bottomRight); + appendWebCorePathToCairoContext(cr, path); + setSourceRGBAFromColor(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; +} + +GdkWindow* GraphicsContext::gdkWindow() const +{ + if (!m_data->expose) + return 0; + + return m_data->expose->window; +} +#endif + +void GraphicsContext::setPlatformShouldAntialias(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/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h new file mode 100644 index 0000000..5602b6c --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -0,0 +1,113 @@ +/* + * 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 "ContextShadow.h" +#include <cairo.h> +#include <math.h> +#include <stdio.h> +#include <wtf/MathExtras.h> + +#if PLATFORM(GTK) +#include <pango/pango.h> +typedef struct _GdkExposeEvent GdkExposeEvent; +#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) + , m_shouldIncludeChildWindows(false) +#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 flush(); + void clip(const FloatRect&); + void clip(const Path&); + void scale(const FloatSize&); + void rotate(float); + void translate(float, float); + void concatCTM(const AffineTransform&); + void concatCTM(const TransformationMatrix&); + void beginTransparencyLayer() { m_transparencyCount++; } + void endTransparencyLayer() { m_transparencyCount--; } + void syncContext(PlatformGraphicsContext* cr); +#else + // On everything else, we do nothing. + void save() {} + void restore() {} + void flush() {} + void clip(const FloatRect&) {} + void clip(const Path&) {} + void scale(const FloatSize&) {} + void rotate(float) {} + void translate(float, float) {} + void concatCTM(const AffineTransform&) {} + void concatCTM(const TransformationMatrix&) {} + void beginTransparencyLayer() {} + void endTransparencyLayer() {} + void syncContext(PlatformGraphicsContext* cr) {} +#endif + + cairo_t* cr; + Vector<float> layers; + + ContextShadow shadow; + Vector<ContextShadow> shadowStack; + +#if PLATFORM(GTK) + GdkEventExpose* expose; +#elif PLATFORM(WIN) + HDC m_hdc; + unsigned m_transparencyCount; + bool m_shouldIncludeChildWindows; +#endif +}; + +} // namespace WebCore + diff --git a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp new file mode 100644 index 0000000..ac5da3d --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2007 Holger Hans Peter Freyther <zecke@selfish.org> + * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. 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. + */ + +#include "config.h" +#include "ImageBuffer.h" + +#include "Base64.h" +#include "BitmapImage.h" +#include "Color.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; + +// Cairo doesn't provide a way to copy a cairo_surface_t. +// See http://lists.cairographics.org/archives/cairo/2007-June/010877.html +// Once cairo provides the way, use the function instead of this. +static inline cairo_surface_t* copySurface(cairo_surface_t* surface) +{ + cairo_format_t format = cairo_image_surface_get_format(surface); + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + cairo_surface_t* newsurface = cairo_image_surface_create(format, width, height); + + cairo_t* cr = cairo_create(newsurface); + cairo_set_source_surface(cr, surface, 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_destroy(cr); + + return newsurface; +} + +namespace WebCore { + +ImageBufferData::ImageBufferData(const IntSize& size) + : m_surface(0) +{ +} + +ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, 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(); +} + +bool ImageBuffer::drawsUsingCopy() const +{ + return false; +} + +PassRefPtr<Image> ImageBuffer::copyImage() const +{ + // BitmapImage will release the passed in surface on destruction + return BitmapImage::create(copySurface(m_data.m_surface)); +} + +void ImageBuffer::clip(GraphicsContext*, const FloatRect&) const +{ + notImplemented(); + // See https://bugs.webkit.org/show_bug.cgi?id=23526 for why this is unimplemented. +} + +void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, + CompositeOperator op , bool useLowQualityScale) +{ + // BitmapImage will release the passed in surface on destruction + RefPtr<Image> image = BitmapImage::create(cairo_surface_reference(m_data.m_surface)); + context->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); +} + +void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) +{ + // BitmapImage will release the passed in surface on destruction + RefPtr<Image> image = BitmapImage::create(cairo_surface_reference(m_data.m_surface)); + image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); +} + +void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) +{ + ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); + + unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface); + int stride = cairo_image_surface_get_stride(m_data.m_surface); + for (int y = 0; y < m_size.height(); ++y) { + unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * y); + for (int x = 0; x < m_size.width(); x++) { + unsigned* pixel = row + x; + Color pixelColor = colorFromPremultipliedARGB(*pixel); + pixelColor = Color(lookUpTable[pixelColor.red()], + lookUpTable[pixelColor.green()], + lookUpTable[pixelColor.blue()], + pixelColor.alpha()); + *pixel = premultipliedARGBFromColor(pixelColor); + } + } + cairo_surface_mark_dirty_rectangle (m_data.m_surface, 0, 0, m_size.width(), m_size.height()); +} + +template <Multiply multiplied> +PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size) +{ + ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); + + RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); + unsigned char* dataSrc = cairo_image_surface_get_data(data.m_surface); + unsigned char* dataDst = result->data(); + + if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height()) + memset(dataDst, 0, result->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.right(); + if (endx > size.width()) + endx = size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.bottom(); + if (endy > size.height()) + endy = size.height(); + int numRows = endy - originy; + + int stride = cairo_image_surface_get_stride(data.m_surface); + unsigned destBytesPerRow = 4 * rect.width(); + + unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * (y + originy)); + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + unsigned* pixel = row + x + originx; + Color pixelColor; + if (multiplied == Unmultiplied) + pixelColor = colorFromPremultipliedARGB(*pixel); + else + pixelColor = Color(*pixel); + destRows[basex] = pixelColor.red(); + destRows[basex + 1] = pixelColor.green(); + destRows[basex + 2] = pixelColor.blue(); + destRows[basex + 3] = pixelColor.alpha(); + } + destRows += destBytesPerRow; + } + + return result.release(); +} + +PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const +{ + return getImageData<Unmultiplied>(rect, m_data, m_size); +} + +PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const +{ + return getImageData<Premultiplied>(rect, m_data, m_size); +} + +template <Multiply multiplied> +void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size) +{ + ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); + + unsigned char* dataDst = cairo_image_surface_get_data(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 < size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * sourceSize.width(); + int stride = cairo_image_surface_get_stride(data.m_surface); + + unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4; + for (int y = 0; y < numRows; ++y) { + unsigned* row = reinterpret_cast<unsigned*>(dataDst + stride * (y + desty)); + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + unsigned* pixel = row + x + destx; + Color pixelColor(srcRows[basex], + srcRows[basex + 1], + srcRows[basex + 2], + srcRows[basex + 3]); + if (multiplied == Unmultiplied) + *pixel = premultipliedARGBFromColor(pixelColor); + else + *pixel = pixelColor.rgb(); + } + srcRows += srcBytesPerRow; + } + cairo_surface_mark_dirty_rectangle (data.m_surface, + destx, desty, + numColumns, numRows); +} + +void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) +{ + putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, m_data, m_size); +} + +void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) +{ + putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, m_data, m_size); +} + +#if !PLATFORM(GTK) +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 double*) 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()); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/cairo/ImageBufferData.h b/Source/WebCore/platform/graphics/cairo/ImageBufferData.h new file mode 100644 index 0000000..49f15df --- /dev/null +++ b/Source/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/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp b/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp new file mode 100644 index 0000000..e51d65a --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2009 Dirk Schulze <krit@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 "BitmapImage.h" + +#if PLATFORM(CAIRO) + +#include "AffineTransform.h" +#include "CairoUtilities.h" +#include "Color.h" +#include "ContextShadow.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "ImageObserver.h" +#include "RefPtrCairo.h" +#include <cairo.h> +#include <math.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +bool FrameData::clear(bool clearMetadata) +{ + if (clearMetadata) + m_haveMetadata = false; + + if (m_frame) { + cairo_surface_destroy(m_frame); + m_frame = 0; + return true; + } + return false; +} + +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_checkedForSolidColor(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, ColorSpace styleColorSpace, CompositeOperator op) +{ + FloatRect srcRect(src); + FloatRect dstRect(dst); + + if (dstRect.width() == 0.0f || dstRect.height() == 0.0f || + srcRect.width() == 0.0f || srcRect.height() == 0.0f) + return; + + startAnimation(); + + cairo_surface_t* image = frameAtIndex(m_currentFrame); + if (!image) // If it's too early we won't have an image yet. + return; + + if (mayFillWithSolidColor()) { + fillWithSolidColor(context, dstRect, solidColor(), styleColorSpace, op); + return; + } + + IntSize selfSize = size(); + + cairo_t* cr = context->platformContext(); + context->save(); + + // 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); + + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); + + 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); + + ContextShadow* shadow = context->contextShadow(); + ASSERT(shadow); + if (shadow->m_type != ContextShadow::NoShadow) { + cairo_t* shadowContext = shadow->beginShadowLayer(context, dstRect); + if (shadowContext) { + cairo_translate(shadowContext, dstRect.x(), dstRect.y()); + cairo_set_source(shadowContext, pattern); + cairo_rectangle(shadowContext, 0, 0, dstRect.width(), dstRect.height()); + cairo_fill(shadowContext); + shadow->endShadowLayer(context); + } + } + + // 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()); + + context->restore(); + + if (imageObserver()) + imageObserver()->didDraw(this); +} + +void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const AffineTransform& patternTransform, + const FloatPoint& phase, ColorSpace colorSpace, 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(); + + drawPatternToCairoContext(cr, image, size(), tileRect, patternTransform, phase, toCairoOperator(op), destRect); + + if (imageObserver()) + imageObserver()->didDraw(this); +} + +void BitmapImage::checkForSolidColor() +{ + m_isSolidColor = false; + m_checkedForSolidColor = true; + + if (frameCount() > 1) + return; + + cairo_surface_t* frameSurface = frameAtIndex(0); + if (!frameSurface) + return; + + ASSERT(cairo_surface_get_type(frameSurface) == CAIRO_SURFACE_TYPE_IMAGE); + + int width = cairo_image_surface_get_width(frameSurface); + int height = cairo_image_surface_get_height(frameSurface); + + if (width != 1 || height != 1) + return; + + unsigned* pixelColor = reinterpret_cast<unsigned*>(cairo_image_surface_get_data(frameSurface)); + m_solidColor = colorFromPremultipliedARGB(*pixelColor); + + m_isSolidColor = true; +} + +} + +#endif // PLATFORM(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp b/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp new file mode 100644 index 0000000..94f6809 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "OwnPtrCairo.h" + +#if defined(USE_FREETYPE) +#include <cairo-ft.h> +#include <fontconfig/fcfreetype.h> +#endif + +#include <cairo.h> + +namespace WTF { + +#if defined(USE_FREETYPE) +template <> void deleteOwnedPtr<FcObjectSet>(FcObjectSet* ptr) +{ + if (ptr) + FcObjectSetDestroy(ptr); +} + +template <> void deleteOwnedPtr<FcFontSet>(FcFontSet* ptr) +{ + if (ptr) + FcFontSetDestroy(ptr); +} +#endif + +template <> void deleteOwnedPtr<cairo_path_t>(cairo_path_t* ptr) +{ + if (ptr) + cairo_path_destroy(ptr); +} + +} // namespace WTF diff --git a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h b/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h new file mode 100644 index 0000000..035d80e --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef OwnPtrCairo_h +#define OwnPtrCairo_h + +#include "OwnPtr.h" + +#if defined(USE_FREETYPE) +typedef struct _FcObjectSet FcObjectSet; +typedef struct _FcFontSet FcFontSet; +#endif + +typedef struct cairo_path cairo_path_t; + +namespace WTF { + +#if defined(USE_FREETYPE) +template <> void deleteOwnedPtr<FcObjectSet>(FcObjectSet*); +template <> void deleteOwnedPtr<FcFontSet>(FcFontSet*); +#endif + +template <> void deleteOwnedPtr<cairo_path_t>(cairo_path_t*); + +} // namespace WTF + +#endif diff --git a/Source/WebCore/platform/graphics/cairo/PathCairo.cpp b/Source/WebCore/platform/graphics/cairo/PathCairo.cpp new file mode 100644 index 0000000..03f1d10 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/PathCairo.cpp @@ -0,0 +1,342 @@ +/* + 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> + 2008 Dirk Schulze <krit@webkit.org> + + 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 "GraphicsContext.h" +#include "OwnPtrCairo.h" +#include "PlatformString.h" +#include "StrokeStyleApplier.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()->context(); + OwnPtr<cairo_path_t> p(cairo_copy_path(other.platformPath()->context())); + cairo_append_path(cr, p.get()); +} + +Path& Path::operator=(const Path& other) +{ + if (&other == this) + return *this; + + clear(); + cairo_t* cr = platformPath()->context(); + OwnPtr<cairo_path_t> p(cairo_copy_path(other.platformPath()->context())); + cairo_append_path(cr, p.get()); + return *this; +} + +void Path::clear() +{ + cairo_t* cr = platformPath()->context(); + cairo_new_path(cr); +} + +bool Path::isEmpty() const +{ + return !cairo_has_current_point(platformPath()->context()); +} + +bool Path::hasCurrentPoint() const +{ + return !isEmpty(); +} + +FloatPoint Path::currentPoint() const +{ + // FIXME: Is this the correct way? + double x; + double y; + cairo_get_current_point(platformPath()->context(), &x, &y); + return FloatPoint(x, y); +} + +void Path::translate(const FloatSize& p) +{ + cairo_t* cr = platformPath()->context(); + cairo_translate(cr, -p.width(), -p.height()); +} + +void Path::moveTo(const FloatPoint& p) +{ + cairo_t* cr = platformPath()->context(); + cairo_move_to(cr, p.x(), p.y()); +} + +void Path::addLineTo(const FloatPoint& p) +{ + cairo_t* cr = platformPath()->context(); + cairo_line_to(cr, p.x(), p.y()); +} + +void Path::addRect(const FloatRect& rect) +{ + cairo_t* cr = platformPath()->context(); + 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()->context(); + 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()->context(); + 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()->context(); + 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) +{ + if (isEmpty()) + return; + + cairo_t* cr = platformPath()->context(); + + double x0, y0; + cairo_get_current_point(cr, &x0, &y0); + FloatPoint p0(x0, y0); + if ((p1.x() == p0.x() && p1.y() == p0.y()) || (p1.x() == p2.x() && p1.y() == p2.y()) || radius == 0.f) { + cairo_line_to(cr, p1.x(), p1.y()); + return; + } + + FloatPoint p1p0((p0.x() - p1.x()),(p0.y() - p1.y())); + FloatPoint p1p2((p2.x() - p1.x()),(p2.y() - p1.y())); + float p1p0_length = sqrtf(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); + float p1p2_length = sqrtf(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); + + double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); + // all points on a line logic + if (cos_phi == -1) { + cairo_line_to(cr, p1.x(), p1.y()); + return; + } + if (cos_phi == 1) { + // add infinite far away point + unsigned int max_length = 65535; + double factor_max = max_length / p1p0_length; + FloatPoint ep((p0.x() + factor_max * p1p0.x()), (p0.y() + factor_max * p1p0.y())); + cairo_line_to(cr, ep.x(), ep.y()); + return; + } + + float tangent = radius / tan(acos(cos_phi) / 2); + float factor_p1p0 = tangent / p1p0_length; + FloatPoint t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); + + FloatPoint orth_p1p0(p1p0.y(), -p1p0.x()); + float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); + float factor_ra = radius / orth_p1p0_length; + + // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 + double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); + if (cos_alpha < 0.f) + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + + FloatPoint p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); + + // calculate angles for addArc + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + float sa = acos(orth_p1p0.x() / orth_p1p0_length); + if (orth_p1p0.y() < 0.f) + sa = 2 * piDouble - sa; + + // anticlockwise logic + bool anticlockwise = false; + + float factor_p1p2 = tangent / p1p2_length; + FloatPoint t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); + FloatPoint orth_p1p2((t_p1p2.x() - p.x()),(t_p1p2.y() - p.y())); + float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); + float ea = acos(orth_p1p2.x() / orth_p1p2_length); + if (orth_p1p2.y() < 0) + ea = 2 * piDouble - ea; + if ((sa > ea) && ((sa - ea) < piDouble)) + anticlockwise = true; + if ((sa < ea) && ((ea - sa) > piDouble)) + anticlockwise = true; + + cairo_line_to(cr, t_p1p0.x(), t_p1p0.y()); + + addArc(p, radius, sa, ea, anticlockwise); +} + +void Path::addEllipse(const FloatRect& rect) +{ + cairo_t* cr = platformPath()->context(); + 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()->context(); + cairo_close_path(cr); +} + +FloatRect Path::boundingRect() const +{ + cairo_t* cr = platformPath()->context(); + double x0, x1, y0, y1; + cairo_path_extents(cr, &x0, &y0, &x1, &y1); + return FloatRect(x0, y0, x1 - x0, y1 - y0); +} + +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + cairo_t* cr = platformPath()->context(); + if (applier) { + GraphicsContext gc(cr); + applier->strokeStyle(&gc); + } + + double x0, x1, y0, y1; + cairo_stroke_extents(cr, &x0, &y0, &x1, &y1); + return FloatRect(x0, y0, x1 - x0, y1 - y0); +} + +bool Path::contains(const FloatPoint& point, WindRule rule) const +{ + if (!isfinite(point.x()) || !isfinite(point.y())) + return false; + cairo_t* cr = platformPath()->context(); + 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; +} + +bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const +{ + ASSERT(applier); + cairo_t* cr = platformPath()->context(); + GraphicsContext gc(cr); + applier->strokeStyle(&gc); + + return cairo_in_stroke(cr, point.x(), point.y()); +} + +void Path::apply(void* info, PathApplierFunction function) const +{ + cairo_t* cr = platformPath()->context(); + OwnPtr<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; + } + } +} + +void Path::transform(const AffineTransform& trans) +{ + cairo_t* cr = platformPath()->context(); + cairo_matrix_t c_matrix = cairo_matrix_t(trans); + cairo_matrix_invert(&c_matrix); + cairo_transform(cr, &c_matrix); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp b/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp new file mode 100644 index 0000000..b067acc --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp @@ -0,0 +1,54 @@ +/* + * 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&) const +{ + cairo_surface_t* surface = tileImage()->nativeImageForCurrentFrame(); + if (!surface) + return 0; + + cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface); + + // cairo merges patter space and user space itself + cairo_matrix_t matrix = m_patternSpaceTransformation; + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + + if (m_repeatX || m_repeatY) + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + return pattern; +} + +} diff --git a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp new file mode 100644 index 0000000..c8b242c --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "RefPtrCairo.h" + +#include <cairo.h> + +#if defined(USE_FREETYPE) +#include <cairo-ft.h> +#include <fontconfig/fcfreetype.h> +#endif + +namespace WTF { + +template<> void refIfNotNull(cairo_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_reference(ptr); +} + +template<> void derefIfNotNull(cairo_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_destroy(ptr); +} + +template<> void refIfNotNull(cairo_surface_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_surface_reference(ptr); +} + +template<> void derefIfNotNull(cairo_surface_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_surface_destroy(ptr); +} + +template<> void refIfNotNull(cairo_font_face_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_font_face_reference(ptr); +} + +template<> void derefIfNotNull(cairo_font_face_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_font_face_reference(ptr); +} + +template<> void refIfNotNull(cairo_scaled_font_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_scaled_font_reference(ptr); +} + +template<> void derefIfNotNull(cairo_scaled_font_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_scaled_font_destroy(ptr); +} + +template<> void refIfNotNull(cairo_pattern_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_pattern_reference(ptr); +} + +template<> void derefIfNotNull(cairo_pattern_t* ptr) +{ + if (LIKELY(ptr != 0)) + cairo_pattern_destroy(ptr); +} + +#if defined(USE_FREETYPE) +template<> void refIfNotNull(FcPattern* ptr) +{ + if (LIKELY(ptr != 0)) + FcPatternReference(ptr); +} + +template<> void derefIfNotNull(FcPattern* ptr) +{ + if (LIKELY(ptr != 0)) + FcPatternDestroy(ptr); +} + +#endif + +} diff --git a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h new file mode 100644 index 0000000..204d1e3 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * 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 + * along 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 RefPtrCairo_h +#define RefPtrCairo_h + +#include "RefPtr.h" + +typedef struct _cairo cairo_t; +typedef struct _cairo_surface cairo_surface_t; +typedef struct _cairo_font_face cairo_font_face_t; +typedef struct _cairo_scaled_font cairo_scaled_font_t; +typedef struct _cairo_pattern cairo_pattern_t; + +#if defined(USE_FREETYPE) +typedef struct _FcPattern FcPattern; +#endif + +namespace WTF { + +template<> void refIfNotNull(cairo_t* ptr); +template<> void derefIfNotNull(cairo_t* ptr); + +template<> void refIfNotNull(cairo_surface_t* ptr); +template<> void derefIfNotNull(cairo_surface_t* ptr); + +template<> void refIfNotNull(cairo_font_face_t* ptr); +template<> void derefIfNotNull(cairo_font_face_t* ptr); + +template<> void refIfNotNull(cairo_scaled_font_t* ptr); +template<> void derefIfNotNull(cairo_scaled_font_t* ptr); + +template<> void refIfNotNull(cairo_pattern_t*); +template<> void derefIfNotNull(cairo_pattern_t*); + +#if defined(USE_FREETYPE) +template<> void refIfNotNull(FcPattern* ptr); +template<> void derefIfNotNull(FcPattern* ptr); +#endif + +} + +#endif // RefPtrCairo_h diff --git a/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp b/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp new file mode 100644 index 0000000..c73dd02 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp @@ -0,0 +1,66 @@ +/* + * + * 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 "TransformationMatrix.h" + +#include "IntRect.h" +#include "FloatRect.h" + +#include <cairo.h> + +namespace WebCore { + +TransformationMatrix::operator cairo_matrix_t() const +{ + cairo_matrix_t m; + + cairo_matrix_init (&m, + a(), + b(), + c(), + d(), + e(), + f()); + return m; +} + +AffineTransform::operator cairo_matrix_t() const +{ + cairo_matrix_t m; + + cairo_matrix_init (&m, + a(), + b(), + c(), + d(), + e(), + f()); + return m; +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/cairo/rgb24-hacks.txt b/Source/WebCore/platform/graphics/cairo/rgb24-hacks.txt new file mode 100644 index 0000000..59f8070 --- /dev/null +++ b/Source/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/Source/WebCore/platform/graphics/cairo/scale-removal.txt b/Source/WebCore/platform/graphics/cairo/scale-removal.txt new file mode 100644 index 0000000..47c0d70 --- /dev/null +++ b/Source/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; |