diff options
author | Steve Block <steveblock@google.com> | 2010-02-02 14:57:50 +0000 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-02-04 15:06:55 +0000 |
commit | d0825bca7fe65beaee391d30da42e937db621564 (patch) | |
tree | 7461c49eb5844ffd1f35d1ba2c8b7584c1620823 /WebCore/platform/graphics | |
parent | 3db770bd97c5a59b6c7574ca80a39e5a51c1defd (diff) | |
download | external_webkit-d0825bca7fe65beaee391d30da42e937db621564.zip external_webkit-d0825bca7fe65beaee391d30da42e937db621564.tar.gz external_webkit-d0825bca7fe65beaee391d30da42e937db621564.tar.bz2 |
Merge webkit.org at r54127 : Initial merge by git
Change-Id: Ib661abb595522f50ea406f72d3a0ce17f7193c82
Diffstat (limited to 'WebCore/platform/graphics')
122 files changed, 7161 insertions, 1283 deletions
diff --git a/WebCore/platform/graphics/BitmapImage.h b/WebCore/platform/graphics/BitmapImage.h index 0031df6..485bb02 100644 --- a/WebCore/platform/graphics/BitmapImage.h +++ b/WebCore/platform/graphics/BitmapImage.h @@ -140,7 +140,7 @@ public: virtual CGImageRef getCGImageRef(); #endif -#if PLATFORM(WIN) || (PLATFORM(QT) && PLATFORM(WIN_OS)) +#if PLATFORM(WIN) || (PLATFORM(QT) && OS(WINDOWS)) static PassRefPtr<BitmapImage> create(HBITMAP); #endif #if PLATFORM(WIN) @@ -173,7 +173,7 @@ protected: #endif virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator); -#if PLATFORM(WX) || (PLATFORM(WINCE) && !PLATFORM(QT)) +#if PLATFORM(WX) || (OS(WINCE) && !PLATFORM(QT)) virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect); #endif @@ -240,7 +240,7 @@ protected: checkForSolidColor(); // WINCE PORT: checkForSolidColor() doesn't set m_checkedForSolidColor until // it gets enough information to make final decision. -#if !PLATFORM(WINCE) +#if !OS(WINCE) ASSERT(m_checkedForSolidColor); #endif } diff --git a/WebCore/platform/graphics/FloatPoint.h b/WebCore/platform/graphics/FloatPoint.h index 45a1e83..6b037ff 100644 --- a/WebCore/platform/graphics/FloatPoint.h +++ b/WebCore/platform/graphics/FloatPoint.h @@ -36,7 +36,7 @@ typedef struct CGPoint CGPoint; #endif -#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGPoint NSPoint; #else @@ -85,7 +85,7 @@ public: #endif #if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)) \ - || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) + || (PLATFORM(CHROMIUM) && OS(DARWIN)) FloatPoint(const NSPoint&); operator NSPoint() const; #endif diff --git a/WebCore/platform/graphics/FloatQuad.cpp b/WebCore/platform/graphics/FloatQuad.cpp index 427230d..d1069fb 100644 --- a/WebCore/platform/graphics/FloatQuad.cpp +++ b/WebCore/platform/graphics/FloatQuad.cpp @@ -85,6 +85,12 @@ FloatRect FloatQuad::boundingBox() const return FloatRect(left, top, right - left, bottom - top); } +bool FloatQuad::isRectilinear() const +{ + return (m_p1.x() == m_p2.x() && m_p2.y() == m_p3.y() && m_p3.x() == m_p4.x() && m_p4.y() == m_p1.y()) + || (m_p1.y() == m_p2.y() && m_p2.x() == m_p3.x() && m_p3.y() == m_p4.y() && m_p4.x() == m_p1.x()); +} + bool FloatQuad::containsPoint(const FloatPoint& p) const { return isPointInTriangle(p, m_p1, m_p2, m_p3) || isPointInTriangle(p, m_p1, m_p3, m_p4); diff --git a/WebCore/platform/graphics/FloatQuad.h b/WebCore/platform/graphics/FloatQuad.h index 5982967..6cd86f6 100644 --- a/WebCore/platform/graphics/FloatQuad.h +++ b/WebCore/platform/graphics/FloatQuad.h @@ -74,6 +74,12 @@ public: // "slanted" empty quads. bool isEmpty() const { return boundingBox().isEmpty(); } + // Tests whether this quad can be losslessly represented by a FloatRect, + // that is, if two edges are parallel to the x-axis and the other two + // are parallel to the y-axis. If this method returns true, the + // corresponding FloatRect can be retrieved with boundingBox(). + bool isRectilinear() const; + // Tests whether the given point is inside, or on an edge or corner of this quad. bool containsPoint(const FloatPoint&) const; diff --git a/WebCore/platform/graphics/FloatRect.h b/WebCore/platform/graphics/FloatRect.h index 2dc854d..74a6293 100644 --- a/WebCore/platform/graphics/FloatRect.h +++ b/WebCore/platform/graphics/FloatRect.h @@ -33,7 +33,7 @@ typedef struct CGRect CGRect; #endif -#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGRect NSRect; #else @@ -61,6 +61,10 @@ struct SkRect; namespace WebCore { +#if PLATFORM(OPENVG) +class VGRect; +#endif + class IntRect; class FloatRect { @@ -129,7 +133,7 @@ public: #endif #if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)) \ - || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) + || (PLATFORM(CHROMIUM) && OS(DARWIN)) FloatRect(const NSRect&); operator NSRect() const; #endif @@ -154,6 +158,10 @@ public: operator SkRect() const; #endif +#if PLATFORM(OPENVG) + operator VGRect() const; +#endif + private: FloatPoint m_location; FloatSize m_size; diff --git a/WebCore/platform/graphics/FloatSize.h b/WebCore/platform/graphics/FloatSize.h index 5a84fd1..1bc3423 100644 --- a/WebCore/platform/graphics/FloatSize.h +++ b/WebCore/platform/graphics/FloatSize.h @@ -34,7 +34,7 @@ typedef struct CGSize CGSize; #endif -#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGSize NSSize; #else @@ -80,7 +80,7 @@ public: #endif #if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)) \ - || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) + || (PLATFORM(CHROMIUM) && OS(DARWIN)) explicit FloatSize(const NSSize &); // don't do this implicitly since it's lossy operator NSSize() const; #endif diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp index 88774cb..7d0e5a9 100644 --- a/WebCore/platform/graphics/Font.cpp +++ b/WebCore/platform/graphics/Font.cpp @@ -24,7 +24,6 @@ #include "config.h" #include "Font.h" -#include "CharacterNames.h" #include "FloatRect.h" #include "FontCache.h" #include "FontFallbackList.h" @@ -266,6 +265,22 @@ FontSelector* Font::fontSelector() const return m_fontList ? m_fontList->fontSelector() : 0; } +String Font::normalizeSpaces(const String& string) +{ + unsigned length = string.length(); + Vector<UChar, 256> buffer(length); + bool didReplacement = false; + + for (unsigned i = 0; i < length; ++i) { + UChar originalCharacter = string[i]; + buffer[i] = normalizeSpaces(originalCharacter); + if (buffer[i] != originalCharacter) + didReplacement = true; + } + + return didReplacement ? String(buffer.data(), length) : string; +} + static bool shouldUseFontSmoothing = true; void Font::setShouldUseSmoothing(bool shouldUseSmoothing) diff --git a/WebCore/platform/graphics/Font.h b/WebCore/platform/graphics/Font.h index c067071..3c07be7 100644 --- a/WebCore/platform/graphics/Font.h +++ b/WebCore/platform/graphics/Font.h @@ -25,10 +25,13 @@ #ifndef Font_h #define Font_h +#include "CharacterNames.h" #include "TextRun.h" #include "FontDescription.h" #include "SimpleFontData.h" +#include "TypesettingFeatures.h" #include <wtf/HashMap.h> +#include <wtf/HashSet.h> #include <wtf/MathExtras.h> #if PLATFORM(QT) @@ -96,6 +99,12 @@ public: FontRenderingMode renderingMode() const { return m_fontDescription.renderingMode(); } + TypesettingFeatures typesettingFeatures() const + { + TextRenderingMode textRenderingMode = m_fontDescription.textRenderingMode(); + return textRenderingMode == OptimizeLegibility || textRenderingMode == GeometricPrecision ? Kerning | Ligatures : 0; + } + FontFamily& firstFamily() { return m_fontDescription.firstFamily(); } const FontFamily& family() const { return m_fontDescription.family(); } @@ -175,6 +184,19 @@ public: static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; } static bool treatAsZeroWidthSpace(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e) || c == 0xFFFC; } + static inline UChar normalizeSpaces(UChar character) + { + if (treatAsSpace(character)) + return space; + + if (treatAsZeroWidthSpace(character)) + return zeroWidthSpace; + + return character; + } + + static String normalizeSpaces(const String&); + #if ENABLE(SVG_FONTS) bool isSVGFont() const; SVGFontElement* svgFont() const; diff --git a/WebCore/platform/graphics/FontCache.cpp b/WebCore/platform/graphics/FontCache.cpp index 06ea56b..2aa68f1 100644 --- a/WebCore/platform/graphics/FontCache.cpp +++ b/WebCore/platform/graphics/FontCache.cpp @@ -53,7 +53,7 @@ FontCache::FontCache() { } -struct FontPlatformDataCacheKey { +struct FontPlatformDataCacheKey : FastAllocBase { FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false, bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode) : m_family(family) @@ -139,7 +139,7 @@ static const AtomicString& alternateFamilyName(const AtomicString& familyName) DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New")); if (equalIgnoringCase(familyName, courier)) return courierNew; -#if !PLATFORM(WIN_OS) +#if !OS(WINDOWS) // On Windows, Courier New (truetype font) is always present and // Courier is a bitmap font. So, we don't want to map Courier New to // Courier. @@ -163,7 +163,7 @@ static const AtomicString& alternateFamilyName(const AtomicString& familyName) if (equalIgnoringCase(familyName, helvetica)) return arial; -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) // On Windows, bitmap fonts are blocked altogether so that we have to // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif")); diff --git a/WebCore/platform/graphics/FontCache.h b/WebCore/platform/graphics/FontCache.h index 4a6222b..9b41e38 100644 --- a/WebCore/platform/graphics/FontCache.h +++ b/WebCore/platform/graphics/FontCache.h @@ -64,7 +64,7 @@ public: // Also implemented by the platform. void platformInit(); -#if PLATFORM(WINCE) && !PLATFORM(QT) +#if OS(WINCE) && !PLATFORM(QT) #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) IMLangFontLink2* getFontLinkInterface(); #else diff --git a/WebCore/platform/graphics/FontFastPath.cpp b/WebCore/platform/graphics/FontFastPath.cpp index 5246593..428e85e 100644 --- a/WebCore/platform/graphics/FontFastPath.cpp +++ b/WebCore/platform/graphics/FontFastPath.cpp @@ -135,13 +135,7 @@ GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCap UChar codeUnits[2]; int codeUnitsLength; if (c <= 0xFFFF) { - UChar c16 = c; - if (Font::treatAsSpace(c16)) - codeUnits[0] = ' '; - else if (Font::treatAsZeroWidthSpace(c16)) - codeUnits[0] = zeroWidthSpace; - else - codeUnits[0] = c16; + codeUnits[0] = Font::normalizeSpaces(c); codeUnitsLength = 1; } else { codeUnits[0] = U16_LEAD(c); @@ -157,7 +151,7 @@ GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCap 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 PLATFORM(WINCE) +#if OS(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. @@ -175,7 +169,7 @@ GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCap // FIXME: It would be nicer to use the missing glyph from the last resort font instead. GlyphData data = primaryFont()->missingGlyphData(); if (!useSmallCapsFont) { -#if PLATFORM(WINCE) +#if OS(WINCE) // See comment about WINCE GDI handling near setGlyphDataForCharacter above. page->setGlyphDataForCharacter(c, c, data.fontData); return page->glyphDataForCharacter(c); @@ -251,8 +245,7 @@ bool Font::canUseGlyphCache(const TextRun& run) const return false; } - TextRenderingMode textMode = m_fontDescription.textRenderingMode(); - if (textMode == OptimizeLegibility || textMode == GeometricPrecision) + if (typesettingFeatures()) return false; return true; diff --git a/WebCore/platform/graphics/GeneratedImage.cpp b/WebCore/platform/graphics/GeneratedImage.cpp index eec7ffb..8395aff 100644 --- a/WebCore/platform/graphics/GeneratedImage.cpp +++ b/WebCore/platform/graphics/GeneratedImage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,17 +10,17 @@ * 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. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -50,19 +50,24 @@ void GeneratedImage::draw(GraphicsContext* context, const FloatRect& dstRect, co void GeneratedImage::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect) { + // Allow the generator to provide visually-equivalent tiling parameters for better performance. + IntSize adjustedSize = m_size; + FloatRect adjustedSrcRect = srcRect; + m_generator->adjustParametersForTiledDrawing(adjustedSize, adjustedSrcRect); + // Create a BitmapImage and call drawPattern on it. - OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(m_size); + OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(adjustedSize); ASSERT(imageBuffer.get()); - + // Fill with the gradient. GraphicsContext* graphicsContext = imageBuffer->context(); - graphicsContext->fillRect(FloatRect(FloatPoint(), m_size), *m_generator.get()); - + graphicsContext->fillRect(FloatRect(FloatPoint(), adjustedSize), *m_generator.get()); + // Grab the final image from the image buffer. Image* bitmap = imageBuffer->image(); - + // Now just call drawTiled on that image. - bitmap->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, compositeOp, destRect); + bitmap->drawPattern(context, adjustedSrcRect, patternTransform, phase, styleColorSpace, compositeOp, destRect); } } diff --git a/WebCore/platform/graphics/Generator.h b/WebCore/platform/graphics/Generator.h index a0af689..b64d051 100644 --- a/WebCore/platform/graphics/Generator.h +++ b/WebCore/platform/graphics/Generator.h @@ -38,6 +38,7 @@ public: virtual ~Generator() {}; virtual void fill(GraphicsContext*, const FloatRect&) = 0; + virtual void adjustParametersForTiledDrawing(IntSize& /* size */, FloatRect& /* srcRect */) { } }; } //namespace diff --git a/WebCore/platform/graphics/GlyphBuffer.h b/WebCore/platform/graphics/GlyphBuffer.h index 04491a7..edb804c 100644 --- a/WebCore/platform/graphics/GlyphBuffer.h +++ b/WebCore/platform/graphics/GlyphBuffer.h @@ -50,7 +50,7 @@ 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) +#elif OS(WINCE) typedef wchar_t GlyphBufferGlyph; #else typedef Glyph GlyphBufferGlyph; @@ -60,7 +60,7 @@ typedef Glyph GlyphBufferGlyph; // can be passed directly to CGContextShowGlyphsWithAdvances in FontMac.mm #if PLATFORM(CG) typedef CGSize GlyphBufferAdvance; -#elif PLATFORM(WINCE) +#elif OS(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; @@ -124,7 +124,7 @@ public: { #if PLATFORM(CG) return m_advances[index].width; -#elif PLATFORM(WINCE) +#elif OS(WINCE) return m_advances[index]; #else return m_advances[index].width(); @@ -156,7 +156,7 @@ public: #if PLATFORM(CG) CGSize advance = { width, 0 }; m_advances.append(advance); -#elif PLATFORM(WINCE) +#elif OS(WINCE) m_advances.append(width); #else m_advances.append(FloatSize(width, 0)); @@ -172,7 +172,7 @@ public: #endif } -#if !PLATFORM(WINCE) +#if !OS(WINCE) void add(Glyph glyph, const SimpleFontData* font, GlyphBufferAdvance advance) { m_fontData.append(font); diff --git a/WebCore/platform/graphics/Gradient.cpp b/WebCore/platform/graphics/Gradient.cpp index 204a2b6..17d461f 100644 --- a/WebCore/platform/graphics/Gradient.cpp +++ b/WebCore/platform/graphics/Gradient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * * Redistribution and use in source and binary forms, with or without @@ -28,6 +28,8 @@ #include "Gradient.h" #include "Color.h" +#include "FloatRect.h" +#include <wtf/UnusedParam.h> namespace WebCore { @@ -62,6 +64,28 @@ Gradient::~Gradient() platformDestroy(); } +void Gradient::adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect) +{ + if (m_radial) + return; + + if (srcRect.isEmpty()) + return; + + if (m_p0.x() == m_p1.x()) { + size.setWidth(1); + srcRect.setWidth(1); + srcRect.setX(0); + return; + } + if (m_p0.y() != m_p1.y()) + return; + + size.setHeight(1); + srcRect.setHeight(1); + srcRect.setY(0); +} + void Gradient::addColorStop(float value, const Color& color) { float r; @@ -81,6 +105,16 @@ static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::Co return a.stop < b.stop; } +void Gradient::sortStopsIfNecessary() +{ + if (m_stopsSorted) + return; + + if (m_stops.size()) + std::stable_sort(m_stops.begin(), m_stops.end(), compareStops); + m_stopsSorted = true; +} + void Gradient::getColor(float value, float* r, float* g, float* b, float* a) const { ASSERT(value >= 0); diff --git a/WebCore/platform/graphics/Gradient.h b/WebCore/platform/graphics/Gradient.h index 011a2cd..b65550d 100644 --- a/WebCore/platform/graphics/Gradient.h +++ b/WebCore/platform/graphics/Gradient.h @@ -36,8 +36,15 @@ #include <wtf/Vector.h> #if PLATFORM(CG) + +#ifdef BUILDING_ON_TIGER typedef struct CGShading* CGShadingRef; typedef CGShadingRef PlatformGradient; +#else +typedef struct CGGradient* CGGradientRef; +typedef CGGradientRef PlatformGradient; +#endif + #elif PLATFORM(QT) QT_BEGIN_NAMESPACE class QGradient; @@ -79,7 +86,7 @@ namespace WebCore { void getColor(float value, float* r, float* g, float* b, float* a) const; -#if PLATFORM(WINCE) && !PLATFORM(QT) +#if OS(WINCE) && !PLATFORM(QT) const FloatPoint& p0() const { return m_p0; } const FloatPoint& p1() const { return m_p1; } float r0() const { return m_r0; } @@ -105,7 +112,7 @@ namespace WebCore { }; void setStopsSorted(bool s) { m_stopsSorted = s; } - + void setSpreadMethod(GradientSpreadMethod); GradientSpreadMethod spreadMethod() { return m_spreadMethod; } void setGradientSpaceTransform(const TransformationMatrix& gradientSpaceTransformation); @@ -113,8 +120,13 @@ namespace WebCore { TransformationMatrix gradientSpaceTransform() { return m_gradientSpaceTransformation; } virtual void fill(GraphicsContext*, const FloatRect&); + virtual void adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect); + void setPlatformGradientSpaceTransform(const TransformationMatrix& gradientSpaceTransformation); +#if PLATFORM(CG) + void paint(GraphicsContext*); +#endif private: Gradient(const FloatPoint& p0, const FloatPoint& p1); Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1); @@ -123,6 +135,7 @@ namespace WebCore { void platformDestroy(); int findStop(float value) const; + void sortStopsIfNecessary(); bool m_radial; FloatPoint m_p0; diff --git a/WebCore/platform/graphics/GraphicsContext.cpp b/WebCore/platform/graphics/GraphicsContext.cpp index e80004f..eac8bcd 100644 --- a/WebCore/platform/graphics/GraphicsContext.cpp +++ b/WebCore/platform/graphics/GraphicsContext.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 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 @@ -10,17 +10,17 @@ * 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. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -328,7 +328,7 @@ void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const drawImage(image, styleColorSpace, FloatRect(dest), srcRect, op, useLowQualityScale); } -#if !PLATFORM(WINCE) || PLATFORM(QT) +#if !OS(WINCE) || PLATFORM(QT) void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to) { if (paintingDisabled()) @@ -382,55 +382,6 @@ void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace); } -void GraphicsContext::initFocusRing(int width, int offset) -{ - if (paintingDisabled()) - return; - clearFocusRing(); - - m_common->m_focusRingWidth = width; - m_common->m_focusRingOffset = offset; -} - -void GraphicsContext::clearFocusRing() -{ - m_common->m_focusRingRects.clear(); -} - -IntRect GraphicsContext::focusRingBoundingRect() -{ - IntRect result = IntRect(0, 0, 0, 0); - - const Vector<IntRect>& rects = focusRingRects(); - unsigned rectCount = rects.size(); - for (unsigned i = 0; i < rectCount; i++) - result.unite(rects[i]); - - return result; -} - -void GraphicsContext::addFocusRingRect(const IntRect& rect) -{ - if (paintingDisabled() || rect.isEmpty()) - return; - m_common->m_focusRingRects.append(rect); -} - -int GraphicsContext::focusRingWidth() const -{ - return m_common->m_focusRingWidth; -} - -int GraphicsContext::focusRingOffset() const -{ - return m_common->m_focusRingOffset; -} - -const Vector<IntRect>& GraphicsContext::focusRingRects() const -{ - return m_common->m_focusRingRects; -} - void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale) { if (paintingDisabled() || !image) @@ -460,24 +411,35 @@ void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const restore(); } -void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op) +void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale) { if (paintingDisabled() || !image) return; - + if (useLowQualityScale) { + save(); + setImageInterpolationQuality(InterpolationLow); + } image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op); + if (useLowQualityScale) + restore(); } -void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op) +void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale) { if (paintingDisabled() || !image) return; + if (useLowQualityScale) { + save(); + setImageInterpolationQuality(InterpolationLow); + } if (hRule == Image::StretchTile && vRule == Image::StretchTile) // Just do a scale. - return drawImage(image, styleColorSpace, dest, srcRect, op); - - image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op); + drawImage(image, styleColorSpace, dest, srcRect, op); + else + image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op); + if (useLowQualityScale) + restore(); } void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, @@ -544,7 +506,11 @@ void GraphicsContext::setPlatformTextDrawingMode(int mode) } #endif +<<<<<<< HEAD #if !PLATFORM(QT) && !PLATFORM(CAIRO) && !(PLATFORM(SKIA) && !PLATFORM(ANDROID)) && !PLATFORM(HAIKU) +======= +#if !PLATFORM(QT) && !PLATFORM(CAIRO) && !PLATFORM(SKIA) && !PLATFORM(HAIKU) && !PLATFORM(OPENVG) +>>>>>>> webkit.org at r54127 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&) { } diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index ecf2101..063c490 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -41,6 +41,11 @@ typedef struct CGContext PlatformGraphicsContext; #elif PLATFORM(CAIRO) typedef struct _cairo PlatformGraphicsContext; +#elif PLATFORM(OPENVG) +namespace WebCore { +class SurfaceOpenVG; +} +typedef class WebCore::SurfaceOpenVG PlatformGraphicsContext; #elif PLATFORM(QT) QT_BEGIN_NAMESPACE class QPainter; @@ -79,7 +84,7 @@ typedef class PlatformContextSkia PlatformGraphicsContext; class BView; typedef BView PlatformGraphicsContext; struct pattern; -#elif PLATFORM(WINCE) +#elif OS(WINCE) typedef struct HDC__ PlatformGraphicsContext; #else typedef void PlatformGraphicsContext; @@ -104,7 +109,7 @@ typedef unsigned char UInt8; namespace WebCore { -#if PLATFORM(WINCE) && !PLATFORM(QT) +#if OS(WINCE) && !PLATFORM(QT) class SharedBitmap; class SimpleFontData; class GlyphBuffer; @@ -152,7 +157,7 @@ namespace WebCore { GraphicsContext(PlatformGraphicsContext*); ~GraphicsContext(); -#if !PLATFORM(WINCE) || PLATFORM(QT) +#if !OS(WINCE) || PLATFORM(QT) PlatformGraphicsContext* platformContext() const; #endif @@ -255,10 +260,10 @@ namespace WebCore { void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false); void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, - CompositeOperator = CompositeSourceOver); + CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false); void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntRect& srcRect, Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile, - CompositeOperator = CompositeSourceOver); + CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false); void setImageInterpolationQuality(InterpolationQuality); InterpolationQuality imageInterpolationQuality() const; @@ -297,11 +302,8 @@ namespace WebCore { bool getShadow(IntSize&, int&, Color&) const; void clearShadow(); - void initFocusRing(int width, int offset); - void addFocusRingRect(const IntRect&); - void drawFocusRing(const Color&); - void clearFocusRing(); - IntRect focusRingBoundingRect(); + void drawFocusRing(const Vector<IntRect>&, int width, int offset, const Color&); + void drawFocusRing(const Vector<Path>&, int width, int offset, const Color&); void setLineCap(LineCap); void setLineDash(const DashArray&, float dashOffset); @@ -330,6 +332,7 @@ namespace WebCore { void scale(const FloatSize&); void rotate(float angleInRadians); + void translate(const FloatSize& size) { translate(size.width(), size.height()); } void translate(float x, float y); IntPoint origin(); @@ -338,7 +341,7 @@ namespace WebCore { void concatCTM(const TransformationMatrix&); TransformationMatrix getCTM() const; -#if PLATFORM(WINCE) && !PLATFORM(QT) +#if OS(WINCE) && !PLATFORM(QT) void setBitmap(PassRefPtr<SharedBitmap>); const TransformationMatrix& affineTransform() const; TransformationMatrix& affineTransform(); @@ -396,7 +399,7 @@ namespace WebCore { void drawWindowsBitmap(WindowsBitmap*, const IntPoint&); #endif -#if (PLATFORM(QT) && defined(Q_WS_WIN)) || (PLATFORM(WX) && PLATFORM(WIN_OS)) +#if (PLATFORM(QT) && defined(Q_WS_WIN)) || (PLATFORM(WX) && OS(WINDOWS)) HDC getWindowsContext(const IntRect&, bool supportAlphaBlend = true, bool mayCreateBitmap = true); void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend = true, bool mayCreateBitmap = true); bool shouldIncludeChildWindows() const { return false; } @@ -446,10 +449,6 @@ namespace WebCore { static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, const StrokeStyle&); - int focusRingWidth() const; - int focusRingOffset() const; - const Vector<IntRect>& focusRingRects() const; - static GraphicsContextPrivate* createGraphicsContextPrivate(); static void destroyGraphicsContextPrivate(GraphicsContextPrivate*); diff --git a/WebCore/platform/graphics/GraphicsContext3D.h b/WebCore/platform/graphics/GraphicsContext3D.h index aad8dd4..d7406c9 100644 --- a/WebCore/platform/graphics/GraphicsContext3D.h +++ b/WebCore/platform/graphics/GraphicsContext3D.h @@ -33,7 +33,7 @@ #include <wtf/PassOwnPtr.h> // FIXME: Find a better way to avoid the name confliction for NO_ERROR. -#if PLATFORM(CHROMIUM) && PLATFORM(WIN_OS) +#if PLATFORM(CHROMIUM) && OS(WINDOWS) #undef NO_ERROR #endif @@ -65,9 +65,6 @@ namespace WebCore { class WebGLShader; class WebGLTexture; class Image; - class HTMLVideoElement; - class ImageData; - class WebKitCSSMatrix; struct ActiveInfo { String name; @@ -385,7 +382,25 @@ namespace WebCore { INVALID_FRAMEBUFFER_OPERATION = 0x0506 }; - static PassOwnPtr<GraphicsContext3D> create(); + // Context creation attributes. + struct Attributes { + Attributes() + : alpha(true) + , depth(true) + , stencil(true) + , antialias(true) + , premultipliedAlpha(true) + { + } + + bool alpha; + bool depth; + bool stencil; + bool antialias; + bool premultipliedAlpha; + }; + + static PassOwnPtr<GraphicsContext3D> create(Attributes attrs); virtual ~GraphicsContext3D(); #if PLATFORM(MAC) @@ -462,6 +477,8 @@ namespace WebCore { void getBufferParameteriv(unsigned long target, unsigned long pname, int* value); + Attributes getContextAttributes(); + unsigned long getError(); void getFloatv(unsigned long pname, float* value); @@ -528,32 +545,14 @@ namespace WebCore { // These next several functions return an error code (0 if no errors) rather than using an ExceptionCode. // Currently they return -1 on any error. - int texImage2D(unsigned target, unsigned level, unsigned internalformat, - unsigned width, unsigned height, unsigned border, - unsigned format, unsigned type, WebGLArray* pixels); - int texImage2D(unsigned target, unsigned level, unsigned internalformat, - unsigned width, unsigned height, unsigned border, - unsigned format, unsigned type, ImageData* pixels); - int texImage2D(unsigned target, unsigned level, Image* image, - bool flipY, bool premultiplyAlpha); - int texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, - bool flipY, bool premultiplyAlpha); + int texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, void* pixels); + int texImage2D(unsigned target, unsigned level, Image* image, bool flipY, bool premultiplyAlpha); void texParameterf(unsigned target, unsigned pname, float param); void texParameteri(unsigned target, unsigned pname, int param); - int texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - unsigned width, unsigned height, - unsigned format, unsigned type, WebGLArray* pixels); - int texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - unsigned width, unsigned height, - unsigned format, unsigned type, ImageData* pixels); - int texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - unsigned width, unsigned height, Image* image, - bool flipY, bool premultiplyAlpha); - int texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - unsigned width, unsigned height, HTMLVideoElement* video, - bool flipY, bool premultiplyAlpha); + int texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, unsigned width, unsigned height, unsigned format, unsigned type, void* pixels); + int texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, Image* image, bool flipY, bool premultiplyAlpha); void uniform1f(long location, float x); void uniform1fv(long location, float* v, int size); @@ -623,11 +622,12 @@ namespace WebCore { void synthesizeGLError(unsigned long error); private: - GraphicsContext3D(); + GraphicsContext3D(Attributes attrs); int m_currentWidth, m_currentHeight; #if PLATFORM(MAC) + Attributes m_attrs; Vector<Vector<float> > m_vertexArray; CGLContextObj m_contextObj; diff --git a/WebCore/platform/graphics/GraphicsContextPrivate.h b/WebCore/platform/graphics/GraphicsContextPrivate.h index c532162..1980337 100644 --- a/WebCore/platform/graphics/GraphicsContextPrivate.h +++ b/WebCore/platform/graphics/GraphicsContextPrivate.h @@ -48,7 +48,7 @@ namespace WebCore { , shadowBlur(0) , shadowsIgnoreTransforms(false) #if PLATFORM(CAIRO) - , globalAlpha(1.0f) + , globalAlpha(1) #endif { } @@ -87,17 +87,12 @@ namespace WebCore { class GraphicsContextPrivate : public Noncopyable { public: GraphicsContextPrivate() - : m_focusRingWidth(0) - , m_focusRingOffset(0) - , m_updatingControlTints(false) + : m_updatingControlTints(false) { } GraphicsContextState state; Vector<GraphicsContextState> stack; - Vector<IntRect> m_focusRingRects; - int m_focusRingWidth; - int m_focusRingOffset; bool m_updatingControlTints; }; diff --git a/WebCore/platform/graphics/GraphicsLayer.cpp b/WebCore/platform/graphics/GraphicsLayer.cpp index e215097..2336d0b 100644 --- a/WebCore/platform/graphics/GraphicsLayer.cpp +++ b/WebCore/platform/graphics/GraphicsLayer.cpp @@ -72,6 +72,8 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) , m_contentsOrientation(CompositingCoordinatesTopDown) , m_parent(0) , m_maskLayer(0) + , m_replicaLayer(0) + , m_replicatedLayer(0) , m_repaintCount(0) { } @@ -214,6 +216,14 @@ void GraphicsLayer::removeFromParent() } } +void GraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer) +{ + if (layer) + layer->setReplicatedLayer(this); + + m_replicaLayer = layer; +} + void GraphicsLayer::setBackgroundColor(const Color& color) { m_backgroundColor = color; @@ -454,14 +464,27 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent) const } ts << ")\n"; - writeIndent(ts, indent + 1); - ts << "(children " << m_children.size() << "\n"; + if (m_replicaLayer) { + writeIndent(ts, indent + 1); + ts << "(replica layer " << m_replicaLayer << ")\n"; + m_replicaLayer->dumpLayer(ts, indent+2); + } + + if (m_replicatedLayer) { + writeIndent(ts, indent + 1); + ts << "(replicated layer " << m_replicatedLayer << ")\n"; + } - unsigned i; - for (i = 0; i < m_children.size(); i++) - m_children[i]->dumpLayer(ts, indent+2); - writeIndent(ts, indent + 1); - ts << ")\n"; + if (m_children.size()) { + writeIndent(ts, indent + 1); + ts << "(children " << m_children.size() << "\n"; + + unsigned i; + for (i = 0; i < m_children.size(); i++) + m_children[i]->dumpLayer(ts, indent+2); + writeIndent(ts, indent + 1); + ts << ")\n"; + } } } // namespace WebCore diff --git a/WebCore/platform/graphics/GraphicsLayer.h b/WebCore/platform/graphics/GraphicsLayer.h index 0456bad..844301e 100644 --- a/WebCore/platform/graphics/GraphicsLayer.h +++ b/WebCore/platform/graphics/GraphicsLayer.h @@ -59,6 +59,10 @@ class WKCACFLayer; typedef WKCACFLayer PlatformLayer; typedef void* NativeLayer; } +#elif PLATFORM(QT) +class QGraphicsItem; +typedef QGraphicsItem PlatformLayer; +typedef QGraphicsItem* NativeLayer; #else typedef void* PlatformLayer; typedef void* NativeLayer; @@ -198,6 +202,16 @@ public: GraphicsLayer* maskLayer() const { return m_maskLayer; } virtual void setMaskLayer(GraphicsLayer* layer) { m_maskLayer = layer; } + // The given layer will replicate this layer and its children; the replica renders behind this layer. + virtual void setReplicatedByLayer(GraphicsLayer*); + // Whether this layer is being replicated by another layer. + bool isReplicated() const { return m_replicaLayer; } + // The layer that replicates this layer (if any). + GraphicsLayer* replicaLayer() const { return m_replicaLayer; } + + const FloatPoint& replicatedLayerPosition() const { return m_replicatedLayerPosition; } + void setReplicatedLayerPosition(const FloatPoint& p) { m_replicatedLayerPosition = p; } + // Offset is origin of the renderer minus origin of the graphics layer (so either zero or negative). IntSize offsetFromRenderer() const { return m_offsetFromRenderer; } void setOffsetFromRenderer(const IntSize& offset) { m_offsetFromRenderer = offset; } @@ -260,17 +274,17 @@ public: // 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 bool addAnimation(const KeyframeValueList&, const IntSize& /*boxSize*/, const Animation*, const String& /*keyframesName*/, double /*timeOffset*/) { return false; } virtual void removeAnimationsForProperty(AnimatedPropertyID) { } virtual void removeAnimationsForKeyframes(const String& /* keyframesName */) { } - virtual void pauseAnimation(const String& /* keyframesName */) { } + virtual void pauseAnimation(const String& /* keyframesName */, double /*timeOffset*/) { } virtual void suspendAnimations(double time); virtual void resumeAnimations(); // Layer contents virtual void setContentsToImage(Image*) { } - virtual void setContentsToVideo(PlatformLayer*) { } + virtual void setContentsToMedia(PlatformLayer*) { } // video or plug-in virtual void setContentsBackgroundColor(const Color&) { } #if ENABLE(3D_CANVAS) @@ -279,6 +293,8 @@ public: #endif // Callback from the underlying graphics system to draw layer contents. void paintGraphicsLayerContents(GraphicsContext&, const IntRect& clip); + // Callback from the underlying graphics system when the layer has been displayed + virtual void didDisplay(PlatformLayer*) { } virtual PlatformLayer* platformLayer() const { return 0; } @@ -327,6 +343,10 @@ protected: virtual void setOpacityInternal(float) { } + // The layer being replicated. + GraphicsLayer* replicatedLayer() const { return m_replicatedLayer; } + virtual void setReplicatedLayer(GraphicsLayer* layer) { m_replicatedLayer = layer; } + GraphicsLayer(GraphicsLayerClient*); void dumpProperties(TextStream&, int indent) const; @@ -365,6 +385,11 @@ protected: GraphicsLayer* m_maskLayer; // Reference to mask layer. We don't own this. + GraphicsLayer* m_replicaLayer; // A layer that replicates this layer. We only allow one, for now. + // The replica is not parented; this is the primary reference to it. + GraphicsLayer* m_replicatedLayer; // For a replica layer, a reference to the original layer. + FloatPoint m_replicatedLayerPosition; // For a replica layer, the position of the replica. + IntRect m_contentsRect; int m_repaintCount; diff --git a/WebCore/platform/graphics/Image.h b/WebCore/platform/graphics/Image.h index f25169b..234104a 100644 --- a/WebCore/platform/graphics/Image.h +++ b/WebCore/platform/graphics/Image.h @@ -154,6 +154,7 @@ public: #if PLATFORM(GTK) virtual GdkPixbuf* getGdkPixbuf() { return 0; } + static PassRefPtr<Image> loadPlatformThemeIcon(const char* name, int size); #endif protected: diff --git a/WebCore/platform/graphics/ImageSource.cpp b/WebCore/platform/graphics/ImageSource.cpp index bf7ae21..c2366c1 100644 --- a/WebCore/platform/graphics/ImageSource.cpp +++ b/WebCore/platform/graphics/ImageSource.cpp @@ -35,12 +35,6 @@ #include "ImageDecoder.h" #endif -#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) -#ifndef IMAGE_DECODER_DOWN_SAMPLING_MAX_NUMBER_OF_PIXELS -#define IMAGE_DECODER_DOWN_SAMPLING_MAX_NUMBER_OF_PIXELS (1024 * 1024) -#endif -#endif - namespace WebCore { ImageSource::ImageSource() @@ -81,6 +75,9 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) if (!m_decoder) { m_decoder = static_cast<NativeImageSourcePtr>(ImageDecoder::create(*data)); #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) +#ifndef IMAGE_DECODER_DOWN_SAMPLING_MAX_NUMBER_OF_PIXELS +#define IMAGE_DECODER_DOWN_SAMPLING_MAX_NUMBER_OF_PIXELS (1024 * 1024) +#endif if (m_decoder) m_decoder->setMaxNumPixels(IMAGE_DECODER_DOWN_SAMPLING_MAX_NUMBER_OF_PIXELS); #endif diff --git a/WebCore/platform/graphics/ImageSource.h b/WebCore/platform/graphics/ImageSource.h index 083c7b0..cc40f5e 100644 --- a/WebCore/platform/graphics/ImageSource.h +++ b/WebCore/platform/graphics/ImageSource.h @@ -55,7 +55,7 @@ class NativeImageSkia; #endif #elif PLATFORM(HAIKU) class BBitmap; -#elif PLATFORM(WINCE) +#elif OS(WINCE) #include "SharedBitmap.h" #endif @@ -105,7 +105,7 @@ typedef wxBitmap* NativeImagePtr; typedef cairo_surface_t* NativeImagePtr; #elif PLATFORM(HAIKU) typedef BBitmap* NativeImagePtr; -#elif PLATFORM(WINCE) +#elif OS(WINCE) typedef RefPtr<SharedBitmap> NativeImagePtr; #endif #endif diff --git a/WebCore/platform/graphics/IntRect.cpp b/WebCore/platform/graphics/IntRect.cpp index 622e525..188b5f9 100644 --- a/WebCore/platform/graphics/IntRect.cpp +++ b/WebCore/platform/graphics/IntRect.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006, 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 @@ -10,17 +10,17 @@ * 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. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -104,4 +104,15 @@ void IntRect::scale(float s) m_size.setHeight((int)(height() * s)); } +IntRect unionRect(const Vector<IntRect>& rects) +{ + IntRect result; + + size_t count = rects.size(); + for (size_t i = 0; i < count; ++i) + result.unite(rects[i]); + + return result; } + +} // namespace WebCore diff --git a/WebCore/platform/graphics/IntRect.h b/WebCore/platform/graphics/IntRect.h index 97b21bc..e3633df 100644 --- a/WebCore/platform/graphics/IntRect.h +++ b/WebCore/platform/graphics/IntRect.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006, 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 @@ -10,17 +10,17 @@ * 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. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef IntRect_h @@ -28,12 +28,13 @@ #include "IntPoint.h" #include <wtf/Platform.h> +#include <wtf/Vector.h> #if PLATFORM(CG) typedef struct CGRect CGRect; #endif -#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGRect NSRect; #else @@ -162,7 +163,7 @@ public: #endif #if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)) \ - || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) + || (PLATFORM(CHROMIUM) && OS(DARWIN)) operator NSRect() const; #endif @@ -185,6 +186,8 @@ inline IntRect unionRect(const IntRect& a, const IntRect& b) return c; } +IntRect unionRect(const Vector<IntRect>&); + inline bool operator==(const IntRect& a, const IntRect& b) { return a.location() == b.location() && a.size() == b.size(); @@ -200,7 +203,7 @@ IntRect enclosingIntRect(const CGRect&); #endif #if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)) \ - || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) + || (PLATFORM(CHROMIUM) && OS(DARWIN)) IntRect enclosingIntRect(const NSRect&); #endif diff --git a/WebCore/platform/graphics/IntSize.h b/WebCore/platform/graphics/IntSize.h index b242784..13d9e22 100644 --- a/WebCore/platform/graphics/IntSize.h +++ b/WebCore/platform/graphics/IntSize.h @@ -69,7 +69,8 @@ public: void setHeight(int height) { m_height = height; } bool isEmpty() const { return m_width <= 0 || m_height <= 0; } - + bool isZero() const { return !m_width && !m_height; } + void expand(int width, int height) { m_width += width; diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index 4c66c50..aed9c95 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,7 +39,7 @@ #if PLATFORM(MAC) #include "MediaPlayerPrivateQTKit.h" -#elif PLATFORM(WINCE) && !PLATFORM(QT) +#elif OS(WINCE) && !PLATFORM(QT) #include "MediaPlayerPrivateWince.h" #elif PLATFORM(WIN) #include "MediaPlayerPrivateQuickTimeWin.h" @@ -83,8 +83,6 @@ public: virtual void seek(float) { } virtual bool seeking() const { return false; } - virtual void setEndTime(float) { } - virtual void setRate(float) { } virtual void setPreservesPitch(bool) { } virtual bool paused() const { return false; } @@ -100,9 +98,6 @@ public: virtual float maxTimeSeekable() const { return 0; } virtual PassRefPtr<TimeRanges> buffered() const { return TimeRanges::create(); } - virtual int dataRate() const { return 0; } - - virtual bool totalBytesKnown() const { return false; } virtual unsigned totalBytes() const { return 0; } virtual unsigned bytesLoaded() const { return 0; } @@ -252,7 +247,8 @@ void MediaPlayer::load(const String& url, const ContentType& contentType) #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) m_private->setMediaPlayerProxy(m_playerProxy); #endif - + m_private->setAutobuffer(autobuffer()); + m_private->setPreservesPitch(preservesPitch()); } if (m_private) @@ -261,6 +257,11 @@ void MediaPlayer::load(const String& url, const ContentType& contentType) m_private.set(createNullMediaPlayer(this)); } +bool MediaPlayer::hasAvailableVideoFrame() const +{ + return m_private->hasAvailableVideoFrame(); +} + bool MediaPlayer::canLoadPoster() const { return m_private->canLoadPoster(); @@ -412,16 +413,6 @@ void MediaPlayer::setPreservesPitch(bool preservesPitch) m_private->setPreservesPitch(preservesPitch); } -int MediaPlayer::dataRate() const -{ - return m_private->dataRate(); -} - -void MediaPlayer::setEndTime(float time) -{ - m_private->setEndTime(time); -} - PassRefPtr<TimeRanges> MediaPlayer::buffered() { return m_private->buffered(); @@ -437,16 +428,6 @@ unsigned MediaPlayer::bytesLoaded() return m_private->bytesLoaded(); } -bool MediaPlayer::totalBytesKnown() -{ - return m_private->totalBytesKnown(); -} - -unsigned MediaPlayer::totalBytes() -{ - return m_private->totalBytes(); -} - void MediaPlayer::setSize(const IntSize& size) { m_size = size; diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index ec8ac33..fa85cfd 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -163,8 +163,6 @@ public: float startTime() const; - void setEndTime(float time); - float rate() const; void setRate(float); @@ -175,8 +173,6 @@ public: float maxTimeSeekable(); unsigned bytesLoaded(); - bool totalBytesKnown(); - unsigned totalBytes(); float volume() const; void setVolume(float); @@ -184,8 +180,6 @@ public: bool hasClosedCaptions() const; void setClosedCaptionsVisible(bool closedCaptionsVisible); - int dataRate() const; - bool autobuffer() const; void setAutobuffer(bool); @@ -213,6 +207,8 @@ public: MediaPlayerClient* mediaPlayerClient() const { return m_mediaPlayerClient; } + bool hasAvailableVideoFrame() const; + bool canLoadPoster() const; void setPoster(const String&); diff --git a/WebCore/platform/graphics/MediaPlayerPrivate.h b/WebCore/platform/graphics/MediaPlayerPrivate.h index 03906bd..6cf12ba 100644 --- a/WebCore/platform/graphics/MediaPlayerPrivate.h +++ b/WebCore/platform/graphics/MediaPlayerPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -67,8 +67,6 @@ public: virtual float startTime() const { return 0; } - virtual void setEndTime(float) = 0; - virtual void setRate(float) = 0; virtual void setPreservesPitch(bool) { } @@ -85,10 +83,6 @@ public: virtual float maxTimeSeekable() const = 0; virtual PassRefPtr<TimeRanges> buffered() const = 0; - virtual int dataRate() const = 0; - - virtual bool totalBytesKnown() const { return totalBytes() > 0; } - virtual unsigned totalBytes() const = 0; virtual unsigned bytesLoaded() const = 0; virtual void setSize(const IntSize&) = 0; @@ -99,6 +93,8 @@ public: virtual void setAutobuffer(bool) { }; + virtual bool hasAvailableVideoFrame() const { return readyState() >= MediaPlayer::HaveCurrentData; } + virtual bool canLoadPoster() const { return false; } virtual void setPoster(const String&) { } diff --git a/WebCore/platform/graphics/Path.h b/WebCore/platform/graphics/Path.h index 6618fb7..79d6cc4 100644 --- a/WebCore/platform/graphics/Path.h +++ b/WebCore/platform/graphics/Path.h @@ -34,10 +34,7 @@ #if PLATFORM(CG) typedef struct CGPath PlatformPath; #elif PLATFORM(QT) -#include <qglobal.h> -QT_BEGIN_NAMESPACE -class QPainterPath; -QT_END_NAMESPACE +#include <qpainterpath.h> typedef QPainterPath PlatformPath; #elif PLATFORM(WX) && USE(WXGC) class wxGraphicsPath; @@ -53,7 +50,7 @@ typedef SkPath PlatformPath; #elif PLATFORM(HAIKU) class BRegion; typedef BRegion PlatformPath; -#elif PLATFORM(WINCE) +#elif OS(WINCE) namespace WebCore { class PlatformPath; } @@ -61,6 +58,13 @@ namespace WebCore { typedef void PlatformPath; #endif +#if PLATFORM(QT) +/* QPainterPath is valued based */ +typedef PlatformPath PlatformPathPtr; +#else +typedef PlatformPath* PlatformPathPtr; +#endif + namespace WebCore { class FloatPoint; @@ -131,7 +135,7 @@ namespace WebCore { String debugString() const; - PlatformPath* platformPath() const { return m_path; } + PlatformPathPtr platformPath() const { return m_path; } static Path createRoundedRectangle(const FloatRect&, const FloatSize& roundingRadii); static Path createRoundedRectangle(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); @@ -144,7 +148,7 @@ namespace WebCore { void transform(const TransformationMatrix&); private: - PlatformPath* m_path; + PlatformPathPtr m_path; }; } diff --git a/WebCore/platform/graphics/Pattern.h b/WebCore/platform/graphics/Pattern.h index aa0a357..f7f612a 100644 --- a/WebCore/platform/graphics/Pattern.h +++ b/WebCore/platform/graphics/Pattern.h @@ -56,7 +56,7 @@ typedef wxBrush* PlatformPatternPtr; #elif PLATFORM(HAIKU) #include <interface/GraphicsDefs.h> typedef pattern* PlatformPatternPtr; -#elif PLATFORM(WINCE) +#elif OS(WINCE) typedef void* PlatformPatternPtr; #endif diff --git a/WebCore/platform/graphics/SimpleFontData.h b/WebCore/platform/graphics/SimpleFontData.h index 387a5c7..09ed0fc 100644 --- a/WebCore/platform/graphics/SimpleFontData.h +++ b/WebCore/platform/graphics/SimpleFontData.h @@ -28,14 +28,14 @@ #include "FontPlatformData.h" #include "GlyphPageTreeNode.h" #include "GlyphWidthMap.h" -#include "TextRenderingMode.h" +#include "TypesettingFeatures.h" #include <wtf/OwnPtr.h> #if USE(ATSUI) typedef struct OpaqueATSUStyle* ATSUStyle; #endif -#if PLATFORM(WIN) && !PLATFORM(WINCE) +#if PLATFORM(WIN) && !OS(WINCE) #include <usp10.h> #endif @@ -115,13 +115,13 @@ public: virtual String description() const; #endif -#if PLATFORM(MAC) +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) NSFont* getNSFont() const { return m_platformData.font(); } #endif #if USE(CORE_TEXT) CTFontRef getCTFont() const; - CFDictionaryRef getCFStringAttributes(TextRenderingMode) const; + CFDictionaryRef getCFStringAttributes(TypesettingFeatures) const; #endif #if USE(ATSUI) @@ -140,7 +140,7 @@ public: #if PLATFORM(WIN) bool isSystemFont() const { return m_isSystemFont; } -#if !PLATFORM(WINCE) // disable unused members to save space +#if !OS(WINCE) // disable unused members to save space SCRIPT_FONTPROPERTIES* scriptFontProperties() const; SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; } #endif @@ -162,7 +162,7 @@ private: void commonInit(); -#if PLATFORM(WIN) && !PLATFORM(WINCE) +#if PLATFORM(WIN) && !OS(WINCE) void initGDIFont(); void platformCommonDestroy(); float widthForGDIGlyph(Glyph glyph) const; @@ -211,8 +211,7 @@ private: #if USE(ATSUI) public: - mutable ATSUStyle m_ATSUStyle; - mutable bool m_ATSUStyleInitialized; + mutable HashMap<unsigned, ATSUStyle> m_ATSUStyleMap; mutable bool m_ATSUMirrors; mutable bool m_checkedShapesArabic; mutable bool m_shapesArabic; @@ -222,12 +221,12 @@ private: #if USE(CORE_TEXT) mutable RetainPtr<CTFontRef> m_CTFont; - mutable RetainPtr<CFDictionaryRef> m_CFStringAttributes; + mutable HashMap<unsigned, RetainPtr<CFDictionaryRef> > m_CFStringAttributes; #endif #if PLATFORM(WIN) bool m_isSystemFont; -#if !PLATFORM(WINCE) // disable unused members to save space +#if !OS(WINCE) // disable unused members to save space mutable SCRIPT_CACHE m_scriptCache; mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties; #endif diff --git a/WebCore/platform/graphics/TypesettingFeatures.h b/WebCore/platform/graphics/TypesettingFeatures.h new file mode 100644 index 0000000..aa46beb --- /dev/null +++ b/WebCore/platform/graphics/TypesettingFeatures.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TypesettingFeatures_h +#define TypesettingFeatures_h + +namespace WebCore { + enum TypesettingFeature { + Kerning = 1 << 0, + Ligatures = 1 << 1, + }; + + typedef unsigned TypesettingFeatures; +} // namespace WebCore + +#endif // TypesettingFeatures_h diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index 14034fd..d866b6c 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -606,12 +606,16 @@ void GraphicsContext::clipPath(WindRule clipRule) cairo_clip(cr); } -void GraphicsContext::drawFocusRing(const Color& color) +void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) +{ + // FIXME: implement +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color) { if (paintingDisabled()) return; - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); cairo_t* cr = m_data->cr; @@ -632,14 +636,14 @@ void GraphicsContext::drawFocusRing(const Color& color) cairo_set_line_width(cr, 2.0f); setPlatformStrokeStyle(DottedStroke); #else - int radius = (focusRingWidth() - 1) / 2; + int radius = (width - 1) / 2; for (unsigned i = 0; i < rectCount; i++) addPath(Path::createRoundedRectangle(rects[i], FloatSize(radius, radius))); // Force the alpha to 50%. This matches what the Mac does with outline rings. Color ringColor(color.red(), color.green(), color.blue(), 127); setColor(cr, ringColor); - cairo_set_line_width(cr, focusRingWidth()); + cairo_set_line_width(cr, width); setPlatformStrokeStyle(SolidStroke); #endif diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index 54e1217..cedc684 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -33,8 +33,8 @@ #include <wtf/MathExtras.h> #if PLATFORM(GTK) -#include <gdk/gdk.h> #include <pango/pango.h> +typedef struct _GdkExposeEvent GdkExposeEvent; #elif PLATFORM(WIN) #include <cairo-win32.h> #endif diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index d991c80..124c7cf 100644 --- a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -132,7 +132,7 @@ void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) pixelColor = Color(lookUpTable[pixelColor.red()], lookUpTable[pixelColor.green()], lookUpTable[pixelColor.blue()], - lookUpTable[pixelColor.alpha()]); + pixelColor.alpha()); *pixel = premultipliedARGBFromColor(pixelColor); } } diff --git a/WebCore/platform/graphics/cg/ColorCG.cpp b/WebCore/platform/graphics/cg/ColorCG.cpp index 40aacc5..e514fa3 100644 --- a/WebCore/platform/graphics/cg/ColorCG.cpp +++ b/WebCore/platform/graphics/cg/ColorCG.cpp @@ -68,7 +68,7 @@ Color::Color(CGColorRef color) m_color = makeRGBA(r * 255, g * 255, b * 255, a * 255); } -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) CGColorRef createCGColor(const Color& c) { @@ -89,7 +89,7 @@ CGColorRef createCGColor(const Color& c) return color; } -#endif // PLATFORM(WIN_OS) +#endif // OS(WINDOWS) } diff --git a/WebCore/platform/graphics/cg/GradientCG.cpp b/WebCore/platform/graphics/cg/GradientCG.cpp index 05a0aad..e9b5de7 100644 --- a/WebCore/platform/graphics/cg/GradientCG.cpp +++ b/WebCore/platform/graphics/cg/GradientCG.cpp @@ -36,10 +36,15 @@ namespace WebCore { void Gradient::platformDestroy() { +#ifdef BUILDING_ON_TIGER CGShadingRelease(m_gradient); +#else + CGGradientRelease(m_gradient); +#endif m_gradient = 0; } +#ifdef BUILDING_ON_TIGER static void gradientCallback(void* info, const CGFloat* in, CGFloat* out) { float r, g, b, a; @@ -69,11 +74,55 @@ CGShadingRef Gradient::platformGradient() return m_gradient; } +#else +CGGradientRef Gradient::platformGradient() +{ + if (m_gradient) + return m_gradient; + + static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + sortStopsIfNecessary(); + + const int cReservedStops = 3; + Vector<CGFloat, 4 * cReservedStops> colorComponents; + colorComponents.reserveCapacity(m_stops.size() * 4); // RGBA components per stop + + Vector<CGFloat, cReservedStops> locations; + locations.reserveCapacity(m_stops.size()); + + for (size_t i = 0; i < m_stops.size(); ++i) { + colorComponents.uncheckedAppend(m_stops[i].red); + colorComponents.uncheckedAppend(m_stops[i].green); + colorComponents.uncheckedAppend(m_stops[i].blue); + colorComponents.uncheckedAppend(m_stops[i].alpha); + + locations.uncheckedAppend(m_stops[i].stop); + } + + m_gradient = CGGradientCreateWithColorComponents(colorSpace, colorComponents.data(), locations.data(), m_stops.size()); + + return m_gradient; +} +#endif void Gradient::fill(GraphicsContext* context, const FloatRect& rect) { context->clip(rect); + paint(context); +} + +void Gradient::paint(GraphicsContext* context) +{ +#ifdef BUILDING_ON_TIGER CGContextDrawShading(context->platformContext(), platformGradient()); +#else + CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; + if (m_radial) + CGContextDrawRadialGradient(context->platformContext(), platformGradient(), m_p0, m_r0, m_p1, m_r1, extendOptions); + else + CGContextDrawLinearGradient(context->platformContext(), platformGradient(), m_p0, m_p1, extendOptions); +#endif } } //namespace diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index 39f06a6..b11ba66 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -43,7 +43,15 @@ #include <wtf/OwnArrayPtr.h> #include <wtf/RetainPtr.h> -#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN)) +#if PLATFORM(MAC) || PLATFORM(CHROMIUM) +#include "WebCoreSystemInterface.h" +#endif + +#if PLATFORM(WIN) +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#endif + +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) // Building on 10.6 or later: kCGInterpolationMedium is defined in the CGInterpolationQuality enum. @@ -490,7 +498,7 @@ static inline bool calculateDrawingMode(const GraphicsContextState& state, CGPat } } else { // Setting mode to kCGPathStroke even if shouldStroke is false. In that case, we return false and mode will not be used, - // but the compiler will not compain about an uninitialized variable. + // but the compiler will not complain about an uninitialized variable. mode = kCGPathStroke; } @@ -547,7 +555,7 @@ void GraphicsContext::fillPath() else CGContextClip(context); CGContextConcatCTM(context, m_common->state.fillGradient->gradientSpaceTransform()); - CGContextDrawShading(context, m_common->state.fillGradient->platformGradient()); + m_common->state.fillGradient->paint(this); CGContextRestoreGState(context); return; } @@ -572,7 +580,7 @@ void GraphicsContext::strokePath() CGContextReplacePathWithStrokedPath(context); CGContextClip(context); CGContextConcatCTM(context, m_common->state.strokeGradient->gradientSpaceTransform()); - CGContextDrawShading(context, m_common->state.strokeGradient->platformGradient()); + m_common->state.strokeGradient->paint(this); CGContextRestoreGState(context); return; } @@ -596,7 +604,7 @@ void GraphicsContext::fillRect(const FloatRect& rect) CGContextSaveGState(context); CGContextClipToRect(context, rect); CGContextConcatCTM(context, m_common->state.fillGradient->gradientSpaceTransform()); - CGContextDrawShading(context, m_common->state.fillGradient->platformGradient()); + m_common->state.fillGradient->paint(this); CGContextRestoreGState(context); return; } @@ -739,56 +747,55 @@ void GraphicsContext::endTransparencyLayer() m_data->m_userToDeviceTransformKnownToBeIdentity = false; } -void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Color& color, ColorSpace colorSpace) +void GraphicsContext::setPlatformShadow(const IntSize& offset, int blur, const Color& color, ColorSpace colorSpace) { if (paintingDisabled()) return; - CGFloat width = size.width(); - CGFloat height = size.height(); + CGFloat xOffset = offset.width(); + CGFloat yOffset = offset.height(); CGFloat blurRadius = blur; CGContextRef context = platformContext(); if (!m_common->state.shadowsIgnoreTransforms) { - CGAffineTransform transform = CGContextGetCTM(context); + CGAffineTransform userToBaseCTM = wkGetUserToBaseCTM(context); - CGFloat A = transform.a * transform.a + transform.b * transform.b; - CGFloat B = transform.a * transform.c + transform.b * transform.d; + CGFloat A = userToBaseCTM.a * userToBaseCTM.a + userToBaseCTM.b * userToBaseCTM.b; + CGFloat B = userToBaseCTM.a * userToBaseCTM.c + userToBaseCTM.b * userToBaseCTM.d; CGFloat C = B; - CGFloat D = transform.c * transform.c + transform.d * transform.d; + CGFloat D = userToBaseCTM.c * userToBaseCTM.c + userToBaseCTM.d * userToBaseCTM.d; CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D))))); // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp blurRadius = min(blur * smallEigenvalue, narrowPrecisionToCGFloat(1000.0)); - CGSize sizeInDeviceSpace = CGSizeApplyAffineTransform(size, transform); - - width = sizeInDeviceSpace.width; - height = sizeInDeviceSpace.height; + CGSize offsetInBaseSpace = CGSizeApplyAffineTransform(offset, userToBaseCTM); + xOffset = offsetInBaseSpace.width; + yOffset = offsetInBaseSpace.height; } // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated // to the desired integer. static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128); - if (width > 0) - width += extraShadowOffset; - else if (width < 0) - width -= extraShadowOffset; + if (xOffset > 0) + xOffset += extraShadowOffset; + else if (xOffset < 0) + xOffset -= extraShadowOffset; - if (height > 0) - height += extraShadowOffset; - else if (height < 0) - height -= extraShadowOffset; + if (yOffset > 0) + yOffset += extraShadowOffset; + else if (yOffset < 0) + yOffset -= extraShadowOffset; // Check for an invalid color, as this means that the color was not set for the shadow // and we should therefore just use the default shadow color. if (!color.isValid()) - CGContextSetShadow(context, CGSizeMake(width, height), blurRadius); + CGContextSetShadow(context, CGSizeMake(xOffset, yOffset), blurRadius); else { RetainPtr<CGColorRef> colorCG(AdoptCF, createCGColorWithColorSpace(color, colorSpace)); CGContextSetShadowWithColor(context, - CGSizeMake(width, height), + CGSizeMake(xOffset, yOffset), blurRadius, colorCG.get()); } @@ -838,7 +845,7 @@ void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth) CGContextAddRect(context, r); CGContextReplacePathWithStrokedPath(context); CGContextClip(context); - CGContextDrawShading(context, m_common->state.strokeGradient->platformGradient()); + m_common->state.strokeGradient->paint(this); CGContextRestoreGState(context); return; } diff --git a/WebCore/platform/graphics/cg/PatternCG.cpp b/WebCore/platform/graphics/cg/PatternCG.cpp index 63628f4..26f402b 100644 --- a/WebCore/platform/graphics/cg/PatternCG.cpp +++ b/WebCore/platform/graphics/cg/PatternCG.cpp @@ -61,7 +61,7 @@ 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 + // INT_MAX is almost correct, but there seems to be some number wrapping occurring making the fill // 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); diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp index 6bd7d7c..6432e17 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp @@ -32,11 +32,11 @@ #include "config.h" #include "FontCustomPlatformData.h" -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) #include "Base64.h" #include "ChromiumBridge.h" #include "OpenTypeUtilities.h" -#elif PLATFORM(LINUX) +#elif OS(LINUX) #include "SkStream.h" #endif @@ -45,9 +45,9 @@ #include "OpenTypeSanitizer.h" #include "SharedBuffer.h" -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) #include <objbase.h> -#elif PLATFORM(LINUX) +#elif OS(LINUX) #include <cstring> #endif @@ -55,10 +55,10 @@ namespace WebCore { FontCustomPlatformData::~FontCustomPlatformData() { -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) if (m_fontReference) RemoveFontMemResourceEx(m_fontReference); -#elif PLATFORM(LINUX) +#elif OS(LINUX) if (m_fontReference) m_fontReference->unref(); #endif @@ -66,7 +66,7 @@ FontCustomPlatformData::~FontCustomPlatformData() FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode mode) { -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) ASSERT(m_fontReference); LOGFONT logFont; @@ -99,7 +99,7 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b HFONT hfont = CreateFontIndirect(&logFont); return FontPlatformData(hfont, size); -#elif PLATFORM(LINUX) +#elif OS(LINUX) ASSERT(m_fontReference); return FontPlatformData(m_fontReference, size, bold && !m_fontReference->isBold(), italic && !m_fontReference->isItalic()); #else @@ -108,7 +108,7 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b #endif } -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) // Creates a unique and unpredictable font name, in order to avoid collisions and to // not allow access from CSS. static String createUniqueFontName() @@ -123,7 +123,7 @@ static String createUniqueFontName() } #endif -#if PLATFORM(LINUX) +#if OS(LINUX) class RemoteFontStream : public SkStream { public: explicit RemoteFontStream(PassRefPtr<SharedBuffer> buffer) @@ -180,7 +180,7 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) buffer = transcodeBuffer.get(); #endif -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) // Introduce the font to GDI. AddFontMemResourceEx should be used with care, because it will pollute the process's // font namespace (Windows has no API for creating an HFONT from data without exposing the font to the // entire process first). @@ -189,9 +189,9 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) if (!fontReference) return 0; return new FontCustomPlatformData(fontReference, fontName); -#elif PLATFORM(LINUX) - RemoteFontStream stream(buffer); - SkTypeface* typeface = SkTypeface::CreateFromStream(&stream); +#elif OS(LINUX) + RemoteFontStream* stream = new RemoteFontStream(buffer); + SkTypeface* typeface = SkTypeface::CreateFromStream(stream); if (!typeface) return 0; return new FontCustomPlatformData(typeface); diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h b/WebCore/platform/graphics/chromium/FontCustomPlatformData.h index a42f1ec..e1fbd48 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.h @@ -35,10 +35,10 @@ #include "FontRenderingMode.h" #include <wtf/Noncopyable.h> -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) #include "PlatformString.h" #include <windows.h> -#elif PLATFORM(LINUX) +#elif OS(LINUX) #include "SkTypeface.h" #endif @@ -48,12 +48,12 @@ class FontPlatformData; class SharedBuffer; struct FontCustomPlatformData : Noncopyable { -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) FontCustomPlatformData(HANDLE fontReference, const String& name) : m_fontReference(fontReference) , m_name(name) {} -#elif PLATFORM(LINUX) +#elif OS(LINUX) explicit FontCustomPlatformData(SkTypeface* typeface) : m_fontReference(typeface) {} @@ -64,10 +64,10 @@ struct FontCustomPlatformData : Noncopyable { FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) HANDLE m_fontReference; String m_name; -#elif PLATFORM(LINUX) +#elif OS(LINUX) SkTypeface* m_fontReference; #endif }; diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp index a4526a8..e76eca8 100644 --- a/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -312,7 +312,8 @@ public: private: const TextRun& getTextRun(const TextRun& originalRun) { - // Convert the |originalRun| to NFC normalized form if combining diacritical marks + // Normalize the text run in two ways: + // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks // (U+0300..) are used in the run. This conversion is necessary since most OpenType // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in // their GSUB tables. @@ -321,9 +322,12 @@ private: // the API returns FALSE (= not normalized) for complex runs that don't require NFC // normalization (e.g., Arabic text). Unless the run contains the diacritical marks, // Harfbuzz will do the same thing for us using the GSUB table. + // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs + // for characters like '\n' otherwise. for (unsigned i = 0; i < originalRun.length(); ++i) { - UBlockCode block = ::ublock_getCode(originalRun[i]); - if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS) { + UChar ch = originalRun[i]; + UBlockCode block = ::ublock_getCode(ch); + if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS || (Font::treatAsSpace(ch) && ch != ' ')) { return getNormalizedTextRun(originalRun); } } @@ -342,6 +346,11 @@ private: normalizedString.extract(m_normalizedBuffer.get(), normalizedString.length() + 1, error); ASSERT(U_SUCCESS(error)); + for (unsigned i = 0; i < normalizedString.length(); ++i) { + if (Font::treatAsSpace(m_normalizedBuffer[i])) + m_normalizedBuffer[i] = ' '; + } + m_normalizedRun.set(new TextRun(originalRun)); m_normalizedRun->setText(m_normalizedBuffer.get(), normalizedString.length()); return *m_normalizedRun; @@ -391,6 +400,8 @@ private: m_item.attributes = new HB_GlyphAttributes[m_maxGlyphs]; m_item.advances = new HB_Fixed[m_maxGlyphs]; m_item.offsets = new HB_FixedPoint[m_maxGlyphs]; + // HB_FixedPoint is a struct, so we must use memset to clear it. + memset(m_item.offsets, 0, m_maxGlyphs * sizeof(HB_FixedPoint)); m_glyphs16 = new uint16_t[m_maxGlyphs]; m_xPositions = new SkScalar[m_maxGlyphs]; @@ -427,18 +438,19 @@ private: 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; + double position = 0; + for (int iter = 0; iter < m_item.num_glyphs; ++iter) { + // Glyphs are stored in logical order, but for layout purposes we always go left to right. + int i = isRTL ? m_item.num_glyphs - iter - 1 : iter; m_glyphs16[i] = m_item.glyphs[i]; - m_xPositions[index] = m_offsetX + m_pixelWidth; - m_pixelWidth += truncateFixedPointToInteger(m_item.advances[index]); + double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); + m_xPositions[i] = m_offsetX + position + offsetX; + + double advance = truncateFixedPointToInteger(m_item.advances[i]); + position += advance; } + m_pixelWidth = position; m_offsetX += m_pixelWidth; } diff --git a/WebCore/platform/graphics/chromium/FontPlatformData.h b/WebCore/platform/graphics/chromium/FontPlatformData.h index c6f1912..871fec8 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformData.h +++ b/WebCore/platform/graphics/chromium/FontPlatformData.h @@ -31,9 +31,9 @@ #ifndef FontPlatformData_h #define FontPlatformData_h -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) #include "FontPlatformDataChromiumWin.h" -#elif defined(__linux__) +#elif OS(LINUX) #include "FontPlatformDataLinux.h" #endif diff --git a/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp index 621d674..be3b0d0 100644 --- a/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp +++ b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp @@ -30,7 +30,6 @@ #include "config.h" -#include "Font.h" #include "FontPlatformData.h" #include "wtf/OwnArrayPtr.h" @@ -42,7 +41,6 @@ extern "C" { #include "harfbuzz-shaper.h" -#include "harfbuzz-unicode.h" } // This file implements the callbacks which Harfbuzz requires by using Skia @@ -67,29 +65,10 @@ static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_u // 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; } @@ -188,15 +167,16 @@ static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* met SkRect bounds; paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); - metrics->x = SkiaScalarToHarfbuzzFixed(width); + metrics->x = SkiaScalarToHarfbuzzFixed(bounds.fLeft); + metrics->y = SkiaScalarToHarfbuzzFixed(bounds.fTop); + metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width()); + metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height()); + + metrics->xOffset = 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); + metrics->yOffset = 0; } static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/WebCore/platform/graphics/chromium/TransparencyWin.cpp index 6dcd595..80df2ec 100644 --- a/WebCore/platform/graphics/chromium/TransparencyWin.cpp +++ b/WebCore/platform/graphics/chromium/TransparencyWin.cpp @@ -371,8 +371,11 @@ void TransparencyWin::initializeNewContext() return; m_drawContext = m_layerBuffer->context(); - if (needReferenceBitmap) + if (needReferenceBitmap) { m_referenceBitmap = m_ownedBuffers->referenceBitmap(); + if (!m_referenceBitmap || !m_referenceBitmap->getPixels()) + return; + } m_validLayer = true; return; } diff --git a/WebCore/platform/graphics/filters/FEColorMatrix.cpp b/WebCore/platform/graphics/filters/FEColorMatrix.cpp index f422157..bd19d14 100644 --- a/WebCore/platform/graphics/filters/FEColorMatrix.cpp +++ b/WebCore/platform/graphics/filters/FEColorMatrix.cpp @@ -184,6 +184,7 @@ void FEColorMatrix::apply(Filter* filter) break; case FECOLORMATRIX_TYPE_LUMINANCETOALPHA: effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(srcPixelArray, imageData, m_values); + setIsAlphaImage(true); break; } diff --git a/WebCore/platform/graphics/filters/FEComponentTransfer.cpp b/WebCore/platform/graphics/filters/FEComponentTransfer.cpp index 1d9cfff..f9aa011 100644 --- a/WebCore/platform/graphics/filters/FEComponentTransfer.cpp +++ b/WebCore/platform/graphics/filters/FEComponentTransfer.cpp @@ -3,6 +3,7 @@ 2004, 2005 Rob Buis <buis@kde.org> 2005 Eric Seidel <eric@webkit.org> 2009 Dirk Schulze <krit@webkit.org> + Copyright (C) Research In Motion Limited 2010. 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 @@ -140,7 +141,8 @@ static void linear(unsigned char* values, const ComponentTransferFunction& trans static void gamma(unsigned char* values, const ComponentTransferFunction& transferFunction) { for (unsigned i = 0; i < 256; ++i) { - double val = 255.0 * (transferFunction.amplitude * pow((i / 255.0), transferFunction.exponent) + transferFunction.offset); + double exponent = transferFunction.exponent; // RCVT doesn't like passing a double and a float to pow, so promote this to double + double val = 255.0 * (transferFunction.amplitude * pow((i / 255.0), exponent) + transferFunction.offset); val = std::max(0.0, std::min(255.0, val)); values[i] = static_cast<unsigned char>(val); } diff --git a/WebCore/platform/graphics/filters/FEComponentTransfer.h b/WebCore/platform/graphics/filters/FEComponentTransfer.h index 773aba7..ab84cf0 100644 --- a/WebCore/platform/graphics/filters/FEComponentTransfer.h +++ b/WebCore/platform/graphics/filters/FEComponentTransfer.h @@ -25,7 +25,6 @@ #if ENABLE(FILTERS) #include "FilterEffect.h" -#include "SVGFEDisplacementMap.h" #include "Filter.h" #include <wtf/Vector.h> diff --git a/WebCore/platform/graphics/filters/FEComposite.cpp b/WebCore/platform/graphics/filters/FEComposite.cpp index c540cb7..67d3d27 100644 --- a/WebCore/platform/graphics/filters/FEComposite.cpp +++ b/WebCore/platform/graphics/filters/FEComposite.cpp @@ -111,9 +111,7 @@ inline void arithmetic(const RefPtr<CanvasPixelArray>& srcPixelArrayA, CanvasPix unsigned char i1 = srcPixelArrayA->get(pixelOffset + channel); unsigned char i2 = srcPixelArrayB->get(pixelOffset + channel); - unsigned char result = scaledK1 * i1 * i2 + k2 * i1 + k3 * i2 + scaledK4; - if (channel == 3 && i1 == 0 && i2 == 0) - result = 0; + double result = scaledK1 * i1 * i2 + k2 * i1 + k3 * i2 + scaledK4; srcPixelArrayB->set(pixelOffset + channel, result); } } diff --git a/WebCore/platform/graphics/gtk/ImageGtk.cpp b/WebCore/platform/graphics/gtk/ImageGtk.cpp index 38da70d..c62d988 100644 --- a/WebCore/platform/graphics/gtk/ImageGtk.cpp +++ b/WebCore/platform/graphics/gtk/ImageGtk.cpp @@ -44,29 +44,30 @@ template <> void freeOwnedGPtr<GtkIconInfo>(GtkIconInfo* info) namespace WebCore { -static CString getIconFileNameOrFallback(const char* name, const char* fallback) +static CString getThemeIconFileName(const char* name, int size) { - GOwnPtr<GtkIconInfo> info(gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), - name, 16, GTK_ICON_LOOKUP_NO_SVG)); - if (!info) - return String::format("%s/webkit-1.0/images/%s.png", DATA_DIR, fallback).utf8(); + GtkIconInfo* iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), + name, size, GTK_ICON_LOOKUP_NO_SVG); + // Try to fallback on MISSING_IMAGE. + if (!iconInfo) + iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), + GTK_STOCK_MISSING_IMAGE, size, + GTK_ICON_LOOKUP_NO_SVG); + if (iconInfo) { + GOwnPtr<GtkIconInfo> info(iconInfo); + return CString(gtk_icon_info_get_filename(info.get())); + } - return CString(gtk_icon_info_get_filename(info.get())); + // No icon was found, this can happen if not GTK theme is set. In + // that case an empty Image will be created. + return CString(); } -static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(const char* name) +static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(CString name) { - CString fileName; - - // Find the path for the image - if (strcmp("missingImage", name) == 0) - fileName = getIconFileNameOrFallback(GTK_STOCK_MISSING_IMAGE, "missingImage"); - else - fileName = String::format("%s/webkit-1.0/images/%s.png", DATA_DIR, name).utf8(); - GOwnPtr<gchar> content; gsize length; - if (!g_file_get_contents(fileName.data(), &content.outPtr(), &length, 0)) + if (!g_file_get_contents(name.data(), &content.outPtr(), &length, 0)) return SharedBuffer::create(); return SharedBuffer::create(content.get(), length); @@ -80,14 +81,32 @@ void BitmapImage::invalidatePlatformData() { } -PassRefPtr<Image> Image::loadPlatformResource(const char* name) +PassRefPtr<Image> loadImageFromFile(CString fileName) { RefPtr<BitmapImage> img = BitmapImage::create(); - RefPtr<SharedBuffer> buffer = loadResourceSharedBuffer(name); - img->setData(buffer.release(), true); + if (!fileName.isNull()) { + RefPtr<SharedBuffer> buffer = loadResourceSharedBuffer(fileName); + img->setData(buffer.release(), true); + } return img.release(); } +PassRefPtr<Image> Image::loadPlatformResource(const char* name) +{ + CString fileName; + if (!strcmp("missingImage", name)) + fileName = getThemeIconFileName(GTK_STOCK_MISSING_IMAGE, 16); + if (fileName.isNull()) + fileName = String::format("%s/webkit-1.0/images/%s.png", DATA_DIR, name).utf8(); + + return loadImageFromFile(fileName); +} + +PassRefPtr<Image> Image::loadPlatformThemeIcon(const char* name, int size) +{ + return loadImageFromFile(getThemeIconFileName(name, size)); +} + static inline unsigned char* getCairoSurfacePixel(unsigned char* data, uint x, uint y, uint rowStride) { return data + (y * rowStride) + x * 4; diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp index a023dae..41f90f0 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp @@ -29,6 +29,9 @@ #include "CString.h" #include "DataSourceGStreamer.h" +#include "Document.h" +#include "Frame.h" +#include "FrameView.h" #include "GraphicsContext.h" #include "IntRect.h" #include "KURL.h" @@ -36,6 +39,7 @@ #include "MediaPlayer.h" #include "NotImplemented.h" #include "ScrollView.h" +#include "SecurityOrigin.h" #include "TimeRanges.h" #include "VideoSinkGStreamer.h" #include "Widget.h" @@ -46,6 +50,7 @@ #include <gst/video/video.h> #include <limits> #include <math.h> +#include <webkit/webkitwebview.h> #include <wtf/gtk/GOwnPtr.h> using namespace std; @@ -59,9 +64,24 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo MediaPlayer::NetworkState error; MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); gint percent = 0; + bool issueError = true; + bool attemptNextLocation = false; + + if (message->structure) { + const gchar* messageTypeName = gst_structure_get_name(message->structure); + + // Redirect messages are sent from elements, like qtdemux, to + // notify of the new location(s) of the media. + if (!g_strcmp0(messageTypeName, "redirect")) { + mp->mediaLocationChanged(message); + return true; + } + } switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ERROR: + if (mp && mp->pipelineReset()) + break; gst_message_parse_error(message, &err.outPtr(), &debug.outPtr()); LOG_VERBOSE(Media, "Error: %d, %s", err->code, err->message); @@ -72,13 +92,18 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo || err->code == GST_CORE_ERROR_MISSING_PLUGIN || err->code == GST_RESOURCE_ERROR_NOT_FOUND) error = MediaPlayer::FormatError; - else if (err->domain == GST_STREAM_ERROR) + else if (err->domain == GST_STREAM_ERROR) { error = MediaPlayer::DecodeError; - else if (err->domain == GST_RESOURCE_ERROR) + attemptNextLocation = true; + } else if (err->domain == GST_RESOURCE_ERROR) error = MediaPlayer::NetworkError; - if (mp) - mp->loadingFailed(error); + if (mp) { + if (attemptNextLocation) + issueError = !mp->loadNextLocation(); + if (issueError) + mp->loadingFailed(error); + } break; case GST_MESSAGE_EOS: LOG_VERBOSE(Media, "End of Stream"); @@ -91,6 +116,10 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo gst_message_parse_buffering(message, &percent); LOG_VERBOSE(Media, "Buffering %d", percent); break; + case GST_MESSAGE_DURATION: + LOG_VERBOSE(Media, "Duration changed"); + mp->durationChanged(); + break; default: LOG_VERBOSE(Media, "Unhandled GStreamer message type: %s", GST_MESSAGE_TYPE_NAME(message)); @@ -99,6 +128,69 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo return true; } +void mediaPlayerPrivateSourceChangedCallback(GObject *object, GParamSpec *pspec, gpointer data) +{ + MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); + GstElement* element; + + g_object_get(mp->m_playBin, "source", &element, NULL); + gst_object_replace((GstObject**) &mp->m_source, (GstObject*) element); + + if (element) { + GParamSpec* pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(element), "cookies"); + + // First check if the source element has a cookies property + // of the format we expect + if (!pspec || pspec->value_type != G_TYPE_STRV) + return; + + // Then get the cookies for the URI and set them + SoupSession* session = webkit_get_default_session(); + SoupCookieJar* cookieJar = SOUP_COOKIE_JAR(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); + + char* location; + g_object_get(element, "location", &location, NULL); + + SoupURI* uri = soup_uri_new(location); + g_free(location); + + // Let Apple web servers know we want to access their nice movie trailers. + if (g_str_equal(uri->host, "movies.apple.com")) + g_object_set(element, "user-agent", "Quicktime/7.2.0", NULL); + + char* cookies = soup_cookie_jar_get_cookies(cookieJar, uri, FALSE); + soup_uri_free(uri); + + char* cookiesStrv[] = {cookies, NULL}; + g_object_set(element, "cookies", cookiesStrv, NULL); + g_free(cookies); + + Frame* frame = mp->m_player->frameView() ? mp->m_player->frameView()->frame() : 0; + Document* document = frame ? frame->document() : 0; + if (document) { + GstStructure* extraHeaders = gst_structure_new("extra-headers", + "Referer", G_TYPE_STRING, + document->documentURI().utf8().data(), 0); + g_object_set(element, "extra-headers", extraHeaders, NULL); + gst_structure_free(extraHeaders); + } + } + + gst_object_unref(element); +} + +void mediaPlayerPrivateVolumeChangedCallback(GObject *element, GParamSpec *pspec, gpointer data) +{ + MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); + mp->volumeChanged(); +} + +gboolean notifyVolumeIdleCallback(MediaPlayer* mp) +{ + mp->volumeChanged(); + return FALSE; +} + static float playbackPosition(GstElement* playbin) { @@ -114,8 +206,8 @@ static float playbackPosition(GstElement* playbin) gint64 position; gst_query_parse_position(query, 0, &position); - // Position is available only if the pipeline is not in NULL or - // READY state. + // Position is available only if the pipeline is not in GST_STATE_NULL or + // GST_STATE_READY state. if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE)) ret = static_cast<float>(position) / static_cast<float>(GST_SECOND); @@ -126,6 +218,7 @@ static float playbackPosition(GstElement* playbin) return ret; } + void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivate* playerPrivate) { g_return_if_fail(GST_IS_BUFFER(buffer)); @@ -146,7 +239,7 @@ void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) static bool gstInitialized = false; -static bool do_gst_init() +static bool doGstInit() { // FIXME: We should pass the arguments from the command line if (!gstInitialized) { @@ -165,7 +258,7 @@ static bool do_gst_init() bool MediaPlayerPrivate::isAvailable() { - if (!do_gst_init()) + if (!doGstInit()) return false; GstElementFactory* factory = gst_element_factory_find("playbin2"); @@ -180,6 +273,7 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_playBin(0) , m_videoSink(0) + , m_fpsSink(0) , m_source(0) , m_seekTime(0) , m_changingRate(false) @@ -190,19 +284,40 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_isStreaming(false) , m_size(IntSize()) , m_buffer(0) + , m_mediaLocations(0) + , m_mediaLocationCurrentIndex(0) + , m_resetPipeline(false) , m_paused(true) , m_seeking(false) + , m_playbackRate(1) , m_errorOccured(false) + , m_volumeIdleId(-1) + , m_mediaDuration(0.0) { - do_gst_init(); + doGstInit(); } MediaPlayerPrivate::~MediaPlayerPrivate() { + if (m_volumeIdleId) { + g_source_remove(m_volumeIdleId); + m_volumeIdleId = -1; + } + if (m_buffer) gst_buffer_unref(m_buffer); m_buffer = 0; + if (m_mediaLocations) { + gst_structure_free(m_mediaLocations); + m_mediaLocations = 0; + } + + if (m_source) { + gst_object_unref(m_source); + m_source = 0; + } + if (m_playBin) { gst_element_set_state(m_playBin, GST_STATE_NULL); gst_object_unref(GST_OBJECT(m_playBin)); @@ -212,6 +327,11 @@ MediaPlayerPrivate::~MediaPlayerPrivate() g_object_unref(m_videoSink); m_videoSink = 0; } + + if (m_fpsSink) { + g_object_unref(m_fpsSink); + m_fpsSink = 0; + } } void MediaPlayerPrivate::load(const String& url) @@ -230,28 +350,35 @@ void MediaPlayerPrivate::load(const String& url) pause(); } -void MediaPlayerPrivate::play() +bool MediaPlayerPrivate::changePipelineState(GstState newState) { - GstState state; + ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED); + + GstState currentState; GstState pending; - gst_element_get_state(m_playBin, &state, &pending, 0); - if (state != GST_STATE_PLAYING && pending != GST_STATE_PLAYING) { - LOG_VERBOSE(Media, "Play"); - gst_element_set_state(m_playBin, GST_STATE_PLAYING); + gst_element_get_state(m_playBin, ¤tState, &pending, 0); + if (currentState != newState && pending != newState) { + GstStateChangeReturn ret = gst_element_set_state(m_playBin, newState); + GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING; + if (currentState != pausedOrPlaying && ret == GST_STATE_CHANGE_FAILURE) { + loadingFailed(MediaPlayer::Empty); + return false; + } } + return true; } -void MediaPlayerPrivate::pause() +void MediaPlayerPrivate::play() { - GstState state; - GstState pending; + if (changePipelineState(GST_STATE_PLAYING)) + LOG_VERBOSE(Media, "Play"); +} - gst_element_get_state(m_playBin, &state, &pending, 0); - if (state != GST_STATE_PAUSED && pending != GST_STATE_PAUSED) { +void MediaPlayerPrivate::pause() +{ + if (changePipelineState(GST_STATE_PAUSED)) LOG_VERBOSE(Media, "Pause"); - gst_element_set_state(m_playBin, GST_STATE_PAUSED); - } } float MediaPlayerPrivate::duration() const @@ -262,6 +389,9 @@ float MediaPlayerPrivate::duration() const if (m_errorOccured) return 0.0; + if (m_mediaDuration) + return m_mediaDuration; + GstFormat timeFormat = GST_FORMAT_TIME; gint64 timeLength = 0; @@ -293,7 +423,9 @@ float MediaPlayerPrivate::currentTime() const void MediaPlayerPrivate::seek(float time) { - GstClockTime sec = (GstClockTime)(time * GST_SECOND); + // Avoid useless seeking. + if (time == playbackPosition(m_playBin)) + return; if (!m_playBin) return; @@ -304,6 +436,7 @@ void MediaPlayerPrivate::seek(float time) if (m_errorOccured) return; + GstClockTime sec = (GstClockTime)(time * GST_SECOND); LOG_VERBOSE(Media, "Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(sec)); if (!gst_element_seek(m_playBin, m_player->rate(), GST_FORMAT_TIME, @@ -317,11 +450,6 @@ void MediaPlayerPrivate::seek(float time) } } -void MediaPlayerPrivate::setEndTime(float time) -{ - notImplemented(); -} - void MediaPlayerPrivate::startEndPointTimerIfNeeded() { notImplemented(); @@ -384,7 +512,7 @@ bool MediaPlayerPrivate::hasVideo() const { gint currentVideo = -1; if (m_playBin) - g_object_get(G_OBJECT(m_playBin), "current-video", ¤tVideo, NULL); + g_object_get(m_playBin, "current-video", ¤tVideo, NULL); return currentVideo > -1; } @@ -392,7 +520,7 @@ bool MediaPlayerPrivate::hasAudio() const { gint currentAudio = -1; if (m_playBin) - g_object_get(G_OBJECT(m_playBin), "current-audio", ¤tAudio, NULL); + g_object_get(m_playBin, "current-audio", ¤tAudio, NULL); return currentAudio > -1; } @@ -401,11 +529,25 @@ void MediaPlayerPrivate::setVolume(float volume) if (!m_playBin) return; - g_object_set(G_OBJECT(m_playBin), "volume", static_cast<double>(volume), NULL); + g_object_set(m_playBin, "volume", static_cast<double>(volume), NULL); } +void MediaPlayerPrivate::volumeChanged() +{ + if (m_volumeIdleId) { + g_source_remove(m_volumeIdleId); + m_volumeIdleId = -1; + } + m_volumeIdleId = g_idle_add((GSourceFunc) notifyVolumeIdleCallback, m_player); +} + + void MediaPlayerPrivate::setRate(float rate) { + // Avoid useless playback rate update. + if (m_playbackRate == rate) + return; + GstState state; GstState pending; @@ -417,6 +559,7 @@ void MediaPlayerPrivate::setRate(float rate) if (m_isStreaming) return; + m_playbackRate = rate; m_changingRate = true; float currentPosition = playbackPosition(m_playBin) * GST_SECOND; GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH); @@ -452,12 +595,6 @@ void MediaPlayerPrivate::setRate(float rate) g_object_set(m_playBin, "mute", mute, NULL); } -int MediaPlayerPrivate::dataRate() const -{ - notImplemented(); - return 1; -} - MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const { return m_networkState; @@ -512,13 +649,7 @@ unsigned MediaPlayerPrivate::bytesLoaded() const if (!dur) return 0;*/ - return 1;//totalBytes() * maxTime / dur; -} - -bool MediaPlayerPrivate::totalBytesKnown() const -{ - LOG_VERBOSE(Media, "totalBytesKnown"); - return totalBytes() > 0; + return 1; // totalBytes() * maxTime / dur; } unsigned MediaPlayerPrivate::totalBytes() const @@ -573,6 +704,8 @@ void MediaPlayerPrivate::updateStates() gst_element_state_get_name(state), gst_element_state_get_name(pending)); + m_resetPipeline = state <= GST_STATE_READY; + if (state == GST_STATE_READY) m_readyState = MediaPlayer::HaveNothing; else if (state == GST_STATE_PAUSED) @@ -581,6 +714,11 @@ void MediaPlayerPrivate::updateStates() if (state == GST_STATE_PLAYING) { m_readyState = MediaPlayer::HaveEnoughData; m_paused = false; + if (!m_mediaDuration) { + float newDuration = duration(); + if (!isinf(newDuration)) + m_mediaDuration = newDuration; + } } else m_paused = true; @@ -595,10 +733,6 @@ void MediaPlayerPrivate::updateStates() } m_networkState = MediaPlayer::Loaded; - - g_object_get(m_playBin, "source", &m_source, NULL); - if (!m_source) - LOG_VERBOSE(Media, "m_source is 0"); break; case GST_STATE_CHANGE_ASYNC: LOG_VERBOSE(Media, "Async: State: %s, pending: %s", @@ -647,6 +781,105 @@ void MediaPlayerPrivate::updateStates() } } +void MediaPlayerPrivate::mediaLocationChanged(GstMessage* message) +{ + if (m_mediaLocations) + gst_structure_free(m_mediaLocations); + + if (message->structure) { + // This structure can contain: + // - both a new-location string and embedded locations structure + // - or only a new-location string. + m_mediaLocations = gst_structure_copy(message->structure); + const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); + + if (locations) + m_mediaLocationCurrentIndex = gst_value_list_get_size(locations) -1; + + loadNextLocation(); + } +} + +bool MediaPlayerPrivate::loadNextLocation() +{ + if (!m_mediaLocations) + return false; + + const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); + const gchar* newLocation = 0; + + if (!locations) { + // Fallback on new-location string. + newLocation = gst_structure_get_string(m_mediaLocations, "new-location"); + if (!newLocation) + return false; + } + + if (!newLocation) { + if (m_mediaLocationCurrentIndex < 0) { + m_mediaLocations = 0; + return false; + } + + const GValue* location = gst_value_list_get_value(locations, + m_mediaLocationCurrentIndex); + const GstStructure* structure = gst_value_get_structure(location); + + if (!structure) { + m_mediaLocationCurrentIndex--; + return false; + } + + newLocation = gst_structure_get_string(structure, "new-location"); + } + + if (newLocation) { + // Found a candidate. new-location is not always an absolute url + // though. We need to take the base of the current url and + // append the value of new-location to it. + + gchar* currentLocation = 0; + g_object_get(m_playBin, "uri", ¤tLocation, NULL); + + KURL currentUrl(KURL(), currentLocation); + g_free(currentLocation); + + KURL newUrl; + + if (gst_uri_is_valid(newLocation)) + newUrl = KURL(KURL(), newLocation); + else + newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation); + + RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl); + if (securityOrigin->canRequest(newUrl)) { + LOG_VERBOSE(Media, "New media url: %s", newUrl.string().utf8().data()); + + // Reset player states. + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + m_readyState = MediaPlayer::HaveNothing; + m_player->readyStateChanged(); + + // Reset pipeline state. + m_resetPipeline = true; + gst_element_set_state(m_playBin, GST_STATE_READY); + + GstState state; + gst_element_get_state(m_playBin, &state, 0, 0); + if (state <= GST_STATE_READY) { + // Set the new uri and start playing. + g_object_set(m_playBin, "uri", newUrl.string().utf8().data(), NULL); + gst_element_set_state(m_playBin, GST_STATE_PLAYING); + return true; + } + } + } + m_mediaLocationCurrentIndex--; + return false; + +} + void MediaPlayerPrivate::loadStateChanged() { updateStates(); @@ -663,14 +896,30 @@ void MediaPlayerPrivate::timeChanged() m_player->timeChanged(); } -void MediaPlayerPrivate::volumeChanged() +void MediaPlayerPrivate::didEnd() { - m_player->volumeChanged(); + // EOS was reached but in case of reverse playback the position is + // not always 0. So to not confuse the HTMLMediaElement we + // synchronize position and duration values. + float now = currentTime(); + if (now > 0) + m_mediaDuration = now; + gst_element_set_state(m_playBin, GST_STATE_PAUSED); + + timeChanged(); } -void MediaPlayerPrivate::didEnd() +void MediaPlayerPrivate::durationChanged() { - timeChanged(); + // Reset cached media duration + m_mediaDuration = 0; + + // And re-cache it if possible. + float newDuration = duration(); + if (!isinf(newDuration)) + m_mediaDuration = newDuration; + + m_player->durationChanged(); } void MediaPlayerPrivate::loadingFailed(MediaPlayer::NetworkState error) @@ -711,7 +960,7 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) return; int width = 0, height = 0; - GstCaps *caps = gst_buffer_get_caps(m_buffer); + GstCaps* caps = gst_buffer_get_caps(m_buffer); GstVideoFormat format; if (!gst_video_format_parse_caps(caps, &format, &width, &height)) { @@ -751,7 +1000,7 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) static HashSet<String> mimeTypeCache() { - do_gst_init(); + doGstInit(); static HashSet<String> cache; static bool typeListInitialized = false; @@ -814,11 +1063,11 @@ static HashSet<String> mimeTypeCache() if (G_VALUE_TYPE(layer) == GST_TYPE_INT_RANGE) { gint minLayer = gst_value_get_int_range_min(layer); gint maxLayer = gst_value_get_int_range_max(layer); - if (minLayer <= 1 <= maxLayer) + if (minLayer <= 1 && 1 <= maxLayer) cache.add(String("audio/mp1")); - if (minLayer <= 2 <= maxLayer) + if (minLayer <= 2 && 2 <= maxLayer) cache.add(String("audio/mp2")); - if (minLayer <= 3 <= maxLayer) + if (minLayer <= 3 && 3 <= maxLayer) cache.add(String("audio/mp3")); } } @@ -887,13 +1136,29 @@ void MediaPlayerPrivate::createGSTPlayBin(String url) g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this); gst_object_unref(bus); - g_object_set(G_OBJECT(m_playBin), "uri", url.utf8().data(), - "volume", static_cast<double>(m_player->volume()), NULL); + g_object_set(m_playBin, "uri", url.utf8().data(), NULL); + + g_signal_connect(m_playBin, "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this); + g_signal_connect(m_playBin, "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this); m_videoSink = webkit_video_sink_new(); g_object_ref_sink(m_videoSink); - g_object_set(m_playBin, "video-sink", m_videoSink, NULL); + + WTFLogChannel* channel = getChannelFromName("Media"); + if (channel->state == WTFLogChannelOn) { + m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink"); + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) { + g_object_set(m_fpsSink, "video-sink", m_videoSink, NULL); + g_object_ref_sink(m_fpsSink); + g_object_set(m_playBin, "video-sink", m_fpsSink, NULL); + } else { + m_fpsSink = 0; + g_object_set(m_playBin, "video-sink", m_videoSink, NULL); + LOG(Media, "Can't display FPS statistics, you need gst-plugins-bad >= 0.10.18"); + } + } else + g_object_set(m_playBin, "video-sink", m_videoSink, NULL); g_signal_connect(m_videoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this); } diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h index 6ab8edb..ec55b29 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h @@ -39,16 +39,19 @@ typedef struct _GstBus GstBus; namespace WebCore { - class GraphicsContext; - class IntSize; - class IntRect; - class String; +class GraphicsContext; +class IntSize; +class IntRect; +class String; - gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data); +gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data); +void mediaPlayerPrivateVolumeChangedCallback(GObject* element, GParamSpec* pspec, gpointer data); +void mediaPlayerPrivateSourceChangedCallback(GObject* element, GParamSpec* pspec, gpointer data); - class MediaPlayerPrivate : public MediaPlayerPrivateInterface { +class MediaPlayerPrivate : public MediaPlayerPrivateInterface { friend gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data); - friend void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivate* playerPrivate); + friend void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer* buffer, MediaPlayerPrivate* playerPrivate); + friend void mediaPlayerPrivateSourceChangedCallback(GObject* element, GParamSpec* pspec, gpointer data); public: static void registerMediaEngine(MediaEngineRegistrar); @@ -60,6 +63,7 @@ namespace WebCore { void load(const String &url); void cancelLoad(); + bool loadNextLocation(); void play(); void pause(); @@ -70,12 +74,10 @@ namespace WebCore { float duration() const; float currentTime() const; void seek(float); - void setEndTime(float); void setRate(float); void setVolume(float); - - int dataRate() const; + void volumeChanged(); MediaPlayer::NetworkState networkState() const; MediaPlayer::ReadyState readyState() const; @@ -83,17 +85,17 @@ namespace WebCore { PassRefPtr<TimeRanges> buffered() const; float maxTimeSeekable() const; unsigned bytesLoaded() const; - bool totalBytesKnown() const; unsigned totalBytes() const; void setVisible(bool); void setSize(const IntSize&); + void mediaLocationChanged(GstMessage*); void loadStateChanged(); void sizeChanged(); void timeChanged(); - void volumeChanged(); void didEnd(); + void durationChanged(); void loadingFailed(MediaPlayer::NetworkState); void repaint(); @@ -103,6 +105,8 @@ namespace WebCore { bool supportsFullscreen() const; + bool pipelineReset() const { return m_resetPipeline; } + private: MediaPlayerPrivate(MediaPlayer*); static MediaPlayerPrivateInterface* create(MediaPlayer* player); @@ -118,11 +122,13 @@ namespace WebCore { void startEndPointTimerIfNeeded(); void createGSTPlayBin(String url); + bool changePipelineState(GstState state); private: MediaPlayer* m_player; GstElement* m_playBin; GstElement* m_videoSink; + GstElement* m_fpsSink; GstElement* m_source; GstClockTime m_seekTime; bool m_changingRate; @@ -134,10 +140,15 @@ namespace WebCore { mutable bool m_isStreaming; IntSize m_size; GstBuffer* m_buffer; - + GstStructure* m_mediaLocations; + gint m_mediaLocationCurrentIndex; + bool m_resetPipeline; bool m_paused; bool m_seeking; + float m_playbackRate; bool m_errorOccured; + guint m_volumeIdleId; + gfloat m_mediaDuration; }; } diff --git a/WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp b/WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp index 4728d56..6038c6a 100644 --- a/WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp +++ b/WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp @@ -204,12 +204,16 @@ void GraphicsContext::clip(const FloatRect& rect) m_data->m_view->ConstrainClippingRegion(®ion); } -void GraphicsContext::drawFocusRing(const Color& color) +void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) +{ + // FIXME: implement +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color) { if (paintingDisabled()) return; - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); // FIXME: maybe we should implement this with BShape? diff --git a/WebCore/platform/graphics/mac/Canvas3DLayer.mm b/WebCore/platform/graphics/mac/Canvas3DLayer.mm index 94819d4..59a7384 100644 --- a/WebCore/platform/graphics/mac/Canvas3DLayer.mm +++ b/WebCore/platform/graphics/mac/Canvas3DLayer.mm @@ -33,8 +33,9 @@ #import "GraphicsLayer.h" #import <QuartzCore/QuartzCore.h> #import <OpenGL/OpenGL.h> +#import <wtf/FastMalloc.h> #import <wtf/RetainPtr.h> -#include <wtf/FastMalloc.h> +#import <wtf/UnusedParam.h> using namespace WebCore; @@ -140,6 +141,13 @@ static void freeData(void *, const void *data, size_t /* size */) return image; } +- (void)display +{ + [super display]; + if (m_layerOwner) + m_layerOwner->didDisplay(self); +} + @end @implementation Canvas3DLayer(WebLayerAdditions) diff --git a/WebCore/platform/graphics/mac/ComplexTextController.cpp b/WebCore/platform/graphics/mac/ComplexTextController.cpp index 265b2c3..7d12b61 100644 --- a/WebCore/platform/graphics/mac/ComplexTextController.cpp +++ b/WebCore/platform/graphics/mac/ComplexTextController.cpp @@ -29,6 +29,13 @@ #include "Font.h" #include "TextBreakIterator.h" +#include <wtf/StdLibExtras.h> + +#if defined(BUILDING_ON_LEOPARD) +// Undefined when compiling agains the 10.5 SDK. +#define kCTVersionNumber10_6 0x00030000 +#endif + using namespace std; namespace WebCore { @@ -106,7 +113,7 @@ int ComplexTextController::offsetForPosition(int h, bool includePartialGlyphs) else hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.stringLength()); - // FIXME: Instead of dividing the glyph's advance equially between the characters, this + // FIXME: Instead of dividing the glyph's advance equally between the characters, this // could use the glyph's "ligature carets". However, there is no Core Text API to get the // ligature carets. CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance); @@ -263,6 +270,62 @@ void ComplexTextController::collectComplexTextRuns() collectComplexTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); } +#if USE(CORE_TEXT) && USE(ATSUI) +static inline bool shouldUseATSUIAPI() +{ + enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText }; + DEFINE_STATIC_LOCAL(TypeRenderingAPIToUse, apiToUse, (UnInitialized)); + + if (UNLIKELY(apiToUse == UnInitialized)) { + if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6) + apiToUse = UseCoreText; + else + apiToUse = UseATSUI; + } + + return apiToUse == UseATSUI; +} +#endif + +CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const +{ +#if USE(CORE_TEXT) && USE(ATSUI) + return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i]; +#elif USE(ATSUI) + return m_atsuiIndices[i]; +#elif USE(CORE_TEXT) + return m_coreTextIndices[i]; +#endif +} + +void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) +{ +#if USE(CORE_TEXT) && USE(ATSUI) + if (shouldUseATSUIAPI()) + return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData); + return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData); +#elif USE(ATSUI) + return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData); +#elif USE(CORE_TEXT) + return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData); +#endif +} + +ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) + : m_fontData(fontData) + , m_characters(characters) + , m_stringLocation(stringLocation) + , m_stringLength(stringLength) +{ +#if USE(CORE_TEXT) && USE(ATSUI) + shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr); +#elif USE(ATSUI) + createTextRunFromFontDataATSUI(ltr); +#elif USE(CORE_TEXT) + createTextRunFromFontDataCoreText(ltr); +#endif +} + void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) { if (static_cast<int>(offset) > m_end) diff --git a/WebCore/platform/graphics/mac/ComplexTextController.h b/WebCore/platform/graphics/mac/ComplexTextController.h index 7a915e2..3fec18a 100644 --- a/WebCore/platform/graphics/mac/ComplexTextController.h +++ b/WebCore/platform/graphics/mac/ComplexTextController.h @@ -39,6 +39,11 @@ class Font; class SimpleFontData; class TextRun; +// ComplexTextController is responsible for rendering and measuring glyphs for +// complex scripts on OS X. +// The underlying API can be selected at compile time based on USE(ATSUI) and +// USE(CORE_TEXT). If both are defined then the Core Text APIs are used for +// OS Versions >= 10.6, ATSUI is used otherwise. class ComplexTextController { public: ComplexTextController(const Font*, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const SimpleFontData*>* fallbackFonts = 0); @@ -65,7 +70,8 @@ private: { return adoptRef(new ComplexTextRun(ctRun, fontData, characters, stringLocation, stringLength)); } -#elif USE(ATSUI) +#endif +#if USE(ATSUI) static PassRefPtr<ComplexTextRun> create(ATSUTextLayout atsuTextLayout, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr, bool directionalOverride) { return adoptRef(new ComplexTextRun(atsuTextLayout, fontData, characters, stringLocation, stringLength, ltr, directionalOverride)); @@ -81,15 +87,18 @@ private: const UChar* characters() const { return m_characters; } unsigned stringLocation() const { return m_stringLocation; } size_t stringLength() const { return m_stringLength; } - CFIndex indexAt(size_t i) const { return m_indices[i]; } + ALWAYS_INLINE CFIndex indexAt(size_t i) const; const CGGlyph* glyphs() const { return m_glyphs; } const CGSize* advances() const { return m_advances; } private: #if USE(CORE_TEXT) ComplexTextRun(CTRunRef, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength); -#elif USE(ATSUI) + void createTextRunFromFontDataCoreText(bool ltr); +#endif +#if USE(ATSUI) ComplexTextRun(ATSUTextLayout, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr, bool directionalOverride); + void createTextRunFromFontDataATSUI(bool ltr); #endif ComplexTextRun(const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr); @@ -101,7 +110,7 @@ private: #endif #if USE(CORE_TEXT) - RetainPtr<CTRunRef> m_CTRun; + RetainPtr<CTRunRef> m_coreTextRun; #endif unsigned m_glyphCount; const SimpleFontData* m_fontData; @@ -109,10 +118,11 @@ private: unsigned m_stringLocation; size_t m_stringLength; #if USE(CORE_TEXT) - RetainPtr<CFMutableDataRef> m_indicesData; - const CFIndex* m_indices; -#elif USE(ATSUI) - Vector<CFIndex, 64> m_indices; + RetainPtr<CFMutableDataRef> m_coreTextIndicesData; + const CFIndex* m_coreTextIndices; +#endif +#if USE(ATSUI) + Vector<CFIndex, 64> m_atsuiIndices; #endif Vector<CGGlyph, 64> m_glyphsVector; const CGGlyph* m_glyphs; @@ -125,7 +135,12 @@ private: }; void collectComplexTextRuns(); + + // collectComplexTextRunsForCharacters() is a stub function that calls through to the ATSUI or Core Text variants based + // on the API in use. void collectComplexTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); + void collectComplexTextRunsForCharactersATSUI(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); + void collectComplexTextRunsForCharactersCoreText(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); void adjustGlyphsAndAdvances(); const Font& m_font; diff --git a/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp b/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp index 78c588f..48aa174 100644 --- a/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp +++ b/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp @@ -45,7 +45,7 @@ OSStatus ComplexTextController::ComplexTextRun::overrideLayoutOperation(ATSULayo ComplexTextRun* complexTextRun = reinterpret_cast<ComplexTextRun*>(refCon); OSStatus status; ItemCount count; - ATSLayoutRecord *layoutRecords; + ATSLayoutRecord* layoutRecords; status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(atsuLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, reinterpret_cast<void**>(&layoutRecords), &count); if (status != noErr) { @@ -66,7 +66,7 @@ OSStatus ComplexTextController::ComplexTextRun::overrideLayoutOperation(ATSULayo complexTextRun->m_glyphCount = count; complexTextRun->m_glyphsVector.reserveCapacity(count); complexTextRun->m_advancesVector.reserveCapacity(count); - complexTextRun->m_indices.reserveCapacity(count); + complexTextRun->m_atsuiIndices.reserveCapacity(count); bool atBeginning = true; CGFloat lastX = 0; @@ -77,7 +77,7 @@ OSStatus ComplexTextController::ComplexTextRun::overrideLayoutOperation(ATSULayo continue; } complexTextRun->m_glyphsVector.uncheckedAppend(layoutRecords[j].glyphID); - complexTextRun->m_indices.uncheckedAppend(layoutRecords[j].originalOffset / 2 + indexOffset); + complexTextRun->m_atsuiIndices.uncheckedAppend(layoutRecords[j].originalOffset / 2 + indexOffset); CGFloat x = FixedToFloat(layoutRecords[j].realPos); if (!atBeginning) complexTextRun->m_advancesVector.uncheckedAppend(CGSizeMake(x - lastX, 0)); @@ -219,33 +219,29 @@ ComplexTextController::ComplexTextRun::ComplexTextRun(ATSUTextLayout atsuTextLay status = ATSUDisposeTextLayout(atsuTextLayout); } -ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) - : m_fontData(fontData) - , m_characters(characters) - , m_stringLocation(stringLocation) - , m_stringLength(stringLength) +void ComplexTextController::ComplexTextRun::createTextRunFromFontDataATSUI(bool ltr) { - m_indices.reserveCapacity(stringLength); + m_atsuiIndices.reserveCapacity(m_stringLength); unsigned r = 0; - while (r < stringLength) { - m_indices.uncheckedAppend(r); - if (U_IS_SURROGATE(characters[r])) { - ASSERT(r + 1 < stringLength); - ASSERT(U_IS_SURROGATE_LEAD(characters[r])); - ASSERT(U_IS_TRAIL(characters[r + 1])); + while (r < m_stringLength) { + m_atsuiIndices.uncheckedAppend(r); + if (U_IS_SURROGATE(m_characters[r])) { + ASSERT(r + 1 < m_stringLength); + ASSERT(U_IS_SURROGATE_LEAD(m_characters[r])); + ASSERT(U_IS_TRAIL(m_characters[r + 1])); r += 2; } else r++; } - m_glyphCount = m_indices.size(); + m_glyphCount = m_atsuiIndices.size(); if (!ltr) { for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) - std::swap(m_indices[r], m_indices[end]); + std::swap(m_atsuiIndices[r], m_atsuiIndices[end]); } m_glyphsVector.fill(0, m_glyphCount); m_glyphs = m_glyphsVector.data(); - m_advancesVector.fill(CGSizeMake(fontData->widthForGlyph(0), 0), m_glyphCount); + m_advancesVector.fill(CGSizeMake(m_fontData->widthForGlyph(0), 0), m_glyphCount); m_advances = m_advancesVector.data(); } @@ -261,33 +257,37 @@ static bool fontHasMirroringInfo(ATSUFontID fontID) return false; } -static void disableLigatures(const SimpleFontData* fontData, TextRenderingMode textMode) +static void disableLigatures(const SimpleFontData* fontData, ATSUStyle atsuStyle, TypesettingFeatures typesettingFeatures) { // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example. // See bugzilla 5166. - if (textMode == OptimizeLegibility || textMode == GeometricPrecision || fontData->platformData().allowsLigatures()) + if ((typesettingFeatures & Ligatures) || fontData->platformData().allowsLigatures()) return; ATSUFontFeatureType featureTypes[] = { kLigaturesType }; ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector }; - OSStatus status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors); + OSStatus status = ATSUSetFontFeatures(atsuStyle, 1, featureTypes, featureSelectors); if (status != noErr) LOG_ERROR("ATSUSetFontFeatures failed (%d) -- ligatures remain enabled", static_cast<int>(status)); } -static void initializeATSUStyle(const SimpleFontData* fontData, TextRenderingMode textMode) +static ATSUStyle initializeATSUStyle(const SimpleFontData* fontData, TypesettingFeatures typesettingFeatures) { - if (fontData->m_ATSUStyleInitialized) - return; + unsigned key = typesettingFeatures + 1; + pair<HashMap<unsigned, ATSUStyle>::iterator, bool> addResult = fontData->m_ATSUStyleMap.add(key, 0); + ATSUStyle& atsuStyle = addResult.first->second; + if (!addResult.second) + return atsuStyle; ATSUFontID fontID = fontData->platformData().m_atsuFontID; if (!fontID) { LOG_ERROR("unable to get ATSUFontID for %p", fontData->platformData().font()); - return; + fontData->m_ATSUStyleMap.remove(addResult.first); + return 0; } - OSStatus status = ATSUCreateStyle(&fontData->m_ATSUStyle); + OSStatus status = ATSUCreateStyle(&atsuStyle); if (status != noErr) LOG_ERROR("ATSUCreateStyle failed (%d)", static_cast<int>(status)); @@ -299,19 +299,18 @@ static void initializeATSUStyle(const SimpleFontData* fontData, TextRenderingMod ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag }; ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &verticalFlip, &kerningInhibitFactor }; - bool allowKerning = textMode == OptimizeLegibility || textMode == GeometricPrecision; - status = ATSUSetAttributes(fontData->m_ATSUStyle, allowKerning ? 3 : 4, styleTags, styleSizes, styleValues); + bool allowKerning = typesettingFeatures & Kerning; + status = ATSUSetAttributes(atsuStyle, allowKerning ? 3 : 4, styleTags, styleSizes, styleValues); if (status != noErr) LOG_ERROR("ATSUSetAttributes failed (%d)", static_cast<int>(status)); fontData->m_ATSUMirrors = fontHasMirroringInfo(fontID); - disableLigatures(fontData, textMode); - - fontData->m_ATSUStyleInitialized = true; + disableLigatures(fontData, atsuStyle, typesettingFeatures); + return atsuStyle; } -void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) +void ComplexTextController::collectComplexTextRunsForCharactersATSUI(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) { if (!fontData) { // Create a run of missing glyphs from the primary font. @@ -322,13 +321,13 @@ void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, if (m_fallbackFonts && fontData != m_font.primaryFont()) m_fallbackFonts->add(fontData); - initializeATSUStyle(fontData, m_font.fontDescription().textRenderingMode()); + ATSUStyle atsuStyle = initializeATSUStyle(fontData, m_font.typesettingFeatures()); OSStatus status; ATSUTextLayout atsuTextLayout; UniCharCount runLength = length; - status = ATSUCreateTextLayoutWithTextPtr(cp, 0, length, length, 1, &runLength, &fontData->m_ATSUStyle, &atsuTextLayout); + status = ATSUCreateTextLayoutWithTextPtr(cp, 0, length, length, 1, &runLength, &atsuStyle, &atsuTextLayout); if (status != noErr) { LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed with error %d", static_cast<int>(status)); return; diff --git a/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp b/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp index c9daf84..dd5e96a 100644 --- a/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp +++ b/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp @@ -29,35 +29,44 @@ #include "Font.h" +#if defined(BUILDING_ON_LEOPARD) +// The following symbols are SPI in 10.5. +extern "C" { +void CTRunGetAdvances(CTRunRef run, CFRange range, CGSize buffer[]); +const CGSize* CTRunGetAdvancesPtr(CTRunRef run); +extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; +} +#endif + namespace WebCore { ComplexTextController::ComplexTextRun::ComplexTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) - : m_CTRun(ctRun) + : m_coreTextRun(ctRun) , m_fontData(fontData) , m_characters(characters) , m_stringLocation(stringLocation) , m_stringLength(stringLength) { - m_glyphCount = CTRunGetGlyphCount(m_CTRun.get()); - m_indices = CTRunGetStringIndicesPtr(m_CTRun.get()); - if (!m_indices) { - m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); - CFDataIncreaseLength(m_indicesData.get(), m_glyphCount * sizeof(CFIndex)); - m_indices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_indicesData.get())); - CTRunGetStringIndices(m_CTRun.get(), CFRangeMake(0, 0), const_cast<CFIndex*>(m_indices)); + m_glyphCount = CTRunGetGlyphCount(m_coreTextRun.get()); + m_coreTextIndices = CTRunGetStringIndicesPtr(m_coreTextRun.get()); + if (!m_coreTextIndices) { + m_coreTextIndicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); + CFDataIncreaseLength(m_coreTextIndicesData.get(), m_glyphCount * sizeof(CFIndex)); + m_coreTextIndices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_coreTextIndicesData.get())); + CTRunGetStringIndices(m_coreTextRun.get(), CFRangeMake(0, 0), const_cast<CFIndex*>(m_coreTextIndices)); } - m_glyphs = CTRunGetGlyphsPtr(m_CTRun.get()); + m_glyphs = CTRunGetGlyphsPtr(m_coreTextRun.get()); if (!m_glyphs) { m_glyphsVector.grow(m_glyphCount); - CTRunGetGlyphs(m_CTRun.get(), CFRangeMake(0, 0), m_glyphsVector.data()); + CTRunGetGlyphs(m_coreTextRun.get(), CFRangeMake(0, 0), m_glyphsVector.data()); m_glyphs = m_glyphsVector.data(); } - m_advances = CTRunGetAdvancesPtr(m_CTRun.get()); + m_advances = CTRunGetAdvancesPtr(m_coreTextRun.get()); if (!m_advances) { m_advancesVector.grow(m_glyphCount); - CTRunGetAdvances(m_CTRun.get(), CFRangeMake(0, 0), m_advancesVector.data()); + CTRunGetAdvances(m_coreTextRun.get(), CFRangeMake(0, 0), m_advancesVector.data()); m_advances = m_advancesVector.data(); } @@ -65,20 +74,16 @@ ComplexTextController::ComplexTextRun::ComplexTextRun(CTRunRef ctRun, const Simp // Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on // glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path. -ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) - : m_fontData(fontData) - , m_characters(characters) - , m_stringLocation(stringLocation) - , m_stringLength(stringLength) +void ComplexTextController::ComplexTextRun::createTextRunFromFontDataCoreText(bool ltr) { Vector<CFIndex, 16> indices; unsigned r = 0; - while (r < stringLength) { + while (r < m_stringLength) { indices.append(r); - if (U_IS_SURROGATE(characters[r])) { - ASSERT(r + 1 < stringLength); - ASSERT(U_IS_SURROGATE_LEAD(characters[r])); - ASSERT(U_IS_TRAIL(characters[r + 1])); + if (U_IS_SURROGATE(m_characters[r])) { + ASSERT(r + 1 < m_stringLength); + ASSERT(U_IS_SURROGATE_LEAD(m_characters[r])); + ASSERT(U_IS_TRAIL(m_characters[r + 1])); r += 2; } else r++; @@ -88,9 +93,9 @@ ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* font for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) std::swap(indices[r], indices[end]); } - m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); - CFDataAppendBytes(m_indicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex)); - m_indices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_indicesData.get())); + m_coreTextIndicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); + CFDataAppendBytes(m_coreTextIndicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex)); + m_coreTextIndices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_coreTextIndicesData.get())); // Synthesize a run of missing glyphs. m_glyphsVector.fill(0, m_glyphCount); @@ -99,7 +104,7 @@ ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* font m_advances = m_advancesVector.data(); } -void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) +void ComplexTextController::collectComplexTextRunsForCharactersCoreText(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) { if (!fontData) { // Create a run of missing glyphs from the primary font. @@ -112,7 +117,7 @@ void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(NULL, cp, length, kCFAllocatorNull)); - RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes(m_font.fontDescription().textRenderingMode()))); + RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes(m_font.typesettingFeatures()))); RetainPtr<CTTypesetterRef> typesetter; diff --git a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp index 41f63a9..5e5e1f4 100644 --- a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp +++ b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp @@ -29,28 +29,24 @@ #include "GraphicsContext3D.h" -#include "CachedImage.h" +#include "CanvasObject.h" +#include "CString.h" +#include "ImageBuffer.h" +#include "NotImplemented.h" #include "WebGLActiveInfo.h" #include "WebGLArray.h" #include "WebGLBuffer.h" #include "WebGLFramebuffer.h" #include "WebGLFloatArray.h" #include "WebGLIntArray.h" -#include "CanvasObject.h" #include "WebGLProgram.h" #include "WebGLRenderbuffer.h" #include "WebGLShader.h" #include "WebGLTexture.h" #include "WebGLUnsignedByteArray.h" -#include "CString.h" -#include "HTMLCanvasElement.h" -#include "HTMLImageElement.h" -#include "ImageBuffer.h" -#include "NotImplemented.h" -#include "WebKitCSSMatrix.h" - #include <CoreGraphics/CGBitmapContext.h> #include <OpenGL/CGLRenderers.h> +#include <wtf/UnusedParam.h> namespace WebCore { @@ -79,18 +75,29 @@ static void setPixelFormat(Vector<CGLPixelFormatAttribute>& attribs, int colorBi attribs.append(static_cast<CGLPixelFormatAttribute>(0)); } -PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create() +PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attrs) { - OwnPtr<GraphicsContext3D> context(new GraphicsContext3D()); + OwnPtr<GraphicsContext3D> context(new GraphicsContext3D(attrs)); return context->m_contextObj ? context.release() : 0; } -GraphicsContext3D::GraphicsContext3D() - : m_contextObj(0) +GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attrs) + : m_attrs(attrs) + , m_contextObj(0) , m_texture(0) , m_fbo(0) , m_depthBuffer(0) { + // FIXME: we need to take into account the user's requested + // context creation attributes, in particular stencil and + // antialias, and determine which could and could not be honored + // based on the capabilities of the OpenGL implementation. + m_attrs.alpha = true; + m_attrs.depth = true; + m_attrs.stencil = false; + m_attrs.antialias = false; + m_attrs.premultipliedAlpha = true; + Vector<CGLPixelFormatAttribute> attribs; CGLPixelFormatObj pixelFormatObj = 0; GLint numPixelFormats = 0; @@ -262,7 +269,7 @@ void GraphicsContext3D::bindBuffer(unsigned long target, WebGLBuffer* buffer) void GraphicsContext3D::bindFramebuffer(unsigned long target, WebGLFramebuffer* buffer) { ensureContext(m_contextObj); - ::glBindFramebufferEXT(target, buffer ? (GLuint) buffer->object() : m_fbo); + ::glBindFramebufferEXT(target, (buffer && buffer->object()) ? (GLuint) buffer->object() : m_fbo); } void GraphicsContext3D::bindRenderbuffer(unsigned long target, WebGLRenderbuffer* renderbuffer) @@ -544,6 +551,11 @@ int GraphicsContext3D::getAttribLocation(WebGLProgram* program, const String& na return ::glGetAttribLocation((GLuint) program->object(), name.utf8().data()); } +GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes() +{ + return m_attrs; +} + unsigned long GraphicsContext3D::getError() { if (m_syntheticErrors.size() > 0) { @@ -1115,55 +1127,38 @@ long GraphicsContext3D::getVertexAttribOffset(unsigned long index, unsigned long return reinterpret_cast<long>(pointer); } -// Assumes the texture you want to go into is bound -static void imageToTexture(Image* image, unsigned target, unsigned level) +// Returned pointer must be freed by fastFree() +static bool imageToTexture(Image* image, GLubyte*& buffer, size_t& width, size_t& height) { if (!image) - return; + return false; CGImageRef textureImage = image->getCGImageRef(); if (!textureImage) - return; + return false; - size_t textureWidth = CGImageGetWidth(textureImage); - size_t textureHeight = CGImageGetHeight(textureImage); + width = CGImageGetWidth(textureImage); + height = CGImageGetHeight(textureImage); - GLubyte* textureData = (GLubyte*) fastMalloc(textureWidth * textureHeight * 4); - if (!textureData) - return; + buffer = (GLubyte*) fastMalloc(width * height * 4); + if (!buffer) + return false; - CGContextRef textureContext = CGBitmapContextCreate(textureData, textureWidth, textureHeight, 8, textureWidth * 4, + CGContextRef textureContext = CGBitmapContextCreate(buffer, width, height, 8, width * 4, CGImageGetColorSpace(textureImage), kCGImageAlphaPremultipliedLast); CGContextSetBlendMode(textureContext, kCGBlendModeCopy); - CGContextDrawImage(textureContext, CGRectMake(0, 0, (CGFloat)textureWidth, (CGFloat)textureHeight), textureImage); + CGContextDrawImage(textureContext, CGRectMake(0, 0, (CGFloat)width, (CGFloat)height), textureImage); CGContextRelease(textureContext); - - ::glTexImage2D(target, level, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData); - fastFree(textureData); + return true; } -int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, WebGLArray* pixels) +int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, void* pixels) { // FIXME: Need to do bounds checking on the buffer here. - ::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels->baseAddress()); + ::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); return 0; } -int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, ImageData* pixels) -{ - // FIXME: need to implement this form - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(internalformat); - UNUSED_PARAM(width); - UNUSED_PARAM(height); - UNUSED_PARAM(border); - UNUSED_PARAM(format); - UNUSED_PARAM(type); - UNUSED_PARAM(pixels); - return -1; -} - int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image, bool flipY, bool premultiplyAlpha) { // FIXME: need to support flipY and premultiplyAlpha @@ -1172,85 +1167,43 @@ int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image, ASSERT(image); ensureContext(m_contextObj); - imageToTexture(image, target, level); + GLubyte* buffer; + size_t width; + size_t height; + if (!imageToTexture(image, buffer, width, height)) + return -1; + + ::glTexImage2D(target, level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + fastFree(buffer); return 0; } - -int GraphicsContext3D::texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, bool flipY, bool premultiplyAlpha) -{ - // FIXME: need to implement this form - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(video); - - // FIXME: need to support flipY and premultiplyAlpha - UNUSED_PARAM(flipY); - UNUSED_PARAM(premultiplyAlpha); - return -1; -} - -int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, unsigned format, unsigned type, WebGLArray* pixels) -{ - // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(xoff); - UNUSED_PARAM(yoff); - UNUSED_PARAM(width); - UNUSED_PARAM(height); - UNUSED_PARAM(format); - UNUSED_PARAM(type); - UNUSED_PARAM(pixels); - return -1; -} - -int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, unsigned format, unsigned type, ImageData* pixels) -{ - // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(xoff); - UNUSED_PARAM(yoff); - UNUSED_PARAM(width); - UNUSED_PARAM(height); - UNUSED_PARAM(format); - UNUSED_PARAM(type); - UNUSED_PARAM(pixels); - return -1; -} - -int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, Image* image, bool flipY, bool premultiplyAlpha) + +int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, unsigned format, unsigned type, void* pixels) { // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(xoff); - UNUSED_PARAM(yoff); - UNUSED_PARAM(width); - UNUSED_PARAM(height); - UNUSED_PARAM(image); - - // FIXME: need to support flipY and premultiplyAlpha - UNUSED_PARAM(flipY); - UNUSED_PARAM(premultiplyAlpha); - return -1; + // FIXME: Need to do bounds checking on the buffer here. + ::glTexSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels); + return 0; } -int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, HTMLVideoElement* video, bool flipY, bool premultiplyAlpha) +int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, Image* image, bool flipY, bool premultiplyAlpha) { // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(xoff); - UNUSED_PARAM(yoff); - UNUSED_PARAM(width); - UNUSED_PARAM(height); - UNUSED_PARAM(video); - - // FIXME: need to support flipY and premultiplyAlpha + // FIXME: need to support flipY and premultiplyAlpha UNUSED_PARAM(flipY); UNUSED_PARAM(premultiplyAlpha); - return -1; + ASSERT(image); + + ensureContext(m_contextObj); + GLubyte* buffer; + size_t width; + size_t height; + if (!imageToTexture(image, buffer, width, height)) + return -1; + + ::glTexSubImage2D(target, level, xoff, yoff, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + fastFree(buffer); + return 0; } unsigned GraphicsContext3D::createBuffer() diff --git a/WebCore/platform/graphics/mac/GraphicsContextMac.mm b/WebCore/platform/graphics/mac/GraphicsContextMac.mm index 6c9b872..5f111f6 100644 --- a/WebCore/platform/graphics/mac/GraphicsContextMac.mm +++ b/WebCore/platform/graphics/mac/GraphicsContextMac.mm @@ -26,7 +26,7 @@ #import "config.h" #import "GraphicsContext.h" -#import "../cg/GraphicsContextPlatformPrivateCG.h" +#import "GraphicsContextPlatformPrivateCG.h" #import <AppKit/AppKit.h> #import <wtf/StdLibExtras.h> @@ -43,33 +43,55 @@ namespace WebCore { // calls in this file are all exception-safe, so we don't block // exceptions for those. -void GraphicsContext::drawFocusRing(const Color& color) +static void drawFocusRingToContext(CGContextRef context, RetainPtr<CGPathRef> focusRingPath, RetainPtr<CGColorRef> colorRef, int radius) +{ +#ifdef BUILDING_ON_TIGER + CGContextBeginTransparencyLayer(context, 0); +#endif + CGContextBeginPath(context); + CGContextAddPath(context, focusRingPath.get()); + wkDrawFocusRing(context, colorRef.get(), radius); +#ifdef BUILDING_ON_TIGER + CGContextEndTransparencyLayer(context); +#endif +} + +void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) +{ + if (paintingDisabled()) + return; + + int radius = (width - 1) / 2; + offset += radius; + RetainPtr<CGColorRef> colorRef; + if (color.isValid()) + colorRef.adoptCF(createCGColor(color)); + + RetainPtr<CGMutablePathRef> focusRingPath(AdoptCF, CGPathCreateMutable()); + unsigned pathCount = paths.size(); + for (unsigned i = 0; i < pathCount; i++) + CGPathAddPath(focusRingPath.get(), 0, paths[i].platformPath()); + + drawFocusRingToContext(platformContext(), focusRingPath, colorRef, radius); +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) { if (paintingDisabled()) return; - int radius = (focusRingWidth() - 1) / 2; - int offset = radius + focusRingOffset(); + int radius = (width - 1) / 2; + offset += radius; RetainPtr<CGColorRef> colorRef; if (color.isValid()) colorRef.adoptCF(createCGColor(color)); RetainPtr<CGMutablePathRef> focusRingPath(AdoptCF, CGPathCreateMutable()); - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); for (unsigned i = 0; i < rectCount; i++) CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rects[i], -offset, -offset)); - CGContextRef context = platformContext(); -#ifdef BUILDING_ON_TIGER - CGContextBeginTransparencyLayer(context, NULL); -#endif - CGContextBeginPath(context); - CGContextAddPath(context, focusRingPath.get()); - wkDrawFocusRing(context, colorRef.get(), radius); -#ifdef BUILDING_ON_TIGER - CGContextEndTransparencyLayer(context); -#endif + drawFocusRingToContext(platformContext(), focusRingPath, colorRef, radius); } #ifdef BUILDING_ON_TIGER // Post-Tiger's setCompositeOperation() is defined in GraphicsContextCG.cpp. diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h index 8024091..5362562 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.h +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.h @@ -30,16 +30,16 @@ #include "GraphicsLayer.h" #include "StringHash.h" +#include "WebLayer.h" +#include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/RetainPtr.h> @class CABasicAnimation; @class CAKeyframeAnimation; -@class CALayer; @class CAMediaTimingFunction; @class CAPropertyAnimation; @class WebAnimationDelegate; -@class WebLayer; namespace WebCore { @@ -64,6 +64,7 @@ public: virtual void removeFromParent(); virtual void setMaskLayer(GraphicsLayer*); + virtual void setReplicatedLayer(GraphicsLayer*); virtual void setPosition(const FloatPoint&); virtual void setAnchorPoint(const FloatPoint3D&); @@ -98,13 +99,13 @@ public: virtual void suspendAnimations(double time); virtual void resumeAnimations(); - virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& keyframesName, double beginTime); + virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& keyframesName, double timeOffset); virtual void removeAnimationsForProperty(AnimatedPropertyID); virtual void removeAnimationsForKeyframes(const String& keyframesName); - virtual void pauseAnimation(const String& keyframesName); + virtual void pauseAnimation(const String& keyframesName, double timeOffset); virtual void setContentsToImage(Image*); - virtual void setContentsToVideo(PlatformLayer*); + virtual void setContentsToMedia(PlatformLayer*); #if ENABLE(3D_CANVAS) virtual void setContentsToGraphicsContext3D(const GraphicsContext3D*); #endif @@ -116,8 +117,9 @@ public: virtual void setGeometryOrientation(CompositingCoordinatesOrientation); + virtual void didDisplay(PlatformLayer*); + void recursiveCommitChanges(); - void commitLayerChanges(); virtual void syncCompositingState(); @@ -127,13 +129,20 @@ protected: private: void updateOpacityOnLayer(); - WebLayer* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } - WebLayer* hostLayerForSublayers() const; - WebLayer* layerForSuperlayer() const; + CALayer* primaryLayer() const { return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); } + CALayer* hostLayerForSublayers() const; + CALayer* layerForSuperlayer() const; CALayer* animatedLayer(AnimatedPropertyID property) const; - 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); + typedef String CloneID; // Identifier for a given clone, based on original/replica branching down the tree. + static bool isReplicatedRootClone(const CloneID& cloneID) { return cloneID[0U] & 1; } + + typedef HashMap<CloneID, RetainPtr<CALayer> > LayerMap; + LayerMap* primaryLayerClones() const { return m_structuralLayer.get() ? m_structuralLayerClones.get() : m_layerClones.get(); } + LayerMap* animatedLayerClones(AnimatedPropertyID property) const; + + bool createAnimationFromKeyframes(const KeyframeValueList&, const Animation*, const String& keyframesName, double timeOffset); + bool createTransformAnimationsFromKeyframes(const KeyframeValueList&, const Animation*, const String& keyframesName, double timeOffset, const IntSize& boxSize); // Return autoreleased animation (use RetainPtr?) CABasicAnimation* createBasicAnimation(const Animation*, AnimatedPropertyID, bool additive); @@ -153,6 +162,9 @@ private: return m_runningKeyframeAnimations.find(keyframesName) != m_runningKeyframeAnimations.end(); } + void commitLayerChangesBeforeSublayers(); + void commitLayerChangesAfterSublayers(); + bool requiresTiledLayer(const FloatSize&) const; void swapFromOrToTiledLayer(bool useTiledLayer); @@ -161,8 +173,75 @@ private: void setupContentsLayer(CALayer*); CALayer* contentsLayer() const { return m_contentsLayer.get(); } + + virtual void setReplicatedByLayer(GraphicsLayer*); + + // Used to track the path down the tree for replica layers. + struct ReplicaState { + static const size_t maxReplicaDepth = 16; + enum ReplicaBranchType { ChildBranch = 0, ReplicaBranch = 1 }; + ReplicaState(ReplicaBranchType firstBranch) + : m_replicaDepth(0) + { + push(firstBranch); + } + + // Called as we walk down the tree to build replicas. + void push(ReplicaBranchType branchType) + { + m_replicaBranches.append(branchType); + if (branchType == ReplicaBranch) + ++m_replicaDepth; + } + + void setBranchType(ReplicaBranchType branchType) + { + ASSERT(!m_replicaBranches.isEmpty()); + + if (m_replicaBranches.last() != branchType) { + if (branchType == ReplicaBranch) + ++m_replicaDepth; + else + --m_replicaDepth; + } + + m_replicaBranches.last() = branchType; + } + + void pop() + { + if (m_replicaBranches.last() == ReplicaBranch) + --m_replicaDepth; + m_replicaBranches.removeLast(); + } + + size_t depth() const { return m_replicaBranches.size(); } + size_t replicaDepth() const { return m_replicaDepth; } + + CloneID cloneID() const; + + private: + Vector<ReplicaBranchType> m_replicaBranches; + size_t m_replicaDepth; + }; + CALayer *replicatedLayerRoot(ReplicaState&); + + enum CloneLevel { RootCloneLevel, IntermediateCloneLevel }; + CALayer *fetchCloneLayers(GraphicsLayer* replicaRoot, ReplicaState&, CloneLevel); + + CALayer *cloneLayer(CALayer *, CloneLevel); + CALayer *findOrMakeClone(CloneID, CALayer *, LayerMap*, CloneLevel); + + void ensureCloneLayers(CloneID index, CALayer *& primaryLayer, CALayer *& structuralLayer, CALayer *& contentsLayer, CloneLevel); + + bool hasCloneLayers() const { return m_layerClones; } + void removeCloneLayers(); + FloatPoint positionForCloneRootLayer() const; + + void propagateLayerChangeToReplicas(); // All these "update" methods will be called inside a BEGIN_BLOCK_OBJC_EXCEPTIONS/END_BLOCK_OBJC_EXCEPTIONS block. + void updateLayerNames(); void updateSublayerList(); void updateLayerPosition(); void updateLayerSize(); @@ -172,25 +251,38 @@ private: void updateMasksToBounds(); void updateContentsOpaque(); void updateBackfaceVisibility(); - void updateLayerPreserves3D(); + void updateStructuralLayer(); void updateLayerDrawsContent(); void updateLayerBackgroundColor(); void updateContentsImage(); - void updateContentsVideo(); + void updateContentsMediaLayer(); #if ENABLE(3D_CANVAS) void updateContentsGraphicsContext3D(); #endif void updateContentsRect(); void updateGeometryOrientation(); void updateMaskLayer(); + void updateReplicatedLayers(); void updateLayerAnimations(); + + enum StructuralLayerPurpose { + NoStructuralLayer = 0, + StructuralLayerForPreserves3D, + StructuralLayerForReplicaFlattening + }; + void ensureStructuralLayer(StructuralLayerPurpose); + StructuralLayerPurpose structuralLayerPurpose() const; - void setAnimationOnLayer(CAPropertyAnimation*, AnimatedPropertyID, int index, double beginTime); - bool removeAnimationFromLayer(AnimatedPropertyID, int index); - void pauseAnimationOnLayer(AnimatedPropertyID, int index); + void setAnimationOnLayer(CAPropertyAnimation*, AnimatedPropertyID, const String& keyframesName, int index, double timeOffset); + bool removeAnimationFromLayer(AnimatedPropertyID, const String& keyframesName, int index); + void pauseAnimationOnLayer(AnimatedPropertyID, const String& keyframesName, int index, double timeOffset); + enum MoveOrCopy { Move, Copy }; + void moveOrCopyAnimationsForProperty(MoveOrCopy, AnimatedPropertyID property, CALayer * fromLayer, CALayer * toLayer); + static void moveOrCopyAllAnimationsForProperty(MoveOrCopy operation, AnimatedPropertyID property, const String& keyframesName, CALayer * fromLayer, CALayer * toLayer); + enum LayerChange { NoChange = 0, NameChanged = 1 << 1, @@ -210,27 +302,34 @@ private: AnimationChanged = 1 << 15, DirtyRectsChanged = 1 << 16, ContentsImageChanged = 1 << 17, - ContentsVideoChanged = 1 << 18, + ContentsMediaLayerChanged = 1 << 18, #if ENABLE(3D_CANVAS) ContentsGraphicsContext3DChanged = 1 << 19, #endif ContentsRectChanged = 1 << 20, GeometryOrientationChanged = 1 << 21, - MaskLayerChanged = 1 << 22 + MaskLayerChanged = 1 << 22, + ReplicatedLayerChanged = 1 << 23 }; typedef unsigned LayerChangeFlags; void noteLayerPropertyChanged(LayerChangeFlags flags); + void noteSublayersChanged(); void repaintLayerDirtyRects(); - RetainPtr<WebLayer> m_layer; - RetainPtr<WebLayer> m_transformLayer; - RetainPtr<CALayer> m_contentsLayer; + RetainPtr<WebLayer> m_layer; // The main layer + RetainPtr<CALayer> m_structuralLayer; // A layer used for structural reasons, like preserves-3d or replica-flattening. Is the parent of m_layer. + RetainPtr<CALayer> m_contentsLayer; // A layer used for inner content, like image and video + + // References to clones of our layers, for replicated layers. + OwnPtr<LayerMap> m_layerClones; + OwnPtr<LayerMap> m_structuralLayerClones; + OwnPtr<LayerMap> m_contentsLayerClones; enum ContentsLayerPurpose { NoContentsLayer = 0, ContentsLayerForImage, - ContentsLayerForVideo + ContentsLayerForMedia #if ENABLE(3D_CANVAS) ,ContentsLayerForGraphicsLayer3D #endif @@ -244,19 +343,19 @@ private: RetainPtr<CGImageRef> m_pendingContentsImage; struct LayerAnimation { - LayerAnimation(CAPropertyAnimation* caAnim, const String& keyframesName, AnimatedPropertyID property, int index, double beginTime) + LayerAnimation(CAPropertyAnimation* caAnim, const String& keyframesName, AnimatedPropertyID property, int index, double timeOffset) : m_animation(caAnim) , m_keyframesName(keyframesName) , m_property(property) , m_index(index) - , m_beginTime(beginTime) + , m_timeOffset(timeOffset) { } RetainPtr<CAPropertyAnimation*> m_animation; String m_keyframesName; AnimatedPropertyID m_property; int m_index; - double m_beginTime; + double m_timeOffset; }; Vector<LayerAnimation> m_uncomittedAnimations; @@ -267,8 +366,16 @@ private: HashSet<AnimatedProperty> m_transitionPropertiesToRemove; - enum { Remove, Pause }; - typedef int AnimationProcessingAction; + enum Action { Remove, Pause }; + struct AnimationProcessingAction { + AnimationProcessingAction(Action action = Remove, double timeOffset = 0) + : action(action) + , timeOffset(timeOffset) + { + } + Action action; + double timeOffset; // only used for pause + }; typedef HashMap<String, AnimationProcessingAction> AnimationsToProcessMap; AnimationsToProcessMap m_keyframeAnimationsToProcess; diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm index dea6bfc..22e39f5 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -42,6 +42,7 @@ #import <QuartzCore/QuartzCore.h> #import "RotateTransformOperation.h" #import "ScaleTransformOperation.h" +#import "StringBuilder.h" #import "SystemTime.h" #import "TranslateTransformOperation.h" #import "WebLayer.h" @@ -86,6 +87,10 @@ static double mediaTimeToCurrentTime(CFTimeInterval t) } // namespace WebCore +@interface CALayer(Private) +- (void)setContentsChanged; +@end + @interface WebAnimationDelegate : NSObject { WebCore::GraphicsLayerCA* m_graphicsLayer; } @@ -246,12 +251,20 @@ static String propertyIdToString(AnimatedPropertyID property) return ""; } -static String animationIdentifier(AnimatedPropertyID property, int index) +static String animationIdentifier(AnimatedPropertyID property, const String& keyframesName, int index) { - String animationId = propertyIdToString(property); - animationId.append("_"); - animationId.append(String::number(index)); - return animationId; + StringBuilder builder; + + builder.append(propertyIdToString(property)); + builder.append("_"); + + if (!keyframesName.isEmpty()) { + builder.append(keyframesName); + builder.append("_"); + } + builder.append("_"); + builder.append(String::number(index)); + return builder.toString(); } #if !HAVE_MODERN_QUARTZCORE @@ -392,9 +405,17 @@ GraphicsLayerCA::~GraphicsLayerCA() [layer setLayerOwner:nil]; } + if (m_contentsLayer) { + if ([m_contentsLayer.get() respondsToSelector:@selector(setLayerOwner:)]) + [(id)m_contentsLayer.get() setLayerOwner:nil]; + } + // animationDidStart: can fire after this, so we need to clear out the layer on the delegate. [m_animationDelegate.get() setLayer:0]; + // Release the clone layers inside the exception-handling block. + removeCloneLayers(); + END_BLOCK_OBJC_EXCEPTIONS } @@ -414,7 +435,7 @@ bool GraphicsLayerCA::setChildren(const Vector<GraphicsLayer*>& children) { bool childrenChanged = GraphicsLayer::setChildren(children); if (childrenChanged) - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); return childrenChanged; } @@ -422,31 +443,31 @@ bool GraphicsLayerCA::setChildren(const Vector<GraphicsLayer*>& children) void GraphicsLayerCA::addChild(GraphicsLayer* childLayer) { GraphicsLayer::addChild(childLayer); - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); } void GraphicsLayerCA::addChildAtIndex(GraphicsLayer* childLayer, int index) { GraphicsLayer::addChildAtIndex(childLayer, index); - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); } void GraphicsLayerCA::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) { GraphicsLayer::addChildBelow(childLayer, sibling); - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); } void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) { GraphicsLayer::addChildAbove(childLayer, sibling); - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); } bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) { if (GraphicsLayer::replaceChild(oldChild, newChild)) { - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); return true; } return false; @@ -455,7 +476,7 @@ bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newCh void GraphicsLayerCA::removeFromParent() { if (m_parent) - static_cast<GraphicsLayerCA*>(m_parent)->noteLayerPropertyChanged(ChildrenChanged); + static_cast<GraphicsLayerCA*>(m_parent)->noteSublayersChanged(); GraphicsLayer::removeFromParent(); } @@ -466,6 +487,30 @@ void GraphicsLayerCA::setMaskLayer(GraphicsLayer* layer) GraphicsLayer::setMaskLayer(layer); noteLayerPropertyChanged(MaskLayerChanged); + + propagateLayerChangeToReplicas(); + + if (m_replicatedLayer) + static_cast<GraphicsLayerCA*>(m_replicatedLayer)->propagateLayerChangeToReplicas(); +} + +void GraphicsLayerCA::setReplicatedLayer(GraphicsLayer* layer) +{ + if (layer == m_replicatedLayer) + return; + + GraphicsLayer::setReplicatedLayer(layer); + noteLayerPropertyChanged(ReplicatedLayerChanged); +} + +void GraphicsLayerCA::setReplicatedByLayer(GraphicsLayer* layer) +{ + if (layer == m_replicaLayer) + return; + + GraphicsLayer::setReplicatedByLayer(layer); + noteSublayersChanged(); + noteLayerPropertyChanged(ReplicatedLayerChanged); } void GraphicsLayerCA::setPosition(const FloatPoint& point) @@ -513,22 +558,41 @@ void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) noteLayerPropertyChanged(ChildrenTransformChanged); } -static void moveAnimation(AnimatedPropertyID property, CALayer* fromLayer, CALayer* toLayer) +void GraphicsLayerCA::moveOrCopyAllAnimationsForProperty(MoveOrCopy operation, AnimatedPropertyID property, const String& keyframesName, CALayer *fromLayer, CALayer *toLayer) { for (int index = 0; ; ++index) { - String animName = animationIdentifier(property, index); + String animName = animationIdentifier(property, keyframesName, index); CAAnimation* anim = [fromLayer animationForKey:animName]; if (!anim) break; - [anim retain]; - [fromLayer removeAnimationForKey:animName]; - [toLayer addAnimation:anim forKey:animName]; - [anim release]; + switch (operation) { + case Move: + [anim retain]; + [fromLayer removeAnimationForKey:animName]; + [toLayer addAnimation:anim forKey:animName]; + [anim release]; + break; + + case Copy: + [toLayer addAnimation:anim forKey:animName]; + break; + } } } +void GraphicsLayerCA::moveOrCopyAnimationsForProperty(MoveOrCopy operation, AnimatedPropertyID property, CALayer *fromLayer, CALayer *toLayer) +{ + // Move transitions for this property. + moveOrCopyAllAnimationsForProperty(operation, property, "", fromLayer, toLayer); + + // Look for running animations affecting this property. + KeyframeAnimationsMap::const_iterator end = m_runningKeyframeAnimations.end(); + for (KeyframeAnimationsMap::const_iterator it = m_runningKeyframeAnimations.begin(); it != end; ++it) + moveOrCopyAllAnimationsForProperty(operation, property, it->first, fromLayer, toLayer); +} + void GraphicsLayerCA::setPreserves3D(bool preserves3D) { if (preserves3D == m_preserves3D) @@ -643,7 +707,7 @@ void GraphicsLayerCA::setContentsRect(const IntRect& rect) noteLayerPropertyChanged(ContentsRectChanged); } -bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double beginTime) +bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset) { if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) return false; @@ -657,9 +721,9 @@ bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const Int bool createdAnimations = false; if (valueList.property() == AnimatedPropertyWebkitTransform) - createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, keyframesName, beginTime, boxSize); + createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, keyframesName, timeOffset, boxSize); else - createdAnimations = createAnimationFromKeyframes(valueList, anim, keyframesName, beginTime); + createdAnimations = createAnimationFromKeyframes(valueList, anim, keyframesName, timeOffset); if (createdAnimations) noteLayerPropertyChanged(AnimationChanged); @@ -681,22 +745,23 @@ void GraphicsLayerCA::removeAnimationsForKeyframes(const String& animationName) if (!animationIsRunning(animationName)) return; - m_keyframeAnimationsToProcess.add(animationName, Remove); + m_keyframeAnimationsToProcess.add(animationName, AnimationProcessingAction(Remove)); noteLayerPropertyChanged(AnimationChanged); } -void GraphicsLayerCA::pauseAnimation(const String& keyframesName) +void GraphicsLayerCA::pauseAnimation(const String& keyframesName, double timeOffset) { if (!animationIsRunning(keyframesName)) return; AnimationsToProcessMap::iterator it = m_keyframeAnimationsToProcess.find(keyframesName); if (it != m_keyframeAnimationsToProcess.end()) { + AnimationProcessingAction& processingInfo = it->second; // If an animation is scheduled to be removed, don't change the remove to a pause. - if (it->second != Remove) - it->second = Pause; + if (processingInfo.action != Remove) + processingInfo.action = Pause; } else - m_keyframeAnimationsToProcess.add(keyframesName, Pause); + m_keyframeAnimationsToProcess.add(keyframesName, AnimationProcessingAction(Pause, timeOffset)); noteLayerPropertyChanged(AnimationChanged); } @@ -716,26 +781,27 @@ void GraphicsLayerCA::setContentsToImage(Image* image) } m_contentsLayerPurpose = ContentsLayerForImage; if (!m_contentsLayer) - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); } else { m_pendingContentsImage = 0; m_contentsLayerPurpose = NoContentsLayer; if (m_contentsLayer) - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); } noteLayerPropertyChanged(ContentsImageChanged); } -void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) +void GraphicsLayerCA::setContentsToMedia(PlatformLayer* mediaLayer) { - if (videoLayer != m_contentsLayer.get()) - noteLayerPropertyChanged(ChildrenChanged); + if (mediaLayer == m_contentsLayer) + return; - m_contentsLayer = videoLayer; - noteLayerPropertyChanged(ContentsVideoChanged); + m_contentsLayer = mediaLayer; + m_contentsLayerPurpose = mediaLayer ? ContentsLayerForMedia : NoContentsLayer; - m_contentsLayerPurpose = videoLayer ? ContentsLayerForVideo : NoContentsLayer; + noteSublayersChanged(); + noteLayerPropertyChanged(ContentsMediaLayerChanged); } void GraphicsLayerCA::setGeometryOrientation(CompositingCoordinatesOrientation orientation) @@ -760,6 +826,35 @@ void GraphicsLayerCA::setGeometryOrientation(CompositingCoordinatesOrientation o #endif } +void GraphicsLayerCA::didDisplay(PlatformLayer* layer) +{ + CALayer* sourceLayer; + LayerMap* layerCloneMap; + + if (layer == m_layer) { + sourceLayer = m_layer.get(); + layerCloneMap = m_layerClones.get(); + } else if (layer == m_contentsLayer) { + sourceLayer = m_contentsLayer.get(); + layerCloneMap = m_contentsLayerClones.get(); + } else + return; + + if (layerCloneMap) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currClone = it->second.get(); + if (!currClone) + continue; + + if ([currClone contents] != [sourceLayer contents]) + [currClone setContents:[sourceLayer contents]]; + else + [currClone setContentsChanged]; + } + } +} + void GraphicsLayerCA::syncCompositingState() { recursiveCommitChanges(); @@ -767,10 +862,10 @@ void GraphicsLayerCA::syncCompositingState() void GraphicsLayerCA::recursiveCommitChanges() { - commitLayerChanges(); + commitLayerChangesBeforeSublayers(); if (m_maskLayer) - static_cast<GraphicsLayerCA*>(m_maskLayer)->commitLayerChanges(); + static_cast<GraphicsLayerCA*>(m_maskLayer)->commitLayerChangesBeforeSublayers(); const Vector<GraphicsLayer*>& childLayers = children(); size_t numChildren = childLayers.size(); @@ -778,9 +873,17 @@ void GraphicsLayerCA::recursiveCommitChanges() GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); curChild->recursiveCommitChanges(); } + + if (m_replicaLayer) + static_cast<GraphicsLayerCA*>(m_replicaLayer)->recursiveCommitChanges(); + + if (m_maskLayer) + static_cast<GraphicsLayerCA*>(m_maskLayer)->commitLayerChangesAfterSublayers(); + + commitLayerChangesAfterSublayers(); } -void GraphicsLayerCA::commitLayerChanges() +void GraphicsLayerCA::commitLayerChangesBeforeSublayers() { if (!m_uncommittedChanges) return; @@ -788,20 +891,17 @@ void GraphicsLayerCA::commitLayerChanges() BEGIN_BLOCK_OBJC_EXCEPTIONS // Need to handle Preserves3DChanged first, because it affects which layers subsequent properties are applied to - if (m_uncommittedChanges & Preserves3DChanged) - updateLayerPreserves3D(); + if (m_uncommittedChanges & (Preserves3DChanged | ReplicatedLayerChanged)) + updateStructuralLayer(); - if (m_uncommittedChanges & NameChanged) { - if (m_transformLayer) - [m_transformLayer.get() setName:("Transform layer " + name())]; - [m_layer.get() setName:name()]; - } + if (m_uncommittedChanges & NameChanged) + updateLayerNames(); if (m_uncommittedChanges & ContentsImageChanged) // Needs to happen before ChildrenChanged updateContentsImage(); - if (m_uncommittedChanges & ContentsVideoChanged) // Needs to happen before ChildrenChanged - updateContentsVideo(); + if (m_uncommittedChanges & ContentsMediaLayerChanged) // Needs to happen before ChildrenChanged + updateContentsMediaLayer(); #if ENABLE(3D_CANVAS) if (m_uncommittedChanges & ContentsGraphicsContext3DChanged) // Needs to happen before ChildrenChanged @@ -859,40 +959,72 @@ void GraphicsLayerCA::commitLayerChanges() if (m_uncommittedChanges & MaskLayerChanged) updateMaskLayer(); + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::commitLayerChangesAfterSublayers() +{ + if (!m_uncommittedChanges) + return; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (m_uncommittedChanges & ReplicatedLayerChanged) + updateReplicatedLayers(); + m_uncommittedChanges = NoChange; END_BLOCK_OBJC_EXCEPTIONS } +void GraphicsLayerCA::updateLayerNames() +{ + switch (structuralLayerPurpose()) { + case StructuralLayerForPreserves3D: + [m_structuralLayer.get() setName:("Transform layer " + name())]; + break; + case StructuralLayerForReplicaFlattening: + [m_structuralLayer.get() setName:("Replica flattening layer " + name())]; + break; + case NoStructuralLayer: + break; + } + [m_layer.get() setName:name()]; +} + void GraphicsLayerCA::updateSublayerList() { NSMutableArray* newSublayers = nil; - if (m_transformLayer) { - // Add the primary layer first. Even if we have negative z-order children, the primary layer always comes behind. - 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. - // This does not cause visible rendering issues because currently contents layers are only used - // for replaced elements that don't have children. - newSublayers = [[NSMutableArray alloc] initWithObjects:m_contentsLayer.get(), nil]; - } - 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 + + if (m_structuralLayer || m_contentsLayer || childLayers.size() > 0) { + newSublayers = [[NSMutableArray alloc] init]; + + if (m_structuralLayer) { + // Add the replica layer first. + if (m_replicaLayer) + [newSublayers addObject:static_cast<GraphicsLayerCA*>(m_replicaLayer)->primaryLayer()]; + // Add the primary layer. Even if we have negative z-order children, the primary layer always comes behind. + [newSublayers addObject:m_layer.get()]; + } else if (m_contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + // This does not cause visible rendering issues because currently contents layers are only used + // for replaced elements that don't have children. + [newSublayers addObject:m_contentsLayer.get()]; + } + + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + CALayer *childLayer = curChild->layerForSuperlayer(); [newSublayers addObject:childLayer]; - } + } - [newSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; + [newSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; + } - if (m_transformLayer) { - safeSetSublayers(m_transformLayer.get(), newSublayers); + if (m_structuralLayer) { + safeSetSublayers(m_structuralLayer.get(), newSublayers); if (m_contentsLayer) { // If we have a transform layer, then the contents layer is parented in the @@ -913,16 +1045,43 @@ void GraphicsLayerCA::updateLayerPosition() m_position.y() + m_anchorPoint.y() * m_size.height()); [primaryLayer() setPosition:posPoint]; + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CGPoint clonePosition = posPoint; + if (m_replicaLayer && isReplicatedRootClone(it->first)) { + // Maintain the special-case position for the root of a clone subtree, + // which we set up in replicatedLayerRoot(). + clonePosition = positionForCloneRootLayer(); + } + CALayer *currLayer = it->second.get(); + [currLayer setPosition:clonePosition]; + } + } } void GraphicsLayerCA::updateLayerSize() { CGRect rect = CGRectMake(0, 0, m_size.width(), m_size.height()); - if (m_transformLayer) { - [m_transformLayer.get() setBounds:rect]; + if (m_structuralLayer) { + [m_structuralLayer.get() setBounds:rect]; + + if (LayerMap* layerCloneMap = m_structuralLayerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + [it->second.get() setBounds:rect]; + } + // The anchor of the contents layer is always at 0.5, 0.5, so the position is center-relative. CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); [m_layer.get() setPosition:centerPoint]; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + [it->second.get() setPosition:centerPoint]; + } } bool needTiledLayer = requiresTiledLayer(m_size); @@ -930,6 +1089,11 @@ void GraphicsLayerCA::updateLayerSize() swapFromOrToTiledLayer(needTiledLayer); [m_layer.get() setBounds:rect]; + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + [it->second.get() setBounds:rect]; + } // Contents transform may depend on height. updateContentsTransform(); @@ -947,6 +1111,18 @@ void GraphicsLayerCA::updateAnchorPoint() #if HAVE_MODERN_QUARTZCORE [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; #endif + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setAnchorPoint:FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())]; +#if HAVE_MODERN_QUARTZCORE + [currLayer setAnchorPointZ:m_anchorPoint.z()]; +#endif + } + } + updateLayerPosition(); } @@ -955,6 +1131,19 @@ void GraphicsLayerCA::updateTransform() CATransform3D transform; copyTransform(transform, m_transform); [primaryLayer() setTransform:transform]; + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + if (m_replicaLayer && isReplicatedRootClone(it->first)) { + // Maintain the special-case transform for the root of a clone subtree, + // which we set up in replicatedLayerRoot(). + [currLayer setTransform:CATransform3DIdentity]; + } else + [currLayer setTransform:transform]; + } + } } void GraphicsLayerCA::updateChildrenTransform() @@ -962,85 +1151,157 @@ void GraphicsLayerCA::updateChildrenTransform() CATransform3D transform; copyTransform(transform, m_childrenTransform); [primaryLayer() setSublayerTransform:transform]; + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setSublayerTransform:transform]; + } + } } void GraphicsLayerCA::updateMasksToBounds() { [m_layer.get() setMasksToBounds:m_masksToBounds]; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setMasksToBounds:m_masksToBounds]; + } + } + updateDebugIndicators(); } void GraphicsLayerCA::updateContentsOpaque() { [m_layer.get() setOpaque:m_contentsOpaque]; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setOpaque:m_contentsOpaque]; + } + } } void GraphicsLayerCA::updateBackfaceVisibility() { [m_layer.get() setDoubleSided:m_backfaceVisibility]; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setDoubleSided:m_backfaceVisibility]; + } + } } -void GraphicsLayerCA::updateLayerPreserves3D() +void GraphicsLayerCA::updateStructuralLayer() { - Class transformLayerClass = NSClassFromString(@"CATransformLayer"); - if (!transformLayerClass) - return; + ensureStructuralLayer(structuralLayerPurpose()); +} - if (m_preserves3D && !m_transformLayer) { - // Create the transform layer. - m_transformLayer.adoptNS([[transformLayerClass alloc] init]); +void GraphicsLayerCA::ensureStructuralLayer(StructuralLayerPurpose purpose) +{ + if (purpose == NoStructuralLayer) { + if (m_structuralLayer) { + // Replace the transformLayer in the parent with this layer. + [m_layer.get() removeFromSuperlayer]; + [[m_structuralLayer.get() superlayer] replaceSublayer:m_structuralLayer.get() with:m_layer.get()]; - // Turn off default animations. - [m_transformLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, m_structuralLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, m_structuralLayer.get(), m_layer.get()); -#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. - updateLayerPosition(); - updateLayerSize(); - updateAnchorPoint(); - updateTransform(); - updateChildrenTransform(); - - CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); - [m_layer.get() setPosition:point]; + // Release the structural layer. + m_structuralLayer = 0; - [m_layer.get() setAnchorPoint:CGPointMake(0.5f, 0.5f)]; - [m_layer.get() setTransform:CATransform3DIdentity]; - - // Set the old layer to opacity of 1. Further down we will set the opacity on the transform layer. - [m_layer.get() setOpacity:1]; + // Update the properties of m_layer now that we no loner have a structural layer. + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); - // Move this layer to be a child of the transform layer. - [[m_layer.get() superlayer] replaceSublayer:m_layer.get() with:m_transformLayer.get()]; - [m_transformLayer.get() addSublayer:m_layer.get()]; + updateSublayerList(); + updateOpacityOnLayer(); + } + return; + } + + bool structuralLayerChanged = false; + + if (purpose == StructuralLayerForPreserves3D) { + Class transformLayerClass = NSClassFromString(@"CATransformLayer"); + if (!transformLayerClass) + return; - moveAnimation(AnimatedPropertyWebkitTransform, m_layer.get(), m_transformLayer.get()); + if (m_structuralLayer && ![m_structuralLayer.get() isKindOfClass:transformLayerClass]) + m_structuralLayer = 0; - 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()); - - // Release the transform layer. - m_transformLayer = 0; - - updateLayerPosition(); - updateLayerSize(); - updateAnchorPoint(); - updateTransform(); - updateChildrenTransform(); + if (!m_structuralLayer) { + m_structuralLayer.adoptNS([[transformLayerClass alloc] init]); + structuralLayerChanged = true; + } + } else { + if (m_structuralLayer && ![m_structuralLayer.get() isMemberOfClass:[CALayer self]]) + m_structuralLayer = 0; - updateSublayerList(); + if (!m_structuralLayer) { + m_structuralLayer.adoptNS([[CALayer alloc] init]); + structuralLayerChanged = true; + } } + + if (!structuralLayerChanged) + return; + + // Turn off default animations. + [m_structuralLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + + updateLayerNames(); + // Update the properties of the structural layer. + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + // Set properties of m_layer to their default values, since these are expressed on on the structural layer. + CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + [m_layer.get() setPosition:point]; + [m_layer.get() setAnchorPoint:CGPointMake(0.5f, 0.5f)]; + [m_layer.get() setTransform:CATransform3DIdentity]; + [m_layer.get() setOpacity:1]; + + // Move this layer to be a child of the transform layer. + [[m_layer.get() superlayer] replaceSublayer:m_layer.get() with:m_structuralLayer.get()]; + [m_structuralLayer.get() addSublayer:m_layer.get()]; + + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, m_layer.get(), m_structuralLayer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, m_layer.get(), m_structuralLayer.get()); + + updateSublayerList(); updateOpacityOnLayer(); } +GraphicsLayerCA::StructuralLayerPurpose GraphicsLayerCA::structuralLayerPurpose() const +{ + if (preserves3D()) + return StructuralLayerForPreserves3D; + + if (isReplicated()) + return StructuralLayerForReplicaFlattening; + + return NoStructuralLayer; +} + void GraphicsLayerCA::updateLayerDrawsContent() { bool needTiledLayer = requiresTiledLayer(m_size); @@ -1087,6 +1348,12 @@ void GraphicsLayerCA::updateContentsImage() #endif [m_contentsLayer.get() setContents:(id)m_pendingContentsImage.get()]; m_pendingContentsImage = 0; + + if (m_contentsLayerClones) { + LayerMap::const_iterator end = m_contentsLayerClones->end(); + for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) + [it->second.get() setContents:[m_contentsLayer.get() contents]]; + } updateContentsRect(); } else { @@ -1096,7 +1363,7 @@ void GraphicsLayerCA::updateContentsImage() } } -void GraphicsLayerCA::updateContentsVideo() +void GraphicsLayerCA::updateContentsMediaLayer() { // Video layer was set as m_contentsLayer, and will get parented in updateSublayerList(). if (m_contentsLayer) { @@ -1131,6 +1398,15 @@ void GraphicsLayerCA::updateContentsRect() [m_contentsLayer.get() setPosition:point]; [m_contentsLayer.get() setBounds:rect]; + + if (m_contentsLayerClones) { + LayerMap::const_iterator end = m_contentsLayerClones->end(); + for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setPosition:point]; + [currLayer setBounds:rect]; + } + } } void GraphicsLayerCA::updateGeometryOrientation() @@ -1152,8 +1428,74 @@ void GraphicsLayerCA::updateGeometryOrientation() void GraphicsLayerCA::updateMaskLayer() { - CALayer* maskCALayer = m_maskLayer ? m_maskLayer->platformLayer() : 0; + CALayer *maskCALayer = m_maskLayer ? m_maskLayer->platformLayer() : 0; [m_layer.get() setMask:maskCALayer]; + + LayerMap* maskLayerCloneMap = m_maskLayer ? static_cast<GraphicsLayerCA*>(m_maskLayer)->primaryLayerClones() : 0; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + + CALayer *maskClone = maskLayerCloneMap ? maskLayerCloneMap->get(it->first).get() : 0; + [currLayer setMask:maskClone]; + } + } +} + +void GraphicsLayerCA::updateReplicatedLayers() +{ + // Clone the descendants of the replicated layer, and parent under us. + ReplicaState replicaState(ReplicaState::ReplicaBranch); + + CALayer *replicaRoot = replicatedLayerRoot(replicaState); + if (!replicaRoot) + return; + + if (m_structuralLayer) + [m_structuralLayer.get() insertSublayer:replicaRoot atIndex:0]; + else + [m_layer.get() insertSublayer:replicaRoot atIndex:0]; +} + +// For now, this assumes that layers only ever have one replica, so replicaIndices contains only 0 and 1. +GraphicsLayerCA::CloneID GraphicsLayerCA::ReplicaState::cloneID() const +{ + size_t depth = m_replicaBranches.size(); + + const size_t bitsPerUChar = sizeof(UChar) * 8; + size_t vectorSize = (depth + bitsPerUChar - 1) / bitsPerUChar; + + Vector<UChar> result(vectorSize); + result.fill(0); + + // Create a string from the bit sequence which we can use to identify the clone. + // Note that the string may contain embedded nulls, but that's OK. + for (size_t i = 0; i < depth; ++i) { + UChar& currChar = result[i / bitsPerUChar]; + currChar = (currChar << 1) | m_replicaBranches[i]; + } + + return String::adopt(result); +} + +CALayer *GraphicsLayerCA::replicatedLayerRoot(ReplicaState& replicaState) +{ + // Limit replica nesting, to avoid 2^N explosion of replica layers. + if (!m_replicatedLayer || replicaState.replicaDepth() == ReplicaState::maxReplicaDepth) + return nil; + + GraphicsLayerCA* replicatedLayer = static_cast<GraphicsLayerCA*>(m_replicatedLayer); + + CALayer *clonedLayerRoot = replicatedLayer->fetchCloneLayers(this, replicaState, RootCloneLevel); + FloatPoint cloneRootPosition = replicatedLayer->positionForCloneRootLayer(); + + // Replica root has no offset or transform + [clonedLayerRoot setPosition:cloneRootPosition]; + [clonedLayerRoot setTransform:CATransform3DIdentity]; + + return clonedLayerRoot; } void GraphicsLayerCA::updateLayerAnimations() @@ -1163,10 +1505,8 @@ void GraphicsLayerCA::updateLayerAnimations() 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)) + if (!removeAnimationFromLayer(currProperty, "", index)) break; } } @@ -1182,21 +1522,21 @@ void GraphicsLayerCA::updateLayerAnimations() if (animationIt == m_runningKeyframeAnimations.end()) continue; - AnimationProcessingAction action = it->second; + const AnimationProcessingAction& processingInfo = it->second; const Vector<AnimationPair>& animations = animationIt->second; for (size_t i = 0; i < animations.size(); ++i) { const AnimationPair& currPair = animations[i]; - switch (action) { + switch (processingInfo.action) { case Remove: - removeAnimationFromLayer(static_cast<AnimatedPropertyID>(currPair.first), currPair.second); + removeAnimationFromLayer(static_cast<AnimatedPropertyID>(currPair.first), currKeyframeName, currPair.second); break; case Pause: - pauseAnimationOnLayer(static_cast<AnimatedPropertyID>(currPair.first), currPair.second); + pauseAnimationOnLayer(static_cast<AnimatedPropertyID>(currPair.first), currKeyframeName, currPair.second, processingInfo.timeOffset); break; } } - if (action == Remove) + if (processingInfo.action == Remove) m_runningKeyframeAnimations.remove(currKeyframeName); } @@ -1207,7 +1547,7 @@ void GraphicsLayerCA::updateLayerAnimations() 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); + setAnimationOnLayer(pendingAnimation.m_animation.get(), pendingAnimation.m_property, pendingAnimation.m_keyframesName, pendingAnimation.m_index, pendingAnimation.m_timeOffset); if (!pendingAnimation.m_keyframesName.isEmpty()) { // If this is a keyframe anim, we have to remember the association of keyframes name to property/index pairs, @@ -1229,19 +1569,28 @@ void GraphicsLayerCA::updateLayerAnimations() } } -void GraphicsLayerCA::setAnimationOnLayer(CAPropertyAnimation* caAnim, AnimatedPropertyID property, int index, double beginTime) +void GraphicsLayerCA::setAnimationOnLayer(CAPropertyAnimation* caAnim, AnimatedPropertyID property, const String& keyframesName, int index, double timeOffset) { PlatformLayer* layer = animatedLayer(property); - if (beginTime) { - NSTimeInterval time = [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil]; - [caAnim setBeginTime:time]; - } + [caAnim setTimeOffset:timeOffset]; - String animationName = animationIdentifier(property, index); + String animationName = animationIdentifier(property, keyframesName, index); [layer removeAnimationForKey:animationName]; [layer addAnimation:caAnim forKey:animationName]; + + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + CALayer *currLayer = it->second.get(); + [currLayer removeAnimationForKey:animationName]; + [currLayer addAnimation:caAnim forKey:animationName]; + } + } } // Workaround for <rdar://problem/7311367> @@ -1259,18 +1608,29 @@ static void bug7311367Workaround(CALayer* transformLayer, const TransformationMa [transformLayer setTransform:caTransform]; } -bool GraphicsLayerCA::removeAnimationFromLayer(AnimatedPropertyID property, int index) +bool GraphicsLayerCA::removeAnimationFromLayer(AnimatedPropertyID property, const String& keyframesName, int index) { PlatformLayer* layer = animatedLayer(property); - String animationName = animationIdentifier(property, index); + String animationName = animationIdentifier(property, keyframesName, index); if (![layer animationForKey:animationName]) return false; [layer removeAnimationForKey:animationName]; - - bug7311367Workaround(m_transformLayer.get(), m_transform); + bug7311367Workaround(m_structuralLayer.get(), m_transform); + + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + + CALayer *currLayer = it->second.get(); + [currLayer removeAnimationForKey:animationName]; + } + } return true; } @@ -1290,11 +1650,11 @@ static void copyAnimationProperties(CAPropertyAnimation* from, CAPropertyAnimati #endif } -void GraphicsLayerCA::pauseAnimationOnLayer(AnimatedPropertyID property, int index) +void GraphicsLayerCA::pauseAnimationOnLayer(AnimatedPropertyID property, const String& keyframesName, int index, double timeOffset) { PlatformLayer* layer = animatedLayer(property); - String animationName = animationIdentifier(property, index); + String animationName = animationIdentifier(property, keyframesName, index); CAAnimation* caAnim = [layer animationForKey:animationName]; if (!caAnim) @@ -1319,10 +1679,23 @@ void GraphicsLayerCA::pauseAnimationOnLayer(AnimatedPropertyID property, int ind pausedAnim = newAnim; } - double t = [layer convertTime:currentTimeToMediaTime(currentTime()) fromLayer:nil]; + // pausedAnim has the beginTime of caAnim already. [pausedAnim setSpeed:0]; - [pausedAnim setTimeOffset:t - [caAnim beginTime]]; + [pausedAnim setTimeOffset:timeOffset]; + [layer addAnimation:pausedAnim forKey:animationName]; // This will replace the running animation. + + // Pause the animations on the clones too. + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + CALayer *currLayer = it->second.get(); + [currLayer addAnimation:pausedAnim forKey:animationName]; + } + } } #if ENABLE(3D_CANVAS) @@ -1337,7 +1710,7 @@ void GraphicsLayerCA::setContentsToGraphicsContext3D(const GraphicsContext3D* gr m_platformGraphicsContext3D = context; m_platformTexture = texture; - noteLayerPropertyChanged(ChildrenChanged); + noteSublayersChanged(); BEGIN_BLOCK_OBJC_EXCEPTIONS @@ -1346,9 +1719,11 @@ void GraphicsLayerCA::setContentsToGraphicsContext3D(const GraphicsContext3D* gr m_contentsLayer.adoptNS([[Canvas3DLayer alloc] initWithContext:static_cast<CGLContextObj>(m_platformGraphicsContext3D) texture:static_cast<GLuint>(m_platformTexture)]); #ifndef NDEBUG [m_contentsLayer.get() setName:@"3D Layer"]; -#endif +#endif + [m_contentsLayer.get() setLayerOwner:this]; } else { // remove the inner layer + [m_contentsLayer.get() setLayerOwner:0]; m_contentsLayer = 0; } @@ -1370,7 +1745,7 @@ void GraphicsLayerCA::repaintLayerDirtyRects() m_dirtyRects.clear(); } -bool GraphicsLayerCA::createAnimationFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& keyframesName, double beginTime) +bool GraphicsLayerCA::createAnimationFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& keyframesName, double timeOffset) { ASSERT(valueList.property() != AnimatedPropertyWebkitTransform); @@ -1396,14 +1771,14 @@ bool GraphicsLayerCA::createAnimationFromKeyframes(const KeyframeValueList& valu if (!valuesOK) return false; - m_uncomittedAnimations.append(LayerAnimation(caAnimation, keyframesName, valueList.property(), animationIndex, beginTime)); + m_uncomittedAnimations.append(LayerAnimation(caAnimation, keyframesName, valueList.property(), animationIndex, timeOffset)); END_BLOCK_OBJC_EXCEPTIONS; return true; } -bool GraphicsLayerCA::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& keyframesName, double beginTime, const IntSize& boxSize) +bool GraphicsLayerCA::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& keyframesName, double timeOffset, const IntSize& boxSize) { ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); @@ -1450,7 +1825,7 @@ bool GraphicsLayerCA::createTransformAnimationsFromKeyframes(const KeyframeValue if (!validMatrices) break; - m_uncomittedAnimations.append(LayerAnimation(caAnimation, keyframesName, valueList.property(), animationIndex, beginTime)); + m_uncomittedAnimations.append(LayerAnimation(caAnimation, keyframesName, valueList.property(), animationIndex, timeOffset)); } END_BLOCK_OBJC_EXCEPTIONS; @@ -1663,25 +2038,42 @@ void GraphicsLayerCA::suspendAnimations(double time) double t = currentTimeToMediaTime(time ? time : currentTime()); [primaryLayer() setSpeed:0]; [primaryLayer() setTimeOffset:t]; + + // Suspend the animations on the clones too. + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setSpeed:0 ]; + [currLayer setTimeOffset:t]; + } + } } void GraphicsLayerCA::resumeAnimations() { [primaryLayer() setSpeed:1]; [primaryLayer() setTimeOffset:0]; + + // Resume the animations on the clones too. + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setSpeed:1]; + [currLayer setTimeOffset:0]; + } + } } -WebLayer* GraphicsLayerCA::hostLayerForSublayers() const +CALayer* GraphicsLayerCA::hostLayerForSublayers() const { - return m_transformLayer ? m_transformLayer.get() : m_layer.get(); + return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); } -WebLayer* GraphicsLayerCA::layerForSuperlayer() const +CALayer* GraphicsLayerCA::layerForSuperlayer() const { - if (m_transformLayer) - return m_transformLayer.get(); - - return m_layer.get(); + return m_structuralLayer ? m_structuralLayer.get() : m_layer.get(); } CALayer* GraphicsLayerCA::animatedLayer(AnimatedPropertyID property) const @@ -1689,6 +2081,11 @@ CALayer* GraphicsLayerCA::animatedLayer(AnimatedPropertyID property) const return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); } +GraphicsLayerCA::LayerMap* GraphicsLayerCA::animatedLayerClones(AnimatedPropertyID property) const +{ + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayerClones.get() : primaryLayerClones(); +} + PlatformLayer* GraphicsLayerCA::platformLayer() const { return primaryLayer(); @@ -1790,9 +2187,9 @@ void GraphicsLayerCA::swapFromOrToTiledLayer(bool useTiledLayer) #endif // move over animations - moveAnimation(AnimatedPropertyWebkitTransform, oldLayer.get(), m_layer.get()); - moveAnimation(AnimatedPropertyOpacity, oldLayer.get(), m_layer.get()); - moveAnimation(AnimatedPropertyBackgroundColor, oldLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, oldLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, oldLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyBackgroundColor, oldLayer.get(), m_layer.get()); // need to tell new layer to draw itself setNeedsDisplay(); @@ -1847,9 +2244,232 @@ void GraphicsLayerCA::setupContentsLayer(CALayer* contentsLayer) } } +CALayer *GraphicsLayerCA::findOrMakeClone(CloneID cloneID, CALayer *sourceLayer, LayerMap* clones, CloneLevel cloneLevel) +{ + if (!sourceLayer) + return 0; + + CALayer *resultLayer; + + // Add with a dummy value to get an iterator for the insertion position, and a boolean that tells + // us whether there's an item there. This technique avoids two hash lookups. + RetainPtr<CALayer> dummy; + pair<LayerMap::iterator, bool> addResult = clones->add(cloneID, dummy); + if (!addResult.second) { + // Value was not added, so it exists already. + resultLayer = addResult.first->second.get(); + } else { + resultLayer = cloneLayer(sourceLayer, cloneLevel); +#ifndef NDEBUG + [resultLayer setName:[NSString stringWithFormat:@"Clone %d of layer %@", cloneID[0U], sourceLayer]]; +#endif + addResult.first->second = resultLayer; + } + + return resultLayer; +} + +void GraphicsLayerCA::ensureCloneLayers(CloneID cloneID, CALayer *& primaryLayer, CALayer *& structuralLayer, CALayer *& contentsLayer, CloneLevel cloneLevel) +{ + structuralLayer = nil; + contentsLayer = nil; + + if (!m_layerClones) + m_layerClones = new LayerMap; + + if (!m_structuralLayerClones && m_structuralLayer) + m_structuralLayerClones = new LayerMap; + + if (!m_contentsLayerClones && m_contentsLayer) + m_contentsLayerClones = new LayerMap; + + primaryLayer = findOrMakeClone(cloneID, m_layer.get(), m_layerClones.get(), cloneLevel); + structuralLayer = findOrMakeClone(cloneID, m_structuralLayer.get(), m_structuralLayerClones.get(), cloneLevel); + contentsLayer = findOrMakeClone(cloneID, m_contentsLayer.get(), m_contentsLayerClones.get(), cloneLevel); +} + +void GraphicsLayerCA::removeCloneLayers() +{ + m_layerClones = 0; + m_structuralLayerClones = 0; + m_contentsLayerClones = 0; +} + +FloatPoint GraphicsLayerCA::positionForCloneRootLayer() const +{ + // This can get called during a sync when we've just removed the m_replicaLayer. + if (!m_replicaLayer) + return FloatPoint(); + + FloatPoint replicaPosition = m_replicaLayer->replicatedLayerPosition(); + return FloatPoint(replicaPosition.x() + m_anchorPoint.x() * m_size.width(), + replicaPosition.y() + m_anchorPoint.y() * m_size.height()); +} + +void GraphicsLayerCA::propagateLayerChangeToReplicas() +{ + for (GraphicsLayer* currLayer = this; currLayer; currLayer = currLayer->parent()) { + GraphicsLayerCA* currLayerCA = static_cast<GraphicsLayerCA*>(currLayer); + if (!currLayerCA->hasCloneLayers()) + break; + + if (currLayerCA->replicaLayer()) + static_cast<GraphicsLayerCA*>(currLayerCA->replicaLayer())->noteLayerPropertyChanged(ReplicatedLayerChanged); + } +} + +CALayer *GraphicsLayerCA::fetchCloneLayers(GraphicsLayer* replicaRoot, ReplicaState& replicaState, CloneLevel cloneLevel) +{ + CALayer *primaryLayer; + CALayer *structuralLayer; + CALayer *contentsLayer; + ensureCloneLayers(replicaState.cloneID(), primaryLayer, structuralLayer, contentsLayer, cloneLevel); + + if (m_maskLayer) { + CALayer *maskClone = static_cast<GraphicsLayerCA*>(m_maskLayer)->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel); + [primaryLayer setMask:maskClone]; + } + + if (m_replicatedLayer) { + // We are a replica being asked for clones of our layers. + CALayer *replicaRoot = replicatedLayerRoot(replicaState); + if (!replicaRoot) + return nil; + + if (structuralLayer) { + [structuralLayer insertSublayer:replicaRoot atIndex:0]; + return structuralLayer; + } + + [primaryLayer insertSublayer:replicaRoot atIndex:0]; + return primaryLayer; + } + + const Vector<GraphicsLayer*>& childLayers = children(); + NSMutableArray* clonalSublayers = nil; + + CALayer *replicaLayer = nil; + if (m_replicaLayer && m_replicaLayer != replicaRoot) { + // We have nested replicas. Ask the replica layer for a clone of its contents. + replicaState.setBranchType(ReplicaState::ReplicaBranch); + replicaLayer = static_cast<GraphicsLayerCA*>(m_replicaLayer)->fetchCloneLayers(replicaRoot, replicaState, RootCloneLevel); + replicaState.setBranchType(ReplicaState::ChildBranch); + } + + if (replicaLayer || structuralLayer || contentsLayer || childLayers.size() > 0) { + clonalSublayers = [[NSMutableArray alloc] init]; + + if (structuralLayer) { + // Replicas render behind the actual layer content. + if (replicaLayer) + [clonalSublayers addObject:replicaLayer]; + + // Add the primary layer next. Even if we have negative z-order children, the primary layer always comes behind. + [clonalSublayers addObject:primaryLayer]; + } else if (contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + // This does not cause visible rendering issues because currently contents layers are only used + // for replaced elements that don't have children. + [clonalSublayers addObject:contentsLayer]; + } + + replicaState.push(ReplicaState::ChildBranch); + + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + + CALayer *childLayer = curChild->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel); + if (childLayer) + [clonalSublayers addObject:childLayer]; + } + + replicaState.pop(); + + [clonalSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; + } + + CALayer *result; + if (structuralLayer) { + [structuralLayer setSublayers:clonalSublayers]; + + if (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). + [primaryLayer setSublayers:nil]; + [primaryLayer addSublayer:contentsLayer]; + } + + result = structuralLayer; + } else { + [primaryLayer setSublayers:clonalSublayers]; + result = primaryLayer; + } + + [clonalSublayers release]; + return result; +} + +CALayer *GraphicsLayerCA::cloneLayer(CALayer *layer, CloneLevel cloneLevel) +{ + static Class transformLayerClass = NSClassFromString(@"CATransformLayer"); + CALayer *newLayer = nil; + if ([layer isKindOfClass:transformLayerClass]) + newLayer = [transformLayerClass layer]; + else + newLayer = [CALayer layer]; + + [newLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + + [newLayer setPosition:[layer position]]; + [newLayer setBounds:[layer bounds]]; + [newLayer setAnchorPoint:[layer anchorPoint]]; +#if HAVE_MODERN_QUARTZCORE + [newLayer setAnchorPointZ:[layer anchorPointZ]]; +#endif + [newLayer setTransform:[layer transform]]; + [newLayer setSublayerTransform:[layer sublayerTransform]]; + [newLayer setContents:[layer contents]]; + [newLayer setMasksToBounds:[layer masksToBounds]]; + [newLayer setDoubleSided:[layer isDoubleSided]]; + [newLayer setOpaque:[layer isOpaque]]; + [newLayer setBackgroundColor:[layer backgroundColor]]; + + if (cloneLevel == IntermediateCloneLevel) { + [newLayer setOpacity:[layer opacity]]; + moveOrCopyAnimationsForProperty(Copy, AnimatedPropertyWebkitTransform, layer, newLayer); + moveOrCopyAnimationsForProperty(Copy, AnimatedPropertyOpacity, layer, newLayer); + } + + if (showDebugBorders()) { + setLayerBorderColor(newLayer, Color(255, 122, 251)); + [newLayer setBorderWidth:2]; + } + + return newLayer; +} + void GraphicsLayerCA::setOpacityInternal(float accumulatedOpacity) { - [(preserves3D() ? m_layer.get() : primaryLayer()) setOpacity:accumulatedOpacity]; + LayerMap* layerCloneMap = 0; + + if (preserves3D()) { + [m_layer.get() setOpacity:accumulatedOpacity]; + layerCloneMap = m_layerClones.get(); + } else { + [primaryLayer() setOpacity:accumulatedOpacity]; + layerCloneMap = primaryLayerClones(); + } + + if (layerCloneMap) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + CALayer *currLayer = it->second.get(); + [currLayer setOpacity:m_opacity]; + } + } } void GraphicsLayerCA::updateOpacityOnLayer() @@ -1860,9 +2480,27 @@ void GraphicsLayerCA::updateOpacityOnLayer() distributeOpacity(parent() ? parent()->accumulatedOpacity() : 1); #else [primaryLayer() setOpacity:m_opacity]; + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + + CALayer *currLayer = it->second.get(); + [currLayer setOpacity:m_opacity]; + } + + } #endif } +void GraphicsLayerCA::noteSublayersChanged() +{ + noteLayerPropertyChanged(ChildrenChanged); + propagateLayerChangeToReplicas(); +} + void GraphicsLayerCA::noteLayerPropertyChanged(LayerChangeFlags flags) { if (!m_uncommittedChanges && m_client) diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index 7aaf95d..e9f64be 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -104,22 +104,19 @@ private: bool hasClosedCaptions() const; void setClosedCaptionsVisible(bool); - void setEndTime(float time); - - int dataRate() const; - MediaPlayer::NetworkState networkState() const { return m_networkState; } MediaPlayer::ReadyState readyState() const { return m_readyState; } PassRefPtr<TimeRanges> buffered() const; float maxTimeSeekable() const; unsigned bytesLoaded() const; - bool totalBytesKnown() const; unsigned totalBytes() const; void setVisible(bool); void setSize(const IntSize&); + virtual bool hasAvailableVideoFrame() const; + void paint(GraphicsContext*, const IntRect&); void paintCurrentFrameInContext(GraphicsContext*, const IntRect&); @@ -176,18 +173,19 @@ private: Timer<MediaPlayerPrivate> m_seekTimer; MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; - bool m_startedPlaying; - bool m_isStreaming; - bool m_visible; IntRect m_rect; FloatSize m_scaleFactor; unsigned m_enabledTrackCount; unsigned m_totalTrackCount; - bool m_hasUnsupportedTracks; float m_reportedDuration; float m_cachedDuration; float m_timeToRestore; RetainPtr<QTMovieLayer> m_qtVideoLayer; + bool m_startedPlaying; + bool m_isStreaming; + bool m_visible; + bool m_hasUnsupportedTracks; + bool m_videoFrameHasDrawn; #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 dfb5958..dd87bb5 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -204,17 +204,18 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) , m_networkState(MediaPlayer::Empty) , m_readyState(MediaPlayer::HaveNothing) - , m_startedPlaying(false) - , m_isStreaming(false) - , m_visible(false) , m_rect() , m_scaleFactor(1, 1) , m_enabledTrackCount(0) , m_totalTrackCount(0) + , m_reportedDuration(-1) + , m_cachedDuration(-1) + , m_timeToRestore(-1) + , m_startedPlaying(false) + , m_isStreaming(false) + , m_visible(false) , m_hasUnsupportedTracks(false) - , m_reportedDuration(-1.0f) - , m_cachedDuration(-1.0f) - , m_timeToRestore(-1.0f) + , m_videoFrameHasDrawn(false) #if DRAW_FRAME_RATE , m_frameCountWhilePlaying(0) , m_timeStartedPlaying(0) @@ -449,7 +450,7 @@ void MediaPlayerPrivate::createQTMovieLayer() // later via acceleratedRenderingStateChanged(). GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); if (videoGraphicsLayer) - videoGraphicsLayer->setContentsToVideo((PlatformLayer *)m_qtVideoLayer.get()); + videoGraphicsLayer->setContentsToMedia(m_qtVideoLayer.get()); } #endif } @@ -498,6 +499,9 @@ MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMod void MediaPlayerPrivate::setUpVideoRendering() { + if (!isReadyForRendering()) + return; + MediaRenderingMode currentMode = currentRenderingMode(); MediaRenderingMode preferredMode = preferredRenderingMode(); if (currentMode == preferredMode && currentMode != MediaRenderingNone) @@ -556,6 +560,7 @@ void MediaPlayerPrivate::load(const String& url) m_player->readyStateChanged(); } cancelSeek(); + m_videoFrameHasDrawn = false; [m_objcObserver.get() setDelayCallbacks:YES]; @@ -651,7 +656,7 @@ void MediaPlayerPrivate::doSeek() [m_qtMovie.get() setRate:0]; [m_qtMovie.get() setCurrentTime:qttime]; - // restore playback only if not at end, othewise QTMovie will loop + // restore playback only if not at end, otherwise QTMovie will loop float timeAfterSeek = currentTime(); if (oldRate && timeAfterSeek < duration()) [m_qtMovie.get() setRate:oldRate]; @@ -687,10 +692,6 @@ void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) } } -void MediaPlayerPrivate::setEndTime(float) -{ -} - bool MediaPlayerPrivate::paused() const { if (!metaDataAvailable()) @@ -765,7 +766,7 @@ void MediaPlayerPrivate::setClosedCaptionsVisible(bool closedCaptionsVisible) #if USE(ACCELERATED_COMPOSITING) && (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) if (closedCaptionsVisible && m_qtVideoLayer) { - // Captions will be rendered upsided down unless we flag the movie as flipped (again). See <rdar://7408440>. + // Captions will be rendered upside down unless we flag the movie as flipped (again). See <rdar://7408440>. [m_qtVideoLayer.get() setGeometryFlipped:YES]; } #endif @@ -796,13 +797,6 @@ void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch) createQTMovie([movieAttributes valueForKey:QTMovieURLAttribute], movieAttributes); } -int MediaPlayerPrivate::dataRate() const -{ - if (!metaDataAvailable()) - return 0; - return wkQTMovieDataRate(m_qtMovie.get()); -} - PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const { RefPtr<TimeRanges> timeRanges = TimeRanges::create(); @@ -839,11 +833,6 @@ unsigned MediaPlayerPrivate::bytesLoaded() const return totalBytes() * maxTimeLoaded() / dur; } -bool MediaPlayerPrivate::totalBytesKnown() const -{ - return totalBytes() > 0; -} - unsigned MediaPlayerPrivate::totalBytes() const { if (!metaDataAvailable()) @@ -981,7 +970,7 @@ void MediaPlayerPrivate::updateStates() } } - if (isReadyForRendering() && !hasSetUpVideoRendering()) + if (!hasSetUpVideoRendering()) setUpVideoRendering(); if (seeking()) @@ -1056,8 +1045,11 @@ void MediaPlayerPrivate::didEnd() // 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(); + // playback at another time, which causes problems in HTMLMediaElement. QTKit's 'ended' event + // fires when playing in reverse so don't update duration when at time zero! + float now = currentTime(); + if (now > 0) + m_cachedDuration = now; updateStates(); m_player->timeChanged(); @@ -1078,14 +1070,27 @@ void MediaPlayerPrivate::setVisible(bool b) { if (m_visible != b) { m_visible = b; - if (b) { - if (m_readyState >= MediaPlayer::HaveMetadata) - setUpVideoRendering(); - } else + if (b) + setUpVideoRendering(); + else tearDownVideoRendering(); } } +bool MediaPlayerPrivate::hasAvailableVideoFrame() const +{ + // When using a QTMovieLayer return true as soon as the movie reaches QTMovieLoadStatePlayable + // because although we don't *know* when the first frame has decoded, by the time we get and + // process the notification a frame should have propagated the VisualContext and been set on + // the layer. + if (currentRenderingMode() == MediaRenderingMovieLayer) + return m_readyState >= MediaPlayer::HaveCurrentData; + + // When using the software renderer QuickTime signals that a frame is available so we might as well + // wait until we know that a frame has been drawn. + return m_videoFrameHasDrawn; +} + void MediaPlayerPrivate::repaint() { if (m_hasUnsupportedTracks) @@ -1100,6 +1105,7 @@ void MediaPlayerPrivate::repaint() m_timeStartedPlaying = [NSDate timeIntervalSinceReferenceDate]; } #endif + m_videoFrameHasDrawn = true; m_player->repaint(); } @@ -1404,7 +1410,7 @@ void MediaPlayerPrivate::acceleratedRenderingStateChanged() if (currentRenderingMode() == MediaRenderingMovieLayer) { GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); if (videoGraphicsLayer) - videoGraphicsLayer->setContentsToVideo((PlatformLayer *)m_qtVideoLayer.get()); + videoGraphicsLayer->setContentsToMedia(m_qtVideoLayer.get()); } } #endif diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index 97a7251..ef7c58f 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -55,8 +55,7 @@ using namespace std; namespace WebCore { const float smallCapsFontSizeMultiplier = 0.7f; -const float contextDPI = 72.0f; -static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x * (contextDPI / (contextDPI * unitsPerEm)); } +static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x / unitsPerEm; } static bool initFontData(SimpleFontData* fontData) { @@ -149,7 +148,6 @@ void SimpleFontData::platformInit() m_styleGroup = 0; #endif #if USE(ATSUI) - m_ATSUStyleInitialized = false; m_ATSUMirrors = false; m_checkedShapesArabic = false; m_shapesArabic = false; @@ -318,8 +316,9 @@ void SimpleFontData::platformDestroy() wkReleaseStyleGroup(m_styleGroup); #endif #if USE(ATSUI) - if (m_ATSUStyleInitialized) - ATSUDisposeStyle(m_ATSUStyle); + HashMap<unsigned, ATSUStyle>::iterator end = m_ATSUStyleMap.end(); + for (HashMap<unsigned, ATSUStyle>::iterator it = m_ATSUStyleMap.begin(); it != end; ++it) + ATSUDisposeStyle(it->second); #endif } @@ -445,13 +444,15 @@ CTFontRef SimpleFontData::getCTFont() const return m_CTFont.get(); } -CFDictionaryRef SimpleFontData::getCFStringAttributes(TextRenderingMode textMode) const +CFDictionaryRef SimpleFontData::getCFStringAttributes(TypesettingFeatures typesettingFeatures) const { - if (m_CFStringAttributes) - return m_CFStringAttributes.get(); + unsigned key = typesettingFeatures + 1; + pair<HashMap<unsigned, RetainPtr<CFDictionaryRef> >::iterator, bool> addResult = m_CFStringAttributes.add(key, RetainPtr<CFDictionaryRef>()); + RetainPtr<CFDictionaryRef>& attributesDictionary = addResult.first->second; + if (!addResult.second) + return attributesDictionary.get(); - bool allowKerning = textMode == OptimizeLegibility || textMode == GeometricPrecision; - bool allowLigatures = platformData().allowsLigatures() || allowKerning; + bool allowLigatures = platformData().allowsLigatures() || (typesettingFeatures & Ligatures); static const int ligaturesNotAllowedValue = 0; static CFNumberRef ligaturesNotAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesNotAllowedValue); @@ -459,25 +460,25 @@ CFDictionaryRef SimpleFontData::getCFStringAttributes(TextRenderingMode textMode static const int ligaturesAllowedValue = 1; static CFNumberRef ligaturesAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesAllowedValue); - if (!allowKerning) { + if (!(typesettingFeatures & Kerning)) { static const float kerningAdjustmentValue = 0; static CFNumberRef kerningAdjustment = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &kerningAdjustmentValue); static const void* keysWithKerningDisabled[] = { kCTFontAttributeName, kCTKernAttributeName, kCTLigatureAttributeName }; const void* valuesWithKerningDisabled[] = { getCTFont(), kerningAdjustment, allowLigatures ? ligaturesAllowed : ligaturesNotAllowed }; - m_CFStringAttributes.adoptCF(CFDictionaryCreate(NULL, keysWithKerningDisabled, valuesWithKerningDisabled, + attributesDictionary.adoptCF(CFDictionaryCreate(NULL, keysWithKerningDisabled, valuesWithKerningDisabled, sizeof(keysWithKerningDisabled) / sizeof(*keysWithKerningDisabled), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); } else { // By omitting the kCTKernAttributeName attribute, we get Core Text's standard kerning. static const void* keysWithKerningEnabled[] = { kCTFontAttributeName, kCTLigatureAttributeName }; const void* valuesWithKerningEnabled[] = { getCTFont(), allowLigatures ? ligaturesAllowed : ligaturesNotAllowed }; - m_CFStringAttributes.adoptCF(CFDictionaryCreate(NULL, keysWithKerningEnabled, valuesWithKerningEnabled, + attributesDictionary.adoptCF(CFDictionaryCreate(NULL, keysWithKerningEnabled, valuesWithKerningEnabled, sizeof(keysWithKerningEnabled) / sizeof(*keysWithKerningEnabled), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); } - return m_CFStringAttributes.get(); + return attributesDictionary.get(); } #endif diff --git a/WebCore/platform/graphics/mac/WebLayer.mm b/WebCore/platform/graphics/mac/WebLayer.mm index 56b28e6..641d421 100644 --- a/WebCore/platform/graphics/mac/WebLayer.mm +++ b/WebCore/platform/graphics/mac/WebLayer.mm @@ -153,6 +153,13 @@ using namespace WebCore; } } +- (void)display +{ + [super display]; + if (m_layerOwner) + m_layerOwner->didDisplay(self); +} + - (void)drawInContext:(CGContextRef)context { [WebLayer drawContents:m_layerOwner ofLayer:self intoContext:context]; diff --git a/WebCore/platform/graphics/mac/WebTiledLayer.mm b/WebCore/platform/graphics/mac/WebTiledLayer.mm index a1f5693..97ba233 100644 --- a/WebCore/platform/graphics/mac/WebTiledLayer.mm +++ b/WebCore/platform/graphics/mac/WebTiledLayer.mm @@ -92,6 +92,13 @@ using namespace WebCore; } } +- (void)display +{ + [super display]; + if (m_layerOwner) + m_layerOwner->didDisplay(self); +} + - (void)drawInContext:(CGContextRef)ctx { [WebLayer drawContents:m_layerOwner ofLayer:self intoContext:ctx]; diff --git a/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp b/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp index 3a60160..12ae09d 100644 --- a/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp +++ b/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp @@ -401,7 +401,7 @@ static size_t renameFontInternal(SharedBuffer* fontData, const String& fontName, return nameTableSize; } -#if PLATFORM(WINCE) +#if OS(WINCE) // AddFontMemResourceEx does not exist on WinCE, so we must handle the font data manually // This function just renames the font and overwrites the old font data with the new bool renameFont(SharedBuffer* fontData, const String& fontName) diff --git a/WebCore/platform/graphics/opentype/OpenTypeUtilities.h b/WebCore/platform/graphics/opentype/OpenTypeUtilities.h index 4c75314..0ef1b2b 100644 --- a/WebCore/platform/graphics/opentype/OpenTypeUtilities.h +++ b/WebCore/platform/graphics/opentype/OpenTypeUtilities.h @@ -36,7 +36,7 @@ struct BigEndianUShort; struct EOTPrefix; class SharedBuffer; -#if PLATFORM(WINCE) +#if OS(WINCE) typedef unsigned __int8 UInt8; #endif diff --git a/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.cpp b/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.cpp new file mode 100644 index 0000000..3c7eaf2 --- /dev/null +++ b/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.cpp @@ -0,0 +1,431 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 "EGLDisplayOpenVG.h" + +#include "EGLUtils.h" +#include "IntSize.h" +#include "SurfaceOpenVG.h" + +#include <wtf/Assertions.h> +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +// Need to typedef this, otherwise DEFINE_STATIC_LOCAL() doesn't swallow it. +typedef HashMap<EGLDisplay, EGLDisplayOpenVG*> EGLDisplayManagerMap; + +// File-static variables. +static EGLDisplayManagerMap& displayManagers() +{ + DEFINE_STATIC_LOCAL(EGLDisplayManagerMap, managers, ()); + return managers; +} + +static EGLDisplayOpenVG* s_current = 0; + +// Static class members. + +SurfaceOpenVG* EGLDisplayOpenVG::currentSurface() +{ + EGLDisplayManagerMap& managers = displayManagers(); + EGLDisplay currentDisplay = eglGetCurrentDisplay(); + + if (currentDisplay == EGL_NO_DISPLAY || !managers.contains(currentDisplay)) + return 0; + + EGLDisplayOpenVG* displayManager = managers.get(currentDisplay); + EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW); + + if (currentSurface == EGL_NO_SURFACE || !displayManager->m_platformSurfaces.contains(currentSurface)) + return 0; + + return displayManager->m_platformSurfaces.get(currentSurface); +} + +void EGLDisplayOpenVG::registerPlatformSurface(SurfaceOpenVG* platformSurface) +{ + EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(platformSurface->eglDisplay()); + displayManager->m_platformSurfaces.set(platformSurface->eglSurface(), platformSurface); +} + +void EGLDisplayOpenVG::unregisterPlatformSurface(SurfaceOpenVG* platformSurface) +{ + EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(platformSurface->eglDisplay()); + displayManager->m_platformSurfaces.remove(platformSurface->eglSurface()); +} + +void EGLDisplayOpenVG::setCurrentDisplay(const EGLDisplay& display) +{ + s_current = EGLDisplayOpenVG::forDisplay(display); +} + +EGLDisplayOpenVG* EGLDisplayOpenVG::current() +{ + if (!s_current) { + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(display, 0, 0); + ASSERT_EGL_NO_ERROR(); + + s_current = EGLDisplayOpenVG::forDisplay(display); + } + return s_current; +} + +EGLDisplayOpenVG* EGLDisplayOpenVG::forDisplay(const EGLDisplay& display) +{ + EGLDisplayManagerMap& managers = displayManagers(); + + if (!managers.contains(display)) + managers.set(display, new EGLDisplayOpenVG(display)); + + return managers.get(display); +} + + +// Object/instance members. + +EGLDisplayOpenVG::EGLDisplayOpenVG(const EGLDisplay& display) + : m_display(display) + , m_sharedPlatformSurface(0) + , m_pbufferConfigId(0) + , m_windowConfigId(0) +{ + eglBindAPI(EGL_OPENVG_API); + ASSERT_EGL_NO_ERROR(); +} + +EGLDisplayOpenVG::~EGLDisplayOpenVG() +{ + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + ASSERT_EGL_NO_ERROR(); + + delete m_sharedPlatformSurface; + + HashMap<EGLSurface, EGLint>::const_iterator end = m_surfaceConfigIds.end(); + for (HashMap<EGLSurface, EGLint>::const_iterator it = m_surfaceConfigIds.begin(); it != end; ++it) + destroySurface((*it).first); + + eglTerminate(m_display); + ASSERT_EGL_NO_ERROR(); +} + +void EGLDisplayOpenVG::setDefaultPbufferConfig(const EGLConfig& config) +{ + EGLint configId; + EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId); + ASSERT(success == EGL_TRUE); + ASSERT(configId != EGL_BAD_ATTRIBUTE); + + m_pbufferConfigId = configId; +} + +EGLConfig EGLDisplayOpenVG::defaultPbufferConfig() +{ + EGLConfig config; + EGLint numConfigs; + + // Hopefully the client will have set the pbuffer config of its choice + // by now - if not, use a 32-bit generic one as default. + if (!m_pbufferConfigId) { + static const EGLint configAttribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_ALPHA_MASK_SIZE, 1, + EGL_LUMINANCE_SIZE, EGL_DONT_CARE, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, + EGL_NONE + }; + eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); + } else { + const EGLint configAttribs[] = { + EGL_CONFIG_ID, m_pbufferConfigId, + EGL_NONE + }; + eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); + } + + ASSERT_EGL_NO_ERROR(); + ASSERT(numConfigs == 1); + return config; +} + +void EGLDisplayOpenVG::setDefaultWindowConfig(const EGLConfig& config) +{ + EGLint configId; + EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId); + ASSERT(success == EGL_TRUE); + ASSERT(configId != EGL_BAD_ATTRIBUTE); + + m_windowConfigId = configId; +} + +EGLConfig EGLDisplayOpenVG::defaultWindowConfig() +{ + EGLConfig config; + EGLint numConfigs; + + // Hopefully the client will have set the window config of its choice + // by now - if not, use a 32-bit generic one as default. + if (!m_windowConfigId) { + static const EGLint configAttribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_ALPHA_MASK_SIZE, 1, + EGL_LUMINANCE_SIZE, EGL_DONT_CARE, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, + EGL_NONE + }; + eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); + } else { + const EGLint configAttribs[] = { + EGL_CONFIG_ID, m_windowConfigId, + EGL_NONE + }; + eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); + } + + ASSERT_EGL_NO_ERROR(); + ASSERT(numConfigs == 1); + return config; +} + +SurfaceOpenVG* EGLDisplayOpenVG::sharedPlatformSurface() +{ + if (!m_sharedPlatformSurface) { + // The shared surface doesn't need to be drawn on, it just exists so + // that we can always make the shared context current (which in turn is + // the owner of long-living resources such as images, paths and fonts). + // We'll just make the shared surface as small as possible: 1x1 pixel. + EGLConfig config = defaultPbufferConfig(); + EGLSurface surface = createPbufferSurface(IntSize(1, 1), config); + + EGLContext context = eglCreateContext(m_display, config, EGL_NO_CONTEXT, 0); + ASSERT_EGL_NO_ERROR(); + m_contexts.set(m_surfaceConfigIds.get(surface), context); + + m_sharedPlatformSurface = new SurfaceOpenVG; + m_sharedPlatformSurface->m_eglDisplay = m_display; + m_sharedPlatformSurface->m_eglSurface = surface; + m_sharedPlatformSurface->m_eglContext = context; + m_platformSurfaces.set(surface, m_sharedPlatformSurface); // a.k.a. registerPlatformSurface() + } + return m_sharedPlatformSurface; +} + +EGLSurface EGLDisplayOpenVG::createPbufferSurface(const IntSize& size, const EGLConfig& config, EGLint* errorCode) +{ + const EGLint attribList[] = { + EGL_WIDTH, size.width(), + EGL_HEIGHT, size.height(), + EGL_NONE + }; + EGLSurface surface = eglCreatePbufferSurface(m_display, config, attribList); + + if (errorCode) + *errorCode = eglGetError(); + else + ASSERT_EGL_NO_ERROR(); + + if (surface == EGL_NO_SURFACE) + return EGL_NO_SURFACE; + + EGLint surfaceConfigId; + EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId); + ASSERT(success == EGL_TRUE); + ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE); + + ASSERT(!m_surfaceConfigIds.contains(surface)); + m_surfaceConfigIds.set(surface, surfaceConfigId); + return surface; +} + +EGLSurface EGLDisplayOpenVG::surfaceForWindow(EGLNativeWindowType wId, const EGLConfig& config) +{ + if (m_windowSurfaces.contains(wId)) + return m_windowSurfaces.get(wId); + + EGLSurface surface = eglCreateWindowSurface(m_display, config, wId, 0); + ASSERT_EGL_NO_ERROR(); + + EGLint surfaceConfigId; + EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId); + ASSERT(success == EGL_TRUE); + ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE); + + ASSERT(!m_surfaceConfigIds.contains(surface)); + m_surfaceConfigIds.set(surface, surfaceConfigId); + return surface; +} + +bool EGLDisplayOpenVG::surfacesCompatible(const EGLSurface& surface, const EGLSurface& otherSurface) +{ + if (surface == EGL_NO_SURFACE || otherSurface == EGL_NO_SURFACE) + return false; + + // Currently, we assume that all surfaces known to this object are + // context-compatible to each other (which is reasonable to assume, + // otherwise eglCreateContext() would fail with EGL_BAD_MATCH for shared + // context compatibility anyways. + return m_surfaceConfigIds.contains(surface) && m_surfaceConfigIds.contains(otherSurface); +} + +void EGLDisplayOpenVG::destroySurface(const EGLSurface& surface) +{ + ASSERT(surface != EGL_NO_SURFACE); + + if (eglGetCurrentSurface(EGL_DRAW) == surface) { + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + ASSERT_EGL_NO_ERROR(); + } + + // Destroy the context associated to the surface, if we already created one. + if (m_surfaceConfigIds.contains(surface)) { + EGLint surfaceConfigId = m_surfaceConfigIds.take(surface); // take = get and remove + bool isContextReferenced = false; + + if (m_compatibleConfigIds.contains(surfaceConfigId)) + surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId); + + HashMap<EGLSurface, EGLint>::iterator end = m_surfaceConfigIds.end(); + + // ...but only if there's no other surfaces associated to that context. + for (HashMap<EGLSurface, EGLint>::iterator it = m_surfaceConfigIds.begin(); it != end; ++it) { + if ((*it).second == surfaceConfigId) { + isContextReferenced = true; + break; + } + } + if (!isContextReferenced && m_contexts.contains(surfaceConfigId)) { + EGLContext context = m_contexts.take(surfaceConfigId); + eglDestroyContext(m_display, context); + ASSERT_EGL_NO_ERROR(); + } + } + + m_platformSurfaces.remove(surface); + + HashMap<EGLNativeWindowType, EGLSurface>::iterator end = m_windowSurfaces.end(); + for (HashMap<EGLNativeWindowType, EGLSurface>::iterator it = m_windowSurfaces.begin(); it != end; ++it) { + if ((*it).second == surface) { + m_windowSurfaces.remove(it); + break; + } + } + + eglDestroySurface(m_display, surface); + ASSERT_EGL_NO_ERROR(); +} + +EGLContext EGLDisplayOpenVG::contextForSurface(const EGLSurface& surface) +{ + ASSERT(surface != EGL_NO_SURFACE); + + if (m_platformSurfaces.contains(surface)) + return m_platformSurfaces.get(surface)->eglContext(); + + eglBindAPI(EGL_OPENVG_API); + ASSERT_EGL_NO_ERROR(); + + EGLint surfaceConfigId; + + if (m_surfaceConfigIds.contains(surface)) + surfaceConfigId = m_surfaceConfigIds.get(surface); + else { + // Retrieve the same EGL config for context creation that was used to + // create the the EGL surface. + EGLBoolean success = eglQuerySurface(m_display, surface, EGL_CONFIG_ID, &surfaceConfigId); + ASSERT(success == EGL_TRUE); + ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE); + + m_surfaceConfigIds.set(surface, surfaceConfigId); + } + + if (m_compatibleConfigIds.contains(surfaceConfigId)) + surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId); + + if (m_contexts.contains(surfaceConfigId)) + return m_contexts.get(surfaceConfigId); + + if (!m_sharedPlatformSurface) // shared context has not been created yet + sharedPlatformSurface(); // creates the shared surface & context + + EGLDisplay currentDisplay = eglGetCurrentDisplay(); + EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ); + EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW); + EGLContext currentContext = eglGetCurrentContext(); + + // Before creating a new context, let's try whether an existing one + // is compatible with the surface. EGL doesn't give us a different way + // to check context/surface compatibility than trying it out, so let's + // do just that. + HashMap<EGLint, EGLContext>::iterator end = m_contexts.end(); + + for (HashMap<EGLint, EGLContext>::iterator it = m_contexts.begin(); it != end; ++it) { + eglMakeCurrent(m_display, surface, surface, (*it).second); + if (eglGetError() == EGL_SUCCESS) { + // Restore previous surface/context. + if (currentContext != EGL_NO_CONTEXT) { + eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext); + ASSERT_EGL_NO_ERROR(); + } + // Cool, surface is compatible to one of our existing contexts. + m_compatibleConfigIds.set(surfaceConfigId, (*it).first); + return (*it).second; + } + } + // Restore previous surface/context. + if (currentContext != EGL_NO_CONTEXT) { + eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext); + ASSERT_EGL_NO_ERROR(); + } + + EGLConfig config; + EGLint numConfigs; + + const EGLint configAttribs[] = { + EGL_CONFIG_ID, surfaceConfigId, + EGL_NONE + }; + + eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs); + ASSERT_EGL_NO_ERROR(); + ASSERT(numConfigs == 1); + + // We share all of the images and paths amongst the different contexts, + // so that they can be used in all of them. Resources that are created + // while m_sharedPlatformSurface->context() is current will be + // accessible from all other contexts, but are not restricted to the + // lifetime of those contexts. + EGLContext context = eglCreateContext(m_display, config, m_sharedPlatformSurface->eglContext(), 0); + ASSERT_EGL_NO_ERROR(); + + ASSERT(!m_contexts.contains(surfaceConfigId)); + m_contexts.set(surfaceConfigId, context); + return context; +} + +} diff --git a/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.h b/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.h new file mode 100644 index 0000000..fd8353d --- /dev/null +++ b/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 EGLDisplayOpenVG_h +#define EGLDisplayOpenVG_h + +#include <egl.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +class IntSize; +class SurfaceOpenVG; + +class EGLDisplayOpenVG { +public: + friend class SurfaceOpenVG; + + static SurfaceOpenVG* currentSurface(); + static void setCurrentDisplay(const EGLDisplay&); + static EGLDisplayOpenVG* current(); + static EGLDisplayOpenVG* forDisplay(const EGLDisplay&); + + void setDefaultPbufferConfig(const EGLConfig&); + EGLConfig defaultPbufferConfig(); + void setDefaultWindowConfig(const EGLConfig&); + EGLConfig defaultWindowConfig(); + + EGLDisplay display() const { return m_display; } + SurfaceOpenVG* sharedPlatformSurface(); + + /** Creates a pbuffer surface using the given config. If no surface + * could be created, EGL_NO_SURFACE is returned and errors can be + * checked with the value that is written to the errorCode parameter + * If no surface could be created and errorCode is zero, this method + * will trigger an assertion by itself. */ + EGLSurface createPbufferSurface(const IntSize&, const EGLConfig&, EGLint* errorCode = 0); + + EGLSurface surfaceForWindow(EGLNativeWindowType, const EGLConfig&); + + bool surfacesCompatible(const EGLSurface&, const EGLSurface&); + + /** Destroy the surface and its corresponding context (unless another + * surface is still using the same context, in which case the context + * is not destroyed). */ + void destroySurface(const EGLSurface&); + + /** Return the context corresponding to the surface. + * If no corresponding context exists, one is created automatically. */ + EGLContext contextForSurface(const EGLSurface&); + +private: + static void registerPlatformSurface(SurfaceOpenVG*); + static void unregisterPlatformSurface(SurfaceOpenVG*); + + EGLDisplayOpenVG(const EGLDisplay& display); + ~EGLDisplayOpenVG(); + + EGLDisplay m_display; + SurfaceOpenVG* m_sharedPlatformSurface; + EGLint m_pbufferConfigId; + EGLint m_windowConfigId; + + HashMap<EGLSurface, SurfaceOpenVG*> m_platformSurfaces; + HashMap<EGLNativeWindowType, EGLSurface> m_windowSurfaces; + HashMap<EGLSurface, EGLint> m_surfaceConfigIds; + HashMap<EGLint, EGLint> m_compatibleConfigIds; + HashMap<EGLint, EGLContext> m_contexts; +}; + +} + +#endif diff --git a/WebCore/platform/graphics/openvg/EGLUtils.h b/WebCore/platform/graphics/openvg/EGLUtils.h new file mode 100644 index 0000000..6f5d793 --- /dev/null +++ b/WebCore/platform/graphics/openvg/EGLUtils.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 EGLUtils_h +#define EGLUtils_h + +#include <egl.h> +#include <wtf/Assertions.h> + +static inline const char* toEGLErrorConstant(EGLint error) +{ + switch (error) { + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "UNKNOWN_ERROR"; + } +} + +#if ASSERT_DISABLED +#define ASSERT_EGL_NO_ERROR() ((void)0) +#else +#define ASSERT_EGL_NO_ERROR() do { \ + EGLint eglErrorCode = eglGetError(); \ + ASSERT_WITH_MESSAGE(eglErrorCode == EGL_SUCCESS, "Found %s", toEGLErrorConstant(eglErrorCode)); \ +} while (0) +#endif + +#endif diff --git a/WebCore/platform/graphics/openvg/GraphicsContextOpenVG.cpp b/WebCore/platform/graphics/openvg/GraphicsContextOpenVG.cpp new file mode 100644 index 0000000..5ed892c --- /dev/null +++ b/WebCore/platform/graphics/openvg/GraphicsContextOpenVG.cpp @@ -0,0 +1,566 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 "GraphicsContext.h" + +#include "GraphicsContextPrivate.h" +#include "NotImplemented.h" +#include "PainterOpenVG.h" +#include "SurfaceOpenVG.h" +#include "TransformationMatrix.h" + +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/UnusedParam.h> +#include <wtf/Vector.h> + +#if PLATFORM(EGL) +#include "EGLDisplayOpenVG.h" +#include "EGLUtils.h" +#include <egl.h> +#endif + +namespace WebCore { + +// typedef'ing doesn't work, let's inherit from PainterOpenVG instead +class GraphicsContextPlatformPrivate : public PainterOpenVG { +public: + GraphicsContextPlatformPrivate(SurfaceOpenVG* surface) + : PainterOpenVG(surface) + { + } +}; + +GraphicsContext::GraphicsContext(SurfaceOpenVG* surface) + : m_common(createGraphicsContextPrivate()) + , m_data(surface ? new GraphicsContextPlatformPrivate(surface) : 0) +{ + setPaintingDisabled(!surface); +} + +GraphicsContext::~GraphicsContext() +{ + destroyGraphicsContextPrivate(m_common); + delete m_data; +} + +PlatformGraphicsContext* GraphicsContext::platformContext() const +{ + if (paintingDisabled()) + return 0; + + return m_data->baseSurface(); +} + +TransformationMatrix GraphicsContext::getCTM() const +{ + if (paintingDisabled()) + return TransformationMatrix(); + + return m_data->transformationMatrix(); +} + +void GraphicsContext::savePlatformState() +{ + if (paintingDisabled()) + return; + + m_data->save(); +} + +void GraphicsContext::restorePlatformState() +{ + if (paintingDisabled()) + return; + + m_data->restore(); +} + +void GraphicsContext::drawRect(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + m_data->drawRect(rect); +} + +void GraphicsContext::drawLine(const IntPoint& from, const IntPoint& to) +{ + if (paintingDisabled()) + return; + + m_data->drawLine(from, to); +} + +/** + * Draw the largest ellipse that fits into the given rectangle. + */ +void GraphicsContext::drawEllipse(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + m_data->drawEllipse(rect); +} + +void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) +{ + if (paintingDisabled()) + return; + + m_data->drawArc(rect, startAngle, angleSpan, VG_STROKE_PATH); +} + +void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias) +{ + if (paintingDisabled()) + return; + + m_data->drawPolygon(numPoints, points); + + UNUSED_PARAM(shouldAntialias); // FIXME +} + +void GraphicsContext::fillPath() +{ + if (paintingDisabled()) + return; + + notImplemented(); +} + +void GraphicsContext::strokePath() +{ + if (paintingDisabled()) + return; + + notImplemented(); +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + m_data->drawRect(rect, VG_FILL_PATH); +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + Color oldColor = m_data->fillColor(); + m_data->setFillColor(color); + m_data->drawRect(rect, VG_FILL_PATH); + m_data->setFillColor(oldColor); + + UNUSED_PARAM(colorSpace); // FIXME +} + +void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + Color oldColor = m_data->fillColor(); + m_data->setFillColor(color); + m_data->drawRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, VG_FILL_PATH); + m_data->setFillColor(oldColor); + + UNUSED_PARAM(colorSpace); // FIXME +} + +void GraphicsContext::beginPath() +{ + if (paintingDisabled()) + return; + + notImplemented(); +} + +void GraphicsContext::addPath(const Path& path) +{ + if (paintingDisabled()) + return; + + notImplemented(); +} + +void GraphicsContext::clip(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + m_data->intersectClipRect(rect); +} + +void GraphicsContext::clipPath(WindRule clipRule) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(clipRule); +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) +{ + if (paintingDisabled()) + return; + + if (rects.isEmpty()) + return; + + // FIXME: We just unite all focus ring rects into one for now. + // We should outline the edge of the full region. + offset += (width - 1) / 2; + IntRect finalFocusRect; + + for (unsigned i = 0; i < rects.size(); i++) { + IntRect focusRect = rects[i]; + focusRect.inflate(offset); + finalFocusRect.unite(focusRect); + } + + StrokeStyle oldStyle = m_data->strokeStyle(); + Color oldStrokeColor = m_data->strokeColor(); + m_data->setStrokeStyle(DashedStroke); + m_data->setStrokeColor(color); + strokeRect(FloatRect(finalFocusRect), 1.f); + m_data->setStrokeStyle(oldStyle); + m_data->setStrokeColor(oldStrokeColor); +} + +void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing) +{ + if (paintingDisabled()) + return; + + if (width <= 0) + return; + + StrokeStyle oldStyle = m_data->strokeStyle(); + m_data->setStrokeStyle(SolidStroke); + drawLine(origin, origin + IntSize(width, 0)); + m_data->setStrokeStyle(oldStyle); + + UNUSED_PARAM(printing); +} + +void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, int width, bool grammar) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(origin); + UNUSED_PARAM(width); + UNUSED_PARAM(grammar); +} + +FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) +{ + if (paintingDisabled()) + return FloatRect(); + + return FloatRect(enclosingIntRect(m_data->transformationMatrix().mapRect(rect))); +} + +void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(size); + UNUSED_PARAM(blur); + UNUSED_PARAM(color); + UNUSED_PARAM(colorSpace); +} + +void GraphicsContext::clearPlatformShadow() +{ + if (paintingDisabled()) + return; + + notImplemented(); +} + +void GraphicsContext::beginTransparencyLayer(float opacity) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(opacity); +} + +void GraphicsContext::endTransparencyLayer() +{ + if (paintingDisabled()) + return; + + notImplemented(); +} + +void GraphicsContext::clearRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + CompositeOperator op = m_data->compositeOperation(); + m_data->setCompositeOperation(CompositeClear); + m_data->drawRect(rect, VG_FILL_PATH); + m_data->setCompositeOperation(op); +} + +void GraphicsContext::strokeRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + m_data->drawRect(rect, VG_STROKE_PATH); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) +{ + if (paintingDisabled()) + return; + + float oldThickness = m_data->strokeThickness(); + m_data->setStrokeThickness(lineWidth); + m_data->drawRect(rect, VG_STROKE_PATH); + m_data->setStrokeThickness(oldThickness); +} + +void GraphicsContext::setLineCap(LineCap lc) +{ + if (paintingDisabled()) + return; + + m_data->setLineCap(lc); +} + +void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) +{ + if (paintingDisabled()) + return; + + m_data->setLineDash(dashes, dashOffset); +} + +void GraphicsContext::setLineJoin(LineJoin lj) +{ + if (paintingDisabled()) + return; + + m_data->setLineJoin(lj); +} + +void GraphicsContext::setMiterLimit(float limit) +{ + if (paintingDisabled()) + return; + + m_data->setMiterLimit(limit); +} + +void GraphicsContext::setAlpha(float opacity) +{ + if (paintingDisabled()) + return; + + m_data->setOpacity(opacity); +} + +void GraphicsContext::setCompositeOperation(CompositeOperator op) +{ + if (paintingDisabled()) + return; + + m_data->setCompositeOperation(op); +} + +void GraphicsContext::clip(const Path& path) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(path); +} + +void GraphicsContext::canvasClip(const Path& path) +{ + clip(path); +} + +void GraphicsContext::clipOut(const Path& path) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(path); +} + +void GraphicsContext::scale(const FloatSize& scaleFactors) +{ + if (paintingDisabled()) + return; + + m_data->scale(scaleFactors); +} + +void GraphicsContext::rotate(float radians) +{ + if (paintingDisabled()) + return; + + m_data->rotate(radians); +} + +void GraphicsContext::translate(float dx, float dy) +{ + if (paintingDisabled()) + return; + + m_data->translate(dx, dy); +} + +IntPoint GraphicsContext::origin() +{ + if (paintingDisabled()) + return IntPoint(); + + TransformationMatrix matrix = m_data->transformationMatrix(); + return IntPoint(roundf(matrix.m41()), roundf(matrix.m42())); +} + +void GraphicsContext::clipOut(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(rect); +} + +void GraphicsContext::clipOutEllipseInRect(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(rect); +} + +void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer* imageBuffer) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(rect); + UNUSED_PARAM(imageBuffer); +} + +void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) +{ + if (paintingDisabled()) + return; + + notImplemented(); + UNUSED_PARAM(rect); + UNUSED_PARAM(thickness); +} + +void GraphicsContext::concatCTM(const TransformationMatrix& transform) +{ + if (paintingDisabled()) + return; + + m_data->concatTransformationMatrix(transform); +} + +void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) +{ + notImplemented(); + UNUSED_PARAM(link); + UNUSED_PARAM(destRect); +} + +void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + m_data->setStrokeColor(color); + + UNUSED_PARAM(colorSpace); // FIXME +} + +void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle) +{ + if (paintingDisabled()) + return; + + m_data->setStrokeStyle(strokeStyle); +} + +void GraphicsContext::setPlatformStrokeThickness(float thickness) +{ + if (paintingDisabled()) + return; + + m_data->setStrokeThickness(thickness); +} + +void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + m_data->setFillColor(color); + + UNUSED_PARAM(colorSpace); // FIXME +} + +void GraphicsContext::setPlatformShouldAntialias(bool enable) +{ + if (paintingDisabled()) + return; + + m_data->setAntialiasingEnabled(enable); +} + +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) +{ + notImplemented(); +} + +InterpolationQuality GraphicsContext::imageInterpolationQuality() const +{ + notImplemented(); + return InterpolationDefault; +} + +} diff --git a/WebCore/platform/graphics/openvg/PainterOpenVG.cpp b/WebCore/platform/graphics/openvg/PainterOpenVG.cpp new file mode 100644 index 0000000..3b7cf85 --- /dev/null +++ b/WebCore/platform/graphics/openvg/PainterOpenVG.cpp @@ -0,0 +1,957 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 "PainterOpenVG.h" + +#include "Color.h" +#include "DashArray.h" +#include "FloatPoint.h" +#include "FloatQuad.h" +#include "FloatRect.h" +#include "IntRect.h" +#include "IntSize.h" +#include "NotImplemented.h" +#include "SurfaceOpenVG.h" +#include "TransformationMatrix.h" +#include "VGUtils.h" + +#if PLATFORM(EGL) +#include "EGLUtils.h" +#endif + +#include <vgu.h> + +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> + +namespace WebCore { + +static bool isNonRotatedAffineTransformation(const TransformationMatrix& matrix) +{ + return matrix.m12() <= FLT_EPSILON && matrix.m13() <= FLT_EPSILON && matrix.m14() <= FLT_EPSILON + && matrix.m21() <= FLT_EPSILON && matrix.m23() <= FLT_EPSILON && matrix.m24() <= FLT_EPSILON + && matrix.m31() <= FLT_EPSILON && matrix.m32() <= FLT_EPSILON && matrix.m34() <= FLT_EPSILON + && matrix.m44() >= 1 - FLT_EPSILON; +} + +static VGCapStyle toVGCapStyle(LineCap lineCap) +{ + switch (lineCap) { + case RoundCap: + return VG_CAP_ROUND; + case SquareCap: + return VG_CAP_SQUARE; + case ButtCap: + default: + return VG_CAP_BUTT; + } +} + +static VGJoinStyle toVGJoinStyle(LineJoin lineJoin) +{ + switch (lineJoin) { + case RoundJoin: + return VG_JOIN_ROUND; + case BevelJoin: + return VG_JOIN_BEVEL; + case MiterJoin: + default: + return VG_JOIN_MITER; + } +} + +static VGFillRule toVGFillRule(WindRule fillRule) +{ + return fillRule == RULE_EVENODD ? VG_EVEN_ODD : VG_NON_ZERO; +} + +static VGuint colorToVGColor(const Color& color) +{ + VGuint vgColor = color.red(); + vgColor = (vgColor << 8) | color.green(); + vgColor = (vgColor << 8) | color.blue(); + vgColor = (vgColor << 8) | color.alpha(); + return vgColor; +} + +static void setVGSolidColor(VGPaintMode paintMode, const Color& color) +{ + VGPaint paint = vgCreatePaint(); + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetColor(paint, colorToVGColor(color)); + vgSetPaint(paint, paintMode); + vgDestroyPaint(paint); + ASSERT_VG_NO_ERROR(); +} + + +struct PlatformPainterState { + TransformationMatrix surfaceTransformationMatrix; + CompositeOperator compositeOperation; + float opacity; + + bool scissoringEnabled; + FloatRect scissorRect; + + Color fillColor; + StrokeStyle strokeStyle; + Color strokeColor; + float strokeThickness; + LineCap strokeLineCap; + LineJoin strokeLineJoin; + float strokeMiterLimit; + DashArray strokeDashArray; + float strokeDashOffset; + + bool antialiasingEnabled; + + PlatformPainterState() + : compositeOperation(CompositeSourceOver) + , opacity(1.0) + , scissoringEnabled(false) + , fillColor(Color::black) + , strokeStyle(NoStroke) + , strokeThickness(0.0) + , strokeLineCap(ButtCap) + , strokeLineJoin(MiterJoin) + , strokeMiterLimit(4.0) + , strokeDashOffset(0.0) + , antialiasingEnabled(true) + { + } + + PlatformPainterState(const PlatformPainterState& state) + { + surfaceTransformationMatrix = state.surfaceTransformationMatrix; + + scissoringEnabled = state.scissoringEnabled; + scissorRect = state.scissorRect; + copyPaintState(&state); + } + + void copyPaintState(const PlatformPainterState* other) + { + compositeOperation = other->compositeOperation; + opacity = other->opacity; + + fillColor = other->fillColor; + strokeStyle = other->strokeStyle; + strokeColor = other->strokeColor; + strokeThickness = other->strokeThickness; + strokeLineCap = other->strokeLineCap; + strokeLineJoin = other->strokeLineJoin; + strokeMiterLimit = other->strokeMiterLimit; + strokeDashArray = other->strokeDashArray; + strokeDashOffset = other->strokeDashOffset; + + antialiasingEnabled = other->antialiasingEnabled; + } + + void applyState(PainterOpenVG* painter) + { + ASSERT(painter); + + setVGSolidColor(VG_FILL_PATH, fillColor); + setVGSolidColor(VG_STROKE_PATH, strokeColor); + + vgSetf(VG_STROKE_LINE_WIDTH, strokeThickness); + vgSeti(VG_STROKE_CAP_STYLE, toVGCapStyle(strokeLineCap)); + vgSeti(VG_STROKE_JOIN_STYLE, toVGJoinStyle(strokeLineJoin)); + vgSetf(VG_STROKE_MITER_LIMIT, strokeMiterLimit); + + if (antialiasingEnabled) + vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_FASTER); + else + vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED); + + applyBlending(painter); + applyStrokeStyle(); + + applyTransformationMatrix(painter); + applyScissorRect(); + } + + void applyBlending(PainterOpenVG* painter) + { + VGBlendMode blendMode = VG_BLEND_SRC_OVER; + + switch (compositeOperation) { + case CompositeClear: { + // Clear means "set to fully transparent regardless of SRC". + // We implement that by multiplying DST with white color + // (= no changes) and an alpha of 1.0 - opacity, so the destination + // pixels will be fully transparent when opacity == 1.0 and + // unchanged when opacity == 0.0. + blendMode = VG_BLEND_DST_IN; + const VGfloat values[] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 - opacity }; + vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values); + vgSeti(VG_COLOR_TRANSFORM, VG_TRUE); + ASSERT_VG_NO_ERROR(); + break; + } + case CompositeCopy: + blendMode = VG_BLEND_SRC; + break; + case CompositeSourceOver: + blendMode = VG_BLEND_SRC_OVER; + break; + case CompositeSourceIn: + blendMode = VG_BLEND_SRC_IN; + break; + case CompositeSourceOut: + notImplemented(); + break; + case CompositeSourceAtop: + notImplemented(); + break; + case CompositeDestinationOver: + blendMode = VG_BLEND_DST_OVER; + break; + case CompositeDestinationIn: + blendMode = VG_BLEND_DST_IN; + break; + case CompositeDestinationOut: + notImplemented(); + break; + case CompositeDestinationAtop: + notImplemented(); + break; + case CompositeXOR: + notImplemented(); + break; + case CompositePlusDarker: + blendMode = VG_BLEND_DARKEN; + break; + case CompositeHighlight: + notImplemented(); + break; + case CompositePlusLighter: + blendMode = VG_BLEND_LIGHTEN; + break; + } + + if (compositeOperation != CompositeClear) { + if (opacity >= (1.0 - FLT_EPSILON)) + vgSeti(VG_COLOR_TRANSFORM, VG_FALSE); + else if (blendMode == VG_BLEND_SRC) { + blendMode = VG_BLEND_SRC_OVER; + VGfloat values[] = { 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, opacity }; + vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values); + vgSeti(VG_COLOR_TRANSFORM, VG_TRUE); + } else { + VGfloat values[] = { 1.0, 1.0, 1.0, opacity, 0.0, 0.0, 0.0, 0.0 }; + vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values); + vgSeti(VG_COLOR_TRANSFORM, VG_TRUE); + } + ASSERT_VG_NO_ERROR(); + } + + vgSeti(VG_BLEND_MODE, blendMode); + ASSERT_VG_NO_ERROR(); + } + + void applyTransformationMatrix(PainterOpenVG* painter) + { + // There are *five* separate transforms that can be applied to OpenVG as of 1.1 + // but it is not clear that we need to set them separately. Instead we set them + // all right here and let this be a call to essentially set the world transformation! + VGMatrix vgMatrix(surfaceTransformationMatrix); + const VGfloat* vgFloatArray = vgMatrix.toVGfloat(); + + vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + vgLoadMatrix(vgFloatArray); + ASSERT_VG_NO_ERROR(); + + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(vgFloatArray); + ASSERT_VG_NO_ERROR(); + +#ifdef OPENVG_VERSION_1_1 + vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE); + vgLoadMatrix(vgFloatArray); + ASSERT_VG_NO_ERROR(); +#endif + } + + void applyScissorRect() + { + if (scissoringEnabled) { + vgSeti(VG_SCISSORING, VG_TRUE); + vgSetfv(VG_SCISSOR_RECTS, 4, VGRect(scissorRect).toVGfloat()); + } else + vgSeti(VG_SCISSORING, VG_FALSE); + + ASSERT_VG_NO_ERROR(); + } + + void applyStrokeStyle() + { + if (strokeStyle == DottedStroke) { + VGfloat vgFloatArray[2] = { 1.0, 1.0 }; + vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray); + vgSetf(VG_STROKE_DASH_PHASE, 0.0); + } else if (strokeStyle == DashedStroke) { + if (!strokeDashArray.size()) { + VGfloat vgFloatArray[2] = { 4.0, 3.0 }; + vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray); + } else { + Vector<VGfloat> vgFloatArray(strokeDashArray.size()); + for (int i = 0; i < strokeDashArray.size(); ++i) + vgFloatArray[i] = strokeDashArray[i]; + + vgSetfv(VG_STROKE_DASH_PATTERN, vgFloatArray.size(), vgFloatArray.data()); + } + vgSetf(VG_STROKE_DASH_PHASE, strokeDashOffset); + } else { + vgSetfv(VG_STROKE_DASH_PATTERN, 0, 0); + vgSetf(VG_STROKE_DASH_PHASE, 0.0); + } + + ASSERT_VG_NO_ERROR(); + } + + inline bool strokeDisabled() const + { + return (compositeOperation == CompositeSourceOver + && (strokeStyle == NoStroke || !strokeColor.alpha())); + } + + inline bool fillDisabled() const + { + return (compositeOperation == CompositeSourceOver && !fillColor.alpha()); + } +}; + + +PainterOpenVG::PainterOpenVG() + : m_state(0) + , m_surface(0) +{ +} + +PainterOpenVG::PainterOpenVG(SurfaceOpenVG* surface) + : m_state(0) + , m_surface(0) +{ + ASSERT(surface); + begin(surface); +} + +PainterOpenVG::~PainterOpenVG() +{ + end(); +} + +void PainterOpenVG::begin(SurfaceOpenVG* surface) +{ + if (surface == m_surface) + return; + + ASSERT(surface); + ASSERT(!m_state); + + m_surface = surface; + + m_stateStack.append(new PlatformPainterState()); + m_state = m_stateStack.last(); + + m_surface->setActivePainter(this); + m_surface->makeCurrent(); +} + +void PainterOpenVG::end() +{ + if (!m_surface) + return; + + m_surface->setActivePainter(0); + m_surface = 0; + + destroyPainterStates(); +} + +void PainterOpenVG::destroyPainterStates() +{ + PlatformPainterState* state = 0; + while (!m_stateStack.isEmpty()) { + state = m_stateStack.last(); + m_stateStack.removeLast(); + delete state; + } + m_state = 0; +} + +// Called by friend SurfaceOpenVG, private otherwise. +void PainterOpenVG::applyState() +{ + ASSERT(m_state); + m_state->applyState(this); +} + +/** + * Copy the current back buffer image onto the surface. + * + * Call this method when all painting operations have been completed, + * otherwise the surface won't visibly change. + */ +void PainterOpenVG::blitToSurface() +{ + ASSERT(m_state); // implies m_surface + m_surface->flush(); +} + +TransformationMatrix PainterOpenVG::transformationMatrix() const +{ + ASSERT(m_state); + return m_state->surfaceTransformationMatrix; +} + +void PainterOpenVG::concatTransformationMatrix(const TransformationMatrix& matrix) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + // We do the multiplication ourself using WebCore's TransformationMatrix rather than + // offloading this to VG via vgMultMatrix to keep things simple and so we can maintain + // state ourselves. + m_state->surfaceTransformationMatrix.multLeft(matrix); + m_state->applyTransformationMatrix(this); +} + +void PainterOpenVG::setTransformationMatrix(const TransformationMatrix& matrix) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->surfaceTransformationMatrix = matrix; + m_state->applyTransformationMatrix(this); +} + +CompositeOperator PainterOpenVG::compositeOperation() const +{ + ASSERT(m_state); + return m_state->compositeOperation; +} + +void PainterOpenVG::setCompositeOperation(CompositeOperator op) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->compositeOperation = op; + m_state->applyBlending(this); +} + +float PainterOpenVG::opacity() const +{ + ASSERT(m_state); + return m_state->opacity; +} + +void PainterOpenVG::setOpacity(float opacity) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->opacity = opacity; + m_state->applyBlending(this); +} + +float PainterOpenVG::strokeThickness() const +{ + ASSERT(m_state); + return m_state->strokeThickness; +} + +void PainterOpenVG::setStrokeThickness(float thickness) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->strokeThickness = thickness; + vgSetf(VG_STROKE_LINE_WIDTH, thickness); + ASSERT_VG_NO_ERROR(); +} + +StrokeStyle PainterOpenVG::strokeStyle() const +{ + ASSERT(m_state); + return m_state->strokeStyle; +} + +void PainterOpenVG::setStrokeStyle(const StrokeStyle& style) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->strokeStyle = style; + m_state->applyStrokeStyle(); +} + +void PainterOpenVG::setLineDash(const DashArray& dashArray, float dashOffset) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->strokeDashArray = dashArray; + m_state->strokeDashOffset = dashOffset; + m_state->applyStrokeStyle(); +} + +void PainterOpenVG::setLineCap(LineCap lineCap) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->strokeLineCap = lineCap; + vgSeti(VG_STROKE_CAP_STYLE, toVGCapStyle(lineCap)); + ASSERT_VG_NO_ERROR(); +} + +void PainterOpenVG::setLineJoin(LineJoin lineJoin) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->strokeLineJoin = lineJoin; + vgSeti(VG_STROKE_JOIN_STYLE, toVGJoinStyle(lineJoin)); + ASSERT_VG_NO_ERROR(); +} + +void PainterOpenVG::setMiterLimit(float miterLimit) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->strokeMiterLimit = miterLimit; + vgSetf(VG_STROKE_MITER_LIMIT, miterLimit); + ASSERT_VG_NO_ERROR(); +} + +Color PainterOpenVG::strokeColor() const +{ + ASSERT(m_state); + return m_state->strokeColor; +} + +void PainterOpenVG::setStrokeColor(const Color& color) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->strokeColor = color; + setVGSolidColor(VG_STROKE_PATH, color); +} + +Color PainterOpenVG::fillColor() const +{ + ASSERT(m_state); + return m_state->fillColor; +} + +void PainterOpenVG::setFillColor(const Color& color) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->fillColor = color; + setVGSolidColor(VG_FILL_PATH, color); +} + +bool PainterOpenVG::antialiasingEnabled() const +{ + ASSERT(m_state); + return m_state->antialiasingEnabled; +} + +void PainterOpenVG::setAntialiasingEnabled(bool enabled) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + m_state->antialiasingEnabled = enabled; + + if (enabled) + vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_FASTER); + else + vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED); +} + +void PainterOpenVG::scale(const FloatSize& scaleFactors) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + TransformationMatrix matrix = m_state->surfaceTransformationMatrix; + matrix.scaleNonUniform(scaleFactors.width(), scaleFactors.height()); + setTransformationMatrix(matrix); +} + +void PainterOpenVG::rotate(float radians) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + TransformationMatrix matrix = m_state->surfaceTransformationMatrix; + matrix.rotate(rad2deg(radians)); + setTransformationMatrix(matrix); +} + +void PainterOpenVG::translate(float dx, float dy) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + TransformationMatrix matrix = m_state->surfaceTransformationMatrix; + matrix.translate(dx, dy); + setTransformationMatrix(matrix); +} + +void PainterOpenVG::intersectScissorRect(const FloatRect& rect) +{ + // Scissor rectangles are defined by float values, but e.g. painting + // something red to a float-clipped rectangle and then painting something + // white to the same rectangle will leave some red remnants as it is + // rendered to full pixels in between. Also, some OpenVG implementations + // are likely to clip to integer coordinates anyways because of the above + // effect. So considering the above (and confirming through tests) the + // visual result is better if we clip to the enclosing integer rectangle + // rather than the exact float rectangle for scissoring. + if (m_state->scissoringEnabled) + m_state->scissorRect.intersect(FloatRect(enclosingIntRect(rect))); + else { + m_state->scissoringEnabled = true; + m_state->scissorRect = FloatRect(enclosingIntRect(rect)); + } + + m_state->applyScissorRect(); +} + +void PainterOpenVG::intersectClipRect(const FloatRect& rect) +{ + ASSERT(m_state); + m_surface->makeCurrent(); + + if (m_state->surfaceTransformationMatrix.isIdentity()) { + // No transformation required, skip all the complex stuff. + intersectScissorRect(rect); + return; + } + + // Check if the actual destination rectangle is still rectilinear (can be + // represented as FloatRect) so we could apply scissoring instead of + // (potentially more expensive) path clipping. Note that scissoring is not + // subject to transformations, so we need to do the transformation to + // surface coordinates by ourselves. + FloatQuad effectiveScissorQuad = + m_state->surfaceTransformationMatrix.mapQuad(FloatQuad(rect)); + + if (effectiveScissorQuad.isRectilinear()) + intersectScissorRect(effectiveScissorQuad.boundingBox()); + else { + // The transformed scissorRect cannot be represented as FloatRect + // anymore, so we need to perform masking instead. Not yet implemented. + notImplemented(); + } +} + +void PainterOpenVG::drawRect(const FloatRect& rect, VGbitfield specifiedPaintModes) +{ + ASSERT(m_state); + + VGbitfield paintModes = 0; + if (!m_state->strokeDisabled()) + paintModes |= VG_STROKE_PATH; + if (!m_state->fillDisabled()) + paintModes |= VG_FILL_PATH; + + paintModes &= specifiedPaintModes; + + if (!paintModes) + return; + + m_surface->makeCurrent(); + + VGPath path = vgCreatePath( + VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1.0 /* scale */, 0.0 /* bias */, + 5 /* expected number of segments */, + 5 /* expected number of total coordinates */, + VG_PATH_CAPABILITY_APPEND_TO); + ASSERT_VG_NO_ERROR(); + + if (vguRect(path, rect.x(), rect.y(), rect.width(), rect.height()) == VGU_NO_ERROR) { + vgDrawPath(path, paintModes); + ASSERT_VG_NO_ERROR(); + } + + vgDestroyPath(path); + ASSERT_VG_NO_ERROR(); +} + +void PainterOpenVG::drawRoundedRect(const FloatRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, VGbitfield specifiedPaintModes) +{ + ASSERT(m_state); + + VGbitfield paintModes = 0; + if (!m_state->strokeDisabled()) + paintModes |= VG_STROKE_PATH; + if (!m_state->fillDisabled()) + paintModes |= VG_FILL_PATH; + + paintModes &= specifiedPaintModes; + + if (!paintModes) + return; + + m_surface->makeCurrent(); + + VGPath path = vgCreatePath( + VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1.0 /* scale */, 0.0 /* bias */, + 10 /* expected number of segments */, + 25 /* expected number of total coordinates */, + VG_PATH_CAPABILITY_APPEND_TO); + ASSERT_VG_NO_ERROR(); + + // clamp corner arc sizes + FloatSize clampedTopLeft = FloatSize(topLeft).shrunkTo(rect.size()).expandedTo(FloatSize()); + FloatSize clampedTopRight = FloatSize(topRight).shrunkTo(rect.size()).expandedTo(FloatSize()); + FloatSize clampedBottomLeft = FloatSize(bottomLeft).shrunkTo(rect.size()).expandedTo(FloatSize()); + FloatSize clampedBottomRight = FloatSize(bottomRight).shrunkTo(rect.size()).expandedTo(FloatSize()); + + // As OpenVG's coordinate system is flipped in comparison to WebKit's, + // we have to specify the opposite value for the "clockwise" value. + static const VGubyte pathSegments[] = { + VG_MOVE_TO_ABS, + VG_HLINE_TO_REL, + VG_SCCWARC_TO_REL, + VG_VLINE_TO_REL, + VG_SCCWARC_TO_REL, + VG_HLINE_TO_REL, + VG_SCCWARC_TO_REL, + VG_VLINE_TO_REL, + VG_SCCWARC_TO_REL, + VG_CLOSE_PATH + }; + // Also, the rounded rectangle path proceeds from the top to the bottom, + // requiring height distances and clamped radius sizes to be flipped. + const VGfloat pathData[] = { + rect.x() + clampedTopLeft.width(), rect.y(), + rect.width() - clampedTopLeft.width() - clampedTopRight.width(), + clampedTopRight.width(), clampedTopRight.height(), 0, clampedTopRight.width(), clampedTopRight.height(), + rect.height() - clampedTopRight.height() - clampedBottomRight.height(), + clampedBottomRight.width(), clampedBottomRight.height(), 0, -clampedBottomRight.width(), clampedBottomRight.height(), + -(rect.width() - clampedBottomLeft.width() - clampedBottomRight.width()), + clampedBottomLeft.width(), clampedBottomLeft.height(), 0, -clampedBottomLeft.width(), -clampedBottomLeft.height(), + -(rect.height() - clampedTopLeft.height() - clampedBottomLeft.height()), + clampedTopLeft.width(), clampedTopLeft.height(), 0, clampedTopLeft.width(), -clampedTopLeft.height(), + }; + + vgAppendPathData(path, 10, pathSegments, pathData); + vgDrawPath(path, paintModes); + vgDestroyPath(path); + ASSERT_VG_NO_ERROR(); +} + +void PainterOpenVG::drawLine(const IntPoint& from, const IntPoint& to) +{ + ASSERT(m_state); + + if (m_state->strokeDisabled()) + return; + + m_surface->makeCurrent(); + + VGPath path = vgCreatePath( + VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1.0 /* scale */, 0.0 /* bias */, + 2 /* expected number of segments */, + 4 /* expected number of total coordinates */, + VG_PATH_CAPABILITY_APPEND_TO); + ASSERT_VG_NO_ERROR(); + + VGUErrorCode errorCode; + + // Try to align lines to pixels, centering them between pixels for odd thickness values. + if (fmod(m_state->strokeThickness + 0.5, 2.0) < 1.0) + errorCode = vguLine(path, from.x(), from.y(), to.x(), to.y()); + else if ((to.y() - from.y()) > (to.x() - from.x())) // more vertical than horizontal + errorCode = vguLine(path, from.x() + 0.5, from.y(), to.x() + 0.5, to.y()); + else + errorCode = vguLine(path, from.x(), from.y() + 0.5, to.x(), to.y() + 0.5); + + if (errorCode == VGU_NO_ERROR) { + vgDrawPath(path, VG_STROKE_PATH); + ASSERT_VG_NO_ERROR(); + } + + vgDestroyPath(path); + ASSERT_VG_NO_ERROR(); +} + +void PainterOpenVG::drawArc(const IntRect& rect, int startAngle, int angleSpan, VGbitfield specifiedPaintModes) +{ + ASSERT(m_state); + + VGbitfield paintModes = 0; + if (!m_state->strokeDisabled()) + paintModes |= VG_STROKE_PATH; + if (!m_state->fillDisabled()) + paintModes |= VG_FILL_PATH; + + paintModes &= specifiedPaintModes; + + if (!paintModes) + return; + + m_surface->makeCurrent(); + + VGPath path = vgCreatePath( + VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1.0 /* scale */, 0.0 /* bias */, + 2 /* expected number of segments */, + 4 /* expected number of total coordinates */, + VG_PATH_CAPABILITY_APPEND_TO); + ASSERT_VG_NO_ERROR(); + + if (vguArc(path, rect.x() + rect.width() / 2.0, rect.y() + rect.height() / 2.0, rect.width(), rect.height(), -startAngle, -angleSpan, VGU_ARC_OPEN) == VGU_NO_ERROR) { + vgDrawPath(path, VG_STROKE_PATH); + ASSERT_VG_NO_ERROR(); + } + + vgDestroyPath(path); + ASSERT_VG_NO_ERROR(); +} + +void PainterOpenVG::drawEllipse(const IntRect& rect, VGbitfield specifiedPaintModes) +{ + ASSERT(m_state); + + VGbitfield paintModes = 0; + if (!m_state->strokeDisabled()) + paintModes |= VG_STROKE_PATH; + if (!m_state->fillDisabled()) + paintModes |= VG_FILL_PATH; + + paintModes &= specifiedPaintModes; + + if (!paintModes) + return; + + m_surface->makeCurrent(); + + VGPath path = vgCreatePath( + VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1.0 /* scale */, 0.0 /* bias */, + 4 /* expected number of segments */, + 12 /* expected number of total coordinates */, + VG_PATH_CAPABILITY_APPEND_TO); + ASSERT_VG_NO_ERROR(); + + if (vguEllipse(path, rect.x() + rect.width() / 2.0, rect.y() + rect.height() / 2.0, rect.width(), rect.height()) == VGU_NO_ERROR) { + vgDrawPath(path, paintModes); + ASSERT_VG_NO_ERROR(); + } + + vgDestroyPath(path); + ASSERT_VG_NO_ERROR(); +} + +void PainterOpenVG::drawPolygon(size_t numPoints, const FloatPoint* points, VGbitfield specifiedPaintModes) +{ + ASSERT(m_state); + + VGbitfield paintModes = 0; + if (!m_state->strokeDisabled()) + paintModes |= VG_STROKE_PATH; + if (!m_state->fillDisabled()) + paintModes |= VG_FILL_PATH; + + paintModes &= specifiedPaintModes; + + if (!paintModes) + return; + + m_surface->makeCurrent(); + + // Path segments: all points + "close path". + const VGint numSegments = numPoints + 1; + const VGint numCoordinates = numPoints * 2; + + VGPath path = vgCreatePath( + VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1.0 /* scale */, 0.0 /* bias */, + numSegments /* expected number of segments */, + numCoordinates /* expected number of total coordinates */, + VG_PATH_CAPABILITY_APPEND_TO); + ASSERT_VG_NO_ERROR(); + + Vector<VGfloat> vgPoints(numCoordinates); + for (int i = 0; i < numPoints; ++i) { + vgPoints[i*2] = points[i].x(); + vgPoints[i*2 + 1] = points[i].y(); + } + + if (vguPolygon(path, vgPoints.data(), numPoints, VG_TRUE /* closed */) == VGU_NO_ERROR) { + vgDrawPath(path, paintModes); + ASSERT_VG_NO_ERROR(); + } + + vgDestroyPath(path); + ASSERT_VG_NO_ERROR(); +} + +void PainterOpenVG::save(PainterOpenVG::SaveMode saveMode) +{ + ASSERT(m_state); + + // If the underlying context/surface was switched away by someone without + // telling us, it might not correspond to the one assigned to this painter. + // Switch back so we can save the state properly. (Should happen rarely.) + // Use DontSaveOrApplyPainterState mode in order to avoid recursion. + m_surface->makeCurrent(SurfaceOpenVG::DontSaveOrApplyPainterState); + + if (saveMode == PainterOpenVG::CreateNewState) { + PlatformPainterState* state = new PlatformPainterState(*m_state); + m_stateStack.append(state); + m_state = m_stateStack.last(); + } else { // if (saveMode == PainterOpenVG::CreateNewStateWithPaintStateOnly) { + PlatformPainterState* state = new PlatformPainterState(); + state->copyPaintState(m_state); + m_stateStack.append(state); + m_state = m_stateStack.last(); + } +} + +void PainterOpenVG::restore() +{ + ASSERT(m_stateStack.size() >= 2); + m_surface->makeCurrent(SurfaceOpenVG::DontApplyPainterState); + + PlatformPainterState* state = m_stateStack.last(); + m_stateStack.removeLast(); + delete state; + + m_state = m_stateStack.last(); + m_state->applyState(this); +} + +} diff --git a/WebCore/platform/graphics/openvg/PainterOpenVG.h b/WebCore/platform/graphics/openvg/PainterOpenVG.h new file mode 100644 index 0000000..6936eee --- /dev/null +++ b/WebCore/platform/graphics/openvg/PainterOpenVG.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 PainterOpenVG_h +#define PainterOpenVG_h + +#include "Color.h" +#include "GraphicsContext.h" + +#include <openvg.h> + +#include <wtf/Noncopyable.h> +#include <wtf/Platform.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class FloatPoint; +class FloatRect; +class IntRect; +class IntSize; +class SurfaceOpenVG; +class TransformationMatrix; + +struct PlatformPainterState; + +class PainterOpenVG : public Noncopyable { +public: + friend class SurfaceOpenVG; + friend struct PlatformPainterState; + + enum SaveMode { + CreateNewState, + CreateNewStateWithPaintStateOnly // internal usage only, do not use outside PainterOpenVG + }; + + PainterOpenVG(); + PainterOpenVG(SurfaceOpenVG*); + ~PainterOpenVG(); + + void begin(SurfaceOpenVG*); + void end(); + + TransformationMatrix transformationMatrix() const; + void setTransformationMatrix(const TransformationMatrix&); + void concatTransformationMatrix(const TransformationMatrix&); + + CompositeOperator compositeOperation() const; + void setCompositeOperation(CompositeOperator); + float opacity() const; + void setOpacity(float); + + float strokeThickness() const; + void setStrokeThickness(float); + StrokeStyle strokeStyle() const; + void setStrokeStyle(const StrokeStyle&); + + void setLineDash(const DashArray&, float dashOffset); + void setLineCap(LineCap); + void setLineJoin(LineJoin); + void setMiterLimit(float); + + Color strokeColor() const; + void setStrokeColor(const Color&); + + Color fillColor() const; + void setFillColor(const Color&); + + bool antialiasingEnabled() const; + void setAntialiasingEnabled(bool); + + void drawRect(const FloatRect&, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH)); + void drawRoundedRect(const FloatRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH)); + void drawLine(const IntPoint& from, const IntPoint& to); + void drawArc(const IntRect& ellipseBounds, int startAngle, int angleSpan, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH)); + void drawEllipse(const IntRect& bounds, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH)); + void drawPolygon(size_t numPoints, const FloatPoint* points, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH)); + + void scale(const FloatSize& scaleFactors); + void rotate(float radians); + void translate(float dx, float dy); + + void intersectClipRect(const FloatRect&); + + void save(PainterOpenVG::SaveMode saveMode = CreateNewState); + void restore(); + + SurfaceOpenVG* surface() { return m_surface; } + void blitToSurface(); + +private: + void destroyPainterStates(); + void applyState(); + + void intersectScissorRect(const FloatRect&); + +private: + Vector<PlatformPainterState*> m_stateStack; + PlatformPainterState* m_state; + SurfaceOpenVG* m_surface; +}; + +} + +#endif diff --git a/WebCore/platform/graphics/openvg/SurfaceOpenVG.cpp b/WebCore/platform/graphics/openvg/SurfaceOpenVG.cpp new file mode 100644 index 0000000..9539f2c --- /dev/null +++ b/WebCore/platform/graphics/openvg/SurfaceOpenVG.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 "SurfaceOpenVG.h" + +#include "IntSize.h" +#include "PainterOpenVG.h" + +#if PLATFORM(EGL) +#include "EGLDisplayOpenVG.h" +#include "EGLUtils.h" +#endif + +#include <wtf/Assertions.h> + +namespace WebCore { + +PainterOpenVG* SurfaceOpenVG::s_currentPainter = 0; + +SurfaceOpenVG* SurfaceOpenVG::currentSurface() +{ +#if PLATFORM(EGL) + return EGLDisplayOpenVG::currentSurface(); +#else + ASSERT_NOT_REACHED(); + return 0; +#endif +} + +#if PLATFORM(EGL) +SurfaceOpenVG::SurfaceOpenVG(const IntSize& size, const EGLDisplay& display, EGLConfig* confPtr, EGLint* errorCode) + : m_activePainter(0) + , m_eglDisplay(display) + , m_eglSurface(EGL_NO_SURFACE) + , m_eglContext(EGL_NO_CONTEXT) +{ + ASSERT(m_eglDisplay != EGL_NO_DISPLAY); + + EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(m_eglDisplay); + EGLConfig config = confPtr ? (*confPtr) : displayManager->defaultPbufferConfig(); + m_eglSurface = displayManager->createPbufferSurface(size, config, errorCode); + + if (m_eglSurface == EGL_NO_SURFACE) + return; + + m_eglContext = displayManager->contextForSurface(m_eglSurface); + EGLDisplayOpenVG::registerPlatformSurface(this); +} + +SurfaceOpenVG::SurfaceOpenVG(EGLNativeWindowType window, const EGLDisplay& display, EGLConfig* confPtr) + : m_activePainter(0) + , m_eglDisplay(display) + , m_eglSurface(EGL_NO_SURFACE) + , m_eglContext(EGL_NO_CONTEXT) +{ + ASSERT(m_eglDisplay != EGL_NO_DISPLAY); + + EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(m_eglDisplay); + EGLConfig config = confPtr ? (*confPtr) : displayManager->defaultWindowConfig(); + m_eglSurface = displayManager->surfaceForWindow(window, config); + ASSERT(m_eglSurface != EGL_NO_SURFACE); + + m_eglContext = displayManager->contextForSurface(m_eglSurface); + EGLDisplayOpenVG::registerPlatformSurface(this); +} + +// Constructor only accessible to EGLDisplayOpenVG for shared context +// initialization. The parameter types might define to void* like in the +// window surface constructor, so it can't be overloaded with all the required +// arguments and EGLDisplayOpenVG basically implements the constructor +// by itself. +SurfaceOpenVG::SurfaceOpenVG() + : m_activePainter(0) + , m_eglDisplay(EGL_NO_DISPLAY) + , m_eglSurface(EGL_NO_SURFACE) + , m_eglContext(EGL_NO_CONTEXT) +{ +} +#endif + +SurfaceOpenVG::~SurfaceOpenVG() +{ + if (!isValid()) + return; + + if (m_activePainter && this == m_activePainter->baseSurface()) + m_activePainter->end(); + +#if PLATFORM(EGL) + EGLDisplayOpenVG::forDisplay(m_eglDisplay)->destroySurface(m_eglSurface); + EGLDisplayOpenVG::unregisterPlatformSurface(this); +#else + ASSERT_NOT_REACHED(); +#endif +} + +bool SurfaceOpenVG::isValid() const +{ +#if PLATFORM(EGL) + return (m_eglSurface != EGL_NO_SURFACE); +#else + ASSERT_NOT_REACHED(); + return false; +#endif +} + +int SurfaceOpenVG::width() const +{ +#if PLATFORM(EGL) + ASSERT(m_eglSurface != EGL_NO_SURFACE); + + EGLint width; + eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &width); + ASSERT_EGL_NO_ERROR(); + return width; +#else + ASSERT_NOT_REACHED(); + return 0; +#endif +} + +int SurfaceOpenVG::height() const +{ +#if PLATFORM(EGL) + ASSERT(m_eglSurface != EGL_NO_SURFACE); + + EGLint height; + eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &height); + ASSERT_EGL_NO_ERROR(); + return height; +#else + ASSERT_NOT_REACHED(); + return 0; +#endif +} + +SurfaceOpenVG* SurfaceOpenVG::sharedSurface() const +{ +#if PLATFORM(EGL) + ASSERT(m_eglSurface != EGL_NO_SURFACE); + return EGLDisplayOpenVG::forDisplay(m_eglDisplay)->sharedPlatformSurface(); +#else + ASSERT_NOT_REACHED(); + return 0; +#endif +} + +void SurfaceOpenVG::makeCurrent(MakeCurrentMode mode) +{ +#if PLATFORM(EGL) + ASSERT(m_eglSurface != EGL_NO_SURFACE); + + eglBindAPI(EGL_OPENVG_API); + ASSERT_EGL_NO_ERROR(); + EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW); + ASSERT_EGL_NO_ERROR(); + + if (currentSurface != m_eglSurface) { + eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext); + ASSERT_EGL_NO_ERROR(); + s_currentPainter = 0; + } +#endif + + if (m_activePainter && mode == ApplyPainterStateOnSurfaceSwitch + && s_currentPainter != m_activePainter) { + m_activePainter->applyState(); + s_currentPainter = m_activePainter; + } +} + +void SurfaceOpenVG::makeCompatibleCurrent() +{ +#if PLATFORM(EGL) + ASSERT(m_eglSurface != EGL_NO_SURFACE); + + eglBindAPI(EGL_OPENVG_API); + ASSERT_EGL_NO_ERROR(); + EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW); + ASSERT_EGL_NO_ERROR(); + + if (currentSurface == m_eglSurface) { + if (m_activePainter && s_currentPainter != m_activePainter) { + m_activePainter->applyState(); + s_currentPainter = m_activePainter; + } + } else if (!EGLDisplayOpenVG::forDisplay(m_eglDisplay)->surfacesCompatible(currentSurface, m_eglSurface)) { + eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext); + ASSERT_EGL_NO_ERROR(); + s_currentPainter = 0; + } + // else: surfaces compatible, no need to switch contexts +#endif +} + +void SurfaceOpenVG::flush() +{ +#if PLATFORM(EGL) + ASSERT(m_eglSurface != EGL_NO_SURFACE); + + eglSwapBuffers(m_eglDisplay, m_eglSurface); + ASSERT_EGL_NO_ERROR(); +#endif +} + +void SurfaceOpenVG::setActivePainter(PainterOpenVG* painter) +{ + ASSERT(isValid()); + + // If painter is non-zero, we want to make sure there was no previous painter set. + ASSERT(!painter || !m_activePainter); + + // Make sure a disabled painter isn't marked as global current painter anymore. + if (!painter && s_currentPainter == m_activePainter) + s_currentPainter = 0; + + m_activePainter = painter; +} + +PainterOpenVG* SurfaceOpenVG::activePainter() +{ + ASSERT(isValid()); + return m_activePainter; +} + +} diff --git a/WebCore/platform/graphics/openvg/SurfaceOpenVG.h b/WebCore/platform/graphics/openvg/SurfaceOpenVG.h new file mode 100644 index 0000000..dc288dd --- /dev/null +++ b/WebCore/platform/graphics/openvg/SurfaceOpenVG.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 SurfaceOpenVG_h +#define SurfaceOpenVG_h + +#if PLATFORM(EGL) +#include <egl.h> +#endif + +#include <wtf/Noncopyable.h> +#include <wtf/Platform.h> + +namespace WebCore { + +#if PLATFORM(EGL) +class EGLDisplayOpenVG; +#endif +class PainterOpenVG; +class IntSize; + +/** + * SurfaceOpenVG provides the functionality of surfaces and contexts that are + * underlying the OpenVG implementation. In the vast majority of cases, that + * underlying technology is EGL, but OpenVG doesn't depend on EGL per se. + * Wrapping surface/context functionality into a separate class avoids lots + * of #ifdefs and should make it easy to add different surface/context + * implementations than EGL. + */ +class SurfaceOpenVG : public Noncopyable { +public: + enum MakeCurrentMode { + ApplyPainterStateOnSurfaceSwitch, + DontApplyPainterState, + }; + + static SurfaceOpenVG* currentSurface(); + +#if PLATFORM(EGL) + friend class EGLDisplayOpenVG; + + /** + * Create a new EGL pbuffer surface with the specified size and config on + * the given display. If config is not specified, the display's default + * pbuffer config is used. + * + * This constructor will trigger an assertion if creation of the surface + * fails, unless you pledge to manually process the error code by passing + * a non-zero pointer as errorCode parameter. The error code returned by + * eglGetError() will be written to that variable. + */ + SurfaceOpenVG(const IntSize& size, const EGLDisplay& display, EGLConfig* config = 0, EGLint* errorCode = 0); + + /** + * Create a new EGL window surface with the specified native window handle + * and config on the given display. If config is not specified, the + * display's default window config is used. + */ + SurfaceOpenVG(EGLNativeWindowType window, const EGLDisplay& display, EGLConfig* config = 0); + + EGLDisplay eglDisplay() const { return m_eglDisplay; } + EGLSurface eglSurface() const { return m_eglSurface; } + EGLContext eglContext() const { return m_eglContext; } +#endif + + ~SurfaceOpenVG(); + + /** + * If a surface is invalid (could not be created), all method calls will + * crash horribly. + */ + bool isValid() const; + + int width() const; + int height() const; + + SurfaceOpenVG* sharedSurface() const; + + /** + * Make the associated GL/EGL context the current one, so that subsequent + * OpenVG commands apply to it. + */ + void makeCurrent(MakeCurrentMode mode = ApplyPainterStateOnSurfaceSwitch); + + /** + * Make a surface/context combination current that is "compatible" + * (i.e. can access its shared resources) to the given one. If no + * surface/context is current, the given one is made current. + * + * This method is meant to avoid context changes if they're not + * necessary, particularly tailored for the case where something + * compatible to the shared surface is requested while actual painting + * happens on another surface. + */ + void makeCompatibleCurrent(); + + /** + * Empty the OpenVG pipeline and make sure all the performed paint + * operations show up on the surface as actual drawn pixels. + */ + void flush(); + + void setActivePainter(PainterOpenVG*); + PainterOpenVG* activePainter(); + +private: + PainterOpenVG* m_activePainter; + static PainterOpenVG* s_currentPainter; // global currently active painter + +#if PLATFORM(EGL) + SurfaceOpenVG(); // for EGLDisplayOpenVG + + EGLDisplay m_eglDisplay; + EGLSurface m_eglSurface; + EGLContext m_eglContext; +#endif +}; + +} + +#endif diff --git a/WebCore/platform/graphics/openvg/VGUtils.cpp b/WebCore/platform/graphics/openvg/VGUtils.cpp new file mode 100644 index 0000000..72ba5b2 --- /dev/null +++ b/WebCore/platform/graphics/openvg/VGUtils.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) Research In Motion Limited 2009. 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 "VGUtils.h" + +#include "FloatRect.h" +#include "TransformationMatrix.h" + +namespace WebCore { + +VGMatrix::VGMatrix(const VGfloat data[9]) +{ + m_data[0] = data[0]; + m_data[1] = data[1]; + m_data[2] = data[2]; + m_data[3] = data[3]; + m_data[4] = data[4]; + m_data[5] = data[5]; + m_data[6] = data[6]; + m_data[7] = data[7]; + m_data[8] = data[8]; +} + +VGMatrix::VGMatrix(const TransformationMatrix& matrix) +{ + m_data[0] = matrix.m11(); + m_data[1] = matrix.m12(); + m_data[2] = matrix.m14(); + m_data[3] = matrix.m21(); + m_data[4] = matrix.m22(); + m_data[5] = matrix.m24(); + m_data[6] = matrix.m41(); + m_data[7] = matrix.m42(); + m_data[8] = matrix.m44(); +} + +VGMatrix::operator TransformationMatrix() const +{ + TransformationMatrix matrix; + matrix.setM11(m_data[0]); + matrix.setM12(m_data[1]); + matrix.setM14(m_data[2]); + matrix.setM21(m_data[3]); + matrix.setM22(m_data[4]); + matrix.setM24(m_data[5]); + matrix.setM41(m_data[6]); + matrix.setM42(m_data[7]); + matrix.setM44(m_data[8]); + return matrix; +} + +TransformationMatrix::operator VGMatrix() const +{ + return VGMatrix(*this); +} + +VGRect::VGRect(const VGfloat data[4]) +{ + m_data[0] = data[0]; + m_data[1] = data[1]; + m_data[2] = data[2]; + m_data[3] = data[3]; +} + +VGRect::VGRect(const FloatRect& rect) +{ + m_data[0] = rect.x(); + m_data[1] = rect.y(); + m_data[2] = rect.width(); + m_data[3] = rect.height(); +} + +VGRect::operator FloatRect() const +{ + return FloatRect(m_data[0], m_data[1], m_data[2], m_data[3]); +} + +FloatRect::operator VGRect() const +{ + return VGRect(*this); +} + +} diff --git a/WebCore/platform/graphics/openvg/VGUtils.h b/WebCore/platform/graphics/openvg/VGUtils.h new file mode 100644 index 0000000..083c15a --- /dev/null +++ b/WebCore/platform/graphics/openvg/VGUtils.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) Research In Motion Limited 2009-2010. 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 VGUtils_h +#define VGUtils_h + +#include <openvg.h> +#include <wtf/Assertions.h> + +static inline const char* toVGErrorConstant(VGErrorCode error) +{ + switch (error) { + case VG_BAD_HANDLE_ERROR: + return "VG_BAD_HANDLE_ERROR"; + case VG_ILLEGAL_ARGUMENT_ERROR: + return "VG_ILLEGAL_ARGUMENT_ERROR"; + case VG_OUT_OF_MEMORY_ERROR: + return "VG_OUT_OF_MEMORY_ERROR"; + case VG_PATH_CAPABILITY_ERROR: + return "VG_PATH_CAPABILITY_ERROR"; + case VG_UNSUPPORTED_IMAGE_FORMAT_ERROR: + return "VG_UNSUPPORTED_IMAGE_FORMAT_ERROR"; + case VG_UNSUPPORTED_PATH_FORMAT_ERROR: + return "VG_UNSUPPORTED_PATH_FORMAT_ERROR"; + case VG_IMAGE_IN_USE_ERROR: + return "VG_IMAGE_IN_USE_ERROR"; + case VG_NO_CONTEXT_ERROR: + return "VG_NO_CONTEXT_ERROR"; + default: + return "UNKNOWN_ERROR"; + } +} + +#if ASSERT_DISABLED +#define ASSERT_VG_NO_ERROR() ((void)0) +#else +#define ASSERT_VG_NO_ERROR() do { \ + VGErrorCode vgErrorCode = vgGetError(); \ + ASSERT_WITH_MESSAGE(vgErrorCode == VG_NO_ERROR, "Found %s", toVGErrorConstant(vgErrorCode)); \ +} while (0) +#endif + + +namespace WebCore { + +class FloatRect; +class TransformationMatrix; + +class VGMatrix { +public: + VGMatrix(const VGfloat data[9]); + VGMatrix(const TransformationMatrix&); + const VGfloat* toVGfloat() const { return m_data; } + operator TransformationMatrix() const; +private: + VGfloat m_data[9]; +}; + +class VGRect { +public: + VGRect(const VGfloat data[4]); + VGRect(const FloatRect&); + const VGfloat* toVGfloat() const { return m_data; } + operator FloatRect() const; +private: + VGfloat m_data[4]; +}; + +} + +#endif diff --git a/WebCore/platform/graphics/qt/FontCacheQt.cpp b/WebCore/platform/graphics/qt/FontCacheQt.cpp index 82fb709..83df0f3 100644 --- a/WebCore/platform/graphics/qt/FontCacheQt.cpp +++ b/WebCore/platform/graphics/qt/FontCacheQt.cpp @@ -56,7 +56,7 @@ FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) { - const AtomicString fallbackFamily = QFont(fontDescription.family().family()).lastResortFont(); + const AtomicString fallbackFamily = QFont(fontDescription.family().family()).lastResortFamily(); return new FontPlatformData(fontDescription, fallbackFamily); } diff --git a/WebCore/platform/graphics/qt/FontPlatformData.h b/WebCore/platform/graphics/qt/FontPlatformData.h index 4a3f8bc..9355142 100644 --- a/WebCore/platform/graphics/qt/FontPlatformData.h +++ b/WebCore/platform/graphics/qt/FontPlatformData.h @@ -31,7 +31,7 @@ namespace WebCore { class String; -class FontPlatformDataPrivate { +class FontPlatformDataPrivate : public Noncopyable { public: FontPlatformDataPrivate() : refCount(1) diff --git a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp index 2cc2fc6..0a1075f 100644 --- a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp +++ b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp @@ -49,17 +49,6 @@ FontPlatformData::FontPlatformData(const FontPlatformData &other) : m_data(other FontPlatformData::FontPlatformData(const FontDescription& description, const AtomicString& familyName, int wordSpacing, int letterSpacing) : m_data(new FontPlatformDataPrivate()) { - QString familyNames(familyName); - if (!familyName.isEmpty()) - familyNames += QLatin1Char(','); - - const FontFamily* family = &description.family(); - while (family) { - familyNames += family->family(); - family = family->next(); - if (family) - familyNames += QLatin1Char(','); - } QFont& font = m_data->font; font.setFamily(familyName); font.setPixelSize(qRound(description.computedSize())); diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp index 1e44626..0196ab2 100644 --- a/WebCore/platform/graphics/qt/FontQt.cpp +++ b/WebCore/platform/graphics/qt/FontQt.cpp @@ -44,24 +44,11 @@ namespace WebCore { -static const QString qstring(const TextRun& run) +static const QString fromRawDataWithoutRef(const String& string) { - // We don't detach - return QString::fromRawData(reinterpret_cast<const QChar*>(run.characters()), run.length()); -} - -static const QString fixSpacing(const QString &string) -{ - //Only detach if we're actually changing something - 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 - } - return possiblyDetached; + // We don't detach. This assumes the WebCore string data will stay valid for the + // lifetime of the QString we pass back, since we don't ref the WebCore string. + return QString::fromRawData(reinterpret_cast<const QChar*>(string.characters()), string.length()); } static QTextLine setupLayout(QTextLayout* layout, const TextRun& style) @@ -110,7 +97,8 @@ void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const Float p->setPen(QPen(QColor(ctx->strokeColor()), ctx->strokeThickness())); } - const QString string = fixSpacing(qstring(run)); + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); // text shadow IntSize shadowSize; @@ -185,7 +173,10 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon { if (!run.length()) return 0; - const QString string = fixSpacing(qstring(run)); + + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); int w = int(line.naturalTextWidth()); @@ -198,7 +189,9 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool) const { - const QString string = fixSpacing(qstring(run)); + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); return line.xToCursor(position); @@ -206,7 +199,9 @@ int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& pt, int h, int from, int to) const { - const QString string = fixSpacing(qstring(run)); + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index a095476..b78a6e8 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -166,7 +166,7 @@ static inline Qt::FillRule toQtFillRule(WindRule rule) return Qt::OddEvenFill; } -struct TransparencyLayer { +struct TransparencyLayer : FastAllocBase { TransparencyLayer(const QPainter* p, const QRect &rect) : pixmap(rect.width(), rect.height()) { @@ -198,7 +198,7 @@ private: TransparencyLayer & operator=(const TransparencyLayer &) { return *this; } }; -class GraphicsContextPlatformPrivate { +class GraphicsContextPlatformPrivate : public Noncopyable { public: GraphicsContextPlatformPrivate(QPainter* painter); ~GraphicsContextPlatformPrivate(); @@ -618,14 +618,14 @@ QPen GraphicsContext::pen() return p->pen(); } -static void inline drawFilledShadowPath(GraphicsContext* context, QPainter* p, const QPainterPath *path) +static void inline drawFilledShadowPath(GraphicsContext* context, QPainter* p, const QPainterPath& path) { IntSize shadowSize; int shadowBlur; Color shadowColor; if (context->getShadow(shadowSize, shadowBlur, shadowColor)) { p->translate(shadowSize.width(), shadowSize.height()); - p->fillPath(*path, QBrush(shadowColor)); + p->fillPath(path, QBrush(shadowColor)); p->translate(-shadowSize.width(), -shadowSize.height()); } } @@ -640,7 +640,7 @@ void GraphicsContext::fillPath() path.setFillRule(toQtFillRule(fillRule())); if (m_common->state.fillPattern || m_common->state.fillGradient || fillColor().alpha()) { - drawFilledShadowPath(this, p, &path); + drawFilledShadowPath(this, p, path); if (m_common->state.fillPattern) { TransformationMatrix affine; p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine))); @@ -751,7 +751,7 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight); QPainter* p = m_data->p(); drawFilledShadowPath(this, p, path.platformPath()); - p->fillPath(*path.platformPath(), QColor(color)); + p->fillPath(path.platformPath(), QColor(color)); } void GraphicsContext::beginPath() @@ -762,7 +762,7 @@ void GraphicsContext::beginPath() void GraphicsContext::addPath(const Path& path) { QPainterPath newPath = m_data->currentPath; - newPath.addPath(*(path.platformPath())); + newPath.addPath(path.platformPath()); m_data->currentPath = newPath; } @@ -795,17 +795,21 @@ void GraphicsContext::clipPath(WindRule clipRule) p->setClipPath(newPath); } +void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) +{ + // FIXME: implement +} + /** * Focus ring handling is not handled here. Qt style in * RenderTheme handles drawing focus on widgets which * need it. */ -void GraphicsContext::drawFocusRing(const Color& color) +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color) { if (paintingDisabled()) return; - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); if (!rects.size()) @@ -1031,7 +1035,7 @@ void GraphicsContext::clip(const Path& path) if (paintingDisabled()) return; - m_data->p()->setClipPath(*path.platformPath(), Qt::IntersectClip); + m_data->p()->setClipPath(path.platformPath(), Qt::IntersectClip); } void GraphicsContext::canvasClip(const Path& path) @@ -1045,7 +1049,7 @@ void GraphicsContext::clipOut(const Path& path) return; QPainter* p = m_data->p(); - QPainterPath clippedOut = *path.platformPath(); + QPainterPath clippedOut = path.platformPath(); QPainterPath newClip; newClip.setFillRule(Qt::OddEvenFill); if (p->hasClipping()) { @@ -1054,7 +1058,7 @@ void GraphicsContext::clipOut(const Path& path) p->setClipPath(newClip, Qt::IntersectClip); } else { newClip.addRect(p->window()); - newClip.addPath(clippedOut & newClip); + newClip.addPath(clippedOut.intersected(newClip)); p->setClipPath(newClip); } } @@ -1293,7 +1297,7 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha memset(bmpInfo.bmBits, 0, bufferSize); } -#if !PLATFORM(WINCE) +#if !OS(WINCE) // Make sure we can do world transforms. SetGraphicsMode(bitmapDC, GM_ADVANCED); diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp new file mode 100644 index 0000000..5712eee --- /dev/null +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -0,0 +1,1118 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 "GraphicsLayerQt.h" + +#include "CurrentTime.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "Image.h" +#include "RefCounted.h" +#include "TranslateTransformOperation.h" +#include "UnitBezier.h" +#include <QtCore/qabstractanimation.h> +#include <QtCore/qdebug.h> +#include <QtCore/qset.h> +#include <QtCore/qtimer.h> +#include <QtGui/qbitmap.h> +#include <QtGui/qcolor.h> +#include <QtGui/qgraphicseffect.h> +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qgraphicsscene.h> +#include <QtGui/qmatrix4x4.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpalette.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qstyleoption.h> + +namespace WebCore { + +class GraphicsLayerQtImpl : public QGraphicsObject { + Q_OBJECT + +public: + // this set of flags help us defer which properties of the layer have been + // modified by the compositor, so we can know what to look for in the next flush + enum ChangeMask { + NoChanges = 0, + ChildrenChange = (1L << 1), + MaskLayerChange = (1L << 2), + PositionChange = (1L << 3), + AnchorPointChange = (1L << 4), + SizeChange = (1L << 5), + TransformChange = (1L << 6), + ContentChange = (1L << 7), + GeometryOrientationChange = (1L << 8), + ContentsOrientationChange = (1L << 9), + OpacityChange = (1L << 10), + ContentsRectChange = (1L << 11), + Preserves3DChange = (1L << 12), + MasksToBoundsChange = (1L << 13), + DrawsContentChange = (1L << 14), + ContentsOpaqueChange = (1L << 15), + BackfaceVisibilityChange = (1L << 16), + ChildrenTransformChange = (1L << 17), + DisplayChange = (1L << 18), + BackgroundColorChange = (1L << 19), + ParentChange = (1L << 20), + DistributesOpacityChange = (1L << 21) + }; + + // the compositor lets us special-case images and colors, so we try to do so + enum StaticContentType { HTMLContentType, PixmapContentType, ColorContentType}; + + GraphicsLayerQtImpl(GraphicsLayerQt* newLayer); + virtual ~GraphicsLayerQtImpl(); + + // reimps from QGraphicsItem + virtual QPainterPath opaqueArea() const; + virtual QRectF boundingRect() const; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); + + // we manage transforms ourselves because transform-origin acts differently in webkit and in Qt + void setBaseTransform(const QTransform&); + void drawContents(QPainter*, const QRectF&, bool mask = false); + + // let the compositor-API tell us which properties were changed + void notifyChange(ChangeMask); + + // called when the compositor is ready for us to show the changes on screen + // this is called indirectly from ChromeClientQt::setNeedsOneShotDrawingSynchronization + // (meaning the sync would happen together with the next draw) + // or ChromeClientQt::scheduleCompositingLayerSync (meaning the sync will happen ASAP) + void flushChanges(bool recursive = true); + +public slots: + // we need to notify the client (aka the layer compositor) when the animation actually starts + void notifyAnimationStarted(); + +public: + GraphicsLayerQt* m_layer; + + QTransform m_baseTransfom; + bool m_transformAnimationRunning; + bool m_opacityAnimationRunning; + + struct ContentData { + QPixmap pixmap; + QRegion regionToUpdate; + bool updateAll; + QColor contentsBackgroundColor; + QColor backgroundColor; + StaticContentType contentType; + float opacity; + ContentData() + : updateAll(false) + , contentType(HTMLContentType) + , opacity(1.f) + { + } + + }; + + ContentData m_pendingContent; + ContentData m_currentContent; + + int m_changeMask; + + QSizeF m_size; + QList<QWeakPointer<QAbstractAnimation> > m_animations; + QTimer m_suspendTimer; + + struct State { + GraphicsLayer* maskLayer; + FloatPoint pos; + FloatPoint3D anchorPoint; + FloatSize size; + TransformationMatrix transform; + TransformationMatrix childrenTransform; + Color backgroundColor; + Color currentColor; + GraphicsLayer::CompositingCoordinatesOrientation geoOrientation; + GraphicsLayer::CompositingCoordinatesOrientation contentsOrientation; + float opacity; + QRect contentsRect; + + bool preserves3D: 1; + bool masksToBounds: 1; + bool drawsContent: 1; + bool contentsOpaque: 1; + bool backfaceVisibility: 1; + bool distributeOpacity: 1; + bool align: 2; + State(): maskLayer(0), opacity(1), preserves3D(false), masksToBounds(false), + drawsContent(false), contentsOpaque(false), backfaceVisibility(false), + distributeOpacity(false) + { + } + } m_state; +}; + +GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) + : QGraphicsObject(0) + , m_layer(newLayer) + , m_transformAnimationRunning(false) + , m_changeMask(NoChanges) +{ + // better to calculate the exposed rect in QGraphicsView than over-render in WebCore + // FIXME: test different approaches + setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); + + // we use graphics-view for compositing, not for interactivity + setAcceptedMouseButtons(Qt::NoButton); + setEnabled(false); + + // we'll set the cache when we know what's going on + setCacheMode(NoCache); +} + +GraphicsLayerQtImpl::~GraphicsLayerQtImpl() +{ + // the compositor manages item lifecycle - we don't want the graphics-view + // system to automatically delete our items + + const QList<QGraphicsItem*> children = childItems(); + for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) { + if (QGraphicsItem* item = *it) { + if (scene()) + scene()->removeItem(item); + item->setParentItem(0); + } + } + + // we do, however, own the animations... + for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_animations.begin(); it != m_animations.end(); ++it) + if (QAbstractAnimation* anim = it->data()) + delete anim; +} + +void GraphicsLayerQtImpl::setBaseTransform(const QTransform& transform) +{ + if (!m_layer) + return; + // webkit has relative-to-size originPoint, graphics-view has a pixel originPoint + // here we convert + QPointF originTranslate( + m_layer->anchorPoint().x() * m_layer->size().width(), m_layer->anchorPoint().y() * m_layer->size().height()); + + resetTransform(); + + // we have to manage this ourselves because QGraphicsView's transformOrigin is incomplete + translate(originTranslate.x(), originTranslate.y()); + setTransform(transform, true); + translate(-originTranslate.x(), -originTranslate.y()); + m_baseTransfom = transform; +} + +QPainterPath GraphicsLayerQtImpl::opaqueArea() const +{ + QPainterPath painterPath; + // we try out best to return the opaque area, maybe it will help graphics-view render less items + if (m_currentContent.backgroundColor.isValid() && m_currentContent.backgroundColor.alpha() == 0xff) + painterPath.addRect(boundingRect()); + else { + if (m_state.contentsOpaque + || (m_currentContent.contentType == ColorContentType && m_currentContent.contentsBackgroundColor.alpha() == 0xff) + || (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlpha())) { + + painterPath.addRect(m_state.contentsRect); + } + } + return painterPath; +} + +QRectF GraphicsLayerQtImpl::boundingRect() const +{ + return QRectF(QPointF(0, 0), QSizeF(m_size)); +} + +void GraphicsLayerQtImpl::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if (m_state.maskLayer && m_state.maskLayer->platformLayer()) { + // FIXME: see if this is better done somewhere else + GraphicsLayerQtImpl* otherMask = static_cast<GraphicsLayerQtImpl*>(m_state.maskLayer->platformLayer()); + otherMask->flushChanges(true); + + // CSS3 mask and QGraphicsOpacityEffect are the same thing! we just need to convert... + // The conversion is as fast as we can make it - we render the layer once and send it to the QGraphicsOpacityEffect + if (!graphicsEffect()) { + QPixmap mask(QSize(m_state.maskLayer->size().width(), m_state.maskLayer->size().height())); + mask.fill(Qt::transparent); + { + QPainter p(&mask); + p.setRenderHints(painter->renderHints(), true); + p.setCompositionMode(QPainter::CompositionMode_Source); + static_cast<GraphicsLayerQtImpl*>(m_state.maskLayer->platformLayer())->drawContents(&p, option->exposedRect, true); + } + QGraphicsOpacityEffect* opacityEffect = new QGraphicsOpacityEffect(this); + opacityEffect->setOpacity(1); + opacityEffect->setOpacityMask(QBrush(mask)); + setGraphicsEffect(opacityEffect); + } + } + drawContents(painter, option->exposedRect); +} + +void GraphicsLayerQtImpl::drawContents(QPainter* painter, const QRectF& r, bool mask) +{ + QRect rect = r.toAlignedRect(); + + if (m_currentContent.contentType != HTMLContentType && !m_state.contentsRect.isEmpty()) + rect = rect.intersected(m_state.contentsRect); + + if (m_currentContent.backgroundColor.isValid()) + painter->fillRect(r, QColor(m_currentContent.backgroundColor)); + + if (!rect.isEmpty()) { + switch (m_currentContent.contentType) { + case PixmapContentType: + // we have to scale the image to the contentsRect + // FIXME: a better way would probably be drawPixmap with a src/target rect + painter->drawPixmap(rect.topLeft(), m_currentContent.pixmap.scaled(m_state.contentsRect.size()), r); + break; + case ColorContentType: + painter->fillRect(rect, m_currentContent.contentsBackgroundColor); + break; + default: + if (m_state.drawsContent) { + // this is the "expensive" bit. we try to minimize calls to this + // neck of the woods by proper caching + GraphicsContext gc(painter); + m_layer->paintGraphicsLayerContents(gc, rect); + } + break; + } + } +} + +void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask) +{ + if (!this) + return; + + m_changeMask |= changeMask; + + if (m_layer->client()) + m_layer->client()->notifySyncRequired(m_layer); +} + +void GraphicsLayerQtImpl::flushChanges(bool recursive) +{ + // this is the bulk of the work. understanding what the compositor is trying to achieve, + // what graphics-view can do, and trying to find a sane common-grounds + if (!m_layer || m_changeMask == NoChanges) + goto afterLayerChanges; + + if (m_currentContent.contentType == HTMLContentType && (m_changeMask & ParentChange)) { + // the WebCore compositor manages item ownership. We have to make sure + // graphics-view doesn't try to snatch that ownership... + if (!m_layer->parent() && !parentItem()) + setParentItem(0); + else if (m_layer && m_layer->parent() && m_layer->parent()->nativeLayer() != parentItem()) + setParentItem(m_layer->parent()->nativeLayer()); + } + + if (m_changeMask & ChildrenChange) { + // we basically do an XOR operation on the list of current children + // and the list of wanted children, and remove/add + QSet<QGraphicsItem*> newChildren; + const Vector<GraphicsLayer*> newChildrenVector = (m_layer->children()); + newChildren.reserve(newChildrenVector.size()); + + for (size_t i = 0; i < newChildrenVector.size(); ++i) + newChildren.insert(newChildrenVector[i]->platformLayer()); + + const QSet<QGraphicsItem*> currentChildren = childItems().toSet(); + const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren; + const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren; + for (QSet<QGraphicsItem*>::const_iterator it = childrenToAdd.begin(); it != childrenToAdd.end(); ++it) { + if (QGraphicsItem* w = *it) + w->setParentItem(this); + } + for (QSet<QGraphicsItem*>::const_iterator it = childrenToRemove.begin(); it != childrenToRemove.end(); ++it) { + if (QGraphicsItem* w = *it) + w->setParentItem(0); + } + + // children are ordered by z-value, let graphics-view know. + for (size_t i = 0; i < newChildrenVector.size(); ++i) + if (newChildrenVector[i]->platformLayer()) + newChildrenVector[i]->platformLayer()->setZValue(i); + } + + if (m_changeMask & MaskLayerChange) { + // we can't paint here, because we don't know if the mask layer + // itself is ready... we'll have to wait till this layer tries to paint + setGraphicsEffect(0); + if (m_layer->maskLayer()) + setFlag(ItemClipsChildrenToShape, true); + else + setFlag(ItemClipsChildrenToShape, m_layer->masksToBounds()); + update(); + } + + if ((m_changeMask & PositionChange) && (m_layer->position() != m_state.pos)) + setPos(m_layer->position().x(), m_layer->position().y()); + + if (m_changeMask & SizeChange) { + if (m_layer->size() != m_state.size) { + prepareGeometryChange(); + m_size = QSizeF(m_layer->size().width(), m_layer->size().height()); + } + } + + if (m_changeMask & (TransformChange | AnchorPointChange | SizeChange)) { + // since we convert a percentage-based origin-point to a pixel-based one, + // the anchor-point, transform and size from WebCore all affect the one + // that we give Qt + if (m_state.transform != m_layer->transform() || m_state.anchorPoint != m_layer->anchorPoint() || m_state.size != m_layer->size()) + setBaseTransform(QTransform(m_layer->transform())); + } + + if (m_changeMask & (ContentChange | DrawsContentChange)) { + switch (m_pendingContent.contentType) { + case PixmapContentType: + // we need cache even for images, because they need to be resized + // to the contents rect. maybe this can be optimized though + setCacheMode(m_transformAnimationRunning ? ItemCoordinateCache : DeviceCoordinateCache); + update(); + setFlag(ItemHasNoContents, false); + break; + + case ColorContentType: + // no point in caching a solid-color rectangle + setCacheMode(QGraphicsItem::NoCache); + if (m_pendingContent.contentType != m_currentContent.contentType || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor) + update(); + m_state.drawsContent = false; + setFlag(ItemHasNoContents, false); + break; + + case HTMLContentType: + if (m_pendingContent.contentType != m_currentContent.contentType) + update(); + if (!m_state.drawsContent && m_layer->drawsContent()) + update(); + if (m_layer->drawsContent()) + setCacheMode(m_transformAnimationRunning ? ItemCoordinateCache : DeviceCoordinateCache); + else + setCacheMode(NoCache); + + setFlag(ItemHasNoContents, !m_layer->drawsContent()); + break; + } + } + + if ((m_changeMask & OpacityChange) && m_state.opacity != m_layer->opacity()) + setOpacity(m_layer->opacity()); + + if (m_changeMask & ContentsRectChange) { + const QRect rect(m_layer->contentsRect()); + if (m_state.contentsRect != rect) { + m_state.contentsRect = rect; + update(); + } + } + + if ((m_changeMask & MasksToBoundsChange) + && m_state.masksToBounds != m_layer->masksToBounds()) { + + setFlag(QGraphicsItem::ItemClipsToShape, m_layer->masksToBounds()); + setFlag(QGraphicsItem::ItemClipsChildrenToShape, m_layer->masksToBounds()); + } + + if ((m_changeMask & ContentsOpaqueChange) && m_state.contentsOpaque != m_layer->contentsOpaque()) + prepareGeometryChange(); + + if (m_changeMask & DisplayChange) + update(m_pendingContent.regionToUpdate.boundingRect()); + + if ((m_changeMask & BackgroundColorChange) && (m_pendingContent.backgroundColor != m_currentContent.backgroundColor)) + update(); + + // FIXME: the following flags are currently not handled, as they don't have a clear test or are in low priority + // GeometryOrientationChange, ContentsOrientationChange, BackfaceVisibilityChange, ChildrenTransformChange + + m_state.maskLayer = m_layer->maskLayer(); + m_state.pos = m_layer->position(); + m_state.anchorPoint = m_layer->anchorPoint(); + m_state.size = m_layer->size(); + m_state.transform = m_layer->transform(); + m_state.geoOrientation = m_layer->geometryOrientation(); + m_state.contentsOrientation =m_layer->contentsOrientation(); + m_state.opacity = m_layer->opacity(); + m_state.contentsRect = m_layer->contentsRect(); + m_state.preserves3D = m_layer->preserves3D(); + m_state.masksToBounds = m_layer->masksToBounds(); + m_state.drawsContent = m_layer->drawsContent(); + m_state.contentsOpaque = m_layer->contentsOpaque(); + m_state.backfaceVisibility = m_layer->backfaceVisibility(); + m_currentContent.pixmap = m_pendingContent.pixmap; + m_currentContent.contentType = m_pendingContent.contentType; + m_currentContent.backgroundColor = m_pendingContent.backgroundColor; + m_currentContent.regionToUpdate |= m_pendingContent.regionToUpdate; + m_currentContent.contentsBackgroundColor = m_pendingContent.contentsBackgroundColor; + m_pendingContent.regionToUpdate = QRegion(); + m_changeMask = NoChanges; + + +afterLayerChanges: + if (!recursive) + return; + + const QList<QGraphicsItem*> children = childItems(); + + for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) { + if (QGraphicsItem* item = *it) + if (GraphicsLayerQtImpl* layer = qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject())) + layer->flushChanges(true); + } +} + +void GraphicsLayerQtImpl::notifyAnimationStarted() +{ + // WebCore notifies javascript when the animation starts + // here we're letting it know + m_layer->client()->notifyAnimationStarted(m_layer, WTF::currentTime()); +} + +GraphicsLayerQt::GraphicsLayerQt(GraphicsLayerClient* client) + : GraphicsLayer(client) + , m_impl(PassOwnPtr<GraphicsLayerQtImpl>(new GraphicsLayerQtImpl(this))) +{ +} + +GraphicsLayerQt::~GraphicsLayerQt() +{ +} + +// this is the hook for WebCore compositor to know that Qt implements compositing with GraphicsLayerQt +PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) +{ + return new GraphicsLayerQt(client); +} + +// reimp from GraphicsLayer.h: Qt is top-down +GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayer::compositingCoordinatesOrientation() +{ + return CompositingCoordinatesTopDown; +} + +// reimp from GraphicsLayer.h: we'll need to update the whole display, and we can't count on the current size because it might change +void GraphicsLayerQt::setNeedsDisplay() +{ + m_impl->m_pendingContent.regionToUpdate = QRegion(QRect(QPoint(0, 0), QSize(size().width(), size().height()))); + m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& r) +{ + m_impl->m_pendingContent.regionToUpdate|= QRectF(r).toAlignedRect(); + m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setName(const String& name) +{ + m_impl->setObjectName(name); + GraphicsLayer::setName(name); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setParent(GraphicsLayer* layer) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange); + GraphicsLayer::setParent(layer); +} + +// reimp from GraphicsLayer.h +bool GraphicsLayerQt::setChildren(const Vector<GraphicsLayer*>& children) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); + return GraphicsLayer::setChildren(children); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::addChild(GraphicsLayer* layer) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); + GraphicsLayer::addChild(layer); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::addChildAtIndex(GraphicsLayer* layer, int index) +{ + GraphicsLayer::addChildAtIndex(layer, index); + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildAbove(layer, sibling); + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling) +{ + + GraphicsLayer::addChildBelow(layer, sibling); + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); +} + +// reimp from GraphicsLayer.h +bool GraphicsLayerQt::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); + return true; + } + + return false; +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::removeFromParent() +{ + if (parent()) + m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange); + GraphicsLayer::removeFromParent(); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setMaskLayer(GraphicsLayer* layer) +{ + GraphicsLayer::setMaskLayer(layer); + m_impl->notifyChange(GraphicsLayerQtImpl::MaskLayerChange); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setPosition(const FloatPoint& p) +{ + if (position() != p) + m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange); + GraphicsLayer::setPosition(p); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& p) +{ + if (anchorPoint() != p) + m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange); + GraphicsLayer::setAnchorPoint(p); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setSize(const FloatSize& size) +{ + if (this->size() != size) + m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange); + GraphicsLayer::setSize(size); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setTransform(const TransformationMatrix& t) +{ + if (!m_impl->m_transformAnimationRunning && transform() != t) + m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange); + GraphicsLayer::setTransform(t); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& t) +{ + GraphicsLayer::setChildrenTransform(t); + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenTransformChange); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setPreserves3D(bool b) +{ + if (b != preserves3D()); + m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange); + GraphicsLayer::setPreserves3D(b); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setMasksToBounds(bool b) +{ + GraphicsLayer::setMasksToBounds(b); + m_impl->notifyChange(GraphicsLayerQtImpl::MasksToBoundsChange); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setDrawsContent(bool b) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::DrawsContentChange); + GraphicsLayer::setDrawsContent(b); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setBackgroundColor(const Color& c) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange); + m_impl->m_pendingContent.backgroundColor = c; + GraphicsLayer::setBackgroundColor(c); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::clearBackgroundColor() +{ + m_impl->m_pendingContent.backgroundColor = QColor(); + m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange); + GraphicsLayer::clearBackgroundColor(); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setContentsOpaque(bool b) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOpaqueChange); + GraphicsLayer::setContentsOpaque(b); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setBackfaceVisibility(bool b) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::BackfaceVisibilityChange); + GraphicsLayer::setBackfaceVisibility(b); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setOpacity(float o) +{ + if (!m_impl->m_opacityAnimationRunning && opacity() != o) + m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange); + GraphicsLayer::setOpacity(o); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setContentsRect(const IntRect& r) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ContentsRectChange); + GraphicsLayer::setContentsRect(r); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setContentsToImage(Image* image) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange); + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType; + GraphicsLayer::setContentsToImage(image); + if (image) { + QPixmap* pxm = image->nativeImageForCurrentFrame(); + if (pxm) { + m_impl->m_pendingContent.pixmap = *pxm; + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::PixmapContentType; + return; + } + } + m_impl->m_pendingContent.pixmap = QPixmap(); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setContentsBackgroundColor(const Color& color) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange); + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::ColorContentType; + m_impl->m_pendingContent.contentsBackgroundColor = QColor(color); + GraphicsLayer::setContentsBackgroundColor(color); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setGeometryOrientation(CompositingCoordinatesOrientation orientation) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::GeometryOrientationChange); + GraphicsLayer::setGeometryOrientation(orientation); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::setContentsOrientation(CompositingCoordinatesOrientation orientation) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOrientationChange); + GraphicsLayer::setContentsOrientation(orientation); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::distributeOpacity(float o) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange); + m_impl->m_state.distributeOpacity = true; +} + +// reimp from GraphicsLayer.h +float GraphicsLayerQt::accumulatedOpacity() const +{ + return m_impl->effectiveOpacity(); +} + +// reimp from GraphicsLayer.h +void GraphicsLayerQt::syncCompositingState() +{ + m_impl->flushChanges(); + GraphicsLayer::syncCompositingState(); +} + +// reimp from GraphicsLayer.h +NativeLayer GraphicsLayerQt::nativeLayer() const +{ + return m_impl.get(); +} + +// reimp from GraphicsLayer.h +PlatformLayer* GraphicsLayerQt::platformLayer() const +{ + return m_impl.get(); +} + +// now we start dealing with WebCore animations translated to Qt animations + +template <typename T> +struct KeyframeValueQt { + TimingFunction timingFunction; + T value; +}; + +// we copy this from the AnimationBase.cpp +static inline double solveEpsilon(double duration) +{ + return 1.0 / (200.0 * duration); +} + +static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, qreal p2y, double t, double duration) +{ + UnitBezier bezier(p1x, p1y, p2x, p2y); + return bezier.solve(t, solveEpsilon(duration)); +} + +// we want the timing function to be as close as possible to what the web-developer intended, so we're using the same function used by WebCore when compositing is disabled +// Using easing-curves would probably work for some of the cases, but wouldn't really buy us anything as we'd have to convert the bezier function back to an easing curve +static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, int duration) +{ + if (timingFunction.type() == LinearTimingFunction) + return progress; + if (timingFunction.type() == CubicBezierTimingFunction) { + return solveCubicBezierFunction(timingFunction.x1(), + timingFunction.y1(), + timingFunction.x2(), + timingFunction.y2(), + double(progress), double(duration) / 1000); + } + return progress; +} + +// helper functions to safely get a value out of WebCore's AnimationValue* +static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, TransformOperations& transformOperations) +{ + transformOperations = TransformOperations(); + if (!animationValue) + return; + + const TransformOperations* ops = static_cast<const TransformAnimationValue*>(animationValue)->value(); + + if (ops) + transformOperations = *ops; +} + +static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, qreal& realValue) +{ + realValue = animationValue ? static_cast<const FloatAnimationValue*>(animationValue)->value() : 0; +} + +// we put a bit of the functionality in a base class to allow casting and to save some code size +class AnimationQtBase : public QAbstractAnimation { +public: + AnimationQtBase(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) + : QAbstractAnimation(0) + , m_layer(layer) + , m_boxSize(boxSize) + , m_duration(anim->duration() * 1000) + , m_isAlternate(anim->direction() == Animation::AnimationDirectionAlternate) + , m_webkitPropertyID(values.property()) + , m_keyframesName(name) + { + } + + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) + { + QAbstractAnimation::updateState(newState, oldState); + + // for some reason I have do this asynchronously - or the animation won't work + if (newState == Running && oldState == Stopped) + QTimer::singleShot(0, m_layer.data(), SLOT(notifyAnimationStarted())); + } + + virtual int duration() const { return m_duration; } + + QWeakPointer<GraphicsLayerQtImpl> m_layer; + IntSize m_boxSize; + int m_duration; + bool m_isAlternate; + AnimatedPropertyID m_webkitPropertyID; + QString m_keyframesName; +}; + +// we'd rather have a templatized QAbstractAnimation than QPropertyAnimation / QVariantAnimation; +// Since we know the types that we're dealing with, the QObject/QProperty/QVariant abstraction +// buys us very little in this case, for too much overhead +template <typename T> +class AnimationQt : public AnimationQtBase { + +public: + AnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) + :AnimationQtBase(layer, values, boxSize, anim, name) + { + // copying those WebCore structures is not trivial, we have to do it like this + for (size_t i = 0; i < values.size(); ++i) { + const AnimationValue* animationValue = values.at(i); + KeyframeValueQt<T> keyframeValue; + if (animationValue->timingFunction()) + keyframeValue.timingFunction = *animationValue->timingFunction(); + webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value); + m_keyframeValues[animationValue->keyTime()] = keyframeValue; + } + } + +protected: + + // this is the part that differs between animated properties + virtual void applyFrame(const T& fromValue, const T& toValue, qreal progress) = 0; + + virtual void updateCurrentTime(int currentTime) + { + if (!m_layer) + return; + + qreal progress = qreal(currentLoopTime()) / duration(); + + if (m_isAlternate && currentLoop()%2) + progress = 1-progress; + + if (m_keyframeValues.isEmpty()) + return; + + // we find the current from-to keyframes in our little map + typename QMap<qreal, KeyframeValueQt<T> >::iterator it = m_keyframeValues.find(progress); + + // we didn't find an exact match, we try the closest match (lower bound) + if (it == m_keyframeValues.end()) + it = m_keyframeValues.lowerBound(progress)-1; + + // we didn't find any match - we use the first keyframe + if (it == m_keyframeValues.end()) + it = m_keyframeValues.begin(); + + typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it+1; + if (it2 == m_keyframeValues.end()) + it2 = m_keyframeValues.begin(); + const KeyframeValueQt<T>& fromKeyframe = it.value(); + const KeyframeValueQt<T>& toKeyframe = it2.value(); + + const TimingFunction& timingFunc = fromKeyframe.timingFunction; + const T& fromValue = fromKeyframe.value; + const T& toValue = toKeyframe.value; + + // now we have a source keyframe, origin keyframe and a timing function + // we can now process the progress and apply the frame + qreal normalizedProgress = (it.key() == it2.key()) ? 0 : (progress - it.key()) / (it2.key() - it.key()); + normalizedProgress = applyTimingFunction(timingFunc, normalizedProgress, duration() / 1000); + applyFrame(fromValue, toValue, normalizedProgress); + } + + QMap<qreal, KeyframeValueQt<T> > m_keyframeValues; +}; + +class TransformAnimationQt : public AnimationQt<TransformOperations> { +public: + TransformAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) + : AnimationQt<TransformOperations>(layer, values, boxSize, anim, name) + { + } + + ~TransformAnimationQt() + { + // this came up during the compositing/animation LayoutTests + // when the animation dies, the transform has to go back to default + if (m_layer) + m_layer.data()->setBaseTransform(QTransform(m_layer.data()->m_layer->transform())); + } + + // the idea is that we let WebCore manage the transform-operations + // and Qt just manages the animation heartbeat and the bottom-line QTransform + // we get the performance not by using QTransform instead of TransformationMatrix, but by proper caching of + // items that are expensive for WebCore to render. We want the rest to be as close to WebCore's idea as possible. + virtual void applyFrame(const TransformOperations& sourceOperations, const TransformOperations& targetOperations, qreal progress) + { + TransformationMatrix transformMatrix; + + // this looks simple but is really tricky to get right. Use caution. + for (size_t i = 0; i < targetOperations.size(); ++i) + targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize); + + m_layer.data()->setBaseTransform(QTransform(transformMatrix)); + } + + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) + { + AnimationQtBase::updateState(newState, oldState); + if (!m_layer) + return; + m_layer.data()->flushChanges(true); + + // to increase FPS, we use a less accurate caching mechanism while animation is going on + // this is a UX choice that should probably be customizable + if (newState == QAbstractAnimation::Running) { + m_layer.data()->m_transformAnimationRunning = true; + if (m_layer.data()->cacheMode() == QGraphicsItem::DeviceCoordinateCache) + m_layer.data()->setCacheMode(QGraphicsItem::ItemCoordinateCache); + } else { + m_layer.data()->m_transformAnimationRunning = false; + if (m_layer.data()->cacheMode() == QGraphicsItem::ItemCoordinateCache) + m_layer.data()->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + } + } +}; + +class OpacityAnimationQt : public AnimationQt<qreal> { +public: + OpacityAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) + : AnimationQt<qreal>(layer, values, boxSize, anim, name) + { + } + + virtual void applyFrame(const qreal& fromValue, const qreal& toValue, qreal progress) + { + m_layer.data()->setOpacity(qMin<qreal>(qMax<qreal>(fromValue + (toValue-fromValue)*progress, 0), 1)); + } + + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) + { + QAbstractAnimation::updateState(newState, oldState); + if (m_layer) + m_layer.data()->m_opacityAnimationRunning = (newState == QAbstractAnimation::Running); + } +}; + +bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset) +{ + if (!anim->duration() || !anim->iterationCount()) + return false; + + QAbstractAnimation* newAnim; + + switch (values.property()) { + case AnimatedPropertyOpacity: + newAnim = new OpacityAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName); + break; + case AnimatedPropertyWebkitTransform: + newAnim = new TransformAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName); + break; + default: + return false; + } + + // we make sure WebCore::Animation and QAnimation are on the same terms + newAnim->setLoopCount(anim->iterationCount()); + m_impl->m_animations.append(QWeakPointer<QAbstractAnimation>(newAnim)); + QObject::connect(&m_impl->m_suspendTimer, SIGNAL(timeout()), newAnim, SLOT(resume())); + timeOffset += anim->delay(); + + // flush now or flicker... + m_impl->flushChanges(false); + + if (timeOffset) + QTimer::singleShot(timeOffset * 1000, newAnim, SLOT(start())); + else + newAnim->start(); + + QObject::connect(newAnim, SIGNAL(finished()), newAnim, SLOT(deleteLater())); + + return true; +} + +void GraphicsLayerQt::removeAnimationsForProperty(AnimatedPropertyID id) +{ + for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (*it) { + AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data()); + if (anim && anim->m_webkitPropertyID == id) { + delete anim; + it = m_impl->m_animations.erase(it); + --it; + } + } + } +} + +void GraphicsLayerQt::removeAnimationsForKeyframes(const String& name) +{ + for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (*it) { + AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data()); + if (anim && anim->m_keyframesName == QString(name)) { + (*it).data()->deleteLater(); + it = m_impl->m_animations.erase(it); + --it; + } + } + } +} + +void GraphicsLayerQt::pauseAnimation(const String& name, double timeOffset) +{ + for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (*it) { + AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data()); + if (anim && anim->m_keyframesName == QString(name)) + QTimer::singleShot(timeOffset * 1000, anim, SLOT(pause())); + } + } +} + +void GraphicsLayerQt::suspendAnimations(double time) +{ + if (m_impl->m_suspendTimer.isActive()) { + m_impl->m_suspendTimer.stop(); + m_impl->m_suspendTimer.start(time * 1000); + } else { + for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + QAbstractAnimation* anim = it->data(); + if (anim) + anim->pause(); + } + } +} + +void GraphicsLayerQt::resumeAnimations() +{ + if (m_impl->m_suspendTimer.isActive()) { + m_impl->m_suspendTimer.stop(); + for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + QAbstractAnimation* anim = (*it).data(); + if (anim) + anim->resume(); + } + } +} + +} + +#include <GraphicsLayerQt.moc> diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.h b/WebCore/platform/graphics/qt/GraphicsLayerQt.h new file mode 100644 index 0000000..3a53bd9 --- /dev/null +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.h @@ -0,0 +1,85 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 GraphicsLayerQt_h +#define GraphicsLayerQt_h + +#include "GraphicsLayer.h" +#include "GraphicsLayerClient.h" + +namespace WebCore { + +class GraphicsLayerQtImpl; + +class GraphicsLayerQt : public GraphicsLayer { + friend class GraphicsLayerQtImpl; + +public: + GraphicsLayerQt(GraphicsLayerClient*); + virtual ~GraphicsLayerQt(); + + // reimps from GraphicsLayer.h + virtual NativeLayer nativeLayer() const; + virtual PlatformLayer* platformLayer() const; + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + virtual void setParent(GraphicsLayer* layer); + virtual void setName(const String& name); + virtual bool setChildren(const Vector<GraphicsLayer*>&); + virtual void addChild(GraphicsLayer*); + virtual void addChildAtIndex(GraphicsLayer*, int index); + virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + virtual void removeFromParent(); + virtual void setMaskLayer(GraphicsLayer* layer); + virtual void setPosition(const FloatPoint& p); + virtual void setAnchorPoint(const FloatPoint3D& p); + virtual void setSize(const FloatSize& size); + virtual void setTransform(const TransformationMatrix& t); + virtual void setChildrenTransform(const TransformationMatrix& t); + virtual void setPreserves3D(bool b); + virtual void setMasksToBounds(bool b); + virtual void setDrawsContent(bool b); + virtual void setBackgroundColor(const Color&); + virtual void clearBackgroundColor(); + virtual void setContentsOpaque(bool b); + virtual void setBackfaceVisibility(bool b); + virtual void setOpacity(float opacity); + virtual void setContentsRect(const IntRect& r); + virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& keyframesName, double timeOffset); + virtual void removeAnimationsForProperty(AnimatedPropertyID); + virtual void removeAnimationsForKeyframes(const String& keyframesName); + virtual void pauseAnimation(const String& keyframesName, double timeOffset); + virtual void suspendAnimations(double time); + virtual void resumeAnimations(); + virtual void setContentsToImage(Image*); + virtual void setContentsBackgroundColor(const Color&); + virtual void setGeometryOrientation(CompositingCoordinatesOrientation orientation); + virtual void setContentsOrientation(CompositingCoordinatesOrientation orientation); + virtual void distributeOpacity(float); + virtual float accumulatedOpacity() const; + virtual void syncCompositingState(); + +private: + OwnPtr<GraphicsLayerQtImpl> m_impl; +}; + +} +#endif // GraphicsLayerQt_h diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index 5255428..d831566 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -114,7 +114,7 @@ void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) value = qRgba(lookUpTable[qRed(value)], lookUpTable[qGreen(value)], lookUpTable[qBlue(value)], - lookUpTable[qAlpha(value)]); + qAlpha(value)); image.setPixel(x, y, value); } } diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp index b6823dd..234f78b 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -47,16 +47,12 @@ ImageDecoder* ImageDecoder::create(const SharedBuffer& data) } ImageDecoderQt::ImageDecoderQt() - : m_buffer(0) - , m_reader(0) - , m_repetitionCount(cAnimationNone) + : m_repetitionCount(cAnimationNone) { } ImageDecoderQt::~ImageDecoderQt() { - delete m_reader; - delete m_buffer; } void ImageDecoderQt::setData(SharedBuffer* data, bool allDataReceived) @@ -77,10 +73,16 @@ void ImageDecoderQt::setData(SharedBuffer* data, bool allDataReceived) // Attempt to load the data QByteArray imageData = QByteArray::fromRawData(m_data->data(), m_data->size()); - m_buffer = new QBuffer; + m_buffer.set(new QBuffer); m_buffer->setData(imageData); m_buffer->open(QBuffer::ReadOnly); - m_reader = new QImageReader(m_buffer, m_format); + m_reader.set(new QImageReader(m_buffer.get(), m_format)); + + // This will force the JPEG decoder to use JDCT_IFAST + m_reader->setQuality(49); + + // QImageReader only allows retrieving the format before reading the image + m_format = m_reader->format(); } bool ImageDecoderQt::isSizeAvailable() @@ -158,7 +160,6 @@ void ImageDecoderQt::internalDecodeSize() if (size.isEmpty()) return failRead(); - m_format = m_reader->format(); setSize(size.width(), size.height()); } @@ -178,10 +179,8 @@ void ImageDecoderQt::internalReadImage(size_t frameIndex) if (m_frameBufferCache[i].status() != RGBA32Buffer::FrameComplete) return; - delete m_reader; - delete m_buffer; - m_buffer = 0; - m_reader = 0; + m_reader.clear(); + m_buffer.clear(); } void ImageDecoderQt::internalHandleCurrentImage(size_t frameIndex) @@ -229,10 +228,8 @@ void ImageDecoderQt::forceLoadEverything() void ImageDecoderQt::failRead() { setFailed(); - delete m_reader; - delete m_buffer; - m_reader = 0; - m_buffer = 0; + m_reader.clear(); + m_buffer.clear(); } } diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.h b/WebCore/platform/graphics/qt/ImageDecoderQt.h index d11b938..be9a9b0 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.h +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.h @@ -33,6 +33,7 @@ #include <QtCore/QList> #include <QtCore/QHash> #include <QtCore/QBuffer> +#include <wtf/OwnPtr.h> namespace WebCore { @@ -66,8 +67,8 @@ private: private: QByteArray m_format; - QBuffer* m_buffer; - QImageReader* m_reader; + OwnPtr<QBuffer> m_buffer; + OwnPtr<QImageReader> m_reader; mutable int m_repetitionCount; }; diff --git a/WebCore/platform/graphics/qt/ImageQt.cpp b/WebCore/platform/graphics/qt/ImageQt.cpp index 9a82911..fea1448 100644 --- a/WebCore/platform/graphics/qt/ImageQt.cpp +++ b/WebCore/platform/graphics/qt/ImageQt.cpp @@ -213,7 +213,7 @@ void BitmapImage::checkForSolidColor() m_solidColor = QColor::fromRgba(framePixmap->toImage().pixel(0, 0)); } -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) PassRefPtr<BitmapImage> BitmapImage::create(HBITMAP hBitmap) { return BitmapImage::create(new QPixmap(QPixmap::fromWinHBITMAP(hBitmap))); diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp index f446755..3274db5 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -38,14 +38,16 @@ #include <QUrl> #include <QEvent> -#include <audiooutput.h> -#include <mediaobject.h> -#include <videowidget.h> +#include <phonon/path.h> +#include <phonon/audiooutput.h> +#include <phonon/mediaobject.h> +#include <phonon/videowidget.h> using namespace Phonon; #define LOG_MEDIAOBJECT() (LOG(Media, "%s", debugMediaObject(this, *m_mediaObject).constData())) +#if !LOG_DISABLED static QByteArray debugMediaObject(WebCore::MediaPlayerPrivate* mediaPlayer, const MediaObject& mediaObject) { QByteArray byteArray; @@ -73,6 +75,7 @@ static QByteArray debugMediaObject(WebCore::MediaPlayerPrivate* mediaPlayer, con return byteArray; } +#endif using namespace WTF; @@ -89,9 +92,7 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) { // Hint to Phonon to disable overlay painting m_videoWidget->setAttribute(Qt::WA_DontShowOnScreen); -#if QT_VERSION < 0x040500 m_videoWidget->setAttribute(Qt::WA_QuitOnClose, false); -#endif createPath(m_mediaObject, m_videoWidget); createPath(m_mediaObject, m_audioOutput); @@ -256,11 +257,6 @@ float MediaPlayerPrivate::currentTime() const return currentTime; } -void MediaPlayerPrivate::setEndTime(float) -{ - notImplemented(); -} - PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const { notImplemented(); @@ -279,12 +275,6 @@ unsigned MediaPlayerPrivate::bytesLoaded() const return 0; } -bool MediaPlayerPrivate::totalBytesKnown() const -{ - //notImplemented(); - return false; -} - unsigned MediaPlayerPrivate::totalBytes() const { //notImplemented(); @@ -308,14 +298,6 @@ void MediaPlayerPrivate::setMuted(bool muted) m_audioOutput->setMuted(muted); } - -int MediaPlayerPrivate::dataRate() const -{ - // This is not used at the moment - return 0; -} - - MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const { const QMetaObject* metaObj = this->metaObject(); @@ -502,7 +484,7 @@ void MediaPlayerPrivate::aboutToFinish() void MediaPlayerPrivate::totalTimeChanged(qint64 totalTime) { - LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%d)", totalTime); + LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%lld)", totalTime); LOG_MEDIAOBJECT(); } diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h index e1193b6..e7630a1 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h @@ -94,21 +94,17 @@ namespace WebCore { 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; PassRefPtr<TimeRanges> buffered() const; float maxTimeSeekable() const; unsigned bytesLoaded() const; - bool totalBytesKnown() const; unsigned totalBytes() const; void setVisible(bool); diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index e5cecc8..4716d32 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -51,38 +51,32 @@ namespace WebCore { Path::Path() - : m_path(new QPainterPath()) { } Path::~Path() { - delete m_path; } Path::Path(const Path& other) - : m_path(new QPainterPath(*other.platformPath())) + : m_path(other.m_path) { } Path& Path::operator=(const Path& other) { - if (&other != this) { - delete m_path; - m_path = new QPainterPath(*other.platformPath()); - } - + m_path = other.m_path; return *this; } bool Path::contains(const FloatPoint& point, WindRule rule) const { - Qt::FillRule savedRule = m_path->fillRule(); - m_path->setFillRule(rule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill); + Qt::FillRule savedRule = m_path.fillRule(); + const_cast<QPainterPath*>(&m_path)->setFillRule(rule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill); - bool contains = m_path->contains(point); + bool contains = m_path.contains(point); - m_path->setFillRule(savedRule); + const_cast<QPainterPath*>(&m_path)->setFillRule(savedRule); return contains; } @@ -105,19 +99,19 @@ bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) stroke.setDashPattern(pen.dashPattern()); stroke.setDashOffset(pen.dashOffset()); - return (stroke.createStroke(*platformPath())).contains(point); + return stroke.createStroke(m_path).contains(point); } void Path::translate(const FloatSize& size) { QTransform matrix; matrix.translate(size.width(), size.height()); - *m_path = (*m_path) * matrix; + m_path = m_path * matrix; } FloatRect Path::boundingRect() const { - return m_path->boundingRect(); + return m_path.boundingRect(); } FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) @@ -138,35 +132,35 @@ FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) stroke.setDashPattern(pen.dashPattern()); stroke.setDashOffset(pen.dashOffset()); } - return (stroke.createStroke(*platformPath())).boundingRect(); + return stroke.createStroke(m_path).boundingRect(); } void Path::moveTo(const FloatPoint& point) { - m_path->moveTo(point); + m_path.moveTo(point); } void Path::addLineTo(const FloatPoint& p) { - m_path->lineTo(p); + m_path.lineTo(p); } void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p) { - m_path->quadTo(cp, p); + m_path.quadTo(cp, p); } void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p) { - m_path->cubicTo(cp1, cp2, p); + m_path.cubicTo(cp1, cp2, p); } void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) { - FloatPoint p0(m_path->currentPosition()); + FloatPoint p0(m_path.currentPosition()); if ((p1.x() == p0.x() && p1.y() == p0.y()) || (p1.x() == p2.x() && p1.y() == p2.y()) || radius == 0.f) { - m_path->lineTo(p1); + m_path.lineTo(p1); return; } @@ -178,7 +172,7 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); // all points on a line logic if (cos_phi == -1) { - m_path->lineTo(p1); + m_path.lineTo(p1); return; } if (cos_phi == 1) { @@ -186,7 +180,7 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) unsigned int max_length = 65535; double factor_max = max_length / p1p0_length; FloatPoint ep((p0.x() + factor_max * p1p0.x()), (p0.y() + factor_max * p1p0.y())); - m_path->lineTo(ep); + m_path.lineTo(ep); return; } @@ -226,14 +220,14 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) if ((sa < ea) && ((ea - sa) > piDouble)) anticlockwise = true; - m_path->lineTo(t_p1p0); + m_path.lineTo(t_p1p0); addArc(p, radius, sa, ea, anticlockwise); } void Path::closeSubpath() { - m_path->closeSubpath(); + m_path.closeSubpath(); } #define DEGREES(t) ((t) * 180.0 / M_PI) @@ -275,32 +269,32 @@ void Path::addArc(const FloatPoint& p, float r, float sar, float ear, bool antic span += ea - sa; } - m_path->moveTo(QPointF(xc + radius * cos(sar), + m_path.moveTo(QPointF(xc + radius * cos(sar), yc - radius * sin(sar))); - m_path->arcTo(xs, ys, width, height, sa, span); + m_path.arcTo(xs, ys, width, height, sa, span); } void Path::addRect(const FloatRect& r) { - m_path->addRect(r.x(), r.y(), r.width(), r.height()); + m_path.addRect(r.x(), r.y(), r.width(), r.height()); } void Path::addEllipse(const FloatRect& r) { - m_path->addEllipse(r.x(), r.y(), r.width(), r.height()); + m_path.addEllipse(r.x(), r.y(), r.width(), r.height()); } void Path::clear() { - *m_path = QPainterPath(); + m_path = QPainterPath(); } bool Path::isEmpty() const { // 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(); + return !m_path.elementCount(); } bool Path::hasCurrentPoint() const @@ -311,8 +305,8 @@ bool Path::hasCurrentPoint() const String Path::debugString() const { QString ret; - for (int i = 0; i < m_path->elementCount(); ++i) { - const QPainterPath::Element &cur = m_path->elementAt(i); + for (int i = 0; i < m_path.elementCount(); ++i) { + const QPainterPath::Element &cur = m_path.elementAt(i); switch (cur.type) { case QPainterPath::MoveToElement: @@ -323,8 +317,8 @@ String Path::debugString() const break; case QPainterPath::CurveToElement: { - const QPainterPath::Element &c1 = m_path->elementAt(i + 1); - const QPainterPath::Element &c2 = m_path->elementAt(i + 2); + const QPainterPath::Element &c1 = m_path.elementAt(i + 1); + const QPainterPath::Element &c2 = m_path.elementAt(i + 2); Q_ASSERT(c1.type == QPainterPath::CurveToDataElement); Q_ASSERT(c2.type == QPainterPath::CurveToDataElement); @@ -348,8 +342,8 @@ void Path::apply(void* info, PathApplierFunction function) const PathElement pelement; FloatPoint points[3]; pelement.points = points; - for (int i = 0; i < m_path->elementCount(); ++i) { - const QPainterPath::Element& cur = m_path->elementAt(i); + for (int i = 0; i < m_path.elementCount(); ++i) { + const QPainterPath::Element& cur = m_path.elementAt(i); switch (cur.type) { case QPainterPath::MoveToElement: @@ -364,8 +358,8 @@ void Path::apply(void* info, PathApplierFunction function) const break; case QPainterPath::CurveToElement: { - const QPainterPath::Element& c1 = m_path->elementAt(i + 1); - const QPainterPath::Element& c2 = m_path->elementAt(i + 2); + const QPainterPath::Element& c1 = m_path.elementAt(i + 1); + const QPainterPath::Element& c2 = m_path.elementAt(i + 2); Q_ASSERT(c1.type == QPainterPath::CurveToDataElement); Q_ASSERT(c2.type == QPainterPath::CurveToDataElement); @@ -387,12 +381,7 @@ void Path::apply(void* info, PathApplierFunction function) const void Path::transform(const TransformationMatrix& transform) { - if (m_path) { - QTransform mat = transform; - QPainterPath temp = mat.map(*m_path); - delete m_path; - m_path = new QPainterPath(temp); - } + m_path = QTransform(transform).map(m_path); } } diff --git a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp index f1536a6..985442c 100644 --- a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp +++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -86,7 +86,10 @@ inline float square(float n) // Ideally, all of these would be fixed in the graphics layer and we would not // have to do any checking. You can uncomment the ENSURE_VALUE_SAFETY_FOR_SKIA // flag to check the graphics layer. -#define ENSURE_VALUE_SAFETY_FOR_SKIA + +// Disabling these checks (20/01/2010), since we think we've fixed all the Skia +// bugs. Leaving the code in for now, so we can revert easily if necessary. +// #define ENSURE_VALUE_SAFETY_FOR_SKIA static bool isCoordinateSkiaSafe(float coord) { @@ -431,7 +434,7 @@ void GraphicsContext::clipToImageBuffer(const FloatRect& rect, if (paintingDisabled()) return; -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) platformContext()->beginLayerClippedToImage(rect, imageBuffer); #endif } @@ -498,12 +501,16 @@ void GraphicsContext::drawEllipse(const IntRect& elipseRect) } } -void GraphicsContext::drawFocusRing(const Color& color) +void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) +{ + // FIXME: implement +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color) { if (paintingDisabled()) return; - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); if (!rectCount) return; diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp index c36f1ce..4ea3d7a 100644 --- a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -66,7 +66,7 @@ ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, b m_data.m_platformContext.setCanvas(&m_data.m_canvas); m_context.set(new GraphicsContext(&m_data.m_platformContext)); -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) m_context->platformContext()->setDrawingToImageBuffer(true); #endif diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index dfffa0d..92a1870 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -89,7 +89,7 @@ struct PlatformContextSkia::State { // color to produce a new output color. SkColor applyAlpha(SkColor) const; -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) // If non-empty, the current State is clipped to this image. SkBitmap m_imageBufferClip; // If m_imageBufferClip is non-empty, this is the region the image is clipped to. @@ -143,7 +143,7 @@ PlatformContextSkia::State::State(const State& other) , m_lineJoin(other.m_lineJoin) , m_dash(other.m_dash) , m_textDrawingMode(other.m_textDrawingMode) -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) , m_imageBufferClip(other.m_imageBufferClip) , m_clip(other.m_clip) #endif @@ -180,7 +180,7 @@ SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const // Danger: canvas can be NULL. PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) : m_canvas(canvas) -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) , m_drawingToImageBuffer(false) #endif { @@ -197,7 +197,7 @@ void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) m_canvas = canvas; } -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) void PlatformContextSkia::setDrawingToImageBuffer(bool value) { m_drawingToImageBuffer = value; @@ -214,7 +214,7 @@ void PlatformContextSkia::save() m_stateStack.append(*m_state); m_state = &m_stateStack.last(); -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) // The clip image only needs to be applied once. Reset the image so that we // don't attempt to clip multiple times. m_state->m_imageBufferClip.reset(); @@ -224,7 +224,7 @@ void PlatformContextSkia::save() canvas()->save(); } -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect, const WebCore::ImageBuffer* imageBuffer) { @@ -234,7 +234,8 @@ void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rec m_state->m_clip = rect; SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) }; - + + canvas()->clipRect(bounds); canvas()->saveLayerAlpha(&bounds, 255, static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag)); // Copy off the image as |imageBuffer| may be deleted before restore is invoked. @@ -271,7 +272,7 @@ void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath) void PlatformContextSkia::restore() { -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) if (!m_state->m_imageBufferClip.empty()) { applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip); canvas()->restore(); @@ -574,7 +575,7 @@ bool PlatformContextSkia::isPrinting() return m_canvas->getTopPlatformDevice().IsVectorial(); } -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, const SkBitmap& imageBuffer) { // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h index 53590bf..e445262 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -71,7 +71,7 @@ public: // to the constructor. void setCanvas(skia::PlatformCanvas*); -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) // If false we're rendering to a GraphicsContext for a web page, if false // we're not (as is the case when rendering to a canvas object). // If this is true the contents have not been marked up with the magic @@ -88,7 +88,7 @@ public: // |rect|. This layer is implicitly restored when the next restore is // invoked. // NOTE: |imageBuffer| may be deleted before the |restore| is invoked. -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) void beginLayerClippedToImage(const WebCore::FloatRect&, const WebCore::ImageBuffer*); #endif @@ -168,7 +168,7 @@ public: bool isPrinting(); private: -#if defined(__linux__) || PLATFORM(WIN_OS) +#if OS(LINUX) || OS(WINDOWS) // Used when restoring and the state has an image clip. Only shows the pixels in // m_canvas that are also in imageBuffer. void applyClipFromImage(const WebCore::FloatRect&, const SkBitmap&); @@ -191,7 +191,7 @@ private: // Current path in global coordinates. SkPath m_path; -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) bool m_drawingToImageBuffer; #endif }; diff --git a/WebCore/platform/graphics/transforms/TransformationMatrix.h b/WebCore/platform/graphics/transforms/TransformationMatrix.h index 802ad3c..9e724d5 100644 --- a/WebCore/platform/graphics/transforms/TransformationMatrix.h +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.h @@ -35,6 +35,8 @@ #include <CoreGraphics/CGAffineTransform.h> #elif PLATFORM(CAIRO) #include <cairo.h> +#elif PLATFORM(OPENVG) +#include "VGUtils.h" #elif PLATFORM(QT) #include <QTransform> #elif PLATFORM(SKIA) @@ -43,6 +45,14 @@ #include <wx/graphics.h> #endif +#if PLATFORM(WIN) || (PLATFORM(QT) && OS(WINDOWS)) || (PLATFORM(WX) && OS(WINDOWS)) +#if COMPILER(MINGW) +typedef struct _XFORM XFORM; +#else +typedef struct tagXFORM XFORM; +#endif +#endif + namespace WebCore { class IntRect; @@ -299,6 +309,8 @@ public: operator CGAffineTransform() const; #elif PLATFORM(CAIRO) operator cairo_matrix_t() const; +#elif PLATFORM(OPENVG) + operator VGMatrix() const; #elif PLATFORM(QT) operator QTransform() const; #elif PLATFORM(SKIA) @@ -307,31 +319,31 @@ public: operator wxGraphicsMatrix() const; #endif -#if PLATFORM(WIN) +#if PLATFORM(WIN) || (PLATFORM(QT) && OS(WINDOWS)) || (PLATFORM(WX) && OS(WINDOWS)) operator XFORM() const; #endif + bool isIdentityOrTranslation() const + { + return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 + && m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0 + && m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 + && m_matrix[3][3] == 1; + } + private: // multiply passed 2D point by matrix (assume z=0) void multVecMatrix(double x, double y, double& dstX, double& dstY) const; - + // multiply passed 3D point by matrix void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const; - + void setMatrix(const Matrix4 m) { if (m && m != m_matrix) memcpy(m_matrix, m, sizeof(Matrix4)); } - - bool isIdentityOrTranslation() const - { - return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 && - m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0 && - m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 && - m_matrix[3][3] == 1; - } - + Matrix4 m_matrix; }; diff --git a/WebCore/platform/graphics/win/FontCGWin.cpp b/WebCore/platform/graphics/win/FontCGWin.cpp index e901669..653b573 100644 --- a/WebCore/platform/graphics/win/FontCGWin.cpp +++ b/WebCore/platform/graphics/win/FontCGWin.cpp @@ -322,7 +322,8 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo } if (font->platformData().useGDI()) { - if (!shouldUseFontSmoothing || (graphicsContext->textDrawingMode() & cTextStroke)) { + static bool canCreateCGFontWithLOGFONT = wkCanCreateCGFontWithLOGFONT(); + if (!shouldUseFontSmoothing || !canCreateCGFontWithLOGFONT && (graphicsContext->textDrawingMode() & cTextStroke)) { drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); return; } diff --git a/WebCore/platform/graphics/win/FontCacheWin.cpp b/WebCore/platform/graphics/win/FontCacheWin.cpp index 8663623..5e61ef3 100644 --- a/WebCore/platform/graphics/win/FontCacheWin.cpp +++ b/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -399,7 +399,7 @@ static int CALLBACK matchImprovingEnumProc(CONST LOGFONT* candidate, CONST TEXTM return 1; } -static HFONT createGDIFont(const AtomicString& family, LONG desiredWeight, bool desiredItalic, int size) +static HFONT createGDIFont(const AtomicString& family, LONG desiredWeight, bool desiredItalic, int size, bool synthesizeItalic) { HDC hdc = GetDC(0); @@ -433,6 +433,9 @@ static HFONT createGDIFont(const AtomicString& family, LONG desiredWeight, bool matchData.m_chosen.lfQuality = DEFAULT_QUALITY; matchData.m_chosen.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + if (desiredItalic && !matchData.m_chosen.lfItalic && synthesizeItalic) + matchData.m_chosen.lfItalic = 1; + HFONT result = CreateFontIndirect(&matchData.m_chosen); if (!result) return 0; @@ -514,8 +517,14 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD // This masks rounding errors related to the HFONT metrics being different from the CGFont metrics. // FIXME: We will eventually want subpixel precision for GDI mode, but the scaled rendering doesn't // look as nice. That may be solvable though. +#if PLATFORM(CG) + bool canCreateCGFontWithLOGFONT = wkCanCreateCGFontWithLOGFONT(); +#else + bool canCreateCGFontWithLOGFONT = true; +#endif LONG weight = adjustedGDIFontWeight(toGDIFontWeight(fontDescription.weight()), family); - HFONT hfont = createGDIFont(family, weight, fontDescription.italic(), fontDescription.computedPixelSize() * (useGDI ? 1 : 32)); + HFONT hfont = createGDIFont(family, weight, fontDescription.italic(), + fontDescription.computedPixelSize() * (useGDI ? 1 : 32), useGDI && canCreateCGFontWithLOGFONT); if (!hfont) return 0; diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp index 24db173..b2d1b32 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -61,7 +61,7 @@ FontCustomPlatformData::~FontCustomPlatformData() FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode renderingMode) { - ASSERT(m_cgFont); + ASSERT(wkCanCreateCGFontWithLOGFONT() || m_cgFont); ASSERT(m_fontReference); ASSERT(T2embedLibrary()); @@ -87,6 +87,12 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b logFont.lfWeight = bold ? 700 : 400; HFONT hfont = CreateFontIndirect(&logFont); + + if (wkCanCreateCGFontWithLOGFONT()) { + RetainPtr<CGFontRef> cgFont(AdoptCF, CGFontCreateWithPlatformFont(&logFont)); + return FontPlatformData(hfont, cgFont.get(), size, bold, italic, renderingMode == AlternateRenderingMode); + } + wkSetFontPlatformInfo(m_cgFont, &logFont, free); return FontPlatformData(hfont, m_cgFont, size, bold, italic, renderingMode == AlternateRenderingMode); } @@ -190,12 +196,15 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) ASSERT_ARG(buffer, buffer); ASSERT(T2embedLibrary()); - // Get CG to create the font. - CGDataProviderDirectAccessCallbacks callbacks = { &getData, &releaseData, &getBytesWithOffset, NULL }; - RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateDirectAccess(buffer, buffer->size(), &callbacks)); - CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider.get()); - if (!cgFont) - return 0; + RetainPtr<CGFontRef> cgFont; + if (!wkCanCreateCGFontWithLOGFONT()) { + // Get CG to create the font. + CGDataProviderDirectAccessCallbacks callbacks = { &getData, &releaseData, &getBytesWithOffset, NULL }; + RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateDirectAccess(buffer, buffer->size(), &callbacks)); + cgFont.adoptCF(CGFontCreateWithDataProvider(dataProvider.get())); + if (!cgFont) + return 0; + } // Introduce the font to GDI. AddFontMemResourceEx cannot be used, because it will pollute the process's // font namespace (Windows has no API for creating an HFONT from data without exposing the font to the @@ -210,10 +219,8 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) size_t overlayDst; size_t overlaySrc; size_t overlayLength; - if (!getEOTHeader(buffer, eotHeader, overlayDst, overlaySrc, overlayLength)) { - CGFontRelease(cgFont); + if (!getEOTHeader(buffer, eotHeader, overlayDst, overlaySrc, overlayLength)) return 0; - } HANDLE fontReference; ULONG privStatus; @@ -225,13 +232,11 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) fontName = String(); else { fontReference = renameAndActivateFont(buffer, fontName); - if (!fontReference) { - CGFontRelease(cgFont); + if (!fontReference) return 0; - } } - return new FontCustomPlatformData(cgFont, fontReference, fontName); + return new FontCustomPlatformData(cgFont.releaseRef(), fontReference, fontName); } } diff --git a/WebCore/platform/graphics/win/FontDatabase.cpp b/WebCore/platform/graphics/win/FontDatabase.cpp index d0773ea..22ad4a6 100644 --- a/WebCore/platform/graphics/win/FontDatabase.cpp +++ b/WebCore/platform/graphics/win/FontDatabase.cpp @@ -188,6 +188,9 @@ void populateFontDatabase() return; initialized = true; + if (wkCanCreateCGFontWithLOGFONT()) + return; + RetainPtr<CFPropertyListRef> propertyList = readFontPlist(); RetainPtr<CFArrayRef> lastFilenamesFromRegistry; if (propertyList && CFGetTypeID(propertyList.get()) == CFDictionaryGetTypeID()) { diff --git a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp index a92e367..d605cf9 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp @@ -109,6 +109,13 @@ static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc) void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName) { + if (wkCanCreateCGFontWithLOGFONT()) { + LOGFONT logfont; + GetObject(font, sizeof(logfont), &logfont); + m_cgFont.adoptCF(CGFontCreateWithPlatformFont(&logfont)); + return; + } + // Try the face name first. Windows may end up localizing this name, and CG doesn't know about // the localization. If the create fails, we'll try the PostScript name. RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName))); diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index 137b914..47a51de 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -124,17 +124,21 @@ void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& po CGContextDrawImage(m_data->m_cgContext.get(), CGRectMake(point.x(), point.y(), image->size().width(), image->size().height()), cgImage.get()); } -void GraphicsContext::drawFocusRing(const Color& color) +void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) +{ + // FIXME: implement +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) { if (paintingDisabled()) return; - float radius = (focusRingWidth() - 1) / 2.0f; - int offset = radius + focusRingOffset(); + float radius = (width - 1) / 2.0f; + offset += radius; CGColorRef colorRef = color.isValid() ? createCGColor(color) : 0; CGMutablePathRef focusRingPath = CGPathCreateMutable(); - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); for (unsigned i = 0; i < rectCount; i++) CGPathAddRect(focusRingPath, 0, CGRectInset(rects[i], -offset, -offset)); diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index 61ae76c..43d92fb 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -40,21 +40,26 @@ static cairo_t* createCairoContextWithHDC(HDC hdc, bool hasAlpha) { // Put the HDC In advanced mode so it will honor affine transforms. SetGraphicsMode(hdc, GM_ADVANCED); - + + cairo_surface_t* surface = 0; + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); BITMAP info; - GetObject(bitmap, sizeof(info), &info); - ASSERT(info.bmBitsPixel == 32); + if (!GetObject(bitmap, sizeof(info), &info)) + surface = cairo_win32_surface_create(hdc); + else { + ASSERT(info.bmBitsPixel == 32); - cairo_surface_t* image = cairo_image_surface_create_for_data((unsigned char*)info.bmBits, + surface = cairo_image_surface_create_for_data((unsigned char*)info.bmBits, CAIRO_FORMAT_ARGB32, info.bmWidth, info.bmHeight, info.bmWidthBytes); + } - cairo_t* context = cairo_create(image); - cairo_surface_destroy(image); + cairo_t* context = cairo_create(surface); + cairo_surface_destroy(surface); return context; } diff --git a/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp b/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp index 22faeb8..5ec90b8 100644 --- a/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp +++ b/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp @@ -36,6 +36,7 @@ #include "PlatformString.h" #include "SystemTime.h" #include "WKCACFLayer.h" +#include <QuartzCoreInterface/QuartzCoreInterface.h> #include <wtf/CurrentTime.h> #include <wtf/StringExtras.h> @@ -123,7 +124,7 @@ GraphicsLayerCACF::GraphicsLayerCACF(GraphicsLayerClient* client) , m_contentsLayerPurpose(NoContentsLayer) , m_contentsLayerHasBackgroundColor(false) { - m_layer = WKCACFLayer::create(kCACFLayer, this); + m_layer = WKCACFLayer::create(WKCACFLayer::Layer, this); updateDebugIndicators(); } @@ -536,7 +537,7 @@ void GraphicsLayerCACF::updateLayerPreserves3D() { if (m_preserves3D && !m_transformLayer) { // Create the transform layer. - m_transformLayer = WKCACFLayer::create(kCACFTransformLayer, this); + m_transformLayer = WKCACFLayer::create(WKCACFLayer::TransformLayer, this); #ifndef NDEBUG m_transformLayer->setName(String().format("Transform Layer CATransformLayer(%p) GraphicsLayer(%p)", m_transformLayer.get(), this)); @@ -552,7 +553,7 @@ void GraphicsLayerCACF::updateLayerPreserves3D() m_layer->setPosition(point); m_layer->setAnchorPoint(CGPointMake(0.5f, 0.5f)); - m_layer->setTransform(CATransform3DIdentity); + m_layer->setTransform(wkqcCATransform3DIdentity()); // Set the old layer to opacity of 1. Further down we will set the opacity on the transform layer. m_layer->setOpacity(1); @@ -609,7 +610,7 @@ void GraphicsLayerCACF::updateContentsImage() { if (m_pendingContentsImage) { if (!m_contentsLayer.get()) { - RefPtr<WKCACFLayer> imageLayer = WKCACFLayer::create(kCACFLayer, this); + RefPtr<WKCACFLayer> imageLayer = WKCACFLayer::create(WKCACFLayer::Layer, this); #ifndef NDEBUG imageLayer->setName("Image Layer"); #endif @@ -620,7 +621,7 @@ void GraphicsLayerCACF::updateContentsImage() // FIXME: maybe only do trilinear if the image is being scaled down, // but then what if the layer size changes? - m_contentsLayer->setMinificationFilter(kCACFFilterTrilinear); + m_contentsLayer->setMinificationFilter(WKCACFLayer::Trilinear); m_contentsLayer->setContents(m_pendingContentsImage.get()); m_pendingContentsImage = 0; diff --git a/WebCore/platform/graphics/win/IconWin.cpp b/WebCore/platform/graphics/win/IconWin.cpp index d71ca00..56b46de 100644 --- a/WebCore/platform/graphics/win/IconWin.cpp +++ b/WebCore/platform/graphics/win/IconWin.cpp @@ -27,7 +27,7 @@ #include <tchar.h> #include <windows.h> -#if PLATFORM(WINCE) +#if OS(WINCE) // SHGFI_SHELLICONSIZE is not available on WINCE #define SHGFI_SHELLICONSIZE 0 #endif @@ -63,7 +63,7 @@ PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) return adoptRef(new Icon(sfi.hIcon)); } -#if PLATFORM(WINCE) +#if OS(WINCE) return 0; #else TCHAR buffer[MAX_PATH]; @@ -86,7 +86,7 @@ void Icon::paint(GraphicsContext* context, const IntRect& r) if (context->paintingDisabled()) return; -#if PLATFORM(WINCE) +#if OS(WINCE) context->drawIcon(m_hIcon, r, DI_NORMAL); #else HDC hdc = context->getWindowsContext(r); diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index a5beea1..b2fe069 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple, Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,10 +35,16 @@ #include "StringHash.h" #include "TimeRanges.h" #include "Timer.h" +#include <wtf/CurrentTime.h> #include <wtf/HashSet.h> #include <wtf/MathExtras.h> #include <wtf/StdLibExtras.h> +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayerCACF.h" +#include "WKCACFLayer.h" +#endif + #if DRAW_FRAME_RATE #include "Font.h" #include "FrameView.h" @@ -67,7 +73,6 @@ void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_seekTo(-1) - , m_endTime(numeric_limits<float>::infinity()) , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) , m_networkState(MediaPlayer::Empty) , m_readyState(MediaPlayer::HaveNothing) @@ -76,6 +81,8 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_hasUnsupportedTracks(false) , m_startedPlaying(false) , m_isStreaming(false) + , m_visible(false) + , m_newFrameAvailable(false) #if DRAW_FRAME_RATE , m_frameCountWhilePlaying(0) , m_timeStartedPlaying(0) @@ -86,6 +93,19 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) MediaPlayerPrivate::~MediaPlayerPrivate() { + tearDownVideoRendering(); +} + +bool MediaPlayerPrivate::supportsFullscreen() const +{ + return true; +} + +PlatformMedia MediaPlayerPrivate::platformMedia() const +{ + PlatformMedia p; + p.qtMovie = reinterpret_cast<QTMovie*>(m_qtMovie.get()); + return p; } class TaskTimer : TimerBase { @@ -178,7 +198,7 @@ void MediaPlayerPrivate::pause() return; m_startedPlaying = false; #if DRAW_FRAME_RATE - m_timeStoppedPlaying = GetTickCount(); + m_timeStoppedPlaying = WTF::currentTime(); #endif m_qtMovie->pause(); } @@ -194,7 +214,7 @@ float MediaPlayerPrivate::currentTime() const { if (!m_qtMovie) return 0; - return min(m_qtMovie->currentTime(), m_endTime); + return m_qtMovie->currentTime(); } void MediaPlayerPrivate::seek(float time) @@ -222,7 +242,7 @@ void MediaPlayerPrivate::doSeek() m_qtMovie->setCurrentTime(m_seekTo); float timeAfterSeek = currentTime(); // restore playback only if not at end, othewise QTMovie will loop - if (oldRate && timeAfterSeek < duration() && timeAfterSeek < m_endTime) + if (oldRate && timeAfterSeek < duration()) m_qtMovie->setRate(oldRate); cancelSeek(); } @@ -254,11 +274,6 @@ void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) } } -void MediaPlayerPrivate::setEndTime(float time) -{ - m_endTime = time; -} - bool MediaPlayerPrivate::paused() const { if (!m_qtMovie) @@ -332,12 +347,6 @@ void MediaPlayerPrivate::setClosedCaptionsVisible(bool visible) m_qtMovie->setClosedCaptionsVisible(visible); } -int MediaPlayerPrivate::dataRate() const -{ - // This is not used at the moment - return 0; -} - PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const { RefPtr<TimeRanges> timeRanges = TimeRanges::create(); @@ -372,11 +381,6 @@ unsigned MediaPlayerPrivate::bytesLoaded() const return totalBytes() * maxTime / dur; } -bool MediaPlayerPrivate::totalBytesKnown() const -{ - return totalBytes() > 0; -} - unsigned MediaPlayerPrivate::totalBytes() const { if (!m_qtMovie) @@ -389,9 +393,11 @@ void MediaPlayerPrivate::cancelLoad() if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) return; + tearDownVideoRendering(); + // Cancel the load by destroying the movie. m_qtMovie.clear(); - + updateStates(); } @@ -454,6 +460,9 @@ void MediaPlayerPrivate::updateStates() } } + if (isReadyForRendering() && !hasSetUpVideoRendering()) + setUpVideoRendering(); + if (seeking()) m_readyState = MediaPlayer::HaveNothing; @@ -463,6 +472,11 @@ void MediaPlayerPrivate::updateStates() m_player->readyStateChanged(); } +bool MediaPlayerPrivate::isReadyForRendering() const +{ + return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); +} + void MediaPlayerPrivate::sawUnsupportedTracks() { m_qtMovie->setDisabled(true); @@ -477,7 +491,7 @@ void MediaPlayerPrivate::didEnd() m_startedPlaying = false; #if DRAW_FRAME_RATE - m_timeStoppedPlaying = GetTickCount(); + m_timeStoppedPlaying = WTF::currentTime(); #endif updateStates(); m_player->timeChanged(); @@ -485,20 +499,32 @@ void MediaPlayerPrivate::didEnd() void MediaPlayerPrivate::setSize(const IntSize& size) { - if (m_hasUnsupportedTracks || !m_qtMovie) + if (m_hasUnsupportedTracks || !m_qtMovie || m_size == size) return; + m_size = size; m_qtMovie->setSize(size.width(), size.height()); } -void MediaPlayerPrivate::setVisible(bool b) +void MediaPlayerPrivate::setVisible(bool visible) { - if (m_hasUnsupportedTracks || !m_qtMovie) + if (m_hasUnsupportedTracks || !m_qtMovie || m_visible == visible) return; - m_qtMovie->setVisible(b); + + m_qtMovie->setVisible(visible); + m_visible = visible; + if (m_visible) { + if (isReadyForRendering()) + setUpVideoRendering(); + } else + tearDownVideoRendering(); } void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) { +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + return; +#endif if (p->paintingDisabled() || !m_qtMovie || m_hasUnsupportedTracks) return; @@ -529,28 +555,50 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) else p->releaseWindowsContext(hdc, r); + paintCompleted(*p, r); +} + +void MediaPlayerPrivate::paintCompleted(GraphicsContext& context, const IntRect& rect) +{ + m_newFrameAvailable = false; + #if DRAW_FRAME_RATE if (m_frameCountWhilePlaying > 10) { - Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL; - Document* document = frame ? frame->document() : NULL; - RenderObject* renderer = document ? document->renderer() : NULL; - RenderStyle* styleToUse = renderer ? renderer->style() : NULL; - if (styleToUse) { - double frameRate = (m_frameCountWhilePlaying - 1) / (0.001 * ( m_startedPlaying ? (GetTickCount() - m_timeStartedPlaying) : - (m_timeStoppedPlaying - m_timeStartedPlaying) )); - String text = String::format("%1.2f", frameRate); - TextRun textRun(text.characters(), text.length()); - const Color color(255, 0, 0); - p->save(); - p->translate(r.x(), r.y() + r.height()); - p->setFont(styleToUse->font()); - p->setStrokeColor(color); - p->setStrokeStyle(SolidStroke); - p->setStrokeThickness(1.0f); - p->setFillColor(color); - p->drawText(textRun, IntPoint(2, -3)); - p->restore(); - } + double interval = m_startedPlaying ? WTF::currentTime() - m_timeStartedPlaying : m_timeStoppedPlaying - m_timeStartedPlaying; + double frameRate = (m_frameCountWhilePlaying - 1) / interval; + CGContextRef cgContext = context.platformContext(); + CGRect drawRect = rect; + + char text[8]; + _snprintf(text, sizeof(text), "%1.2f", frameRate); + + static const int fontSize = 25; + static const int fontCharWidth = 12; + static const int boxHeight = 25; + static const int boxBorderWidth = 4; + drawRect.size.width = boxBorderWidth * 2 + fontCharWidth * strlen(text); + drawRect.size.height = boxHeight; + + CGContextSaveGState(cgContext); +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + CGContextScaleCTM(cgContext, 1, -1); + CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, m_qtVideoLayer ? -rect.height() : 0); +#else + CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, 0); +#endif + static const CGFloat backgroundColor[4] = { 0.98, 0.98, 0.82, 0.8 }; + CGContextSetFillColor(cgContext, backgroundColor); + CGContextFillRect(cgContext, drawRect); + + static const CGFloat textColor[4] = { 0, 0, 0, 1 }; + CGContextSetFillColor(cgContext, textColor); + CGContextSetTextMatrix(cgContext, CGAffineTransformMakeScale(1, -1)); + CGContextSelectFont(cgContext, "Helvetica", fontSize, kCGEncodingMacRoman); + + CGContextShowTextAtPoint(cgContext, drawRect.origin.x + boxBorderWidth, drawRect.origin.y + boxHeight - boxBorderWidth, text, strlen(text)); + + CGContextRestoreGState(cgContext); } #endif } @@ -630,13 +678,21 @@ void MediaPlayerPrivate::movieNewImageAvailable(QTMovieWin* movie) #if DRAW_FRAME_RATE if (m_startedPlaying) { m_frameCountWhilePlaying++; - // to eliminate preroll costs from our calculation, - // our frame rate calculation excludes the first frame drawn after playback starts - if (1==m_frameCountWhilePlaying) - m_timeStartedPlaying = GetTickCount(); + // To eliminate preroll costs from our calculation, our frame rate calculation excludes + // the first frame drawn after playback starts. + if (m_frameCountWhilePlaying == 1) + m_timeStartedPlaying = WTF::currentTime(); } #endif - m_player->repaint(); + + m_newFrameAvailable = true; + +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + m_qtVideoLayer->platformLayer()->setNeedsDisplay(); + else +#endif + m_player->repaint(); } bool MediaPlayerPrivate::hasSingleSecurityOrigin() const @@ -646,6 +702,171 @@ bool MediaPlayerPrivate::hasSingleSecurityOrigin() const return true; } +MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode() const +{ + if (!m_qtMovie) + return MediaRenderingNone; + +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + return MediaRenderingMovieLayer; +#endif + + return MediaRenderingSoftwareRenderer; +} + +MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMode() const +{ + if (!m_player->frameView() || !m_qtMovie) + return MediaRenderingNone; + +#if USE(ACCELERATED_COMPOSITING) + if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) + return MediaRenderingMovieLayer; +#endif + + return MediaRenderingSoftwareRenderer; +} + +void MediaPlayerPrivate::setUpVideoRendering() +{ + MediaRenderingMode currentMode = currentRenderingMode(); + MediaRenderingMode preferredMode = preferredRenderingMode(); + +#if !USE(ACCELERATED_COMPOSITING) + ASSERT(preferredMode != MediaRenderingMovieLayer); +#endif + + if (currentMode == preferredMode && currentMode != MediaRenderingNone) + return; + + if (currentMode != MediaRenderingNone) + tearDownVideoRendering(); + + if (preferredMode == MediaRenderingMovieLayer) + createLayerForMovie(); +} + +void MediaPlayerPrivate::tearDownVideoRendering() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_qtVideoLayer) + destroyLayerForMovie(); +#endif +} + +bool MediaPlayerPrivate::hasSetUpVideoRendering() const +{ +#if USE(ACCELERATED_COMPOSITING) + return m_qtVideoLayer || currentRenderingMode() != MediaRenderingMovieLayer; +#else + return true; +#endif +} + +#if USE(ACCELERATED_COMPOSITING) + +// Up-call from compositing layer drawing callback. +void MediaPlayerPrivate::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase, const IntRect&) +{ + if (m_hasUnsupportedTracks) + return; + + ASSERT(supportsAcceleratedRendering()); + + // No reason to replace the current layer image unless we have something new to show. + if (!m_newFrameAvailable) + return; + + static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + void* buffer; + unsigned bitsPerPixel; + unsigned rowBytes; + unsigned width; + unsigned height; + + m_qtMovie->getCurrentFrameInfo(buffer, bitsPerPixel, rowBytes, width, height); + if (!buffer) + return ; + + RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(0, static_cast<UInt8*>(buffer), rowBytes * height, kCFAllocatorNull)); + RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData(data.get())); + RetainPtr<CGImageRef> frameImage(AdoptCF, CGImageCreate(width, height, 8, bitsPerPixel, rowBytes, colorSpace, + kCGBitmapByteOrder32Little | kCGImageAlphaFirst, provider.get(), 0, false, kCGRenderingIntentDefault)); + if (!frameImage) + return; + + IntRect rect(0, 0, m_size.width(), m_size.height()); + CGContextDrawImage(context.platformContext(), rect, frameImage.get()); + paintCompleted(context, rect); +} +#endif + +void MediaPlayerPrivate::createLayerForMovie() +{ +#if USE(ACCELERATED_COMPOSITING) + ASSERT(supportsAcceleratedRendering()); + + if (!m_qtMovie || m_qtVideoLayer) + return; + + // Do nothing if the parent layer hasn't been set up yet. + GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); + if (!videoGraphicsLayer) + return; + + // Create a GraphicsLayer that won't be inserted directly into the render tree, but will used + // as a wrapper for a WKCACFLayer which gets inserted as the content layer of the video + // renderer's GraphicsLayer. + m_qtVideoLayer.set(new GraphicsLayerCACF(this)); + if (!m_qtVideoLayer) + return; + + // Mark the layer as drawing itself, anchored in the top left, and bottom-up. + m_qtVideoLayer->setDrawsContent(true); + m_qtVideoLayer->setAnchorPoint(FloatPoint3D()); + m_qtVideoLayer->setContentsOrientation(GraphicsLayer::CompositingCoordinatesBottomUp); +#ifndef NDEBUG + m_qtVideoLayer->setName("Video layer"); +#endif + + // Hang the video layer from the render layer. + videoGraphicsLayer->setContentsToMedia(m_qtVideoLayer->platformLayer()); +#endif +} + +void MediaPlayerPrivate::destroyLayerForMovie() +{ +#if USE(ACCELERATED_COMPOSITING) + if (!m_qtVideoLayer) + return; + m_qtVideoLayer = 0; +#endif +} + +#if USE(ACCELERATED_COMPOSITING) +bool MediaPlayerPrivate::supportsAcceleratedRendering() const +{ + return isReadyForRendering(); +} + +void MediaPlayerPrivate::acceleratedRenderingStateChanged() +{ + // Set up or change the rendering path if necessary. + setUpVideoRendering(); +} + +void MediaPlayerPrivate::notifySyncRequired(const GraphicsLayer*) +{ + GraphicsLayerCACF* videoGraphicsLayer = static_cast<GraphicsLayerCACF*>(m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player)); + if (videoGraphicsLayer) + videoGraphicsLayer->notifySyncRequired(); + } + + +#endif + + } #endif diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h index 2bccbbf..d58f44f 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,11 +32,18 @@ #include "Timer.h" #include <QTMovieWin.h> #include <wtf/OwnPtr.h> +#include <wtf/RetainPtr.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayerClient.h" +#endif #ifndef DRAW_FRAME_RATE #define DRAW_FRAME_RATE 0 #endif +typedef struct CGImage *CGImageRef; + namespace WebCore { class GraphicsContext; @@ -44,15 +51,32 @@ class IntSize; class IntRect; class String; -class MediaPlayerPrivate : public MediaPlayerPrivateInterface, public QTMovieWinClient { +class MediaPlayerPrivate : public MediaPlayerPrivateInterface, public QTMovieWinClient +#if USE(ACCELERATED_COMPOSITING) + , public GraphicsLayerClient +#endif +{ public: static void registerMediaEngine(MediaEngineRegistrar); ~MediaPlayerPrivate(); private: + +#if USE(ACCELERATED_COMPOSITING) + // GraphicsLayerClient methods + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip); + virtual void notifyAnimationStarted(const GraphicsLayer*, double time) { } + virtual void notifySyncRequired(const GraphicsLayer*); + virtual bool showDebugBorders() const { return false; } + virtual bool showRepaintCounter() const { return false; } +#endif + MediaPlayerPrivate(MediaPlayer*); + virtual bool supportsFullscreen() const; + virtual PlatformMedia platformMedia() const; + IntSize naturalSize() const; bool hasVideo() const; bool hasAudio() const; @@ -69,21 +93,17 @@ private: float duration() const; float currentTime() const; void seek(float time); - void setEndTime(float); void setRate(float); void setVolume(float); void setPreservesPitch(bool); - int dataRate() const; - MediaPlayer::NetworkState networkState() const { return m_networkState; } MediaPlayer::ReadyState readyState() const { return m_readyState; } PassRefPtr<TimeRanges> buffered() const; float maxTimeSeekable() const; unsigned bytesLoaded() const; - bool totalBytesKnown() const; unsigned totalBytes() const; void setVisible(bool); @@ -93,7 +113,8 @@ private: void didEnd(); void paint(GraphicsContext*, const IntRect&); - + void paintCompleted(GraphicsContext&, const IntRect&); + bool hasSingleSecurityOrigin() const; bool hasClosedCaptions() const; @@ -117,11 +138,31 @@ private: static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); static bool isAvailable(); +#if USE(ACCELERATED_COMPOSITING) + virtual bool supportsAcceleratedRendering() const; + virtual void acceleratedRenderingStateChanged(); +#endif + + enum MediaRenderingMode { MediaRenderingNone, MediaRenderingSoftwareRenderer, MediaRenderingMovieLayer }; + MediaRenderingMode currentRenderingMode() const; + MediaRenderingMode preferredRenderingMode() const; + bool isReadyForRendering() const; + + void setUpVideoRendering(); + void tearDownVideoRendering(); + bool hasSetUpVideoRendering() const; + + void createLayerForMovie(); + void destroyLayerForMovie(); + MediaPlayer* m_player; OwnPtr<QTMovieWin> m_qtMovie; +#if USE(ACCELERATED_COMPOSITING) + OwnPtr<GraphicsLayer> m_qtVideoLayer; +#endif float m_seekTo; - float m_endTime; Timer<MediaPlayerPrivate> m_seekTimer; + IntSize m_size; MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; unsigned m_enabledTrackCount; @@ -129,10 +170,12 @@ private: bool m_hasUnsupportedTracks; bool m_startedPlaying; bool m_isStreaming; + bool m_visible; + bool m_newFrameAvailable; #if DRAW_FRAME_RATE - int m_frameCountWhilePlaying; - int m_timeStartedPlaying; - int m_timeStoppedPlaying; + double m_frameCountWhilePlaying; + double m_timeStartedPlaying; + double m_timeStoppedPlaying; #endif }; diff --git a/WebCore/platform/graphics/win/QTMovieWin.cpp b/WebCore/platform/graphics/win/QTMovieWin.cpp index 2d4c2ea..8fd6c71 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.cpp +++ b/WebCore/platform/graphics/win/QTMovieWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple, Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,18 +24,15 @@ */ #include "config.h" -#include <windows.h> - #include "QTMovieWin.h" // Put Movies.h first so build failures here point clearly to QuickTime #include <Movies.h> -#include <QuickTimeComponents.h> -#include <GXMath.h> -#include <QTML.h> #include "QTMovieWinTimer.h" - +#include <GXMath.h> +#include <QTML.h> +#include <QuickTimeComponents.h> #include <wtf/Assertions.h> #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> @@ -50,6 +47,7 @@ static const long subTitleTrackType = 'sbtl'; static const long mpeg4ObjectDescriptionTrackType = 'odsm'; static const long mpeg4SceneDescriptionTrackType = 'sdsm'; static const long closedCaptionDisplayPropertyID = 'disp'; +static LPCTSTR fullscreenQTMovieWinPointerProp = TEXT("fullscreenQTMovieWinPointer"); // Resizing GWorlds is slow, give them a minimum size so size of small // videos can be animated smoothly @@ -130,6 +128,11 @@ public: #if !ASSERT_DISABLED bool m_scaleCached; #endif + WindowPtr m_fullscreenWindow; + GWorldPtr m_fullscreenOrigGWorld; + Rect m_fullscreenRect; + QTMovieWinFullscreenClient* m_fullscreenClient; + char* m_fullscreenRestoreState; }; QTMovieWinPrivate::QTMovieWinPrivate() @@ -159,11 +162,19 @@ QTMovieWinPrivate::QTMovieWinPrivate() #if !ASSERT_DISABLED , m_scaleCached(false) #endif + , m_fullscreenWindow(0) + , m_fullscreenOrigGWorld(0) + , m_fullscreenClient(0) + , m_fullscreenRestoreState(0) { + Rect rect = { 0, 0, 0, 0 }; + m_fullscreenRect = rect; } QTMovieWinPrivate::~QTMovieWinPrivate() { + ASSERT(!m_fullscreenWindow); + endTask(); if (m_gWorld) deleteGWorld(); @@ -359,7 +370,7 @@ void QTMovieWinPrivate::createGWorld() bounds.left = 0; bounds.right = m_gWorldWidth; bounds.bottom = m_gWorldHeight; - OSErr err = QTNewGWorld(&m_gWorld, k32BGRAPixelFormat, &bounds, NULL, NULL, NULL); + OSErr err = QTNewGWorld(&m_gWorld, k32BGRAPixelFormat, &bounds, 0, 0, 0); if (err) return; GetMovieGWorld(m_movie, &m_savedGWorld, 0); @@ -597,6 +608,24 @@ void QTMovieWin::setVisible(bool b) m_private->updateGWorld(); } +void QTMovieWin::getCurrentFrameInfo(void*& buffer, unsigned& bitsPerPixel, unsigned& rowBytes, unsigned& width, unsigned& height) +{ + if (!m_private->m_gWorld) { + buffer = 0; + bitsPerPixel = 0; + rowBytes = 0; + width = 0; + height = 0; + return; + } + PixMapHandle offscreenPixMap = GetGWorldPixMap(m_private->m_gWorld); + buffer = (*offscreenPixMap)->baseAddr; + bitsPerPixel = (*offscreenPixMap)->pixelSize; + rowBytes = (*offscreenPixMap)->rowBytes & 0x3FFF; + width = m_private->m_width; + height = m_private->m_height; +} + void QTMovieWin::paint(HDC hdc, int x, int y) { if (!m_private->m_gWorld) @@ -729,7 +758,7 @@ void QTMovieWin::load(CFURLRef url, bool preservesPitch) moviePropCount++; ASSERT(moviePropCount <= sizeof(movieProps)/sizeof(movieProps[0])); - m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, NULL, &m_private->m_movie); + m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, 0, &m_private->m_movie); end: m_private->startTask(); @@ -953,7 +982,7 @@ static void initializeSupportedTypes() continue; if (!(infoCD.componentFlags & hasMovieImportMIMEList)) continue; - QTAtomContainer mimeList = NULL; + QTAtomContainer mimeList = 0; err = MovieImportGetMIMETypeList((ComponentInstance)comp, &mimeList); if (err || !mimeList) continue; @@ -962,7 +991,7 @@ static void initializeSupportedTypes() QTLockContainer(mimeList); int typeCount = QTCountChildrenOfType(mimeList, kParentAtomIsContainer, kMimeInfoMimeTypeTag); for (int typeIndex = 1; typeIndex <= typeCount; typeIndex++) { - QTAtom mimeTag = QTFindChildByIndex(mimeList, 0, kMimeInfoMimeTypeTag, typeIndex, NULL); + QTAtom mimeTag = QTFindChildByIndex(mimeList, 0, kMimeInfoMimeTypeTag, typeIndex, 0); if (!mimeTag) continue; char* atomData; @@ -980,7 +1009,7 @@ static void initializeSupportedTypes() if (strncmp(typeBuffer, "audio/", 6) && strncmp(typeBuffer, "video/", 6)) continue; - CFStringRef cfMimeType = CFStringCreateWithCString(NULL, typeBuffer, kCFStringEncodingUTF8); + CFStringRef cfMimeType = CFStringCreateWithCString(0, typeBuffer, kCFStringEncodingUTF8); if (!cfMimeType) continue; @@ -1040,7 +1069,7 @@ bool QTMovieWin::initializeQuickTime() if (!initialized) { initialized = true; // Initialize and check QuickTime version - OSErr result = InitializeQTML(0); + OSErr result = InitializeQTML(kInitializeQTMLEnableDoubleBufferedSurface); if (result == noErr) result = Gestalt(gestaltQuickTime, &quickTimeVersion); if (result != noErr) { @@ -1058,15 +1087,80 @@ bool QTMovieWin::initializeQuickTime() return initializationSucceeded; } +LRESULT QTMovieWin::fullscreenWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + QTMovieWin* movie = static_cast<QTMovieWin*>(GetProp(wnd, fullscreenQTMovieWinPointerProp)); + + if (message == WM_DESTROY) + RemoveProp(wnd, fullscreenQTMovieWinPointerProp); + + if (!movie) + return DefWindowProc(wnd, message, wParam, lParam); + + return movie->m_private->m_fullscreenClient->fullscreenClientWndProc(wnd, message, wParam, lParam); +} + +HWND QTMovieWin::enterFullscreen(QTMovieWinFullscreenClient* client) +{ + m_private->m_fullscreenClient = client; + + BeginFullScreen(&m_private->m_fullscreenRestoreState, 0, 0, 0, &m_private->m_fullscreenWindow, 0, fullScreenAllowEvents); + QTMLSetWindowWndProc(m_private->m_fullscreenWindow, fullscreenWndProc); + CreatePortAssociation(GetPortNativeWindow(m_private->m_fullscreenWindow), 0, 0); + + GetMovieBox(m_private->m_movie, &m_private->m_fullscreenRect); + GetMovieGWorld(m_private->m_movie, &m_private->m_fullscreenOrigGWorld, 0); + SetMovieGWorld(m_private->m_movie, reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow), GetGWorldDevice(reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow))); + + // Set the size of the box to preserve aspect ratio + Rect rect = m_private->m_fullscreenWindow->portRect; + + float movieRatio = static_cast<float>(m_private->m_width) / m_private->m_height; + int windowWidth = rect.right - rect.left; + int windowHeight = rect.bottom - rect.top; + float windowRatio = static_cast<float>(windowWidth) / windowHeight; + int actualWidth = (windowRatio > movieRatio) ? (windowHeight * movieRatio) : windowWidth; + int actualHeight = (windowRatio < movieRatio) ? (windowWidth / movieRatio) : windowHeight; + int offsetX = (windowWidth - actualWidth) / 2; + int offsetY = (windowHeight - actualHeight) / 2; + + rect.left = offsetX; + rect.right = offsetX + actualWidth; + rect.top = offsetY; + rect.bottom = offsetY + actualHeight; + + SetMovieBox(m_private->m_movie, &rect); + ShowHideTaskBar(true); + + // Set the 'this' pointer on the HWND + HWND wnd = static_cast<HWND>(GetPortNativeWindow(m_private->m_fullscreenWindow)); + SetProp(wnd, fullscreenQTMovieWinPointerProp, static_cast<HANDLE>(this)); + + return wnd; +} + +void QTMovieWin::exitFullscreen() +{ + if (!m_private->m_fullscreenWindow) + return; + + HWND wnd = static_cast<HWND>(GetPortNativeWindow(m_private->m_fullscreenWindow)); + DestroyPortAssociation(reinterpret_cast<CGrafPtr>(m_private->m_fullscreenWindow)); + SetMovieGWorld(m_private->m_movie, m_private->m_fullscreenOrigGWorld, 0); + EndFullScreen(m_private->m_fullscreenRestoreState, 0L); + SetMovieBox(m_private->m_movie, &m_private->m_fullscreenRect); + m_private->m_fullscreenWindow = 0; +} + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { - case DLL_PROCESS_ATTACH: - return TRUE; - case DLL_PROCESS_DETACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - return FALSE; + case DLL_PROCESS_ATTACH: + return TRUE; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + return FALSE; } ASSERT_NOT_REACHED(); return FALSE; diff --git a/WebCore/platform/graphics/win/QTMovieWin.h b/WebCore/platform/graphics/win/QTMovieWin.h index 778f9aa..d2a7ed0 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.h +++ b/WebCore/platform/graphics/win/QTMovieWin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple, Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,6 +27,7 @@ #define QTMovieWin_h #include <Unicode.h> +#include <windows.h> #ifdef QTMOVIEWIN_EXPORTS #define QTMOVIEWIN_API __declspec(dllexport) @@ -45,6 +46,11 @@ public: virtual void movieNewImageAvailable(QTMovieWin*) = 0; }; +class QTMovieWinFullscreenClient { +public: + virtual LRESULT fullscreenClientWndProc(HWND, UINT message, WPARAM, LPARAM) = 0; +}; + enum { QTMovieLoadStateError = -1L, QTMovieLoadStateLoaded = 2000L, @@ -91,6 +97,7 @@ public: void setVisible(bool); void paint(HDC, int x, int y); + void getCurrentFrameInfo(void*& buffer, unsigned& bitsPerPixel, unsigned& rowBytes, unsigned& width, unsigned& height); void disableUnsupportedTracks(unsigned& enabledTrackCount, unsigned& totalTrackCount); void setDisabled(bool); @@ -104,8 +111,13 @@ public: static unsigned countSupportedTypes(); static void getSupportedType(unsigned index, const UChar*& str, unsigned& len); + // Returns the full-screen window created + HWND enterFullscreen(QTMovieWinFullscreenClient*); + void exitFullscreen(); + private: void load(CFURLRef, bool preservesPitch); + static LRESULT fullscreenWndProc(HWND, UINT message, WPARAM, LPARAM); QTMovieWinPrivate* m_private; bool m_disabled; diff --git a/WebCore/platform/graphics/win/TransformationMatrixWin.cpp b/WebCore/platform/graphics/win/TransformationMatrixWin.cpp index 38dbfbf..47806a2 100644 --- a/WebCore/platform/graphics/win/TransformationMatrixWin.cpp +++ b/WebCore/platform/graphics/win/TransformationMatrixWin.cpp @@ -26,6 +26,8 @@ #include "config.h" #include "TransformationMatrix.h" +#include <windows.h> + namespace WebCore { TransformationMatrix::operator XFORM() const diff --git a/WebCore/platform/graphics/win/WKCACFLayer.cpp b/WebCore/platform/graphics/win/WKCACFLayer.cpp index 21e010d..ad1fc85 100644 --- a/WebCore/platform/graphics/win/WKCACFLayer.cpp +++ b/WebCore/platform/graphics/win/WKCACFLayer.cpp @@ -30,12 +30,19 @@ #include "WKCACFLayer.h" #include "WKCACFContextFlusher.h" +#include "WKCACFLayerRenderer.h" #include <stdio.h> #include <QuartzCore/CACFContext.h> #include <QuartzCore/CARender.h> +#include <QuartzCoreInterface/QuartzCoreInterface.h> -#pragma comment(lib, "QuartzCore")
+#ifdef DEBUG_ALL +#pragma comment(lib, "QuartzCore_debug") +#else +#pragma comment(lib, "QuartzCore") +#endif +#pragma comment(lib, "QuartzCoreInterface") namespace WebCore { @@ -47,11 +54,135 @@ static void displayInContext(CACFLayerRef layer, CGContextRef context) WKCACFLayer::layer(layer)->display(context); } +#define STATIC_CACF_STRING(name) \ + static CFStringRef name() \ + { \ + static CFStringRef name = wkqcCFStringRef(wkqc##name); \ + return name; \ + } + +STATIC_CACF_STRING(kCACFLayer) +STATIC_CACF_STRING(kCACFTransformLayer) +STATIC_CACF_STRING(kCACFGravityCenter) +STATIC_CACF_STRING(kCACFGravityTop) +STATIC_CACF_STRING(kCACFGravityBottom) +STATIC_CACF_STRING(kCACFGravityLeft) +STATIC_CACF_STRING(kCACFGravityRight) +STATIC_CACF_STRING(kCACFGravityTopLeft) +STATIC_CACF_STRING(kCACFGravityTopRight) +STATIC_CACF_STRING(kCACFGravityBottomLeft) +STATIC_CACF_STRING(kCACFGravityBottomRight) +STATIC_CACF_STRING(kCACFGravityResize) +STATIC_CACF_STRING(kCACFGravityResizeAspect) +STATIC_CACF_STRING(kCACFGravityResizeAspectFill) +STATIC_CACF_STRING(kCACFFilterLinear) +STATIC_CACF_STRING(kCACFFilterNearest) +STATIC_CACF_STRING(kCACFFilterTrilinear) +STATIC_CACF_STRING(kCACFFilterLanczos) + +static CFStringRef toCACFLayerType(WKCACFLayer::LayerType type) +{ + switch (type) { + case WKCACFLayer::Layer: return kCACFLayer(); + case WKCACFLayer::TransformLayer: return kCACFTransformLayer(); + default: return 0; + } +} + +static CFStringRef toCACFContentsGravityType(WKCACFLayer::ContentsGravityType type) +{ + switch (type) { + case WKCACFLayer::Center: return kCACFGravityCenter(); + case WKCACFLayer::Top: return kCACFGravityTop(); + case WKCACFLayer::Bottom: return kCACFGravityBottom(); + case WKCACFLayer::Left: return kCACFGravityLeft(); + case WKCACFLayer::Right: return kCACFGravityRight(); + case WKCACFLayer::TopLeft: return kCACFGravityTopLeft(); + case WKCACFLayer::TopRight: return kCACFGravityTopRight(); + case WKCACFLayer::BottomLeft: return kCACFGravityBottomLeft(); + case WKCACFLayer::BottomRight: return kCACFGravityBottomRight(); + case WKCACFLayer::Resize: return kCACFGravityResize(); + case WKCACFLayer::ResizeAspect: return kCACFGravityResizeAspect(); + case WKCACFLayer::ResizeAspectFill: return kCACFGravityResizeAspectFill(); + default: return 0; + } +} + +static WKCACFLayer::ContentsGravityType fromCACFContentsGravityType(CFStringRef string) +{ + if (CFEqual(string, kCACFGravityTop())) + return WKCACFLayer::Top; + + if (CFEqual(string, kCACFGravityBottom())) + return WKCACFLayer::Bottom; + + if (CFEqual(string, kCACFGravityLeft())) + return WKCACFLayer::Left; + + if (CFEqual(string, kCACFGravityRight())) + return WKCACFLayer::Right; + + if (CFEqual(string, kCACFGravityTopLeft())) + return WKCACFLayer::TopLeft; + + if (CFEqual(string, kCACFGravityTopRight())) + return WKCACFLayer::TopRight; + + if (CFEqual(string, kCACFGravityBottomLeft())) + return WKCACFLayer::BottomLeft; + + if (CFEqual(string, kCACFGravityBottomRight())) + return WKCACFLayer::BottomRight; + + if (CFEqual(string, kCACFGravityResize())) + return WKCACFLayer::Resize; + + if (CFEqual(string, kCACFGravityResizeAspect())) + return WKCACFLayer::ResizeAspect; + + if (CFEqual(string, kCACFGravityResizeAspectFill())) + return WKCACFLayer::ResizeAspectFill; + + return WKCACFLayer::Center; +} + +static CFStringRef toCACFFilterType(WKCACFLayer::FilterType type) +{ + switch (type) { + case WKCACFLayer::Linear: return kCACFFilterLinear(); + case WKCACFLayer::Nearest: return kCACFFilterNearest(); + case WKCACFLayer::Trilinear: return kCACFFilterTrilinear(); + case WKCACFLayer::Lanczos: return kCACFFilterLanczos(); + default: return 0; + } +} + +static WKCACFLayer::FilterType fromCACFFilterType(CFStringRef string) +{ + if (CFEqual(string, kCACFFilterNearest())) + return WKCACFLayer::Nearest; + + if (CFEqual(string, kCACFFilterTrilinear())) + return WKCACFLayer::Trilinear; + + if (CFEqual(string, kCACFFilterLanczos())) + return WKCACFLayer::Lanczos; + + return WKCACFLayer::Linear; +} + +PassRefPtr<WKCACFLayer> WKCACFLayer::create(LayerType type, GraphicsLayerCACF* owner) +{ + if (!WKCACFLayerRenderer::acceleratedCompositingAvailable()) + return 0; + return adoptRef(new WKCACFLayer(type, owner)); +} + // FIXME: It might be good to have a way of ensuring that all WKCACFLayers eventually // get destroyed in debug builds. A static counter could accomplish this pretty easily. -WKCACFLayer::WKCACFLayer(CFStringRef className, GraphicsLayerCACF* owner) - : m_layer(AdoptCF, CACFLayerCreate(className)) +WKCACFLayer::WKCACFLayer(LayerType type, GraphicsLayerCACF* owner) + : m_layer(AdoptCF, CACFLayerCreate(toCACFLayerType(type))) , m_needsDisplayOnBoundsChange(false) , m_owner(owner) { @@ -291,6 +422,39 @@ void WKCACFLayer::setFrame(const CGRect& rect) setNeedsDisplay(); } +void WKCACFLayer::setContentsGravity(ContentsGravityType type) +{ + CACFLayerSetContentsGravity(layer(), toCACFContentsGravityType(type)); + setNeedsCommit(); +} + +WKCACFLayer::ContentsGravityType WKCACFLayer::contentsGravity() const +{ + return fromCACFContentsGravityType(CACFLayerGetContentsGravity(layer())); +} + +void WKCACFLayer::setMagnificationFilter(FilterType type) +{ + CACFLayerSetMagnificationFilter(layer(), toCACFFilterType(type)); + setNeedsCommit(); +} + +WKCACFLayer::FilterType WKCACFLayer::magnificationFilter() const +{ + return fromCACFFilterType(CACFLayerGetMagnificationFilter(layer())); +} + +void WKCACFLayer::setMinificationFilter(FilterType type) +{ + CACFLayerSetMinificationFilter(layer(), toCACFFilterType(type)); + setNeedsCommit(); +} + +WKCACFLayer::FilterType WKCACFLayer::minificationFilter() const +{ + return fromCACFFilterType(CACFLayerGetMinificationFilter(layer())); +} + WKCACFLayer* WKCACFLayer::rootLayer() const { WKCACFLayer* layer = const_cast<WKCACFLayer*>(this); diff --git a/WebCore/platform/graphics/win/WKCACFLayer.h b/WebCore/platform/graphics/win/WKCACFLayer.h index 6655f7a..6892c6e 100644 --- a/WebCore/platform/graphics/win/WKCACFLayer.h +++ b/WebCore/platform/graphics/win/WKCACFLayer.h @@ -50,7 +50,12 @@ class WKCACFTimingFunction; class WKCACFLayer : public RefCounted<WKCACFLayer> { public: - static PassRefPtr<WKCACFLayer> create(CFStringRef className, GraphicsLayerCACF* owner = 0) { return adoptRef(new WKCACFLayer(className, owner)); } + enum LayerType { Layer, TransformLayer }; + enum FilterType { Linear, Nearest, Trilinear, Lanczos }; + enum ContentsGravityType { Center, Top, Bottom, Left, Right, TopLeft, TopRight, + BottomLeft, BottomRight, Resize, ResizeAspect, ResizeAspectFill }; + + static PassRefPtr<WKCACFLayer> create(LayerType, GraphicsLayerCACF* owner = 0); static WKCACFLayer* layer(CACFLayerRef layer) { return static_cast<WKCACFLayer*>(CACFLayerGetUserData(layer)); } ~WKCACFLayer(); @@ -142,8 +147,8 @@ public: void setContentsRect(const CGRect& contentsRect) { CACFLayerSetContentsRect(layer(), contentsRect); setNeedsCommit(); } CGRect contentsRect() const { return CACFLayerGetContentsRect(layer()); } - void setContentsGravity(CFStringRef str) { CACFLayerSetContentsGravity(layer(), str); setNeedsCommit(); } - CFStringRef contentsGravity() const { return CACFLayerGetContentsGravity(layer()); } + void setContentsGravity(ContentsGravityType); + ContentsGravityType contentsGravity() const; void setDoubleSided(bool b) { CACFLayerSetDoubleSided(layer(), b); setNeedsCommit(); } bool doubleSided() const { return CACFLayerIsDoubleSided(layer()); } @@ -163,11 +168,11 @@ public: void setMasksToBounds(bool b) { CACFLayerSetMasksToBounds(layer(), b); } bool masksToBounds() const { return CACFLayerGetMasksToBounds(layer()); } - void setMagnificationFilter(const String& string) { CACFLayerSetMagnificationFilter(layer(), RetainPtr<CFStringRef>(AdoptCF, string.createCFString()).get()); } - String magnificationFilter() const { return CACFLayerGetMagnificationFilter(layer()); } + void setMagnificationFilter(FilterType); + FilterType magnificationFilter() const; - void setMinificationFilter(const String& string) { CACFLayerSetMinificationFilter(layer(), RetainPtr<CFStringRef>(AdoptCF, string.createCFString()).get()); } - String minificationFilter() const { return CACFLayerGetMinificationFilter(layer()); } + void setMinificationFilter(FilterType); + FilterType minificationFilter() const; void setMinificationFilterBias(float bias) { CACFLayerSetMinificationFilterBias(layer(), bias); } float minificationFilterBias() const { return CACFLayerGetMinificationFilterBias(layer()); } @@ -218,9 +223,9 @@ public: void setGeometryFlipped(bool flipped) { CACFLayerSetGeometryFlipped(layer(), flipped); setNeedsCommit(); } bool geometryFlipped() const { return CACFLayerIsGeometryFlipped(layer()); } - WKCACFLayer(CFStringRef className, GraphicsLayerCACF* owner); - private: + WKCACFLayer(LayerType, GraphicsLayerCACF* owner); + void setNeedsCommit(); CACFLayerRef layer() const { return m_layer.get(); } size_t numSublayers() const diff --git a/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp b/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp index 9fbd0fc..3bbd4f8 100644 --- a/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp +++ b/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp @@ -34,15 +34,20 @@ #include <CoreGraphics/CGSRegion.h> #include <QuartzCore/CACFContext.h> #include <QuartzCore/CARenderOGL.h> +#include <QuartzCoreInterface/QuartzCoreInterface.h> #include <wtf/HashMap.h> #include <wtf/OwnArrayPtr.h> +#include <wtf/StdLibExtras.h> #include <d3d9.h> #include <d3dx9.h> -#include <dxerr9.h> -#pragma comment(lib, "d3d9")
-#pragma comment(lib, "d3dx9")
-#pragma comment(lib, "QuartzCore")
+#pragma comment(lib, "d3d9") +#pragma comment(lib, "d3dx9") +#ifdef DEBUG_ALL +#pragma comment(lib, "QuartzCore_debug") +#else +#pragma comment(lib, "QuartzCore") +#endif static IDirect3D9* s_d3d = 0; static IDirect3D9* d3d() @@ -90,29 +95,29 @@ static D3DPRESENT_PARAMETERS initialPresentationParameters() return parameters; } -bool WKCACFLayerRenderer::acceleratedCompositingAvailable()
-{
- static bool available;
- static bool tested;
-
- if (tested)
- return available;
-
- tested = true;
- HMODULE library = LoadLibrary(TEXT("d3d9.dll"));
- if (!library)
- return false;
-
- FreeLibrary(library);
- library = LoadLibrary(TEXT("QuartzCore.dll"));
- if (!library)
- return false;
-
- FreeLibrary(library);
- available = true;
- return available;
-}
-
+bool WKCACFLayerRenderer::acceleratedCompositingAvailable() +{ + static bool available; + static bool tested; + + if (tested) + return available; + + tested = true; + HMODULE library = LoadLibrary(TEXT("d3d9.dll")); + if (!library) + return false; + + FreeLibrary(library); + library = LoadLibrary(TEXT("QuartzCore.dll")); + if (!library) + return false; + + FreeLibrary(library); + available = true; + return available; +} + void WKCACFLayerRenderer::didFlushContext(CACFContextRef context) { WKCACFLayerRenderer* window = windowsForContexts().get(context); @@ -135,8 +140,7 @@ WKCACFLayerRenderer::WKCACFLayerRenderer() , m_renderer(0) , m_hostWindow(0) , m_renderTimer(this, &WKCACFLayerRenderer::renderTimerFired) - , m_scrollFrameWidth(1) // Default to 1 to avoid 0 size frames - , m_scrollFrameHeight(1) // Default to 1 to avoid 0 size frames + , m_scrollFrame(0, 0, 1, 1) // Default to 1 to avoid 0 size frames { } @@ -145,19 +149,15 @@ WKCACFLayerRenderer::~WKCACFLayerRenderer() destroyRenderer(); } -void WKCACFLayerRenderer::setScrollFrame(int width, int height, int scrollX, int scrollY) +void WKCACFLayerRenderer::setScrollFrame(const IntRect& scrollFrame) { - m_scrollFrameWidth = width; - m_scrollFrameHeight = height; - - CGRect contentsRect = CGRectMake(scrollX, scrollY, width, height); - m_scrollLayer->setFrame(contentsRect); + m_scrollFrame = scrollFrame; + CGRect frameBounds = bounds(); + m_scrollLayer->setBounds(CGRectMake(0, 0, m_scrollFrame.width(), m_scrollFrame.height())); + m_scrollLayer->setPosition(CGPointMake(0, frameBounds.size.height)); - if (m_rootChildLayer) { - contentsRect.origin.x = 0; - contentsRect.origin.y = 0; - m_rootChildLayer->setFrame(contentsRect); - } + if (m_rootChildLayer) + m_rootChildLayer->setPosition(CGPointMake(m_scrollFrame.x(), m_scrollFrame.height() + m_scrollFrame.y())); } void WKCACFLayerRenderer::setRootContents(CGImageRef image) @@ -177,7 +177,8 @@ void WKCACFLayerRenderer::setRootChildLayer(WebCore::PlatformLayer* layer) m_scrollLayer->addSublayer(layer); // Set the frame - layer->setFrame(CGRectMake(0, 0, m_scrollFrameWidth, m_scrollFrameHeight)); + layer->setAnchorPoint(CGPointMake(0, 1)); + setScrollFrame(m_scrollFrame); } m_rootChildLayer = layer; @@ -225,14 +226,15 @@ void WKCACFLayerRenderer::createRenderer() windowsForContexts().set(m_context.get(), this); m_renderContext = static_cast<CARenderContext*>(CACFContextGetRenderContext(m_context.get())); - m_renderer = CARenderOGLNew(&kCARenderDX9Callbacks, m_d3dDevice.get(), 0); + m_renderer = CARenderOGLNew(wkqcCARenderOGLCallbacks(wkqckCARenderDX9Callbacks), m_d3dDevice.get(), 0); // Create the root hierarchy - m_rootLayer = WKCACFLayer::create(kCACFLayer); - m_scrollLayer = WKCACFLayer::create(kCACFLayer); + m_rootLayer = WKCACFLayer::create(WKCACFLayer::Layer); + m_scrollLayer = WKCACFLayer::create(WKCACFLayer::Layer); m_rootLayer->addSublayer(m_scrollLayer); m_scrollLayer->setMasksToBounds(true); + m_scrollLayer->setAnchorPoint(CGPointMake(0, 1)); #ifndef NDEBUG CGColorRef debugColor = createCGColor(Color(255, 0, 0, 204)); @@ -240,14 +242,9 @@ void WKCACFLayerRenderer::createRenderer() CGColorRelease(debugColor); #endif - if (IsWindow(m_hostWindow)) { + if (IsWindow(m_hostWindow)) m_rootLayer->setFrame(bounds()); - // For now this will include the scroll bars. Later in the setScrollFrame - // we will fix it - m_scrollLayer->setFrame(bounds()); - } - if (m_context) m_rootLayer->becomeRootLayerForContext(m_context.get()); } @@ -284,6 +281,7 @@ void WKCACFLayerRenderer::resize() if (m_rootLayer) { m_rootLayer->setFrame(bounds()); WKCACFContextFlusher::shared().flushAllContexts(); + setScrollFrame(m_scrollFrame); } } diff --git a/WebCore/platform/graphics/win/WKCACFLayerRenderer.h b/WebCore/platform/graphics/win/WKCACFLayerRenderer.h index 12cde48..cb9f04f 100644 --- a/WebCore/platform/graphics/win/WKCACFLayerRenderer.h +++ b/WebCore/platform/graphics/win/WKCACFLayerRenderer.h @@ -58,7 +58,7 @@ public: static bool acceleratedCompositingAvailable(); static void didFlushContext(CACFContextRef); - void setScrollFrame(int width, int height, int scrollX, int scrollY); + void setScrollFrame(const IntRect&); void setRootContents(CGImageRef); void setRootChildLayer(WebCore::PlatformLayer* layer); void setNeedsDisplay(); @@ -96,7 +96,7 @@ private: CARenderOGLContext* m_renderer; HWND m_hostWindow; Timer<WKCACFLayerRenderer> m_renderTimer; - int m_scrollFrameWidth, m_scrollFrameHeight; + IntRect m_scrollFrame; }; } diff --git a/WebCore/platform/graphics/wince/GraphicsContextWince.cpp b/WebCore/platform/graphics/wince/GraphicsContextWince.cpp index f22e6c9..0e387f5 100644 --- a/WebCore/platform/graphics/wince/GraphicsContextWince.cpp +++ b/WebCore/platform/graphics/wince/GraphicsContextWince.cpp @@ -1002,7 +1002,12 @@ void GraphicsContext::clipOut(const IntRect& rect) ExcludeClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); } -void GraphicsContext::drawFocusRing(const Color& color) +void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) +{ + // FIXME: implement +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) { if (!m_data->m_opacity || paintingDisabled()) return; @@ -1011,10 +1016,9 @@ void GraphicsContext::drawFocusRing(const Color& color) if (!m_data->m_dc) return; - int radius = (focusRingWidth() - 1) / 2; - int offset = radius + focusRingOffset(); + int radius = (width - 1) / 2; + offset += radius; - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); IntRect finalFocusRect; for (unsigned i = 0; i < rectCount; i++) { diff --git a/WebCore/platform/graphics/wince/MediaPlayerPrivateWince.h b/WebCore/platform/graphics/wince/MediaPlayerPrivateWince.h index 2d6c358..a657e3e 100644 --- a/WebCore/platform/graphics/wince/MediaPlayerPrivateWince.h +++ b/WebCore/platform/graphics/wince/MediaPlayerPrivateWince.h @@ -61,20 +61,16 @@ namespace WebCore { float duration() const; float currentTime() const; void seek(float time); - void setEndTime(float); void setRate(float); void setVolume(float); - int dataRate() const; - MediaPlayer::NetworkState networkState() const { return m_networkState; } MediaPlayer::ReadyState readyState() const { return m_readyState; } PassRefPtr<TimeRanges> buffered() const; float maxTimeSeekable() const; unsigned bytesLoaded() const; - bool totalBytesKnown() const; unsigned totalBytes() const; void setVisible(bool); diff --git a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp index 9c05ce5..839bc59 100644 --- a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp +++ b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp @@ -270,7 +270,12 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef notImplemented(); } -void GraphicsContext::drawFocusRing(const Color& color) +void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) +{ + // FIXME: implement +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) { if (paintingDisabled()) return; @@ -566,7 +571,7 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness notImplemented(); } -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { if (dstRect.isEmpty()) |