diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/graphics/win | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/graphics/win')
73 files changed, 14247 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/win/FontCGWin.cpp b/Source/WebCore/platform/graphics/win/FontCGWin.cpp new file mode 100644 index 0000000..8012722 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontCGWin.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 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; + TextDrawingModeFlags drawingMode = graphicsContext->textDrawingMode(); + if (drawingMode == TextModeFill) { + if (!fillColor.alpha()) + return; + + drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer(); + if (!drawIntoBitmap) { + FloatSize offset; + float blur; + Color color; + ColorSpace shadowColorSpace; + + graphicsContext->getShadow(offset, blur, color, shadowColorSpace); + drawIntoBitmap = offset.width() || offset.height() || 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->platformData().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 == TextModeFill) { + 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->syntheticBoldOffset()) { + xform.eM21 = 0; + xform.eDx = font->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 { + 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; + 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())); + + for (unsigned i = 0; i < numGlyphs; ++i) { + RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); + CGContextSaveGState(cgContext); + CGContextConcatCTM(cgContext, initialGlyphTransform); + + if (drawingMode & TextModeFill) { + CGContextAddPath(cgContext, glyphPath.get()); + CGContextFillPath(cgContext); + if (font->syntheticBoldOffset()) { + CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); + CGContextAddPath(cgContext, glyphPath.get()); + CGContextFillPath(cgContext); + CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); + } + } + if (drawingMode & TextModeStroke) { + CGContextAddPath(cgContext, glyphPath.get()); + CGContextStrokePath(cgContext); + if (font->syntheticBoldOffset()) { + CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); + CGContextAddPath(cgContext, glyphPath.get()); + CGContextStrokePath(cgContext); + CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); + } + } + + CGContextRestoreGState(cgContext); + CGContextTranslateCTM(cgContext, gdiAdvances[i], 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 +{ + CGContextRef cgContext = graphicsContext->platformContext(); + bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); + + switch(fontDescription().fontSmoothing()) { + case Antialiased: { + graphicsContext->setShouldAntialias(true); + shouldUseFontSmoothing = false; + break; + } + case SubpixelAntialiased: { + graphicsContext->setShouldAntialias(true); + shouldUseFontSmoothing = true; + break; + } + case NoSmoothing: { + graphicsContext->setShouldAntialias(false); + shouldUseFontSmoothing = false; + break; + } + case AutoSmoothing: { + // For the AutoSmooth case, don't do anything! Keep the default settings. + break; + } + default: + ASSERT_NOT_REACHED(); + } + + if (font->platformData().useGDI() && !shouldUseFontSmoothing) { + drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); + return; + } + + uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); + + 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, font->platformData().useGDI()); + + FloatSize shadowOffset; + float shadowBlur; + Color shadowColor; + ColorSpace shadowColorSpace; + graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); + + bool hasSimpleShadow = graphicsContext->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && (!graphicsContext->shadowsIgnoreTransforms() || graphicsContext->getCTM().isIdentityOrTranslationOrFlipped()); + 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, ColorSpaceDeviceRGB); + float shadowTextX = point.x() + translation.width() + shadowOffset.width(); + // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative. + float shadowTextY = point.y() + translation.height() + shadowOffset.height() * (graphicsContext->shadowsIgnoreTransforms() ? -1 : 1); + CGContextSetTextPosition(cgContext, shadowTextX, shadowTextY); + CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + if (font->syntheticBoldOffset()) { + CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height()); + CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + } + graphicsContext->setFillColor(fillColor, ColorSpaceDeviceRGB); + } + + CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); + CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + if (font->syntheticBoldOffset()) { + CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->syntheticBoldOffset(), point.y() + translation.height()); + CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + } + + if (hasSimpleShadow) + graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB); + + wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); +} + +} diff --git a/Source/WebCore/platform/graphics/win/FontCacheWin.cpp b/Source/WebCore/platform/graphics/win/FontCacheWin.cpp new file mode 100644 index 0000000..e800245 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -0,0 +1,603 @@ +/* + * 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 "UnicodeRange.h" +#include <mlang.h> +#include <windows.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringHash.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; +} + +SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +static SimpleFontData* fontDataFromDescriptionAndLogFont(FontCache* fontCache, const FontDescription& fontDescription, const LOGFONT& font, AtomicString& outFontFamilyName) +{ + AtomicString familyName = String(font.lfFaceName, wcsnlen(font.lfFaceName, LF_FACESIZE)); + SimpleFontData* fontData = fontCache->getCachedFontData(fontDescription, familyName); + if (fontData) + outFontFamilyName = familyName; + return fontData; +} + +SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) +{ + DEFINE_STATIC_LOCAL(AtomicString, fallbackFontName, ()); + if (!fallbackFontName.isEmpty()) + return getCachedFontData(fontDescription, fallbackFontName); + + // 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. + + // Search all typical Windows-installed full Unicode fonts. + // Sorted by most to least glyphs according to http://en.wikipedia.org/wiki/Unicode_typefaces + // Start with Times New Roman also since it is the default if the user doesn't change prefs. + static AtomicString fallbackFonts[] = { + AtomicString("Times New Roman"), + AtomicString("Microsoft Sans Serif"), + AtomicString("Tahoma"), + AtomicString("Lucida Sans Unicode"), + AtomicString("Arial") + }; + SimpleFontData* simpleFont; + for (size_t i = 0; i < WTF_ARRAY_LENGTH(fallbackFonts); ++i) { + if (simpleFont = getCachedFontData(fontDescription, fallbackFonts[i])) { + fallbackFontName = fallbackFonts[i]; + return simpleFont; + } + } + + // Fall back to the DEFAULT_GUI_FONT if no known Unicode fonts are available. + if (HFONT defaultGUIFont = static_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT))) { + LOGFONT defaultGUILogFont; + GetObject(defaultGUIFont, sizeof(defaultGUILogFont), &defaultGUILogFont); + if (simpleFont = fontDataFromDescriptionAndLogFont(this, fontDescription, defaultGUILogFont, fallbackFontName)) + return simpleFont; + } + + // Fall back to Non-client metrics fonts. + NONCLIENTMETRICS nonClientMetrics = {0}; + nonClientMetrics.cbSize = sizeof(nonClientMetrics); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nonClientMetrics), &nonClientMetrics, 0)) { + if (simpleFont = fontDataFromDescriptionAndLogFont(this, fontDescription, nonClientMetrics.lfMessageFont, fallbackFontName)) + return simpleFont; + if (simpleFont = fontDataFromDescriptionAndLogFont(this, fontDescription, nonClientMetrics.lfMenuFont, fallbackFontName)) + return simpleFont; + if (simpleFont = fontDataFromDescriptionAndLogFont(this, fontDescription, nonClientMetrics.lfStatusFont, fallbackFontName)) + return simpleFont; + if (simpleFont = fontDataFromDescriptionAndLogFont(this, fontDescription, nonClientMetrics.lfCaptionFont, fallbackFontName)) + return simpleFont; + if (simpleFont = fontDataFromDescriptionAndLogFont(this, fontDescription, nonClientMetrics.lfSmCaptionFont, fallbackFontName)) + return simpleFont; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +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, bool synthesizeItalic) +{ + 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; + + if (desiredItalic && !matchData.m_chosen.lfItalic && synthesizeItalic) + matchData.m_chosen.lfItalic = 1; + + HFONT result = CreateFontIndirect(&matchData.m_chosen); + if (!result) + return 0; + + HDC dc = GetDC(0); + SaveDC(dc); + SelectObject(dc, result); + WCHAR actualName[LF_FACESIZE]; + GetTextFace(dc, LF_FACESIZE, actualName); + RestoreDC(dc, -1); + ReleaseDC(0, dc); + + if (wcsicmp(matchData.m_chosen.lfFaceName, actualName)) { + DeleteObject(result); + result = 0; + } + + return result; +} + +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), useGDI); + + 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/Source/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/win/FontCustomPlatformData.cpp new file mode 100644 index 0000000..9cae99b --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "WOFFFileFormat.h" +#include <ApplicationServices/ApplicationServices.h> +#include <WebKitSystemInterface/WebKitSystemInterface.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() +{ + 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, FontOrientation, FontRenderingMode renderingMode) +{ + ASSERT(m_fontReference); + ASSERT(T2embedLibrary()); + + LOGFONT& logFont = *static_cast<LOGFONT*>(malloc(sizeof(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); + + RetainPtr<CGFontRef> cgFont(AdoptCF, CGFontCreateWithPlatformFont(&logFont)); + return FontPlatformData(hfont, cgFont.get(), size, bold, italic, renderingMode == AlternateRenderingMode); +} + +// Streams the concatenation of a header and font data. +class EOTStream { +public: + EOTStream(const EOTHeader& 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 EOTHeader& 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()); + + RefPtr<SharedBuffer> sfntBuffer; + if (isWOFF(buffer)) { + Vector<char> sfnt; + if (!convertWOFFToSfnt(buffer, sfnt)) + return 0; + + sfntBuffer = SharedBuffer::adoptVector(sfnt); + buffer = sfntBuffer.get(); + } + + // 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. + EOTHeader eotHeader; + size_t overlayDst; + size_t overlaySrc; + size_t overlayLength; + if (!getEOTHeader(buffer, eotHeader, overlayDst, overlaySrc, overlayLength)) + 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) + return 0; + } + + return new FontCustomPlatformData(fontReference, fontName); +} + +bool FontCustomPlatformData::supportsFormat(const String& format) +{ + return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype") || equalIgnoringCase(format, "woff"); +} + +} diff --git a/Source/WebCore/platform/graphics/win/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/win/FontCustomPlatformData.h new file mode 100644 index 0000000..1bdf270 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontCustomPlatformData.h @@ -0,0 +1,58 @@ +/* + * 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 "FontOrientation.h" +#include "FontRenderingMode.h" +#include "PlatformString.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +typedef struct CGFont* CGFontRef; + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformData : Noncopyable { + FontCustomPlatformData(HANDLE fontReference, const String& name) + : m_fontReference(fontReference) + , m_name(name) + { + } + + ~FontCustomPlatformData(); + + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + + static bool supportsFormat(const String&); + + HANDLE m_fontReference; + String m_name; +}; + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); + +} + +#endif diff --git a/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp b/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp new file mode 100644 index 0000000..c3decbf --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp @@ -0,0 +1,66 @@ +/* + * 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 { + +FontCustomPlatformData::~FontCustomPlatformData() +{ + cairo_font_face_destroy(m_fontFace); +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation) +{ + return FontPlatformData(m_fontFace, size, bold, italic); +} + +static void releaseData(void* data) +{ + static_cast<SharedBuffer*>(data)->deref(); +} + +FontCustomPlatformData* 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 FontCustomPlatformData(fontFace); +} + +bool FontCustomPlatformData::supportsFormat(const String& format) +{ + return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype"); +} + +} diff --git a/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h b/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h new file mode 100644 index 0000000..3ab52b8 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (C) 2010 Brent Fulgham <bfulgham@webkit.org> + * + * 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 "FontDescription.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +#include <cairo.h> + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformData : Noncopyable { + FontCustomPlatformData(cairo_font_face_t* fontFace) + : m_fontFace(fontFace) + { + } + ~FontCustomPlatformData(); + + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal); + + static bool supportsFormat(const String&); + + cairo_font_face_t* m_fontFace; +}; + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); + +} + +#endif diff --git a/Source/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/Source/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp new file mode 100644 index 0000000..9234229 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp @@ -0,0 +1,131 @@ +/* + * 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, 2009 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 <ApplicationServices/ApplicationServices.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.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) +{ + LOGFONT logfont; + GetObject(font, sizeof(logfont), &logfont); + m_cgFont.adoptCF(CGFontCreateWithPlatformFont(&logfont)); +} + +FontPlatformData::FontPlatformData(HFONT hfont, CGFontRef font, float size, bool bold, bool oblique, bool useGDI) + : m_font(RefCountedGDIHandle<HFONT>::create(hfont)) + , m_size(size) + , m_cgFont(font) + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) + , m_useGDI(useGDI) +{ +} + +FontPlatformData::~FontPlatformData() +{ +} + +} diff --git a/Source/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp b/Source/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp new file mode 100644 index 0000000..0f5c365 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp @@ -0,0 +1,143 @@ +/* + * 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, 2010 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 <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +#include <cairo-win32.h> + +using namespace std; + +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); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& source) + : m_font(source.m_font) + , m_size(source.m_size) + , m_fontFace(0) + , m_scaledFont(0) + , m_syntheticBold(source.m_syntheticBold) + , m_syntheticOblique(source.m_syntheticOblique) + , m_useGDI(source.m_useGDI) +{ + if (source.m_fontFace) + m_fontFace = cairo_font_face_reference(source.m_fontFace); + + if (source.m_scaledFont) + m_scaledFont = cairo_scaled_font_reference(source.m_scaledFont); +} + + +FontPlatformData::~FontPlatformData() +{ + cairo_scaled_font_destroy(m_scaledFont); + cairo_font_face_destroy(m_fontFace); +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) +{ + // Check for self-assignment. + if (this == &other) + return *this; + + m_font = other.m_font; + m_size = other.m_size; + m_syntheticBold = other.m_syntheticBold; + m_syntheticOblique = other.m_syntheticOblique; + m_useGDI = other.m_useGDI; + + if (other.m_fontFace) + cairo_font_face_reference(other.m_fontFace); + if (m_fontFace) + cairo_font_face_destroy(m_fontFace); + m_fontFace = other.m_fontFace; + + if (other.m_scaledFont) + cairo_scaled_font_reference(other.m_scaledFont); + if (m_scaledFont) + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = other.m_scaledFont; + + return *this; +} + +bool FontPlatformData::operator==(const FontPlatformData& other) const +{ + return m_font == other.m_font + && m_fontFace == other.m_fontFace + && m_scaledFont == other.m_scaledFont + && m_size == other.m_size + && m_syntheticBold == other.m_syntheticBold + && m_syntheticOblique == other.m_syntheticOblique + && m_useGDI == other.m_useGDI; +} + +} diff --git a/Source/WebCore/platform/graphics/win/FontPlatformDataWin.cpp b/Source/WebCore/platform/graphics/win/FontPlatformDataWin.cpp new file mode 100644 index 0000000..09ed4a6 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontPlatformDataWin.cpp @@ -0,0 +1,95 @@ +/* + * 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 <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +using std::min; + +namespace WebCore { + +FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool oblique, bool useGDI) + : m_font(RefCountedGDIHandle<HFONT>::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) +{ +} + +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + +} diff --git a/Source/WebCore/platform/graphics/win/FontWin.cpp b/Source/WebCore/platform/graphics/win/FontWin.cpp new file mode 100644 index 0000000..2170954 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/FontWin.cpp @@ -0,0 +1,142 @@ +/* + * 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 "Logging.h" +#include "SimpleFontData.h" +#include "UniscribeController.h" +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +bool Font::canReturnFallbackFontsForComplexText() +{ + return true; +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& 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); +} + +float Font::getGlyphsAndAdvancesForComplexText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const +{ + if (forTextEmphasis) { + // FIXME: Add forTextEmphasis paremeter to UniscribeController and use it. + LOG_ERROR("Not implemented for text emphasis."); + return 0; + } + + UniscribeController controller(this, run); + controller.advance(from); + float beforeWidth = controller.runWidthSoFar(); + controller.advance(to, &glyphBuffer); + + if (glyphBuffer.isEmpty()) + return 0; + + float afterWidth = controller.runWidthSoFar(); + + if (run.rtl()) { + controller.advance(run.length()); + return controller.runWidthSoFar() - afterWidth; + } + return beforeWidth; +} + +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() + getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer); + + // We couldn't generate any glyphs for the run. Give up. + if (glyphBuffer.isEmpty()) + return; + + // Draw the glyph buffer now at the starting point returned in startX. + FloatPoint startPoint(startX, point.y()); + drawGlyphBuffer(context, glyphBuffer, startPoint); +} + +void Font::drawEmphasisMarksForComplexText(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const +{ + GlyphBuffer glyphBuffer; + float initialAdvance = getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer, ForTextEmphasis); + + if (glyphBuffer.isEmpty()) + return; + + drawEmphasisMarks(context, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y())); +} + +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const +{ + UniscribeController controller(this, run, fallbackFonts); + controller.advance(run.length()); + if (glyphOverflow) { + glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - ascent()); + glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - descent()); + glyphOverflow->left = max<int>(0, ceilf(-controller.minGlyphBoundingBoxX())); + glyphOverflow->right = max<int>(0, ceilf(controller.maxGlyphBoundingBoxX() - controller.runWidthSoFar())); + } + return controller.runWidthSoFar(); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, bool includePartialGlyphs) const +{ + // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers + // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. + int x = static_cast<int>(xFloat); + + UniscribeController controller(this, run); + return controller.offsetForPosition(x, includePartialGlyphs); +} + +} diff --git a/Source/WebCore/platform/graphics/win/GDIExtras.cpp b/Source/WebCore/platform/graphics/win/GDIExtras.cpp new file mode 100644 index 0000000..4bd95da --- /dev/null +++ b/Source/WebCore/platform/graphics/win/GDIExtras.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * 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 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 "GDIExtras.h" + +#include "SoftLinking.h" + +namespace WebCore { + +#if OS(WINCE) +SOFT_LINK_LIBRARY(coredll) +SOFT_LINK_OPTIONAL(coredll, AlphaBlend, BOOL, APIENTRY, (HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction)) + +AlphaBlendPointerType AlphaBlendPointer() +{ + return AlphaBlendPtr(); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/win/GDIExtras.h b/Source/WebCore/platform/graphics/win/GDIExtras.h new file mode 100644 index 0000000..0166124 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/GDIExtras.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * 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 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 GDIExtras_h +#define GDIExtras_h + +#include <windows.h> + +namespace WebCore { + +typedef BOOL (APIENTRY *AlphaBlendPointerType) (HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction); + +#if OS(WINCE) +AlphaBlendPointerType AlphaBlendPointer(); +#endif + +inline bool hasAlphaBlendSupport() +{ +#if OS(WINCE) + return AlphaBlendPointer(); +#else + return true; +#endif +} + +inline bool alphaBlendIfSupported(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction) +{ +#if OS(WINCE) + AlphaBlendPointerType alphaBlendPointer = AlphaBlendPointer(); + if (!alphaBlendPointer) + return false; + + alphaBlendPointer(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, blendFunction); +#else + AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, blendFunction); +#endif + return true; +} + +} // namespace WebCore + +#endif // GDIExtras_h diff --git a/Source/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp b/Source/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp new file mode 100644 index 0000000..c11fc1b --- /dev/null +++ b/Source/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/Source/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp b/Source/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp new file mode 100644 index 0000000..b679ced --- /dev/null +++ b/Source/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/Source/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp new file mode 100644 index 0000000..d3c6b58 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2010 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 "GraphicsContextCG.h" + +#include "AffineTransform.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)); + BITMAP info; + + GetObject(bitmap, sizeof(info), &info); + + // FIXME: We can get here because we asked for a bitmap that is too big + // when we have a tiled layer and we're compositing. In that case + // bmBitsPixel will be 0. This seems to be benign, so for now we will + // exit gracefully and look at it later: + // https://bugs.webkit.org/show_bug.cgi?id=52041 + // ASSERT(info.bmBitsPixel == 32); + if (info.bmBitsPixel != 32) + return 0; + + CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | (hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst); + CGContextRef context = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, + info.bmWidthBytes, deviceRGBColorSpaceRef(), bitmapInfo); + + // 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_updatingControlTints(false) +{ + platformInit(hdc, hasAlpha); +} + +void GraphicsContext::platformInit(HDC hdc, bool hasAlpha) +{ + m_data = new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc, hasAlpha)); + CGContextRelease(m_data->m_cgContext.get()); + 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(), ColorSpaceDeviceRGB); + setPlatformStrokeColor(strokeColor(), ColorSpaceDeviceRGB); + } +} + +// FIXME: Is it possible to merge getWindowsContext and createWindowsBitmap into a single API +// suitable for all clients? +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) +{ + bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || inTransparencyLayer()); + if (!createdBitmap) { + m_data->restore(); + return; + } + + 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); + + CGContextRef bitmapContext = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, + info.bmWidthBytes, deviceRGBColorSpaceRef(), kCGBitmapByteOrder32Little | + (supportAlphaBlend ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst)); + + CGImageRef image = CGBitmapContextCreateImage(bitmapContext); + CGContextDrawImage(m_data->m_cgContext.get(), dstRect, image); + + // Delete all our junk. + CGImageRelease(image); + CGContextRelease(bitmapContext); + ::DeleteDC(hdc); + ::DeleteObject(bitmap); +} + +void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point) +{ + // FIXME: Creating CFData is non-optimal, but needed to avoid crashing when printing. Ideally we should + // make a custom CGDataProvider that controls the WindowsBitmap lifetime. see <rdar://6394455> + RetainPtr<CFDataRef> imageData(AdoptCF, CFDataCreate(kCFAllocatorDefault, image->buffer(), image->bufferLength())); + RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(imageData.get())); + RetainPtr<CGImageRef> cgImage(AdoptCF, CGImageCreate(image->size().width(), image->size().height(), 8, 32, image->bytesPerRow(), deviceRGBColorSpaceRef(), + kCGBitmapByteOrder32Little | kCGImageAlphaFirst, dataProvider.get(), 0, true, kCGRenderingIntentDefault)); + CGContextDrawImage(m_data->m_cgContext.get(), CGRectMake(point.x(), point.y(), image->size().width(), image->size().height()), cgImage.get()); +} + +void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color) +{ + // FIXME: implement +} + +// FIXME: This is nearly identical to the GraphicsContext::drawFocusRing function in GraphicsContextMac.mm. +// The code could move to GraphicsContextCG.cpp and be shared. +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) +{ + if (paintingDisabled()) + return; + + float radius = (width - 1) / 2.0f; + offset += radius; + CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0; + + CGMutablePathRef focusRingPath = CGPathCreateMutable(); + 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); + + 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::drawLineForTextChecking(const IntPoint& point, int width, TextCheckingLineStyle style) +{ + if (paintingDisabled()) + return; + + if (style != TextCheckingSpellingLineStyle && style != TextCheckingGrammarLineStyle) + 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 = style == TextCheckingGrammarLineStyle ? 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, WTF_ARRAY_LENGTH(edge_dash_lengths)); + CGContextSetAlpha(context, upperOpacity); + CGContextStrokeLineSegments(context, upperPoints, 2); + + // Middle line + CGContextSetLineDash(context, middle_offset, middle_dash_lengths, WTF_ARRAY_LENGTH(middle_dash_lengths)); + CGContextSetAlpha(context, middleOpacity); + CGContextStrokeLineSegments(context, middlePoints, 2); + + // Bottom line + CGContextSetLineDash(context, edge_offset, edge_dash_lengths, WTF_ARRAY_LENGTH(edge_dash_lengths)); + CGContextSetAlpha(context, lowerOpacity); + CGContextStrokeLineSegments(context, lowerPoints, 2); + + CGContextRestoreGState(context); +} + +void GraphicsContextPlatformPrivate::flush() +{ + CGContextFlush(m_cgContext.get()); +} + +} diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp new file mode 100644 index 0000000..b2c702f --- /dev/null +++ b/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -0,0 +1,169 @@ +/* + * 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 "Path.h" + +#include <cairo-win32.h> +#include "GraphicsContextPlatformPrivateCairo.h" + +using namespace std; + +namespace WebCore { + +static cairo_t* createCairoContextWithHDC(HDC hdc, bool hasAlpha) +{ + // Put the HDC In advanced mode so it will honor affine transforms. + SetGraphicsMode(hdc, GM_ADVANCED); + + cairo_surface_t* surface = 0; + + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); + + BITMAP info; + if (!GetObject(bitmap, sizeof(info), &info)) + surface = cairo_win32_surface_create(hdc); + else { + ASSERT(info.bmBitsPixel == 32); + + surface = cairo_image_surface_create_for_data((unsigned char*)info.bmBits, + CAIRO_FORMAT_ARGB32, + info.bmWidth, + info.bmHeight, + info.bmWidthBytes); + } + + cairo_t* context = cairo_create(surface); + cairo_surface_destroy(surface); + + return context; +} + +GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha) + : m_updatingControlTints(false) +{ + platformInit(dc, hasAlpha); +} + +void GraphicsContext::platformInit(HDC dc, bool hasAlpha) +{ + m_data = new GraphicsContextPlatformPrivate; + + if (dc) { + m_data->cr = createCairoContextWithHDC(dc, hasAlpha); + 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(), fillColorSpace()); + setPlatformStrokeColor(strokeColor(), strokeColorSpace()); + } +} + +static void setRGBABitmapAlpha(unsigned char* bytes, size_t length, unsigned char level) +{ + for (size_t i = 0; i < length; i += 4) + bytes[i + 3] = level; +} + +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) +{ + bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || inTransparencyLayer()); + if (!hdc || !createdBitmap) { + m_data->restore(); + return; + } + + if (dstRect.isEmpty()) + return; + + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); + + BITMAP info; + GetObject(bitmap, sizeof(info), &info); + ASSERT(info.bmBitsPixel == 32); + + // If this context does not support alpha blending, then it may have + // been drawn with GDI functions which always set the alpha channel + // to zero. We need to manually set the bitmap to be fully opaque. + unsigned char* bytes = reinterpret_cast<unsigned char*>(info.bmBits); + if (!supportAlphaBlend) + setRGBABitmapAlpha(bytes, info.bmHeight * info.bmWidthBytes, 255); + + // Need to make a cairo_surface_t out of the bitmap's pixel buffer and then draw + // it into our context. + cairo_surface_t* image = cairo_image_surface_create_for_data(bytes, + CAIRO_FORMAT_ARGB32, + info.bmWidth, + info.bmHeight, + info.bmWidthBytes); + + // Scale the target surface to the new image size, and flip it + // so that when we set the srcImage as the surface it will draw + // right-side-up. + cairo_save(m_data->cr); + cairo_translate(m_data->cr, dstRect.x(), dstRect.height() + dstRect.y()); + cairo_scale(m_data->cr, 1.0, -1.0); + cairo_set_source_surface(m_data->cr, image, 0, 0); + + if (m_data->layers.size()) + cairo_paint_with_alpha(m_data->cr, m_data->layers.last()); + else + cairo_paint(m_data->cr); + + // Delete all our junk. + cairo_surface_destroy(image); + ::DeleteDC(hdc); + ::DeleteObject(bitmap); + cairo_restore(m_data->cr); +} + +void GraphicsContextPlatformPrivate::syncContext(PlatformGraphicsContext* cr) +{ + if (!cr) + return; + + cairo_surface_t* surface = cairo_get_target(cr); + m_hdc = cairo_win32_surface_get_dc(surface); + + SetGraphicsMode(m_hdc, GM_ADVANCED); // We need this call for themes to honor world transforms. +} + +void GraphicsContextPlatformPrivate::flush() +{ + cairo_surface_t* surface = cairo_win32_surface_create(m_hdc); + cairo_surface_flush(surface); + cairo_surface_destroy(surface); +} + +} diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp new file mode 100644 index 0000000..f1953e4 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -0,0 +1,200 @@ +/* + * 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 "BitmapInfo.h" +#include "TransformationMatrix.h" +#include "NotImplemented.h" +#include "Path.h" +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +static void fillWithClearColor(HBITMAP bitmap) +{ + BITMAP bmpInfo; + GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); + int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; + memset(bmpInfo.bmBits, 0, bufferSize); +} + +bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } + +void GraphicsContext::setShouldIncludeChildWindows(bool include) +{ + m_data->m_shouldIncludeChildWindows = include; +} + +bool GraphicsContext::shouldIncludeChildWindows() const +{ + return m_data->m_shouldIncludeChildWindows; +} + +GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size) + : m_hdc(0) + , m_size(size) +{ + BitmapInfo bitmapInfo = BitmapInfo::create(m_size); + + 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); +} + +HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) +{ + // FIXME: Should a bitmap be created also when a shadow is set? + if (mayCreateBitmap && (!m_data->m_hdc || inTransparencyLayer())) { + if (dstRect.isEmpty()) + return 0; + + // Create a bitmap DC in which to draw. + BitmapInfo bitmapInfo = BitmapInfo::create(dstRect.size()); + + 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) + fillWithClearColor(bitmap); + + // 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 = TransformationMatrix().translate(-dstRect.x(), -dstRect.y()); + + ::SetWorldTransform(bitmapDC, &xform); + + return bitmapDC; + } + + m_data->flush(); + m_data->save(); + return m_data->m_hdc; +} + +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 = TransformationMatrix().scaleNonUniform(size.width(), size.height()); + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +static const double deg2rad = 0.017453292519943295769; // pi/180 + +void GraphicsContextPlatformPrivate::rotate(float degreesAngle) +{ + XFORM xform = TransformationMatrix().rotate(degreesAngle); + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +void GraphicsContextPlatformPrivate::translate(float x , float y) +{ + if (!m_hdc) + return; + + XFORM xform = TransformationMatrix().translate(x, y); + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) +{ + if (!m_hdc) + return; + + XFORM xform = transform.toTransformationMatrix(); + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +} diff --git a/Source/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp b/Source/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp new file mode 100644 index 0000000..984fd3f --- /dev/null +++ b/Source/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp @@ -0,0 +1,757 @@ +/* + * Copyright (C) 2009 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 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 USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayerCACF.h" + +#include "FloatConversion.h" +#include "FloatRect.h" +#include "Font.h" +#include "FontSelector.h" +#include "Image.h" +#include "PlatformString.h" +#include "SystemTime.h" +#include "WebLayer.h" +#include "WebTiledLayer.h" +#include <wtf/CurrentTime.h> +#include <wtf/StringExtras.h> +#include <wtf/text/CString.h> + +using namespace std; + +namespace WebCore { + +// The threshold width or height above which a tiled layer will be used. This should be +// large enough to avoid tiled layers for most GraphicsLayers, but less than the D3D +// texture size limit on all supported hardware. +static const int cMaxPixelDimension = 2000; + +// The width and height of a single tile in a tiled layer. Should be large enough to +// avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough +// to keep the overall tile cost low. +static const int cTiledLayerTileSize = 512; + +static inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) +{ + toT3D.m11 = narrowPrecisionToFloat(t.m11()); + toT3D.m12 = narrowPrecisionToFloat(t.m12()); + toT3D.m13 = narrowPrecisionToFloat(t.m13()); + toT3D.m14 = narrowPrecisionToFloat(t.m14()); + toT3D.m21 = narrowPrecisionToFloat(t.m21()); + toT3D.m22 = narrowPrecisionToFloat(t.m22()); + toT3D.m23 = narrowPrecisionToFloat(t.m23()); + toT3D.m24 = narrowPrecisionToFloat(t.m24()); + toT3D.m31 = narrowPrecisionToFloat(t.m31()); + toT3D.m32 = narrowPrecisionToFloat(t.m32()); + toT3D.m33 = narrowPrecisionToFloat(t.m33()); + toT3D.m34 = narrowPrecisionToFloat(t.m34()); + toT3D.m41 = narrowPrecisionToFloat(t.m41()); + toT3D.m42 = narrowPrecisionToFloat(t.m42()); + toT3D.m43 = narrowPrecisionToFloat(t.m43()); + toT3D.m44 = narrowPrecisionToFloat(t.m44()); +} + +TransformationMatrix CAToTransform3D(const CATransform3D& fromT3D) +{ + return TransformationMatrix( + fromT3D.m11, + fromT3D.m12, + fromT3D.m13, + fromT3D.m14, + fromT3D.m21, + fromT3D.m22, + fromT3D.m23, + fromT3D.m24, + fromT3D.m31, + fromT3D.m32, + fromT3D.m33, + fromT3D.m34, + fromT3D.m41, + fromT3D.m42, + fromT3D.m43, + fromT3D.m44); +} + +static void setLayerBorderColor(WKCACFLayer* layer, const Color& color) +{ + layer->setBorderColor(cachedCGColor(color, ColorSpaceDeviceRGB)); +} + +static void clearBorderColor(WKCACFLayer* layer) +{ + layer->setBorderColor(0); +} + +static void setLayerBackgroundColor(WKCACFLayer* layer, const Color& color) +{ + layer->setBackgroundColor(cachedCGColor(color, ColorSpaceDeviceRGB)); +} + +static void clearLayerBackgroundColor(WKCACFLayer* layer) +{ + layer->setBackgroundColor(0); +} + +PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) +{ + return new GraphicsLayerCACF(client); +} + +GraphicsLayerCACF::GraphicsLayerCACF(GraphicsLayerClient* client) + : GraphicsLayer(client) + , m_contentsLayerPurpose(NoContentsLayer) + , m_contentsLayerHasBackgroundColor(false) +{ + m_layer = WebLayer::create(WKCACFLayer::Layer, this); + + updateDebugIndicators(); +} + +GraphicsLayerCACF::~GraphicsLayerCACF() +{ + // clean up the WK layer + if (m_layer) + m_layer->removeFromSuperlayer(); + + if (m_contentsLayer) + m_contentsLayer->removeFromSuperlayer(); + + if (m_transformLayer) + m_transformLayer->removeFromSuperlayer(); +} + +void GraphicsLayerCACF::setName(const String& name) +{ + String longName = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + name; + GraphicsLayer::setName(longName); + + m_layer->setName(longName); +} + +bool GraphicsLayerCACF::setChildren(const Vector<GraphicsLayer*>& children) +{ + bool childrenChanged = GraphicsLayer::setChildren(children); + // FIXME: GraphicsLayer::setChildren calls addChild() for each sublayer, which + // will end up calling updateSublayerList() N times. + if (childrenChanged) + updateSublayerList(); + + return childrenChanged; +} + +void GraphicsLayerCACF::addChild(GraphicsLayer* childLayer) +{ + GraphicsLayer::addChild(childLayer); + updateSublayerList(); +} + +void GraphicsLayerCACF::addChildAtIndex(GraphicsLayer* childLayer, int index) +{ + GraphicsLayer::addChildAtIndex(childLayer, index); + updateSublayerList(); +} + +void GraphicsLayerCACF::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildBelow(childLayer, sibling); + updateSublayerList(); +} + +void GraphicsLayerCACF::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer *sibling) +{ + GraphicsLayer::addChildAbove(childLayer, sibling); + updateSublayerList(); +} + +bool GraphicsLayerCACF::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + updateSublayerList(); + return true; + } + return false; +} + +void GraphicsLayerCACF::removeFromParent() +{ + GraphicsLayer::removeFromParent(); + layerForSuperlayer()->removeFromSuperlayer(); +} + +void GraphicsLayerCACF::setPosition(const FloatPoint& point) +{ + GraphicsLayer::setPosition(point); + updateLayerPosition(); +} + +void GraphicsLayerCACF::setAnchorPoint(const FloatPoint3D& point) +{ + if (point == m_anchorPoint) + return; + + GraphicsLayer::setAnchorPoint(point); + updateAnchorPoint(); +} + +void GraphicsLayerCACF::setSize(const FloatSize& size) +{ + if (size == m_size) + return; + + GraphicsLayer::setSize(size); + updateLayerSize(); +} + +void GraphicsLayerCACF::setTransform(const TransformationMatrix& t) +{ + if (t == m_transform) + return; + + GraphicsLayer::setTransform(t); + updateTransform(); +} + +void GraphicsLayerCACF::setChildrenTransform(const TransformationMatrix& t) +{ + if (t == m_childrenTransform) + return; + + GraphicsLayer::setChildrenTransform(t); + updateChildrenTransform(); +} + +void GraphicsLayerCACF::setPreserves3D(bool preserves3D) +{ + if (preserves3D == m_preserves3D) + return; + + GraphicsLayer::setPreserves3D(preserves3D); + updateLayerPreserves3D(); +} + +void GraphicsLayerCACF::setMasksToBounds(bool masksToBounds) +{ + if (masksToBounds == m_masksToBounds) + return; + + GraphicsLayer::setMasksToBounds(masksToBounds); + updateMasksToBounds(); +} + +void GraphicsLayerCACF::setDrawsContent(bool drawsContent) +{ + if (drawsContent == m_drawsContent) + return; + + GraphicsLayer::setDrawsContent(drawsContent); + updateLayerDrawsContent(); +} + +void GraphicsLayerCACF::setBackgroundColor(const Color& color) +{ + if (m_backgroundColorSet && m_backgroundColor == color) + return; + + GraphicsLayer::setBackgroundColor(color); + + m_contentsLayerHasBackgroundColor = true; + updateLayerBackgroundColor(); +} + +void GraphicsLayerCACF::clearBackgroundColor() +{ + if (!m_backgroundColorSet) + return; + + GraphicsLayer::clearBackgroundColor(); + clearLayerBackgroundColor(m_contentsLayer.get()); +} + +void GraphicsLayerCACF::setContentsOpaque(bool opaque) +{ + if (m_contentsOpaque == opaque) + return; + + GraphicsLayer::setContentsOpaque(opaque); + updateContentsOpaque(); +} + +void GraphicsLayerCACF::setBackfaceVisibility(bool visible) +{ + if (m_backfaceVisibility == visible) + return; + + GraphicsLayer::setBackfaceVisibility(visible); + updateBackfaceVisibility(); +} + +void GraphicsLayerCACF::setOpacity(float opacity) +{ + float clampedOpacity = max(min(opacity, 1.0f), 0.0f); + + if (m_opacity == clampedOpacity) + return; + + GraphicsLayer::setOpacity(clampedOpacity); + primaryLayer()->setOpacity(opacity); +} + +void GraphicsLayerCACF::setNeedsDisplay() +{ + if (drawsContent()) + m_layer->setNeedsDisplay(); +} + +void GraphicsLayerCACF::setNeedsDisplayInRect(const FloatRect& rect) +{ + if (drawsContent()) { + CGRect cgRect = rect; + m_layer->setNeedsDisplay(&cgRect); + } +} + +void GraphicsLayerCACF::setContentsRect(const IntRect& rect) +{ + if (rect == m_contentsRect) + return; + + GraphicsLayer::setContentsRect(rect); + updateContentsRect(); +} + +void GraphicsLayerCACF::setContentsToImage(Image* image) +{ + bool childrenChanged = false; + + if (image) { + m_pendingContentsImage = image->nativeImageForCurrentFrame(); + m_contentsLayerPurpose = ContentsLayerForImage; + if (!m_contentsLayer) + childrenChanged = true; + } else { + m_pendingContentsImage = 0; + m_contentsLayerPurpose = NoContentsLayer; + if (m_contentsLayer) + childrenChanged = true; + } + + updateContentsImage(); + + // This has to happen after updateContentsImage + if (childrenChanged) + updateSublayerList(); +} + +void GraphicsLayerCACF::setContentsToMedia(PlatformLayer* mediaLayer) +{ + if (mediaLayer == m_contentsLayer) + return; + + m_contentsLayer = mediaLayer; + m_contentsLayerPurpose = mediaLayer ? ContentsLayerForMedia : NoContentsLayer; + + updateContentsMedia(); + + // This has to happen after updateContentsMedia + updateSublayerList(); +} + +PlatformLayer* GraphicsLayerCACF::hostLayerForSublayers() const +{ + return m_transformLayer ? m_transformLayer.get() : m_layer.get(); +} + +PlatformLayer* GraphicsLayerCACF::layerForSuperlayer() const +{ + return m_transformLayer ? m_transformLayer.get() : m_layer.get(); +} + +PlatformLayer* GraphicsLayerCACF::platformLayer() const +{ + return primaryLayer(); +} + +void GraphicsLayerCACF::setDebugBackgroundColor(const Color& color) +{ + if (color.isValid()) + setLayerBackgroundColor(m_layer.get(), color); + else + clearLayerBackgroundColor(m_layer.get()); +} + +void GraphicsLayerCACF::setDebugBorder(const Color& color, float borderWidth) +{ + if (color.isValid()) { + setLayerBorderColor(m_layer.get(), color); + m_layer->setBorderWidth(borderWidth); + } else { + clearBorderColor(m_layer.get()); + m_layer->setBorderWidth(0); + } +} + +bool GraphicsLayerCACF::requiresTiledLayer(const FloatSize& size) const +{ + if (!m_drawsContent) + return false; + + // FIXME: catch zero-size height or width here (or earlier)? + return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; +} + +void GraphicsLayerCACF::swapFromOrToTiledLayer(bool useTiledLayer) +{ + if (useTiledLayer == m_usingTiledLayer) + return; + + CGSize tileSize = CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize); + + RefPtr<WKCACFLayer> oldLayer = m_layer; + if (useTiledLayer) + m_layer = WebTiledLayer::create(tileSize, this); + else + m_layer = WebLayer::create(WKCACFLayer::Layer, this); + + m_usingTiledLayer = useTiledLayer; + + m_layer->adoptSublayers(oldLayer.get()); + if (oldLayer->superlayer()) + oldLayer->superlayer()->replaceSublayer(oldLayer.get(), m_layer.get()); + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + updateMasksToBounds(); + updateContentsOpaque(); + updateBackfaceVisibility(); + updateLayerBackgroundColor(); + + updateOpacityOnLayer(); + +#ifndef NDEBUG + String name = String::format("CALayer(%p) GraphicsLayer(%p) %s", m_layer.get(), this, m_usingTiledLayer ? "[Tiled Layer] " : "") + m_name; + m_layer->setName(name); +#endif + + // need to tell new layer to draw itself + setNeedsDisplay(); + + updateDebugIndicators(); +} + +GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayerCACF::defaultContentsOrientation() const +{ + return CompositingCoordinatesTopDown; +} + +void GraphicsLayerCACF::updateSublayerList() +{ + Vector<RefPtr<WKCACFLayer> > newSublayers; + + if (m_transformLayer) { + // Add the primary layer first. Even if we have negative z-order children, the primary layer always comes behind. + newSublayers.append(m_layer.get()); + } else if (m_contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + // This does not cause visible rendering issues because currently contents layers are only used + // for replaced elements that don't have children. + newSublayers.append(m_contentsLayer.get()); + } + + const Vector<GraphicsLayer*>& childLayers = children(); + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCACF* curChild = static_cast<GraphicsLayerCACF*>(childLayers[i]); + + WKCACFLayer* childLayer = curChild->layerForSuperlayer(); + newSublayers.append(childLayer); + } + + for (int i = 0; i < newSublayers.size(); ++i) + newSublayers[i]->removeFromSuperlayer(); + + if (m_transformLayer) { + m_transformLayer->setSublayers(newSublayers); + + if (m_contentsLayer) { + // If we have a transform layer, then the contents layer is parented in the + // primary layer (which is itself a child of the transform layer). + m_layer->removeAllSublayers(); + m_layer->addSublayer(m_contentsLayer); + } + } else + m_layer->setSublayers(newSublayers); +} + +void GraphicsLayerCACF::updateLayerPosition() +{ + // Position is offset on the layer by the layer anchor point. + CGPoint posPoint = CGPointMake(m_position.x() + m_anchorPoint.x() * m_size.width(), + m_position.y() + m_anchorPoint.y() * m_size.height()); + + primaryLayer()->setPosition(posPoint); +} + +void GraphicsLayerCACF::updateLayerSize() +{ + CGRect rect = CGRectMake(0, 0, m_size.width(), m_size.height()); + if (m_transformLayer) { + m_transformLayer->setBounds(rect); + // The anchor of the contents layer is always at 0.5, 0.5, so the position is center-relative. + CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + m_layer->setPosition(centerPoint); + } + + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + m_layer->setBounds(rect); + + // Note that we don't resize m_contentsLayer. It's up the caller to do that. + + // if we've changed the bounds, we need to recalculate the position + // of the layer, taking anchor point into account. + updateLayerPosition(); +} + +void GraphicsLayerCACF::updateAnchorPoint() +{ + primaryLayer()->setAnchorPoint(FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())); + primaryLayer()->setAnchorPointZ(m_anchorPoint.z()); + updateLayerPosition(); +} + +void GraphicsLayerCACF::updateTransform() +{ + CATransform3D transform; + copyTransform(transform, m_transform); + primaryLayer()->setTransform(transform); +} + +void GraphicsLayerCACF::updateChildrenTransform() +{ + CATransform3D transform; + copyTransform(transform, m_childrenTransform); + primaryLayer()->setSublayerTransform(transform); +} + +void GraphicsLayerCACF::updateMasksToBounds() +{ + m_layer->setMasksToBounds(m_masksToBounds); + updateDebugIndicators(); +} + +void GraphicsLayerCACF::updateContentsOpaque() +{ + m_layer->setOpaque(m_contentsOpaque); +} + +void GraphicsLayerCACF::updateBackfaceVisibility() +{ + m_layer->setDoubleSided(m_backfaceVisibility); +} + +void GraphicsLayerCACF::updateLayerPreserves3D() +{ + if (m_preserves3D && !m_transformLayer) { + // Create the transform layer. + m_transformLayer = WebLayer::create(WKCACFLayer::TransformLayer, this); + +#ifndef NDEBUG + m_transformLayer->setName(String().format("Transform Layer CATransformLayer(%p) GraphicsLayer(%p)", m_transformLayer.get(), this)); +#endif + // Copy the position from this layer. + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + m_layer->setPosition(point); + + m_layer->setAnchorPoint(CGPointMake(0.5f, 0.5f)); + m_layer->setTransform(CATransform3DIdentity); + + // Set the old layer to opacity of 1. Further down we will set the opacity on the transform layer. + m_layer->setOpacity(1); + + // Move this layer to be a child of the transform layer. + if (m_layer->superlayer()) + m_layer->superlayer()->replaceSublayer(m_layer.get(), m_transformLayer.get()); + m_transformLayer->addSublayer(m_layer.get()); + + updateSublayerList(); + } else if (!m_preserves3D && m_transformLayer) { + // Relace the transformLayer in the parent with this layer. + m_layer->removeFromSuperlayer(); + m_transformLayer->superlayer()->replaceSublayer(m_transformLayer.get(), m_layer.get()); + + // Release the transform layer. + m_transformLayer = 0; + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + updateSublayerList(); + } + + updateOpacityOnLayer(); +} + +void GraphicsLayerCACF::updateLayerDrawsContent() +{ + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + if (m_drawsContent) + m_layer->setNeedsDisplay(); + else + m_layer->setContents(0); + + updateDebugIndicators(); +} + +void GraphicsLayerCACF::updateLayerBackgroundColor() +{ + if (!m_contentsLayer) + return; + + // We never create the contents layer just for background color yet. + if (m_backgroundColorSet) + setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); + else + clearLayerBackgroundColor(m_contentsLayer.get()); +} + +void GraphicsLayerCACF::updateContentsImage() +{ + if (m_pendingContentsImage) { + if (!m_contentsLayer.get()) { + RefPtr<WKCACFLayer> imageLayer = WebLayer::create(WKCACFLayer::Layer, this); +#ifndef NDEBUG + imageLayer->setName("Image Layer"); +#endif + setupContentsLayer(imageLayer.get()); + m_contentsLayer = imageLayer; + // m_contentsLayer will be parented by updateSublayerList + } + + // FIXME: maybe only do trilinear if the image is being scaled down, + // but then what if the layer size changes? + m_contentsLayer->setMinificationFilter(WKCACFLayer::Trilinear); + m_contentsLayer->setContents(m_pendingContentsImage.get()); + m_pendingContentsImage = 0; + + updateContentsRect(); + } else { + // No image. + // m_contentsLayer will be removed via updateSublayerList. + m_contentsLayer = 0; + } +} + +void GraphicsLayerCACF::updateContentsMedia() +{ + // Media layer was set as m_contentsLayer, and will get parented in updateSublayerList(). + if (m_contentsLayer) { + setupContentsLayer(m_contentsLayer.get()); + updateContentsRect(); + } +} + +void GraphicsLayerCACF::updateContentsRect() +{ + if (!m_contentsLayer) + return; + + CGPoint point = CGPointMake(m_contentsRect.x(), + m_contentsRect.y()); + CGRect rect = CGRectMake(0.0f, + 0.0f, + m_contentsRect.width(), + m_contentsRect.height()); + + m_contentsLayer->setPosition(point); + m_contentsLayer->setBounds(rect); +} + +void GraphicsLayerCACF::setupContentsLayer(WKCACFLayer* contentsLayer) +{ + if (contentsLayer == m_contentsLayer) + return; + + if (m_contentsLayer) { + m_contentsLayer->removeFromSuperlayer(); + m_contentsLayer = 0; + } + + if (contentsLayer) { + m_contentsLayer = contentsLayer; + + if (defaultContentsOrientation() == CompositingCoordinatesBottomUp) { + CATransform3D flipper = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + m_contentsLayer->setTransform(flipper); + m_contentsLayer->setAnchorPoint(CGPointMake(0.0f, 1.0f)); + } else + m_contentsLayer->setAnchorPoint(CGPointMake(0.0f, 0.0f)); + + // Insert the content layer first. Video elements require this, because they have + // shadow content that must display in front of the video. + m_layer->insertSublayer(m_contentsLayer.get(), 0); + + updateContentsRect(); + + if (showDebugBorders()) { + setLayerBorderColor(m_contentsLayer.get(), Color(0, 0, 128, 180)); + m_contentsLayer->setBorderWidth(1.0f); + } + } + updateDebugIndicators(); +} + +// This function simply mimics the operation of GraphicsLayerCA +void GraphicsLayerCACF::updateOpacityOnLayer() +{ + primaryLayer()->setOpacity(m_opacity); +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/win/GraphicsLayerCACF.h b/Source/WebCore/platform/graphics/win/GraphicsLayerCACF.h new file mode 100644 index 0000000..23f36b2 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/GraphicsLayerCACF.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009 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 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 GraphicsLayerCACF_h +#define GraphicsLayerCACF_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayer.h" +#include "GraphicsContext.h" +#include <wtf/RetainPtr.h> + +namespace WebCore { + +class WKCACFLayer; + +class GraphicsLayerCACF : public GraphicsLayer { +public: + + GraphicsLayerCACF(GraphicsLayerClient*); + virtual ~GraphicsLayerCACF(); + + virtual void setName(const String& inName); + + virtual bool setChildren(const Vector<GraphicsLayer*>&); + virtual void addChild(GraphicsLayer *layer); + virtual void addChildAtIndex(GraphicsLayer *layer, int index); + virtual void addChildAbove(GraphicsLayer *layer, GraphicsLayer *sibling); + virtual void addChildBelow(GraphicsLayer *layer, GraphicsLayer *sibling); + virtual bool replaceChild(GraphicsLayer *oldChild, GraphicsLayer *newChild); + + virtual void removeFromParent(); + + virtual void setPosition(const FloatPoint& inPoint); + virtual void setAnchorPoint(const FloatPoint3D& inPoint); + virtual void setSize(const FloatSize& inSize); + + virtual void setTransform(const TransformationMatrix&); + + virtual void setChildrenTransform(const TransformationMatrix&); + + virtual void setPreserves3D(bool); + virtual void setMasksToBounds(bool); + virtual void setDrawsContent(bool); + + virtual void setBackgroundColor(const Color&); + virtual void clearBackgroundColor(); + + virtual void setContentsOpaque(bool); + virtual void setBackfaceVisibility(bool); + + virtual void setOpacity(float opacity); + + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + + virtual void setContentsRect(const IntRect&); + + virtual void setContentsToImage(Image*); + virtual void setContentsToMedia(PlatformLayer*); + + virtual PlatformLayer* platformLayer() const; + + virtual void setDebugBackgroundColor(const Color&); + virtual void setDebugBorder(const Color&, float borderWidth); + +private: + void updateOpacityOnLayer(); + + WKCACFLayer* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } + WKCACFLayer* hostLayerForSublayers() const; + WKCACFLayer* layerForSuperlayer() const; + + bool requiresTiledLayer(const FloatSize&) const; + void swapFromOrToTiledLayer(bool useTiledLayer); + + CompositingCoordinatesOrientation defaultContentsOrientation() const; + void updateSublayerList(); + void updateLayerPosition(); + void updateLayerSize(); + void updateAnchorPoint(); + void updateTransform(); + void updateChildrenTransform(); + void updateMasksToBounds(); + void updateContentsOpaque(); + void updateBackfaceVisibility(); + void updateLayerPreserves3D(); + void updateLayerDrawsContent(); + void updateLayerBackgroundColor(); + + void updateContentsImage(); + void updateContentsMedia(); + void updateContentsRect(); + + void setupContentsLayer(WKCACFLayer*); + WKCACFLayer* contentsLayer() const { return m_contentsLayer.get(); } + + RefPtr<WKCACFLayer> m_layer; + RefPtr<WKCACFLayer> m_transformLayer; + RefPtr<WKCACFLayer> m_contentsLayer; + + enum ContentsLayerPurpose { + NoContentsLayer = 0, + ContentsLayerForImage, + ContentsLayerForMedia + }; + + ContentsLayerPurpose m_contentsLayerPurpose; + bool m_contentsLayerHasBackgroundColor : 1; + RetainPtr<CGImageRef> m_pendingContentsImage; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayerCACF_h diff --git a/Source/WebCore/platform/graphics/win/IconWin.cpp b/Source/WebCore/platform/graphics/win/IconWin.cpp new file mode 100644 index 0000000..4d4d219 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/IconWin.cpp @@ -0,0 +1,99 @@ +/* +* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. +* Copyright (C) 2007-2009 Torch Mobile, 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 "Icon.h" + +#include "GraphicsContext.h" +#include "LocalWindowsContext.h" +#include "PlatformString.h" +#include <tchar.h> +#include <windows.h> + +#if OS(WINCE) +// SHGFI_SHELLICONSIZE is not available on WINCE +#define SHGFI_SHELLICONSIZE 0 +#endif + +namespace WebCore { + +static const int shell32MultipleFileIconIndex = 54; + +Icon::Icon(HICON icon) + : m_hIcon(icon) +{ + ASSERT(icon); +} + +Icon::~Icon() +{ + DestroyIcon(m_hIcon); +} + +// FIXME: Move the code to ChromeClient::iconForFiles(). +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +{ + if (filenames.isEmpty()) + return 0; + + if (filenames.size() == 1) { + SHFILEINFO sfi; + memset(&sfi, 0, sizeof(sfi)); + + String tmpFilename = filenames[0]; + if (!SHGetFileInfo(tmpFilename.charactersWithNullTermination(), 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SHELLICONSIZE | SHGFI_SMALLICON)) + return 0; + + return adoptRef(new Icon(sfi.hIcon)); + } + +#if OS(WINCE) + return 0; +#else + TCHAR buffer[MAX_PATH]; + UINT length = ::GetSystemDirectory(buffer, WTF_ARRAY_LENGTH(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)); +#endif +} + +void Icon::paint(GraphicsContext* context, const IntRect& r) +{ + if (context->paintingDisabled()) + return; + +#if OS(WINCE) + context->drawIcon(m_hIcon, r, DI_NORMAL); +#else + LocalWindowsContext windowContext(context, r); + DrawIconEx(windowContext.hdc(), r.x(), r.y(), m_hIcon, r.width(), r.height(), 0, 0, DI_NORMAL); +#endif +} + +} diff --git a/Source/WebCore/platform/graphics/win/ImageCGWin.cpp b/Source/WebCore/platform/graphics/win/ImageCGWin.cpp new file mode 100644 index 0000000..6cc5d6d --- /dev/null +++ b/Source/WebCore/platform/graphics/win/ImageCGWin.cpp @@ -0,0 +1,108 @@ +/* + * 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 "BitmapInfo.h" +#include "GraphicsContextCG.h" +#include "PlatformString.h" +#include <ApplicationServices/ApplicationServices.h> +#include <windows.h> +#include <wtf/RetainPtr.h> + +namespace WebCore { + +PassRefPtr<BitmapImage> BitmapImage::create(HBITMAP hBitmap) +{ + DIBSECTION dibSection; + if (!GetObject(hBitmap, sizeof(DIBSECTION), &dibSection)) + return 0; + + ASSERT(dibSection.dsBm.bmBitsPixel == 32); + if (dibSection.dsBm.bmBitsPixel != 32) + return 0; + + ASSERT(dibSection.dsBm.bmBits); + if (!dibSection.dsBm.bmBits) + return 0; + + RetainPtr<CGContextRef> bitmapContext(AdoptCF, CGBitmapContextCreate(dibSection.dsBm.bmBits, dibSection.dsBm.bmWidth, dibSection.dsBm.bmHeight, 8, + dibSection.dsBm.bmWidthBytes, deviceRGBColorSpaceRef(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst)); + + // The BitmapImage takes ownership of this. + CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext.get()); + + return adoptRef(new BitmapImage(cgImage)); +} + +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; + + CGContextRef cgContext = CGBitmapContextCreate(bmpInfo.bmBits, bmpInfo.bmWidth, bmpInfo.bmHeight, + 8, bmpInfo.bmWidthBytes, deviceRGBColorSpaceRef(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); + + GraphicsContext gc(cgContext); + + IntSize imageSize = BitmapImage::size(); + if (size) + drawFrameMatchingSourceSize(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), ColorSpaceDeviceRGB, CompositeCopy); + else + draw(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), ColorSpaceDeviceRGB, CompositeCopy); + + // Do cleanup + CGContextRelease(cgContext); + + return true; +} + +void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext* ctxt, const FloatRect& dstRect, const IntSize& srcSize, ColorSpace styleColorSpace, CompositeOperator compositeOp) +{ + size_t frames = frameCount(); + for (size_t 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()), styleColorSpace, 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()), styleColorSpace, compositeOp); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/win/ImageCairoWin.cpp b/Source/WebCore/platform/graphics/win/ImageCairoWin.cpp new file mode 100644 index 0000000..70b132e --- /dev/null +++ b/Source/WebCore/platform/graphics/win/ImageCairoWin.cpp @@ -0,0 +1,114 @@ +/* + * 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 <cairo-win32.h> + +#include <windows.h> +#include "PlatformString.h" + +namespace WebCore { + +PassRefPtr<BitmapImage> BitmapImage::create(HBITMAP hBitmap) +{ + DIBSECTION dibSection; + if (!GetObject(hBitmap, sizeof(DIBSECTION), &dibSection)) + return 0; + + ASSERT(dibSection.dsBm.bmBitsPixel == 32); + if (dibSection.dsBm.bmBitsPixel != 32) + return 0; + + ASSERT(dibSection.dsBm.bmBits); + if (!dibSection.dsBm.bmBits) + return 0; + + cairo_surface_t* image = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, dibSection.dsBm.bmWidth, dibSection.dsBm.bmHeight); + + // The BitmapImage object takes over ownership of the cairo_surface_t*, so no need to destroy here. + return adoptRef(new BitmapImage(image)); +} + +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); + } + + cairo_surface_t* image = cairo_image_surface_create_for_data((unsigned char*)bmpInfo.bmBits, + CAIRO_FORMAT_ARGB32, + bmpInfo.bmWidth, + bmpInfo.bmHeight, + bmpInfo.bmWidthBytes); + + + cairo_t* targetRef = cairo_create(image); + cairo_surface_destroy(image); + + GraphicsContext gc(targetRef); + + IntSize imageSize = BitmapImage::size(); + if (size) + drawFrameMatchingSourceSize(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), ColorSpaceDeviceRGB, CompositeCopy); + else + draw(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), ColorSpaceDeviceRGB, CompositeCopy); + + // Do cleanup + cairo_destroy(targetRef); + + return true; +} + +void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext* ctxt, const FloatRect& dstRect, const IntSize& srcSize, ColorSpace styleColorSpace, CompositeOperator compositeOp) +{ + size_t frames = frameCount(); + for (size_t 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()), ColorSpaceDeviceRGB, 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()), ColorSpaceDeviceRGB, compositeOp); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/win/ImageWin.cpp b/Source/WebCore/platform/graphics/win/ImageWin.cpp new file mode 100644 index 0000000..54c5b41 --- /dev/null +++ b/Source/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/Source/WebCore/platform/graphics/win/IntPointWin.cpp b/Source/WebCore/platform/graphics/win/IntPointWin.cpp new file mode 100644 index 0000000..73db199 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/IntPointWin.cpp @@ -0,0 +1,57 @@ +/* + * 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 "config.h" +#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/Source/WebCore/platform/graphics/win/IntRectWin.cpp b/Source/WebCore/platform/graphics/win/IntRectWin.cpp new file mode 100644 index 0000000..fe25a7f --- /dev/null +++ b/Source/WebCore/platform/graphics/win/IntRectWin.cpp @@ -0,0 +1,45 @@ + +/* + * 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 "config.h" +#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/Source/WebCore/platform/graphics/win/IntSizeWin.cpp b/Source/WebCore/platform/graphics/win/IntSizeWin.cpp new file mode 100644 index 0000000..26e68da --- /dev/null +++ b/Source/WebCore/platform/graphics/win/IntSizeWin.cpp @@ -0,0 +1,45 @@ +/* + * 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 "config.h" +#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/Source/WebCore/platform/graphics/win/LocalWindowsContext.h b/Source/WebCore/platform/graphics/win/LocalWindowsContext.h new file mode 100644 index 0000000..c216140 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/LocalWindowsContext.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 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 LocalWindowsContext_h +#define LocalWindowsContext_h + +#include "config.h" +#include "GraphicsContext.h" + +namespace WebCore { + +class LocalWindowsContext : public Noncopyable { +public: + LocalWindowsContext(GraphicsContext* graphicsContext, const IntRect& rect, bool supportAlphaBlend = true, bool mayCreateBitmap = true) + : m_graphicsContext(graphicsContext) + , m_rect(rect) + , m_supportAlphaBlend(supportAlphaBlend) + , m_mayCreateBitmap(mayCreateBitmap) + { + m_hdc = m_graphicsContext->getWindowsContext(m_rect, m_supportAlphaBlend, m_mayCreateBitmap); + } + + ~LocalWindowsContext() + { + m_graphicsContext->releaseWindowsContext(m_hdc, m_rect, m_supportAlphaBlend, m_mayCreateBitmap); + } + + HDC hdc() const { return m_hdc; } + +private: + GraphicsContext* m_graphicsContext; + HDC m_hdc; + IntRect m_rect; + bool m_supportAlphaBlend; + bool m_mayCreateBitmap; +}; + +} // namespace WebCore +#endif // LocalWindowsContext_h diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp new file mode 100644 index 0000000..cbe38aa --- /dev/null +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2010 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 INC. 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 INC. 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 "MediaPlayerPrivateFullscreenWindow.h" + +#include "IntRect.h" +#include "WebCoreInstanceHandle.h" +#include <windows.h> + +#if PLATFORM(CG) +#include <CoreGraphics/CGColor.h> +#endif + +namespace WebCore { + +MediaPlayerPrivateFullscreenWindow::MediaPlayerPrivateFullscreenWindow(MediaPlayerPrivateFullscreenClient* client) + : m_client(client) + , m_hwnd(0) +#if USE(ACCELERATED_COMPOSITING) + , m_layerRenderer(WKCACFLayerRenderer::create(0)) +#endif +{ +} + +MediaPlayerPrivateFullscreenWindow::~MediaPlayerPrivateFullscreenWindow() +{ + if (m_hwnd) + close(); +} + +void MediaPlayerPrivateFullscreenWindow::createWindow(HWND parentHwnd) +{ + static ATOM windowAtom; + static LPCWSTR windowClassName = L"MediaPlayerPrivateFullscreenWindowClass"; + if (!windowAtom) { + WNDCLASSEX wcex = {0}; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = staticWndProc; + wcex.hInstance = instanceHandle(); + wcex.lpszClassName = windowClassName; + windowAtom = ::RegisterClassEx(&wcex); + } + + if (m_hwnd) + close(); + + MONITORINFO mi = {0}; + mi.cbSize = sizeof(MONITORINFO); + if (!GetMonitorInfo(MonitorFromWindow(parentHwnd, MONITOR_DEFAULTTONEAREST), &mi)) + return; + + IntRect monitorRect = mi.rcMonitor; + + ::CreateWindowExW(WS_EX_TOOLWINDOW, windowClassName, L"", WS_POPUP | WS_VISIBLE, + monitorRect.x(), monitorRect.y(), monitorRect.width(), monitorRect.height(), + parentHwnd, 0, WebCore::instanceHandle(), this); + ASSERT(IsWindow(m_hwnd)); + + ::SetFocus(m_hwnd); +} + +void MediaPlayerPrivateFullscreenWindow::close() +{ + ::DestroyWindow(m_hwnd); + ASSERT(!m_hwnd); +} + +#if USE(ACCELERATED_COMPOSITING) +void MediaPlayerPrivateFullscreenWindow::setRootChildLayer(PassRefPtr<PlatformCALayer> rootChild) +{ + if (m_rootChild == rootChild) + return; + + if (m_rootChild) + m_rootChild->removeFromSuperlayer(); + + m_rootChild = rootChild; + + if (!m_rootChild) + return; + + m_layerRenderer->setRootChildLayer(m_rootChild.get()); + PlatformCALayer* rootLayer = m_rootChild->rootLayer(); + CGRect rootBounds = m_rootChild->rootLayer()->bounds(); + m_rootChild->setFrame(rootBounds); + m_rootChild->setBackgroundColor(CGColorGetConstantColor(kCGColorBlack)); +#ifndef NDEBUG + RetainPtr<CGColorRef> redColor(AdoptCF, CGColorCreateGenericRGB(1, 0, 0, 1)); + rootLayer->setBackgroundColor(redColor.get()); +#else + rootLayer->setBackgroundColor(CGColorGetConstantColor(kCGColorBlack)); +#endif +} +#endif + +LRESULT MediaPlayerPrivateFullscreenWindow::staticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LONG_PTR longPtr = GetWindowLongPtr(hWnd, GWLP_USERDATA); + + if (!longPtr && message == WM_CREATE) { + LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam); + longPtr = reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams); + ::SetWindowLongPtr(hWnd, GWLP_USERDATA, longPtr); + } + + if (MediaPlayerPrivateFullscreenWindow* window = reinterpret_cast<MediaPlayerPrivateFullscreenWindow*>(longPtr)) + return window->wndProc(hWnd, message, wParam, lParam); + + return ::DefWindowProc(hWnd, message, wParam, lParam); +} + +LRESULT MediaPlayerPrivateFullscreenWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LRESULT lResult = 0; + switch (message) { + case WM_CREATE: + m_hwnd = hWnd; +#if USE(ACCELERATED_COMPOSITING) + m_layerRenderer->setHostWindow(m_hwnd); + m_layerRenderer->createRenderer(); + if (m_rootChild) + m_layerRenderer->setNeedsDisplay(); +#endif + break; + case WM_DESTROY: + m_hwnd = 0; +#if USE(ACCELERATED_COMPOSITING) + m_layerRenderer->destroyRenderer(); + m_layerRenderer->setHostWindow(0); +#endif + break; + case WM_WINDOWPOSCHANGED: + { + LPWINDOWPOS wp = reinterpret_cast<LPWINDOWPOS>(lParam); + if (wp->flags & SWP_NOSIZE) + break; +#if USE(ACCELERATED_COMPOSITING) + m_layerRenderer->resize(); + PlatformCALayer* rootLayer = m_rootChild->rootLayer(); + CGRect rootBounds = m_rootChild->rootLayer()->bounds(); + m_rootChild->setFrame(rootBounds); + m_rootChild->setNeedsLayout(); +#endif + } + break; + case WM_PAINT: +#if USE(ACCELERATED_COMPOSITING) + m_layerRenderer->renderSoon(); +#endif + break; + } + + if (m_client) + lResult = m_client->fullscreenClientWndProc(hWnd, message, wParam, lParam); + + return lResult; +} + +} diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.h b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.h new file mode 100644 index 0000000..a18f0cc --- /dev/null +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 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 INC. 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 INC. 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 MediaPlayerPrivateFullscreenWindow_h +#define MediaPlayerPrivateFullscreenWindow_h + +#if USE(ACCELERATED_COMPOSITING) +#include "PlatformCALayer.h" +#include "WKCACFLayerRenderer.h" +#endif +#include <wtf/OwnPtr.h> + +typedef unsigned WPARAM; +typedef long LPARAM; +typedef struct HWND__* HWND; +typedef _W64 long LONG_PTR; +typedef LONG_PTR LRESULT; +typedef unsigned int UINT; + +namespace WebCore { + +class MediaPlayerPrivateFullscreenClient { +public: + virtual LRESULT fullscreenClientWndProc(HWND, UINT message, WPARAM, LPARAM) = 0; +protected: + virtual ~MediaPlayerPrivateFullscreenClient() {} +}; + +class MediaPlayerPrivateFullscreenWindow { +public: + MediaPlayerPrivateFullscreenWindow(MediaPlayerPrivateFullscreenClient*); + ~MediaPlayerPrivateFullscreenWindow(); + + void createWindow(HWND ownerWindow); + void close(); + + HWND hwnd() const { return m_hwnd; } + +#if USE(ACCELERATED_COMPOSITING) + WKCACFLayerRenderer* layerRenderer() const { return m_layerRenderer.get(); } + + PlatformCALayer* rootChildLayer() const { return m_rootChild.get(); } + void setRootChildLayer(PassRefPtr<PlatformCALayer>); +#endif + +private: + static LRESULT __stdcall staticWndProc(HWND, UINT message, WPARAM, LPARAM); + LRESULT wndProc(HWND, UINT message, WPARAM, LPARAM); + + MediaPlayerPrivateFullscreenClient* m_client; +#if USE(ACCELERATED_COMPOSITING) + OwnPtr<WKCACFLayerRenderer> m_layerRenderer; + RefPtr<PlatformCALayer> m_rootChild; +#endif + HWND m_hwnd; +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp new file mode 100644 index 0000000..0b91455 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp @@ -0,0 +1,1252 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "MediaPlayerPrivateQuickTimeVisualContext.h" + +#include "Cookie.h" +#include "CookieJar.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "KURL.h" +#include "MediaPlayerPrivateTaskTimer.h" +#include "QTCFDictionary.h" +#include "QTDecompressionSession.h" +#include "QTMovie.h" +#include "QTMovieTask.h" +#include "QTMovieVisualContext.h" +#include "ScrollView.h" +#include "Settings.h" +#include "SoftLinking.h" +#include "TimeRanges.h" +#include "Timer.h" +#include <AssertMacros.h> +#include <CoreGraphics/CGContext.h> +#include <Wininet.h> +#include <wtf/CurrentTime.h> +#include <wtf/HashSet.h> +#include <wtf/MainThread.h> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/StringHash.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "PlatformCALayer.h" +#include "WKCAImageQueue.h" +#endif + +using namespace std; + +namespace WebCore { + +static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer); +static bool requiredDllsAvailable(); + +SOFT_LINK_LIBRARY(Wininet) +SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved), (lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved)) + +// Interface declaration for MediaPlayerPrivateQuickTimeVisualContext's QTMovieClient aggregate +class MediaPlayerPrivateQuickTimeVisualContext::MovieClient : public QTMovieClient { +public: + MovieClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {} + virtual ~MovieClient() { m_parent = 0; } + virtual void movieEnded(QTMovie*); + virtual void movieLoadStateChanged(QTMovie*); + virtual void movieTimeChanged(QTMovie*); +private: + MediaPlayerPrivateQuickTimeVisualContext* m_parent; +}; + +#if USE(ACCELERATED_COMPOSITING) +class MediaPlayerPrivateQuickTimeVisualContext::LayerClient : public PlatformCALayerClient { +public: + LayerClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {} + virtual ~LayerClient() { m_parent = 0; } + +private: + virtual void platformCALayerLayoutSublayersOfLayer(PlatformCALayer*); + virtual bool platformCALayerRespondsToLayoutChanges() const { return true; } + + virtual void platformCALayerAnimationStarted(CFTimeInterval beginTime) { } + virtual GraphicsLayer::CompositingCoordinatesOrientation platformCALayerContentsOrientation() const { return GraphicsLayer::CompositingCoordinatesBottomUp; } + virtual void platformCALayerPaintContents(GraphicsContext&, const IntRect& inClip) { } + virtual bool platformCALayerShowDebugBorders() const { return false; } + virtual bool platformCALayerShowRepaintCounter() const { return false; } + virtual int platformCALayerIncrementRepaintCount() { return 0; } + + virtual bool platformCALayerContentsOpaque() const { return false; } + virtual bool platformCALayerDrawsContent() const { return false; } + virtual void platformCALayerLayerDidDisplay(PlatformLayer*) { } + + MediaPlayerPrivateQuickTimeVisualContext* m_parent; +}; + +void MediaPlayerPrivateQuickTimeVisualContext::LayerClient::platformCALayerLayoutSublayersOfLayer(PlatformCALayer* layer) +{ + ASSERT(m_parent); + ASSERT(m_parent->m_transformLayer == layer); + + FloatSize parentSize = layer->bounds().size(); + FloatSize naturalSize = m_parent->naturalSize(); + + // Calculate the ratio of these two sizes and use that ratio to scale the qtVideoLayer: + FloatSize ratio(parentSize.width() / naturalSize.width(), parentSize.height() / naturalSize.height()); + + int videoWidth = 0; + int videoHeight = 0; + m_parent->m_movie->getNaturalSize(videoWidth, videoHeight); + FloatRect videoBounds(0, 0, videoWidth * ratio.width(), videoHeight * ratio.height()); + FloatPoint3D videoAnchor = m_parent->m_qtVideoLayer->anchorPoint(); + + // Calculate the new position based on the parent's size: + FloatPoint position(parentSize.width() * 0.5 - videoBounds.width() * (0.5 - videoAnchor.x()), + parentSize.height() * 0.5 - videoBounds.height() * (0.5 - videoAnchor.y())); + + m_parent->m_qtVideoLayer->setBounds(videoBounds); + m_parent->m_qtVideoLayer->setPosition(position); +} +#endif + +class MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient : public QTMovieVisualContextClient { +public: + VisualContextClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {} + virtual ~VisualContextClient() { m_parent = 0; } + void imageAvailableForTime(const QTCVTimeStamp*); + static void retrieveCurrentImageProc(void*); +private: + MediaPlayerPrivateQuickTimeVisualContext* m_parent; +}; + +MediaPlayerPrivateInterface* MediaPlayerPrivateQuickTimeVisualContext::create(MediaPlayer* player) +{ + return new MediaPlayerPrivateQuickTimeVisualContext(player); +} + +void MediaPlayerPrivateQuickTimeVisualContext::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + +MediaPlayerPrivateQuickTimeVisualContext::MediaPlayerPrivateQuickTimeVisualContext(MediaPlayer* player) + : m_player(player) + , m_seekTo(-1) + , m_seekTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired) + , m_visualContextTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired) + , m_networkState(MediaPlayer::Empty) + , m_readyState(MediaPlayer::HaveNothing) + , m_enabledTrackCount(0) + , m_totalTrackCount(0) + , m_hasUnsupportedTracks(false) + , m_startedPlaying(false) + , m_isStreaming(false) + , m_visible(false) + , m_newFrameAvailable(false) + , m_movieClient(new MediaPlayerPrivateQuickTimeVisualContext::MovieClient(this)) +#if USE(ACCELERATED_COMPOSITING) + , m_layerClient(new MediaPlayerPrivateQuickTimeVisualContext::LayerClient(this)) + , m_movieTransform(CGAffineTransformIdentity) +#endif + , m_visualContextClient(new MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient(this)) + , m_delayingLoad(false) + , m_preload(MediaPlayer::Auto) +{ +} + +MediaPlayerPrivateQuickTimeVisualContext::~MediaPlayerPrivateQuickTimeVisualContext() +{ + tearDownVideoRendering(); + cancelCallOnMainThread(&VisualContextClient::retrieveCurrentImageProc, this); +} + +bool MediaPlayerPrivateQuickTimeVisualContext::supportsFullscreen() const +{ +#if USE(ACCELERATED_COMPOSITING) + Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument(); + if (document && document->settings()) + return document->settings()->acceleratedCompositingEnabled(); +#endif + return false; +} + +PlatformMedia MediaPlayerPrivateQuickTimeVisualContext::platformMedia() const +{ + PlatformMedia p; + p.type = PlatformMedia::QTMovieVisualContextType; + p.media.qtMovieVisualContext = m_visualContext.get(); + return p; +} +#if USE(ACCELERATED_COMPOSITING) + +PlatformLayer* MediaPlayerPrivateQuickTimeVisualContext::platformLayer() const +{ + return m_transformLayer ? m_transformLayer->platformLayer() : 0; +} +#endif + +String MediaPlayerPrivateQuickTimeVisualContext::rfc2616DateStringFromTime(CFAbsoluteTime time) +{ + static const char* const dayStrings[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; + static const char* const monthStrings[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static const CFStringRef dateFormatString = CFSTR("%s, %02d %s %04d %02d:%02d:%02d GMT"); + static CFTimeZoneRef gmtTimeZone; + if (!gmtTimeZone) + gmtTimeZone = CFTimeZoneCopyDefault(); + + CFGregorianDate dateValue = CFAbsoluteTimeGetGregorianDate(time, gmtTimeZone); + if (!CFGregorianDateIsValid(dateValue, kCFGregorianAllUnits)) + return String(); + + time = CFGregorianDateGetAbsoluteTime(dateValue, gmtTimeZone); + SInt32 day = CFAbsoluteTimeGetDayOfWeek(time, 0); + + RetainPtr<CFStringRef> dateCFString(AdoptCF, CFStringCreateWithFormat(0, 0, dateFormatString, dayStrings[day - 1], dateValue.day, + monthStrings[dateValue.month - 1], dateValue.year, dateValue.hour, dateValue.minute, (int)dateValue.second)); + return dateCFString.get(); +} + +static void addCookieParam(StringBuilder& cookieBuilder, const String& name, const String& value) +{ + if (name.isEmpty()) + return; + + // If this isn't the first parameter added, terminate the previous one. + if (cookieBuilder.length()) + cookieBuilder.append("; "); + + // Add parameter name, and value if there is one. + cookieBuilder.append(name); + if (!value.isEmpty()) { + cookieBuilder.append('='); + cookieBuilder.append(value); + } +} + +void MediaPlayerPrivateQuickTimeVisualContext::setUpCookiesForQuickTime(const String& url) +{ + // WebCore loaded the page with the movie URL with CFNetwork but QuickTime will + // use WinINet to download the movie, so we need to copy any cookies needed to + // download the movie into WinInet before asking QuickTime to open it. + Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument(); + Frame* frame = document ? document->frame() : 0; + if (!frame || !frame->page() || !frame->page()->cookieEnabled()) + return; + + KURL movieURL = KURL(KURL(), url); + Vector<Cookie> documentCookies; + if (!getRawCookies(frame->document(), movieURL, documentCookies)) + return; + + for (size_t ndx = 0; ndx < documentCookies.size(); ndx++) { + const Cookie& cookie = documentCookies[ndx]; + + if (cookie.name.isEmpty()) + continue; + + // Build up the cookie string with as much information as we can get so WinINet + // knows what to do with it. + StringBuilder cookieBuilder; + addCookieParam(cookieBuilder, cookie.name, cookie.value); + addCookieParam(cookieBuilder, "path", cookie.path); + if (cookie.expires) + addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires)); + if (cookie.httpOnly) + addCookieParam(cookieBuilder, "httpOnly", String()); + cookieBuilder.append(';'); + + String cookieURL; + if (!cookie.domain.isEmpty()) { + StringBuilder urlBuilder; + + urlBuilder.append(movieURL.protocol()); + urlBuilder.append("://"); + if (cookie.domain[0] == '.') + urlBuilder.append(cookie.domain.substring(1)); + else + urlBuilder.append(cookie.domain); + if (cookie.path.length() > 1) + urlBuilder.append(cookie.path); + + cookieURL = urlBuilder.toString(); + } else + cookieURL = movieURL; + + InternetSetCookieExW(cookieURL.charactersWithNullTermination(), 0, cookieBuilder.toString().charactersWithNullTermination(), 0, 0); + } +} + +static void disableComponentsOnce() +{ + static bool sComponentsDisabled = false; + if (sComponentsDisabled) + return; + sComponentsDisabled = true; + + uint32_t componentsToDisable[][5] = { + {'eat ', 'TEXT', 'text', 0, 0}, + {'eat ', 'TXT ', 'text', 0, 0}, + {'eat ', 'utxt', 'text', 0, 0}, + {'eat ', 'TEXT', 'tx3g', 0, 0}, + }; + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(componentsToDisable); ++i) + QTMovie::disableComponent(componentsToDisable[i]); +} + +void MediaPlayerPrivateQuickTimeVisualContext::resumeLoad() +{ + m_delayingLoad = false; + + if (!m_movieURL.isEmpty()) + loadInternal(m_movieURL); +} + +void MediaPlayerPrivateQuickTimeVisualContext::load(const String& url) +{ + m_movieURL = url; + + if (m_preload == MediaPlayer::None) { + m_delayingLoad = true; + return; + } + + loadInternal(url); +} + +void MediaPlayerPrivateQuickTimeVisualContext::loadInternal(const String& url) +{ + if (!QTMovie::initializeQuickTime()) { + // FIXME: is this the right error to return? + m_networkState = MediaPlayer::DecodeError; + m_player->networkStateChanged(); + return; + } + + disableComponentsOnce(); + + // Initialize the task timer. + MediaPlayerPrivateTaskTimer::initialize(); + + if (m_networkState != MediaPlayer::Loading) { + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + } + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; + m_player->readyStateChanged(); + } + cancelSeek(); + + setUpCookiesForQuickTime(url); + + m_movie = adoptRef(new QTMovie(m_movieClient.get())); + m_movie->load(url.characters(), url.length(), m_player->preservesPitch()); + m_movie->setVolume(m_player->volume()); +} + +void MediaPlayerPrivateQuickTimeVisualContext::prepareToPlay() +{ + if (!m_movie || m_delayingLoad) + resumeLoad(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::play() +{ + if (!m_movie) + return; + m_startedPlaying = true; + + m_movie->play(); + m_visualContextTimer.startRepeating(1.0 / 30); +} + +void MediaPlayerPrivateQuickTimeVisualContext::pause() +{ + if (!m_movie) + return; + m_startedPlaying = false; + + m_movie->pause(); + m_visualContextTimer.stop(); +} + +float MediaPlayerPrivateQuickTimeVisualContext::duration() const +{ + if (!m_movie) + return 0; + return m_movie->duration(); +} + +float MediaPlayerPrivateQuickTimeVisualContext::currentTime() const +{ + if (!m_movie) + return 0; + return m_movie->currentTime(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::seek(float time) +{ + cancelSeek(); + + if (!m_movie) + return; + + if (time > duration()) + time = duration(); + + m_seekTo = time; + if (maxTimeLoaded() >= m_seekTo) + doSeek(); + else + m_seekTimer.start(0, 0.5f); +} + +void MediaPlayerPrivateQuickTimeVisualContext::doSeek() +{ + float oldRate = m_movie->rate(); + if (oldRate) + m_movie->setRate(0); + m_movie->setCurrentTime(m_seekTo); + float timeAfterSeek = currentTime(); + // restore playback only if not at end, othewise QTMovie will loop + if (oldRate && timeAfterSeek < duration()) + m_movie->setRate(oldRate); + cancelSeek(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::cancelSeek() +{ + m_seekTo = -1; + m_seekTimer.stop(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*) +{ + if (!m_movie || !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(); + } + } +} + +bool MediaPlayerPrivateQuickTimeVisualContext::paused() const +{ + if (!m_movie) + return true; + return (!m_movie->rate()); +} + +bool MediaPlayerPrivateQuickTimeVisualContext::seeking() const +{ + if (!m_movie) + return false; + return m_seekTo >= 0; +} + +IntSize MediaPlayerPrivateQuickTimeVisualContext::naturalSize() const +{ + if (!m_movie) + return IntSize(); + int width; + int height; + m_movie->getNaturalSize(width, height); +#if USE(ACCELERATED_COMPOSITING) + CGSize originalSize = {width, height}; + CGSize transformedSize = CGSizeApplyAffineTransform(originalSize, m_movieTransform); + return IntSize(abs(transformedSize.width), abs(transformedSize.height)); +#else + return IntSize(width, height); +#endif +} + +bool MediaPlayerPrivateQuickTimeVisualContext::hasVideo() const +{ + if (!m_movie) + return false; + return m_movie->hasVideo(); +} + +bool MediaPlayerPrivateQuickTimeVisualContext::hasAudio() const +{ + if (!m_movie) + return false; + return m_movie->hasAudio(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::setVolume(float volume) +{ + if (!m_movie) + return; + m_movie->setVolume(volume); +} + +void MediaPlayerPrivateQuickTimeVisualContext::setRate(float rate) +{ + if (!m_movie) + return; + + // Do not call setRate(...) unless we have started playing; otherwise + // QuickTime's VisualContext can get wedged waiting for a rate change + // call which will never come. + if (m_startedPlaying) + m_movie->setRate(rate); +} + +void MediaPlayerPrivateQuickTimeVisualContext::setPreservesPitch(bool preservesPitch) +{ + if (!m_movie) + return; + m_movie->setPreservesPitch(preservesPitch); +} + +bool MediaPlayerPrivateQuickTimeVisualContext::hasClosedCaptions() const +{ + if (!m_movie) + return false; + return m_movie->hasClosedCaptions(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::setClosedCaptionsVisible(bool visible) +{ + if (!m_movie) + return; + m_movie->setClosedCaptionsVisible(visible); +} + +PassRefPtr<TimeRanges> MediaPlayerPrivateQuickTimeVisualContext::buffered() const +{ + RefPtr<TimeRanges> timeRanges = TimeRanges::create(); + float loaded = maxTimeLoaded(); + // rtsp streams are not buffered + if (!m_isStreaming && loaded > 0) + timeRanges->add(0, loaded); + return timeRanges.release(); +} + +float MediaPlayerPrivateQuickTimeVisualContext::maxTimeSeekable() const +{ + // infinite duration means live stream + return !isfinite(duration()) ? 0 : maxTimeLoaded(); +} + +float MediaPlayerPrivateQuickTimeVisualContext::maxTimeLoaded() const +{ + if (!m_movie) + return 0; + return m_movie->maxTimeLoaded(); +} + +unsigned MediaPlayerPrivateQuickTimeVisualContext::bytesLoaded() const +{ + if (!m_movie) + return 0; + float dur = duration(); + float maxTime = maxTimeLoaded(); + if (!dur) + return 0; + return totalBytes() * maxTime / dur; +} + +unsigned MediaPlayerPrivateQuickTimeVisualContext::totalBytes() const +{ + if (!m_movie) + return 0; + return m_movie->dataSize(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::cancelLoad() +{ + if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) + return; + + tearDownVideoRendering(); + + // Cancel the load by destroying the movie. + m_movie.clear(); + + updateStates(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::updateStates() +{ + MediaPlayer::NetworkState oldNetworkState = m_networkState; + MediaPlayer::ReadyState oldReadyState = m_readyState; + + long loadState = m_movie ? m_movie->loadState() : QTMovieLoadStateError; + + if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) { + m_movie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount); + if (m_player->inMediaDocument()) { + if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) { + // This is a type of media that we do not handle directly with a <video> + // element, eg. QuickTime VR, a movie with a sprite track, etc. Tell the + // MediaPlayerClient that we won't support it. + sawUnsupportedTracks(); + return; + } + } else if (!m_enabledTrackCount) + loadState = QTMovieLoadStateError; + } + + // "Loaded" is reserved for fully buffered movies, never the case when streaming + if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::HaveEnoughData; + } else if (loadState >= QTMovieLoadStatePlaythroughOK) { + m_readyState = MediaPlayer::HaveEnoughData; + } else if (loadState >= QTMovieLoadStatePlayable) { + // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; + } else if (loadState >= QTMovieLoadStateLoaded) { + m_readyState = MediaPlayer::HaveMetadata; + } else if (loadState > QTMovieLoadStateError) { + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::HaveNothing; + } else { + if (m_player->inMediaDocument()) { + // Something went wrong in the loading of media within a standalone file. + // This can occur with chained ref movies that eventually resolve to a + // file we don't support. + sawUnsupportedTracks(); + return; + } + + float loaded = maxTimeLoaded(); + if (!loaded) + m_readyState = MediaPlayer::HaveNothing; + + if (!m_enabledTrackCount) + m_networkState = MediaPlayer::FormatError; + else { + // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> + if (loaded > 0) + m_networkState = MediaPlayer::DecodeError; + else + m_readyState = MediaPlayer::HaveNothing; + } + } + + if (isReadyForRendering() && !hasSetUpVideoRendering()) + setUpVideoRendering(); + + if (seeking()) + m_readyState = MediaPlayer::HaveNothing; + + if (m_networkState != oldNetworkState) + m_player->networkStateChanged(); + if (m_readyState != oldReadyState) + m_player->readyStateChanged(); +} + +bool MediaPlayerPrivateQuickTimeVisualContext::isReadyForRendering() const +{ + return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::sawUnsupportedTracks() +{ + m_movie->setDisabled(true); + m_hasUnsupportedTracks = true; + m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); +} + +void MediaPlayerPrivateQuickTimeVisualContext::didEnd() +{ + if (m_hasUnsupportedTracks) + return; + + m_startedPlaying = false; + + updateStates(); + m_player->timeChanged(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::setSize(const IntSize& size) +{ + if (m_hasUnsupportedTracks || !m_movie || m_size == size) + return; + m_size = size; +} + +void MediaPlayerPrivateQuickTimeVisualContext::setVisible(bool visible) +{ + if (m_hasUnsupportedTracks || !m_movie || m_visible == visible) + return; + + m_visible = visible; + if (m_visible) { + if (isReadyForRendering()) + setUpVideoRendering(); + } else + tearDownVideoRendering(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::paint(GraphicsContext* p, const IntRect& r) +{ + MediaRenderingMode currentMode = currentRenderingMode(); + + if (currentMode == MediaRenderingNone) + return; + + if (currentMode == MediaRenderingSoftwareRenderer && !m_visualContext) + return; + + QTPixelBuffer buffer = m_visualContext->imageForTime(0); + if (buffer.pixelBufferRef()) { +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) { + // We are probably being asked to render the video into a canvas, but + // there's a good chance the QTPixelBuffer is not ARGB and thus can't be + // drawn using CG. If so, fire up an ICMDecompressionSession and convert + // the current frame into something which can be rendered by CG. + if (!buffer.pixelFormatIs32ARGB() && !buffer.pixelFormatIs32BGRA()) { + // The decompression session will only decompress a specific pixelFormat + // at a specific width and height; if these differ, the session must be + // recreated with the new parameters. + if (!m_decompressionSession || !m_decompressionSession->canDecompress(buffer)) + m_decompressionSession = QTDecompressionSession::create(buffer.pixelFormatType(), buffer.width(), buffer.height()); + buffer = m_decompressionSession->decompress(buffer); + } + } +#endif + CGImageRef image = CreateCGImageFromPixelBuffer(buffer); + + CGContextRef context = p->platformContext(); + CGContextSaveGState(context); + CGContextTranslateCTM(context, r.x(), r.y()); + CGContextTranslateCTM(context, 0, r.height()); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, CGRectMake(0, 0, r.width(), r.height()), image); + CGContextRestoreGState(context); + + CGImageRelease(image); + } + paintCompleted(*p, r); +} + +void MediaPlayerPrivateQuickTimeVisualContext::paintCompleted(GraphicsContext& context, const IntRect& rect) +{ + m_newFrameAvailable = false; +} + +void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::retrieveCurrentImageProc(void* refcon) +{ + static_cast<MediaPlayerPrivateQuickTimeVisualContext*>(refcon)->retrieveCurrentImage(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::imageAvailableForTime(const QTCVTimeStamp* timeStamp) +{ + // This call may come in on another thread, so marshall to the main thread first: + callOnMainThread(&retrieveCurrentImageProc, m_parent); + + // callOnMainThread must be paired with cancelCallOnMainThread in the destructor, + // in case this object is deleted before the main thread request is handled. +} + +void MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*) +{ + if (m_visualContext && m_visualContext->isImageAvailableForTime(0)) + retrieveCurrentImage(); +} + +static CFDictionaryRef QTCFDictionaryCreateWithDataCallback(CFAllocatorRef allocator, const UInt8* bytes, CFIndex length) +{ + RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(allocator, bytes, length, kCFAllocatorNull)); + if (!data) + return 0; + + return reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(allocator, data.get(), kCFPropertyListImmutable, 0)); +} + +static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer) +{ +#if USE(ACCELERATED_COMPOSITING) + CGDataProviderRef provider = 0; + CGColorSpaceRef colorSpace = 0; + CGImageRef image = 0; + + size_t bitsPerComponent = 0; + size_t bitsPerPixel = 0; + CGImageAlphaInfo alphaInfo = kCGImageAlphaNone; + + if (buffer.pixelFormatIs32BGRA()) { + bitsPerComponent = 8; + bitsPerPixel = 32; + alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little); + } else if (buffer.pixelFormatIs32ARGB()) { + bitsPerComponent = 8; + bitsPerPixel = 32; + alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big); + } else { + // All other pixel formats are currently unsupported: + ASSERT_NOT_REACHED(); + } + + CGDataProviderDirectAccessCallbacks callbacks = { + &QTPixelBuffer::dataProviderGetBytePointerCallback, + &QTPixelBuffer::dataProviderReleaseBytePointerCallback, + &QTPixelBuffer::dataProviderGetBytesAtPositionCallback, + &QTPixelBuffer::dataProviderReleaseInfoCallback, + }; + + // Colorspace should be device, so that Quartz does not have to do an extra render. + colorSpace = CGColorSpaceCreateDeviceRGB(); + require(colorSpace, Bail); + + provider = CGDataProviderCreateDirectAccess(buffer.pixelBufferRef(), buffer.dataSize(), &callbacks); + require(provider, Bail); + + // CGDataProvider does not retain the buffer, but it will release it later, so do an extra retain here: + QTPixelBuffer::retainCallback(buffer.pixelBufferRef()); + + image = CGImageCreate(buffer.width(), buffer.height(), bitsPerComponent, bitsPerPixel, buffer.bytesPerRow(), colorSpace, alphaInfo, provider, 0, false, kCGRenderingIntentDefault); + +Bail: + // Once the image is created we can release our reference to the provider and the colorspace, they are retained by the image + if (provider) + CGDataProviderRelease(provider); + if (colorSpace) + CGColorSpaceRelease(colorSpace); + + return image; +#else + return 0; +#endif +} + + +void MediaPlayerPrivateQuickTimeVisualContext::retrieveCurrentImage() +{ + if (!m_visualContext) + return; + +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) { + + QTPixelBuffer buffer = m_visualContext->imageForTime(0); + if (!buffer.pixelBufferRef()) + return; + + PlatformCALayer* layer = m_qtVideoLayer.get(); + + if (!buffer.lockBaseAddress()) { + if (requiredDllsAvailable()) { + if (!m_imageQueue) { + m_imageQueue = new WKCAImageQueue(buffer.width(), buffer.height(), 30); + m_imageQueue->setFlags(WKCAImageQueue::Fill, WKCAImageQueue::Fill); + layer->setContents(m_imageQueue->get()); + } + + // Debug QuickTime links against a non-Debug version of CoreFoundation, so the + // CFDictionary attached to the CVPixelBuffer cannot be directly passed on into the + // CAImageQueue without being converted to a non-Debug CFDictionary. Additionally, + // old versions of QuickTime used a non-AAS CoreFoundation, so the types are not + // interchangable even in the release case. + RetainPtr<CFDictionaryRef> attachments(AdoptCF, QTCFDictionaryCreateCopyWithDataCallback(kCFAllocatorDefault, buffer.attachments(), &QTCFDictionaryCreateWithDataCallback)); + CFTimeInterval imageTime = QTMovieVisualContext::currentHostTime(); + + m_imageQueue->collect(); + + uint64_t imageId = m_imageQueue->registerPixelBuffer(buffer.baseAddress(), buffer.dataSize(), buffer.bytesPerRow(), buffer.width(), buffer.height(), buffer.pixelFormatType(), attachments.get(), 0); + + if (m_imageQueue->insertImage(imageTime, WKCAImageQueue::Buffer, imageId, WKCAImageQueue::Opaque | WKCAImageQueue::Flush, &QTPixelBuffer::imageQueueReleaseCallback, buffer.pixelBufferRef())) { + // Retain the buffer one extra time so it doesn't dissappear before CAImageQueue decides to release it: + QTPixelBuffer::retainCallback(buffer.pixelBufferRef()); + } + + } else { + CGImageRef image = CreateCGImageFromPixelBuffer(buffer); + layer->setContents(image); + CGImageRelease(image); + } + + buffer.unlockBaseAddress(); + layer->setNeedsCommit(); + } + } else +#endif + m_player->repaint(); + + m_visualContext->task(); +} + +static HashSet<String> mimeTypeCache() +{ + DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ()); + static bool typeListInitialized = false; + + if (!typeListInitialized) { + unsigned count = QTMovie::countSupportedTypes(); + for (unsigned n = 0; n < count; n++) { + const UChar* character; + unsigned len; + QTMovie::getSupportedType(n, character, len); + if (len) + typeCache.add(String(character, len)); + } + + typeListInitialized = true; + } + + return typeCache; +} + +static CFStringRef createVersionStringFromModuleName(LPCWSTR moduleName) +{ + HMODULE module = GetModuleHandleW(moduleName); + if (!module) + return 0; + + wchar_t filePath[MAX_PATH] = {0}; + if (!GetModuleFileNameW(module, filePath, MAX_PATH)) + return 0; + + DWORD versionInfoSize = GetFileVersionInfoSizeW(filePath, 0); + if (!versionInfoSize) + return 0; + + CFStringRef versionString = 0; + void* versionInfo = calloc(versionInfoSize, sizeof(char)); + if (GetFileVersionInfo(filePath, 0, versionInfoSize, versionInfo)) { + VS_FIXEDFILEINFO* fileInfo = 0; + UINT fileInfoLength = 0; + + if (VerQueryValueW(versionInfo, L"\\", reinterpret_cast<LPVOID*>(&fileInfo), &fileInfoLength)) { + versionString = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%d.%d.%d.%d"), + HIWORD(fileInfo->dwFileVersionMS), LOWORD(fileInfo->dwFileVersionMS), + HIWORD(fileInfo->dwFileVersionLS), LOWORD(fileInfo->dwFileVersionLS)); + } + } + free(versionInfo); + + return versionString; +} + +static bool requiredDllsAvailable() +{ + static bool s_prerequisitesChecked = false; + static bool s_prerequisitesSatisfied; + static const CFStringRef kMinQuartzCoreVersion = CFSTR("1.0.42.0"); + static const CFStringRef kMinCoreVideoVersion = CFSTR("1.0.1.0"); + + if (s_prerequisitesChecked) + return s_prerequisitesSatisfied; + s_prerequisitesChecked = true; + s_prerequisitesSatisfied = false; + + CFStringRef quartzCoreString = createVersionStringFromModuleName(L"QuartzCore"); + if (!quartzCoreString) + quartzCoreString = createVersionStringFromModuleName(L"QuartzCore_debug"); + + CFStringRef coreVideoString = createVersionStringFromModuleName(L"CoreVideo"); + if (!coreVideoString) + coreVideoString = createVersionStringFromModuleName(L"CoreVideo_debug"); + + s_prerequisitesSatisfied = (quartzCoreString && coreVideoString + && CFStringCompare(quartzCoreString, kMinQuartzCoreVersion, kCFCompareNumerically) != kCFCompareLessThan + && CFStringCompare(coreVideoString, kMinCoreVideoVersion, kCFCompareNumerically) != kCFCompareLessThan); + + if (quartzCoreString) + CFRelease(quartzCoreString); + if (coreVideoString) + CFRelease(coreVideoString); + + return s_prerequisitesSatisfied; +} + +void MediaPlayerPrivateQuickTimeVisualContext::getSupportedTypes(HashSet<String>& types) +{ + types = mimeTypeCache(); +} + +bool MediaPlayerPrivateQuickTimeVisualContext::isAvailable() +{ + return QTMovie::initializeQuickTime(); +} + +MediaPlayer::SupportsType MediaPlayerPrivateQuickTimeVisualContext::supportsType(const String& type, const String& codecs) +{ + // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an + // extended MIME type + return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; +} + +void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieEnded(QTMovie* movie) +{ + if (m_parent->m_hasUnsupportedTracks) + return; + + m_parent->m_visualContextTimer.stop(); + + ASSERT(m_parent->m_movie.get() == movie); + m_parent->didEnd(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieLoadStateChanged(QTMovie* movie) +{ + if (m_parent->m_hasUnsupportedTracks) + return; + + ASSERT(m_parent->m_movie.get() == movie); + m_parent->updateStates(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieTimeChanged(QTMovie* movie) +{ + if (m_parent->m_hasUnsupportedTracks) + return; + + ASSERT(m_parent->m_movie.get() == movie); + m_parent->updateStates(); + m_parent->m_player->timeChanged(); +} + +bool MediaPlayerPrivateQuickTimeVisualContext::hasSingleSecurityOrigin() const +{ + // We tell quicktime to disallow resources that come from different origins + // so we all media is single origin. + return true; +} + +void MediaPlayerPrivateQuickTimeVisualContext::setPreload(MediaPlayer::Preload preload) +{ + m_preload = preload; + if (m_delayingLoad && m_preload != MediaPlayer::None) + resumeLoad(); +} + +float MediaPlayerPrivateQuickTimeVisualContext::mediaTimeForTimeValue(float timeValue) const +{ + long timeScale; + if (m_readyState < MediaPlayer::HaveMetadata || !(timeScale = m_movie->timeScale())) + return timeValue; + + long mediaTimeValue = static_cast<long>(timeValue * timeScale); + return static_cast<float>(mediaTimeValue) / timeScale; +} + +MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::currentRenderingMode() const +{ + if (!m_movie) + return MediaRenderingNone; + +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + return MediaRenderingMovieLayer; +#endif + + return m_visualContext ? MediaRenderingSoftwareRenderer : MediaRenderingNone; +} + +MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::preferredRenderingMode() const +{ + if (!m_player->frameView() || !m_movie) + return MediaRenderingNone; + +#if USE(ACCELERATED_COMPOSITING) + if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) + return MediaRenderingMovieLayer; +#endif + + return MediaRenderingSoftwareRenderer; +} + +void MediaPlayerPrivateQuickTimeVisualContext::setUpVideoRendering() +{ + MediaRenderingMode currentMode = currentRenderingMode(); + MediaRenderingMode preferredMode = preferredRenderingMode(); + +#if !USE(ACCELERATED_COMPOSITING) + ASSERT(preferredMode != MediaRenderingMovieLayer); +#endif + + if (currentMode == preferredMode && currentMode != MediaRenderingNone) + return; + + if (currentMode != MediaRenderingNone) + tearDownVideoRendering(); + + if (preferredMode == MediaRenderingMovieLayer) + createLayerForMovie(); + +#if USE(ACCELERATED_COMPOSITING) + if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer) + m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); +#endif + + QTPixelBuffer::Type contextType = requiredDllsAvailable() && preferredMode == MediaRenderingMovieLayer ? QTPixelBuffer::ConfigureForCAImageQueue : QTPixelBuffer::ConfigureForCGImage; + m_visualContext = QTMovieVisualContext::create(m_visualContextClient.get(), contextType); + m_visualContext->setMovie(m_movie.get()); +} + +void MediaPlayerPrivateQuickTimeVisualContext::tearDownVideoRendering() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + destroyLayerForMovie(); +#endif + + m_visualContext = 0; +} + +bool MediaPlayerPrivateQuickTimeVisualContext::hasSetUpVideoRendering() const +{ +#if USE(ACCELERATED_COMPOSITING) + return m_qtVideoLayer || (currentRenderingMode() != MediaRenderingMovieLayer && m_visualContext); +#else + return true; +#endif +} + +void MediaPlayerPrivateQuickTimeVisualContext::retrieveAndResetMovieTransform() +{ +#if USE(ACCELERATED_COMPOSITING) + // First things first, reset the total movie transform so that + // we can bail out early: + m_movieTransform = CGAffineTransformIdentity; + + if (!m_movie || !m_movie->hasVideo()) + return; + + // This trick will only work on movies with a single video track, + // so bail out early if the video contains more than one (or zero) + // video tracks. + QTTrackArray videoTracks = m_movie->videoTracks(); + if (videoTracks.size() != 1) + return; + + QTTrack* track = videoTracks[0].get(); + ASSERT(track); + + CGAffineTransform movieTransform = m_movie->getTransform(); + if (!CGAffineTransformEqualToTransform(movieTransform, CGAffineTransformIdentity)) + m_movie->resetTransform(); + + CGAffineTransform trackTransform = track->getTransform(); + if (!CGAffineTransformEqualToTransform(trackTransform, CGAffineTransformIdentity)) + track->resetTransform(); + + // Multiply the two transforms together, taking care to + // do so in the correct order, track * movie = final: + m_movieTransform = CGAffineTransformConcat(trackTransform, movieTransform); +#endif +} + +void MediaPlayerPrivateQuickTimeVisualContext::createLayerForMovie() +{ +#if USE(ACCELERATED_COMPOSITING) + ASSERT(supportsAcceleratedRendering()); + + if (!m_movie || m_qtVideoLayer) + return; + + // Create a PlatformCALayer which will transform the contents of the video layer + // which is in m_qtVideoLayer. + m_transformLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, m_layerClient.get()); + if (!m_transformLayer) + return; + + // Mark the layer as anchored in the top left. + m_transformLayer->setAnchorPoint(FloatPoint3D()); + + m_qtVideoLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, 0); + if (!m_qtVideoLayer) + return; + + if (CGAffineTransformEqualToTransform(m_movieTransform, CGAffineTransformIdentity)) + retrieveAndResetMovieTransform(); + CGAffineTransform t = m_movieTransform; + + // Remove the translation portion of the transform, since we will always rotate about + // the layer's center point. In our limited use-case (a single video track), this is + // safe: + t.tx = t.ty = 0; + m_qtVideoLayer->setTransform(CATransform3DMakeAffineTransform(t)); + +#ifndef NDEBUG + m_qtVideoLayer->setName("Video layer"); +#endif + m_transformLayer->appendSublayer(m_qtVideoLayer.get()); + m_transformLayer->setNeedsLayout(); + // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration(). +#endif + + // Fill the newly created layer with image data, so we're not looking at + // an empty layer until the next time a new image is available, which could + // be a long time if we're paused. + if (m_visualContext) + retrieveCurrentImage(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::destroyLayerForMovie() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) { + m_qtVideoLayer->removeFromSuperlayer(); + m_qtVideoLayer = 0; + } + + if (m_transformLayer) + m_transformLayer = 0; + + if (m_imageQueue) + m_imageQueue = 0; +#endif +} + +#if USE(ACCELERATED_COMPOSITING) +bool MediaPlayerPrivateQuickTimeVisualContext::supportsAcceleratedRendering() const +{ + return isReadyForRendering(); +} + +void MediaPlayerPrivateQuickTimeVisualContext::acceleratedRenderingStateChanged() +{ + // Set up or change the rendering path if necessary. + setUpVideoRendering(); +} + +#endif + + +} + +#endif diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.h b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.h new file mode 100644 index 0000000..a12d79e --- /dev/null +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 MediaPlayerPrivateQuickTimeVisualContext_h +#define MediaPlayerPrivateQuickTimeVisualContext_h + +#if ENABLE(VIDEO) + +#include "MediaPlayerPrivate.h" +#include "Timer.h" +#include <wtf/Forward.h> +#include <wtf/OwnPtr.h> +#include <wtf/RetainPtr.h> + +#ifndef DRAW_FRAME_RATE +#define DRAW_FRAME_RATE 0 +#endif + +typedef struct CGImage *CGImageRef; +class QTMovie; +class QTMovieVisualContext; +class QTDecompressionSession; + +namespace WebCore { + +class GraphicsContext; +class IntSize; +class IntRect; + +#if USE(ACCELERATED_COMPOSITING) +class PlatformCALayer; +class WKCAImageQueue; +#endif + +class MediaPlayerPrivateQuickTimeVisualContext : public MediaPlayerPrivateInterface { +public: + static void registerMediaEngine(MediaEngineRegistrar); + + ~MediaPlayerPrivateQuickTimeVisualContext(); + +private: + MediaPlayerPrivateQuickTimeVisualContext(MediaPlayer*); + + virtual bool supportsFullscreen() const; + virtual PlatformMedia platformMedia() const; +#if USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const; +#endif + + IntSize naturalSize() const; + bool hasVideo() const; + bool hasAudio() const; + + void load(const String& url); + void cancelLoad(); + void loadInternal(const String& url); + void resumeLoad(); + + void play(); + void pause(); + void prepareToPlay(); + + bool paused() const; + bool seeking() const; + + float duration() const; + float currentTime() const; + void seek(float time); + + void setRate(float); + void setVolume(float); + void setPreservesPitch(bool); + + MediaPlayer::NetworkState networkState() const { return m_networkState; } + MediaPlayer::ReadyState readyState() const { return m_readyState; } + + PassRefPtr<TimeRanges> buffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + unsigned totalBytes() const; + + void setVisible(bool); + void setSize(const IntSize&); + + void loadStateChanged(); + void didEnd(); + + void paint(GraphicsContext*, const IntRect&); + void paintCompleted(GraphicsContext&, const IntRect&); + + bool hasSingleSecurityOrigin() const; + + bool hasClosedCaptions() const; + void setClosedCaptionsVisible(bool); + + void setPreload(MediaPlayer::Preload); + + void updateStates(); + void doSeek(); + void cancelSeek(); + void seekTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*); + float maxTimeLoaded() const; + void sawUnsupportedTracks(); + + // engine support + static MediaPlayerPrivateInterface* create(MediaPlayer*); + static void getSupportedTypes(HashSet<String>& types); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + +#if USE(ACCELERATED_COMPOSITING) + virtual bool supportsAcceleratedRendering() const; + virtual void acceleratedRenderingStateChanged(); +#endif + + enum MediaRenderingMode { MediaRenderingNone, MediaRenderingSoftwareRenderer, MediaRenderingMovieLayer }; + MediaRenderingMode currentRenderingMode() const; + MediaRenderingMode preferredRenderingMode() const; + bool isReadyForRendering() const; + + void setUpVideoRendering(); + void tearDownVideoRendering(); + bool hasSetUpVideoRendering() const; + + void createLayerForMovie(); + void destroyLayerForMovie(); + + void setUpCookiesForQuickTime(const String& url); + String rfc2616DateStringFromTime(CFAbsoluteTime); + + void visualContextTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*); + void retrieveCurrentImage(); + + class MovieClient; + friend class MovieClient; + OwnPtr<MovieClient> m_movieClient; + +#if USE(ACCELERATED_COMPOSITING) + class LayerClient; + friend class LayerClient; + OwnPtr<LayerClient> m_layerClient; +#endif + + class VisualContextClient; + friend class VisualContextClient; + OwnPtr<VisualContextClient> m_visualContextClient; + + void retrieveAndResetMovieTransform(); + + virtual float mediaTimeForTimeValue(float) const; + + MediaPlayer* m_player; + RefPtr<QTMovie> m_movie; +#if USE(ACCELERATED_COMPOSITING) + RefPtr<PlatformCALayer> m_qtVideoLayer; + RefPtr<PlatformCALayer> m_transformLayer; + OwnPtr<WKCAImageQueue> m_imageQueue; + OwnPtr<QTDecompressionSession> m_decompressionSession; + CGAffineTransform m_movieTransform; +#endif + RefPtr<QTMovieVisualContext> m_visualContext; + float m_seekTo; + Timer<MediaPlayerPrivateQuickTimeVisualContext> m_seekTimer; + Timer<MediaPlayerPrivateQuickTimeVisualContext> m_visualContextTimer; + IntSize m_size; + MediaPlayer::NetworkState m_networkState; + MediaPlayer::ReadyState m_readyState; + unsigned m_enabledTrackCount; + unsigned m_totalTrackCount; + bool m_hasUnsupportedTracks; + bool m_startedPlaying; + bool m_isStreaming; + bool m_visible; + bool m_newFrameAvailable; + bool m_delayingLoad; + String m_movieURL; + MediaPlayer::Preload m_preload; +#if DRAW_FRAME_RATE + double m_frameCountWhilePlaying; + double m_timeStartedPlaying; + double m_timeStoppedPlaying; +#endif + +}; + +} + +#endif +#endif diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp new file mode 100644 index 0000000..431d624 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -0,0 +1,933 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "Cookie.h" +#include "CookieJar.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "KURL.h" +#include "MediaPlayerPrivateTaskTimer.h" +#include "QTMovieTask.h" +#include "ScrollView.h" +#include "SoftLinking.h" +#include "TimeRanges.h" +#include "Timer.h" +#include <Wininet.h> +#include <wtf/CurrentTime.h> +#include <wtf/HashSet.h> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/StringHash.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayerCACF.h" +#include "PlatformCALayer.h" +#endif + +#if DRAW_FRAME_RATE +#include "Document.h" +#include "Font.h" +#include "RenderObject.h" +#include "RenderStyle.h" +#include "Windows.h" +#endif + +using namespace std; + +namespace WebCore { + +SOFT_LINK_LIBRARY(Wininet) +SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved), (lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved)) + +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + +MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) + : m_player(player) + , m_seekTo(-1) + , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) + , m_networkState(MediaPlayer::Empty) + , m_readyState(MediaPlayer::HaveNothing) + , m_enabledTrackCount(0) + , m_totalTrackCount(0) + , m_hasUnsupportedTracks(false) + , m_startedPlaying(false) + , m_isStreaming(false) + , m_visible(false) + , m_newFrameAvailable(false) +#if DRAW_FRAME_RATE + , m_frameCountWhilePlaying(0) + , m_timeStartedPlaying(0) + , m_timeStoppedPlaying(0) +#endif +{ +} + +MediaPlayerPrivate::~MediaPlayerPrivate() +{ + tearDownVideoRendering(); + m_qtGWorld->setMovie(0); +} + +bool MediaPlayerPrivate::supportsFullscreen() const +{ + return true; +} + +PlatformMedia MediaPlayerPrivate::platformMedia() const +{ + PlatformMedia p; + p.type = PlatformMedia::QTMovieGWorldType; + p.media.qtMovieGWorld = m_qtGWorld.get(); + return p; +} + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* MediaPlayerPrivate::platformLayer() const +{ + return m_qtVideoLayer ? m_qtVideoLayer->platformLayer() : 0; +} +#endif + +String MediaPlayerPrivate::rfc2616DateStringFromTime(CFAbsoluteTime time) +{ + static const char* const dayStrings[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; + static const char* const monthStrings[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static const CFStringRef dateFormatString = CFSTR("%s, %02d %s %04d %02d:%02d:%02d GMT"); + static CFTimeZoneRef gmtTimeZone; + if (!gmtTimeZone) + gmtTimeZone = CFTimeZoneCopyDefault(); + + CFGregorianDate dateValue = CFAbsoluteTimeGetGregorianDate(time, gmtTimeZone); + if (!CFGregorianDateIsValid(dateValue, kCFGregorianAllUnits)) + return String(); + + time = CFGregorianDateGetAbsoluteTime(dateValue, gmtTimeZone); + SInt32 day = CFAbsoluteTimeGetDayOfWeek(time, 0); + + RetainPtr<CFStringRef> dateCFString(AdoptCF, CFStringCreateWithFormat(0, 0, dateFormatString, dayStrings[day - 1], dateValue.day, + monthStrings[dateValue.month - 1], dateValue.year, dateValue.hour, dateValue.minute, (int)dateValue.second)); + return dateCFString.get(); +} + +static void addCookieParam(StringBuilder& cookieBuilder, const String& name, const String& value) +{ + if (name.isEmpty()) + return; + + // If this isn't the first parameter added, terminate the previous one. + if (cookieBuilder.length()) + cookieBuilder.append("; "); + + // Add parameter name, and value if there is one. + cookieBuilder.append(name); + if (!value.isEmpty()) { + cookieBuilder.append('='); + cookieBuilder.append(value); + } +} + + +void MediaPlayerPrivate::setUpCookiesForQuickTime(const String& url) +{ + // WebCore loaded the page with the movie URL with CFNetwork but QuickTime will + // use WinINet to download the movie, so we need to copy any cookies needed to + // download the movie into WinInet before asking QuickTime to open it. + Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0; + if (!frame || !frame->page() || !frame->page()->cookieEnabled()) + return; + + KURL movieURL = KURL(KURL(), url); + Vector<Cookie> documentCookies; + if (!getRawCookies(frame->document(), movieURL, documentCookies)) + return; + + for (size_t ndx = 0; ndx < documentCookies.size(); ndx++) { + const Cookie& cookie = documentCookies[ndx]; + + if (cookie.name.isEmpty()) + continue; + + // Build up the cookie string with as much information as we can get so WinINet + // knows what to do with it. + StringBuilder cookieBuilder; + addCookieParam(cookieBuilder, cookie.name, cookie.value); + addCookieParam(cookieBuilder, "path", cookie.path); + if (cookie.expires) + addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires)); + if (cookie.httpOnly) + addCookieParam(cookieBuilder, "httpOnly", String()); + cookieBuilder.append(';'); + + String cookieURL; + if (!cookie.domain.isEmpty()) { + StringBuilder urlBuilder; + + urlBuilder.append(movieURL.protocol()); + urlBuilder.append("://"); + if (cookie.domain[0] == '.') + urlBuilder.append(cookie.domain.substring(1)); + else + urlBuilder.append(cookie.domain); + if (cookie.path.length() > 1) + urlBuilder.append(cookie.path); + + cookieURL = urlBuilder.toString(); + } else + cookieURL = movieURL; + + InternetSetCookieExW(cookieURL.charactersWithNullTermination(), 0, cookieBuilder.toString().charactersWithNullTermination(), 0, 0); + } +} + +void MediaPlayerPrivate::load(const String& url) +{ + if (!QTMovie::initializeQuickTime()) { + // FIXME: is this the right error to return? + m_networkState = MediaPlayer::DecodeError; + m_player->networkStateChanged(); + return; + } + + // Initialize the task timer. + MediaPlayerPrivateTaskTimer::initialize(); + + if (m_networkState != MediaPlayer::Loading) { + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + } + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; + m_player->readyStateChanged(); + } + cancelSeek(); + + setUpCookiesForQuickTime(url); + + m_qtMovie = adoptRef(new QTMovie(this)); + m_qtMovie->load(url.characters(), url.length(), m_player->preservesPitch()); + m_qtMovie->setVolume(m_player->volume()); + + m_qtGWorld = adoptRef(new QTMovieGWorld(this)); + m_qtGWorld->setMovie(m_qtMovie.get()); + m_qtGWorld->setVisible(m_player->visible()); +} + +void MediaPlayerPrivate::play() +{ + if (!m_qtMovie) + return; + m_startedPlaying = true; +#if DRAW_FRAME_RATE + m_frameCountWhilePlaying = 0; +#endif + + m_qtMovie->play(); +} + +void MediaPlayerPrivate::pause() +{ + if (!m_qtMovie) + return; + m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = WTF::currentTime(); +#endif + m_qtMovie->pause(); +} + +float MediaPlayerPrivate::duration() const +{ + if (!m_qtMovie) + return 0; + return m_qtMovie->duration(); +} + +float MediaPlayerPrivate::currentTime() const +{ + if (!m_qtMovie) + return 0; + return m_qtMovie->currentTime(); +} + +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(); + if (oldRate) + m_qtMovie->setRate(0); + m_qtMovie->setCurrentTime(m_seekTo); + float timeAfterSeek = currentTime(); + // restore playback only if not at end, othewise QTMovie will loop + if (oldRate && timeAfterSeek < duration()) + 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(); + } + } +} + +bool MediaPlayerPrivate::paused() const +{ + if (!m_qtMovie) + return true; + return (!m_qtMovie->rate()); +} + +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 +{ + if (!m_qtMovie) + return false; + return m_qtMovie->hasVideo(); +} + +bool MediaPlayerPrivate::hasAudio() const +{ + if (!m_qtMovie) + return false; + return m_qtMovie->hasAudio(); +} + +void MediaPlayerPrivate::setVolume(float volume) +{ + if (!m_qtMovie) + return; + m_qtMovie->setVolume(volume); +} + +void MediaPlayerPrivate::setRate(float rate) +{ + if (!m_qtMovie) + return; + m_qtMovie->setRate(rate); +} + +void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch) +{ + if (!m_qtMovie) + return; + m_qtMovie->setPreservesPitch(preservesPitch); +} + +bool MediaPlayerPrivate::hasClosedCaptions() const +{ + if (!m_qtMovie) + return false; + return m_qtMovie->hasClosedCaptions(); +} + +void MediaPlayerPrivate::setClosedCaptionsVisible(bool visible) +{ + if (!m_qtMovie) + return; + m_qtMovie->setClosedCaptionsVisible(visible); +} + +PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const +{ + RefPtr<TimeRanges> timeRanges = TimeRanges::create(); + float loaded = maxTimeLoaded(); + // rtsp streams are not buffered + if (!m_isStreaming && loaded > 0) + timeRanges->add(0, loaded); + return timeRanges.release(); +} + +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; +} + +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; + + tearDownVideoRendering(); + + // 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_readyState < MediaPlayer::HaveMetadata) { + m_qtMovie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount); + if (m_player->inMediaDocument()) { + if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) { + // This is a type of media that we do not handle directly with a <video> + // element, eg. QuickTime VR, a movie with a sprite track, etc. Tell the + // MediaPlayerClient that we won't support it. + sawUnsupportedTracks(); + return; + } + } else if (!m_enabledTrackCount) + loadState = QTMovieLoadStateError; + } + + // "Loaded" is reserved for fully buffered movies, never the case when streaming + if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::HaveEnoughData; + } else if (loadState >= QTMovieLoadStatePlaythroughOK) { + m_readyState = MediaPlayer::HaveEnoughData; + } else if (loadState >= QTMovieLoadStatePlayable) { + // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; + } else if (loadState >= QTMovieLoadStateLoaded) { + m_readyState = MediaPlayer::HaveMetadata; + } else if (loadState > QTMovieLoadStateError) { + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::HaveNothing; + } else { + if (m_player->inMediaDocument()) { + // Something went wrong in the loading of media within a standalone file. + // This can occur with chained ref movies that eventually resolve to a + // file we don't support. + sawUnsupportedTracks(); + return; + } + + float loaded = maxTimeLoaded(); + if (!loaded) + m_readyState = MediaPlayer::HaveNothing; + + if (!m_enabledTrackCount) + m_networkState = MediaPlayer::FormatError; + else { + // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> + if (loaded > 0) + m_networkState = MediaPlayer::DecodeError; + else + m_readyState = MediaPlayer::HaveNothing; + } + } + + if (isReadyForRendering() && !hasSetUpVideoRendering()) + setUpVideoRendering(); + + if (seeking()) + m_readyState = MediaPlayer::HaveNothing; + + if (m_networkState != oldNetworkState) + m_player->networkStateChanged(); + if (m_readyState != oldReadyState) + m_player->readyStateChanged(); +} + +bool MediaPlayerPrivate::isReadyForRendering() const +{ + return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); +} + +void MediaPlayerPrivate::sawUnsupportedTracks() +{ + m_qtMovie->setDisabled(true); + m_hasUnsupportedTracks = true; + m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); +} + +void MediaPlayerPrivate::didEnd() +{ + if (m_hasUnsupportedTracks) + return; + + m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = WTF::currentTime(); +#endif + updateStates(); + m_player->timeChanged(); +} + +void MediaPlayerPrivate::setSize(const IntSize& size) +{ + if (m_hasUnsupportedTracks || !m_qtMovie || m_size == size) + return; + m_size = size; + m_qtGWorld->setSize(size.width(), size.height()); +} + +void MediaPlayerPrivate::setVisible(bool visible) +{ + if (m_hasUnsupportedTracks || !m_qtMovie || m_visible == visible) + return; + + m_qtGWorld->setVisible(visible); + m_visible = visible; + if (m_visible) { + if (isReadyForRendering()) + setUpVideoRendering(); + } else + tearDownVideoRendering(); +} + +void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + return; +#endif + if (p->paintingDisabled() || !m_qtMovie || m_hasUnsupportedTracks) + return; + + bool usingTempBitmap = false; + OwnPtr<GraphicsContext::WindowsBitmap> bitmap; + // FIXME: use LocalWindowsContext. + HDC hdc = p->getWindowsContext(r); + if (!hdc) { + // The graphics context doesn't have an associated HDC so create a temporary + // bitmap where QTMovieGWorld can draw the frame and we can copy it. + usingTempBitmap = true; + bitmap.set(p->createWindowsBitmap(r.size())); + hdc = bitmap->hdc(); + + // FIXME: is this necessary?? + XFORM xform; + xform.eM11 = 1.0f; + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = 1.0f; + xform.eDx = -r.x(); + xform.eDy = -r.y(); + SetWorldTransform(hdc, &xform); + } + + m_qtGWorld->paint(hdc, r.x(), r.y()); + if (usingTempBitmap) + p->drawWindowsBitmap(bitmap.get(), r.topLeft()); + else + p->releaseWindowsContext(hdc, r); + + paintCompleted(*p, r); +} + +void MediaPlayerPrivate::paintCompleted(GraphicsContext& context, const IntRect& rect) +{ + m_newFrameAvailable = false; + +#if DRAW_FRAME_RATE + if (m_frameCountWhilePlaying > 10) { + double interval = m_startedPlaying ? WTF::currentTime() - m_timeStartedPlaying : m_timeStoppedPlaying - m_timeStartedPlaying; + double frameRate = (m_frameCountWhilePlaying - 1) / interval; + CGContextRef cgContext = context.platformContext(); + CGRect drawRect = rect; + + char text[8]; + _snprintf(text, sizeof(text), "%1.2f", frameRate); + + static const int fontSize = 25; + static const int fontCharWidth = 12; + static const int boxHeight = 25; + static const int boxBorderWidth = 4; + drawRect.size.width = boxBorderWidth * 2 + fontCharWidth * strlen(text); + drawRect.size.height = boxHeight; + + CGContextSaveGState(cgContext); +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + CGContextScaleCTM(cgContext, 1, -1); + CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, m_qtVideoLayer ? -rect.height() : 0); +#else + CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, 0); +#endif + static const CGFloat backgroundColor[4] = { 0.98, 0.98, 0.82, 0.8 }; + CGContextSetFillColor(cgContext, backgroundColor); + CGContextFillRect(cgContext, drawRect); + + static const CGFloat textColor[4] = { 0, 0, 0, 1 }; + CGContextSetFillColor(cgContext, textColor); + CGContextSetTextMatrix(cgContext, CGAffineTransformMakeScale(1, -1)); + CGContextSelectFont(cgContext, "Helvetica", fontSize, kCGEncodingMacRoman); + + CGContextShowTextAtPoint(cgContext, drawRect.origin.x + boxBorderWidth, drawRect.origin.y + boxHeight - boxBorderWidth, text, strlen(text)); + + CGContextRestoreGState(cgContext); + } +#endif +} + +static HashSet<String> mimeTypeCache() +{ + DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ()); + static bool typeListInitialized = false; + + if (!typeListInitialized) { + unsigned count = QTMovie::countSupportedTypes(); + for (unsigned n = 0; n < count; n++) { + const UChar* character; + unsigned len; + QTMovie::getSupportedType(n, character, len); + if (len) + typeCache.add(String(character, len)); + } + + typeListInitialized = true; + } + + return typeCache; +} + +void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +{ + types = mimeTypeCache(); +} + +bool MediaPlayerPrivate::isAvailable() +{ + return QTMovie::initializeQuickTime(); +} + +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an + // extended MIME type + return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; +} + +void MediaPlayerPrivate::movieEnded(QTMovie* movie) +{ + if (m_hasUnsupportedTracks) + return; + + ASSERT(m_qtMovie.get() == movie); + didEnd(); +} + +void MediaPlayerPrivate::movieLoadStateChanged(QTMovie* movie) +{ + if (m_hasUnsupportedTracks) + return; + + ASSERT(m_qtMovie.get() == movie); + updateStates(); +} + +void MediaPlayerPrivate::movieTimeChanged(QTMovie* movie) +{ + if (m_hasUnsupportedTracks) + return; + + ASSERT(m_qtMovie.get() == movie); + updateStates(); + m_player->timeChanged(); +} + +void MediaPlayerPrivate::movieNewImageAvailable(QTMovieGWorld* movie) +{ + if (m_hasUnsupportedTracks) + return; + + ASSERT(m_qtGWorld.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 (m_frameCountWhilePlaying == 1) + m_timeStartedPlaying = WTF::currentTime(); + } +#endif + + m_newFrameAvailable = true; + +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + m_qtVideoLayer->setNeedsDisplay(); + else +#endif + m_player->repaint(); +} + +bool MediaPlayerPrivate::hasSingleSecurityOrigin() const +{ + // We tell quicktime to disallow resources that come from different origins + // so we all media is single origin. + return true; +} + +MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode() const +{ + if (!m_qtMovie) + return MediaRenderingNone; + +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + return MediaRenderingMovieLayer; +#endif + + return MediaRenderingSoftwareRenderer; +} + +MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMode() const +{ + if (!m_player->frameView() || !m_qtMovie) + return MediaRenderingNone; + +#if USE(ACCELERATED_COMPOSITING) + if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) + return MediaRenderingMovieLayer; +#endif + + return MediaRenderingSoftwareRenderer; +} + +void MediaPlayerPrivate::setUpVideoRendering() +{ + MediaRenderingMode currentMode = currentRenderingMode(); + MediaRenderingMode preferredMode = preferredRenderingMode(); + +#if !USE(ACCELERATED_COMPOSITING) + ASSERT(preferredMode != MediaRenderingMovieLayer); +#endif + + if (currentMode == preferredMode && currentMode != MediaRenderingNone) + return; + + if (currentMode != MediaRenderingNone) + tearDownVideoRendering(); + + if (preferredMode == MediaRenderingMovieLayer) + createLayerForMovie(); + +#if USE(ACCELERATED_COMPOSITING) + if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer) + m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); +#endif +} + +void MediaPlayerPrivate::tearDownVideoRendering() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + destroyLayerForMovie(); +#endif +} + +bool MediaPlayerPrivate::hasSetUpVideoRendering() const +{ +#if USE(ACCELERATED_COMPOSITING) + return m_qtVideoLayer || currentRenderingMode() != MediaRenderingMovieLayer; +#else + return true; +#endif +} + +#if USE(ACCELERATED_COMPOSITING) + +// Up-call from compositing layer drawing callback. +void MediaPlayerPrivate::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase, const IntRect&) +{ + if (m_hasUnsupportedTracks) + return; + + ASSERT(supportsAcceleratedRendering()); + + // No reason to replace the current layer image unless we have something new to show. + if (!m_newFrameAvailable) + return; + + static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + void* buffer; + unsigned bitsPerPixel; + unsigned rowBytes; + unsigned width; + unsigned height; + + m_qtGWorld->getCurrentFrameInfo(buffer, bitsPerPixel, rowBytes, width, height); + if (!buffer) + return; + + RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(0, static_cast<UInt8*>(buffer), rowBytes * height, kCFAllocatorNull)); + RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData(data.get())); + RetainPtr<CGImageRef> frameImage(AdoptCF, CGImageCreate(width, height, 8, bitsPerPixel, rowBytes, colorSpace, + kCGBitmapByteOrder32Little | kCGImageAlphaFirst, provider.get(), 0, false, kCGRenderingIntentDefault)); + if (!frameImage) + return; + + IntRect rect(0, 0, m_size.width(), m_size.height()); + CGContextDrawImage(context.platformContext(), rect, frameImage.get()); + paintCompleted(context, rect); +} +#endif + +void MediaPlayerPrivate::createLayerForMovie() +{ +#if USE(ACCELERATED_COMPOSITING) + ASSERT(supportsAcceleratedRendering()); + + if (!m_qtMovie || m_qtVideoLayer) + return; + + // Create a GraphicsLayer that won't be inserted directly into the render tree, but will used + // as a wrapper for a PlatformCALayer which gets inserted as the content layer of the video + // renderer's GraphicsLayer. + m_qtVideoLayer.set(new GraphicsLayerCACF(this)); + if (!m_qtVideoLayer) + return; + + // Mark the layer as drawing itself, anchored in the top left, and bottom-up. + m_qtVideoLayer->setDrawsContent(true); + m_qtVideoLayer->setAnchorPoint(FloatPoint3D()); + m_qtVideoLayer->setContentsOrientation(GraphicsLayer::CompositingCoordinatesBottomUp); +#ifndef NDEBUG + m_qtVideoLayer->setName("Video layer"); +#endif + // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration(). +#endif +} + +void MediaPlayerPrivate::destroyLayerForMovie() +{ +#if USE(ACCELERATED_COMPOSITING) + if (!m_qtVideoLayer) + return; + m_qtVideoLayer = 0; +#endif +} + +#if USE(ACCELERATED_COMPOSITING) +bool MediaPlayerPrivate::supportsAcceleratedRendering() const +{ + return isReadyForRendering(); +} + +void MediaPlayerPrivate::acceleratedRenderingStateChanged() +{ + // Set up or change the rendering path if necessary. + setUpVideoRendering(); +} + +#endif + + +} + +#endif diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h new file mode 100644 index 0000000..ab9b1f0 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 MediaPlayerPrivateQuickTimeWin_h +#define MediaPlayerPrivateQuickTimeWin_h + +#if ENABLE(VIDEO) + +#include "MediaPlayerPrivate.h" +#include "Timer.h" +#include <QTMovie.h> +#include <QTMovieGWorld.h> +#include <wtf/Forward.h> +#include <wtf/OwnPtr.h> +#include <wtf/RetainPtr.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayerClient.h" +#endif + +#ifndef DRAW_FRAME_RATE +#define DRAW_FRAME_RATE 0 +#endif + +typedef struct CGImage *CGImageRef; + +namespace WebCore { + +class GraphicsContext; +class IntSize; +class IntRect; + +class MediaPlayerPrivate : public MediaPlayerPrivateInterface, public QTMovieClient, public QTMovieGWorldClient +#if USE(ACCELERATED_COMPOSITING) + , public GraphicsLayerClient +#endif +{ +public: + static void registerMediaEngine(MediaEngineRegistrar); + + ~MediaPlayerPrivate(); + +private: + +#if USE(ACCELERATED_COMPOSITING) + // GraphicsLayerClient methods + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip); + virtual void notifyAnimationStarted(const GraphicsLayer*, double time) { } + virtual void notifySyncRequired(const GraphicsLayer*) { } + virtual bool showDebugBorders() const { return false; } + virtual bool showRepaintCounter() const { return false; } +#endif + + MediaPlayerPrivate(MediaPlayer*); + + virtual bool supportsFullscreen() const; + virtual PlatformMedia platformMedia() const; +#if USE(ACCELERATED_COMPOSITING) + PlatformLayer* platformLayer() const; +#endif + + IntSize naturalSize() const; + bool hasVideo() const; + bool hasAudio() 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 setRate(float); + void setVolume(float); + void setPreservesPitch(bool); + + MediaPlayer::NetworkState networkState() const { return m_networkState; } + MediaPlayer::ReadyState readyState() const { return m_readyState; } + + PassRefPtr<TimeRanges> buffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + unsigned totalBytes() const; + + void setVisible(bool); + void setSize(const IntSize&); + + void loadStateChanged(); + void didEnd(); + + void paint(GraphicsContext*, const IntRect&); + void paintCompleted(GraphicsContext&, const IntRect&); + + bool hasSingleSecurityOrigin() const; + + bool hasClosedCaptions() const; + void setClosedCaptionsVisible(bool); + + void updateStates(); + void doSeek(); + void cancelSeek(); + void seekTimerFired(Timer<MediaPlayerPrivate>*); + float maxTimeLoaded() const; + void sawUnsupportedTracks(); + + virtual void movieEnded(QTMovie*); + virtual void movieLoadStateChanged(QTMovie*); + virtual void movieTimeChanged(QTMovie*); + virtual void movieNewImageAvailable(QTMovieGWorld*); + + // engine support + static MediaPlayerPrivateInterface* create(MediaPlayer*); + static void getSupportedTypes(HashSet<String>& types); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + +#if USE(ACCELERATED_COMPOSITING) + virtual bool supportsAcceleratedRendering() const; + virtual void acceleratedRenderingStateChanged(); +#endif + + enum MediaRenderingMode { MediaRenderingNone, MediaRenderingSoftwareRenderer, MediaRenderingMovieLayer }; + MediaRenderingMode currentRenderingMode() const; + MediaRenderingMode preferredRenderingMode() const; + bool isReadyForRendering() const; + + void setUpVideoRendering(); + void tearDownVideoRendering(); + bool hasSetUpVideoRendering() const; + + void createLayerForMovie(); + void destroyLayerForMovie(); + + void setUpCookiesForQuickTime(const String& url); + String rfc2616DateStringFromTime(CFAbsoluteTime); + + MediaPlayer* m_player; + RefPtr<QTMovie> m_qtMovie; + RefPtr<QTMovieGWorld> m_qtGWorld; +#if USE(ACCELERATED_COMPOSITING) + OwnPtr<GraphicsLayer> m_qtVideoLayer; +#endif + float m_seekTo; + Timer<MediaPlayerPrivate> m_seekTimer; + IntSize m_size; + MediaPlayer::NetworkState m_networkState; + MediaPlayer::ReadyState m_readyState; + unsigned m_enabledTrackCount; + unsigned m_totalTrackCount; + bool m_hasUnsupportedTracks; + bool m_startedPlaying; + bool m_isStreaming; + bool m_visible; + bool m_newFrameAvailable; +#if DRAW_FRAME_RATE + double m_frameCountWhilePlaying; + double m_timeStartedPlaying; + double m_timeStoppedPlaying; +#endif +}; + +} + +#endif +#endif diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateTaskTimer.cpp b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateTaskTimer.cpp new file mode 100644 index 0000000..770e73e --- /dev/null +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateTaskTimer.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 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 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 "MediaPlayerPrivateTaskTimer.h" + +#include "QTMovieTask.h" + +namespace WebCore { + +MediaPlayerPrivateTaskTimer* MediaPlayerPrivateTaskTimer::s_timer = 0; + +void MediaPlayerPrivateTaskTimer::initialize() +{ + if (s_timer) + return; + + s_timer = new MediaPlayerPrivateTaskTimer; + + QTMovieTask::sharedTask()->setTaskTimerFuncs(setDelay, stopTaskTimer); +} + +void MediaPlayerPrivateTaskTimer::setDelay(double delayInSeconds) +{ + ASSERT(s_timer); + + s_timer->startOneShot(delayInSeconds); +} + +void MediaPlayerPrivateTaskTimer::stopTaskTimer() +{ + ASSERT(s_timer); + + s_timer->stop(); +} + +void MediaPlayerPrivateTaskTimer::fired() +{ + QTMovieTask::sharedTask()->fireTaskClients(); +} + +} diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateTaskTimer.h b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateTaskTimer.h new file mode 100644 index 0000000..10f861e --- /dev/null +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateTaskTimer.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 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 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 MediaPlayerPrivateTaskTimer_h +#define MediaPlayerPrivateTaskTimer_h + +#include "Timer.h" + +namespace WebCore { + +class MediaPlayerPrivateTaskTimer : TimerBase { +public: + static void initialize(); + +private: + static void setDelay(double); + static void stopTaskTimer(); + + void fired(); + + static MediaPlayerPrivateTaskTimer* s_timer; +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/win/QTCFDictionary.cpp b/Source/WebCore/platform/graphics/win/QTCFDictionary.cpp new file mode 100644 index 0000000..3c72792 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTCFDictionary.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "QTCFDictionary.h" + +#include <CFData.h> +#include <windows.h> + +CFDataRef QTCFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList) +{ + + typedef CFDataRef (* pfnCFPropertyListCreateXMLData)(CFAllocatorRef allocator, CFPropertyListRef propertyList); + static pfnCFPropertyListCreateXMLData pCFPropertyListCreateXMLData = 0; + if (!pCFPropertyListCreateXMLData) { + HMODULE qtcfDLL = LoadLibraryW(L"QTCF.dll"); + if (qtcfDLL) + pCFPropertyListCreateXMLData = reinterpret_cast<pfnCFPropertyListCreateXMLData>(GetProcAddress(qtcfDLL, "QTCF_CFPropertyListCreateXMLData")); + } + + if (pCFPropertyListCreateXMLData) + return pCFPropertyListCreateXMLData(allocator, propertyList); + return 0; +} + +CFDictionaryRef QTCFDictionaryCreateCopyWithDataCallback(CFAllocatorRef allocator, CFDictionaryRef dictionary, QTCFDictonaryCreateFromDataCallback callback) +{ + ASSERT(dictionary); + ASSERT(callback); + + CFDataRef data = QTCFPropertyListCreateXMLData(kCFAllocatorDefault, dictionary); + if (!data) + return 0; + CFDictionaryRef outputDictionary = callback(allocator, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); + + return outputDictionary; +} diff --git a/Source/WebCore/platform/graphics/win/QTCFDictionary.h b/Source/WebCore/platform/graphics/win/QTCFDictionary.h new file mode 100644 index 0000000..ff34328 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTCFDictionary.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 QTCFDictionary_h +#define QTCFDictionary_h + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +#include <CoreFoundation/CFBase.h> + +typedef const struct __CFDictionary * CFDictionaryRef; +typedef const struct __CFAllocator * CFAllocatorRef; + +typedef CFDictionaryRef (* QTCFDictonaryCreateFromDataCallback)(CFAllocatorRef, const UInt8*, CFIndex); + +QTMOVIEWIN_API CFDictionaryRef QTCFDictionaryCreateCopyWithDataCallback(CFAllocatorRef, CFDictionaryRef, QTCFDictonaryCreateFromDataCallback); + +#endif diff --git a/Source/WebCore/platform/graphics/win/QTDecompressionSession.cpp b/Source/WebCore/platform/graphics/win/QTDecompressionSession.cpp new file mode 100644 index 0000000..eeb3ca7 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTDecompressionSession.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2010 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 "QTDecompressionSession.h" + +#include <ImageCompression.h> +#include <algorithm> + +class QTDecompressionSessionClient { +public: + static void trackingCallback(void *decompressionTrackingRefCon, OSStatus, + ICMDecompressionTrackingFlags decompressionTrackingFlags, CVPixelBufferRef pixelBuffer, + TimeValue64, TimeValue64, ICMValidTimeFlags, void *, void *) + { + QTDecompressionSession* session = static_cast<QTDecompressionSession*>(decompressionTrackingRefCon); + ASSERT(session); + + if (decompressionTrackingFlags & kICMDecompressionTracking_FrameDecoded) + session->m_latestFrame = QTPixelBuffer(pixelBuffer); + } +}; + +PassOwnPtr<QTDecompressionSession> QTDecompressionSession::create(unsigned long pixelFormat, size_t width, size_t height) +{ + return adoptPtr(new QTDecompressionSession(pixelFormat, width, height)); +} + +QTDecompressionSession::QTDecompressionSession(unsigned long pixelFormat, size_t width, size_t height) + : m_session(0) + , m_pixelFormat(pixelFormat) + , m_width(width) + , m_height(height) +{ + initializeSession(); +} + +QTDecompressionSession::~QTDecompressionSession() +{ + if (m_session) + ICMDecompressionSessionRelease(m_session); +} + +void QTDecompressionSession::initializeSession() +{ + if (m_session) + return; + + ICMPixelFormatInfo pixelFormatInfo = {sizeof(ICMPixelFormatInfo), 0}; + if (ICMGetPixelFormatInfo(m_pixelFormat, &pixelFormatInfo) != noErr) { + // The ICM does not know anything about the pixelFormat contained in + // the pixel buffer, so it won't be able to convert it to RGBA. + return; + } + + // The depth and cType fields of the ImageDescriptionHandle are filled + // out according to the instructions in Technical Q&A QA1183: + // http://developer.apple.com/library/mac/#qa/qa2001/qa1183.html + bool isIndexed = pixelFormatInfo.formatFlags & kICMPixelFormatIsIndexed; + bool isQD = pixelFormatInfo.formatFlags & kICMPixelFormatIsSupportedByQD; + bool isMonochrome = pixelFormatInfo.formatFlags & kICMPixelFormatIsMonochrome; + bool hasAlpha = pixelFormatInfo.formatFlags & kICMPixelFormatHasAlphaChannel; + + unsigned int depth = 24; // The default depth is 24. + if (hasAlpha) + depth = 32; // Any pixel format with alpha gets a depth of 32. + else if (isMonochrome) { + // Grayscale pixel formats get depths 33 through 40, depending + // on their bits per pixel. Yes, this means that 16-bit grayscale + // and 8-bit grayscale have the same pixel depth. + depth = 32 + std::min<unsigned int>(8, pixelFormatInfo.bitsPerPixel[0]); + } else if (isIndexed) { + // Indexed pixel formats get a depth of 1 through 8, depending on + // the their bits per pixel. + depth = pixelFormatInfo.bitsPerPixel[0]; + } + + // If QuickDraw supports the given pixel format, the cType should be kRawCodecType. + // Otherwise, use the pixel format code for the cType. We are assuming the pixel + // buffer is uncompressed. + unsigned long cType = isQD ? kRawCodecType : m_pixelFormat; + + ImageDescriptionHandle description = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription)); + (**description).idSize = sizeof(ImageDescription); + (**description).cType = cType; + (**description).version = 2; + (**description).spatialQuality = codecLosslessQuality; + (**description).width = m_width; + (**description).height = m_height; + (**description).hRes = 72 << 16; // 72 DPI as a fixed-point number + (**description).vRes = 72 << 16; // 72 DPI as a fixed-point number + (**description).frameCount = 1; + (**description).depth = depth; + (**description).clutID = -1; + + // Create the mandatory ICMDecompressionSessionOptions, but leave + // all the default values. + ICMDecompressionSessionOptionsRef options = 0; + ICMDecompressionSessionOptionsCreate(kCFAllocatorDefault, &options); + + CFDictionaryRef pixelBufferAttributes = QTPixelBuffer::createPixelBufferAttributesDictionary(QTPixelBuffer::ConfigureForCGImage); + + ICMDecompressionTrackingCallbackRecord callback = { + QTDecompressionSessionClient::trackingCallback, + this, + }; + + ICMDecompressionSessionCreate(kCFAllocatorDefault, + description, + options, + pixelBufferAttributes, + &callback, + &m_session); + + if (pixelBufferAttributes) + CFRelease(pixelBufferAttributes); + + ICMDecompressionSessionOptionsRelease(options); + DisposeHandle((Handle)description); +} + +bool QTDecompressionSession::canDecompress(QTPixelBuffer inBuffer) +{ + return m_session + && inBuffer.pixelFormatType() == m_pixelFormat + && inBuffer.width() == m_width + && inBuffer.height() == m_height; +} + +QTPixelBuffer QTDecompressionSession::decompress(QTPixelBuffer inBuffer) +{ + if (!canDecompress(inBuffer)) + return QTPixelBuffer(); + + inBuffer.lockBaseAddress(); + ICMDecompressionSessionDecodeFrame(m_session, + static_cast<UInt8*>(inBuffer.baseAddress()), + inBuffer.dataSize(), + 0, // frameOptions + 0, // frameTime + 0); // sourceFrameRefCon + + // Because we passed in 0 for frameTime, the above function + // is synchronous, and the client callback will have been + // called before the function returns, and m_latestFrame + // will contain the newly decompressed frame. + return m_latestFrame; +} diff --git a/Source/WebCore/platform/graphics/win/QTDecompressionSession.h b/Source/WebCore/platform/graphics/win/QTDecompressionSession.h new file mode 100644 index 0000000..67b6635 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTDecompressionSession.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 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 QTDecompressionSession_h +#define QTDecompressionSession_h + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +#include "QTPixelBuffer.h" + +#include <WTF/PassOwnPtr.h> + +class QTDecompressionSessionClient; +typedef struct OpaqueICMDecompressionSession* ICMDecompressionSessionRef; + +class QTMOVIEWIN_API QTDecompressionSession { +public: + static PassOwnPtr<QTDecompressionSession> create(unsigned long pixelFormat, size_t width, size_t height); + ~QTDecompressionSession(); + + bool canDecompress(QTPixelBuffer); + + // The resulting QTPixelBuffer will be a CG compatable ARGB pixel buffer. + QTPixelBuffer decompress(QTPixelBuffer); + +private: + friend class QTDecompressionSessionClient; + QTDecompressionSession(unsigned long pixelFormat, size_t width, size_t height); + void initializeSession(); + + unsigned long m_pixelFormat; + size_t m_width; + size_t m_height; + QTPixelBuffer m_latestFrame; + ICMDecompressionSessionRef m_session; +}; + +#endif diff --git a/Source/WebCore/platform/graphics/win/QTMovie.cpp b/Source/WebCore/platform/graphics/win/QTMovie.cpp new file mode 100644 index 0000000..efaf218 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovie.cpp @@ -0,0 +1,942 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "QTMovie.h" + +#include "QTMovieTask.h" +#include "QTMovieWinTimer.h" +#include <FixMath.h> +#include <GXMath.h> +#include <Movies.h> +#include <QTML.h> +#include <QuickTimeComponents.h> +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +using namespace std; + +static const long minimumQuickTimeVersion = 0x07300000; // 7.3 + +static const long closedCaptionTrackType = 'clcp'; +static const long subTitleTrackType = 'sbtl'; +static const long mpeg4ObjectDescriptionTrackType = 'odsm'; +static const long mpeg4SceneDescriptionTrackType = 'sdsm'; +static const long closedCaptionDisplayPropertyID = 'disp'; +static LPCTSTR fullscreenQTMoviePointerProp = TEXT("fullscreenQTMoviePointer"); + +// 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 Vector<CFStringRef>* gSupportedTypes = 0; +static SInt32 quickTimeVersion = 0; + +class QTMoviePrivate : public Noncopyable, public QTMovieTaskClient { +public: + QTMoviePrivate(); + ~QTMoviePrivate(); + void task(); + void startTask(); + void endTask(); + + void createMovieController(); + void cacheMovieScale(); + + QTMovie* m_movieWin; + Movie m_movie; + MovieController m_movieController; + bool m_tasking; + bool m_disabled; + Vector<QTMovieClient*> m_clients; + long m_loadState; + bool m_ended; + bool m_seeking; + float m_lastMediaTime; + double m_lastLoadStateCheckTime; + int m_width; + int m_height; + bool m_visible; + long m_loadError; + float m_widthScaleFactor; + float m_heightScaleFactor; + CFURLRef m_currentURL; + float m_timeToRestore; + float m_rateToRestore; +#if !ASSERT_DISABLED + bool m_scaleCached; +#endif +}; + +QTMoviePrivate::QTMoviePrivate() + : m_movieWin(0) + , m_movie(0) + , m_movieController(0) + , m_tasking(false) + , 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_loadError(0) + , m_widthScaleFactor(1) + , m_heightScaleFactor(1) + , m_currentURL(0) + , m_timeToRestore(-1.0f) + , m_rateToRestore(-1.0f) + , m_disabled(false) +#if !ASSERT_DISABLED + , m_scaleCached(false) +#endif +{ +} + +QTMoviePrivate::~QTMoviePrivate() +{ + endTask(); + if (m_movieController) + DisposeMovieController(m_movieController); + if (m_movie) + DisposeMovie(m_movie); + if (m_currentURL) + CFRelease(m_currentURL); +} + +void QTMoviePrivate::startTask() +{ + if (!m_tasking) { + QTMovieTask::sharedTask()->addTaskClient(this); + m_tasking = true; + } + QTMovieTask::sharedTask()->updateTaskTimer(); +} + +void QTMoviePrivate::endTask() +{ + if (m_tasking) { + QTMovieTask::sharedTask()->removeTaskClient(this); + m_tasking = false; + } + QTMovieTask::sharedTask()->updateTaskTimer(); +} + +void QTMoviePrivate::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 QTMovieLoadStateComplete. + // This is different from QTKit API and seems strange. + long loadState = m_loadError ? QTMovieLoadStateError : 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 + bool shouldRestorePlaybackState = false; + bool movieNewlyPlayable = loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded; + m_loadState = loadState; + if (movieNewlyPlayable) { + cacheMovieScale(); + shouldRestorePlaybackState = true; + } + + if (!m_movieController && m_loadState >= QTMovieLoadStateLoaded) + createMovieController(); + + for (size_t i = 0; i < m_clients.size(); ++i) + m_clients[i]->movieLoadStateChanged(m_movieWin); + + if (shouldRestorePlaybackState && m_timeToRestore != -1.0f) { + m_movieWin->setCurrentTime(m_timeToRestore); + m_timeToRestore = -1.0f; + m_movieWin->setRate(m_rateToRestore); + m_rateToRestore = -1.0f; + } + + if (m_disabled) { + endTask(); + return; + } + } + m_lastLoadStateCheckTime = systemTime(); + } + + bool ended = !!IsMovieDone(m_movie); + if (ended != m_ended) { + m_ended = ended; + if (ended) { + for (size_t i = 0; i < m_clients.size(); ++i) + m_clients[i]->movieEnded(m_movieWin); + } + } + + float time = m_movieWin->currentTime(); + if (time < m_lastMediaTime || time >= m_lastMediaTime + cNonContinuousTimeChange || m_seeking) { + m_seeking = false; + for (size_t i = 0; i < m_clients.size(); ++i) + m_clients[i]->movieTimeChanged(m_movieWin); + } + m_lastMediaTime = time; + + if (m_loadError) + endTask(); + else + QTMovieTask::sharedTask()->updateTaskTimer(); +} + +void QTMoviePrivate::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; + + // Disable automatic looping. + MCDoAction(m_movieController, mcActionSetLooping, 0); +} + +void QTMoviePrivate::cacheMovieScale() +{ + Rect naturalRect; + Rect initialRect; + + GetMovieNaturalBoundsRect(m_movie, &naturalRect); + GetMovieBox(m_movie, &initialRect); + + float naturalWidth = naturalRect.right - naturalRect.left; + float naturalHeight = naturalRect.bottom - naturalRect.top; + + if (naturalWidth) + m_widthScaleFactor = (initialRect.right - initialRect.left) / naturalWidth; + if (naturalHeight) + m_heightScaleFactor = (initialRect.bottom - initialRect.top) / naturalHeight; +#if !ASSERT_DISABLED + m_scaleCached = true; +#endif +} + +QTMovie::QTMovie(QTMovieClient* client) + : m_private(new QTMoviePrivate()) +{ + m_private->m_movieWin = this; + if (client) + m_private->m_clients.append(client); + initializeQuickTime(); +} + +QTMovie::~QTMovie() +{ + delete m_private; +} + +void QTMovie::disableComponent(uint32_t cd[5]) +{ + ComponentDescription nullDesc = {'null', 'base', kAppleManufacturer, 0, 0}; + Component nullComp = FindNextComponent(0, &nullDesc); + Component disabledComp = 0; + + while (disabledComp = FindNextComponent(disabledComp, (ComponentDescription*)&cd[0])) + CaptureComponent(disabledComp, nullComp); +} + +void QTMovie::addClient(QTMovieClient* client) +{ + if (client) + m_private->m_clients.append(client); +} + +void QTMovie::removeClient(QTMovieClient* client) +{ + size_t indexOfClient = m_private->m_clients.find(client); + if (indexOfClient != notFound) + m_private->m_clients.remove(indexOfClient); +} + +void QTMovie::play() +{ + m_private->m_timeToRestore = -1.0f; + + 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 QTMovie::pause() +{ + m_private->m_timeToRestore = -1.0f; + + if (m_private->m_movieController) + MCDoAction(m_private->m_movieController, mcActionPlay, 0); + else + StopMovie(m_private->m_movie); + QTMovieTask::sharedTask()->updateTaskTimer(); +} + +float QTMovie::rate() const +{ + if (!m_private->m_movie) + return 0; + return FixedToFloat(GetMovieRate(m_private->m_movie)); +} + +void QTMovie::setRate(float rate) +{ + if (!m_private->m_movie) + return; + m_private->m_timeToRestore = -1.0f; + + if (m_private->m_movieController) + MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate)); + else + SetMovieRate(m_private->m_movie, FloatToFixed(rate)); + QTMovieTask::sharedTask()->updateTaskTimer(); +} + +float QTMovie::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 QTMovie::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 QTMovie::setCurrentTime(float time) const +{ + if (!m_private->m_movie) + return; + + m_private->m_timeToRestore = -1.0f; + + 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)); + QTMovieTask::sharedTask()->updateTaskTimer(); +} + +void QTMovie::setVolume(float volume) +{ + if (!m_private->m_movie) + return; + SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256)); +} + +void QTMovie::setPreservesPitch(bool preservesPitch) +{ + if (!m_private->m_movie || !m_private->m_currentURL) + return; + + OSErr error; + bool prop = false; + + error = QTGetMovieProperty(m_private->m_movie, kQTPropertyClass_Audio, kQTAudioPropertyID_RateChangesPreservePitch, + sizeof(kQTAudioPropertyID_RateChangesPreservePitch), static_cast<QTPropertyValuePtr>(&prop), 0); + + if (error || prop == preservesPitch) + return; + + m_private->m_timeToRestore = currentTime(); + m_private->m_rateToRestore = rate(); + load(m_private->m_currentURL, preservesPitch); +} + +unsigned QTMovie::dataSize() const +{ + if (!m_private->m_movie) + return 0; + return GetMovieDataSize(m_private->m_movie, 0, GetMovieDuration(m_private->m_movie)); +} + +float QTMovie::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 QTMovie::loadState() const +{ + return m_private->m_loadState; +} + +void QTMovie::getNaturalSize(int& width, int& height) +{ + Rect rect = { 0, }; + + if (m_private->m_movie) + GetMovieNaturalBoundsRect(m_private->m_movie, &rect); + width = (rect.right - rect.left) * m_private->m_widthScaleFactor; + height = (rect.bottom - rect.top) * m_private->m_heightScaleFactor; +} + +void QTMovie::load(const UChar* url, int len, bool preservesPitch) +{ + CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len); + CFURLRef cfURL = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, 0); + + load(cfURL, preservesPitch); + + CFRelease(cfURL); + CFRelease(urlStringRef); +} + +void QTMovie::load(CFURLRef url, bool preservesPitch) +{ + if (!url) + return; + + if (m_private->m_movie) { + m_private->endTask(); + 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; + m_private->m_loadState = 0; + } + + // Define a property array for NewMovieFromProperties. 8 should be enough for our needs. + QTNewMoviePropertyElement movieProps[8]; + ItemCount moviePropCount = 0; + + bool boolTrue = true; + + // Disable streaming support for now. + CFStringRef scheme = CFURLCopyScheme(url); + bool isRTSP = CFStringHasPrefix(scheme, CFSTR("rtsp:")); + CFRelease(scheme); + + if (isRTSP) { + m_private->m_loadError = noMovieFound; + goto end; + } + + if (m_private->m_currentURL) { + if (m_private->m_currentURL != url) { + CFRelease(m_private->m_currentURL); + m_private->m_currentURL = url; + CFRetain(url); + } + } else { + m_private->m_currentURL = url; + CFRetain(url); + } + + // Add the movie data location to the property array + movieProps[moviePropCount].propClass = kQTPropertyClass_DataLocation; + movieProps[moviePropCount].propID = kQTDataLocationPropertyID_CFURL; + movieProps[moviePropCount].propValueSize = sizeof(m_private->m_currentURL); + movieProps[moviePropCount].propValueAddress = &(m_private->m_currentURL); + 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++; + + movieProps[moviePropCount].propClass = kQTPropertyClass_Audio; + movieProps[moviePropCount].propID = kQTAudioPropertyID_RateChangesPreservePitch; + movieProps[moviePropCount].propValueSize = sizeof(preservesPitch); + movieProps[moviePropCount].propValueAddress = &preservesPitch; + movieProps[moviePropCount].propStatus = 0; + moviePropCount++; + + ASSERT(moviePropCount <= WTF_ARRAY_LENGTH(movieProps)); + m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, 0, &m_private->m_movie); + +end: + m_private->startTask(); + // get the load fail callback quickly + if (m_private->m_loadError) + QTMovieTask::sharedTask()->updateTaskTimer(0); + else { + OSType mode = kQTApertureMode_CleanAperture; + + // Set the aperture mode property on a movie to signal that we want aspect ratio + // and clean aperture dimensions. Don't worry about errors, we can't do anything if + // the installed version of QT doesn't support it and it isn't serious enough to + // warrant failing. + QTSetMovieProperty(m_private->m_movie, kQTPropertyClass_Visual, kQTVisualPropertyID_ApertureMode, sizeof(mode), &mode); + } +} + +void QTMovie::disableUnsupportedTracks(unsigned& enabledTrackCount, unsigned& totalTrackCount) +{ + if (!m_private->m_movie) { + totalTrackCount = 0; + 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(closedCaptionTrackType); + allowedTrackTypes->add(subTitleTrackType); + allowedTrackTypes->add(mpeg4ObjectDescriptionTrackType); + allowedTrackTypes->add(mpeg4SceneDescriptionTrackType); + allowedTrackTypes->add(TimeCodeMediaType); + allowedTrackTypes->add(TimeCode64MediaType); + } + + long trackCount = GetMovieTrackCount(m_private->m_movie); + enabledTrackCount = trackCount; + totalTrackCount = 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)) { + + // Different mpeg variants import as different track types so check for the "mpeg + // characteristic" instead of hard coding the (current) list of mpeg media types. + if (GetMovieIndTrackType(m_private->m_movie, 1, 'mpeg', movieTrackCharacteristic | movieTrackEnabledOnly)) + continue; + + 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; + } + } +} + +bool QTMovie::isDisabled() const +{ + return m_private->m_disabled; +} + +void QTMovie::setDisabled(bool b) +{ + m_private->m_disabled = b; +} + + +bool QTMovie::hasVideo() const +{ + if (!m_private->m_movie) + return false; + return (GetMovieIndTrackType(m_private->m_movie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly)); +} + +bool QTMovie::hasAudio() const +{ + if (!m_private->m_movie) + return false; + return (GetMovieIndTrackType(m_private->m_movie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly)); +} + +QTTrackArray QTMovie::videoTracks() const +{ + QTTrackArray tracks; + long trackIndex = 1; + + while (Track theTrack = GetMovieIndTrackType(m_private->m_movie, trackIndex++, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly)) + tracks.append(QTTrack::create(theTrack)); + + return tracks; +} + +bool QTMovie::hasClosedCaptions() const +{ + if (!m_private->m_movie) + return false; + return GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType); +} + +void QTMovie::setClosedCaptionsVisible(bool visible) +{ + if (!m_private->m_movie) + return; + + Track ccTrack = GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType); + if (!ccTrack) + return; + + Boolean doDisplay = visible; + QTSetTrackProperty(ccTrack, closedCaptionTrackType, closedCaptionDisplayPropertyID, sizeof(doDisplay), &doDisplay); +} + +long QTMovie::timeScale() const +{ + if (!m_private->m_movie) + return 0; + + return GetMovieTimeScale(m_private->m_movie); +} + +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 = 0; + 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, 0); + 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(0, 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 QTMovie::countSupportedTypes() +{ + initializeSupportedTypes(); + return static_cast<unsigned>(gSupportedTypes->size()); +} + +void QTMovie::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); + +} + +CGAffineTransform QTMovie::getTransform() const +{ + ASSERT(m_private->m_movie); + MatrixRecord m = {0}; + GetMovieMatrix(m_private->m_movie, &m); + + ASSERT(!m.matrix[0][2]); + ASSERT(!m.matrix[1][2]); + CGAffineTransform transform = CGAffineTransformMake( + Fix2X(m.matrix[0][0]), + Fix2X(m.matrix[0][1]), + Fix2X(m.matrix[1][0]), + Fix2X(m.matrix[1][1]), + Fix2X(m.matrix[2][0]), + Fix2X(m.matrix[2][1])); + return transform; +} + +void QTMovie::setTransform(CGAffineTransform t) +{ + ASSERT(m_private->m_movie); + MatrixRecord m = {{ + {X2Fix(t.a), X2Fix(t.b), 0}, + {X2Fix(t.c), X2Fix(t.d), 0}, + {X2Fix(t.tx), X2Fix(t.ty), fract1}, + }}; + + SetMovieMatrix(m_private->m_movie, &m); + m_private->cacheMovieScale(); +} + +void QTMovie::resetTransform() +{ + ASSERT(m_private->m_movie); + SetMovieMatrix(m_private->m_movie, 0); + m_private->cacheMovieScale(); +} + + +bool QTMovie::initializeQuickTime() +{ + static bool initialized = false; + static bool initializationSucceeded = false; + if (!initialized) { + initialized = true; + // Initialize and check QuickTime version + OSErr result = InitializeQTML(kInitializeQTMLEnableDoubleBufferedSurface); + 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(); + initializationSucceeded = true; + } + return initializationSucceeded; +} + +Movie QTMovie::getMovieHandle() const +{ + return m_private->m_movie; +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + return TRUE; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + return FALSE; + } + ASSERT_NOT_REACHED(); + return FALSE; +} diff --git a/Source/WebCore/platform/graphics/win/QTMovie.h b/Source/WebCore/platform/graphics/win/QTMovie.h new file mode 100644 index 0000000..5e4c4e7 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovie.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 QTMovie_h +#define QTMovie_h + +#include "QTTrack.h" +#include <WTF/Vector.h> + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +class QTMovie; +class QTMoviePrivate; +typedef struct MovieType** Movie; +typedef Vector<RefPtr<QTTrack>> QTTrackArray; + +class QTMovieClient { +public: + virtual void movieEnded(QTMovie*) = 0; + virtual void movieLoadStateChanged(QTMovie*) = 0; + virtual void movieTimeChanged(QTMovie*) = 0; +}; + +enum { + QTMovieLoadStateError = -1L, + QTMovieLoadStateLoaded = 2000L, + QTMovieLoadStatePlayable = 10000L, + QTMovieLoadStatePlaythroughOK = 20000L, + QTMovieLoadStateComplete = 100000L +}; + +typedef const struct __CFURL * CFURLRef; + +class QTMOVIEWIN_API QTMovie : public RefCounted<QTMovie> { +public: + static bool initializeQuickTime(); + static void taskTimerFired(); + + static void disableComponent(uint32_t[5]); + + QTMovie(QTMovieClient*); + ~QTMovie(); + + void addClient(QTMovieClient*); + void removeClient(QTMovieClient*); + + void load(const UChar* url, int len, bool preservesPitch); + void load(CFURLRef, bool preservesPitch); + + 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); + void setPreservesPitch(bool); + + unsigned dataSize() const; + + void getNaturalSize(int& width, int& height); + + void disableUnsupportedTracks(unsigned& enabledTrackCount, unsigned& totalTrackCount); + + bool isDisabled() const; + void setDisabled(bool); + + bool hasVideo() const; + bool hasAudio() const; + + QTTrackArray videoTracks() const; + + bool hasClosedCaptions() const; + void setClosedCaptionsVisible(bool); + + static unsigned countSupportedTypes(); + static void getSupportedType(unsigned index, const UChar*& str, unsigned& len); + + CGAffineTransform getTransform() const; + void setTransform(CGAffineTransform); + void resetTransform(); + + Movie getMovieHandle() const; + + long timeScale() const; + +private: + QTMoviePrivate* m_private; + friend class QTMoviePrivate; +}; + +#endif diff --git a/Source/WebCore/platform/graphics/win/QTMovieGWorld.cpp b/Source/WebCore/platform/graphics/win/QTMovieGWorld.cpp new file mode 100644 index 0000000..e13f732 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovieGWorld.cpp @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "QTMovieGWorld.h" + +#include "QTMovieTask.h" +#include <GXMath.h> +#include <Movies.h> +#include <QTML.h> +#include <QuickTimeComponents.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 + +static LPCTSTR fullscreenQTMovieGWorldPointerProp = TEXT("fullscreenQTMovieGWorldPointer"); + +// 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<QTMovieGWorldPrivate*>* gTaskList; +static Vector<CFStringRef>* gSupportedTypes = 0; +static SInt32 quickTimeVersion = 0; + +class QTMovieGWorldPrivate : public QTMovieClient { +public: + QTMovieGWorldPrivate(QTMovieGWorld* movieWin); + virtual ~QTMovieGWorldPrivate(); + + void registerDrawingCallback(); + void unregisterDrawingCallback(); + void drawingComplete(); + void updateGWorld(); + void createGWorld(); + void deleteGWorld(); + void clearGWorld(); + void updateMovieSize(); + + void setSize(int, int); + + virtual void movieEnded(QTMovie*); + virtual void movieLoadStateChanged(QTMovie*); + virtual void movieTimeChanged(QTMovie*); + + QTMovieGWorld* m_movieWin; + RefPtr<QTMovie> m_qtMovie; + Movie m_movie; + QTMovieGWorldClient* m_client; + long m_loadState; + int m_width; + int m_height; + bool m_visible; + GWorldPtr m_gWorld; + int m_gWorldWidth; + int m_gWorldHeight; + GWorldPtr m_savedGWorld; + float m_widthScaleFactor; + float m_heightScaleFactor; +#if !ASSERT_DISABLED + bool m_scaleCached; +#endif + WindowPtr m_fullscreenWindow; + GWorldPtr m_fullscreenOrigGWorld; + Rect m_fullscreenRect; + QTMovieGWorldFullscreenClient* m_fullscreenClient; + char* m_fullscreenRestoreState; + bool m_disabled; +}; + +QTMovieGWorldPrivate::QTMovieGWorldPrivate(QTMovieGWorld* movieWin) + : m_movieWin(movieWin) + , m_movie(0) + , m_client(0) + , m_loadState(0) + , m_width(0) + , m_height(0) + , m_visible(false) + , m_gWorld(0) + , m_gWorldWidth(0) + , m_gWorldHeight(0) + , m_savedGWorld(0) + , m_widthScaleFactor(1) + , m_heightScaleFactor(1) +#if !ASSERT_DISABLED + , m_scaleCached(false) +#endif + , m_fullscreenWindow(0) + , m_fullscreenOrigGWorld(0) + , m_fullscreenClient(0) + , m_fullscreenRestoreState(0) + , m_disabled(false) + , m_qtMovie(0) +{ + Rect rect = { 0, 0, 0, 0 }; + m_fullscreenRect = rect; +} + +QTMovieGWorldPrivate::~QTMovieGWorldPrivate() +{ + ASSERT(!m_fullscreenWindow); + + if (m_gWorld) + deleteGWorld(); +} + +pascal OSErr movieDrawingCompleteProc(Movie movie, long data) +{ + UppParam param; + param.longValue = data; + QTMovieGWorldPrivate* mp = static_cast<QTMovieGWorldPrivate*>(param.ptr); + if (mp) + mp->drawingComplete(); + return 0; +} + +void QTMovieGWorldPrivate::registerDrawingCallback() +{ + if (!gMovieDrawingCompleteUPP) + gMovieDrawingCompleteUPP = NewMovieDrawingCompleteUPP(movieDrawingCompleteProc); + + UppParam param; + param.ptr = this; + SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, gMovieDrawingCompleteUPP, param.longValue); +} + +void QTMovieGWorldPrivate::unregisterDrawingCallback() +{ + SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, 0, 0); +} + +void QTMovieGWorldPrivate::drawingComplete() +{ + if (!m_gWorld || m_movieWin->m_private->m_disabled || m_loadState < QTMovieLoadStateLoaded) + return; + m_client->movieNewImageAvailable(m_movieWin); +} + +void QTMovieGWorldPrivate::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 QTMovieGWorldPrivate::createGWorld() +{ + ASSERT(!m_gWorld); + if (!m_movie || m_loadState < QTMovieLoadStateLoaded) + 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, 0, 0, 0); + if (err) + return; + GetMovieGWorld(m_movie, &m_savedGWorld, 0); + SetMovieGWorld(m_movie, m_gWorld, 0); + bounds.right = m_width; + bounds.bottom = m_height; + SetMovieBox(m_movie, &bounds); +} + +void QTMovieGWorldPrivate::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 QTMovieGWorldPrivate::setSize(int width, int height) +{ + if (m_width == width && m_height == height) + return; + m_width = width; + m_height = height; + + // Do not change movie box before reaching load state loaded as we grab + // the initial size when task() sees that state for the first time, and + // we need the initial size to be able to scale movie properly. + if (!m_movie || m_loadState < QTMovieLoadStateLoaded) + return; + +#if !ASSERT_DISABLED + ASSERT(m_scaleCached); +#endif + + updateMovieSize(); +} + +void QTMovieGWorldPrivate::updateMovieSize() +{ + if (!m_movie || m_loadState < QTMovieLoadStateLoaded) + return; + + Rect bounds; + bounds.top = 0; + bounds.left = 0; + bounds.right = m_width; + bounds.bottom = m_height; + SetMovieBox(m_movie, &bounds); + updateGWorld(); +} + + +void QTMovieGWorldPrivate::deleteGWorld() +{ + ASSERT(m_gWorld); + if (m_movie) + SetMovieGWorld(m_movie, m_savedGWorld, 0); + m_savedGWorld = 0; + DisposeGWorld(m_gWorld); + m_gWorld = 0; + m_gWorldWidth = 0; + m_gWorldHeight = 0; +} + +void QTMovieGWorldPrivate::movieEnded(QTMovie*) +{ + // Do nothing +} + +void QTMovieGWorldPrivate::movieLoadStateChanged(QTMovie* movie) +{ + long loadState = GetMovieLoadState(movie->getMovieHandle()); + 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 + bool movieNewlyPlayable = loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded; + m_loadState = loadState; + + if (movieNewlyPlayable) { + updateMovieSize(); + if (m_visible) + clearGWorld(); + } + } +} + +void QTMovieGWorldPrivate::movieTimeChanged(QTMovie*) +{ + // Do nothing +} + +QTMovieGWorld::QTMovieGWorld(QTMovieGWorldClient* client) + : m_private(new QTMovieGWorldPrivate(this)) +{ + m_private->m_client = client; +} + +QTMovieGWorld::~QTMovieGWorld() +{ + delete m_private; +} + +void QTMovieGWorld::setSize(int width, int height) +{ + m_private->setSize(width, height); + QTMovieTask::sharedTask()->updateTaskTimer(); +} + +void QTMovieGWorld::setVisible(bool b) +{ + m_private->m_visible = b; + m_private->updateGWorld(); +} + +void QTMovieGWorld::getCurrentFrameInfo(void*& buffer, unsigned& bitsPerPixel, unsigned& rowBytes, unsigned& width, unsigned& height) +{ + if (!m_private->m_gWorld) { + buffer = 0; + bitsPerPixel = 0; + rowBytes = 0; + width = 0; + height = 0; + return; + } + PixMapHandle offscreenPixMap = GetGWorldPixMap(m_private->m_gWorld); + buffer = (*offscreenPixMap)->baseAddr; + bitsPerPixel = (*offscreenPixMap)->pixelSize; + rowBytes = (*offscreenPixMap)->rowBytes & 0x3FFF; + width = m_private->m_width; + height = m_private->m_height; +} + +void QTMovieGWorld::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 QTMovieGWorld::setDisabled(bool b) +{ + m_private->m_disabled = b; +} + +bool QTMovieGWorld::isDisabled() const +{ + return m_private->m_disabled; +} + +LRESULT QTMovieGWorld::fullscreenWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + QTMovieGWorld* movie = static_cast<QTMovieGWorld*>(GetProp(wnd, fullscreenQTMovieGWorldPointerProp)); + + if (message == WM_DESTROY) + RemoveProp(wnd, fullscreenQTMovieGWorldPointerProp); + + if (!movie) + return DefWindowProc(wnd, message, wParam, lParam); + + return movie->m_private->m_fullscreenClient->fullscreenClientWndProc(wnd, message, wParam, lParam); +} + +HWND QTMovieGWorld::enterFullscreen(QTMovieGWorldFullscreenClient* client) +{ + m_private->m_fullscreenClient = client; + + BeginFullScreen(&m_private->m_fullscreenRestoreState, 0, 0, 0, &m_private->m_fullscreenWindow, 0, fullScreenAllowEvents); + QTMLSetWindowWndProc(m_private->m_fullscreenWindow, fullscreenWndProc); + CreatePortAssociation(GetPortNativeWindow(m_private->m_fullscreenWindow), 0, 0); + + GetMovieBox(m_private->m_movie, &m_private->m_fullscreenRect); + GetMovieGWorld(m_private->m_movie, &m_private->m_fullscreenOrigGWorld, 0); + SetMovieGWorld(m_private->m_movie, reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow), GetGWorldDevice(reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow))); + + // Set the size of the box to preserve aspect ratio + Rect rect = m_private->m_fullscreenWindow->portRect; + + float movieRatio = static_cast<float>(m_private->m_width) / m_private->m_height; + int windowWidth = rect.right - rect.left; + int windowHeight = rect.bottom - rect.top; + float windowRatio = static_cast<float>(windowWidth) / windowHeight; + int actualWidth = (windowRatio > movieRatio) ? (windowHeight * movieRatio) : windowWidth; + int actualHeight = (windowRatio < movieRatio) ? (windowWidth / movieRatio) : windowHeight; + int offsetX = (windowWidth - actualWidth) / 2; + int offsetY = (windowHeight - actualHeight) / 2; + + rect.left = offsetX; + rect.right = offsetX + actualWidth; + rect.top = offsetY; + rect.bottom = offsetY + actualHeight; + + SetMovieBox(m_private->m_movie, &rect); + ShowHideTaskBar(true); + + // Set the 'this' pointer on the HWND + HWND wnd = static_cast<HWND>(GetPortNativeWindow(m_private->m_fullscreenWindow)); + SetProp(wnd, fullscreenQTMovieGWorldPointerProp, static_cast<HANDLE>(this)); + + return wnd; +} + +void QTMovieGWorld::exitFullscreen() +{ + if (!m_private->m_fullscreenWindow) + return; + + HWND wnd = static_cast<HWND>(GetPortNativeWindow(m_private->m_fullscreenWindow)); + DestroyPortAssociation(reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow)); + SetMovieGWorld(m_private->m_movie, m_private->m_fullscreenOrigGWorld, 0); + EndFullScreen(m_private->m_fullscreenRestoreState, 0L); + SetMovieBox(m_private->m_movie, &m_private->m_fullscreenRect); + m_private->m_fullscreenWindow = 0; +} + +void QTMovieGWorld::setMovie(PassRefPtr<QTMovie> movie) +{ + if (m_private->m_qtMovie) { + m_private->unregisterDrawingCallback(); + m_private->m_qtMovie->removeClient(m_private); + m_private->m_qtMovie = 0; + m_private->m_movie = 0; + } + + m_private->m_qtMovie = movie; + + if (m_private->m_qtMovie) { + m_private->m_qtMovie->addClient(m_private); + m_private->m_movie = m_private->m_qtMovie->getMovieHandle(); + m_private->registerDrawingCallback(); + } +} + +QTMovie* QTMovieGWorld::movie() const +{ + return m_private->m_qtMovie.get(); +} diff --git a/Source/WebCore/platform/graphics/win/QTMovieGWorld.h b/Source/WebCore/platform/graphics/win/QTMovieGWorld.h new file mode 100644 index 0000000..495fe25 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovieGWorld.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 QTMovieGWorld_h +#define QTMovieGWorld_h + + +#include "QTMovie.h" +#include <Unicode.h> +#include <WTF/RefCounted.h> +#include <WTF/RefPtr.h> +#include <windows.h> + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +class QTMovieGWorld; +class QTMovieGWorldPrivate; + +class QTMovieGWorldClient { +public: + virtual void movieNewImageAvailable(QTMovieGWorld*) = 0; +}; + +class QTMovieGWorldFullscreenClient { +public: + virtual LRESULT fullscreenClientWndProc(HWND, UINT message, WPARAM, LPARAM) = 0; +}; + +class QTMOVIEWIN_API QTMovieGWorld : public RefCounted<QTMovieGWorld> { +public: + QTMovieGWorld(QTMovieGWorldClient*); + ~QTMovieGWorld(); + + void getNaturalSize(int& width, int& height); + void setSize(int width, int height); + + void setVisible(bool); + void paint(HDC, int x, int y); + void getCurrentFrameInfo(void*& buffer, unsigned& bitsPerPixel, unsigned& rowBytes, unsigned& width, unsigned& height); + + void setDisabled(bool); + bool isDisabled() const; + + // Returns the full-screen window created + HWND enterFullscreen(QTMovieGWorldFullscreenClient*); + void exitFullscreen(); + + void setMovie(PassRefPtr<QTMovie>); + QTMovie* movie() const; + +private: + static LRESULT fullscreenWndProc(HWND, UINT message, WPARAM, LPARAM); + + QTMovieGWorldPrivate* m_private; + friend class QTMovieGWorldPrivate; +}; + +#endif diff --git a/Source/WebCore/platform/graphics/win/QTMovieTask.cpp b/Source/WebCore/platform/graphics/win/QTMovieTask.cpp new file mode 100644 index 0000000..8713588 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovieTask.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "QTMovieTask.h" + +// Put Movies.h first so build failures here point clearly to QuickTime +#include <Movies.h> + +#include <wtf/HashSet.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> + +QTMovieTask::QTMovieTask() + : m_setTaskTimerDelay(0) + , m_stopTaskTimer(0) +{ +} + +QTMovieTask::~QTMovieTask() +{ +} + +QTMovieTask* QTMovieTask::sharedTask() +{ + static QTMovieTask* s_sharedTask = new QTMovieTask; + return s_sharedTask; +} + +void QTMovieTask::updateTaskTimer(double maxInterval, double minInterval) +{ + ASSERT(m_setTaskTimerDelay); + if (!m_setTaskTimerDelay) + return; + + ASSERT(m_stopTaskTimer); + if (!m_taskList.size() && m_stopTaskTimer) { + m_stopTaskTimer(); + return; + } + + long intervalInMS; + OSStatus status = QTGetTimeUntilNextTask(&intervalInMS, 1000); + double interval = intervalInMS / 1000.0; + if (interval < minInterval) + interval = minInterval; + if (interval > maxInterval) + interval = maxInterval; + m_setTaskTimerDelay(interval); +} + +void QTMovieTask::fireTaskClients() +{ + Vector<QTMovieTaskClient*> clients; + copyToVector(m_taskList, clients); + for (Vector<QTMovieTaskClient*>::iterator i = clients.begin(); i != clients.end(); ++i) + (*i)->task(); +} + +void QTMovieTask::addTaskClient(QTMovieTaskClient* client) +{ + ASSERT(client); + if (!client) + return; + + m_taskList.add(client); +} + +void QTMovieTask::removeTaskClient(QTMovieTaskClient* client) +{ + ASSERT(client); + if (!client) + return; + + m_taskList.remove(client); +} + +void QTMovieTask::setTaskTimerFuncs(SetTaskTimerDelayFunc setTaskTimerDelay, StopTaskTimerFunc stopTaskTimer) +{ + m_setTaskTimerDelay = setTaskTimerDelay; + m_stopTaskTimer = stopTaskTimer; +} + diff --git a/Source/WebCore/platform/graphics/win/QTMovieTask.h b/Source/WebCore/platform/graphics/win/QTMovieTask.h new file mode 100644 index 0000000..e394d6e --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovieTask.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 QTMovieTask_h +#define QTMovieTask_h + +#include <WTF/HashSet.h> + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +class QTMovieTaskClient { +public: + virtual void task() = 0; +}; + +typedef void (*SetTaskTimerDelayFunc)(double); +typedef void (*StopTaskTimerFunc)(); + +class QTMOVIEWIN_API QTMovieTask { +public: + static QTMovieTask* sharedTask(); + + void addTaskClient(QTMovieTaskClient* client); + void removeTaskClient(QTMovieTaskClient*); + void fireTaskClients(); + + void updateTaskTimer(double maxInterval = 1.0, double minInterval = 1.0 / 30); + void setTaskTimerFuncs(SetTaskTimerDelayFunc setTaskTimerDelay, StopTaskTimerFunc stopTaskTimer); + +protected: + QTMovieTask(); + ~QTMovieTask(); + + SetTaskTimerDelayFunc m_setTaskTimerDelay; + StopTaskTimerFunc m_stopTaskTimer; + HashSet<QTMovieTaskClient*> m_taskList; + +private: + QTMovieTask(const QTMovieTask&); + QTMovieTask& operator=(const QTMovieTask&); +}; + +#endif diff --git a/Source/WebCore/platform/graphics/win/QTMovieVisualContext.cpp b/Source/WebCore/platform/graphics/win/QTMovieVisualContext.cpp new file mode 100644 index 0000000..0fcc7e2 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovieVisualContext.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "QTMovieVisualContext.h" + +#include "QTMovieTask.h" +#include <CVBase.h> +#include <CVHostTime.h> +#include <ImageCompression.h> +#include <Movies.h> +#include <windows.h> + +struct QTCVTimeStamp { + CVTimeStamp t; +}; + +class QTMovieVisualContextPriv { +public: + QTMovieVisualContextPriv(QTMovieVisualContext* parent, QTMovieVisualContextClient* client, QTPixelBuffer::Type contextType); + ~QTMovieVisualContextPriv(); + + bool isImageAvailableForTime(const QTCVTimeStamp*) const; + QTPixelBuffer imageForTime(const QTCVTimeStamp*); + void task(); + + QTVisualContextRef visualContextRef(); + + void setMovie(PassRefPtr<QTMovie>); + QTMovie* movie() const; + + static void imageAvailableCallback(QTVisualContextRef visualContext, const CVTimeStamp *timeStamp, void *refCon); + +private: + QTMovieVisualContext* m_parent; + QTMovieVisualContextClient* m_client; + QTVisualContextRef m_visualContext; + RefPtr<QTMovie> m_movie; + +}; + +static CFDictionaryRef createPixelBufferOptionsDictionary(QTPixelBuffer::Type contextType) +{ + const void* key = kQTVisualContextPixelBufferAttributesKey; + const void* value = QTPixelBuffer::createPixelBufferAttributesDictionary(contextType); + CFDictionaryRef pixelBufferOptions = CFDictionaryCreate(kCFAllocatorDefault, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease(value); + return pixelBufferOptions; +} + +static CFDictionaryRef pixelBufferCreationOptions(QTPixelBuffer::Type contextType) +{ + if (contextType == QTPixelBuffer::ConfigureForCAImageQueue) { + static CFDictionaryRef imageQueueOptions = createPixelBufferOptionsDictionary(contextType); + return imageQueueOptions; + } + + ASSERT(contextType == QTPixelBuffer::ConfigureForCGImage); + static CFDictionaryRef cgImageOptions = createPixelBufferOptionsDictionary(contextType); + return cgImageOptions; +} + +QTMovieVisualContextPriv::QTMovieVisualContextPriv(QTMovieVisualContext* parent, QTMovieVisualContextClient* client, QTPixelBuffer::Type contextType) + : m_parent(parent) + , m_client(client) + , m_visualContext(0) +{ + typedef OSStatus ( __cdecl *pfnQTPixelBufferContextCreate)(CFAllocatorRef, CFDictionaryRef, QTVisualContextRef*); + static pfnQTPixelBufferContextCreate pPixelBufferContextCreate = 0; + if (!pPixelBufferContextCreate) { + HMODULE qtmlDLL = ::LoadLibraryW(L"QTMLClient.dll"); + if (!qtmlDLL) + return; + pPixelBufferContextCreate = reinterpret_cast<pfnQTPixelBufferContextCreate>(GetProcAddress(qtmlDLL, "QTPixelBufferContextCreate")); + if (!pPixelBufferContextCreate) + return; + } + + OSStatus status = pPixelBufferContextCreate(kCFAllocatorDefault, pixelBufferCreationOptions(contextType), &m_visualContext); + if (status == noErr && m_visualContext) + QTVisualContextSetImageAvailableCallback(m_visualContext, &QTMovieVisualContextPriv::imageAvailableCallback, static_cast<void*>(this)); +} + +QTMovieVisualContextPriv::~QTMovieVisualContextPriv() +{ + if (m_visualContext) + QTVisualContextSetImageAvailableCallback(m_visualContext, 0, 0); +} + +bool QTMovieVisualContextPriv::isImageAvailableForTime(const QTCVTimeStamp* timeStamp) const +{ + if (!m_visualContext) + return false; + + return QTVisualContextIsNewImageAvailable(m_visualContext, reinterpret_cast<const CVTimeStamp*>(timeStamp)); +} + +QTPixelBuffer QTMovieVisualContextPriv::imageForTime(const QTCVTimeStamp* timeStamp) +{ + QTPixelBuffer pixelBuffer; + if (m_visualContext) { + CVImageBufferRef newImage = 0; + OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, kCFAllocatorDefault, reinterpret_cast<const CVTimeStamp*>(timeStamp), &newImage); + if (status == noErr) + pixelBuffer.adopt(newImage); + } + return pixelBuffer; +} + +void QTMovieVisualContextPriv::task() +{ + if (m_visualContext) + QTVisualContextTask(m_visualContext); +} + +QTVisualContextRef QTMovieVisualContextPriv::visualContextRef() +{ + return m_visualContext; +} + +void QTMovieVisualContextPriv::setMovie(PassRefPtr<QTMovie> movie) +{ + if (movie == m_movie) + return; + + if (m_movie) { + SetMovieVisualContext(m_movie->getMovieHandle(), 0); + m_movie = 0; + } + + if (movie) + OSStatus status = SetMovieVisualContext(movie->getMovieHandle(), m_visualContext); + + m_movie = movie; +} + +QTMovie* QTMovieVisualContextPriv::movie() const +{ + return m_movie.get(); +} + +void QTMovieVisualContextPriv::imageAvailableCallback(QTVisualContextRef visualContext, const CVTimeStamp *timeStamp, void *refCon) +{ + if (!refCon) + return; + + QTMovieVisualContextPriv* vc = static_cast<QTMovieVisualContextPriv*>(refCon); + if (!vc->m_client) + return; + + vc->m_client->imageAvailableForTime(reinterpret_cast<const QTCVTimeStamp*>(timeStamp)); +} + +PassRefPtr<QTMovieVisualContext> QTMovieVisualContext::create(QTMovieVisualContextClient* client, QTPixelBuffer::Type contextType) +{ + return adoptRef(new QTMovieVisualContext(client, contextType)); +} + +QTMovieVisualContext::QTMovieVisualContext(QTMovieVisualContextClient* client, QTPixelBuffer::Type contextType) + : m_private(new QTMovieVisualContextPriv(this, client, contextType)) +{ +} + +QTMovieVisualContext::~QTMovieVisualContext() +{ +} + +bool QTMovieVisualContext::isImageAvailableForTime(const QTCVTimeStamp* timeStamp) const +{ + return m_private->isImageAvailableForTime(timeStamp); +} + +QTPixelBuffer QTMovieVisualContext::imageForTime(const QTCVTimeStamp* timeStamp) +{ + return m_private->imageForTime(timeStamp); +} + +void QTMovieVisualContext::task() +{ + m_private->task(); +} + +QTVisualContextRef QTMovieVisualContext::visualContextRef() +{ + return m_private->visualContextRef(); +} + +void QTMovieVisualContext::setMovie(PassRefPtr<QTMovie> movie) +{ + m_private->setMovie(movie); +} + +QTMovie* QTMovieVisualContext::movie() const +{ + return m_private->movie(); +} + +double QTMovieVisualContext::currentHostTime() +{ + return CVGetCurrentHostTime() / CVGetHostClockFrequency(); +} diff --git a/Source/WebCore/platform/graphics/win/QTMovieVisualContext.h b/Source/WebCore/platform/graphics/win/QTMovieVisualContext.h new file mode 100644 index 0000000..8410208 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovieVisualContext.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 QTMovieVisualContext_h +#define QTMovieVisualContext_h + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +#include "QTMovie.h" +#include "QTMovieTask.h" +#include "QTPixelBuffer.h" +#include <WTF/OwnPtr.h> +#include <WTF/RefCounted.h> + +typedef const struct __CFDictionary* CFDictionaryRef; +typedef struct OpaqueQTVisualContext* QTVisualContextRef; + +// QTCVTimeStamp is a struct containing only a CVTimeStamp. This is to +// work around the inability of CVTimeStamp to be forward declared, in +// addition to it being declared in different header files when building +// the QTMovieWin and WebCore projects. +struct QTCVTimeStamp; + +class QTMovieVisualContextClient { +public: + virtual void imageAvailableForTime(const QTCVTimeStamp*) = 0; +}; + +class QTMOVIEWIN_API QTMovieVisualContext : public RefCounted<QTMovieVisualContext> { +public: + static PassRefPtr<QTMovieVisualContext> create(QTMovieVisualContextClient*, QTPixelBuffer::Type); + ~QTMovieVisualContext(); + + bool isImageAvailableForTime(const QTCVTimeStamp*) const; + QTPixelBuffer imageForTime(const QTCVTimeStamp*); + void task(); + + QTVisualContextRef visualContextRef(); + + void setMovie(PassRefPtr<QTMovie>); + QTMovie* movie() const; + + static double currentHostTime(); + +protected: + QTMovieVisualContext(QTMovieVisualContextClient*, QTPixelBuffer::Type); + void setupVisualContext(); + + friend class QTMovieVisualContextPriv; + OwnPtr<QTMovieVisualContextPriv> m_private; +}; + +#endif diff --git a/Source/WebCore/platform/graphics/win/QTMovieWinTimer.cpp b/Source/WebCore/platform/graphics/win/QTMovieWinTimer.cpp new file mode 100644 index 0000000..f6103ea --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTMovieWinTimer.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2006, 2007, 2009 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 if (message == WM_TIMER && wParam == timerID) { + stopSharedTimer(); + sharedTimerFiredFunction(); + } 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; +} + +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; + } + + stopSharedTimer(); + initializeOffScreenTimerWindow(); + + // 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) + PostMessage(timerWindowHandle, timerFiredMessage, 0, 0); + } else + timerID = SetTimer(timerWindowHandle, timerFiredMessage, intervalInMS, 0); +} + +void stopSharedTimer() +{ + if (timerID) { + KillTimer(timerWindowHandle, 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/Source/WebCore/platform/graphics/win/QTMovieWinTimer.h b/Source/WebCore/platform/graphics/win/QTMovieWinTimer.h new file mode 100644 index 0000000..976b310 --- /dev/null +++ b/Source/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 QTMovieWinTimer_h +#define QTMovieWinTimer_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/Source/WebCore/platform/graphics/win/QTPixelBuffer.cpp b/Source/WebCore/platform/graphics/win/QTPixelBuffer.cpp new file mode 100644 index 0000000..44a1b0e --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTPixelBuffer.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "QTPixelBuffer.h" + +#include <CFNumber.h> +#include <CFString.h> +#include <CGColorSpace.h> +#include <CGImage.h> +#include <CVPixelBuffer.h> +#include <QuickDraw.h> +#include <memory.h> + +static OSStatus SetNumberValue(CFMutableDictionaryRef inDict, CFStringRef inKey, SInt32 inValue) +{ + CFNumberRef number; + + number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue); + if (!number) + return coreFoundationUnknownErr; + + CFDictionarySetValue(inDict, inKey, number); + CFRelease(number); + + return noErr; +} + +CFDictionaryRef QTPixelBuffer::createPixelBufferAttributesDictionary(QTPixelBuffer::Type contextType) +{ + static const CFStringRef kDirect3DCompatibilityKey = CFSTR("Direct3DCompatibility"); + + CFMutableDictionaryRef pixelBufferAttributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (contextType == QTPixelBuffer::ConfigureForCAImageQueue) { + // Ask for D3D compatible pixel buffers so no further work is needed. + CFDictionarySetValue(pixelBufferAttributes, kDirect3DCompatibilityKey, kCFBooleanTrue); + } else { + // Use the k32BGRAPixelFormat, as QuartzCore will be able to use the pixels directly, + // without needing an additional copy or rendering pass. + SetNumberValue(pixelBufferAttributes, kCVPixelBufferPixelFormatTypeKey, k32BGRAPixelFormat); + + // Set kCVPixelBufferBytesPerRowAlignmentKey to 16 to ensure that each row of pixels + // starts at a 16 byte aligned address for most efficient data reading. + SetNumberValue(pixelBufferAttributes, kCVPixelBufferBytesPerRowAlignmentKey, 16); + CFDictionarySetValue(pixelBufferAttributes, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue); + } + return pixelBufferAttributes; +} + +QTPixelBuffer::QTPixelBuffer() + : m_pixelBuffer(0) +{ +} + +QTPixelBuffer::QTPixelBuffer(const QTPixelBuffer& p) + : m_pixelBuffer(p.m_pixelBuffer) +{ + CVPixelBufferRetain(m_pixelBuffer); +} + +QTPixelBuffer::QTPixelBuffer(CVPixelBufferRef ref) + : m_pixelBuffer(ref) +{ + CVPixelBufferRetain(m_pixelBuffer); +} + +QTPixelBuffer::~QTPixelBuffer() +{ + clear(); +} + +QTPixelBuffer& QTPixelBuffer::operator=(const QTPixelBuffer& p) +{ + set(p.m_pixelBuffer); + return *this; +} + +void QTPixelBuffer::set(CVPixelBufferRef ref) +{ + CVPixelBufferRetain(ref); + CVPixelBufferRelease(m_pixelBuffer); + m_pixelBuffer = ref; +} + +CVPixelBufferRef QTPixelBuffer::pixelBufferRef() +{ + return m_pixelBuffer; +} + +void QTPixelBuffer::adopt(CVPixelBufferRef ref) +{ + if (ref == m_pixelBuffer) + return; + CVPixelBufferRelease(m_pixelBuffer); + m_pixelBuffer = ref; +} + +void QTPixelBuffer::clear() +{ + CVPixelBufferRelease(m_pixelBuffer); + m_pixelBuffer = 0; +} + +CVReturn QTPixelBuffer::lockBaseAddress() +{ + return CVPixelBufferLockBaseAddress(m_pixelBuffer, 0); +} + +CVReturn QTPixelBuffer::unlockBaseAddress() +{ + return CVPixelBufferUnlockBaseAddress(m_pixelBuffer, 0); +} + +void* QTPixelBuffer::baseAddress() +{ + return CVPixelBufferGetBaseAddress(m_pixelBuffer); +} + +size_t QTPixelBuffer::width() const +{ + return CVPixelBufferGetWidth(m_pixelBuffer); +} + +size_t QTPixelBuffer::height() const +{ + return CVPixelBufferGetHeight(m_pixelBuffer); +} + +unsigned long QTPixelBuffer::pixelFormatType() const +{ + return CVPixelBufferGetPixelFormatType(m_pixelBuffer); +} + +bool QTPixelBuffer::pixelFormatIs32ARGB() const +{ + return CVPixelBufferGetPixelFormatType(m_pixelBuffer) == k32ARGBPixelFormat; +} + +bool QTPixelBuffer::pixelFormatIs32BGRA() const +{ + return CVPixelBufferGetPixelFormatType(m_pixelBuffer) == k32BGRAPixelFormat; +} + +size_t QTPixelBuffer::bytesPerRow() const +{ + return CVPixelBufferGetBytesPerRow(m_pixelBuffer); +} + +size_t QTPixelBuffer::dataSize() const +{ + return CVPixelBufferGetDataSize(m_pixelBuffer); +} + +bool QTPixelBuffer::isPlanar() const +{ + return CVPixelBufferIsPlanar(m_pixelBuffer); +} + +size_t QTPixelBuffer::planeCount() const +{ + return CVPixelBufferGetPlaneCount(m_pixelBuffer); +} + +size_t QTPixelBuffer::widthOfPlane(size_t plane) const +{ + return CVPixelBufferGetWidthOfPlane(m_pixelBuffer, plane); +} + +size_t QTPixelBuffer::heightOfPlane(size_t plane) const +{ + return CVPixelBufferGetHeightOfPlane(m_pixelBuffer, plane); +} + +void* QTPixelBuffer::baseAddressOfPlane(size_t plane) const +{ + return CVPixelBufferGetBaseAddressOfPlane(m_pixelBuffer, plane); +} + +size_t QTPixelBuffer::bytesPerRowOfPlane(size_t plane) const +{ + return CVPixelBufferGetBytesPerRowOfPlane(m_pixelBuffer, plane); +} + +void QTPixelBuffer::getExtendedPixels(size_t* left, size_t* right, size_t* top, size_t* bottom) const +{ + return CVPixelBufferGetExtendedPixels(m_pixelBuffer, left, right, top, bottom); +} + +CFDictionaryRef QTPixelBuffer::attachments() const +{ + return CVBufferGetAttachments(m_pixelBuffer, kCVAttachmentMode_ShouldPropagate); +} + +void QTPixelBuffer::retainCallback(void* refcon) +{ + CVPixelBufferRetain(static_cast<CVPixelBufferRef>(refcon)); +} + +void QTPixelBuffer::releaseCallback(void* refcon) +{ + CVPixelBufferRelease(static_cast<CVPixelBufferRef>(refcon)); +} + +void QTPixelBuffer::imageQueueReleaseCallback(unsigned int type, uint64_t id, void* refcon) +{ + CVPixelBufferRelease(static_cast<CVPixelBufferRef>(refcon)); +} + +void QTPixelBuffer::dataProviderReleaseBytePointerCallback(void* refcon, const void* pointer) +{ + CVPixelBufferUnlockBaseAddress(static_cast<CVPixelBufferRef>(refcon), 0); +} + +const void* QTPixelBuffer::dataProviderGetBytePointerCallback(void* refcon) +{ + CVPixelBufferLockBaseAddress(static_cast<CVPixelBufferRef>(refcon), 0); + return CVPixelBufferGetBaseAddress(static_cast<CVPixelBufferRef>(refcon)); +} + +size_t QTPixelBuffer::dataProviderGetBytesAtPositionCallback(void* refcon, void* buffer, size_t position, size_t count) +{ + char* data = (char*)CVPixelBufferGetBaseAddress(static_cast<CVPixelBufferRef>(refcon)); + size_t size = CVPixelBufferGetDataSize(static_cast<CVPixelBufferRef>(refcon)); + if (size - position < count) + count = size - position; + + memcpy(buffer, data+position, count); + return count; +} + +void QTPixelBuffer::dataProviderReleaseInfoCallback(void* refcon) +{ + CVPixelBufferRelease(static_cast<CVPixelBufferRef>(refcon)); +} diff --git a/Source/WebCore/platform/graphics/win/QTPixelBuffer.h b/Source/WebCore/platform/graphics/win/QTPixelBuffer.h new file mode 100644 index 0000000..13630da --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTPixelBuffer.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 QTPixelBuffer_h +#define QTPixelBuffer_h + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +#include <stdint.h> + +typedef struct __CVBuffer *CVBufferRef; +typedef CVBufferRef CVPixelBufferRef; +typedef struct CGImage* CGImageRef; +typedef int32_t CVReturn; +typedef const struct __CFDictionary * CFDictionaryRef; + +// QTPixelBuffer wraps QuickTime's implementation of CVPixelBuffer, so its functions are +// safe to call within WebKit. +class QTMOVIEWIN_API QTPixelBuffer { +public: + enum Type { ConfigureForCGImage, ConfigureForCAImageQueue }; + static CFDictionaryRef createPixelBufferAttributesDictionary(Type); + + QTPixelBuffer(); + QTPixelBuffer(const QTPixelBuffer&); + QTPixelBuffer(CVPixelBufferRef); + QTPixelBuffer& operator=(const QTPixelBuffer&); + ~QTPixelBuffer(); + + void set(CVPixelBufferRef); + CVPixelBufferRef pixelBufferRef(); + void adopt(CVPixelBufferRef); + void clear(); + + CVReturn lockBaseAddress(); + CVReturn unlockBaseAddress(); + void* baseAddress(); + + size_t width() const; + size_t height() const; + unsigned long pixelFormatType() const; + bool pixelFormatIs32ARGB() const; + bool pixelFormatIs32BGRA() const; + size_t bytesPerRow() const; + size_t dataSize() const; + + bool isPlanar() const; + size_t planeCount() const; + size_t widthOfPlane(size_t) const; + size_t heightOfPlane(size_t) const; + void* baseAddressOfPlane(size_t) const; + size_t bytesPerRowOfPlane(size_t) const; + + void getExtendedPixels(size_t* left, size_t* right, size_t* top, size_t* bottom) const; + CFDictionaryRef attachments() const; + + // Generic CFRetain/CFRelease callbacks + static void releaseCallback(void* refcon); + static void retainCallback(void* refcon); + + // CAImageQueue callbacks + static void imageQueueReleaseCallback(unsigned int type, uint64_t id, void* refcon); + + // CGDataProvider callbacks + static void dataProviderReleaseBytePointerCallback(void* refcon, const void* pointer); + static const void* dataProviderGetBytePointerCallback(void* refcon); + static size_t dataProviderGetBytesAtPositionCallback(void* refcon, void* buffer, size_t position, size_t count); + static void dataProviderReleaseInfoCallback(void* refcon); + +private: + CVPixelBufferRef m_pixelBuffer; +}; + +#endif diff --git a/Source/WebCore/platform/graphics/win/QTTrack.cpp b/Source/WebCore/platform/graphics/win/QTTrack.cpp new file mode 100644 index 0000000..09142bc --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTTrack.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 "QTTrack.h" + +#include <Movies.h> +#include <QTML.h> + +using namespace std; + +class QTTrackPrivate : public Noncopyable { +public: + QTTrackPrivate(); + ~QTTrackPrivate(); + + QTTrack* m_track; + Track m_trackHandle; +}; + +QTTrackPrivate::QTTrackPrivate() + : m_track(0) + , m_trackHandle(0) +{ +} + +QTTrackPrivate::~QTTrackPrivate() +{ + m_trackHandle = 0; +} + +PassRefPtr<QTTrack> QTTrack::create(Track trackHandle) +{ + return adoptRef(new QTTrack(trackHandle)); +} + +QTTrack::QTTrack(Track trackHandle) + : m_private(new QTTrackPrivate()) +{ + m_private->m_track = this; + m_private->m_trackHandle = trackHandle; +} + +QTTrack::~QTTrack() +{ + delete m_private; +} + +bool QTTrack::isEnabled() const +{ + ASSERT(m_private->m_track); + return GetTrackEnabled(m_private->m_trackHandle); +} + +void QTTrack::setEnabled(bool enabled) +{ + ASSERT(m_private->m_trackHandle); + SetTrackEnabled(m_private->m_trackHandle, enabled); +} + +CGAffineTransform QTTrack::getTransform() const +{ + ASSERT(m_private->m_trackHandle); + MatrixRecord m = {0}; + GetTrackMatrix(m_private->m_trackHandle, &m); + + ASSERT(!m.matrix[0][2]); + ASSERT(!m.matrix[1][2]); + CGAffineTransform transform = CGAffineTransformMake( + Fix2X(m.matrix[0][0]), + Fix2X(m.matrix[0][1]), + Fix2X(m.matrix[1][0]), + Fix2X(m.matrix[1][1]), + Fix2X(m.matrix[2][0]), + Fix2X(m.matrix[2][1])); + + return transform; +} + +void QTTrack::setTransform(CGAffineTransform t) +{ + ASSERT(m_private->m_trackHandle); + MatrixRecord m = {{ + {X2Fix(t.a), X2Fix(t.b), 0}, + {X2Fix(t.c), X2Fix(t.d), 0}, + {X2Fix(t.tx), X2Fix(t.ty), fract1}, + }}; + + SetTrackMatrix(m_private->m_trackHandle, &m); +} + +void QTTrack::resetTransform() +{ + ASSERT(m_private->m_trackHandle); + SetTrackMatrix(m_private->m_trackHandle, 0); +} + diff --git a/Source/WebCore/platform/graphics/win/QTTrack.h b/Source/WebCore/platform/graphics/win/QTTrack.h new file mode 100644 index 0000000..bda5644 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/QTTrack.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 QTTrack_h +#define QTTrack_h + +#include <Unicode.h> +#include <windows.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +// We must include this after <Unicode.h>, or the definition of OSErr will change: +#include <CoreGraphics/CGAffineTransform.h> + +#ifdef QTMOVIEWIN_EXPORTS +#define QTMOVIEWIN_API __declspec(dllexport) +#else +#define QTMOVIEWIN_API __declspec(dllimport) +#endif + +class QTTrack; +class QTTrackPrivate; +typedef struct TrackType** Track; + +class QTMOVIEWIN_API QTTrack : public RefCounted<QTTrack> { +public: + static PassRefPtr<QTTrack> create(Track); + ~QTTrack(); + + CGAffineTransform getTransform() const; + void setTransform(CGAffineTransform); + void resetTransform(); + + bool isEnabled() const; + void setEnabled(bool); + + Track getTrackHandle() const; +private: + QTTrack(Track); + QTTrackPrivate* m_private; + friend class QTTrackPrivate; +}; + +#endif diff --git a/Source/WebCore/platform/graphics/win/RefCountedGDIHandle.h b/Source/WebCore/platform/graphics/win/RefCountedGDIHandle.h new file mode 100644 index 0000000..65f66f1 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/RefCountedGDIHandle.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2010 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 RefCountedGDIHandle_h +#define RefCountedGDIHandle_h + +#include <windows.h> +#include <wtf/HashFunctions.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +template <typename T> class RefCountedGDIHandle : public RefCounted<RefCountedGDIHandle<T> > { +public: + static PassRefPtr<RefCountedGDIHandle> create(T handle) + { + return adoptRef(new RefCountedGDIHandle<T>(handle)); + } + + static PassRefPtr<RefCountedGDIHandle<T> > createDeleted() + { + return adoptRef(new RefCountedGDIHandle<T>(reinterpret_cast<T>(-1))); + } + + ~RefCountedGDIHandle() + { + if (m_handle != reinterpret_cast<T>(-1)) + WTF::deleteOwnedPtr(m_handle); + } + + T handle() const + { + return m_handle; + } + + unsigned hash() const + { + return WTF::PtrHash<T>::hash(m_handle); + } + +private: + RefCountedGDIHandle(T handle) + : m_handle(handle) + { + } + + T m_handle; +}; + +} // namespace WebCore + +#endif // RefCountedGDIHandle_h diff --git a/Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp b/Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp new file mode 100644 index 0000000..20d42ff --- /dev/null +++ b/Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp @@ -0,0 +1,161 @@ +/* + * 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_platformData.syntheticBold() ? 1.0f : 0.f; + m_scriptCache = 0; + m_scriptFontProperties = 0; + m_isSystemFont = false; + + if (m_platformData.useGDI()) + return initGDIFont(); + + CGFontRef font = m_platformData.cgFont(); + int iAscent = CGFontGetAscent(font); + int iDescent = CGFontGetDescent(font); + int iLineGap = CGFontGetLeading(font); + m_unitsPerEm = CGFontGetUnitsPerEm(font); + float pointSize = m_platformData.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_platformData.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::platformCharWidthInit() +{ + // GDI Fonts init charwidths in initGDIFont. + if (!m_platformData.useGDI()) { + m_avgCharWidth = 0.f; + m_maxCharWidth = 0.f; + initCharWidths(); + } +} +FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const +{ + if (m_platformData.useGDI()) + return boundsForGDIGlyph(glyph); + + CGRect box; + CGFontGetGlyphBBoxes(m_platformData.cgFont(), &glyph, 1, &box); + float pointSize = m_platformData.size(); + CGFloat scale = pointSize / unitsPerEm(); + FloatRect boundingBox = CGRectApplyAffineTransform(box, CGAffineTransformMakeScale(scale, -scale)); + if (m_syntheticBoldOffset) + boundingBox.setWidth(boundingBox.width() + m_syntheticBoldOffset); + + return boundingBox; +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + if (m_platformData.useGDI()) + return widthForGDIGlyph(glyph); + + CGFontRef font = m_platformData.cgFont(); + float pointSize = m_platformData.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/Source/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp b/Source/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp new file mode 100644 index 0000000..62ea060 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp @@ -0,0 +1,130 @@ +/* + * 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 <cairo.h> +#include <cairo-win32.h> +#include <mlang.h> +#include <tchar.h> +#include <wtf/MathExtras.h> + +namespace WebCore { + +void SimpleFontData::platformInit() +{ + m_scriptCache = 0; + m_scriptFontProperties = 0; + m_isSystemFont = false; + + m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; + + if (m_platformData.useGDI()) + return initGDIFont(); + + HDC hdc = GetDC(0); + SaveDC(hdc); + + cairo_scaled_font_t* scaledFont = m_platformData.scaledFont(); + const double metricsMultiplier = cairo_win32_scaled_font_get_metrics_factor(scaledFont) * m_platformData.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; + m_avgCharWidth = lroundf(textMetrics.tmAveCharWidth * metricsMultiplier); + m_maxCharWidth = lroundf(textMetrics.tmMaxCharWidth * metricsMultiplier); + + 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::platformCharWidthInit() +{ + // charwidths are set in platformInit. +} + +FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const +{ + if (m_platformData.useGDI()) + return boundsForGDIGlyph(glyph); + //FIXME: Implement this + return FloatRect(); +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + if (m_platformData.useGDI()) + return widthForGDIGlyph(glyph); + + HDC hdc = GetDC(0); + SaveDC(hdc); + + cairo_scaled_font_t* scaledFont = m_platformData.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_platformData.size(); + return width * metricsMultiplier; +} + +} diff --git a/Source/WebCore/platform/graphics/win/SimpleFontDataWin.cpp b/Source/WebCore/platform/graphics/win/SimpleFontDataWin.cpp new file mode 100644 index 0000000..60afe6a --- /dev/null +++ b/Source/WebCore/platform/graphics/win/SimpleFontDataWin.cpp @@ -0,0 +1,259 @@ +/* + * 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 <mlang.h> +#include <tchar.h> + +#if PLATFORM(CG) +#include <ApplicationServices/ApplicationServices.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#endif + +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() +{ + if (!m_platformData.size()) { + m_ascent = 0; + m_descent = 0; + m_lineGap = 0; + m_lineSpacing = 0; + m_avgCharWidth = 0; + m_maxCharWidth = 0; + m_xHeight = 0; + m_unitsPerEm = 0; + return; + } + + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_platformData.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_avgCharWidth = textMetrics.tmAveCharWidth; + m_maxCharWidth = textMetrics.tmMaxCharWidth; + 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::platformDestroy() +{ + ScriptFreeCache(&m_scriptCache); + delete m_scriptFontProperties; +} + +SimpleFontData* SimpleFontData::scaledFontData(const FontDescription& fontDescription, float scaleFactor) const +{ + float scaledSize = scaleFactor * m_platformData.size(); + if (isCustomFont()) { + FontPlatformData scaledFont(m_platformData); + scaledFont.setSize(scaledSize); + return new SimpleFontData(scaledFont, true, false); + } + + LOGFONT winfont; + GetObject(m_platformData.hfont(), sizeof(LOGFONT), &winfont); + winfont.lfHeight = -lroundf(scaledSize * (m_platformData.useGDI() ? 1 : 32)); + HFONT hfont = CreateFontIndirect(&winfont); + return new SimpleFontData(FontPlatformData(hfont, scaledSize, m_platformData.syntheticBold(), m_platformData.syntheticOblique(), m_platformData.useGDI()), isCustomFont(), false); +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->smallCaps) + m_derivedFontData->smallCaps = scaledFontData(fontDescription, cSmallCapsFontSizeMultiplier); + + return m_derivedFontData->smallCaps.get(); +} + +SimpleFontData* SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const +{ + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->emphasisMark) + m_derivedFontData->emphasisMark = scaledFontData(fontDescription, .5); + + return m_derivedFontData->emphasisMark.get(); +} + +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_platformData.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_platformData.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); +} + +FloatRect SimpleFontData::boundsForGDIGlyph(Glyph glyph) const +{ + HDC hdc = GetDC(0); + SetGraphicsMode(hdc, GM_ADVANCED); + HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); + + GLYPHMETRICS gdiMetrics; + static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; + GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity); + + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + + return FloatRect(gdiMetrics.gmptGlyphOrigin.x, -gdiMetrics.gmptGlyphOrigin.y, + gdiMetrics.gmBlackBoxX + m_syntheticBoldOffset, gdiMetrics.gmBlackBoxY); +} + +float SimpleFontData::widthForGDIGlyph(Glyph glyph) const +{ + HDC hdc = GetDC(0); + SetGraphicsMode(hdc, GM_ADVANCED); + HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); + + GLYPHMETRICS gdiMetrics; + static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; + GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity); + + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + + return gdiMetrics.gmCellIncX + 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_platformData.hfont()); + ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties); + RestoreDC(dc, -1); + ReleaseDC(0, dc); + } + } + return m_scriptFontProperties; +} + +} diff --git a/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp b/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp new file mode 100644 index 0000000..47806a2 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 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 "TransformationMatrix.h" + +#include <windows.h> + +namespace WebCore { + +TransformationMatrix::operator XFORM() const +{ + XFORM xform; + xform.eM11 = a(); + xform.eM12 = b(); + xform.eM21 = c(); + xform.eM22 = d(); + xform.eDx = e(); + xform.eDy = f(); + + return xform; +} + +} diff --git a/Source/WebCore/platform/graphics/win/UniscribeController.cpp b/Source/WebCore/platform/graphics/win/UniscribeController.cpp new file mode 100644 index 0000000..ab32150 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/UniscribeController.cpp @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 INC. 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 INC. 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> + +using namespace std; + +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, HashSet<const SimpleFontData*>* fallbackFonts) + : m_font(*font) + , m_run(run) + , m_fallbackFonts(fallbackFonts) + , m_minGlyphBoundingBoxX(numeric_limits<float>::max()) + , m_maxGlyphBoundingBoxX(numeric_limits<float>::min()) + , m_minGlyphBoundingBoxY(numeric_limits<float>::max()) + , m_maxGlyphBoundingBoxY(numeric_limits<float>::min()) + , m_end(run.length()) + , m_currentCharacter(0) + , m_runWidthSoFar(0) + , m_padding(run.padding()) + , m_computingOffsetPosition(false) + , m_includePartialGlyphs(false) + , m_offsetX(0) + , m_offsetPosition(0) +{ + 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 = m_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 ? SmallCapsVariant : AutoVariant).fontData; + if (m_font.isSmallCaps()) { + nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c; + if (nextIsSmallCaps) + smallCapsBuffer[index] = forceSmallCaps ? c : newC; + } + + if (m_fallbackFonts && nextFontData != fontData && fontData != m_font.primaryFont()) + m_fallbackFonts->add(fontData); + + 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) { + if (m_fallbackFonts && nextFontData != m_font.primaryFont()) + m_fallbackFonts->add(nextFontData); + + 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->platformData().useGDI() ? 1.0f : 32.0f; + unsigned logicalSpaceWidth = fontData->spaceWidth() * cLogicalScale; + float roundedSpaceWidth = roundf(fontData->spaceWidth()); + + for (int k = 0; k < len; k++) { + UChar ch = *(str + k); + bool treatAsSpace = Font::treatAsSpace(ch); + bool treatAsZeroWidthSpace = ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch); + if (treatAsSpace || treatAsZeroWidthSpace) { + // Substitute in the space glyph at the appropriate place in the glyphs + // array. + glyphs[clusters[k]] = fontData->spaceGlyph(); + advances[clusters[k]] = treatAsSpace ? logicalSpaceWidth : 0; + if (treatAsSpace) + 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()) { + // When at the last character in the str, don't look one past the end for a rounding hack character. + // Instead look ahead to the first character of next item, if there is a next one. + if (k + 1 == len) { + if (i + 2 < m_items.size() // Check for at least 2 items remaining. The last item is a terminating item containing no characters. + && Font::isRoundingHackCharacter(*(cp + m_items[i + 1].iCharPos))) + roundingHackWordBoundaries[clusters[k]] = boundary; + } else if (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->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->pitch() == FixedPitch || glyph == fontData->spaceGlyph()) && + m_run.applyWordRounding()) + advance = fontData->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. + int characterIndex = spaceCharacters[k]; + // characterIndex is left at the initial value of -1 for glyphs that do not map back to treated-as-space characters. + if (characterIndex != -1) { + // 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 { + float previousPadding = m_padding; + m_padding -= m_padPerSpace; + advance += roundf(previousPadding) - roundf(m_padding); + } + } + + // Account for word-spacing. + 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); + } + + FloatRect glyphBounds = fontData->boundsForGlyph(glyph); + glyphBounds.move(m_glyphOrigin.x(), m_glyphOrigin.y()); + m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x()); + m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.right()); + m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); + m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.bottom()); + m_glyphOrigin.move(advance + offsetX, -offsetY); + + // 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/Source/WebCore/platform/graphics/win/UniscribeController.h b/Source/WebCore/platform/graphics/win/UniscribeController.h new file mode 100644 index 0000000..162ddbe --- /dev/null +++ b/Source/WebCore/platform/graphics/win/UniscribeController.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 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 INC. 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 INC. 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&, HashSet<const SimpleFontData*>* fallbackFonts = 0); + + // 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; } + + float minGlyphBoundingBoxX() const { return m_minGlyphBoundingBoxX; } + float maxGlyphBoundingBoxX() const { return m_maxGlyphBoundingBoxX; } + float minGlyphBoundingBoxY() const { return m_minGlyphBoundingBoxY; } + float maxGlyphBoundingBoxY() const { return m_maxGlyphBoundingBoxY; } + +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; + HashSet<const SimpleFontData*>* m_fallbackFonts; + FloatPoint m_glyphOrigin; + float m_minGlyphBoundingBoxX; + float m_maxGlyphBoundingBoxX; + float m_minGlyphBoundingBoxY; + float m_maxGlyphBoundingBoxY; + + 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 diff --git a/Source/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp b/Source/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp new file mode 100644 index 0000000..d75c854 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2009 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 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 USE(ACCELERATED_COMPOSITING) + +#include "WKCACFContextFlusher.h" + +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +WKCACFContextFlusher& WKCACFContextFlusher::shared() +{ + DEFINE_STATIC_LOCAL(WKCACFContextFlusher, flusher, ()); + return flusher; +} + +WKCACFContextFlusher::WKCACFContextFlusher() +{ +} + +WKCACFContextFlusher::~WKCACFContextFlusher() +{ +} + +void WKCACFContextFlusher::addContext(WKCACFContext* context) +{ + ASSERT(context); + + m_contexts.add(context); +} + +void WKCACFContextFlusher::removeContext(WKCACFContext* context) +{ + ASSERT(context); + + m_contexts.remove(context); +} + +void WKCACFContextFlusher::flushAllContexts() +{ + // addContext might get called beneath CACFContextFlush, and we don't want m_contexts to change while + // we're iterating over it, so we move the contexts into a local ContextSet and iterate over that instead. + ContextSet contextsToFlush; + contextsToFlush.swap(m_contexts); + + ContextSet::const_iterator end = contextsToFlush.end(); + for (ContextSet::const_iterator it = contextsToFlush.begin(); it != end; ++it) + wkCACFContextFlush(*it); +} + +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/win/WKCACFContextFlusher.h b/Source/WebCore/platform/graphics/win/WKCACFContextFlusher.h new file mode 100644 index 0000000..17ec41d --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WKCACFContextFlusher.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009 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 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 WKCACFContextFlusher_h +#define WKCACFContextFlusher_h + +#if USE(ACCELERATED_COMPOSITING) + +#include <wtf/Noncopyable.h> + +#include <wtf/HashSet.h> + +struct WKCACFContext; + +namespace WebCore { + +class WKCACFContextFlusher : public Noncopyable { +public: + static WKCACFContextFlusher& shared(); + + void addContext(WKCACFContext*); + void removeContext(WKCACFContext*); + + void flushAllContexts(); + +private: + WKCACFContextFlusher(); + ~WKCACFContextFlusher(); + + typedef HashSet<WKCACFContext*> ContextSet; + ContextSet m_contexts; +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WKCACFContextFlusher_h diff --git a/Source/WebCore/platform/graphics/win/WKCACFLayer.cpp b/Source/WebCore/platform/graphics/win/WKCACFLayer.cpp new file mode 100644 index 0000000..a8714e3 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WKCACFLayer.cpp @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2009 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 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 USE(ACCELERATED_COMPOSITING) + +#include "WKCACFLayer.h" + +#include "WKCACFLayerRenderer.h" +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <stdio.h> +#include <wtf/CurrentTime.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +using namespace std; + +#ifndef NDEBUG +void WKCACFLayer::internalCheckLayerConsistency() +{ + ASSERT(layer()); + size_t n = sublayerCount(); + for (size_t i = 0; i < n; ++i) { + // This will ASSERT in internalSublayerAtIndex if this entry doesn't have proper user data + WKCACFLayer* sublayer = internalSublayerAtIndex(i); + + // Make sure we don't have any null entries in the list + ASSERT(sublayer); + + // Make sure the each layer has a corresponding CACFLayer + ASSERT(sublayer->layer()); + } +} +#endif + +static void displayCallback(CACFLayerRef layer, CGContextRef context) +{ + ASSERT_ARG(layer, WKCACFLayer::layer(layer)); + WKCACFLayer::layer(layer)->drawInContext(context); +} + +static CFStringRef toCACFLayerType(WKCACFLayer::LayerType type) +{ + switch (type) { + case WKCACFLayer::Layer: return kCACFLayer; + case WKCACFLayer::TransformLayer: return kCACFTransformLayer; + default: return 0; + } +} + +static CFStringRef toCACFContentsGravityType(WKCACFLayer::ContentsGravityType type) +{ + switch (type) { + case WKCACFLayer::Center: return kCACFGravityCenter; + case WKCACFLayer::Top: return kCACFGravityTop; + case WKCACFLayer::Bottom: return kCACFGravityBottom; + case WKCACFLayer::Left: return kCACFGravityLeft; + case WKCACFLayer::Right: return kCACFGravityRight; + case WKCACFLayer::TopLeft: return kCACFGravityTopLeft; + case WKCACFLayer::TopRight: return kCACFGravityTopRight; + case WKCACFLayer::BottomLeft: return kCACFGravityBottomLeft; + case WKCACFLayer::BottomRight: return kCACFGravityBottomRight; + case WKCACFLayer::Resize: return kCACFGravityResize; + case WKCACFLayer::ResizeAspect: return kCACFGravityResizeAspect; + case WKCACFLayer::ResizeAspectFill: return kCACFGravityResizeAspectFill; + default: return 0; + } +} + +static WKCACFLayer::ContentsGravityType fromCACFContentsGravityType(CFStringRef string) +{ + if (CFEqual(string, kCACFGravityTop)) + return WKCACFLayer::Top; + + if (CFEqual(string, kCACFGravityBottom)) + return WKCACFLayer::Bottom; + + if (CFEqual(string, kCACFGravityLeft)) + return WKCACFLayer::Left; + + if (CFEqual(string, kCACFGravityRight)) + return WKCACFLayer::Right; + + if (CFEqual(string, kCACFGravityTopLeft)) + return WKCACFLayer::TopLeft; + + if (CFEqual(string, kCACFGravityTopRight)) + return WKCACFLayer::TopRight; + + if (CFEqual(string, kCACFGravityBottomLeft)) + return WKCACFLayer::BottomLeft; + + if (CFEqual(string, kCACFGravityBottomRight)) + return WKCACFLayer::BottomRight; + + if (CFEqual(string, kCACFGravityResize)) + return WKCACFLayer::Resize; + + if (CFEqual(string, kCACFGravityResizeAspect)) + return WKCACFLayer::ResizeAspect; + + if (CFEqual(string, kCACFGravityResizeAspectFill)) + return WKCACFLayer::ResizeAspectFill; + + return WKCACFLayer::Center; +} + +static CFStringRef toCACFFilterType(WKCACFLayer::FilterType type) +{ + switch (type) { + case WKCACFLayer::Linear: return kCACFFilterLinear; + case WKCACFLayer::Nearest: return kCACFFilterNearest; + case WKCACFLayer::Trilinear: return kCACFFilterTrilinear; + default: return 0; + } +} + +static WKCACFLayer::FilterType fromCACFFilterType(CFStringRef string) +{ + if (CFEqual(string, kCACFFilterNearest)) + return WKCACFLayer::Nearest; + + if (CFEqual(string, kCACFFilterTrilinear)) + return WKCACFLayer::Trilinear; + + return WKCACFLayer::Linear; +} + +PassRefPtr<WKCACFLayer> WKCACFLayer::create(LayerType type) +{ + if (!WKCACFLayerRenderer::acceleratedCompositingAvailable()) + return 0; + return adoptRef(new WKCACFLayer(type)); +} + +// FIXME: It might be good to have a way of ensuring that all WKCACFLayers eventually +// get destroyed in debug builds. A static counter could accomplish this pretty easily. + +WKCACFLayer::WKCACFLayer(LayerType type) + : m_layer(AdoptCF, CACFLayerCreate(toCACFLayerType(type))) + , m_layoutClient(0) + , m_needsDisplayOnBoundsChange(false) +{ + CACFLayerSetUserData(layer(), this); + CACFLayerSetDisplayCallback(layer(), displayCallback); +} + +WKCACFLayer::~WKCACFLayer() +{ + // Our superlayer should be holding a reference to us, so there should be no way for us to be destroyed while we still have a superlayer. + ASSERT(!superlayer()); + + // Get rid of the children so we don't have any dangling references around + removeAllSublayers(); + +#ifndef NDEBUG + CACFLayerSetUserData(layer(), reinterpret_cast<void*>(0xDEADBEEF)); +#else + CACFLayerSetUserData(layer(), 0); +#endif + CACFLayerSetDisplayCallback(layer(), 0); +} + +void WKCACFLayer::becomeRootLayerForContext(WKCACFContext* context) +{ + wkCACFContextSetLayer(context, layer()); + setNeedsCommit(); +} + +void WKCACFLayer::setNeedsCommit() +{ + WKCACFLayer* root = rootLayer(); + + // Call setNeedsRender on the root layer, which will cause a render to + // happen in WKCACFLayerRenderer + root->setNeedsRender(); +} + +bool WKCACFLayer::isTransformLayer() const +{ + return CACFLayerGetClass(layer()) == kCACFTransformLayer; +} + +void WKCACFLayer::addSublayer(PassRefPtr<WKCACFLayer> sublayer) +{ + insertSublayer(sublayer, sublayerCount()); +} + +void WKCACFLayer::internalInsertSublayer(PassRefPtr<WKCACFLayer> sublayer, size_t index) +{ + index = min(index, sublayerCount() + 1); + sublayer->removeFromSuperlayer(); + CACFLayerInsertSublayer(layer(), sublayer->layer(), index); + setNeedsCommit(); + checkLayerConsistency(); +} + +void WKCACFLayer::insertSublayerAboveLayer(PassRefPtr<WKCACFLayer> sublayer, const WKCACFLayer* reference) +{ + if (!reference) { + insertSublayer(sublayer, 0); + return; + } + + int referenceIndex = internalIndexOfSublayer(reference); + if (referenceIndex == -1) { + addSublayer(sublayer); + return; + } + + insertSublayer(sublayer, referenceIndex + 1); +} + +void WKCACFLayer::insertSublayerBelowLayer(PassRefPtr<WKCACFLayer> sublayer, const WKCACFLayer* reference) +{ + if (!reference) { + insertSublayer(sublayer, 0); + return; + } + + int referenceIndex = internalIndexOfSublayer(reference); + if (referenceIndex == -1) { + addSublayer(sublayer); + return; + } + + insertSublayer(sublayer, referenceIndex); +} + +void WKCACFLayer::replaceSublayer(WKCACFLayer* reference, PassRefPtr<WKCACFLayer> newLayer) +{ + ASSERT_ARG(reference, reference); + ASSERT_ARG(reference, reference->superlayer() == this); + + if (reference == newLayer) + return; + + int referenceIndex = internalIndexOfSublayer(reference); + ASSERT(referenceIndex != -1); + if (referenceIndex == -1) + return; + + reference->removeFromSuperlayer(); + + if (newLayer) { + newLayer->removeFromSuperlayer(); + insertSublayer(newLayer, referenceIndex); + } +} + +size_t WKCACFLayer::internalSublayerCount() const +{ + CFArrayRef sublayers = CACFLayerGetSublayers(layer()); + return sublayers ? CFArrayGetCount(sublayers) : 0; +} + +void WKCACFLayer::adoptSublayers(WKCACFLayer* source) +{ + // We will use setSublayers() because it properly nulls + // out the superlayer pointer. + Vector<RefPtr<WKCACFLayer> > sublayers; + size_t n = source->sublayerCount(); + + for (size_t i = 0; i < n; ++i) + sublayers.append(source->internalSublayerAtIndex(i)); + + setSublayers(sublayers); + source->checkLayerConsistency(); +} + +void WKCACFLayer::removeFromSuperlayer() +{ + WKCACFLayer* superlayer = this->superlayer(); + CACFLayerRemoveFromSuperlayer(layer()); + checkLayerConsistency(); + + if (superlayer) + superlayer->setNeedsCommit(); +} + +WKCACFLayer* WKCACFLayer::internalSublayerAtIndex(int index) const +{ + CFArrayRef sublayers = CACFLayerGetSublayers(layer()); + if (!sublayers || index < 0 || CFArrayGetCount(sublayers) <= index) + return 0; + + return layer(static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index)))); +} + +int WKCACFLayer::internalIndexOfSublayer(const WKCACFLayer* reference) +{ + CACFLayerRef ref = reference->layer(); + if (!ref) + return -1; + + CFArrayRef sublayers = CACFLayerGetSublayers(layer()); + if (!sublayers) + return -1; + + size_t n = CFArrayGetCount(sublayers); + + for (size_t i = 0; i < n; ++i) + if (CFArrayGetValueAtIndex(sublayers, i) == ref) + return i; + + return -1; +} + +WKCACFLayer* WKCACFLayer::ancestorOrSelfWithSuperlayer(WKCACFLayer* superlayer) const +{ + WKCACFLayer* layer = const_cast<WKCACFLayer*>(this); + for (WKCACFLayer* ancestor = this->superlayer(); ancestor; layer = ancestor, ancestor = ancestor->superlayer()) { + if (ancestor == superlayer) + return layer; + } + return 0; +} + +void WKCACFLayer::setBounds(const CGRect& rect) +{ + if (CGRectEqualToRect(rect, bounds())) + return; + + CACFLayerSetBounds(layer(), rect); + setNeedsCommit(); + + if (m_needsDisplayOnBoundsChange) + setNeedsDisplay(); + + if (m_layoutClient) + setNeedsLayout(); +} + +void WKCACFLayer::setFrame(const CGRect& rect) +{ + CGRect oldFrame = frame(); + if (CGRectEqualToRect(rect, oldFrame)) + return; + + CACFLayerSetFrame(layer(), rect); + setNeedsCommit(); + + if (m_needsDisplayOnBoundsChange && !CGSizeEqualToSize(rect.size, oldFrame.size)) + setNeedsDisplay(); + + if (m_layoutClient) + setNeedsLayout(); +} + +void WKCACFLayer::setContentsGravity(ContentsGravityType type) +{ + CACFLayerSetContentsGravity(layer(), toCACFContentsGravityType(type)); + setNeedsCommit(); +} + +WKCACFLayer::ContentsGravityType WKCACFLayer::contentsGravity() const +{ + return fromCACFContentsGravityType(CACFLayerGetContentsGravity(layer())); +} + +void WKCACFLayer::setMagnificationFilter(FilterType type) +{ + CACFLayerSetMagnificationFilter(layer(), toCACFFilterType(type)); + setNeedsCommit(); +} + +WKCACFLayer::FilterType WKCACFLayer::magnificationFilter() const +{ + return fromCACFFilterType(CACFLayerGetMagnificationFilter(layer())); +} + +void WKCACFLayer::setMinificationFilter(FilterType type) +{ + CACFLayerSetMinificationFilter(layer(), toCACFFilterType(type)); + setNeedsCommit(); +} + +WKCACFLayer::FilterType WKCACFLayer::minificationFilter() const +{ + return fromCACFFilterType(CACFLayerGetMinificationFilter(layer())); +} + +WKCACFLayer* WKCACFLayer::rootLayer() const +{ + WKCACFLayer* layer = const_cast<WKCACFLayer*>(this); + for (WKCACFLayer* superlayer = layer->superlayer(); superlayer; layer = superlayer, superlayer = superlayer->superlayer()) { } + return layer; +} + +void WKCACFLayer::internalRemoveAllSublayers() +{ + CACFLayerSetSublayers(layer(), 0); + setNeedsCommit(); +} + +void WKCACFLayer::internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) +{ + // Remove all the current sublayers and add the passed layers + CACFLayerSetSublayers(layer(), 0); + + // Perform removeFromSuperLayer in a separate pass. CACF requires superlayer to + // be null or CACFLayerInsertSublayer silently fails. + for (size_t i = 0; i < sublayers.size(); i++) + CACFLayerRemoveFromSuperlayer(sublayers[i]->layer()); + + for (size_t i = 0; i < sublayers.size(); i++) + CACFLayerInsertSublayer(layer(), sublayers[i]->layer(), i); + + setNeedsCommit(); +} + +WKCACFLayer* WKCACFLayer::superlayer() const +{ + CACFLayerRef super = CACFLayerGetSuperlayer(layer()); + if (!super) + return 0; + return WKCACFLayer::layer(super); +} + +void WKCACFLayer::internalSetNeedsDisplay(const CGRect* dirtyRect) +{ + CACFLayerSetNeedsDisplay(layer(), dirtyRect); +} + +void WKCACFLayer::setLayoutClient(WKCACFLayerLayoutClient* layoutClient) +{ + if (layoutClient == m_layoutClient) + return; + + m_layoutClient = layoutClient; + CACFLayerSetLayoutCallback(layer(), m_layoutClient ? layoutSublayersProc : 0); +} + +void WKCACFLayer::layoutSublayersProc(CACFLayerRef caLayer) +{ + WKCACFLayer* layer = WKCACFLayer::layer(caLayer); + if (layer && layer->m_layoutClient) + layer->m_layoutClient->layoutSublayersOfLayer(layer); +} + +#ifndef NDEBUG +static void printIndent(int indent) +{ + for ( ; indent > 0; --indent) + fprintf(stderr, " "); +} + +static void printTransform(const CATransform3D& transform) +{ + fprintf(stderr, "[%g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g]", + transform.m11, transform.m12, transform.m13, transform.m14, + transform.m21, transform.m22, transform.m23, transform.m24, + transform.m31, transform.m32, transform.m33, transform.m34, + transform.m41, transform.m42, transform.m43, transform.m44); +} + +void WKCACFLayer::printTree() const +{ + // Print heading info + CGRect rootBounds = bounds(); + fprintf(stderr, "\n\n** Render tree at time %g (bounds %g, %g %gx%g) **\n\n", + currentTime(), rootBounds.origin.x, rootBounds.origin.y, rootBounds.size.width, rootBounds.size.height); + + // Print layer tree from the root + printLayer(0); +} + +void WKCACFLayer::printLayer(int indent) const +{ + CGPoint layerPosition = position(); + CGPoint layerAnchorPoint = anchorPoint(); + CGRect layerBounds = bounds(); + printIndent(indent); + fprintf(stderr, "(%s [%g %g %g] [%g %g %g %g] [%g %g %g] superlayer=%p\n", + isTransformLayer() ? "transform-layer" : "layer", + layerPosition.x, layerPosition.y, zPosition(), + layerBounds.origin.x, layerBounds.origin.y, layerBounds.size.width, layerBounds.size.height, + layerAnchorPoint.x, layerAnchorPoint.y, anchorPointZ(), superlayer()); + + // Print name if needed + String layerName = name(); + if (!layerName.isEmpty()) { + printIndent(indent + 1); + fprintf(stderr, "(name %s)\n", layerName.utf8().data()); + } + + // Print masksToBounds if needed + bool layerMasksToBounds = masksToBounds(); + if (layerMasksToBounds) { + printIndent(indent + 1); + fprintf(stderr, "(masksToBounds true)\n"); + } + + // Print opacity if needed + float layerOpacity = opacity(); + if (layerOpacity != 1) { + printIndent(indent + 1); + fprintf(stderr, "(opacity %hf)\n", layerOpacity); + } + + // Print sublayerTransform if needed + CATransform3D layerTransform = sublayerTransform(); + if (!CATransform3DIsIdentity(layerTransform)) { + printIndent(indent + 1); + fprintf(stderr, "(sublayerTransform "); + printTransform(layerTransform); + fprintf(stderr, ")\n"); + } + + // Print transform if needed + layerTransform = transform(); + if (!CATransform3DIsIdentity(layerTransform)) { + printIndent(indent + 1); + fprintf(stderr, "(transform "); + printTransform(layerTransform); + fprintf(stderr, ")\n"); + } + + // Print contents if needed + CFTypeRef layerContents = contents(); + if (layerContents) { + if (CFGetTypeID(layerContents) == CGImageGetTypeID()) { + CGImageRef imageContents = static_cast<CGImageRef>(const_cast<void*>(layerContents)); + printIndent(indent + 1); + fprintf(stderr, "(contents (image [%d %d]))\n", + CGImageGetWidth(imageContents), CGImageGetHeight(imageContents)); + } + } + + // Print sublayers if needed + int n = sublayerCount(); + if (n > 0) { + printIndent(indent + 1); + fprintf(stderr, "(sublayers\n"); + for (int i = 0; i < n; ++i) + internalSublayerAtIndex(i)->printLayer(indent + 2); + + printIndent(indent + 1); + fprintf(stderr, ")\n"); + } + + printIndent(indent); + fprintf(stderr, ")\n"); +} +#endif // #ifndef NDEBUG +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/win/WKCACFLayer.h b/Source/WebCore/platform/graphics/win/WKCACFLayer.h new file mode 100644 index 0000000..4c6639a --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WKCACFLayer.h @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2009 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 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 WKCACFLayer_h +#define WKCACFLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include <wtf/RefCounted.h> + +#include <QuartzCore/CACFLayer.h> +#include <QuartzCore/CACFVector.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +#include "GraphicsContext.h" +#include "PlatformString.h" +#include "TransformationMatrix.h" + +struct WKCACFContext; + +namespace WebCore { + +class WKCACFLayer; + +class WKCACFLayerLayoutClient { +public: + virtual void layoutSublayersOfLayer(WKCACFLayer*) = 0; +protected: + virtual ~WKCACFLayerLayoutClient() {} +}; + +class WKCACFLayer : public RefCounted<WKCACFLayer> { +public: + enum LayerType { Layer, TransformLayer }; + enum FilterType { Linear, Nearest, Trilinear }; + enum ContentsGravityType { Center, Top, Bottom, Left, Right, TopLeft, TopRight, + BottomLeft, BottomRight, Resize, ResizeAspect, ResizeAspectFill }; + + static PassRefPtr<WKCACFLayer> create(LayerType); + static WKCACFLayer* layer(CACFLayerRef layer) + { + ASSERT(CACFLayerGetUserData(layer) != reinterpret_cast<void*>(0xDEADBEEF)); + return static_cast<WKCACFLayer*>(CACFLayerGetUserData(layer)); + } + + virtual ~WKCACFLayer(); + + virtual void setNeedsRender() { } + + virtual void drawInContext(PlatformGraphicsContext*) { } + + void setLayoutClient(WKCACFLayerLayoutClient*); + WKCACFLayerLayoutClient* layoutClient() const { return m_layoutClient; } + void setNeedsLayout() { CACFLayerSetNeedsLayout(layer()); } + + void setNeedsDisplay(const CGRect* dirtyRect = 0) + { + internalSetNeedsDisplay(dirtyRect); + setNeedsCommit(); + } + + // Makes this layer the root when the passed context is rendered + void becomeRootLayerForContext(WKCACFContext*); + + static RetainPtr<CFTypeRef> cfValue(float value) { return RetainPtr<CFTypeRef>(AdoptCF, CFNumberCreate(0, kCFNumberFloat32Type, &value)); } + static RetainPtr<CFTypeRef> cfValue(const TransformationMatrix& value) + { + CATransform3D t; + t.m11 = value.m11(); + t.m12 = value.m12(); + t.m13 = value.m13(); + t.m14 = value.m14(); + t.m21 = value.m21(); + t.m22 = value.m22(); + t.m23 = value.m23(); + t.m24 = value.m24(); + t.m31 = value.m31(); + t.m32 = value.m32(); + t.m33 = value.m33(); + t.m34 = value.m34(); + t.m41 = value.m41(); + t.m42 = value.m42(); + t.m43 = value.m43(); + t.m44 = value.m44(); + return RetainPtr<CFTypeRef>(AdoptCF, CACFVectorCreateTransform(t)); + } + static RetainPtr<CFTypeRef> cfValue(const FloatPoint& value) + { + CGPoint p; + p.x = value.x(); p.y = value.y(); + return RetainPtr<CFTypeRef>(AdoptCF, CACFVectorCreatePoint(p)); + } + static RetainPtr<CFTypeRef> cfValue(const FloatRect& rect) + { + CGRect r; + r.origin.x = rect.x(); + r.origin.y = rect.y(); + r.size.width = rect.width(); + r.size.height = rect.height(); + CGFloat v[4] = { CGRectGetMinX(r), CGRectGetMinY(r), CGRectGetMaxX(r), CGRectGetMaxY(r) }; + return RetainPtr<CFTypeRef>(AdoptCF, CACFVectorCreate(4, v)); + } + static RetainPtr<CFTypeRef> cfValue(const Color& color) + { + return RetainPtr<CFTypeRef>(AdoptCF, CGColorCreateGenericRGB(color.red(), color.green(), color.blue(), color.alpha())); + } + + bool isTransformLayer() const; + + void addSublayer(PassRefPtr<WKCACFLayer> sublayer); + void insertSublayerAboveLayer(PassRefPtr<WKCACFLayer>, const WKCACFLayer* reference); + void insertSublayerBelowLayer(PassRefPtr<WKCACFLayer>, const WKCACFLayer* reference); + void replaceSublayer(WKCACFLayer* reference, PassRefPtr<WKCACFLayer>); + void adoptSublayers(WKCACFLayer* source); + + void removeAllSublayers() { internalRemoveAllSublayers(); } + void setSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) + { + internalSetSublayers(sublayers); + checkLayerConsistency(); + } + + void insertSublayer(PassRefPtr<WKCACFLayer> layer, size_t index) { internalInsertSublayer(layer, index); } + + size_t sublayerCount() const { return internalSublayerCount(); } + + void removeFromSuperlayer(); + + WKCACFLayer* ancestorOrSelfWithSuperlayer(WKCACFLayer*) const; + + void setAnchorPoint(const CGPoint& p) { CACFLayerSetAnchorPoint(layer(), p); setNeedsCommit(); } + CGPoint anchorPoint() const { return CACFLayerGetAnchorPoint(layer()); } + + void setAnchorPointZ(CGFloat z) { CACFLayerSetAnchorPointZ(layer(), z); setNeedsCommit(); } + CGFloat anchorPointZ() const { return CACFLayerGetAnchorPointZ(layer()); } + + void setBackgroundColor(CGColorRef color) { CACFLayerSetBackgroundColor(layer(), color); setNeedsCommit(); } + CGColorRef backgroundColor() const { return CACFLayerGetBackgroundColor(layer()); } + + void setBorderColor(CGColorRef color) { CACFLayerSetBorderColor(layer(), color); setNeedsCommit(); } + CGColorRef borderColor() const { return CACFLayerGetBorderColor(layer()); } + + void setBorderWidth(CGFloat width) { CACFLayerSetBorderWidth(layer(), width); setNeedsCommit(); } + CGFloat borderWidth() const { return CACFLayerGetBorderWidth(layer()); } + + virtual void setBounds(const CGRect&); + CGRect bounds() const { return CACFLayerGetBounds(layer()); } + + void setContents(CFTypeRef contents) { CACFLayerSetContents(layer(), contents); setNeedsCommit(); } + CFTypeRef contents() const { return CACFLayerGetContents(layer()); } + + void setContentsRect(const CGRect& contentsRect) { CACFLayerSetContentsRect(layer(), contentsRect); setNeedsCommit(); } + CGRect contentsRect() const { return CACFLayerGetContentsRect(layer()); } + + void setContentsGravity(ContentsGravityType); + ContentsGravityType contentsGravity() const; + + void setDoubleSided(bool b) { CACFLayerSetDoubleSided(layer(), b); setNeedsCommit(); } + bool doubleSided() const { return CACFLayerIsDoubleSided(layer()); } + + void setEdgeAntialiasingMask(uint32_t mask) { CACFLayerSetEdgeAntialiasingMask(layer(), mask); setNeedsCommit(); } + uint32_t edgeAntialiasingMask() const { return CACFLayerGetEdgeAntialiasingMask(layer()); } + + virtual void setFrame(const CGRect&); + CGRect frame() const { return CACFLayerGetFrame(layer()); } + + void setHidden(bool hidden) { CACFLayerSetHidden(layer(), hidden); setNeedsCommit(); } + bool isHidden() const { return CACFLayerIsHidden(layer()); } + + void setMasksToBounds(bool b) { CACFLayerSetMasksToBounds(layer(), b); } + bool masksToBounds() const { return CACFLayerGetMasksToBounds(layer()); } + + void setMagnificationFilter(FilterType); + FilterType magnificationFilter() const; + + void setMinificationFilter(FilterType); + FilterType minificationFilter() const; + + void setMinificationFilterBias(float bias) { CACFLayerSetMinificationFilterBias(layer(), bias); } + float minificationFilterBias() const { return CACFLayerGetMinificationFilterBias(layer()); } + + void setName(const String& name) { CACFLayerSetName(layer(), RetainPtr<CFStringRef>(AdoptCF, name.createCFString()).get()); } + String name() const { return CACFLayerGetName(layer()); } + + void setNeedsDisplayOnBoundsChange(bool needsDisplay) { m_needsDisplayOnBoundsChange = needsDisplay; } + + void setOpacity(float opacity) { CACFLayerSetOpacity(layer(), opacity); setNeedsCommit(); } + float opacity() const { return CACFLayerGetOpacity(layer()); } + + void setOpaque(bool b) { CACFLayerSetOpaque(layer(), b); setNeedsCommit(); } + bool opaque() const { return CACFLayerIsOpaque(layer()); } + + void setPosition(const CGPoint& position) { CACFLayerSetPosition(layer(), position); setNeedsCommit(); } + CGPoint position() const { return CACFLayerGetPosition(layer()); } + + void setZPosition(CGFloat position) { CACFLayerSetZPosition(layer(), position); setNeedsCommit(); } + CGFloat zPosition() const { return CACFLayerGetZPosition(layer()); } + + void setSpeed(float speed) { CACFLayerSetSpeed(layer(), speed); } + CFTimeInterval speed() const { return CACFLayerGetSpeed(layer()); } + + void setTimeOffset(CFTimeInterval t) { CACFLayerSetTimeOffset(layer(), t); } + CFTimeInterval timeOffset() const { return CACFLayerGetTimeOffset(layer()); } + + WKCACFLayer* rootLayer() const; + + void setSublayerTransform(const CATransform3D& transform) { CACFLayerSetSublayerTransform(layer(), transform); setNeedsCommit(); } + CATransform3D sublayerTransform() const { return CACFLayerGetSublayerTransform(layer()); } + + WKCACFLayer* superlayer() const; + + void setTransform(const CATransform3D& transform) { CACFLayerSetTransform(layer(), transform); setNeedsCommit(); } + CATransform3D transform() const { return CACFLayerGetTransform(layer()); } + + void setGeometryFlipped(bool flipped) { CACFLayerSetGeometryFlipped(layer(), flipped); setNeedsCommit(); } + bool geometryFlipped() const { return CACFLayerIsGeometryFlipped(layer()); } + +#ifndef NDEBUG + // Print the tree from the root. Also does consistency checks + void printTree() const; +#endif + +protected: + WKCACFLayer(LayerType); + + void setNeedsCommit(); + + CACFLayerRef layer() const { return m_layer.get(); } + // This should only be called from removeFromSuperlayer. + void removeSublayer(const WKCACFLayer*); + + void checkLayerConsistency() + { +#ifndef NDEBUG + internalCheckLayerConsistency(); +#endif + } + + // Methods to be overridden for sublayer and rendering management + virtual WKCACFLayer* internalSublayerAtIndex(int) const; + + // Returns the index of the passed layer in this layer's sublayers list + // or -1 if not found + virtual int internalIndexOfSublayer(const WKCACFLayer*); + + virtual size_t internalSublayerCount() const; + virtual void internalInsertSublayer(PassRefPtr<WKCACFLayer>, size_t index); + virtual void internalRemoveAllSublayers(); + virtual void internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >&); + + virtual void internalSetNeedsDisplay(const CGRect* dirtyRect); + +#ifndef NDEBUG + virtual void internalCheckLayerConsistency(); +#endif + +#ifndef NDEBUG + // Print this layer and its children to the console + void printLayer(int indent) const; +#endif + +private: + static void layoutSublayersProc(CACFLayerRef); + + RetainPtr<CACFLayerRef> m_layer; + WKCACFLayerLayoutClient* m_layoutClient; + bool m_needsDisplayOnBoundsChange; +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WKCACFLayer_h diff --git a/Source/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp b/Source/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp new file mode 100644 index 0000000..4c5e61d --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2009 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 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 USE(ACCELERATED_COMPOSITING) + +#ifndef NDEBUG +#define D3D_DEBUG_INFO +#endif + +#include "WKCACFLayerRenderer.h" + +#include "PlatformCALayer.h" +#include "WKCACFContextFlusher.h" +#include "WebCoreInstanceHandle.h" +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <limits.h> +#include <wtf/CurrentTime.h> +#include <wtf/HashMap.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/StdLibExtras.h> +#include <d3d9.h> +#include <d3dx9.h> + +using namespace std; + +#pragma comment(lib, "d3d9") +#pragma comment(lib, "d3dx9") +#ifdef DEBUG_ALL +#pragma comment(lib, "QuartzCore_debug") +#else +#pragma comment(lib, "QuartzCore") +#endif + +static IDirect3D9* s_d3d = 0; +static IDirect3D9* d3d() +{ + if (s_d3d) + return s_d3d; + + if (!LoadLibrary(TEXT("d3d9.dll"))) + return 0; + + s_d3d = Direct3DCreate9(D3D_SDK_VERSION); + + return s_d3d; +} + +inline static CGRect winRectToCGRect(RECT rc) +{ + return CGRectMake(rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top)); +} + +inline static CGRect winRectToCGRect(RECT rc, RECT relativeToRect) +{ + return CGRectMake(rc.left, (relativeToRect.bottom-rc.bottom), (rc.right - rc.left), (rc.bottom - rc.top)); +} + +namespace WebCore { + +static D3DPRESENT_PARAMETERS initialPresentationParameters() +{ + D3DPRESENT_PARAMETERS parameters = {0}; + parameters.Windowed = TRUE; + parameters.SwapEffect = D3DSWAPEFFECT_COPY; + parameters.BackBufferCount = 1; + parameters.BackBufferFormat = D3DFMT_A8R8G8B8; + parameters.MultiSampleType = D3DMULTISAMPLE_NONE; + + return parameters; +} + +// FIXME: <rdar://6507851> Share this code with CoreAnimation. +static bool hardwareCapabilitiesIndicateCoreAnimationSupport(const D3DCAPS9& caps) +{ + // CoreAnimation needs two or more texture units. + if (caps.MaxTextureBlendStages < 2) + return false; + + // CoreAnimation needs non-power-of-two textures. + if ((caps.TextureCaps & D3DPTEXTURECAPS_POW2) && !(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) + return false; + + // CoreAnimation needs vertex shader 2.0 or greater. + if (D3DSHADER_VERSION_MAJOR(caps.VertexShaderVersion) < 2) + return false; + + // CoreAnimation needs pixel shader 2.0 or greater. + if (D3DSHADER_VERSION_MAJOR(caps.PixelShaderVersion) < 2) + return false; + + return true; +} + +bool WKCACFLayerRenderer::acceleratedCompositingAvailable() +{ + static bool available; + static bool tested; + + if (tested) + return available; + + tested = true; + + // Initialize available to true since this function will be called from a + // propagation within createRenderer(). We want to be able to return true + // when that happens so that the test can continue. + available = true; + + HMODULE library = LoadLibrary(TEXT("d3d9.dll")); + if (!library) { + available = false; + return available; + } + + FreeLibrary(library); +#ifdef DEBUG_ALL + library = LoadLibrary(TEXT("QuartzCore_debug.dll")); +#else + library = LoadLibrary(TEXT("QuartzCore.dll")); +#endif + if (!library) { + available = false; + return available; + } + + FreeLibrary(library); + + // Make a dummy HWND. + WNDCLASSEX wcex = { 0 }; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.lpfnWndProc = DefWindowProc; + wcex.hInstance = WebCore::instanceHandle(); + wcex.lpszClassName = L"CoreAnimationTesterWindowClass"; + ::RegisterClassEx(&wcex); + HWND testWindow = ::CreateWindow(L"CoreAnimationTesterWindowClass", L"CoreAnimationTesterWindow", WS_POPUP, -500, -500, 0, 0, 0, 0, 0, 0); + + if (!testWindow) { + available = false; + return available; + } + + OwnPtr<WKCACFLayerRenderer> testLayerRenderer = WKCACFLayerRenderer::create(0); + testLayerRenderer->setHostWindow(testWindow); + available = testLayerRenderer->createRenderer(); + ::DestroyWindow(testWindow); + + return available; +} + +PassOwnPtr<WKCACFLayerRenderer> WKCACFLayerRenderer::create(WKCACFLayerRendererClient* client) +{ + if (!acceleratedCompositingAvailable()) + return 0; + return new WKCACFLayerRenderer(client); +} + +WKCACFLayerRenderer::WKCACFLayerRenderer(WKCACFLayerRendererClient* client) + : m_client(client) + , m_mightBeAbleToCreateDeviceLater(true) + , m_rootLayer(PlatformCALayer::create(PlatformCALayer::LayerTypeRootLayer, 0)) + , m_context(wkCACFContextCreate()) + , m_hostWindow(0) + , m_renderTimer(this, &WKCACFLayerRenderer::renderTimerFired) + , m_backingStoreDirty(false) + , m_mustResetLostDeviceBeforeRendering(false) + , m_syncLayerChanges(false) +{ + // Point the CACFContext to this + wkCACFContextSetUserData(m_context, this); + + // Under the root layer, we have a clipping layer to clip the content, + // that contains a scroll layer that we use for scrolling the content. + // The root layer is the size of the client area of the window. + // The clipping layer is the size of the WebView client area (window less the scrollbars). + // The scroll layer is the size of the root child layer. + // Resizing the window will change the bounds of the rootLayer and the clip layer and will not + // cause any repositioning. + // Scrolling will affect only the position of the scroll layer without affecting the bounds. + + m_rootLayer->setName("WKCACFLayerRenderer rootLayer"); + m_rootLayer->setAnchorPoint(FloatPoint3D(0, 0, 0)); + m_rootLayer->setGeometryFlipped(true); + +#ifndef NDEBUG + CGColorRef debugColor = CGColorCreateGenericRGB(1, 0, 0, 0.8); + m_rootLayer->setBackgroundColor(debugColor); + CGColorRelease(debugColor); +#endif + + if (m_context) + wkCACFContextSetLayer(m_context, m_rootLayer->platformLayer()); + +#ifndef NDEBUG + char* printTreeFlag = getenv("CA_PRINT_TREE"); + m_printTree = printTreeFlag && atoi(printTreeFlag); +#endif +} + +WKCACFLayerRenderer::~WKCACFLayerRenderer() +{ + destroyRenderer(); + wkCACFContextDestroy(m_context); +} + +PlatformCALayer* WKCACFLayerRenderer::rootLayer() const +{ + return m_rootLayer.get(); +} + +void WKCACFLayerRenderer::addPendingAnimatedLayer(PassRefPtr<PlatformCALayer> layer) +{ + m_pendingAnimatedLayers.add(layer); +} + +void WKCACFLayerRenderer::setRootContents(CGImageRef image) +{ + ASSERT(m_rootLayer); + m_rootLayer->setContents(image); + renderSoon(); +} + +void WKCACFLayerRenderer::setRootContentsAndDisplay(CGImageRef image) +{ + ASSERT(m_rootLayer); + m_rootLayer->setContents(image); + paint(); +} + +void WKCACFLayerRenderer::setRootChildLayer(PlatformCALayer* layer) +{ + m_rootLayer->removeAllSublayers(); + m_rootChildLayer = layer; + if (m_rootChildLayer) + m_rootLayer->appendSublayer(m_rootChildLayer.get()); +} + +void WKCACFLayerRenderer::layerTreeDidChange() +{ + WKCACFContextFlusher::shared().addContext(m_context); + renderSoon(); +} + +void WKCACFLayerRenderer::setNeedsDisplay(bool sync) +{ + if (!m_syncLayerChanges && sync) + m_syncLayerChanges = true; + + ASSERT(m_rootLayer); + m_rootLayer->setNeedsDisplay(0); + renderSoon(); +} + +bool WKCACFLayerRenderer::createRenderer() +{ + if (m_d3dDevice || !m_mightBeAbleToCreateDeviceLater) + return m_d3dDevice; + + m_mightBeAbleToCreateDeviceLater = false; + D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); + + if (!d3d() || !::IsWindow(m_hostWindow)) + return false; + + // D3D doesn't like to make back buffers for 0 size windows. We skirt this problem if we make the + // passed backbuffer width and height non-zero. The window will necessarily get set to a non-zero + // size eventually, and then the backbuffer size will get reset. + RECT rect; + GetClientRect(m_hostWindow, &rect); + + if (rect.left-rect.right == 0 || rect.bottom-rect.top == 0) { + parameters.BackBufferWidth = 1; + parameters.BackBufferHeight = 1; + } + + D3DCAPS9 d3dCaps; + if (FAILED(d3d()->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps))) + return false; + + DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE; + if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && d3dCaps.VertexProcessingCaps) + behaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; + else + behaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + + COMPtr<IDirect3DDevice9> device; + if (FAILED(d3d()->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hostWindow, behaviorFlags, ¶meters, &device))) { + // In certain situations (e.g., shortly after waking from sleep), Direct3DCreate9() will + // return an IDirect3D9 for which IDirect3D9::CreateDevice will always fail. In case we + // have one of these bad IDirect3D9s, get rid of it so we'll fetch a new one the next time + // we want to call CreateDevice. + s_d3d->Release(); + s_d3d = 0; + + // Even if we don't have a bad IDirect3D9, in certain situations (e.g., shortly after + // waking from sleep), CreateDevice will fail, but will later succeed if called again. + m_mightBeAbleToCreateDeviceLater = true; + + return false; + } + + // Now that we've created the IDirect3DDevice9 based on the capabilities we + // got from the IDirect3D9 global object, we requery the device for its + // actual capabilities. The capabilities returned by the device can + // sometimes be more complete, for example when using software vertex + // processing. + D3DCAPS9 deviceCaps; + if (FAILED(device->GetDeviceCaps(&deviceCaps))) + return false; + + if (!hardwareCapabilitiesIndicateCoreAnimationSupport(deviceCaps)) + return false; + + m_d3dDevice = device; + + initD3DGeometry(); + + wkCACFContextInitializeD3DDevice(m_context, m_d3dDevice.get()); + + if (IsWindow(m_hostWindow)) + m_rootLayer->setBounds(bounds()); + + return true; +} + +void WKCACFLayerRenderer::destroyRenderer() +{ + wkCACFContextSetLayer(m_context, m_rootLayer->platformLayer()); + + m_d3dDevice = 0; + if (s_d3d) + s_d3d->Release(); + + s_d3d = 0; + m_rootLayer = 0; + m_rootChildLayer = 0; + + m_mightBeAbleToCreateDeviceLater = true; +} + +void WKCACFLayerRenderer::resize() +{ + if (!m_d3dDevice) + return; + + // Resetting the device might fail here. But that's OK, because if it does it we will attempt to + // reset the device the next time we try to render. + resetDevice(ChangedWindowSize); + + if (m_rootLayer) { + m_rootLayer->setBounds(bounds()); + WKCACFContextFlusher::shared().flushAllContexts(); + } +} + +static void getDirtyRects(HWND window, Vector<CGRect>& outRects) +{ + ASSERT_ARG(outRects, outRects.isEmpty()); + + RECT clientRect; + if (!GetClientRect(window, &clientRect)) + return; + + OwnPtr<HRGN> region(CreateRectRgn(0, 0, 0, 0)); + int regionType = GetUpdateRgn(window, region.get(), false); + if (regionType != COMPLEXREGION) { + RECT dirtyRect; + if (GetUpdateRect(window, &dirtyRect, false)) + outRects.append(winRectToCGRect(dirtyRect, clientRect)); + return; + } + + DWORD dataSize = GetRegionData(region.get(), 0, 0); + OwnArrayPtr<unsigned char> regionDataBuffer(new unsigned char[dataSize]); + RGNDATA* regionData = reinterpret_cast<RGNDATA*>(regionDataBuffer.get()); + if (!GetRegionData(region.get(), dataSize, regionData)) + return; + + outRects.resize(regionData->rdh.nCount); + + RECT* rect = reinterpret_cast<RECT*>(regionData->Buffer); + for (size_t i = 0; i < outRects.size(); ++i, ++rect) + outRects[i] = winRectToCGRect(*rect, clientRect); +} + +void WKCACFLayerRenderer::renderTimerFired(Timer<WKCACFLayerRenderer>*) +{ + paint(); +} + +void WKCACFLayerRenderer::paint() +{ + createRenderer(); + if (!m_d3dDevice) { + if (m_mightBeAbleToCreateDeviceLater) + renderSoon(); + return; + } + + if (m_backingStoreDirty) { + // If the backing store is still dirty when we are about to draw the + // composited content, we need to force the window to paint into the + // backing store. The paint will only paint the dirty region that + // if being tracked in WebView. + UpdateWindow(m_hostWindow); + return; + } + + Vector<CGRect> dirtyRects; + getDirtyRects(m_hostWindow, dirtyRects); + render(dirtyRects); +} + +void WKCACFLayerRenderer::render(const Vector<CGRect>& windowDirtyRects) +{ + ASSERT(m_d3dDevice); + + if (m_mustResetLostDeviceBeforeRendering && !resetDevice(LostDevice)) { + // We can't reset the device right now. Try again soon. + renderSoon(); + return; + } + + if (m_client && !m_client->shouldRender()) { + renderSoon(); + return; + } + + // Sync the layer if needed + if (m_syncLayerChanges) { + m_client->syncCompositingState(); + m_syncLayerChanges = false; + } + + // Flush the root layer to the render tree. + wkCACFContextFlush(m_context); + + // All pending animations will have been started with the flush. Fire the animationStarted calls + double currentTime = WTF::currentTime(); + double currentMediaTime = CACurrentMediaTime(); + double t = currentTime + wkCACFContextGetLastCommitTime(m_context) - currentMediaTime; + ASSERT(t <= currentTime); + + HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end(); + for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it) { + PlatformCALayerClient* owner = (*it)->owner(); + owner->platformCALayerAnimationStarted(t); + } + + m_pendingAnimatedLayers.clear(); + + CGRect bounds = this->bounds(); + + // Give the renderer some space to use. This needs to be valid until the + // wkCACFContextFinishUpdate() call below. + char space[4096]; + if (!wkCACFContextBeginUpdate(m_context, space, sizeof(space), currentMediaTime, bounds, windowDirtyRects.data(), windowDirtyRects.size())) + return; + + HRESULT err = S_OK; + CFTimeInterval timeToNextRender = numeric_limits<CFTimeInterval>::infinity(); + + do { + // FIXME: don't need to clear dirty region if layer tree is opaque. + + WKCACFUpdateRectEnumerator* e = wkCACFContextCopyUpdateRectEnumerator(m_context); + if (!e) + break; + + Vector<D3DRECT, 64> rects; + for (const CGRect* r = wkCACFUpdateRectEnumeratorNextRect(e); r; r = wkCACFUpdateRectEnumeratorNextRect(e)) { + D3DRECT rect; + rect.x1 = r->origin.x; + rect.x2 = rect.x1 + r->size.width; + rect.y1 = bounds.origin.y + bounds.size.height - (r->origin.y + r->size.height); + rect.y2 = rect.y1 + r->size.height; + + rects.append(rect); + } + wkCACFUpdateRectEnumeratorRelease(e); + + timeToNextRender = wkCACFContextGetNextUpdateTime(m_context); + + if (rects.isEmpty()) + break; + + m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0); + + m_d3dDevice->BeginScene(); + wkCACFContextRenderUpdate(m_context); + m_d3dDevice->EndScene(); + + err = m_d3dDevice->Present(0, 0, 0, 0); + + if (err == D3DERR_DEVICELOST) { + wkCACFContextAddUpdateRect(m_context, bounds); + if (!resetDevice(LostDevice)) { + // We can't reset the device right now. Try again soon. + renderSoon(); + return; + } + } + } while (err == D3DERR_DEVICELOST); + + wkCACFContextFinishUpdate(m_context); + +#ifndef NDEBUG + if (m_printTree) + m_rootLayer->printTree(); +#endif + + // If timeToNextRender is not infinity, it means animations are running, so queue up to render again + if (timeToNextRender != numeric_limits<CFTimeInterval>::infinity()) + renderSoon(); +} + +void WKCACFLayerRenderer::renderSoon() +{ + if (!m_renderTimer.isActive()) + m_renderTimer.startOneShot(0); +} + +CGRect WKCACFLayerRenderer::bounds() const +{ + RECT clientRect; + GetClientRect(m_hostWindow, &clientRect); + + return winRectToCGRect(clientRect); +} + +void WKCACFLayerRenderer::initD3DGeometry() +{ + ASSERT(m_d3dDevice); + + CGRect bounds = this->bounds(); + + float x0 = bounds.origin.x; + float y0 = bounds.origin.y; + float x1 = x0 + bounds.size.width; + float y1 = y0 + bounds.size.height; + + D3DXMATRIXA16 projection; + D3DXMatrixOrthoOffCenterRH(&projection, x0, x1, y0, y1, -1.0f, 1.0f); + + m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection); +} + +bool WKCACFLayerRenderer::resetDevice(ResetReason reason) +{ + ASSERT(m_d3dDevice); + ASSERT(m_context); + + HRESULT hr = m_d3dDevice->TestCooperativeLevel(); + + if (hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR) { + // The device cannot be reset at this time. Try again soon. + m_mustResetLostDeviceBeforeRendering = true; + return false; + } + + m_mustResetLostDeviceBeforeRendering = false; + + if (reason == LostDevice && hr == D3D_OK) { + // The device wasn't lost after all. + return true; + } + + // We can reset the device. + + // We have to release the context's D3D resrouces whenever we reset the IDirect3DDevice9 in order to + // destroy any D3DPOOL_DEFAULT resources that Core Animation has allocated (e.g., textures used + // for mask layers). See <http://msdn.microsoft.com/en-us/library/bb174425(v=VS.85).aspx>. + wkCACFContextReleaseD3DResources(m_context); + + D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); + hr = m_d3dDevice->Reset(¶meters); + + // TestCooperativeLevel told us the device may be reset now, so we should + // not be told here that the device is lost. + ASSERT(hr != D3DERR_DEVICELOST); + + initD3DGeometry(); + + return true; +} + +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/win/WKCACFLayerRenderer.h b/Source/WebCore/platform/graphics/win/WKCACFLayerRenderer.h new file mode 100644 index 0000000..aff1f83 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WKCACFLayerRenderer.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2009 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 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 WKCACFLayerRenderer_h +#define WKCACFLayerRenderer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "COMPtr.h" +#include "Timer.h" + +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> + +#include <CoreGraphics/CGGeometry.h> + +interface IDirect3DDevice9; +struct WKCACFContext; + +typedef struct CGImage* CGImageRef; + +namespace WebCore { + +class PlatformCALayer; + +class WKCACFLayerRendererClient { +public: + virtual ~WKCACFLayerRendererClient() { } + virtual bool shouldRender() const = 0; + virtual void animationsStarted(CFTimeInterval) { } + virtual void syncCompositingState() { } +}; + +// FIXME: Currently there is a WKCACFLayerRenderer for each WebView and each +// has its own CARenderOGLContext and Direct3DDevice9, which is inefficient. +// (https://bugs.webkit.org/show_bug.cgi?id=31855) +class WKCACFLayerRenderer : public Noncopyable { + friend PlatformCALayer; + +public: + static PassOwnPtr<WKCACFLayerRenderer> create(WKCACFLayerRendererClient*); + ~WKCACFLayerRenderer(); + + static bool acceleratedCompositingAvailable(); + + void setRootContents(CGImageRef); + void setRootContentsAndDisplay(CGImageRef); + void setRootChildLayer(PlatformCALayer*); + void layerTreeDidChange(); + void setNeedsDisplay(bool sync = false); + void setHostWindow(HWND window) { m_hostWindow = window; } + void setBackingStoreDirty(bool dirty) { m_backingStoreDirty = dirty; } + bool createRenderer(); + void destroyRenderer(); + void resize(); + void renderSoon(); + +protected: + PlatformCALayer* rootLayer() const; + void addPendingAnimatedLayer(PassRefPtr<PlatformCALayer>); + +private: + WKCACFLayerRenderer(WKCACFLayerRendererClient*); + + void renderTimerFired(Timer<WKCACFLayerRenderer>*); + + CGRect bounds() const; + + void initD3DGeometry(); + + // Call this when the device window has changed size or when IDirect3DDevice9::Present returns + // D3DERR_DEVICELOST. Returns true if the device was recovered, false if rendering must be + // aborted and reattempted soon. + enum ResetReason { ChangedWindowSize, LostDevice }; + bool resetDevice(ResetReason); + + void render(const Vector<CGRect>& dirtyRects = Vector<CGRect>()); + void paint(); + + WKCACFLayerRendererClient* m_client; + bool m_mightBeAbleToCreateDeviceLater; + COMPtr<IDirect3DDevice9> m_d3dDevice; + RefPtr<PlatformCALayer> m_rootLayer; + RefPtr<PlatformCALayer> m_rootChildLayer; + WKCACFContext* m_context; + HWND m_hostWindow; + Timer<WKCACFLayerRenderer> m_renderTimer; + bool m_backingStoreDirty; + bool m_mustResetLostDeviceBeforeRendering; + bool m_syncLayerChanges; + HashSet<RefPtr<PlatformCALayer> > m_pendingAnimatedLayers; + +#ifndef NDEBUG + bool m_printTree; +#endif +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WKCACFLayerRenderer_h diff --git a/Source/WebCore/platform/graphics/win/WKCAImageQueue.cpp b/Source/WebCore/platform/graphics/win/WKCAImageQueue.cpp new file mode 100644 index 0000000..c2a178b --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WKCAImageQueue.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2010 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 INC. 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 INC. 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" + +#if USE(ACCELERATED_COMPOSITING) + +#include "WKCAImageQueue.h" + +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <wtf/RetainPtr.h> + +namespace WebCore { + +class WKCAImageQueuePrivate { +public: + RetainPtr<CAImageQueueRef> m_imageQueue; +}; + +static CAImageQueueRef WKCAImageQueueRetain(CAImageQueueRef iq) +{ + if (iq) + return (CAImageQueueRef)CFRetain(iq); + return 0; +} + +static void WKCAImageQueueRelease(CAImageQueueRef iq) +{ + if (iq) + CFRelease(iq); +} + +WKCAImageQueue::WKCAImageQueue(uint32_t width, uint32_t height, uint32_t capacity) + : m_private(new WKCAImageQueuePrivate()) +{ + m_private->m_imageQueue.adoptCF(wkCAImageQueueCreate(width, height, capacity)); +} + +WKCAImageQueue::WKCAImageQueue(const WKCAImageQueue& o) + : m_private(new WKCAImageQueuePrivate()) +{ + m_private->m_imageQueue = o.m_private->m_imageQueue; +} + +WKCAImageQueue::~WKCAImageQueue(void) +{ +} + +WKCAImageQueue& WKCAImageQueue::operator=(const WKCAImageQueue& o) +{ + m_private->m_imageQueue = o.m_private->m_imageQueue; + return *this; +} + +size_t WKCAImageQueue::collect() +{ + return wkCAImageQueueCollect(m_private->m_imageQueue.get()); +} + +bool WKCAImageQueue::insertImage(double t, unsigned int type, uint64_t id, uint32_t flags, ReleaseCallback release, void* info) +{ + return wkCAImageQueueInsertImage(m_private->m_imageQueue.get(), t, type, id, flags, release, info); +} + +uint64_t WKCAImageQueue::registerPixelBuffer(void *data, size_t data_size, size_t rowbytes, size_t width, size_t height, uint32_t pixel_format, CFDictionaryRef attachments, uint32_t flags) +{ + return wkCAImageQueueRegisterPixelBuffer(m_private->m_imageQueue.get(), data, data_size, rowbytes, width, height, pixel_format, attachments, flags); +} + +void WKCAImageQueue::setFlags(uint32_t mask, uint32_t flags) +{ + wkCAImageQueueSetFlags(m_private->m_imageQueue.get(), mask, flags); +} + +CFTypeRef WKCAImageQueue::get() +{ + return m_private->m_imageQueue.get(); +} + +} + +#endif diff --git a/Source/WebCore/platform/graphics/win/WKCAImageQueue.h b/Source/WebCore/platform/graphics/win/WKCAImageQueue.h new file mode 100644 index 0000000..3d25b48 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WKCAImageQueue.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 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 INC. 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 INC. 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 WKCAImageQueue_h +#define WKCAImageQueue_h + +#if USE(ACCELERATED_COMPOSITING) + +typedef const void * CFTypeRef; +typedef const struct __CFDictionary * CFDictionaryRef; + +#include <stdint.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class WKCAImageQueuePrivate; + +class WKCAImageQueue { +public: + enum Flags { + Async = 1U << 0, + Fill = 1U << 1, + Protected = 1U << 2, + UseCleanAperture = 1U << 3, + UseAspectRatio = 1U << 4, + LowQualityColor = 1U << 5, + }; + + enum ImageType { + Nil = 1, + Surface, + Buffer, + IOSurface, + }; + + enum ImageFlags { + Opaque = 1U << 0, + Flush = 1U << 1, + WillFlush = 1U << 2, + Flipped = 1U << 3, + WaitGPU = 1U << 4, + }; + + typedef void (*ReleaseCallback)(unsigned int type, uint64_t id, void* info); + + WKCAImageQueue(uint32_t width, uint32_t height, uint32_t capacity); + ~WKCAImageQueue(void); + + size_t collect(); + + bool insertImage(double t, unsigned int type, uint64_t id, uint32_t flags, ReleaseCallback release, void* info); + uint64_t registerPixelBuffer(void *data, size_t data_size, size_t rowbytes, size_t width, size_t height, uint32_t pixel_format, CFDictionaryRef attachments, uint32_t flags); + + uint32_t flags() const; + void setFlags(uint32_t mask, uint32_t flags); + + CFTypeRef get(); + +private: + WKCAImageQueue(const WKCAImageQueue&); + WKCAImageQueue& operator=(const WKCAImageQueue&); + OwnPtr<WKCAImageQueuePrivate> m_private; +}; + +} + +#endif + +#endif diff --git a/Source/WebCore/platform/graphics/win/WebLayer.cpp b/Source/WebCore/platform/graphics/win/WebLayer.cpp new file mode 100644 index 0000000..ecda294 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WebLayer.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2009 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 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 USE(ACCELERATED_COMPOSITING) + +#include "WebLayer.h" + +#include "Font.h" +#include "GraphicsLayer.h" + +namespace WebCore { + +using namespace std; + +void WebLayer::internalSetNeedsDisplay(const CGRect* dirtyRect) +{ + if (m_owner) { + if (m_owner->showRepaintCounter()) { + CGRect layerBounds = bounds(); + CGRect repaintCounterRect = layerBounds; + // We assume a maximum of 4 digits and a font size of 18. + repaintCounterRect.size.width = 80; + repaintCounterRect.size.height = 22; + if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) + repaintCounterRect.origin.y = layerBounds.size.height - (layerBounds.origin.y + repaintCounterRect.size.height); + WKCACFLayer::internalSetNeedsDisplay(&repaintCounterRect); + } + if (dirtyRect && m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { + CGRect flippedDirtyRect = *dirtyRect; + flippedDirtyRect.origin.y = bounds().size.height - (flippedDirtyRect.origin.y + flippedDirtyRect.size.height); + WKCACFLayer::internalSetNeedsDisplay(&flippedDirtyRect); + return; + } + } + + WKCACFLayer::internalSetNeedsDisplay(dirtyRect); +} + +void WebLayer::drawInContext(PlatformGraphicsContext* context) +{ + if (!m_owner) + return; + + CGContextSaveGState(context); + + CGRect layerBounds = bounds(); + if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM(context, 0, -layerBounds.size.height); + } + + if (m_owner->client()) { + GraphicsContext graphicsContext(context); + + // It's important to get the clip from the context, because it may be significantly + // smaller than the layer bounds (e.g. tiled layers) + CGRect clipBounds = CGContextGetClipBoundingBox(context); + IntRect clip(enclosingIntRect(clipBounds)); + m_owner->paintGraphicsLayerContents(graphicsContext, clip); + } +#ifndef NDEBUG + else { + ASSERT_NOT_REACHED(); + + // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color, + // so CA never makes backing store for it (which is what -setNeedsDisplay will do above). + CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f); + CGContextFillRect(context, layerBounds); + } +#endif + + if (m_owner->showRepaintCounter()) { + String text = String::number(m_owner->incrementRepaintCount()); + + CGContextSaveGState(context); + + // Make the background of the counter the same as the border color, + // unless there is no border, then make it red + float borderWidth = CACFLayerGetBorderWidth(layer()); + if (borderWidth > 0) { + CGColorRef borderColor = CACFLayerGetBorderColor(layer()); + const CGFloat* colors = CGColorGetComponents(borderColor); + CGContextSetRGBFillColor(context, colors[0], colors[1], colors[2], colors[3]); + } else + CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); + + CGRect aBounds = layerBounds; + + aBounds.size.width = 10 + 10 * text.length(); + aBounds.size.height = 22; + CGContextFillRect(context, aBounds); + + FontDescription desc; + + NONCLIENTMETRICS metrics; + metrics.cbSize = sizeof(metrics); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); + FontFamily family; + family.setFamily(metrics.lfSmCaptionFont.lfFaceName); + desc.setFamily(family); + + desc.setComputedSize(18); + + Font font = Font(desc, 0, 0); + font.update(0); + + GraphicsContext cg(context); + cg.setFillColor(Color::black, ColorSpaceDeviceRGB); + cg.drawText(font, TextRun(text), IntPoint(aBounds.origin.x + 5, aBounds.origin.y + 17)); + + CGContextRestoreGState(context); + } + + CGContextRestoreGState(context); +} + +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/win/WebLayer.h b/Source/WebCore/platform/graphics/win/WebLayer.h new file mode 100644 index 0000000..8dab5cc --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WebLayer.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 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 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 WebLayer_h +#define WebLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "WKCACFLayer.h" + +namespace WebCore { + +class GraphicsLayer; + +class WebLayer : public WKCACFLayer { +public: + static PassRefPtr<WKCACFLayer> create(LayerType layerType, GraphicsLayer* owner) + { + return adoptRef(new WebLayer(layerType, owner)); + } + + virtual void drawInContext(PlatformGraphicsContext*); + +protected: + WebLayer(LayerType layerType, GraphicsLayer* owner) + : WKCACFLayer(layerType) + , m_owner(owner) + { + } + + virtual void internalSetNeedsDisplay(const CGRect* dirtyRect); + + GraphicsLayer* m_owner; +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebLayer_h diff --git a/Source/WebCore/platform/graphics/win/WebTiledLayer.cpp b/Source/WebCore/platform/graphics/win/WebTiledLayer.cpp new file mode 100644 index 0000000..4705033 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WebTiledLayer.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2009 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 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 USE(ACCELERATED_COMPOSITING) + +#include "WebTiledLayer.h" + +#include "GraphicsLayer.h" +#include "WKCACFLayerRenderer.h" + +namespace WebCore { + +using namespace std; + +#ifndef NDEBUG +void WebTiledLayer::internalCheckLayerConsistency() +{ + WKCACFLayer::internalCheckLayerConsistency(); + + // Additionally make sure the tiled parent is valid + CFArrayRef sublayers = CACFLayerGetSublayers(layer()); + + // Make sure there is a tile parent and it is the same as we remember + size_t n = CFArrayGetCount(sublayers); + ASSERT(n > 0); + const void* element = CFArrayGetValueAtIndex(sublayers, 0); + ASSERT(m_tileParent.get() == element); + + // Make sure the tile parent doesn't have user data. If it does, it is probably + // a WKCACFLayer in the wrong place. + ASSERT(!layer(m_tileParent.get())); +} +#endif + +void WebTiledLayer::tileDisplayCallback(CACFLayerRef layer, CGContextRef context) +{ + static_cast<WebTiledLayer*>(CACFLayerGetUserData(layer))->drawTile(layer, context); +} + +PassRefPtr<WebTiledLayer> WebTiledLayer::create(const CGSize& tileSize, GraphicsLayer* owner) +{ + ASSERT(WKCACFLayerRenderer::acceleratedCompositingAvailable()); + return adoptRef(new WebTiledLayer(tileSize, owner)); +} + +WebTiledLayer::WebTiledLayer(const CGSize& tileSize, GraphicsLayer* owner) + : WebLayer(WKCACFLayer::Layer, owner) + , m_tileSize(tileSize) + , m_constrainedSize(constrainedSize(bounds().size)) +{ + // Tiled layers are placed in a child layer that is always the first child of the TiledLayer + m_tileParent.adoptCF(CACFLayerCreate(kCACFLayer)); + CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); + + updateTiles(); +} + +WebTiledLayer::~WebTiledLayer() +{ +} + +void WebTiledLayer::setBounds(const CGRect& rect) +{ + if (CGRectEqualToRect(rect, bounds())) + return; + + WebLayer::setBounds(rect); + m_constrainedSize = constrainedSize(rect.size); + updateTiles(); +} + +void WebTiledLayer::setFrame(const CGRect& rect) +{ + if (CGRectEqualToRect(rect, frame())) + return; + + WebLayer::setFrame(rect); + updateTiles(); +} + +void WebTiledLayer::internalSetNeedsDisplay(const CGRect* dirtyRect) +{ + // FIXME: Only setNeedsDisplay for tiles that are currently visible + int numTileLayers = tileCount(); + for (int i = 0; i < numTileLayers; ++i) + CACFLayerSetNeedsDisplay(tileAtIndex(i), dirtyRect); + + if (m_owner->showRepaintCounter()) { + CGRect layerBounds = bounds(); + CGRect indicatorRect = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, 80, 25); + CACFLayerSetNeedsDisplay(tileAtIndex(0), &indicatorRect); + } +} + +size_t WebTiledLayer::internalSublayerCount() const +{ + ASSERT(WebLayer::internalSublayerCount() > 0); + + // Subtract 1 to account for the tile parent layer + return WebLayer::internalSublayerCount() - 1; +} + +void WebTiledLayer::internalRemoveAllSublayers() +{ + // Restore the tile parent after removal + WebLayer::internalRemoveAllSublayers(); + CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); +} + +void WebTiledLayer::internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) +{ + // Preserve the tile parent after set + WebLayer::internalSetSublayers(sublayers); + CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); +} + +void WebTiledLayer::internalInsertSublayer(PassRefPtr<WKCACFLayer> layer, size_t index) +{ + // Add 1 to account for the tile parent layer + WebLayer::internalInsertSublayer(layer, index + 1); +} + +WKCACFLayer* WebTiledLayer::internalSublayerAtIndex(int i) const +{ + // Add 1 to account for the tile parent layer + return WebLayer::internalSublayerAtIndex(i + 1); +} + +int WebTiledLayer::internalIndexOfSublayer(const WKCACFLayer* layer) +{ + int i = WebLayer::internalIndexOfSublayer(layer); + + // Add 1 to account for the tile parent layer (but be safe about it) + return (i > 0) ? i - 1 : -1; +} + +CGSize WebTiledLayer::constrainedSize(const CGSize& size) const +{ + const int cMaxTileCount = 512; + const float cSqrtMaxTileCount = sqrtf(cMaxTileCount); + + CGSize constrainedSize = size; + + int tileColumns = ceilf(constrainedSize.width / m_tileSize.width); + int tileRows = ceilf(constrainedSize.height / m_tileSize.height); + int numTiles = tileColumns * tileRows; + + // If number of tiles vertically or horizontally is < sqrt(cMaxTileCount) + // just shorten the longer dimension. Otherwise shorten both dimensions + // according to the ratio of width to height + + if (numTiles > cMaxTileCount) { + if (tileRows < cSqrtMaxTileCount) + tileColumns = floorf(cMaxTileCount / tileRows); + else if (tileColumns < cSqrtMaxTileCount) + tileRows = floorf(cMaxTileCount / tileColumns); + else { + tileRows = ceilf(sqrtf(cMaxTileCount * constrainedSize.height / constrainedSize.width)); + tileColumns = floorf(cMaxTileCount / tileRows); + } + + constrainedSize.width = tileColumns * m_tileSize.width; + constrainedSize.height = tileRows * m_tileSize.height; + } + + return constrainedSize; +} + +void WebTiledLayer::addTile() +{ + RetainPtr<CACFLayerRef> newLayer(AdoptCF, CACFLayerCreate(kCACFLayer)); + CACFLayerSetAnchorPoint(newLayer.get(), CGPointMake(0, 1)); + CACFLayerSetUserData(newLayer.get(), this); + CACFLayerSetDisplayCallback(newLayer.get(), tileDisplayCallback); + + CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); + CACFLayerInsertSublayer(m_tileParent.get(), newLayer.get(), sublayers ? CFArrayGetCount(sublayers) : 0); + + if (m_owner->showDebugBorders()) { + CGColorRef borderColor = CGColorCreateGenericRGB(0.5, 0, 0.5, 0.7); + CACFLayerSetBorderColor(newLayer.get(), borderColor); + CGColorRelease(borderColor); + CACFLayerSetBorderWidth(newLayer.get(), 2); + } +} + +void WebTiledLayer::removeTile() +{ + CACFLayerRemoveFromSuperlayer(tileAtIndex(tileCount() - 1)); +} + +CACFLayerRef WebTiledLayer::tileAtIndex(int index) +{ + CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); + if (!sublayers || index < 0 || index >= tileCount() ) + return 0; + + return static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index))); +} + +int WebTiledLayer::tileCount() const +{ + CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); + return sublayers ? CFArrayGetCount(sublayers) : 0; +} + +void WebTiledLayer::updateTiles() +{ + // FIXME: In addition to redoing the number of tiles, we need to only render and have backing + // store for visible layers + int numTilesHorizontal = ceil(m_constrainedSize.width / m_tileSize.width); + int numTilesVertical = ceil(m_constrainedSize.height / m_tileSize.height); + int numTilesTotal = numTilesHorizontal * numTilesVertical; + + int numTilesToChange = numTilesTotal - tileCount(); + if (numTilesToChange >= 0) { + // Add new tiles + for (int i = 0; i < numTilesToChange; ++i) + addTile(); + } else { + // Remove old tiles + numTilesToChange = -numTilesToChange; + for (int i = 0; i < numTilesToChange; ++i) + removeTile(); + } + + // Set coordinates for all tiles + CFArrayRef tileArray = CACFLayerGetSublayers(m_tileParent.get()); + + for (int i = 0; i < numTilesHorizontal; ++i) { + for (int j = 0; j < numTilesVertical; ++j) { + CACFLayerRef tile = static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(tileArray, i * numTilesVertical + j))); + CACFLayerSetPosition(tile, CGPointMake(i * m_tileSize.width, j * m_tileSize.height)); + int width = min(m_tileSize.width, m_constrainedSize.width - i * m_tileSize.width); + int height = min(m_tileSize.height, m_constrainedSize.height - j * m_tileSize.height); + CACFLayerSetBounds(tile, CGRectMake(i * m_tileSize.width, j * m_tileSize.height, width, height)); + + // Flip Y to compensate for the flipping that happens during render to match the CG context coordinate space + CATransform3D transform = CATransform3DMakeScale(1, -1, 1); + CATransform3DTranslate(transform, 0, height, 0); + CACFLayerSetTransform(tile, transform); + +#ifndef NDEBUG + String name = "Tile (" + String::number(i) + "," + String::number(j) + ")"; + CACFLayerSetName(tile, RetainPtr<CFStringRef>(AdoptCF, name.createCFString()).get()); +#endif + } + } +} + +void WebTiledLayer::drawTile(CACFLayerRef tile, CGContextRef context) +{ + CGPoint tilePosition = CACFLayerGetPosition(tile); + CGRect tileBounds = CACFLayerGetBounds(tile); + + CGContextSaveGState(context); + + // Transform context to be at the origin of the parent layer + CGContextTranslateCTM(context, -tilePosition.x, -tilePosition.y); + + // Set the context clipping rectangle to the current tile + CGContextClipToRect(context, CGRectMake(tilePosition.x, tilePosition.y, tileBounds.size.width, tileBounds.size.height)); + + if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { + // If the layer is rendering top-down, it will flip the coordinates in y. Tiled layers are + // already flipping, so we need to undo that here. + CGContextTranslateCTM(context, 0, bounds().size.height); + CGContextScaleCTM(context, 1, -1); + } + + // Draw the tile + drawInContext(context); + + CGContextRestoreGState(context); +} + +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/win/WebTiledLayer.h b/Source/WebCore/platform/graphics/win/WebTiledLayer.h new file mode 100644 index 0000000..b8ae320 --- /dev/null +++ b/Source/WebCore/platform/graphics/win/WebTiledLayer.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010 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 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 WebTiledLayer_h +#define WebTiledLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "WebLayer.h" + +namespace WebCore { + +class WebTiledLayer : public WebLayer { +public: + static PassRefPtr<WebTiledLayer> create(const CGSize& tileSize, GraphicsLayer* owner); + + virtual ~WebTiledLayer(); + + virtual void setBounds(const CGRect&); + virtual void setFrame(const CGRect&); + +protected: + WebTiledLayer(const CGSize& tileSize, GraphicsLayer* owner); + + // Overridden from WKCACFLayer + virtual WKCACFLayer* internalSublayerAtIndex(int) const; + virtual int internalIndexOfSublayer(const WKCACFLayer*); + + virtual size_t internalSublayerCount() const; + virtual void internalInsertSublayer(PassRefPtr<WKCACFLayer>, size_t index); + + virtual void internalRemoveAllSublayers(); + virtual void internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >&); + + virtual void internalSetNeedsDisplay(const CGRect* dirtyRect); + +#ifndef NDEBUG + virtual void internalCheckLayerConsistency(); +#endif + +private: + static void tileDisplayCallback(CACFLayerRef, CGContextRef); + void drawTile(CACFLayerRef, CGContextRef); + + CGSize constrainedSize(const CGSize& size) const; + + void addTile(); + void removeTile(); + CACFLayerRef tileAtIndex(int); + int tileCount() const; + + void updateTiles(); + + CGSize m_tileSize; + CGSize m_constrainedSize; + RetainPtr<CACFLayerRef> m_tileParent; +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebTiledLayer_h diff --git a/Source/WebCore/platform/graphics/win/cairo/FontPlatformData.h b/Source/WebCore/platform/graphics/win/cairo/FontPlatformData.h new file mode 100644 index 0000000..d8f538a --- /dev/null +++ b/Source/WebCore/platform/graphics/win/cairo/FontPlatformData.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * Copyright (C) 2010 Brent Fulgham <bfulgham@webkit.org> + * 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 FontPlatformDataCairoWin_h +#define FontPlatformDataCairoWin_h + +#include "FontOrientation.h" +#include "GlyphBuffer.h" +#include "RefCountedGDIHandle.h" +#include "StringImpl.h" +#include <cairo-win32.h> +#include <cairo.h> +#include <wtf/Forward.h> + +typedef struct HFONT__* HFONT; + +namespace WebCore { + +class FontDescription; + +class FontPlatformData { +public: + FontPlatformData(WTF::HashTableDeletedValueType) + : m_fontFace(0) + , m_useGDI(false) + , m_font(WTF::HashTableDeletedValue) + , m_size(0) + , m_syntheticBold(false) + , m_syntheticOblique(false) + , m_scaledFont(0) + { } + + FontPlatformData() + : m_fontFace(0) + , m_useGDI(false) + , m_size(0) + , m_syntheticBold(false) + , m_syntheticOblique(false) + , m_scaledFont(0) + { } + + FontPlatformData(HFONT, float size, bool bold, bool oblique, bool useGDI); + FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool italic); + FontPlatformData(float size, bool bold, bool italic); + FontPlatformData(const FontPlatformData&); + ~FontPlatformData(); + + HFONT hfont() const { return m_font->handle(); } + bool useGDI() const { return m_useGDI; } + cairo_font_face_t* fontFace() const { return m_fontFace; } + + bool isFixedPitch(); + 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; } + + FontOrientation orientation() const { return Horizontal; } // FIXME: Implement. + + cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } + + unsigned hash() const + { + return m_font->hash(); + } + + bool operator==(const FontPlatformData&) const; + FontPlatformData& operator=(const FontPlatformData&); + bool isHashTableDeletedValue() const + { + return m_font.isHashTableDeletedValue(); + } + +#ifndef NDEBUG + String description() const; +#endif + +private: + void platformDataInit(HFONT, float size, HDC, WCHAR* faceName); + + RefPtr<RefCountedGDIHandle<HFONT> > m_font; + cairo_font_face_t* m_fontFace; + bool m_useGDI; + float m_size; + bool m_syntheticBold; + bool m_syntheticOblique; + cairo_scaled_font_t* m_scaledFont; +}; + +} + +#endif // FontPlatformDataCairoWin_h |