[WPE] libepoxy headers can use EGL_CAST, which might not be defined by eglplatform.h
[WebKit-https.git] / Source / WebCore / platform / graphics / cairo / CairoUtilities.cpp
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 #if USE(CAIRO)
31
32 #include "AffineTransform.h"
33 #include "Color.h"
34 #include "FloatPoint.h"
35 #include "FloatRect.h"
36 #include "IntRect.h"
37 #include "Path.h"
38 #include "PlatformPathCairo.h"
39 #include "RefPtrCairo.h"
40 #include "Region.h"
41 #include <wtf/Assertions.h>
42 #include <wtf/NeverDestroyed.h>
43 #include <wtf/Vector.h>
44
45 #if ENABLE(ACCELERATED_2D_CANVAS)
46 #if USE(EGL) && USE(LIBEPOXY)
47 #include "EpoxyEGL.h"
48 #endif
49 #include <cairo-gl.h>
50 #endif
51
52 #if OS(WINDOWS)
53 #include <cairo-win32.h>
54 #endif
55
56 namespace WebCore {
57
58 #if USE(FREETYPE) && !PLATFORM(GTK)
59 const cairo_font_options_t* getDefaultCairoFontOptions()
60 {
61     static NeverDestroyed<cairo_font_options_t*> options = cairo_font_options_create();
62     return options;
63 }
64 #endif
65
66 void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr)
67 {
68     cairo_set_antialias(dstCr, cairo_get_antialias(srcCr));
69
70     size_t dashCount = cairo_get_dash_count(srcCr);
71     Vector<double> dashes(dashCount);
72
73     double offset;
74     cairo_get_dash(srcCr, dashes.data(), &offset);
75     cairo_set_dash(dstCr, dashes.data(), dashCount, offset);
76     cairo_set_line_cap(dstCr, cairo_get_line_cap(srcCr));
77     cairo_set_line_join(dstCr, cairo_get_line_join(srcCr));
78     cairo_set_line_width(dstCr, cairo_get_line_width(srcCr));
79     cairo_set_miter_limit(dstCr, cairo_get_miter_limit(srcCr));
80     cairo_set_fill_rule(dstCr, cairo_get_fill_rule(srcCr));
81 }
82
83 void setSourceRGBAFromColor(cairo_t* context, const Color& color)
84 {
85     if (color.isExtended())
86         cairo_set_source_rgba(context, color.asExtended().red(), color.asExtended().green(), color.asExtended().blue(), color.asExtended().alpha());
87     else {
88         float red, green, blue, alpha;
89         color.getRGBA(red, green, blue, alpha);
90         cairo_set_source_rgba(context, red, green, blue, alpha);
91     }
92 }
93
94 void appendPathToCairoContext(cairo_t* to, cairo_t* from)
95 {
96     auto cairoPath = cairo_copy_path(from);
97     cairo_append_path(to, cairoPath);
98     cairo_path_destroy(cairoPath);
99 }
100
101 void setPathOnCairoContext(cairo_t* to, cairo_t* from)
102 {
103     cairo_new_path(to);
104     appendPathToCairoContext(to, from);
105 }
106
107 void appendWebCorePathToCairoContext(cairo_t* context, const Path& path)
108 {
109     if (path.isEmpty())
110         return;
111     appendPathToCairoContext(context, path.platformPath()->context());
112 }
113
114 void appendRegionToCairoContext(cairo_t* to, const cairo_region_t* region)
115 {
116     if (!region)
117         return;
118
119     const int rectCount = cairo_region_num_rectangles(region);
120     for (int i = 0; i < rectCount; ++i) {
121         cairo_rectangle_int_t rect;
122         cairo_region_get_rectangle(region, i, &rect);
123         cairo_rectangle(to, rect.x, rect.y, rect.width, rect.height);
124     }
125 }
126
127 static cairo_operator_t toCairoCompositeOperator(CompositeOperator op)
128 {
129     switch (op) {
130     case CompositeClear:
131         return CAIRO_OPERATOR_CLEAR;
132     case CompositeCopy:
133         return CAIRO_OPERATOR_SOURCE;
134     case CompositeSourceOver:
135         return CAIRO_OPERATOR_OVER;
136     case CompositeSourceIn:
137         return CAIRO_OPERATOR_IN;
138     case CompositeSourceOut:
139         return CAIRO_OPERATOR_OUT;
140     case CompositeSourceAtop:
141         return CAIRO_OPERATOR_ATOP;
142     case CompositeDestinationOver:
143         return CAIRO_OPERATOR_DEST_OVER;
144     case CompositeDestinationIn:
145         return CAIRO_OPERATOR_DEST_IN;
146     case CompositeDestinationOut:
147         return CAIRO_OPERATOR_DEST_OUT;
148     case CompositeDestinationAtop:
149         return CAIRO_OPERATOR_DEST_ATOP;
150     case CompositeXOR:
151         return CAIRO_OPERATOR_XOR;
152     case CompositePlusDarker:
153         return CAIRO_OPERATOR_DARKEN;
154     case CompositePlusLighter:
155         return CAIRO_OPERATOR_ADD;
156     case CompositeDifference:
157         return CAIRO_OPERATOR_DIFFERENCE;
158     default:
159         return CAIRO_OPERATOR_SOURCE;
160     }
161 }
162
163 cairo_operator_t toCairoOperator(CompositeOperator op, BlendMode blendOp)
164 {
165     switch (blendOp) {
166     case BlendModeNormal:
167         return toCairoCompositeOperator(op);
168     case BlendModeMultiply:
169         return CAIRO_OPERATOR_MULTIPLY;
170     case BlendModeScreen:
171         return CAIRO_OPERATOR_SCREEN;
172     case BlendModeOverlay:
173         return CAIRO_OPERATOR_OVERLAY;
174     case BlendModeDarken:
175         return CAIRO_OPERATOR_DARKEN;
176     case BlendModeLighten:
177         return CAIRO_OPERATOR_LIGHTEN;
178     case BlendModeColorDodge:
179         return CAIRO_OPERATOR_COLOR_DODGE;
180     case BlendModeColorBurn:
181         return CAIRO_OPERATOR_COLOR_BURN;
182     case BlendModeHardLight:
183         return CAIRO_OPERATOR_HARD_LIGHT;
184     case BlendModeSoftLight:
185         return CAIRO_OPERATOR_SOFT_LIGHT;
186     case BlendModeDifference:
187         return CAIRO_OPERATOR_DIFFERENCE;
188     case BlendModeExclusion:
189         return CAIRO_OPERATOR_EXCLUSION;
190     case BlendModeHue:
191         return CAIRO_OPERATOR_HSL_HUE;
192     case BlendModeSaturation:
193         return CAIRO_OPERATOR_HSL_SATURATION;
194     case BlendModeColor:
195         return CAIRO_OPERATOR_HSL_COLOR;
196     case BlendModeLuminosity:
197         return CAIRO_OPERATOR_HSL_LUMINOSITY;
198     default:
199         return CAIRO_OPERATOR_OVER;
200     }
201 }
202
203 void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect,
204                                const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect)
205 {
206     // Avoid NaN
207     if (!std::isfinite(phase.x()) || !std::isfinite(phase.y()))
208        return;
209
210     cairo_save(cr);
211
212     RefPtr<cairo_surface_t> clippedImageSurface = 0;
213     if (tileRect.size() != imageSize) {
214         IntRect imageRect = enclosingIntRect(tileRect);
215         clippedImageSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, imageRect.width(), imageRect.height()));
216         RefPtr<cairo_t> clippedImageContext = adoptRef(cairo_create(clippedImageSurface.get()));
217         cairo_set_source_surface(clippedImageContext.get(), image, -tileRect.x(), -tileRect.y());
218         cairo_paint(clippedImageContext.get());
219         image = clippedImageSurface.get();
220     }
221
222     cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
223     cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
224
225     // Due to a limitation in pixman, cairo cannot handle transformation matrices with values bigger than 32768. If the value is
226     // bigger, cairo is not able to paint anything, and this is the reason for the missing backgrounds reported in
227     // https://bugs.webkit.org/show_bug.cgi?id=154283.
228
229     // When drawing a pattern there are 2 matrices that can overflow this limitation, and they are the current transformation
230     // matrix (which translates user space coordinates to coordinates of the output device) and the pattern matrix (which translates
231     // user space coordinates to pattern coordinates). The overflow happens only in the translation components of the matrices.
232
233     // To avoid the problem in the transformation matrix what we do is remove the translation components of the transformation matrix
234     // and perform the translation by moving the destination rectangle instead. For this, we get its translation components (which are in
235     // device coordinates) and divide them by the scale factor to take them to user space coordinates. Then we move the transformation
236     // matrix by the opposite of that amount (which will zero the translation components of the transformation matrix), and move
237     // the destination rectangle by the same amount. We also need to apply the same translation to the pattern matrix, so we get the
238     // same pattern coordinates for the new destination rectangle.
239
240     cairo_matrix_t ctm;
241     cairo_get_matrix(cr, &ctm);
242     double dx = 0, dy = 0;
243     cairo_matrix_transform_point(&ctm, &dx, &dy);
244     double xScale = 1, yScale = 1;
245     cairo_matrix_transform_distance(&ctm, &xScale, &yScale);
246
247     dx = dx / xScale;
248     dy = dy / yScale;
249     cairo_translate(cr, -dx, -dy);
250     FloatRect adjustedDestRect(destRect);
251     adjustedDestRect.move(dx, dy);
252
253     // Regarding the pattern matrix, what we do is reduce the translation component of the matrix taking advantage of the fact that we
254     // are drawing a repeated pattern. This means that, assuming that (w, h) is the size of the pattern, samplig it at (x, y) is the same
255     // than sampling it at (x mod w, y mod h), so we transform the translation component of the pattern matrix in that way.
256
257     cairo_matrix_t patternMatrix = cairo_matrix_t(patternTransform);
258     // dx and dy are added here as well to compensate the previous translation of the destination rectangle.
259     double phaseOffsetX = phase.x() + tileRect.x() * patternTransform.a() + dx;
260     double phaseOffsetY = phase.y() + tileRect.y() * patternTransform.d() + dy;
261     // this is where we perform the (x mod w, y mod h) metioned above, but with floats instead of integers.
262     phaseOffsetX -= std::trunc(phaseOffsetX / (tileRect.width() * patternTransform.a())) * tileRect.width() * patternTransform.a();
263     phaseOffsetY -= std::trunc(phaseOffsetY / (tileRect.height() * patternTransform.d())) * tileRect.height() * patternTransform.d();
264     cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phaseOffsetX, phaseOffsetY};
265     cairo_matrix_t combined;
266     cairo_matrix_multiply(&combined, &patternMatrix, &phaseMatrix);
267     cairo_matrix_invert(&combined);
268     cairo_pattern_set_matrix(pattern, &combined);
269
270     cairo_set_operator(cr, op);
271     cairo_set_source(cr, pattern);
272     cairo_pattern_destroy(pattern);
273     cairo_rectangle(cr, adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.width(), adjustedDestRect.height());
274     cairo_fill(cr);
275
276     cairo_restore(cr);
277 }
278
279 RefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t* originalSurface)
280 {
281     // Cairo doesn't provide a way to copy a cairo_surface_t.
282     // See http://lists.cairographics.org/archives/cairo/2007-June/010877.html
283     // Once cairo provides the way, use the function instead of this.
284     IntSize size = cairoSurfaceSize(originalSurface);
285     RefPtr<cairo_surface_t> newSurface = adoptRef(cairo_surface_create_similar(originalSurface,
286         cairo_surface_get_content(originalSurface), size.width(), size.height()));
287
288     RefPtr<cairo_t> cr = adoptRef(cairo_create(newSurface.get()));
289     cairo_set_source_surface(cr.get(), originalSurface, 0, 0);
290     cairo_set_operator(cr.get(), CAIRO_OPERATOR_SOURCE);
291     cairo_paint(cr.get());
292     return newSurface;
293 }
294
295 void copyRectFromCairoSurfaceToContext(cairo_surface_t* from, cairo_t* to, const IntSize& offset, const IntRect& rect)
296 {
297     cairo_set_source_surface(to, from, offset.width(), offset.height());
298     cairo_rectangle(to, rect.x(), rect.y(), rect.width(), rect.height());
299     cairo_fill(to);
300 }
301
302 void copyRectFromOneSurfaceToAnother(cairo_surface_t* from, cairo_surface_t* to, const IntSize& sourceOffset, const IntRect& rect, const IntSize& destOffset, cairo_operator_t cairoOperator)
303 {
304     RefPtr<cairo_t> context = adoptRef(cairo_create(to));
305     cairo_translate(context.get(), destOffset.width(), destOffset.height());
306     cairo_set_operator(context.get(), cairoOperator);
307     copyRectFromCairoSurfaceToContext(from, context.get(), sourceOffset, rect);
308 }
309
310 IntSize cairoSurfaceSize(cairo_surface_t* surface)
311 {
312     switch (cairo_surface_get_type(surface)) {
313     case CAIRO_SURFACE_TYPE_IMAGE:
314         return IntSize(cairo_image_surface_get_width(surface), cairo_image_surface_get_height(surface));
315 #if ENABLE(ACCELERATED_2D_CANVAS)
316     case CAIRO_SURFACE_TYPE_GL:
317         return IntSize(cairo_gl_surface_get_width(surface), cairo_gl_surface_get_height(surface));
318 #endif
319 #if OS(WINDOWS)
320     case CAIRO_SURFACE_TYPE_WIN32:
321         surface = cairo_win32_surface_get_image(surface);
322         ASSERT(surface);
323         ASSERT(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE);
324         return IntSize(cairo_image_surface_get_width(surface), cairo_image_surface_get_height(surface));
325 #endif
326     default:
327         ASSERT_NOT_REACHED();
328         return IntSize();
329     }
330 }
331
332 void flipImageSurfaceVertically(cairo_surface_t* surface)
333 {
334     ASSERT(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE);
335
336     IntSize size = cairoSurfaceSize(surface);
337     ASSERT(!size.isEmpty());
338
339     int stride = cairo_image_surface_get_stride(surface);
340     int halfHeight = size.height() / 2;
341
342     uint8_t* source = static_cast<uint8_t*>(cairo_image_surface_get_data(surface));
343     std::unique_ptr<uint8_t[]> tmp = std::make_unique<uint8_t[]>(stride);
344
345     for (int i = 0; i < halfHeight; ++i) {
346         uint8_t* top = source + (i * stride);
347         uint8_t* bottom = source + ((size.height()-i-1) * stride);
348
349         memcpy(tmp.get(), top, stride);
350         memcpy(top, bottom, stride);
351         memcpy(bottom, tmp.get(), stride);
352     }
353 }
354
355 void cairoSurfaceSetDeviceScale(cairo_surface_t* surface, double xScale, double yScale)
356 {
357     // This function was added pretty much simultaneous to when 1.13 was branched.
358 #if HAVE(CAIRO_SURFACE_SET_DEVICE_SCALE)
359     cairo_surface_set_device_scale(surface, xScale, yScale);
360 #else
361     UNUSED_PARAM(surface);
362     ASSERT_UNUSED(xScale, 1 == xScale);
363     ASSERT_UNUSED(yScale, 1 == yScale);
364 #endif
365 }
366
367 void cairoSurfaceGetDeviceScale(cairo_surface_t* surface, double& xScale, double& yScale)
368 {
369 #if HAVE(CAIRO_SURFACE_SET_DEVICE_SCALE)
370     cairo_surface_get_device_scale(surface, &xScale, &yScale);
371 #else
372     UNUSED_PARAM(surface);
373     xScale = 1;
374     yScale = 1;
375 #endif
376 }
377
378 RefPtr<cairo_region_t> toCairoRegion(const Region& region)
379 {
380     RefPtr<cairo_region_t> cairoRegion = adoptRef(cairo_region_create());
381     for (const auto& rect : region.rects()) {
382         cairo_rectangle_int_t cairoRect = rect;
383         cairo_region_union_rectangle(cairoRegion.get(), &cairoRect);
384     }
385     return cairoRegion;
386 }
387
388 } // namespace WebCore
389
390 #endif // USE(CAIRO)