diff options
Diffstat (limited to 'WebCore/platform/graphics/win')
27 files changed, 2112 insertions, 569 deletions
diff --git a/WebCore/platform/graphics/win/ColorSafari.cpp b/WebCore/platform/graphics/win/ColorSafari.cpp index e7fbf47..a04fd81 100644 --- a/WebCore/platform/graphics/win/ColorSafari.cpp +++ b/WebCore/platform/graphics/win/ColorSafari.cpp @@ -68,9 +68,4 @@ Color focusRingColor() return focusRingColor; } -void setFocusRingColorChangeFunction(void (*)()) -{ - notImplemented(); -} - } // namespace WebCore 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); } diff --git a/WebCore/platform/graphics/win/FontCacheWin.cpp b/WebCore/platform/graphics/win/FontCacheWin.cpp index 502863b..49b3d76 100644 --- a/WebCore/platform/graphics/win/FontCacheWin.cpp +++ b/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,8 +35,10 @@ #include "UnicodeRange.h" #include <windows.h> #include <mlang.h> +#if PLATFORM(CG) #include <ApplicationServices/ApplicationServices.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> +#endif using std::min; @@ -45,7 +47,9 @@ namespace WebCore void FontCache::platformInit() { +#if PLATFORM(CG) wkSetUpFontCache(1536 * 1024 * 4); // This size matches Mac. +#endif } IMLangFontLink2* FontCache::getFontLinkInterface() @@ -304,55 +308,169 @@ FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fo return getCachedFontPlatformData(fontDescription, timesStr); } -bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family) +static LONG toGDIFontWeight(FontWeight fontWeight) { - LOGFONT winfont; - - // The size here looks unusual. The negative number is intentional. The logical size constant is 32. - winfont.lfHeight = -fontDescription.computedPixelSize() * 32; - winfont.lfWidth = 0; - winfont.lfEscapement = 0; - winfont.lfOrientation = 0; - winfont.lfUnderline = false; - winfont.lfStrikeOut = false; - winfont.lfCharSet = DEFAULT_CHARSET; -#if PLATFORM(CG) - winfont.lfOutPrecision = OUT_TT_ONLY_PRECIS; + static LONG gdiFontWeights[] = { + FW_THIN, // FontWeight100 + FW_EXTRALIGHT, // FontWeight200 + FW_LIGHT, // FontWeight300 + FW_NORMAL, // FontWeight400 + FW_MEDIUM, // FontWeight500 + FW_SEMIBOLD, // FontWeight600 + FW_BOLD, // FontWeight700 + FW_EXTRABOLD, // FontWeight800 + FW_HEAVY // FontWeight900 + }; + return gdiFontWeights[fontWeight]; +} + +static inline bool isGDIFontWeightBold(LONG gdiFontWeight) +{ + return gdiFontWeight >= FW_SEMIBOLD; +} + +static LONG adjustedGDIFontWeight(LONG gdiFontWeight, const String& family) +{ + static AtomicString lucidaStr("Lucida Grande"); + if (equalIgnoringCase(family, lucidaStr)) { + if (gdiFontWeight == FW_NORMAL) + return FW_MEDIUM; + if (gdiFontWeight == FW_BOLD) + return FW_SEMIBOLD; + } + return gdiFontWeight; +} + +struct MatchImprovingProcData { + MatchImprovingProcData(LONG desiredWeight, bool desiredItalic) + : m_desiredWeight(desiredWeight) + , m_desiredItalic(desiredItalic) + , m_hasMatched(false) + { + } + + LONG m_desiredWeight; + bool m_desiredItalic; + bool m_hasMatched; + LOGFONT m_chosen; +}; + +static int CALLBACK matchImprovingEnumProc(CONST LOGFONT* candidate, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) +{ + MatchImprovingProcData* matchData = reinterpret_cast<MatchImprovingProcData*>(lParam); + + if (!matchData->m_hasMatched) { + matchData->m_hasMatched = true; + matchData->m_chosen = *candidate; + return 1; + } + + if (!candidate->lfItalic != !matchData->m_chosen.lfItalic) { + if (!candidate->lfItalic == !matchData->m_desiredItalic) + matchData->m_chosen = *candidate; + + return 1; + } + + unsigned chosenWeightDeltaMagnitude = abs(matchData->m_chosen.lfWeight - matchData->m_desiredWeight); + unsigned candidateWeightDeltaMagnitude = abs(candidate->lfWeight - matchData->m_desiredWeight); + + // If both are the same distance from the desired weight, prefer the candidate if it is further from regular. + if (chosenWeightDeltaMagnitude == candidateWeightDeltaMagnitude && abs(candidate->lfWeight - FW_NORMAL) > abs(matchData->m_chosen.lfWeight - FW_NORMAL)) { + matchData->m_chosen = *candidate; + return 1; + } + + // Otherwise, prefer the one closer to the desired weight. + if (candidateWeightDeltaMagnitude < chosenWeightDeltaMagnitude) + matchData->m_chosen = *candidate; + + return 1; +} + +static HFONT createGDIFont(const AtomicString& family, LONG desiredWeight, bool desiredItalic, int size) +{ + HDC hdc = GetDC(0); + + LOGFONT logFont; + logFont.lfCharSet = DEFAULT_CHARSET; + unsigned familyLength = min(family.length(), static_cast<unsigned>(LF_FACESIZE - 1)); + memcpy(logFont.lfFaceName, family.characters(), familyLength * sizeof(UChar)); + logFont.lfFaceName[familyLength] = 0; + logFont.lfPitchAndFamily = 0; + + MatchImprovingProcData matchData(desiredWeight, desiredItalic); + EnumFontFamiliesEx(hdc, &logFont, matchImprovingEnumProc, reinterpret_cast<LPARAM>(&matchData), 0); + + ReleaseDC(0, hdc); + + if (!matchData.m_hasMatched) + return 0; + + matchData.m_chosen.lfHeight = -size; + matchData.m_chosen.lfWidth = 0; + matchData.m_chosen.lfEscapement = 0; + matchData.m_chosen.lfOrientation = 0; + matchData.m_chosen.lfUnderline = false; + matchData.m_chosen.lfStrikeOut = false; + matchData.m_chosen.lfCharSet = DEFAULT_CHARSET; +#if PLATFORM(CG) || PLATFORM(CAIRO) + matchData.m_chosen.lfOutPrecision = OUT_TT_ONLY_PRECIS; #else - winfont.lfOutPrecision = OUT_TT_PRECIS; + matchData.m_chosen.lfOutPrecision = OUT_TT_PRECIS; #endif - winfont.lfQuality = 5; // Force cleartype. - winfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - winfont.lfItalic = fontDescription.italic(); - - // FIXME: Support weights for real. Do our own enumeration of the available weights. - // We can't rely on Windows here, since we need to follow the CSS2 algorithm for how to fill in - // gaps in the weight list. - // FIXME: Hardcoding Lucida Grande for now. It uses different weights than typical Win32 fonts - // (500/600 instead of 400/700). - static AtomicString lucidaStr("Lucida Grande"); - if (equalIgnoringCase(family, lucidaStr)) - winfont.lfWeight = fontDescription.bold() ? 600 : 500; - else - winfont.lfWeight = fontDescription.bold() ? 700 : 400; - int len = min(family.length(), (unsigned int)LF_FACESIZE - 1); - memcpy(winfont.lfFaceName, family.characters(), len * sizeof(WORD)); - winfont.lfFaceName[len] = '\0'; - - HFONT hfont = CreateFontIndirect(&winfont); - // Windows will always give us a valid pointer here, even if the face name is non-existent. We have to double-check - // and see if the family name was really used. - HDC dc = GetDC(0); - SaveDC(dc); - SelectObject(dc, hfont); - WCHAR name[LF_FACESIZE]; - GetTextFace(dc, LF_FACESIZE, name); - RestoreDC(dc, -1); - ReleaseDC(0, dc); - - DeleteObject(hfont); - - return !wcsicmp(winfont.lfFaceName, name); + matchData.m_chosen.lfQuality = DEFAULT_QUALITY; + matchData.m_chosen.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + return CreateFontIndirect(&matchData.m_chosen); +} + +struct TraitsInFamilyProcData { + TraitsInFamilyProcData(const AtomicString& familyName) + : m_familyName(familyName) + { + } + + const AtomicString& m_familyName; + HashSet<unsigned> m_traitsMasks; +}; + +static int CALLBACK traitsInFamilyEnumProc(CONST LOGFONT* logFont, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) +{ + TraitsInFamilyProcData* procData = reinterpret_cast<TraitsInFamilyProcData*>(lParam); + + unsigned traitsMask = 0; + traitsMask |= logFont->lfItalic ? FontStyleItalicMask : FontStyleNormalMask; + traitsMask |= FontVariantNormalMask; + LONG weight = adjustedGDIFontWeight(logFont->lfWeight, procData->m_familyName); + traitsMask |= weight == FW_THIN ? FontWeight100Mask : + weight == FW_EXTRALIGHT ? FontWeight200Mask : + weight == FW_LIGHT ? FontWeight300Mask : + weight == FW_NORMAL ? FontWeight400Mask : + weight == FW_MEDIUM ? FontWeight500Mask : + weight == FW_SEMIBOLD ? FontWeight600Mask : + weight == FW_BOLD ? FontWeight700Mask : + weight == FW_EXTRABOLD ? FontWeight800Mask : + FontWeight900Mask; + procData->m_traitsMasks.add(traitsMask); + return 1; +} +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) +{ + HDC hdc = GetDC(0); + + LOGFONT logFont; + logFont.lfCharSet = DEFAULT_CHARSET; + unsigned familyLength = min(familyName.length(), static_cast<unsigned>(LF_FACESIZE - 1)); + memcpy(logFont.lfFaceName, familyName.characters(), familyLength * sizeof(UChar)); + logFont.lfFaceName[familyLength] = 0; + logFont.lfPitchAndFamily = 0; + + TraitsInFamilyProcData procData(familyName); + EnumFontFamiliesEx(hdc, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0); + copyToVector(procData.m_traitsMasks, traitsMasks); + + ReleaseDC(0, hdc); } FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) @@ -364,57 +482,34 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD bool useGDI = fontDescription.renderingMode() == AlternateRenderingMode && !isLucidaGrande; - LOGFONT winfont; - - // The size here looks unusual. The negative number is intentional. The logical size constant is 32. We do this - // for subpixel precision when rendering using Uniscribe. This masks rounding errors related to the HFONT metrics being - // different from the CGFont metrics. - // FIXME: We will eventually want subpixel precision for GDI mode, but the scaled rendering doesn't look as nice. That may be solvable though. - winfont.lfHeight = -fontDescription.computedPixelSize() * (useGDI ? 1 : 32); - winfont.lfWidth = 0; - winfont.lfEscapement = 0; - winfont.lfOrientation = 0; - winfont.lfUnderline = false; - winfont.lfStrikeOut = false; - winfont.lfCharSet = DEFAULT_CHARSET; - winfont.lfOutPrecision = OUT_TT_ONLY_PRECIS; - winfont.lfQuality = DEFAULT_QUALITY; - winfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - winfont.lfItalic = fontDescription.italic(); - - // FIXME: Support weights for real. Do our own enumeration of the available weights. - // We can't rely on Windows here, since we need to follow the CSS2 algorithm for how to fill in - // gaps in the weight list. - // FIXME: Hardcoding Lucida Grande for now. It uses different weights than typical Win32 fonts - // (500/600 instead of 400/700). - if (isLucidaGrande) { - winfont.lfWeight = fontDescription.bold() ? 600 : 500; - useGDI = false; // Never use GDI for Lucida Grande. - } else - winfont.lfWeight = fontDescription.bold() ? 700 : 400; - int len = min(family.length(), (unsigned int)LF_FACESIZE - 1); - memcpy(winfont.lfFaceName, family.characters(), len * sizeof(WORD)); - winfont.lfFaceName[len] = '\0'; - - HFONT hfont = CreateFontIndirect(&winfont); - // Windows will always give us a valid pointer here, even if the face name is non-existent. We have to double-check - // and see if the family name was really used. - HDC dc = GetDC(0); - SaveDC(dc); - SelectObject(dc, hfont); - WCHAR name[LF_FACESIZE]; - GetTextFace(dc, LF_FACESIZE, name); - RestoreDC(dc, -1); - ReleaseDC(0, dc); - - if (_wcsicmp(winfont.lfFaceName, name)) { - DeleteObject(hfont); + // The logical size constant is 32. We do this for subpixel precision when rendering using Uniscribe. + // This masks rounding errors related to the HFONT metrics being different from the CGFont metrics. + // FIXME: We will eventually want subpixel precision for GDI mode, but the scaled rendering doesn't + // look as nice. That may be solvable though. + LONG weight = adjustedGDIFontWeight(toGDIFontWeight(fontDescription.weight()), family); + HFONT hfont = createGDIFont(family, weight, fontDescription.italic(), fontDescription.computedPixelSize() * (useGDI ? 1 : 32)); + + if (!hfont) return 0; - } - - FontPlatformData* result = new FontPlatformData(hfont, fontDescription.computedPixelSize(), - fontDescription.bold(), fontDescription.italic(), useGDI); - if (!result->cgFont()) { + + if (isLucidaGrande) + useGDI = false; // Never use GDI for Lucida Grande. + + LOGFONT logFont; + GetObject(hfont, sizeof(LOGFONT), &logFont); + + bool synthesizeBold = isGDIFontWeightBold(weight) && !isGDIFontWeightBold(logFont.lfWeight); + bool synthesizeItalic = fontDescription.italic() && !logFont.lfItalic; + + FontPlatformData* result = new FontPlatformData(hfont, fontDescription.computedPixelSize(), synthesizeBold, synthesizeItalic, useGDI); + +#if PLATFORM(CG) + bool fontCreationFailed = !result->cgFont(); +#elif PLATFORM(CAIRO) + bool fontCreationFailed = !result->fontFace(); +#endif + + if (fontCreationFailed) { // The creation of the CGFontRef failed for some reason. We already asserted in debug builds, but to make // absolutely sure that we don't use this font, go ahead and return 0 so that we can fall back to the next // font. diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp index cf03502..26fceba 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,21 +21,72 @@ #include "config.h" #include "FontCustomPlatformData.h" -#include <wtf/RetainPtr.h> -#include <ApplicationServices/ApplicationServices.h> -#include "SharedBuffer.h" +#include "Base64.h" #include "FontPlatformData.h" +#include "OpenTypeUtilities.h" +#include "SharedBuffer.h" +#include "SoftLinking.h" +#include <ApplicationServices/ApplicationServices.h> +#include <wtf/RetainPtr.h> + +// From t2embapi.h, which is missing from the Microsoft Platform SDK. +typedef unsigned long(WINAPIV *READEMBEDPROC) (void*, void*, unsigned long); +struct TTLOADINFO; +#define TTLOAD_PRIVATE 0x00000001 +#define LICENSE_PREVIEWPRINT 0x0004 +#define E_NONE 0x0000L namespace WebCore { +using namespace std; + +SOFT_LINK_LIBRARY(T2embed); +SOFT_LINK(T2embed, TTLoadEmbeddedFont, LONG, __stdcall, (HANDLE* phFontReference, ULONG ulFlags, ULONG* pulPrivStatus, ULONG ulPrivs, ULONG* pulStatus, READEMBEDPROC lpfnReadFromStream, LPVOID lpvReadStream, LPWSTR szWinFamilyName, LPSTR szMacFamilyName, TTLOADINFO* pTTLoadInfo), (phFontReference, ulFlags,pulPrivStatus, ulPrivs, pulStatus, lpfnReadFromStream, lpvReadStream, szWinFamilyName, szMacFamilyName, pTTLoadInfo)); +SOFT_LINK(T2embed, TTGetNewFontName, LONG, __stdcall, (HANDLE* phFontReference, LPWSTR szWinFamilyName, long cchMaxWinName, LPSTR szMacFamilyName, long cchMaxMacName), (phFontReference, szWinFamilyName, cchMaxWinName, szMacFamilyName, cchMaxMacName)); +SOFT_LINK(T2embed, TTDeleteEmbeddedFont, LONG, __stdcall, (HANDLE hFontReference, ULONG ulFlags, ULONG* pulStatus), (hFontReference, ulFlags, pulStatus)); + FontCustomPlatformData::~FontCustomPlatformData() { CGFontRelease(m_cgFont); + if (m_fontReference) { + if (m_name.isNull()) { + ASSERT(T2embedLibrary()); + ULONG status; + TTDeleteEmbeddedFont(m_fontReference, 0, &status); + } else + RemoveFontMemResourceEx(m_fontReference); + } } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode renderingMode) { - return FontPlatformData(m_cgFont, size, bold, italic); + ASSERT(m_cgFont); + ASSERT(m_fontReference); + ASSERT(T2embedLibrary()); + + LOGFONT logFont; + if (m_name.isNull()) + TTGetNewFontName(&m_fontReference, logFont.lfFaceName, LF_FACESIZE, 0, 0); + else + memcpy(logFont.lfFaceName, m_name.charactersWithNullTermination(), sizeof(logFont.lfFaceName[0]) * min(static_cast<size_t>(LF_FACESIZE), 1 + m_name.length())); + + logFont.lfHeight = -size; + if (renderingMode == NormalRenderingMode) + logFont.lfHeight *= 32; + logFont.lfWidth = 0; + logFont.lfEscapement = 0; + logFont.lfOrientation = 0; + logFont.lfUnderline = false; + logFont.lfStrikeOut = false; + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfOutPrecision = OUT_TT_ONLY_PRECIS; + logFont.lfQuality = CLEARTYPE_QUALITY; + logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + logFont.lfItalic = italic; + logFont.lfWeight = bold ? 700 : 400; + + HFONT hfont = CreateFontIndirect(&logFont); + return FontPlatformData(hfont, m_cgFont, size, bold, italic, renderingMode == AlternateRenderingMode); } const void* getData(void* info) @@ -60,9 +111,82 @@ size_t getBytesWithOffset(void *info, void* buffer, size_t offset, size_t count) return availBytes; } +// Streams the concatenation of a header and font data. +class EOTStream { +public: + EOTStream(const Vector<UInt8, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) + : m_eotHeader(eotHeader) + , m_fontData(fontData) + , m_overlayDst(overlayDst) + , m_overlaySrc(overlaySrc) + , m_overlayLength(overlayLength) + , m_offset(0) + , m_inHeader(true) + { + } + + size_t read(void* buffer, size_t count); + +private: + const Vector<UInt8, 512>& m_eotHeader; + const SharedBuffer* m_fontData; + size_t m_overlayDst; + size_t m_overlaySrc; + size_t m_overlayLength; + size_t m_offset; + bool m_inHeader; +}; + +size_t EOTStream::read(void* buffer, size_t count) +{ + size_t bytesToRead = count; + if (m_inHeader) { + size_t bytesFromHeader = min(m_eotHeader.size() - m_offset, count); + memcpy(buffer, m_eotHeader.data() + m_offset, bytesFromHeader); + m_offset += bytesFromHeader; + bytesToRead -= bytesFromHeader; + if (m_offset == m_eotHeader.size()) { + m_inHeader = false; + m_offset = 0; + } + } + if (bytesToRead && !m_inHeader) { + size_t bytesFromData = min(m_fontData->size() - m_offset, bytesToRead); + memcpy(buffer, m_fontData->data() + m_offset, bytesFromData); + if (m_offset < m_overlayDst + m_overlayLength && m_offset + bytesFromData >= m_overlayDst) { + size_t dstOffset = max<int>(m_overlayDst - m_offset, 0); + size_t srcOffset = max<int>(0, m_offset - m_overlayDst); + size_t bytesToCopy = min(bytesFromData - dstOffset, m_overlayLength - srcOffset); + memcpy(reinterpret_cast<char*>(buffer) + dstOffset, m_fontData->data() + m_overlaySrc + srcOffset, bytesToCopy); + } + m_offset += bytesFromData; + bytesToRead -= bytesFromData; + } + return count - bytesToRead; +} + +static unsigned long WINAPIV readEmbedProc(void* stream, void* buffer, unsigned long length) +{ + return static_cast<EOTStream*>(stream)->read(buffer, length); +} + +// Creates a unique and unpredictable font name, in order to avoid collisions and to +// not allow access from CSS. +static String createUniqueFontName() +{ + Vector<char> fontUuid(sizeof(GUID)); + CoCreateGuid(reinterpret_cast<GUID*>(fontUuid.data())); + + Vector<char> fontNameVector; + base64Encode(fontUuid, fontNameVector); + ASSERT(fontNameVector.size() < LF_FACESIZE); + return String(fontNameVector.data(), fontNameVector.size()); +} + FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) { ASSERT_ARG(buffer, buffer); + ASSERT(T2embedLibrary()); // Get CG to create the font. CGDataProviderDirectAccessCallbacks callbacks = { &getData, &releaseData, &getBytesWithOffset, NULL }; @@ -70,7 +194,42 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider.get()); if (!cgFont) return 0; - return new FontCustomPlatformData(cgFont); + + // Introduce the font to GDI. AddFontMemResourceEx cannot be used, because it will pollute the process's + // font namespace (Windows has no API for creating an HFONT from data without exposing the font to the + // entire process first). TTLoadEmbeddedFont lets us override the font family name, so using a unique name + // we avoid namespace collisions. + + String fontName = createUniqueFontName(); + + // TTLoadEmbeddedFont works only with Embedded OpenType (.eot) data, so we need to create an EOT header + // and prepend it to the font data. + Vector<UInt8, 512> eotHeader; + size_t overlayDst; + size_t overlaySrc; + size_t overlayLength; + if (!getEOTHeader(buffer, eotHeader, overlayDst, overlaySrc, overlayLength)) { + CGFontRelease(cgFont); + return 0; + } + + HANDLE fontReference; + ULONG privStatus; + ULONG status; + EOTStream eotStream(eotHeader, buffer, overlayDst, overlaySrc, overlayLength); + + LONG loadEmbeddedFontResult = TTLoadEmbeddedFont(&fontReference, TTLOAD_PRIVATE, &privStatus, LICENSE_PREVIEWPRINT, &status, readEmbedProc, &eotStream, const_cast<LPWSTR>(fontName.charactersWithNullTermination()), 0, 0); + if (loadEmbeddedFontResult == E_NONE) + fontName = String(); + else { + fontReference = renameAndActivateFont(buffer, fontName); + if (!fontReference) { + CGFontRelease(cgFont); + return 0; + } + } + + return new FontCustomPlatformData(cgFont, fontReference, fontName); } } diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.h b/WebCore/platform/graphics/win/FontCustomPlatformData.h index 9f30424..34a9851 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,6 +21,8 @@ #ifndef FontCustomPlatformData_h #define FontCustomPlatformData_h +#include "FontRenderingMode.h" +#include "PlatformString.h" #include <wtf/Noncopyable.h> typedef struct CGFont* CGFontRef; @@ -31,17 +33,23 @@ class FontPlatformData; class SharedBuffer; struct FontCustomPlatformData : Noncopyable { - FontCustomPlatformData(CGFontRef cgFont) - : m_cgFont(cgFont) - {} + FontCustomPlatformData(CGFontRef cgFont, HANDLE fontReference, const String& name) + : m_cgFont(cgFont) + , m_fontReference(fontReference) + , m_name(name) + { + } + ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); CGFontRef m_cgFont; + HANDLE m_fontReference; + String m_name; }; -FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer); +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); } diff --git a/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp new file mode 100644 index 0000000..e54d85a --- /dev/null +++ b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007, 2008 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "FontCustomPlatformDataCairo.h" + +#include "SharedBuffer.h" +#include "FontPlatformData.h" +#include <wtf/RetainPtr.h> + +namespace WebCore { + +FontCustomPlatformDataCairo::~FontCustomPlatformDataCairo() +{ + cairo_font_face_destroy(m_fontFace); +} + +FontPlatformData FontCustomPlatformDataCairo::fontPlatformData(int size, bool bold, bool italic) +{ + return FontPlatformData(m_fontFace, size, bold, italic); +} + +static void releaseData(void* data) +{ + static_cast<SharedBuffer*>(data)->deref(); +} + +FontCustomPlatformDataCairo* createFontCustomPlatformData(SharedBuffer* buffer) +{ + ASSERT_ARG(buffer, buffer); + + buffer->ref(); + HFONT font = reinterpret_cast<HFONT>(buffer); + cairo_font_face_t* fontFace = cairo_win32_font_face_create_for_hfont(font); + if (!fontFace) + return 0; + + static cairo_user_data_key_t bufferKey; + cairo_font_face_set_user_data(fontFace, &bufferKey, buffer, releaseData); + + return new FontCustomPlatformDataCairo(fontFace); +} + +} diff --git a/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h new file mode 100644 index 0000000..87794b5 --- /dev/null +++ b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef FontCustomPlatformDataCairo_h +#define FontCustomPlatformDataCairo_h + +#include <wtf/Noncopyable.h> + +#include <cairo.h> + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformDataCairo : Noncopyable { + FontCustomPlatformDataCairo(cairo_font_face_t* fontFace) + : m_fontFace(fontFace) + { + } + ~FontCustomPlatformDataCairo(); + + FontPlatformData fontPlatformData(int size, bool bold, bool italic); + + cairo_font_face_t* m_fontFace; +}; + +FontCustomPlatformDataCairo* createFontCustomPlatformData(SharedBuffer*); + +} + +#endif diff --git a/WebCore/platform/graphics/win/FontPlatformData.h b/WebCore/platform/graphics/win/FontPlatformData.h index 15d23cd..d61afa8 100644 --- a/WebCore/platform/graphics/win/FontPlatformData.h +++ b/WebCore/platform/graphics/win/FontPlatformData.h @@ -2,7 +2,7 @@ * This file is part of the internal font implementation. It should not be included by anyone other than * FontMac.cpp, FontWin.cpp and Font.cpp. * - * Copyright (C) 2006, 2007 Apple Inc. + * Copyright (C) 2006, 2007, 2008 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,45 +25,58 @@ #define FontPlatformData_H #include "StringImpl.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RetainPtr.h> +#include <wtf/RefCounted.h> -typedef struct HFONT__ *HFONT; -typedef struct CGFont *CGFontRef; +#if PLATFORM(CAIRO) +#include <cairo-win32.h> +#endif + +typedef struct HFONT__* HFONT; +typedef struct CGFont* CGFontRef; namespace WebCore { class FontDescription; -class FontPlatformData -{ +class FontPlatformData { public: - class Deleted {}; - - // Used for deleted values in the font cache's hash tables. - FontPlatformData(Deleted) - : m_font((HFONT)-1) - , m_cgFont(NULL) - , m_size(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_useGDI(false) - {} - FontPlatformData() - : m_font(0) - , m_cgFont(NULL) - , m_size(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_useGDI(false) - {} +#if PLATFORM(CAIRO) + : m_fontFace(0) + , +#else + : +#endif + m_size(0) + , m_syntheticBold(false) + , m_syntheticOblique(false) + , m_useGDI(false) + { + } FontPlatformData(HFONT, float size, bool bold, bool oblique, bool useGDI); FontPlatformData(float size, bool bold, bool oblique); - FontPlatformData(CGFontRef, float size, bool bold, bool oblique); + +#if PLATFORM(CG) + FontPlatformData(HFONT, CGFontRef, float size, bool bold, bool oblique, bool useGDI); +#elif PLATFORM(CAIRO) + FontPlatformData(cairo_font_face_t*, float size, bool bold, bool oblique); +#endif ~FontPlatformData(); - HFONT hfont() const { return m_font; } - CGFontRef cgFont() const { return m_cgFont; } + FontPlatformData(WTF::HashTableDeletedValueType) : m_font(WTF::HashTableDeletedValue) { } + bool isHashTableDeletedValue() const { return m_font.isHashTableDeletedValue(); } + + HFONT hfont() const { return m_font->hfont(); } +#if PLATFORM(CG) + CGFontRef cgFont() const { return m_cgFont.get(); } +#elif PLATFORM(CAIRO) + void setFont(cairo_t* ft) const; + cairo_font_face_t* fontFace() const { return m_fontFace; } + cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } +#endif float size() const { return m_size; } void setSize(float size) { m_size = size; } @@ -73,19 +86,55 @@ public: unsigned hash() const { - return StringImpl::computeHash((UChar*)(&m_font), sizeof(HFONT) / sizeof(UChar)); + return m_font->hash(); } bool operator==(const FontPlatformData& other) const { - return m_font == other.m_font && m_cgFont ==other.m_cgFont && m_size == other.m_size && + return m_font == other.m_font && +#if PLATFORM(CG) + m_cgFont == other.m_cgFont && +#elif PLATFORM(CAIRO) + m_fontFace == other.m_fontFace && + m_scaledFont == other.m_scaledFont && +#endif + m_size == other.m_size && m_syntheticBold == other.m_syntheticBold && m_syntheticOblique == other.m_syntheticOblique && m_useGDI == other.m_useGDI; } private: - HFONT m_font; - CGFontRef m_cgFont; + class RefCountedHFONT : public RefCounted<RefCountedHFONT> { + public: + static PassRefPtr<RefCountedHFONT> create(HFONT hfont) { return adoptRef(new RefCountedHFONT(hfont)); } + static PassRefPtr<RefCountedHFONT> createDeleted() { return adoptRef(new RefCountedHFONT(reinterpret_cast<HFONT>(-1))); } + + ~RefCountedHFONT() { if (m_hfont != reinterpret_cast<HFONT>(-1)) DeleteObject(m_hfont); } + + HFONT hfont() const { return m_hfont; } + unsigned hash() const + { + return StringImpl::computeHash(reinterpret_cast<const UChar*>(&m_hfont), sizeof(HFONT) / sizeof(UChar)); + } + + private: + RefCountedHFONT(HFONT hfont) + : m_hfont(hfont) + { + } + + HFONT m_hfont; + }; + + void platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName); + + RefPtr<RefCountedHFONT> m_font; +#if PLATFORM(CG) + RetainPtr<CGFontRef> m_cgFont; +#elif PLATFORM(CAIRO) + cairo_font_face_t* m_fontFace; + cairo_scaled_font_t* m_scaledFont; +#endif float m_size; bool m_syntheticBold; diff --git a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp new file mode 100644 index 0000000..bbfdb9f --- /dev/null +++ b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp @@ -0,0 +1,134 @@ +/* + * This file is part of the internal font implementation. It should not be included by anyone other than + * FontMac.cpp, FontWin.cpp and Font.cpp. + * + * Copyright (C) 2006, 2007, 2008 Apple Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "FontPlatformData.h" + +#include "PlatformString.h" +#include "StringHash.h" +#include <ApplicationServices/ApplicationServices.h> +#include <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> + +using std::min; + +namespace WebCore { + +static inline USHORT readBigEndianWord(const BYTE* word) { return (word[0] << 8) | word[1]; } + +static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc) +{ + const DWORD cMaxNameTableSize = 1024 * 1024; + + static HashMap<String, RetainPtr<CFStringRef> > nameMap; + + // Check our hash first. + String faceString(faceName); + RetainPtr<CFStringRef> result = nameMap.get(faceString); + if (result) + return result.get(); + + // We need to obtain the PostScript name from the name table and use it instead,. + DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards + if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize) + return NULL; + + Vector<BYTE> bufferVector(bufferSize); + BYTE* buffer = bufferVector.data(); + if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR) + return NULL; + + if (bufferSize < 6) + return NULL; + + USHORT numberOfRecords = readBigEndianWord(buffer + 2); + UINT stringsOffset = readBigEndianWord(buffer + 4); + if (bufferSize < stringsOffset) + return NULL; + + BYTE* strings = buffer + stringsOffset; + + // Now walk each name record looking for a Mac or Windows PostScript name. + UINT offset = 6; + for (int i = 0; i < numberOfRecords; i++) { + if (bufferSize < offset + 12) + return NULL; + + USHORT platformID = readBigEndianWord(buffer + offset); + USHORT encodingID = readBigEndianWord(buffer + offset + 2); + USHORT languageID = readBigEndianWord(buffer + offset + 4); + USHORT nameID = readBigEndianWord(buffer + offset + 6); + USHORT length = readBigEndianWord(buffer + offset + 8); + USHORT nameOffset = readBigEndianWord(buffer + offset + 10); + + if (platformID == 3 && encodingID == 1 && languageID == 0x409 && nameID == 6) { + // This is a Windows PostScript name and is therefore UTF-16. + // Pass Big Endian as the encoding. + if (bufferSize < stringsOffset + nameOffset + length) + return NULL; + result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingUTF16BE, false)); + break; + } else if (platformID == 1 && encodingID == 0 && languageID == 0 && nameID == 6) { + // This is a Mac PostScript name and is therefore ASCII. + // See http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html + if (bufferSize < stringsOffset + nameOffset + length) + return NULL; + result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingASCII, false)); + break; + } + + offset += 12; + } + + if (result) + nameMap.set(faceString, result); + return result.get(); +} + +void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName) +{ + // Try the face name first. Windows may end up localizing this name, and CG doesn't know about + // the localization. If the create fails, we'll try the PostScript name. + RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName))); + m_cgFont.adoptCF(CGFontCreateWithFontName(fullName.get())); + if (!m_cgFont) { + CFStringRef postScriptName = getPostScriptName(fullName.get(), hdc); + if (postScriptName) { + m_cgFont.adoptCF(CGFontCreateWithFontName(postScriptName)); + ASSERT(m_cgFont); + } + } +} + +FontPlatformData::FontPlatformData(HFONT hfont, CGFontRef font, float size, bool bold, bool oblique, bool useGDI) + : m_font(RefCountedHFONT::create(hfont)) + , m_size(size) + , m_cgFont(font) + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) + , m_useGDI(useGDI) +{ +} + +} diff --git a/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp new file mode 100644 index 0000000..438d0a9 --- /dev/null +++ b/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp @@ -0,0 +1,89 @@ +/* + * This file is part of the internal font implementation. It should not be included by anyone other than + * FontMac.cpp, FontWin.cpp and Font.cpp. + * + * Copyright (C) 2006, 2007, 2008 Apple Inc. + * Copyright (C) 2007 Alp Toker + * Copyright (C) 2008 Brent Fulgham + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "FontPlatformData.h" + +#include "PlatformString.h" +#include "StringHash.h" +#include <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> + +#include <cairo-win32.h> + +using std::min; + +namespace WebCore { + +void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName) +{ + m_fontFace = cairo_win32_font_face_create_for_hfont(font); + cairo_matrix_t sizeMatrix, ctm; + cairo_matrix_init_identity(&ctm); + cairo_matrix_init_scale(&sizeMatrix, size, size); + + static cairo_font_options_t* fontOptions = 0; + if (!fontOptions) + { + fontOptions = cairo_font_options_create(); + cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_SUBPIXEL); + } + + m_scaledFont = cairo_scaled_font_create(m_fontFace, &sizeMatrix, &ctm, fontOptions); +} + +FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool oblique) + : m_font(0) + , m_size(size) + , m_fontFace(fontFace) + , m_scaledFont(0) + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) + , m_useGDI(false) +{ + cairo_matrix_t fontMatrix; + cairo_matrix_init_scale(&fontMatrix, size, size); + cairo_matrix_t ctm; + cairo_matrix_init_identity(&ctm); + cairo_font_options_t* options = cairo_font_options_create(); + + // We force antialiasing and disable hinting to provide consistent + // typographic qualities for custom fonts on all platforms. + cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); + + m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options); + cairo_font_options_destroy(options); +} + +void FontPlatformData::setFont(cairo_t* cr) const +{ + ASSERT(m_scaledFont); + + cairo_set_scaled_font(cr, m_scaledFont); +} + +} diff --git a/WebCore/platform/graphics/win/FontPlatformDataWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp index a5fab36..4b4df5a 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp @@ -2,7 +2,8 @@ * This file is part of the internal font implementation. It should not be included by anyone other than * FontMac.cpp, FontWin.cpp and Font.cpp. * - * Copyright (C) 2006, 2007 Apple Inc. + * Copyright (C) 2006, 2007, 2008 Apple Inc. + * Copyright (C) 2008 Brent Fulgham * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,7 +27,6 @@ #include "PlatformString.h" #include "StringHash.h" -#include <ApplicationServices/ApplicationServices.h> #include <wtf/HashMap.h> #include <wtf/RetainPtr.h> #include <wtf/Vector.h> @@ -35,105 +35,17 @@ using std::min; namespace WebCore { -static const int Bold = (1 << 0); -static const int Italic = (1 << 1); -static const int BoldOblique = (1 << 2); - -static inline USHORT readBigEndianWord(const BYTE* word) { return (word[0] << 8) | word[1]; } - -static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc) -{ - const DWORD cMaxNameTableSize = 1024 * 1024; - - static HashMap<String, RetainPtr<CFStringRef> > nameMap; - - // Check our hash first. - String faceString(faceName); - RetainPtr<CFStringRef> result = nameMap.get(faceString); - if (result) - return result.get(); - - // We need to obtain the PostScript name from the name table and use it instead,. - DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards - if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize) - return NULL; - - Vector<BYTE> bufferVector(bufferSize); - BYTE* buffer = bufferVector.data(); - if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR) - return NULL; - - if (bufferSize < 6) - return NULL; - - USHORT numberOfRecords = readBigEndianWord(buffer + 2); - UINT stringsOffset = readBigEndianWord(buffer + 4); - if (bufferSize < stringsOffset) - return NULL; - - BYTE* strings = buffer + stringsOffset; - - // Now walk each name record looking for a Mac or Windows PostScript name. - UINT offset = 6; - for (int i = 0; i < numberOfRecords; i++) { - if (bufferSize < offset + 12) - return NULL; - - USHORT platformID = readBigEndianWord(buffer + offset); - USHORT encodingID = readBigEndianWord(buffer + offset + 2); - USHORT languageID = readBigEndianWord(buffer + offset + 4); - USHORT nameID = readBigEndianWord(buffer + offset + 6); - USHORT length = readBigEndianWord(buffer + offset + 8); - USHORT nameOffset = readBigEndianWord(buffer + offset + 10); - - if (platformID == 3 && encodingID == 1 && languageID == 0x409 && nameID == 6) { - // This is a Windows PostScript name and is therefore UTF-16. - // Pass Big Endian as the encoding. - if (bufferSize < stringsOffset + nameOffset + length) - return NULL; - result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingUTF16BE, false)); - break; - } else if (platformID == 1 && encodingID == 0 && languageID == 0 && nameID == 6) { - // This is a Mac PostScript name and is therefore ASCII. - // See http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html - if (bufferSize < stringsOffset + nameOffset + length) - return NULL; - result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingASCII, false)); - break; - } - - offset += 12; - } - - if (result) - nameMap.set(faceString, result); - return result.get(); -} - -static int CALLBACK enumStylesCallback(const LOGFONT* logFont, const TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) -{ - int *style = reinterpret_cast<int*>(lParam); - - // FIXME: In order to accommodate Lucida we go ahead and consider a weight of 600 to be bold. - // This does mean we'll consider demibold and semibold fonts on windows to also be bold. This - // is rare enough that it seems like an ok hack for now. - if (logFont->lfWeight >= 600) { - if (logFont->lfItalic) - *style |= BoldOblique; - else - *style |= Bold; - } else if (logFont->lfItalic) - *style |= Italic; - - return 1; -} - FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool oblique, bool useGDI) - : m_font(font) + : m_font(RefCountedHFONT::create(font)) , m_size(size) +#if PLATFORM(CG) , m_cgFont(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) +#elif PLATFORM(CAIRO) + , m_fontFace(0) + , m_scaledFont(0) +#endif + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) , m_useGDI(useGDI) { HDC hdc = GetDC(0); @@ -142,54 +54,16 @@ FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool obliq SelectObject(hdc, font); UINT bufferSize = GetOutlineTextMetrics(hdc, 0, NULL); - ASSERT_WITH_MESSAGE(bufferSize != 0, "Bitmap fonts not supported with CoreGraphics."); + ASSERT_WITH_MESSAGE(bufferSize, "Bitmap fonts not supported with CoreGraphics."); - if (bufferSize != 0) { + if (bufferSize) { OUTLINETEXTMETRICW* metrics = (OUTLINETEXTMETRICW*)malloc(bufferSize); GetOutlineTextMetricsW(hdc, bufferSize, metrics); WCHAR* faceName = (WCHAR*)((uintptr_t)metrics + (uintptr_t)metrics->otmpFaceName); - if (!useGDI && (bold || oblique)) { - LOGFONT logFont; - - int len = min((int)wcslen(faceName), LF_FACESIZE - 1); - memcpy(logFont.lfFaceName, faceName, len * sizeof(WORD)); - logFont.lfFaceName[len] = '\0'; - logFont.lfCharSet = metrics->otmTextMetrics.tmCharSet; - logFont.lfPitchAndFamily = 0; - - int styles = 0; - EnumFontFamiliesEx(hdc, &logFont, enumStylesCallback, reinterpret_cast<LPARAM>(&styles), 0); - - // Check if we need to synthesize bold or oblique. The rule that complicates things here - // is that if the requested font is bold and oblique, and both a bold font and an oblique font - // exist, the bold font should be used, and oblique synthesized. - if (bold && oblique) { - if (styles == 0) { - m_syntheticBold = true; - m_syntheticOblique = true; - } else if (styles & Bold) - m_syntheticOblique = true; - else if (styles & Italic) - m_syntheticBold = true; - } else if (bold && (!(styles & Bold))) - m_syntheticBold = true; - else if (oblique && !(styles & Italic)) - m_syntheticOblique = true; - } + platformDataInit(font, size, hdc, faceName); - // Try the face name first. Windows may end up localizing this name, and CG doesn't know about - // the localization. If the create fails, we'll try the PostScript name. - RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName))); - m_cgFont = CGFontCreateWithFontName(fullName.get()); - if (!m_cgFont) { - CFStringRef postScriptName = getPostScriptName(fullName.get(), hdc); - if (postScriptName) { - m_cgFont = CGFontCreateWithFontName(postScriptName); - ASSERT(m_cgFont); - } - } free(metrics); } @@ -198,19 +72,13 @@ FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool obliq } FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) - : m_font(0) - , m_size(size) + : m_size(size) +#if PLATFORM(CG) , m_cgFont(0) - , m_syntheticBold(bold) - , m_syntheticOblique(oblique) - , m_useGDI(false) -{ -} - -FontPlatformData::FontPlatformData(CGFontRef font, float size, bool bold, bool oblique) - : m_font(0) - , m_size(size) - , m_cgFont(font) +#elif PLATFORM(CAIRO) + , m_fontFace(0) + , m_scaledFont(0) +#endif , m_syntheticBold(bold) , m_syntheticOblique(oblique) , m_useGDI(false) diff --git a/WebCore/platform/graphics/win/GlyphPageTreeNodeWin.cpp b/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp index 23b756c..c11fc1b 100644 --- a/WebCore/platform/graphics/win/GlyphPageTreeNodeWin.cpp +++ b/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,9 +36,9 @@ namespace WebCore { bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { - // The bufferLength will be greater than the glyph page size if the buffer has Unicode supplementary characters. + // bufferLength will be greater than the requested number of glyphs if the buffer contains surrogate pairs. // We won't support this for now. - if (bufferLength > GlyphPage::size) + if (bufferLength > length) return false; bool haveGlyphs = false; diff --git a/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp b/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp new file mode 100644 index 0000000..b679ced --- /dev/null +++ b/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GlyphPageTreeNode.h" + +#include "SimpleFontData.h" + +namespace WebCore { + +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + // bufferLength will be greater than the requested number of glyphs if the buffer contains surrogate pairs. + // We won't support this for now. + if (bufferLength > length) + return false; + + bool haveGlyphs = false; + + HDC dc = GetDC((HWND)0); + SaveDC(dc); + SelectObject(dc, fontData->platformData().hfont()); + + TEXTMETRIC tm; + GetTextMetrics(dc, &tm); + + WORD localGlyphBuffer[GlyphPage::size * 2]; + DWORD result = GetGlyphIndices(dc, buffer, bufferLength, localGlyphBuffer, 0); + bool success = result != GDI_ERROR && static_cast<unsigned>(result) == bufferLength; + if (success) { + for (unsigned i = 0; i < length; i++) { + Glyph glyph = localGlyphBuffer[i]; + if (!glyph) + setGlyphDataForIndex(offset + i, 0, 0); + else { + setGlyphDataForIndex(offset + i, glyph, fontData); + haveGlyphs = true; + } + } + } + RestoreDC(dc, -1); + ReleaseDC(0, dc); + + return haveGlyphs; +} + +} diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index 58829b5..5a4279a 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -38,7 +38,7 @@ using namespace std; namespace WebCore { -static CGContextRef CGContextWithHDC(HDC hdc) +static CGContextRef CGContextWithHDC(HDC hdc, bool hasAlpha) { HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); @@ -46,8 +46,10 @@ static CGContextRef CGContextWithHDC(HDC hdc) GetObject(bitmap, sizeof(info), &info); ASSERT(info.bmBitsPixel == 32); + + CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | (hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst); CGContextRef context = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, - info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst); + info.bmWidthBytes, deviceRGB, bitmapInfo); CGColorSpaceRelease(deviceRGB); // Flip coords @@ -60,9 +62,9 @@ static CGContextRef CGContextWithHDC(HDC hdc) return context; } -GraphicsContext::GraphicsContext(HDC hdc) +GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha) : m_common(createGraphicsContextPrivate()) - , m_data(new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc))) + , m_data(new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc, hasAlpha))) { CGContextRelease(m_data->m_cgContext); m_data->m_hdc = hdc; @@ -76,9 +78,12 @@ GraphicsContext::GraphicsContext(HDC hdc) bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } -HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend) +// FIXME: Is it possible to merge getWindowsContext and createWindowsBitmap into a single API +// suitable for all clients? +HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { - if (inTransparencyLayer()) { + // FIXME: Should a bitmap be created also when a shadow is set? + if (mayCreateBitmap && inTransparencyLayer()) { if (dstRect.isEmpty()) return 0; @@ -133,9 +138,9 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha return m_data->m_hdc; } -void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend) +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { - if (hdc && inTransparencyLayer()) { + if (mayCreateBitmap && hdc && inTransparencyLayer()) { if (dstRect.isEmpty()) return; @@ -168,75 +173,60 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo m_data->restore(); } -void GraphicsContextPlatformPrivate::save() +GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size) + : m_hdc(0) + , m_size(size) { - if (!m_hdc) + BITMAPINFO bitmapInfo; + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = m_size.width(); + bitmapInfo.bmiHeader.biHeight = m_size.height(); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + bitmapInfo.bmiHeader.biSizeImage = 0; + bitmapInfo.bmiHeader.biXPelsPerMeter = 0; + bitmapInfo.bmiHeader.biYPelsPerMeter = 0; + bitmapInfo.bmiHeader.biClrUsed = 0; + bitmapInfo.bmiHeader.biClrImportant = 0; + + m_bitmap = CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&m_bitmapBuffer), 0, 0); + if (!m_bitmap) return; - SaveDC(m_hdc); -} -void GraphicsContextPlatformPrivate::restore() -{ - if (!m_hdc) - return; - RestoreDC(m_hdc, -1); -} + m_hdc = CreateCompatibleDC(hdc); + SelectObject(m_hdc, m_bitmap); -void GraphicsContextPlatformPrivate::clip(const IntRect& clipRect) -{ - if (!m_hdc) - return; - IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); -} + BITMAP bmpInfo; + GetObject(m_bitmap, sizeof(bmpInfo), &bmpInfo); + m_bytesPerRow = bmpInfo.bmWidthBytes; + m_bitmapBufferLength = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; -void GraphicsContextPlatformPrivate::clip(const Path&) -{ - notImplemented(); + SetGraphicsMode(m_hdc, GM_ADVANCED); } -void GraphicsContextPlatformPrivate::scale(const FloatSize& size) +GraphicsContext::WindowsBitmap::~WindowsBitmap() { - if (!m_hdc) + if (!m_bitmap) return; - XFORM xform; - xform.eM11 = size.width(); - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = size.height(); - xform.eDx = 0.0f; - xform.eDy = 0.0f; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} -static const double deg2rad = 0.017453292519943295769; // pi/180 + DeleteDC(m_hdc); + DeleteObject(m_bitmap); +} -void GraphicsContextPlatformPrivate::rotate(float degreesAngle) +GraphicsContext::WindowsBitmap* GraphicsContext::createWindowsBitmap(IntSize size) { - float radiansAngle = degreesAngle * deg2rad; - float cosAngle = cosf(radiansAngle); - float sinAngle = sinf(radiansAngle); - XFORM xform; - xform.eM11 = cosAngle; - xform.eM12 = -sinAngle; - xform.eM21 = sinAngle; - xform.eM22 = cosAngle; - xform.eDx = 0.0f; - xform.eDy = 0.0f; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); + return new WindowsBitmap(m_data->m_hdc, size); } -void GraphicsContextPlatformPrivate::translate(float x , float y) +void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point) { - if (!m_hdc) - return; - XFORM xform; - xform.eM11 = 1.0f; - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = 1.0f; - xform.eDx = x; - xform.eDy = y; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); + RetainPtr<CGColorSpaceRef> deviceRGB(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CFDataRef> imageData(AdoptCF, CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, image->buffer(), image->bufferLength(), kCFAllocatorNull)); + RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(imageData.get())); + RetainPtr<CGImageRef> cgImage(AdoptCF, CGImageCreate(image->size().width(), image->size().height(), 8, 32, image->bytesPerRow(), deviceRGB.get(), + kCGBitmapByteOrder32Little | kCGImageAlphaFirst, dataProvider.get(), 0, true, kCGRenderingIntentDefault)); + CGContextDrawImage(m_data->m_cgContext, CGRectMake(point.x(), point.y(), image->size().width(), image->size().height()), cgImage.get()); } void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index c3fbdd7..3dcf6ba 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -37,7 +37,7 @@ using namespace std; namespace WebCore { -GraphicsContext::GraphicsContext(HDC dc) +GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate) { @@ -58,8 +58,13 @@ GraphicsContext::GraphicsContext(HDC dc) } } -HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend) +HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { + // FIXME: We aren't really doing anything with the 'mayCreateBitmap' flag. This needs + // to be addressed. + if (dstRect.isEmpty()) + return 0; + // This is probably wrong, and definitely out of date. Pulled from old SVN cairo_surface_t* surface = cairo_get_target(platformContext()); HDC hdc = cairo_win32_surface_get_dc(surface); @@ -77,36 +82,41 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha xform.eM22 = mat.yy; xform.eDx = mat.x0; xform.eDy = mat.y0; - SetWorldTransform(hdc, &xform); + ::SetWorldTransform(hdc, &xform); return hdc; } bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } -void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend) +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { + // FIXME: We aren't really doing anything with the 'mayCreateBitmap' flag. This needs + // to be addressed. + if (dstRect.isEmpty()) + return; + cairo_surface_t* surface = cairo_get_target(platformContext()); HDC hdc2 = cairo_win32_surface_get_dc(surface); RestoreDC(hdc2, -1); cairo_surface_mark_dirty(surface); } -void GraphicsContext::concatCTM(const AffineTransform& transform) +void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) { - cairo_surface_t* surface = cairo_get_target(platformContext()); + cairo_surface_t* surface = cairo_get_target(cr); HDC hdc = cairo_win32_surface_get_dc(surface); SaveDC(hdc); - cairo_matrix_t mat; - cairo_get_matrix(platformContext(), &mat); + const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); + XFORM xform; - xform.eM11 = mat.xx; - xform.eM12 = mat.xy; - xform.eM21 = mat.yx; - xform.eM22 = mat.yy; - xform.eDx = mat.x0; - xform.eDy = mat.y0; + xform.eM11 = matrix->xx; + xform.eM12 = matrix->xy; + xform.eM21 = matrix->yx; + xform.eM22 = matrix->yy; + xform.eDx = matrix->x0; + xform.eDy = matrix->y0; ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); } diff --git a/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/WebCore/platform/graphics/win/GraphicsContextWin.cpp index a8f2148..dbf9fad 100644 --- a/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -26,6 +26,12 @@ #include "config.h" #include "GraphicsContext.h" +#if PLATFORM(CG) +#include "GraphicsContextPlatformPrivateCG.h" +#elif PLATFORM(CAIRO) +#include "GraphicsContextPlatformPrivateCairo.h" +#endif + #include "AffineTransform.h" #include "NotImplemented.h" #include "Path.h" @@ -37,6 +43,77 @@ namespace WebCore { class SVGResourceImage; +void GraphicsContextPlatformPrivate::save() +{ + if (!m_hdc) + return; + SaveDC(m_hdc); +} + +void GraphicsContextPlatformPrivate::restore() +{ + if (!m_hdc) + return; + RestoreDC(m_hdc, -1); +} + +void GraphicsContextPlatformPrivate::clip(const FloatRect& clipRect) +{ + if (!m_hdc) + return; + IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); +} + +void GraphicsContextPlatformPrivate::clip(const Path&) +{ + notImplemented(); +} + +void GraphicsContextPlatformPrivate::scale(const FloatSize& size) +{ + if (!m_hdc) + return; + XFORM xform; + xform.eM11 = size.width(); + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = size.height(); + xform.eDx = 0.0f; + xform.eDy = 0.0f; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +static const double deg2rad = 0.017453292519943295769; // pi/180 + +void GraphicsContextPlatformPrivate::rotate(float degreesAngle) +{ + float radiansAngle = degreesAngle * deg2rad; + float cosAngle = cosf(radiansAngle); + float sinAngle = sinf(radiansAngle); + XFORM xform; + xform.eM11 = cosAngle; + xform.eM12 = -sinAngle; + xform.eM21 = sinAngle; + xform.eM22 = cosAngle; + xform.eDx = 0.0f; + xform.eDy = 0.0f; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +void GraphicsContextPlatformPrivate::translate(float x , float y) +{ + if (!m_hdc) + return; + XFORM xform; + xform.eM11 = 1.0f; + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = 1.0f; + xform.eDx = x; + xform.eDy = y; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + #if ENABLE(SVG) GraphicsContext* contextForImage(SVGResourceImage*) { diff --git a/WebCore/platform/graphics/win/IconWin.cpp b/WebCore/platform/graphics/win/IconWin.cpp index 60cebe3..c02b56e 100644 --- a/WebCore/platform/graphics/win/IconWin.cpp +++ b/WebCore/platform/graphics/win/IconWin.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2006, 2007 Apple Inc. +* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,27 +23,25 @@ #include "GraphicsContext.h" #include "PlatformString.h" +#include <tchar.h> #include <windows.h> namespace WebCore { -Icon::Icon() - : m_hIcon(0) -{ -} +static const int shell32MultipleFileIconIndex = 54; Icon::Icon(HICON icon) : m_hIcon(icon) { + ASSERT(icon); } Icon::~Icon() { - if (m_hIcon) - DestroyIcon(m_hIcon); + DestroyIcon(m_hIcon); } -PassRefPtr<Icon> Icon::newIconForFile(const String& filename) +PassRefPtr<Icon> Icon::createIconForFile(const String& filename) { SHFILEINFO sfi; memset(&sfi, 0, sizeof(sfi)); @@ -52,9 +50,23 @@ PassRefPtr<Icon> Icon::newIconForFile(const String& filename) if (!SHGetFileInfo(tmpFilename.charactersWithNullTermination(), 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SHELLICONSIZE | SHGFI_SMALLICON)) return 0; - Icon* icon = new Icon(); - icon->m_hIcon = sfi.hIcon; - return icon; + return adoptRef(new Icon(sfi.hIcon)); +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>&) +{ + TCHAR buffer[MAX_PATH]; + UINT length = ::GetSystemDirectory(buffer, ARRAYSIZE(buffer)); + if (!length) + return 0; + + if (_tcscat_s(buffer, TEXT("\\shell32.dll"))) + return 0; + + HICON hIcon; + if (!::ExtractIconEx(buffer, shell32MultipleFileIconIndex, 0, &hIcon, 1)) + return 0; + return adoptRef(new Icon(hIcon)); } void Icon::paint(GraphicsContext* context, const IntRect& r) diff --git a/WebCore/platform/graphics/win/ImageWin.cpp b/WebCore/platform/graphics/win/ImageWin.cpp index 2d3a87a..54c5b41 100644 --- a/WebCore/platform/graphics/win/ImageWin.cpp +++ b/WebCore/platform/graphics/win/ImageWin.cpp @@ -42,12 +42,12 @@ void BitmapImage::invalidatePlatformData() { } -Image* Image::loadPlatformResource(const char *name) +PassRefPtr<Image> Image::loadPlatformResource(const char *name) { RefPtr<SharedBuffer> buffer = loadResourceIntoBuffer(name); - BitmapImage* img = new BitmapImage; + RefPtr<BitmapImage> img = BitmapImage::create(); img->setData(buffer.release(), true); - return img; + return img.release(); } bool BitmapImage::getHBITMAP(HBITMAP bmp) diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index 65b3db6..cef4217 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -28,13 +28,22 @@ #if ENABLE(VIDEO) #include "MediaPlayerPrivateQuickTimeWin.h" -#include "DeprecatedString.h" #include "GraphicsContext.h" #include "KURL.h" #include "QTMovieWin.h" #include "ScrollView.h" #include <wtf/MathExtras.h> +#if DRAW_FRAME_RATE +#include "Font.h" +#include "FrameView.h" +#include "Frame.h" +#include "Document.h" +#include "RenderObject.h" +#include "RenderStyle.h" +#include "Windows.h" +#endif + using namespace std; namespace WebCore { @@ -51,6 +60,11 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_readyState(MediaPlayer::DataUnavailable) , m_startedPlaying(false) , m_isStreaming(false) +#if DRAW_FRAME_RATE + , m_frameCountWhilePlaying(0) + , m_timeStartedPlaying(0) + , m_timeStoppedPlaying(0) +#endif { } @@ -88,6 +102,9 @@ void MediaPlayerPrivate::play() if (!m_qtMovie) return; m_startedPlaying = true; +#if DRAW_FRAME_RATE + m_frameCountWhilePlaying = 0; +#endif m_qtMovie->play(); startEndPointTimerIfNeeded(); @@ -98,6 +115,9 @@ void MediaPlayerPrivate::pause() if (!m_qtMovie) return; m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = GetTickCount(); +#endif m_qtMovie->pause(); m_endPointTimer.stop(); } @@ -304,7 +324,7 @@ void MediaPlayerPrivate::updateStates() long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError; - if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData) { + if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData && !m_player->inMediaDocument()) { unsigned enabledTrackCount; m_qtMovie->disableUnsupportedTracks(enabledTrackCount); // FIXME: We should differentiate between load errors and decode errors <rdar://problem/5605692> @@ -352,6 +372,9 @@ void MediaPlayerPrivate::didEnd() { m_endPointTimer.stop(); m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = GetTickCount(); +#endif updateStates(); m_player->timeChanged(); } @@ -376,6 +399,31 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) HDC hdc = p->getWindowsContext(r); m_qtMovie->paint(hdc, r.x(), r.y()); p->releaseWindowsContext(hdc, r); + +#if DRAW_FRAME_RATE + if (m_frameCountWhilePlaying > 10) { + Frame* frame = m_player->m_frameView ? m_player->m_frameView->frame() : NULL; + Document* document = frame ? frame->document() : NULL; + RenderObject* renderer = document ? document->renderer() : NULL; + RenderStyle* styleToUse = renderer ? renderer->style() : NULL; + if (styleToUse) { + double frameRate = (m_frameCountWhilePlaying - 1) / (0.001 * ( m_startedPlaying ? (GetTickCount() - m_timeStartedPlaying) : + (m_timeStoppedPlaying - m_timeStartedPlaying) )); + String text = String::format("%1.2f", frameRate); + TextRun textRun(text.characters(), text.length()); + const Color color(255, 0, 0); + p->save(); + p->translate(r.x(), r.y() + r.height()); + p->setFont(styleToUse->font()); + p->setStrokeColor(color); + p->setStrokeStyle(SolidStroke); + p->setStrokeThickness(1.0f); + p->setFillColor(color); + p->drawText(textRun, IntPoint(2, -3)); + p->restore(); + } + } +#endif } void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) @@ -417,6 +465,15 @@ void MediaPlayerPrivate::movieTimeChanged(QTMovieWin* movie) void MediaPlayerPrivate::movieNewImageAvailable(QTMovieWin* movie) { ASSERT(m_qtMovie.get() == movie); +#if DRAW_FRAME_RATE + if (m_startedPlaying) { + m_frameCountWhilePlaying++; + // to eliminate preroll costs from our calculation, + // our frame rate calculation excludes the first frame drawn after playback starts + if (1==m_frameCountWhilePlaying) + m_timeStartedPlaying = GetTickCount(); + } +#endif m_player->repaint(); } diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h index 37b5b05..c4c893c 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -33,6 +33,10 @@ #include <QTMovieWin.h> #include <wtf/OwnPtr.h> +#ifndef DRAW_FRAME_RATE +#define DRAW_FRAME_RATE 0 +#endif + namespace WebCore { class GraphicsContext; @@ -111,6 +115,11 @@ private: MediaPlayer::ReadyState m_readyState; bool m_startedPlaying; bool m_isStreaming; +#if DRAW_FRAME_RATE + int m_frameCountWhilePlaying; + int m_timeStartedPlaying; + int m_timeStoppedPlaying; +#endif }; } diff --git a/WebCore/platform/graphics/win/OpenTypeUtilities.cpp b/WebCore/platform/graphics/win/OpenTypeUtilities.cpp new file mode 100644 index 0000000..1951320 --- /dev/null +++ b/WebCore/platform/graphics/win/OpenTypeUtilities.cpp @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OpenTypeUtilities.h" + +#include "SharedBuffer.h" + +namespace WebCore { + +struct BigEndianUShort { + operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; } + BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { } + unsigned short v; +}; + +struct BigEndianULong { + operator unsigned() const { return (v & 0xff) << 24 | (v & 0xff00) << 8 | (v & 0xff0000) >> 8 | v >> 24; } + BigEndianULong(unsigned u) : v((u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24) { } + unsigned v; +}; + +#pragma pack(1) + +struct EOTPrefix { + unsigned eotSize; + unsigned fontDataSize; + unsigned version; + unsigned flags; + UInt8 fontPANOSE[10]; + UInt8 charset; + UInt8 italic; + unsigned weight; + unsigned short fsType; + unsigned short magicNumber; + unsigned unicodeRange[4]; + unsigned codePageRange[2]; + unsigned checkSumAdjustment; + unsigned reserved[4]; + unsigned short padding1; +}; + +struct TableDirectoryEntry { + BigEndianULong tag; + BigEndianULong checkSum; + BigEndianULong offset; + BigEndianULong length; +}; + +struct sfntHeader { + Fixed version; + BigEndianUShort numTables; + BigEndianUShort searchRange; + BigEndianUShort entrySelector; + BigEndianUShort rangeShift; + TableDirectoryEntry tables[1]; +}; + +struct OS2Table { + BigEndianUShort version; + BigEndianUShort avgCharWidth; + BigEndianUShort weightClass; + BigEndianUShort widthClass; + BigEndianUShort fsType; + BigEndianUShort subscriptXSize; + BigEndianUShort subscriptYSize; + BigEndianUShort subscriptXOffset; + BigEndianUShort subscriptYOffset; + BigEndianUShort superscriptXSize; + BigEndianUShort superscriptYSize; + BigEndianUShort superscriptXOffset; + BigEndianUShort superscriptYOffset; + BigEndianUShort strikeoutSize; + BigEndianUShort strikeoutPosition; + BigEndianUShort familyClass; + UInt8 panose[10]; + BigEndianULong unicodeRange[4]; + UInt8 vendID[4]; + BigEndianUShort fsSelection; + BigEndianUShort firstCharIndex; + BigEndianUShort lastCharIndex; + BigEndianUShort typoAscender; + BigEndianUShort typoDescender; + BigEndianUShort typoLineGap; + BigEndianUShort winAscent; + BigEndianUShort winDescent; + BigEndianULong codePageRange[2]; + BigEndianUShort xHeight; + BigEndianUShort capHeight; + BigEndianUShort defaultChar; + BigEndianUShort breakChar; + BigEndianUShort maxContext; +}; + +struct headTable { + Fixed version; + Fixed fontRevision; + BigEndianULong checkSumAdjustment; + BigEndianULong magicNumber; + BigEndianUShort flags; + BigEndianUShort unitsPerEm; + long long created; + long long modified; + BigEndianUShort xMin; + BigEndianUShort xMax; + BigEndianUShort yMin; + BigEndianUShort yMax; + BigEndianUShort macStyle; + BigEndianUShort lowestRectPPEM; + BigEndianUShort fontDirectionHint; + BigEndianUShort indexToLocFormat; + BigEndianUShort glyphDataFormat; +}; + +struct nameRecord { + BigEndianUShort platformID; + BigEndianUShort encodingID; + BigEndianUShort languageID; + BigEndianUShort nameID; + BigEndianUShort length; + BigEndianUShort offset; +}; + +struct nameTable { + BigEndianUShort format; + BigEndianUShort count; + BigEndianUShort stringOffset; + nameRecord nameRecords[1]; +}; + +#pragma pack() + +static void appendBigEndianStringToEOTHeader(Vector<UInt8, 512>& eotHeader, const BigEndianUShort* string, unsigned short length) +{ + size_t size = eotHeader.size(); + eotHeader.resize(size + length + 2 * sizeof(unsigned short)); + UChar* dst = reinterpret_cast<UChar*>(eotHeader.data() + size); + unsigned i = 0; + dst[i++] = length; + unsigned numCharacters = length / 2; + for (unsigned j = 0; j < numCharacters; j++) + dst[i++] = string[j]; + dst[i] = 0; +} + +bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength) +{ + overlayDst = 0; + overlaySrc = 0; + overlayLength = 0; + + size_t dataLength = fontData->size(); + const char* data = fontData->data(); + + eotHeader.resize(sizeof(EOTPrefix)); + EOTPrefix* prefix = reinterpret_cast<EOTPrefix*>(eotHeader.data()); + + prefix->fontDataSize = dataLength; + prefix->version = 0x00020001; + prefix->flags = 0; + + if (dataLength < offsetof(sfntHeader, tables)) + return false; + + const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data); + + if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry)) + return false; + + bool haveOS2 = false; + bool haveHead = false; + bool haveName = false; + + const BigEndianUShort* familyName = 0; + unsigned short familyNameLength = 0; + const BigEndianUShort* subfamilyName = 0; + unsigned short subfamilyNameLength = 0; + const BigEndianUShort* fullName = 0; + unsigned short fullNameLength = 0; + const BigEndianUShort* versionString = 0; + unsigned short versionStringLength = 0; + + for (unsigned i = 0; i < sfnt->numTables; i++) { + unsigned tableOffset = sfnt->tables[i].offset; + unsigned tableLength = sfnt->tables[i].length; + + if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength) + return false; + + unsigned tableTag = sfnt->tables[i].tag; + switch (tableTag) { + case 'OS/2': + { + if (dataLength < tableOffset + sizeof(OS2Table)) + return false; + + haveOS2 = true; + const OS2Table* OS2 = reinterpret_cast<const OS2Table*>(data + tableOffset); + for (unsigned j = 0; j < 10; j++) + prefix->fontPANOSE[j] = OS2->panose[j]; + prefix->italic = OS2->fsSelection & 0x01; + prefix->weight = OS2->weightClass; + // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value. + // Since ATS does not enforce this on Mac OS X, we do not enforce it either. + prefix->fsType = 0; + for (unsigned j = 0; j < 4; j++) + prefix->unicodeRange[j] = OS2->unicodeRange[j]; + for (unsigned j = 0; j < 2; j++) + prefix->codePageRange[j] = OS2->codePageRange[j]; + break; + } + case 'head': + { + if (dataLength < tableOffset + sizeof(headTable)) + return false; + + haveHead = true; + const headTable* head = reinterpret_cast<const headTable*>(data + tableOffset); + prefix->checkSumAdjustment = head->checkSumAdjustment; + break; + } + case 'name': + { + if (dataLength < tableOffset + offsetof(nameTable, nameRecords)) + return false; + + haveName = true; + const nameTable* name = reinterpret_cast<const nameTable*>(data + tableOffset); + for (int j = 0; j < name->count; j++) { + if (dataLength < tableOffset + offsetof(nameTable, nameRecords) + (j + 1) * sizeof(nameRecord)) + return false; + if (name->nameRecords[j].platformID == 3 && name->nameRecords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) { + if (dataLength < tableOffset + name->stringOffset + name->nameRecords[j].offset + name->nameRecords[j].length) + return false; + + unsigned short nameLength = name->nameRecords[j].length; + const BigEndianUShort* nameString = reinterpret_cast<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRecords[j].offset); + + switch (name->nameRecords[j].nameID) { + case 1: + familyNameLength = nameLength; + familyName = nameString; + break; + case 2: + subfamilyNameLength = nameLength; + subfamilyName = nameString; + break; + case 4: + fullNameLength = nameLength; + fullName = nameString; + break; + case 5: + versionStringLength = nameLength; + versionString = nameString; + break; + default: + break; + } + } + } + break; + } + default: + break; + } + if (haveOS2 && haveHead && haveName) + break; + } + + prefix->charset = DEFAULT_CHARSET; + prefix->magicNumber = 0x504c; + prefix->reserved[0] = 0; + prefix->reserved[1] = 0; + prefix->reserved[2] = 0; + prefix->reserved[3] = 0; + prefix->padding1 = 0; + + appendBigEndianStringToEOTHeader(eotHeader, familyName, familyNameLength); + appendBigEndianStringToEOTHeader(eotHeader, subfamilyName, subfamilyNameLength); + appendBigEndianStringToEOTHeader(eotHeader, versionString, versionStringLength); + + // If possible, ensure that the family name is a prefix of the full name. + if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, familyNameLength)) { + overlaySrc = reinterpret_cast<const char*>(fullName) - data; + overlayDst = reinterpret_cast<const char*>(familyName) - data; + overlayLength = familyNameLength; + } + + appendBigEndianStringToEOTHeader(eotHeader, fullName, fullNameLength); + + unsigned short padding = 0; + eotHeader.append(reinterpret_cast<UInt8*>(&padding), sizeof(padding)); + + prefix->eotSize = eotHeader.size() + fontData->size(); + + return true; +} + +HANDLE renameAndActivateFont(SharedBuffer* fontData, const String& fontName) +{ + size_t originalDataSize = fontData->size(); + const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData->data()); + + unsigned t; + for (t = 0; t < sfnt->numTables; ++t) { + if (sfnt->tables[t].tag == 'name') + break; + } + if (t == sfnt->numTables) + return 0; + + const int nameRecordCount = 5; + + // Rounded up to a multiple of 4 to simplify the checksum calculation. + size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4; + + Vector<char> rewrittenFontData(fontData->size() + nameTableSize); + char* data = rewrittenFontData.data(); + memcpy(data, fontData->data(), originalDataSize); + + // Make the table directory entry point to the new 'name' table. + sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data); + rewrittenSfnt->tables[t].length = nameTableSize; + rewrittenSfnt->tables[t].offset = originalDataSize; + + // Write the new 'name' table after the original font data. + nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize); + name->format = 0; + name->count = nameRecordCount; + name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord); + for (unsigned i = 0; i < nameRecordCount; ++i) { + name->nameRecords[i].platformID = 3; + name->nameRecords[i].encodingID = 1; + name->nameRecords[i].languageID = 0x0409; + name->nameRecords[i].offset = 0; + name->nameRecords[i].length = fontName.length() * sizeof(UChar); + } + + // The required 'name' record types: Family, Style, Unique, Full and PostScript. + name->nameRecords[0].nameID = 1; + name->nameRecords[1].nameID = 2; + name->nameRecords[2].nameID = 3; + name->nameRecords[3].nameID = 4; + name->nameRecords[4].nameID = 6; + + for (unsigned i = 0; i < fontName.length(); ++i) + reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i]; + + // Update the table checksum in the directory entry. + rewrittenSfnt->tables[t].checkSum = 0; + for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i) + rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i]; + + DWORD numFonts = 0; + HANDLE fontHandle = AddFontMemResourceEx(data, originalDataSize + nameTableSize, 0, &numFonts); + + if (fontHandle && numFonts != 1) { + RemoveFontMemResourceEx(fontHandle); + return 0; + } + + return fontHandle; +} + +} diff --git a/WebCore/platform/graphics/win/FontCairoWin.cpp b/WebCore/platform/graphics/win/OpenTypeUtilities.h index 302e79d..ab35551 100644 --- a/WebCore/platform/graphics/win/FontCairoWin.cpp +++ b/WebCore/platform/graphics/win/OpenTypeUtilities.h @@ -23,20 +23,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "Font.h" +#ifndef OpenTypeUtilities_h +#define OpenTypeUtilities_h -#include "GlyphBuffer.h" -#include "GraphicsContext.h" -#include "NotImplemented.h" -#include "SimpleFontData.h" +#include "PlatformString.h" +#include <wtf/Forward.h> namespace WebCore { -void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, - int from, int numGlyphs, const FloatPoint& point) const -{ - notImplemented(); -} +class SharedBuffer; -} +bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength); +HANDLE renameAndActivateFont(SharedBuffer*, const String&); + +} // namespace WebCore + +#endif // OpenTypeUtilities_h diff --git a/WebCore/platform/graphics/win/QTMovieWin.cpp b/WebCore/platform/graphics/win/QTMovieWin.cpp index 80c6d50..8eee41b 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.cpp +++ b/WebCore/platform/graphics/win/QTMovieWin.cpp @@ -30,6 +30,7 @@ // Put Movies.h first so build failures here point clearly to QuickTime #include <Movies.h> +#include <QuickTimeComponents.h> #include <GXMath.h> #include <QTML.h> @@ -59,6 +60,7 @@ union UppParam { static MovieDrawingCompleteUPP gMovieDrawingCompleteUPP = 0; static HashSet<QTMovieWinPrivate*>* gTaskList; static Vector<CFStringRef>* gSupportedTypes = 0; +static SInt32 quickTimeVersion = 0; static void updateTaskTimer(int maxInterval = 1000) { @@ -82,16 +84,19 @@ public: void startTask(); void endTask(); + void createMovieController(); void registerDrawingCallback(); void drawingComplete(); void updateGWorld(); void createGWorld(); void deleteGWorld(); + void clearGWorld(); void setSize(int, int); QTMovieWin* m_movieWin; Movie m_movie; + MovieController m_movieController; bool m_tasking; QTMovieWinClient* m_client; long m_loadState; @@ -112,6 +117,7 @@ public: QTMovieWinPrivate::QTMovieWinPrivate() : m_movieWin(0) , m_movie(0) + , m_movieController(0) , m_tasking(false) , m_client(0) , m_loadState(0) @@ -135,6 +141,8 @@ QTMovieWinPrivate::~QTMovieWinPrivate() endTask(); if (m_gWorld) deleteGWorld(); + if (m_movieController) + DisposeMovieController(m_movieController); if (m_movie) DisposeMovie(m_movie); } @@ -175,8 +183,12 @@ void QTMovieWinPrivate::task() { ASSERT(m_tasking); - if (!m_loadError) - MoviesTask(m_movie, 0); + if (!m_loadError) { + if (m_movieController) + MCIdle(m_movieController); + else + MoviesTask(m_movie, 0); + } // GetMovieLoadState documentation says that you should not call it more often than every quarter of a second. if (systemTime() >= m_lastLoadStateCheckTime + 0.25 || m_loadError) { @@ -184,7 +196,15 @@ void QTMovieWinPrivate::task() // This is different from QTKit API and seems strange. long loadState = m_loadError ? kMovieLoadStateError : GetMovieLoadState(m_movie); if (loadState != m_loadState) { + + // we only need to erase the movie gworld when the load state changes to loaded while it + // is visible as the gworld is destroyed/created when visibility changes + if (loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded && m_visible) + clearGWorld(); + m_loadState = loadState; + if (!m_movieController && m_loadState >= kMovieLoadStateLoaded) + createMovieController(); m_client->movieLoadStateChanged(m_movieWin); } m_lastLoadStateCheckTime = systemTime(); @@ -209,6 +229,27 @@ void QTMovieWinPrivate::task() endTask(); } +void QTMovieWinPrivate::createMovieController() +{ + Rect bounds; + long flags; + + if (!m_movie) + return; + + if (m_movieController) + DisposeMovieController(m_movieController); + + GetMovieBox(m_movie, &bounds); + flags = mcTopLeftMovie | mcNotVisible; + m_movieController = NewMovieController(m_movie, &bounds, flags); + if (!m_movieController) + return; + + MCSetControllerPort(m_movieController, m_gWorld); + MCSetControllerAttached(m_movieController, false); +} + void QTMovieWinPrivate::registerDrawingCallback() { UppParam param; @@ -218,7 +259,7 @@ void QTMovieWinPrivate::registerDrawingCallback() void QTMovieWinPrivate::drawingComplete() { - if (!m_gWorld) + if (!m_gWorld || m_loadState < kMovieLoadStateLoaded) return; m_client->movieNewImageAvailable(m_movieWin); } @@ -257,12 +298,36 @@ void QTMovieWinPrivate::createGWorld() if (err) return; GetMovieGWorld(m_movie, &m_savedGWorld, 0); + if (m_movieController) + MCSetControllerPort(m_movieController, m_gWorld); SetMovieGWorld(m_movie, m_gWorld, 0); bounds.right = m_width; bounds.bottom = m_height; + if (m_movieController) + MCSetControllerBoundsRect(m_movieController, &bounds); SetMovieBox(m_movie, &bounds); } +void QTMovieWinPrivate::clearGWorld() +{ + if (!m_movie||!m_gWorld) + return; + + GrafPtr savePort; + GetPort(&savePort); + MacSetPort((GrafPtr)m_gWorld); + + Rect bounds; + bounds.top = 0; + bounds.left = 0; + bounds.right = m_gWorldWidth; + bounds.bottom = m_gWorldHeight; + EraseRect(&bounds); + + MacSetPort(savePort); +} + + void QTMovieWinPrivate::setSize(int width, int height) { if (m_width == width && m_height == height) @@ -276,6 +341,8 @@ void QTMovieWinPrivate::setSize(int width, int height) bounds.left = 0; bounds.right = width; bounds.bottom = height; + if (m_movieController) + MCSetControllerBoundsRect(m_movieController, &bounds); SetMovieBox(m_movie, &bounds); updateGWorld(); } @@ -283,6 +350,8 @@ void QTMovieWinPrivate::setSize(int width, int height) void QTMovieWinPrivate::deleteGWorld() { ASSERT(m_gWorld); + if (m_movieController) + MCSetControllerPort(m_movieController, m_savedGWorld); if (m_movie) SetMovieGWorld(m_movie, m_savedGWorld, 0); m_savedGWorld = 0; @@ -308,24 +377,37 @@ QTMovieWin::~QTMovieWin() void QTMovieWin::play() { - StartMovie(m_private->m_movie); + if (m_private->m_movieController) + MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)GetMoviePreferredRate(m_private->m_movie)); + else + StartMovie(m_private->m_movie); m_private->startTask(); } void QTMovieWin::pause() { - StopMovie(m_private->m_movie); + if (m_private->m_movieController) + MCDoAction(m_private->m_movieController, mcActionPlay, 0); + else + StopMovie(m_private->m_movie); updateTaskTimer(); } float QTMovieWin::rate() const { + if (!m_private->m_movie) + return 0; return FixedToFloat(GetMovieRate(m_private->m_movie)); } void QTMovieWin::setRate(float rate) { - SetMovieRate(m_private->m_movie, FloatToFixed(rate)); + if (!m_private->m_movie) + return; + if (m_private->m_movieController) + MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate)); + else + SetMovieRate(m_private->m_movie, FloatToFixed(rate)); updateTaskTimer(); } @@ -353,19 +435,26 @@ void QTMovieWin::setCurrentTime(float time) const return; m_private->m_seeking = true; TimeScale scale = GetMovieTimeScale(m_private->m_movie); - SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale)); + if (m_private->m_movieController){ + QTRestartAtTimeRecord restart = { time * scale , 0 }; + MCDoAction(m_private->m_movieController, mcActionRestartAtTime, (void *)&restart); + } else + SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale)); updateTaskTimer(); } void QTMovieWin::setVolume(float volume) { + if (!m_private->m_movie) + return; SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256)); } unsigned QTMovieWin::dataSize() const { - // FIXME: How to get this? - return 1000; + if (!m_private->m_movie) + return 0; + return GetMovieDataSize(m_private->m_movie, 0, GetMovieDuration(m_private->m_movie)); } float QTMovieWin::maxTimeLoaded() const @@ -385,8 +474,10 @@ long QTMovieWin::loadState() const void QTMovieWin::getNaturalSize(int& width, int& height) { - Rect rect; - GetMovieNaturalBoundsRect(m_private->m_movie, &rect); + Rect rect = { 0, }; + + if (m_private->m_movie) + GetMovieNaturalBoundsRect(m_private->m_movie, &rect); width = rect.right; height = rect.bottom; } @@ -428,6 +519,9 @@ void QTMovieWin::load(const UChar* url, int len) m_private->endTask(); if (m_private->m_gWorld) m_private->deleteGWorld(); + if (m_private->m_movieController) + DisposeMovieController(m_private->m_movieController); + m_private->m_movieController = 0; DisposeMovie(m_private->m_movie); m_private->m_movie = 0; } @@ -630,18 +724,89 @@ static void initializeSupportedTypes() { if (gSupportedTypes) return; - // FIXME: This list might not be complete. - // There must be some way to get it out from QuickTime. + gSupportedTypes = new Vector<CFStringRef>; - gSupportedTypes->append(CFSTR("video/3gpp")); - gSupportedTypes->append(CFSTR("video/3gpp2")); - gSupportedTypes->append(CFSTR("video/mp4")); - gSupportedTypes->append(CFSTR("video/mpeg")); + if (quickTimeVersion < minimumQuickTimeVersion) { + LOG_ERROR("QuickTime version %x detected, at least %x required. Returning empty list of supported media MIME types.", quickTimeVersion, minimumQuickTimeVersion); + return; + } + + // QuickTime doesn't have an importer for video/quicktime. Add it manually. gSupportedTypes->append(CFSTR("video/quicktime")); - gSupportedTypes->append(CFSTR("audio/ac3")); - gSupportedTypes->append(CFSTR("audio/aiff")); - gSupportedTypes->append(CFSTR("audio/basic")); - gSupportedTypes->append(CFSTR("audio/mpeg")); + + for (int index = 0; index < 2; index++) { + ComponentDescription findCD; + + // look at all movie importers that can import in place and are installed. + findCD.componentType = MovieImportType; + findCD.componentSubType = 0; + findCD.componentManufacturer = 0; + findCD.componentFlagsMask = cmpIsMissing | movieImportSubTypeIsFileExtension | canMovieImportInPlace | dontAutoFileMovieImport; + + // look at those registered by HFS file types the first time through, by file extension the second time + findCD.componentFlags = canMovieImportInPlace | (index ? movieImportSubTypeIsFileExtension : 0); + + long componentCount = CountComponents(&findCD); + if (!componentCount) + continue; + + Component comp = 0; + while (comp = FindNextComponent(comp, &findCD)) { + // Does this component have a MIME type container? + ComponentDescription infoCD; + OSErr err = GetComponentInfo(comp, &infoCD, nil /*name*/, nil /*info*/, nil /*icon*/); + if (err) + continue; + if (!(infoCD.componentFlags & hasMovieImportMIMEList)) + continue; + QTAtomContainer mimeList = NULL; + err = MovieImportGetMIMETypeList((ComponentInstance)comp, &mimeList); + if (err || !mimeList) + continue; + + // Grab every type from the container. + QTLockContainer(mimeList); + int typeCount = QTCountChildrenOfType(mimeList, kParentAtomIsContainer, kMimeInfoMimeTypeTag); + for (int typeIndex = 1; typeIndex <= typeCount; typeIndex++) { + QTAtom mimeTag = QTFindChildByIndex(mimeList, 0, kMimeInfoMimeTypeTag, typeIndex, NULL); + if (!mimeTag) + continue; + char* atomData; + long typeLength; + if (noErr != QTGetAtomDataPtr(mimeList, mimeTag, &typeLength, &atomData)) + continue; + + char typeBuffer[256]; + if (typeLength >= sizeof(typeBuffer)) + continue; + memcpy(typeBuffer, atomData, typeLength); + typeBuffer[typeLength] = 0; + + // Only add "audio/..." and "video/..." types. + if (strncmp(typeBuffer, "audio/", 6) && strncmp(typeBuffer, "video/", 6)) + continue; + + CFStringRef cfMimeType = CFStringCreateWithCString(NULL, typeBuffer, kCFStringEncodingUTF8); + if (!cfMimeType) + continue; + + // Only add each type once. + bool alreadyAdded = false; + for (int addedIndex = 0; addedIndex < gSupportedTypes->size(); addedIndex++) { + CFStringRef type = gSupportedTypes->at(addedIndex); + if (kCFCompareEqualTo == CFStringCompare(cfMimeType, type, kCFCompareCaseInsensitive)) { + alreadyAdded = true; + break; + } + } + if (!alreadyAdded) + gSupportedTypes->append(cfMimeType); + else + CFRelease(cfMimeType); + } + DisposeHandle(mimeList); + } + } } unsigned QTMovieWin::countSupportedTypes() @@ -676,15 +841,14 @@ bool QTMovieWin::initializeQuickTime() initialized = true; // Initialize and check QuickTime version OSErr result = InitializeQTML(0); - SInt32 version = 0; if (result == noErr) - result = Gestalt(gestaltQuickTime, &version); + result = Gestalt(gestaltQuickTime, &quickTimeVersion); if (result != noErr) { LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support."); return false; } - if (version < minimumQuickTimeVersion) { - LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", version, minimumQuickTimeVersion); + if (quickTimeVersion < minimumQuickTimeVersion) { + LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", quickTimeVersion, minimumQuickTimeVersion); return false; } EnterMovies(); diff --git a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp index 0c31672..8b5ab87 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp @@ -34,7 +34,9 @@ #include "FontCache.h" #include "FloatRect.h" #include "FontDescription.h" +#include "PlatformString.h" #include <wtf/MathExtras.h> +#include <wtf/RetainPtr.h> #include <unicode/uchar.h> #include <unicode/unorm.h> #include <ApplicationServices/ApplicationServices.h> @@ -54,32 +56,9 @@ void SimpleFontData::platformInit() m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; - - if (m_font.useGDI()) { - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - OUTLINETEXTMETRIC metrics; - GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); - TEXTMETRIC& textMetrics = metrics.otmTextMetrics; - m_ascent = textMetrics.tmAscent; - m_descent = textMetrics.tmDescent; - m_lineGap = textMetrics.tmExternalLeading; - m_lineSpacing = m_ascent + m_descent + m_lineGap; - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. - - GLYPHMETRICS gm; - MAT2 mat = { 1, 0, 0, 1 }; - DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); - if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) - m_xHeight = gm.gmptGlyphOrigin.y; - - m_unitsPerEm = metrics.otmEMSquare; - - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); - - return; - } + + if (m_font.useGDI()) + return initGDIFont(); CGFontRef font = m_font.cgFont(); int iAscent = CGFontGetAscent(font); @@ -139,29 +118,13 @@ void SimpleFontData::platformInit() void SimpleFontData::platformDestroy() { - if (!isCustomFont()) { - DeleteObject(m_font.hfont()); - CGFontRelease(m_font.cgFont()); - } - - // We don't hash this on Win32, so it's effectively owned by us. - delete m_smallCapsFontData; - - ScriptFreeCache(&m_scriptCache); - delete m_scriptFontProperties; + platformCommonDestroy(); } float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { - if (m_font.useGDI()) { - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - int width; - GetCharWidthI(hdc, glyph, 1, 0, &width); - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); - return width; - } + if (m_font.useGDI()) + return widthForGDIGlyph(glyph); CGFontRef font = m_font.cgFont(); float pointSize = m_font.size(); diff --git a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp index e7b2c81..07d5305 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp @@ -34,7 +34,10 @@ #include "Font.h" #include "FontCache.h" #include "FontDescription.h" +#include "MathExtras.h" #include "NotImplemented.h" +#include <cairo.h> +#include <cairo-win32.h> #include <mlang.h> #include <tchar.h> @@ -45,58 +48,84 @@ void SimpleFontData::platformInit() m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; - - if (m_font.useGDI()) { - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - OUTLINETEXTMETRIC metrics; - GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); - TEXTMETRIC& textMetrics = metrics.otmTextMetrics; - m_ascent = textMetrics.tmAscent; - m_descent = textMetrics.tmDescent; - m_lineGap = textMetrics.tmExternalLeading; - m_lineSpacing = m_ascent + m_descent + m_lineGap; - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. + m_syntheticBoldOffset = 0; + if (m_font.useGDI()) + return initGDIFont(); + + HDC hdc = GetDC(0); + SaveDC(hdc); + + cairo_scaled_font_t* scaledFont = m_font.scaledFont(); + const double metricsMultiplier = cairo_win32_scaled_font_get_metrics_factor(scaledFont) * m_font.size(); + + cairo_win32_scaled_font_select_font(scaledFont, hdc); + + TEXTMETRIC textMetrics; + GetTextMetrics(hdc, &textMetrics); + m_ascent = lroundf(textMetrics.tmAscent * metricsMultiplier); + m_descent = lroundf(textMetrics.tmDescent * metricsMultiplier); + m_xHeight = m_ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. + m_lineGap = lroundf(textMetrics.tmExternalLeading * metricsMultiplier); + m_lineSpacing = m_ascent + m_descent + m_lineGap; + + OUTLINETEXTMETRIC metrics; + if (GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics) > 0) { + // This is a TrueType font. We might be able to get an accurate xHeight GLYPHMETRICS gm; MAT2 mat = { 1, 0, 0, 1 }; DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) - m_xHeight = gm.gmptGlyphOrigin.y; - - m_unitsPerEm = metrics.otmEMSquare; + m_xHeight = gm.gmptGlyphOrigin.y * metricsMultiplier; + } - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); + cairo_win32_scaled_font_done_font(scaledFont); - return; - } + m_isSystemFont = false; + m_scriptCache = 0; + m_scriptFontProperties = 0; - // FIXME: This section should determine font dimensions (see CG implementation). - notImplemented(); + RestoreDC(hdc, -1); + ReleaseDC(0, hdc); } void SimpleFontData::platformDestroy() { - notImplemented(); + cairo_font_face_destroy(m_font.fontFace()); + cairo_scaled_font_destroy(m_font.scaledFont()); + + DeleteObject(m_font.hfont()); + + platformCommonDestroy(); } float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { - if (m_font.useGDI()) { - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - int width; - GetCharWidthI(hdc, glyph, 1, 0, &width); - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); - return width; - } + if (m_font.useGDI()) + return widthForGDIGlyph(glyph); + + HDC hdc = GetDC(0); + SaveDC(hdc); + + cairo_scaled_font_t* scaledFont = m_font.scaledFont(); + cairo_win32_scaled_font_select_font(scaledFont, hdc); - // FIXME: Flesh out with Cairo/win32 font implementation - notImplemented(); + int width; + GetCharWidthI(hdc, glyph, 1, 0, &width); - return 0; + cairo_win32_scaled_font_done_font(scaledFont); + + RestoreDC(hdc, -1); + ReleaseDC(0, hdc); + + const double metricsMultiplier = cairo_win32_scaled_font_get_metrics_factor(scaledFont) * m_font.size(); + return width * metricsMultiplier; +} + +void SimpleFontData::setFont(cairo_t* cr) const +{ + ASSERT(cr); + m_font.setFont(cr); } } diff --git a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp index 344d964..0e9f9fb 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp @@ -60,6 +60,42 @@ bool SimpleFontData::shouldApplyMacAscentHack() return g_shouldApplyMacAscentHack; } +void SimpleFontData::initGDIFont() +{ + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); + OUTLINETEXTMETRIC metrics; + GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); + TEXTMETRIC& textMetrics = metrics.otmTextMetrics; + m_ascent = textMetrics.tmAscent; + m_descent = textMetrics.tmDescent; + m_lineGap = textMetrics.tmExternalLeading; + m_lineSpacing = m_ascent + m_descent + m_lineGap; + m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. + + GLYPHMETRICS gm; + MAT2 mat = { 1, 0, 0, 1 }; + DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); + if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) + m_xHeight = gm.gmptGlyphOrigin.y; + + m_unitsPerEm = metrics.otmEMSquare; + + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + + return; +} + +void SimpleFontData::platformCommonDestroy() +{ + // We don't hash this on Win32, so it's effectively owned by us. + delete m_smallCapsFontData; + + ScriptFreeCache(&m_scriptCache); + delete m_scriptFontProperties; +} + SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const { if (!m_smallCapsFontData) { @@ -73,7 +109,7 @@ SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDes GetObject(m_font.hfont(), sizeof(LOGFONT), &winfont); winfont.lfHeight = -lroundf(smallCapsHeight * (m_font.useGDI() ? 1 : 32)); HFONT hfont = CreateFontIndirect(&winfont); - m_smallCapsFontData = new SimpleFontData(FontPlatformData(hfont, smallCapsHeight, fontDescription.bold(), fontDescription.italic(), m_font.useGDI())); + m_smallCapsFontData = new SimpleFontData(FontPlatformData(hfont, smallCapsHeight, m_font.syntheticBold(), m_font.syntheticOblique(), m_font.useGDI())); } } return m_smallCapsFontData; @@ -137,6 +173,17 @@ void SimpleFontData::determinePitch() ReleaseDC(0, dc); } +float SimpleFontData::widthForGDIGlyph(Glyph glyph) const +{ + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); + int width; + GetCharWidthI(hdc, glyph, 1, 0, &width); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + return width + m_syntheticBoldOffset; +} + SCRIPT_FONTPROPERTIES* SimpleFontData::scriptFontProperties() const { if (!m_scriptFontProperties) { diff --git a/WebCore/platform/graphics/win/UniscribeController.cpp b/WebCore/platform/graphics/win/UniscribeController.cpp index 876ff43..371bc51 100644 --- a/WebCore/platform/graphics/win/UniscribeController.cpp +++ b/WebCore/platform/graphics/win/UniscribeController.cpp @@ -299,7 +299,9 @@ bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const S offsetX = roundf(offsetX); offsetY = roundf(offsetY); } - + + advance += fontData->m_syntheticBoldOffset; + // We special case spaces in two ways when applying word rounding. // First, we round spaces to an adjusted width in all fonts. // Second, in fixed-pitch fonts we ensure that all glyphs that @@ -361,7 +363,7 @@ bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const S // as well, so that when the time comes to draw those glyphs, we can apply the appropriate // translation. if (glyphBuffer) { - FloatSize size(offsetX, offsetY); + FloatSize size(offsetX, -offsetY); glyphBuffer->add(glyph, fontData, advance, &size); } @@ -422,25 +424,9 @@ bool UniscribeController::shape(const UChar* str, int len, SCRIPT_ITEM item, con if (FAILED(shapeResult)) return false; - - // FIXME: We need to do better than this. Falling back on the entire item is not good enough. - // We may still have missing glyphs even if we succeeded. We need to treat missing glyphs as - // a failure so that we will fall back to another font. - bool containsMissingGlyphs = false; - SCRIPT_FONTPROPERTIES* fontProperties = fontData->scriptFontProperties(); - for (int i = 0; i < glyphCount; i++) { - WORD glyph = glyphs[i]; - if (glyph == fontProperties->wgDefault) { - containsMissingGlyphs = true; - break; - } - } - - if (containsMissingGlyphs) - return false; - glyphs.resize(glyphCount); - visualAttributes.resize(glyphCount); + glyphs.shrink(glyphCount); + visualAttributes.shrink(glyphCount); return true; } |