b5bff271f2318795e2f1112fb584f6b023ec84e3
[WebKit-https.git] / Source / WebCore / platform / graphics / win / FontCGWin.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2013 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "FontCascade.h"
28
29 #include "AffineTransform.h"
30 #include "FloatConversion.h"
31 #include "Font.h"
32 #include "GlyphBuffer.h"
33 #include "GraphicsContext.h"
34 #include "IntRect.h"
35 #include "UniscribeController.h"
36 #include "WebCoreTextRenderer.h"
37 #include <ApplicationServices/ApplicationServices.h>
38 #include <WebKitSystemInterface/WebKitSystemInterface.h>
39 #include <wtf/MathExtras.h>
40
41 namespace WebCore {
42
43 const int syntheticObliqueAngle = 14;
44
45 static inline CGFloat toCGFloat(FIXED f)
46 {
47     return f.value + f.fract / CGFloat(65536.0);
48 }
49
50 static CGPathRef createPathForGlyph(HDC hdc, Glyph glyph)
51 {
52     CGMutablePathRef path = CGPathCreateMutable();
53
54     static const MAT2 identity = { 0, 1,  0, 0,  0, 0,  0, 1 };
55     GLYPHMETRICS glyphMetrics;
56     // GGO_NATIVE matches the outline perfectly when Windows font smoothing is off.
57     // GGO_NATIVE | GGO_UNHINTED does not match perfectly either when Windows font smoothing is on or off.
58     DWORD outlineLength = GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, 0, 0, &identity);
59     ASSERT(outlineLength >= 0);
60     if (outlineLength < 0)
61         return path;
62
63     Vector<UInt8> outline(outlineLength);
64     GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, outlineLength, outline.data(), &identity);
65
66     unsigned offset = 0;
67     while (offset < outlineLength) {
68         LPTTPOLYGONHEADER subpath = reinterpret_cast<LPTTPOLYGONHEADER>(outline.data() + offset);
69         ASSERT(subpath->dwType == TT_POLYGON_TYPE);
70         if (subpath->dwType != TT_POLYGON_TYPE)
71             return path;
72
73         CGPathMoveToPoint(path, 0, toCGFloat(subpath->pfxStart.x), toCGFloat(subpath->pfxStart.y));
74
75         unsigned subpathOffset = sizeof(*subpath);
76         while (subpathOffset < subpath->cb) {
77             LPTTPOLYCURVE segment = reinterpret_cast<LPTTPOLYCURVE>(reinterpret_cast<UInt8*>(subpath) + subpathOffset);
78             switch (segment->wType) {
79                 case TT_PRIM_LINE:
80                     for (unsigned i = 0; i < segment->cpfx; i++)
81                         CGPathAddLineToPoint(path, 0, toCGFloat(segment->apfx[i].x), toCGFloat(segment->apfx[i].y));
82                     break;
83
84                 case TT_PRIM_QSPLINE:
85                     for (unsigned i = 0; i < segment->cpfx; i++) {
86                         CGFloat x = toCGFloat(segment->apfx[i].x);
87                         CGFloat y = toCGFloat(segment->apfx[i].y);
88                         CGFloat cpx;
89                         CGFloat cpy;
90
91                         if (i == segment->cpfx - 2) {
92                             cpx = toCGFloat(segment->apfx[i + 1].x);
93                             cpy = toCGFloat(segment->apfx[i + 1].y);
94                             i++;
95                         } else {
96                             cpx = (toCGFloat(segment->apfx[i].x) + toCGFloat(segment->apfx[i + 1].x)) / 2;
97                             cpy = (toCGFloat(segment->apfx[i].y) + toCGFloat(segment->apfx[i + 1].y)) / 2;
98                         }
99
100                         CGPathAddQuadCurveToPoint(path, 0, x, y, cpx, cpy);
101                     }
102                     break;
103
104                 case TT_PRIM_CSPLINE:
105                     for (unsigned i = 0; i < segment->cpfx; i += 3) {
106                         CGFloat cp1x = toCGFloat(segment->apfx[i].x);
107                         CGFloat cp1y = toCGFloat(segment->apfx[i].y);
108                         CGFloat cp2x = toCGFloat(segment->apfx[i + 1].x);
109                         CGFloat cp2y = toCGFloat(segment->apfx[i + 1].y);
110                         CGFloat x = toCGFloat(segment->apfx[i + 2].x);
111                         CGFloat y = toCGFloat(segment->apfx[i + 2].y);
112
113                         CGPathAddCurveToPoint(path, 0, cp1x, cp1y, cp2x, cp2y, x, y);
114                     }
115                     break;
116
117                 default:
118                     ASSERT_NOT_REACHED();
119                     return path;
120             }
121
122             subpathOffset += sizeof(*segment) + (segment->cpfx - 1) * sizeof(segment->apfx[0]);
123         }
124         CGPathCloseSubpath(path);
125         offset += subpath->cb;
126     }
127     return path;
128 }
129
130 void FontCascade::drawGlyphs(GraphicsContext& graphicsContext, const Font& font, const GlyphBuffer& glyphBuffer,
131     int from, int numGlyphs, const FloatPoint& point, FontSmoothingMode smoothingMode)
132 {
133     CGContextRef cgContext = graphicsContext.platformContext();
134     bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing();
135
136     switch (smoothingMode) {
137     case Antialiased: {
138         graphicsContext.setShouldAntialias(true);
139         shouldUseFontSmoothing = false;
140         break;
141     }
142     case SubpixelAntialiased: {
143         graphicsContext.setShouldAntialias(true);
144         shouldUseFontSmoothing = true;
145         break;
146     }
147     case NoSmoothing: {
148         graphicsContext.setShouldAntialias(false);
149         shouldUseFontSmoothing = false;
150         break;
151     }
152     case AutoSmoothing: {
153         // For the AutoSmooth case, don't do anything! Keep the default settings.
154         break; 
155     }
156     default: 
157         ASSERT_NOT_REACHED();
158     }
159
160     uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing);
161
162     const FontPlatformData& platformData = font.platformData();
163
164     CGContextSetFont(cgContext, platformData.cgFont());
165
166     CGAffineTransform matrix = CGAffineTransformIdentity;
167     matrix.b = -matrix.b;
168     matrix.d = -matrix.d;
169
170     if (platformData.syntheticOblique()) {
171         static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f);
172         matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0));
173     }
174
175     CGContextSetTextMatrix(cgContext, matrix);
176
177     // Uniscribe gives us offsets to help refine the positioning of combining glyphs.
178     FloatSize translation = glyphBuffer.offsetAt(from);
179
180     CGContextSetFontSize(cgContext, platformData.size());
181     wkSetCGContextFontRenderingStyle(cgContext, font.platformData().isSystemFont(), false, font.platformData().useGDI());
182
183     FloatSize shadowOffset;
184     float shadowBlur;
185     Color shadowColor;
186     graphicsContext.getShadow(shadowOffset, shadowBlur, shadowColor);
187
188     bool hasSimpleShadow = graphicsContext.textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && (!graphicsContext.shadowsIgnoreTransforms() || graphicsContext.getCTM().isIdentityOrTranslationOrFlipped());
189     if (hasSimpleShadow) {
190         // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
191         graphicsContext.clearShadow();
192         Color fillColor = graphicsContext.fillColor();
193         Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
194         graphicsContext.setFillColor(shadowFillColor);
195         float shadowTextX = point.x() + translation.width() + shadowOffset.width();
196         // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative.
197         float shadowTextY = point.y() + translation.height() + shadowOffset.height() * (graphicsContext.shadowsIgnoreTransforms() ? -1 : 1);
198         CGContextSetTextPosition(cgContext, shadowTextX, shadowTextY);
199         CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
200         if (font.syntheticBoldOffset()) {
201             CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font.syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height());
202             CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
203         }
204         graphicsContext.setFillColor(fillColor);
205     }
206
207     CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height());
208     CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
209     if (font.syntheticBoldOffset()) {
210         CGContextSetTextPosition(cgContext, point.x() + translation.width() + font.syntheticBoldOffset(), point.y() + translation.height());
211         CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
212     }
213
214     if (hasSimpleShadow)
215         graphicsContext.setShadow(shadowOffset, shadowBlur, shadowColor);
216
217     wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle);
218 }
219
220 }