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 IntSize size; 144 int blur; 145 Color color; 146 graphicsContext->getShadow(size, blur, color); 147 drawIntoBitmap = !size.isEmpty() || 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()) { 325 static bool canCreateCGFontWithLOGFONT = wkCanCreateCGFontWithLOGFONT(); 326 if (!shouldUseFontSmoothing || !canCreateCGFontWithLOGFONT && (graphicsContext->textDrawingMode() & cTextStroke)) { 327 drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); 328 return; 329 } 330 } 331 332 uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); 333 334 const FontPlatformData& platformData = font->platformData(); 335 336 CGContextSetFont(cgContext, platformData.cgFont()); 337 338 CGAffineTransform matrix = CGAffineTransformIdentity; 339 matrix.b = -matrix.b; 340 matrix.d = -matrix.d; 341 342 if (platformData.syntheticOblique()) { 343 static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f); 344 matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); 345 } 346 347 CGContextSetTextMatrix(cgContext, matrix); 348 349 // Uniscribe gives us offsets to help refine the positioning of combining glyphs. 350 FloatSize translation = glyphBuffer.offsetAt(from); 351 352 CGContextSetFontSize(cgContext, platformData.size()); 353 wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI()); 354 355 IntSize shadowSize; 356 int shadowBlur; 357 Color shadowColor; 358 graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor); 359 360 bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; 361 if (hasSimpleShadow) { 362 // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. 363 graphicsContext->clearShadow(); 364 Color fillColor = graphicsContext->fillColor(); 365 Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); 366 graphicsContext->setFillColor(shadowFillColor, DeviceColorSpace); 367 CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width(), point.y() + translation.height() + shadowSize.height()); 368 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); 369 if (font->syntheticBoldOffset()) { 370 CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowSize.height()); 371 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); 372 } 373 graphicsContext->setFillColor(fillColor, DeviceColorSpace); 374 } 375 376 CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); 377 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); 378 if (font->syntheticBoldOffset()) { 379 CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->syntheticBoldOffset(), point.y() + translation.height()); 380 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); 381 } 382 383 if (hasSimpleShadow) 384 graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor, DeviceColorSpace); 385 386 wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); 387 } 388 389 } 390