e895f175966294099d9bf0ade26f124483971b36
[WebKit-https.git] / Source / WebCore / platform / graphics / cairo / FontCairo.cpp
1 /*
2  * Copyright (C) 2006 Apple Inc.  All rights reserved.
3  * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
4  * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
5  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
6  * Copyright (C) 2010 Holger Hans Peter Freyther
7  * Copyright (C) 2014 Igalia S.L.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "FontCascade.h"
33
34 #if USE(CAIRO)
35
36 #include "AffineTransform.h"
37 #include "CairoUtilities.h"
38 #include "Font.h"
39 #include "GlyphBuffer.h"
40 #include "Gradient.h"
41 #include "GraphicsContext.h"
42 #include "ImageBuffer.h"
43 #include "Pattern.h"
44 #include "PlatformContextCairo.h"
45 #include "PlatformPathCairo.h"
46 #include "ShadowBlur.h"
47
48 namespace WebCore {
49
50 static void drawGlyphsToContext(cairo_t* context, const Font& font, GlyphBufferGlyph* glyphs, int numGlyphs)
51 {
52     cairo_matrix_t originalTransform;
53     float syntheticBoldOffset = font.syntheticBoldOffset();
54     if (syntheticBoldOffset)
55         cairo_get_matrix(context, &originalTransform);
56
57     cairo_set_scaled_font(context, font.platformData().scaledFont());
58     cairo_show_glyphs(context, glyphs, numGlyphs);
59
60     if (syntheticBoldOffset) {
61         cairo_translate(context, syntheticBoldOffset, 0);
62         cairo_show_glyphs(context, glyphs, numGlyphs);
63     }
64
65     if (syntheticBoldOffset)
66         cairo_set_matrix(context, &originalTransform);
67 }
68
69 static void drawGlyphsShadow(GraphicsContext& graphicsContext, const FloatPoint& point, const Font& font, GlyphBufferGlyph* glyphs, int numGlyphs)
70 {
71     ShadowBlur& shadow = graphicsContext.platformContext()->shadowBlur();
72
73     if (!(graphicsContext.textDrawingMode() & TextModeFill) || shadow.type() == ShadowBlur::NoShadow)
74         return;
75
76     if (!graphicsContext.mustUseShadowBlur()) {
77         // Optimize non-blurry shadows, by just drawing text without the ShadowBlur.
78         cairo_t* context = graphicsContext.platformContext()->cr();
79         cairo_save(context);
80
81         FloatSize shadowOffset(graphicsContext.state().shadowOffset);
82         cairo_translate(context, shadowOffset.width(), shadowOffset.height());
83         setSourceRGBAFromColor(context, graphicsContext.state().shadowColor);
84         drawGlyphsToContext(context, font, glyphs, numGlyphs);
85
86         cairo_restore(context);
87         return;
88     }
89
90     cairo_text_extents_t extents;
91     cairo_scaled_font_glyph_extents(font.platformData().scaledFont(), glyphs, numGlyphs, &extents);
92     FloatRect fontExtentsRect(point.x() + extents.x_bearing, point.y() + extents.y_bearing, extents.width, extents.height);
93
94     if (GraphicsContext* shadowContext = shadow.beginShadowLayer(graphicsContext, fontExtentsRect)) {
95         drawGlyphsToContext(shadowContext->platformContext()->cr(), font, glyphs, numGlyphs);
96         shadow.endShadowLayer(graphicsContext);
97     }
98 }
99
100 void FontCascade::drawGlyphs(GraphicsContext& context, const Font& font, const GlyphBuffer& glyphBuffer,
101     int from, int numGlyphs, const FloatPoint& point, FontSmoothingMode)
102 {
103     if (!font.platformData().size())
104         return;
105
106     GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from));
107
108     float offset = point.x();
109     for (int i = 0; i < numGlyphs; i++) {
110         glyphs[i].x = offset;
111         glyphs[i].y = point.y();
112         offset += glyphBuffer.advanceAt(from + i).width();
113     }
114
115     PlatformContextCairo* platformContext = context.platformContext();
116     drawGlyphsShadow(context, point, font, glyphs, numGlyphs);
117
118     cairo_t* cr = platformContext->cr();
119     cairo_save(cr);
120
121     if (context.textDrawingMode() & TextModeFill) {
122         platformContext->prepareForFilling(context.state(), PlatformContextCairo::AdjustPatternForGlobalAlpha);
123         drawGlyphsToContext(cr, font, glyphs, numGlyphs);
124     }
125
126     // Prevent running into a long computation within cairo. If the stroke width is
127     // twice the size of the width of the text we will not ask cairo to stroke
128     // the text as even one single stroke would cover the full wdth of the text.
129     //  See https://bugs.webkit.org/show_bug.cgi?id=33759.
130     if (context.textDrawingMode() & TextModeStroke && context.strokeThickness() < 2 * offset) {
131         platformContext->prepareForStroking(context.state());
132         cairo_set_line_width(cr, context.strokeThickness());
133
134         // This may disturb the CTM, but we are going to call cairo_restore soon after.
135         cairo_set_scaled_font(cr, font.platformData().scaledFont());
136         cairo_glyph_path(cr, glyphs, numGlyphs);
137         cairo_stroke(cr);
138     }
139
140     cairo_restore(cr);
141 }
142
143 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
144 struct GlyphIterationState {
145     GlyphIterationState(FloatPoint startingPoint, FloatPoint currentPoint, float centerOfLine, float minX, float maxX)
146         : startingPoint(startingPoint)
147         , currentPoint(currentPoint)
148         , centerOfLine(centerOfLine)
149         , minX(minX)
150         , maxX(maxX)
151     {
152     }
153     FloatPoint startingPoint;
154     FloatPoint currentPoint;
155     float centerOfLine;
156     float minX;
157     float maxX;
158 };
159
160 static bool findIntersectionPoint(float y, FloatPoint p1, FloatPoint p2, float& x)
161 {
162     x = p1.x() + (y - p1.y()) * (p2.x() - p1.x()) / (p2.y() - p1.y());
163     return (p1.y() < y && p2.y() > y) || (p1.y() > y && p2.y() < y);
164 }
165
166 static void updateX(GlyphIterationState& state, float x)
167 {
168     state.minX = std::min(state.minX, x);
169     state.maxX = std::max(state.maxX, x);
170 }
171
172 // This function is called by Path::apply and is therefore invoked for each contour in a glyph. This
173 // function models each contours as a straight line and calculates the intersections between each
174 // pseudo-contour and the vertical center of the underline found in GlyphIterationState::centerOfLine.
175 // It keeps track of the leftmost and rightmost intersection in  GlyphIterationState::minX and 
176 // GlyphIterationState::maxX.
177 static void findPathIntersections(GlyphIterationState& state, const PathElement& element)
178 {
179     bool doIntersection = false;
180     FloatPoint point = FloatPoint();
181     switch (element.type) {
182     case PathElementMoveToPoint:
183         state.startingPoint = element.points[0];
184         state.currentPoint = element.points[0];
185         break;
186     case PathElementAddLineToPoint:
187         doIntersection = true;
188         point = element.points[0];
189         break;
190     case PathElementAddQuadCurveToPoint:
191         doIntersection = true;
192         point = element.points[1];
193         break;
194     case PathElementAddCurveToPoint:
195         doIntersection = true;
196         point = element.points[2];
197         break;
198     case PathElementCloseSubpath:
199         doIntersection = true;
200         point = state.startingPoint;
201         break;
202     }
203
204     if (!doIntersection)
205         return;
206
207     float x;
208     if (findIntersectionPoint(state.centerOfLine, state.currentPoint, point, x))
209         updateX(state, x);
210
211     state.currentPoint = point;
212 }
213
214 class CairoGlyphToPathTranslator final : public GlyphToPathTranslator {
215 public:
216     CairoGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
217         : m_index(0)
218         , m_textRun(textRun)
219         , m_glyphBuffer(glyphBuffer)
220         , m_fontData(glyphBuffer.fontAt(m_index))
221         , m_translation(AffineTransform().translate(textOrigin.x(), textOrigin.y()))
222     {
223     }
224
225     bool containsMorePaths() final { return m_index != m_glyphBuffer.size(); }
226     Path path() final;
227     std::pair<float, float> extents() final;
228     GlyphUnderlineType underlineType() final;
229     void advance() final;
230
231 private:
232     int m_index;
233     const TextRun& m_textRun;
234     const GlyphBuffer& m_glyphBuffer;
235     const Font* m_fontData;
236     AffineTransform m_translation;
237 };
238
239 Path CairoGlyphToPathTranslator::path()
240 {
241     Path path;
242     path.ensurePlatformPath();
243
244     cairo_glyph_t cairoGlyph;
245     cairoGlyph.index = m_glyphBuffer.glyphAt(m_index);
246     cairo_set_scaled_font(path.platformPath()->context(), m_fontData->platformData().scaledFont());
247     cairo_glyph_path(path.platformPath()->context(), &cairoGlyph, 1);
248
249     float syntheticBoldOffset = m_fontData->syntheticBoldOffset();
250     if (syntheticBoldOffset) {
251         cairo_translate(path.platformPath()->context(), syntheticBoldOffset, 0);
252         cairo_show_glyphs(path.platformPath()->context(), &cairoGlyph, 1);
253     }
254
255     path.transform(m_translation);
256     return path;
257 }
258
259 std::pair<float, float> CairoGlyphToPathTranslator::extents()
260 {
261     FloatPoint beginning = m_translation.mapPoint(FloatPoint());
262     FloatSize end = m_translation.mapSize(m_glyphBuffer.advanceAt(m_index));
263     return std::make_pair(static_cast<float>(beginning.x()), static_cast<float>(beginning.x() + end.width()));
264 }
265
266 GlyphToPathTranslator::GlyphUnderlineType CairoGlyphToPathTranslator::underlineType()
267 {
268     return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
269 }
270
271 void CairoGlyphToPathTranslator::advance()
272 {
273     GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
274     m_translation = m_translation.translate(advance.width(), advance.height());
275     ++m_index;
276     if (m_index < m_glyphBuffer.size())
277         m_fontData = m_glyphBuffer.fontAt(m_index);
278 }
279
280 DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
281 {
282     if (isLoadingCustomFonts())
283         return DashArray();
284
285     GlyphBuffer glyphBuffer;
286     glyphBuffer.saveOffsetsInString();
287     float deltaX;
288     if (codePath(run) != FontCascade::Complex)
289         deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
290     else
291         deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
292
293     if (!glyphBuffer.size())
294         return DashArray();
295
296     // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778
297     FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
298     CairoGlyphToPathTranslator translator(run, glyphBuffer, origin);
299     DashArray result;
300     for (int index = 0; translator.containsMorePaths(); ++index, translator.advance()) {
301         float centerOfLine = lineExtents.y() + (lineExtents.height() / 2);
302         GlyphIterationState info = GlyphIterationState(FloatPoint(), FloatPoint(), centerOfLine, lineExtents.x() + lineExtents.width(), lineExtents.x());
303         const Font* localFontData = glyphBuffer.fontAt(index);
304         if (!localFontData) {
305             // The advances will get all messed up if we do anything other than bail here.
306             result.clear();
307             break;
308         }
309         switch (translator.underlineType()) {
310         case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: {
311             Path path = translator.path();
312             path.apply([&info](const PathElement& pathElement) {
313                 findPathIntersections(info, pathElement);
314             });
315             if (info.minX < info.maxX) {
316                 result.append(info.minX - lineExtents.x());
317                 result.append(info.maxX - lineExtents.x());
318             }
319             break;
320         }
321         case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: {
322             std::pair<float, float> extents = translator.extents();
323             result.append(extents.first - lineExtents.x());
324             result.append(extents.second - lineExtents.x());
325             break;
326         }
327         case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph:
328             // Nothing to do
329             break;
330         }
331     }
332     return result;
333 }
334 #endif // ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
335
336 } // namespace WebCore
337
338 #endif // USE(CAIRO)