2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "AffineTransform.h"
30 #include "FloatConversion.h"
31 #include "GlyphBuffer.h"
32 #include "GraphicsContext.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>
43 const int syntheticObliqueAngle = 14;
45 static inline CGFloat toCGFloat(FIXED f)
47 return f.value + f.fract / CGFloat(65536.0);
50 static CGPathRef createPathForGlyph(HDC hdc, Glyph glyph)
52 CGMutablePathRef path = CGPathCreateMutable();
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)
63 Vector<UInt8> outline(outlineLength);
64 GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, outlineLength, outline.data(), &identity);
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)
73 CGPathMoveToPoint(path, 0, toCGFloat(subpath->pfxStart.x), toCGFloat(subpath->pfxStart.y));
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) {
80 for (unsigned i = 0; i < segment->cpfx; i++)
81 CGPathAddLineToPoint(path, 0, toCGFloat(segment->apfx[i].x), toCGFloat(segment->apfx[i].y));
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);
91 if (i == segment->cpfx - 2) {
92 cpx = toCGFloat(segment->apfx[i + 1].x);
93 cpy = toCGFloat(segment->apfx[i + 1].y);
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;
100 CGPathAddQuadCurveToPoint(path, 0, x, y, cpx, cpy);
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);
113 CGPathAddCurveToPoint(path, 0, cp1x, cp1y, cp2x, cp2y, x, y);
118 ASSERT_NOT_REACHED();
122 subpathOffset += sizeof(*segment) + (segment->cpfx - 1) * sizeof(segment->apfx[0]);
124 CGPathCloseSubpath(path);
125 offset += subpath->cb;
130 static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
131 int from, int numGlyphs, const FloatPoint& point)
133 Color fillColor = graphicsContext->fillColor();
135 bool drawIntoBitmap = false;
136 int drawingMode = graphicsContext->textDrawingMode();
137 if (drawingMode == cTextFill) {
138 if (!fillColor.alpha())
141 drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer();
142 if (!drawIntoBitmap) {
146 graphicsContext->getShadow(offset, blur, color);
147 drawIntoBitmap = offset.width() || offset.height() || blur;
151 // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances.
152 Vector<int, 2048> gdiAdvances;
154 for (int i = 0; i < numGlyphs; i++) {
155 gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i)));
156 totalWidth += gdiAdvances[i];
160 OwnPtr<GraphicsContext::WindowsBitmap> bitmap;
163 hdc = graphicsContext->getWindowsContext(textRect, true, false);
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());
179 xform.eDx = -textRect.x();
180 xform.eDy = -textRect.y();
181 SetWorldTransform(hdc, &xform);
184 SelectObject(hdc, font->platformData().hfont());
186 // Set the correct color.
188 SetTextColor(hdc, RGB(0, 0, 0));
190 SetTextColor(hdc, RGB(fillColor.red(), fillColor.green(), fillColor.blue()));
192 SetBkMode(hdc, TRANSPARENT);
193 SetTextAlign(hdc, TA_LEFT | TA_BASELINE);
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()) {
203 xform.eDx = translation.width();
204 xform.eDy = translation.height();
205 ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
208 if (drawingMode == cTextFill) {
212 xform.eM21 = font->platformData().syntheticOblique() ? -tanf(syntheticObliqueAngle * piFloat / 180.0f) : 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()) {
220 xform.eDx = font->syntheticBoldOffset();
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());
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();
236 CGContextSaveGState(cgContext);
238 BOOL fontSmoothingEnabled = false;
239 SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0);
240 CGContextSetShouldAntialias(cgContext, fontSmoothingEnabled);
242 CGContextScaleCTM(cgContext, 1.0, -1.0);
243 CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height()));
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);
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);
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);
271 CGContextRestoreGState(cgContext);
272 CGContextTranslateCTM(cgContext, gdiAdvances[i], 0);
275 CGContextRestoreGState(cgContext);
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;
289 graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.topLeft());
291 graphicsContext->releaseWindowsContext(hdc, textRect, true, false);
294 void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
295 int from, int numGlyphs, const FloatPoint& point) const
297 CGContextRef cgContext = graphicsContext->platformContext();
298 bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing();
300 switch(fontDescription().fontSmoothing()) {
302 graphicsContext->setShouldAntialias(true);
303 shouldUseFontSmoothing = false;
306 case SubpixelAntialiased: {
307 graphicsContext->setShouldAntialias(true);
308 shouldUseFontSmoothing = true;
312 graphicsContext->setShouldAntialias(false);
313 shouldUseFontSmoothing = false;
316 case AutoSmoothing: {
317 // For the AutoSmooth case, don't do anything! Keep the default settings.
321 ASSERT_NOT_REACHED();
324 if (font->platformData().useGDI() && !shouldUseFontSmoothing) {
325 drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
329 uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing);
331 const FontPlatformData& platformData = font->platformData();
333 CGContextSetFont(cgContext, platformData.cgFont());
335 CGAffineTransform matrix = CGAffineTransformIdentity;
336 matrix.b = -matrix.b;
337 matrix.d = -matrix.d;
339 if (platformData.syntheticOblique()) {
340 static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f);
341 matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0));
344 CGContextSetTextMatrix(cgContext, matrix);
346 // Uniscribe gives us offsets to help refine the positioning of combining glyphs.
347 FloatSize translation = glyphBuffer.offsetAt(from);
349 CGContextSetFontSize(cgContext, platformData.size());
350 wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI());
352 FloatSize shadowOffset;
355 graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor);
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);
370 graphicsContext->setFillColor(fillColor, ColorSpaceDeviceRGB);
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);
381 graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB);
383 wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle);