1/* 2 * Copyright (C) 2010 Igalia S.L. 3 * Copyright (C) 2011 ProFUSION embedded systems 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "CairoUtilities.h" 29 30#include "AffineTransform.h" 31#include "Color.h" 32#include "FloatPoint.h" 33#include "FloatRect.h" 34#include "IntRect.h" 35#include "OwnPtrCairo.h" 36#include "Path.h" 37#include "PlatformPathCairo.h" 38#include "RefPtrCairo.h" 39#include <wtf/Assertions.h> 40#include <wtf/Vector.h> 41 42#if ENABLE(ACCELERATED_2D_CANVAS) 43#include <cairo-gl.h> 44#endif 45 46namespace WebCore { 47 48void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr) 49{ 50 cairo_set_antialias(dstCr, cairo_get_antialias(srcCr)); 51 52 size_t dashCount = cairo_get_dash_count(srcCr); 53 Vector<double> dashes(dashCount); 54 55 double offset; 56 cairo_get_dash(srcCr, dashes.data(), &offset); 57 cairo_set_dash(dstCr, dashes.data(), dashCount, offset); 58 cairo_set_line_cap(dstCr, cairo_get_line_cap(srcCr)); 59 cairo_set_line_join(dstCr, cairo_get_line_join(srcCr)); 60 cairo_set_line_width(dstCr, cairo_get_line_width(srcCr)); 61 cairo_set_miter_limit(dstCr, cairo_get_miter_limit(srcCr)); 62 cairo_set_fill_rule(dstCr, cairo_get_fill_rule(srcCr)); 63} 64 65void setSourceRGBAFromColor(cairo_t* context, const Color& color) 66{ 67 float red, green, blue, alpha; 68 color.getRGBA(red, green, blue, alpha); 69 cairo_set_source_rgba(context, red, green, blue, alpha); 70} 71 72void appendPathToCairoContext(cairo_t* to, cairo_t* from) 73{ 74 OwnPtr<cairo_path_t> cairoPath = adoptPtr(cairo_copy_path(from)); 75 cairo_append_path(to, cairoPath.get()); 76} 77 78void setPathOnCairoContext(cairo_t* to, cairo_t* from) 79{ 80 cairo_new_path(to); 81 appendPathToCairoContext(to, from); 82} 83 84void appendWebCorePathToCairoContext(cairo_t* context, const Path& path) 85{ 86 if (path.isEmpty()) 87 return; 88 appendPathToCairoContext(context, path.platformPath()->context()); 89} 90 91void appendRegionToCairoContext(cairo_t* to, const cairo_region_t* region) 92{ 93 if (!region) 94 return; 95 96 const int rectCount = cairo_region_num_rectangles(region); 97 for (int i = 0; i < rectCount; ++i) { 98 cairo_rectangle_int_t rect; 99 cairo_region_get_rectangle(region, i, &rect); 100 cairo_rectangle(to, rect.x, rect.y, rect.width, rect.height); 101 } 102} 103 104cairo_operator_t toCairoOperator(CompositeOperator op) 105{ 106 switch (op) { 107 case CompositeClear: 108 return CAIRO_OPERATOR_CLEAR; 109 case CompositeCopy: 110 return CAIRO_OPERATOR_SOURCE; 111 case CompositeSourceOver: 112 return CAIRO_OPERATOR_OVER; 113 case CompositeSourceIn: 114 return CAIRO_OPERATOR_IN; 115 case CompositeSourceOut: 116 return CAIRO_OPERATOR_OUT; 117 case CompositeSourceAtop: 118 return CAIRO_OPERATOR_ATOP; 119 case CompositeDestinationOver: 120 return CAIRO_OPERATOR_DEST_OVER; 121 case CompositeDestinationIn: 122 return CAIRO_OPERATOR_DEST_IN; 123 case CompositeDestinationOut: 124 return CAIRO_OPERATOR_DEST_OUT; 125 case CompositeDestinationAtop: 126 return CAIRO_OPERATOR_DEST_ATOP; 127 case CompositeXOR: 128 return CAIRO_OPERATOR_XOR; 129 case CompositePlusDarker: 130 return CAIRO_OPERATOR_DARKEN; 131 case CompositePlusLighter: 132 return CAIRO_OPERATOR_ADD; 133 case CompositeDifference: 134 return CAIRO_OPERATOR_DIFFERENCE; 135 default: 136 return CAIRO_OPERATOR_SOURCE; 137 } 138} 139cairo_operator_t toCairoOperator(BlendMode blendOp) 140{ 141 switch (blendOp) { 142 case BlendModeNormal: 143 return CAIRO_OPERATOR_OVER; 144 case BlendModeMultiply: 145 return CAIRO_OPERATOR_MULTIPLY; 146 case BlendModeScreen: 147 return CAIRO_OPERATOR_SCREEN; 148 case BlendModeOverlay: 149 return CAIRO_OPERATOR_OVERLAY; 150 case BlendModeDarken: 151 return CAIRO_OPERATOR_DARKEN; 152 case BlendModeLighten: 153 return CAIRO_OPERATOR_LIGHTEN; 154 case BlendModeColorDodge: 155 return CAIRO_OPERATOR_COLOR_DODGE; 156 case BlendModeColorBurn: 157 return CAIRO_OPERATOR_COLOR_BURN; 158 case BlendModeHardLight: 159 return CAIRO_OPERATOR_HARD_LIGHT; 160 case BlendModeSoftLight: 161 return CAIRO_OPERATOR_SOFT_LIGHT; 162 case BlendModeDifference: 163 return CAIRO_OPERATOR_DIFFERENCE; 164 case BlendModeExclusion: 165 return CAIRO_OPERATOR_EXCLUSION; 166 case BlendModeHue: 167 return CAIRO_OPERATOR_HSL_HUE; 168 case BlendModeSaturation: 169 return CAIRO_OPERATOR_HSL_SATURATION; 170 case BlendModeColor: 171 return CAIRO_OPERATOR_HSL_COLOR; 172 case BlendModeLuminosity: 173 return CAIRO_OPERATOR_HSL_LUMINOSITY; 174 default: 175 return CAIRO_OPERATOR_OVER; 176 } 177} 178 179void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect, 180 const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect) 181{ 182 // Avoid NaN 183 if (!std::isfinite(phase.x()) || !std::isfinite(phase.y())) 184 return; 185 186 cairo_save(cr); 187 188 RefPtr<cairo_surface_t> clippedImageSurface = 0; 189 if (tileRect.size() != imageSize) { 190 IntRect imageRect = enclosingIntRect(tileRect); 191 clippedImageSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, imageRect.width(), imageRect.height())); 192 RefPtr<cairo_t> clippedImageContext = adoptRef(cairo_create(clippedImageSurface.get())); 193 cairo_set_source_surface(clippedImageContext.get(), image, -tileRect.x(), -tileRect.y()); 194 cairo_paint(clippedImageContext.get()); 195 image = clippedImageSurface.get(); 196 } 197 198 cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image); 199 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); 200 201 cairo_matrix_t patternMatrix = cairo_matrix_t(patternTransform); 202 cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()}; 203 cairo_matrix_t combined; 204 cairo_matrix_multiply(&combined, &patternMatrix, &phaseMatrix); 205 cairo_matrix_invert(&combined); 206 cairo_pattern_set_matrix(pattern, &combined); 207 208 cairo_set_operator(cr, op); 209 cairo_set_source(cr, pattern); 210 cairo_pattern_destroy(pattern); 211 cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height()); 212 cairo_fill(cr); 213 214 cairo_restore(cr); 215} 216 217PassRefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t* originalSurface) 218{ 219 // Cairo doesn't provide a way to copy a cairo_surface_t. 220 // See http://lists.cairographics.org/archives/cairo/2007-June/010877.html 221 // Once cairo provides the way, use the function instead of this. 222 IntSize size = cairoSurfaceSize(originalSurface); 223 RefPtr<cairo_surface_t> newSurface = adoptRef(cairo_surface_create_similar(originalSurface, 224 cairo_surface_get_content(originalSurface), size.width(), size.height())); 225 226 RefPtr<cairo_t> cr = adoptRef(cairo_create(newSurface.get())); 227 cairo_set_source_surface(cr.get(), originalSurface, 0, 0); 228 cairo_set_operator(cr.get(), CAIRO_OPERATOR_SOURCE); 229 cairo_paint(cr.get()); 230 return newSurface.release(); 231} 232 233void copyRectFromCairoSurfaceToContext(cairo_surface_t* from, cairo_t* to, const IntSize& offset, const IntRect& rect) 234{ 235 cairo_set_source_surface(to, from, offset.width(), offset.height()); 236 cairo_rectangle(to, rect.x(), rect.y(), rect.width(), rect.height()); 237 cairo_fill(to); 238} 239 240void copyRectFromOneSurfaceToAnother(cairo_surface_t* from, cairo_surface_t* to, const IntSize& sourceOffset, const IntRect& rect, const IntSize& destOffset, cairo_operator_t cairoOperator) 241{ 242 RefPtr<cairo_t> context = adoptRef(cairo_create(to)); 243 cairo_translate(context.get(), destOffset.width(), destOffset.height()); 244 cairo_set_operator(context.get(), cairoOperator); 245 copyRectFromCairoSurfaceToContext(from, context.get(), sourceOffset, rect); 246} 247 248IntSize cairoSurfaceSize(cairo_surface_t* surface) 249{ 250 switch (cairo_surface_get_type(surface)) { 251 case CAIRO_SURFACE_TYPE_IMAGE: 252 return IntSize(cairo_image_surface_get_width(surface), cairo_image_surface_get_height(surface)); 253#if ENABLE(ACCELERATED_2D_CANVAS) 254 case CAIRO_SURFACE_TYPE_GL: 255 return IntSize(cairo_gl_surface_get_width(surface), cairo_gl_surface_get_height(surface)); 256#endif 257 default: 258 ASSERT_NOT_REACHED(); 259 return IntSize(); 260 } 261} 262 263void flipImageSurfaceVertically(cairo_surface_t* surface) 264{ 265 ASSERT(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE); 266 267 IntSize size = cairoSurfaceSize(surface); 268 ASSERT(!size.isEmpty()); 269 270 int stride = cairo_image_surface_get_stride(surface); 271 int halfHeight = size.height() / 2; 272 273 uint8_t* source = static_cast<uint8_t*>(cairo_image_surface_get_data(surface)); 274 std::unique_ptr<uint8_t[]> tmp = std::make_unique<uint8_t[]>(stride); 275 276 for (int i = 0; i < halfHeight; ++i) { 277 uint8_t* top = source + (i * stride); 278 uint8_t* bottom = source + ((size.height()-i-1) * stride); 279 280 memcpy(tmp.get(), top, stride); 281 memcpy(top, bottom, stride); 282 memcpy(bottom, tmp.get(), stride); 283 } 284} 285 286void cairoSurfaceSetDeviceScale(cairo_surface_t* surface, double xScale, double yScale) 287{ 288 // This function was added pretty much simultaneous to when 1.13 was branched. 289#if HAVE(CAIRO_SURFACE_SET_DEVICE_SCALE) 290 cairo_surface_set_device_scale(surface, xScale, yScale); 291#else 292 UNUSED_PARAM(surface); 293 ASSERT_UNUSED(xScale, 1 == xScale); 294 ASSERT_UNUSED(yScale, 1 == yScale); 295#endif 296} 297} // namespace WebCore 298