diff options
Diffstat (limited to 'WebCore/platform/graphics')
171 files changed, 8653 insertions, 2696 deletions
diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index 2f9412d..0b94efb 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -86,7 +86,7 @@ void BitmapImage::destroyDecodedData(bool destroyAll) destroyMetadataAndNotify(framesCleared); - m_source.clear(destroyAll, clearBeforeFrame, m_data.get(), m_allDataReceived); + m_source.clear(destroyAll, clearBeforeFrame, data(), m_allDataReceived); return; } @@ -163,7 +163,7 @@ bool BitmapImage::dataChanged(bool allDataReceived) // Feed all the data we've seen so far to the image decoder. m_allDataReceived = allDataReceived; - m_source.setData(m_data.get(), allDataReceived); + m_source.setData(data(), allDataReceived); // Clear the frame count. m_haveFrameCount = false; diff --git a/WebCore/platform/graphics/BitmapImage.h b/WebCore/platform/graphics/BitmapImage.h index a2de5d7..807c11b 100644 --- a/WebCore/platform/graphics/BitmapImage.h +++ b/WebCore/platform/graphics/BitmapImage.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008-2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -164,7 +165,7 @@ protected: virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator); #endif virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); -#if PLATFORM(WX) +#if PLATFORM(WX) || PLATFORM(WINCE) virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); #endif @@ -224,7 +225,11 @@ protected: { if (!m_checkedForSolidColor && frameCount() > 0) { checkForSolidColor(); + // WINCE PORT: checkForSolidColor() doesn't set m_checkedForSolidColor until + // it gets enough information to make final decision. +#if !PLATFORM(WINCE) ASSERT(m_checkedForSolidColor); +#endif } return m_isSolidColor && m_currentFrame == 0; } diff --git a/WebCore/platform/graphics/Color.h b/WebCore/platform/graphics/Color.h index 3b815d2..5baa56e 100644 --- a/WebCore/platform/graphics/Color.h +++ b/WebCore/platform/graphics/Color.h @@ -149,7 +149,6 @@ inline bool operator!=(const Color& a, const Color& b) return !(a == b); } -Color focusRingColor(); Color colorFromPremultipliedARGB(unsigned); unsigned premultipliedARGBFromColor(const Color&); diff --git a/WebCore/platform/graphics/FloatPoint.h b/WebCore/platform/graphics/FloatPoint.h index a3fbeea..a9850c9 100644 --- a/WebCore/platform/graphics/FloatPoint.h +++ b/WebCore/platform/graphics/FloatPoint.h @@ -51,11 +51,15 @@ class QPointF; QT_END_NAMESPACE #endif +#ifdef MANUAL_MERGE_REQUIRED #if PLATFORM(SYMBIAN) class TPoint; #endif #if (PLATFORM(SKIA) || PLATFORM(SGL)) +#else // MANUAL_MERGE_REQUIRED +#if PLATFORM(SKIA) +#endif // MANUAL_MERGE_REQUIRED struct SkPoint; #endif @@ -94,12 +98,16 @@ public: operator QPointF() const; #endif +#ifdef MANUAL_MERGE_REQUIRED #if PLATFORM(SYMBIAN) operator TPoint() const; FloatPoint(const TPoint&); #endif #if (PLATFORM(SKIA) || PLATFORM(SGL)) +#else // MANUAL_MERGE_REQUIRED +#if PLATFORM(SKIA) +#endif // MANUAL_MERGE_REQUIRED operator SkPoint() const; FloatPoint(const SkPoint&); #endif diff --git a/WebCore/platform/graphics/FloatPoint3D.h b/WebCore/platform/graphics/FloatPoint3D.h index 2e71ddd..73c3a37 100644 --- a/WebCore/platform/graphics/FloatPoint3D.h +++ b/WebCore/platform/graphics/FloatPoint3D.h @@ -49,6 +49,16 @@ private: float m_z; }; +inline bool operator==(const FloatPoint3D& a, const FloatPoint3D& b) +{ + return a.x() == b.x() && a.y() == b.y() && a.z() == b.z(); +} + +inline bool operator!=(const FloatPoint3D& a, const FloatPoint3D& b) +{ + return a.x() != b.x() || a.y() != b.y() || a.z() != b.z(); +} + } // namespace WebCore #endif // FloatPoint3D_h diff --git a/WebCore/platform/graphics/FloatRect.h b/WebCore/platform/graphics/FloatRect.h index 786fb3e..e906812 100644 --- a/WebCore/platform/graphics/FloatRect.h +++ b/WebCore/platform/graphics/FloatRect.h @@ -132,11 +132,6 @@ public: FloatRect(const QRectF&); operator QRectF() const; #endif -#if PLATFORM(SYMBIAN) - FloatRect(const TRect&); - operator TRect() const; - TRect rect() const; -#endif #if PLATFORM(WX) && USE(WXGC) FloatRect(const wxRect2DDouble&); diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp index 85fe882..88774cb 100644 --- a/WebCore/platform/graphics/Font.cpp +++ b/WebCore/platform/graphics/Font.cpp @@ -59,9 +59,7 @@ Font::CodePath Font::s_codePath = Auto; // ============================================================================================ Font::Font() - : m_pageZero(0) - , m_cachedPrimaryFont(0) - , m_letterSpacing(0) + : m_letterSpacing(0) , m_wordSpacing(0) , m_isPlatformFont(false) { @@ -69,8 +67,6 @@ Font::Font() Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) : m_fontDescription(fd) - , m_pageZero(0) - , m_cachedPrimaryFont(0) , m_letterSpacing(letterSpacing) , m_wordSpacing(wordSpacing) , m_isPlatformFont(false) @@ -79,8 +75,6 @@ Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) Font::Font(const FontPlatformData& fontData, bool isPrinterFont) : m_fontList(FontFallbackList::create()) - , m_pageZero(0) - , m_cachedPrimaryFont(0) , m_letterSpacing(0) , m_wordSpacing(0) , m_isPlatformFont(true) @@ -92,9 +86,6 @@ Font::Font(const FontPlatformData& fontData, bool isPrinterFont) Font::Font(const Font& other) : m_fontDescription(other.m_fontDescription) , m_fontList(other.m_fontList) - , m_pages(other.m_pages) - , m_pageZero(other.m_pageZero) - , m_cachedPrimaryFont(other.m_cachedPrimaryFont) , m_letterSpacing(other.m_letterSpacing) , m_wordSpacing(other.m_wordSpacing) , m_isPlatformFont(other.m_isPlatformFont) @@ -105,9 +96,6 @@ Font& Font::operator=(const Font& other) { m_fontDescription = other.m_fontDescription; m_fontList = other.m_fontList; - m_pages = other.m_pages; - m_pageZero = other.m_pageZero; - m_cachedPrimaryFont = other.m_cachedPrimaryFont; m_letterSpacing = other.m_letterSpacing; m_wordSpacing = other.m_wordSpacing; m_isPlatformFont = other.m_isPlatformFont; @@ -136,11 +124,10 @@ bool Font::operator==(const Font& other) const && (m_fontList ? m_fontList->generation() : 0) == (other.m_fontList ? other.m_fontList->generation() : 0); } -void Font::cachePrimaryFont() const +const SimpleFontData* Font::primaryFont() const { ASSERT(m_fontList); - ASSERT(!m_cachedPrimaryFont); - m_cachedPrimaryFont = m_fontList->primaryFont(this)->fontDataForCharacter(' '); + return m_fontList->primarySimpleFontData(this); } const FontData* Font::fontDataAt(unsigned index) const @@ -165,9 +152,6 @@ void Font::update(PassRefPtr<FontSelector> fontSelector) const if (!m_fontList) m_fontList = FontFallbackList::create(); m_fontList->invalidate(fontSelector); - m_cachedPrimaryFont = 0; - m_pageZero = 0; - m_pages.clear(); } bool Font::isFixedPitch() const diff --git a/WebCore/platform/graphics/Font.h b/WebCore/platform/graphics/Font.h index 8d85d8b..c067071 100644 --- a/WebCore/platform/graphics/Font.h +++ b/WebCore/platform/graphics/Font.h @@ -115,14 +115,7 @@ public: int spaceWidth() const { return (int)ceilf(primaryFont()->adjustedSpaceWidth() + m_letterSpacing); } int tabWidth() const { return 8 * spaceWidth(); } - const SimpleFontData* primaryFont() const - { - ASSERT(isMainThread()); - if (!m_cachedPrimaryFont) - cachePrimaryFont(); - return m_cachedPrimaryFont; - } - + const SimpleFontData* primaryFont() const; const FontData* fontDataAt(unsigned) const; GlyphData glyphDataForCharacter(UChar32, bool mirror, bool forceSmallCaps = false) const; // Used for complex text, and does not utilize the glyph map cache. @@ -160,7 +153,6 @@ private: float floatWidthForComplexText(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0) const; int offsetForPositionForComplexText(const TextRun&, int position, bool includePartialGlyphs) const; FloatRect selectionRectForComplexText(const TextRun&, const IntPoint&, int h, int from, int to) const; - void cachePrimaryFont() const; friend struct WidthIterator; @@ -191,9 +183,6 @@ public: private: FontDescription m_fontDescription; mutable RefPtr<FontFallbackList> m_fontList; - mutable HashMap<int, GlyphPageTreeNode*> m_pages; - mutable GlyphPageTreeNode* m_pageZero; - mutable const SimpleFontData* m_cachedPrimaryFont; short m_letterSpacing; short m_wordSpacing; bool m_isPlatformFont; diff --git a/WebCore/platform/graphics/FontCache.cpp b/WebCore/platform/graphics/FontCache.cpp index d9b4b28..06ea56b 100644 --- a/WebCore/platform/graphics/FontCache.cpp +++ b/WebCore/platform/graphics/FontCache.cpp @@ -328,18 +328,20 @@ void FontCache::purgeInactiveFontData(int count) for (size_t i = 0; i < fontDataToDeleteCount; ++i) delete fontDataToDelete[i]; - Vector<FontPlatformDataCacheKey> keysToRemove; - keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); - FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); - for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { - if (platformData->second && !gFontDataCache->contains(*platformData->second)) - keysToRemove.append(platformData->first); + if (gFontPlatformDataCache) { + Vector<FontPlatformDataCacheKey> keysToRemove; + keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); + FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); + for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { + if (platformData->second && !gFontDataCache->contains(*platformData->second)) + keysToRemove.append(platformData->first); + } + + size_t keysToRemoveCount = keysToRemove.size(); + for (size_t i = 0; i < keysToRemoveCount; ++i) + delete gFontPlatformDataCache->take(keysToRemove[i]); } - size_t keysToRemoveCount = keysToRemove.size(); - for (size_t i = 0; i < keysToRemoveCount; ++i) - delete gFontPlatformDataCache->take(keysToRemove[i]); - isPurging = false; } diff --git a/WebCore/platform/graphics/FontData.h b/WebCore/platform/graphics/FontData.h index cb79919..76927f5 100644 --- a/WebCore/platform/graphics/FontData.h +++ b/WebCore/platform/graphics/FontData.h @@ -32,8 +32,9 @@ namespace WebCore { class SimpleFontData; +class String; -class FontData : Noncopyable { +class FontData : public Noncopyable { public: FontData() : m_maxGlyphPageTreeLevel(0) @@ -51,6 +52,10 @@ public: void setMaxGlyphPageTreeLevel(unsigned level) const { m_maxGlyphPageTreeLevel = level; } unsigned maxGlyphPageTreeLevel() const { return m_maxGlyphPageTreeLevel; } +#ifndef NDEBUG + virtual String description() const = 0; +#endif + private: mutable unsigned m_maxGlyphPageTreeLevel; }; diff --git a/WebCore/platform/graphics/FontFallbackList.cpp b/WebCore/platform/graphics/FontFallbackList.cpp index decacc5..649c117 100644 --- a/WebCore/platform/graphics/FontFallbackList.cpp +++ b/WebCore/platform/graphics/FontFallbackList.cpp @@ -36,7 +36,9 @@ namespace WebCore { FontFallbackList::FontFallbackList() - : m_fontSelector(0) + : m_pageZero(0) + , m_cachedPrimarySimpleFontData(0) + , m_fontSelector(0) , m_familyIndex(0) , m_pitch(UnknownPitch) , m_loadingCustomFonts(false) @@ -48,6 +50,9 @@ void FontFallbackList::invalidate(PassRefPtr<FontSelector> fontSelector) { releaseFontData(); m_fontList.clear(); + m_pageZero = 0; + m_pages.clear(); + m_cachedPrimarySimpleFontData = 0; m_familyIndex = 0; m_pitch = UnknownPitch; m_loadingCustomFonts = false; @@ -68,7 +73,7 @@ void FontFallbackList::releaseFontData() void FontFallbackList::determinePitch(const Font* font) const { - const FontData* fontData = primaryFont(font); + const FontData* fontData = primaryFontData(font); if (!fontData->isSegmented()) m_pitch = static_cast<const SimpleFontData*>(fontData)->pitch(); else { diff --git a/WebCore/platform/graphics/FontFallbackList.h b/WebCore/platform/graphics/FontFallbackList.h index 07938ae..14ebbe4 100644 --- a/WebCore/platform/graphics/FontFallbackList.h +++ b/WebCore/platform/graphics/FontFallbackList.h @@ -31,6 +31,7 @@ namespace WebCore { class Font; +class GlyphPageTreeNode; class GraphicsContext; class IntRect; class FontDescription; @@ -57,7 +58,15 @@ public: private: FontFallbackList(); - const FontData* primaryFont(const Font* f) const { return fontDataAt(f, 0); } + const SimpleFontData* primarySimpleFontData(const Font* f) + { + ASSERT(isMainThread()); + if (!m_cachedPrimarySimpleFontData) + m_cachedPrimarySimpleFontData = primaryFontData(f)->fontDataForCharacter(' '); + return m_cachedPrimarySimpleFontData; + } + + const FontData* primaryFontData(const Font* f) const { return fontDataAt(f, 0); } const FontData* fontDataAt(const Font*, unsigned index) const; const FontData* fontDataForCharacters(const Font*, const UChar*, int length) const; @@ -66,6 +75,9 @@ private: void releaseFontData(); mutable Vector<pair<const FontData*, bool>, 1> m_fontList; + mutable HashMap<int, GlyphPageTreeNode*> m_pages; + mutable GlyphPageTreeNode* m_pageZero; + mutable const SimpleFontData* m_cachedPrimarySimpleFontData; RefPtr<FontSelector> m_fontSelector; mutable int m_familyIndex; mutable Pitch m_pitch; diff --git a/WebCore/platform/graphics/FontFastPath.cpp b/WebCore/platform/graphics/FontFastPath.cpp index deac1b6..b0e39db 100644 --- a/WebCore/platform/graphics/FontFastPath.cpp +++ b/WebCore/platform/graphics/FontFastPath.cpp @@ -1,6 +1,7 @@ /** * Copyright (C) 2003, 2006 Apple Computer, Inc. * Copyright (C) 2008 Holger Hans Peter Freyther + * Copyright (C) 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 @@ -24,6 +25,7 @@ #include "CharacterNames.h" #include "FontCache.h" +#include "FontFallbackList.h" #include "FloatRect.h" #include "GlyphBuffer.h" #include "GlyphPageTreeNode.h" @@ -57,13 +59,13 @@ GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCap unsigned pageNumber = (c / GlyphPage::size); - GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero; + GlyphPageTreeNode* node = pageNumber ? m_fontList->m_pages.get(pageNumber) : m_fontList->m_pageZero; if (!node) { node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); if (pageNumber) - m_pages.set(pageNumber, node); + m_fontList->m_pages.set(pageNumber, node); else - m_pageZero = node; + m_fontList->m_pageZero = node; } GlyphPage* page; @@ -82,9 +84,9 @@ GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCap // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) - m_pages.set(pageNumber, node); + m_fontList->m_pages.set(pageNumber, node); else - m_pageZero = node; + m_fontList->m_pageZero = node; } } else { while (true) { @@ -118,9 +120,9 @@ GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCap // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) - m_pages.set(pageNumber, node); + m_fontList->m_pages.set(pageNumber, node); else - m_pageZero = node; + m_fontList->m_pageZero = node; } } @@ -154,16 +156,33 @@ GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCap GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page(); GlyphData data = fallbackPage && fallbackPage->fontDataForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); // Cache it so we don't have to do system fallback again next time. - if (!useSmallCapsFont) + if (!useSmallCapsFont) { +#if PLATFORM(WINCE) + // missingGlyphData returns a null character, which is not suitable for GDI to display. + // Also, sometimes we cannot map a font for the character on WINCE, but GDI can still + // display the character, probably because the font package is not installed correctly. + // So we just always set the glyph to be same as the character, and let GDI solve it. + page->setGlyphDataForCharacter(c, c, characterFontData); + return page->glyphDataForCharacter(c); +#else page->setGlyphDataForCharacter(c, data.glyph, data.fontData); +#endif + } return data; } // Even system fallback can fail; use the missing glyph in that case. // FIXME: It would be nicer to use the missing glyph from the last resort font instead. GlyphData data = primaryFont()->missingGlyphData(); - if (!useSmallCapsFont) + if (!useSmallCapsFont) { +#if PLATFORM(WINCE) + // See comment about WINCE GDI handling near setGlyphDataForCharacter above. + page->setGlyphDataForCharacter(c, c, data.fontData); + return page->glyphDataForCharacter(c); +#else page->setGlyphDataForCharacter(c, data.glyph, data.fontData); +#endif + } return data; } diff --git a/WebCore/platform/graphics/GeneratedImage.cpp b/WebCore/platform/graphics/GeneratedImage.cpp index c40a40a..bac9da0 100644 --- a/WebCore/platform/graphics/GeneratedImage.cpp +++ b/WebCore/platform/graphics/GeneratedImage.cpp @@ -51,7 +51,7 @@ void GeneratedImage::drawPattern(GraphicsContext* context, const FloatRect& srcR const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect) { // Create a BitmapImage and call drawPattern on it. - OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(m_size, false); + OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(m_size); ASSERT(imageBuffer.get()); // Fill with the gradient. diff --git a/WebCore/platform/graphics/GlyphBuffer.h b/WebCore/platform/graphics/GlyphBuffer.h index dcda419..04491a7 100644 --- a/WebCore/platform/graphics/GlyphBuffer.h +++ b/WebCore/platform/graphics/GlyphBuffer.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007-2008 Torch Mobile Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -49,6 +50,8 @@ class SimpleFontData; #if PLATFORM(CAIRO) // FIXME: Why does Cairo use such a huge struct instead of just an offset into an array? typedef cairo_glyph_t GlyphBufferGlyph; +#elif PLATFORM(WINCE) +typedef wchar_t GlyphBufferGlyph; #else typedef Glyph GlyphBufferGlyph; #endif @@ -57,6 +60,10 @@ typedef Glyph GlyphBufferGlyph; // can be passed directly to CGContextShowGlyphsWithAdvances in FontMac.mm #if PLATFORM(CG) typedef CGSize GlyphBufferAdvance; +#elif PLATFORM(WINCE) +// There is no cross-platform code that uses the height of GlyphBufferAdvance, +// so we can save memory space on embedded devices by storing only the width +typedef float GlyphBufferAdvance; #else typedef FloatSize GlyphBufferAdvance; #endif @@ -117,6 +124,8 @@ public: { #if PLATFORM(CG) return m_advances[index].width; +#elif PLATFORM(WINCE) + return m_advances[index]; #else return m_advances[index].width(); #endif @@ -147,6 +156,8 @@ public: #if PLATFORM(CG) CGSize advance = { width, 0 }; m_advances.append(advance); +#elif PLATFORM(WINCE) + m_advances.append(width); #else m_advances.append(FloatSize(width, 0)); #endif @@ -161,6 +172,7 @@ public: #endif } +#if !PLATFORM(WINCE) void add(Glyph glyph, const SimpleFontData* font, GlyphBufferAdvance advance) { m_fontData.append(font); @@ -174,6 +186,7 @@ public: m_advances.append(advance); } +#endif private: Vector<const SimpleFontData*, 2048> m_fontData; diff --git a/WebCore/platform/graphics/GlyphPageTreeNode.cpp b/WebCore/platform/graphics/GlyphPageTreeNode.cpp index a34a192..6419e0c 100644 --- a/WebCore/platform/graphics/GlyphPageTreeNode.cpp +++ b/WebCore/platform/graphics/GlyphPageTreeNode.cpp @@ -29,6 +29,7 @@ #include "config.h" #include "GlyphPageTreeNode.h" +#include "CString.h" #include "CharacterNames.h" #include "SegmentedFontData.h" #include "SimpleFontData.h" @@ -377,4 +378,41 @@ void GlyphPageTreeNode::pruneFontData(const SimpleFontData* fontData, unsigned l it->second->pruneFontData(fontData, level); } +#ifndef NDEBUG + void GlyphPageTreeNode::showSubtree() + { + Vector<char> indent(level()); + indent.fill('\t', level()); + indent.append(0); + + HashMap<const FontData*, GlyphPageTreeNode*>::iterator end = m_children.end(); + for (HashMap<const FontData*, GlyphPageTreeNode*>::iterator it = m_children.begin(); it != end; ++it) { + printf("%s\t%p %s\n", indent.data(), it->first, it->first->description().utf8().data()); + it->second->showSubtree(); + } + if (m_systemFallbackChild) { + printf("%s\t* fallback\n", indent.data()); + m_systemFallbackChild->showSubtree(); + } + } +#endif + } + +#ifndef NDEBUG +void showGlyphPageTrees() +{ + printf("Page 0:\n"); + showGlyphPageTree(0); + HashMap<int, WebCore::GlyphPageTreeNode*>::iterator end = WebCore::GlyphPageTreeNode::roots->end(); + for (HashMap<int, WebCore::GlyphPageTreeNode*>::iterator it = WebCore::GlyphPageTreeNode::roots->begin(); it != end; ++it) { + printf("\nPage %d:\n", it->first); + showGlyphPageTree(it->first); + } +} + +void showGlyphPageTree(unsigned pageNumber) +{ + WebCore::GlyphPageTreeNode::getRoot(pageNumber)->showSubtree(); +} +#endif diff --git a/WebCore/platform/graphics/GlyphPageTreeNode.h b/WebCore/platform/graphics/GlyphPageTreeNode.h index 80e87aa..7918ac5 100644 --- a/WebCore/platform/graphics/GlyphPageTreeNode.h +++ b/WebCore/platform/graphics/GlyphPageTreeNode.h @@ -35,6 +35,11 @@ #include <wtf/RefCounted.h> #include <wtf/unicode/Unicode.h> +#ifndef NDEBUG +void showGlyphPageTrees(); +void showGlyphPageTree(unsigned pageNumber); +#endif + namespace WebCore { class FontData; @@ -210,6 +215,10 @@ private: static GlyphPageTreeNode* getRoot(unsigned pageNumber); void initializePage(const FontData*, unsigned pageNumber); +#ifndef NDEBUG + void showSubtree(); +#endif + GlyphPageTreeNode* m_parent; RefPtr<GlyphPage> m_page; unsigned m_level; @@ -220,6 +229,8 @@ private: #ifndef NDEBUG unsigned m_pageNumber; + + friend void ::showGlyphPageTree(unsigned pageNumber); #endif }; diff --git a/WebCore/platform/graphics/GlyphWidthMap.h b/WebCore/platform/graphics/GlyphWidthMap.h index e194ecf..66dea1f 100644 --- a/WebCore/platform/graphics/GlyphWidthMap.h +++ b/WebCore/platform/graphics/GlyphWidthMap.h @@ -39,7 +39,7 @@ typedef unsigned short Glyph; const float cGlyphWidthUnknown = -1; -class GlyphWidthMap : Noncopyable { +class GlyphWidthMap : public Noncopyable { public: GlyphWidthMap() : m_filledPrimaryPage(false) { } ~GlyphWidthMap() { if (m_pages) { deleteAllValues(*m_pages); } } diff --git a/WebCore/platform/graphics/Gradient.cpp b/WebCore/platform/graphics/Gradient.cpp index 51c7162..77a0d21 100644 --- a/WebCore/platform/graphics/Gradient.cpp +++ b/WebCore/platform/graphics/Gradient.cpp @@ -155,4 +155,17 @@ void Gradient::setSpreadMethod(GradientSpreadMethod spreadMethod) m_spreadMethod = spreadMethod; } +void Gradient::setGradientSpaceTransform(const TransformationMatrix& gradientSpaceTransformation) +{ + m_gradientSpaceTransformation = gradientSpaceTransformation; + setPlatformGradientSpaceTransform(gradientSpaceTransformation); +} + +#if !PLATFORM(SKIA) +void Gradient::setPlatformGradientSpaceTransform(const TransformationMatrix&) +{ +} +#endif + + } //namespace diff --git a/WebCore/platform/graphics/Gradient.h b/WebCore/platform/graphics/Gradient.h index 764deee..074c99d 100644 --- a/WebCore/platform/graphics/Gradient.h +++ b/WebCore/platform/graphics/Gradient.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -76,12 +77,28 @@ namespace WebCore { void getColor(float value, float* r, float* g, float* b, float* a) const; +#ifdef MANUAL_MERGE_REQUIRED #if PLATFORM(SGL) SkShader* getShader(SkShader::TileMode); #else +#else // MANUAL_MERGE_REQUIRED +#if PLATFORM(WINCE) && !PLATFORM(QT) + const FloatPoint& p0() const { return m_p0; } + const FloatPoint& p1() const { return m_p1; } + float r0() const { return m_r0; } + float r1() const { return m_r1; } + bool isRadial() const { return m_radial; } + struct ColorStop; + const Vector<ColorStop>& getStops() const; +#else +#endif // MANUAL_MERGE_REQUIRED PlatformGradient platformGradient(); +#ifdef MANUAL_MERGE_REQUIRED #endif +#else // MANUAL_MERGE_REQUIRED +#endif +#endif // MANUAL_MERGE_REQUIRED struct ColorStop { float stop; float red; @@ -97,11 +114,12 @@ namespace WebCore { void setSpreadMethod(GradientSpreadMethod); GradientSpreadMethod spreadMethod() { return m_spreadMethod; } - void setGradientSpaceTransform(const TransformationMatrix& gradientSpaceTransformation) { m_gradientSpaceTransformation = gradientSpaceTransformation; } + void setGradientSpaceTransform(const TransformationMatrix& gradientSpaceTransformation); // Qt and CG transform the gradient at draw time TransformationMatrix gradientSpaceTransform() { return m_gradientSpaceTransformation; } virtual void fill(GraphicsContext*, const FloatRect&); + void setPlatformGradientSpaceTransform(const TransformationMatrix& gradientSpaceTransformation); private: Gradient(const FloatPoint& p0, const FloatPoint& p1); diff --git a/WebCore/platform/graphics/GraphicsContext.cpp b/WebCore/platform/graphics/GraphicsContext.cpp index 4dae3d2..a4e8408 100644 --- a/WebCore/platform/graphics/GraphicsContext.cpp +++ b/WebCore/platform/graphics/GraphicsContext.cpp @@ -214,6 +214,7 @@ void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) } m_common->state.strokeColorSpace = PatternColorSpace; m_common->state.strokePattern = pattern; + setPlatformStrokePattern(m_common->state.strokePattern.get()); } void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) @@ -225,6 +226,7 @@ void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) } m_common->state.fillColorSpace = PatternColorSpace; m_common->state.fillPattern = pattern; + setPlatformFillPattern(m_common->state.fillPattern.get()); } void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) @@ -236,6 +238,7 @@ void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) } m_common->state.strokeColorSpace = GradientColorSpace; m_common->state.strokeGradient = gradient; + setPlatformStrokeGradient(m_common->state.strokeGradient.get()); } void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) @@ -247,6 +250,7 @@ void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) } m_common->state.fillColorSpace = GradientColorSpace; m_common->state.fillGradient = gradient; + setPlatformFillGradient(m_common->state.fillGradient.get()); } Gradient* GraphicsContext::fillGradient() const @@ -320,6 +324,7 @@ void GraphicsContext::drawImage(Image* image, const IntRect& dest, const IntRect drawImage(image, FloatRect(dest), srcRect, op, useLowQualityScale); } +#if !PLATFORM(WINCE) || PLATFORM(QT) void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to) { if (paintingDisabled()) @@ -327,6 +332,7 @@ void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPo font.drawText(this, run, point, from, to); } +#endif void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point) { @@ -508,6 +514,24 @@ void GraphicsContext::fillRect(const FloatRect& rect, Generator& generator) generator.fill(this, rect); } +#if !PLATFORM(SKIA) +void GraphicsContext::setPlatformFillGradient(Gradient*) +{ +} + +void GraphicsContext::setPlatformFillPattern(Pattern*) +{ +} + +void GraphicsContext::setPlatformStrokeGradient(Gradient*) +{ +} + +void GraphicsContext::setPlatformStrokePattern(Pattern*) +{ +} +#endif + #if !PLATFORM(CG) && !PLATFORM(SKIA) // Implement this if you want to go ahead and push the drawing mode into your native context // immediately. diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index 3fdafad..b52344d 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008-2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -71,6 +72,8 @@ class wxWindowDC; #endif #elif PLATFORM(SKIA) typedef class PlatformContextSkia PlatformGraphicsContext; +#elif PLATFORM(WINCE) +typedef struct HDC__ PlatformGraphicsContext; #else typedef void PlatformGraphicsContext; #endif @@ -94,6 +97,12 @@ typedef unsigned char UInt8; namespace WebCore { +#if PLATFORM(WINCE) && !PLATFORM(QT) + class SharedBitmap; + class SimpleFontData; + class GlyphBuffer; +#endif + const int cMisspellingLineThickness = 3; const int cMisspellingLinePatternWidth = 4; const int cMisspellingLinePatternGapWidth = 1; @@ -143,12 +152,14 @@ namespace WebCore { InterpolationHigh }; - class GraphicsContext : Noncopyable { + class GraphicsContext : public Noncopyable { public: GraphicsContext(PlatformGraphicsContext*); ~GraphicsContext(); +#if !PLATFORM(WINCE) || PLATFORM(QT) PlatformGraphicsContext* platformContext() const; +#endif float strokeThickness() const; void setStrokeThickness(float); @@ -329,7 +340,23 @@ namespace WebCore { void concatCTM(const TransformationMatrix&); TransformationMatrix getCTM() const; -#if PLATFORM(WIN) +#if PLATFORM(WINCE) && !PLATFORM(QT) + void setBitmap(PassRefPtr<SharedBitmap>); + const TransformationMatrix& affineTransform() const; + TransformationMatrix& affineTransform(); + void resetAffineTransform(); + void fillRect(const FloatRect&, const Gradient*); + void drawText(const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point); + void drawFrameControl(const IntRect& rect, unsigned type, unsigned state); + void drawFocusRect(const IntRect& rect); + void paintTextField(const IntRect& rect, unsigned state); + void drawBitmap(SharedBitmap*, const IntRect& dstRect, const IntRect& srcRect, CompositeOperator compositeOp); + void drawBitmapPattern(SharedBitmap*, const FloatRect& tileRectIn, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, const IntSize& origSourceSize); + void drawIcon(HICON icon, const IntRect& dstRect, UINT flags); + HDC getWindowsContext(const IntRect&, bool supportAlphaBlend = false, bool mayCreateBitmap = true); // The passed in rect is used to create a bitmap for compositing inside transparency layers. + void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend = false, bool mayCreateBitmap = true); // The passed in HDC should be the one handed back by getWindowsContext. + void drawRoundCorner(bool newClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height); +#elif PLATFORM(WIN) GraphicsContext(HDC, bool hasAlpha = false); // FIXME: To be removed. bool inTransparencyLayer() const; HDC getWindowsContext(const IntRect&, bool supportAlphaBlend = true, bool mayCreateBitmap = true); // The passed in rect is used to create a bitmap for compositing inside transparency layers. @@ -399,8 +426,12 @@ namespace WebCore { void setPlatformStrokeColor(const Color&); void setPlatformStrokeStyle(const StrokeStyle&); void setPlatformStrokeThickness(float); + void setPlatformStrokeGradient(Gradient*); + void setPlatformStrokePattern(Pattern*); void setPlatformFillColor(const Color&); + void setPlatformFillGradient(Gradient*); + void setPlatformFillPattern(Pattern*); void setPlatformShouldAntialias(bool b); diff --git a/WebCore/platform/graphics/GraphicsLayer.cpp b/WebCore/platform/graphics/GraphicsLayer.cpp index 0c442a2..7d43832 100644 --- a/WebCore/platform/graphics/GraphicsLayer.cpp +++ b/WebCore/platform/graphics/GraphicsLayer.cpp @@ -35,147 +35,24 @@ namespace WebCore { -void GraphicsLayer::FloatValue::set(float key, float value, const TimingFunction* timingFunction) -{ - m_key = key; - m_value = value; - if (timingFunction != m_timingFunction) { - if (timingFunction) - m_timingFunction.set(new TimingFunction(*timingFunction)); - else - m_timingFunction.clear(); - } -} - -void GraphicsLayer::TransformValue::set(float key, const TransformOperations* value, const TimingFunction* timingFunction) -{ - m_key = key; - if (value != m_value) { - if (value) - m_value.set(new TransformOperations(*value)); - else - m_value.clear(); - } - if (timingFunction != m_timingFunction) { - if (timingFunction) - m_timingFunction.set(new TimingFunction(*timingFunction)); - else - m_timingFunction.clear(); - } -} - -void GraphicsLayer::FloatValueList::insert(float key, float value, const TimingFunction* timingFunction) -{ - for (size_t i = 0; i < m_values.size(); ++i) { - FloatValue& curFloatValue = m_values[i]; - if (curFloatValue.key() == key) { - curFloatValue.set(key, value, timingFunction); - return; - } - if (curFloatValue.key() > key) { - // insert before - m_values.insert(i, FloatValue(key, value, timingFunction)); - return; - } - } - - // append - m_values.append(FloatValue(key, value, timingFunction)); -} - -void GraphicsLayer::TransformValueList::insert(float key, const TransformOperations* value, const TimingFunction* timingFunction) +void KeyframeValueList::insert(const AnimationValue* value) { for (size_t i = 0; i < m_values.size(); ++i) { - TransformValue& curTransValue = m_values[i]; - if (curTransValue.key() == key) { - curTransValue.set(key, value, timingFunction); + const AnimationValue* curValue = m_values[i]; + if (curValue->keyTime() == value->keyTime()) { + ASSERT_NOT_REACHED(); + // insert after + m_values.insert(i + 1, value); return; } - if (curTransValue.key() > key) { + if (curValue->keyTime() > value->keyTime()) { // insert before - m_values.insert(i, TransformValue(key, value, timingFunction)); + m_values.insert(i, value); return; } } - // append - m_values.append(TransformValue(key, value, timingFunction)); -} - -// An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix -// The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is -// true if the rotation between any two keyframes is >= 180 degrees. -void GraphicsLayer::TransformValueList::makeFunctionList(FunctionList& list, bool& isValid, bool& hasBigRotation) const -{ - list.clear(); - isValid = false; - hasBigRotation = false; - - if (m_values.size() < 2) - return; - - // empty transforms match anything, so find the first non-empty entry as the reference - size_t firstIndex = 0; - for ( ; firstIndex < m_values.size(); ++firstIndex) { - if (m_values[firstIndex].value()->operations().size() > 0) - break; - } - - if (firstIndex >= m_values.size()) - return; - - const TransformOperations* firstVal = m_values[firstIndex].value(); - - // see if the keyframes are valid - for (size_t i = firstIndex + 1; i < m_values.size(); ++i) { - const TransformOperations* val = m_values[i].value(); - - // a null transform matches anything - if (val->operations().isEmpty()) - continue; - - if (firstVal->operations().size() != val->operations().size()) - return; - - for (size_t j = 0; j < firstVal->operations().size(); ++j) { - if (!firstVal->operations().at(j)->isSameType(*val->operations().at(j))) - return; - } - } - - // keyframes are valid, fill in the list - isValid = true; - - double lastRotAngle = 0.0; - double maxRotAngle = -1.0; - - list.resize(firstVal->operations().size()); - for (size_t j = 0; j < firstVal->operations().size(); ++j) { - TransformOperation::OperationType type = firstVal->operations().at(j)->getOperationType(); - list[j] = type; - - // if this is a rotation entry, we need to see if any angle differences are >= 180 deg - if (type == TransformOperation::ROTATE_X || - type == TransformOperation::ROTATE_Y || - type == TransformOperation::ROTATE_Z || - type == TransformOperation::ROTATE_3D) { - lastRotAngle = static_cast<RotateTransformOperation*>(firstVal->operations().at(j).get())->angle(); - - if (maxRotAngle < 0) - maxRotAngle = fabs(lastRotAngle); - - for (size_t i = firstIndex + 1; i < m_values.size(); ++i) { - const TransformOperations* val = m_values[i].value(); - double rotAngle = val->operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val->operations().at(j).get())->angle()); - double diffAngle = fabs(rotAngle - lastRotAngle); - if (diffAngle > maxRotAngle) - maxRotAngle = diffAngle; - lastRotAngle = rotAngle; - } - } - } - - hasBigRotation = maxRotAngle >= 180.0; + m_values.append(value); } GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) @@ -193,6 +70,8 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) , m_masksToBounds(false) , m_drawsContent(false) , m_paintingPhase(GraphicsLayerPaintAllMask) + , m_geometryOrientation(CompositingCoordinatesTopDown) + , m_contentsOrientation(CompositingCoordinatesTopDown) , m_parent(0) #ifndef NDEBUG , m_repaintCount(0) @@ -202,8 +81,6 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) GraphicsLayer::~GraphicsLayer() { - removeAllAnimations(); - removeAllChildren(); removeFromParent(); } @@ -315,9 +192,9 @@ void GraphicsLayer::removeFromParent() } } -void GraphicsLayer::setBackgroundColor(const Color& inColor, const Animation*, double /*beginTime*/) +void GraphicsLayer::setBackgroundColor(const Color& color) { - m_backgroundColor = inColor; + m_backgroundColor = color; m_backgroundColorSet = true; } @@ -327,96 +204,13 @@ void GraphicsLayer::clearBackgroundColor() m_backgroundColorSet = false; } -bool GraphicsLayer::setOpacity(float opacity, const Animation*, double) -{ - m_opacity = opacity; - return false; // not animating -} - void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const IntRect& clip) { - m_client->paintContents(this, context, m_paintingPhase, clip); -} - -String GraphicsLayer::propertyIdToString(AnimatedPropertyID property) -{ - switch (property) { - case AnimatedPropertyWebkitTransform: - return "transform"; - case AnimatedPropertyOpacity: - return "opacity"; - case AnimatedPropertyBackgroundColor: - return "backgroundColor"; - case AnimatedPropertyInvalid: - ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return ""; -} - -int GraphicsLayer::findAnimationEntry(AnimatedPropertyID property, short index) const -{ - for (size_t i = 0; i < m_animations.size(); ++i) { - if (m_animations[i].matches(property, index)) - return static_cast<int>(i); - } - return -1; -} - -void GraphicsLayer::addAnimationEntry(AnimatedPropertyID property, short index, bool isTransition, const Animation* transition) -{ - int i = findAnimationEntry(property, index); - - if (i >= 0) - m_animations[i].reset(transition, isTransition); - else - m_animations.append(AnimationEntry(transition, property, index, isTransition)); -} - -void GraphicsLayer::removeAllAnimations() -{ - size_t size = m_animations.size(); - for (size_t i = 0; i < size; ++i) - removeAnimation(0, true); -} - -void GraphicsLayer::removeAllAnimationsForProperty(AnimatedPropertyID property) -{ - for (short j = 0; ; ++j) { - int i = findAnimationEntry(property, j); - if (i < 0) - break; - removeAnimation(i, false); - } -} - -void GraphicsLayer::removeFinishedAnimations(const String& name, int /*index*/, bool reset) -{ - size_t size = m_animations.size(); - for (size_t i = 0; i < size; ) { - AnimationEntry& anim = m_animations[i]; - if (!anim.isTransition() && anim.animation()->name() == name) { - removeAnimation(i, reset); - --size; - } else - ++i; - } -} - -void GraphicsLayer::removeFinishedTransitions(AnimatedPropertyID property) -{ - size_t size = m_animations.size(); - for (size_t i = 0; i < size; ) { - AnimationEntry& anim = m_animations[i]; - if (anim.isTransition() && property == anim.property()) { - removeAnimation(i, false); - --size; - } else - ++i; - } + if (m_client) + m_client->paintContents(this, context, m_paintingPhase, clip); } -void GraphicsLayer::suspendAnimations() +void GraphicsLayer::suspendAnimations(double) { } @@ -448,6 +242,116 @@ void GraphicsLayer::setZPosition(float position) } #endif +float GraphicsLayer::accumulatedOpacity() const +{ + if (!preserves3D()) + return 1; + + return m_opacity * (parent() ? parent()->accumulatedOpacity() : 1); +} + +void GraphicsLayer::distributeOpacity(float accumulatedOpacity) +{ + // If this is a transform layer we need to distribute our opacity to all our children + + // Incoming accumulatedOpacity is the contribution from our parent(s). We mutiply this by our own + // opacity to get the total contribution + accumulatedOpacity *= m_opacity; + + setOpacityInternal(accumulatedOpacity); + + if (preserves3D()) { + size_t numChildren = children().size(); + for (size_t i = 0; i < numChildren; ++i) + children()[i]->distributeOpacity(accumulatedOpacity); + } +} + +// An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix +// The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is +// true if the rotation between any two keyframes is >= 180 degrees. + +static inline const TransformOperations* operationsAt(const KeyframeValueList& valueList, size_t index) +{ + return static_cast<const TransformAnimationValue*>(valueList.at(index))->value(); +} + +void GraphicsLayer::fetchTransformOperationList(const KeyframeValueList& valueList, TransformOperationList& list, bool& isValid, bool& hasBigRotation) +{ + ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); + + list.clear(); + isValid = false; + hasBigRotation = false; + + if (valueList.size() < 2) + return; + + // Empty transforms match anything, so find the first non-empty entry as the reference. + size_t firstIndex = 0; + for ( ; firstIndex < valueList.size(); ++firstIndex) { + if (operationsAt(valueList, firstIndex)->operations().size() > 0) + break; + } + + if (firstIndex >= valueList.size()) + return; + + const TransformOperations* firstVal = operationsAt(valueList, firstIndex); + + // See if the keyframes are valid. + for (size_t i = firstIndex + 1; i < valueList.size(); ++i) { + const TransformOperations* val = operationsAt(valueList, i); + + // a null transform matches anything + if (val->operations().isEmpty()) + continue; + + if (firstVal->operations().size() != val->operations().size()) + return; + + for (size_t j = 0; j < firstVal->operations().size(); ++j) { + if (!firstVal->operations().at(j)->isSameType(*val->operations().at(j))) + return; + } + } + + // Keyframes are valid, fill in the list. + isValid = true; + + double lastRotAngle = 0.0; + double maxRotAngle = -1.0; + + list.resize(firstVal->operations().size()); + for (size_t j = 0; j < firstVal->operations().size(); ++j) { + TransformOperation::OperationType type = firstVal->operations().at(j)->getOperationType(); + list[j] = type; + + // if this is a rotation entry, we need to see if any angle differences are >= 180 deg + if (type == TransformOperation::ROTATE_X || + type == TransformOperation::ROTATE_Y || + type == TransformOperation::ROTATE_Z || + type == TransformOperation::ROTATE_3D) { + lastRotAngle = static_cast<RotateTransformOperation*>(firstVal->operations().at(j).get())->angle(); + + if (maxRotAngle < 0) + maxRotAngle = fabs(lastRotAngle); + + for (size_t i = firstIndex + 1; i < valueList.size(); ++i) { + const TransformOperations* val = operationsAt(valueList, i); + double rotAngle = val->operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val->operations().at(j).get())->angle()); + double diffAngle = fabs(rotAngle - lastRotAngle); + if (diffAngle > maxRotAngle) + maxRotAngle = diffAngle; + lastRotAngle = rotAngle; + } + } + } + + hasBigRotation = maxRotAngle >= 180.0; +} + + static void writeIndent(TextStream& ts, int indent) { for (int i = 0; i != indent; ++i) diff --git a/WebCore/platform/graphics/GraphicsLayer.h b/WebCore/platform/graphics/GraphicsLayer.h index ae51951..4d7668a 100644 --- a/WebCore/platform/graphics/GraphicsLayer.h +++ b/WebCore/platform/graphics/GraphicsLayer.h @@ -34,6 +34,7 @@ #include "FloatPoint3D.h" #include "FloatSize.h" #include "GraphicsLayerClient.h" +#include "IntRect.h" #include "TransformationMatrix.h" #include "TransformOperations.h" #include <wtf/OwnPtr.h> @@ -42,7 +43,7 @@ #ifdef __OBJC__ @class WebLayer; @class CALayer; -typedef WebLayer PlatformLayer; +typedef CALayer PlatformLayer; typedef CALayer* NativeLayer; #else typedef void* PlatformLayer; @@ -61,117 +62,95 @@ class Image; class TextStream; class TimingFunction; -// GraphicsLayer is an abstraction for a rendering surface with backing store, -// which may have associated transformation and animations. +// Base class for animation values (also used for transitions). Here to +// represent values for properties being animated via the GraphicsLayer, +// without pulling in style-related data from outside of the platform directory. +class AnimationValue : public Noncopyable { +public: + AnimationValue(float keyTime, const TimingFunction* timingFunction = 0) + : m_keyTime(keyTime) + , m_timingFunction(0) + { + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + } + + virtual ~AnimationValue() { } -class GraphicsLayer { + float keyTime() const { return m_keyTime; } + const TimingFunction* timingFunction() const { return m_timingFunction.get(); } + +private: + float m_keyTime; + OwnPtr<TimingFunction> m_timingFunction; +}; + +// Used to store one float value of an animation. +class FloatAnimationValue : public AnimationValue { +public: + FloatAnimationValue(float keyTime, float value, const TimingFunction* timingFunction = 0) + : AnimationValue(keyTime, timingFunction) + , m_value(value) + { + } + + float value() const { return m_value; } + +private: + float m_value; +}; + +// Used to store one transform value in a keyframe list. +class TransformAnimationValue : public AnimationValue { public: - // Used to store one float value of a keyframe animation. - class FloatValue { - public: - FloatValue(float key, float value, const TimingFunction* timingFunction = 0) - : m_key(key), m_value(value), m_timingFunction(0) - { - if (timingFunction) - m_timingFunction.set(new TimingFunction(*timingFunction)); - } - - FloatValue(const FloatValue& other) - : m_key(other.key()), m_value(other.value()), m_timingFunction(0) - { - if (other.timingFunction()) - m_timingFunction.set(new TimingFunction(*other.timingFunction())); - } - - const FloatValue& operator=(const FloatValue& other) - { - if (&other != this) - set(other.key(), other.value(), other.timingFunction()); - return *this; - } - - void set(float key, float value, const TimingFunction*); - - float key() const { return m_key; } - float value() const { return m_value; } - const TimingFunction* timingFunction() const { return m_timingFunction.get(); } - - private: - float m_key; - float m_value; - OwnPtr<TimingFunction> m_timingFunction; - }; + TransformAnimationValue(float keyTime, const TransformOperations* value = 0, const TimingFunction* timingFunction = 0) + : AnimationValue(keyTime, timingFunction) + { + if (value) + m_value.set(new TransformOperations(*value)); + } + + const TransformOperations* value() const { return m_value.get(); } + +private: + OwnPtr<TransformOperations> m_value; +}; + +// Used to store a series of values in a keyframe list. Values will all be of the same type, +// which can be inferred from the property. +class KeyframeValueList : public Noncopyable { +public: + + KeyframeValueList(AnimatedPropertyID property) + : m_property(property) + { + } + ~KeyframeValueList() + { + deleteAllValues(m_values); + } - class FloatValueList { - public: - void insert(float key, float value, const TimingFunction* timingFunction); - - size_t size() const { return m_values.size(); } - const FloatValue& at(size_t i) const { return m_values.at(i); } - const Vector<FloatValue>& values() const { return m_values; } - - private: - Vector<FloatValue> m_values; - }; - - // Used to store one transform in a keyframe list. - class TransformValue { - public: - TransformValue(float key = NAN, const TransformOperations* value = 0, const TimingFunction* timingFunction = 0) - : m_key(key) - { - if (value) - m_value.set(new TransformOperations(*value)); - if (timingFunction) - m_timingFunction.set(new TimingFunction(*timingFunction)); - } - - TransformValue(const TransformValue& other) - : m_key(other.key()) - { - if (other.value()) - m_value.set(new TransformOperations(*other.value())); - if (other.timingFunction()) - m_timingFunction.set(new TimingFunction(*other.timingFunction())); - } - - const TransformValue& operator=(const TransformValue& other) - { - if (&other != this) - set(other.key(), other.value(), other.timingFunction()); - return *this; - } - - void set(float key, const TransformOperations* value, const TimingFunction* timingFunction); - - float key() const { return m_key; } - const TransformOperations* value() const { return m_value.get(); } - const TimingFunction* timingFunction() const { return m_timingFunction.get(); } - - private: - float m_key; - OwnPtr<TransformOperations> m_value; - OwnPtr<TimingFunction> m_timingFunction; - }; + AnimatedPropertyID property() const { return m_property; } + + size_t size() const { return m_values.size(); } + const AnimationValue* at(size_t i) const { return m_values.at(i); } + + // Insert, sorted by keyTime. Takes ownership of the pointer. + void insert(const AnimationValue*); - // Used to store a series of transforms in a keyframe list. - class TransformValueList { - public: - typedef Vector<TransformOperation::OperationType> FunctionList; - - size_t size() const { return m_values.size(); } - const TransformValue& at(size_t i) const { return m_values.at(i); } - const Vector<TransformValue>& values() const { return m_values; } - - void insert(float key, const TransformOperations* value, const TimingFunction* timingFunction); - - // return a list of the required functions. List is empty if keyframes are not valid - // If return value is true, functions contain rotations of >= 180 degrees - void makeFunctionList(FunctionList& list, bool& isValid, bool& hasBigRotation) const; - private: - Vector<TransformValue> m_values; - }; +protected: + Vector<const AnimationValue*> m_values; + AnimatedPropertyID m_property; +}; + + + +// GraphicsLayer is an abstraction for a rendering surface with backing store, +// which may have associated transformation and animations. + +class GraphicsLayer { +public: static GraphicsLayer* createGraphicsLayer(GraphicsLayerClient*); @@ -235,7 +214,7 @@ public: // The color used to paint the layer backgrounds const Color& backgroundColor() const { return m_backgroundColor; } - virtual void setBackgroundColor(const Color&, const Animation* = 0, double beginTime = 0); + virtual void setBackgroundColor(const Color&); virtual void clearBackgroundColor(); bool backgroundColorSet() const { return m_backgroundColorSet; } @@ -247,8 +226,7 @@ public: virtual void setBackfaceVisibility(bool b) { m_backfaceVisibility = b; } float opacity() const { return m_opacity; } - // return true if we started an animation - virtual bool setOpacity(float o, const Animation* = 0, double beginTime = 0); + virtual void setOpacity(float opacity) { m_opacity = opacity; } // Some GraphicsLayers paint only the foreground or the background content GraphicsLayerPaintingPhase drawingPhase() const { return m_paintingPhase; } @@ -258,24 +236,25 @@ public: // mark the given rect (in layer coords) as needing dispay. Never goes deep. virtual void setNeedsDisplayInRect(const FloatRect&) = 0; - virtual bool animateTransform(const TransformValueList&, const IntSize&, const Animation*, double beginTime, bool isTransition) = 0; - virtual bool animateFloat(AnimatedPropertyID, const FloatValueList&, const Animation*, double beginTime) = 0; + // Set that the position/size of the contents (image or video). + IntRect contentsRect() const { return m_contentsRect; } + virtual void setContentsRect(const IntRect& r) { m_contentsRect = r; } - void removeFinishedAnimations(const String& name, int index, bool reset); - void removeFinishedTransitions(AnimatedPropertyID); - void removeAllAnimations(); - - virtual void suspendAnimations(); + // Return true if the animation is handled by the compositing system. If this returns + // false, the animation will be run by AnimationController. + virtual bool addAnimation(const KeyframeValueList&, const IntSize& /*boxSize*/, const Animation*, const String& /*keyframesName*/, double /*beginTime*/) { return false; } + virtual void removeAnimationsForProperty(AnimatedPropertyID) { } + virtual void removeAnimationsForKeyframes(const String& /* keyframesName */) { } + virtual void pauseAnimation(const String& /* keyframesName */) { } + + virtual void suspendAnimations(double time); virtual void resumeAnimations(); // Layer contents virtual void setContentsToImage(Image*) { } virtual void setContentsToVideo(PlatformLayer*) { } virtual void setContentsBackgroundColor(const Color&) { } - virtual void clearContents() { } - virtual void updateContentsRect() { } - // Callback from the underlying graphics system to draw layer contents. void paintGraphicsLayerContents(GraphicsContext&, const IntRect& clip); @@ -293,6 +272,14 @@ public: enum CompositingCoordinatesOrientation { CompositingCoordinatesTopDown, CompositingCoordinatesBottomUp }; static CompositingCoordinatesOrientation compositingCoordinatesOrientation(); + // Set the geometry orientation (top-down, or bottom-up) for this layer, which also controls sublayer geometry. + virtual void setGeometryOrientation(CompositingCoordinatesOrientation orientation) { m_geometryOrientation = orientation; } + CompositingCoordinatesOrientation geometryOrientation() const { return m_geometryOrientation; } + + // Flippedness of the contents of this layer. Does not affect sublayer geometry. + virtual void setContentsOrientation(CompositingCoordinatesOrientation orientation) { m_contentsOrientation = orientation; } + CompositingCoordinatesOrientation contentsOrientation() const { return m_contentsOrientation; } + #ifndef NDEBUG static bool showDebugBorders(); static bool showRepaintCounter(); @@ -306,19 +293,25 @@ public: virtual void setZPosition(float); #endif - static String propertyIdToString(AnimatedPropertyID); - + virtual void distributeOpacity(float); + virtual float accumulatedOpacity() const; + + // Some compositing systems may do internal batching to synchronize compositing updates + // with updates drawn into the window. This is a signal to flush any internal batched state. + virtual void syncCompositingState() { } + protected: - GraphicsLayer(GraphicsLayerClient*); - void dumpProperties(TextStream&, int indent) const; + typedef Vector<TransformOperation::OperationType> TransformOperationList; + // Given a list of TransformAnimationValues, return an array of transform operations. + // On return, if hasBigRotation is true, functions contain rotations of >= 180 degrees + static void fetchTransformOperationList(const KeyframeValueList&, TransformOperationList&, bool& isValid, bool& hasBigRotation); - // returns -1 if not found - int findAnimationEntry(AnimatedPropertyID, short index) const; - void addAnimationEntry(AnimatedPropertyID, short index, bool isTransition, const Animation*); + virtual void setOpacityInternal(float) { } + + GraphicsLayer(GraphicsLayerClient*); - virtual void removeAnimation(int /*index*/, bool /*reset*/) {} - void removeAllAnimationsForProperty(AnimatedPropertyID); + void dumpProperties(TextStream&, int indent) const; GraphicsLayerClient* m_client; String m_name; @@ -348,53 +341,14 @@ protected: bool m_drawsContent : 1; GraphicsLayerPaintingPhase m_paintingPhase; + CompositingCoordinatesOrientation m_geometryOrientation; // affects geometry of layer positions + CompositingCoordinatesOrientation m_contentsOrientation; // affects orientation of layer contents Vector<GraphicsLayer*> m_children; GraphicsLayer* m_parent; - - // AnimationEntry represents an animation of a property on this layer. - // For transform only, there may be more than one, in which case 'index' - // is an index into the list of transforms. - class AnimationEntry { - public: - AnimationEntry(const Animation* animation, AnimatedPropertyID property, short index, bool isTransition) - : m_animation(const_cast<Animation*>(animation)) - , m_property(property) - , m_index(index) - , m_isCurrent(true) - , m_isTransition(isTransition) - { - } - - const Animation* animation() const { return m_animation.get(); } - AnimatedPropertyID property() const { return m_property; } - int index() const { return m_index; } - bool isCurrent() const { return m_isCurrent; } - void setIsCurrent(bool b = true) { m_isCurrent = b; } - bool isTransition() const { return m_isTransition; } - - bool matches(AnimatedPropertyID property, short index) const - { - return m_property == property && m_index == index; - } - - void reset(const Animation* animation, bool isTransition) - { - m_animation = const_cast<Animation*>(animation); - m_isTransition = isTransition; - m_isCurrent = true; - } - - private: - RefPtr<Animation> m_animation; - AnimatedPropertyID m_property : 14; - short m_index : 16; - bool m_isCurrent : 1; - bool m_isTransition : 1; - }; - - Vector<AnimationEntry> m_animations; // running animations/transitions - + + IntRect m_contentsRect; + #ifndef NDEBUG int m_repaintCount; #endif diff --git a/WebCore/platform/graphics/GraphicsLayerClient.h b/WebCore/platform/graphics/GraphicsLayerClient.h index 46382f2..8c0b7ed 100644 --- a/WebCore/platform/graphics/GraphicsLayerClient.h +++ b/WebCore/platform/graphics/GraphicsLayerClient.h @@ -53,13 +53,14 @@ class GraphicsLayerClient { public: virtual ~GraphicsLayerClient() {} - // Callbacks for when hardware-accelerated transitions and animation started + // Callback for when hardware-accelerated animation started. virtual void notifyAnimationStarted(const GraphicsLayer*, double time) = 0; + + // Notification that a layer property changed that requires a subsequent call to syncCompositingState() + // to appear on the screen. + virtual void notifySyncRequired(const GraphicsLayer*) = 0; virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) = 0; - - // Return a rect for the "contents" of the graphics layer, i.e. video or image content, in GraphicsLayer coordinates. - virtual IntRect contentsBox(const GraphicsLayer*) = 0; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/Image.cpp b/WebCore/platform/graphics/Image.cpp index 08d96b4..80b5457 100644 --- a/WebCore/platform/graphics/Image.cpp +++ b/WebCore/platform/graphics/Image.cpp @@ -119,7 +119,6 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); - TransformationMatrix patternTransform = TransformationMatrix().scaleNonUniform(scale.width(), scale.height()); FloatRect oneTileRect; oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), scaledTileSize.width()) - scaledTileSize.width(), scaledTileSize.width())); @@ -137,6 +136,7 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl return; } + TransformationMatrix patternTransform = TransformationMatrix().scaleNonUniform(scale.width(), scale.height()); FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), op, destRect); diff --git a/WebCore/platform/graphics/Image.h b/WebCore/platform/graphics/Image.h index cff8b22..294ddf6 100644 --- a/WebCore/platform/graphics/Image.h +++ b/WebCore/platform/graphics/Image.h @@ -31,9 +31,9 @@ #include "GraphicsTypes.h" #include "ImageSource.h" #include "IntRect.h" -#include <wtf/RefPtr.h> -#include <wtf/PassRefPtr.h> #include "SharedBuffer.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> #if PLATFORM(MAC) #ifdef __OBJC__ @@ -67,13 +67,13 @@ typedef struct _GdkPixbuf GdkPixbuf; namespace WebCore { -class TransformationMatrix; class FloatPoint; class FloatRect; class FloatSize; class GraphicsContext; class SharedBuffer; class String; +class TransformationMatrix; // This class gets notified when an image creates or destroys decoded frames and when it advances animation frames. class ImageObserver; @@ -81,6 +81,7 @@ class ImageObserver; class Image : public RefCounted<Image> { friend class GeneratedImage; friend class GraphicsContext; + public: virtual ~Image(); @@ -172,13 +173,8 @@ protected: virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); -#if PLATFORM(CG) - // These are private to CG. Ideally they would be only in the .cpp file, but the callback requires access - // to the private function nativeImageForCurrentFrame() - static void drawPatternCallback(void* info, CGContext*); -#endif - -protected: + +private: RefPtr<SharedBuffer> m_data; // The encoded raw data for the image. ImageObserver* m_imageObserver; }; diff --git a/WebCore/platform/graphics/ImageBuffer.cpp b/WebCore/platform/graphics/ImageBuffer.cpp new file mode 100644 index 0000000..55305d1 --- /dev/null +++ b/WebCore/platform/graphics/ImageBuffer.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * + * 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 "ImageBuffer.h" + +#if !PLATFORM(CG) + +#include <math.h> + +namespace WebCore { + +void ImageBuffer::transformColorSpace(ImageColorSpace srcColorSpace, ImageColorSpace dstColorSpace) +{ + if (srcColorSpace == dstColorSpace) + return; + + // only sRGB <-> linearRGB are supported at the moment + if ((srcColorSpace != LinearRGB && srcColorSpace != DeviceRGB) || + (dstColorSpace != LinearRGB && dstColorSpace != DeviceRGB)) + return; + + Vector<int> lookUpTable; + if (dstColorSpace == LinearRGB) { + if (m_linearRgbLUT.isEmpty()) { + for (unsigned i = 0; i < 256; i++) { + float color = i / 255.0f; + color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) / 1.055f, 2.4f)); + color = std::max(0.0f, color); + color = std::min(1.0f, color); + m_linearRgbLUT.append(static_cast<int>(color * 255)); + } + } + platformTransformColorSpace(m_linearRgbLUT); + } else if (dstColorSpace == DeviceRGB) { + if (m_deviceRgbLUT.isEmpty()) { + for (unsigned i = 0; i < 256; i++) { + float color = i / 255.0f; + color = pow(1.055f * color, 1.0f / 2.4f) - 0.055f; + color = std::max(0.0f, color); + color = std::min(1.0f, color); + m_deviceRgbLUT.append(static_cast<int>(color * 255)); + } + } + platformTransformColorSpace(m_deviceRgbLUT); + } +} + +} + +#endif // PLATFORM(CG) diff --git a/WebCore/platform/graphics/ImageBuffer.h b/WebCore/platform/graphics/ImageBuffer.h index 573e274..2a96d3b 100644 --- a/WebCore/platform/graphics/ImageBuffer.h +++ b/WebCore/platform/graphics/ImageBuffer.h @@ -43,13 +43,20 @@ namespace WebCore { class IntRect; class String; - class ImageBuffer : Noncopyable { + enum ImageColorSpace { + Unknown, + DeviceRGB, // like sRGB + GrayScale, + LinearRGB + }; + + class ImageBuffer : public Noncopyable { public: // Will return a null pointer on allocation failure. - static PassOwnPtr<ImageBuffer> create(const IntSize& size, bool grayScale) + static PassOwnPtr<ImageBuffer> create(const IntSize& size, ImageColorSpace colorSpace = DeviceRGB) { bool success = false; - OwnPtr<ImageBuffer> buf(new ImageBuffer(size, grayScale, success)); + OwnPtr<ImageBuffer> buf(new ImageBuffer(size, colorSpace, success)); if (success) return buf.release(); return 0; @@ -70,6 +77,8 @@ namespace WebCore { String toDataURL(const String& mimeType) const; #if !PLATFORM(CG) TransformationMatrix baseTransform() const { return TransformationMatrix(); } + void transformColorSpace(ImageColorSpace srcColorSpace, ImageColorSpace dstColorSpace); + void platformTransformColorSpace(const Vector<int>&); #else TransformationMatrix baseTransform() const { return TransformationMatrix(1, 0, 0, -1, 0, m_size.height()); } #endif @@ -80,9 +89,14 @@ namespace WebCore { OwnPtr<GraphicsContext> m_context; mutable RefPtr<Image> m_image; +#if !PLATFORM(CG) + Vector<int> m_linearRgbLUT; + Vector<int> m_deviceRgbLUT; +#endif + // This constructor will place its success into the given out-variable // so that create() knows when it should return failure. - ImageBuffer(const IntSize&, bool grayScale, bool& success); + ImageBuffer(const IntSize&, ImageColorSpace colorSpace, bool& success); }; } // namespace WebCore diff --git a/WebCore/platform/graphics/ImageSource.h b/WebCore/platform/graphics/ImageSource.h index d4a1658..c9ac03a 100644 --- a/WebCore/platform/graphics/ImageSource.h +++ b/WebCore/platform/graphics/ImageSource.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007-2008 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -50,6 +51,8 @@ class SkBitmapRef; class PrivateAndroidImageSourceRec; #elif PLATFORM(SKIA) class NativeImageSkia; +#elif PLATFORM(WINCE) +#include "SharedBitmap.h" #endif namespace WebCore { @@ -96,12 +99,16 @@ typedef cairo_surface_t* NativeImagePtr; class ImageDecoder; typedef ImageDecoder* NativeImageSourcePtr; typedef NativeImageSkia* NativeImagePtr; +#elif PLATFORM(WINCE) +class ImageDecoder; +typedef ImageDecoder* NativeImageSourcePtr; +typedef RefPtr<SharedBitmap> NativeImagePtr; #endif const int cAnimationLoopOnce = -1; const int cAnimationNone = -2; -class ImageSource : Noncopyable { +class ImageSource : public Noncopyable { public: ImageSource(); ~ImageSource(); @@ -153,6 +160,7 @@ public: bool frameHasAlphaAtIndex(size_t); // Whether or not the frame actually used any alpha. bool frameIsCompleteAtIndex(size_t); // Whether or not the frame is completely decoded. +#ifdef MANUAL_MERGE_REQUIRED #if PLATFORM(SGL) void clearURL(); void setURL(const String& url); @@ -161,6 +169,9 @@ private: // FIXME: This is protected only to allow ImageSourceSkia to set ICO decoder // with a preferred size. See ImageSourceSkia.h for discussion. protected: +#else // MANUAL_MERGE_REQUIRED +private: +#endif // MANUAL_MERGE_REQUIRED NativeImageSourcePtr m_decoder; }; diff --git a/WebCore/platform/graphics/IntPoint.h b/WebCore/platform/graphics/IntPoint.h index 1bfeeaa..e6d4816 100644 --- a/WebCore/platform/graphics/IntPoint.h +++ b/WebCore/platform/graphics/IntPoint.h @@ -29,10 +29,15 @@ #include "IntSize.h" #include <wtf/Platform.h> +#if PLATFORM(QT) +#include <QDataStream> +#endif + #if PLATFORM(CG) typedef struct CGPoint CGPoint; #endif + #if PLATFORM(MAC) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGPoint NSPoint; @@ -51,9 +56,6 @@ QT_END_NAMESPACE #elif PLATFORM(GTK) typedef struct _GdkPoint GdkPoint; #endif -#if PLATFORM(SYMBIAN) -class TPoint; -#endif #if PLATFORM(WX) class wxPoint; @@ -78,6 +80,7 @@ public: void setX(int x) { m_x = x; } void setY(int y) { m_y = y; } + void move(const IntSize& s) { move(s.width(), s.height()); } void move(int dx, int dy) { m_x += dx; m_y += dy; } IntPoint expandedTo(const IntPoint& other) const @@ -119,10 +122,6 @@ public: IntPoint(const GdkPoint&); operator GdkPoint() const; #endif -#if PLATFORM(SYMBIAN) - IntPoint(const TPoint&); - operator TPoint() const; -#endif #if PLATFORM(WX) IntPoint(const wxPoint&); @@ -176,6 +175,23 @@ inline bool operator!=(const IntPoint& a, const IntPoint& b) return a.x() != b.x() || a.y() != b.y(); } +#if PLATFORM(QT) +inline QDataStream& operator<<(QDataStream& stream, const IntPoint& point) +{ + stream << point.x() << point.y(); + return stream; +} + +inline QDataStream& operator>>(QDataStream& stream, IntPoint& point) +{ + int x, y; + stream >> x >> y; + point.setX(x); + point.setY(y); + return stream; +} +#endif + } // namespace WebCore #endif // IntPoint_h diff --git a/WebCore/platform/graphics/IntRect.h b/WebCore/platform/graphics/IntRect.h index 1be98b8..0b607f5 100644 --- a/WebCore/platform/graphics/IntRect.h +++ b/WebCore/platform/graphics/IntRect.h @@ -50,9 +50,6 @@ QT_END_NAMESPACE #elif PLATFORM(GTK) typedef struct _GdkRectangle GdkRectangle; #endif -#if PLATFORM(SYMBIAN) -class TRect; -#endif #if PLATFORM(WX) class wxRect; @@ -148,11 +145,6 @@ public: IntRect(const GdkRectangle&); operator GdkRectangle() const; #endif -#if PLATFORM(SYMBIAN) - IntRect(const TRect&); - operator TRect() const; - TRect Rect() const; -#endif #if PLATFORM(CG) operator CGRect() const; diff --git a/WebCore/platform/graphics/IntSize.h b/WebCore/platform/graphics/IntSize.h index cac0bd1..dc7a85d 100644 --- a/WebCore/platform/graphics/IntSize.h +++ b/WebCore/platform/graphics/IntSize.h @@ -48,9 +48,6 @@ QT_BEGIN_NAMESPACE class QSize; QT_END_NAMESPACE #endif -#if PLATFORM(SYMBIAN) -class TSize; -#endif namespace WebCore { @@ -115,10 +112,6 @@ public: IntSize(const QSize&); operator QSize() const; #endif -#if PLATFORM(SYMBIAN) - IntSize(const TSize&); - operator TSize() const; -#endif private: diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index fb7225a..6205a7b 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -38,6 +38,8 @@ #if PLATFORM(MAC) #include "MediaPlayerPrivateQTKit.h" +#elif PLATFORM(WINCE) +#include "MediaPlayerPrivateWince.h" #elif PLATFORM(WIN) #include "MediaPlayerPrivateQuickTimeWin.h" #elif PLATFORM(GTK) @@ -64,6 +66,8 @@ public: virtual void play() { } virtual void pause() { } + virtual bool supportsFullscreen() const { return false; } + virtual IntSize naturalSize() const { return IntSize(0, 0); } virtual bool hasVideo() const { return false; } @@ -79,6 +83,7 @@ public: virtual void setEndTime(float) { } virtual void setRate(float) { } + virtual void setPreservesPitch(bool) { } virtual bool paused() const { return false; } virtual void setVolume(float) { } @@ -104,6 +109,8 @@ public: virtual void deliverNotification(MediaPlayerProxyNotificationType) { } virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) { } #endif + + virtual bool hasSingleSecurityOrigin() const { return true; } }; static MediaPlayerPrivateInterface* createNullMediaPlayer(MediaPlayer* player) @@ -185,6 +192,7 @@ MediaPlayer::MediaPlayer(MediaPlayerClient* client) , m_visible(false) , m_rate(1.0f) , m_volume(1.0f) + , m_preservesPitch(true) , m_autobuffer(false) #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) , m_playerProxy(0) @@ -225,7 +233,7 @@ void MediaPlayer::load(const String& url, const ContentType& contentType) engine = chooseBestEngineForTypeAndCodecs(type, codecs); // if we didn't find an engine that claims the MIME type, just use the first engine - if (!engine) + if (!engine && !installedMediaEngines().isEmpty()) engine = installedMediaEngines()[0]; // don't delete and recreate the player unless it comes from a different engine @@ -297,6 +305,16 @@ bool MediaPlayer::seeking() const return m_private->seeking(); } +bool MediaPlayer::supportsFullscreen() const +{ + return m_private->supportsFullscreen(); +} + +bool MediaPlayer::supportsSave() const +{ + return m_private->supportsSave(); +} + IntSize MediaPlayer::naturalSize() { return m_private->naturalSize(); @@ -347,6 +365,17 @@ void MediaPlayer::setRate(float rate) m_private->setRate(rate); } +bool MediaPlayer::preservesPitch() const +{ + return m_preservesPitch; +} + +void MediaPlayer::setPreservesPitch(bool preservesPitch) +{ + m_preservesPitch = preservesPitch; + m_private->setPreservesPitch(preservesPitch); +} + int MediaPlayer::dataRate() const { return m_private->dataRate(); @@ -417,6 +446,11 @@ void MediaPlayer::paint(GraphicsContext* p, const IntRect& r) m_private->paint(p, r); } +void MediaPlayer::paintCurrentFrameInContext(GraphicsContext* p, const IntRect& r) +{ + m_private->paintCurrentFrameInContext(p, r); +} + MediaPlayer::SupportsType MediaPlayer::supportsType(ContentType contentType) { String type = contentType.type(); @@ -458,6 +492,29 @@ void MediaPlayer::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) } #endif +#if USE(ACCELERATED_COMPOSITING) +void MediaPlayer::acceleratedRenderingStateChanged() +{ + m_private->acceleratedRenderingStateChanged(); +} + +bool MediaPlayer::supportsAcceleratedRendering() const +{ + return m_private->supportsAcceleratedRendering(); +} +#endif // USE(ACCELERATED_COMPOSITING) + +bool MediaPlayer::hasSingleSecurityOrigin() const +{ + return m_private->hasSingleSecurityOrigin(); +} + +MediaPlayer::MovieLoadType MediaPlayer::movieLoadType() const +{ + return m_private->movieLoadType(); +} + +// Client callbacks. void MediaPlayer::networkStateChanged() { if (m_mediaPlayerClient) @@ -507,4 +564,5 @@ void MediaPlayer::rateChanged() } } + #endif diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 9b2f685..7f5f2a0 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -49,6 +49,10 @@ class MediaPlayer; class MediaPlayerPrivateInterface; class String; +#if USE(ACCELERATED_COMPOSITING) +class GraphicsLayer; +#endif + class MediaPlayerClient { public: virtual ~MediaPlayerClient() { } @@ -65,25 +69,34 @@ public: // time has jumped, eg. not as a result of normal playback virtual void mediaPlayerTimeChanged(MediaPlayer*) { } - // a new frame of video is available - virtual void mediaPlayerRepaint(MediaPlayer*) { } - // the media file duration has changed, or is now known virtual void mediaPlayerDurationChanged(MediaPlayer*) { } // the playback rate has changed virtual void mediaPlayerRateChanged(MediaPlayer*) { } - // the movie size has changed - virtual void mediaPlayerSizeChanged(MediaPlayer*) { } - // The MediaPlayer has found potentially problematic media content. // This is used internally to trigger swapping from a <video> // element to an <embed> in standalone documents virtual void mediaPlayerSawUnsupportedTracks(MediaPlayer*) { } + +// Presentation-related methods + // a new frame of video is available + virtual void mediaPlayerRepaint(MediaPlayer*) { } + + // the movie size has changed + virtual void mediaPlayerSizeChanged(MediaPlayer*) { } + +#if USE(ACCELERATED_COMPOSITING) + // whether the rendering system can accelerate the display of this MediaPlayer. + virtual bool mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) { return false; } + + // return the GraphicsLayer that will host the presentation for this MediaPlayer. + virtual GraphicsLayer* mediaPlayerGraphicsLayer(MediaPlayer*) { return 0; } +#endif }; -class MediaPlayer : Noncopyable { +class MediaPlayer : public Noncopyable { public: MediaPlayer(MediaPlayerClient*); virtual ~MediaPlayer(); @@ -94,6 +107,8 @@ public: static void getSupportedTypes(HashSet<String>&); static bool isAvailable(); + bool supportsFullscreen() const; + bool supportsSave() const; IntSize naturalSize(); bool hasVideo(); @@ -126,6 +141,9 @@ public: float rate() const; void setRate(float); + + bool preservesPitch() const; + void setPreservesPitch(bool); float maxTimeBuffered(); float maxTimeSeekable(); @@ -143,6 +161,7 @@ public: void setAutobuffer(bool); void paint(GraphicsContext*, const IntRect&); + void paintCurrentFrameInContext(GraphicsContext*, const IntRect&); enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError }; NetworkState networkState(); @@ -150,6 +169,9 @@ public: enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData }; ReadyState readyState(); + enum MovieLoadType { Unknown, Download, StoredStream, LiveStream }; + MovieLoadType movieLoadType() const; + void networkStateChanged(); void readyStateChanged(); void volumeChanged(); @@ -168,6 +190,15 @@ public: void setMediaPlayerProxy(WebMediaPlayerProxy* proxy); #endif +#if USE(ACCELERATED_COMPOSITING) + // whether accelerated rendering is supported by the media engine for the current media. + bool supportsAcceleratedRendering() const; + // called when the rendering system flips the into or out of accelerated rendering mode. + void acceleratedRenderingStateChanged(); +#endif + + bool hasSingleSecurityOrigin() const; + private: static void initializeMediaEngines(); @@ -179,6 +210,7 @@ private: bool m_visible; float m_rate; float m_volume; + bool m_preservesPitch; bool m_autobuffer; #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) WebMediaPlayerProxy* m_playerProxy; // not owned or used, passed to m_private diff --git a/WebCore/platform/graphics/MediaPlayerPrivate.h b/WebCore/platform/graphics/MediaPlayerPrivate.h index e17259c..6d1359b 100644 --- a/WebCore/platform/graphics/MediaPlayerPrivate.h +++ b/WebCore/platform/graphics/MediaPlayerPrivate.h @@ -46,6 +46,9 @@ public: virtual void play() = 0; virtual void pause() = 0; + virtual bool supportsFullscreen() const { return false; } + virtual bool supportsSave() const { return false; } + virtual IntSize naturalSize() const = 0; virtual bool hasVideo() const = 0; @@ -63,6 +66,8 @@ public: virtual void setEndTime(float) = 0; virtual void setRate(float) = 0; + virtual void setPreservesPitch(bool) { } + virtual bool paused() const = 0; virtual void setVolume(float) = 0; @@ -81,7 +86,9 @@ public: virtual void setSize(const IntSize&) = 0; - virtual void paint(GraphicsContext*, const IntRect&) = 0 ; + virtual void paint(GraphicsContext*, const IntRect&) = 0; + + virtual void paintCurrentFrameInContext(GraphicsContext* c, const IntRect& r) { paint(c, r); } virtual void setAutobuffer(bool) { }; @@ -90,6 +97,18 @@ public: virtual void deliverNotification(MediaPlayerProxyNotificationType) = 0; virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) = 0; #endif + +#if USE(ACCELERATED_COMPOSITING) + // whether accelerated rendering is supported by the media engine for the current media. + virtual bool supportsAcceleratedRendering() const { return false; } + // called when the rendering system flips the into or out of accelerated rendering mode. + virtual void acceleratedRenderingStateChanged() { } +#endif + + virtual bool hasSingleSecurityOrigin() const { return false; } + + virtual MediaPlayer::MovieLoadType movieLoadType() const { return MediaPlayer::Unknown; } + }; } diff --git a/WebCore/platform/graphics/Path.h b/WebCore/platform/graphics/Path.h index 2b0a7d1..da324bc 100644 --- a/WebCore/platform/graphics/Path.h +++ b/WebCore/platform/graphics/Path.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved. * 2006 Rob Buis <buis@kde.org> + * Copyright (C) 2007-2008 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -51,6 +52,10 @@ typedef WebCore::CairoPath PlatformPath; #elif PLATFORM(SKIA) class SkPath; typedef SkPath PlatformPath; +#elif PLATFORM(WINCE) +namespace WebCore { + class PlatformPath; +} #else typedef void PlatformPath; #endif @@ -106,6 +111,9 @@ namespace WebCore { void clear(); bool isEmpty() const; + // Gets the current point of the current path, which is conceptually the final point reached by the path so far. + // Note the Path can be empty (isEmpty() == true) and still have a current point. + bool hasCurrentPoint() const; void moveTo(const FloatPoint&); void addLineTo(const FloatPoint&); diff --git a/WebCore/platform/graphics/Pattern.h b/WebCore/platform/graphics/Pattern.h index 6981748..02ad3ec 100644 --- a/WebCore/platform/graphics/Pattern.h +++ b/WebCore/platform/graphics/Pattern.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * Copyright (C) 2007-2008 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -52,6 +53,8 @@ typedef wxGraphicsBrush* PlatformPatternPtr; class wxBrush; typedef wxBrush* PlatformPatternPtr; #endif // USE(WXGC) +#elif PLATFORM(WINCE) +typedef void* PlatformPatternPtr; #endif namespace WebCore { diff --git a/WebCore/platform/graphics/SegmentedFontData.cpp b/WebCore/platform/graphics/SegmentedFontData.cpp index 1731d16..7e10040 100644 --- a/WebCore/platform/graphics/SegmentedFontData.cpp +++ b/WebCore/platform/graphics/SegmentedFontData.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "SegmentedFontData.h" +#include "PlatformString.h" #include "SimpleFontData.h" #include <wtf/Assertions.h> @@ -87,4 +88,11 @@ bool SegmentedFontData::isSegmented() const return true; } +#ifndef NDEBUG +String SegmentedFontData::description() const +{ + return "[segmented font]"; +} +#endif + } // namespace WebCore diff --git a/WebCore/platform/graphics/SegmentedFontData.h b/WebCore/platform/graphics/SegmentedFontData.h index 0a78321..645dc0d 100644 --- a/WebCore/platform/graphics/SegmentedFontData.h +++ b/WebCore/platform/graphics/SegmentedFontData.h @@ -59,6 +59,10 @@ public: unsigned numRanges() const { return m_ranges.size(); } const FontDataRange& rangeAt(unsigned i) const { return m_ranges[i]; } +#ifndef NDEBUG + virtual String description() const; +#endif + private: virtual const SimpleFontData* fontDataForCharacter(UChar32) const; virtual bool containsCharacters(const UChar*, int length) const; diff --git a/WebCore/platform/graphics/SimpleFontData.cpp b/WebCore/platform/graphics/SimpleFontData.cpp index bab7d99..c879228 100644 --- a/WebCore/platform/graphics/SimpleFontData.cpp +++ b/WebCore/platform/graphics/SimpleFontData.cpp @@ -190,4 +190,16 @@ bool SimpleFontData::isSegmented() const return false; } +#ifndef NDEBUG +String SimpleFontData::description() const +{ + if (isSVGFont()) + return "[SVG font]"; + if (isCustomFont()) + return "[custom font]"; + + return platformData().description(); +} +#endif + } // namespace WebCore diff --git a/WebCore/platform/graphics/SimpleFontData.h b/WebCore/platform/graphics/SimpleFontData.h index aab6429..cb472b0 100644 --- a/WebCore/platform/graphics/SimpleFontData.h +++ b/WebCore/platform/graphics/SimpleFontData.h @@ -105,6 +105,10 @@ public: const GlyphData& missingGlyphData() const { return m_missingGlyphData; } +#ifndef NDEBUG + virtual String description() const; +#endif + #if PLATFORM(MAC) NSFont* getNSFont() const { return m_platformData.font(); } #endif diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index 23f30f3..5765546 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -62,15 +62,6 @@ namespace WebCore { -static const unsigned aquaFocusRingColor = 0xFF7DADD9; - -Color focusRingColor() -{ - static Color focusRingColor = aquaFocusRingColor; - - return focusRingColor; -} - static inline void setColor(cairo_t* cr, const Color& col) { float red, green, blue, alpha; diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index 531ebf4..54e1217 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -65,6 +65,7 @@ public: // On Windows, we need to update the HDC for form controls to draw in the right place. void save(); void restore(); + void flush(); void clip(const FloatRect&); void clip(const Path&); void scale(const FloatSize&); @@ -78,6 +79,7 @@ public: // On everything else, we do nothing. void save() {} void restore() {} + void flush() {} void clip(const FloatRect&) {} void clip(const Path&) {} void scale(const FloatSize&) {} diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index d2652d6..c905ee8 100644 --- a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -39,9 +39,29 @@ #include <cairo.h> #include <wtf/Vector.h> +#include <math.h> using namespace std; +// Cairo doesn't provide a way to copy a cairo_surface_t. +// See http://lists.cairographics.org/archives/cairo/2007-June/010877.html +// Once cairo provides the way, use the function instead of this. +static inline cairo_surface_t* copySurface(cairo_surface_t* surface) +{ + cairo_format_t format = cairo_image_surface_get_format(surface); + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + cairo_surface_t* newsurface = cairo_image_surface_create(format, width, height); + + cairo_t* cr = cairo_create(newsurface); + cairo_set_source_surface(cr, surface, 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_destroy(cr); + + return newsurface; +} + namespace WebCore { ImageBufferData::ImageBufferData(const IntSize& size) @@ -49,7 +69,7 @@ ImageBufferData::ImageBufferData(const IntSize& size) { } -ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success) : m_data(size) , m_size(size) { @@ -82,12 +102,42 @@ Image* ImageBuffer::image() const // It's assumed that if image() is called, the actual rendering to the // GraphicsContext must be done. ASSERT(context()); + + // This creates a COPY of the image and will cache that copy. This means + // that if subsequent operations take place on the context, neither the + // currently-returned image, nor the results of future image() calls, + // will contain that operation. + // + // This seems silly, but is the way the CG port works: image() is + // intended to be used only when rendering is "complete." + cairo_surface_t* newsurface = copySurface(m_data.m_surface); + // BitmapImage will release the passed in surface on destruction - m_image = BitmapImage::create(cairo_surface_reference(m_data.m_surface)); + m_image = BitmapImage::create(newsurface); } return m_image.get(); } +void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) +{ + ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); + + unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface); + int stride = cairo_image_surface_get_stride(m_data.m_surface); + for (int y = 0; y < m_size.height(); ++y) { + unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * y); + for (int x = 0; x < m_size.width(); x++) { + unsigned* pixel = row + x; + Color pixelColor = colorFromPremultipliedARGB(*pixel); + pixelColor = Color(lookUpTable[pixelColor.red()], + lookUpTable[pixelColor.green()], + lookUpTable[pixelColor.blue()], + lookUpTable[pixelColor.alpha()]); + *pixel = premultipliedARGBFromColor(pixelColor); + } + } +} + PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const { ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); diff --git a/WebCore/platform/graphics/cairo/ImageCairo.cpp b/WebCore/platform/graphics/cairo/ImageCairo.cpp index 7c34e6f..c8c992e 100644 --- a/WebCore/platform/graphics/cairo/ImageCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageCairo.cpp @@ -157,11 +157,15 @@ void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, con if (!image) // If it's too early we won't have an image yet. return; + // Avoid NaN + if (!isfinite(phase.x()) || !isfinite(phase.y())) + return; + cairo_t* cr = context->platformContext(); context->save(); IntRect imageSize = enclosingIntRect(tileRect); - OwnPtr<ImageBuffer> imageSurface = ImageBuffer::create(imageSize.size(), false); + OwnPtr<ImageBuffer> imageSurface = ImageBuffer::create(imageSize.size()); if (!imageSurface) return; diff --git a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp index b51caf6..df62618 100644 --- a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp @@ -34,13 +34,10 @@ #include "ICOImageDecoder.h" #include "JPEGImageDecoder.h" #include "PNGImageDecoder.h" +#include "XBMImageDecoder.h" #include "SharedBuffer.h" #include <cairo.h> -#if !PLATFORM(WIN) -#include "XBMImageDecoder.h" -#endif - namespace WebCore { ImageDecoder* createDecoder(const Vector<char>& data) @@ -80,11 +77,9 @@ ImageDecoder* createDecoder(const Vector<char>& data) !memcmp(contents, "\000\000\002\000", 4)) return new ICOImageDecoder(); -#if !PLATFORM(WIN) // XBMs require 8 bytes of info. if (length >= 8 && strncmp(contents, "#define ", 8) == 0) return new XBMImageDecoder(); -#endif // Give up. We don't know what the heck this is. return 0; @@ -158,9 +153,12 @@ IntSize ImageSource::size() const return m_decoder->size(); } -IntSize ImageSource::frameSizeAtIndex(size_t) const +IntSize ImageSource::frameSizeAtIndex(size_t index) const { - return size(); + if (!m_decoder) + return IntSize(); + + return m_decoder->frameSizeAtIndex(index); } int ImageSource::repetitionCount() @@ -193,11 +191,7 @@ NativeImagePtr ImageSource::createFrameAtIndex(size_t index) if (!size().height()) return 0; - return cairo_image_surface_create_for_data((unsigned char*)buffer->bytes().data(), - CAIRO_FORMAT_ARGB32, - size().width(), - size().height(), - size().width()*4); + return buffer->asNewNativeImage(); } bool ImageSource::frameIsCompleteAtIndex(size_t index) diff --git a/WebCore/platform/graphics/cairo/PathCairo.cpp b/WebCore/platform/graphics/cairo/PathCairo.cpp index 13ca1e2..75681bd 100644 --- a/WebCore/platform/graphics/cairo/PathCairo.cpp +++ b/WebCore/platform/graphics/cairo/PathCairo.cpp @@ -89,6 +89,11 @@ bool Path::isEmpty() const #endif } +bool Path::hasCurrentPoint() const +{ + return !isEmpty(); +} + void Path::translate(const FloatSize& p) { cairo_t* cr = platformPath()->m_cr; diff --git a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h index beee660..f63a8dd 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h +++ b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h @@ -50,6 +50,7 @@ public: // These methods do nothing on Mac. void save() {} void restore() {} + void flush() {} void clip(const FloatRect&) {} void clip(const Path&) {} void scale(const FloatSize&) {} @@ -64,6 +65,7 @@ public: // On Windows, we need to update the HDC for form controls to draw in the right place. void save(); void restore(); + void flush(); void clip(const FloatRect&); void clip(const Path&); void scale(const FloatSize&); diff --git a/WebCore/platform/graphics/cg/ImageBufferCG.cpp b/WebCore/platform/graphics/cg/ImageBufferCG.cpp index 96e5604..6db7e88 100644 --- a/WebCore/platform/graphics/cg/ImageBufferCG.cpp +++ b/WebCore/platform/graphics/cg/ImageBufferCG.cpp @@ -38,6 +38,7 @@ #include <wtf/Assertions.h> #include <wtf/OwnArrayPtr.h> #include <wtf/RetainPtr.h> +#include <math.h> using namespace std; @@ -48,7 +49,7 @@ ImageBufferData::ImageBufferData(const IntSize&) { } -ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success) : m_data(size) , m_size(size) { @@ -57,7 +58,7 @@ ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) if (size.width() < 0 || size.height() < 0) return; bytesPerRow = size.width(); - if (!grayScale) { + if (imageColorSpace != GrayScale) { // Protect against overflow if (bytesPerRow > 0x3FFFFFFF) return; @@ -67,9 +68,26 @@ ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) m_data.m_data = tryFastCalloc(size.height(), bytesPerRow); ASSERT((reinterpret_cast<size_t>(m_data.m_data) & 2) == 0); - CGColorSpaceRef colorSpace = grayScale ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB(); + CGColorSpaceRef colorSpace; + switch(imageColorSpace) { + case DeviceRGB: + colorSpace = CGColorSpaceCreateDeviceRGB(); + break; + case GrayScale: + colorSpace = CGColorSpaceCreateDeviceGray(); + break; +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) + case LinearRGB: + colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear); + break; +#endif + default: + colorSpace = CGColorSpaceCreateDeviceRGB(); + break; + } + CGContextRef cgContext = CGBitmapContextCreate(m_data.m_data, size.width(), size.height(), 8, bytesPerRow, - colorSpace, grayScale ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast); + colorSpace, (imageColorSpace == GrayScale) ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpace); if (!cgContext) return; diff --git a/WebCore/platform/graphics/cg/ImageCG.cpp b/WebCore/platform/graphics/cg/ImageCG.cpp index dbf1d85..a5620e8 100644 --- a/WebCore/platform/graphics/cg/ImageCG.cpp +++ b/WebCore/platform/graphics/cg/ImageCG.cpp @@ -166,32 +166,44 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const F // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed // into the destination rect. See <rdar://problem/6112909>. shouldUseSubimage = (interpolationQuality == kCGInterpolationHigh || interpolationQuality == kCGInterpolationDefault) && srcRect.size() != destRect.size(); + float xScale = srcRect.width() / destRect.width(); + float yScale = srcRect.height() / destRect.height(); if (shouldUseSubimage) { - image = CGImageCreateWithImageInRect(image, srcRect); + FloatRect subimageRect = srcRect; + float leftPadding = srcRect.x() - floorf(srcRect.x()); + float topPadding = srcRect.y() - floorf(srcRect.y()); + + subimageRect.move(-leftPadding, -topPadding); + adjustedDestRect.move(-leftPadding / xScale, -topPadding / yScale); + + subimageRect.setWidth(ceilf(subimageRect.width() + leftPadding)); + adjustedDestRect.setWidth(subimageRect.width() / xScale); + + subimageRect.setHeight(ceilf(subimageRect.height() + topPadding)); + adjustedDestRect.setHeight(subimageRect.height() / yScale); + + image = CGImageCreateWithImageInRect(image, subimageRect); if (currHeight < srcRect.bottom()) { ASSERT(CGImageGetHeight(image) == currHeight - CGRectIntegral(srcRect).origin.y); - adjustedDestRect.setHeight(destRect.height() / srcRect.height() * CGImageGetHeight(image)); + adjustedDestRect.setHeight(CGImageGetHeight(image) / yScale); } } else { - float xScale = srcRect.width() / destRect.width(); - float yScale = srcRect.height() / destRect.height(); - adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale)); adjustedDestRect.setSize(FloatSize(selfSize.width() / xScale, selfSize.height() / yScale)); - - CGContextClipToRect(context, destRect); } + + CGContextClipToRect(context, destRect); } // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. if (!shouldUseSubimage && currHeight < selfSize.height()) adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height()); - // Flip the coords. ctxt->setCompositeOperation(compositeOp); - CGContextTranslateCTM(context, adjustedDestRect.x(), adjustedDestRect.bottom()); + + // Flip the coords. CGContextScaleCTM(context, 1, -1); - adjustedDestRect.setLocation(FloatPoint()); + adjustedDestRect.setY(-adjustedDestRect.bottom()); // Draw the image. CGContextDrawImage(context, adjustedDestRect, image); @@ -205,7 +217,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const F imageObserver()->didDraw(this); } -void Image::drawPatternCallback(void* info, CGContextRef context) +static void drawPatternCallback(void* info, CGContextRef context) { CGImageRef image = (CGImageRef)info; CGContextDrawImage(context, GraphicsContext(context).roundToDevicePixels(FloatRect(0, 0, CGImageGetWidth(image), CGImageGetHeight(image))), image); diff --git a/WebCore/platform/graphics/cg/ImageSourceCG.cpp b/WebCore/platform/graphics/cg/ImageSourceCG.cpp index 7cb8799..b716060 100644 --- a/WebCore/platform/graphics/cg/ImageSourceCG.cpp +++ b/WebCore/platform/graphics/cg/ImageSourceCG.cpp @@ -38,7 +38,14 @@ namespace WebCore { static const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32"); -static const CFStringRef kCGImageSourceDoNotCacheImageBlocks = CFSTR("kCGImageSourceDoNotCacheImageBlocks"); + +#if !PLATFORM(MAC) +static void sharedBufferDerefCallback(void*, void* info) +{ + SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info); + sharedBuffer->deref(); +} +#endif ImageSource::ImageSource() : m_decoder(0) @@ -79,11 +86,12 @@ void ImageSource::clear(bool destroyAllFrames, size_t, SharedBuffer* data, bool static CFDictionaryRef imageSourceOptions() { static CFDictionaryRef options; - + if (!options) { - const void* keys[3] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32, kCGImageSourceDoNotCacheImageBlocks }; - const void* values[3] = { kCFBooleanTrue, kCFBooleanTrue, kCFBooleanTrue }; - options = CFDictionaryCreate(NULL, keys, values, 3, + const unsigned numOptions = 2; + const void* keys[numOptions] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 }; + const void* values[numOptions] = { kCFBooleanTrue, kCFBooleanTrue }; + options = CFDictionaryCreate(NULL, keys, values, numOptions, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } return options; @@ -104,8 +112,12 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) CFDataRef cfData = data->createCFData(); #else // If no NSData is available, then we know SharedBuffer will always just be a vector. That means no secret changes can occur to it behind the - // scenes. We use CFDataCreateWithBytesNoCopy in that case. - CFDataRef cfData = CFDataCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data->data()), data->size(), kCFAllocatorNull); + // scenes. We use CFDataCreateWithBytesNoCopy in that case. Ensure that the SharedBuffer lives as long as the CFDataRef. + data->ref(); + CFAllocatorContext context = {0, data, 0, 0, 0, 0, 0, &sharedBufferDerefCallback, 0}; + CFAllocatorRef derefAllocator = CFAllocatorCreate(kCFAllocatorDefault, &context); + CFDataRef cfData = CFDataCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data->data()), data->size(), derefAllocator); + CFRelease(derefAllocator); #endif CGImageSourceUpdateData(m_decoder, cfData, allDataReceived); CFRelease(cfData); diff --git a/WebCore/platform/graphics/cg/PDFDocumentImage.cpp b/WebCore/platform/graphics/cg/PDFDocumentImage.cpp index 2578f08..858b18e 100644 --- a/WebCore/platform/graphics/cg/PDFDocumentImage.cpp +++ b/WebCore/platform/graphics/cg/PDFDocumentImage.cpp @@ -68,11 +68,11 @@ bool PDFDocumentImage::dataChanged(bool allDataReceived) #if PLATFORM(MAC) // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge. We use SharedBuffer's ability // to wrap itself inside CFData to get around this, ensuring that ImageIO is really looking at the SharedBuffer. - CFDataRef data = m_data->createCFData(); + CFDataRef data = this->data()->createCFData(); #else // If no NSData is available, then we know SharedBuffer will always just be a vector. That means no secret changes can occur to it behind the // scenes. We use CFDataCreateWithBytesNoCopy in that case. - CFDataRef data = CFDataCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(m_data->data()), m_data->size(), kCFAllocatorNull); + CFDataRef data = CFDataCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(this->data()->data()), this->data()->size(), kCFAllocatorNull); #endif CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data); CFRelease(data); diff --git a/WebCore/platform/graphics/cg/PathCG.cpp b/WebCore/platform/graphics/cg/PathCG.cpp index ebd0359..5812cea 100644 --- a/WebCore/platform/graphics/cg/PathCG.cpp +++ b/WebCore/platform/graphics/cg/PathCG.cpp @@ -245,7 +245,12 @@ void Path::clear() bool Path::isEmpty() const { return CGPathIsEmpty(m_path); - } +} + +bool Path::hasCurrentPoint() const +{ + return !isEmpty(); +} static void CGPathToCFStringApplierFunction(void* info, const CGPathElement *element) { diff --git a/WebCore/platform/graphics/cg/PatternCG.cpp b/WebCore/platform/graphics/cg/PatternCG.cpp index 697bc57..63628f4 100644 --- a/WebCore/platform/graphics/cg/PatternCG.cpp +++ b/WebCore/platform/graphics/cg/PatternCG.cpp @@ -62,10 +62,10 @@ CGPatternRef Pattern::createPlatformPattern(const TransformationMatrix& userSpac // If FLT_MAX should also be used for xStep or yStep, nothing is rendered. Using fractions of FLT_MAX also // result in nothing being rendered. // INT_MAX is almost correct, but there seems to be some number wrapping occuring making the fill - // pattern is not filled correctly. - // So, just pick a really large number that works. - float xStep = m_repeatX ? tileRect.width() : (100000000.0f); - float yStep = m_repeatY ? tileRect.height() : (100000000.0f); + // pattern is not filled correctly. + // To make error of floating point less than 0.5, we use the half of the number of mantissa of float (1 << 22). + CGFloat xStep = m_repeatX ? tileRect.width() : (1 << 22); + CGFloat yStep = m_repeatY ? tileRect.height() : (1 << 22); // The pattern will release the tile when it's done rendering in patternReleaseCallback tileImage()->ref(); diff --git a/WebCore/platform/graphics/chromium/ColorChromiumMac.mm b/WebCore/platform/graphics/chromium/ColorChromiumMac.mm deleted file mode 100644 index 01dff7e..0000000 --- a/WebCore/platform/graphics/chromium/ColorChromiumMac.mm +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2009 Google 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 "Color.h" - -#import <AppKit/NSColor.h> -#import <wtf/Assertions.h> -#import <wtf/StdLibExtras.h> -#import <wtf/RetainPtr.h> - -namespace WebCore { - -Color focusRingColor() -{ - // To avoid the Mac Chromium build having to rebasline 500+ layout tests and - // continue to do this w/ new tests that get landed in WebKit, we want to - // run the layout tests w/ the same color that stock WebKit uses. - // - // FIXME: For now we've hard coded the color that WebKit uses for layout - // tests. We need to revisit this and do either of the following: - // A. Fully honor the color from the UI, which means collecting the color - // (and change notifications) in the browser process, and messaging the - // color to the render process. - // B. Adding a "layout tests" flag, to control the orage vs. blue colors - // depending if we're running layout tests. - // To see the WebKit implementation of using the UI color and/or a flag for - // layout tests see WebKit/WebCore/platform/graphics/mac/ColorMac.mm. - // (Reality is we just need an api to override the focus color and both - // of the above are covered for what this file needs to provide, the - // two options would be details that happen in other places.) - - // From WebKit: - // static RGBA32 oldAquaFocusRingColorRGBA = 0xFF7DADD9; - static Color oldAquaFocusRingColor(0x7D, 0xAD, 0xD9, 0xFF); - return oldAquaFocusRingColor; -} - -// createCGColor() and the functions it calls are verbatum copies of -// graphics/mac/ColorMac.mm. These are copied here so that we don't need to -// include ColorMac.mm in the Chromium build. -// FIXME: Check feasibility of using pure CG calls and unifying this copy with -// ColorMac.mm's under graphics/cg. - -NSColor* nsColor(const Color& color) -{ - unsigned c = color.rgb(); - switch (c) { - case 0: { - // Need this to avoid returning nil because cachedRGBAValues will default to 0. - DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, clearColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:0.0f])); - return clearColor.get(); - } - case Color::black: { - DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, blackColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:1.0f])); - return blackColor.get(); - } - case Color::white: { - DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, whiteColor, ([NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f])); - return whiteColor.get(); - } - default: { - const int cacheSize = 32; - static unsigned cachedRGBAValues[cacheSize]; - static RetainPtr<NSColor>* cachedColors = new RetainPtr<NSColor>[cacheSize]; - - for (int i = 0; i != cacheSize; ++i) - if (cachedRGBAValues[i] == c) - return cachedColors[i].get(); - - NSColor* result = [NSColor colorWithDeviceRed:color.red() / 255.0f - green:color.green() / 255.0f - blue:color.blue() / 255.0f - alpha:color.alpha() /255.0f]; - - static int cursor; - cachedRGBAValues[cursor] = c; - cachedColors[cursor] = result; - if (++cursor == cacheSize) - cursor = 0; - return result; - } - } -} - -static CGColorRef CGColorFromNSColor(NSColor* color) -{ - // This needs to always use device colorspace so it can de-calibrate the color for - // CGColor to possibly recalibrate it. - NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - CGFloat red = [deviceColor redComponent]; - CGFloat green = [deviceColor greenComponent]; - CGFloat blue = [deviceColor blueComponent]; - CGFloat alpha = [deviceColor alphaComponent]; - const CGFloat components[4] = { red, green, blue, alpha }; - static CGColorSpaceRef deviceRGBColorSpace = CGColorSpaceCreateDeviceRGB(); - CGColorRef cgColor = CGColorCreate(deviceRGBColorSpace, components); - return cgColor; -} - -CGColorRef createCGColor(const Color& c) -{ - // We could directly create a CGColor here, but that would - // skip any RGB caching the nsColor method does. A direct - // creation could be investigated for a possible performance win. - return CGColorFromNSColor(nsColor(c)); -} - -} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp index bf1cd2e..9252ae0 100644 --- a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp @@ -288,7 +288,7 @@ static bool fontContainsCharacter(const FontPlatformData* fontData, if (count == 0 && ChromiumBridge::ensureFontLoaded(hfont)) count = GetFontUnicodeRanges(hdc, 0); if (count == 0) { - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the font unicode range after second attempt"); SelectObject(hdc, oldFont); ReleaseDC(0, hdc); return true; diff --git a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp index 797825e..3fe1561 100644 --- a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp @@ -31,9 +31,8 @@ #include "config.h" #include "FontCache.h" -#include <fontconfig/fontconfig.h> - #include "AtomicString.h" +#include "ChromiumBridge.h" #include "CString.h" #include "Font.h" #include "FontDescription.h" @@ -46,6 +45,7 @@ #include "SkTypeface.h" #include "SkUtils.h" +#include <unicode/utf16.h> #include <wtf/Assertions.h> namespace WebCore { @@ -58,38 +58,12 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) { - FcCharSet* cset = FcCharSetCreate(); - for (int i = 0; i < length; ++i) - FcCharSetAddChar(cset, characters[i]); - - FcPattern* pattern = FcPatternCreate(); - - FcValue fcvalue; - fcvalue.type = FcTypeCharSet; - fcvalue.u.c = cset; - FcPatternAdd(pattern, FC_CHARSET, fcvalue, 0); - - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - FcResult result; - FcPattern* match = FcFontMatch(0, pattern, &result); - FcPatternDestroy(pattern); - - SimpleFontData* ret = 0; - - if (match) { - FcChar8* family; - if (FcPatternGetString(match, FC_FAMILY, 0, &family) == FcResultMatch) { - AtomicString fontFamily(reinterpret_cast<char*>(family)); - ret = getCachedFontData(getCachedFontPlatformData(font.fontDescription(), fontFamily, false)); - } - FcPatternDestroy(match); - } - - FcCharSetDestroy(cset); + String family = ChromiumBridge::getFontFamilyForCharacters(characters, length); + if (family.isEmpty()) + return 0; - return ret; + AtomicString atomicFamily(family); + return getCachedFontData(getCachedFontPlatformData(font.fontDescription(), atomicFamily, false)); } FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) @@ -165,13 +139,7 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD if (fontDescription.italic()) style |= SkTypeface::kItalic; - // FIXME: This #ifdef can go away once we're firmly using the new Skia. - // During the transition, this makes the code compatible with both versions. -#ifdef SK_USE_OLD_255_TO_256 SkTypeface* tf = SkTypeface::CreateFromName(name, static_cast<SkTypeface::Style>(style)); -#else - SkTypeface* tf = SkTypeface::Create(name, static_cast<SkTypeface::Style>(style)); -#endif if (!tf) return 0; diff --git a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp index 4710245..3d67992 100644 --- a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -146,17 +146,23 @@ void TransparencyAwareFontPainter::initializeForGDI() // know everything is opaque so don't need to do anything special. layerMode = TransparencyWin::NoLayer; } - m_transparency.init(m_graphicsContext, layerMode, TransparencyWin::KeepTransform, layerRect); + + // Bug 26088 - init() might fail if layerRect is invalid. Given this, we + // need to be careful to check for null pointers everywhere after this call + m_transparency.init(m_graphicsContext, layerMode, + TransparencyWin::KeepTransform, layerRect); // Set up the DC, using the one from the transparency helper. - m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint(); - SetTextColor(m_hdc, skia::SkColorToCOLORREF(color)); - SetBkMode(m_hdc, TRANSPARENT); + if (m_transparency.platformContext()) { + m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint(); + SetTextColor(m_hdc, skia::SkColorToCOLORREF(color)); + SetBkMode(m_hdc, TRANSPARENT); + } } TransparencyAwareFontPainter::~TransparencyAwareFontPainter() { - if (!m_useGDI) + if (!m_useGDI || !m_graphicsContext || !m_platformContext) return; // Nothing to do. m_transparency.composite(); if (m_createdTransparencyLayer) @@ -208,12 +214,13 @@ TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter( { init(); - m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); + if (m_hdc) + m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); } TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter() { - if (m_useGDI) + if (m_useGDI && m_hdc) ::SelectObject(m_hdc, m_oldFont); } @@ -245,6 +252,9 @@ bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, numGlyphs, glyphs, advances, 0, &origin); } + if (!m_graphicsContext || !m_hdc) + return true; + // Windows' origin is the top-left of the bounding box, so we have // to subtract off the font ascent to get it. int x = lroundf(m_point.x() + startAdvance); @@ -265,7 +275,7 @@ bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, COLORREF savedTextColor = GetTextColor(m_hdc); SetTextColor(m_hdc, textColor); ExtTextOut(m_hdc, x + shadowSize.width(), y + shadowSize.height(), ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); - SetTextColor(m_hdc, savedTextColor); + SetTextColor(m_hdc, savedTextColor); } return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); @@ -379,6 +389,17 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, for (int i = 0; i < curLen; ++i, ++glyphIndex) { glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); advances[i] = static_cast<int>(glyphBuffer.advanceAt(from + glyphIndex)); + + // Bug 26088 - very large positive or negative runs can fail to + // render so we clamp the size here. In the specs, negative + // letter-spacing is implementation-defined, so this should be + // fine, and it matches Safari's implementation. The call actually + // seems to crash if kMaxNegativeRun is set to somewhere around + // -32830, so we give ourselves a little breathing room. + const int maxNegativeRun = -32768; + const int maxPositiveRun = 32768; + if ((curWidth + advances[i] < maxNegativeRun) || (curWidth + advances[i] > maxPositiveRun)) + advances[i] = 0; curWidth += advances[i]; } @@ -394,7 +415,9 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, break; } - ASSERT(success); + if (!success) + LOG_ERROR("Unable to draw the glyphs after second attempt"); + curAdvance += curWidth; } } @@ -436,6 +459,8 @@ void Font::drawComplexText(GraphicsContext* graphicsContext, TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point); HDC hdc = painter.hdc(); + if (!hdc) + return; // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. // Enforce non-transparent color. diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp index e99c12a..88035d5 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp @@ -36,6 +36,8 @@ #include "Base64.h" #include "ChromiumBridge.h" #include "OpenTypeUtilities.h" +#elif PLATFORM(LINUX) +#include "SkStream.h" #endif #include "FontPlatformData.h" @@ -46,6 +48,8 @@ #include <objbase.h> #include <t2embapi.h> #pragma comment(lib, "t2embed") +#elif PLATFORM(LINUX) +#include <cstring> #endif namespace WebCore { @@ -60,6 +64,9 @@ FontCustomPlatformData::~FontCustomPlatformData() } else RemoveFontMemResourceEx(m_fontReference); } +#elif PLATFORM(LINUX) + if (m_fontReference) + m_fontReference->unref(); #endif } @@ -102,6 +109,9 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b HFONT hfont = CreateFontIndirect(&logFont); return FontPlatformData(hfont, size); +#elif PLATFORM(LINUX) + ASSERT(m_fontReference); + return FontPlatformData(m_fontReference, size, bold && !m_fontReference->isBold(), italic && !m_fontReference->isItalic()); #else notImplemented(); return FontPlatformData(); @@ -186,6 +196,51 @@ static String createUniqueFontName() } #endif +#if PLATFORM(LINUX) +class RemoteFontStream : public SkStream { +public: + explicit RemoteFontStream(PassRefPtr<SharedBuffer> buffer) + : m_buffer(buffer) + , m_offset(0) + { + } + + virtual ~RemoteFontStream() + { + } + + virtual bool rewind() + { + m_offset = 0; + return true; + } + + virtual size_t read(void* buffer, size_t size) + { + if (!buffer && !size) { + // This is request for the length of the stream. + return m_buffer->size(); + } + if (!buffer) { + // This is a request to skip bytes. This operation is not supported. + return 0; + } + // This is a request to read bytes. + if (!m_buffer->data() || !m_buffer->size()) + return 0; + size_t left = m_buffer->size() - m_offset; + size_t toRead = (left > size) ? size : left; + std::memcpy(buffer, m_buffer->data() + m_offset, toRead); + m_offset += toRead; + return toRead; + } + +private: + RefPtr<SharedBuffer> m_buffer; + size_t m_offset; +}; +#endif + FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) { ASSERT_ARG(buffer, buffer); @@ -223,8 +278,14 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) } return new FontCustomPlatformData(fontReference, fontName); +#elif PLATFORM(LINUX) + RemoteFontStream stream(buffer); + SkTypeface* typeface = SkTypeface::CreateFromStream(&stream); + if (!typeface) + return 0; + return new FontCustomPlatformData(typeface); #else - notImplemented();; + notImplemented(); return 0; #endif } diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h b/WebCore/platform/graphics/chromium/FontCustomPlatformData.h index 2f1a597..a42f1ec 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.h @@ -32,12 +32,14 @@ #ifndef FontCustomPlatformData_h #define FontCustomPlatformData_h +#include "FontRenderingMode.h" #include <wtf/Noncopyable.h> #if PLATFORM(WIN_OS) -#include "FontRenderingMode.h" #include "PlatformString.h" #include <windows.h> +#elif PLATFORM(LINUX) +#include "SkTypeface.h" #endif namespace WebCore { @@ -51,6 +53,10 @@ struct FontCustomPlatformData : Noncopyable { : m_fontReference(fontReference) , m_name(name) {} +#elif PLATFORM(LINUX) + explicit FontCustomPlatformData(SkTypeface* typeface) + : m_fontReference(typeface) + {} #endif ~FontCustomPlatformData(); @@ -61,6 +67,8 @@ struct FontCustomPlatformData : Noncopyable { #if PLATFORM(WIN_OS) HANDLE m_fontReference; String m_name; +#elif PLATFORM(LINUX) + SkTypeface* m_fontReference; #endif }; diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp index a952685..d4e45fb 100644 --- a/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -34,6 +34,7 @@ #include "FloatRect.h" #include "GlyphBuffer.h" #include "GraphicsContext.h" +#include "HarfbuzzSkia.h" #include "NotImplemented.h" #include "PlatformContextSkia.h" #include "SimpleFontData.h" @@ -54,7 +55,7 @@ bool Font::canReturnFallbackFontsForComplexText() void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { - SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert + SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); SkScalar x = SkFloatToScalar(point.x()); @@ -96,7 +97,6 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, SkPaint paint; gc->platformContext()->setupPaintForStroking(&paint, 0, 0); font->platformData().setupPaint(&paint); - paint.setFlags(SkPaint::kAntiAlias_Flag); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setColor(gc->strokeColor().rgb()); @@ -110,31 +110,503 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, } } -void Font::drawComplexText(GraphicsContext* context, const TextRun& run, +// Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't +// handle subpixel positioning so this function is used to truncate Harfbuzz +// values to a number of pixels. +static int truncateFixedPointToInteger(HB_Fixed value) +{ + return value >> 6; +} + +// TextRunWalker walks a TextRun and presents each script run in sequence. A +// TextRun is a sequence of code-points with the same embedding level (i.e. they +// are all left-to-right or right-to-left). A script run is a subsequence where +// all the characters have the same script (e.g. Arabic, Thai etc). Shaping is +// only ever done with script runs since the shapers only know how to deal with +// a single script. +// +// After creating it, the script runs are either iterated backwards or forwards. +// It defaults to backwards for RTL and forwards otherwise (which matches the +// presentation order), however you can set it with |setBackwardsIteration|. +// +// Once you have setup the object, call |nextScriptRun| to get the first script +// run. This will return false when the iteration is complete. At any time you +// can call |reset| to start over again. +class TextRunWalker { +public: + TextRunWalker(const TextRun& run, unsigned startingX, const Font* font) + : m_font(font) + , m_run(run) + , m_startingX(startingX) + , m_offsetX(m_startingX) + , m_iterateBackwards(run.rtl()) + { + memset(&m_item, 0, sizeof(m_item)); + // We cannot know, ahead of time, how many glyphs a given script run + // will produce. We take a guess that script runs will not produce more + // than twice as many glyphs as there are code points and fallback if + // we find that we are wrong. + m_maxGlyphs = run.length() * 2; + createGlyphArrays(); + + m_item.log_clusters = new unsigned short[run.length()]; + + m_item.face = 0; + m_item.font = allocHarfbuzzFont(); + + m_item.string = run.characters(); + m_item.stringLength = run.length(); + m_item.item.bidiLevel = run.rtl(); + + reset(); + } + + ~TextRunWalker() + { + fastFree(m_item.font); + deleteGlyphArrays(); + delete[] m_item.log_clusters; + } + + void reset() + { + if (m_iterateBackwards) + m_indexOfNextScriptRun = m_run.length() - 1; + else + m_indexOfNextScriptRun = 0; + m_offsetX = m_startingX; + } + + // Set the x offset for the next script run. This affects the values in + // |xPositions| + void setXOffsetToZero() + { + m_offsetX = 0; + } + + bool rtl() const + { + return m_run.rtl(); + } + + void setBackwardsIteration(bool isBackwards) + { + m_iterateBackwards = isBackwards; + reset(); + } + + // Advance to the next script run, returning false when the end of the + // TextRun has been reached. + bool nextScriptRun() + { + if (m_iterateBackwards) { + // In right-to-left mode we need to render the shaped glyph backwards and + // also render the script runs themselves backwards. So given a TextRun: + // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai) + // we render: + // TTTTTTCAAAAAAA + // (and the glyphs in each A, C and T section are backwards too) + if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) + return false; + } else { + if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) + return false; + } + + setupFontForScriptRun(); + + if (!shapeGlyphs()) + return false; + setGlyphXPositions(rtl()); + return true; + } + + const uint16_t* glyphs() const + { + return m_glyphs16; + } + + // Return the length of the array returned by |glyphs| + const unsigned length() const + { + return m_item.num_glyphs; + } + + // Return the x offset for each of the glyphs. Note that this is translated + // by the current x offset and that the x offset is updated for each script + // run. + const SkScalar* xPositions() const + { + return m_xPositions; + } + + // Get the advances (widths) for each glyph. + const HB_Fixed* advances() const + { + return m_item.advances; + } + + // Return the width (in px) of the current script run. + const unsigned width() const + { + return m_pixelWidth; + } + + // Return the cluster log for the current script run. For example: + // script run: f i a n c é (fi gets ligatured) + // log clutrs: 0 0 1 2 3 4 + // So, for each input code point, the log tells you which output glyph was + // generated for it. + const unsigned short* logClusters() const + { + return m_item.log_clusters; + } + + // return the number of code points in the current script run + const unsigned numCodePoints() const + { + return m_numCodePoints; + } + + const FontPlatformData* fontPlatformDataForScriptRun() + { + return reinterpret_cast<FontPlatformData*>(m_item.font->userData); + } + + float widthOfFullRun() + { + float widthSum = 0; + while (nextScriptRun()) + widthSum += width(); + + return widthSum; + } + +private: + void setupFontForScriptRun() + { + const FontData* fontData = m_font->fontDataAt(0); + if (!fontData->containsCharacters(m_item.string + m_item.item.pos, m_item.item.length)) + fontData = m_font->fontDataForCharacters(m_item.string + m_item.item.pos, m_item.item.length); + const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); + m_item.face = platformData.harfbuzzFace(); + void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); + m_item.font->userData = opaquePlatformData; + } + + HB_FontRec* allocHarfbuzzFont() + { + HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec))); + memset(font, 0, sizeof(HB_FontRec)); + font->klass = &harfbuzzSkiaClass; + font->userData = 0; + // The values which harfbuzzSkiaClass returns are already scaled to + // pixel units, so we just set all these to one to disable further + // scaling. + font->x_ppem = 1; + font->y_ppem = 1; + font->x_scale = 1; + font->y_scale = 1; + + return font; + } + + void deleteGlyphArrays() + { + delete[] m_item.glyphs; + delete[] m_item.attributes; + delete[] m_item.advances; + delete[] m_item.offsets; + delete[] m_glyphs16; + delete[] m_xPositions; + } + + bool createGlyphArrays() + { + m_item.glyphs = new HB_Glyph[m_maxGlyphs]; + m_item.attributes = new HB_GlyphAttributes[m_maxGlyphs]; + m_item.advances = new HB_Fixed[m_maxGlyphs]; + m_item.offsets = new HB_FixedPoint[m_maxGlyphs]; + m_glyphs16 = new uint16_t[m_maxGlyphs]; + m_xPositions = new SkScalar[m_maxGlyphs]; + + return m_item.glyphs + && m_item.attributes + && m_item.advances + && m_item.offsets + && m_glyphs16 + && m_xPositions; + } + + bool expandGlyphArrays() + { + deleteGlyphArrays(); + m_maxGlyphs <<= 1; + return createGlyphArrays(); + } + + bool shapeGlyphs() + { + for (;;) { + m_item.num_glyphs = m_maxGlyphs; + HB_ShapeItem(&m_item); + if (m_item.num_glyphs < m_maxGlyphs) + break; + + // We overflowed our arrays. Resize and retry. + if (!expandGlyphArrays()) + return false; + } + + return true; + } + + void setGlyphXPositions(bool isRTL) + { + m_pixelWidth = 0; + for (unsigned i = 0; i < m_item.num_glyphs; ++i) { + int index; + if (isRTL) + index = m_item.num_glyphs - (i + 1); + else + index = i; + + m_glyphs16[i] = m_item.glyphs[i]; + m_xPositions[index] = m_offsetX + m_pixelWidth; + m_pixelWidth += truncateFixedPointToInteger(m_item.advances[index]); + } + m_offsetX += m_pixelWidth; + } + + const Font* const m_font; + const TextRun& m_run; + HB_ShaperItem m_item; + uint16_t* m_glyphs16; // A vector of 16-bit glyph ids. + SkScalar* m_xPositions; // A vector of x positions for each glyph. + ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|. + const unsigned m_startingX; // Offset in pixels of the first script run. + unsigned m_offsetX; // Offset in pixels to the start of the next script run. + unsigned m_pixelWidth; // Width (in px) of the current script run. + unsigned m_numCodePoints; // Code points in current script run. + unsigned m_maxGlyphs; // Current size of all the Harfbuzz arrays. + bool m_iterateBackwards; +}; + +static void setupForTextPainting(SkPaint* paint, SkColor color) +{ + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint->setColor(color); +} + +void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, const FloatPoint& point, int from, int to) const { - notImplemented(); + if (!run.length()) + return; + + SkCanvas* canvas = gc->platformContext()->canvas(); + int textMode = gc->platformContext()->getTextDrawingMode(); + bool fill = textMode & cTextFill; + bool stroke = (textMode & cTextStroke) + && gc->platformContext()->getStrokeStyle() != NoStroke + && gc->platformContext()->getStrokeThickness() > 0; + + if (!fill && !stroke) + return; + + SkPaint strokePaint, fillPaint; + if (fill) { + gc->platformContext()->setupPaintForFilling(&fillPaint); + setupForTextPainting(&fillPaint, gc->fillColor().rgb()); + } + if (stroke) { + gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0); + setupForTextPainting(&strokePaint, gc->strokeColor().rgb()); + } + + TextRunWalker walker(run, point.x(), this); + + while (walker.nextScriptRun()) { + if (fill) { + walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); + canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint); + } + + if (stroke) { + walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); + canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint); + } + } } float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */) const { - notImplemented(); - return 0; + TextRunWalker walker(run, 0, this); + return walker.widthOfFullRun(); } +static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x) +{ + const HB_Fixed* advances = walker.advances(); + int glyphIndex; + if (walker.rtl()) { + for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) { + if (x < truncateFixedPointToInteger(advances[glyphIndex])) + break; + x -= truncateFixedPointToInteger(advances[glyphIndex]); + } + } else { + for (glyphIndex = 0; glyphIndex < walker.length(); ++glyphIndex) { + if (x < truncateFixedPointToInteger(advances[glyphIndex])) + break; + x -= truncateFixedPointToInteger(advances[glyphIndex]); + } + } + + return glyphIndex; +} + +// Return the code point index for the given |x| offset into the text run. int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const { - notImplemented(); - return 0; + // (Mac code ignores includePartialGlyphs, and they don't know what it's + // supposed to do, so we just ignore it as well.) + TextRunWalker walker(run, 0, this); + + // If this is RTL text, the first glyph from the left is actually the last + // code point. So we need to know how many code points there are total in + // order to subtract. This is different from the length of the TextRun + // because UTF-16 surrogate pairs are a single code point, but 32-bits long. + // In LTR we leave this as 0 so that we get the correct value for + // |basePosition|, below. + unsigned totalCodePoints = 0; + if (walker.rtl()) { + ssize_t offset = 0; + while (offset < run.length()) { + utf16_to_code_point(run.characters(), run.length(), &offset); + totalCodePoints++; + } + } + + unsigned basePosition = totalCodePoints; + + // For RTL: + // code-point order: abcd efg hijkl + // on screen: lkjih gfe dcba + // ^ ^ + // | | + // basePosition--| | + // totalCodePoints----| + // Since basePosition is currently the total number of code-points, the + // first thing we do is decrement it so that it's pointing to the start of + // the current script-run. + // + // For LTR, basePosition is zero so it already points to the start of the + // first script run. + while (walker.nextScriptRun()) { + if (walker.rtl()) + basePosition -= walker.numCodePoints(); + + if (x < walker.width()) { + // The x value in question is within this script run. We consider + // each glyph in presentation order and stop when we find the one + // covering this position. + const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x); + + // Now that we have a glyph index, we have to turn that into a + // code-point index. Because of ligatures, several code-points may + // have gone into a single glyph. We iterate over the clusters log + // and find the first code-point which contributed to the glyph. + + // Some shapers (i.e. Khmer) will produce cluster logs which report + // that /no/ code points contributed to certain glyphs. Because of + // this, we take any code point which contributed to the glyph in + // question, or any subsequent glyph. If we run off the end, then + // we take the last code point. + const unsigned short* log = walker.logClusters(); + for (unsigned j = 0; j < walker.numCodePoints(); ++j) { + if (log[j] >= glyphIndex) + return basePosition + j; + } + + return basePosition + walker.numCodePoints() - 1; + } + + x -= walker.width(); + + if (!walker.rtl()) + basePosition += walker.numCodePoints(); + } + + return basePosition; } +// Return the rectangle for selecting the given range of code-points in the TextRun. FloatRect Font::selectionRectForComplexText(const TextRun& run, - const IntPoint& point, int h, + const IntPoint& point, int height, int from, int to) const { - notImplemented(); - return FloatRect(); + int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1; + TextRunWalker walker(run, 0, this); + + // Base will point to the x offset for the current script run. Note that, in + // the LTR case, width will be 0. + int base = walker.rtl() ? walker.widthOfFullRun() : 0; + const int leftEdge = base; + + // We want to enumerate the script runs in code point order in the following + // code. This call also resets |walker|. + walker.setBackwardsIteration(false); + + while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) { + // TextRunWalker will helpfully accululate the x offsets for different + // script runs for us. For this code, however, we always want the x offsets + // to start from zero so we call this before each script run. + walker.setXOffsetToZero(); + + if (walker.rtl()) + base -= walker.width(); + + if (fromX == -1 && from < walker.numCodePoints()) { + // |from| is within this script run. So we index the clusters log to + // find which glyph this code-point contributed to and find its x + // position. + int glyph = walker.logClusters()[from]; + fromX = base + walker.xPositions()[glyph]; + fromAdvance = walker.advances()[glyph]; + } else + from -= walker.numCodePoints(); + + if (toX == -1 && to < walker.numCodePoints()) { + int glyph = walker.logClusters()[to]; + toX = base + walker.xPositions()[glyph]; + toAdvance = walker.advances()[glyph]; + } else + to -= walker.numCodePoints(); + + if (!walker.rtl()) + base += walker.width(); + } + + // The position in question might be just after the text. + const int rightEdge = base; + if (fromX == -1 && !from) + fromX = leftEdge; + else if (walker.rtl()) + fromX += truncateFixedPointToInteger(fromAdvance); + + if (toX == -1 && !to) + toX = rightEdge; + else if (!walker.rtl()) + toX += truncateFixedPointToInteger(toAdvance); + + ASSERT(fromX != -1 && toX != -1); + + if (fromX < toX) + return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); + + return FloatRect(point.x() + toX, point.y(), fromX - toX, height); } -} // namespace WebCore +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp index 767fe76..d6c83ec 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp @@ -141,7 +141,7 @@ SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const hr = ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties); if (S_OK != hr) { - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the font properties after second attempt"); } } } @@ -153,4 +153,11 @@ SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const return m_scriptFontProperties; } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + } diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h index ce15a93..25c9cf8 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h +++ b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h @@ -45,6 +45,7 @@ typedef struct HFONT__ *HFONT; namespace WebCore { class FontDescription; +class String; class FontPlatformData { public: @@ -78,6 +79,10 @@ public: return m_font == other.m_font && m_size == other.m_size; } +#ifndef NDEBUG + String description() const; +#endif + SCRIPT_FONTPROPERTIES* scriptFontProperties() const; SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; } diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp index e6a61f6..bf4697f 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp @@ -31,19 +31,45 @@ #include "config.h" #include "FontPlatformData.h" -#include "StringImpl.h" +#include "HarfbuzzSkia.h" #include "NotImplemented.h" +#include "PlatformString.h" +#include "StringImpl.h" #include "SkPaint.h" #include "SkTypeface.h" namespace WebCore { +static SkPaint::Hinting skiaHinting = SkPaint::kNormal_Hinting; +static bool isSkiaAntiAlias = true, isSkiaSubpixelGlyphs; + +void FontPlatformData::setHinting(SkPaint::Hinting hinting) +{ + skiaHinting = hinting; +} + +void FontPlatformData::setAntiAlias(bool isAntiAlias) +{ + isSkiaAntiAlias = isAntiAlias; +} + +void FontPlatformData::setSubpixelGlyphs(bool isSubpixelGlyphs) +{ + isSkiaSubpixelGlyphs = isSubpixelGlyphs; +} + +FontPlatformData::RefCountedHarfbuzzFace::~RefCountedHarfbuzzFace() +{ + HB_FreeFace(m_harfbuzzFace); +} + FontPlatformData::FontPlatformData(const FontPlatformData& src) : m_typeface(src.m_typeface) , m_textSize(src.m_textSize) , m_fakeBold(src.m_fakeBold) , m_fakeItalic(src.m_fakeItalic) + , m_harfbuzzFace(src.m_harfbuzzFace) { m_typeface->safeRef(); } @@ -62,6 +88,7 @@ FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) , m_textSize(textSize) , m_fakeBold(src.m_fakeBold) , m_fakeItalic(src.m_fakeItalic) + , m_harfbuzzFace(src.m_harfbuzzFace) { m_typeface->safeRef(); } @@ -78,21 +105,29 @@ FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) m_textSize = src.m_textSize; m_fakeBold = src.m_fakeBold; m_fakeItalic = src.m_fakeItalic; + m_harfbuzzFace = src.m_harfbuzzFace; return *this; } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + void FontPlatformData::setupPaint(SkPaint* paint) const { const float ts = m_textSize > 0 ? m_textSize : 12; - paint->setAntiAlias(true); - paint->setSubpixelText(false); + paint->setAntiAlias(isSkiaAntiAlias); + paint->setHinting(skiaHinting); + paint->setLCDRenderText(isSkiaSubpixelGlyphs); paint->setTextSize(SkFloatToScalar(ts)); paint->setTypeface(m_typeface); paint->setFakeBoldText(m_fakeBold); paint->setTextSkewX(m_fakeItalic ? -SK_Scalar1 / 4 : 0); - paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); } SkFontID FontPlatformData::uniqueID() const @@ -141,4 +176,12 @@ bool FontPlatformData::isFixedPitch() const return false; } +HB_FaceRec_* FontPlatformData::harfbuzzFace() const +{ + if (!m_harfbuzzFace) + m_harfbuzzFace = RefCountedHarfbuzzFace::create(HB_NewFace(const_cast<FontPlatformData*>(this), harfbuzzSkiaGetTable)); + + return m_harfbuzzFace->face(); +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h index c63a860..29ce8e7 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h +++ b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h @@ -33,14 +33,17 @@ #include "StringImpl.h" #include <wtf/RefPtr.h> +#include <SkPaint.h> -class SkPaint; class SkTypeface; typedef uint32_t SkFontID; +struct HB_FaceRec_; + namespace WebCore { class FontDescription; +class String; // ----------------------------------------------------------------------------- // FontPlatformData is the handle which WebKit has on a specific face. A face @@ -104,12 +107,45 @@ public: FontPlatformData& operator=(const FontPlatformData&); bool isHashTableDeletedValue() const { return m_typeface == hashTableDeletedFontValue(); } +#ifndef NDEBUG + String description() const; +#endif + + HB_FaceRec_* harfbuzzFace() const; + + // ------------------------------------------------------------------------- + // Global font preferences... + + static void setHinting(SkPaint::Hinting); + static void setAntiAlias(bool on); + static void setSubpixelGlyphs(bool on); + private: + class RefCountedHarfbuzzFace : public RefCounted<RefCountedHarfbuzzFace> { + public: + static PassRefPtr<RefCountedHarfbuzzFace> create(HB_FaceRec_* harfbuzzFace) + { + return adoptRef(new RefCountedHarfbuzzFace(harfbuzzFace)); + } + + ~RefCountedHarfbuzzFace(); + + HB_FaceRec_* face() const { return m_harfbuzzFace; } + + private: + RefCountedHarfbuzzFace(HB_FaceRec_* harfbuzzFace) : m_harfbuzzFace(harfbuzzFace) + { + } + + HB_FaceRec_* m_harfbuzzFace; + }; + // FIXME: Could SkAutoUnref be used here? SkTypeface* m_typeface; float m_textSize; bool m_fakeBold; bool m_fakeItalic; + mutable RefPtr<RefCountedHarfbuzzFace> m_harfbuzzFace; SkTypeface* hashTableDeletedFontValue() const { return reinterpret_cast<SkTypeface*>(-1); } }; diff --git a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp index 2cb1cc5..e2c47c1 100644 --- a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp @@ -87,10 +87,9 @@ static bool fillBMPGlyphs(unsigned offset, return false; } } else { - // FIXME: This should never happen. We want to crash the - // process and receive a crash dump. We should revisit this code later. + // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401 - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the text metrics after second attempt"); fillEmptyGlyphs(page); return false; } diff --git a/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp new file mode 100644 index 0000000..621d674 --- /dev/null +++ b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "FontPlatformData.h" +#include "wtf/OwnArrayPtr.h" + +#include "SkFontHost.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPoint.h" +#include "SkRect.h" + +extern "C" { +#include "harfbuzz-shaper.h" +#include "harfbuzz-unicode.h" +} + +// This file implements the callbacks which Harfbuzz requires by using Skia +// calls. See the Harfbuzz source for references about what these callbacks do. + +namespace WebCore { + +static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value) +{ + // HB_Fixed is a 26.6 fixed point format. + return value * 64; +} + +static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), reinterpret_cast<uint16_t*>(glyphs)); + + // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our + // |glyphs| array needs to be converted. + // Additionally, if the CSS white-space property is inhibiting line + // breaking, we might find end-of-line charactors rendered via the complex + // text path. Fonts don't provide glyphs for these code points so, if we + // find one, we simulate the space glyph being interposed in this case. + // Because the input is variable-length per code point, we walk the input + // in step with the output. + // FIXME: it seems that this logic is duplicated in CoreTextController and UniscribeController + ssize_t indexOfNextCodePoint = 0; + uint16_t spaceGlyphNumber = 0; + for (int i = numGlyphs - 1; i >= 0; --i) { + const uint32_t currentCodePoint = utf16_to_code_point(characters, length, &indexOfNextCodePoint); + + uint16_t value; + // We use a memcpy to avoid breaking strict aliasing rules. + memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(uint16_t)); + + if (!value && Font::treatAsSpace(currentCodePoint)) { + static const uint16_t spaceUTF16 = ' '; + if (!spaceGlyphNumber) + paint.textToGlyphs(&spaceUTF16, sizeof(spaceUTF16), &spaceGlyphNumber); + value = spaceGlyphNumber; + } + + glyphs[i] = value; + } + + *glyphsSize = numGlyphs; + return 1; +} + +static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs, HB_Fixed* advances, int flags) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + OwnArrayPtr<uint16_t> glyphs16(new uint16_t[numGlyphs]); + if (!glyphs16.get()) + return; + for (unsigned i = 0; i < numGlyphs; ++i) + glyphs16[i] = glyphs[i]; + paint.getTextWidths(glyphs16.get(), numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances)); + + // The |advances| values which Skia outputs are SkScalars, which are floats + // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format. + // These two formats are both 32-bits long. + for (unsigned i = 0; i < numGlyphs; ++i) { + float value; + // We use a memcpy to avoid breaking strict aliasing rules. + memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(float)); + advances[i] = SkiaScalarToHarfbuzzFixed(value); + } +} + +static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + OwnArrayPtr<uint16_t> glyphs16(new uint16_t[length]); + if (!glyphs16.get()) + return 0; + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16.get()); + + bool canRender = true; + for (int i = 0; i < numGlyphs; ++i) { + if (!glyphs16[i]) { + canRender = false; + break; + } + } + + return canRender; +} + +static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + if (flags & HB_ShaperFlag_UseDesignMetrics) + return HB_Err_Invalid_Argument; // This is requesting pre-hinted positions. We can't support this. + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + uint16_t glyph16 = glyph; + SkPath path; + paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path); + int numPoints = path.getPoints(NULL, 0); + if (point >= numPoints) + return HB_Err_Invalid_SubTable; + SkPoint* points = reinterpret_cast<SkPoint*>(fastMalloc(sizeof(SkPoint) * (point + 1))); + if (!points) + return HB_Err_Invalid_SubTable; + // Skia does let us get a single point from the path. + path.getPoints(points, point + 1); + *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX); + *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY); + *resultingNumPoints = numPoints; + fastFree(points); + + return HB_Err_Ok; +} + +static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + uint16_t glyph16 = glyph; + SkScalar width; + SkRect bounds; + paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); + + metrics->x = SkiaScalarToHarfbuzzFixed(width); + // We can't actually get the |y| correct because Skia doesn't export + // the vertical advance. However, nor we do ever render vertical text at + // the moment so it's unimportant. + metrics->y = 0; + metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width()); + metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height()); + metrics->xOffset = SkiaScalarToHarfbuzzFixed(bounds.fLeft); + metrics->yOffset = SkiaScalarToHarfbuzzFixed(bounds.fTop); +} + +static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + SkPaint::FontMetrics skiaMetrics; + paint.getFontMetrics(&skiaMetrics); + + switch (metric) { + case HB_FontAscent: + return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent); + // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them. + default: + return 0; + } +} + +HB_FontClass harfbuzzSkiaClass = { + stringToGlyphs, + glyphsToAdvances, + canRender, + getOutlinePoint, + getGlyphMetrics, + getFontMetric, +}; + +HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(voidface); + + const size_t tableSize = SkFontHost::GetTableSize(font->uniqueID(), tag); + if (!tableSize) + return HB_Err_Invalid_Argument; + // If Harfbuzz specified a NULL buffer then it's asking for the size of the table. + if (!buffer) { + *len = tableSize; + return HB_Err_Ok; + } + + if (*len < tableSize) + return HB_Err_Invalid_Argument; + SkFontHost::GetTableData(font->uniqueID(), tag, 0, tableSize, buffer); + return HB_Err_Ok; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.h b/WebCore/platform/graphics/chromium/HarfbuzzSkia.h index 9cb4a95..f7e0496 100644 --- a/WebCore/platform/graphics/skia/ImageSourceSkia.h +++ b/WebCore/platform/graphics/chromium/HarfbuzzSkia.h @@ -1,10 +1,10 @@ /* - * Copyright (c) 2008, Google Inc. All rights reserved. - * + * Copyright (c) 2009, Google 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: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -14,7 +14,7 @@ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -28,33 +28,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "ImageSource.h" +#ifndef HarfbuzzSkia_h +#define HarfbuzzSkia_h -namespace WebCore { +extern "C" { +#include "harfbuzz-shaper.h" +#include "harfbuzz-unicode.h" +} -class ImageSourceSkia : public ImageSource { -public: - // FIXME: This class is a hack to support Chromium's ICO decoder - // Currently our ICO decoder decodes all data during setData() instead of - // being lazy. In addition, it only decodes one frame (closest to the size - // passed to the decoder during createDecoder, called from setData) and - // discards all other data in the file. - // - // To fix this will require fixing the ICO decoder to be lazy, or to decode - // all frames. Apple's decoders (ImageIO) decode all frames, and return - // them all sorted in decreasing size. WebCore always draws the first frame. - // - // This is a special-purpose routine for the favicon decoder, which is used - // to specify a particular icon size for the ICOImageDecoder to prefer - // decoding. Note that not all favicons are ICOs, so this won't - // necessarily do anything differently than ImageSource::setData(). - // - // Passing an empty IntSize for |preferredIconSize| here is exactly - // equivalent to just calling ImageSource::setData(). See also comments in - // ICOImageDecoder.cpp. - void setData(SharedBuffer* data, - bool allDataReceived, - const IntSize& preferredIconSize); -}; +namespace WebCore { + HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len); + extern const HB_FontClass harfbuzzSkiaClass; +} // namespace WebCore -} +#endif diff --git a/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp b/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp index 6f5ce90..3d68ea8 100644 --- a/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp @@ -63,7 +63,7 @@ void SimpleFontData::platformInit() // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401. if (!GetTextMetrics(dc, &textMetric)) - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the text metrics after second attempt"); } } @@ -141,7 +141,7 @@ void SimpleFontData::determinePitch() // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401. if (!GetTextMetrics(dc, &textMetric)) - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the text metrics after second attempt"); } } @@ -163,7 +163,7 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401. if (!GetCharWidthI(dc, glyph, 1, 0, &width)) - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the char width after second attempt"); } } diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/WebCore/platform/graphics/chromium/TransparencyWin.cpp index d3ced81..7957d5a 100644 --- a/WebCore/platform/graphics/chromium/TransparencyWin.cpp +++ b/WebCore/platform/graphics/chromium/TransparencyWin.cpp @@ -109,7 +109,7 @@ class TransparencyWin::OwnedBuffers { public: OwnedBuffers(const IntSize& size, bool needReferenceBuffer) { - m_destBitmap = ImageBuffer::create(size, false); + m_destBitmap = ImageBuffer::create(size); if (needReferenceBuffer) { m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); @@ -152,6 +152,7 @@ TransparencyWin::TransparencyWin() , m_savedOnDrawContext(false) , m_layerBuffer(0) , m_referenceBitmap(0) + , m_validLayer(false) { } @@ -237,11 +238,14 @@ void TransparencyWin::setupLayer() void TransparencyWin::setupLayerForNoLayer() { m_drawContext = m_destContext; // Draw to the source context. + m_validLayer = true; } void TransparencyWin::setupLayerForOpaqueCompositeLayer() { initializeNewContext(); + if (!m_validLayer) + return; TransformationMatrix mapping; mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); @@ -268,6 +272,9 @@ void TransparencyWin::setupLayerForTextComposite() void TransparencyWin::setupLayerForWhiteLayer() { initializeNewContext(); + if (!m_validLayer) + return; + m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white); // Layer rect represents the part of the original layer. } @@ -289,6 +296,9 @@ void TransparencyWin::setupTransform(const IntRect& region) void TransparencyWin::setupTransformForKeepTransform(const IntRect& region) { + if (!m_validLayer) + return; + if (m_layerMode != NoLayer) { // Need to save things since we're modifying the transform. m_drawContext->save(); @@ -319,6 +329,9 @@ void TransparencyWin::setupTransformForUntransform() void TransparencyWin::setupTransformForScaleTransform() { + if (!m_validLayer) + return; + if (m_layerMode == NoLayer) { // Need to save things since we're modifying the layer. m_drawContext->save(); @@ -345,16 +358,22 @@ void TransparencyWin::setTextCompositeColor(Color color) void TransparencyWin::initializeNewContext() { int pixelSize = m_layerSize.width() * m_layerSize.height(); + if (pixelSize <= 0) + return; + if (pixelSize > maxCachedBufferPixelSize) { // Create a 1-off buffer for drawing into. We only need the reference // buffer if we're making an OpaqueCompositeLayer. bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer; m_ownedBuffers.set(new OwnedBuffers(m_layerSize, needReferenceBitmap)); - m_layerBuffer = m_ownedBuffers->destBitmap(); + if (!m_layerBuffer) + return; + m_drawContext = m_layerBuffer->context(); if (needReferenceBitmap) m_referenceBitmap = m_ownedBuffers->referenceBitmap(); + m_validLayer = true; return; } @@ -366,6 +385,7 @@ void TransparencyWin::initializeNewContext() bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0); m_referenceBitmap = m_cachedBuffers->referenceBitmap(); m_referenceBitmap->eraseARGB(0, 0, 0, 0); + m_validLayer = true; return; } @@ -377,10 +397,14 @@ void TransparencyWin::initializeNewContext() m_layerBuffer = m_cachedBuffers->destBitmap(); m_drawContext = m_cachedBuffers->destBitmap()->context(); m_referenceBitmap = m_cachedBuffers->referenceBitmap(); + m_validLayer = true; } void TransparencyWin::compositeOpaqueComposite() { + if (!m_validLayer) + return; + SkCanvas* destCanvas = canvasForContext(*m_destContext); destCanvas->save(); @@ -436,6 +460,9 @@ void TransparencyWin::compositeOpaqueComposite() void TransparencyWin::compositeTextComposite() { + if (!m_validLayer) + return; + const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopPlatformDevice().accessBitmap(true); SkColor textColor = m_textCompositeColor.rgb(); for (int y = 0; y < m_layerSize.height(); y++) { @@ -451,6 +478,7 @@ void TransparencyWin::compositeTextComposite() // Now the layer has text with the proper color and opacity. SkCanvas* destCanvas = canvasForContext(*m_destContext); + destCanvas->save(); // We want to use Untransformed space (see above) SkMatrix identity; @@ -467,6 +495,9 @@ void TransparencyWin::compositeTextComposite() void TransparencyWin::makeLayerOpaque() { + if (!m_validLayer) + return; + SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()-> canvas()->getTopPlatformDevice().accessBitmap(true)); for (int y = 0; y < m_layerSize.height(); y++) { @@ -477,4 +508,3 @@ void TransparencyWin::makeLayerOpaque() } } // namespace WebCore - diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.h b/WebCore/platform/graphics/chromium/TransparencyWin.h index e1963b3..ab75375 100644 --- a/WebCore/platform/graphics/chromium/TransparencyWin.h +++ b/WebCore/platform/graphics/chromium/TransparencyWin.h @@ -135,13 +135,16 @@ public: const IntRect& region); // Combines the source and destination bitmaps using the given mode. + // Calling this function before the destructor runs is mandatory in most + // cases, and harmless otherwise. The mandatory cases are: + // (m_layerMode != NoLayer) || (m_transformMode == ScaleTransform) void composite(); // Returns the context for drawing into, which may be the destination // context, or a temporary one. GraphicsContext* context() const { return m_drawContext; } - PlatformGraphicsContext* platformContext() const { return m_drawContext->platformContext(); } + PlatformGraphicsContext* platformContext() const { return m_drawContext ? m_drawContext->platformContext() : 0; } // When the mode is TextComposite, this sets the color that the text will // get. See the enum above for more. @@ -245,6 +248,12 @@ private: // m_layerBuffer, which will either point to this object, or the statically // cached one. Don't access directly. OwnPtr<OwnedBuffers> m_ownedBuffers; + + // Sometimes we're asked to create layers that have negative dimensions. + // This API is not designed to fail to initialize, so we hide the fact + // that they are illegal and can't be rendered (failing silently, drawing + // nothing). + bool m_validLayer; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/filters/FEBlend.h b/WebCore/platform/graphics/filters/FEBlend.h index dec04ac..b09cd72 100644 --- a/WebCore/platform/graphics/filters/FEBlend.h +++ b/WebCore/platform/graphics/filters/FEBlend.h @@ -47,7 +47,8 @@ namespace WebCore { BlendModeType blendMode() const; void setBlendMode(BlendModeType); - + + virtual FloatRect uniteChildEffectSubregions(Filter* filter) { return calculateUnionOfChildEffectSubregions(filter, m_in.get(), m_in2.get()); } void apply(Filter*); void dump(); diff --git a/WebCore/platform/graphics/filters/FEColorMatrix.cpp b/WebCore/platform/graphics/filters/FEColorMatrix.cpp index 8704e64..fb0a194 100644 --- a/WebCore/platform/graphics/filters/FEColorMatrix.cpp +++ b/WebCore/platform/graphics/filters/FEColorMatrix.cpp @@ -2,6 +2,7 @@ Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> 2004, 2005 Rob Buis <buis@kde.org> 2005 Eric Seidel <eric@webkit.org> + 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -24,7 +25,11 @@ #if ENABLE(FILTERS) #include "FEColorMatrix.h" +#include "CanvasPixelArray.h" #include "Filter.h" +#include "GraphicsContext.h" +#include "ImageData.h" +#include <math.h> namespace WebCore { @@ -61,8 +66,127 @@ void FEColorMatrix::setValues(const Vector<float> &values) m_values = values; } -void FEColorMatrix::apply(Filter*) +inline void matrix(double& red, double& green, double& blue, double& alpha, const Vector<float>& values) { + double r = values[0] * red + values[1] * green + values[2] * blue + values[3] * alpha; + double g = values[5] * red + values[6] * green + values[7] * blue + values[8] * alpha; + double b = values[10] * red + values[11] * green + values[12] * blue + values[13] * alpha; + double a = values[15] * red + values[16] * green + values[17] * blue + values[18] * alpha; + + red = r; + green = g; + blue = b; + alpha = a; +} + +inline void saturate(double& red, double& green, double& blue, const float& s) +{ + double r = (0.213 + 0.787 * s) * red + (0.715 - 0.715 * s) * green + (0.072 - 0.072 * s) * blue; + double g = (0.213 - 0.213 * s) * red + (0.715 + 0.285 * s) * green + (0.072 - 0.072 * s) * blue; + double b = (0.213 - 0.213 * s) * red + (0.715 - 0.715 * s) * green + (0.072 + 0.928 * s) * blue; + + red = r; + green = g; + blue = b; +} + +inline void huerotate(double& red, double& green, double& blue, const float& hue) +{ + double cosHue = cos(hue * M_PI / 180); + double sinHue = sin(hue * M_PI / 180); + double r = red * (0.213 + cosHue * 0.787 - sinHue * 0.213) + + green * (0.715 - cosHue * 0.715 - sinHue * 0.715) + + blue * (0.072 - cosHue * 0.072 + sinHue * 0.928); + double g = red * (0.213 - cosHue * 0.213 + sinHue * 0.143) + + green * (0.715 + cosHue * 0.285 + sinHue * 0.140) + + blue * (0.072 - cosHue * 0.072 - sinHue * 0.283); + double b = red * (0.213 - cosHue * 0.213 - sinHue * 0.787) + + green * (0.715 - cosHue * 0.715 + sinHue * 0.715) + + blue * (0.072 + cosHue * 0.928 + sinHue * 0.072); + + red = r; + green = g; + blue = b; +} + +inline void luminance(double& red, double& green, double& blue, double& alpha) +{ + alpha = 0.2125 * red + 0.7154 * green + 0.0721 * blue; + red = 0.; + green = 0.; + blue = 0.; +} + +template<ColorMatrixType filterType> +void effectType(const PassRefPtr<CanvasPixelArray>& srcPixelArray, PassRefPtr<ImageData>& imageData, const Vector<float>& values) +{ + for (unsigned pixelOffset = 0; pixelOffset < srcPixelArray->length(); pixelOffset++) { + unsigned pixelByteOffset = pixelOffset * 4; + + unsigned char r = 0, g = 0, b = 0, a = 0; + srcPixelArray->get(pixelByteOffset, r); + srcPixelArray->get(pixelByteOffset + 1, g); + srcPixelArray->get(pixelByteOffset + 2, b); + srcPixelArray->get(pixelByteOffset + 3, a); + + double red = r, green = g, blue = b, alpha = a; + + switch (filterType) { + case FECOLORMATRIX_TYPE_MATRIX: + matrix(red, green, blue, alpha, values); + break; + case FECOLORMATRIX_TYPE_SATURATE: + saturate(red, green, blue, values[0]); + break; + case FECOLORMATRIX_TYPE_HUEROTATE: + huerotate(red, green, blue, values[0]); + break; + case FECOLORMATRIX_TYPE_LUMINANCETOALPHA: + luminance(red, green, blue, alpha); + break; + } + + imageData->data()->set(pixelByteOffset, red); + imageData->data()->set(pixelByteOffset + 1, green); + imageData->data()->set(pixelByteOffset + 2, blue); + imageData->data()->set(pixelByteOffset + 3, alpha); + } +} + +void FEColorMatrix::apply(Filter* filter) +{ + m_in->apply(filter); + if (!m_in->resultImage()) + return; + + GraphicsContext* filterContext = getEffectContext(); + if (!filterContext) + return; + + filterContext->drawImage(m_in->resultImage()->image(), calculateDrawingRect(m_in->subRegion())); + + IntRect imageRect(IntPoint(), resultImage()->size()); + PassRefPtr<ImageData> imageData(resultImage()->getImageData(imageRect)); + PassRefPtr<CanvasPixelArray> srcPixelArray(imageData->data()); + + switch (m_type) { + case FECOLORMATRIX_TYPE_UNKNOWN: + break; + case FECOLORMATRIX_TYPE_MATRIX: + effectType<FECOLORMATRIX_TYPE_MATRIX>(srcPixelArray, imageData, m_values); + break; + case FECOLORMATRIX_TYPE_SATURATE: + effectType<FECOLORMATRIX_TYPE_SATURATE>(srcPixelArray, imageData, m_values); + break; + case FECOLORMATRIX_TYPE_HUEROTATE: + effectType<FECOLORMATRIX_TYPE_HUEROTATE>(srcPixelArray, imageData, m_values); + break; + case FECOLORMATRIX_TYPE_LUMINANCETOALPHA: + effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(srcPixelArray, imageData, m_values); + break; + } + + resultImage()->putImageData(imageData.get(), imageRect, IntPoint()); } void FEColorMatrix::dump() diff --git a/WebCore/platform/graphics/filters/FEColorMatrix.h b/WebCore/platform/graphics/filters/FEColorMatrix.h index eeb3557..106b2fa 100644 --- a/WebCore/platform/graphics/filters/FEColorMatrix.h +++ b/WebCore/platform/graphics/filters/FEColorMatrix.h @@ -47,7 +47,8 @@ namespace WebCore { const Vector<float>& values() const; void setValues(const Vector<float>&); - + + virtual FloatRect uniteChildEffectSubregions(Filter* filter) { return calculateUnionOfChildEffectSubregions(filter, m_in.get()); } void apply(Filter*); void dump(); diff --git a/WebCore/platform/graphics/filters/FEComponentTransfer.h b/WebCore/platform/graphics/filters/FEComponentTransfer.h index cc1d1f8..773aba7 100644 --- a/WebCore/platform/graphics/filters/FEComponentTransfer.h +++ b/WebCore/platform/graphics/filters/FEComponentTransfer.h @@ -78,7 +78,8 @@ namespace WebCore { ComponentTransferFunction alphaFunction() const; void setAlphaFunction(const ComponentTransferFunction&); - + + virtual FloatRect uniteChildEffectSubregions(Filter* filter) { return calculateUnionOfChildEffectSubregions(filter, m_in.get()); } void apply(Filter*); void dump(); diff --git a/WebCore/platform/graphics/filters/FEComposite.h b/WebCore/platform/graphics/filters/FEComposite.h index b623cce..ad096f2 100644 --- a/WebCore/platform/graphics/filters/FEComposite.h +++ b/WebCore/platform/graphics/filters/FEComposite.h @@ -59,7 +59,8 @@ namespace WebCore { float k4() const; void setK4(float); - + + virtual FloatRect uniteChildEffectSubregions(Filter* filter) { return calculateUnionOfChildEffectSubregions(filter, m_in.get(), m_in2.get()); } void apply(Filter*); void dump(); diff --git a/WebCore/platform/graphics/filters/Filter.h b/WebCore/platform/graphics/filters/Filter.h index 84909da..ee97afc 100644 --- a/WebCore/platform/graphics/filters/Filter.h +++ b/WebCore/platform/graphics/filters/Filter.h @@ -21,7 +21,8 @@ #define Filter_h #if ENABLE(FILTERS) -#include "Image.h" +#include "FloatRect.h" +#include "ImageBuffer.h" #include "StringHash.h" #include <wtf/PassRefPtr.h> @@ -36,13 +37,18 @@ namespace WebCore { public: virtual ~Filter() { } - void setSourceImage(PassRefPtr<Image> image) { m_image = image; } - Image* sourceImage() { return m_image.get(); } + void setSourceImage(PassOwnPtr<ImageBuffer> sourceImage) { m_sourceImage = sourceImage; } + ImageBuffer* sourceImage() { return m_sourceImage.get(); } + virtual FloatRect sourceImageRect() = 0; + virtual FloatRect filterRegion() = 0; + + // SVG specific virtual void calculateEffectSubRegion(FilterEffect*) = 0; + virtual bool effectBoundingBoxMode() = 0; private: - RefPtr<Image> m_image; + OwnPtr<ImageBuffer> m_sourceImage; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/filters/FilterEffect.cpp b/WebCore/platform/graphics/filters/FilterEffect.cpp index cd74992..41e8a39 100644 --- a/WebCore/platform/graphics/filters/FilterEffect.cpp +++ b/WebCore/platform/graphics/filters/FilterEffect.cpp @@ -29,6 +29,10 @@ FilterEffect::FilterEffect() , m_yBBoxMode(false) , m_widthBBoxMode(false) , m_heightBBoxMode(false) + , m_hasX(false) + , m_hasY(false) + , m_hasWidth(false) + , m_hasHeight(false) { } @@ -36,6 +40,39 @@ FilterEffect::~FilterEffect() { } +FloatRect FilterEffect::calculateUnionOfChildEffectSubregions(Filter* filter, FilterEffect* in) +{ + return in->calculateEffectRect(filter); +} + +FloatRect FilterEffect::calculateUnionOfChildEffectSubregions(Filter* filter, FilterEffect* in, FilterEffect* in2) +{ + FloatRect uniteEffectRect = in->calculateEffectRect(filter); + uniteEffectRect.unite(in2->calculateEffectRect(filter)); + return uniteEffectRect; +} + +FloatRect FilterEffect::calculateEffectRect(Filter* filter) +{ + setUnionOfChildEffectSubregions(uniteChildEffectSubregions(filter)); + filter->calculateEffectSubRegion(this); + return subRegion(); +} + +FloatRect FilterEffect::calculateDrawingRect(const FloatRect& srcRect) +{ + FloatPoint startPoint = FloatPoint(srcRect.x() - subRegion().x(), srcRect.y() - subRegion().y()); + FloatRect drawingRect = FloatRect(startPoint, srcRect.size()); + return drawingRect; +} + +GraphicsContext* FilterEffect::getEffectContext() +{ + IntRect bufferRect = enclosingIntRect(subRegion()); + m_effectBuffer = ImageBuffer::create(bufferRect.size(), LinearRGB); + return m_effectBuffer->context(); +} + TextStream& FilterEffect::externalRepresentation(TextStream& ts) const { return ts; diff --git a/WebCore/platform/graphics/filters/FilterEffect.h b/WebCore/platform/graphics/filters/FilterEffect.h index e2a058d..8dc6233 100644 --- a/WebCore/platform/graphics/filters/FilterEffect.h +++ b/WebCore/platform/graphics/filters/FilterEffect.h @@ -24,9 +24,11 @@ #if ENABLE(FILTERS) #include "Filter.h" #include "FloatRect.h" +#include "GraphicsContext.h" #include "ImageBuffer.h" #include "TextStream.h" +#include <wtf/PassOwnPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -48,16 +50,41 @@ namespace WebCore { bool heightBoundingBoxMode() const { return m_heightBBoxMode; } void setHeightBoundingBoxMode(bool bboxMode) { m_heightBBoxMode = bboxMode; } + void setUnionOfChildEffectSubregions(const FloatRect& uniteRect) { m_unionOfChildEffectSubregions = uniteRect; } + FloatRect unionOfChildEffectSubregions() const { return m_unionOfChildEffectSubregions; } + FloatRect subRegion() const { return m_subRegion; } void setSubRegion(const FloatRect& subRegion) { m_subRegion = subRegion; } + bool hasX() { return m_hasX; } + void setHasX(bool value) { m_hasX = value; } + + bool hasY() { return m_hasY; } + void setHasY(bool value) { m_hasY = value; } + + bool hasWidth() { return m_hasWidth; } + void setHasWidth(bool value) { m_hasWidth = value; } + + bool hasHeight() { return m_hasHeight; } + void setHasHeight(bool value) { m_hasHeight = value; } + // The result is bounded by the size of the filter primitive to save resources ImageBuffer* resultImage() { return m_effectBuffer.get(); } - void setEffectBuffer(ImageBuffer* effectBuffer) { m_effectBuffer.set(effectBuffer); } + void setEffectBuffer(PassOwnPtr<ImageBuffer> effectBuffer) { m_effectBuffer = effectBuffer; } + FloatRect calculateUnionOfChildEffectSubregions(Filter*, FilterEffect*, FilterEffect*); + FloatRect calculateUnionOfChildEffectSubregions(Filter*, FilterEffect*); + + GraphicsContext* getEffectContext(); + FloatRect calculateDrawingRect(const FloatRect&); + + virtual FloatRect uniteChildEffectSubregions(Filter* filter) { return filter->filterRegion(); } + virtual FloatRect calculateEffectRect(Filter*); virtual void apply(Filter*) = 0; virtual void dump() = 0; + virtual bool isSourceInput() { return false; } + virtual TextStream& externalRepresentation(TextStream&) const; protected: FilterEffect(); @@ -69,7 +96,13 @@ namespace WebCore { bool m_widthBBoxMode : 1; bool m_heightBBoxMode : 1; + bool m_hasX : 1; + bool m_hasY : 1; + bool m_hasWidth : 1; + bool m_hasHeight : 1; + FloatRect m_subRegion; + FloatRect m_unionOfChildEffectSubregions; mutable OwnPtr<ImageBuffer> m_effectBuffer; }; diff --git a/WebCore/platform/graphics/filters/SourceAlpha.h b/WebCore/platform/graphics/filters/SourceAlpha.h index 21497aa..5341562 100644 --- a/WebCore/platform/graphics/filters/SourceAlpha.h +++ b/WebCore/platform/graphics/filters/SourceAlpha.h @@ -34,6 +34,8 @@ namespace WebCore { static const AtomicString& effectName(); + virtual bool isSourceInput() { return true; } + virtual FloatRect calculateEffectRect(Filter* filter) { return filter->sourceImageRect(); } void apply(Filter*); void dump(); diff --git a/WebCore/platform/graphics/filters/SourceGraphic.cpp b/WebCore/platform/graphics/filters/SourceGraphic.cpp index 39d4810..023eeac 100644 --- a/WebCore/platform/graphics/filters/SourceGraphic.cpp +++ b/WebCore/platform/graphics/filters/SourceGraphic.cpp @@ -41,8 +41,24 @@ const AtomicString& SourceGraphic::effectName() return s_effectName; } -void SourceGraphic::apply(Filter*) +FloatRect SourceGraphic::calculateEffectRect(Filter* filter) { + FloatRect clippedSourceRect = filter->sourceImageRect(); + if (filter->sourceImageRect().x() < filter->filterRegion().x()) + clippedSourceRect.setX(filter->filterRegion().x()); + if (filter->sourceImageRect().y() < filter->filterRegion().y()) + clippedSourceRect.setY(filter->filterRegion().y()); + setSubRegion(clippedSourceRect); + return filter->filterRegion(); +} + +void SourceGraphic::apply(Filter* filter) +{ + GraphicsContext* filterContext = getEffectContext(); + if (!filterContext) + return; + + filterContext->drawImage(filter->sourceImage()->image(), IntPoint()); } void SourceGraphic::dump() diff --git a/WebCore/platform/graphics/filters/SourceGraphic.h b/WebCore/platform/graphics/filters/SourceGraphic.h index 363fb97..0c8095e 100644 --- a/WebCore/platform/graphics/filters/SourceGraphic.h +++ b/WebCore/platform/graphics/filters/SourceGraphic.h @@ -24,8 +24,8 @@ #if ENABLE(FILTERS) #include "FilterEffect.h" -#include "PlatformString.h" #include "Filter.h" +#include "PlatformString.h" namespace WebCore { @@ -35,6 +35,8 @@ namespace WebCore { static const AtomicString& effectName(); + virtual bool isSourceInput() { return true; } + virtual FloatRect calculateEffectRect(Filter*); void apply(Filter*); void dump(); diff --git a/WebCore/platform/graphics/gtk/FontGtk.cpp b/WebCore/platform/graphics/gtk/FontGtk.cpp index 6561f02..ee86f96 100644 --- a/WebCore/platform/graphics/gtk/FontGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontGtk.cpp @@ -44,12 +44,6 @@ #include <pango/pangofc-fontmap.h> #endif -#if !defined(PANGO_VERSION_CHECK) -// PANGO_VERSION_CHECK() and pango_layout_get_line_readonly() appeared in 1.5.2 -#define pango_layout_get_line_readonly pango_layout_get_line -#define PANGO_VERSION_CHECK(major,minor,micro) 0 -#endif - namespace WebCore { #define IS_HIGH_SURROGATE(u) ((UChar)(u) >= (UChar)0xd800 && (UChar)(u) <= (UChar)0xdbff) diff --git a/WebCore/platform/graphics/gtk/FontPlatformData.h b/WebCore/platform/graphics/gtk/FontPlatformData.h index ae1f134..2a65f1e 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformData.h +++ b/WebCore/platform/graphics/gtk/FontPlatformData.h @@ -42,6 +42,8 @@ namespace WebCore { +class String; + class FontPlatformData { public: FontPlatformData(WTF::HashTableDeletedValueType) @@ -107,6 +109,10 @@ public: #endif }; +#ifndef NDEBUG + String description() const; +#endif + #if defined(USE_FREETYPE) FcPattern* m_pattern; FcFontSet* m_fallbacks; diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp index f0f0dd5..f2c5f0c 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp @@ -259,4 +259,11 @@ bool FontPlatformData::operator==(const FontPlatformData& other) const return FcPatternEqual(m_pattern, other.m_pattern); } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + } diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp index 88bf427..9ea6811 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp +++ b/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp @@ -34,10 +34,6 @@ #include <pango/pango.h> #include <pango/pangocairo.h> -#if !defined(PANGO_VERSION_CHECK) -#define PANGO_VERSION_CHECK(major,minor,micro) 0 -#endif - // Use cairo-ft i a recent enough Pango version isn't available #if !PANGO_VERSION_CHECK(1,18,0) #include <cairo-ft.h> @@ -113,14 +109,14 @@ FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const cairo_font_face_t* face = cairo_ft_font_face_create_for_pattern(fcfont->font_pattern); double size; if (FcPatternGetDouble(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch) - size = 12.0; + size = 12.0; cairo_matrix_t fontMatrix; cairo_matrix_init_scale(&fontMatrix, size, size); cairo_font_options_t* fontOptions; if (pango_cairo_context_get_font_options(m_context)) - fontOptions = cairo_font_options_copy(pango_cairo_context_get_font_options(m_context)); + fontOptions = cairo_font_options_copy(pango_cairo_context_get_font_options(m_context)); else - fontOptions = cairo_font_options_create(); + fontOptions = cairo_font_options_create(); cairo_matrix_t ctm; cairo_matrix_init_identity(&ctm); m_scaledFont = cairo_scaled_font_create(face, &fontMatrix, &ctm, fontOptions); @@ -279,4 +275,11 @@ bool FontPlatformData::operator==(const FontPlatformData& other) const return result; } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + } diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp index 8dd1333..4e4bda9 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp @@ -24,7 +24,6 @@ #if ENABLE(VIDEO) #include "MediaPlayerPrivateGStreamer.h" -#include "VideoSinkGStreamer.h" #include "CString.h" #include "GraphicsContext.h" @@ -34,10 +33,9 @@ #include "MediaPlayer.h" #include "NotImplemented.h" #include "ScrollView.h" +#include "VideoSinkGStreamer.h" #include "Widget.h" -#include <wtf/GOwnPtr.h> -#include <gdk/gdkx.h> #include <gst/base/gstbasesrc.h> #include <gst/gst.h> #include <gst/interfaces/mixer.h> @@ -45,6 +43,7 @@ #include <gst/video/video.h> #include <limits> #include <math.h> +#include <wtf/GOwnPtr.h> using namespace std; @@ -52,8 +51,7 @@ namespace WebCore { gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data) { - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) - { + if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) { GOwnPtr<GError> err; GOwnPtr<gchar> debug; @@ -71,8 +69,7 @@ gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpoin gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data) { - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS) - { + if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS) { LOG_VERBOSE(Media, "End of Stream"); MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); mp->didEnd(); @@ -82,8 +79,7 @@ gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointe gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data) { - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_STATE_CHANGED) - { + if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_STATE_CHANGED) { MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); mp->updateStates(); } @@ -92,8 +88,7 @@ gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpoin gboolean mediaPlayerPrivateBufferingCallback(GstBus* bus, GstMessage* message, gpointer data) { - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_BUFFERING) - { + if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_BUFFERING) { gint percent = 0; gst_message_parse_buffering(message, &percent); LOG_VERBOSE(Media, "Buffering %d", percent); @@ -106,8 +101,8 @@ static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, MediaPlayerPriva playerPrivate->repaint(); } -MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) -{ +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ return new MediaPlayerPrivate(player); } @@ -137,7 +132,7 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) static bool gstInitialized = false; // FIXME: We should pass the arguments from the command line if (!gstInitialized) { - gst_init(0, NULL); + gst_init(0, 0); gstInitialized = true; } @@ -229,7 +224,7 @@ float MediaPlayerPrivate::currentTime() const GstQuery* query = gst_query_new_position(GST_FORMAT_TIME); if (gst_element_query(m_playBin, query)) { gint64 position; - gst_query_parse_position(query, NULL, &position); + gst_query_parse_position(query, 0, &position); ret = (float) (position / 1000000000.0); LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position)); } else { @@ -277,7 +272,7 @@ void MediaPlayerPrivate::setEndTime(float time) GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), GST_SEEK_TYPE_SET, start, - GST_SEEK_TYPE_SET, end )) + GST_SEEK_TYPE_SET, end)) LOG_VERBOSE(Media, "Seek to %f failed", time); } } @@ -345,9 +340,9 @@ void MediaPlayerPrivate::setMuted(bool b) if (b) { g_object_get(G_OBJECT(m_playBin), "volume", &m_volume, NULL); g_object_set(G_OBJECT(m_playBin), "volume", (double)0.0, NULL); - } else { + } else g_object_set(G_OBJECT(m_playBin), "volume", m_volume, NULL); - } + } void MediaPlayerPrivate::setRate(float rate) @@ -465,10 +460,10 @@ void MediaPlayerPrivate::updateStates() if (!m_playBin) return; - GstStateChangeReturn ret = gst_element_get_state (m_playBin, + GstStateChangeReturn ret = gst_element_get_state(m_playBin, &state, &pending, 250 * GST_NSECOND); - switch(ret) { + switch (ret) { case GST_STATE_CHANGE_SUCCESS: LOG_VERBOSE(Media, "State: %s, pending: %s", gst_element_state_get_name(state), @@ -476,14 +471,14 @@ void MediaPlayerPrivate::updateStates() if (state == GST_STATE_READY) { m_readyState = MediaPlayer::HaveEnoughData; - } else if (state == GST_STATE_PAUSED) { + } else if (state == GST_STATE_PAUSED) m_readyState = MediaPlayer::HaveEnoughData; - } + m_networkState = MediaPlayer::Loaded; g_object_get(m_playBin, "source", &m_source, NULL); if (!m_source) - LOG_VERBOSE(Media, "m_source is NULL"); + LOG_VERBOSE(Media, "m_source is 0"); break; case GST_STATE_CHANGE_ASYNC: LOG_VERBOSE(Media, "Async: State: %s, pending: %s", @@ -498,9 +493,9 @@ void MediaPlayerPrivate::updateStates() gst_element_state_get_name(pending)); if (state == GST_STATE_READY) { m_readyState = MediaPlayer::HaveFutureData; - } else if (state == GST_STATE_PAUSED) { + } else if (state == GST_STATE_PAUSED) m_readyState = MediaPlayer::HaveCurrentData; - } + m_networkState = MediaPlayer::Loading; break; default: @@ -614,7 +609,7 @@ MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, c { // FIXME: query the engine to see what types are supported notImplemented(); - return type == "video/x-theora+ogg" ? (!codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; + return type == "video/x-theora+ogg" ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; } void MediaPlayerPrivate::createGSTPlayBin(String url) @@ -632,7 +627,7 @@ void MediaPlayerPrivate::createGSTPlayBin(String url) g_object_set(G_OBJECT(m_playBin), "uri", url.utf8().data(), NULL); - GstElement* audioSink = gst_element_factory_make("gconfaudiosink", NULL); + GstElement* audioSink = gst_element_factory_make("gconfaudiosink", 0); m_videoSink = webkit_video_sink_new(m_surface); g_object_set(m_playBin, "audio-sink", audioSink, NULL); diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h index 628f0ac..8842f84 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h @@ -27,7 +27,8 @@ #include "MediaPlayerPrivate.h" #include "Timer.h" -#include <gtk/gtk.h> +#include <cairo.h> +#include <glib.h> typedef struct _GstElement GstElement; typedef struct _GstMessage GstMessage; @@ -44,96 +45,93 @@ namespace WebCore { gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); - class MediaPlayerPrivate : public MediaPlayerPrivateInterface - { - friend gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data); - friend gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); - friend gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); - - public: - - static void registerMediaEngine(MediaEngineRegistrar); - ~MediaPlayerPrivate(); - - IntSize naturalSize() const; - bool hasVideo() const; - - void load(const String &url); - void cancelLoad(); - - void play(); - void pause(); - - bool paused() const; - bool seeking() const; - - float duration() const; - float currentTime() const; - void seek(float); - void setEndTime(float); - - void setRate(float); - void setVolume(float); - void setMuted(bool); - - int dataRate() const; - - MediaPlayer::NetworkState networkState() const; - MediaPlayer::ReadyState readyState() const; - - float maxTimeBuffered() const; - float maxTimeSeekable() const; - unsigned bytesLoaded() const; - bool totalBytesKnown() const; - unsigned totalBytes() const; - - void setVisible(bool); - void setSize(const IntSize&); - - void loadStateChanged(); - void rateChanged(); - void sizeChanged(); - void timeChanged(); - void volumeChanged(); - void didEnd(); - void loadingFailed(); - - void repaint(); - void paint(GraphicsContext*, const IntRect&); - - private: - - MediaPlayerPrivate(MediaPlayer*); - static MediaPlayerPrivateInterface* create(MediaPlayer* player); - - static void getSupportedTypes(HashSet<String>&); - static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); - static bool isAvailable() { return true; } - - void updateStates(); - void cancelSeek(); - void endPointTimerFired(Timer<MediaPlayerPrivate>*); - float maxTimeLoaded() const; - void startEndPointTimerIfNeeded(); - - void createGSTPlayBin(String url); - - private: - MediaPlayer* m_player; - GstElement* m_playBin; - GstElement* m_videoSink; - GstElement* m_source; - float m_rate; - float m_endTime; - bool m_isEndReached; - double m_volume; - MediaPlayer::NetworkState m_networkState; - MediaPlayer::ReadyState m_readyState; - bool m_startedPlaying; - mutable bool m_isStreaming; - IntSize m_size; - bool m_visible; - cairo_surface_t* m_surface; + class MediaPlayerPrivate : public MediaPlayerPrivateInterface { + friend gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data); + friend gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); + friend gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); + + public: + static void registerMediaEngine(MediaEngineRegistrar); + ~MediaPlayerPrivate(); + + IntSize naturalSize() const; + bool hasVideo() const; + + void load(const String &url); + void cancelLoad(); + + void play(); + void pause(); + + bool paused() const; + bool seeking() const; + + float duration() const; + float currentTime() const; + void seek(float); + void setEndTime(float); + + void setRate(float); + void setVolume(float); + void setMuted(bool); + + int dataRate() const; + + MediaPlayer::NetworkState networkState() const; + MediaPlayer::ReadyState readyState() const; + + float maxTimeBuffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + bool totalBytesKnown() const; + unsigned totalBytes() const; + + void setVisible(bool); + void setSize(const IntSize&); + + void loadStateChanged(); + void rateChanged(); + void sizeChanged(); + void timeChanged(); + void volumeChanged(); + void didEnd(); + void loadingFailed(); + + void repaint(); + void paint(GraphicsContext*, const IntRect&); + + private: + MediaPlayerPrivate(MediaPlayer*); + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable() { return true; } + + void updateStates(); + void cancelSeek(); + void endPointTimerFired(Timer<MediaPlayerPrivate>*); + float maxTimeLoaded() const; + void startEndPointTimerIfNeeded(); + + void createGSTPlayBin(String url); + + private: + MediaPlayer* m_player; + GstElement* m_playBin; + GstElement* m_videoSink; + GstElement* m_source; + float m_rate; + float m_endTime; + bool m_isEndReached; + double m_volume; + MediaPlayer::NetworkState m_networkState; + MediaPlayer::ReadyState m_readyState; + bool m_startedPlaying; + mutable bool m_isStreaming; + IntSize m_size; + bool m_visible; + cairo_surface_t* m_surface; }; } diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp index 6684108..a77c1cf 100644 --- a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp @@ -99,7 +99,7 @@ bool SimpleFontData::containsCharacters(const UChar* characters, int length) con if (!face) return false; - for (unsigned i = 0; i < length; i++) { + for (int i = 0; i < length; i++) { if (FcFreeTypeCharIndex(face, characters[i]) == 0) { cairo_ft_scaled_font_unlock_face(m_platformData.m_scaledFont); return false; diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp index 436841c..f049998 100644 --- a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp @@ -40,10 +40,10 @@ GST_DEBUG_CATEGORY_STATIC(webkit_video_sink_debug); #define GST_CAT_DEFAULT webkit_video_sink_debug static GstElementDetails webkit_video_sink_details = - GST_ELEMENT_DETAILS((gchar*) "WebKit video sink", - (gchar*) "Sink/Video", - (gchar*) "Sends video data from a GStreamer pipeline to a Cairo surface", - (gchar*) "Alp Toker <alp@atoker.com>"); + GST_ELEMENT_DETAILS((gchar*) "WebKit video sink", + (gchar*) "Sink/Video", + (gchar*) "Sends video data from a GStreamer pipeline to a Cairo surface", + (gchar*) "Alp Toker <alp@atoker.com>"); enum { REPAINT_REQUESTED, @@ -70,10 +70,10 @@ struct _WebKitVideoSinkPrivate { }; #define _do_init(bla) \ - GST_DEBUG_CATEGORY_INIT (webkit_video_sink_debug, \ - "webkitsink", \ - 0, \ - "webkit video sink") + GST_DEBUG_CATEGORY_INIT(webkit_video_sink_debug, \ + "webkitsink", \ + 0, \ + "webkit video sink") GST_BOILERPLATE_FULL(WebKitVideoSink, webkit_video_sink, @@ -110,11 +110,11 @@ webkit_video_sink_idle_func(gpointer data) return FALSE; buffer = (GstBuffer*)g_async_queue_try_pop(priv->async_queue); - if (buffer == NULL || G_UNLIKELY(!GST_IS_BUFFER(buffer))) + if (!buffer || G_UNLIKELY(!GST_IS_BUFFER(buffer))) return FALSE; // TODO: consider priv->rgb_ordering? - cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(buffer), CAIRO_FORMAT_RGB24, priv->width, priv->height, (4 * priv->width + 3) & ~ 3); + cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(buffer), CAIRO_FORMAT_RGB24, priv->width, priv->height, (4 * priv->width + 3) & ~3); // TODO: We copy the data twice right now. This could be easily improved. cairo_t* cr = cairo_create(priv->surface); @@ -139,7 +139,7 @@ webkit_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer) WebKitVideoSinkPrivate* priv = sink->priv; g_async_queue_push(priv->async_queue, gst_buffer_ref(buffer)); - g_idle_add_full(G_PRIORITY_HIGH_IDLE, webkit_video_sink_idle_func, sink, NULL); + g_idle_add_full(G_PRIORITY_HIGH_IDLE, webkit_video_sink_idle_func, sink, 0); return GST_FLOW_OK; } @@ -168,7 +168,7 @@ webkit_video_sink_set_caps(GstBaseSink* bsink, GstCaps* caps) ret = gst_structure_get_int(structure, "width", &width); ret &= gst_structure_get_int(structure, "height", &height); fps = gst_structure_get_value(structure, "framerate"); - ret &= (fps != NULL); + ret &= (fps != 0); par = gst_structure_get_value(structure, "pixel-aspect-ratio"); @@ -202,12 +202,12 @@ webkit_video_sink_dispose(GObject* object) if (priv->surface) { cairo_surface_destroy(priv->surface); - priv->surface = NULL; + priv->surface = 0; } if (priv->async_queue) { g_async_queue_unref(priv->async_queue); - priv->async_queue = NULL; + priv->async_queue = 0; } G_OBJECT_CLASS(parent_class)->dispose(object); @@ -260,7 +260,7 @@ webkit_video_sink_stop(GstBaseSink* base_sink) g_async_queue_lock(priv->async_queue); /* Remove all remaining objects from the queue */ - while(GstBuffer* buffer = (GstBuffer*)g_async_queue_try_pop_unlocked(priv->async_queue)) + while (GstBuffer* buffer = (GstBuffer*)g_async_queue_try_pop_unlocked(priv->async_queue)) gst_buffer_unref(buffer); g_async_queue_unlock(priv->async_queue); @@ -291,8 +291,8 @@ webkit_video_sink_class_init(WebKitVideoSinkClass* klass) G_TYPE_FROM_CLASS(klass), (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), 0, - NULL, - NULL, + 0, + 0, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -314,7 +314,7 @@ webkit_video_sink_class_init(WebKitVideoSinkClass* klass) GstElement* webkit_video_sink_new(cairo_surface_t* surface) { - return (GstElement*)g_object_new(WEBKIT_TYPE_VIDEO_SINK, "surface", surface, NULL); + return (GstElement*)g_object_new(WEBKIT_TYPE_VIDEO_SINK, "surface", surface, 0); } void diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.h b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.h index 2a706fb..be2c94c 100644 --- a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.h +++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.h @@ -29,54 +29,52 @@ G_BEGIN_DECLS #define WEBKIT_TYPE_VIDEO_SINK webkit_video_sink_get_type() #define WEBKIT_VIDEO_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSink)) + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSink)) #define WEBKIT_VIDEO_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkClass)) + (G_TYPE_CHECK_CLASS_CAST((klass), \ + WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkClass)) #define WEBKIT_IS_VIDEO_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - WEBKIT_TYPE_VIDEO_SINK)) + (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + WEBKIT_TYPE_VIDEO_SINK)) #define WEBKIT_IS_VIDEO_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - WEBKIT_TYPE_VIDEO_SINK)) + (G_TYPE_CHECK_CLASS_TYPE((klass), \ + WEBKIT_TYPE_VIDEO_SINK)) #define WEBKIT_VIDEO_SINK_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkClass)) + (G_TYPE_INSTANCE_GET_CLASS((obj), \ + WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkClass)) typedef struct _WebKitVideoSink WebKitVideoSink; typedef struct _WebKitVideoSinkClass WebKitVideoSinkClass; typedef struct _WebKitVideoSinkPrivate WebKitVideoSinkPrivate; -struct _WebKitVideoSink -{ - /*< private >*/ - GstBaseSink parent; - WebKitVideoSinkPrivate *priv; +struct _WebKitVideoSink { + /*< private >*/ + GstBaseSink parent; + WebKitVideoSinkPrivate *priv; }; -struct _WebKitVideoSinkClass -{ - /*< private >*/ - GstBaseSinkClass parent_class; - - /* Future padding */ - void (* _webkit_reserved1) (void); - void (* _webkit_reserved2) (void); - void (* _webkit_reserved3) (void); - void (* _webkit_reserved4) (void); - void (* _webkit_reserved5) (void); - void (* _webkit_reserved6) (void); +struct _WebKitVideoSinkClass { + /*< private >*/ + GstBaseSinkClass parent_class; + + /* Future padding */ + void (* _webkit_reserved1)(void); + void (* _webkit_reserved2)(void); + void (* _webkit_reserved3)(void); + void (* _webkit_reserved4)(void); + void (* _webkit_reserved5)(void); + void (* _webkit_reserved6)(void); }; -GType webkit_video_sink_get_type (void) G_GNUC_CONST; -GstElement *webkit_video_sink_new (cairo_surface_t *surface); +GType webkit_video_sink_get_type(void) G_GNUC_CONST; +GstElement *webkit_video_sink_new(cairo_surface_t *surface); -void webkit_video_sink_set_surface (WebKitVideoSink *sink, cairo_surface_t *surface); +void webkit_video_sink_set_surface(WebKitVideoSink *sink, cairo_surface_t *surface); G_END_DECLS diff --git a/WebCore/platform/graphics/mac/ColorMac.h b/WebCore/platform/graphics/mac/ColorMac.h index 8a5ce31..b68b157 100644 --- a/WebCore/platform/graphics/mac/ColorMac.h +++ b/WebCore/platform/graphics/mac/ColorMac.h @@ -46,6 +46,9 @@ namespace WebCore { bool usesTestModeFocusRingColor(); void setUsesTestModeFocusRingColor(bool); + // Focus ring color used for testing purposes. + RGBA32 oldAquaFocusRingColor(); + } #endif diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm index 05dd78d..c8ea9b1 100644 --- a/WebCore/platform/graphics/mac/ColorMac.mm +++ b/WebCore/platform/graphics/mac/ColorMac.mm @@ -29,18 +29,26 @@ #import <wtf/RetainPtr.h> #import <wtf/StdLibExtras.h> -@interface WebCoreControlTintObserver : NSObject -+ (void)controlTintDidChange; -@end - namespace WebCore { // NSColor calls don't throw, so no need to block Cocoa exceptions in this file -static RGBA32 oldAquaFocusRingColor = 0xFF7DADD9; -static RGBA32 systemFocusRingColor; static bool useOldAquaFocusRingColor; +RGBA32 oldAquaFocusRingColor() +{ + return 0xFF7DADD9; +} + +void setUsesTestModeFocusRingColor(bool newValue) +{ + useOldAquaFocusRingColor = newValue; +} + +bool usesTestModeFocusRingColor() +{ + return useOldAquaFocusRingColor; +} static RGBA32 makeRGBAFromNSColor(NSColor *c) { @@ -119,42 +127,4 @@ CGColorRef createCGColor(const Color& c) return CGColorFromNSColor(nsColor(c)); } -Color focusRingColor() -{ - static bool tintIsKnown = false; - if (!tintIsKnown) { - [[NSNotificationCenter defaultCenter] addObserver:[WebCoreControlTintObserver class] - selector:@selector(controlTintDidChange) - name:NSControlTintDidChangeNotification - object:NSApp]; - [WebCoreControlTintObserver controlTintDidChange]; - tintIsKnown = true; - } - - if (usesTestModeFocusRingColor()) - return oldAquaFocusRingColor; - - return systemFocusRingColor; -} - -bool usesTestModeFocusRingColor() -{ - return useOldAquaFocusRingColor; -} - -void setUsesTestModeFocusRingColor(bool newValue) -{ - useOldAquaFocusRingColor = newValue; -} - -} - -@implementation WebCoreControlTintObserver - -+ (void)controlTintDidChange -{ - NSColor *color = [[NSColor keyboardFocusIndicatorColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - WebCore::systemFocusRingColor = WebCore::makeRGBAFromNSColor(color); -} - -@end +} // namespace WebCore diff --git a/WebCore/platform/graphics/mac/FontPlatformData.h b/WebCore/platform/graphics/mac/FontPlatformData.h index 1b7b884..faf5f2e 100644 --- a/WebCore/platform/graphics/mac/FontPlatformData.h +++ b/WebCore/platform/graphics/mac/FontPlatformData.h @@ -45,6 +45,8 @@ typedef UInt32 ATSUFontID; namespace WebCore { +class String; + #ifndef BUILDING_ON_TIGER inline CTFontRef toCTFontRef(NSFont *nsFont) { return reinterpret_cast<CTFontRef>(nsFont); } #endif @@ -118,6 +120,10 @@ struct FontPlatformData { CGFontRef cgFont() const { return m_cgFont; } #endif +#ifndef NDEBUG + String description() const; +#endif + private: static NSFont *hashTableDeletedFontValue() { return reinterpret_cast<NSFont *>(-1); } diff --git a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm index 83da4a9..0118c8b 100644 --- a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm +++ b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm @@ -23,6 +23,7 @@ #import "config.h" #import "FontPlatformData.h" +#import "PlatformString.h" #import "WebCoreSystemInterface.h" #import <AppKit/NSFont.h> @@ -107,4 +108,12 @@ bool FontPlatformData::allowsLigatures() const return ![[m_font coveredCharacterSet] characterIsMember:'a']; } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + RetainPtr<CFStringRef> cgFontDescription(AdoptCF, CFCopyDescription(cgFont())); + return String(cgFontDescription.get()) + " " + String::number(m_size) + (m_syntheticBold ? " synthetic bold" : "") + (m_syntheticOblique ? " syntheitic oblique" : ""); +} +#endif + } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h index 50138d5..ebdc6ac 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.h +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.h @@ -29,8 +29,15 @@ #if USE(ACCELERATED_COMPOSITING) #include "GraphicsLayer.h" +#include "StringHash.h" +#include <wtf/HashSet.h> #include <wtf/RetainPtr.h> +@class CABasicAnimation; +@class CAKeyframeAnimation; +@class CALayer; +@class CAMediaTimingFunction; +@class CAPropertyAnimation; @class WebAnimationDelegate; @class WebLayer; @@ -67,66 +74,192 @@ public: virtual void setMasksToBounds(bool); virtual void setDrawsContent(bool); - virtual void setBackgroundColor(const Color&, const Animation* anim = 0, double beginTime = 0); + virtual void setBackgroundColor(const Color&); virtual void clearBackgroundColor(); virtual void setContentsOpaque(bool); virtual void setBackfaceVisibility(bool); // return true if we started an animation - virtual bool setOpacity(float, const Animation* anim = 0, double beginTime = 0); + virtual void setOpacity(float); virtual void setNeedsDisplay(); virtual void setNeedsDisplayInRect(const FloatRect&); - virtual void suspendAnimations(); + virtual void setContentsRect(const IntRect&); + + virtual void suspendAnimations(double time); virtual void resumeAnimations(); - virtual bool animateTransform(const TransformValueList&, const IntSize&, const Animation*, double beginTime, bool isTransition); - virtual bool animateFloat(AnimatedPropertyID, const FloatValueList&, const Animation*, double beginTime); - + virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& keyframesName, double beginTime); + virtual void removeAnimationsForProperty(AnimatedPropertyID); + virtual void removeAnimationsForKeyframes(const String& keyframesName); + virtual void pauseAnimation(const String& keyframesName); + virtual void setContentsToImage(Image*); virtual void setContentsToVideo(PlatformLayer*); - virtual void clearContents(); - virtual void updateContentsRect(); - virtual PlatformLayer* platformLayer() const; #ifndef NDEBUG virtual void setDebugBackgroundColor(const Color&); virtual void setDebugBorder(const Color&, float borderWidth); - virtual void setZPosition(float); #endif + virtual void setGeometryOrientation(CompositingCoordinatesOrientation); + + void recursiveCommitChanges(); + void commitLayerChanges(); + + virtual void syncCompositingState(); + +protected: + virtual void setOpacityInternal(float); + private: - WebLayer* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } + void updateOpacityOnLayer(); + + WebLayer* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } WebLayer* hostLayerForSublayers() const; WebLayer* layerForSuperlayer() const; + CALayer* animatedLayer(AnimatedPropertyID property) const; - WebLayer* animatedLayer(AnimatedPropertyID property) const - { - return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); - } + bool createAnimationFromKeyframes(const KeyframeValueList&, const Animation*, const String& keyframesName, double beginTime); + bool createTransformAnimationsFromKeyframes(const KeyframeValueList&, const Animation*, const String& keyframesName, double beginTime, const IntSize& boxSize); - void setBasicAnimation(AnimatedPropertyID, TransformOperation::OperationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation*, double time); - void setKeyframeAnimation(AnimatedPropertyID, TransformOperation::OperationType, short index, void* keys, void* values, void* timingFunctions, bool isTransition, const Animation*, double time); + // Return autoreleased animation (use RetainPtr?) + CABasicAnimation* createBasicAnimation(const Animation*, AnimatedPropertyID, bool additive); + CAKeyframeAnimation* createKeyframeAnimation(const Animation*, AnimatedPropertyID, bool additive); + void setupAnimation(CAPropertyAnimation*, const Animation*, bool additive); + + CAMediaTimingFunction* timingFunctionForAnimationValue(const AnimationValue*, const Animation*); + + bool setAnimationEndpoints(const KeyframeValueList&, const Animation*, CABasicAnimation*); + bool setAnimationKeyframes(const KeyframeValueList&, const Animation*, CAKeyframeAnimation*); - virtual void removeAnimation(int index, bool reset); + bool setTransformAnimationEndpoints(const KeyframeValueList&, const Animation*, CABasicAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); + bool setTransformAnimationKeyframes(const KeyframeValueList&, const Animation*, CAKeyframeAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); + + bool animationIsRunning(const String& keyframesName) const + { + return m_runningKeyframeAnimations.find(keyframesName) != m_runningKeyframeAnimations.end(); + } bool requiresTiledLayer(const FloatSize&) const; void swapFromOrToTiledLayer(bool useTiledLayer); - void setContentsLayer(WebLayer*); - WebLayer* contentsLayer() const { return m_contentsLayer.get(); } + CompositingCoordinatesOrientation defaultContentsOrientation() const; + void updateContentsTransform(); + void setupContentsLayer(CALayer*); + CALayer* contentsLayer() const { return m_contentsLayer.get(); } + + // All these "update" methods will be called inside a BEGIN_BLOCK_OBJC_EXCEPTIONS/END_BLOCK_OBJC_EXCEPTIONS block. + 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 updateContentsVideo(); + void updateContentsRect(); + void updateGeometryOrientation(); + + void updateLayerAnimations(); + + void setAnimationOnLayer(CAPropertyAnimation*, AnimatedPropertyID, int index, double beginTime); + bool removeAnimationFromLayer(AnimatedPropertyID, int index); + void pauseAnimationOnLayer(AnimatedPropertyID, int index); + + enum LayerChange { + NoChange = 0, + NameChanged = 1 << 1, + ChildrenChanged = 1 << 2, // also used for content layer, and preserves-3d, and size if tiling changes? + PositionChanged = 1 << 3, + AnchorPointChanged = 1 << 4, + SizeChanged = 1 << 5, + TransformChanged = 1 << 6, + ChildrenTransformChanged = 1 << 7, + Preserves3DChanged = 1 << 8, + MasksToBoundsChanged = 1 << 9, + DrawsContentChanged = 1 << 10, // need this? + BackgroundColorChanged = 1 << 11, + ContentsOpaqueChanged = 1 << 12, + BackfaceVisibilityChanged = 1 << 13, + OpacityChanged = 1 << 14, + AnimationChanged = 1 << 15, + DirtyRectsChanged = 1 << 16, + ContentsImageChanged = 1 << 17, + ContentsVideoChanged = 1 << 18, + ContentsRectChanged = 1 << 19, + GeometryOrientationChanged = 1 << 20 + }; + typedef unsigned LayerChangeFlags; + void noteLayerPropertyChanged(LayerChangeFlags flags); + + void repaintLayerDirtyRects(); + RetainPtr<WebLayer> m_layer; RetainPtr<WebLayer> m_transformLayer; - RetainPtr<WebLayer> m_contentsLayer; + RetainPtr<CALayer> m_contentsLayer; + + enum ContentsLayerPurpose { + NoContentsLayer = 0, + ContentsLayerForImage, + ContentsLayerForVideo + }; + + ContentsLayerPurpose m_contentsLayerPurpose; + bool m_contentsLayerHasBackgroundColor : 1; RetainPtr<WebAnimationDelegate> m_animationDelegate; - bool m_contentLayerForImageOrVideo; + RetainPtr<CGImageRef> m_pendingContentsImage; + + struct LayerAnimation { + LayerAnimation(CAPropertyAnimation* caAnim, const String& keyframesName, AnimatedPropertyID property, int index, double beginTime) + : m_animation(caAnim) + , m_keyframesName(keyframesName) + , m_property(property) + , m_index(index) + , m_beginTime(beginTime) + { } + + RetainPtr<CAPropertyAnimation*> m_animation; + String m_keyframesName; + AnimatedPropertyID m_property; + int m_index; + double m_beginTime; + }; + + Vector<LayerAnimation> m_uncomittedAnimations; + + // Animations on the layer are identified by property + index. + typedef int AnimatedProperty; // std containers choke on the AnimatedPropertyID enum + typedef pair<AnimatedProperty, int> AnimationPair; + + HashSet<AnimatedProperty> m_transitionPropertiesToRemove; + + enum { Remove, Pause }; + typedef int AnimationProcessingAction; + typedef HashMap<String, AnimationProcessingAction> AnimationsToProcessMap; + AnimationsToProcessMap m_keyframeAnimationsToProcess; + + // Map of keyframe names to their associated lists of animations for running animations, so we can remove/pause them. + typedef HashMap<String, Vector<AnimationPair> > KeyframeAnimationsMap; + KeyframeAnimationsMap m_runningKeyframeAnimations; + + Vector<FloatRect> m_dirtyRects; + + LayerChangeFlags m_uncommittedChanges; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm index f361437..e5b9035 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -43,8 +43,10 @@ #import "TranslateTransformOperation.h" #import "WebLayer.h" #import "WebTiledLayer.h" +#import <limits.h> #import <wtf/CurrentTime.h> #import <wtf/UnusedParam.h> +#import <wtf/RetainPtr.h> using namespace std; @@ -80,8 +82,6 @@ static double mediaTimeToCurrentTime(CFTimeInterval t) } // namespace WebCore -static NSString* const WebAnimationCSSPropertyKey = @"GraphicsLayerCA_property"; - @interface WebAnimationDelegate : NSObject { WebCore::GraphicsLayerCA* m_graphicsLayer; } @@ -137,27 +137,25 @@ static inline void copyTransform(CATransform3D& toT3D, const TransformationMatri toT3D.m44 = narrowPrecisionToFloat(t.m44()); } -static NSValue* getTransformFunctionValue(const GraphicsLayer::TransformValue& transformValue, size_t index, const IntSize& size, TransformOperation::OperationType transformType) +static NSValue* getTransformFunctionValue(const TransformOperation* transformOp, TransformOperation::OperationType transformType, const IntSize& size) { - TransformOperation* op = (index >= transformValue.value()->operations().size()) ? 0 : transformValue.value()->operations()[index].get(); - switch (transformType) { case TransformOperation::ROTATE: case TransformOperation::ROTATE_X: case TransformOperation::ROTATE_Y: - return [NSNumber numberWithDouble:op ? deg2rad(static_cast<RotateTransformOperation*>(op)->angle()) : 0]; + return [NSNumber numberWithDouble:transformOp ? deg2rad(static_cast<const RotateTransformOperation*>(transformOp)->angle()) : 0]; case TransformOperation::SCALE_X: - return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->x() : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const ScaleTransformOperation*>(transformOp)->x() : 0]; case TransformOperation::SCALE_Y: - return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->y() : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const ScaleTransformOperation*>(transformOp)->y() : 0]; case TransformOperation::SCALE_Z: - return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->z() : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const ScaleTransformOperation*>(transformOp)->z() : 0]; case TransformOperation::TRANSLATE_X: - return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->x(size) : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const TranslateTransformOperation*>(transformOp)->x(size) : 0]; case TransformOperation::TRANSLATE_Y: - return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->y(size) : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const TranslateTransformOperation*>(transformOp)->y(size) : 0]; case TransformOperation::TRANSLATE_Z: - return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->z(size) : 0]; + return [NSNumber numberWithDouble:transformOp ? static_cast<const TranslateTransformOperation*>(transformOp)->z(size) : 0]; case TransformOperation::SCALE: case TransformOperation::TRANSLATE: case TransformOperation::SKEW_X: @@ -171,12 +169,12 @@ static NSValue* getTransformFunctionValue(const GraphicsLayer::TransformValue& t case TransformOperation::PERSPECTIVE: case TransformOperation::IDENTITY: case TransformOperation::NONE: { - TransformationMatrix t; - if (op) - op->apply(t, size); - CATransform3D cat; - copyTransform(cat, t); - return [NSValue valueWithCATransform3D:cat]; + TransformationMatrix transform; + if (transformOp) + transformOp->apply(transform, size); + CATransform3D caTransform; + copyTransform(caTransform, transform); + return [NSValue valueWithCATransform3D:caTransform]; } } @@ -212,6 +210,39 @@ static NSString* getValueFunctionNameForTransformOperation(TransformOperation::O } #endif +static String propertyIdToString(AnimatedPropertyID property) +{ + switch (property) { + case AnimatedPropertyWebkitTransform: + return "transform"; + case AnimatedPropertyOpacity: + return "opacity"; + case AnimatedPropertyBackgroundColor: + return "backgroundColor"; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return ""; +} + +static String animationIdentifier(AnimatedPropertyID property, int index) +{ + String animationId = propertyIdToString(property); + animationId.append("_"); + animationId.append(String::number(index)); + return animationId; +} + +#if !HAVE_MODERN_QUARTZCORE +static TransformationMatrix flipTransform() +{ + TransformationMatrix flipper; + flipper.flipY(); + return flipper; +} +#endif + static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction) { switch (timingFunction.type()) { @@ -250,15 +281,6 @@ static void clearLayerBackgroundColor(PlatformLayer* layer) [layer setBackgroundColor:0]; } -static CALayer* getPresentationLayer(CALayer* layer) -{ - CALayer* presLayer = [layer presentationLayer]; - if (!presLayer) - presLayer = layer; - - return presLayer; -} - static bool caValueFunctionSupported() { static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; @@ -304,9 +326,7 @@ static NSDictionary* nullActionsDictionary() nullValue, @"sublayerTransform", nullValue, @"sublayers", nullValue, @"transform", -#ifndef NDEBUG nullValue, @"zPosition", -#endif nil]; return actions; } @@ -318,12 +338,18 @@ GraphicsLayer* GraphicsLayer::createGraphicsLayer(GraphicsLayerClient* client) GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) : GraphicsLayer(client) -, m_contentLayerForImageOrVideo(false) +, m_contentsLayerPurpose(NoContentsLayer) +, m_contentsLayerHasBackgroundColor(false) +, m_uncommittedChanges(NoChange) { BEGIN_BLOCK_OBJC_EXCEPTIONS m_layer.adoptNS([[WebLayer alloc] init]); [m_layer.get() setLayerOwner:this]; +#if !HAVE_MODERN_QUARTZCORE + setContentsOrientation(defaultContentsOrientation()); +#endif + #ifndef NDEBUG updateDebugIndicators(); #endif @@ -336,21 +362,18 @@ GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) GraphicsLayerCA::~GraphicsLayerCA() { - // Remove a inner layer if there is one. - clearContents(); - + // We release our references to the CALayers here, but do not actively unparent them, + // since that will cause a commit and break our batched commit model. The layers will + // get released when the rootmost modified GraphicsLayerCA rebuilds its child layers. + BEGIN_BLOCK_OBJC_EXCEPTIONS // Clean up the WK layer. if (m_layer) { WebLayer* layer = m_layer.get(); [layer setLayerOwner:nil]; - [layer removeFromSuperlayer]; } - if (m_transformLayer) - [m_transformLayer.get() removeFromSuperlayer]; - // animationDidStart: can fire after this, so we need to clear out the layer on the delegate. [m_animationDelegate.get() setLayer:0]; @@ -361,10 +384,7 @@ void GraphicsLayerCA::setName(const String& name) { String longName = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + name; GraphicsLayer::setName(longName); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - [m_layer.get() setName:name]; - END_BLOCK_OBJC_EXCEPTIONS + noteLayerPropertyChanged(NameChanged); } NativeLayer GraphicsLayerCA::nativeLayer() const @@ -375,176 +395,485 @@ NativeLayer GraphicsLayerCA::nativeLayer() const void GraphicsLayerCA::addChild(GraphicsLayer* childLayer) { GraphicsLayer::addChild(childLayer); - - GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); - BEGIN_BLOCK_OBJC_EXCEPTIONS - [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; - END_BLOCK_OBJC_EXCEPTIONS + noteLayerPropertyChanged(ChildrenChanged); } void GraphicsLayerCA::addChildAtIndex(GraphicsLayer* childLayer, int index) { GraphicsLayer::addChildAtIndex(childLayer, index); - - GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); - BEGIN_BLOCK_OBJC_EXCEPTIONS - [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() atIndex:index]; - END_BLOCK_OBJC_EXCEPTIONS + noteLayerPropertyChanged(ChildrenChanged); } void GraphicsLayerCA::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) { - // FIXME: share code with base class - ASSERT(childLayer != this); - childLayer->removeFromParent(); + GraphicsLayer::addChildBelow(childLayer, sibling); + noteLayerPropertyChanged(ChildrenChanged); +} - bool found = false; - for (unsigned i = 0; i < m_children.size(); i++) { - if (sibling == m_children[i]) { - m_children.insert(i, childLayer); - found = true; - break; - } +void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildAbove(childLayer, sibling); + noteLayerPropertyChanged(ChildrenChanged); +} + +bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + noteLayerPropertyChanged(ChildrenChanged); + return true; } - childLayer->setParent(this); + return false; +} - BEGIN_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::removeFromParent() +{ + if (m_parent) + static_cast<GraphicsLayerCA*>(m_parent)->noteLayerPropertyChanged(ChildrenChanged); + GraphicsLayer::removeFromParent(); +} - GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); - GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(sibling); - if (found) - [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() below:siblingLayerCA->layerForSuperlayer()]; - else { - m_children.append(childLayer); - [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; - } +void GraphicsLayerCA::setPosition(const FloatPoint& point) +{ + if (point == m_position) + return; - END_BLOCK_OBJC_EXCEPTIONS + GraphicsLayer::setPosition(point); + noteLayerPropertyChanged(PositionChanged); } -void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) { - // FIXME: share code with base class - ASSERT(childLayer != this); - childLayer->removeFromParent(); + if (point == m_anchorPoint) + return; + + GraphicsLayer::setAnchorPoint(point); + noteLayerPropertyChanged(AnchorPointChanged); +} - unsigned i; - bool found = false; - for (i = 0; i < m_children.size(); i++) { - if (sibling == m_children[i]) { - m_children.insert(i+1, childLayer); - found = true; +void GraphicsLayerCA::setSize(const FloatSize& size) +{ + if (size == m_size) + return; + + GraphicsLayer::setSize(size); + noteLayerPropertyChanged(SizeChanged); +} + +void GraphicsLayerCA::setTransform(const TransformationMatrix& t) +{ + if (t == m_transform) + return; + + GraphicsLayer::setTransform(t); + noteLayerPropertyChanged(TransformChanged); +} + +void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) +{ + if (t == m_childrenTransform) + return; + + GraphicsLayer::setChildrenTransform(t); + noteLayerPropertyChanged(ChildrenTransformChanged); +} + +static void moveAnimation(AnimatedPropertyID property, CALayer* fromLayer, CALayer* toLayer) +{ + for (int index = 0; ; ++index) { + String animName = animationIdentifier(property, index); + + CAAnimation* anim = [fromLayer animationForKey:animName]; + if (!anim) break; - } + + [anim retain]; + [fromLayer removeAnimationForKey:animName]; + [toLayer addAnimation:anim forKey:animName]; + [anim release]; } - childLayer->setParent(this); +} - BEGIN_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::setPreserves3D(bool preserves3D) +{ + if (preserves3D == m_preserves3D) + return; - GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); - GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(sibling); - if (found) { - [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() above:siblingLayerCA->layerForSuperlayer()]; - } else { - m_children.append(childLayer); - [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; + GraphicsLayer::setPreserves3D(preserves3D); + noteLayerPropertyChanged(Preserves3DChanged); +} + +void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) +{ + if (masksToBounds == m_masksToBounds) + return; + + GraphicsLayer::setMasksToBounds(masksToBounds); + noteLayerPropertyChanged(MasksToBoundsChanged); +} + +void GraphicsLayerCA::setDrawsContent(bool drawsContent) +{ + if (drawsContent == m_drawsContent) + return; + + GraphicsLayer::setDrawsContent(drawsContent); + noteLayerPropertyChanged(DrawsContentChanged); +} + +void GraphicsLayerCA::setBackgroundColor(const Color& color) +{ + if (m_backgroundColorSet && m_backgroundColor == color) + return; + + GraphicsLayer::setBackgroundColor(color); + + m_contentsLayerHasBackgroundColor = true; + noteLayerPropertyChanged(BackgroundColorChanged); +} + +void GraphicsLayerCA::clearBackgroundColor() +{ + if (!m_backgroundColorSet) + return; + + GraphicsLayer::clearBackgroundColor(); + m_contentsLayerHasBackgroundColor = false; + noteLayerPropertyChanged(BackgroundColorChanged); +} + +void GraphicsLayerCA::setContentsOpaque(bool opaque) +{ + if (m_contentsOpaque == opaque) + return; + + GraphicsLayer::setContentsOpaque(opaque); + noteLayerPropertyChanged(ContentsOpaqueChanged); +} + +void GraphicsLayerCA::setBackfaceVisibility(bool visible) +{ + if (m_backfaceVisibility == visible) + return; + + GraphicsLayer::setBackfaceVisibility(visible); + noteLayerPropertyChanged(BackfaceVisibilityChanged); +} + +void GraphicsLayerCA::setOpacity(float opacity) +{ + float clampedOpacity = max(0.0f, min(opacity, 1.0f)); + + if (clampedOpacity == m_opacity) + return; + + GraphicsLayer::setOpacity(clampedOpacity); + noteLayerPropertyChanged(OpacityChanged); +} + +void GraphicsLayerCA::setNeedsDisplay() +{ + FloatRect hugeRect(-numeric_limits<float>::max() / 2, -numeric_limits<float>::max() / 2, + numeric_limits<float>::max(), numeric_limits<float>::max()); + + setNeedsDisplayInRect(hugeRect); +} + +void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) +{ + if (!drawsContent()) + return; + + const size_t maxDirtyRects = 32; + + for (size_t i = 0; i < m_dirtyRects.size(); ++i) { + if (m_dirtyRects[i].contains(rect)) + return; } + + if (m_dirtyRects.size() < maxDirtyRects) + m_dirtyRects.append(rect); + else + m_dirtyRects[0].unite(rect); - END_BLOCK_OBJC_EXCEPTIONS + noteLayerPropertyChanged(DirtyRectsChanged); } -bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +void GraphicsLayerCA::setContentsRect(const IntRect& rect) { - // FIXME: share code with base class - ASSERT(!newChild->parent()); + if (rect == m_contentsRect) + return; - bool found = false; - for (unsigned i = 0; i < m_children.size(); i++) { - if (oldChild == m_children[i]) { - m_children[i] = newChild; - found = true; - break; + GraphicsLayer::setContentsRect(rect); + noteLayerPropertyChanged(ContentsRectChanged); +} + +bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double beginTime) +{ + if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) + return false; + +#if !HAVE_MODERN_QUARTZCORE + // Older versions of QuartzCore do not handle opacity in transform layers properly, so we will + // always do software animation in that case. + if (valueList.property() == AnimatedPropertyOpacity) + return false; +#endif + + bool createdAnimations = false; + if (valueList.property() == AnimatedPropertyWebkitTransform) + createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, keyframesName, beginTime, boxSize); + else + createdAnimations = createAnimationFromKeyframes(valueList, anim, keyframesName, beginTime); + + if (createdAnimations) + noteLayerPropertyChanged(AnimationChanged); + + return createdAnimations; +} + +void GraphicsLayerCA::removeAnimationsForProperty(AnimatedPropertyID property) +{ + if (m_transitionPropertiesToRemove.find(property) != m_transitionPropertiesToRemove.end()) + return; + + m_transitionPropertiesToRemove.add(property); + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerCA::removeAnimationsForKeyframes(const String& animationName) +{ + if (!animationIsRunning(animationName)) + return; + + m_keyframeAnimationsToProcess.add(animationName, Remove); + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerCA::pauseAnimation(const String& keyframesName) +{ + if (!animationIsRunning(keyframesName)) + return; + + AnimationsToProcessMap::iterator it = m_keyframeAnimationsToProcess.find(keyframesName); + if (it != m_keyframeAnimationsToProcess.end()) { + // If an animation is scheduled to be removed, don't change the remove to a pause. + if (it->second != Remove) + it->second = Pause; + } else + m_keyframeAnimationsToProcess.add(keyframesName, Pause); + + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerCA::setContentsToImage(Image* image) +{ + if (image) { + m_pendingContentsImage = image->nativeImageForCurrentFrame(); + CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_pendingContentsImage.get()); + + static CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + if (CFEqual(colorSpace, deviceRGB)) { + // CoreGraphics renders images tagged with DeviceRGB using GenericRGB. When we hand such + // images to CA we need to tag them similarly so CA rendering matches CG rendering. + static CGColorSpaceRef genericRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + m_pendingContentsImage.adoptCF(CGImageCreateCopyWithColorSpace(m_pendingContentsImage.get(), genericRGB)); } + m_contentsLayerPurpose = ContentsLayerForImage; + if (!m_contentsLayer) + noteLayerPropertyChanged(ChildrenChanged); + } else { + m_pendingContentsImage = 0; + m_contentsLayerPurpose = NoContentsLayer; + if (m_contentsLayer) + noteLayerPropertyChanged(ChildrenChanged); } - if (found) { - oldChild->setParent(0); + noteLayerPropertyChanged(ContentsImageChanged); +} + +void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) +{ + if (videoLayer != m_contentsLayer.get()) + noteLayerPropertyChanged(ChildrenChanged); - newChild->removeFromParent(); - newChild->setParent(this); + m_contentsLayer = videoLayer; + noteLayerPropertyChanged(ContentsVideoChanged); - BEGIN_BLOCK_OBJC_EXCEPTIONS - GraphicsLayerCA* oldChildCA = static_cast<GraphicsLayerCA*>(oldChild); - GraphicsLayerCA* newChildCA = static_cast<GraphicsLayerCA*>(newChild); - [hostLayerForSublayers() replaceSublayer:oldChildCA->layerForSuperlayer() with:newChildCA->layerForSuperlayer()]; - END_BLOCK_OBJC_EXCEPTIONS - return true; - } - return false; + m_contentsLayerPurpose = videoLayer ? ContentsLayerForVideo : NoContentsLayer; } -void GraphicsLayerCA::removeFromParent() +void GraphicsLayerCA::setGeometryOrientation(CompositingCoordinatesOrientation orientation) { - GraphicsLayer::removeFromParent(); + if (orientation == m_geometryOrientation) + return; - BEGIN_BLOCK_OBJC_EXCEPTIONS - [layerForSuperlayer() removeFromSuperlayer]; - END_BLOCK_OBJC_EXCEPTIONS + GraphicsLayer::setGeometryOrientation(orientation); + noteLayerPropertyChanged(GeometryOrientationChanged); + +#if !HAVE_MODERN_QUARTZCORE + // Geometry orientation is mapped onto children transform in older QuartzCores. + switch (m_geometryOrientation) { + case CompositingCoordinatesTopDown: + setChildrenTransform(TransformationMatrix()); + break; + + case CompositingCoordinatesBottomUp: + setChildrenTransform(flipTransform()); + break; + } +#endif } -void GraphicsLayerCA::setPosition(const FloatPoint& point) +void GraphicsLayerCA::syncCompositingState() { - // Don't short-circuit here, because position and anchor point are inter-dependent. - GraphicsLayer::setPosition(point); + recursiveCommitChanges(); +} - // 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()); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - [primaryLayer() setPosition:posPoint]; - END_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::recursiveCommitChanges() +{ + commitLayerChanges(); + + const Vector<GraphicsLayer*>& childLayers = children(); + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + curChild->recursiveCommitChanges(); + } } -void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) +void GraphicsLayerCA::commitLayerChanges() { - // Don't short-circuit here, because position and anchor point are inter-dependent. - bool zChanged = (point.z() != m_anchorPoint.z()); - GraphicsLayer::setAnchorPoint(point); + if (!m_uncommittedChanges) + return; BEGIN_BLOCK_OBJC_EXCEPTIONS - // set the value on the layer to the new transform. - [primaryLayer() setAnchorPoint:FloatPoint(point.x(), point.y())]; - if (zChanged) { -#if HAVE_MODERN_QUARTZCORE - [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; -#endif + // Need to handle Preserves3DChanged first, because it affects which layers subsequent properties are applied to + if (m_uncommittedChanges & Preserves3DChanged) + updateLayerPreserves3D(); + + if (m_uncommittedChanges & NameChanged) { + if (m_transformLayer) + [m_transformLayer.get() setName:("Transform layer " + name())]; + [m_layer.get() setName:name()]; } - // Position depends on anchor point, so update it now. - setPosition(m_position); + if (m_uncommittedChanges & ContentsImageChanged) // Needs to happen before ChildrenChanged + updateContentsImage(); + + if (m_uncommittedChanges & ContentsVideoChanged) // Needs to happen before ChildrenChanged + updateContentsVideo(); + + if (m_uncommittedChanges & BackgroundColorChanged) // Needs to happen before ChildrenChanged, and after updating image or video + updateLayerBackgroundColor(); + + if (m_uncommittedChanges & ChildrenChanged) + updateSublayerList(); + + if (m_uncommittedChanges & PositionChanged) + updateLayerPosition(); + + if (m_uncommittedChanges & AnchorPointChanged) + updateAnchorPoint(); + + if (m_uncommittedChanges & SizeChanged) + updateLayerSize(); + + if (m_uncommittedChanges & TransformChanged) + updateTransform(); + + if (m_uncommittedChanges & ChildrenTransformChanged) + updateChildrenTransform(); + + if (m_uncommittedChanges & MasksToBoundsChanged) + updateMasksToBounds(); + + if (m_uncommittedChanges & DrawsContentChanged) + updateLayerDrawsContent(); + + if (m_uncommittedChanges & ContentsOpaqueChanged) + updateContentsOpaque(); + + if (m_uncommittedChanges & BackfaceVisibilityChanged) + updateBackfaceVisibility(); + + if (m_uncommittedChanges & OpacityChanged) + updateOpacityOnLayer(); + + if (m_uncommittedChanges & AnimationChanged) + updateLayerAnimations(); + + if (m_uncommittedChanges & DirtyRectsChanged) + repaintLayerDirtyRects(); + + if (m_uncommittedChanges & ContentsRectChanged) + updateContentsRect(); + + if (m_uncommittedChanges & GeometryOrientationChanged) + updateGeometryOrientation(); + + m_uncommittedChanges = NoChange; END_BLOCK_OBJC_EXCEPTIONS } -void GraphicsLayerCA::setSize(const FloatSize& size) +void GraphicsLayerCA::updateSublayerList() { - GraphicsLayer::setSize(size); + NSMutableArray* newSublayers = nil; - CGRect rect = CGRectMake(0.0f, - 0.0f, - m_size.width(), - m_size.height()); + if (m_transformLayer) { + // FIXME: add the primary layer in the correct order with negative z-order children. + newSublayers = [[NSMutableArray alloc] initWithObjects:m_layer.get(), nil]; + } else if (m_contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + newSublayers = [[NSMutableArray alloc] initWithObjects:m_contentsLayer.get(), nil]; + } - CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + const Vector<GraphicsLayer*>& childLayers = children(); + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + + CALayer* childLayer = curChild->layerForSuperlayer(); + if (!newSublayers) + newSublayers = [[NSMutableArray alloc] initWithObjects:childLayer, nil]; + else + [newSublayers addObject:childLayer]; + } + + [newSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; + + if (m_transformLayer) { + [m_transformLayer.get() 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.get() setSublayers:nil]; + [m_layer.get() addSublayer:m_contentsLayer.get()]; + } + } else { + [m_layer.get() setSublayers:newSublayers]; + } + + [newSublayers release]; +} + +void GraphicsLayerCA::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()); - BEGIN_BLOCK_OBJC_EXCEPTIONS + [primaryLayer() setPosition:posPoint]; +} +void GraphicsLayerCA::updateLayerSize() +{ + CGRect rect = CGRectMake(0, 0, m_size.width(), m_size.height()); if (m_transformLayer) { [m_transformLayer.get() setBounds:rect]; - - // the anchor of the contents layer is always at 0.5, 0.5, so the position - // is center-relative + // 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.get() setPosition:centerPoint]; } @@ -553,136 +882,88 @@ void GraphicsLayerCA::setSize(const FloatSize& size) swapFromOrToTiledLayer(needTiledLayer); [m_layer.get() setBounds:rect]; + + // Contents transform may depend on height. + updateContentsTransform(); // Note that we don't resize m_contentsLayer. It's up the caller to do that. - END_BLOCK_OBJC_EXCEPTIONS - // if we've changed the bounds, we need to recalculate the position - // of the layer, taking anchor point into account - setPosition(m_position); + // of the layer, taking anchor point into account. + updateLayerPosition(); } -void GraphicsLayerCA::setTransform(const TransformationMatrix& t) +void GraphicsLayerCA::updateAnchorPoint() +{ + [primaryLayer() setAnchorPoint:FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())]; +#if HAVE_MODERN_QUARTZCORE + [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; +#endif + updateLayerPosition(); +} + +void GraphicsLayerCA::updateTransform() { - GraphicsLayer::setTransform(t); - - BEGIN_BLOCK_OBJC_EXCEPTIONS CATransform3D transform; - copyTransform(transform, t); + copyTransform(transform, m_transform); [primaryLayer() setTransform:transform]; - END_BLOCK_OBJC_EXCEPTIONS - - // Remove any old transition entries for transform. - removeAllAnimationsForProperty(AnimatedPropertyWebkitTransform); - - // Even if we don't have a transition in the list, the layer may still have one. - // This happens when we are setting the final transform value after an animation or - // transition has ended. In removeAnimation we toss the entry from the list but don't - // remove it from the list. That way we aren't in danger of displaying a stale transform - // in the time between removing the animation and setting the new unanimated value. We - // can't do this in removeAnimation because we don't know the new transform value there. - String keyPath = propertyIdToString(AnimatedPropertyWebkitTransform); - CALayer* layer = animatedLayer(AnimatedPropertyWebkitTransform); - - for (int i = 0; ; ++i) { - String animName = keyPath + "_" + String::number(i); - if (![layer animationForKey: animName]) - break; - [layer removeAnimationForKey:animName]; - } } -void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) +void GraphicsLayerCA::updateChildrenTransform() { - if (t == m_childrenTransform) - return; - - GraphicsLayer::setChildrenTransform(t); - CATransform3D transform; - copyTransform(transform, t); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - // Set the value on the layer to the new transform. + copyTransform(transform, m_childrenTransform); [primaryLayer() setSublayerTransform:transform]; - END_BLOCK_OBJC_EXCEPTIONS } -static void moveAnimation(AnimatedPropertyID property, CALayer* fromLayer, CALayer* toLayer) +void GraphicsLayerCA::updateMasksToBounds() { - String keyPath = GraphicsLayer::propertyIdToString(property); - for (short index = 0; ; ++index) { - String animName = keyPath + "_" + String::number(index); - CAAnimation* anim = [fromLayer animationForKey:animName]; - if (!anim) - break; - - [anim retain]; - [fromLayer removeAnimationForKey:animName]; - [toLayer addAnimation:anim forKey:animName]; - [anim release]; - } + [m_layer.get() setMasksToBounds:m_masksToBounds]; +#ifndef NDEBUG + updateDebugIndicators(); +#endif } -static void moveSublayers(CALayer* fromLayer, CALayer* toLayer) +void GraphicsLayerCA::updateContentsOpaque() { - NSArray* sublayersCopy = [[fromLayer sublayers] copy]; // Avoid mutation while enumerating, and keep the sublayers alive. - NSEnumerator* childrenEnumerator = [sublayersCopy objectEnumerator]; - - CALayer* layer; - while ((layer = [childrenEnumerator nextObject]) != nil) { - [layer removeFromSuperlayer]; - [toLayer addSublayer:layer]; - } - [sublayersCopy release]; + [m_layer.get() setOpaque:m_contentsOpaque]; } -void GraphicsLayerCA::setPreserves3D(bool preserves3D) +void GraphicsLayerCA::updateBackfaceVisibility() { - GraphicsLayer::setPreserves3D(preserves3D); - - CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); - CGPoint centerPoint = CGPointMake(0.5f, 0.5f); - - BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setDoubleSided:m_backfaceVisibility]; +} +void GraphicsLayerCA::updateLayerPreserves3D() +{ Class transformLayerClass = NSClassFromString(@"CATransformLayer"); - if (preserves3D && !m_transformLayer && transformLayerClass) { + if (!transformLayerClass) + return; + + if (m_preserves3D && !m_transformLayer) { // Create the transform layer. m_transformLayer.adoptNS([[transformLayerClass alloc] init]); // Turn off default animations. [m_transformLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - + #ifndef NDEBUG [m_transformLayer.get() setName:[NSString stringWithFormat:@"Transform Layer CATransformLayer(%p) GraphicsLayer(%p)", m_transformLayer.get(), this]]; #endif // Copy the position from this layer. - [m_transformLayer.get() setBounds:[m_layer.get() bounds]]; - [m_transformLayer.get() setPosition:[m_layer.get() position]]; - [m_transformLayer.get() setAnchorPoint:[m_layer.get() anchorPoint]]; -#if HAVE_MODERN_QUARTZCORE - [m_transformLayer.get() setAnchorPointZ:[m_layer.get() anchorPointZ]]; -#endif - [m_transformLayer.get() setContentsRect:[m_layer.get() contentsRect]]; -#ifndef NDEBUG - [m_transformLayer.get() setZPosition:[m_layer.get() zPosition]]; -#endif + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); - // The contents layer is positioned at (0,0) relative to the transformLayer. + CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); [m_layer.get() setPosition:point]; - [m_layer.get() setAnchorPoint:centerPoint]; -#ifndef NDEBUG - [m_layer.get() setZPosition:0.0f]; -#endif - - // Transfer the transform over. - [m_transformLayer.get() setTransform:[m_layer.get() transform]]; + + [m_layer.get() setAnchorPoint:CGPointMake(0.5f, 0.5f)]; [m_layer.get() setTransform:CATransform3DIdentity]; - // Transfer the opacity from the old layer to the transform layer. - [m_transformLayer.get() setOpacity:m_opacity]; + // Set the old layer to opacity of 1. Further down we will set the opacity on the transform layer. [m_layer.get() setOpacity:1]; // Move this layer to be a child of the transform layer. @@ -690,600 +971,584 @@ void GraphicsLayerCA::setPreserves3D(bool preserves3D) [m_transformLayer.get() addSublayer:m_layer.get()]; moveAnimation(AnimatedPropertyWebkitTransform, m_layer.get(), m_transformLayer.get()); - moveSublayers(m_layer.get(), m_transformLayer.get()); - - } else if (!preserves3D && m_transformLayer) { + + updateSublayerList(); + } else if (!m_preserves3D && m_transformLayer) { // Relace the transformLayer in the parent with this layer. [m_layer.get() removeFromSuperlayer]; [[m_transformLayer.get() superlayer] replaceSublayer:m_transformLayer.get() with:m_layer.get()]; moveAnimation(AnimatedPropertyWebkitTransform, m_transformLayer.get(), m_layer.get()); - moveSublayers(m_transformLayer.get(), m_layer.get()); - - // Reset the layer position and transform. - [m_layer.get() setPosition:[m_transformLayer.get() position]]; - [m_layer.get() setAnchorPoint:[m_transformLayer.get() anchorPoint]]; -#if HAVE_MODERN_QUARTZCORE - [m_layer.get() setAnchorPointZ:[m_transformLayer.get() anchorPointZ]]; -#endif - [m_layer.get() setContentsRect:[m_transformLayer.get() contentsRect]]; - [m_layer.get() setTransform:[m_transformLayer.get() transform]]; - [m_layer.get() setOpacity:[m_transformLayer.get() opacity]]; -#ifndef NDEBUG - [m_layer.get() setZPosition:[m_transformLayer.get() zPosition]]; -#endif // Release the transform layer. m_transformLayer = 0; + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + updateSublayerList(); } - END_BLOCK_OBJC_EXCEPTIONS + updateOpacityOnLayer(); } -void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) +void GraphicsLayerCA::updateLayerDrawsContent() { - if (masksToBounds == m_masksToBounds) - return; - - GraphicsLayer::setMasksToBounds(masksToBounds); + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); - BEGIN_BLOCK_OBJC_EXCEPTIONS - [m_layer.get() setMasksToBounds:masksToBounds]; - END_BLOCK_OBJC_EXCEPTIONS + if (m_drawsContent) + [m_layer.get() setNeedsDisplay]; + else + [m_layer.get() setContents:nil]; #ifndef NDEBUG updateDebugIndicators(); #endif } -void GraphicsLayerCA::setDrawsContent(bool drawsContent) +void GraphicsLayerCA::updateLayerBackgroundColor() { - if (drawsContent != m_drawsContent) { - GraphicsLayer::setDrawsContent(drawsContent); - - bool needTiledLayer = requiresTiledLayer(m_size); - if (needTiledLayer != m_usingTiledLayer) - swapFromOrToTiledLayer(needTiledLayer); + if (!m_contentsLayer) + return; - BEGIN_BLOCK_OBJC_EXCEPTIONS - if (m_drawsContent) - [m_layer.get() setNeedsDisplay]; - else - [m_layer.get() setContents:nil]; - -#ifndef NDEBUG - updateDebugIndicators(); -#endif - END_BLOCK_OBJC_EXCEPTIONS - } + // 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 GraphicsLayerCA::setBackgroundColor(const Color& color, const Animation* transition, double beginTime) +void GraphicsLayerCA::updateContentsImage() { - GraphicsLayer::setBackgroundColor(color, transition, beginTime); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - if (!m_contentsLayer.get()) { - WebLayer* colorLayer = [WebLayer layer]; + if (m_pendingContentsImage) { + if (!m_contentsLayer.get()) { + WebLayer* imageLayer = [WebLayer layer]; #ifndef NDEBUG - [colorLayer setName:@"Color Layer"]; + [imageLayer setName:@"Image Layer"]; #endif - setContentsLayer(colorLayer); - } - - if (transition && !transition->isEmptyOrZeroDuration()) { - CALayer* presLayer = [m_contentsLayer.get() presentationLayer]; - // If we don't have a presentationLayer, just use the CALayer - if (!presLayer) - presLayer = m_contentsLayer.get(); - - // Get the current value of the background color from the layer - CGColorRef fromBackgroundColor = [presLayer backgroundColor]; - - CGColorRef bgColor = createCGColor(color); - setBasicAnimation(AnimatedPropertyBackgroundColor, TransformOperation::NONE, 0, fromBackgroundColor, bgColor, true, transition, beginTime); - CGColorRelease(bgColor); + setupContentsLayer(imageLayer); + m_contentsLayer.adoptNS([imageLayer retain]); + // 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? +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [m_contentsLayer.get() setMinificationFilter:kCAFilterTrilinear]; +#endif + [m_contentsLayer.get() setContents:(id)m_pendingContentsImage.get()]; + m_pendingContentsImage = 0; + + updateContentsRect(); } else { - removeAllAnimationsForProperty(AnimatedPropertyBackgroundColor); - setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); + // No image. + // m_contentsLayer will be removed via updateSublayerList. + m_contentsLayer = 0; } - - END_BLOCK_OBJC_EXCEPTIONS } -void GraphicsLayerCA::clearBackgroundColor() +void GraphicsLayerCA::updateContentsVideo() { - if (!m_contentLayerForImageOrVideo) - setContentsLayer(0); - else - clearLayerBackgroundColor(m_contentsLayer.get()); + // Video layer was set as m_contentsLayer, and will get parented in updateSublayerList(). + if (m_contentsLayer) { + setupContentsLayer(m_contentsLayer.get()); + updateContentsRect(); + } } -void GraphicsLayerCA::setContentsOpaque(bool opaque) +void GraphicsLayerCA::updateContentsRect() { - GraphicsLayer::setContentsOpaque(opaque); + if (!m_contentsLayer) + return; - BEGIN_BLOCK_OBJC_EXCEPTIONS - [m_layer.get() setOpaque:m_contentsOpaque]; - END_BLOCK_OBJC_EXCEPTIONS + CGPoint point = CGPointMake(m_contentsRect.x(), + m_contentsRect.y()); + CGRect rect = CGRectMake(0.0f, + 0.0f, + m_contentsRect.width(), + m_contentsRect.height()); + + [m_contentsLayer.get() setPosition:point]; + [m_contentsLayer.get() setBounds:rect]; } -void GraphicsLayerCA::setBackfaceVisibility(bool visible) +void GraphicsLayerCA::updateGeometryOrientation() { - if (m_backfaceVisibility == visible) - return; - - GraphicsLayer::setBackfaceVisibility(visible); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - [m_layer.get() setDoubleSided:visible]; - END_BLOCK_OBJC_EXCEPTIONS +#if HAVE_MODERN_QUARTZCORE + switch (geometryOrientation()) { + case CompositingCoordinatesTopDown: + [m_layer.get() setGeometryFlipped:NO]; + break; + + case CompositingCoordinatesBottomUp: + [m_layer.get() setGeometryFlipped:YES]; + break; + } + // Geometry orientation is mapped onto children transform in older QuartzCores, + // so is handled via setGeometryOrientation(). +#endif } -bool GraphicsLayerCA::setOpacity(float opacity, const Animation* transition, double beginTime) +void GraphicsLayerCA::updateLayerAnimations() { - if (forceSoftwareAnimation()) - return false; - - float clampedOpacity = max(0.0f, min(opacity, 1.0f)); - - bool opacitiesDiffer = (m_opacity != clampedOpacity); - - GraphicsLayer::setOpacity(clampedOpacity, transition, beginTime); - - int animIndex = findAnimationEntry(AnimatedPropertyOpacity, 0); - - // If we don't have a transition just set the opacity - if (!transition || transition->isEmptyOrZeroDuration()) { - // Three cases: - // 1) no existing animation or transition: just set opacity - // 2) existing transition: clear it and set opacity - // 3) existing animation: just return - // - if (animIndex < 0 || m_animations[animIndex].isTransition()) { - BEGIN_BLOCK_OBJC_EXCEPTIONS - [primaryLayer() setOpacity:opacity]; - if (animIndex >= 0) { - removeAllAnimationsForProperty(AnimatedPropertyOpacity); - animIndex = -1; - } else { - String keyPath = propertyIdToString(AnimatedPropertyOpacity); - - // FIXME: using hardcoded '0' here. We should be clearing all animations. For now there is only 1. - String animName = keyPath + "_0"; - [animatedLayer(AnimatedPropertyOpacity) removeAnimationForKey:animName]; + if (m_transitionPropertiesToRemove.size()) { + HashSet<int>::const_iterator end = m_transitionPropertiesToRemove.end(); + for (HashSet<AnimatedProperty>::const_iterator it = m_transitionPropertiesToRemove.begin(); it != end; ++it) { + AnimatedPropertyID currProperty = static_cast<AnimatedPropertyID>(*it); + // Remove all animations with this property in the key. + // We can't tell if this property is animating via a transition or animation here, but + // that's OK because the style system never sends both transitions and animations for the same property. + for (int index = 0; ; ++index) { + if (!removeAnimationFromLayer(currProperty, index)) + break; } - END_BLOCK_OBJC_EXCEPTIONS - } else { - // We have an animation, so don't set the opacity directly. - return false; - } - } else { - // At this point, we know we have a transition. But if it is the same and - // the opacity value has not changed, don't do anything. - if (!opacitiesDiffer && - ((animIndex == -1) || - (m_animations[animIndex].isTransition() && *(m_animations[animIndex].animation()) == *transition))) { - return false; } + + m_transitionPropertiesToRemove.clear(); } - - // If an animation is running, ignore this transition, but still save the value. - if (animIndex >= 0 && !m_animations[animIndex].isTransition()) - return false; - bool didAnimate = false; - - BEGIN_BLOCK_OBJC_EXCEPTIONS + if (m_keyframeAnimationsToProcess.size()) { + AnimationsToProcessMap::const_iterator end = m_keyframeAnimationsToProcess.end(); + for (AnimationsToProcessMap::const_iterator it = m_keyframeAnimationsToProcess.begin(); it != end; ++it) { + const String& currKeyframeName = it->first; + KeyframeAnimationsMap::iterator animationIt = m_runningKeyframeAnimations.find(currKeyframeName); + if (animationIt == m_runningKeyframeAnimations.end()) + continue; + + AnimationProcessingAction action = it->second; + const Vector<AnimationPair>& animations = animationIt->second; + for (size_t i = 0; i < animations.size(); ++i) { + const AnimationPair& currPair = animations[i]; + switch (action) { + case Remove: + removeAnimationFromLayer(static_cast<AnimatedPropertyID>(currPair.first), currPair.second); + break; + case Pause: + pauseAnimationOnLayer(static_cast<AnimatedPropertyID>(currPair.first), currPair.second); + break; + } + } + + if (action == Remove) + m_runningKeyframeAnimations.remove(currKeyframeName); + } - NSNumber* fromOpacityValue = nil; - NSNumber* toOpacityValue = [NSNumber numberWithFloat:opacity]; + m_keyframeAnimationsToProcess.clear(); + } - if (transition && !transition->isEmptyOrZeroDuration()) { - CALayer* presLayer = getPresentationLayer(primaryLayer()); - float fromOpacity = [presLayer opacity]; - fromOpacityValue = [NSNumber numberWithFloat:fromOpacity]; - setBasicAnimation(AnimatedPropertyOpacity, TransformOperation::NONE, 0, fromOpacityValue, toOpacityValue, true, transition, beginTime); - didAnimate = true; + size_t numAnimations; + if ((numAnimations = m_uncomittedAnimations.size())) { + for (size_t i = 0; i < numAnimations; ++i) { + const LayerAnimation& pendingAnimation = m_uncomittedAnimations[i]; + setAnimationOnLayer(pendingAnimation.m_animation.get(), pendingAnimation.m_property, pendingAnimation.m_index, pendingAnimation.m_beginTime); + + if (!pendingAnimation.m_keyframesName.isEmpty()) { + // If this is a keyframe anim, we have to remember the association of keyframes name to property/index pairs, + // so we can remove the animations later if needed. + // For transitions, we can just generate animation names with property and index. + KeyframeAnimationsMap::iterator it = m_runningKeyframeAnimations.find(pendingAnimation.m_keyframesName); + if (it == m_runningKeyframeAnimations.end()) { + Vector<AnimationPair> firstPair; + firstPair.append(AnimationPair(pendingAnimation.m_property, pendingAnimation.m_index)); + m_runningKeyframeAnimations.add(pendingAnimation.m_keyframesName, firstPair); + } else { + Vector<AnimationPair>& animPairs = it->second; + animPairs.append(AnimationPair(pendingAnimation.m_property, pendingAnimation.m_index)); + } + } + } + + m_uncomittedAnimations.clear(); } +} - END_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::setAnimationOnLayer(CAPropertyAnimation* caAnim, AnimatedPropertyID property, int index, double beginTime) +{ + PlatformLayer* layer = animatedLayer(property); + + if (beginTime) { + NSTimeInterval time = [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil]; + [caAnim setBeginTime:time]; + } - return didAnimate; + String animationName = animationIdentifier(property, index); + + [layer removeAnimationForKey:animationName]; + [layer addAnimation:caAnim forKey:animationName]; } -void GraphicsLayerCA::setNeedsDisplay() +bool GraphicsLayerCA::removeAnimationFromLayer(AnimatedPropertyID property, int index) { - BEGIN_BLOCK_OBJC_EXCEPTIONS + PlatformLayer* layer = animatedLayer(property); + + String animationName = animationIdentifier(property, index); + + if (![layer animationForKey:animationName]) + return false; - if (drawsContent()) - [m_layer.get() setNeedsDisplay]; + [layer removeAnimationForKey:animationName]; + return true; +} - END_BLOCK_OBJC_EXCEPTIONS + +static void copyAnimationProperties(CAPropertyAnimation* from, CAPropertyAnimation* to) +{ + [to setBeginTime:[from beginTime]]; + [to setDuration:[from duration]]; + [to setRepeatCount:[from repeatCount]]; + [to setAutoreverses:[from autoreverses]]; + [to setFillMode:[from fillMode]]; + [to setRemovedOnCompletion:[from isRemovedOnCompletion]]; + [to setAdditive:[from isAdditive]]; + [to setTimingFunction:[from timingFunction]]; + +#if HAVE_MODERN_QUARTZCORE + [to setValueFunction:[from valueFunction]]; +#endif } -void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) +void GraphicsLayerCA::pauseAnimationOnLayer(AnimatedPropertyID property, int index) { - BEGIN_BLOCK_OBJC_EXCEPTIONS - - if (drawsContent()) - [m_layer.get() setNeedsDisplayInRect:rect]; + PlatformLayer* layer = animatedLayer(property); - END_BLOCK_OBJC_EXCEPTIONS + String animationName = animationIdentifier(property, index); + + CAAnimation* caAnim = [layer animationForKey:animationName]; + if (!caAnim) + return; + + // Animations on the layer are immutable, so we have to clone and modify. + CAPropertyAnimation* pausedAnim = nil; + if ([caAnim isKindOfClass:[CAKeyframeAnimation class]]) { + CAKeyframeAnimation* existingKeyframeAnim = static_cast<CAKeyframeAnimation*>(caAnim); + CAKeyframeAnimation* newAnim = [CAKeyframeAnimation animationWithKeyPath:[existingKeyframeAnim keyPath]]; + copyAnimationProperties(existingKeyframeAnim, newAnim); + [newAnim setValues:[existingKeyframeAnim values]]; + [newAnim setKeyTimes:[existingKeyframeAnim keyTimes]]; + [newAnim setTimingFunctions:[existingKeyframeAnim timingFunctions]]; + pausedAnim = newAnim; + } else if ([caAnim isKindOfClass:[CABasicAnimation class]]) { + CABasicAnimation* existingPropertyAnim = static_cast<CABasicAnimation*>(caAnim); + CABasicAnimation* newAnim = [CABasicAnimation animationWithKeyPath:[existingPropertyAnim keyPath]]; + copyAnimationProperties(existingPropertyAnim, newAnim); + [newAnim setFromValue:[existingPropertyAnim fromValue]]; + [newAnim setToValue:[existingPropertyAnim toValue]]; + pausedAnim = newAnim; + } + + double t = [layer convertTime:currentTimeToMediaTime(currentTime()) fromLayer:nil]; + [pausedAnim setSpeed:0]; + [pausedAnim setTimeOffset:t - [caAnim beginTime]]; + [layer addAnimation:pausedAnim forKey:animationName]; // This will replace the running animation. } +void GraphicsLayerCA::repaintLayerDirtyRects() +{ + if (!m_dirtyRects.size()) + return; + + for (size_t i = 0; i < m_dirtyRects.size(); ++i) + [m_layer.get() setNeedsDisplayInRect:m_dirtyRects[i]]; + + m_dirtyRects.clear(); +} -bool GraphicsLayerCA::animateTransform(const TransformValueList& valueList, const IntSize& size, const Animation* anim, double beginTime, bool isTransition) +bool GraphicsLayerCA::createAnimationFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& keyframesName, double beginTime) { - if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) + ASSERT(valueList.property() != AnimatedPropertyWebkitTransform); + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + bool isKeyframe = valueList.size() > 2; + bool valuesOK; + + bool additive = false; + int animationIndex = 0; + + CAPropertyAnimation* caAnimation; + if (isKeyframe) { + CAKeyframeAnimation* keyframeAnim = createKeyframeAnimation(animation, valueList.property(), additive); + valuesOK = setAnimationKeyframes(valueList, animation, keyframeAnim); + caAnimation = keyframeAnim; + } else { + CABasicAnimation* basicAnim = createBasicAnimation(animation, valueList.property(), additive); + valuesOK = setAnimationEndpoints(valueList, animation, basicAnim); + caAnimation = basicAnim; + } + + if (!valuesOK) return false; - - TransformValueList::FunctionList functionList; - bool isValid, hasBigRotation; - valueList.makeFunctionList(functionList, isValid, hasBigRotation); + + m_uncomittedAnimations.append(LayerAnimation(caAnimation, keyframesName, valueList.property(), animationIndex, beginTime)); + + END_BLOCK_OBJC_EXCEPTIONS; + + return true; +} + +bool GraphicsLayerCA::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& keyframesName, double beginTime, const IntSize& boxSize) +{ + ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); + + TransformOperationList functionList; + bool listsMatch, hasBigRotation; + fetchTransformOperationList(valueList, functionList, listsMatch, hasBigRotation); // We need to fall back to software animation if we don't have setValueFunction:, and // we would need to animate each incoming transform function separately. This is the // case if we have a rotation >= 180 or we have more than one transform function. if ((hasBigRotation || functionList.size() > 1) && !caValueFunctionSupported()) return false; + + bool validMatrices = true; + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + // If functionLists don't match we do a matrix animation, otherwise we do a component hardware animation. + // Also, we can't do component animation unless we have valueFunction, so we need to do matrix animation + // if that's not true as well. + bool isMatrixAnimation = !listsMatch || !caValueFunctionSupported(); - BEGIN_BLOCK_OBJC_EXCEPTIONS - - // Rules for animation: - // - // 1) If functionList is empty or we don't have a big rotation, we do a matrix animation. We could - // use component animation for lists without a big rotation, but there is no need to, and this - // is more efficient. - // - // 2) Otherwise we do a component hardware animation. - bool isMatrixAnimation = !isValid || !hasBigRotation; - - // Set transform to identity since we are animating components and we need the base - // to be the identity transform. - TransformationMatrix t; - CATransform3D toT3D; - copyTransform(toT3D, t); - [primaryLayer() setTransform:toT3D]; - + size_t numAnimations = isMatrixAnimation ? 1 : functionList.size(); bool isKeyframe = valueList.size() > 2; - + // Iterate through the transform functions, sending an animation for each one. - for (int functionIndex = 0; ; ++functionIndex) { - if (functionIndex >= static_cast<int>(functionList.size()) && !isMatrixAnimation) - break; - - TransformOperation::OperationType opType = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[functionIndex]; + for (size_t animationIndex = 0; animationIndex < numAnimations; ++animationIndex) { + TransformOperation::OperationType transformOp = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[animationIndex]; + CAPropertyAnimation* caAnimation; + // CA applies animations in reverse order (<rdar://problem/7095638>) so we need the last one we add (per property) + // to be non-additive. + bool additive = animationIndex < (numAnimations - 1); if (isKeyframe) { - NSMutableArray* timesArray = [[NSMutableArray alloc] init]; - NSMutableArray* valArray = [[NSMutableArray alloc] init]; - NSMutableArray* tfArray = [[NSMutableArray alloc] init]; - - // Iterate through the keyframes, building arrays for the animation. - for (Vector<TransformValue>::const_iterator it = valueList.values().begin(); it != valueList.values().end(); ++it) { - const TransformValue& curValue = (*it); - - // fill in the key time and timing function - [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; - - const TimingFunction* tf = 0; - if (curValue.timingFunction()) - tf = curValue.timingFunction(); - else if (anim->isTimingFunctionSet()) - tf = &anim->timingFunction(); - - CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction(LinearTimingFunction)); - [tfArray addObject:timingFunction]; - - // fill in the function - if (isMatrixAnimation) { - TransformationMatrix t; - curValue.value()->apply(size, t); - CATransform3D cat; - copyTransform(cat, t); - [valArray addObject:[NSValue valueWithCATransform3D:cat]]; - } else - [valArray addObject:getTransformFunctionValue(curValue, functionIndex, size, opType)]; - } - - // We toss the last tfArray value because it has to one shorter than the others. - [tfArray removeLastObject]; - - setKeyframeAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, timesArray, valArray, tfArray, isTransition, anim, beginTime); - - [timesArray release]; - [valArray release]; - [tfArray release]; + CAKeyframeAnimation* keyframeAnim = createKeyframeAnimation(animation, valueList.property(), additive); + validMatrices = setTransformAnimationKeyframes(valueList, animation, keyframeAnim, animationIndex, transformOp, isMatrixAnimation, boxSize); + caAnimation = keyframeAnim; } else { - // Is a transition - id fromValue, toValue; - - if (isMatrixAnimation) { - TransformationMatrix fromt, tot; - valueList.at(0).value()->apply(size, fromt); - valueList.at(1).value()->apply(size, tot); - - CATransform3D cat; - copyTransform(cat, fromt); - fromValue = [NSValue valueWithCATransform3D:cat]; - copyTransform(cat, tot); - toValue = [NSValue valueWithCATransform3D:cat]; - } else { - fromValue = getTransformFunctionValue(valueList.at(0), functionIndex, size, opType); - toValue = getTransformFunctionValue(valueList.at(1), functionIndex, size, opType); - } - - setBasicAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, fromValue, toValue, isTransition, anim, beginTime); + CABasicAnimation* basicAnim = createBasicAnimation(animation, valueList.property(), additive); + validMatrices = setTransformAnimationEndpoints(valueList, animation, basicAnim, animationIndex, transformOp, isMatrixAnimation, boxSize); + caAnimation = basicAnim; } - if (isMatrixAnimation) + if (!validMatrices) break; + + m_uncomittedAnimations.append(LayerAnimation(caAnimation, keyframesName, valueList.property(), animationIndex, beginTime)); } - END_BLOCK_OBJC_EXCEPTIONS - return true; + END_BLOCK_OBJC_EXCEPTIONS; + + return validMatrices; } -bool GraphicsLayerCA::animateFloat(AnimatedPropertyID property, const FloatValueList& valueList, const Animation* animation, double beginTime) +CABasicAnimation* GraphicsLayerCA::createBasicAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) { - if (forceSoftwareAnimation() || valueList.size() < 2) - return false; - - // if there is already is an animation for this property and it hasn't changed, ignore it. - int i = findAnimationEntry(property, 0); - if (i >= 0 && *m_animations[i].animation() == *animation) { - m_animations[i].setIsCurrent(); - return false; - } - - if (valueList.size() == 2) { - float fromVal = valueList.at(0).value(); - float toVal = valueList.at(1).value(); - if (isnan(toVal) && isnan(fromVal)) - return false; - - // initialize the property to 0 - [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; - setBasicAnimation(property, TransformOperation::NONE, 0, isnan(fromVal) ? nil : [NSNumber numberWithFloat:fromVal], isnan(toVal) ? nil : [NSNumber numberWithFloat:toVal], false, animation, beginTime); - return true; - } - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - NSMutableArray* timesArray = [[NSMutableArray alloc] init]; - NSMutableArray* valArray = [[NSMutableArray alloc] init]; - NSMutableArray* tfArray = [[NSMutableArray alloc] init]; - - for (unsigned i = 0; i < valueList.values().size(); ++i) { - const FloatValue& curValue = valueList.values()[i]; - [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; - [valArray addObject:[NSNumber numberWithFloat:curValue.value()]]; - - const TimingFunction* tf = 0; - if (curValue.timingFunction()) - tf = curValue.timingFunction(); - else if (animation->isTimingFunctionSet()) - tf = &animation->timingFunction(); - - CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); - [tfArray addObject:timingFunction]; - } - - // We toss the last tfArray value because it has to one shorter than the others. - [tfArray removeLastObject]; - - // Initialize the property to 0. - [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; - // Then set the animation. - setKeyframeAnimation(property, TransformOperation::NONE, 0, timesArray, valArray, tfArray, false, animation, beginTime); - - [timesArray release]; - [valArray release]; - [tfArray release]; + CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:propertyIdToString(property)]; + setupAnimation(basicAnim, anim, additive); + return basicAnim; +} - END_BLOCK_OBJC_EXCEPTIONS - return true; +CAKeyframeAnimation* GraphicsLayerCA::createKeyframeAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) +{ + CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:propertyIdToString(property)]; + setupAnimation(keyframeAnim, anim, additive); + return keyframeAnim; } -void GraphicsLayerCA::setContentsToImage(Image* image) +void GraphicsLayerCA::setupAnimation(CAPropertyAnimation* propertyAnim, const Animation* anim, bool additive) { - if (image) { - BEGIN_BLOCK_OBJC_EXCEPTIONS - { - if (!m_contentsLayer.get()) { - WebLayer* imageLayer = [WebLayer layer]; -#ifndef NDEBUG - [imageLayer setName:@"Image Layer"]; -#endif - setContentsLayer(imageLayer); - } + double duration = anim->duration(); + if (duration <= 0) + duration = cAnimationAlmostZeroDuration; - // FIXME: maybe only do trilinear if the image is being scaled down, - // but then what if the layer size changes? -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - [m_contentsLayer.get() setMinificationFilter:kCAFilterTrilinear]; -#endif - CGImageRef theImage = image->nativeImageForCurrentFrame(); - [m_contentsLayer.get() setContents:(id)theImage]; - } - END_BLOCK_OBJC_EXCEPTIONS - } else - setContentsLayer(0); + float repeatCount = anim->iterationCount(); + if (repeatCount == Animation::IterationCountInfinite) + repeatCount = FLT_MAX; + else if (anim->direction() == Animation::AnimationDirectionAlternate) + repeatCount /= 2; - m_contentLayerForImageOrVideo = (image != 0); -} + [propertyAnim setDuration:duration]; + [propertyAnim setRepeatCount:repeatCount]; + [propertyAnim setAutoreverses:anim->direction()]; + [propertyAnim setRemovedOnCompletion:NO]; + [propertyAnim setAdditive:additive]; + [propertyAnim setFillMode:@"extended"]; -void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) -{ - setContentsLayer(videoLayer); - m_contentLayerForImageOrVideo = (videoLayer != 0); + [propertyAnim setDelegate:m_animationDelegate.get()]; } -void GraphicsLayerCA::clearContents() +CAMediaTimingFunction* GraphicsLayerCA::timingFunctionForAnimationValue(const AnimationValue* animValue, const Animation* anim) { - if (m_contentLayerForImageOrVideo) { - setContentsLayer(0); - m_contentLayerForImageOrVideo = false; - } + const TimingFunction* tf = 0; + if (animValue->timingFunction()) + tf = animValue->timingFunction(); + else if (anim->isTimingFunctionSet()) + tf = &anim->timingFunction(); + + return getCAMediaTimingFunction(tf ? *tf : TimingFunction()); } -void GraphicsLayerCA::updateContentsRect() +bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim) { - if (m_client && m_contentsLayer) { - IntRect contentRect = m_client->contentsBox(this); - - CGPoint point = CGPointMake(contentRect.x(), - contentRect.y()); - CGRect rect = CGRectMake(0.0f, - 0.0f, - contentRect.width(), - contentRect.height()); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - [m_contentsLayer.get() setPosition:point]; - [m_contentsLayer.get() setBounds:rect]; - END_BLOCK_OBJC_EXCEPTIONS + id fromValue = nil; + id toValue = nil; + + switch (valueList.property()) { + case AnimatedPropertyOpacity: { + const FloatAnimationValue* startVal = static_cast<const FloatAnimationValue*>(valueList.at(0)); + const FloatAnimationValue* endVal = static_cast<const FloatAnimationValue*>(valueList.at(1)); + fromValue = [NSNumber numberWithFloat:startVal->value()]; + toValue = [NSNumber numberWithFloat:endVal->value()]; + break; + } + default: + ASSERT_NOT_REACHED(); // we don't animate color yet + break; } + + // This codepath is used for 2-keyframe animations, so we still need to look in the start + // for a timing function. + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); + [basicAnim setTimingFunction:timingFunction]; + + [basicAnim setFromValue:fromValue]; + [basicAnim setToValue:toValue]; + + return true; } -void GraphicsLayerCA::setBasicAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation* transition, double beginTime) +bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* anim, CAKeyframeAnimation* keyframeAnim) { - ASSERT(fromVal || toVal); + RetainPtr<NSMutableArray> keyTimes(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr<NSMutableArray> values(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr<NSMutableArray> timingFunctions(AdoptNS, [[NSMutableArray alloc] init]); - WebLayer* layer = animatedLayer(property); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - // add an entry for this animation - addAnimationEntry(property, index, isTransition, transition); + for (unsigned i = 0; i < valueList.size(); ++i) { + const AnimationValue* curValue = valueList.at(i); + [keyTimes.get() addObject:[NSNumber numberWithFloat:curValue->keyTime()]]; - String keyPath = propertyIdToString(property); - String animName = keyPath + "_" + String::number(index); + switch (valueList.property()) { + case AnimatedPropertyOpacity: { + const FloatAnimationValue* floatValue = static_cast<const FloatAnimationValue*>(curValue); + [values.get() addObject:[NSNumber numberWithFloat:floatValue->value()]]; + break; + } + default: + ASSERT_NOT_REACHED(); // we don't animate color yet + break; + } - CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:keyPath]; + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, anim); + [timingFunctions.get() addObject:timingFunction]; + } - double duration = transition->duration(); - if (duration <= 0) - duration = cAnimationAlmostZeroDuration; - - float repeatCount = transition->iterationCount(); - if (repeatCount == Animation::IterationCountInfinite) - repeatCount = FLT_MAX; - else if (transition->direction() == Animation::AnimationDirectionAlternate) - repeatCount /= 2; - - [basicAnim setDuration:duration]; - [basicAnim setRepeatCount:repeatCount]; - [basicAnim setAutoreverses:transition->direction()]; - [basicAnim setRemovedOnCompletion:NO]; - - // Note that currently transform is the only property which has animations - // with an index > 0. - [basicAnim setAdditive:property == AnimatedPropertyWebkitTransform]; - [basicAnim setFillMode:@"extended"]; -#if HAVE_MODERN_QUARTZCORE - if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) - [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; -#else - UNUSED_PARAM(operationType); -#endif + // We toss the last tfArray value because it has to one shorter than the others. + [timingFunctions.get() removeLastObject]; + + [keyframeAnim setKeyTimes:keyTimes.get()]; + [keyframeAnim setValues:values.get()]; + [keyframeAnim setTimingFunctions:timingFunctions.get()]; - // Set the delegate (and property value). - int prop = isTransition ? property : AnimatedPropertyInvalid; - [basicAnim setValue:[NSNumber numberWithInt:prop] forKey:WebAnimationCSSPropertyKey]; - [basicAnim setDelegate:m_animationDelegate.get()]; + return true; +} - NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; - [basicAnim setBeginTime:bt]; +bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOp, bool isMatrixAnimation, const IntSize& boxSize) +{ + id fromValue; + id toValue; + + ASSERT(valueList.size() == 2); + const TransformAnimationValue* startValue = static_cast<const TransformAnimationValue*>(valueList.at(0)); + const TransformAnimationValue* endValue = static_cast<const TransformAnimationValue*>(valueList.at(1)); - if (fromVal) - [basicAnim setFromValue:reinterpret_cast<id>(fromVal)]; - if (toVal) - [basicAnim setToValue:reinterpret_cast<id>(toVal)]; + if (isMatrixAnimation) { + TransformationMatrix fromTransform, toTransform; + startValue->value()->apply(boxSize, fromTransform); + endValue->value()->apply(boxSize, toTransform); - const TimingFunction* tf = 0; - if (transition->isTimingFunctionSet()) - tf = &transition->timingFunction(); + // If any matrix is singular, CA won't animate it correctly. So fall back to software animation + if (!fromTransform.isInvertible() || !toTransform.isInvertible()) + return false; + + CATransform3D caTransform; + copyTransform(caTransform, fromTransform); + fromValue = [NSValue valueWithCATransform3D:caTransform]; - CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); + copyTransform(caTransform, toTransform); + toValue = [NSValue valueWithCATransform3D:caTransform]; + } else { + fromValue = getTransformFunctionValue(startValue->value()->at(functionIndex), transformOp, boxSize); + toValue = getTransformFunctionValue(endValue->value()->at(functionIndex), transformOp, boxSize); + } + + // This codepath is used for 2-keyframe animations, so we still need to look in the start + // for a timing function. + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); [basicAnim setTimingFunction:timingFunction]; - // Send over the animation. - [layer removeAnimationForKey:animName]; - [layer addAnimation:basicAnim forKey:animName]; - - END_BLOCK_OBJC_EXCEPTIONS + [basicAnim setFromValue:fromValue]; + [basicAnim setToValue:toValue]; + +#if HAVE_MODERN_QUARTZCORE + if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(transformOp)) + [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; +#endif + + return true; } -void GraphicsLayerCA::setKeyframeAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* keys, void* values, void* timingFunctions, - bool isTransition, const Animation* anim, double beginTime) +bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& valueList, const Animation* animation, CAKeyframeAnimation* keyframeAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize) { - PlatformLayer* layer = animatedLayer(property); - - // Add an entry for this animation (which may change beginTime). - addAnimationEntry(property, index, isTransition, anim); + RetainPtr<NSMutableArray> keyTimes(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr<NSMutableArray> values(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr<NSMutableArray> timingFunctions(AdoptNS, [[NSMutableArray alloc] init]); - String keyPath = propertyIdToString(property); - String animName = keyPath + "_" + String::number(index); + for (unsigned i = 0; i < valueList.size(); ++i) { + const TransformAnimationValue* curValue = static_cast<const TransformAnimationValue*>(valueList.at(i)); + [keyTimes.get() addObject:[NSNumber numberWithFloat:curValue->keyTime()]]; - BEGIN_BLOCK_OBJC_EXCEPTIONS + if (isMatrixAnimation) { + TransformationMatrix transform; + curValue->value()->apply(boxSize, transform); - CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:keyPath]; + // If any matrix is singular, CA won't animate it correctly. So fall back to software animation + if (!transform.isInvertible()) + return false; - double duration = anim->duration(); - if (duration <= 0) - duration = cAnimationAlmostZeroDuration; + CATransform3D caTransform; + copyTransform(caTransform, transform); + [values.get() addObject:[NSValue valueWithCATransform3D:caTransform]]; + } else { + const TransformOperation* transformOp = curValue->value()->at(functionIndex); + [values.get() addObject:getTransformFunctionValue(transformOp, transformOpType, boxSize)]; + } - float repeatCount = anim->iterationCount(); - if (repeatCount == Animation::IterationCountInfinite) - repeatCount = FLT_MAX; - else if (anim->direction() == Animation::AnimationDirectionAlternate) - repeatCount /= 2; + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation); + [timingFunctions.get() addObject:timingFunction]; + } + + // We toss the last tfArray value because it has to one shorter than the others. + [timingFunctions.get() removeLastObject]; - [keyframeAnim setDuration:duration]; - [keyframeAnim setRepeatCount:repeatCount]; - [keyframeAnim setAutoreverses:anim->direction()]; - [keyframeAnim setRemovedOnCompletion:NO]; + [keyframeAnim setKeyTimes:keyTimes.get()]; + [keyframeAnim setValues:values.get()]; + [keyframeAnim setTimingFunctions:timingFunctions.get()]; - // The first animation is non-additive, all the rest are additive. - // Note that currently transform is the only property which has animations - // with an index > 0. - [keyframeAnim setAdditive:(property == AnimatedPropertyWebkitTransform) ? YES : NO]; - [keyframeAnim setFillMode:@"extended"]; #if HAVE_MODERN_QUARTZCORE - if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) + if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(transformOpType)) [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; -#else - UNUSED_PARAM(operationType); #endif - - [keyframeAnim setKeyTimes:reinterpret_cast<id>(keys)]; - [keyframeAnim setValues:reinterpret_cast<id>(values)]; - - // Set the delegate (and property value). - int prop = isTransition ? property : AnimatedPropertyInvalid; - [keyframeAnim setValue:[NSNumber numberWithInt: prop] forKey:WebAnimationCSSPropertyKey]; - [keyframeAnim setDelegate:m_animationDelegate.get()]; - - NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; - [keyframeAnim setBeginTime:bt]; - - // Set the timing functions, if any. - if (timingFunctions != nil) - [keyframeAnim setTimingFunctions:(id)timingFunctions]; - - // Send over the animation. - [layer removeAnimationForKey:animName]; - [layer addAnimation:keyframeAnim forKey:animName]; - - END_BLOCK_OBJC_EXCEPTIONS + return true; } -void GraphicsLayerCA::suspendAnimations() +void GraphicsLayerCA::suspendAnimations(double time) { - double t = currentTimeToMediaTime(currentTime()); + double t = currentTimeToMediaTime(time ? time : currentTime()); [primaryLayer() setSpeed:0]; [primaryLayer() setTimeOffset:t]; } @@ -1294,64 +1559,12 @@ void GraphicsLayerCA::resumeAnimations() [primaryLayer() setTimeOffset:0]; } -void GraphicsLayerCA::removeAnimation(int index, bool reset) -{ - ASSERT(index >= 0); - - AnimatedPropertyID property = m_animations[index].property(); - - // Set the value of the property and remove the animation. - String keyPath = propertyIdToString(property); - String animName = keyPath + "_" + String::number(m_animations[index].index()); - CALayer* layer = animatedLayer(property); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - // If we are not resetting, it means we are pausing. So we need to get the current presentation - // value into the property before we remove the animation. - if (!reset) { - // Put the current value into the property. - CALayer* presLayer = [layer presentationLayer]; - if (presLayer) - [layer setValue:[presLayer valueForKeyPath:keyPath] forKeyPath:keyPath]; - - // Make sure the saved values accurately reflect the value in the layer. - id val = [layer valueForKeyPath:keyPath]; - switch (property) { - case AnimatedPropertyWebkitTransform: - // FIXME: needs comment explaining why the m_transform is not obtained from the layer - break; - case AnimatedPropertyBackgroundColor: - m_backgroundColor = Color(reinterpret_cast<CGColorRef>(val)); - break; - case AnimatedPropertyOpacity: - m_opacity = [val floatValue]; - break; - case AnimatedPropertyInvalid: - ASSERT_NOT_REACHED(); - break; - } - } - - // If we have reached the end of an animation, we don't want to actually remove the - // animation from the CALayer. At some point we will be setting the property to its - // unanimated value and at that point we will remove the animation. That will avoid - // any flashing between the time the animation is removed and the property is set. - if (!reset || m_animations[index].isTransition()) - [layer removeAnimationForKey:animName]; - - END_BLOCK_OBJC_EXCEPTIONS - - // Remove the animation entry. - m_animations.remove(index); -} - -PlatformLayer* GraphicsLayerCA::hostLayerForSublayers() const +WebLayer* GraphicsLayerCA::hostLayerForSublayers() const { return m_transformLayer ? m_transformLayer.get() : m_layer.get(); } -PlatformLayer* GraphicsLayerCA::layerForSuperlayer() const +WebLayer* GraphicsLayerCA::layerForSuperlayer() const { if (m_transformLayer) return m_transformLayer.get(); @@ -1359,6 +1572,11 @@ PlatformLayer* GraphicsLayerCA::layerForSuperlayer() const return m_layer.get(); } +CALayer* GraphicsLayerCA::animatedLayer(AnimatedPropertyID property) const +{ + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); +} + PlatformLayer* GraphicsLayerCA::platformLayer() const { return primaryLayer(); @@ -1391,16 +1609,7 @@ void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) END_BLOCK_OBJC_EXCEPTIONS } - -void GraphicsLayerCA::setZPosition(float position) -{ - GraphicsLayer::setZPosition(position); - - BEGIN_BLOCK_OBJC_EXCEPTIONS - [primaryLayer() setZPosition:position]; - END_BLOCK_OBJC_EXCEPTIONS -} -#endif +#endif // NDEBUG bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const { @@ -1411,21 +1620,21 @@ bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; } -void GraphicsLayerCA::swapFromOrToTiledLayer(bool userTiledLayer) +void GraphicsLayerCA::swapFromOrToTiledLayer(bool useTiledLayer) { - if (userTiledLayer == m_usingTiledLayer) + if (useTiledLayer == m_usingTiledLayer) return; CGSize tileSize = CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize); - BEGIN_BLOCK_OBJC_EXCEPTIONS - RetainPtr<CALayer> oldLayer = m_layer.get(); - Class layerClass = userTiledLayer ? [WebTiledLayer self] : [WebLayer self]; + Class layerClass = useTiledLayer ? [WebTiledLayer self] : [WebLayer self]; m_layer.adoptNS([[layerClass alloc] init]); - if (userTiledLayer) { + m_usingTiledLayer = useTiledLayer; + + if (useTiledLayer) { WebTiledLayer* tiledLayer = (WebTiledLayer*)m_layer.get(); [tiledLayer setTileSize:tileSize]; [tiledLayer setLevelsOfDetail:1]; @@ -1435,24 +1644,35 @@ void GraphicsLayerCA::swapFromOrToTiledLayer(bool userTiledLayer) [tiledLayer setContentsGravity:@"bottomLeft"]; else [tiledLayer setContentsGravity:@"topLeft"]; + +#if !HAVE_MODERN_QUARTZCORE + // Tiled layer has issues with flipped coordinates. + setContentsOrientation(CompositingCoordinatesTopDown); +#endif + } else { +#if !HAVE_MODERN_QUARTZCORE + setContentsOrientation(defaultContentsOrientation()); +#endif } [m_layer.get() setLayerOwner:this]; [m_layer.get() setSublayers:[oldLayer.get() sublayers]]; [[oldLayer.get() superlayer] replaceSublayer:oldLayer.get() with:m_layer.get()]; + + updateContentsTransform(); + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + updateMasksToBounds(); + updateContentsOpaque(); + updateBackfaceVisibility(); + updateLayerBackgroundColor(); - [m_layer.get() setBounds:[oldLayer.get() bounds]]; - [m_layer.get() setPosition:[oldLayer.get() position]]; - [m_layer.get() setAnchorPoint:[oldLayer.get() anchorPoint]]; - [m_layer.get() setOpaque:[oldLayer.get() isOpaque]]; - [m_layer.get() setOpacity:[oldLayer.get() opacity]]; - [m_layer.get() setTransform:[oldLayer.get() transform]]; - [m_layer.get() setSublayerTransform:[oldLayer.get() sublayerTransform]]; - [m_layer.get() setDoubleSided:[oldLayer.get() isDoubleSided]]; -#ifndef NDEBUG - [m_layer.get() setZPosition:[oldLayer.get() zPosition]]; -#endif + updateOpacityOnLayer(); #ifndef NDEBUG String name = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + m_name; @@ -1467,68 +1687,84 @@ void GraphicsLayerCA::swapFromOrToTiledLayer(bool userTiledLayer) // need to tell new layer to draw itself setNeedsDisplay(); - END_BLOCK_OBJC_EXCEPTIONS - - m_usingTiledLayer = userTiledLayer; - #ifndef NDEBUG updateDebugIndicators(); #endif } -void GraphicsLayerCA::setContentsLayer(WebLayer* contentsLayer) +GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayerCA::defaultContentsOrientation() const { - if (contentsLayer == m_contentsLayer) - return; - - BEGIN_BLOCK_OBJC_EXCEPTIONS +#if !HAVE_MODERN_QUARTZCORE + // Older QuartzCore does not support -geometryFlipped, so we manually flip the root + // layer geometry, and then flip the contents of each layer back so that the CTM for CG + // is unflipped, allowing it to do the correct font auto-hinting. + return CompositingCoordinatesBottomUp; +#else + return CompositingCoordinatesTopDown; +#endif +} - if (m_contentsLayer) { - [m_contentsLayer.get() removeFromSuperlayer]; - m_contentsLayer = 0; +void GraphicsLayerCA::updateContentsTransform() +{ +#if !HAVE_MODERN_QUARTZCORE + if (contentsOrientation() == CompositingCoordinatesBottomUp) { + CGAffineTransform contentsTransform = CGAffineTransformMakeScale(1, -1); + contentsTransform = CGAffineTransformTranslate(contentsTransform, 0, -[m_layer.get() bounds].size.height); + [m_layer.get() setContentsTransform:contentsTransform]; } - - if (contentsLayer) { - // Turn off implicit animations on the inner layer. - [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - - m_contentsLayer.adoptNS([contentsLayer retain]); - - bool needToFlip = GraphicsLayer::compositingCoordinatesOrientation() == GraphicsLayer::CompositingCoordinatesBottomUp; - CGPoint anchorPoint = needToFlip ? CGPointMake(0.0f, 1.0f) : CGPointZero; - - // If the layer world is flipped, we need to un-flip the contents layer - if (needToFlip) { - 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.get() setTransform:flipper]; - } - [m_contentsLayer.get() setAnchorPoint:anchorPoint]; +#else + ASSERT(contentsOrientation() == CompositingCoordinatesTopDown); +#endif +} - // Insert the content layer first. Video elements require this, because they have - // shadow content that must display in front of the video. - [m_layer.get() insertSublayer:m_contentsLayer.get() atIndex:0]; +void GraphicsLayerCA::setupContentsLayer(CALayer* contentsLayer) +{ + // Turn off implicit animations on the inner layer. + [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - updateContentsRect(); + 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}; + [contentsLayer setTransform:flipper]; + [contentsLayer setAnchorPoint:CGPointMake(0.0f, 1.0f)]; + } else + [contentsLayer setAnchorPoint:CGPointZero]; #ifndef NDEBUG - if (showDebugBorders()) { - setLayerBorderColor(m_contentsLayer.get(), Color(0, 0, 128, 180)); - [m_contentsLayer.get() setBorderWidth:1.0f]; - } -#endif + if (showDebugBorders()) { + setLayerBorderColor(contentsLayer, Color(0, 0, 128, 180)); + [contentsLayer setBorderWidth:1.0f]; } -#ifndef NDEBUG - updateDebugIndicators(); #endif +} - END_BLOCK_OBJC_EXCEPTIONS +void GraphicsLayerCA::setOpacityInternal(float accumulatedOpacity) +{ + [(preserves3D() ? m_layer.get() : primaryLayer()) setOpacity:accumulatedOpacity]; } -} // namespace WebCore +void GraphicsLayerCA::updateOpacityOnLayer() +{ +#if !HAVE_MODERN_QUARTZCORE + // Distribute opacity either to our own layer or to our children. We pass in the + // contribution from our parent(s). + distributeOpacity(parent() ? parent()->accumulatedOpacity() : 1); +#else + [primaryLayer() setOpacity:m_opacity]; +#endif +} +void GraphicsLayerCA::noteLayerPropertyChanged(LayerChangeFlags flags) +{ + if (!m_uncommittedChanges && m_client) + m_client->notifySyncRequired(this); + + m_uncommittedChanges |= flags; +} + +} // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index f90f258..54eea00 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -37,12 +37,14 @@ #import <QTKit/QTTime.h> @class QTMovie; @class QTMovieView; +@class QTMovieLayer; @class QTVideoRendererWebKitOnly; @class WebCoreMovieObserver; #else class QTMovie; class QTMovieView; class QTTime; +class QTMovieLayer; class QTVideoRendererWebKitOnly; class WebCoreMovieObserver; #endif @@ -93,6 +95,7 @@ private: void setRate(float); void setVolume(float); + void setPreservesPitch(bool); void setEndTime(float time); @@ -111,14 +114,37 @@ private: void setSize(const IntSize&); void paint(GraphicsContext*, const IntRect&); + void paintCurrentFrameInContext(GraphicsContext*, const IntRect&); + +#if USE(ACCELERATED_COMPOSITING) + bool supportsAcceleratedRendering() const; + void acceleratedRenderingStateChanged(); +#endif + + bool hasSingleSecurityOrigin() const; + MediaPlayer::MovieLoadType movieLoadType() const; void createQTMovie(const String& url); + void createQTMovie(NSURL *, NSDictionary *movieAttributes); + + enum MediaRenderingMode { MediaRenderingNone, MediaRenderingMovieView, MediaRenderingSoftwareRenderer, MediaRenderingMovieLayer }; + MediaRenderingMode currentRenderingMode() const; + MediaRenderingMode preferredRenderingMode() const; + void setUpVideoRendering(); void tearDownVideoRendering(); + bool hasSetUpVideoRendering() const; + void createQTMovieView(); void detachQTMovieView(); - void createQTVideoRenderer(); + + enum QTVideoRendererMode { QTVideoRendererModeDefault, QTVideoRendererModeListensForNewImages }; + void createQTVideoRenderer(QTVideoRendererMode rendererMode); void destroyQTVideoRenderer(); + + void createQTMovieLayer(); + void destroyQTMovieLayer(); + QTTime createQTTime(float time) const; void updateStates(); @@ -132,6 +158,8 @@ private: void cacheMovieScale(); bool metaDataAvailable() const { return m_qtMovie && m_readyState >= MediaPlayer::HaveMetadata; } + bool isReadyForRendering() const; + MediaPlayer* m_player; RetainPtr<QTMovie> m_qtMovie; RetainPtr<QTMovieView> m_qtMovieView; @@ -149,7 +177,10 @@ private: unsigned m_enabledTrackCount; unsigned m_totalTrackCount; bool m_hasUnsupportedTracks; - float m_duration; + float m_reportedDuration; + float m_cachedDuration; + float m_timeToRestore; + RetainPtr<QTMovieLayer> m_qtVideoLayer; #if DRAW_FRAME_RATE int m_frameCountWhilePlaying; double m_timeStartedPlaying; diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index dd41ed3..c1d7fcb 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -44,6 +44,10 @@ #import <objc/objc-runtime.h> #import <wtf/UnusedParam.h> +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayer.h" +#endif + #if DRAW_FRAME_RATE #import "Font.h" #import "Frame.h" @@ -67,6 +71,7 @@ SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (tim SOFT_LINK_CLASS(QTKit, QTMovie) SOFT_LINK_CLASS(QTKit, QTMovieView) +SOFT_LINK_CLASS(QTKit, QTMovieLayer) SOFT_LINK_POINTER(QTKit, QTTrackMediaTypeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *) @@ -85,6 +90,7 @@ SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieCurrentSizeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMoviePreventExternalURLLinksAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieRateChangesPreservePitchAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *) @@ -100,6 +106,7 @@ SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *) #define QTMovie getQTMovieClass() #define QTMovieView getQTMovieViewClass() +#define QTMovieLayer getQTMovieLayerClass() #define QTTrackMediaTypeAttribute getQTTrackMediaTypeAttribute() #define QTMediaTypeAttribute getQTMediaTypeAttribute() @@ -118,6 +125,7 @@ SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *) #define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute() #define QTMovieCurrentSizeAttribute getQTMovieCurrentSizeAttribute() #define QTMoviePreventExternalURLLinksAttribute getQTMoviePreventExternalURLLinksAttribute() +#define QTMovieRateChangesPreservePitchAttribute getQTMovieRateChangesPreservePitchAttribute() #define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification() #define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification() #define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification() @@ -201,7 +209,9 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_enabledTrackCount(0) , m_totalTrackCount(0) , m_hasUnsupportedTracks(false) - , m_duration(-1.0f) + , m_reportedDuration(-1.0f) + , m_cachedDuration(-1.0f) + , m_timeToRestore(-1.0f) #if DRAW_FRAME_RATE , m_frameCountWhilePlaying(0) , m_timeStartedPlaying(0) @@ -220,41 +230,49 @@ MediaPlayerPrivate::~MediaPlayerPrivate() void MediaPlayerPrivate::createQTMovie(const String& url) { + NSURL *cocoaURL = KURL(url); + NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: + cocoaURL, QTMovieURLAttribute, + [NSNumber numberWithBool:m_player->preservesPitch()], QTMovieRateChangesPreservePitchAttribute, + [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, + [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, + [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute", +#endif +#ifndef BUILDING_ON_TIGER + QTMovieApertureModeClean, QTMovieApertureModeAttribute, +#endif + nil]; + + createQTMovie(cocoaURL, movieAttributes); +} + +void MediaPlayerPrivate::createQTMovie(NSURL *url, NSDictionary *movieAttributes) +{ [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; + bool recreating = false; if (m_qtMovie) { + recreating = true; destroyQTVideoRenderer(); m_qtMovie = 0; } - // Disable streaming support for now, <rdar://problem/5693967> - if (protocolIs(url, "rtsp")) + // Disable rtsp streams for now, <rdar://problem/5693967> + if (protocolIs([url scheme], "rtsp")) return; - - NSURL *cocoaURL = KURL(url); - NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: - cocoaURL, QTMovieURLAttribute, - [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, - [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, - [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute", -#endif -#ifndef BUILDING_ON_TIGER - QTMovieApertureModeClean, QTMovieApertureModeAttribute, -#endif - nil]; NSError *error = nil; m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]); - // FIXME: Find a proper way to detect streaming content. - m_isStreaming = protocolIs(url, "rtsp"); - if (!m_qtMovie) return; [m_qtMovie.get() setVolume:m_player->volume()]; + + if (recreating && hasVideo()) + createQTVideoRenderer(QTVideoRendererModeListensForNewImages); [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(loadStateChanged:) @@ -370,7 +388,7 @@ void MediaPlayerPrivate::detachQTMovieView() } } -void MediaPlayerPrivate::createQTVideoRenderer() +void MediaPlayerPrivate::createQTVideoRenderer(QTVideoRendererMode rendererMode) { destroyQTVideoRenderer(); @@ -381,11 +399,13 @@ void MediaPlayerPrivate::createQTVideoRenderer() // associate our movie with our instance of QTVideoRendererWebKitOnly [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()]; - // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification - [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() - selector:@selector(newImageAvailable:) - name:QTVideoRendererWebKitOnlyNewImageAvailableNotification - object:m_qtVideoRenderer.get()]; + if (rendererMode == QTVideoRendererModeListensForNewImages) { + // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(newImageAvailable:) + name:QTVideoRendererWebKitOnlyNewImageAvailableNotification + object:m_qtVideoRenderer.get()]; + } } void MediaPlayerPrivate::destroyQTVideoRenderer() @@ -404,23 +424,114 @@ void MediaPlayerPrivate::destroyQTVideoRenderer() m_qtVideoRenderer = nil; } -void MediaPlayerPrivate::setUpVideoRendering() +void MediaPlayerPrivate::createQTMovieLayer() +{ +#if USE(ACCELERATED_COMPOSITING) + if (!m_qtMovie) + return; + + ASSERT(supportsAcceleratedRendering()); + + if (!m_qtVideoLayer) { + m_qtVideoLayer.adoptNS([[QTMovieLayer alloc] init]); + if (!m_qtVideoLayer) + return; + + [m_qtVideoLayer.get() setMovie:m_qtMovie.get()]; +#ifndef NDEBUG + [(CALayer *)m_qtVideoLayer.get() setName:@"Video layer"]; +#endif + + // Hang the video layer from the render layer, if we have one yet. If not, we'll do this + // later via acceleratedRenderingStateChanged(). + GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); + if (videoGraphicsLayer) + videoGraphicsLayer->setContentsToVideo((PlatformLayer *)m_qtVideoLayer.get()); + } +#endif +} + +void MediaPlayerPrivate::destroyQTMovieLayer() +{ +#if USE(ACCELERATED_COMPOSITING) + if (!m_qtVideoLayer) + return; + + // disassociate our movie from our instance of QTMovieLayer + [m_qtVideoLayer.get() setMovie:nil]; + m_qtVideoLayer = nil; +#endif +} + +MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode() const +{ + if (m_qtMovieView) + return MediaRenderingMovieView; + + if (m_qtVideoLayer) + return MediaRenderingMovieLayer; + + if (m_qtVideoRenderer) + return MediaRenderingSoftwareRenderer; + + return MediaRenderingNone; +} + +MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMode() const { if (!m_player->frameView() || !m_qtMovie) + return MediaRenderingNone; + + if (m_player->inMediaDocument() || !QTVideoRendererClass()) + return MediaRenderingMovieView; + +#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 (currentMode == preferredMode && currentMode != MediaRenderingNone) return; - if (m_player->inMediaDocument() || !QTVideoRendererClass() ) + if (currentMode != MediaRenderingNone) + tearDownVideoRendering(); + + switch (preferredMode) { + case MediaRenderingMovieView: createQTMovieView(); - else - createQTVideoRenderer(); + break; + case MediaRenderingNone: + case MediaRenderingSoftwareRenderer: + createQTVideoRenderer(QTVideoRendererModeListensForNewImages); + break; + case MediaRenderingMovieLayer: + createQTMovieLayer(); + break; + } } void MediaPlayerPrivate::tearDownVideoRendering() { if (m_qtMovieView) detachQTMovieView(); - else + if (m_qtVideoRenderer) destroyQTVideoRenderer(); + if (m_qtVideoLayer) + destroyQTMovieLayer(); +} + +bool MediaPlayerPrivate::hasSetUpVideoRendering() const +{ + return m_qtMovieView + || m_qtVideoLayer + || m_qtVideoRenderer; } QTTime MediaPlayerPrivate::createQTTime(float time) const @@ -481,6 +592,10 @@ float MediaPlayerPrivate::duration() const { if (!metaDataAvailable()) return 0; + + if (m_cachedDuration != -1.0f) + return m_cachedDuration; + QTTime time = [m_qtMovie.get() duration]; if (time.flags == kQTTimeIsIndefinite) return numeric_limits<float>::infinity(); @@ -497,6 +612,10 @@ float MediaPlayerPrivate::currentTime() const void MediaPlayerPrivate::seek(float time) { + // Nothing to do if we are already in the middle of a seek to the same time. + if (time == m_seekTo) + return; + cancelSeek(); if (!metaDataAvailable()) @@ -506,7 +625,7 @@ void MediaPlayerPrivate::seek(float time) time = duration(); m_seekTo = time; - if (maxTimeLoaded() >= m_seekTo) + if (maxTimeSeekable() >= m_seekTo) doSeek(); else m_seekTimer.start(0, 0.5f); @@ -518,12 +637,16 @@ void MediaPlayerPrivate::doSeek() // setCurrentTime generates several event callbacks, update afterwards [m_objcObserver.get() setDelayCallbacks:YES]; float oldRate = [m_qtMovie.get() rate]; - [m_qtMovie.get() setRate:0]; + + if (oldRate) + [m_qtMovie.get() setRate:0]; [m_qtMovie.get() setCurrentTime:qttime]; - float timeAfterSeek = currentTime(); + // restore playback only if not at end, othewise QTMovie will loop + float timeAfterSeek = currentTime(); if (oldRate && timeAfterSeek < duration()) [m_qtMovie.get() setRate:oldRate]; + cancelSeek(); [m_objcObserver.get() setDelayCallbacks:NO]; } @@ -542,8 +665,8 @@ void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) m_player->timeChanged(); return; } - - if (maxTimeLoaded() >= m_seekTo) + + if (maxTimeSeekable() >= m_seekTo) doSeek(); else { MediaPlayer::NetworkState state = networkState(); @@ -607,8 +730,25 @@ void MediaPlayerPrivate::setRate(float rate) { if (!metaDataAvailable()) return; - if (!paused()) - [m_qtMovie.get() setRate:rate]; + [m_qtMovie.get() setRate:rate]; +} + +void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch) +{ + if (!m_qtMovie) + return; + + // QTMovieRateChangesPreservePitchAttribute cannot be changed dynamically after QTMovie creation. + // If the passed in value is different than what already exists, we need to recreate the QTMovie for it to take effect. + if ([[m_qtMovie.get() attributeForKey:QTMovieRateChangesPreservePitchAttribute] boolValue] == preservesPitch) + return; + + NSDictionary *movieAttributes = [[m_qtMovie.get() movieAttributes] mutableCopy]; + ASSERT(movieAttributes); + [movieAttributes setValue:[NSNumber numberWithBool:preservesPitch] forKey:QTMovieRateChangesPreservePitchAttribute]; + m_timeToRestore = currentTime(); + + createQTMovie([movieAttributes valueForKey:QTMovieURLAttribute], movieAttributes); } int MediaPlayerPrivate::dataRate() const @@ -621,8 +761,7 @@ int MediaPlayerPrivate::dataRate() const float MediaPlayerPrivate::maxTimeBuffered() const { - // rtsp streams are not buffered - return m_isStreaming ? 0 : maxTimeLoaded(); + return maxTimeLoaded(); } float MediaPlayerPrivate::maxTimeSeekable() const @@ -701,6 +840,11 @@ void MediaPlayerPrivate::cacheMovieScale() m_scaleFactor.setHeight(initialSize.height / naturalSize.height); } +bool MediaPlayerPrivate::isReadyForRendering() const +{ + return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); +} + void MediaPlayerPrivate::updateStates() { MediaPlayer::NetworkState oldNetworkState = m_networkState; @@ -711,19 +855,33 @@ void MediaPlayerPrivate::updateStates() if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) { disableUnsupportedTracks(); 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, likely streamed media or QuickTime VR. Tell the MediaPlayerClient + if (!m_enabledTrackCount || m_hasUnsupportedTracks) { + // This has a type of media that we do not handle directly with a <video> + // element, eg. a rtsp track or QuickTime VR. Tell the MediaPlayerClient // that we noticed. sawUnsupportedTracks(); return; } - } else if (!m_enabledTrackCount) { + } else if (!m_enabledTrackCount) loadState = QTMovieLoadStateError; - } - - if (loadState != QTMovieLoadStateError) + + if (loadState != QTMovieLoadStateError) { cacheMovieScale(); + MediaPlayer::MovieLoadType movieType = movieLoadType(); + m_isStreaming = movieType == MediaPlayer::StoredStream || movieType == MediaPlayer::LiveStream; + } + } + + // If this movie is reloading and we mean to restore the current time/rate, this might be the right time to do it. + if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::Loaded && m_timeToRestore != -1.0f) { + QTTime qttime = createQTTime(m_timeToRestore); + m_timeToRestore = -1.0f; + + // Disable event callbacks from setCurrentTime for restoring time in a recreated video + [m_objcObserver.get() setDelayCallbacks:YES]; + [m_qtMovie.get() setCurrentTime:qttime]; + [m_qtMovie.get() setRate:m_player->rate()]; + [m_objcObserver.get() setDelayCallbacks:NO]; } BOOL completelyLoaded = !m_isStreaming && (loadState >= QTMovieLoadStateComplete); @@ -751,6 +909,8 @@ void MediaPlayerPrivate::updateStates() m_readyState = MediaPlayer::HaveNothing; m_networkState = MediaPlayer::Loading; } else { + // Loading or decoding failed. + if (m_player->inMediaDocument()) { // Something went wrong in the loading of media within a standalone file. // This can occur with chained refmovies pointing to streamed media. @@ -773,23 +933,28 @@ void MediaPlayerPrivate::updateStates() } } + if (isReadyForRendering() && !hasSetUpVideoRendering()) + setUpVideoRendering(); + if (seeking()) - m_readyState = MediaPlayer::HaveNothing; + m_readyState = m_readyState >= MediaPlayer::HaveMetadata ? MediaPlayer::HaveMetadata : MediaPlayer::HaveNothing; + + // Streaming movies don't use the network when paused. + if (m_isStreaming && m_readyState >= MediaPlayer::HaveMetadata && m_networkState >= MediaPlayer::Loading && [m_qtMovie.get() rate] == 0) + m_networkState = MediaPlayer::Idle; if (m_networkState != oldNetworkState) m_player->networkStateChanged(); + if (m_readyState != oldReadyState) m_player->readyStateChanged(); - if (loadState >= QTMovieLoadStateLoaded && (!m_qtMovieView && !m_qtVideoRenderer) && m_player->visible()) - setUpVideoRendering(); - if (loadState >= QTMovieLoadStateLoaded) { float dur = duration(); - if (dur != m_duration) { - if (m_duration != -1.0f) + if (dur != m_reportedDuration) { + if (m_reportedDuration != -1.0f) m_player->durationChanged(); - m_duration = dur; + m_reportedDuration = dur; } } } @@ -820,6 +985,13 @@ void MediaPlayerPrivate::timeChanged() if (m_hasUnsupportedTracks) return; + // It may not be possible to seek to a specific time in a streamed movie. When seeking in a + // stream QuickTime sets the movie time to closest time possible and posts a timechanged + // notification. Update m_seekTo so we can detect when the seek completes. + if (m_seekTo != -1) + m_seekTo = currentTime(); + + m_timeToRestore = -1.0f; updateStates(); m_player->timeChanged(); } @@ -833,6 +1005,12 @@ void MediaPlayerPrivate::didEnd() #if DRAW_FRAME_RATE m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; #endif + + // Hang onto the current time and use it as duration from now on since QuickTime is telling us we + // are at the end. Do this because QuickTime sometimes reports one time for duration and stops + // playback at another time, which causes problems in HTMLMediaElement. + m_cachedDuration = currentTime(); + updateStates(); m_player->timeChanged(); } @@ -877,6 +1055,20 @@ void MediaPlayerPrivate::repaint() m_player->repaint(); } +void MediaPlayerPrivate::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& r) +{ + id qtVideoRenderer = m_qtVideoRenderer.get(); + if (!qtVideoRenderer && currentRenderingMode() == MediaRenderingMovieLayer) { + // We're being told to render into a context, but we already have the + // MovieLayer going. This probably means we've been called from <canvas>. + // Set up a QTVideoRenderer to use, but one that doesn't register for + // update callbacks. That way, it won't bother us asking to repaint. + createQTVideoRenderer(QTVideoRendererModeDefault); + qtVideoRenderer = m_qtVideoRenderer.get(); + } + paint(context, r); +} + void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) { if (context->paintingDisabled() || m_hasUnsupportedTracks) @@ -944,12 +1136,11 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) TextRun textRun(text.characters(), text.length()); const Color color(255, 0, 0); context->scale(FloatSize(1.0f, -1.0f)); - context->setFont(styleToUse->font()); context->setStrokeColor(color); context->setStrokeStyle(SolidStroke); context->setStrokeThickness(1.0f); context->setFillColor(color); - context->drawText(textRun, IntPoint(2, -3)); + context->drawText(styleToUse->font(), textRun, IntPoint(2, -3)); } } #endif @@ -1029,7 +1220,7 @@ MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, c // We check the "modern" type cache first, as it doesn't require QTKitServer to start. if (mimeModernTypesCache().contains(type) || mimeCommonTypesCache().contains(type)) - return (codecs && !codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported); + return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; return MediaPlayer::IsNotSupported; } @@ -1092,8 +1283,10 @@ void MediaPlayerPrivate::disableUnsupportedTracks() // Check to see if the track is disabled already, we should move along. // We don't need to re-disable it. - if (![track isEnabled]) + if (![track isEnabled]) { + --m_enabledTrackCount; continue; + } // Get the track's media type. NSString *mediaType = [track attributeForKey:QTTrackMediaTypeAttribute]; @@ -1105,6 +1298,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks() // If this track type is not allowed, then we need to disable it. [track setEnabled:NO]; --m_enabledTrackCount; + m_hasUnsupportedTracks = true; } // Disable chapter tracks. These are most likely to lead to trouble, as @@ -1137,6 +1331,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks() // Disable the evil, evil track. [chapterTrack setEnabled:NO]; --m_enabledTrackCount; + m_hasUnsupportedTracks = true; } } @@ -1146,8 +1341,50 @@ void MediaPlayerPrivate::sawUnsupportedTracks() m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); } +#if USE(ACCELERATED_COMPOSITING) +bool MediaPlayerPrivate::supportsAcceleratedRendering() const +{ + // When in the media document we render via QTMovieView, which is already accelerated. + return isReadyForRendering() && getQTMovieLayerClass() != Nil && !m_player->inMediaDocument(); } +void MediaPlayerPrivate::acceleratedRenderingStateChanged() +{ + // Set up or change the rendering path if necessary. + setUpVideoRendering(); + + if (currentRenderingMode() == MediaRenderingMovieLayer) { + GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); + if (videoGraphicsLayer) + videoGraphicsLayer->setContentsToVideo((PlatformLayer *)m_qtVideoLayer.get()); + } +} +#endif + +bool MediaPlayerPrivate::hasSingleSecurityOrigin() const +{ + // We tell quicktime to disallow resources that come from different origins + // so we know all media is single origin. + return true; +} + +MediaPlayer::MovieLoadType MediaPlayerPrivate::movieLoadType() const +{ + if (!m_qtMovie) + return MediaPlayer::Unknown; + + MediaPlayer::MovieLoadType movieType = (MediaPlayer::MovieLoadType)wkQTMovieGetType(m_qtMovie.get()); + + // Can't include WebKitSystemInterface from WebCore so we can't get the enum returned + // by wkQTMovieGetType, but at least verify that the value is in the valid range. + ASSERT(movieType >= MediaPlayer::Unknown && movieType <= MediaPlayer::LiveStream); + + return movieType; +} + + +} // namespace WebCore + @implementation WebCoreMovieObserver - (id)initWithCallback:(MediaPlayerPrivate*)callback diff --git a/WebCore/platform/graphics/mac/WebLayer.h b/WebCore/platform/graphics/mac/WebLayer.h index b8b46ed..af53ae6 100644 --- a/WebCore/platform/graphics/mac/WebLayer.h +++ b/WebCore/platform/graphics/mac/WebLayer.h @@ -42,6 +42,13 @@ namespace WebCore { @end +#if defined(BUILDING_ON_LEOPARD) +@interface CALayer(WebLayerInternal) +- (CGAffineTransform)contentsTransform; +- (void)setContentsTransform:(CGAffineTransform)t; +@end +#endif + @interface WebLayer : CALayer { WebCore::GraphicsLayer* m_layerOwner; diff --git a/WebCore/platform/graphics/mac/WebLayer.mm b/WebCore/platform/graphics/mac/WebLayer.mm index fad3916..2647466 100644 --- a/WebCore/platform/graphics/mac/WebLayer.mm +++ b/WebCore/platform/graphics/mac/WebLayer.mm @@ -40,10 +40,18 @@ using namespace WebCore; + (void)drawContents:(WebCore::GraphicsLayer*)layerContents ofLayer:(CALayer*)layer intoContext:(CGContextRef)context { - UNUSED_PARAM(layer); + if (!layerContents) + return; + CGContextSaveGState(context); - - if (layerContents && layerContents->client()) { + + CGRect layerBounds = [layer bounds]; + if (layerContents->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesBottomUp) { + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM(context, 0, -layerBounds.size.height); + } + + if (layerContents->client()) { [NSGraphicsContext saveGraphicsState]; // Set up an NSGraphicsContext for the context, so that parts of AppKit that rely on @@ -68,29 +76,24 @@ using namespace WebCore; // 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); - CGRect aBounds = [layer bounds]; - CGContextFillRect(context, aBounds); + CGContextFillRect(context, layerBounds); } #endif - CGContextRestoreGState(context); - #ifndef NDEBUG - if (layerContents && layerContents->showRepaintCounter()) { + if (layerContents->showRepaintCounter()) { bool isTiledLayer = [layer isKindOfClass:[CATiledLayer class]]; char text[16]; // that's a lot of repaints snprintf(text, sizeof(text), "%d", layerContents->incrementRepaintCount()); - CGAffineTransform a = CGContextGetCTM(context); - CGContextSaveGState(context); if (isTiledLayer) CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 0.8f); else CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); - CGRect aBounds = [layer bounds]; + CGRect aBounds = layerBounds; aBounds.size.width = 10 + 12 * strlen(text); aBounds.size.height = 25; @@ -105,6 +108,8 @@ using namespace WebCore; CGContextRestoreGState(context); } #endif + + CGContextRestoreGState(context); } // Disable default animations @@ -132,12 +137,19 @@ using namespace WebCore; - (void)setNeedsDisplayInRect:(CGRect)dirtyRect { if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) { +#if defined(BUILDING_ON_LEOPARD) + dirtyRect = CGRectApplyAffineTransform(dirtyRect, [self contentsTransform]); +#endif [super setNeedsDisplayInRect:dirtyRect]; #ifndef NDEBUG if (m_layerOwner->showRepaintCounter()) { CGRect bounds = [self bounds]; - [super setNeedsDisplayInRect:CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25)]; + CGRect indicatorRect = CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25); +#if defined(BUILDING_ON_LEOPARD) + indicatorRect = CGRectApplyAffineTransform(indicatorRect, [self contentsTransform]); +#endif + [super setNeedsDisplayInRect:indicatorRect]; } #endif } diff --git a/WebCore/platform/graphics/mac/WebTiledLayer.mm b/WebCore/platform/graphics/mac/WebTiledLayer.mm index 1dd00ba..a1f5693 100644 --- a/WebCore/platform/graphics/mac/WebTiledLayer.mm +++ b/WebCore/platform/graphics/mac/WebTiledLayer.mm @@ -74,12 +74,19 @@ using namespace WebCore; - (void)setNeedsDisplayInRect:(CGRect)dirtyRect { if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) { +#if defined(BUILDING_ON_LEOPARD) + dirtyRect = CGRectApplyAffineTransform(dirtyRect, [self contentsTransform]); +#endif [super setNeedsDisplayInRect:dirtyRect]; #ifndef NDEBUG if (m_layerOwner->showRepaintCounter()) { CGRect bounds = [self bounds]; - [super setNeedsDisplayInRect:CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25)]; + CGRect indicatorRect = CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25); +#if defined(BUILDING_ON_LEOPARD) + indicatorRect = CGRectApplyAffineTransform(indicatorRect, [self contentsTransform]); +#endif + [super setNeedsDisplayInRect:indicatorRect]; } #endif } diff --git a/WebCore/platform/graphics/qt/FontCacheQt.cpp b/WebCore/platform/graphics/qt/FontCacheQt.cpp index 668912e..5d29389 100644 --- a/WebCore/platform/graphics/qt/FontCacheQt.cpp +++ b/WebCore/platform/graphics/qt/FontCacheQt.cpp @@ -1,6 +1,8 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) Copyright (C) 2008 Holger Hans Peter Freyther + Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -26,10 +28,13 @@ #include "FontDescription.h" #include "FontPlatformData.h" #include "Font.h" +#include "PlatformString.h" #include "StringHash.h" +#include <utility> +#include <wtf/ListHashSet.h> #include <wtf/StdLibExtras.h> -#include <QHash> +using namespace WTF; namespace WebCore { @@ -47,36 +52,173 @@ void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigne { } -typedef QHash<FontDescription, FontPlatformData*> FontPlatformDataCache; +// This type must be consistent with FontPlatformData's ctor - the one which +// gets FontDescription as it's parameter. +class FontPlatformDataCacheKey { +public: + FontPlatformDataCacheKey(const FontDescription& description) + : m_familyName() + , m_bold(false) + , m_size(description.computedPixelSize()) + , m_italic(description.italic()) + , m_smallCaps(description.smallCaps()) + , m_hash(0) + { + // FIXME: Map all FontWeight values to QFont weights in FontPlatformData's ctor and follow it here + if (FontPlatformData::toQFontWeight(description.weight()) > QFont::Normal) + m_bold = true; -// using Q_GLOBAL_STATIC leads to crash. TODO investigate the way to fix this. -static FontPlatformDataCache* gFontPlatformDataCache; + const FontFamily* family = &description.family(); + while (family) { + m_familyName.append(family->family()); + family = family->next(); + if (family) + m_familyName.append(','); + } -uint qHash(const FontDescription& key) -{ - uint value = CaseFoldingHash::hash(key.family().family()); - value ^= key.computedPixelSize(); - value ^= static_cast<int>(key.weight()); - return value; -} + computeHash(); + } + + FontPlatformDataCacheKey(const FontPlatformData& fontData) + : m_familyName(static_cast<String>(fontData.family())) + , m_size(fontData.pixelSize()) + , m_bold(fontData.bold()) + , m_italic(fontData.italic()) + , m_smallCaps(fontData.smallCaps()) + , m_hash(0) + { + computeHash(); + } + + FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { } + bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); } + + enum HashTableEmptyValueType { HashTableEmptyValue }; + + FontPlatformDataCacheKey(HashTableEmptyValueType) + : m_familyName() + , m_size(0) + , m_bold(false) + , m_italic(false) + , m_smallCaps(false) + , m_hash(0) + { + } + + bool operator==(const FontPlatformDataCacheKey& other) const + { + if (m_hash != other.m_hash) + return false; + + return equalIgnoringCase(m_familyName, other.m_familyName) && m_size == other.m_size && + m_bold == other.m_bold && m_italic == other.m_italic && m_smallCaps == other.m_smallCaps; + } + + unsigned hash() const + { + return m_hash; + } + + void computeHash() + { + unsigned hashCodes[] = { + CaseFoldingHash::hash(m_familyName), + m_size | static_cast<unsigned>(m_bold << sizeof(unsigned) * 8 - 1) + | static_cast<unsigned>(m_italic) << sizeof(unsigned) *8 - 2 + | static_cast<unsigned>(m_smallCaps) << sizeof(unsigned) * 8 - 3 + }; + m_hash = StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); + } + +private: + String m_familyName; + int m_size; + bool m_bold; + bool m_italic; + bool m_smallCaps; + unsigned m_hash; + + static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } +}; + +struct FontPlatformDataCacheKeyHash { + static unsigned hash(const FontPlatformDataCacheKey& key) + { + return key.hash(); + } + + static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b) + { + return a == b; + } + + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> { + static const bool needsDestruction = true; + static const FontPlatformDataCacheKey& emptyValue() + { + DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (FontPlatformDataCacheKey::HashTableEmptyValue)); + return key; + } + static void constructDeletedValue(FontPlatformDataCacheKey& slot) + { + new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue); + } + static bool isDeletedValue(const FontPlatformDataCacheKey& value) + { + return value.isHashTableDeletedValue(); + } +}; + +typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache; + +// using Q_GLOBAL_STATIC leads to crash. TODO investigate the way to fix this. +static FontPlatformDataCache* gFontPlatformDataCache = 0; FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& description, const AtomicString& family, bool checkingAlternateName) { if (!gFontPlatformDataCache) gFontPlatformDataCache = new FontPlatformDataCache; - FontPlatformData* fontData = gFontPlatformDataCache->value(description, 0); - if (!fontData) { - fontData = new FontPlatformData(description); - gFontPlatformDataCache->insert(description, fontData); + FontPlatformDataCacheKey key(description); + FontPlatformData* platformData = gFontPlatformDataCache->get(key); + if (!platformData) { + platformData = new FontPlatformData(description); + gFontPlatformDataCache->add(key, platformData); } - - return fontData; + return platformData; } -SimpleFontData* FontCache::getCachedFontData(const FontPlatformData*) +typedef HashMap<FontPlatformDataCacheKey, std::pair<SimpleFontData*, unsigned>, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontDataCache; + +static FontDataCache* gFontDataCache = 0; + +static const int cMaxInactiveFontData = 40; +static const int cTargetInactiveFontData = 32; + +static ListHashSet<const SimpleFontData*>* gInactiveFontDataSet = 0; + +SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* fontPlatformData) { - return 0; + if (!gFontDataCache) { + gFontDataCache = new FontDataCache; + gInactiveFontDataSet = new ListHashSet<const SimpleFontData*>; + } + + FontPlatformDataCacheKey key(*fontPlatformData); + FontDataCache::iterator it = gFontDataCache->find(key); + if (it == gFontDataCache->end()) { + SimpleFontData* fontData = new SimpleFontData(*fontPlatformData); + gFontDataCache->add(key, std::pair<SimpleFontData*, unsigned>(fontData, 1)); + return fontData; + } + if (!it->second.second++) { + ASSERT(gInactiveFontDataSet->contains(it->second.first)); + gInactiveFontDataSet->remove(it->second.first); + } + return it->second.first; } FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription&) @@ -84,8 +226,52 @@ FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription&) return 0; } -void FontCache::releaseFontData(const WebCore::SimpleFontData*) +void FontCache::releaseFontData(const WebCore::SimpleFontData* fontData) { + ASSERT(gFontDataCache); + ASSERT(!fontData->isCustomFont()); + + FontPlatformDataCacheKey key(fontData->platformData()); + FontDataCache::iterator it = gFontDataCache->find(key); + ASSERT(it != gFontDataCache->end()); + if (!--it->second.second) { + gInactiveFontDataSet->add(it->second.first); + if (gInactiveFontDataSet->size() > cMaxInactiveFontData) + purgeInactiveFontData(gInactiveFontDataSet->size() - cTargetInactiveFontData); + } +} + +void FontCache::purgeInactiveFontData(int count) +{ + static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData. + if (isPurging) + return; + + isPurging = true; + + ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontDataSet->begin(); + ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontDataSet->end(); + for (int i = 0; i < count && it != end; ++i, ++it) { + FontPlatformDataCacheKey key = (*it)->platformData(); + pair<SimpleFontData*, unsigned> fontDataPair = gFontDataCache->take(key); + ASSERT(fontDataPair.first != 0); + ASSERT(!fontDataPair.second); + delete fontDataPair.first; + + FontPlatformData* platformData = gFontPlatformDataCache->take(key); + if (platformData) + delete platformData; + } + + if (it == end) { + // Removed everything + gInactiveFontDataSet->clear(); + } else { + for (int i = 0; i < count; ++i) + gInactiveFontDataSet->remove(gInactiveFontDataSet->begin()); + } + + isPurging = false; } void FontCache::addClient(FontSelector*) @@ -98,10 +284,10 @@ void FontCache::removeClient(FontSelector*) void FontCache::invalidate() { - if (!gFontPlatformDataCache) + if (!gFontPlatformDataCache || !gFontDataCache) return; - gFontPlatformDataCache->clear(); + purgeInactiveFontData(); } } // namespace WebCore diff --git a/WebCore/platform/graphics/qt/FontFallbackListQt.cpp b/WebCore/platform/graphics/qt/FontFallbackListQt.cpp index 50627b7..c29fd56 100644 --- a/WebCore/platform/graphics/qt/FontFallbackListQt.cpp +++ b/WebCore/platform/graphics/qt/FontFallbackListQt.cpp @@ -25,6 +25,7 @@ #include "FontFallbackList.h" #include "Font.h" +#include "FontCache.h" #include "SegmentedFontData.h" #include <QDebug> @@ -32,16 +33,23 @@ namespace WebCore { FontFallbackList::FontFallbackList() - : m_familyIndex(0) + : m_pageZero(0) + , m_cachedPrimarySimpleFontData(0) + , m_fontSelector(0) + , m_familyIndex(0) , m_pitch(UnknownPitch) , m_loadingCustomFonts(false) - , m_fontSelector(0) , m_generation(0) { } void FontFallbackList::invalidate(WTF::PassRefPtr<WebCore::FontSelector> fontSelector) { + releaseFontData(); + m_fontList.clear(); + m_pageZero = 0; + m_pages.clear(); + m_cachedPrimarySimpleFontData = 0; m_familyIndex = 0; m_pitch = UnknownPitch; m_loadingCustomFonts = false; @@ -51,14 +59,20 @@ void FontFallbackList::invalidate(WTF::PassRefPtr<WebCore::FontSelector> fontSel void FontFallbackList::releaseFontData() { - if (m_fontList.size()) - delete m_fontList[0].first; - m_fontList.clear(); + unsigned numFonts = m_fontList.size(); + for (unsigned i = 0; i < numFonts; ++i) { + if (m_fontList[i].second) + delete m_fontList[i].first; + else { + ASSERT(!m_fontList[i].first->isSegmented()); + fontCache()->releaseFontData(static_cast<const SimpleFontData*>(m_fontList[i].first)); + } + } } void FontFallbackList::determinePitch(const WebCore::Font* font) const { - const FontData* fontData = primaryFont(font); + const FontData* fontData = primaryFontData(font); if (!fontData->isSegmented()) m_pitch = static_cast<const SimpleFontData*>(fontData)->pitch(); else { @@ -76,6 +90,14 @@ const FontData* FontFallbackList::fontDataAt(const WebCore::Font* _font, unsigne if (index != 0) return 0; + // Search for the WebCore font that is already in the list + for (int i = m_fontList.size() - 1; i >= 0; --i) { + pair<const FontData*, bool> item = m_fontList[i]; + // item.second means that the item was created locally or not + if (!item.second) + return item.first; + } + // Use the FontSelector to get a WebCore font and then fallback to Qt const FontDescription& description = _font->fontDescription(); const FontFamily* family = &description.family(); @@ -85,6 +107,10 @@ const FontData* FontFallbackList::fontDataAt(const WebCore::Font* _font, unsigne if (data) { if (data->isLoading()) m_loadingCustomFonts = true; + if (!data->isCustomFont()) { + // Custom fonts can be freed anytime so we must not hold them + m_fontList.append(pair<const FontData*, bool>(data, false)); + } return data; } } @@ -94,14 +120,14 @@ const FontData* FontFallbackList::fontDataAt(const WebCore::Font* _font, unsigne if (m_fontList.size()) return m_fontList[0].first; - const FontData* result = new SimpleFontData(FontPlatformData(description), _font->wordSpacing(), _font->letterSpacing()); - m_fontList.append(pair<const FontData*, bool>(result, result->isCustomFont())); + const FontData* result = new SimpleFontData(FontPlatformData(description, _font->wordSpacing(), _font->letterSpacing()), true); + m_fontList.append(pair<const FontData*, bool>(result, true)); return result; } const FontData* FontFallbackList::fontDataForCharacters(const WebCore::Font* font, const UChar*, int) const { - return primaryFont(font); + return primaryFontData(font); } void FontFallbackList::setPlatformFont(const WebCore::FontPlatformData& platformData) diff --git a/WebCore/platform/graphics/qt/FontPlatformData.h b/WebCore/platform/graphics/qt/FontPlatformData.h index 5e97678..92219fd 100644 --- a/WebCore/platform/graphics/qt/FontPlatformData.h +++ b/WebCore/platform/graphics/qt/FontPlatformData.h @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) Copyright (C) 2008 Holger Hans Peter Freyther + Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -24,11 +25,12 @@ #define FontPlatformData_h #include "FontDescription.h" - #include <QFont> namespace WebCore { +class String; + class FontPlatformData { public: @@ -39,8 +41,38 @@ public: FontPlatformData(const FontDescription&, int wordSpacing = 0, int letterSpacing = 0); FontPlatformData(const QFont&, bool bold); + static inline QFont::Weight toQFontWeight(FontWeight fontWeight) + { + switch (fontWeight) { + case FontWeight100: + case FontWeight200: + return QFont::Light; // QFont::Light == Weight of 25 + case FontWeight600: + return QFont::DemiBold; // QFont::DemiBold == Weight of 63 + case FontWeight700: + case FontWeight800: + return QFont::Bold; // QFont::Bold == Weight of 75 + case FontWeight900: + return QFont::Black; // QFont::Black == Weight of 87 + case FontWeight300: + case FontWeight400: + case FontWeight500: + default: + return QFont::Normal; // QFont::Normal == Weight of 50 + } + } + QFont font() const { return m_font; } float size() const { return m_size; } + QString family() const { return m_font.family(); } + bool bold() const { return m_bold; } + bool italic() const { return m_font.italic(); } + bool smallCaps() const { return m_font.capitalization() == QFont::SmallCaps; } + int pixelSize() const { return m_font.pixelSize(); } + +#ifndef NDEBUG + String description() const; +#endif float m_size; bool m_bold; diff --git a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp index f0dd3ea..7709be6 100644 --- a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp +++ b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Holger Hans Peter Freyther + Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -21,6 +22,8 @@ #include "config.h" #include "FontPlatformData.h" +#include "PlatformString.h" + namespace WebCore { FontPlatformData::FontPlatformData(const FontDescription& description, int wordSpacing, int letterSpacing) @@ -40,11 +43,9 @@ FontPlatformData::FontPlatformData(const FontDescription& description, int wordS m_font.setFamily(familyName); m_font.setPixelSize(qRound(description.computedSize())); m_font.setItalic(description.italic()); - // FIXME: Map all FontWeight values to QFont weights. - if (description.weight() >= FontWeight600) - m_font.setWeight(QFont::Bold); - else - m_font.setWeight(QFont::Normal); + + m_font.setWeight(toQFontWeight(description.weight())); + m_bold = m_font.bold(); bool smallCaps = description.smallCaps(); m_font.setCapitalization(smallCaps ? QFont::SmallCaps : QFont::MixedCase); @@ -77,4 +78,11 @@ FontPlatformData::FontPlatformData() { } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + } diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp index 5a4b7b2..e8eb923 100644 --- a/WebCore/platform/graphics/qt/FontQt.cpp +++ b/WebCore/platform/graphics/qt/FontQt.cpp @@ -47,8 +47,8 @@ namespace WebCore { static const QString qstring(const TextRun& run) { - //We don't detach - return QString::fromRawData((const QChar *)run.characters(), run.length()); + // We don't detach + return QString::fromRawData(reinterpret_cast<const QChar*>(run.characters()), run.length()); } static const QString fixSpacing(const QString &string) @@ -57,11 +57,10 @@ static const QString fixSpacing(const QString &string) QString possiblyDetached = string; for (int i = 0; i < string.length(); ++i) { const QChar c = string.at(i); - if (c.unicode() != 0x20 && Font::treatAsSpace(c.unicode())) { - possiblyDetached[i] = 0x20; //detach - } else if (c.unicode() != 0x200c && Font::treatAsZeroWidthSpace(c.unicode())) { - possiblyDetached[i] = 0x200c; //detach - } + if (c.unicode() != 0x20 && Font::treatAsSpace(c.unicode())) + possiblyDetached[i] = 0x20; // detach + else if (c.unicode() != 0x200c && Font::treatAsZeroWidthSpace(c.unicode())) + possiblyDetached[i] = 0x200c; // detach } return possiblyDetached; } @@ -222,7 +221,10 @@ FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& QFont Font::font() const { - return primaryFont()->getQtFont(); + QFont f = primaryFont()->getQtFont(); + f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing); + f.setWordSpacing(m_wordSpacing); + return f; } } diff --git a/WebCore/platform/graphics/qt/FontQt43.cpp b/WebCore/platform/graphics/qt/FontQt43.cpp index 137b7c9..45bf05d 100644 --- a/WebCore/platform/graphics/qt/FontQt43.cpp +++ b/WebCore/platform/graphics/qt/FontQt43.cpp @@ -105,9 +105,9 @@ static int generateComponents(Vector<TextRunComponent, 1024>* components, const offset += add + letterSpacing + components->last().width; start = 1; // qDebug() << "space at 0" << offset; - } else if (smallCaps) { + } else if (smallCaps) f = (QChar::category(run[0]) == QChar::Letter_Lowercase ? &font.scFont() : &font.font()); - } + for (int i = 1; i < run.length(); ++i) { uint ch = run[i]; if (QChar(ch).isHighSurrogate() && QChar(run[i-1]).isLowSurrogate()) @@ -263,7 +263,7 @@ int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool if (!l.isValid()) return offset; - l.setLineWidth(INT_MAX/256); + l.setLineWidth(INT_MAX / 256); layout.endLayout(); if (position - xs >= l.width()) @@ -272,9 +272,8 @@ int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool if (cursor > 1) --cursor; return offset + cursor; - } else { + } else offset += components.at(i).string.length() - 1; - } } } else { for (int i = 0; i < components.size(); ++i) { @@ -287,7 +286,7 @@ int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool if (!l.isValid()) return offset; - l.setLineWidth(INT_MAX/256); + l.setLineWidth(INT_MAX / 256); layout.endLayout(); if (position - xs >= l.width()) @@ -296,9 +295,8 @@ int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool if (cursor > 1) --cursor; return offset + cursor; - } else { + } else offset += components.at(i).string.length() - 1; - } } } return run.length(); @@ -321,7 +319,7 @@ static float cursorToX(const Vector<TextRunComponent, 1024>& components, int wid if (!l.isValid()) return 0; - l.setLineWidth(INT_MAX/256); + l.setLineWidth(INT_MAX / 256); layout.endLayout(); return xs + l.cursorToX(cursor - start + 1); diff --git a/WebCore/platform/graphics/qt/GradientQt.cpp b/WebCore/platform/graphics/qt/GradientQt.cpp index 1e71f58..9b9acc2 100644 --- a/WebCore/platform/graphics/qt/GradientQt.cpp +++ b/WebCore/platform/graphics/qt/GradientQt.cpp @@ -67,7 +67,7 @@ QGradient* Gradient::platformGradient() ++stopIterator; } - switch(m_spreadMethod) { + switch (m_spreadMethod) { case SpreadMethodPad: m_gradient->setSpread(QGradient::PadSpread); break; diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index ed7ac47..e259a4e 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -95,7 +95,8 @@ static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op case CompositeXOR: return QPainter::CompositionMode_Xor; case CompositePlusDarker: - return QPainter::CompositionMode_SourceOver; + // there is no exact match, but this is the closest + return QPainter::CompositionMode_Darken; case CompositeHighlight: return QPainter::CompositionMode_SourceOver; case CompositePlusLighter: @@ -155,7 +156,7 @@ static Qt::PenStyle toQPenStyle(StrokeStyle style) static inline Qt::FillRule toQtFillRule(WindRule rule) { - switch(rule) { + switch (rule) { case RULE_EVENODD: return Qt::OddEvenFill; case RULE_NONZERO: @@ -165,8 +166,7 @@ static inline Qt::FillRule toQtFillRule(WindRule rule) return Qt::OddEvenFill; } -struct TransparencyLayer -{ +struct TransparencyLayer { TransparencyLayer(const QPainter* p, const QRect &rect) : pixmap(rect.width(), rect.height()) { @@ -198,8 +198,7 @@ private: TransparencyLayer & operator=(const TransparencyLayer &) { return *this; } }; -class GraphicsContextPlatformPrivate -{ +class GraphicsContextPlatformPrivate { public: GraphicsContextPlatformPrivate(QPainter* painter); ~GraphicsContextPlatformPrivate(); @@ -217,7 +216,7 @@ public: bool antiAliasingForRectsAndLines; - QStack<TransparencyLayer *> layers; + QStack<TransparencyLayer*> layers; QPainter* redirect; QBrush solidColor; @@ -242,9 +241,8 @@ GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p) antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing); // FIXME: Maybe only enable in SVG mode? painter->setRenderHint(QPainter::Antialiasing, true); - } else { + } else antiAliasingForRectsAndLines = false; - } } GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate() @@ -265,7 +263,7 @@ GraphicsContext::GraphicsContext(PlatformGraphicsContext* context) GraphicsContext::~GraphicsContext() { - while(!m_data->layers.isEmpty()) + while (!m_data->layers.isEmpty()) endTransparencyLayer(); destroyGraphicsContextPrivate(m_common); @@ -280,7 +278,7 @@ PlatformGraphicsContext* GraphicsContext::platformContext() const TransformationMatrix GraphicsContext::getCTM() const { QTransform matrix(platformContext()->combinedTransform()); - return TransformationMatrix(matrix.m11(), matrix.m12(), 0, matrix.m13(), + return TransformationMatrix(matrix.m11(), matrix.m12(), 0, matrix.m13(), matrix.m21(), matrix.m22(), 0, matrix.m23(), 0, 0, 1, 0, matrix.m31(), matrix.m32(), 0, matrix.m33()); @@ -763,7 +761,6 @@ void GraphicsContext::clipPath(WindRule clipRule) * RenderTheme handles drawing focus on widgets which * need it. */ -Color focusRingColor() { return Color(0, 0, 0); } void GraphicsContext::drawFocusRing(const Color& color) { if (paintingDisabled()) @@ -1127,8 +1124,9 @@ void GraphicsContext::concatCTM(const TransformationMatrix& transform) m_data->p()->setWorldTransform(transform, true); - // Transformations to the context shouldn't transform the currentPath. - // We have to undo every change made to the context from the currentPath to avoid wrong drawings. + // Transformations to the context shouldn't transform the currentPath. + // We have to undo every change made to the context from the currentPath + // to avoid wrong drawings. if (!m_data->currentPath.isEmpty() && transform.isInvertible()) { QTransform matrix = transform.inverse(); m_data->currentPath = m_data->currentPath * matrix; @@ -1211,7 +1209,7 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha bitmapInfo.bmiHeader.biClrImportant = 0; void* pixels = 0; - HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); + HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); if (!bitmap) return 0; @@ -1229,7 +1227,7 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha memset(bmpInfo.bmBits, 0, bufferSize); } -#if !PLATFORM(WIN_CE) +#if !PLATFORM(WINCE) // Make sure we can do world transforms. SetGraphicsMode(bitmapDC, GM_ADVANCED); diff --git a/WebCore/platform/graphics/qt/IconQt.cpp b/WebCore/platform/graphics/qt/IconQt.cpp index c9f3ced..34c3c47 100644 --- a/WebCore/platform/graphics/qt/IconQt.cpp +++ b/WebCore/platform/graphics/qt/IconQt.cpp @@ -39,7 +39,7 @@ Icon::Icon() Icon::~Icon() { } - + PassRefPtr<Icon> Icon::createIconForFile(const String& filename) { RefPtr<Icon> i = adoptRef(new Icon); @@ -57,9 +57,8 @@ void Icon::paint(GraphicsContext* ctx, const IntRect& rect) { QPixmap px = m_icon.pixmap(rect.size()); QPainter *p = static_cast<QPainter*>(ctx->platformContext()); - if (p && !px.isNull()) { + if (p && !px.isNull()) p->drawPixmap(rect.x(), rect.y(), px); - } } } diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index 506a8ea..22a5a43 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -40,6 +40,7 @@ #include <QImageWriter> #include <QPainter> #include <QPixmap> +#include <math.h> namespace WebCore { @@ -67,7 +68,7 @@ ImageBufferData::ImageBufferData(const IntSize& size) painter->setCompositionMode(QPainter::CompositionMode_SourceOver); } -ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success) : m_data(size) , m_size(size) { @@ -98,6 +99,32 @@ Image* ImageBuffer::image() const return m_image.get(); } +void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) +{ + bool isPainting = m_data.m_painter->isActive(); + if (isPainting) + m_data.m_painter->end(); + + QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + ASSERT(!image.isNull()); + + for (int y = 0; y < m_size.height(); ++y) { + for (int x = 0; x < m_size.width(); x++) { + QRgb value = image.pixel(x, y); + value = qRgba(lookUpTable[qRed(value)], + lookUpTable[qGreen(value)], + lookUpTable[qBlue(value)], + lookUpTable[qAlpha(value)]); + image.setPixel(x, y, value); + } + } + + m_data.m_pixmap = QPixmap::fromImage(image); + + if (isPainting) + m_data.m_painter->begin(&m_data.m_pixmap); +} + PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const { PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp index cd32428..7bbdcc0 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -118,7 +118,7 @@ ImageDecoderQt::ReadContext::ReadResult // Attempt to construct an empty image of the matching size and format // for efficient reading QImage newImage = m_dataFormat != QImage::Format_Invalid ? - QImage(m_size,m_dataFormat) : QImage(); + QImage(m_size, m_dataFormat) : QImage(); m_target.push_back(ImageData(newImage)); } @@ -137,8 +137,8 @@ ImageDecoderQt::ReadContext::ReadResult const bool supportsAnimation = m_reader.supportsAnimation(); if (debugImageDecoderQt) - qDebug() << "readImage(): #" << m_target.size() << " complete, " << m_size << " format " << m_dataFormat - << " supportsAnimation=" << supportsAnimation ; + qDebug() << "readImage(): #" << m_target.size() << " complete, " << m_size + << " format " << m_dataFormat << " supportsAnimation=" << supportsAnimation; // No point in readinfg further if (!supportsAnimation) return ReadComplete; @@ -158,7 +158,7 @@ ImageDecoderQt::ReadContext::IncrementalReadResult // set state to reflect complete header, etc. // For now, we read the whole image. - const qint64 startPos = m_buffer.pos (); + const qint64 startPos = m_buffer.pos(); // Oops, failed. Rewind. if (!m_reader.read(&imageData.m_image)) { m_buffer.seek(startPos); @@ -217,9 +217,7 @@ void ImageDecoderQt::reset() m_failed = false; m_imageList.clear(); m_pixmapCache.clear(); - m_sizeAvailable = false; m_loopCount = cAnimationNone; - m_size = IntSize(-1, -1); } void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived) @@ -238,7 +236,7 @@ void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived) if (debugImageDecoderQt) qDebug() << " read returns " << readResult; - switch ( readResult) { + switch (readResult) { case ReadContext::ReadFailed: m_failed = true; break; @@ -247,8 +245,8 @@ void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived) case ReadContext::ReadComplete: // Did we read anything - try to set the size. if (hasFirstImageHeader()) { - m_sizeAvailable = true; - m_size = m_imageList[0].m_image.size(); + QSize imgSize = m_imageList[0].m_image.size(); + setSize(imgSize.width(), imgSize.height()); if (readContext.reader()->supportsAnimation()) { if (readContext.reader()->loopCount() != -1) @@ -262,14 +260,14 @@ void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived) } -bool ImageDecoderQt::isSizeAvailable() const +bool ImageDecoderQt::isSizeAvailable() { if (debugImageDecoderQt) - qDebug() << " ImageDecoderQt::isSizeAvailable() returns" << m_sizeAvailable; - return m_sizeAvailable; + qDebug() << " ImageDecoderQt::isSizeAvailable() returns" << ImageDecoder::isSizeAvailable(); + return ImageDecoder::isSizeAvailable(); } -int ImageDecoderQt::frameCount() const +size_t ImageDecoderQt::frameCount() const { if (debugImageDecoderQt) qDebug() << " ImageDecoderQt::frameCount() returns" << m_imageList.size(); diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.h b/WebCore/platform/graphics/qt/ImageDecoderQt.h index b8c3edd..fc52479 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.h +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.h @@ -45,8 +45,8 @@ public: typedef Vector<char> IncomingData; virtual void setData(const IncomingData& data, bool allDataReceived); - virtual bool isSizeAvailable() const; - virtual int frameCount() const; + virtual bool isSizeAvailable(); + virtual size_t frameCount() const; virtual int repetitionCount() const; virtual RGBA32Buffer* frameBufferAtIndex(size_t index); diff --git a/WebCore/platform/graphics/qt/ImageQt.cpp b/WebCore/platform/graphics/qt/ImageQt.cpp index a2e96f3..5d40e26 100644 --- a/WebCore/platform/graphics/qt/ImageQt.cpp +++ b/WebCore/platform/graphics/qt/ImageQt.cpp @@ -83,7 +83,6 @@ bool FrameData::clear(bool clearMetadata) } - // ================================================ // Image Class // ================================================ @@ -93,7 +92,6 @@ PassRefPtr<Image> Image::loadPlatformResource(const char* name) return StillImage::create(loadResourcePixmap(name)); } - void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { @@ -103,9 +101,8 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const QPixmap pixmap = *framePixmap; QRect tr = QRectF(tileRect).toRect(); - if (tr.x() || tr.y() || tr.width() != pixmap.width() || tr.height() != pixmap.height()) { + if (tr.x() || tr.y() || tr.width() != pixmap.width() || tr.height() != pixmap.height()) pixmap = pixmap.copy(tr); - } QBrush b(pixmap); b.setTransform(patternTransform); @@ -129,7 +126,7 @@ void BitmapImage::initPlatformData() void BitmapImage::invalidatePlatformData() { } - + // Drawing Routines void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatRect& src, CompositeOperator op) @@ -139,7 +136,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, QPixmap* image = nativeImageForCurrentFrame(); if (!image) return; - + if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, dst, solidColor(), op); return; @@ -158,7 +155,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, painter->setCompositionMode(QPainter::CompositionMode_Source); // Test using example site at - // http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html + // http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html painter->drawPixmap(dst, *image, src); ctxt->restore(); diff --git a/WebCore/platform/graphics/qt/ImageSourceQt.cpp b/WebCore/platform/graphics/qt/ImageSourceQt.cpp index 621728e..8ae449c 100644 --- a/WebCore/platform/graphics/qt/ImageSourceQt.cpp +++ b/WebCore/platform/graphics/qt/ImageSourceQt.cpp @@ -91,9 +91,12 @@ IntSize ImageSource::size() const return m_decoder->size(); } -IntSize ImageSource::frameSizeAtIndex(size_t) const +IntSize ImageSource::frameSizeAtIndex(size_t index) const { - return size(); + if (!m_decoder) + return IntSize(); + + return m_decoder->frameSizeAtIndex(index); } int ImageSource::repetitionCount() @@ -124,7 +127,7 @@ float ImageSource::frameDurationAtIndex(size_t index) { if (!m_decoder) return 0; - + // Many annoying ads specify a 0 duration to make an image flash as quickly // as possible. We follow WinIE's behavior and use a duration of 100 ms // for any frames that specify a duration of <= 50 ms. See @@ -138,17 +141,17 @@ bool ImageSource::frameHasAlphaAtIndex(size_t index) { if (!m_decoder || !m_decoder->supportsAlpha()) return false; - - const QPixmap* source = m_decoder->imageAtIndex( index); + + const QPixmap* source = m_decoder->imageAtIndex(index); if (!source) return false; - + return source->hasAlphaChannel(); } bool ImageSource::frameIsCompleteAtIndex(size_t index) { - return (m_decoder && m_decoder->imageAtIndex(index) != 0); + return (m_decoder && m_decoder->imageAtIndex(index)); } void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp index c80d73b..76b1494 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -36,11 +36,14 @@ #include <QMetaEnum> #include <QUrl> #include <QEvent> -#include <phonon> + +#include <Phonon/AudioOutput> +#include <Phonon/MediaObject> +#include <Phonon/VideoWidget> using namespace Phonon; -#define LOG_MEDIAOBJECT() (LOG(Media,"%s", debugMediaObject(this, *m_mediaObject).constData())) +#define LOG_MEDIAOBJECT() (LOG(Media, "%s", debugMediaObject(this, *m_mediaObject).constData())) static QByteArray debugMediaObject(WebCore::MediaPlayerPrivate* mediaPlayer, const MediaObject& mediaObject) { @@ -94,9 +97,8 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) // Make sure we get updates for each frame m_videoWidget->installEventFilter(this); - foreach(QWidget* widget, qFindChildren<QWidget*>(m_videoWidget)) { + foreach (QWidget* widget, qFindChildren<QWidget*>(m_videoWidget)) widget->installEventFilter(this); - } connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged(Phonon::State, Phonon::State))); @@ -111,8 +113,8 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) connect(m_mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); } -MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) -{ +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ return new MediaPlayerPrivate(player); } @@ -263,7 +265,7 @@ float MediaPlayerPrivate::maxTimeSeekable() const } unsigned MediaPlayerPrivate::bytesLoaded() const -{ +{ notImplemented(); return 0; } @@ -343,9 +345,8 @@ void MediaPlayerPrivate::updateStates() m_networkState = MediaPlayer::NetworkError; m_readyState = MediaPlayer::HaveNothing; cancelLoad(); - } else { + } else m_mediaObject->pause(); - } } if (seeking()) diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index 7569031..e5cecc8 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -92,7 +92,7 @@ bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) // FIXME: We should try to use a 'shared Context' instead of creating a new ImageBuffer // on each call. - OwnPtr<ImageBuffer> scratchImage = ImageBuffer::create(IntSize(1, 1), false); + OwnPtr<ImageBuffer> scratchImage = ImageBuffer::create(IntSize(1, 1)); GraphicsContext* gc = scratchImage->context(); QPainterPathStroker stroke; applier->strokeStyle(gc); @@ -124,7 +124,7 @@ FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) { // FIXME: We should try to use a 'shared Context' instead of creating a new ImageBuffer // on each call. - OwnPtr<ImageBuffer> scratchImage = ImageBuffer::create(IntSize(1, 1), false); + OwnPtr<ImageBuffer> scratchImage = ImageBuffer::create(IntSize(1, 1)); GraphicsContext* gc = scratchImage->context(); QPainterPathStroker stroke; if (applier) { @@ -170,8 +170,8 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) return; } - FloatPoint p1p0((p0.x() - p1.x()),(p0.y() - p1.y())); - FloatPoint p1p2((p2.x() - p1.x()),(p2.y() - p1.y())); + FloatPoint p1p0((p0.x() - p1.x()), (p0.y() - p1.y())); + FloatPoint p1p2((p2.x() - p1.x()), (p2.y() - p1.y())); float p1p0_length = sqrtf(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); float p1p2_length = sqrtf(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); @@ -216,7 +216,7 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) float factor_p1p2 = tangent / p1p2_length; FloatPoint t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); - FloatPoint orth_p1p2((t_p1p2.x() - p.x()),(t_p1p2.y() - p.y())); + FloatPoint orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y())); float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); float ea = acos(orth_p1p2.x() / orth_p1p2_length); if (orth_p1p2.y() < 0) @@ -298,7 +298,14 @@ void Path::clear() bool Path::isEmpty() const { - return m_path->isEmpty(); + // Don't use QPainterPath::isEmpty(), as that also returns true if there's only + // one initial MoveTo element in the path. + return !m_path->elementCount(); +} + +bool Path::hasCurrentPoint() const +{ + return !isEmpty(); } String Path::debugString() const diff --git a/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp b/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp index 15f0cc5..37b86f3 100644 --- a/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp +++ b/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp @@ -32,7 +32,7 @@ namespace WebCore { TransformationMatrix::operator QTransform() const -{ +{ return QTransform(m11(), m12(), m14(), m21(), m22(), m24(), m41(), m42(), m44()); } diff --git a/WebCore/platform/graphics/skia/GradientSkia.cpp b/WebCore/platform/graphics/skia/GradientSkia.cpp index ac7366c..3bdddb2 100644 --- a/WebCore/platform/graphics/skia/GradientSkia.cpp +++ b/WebCore/platform/graphics/skia/GradientSkia.cpp @@ -158,15 +158,21 @@ SkShader* Gradient::platformGradient() // circle" (m_p1/m_r1). // See http://webkit.org/blog/175/introducing-css-gradients/ for a // description of the expected behavior. + + // The radius we give to Skia must be positive (and non-zero). If + // we're given a zero radius, just ask for a very small radius so + // Skia will still return an object. + SkScalar radius = m_r1 > 0 ? WebCoreFloatToSkScalar(m_r1) : SK_ScalarMin; m_gradient = SkGradientShader::CreateRadial(m_p1, - WebCoreFloatToSkScalar(m_r1), colors, pos, - static_cast<int>(countUsed), tile); + radius, colors, pos, static_cast<int>(countUsed), tile); } else { SkPoint pts[2] = { m_p0, m_p1 }; m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, static_cast<int>(countUsed), tile); } + ASSERT(m_gradient); + SkMatrix matrix = m_gradientSpaceTransformation; m_gradient->setLocalMatrix(matrix); @@ -179,4 +185,10 @@ void Gradient::fill(GraphicsContext* context, const FloatRect& rect) context->fillRect(rect); } +void Gradient::setPlatformGradientSpaceTransform(const TransformationMatrix& matrix) +{ + if (m_gradient) + m_gradient->setLocalMatrix(m_gradientSpaceTransformation); +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h b/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h index 29738f4..5e12ad6 100644 --- a/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h +++ b/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h @@ -38,7 +38,7 @@ class PlatformContextSkia; namespace WebCore { // This class just holds onto a PlatformContextSkia for GraphicsContext. -class GraphicsContextPlatformPrivate : Noncopyable { +class GraphicsContextPlatformPrivate : public Noncopyable { public: GraphicsContextPlatformPrivate(PlatformContextSkia* platformContext) : m_context(platformContext) { } diff --git a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp index 33ca23a..bbb42c9 100644 --- a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp +++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -331,7 +331,7 @@ void GraphicsContext::clearRect(const FloatRect& rect) SkPaint paint; platformContext()->setupPaintForFilling(&paint); - paint.setPorterDuffXfermode(SkPorterDuff::kClear_Mode); + paint.setXfermodeMode(SkXfermode::kClear_Mode); platformContext()->canvas()->drawRect(r, paint); } @@ -503,7 +503,7 @@ void GraphicsContext::drawFocusRing(const Color& color) paint.setAntiAlias(true); paint.setStyle(SkPaint::kStroke_Style); - paint.setColor(focusRingColor().rgb()); + paint.setColor(color.rgb()); paint.setStrokeWidth(focusRingOutset * 2); paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref(); focusRingRegion.getBoundaryPath(&path); @@ -530,16 +530,48 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) // probably worth the speed up of no square root, which also won't be exact. SkPoint disp = pts[1] - pts[0]; int length = SkScalarRound(disp.fX + disp.fY); - int width = roundf( - platformContext()->setupPaintForStroking(&paint, 0, length)); + platformContext()->setupPaintForStroking(&paint, 0, length); + int width = roundf(strokeThickness()); + bool isVerticalLine = pts[0].fX == pts[1].fX; + + if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { + // Do a rect fill of our endpoints. This ensures we always have the + // appearance of being a border. We then draw the actual dotted/dashed line. + + SkRect r1, r2; + r1.set(pts[0].fX, pts[0].fY, pts[0].fX + width, pts[0].fY + width); + r2.set(pts[1].fX, pts[1].fY, pts[1].fX + width, pts[1].fY + width); + + if (isVerticalLine) { + r1.offset(-width / 2, 0); + r2.offset(-width / 2, -width); + } else { + r1.offset(0, -width / 2); + r2.offset(-width, -width / 2); + } + SkPaint fillPaint; + fillPaint.setColor(paint.getColor()); + platformContext()->canvas()->drawRect(r1, fillPaint); + platformContext()->canvas()->drawRect(r2, fillPaint); + + // Since we've already rendered the endcaps, adjust the endpoints to + // exclude them from the line itself. + if (isVerticalLine) { + pts[0].fY += width; + pts[1].fY -= width; + } else { + pts[0].fX += width; + pts[1].fX -= width; + } + } // "Borrowed" this comment and idea from GraphicsContextCG.cpp + // // For odd widths, we add in 0.5 to the appropriate x/y so that the float // arithmetic works out. For example, with a border width of 3, KHTML will // pass us (y1+y2)/2, e.g., (50+53)/2 = 103/2 = 51 when we want 51.5. It is // always true that an even width gave us a perfect position, but an odd // width gave us a position that is off by exactly 0.5. - bool isVerticalLine = pts[0].fX == pts[1].fX; if (width & 1) { // Odd. if (isVerticalLine) { @@ -687,13 +719,6 @@ void GraphicsContext::fillPath() SkPaint paint; platformContext()->setupPaintForFilling(&paint); - if (colorSpace == PatternColorSpace) { - SkShader* pat = state.fillPattern->createPlatformPattern(getCTM()); - paint.setShader(pat); - pat->unref(); - } else if (colorSpace == GradientColorSpace) - paint.setShader(state.fillGradient->platformGradient()); - platformContext()->canvas()->drawPath(path, paint); } @@ -713,14 +738,6 @@ void GraphicsContext::fillRect(const FloatRect& rect) SkPaint paint; platformContext()->setupPaintForFilling(&paint); - - if (colorSpace == PatternColorSpace) { - SkShader* pat = state.fillPattern->createPlatformPattern(getCTM()); - paint.setShader(pat); - pat->unref(); - } else if (colorSpace == GradientColorSpace) - paint.setShader(state.fillGradient->platformGradient()); - platformContext()->canvas()->drawRect(r, paint); } @@ -858,7 +875,7 @@ void GraphicsContext::setCompositeOperation(CompositeOperator op) { if (paintingDisabled()) return; - platformContext()->setPorterDuffMode(WebCoreCompositeToSkiaComposite(op)); + platformContext()->setXfermodeMode(WebCoreCompositeToSkiaComposite(op)); } void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) @@ -947,6 +964,24 @@ void GraphicsContext::setPlatformFillColor(const Color& color) platformContext()->setFillColor(color.rgb()); } +void GraphicsContext::setPlatformFillGradient(Gradient* gradient) +{ + if (paintingDisabled()) + return; + + platformContext()->setFillShader(gradient->platformGradient()); +} + +void GraphicsContext::setPlatformFillPattern(Pattern* pattern) +{ + if (paintingDisabled()) + return; + + SkShader* pat = pattern->createPlatformPattern(getCTM()); + platformContext()->setFillShader(pat); + pat->safeUnref(); +} + void GraphicsContext::setPlatformShadow(const IntSize& size, int blurInt, const Color& color) @@ -1015,6 +1050,24 @@ void GraphicsContext::setPlatformStrokeThickness(float thickness) platformContext()->setStrokeThickness(thickness); } +void GraphicsContext::setPlatformStrokeGradient(Gradient* gradient) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeShader(gradient->platformGradient()); +} + +void GraphicsContext::setPlatformStrokePattern(Pattern* pattern) +{ + if (paintingDisabled()) + return; + + SkShader* pat = pattern->createPlatformPattern(getCTM()); + platformContext()->setStrokeShader(pat); + pat->safeUnref(); +} + void GraphicsContext::setPlatformTextDrawingMode(int mode) { if (paintingDisabled()) @@ -1077,13 +1130,6 @@ void GraphicsContext::strokePath() SkPaint paint; platformContext()->setupPaintForStroking(&paint, 0, 0); - if (colorSpace == PatternColorSpace) { - SkShader* pat = state.strokePattern->createPlatformPattern(getCTM()); - paint.setShader(pat); - pat->unref(); - } else if (colorSpace == GradientColorSpace) - paint.setShader(state.strokeGradient->platformGradient()); - platformContext()->canvas()->drawPath(path, paint); } @@ -1102,13 +1148,6 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) platformContext()->setupPaintForStroking(&paint, 0, 0); paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); - if (colorSpace == PatternColorSpace) { - SkShader* pat = state.strokePattern->createPlatformPattern(getCTM()); - paint.setShader(pat); - pat->unref(); - } else if (colorSpace == GradientColorSpace) - paint.setShader(state.strokeGradient->platformGradient()); - platformContext()->canvas()->drawRect(rect, paint); } diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp index 600882d..7935ff1 100644 --- a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2008, Google Inc. All rights reserved. + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -53,7 +54,7 @@ ImageBufferData::ImageBufferData(const IntSize& size) { } -ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success) : m_data(size) , m_size(size) { @@ -71,7 +72,7 @@ ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) // Make the background transparent. It would be nice if this wasn't // required, but the canvas is currently filled with the magic transparency // color. Can we have another way to manage this? - m_data.m_canvas.drawARGB(0, 0, 0, 0, SkPorterDuff::kClear_Mode); + m_data.m_canvas.drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); success = true; } @@ -100,6 +101,23 @@ Image* ImageBuffer::image() const return m_image.get(); } +void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) +{ + const SkBitmap& bitmap = *context()->platformContext()->bitmap(); + ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); + SkAutoLockPixels bitmapLock(bitmap); + for (int y = 0; y < m_size.height(); ++y) { + uint32_t* srcRow = bitmap.getAddr32(0, y); + for (int x = 0; x < m_size.width(); ++x) { + SkColor color = SkPMColorToColor(srcRow[x]); + srcRow[x] = SkPreMultiplyARGB(lookUpTable[SkColorGetA(color)], + lookUpTable[SkColorGetR(color)], + lookUpTable[SkColorGetG(color)], + lookUpTable[SkColorGetB(color)]); + } + } +} + PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const { ASSERT(context()); diff --git a/WebCore/platform/graphics/skia/ImageSkia.cpp b/WebCore/platform/graphics/skia/ImageSkia.cpp index cb089bb..45c3dcd 100644 --- a/WebCore/platform/graphics/skia/ImageSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSkia.cpp @@ -220,10 +220,10 @@ static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeIm } } -static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkPorterDuff::Mode& compOp) +static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp) { SkPaint paint; - paint.setPorterDuffXfermode(compOp); + paint.setXfermodeMode(compOp); paint.setFilterBitmap(true); int alpha = roundf(platformContext->getAlpha() * 256); if (alpha > 255) @@ -379,7 +379,7 @@ void Image::drawPattern(GraphicsContext* context, SkPaint paint; paint.setShader(shader)->unref(); - paint.setPorterDuffXfermode(WebCoreCompositeToSkiaComposite(compositeOp)); + paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); context->platformContext()->paintSkPaint(destRect, paint); diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp index b5f7e1d..1647b86 100644 --- a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp @@ -30,21 +30,21 @@ #include "config.h" -#include "ImageSourceSkia.h" +#include "ImageSource.h" #include "SharedBuffer.h" #include "GIFImageDecoder.h" +#include "ICOImageDecoder.h" #include "JPEGImageDecoder.h" #include "PNGImageDecoder.h" #include "BMPImageDecoder.h" #include "XBMImageDecoder.h" -#include "ICOImageDecoder.h" #include "SkBitmap.h" namespace WebCore { -ImageDecoder* createDecoder(const Vector<char>& data, const IntSize& preferredIconSize) +ImageDecoder* createDecoder(const Vector<char>& data) { // We need at least 4 bytes to figure out what kind of image we're dealing with. int length = data.size(); @@ -79,7 +79,7 @@ ImageDecoder* createDecoder(const Vector<char>& data, const IntSize& preferredIc // CURs begin with 2-byte 0 followed by 2-byte 2. if (!memcmp(contents, "\000\000\001\000", 4) || !memcmp(contents, "\000\000\002\000", 4)) - return new ICOImageDecoder(preferredIconSize); + return new ICOImageDecoder(); // XBMs require 8 bytes of info. if (length >= 8 && strncmp(contents, "#define ", 8) == 0) @@ -124,7 +124,7 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) // If insufficient bytes are available to determine the image type, no decoder plugin will be // made. if (!m_decoder) - m_decoder = createDecoder(data->buffer(), IntSize()); + m_decoder = createDecoder(data->buffer()); // CreateDecoder will return NULL if the decoder could not be created. Plus, // we should not send more data to a decoder which has already decided it @@ -150,10 +150,12 @@ IntSize ImageSource::size() const return m_decoder->size(); } -IntSize ImageSource::frameSizeAtIndex(size_t) const +IntSize ImageSource::frameSizeAtIndex(size_t index) const { - // TODO(brettw) do we need anything here? - return size(); + if (!m_decoder) + return IntSize(); + + return m_decoder->frameSizeAtIndex(index); } int ImageSource::repetitionCount() @@ -185,9 +187,8 @@ NativeImagePtr ImageSource::createFrameAtIndex(size_t index) return 0; // Copy the bitmap. The pixel data is refcounted internally by SkBitmap, so - // this doesn't cost much. This pointer will be owned by the BitmapImage - // and freed in FrameData::clear(). - return new NativeImageSkia(buffer->bitmap()); + // this doesn't cost much. + return buffer->asNewNativeImage(); } bool ImageSource::frameIsCompleteAtIndex(size_t index) @@ -229,16 +230,6 @@ bool ImageSource::frameHasAlphaAtIndex(size_t index) return buffer->hasAlpha(); } -void ImageSourceSkia::setData(SharedBuffer* data, - bool allDataReceived, - const IntSize& preferredIconSize) -{ - if (!m_decoder) - m_decoder = createDecoder(data->buffer(), preferredIconSize); - - ImageSource::setData(data, allDataReceived); -} - String ImageSource::filenameExtension() const { return m_decoder ? m_decoder->filenameExtension() : String(); diff --git a/WebCore/platform/graphics/skia/PathSkia.cpp b/WebCore/platform/graphics/skia/PathSkia.cpp index 9d9df52..5ac14b9 100644 --- a/WebCore/platform/graphics/skia/PathSkia.cpp +++ b/WebCore/platform/graphics/skia/PathSkia.cpp @@ -68,6 +68,11 @@ bool Path::isEmpty() const return m_path->isEmpty(); } +bool Path::hasCurrentPoint() const +{ + return m_path->getPoints(NULL, 0) != 0; +} + bool Path::contains(const FloatPoint& point, WindRule rule) const { return SkPathContainsPoint(m_path, point, @@ -81,15 +86,7 @@ void Path::translate(const FloatSize& size) FloatRect Path::boundingRect() const { - // FIXME: This #ifdef can go away once we're firmly using the new Skia. - // During the transition, this makes the code compatible with both versions. -#ifdef SK_USE_OLD_255_TO_256 return m_path->getBounds(); -#else - SkRect rect; - m_path->computeBounds(&rect, SkPath::kExact_BoundsType); - return rect; -#endif } void Path::moveTo(const FloatPoint& point) @@ -281,15 +278,7 @@ static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context) context->platformContext()->setupPaintForStroking(&paint, 0, 0); SkPath boundingPath; paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath); - // FIXME: This #ifdef can go away once we're firmly using the new Skia. - // During the transition, this makes the code compatible with both versions. -#ifdef SK_USE_OLD_255_TO_256 return boundingPath.getBounds(); -#else - SkRect r; - boundingPath.computeBounds(&r, SkPath::kExact_BoundsType); - return r; -#endif } FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) diff --git a/WebCore/platform/graphics/skia/PatternSkia.cpp b/WebCore/platform/graphics/skia/PatternSkia.cpp index be8eb8a..11b5cf1 100644 --- a/WebCore/platform/graphics/skia/PatternSkia.cpp +++ b/WebCore/platform/graphics/skia/PatternSkia.cpp @@ -33,8 +33,10 @@ #include "NativeImageSkia.h" #include "TransformationMatrix.h" -#include "SkShader.h" #include "SkCanvas.h" +#include "SkColor.h" +#include "SkColorShader.h" +#include "SkShader.h" namespace WebCore { @@ -49,6 +51,10 @@ PlatformPatternPtr Pattern::createPlatformPattern(const TransformationMatrix& pa // LayoutTests/svg/W3C-SVG-1.1/pservers-grad-06-b.svg SkBitmap* bm = m_tileImage->nativeImageForCurrentFrame(); + // If we don't have a bitmap, return a transparent shader. + if (!bm) + return new SkColorShader(SkColorSetARGB(0, 0, 0, 0)); + if (m_repeatX && m_repeatY) return SkShader::CreateBitmapShader(*bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index 74b2bfe..e0a292c 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -57,18 +57,18 @@ struct PlatformContextSkia::State { // Common shader state. float m_alpha; - SkPorterDuff::Mode m_porterDuffMode; - SkShader* m_gradient; - SkShader* m_pattern; + SkXfermode::Mode m_xferMode; bool m_useAntialiasing; SkDrawLooper* m_looper; // Fill. SkColor m_fillColor; + SkShader* m_fillShader; // Stroke. WebCore::StrokeStyle m_strokeStyle; SkColor m_strokeColor; + SkShader* m_strokeShader; float m_strokeThickness; int m_dashRatio; // Ratio of the length of a dash to its width. float m_miterLimit; @@ -98,15 +98,15 @@ private: // Note: Keep theses default values in sync with GraphicsContextState. PlatformContextSkia::State::State() : m_alpha(1) - , m_porterDuffMode(SkPorterDuff::kSrcOver_Mode) - , m_gradient(0) - , m_pattern(0) + , m_xferMode(SkXfermode::kSrcOver_Mode) , m_useAntialiasing(true) , m_looper(0) , m_fillColor(0xFF000000) + , m_fillShader(0) , m_strokeStyle(WebCore::SolidStroke) , m_strokeColor(WebCore::Color::black) , m_strokeThickness(0) + , m_strokeShader(0) , m_dashRatio(3) , m_miterLimit(4) , m_lineCap(SkPaint::kDefault_Cap) @@ -118,15 +118,15 @@ PlatformContextSkia::State::State() PlatformContextSkia::State::State(const State& other) : m_alpha(other.m_alpha) - , m_porterDuffMode(other.m_porterDuffMode) - , m_gradient(other.m_gradient) - , m_pattern(other.m_pattern) + , m_xferMode(other.m_xferMode) , m_useAntialiasing(other.m_useAntialiasing) , m_looper(other.m_looper) , m_fillColor(other.m_fillColor) + , m_fillShader(other.m_fillShader) , m_strokeStyle(other.m_strokeStyle) , m_strokeColor(other.m_strokeColor) , m_strokeThickness(other.m_strokeThickness) + , m_strokeShader(other.m_strokeShader) , m_dashRatio(other.m_dashRatio) , m_miterLimit(other.m_miterLimit) , m_lineCap(other.m_lineCap) @@ -141,16 +141,16 @@ PlatformContextSkia::State::State(const State& other) // Up the ref count of these. saveRef does nothing if 'this' is NULL. m_looper->safeRef(); m_dash->safeRef(); - m_gradient->safeRef(); - m_pattern->safeRef(); + m_fillShader->safeRef(); + m_strokeShader->safeRef(); } PlatformContextSkia::State::~State() { m_looper->safeUnref(); m_dash->safeUnref(); - m_gradient->safeUnref(); - m_pattern->safeUnref(); + m_fillShader->safeUnref(); + m_strokeShader->safeUnref(); } SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const @@ -170,7 +170,6 @@ SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const // Danger: canvas can be NULL. PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) : m_canvas(canvas) - , m_stateStack(sizeof(State)) #if PLATFORM(WIN_OS) , m_drawingToImageBuffer(false) #endif @@ -274,8 +273,11 @@ void PlatformContextSkia::drawRect(SkRect rect) (m_state->m_strokeColor & 0xFF000000)) { // We do a fill of four rects to simulate the stroke of a border. SkColor oldFillColor = m_state->m_fillColor; - if (oldFillColor != m_state->m_strokeColor) - setFillColor(m_state->m_strokeColor); + + // setFillColor() will set the shader to NULL, so save a ref to it now. + SkShader* oldFillShader = m_state->m_fillShader; + oldFillShader->safeRef(); + setFillColor(m_state->m_strokeColor); setupPaintForFilling(&paint); SkRect topBorder = { rect.fLeft, rect.fTop, rect.fRight, rect.fTop + 1 }; canvas()->drawRect(topBorder, paint); @@ -285,8 +287,9 @@ void PlatformContextSkia::drawRect(SkRect rect) canvas()->drawRect(leftBorder, paint); SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, rect.fRight, rect.fBottom - 1 }; canvas()->drawRect(rightBorder, paint); - if (oldFillColor != m_state->m_strokeColor) - setFillColor(oldFillColor); + setFillColor(oldFillColor); + setFillShader(oldFillShader); + oldFillShader->safeUnref(); } } @@ -300,19 +303,15 @@ void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const #endif paint->setAntiAlias(m_state->m_useAntialiasing); - paint->setPorterDuffXfermode(m_state->m_porterDuffMode); + paint->setXfermodeMode(m_state->m_xferMode); paint->setLooper(m_state->m_looper); - - if (m_state->m_gradient) - paint->setShader(m_state->m_gradient); - else if (m_state->m_pattern) - paint->setShader(m_state->m_pattern); } void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const { setupPaintCommon(paint); paint->setColor(m_state->applyAlpha(m_state->m_fillColor)); + paint->setShader(m_state->m_fillShader); } float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const @@ -321,6 +320,7 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i float width = m_state->m_strokeThickness; paint->setColor(m_state->applyAlpha(m_state->m_strokeColor)); + paint->setShader(m_state->m_strokeShader); paint->setStyle(SkPaint::kStroke_Style); paint->setStrokeWidth(SkFloatToScalar(width)); paint->setStrokeCap(m_state->m_lineCap); @@ -338,20 +338,27 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i width = m_state->m_dashRatio * width; // Fall through. case WebCore::DottedStroke: - SkScalar dashLength; - if (length) { - // Determine about how many dashes or dots we should have. - float roundedWidth = roundf(width); - int numDashes = roundedWidth ? (length / roundedWidth) : length; - if (!(numDashes & 1)) - numDashes++; // Make it odd so we end on a dash/dot. - // Use the number of dashes to determine the length of a - // dash/dot, which will be approximately width - dashLength = SkScalarDiv(SkIntToScalar(length), SkIntToScalar(numDashes)); - } else - dashLength = SkFloatToScalar(width); - SkScalar intervals[2] = { dashLength, dashLength }; - paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref(); + // Truncate the width, since we don't want fuzzy dots or dashes. + int dashLength = static_cast<int>(width); + // Subtract off the endcaps, since they're rendered separately. + int distance = length - 2 * static_cast<int>(m_state->m_strokeThickness); + int phase = 1; + if (dashLength > 1) { + // Determine how many dashes or dots we should have. + int numDashes = distance / dashLength; + int remainder = distance % dashLength; + // Adjust the phase to center the dashes within the line. + if (numDashes % 2 == 0) { + // Even: shift right half a dash, minus half the remainder + phase = (dashLength - remainder) / 2; + } else { + // Odd: shift right a full dash, minus half the remainder + phase = dashLength - remainder / 2; + } + } + SkScalar dashLengthSk = SkIntToScalar(dashLength); + SkScalar intervals[2] = { dashLengthSk, dashLengthSk }; + paint->setPathEffect(new SkDashPathEffect(intervals, 2, SkIntToScalar(phase)))->unref(); } } @@ -383,14 +390,15 @@ void PlatformContextSkia::setLineJoin(SkPaint::Join lj) m_state->m_lineJoin = lj; } -void PlatformContextSkia::setPorterDuffMode(SkPorterDuff::Mode pdm) +void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm) { - m_state->m_porterDuffMode = pdm; + m_state->m_xferMode = pdm; } void PlatformContextSkia::setFillColor(SkColor color) { m_state->m_fillColor = color; + setFillShader(NULL); } SkDrawLooper* PlatformContextSkia::getDrawLooper() const @@ -411,6 +419,7 @@ void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle) void PlatformContextSkia::setStrokeColor(SkColor strokeColor) { m_state->m_strokeColor = strokeColor; + setStrokeShader(NULL); } float PlatformContextSkia::getStrokeThickness() const @@ -423,6 +432,15 @@ void PlatformContextSkia::setStrokeThickness(float thickness) m_state->m_strokeThickness = thickness; } +void PlatformContextSkia::setStrokeShader(SkShader* strokeShader) +{ + if (strokeShader != m_state->m_strokeShader) { + m_state->m_strokeShader->safeUnref(); + m_state->m_strokeShader = strokeShader; + m_state->m_strokeShader->safeRef(); + } +} + int PlatformContextSkia::getTextDrawingMode() const { return m_state->m_textDrawingMode; @@ -471,7 +489,8 @@ SkPath PlatformContextSkia::currentPathInLocalCoordinates() const SkPath localPath = m_path; const SkMatrix& matrix = m_canvas->getTotalMatrix(); SkMatrix inverseMatrix; - matrix.invert(&inverseMatrix); + if (!matrix.invert(&inverseMatrix)) + return SkPath(); localPath.transform(inverseMatrix); return localPath; } @@ -481,19 +500,12 @@ void PlatformContextSkia::setFillRule(SkPath::FillType fr) m_path.setFillType(fr); } -void PlatformContextSkia::setGradient(SkShader* gradient) -{ - if (gradient != m_state->m_gradient) { - m_state->m_gradient->safeUnref(); - m_state->m_gradient = gradient; - } -} - -void PlatformContextSkia::setPattern(SkShader* pattern) +void PlatformContextSkia::setFillShader(SkShader* fillShader) { - if (pattern != m_state->m_pattern) { - m_state->m_pattern->safeUnref(); - m_state->m_pattern = pattern; + if (fillShader != m_state->m_fillShader) { + m_state->m_fillShader->safeUnref(); + m_state->m_fillShader = fillShader; + m_state->m_fillShader->safeRef(); } } @@ -527,7 +539,7 @@ void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, con // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping. SkPaint paint; - paint.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode); + paint.setXfermodeMode(SkXfermode::kDstIn_Mode); m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint); } #endif diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h index 25495aa..0c87fc2 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -60,7 +60,7 @@ // responsible for managing the painting state which is store in separate // SkPaint objects. This class provides the adaptor that allows the painting // state to be pushed and popped along with the bitmap. -class PlatformContextSkia : Noncopyable { +class PlatformContextSkia : public Noncopyable { public: // For printing, there shouldn't be any canvas. canvas can be NULL. If you // supply a NULL canvas, you can also call setCanvas later. @@ -115,15 +115,15 @@ public: void setLineCap(SkPaint::Cap); void setLineJoin(SkPaint::Join); void setFillRule(SkPath::FillType); - void setPorterDuffMode(SkPorterDuff::Mode); + void setXfermodeMode(SkXfermode::Mode); void setFillColor(SkColor); + void setFillShader(SkShader*); void setStrokeStyle(WebCore::StrokeStyle); void setStrokeColor(SkColor); void setStrokeThickness(float thickness); + void setStrokeShader(SkShader*); void setTextDrawingMode(int mode); void setUseAntialiasing(bool enable); - void setGradient(SkShader*); - void setPattern(SkShader*); void setDashPathEffect(SkDashPathEffect*); SkDrawLooper* getDrawLooper() const; diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.cpp b/WebCore/platform/graphics/skia/SkiaFontWin.cpp index 7f12508..f1c5cdc 100644 --- a/WebCore/platform/graphics/skia/SkiaFontWin.cpp +++ b/WebCore/platform/graphics/skia/SkiaFontWin.cpp @@ -267,21 +267,11 @@ static bool skiaDrawText(HFONT hfont, SkCanvas* canvas, const SkPoint& point, SkPaint* paint, - const TransformationMatrix& transformationMatrix, - Gradient* gradient, - Pattern* pattern, const WORD* glyphs, const int* advances, const GOFFSET* offsets, int numGlyphs) { - SkShader* shader = NULL; - if (gradient) - shader = gradient->platformGradient(); - else if (pattern) - shader = pattern->createPlatformPattern(transformationMatrix); - - paint->setShader(shader); float x = point.fX, y = point.fY; for (int i = 0; i < numGlyphs; i++) { @@ -326,14 +316,7 @@ bool paintSkiaText(GraphicsContext* context, bool didFill = false; if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) { - Gradient* fillGradient = 0; - Pattern* fillPattern = 0; - if (context->fillColorSpace() == GradientColorSpace) - fillGradient = context->fillGradient(); - else if (context->fillColorSpace() == PatternColorSpace) - fillPattern = context->fillPattern(); if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, - context->getCTM(), fillGradient, fillPattern, &glyphs[0], &advances[0], &offsets[0], numGlyphs)) return false; didFill = true; @@ -360,14 +343,7 @@ bool paintSkiaText(GraphicsContext* context, paint.setLooper(0)->safeUnref(); } - Gradient* strokeGradient = 0; - Pattern* strokePattern = 0; - if (context->strokeColorSpace() == GradientColorSpace) - strokeGradient = context->strokeGradient(); - else if (context->strokeColorSpace() == PatternColorSpace) - strokePattern = context->strokePattern(); if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, - context->getCTM(), strokeGradient, strokePattern, &glyphs[0], &advances[0], &offsets[0], numGlyphs)) return false; } diff --git a/WebCore/platform/graphics/skia/SkiaUtils.cpp b/WebCore/platform/graphics/skia/SkiaUtils.cpp index a7a266d..dbbbdbf 100644 --- a/WebCore/platform/graphics/skia/SkiaUtils.cpp +++ b/WebCore/platform/graphics/skia/SkiaUtils.cpp @@ -42,6 +42,7 @@ namespace WebCore { +#ifdef MANUAL_MERGE_REQUIRED static const struct CompositOpToSkiaMode { uint8_t mCompositOp; uint8_t mMode; @@ -77,36 +78,39 @@ SkXfermode::Mode WebCoreCompositeToSkiaMode(CompositeOperator op) } static const struct CompositOpToPorterDuffMode { +#else // MANUAL_MERGE_REQUIRED +static const struct CompositOpToXfermodeMode { +#endif // MANUAL_MERGE_REQUIRED uint8_t mCompositOp; - uint8_t mPorterDuffMode; -} gMapCompositOpsToPorterDuffModes[] = { - { CompositeClear, SkPorterDuff::kClear_Mode }, - { CompositeCopy, SkPorterDuff::kSrc_Mode }, - { CompositeSourceOver, SkPorterDuff::kSrcOver_Mode }, - { CompositeSourceIn, SkPorterDuff::kSrcIn_Mode }, - { CompositeSourceOut, SkPorterDuff::kSrcOut_Mode }, - { CompositeSourceAtop, SkPorterDuff::kSrcATop_Mode }, - { CompositeDestinationOver, SkPorterDuff::kDstOver_Mode }, - { CompositeDestinationIn, SkPorterDuff::kDstIn_Mode }, - { CompositeDestinationOut, SkPorterDuff::kDstOut_Mode }, - { CompositeDestinationAtop, SkPorterDuff::kDstATop_Mode }, - { CompositeXOR, SkPorterDuff::kXor_Mode }, - { CompositePlusDarker, SkPorterDuff::kDarken_Mode }, - { CompositeHighlight, SkPorterDuff::kSrcOver_Mode }, // TODO - { CompositePlusLighter, SkPorterDuff::kAdd_Mode } + uint8_t m_xfermodeMode; +} gMapCompositOpsToXfermodeModes[] = { + { CompositeClear, SkXfermode::kClear_Mode }, + { CompositeCopy, SkXfermode::kSrc_Mode }, + { CompositeSourceOver, SkXfermode::kSrcOver_Mode }, + { CompositeSourceIn, SkXfermode::kSrcIn_Mode }, + { CompositeSourceOut, SkXfermode::kSrcOut_Mode }, + { CompositeSourceAtop, SkXfermode::kSrcATop_Mode }, + { CompositeDestinationOver, SkXfermode::kDstOver_Mode }, + { CompositeDestinationIn, SkXfermode::kDstIn_Mode }, + { CompositeDestinationOut, SkXfermode::kDstOut_Mode }, + { CompositeDestinationAtop, SkXfermode::kDstATop_Mode }, + { CompositeXOR, SkXfermode::kXor_Mode }, + { CompositePlusDarker, SkXfermode::kDarken_Mode }, + { CompositeHighlight, SkXfermode::kSrcOver_Mode }, // TODO + { CompositePlusLighter, SkXfermode::kPlus_Mode } }; -SkPorterDuff::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op) +SkXfermode::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op) { - const CompositOpToPorterDuffMode* table = gMapCompositOpsToPorterDuffModes; + const CompositOpToXfermodeMode* table = gMapCompositOpsToXfermodeModes; - for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToPorterDuffModes); i++) { + for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToXfermodeModes); i++) { if (table[i].mCompositOp == op) - return (SkPorterDuff::Mode)table[i].mPorterDuffMode; + return (SkXfermode::Mode)table[i].m_xfermodeMode; } SkDEBUGF(("GraphicsContext::setCompositeOperation uknown CompositeOperator %d\n", op)); - return SkPorterDuff::kSrcOver_Mode; // fall-back + return SkXfermode::kSrcOver_Mode; // fall-back } Color SkPMColorToWebCoreColor(SkPMColor pm) @@ -168,8 +172,12 @@ bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath:: SkPath scaledPath; int scale = 1; +#ifdef MANUAL_MERGE_REQUIRED SkRect bounds; bounds = originalPath->getBounds(); +#else // MANUAL_MERGE_REQUIRED + SkRect bounds = originalPath->getBounds(); +#endif // MANUAL_MERGE_REQUIRED // We can immediately return false if the point is outside the bounding rect if (!bounds.contains(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()))) @@ -208,7 +216,7 @@ GraphicsContext* scratchContext() { static ImageBuffer* scratch = 0; if (!scratch) - scratch = ImageBuffer::create(IntSize(1, 1), false).release(); + scratch = ImageBuffer::create(IntSize(1, 1)).release(); // We don't bother checking for failure creating the ImageBuffer, since our // ImageBuffer initializer won't fail. return scratch->context(); diff --git a/WebCore/platform/graphics/skia/SkiaUtils.h b/WebCore/platform/graphics/skia/SkiaUtils.h index aa4cd4d..a210cd3 100644 --- a/WebCore/platform/graphics/skia/SkiaUtils.h +++ b/WebCore/platform/graphics/skia/SkiaUtils.h @@ -36,16 +36,24 @@ #include <wtf/MathExtras.h> #include "GraphicsContext.h" #include "SkPath.h" +#ifdef MANUAL_MERGE_REQUIRED #include "SkPorterDuff.h" #include "SkXfermode.h" +#else // MANUAL_MERGE_REQUIRED +#include "SkXfermode.h" +#endif // MANUAL_MERGE_REQUIRED class SkCanvas; class SkRegion; namespace WebCore { +#ifdef MANUAL_MERGE_REQUIRED SkXfermode::Mode WebCoreCompositeToSkiaMode(CompositeOperator); SkPorterDuff::Mode WebCoreCompositeToSkiaComposite(CompositeOperator); +#else // MANUAL_MERGE_REQUIRED +SkXfermode::Mode WebCoreCompositeToSkiaComposite(CompositeOperator); +#endif // MANUAL_MERGE_REQUIRED // move this guy into SkColor.h SkColor SkPMColorToColor(SkPMColor); diff --git a/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp index ab3413b..230be3c 100644 --- a/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp @@ -47,7 +47,7 @@ PassRefPtr<TransformOperation> Matrix3DTransformOperation::blend(const Transform apply(toT, size); if (blendToIdentity) - swap(fromT, toT); + std::swap(fromT, toT); toT.blend(fromT, progress); return Matrix3DTransformOperation::create(toT); diff --git a/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp b/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp index 4934fa6..0eaccea 100644 --- a/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp @@ -43,7 +43,7 @@ PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOp } if (blendToIdentity) - swap(fromT, toT); + std::swap(fromT, toT); toT.blend(fromT, progress); return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f()); diff --git a/WebCore/platform/graphics/transforms/TransformOperations.h b/WebCore/platform/graphics/transforms/TransformOperations.h index 11605e8..dd56408 100644 --- a/WebCore/platform/graphics/transforms/TransformOperations.h +++ b/WebCore/platform/graphics/transforms/TransformOperations.h @@ -60,6 +60,9 @@ public: Vector<RefPtr<TransformOperation> >& operations() { return m_operations; } const Vector<RefPtr<TransformOperation> >& operations() const { return m_operations; } + size_t size() const { return m_operations.size(); } + const TransformOperation* at(size_t index) const { return index < m_operations.size() ? m_operations.at(index).get() : 0; } + private: Vector<RefPtr<TransformOperation> > m_operations; }; diff --git a/WebCore/platform/graphics/transforms/TransformationMatrix.cpp b/WebCore/platform/graphics/transforms/TransformationMatrix.cpp index a358aaf..13ef281 100644 --- a/WebCore/platform/graphics/transforms/TransformationMatrix.cpp +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -865,6 +866,16 @@ TransformationMatrix& TransformationMatrix::applyPerspective(double p) return *this; } +TransformationMatrix TransformationMatrix::rectToRect(const FloatRect& from, const FloatRect& to) +{ + ASSERT(!from.isEmpty()); + return TransformationMatrix(to.width() / from.width(), + 0, 0, + to.height() / from.height(), + to.x() - from.x(), + to.y() - from.y()); +} + // // *this = mat * *this // diff --git a/WebCore/platform/graphics/transforms/TransformationMatrix.h b/WebCore/platform/graphics/transforms/TransformationMatrix.h index 7b93e04..a7fbb3d 100644 --- a/WebCore/platform/graphics/transforms/TransformationMatrix.h +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.h @@ -225,6 +225,9 @@ public: TransformationMatrix& applyPerspective(double p); bool hasPerspective() const { return m_matrix[2][3] != 0.0f; } + // returns a transformation that maps a rect to a rect + static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&); + bool isInvertible() const; // This method returns the identity matrix if it is not invertible. @@ -284,7 +287,7 @@ public: } // result = *this * t (i.e., a multRight) - TransformationMatrix operator*(const TransformationMatrix& t) + TransformationMatrix operator*(const TransformationMatrix& t) const { TransformationMatrix result = t; result.multLeft(*this); @@ -303,6 +306,10 @@ public: operator wxGraphicsMatrix() const; #endif +#if PLATFORM(WIN) + operator XFORM() const; +#endif + private: // multiply passed 2D point by matrix (assume z=0) void multVecMatrix(double x, double y, double& dstX, double& dstY) const; diff --git a/WebCore/platform/graphics/win/ColorSafari.cpp b/WebCore/platform/graphics/win/ColorSafari.cpp deleted file mode 100644 index 25b6b89..0000000 --- a/WebCore/platform/graphics/win/ColorSafari.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "Color.h" - -#include <CoreGraphics/CGColor.h> -#include <SafariTheme/SafariTheme.h> -#include <wtf/Assertions.h> -#include <wtf/RetainPtr.h> - -using namespace SafariTheme; - -namespace WebCore { - -typedef CGColorRef (APIENTRY*stCopyThemeColorPtr)(unsigned, SafariTheme::ThemeControlState); -static const unsigned stFocusRingColorID = 4; - -static const unsigned aquaFocusRingColor = 0xFF7DADD9; - -static RGBA32 makeRGBAFromCGColor(CGColorRef c) -{ - const CGFloat* components = CGColorGetComponents(c); - return makeRGBA(255 * components[0], 255 * components[1], 255 * components[2], 255 * components[3]); -} - -Color focusRingColor() -{ - static Color focusRingColor; - focusRingColor.isValid(); - - if (!focusRingColor.isValid()) { - if (HMODULE module = LoadLibrary(SAFARITHEMEDLL)) - if (stCopyThemeColorPtr stCopyThemeColor = (stCopyThemeColorPtr)GetProcAddress(module, "STCopyThemeColor")) { - RetainPtr<CGColorRef> c(AdoptCF, stCopyThemeColor(stFocusRingColorID, SafariTheme::ActiveState)); - focusRingColor = makeRGBAFromCGColor(c.get()); - } - if (!focusRingColor.isValid()) - focusRingColor = aquaFocusRingColor; - } - - return focusRingColor; -} - -} // namespace WebCore diff --git a/WebCore/platform/graphics/win/FontPlatformData.h b/WebCore/platform/graphics/win/FontPlatformData.h index 09a8b55..0660d90 100644 --- a/WebCore/platform/graphics/win/FontPlatformData.h +++ b/WebCore/platform/graphics/win/FontPlatformData.h @@ -39,6 +39,7 @@ typedef struct CGFont* CGFontRef; namespace WebCore { class FontDescription; +class String; class FontPlatformData { public: @@ -64,6 +65,9 @@ public: FontPlatformData(HFONT, CGFontRef, float size, bool bold, bool oblique, bool useGDI); #elif PLATFORM(CAIRO) FontPlatformData(cairo_font_face_t*, float size, bool bold, bool oblique); + FontPlatformData(const FontPlatformData&); + + FontPlatformData& operator=(const FontPlatformData&); #endif ~FontPlatformData(); @@ -104,6 +108,10 @@ public: m_useGDI == other.m_useGDI; } +#ifndef NDEBUG + String description() const; +#endif + private: class RefCountedHFONT : public RefCounted<RefCountedHFONT> { public: diff --git a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp index 59f7e5c..a92e367 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp @@ -137,4 +137,8 @@ FontPlatformData::FontPlatformData(HFONT hfont, CGFontRef font, float size, bool { } +FontPlatformData::~FontPlatformData() +{ +} + } diff --git a/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp index 438d0a9..b56a71c 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp @@ -41,6 +41,7 @@ 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); @@ -79,6 +80,22 @@ FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool 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); +} + void FontPlatformData::setFont(cairo_t* cr) const { ASSERT(m_scaledFont); @@ -86,4 +103,37 @@ void FontPlatformData::setFont(cairo_t* cr) const cairo_set_scaled_font(cr, 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; +} + } diff --git a/WebCore/platform/graphics/win/FontPlatformDataWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp index 4b4df5a..cc02c4c 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp @@ -85,8 +85,11 @@ FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) { } -FontPlatformData::~FontPlatformData() +#ifndef NDEBUG +String FontPlatformData::description() const { + return String(); } +#endif } diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index 917631b..9eaf54b 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -77,64 +77,6 @@ GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha) // FIXME: Is it possible to merge getWindowsContext and createWindowsBitmap into a single API // suitable for all clients? -HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) -{ - // FIXME: Should a bitmap be created also when a shadow is set? - if (mayCreateBitmap && inTransparencyLayer()) { - if (dstRect.isEmpty()) - return 0; - - // Create a bitmap DC in which to draw. - BITMAPINFO bitmapInfo; - bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bitmapInfo.bmiHeader.biWidth = dstRect.width(); - bitmapInfo.bmiHeader.biHeight = dstRect.height(); - bitmapInfo.bmiHeader.biPlanes = 1; - bitmapInfo.bmiHeader.biBitCount = 32; - bitmapInfo.bmiHeader.biCompression = BI_RGB; - bitmapInfo.bmiHeader.biSizeImage = 0; - bitmapInfo.bmiHeader.biXPelsPerMeter = 0; - bitmapInfo.bmiHeader.biYPelsPerMeter = 0; - bitmapInfo.bmiHeader.biClrUsed = 0; - bitmapInfo.bmiHeader.biClrImportant = 0; - - void* pixels = 0; - HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); - if (!bitmap) - return 0; - - HDC bitmapDC = ::CreateCompatibleDC(m_data->m_hdc); - ::SelectObject(bitmapDC, bitmap); - - // Fill our buffer with clear if we're going to alpha blend. - if (supportAlphaBlend) { - BITMAP bmpInfo; - GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); - int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; - memset(bmpInfo.bmBits, 0, bufferSize); - } - - // Make sure we can do world transforms. - SetGraphicsMode(bitmapDC, GM_ADVANCED); - - // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap. - XFORM xform; - xform.eM11 = 1.0f; - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = 1.0f; - xform.eDx = -dstRect.x(); - xform.eDy = -dstRect.y(); - ::SetWorldTransform(bitmapDC, &xform); - - return bitmapDC; - } - - CGContextFlush(platformContext()); - m_data->save(); - return m_data->m_hdc; -} - void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { if (mayCreateBitmap && hdc && inTransparencyLayer()) { @@ -170,52 +112,6 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo m_data->restore(); } -GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size) - : m_hdc(0) - , m_size(size) -{ - BITMAPINFO bitmapInfo; - bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bitmapInfo.bmiHeader.biWidth = m_size.width(); - bitmapInfo.bmiHeader.biHeight = m_size.height(); - bitmapInfo.bmiHeader.biPlanes = 1; - bitmapInfo.bmiHeader.biBitCount = 32; - bitmapInfo.bmiHeader.biCompression = BI_RGB; - bitmapInfo.bmiHeader.biSizeImage = 0; - bitmapInfo.bmiHeader.biXPelsPerMeter = 0; - bitmapInfo.bmiHeader.biYPelsPerMeter = 0; - bitmapInfo.bmiHeader.biClrUsed = 0; - bitmapInfo.bmiHeader.biClrImportant = 0; - - m_bitmap = CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&m_bitmapBuffer), 0, 0); - if (!m_bitmap) - return; - - m_hdc = CreateCompatibleDC(hdc); - SelectObject(m_hdc, m_bitmap); - - BITMAP bmpInfo; - GetObject(m_bitmap, sizeof(bmpInfo), &bmpInfo); - m_bytesPerRow = bmpInfo.bmWidthBytes; - m_bitmapBufferLength = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; - - SetGraphicsMode(m_hdc, GM_ADVANCED); -} - -GraphicsContext::WindowsBitmap::~WindowsBitmap() -{ - if (!m_bitmap) - return; - - DeleteDC(m_hdc); - DeleteObject(m_bitmap); -} - -GraphicsContext::WindowsBitmap* GraphicsContext::createWindowsBitmap(IntSize size) -{ - return new WindowsBitmap(m_data->m_hdc, size); -} - void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point) { RetainPtr<CGColorSpaceRef> deviceRGB(AdoptCF, CGColorSpaceCreateDeviceRGB()); @@ -228,23 +124,6 @@ void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& po CGContextDrawImage(m_data->m_cgContext, CGRectMake(point.x(), point.y(), image->size().width(), image->size().height()), cgImage.get()); } -void GraphicsContextPlatformPrivate::concatCTM(const TransformationMatrix& transform) -{ - if (!m_hdc) - return; - - CGAffineTransform mat = transform; - XFORM xform; - xform.eM11 = mat.a; - xform.eM12 = mat.b; - xform.eM21 = mat.c; - xform.eM22 = mat.d; - xform.eDx = mat.tx; - xform.eDy = mat.ty; - - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - void GraphicsContext::drawFocusRing(const Color& color) { if (paintingDisabled()) @@ -362,4 +241,9 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& point, CGContextRestoreGState(context); } +void GraphicsContextPlatformPrivate::flush() +{ + CGContextFlush(m_cgContext); +} + } diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index ca3cb5d..2489e02 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -59,32 +59,6 @@ static cairo_t* createCairoContextWithHDC(HDC hdc, bool hasAlpha) return context; } -static BITMAPINFO bitmapInfoForSize(const IntSize& size) -{ - BITMAPINFO bitmapInfo; - bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bitmapInfo.bmiHeader.biWidth = size.width(); - bitmapInfo.bmiHeader.biHeight = size.height(); - bitmapInfo.bmiHeader.biPlanes = 1; - bitmapInfo.bmiHeader.biBitCount = 32; - bitmapInfo.bmiHeader.biCompression = BI_RGB; - bitmapInfo.bmiHeader.biSizeImage = 0; - bitmapInfo.bmiHeader.biXPelsPerMeter = 0; - bitmapInfo.bmiHeader.biYPelsPerMeter = 0; - bitmapInfo.bmiHeader.biClrUsed = 0; - bitmapInfo.bmiHeader.biClrImportant = 0; - - return bitmapInfo; -} - -static void fillWithClearColor(HBITMAP bitmap) -{ - BITMAP bmpInfo; - GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); - int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; - memset(bmpInfo.bmBits, 0, bufferSize); -} - GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate) @@ -105,53 +79,6 @@ GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha) } } -HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) -{ - // FIXME: Should a bitmap be created also when a shadow is set? - if (mayCreateBitmap && inTransparencyLayer()) { - if (dstRect.isEmpty()) - return 0; - - // Create a bitmap DC in which to draw. - BITMAPINFO bitmapInfo = bitmapInfoForSize(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; - xform.eM11 = 1.0f; - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = 1.0f; - xform.eDx = -dstRect.x(); - xform.eDy = -dstRect.y(); - ::SetWorldTransform(bitmapDC, &xform); - - return bitmapDC; - } - - cairo_surface_t* surface = cairo_win32_surface_create(m_data->m_hdc); - cairo_surface_flush(surface); - cairo_surface_destroy(surface); - - m_data->save(); - - return m_data->m_hdc; -} - void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { if (!mayCreateBitmap || !hdc || !inTransparencyLayer()) { @@ -194,27 +121,22 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo ::DeleteObject(bitmap); } -void GraphicsContextPlatformPrivate::concatCTM(const TransformationMatrix& transform) -{ - const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); - - XFORM xform; - xform.eM11 = matrix->xx; - xform.eM12 = matrix->xy; - xform.eM21 = matrix->yx; - xform.eM22 = matrix->yy; - xform.eDx = matrix->x0; - xform.eDy = matrix->y0; - - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - 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/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/WebCore/platform/graphics/win/GraphicsContextWin.cpp index e4c5b04..54b0cb2 100644 --- a/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -32,6 +32,7 @@ #include "GraphicsContextPlatformPrivateCairo.h" #endif +#include "BitmapInfo.h" #include "TransformationMatrix.h" #include "NotImplemented.h" #include "Path.h" @@ -43,6 +44,14 @@ namespace WebCore { class SVGResourceImage; +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) @@ -55,6 +64,79 @@ 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 && 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) @@ -85,13 +167,8 @@ void GraphicsContextPlatformPrivate::scale(const FloatSize& size) { if (!m_hdc) return; - XFORM xform; - xform.eM11 = size.width(); - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = size.height(); - xform.eDx = 0.0f; - xform.eDy = 0.0f; + + XFORM xform = TransformationMatrix().scaleNonUniform(size.width(), size.height()); ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); } @@ -99,16 +176,7 @@ static const double deg2rad = 0.017453292519943295769; // pi/180 void GraphicsContextPlatformPrivate::rotate(float degreesAngle) { - float radiansAngle = degreesAngle * deg2rad; - float cosAngle = cosf(radiansAngle); - float sinAngle = sinf(radiansAngle); - XFORM xform; - xform.eM11 = cosAngle; - xform.eM12 = -sinAngle; - xform.eM21 = sinAngle; - xform.eM22 = cosAngle; - xform.eDx = 0.0f; - xform.eDy = 0.0f; + XFORM xform = TransformationMatrix().rotate(degreesAngle); ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); } @@ -116,13 +184,17 @@ void GraphicsContextPlatformPrivate::translate(float x , float y) { if (!m_hdc) return; - XFORM xform; - xform.eM11 = 1.0f; - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = 1.0f; - xform.eDx = x; - xform.eDy = y; + + XFORM xform = TransformationMatrix().translate(x, y); + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +void GraphicsContextPlatformPrivate::concatCTM(const TransformationMatrix& transform) +{ + if (!m_hdc) + return; + + XFORM xform = transform; ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); } diff --git a/WebCore/platform/graphics/win/IconWin.cpp b/WebCore/platform/graphics/win/IconWin.cpp index c02b56e..61f1fd3 100644 --- a/WebCore/platform/graphics/win/IconWin.cpp +++ b/WebCore/platform/graphics/win/IconWin.cpp @@ -1,5 +1,6 @@ /* * 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 @@ -26,6 +27,11 @@ #include <tchar.h> #include <windows.h> +#if PLATFORM(WINCE) +// SHGFI_SHELLICONSIZE is not available on WINCE +#define SHGFI_SHELLICONSIZE 0 +#endif + namespace WebCore { static const int shell32MultipleFileIconIndex = 54; @@ -55,6 +61,9 @@ PassRefPtr<Icon> Icon::createIconForFile(const String& filename) PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>&) { +#if PLATFORM(WINCE) + return 0; +#else TCHAR buffer[MAX_PATH]; UINT length = ::GetSystemDirectory(buffer, ARRAYSIZE(buffer)); if (!length) @@ -67,6 +76,7 @@ PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>&) if (!::ExtractIconEx(buffer, shell32MultipleFileIconIndex, 0, &hIcon, 1)) return 0; return adoptRef(new Icon(hIcon)); +#endif } void Icon::paint(GraphicsContext* context, const IntRect& r) @@ -74,11 +84,15 @@ void Icon::paint(GraphicsContext* context, const IntRect& r) if (context->paintingDisabled()) return; +#if PLATFORM(WINCE) + context->drawIcon(m_hIcon, r, DI_NORMAL); +#else HDC hdc = context->getWindowsContext(r); DrawIconEx(hdc, r.x(), r.y(), m_hIcon, r.width(), r.height(), 0, 0, DI_NORMAL); context->releaseWindowsContext(hdc, r); +#endif } } diff --git a/WebCore/platform/graphics/win/ImageCGWin.cpp b/WebCore/platform/graphics/win/ImageCGWin.cpp index 752729c..8a8e943 100644 --- a/WebCore/platform/graphics/win/ImageCGWin.cpp +++ b/WebCore/platform/graphics/win/ImageCGWin.cpp @@ -65,8 +65,8 @@ bool BitmapImage::getHBITMAPOfSize(HBITMAP bmp, LPSIZE size) void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext* ctxt, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator compositeOp) { - int frames = frameCount(); - for (int i = 0; i < frames; ++i) { + 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; diff --git a/WebCore/platform/graphics/win/ImageCairoWin.cpp b/WebCore/platform/graphics/win/ImageCairoWin.cpp index 06428b8..591375f 100644 --- a/WebCore/platform/graphics/win/ImageCairoWin.cpp +++ b/WebCore/platform/graphics/win/ImageCairoWin.cpp @@ -73,8 +73,8 @@ bool BitmapImage::getHBITMAPOfSize(HBITMAP bmp, LPSIZE size) void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext* ctxt, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator compositeOp) { - int frames = frameCount(); - for (int i = 0; i < frames; ++i) { + 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; diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index 35ea786..eb7334e 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -106,7 +106,7 @@ void MediaPlayerPrivate::load(const String& url) cancelSeek(); m_qtMovie.set(new QTMovieWin(this)); - m_qtMovie->load(url.characters(), url.length()); + m_qtMovie->load(url.characters(), url.length(), m_player->preservesPitch()); m_qtMovie->setVolume(m_player->volume()); m_qtMovie->setVisible(m_player->visible()); } @@ -252,8 +252,14 @@ void MediaPlayerPrivate::setRate(float rate) { if (!m_qtMovie) return; - if (!paused()) - m_qtMovie->setRate(rate); + m_qtMovie->setRate(rate); +} + +void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch) +{ + if (!m_qtMovie) + return; + m_qtMovie->setPreservesPitch(preservesPitch); } int MediaPlayerPrivate::dataRate() const @@ -421,9 +427,33 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) { if (p->paintingDisabled() || !m_qtMovie || m_hasUnsupportedTracks) return; + + bool usingTempBitmap = false; + OwnPtr<GraphicsContext::WindowsBitmap> bitmap; HDC hdc = p->getWindowsContext(r); + if (!hdc) { + // The graphics context doesn't have an associated HDC so create a temporary + // bitmap where QTMovieWin 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_qtMovie->paint(hdc, r.x(), r.y()); - p->releaseWindowsContext(hdc, r); + if (usingTempBitmap) + p->drawWindowsBitmap(bitmap.get(), r.topLeft()); + else + p->releaseWindowsContext(hdc, r); #if DRAW_FRAME_RATE if (m_frameCountWhilePlaying > 10) { @@ -486,7 +516,7 @@ MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, c { // 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; + return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; } void MediaPlayerPrivate::movieEnded(QTMovieWin* movie) @@ -535,6 +565,13 @@ void MediaPlayerPrivate::movieNewImageAvailable(QTMovieWin* movie) 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; +} + } #endif diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h index 3207867..f584148 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -69,6 +69,7 @@ public: void setRate(float); void setVolume(float); + void setPreservesPitch(bool); int dataRate() const; @@ -89,6 +90,7 @@ public: void paint(GraphicsContext*, const IntRect&); + bool hasSingleSecurityOrigin() const; private: MediaPlayerPrivate(MediaPlayer*); diff --git a/WebCore/platform/graphics/win/QTMovieWin.cpp b/WebCore/platform/graphics/win/QTMovieWin.cpp index 1d10006..aaa61f1 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.cpp +++ b/WebCore/platform/graphics/win/QTMovieWin.cpp @@ -76,7 +76,7 @@ static void updateTaskTimer(int maxInterval = 1000) setSharedTimerFireDelay(static_cast<float>(intervalInMS) / 1000); } -class QTMovieWinPrivate : Noncopyable { +class QTMovieWinPrivate : public Noncopyable { public: QTMovieWinPrivate(); ~QTMovieWinPrivate(); @@ -115,6 +115,9 @@ public: 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 @@ -141,6 +144,9 @@ QTMovieWinPrivate::QTMovieWinPrivate() , m_loadError(0) , m_widthScaleFactor(1) , m_heightScaleFactor(1) + , m_currentURL(0) + , m_timeToRestore(-1.0f) + , m_rateToRestore(-1.0f) #if !ASSERT_DISABLED , m_scaleCached(false) #endif @@ -156,6 +162,8 @@ QTMovieWinPrivate::~QTMovieWinPrivate() DisposeMovieController(m_movieController); if (m_movie) DisposeMovie(m_movie); + if (m_currentURL) + CFRelease(m_currentURL); } static void taskTimerFired() @@ -230,16 +238,26 @@ void QTMovieWinPrivate::task() // 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; if (loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded) { if (m_visible) clearGWorld(); cacheMovieScale(); + shouldRestorePlaybackState = true; } m_loadState = loadState; if (!m_movieController && m_loadState >= QTMovieLoadStateLoaded) createMovieController(); m_client->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_movieWin->m_disabled) { endTask(); return; @@ -425,6 +443,8 @@ QTMovieWin::~QTMovieWin() void QTMovieWin::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 @@ -434,6 +454,8 @@ void QTMovieWin::play() void QTMovieWin::pause() { + m_private->m_timeToRestore = -1.0f; + if (m_private->m_movieController) MCDoAction(m_private->m_movieController, mcActionPlay, 0); else @@ -451,7 +473,9 @@ float QTMovieWin::rate() const void QTMovieWin::setRate(float rate) { if (!m_private->m_movie) - return; + return; + m_private->m_timeToRestore = -1.0f; + if (m_private->m_movieController) MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate)); else @@ -481,6 +505,9 @@ void QTMovieWin::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){ @@ -498,6 +525,25 @@ void QTMovieWin::setVolume(float volume) SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256)); } +void QTMovieWin::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 QTMovieWin::dataSize() const { if (!m_private->m_movie) @@ -561,8 +607,22 @@ void QTMovieWin::paint(HDC hdc, int x, int y) 0, 0, m_private->m_width, m_private->m_height, blendFunction); } -void QTMovieWin::load(const UChar* url, int len) +void QTMovieWin::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 QTMovieWin::load(CFURLRef url, bool preservesPitch) +{ + if (!url) + return; + if (m_private->m_movie) { m_private->endTask(); if (m_private->m_gWorld) @@ -572,6 +632,7 @@ void QTMovieWin::load(const UChar* url, int len) 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. @@ -579,23 +640,33 @@ void QTMovieWin::load(const UChar* url, int len) ItemCount moviePropCount = 0; bool boolTrue = true; - - // Create a URL data reference of type CFURL - CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len); // Disable streaming support for now. - if (CFStringHasPrefix(urlStringRef, CFSTR("rtsp:"))) { + CFStringRef scheme = CFURLCopyScheme(url); + bool isRTSP = CFStringHasPrefix(scheme, CFSTR("rtsp:")); + CFRelease(scheme); + + if (isRTSP) { m_private->m_loadError = noMovieFound; goto end; } - CFURLRef urlRef = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, 0); + 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(urlRef); - movieProps[moviePropCount].propValueAddress = &urlRef; + movieProps[moviePropCount].propValueSize = sizeof(m_private->m_currentURL); + movieProps[moviePropCount].propValueAddress = &(m_private->m_currentURL); movieProps[moviePropCount].propStatus = 0; moviePropCount++; @@ -639,12 +710,18 @@ void QTMovieWin::load(const UChar* url, int len) 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 <= sizeof(movieProps)/sizeof(movieProps[0])); m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, NULL, &m_private->m_movie); - CFRelease(urlRef); end: m_private->startTask(); // get the load fail callback quickly @@ -660,8 +737,6 @@ end: QTSetMovieProperty(m_private->m_movie, kQTPropertyClass_Visual, kQTVisualPropertyID_ApertureMode, sizeof(mode), &mode); m_private->registerDrawingCallback(); } - - CFRelease(urlStringRef); } void QTMovieWin::disableUnsupportedTracks(unsigned& enabledTrackCount, unsigned& totalTrackCount) diff --git a/WebCore/platform/graphics/win/QTMovieWin.h b/WebCore/platform/graphics/win/QTMovieWin.h index 70cbef5..f46efd3 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.h +++ b/WebCore/platform/graphics/win/QTMovieWin.h @@ -53,6 +53,8 @@ enum { QTMovieLoadStateComplete = 100000L }; +typedef const struct __CFURL * CFURLRef; + class QTMOVIEWIN_API QTMovieWin { public: static bool initializeQuickTime(); @@ -60,7 +62,7 @@ public: QTMovieWin(QTMovieWinClient*); ~QTMovieWin(); - void load(const UChar* url, int len); + void load(const UChar* url, int len, bool preservesPitch); long loadState() const; float maxTimeLoaded() const; @@ -75,6 +77,7 @@ public: void setCurrentTime(float) const; void setVolume(float); + void setPreservesPitch(bool); unsigned dataSize() const; @@ -93,6 +96,8 @@ public: static void getSupportedType(unsigned index, const UChar*& str, unsigned& len); private: + void load(CFURLRef, bool preservesPitch); + QTMovieWinPrivate* m_private; bool m_disabled; friend class QTMovieWinPrivate; diff --git a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp index aaa089a..6b3a96e 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp @@ -126,11 +126,6 @@ void SimpleFontData::platformCharWidthInit() } } -void SimpleFontData::platformDestroy() -{ - platformCommonDestroy(); -} - float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { if (m_platformData.useGDI()) diff --git a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp index 2e51621..0343007 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp @@ -47,7 +47,6 @@ void SimpleFontData::platformInit() m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; - m_syntheticBoldOffset = 0; m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; @@ -97,16 +96,6 @@ void SimpleFontData::platformCharWidthInit() // charwidths are set in platformInit. } -void SimpleFontData::platformDestroy() -{ - cairo_font_face_destroy(m_platformData.fontFace()); - cairo_scaled_font_destroy(m_platformData.scaledFont()); - - DeleteObject(m_platformData.hfont()); - - platformCommonDestroy(); -} - float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { if (m_platformData.useGDI()) diff --git a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp index 9835e9f..5a3244c 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp @@ -37,10 +37,13 @@ #include <wtf/MathExtras.h> #include <unicode/uchar.h> #include <unicode/unorm.h> -#include <ApplicationServices/ApplicationServices.h> #include <mlang.h> #include <tchar.h> + +#if PLATFORM(CG) +#include <ApplicationServices/ApplicationServices.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> +#endif namespace WebCore { @@ -89,7 +92,7 @@ void SimpleFontData::initGDIFont() return; } -void SimpleFontData::platformCommonDestroy() +void SimpleFontData::platformDestroy() { // We don't hash this on Win32, so it's effectively owned by us. delete m_smallCapsFontData; diff --git a/WebCore/platform/graphics/chromium/ColorChromium.cpp b/WebCore/platform/graphics/win/TransformationMatrixWin.cpp index 647169c..38dbfbf 100644 --- a/WebCore/platform/graphics/chromium/ColorChromium.cpp +++ b/WebCore/platform/graphics/win/TransformationMatrixWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Google Inc. All rights reserved. + * 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 @@ -24,19 +24,21 @@ */ #include "config.h" -#include "Color.h" +#include "TransformationMatrix.h" namespace WebCore { -#if !PLATFORM(DARWIN) -// On OS X, there's code to monitor changes in the focus color system setting. -// On Windows/Linux there is no equivalent system setting and therefore a static -// color is all we need. -Color focusRingColor() +TransformationMatrix::operator XFORM() const { - static Color focusRingColor(229, 151, 0, 255); - return focusRingColor; + XFORM xform; + xform.eM11 = a(); + xform.eM12 = b(); + xform.eM21 = c(); + xform.eM22 = d(); + xform.eDx = e(); + xform.eDy = f(); + + return xform; } -#endif -} // namespace WebCore +} diff --git a/WebCore/platform/graphics/wince/FontCacheWince.cpp b/WebCore/platform/graphics/wince/FontCacheWince.cpp new file mode 100644 index 0000000..f67f1b4 --- /dev/null +++ b/WebCore/platform/graphics/wince/FontCacheWince.cpp @@ -0,0 +1,352 @@ +/* +* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. +* Copyright (C) 2007-2009 Torch Mobile, Inc. +* +* 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 "FontCache.h" + +#include "Font.h" +#include "FontData.h" +#include "SimpleFontData.h" +#include "UnicodeRange.h" +#include "wtf/OwnPtr.h" + +#include <windows.h> +#include <mlang.h> + +namespace WebCore { + +extern HDC g_screenDC; + +static IMultiLanguage *multiLanguage = 0; + +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) +static IMLangFontLink2* langFontLink = 0; +#else +static IMLangFontLink* langFontLink = 0; +#endif + +IMultiLanguage* getMultiLanguageInterface() +{ + if (!multiLanguage) + CoCreateInstance(CLSID_CMultiLanguage, 0, CLSCTX_INPROC_SERVER, IID_IMultiLanguage, (void**)&multiLanguage); + + return multiLanguage; +} + +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) +IMLangFontLink2* FontCache::getFontLinkInterface() +#else +IMLangFontLink* FontCache::getFontLinkInterface() +#endif +{ + if (!langFontLink) { + if (IMultiLanguage* mli = getMultiLanguageInterface()) + mli->QueryInterface(&langFontLink); + } + + return langFontLink; +} + +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) +static bool currentFontContainsCharacter(IMLangFontLink2* langFontLink, HDC hdc, UChar character) +{ + UINT unicodeRanges; + if (S_OK != langFontLink->GetFontUnicodeRanges(hdc, &unicodeRanges, 0)) + return false; + + static Vector<UNICODERANGE, 64> glyphsetBuffer; + glyphsetBuffer.resize(unicodeRanges); + + if (S_OK != langFontLink->GetFontUnicodeRanges(hdc, &unicodeRanges, glyphsetBuffer.data())) + return false; + + // FIXME: Change this to a binary search. (Yong Li: That's easy. But, is it guaranteed that the ranges are sorted?) + for (Vector<UNICODERANGE, 64>::const_iterator i = glyphsetBuffer.begin(); i != glyphsetBuffer.end(); ++i) { + if (i->wcTo >= character) + return i->wcFrom <= character; + } + + return false; +} +#else +static bool currentFontContainsCharacter(IMLangFontLink* langFontLink, HDC hdc, HFONT hfont, UChar character, const wchar_t* faceName) +{ + DWORD fontCodePages = 0, charCodePages = 0; + HRESULT result = langFontLink->GetFontCodePages(hdc, hfont, &fontCodePages); + if (result != S_OK) + return false; + result = langFontLink->GetCharCodePages(character, &charCodePages); + if (result != S_OK) + return false; + + fontCodePages |= FontPlatformData::getKnownFontCodePages(faceName); + if (fontCodePages & charCodePages) + return true; + + return false; +} +#endif + +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) +static HFONT createMLangFont(IMLangFontLink2* langFontLink, HDC hdc, DWORD codePageMask, UChar character = 0) +{ + HFONT mlangFont; + if (SUCCEEDED(langFontLink->MapFont(hdc, codePageMask, character, &mlangFont))) + return mlangFont; + + return 0; +} +#else +static HFONT createMLangFont(IMLangFontLink* langFontLink, HDC hdc, const FontPlatformData& refFont, DWORD codePageMask) +{ + HFONT mlangFont; + LRESULT result = langFontLink->MapFont(hdc, codePageMask, refFont.hfont(), &mlangFont); + + return result == S_OK ? mlangFont : 0; +} +#endif + +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; +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface(); +#else + IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface(); +#endif + 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; +} + + +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 = FontPlatformData::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::platformInit() +{ +} + +void FontCache::comInitialize() +{ +} + +void FontCache::comUninitialize() +{ + if (langFontLink) { + langFontLink->Release(); + langFontLink = 0; + } + if (multiLanguage) { + multiLanguage->Release(); + multiLanguage = 0; + } +} + +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) +{ + String familyName; + WCHAR name[LF_FACESIZE]; + + UChar character = characters[0]; + const FontPlatformData& origFont = font.primaryFont()->fontDataForCharacter(character)->platformData(); + unsigned unicodeRange = findCharUnicodeRange(character); + +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + if (IMLangFontLink2* langFontLink = getFontLinkInterface()) { +#else + if (IMLangFontLink* langFontLink = getFontLinkInterface()) { +#endif + HGDIOBJ oldFont = GetCurrentObject(g_screenDC, OBJ_FONT); + HFONT hfont = 0; + DWORD codePages = 0; + UINT codePage = 0; + // Try MLang font linking first. + langFontLink->GetCharCodePages(character, &codePages); + if (codePages && unicodeRange == 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; ++i) { +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + hfont = createMLangFont(langFontLink, g_screenDC, CJKCodePageMasks[i]); +#else + hfont = createMLangFont(langFontLink, g_screenDC, origFont, CJKCodePageMasks[i]); +#endif + if (!hfont) + continue; + + SelectObject(g_screenDC, hfont); + GetTextFace(g_screenDC, LF_FACESIZE, name); + + 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. +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + if (!currentFontContainsCharacter(langFontLink, g_screenDC, character)) { +#else + if (!currentFontContainsCharacter(langFontLink, g_screenDC, hfont, character, name)) { +#endif + SelectObject(g_screenDC, oldFont); + langFontLink->ReleaseFont(hfont); + hfont = 0; + continue; + } + } + break; + } + } else { +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + hfont = createMLangFont(langFontLink, g_screenDC, codePages, character); +#else + hfont = createMLangFont(langFontLink, g_screenDC, origFont, codePages); +#endif + SelectObject(g_screenDC, hfont); + GetTextFace(g_screenDC, LF_FACESIZE, name); + } + SelectObject(g_screenDC, oldFont); + + if (hfont) { + familyName = name; + langFontLink->ReleaseFont(hfont); + } else + FontPlatformData::mapKnownFont(codePages, familyName); + } + + if (familyName.isEmpty()) + familyName = FontPlatformData::defaultFontFamily(); + + if (!familyName.isEmpty()) { + // FIXME: temporary workaround for Thai font problem + FontDescription fontDescription(font.fontDescription()); + if (unicodeRange == cRangeThai && fontDescription.weight() > FontWeightNormal) + fontDescription.setWeight(FontWeightNormal); + + FontPlatformData* result = getCachedFontPlatformData(fontDescription, familyName); + if (result && result->hash() != origFont.hash()) { + if (SimpleFontData* fontData = getCachedFontData(result)) + return fontData; + } + } + + return 0; +} + +FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fontDesc) +{ + // 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. + return getCachedFontPlatformData(fontDesc, FontPlatformData::defaultFontFamily()); +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) +{ + FontPlatformData* result = new FontPlatformData(fontDescription, family); + return result; +} + +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) +{ + LOGFONT logFont; + logFont.lfCharSet = DEFAULT_CHARSET; + unsigned familyLength = std::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(g_screenDC, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0); + copyToVector(procData.m_traitsMasks, traitsMasks); +} + +} + diff --git a/WebCore/platform/graphics/wince/FontCustomPlatformData.cpp b/WebCore/platform/graphics/wince/FontCustomPlatformData.cpp new file mode 100644 index 0000000..7c6853c --- /dev/null +++ b/WebCore/platform/graphics/wince/FontCustomPlatformData.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile 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 "CachedFont.h" +#include "FontPlatformData.h" +#include <wtf/RandomNumber.h> + +namespace WebCore { + +static CustomFontCache* g_customFontCache = 0; + +bool renameFont(SharedBuffer* fontData, const String& fontName); + +FontCustomPlatformData::~FontCustomPlatformData() +{ + if (g_customFontCache && !m_name.isEmpty()) + g_customFontCache->unregisterFont(m_name); +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode renderingMode) +{ + FontDescription fontDesc; + fontDesc.setComputedSize(size); + fontDesc.setSpecifiedSize(size); + fontDesc.setItalic(italic); + fontDesc.setWeight(bold ? FontWeightBold : FontWeightNormal); + return FontPlatformData(fontDesc, m_name, false); +} + +// 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)); + + unsigned int* ptr = reinterpret_cast<unsigned int*>(fontUuid.data()); + for (int i = 0; i < sizeof(GUID) / sizeof(int) ; ++i) + *(ptr + i) = static_cast<unsigned int>(WTF::randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)); + + Vector<char> fontNameVector; + base64Encode(fontUuid, fontNameVector); + ASSERT(fontNameVector.size() < LF_FACESIZE); + String fontName(fontNameVector.data(), fontNameVector.size()); + return fontName.replace('/', '_'); +} + +FontCustomPlatformData* createFontCustomPlatformData(CachedFont* cachedFont) +{ + if (g_customFontCache && cachedFont->CachedResource::data()) { + String fontName = createUniqueFontName(); + if (renameFont(cachedFont->CachedResource::data(), fontName) && g_customFontCache->registerFont(fontName, cachedFont)) + return new FontCustomPlatformData(fontName); + } + return 0; +} + +} diff --git a/WebCore/platform/graphics/wince/FontCustomPlatformData.h b/WebCore/platform/graphics/wince/FontCustomPlatformData.h new file mode 100644 index 0000000..b1f64a0 --- /dev/null +++ b/WebCore/platform/graphics/wince/FontCustomPlatformData.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef FontCustomPlatformData_h +#define FontCustomPlatformData_h + +#include "FontRenderingMode.h" +#include "PlatformString.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + + class FontPlatformData; + class CachedFont; + + class CustomFontCache { + public: + virtual bool registerFont(const String& fontName, CachedFont*) = 0; + virtual void unregisterFont(const String& fontName) = 0; + }; + + struct FontCustomPlatformData : Noncopyable { + FontCustomPlatformData(const String& name) + : m_name(name) + { + } + + ~FontCustomPlatformData(); + + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); + String m_name; + }; + + FontCustomPlatformData* createFontCustomPlatformData(CachedFont*); + +} + +#endif diff --git a/WebCore/platform/graphics/wince/FontPlatformData.cpp b/WebCore/platform/graphics/wince/FontPlatformData.cpp new file mode 100644 index 0000000..f0db2ff --- /dev/null +++ b/WebCore/platform/graphics/wince/FontPlatformData.cpp @@ -0,0 +1,532 @@ +/* + * 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 "FontPlatformData.h" + +#include "Font.h" +#include "FontCache.h" +#include "FontData.h" +#include "PlatformString.h" +#include "SimpleFontData.h" +#include "UnicodeRange.h" +#include "wtf/OwnPtr.h" + +#include <windows.h> +#include <mlang.h> + +namespace WebCore { + +extern HDC g_screenDC; + +static wchar_t songTiStr[] = { 0x5b8b, 0x4f53, 0 }; +static wchar_t heiTiStr[] = { 0x9ed1, 0x4f53, 0 }; + +class FontFamilyCodePageInfo { +public: + FontFamilyCodePageInfo() + : m_codePage(0), m_codePages(0) + { + } + FontFamilyCodePageInfo(const wchar_t* family, UINT codePage) + : m_family(family), m_codePage(codePage), m_codePages(0) + { + } + DWORD codePages() const + { + if (!m_codePages) { +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + if (IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface()) + langFontLink->CodePageToCodePages(m_codePage, &m_codePages); +#else + if (IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface()) + langFontLink->CodePageToCodePages(m_codePage, &m_codePages); +#endif + } + return m_codePages; + } + + String m_family; + UINT m_codePage; +private: + mutable DWORD m_codePages; +}; + +class FontFamilyChecker { +public: + FontFamilyChecker(const wchar_t* family) + : m_exists(false) + { + EnumFontFamilies(g_screenDC, family, enumFontFamProc, (LPARAM)this); + } + bool isSupported() const { return m_exists; } +private: + bool m_exists; + static int CALLBACK enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam); +}; + +class ValidFontFamilyFinder { +public: + ValidFontFamilyFinder() + { + EnumFontFamilies(g_screenDC, 0, enumFontFamProc, (LPARAM)this); + } + const String& family() const { return m_family; } +private: + String m_family; + static int CALLBACK enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam); +}; + +class FixedSizeFontData: public RefCounted<FixedSizeFontData> { +public: + LOGFONT m_font; + OwnPtr<HFONT> m_hfont; + TEXTMETRIC m_metrics; + DWORD m_codePages; + unsigned m_weight; + bool m_italic; + + static PassRefPtr<FixedSizeFontData> create(const AtomicString& family, unsigned weight, bool italic); +private: + FixedSizeFontData() + : m_codePages(0) + , m_weight(0) + , m_italic(false) + { + memset(&m_font, 0, sizeof(m_font)); + memset(&m_metrics, 0, sizeof(m_metrics)); + } +}; + +struct FixedSizeFontDataKey { + FixedSizeFontDataKey(const AtomicString& family = AtomicString(), unsigned weight = 0, bool italic = false) + : m_family(family) + , m_weight(weight) + , m_italic(italic) + { + } + + FixedSizeFontDataKey(WTF::HashTableDeletedValueType) : m_weight(-2) { } + bool isHashTableDeletedValue() const { return m_weight == -2; } + + bool operator==(const FixedSizeFontDataKey& other) const + { + return equalIgnoringCase(m_family, other.m_family) + && m_weight == other.m_weight + && m_italic == other.m_italic; + } + + AtomicString m_family; + unsigned m_weight; + bool m_italic; +}; + +struct FixedSizeFontDataKeyHash { + static unsigned hash(const FixedSizeFontDataKey& font) + { + unsigned hashCodes[] = { + CaseFoldingHash::hash(font.m_family), + font.m_weight, + // static_cast<unsigned>(font.m_italic); + }; + return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); + } + + static bool equal(const FixedSizeFontDataKey& a, const FixedSizeFontDataKey& b) + { + return a == b; + } + + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +struct FixedSizeFontDataKeyTraits : WTF::GenericHashTraits<FixedSizeFontDataKey> { + static const bool emptyValueIsZero = true; + static const FixedSizeFontDataKey& emptyValue() + { + DEFINE_STATIC_LOCAL(FixedSizeFontDataKey, key, (nullAtom)); + return key; + } + static void constructDeletedValue(FixedSizeFontDataKey& slot) + { + new (&slot) FixedSizeFontDataKey(WTF::HashTableDeletedValue); + } + static bool isDeletedValue(const FixedSizeFontDataKey& value) + { + return value.isHashTableDeletedValue(); + } +}; + +int CALLBACK FontFamilyChecker::enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam) +{ + ((FontFamilyChecker*)lParam)->m_exists = true; + return 0; +} + +int CALLBACK ValidFontFamilyFinder::enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam) +{ + if (lpelf->lfCharSet != SYMBOL_CHARSET) { + ((ValidFontFamilyFinder*)lParam)->m_family = String(lpelf->lfFaceName); + return 0; + } + return 1; +} + +typedef Vector<FontFamilyCodePageInfo> KnownFonts; +static KnownFonts& knownFonts() +{ + static KnownFonts fonts; + static bool firstTime = true; + if (firstTime) { + firstTime = false; + if (FontPlatformData::isSongTiSupported()) + fonts.append(FontFamilyCodePageInfo(songTiStr, 936)); + } + return fonts; +} + +static String getDefaultFontFamily() +{ + if (FontFamilyChecker(L"Tahoma").isSupported()) + return String(L"Tahoma"); + + bool good = false; + String family; + HKEY key; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\GDI\\SysFnt", 0, 0, &key) == ERROR_SUCCESS) { + DWORD maxlen, type; + if (RegQueryValueEx(key, L"Nm", 0, &type, 0, &maxlen) == ERROR_SUCCESS && type == REG_SZ) { + ++maxlen; + if (wchar_t* buffer = new wchar_t[maxlen]) { + if (RegQueryValueEx(key, L"Nm", 0, &type, (LPBYTE)buffer, &maxlen) == ERROR_SUCCESS) { + family = String(buffer, maxlen); + good = true; + } + delete[] buffer; + } + } + RegCloseKey(key); + } + if (good) + return family; + + return ValidFontFamilyFinder().family(); +} + +typedef HashMap<FixedSizeFontDataKey, RefPtr<FixedSizeFontData>, FixedSizeFontDataKeyHash, FixedSizeFontDataKeyTraits> FixedSizeFontCache; +FixedSizeFontCache g_fixedSizeFontCache; + +PassRefPtr<FixedSizeFontData> FixedSizeFontData::create(const AtomicString& family, unsigned weight, bool italic) +{ + FixedSizeFontData* fontData = new FixedSizeFontData(); + + fontData->m_weight = weight; + fontData->m_italic = italic; + + LOGFONT& winFont = fontData->m_font; + // The size here looks unusual. The negative number is intentional. + winFont.lfHeight = -72; + winFont.lfWidth = 0; + winFont.lfEscapement = 0; + winFont.lfOrientation = 0; + winFont.lfUnderline = false; + winFont.lfStrikeOut = false; + winFont.lfCharSet = DEFAULT_CHARSET; + winFont.lfOutPrecision = OUT_DEFAULT_PRECIS; + winFont.lfQuality = CLEARTYPE_QUALITY; //DEFAULT_QUALITY; + winFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + winFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + winFont.lfItalic = italic; + winFont.lfWeight = FontPlatformData::adjustedGDIFontWeight(weight, family); + + int len = std::min(family.length(), (unsigned int)LF_FACESIZE - 1); + wmemcpy(winFont.lfFaceName, family.characters(), len); + winFont.lfFaceName[len] = L'\0'; + + fontData->m_hfont.set(CreateFontIndirect(&winFont)); + + HGDIOBJ oldFont = SelectObject(g_screenDC, fontData->m_hfont.get()); + + GetTextMetrics(g_screenDC, &fontData->m_metrics); + +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + if (IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface()) { +#else + if (IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface()) { +#endif + langFontLink->GetFontCodePages(g_screenDC, fontData->m_hfont.get(), &fontData->m_codePages); + fontData->m_codePages |= FontPlatformData::getKnownFontCodePages(winFont.lfFaceName); + } + + SelectObject(g_screenDC, oldFont); + + return adoptRef(fontData); +} + +static PassRefPtr<FixedSizeFontData> createFixedSizeFontData(const AtomicString& family, unsigned weight, bool italic) +{ + FixedSizeFontDataKey key(family, weight, italic); + pair<FixedSizeFontCache::iterator, bool> result = g_fixedSizeFontCache.add(key, RefPtr<FixedSizeFontData>()); + if (result.second) + result.first->second = FixedSizeFontData::create(family, weight, italic); + + return result.first->second; +} + +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]; +} + +class FontPlatformPrivateData { +public: + int m_reference; + RefPtr<FixedSizeFontData> m_rootFontData; + AtomicString m_family; + FontDescription m_fontDescription; + OwnPtr<HFONT> m_hfontScaled; + int m_size; + long m_fontScaledWidth; + long m_fontScaledHeight; + bool m_disabled; + FontPlatformPrivateData(int size, unsigned weight) + : m_reference(1) + , m_family(FontPlatformData::defaultFontFamily()) + , m_size(size) + , m_fontScaledWidth(0) + , m_fontScaledHeight(0) + , m_disabled(false) + { + m_rootFontData = createFixedSizeFontData(m_family, weight, false); + } + FontPlatformPrivateData(const FontDescription& fontDescription, const AtomicString& family) + : m_reference(1) + , m_size(fontDescription.computedPixelSize()) + , m_fontDescription(fontDescription) + , m_family(family) + , m_fontScaledWidth(0) + , m_fontScaledHeight(0) + , m_disabled(!fontDescription.specifiedSize()) + { + m_rootFontData = FixedSizeFontData::create(family, toGDIFontWeight(fontDescription.weight()), fontDescription.italic()); + } +}; + +FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& desiredFamily, bool useDefaultFontIfNotPresent) +{ + String family(desiredFamily); + if (!equalIgnoringCase(family, defaultFontFamily()) && !FontFamilyChecker(family.charactersWithNullTermination()).isSupported()) { + if (equalIgnoringCase(family, String(heiTiStr)) && isSongTiSupported()) + family = String(songTiStr); + else if (useDefaultFontIfNotPresent) + family = defaultFontFamily(); + } + + m_private = new FontPlatformPrivateData(fontDescription, family); +} + +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) +{ + if (!size) + m_private = 0; + else + m_private = new FontPlatformPrivateData((int)(size + 0.5), bold ? FW_BOLD : FW_NORMAL); +} + +FontPlatformData::~FontPlatformData() +{ + if (isValid() && !--m_private->m_reference) { + if (m_private->m_rootFontData->refCount() == 2) { + FixedSizeFontDataKey key(m_private->m_family, m_private->m_rootFontData->m_weight, m_private->m_rootFontData->m_italic); + g_fixedSizeFontCache.remove(key); + } + delete m_private; + } +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& o) +{ + if (isValid() && !--m_private->m_reference) + delete m_private; + + if (m_private = o.m_private) + ++m_private->m_reference; + + return *this; +} + +HFONT FontPlatformData::hfont() const +{ + if (!isValid()) + return 0; + + if (m_private->m_disabled) + return 0; + + if (!m_private->m_rootFontData->m_hfont) + m_private->m_rootFontData->m_hfont.set(CreateFontIndirect(&m_private->m_rootFontData->m_font)); + + return m_private->m_rootFontData->m_hfont.get(); +} + +HFONT FontPlatformData::getScaledFontHandle(int height, int width) const +{ + if (!isValid() || m_private->m_disabled) + return 0; + + if (!m_private->m_hfontScaled || m_private->m_fontScaledHeight != height || m_private->m_fontScaledWidth != width) { + m_private->m_fontScaledHeight = height; + m_private->m_fontScaledWidth = width; + LOGFONT font = m_private->m_rootFontData->m_font; + font.lfHeight = -height; + font.lfWidth = width; + m_private->m_hfontScaled.set(CreateFontIndirect(&font)); + } + + return m_private->m_hfontScaled.get(); +} + +bool FontPlatformData::discardFontHandle() +{ + if (!isValid()) + return false; + + if (m_private->m_rootFontData->m_hfont) { + m_private->m_rootFontData->m_hfont.set(0); + return true; + } + + if (m_private->m_hfontScaled) { + m_private->m_hfontScaled.set(0); + return true; + } + return false; +} + +const TEXTMETRIC& FontPlatformData::metrics() const +{ + return m_private->m_rootFontData->m_metrics; +} + +bool FontPlatformData::isSystemFont() const +{ + return false; +} + +int FontPlatformData::size() const +{ + return m_private->m_size; +} + +const FontDescription& FontPlatformData::fontDescription() const +{ + return m_private->m_fontDescription; +} + +const AtomicString& FontPlatformData::family() const +{ + return m_private->m_family; +} + +const LOGFONT& FontPlatformData::logFont() const +{ + return m_private->m_rootFontData->m_font; +} + +int FontPlatformData::averageCharWidth() const +{ + return (m_private->m_rootFontData->m_metrics.tmAveCharWidth * size() + 36) / 72; +} + +bool FontPlatformData::isDisabled() const +{ + return !isValid() || m_private->m_disabled; +} + +DWORD FontPlatformData::codePages() const +{ + return m_private->m_rootFontData->m_codePages; +} + +bool FontPlatformData::isSongTiSupported() +{ + static bool exists = FontFamilyChecker(songTiStr).isSupported(); + return exists; +} + +bool FontPlatformData::mapKnownFont(DWORD codePages, String& family) +{ + KnownFonts& fonts = knownFonts(); + for (KnownFonts::iterator i = fonts.begin(); i != fonts.end(); ++i) { + if (i->codePages() & codePages) { + family = i->m_family; + return true; + } + } + return false; +} + +DWORD FontPlatformData::getKnownFontCodePages(const wchar_t* family) +{ + KnownFonts& fonts = knownFonts(); + for (KnownFonts::iterator i = fonts.begin(); i != fonts.end(); ++i) { + if (equalIgnoringCase(i->m_family, String(family))) + return i->codePages(); + } + return 0; +} + +const String& FontPlatformData::defaultFontFamily() +{ + static String family(getDefaultFontFamily()); + return family; +} + +LONG FontPlatformData::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; +} + +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + +} diff --git a/WebCore/platform/graphics/wince/FontPlatformData.h b/WebCore/platform/graphics/wince/FontPlatformData.h new file mode 100644 index 0000000..77803d3 --- /dev/null +++ b/WebCore/platform/graphics/wince/FontPlatformData.h @@ -0,0 +1,90 @@ +/* + * This file is part of the internal font implementation. It should not be included by anyone other than + * FontMac.cpp, FontWin.cpp and Font.cpp. + * + * Copyright (C) 2006, 2007 Apple Inc. + * Copyright (C) 2007-2008 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. + * + */ + +#ifndef FontPlatformDataWince_H +#define FontPlatformDataWince_H + +#include "FontDescription.h" +#include "StringImpl.h" +#include <wtf/Noncopyable.h> + +typedef struct tagTEXTMETRICW TEXTMETRIC; +typedef struct tagLOGFONTW LOGFONT; + +namespace WebCore { + + class FontPlatformPrivateData; + class String; + + class FontPlatformData { + + public: + + FontPlatformData(): m_private(0) {} + FontPlatformData(float size, bool bold, bool oblique); + + // Used for deleted values in the font cache's hash tables. + FontPlatformData(WTF::HashTableDeletedValueType) : m_private((FontPlatformPrivateData*)1) {} + bool isHashTableDeletedValue() const { return (unsigned)m_private == 1; } + + FontPlatformData(const FontDescription& fontDescription, const AtomicString& family, bool useDefaultFontIfNotPresent = true); + + ~FontPlatformData(); + + FontPlatformData(const FontPlatformData& o) : m_private(0) { operator=(o); } + FontPlatformData& operator=(const FontPlatformData& o); + + int isValid() const { return reinterpret_cast<unsigned>(m_private) & ~1; } + HFONT hfont() const; + const TEXTMETRIC& metrics() const; + bool isSystemFont() const; + int size() const; + unsigned hash() const { return (unsigned)m_private; } + const FontDescription& fontDescription() const; + const AtomicString& family() const; + bool operator==(const FontPlatformData& other) const { return m_private == other.m_private; } + HFONT getScaledFontHandle(int height, int width) const; + const LOGFONT& logFont() const; + int averageCharWidth() const; + bool isDisabled() const; + bool discardFontHandle(); + DWORD codePages() const; + + static bool isSongTiSupported(); + static bool mapKnownFont(DWORD codePages, String& family); + static DWORD getKnownFontCodePages(const wchar_t* family); + static const String& defaultFontFamily(); + static LONG adjustedGDIFontWeight(LONG gdiFontWeight, const String& family); + +#ifndef NDEBUG + String description() const; +#endif + + private: + FontPlatformPrivateData* m_private; + }; + +} + +#endif diff --git a/WebCore/platform/graphics/wince/FontWince.cpp b/WebCore/platform/graphics/wince/FontWince.cpp new file mode 100644 index 0000000..d00336b --- /dev/null +++ b/WebCore/platform/graphics/wince/FontWince.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * 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 "FloatRect.h" +#include "FontCache.h" +#include "FontData.h" +#include "FontFallbackList.h" +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "IntRect.h" +#include "NotImplemented.h" +#include "TransformationMatrix.h" +#include "WidthIterator.h" +#include <wtf/MathExtras.h> +#include <wtf/OwnPtr.h> +#include <wtf/unicode/Unicode.h> + +#include <windows.h> + +using namespace WTF::Unicode; + +namespace WebCore { + +HDC g_screenDC = GetDC(0); + +class ScreenDcReleaser { +public: + ~ScreenDcReleaser() + { + ReleaseDC(0, g_screenDC); + } +}; + +ScreenDcReleaser releaseScreenDc; + +void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, const FloatPoint& point) const +{ + graphicsContext->drawText(fontData, glyphBuffer, from, numGlyphs, point); +} + +class TextRunComponent { +public: + TextRunComponent() : m_textRun(0, 0) {} + TextRunComponent(const UChar *start, int length, const TextRun& parentTextRun, const Font &font, int offset); + TextRunComponent(int spaces, const Font &font, int offset); + ~TextRunComponent() { m_textRun; } + + bool isSpace() const { return m_spaces; } + int textLength() const { return m_spaces ? m_spaces : m_textRun.length(); } + + TextRun m_textRun; + float m_width; + int m_offset; + int m_spaces; +}; + +TextRunComponent::TextRunComponent(const UChar *start, int length, const TextRun& parentTextRun, const Font &font, int o) + : m_textRun(start, length, parentTextRun.allowTabs(), 0, 0 + , parentTextRun.rtl() + , parentTextRun.directionalOverride() + , parentTextRun.applyRunRounding() + , parentTextRun.applyWordRounding()) + , m_offset(o) + , m_spaces(0) +{ + WidthIterator it(&font, m_textRun); + it.advance(m_textRun.length(), 0); + m_width = it.m_runWidthSoFar; +} + +TextRunComponent::TextRunComponent(int s, const Font &font, int o) + : m_textRun(0, 0) + , m_offset(o) + , m_spaces(s) +{ + m_width = s * font.primaryFont()->widthForGlyph(' '); +} + +typedef Vector<TextRunComponent, 128> TextRunComponents; + +static int generateComponents(TextRunComponents* components, const Font &font, const TextRun &run) +{ + int letterSpacing = font.letterSpacing(); + int wordSpacing = font.wordSpacing(); + int padding = run.padding(); + int numSpaces = 0; + if (padding) { + for (int i = 0; i < run.length(); i++) + if (Font::treatAsSpace(run[i])) + ++numSpaces; + } + + int offset = 0; + if (letterSpacing) { + // need to draw every letter on it's own + int start = 0; + if (Font::treatAsSpace(run[0])) { + int add = 0; + if (numSpaces) { + add = padding/numSpaces; + padding -= add; + --numSpaces; + } + components->append(TextRunComponent(1, font, offset)); + offset += add + letterSpacing + components->last().m_width; + start = 1; + } + for (int i = 1; i < run.length(); ++i) { + uint ch = run[i]; + if (isHighSurrogate(ch) && isLowSurrogate(run[i-1])) + ch = surrogateToUcs4(ch, run[i-1]); + if (isLowSurrogate(ch) || category(ch) == Mark_NonSpacing) + continue; + if (Font::treatAsSpace(run[i])) { + int add = 0; + if (i - start > 0) { + components->append(TextRunComponent(run.characters() + start, i - start, + run, font, offset)); + offset += components->last().m_width + letterSpacing; + } + if (numSpaces) { + add = padding/numSpaces; + padding -= add; + --numSpaces; + } + components->append(TextRunComponent(1, font, offset)); + offset += wordSpacing + add + components->last().m_width + letterSpacing; + start = i + 1; + continue; + } + if (i - start > 0) { + components->append(TextRunComponent(run.characters() + start, i - start, + run, + font, offset)); + offset += components->last().m_width + letterSpacing; + } + start = i; + } + if (run.length() - start > 0) { + components->append(TextRunComponent(run.characters() + start, run.length() - start, + run, + font, offset)); + offset += components->last().m_width; + } + offset += letterSpacing; + } else { + int start = 0; + for (int i = 0; i < run.length(); ++i) { + if (Font::treatAsSpace(run[i])) { + if (i - start > 0) { + components->append(TextRunComponent(run.characters() + start, i - start, + run, + font, offset)); + offset += components->last().m_width; + } + int add = 0; + if (numSpaces) { + add = padding/numSpaces; + padding -= add; + --numSpaces; + } + components->append(TextRunComponent(1, font, offset)); + offset += add + components->last().m_width; + if (i) + offset += wordSpacing; + start = i + 1; + } + } + if (run.length() - start > 0) { + components->append(TextRunComponent(run.characters() + start, run.length() - start, + run, + font, offset)); + offset += components->last().m_width; + } + } + return offset; +} + +void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, + int from, int to) const +{ + if (to < 0) + to = run.length(); + if (from < 0) + from = 0; + + TextRunComponents components; + int w = generateComponents(&components, *this, run); + + int curPos = 0; + for (int i = 0; i < (int)components.size(); ++i) { + const TextRunComponent& comp = components.at(i); + int len = comp.textLength(); + int curEnd = curPos + len; + if (curPos < to && from < curEnd && !comp.isSpace()) { + FloatPoint pt = point; + if (run.rtl()) + pt.setX(point.x() + w - comp.m_offset - comp.m_width); + else + pt.setX(point.x() + comp.m_offset); + drawSimpleText(context, comp.m_textRun, pt, from - curPos, std::min(to, curEnd) - curPos); + } + curPos += len; + if (from < curPos) + from = curPos; + } +} + +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts) const +{ + TextRunComponents components; + int w = generateComponents(&components, *this, run); + return w; +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool includePartialGlyphs) const +{ + TextRunComponents components; + int w = generateComponents(&components, *this, run); + + if (position >= w) + return run.length(); + + int offset = 0; + if (run.rtl()) { + for (size_t i = 0; i < components.size(); ++i) { + const TextRunComponent& comp = components.at(i); + int xe = w - comp.m_offset; + int xs = xe - comp.m_width; + if (position >= xs) + return offset + (comp.isSpace() + ? static_cast<int>((position - xe) * comp.m_spaces / std::max(1.f, comp.m_width) + 0.5) + : offsetForPositionForSimpleText(comp.m_textRun, position - xs, includePartialGlyphs)); + + offset += comp.textLength(); + } + } else { + for (size_t i = 0; i < components.size(); ++i) { + const TextRunComponent& comp = components.at(i); + int xs = comp.m_offset; + int xe = xs + comp.m_width; + if (position <= xe) { + if (position - xs >= xe) + return offset + comp.textLength(); + return offset + (comp.isSpace() + ? static_cast<int>((position - xs) * comp.m_spaces / std::max(1.f, comp.m_width) + 0.5) + : offsetForPositionForSimpleText(comp.m_textRun, position - xs, includePartialGlyphs)); + } + offset += comp.textLength(); + } + } + return run.length(); +} + + +static float cursorToX(const Font* font, const TextRunComponents& components, int width, bool rtl, int cursor) +{ + int start = 0; + for (size_t i = 0; i < components.size(); ++i) { + const TextRunComponent& comp = components.at(i); + if (start + comp.textLength() <= cursor) { + start += comp.textLength(); + continue; + } + int xs = comp.m_offset; + if (rtl) + xs = width - xs - comp.m_width; + + int pos = cursor - start; + if (comp.isSpace()) { + if (rtl) + pos = comp.textLength() - pos; + return xs + pos * comp.m_width / comp.m_spaces; + } + WidthIterator it(font, comp.m_textRun); + it.advance(pos); + return xs + it.m_runWidthSoFar; + } + return width; +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& pt, + int h, int from, int to) const +{ + TextRunComponents components; + int w = generateComponents(&components, *this, run); + + if (!from && to == run.length()) + return FloatRect(pt.x(), pt.y(), w, h); + + float x1 = cursorToX(this, components, w, run.rtl(), from); + float x2 = cursorToX(this, components, w, run.rtl(), to); + if (x2 < x1) + std::swap(x1, x2); + + return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h); +} + +bool Font::canReturnFallbackFontsForComplexText() +{ + return false; +} + +} diff --git a/WebCore/platform/graphics/wince/GlyphPageTreeNodeWince.cpp b/WebCore/platform/graphics/wince/GlyphPageTreeNodeWince.cpp new file mode 100644 index 0000000..27c4e15 --- /dev/null +++ b/WebCore/platform/graphics/wince/GlyphPageTreeNodeWince.cpp @@ -0,0 +1,79 @@ +/* + * 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 "GlyphPageTreeNode.h" + +#include "Font.h" +#include "FontCache.h" +#include "FontData.h" +#include "SimpleFontData.h" + +namespace WebCore { + +DWORD getKnownFontCodePages(const wchar_t* family); + +typedef unsigned (*funcGetCharCodePages)(unsigned short c, unsigned& lastPos); +funcGetCharCodePages getCharCodePages = 0; + +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + if (length != bufferLength) + return false; + + if (fontData->platformData().hfont()) { + DWORD fontCodePages = fontData->platformData().codePages(); + if (fontCodePages) { + if (getCharCodePages) { + unsigned lastPos = 0; + for (unsigned i = 0; i < bufferLength; ++i) { + DWORD actualCodePages = getCharCodePages(buffer[i], lastPos); + if (!actualCodePages || (actualCodePages & fontCodePages)) + setGlyphDataForIndex(offset + i, buffer[i], fontData); + else + setGlyphDataForIndex(offset + i, buffer[i], 0); + } + return true; +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + } else if (IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface()) { +#else + } else if (IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface()) { +#endif + for (unsigned i = 0; i < bufferLength; ++i) { + DWORD actualCodePages; + langFontLink->GetCharCodePages(buffer[i], &actualCodePages); + if (!actualCodePages || (actualCodePages & fontCodePages)) + setGlyphDataForIndex(offset + i, buffer[i], fontData); + else + setGlyphDataForIndex(offset + i, buffer[i], 0); + } + return true; + } + } + } + + for (unsigned i = 0; i < length; ++i) + setGlyphDataForIndex(offset + i, buffer[i], fontData); + + return true; +} + +} + diff --git a/WebCore/platform/graphics/wince/GraphicsContextWince.cpp b/WebCore/platform/graphics/wince/GraphicsContextWince.cpp new file mode 100644 index 0000000..c114c0e --- /dev/null +++ b/WebCore/platform/graphics/wince/GraphicsContextWince.cpp @@ -0,0 +1,1930 @@ +/* + * 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 "GraphicsContext.h" + +#include "CharacterNames.h" +#include "GlyphBuffer.h" +#include "Gradient.h" +#include "GraphicsContextPrivate.h" +#include "NotImplemented.h" +#include "Path.h" +#include "PlatformPathWince.h" +#include "SharedBitmap.h" +#include "SimpleFontData.h" +#include "TransformationMatrix.h" +#include <wtf/OwnPtr.h> + +#include <windows.h> + +namespace WebCore { + +typedef void (*FuncGradientFillRectLinear)(HDC hdc, const IntRect& r, const IntPoint& p0, const IntPoint& p1, const Vector<Gradient::ColorStop>& stops); +typedef void (*FuncGradientFillRectRadial)(HDC hdc, const IntRect& r, const IntPoint& p0, const IntPoint& p1, float r0, float r1, const Vector<Gradient::ColorStop>& stops); +FuncGradientFillRectLinear g_linearGradientFiller = 0; +FuncGradientFillRectRadial g_radialGradientFiller = 0; + +static inline bool isZero(double d) +{ + return d > 0 ? d <= 1.E-10 : d >= -1.E-10; +} + +// stableRound rounds -0.5 to 0, where lround rounds -0.5 to -1. +static inline int stableRound(double d) +{ + if (d > 0) + return static_cast<int>(d + 0.5); + + int i = static_cast<int>(d); + return i - d > 0.5 ? i - 1 : i; +} + +// Unlike enclosingIntRect(), this function does strict rounding. +static inline IntRect roundRect(const FloatRect& r) +{ + return IntRect(stableRound(r.x()), stableRound(r.y()), stableRound(r.right()) - stableRound(r.x()), stableRound(r.bottom()) - stableRound(r.y())); +} + +// Rotation transformation +class RotationTransform { +public: + RotationTransform() + : m_cosA(1.) + , m_sinA(0.) + , m_preShiftX(0) + , m_preShiftY(0) + , m_postShiftX(0) + , m_postShiftY(0) + { + } + RotationTransform operator-() const + { + RotationTransform rtn; + rtn.m_cosA = m_cosA; + rtn.m_sinA = -m_sinA; + rtn.m_preShiftX = m_postShiftX; + rtn.m_preShiftY = m_postShiftY; + rtn.m_postShiftX = m_preShiftX; + rtn.m_postShiftY = m_preShiftY; + return rtn; + } + void map(double x1, double y1, double* x2, double* y2) const + { + x1 += m_preShiftX; + y1 += m_preShiftY; + *x2 = x1 * m_cosA + y1 * m_sinA + m_postShiftX; + *y2 = y1 * m_cosA - x1 * m_sinA + m_postShiftY; + } + void map(int x1, int y1, int* x2, int* y2) const + { + x1 += m_preShiftX; + y1 += m_preShiftY; + *x2 = stableRound(x1 * m_cosA + y1 * m_sinA) + m_postShiftX; + *y2 = stableRound(y1 * m_cosA - x1 * m_sinA) + m_postShiftY; + } + + double m_cosA; + double m_sinA; + int m_preShiftX; + int m_preShiftY; + int m_postShiftX; + int m_postShiftY; +}; + +template<class T> static inline IntPoint mapPoint(const IntPoint& p, const T& t) +{ + int x, y; + t.map(p.x(), p.y(), &x, &y); + return IntPoint(x, y); +} + +template<class T> static inline FloatPoint mapPoint(const FloatPoint& p, const T& t) +{ + double x, y; + t.map(p.x(), p.y(), &x, &y); + return FloatPoint(static_cast<float>(x), static_cast<float>(y)); +} + +template<class Transform, class Rect, class Value> static inline Rect mapRect(const Rect& rect, const Transform& transform) +{ + Value x[4], y[4]; + Value l, t, r, b; + r = rect.right() - 1; + b = rect.bottom() - 1; + transform.map(rect.x(), rect.y(), x, y); + transform.map(rect.x(), b, x + 1, y + 1); + transform.map(r, b, x + 2, y + 2); + transform.map(r, rect.y(), x + 3, y + 3); + l = r = x[3]; + t = b = y[3]; + for (int i = 0; i < 3; ++i) { + if (x[i] < l) + l = x[i]; + else if (x[i] > r) + r = x[i]; + + if (y[i] < t) + t = y[i]; + else if (y[i] > b) + b = y[i]; + } + + return IntRect(l, t, r - l + 1, b - t + 1); +} + +template<class T> static inline IntRect mapRect(const IntRect& rect, const T& transform) +{ + return mapRect<T, IntRect, int>(rect, transform); +} + +template<class T> static inline FloatRect mapRect(const FloatRect& rect, const T& transform) +{ + return mapRect<T, FloatRect, double>(rect, transform); +} + +class GraphicsContextPlatformPrivateData { +public: + GraphicsContextPlatformPrivateData() + : m_transform() + , m_opacity(1.0) + { + } + + TransformationMatrix m_transform; + float m_opacity; + Vector<Path> m_paths; +}; + +enum AlphaPaintType { + AlphaPaintNone, + AlphaPaintImage, + AlphaPaintOther, +}; + +class GraphicsContextPlatformPrivate : public GraphicsContextPlatformPrivateData { +public: + GraphicsContextPlatformPrivate(HDC dc) + : m_dc(dc) + { + } + ~GraphicsContextPlatformPrivate() + { + while (!m_backupData.isEmpty()) + restore(); + } + + IntPoint origin() const + { + return IntPoint(stableRound(-m_transform.e()), stableRound(-m_transform.f())); + } + + void translate(float x, float y) + { + m_transform.translate(x, y); + } + + void scale(const FloatSize& size) + { + m_transform.scaleNonUniform(size.width(), size.height()); + } + + void rotate(float radians) + { + m_transform.rotate(rad2deg(radians)); + } + + void concatCTM(const TransformationMatrix& transform) + { + m_transform = transform * m_transform; + } + + IntRect mapRect(const IntRect& rect) const + { + return m_transform.mapRect(rect); + } + + FloatRect mapRect(const FloatRect& rect) const + { + return m_transform.mapRect(rect); + } + + IntPoint mapPoint(const IntPoint& point) const + { + return m_transform.mapPoint(point); + } + + FloatPoint mapPoint(const FloatPoint& point) const + { + return m_transform.mapPoint(point); + } + + FloatSize mapSize(const FloatSize& size) const + { + double w, h; + m_transform.map(size.width(), size.height(), w, h); + return FloatSize(static_cast<float>(w), static_cast<float>(h)); + } + + void save() + { + if (m_dc) + SaveDC(m_dc); + + m_backupData.append(*static_cast<GraphicsContextPlatformPrivateData*>(this)); + } + + void restore() + { + if (m_backupData.isEmpty()) + return; + + if (m_dc) + RestoreDC(m_dc, -1); + + GraphicsContextPlatformPrivateData::operator=(m_backupData.last()); + m_backupData.removeLast(); + } + + bool hasAlpha() const { return m_bitmap && m_bitmap->hasAlpha(); } + + PassRefPtr<SharedBitmap> getTransparentLayerBitmap(IntRect& origRect, AlphaPaintType alphaPaint, RECT& bmpRect, bool checkClipBox, bool force) const + { + if (m_opacity <= 0) + return 0; + + if (force || m_opacity < 1.) { + if (checkClipBox) { + RECT clipBox; + int clipType = GetClipBox(m_dc, &clipBox); + if (clipType == SIMPLEREGION || clipType == COMPLEXREGION) + origRect.intersect(clipBox); + if (origRect.isEmpty()) + return 0; + } + + RefPtr<SharedBitmap> bmp = SharedBitmap::createInstance(alphaPaint == AlphaPaintNone, origRect.width(), origRect.height(), false); + SetRect(&bmpRect, 0, 0, origRect.width(), origRect.height()); + if (bmp) { + switch (alphaPaint) { + case AlphaPaintNone: + case AlphaPaintImage: + { + SharedBitmap::DCHolder dc(bmp.get()); + if (dc.get()) { + BitBlt(dc.get(), 0, 0, origRect.width(), origRect.height(), m_dc, origRect.x(), origRect.y(), SRCCOPY); + if (bmp->is32bit() && (!m_bitmap || m_bitmap->is16bit())) { + // Set alpha channel + unsigned* pixels = (unsigned*)bmp->bytes(); + const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels(); + while (pixels < pixelsEnd) { + *pixels |= 0xFF000000; + ++pixels; + } + } + return bmp; + } + } + break; + //case AlphaPaintOther: + default: + memset(bmp->bytes(), 0xFF, bmp->bitmapInfo().numPixels() * 4); + return bmp; + break; + } + } + } + + bmpRect = origRect; + return 0; + } + + void paintBackTransparentLayerBitmap(HDC hdc, SharedBitmap* bmp, const IntRect& origRect, AlphaPaintType alphaPaint, const RECT& bmpRect) + { + if (hdc == m_dc) + return; + +#if !defined(NO_ALPHABLEND) + if (alphaPaint == AlphaPaintOther) { + ASSERT(bmp && bmp->bytes() && bmp->is32bit()); + unsigned* pixels = (unsigned*)bmp->bytes(); + const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels(); + while (pixels < pixelsEnd) { + *pixels ^= 0xFF000000; + ++pixels; + } + } + if (m_opacity < 1. || alphaPaint == AlphaPaintOther) { + const BLENDFUNCTION blend = { AC_SRC_OVER, 0 + , m_opacity >= 1. ? 255 : (BYTE)(m_opacity * 255) + , alphaPaint == AlphaPaintNone ? 0 : AC_SRC_ALPHA }; + AlphaBlend(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, blend); + } else +#endif + StretchBlt(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, SRCCOPY); + } + + HDC m_dc; + RefPtr<SharedBitmap> m_bitmap; + Vector<GraphicsContextPlatformPrivateData> m_backupData; +}; + +static HPEN createPen(const Color& col, double fWidth, StrokeStyle style) +{ + int width = stableRound(fWidth); + if (width < 1) + width = 1; + + int penStyle = PS_NULL; + switch (style) { + case SolidStroke: + penStyle = PS_SOLID; + break; + case DottedStroke: // not supported on Windows CE + case DashedStroke: + penStyle = PS_DASH; + width = 1; + break; + default: + break; + } + + return CreatePen(penStyle, width, RGB(col.red(), col.green(), col.blue())); +} + +static inline HGDIOBJ createBrush(const Color& col) +{ + return CreateSolidBrush(RGB(col.red(), col.green(), col.blue())); +} + +template <typename PixelType, bool Is16bit> static void _rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform) +{ + int destW = destBmp->width(); + int destH = destBmp->height(); + int sourceW = sourceBmp->width(); + int sourceH = sourceBmp->height(); + PixelType* dest = (PixelType*)destBmp->bytes(); + const PixelType* source = (const PixelType*)sourceBmp->bytes(); + int padding; + int paddedSourceW; + if (Is16bit) { + padding = destW & 1; + paddedSourceW = sourceW + (sourceW & 1); + } else { + padding = 0; + paddedSourceW = sourceW; + } + if (isZero(transform.m_sinA)) { + int cosA = transform.m_cosA > 0 ? 1 : -1; + for (int y = 0; y < destH; ++y) { + for (int x = 0; x < destW; ++x) { + int x1 = x + transform.m_preShiftX; + int y1 = y + transform.m_preShiftY; + int srcX = x1 * cosA + transform.m_postShiftX; + int srcY = y1 * cosA - transform.m_postShiftY; + if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH) + *dest++ = source[srcY * paddedSourceW + srcX] | 0xFF000000; + else + *dest++ |= 0xFF; + } + dest += padding; + } + } else if (isZero(transform.m_cosA)) { + int sinA = transform.m_sinA > 0 ? 1 : -1; + for (int y = 0; y < destH; ++y) { + for (int x = 0; x < destW; ++x) { + int x1 = x + transform.m_preShiftX; + int y1 = y + transform.m_preShiftY; + int srcX = y1 * sinA + transform.m_postShiftX; + int srcY = -x1 * sinA + transform.m_postShiftY; + if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH) + *dest++ = source[srcY * paddedSourceW + srcX]; + } + dest += padding; + } + } else { + for (int y = 0; y < destH; ++y) { + for (int x = 0; x < destW; ++x) { + // FIXME: for best quality, we should get weighted sum of four neighbours, + // but that will be too expensive + int srcX, srcY; + transform.map(x, y, &srcX, &srcY); + if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH) + *dest++ = source[srcY * paddedSourceW + srcX]; + } + dest += padding; + } + } +} + +static void rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform) +{ + ASSERT(destBmp->is16bit() == sourceBmp->is16bit()); + if (destBmp->is16bit()) + _rotateBitmap<unsigned short, true>(destBmp, sourceBmp, transform); + else + _rotateBitmap<unsigned, false>(destBmp, sourceBmp, transform); +} + +class TransparentLayerDC : Noncopyable { +public: + TransparentLayerDC(GraphicsContextPlatformPrivate* data, IntRect& origRect, const IntRect* rectBeforeTransform = 0, int alpha = 255, bool paintImage = false); + ~TransparentLayerDC(); + + HDC hdc() const { return m_memDc; } + const RECT& rect() const { return m_bmpRect; } + IntSize toShift() const { return IntSize(m_bmpRect.left - m_origRect.x(), m_bmpRect.top - m_origRect.y()); } + void fillAlphaChannel(); + +private: + GraphicsContextPlatformPrivate* m_data; + IntRect m_origRect; + IntRect m_rotatedOrigRect; + HDC m_memDc; + RefPtr<SharedBitmap> m_bitmap; + RefPtr<SharedBitmap> m_rotatedBitmap; + RECT m_bmpRect; + unsigned m_key1; + unsigned m_key2; + RotationTransform m_rotation; + float m_oldOpacity; + AlphaPaintType m_alphaPaintType; +}; + +TransparentLayerDC::TransparentLayerDC(GraphicsContextPlatformPrivate* data, IntRect& origRect, const IntRect* rectBeforeTransform, int alpha, bool paintImage) +: m_data(data) +, m_origRect(origRect) +, m_oldOpacity(data->m_opacity) +// m_key1 and m_key2 are not initalized here. They are used only in the case that +// SharedBitmap::getDC() is called, I.E., when m_bitmap is not null. +{ + m_data->m_opacity *= alpha / 255.; + bool mustCreateLayer; + if (!m_data->hasAlpha()) { + mustCreateLayer = false; + m_alphaPaintType = AlphaPaintNone; + } else { + mustCreateLayer = true; + m_alphaPaintType = paintImage ? AlphaPaintImage : AlphaPaintOther; + } + if (rectBeforeTransform && !isZero(m_data->m_transform.b())) { + m_rotatedOrigRect = origRect; + m_rotatedBitmap = m_data->getTransparentLayerBitmap(m_rotatedOrigRect, m_alphaPaintType, m_bmpRect, false, true); + if (m_rotatedBitmap) { + double a = m_data->m_transform.a(); + double b = m_data->m_transform.b(); + double c = _hypot(a, b); + m_rotation.m_cosA = a / c; + m_rotation.m_sinA = b / c; + + int centerX = origRect.x() + origRect.width() / 2; + int centerY = origRect.y() + origRect.height() / 2; + m_rotation.m_preShiftX = -centerX; + m_rotation.m_preShiftY = -centerY; + m_rotation.m_postShiftX = centerX; + m_rotation.m_postShiftY = centerY; + + m_origRect = mapRect(m_rotatedOrigRect, m_rotation); + + m_rotation.m_preShiftX += m_rotatedOrigRect.x(); + m_rotation.m_preShiftY += m_rotatedOrigRect.y(); + m_rotation.m_postShiftX -= m_origRect.x(); + m_rotation.m_postShiftY -= m_origRect.y(); + + FloatPoint topLeft = m_data->m_transform.mapPoint(FloatPoint(rectBeforeTransform->topLeft())); + FloatPoint topRight(rectBeforeTransform->right() - 1, rectBeforeTransform->y()); + topRight = m_data->m_transform.mapPoint(topRight); + FloatPoint bottomLeft(rectBeforeTransform->x(), rectBeforeTransform->bottom() - 1); + bottomLeft = m_data->m_transform.mapPoint(bottomLeft); + FloatSize sideTop = topRight - topLeft; + FloatSize sideLeft = bottomLeft - topLeft; + float width = _hypot(sideTop.width() + 1, sideTop.height() + 1); + float height = _hypot(sideLeft.width() + 1, sideLeft.height() + 1); + + origRect.inflateX(stableRound((width - origRect.width()) * 0.5)); + origRect.inflateY(stableRound((height - origRect.height()) * 0.5)); + + m_bitmap = SharedBitmap::createInstance(m_rotatedBitmap->is16bit(), m_origRect.width(), m_origRect.height(), true); + if (m_bitmap) + rotateBitmap(m_bitmap.get(), m_rotatedBitmap.get(), -m_rotation); + else + m_rotatedBitmap = 0; + } + } else + m_bitmap = m_data->getTransparentLayerBitmap(m_origRect, m_alphaPaintType, m_bmpRect, true, mustCreateLayer); + if (m_bitmap) + m_memDc = m_bitmap->getDC(&m_key1, &m_key2); + else + m_memDc = m_data->m_dc; +} + +TransparentLayerDC::~TransparentLayerDC() +{ + if (m_rotatedBitmap) { + m_bitmap->releaseDC(m_memDc, m_key1, m_key2); + m_key1 = m_key2 = 0; + rotateBitmap(m_rotatedBitmap.get(), m_bitmap.get(), m_rotation); + m_memDc = m_rotatedBitmap->getDC(&m_key1, &m_key2); + m_data->paintBackTransparentLayerBitmap(m_memDc, m_rotatedBitmap.get(), m_rotatedOrigRect, m_alphaPaintType, m_bmpRect); + m_rotatedBitmap->releaseDC(m_memDc, m_key1, m_key2); + } else if (m_bitmap) { + m_data->paintBackTransparentLayerBitmap(m_memDc, m_bitmap.get(), m_origRect, m_alphaPaintType, m_bmpRect); + m_bitmap->releaseDC(m_memDc, m_key1, m_key2); + } + m_data->m_opacity = m_oldOpacity; +} + +void TransparentLayerDC::fillAlphaChannel() +{ + if (!m_bitmap || !m_bitmap->is32bit()) + return; + + unsigned* pixels = (unsigned*)m_bitmap->bytes(); + const unsigned* const pixelsEnd = pixels + m_bitmap->bitmapInfo().numPixels(); + while (pixels < pixelsEnd) { + *pixels |= 0xFF000000; + ++pixels; + } +} + +class ScopeDCProvider : Noncopyable { +public: + explicit ScopeDCProvider(GraphicsContextPlatformPrivate* data) + : m_data(data) + { + if (m_data->m_bitmap) + m_data->m_dc = m_data->m_bitmap->getDC(&m_key1, &m_key2); + } + ~ScopeDCProvider() + { + if (m_data->m_bitmap) { + m_data->m_bitmap->releaseDC(m_data->m_dc, m_key1, m_key2); + m_data->m_dc = 0; + } + } +private: + GraphicsContextPlatformPrivate* m_data; + unsigned m_key1; + unsigned m_key2; +}; + + +GraphicsContext::GraphicsContext(PlatformGraphicsContext* dc) +: m_common(createGraphicsContextPrivate()) +, m_data(new GraphicsContextPlatformPrivate(dc)) +{ +} + +GraphicsContext::~GraphicsContext() +{ + destroyGraphicsContextPrivate(m_common); + delete m_data; +} + +void GraphicsContext::setBitmap(PassRefPtr<SharedBitmap> bmp) +{ + ASSERT(!m_data->m_dc); + m_data->m_bitmap = bmp; +} + +HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) +{ + notImplemented(); + ASSERT_NOT_REACHED(); + return 0; +} + +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) +{ + notImplemented(); + ASSERT_NOT_REACHED(); +} + +void GraphicsContext::savePlatformState() +{ + m_data->save(); +} + +void GraphicsContext::restorePlatformState() +{ + m_data->restore(); +} + +void GraphicsContext::drawRect(const IntRect& rect) +{ + if (!m_data->m_opacity || paintingDisabled() || rect.isEmpty()) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect trRect = m_data->mapRect(rect); + TransparentLayerDC transparentDC(m_data, trRect, &rect); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + + HGDIOBJ brush = 0; + HGDIOBJ oldBrush; + if (fillColor().alpha()) { + brush = createBrush(fillColor()); + oldBrush = SelectObject(dc, brush); + } else + SelectObject(dc, GetStockObject(NULL_BRUSH)); + + HGDIOBJ pen = 0; + HGDIOBJ oldPen; + if (strokeStyle() != NoStroke) { + pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + oldPen = SelectObject(dc, pen); + } else + SelectObject(dc, GetStockObject(NULL_PEN)); + + if (!brush && !pen) + return; + + if (trRect.width() <= 0) + trRect.setWidth(1); + if (trRect.height() <= 0) + trRect.setHeight(1); + + Rectangle(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + + if (pen) { + SelectObject(dc, oldPen); + DeleteObject(pen); + } + + if (brush) { + SelectObject(dc, oldBrush); + DeleteObject(brush); + } +} + +void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) +{ + if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke || !strokeColor().alpha()) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntPoint trPoint1 = m_data->mapPoint(point1); + IntPoint trPoint2 = m_data->mapPoint(point2); + + IntRect lineRect(trPoint1, trPoint2 - trPoint1); + lineRect.setHeight(lineRect.height() + strokeThickness()); + TransparentLayerDC transparentDC(m_data, lineRect, 0, strokeColor().alpha()); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trPoint1 += transparentDC.toShift(); + trPoint2 += transparentDC.toShift(); + + HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + HGDIOBJ oldPen = SelectObject(dc, pen); + + MoveToEx(dc, trPoint1.x(), trPoint1.y(), 0); + LineTo(dc, trPoint2.x(), trPoint2.y()); + + SelectObject(dc, oldPen); + DeleteObject(pen); +} + +void GraphicsContext::drawEllipse(const IntRect& rect) +{ + if (!m_data->m_opacity || paintingDisabled() || (!fillColor().alpha() && strokeStyle() == NoStroke)) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect trRect = m_data->mapRect(rect); + TransparentLayerDC transparentDC(m_data, trRect, &rect); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + + HGDIOBJ brush = 0; + HGDIOBJ oldBrush; + if (fillColor().alpha()) { + brush = createBrush(fillColor()); + oldBrush = SelectObject(dc, brush); + } else + SelectObject(dc, GetStockObject(NULL_BRUSH)); + HGDIOBJ pen = 0; + HGDIOBJ oldPen; + if (strokeStyle() != NoStroke) { + pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + oldPen = SelectObject(dc, pen); + } else + SelectObject(dc, GetStockObject(NULL_PEN)); + + Ellipse(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + + if (pen) { + SelectObject(dc, oldPen); + DeleteObject(pen); + } + + if (brush) { + SelectObject(dc, oldBrush); + DeleteObject(brush); + } +} + +static inline bool equalAngle(double a, double b) +{ + return fabs(a - b) < 1E-5; +} + +void getEllipsePointByAngle(double angle, double a, double b, float& x, float& y) +{ + while (angle < 0) + angle += 2 * piDouble; + while (angle >= 2 * piDouble) + angle -= 2 * piDouble; + + if (equalAngle(angle, 0) || equalAngle(angle, 2 * piDouble)) { + x = a; + y = 0; + } else if (equalAngle(angle, piDouble)) { + x = -a; + y = 0; + } else if (equalAngle(angle, .5 * piDouble)) { + x = 0; + y = b; + } else if (equalAngle(angle, 1.5 * piDouble)) { + x = 0; + y = -b; + } else { + double k = tan(angle); + double sqA = a * a; + double sqB = b * b; + double tmp = 1. / (1. / sqA + (k * k) / sqB); + tmp = tmp <= 0 ? 0 : sqrt(tmp); + if (angle > .5 * piDouble && angle < 1.5 * piDouble) + tmp = -tmp; + x = tmp; + + k = tan(.5 * piDouble - angle); + tmp = 1. / ((k * k) / sqA + 1 / sqB); + tmp = tmp <= 0 ? 0 : sqrt(tmp); + if (angle > piDouble) + tmp = -tmp; + y = tmp; + } +} + +void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) +{ + if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke || rect.isEmpty()) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect trRect = m_data->mapRect(rect); + TransparentLayerDC transparentDC(m_data, trRect, &rect); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + + HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + HGDIOBJ oldPen = SelectObject(dc, pen); + + double a = trRect.width() * 0.5; + double b = trRect.height() * 0.5; + int centerX = stableRound(trRect.x() + a); + int centerY = stableRound(trRect.y() + b); + float fstartX, fstartY, fendX, fendY; + int startX, startY, endX, endY; + getEllipsePointByAngle(deg2rad((double)startAngle), a, b, fstartX, fstartY); + getEllipsePointByAngle(deg2rad((double)startAngle + angleSpan), a, b, fendX, fendY); + startX = stableRound(fstartX); + startY = stableRound(fstartY); + endX = stableRound(fendX); + endY = stableRound(fendY); + + startX += centerX; + startY = centerY - startY; + endX += centerX; + endY = centerY - endY; + RECT clipRect; + if (startX < endX) { + clipRect.left = startX; + clipRect.right = endX; + } else { + clipRect.left = endX; + clipRect.right = startX; + } + if (startY < endY) { + clipRect.top = startY; + clipRect.bottom = endY; + } else { + clipRect.top = endY; + clipRect.bottom = startY; + } + + OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0)); + bool newClip; + if (GetClipRgn(dc, clipRgn.get()) <= 0) { + newClip = true; + clipRgn.set(CreateRectRgn(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom)); + SelectClipRgn(dc, clipRgn.get()); + } else { + newClip = false; + IntersectClipRect(dc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + } + + HGDIOBJ oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); + Ellipse(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + SelectObject(dc, oldBrush); + + if (newClip) + SelectClipRgn(dc, 0); + else + SelectClipRgn(dc, clipRgn.get()); + + SelectObject(dc, oldPen); + DeleteObject(pen); +} + +void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) +{ + if (!m_data->m_opacity || paintingDisabled() || npoints <= 1 || !points) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + Vector<POINT, 20> winPoints(npoints); + FloatPoint trPoint = m_data->mapPoint(points[0]); + winPoints[0].x = stableRound(trPoint.x()); + winPoints[0].y = stableRound(trPoint.y()); + RECT rect = { winPoints[0].x, winPoints[0].y, winPoints[0].x, winPoints[0].y }; + for (size_t i = 1; i < npoints; ++i) { + trPoint = m_data->mapPoint(points[i]); + winPoints[i].x = stableRound(trPoint.x()); + winPoints[i].y = stableRound(trPoint.y()); + if (rect.left > winPoints[i].x) + rect.left = winPoints[i].x; + else if (rect.right < winPoints[i].x) + rect.right = winPoints[i].x; + if (rect.top > winPoints[i].y) + rect.top = winPoints[i].y; + else if (rect.bottom < winPoints[i].y) + rect.bottom = winPoints[i].y; + } + rect.bottom += 1; + rect.right += 1; + + IntRect intRect(rect); + TransparentLayerDC transparentDC(m_data, intRect); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + + for (size_t i = 0; i < npoints; ++i) { + winPoints[i].x += transparentDC.toShift().width(); + winPoints[i].y += transparentDC.toShift().height(); + } + + HGDIOBJ brush = 0; + HGDIOBJ oldBrush; + if (fillColor().alpha()) { + brush = createBrush(fillColor()); + oldBrush = SelectObject(dc, brush); + } else + SelectObject(dc, GetStockObject(NULL_BRUSH)); + + HGDIOBJ pen = 0; + HGDIOBJ oldPen; + if (strokeStyle() != NoStroke) { + pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + oldPen = SelectObject(dc, pen); + } else + SelectObject(dc, GetStockObject(NULL_PEN)); + + if (!brush && !pen) + return; + + Polygon(dc, winPoints.data(), npoints); + + if (pen) { + SelectObject(dc, oldPen); + DeleteObject(pen); + } + + if (brush) { + SelectObject(dc, oldBrush); + DeleteObject(brush); + } +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) +{ + if (paintingDisabled() || !m_data->m_opacity) + return; + + int alpha = color.alpha(); + if (!alpha) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect intRect = enclosingIntRect(rect); + TransparentLayerDC transparentDC(m_data, m_data->mapRect(intRect), &intRect, alpha); + + if (!transparentDC.hdc()) + return; + + OwnPtr<HBRUSH> hbrush(CreateSolidBrush(RGB(color.red(), color.green(), color.blue()))); + FillRect(transparentDC.hdc(), &transparentDC.rect(), hbrush.get()); +} + +void GraphicsContext::clip(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + if (!m_data->m_dc) + return; + + IntRect trRect = enclosingIntRect(m_data->mapRect(rect)); + + OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0)); + if (GetClipRgn(m_data->m_dc, clipRgn.get()) > 0) + IntersectClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + else { + clipRgn.set(CreateRectRgn(trRect.x(), trRect.y(), trRect.right(), trRect.bottom())); + SelectClipRgn(m_data->m_dc, clipRgn.get()); + } +} + +void GraphicsContext::clipOut(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + if (!m_data->m_dc) + return; + + IntRect trRect = m_data->mapRect(rect); + + ExcludeClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); +} + +void GraphicsContext::drawFocusRing(const Color& color) +{ + if (!m_data->m_opacity || paintingDisabled()) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + int radius = (focusRingWidth() - 1) / 2; + int offset = radius + focusRingOffset(); + + const Vector<IntRect>& rects = focusRingRects(); + unsigned rectCount = rects.size(); + IntRect finalFocusRect; + for (unsigned i = 0; i < rectCount; i++) { + IntRect focusRect = rects[i]; + focusRect.inflate(offset); + finalFocusRect.unite(focusRect); + } + + IntRect intRect = finalFocusRect; + IntRect trRect = m_data->mapRect(finalFocusRect); + TransparentLayerDC transparentDC(m_data, trRect, &intRect); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + + RECT rect = trRect; + DrawFocusRect(dc, &rect); +} + +void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing) +{ + if (paintingDisabled()) + return; + + StrokeStyle oldStyle = strokeStyle(); + setStrokeStyle(SolidStroke); + drawLine(origin, origin + IntSize(width, 0)); + setStrokeStyle(oldStyle); +} + +void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&, int width, bool grammar) +{ + notImplemented(); +} + +void GraphicsContext::setPlatformFillColor(const Color& col) +{ + notImplemented(); +} + +void GraphicsContext::setPlatformStrokeColor(const Color& col) +{ + notImplemented(); +} + +void GraphicsContext::setPlatformStrokeThickness(float strokeThickness) +{ + notImplemented(); +} + +void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) +{ + notImplemented(); +} + +void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) +{ + // We can only clip rectangles on WINCE + clip(rect); +} + +void GraphicsContext::clearRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + if (m_data->hasAlpha()) { + IntRect trRect = enclosingIntRect(m_data->mapRect(rect)); + m_data->m_bitmap->clearPixels(trRect); + return; + } + + fillRect(rect, Color(Color::white)); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float width) +{ + if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect intRect = enclosingIntRect(rect); + IntRect trRect = m_data->mapRect(intRect); + TransparentLayerDC transparentDC(m_data, trRect, &intRect); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + + HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + HGDIOBJ oldPen = SelectObject(dc, pen); + + int right = trRect.right() - 1; + int bottom = trRect.bottom() - 1; + const POINT intPoints[5] = + { + { trRect.x(), trRect.y() }, + { right, trRect.y() }, + { right, bottom }, + { trRect.x(), bottom }, + { trRect.x(), trRect.y() } + }; + + Polyline(dc, intPoints, 5); + + SelectObject(dc, oldPen); + DeleteObject(pen); +} + +void GraphicsContext::beginTransparencyLayer(float opacity) +{ + m_data->save(); + m_data->m_opacity *= opacity; +} + +void GraphicsContext::endTransparencyLayer() +{ + m_data->restore(); +} + +void GraphicsContext::concatCTM(const TransformationMatrix& transform) +{ + m_data->concatCTM(transform); +} + +TransformationMatrix& GraphicsContext::affineTransform() +{ + return m_data->m_transform; +} + +const TransformationMatrix& GraphicsContext::affineTransform() const +{ + return m_data->m_transform; +} + +void GraphicsContext::resetAffineTransform() +{ + m_data->m_transform.makeIdentity(); +} + +void GraphicsContext::translate(float x, float y) +{ + m_data->translate(x, y); +} + +void GraphicsContext::rotate(float radians) +{ + m_data->rotate(radians); +} + +IntPoint GraphicsContext::origin() +{ + return m_data->origin(); +} + +void GraphicsContext::scale(const FloatSize& size) +{ + m_data->scale(size); +} + +void GraphicsContext::setLineCap(LineCap lineCap) +{ + notImplemented(); +} + +void GraphicsContext::setLineJoin(LineJoin lineJoin) +{ + notImplemented(); +} + +void GraphicsContext::setMiterLimit(float miter) +{ + notImplemented(); +} + +void GraphicsContext::setAlpha(float alpha) +{ + m_data->m_opacity = alpha; +} + +void GraphicsContext::setCompositeOperation(CompositeOperator op) +{ + notImplemented(); +} + +void GraphicsContext::beginPath() +{ + m_data->m_paths.clear(); +} + +void GraphicsContext::addPath(const Path& path) +{ + m_data->m_paths.append(path); +} + +void GraphicsContext::clip(const Path& path) +{ + notImplemented(); +} + +void GraphicsContext::clipOut(const Path&) +{ + notImplemented(); +} + +void GraphicsContext::clipOutEllipseInRect(const IntRect&) +{ + notImplemented(); +} + +static inline IntPoint rectCenterPoint(const RECT& rect) +{ + return IntPoint(rect.left + (rect.right - rect.left) / 2, rect.top + (rect.bottom - rect.top) / 2); +} +void GraphicsContext::fillRoundedRect(const IntRect& fillRect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& c) +{ + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntSize shadowSize; + int shadowBlur = 0; + Color shadowColor; + + getShadow(shadowSize, shadowBlur, shadowColor); + + IntRect dstRect = fillRect; + + dstRect.move(shadowSize); + dstRect.inflate(shadowBlur); + dstRect = m_data->mapRect(dstRect); + + FloatSize newTopLeft(m_data->mapSize(topLeft)); + FloatSize newTopRight(m_data->mapSize(topRight)); + FloatSize newBottomLeft(m_data->mapSize(bottomLeft)); + FloatSize newBottomRight(m_data->mapSize(bottomRight)); + + TransparentLayerDC transparentDc(m_data, dstRect, &fillRect); + HDC dc = transparentDc.hdc(); + if (!dc) + return; + + dstRect.move(transparentDc.toShift()); + + RECT rectWin = dstRect; + + HGDIOBJ brush = createBrush(shadowColor); + HGDIOBJ oldBrush = SelectObject(dc, brush); + + SelectObject(dc, GetStockObject(NULL_PEN)); + + IntPoint centerPoint = rectCenterPoint(rectWin); + // Draw top left half + RECT clipRect(rectWin); + clipRect.right = centerPoint.x(); + clipRect.bottom = centerPoint.y(); + + OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0)); + bool needsNewClip = (GetClipRgn(dc, clipRgn.get()) <= 0); + + drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newTopLeft.width() * 2), stableRound(newTopLeft.height() * 2)); + + // Draw top right + clipRect = rectWin; + clipRect.left = centerPoint.x(); + clipRect.bottom = centerPoint.y(); + + drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newTopRight.width() * 2), stableRound(newTopRight.height() * 2)); + + // Draw bottom left + clipRect = rectWin; + clipRect.right = centerPoint.x(); + clipRect.top = centerPoint.y(); + + drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomLeft.width() * 2), stableRound(newBottomLeft.height() * 2)); + + // Draw bottom right + clipRect = rectWin; + clipRect.left = centerPoint.x(); + clipRect.top = centerPoint.y(); + + drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomRight.width() * 2), stableRound(newBottomRight.height() * 2)); + + SelectObject(dc, oldBrush); + DeleteObject(brush); +} + + +void GraphicsContext::drawRoundCorner(bool needsNewClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height) +{ + if (!dc) + return; + + OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0)); + if (needsNewClip) { + clipRgn.set(CreateRectRgn(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom)); + SelectClipRgn(dc, clipRgn.get()); + } else + IntersectClipRect(dc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + + ::RoundRect(dc, rectWin.left , rectWin.top , rectWin.right , rectWin.bottom , width, height); + + SelectClipRgn(dc, needsNewClip ? 0 : clipRgn.get()); +} + + +FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) +{ + notImplemented(); + return frect; +} + +Color gradientAverageColor(const Gradient* gradient) +{ + const Vector<Gradient::ColorStop>& stops = gradient->getStops(); + if (stops.isEmpty()) + return Color(); + + const Gradient::ColorStop& stop = stops.first(); + if (stops.size() == 1) + return Color(stop.red, stop.green, stop.blue, stop.alpha); + + const Gradient::ColorStop& lastStop = stops.last(); + return Color((stop.red + lastStop.red) * 0.5f + , (stop.green + lastStop.green) * 0.5f + , (stop.blue + lastStop.blue) * 0.5f + , (stop.alpha + lastStop.alpha) * 0.5f); +} + +void GraphicsContext::fillPath() +{ + Color c = m_common->state.fillColorSpace == GradientColorSpace && m_common->state.fillGradient + ? gradientAverageColor(m_common->state.fillGradient.get()) + : fillColor(); + + if (!c.alpha() || !m_data->m_opacity) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) { + HGDIOBJ brush = createBrush(c); + for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) { + IntRect trRect = enclosingIntRect(m_data->mapRect(i->boundingRect())); + trRect.inflate(1); + TransparentLayerDC transparentDC(m_data, trRect); + HDC dc = transparentDC.hdc(); + if (!dc) + continue; + + TransformationMatrix tr = m_data->m_transform; + tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height()); + + SelectObject(dc, GetStockObject(NULL_PEN)); + HGDIOBJ oldBrush = SelectObject(dc, brush); + i->platformPath()->fillPath(dc, &tr); + SelectObject(dc, oldBrush); + } + DeleteObject(brush); + } else { + SelectObject(m_data->m_dc, GetStockObject(NULL_PEN)); + HGDIOBJ brush = createBrush(c); + HGDIOBJ oldBrush = SelectObject(m_data->m_dc, brush); + for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) + i->platformPath()->fillPath(m_data->m_dc, &m_data->m_transform); + SelectObject(m_data->m_dc, oldBrush); + DeleteObject(brush); + } +} + + +void GraphicsContext::strokePath() +{ + if (!m_data->m_opacity) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) { + HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) { + IntRect trRect = enclosingIntRect(m_data->mapRect(i->boundingRect())); + trRect.inflate(1); + TransparentLayerDC transparentDC(m_data, trRect); + HDC dc = transparentDC.hdc(); + if (!dc) + continue; + + TransformationMatrix tr = m_data->m_transform; + tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height()); + + SelectObject(dc, GetStockObject(NULL_BRUSH)); + HGDIOBJ oldPen = SelectObject(dc, pen); + i->platformPath()->strokePath(dc, &tr); + SelectObject(dc, oldPen); + } + DeleteObject(pen); + } else { + SelectObject(m_data->m_dc, GetStockObject(NULL_BRUSH)); + HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + HGDIOBJ oldPen = SelectObject(m_data->m_dc, pen); + for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) + i->platformPath()->strokePath(m_data->m_dc, &m_data->m_transform); + SelectObject(m_data->m_dc, oldPen); + DeleteObject(pen); + } +} + +void GraphicsContext::fillRect(const FloatRect& r, const Gradient* gradient) +{ + if (!m_data->m_opacity) + return; + + const Vector<Gradient::ColorStop>& stops = gradient->getStops(); + if (stops.isEmpty()) + return; + + size_t numStops = stops.size(); + if (numStops == 1) { + const Gradient::ColorStop& stop = stops.first(); + Color color(stop.red, stop.green, stop.blue, stop.alpha); + fillRect(r, color); + return; + } + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect intRect = enclosingIntRect(r); + IntRect rect = m_data->mapRect(intRect); + TransparentLayerDC transparentDC(m_data, rect, &intRect, 255, true); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + + rect.move(transparentDC.toShift()); + FloatPoint fp0 = m_data->mapPoint(gradient->p0()); + FloatPoint fp1 = m_data->mapPoint(gradient->p1()); + IntPoint p0(stableRound(fp0.x()), stableRound(fp0.y())); + IntPoint p1(stableRound(fp1.x()), stableRound(fp1.y())); + p0 += transparentDC.toShift(); + p1 += transparentDC.toShift(); + + if (gradient->isRadial()) { + if (g_radialGradientFiller) { + // FIXME: don't support 2D scaling at this time + double scale = (m_data->m_transform.a() + m_data->m_transform.d()) * 0.5; + float r0 = gradient->r0() * scale; + float r1 = gradient->r1() * scale; + g_radialGradientFiller(dc, rect, p0, p1, r0, r1, gradient->getStops()); + return; + } + } else if (g_linearGradientFiller) { + g_linearGradientFiller(dc, rect, p0, p1, gradient->getStops()); + return; + } + + // Simple 1D linear solution that assumes p0 is on the top or left side, and p1 is on the right or bottom side + size_t numRects = (numStops - 1); + Vector<TRIVERTEX, 20> tv; + tv.resize(numRects * 2); + Vector<GRADIENT_RECT, 10> mesh; + mesh.resize(numRects); + int x = rect.x(); + int y = rect.y(); + int width = rect.width(); + int height = rect.height(); + FloatSize d = gradient->p1() - gradient->p0(); + bool vertical = abs(d.height()) > abs(d.width()); + for (size_t i = 0; i < numStops; ++i) { + const Gradient::ColorStop& stop = stops[i]; + int iTv = i ? 2 * i - 1 : 0; + tv[iTv].Red = stop.red * 0xFFFF; + tv[iTv].Green = stop.green * 0xFFFF; + tv[iTv].Blue = stop.blue * 0xFFFF; + tv[iTv].Alpha = stop.alpha * 0xFFFF; + if (i) { + tv[iTv].x = vertical ? x + width: x + width * stop.stop; + tv[iTv].y = vertical ? y + height * stop.stop : y + height; + mesh[i - 1].UpperLeft = iTv - 1; + mesh[i - 1].LowerRight = iTv; + } else { + tv[iTv].x = x; + tv[iTv].y = y; + } + + if (i && i < numRects) { + tv[iTv + 1] = tv[iTv]; + if (vertical) + tv[iTv + 1].x = x; + else + tv[iTv + 1].y = y; + } + } + + GradientFill(dc, tv.data(), tv.size(), mesh.data(), mesh.size(), vertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H); +} + +TransformationMatrix GraphicsContext::getCTM() const +{ + return m_data->m_transform; +} + +void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) +{ + notImplemented(); +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (m_common->state.fillColorSpace == GradientColorSpace && m_common->state.fillGradient) + fillRect(rect, m_common->state.fillGradient.get()); + else + fillRect(rect, fillColor()); +} + +void GraphicsContext::setPlatformShadow(const IntSize&, int, const Color&) +{ + notImplemented(); +} + +void GraphicsContext::clearPlatformShadow() +{ + notImplemented(); +} + +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) +{ + notImplemented(); +} + +static inline bool isCharVisible(UChar c) +{ + return c && c != zeroWidthSpace; +} + +void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to) +{ + if (paintingDisabled() || !fillColor().alpha() || !m_data->m_opacity) + return; + + bool mustSupportAlpha = m_data->hasAlpha(); + + if (!mustSupportAlpha && fillColor().alpha() == 0xFF && m_data->m_opacity >= 1.0) { + font.drawText(this, run, point, from, to); + return; + } + + float oldOpacity = m_data->m_opacity; + m_data->m_opacity *= fillColor().alpha() / 255.0; + + FloatRect textRect = font.selectionRectForText(run, point, font.height(), from, to); + textRect.setY(textRect.y() - font.ascent()); + IntRect trRect = enclosingIntRect(m_data->mapRect(textRect)); + RECT bmpRect; + AlphaPaintType alphaPaintType = mustSupportAlpha ? AlphaPaintOther : AlphaPaintNone; + if (RefPtr<SharedBitmap> bmp = m_data->getTransparentLayerBitmap(trRect, alphaPaintType, bmpRect, true, mustSupportAlpha)) { + { + GraphicsContext gc(0); + gc.setBitmap(bmp); + gc.scale(FloatSize(m_data->m_transform.a(), m_data->m_transform.d())); + font.drawText(&gc, run, IntPoint(0, font.ascent()), from, to); + } + unsigned key1, key2; + HDC memDC = bmp->getDC(&key1, &key2); + if (memDC) { + m_data->paintBackTransparentLayerBitmap(memDC, bmp.get(), trRect, alphaPaintType, bmpRect); + bmp->releaseDC(memDC, key1, key2); + } + } + + m_data->m_opacity = oldOpacity; +} + +void GraphicsContext::drawText(const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, const FloatPoint& point) +{ + if (!m_data->m_opacity) + return; + + for (;;) { + if (!numGlyphs) + return; + if (isCharVisible(*glyphBuffer.glyphs(from))) + break; + ++from; + --numGlyphs; + } + + double scaleX = m_data->m_transform.a(); + double scaleY = m_data->m_transform.d(); + + int height = fontData->platformData().size() * scaleY; + int width = fontData->platformData().averageCharWidth() * scaleX; + + if (!height || !width) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + HFONT hFont = height > 1 + ? fontData->platformData().getScaledFontHandle(height, scaleX == scaleY ? 0 : width) + : 0; + + FloatPoint startPoint(point.x(), point.y() - fontData->ascent()); + FloatPoint trPoint = m_data->mapPoint(startPoint); + int y = stableRound(trPoint.y()); + + Color color = fillColor(); + if (!color.alpha()) + return; + + COLORREF fontColor = RGB(color.red(), color.green(), color.blue()); + + if (!hFont) { + double offset = trPoint.x(); + const GlyphBufferAdvance* advance = glyphBuffer.advances(from); + if (scaleX == 1.) + for (int i = 1; i < numGlyphs; ++i) + offset += *advance++; + else + for (int i = 1; i < numGlyphs; ++i) + offset += *advance++ * scaleX; + + offset += width; + + OwnPtr<HPEN> hPen(CreatePen(PS_DASH, 1, fontColor)); + HGDIOBJ oldPen = SelectObject(m_data->m_dc, hPen.get()); + + MoveToEx(m_data->m_dc, stableRound(trPoint.x()), y, 0); + LineTo(m_data->m_dc, stableRound(offset), y); + + SelectObject(m_data->m_dc, oldPen); + return; + } + + IntSize shadowSize; + int shadowBlur = 0; + Color shadowColor; + bool hasShadow = textDrawingMode() == cTextFill + && getShadow(shadowSize, shadowBlur, shadowColor) + && shadowColor.alpha(); + COLORREF shadowRGBColor; + FloatPoint trShadowPoint; + if (hasShadow) { + shadowRGBColor = RGB(shadowColor.red(), shadowColor.green(), shadowColor.blue()); + trShadowPoint = m_data->mapPoint(startPoint + shadowSize); + } + + HGDIOBJ hOldFont = SelectObject(m_data->m_dc, hFont); + COLORREF oldTextColor = GetTextColor(m_data->m_dc); + int oldTextAlign = GetTextAlign(m_data->m_dc); + SetTextAlign(m_data->m_dc, 0); + + int oldBkMode = GetBkMode(m_data->m_dc); + SetBkMode(m_data->m_dc, TRANSPARENT); + + if (numGlyphs > 1) { + double offset = trPoint.x(); + Vector<int, 256> glyphSpace(numGlyphs); + Vector<UChar, 256> text(numGlyphs); + int* curSpace = glyphSpace.data(); + UChar* curChar = text.data(); + const UChar* srcChar = glyphBuffer.glyphs(from); + const UChar* const srcCharEnd = srcChar + numGlyphs; + *curChar++ = *srcChar++; + int firstOffset = stableRound(offset); + int lastOffset = firstOffset; + const GlyphBufferAdvance* advance = glyphBuffer.advances(from); + // FIXME: ExtTextOut() can flip over each word for RTL languages, even when TA_RTLREADING is off. + // (this can be GDI bug or font driver bug?) + // We are not clear how it processes characters and handles specified spaces. On the other side, + // our glyph buffer is already in the correct order for rendering. So, the solution is that we + // call ExtTextOut() for each single character when the text contains any RTL character. + // This solution is not perfect as it is slower than calling ExtTextOut() one time for all characters. + // Drawing characters one by one may be too slow. + bool drawOneByOne = false; + if (scaleX == 1.) { + for (; srcChar < srcCharEnd; ++srcChar) { + offset += *advance++; + int offsetInt = stableRound(offset); + if (isCharVisible(*srcChar)) { + if (!drawOneByOne && WTF::Unicode::direction(*srcChar) == WTF::Unicode::RightToLeft) + drawOneByOne = true; + *curChar++ = *srcChar; + *curSpace++ = offsetInt - lastOffset; + lastOffset = offsetInt; + } + } + } else { + for (; srcChar < srcCharEnd; ++srcChar) { + offset += *advance++ * scaleX; + int offsetInt = stableRound(offset); + if (isCharVisible(*srcChar)) { + if (!drawOneByOne && WTF::Unicode::direction(*srcChar) == WTF::Unicode::RightToLeft) + drawOneByOne = true; + *curChar++ = *srcChar; + *curSpace++ = offsetInt - lastOffset; + lastOffset = offsetInt; + } + } + } + numGlyphs = curChar - text.data(); + if (hasShadow) { + SetTextColor(m_data->m_dc, shadowRGBColor); + if (drawOneByOne) { + int xShadow = firstOffset + stableRound(trShadowPoint.x() - trPoint.x()); + int yShadow = stableRound(trShadowPoint.y()); + for (int i = 0; i < numGlyphs; ++i) { + ExtTextOut(m_data->m_dc, xShadow, yShadow, 0, NULL, text.data() + i, 1, 0); + xShadow += glyphSpace[i]; + } + } else + ExtTextOut(m_data->m_dc, firstOffset + stableRound(trShadowPoint.x() - trPoint.x()), stableRound(trShadowPoint.y()), 0, NULL, text.data(), numGlyphs, glyphSpace.data()); + } + SetTextColor(m_data->m_dc, fontColor); + if (drawOneByOne) { + int x = firstOffset; + for (int i = 0; i < numGlyphs; ++i) { + ExtTextOut(m_data->m_dc, x, y, 0, NULL, text.data() + i, 1, 0); + x += glyphSpace[i]; + } + } else + ExtTextOut(m_data->m_dc, firstOffset, y, 0, NULL, text.data(), numGlyphs, glyphSpace.data()); + } else { + UChar c = *glyphBuffer.glyphs(from); + if (hasShadow) { + SetTextColor(m_data->m_dc, shadowRGBColor); + ExtTextOut(m_data->m_dc, stableRound(trShadowPoint.x()), stableRound(trShadowPoint.y()), 0, NULL, &c, 1, 0); + } + SetTextColor(m_data->m_dc, fontColor); + ExtTextOut(m_data->m_dc, stableRound(trPoint.x()), y, 0, NULL, &c, 1, 0); + } + + SetTextAlign(m_data->m_dc, oldTextAlign); + SetTextColor(m_data->m_dc, oldTextColor); + SetBkMode(m_data->m_dc, oldBkMode); + SelectObject(m_data->m_dc, hOldFont); +} + +void GraphicsContext::drawFrameControl(const IntRect& rect, unsigned type, unsigned state) +{ + if (!m_data->m_opacity) + return; + + const int boxWidthBest = 8; + const int boxHeightBest = 8; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect trRect = m_data->mapRect(rect); + TransparentLayerDC transparentDC(m_data, trRect, &rect, 255, true); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + + RECT rectWin = trRect; + + if ((rectWin.right - rectWin.left) < boxWidthBest) { + RefPtr<SharedBitmap> bmp = SharedBitmap::createInstance(true, boxWidthBest, boxHeightBest, true); + SharedBitmap::DCHolder memDC(bmp.get()); + if (memDC.get()) { + RECT tempRect = {0, 0, boxWidthBest, boxHeightBest}; + DrawFrameControl(memDC.get(), &tempRect, type, state); + + ::StretchBlt(dc, rectWin.left, rectWin.top, rectWin.right - rectWin.left, rectWin.bottom - rectWin.top, memDC.get(), 0, 0, boxWidthBest, boxHeightBest, SRCCOPY); + return; + } + } + + DrawFrameControl(dc, &rectWin, type, state); +} + +void GraphicsContext::drawFocusRect(const IntRect& rect) +{ + if (!m_data->m_opacity) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect trRect = m_data->mapRect(rect); + TransparentLayerDC transparentDC(m_data, trRect, &rect); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + + RECT rectWin = trRect; + DrawFocusRect(dc, &rectWin); +} + +void GraphicsContext::paintTextField(const IntRect& rect, unsigned state) +{ + if (!m_data->m_opacity) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect trRect = m_data->mapRect(rect); + TransparentLayerDC transparentDC(m_data, trRect, &rect); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + + RECT rectWin = trRect; + DrawEdge(dc, &rectWin, EDGE_ETCHED, BF_RECT | BF_ADJUST); + FillRect(dc, &rectWin, reinterpret_cast<HBRUSH>(((state & DFCS_INACTIVE) ? COLOR_BTNFACE : COLOR_WINDOW) + 1)); +} + +void GraphicsContext::drawBitmap(SharedBitmap* bmp, const IntRect& dstRectIn, const IntRect& srcRect, CompositeOperator compositeOp) +{ + if (!m_data->m_opacity) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect dstRect = m_data->mapRect(dstRectIn); + TransparentLayerDC transparentDC(m_data, dstRect, &dstRectIn, 255, true); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + dstRect.move(transparentDC.toShift()); + + bmp->draw(dc, dstRect, srcRect, compositeOp); + + if (bmp->is16bit()) + transparentDC.fillAlphaChannel(); +} + +void GraphicsContext::drawBitmapPattern(SharedBitmap* bmp, const FloatRect& tileRectIn, const TransformationMatrix& patternTransform, + const FloatPoint& phase, CompositeOperator op, const FloatRect& destRectIn, const IntSize& origSourceSize) +{ + if (!m_data->m_opacity) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect intDstRect = enclosingIntRect(destRectIn); + IntRect trRect = m_data->mapRect(intDstRect); + TransparentLayerDC transparentDC(m_data, trRect, &intDstRect, 255, true); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + trRect.move(transparentDC.toShift()); + FloatRect movedDstRect = m_data->m_transform.inverse().mapRect(FloatRect(trRect)); + FloatSize moved(movedDstRect.location() - destRectIn.location()); + TransformationMatrix transform = m_data->m_transform; + transform.translate(moved.width(), moved.height()); + + bmp->drawPattern(dc, transform, tileRectIn, patternTransform, phase, op, destRectIn, origSourceSize); + + if (!bmp->hasAlpha()) + transparentDC.fillAlphaChannel(); +} + +void GraphicsContext::drawIcon(HICON icon, const IntRect& dstRectIn, UINT flags) +{ + if (!m_data->m_opacity) + return; + + ScopeDCProvider dcProvider(m_data); + if (!m_data->m_dc) + return; + + IntRect dstRect = m_data->mapRect(dstRectIn); + TransparentLayerDC transparentDC(m_data, dstRect, &dstRectIn, 255, true); + HDC dc = transparentDC.hdc(); + if (!dc) + return; + dstRect.move(transparentDC.toShift()); + + DrawIconEx(dc, dstRect.x(), dstRect.y(), icon, dstRect.width(), dstRect.height(), 0, NULL, flags); +} + +void GraphicsContext::setPlatformShouldAntialias(bool) +{ + notImplemented(); +} + +void GraphicsContext::setLineDash(const DashArray&, float) +{ + notImplemented(); +} + +void GraphicsContext::clipPath(WindRule) +{ + notImplemented(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/wince/SimpleFontDataWince.cpp b/WebCore/platform/graphics/wince/SimpleFontDataWince.cpp new file mode 100644 index 0000000..1195294 --- /dev/null +++ b/WebCore/platform/graphics/wince/SimpleFontDataWince.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * + * 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 "FloatRect.h" +#include "Font.h" +#include "FontCache.h" +#include "FontDescription.h" +#include <wtf/MathExtras.h> +#include <mlang.h> +#include <tchar.h> + +namespace WebCore { + +extern HDC g_screenDC; + +void SimpleFontData::platformInit() +{ + if (!m_platformData.isValid()) + return; + + const TEXTMETRIC& tm = m_platformData.metrics(); + m_isSystemFont = m_platformData.isSystemFont(); + + m_ascent = (tm.tmAscent * m_platformData.size() + 36) / 72; + m_descent = (tm.tmDescent * m_platformData.size() + 36) / 72; + m_lineGap = (tm.tmExternalLeading * m_platformData.size() + 36) / 72; + m_lineSpacing = m_ascent + m_descent + m_lineGap; + m_xHeight = m_ascent * 0.56f; +} + +void SimpleFontData::platformDestroy() +{ + delete m_smallCapsFontData; + m_smallCapsFontData = 0; +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_smallCapsFontData) { + FontDescription fontDesc(fontDescription); + fontDesc.setComputedSize(lroundf(0.70f * fontDesc.computedSize())); + fontDesc.setSpecifiedSize(lroundf(0.70f * fontDesc.specifiedSize())); + fontDesc.setKeywordSize(lroundf(0.70f * fontDesc.keywordSize())); + FontPlatformData* result = fontCache()->getCachedFontPlatformData(fontDesc, m_platformData.family()); + if (result) + m_smallCapsFontData = new SimpleFontData(*result); + } + return m_smallCapsFontData; +} + +DWORD getKnownFontCodePages(const wchar_t* family); + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + if (m_platformData.isDisabled()) + return true; + + // 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? + + // FIXME: in the case that we failed to get the interface, still use the font. +#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) + IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface(); +#else + IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface(); +#endif + if (!langFontLink) + return true; + + DWORD fontCodePages = m_platformData.codePages(); + if (!fontCodePages) + return false; + + DWORD acpCodePages = 0; + langFontLink->CodePageToCodePages(CP_ACP, &acpCodePages); + + DWORD actualCodePages; + long numCharactersProcessed; + while (length) { + langFontLink->GetStrCodePages(characters, length, acpCodePages, &actualCodePages, &numCharactersProcessed); + if (actualCodePages && !(actualCodePages & fontCodePages)) + return false; + + length -= numCharactersProcessed; + characters += numCharactersProcessed; + } + + return true; +} + +void SimpleFontData::determinePitch() +{ + if (!m_platformData.isValid()) + return; + + const TEXTMETRIC& tm = m_platformData.metrics(); + + // Yes, this looks backwards, but the fixed pitch bit is actually set if the font + // is *not* fixed pitch. Unbelievable but true. + m_treatAsFixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + if (m_platformData.isDisabled()) + return 0; + + HGDIOBJ hOldFont = SelectObject(g_screenDC, m_platformData.hfont()); + + SIZE fontSize; + wchar_t c = glyph; + GetTextExtentPoint32(g_screenDC, &c, 1, &fontSize); + + SelectObject(g_screenDC, hOldFont); + + return (float)fontSize.cx * (float)m_platformData.size() / 72.f; +} + + +void SimpleFontData::platformCharWidthInit() +{ + if (!m_platformData.isValid()) + return; + + const TEXTMETRIC& tm = m_platformData.metrics(); + m_avgCharWidth = (tm.tmAveCharWidth * m_platformData.size() + 36) / 72; + m_maxCharWidth = (tm.tmMaxCharWidth * m_platformData.size() + 36) / 72; +} + +} diff --git a/WebCore/platform/graphics/wx/FontPlatformData.h b/WebCore/platform/graphics/wx/FontPlatformData.h index c77968a..3b99830 100644 --- a/WebCore/platform/graphics/wx/FontPlatformData.h +++ b/WebCore/platform/graphics/wx/FontPlatformData.h @@ -40,6 +40,8 @@ namespace WebCore { +class String; + class FontHolder: public WTF::RefCounted<FontHolder> { public: @@ -111,7 +113,9 @@ public: bool isHashTableDeletedValue() const { return m_fontState == DELETED; } - +#ifndef NDEBUG + String description() const; +#endif private: WTF::RefPtr<FontHolder> m_font; diff --git a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp index ce5e40e..de92fcd 100644 --- a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp +++ b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp @@ -27,7 +27,7 @@ #include "FontPlatformData.h" #include "FontDescription.h" - +#include "PlatformString.h" #include <wx/defs.h> #include <wx/gdicmn.h> #include <wx/font.h> @@ -122,4 +122,11 @@ FontPlatformData::~FontPlatformData() m_font = 0; } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + } diff --git a/WebCore/platform/graphics/wx/ImageBufferWx.cpp b/WebCore/platform/graphics/wx/ImageBufferWx.cpp index ea3dfe8..e71dbde 100644 --- a/WebCore/platform/graphics/wx/ImageBufferWx.cpp +++ b/WebCore/platform/graphics/wx/ImageBufferWx.cpp @@ -36,7 +36,7 @@ ImageBufferData::ImageBufferData(const IntSize&) { } -ImageBuffer::ImageBuffer(const IntSize&, bool grayScale, bool& success) : +ImageBuffer::ImageBuffer(const IntSize&, ImageColorSpace imageColorSpace, bool& success) : m_data(IntSize()) { notImplemented(); diff --git a/WebCore/platform/graphics/wx/ImageSourceWx.cpp b/WebCore/platform/graphics/wx/ImageSourceWx.cpp index 2f71d62..06c165d 100644 --- a/WebCore/platform/graphics/wx/ImageSourceWx.cpp +++ b/WebCore/platform/graphics/wx/ImageSourceWx.cpp @@ -110,6 +110,8 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) // This method will examine the data and instantiate an instance of the appropriate decoder plugin. // If insufficient bytes are available to determine the image type, no decoder plugin will be // made. + if (m_decoder) + delete m_decoder; m_decoder = createDecoder(*data); if (!m_decoder) return; @@ -132,9 +134,12 @@ IntSize ImageSource::size() const return m_decoder->size(); } -IntSize ImageSource::frameSizeAtIndex(size_t) const +IntSize ImageSource::frameSizeAtIndex(size_t index) const { - return size(); + if (!m_decoder) + return IntSize(); + + return m_decoder->frameSizeAtIndex(index); } int ImageSource::repetitionCount() @@ -185,56 +190,7 @@ NativeImagePtr ImageSource::createFrameAtIndex(size_t index) if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; - IntRect imageRect = buffer->rect(); - unsigned char* bytes = (unsigned char*)buffer->bytes().data(); - long colorSize = buffer->bytes().size(); - - typedef wxPixelData<wxBitmap, wxAlphaPixelFormat> PixelData; - - int width = size().width(); - int height = size().height(); - - wxBitmap* bmp = new wxBitmap(width, height, 32); - PixelData data(*bmp); - - int rowCounter = 0; - long pixelCounter = 0; - - PixelData::Iterator p(data); - - PixelData::Iterator rowStart = p; - - // NB: It appears that the data is in BGRA format instead of RGBA format. - // This code works properly on both ppc and intel, meaning the issue is - // likely not an issue of byte order getting mixed up on different archs. - for (long i = 0; i < buffer->bytes().size()*4; i+=4) { - p.Red() = bytes[i+2]; - p.Green() = bytes[i+1]; - p.Blue() = bytes[i+0]; - p.Alpha() = bytes[i+3]; - - p++; - - pixelCounter++; - if ( (pixelCounter % width ) == 0 ) { - rowCounter++; - p = rowStart; - p.MoveTo(data, 0, rowCounter); - } - - } -#if !wxCHECK_VERSION(2,9,0) - bmp->UseAlpha(); -#endif - ASSERT(bmp->IsOk()); - -#if USE(WXGC) - wxGraphicsBitmap* bitmap = new wxGraphicsBitmap(wxGraphicsRenderer::GetDefaultRenderer()->CreateBitmap(*bmp)); - delete bmp; - return bitmap; -#else - return bmp; -#endif + return buffer->asNewNativeImage(); } float ImageSource::frameDurationAtIndex(size_t index) diff --git a/WebCore/platform/graphics/wx/PathWx.cpp b/WebCore/platform/graphics/wx/PathWx.cpp index 04a952d..cebc05a 100644 --- a/WebCore/platform/graphics/wx/PathWx.cpp +++ b/WebCore/platform/graphics/wx/PathWx.cpp @@ -227,4 +227,9 @@ bool Path::isEmpty() const return true; } +bool Path::hasCurrentPoint() const +{ + return !isEmpty(); +} + } |