1 /*
2 * Copyright (C) 2006 Apple Computer, 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 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "config.h"
31 #include "Font.h"
32
33 #include "AffineTransform.h"
34 #include "CairoUtilities.h"
35 #include "ContextShadow.h"
36 #include "GlyphBuffer.h"
37 #include "Gradient.h"
38 #include "GraphicsContext.h"
39 #include "PlatformContextCairo.h"
40 #include "ImageBuffer.h"
41 #include "Pattern.h"
42 #include "SimpleFontData.h"
43
44 namespace WebCore {
45
prepareContextForGlyphDrawing(cairo_t * context,const SimpleFontData * font,const FloatPoint & point)46 static void prepareContextForGlyphDrawing(cairo_t* context, const SimpleFontData* font, const FloatPoint& point)
47 {
48 static const float syntheticObliqueSkew = -tanf(14 * acosf(0) / 90);
49 cairo_set_scaled_font(context, font->platformData().scaledFont());
50 if (font->platformData().syntheticOblique()) {
51 cairo_matrix_t mat = {1, 0, syntheticObliqueSkew, 1, point.x(), point.y()};
52 cairo_transform(context, &mat);
53 } else
54 cairo_translate(context, point.x(), point.y());
55 }
56
drawGlyphsToContext(cairo_t * context,const SimpleFontData * font,GlyphBufferGlyph * glyphs,int numGlyphs)57 static void drawGlyphsToContext(cairo_t* context, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs)
58 {
59 cairo_show_glyphs(context, glyphs, numGlyphs);
60 if (font->syntheticBoldOffset()) {
61 // We could use cairo_save/cairo_restore here, but two translations are likely faster.
62 cairo_translate(context, font->syntheticBoldOffset(), 0);
63 cairo_show_glyphs(context, glyphs, numGlyphs);
64 cairo_translate(context, -font->syntheticBoldOffset(), 0);
65 }
66 }
67
drawGlyphsShadow(GraphicsContext * graphicsContext,const FloatPoint & point,const SimpleFontData * font,GlyphBufferGlyph * glyphs,int numGlyphs)68 static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint& point, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs)
69 {
70 ContextShadow* shadow = graphicsContext->contextShadow();
71 ASSERT(shadow);
72
73 if (!(graphicsContext->textDrawingMode() & TextModeFill) || shadow->m_type == ContextShadow::NoShadow)
74 return;
75
76 if (!shadow->mustUseContextShadow(graphicsContext)) {
77 // Optimize non-blurry shadows, by just drawing text without the ContextShadow.
78 cairo_t* context = graphicsContext->platformContext()->cr();
79 cairo_save(context);
80 cairo_translate(context, shadow->m_offset.width(), shadow->m_offset.height());
81 setSourceRGBAFromColor(context, shadow->m_color);
82 prepareContextForGlyphDrawing(context, font, point);
83 cairo_show_glyphs(context, glyphs, numGlyphs);
84 cairo_restore(context);
85 return;
86 }
87
88 cairo_text_extents_t extents;
89 cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents);
90 FloatRect fontExtentsRect(point.x(), point.y() - extents.height, extents.width, extents.height);
91 cairo_t* shadowContext = shadow->beginShadowLayer(graphicsContext, fontExtentsRect);
92 if (shadowContext) {
93 prepareContextForGlyphDrawing(shadowContext, font, point);
94 drawGlyphsToContext(shadowContext, font, glyphs, numGlyphs);
95 shadow->endShadowLayer(graphicsContext);
96 }
97 }
98
drawGlyphs(GraphicsContext * context,const SimpleFontData * font,const GlyphBuffer & glyphBuffer,int from,int numGlyphs,const FloatPoint & point) const99 void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
100 int from, int numGlyphs, const FloatPoint& point) const
101 {
102 GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from);
103
104 float offset = 0.0f;
105 for (int i = 0; i < numGlyphs; i++) {
106 glyphs[i].x = offset;
107 glyphs[i].y = 0.0f;
108 offset += glyphBuffer.advanceAt(from + i);
109 }
110
111 PlatformContextCairo* platformContext = context->platformContext();
112 drawGlyphsShadow(context, point, font, glyphs, numGlyphs);
113
114 cairo_t* cr = platformContext->cr();
115 cairo_save(cr);
116 prepareContextForGlyphDrawing(cr, font, point);
117 if (context->textDrawingMode() & TextModeFill) {
118 if (context->fillGradient()) {
119 cairo_set_source(cr, context->fillGradient()->platformGradient());
120 if (context->getAlpha() < 1.0f) {
121 cairo_push_group(cr);
122 cairo_paint_with_alpha(cr, context->getAlpha());
123 cairo_pop_group_to_source(cr);
124 }
125 } else if (context->fillPattern()) {
126 AffineTransform affine;
127 cairo_pattern_t* pattern = context->fillPattern()->createPlatformPattern(affine);
128 cairo_set_source(cr, pattern);
129 if (context->getAlpha() < 1.0f) {
130 cairo_push_group(cr);
131 cairo_paint_with_alpha(cr, context->getAlpha());
132 cairo_pop_group_to_source(cr);
133 }
134 cairo_pattern_destroy(pattern);
135 } else {
136 float red, green, blue, alpha;
137 context->fillColor().getRGBA(red, green, blue, alpha);
138 cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha());
139 }
140 drawGlyphsToContext(cr, font, glyphs, numGlyphs);
141 }
142
143 // Prevent running into a long computation within cairo. If the stroke width is
144 // twice the size of the width of the text we will not ask cairo to stroke
145 // the text as even one single stroke would cover the full wdth of the text.
146 // See https://bugs.webkit.org/show_bug.cgi?id=33759.
147 if (context->textDrawingMode() & TextModeStroke && context->strokeThickness() < 2 * offset) {
148 if (context->strokeGradient()) {
149 cairo_set_source(cr, context->strokeGradient()->platformGradient());
150 if (context->getAlpha() < 1.0f) {
151 cairo_push_group(cr);
152 cairo_paint_with_alpha(cr, context->getAlpha());
153 cairo_pop_group_to_source(cr);
154 }
155 } else if (context->strokePattern()) {
156 AffineTransform affine;
157 cairo_pattern_t* pattern = context->strokePattern()->createPlatformPattern(affine);
158 cairo_set_source(cr, pattern);
159 if (context->getAlpha() < 1.0f) {
160 cairo_push_group(cr);
161 cairo_paint_with_alpha(cr, context->getAlpha());
162 cairo_pop_group_to_source(cr);
163 }
164 cairo_pattern_destroy(pattern);
165 } else {
166 float red, green, blue, alpha;
167 context->strokeColor().getRGBA(red, green, blue, alpha);
168 cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha());
169 }
170 cairo_glyph_path(cr, glyphs, numGlyphs);
171 cairo_set_line_width(cr, context->strokeThickness());
172 cairo_stroke(cr);
173 }
174
175 cairo_restore(cr);
176 }
177
178 }
179