OSDN Git Service

Merge Webkit at r70949: Initial merge by git.
[android-x86/external-webkit.git] / WebCore / platform / graphics / win / FontCGWin.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009 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 COMPUTER, 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 COMPUTER, 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 "Font.h"
28
29 #include "AffineTransform.h"
30 #include "FloatConversion.h"
31 #include "GlyphBuffer.h"
32 #include "GraphicsContext.h"
33 #include "IntRect.h"
34 #include "SimpleFontData.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 static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, 
131                       int from, int numGlyphs, const FloatPoint& point)
132 {
133     Color fillColor = graphicsContext->fillColor();
134
135     bool drawIntoBitmap = false;
136     int drawingMode = graphicsContext->textDrawingMode();
137     if (drawingMode == cTextFill) {
138         if (!fillColor.alpha())
139             return;
140
141         drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer();
142         if (!drawIntoBitmap) {
143             FloatSize offset;
144             float blur;
145             Color color;
146             graphicsContext->getShadow(offset, blur, color);
147             drawIntoBitmap = offset.width() || offset.height() || blur;
148         }
149     }
150
151     // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances.
152     Vector<int, 2048> gdiAdvances;
153     int totalWidth = 0;
154     for (int i = 0; i < numGlyphs; i++) {
155         gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i)));
156         totalWidth += gdiAdvances[i];
157     }
158
159     HDC hdc = 0;
160     OwnPtr<GraphicsContext::WindowsBitmap> bitmap;
161     IntRect textRect;
162     if (!drawIntoBitmap)
163         hdc = graphicsContext->getWindowsContext(textRect, true, false);
164     if (!hdc) {
165         drawIntoBitmap = true;
166         // We put slop into this rect, since glyphs can overflow the ascent/descent bounds and the left/right edges.
167         // FIXME: Can get glyphs' optical bounds (even from CG) to get this right.
168         int lineGap = font->lineGap();
169         textRect = IntRect(point.x() - (font->ascent() + font->descent()) / 2, point.y() - font->ascent() - lineGap, totalWidth + font->ascent() + font->descent(), font->lineSpacing());
170         bitmap.set(graphicsContext->createWindowsBitmap(textRect.size()));
171         memset(bitmap->buffer(), 255, bitmap->bufferLength());
172         hdc = bitmap->hdc();
173
174         XFORM xform;
175         xform.eM11 = 1.0f;
176         xform.eM12 = 0.0f;
177         xform.eM21 = 0.0f;
178         xform.eM22 = 1.0f;
179         xform.eDx = -textRect.x();
180         xform.eDy = -textRect.y();
181         SetWorldTransform(hdc, &xform);
182     }
183
184     SelectObject(hdc, font->platformData().hfont());
185
186     // Set the correct color.
187     if (drawIntoBitmap)
188         SetTextColor(hdc, RGB(0, 0, 0));
189     else
190         SetTextColor(hdc, RGB(fillColor.red(), fillColor.green(), fillColor.blue()));
191
192     SetBkMode(hdc, TRANSPARENT);
193     SetTextAlign(hdc, TA_LEFT | TA_BASELINE);
194
195     // Uniscribe gives us offsets to help refine the positioning of combining glyphs.
196     FloatSize translation = glyphBuffer.offsetAt(from);
197     if (translation.width() || translation.height()) {
198         XFORM xform;
199         xform.eM11 = 1.0;
200         xform.eM12 = 0;
201         xform.eM21 = 0;
202         xform.eM22 = 1.0;
203         xform.eDx = translation.width();
204         xform.eDy = translation.height();
205         ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
206     }
207
208     if (drawingMode == cTextFill) {
209         XFORM xform;
210         xform.eM11 = 1.0;
211         xform.eM12 = 0;
212         xform.eM21 = font->platformData().syntheticOblique() ? -tanf(syntheticObliqueAngle * piFloat / 180.0f) : 0;
213         xform.eM22 = 1.0;
214         xform.eDx = point.x();
215         xform.eDy = point.y();
216         ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
217         ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data());
218         if (font->syntheticBoldOffset()) {
219             xform.eM21 = 0;
220             xform.eDx = font->syntheticBoldOffset();
221             xform.eDy = 0;
222             ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
223             ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data());
224         }
225     } else {
226         XFORM xform;
227         GetWorldTransform(hdc, &xform);
228         AffineTransform hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy);
229         CGAffineTransform initialGlyphTransform = hdcTransform.isInvertible() ? hdcTransform.inverse() : CGAffineTransformIdentity;
230         if (font->platformData().syntheticOblique())
231             initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0));
232         initialGlyphTransform.tx = 0;
233         initialGlyphTransform.ty = 0;
234         CGContextRef cgContext = graphicsContext->platformContext();
235
236         CGContextSaveGState(cgContext);
237
238         BOOL fontSmoothingEnabled = false;
239         SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0);
240         CGContextSetShouldAntialias(cgContext, fontSmoothingEnabled);
241
242         CGContextScaleCTM(cgContext, 1.0, -1.0);
243         CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height()));
244
245         for (unsigned i = 0; i < numGlyphs; ++i) {
246             RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i)));
247             CGContextSaveGState(cgContext);
248             CGContextConcatCTM(cgContext, initialGlyphTransform);
249
250             if (drawingMode & cTextFill) {
251                 CGContextAddPath(cgContext, glyphPath.get());
252                 CGContextFillPath(cgContext);
253                 if (font->syntheticBoldOffset()) {
254                     CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0);
255                     CGContextAddPath(cgContext, glyphPath.get());
256                     CGContextFillPath(cgContext);
257                     CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0);
258                 }
259             }
260             if (drawingMode & cTextStroke) {
261                 CGContextAddPath(cgContext, glyphPath.get());
262                 CGContextStrokePath(cgContext);
263                 if (font->syntheticBoldOffset()) {
264                     CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0);
265                     CGContextAddPath(cgContext, glyphPath.get());
266                     CGContextStrokePath(cgContext);
267                     CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0);
268                 }
269             }
270
271             CGContextRestoreGState(cgContext);
272             CGContextTranslateCTM(cgContext, gdiAdvances[i], 0);
273         }
274
275         CGContextRestoreGState(cgContext);
276     }
277
278     if (drawIntoBitmap) {
279         UInt8* buffer = bitmap->buffer();
280         unsigned bufferLength = bitmap->bufferLength();
281         for (unsigned i = 0; i < bufferLength; i += 4) {
282             // Use green, which is always in the middle.
283             UInt8 alpha = (255 - buffer[i + 1]) * fillColor.alpha() / 255;
284             buffer[i] = fillColor.blue();
285             buffer[i + 1] = fillColor.green();
286             buffer[i + 2] = fillColor.red();
287             buffer[i + 3] = alpha;
288         }
289         graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.topLeft());
290     } else
291         graphicsContext->releaseWindowsContext(hdc, textRect, true, false);
292 }
293
294 void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, 
295                       int from, int numGlyphs, const FloatPoint& point) const
296 {
297     CGContextRef cgContext = graphicsContext->platformContext();
298     bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing();
299
300     switch(fontDescription().fontSmoothing()) {
301     case Antialiased: {
302         graphicsContext->setShouldAntialias(true);
303         shouldUseFontSmoothing = false;
304         break;
305     }
306     case SubpixelAntialiased: {
307         graphicsContext->setShouldAntialias(true);
308         shouldUseFontSmoothing = true;
309         break;
310     }
311     case NoSmoothing: {
312         graphicsContext->setShouldAntialias(false);
313         shouldUseFontSmoothing = false;
314         break;
315     }
316     case AutoSmoothing: {
317         // For the AutoSmooth case, don't do anything! Keep the default settings.
318         break; 
319     }
320     default: 
321         ASSERT_NOT_REACHED();
322     }
323
324     if (font->platformData().useGDI() && !shouldUseFontSmoothing) {
325         drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
326         return;
327     }
328
329     uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing);
330
331     const FontPlatformData& platformData = font->platformData();
332
333     CGContextSetFont(cgContext, platformData.cgFont());
334
335     CGAffineTransform matrix = CGAffineTransformIdentity;
336     matrix.b = -matrix.b;
337     matrix.d = -matrix.d;
338
339     if (platformData.syntheticOblique()) {
340         static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f);
341         matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0));
342     }
343
344     CGContextSetTextMatrix(cgContext, matrix);
345
346     // Uniscribe gives us offsets to help refine the positioning of combining glyphs.
347     FloatSize translation = glyphBuffer.offsetAt(from);
348
349     CGContextSetFontSize(cgContext, platformData.size());
350     wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI());
351
352     FloatSize shadowOffset;
353     float shadowBlur;
354     Color shadowColor;
355     graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor);
356
357     bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur;
358     if (hasSimpleShadow) {
359         // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
360         graphicsContext->clearShadow();
361         Color fillColor = graphicsContext->fillColor();
362         Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
363         graphicsContext->setFillColor(shadowFillColor, ColorSpaceDeviceRGB);
364         CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width(), point.y() + translation.height() + shadowOffset.height());
365         CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
366         if (font->syntheticBoldOffset()) {
367             CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height());
368             CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
369         }
370         graphicsContext->setFillColor(fillColor, ColorSpaceDeviceRGB);
371     }
372
373     CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height());
374     CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
375     if (font->syntheticBoldOffset()) {
376         CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->syntheticBoldOffset(), point.y() + translation.height());
377         CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
378     }
379
380     if (hasSimpleShadow)
381         graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB);
382
383     wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle);
384 }
385
386 }