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