summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/win/FontCGWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/win/FontCGWin.cpp')
-rw-r--r--WebCore/platform/graphics/win/FontCGWin.cpp314
1 files changed, 274 insertions, 40 deletions
diff --git a/WebCore/platform/graphics/win/FontCGWin.cpp b/WebCore/platform/graphics/win/FontCGWin.cpp
index 8b59a48..1766cd9 100644
--- a/WebCore/platform/graphics/win/FontCGWin.cpp
+++ b/WebCore/platform/graphics/win/FontCGWin.cpp
@@ -26,11 +26,14 @@
#include "config.h"
#include "Font.h"
+#include "AffineTransform.h"
+#include "FloatConversion.h"
#include "GlyphBuffer.h"
#include "GraphicsContext.h"
#include "IntRect.h"
#include "SimpleFontData.h"
#include "UniscribeController.h"
+#include "WebCoreTextRenderer.h"
#include <ApplicationServices/ApplicationServices.h>
#include <WebKitSystemInterface/WebKitSystemInterface.h>
#include <wtf/MathExtras.h>
@@ -39,59 +42,266 @@ namespace WebCore {
const int syntheticObliqueAngle = 14;
-void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
- int from, int numGlyphs, const FloatPoint& point) const
+static inline CGFloat toCGFloat(FIXED f)
{
- if (font->m_font.useGDI()) {
- // FIXME: Support alpha blending.
- // FIXME: Support text stroke/fill.
- // FIXME: Support text shadow.
- Color fillColor = graphicsContext->fillColor();
- if (fillColor.alpha() == 0)
+ return f.value + f.fract / CGFloat(65536.0);
+}
+
+static CGPathRef createPathForGlyph(HDC hdc, Glyph glyph)
+{
+ CGMutablePathRef path = CGPathCreateMutable();
+
+ static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 };
+ GLYPHMETRICS glyphMetrics;
+ // GGO_NATIVE matches the outline perfectly when Windows font smoothing is off.
+ // GGO_NATIVE | GGO_UNHINTED does not match perfectly either when Windows font smoothing is on or off.
+ DWORD outlineLength = GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, 0, 0, &identity);
+ ASSERT(outlineLength >= 0);
+ if (outlineLength < 0)
+ return path;
+
+ Vector<UInt8> outline(outlineLength);
+ GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, outlineLength, outline.data(), &identity);
+
+ unsigned offset = 0;
+ while (offset < outlineLength) {
+ LPTTPOLYGONHEADER subpath = reinterpret_cast<LPTTPOLYGONHEADER>(outline.data() + offset);
+ ASSERT(subpath->dwType == TT_POLYGON_TYPE);
+ if (subpath->dwType != TT_POLYGON_TYPE)
+ return path;
+
+ CGPathMoveToPoint(path, 0, toCGFloat(subpath->pfxStart.x), toCGFloat(subpath->pfxStart.y));
+
+ unsigned subpathOffset = sizeof(*subpath);
+ while (subpathOffset < subpath->cb) {
+ LPTTPOLYCURVE segment = reinterpret_cast<LPTTPOLYCURVE>(reinterpret_cast<UInt8*>(subpath) + subpathOffset);
+ switch (segment->wType) {
+ case TT_PRIM_LINE:
+ for (unsigned i = 0; i < segment->cpfx; i++)
+ CGPathAddLineToPoint(path, 0, toCGFloat(segment->apfx[i].x), toCGFloat(segment->apfx[i].y));
+ break;
+
+ case TT_PRIM_QSPLINE:
+ for (unsigned i = 0; i < segment->cpfx; i++) {
+ CGFloat x = toCGFloat(segment->apfx[i].x);
+ CGFloat y = toCGFloat(segment->apfx[i].y);
+ CGFloat cpx;
+ CGFloat cpy;
+
+ if (i == segment->cpfx - 2) {
+ cpx = toCGFloat(segment->apfx[i + 1].x);
+ cpy = toCGFloat(segment->apfx[i + 1].y);
+ i++;
+ } else {
+ cpx = (toCGFloat(segment->apfx[i].x) + toCGFloat(segment->apfx[i + 1].x)) / 2;
+ cpy = (toCGFloat(segment->apfx[i].y) + toCGFloat(segment->apfx[i + 1].y)) / 2;
+ }
+
+ CGPathAddQuadCurveToPoint(path, 0, x, y, cpx, cpy);
+ }
+ break;
+
+ case TT_PRIM_CSPLINE:
+ for (unsigned i = 0; i < segment->cpfx; i += 3) {
+ CGFloat cp1x = toCGFloat(segment->apfx[i].x);
+ CGFloat cp1y = toCGFloat(segment->apfx[i].y);
+ CGFloat cp2x = toCGFloat(segment->apfx[i + 1].x);
+ CGFloat cp2y = toCGFloat(segment->apfx[i + 1].y);
+ CGFloat x = toCGFloat(segment->apfx[i + 2].x);
+ CGFloat y = toCGFloat(segment->apfx[i + 2].y);
+
+ CGPathAddCurveToPoint(path, 0, cp1x, cp1y, cp2x, cp2y, x, y);
+ }
+ break;
+
+ default:
+ ASSERT_NOT_REACHED();
+ return path;
+ }
+
+ subpathOffset += sizeof(*segment) + (segment->cpfx - 1) * sizeof(segment->apfx[0]);
+ }
+ CGPathCloseSubpath(path);
+ offset += subpath->cb;
+ }
+ return path;
+}
+
+static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
+ int from, int numGlyphs, const FloatPoint& point)
+{
+ Color fillColor = graphicsContext->fillColor();
+
+ bool drawIntoBitmap = false;
+ int drawingMode = graphicsContext->textDrawingMode();
+ if (drawingMode == cTextFill) {
+ if (!fillColor.alpha())
return;
- // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances.
- Vector<int, 2048> gdiAdvances;
- int totalWidth = 0;
- for (int i = 0; i < numGlyphs; i++) {
- gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i)));
- totalWidth += gdiAdvances[i];
+ drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer();
+ if (!drawIntoBitmap) {
+ IntSize size;
+ int blur;
+ Color color;
+ graphicsContext->getShadow(size, blur, color);
+ drawIntoBitmap = !size.isEmpty() || blur;
}
+ }
+
+ // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances.
+ Vector<int, 2048> gdiAdvances;
+ int totalWidth = 0;
+ for (int i = 0; i < numGlyphs; i++) {
+ gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i)));
+ totalWidth += gdiAdvances[i];
+ }
+ HDC hdc = 0;
+ OwnPtr<GraphicsContext::WindowsBitmap> bitmap;
+ IntRect textRect;
+ if (!drawIntoBitmap)
+ hdc = graphicsContext->getWindowsContext(textRect, true, false);
+ if (!hdc) {
+ drawIntoBitmap = true;
// We put slop into this rect, since glyphs can overflow the ascent/descent bounds and the left/right edges.
+ // FIXME: Can get glyphs' optical bounds (even from CG) to get this right.
int lineGap = font->lineGap();
- IntRect textRect(point.x() - lineGap, point.y() - font->ascent() - lineGap, totalWidth + 2 * lineGap, font->lineSpacing());
- HDC hdc = graphicsContext->getWindowsContext(textRect);
- SelectObject(hdc, font->m_font.hfont());
+ textRect = IntRect(point.x() - (font->ascent() + font->descent()) / 2, point.y() - font->ascent() - lineGap, totalWidth + font->ascent() + font->descent(), font->lineSpacing());
+ bitmap.set(graphicsContext->createWindowsBitmap(textRect.size()));
+ memset(bitmap->buffer(), 255, bitmap->bufferLength());
+ hdc = bitmap->hdc();
- // Set the correct color.
- HDC textDrawingDC = hdc;
+ XFORM xform;
+ xform.eM11 = 1.0f;
+ xform.eM12 = 0.0f;
+ xform.eM21 = 0.0f;
+ xform.eM22 = 1.0f;
+ xform.eDx = -textRect.x();
+ xform.eDy = -textRect.y();
+ SetWorldTransform(hdc, &xform);
+ }
+
+ SelectObject(hdc, font->m_font.hfont());
+
+ // Set the correct color.
+ if (drawIntoBitmap)
+ SetTextColor(hdc, RGB(0, 0, 0));
+ else
SetTextColor(hdc, RGB(fillColor.red(), fillColor.green(), fillColor.blue()));
- SetBkMode(hdc, TRANSPARENT);
- SetTextAlign(hdc, TA_LEFT | TA_BASELINE);
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextAlign(hdc, TA_LEFT | TA_BASELINE);
- // Uniscribe gives us offsets to help refine the positioning of combining glyphs.
- FloatSize translation = glyphBuffer.offsetAt(from);
- if (translation.width() || translation.height()) {
- XFORM xform;
- xform.eM11 = 1.0;
- xform.eM12 = 0;
+ // Uniscribe gives us offsets to help refine the positioning of combining glyphs.
+ FloatSize translation = glyphBuffer.offsetAt(from);
+ if (translation.width() || translation.height()) {
+ XFORM xform;
+ xform.eM11 = 1.0;
+ xform.eM12 = 0;
+ xform.eM21 = 0;
+ xform.eM22 = 1.0;
+ xform.eDx = translation.width();
+ xform.eDy = translation.height();
+ ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
+ }
+
+ if (drawingMode == cTextFill) {
+ XFORM xform;
+ xform.eM11 = 1.0;
+ xform.eM12 = 0;
+ xform.eM21 = font->platformData().syntheticOblique() ? -tanf(syntheticObliqueAngle * piFloat / 180.0f) : 0;
+ xform.eM22 = 1.0;
+ xform.eDx = point.x();
+ xform.eDy = point.y();
+ ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
+ ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data());
+ if (font->m_syntheticBoldOffset) {
xform.eM21 = 0;
- xform.eM22 = 1.0;
- xform.eDx = translation.width();
- xform.eDy = translation.height();
+ xform.eDx = font->m_syntheticBoldOffset;
+ xform.eDy = 0;
ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
+ ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data());
+ }
+ } else {
+ RetainPtr<CGMutablePathRef> path(AdoptCF, CGPathCreateMutable());
+
+ XFORM xform;
+ GetWorldTransform(hdc, &xform);
+ AffineTransform hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy);
+ CGAffineTransform initialGlyphTransform = hdcTransform.isInvertible() ? hdcTransform.inverse() : CGAffineTransformIdentity;
+ if (font->platformData().syntheticOblique())
+ initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0));
+ initialGlyphTransform.tx = 0;
+ initialGlyphTransform.ty = 0;
+ CGAffineTransform glyphTranslation = CGAffineTransformIdentity;
+
+ for (unsigned i = 0; i < numGlyphs; ++i) {
+ RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i)));
+ CGAffineTransform glyphTransform = CGAffineTransformConcat(initialGlyphTransform, glyphTranslation);
+ CGPathAddPath(path.get(), &glyphTransform, glyphPath.get());
+ glyphTranslation = CGAffineTransformTranslate(glyphTranslation, gdiAdvances[i], 0);
+ }
+
+ CGContextRef cgContext = graphicsContext->platformContext();
+ CGContextSaveGState(cgContext);
+
+ BOOL fontSmoothingEnabled = false;
+ SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0);
+ CGContextSetShouldAntialias(cgContext, fontSmoothingEnabled);
+
+ CGContextScaleCTM(cgContext, 1.0, -1.0);
+ CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height()));
+
+ if (drawingMode & cTextFill) {
+ CGContextAddPath(cgContext, path.get());
+ CGContextFillPath(cgContext);
+ if (font->m_syntheticBoldOffset) {
+ CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0);
+ CGContextAddPath(cgContext, path.get());
+ CGContextFillPath(cgContext);
+ CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0);
+ }
+ }
+ if (drawingMode & cTextStroke) {
+ CGContextAddPath(cgContext, path.get());
+ CGContextStrokePath(cgContext);
+ if (font->m_syntheticBoldOffset) {
+ CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0);
+ CGContextAddPath(cgContext, path.get());
+ CGContextStrokePath(cgContext);
+ CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0);
+ }
}
- ExtTextOut(hdc, point.x(), point.y(), ETO_GLYPH_INDEX, 0, (WCHAR*)glyphBuffer.glyphs(from), numGlyphs, gdiAdvances.data());
+ CGContextRestoreGState(cgContext);
+ }
+
+ if (drawIntoBitmap) {
+ UInt8* buffer = bitmap->buffer();
+ unsigned bufferLength = bitmap->bufferLength();
+ for (unsigned i = 0; i < bufferLength; i += 4) {
+ // Use green, which is always in the middle.
+ UInt8 alpha = (255 - buffer[i + 1]) * fillColor.alpha() / 255;
+ buffer[i] = fillColor.blue();
+ buffer[i + 1] = fillColor.green();
+ buffer[i + 2] = fillColor.red();
+ buffer[i + 3] = alpha;
+ }
+ graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.topLeft());
+ } else
+ graphicsContext->releaseWindowsContext(hdc, textRect, true, false);
+}
- graphicsContext->releaseWindowsContext(hdc, textRect);
+void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
+ int from, int numGlyphs, const FloatPoint& point) const
+{
+ if (font->m_font.useGDI()) {
+ drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
return;
}
CGContextRef cgContext = graphicsContext->platformContext();
- uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext);
+ uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, WebCoreShouldUseFontSmoothing());
const FontPlatformData& platformData = font->platformData();
@@ -102,25 +312,49 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo
matrix.d = -matrix.d;
if (platformData.syntheticOblique()) {
- static float skew = -tanf(syntheticObliqueAngle * acosf(0) / 90.0f);
+ static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f);
matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0));
}
+ CGContextSetTextMatrix(cgContext, matrix);
+
// Uniscribe gives us offsets to help refine the positioning of combining glyphs.
FloatSize translation = glyphBuffer.offsetAt(from);
- if (translation.width() || translation.height())
- CGAffineTransformTranslate(matrix, translation.width(), translation.height());
-
- CGContextSetTextMatrix(cgContext, matrix);
CGContextSetFontSize(cgContext, platformData.size());
- CGContextSetTextPosition(cgContext, point.x(), point.y());
+ wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false);
+
+ IntSize shadowSize;
+ int shadowBlur;
+ Color shadowColor;
+ graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor);
+
+ bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur;
+ if (hasSimpleShadow) {
+ // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
+ graphicsContext->clearShadow();
+ Color fillColor = graphicsContext->fillColor();
+ Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
+ graphicsContext->setFillColor(shadowFillColor);
+ CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width(), point.y() + translation.height() + shadowSize.height());
+ CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
+ if (font->m_syntheticBoldOffset) {
+ CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width() + font->m_syntheticBoldOffset, point.y() + translation.height() + shadowSize.height());
+ CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
+ }
+ graphicsContext->setFillColor(fillColor);
+ }
+
+ CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height());
CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
if (font->m_syntheticBoldOffset) {
- CGContextSetTextPosition(cgContext, point.x() + font->m_syntheticBoldOffset, point.y());
+ CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->m_syntheticBoldOffset, point.y() + translation.height());
CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
}
+ if (hasSimpleShadow)
+ graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor);
+
wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle);
}