Home | History | Annotate | Download | only in win
      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