diff options
Diffstat (limited to 'WebCore/platform/graphics/win')
39 files changed, 6500 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/win/ColorSafari.cpp b/WebCore/platform/graphics/win/ColorSafari.cpp new file mode 100644 index 0000000..a04fd81 --- /dev/null +++ b/WebCore/platform/graphics/win/ColorSafari.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007 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 "Color.h" + +#include "NotImplemented.h" +#include <CoreGraphics/CGColor.h> +#include <SafariTheme/SafariTheme.h> +#include <wtf/Assertions.h> +#include <wtf/RetainPtr.h> + +using namespace SafariTheme; + +namespace WebCore { + +typedef CGColorRef (APIENTRY*stCopyThemeColorPtr)(unsigned, SafariTheme::ThemeControlState); +static const unsigned stFocusRingColorID = 4; + +static const unsigned aquaFocusRingColor = 0xFF7DADD9; + +static RGBA32 makeRGBAFromCGColor(CGColorRef c) +{ + const CGFloat* components = CGColorGetComponents(c); + return makeRGBA(255 * components[0], 255 * components[1], 255 * components[2], 255 * components[3]); +} + +Color focusRingColor() +{ + static Color focusRingColor; + focusRingColor.isValid(); + + if (!focusRingColor.isValid()) { + if (HMODULE module = LoadLibrary(SAFARITHEMEDLL)) + if (stCopyThemeColorPtr stCopyThemeColor = (stCopyThemeColorPtr)GetProcAddress(module, "STCopyThemeColor")) { + RetainPtr<CGColorRef> c(AdoptCF, stCopyThemeColor(stFocusRingColorID, SafariTheme::ActiveState)); + focusRingColor = makeRGBAFromCGColor(c.get()); + } + if (!focusRingColor.isValid()) + focusRingColor = aquaFocusRingColor; + } + + return focusRingColor; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/win/FontCGWin.cpp b/WebCore/platform/graphics/win/FontCGWin.cpp new file mode 100644 index 0000000..1766cd9 --- /dev/null +++ b/WebCore/platform/graphics/win/FontCGWin.cpp @@ -0,0 +1,361 @@ +/* + * 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. + * + * 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 "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> + +namespace WebCore { + +const int syntheticObliqueAngle = 14; + +static inline CGFloat toCGFloat(FIXED f) +{ + 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; + + 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(); + 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(); + + 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); + + // 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.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); + } + } + 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); +} + +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, WebCoreShouldUseFontSmoothing()); + + const FontPlatformData& platformData = font->platformData(); + + CGContextSetFont(cgContext, platformData.cgFont()); + + CGAffineTransform matrix = CGAffineTransformIdentity; + matrix.b = -matrix.b; + matrix.d = -matrix.d; + + if (platformData.syntheticOblique()) { + 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); + + CGContextSetFontSize(cgContext, platformData.size()); + 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() + 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 new file mode 100644 index 0000000..49b3d76 --- /dev/null +++ b/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -0,0 +1,525 @@ +/* + * 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 <winsock2.h> +#include "FontCache.h" +#include "Font.h" +#include "SimpleFontData.h" +#include "StringHash.h" +#include "UnicodeRange.h" +#include <windows.h> +#include <mlang.h> +#if PLATFORM(CG) +#include <ApplicationServices/ApplicationServices.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#endif + +using std::min; + +namespace WebCore +{ + +void FontCache::platformInit() +{ +#if PLATFORM(CG) + wkSetUpFontCache(1536 * 1024 * 4); // This size matches Mac. +#endif +} + +IMLangFontLink2* FontCache::getFontLinkInterface() +{ + static IMultiLanguage *multiLanguage; + if (!multiLanguage) { + if (CoCreateInstance(CLSID_CMultiLanguage, 0, CLSCTX_ALL, IID_IMultiLanguage, (void**)&multiLanguage) != S_OK) + return 0; + } + + static IMLangFontLink2* langFontLink; + if (!langFontLink) { + if (multiLanguage->QueryInterface(&langFontLink) != S_OK) + return 0; + } + + return langFontLink; +} + +static int CALLBACK metaFileEnumProc(HDC hdc, HANDLETABLE* table, CONST ENHMETARECORD* record, int tableEntries, LPARAM logFont) +{ + if (record->iType == EMR_EXTCREATEFONTINDIRECTW) { + const EMREXTCREATEFONTINDIRECTW* createFontRecord = reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record); + *reinterpret_cast<LOGFONT*>(logFont) = createFontRecord->elfw.elfLogFont; + } + return true; +} + +static int CALLBACK linkedFontEnumProc(CONST LOGFONT* logFont, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM hfont) +{ + *reinterpret_cast<HFONT*>(hfont) = CreateFontIndirect(logFont); + return false; +} + +static const Vector<String>* getLinkedFonts(String& family) +{ + static HashMap<String, Vector<String>*> systemLinkMap; + Vector<String>* result = systemLinkMap.get(family); + if (result) + return result; + + result = new Vector<String>; + systemLinkMap.set(family, result); + HKEY fontLinkKey; + if (FAILED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink", 0, KEY_READ, &fontLinkKey))) + return result; + + DWORD linkedFontsBufferSize = 0; + RegQueryValueEx(fontLinkKey, family.charactersWithNullTermination(), 0, NULL, NULL, &linkedFontsBufferSize); + WCHAR* linkedFonts = reinterpret_cast<WCHAR*>(malloc(linkedFontsBufferSize)); + if (SUCCEEDED(RegQueryValueEx(fontLinkKey, family.charactersWithNullTermination(), 0, NULL, reinterpret_cast<BYTE*>(linkedFonts), &linkedFontsBufferSize))) { + unsigned i = 0; + unsigned length = linkedFontsBufferSize / sizeof(*linkedFonts); + while (i < length) { + while (i < length && linkedFonts[i] != ',') + i++; + i++; + unsigned j = i; + while (j < length && linkedFonts[j]) + j++; + result->append(String(linkedFonts + i, j - i)); + i = j + 1; + } + } + free(linkedFonts); + RegCloseKey(fontLinkKey); + return result; +} + +static const Vector<DWORD, 4>& getCJKCodePageMasks() +{ + // The default order in which we look for a font for a CJK character. If the user's default code page is + // one of these, we will use it first. + static const UINT CJKCodePages[] = { + 932, /* Japanese */ + 936, /* Simplified Chinese */ + 950, /* Traditional Chinese */ + 949 /* Korean */ + }; + + static Vector<DWORD, 4> codePageMasks; + static bool initialized; + if (!initialized) { + initialized = true; + IMLangFontLink2* langFontLink = FontCache::getFontLinkInterface(); + if (!langFontLink) + return codePageMasks; + + UINT defaultCodePage; + DWORD defaultCodePageMask = 0; + if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE, reinterpret_cast<LPWSTR>(&defaultCodePage), sizeof(defaultCodePage))) + langFontLink->CodePageToCodePages(defaultCodePage, &defaultCodePageMask); + + if (defaultCodePage == CJKCodePages[0] || defaultCodePage == CJKCodePages[1] || defaultCodePage == CJKCodePages[2] || defaultCodePage == CJKCodePages[3]) + codePageMasks.append(defaultCodePageMask); + for (unsigned i = 0; i < 4; ++i) { + if (defaultCodePage != CJKCodePages[i]) { + DWORD codePageMask; + langFontLink->CodePageToCodePages(CJKCodePages[i], &codePageMask); + codePageMasks.append(codePageMask); + } + } + } + return codePageMasks; +} + +static bool currentFontContainsCharacter(HDC hdc, UChar character) +{ + static Vector<char, 512> glyphsetBuffer; + glyphsetBuffer.resize(GetFontUnicodeRanges(hdc, 0)); + GLYPHSET* glyphset = reinterpret_cast<GLYPHSET*>(glyphsetBuffer.data()); + GetFontUnicodeRanges(hdc, glyphset); + + // FIXME: Change this to a binary search. + unsigned i = 0; + while (i < glyphset->cRanges && glyphset->ranges[i].wcLow <= character) + i++; + + return i && glyphset->ranges[i - 1].wcLow + glyphset->ranges[i - 1].cGlyphs > character; +} + +static HFONT createMLangFont(IMLangFontLink2* langFontLink, HDC hdc, DWORD codePageMask, UChar character = 0) +{ + HFONT MLangFont; + HFONT hfont = 0; + if (SUCCEEDED(langFontLink->MapFont(hdc, codePageMask, character, &MLangFont)) && MLangFont) { + LOGFONT lf; + GetObject(MLangFont, sizeof(LOGFONT), &lf); + langFontLink->ReleaseFont(MLangFont); + hfont = CreateFontIndirect(&lf); + } + return hfont; +} + +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) +{ + UChar character = characters[0]; + SimpleFontData* fontData = 0; + HDC hdc = GetDC(0); + HFONT primaryFont = font.primaryFont()->fontDataForCharacter(character)->platformData().hfont(); + HGDIOBJ oldFont = SelectObject(hdc, primaryFont); + HFONT hfont = 0; + + if (IMLangFontLink2* langFontLink = getFontLinkInterface()) { + // Try MLang font linking first. + DWORD codePages = 0; + langFontLink->GetCharCodePages(character, &codePages); + + if (codePages && findCharUnicodeRange(character) == cRangeSetCJK) { + // The CJK character may belong to multiple code pages. We want to + // do font linking against a single one of them, preferring the default + // code page for the user's locale. + const Vector<DWORD, 4>& CJKCodePageMasks = getCJKCodePageMasks(); + unsigned numCodePages = CJKCodePageMasks.size(); + for (unsigned i = 0; i < numCodePages && !hfont; ++i) { + hfont = createMLangFont(langFontLink, hdc, CJKCodePageMasks[i]); + if (hfont && !(codePages & CJKCodePageMasks[i])) { + // We asked about a code page that is not one of the code pages + // returned by MLang, so the font might not contain the character. + SelectObject(hdc, hfont); + if (!currentFontContainsCharacter(hdc, character)) { + DeleteObject(hfont); + hfont = 0; + } + SelectObject(hdc, primaryFont); + } + } + } else + hfont = createMLangFont(langFontLink, hdc, codePages, character); + } + + // A font returned from MLang is trusted to contain the character. + bool containsCharacter = hfont; + + if (!hfont) { + // To find out what font Uniscribe would use, we make it draw into a metafile and intercept + // calls to CreateFontIndirect(). + HDC metaFileDc = CreateEnhMetaFile(hdc, NULL, NULL, NULL); + SelectObject(metaFileDc, primaryFont); + + bool scriptStringOutSucceeded = false; + SCRIPT_STRING_ANALYSIS ssa; + + // FIXME: If length is greater than 1, we actually return the font for the last character. + // This function should be renamed getFontDataForCharacter and take a single 32-bit character. + if (SUCCEEDED(ScriptStringAnalyse(metaFileDc, characters, length, 0, -1, SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK, + 0, NULL, NULL, NULL, NULL, NULL, &ssa))) { + scriptStringOutSucceeded = SUCCEEDED(ScriptStringOut(ssa, 0, 0, 0, NULL, 0, 0, FALSE)); + ScriptStringFree(&ssa); + } + HENHMETAFILE metaFile = CloseEnhMetaFile(metaFileDc); + if (scriptStringOutSucceeded) { + LOGFONT logFont; + logFont.lfFaceName[0] = 0; + EnumEnhMetaFile(0, metaFile, metaFileEnumProc, &logFont, NULL); + if (logFont.lfFaceName[0]) + hfont = CreateFontIndirect(&logFont); + } + DeleteEnhMetaFile(metaFile); + } + + String familyName; + const Vector<String>* linkedFonts = 0; + unsigned linkedFontIndex = 0; + while (hfont) { + SelectObject(hdc, hfont); + WCHAR name[LF_FACESIZE]; + GetTextFace(hdc, LF_FACESIZE, name); + familyName = name; + + if (containsCharacter || currentFontContainsCharacter(hdc, character)) + break; + + if (!linkedFonts) + linkedFonts = getLinkedFonts(familyName); + SelectObject(hdc, oldFont); + DeleteObject(hfont); + hfont = 0; + + if (linkedFonts->size() <= linkedFontIndex) + break; + + LOGFONT logFont; + logFont.lfCharSet = DEFAULT_CHARSET; + memcpy(logFont.lfFaceName, linkedFonts->at(linkedFontIndex).characters(), linkedFonts->at(linkedFontIndex).length() * sizeof(WCHAR)); + logFont.lfFaceName[linkedFonts->at(linkedFontIndex).length()] = 0; + EnumFontFamiliesEx(hdc, &logFont, linkedFontEnumProc, reinterpret_cast<LPARAM>(&hfont), 0); + linkedFontIndex++; + } + + if (hfont) { + if (!familyName.isEmpty()) { + FontPlatformData* result = getCachedFontPlatformData(font.fontDescription(), familyName); + if (result) + fontData = getCachedFontData(result); + } + + SelectObject(hdc, oldFont); + DeleteObject(hfont); + } + + ReleaseDC(0, hdc); + return fontData; +} + +FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) +{ + // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick + // the default that the user would get without changing any prefs. + static AtomicString timesStr("Times New Roman"); + return getCachedFontPlatformData(fontDescription, timesStr); +} + +static LONG toGDIFontWeight(FontWeight fontWeight) +{ + 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 + matchData.m_chosen.lfOutPrecision = OUT_TT_PRECIS; +#endif + 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) +{ + bool isLucidaGrande = false; + static AtomicString lucidaStr("Lucida Grande"); + if (equalIgnoringCase(family, lucidaStr)) + isLucidaGrande = true; + + bool useGDI = fontDescription.renderingMode() == AlternateRenderingMode && !isLucidaGrande; + + // 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; + + 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. + delete result; + DeleteObject(hfont); + return 0; + } + + return result; +} + +} + diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp new file mode 100644 index 0000000..26fceba --- /dev/null +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -0,0 +1,235 @@ +/* + * 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 + * 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 "FontCustomPlatformData.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, FontRenderingMode renderingMode) +{ + 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) +{ + SharedBuffer* buffer = static_cast<SharedBuffer*>(info); + buffer->ref(); + return (void*)buffer->data(); +} + +void releaseData(void* info, const void* data) +{ + static_cast<SharedBuffer*>(info)->deref(); +} + +size_t getBytesWithOffset(void *info, void* buffer, size_t offset, size_t count) +{ + SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info); + size_t availBytes = count; + if (offset + count > sharedBuffer->size()) + availBytes -= (offset + count) - sharedBuffer->size(); + memcpy(buffer, sharedBuffer->data() + offset, availBytes); + 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 }; + RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateDirectAccess(buffer, buffer->size(), &callbacks)); + CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider.get()); + if (!cgFont) + return 0; + + // 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 new file mode 100644 index 0000000..34a9851 --- /dev/null +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.h @@ -0,0 +1,56 @@ +/* + * 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 + * 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 FontCustomPlatformData_h +#define FontCustomPlatformData_h + +#include "FontRenderingMode.h" +#include "PlatformString.h" +#include <wtf/Noncopyable.h> + +typedef struct CGFont* CGFontRef; + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformData : Noncopyable { + 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, FontRenderingMode = NormalRenderingMode); + + CGFontRef m_cgFont; + HANDLE m_fontReference; + String m_name; +}; + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); + +} + +#endif 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/FontDatabase.cpp b/WebCore/platform/graphics/win/FontDatabase.cpp new file mode 100644 index 0000000..1308ff0 --- /dev/null +++ b/WebCore/platform/graphics/win/FontDatabase.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2007 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 "FontDatabase.h" + +#include "CString.h" +#include "FileSystem.h" +#include "PlatformString.h" +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <shlobj.h> +#include <wtf/RetainPtr.h> + +namespace WebCore { + +static String systemFontsDirectory() +{ + static bool initialized; + static String directory; + + if (!initialized) { + initialized = true; + + Vector<UChar> buffer(MAX_PATH); + if (FAILED(SHGetFolderPath(0, CSIDL_FONTS | CSIDL_FLAG_CREATE, 0, 0, buffer.data()))) + return directory; + buffer.resize(wcslen(buffer.data())); + + directory = String::adopt(buffer); + } + + return directory; +} + +static String fontsPlistPath() +{ + static String path = pathByAppendingComponent(localUserSpecificStorageDirectory(), "FontsList.plist"); + return path; +} + +static bool systemHasFontsNewerThanFontsPlist() +{ + WIN32_FILE_ATTRIBUTE_DATA plistAttributes = {0}; + if (!GetFileAttributesEx(fontsPlistPath().charactersWithNullTermination(), GetFileExInfoStandard, &plistAttributes)) + return true; + + WIN32_FILE_ATTRIBUTE_DATA fontsDirectoryAttributes = {0}; + if (!GetFileAttributesEx(systemFontsDirectory().charactersWithNullTermination(), GetFileExInfoStandard, &fontsDirectoryAttributes)) + return true; + + return CompareFileTime(&plistAttributes.ftLastWriteTime, &fontsDirectoryAttributes.ftLastWriteTime) < 0; +} + +static RetainPtr<CFPropertyListRef> readFontPlist() +{ + CString plistPath = fontsPlistPath().utf8(); + + RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateFromFileSystemRepresentation(0, reinterpret_cast<const UInt8*>(plistPath.data()), plistPath.length(), false)); + if (!url) + return 0; + + RetainPtr<CFReadStreamRef> stream(AdoptCF, CFReadStreamCreateWithFile(0, url.get())); + if (!stream) + return 0; + + if (!CFReadStreamOpen(stream.get())) + return 0; + + CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0 | kCFPropertyListXMLFormat_v1_0; + RetainPtr<CFPropertyListRef> plist(AdoptCF, CFPropertyListCreateFromStream(0, stream.get(), 0, kCFPropertyListMutableContainersAndLeaves, &format, 0)); + + CFReadStreamClose(stream.get()); + + return plist; +} + +static bool populateFontDatabaseFromPlist() +{ + RetainPtr<CFPropertyListRef> plist = readFontPlist(); + if (!plist) + return false; + + RetainPtr<CFDataRef> data(AdoptCF, CFPropertyListCreateXMLData(0, plist.get())); + if (!data) + return false; + + wkAddFontsFromPlistRepresentation(data.get()); + return true; +} + +static bool populateFontDatabaseFromFileSystem() +{ + RetainPtr<CFStringRef> directory(AdoptCF, systemFontsDirectory().createCFString()); + if (!directory) + return false; + + wkAddFontsInDirectory(directory.get()); + return true; +} + +static void writeFontDatabaseToPlist() +{ + RetainPtr<CFDataRef> data(AdoptCF, wkCreateFontsPlistRepresentation()); + if (!data) + return; + + safeCreateFile(fontsPlistPath(), data.get()); +} + +void populateFontDatabase() +{ + static bool initialized; + if (initialized) + return; + initialized = true; + + if (!systemHasFontsNewerThanFontsPlist()) + if (populateFontDatabaseFromPlist()) + return; + + if (populateFontDatabaseFromFileSystem()) + writeFontDatabaseToPlist(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/win/FontDatabase.h b/WebCore/platform/graphics/win/FontDatabase.h new file mode 100644 index 0000000..4f76c9e --- /dev/null +++ b/WebCore/platform/graphics/win/FontDatabase.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef FontDatabase_h +#define FontDatabase_h + +namespace WebCore { + + void populateFontDatabase(); + +} // namespace WebCore + +#endif // !defined(FontDatabase_h) diff --git a/WebCore/platform/graphics/win/FontPlatformData.h b/WebCore/platform/graphics/win/FontPlatformData.h new file mode 100644 index 0000000..d61afa8 --- /dev/null +++ b/WebCore/platform/graphics/win/FontPlatformData.h @@ -0,0 +1,147 @@ +/* + * 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. + * + */ + +#ifndef FontPlatformData_H +#define FontPlatformData_H + +#include "StringImpl.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RetainPtr.h> +#include <wtf/RefCounted.h> + +#if PLATFORM(CAIRO) +#include <cairo-win32.h> +#endif + +typedef struct HFONT__* HFONT; +typedef struct CGFont* CGFontRef; + +namespace WebCore { + +class FontDescription; + +class FontPlatformData { +public: + FontPlatformData() +#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); + +#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(); + + 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; } + bool syntheticBold() const { return m_syntheticBold; } + bool syntheticOblique() const { return m_syntheticOblique; } + bool useGDI() const { return m_useGDI; } + + unsigned hash() const + { + return m_font->hash(); + } + + bool operator==(const FontPlatformData& other) const + { + 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: + 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; + bool m_syntheticOblique; + bool m_useGDI; +}; + +} + +#endif 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 new file mode 100644 index 0000000..4b4df5a --- /dev/null +++ b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp @@ -0,0 +1,92 @@ +/* + * 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) 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> + +using std::min; + +namespace WebCore { + +FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool oblique, bool useGDI) + : m_font(RefCountedHFONT::create(font)) + , m_size(size) +#if PLATFORM(CG) + , m_cgFont(0) +#elif PLATFORM(CAIRO) + , m_fontFace(0) + , m_scaledFont(0) +#endif + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) + , m_useGDI(useGDI) +{ + HDC hdc = GetDC(0); + SaveDC(hdc); + + SelectObject(hdc, font); + UINT bufferSize = GetOutlineTextMetrics(hdc, 0, NULL); + + ASSERT_WITH_MESSAGE(bufferSize, "Bitmap fonts not supported with CoreGraphics."); + + if (bufferSize) { + OUTLINETEXTMETRICW* metrics = (OUTLINETEXTMETRICW*)malloc(bufferSize); + + GetOutlineTextMetricsW(hdc, bufferSize, metrics); + WCHAR* faceName = (WCHAR*)((uintptr_t)metrics + (uintptr_t)metrics->otmpFaceName); + + platformDataInit(font, size, hdc, faceName); + + free(metrics); + } + + RestoreDC(hdc, -1); + ReleaseDC(0, hdc); +} + +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) + : m_size(size) +#if PLATFORM(CG) + , m_cgFont(0) +#elif PLATFORM(CAIRO) + , m_fontFace(0) + , m_scaledFont(0) +#endif + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) + , m_useGDI(false) +{ +} + +FontPlatformData::~FontPlatformData() +{ +} + +} diff --git a/WebCore/platform/graphics/win/FontWin.cpp b/WebCore/platform/graphics/win/FontWin.cpp new file mode 100644 index 0000000..5e423e0 --- /dev/null +++ b/WebCore/platform/graphics/win/FontWin.cpp @@ -0,0 +1,101 @@ +/* + * 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. + * + * 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 "Font.h" + +#include "FontFallbackList.h" +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "IntRect.h" +#include "NotImplemented.h" +#include "SimpleFontData.h" +#include "UniscribeController.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, + int from, int to) const +{ + UniscribeController it(this, run); + it.advance(from); + float beforeWidth = it.runWidthSoFar(); + it.advance(to); + float afterWidth = it.runWidthSoFar(); + + // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning + if (run.rtl()) { + it.advance(run.length()); + float totalWidth = it.runWidthSoFar(); + return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); + } + + return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); +} + +void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, + int from, int to) const +{ + // This glyph buffer holds our glyphs + advances + font data for each glyph. + GlyphBuffer glyphBuffer; + + float startX = point.x(); + UniscribeController controller(this, run); + controller.advance(from); + float beforeWidth = controller.runWidthSoFar(); + controller.advance(to, &glyphBuffer); + + // We couldn't generate any glyphs for the run. Give up. + if (glyphBuffer.isEmpty()) + return; + + float afterWidth = controller.runWidthSoFar(); + + if (run.rtl()) { + controller.advance(run.length()); + startX += controller.runWidthSoFar() - afterWidth; + } else + startX += beforeWidth; + + // Draw the glyph buffer now at the starting point returned in startX. + FloatPoint startPoint(startX, point.y()); + drawGlyphBuffer(context, glyphBuffer, run, startPoint); +} + +float Font::floatWidthForComplexText(const TextRun& run) const +{ + UniscribeController controller(this, run); + controller.advance(run.length()); + return controller.runWidthSoFar(); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const +{ + UniscribeController controller(this, run); + return controller.offsetForPosition(x, includePartialGlyphs); +} + +} diff --git a/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp b/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp new file mode 100644 index 0000000..c11fc1b --- /dev/null +++ b/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp @@ -0,0 +1,59 @@ +/* + * 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" +#include <WebKitSystemInterface/WebKitSystemInterface.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; + CGGlyph localGlyphBuffer[GlyphPage::size]; + wkGetGlyphs(fontData->platformData().cgFont(), buffer, localGlyphBuffer, bufferLength); + 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; + } + } + return haveGlyphs; +} + +} 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 new file mode 100644 index 0000000..5a4279a --- /dev/null +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2003, 2004, 2005, 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. + * + * 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 "GraphicsContext.h" + +#include "AffineTransform.h" +#include "NotImplemented.h" +#include "Path.h" + +#include <CoreGraphics/CGBitmapContext.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include "GraphicsContextPlatformPrivateCG.h" + +using namespace std; + +namespace WebCore { + +static CGContextRef CGContextWithHDC(HDC hdc, bool hasAlpha) +{ + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); + CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + BITMAP info; + + 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, bitmapInfo); + CGColorSpaceRelease(deviceRGB); + + // Flip coords + CGContextTranslateCTM(context, 0, info.bmHeight); + CGContextScaleCTM(context, 1, -1); + + // Put the HDC In advanced mode so it will honor affine transforms. + SetGraphicsMode(hdc, GM_ADVANCED); + + return context; +} + +GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha) + : m_common(createGraphicsContextPrivate()) + , m_data(new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc, hasAlpha))) +{ + CGContextRelease(m_data->m_cgContext); + m_data->m_hdc = hdc; + setPaintingDisabled(!m_data->m_cgContext); + if (m_data->m_cgContext) { + // Make sure the context starts in sync with our state. + setPlatformFillColor(fillColor()); + setPlatformStrokeColor(strokeColor()); + } +} + +bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } + +// 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) +{ + // FIXME: Should a bitmap be created also when a shadow is set? + if (mayCreateBitmap && inTransparencyLayer()) { + if (dstRect.isEmpty()) + return 0; + + // Create a bitmap DC in which to draw. + BITMAPINFO bitmapInfo; + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = dstRect.width(); + bitmapInfo.bmiHeader.biHeight = dstRect.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; + + void* pixels = 0; + HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); + if (!bitmap) + return 0; + + HDC bitmapDC = ::CreateCompatibleDC(m_data->m_hdc); + ::SelectObject(bitmapDC, bitmap); + + // Fill our buffer with clear if we're going to alpha blend. + if (supportAlphaBlend) { + BITMAP bmpInfo; + GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); + int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; + memset(bmpInfo.bmBits, 0, bufferSize); + } + + // Make sure we can do world transforms. + SetGraphicsMode(bitmapDC, GM_ADVANCED); + + // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap. + XFORM xform; + xform.eM11 = 1.0f; + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = 1.0f; + xform.eDx = -dstRect.x(); + xform.eDy = -dstRect.y(); + ::SetWorldTransform(bitmapDC, &xform); + + return bitmapDC; + } + + CGContextFlush(platformContext()); + m_data->save(); + return m_data->m_hdc; +} + +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) +{ + if (mayCreateBitmap && hdc && inTransparencyLayer()) { + if (dstRect.isEmpty()) + return; + + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); + + // Need to make a CGImage out of the bitmap's pixel buffer and then draw + // it into our context. + BITMAP info; + GetObject(bitmap, sizeof(info), &info); + ASSERT(info.bmBitsPixel == 32); + + CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + CGContextRef bitmapContext = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, + info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | + (supportAlphaBlend ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst)); + CGColorSpaceRelease(deviceRGB); + + CGImageRef image = CGBitmapContextCreateImage(bitmapContext); + CGContextDrawImage(m_data->m_cgContext, dstRect, image); + + // Delete all our junk. + CGImageRelease(image); + CGContextRelease(bitmapContext); + ::DeleteDC(hdc); + ::DeleteObject(bitmap); + + return; + } + + m_data->restore(); +} + +GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size) + : m_hdc(0) + , m_size(size) +{ + 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; + + m_hdc = CreateCompatibleDC(hdc); + SelectObject(m_hdc, m_bitmap); + + BITMAP bmpInfo; + GetObject(m_bitmap, sizeof(bmpInfo), &bmpInfo); + m_bytesPerRow = bmpInfo.bmWidthBytes; + m_bitmapBufferLength = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; + + SetGraphicsMode(m_hdc, GM_ADVANCED); +} + +GraphicsContext::WindowsBitmap::~WindowsBitmap() +{ + if (!m_bitmap) + return; + + DeleteDC(m_hdc); + DeleteObject(m_bitmap); +} + +GraphicsContext::WindowsBitmap* GraphicsContext::createWindowsBitmap(IntSize size) +{ + return new WindowsBitmap(m_data->m_hdc, size); +} + +void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point) +{ + 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) +{ + if (!m_hdc) + return; + + CGAffineTransform mat = transform; + XFORM xform; + xform.eM11 = mat.a; + xform.eM12 = mat.b; + xform.eM21 = mat.c; + xform.eM22 = mat.d; + xform.eDx = mat.tx; + xform.eDy = mat.ty; + + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +void GraphicsContext::drawFocusRing(const Color& color) +{ + if (paintingDisabled()) + return; + + float radius = (focusRingWidth() - 1) / 2.0f; + int offset = radius + focusRingOffset(); + CGColorRef colorRef = color.isValid() ? cgColor(color) : 0; + + CGMutablePathRef focusRingPath = CGPathCreateMutable(); + const Vector<IntRect>& rects = focusRingRects(); + unsigned rectCount = rects.size(); + for (unsigned i = 0; i < rectCount; i++) + CGPathAddRect(focusRingPath, 0, CGRectInset(rects[i], -offset, -offset)); + + CGContextRef context = platformContext(); + CGContextSaveGState(context); + + CGContextBeginPath(context); + CGContextAddPath(context, focusRingPath); + + wkDrawFocusRing(context, colorRef, radius); + + CGColorRelease(colorRef); + + CGPathRelease(focusRingPath); + + CGContextRestoreGState(context); +} + +// Pulled from GraphicsContextCG +static void setCGStrokeColor(CGContextRef context, const Color& color) +{ + CGFloat red, green, blue, alpha; + color.getRGBA(red, green, blue, alpha); + CGContextSetRGBStrokeColor(context, red, green, blue, alpha); +} + +static const Color& spellingPatternColor() { + static const Color spellingColor(255, 0, 0); + return spellingColor; +} + +static const Color& grammarPatternColor() { + static const Color grammarColor(0, 128, 0); + return grammarColor; +} + +void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& point, int width, bool grammar) +{ + if (paintingDisabled()) + return; + + // These are the same for misspelling or bad grammar + const int patternHeight = 3; // 3 rows + ASSERT(cMisspellingLineThickness == patternHeight); + const int patternWidth = 4; // 4 pixels + ASSERT(patternWidth == cMisspellingLinePatternWidth); + + // Make sure to draw only complete dots. + // NOTE: Code here used to shift the underline to the left and increase the width + // to make sure everything gets underlined, but that results in drawing out of + // bounds (e.g. when at the edge of a view) and could make it appear that the + // space between adjacent misspelled words was underlined. + // allow slightly more considering that the pattern ends with a transparent pixel + int widthMod = width % patternWidth; + if (patternWidth - widthMod > cMisspellingLinePatternGapWidth) + width -= widthMod; + + // Draw the underline + CGContextRef context = platformContext(); + CGContextSaveGState(context); + + const Color& patternColor = grammar ? grammarPatternColor() : spellingPatternColor(); + setCGStrokeColor(context, patternColor); + + wkSetPatternPhaseInUserSpace(context, point); + CGContextSetBlendMode(context, kCGBlendModeNormal); + + // 3 rows, each offset by half a pixel for blending purposes + const CGPoint upperPoints [] = {{point.x(), point.y() + patternHeight - 2.5 }, {point.x() + width, point.y() + patternHeight - 2.5}}; + const CGPoint middlePoints [] = {{point.x(), point.y() + patternHeight - 1.5 }, {point.x() + width, point.y() + patternHeight - 1.5}}; + const CGPoint lowerPoints [] = {{point.x(), point.y() + patternHeight - 0.5 }, {point.x() + width, point.y() + patternHeight - 0.5 }}; + + // Dash lengths for the top and bottom of the error underline are the same. + // These are magic. + static const float edge_dash_lengths[] = {2.0f, 2.0f}; + static const float middle_dash_lengths[] = {2.76f, 1.24f}; + static const float edge_offset = -(edge_dash_lengths[1] - 1.0f) / 2.0f; + static const float middle_offset = -(middle_dash_lengths[1] - 1.0f) / 2.0f; + + // Line opacities. Once again, these are magic. + const float upperOpacity = 0.33f; + const float middleOpacity = 0.75f; + const float lowerOpacity = 0.88f; + + //Top line + CGContextSetLineDash(context, edge_offset, edge_dash_lengths, + sizeof(edge_dash_lengths) / sizeof(edge_dash_lengths[0])); + CGContextSetAlpha(context, upperOpacity); + CGContextStrokeLineSegments(context, upperPoints, 2); + + // Middle line + CGContextSetLineDash(context, middle_offset, middle_dash_lengths, + sizeof(middle_dash_lengths) / sizeof(middle_dash_lengths[0])); + CGContextSetAlpha(context, middleOpacity); + CGContextStrokeLineSegments(context, middlePoints, 2); + + // Bottom line + CGContextSetLineDash(context, edge_offset, edge_dash_lengths, + sizeof(edge_dash_lengths) / sizeof(edge_dash_lengths[0])); + CGContextSetAlpha(context, lowerOpacity); + CGContextStrokeLineSegments(context, lowerPoints, 2); + + CGContextRestoreGState(context); +} + +} diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp new file mode 100644 index 0000000..3dcf6ba --- /dev/null +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -0,0 +1,124 @@ +/* + * 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 "GraphicsContext.h" + +#include "AffineTransform.h" +#include "NotImplemented.h" +#include "Path.h" + +#include <cairo-win32.h> +#include "GraphicsContextPlatformPrivateCairo.h" + +using namespace std; + +namespace WebCore { + +GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha) + : m_common(createGraphicsContextPrivate()) + , m_data(new GraphicsContextPlatformPrivate) +{ + if (dc) { + cairo_surface_t* surface = cairo_win32_surface_create(dc); + m_data->cr = cairo_create(surface); + m_data->m_hdc = dc; + } else { + setPaintingDisabled(true); + m_data->cr = 0; + m_data->m_hdc = 0; + } + + if (m_data->cr) { + // Make sure the context starts in sync with our state. + setPlatformFillColor(fillColor()); + setPlatformStrokeColor(strokeColor()); + } +} + +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); + SaveDC(hdc); + + // FIXME: We need to make sure a clip is really set on the HDC. + // Call SetWorldTransform to honor the current Cairo transform. + SetGraphicsMode(hdc, GM_ADVANCED); // We need this call for themes to honor world transforms. + cairo_matrix_t mat; + cairo_get_matrix(platformContext(), &mat); + 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; + ::SetWorldTransform(hdc, &xform); + + return hdc; +} + +bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } + +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 GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) +{ + cairo_surface_t* surface = cairo_get_target(cr); + HDC hdc = cairo_win32_surface_get_dc(surface); + SaveDC(hdc); + + const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); + + XFORM xform; + 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 new file mode 100644 index 0000000..dbf9fad --- /dev/null +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2003, 2004, 2005, 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. + * + * 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 "GraphicsContext.h" + +#if PLATFORM(CG) +#include "GraphicsContextPlatformPrivateCG.h" +#elif PLATFORM(CAIRO) +#include "GraphicsContextPlatformPrivateCairo.h" +#endif + +#include "AffineTransform.h" +#include "NotImplemented.h" +#include "Path.h" +#include <wtf/MathExtras.h> + +using namespace std; + +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*) +{ + // FIXME: This should go in GraphicsContextCG.cpp + notImplemented(); + return 0; +} +#endif + +} diff --git a/WebCore/platform/graphics/win/IconWin.cpp b/WebCore/platform/graphics/win/IconWin.cpp new file mode 100644 index 0000000..c02b56e --- /dev/null +++ b/WebCore/platform/graphics/win/IconWin.cpp @@ -0,0 +1,84 @@ +/* +* 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 +* 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 "Icon.h" + +#include "GraphicsContext.h" +#include "PlatformString.h" +#include <tchar.h> +#include <windows.h> + +namespace WebCore { + +static const int shell32MultipleFileIconIndex = 54; + +Icon::Icon(HICON icon) + : m_hIcon(icon) +{ + ASSERT(icon); +} + +Icon::~Icon() +{ + DestroyIcon(m_hIcon); +} + +PassRefPtr<Icon> Icon::createIconForFile(const String& filename) +{ + SHFILEINFO sfi; + memset(&sfi, 0, sizeof(sfi)); + + String tmpFilename = filename; + if (!SHGetFileInfo(tmpFilename.charactersWithNullTermination(), 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SHELLICONSIZE | SHGFI_SMALLICON)) + return 0; + + 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) +{ + if (context->paintingDisabled()) + return; + + HDC hdc = context->getWindowsContext(r); + + DrawIconEx(hdc, r.x(), r.y(), m_hIcon, r.width(), r.height(), 0, 0, DI_NORMAL); + + context->releaseWindowsContext(hdc, r); +} + +} diff --git a/WebCore/platform/graphics/win/ImageCGWin.cpp b/WebCore/platform/graphics/win/ImageCGWin.cpp new file mode 100644 index 0000000..752729c --- /dev/null +++ b/WebCore/platform/graphics/win/ImageCGWin.cpp @@ -0,0 +1,85 @@ +/* + * 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. + * + * 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 "Image.h" +#include "BitmapImage.h" +#include "GraphicsContext.h" +#include <ApplicationServices/ApplicationServices.h> + +#include <windows.h> +#include "PlatformString.h" + +namespace WebCore { + +bool BitmapImage::getHBITMAPOfSize(HBITMAP bmp, LPSIZE size) +{ + ASSERT(bmp); + + BITMAP bmpInfo; + GetObject(bmp, sizeof(BITMAP), &bmpInfo); + + ASSERT(bmpInfo.bmBitsPixel == 32); + int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; + + CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + CGContextRef cgContext = CGBitmapContextCreate(bmpInfo.bmBits, bmpInfo.bmWidth, bmpInfo.bmHeight, + 8, bmpInfo.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); + + GraphicsContext gc(cgContext); + + IntSize imageSize = BitmapImage::size(); + if (size) + drawFrameMatchingSourceSize(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), CompositeCopy); + else + draw(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), CompositeCopy); + + // Do cleanup + CGContextRelease(cgContext); + CGColorSpaceRelease(deviceRGB); + + return true; +} + +void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext* ctxt, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator compositeOp) +{ + int frames = frameCount(); + for (int i = 0; i < frames; ++i) { + CGImageRef image = frameAtIndex(i); + if (CGImageGetHeight(image) == static_cast<size_t>(srcSize.height()) && CGImageGetWidth(image) == static_cast<size_t>(srcSize.width())) { + size_t currentFrame = m_currentFrame; + m_currentFrame = i; + draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, srcSize.width(), srcSize.height()), compositeOp); + m_currentFrame = currentFrame; + return; + } + } + + // No image of the correct size was found, fallback to drawing the current frame + IntSize imageSize = BitmapImage::size(); + draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), compositeOp); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/win/ImageCairoWin.cpp b/WebCore/platform/graphics/win/ImageCairoWin.cpp new file mode 100644 index 0000000..95bb7bc --- /dev/null +++ b/WebCore/platform/graphics/win/ImageCairoWin.cpp @@ -0,0 +1,89 @@ +/* + * 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 "Image.h" +#include "BitmapImage.h" +#include "GraphicsContext.h" +#include <cairo.h> + +#include <windows.h> +#include "PlatformString.h" + +namespace WebCore { + +bool BitmapImage::getHBITMAPOfSize(HBITMAP bmp, LPSIZE size) +{ + ASSERT(bmp); + + BITMAP bmpInfo; + GetObject(bmp, sizeof(BITMAP), &bmpInfo); + + // If this is a 32bpp bitmap, which it always should be, we'll clear it so alpha-wise it will be visible + if (bmpInfo.bmBitsPixel == 32) { + int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; + memset(bmpInfo.bmBits, 255, bufferSize); + } + + HDC tempDC = CreateCompatibleDC(0); + if (!tempDC) { + LOG_ERROR("Failed to create in-memory DC for Image::blit()"); + return false; + } + SelectObject(tempDC, bmp); + GraphicsContext gc(tempDC); + + IntSize imageSize = BitmapImage::size(); + if (size) + drawFrameMatchingSourceSize(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), CompositeCopy); + else + draw(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), CompositeCopy); + + // Do cleanup + DeleteDC(tempDC); + + return true; +} + +void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext* ctxt, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator compositeOp) +{ + int frames = frameCount(); + for (int i = 0; i < frames; ++i) { + cairo_surface_t* image = frameAtIndex(i); + if (cairo_image_surface_get_height(image) == static_cast<size_t>(srcSize.height()) && cairo_image_surface_get_width(image) == static_cast<size_t>(srcSize.width())) { + size_t currentFrame = m_currentFrame; + m_currentFrame = i; + draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, srcSize.width(), srcSize.height()), compositeOp); + m_currentFrame = currentFrame; + return; + } + } + + // No image of the correct size was found, fallback to drawing the current frame + IntSize imageSize = BitmapImage::size(); + draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), compositeOp); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/win/ImageWin.cpp b/WebCore/platform/graphics/win/ImageWin.cpp new file mode 100644 index 0000000..54c5b41 --- /dev/null +++ b/WebCore/platform/graphics/win/ImageWin.cpp @@ -0,0 +1,58 @@ +/* + * 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. + * + * 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 "Image.h" +#include "BitmapImage.h" + +#include "SharedBuffer.h" + +// This function loads resources from WebKit +PassRefPtr<WebCore::SharedBuffer> loadResourceIntoBuffer(const char*); + +namespace WebCore { + +void BitmapImage::initPlatformData() +{ +} + +void BitmapImage::invalidatePlatformData() +{ +} + +PassRefPtr<Image> Image::loadPlatformResource(const char *name) +{ + RefPtr<SharedBuffer> buffer = loadResourceIntoBuffer(name); + RefPtr<BitmapImage> img = BitmapImage::create(); + img->setData(buffer.release(), true); + return img.release(); +} + +bool BitmapImage::getHBITMAP(HBITMAP bmp) +{ + return getHBITMAPOfSize(bmp, 0); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/win/IntPointWin.cpp b/WebCore/platform/graphics/win/IntPointWin.cpp new file mode 100644 index 0000000..a6ce0bb --- /dev/null +++ b/WebCore/platform/graphics/win/IntPointWin.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 "IntPoint.h" + +#include <windows.h> + +namespace WebCore { + +IntPoint::IntPoint(const POINT& p) + : m_x(p.x) + , m_y(p.y) +{ +} + +IntPoint::operator POINT() const +{ + POINT p = {m_x, m_y}; + return p; +} + +IntPoint::IntPoint(const POINTS& p) + : m_x(p.x) + , m_y(p.y) +{ +} + +IntPoint::operator POINTS() const +{ + POINTS p = {m_x, m_y}; + return p; +} + +} diff --git a/WebCore/platform/graphics/win/IntRectWin.cpp b/WebCore/platform/graphics/win/IntRectWin.cpp new file mode 100644 index 0000000..6228be8 --- /dev/null +++ b/WebCore/platform/graphics/win/IntRectWin.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 "IntRect.h" + +#include <windows.h> + +namespace WebCore { + +IntRect::IntRect(const RECT& r) + : m_location(IntPoint(r.left, r.top)), m_size(IntSize(r.right-r.left, r.bottom-r.top)) +{ +} + +IntRect::operator RECT() const +{ + RECT rect = { x(), y(), right(), bottom() }; + return rect; +} + +} diff --git a/WebCore/platform/graphics/win/IntSizeWin.cpp b/WebCore/platform/graphics/win/IntSizeWin.cpp new file mode 100644 index 0000000..8a27cdb --- /dev/null +++ b/WebCore/platform/graphics/win/IntSizeWin.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 "IntSize.h" + +#include <windows.h> + +namespace WebCore { + +IntSize::IntSize(const SIZE& s) + : m_width(s.cx) + , m_height(s.cy) +{ +} + +IntSize::operator SIZE() const +{ + SIZE s = {m_width, m_height}; + return s; +} + +} diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp new file mode 100644 index 0000000..cef4217 --- /dev/null +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2007 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" + +#if ENABLE(VIDEO) +#include "MediaPlayerPrivateQuickTimeWin.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 { + +static const double endPointTimerInterval = 0.020; + +MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) + : m_player(player) + , m_seekTo(-1) + , m_endTime(numeric_limits<float>::infinity()) + , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) + , m_endPointTimer(this, &MediaPlayerPrivate::endPointTimerFired) + , m_networkState(MediaPlayer::Empty) + , m_readyState(MediaPlayer::DataUnavailable) + , m_startedPlaying(false) + , m_isStreaming(false) +#if DRAW_FRAME_RATE + , m_frameCountWhilePlaying(0) + , m_timeStartedPlaying(0) + , m_timeStoppedPlaying(0) +#endif +{ +} + +MediaPlayerPrivate::~MediaPlayerPrivate() +{ +} + +void MediaPlayerPrivate::load(const String& url) +{ + if (!QTMovieWin::initializeQuickTime()) { + m_networkState = MediaPlayer::LoadFailed; + m_player->networkStateChanged(); + return; + } + + if (m_networkState != MediaPlayer::Loading) { + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + } + if (m_readyState != MediaPlayer::DataUnavailable) { + m_readyState = MediaPlayer::DataUnavailable; + m_player->readyStateChanged(); + } + cancelSeek(); + m_endPointTimer.stop(); + + m_qtMovie.set(new QTMovieWin(this)); + m_qtMovie->load(url.characters(), url.length()); + m_qtMovie->setVolume(m_player->m_volume); + m_qtMovie->setVisible(m_player->m_visible); +} + +void MediaPlayerPrivate::play() +{ + if (!m_qtMovie) + return; + m_startedPlaying = true; +#if DRAW_FRAME_RATE + m_frameCountWhilePlaying = 0; +#endif + + m_qtMovie->play(); + startEndPointTimerIfNeeded(); +} + +void MediaPlayerPrivate::pause() +{ + if (!m_qtMovie) + return; + m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = GetTickCount(); +#endif + m_qtMovie->pause(); + m_endPointTimer.stop(); +} + +float MediaPlayerPrivate::duration() const +{ + if (!m_qtMovie) + return 0; + return m_qtMovie->duration(); +} + +float MediaPlayerPrivate::currentTime() const +{ + if (!m_qtMovie) + return 0; + return min(m_qtMovie->currentTime(), m_endTime); +} + +void MediaPlayerPrivate::seek(float time) +{ + cancelSeek(); + + if (!m_qtMovie) + return; + + if (time > duration()) + time = duration(); + + m_seekTo = time; + if (maxTimeLoaded() >= m_seekTo) + doSeek(); + else + m_seekTimer.start(0, 0.5f); +} + +void MediaPlayerPrivate::doSeek() +{ + float oldRate = m_qtMovie->rate(); + m_qtMovie->setRate(0); + m_qtMovie->setCurrentTime(m_seekTo); + float timeAfterSeek = currentTime(); + // restore playback only if not at end, othewise QTMovie will loop + if (timeAfterSeek < duration() && timeAfterSeek < m_endTime) + m_qtMovie->setRate(oldRate); + cancelSeek(); +} + +void MediaPlayerPrivate::cancelSeek() +{ + m_seekTo = -1; + m_seekTimer.stop(); +} + +void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) +{ + if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) { + cancelSeek(); + updateStates(); + m_player->timeChanged(); + return; + } + + if (maxTimeLoaded() >= m_seekTo) + doSeek(); + else { + MediaPlayer::NetworkState state = networkState(); + if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) { + cancelSeek(); + updateStates(); + m_player->timeChanged(); + } + } +} + +void MediaPlayerPrivate::setEndTime(float time) +{ + m_endTime = time; + startEndPointTimerIfNeeded(); +} + +void MediaPlayerPrivate::startEndPointTimerIfNeeded() +{ + if (m_endTime < duration() && m_startedPlaying && !m_endPointTimer.isActive()) + m_endPointTimer.startRepeating(endPointTimerInterval); +} + +void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*) +{ + float time = currentTime(); + if (time >= m_endTime) { + pause(); + didEnd(); + } +} + +bool MediaPlayerPrivate::paused() const +{ + if (!m_qtMovie) + return true; + return m_qtMovie->rate() == 0.0f; +} + +bool MediaPlayerPrivate::seeking() const +{ + if (!m_qtMovie) + return false; + return m_seekTo >= 0; +} + +IntSize MediaPlayerPrivate::naturalSize() const +{ + if (!m_qtMovie) + return IntSize(); + int width; + int height; + m_qtMovie->getNaturalSize(width, height); + return IntSize(width, height); +} + +bool MediaPlayerPrivate::hasVideo() const +{ + // This is not used at the moment + return true; +} + +void MediaPlayerPrivate::setVolume(float volume) +{ + if (!m_qtMovie) + return; + m_qtMovie->setVolume(volume); +} + +void MediaPlayerPrivate::setRate(float rate) +{ + if (!m_qtMovie) + return; + if (!paused()) + m_qtMovie->setRate(rate); +} + +int MediaPlayerPrivate::dataRate() const +{ + // This is not used at the moment + return 0; +} + +float MediaPlayerPrivate::maxTimeBuffered() const +{ + // rtsp streams are not buffered + return m_isStreaming ? 0 : maxTimeLoaded(); +} + +float MediaPlayerPrivate::maxTimeSeekable() const +{ + // infinite duration means live stream + return !isfinite(duration()) ? 0 : maxTimeLoaded(); +} + +float MediaPlayerPrivate::maxTimeLoaded() const +{ + if (!m_qtMovie) + return 0; + return m_qtMovie->maxTimeLoaded(); +} + +unsigned MediaPlayerPrivate::bytesLoaded() const +{ + if (!m_qtMovie) + return 0; + float dur = duration(); + float maxTime = maxTimeLoaded(); + if (!dur) + return 0; + return totalBytes() * maxTime / dur; +} + +bool MediaPlayerPrivate::totalBytesKnown() const +{ + return totalBytes() > 0; +} + +unsigned MediaPlayerPrivate::totalBytes() const +{ + if (!m_qtMovie) + return 0; + return m_qtMovie->dataSize(); +} + +void MediaPlayerPrivate::cancelLoad() +{ + if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) + return; + + // Cancel the load by destroying the movie. + m_qtMovie.clear(); + + updateStates(); +} + +void MediaPlayerPrivate::updateStates() +{ + MediaPlayer::NetworkState oldNetworkState = m_networkState; + MediaPlayer::ReadyState oldReadyState = m_readyState; + + long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError; + + 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> + if (!enabledTrackCount) + loadState = QTMovieLoadStateError; + } + + // "Loaded" is reserved for fully buffered movies, never the case when streaming + if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { + if (m_networkState < MediaPlayer::Loaded) + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::CanPlayThrough; + } else if (loadState >= QTMovieLoadStatePlaythroughOK) { + if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) + m_networkState = MediaPlayer::LoadedFirstFrame; + m_readyState = MediaPlayer::CanPlayThrough; + } else if (loadState >= QTMovieLoadStatePlayable) { + if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) + m_networkState = MediaPlayer::LoadedFirstFrame; + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::CanPlay : MediaPlayer::DataUnavailable; + } else if (loadState >= QTMovieLoadStateLoaded) { + if (m_networkState < MediaPlayer::LoadedMetaData) + m_networkState = MediaPlayer::LoadedMetaData; + m_readyState = MediaPlayer::DataUnavailable; + } else if (loadState > QTMovieLoadStateError) { + if (m_networkState < MediaPlayer::Loading) + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::DataUnavailable; + } else { + m_networkState = MediaPlayer::LoadFailed; + m_readyState = MediaPlayer::DataUnavailable; + } + + if (seeking()) + m_readyState = MediaPlayer::DataUnavailable; + + if (m_networkState != oldNetworkState) + m_player->networkStateChanged(); + if (m_readyState != oldReadyState) + m_player->readyStateChanged(); +} + + +void MediaPlayerPrivate::didEnd() +{ + m_endPointTimer.stop(); + m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = GetTickCount(); +#endif + updateStates(); + m_player->timeChanged(); +} + +void MediaPlayerPrivate::setRect(const IntRect& r) +{ + if (m_qtMovie) + m_qtMovie->setSize(r.width(), r.height()); +} + +void MediaPlayerPrivate::setVisible(bool b) +{ + if (!m_qtMovie) + return; + m_qtMovie->setVisible(b); +} + +void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) +{ + if (p->paintingDisabled() || !m_qtMovie) + return; + 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) +{ + unsigned count = QTMovieWin::countSupportedTypes(); + for (unsigned n = 0; n < count; n++) { + const UChar* character; + unsigned len; + QTMovieWin::getSupportedType(n, character, len); + if (len) + types.add(String(character, len)); + } +} + +bool MediaPlayerPrivate::isAvailable() +{ + return QTMovieWin::initializeQuickTime(); +} + +void MediaPlayerPrivate::movieEnded(QTMovieWin* movie) +{ + ASSERT(m_qtMovie.get() == movie); + didEnd(); +} + +void MediaPlayerPrivate::movieLoadStateChanged(QTMovieWin* movie) +{ + ASSERT(m_qtMovie.get() == movie); + updateStates(); +} + +void MediaPlayerPrivate::movieTimeChanged(QTMovieWin* movie) +{ + ASSERT(m_qtMovie.get() == movie); + updateStates(); + m_player->timeChanged(); +} + +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(); +} + +} + +#endif + diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h new file mode 100644 index 0000000..c4c893c --- /dev/null +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef MediaPlayerPrivateQTKit_h +#define MediaPlayerPrivateQTKit_h + +#if ENABLE(VIDEO) + +#include "MediaPlayer.h" +#include "Timer.h" +#include <QTMovieWin.h> +#include <wtf/OwnPtr.h> + +#ifndef DRAW_FRAME_RATE +#define DRAW_FRAME_RATE 0 +#endif + +namespace WebCore { + +class GraphicsContext; +class IntSize; +class IntRect; +class String; + +class MediaPlayerPrivate : QTMovieWinClient, Noncopyable { +public: + MediaPlayerPrivate(MediaPlayer*); + ~MediaPlayerPrivate(); + + IntSize naturalSize() const; + bool hasVideo() const; + + void load(const String& url); + void cancelLoad(); + + void play(); + void pause(); + + bool paused() const; + bool seeking() const; + + float duration() const; + float currentTime() const; + void seek(float time); + void setEndTime(float); + + void setRate(float); + void setVolume(float); + + int dataRate() const; + + MediaPlayer::NetworkState networkState() const { return m_networkState; } + MediaPlayer::ReadyState readyState() const { return m_readyState; } + + float maxTimeBuffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + bool totalBytesKnown() const; + unsigned totalBytes() const; + + void setVisible(bool); + void setRect(const IntRect&); + + void loadStateChanged(); + void didEnd(); + + void paint(GraphicsContext*, const IntRect&); + + static void getSupportedTypes(HashSet<String>& types); + static bool isAvailable(); + +private: + void updateStates(); + void doSeek(); + void cancelSeek(); + void seekTimerFired(Timer<MediaPlayerPrivate>*); + void endPointTimerFired(Timer<MediaPlayerPrivate>*); + float maxTimeLoaded() const; + void startEndPointTimerIfNeeded(); + + virtual void movieEnded(QTMovieWin*); + virtual void movieLoadStateChanged(QTMovieWin*); + virtual void movieTimeChanged(QTMovieWin*); + virtual void movieNewImageAvailable(QTMovieWin*); + + MediaPlayer* m_player; + OwnPtr<QTMovieWin> m_qtMovie; + float m_seekTo; + float m_endTime; + Timer<MediaPlayerPrivate> m_seekTimer; + Timer<MediaPlayerPrivate> m_endPointTimer; + MediaPlayer::NetworkState m_networkState; + MediaPlayer::ReadyState m_readyState; + bool m_startedPlaying; + bool m_isStreaming; +#if DRAW_FRAME_RATE + int m_frameCountWhilePlaying; + int m_timeStartedPlaying; + int m_timeStoppedPlaying; +#endif +}; + +} + +#endif +#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/OpenTypeUtilities.h b/WebCore/platform/graphics/win/OpenTypeUtilities.h new file mode 100644 index 0000000..ab35551 --- /dev/null +++ b/WebCore/platform/graphics/win/OpenTypeUtilities.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef OpenTypeUtilities_h +#define OpenTypeUtilities_h + +#include "PlatformString.h" +#include <wtf/Forward.h> + +namespace WebCore { + +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 new file mode 100644 index 0000000..8eee41b --- /dev/null +++ b/WebCore/platform/graphics/win/QTMovieWin.cpp @@ -0,0 +1,875 @@ +/* + * Copyright (C) 2007 Apple Computer, 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 <windows.h> + +#include "QTMovieWin.h" + +// Put Movies.h first so build failures here point clearly to QuickTime +#include <Movies.h> +#include <QuickTimeComponents.h> +#include <GXMath.h> +#include <QTML.h> + +#include "QTMovieWinTimer.h" + +#include <wtf/Assertions.h> +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +using namespace std; + +static const long minimumQuickTimeVersion = 0x07300000; // 7.3 + +// Resizing GWorlds is slow, give them a minimum size so size of small +// videos can be animated smoothly +static const int cGWorldMinWidth = 640; +static const int cGWorldMinHeight = 360; + +static const float cNonContinuousTimeChange = 0.2f; + +union UppParam { + long longValue; + void* ptr; +}; + +static MovieDrawingCompleteUPP gMovieDrawingCompleteUPP = 0; +static HashSet<QTMovieWinPrivate*>* gTaskList; +static Vector<CFStringRef>* gSupportedTypes = 0; +static SInt32 quickTimeVersion = 0; + +static void updateTaskTimer(int maxInterval = 1000) +{ + if (!gTaskList->size()) { + stopSharedTimer(); + return; + } + + long intervalInMS; + QTGetTimeUntilNextTask(&intervalInMS, 1000); + if (intervalInMS > maxInterval) + intervalInMS = maxInterval; + setSharedTimerFireDelay(static_cast<float>(intervalInMS) / 1000); +} + +class QTMovieWinPrivate : Noncopyable { +public: + QTMovieWinPrivate(); + ~QTMovieWinPrivate(); + void task(); + 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; + bool m_ended; + bool m_seeking; + float m_lastMediaTime; + double m_lastLoadStateCheckTime; + int m_width; + int m_height; + bool m_visible; + GWorldPtr m_gWorld; + int m_gWorldWidth; + int m_gWorldHeight; + GWorldPtr m_savedGWorld; + long m_loadError; +}; + +QTMovieWinPrivate::QTMovieWinPrivate() + : m_movieWin(0) + , m_movie(0) + , m_movieController(0) + , m_tasking(false) + , m_client(0) + , m_loadState(0) + , m_ended(false) + , m_seeking(false) + , m_lastMediaTime(0) + , m_lastLoadStateCheckTime(0) + , m_width(0) + , m_height(0) + , m_visible(false) + , m_gWorld(0) + , m_gWorldWidth(0) + , m_gWorldHeight(0) + , m_savedGWorld(0) + , m_loadError(0) +{ +} + +QTMovieWinPrivate::~QTMovieWinPrivate() +{ + endTask(); + if (m_gWorld) + deleteGWorld(); + if (m_movieController) + DisposeMovieController(m_movieController); + if (m_movie) + DisposeMovie(m_movie); +} + +static void taskTimerFired() +{ + // The hash content might change during task() + Vector<QTMovieWinPrivate*> tasks; + copyToVector(*gTaskList, tasks); + size_t count = tasks.size(); + for (unsigned n = 0; n < count; ++n) + tasks[n]->task(); + + updateTaskTimer(); +} + +void QTMovieWinPrivate::startTask() +{ + if (m_tasking) + return; + if (!gTaskList) + gTaskList = new HashSet<QTMovieWinPrivate*>; + gTaskList->add(this); + m_tasking = true; + updateTaskTimer(); +} + +void QTMovieWinPrivate::endTask() +{ + if (!m_tasking) + return; + gTaskList->remove(this); + m_tasking = false; + updateTaskTimer(); +} + +void QTMovieWinPrivate::task() +{ + ASSERT(m_tasking); + + 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) { + // If load fails QT's load state is kMovieLoadStateComplete. + // 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(); + } + + bool ended = !!IsMovieDone(m_movie); + if (ended != m_ended) { + m_ended = ended; + if (m_client && ended) + m_client->movieEnded(m_movieWin); + } + + float time = m_movieWin->currentTime(); + if (time < m_lastMediaTime || time >= m_lastMediaTime + cNonContinuousTimeChange || m_seeking) { + m_seeking = false; + if (m_client) + m_client->movieTimeChanged(m_movieWin); + } + m_lastMediaTime = time; + + if (m_loadError) + 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; + param.ptr = this; + SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, gMovieDrawingCompleteUPP, param.longValue); +} + +void QTMovieWinPrivate::drawingComplete() +{ + if (!m_gWorld || m_loadState < kMovieLoadStateLoaded) + return; + m_client->movieNewImageAvailable(m_movieWin); +} + +void QTMovieWinPrivate::updateGWorld() +{ + bool shouldBeVisible = m_visible; + if (!m_height || !m_width) + shouldBeVisible = false; + + if (shouldBeVisible && !m_gWorld) + createGWorld(); + else if (!shouldBeVisible && m_gWorld) + deleteGWorld(); + else if (m_gWorld && (m_width > m_gWorldWidth || m_height > m_gWorldHeight)) { + // need a bigger, better gWorld + deleteGWorld(); + createGWorld(); + } +} + +void QTMovieWinPrivate::createGWorld() +{ + ASSERT(!m_gWorld); + if (!m_movie) + return; + + m_gWorldWidth = max(cGWorldMinWidth, m_width); + m_gWorldHeight = max(cGWorldMinHeight, m_height); + Rect bounds; + bounds.top = 0; + bounds.left = 0; + bounds.right = m_gWorldWidth; + bounds.bottom = m_gWorldHeight; + OSErr err = QTNewGWorld(&m_gWorld, k32BGRAPixelFormat, &bounds, NULL, NULL, NULL); + 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) + return; + m_width = width; + m_height = height; + if (!m_movie) + return; + Rect bounds; + bounds.top = 0; + bounds.left = 0; + bounds.right = width; + bounds.bottom = height; + if (m_movieController) + MCSetControllerBoundsRect(m_movieController, &bounds); + SetMovieBox(m_movie, &bounds); + updateGWorld(); +} + +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; + DisposeGWorld(m_gWorld); + m_gWorld = 0; + m_gWorldWidth = 0; + m_gWorldHeight = 0; +} + + +QTMovieWin::QTMovieWin(QTMovieWinClient* client) + : m_private(new QTMovieWinPrivate()) +{ + m_private->m_movieWin = this; + m_private->m_client = client; + initializeQuickTime(); +} + +QTMovieWin::~QTMovieWin() +{ + delete m_private; +} + +void QTMovieWin::play() +{ + 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() +{ + 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) +{ + 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(); +} + +float QTMovieWin::duration() const +{ + if (!m_private->m_movie) + return 0; + TimeValue val = GetMovieDuration(m_private->m_movie); + TimeScale scale = GetMovieTimeScale(m_private->m_movie); + return static_cast<float>(val) / scale; +} + +float QTMovieWin::currentTime() const +{ + if (!m_private->m_movie) + return 0; + TimeValue val = GetMovieTime(m_private->m_movie, 0); + TimeScale scale = GetMovieTimeScale(m_private->m_movie); + return static_cast<float>(val) / scale; +} + +void QTMovieWin::setCurrentTime(float time) const +{ + if (!m_private->m_movie) + return; + m_private->m_seeking = true; + TimeScale scale = GetMovieTimeScale(m_private->m_movie); + 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 +{ + if (!m_private->m_movie) + return 0; + return GetMovieDataSize(m_private->m_movie, 0, GetMovieDuration(m_private->m_movie)); +} + +float QTMovieWin::maxTimeLoaded() const +{ + if (!m_private->m_movie) + return 0; + TimeValue val; + GetMaxLoadedTimeInMovie(m_private->m_movie, &val); + TimeScale scale = GetMovieTimeScale(m_private->m_movie); + return static_cast<float>(val) / scale; +} + +long QTMovieWin::loadState() const +{ + return m_private->m_loadState; +} + +void QTMovieWin::getNaturalSize(int& width, int& height) +{ + Rect rect = { 0, }; + + if (m_private->m_movie) + GetMovieNaturalBoundsRect(m_private->m_movie, &rect); + width = rect.right; + height = rect.bottom; +} + +void QTMovieWin::setSize(int width, int height) +{ + m_private->setSize(width, height); + updateTaskTimer(0); +} + +void QTMovieWin::setVisible(bool b) +{ + m_private->m_visible = b; + m_private->updateGWorld(); +} + +void QTMovieWin::paint(HDC hdc, int x, int y) +{ + if (!m_private->m_gWorld) + return; + + HDC hdcSrc = static_cast<HDC>(GetPortHDC(reinterpret_cast<GrafPtr>(m_private->m_gWorld))); + if (!hdcSrc) + return; + + // FIXME: If we could determine the movie has no alpha, we could use BitBlt for those cases, which might be faster. + BLENDFUNCTION blendFunction; + blendFunction.BlendOp = AC_SRC_OVER; + blendFunction.BlendFlags = 0; + blendFunction.SourceConstantAlpha = 255; + blendFunction.AlphaFormat = AC_SRC_ALPHA; + AlphaBlend(hdc, x, y, m_private->m_width, m_private->m_height, hdcSrc, + 0, 0, m_private->m_width, m_private->m_height, blendFunction); +} + +void QTMovieWin::load(const UChar* url, int len) +{ + if (m_private->m_movie) { + 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; + } + + // Define a property array for NewMovieFromProperties. 8 should be enough for our needs. + QTNewMoviePropertyElement movieProps[8]; + ItemCount moviePropCount = 0; + + bool boolTrue = true; + + // Create a URL data reference of type CFURL + CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len); + + // Disable streaming support for now. + if (CFStringHasPrefix(urlStringRef, CFSTR("rtsp:"))) { + m_private->m_loadError = noMovieFound; + goto end; + } + + CFURLRef urlRef = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, 0); + + // Add the movie data location to the property array + movieProps[moviePropCount].propClass = kQTPropertyClass_DataLocation; + movieProps[moviePropCount].propID = kQTDataLocationPropertyID_CFURL; + movieProps[moviePropCount].propValueSize = sizeof(urlRef); + movieProps[moviePropCount].propValueAddress = &urlRef; + movieProps[moviePropCount].propStatus = 0; + moviePropCount++; + + movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; + movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_DontAskUnresolvedDataRefs; + movieProps[moviePropCount].propValueSize = sizeof(boolTrue); + movieProps[moviePropCount].propValueAddress = &boolTrue; + movieProps[moviePropCount].propStatus = 0; + moviePropCount++; + + movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; + movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_AsyncOK; + movieProps[moviePropCount].propValueSize = sizeof(boolTrue); + movieProps[moviePropCount].propValueAddress = &boolTrue; + movieProps[moviePropCount].propStatus = 0; + moviePropCount++; + + movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty; + movieProps[moviePropCount].propID = kQTNewMoviePropertyID_Active; + movieProps[moviePropCount].propValueSize = sizeof(boolTrue); + movieProps[moviePropCount].propValueAddress = &boolTrue; + movieProps[moviePropCount].propStatus = 0; + moviePropCount++; + + movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty; + movieProps[moviePropCount].propID = kQTNewMoviePropertyID_DontInteractWithUser; + movieProps[moviePropCount].propValueSize = sizeof(boolTrue); + movieProps[moviePropCount].propValueAddress = &boolTrue; + movieProps[moviePropCount].propStatus = 0; + moviePropCount++; + + movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; + movieProps[moviePropCount].propID = '!url'; + movieProps[moviePropCount].propValueSize = sizeof(boolTrue); + movieProps[moviePropCount].propValueAddress = &boolTrue; + movieProps[moviePropCount].propStatus = 0; + moviePropCount++; + + movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; + movieProps[moviePropCount].propID = 'site'; + movieProps[moviePropCount].propValueSize = sizeof(boolTrue); + movieProps[moviePropCount].propValueAddress = &boolTrue; + movieProps[moviePropCount].propStatus = 0; + moviePropCount++; + + m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, NULL, &m_private->m_movie); + + CFRelease(urlRef); +end: + m_private->startTask(); + // get the load fail callback quickly + if (m_private->m_loadError) + updateTaskTimer(0); + else + m_private->registerDrawingCallback(); + + CFRelease(urlStringRef); +} + +void QTMovieWin::disableUnsupportedTracks(unsigned& enabledTrackCount) +{ + if (!m_private->m_movie) { + enabledTrackCount = 0; + return; + } + + static HashSet<OSType>* allowedTrackTypes = 0; + if (!allowedTrackTypes) { + allowedTrackTypes = new HashSet<OSType>; + allowedTrackTypes->add(VideoMediaType); + allowedTrackTypes->add(SoundMediaType); + allowedTrackTypes->add(TextMediaType); + allowedTrackTypes->add(BaseMediaType); + allowedTrackTypes->add('clcp'); // Closed caption + allowedTrackTypes->add('sbtl'); // Subtitle + } + + long trackCount = GetMovieTrackCount(m_private->m_movie); + enabledTrackCount = trackCount; + + // Track indexes are 1-based. yuck. These things must descend from old- + // school mac resources or something. + for (long trackIndex = 1; trackIndex <= trackCount; trackIndex++) { + // Grab the track at the current index. If there isn't one there, then + // we can move onto the next one. + Track currentTrack = GetMovieIndTrack(m_private->m_movie, trackIndex); + if (!currentTrack) + continue; + + // Check to see if the track is disabled already, we should move along. + // We don't need to re-disable it. + if (!GetTrackEnabled(currentTrack)) + continue; + + // Grab the track's media. We're going to check to see if we need to + // disable the tracks. They could be unsupported. + Media trackMedia = GetTrackMedia(currentTrack); + if (!trackMedia) + continue; + + // Grab the media type for this track. Make sure that we don't + // get an error in doing so. If we do, then something really funky is + // wrong. + OSType mediaType; + GetMediaHandlerDescription(trackMedia, &mediaType, nil, nil); + OSErr mediaErr = GetMoviesError(); + if (mediaErr != noErr) + continue; + + if (!allowedTrackTypes->contains(mediaType)) { + SetTrackEnabled(currentTrack, false); + --enabledTrackCount; + } + + // Grab the track reference count for chapters. This will tell us if it + // has chapter tracks in it. If there aren't any references, then we + // can move on the next track. + long referenceCount = GetTrackReferenceCount(currentTrack, kTrackReferenceChapterList); + if (referenceCount <= 0) + continue; + + long referenceIndex = 0; + while (1) { + // If we get nothing here, we've overstepped our bounds and can stop + // looking. Chapter indices here are 1-based as well - hence, the + // pre-increment. + referenceIndex++; + Track chapterTrack = GetTrackReference(currentTrack, kTrackReferenceChapterList, referenceIndex); + if (!chapterTrack) + break; + + // Try to grab the media for the track. + Media chapterMedia = GetTrackMedia(chapterTrack); + if (!chapterMedia) + continue; + + // Grab the media type for this track. Make sure that we don't + // get an error in doing so. If we do, then something really + // funky is wrong. + OSType mediaType; + GetMediaHandlerDescription(chapterMedia, &mediaType, nil, nil); + OSErr mediaErr = GetMoviesError(); + if (mediaErr != noErr) + continue; + + // Check to see if the track is a video track. We don't care about + // other non-video tracks. + if (mediaType != VideoMediaType) + continue; + + // Check to see if the track is already disabled. If it is, we + // should move along. + if (!GetTrackEnabled(chapterTrack)) + continue; + + // Disabled the evil, evil track. + SetTrackEnabled(chapterTrack, false); + --enabledTrackCount; + } + } +} + +pascal OSErr movieDrawingCompleteProc(Movie movie, long data) +{ + UppParam param; + param.longValue = data; + QTMovieWinPrivate* mp = static_cast<QTMovieWinPrivate*>(param.ptr); + if (mp) + mp->drawingComplete(); + return 0; +} + +static void initializeSupportedTypes() +{ + if (gSupportedTypes) + return; + + gSupportedTypes = new Vector<CFStringRef>; + 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")); + + 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() +{ + initializeSupportedTypes(); + return static_cast<unsigned>(gSupportedTypes->size()); +} + +void QTMovieWin::getSupportedType(unsigned index, const UChar*& str, unsigned& len) +{ + initializeSupportedTypes(); + ASSERT(index < gSupportedTypes->size()); + + // Allocate sufficient buffer to hold any MIME type + static UniChar* staticBuffer = 0; + if (!staticBuffer) + staticBuffer = new UniChar[32]; + + CFStringRef cfstr = gSupportedTypes->at(index); + len = CFStringGetLength(cfstr); + CFRange range = { 0, len }; + CFStringGetCharacters(cfstr, range, staticBuffer); + str = reinterpret_cast<const UChar*>(staticBuffer); + +} + +bool QTMovieWin::initializeQuickTime() +{ + static bool initialized = false; + static bool initializationSucceeded = false; + if (!initialized) { + initialized = true; + // Initialize and check QuickTime version + OSErr result = InitializeQTML(0); + if (result == noErr) + result = Gestalt(gestaltQuickTime, &quickTimeVersion); + if (result != noErr) { + LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support."); + return false; + } + if (quickTimeVersion < minimumQuickTimeVersion) { + LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", quickTimeVersion, minimumQuickTimeVersion); + return false; + } + EnterMovies(); + setSharedTimerFiredFunction(taskTimerFired); + gMovieDrawingCompleteUPP = NewMovieDrawingCompleteUPP(movieDrawingCompleteProc); + initializationSucceeded = true; + } + return initializationSucceeded; +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + setSharedTimerInstanceHandle(hinstDLL); + return TRUE; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + return FALSE; + } + ASSERT_NOT_REACHED(); + return FALSE; +} diff --git a/WebCore/platform/graphics/win/QTMovieWin.h b/WebCore/platform/graphics/win/QTMovieWin.h new file mode 100644 index 0000000..e31780a --- /dev/null +++ b/WebCore/platform/graphics/win/QTMovieWin.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007 Apple Computer, 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. + */ + +#ifndef QTMovieWin_h +#define QTMovieWin_h + +#include <Unicode.h> + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +class QTMovieWin; +class QTMovieWinPrivate; + +class QTMovieWinClient { +public: + virtual void movieEnded(QTMovieWin*) = 0; + virtual void movieLoadStateChanged(QTMovieWin*) = 0; + virtual void movieTimeChanged(QTMovieWin*) = 0; + virtual void movieNewImageAvailable(QTMovieWin*) = 0; +}; + +enum { + QTMovieLoadStateError = -1L, + QTMovieLoadStateLoaded = 2000L, + QTMovieLoadStatePlayable = 10000L, + QTMovieLoadStatePlaythroughOK = 20000L, + QTMovieLoadStateComplete = 100000L +}; + +class QTMOVIEWIN_API QTMovieWin { +public: + static bool initializeQuickTime(); + + QTMovieWin(QTMovieWinClient*); + ~QTMovieWin(); + + void load(const UChar* url, int len); + long loadState() const; + float maxTimeLoaded() const; + + void play(); + void pause(); + + float rate() const; + void setRate(float); + + float duration() const; + float currentTime() const; + void setCurrentTime(float) const; + + void setVolume(float); + + unsigned dataSize() const; + + void getNaturalSize(int& width, int& height); + void setSize(int width, int height); + + void setVisible(bool); + void paint(HDC, int x, int y); + + void disableUnsupportedTracks(unsigned& enabledTrackCount); + + static unsigned countSupportedTypes(); + static void getSupportedType(unsigned index, const UChar*& str, unsigned& len); + +private: + QTMovieWinPrivate* m_private; + friend class QTMovieWinPrivate; +}; + +#endif diff --git a/WebCore/platform/graphics/win/QTMovieWinTimer.cpp b/WebCore/platform/graphics/win/QTMovieWinTimer.cpp new file mode 100644 index 0000000..d0aa3e6 --- /dev/null +++ b/WebCore/platform/graphics/win/QTMovieWinTimer.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, 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 <windows.h> + +#include "QTMovieWinTimer.h" + +#include <wtf/Assertions.h> + +// This file is used by the QuickTime access DLL. It copies some WebCore code +// which can't be used directly due to dependency issues. + +// FIXME: Find a way to do timers that can manage <10ms resolution properly too. + +static UINT_PTR timerID; +static void (*sharedTimerFiredFunction)(); + +static HINSTANCE instanceHandle = 0; + +static HWND timerWindowHandle = 0; +static UINT timerFiredMessage = 0; +static const LPCWSTR kTimerWindowClassName = L"TimerWindowClass"; +static bool processingCustomTimerMessage = false; + +static LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == timerFiredMessage) { + processingCustomTimerMessage = true; + sharedTimerFiredFunction(); + processingCustomTimerMessage = false; + } else + return DefWindowProc(hWnd, message, wParam, lParam); + return 0; +} + +static void initializeOffScreenTimerWindow() +{ + if (timerWindowHandle) + return; + + WNDCLASSEX wcex; + memset(&wcex, 0, sizeof(WNDCLASSEX)); + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.lpfnWndProc = TimerWindowWndProc; + wcex.hInstance = instanceHandle; + wcex.lpszClassName = kTimerWindowClassName; + RegisterClassEx(&wcex); + + timerWindowHandle = CreateWindow(kTimerWindowClassName, 0, 0, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, 0, instanceHandle, 0); + timerFiredMessage = RegisterWindowMessage(L"com.apple.WebKit.TimerFired"); +} + +void setSharedTimerFiredFunction(void (*f)()) +{ + sharedTimerFiredFunction = f; +} + +static void CALLBACK timerFired(HWND, UINT, UINT_PTR, DWORD) +{ + sharedTimerFiredFunction(); +} + +void setSharedTimerFireDelay(double interval) +{ + ASSERT(sharedTimerFiredFunction); + + unsigned intervalInMS; + if (interval < 0) + intervalInMS = 0; + else { + interval *= 1000; + if (interval > USER_TIMER_MAXIMUM) + intervalInMS = USER_TIMER_MAXIMUM; + else + intervalInMS = (unsigned)interval; + } + + if (timerID) { + KillTimer(0, timerID); + timerID = 0; + } + + // We don't allow nested PostMessages, since the custom messages will effectively starve + // painting and user input. (Win32 has a tri-level queue with application messages > + // user input > WM_PAINT/WM_TIMER.) + // In addition, if the queue contains input events that have been there since the last call to + // GetQueueStatus, PeekMessage or GetMessage we favor timers. + if (intervalInMS < USER_TIMER_MINIMUM && processingCustomTimerMessage && + !LOWORD(::GetQueueStatus(QS_ALLINPUT))) { + // Windows SetTimer does not allow timeouts smaller than 10ms (USER_TIMER_MINIMUM) + initializeOffScreenTimerWindow(); + PostMessage(timerWindowHandle, timerFiredMessage, 0, 0); + } else + timerID = SetTimer(0, 0, intervalInMS, timerFired); +} + +void stopSharedTimer() +{ + if (timerID) { + KillTimer(0, timerID); + timerID = 0; + } +} + +void setSharedTimerInstanceHandle(HINSTANCE handle) +{ + instanceHandle = handle; +} + +double systemTime() +{ + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + ULARGE_INTEGER t; + memcpy(&t, &ft, sizeof(t)); + return t.QuadPart * 0.0000001 - 11644473600.0; +} diff --git a/WebCore/platform/graphics/win/QTMovieWinTimer.h b/WebCore/platform/graphics/win/QTMovieWinTimer.h new file mode 100644 index 0000000..3e3c2bc --- /dev/null +++ b/WebCore/platform/graphics/win/QTMovieWinTimer.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, 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. + */ + +#ifndef QTMovieViewTimer_h +#define QTMovieViewTimer_h + +// This header should not be included from WebCore. +// It is used by the QuickTime access DLL. It copies some WebCore code +// which can't be used directly due to dependency issues. + +void setSharedTimerFiredFunction(void (*)()); +void setSharedTimerFireDelay(double delay); +void stopSharedTimer(); +void setSharedTimerInstanceHandle(HINSTANCE handle); +double systemTime(); + +#endif diff --git a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp new file mode 100644 index 0000000..8b5ab87 --- /dev/null +++ b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp @@ -0,0 +1,141 @@ +/* + * 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 "SimpleFontData.h" + +#include <winsock2.h> +#include "Font.h" +#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> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <mlang.h> +#include <tchar.h> + +namespace WebCore { + +using std::max; + +static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return unitsPerEm ? x / static_cast<float>(unitsPerEm) : x; } + +void SimpleFontData::platformInit() +{ + m_syntheticBoldOffset = m_font.syntheticBold() ? 1.0f : 0.f; + m_scriptCache = 0; + m_scriptFontProperties = 0; + m_isSystemFont = false; + + if (m_font.useGDI()) + return initGDIFont(); + + CGFontRef font = m_font.cgFont(); + int iAscent = CGFontGetAscent(font); + int iDescent = CGFontGetDescent(font); + int iLineGap = CGFontGetLeading(font); + m_unitsPerEm = CGFontGetUnitsPerEm(font); + float pointSize = m_font.size(); + float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize; + float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize; + float fLineGap = scaleEmToUnits(iLineGap, m_unitsPerEm) * pointSize; + + if (!isCustomFont()) { + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, m_font.hfont()); + int faceLength = GetTextFace(dc, 0, 0); + Vector<TCHAR> faceName(faceLength); + GetTextFace(dc, faceLength, faceName.data()); + m_isSystemFont = !_tcscmp(faceName.data(), _T("Lucida Grande")); + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + if (shouldApplyMacAscentHack()) { + // This code comes from FontDataMac.mm. We only ever do this when running regression tests so that our metrics will match Mac. + + // We need to adjust Times, Helvetica, and Courier to closely match the + // vertical metrics of their Microsoft counterparts that are the de facto + // web standard. The AppKit adjustment of 20% is too big and is + // incorrectly added to line spacing, so we use a 15% adjustment instead + // and add it to the ascent. + if (!_tcscmp(faceName.data(), _T("Times")) || !_tcscmp(faceName.data(), _T("Helvetica")) || !_tcscmp(faceName.data(), _T("Courier"))) + fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f); + } + } + + m_ascent = lroundf(fAscent); + m_descent = lroundf(fDescent); + m_lineGap = lroundf(fLineGap); + m_lineSpacing = m_ascent + m_descent + m_lineGap; + + // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font. + // Unfortunately, NSFont will round this for us so we don't quite get the right value. + GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); + Glyph xGlyph = glyphPageZero ? glyphPageZero->glyphDataForCharacter('x').glyph : 0; + if (xGlyph) { + CGRect xBox; + CGFontGetGlyphBBoxes(font, &xGlyph, 1, &xBox); + // Use the maximum of either width or height because "x" is nearly square + // and web pages that foolishly use this metric for width will be laid out + // poorly if we return an accurate height. Classic case is Times 13 point, + // which has an "x" that is 7x6 pixels. + m_xHeight = scaleEmToUnits(max(CGRectGetMaxX(xBox), CGRectGetMaxY(xBox)), m_unitsPerEm) * pointSize; + } else { + int iXHeight = CGFontGetXHeight(font); + m_xHeight = scaleEmToUnits(iXHeight, m_unitsPerEm) * pointSize; + } +} + +void SimpleFontData::platformDestroy() +{ + platformCommonDestroy(); +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + if (m_font.useGDI()) + return widthForGDIGlyph(glyph); + + CGFontRef font = m_font.cgFont(); + float pointSize = m_font.size(); + CGSize advance; + CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize); + + // FIXME: Need to add real support for printer fonts. + bool isPrinterFont = false; + wkGetGlyphAdvances(font, m, m_isSystemFont, isPrinterFont, glyph, advance); + + return advance.width + m_syntheticBoldOffset; +} + +} diff --git a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp new file mode 100644 index 0000000..07d5305 --- /dev/null +++ b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp @@ -0,0 +1,131 @@ +/* + * 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. + * 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 "SimpleFontData.h" + +#include <windows.h> + +#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> + +namespace WebCore { + +void SimpleFontData::platformInit() +{ + m_scriptCache = 0; + m_scriptFontProperties = 0; + m_isSystemFont = false; + 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 * metricsMultiplier; + } + + cairo_win32_scaled_font_done_font(scaledFont); + + m_isSystemFont = false; + m_scriptCache = 0; + m_scriptFontProperties = 0; + + RestoreDC(hdc, -1); + ReleaseDC(0, hdc); +} + +void SimpleFontData::platformDestroy() +{ + 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()) + 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); + + int width; + GetCharWidthI(hdc, glyph, 1, 0, &width); + + 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 new file mode 100644 index 0000000..0e9f9fb --- /dev/null +++ b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp @@ -0,0 +1,206 @@ +/* + * 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 "SimpleFontData.h" + +#include <winsock2.h> +#include "Font.h" +#include "FontCache.h" +#include "FloatRect.h" +#include "FontDescription.h" +#include <wtf/MathExtras.h> +#include <unicode/uchar.h> +#include <unicode/unorm.h> +#include <ApplicationServices/ApplicationServices.h> +#include <mlang.h> +#include <tchar.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> + +namespace WebCore { + +using std::max; + +const float cSmallCapsFontSizeMultiplier = 0.7f; + +static bool g_shouldApplyMacAscentHack; + +void SimpleFontData::setShouldApplyMacAscentHack(bool b) +{ + g_shouldApplyMacAscentHack = b; +} + +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) { + float smallCapsHeight = cSmallCapsFontSizeMultiplier * m_font.size(); + if (isCustomFont()) { + FontPlatformData smallCapsFontData(m_font); + smallCapsFontData.setSize(smallCapsHeight); + m_smallCapsFontData = new SimpleFontData(smallCapsFontData, true, false); + } else { + LOGFONT winfont; + 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, m_font.syntheticBold(), m_font.syntheticOblique(), m_font.useGDI())); + } + } + return m_smallCapsFontData; +} + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + // FIXME: Support custom fonts. + if (isCustomFont()) + return false; + + // FIXME: Microsoft documentation seems to imply that characters can be output using a given font and DC + // merely by testing code page intersection. This seems suspect though. Can't a font only partially + // cover a given code page? + IMLangFontLink2* langFontLink = FontCache::getFontLinkInterface(); + if (!langFontLink) + return false; + + HDC dc = GetDC(0); + + DWORD acpCodePages; + langFontLink->CodePageToCodePages(CP_ACP, &acpCodePages); + + DWORD fontCodePages; + langFontLink->GetFontCodePages(dc, m_font.hfont(), &fontCodePages); + + DWORD actualCodePages; + long numCharactersProcessed; + long offset = 0; + while (offset < length) { + langFontLink->GetStrCodePages(characters, length, acpCodePages, &actualCodePages, &numCharactersProcessed); + if ((actualCodePages & fontCodePages) == 0) + return false; + offset += numCharactersProcessed; + } + + ReleaseDC(0, dc); + + return true; +} + +void SimpleFontData::determinePitch() +{ + if (isCustomFont()) { + m_treatAsFixedPitch = false; + return; + } + + // TEXTMETRICS have this. Set m_treatAsFixedPitch based off that. + HDC dc = GetDC(0); + SaveDC(dc); + SelectObject(dc, m_font.hfont()); + + // Yes, this looks backwards, but the fixed pitch bit is actually set if the font + // is *not* fixed pitch. Unbelievable but true. + TEXTMETRIC tm; + GetTextMetrics(dc, &tm); + m_treatAsFixedPitch = ((tm.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0); + + RestoreDC(dc, -1); + 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) { + m_scriptFontProperties = new SCRIPT_FONTPROPERTIES; + memset(m_scriptFontProperties, 0, sizeof(SCRIPT_FONTPROPERTIES)); + m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES); + HRESULT result = ScriptGetFontProperties(0, scriptCache(), m_scriptFontProperties); + if (result == E_PENDING) { + HDC dc = GetDC(0); + SaveDC(dc); + SelectObject(dc, m_font.hfont()); + ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties); + RestoreDC(dc, -1); + ReleaseDC(0, dc); + } + } + return m_scriptFontProperties; +} + +} diff --git a/WebCore/platform/graphics/win/UniscribeController.cpp b/WebCore/platform/graphics/win/UniscribeController.cpp new file mode 100644 index 0000000..371bc51 --- /dev/null +++ b/WebCore/platform/graphics/win/UniscribeController.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2007 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 "UniscribeController.h" +#include "Font.h" +#include "SimpleFontData.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +// FIXME: Rearchitect this to be more like WidthIterator in Font.cpp. Have an advance() method +// that does stuff in that method instead of doing everything in the constructor. Have advance() +// take the GlyphBuffer as an arg so that we don't have to populate the glyph buffer when +// measuring. +UniscribeController::UniscribeController(const Font* font, const TextRun& run) +: m_font(*font) +, m_run(run) +, m_end(run.length()) +, m_currentCharacter(0) +, m_runWidthSoFar(0) +, m_computingOffsetPosition(false) +, m_includePartialGlyphs(false) +, m_offsetX(0) +, m_offsetPosition(0) +{ + m_padding = m_run.padding(); + if (!m_padding) + m_padPerSpace = 0; + else { + float numSpaces = 0; + for (int s = 0; s < m_run.length(); s++) + if (Font::treatAsSpace(m_run[s])) + numSpaces++; + + if (numSpaces == 0) + m_padPerSpace = 0; + else + m_padPerSpace = ceilf(m_run.padding() / numSpaces); + } + + // Null out our uniscribe structs + resetControlAndState(); +} + +int UniscribeController::offsetForPosition(int x, bool includePartialGlyphs) +{ + m_computingOffsetPosition = true; + m_includePartialGlyphs = includePartialGlyphs; + m_offsetX = x; + m_offsetPosition = 0; + advance(m_run.length()); + if (m_computingOffsetPosition) { + // The point is to the left or to the right of the entire run. + if (m_offsetX >= m_runWidthSoFar && m_run.ltr() || m_offsetX < 0 && m_run.rtl()) + m_offsetPosition = m_end; + } + m_computingOffsetPosition = false; + return m_offsetPosition; +} + +void UniscribeController::advance(unsigned offset, GlyphBuffer* glyphBuffer) +{ + // FIXME: We really want to be using a newer version of Uniscribe that supports the new OpenType + // functions. Those functions would allow us to turn off kerning and ligatures. Without being able + // to do that, we will have buggy line breaking and metrics when simple and complex text are close + // together (the complex code path will narrow the text because of kerning and ligatures and then + // when bidi processing splits into multiple runs, the simple portions will get wider and cause us to + // spill off the edge of a line). + if (static_cast<int>(offset) > m_end) + offset = m_end; + + // Itemize the string. + const UChar* cp = m_run.data(m_currentCharacter); + int length = offset - m_currentCharacter; + if (length <= 0) + return; + + unsigned baseCharacter = m_currentCharacter; + + // We break up itemization of the string by fontData and (if needed) the use of small caps. + + // FIXME: It's inconsistent that we use logical order when itemizing, since this + // does not match normal RTL. + + // FIXME: This function should decode surrogate pairs. Currently it makes little difference that + // it does not because the font cache on Windows does not support non-BMP characters. + Vector<UChar, 256> smallCapsBuffer; + if (m_font.isSmallCaps()) + smallCapsBuffer.resize(length); + + unsigned indexOfFontTransition = m_run.rtl() ? length - 1 : 0; + const UChar* curr = m_run.rtl() ? cp + length - 1 : cp; + const UChar* end = m_run.rtl() ? cp - 1 : cp + length; + + const SimpleFontData* fontData; + const SimpleFontData* nextFontData = m_font.glyphDataForCharacter(*curr, false).fontData; + + UChar newC = 0; + + bool isSmallCaps; + bool nextIsSmallCaps = m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr; + + if (nextIsSmallCaps) + smallCapsBuffer[curr - cp] = newC; + + while (true) { + curr = m_run.rtl() ? curr - 1 : curr + 1; + if (curr == end) + break; + + fontData = nextFontData; + isSmallCaps = nextIsSmallCaps; + int index = curr - cp; + UChar c = *curr; + + bool forceSmallCaps = isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK); + nextFontData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps).fontData; + if (m_font.isSmallCaps()) { + nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c; + if (nextIsSmallCaps) + smallCapsBuffer[index] = forceSmallCaps ? c : newC; + } + + if (nextFontData != fontData || nextIsSmallCaps != isSmallCaps) { + int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition; + int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition; + m_currentCharacter = baseCharacter + itemStart; + itemizeShapeAndPlace((isSmallCaps ? smallCapsBuffer.data() : cp) + itemStart, itemLength, fontData, glyphBuffer); + indexOfFontTransition = index; + } + } + + int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : length - indexOfFontTransition; + if (itemLength) { + int itemStart = m_run.rtl() ? 0 : indexOfFontTransition; + m_currentCharacter = baseCharacter + itemStart; + itemizeShapeAndPlace((nextIsSmallCaps ? smallCapsBuffer.data() : cp) + itemStart, itemLength, nextFontData, glyphBuffer); + } + + m_currentCharacter = baseCharacter + length; +} + +void UniscribeController::itemizeShapeAndPlace(const UChar* cp, unsigned length, const SimpleFontData* fontData, GlyphBuffer* glyphBuffer) +{ + // ScriptItemize (in Windows XP versions prior to SP2) can overflow by 1. This is why there is an extra empty item + // hanging out at the end of the array + m_items.resize(6); + int numItems = 0; + while (ScriptItemize(cp, length, m_items.size() - 1, &m_control, &m_state, m_items.data(), &numItems) == E_OUTOFMEMORY) { + m_items.resize(m_items.size() * 2); + resetControlAndState(); + } + m_items.resize(numItems + 1); + + if (m_run.rtl()) { + for (int i = m_items.size() - 2; i >= 0; i--) { + if (!shapeAndPlaceItem(cp, i, fontData, glyphBuffer)) + return; + } + } else { + for (unsigned i = 0; i < m_items.size() - 1; i++) { + if (!shapeAndPlaceItem(cp, i, fontData, glyphBuffer)) + return; + } + } +} + +void UniscribeController::resetControlAndState() +{ + memset(&m_control, 0, sizeof(SCRIPT_CONTROL)); + memset(&m_state, 0, sizeof(SCRIPT_STATE)); + + // Set up the correct direction for the run. + m_state.uBidiLevel = m_run.rtl(); + + // Lock the correct directional override. + m_state.fOverrideDirection = m_run.directionalOverride(); +} + +bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const SimpleFontData* fontData, GlyphBuffer* glyphBuffer) +{ + // Determine the string for this item. + const UChar* str = cp + m_items[i].iCharPos; + int len = m_items[i+1].iCharPos - m_items[i].iCharPos; + SCRIPT_ITEM item = m_items[i]; + + // Set up buffers to hold the results of shaping the item. + Vector<WORD> glyphs; + Vector<WORD> clusters; + Vector<SCRIPT_VISATTR> visualAttributes; + clusters.resize(len); + + // Shape the item. + // The recommended size for the glyph buffer is 1.5 * the character length + 16 in the uniscribe docs. + // Apparently this is a good size to avoid having to make repeated calls to ScriptShape. + glyphs.resize(1.5 * len + 16); + visualAttributes.resize(glyphs.size()); + + if (!shape(str, len, item, fontData, glyphs, clusters, visualAttributes)) + return true; + + // We now have a collection of glyphs. + Vector<GOFFSET> offsets; + Vector<int> advances; + offsets.resize(glyphs.size()); + advances.resize(glyphs.size()); + int glyphCount = 0; + HRESULT placeResult = ScriptPlace(0, fontData->scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(), + &item.a, advances.data(), offsets.data(), 0); + if (placeResult == E_PENDING) { + // The script cache isn't primed with enough info yet. We need to select our HFONT into + // a DC and pass the DC in to ScriptPlace. + HDC hdc = GetDC(0); + HFONT hfont = fontData->platformData().hfont(); + HFONT oldFont = (HFONT)SelectObject(hdc, hfont); + placeResult = ScriptPlace(hdc, fontData->scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(), + &item.a, advances.data(), offsets.data(), 0); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + } + + if (FAILED(placeResult) || glyphs.isEmpty()) + return true; + + // Convert all chars that should be treated as spaces to use the space glyph. + // We also create a map that allows us to quickly go from space glyphs or rounding + // hack glyphs back to their corresponding characters. + Vector<int> spaceCharacters(glyphs.size()); + spaceCharacters.fill(-1); + Vector<int> roundingHackCharacters(glyphs.size()); + roundingHackCharacters.fill(-1); + Vector<int> roundingHackWordBoundaries(glyphs.size()); + roundingHackWordBoundaries.fill(-1); + + const float cLogicalScale = fontData->m_font.useGDI() ? 1.0f : 32.0f; + unsigned logicalSpaceWidth = fontData->m_spaceWidth * cLogicalScale; + float roundedSpaceWidth = roundf(fontData->m_spaceWidth); + + for (int k = 0; k < len; k++) { + UChar ch = *(str + k); + if (Font::treatAsSpace(ch)) { + // Substitute in the space glyph at the appropriate place in the glyphs + // array. + glyphs[clusters[k]] = fontData->m_spaceGlyph; + advances[clusters[k]] = logicalSpaceWidth; + spaceCharacters[clusters[k]] = m_currentCharacter + k + item.iCharPos; + } + + if (Font::isRoundingHackCharacter(ch)) + roundingHackCharacters[clusters[k]] = m_currentCharacter + k + item.iCharPos; + + int boundary = k + m_currentCharacter + item.iCharPos; + if (boundary < m_run.length() && + Font::isRoundingHackCharacter(*(str + k + 1))) + roundingHackWordBoundaries[clusters[k]] = boundary; + } + + // Populate our glyph buffer with this information. + bool hasExtraSpacing = m_font.letterSpacing() || m_font.wordSpacing() || m_padding; + + float leftEdge = m_runWidthSoFar; + + for (unsigned k = 0; k < glyphs.size(); k++) { + Glyph glyph = glyphs[k]; + float advance = advances[k] / cLogicalScale; + float offsetX = offsets[k].du / cLogicalScale; + float offsetY = offsets[k].dv / cLogicalScale; + + // Match AppKit's rules for the integer vs. non-integer rendering modes. + float roundedAdvance = roundf(advance); + if (!m_font.isPrinterFont() && !fontData->isSystemFont()) { + advance = roundedAdvance; + 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 + // match the width of the space glyph have the same width as the space glyph. + if (roundedAdvance == roundedSpaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && + m_run.applyWordRounding()) + advance = fontData->m_adjustedSpaceWidth; + + if (hasExtraSpacing) { + // If we're a glyph with an advance, go ahead and add in letter-spacing. + // That way we weed out zero width lurkers. This behavior matches the fast text code path. + if (advance && m_font.letterSpacing()) + advance += m_font.letterSpacing(); + + // Handle justification and word-spacing. + if (glyph == fontData->m_spaceGlyph) { + // Account for padding. WebCore uses space padding to justify text. + // We distribute the specified padding over the available spaces in the run. + if (m_padding) { + // Use leftover padding if not evenly divisible by number of spaces. + if (m_padding < m_padPerSpace) { + advance += m_padding; + m_padding = 0; + } else { + advance += m_padPerSpace; + m_padding -= m_padPerSpace; + } + } + + // Account for word-spacing. + int characterIndex = spaceCharacters[k]; + if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) + advance += m_font.wordSpacing(); + } + } + + // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters + // followed by a character defined by isRoundingHackCharacter()) are always an integer width. + // We adjust the width of the last character of a "word" to ensure an integer width. + // Force characters that are used to determine word boundaries for the rounding hack + // to be integer width, so the following words will start on an integer boundary. + int roundingHackIndex = roundingHackCharacters[k]; + if (m_run.applyWordRounding() && roundingHackIndex != -1) + advance = ceilf(advance); + + // Check to see if the next character is a "rounding hack character", if so, adjust the + // width so that the total run width will be on an integer boundary. + int position = m_currentCharacter + len; + bool lastGlyph = (k == glyphs.size() - 1) && (m_run.rtl() ? i == 0 : i == m_items.size() - 2) && (position >= m_end); + if ((m_run.applyWordRounding() && roundingHackWordBoundaries[k] != -1) || + (m_run.applyRunRounding() && lastGlyph)) { + float totalWidth = m_runWidthSoFar + advance; + advance += ceilf(totalWidth) - totalWidth; + } + + m_runWidthSoFar += advance; + + // FIXME: We need to take the GOFFSETS for combining glyphs and store them in the glyph buffer + // as well, so that when the time comes to draw those glyphs, we can apply the appropriate + // translation. + if (glyphBuffer) { + FloatSize size(offsetX, -offsetY); + glyphBuffer->add(glyph, fontData, advance, &size); + } + + // Mutate the glyph array to contain our altered advances. + if (m_computingOffsetPosition) + advances[k] = advance; + } + + while (m_computingOffsetPosition && m_offsetX >= leftEdge && m_offsetX < m_runWidthSoFar) { + // The position is somewhere inside this run. + int trailing = 0; + ScriptXtoCP(m_offsetX - leftEdge, clusters.size(), glyphs.size(), clusters.data(), visualAttributes.data(), + advances.data(), &item.a, &m_offsetPosition, &trailing); + if (trailing && m_includePartialGlyphs && m_offsetPosition < len - 1) { + m_offsetPosition += m_currentCharacter + m_items[i].iCharPos; + m_offsetX += m_run.rtl() ? -trailing : trailing; + } else { + m_computingOffsetPosition = false; + m_offsetPosition += m_currentCharacter + m_items[i].iCharPos; + if (trailing && m_includePartialGlyphs) + m_offsetPosition++; + return false; + } + } + + return true; +} + +bool UniscribeController::shape(const UChar* str, int len, SCRIPT_ITEM item, const SimpleFontData* fontData, + Vector<WORD>& glyphs, Vector<WORD>& clusters, + Vector<SCRIPT_VISATTR>& visualAttributes) +{ + HDC hdc = 0; + HFONT oldFont = 0; + HRESULT shapeResult = E_PENDING; + int glyphCount = 0; + do { + shapeResult = ScriptShape(hdc, fontData->scriptCache(), str, len, glyphs.size(), &item.a, + glyphs.data(), clusters.data(), visualAttributes.data(), &glyphCount); + if (shapeResult == E_PENDING) { + // The script cache isn't primed with enough info yet. We need to select our HFONT into + // a DC and pass the DC in to ScriptShape. + ASSERT(!hdc); + hdc = GetDC(0); + HFONT hfont = fontData->platformData().hfont(); + oldFont = (HFONT)SelectObject(hdc, hfont); + } else if (shapeResult == E_OUTOFMEMORY) { + // Need to resize our buffers. + glyphs.resize(glyphs.size() * 2); + visualAttributes.resize(glyphs.size()); + } + } while (shapeResult == E_PENDING || shapeResult == E_OUTOFMEMORY); + + if (hdc) { + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + } + + if (FAILED(shapeResult)) + return false; + + glyphs.shrink(glyphCount); + visualAttributes.shrink(glyphCount); + + return true; +} + +} diff --git a/WebCore/platform/graphics/win/UniscribeController.h b/WebCore/platform/graphics/win/UniscribeController.h new file mode 100644 index 0000000..6ea45e1 --- /dev/null +++ b/WebCore/platform/graphics/win/UniscribeController.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef UniscribeController_h +#define UniscribeController_h + +#include <usp10.h> +#include "Font.h" +#include "GlyphBuffer.h" +#include "Vector.h" + +namespace WebCore { + +class UniscribeController { +public: + UniscribeController(const Font*, const TextRun&); + + // Advance and measure/place up to the specified character. + void advance(unsigned to, GlyphBuffer* = 0); + + // Compute the character offset for a given x coordinate. + int offsetForPosition(int x, bool includePartialGlyphs); + + // Returns the width of everything we've consumed so far. + float runWidthSoFar() const { return m_runWidthSoFar; } + +private: + void resetControlAndState(); + + void itemizeShapeAndPlace(const UChar*, unsigned length, const SimpleFontData*, GlyphBuffer*); + bool shapeAndPlaceItem(const UChar*, unsigned index, const SimpleFontData*, GlyphBuffer*); + bool shape(const UChar* str, int len, SCRIPT_ITEM item, const SimpleFontData* fontData, + Vector<WORD>& glyphs, Vector<WORD>& clusters, + Vector<SCRIPT_VISATTR>& visualAttributes); + + const Font& m_font; + const TextRun& m_run; + + SCRIPT_CONTROL m_control; + SCRIPT_STATE m_state; + Vector<SCRIPT_ITEM> m_items; + + unsigned m_currentCharacter; + int m_end; + + float m_runWidthSoFar; + float m_padding; + float m_padPerSpace; + + bool m_computingOffsetPosition; + bool m_includePartialGlyphs; + float m_offsetX; + int m_offsetPosition; +}; + +} +#endif |