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 "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 41namespace WebCore { 42 43const int syntheticObliqueAngle = 14; 44 45static inline CGFloat toCGFloat(FIXED f) 46{ 47 return f.value + f.fract / CGFloat(65536.0); 48} 49 50static 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 130void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, 131 int from, int numGlyphs, const FloatPoint& point) const 132{ 133 CGContextRef cgContext = graphicsContext->platformContext(); 134 bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); 135 136 switch(fontDescription().fontSmoothing()) { 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->isSystemFont(), false, font->platformData().useGDI()); 182 183 FloatSize shadowOffset; 184 float shadowBlur; 185 Color shadowColor; 186 ColorSpace shadowColorSpace; 187 graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); 188 189 bool hasSimpleShadow = graphicsContext->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && (!graphicsContext->shadowsIgnoreTransforms() || graphicsContext->getCTM().isIdentityOrTranslationOrFlipped()); 190 if (hasSimpleShadow) { 191 // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. 192 graphicsContext->clearShadow(); 193 Color fillColor = graphicsContext->fillColor(); 194 ColorSpace fillColorSpace = graphicsContext->fillColorSpace(); 195 Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); 196 graphicsContext->setFillColor(shadowFillColor, shadowColorSpace); 197 float shadowTextX = point.x() + translation.width() + shadowOffset.width(); 198 // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative. 199 float shadowTextY = point.y() + translation.height() + shadowOffset.height() * (graphicsContext->shadowsIgnoreTransforms() ? -1 : 1); 200 CGContextSetTextPosition(cgContext, shadowTextX, shadowTextY); 201 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 202 if (font->syntheticBoldOffset()) { 203 CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height()); 204 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 205 } 206 graphicsContext->setFillColor(fillColor, fillColorSpace); 207 } 208 209 CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); 210 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 211 if (font->syntheticBoldOffset()) { 212 CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->syntheticBoldOffset(), point.y() + translation.height()); 213 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 214 } 215 216 if (hasSimpleShadow) 217 graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); 218 219 wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); 220} 221 222} 223