diff options
Diffstat (limited to 'WebCore/platform/graphics')
149 files changed, 8663 insertions, 2769 deletions
diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index 68863df..1d97632 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -53,6 +53,7 @@ BitmapImage::BitmapImage(ImageObserver* observer) , m_repetitionsComplete(0) , m_desiredFrameStartTime(0) , m_isSolidColor(false) + , m_checkedForSolidColor(false) , m_animationFinished(false) , m_allDataReceived(false) , m_haveSize(false) diff --git a/WebCore/platform/graphics/BitmapImage.h b/WebCore/platform/graphics/BitmapImage.h index 110aec4..db05d1c 100644 --- a/WebCore/platform/graphics/BitmapImage.h +++ b/WebCore/platform/graphics/BitmapImage.h @@ -166,7 +166,7 @@ protected: virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator); #endif virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); -#if PLATFORM(QT) || PLATFORM(WX) +#if PLATFORM(WX) virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); #endif @@ -218,9 +218,18 @@ protected: void invalidatePlatformData(); // Checks to see if the image is a 1x1 solid color. We optimize these images and just do a fill rect instead. + // This check should happen regardless whether m_checkedForSolidColor is already set, as the frame may have + // changed. void checkForSolidColor(); - virtual bool mayFillWithSolidColor() const { return m_isSolidColor && m_currentFrame == 0; } + virtual bool mayFillWithSolidColor() + { + if (!m_checkedForSolidColor && frameCount() > 0) { + checkForSolidColor(); + ASSERT(m_checkedForSolidColor); + } + return m_isSolidColor && m_currentFrame == 0; + } virtual Color solidColor() const { return m_solidColor; } ImageSource m_source; @@ -242,6 +251,7 @@ protected: Color m_solidColor; // If we're a 1x1 solid color, this is the color to use to fill. bool m_isSolidColor; // Whether or not we are a 1x1 solid image. + bool m_checkedForSolidColor; // Whether we've checked the frame for solid color. bool m_animationFinished; // Whether or not we've completed the entire animation. diff --git a/WebCore/platform/graphics/Color.cpp b/WebCore/platform/graphics/Color.cpp index c7e11ee..e85ac00 100644 --- a/WebCore/platform/graphics/Color.cpp +++ b/WebCore/platform/graphics/Color.cpp @@ -116,6 +116,15 @@ RGBA32 makeRGBAFromHSLA(double hue, double saturation, double lightness, double static_cast<int>(alpha * scaleFactor)); } +RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a) +{ + double colors = 1 - k; + int r = static_cast<int>(nextafter(256, 0) * (colors * (1 - c))); + int g = static_cast<int>(nextafter(256, 0) * (colors * (1 - m))); + int b = static_cast<int>(nextafter(256, 0) * (colors * (1 - y))); + return makeRGBA(r, g, b, static_cast<float>(nextafter(256, 0) * a)); +} + // originally moved here from the CSS parser bool Color::parseHexColor(const String& name, RGBA32& rgb) { @@ -312,4 +321,34 @@ void Color::getRGBA(double& r, double& g, double& b, double& a) const a = alpha() / 255.0; } +Color colorFromPremultipliedARGB(unsigned pixelColor) +{ + RGBA32 rgba; + + if (unsigned alpha = (pixelColor & 0xFF000000) >> 24) { + rgba = makeRGBA(((pixelColor & 0x00FF0000) >> 16) * 255 / alpha, + ((pixelColor & 0x0000FF00) >> 8) * 255 / alpha, + (pixelColor & 0x000000FF) * 255 / alpha, + alpha); + } else + rgba = pixelColor; + + return Color(rgba); +} + +unsigned premultipliedARGBFromColor(const Color& color) +{ + unsigned pixelColor; + + if (unsigned alpha = color.alpha()) { + pixelColor = alpha << 24 | + ((color.red() * alpha + 254) / 255) << 16 | + ((color.green() * alpha + 254) / 255) << 8 | + ((color.blue() * alpha + 254) / 255); + } else + pixelColor = color.rgb(); + + return pixelColor; +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/Color.h b/WebCore/platform/graphics/Color.h index 61fc74c..3c889f9 100644 --- a/WebCore/platform/graphics/Color.h +++ b/WebCore/platform/graphics/Color.h @@ -60,6 +60,7 @@ RGBA32 makeRGBA(int r, int g, int b, int a); RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha); RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a); RGBA32 makeRGBAFromHSLA(double h, double s, double l, double a); +RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a); int differenceSquared(const Color&, const Color&); @@ -71,6 +72,8 @@ public: Color(int r, int g, int b, int a) : m_color(makeRGBA(r, g, b, a)), m_valid(true) { } // Color is currently limited to 32bit RGBA, perhaps some day we'll support better colors Color(float r, float g, float b, float a) : m_color(makeRGBA32FromFloats(r, g, b, a)), m_valid(true) { } + // Creates a new color from the specific CMYK and alpha values. + Color(float c, float m, float y, float k, float a) : m_color(makeRGBAFromCMYKA(c, m, y, k, a)), m_valid(true) { } explicit Color(const String&); explicit Color(const char*); @@ -146,9 +149,11 @@ inline bool operator!=(const Color& a, const Color& b) } Color focusRingColor(); +Color colorFromPremultipliedARGB(unsigned); +unsigned premultipliedARGBFromColor(const Color&); #if PLATFORM(CG) -CGColorRef cgColor(const Color&); +CGColorRef createCGColor(const Color&); #endif } // namespace WebCore diff --git a/WebCore/platform/graphics/FloatPoint.cpp b/WebCore/platform/graphics/FloatPoint.cpp index 564ea86..7765ba9 100644 --- a/WebCore/platform/graphics/FloatPoint.cpp +++ b/WebCore/platform/graphics/FloatPoint.cpp @@ -40,7 +40,7 @@ FloatPoint::FloatPoint(const IntPoint& p) : m_x(p.x()), m_y(p.y()) FloatPoint FloatPoint::matrixTransform(const TransformationMatrix& transform) const { double newX, newY; - transform.map(static_cast<double>(m_x), static_cast<double>(m_y), &newX, &newY); + transform.map(static_cast<double>(m_x), static_cast<double>(m_y), newX, newY); return narrowPrecision(newX, newY); } diff --git a/WebCore/platform/graphics/FloatPoint3D.cpp b/WebCore/platform/graphics/FloatPoint3D.cpp index e3ba422..8c21ef3 100644 --- a/WebCore/platform/graphics/FloatPoint3D.cpp +++ b/WebCore/platform/graphics/FloatPoint3D.cpp @@ -21,7 +21,6 @@ #include "config.h" -#if ENABLE(SVG) #include <math.h> #include "FloatPoint.h" #include "FloatPoint3D.h" @@ -62,4 +61,3 @@ void FloatPoint3D::normalize() } // namespace WebCore -#endif // ENABLE(SVG) diff --git a/WebCore/platform/graphics/FloatPoint3D.h b/WebCore/platform/graphics/FloatPoint3D.h index 184e914..2e71ddd 100644 --- a/WebCore/platform/graphics/FloatPoint3D.h +++ b/WebCore/platform/graphics/FloatPoint3D.h @@ -22,8 +22,6 @@ #ifndef FloatPoint3D_h #define FloatPoint3D_h -#if ENABLE(SVG) - namespace WebCore { class FloatPoint; @@ -53,6 +51,4 @@ private: } // namespace WebCore -#endif // ENABLE(SVG) - #endif // FloatPoint3D_h diff --git a/WebCore/platform/graphics/FontCache.cpp b/WebCore/platform/graphics/FontCache.cpp index 2d219be..130313d 100644 --- a/WebCore/platform/graphics/FontCache.cpp +++ b/WebCore/platform/graphics/FontCache.cpp @@ -139,8 +139,13 @@ static const AtomicString& alternateFamilyName(const AtomicString& familyName) DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New")); if (equalIgnoringCase(familyName, courier)) return courierNew; +#if !PLATFORM(WIN_OS) + // 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. if (equalIgnoringCase(familyName, courierNew)) return courier; +#endif // Alias Times and Times New Roman. DEFINE_STATIC_LOCAL(AtomicString, times, ("Times")); @@ -158,6 +163,21 @@ static const AtomicString& alternateFamilyName(const AtomicString& familyName) if (equalIgnoringCase(familyName, helvetica)) return arial; +#if PLATFORM(WIN_OS) + // 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")); + DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif")); + if (equalIgnoringCase(familyName, msSans)) + return microsoftSans; + + // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no + // 'Microsoft Sans Serif-equivalent' for Serif. + static AtomicString msSerif("MS Serif"); + if (equalIgnoringCase(familyName, msSerif)) + return timesNewRoman; +#endif + return emptyAtom; } @@ -216,7 +236,7 @@ struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { static const bool needsDestruction = true; static const FontPlatformData& emptyValue() { - DEFINE_STATIC_LOCAL(FontPlatformData, key, ()); + DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false)); return key; } static void constructDeletedValue(FontPlatformData& slot) @@ -304,7 +324,7 @@ void FontCache::purgeInactiveFontData(int count) } Vector<FontPlatformDataCacheKey> keysToRemove; - keysToRemove.reserveCapacity(gFontPlatformDataCache->size()); + keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { if (platformData->second && !gFontDataCache->contains(*platformData->second)) @@ -424,7 +444,7 @@ void FontCache::invalidate() Vector<RefPtr<FontSelector> > clients; size_t numClients = gClients->size(); - clients.reserveCapacity(numClients); + clients.reserveInitialCapacity(numClients); HashSet<FontSelector*>::iterator end = gClients->end(); for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it) clients.append(*it); diff --git a/WebCore/platform/graphics/GlyphBuffer.h b/WebCore/platform/graphics/GlyphBuffer.h index fdb306f..dcda419 100644 --- a/WebCore/platform/graphics/GlyphBuffer.h +++ b/WebCore/platform/graphics/GlyphBuffer.h @@ -37,7 +37,7 @@ #include <ApplicationServices/ApplicationServices.h> #endif -#if PLATFORM(CAIRO) +#if PLATFORM(CAIRO) || (PLATFORM(WX) && defined(__WXGTK__)) #include <cairo.h> #endif diff --git a/WebCore/platform/graphics/GlyphPageTreeNode.cpp b/WebCore/platform/graphics/GlyphPageTreeNode.cpp index 6b9d23d..bd838de 100644 --- a/WebCore/platform/graphics/GlyphPageTreeNode.cpp +++ b/WebCore/platform/graphics/GlyphPageTreeNode.cpp @@ -218,6 +218,7 @@ void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNu } haveGlyphs |= pageToFill->fill(from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData()); if (scratchPage) { + ASSERT(to <= static_cast<int>(GlyphPage::size)); for (int j = from; j < to; j++) { if (!m_page->m_glyphs[j].glyph && pageToFill->m_glyphs[j].glyph) m_page->m_glyphs[j] = pageToFill->m_glyphs[j]; diff --git a/WebCore/platform/graphics/GlyphWidthMap.cpp b/WebCore/platform/graphics/GlyphWidthMap.cpp index 6e8d68d..43cab65 100644 --- a/WebCore/platform/graphics/GlyphWidthMap.cpp +++ b/WebCore/platform/graphics/GlyphWidthMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 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 @@ -29,43 +29,22 @@ #include "config.h" #include "GlyphWidthMap.h" -namespace WebCore -{ - -float GlyphWidthMap::widthForGlyph(Glyph g) -{ - unsigned pageNumber = (g / GlyphWidthPage::size); - GlyphWidthPage* page = locatePage(pageNumber); - if (page) - return page->widthForGlyph(g); - return cGlyphWidthUnknown; -} - -void GlyphWidthMap::setWidthForGlyph(Glyph glyph, float width) -{ - unsigned pageNumber = (glyph / GlyphWidthPage::size); - GlyphWidthPage* page = locatePage(pageNumber); - if (page) - page->setWidthForGlyph(glyph, width); -} +namespace WebCore { -inline GlyphWidthMap::GlyphWidthPage* GlyphWidthMap::locatePage(unsigned pageNumber) +GlyphWidthMap::GlyphWidthPage* GlyphWidthMap::locatePageSlowCase(unsigned pageNumber) { GlyphWidthPage* page; if (pageNumber == 0) { - if (m_filledPrimaryPage) - return &m_primaryPage; + ASSERT(!m_filledPrimaryPage); page = &m_primaryPage; m_filledPrimaryPage = true; } else { if (m_pages) { - GlyphWidthPage* result = m_pages->get(pageNumber); - if (result) - return result; - } + if ((page = m_pages->get(pageNumber))) + return page; + } else + m_pages.set(new HashMap<int, GlyphWidthPage*>); page = new GlyphWidthPage; - if (!m_pages) - m_pages = new HashMap<int, GlyphWidthPage*>; m_pages->set(pageNumber, page); } diff --git a/WebCore/platform/graphics/GlyphWidthMap.h b/WebCore/platform/graphics/GlyphWidthMap.h index 1633769..e194ecf 100644 --- a/WebCore/platform/graphics/GlyphWidthMap.h +++ b/WebCore/platform/graphics/GlyphWidthMap.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 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 @@ -29,9 +29,9 @@ #ifndef GlyphWidthMap_h #define GlyphWidthMap_h -#include <wtf/unicode/Unicode.h> -#include <wtf/Noncopyable.h> #include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> +#include <wtf/unicode/Unicode.h> namespace WebCore { @@ -41,34 +41,49 @@ const float cGlyphWidthUnknown = -1; class GlyphWidthMap : Noncopyable { public: - GlyphWidthMap() : m_filledPrimaryPage(false), m_pages(0) {} - ~GlyphWidthMap() { if (m_pages) { deleteAllValues(*m_pages); delete m_pages; } } + GlyphWidthMap() : m_filledPrimaryPage(false) { } + ~GlyphWidthMap() { if (m_pages) { deleteAllValues(*m_pages); } } + + float widthForGlyph(Glyph glyph) + { + return locatePage(glyph / GlyphWidthPage::size)->widthForGlyph(glyph); + } - float widthForGlyph(Glyph); - void setWidthForGlyph(Glyph, float); + void setWidthForGlyph(Glyph glyph, float width) + { + locatePage(glyph / GlyphWidthPage::size)->setWidthForGlyph(glyph, width); + } private: struct GlyphWidthPage { static const size_t size = 256; // Usually covers Latin-1 in a single page. float m_widths[size]; - float widthForGlyph(Glyph g) const { return m_widths[g % size]; } - void setWidthForGlyph(Glyph g, float w) + float widthForGlyph(Glyph glyph) const { return m_widths[glyph % size]; } + void setWidthForGlyph(Glyph glyph, float width) { - setWidthForIndex(g % size, w); + setWidthForIndex(glyph % size, width); } - void setWidthForIndex(unsigned index, float w) + void setWidthForIndex(unsigned index, float width) { - m_widths[index] = w; + m_widths[index] = width; } }; - GlyphWidthPage* locatePage(unsigned page); + GlyphWidthPage* locatePage(unsigned pageNumber) + { + if (!pageNumber && m_filledPrimaryPage) + return &m_primaryPage; + return locatePageSlowCase(pageNumber); + } + + GlyphWidthPage* locatePageSlowCase(unsigned pageNumber); bool m_filledPrimaryPage; GlyphWidthPage m_primaryPage; // We optimize for the page that contains glyph indices 0-255. - HashMap<int, GlyphWidthPage*>* m_pages; + OwnPtr<HashMap<int, GlyphWidthPage*> > m_pages; }; } + #endif diff --git a/WebCore/platform/graphics/Gradient.cpp b/WebCore/platform/graphics/Gradient.cpp index 2e6a5d2..24e8bbf 100644 --- a/WebCore/platform/graphics/Gradient.cpp +++ b/WebCore/platform/graphics/Gradient.cpp @@ -39,6 +39,7 @@ Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1) , m_r1(0) , m_stopsSorted(false) , m_lastStop(0) + , m_spreadMethod(SpreadMethodPad) { platformInit(); } @@ -146,4 +147,11 @@ int Gradient::findStop(float value) const return m_lastStop; } +void Gradient::setSpreadMethod(GradientSpreadMethod spreadMethod) +{ + // FIXME: Should it become necessary, allow calls to this method after m_gradient has been set. + ASSERT(m_gradient == 0); + m_spreadMethod = spreadMethod; +} + } //namespace diff --git a/WebCore/platform/graphics/Gradient.h b/WebCore/platform/graphics/Gradient.h index 00ef2b6..764deee 100644 --- a/WebCore/platform/graphics/Gradient.h +++ b/WebCore/platform/graphics/Gradient.h @@ -29,6 +29,8 @@ #include "FloatPoint.h" #include "Generator.h" +#include "GraphicsTypes.h" +#include "TransformationMatrix.h" #include <wtf/PassRefPtr.h> #include <wtf/Vector.h> @@ -93,6 +95,12 @@ namespace WebCore { void setStopsSorted(bool s) { m_stopsSorted = s; } + void setSpreadMethod(GradientSpreadMethod); + GradientSpreadMethod spreadMethod() { return m_spreadMethod; } + void setGradientSpaceTransform(const TransformationMatrix& gradientSpaceTransformation) { m_gradientSpaceTransformation = gradientSpaceTransformation; } + // Qt and CG transform the gradient at draw time + TransformationMatrix gradientSpaceTransform() { return m_gradientSpaceTransformation; } + virtual void fill(GraphicsContext*, const FloatRect&); private: @@ -112,6 +120,8 @@ namespace WebCore { mutable Vector<ColorStop> m_stops; mutable bool m_stopsSorted; mutable int m_lastStop; + GradientSpreadMethod m_spreadMethod; + TransformationMatrix m_gradientSpaceTransformation; PlatformGradient m_gradient; }; diff --git a/WebCore/platform/graphics/GraphicsContext.cpp b/WebCore/platform/graphics/GraphicsContext.cpp index 8426011..8cad794 100644 --- a/WebCore/platform/graphics/GraphicsContext.cpp +++ b/WebCore/platform/graphics/GraphicsContext.cpp @@ -128,6 +128,11 @@ void GraphicsContext::setStrokeColor(const Color& color) setPlatformStrokeColor(color); } +ColorSpace GraphicsContext::strokeColorSpace() const +{ + return m_common->state.strokeColorSpace; +} + void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color) { m_common->state.shadowSize = size; @@ -178,16 +183,6 @@ void GraphicsContext::setFillRule(WindRule fillRule) m_common->state.fillRule = fillRule; } -GradientSpreadMethod GraphicsContext::spreadMethod() const -{ - return m_common->state.spreadMethod; -} - -void GraphicsContext::setSpreadMethod(GradientSpreadMethod spreadMethod) -{ - m_common->state.spreadMethod = spreadMethod; -} - void GraphicsContext::setFillColor(const Color& color) { m_common->state.fillColorSpace = SolidColorSpace; @@ -255,6 +250,31 @@ void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) m_common->state.fillGradient = gradient; } +Gradient* GraphicsContext::fillGradient() const +{ + return m_common->state.fillGradient.get(); +} + +ColorSpace GraphicsContext::fillColorSpace() const +{ + return m_common->state.fillColorSpace; +} + +Gradient* GraphicsContext::strokeGradient() const +{ + return m_common->state.strokeGradient.get(); +} + +Pattern* GraphicsContext::fillPattern() const +{ + return m_common->state.fillPattern.get(); +} + +Pattern* GraphicsContext::strokePattern() const +{ + return m_common->state.strokePattern.get(); +} + void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms) { m_common->state.shadowsIgnoreTransforms = ignoreTransforms; diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index c27f38f..7c1c4b0 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -123,6 +123,18 @@ namespace WebCore { DashedStroke }; +// FIXME: This is a place-holder until we decide to add +// real color space support to WebCore. At that time, ColorSpace will be a +// class and instances will be held off of Colors. There will be +// special singleton Gradient and Pattern color spaces to mark when +// a fill or stroke is using a gradient or pattern instead of a solid color. +// https://bugs.webkit.org/show_bug.cgi?id=20558 + enum ColorSpace { + SolidColorSpace, + PatternColorSpace, + GradientColorSpace + }; + enum InterpolationQuality { InterpolationDefault, InterpolationNone, @@ -131,14 +143,6 @@ namespace WebCore { InterpolationHigh }; - // FIXME: Currently these constants have to match the values used in the SVG - // DOM API. That's a mistake. We need to make cut that dependency. - enum GradientSpreadMethod { - SpreadMethodPad = 1, - SpreadMethodReflect = 2, - SpreadMethodRepeat = 3 - }; - class GraphicsContext : Noncopyable { public: GraphicsContext(PlatformGraphicsContext*); @@ -152,17 +156,28 @@ namespace WebCore { void setStrokeStyle(const StrokeStyle& style); Color strokeColor() const; void setStrokeColor(const Color&); + + ColorSpace strokeColorSpace() const; + void setStrokePattern(PassRefPtr<Pattern>); + Pattern* strokePattern() const; + void setStrokeGradient(PassRefPtr<Gradient>); + Gradient* strokeGradient() const; WindRule fillRule() const; void setFillRule(WindRule); - GradientSpreadMethod spreadMethod() const; - void setSpreadMethod(GradientSpreadMethod); Color fillColor() const; void setFillColor(const Color&); + void setFillPattern(PassRefPtr<Pattern>); + Pattern* fillPattern() const; + void setFillGradient(PassRefPtr<Gradient>); + Gradient* fillGradient() const; + + ColorSpace fillColorSpace() const; + void setShadowsIgnoreTransforms(bool); void setShouldAntialias(bool); @@ -206,6 +221,9 @@ namespace WebCore { void restore(); // These draw methods will do both stroking and filling. + // FIXME: ...except drawRect(), which fills properly but always strokes + // using a 1-pixel stroke inset from the rect borders (of the correct + // stroke color). void drawRect(const IntRect&); void drawLine(const IntPoint&, const IntPoint&); void drawEllipse(const IntRect&); @@ -402,3 +420,4 @@ namespace WebCore { } // namespace WebCore #endif // GraphicsContext_h + diff --git a/WebCore/platform/graphics/GraphicsContextPrivate.h b/WebCore/platform/graphics/GraphicsContextPrivate.h index 87123eb..98baab1 100644 --- a/WebCore/platform/graphics/GraphicsContextPrivate.h +++ b/WebCore/platform/graphics/GraphicsContextPrivate.h @@ -33,18 +33,6 @@ namespace WebCore { -// FIXME: This is a place-holder until we decide to add -// real color space support to WebCore. At that time, ColorSpace will be a -// class and instances will be held off of Colors. There will be -// special singleton Gradient and Pattern color spaces to mark when -// a fill or stroke is using a gradient or pattern instead of a solid color. -// https://bugs.webkit.org/show_bug.cgi?id=20558 - enum ColorSpace { - SolidColorSpace, - PatternColorSpace, - GradientColorSpace - }; - struct GraphicsContextState { GraphicsContextState() : textDrawingMode(cTextFill) @@ -80,7 +68,6 @@ namespace WebCore { RefPtr<Pattern> strokePattern; WindRule fillRule; - GradientSpreadMethod spreadMethod; ColorSpace fillColorSpace; Color fillColor; RefPtr<Gradient> fillGradient; diff --git a/WebCore/platform/graphics/GraphicsLayer.cpp b/WebCore/platform/graphics/GraphicsLayer.cpp new file mode 100644 index 0000000..0c442a2 --- /dev/null +++ b/WebCore/platform/graphics/GraphicsLayer.cpp @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayer.h" + +#include "FloatPoint.h" +#include "RotateTransformOperation.h" +#include "TextStream.h" + +namespace WebCore { + +void GraphicsLayer::FloatValue::set(float key, float value, const TimingFunction* timingFunction) +{ + m_key = key; + m_value = value; + if (timingFunction != m_timingFunction) { + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + else + m_timingFunction.clear(); + } +} + +void GraphicsLayer::TransformValue::set(float key, const TransformOperations* value, const TimingFunction* timingFunction) +{ + m_key = key; + if (value != m_value) { + if (value) + m_value.set(new TransformOperations(*value)); + else + m_value.clear(); + } + if (timingFunction != m_timingFunction) { + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + else + m_timingFunction.clear(); + } +} + +void GraphicsLayer::FloatValueList::insert(float key, float value, const TimingFunction* timingFunction) +{ + for (size_t i = 0; i < m_values.size(); ++i) { + FloatValue& curFloatValue = m_values[i]; + if (curFloatValue.key() == key) { + curFloatValue.set(key, value, timingFunction); + return; + } + if (curFloatValue.key() > key) { + // insert before + m_values.insert(i, FloatValue(key, value, timingFunction)); + return; + } + } + + // append + m_values.append(FloatValue(key, value, timingFunction)); +} + +void GraphicsLayer::TransformValueList::insert(float key, const TransformOperations* value, const TimingFunction* timingFunction) +{ + for (size_t i = 0; i < m_values.size(); ++i) { + TransformValue& curTransValue = m_values[i]; + if (curTransValue.key() == key) { + curTransValue.set(key, value, timingFunction); + return; + } + if (curTransValue.key() > key) { + // insert before + m_values.insert(i, TransformValue(key, value, timingFunction)); + return; + } + } + + // append + m_values.append(TransformValue(key, value, timingFunction)); +} + +// An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix +// The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is +// true if the rotation between any two keyframes is >= 180 degrees. +void GraphicsLayer::TransformValueList::makeFunctionList(FunctionList& list, bool& isValid, bool& hasBigRotation) const +{ + list.clear(); + isValid = false; + hasBigRotation = false; + + if (m_values.size() < 2) + return; + + // empty transforms match anything, so find the first non-empty entry as the reference + size_t firstIndex = 0; + for ( ; firstIndex < m_values.size(); ++firstIndex) { + if (m_values[firstIndex].value()->operations().size() > 0) + break; + } + + if (firstIndex >= m_values.size()) + return; + + const TransformOperations* firstVal = m_values[firstIndex].value(); + + // see if the keyframes are valid + for (size_t i = firstIndex + 1; i < m_values.size(); ++i) { + const TransformOperations* val = m_values[i].value(); + + // a null transform matches anything + if (val->operations().isEmpty()) + continue; + + if (firstVal->operations().size() != val->operations().size()) + return; + + for (size_t j = 0; j < firstVal->operations().size(); ++j) { + if (!firstVal->operations().at(j)->isSameType(*val->operations().at(j))) + return; + } + } + + // keyframes are valid, fill in the list + isValid = true; + + double lastRotAngle = 0.0; + double maxRotAngle = -1.0; + + list.resize(firstVal->operations().size()); + for (size_t j = 0; j < firstVal->operations().size(); ++j) { + TransformOperation::OperationType type = firstVal->operations().at(j)->getOperationType(); + list[j] = type; + + // if this is a rotation entry, we need to see if any angle differences are >= 180 deg + if (type == TransformOperation::ROTATE_X || + type == TransformOperation::ROTATE_Y || + type == TransformOperation::ROTATE_Z || + type == TransformOperation::ROTATE_3D) { + lastRotAngle = static_cast<RotateTransformOperation*>(firstVal->operations().at(j).get())->angle(); + + if (maxRotAngle < 0) + maxRotAngle = fabs(lastRotAngle); + + for (size_t i = firstIndex + 1; i < m_values.size(); ++i) { + const TransformOperations* val = m_values[i].value(); + double rotAngle = val->operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val->operations().at(j).get())->angle()); + double diffAngle = fabs(rotAngle - lastRotAngle); + if (diffAngle > maxRotAngle) + maxRotAngle = diffAngle; + lastRotAngle = rotAngle; + } + } + } + + hasBigRotation = maxRotAngle >= 180.0; +} + +GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) + : m_client(client) + , m_anchorPoint(0.5f, 0.5f, 0) + , m_opacity(1) +#ifndef NDEBUG + , m_zPosition(0) +#endif + , m_backgroundColorSet(false) + , m_contentsOpaque(false) + , m_preserves3D(false) + , m_backfaceVisibility(true) + , m_usingTiledLayer(false) + , m_masksToBounds(false) + , m_drawsContent(false) + , m_paintingPhase(GraphicsLayerPaintAllMask) + , m_parent(0) +#ifndef NDEBUG + , m_repaintCount(0) +#endif +{ +} + +GraphicsLayer::~GraphicsLayer() +{ + removeAllAnimations(); + + removeAllChildren(); + removeFromParent(); +} + +void GraphicsLayer::addChild(GraphicsLayer* childLayer) +{ + ASSERT(childLayer != this); + + if (childLayer->parent()) + childLayer->removeFromParent(); + + childLayer->setParent(this); + m_children.append(childLayer); +} + +void GraphicsLayer::addChildAtIndex(GraphicsLayer* childLayer, int index) +{ + ASSERT(childLayer != this); + + if (childLayer->parent()) + childLayer->removeFromParent(); + + childLayer->setParent(this); + m_children.insert(index, childLayer); +} + +void GraphicsLayer::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + ASSERT(childLayer != this); + childLayer->removeFromParent(); + + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (sibling == m_children[i]) { + m_children.insert(i, childLayer); + found = true; + break; + } + } + + childLayer->setParent(this); + + if (!found) + m_children.append(childLayer); +} + +void GraphicsLayer::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + childLayer->removeFromParent(); + ASSERT(childLayer != this); + + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (sibling == m_children[i]) { + m_children.insert(i+1, childLayer); + found = true; + break; + } + } + + childLayer->setParent(this); + + if (!found) + m_children.append(childLayer); +} + +bool GraphicsLayer::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + ASSERT(!newChild->parent()); + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (oldChild == m_children[i]) { + m_children[i] = newChild; + found = true; + break; + } + } + if (found) { + oldChild->setParent(0); + + newChild->removeFromParent(); + newChild->setParent(this); + return true; + } + return false; +} + +void GraphicsLayer::removeAllChildren() +{ + while (m_children.size()) { + GraphicsLayer* curLayer = m_children[0]; + ASSERT(curLayer->parent()); + curLayer->removeFromParent(); + } +} + +void GraphicsLayer::removeFromParent() +{ + if (m_parent) { + unsigned i; + for (i = 0; i < m_parent->m_children.size(); i++) { + if (this == m_parent->m_children[i]) { + m_parent->m_children.remove(i); + break; + } + } + + setParent(0); + } +} + +void GraphicsLayer::setBackgroundColor(const Color& inColor, const Animation*, double /*beginTime*/) +{ + m_backgroundColor = inColor; + m_backgroundColorSet = true; +} + +void GraphicsLayer::clearBackgroundColor() +{ + m_backgroundColor = Color(); + m_backgroundColorSet = false; +} + +bool GraphicsLayer::setOpacity(float opacity, const Animation*, double) +{ + m_opacity = opacity; + return false; // not animating +} + +void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const IntRect& clip) +{ + m_client->paintContents(this, context, m_paintingPhase, clip); +} + +String GraphicsLayer::propertyIdToString(AnimatedPropertyID property) +{ + switch (property) { + case AnimatedPropertyWebkitTransform: + return "transform"; + case AnimatedPropertyOpacity: + return "opacity"; + case AnimatedPropertyBackgroundColor: + return "backgroundColor"; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return ""; +} + +int GraphicsLayer::findAnimationEntry(AnimatedPropertyID property, short index) const +{ + for (size_t i = 0; i < m_animations.size(); ++i) { + if (m_animations[i].matches(property, index)) + return static_cast<int>(i); + } + return -1; +} + +void GraphicsLayer::addAnimationEntry(AnimatedPropertyID property, short index, bool isTransition, const Animation* transition) +{ + int i = findAnimationEntry(property, index); + + if (i >= 0) + m_animations[i].reset(transition, isTransition); + else + m_animations.append(AnimationEntry(transition, property, index, isTransition)); +} + +void GraphicsLayer::removeAllAnimations() +{ + size_t size = m_animations.size(); + for (size_t i = 0; i < size; ++i) + removeAnimation(0, true); +} + +void GraphicsLayer::removeAllAnimationsForProperty(AnimatedPropertyID property) +{ + for (short j = 0; ; ++j) { + int i = findAnimationEntry(property, j); + if (i < 0) + break; + removeAnimation(i, false); + } +} + +void GraphicsLayer::removeFinishedAnimations(const String& name, int /*index*/, bool reset) +{ + size_t size = m_animations.size(); + for (size_t i = 0; i < size; ) { + AnimationEntry& anim = m_animations[i]; + if (!anim.isTransition() && anim.animation()->name() == name) { + removeAnimation(i, reset); + --size; + } else + ++i; + } +} + +void GraphicsLayer::removeFinishedTransitions(AnimatedPropertyID property) +{ + size_t size = m_animations.size(); + for (size_t i = 0; i < size; ) { + AnimationEntry& anim = m_animations[i]; + if (anim.isTransition() && property == anim.property()) { + removeAnimation(i, false); + --size; + } else + ++i; + } +} + +void GraphicsLayer::suspendAnimations() +{ +} + +void GraphicsLayer::resumeAnimations() +{ +} + +#ifndef NDEBUG +void GraphicsLayer::updateDebugIndicators() +{ + if (GraphicsLayer::showDebugBorders()) { + if (drawsContent()) { + if (m_usingTiledLayer) + setDebugBorder(Color(0, 255, 0, 204), 2.0f); // tiled layer: green + else + setDebugBorder(Color(255, 0, 0, 204), 2.0f); // normal layer: red + } else if (masksToBounds()) { + setDebugBorder(Color(128, 255, 255, 178), 2.0f); // masking layer: pale blue + if (GraphicsLayer::showDebugBorders()) + setDebugBackgroundColor(Color(128, 255, 255, 52)); + } else + setDebugBorder(Color(255, 255, 0, 204), 2.0f); // container: yellow + } +} + +void GraphicsLayer::setZPosition(float position) +{ + m_zPosition = position; +} +#endif + +static void writeIndent(TextStream& ts, int indent) +{ + for (int i = 0; i != indent; ++i) + ts << " "; +} + +void GraphicsLayer::dumpLayer(TextStream& ts, int indent) const +{ + writeIndent(ts, indent); + ts << "(" << "GraphicsLayer" << " " << static_cast<void*>(const_cast<GraphicsLayer*>(this)); + ts << " \"" << m_name << "\"\n"; + dumpProperties(ts, indent); + writeIndent(ts, indent); + ts << ")\n"; +} + +void GraphicsLayer::dumpProperties(TextStream& ts, int indent) const +{ + writeIndent(ts, indent + 1); + ts << "(position " << m_position.x() << " " << m_position.y() << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y() << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(bounds " << m_size.width() << " " << m_size.height() << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(opacity " << m_opacity << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(usingTiledLayer " << m_usingTiledLayer << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(m_preserves3D " << m_preserves3D << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(drawsContent " << m_drawsContent << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(m_backfaceVisibility " << (m_backfaceVisibility ? "visible" : "hidden") << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(client "; + if (m_client) + ts << static_cast<void*>(m_client); + else + ts << "none"; + ts << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(backgroundColor "; + if (!m_backgroundColorSet) + ts << "none"; + else + ts << m_backgroundColor.name(); + ts << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(transform "; + if (m_transform.isIdentity()) + ts << "identity"; + else { + ts << "[" << m_transform.m11() << " " << m_transform.m12() << " " << m_transform.m13() << " " << m_transform.m14() << "] "; + ts << "[" << m_transform.m21() << " " << m_transform.m22() << " " << m_transform.m23() << " " << m_transform.m24() << "] "; + ts << "[" << m_transform.m31() << " " << m_transform.m32() << " " << m_transform.m33() << " " << m_transform.m34() << "] "; + ts << "[" << m_transform.m41() << " " << m_transform.m42() << " " << m_transform.m43() << " " << m_transform.m44() << "]"; + } + ts << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(childrenTransform "; + if (m_childrenTransform.isIdentity()) + ts << "identity"; + else { + ts << "[" << m_childrenTransform.m11() << " " << m_childrenTransform.m12() << " " << m_childrenTransform.m13() << " " << m_childrenTransform.m14() << "] "; + ts << "[" << m_childrenTransform.m21() << " " << m_childrenTransform.m22() << " " << m_childrenTransform.m23() << " " << m_childrenTransform.m24() << "] "; + ts << "[" << m_childrenTransform.m31() << " " << m_childrenTransform.m32() << " " << m_childrenTransform.m33() << " " << m_childrenTransform.m34() << "] "; + ts << "[" << m_childrenTransform.m41() << " " << m_childrenTransform.m42() << " " << m_childrenTransform.m43() << " " << m_childrenTransform.m44() << "]"; + } + ts << ")\n"; + + 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 + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/GraphicsLayer.h b/WebCore/platform/graphics/GraphicsLayer.h new file mode 100644 index 0000000..f928ce8 --- /dev/null +++ b/WebCore/platform/graphics/GraphicsLayer.h @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GraphicsLayer_h +#define GraphicsLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "Animation.h" +#include "Color.h" +#include "FloatPoint.h" +#include "FloatPoint3D.h" +#include "FloatSize.h" +#include "GraphicsLayerClient.h" +#include "TransformationMatrix.h" +#include "TransformOperations.h" +#include <wtf/OwnPtr.h> + +#if PLATFORM(MAC) +#ifdef __OBJC__ +@class WebLayer; +@class CALayer; +typedef WebLayer PlatformLayer; +typedef CALayer* NativeLayer; +#else +typedef void* PlatformLayer; +typedef void* NativeLayer; +#endif +#else +typedef void* PlatformLayer; +typedef void* NativeLayer; +#endif + +namespace WebCore { + +class FloatPoint3D; +class GraphicsContext; +class Image; +class TextStream; +class TimingFunction; + +// GraphicsLayer is an abstraction for a rendering surface with backing store, +// which may have associated transformation and animations. + +class GraphicsLayer { +public: + // Used to store one float value of a keyframe animation. + class FloatValue { + public: + FloatValue(float key, float value, const TimingFunction* timingFunction = 0) + : m_key(key), m_value(value), m_timingFunction(0) + { + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + } + + FloatValue(const FloatValue& other) + : m_key(other.key()), m_value(other.value()), m_timingFunction(0) + { + if (other.timingFunction()) + m_timingFunction.set(new TimingFunction(*other.timingFunction())); + } + + const FloatValue& operator=(const FloatValue& other) + { + if (&other != this) + set(other.key(), other.value(), other.timingFunction()); + return *this; + } + + void set(float key, float value, const TimingFunction*); + + float key() const { return m_key; } + float value() const { return m_value; } + const TimingFunction* timingFunction() const { return m_timingFunction.get(); } + + private: + float m_key; + float m_value; + OwnPtr<TimingFunction> m_timingFunction; + }; + + + class FloatValueList { + public: + void insert(float key, float value, const TimingFunction* timingFunction); + + size_t size() const { return m_values.size(); } + const FloatValue& at(size_t i) const { return m_values.at(i); } + const Vector<FloatValue>& values() const { return m_values; } + + private: + Vector<FloatValue> m_values; + }; + + // Used to store one transform in a keyframe list. + class TransformValue { + public: + TransformValue(float key = NAN, const TransformOperations* value = 0, const TimingFunction* timingFunction = 0) + : m_key(key) + { + if (value) + m_value.set(new TransformOperations(*value)); + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + } + + TransformValue(const TransformValue& other) + : m_key(other.key()) + { + if (other.value()) + m_value.set(new TransformOperations(*other.value())); + if (other.timingFunction()) + m_timingFunction.set(new TimingFunction(*other.timingFunction())); + } + + const TransformValue& operator=(const TransformValue& other) + { + if (&other != this) + set(other.key(), other.value(), other.timingFunction()); + return *this; + } + + void set(float key, const TransformOperations* value, const TimingFunction* timingFunction); + + float key() const { return m_key; } + const TransformOperations* value() const { return m_value.get(); } + const TimingFunction* timingFunction() const { return m_timingFunction.get(); } + + private: + float m_key; + OwnPtr<TransformOperations> m_value; + OwnPtr<TimingFunction> m_timingFunction; + }; + + // Used to store a series of transforms in a keyframe list. + class TransformValueList { + public: + typedef Vector<TransformOperation::OperationType> FunctionList; + + size_t size() const { return m_values.size(); } + const TransformValue& at(size_t i) const { return m_values.at(i); } + const Vector<TransformValue>& values() const { return m_values; } + + void insert(float key, const TransformOperations* value, const TimingFunction* timingFunction); + + // return a list of the required functions. List is empty if keyframes are not valid + // If return value is true, functions contain rotations of >= 180 degrees + void makeFunctionList(FunctionList& list, bool& isValid, bool& hasBigRotation) const; + private: + Vector<TransformValue> m_values; + }; + + static GraphicsLayer* createGraphicsLayer(GraphicsLayerClient*); + + virtual ~GraphicsLayer(); + + GraphicsLayerClient* client() const { return m_client; } + + // Layer name. Only used to identify layers in debug output + const String& name() const { return m_name; } + virtual void setName(const String& name) { m_name = name; } + + // For hosting this GraphicsLayer in a native layer hierarchy. + virtual NativeLayer nativeLayer() const { return 0; } + + GraphicsLayer* parent() const { return m_parent; }; + void setParent(GraphicsLayer* layer) { m_parent = layer; } // Internal use only. + + const Vector<GraphicsLayer*>& children() const { return m_children; } + + // Add child layers. If the child is already parented, it will be removed from its old parent. + 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); + + void removeAllChildren(); + virtual void removeFromParent(); + + // 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; } + + // The position of the layer (the location of its top-left corner in its parent) + const FloatPoint& position() const { return m_position; } + virtual void setPosition(const FloatPoint& p) { m_position = p; } + + // Anchor point: (0, 0) is top left, (1, 1) is bottom right. The anchor point + // affects the origin of the transforms. + const FloatPoint3D& anchorPoint() const { return m_anchorPoint; } + virtual void setAnchorPoint(const FloatPoint3D& p) { m_anchorPoint = p; } + + // The bounds of the layer + const FloatSize& size() const { return m_size; } + virtual void setSize(const FloatSize& size) { m_size = size; } + + const TransformationMatrix& transform() const { return m_transform; } + virtual void setTransform(const TransformationMatrix& t) { m_transform = t; } + + const TransformationMatrix& childrenTransform() const { return m_childrenTransform; } + virtual void setChildrenTransform(const TransformationMatrix& t) { m_childrenTransform = t; } + + bool preserves3D() const { return m_preserves3D; } + virtual void setPreserves3D(bool b) { m_preserves3D = b; } + + bool masksToBounds() const { return m_masksToBounds; } + virtual void setMasksToBounds(bool b) { m_masksToBounds = b; } + + bool drawsContent() const { return m_drawsContent; } + virtual void setDrawsContent(bool b) { m_drawsContent = b; } + + // The color used to paint the layer backgrounds + const Color& backgroundColor() const { return m_backgroundColor; } + virtual void setBackgroundColor(const Color&, const Animation* = 0, double beginTime = 0); + virtual void clearBackgroundColor(); + bool backgroundColorSet() const { return m_backgroundColorSet; } + + // opaque means that we know the layer contents have no alpha + bool contentsOpaque() const { return m_contentsOpaque; } + virtual void setContentsOpaque(bool b) { m_contentsOpaque = b; } + + bool backfaceVisibility() const { return m_backfaceVisibility; } + virtual void setBackfaceVisibility(bool b) { m_backfaceVisibility = b; } + + float opacity() const { return m_opacity; } + // return true if we started an animation + virtual bool setOpacity(float o, const Animation* = 0, double beginTime = 0); + + // Some GraphicsLayers paint only the foreground or the background content + GraphicsLayerPaintingPhase drawingPhase() const { return m_paintingPhase; } + void setDrawingPhase(GraphicsLayerPaintingPhase phase) { m_paintingPhase = phase; } + + virtual void setNeedsDisplay() = 0; + // mark the given rect (in layer coords) as needing dispay. Never goes deep. + virtual void setNeedsDisplayInRect(const FloatRect&) = 0; + + virtual bool animateTransform(const TransformValueList&, const IntSize&, const Animation*, double beginTime, bool isTransition) = 0; + virtual bool animateFloat(AnimatedPropertyID, const FloatValueList&, const Animation*, double beginTime) = 0; + + void removeFinishedAnimations(const String& name, int index, bool reset); + void removeFinishedTransitions(AnimatedPropertyID); + void removeAllAnimations(); + + virtual void suspendAnimations(); + virtual void resumeAnimations(); + + // Layer contents + virtual void setContentsToImage(Image*) { } + virtual void setContentsToVideo(PlatformLayer*) { } + virtual void setContentsBackgroundColor(const Color&) { } + virtual void clearContents() { } + + virtual void updateContentsRect() { } + + // Callback from the underlying graphics system to draw layer contents. + void paintGraphicsLayerContents(GraphicsContext&, const IntRect& clip); + + virtual PlatformLayer* platformLayer() const { return 0; } + + void dumpLayer(TextStream&, int indent = 0) const; + +#ifndef NDEBUG + int repaintCount() const { return m_repaintCount; } + int incrementRepaintCount() { return ++m_repaintCount; } +#endif + + // Platform behaviors + static bool graphicsContextsFlipped(); + +#ifndef NDEBUG + static bool showDebugBorders(); + static bool showRepaintCounter(); + + void updateDebugIndicators(); + + virtual void setDebugBackgroundColor(const Color&) { } + virtual void setDebugBorder(const Color&, float /*borderWidth*/) { } + // z-position is the z-equivalent of position(). It's only used for debugging purposes. + virtual float zPosition() const { return m_zPosition; } + virtual void setZPosition(float); +#endif + + static String propertyIdToString(AnimatedPropertyID); + +protected: + GraphicsLayer(GraphicsLayerClient*); + + void dumpProperties(TextStream&, int indent) const; + + // returns -1 if not found + int findAnimationEntry(AnimatedPropertyID, short index) const; + void addAnimationEntry(AnimatedPropertyID, short index, bool isTransition, const Animation*); + + virtual void removeAnimation(int /*index*/, bool /*reset*/) {} + void removeAllAnimationsForProperty(AnimatedPropertyID); + + GraphicsLayerClient* m_client; + String m_name; + + // Offset from the owning renderer + IntSize m_offsetFromRenderer; + + // Position is relative to the parent GraphicsLayer + FloatPoint m_position; + FloatPoint3D m_anchorPoint; + FloatSize m_size; + TransformationMatrix m_transform; + TransformationMatrix m_childrenTransform; + + Color m_backgroundColor; + float m_opacity; +#ifndef NDEBUG + float m_zPosition; +#endif + + bool m_backgroundColorSet : 1; + bool m_contentsOpaque : 1; + bool m_preserves3D: 1; + bool m_backfaceVisibility : 1; + bool m_usingTiledLayer : 1; + bool m_masksToBounds : 1; + bool m_drawsContent : 1; + + GraphicsLayerPaintingPhase m_paintingPhase; + + Vector<GraphicsLayer*> m_children; + GraphicsLayer* m_parent; + + // AnimationEntry represents an animation of a property on this layer. + // For transform only, there may be more than one, in which case 'index' + // is an index into the list of transforms. + class AnimationEntry { + public: + AnimationEntry(const Animation* animation, AnimatedPropertyID property, short index, bool isTransition) + : m_animation(const_cast<Animation*>(animation)) + , m_property(property) + , m_index(index) + , m_isCurrent(true) + , m_isTransition(isTransition) + { + } + + const Animation* animation() const { return m_animation.get(); } + AnimatedPropertyID property() const { return m_property; } + int index() const { return m_index; } + bool isCurrent() const { return m_isCurrent; } + void setIsCurrent(bool b = true) { m_isCurrent = b; } + bool isTransition() const { return m_isTransition; } + + bool matches(AnimatedPropertyID property, short index) const + { + return m_property == property && m_index == index; + } + + void reset(const Animation* animation, bool isTransition) + { + m_animation = const_cast<Animation*>(animation); + m_isTransition = isTransition; + m_isCurrent = true; + } + + private: + RefPtr<Animation> m_animation; + AnimatedPropertyID m_property : 14; + short m_index : 16; + bool m_isCurrent : 1; + bool m_isTransition : 1; + }; + + Vector<AnimationEntry> m_animations; // running animations/transitions + +#ifndef NDEBUG + int m_repaintCount; +#endif +}; + + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayer_h + diff --git a/WebCore/platform/graphics/GraphicsLayerClient.h b/WebCore/platform/graphics/GraphicsLayerClient.h new file mode 100644 index 0000000..46382f2 --- /dev/null +++ b/WebCore/platform/graphics/GraphicsLayerClient.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GraphicsLayerClient_h +#define GraphicsLayerClient_h + +#if USE(ACCELERATED_COMPOSITING) + +namespace WebCore { + +class GraphicsContext; +class GraphicsLayer; +class IntPoint; +class IntRect; +class FloatPoint; + +enum GraphicsLayerPaintingPhase { + GraphicsLayerPaintBackgroundMask = (1 << 0), + GraphicsLayerPaintForegroundMask = (1 << 1), + GraphicsLayerPaintAllMask = (GraphicsLayerPaintBackgroundMask | GraphicsLayerPaintForegroundMask) +}; + +enum AnimatedPropertyID { + AnimatedPropertyInvalid, + AnimatedPropertyWebkitTransform, + AnimatedPropertyOpacity, + AnimatedPropertyBackgroundColor +}; + +class GraphicsLayerClient { +public: + virtual ~GraphicsLayerClient() {} + + // Callbacks for when hardware-accelerated transitions and animation started + virtual void notifyAnimationStarted(const GraphicsLayer*, double time) = 0; + + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) = 0; + + // Return a rect for the "contents" of the graphics layer, i.e. video or image content, in GraphicsLayer coordinates. + virtual IntRect contentsBox(const GraphicsLayer*) = 0; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayerClient_h diff --git a/WebCore/platform/graphics/GraphicsTypes.h b/WebCore/platform/graphics/GraphicsTypes.h index cdf5e31..769207a 100644 --- a/WebCore/platform/graphics/GraphicsTypes.h +++ b/WebCore/platform/graphics/GraphicsTypes.h @@ -50,6 +50,14 @@ namespace WebCore { CompositePlusLighter }; + // FIXME: Currently these constants have to match the values used in the SVG + // DOM API. That's a mistake. We need to make cut that dependency. + enum GradientSpreadMethod { + SpreadMethodPad = 1, + SpreadMethodReflect = 2, + SpreadMethodRepeat = 3 + }; + enum LineCap { ButtCap, RoundCap, SquareCap }; enum LineJoin { MiterJoin, RoundJoin, BevelJoin }; diff --git a/WebCore/platform/graphics/Image.cpp b/WebCore/platform/graphics/Image.cpp index 49961e1..08d96b4 100644 --- a/WebCore/platform/graphics/Image.cpp +++ b/WebCore/platform/graphics/Image.cpp @@ -119,7 +119,7 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); - TransformationMatrix patternTransform = TransformationMatrix().scale(scale.width(), scale.height()); + TransformationMatrix patternTransform = TransformationMatrix().scaleNonUniform(scale.width(), scale.height()); FloatRect oneTileRect; oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), scaledTileSize.width()) - scaledTileSize.width(), scaledTileSize.width())); @@ -158,7 +158,7 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& dstRect, const Flo vRule = RepeatTile; FloatSize scale = calculatePatternScale(dstRect, srcRect, hRule, vRule); - TransformationMatrix patternTransform = TransformationMatrix().scale(scale.width(), scale.height()); + TransformationMatrix patternTransform = TransformationMatrix().scaleNonUniform(scale.width(), scale.height()); // We want to construct the phase such that the pattern is centered (when stretch is not // set for a particular rule). diff --git a/WebCore/platform/graphics/Image.h b/WebCore/platform/graphics/Image.h index c3cf2e7..70f6d49 100644 --- a/WebCore/platform/graphics/Image.h +++ b/WebCore/platform/graphics/Image.h @@ -118,9 +118,9 @@ public: SharedBuffer* data() { return m_data.get(); } - // It may look unusual that there is no start animation call as public API. This is because - // we start and stop animating lazily. Animation begins whenever someone draws the image. It will - // automatically pause once all observers no longer want to render the image anywhere. + // Animation begins whenever someone draws the image, so startAnimation() is not normally called. + // It will automatically pause once all observers no longer want to render the image anywhere. + virtual void startAnimation(bool /*catchUpIfNecessary*/ = true) { } virtual void stopAnimation() {} virtual void resetAnimation() {} @@ -164,11 +164,9 @@ protected: void drawTiled(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, TileRule hRule, TileRule vRule, CompositeOperator); // Supporting tiled drawing - virtual bool mayFillWithSolidColor() const { return false; } + virtual bool mayFillWithSolidColor() { return false; } virtual Color solidColor() const { return Color(); } - virtual void startAnimation(bool /*catchUpIfNecessary*/ = true) { } - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); #if PLATFORM(CG) diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index 21e31fc..99d6aa4 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,7 +27,9 @@ #if ENABLE(VIDEO) #include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" +#include "ContentType.h" #include "IntRect.h" #include "MIMETypeRegistry.h" #include "FrameView.h" @@ -47,26 +49,196 @@ #endif namespace WebCore { + +// a null player to make MediaPlayer logic simpler + +class NullMediaPlayerPrivate : public MediaPlayerPrivateInterface { +public: + NullMediaPlayerPrivate(MediaPlayer*) { } + + virtual void load(const String&) { } + virtual void cancelLoad() { } + + virtual void play() { } + virtual void pause() { } + + virtual IntSize naturalSize() const { return IntSize(0, 0); } + + virtual bool hasVideo() const { return false; } + + virtual void setVisible(bool) { } + + virtual float duration() const { return 0; } + + virtual float currentTime() const { return 0; } + virtual void seek(float) { } + virtual bool seeking() const { return false; } + + virtual void setEndTime(float) { } + + virtual void setRate(float) { } + virtual bool paused() const { return false; } + + virtual void setVolume(float) { } + + virtual MediaPlayer::NetworkState networkState() const { return MediaPlayer::Empty; } + virtual MediaPlayer::ReadyState readyState() const { return MediaPlayer::HaveNothing; } + + virtual float maxTimeSeekable() const { return 0; } + virtual float maxTimeBuffered() const { return 0; } + + virtual int dataRate() const { return 0; } + + virtual bool totalBytesKnown() const { return false; } + virtual unsigned totalBytes() const { return 0; } + virtual unsigned bytesLoaded() const { return 0; } + + virtual void setSize(const IntSize&) { } + + virtual void paint(GraphicsContext*, const IntRect&) { } + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + virtual void setPoster(const String& /*url*/) { } + virtual void deliverNotification(MediaPlayerProxyNotificationType) { } + virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) { } +#endif +}; + +static MediaPlayerPrivateInterface* createNullMediaPlayer(MediaPlayer* player) +{ + return new NullMediaPlayerPrivate(player); +} + + +// engine support + +struct MediaPlayerFactory { + MediaPlayerFactory(CreateMediaEnginePlayer constructor, MediaEngineSupportedTypes getSupportedTypes, MediaEngineSupportsType supportsTypeAndCodecs) + : constructor(constructor) + , getSupportedTypes(getSupportedTypes) + , supportsTypeAndCodecs(supportsTypeAndCodecs) + { + } + + CreateMediaEnginePlayer constructor; + MediaEngineSupportedTypes getSupportedTypes; + MediaEngineSupportsType supportsTypeAndCodecs; +}; + +static void addMediaEngine(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType); +static MediaPlayerFactory* chooseBestEngineForTypeAndCodecs(const String& type, const String& codecs); + +static Vector<MediaPlayerFactory*>& installedMediaEngines() +{ + DEFINE_STATIC_LOCAL(Vector<MediaPlayerFactory*>, installedEngines, ()); + static bool enginesQueried = false; + + if (!enginesQueried) { + enginesQueried = true; + MediaPlayerPrivate::registerMediaEngine(addMediaEngine); + + // register additional engines here + } - MediaPlayer::MediaPlayer(MediaPlayerClient* client) + return installedEngines; +} + +static void addMediaEngine(CreateMediaEnginePlayer constructor, MediaEngineSupportedTypes getSupportedTypes, MediaEngineSupportsType supportsType) +{ + ASSERT(constructor); + ASSERT(getSupportedTypes); + ASSERT(supportsType); + installedMediaEngines().append(new MediaPlayerFactory(constructor, getSupportedTypes, supportsType)); +} + +static MediaPlayerFactory* chooseBestEngineForTypeAndCodecs(const String& type, const String& codecs) +{ + Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); + + if (engines.isEmpty()) + return 0; + + MediaPlayerFactory* engine = 0; + MediaPlayer::SupportsType supported = MediaPlayer::IsNotSupported; + + unsigned count = engines.size(); + for (unsigned ndx = 0; ndx < count; ndx++) { + MediaPlayer::SupportsType engineSupport = engines[ndx]->supportsTypeAndCodecs(type, codecs); + if (engineSupport > supported) { + supported = engineSupport; + engine = engines[ndx]; + } + } + + return engine; +} + +// media player + +MediaPlayer::MediaPlayer(MediaPlayerClient* client) : m_mediaPlayerClient(client) - , m_private(new MediaPlayerPrivate(this)) + , m_private(createNullMediaPlayer(this)) + , m_currentMediaEngine(0) , m_frameView(0) , m_visible(false) , m_rate(1.0f) , m_volume(1.0f) +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + , m_playerProxy(0) +#endif { +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); + if (!engines.isEmpty()) { + m_currentMediaEngine = engines[0]; + m_private.clear(); + m_private.set(engines[0]->constructor(this)); + } +#endif } MediaPlayer::~MediaPlayer() { - delete m_private; } -void MediaPlayer::load(const String& url) +void MediaPlayer::load(const String& url, const ContentType& contentType) +{ + String type = contentType.type(); + String codecs = contentType.parameter("codecs"); + + // if we don't know the MIME type, see if the path can help + if (type.isEmpty()) + type = MIMETypeRegistry::getMIMETypeForPath(url); + + MediaPlayerFactory* engine = chooseBestEngineForTypeAndCodecs(type, codecs); + + // if we didn't find an engine that claims the MIME type, just use the first engine + if (!engine) + engine = installedMediaEngines()[0]; + + // don't delete and recreate the player unless it comes from a different engine + if (engine && m_currentMediaEngine != engine) { + m_currentMediaEngine = engine; + m_private.clear(); + m_private.set(engine->constructor(this)); +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + m_private->setMediaPlayerProxy(m_playerProxy); +#endif + + } + + if (m_private) + m_private->load(url); + else + m_private.set(createNullMediaPlayer(this)); +} + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +void MediaPlayer::setPoster(const String& url) { - m_private->load(url); + m_private->setPoster(url); } +#endif void MediaPlayer::cancelLoad() { @@ -90,7 +262,7 @@ float MediaPlayer::duration() const float MediaPlayer::currentTime() const { - return m_private->currentTime(); + return m_private->currentTime(); } void MediaPlayer::seek(float time) @@ -193,10 +365,10 @@ unsigned MediaPlayer::totalBytes() return m_private->totalBytes(); } -void MediaPlayer::setRect(const IntRect& r) +void MediaPlayer::setSize(const IntSize& size) { - m_rect = r; - m_private->setRect(r); + m_size = size; + m_private->setSize(size); } bool MediaPlayer::visible() const @@ -215,29 +387,47 @@ void MediaPlayer::paint(GraphicsContext* p, const IntRect& r) m_private->paint(p, r); } -bool MediaPlayer::supportsType(const String& type) +MediaPlayer::SupportsType MediaPlayer::supportsType(ContentType contentType) { - HashSet<String> types; - getSupportedTypes(types); - return MIMETypeRegistry::isSupportedMediaMIMEType(type) && types.contains(type); + String type = contentType.type(); + String codecs = contentType.parameter("codecs"); + MediaPlayerFactory* engine = chooseBestEngineForTypeAndCodecs(type, codecs); + + if (!engine) + return IsNotSupported; + + return engine->supportsTypeAndCodecs(type, codecs); } void MediaPlayer::getSupportedTypes(HashSet<String>& types) { - MediaPlayerPrivate::getSupportedTypes(types); + Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); + if (engines.isEmpty()) + return; + + unsigned count = engines.size(); + for (unsigned ndx = 0; ndx < count; ndx++) + engines[ndx]->getSupportedTypes(types); } - + bool MediaPlayer::isAvailable() { - static bool availabityKnown = false; - static bool isAvailable; - if (!availabityKnown) { - isAvailable = MediaPlayerPrivate::isAvailable(); - availabityKnown = true; - } - return isAvailable; + return !installedMediaEngines().isEmpty(); } +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +void MediaPlayer::deliverNotification(MediaPlayerProxyNotificationType notification) +{ + m_private->deliverNotification(notification); +} + +void MediaPlayer::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) +{ + m_playerProxy = proxy; + m_private->setMediaPlayerProxy(proxy); +} +#endif + void MediaPlayer::networkStateChanged() { if (m_mediaPlayerClient) @@ -262,11 +452,29 @@ void MediaPlayer::timeChanged() m_mediaPlayerClient->mediaPlayerTimeChanged(this); } +void MediaPlayer::sizeChanged() +{ + if (m_mediaPlayerClient) + m_mediaPlayerClient->mediaPlayerSizeChanged(this); +} + void MediaPlayer::repaint() { if (m_mediaPlayerClient) m_mediaPlayerClient->mediaPlayerRepaint(this); } +void MediaPlayer::durationChanged() +{ + if (m_mediaPlayerClient) + m_mediaPlayerClient->mediaPlayerDurationChanged(this); +} + +void MediaPlayer::rateChanged() +{ + if (m_mediaPlayerClient) + m_mediaPlayerClient->mediaPlayerRateChanged(this); +} + } #endif diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 203f299..7d90e44 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -28,52 +28,78 @@ #if ENABLE(VIDEO) +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "MediaPlayerProxy.h" +#endif + #include "IntRect.h" #include "StringHash.h" #include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> #include <wtf/Noncopyable.h> namespace WebCore { +class ContentType; class FrameView; class GraphicsContext; +class IntRect; class IntSize; class MediaPlayer; -class MediaPlayerPrivate; +class MediaPlayerPrivateInterface; class String; class MediaPlayerClient { public: virtual ~MediaPlayerClient() { } + + // the network state has changed virtual void mediaPlayerNetworkStateChanged(MediaPlayer*) { } + + // the ready state has changed virtual void mediaPlayerReadyStateChanged(MediaPlayer*) { } + + // the volume or muted state has changed virtual void mediaPlayerVolumeChanged(MediaPlayer*) { } + + // time has jumped, eg. not as a result of normal playback virtual void mediaPlayerTimeChanged(MediaPlayer*) { } + + // a new frame of video is available virtual void mediaPlayerRepaint(MediaPlayer*) { } + + // the media file duration has changed, or is now known + virtual void mediaPlayerDurationChanged(MediaPlayer*) { } + + // the playback rate has changed + virtual void mediaPlayerRateChanged(MediaPlayer*) { } + + // the movie size has changed + virtual void mediaPlayerSizeChanged(MediaPlayer*) { } }; class MediaPlayer : Noncopyable { public: MediaPlayer(MediaPlayerClient*); virtual ~MediaPlayer(); - - static bool isAvailable(); - static bool supportsType(const String&); + + // media engine support + enum SupportsType { IsNotSupported, IsSupported, MayBeSupported }; + static MediaPlayer::SupportsType supportsType(ContentType contentType); static void getSupportedTypes(HashSet<String>&); + static bool isAvailable(); IntSize naturalSize(); bool hasVideo(); void setFrameView(FrameView* frameView) { m_frameView = frameView; } + FrameView* frameView() { return m_frameView; } bool inMediaDocument(); - // FIXME: it would be better to just have a getter and setter for size. - // This is currently an absolute rect, which is not appropriate for - // content with transforms - IntRect rect() const { return m_rect; } - void setRect(const IntRect& r); + IntSize size() const { return m_size; } + void setSize(const IntSize& size); - void load(const String& url); + void load(const String& url, const ContentType& contentType); void cancelLoad(); bool visible() const; @@ -96,7 +122,7 @@ public: float maxTimeBuffered(); float maxTimeSeekable(); - + unsigned bytesLoaded(); bool totalBytesKnown(); unsigned totalBytes(); @@ -108,33 +134,55 @@ public: void paint(GraphicsContext*, const IntRect&); - enum NetworkState { Empty, LoadFailed, Loading, LoadedMetaData, LoadedFirstFrame, Loaded }; + enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError }; NetworkState networkState(); - enum ReadyState { DataUnavailable, CanShowCurrentFrame, CanPlay, CanPlayThrough }; + enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData }; ReadyState readyState(); void networkStateChanged(); void readyStateChanged(); void volumeChanged(); void timeChanged(); + void sizeChanged(); + void rateChanged(); + void durationChanged(); void repaint(); - + + MediaPlayerClient* mediaPlayerClient() const { return m_mediaPlayerClient; } + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + void setPoster(const String& url); + void deliverNotification(MediaPlayerProxyNotificationType notification); + void setMediaPlayerProxy(WebMediaPlayerProxy* proxy); +#endif + private: - - friend class MediaPlayerPrivate; - + static void initializeMediaEngines(); + MediaPlayerClient* m_mediaPlayerClient; - MediaPlayerPrivate* m_private; + OwnPtr<MediaPlayerPrivateInterface*> m_private; + void* m_currentMediaEngine; FrameView* m_frameView; - IntRect m_rect; + IntSize m_size; bool m_visible; float m_rate; float m_volume; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + WebMediaPlayerProxy* m_playerProxy; // not owned or used, passed to m_private +#endif }; +typedef MediaPlayerPrivateInterface* (*CreateMediaEnginePlayer)(MediaPlayer*); +typedef void (*MediaEngineSupportedTypes)(HashSet<String>& types); +typedef MediaPlayer::SupportsType (*MediaEngineSupportsType)(const String& type, const String& codecs); + +typedef void (*MediaEngineRegistrar)(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType); + + } -#endif +#endif // ENABLE(VIDEO) + #endif diff --git a/WebCore/platform/graphics/MediaPlayerPrivate.h b/WebCore/platform/graphics/MediaPlayerPrivate.h new file mode 100644 index 0000000..2e73e7e --- /dev/null +++ b/WebCore/platform/graphics/MediaPlayerPrivate.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlayerPrivate_h +#define MediaPlayerPrivate_h + +#if ENABLE(VIDEO) + +#include "MediaPlayer.h" + +namespace WebCore { + +class IntRect; +class IntSize; +class String; + +class MediaPlayerPrivateInterface { +public: + virtual ~MediaPlayerPrivateInterface() { } + + virtual void load(const String& url) = 0; + virtual void cancelLoad() = 0; + + virtual void play() = 0; + virtual void pause() = 0; + + virtual IntSize naturalSize() const = 0; + + virtual bool hasVideo() const = 0; + + virtual void setVisible(bool) = 0; + + virtual float duration() const = 0; + + virtual float currentTime() const = 0; + virtual void seek(float time) = 0; + virtual bool seeking() const = 0; + + virtual void setEndTime(float time) = 0; + + virtual void setRate(float) = 0; + virtual bool paused() const = 0; + + virtual void setVolume(float) = 0; + + virtual MediaPlayer::NetworkState networkState() const = 0; + virtual MediaPlayer::ReadyState readyState() const = 0; + + virtual float maxTimeSeekable() const = 0; + virtual float maxTimeBuffered() 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; + + virtual void paint(GraphicsContext*, const IntRect&) = 0 ; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + virtual void setPoster(const String& url) = 0; + virtual void deliverNotification(MediaPlayerProxyNotificationType) = 0; + virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) = 0; +#endif +}; + +} + +#endif +#endif diff --git a/WebCore/platform/graphics/Pattern.h b/WebCore/platform/graphics/Pattern.h index 716a645..6981748 100644 --- a/WebCore/platform/graphics/Pattern.h +++ b/WebCore/platform/graphics/Pattern.h @@ -30,6 +30,7 @@ #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> +#include "TransformationMatrix.h" #if PLATFORM(CG) typedef struct CGPattern* CGPatternRef; @@ -67,7 +68,9 @@ namespace WebCore { Image* tileImage() const { return m_tileImage.get(); } - PlatformPatternPtr createPlatformPattern(const TransformationMatrix& patternTransform) const; + // Pattern space is an abstract space that maps to the default user space by the transformation 'userSpaceTransformation' + PlatformPatternPtr createPlatformPattern(const TransformationMatrix& userSpaceTransformation) const; + void setPatternSpaceTransform(const TransformationMatrix& patternSpaceTransformation) { m_patternSpaceTransformation = patternSpaceTransformation; } private: Pattern(Image*, bool repeatX, bool repeatY); @@ -75,6 +78,7 @@ namespace WebCore { RefPtr<Image> m_tileImage; bool m_repeatX; bool m_repeatY; + TransformationMatrix m_patternSpaceTransformation; }; } //namespace diff --git a/WebCore/platform/graphics/SimpleFontData.cpp b/WebCore/platform/graphics/SimpleFontData.cpp index 9670b55..9f51037 100644 --- a/WebCore/platform/graphics/SimpleFontData.cpp +++ b/WebCore/platform/graphics/SimpleFontData.cpp @@ -136,20 +136,6 @@ SimpleFontData::~SimpleFontData() } } -#if !PLATFORM(QT) -float SimpleFontData::widthForGlyph(Glyph glyph) const -{ - float width = m_glyphToWidthMap.widthForGlyph(glyph); - if (width != cGlyphWidthUnknown) - return width; - - width = platformWidthForGlyph(glyph); - m_glyphToWidthMap.setWidthForGlyph(glyph, width); - - return width; -} -#endif - const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const { return this; diff --git a/WebCore/platform/graphics/SimpleFontData.h b/WebCore/platform/graphics/SimpleFontData.h index e572e30..d2dd0b9 100644 --- a/WebCore/platform/graphics/SimpleFontData.h +++ b/WebCore/platform/graphics/SimpleFontData.h @@ -131,7 +131,7 @@ public: #endif #if PLATFORM(WX) - wxFont getWxFont() const { return m_font.font(); } + wxFont* getWxFont() const { return m_font.font(); } #endif private: @@ -205,6 +205,21 @@ public: mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties; #endif }; + + +#if !PLATFORM(QT) +ALWAYS_INLINE float SimpleFontData::widthForGlyph(Glyph glyph) const +{ + float width = m_glyphToWidthMap.widthForGlyph(glyph); + if (width != cGlyphWidthUnknown) + return width; + + width = platformWidthForGlyph(glyph); + m_glyphToWidthMap.setWidthForGlyph(glyph, width); + + return width; +} +#endif } // namespace WebCore diff --git a/WebCore/platform/graphics/android/FontPlatformData.h b/WebCore/platform/graphics/android/FontPlatformData.h index 2bb8834..35b7200 100644 --- a/WebCore/platform/graphics/android/FontPlatformData.h +++ b/WebCore/platform/graphics/android/FontPlatformData.h @@ -42,6 +42,8 @@ public: FontPlatformData(const FontPlatformData&); FontPlatformData(SkTypeface*, float textSize, bool fakeBold, bool fakeItalic); FontPlatformData(const FontPlatformData& src, float textSize); + FontPlatformData(float size, bool syntheticBold, bool syntheticOblique); + ~FontPlatformData(); FontPlatformData(WTF::HashTableDeletedValueType) diff --git a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp index e82c1f6..4496408 100644 --- a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp +++ b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp @@ -103,6 +103,13 @@ FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) trace(4); } +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) + : mTypeface(NULL), mTextSize(size), mFakeBold(bold), mFakeItalic(oblique) +{ + inc_count(); + trace(5); +} + FontPlatformData::~FontPlatformData() { dec_count(); diff --git a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp index 6a92a7f..7571926 100644 --- a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp +++ b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp @@ -315,7 +315,8 @@ static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm) { } static void extactShader(SkPaint* paint, ColorSpace cs, Pattern* pat, - Gradient* grad, GradientSpreadMethod sm) { + Gradient* grad) +{ switch (cs) { case PatternColorSpace: // createPlatformPattern() returns a new inst @@ -324,6 +325,7 @@ static void extactShader(SkPaint* paint, ColorSpace cs, Pattern* pat, break; case GradientColorSpace: { // grad->getShader() returns a cached obj + GradientSpreadMethod sm = grad->spreadMethod(); paint->setShader(grad->getShader(SpreadMethod2TileMode(sm))); break; } @@ -670,7 +672,7 @@ void GraphicsContext::fillRect(const FloatRect& rect) extactShader(&paint, m_common->state.fillColorSpace, m_common->state.fillPattern.get(), - m_common->state.fillGradient.get(), spreadMethod()); + m_common->state.fillGradient.get()); GC2Canvas(this)->drawRect(r, paint); } @@ -1077,7 +1079,13 @@ void GraphicsContext::setPlatformShouldAntialias(bool useAA) TransformationMatrix GraphicsContext::getCTM() const { - return TransformationMatrix(GC2Canvas(this)->getTotalMatrix()); + const SkMatrix& m = GC2Canvas(this)->getTotalMatrix(); + return TransformationMatrix(SkScalarToDouble(m.getScaleX()), // a + SkScalarToDouble(m.getSkewY()), // b + SkScalarToDouble(m.getSkewX()), // c + SkScalarToDouble(m.getScaleY()), // d + SkScalarToDouble(m.getTranslateX()), // e + SkScalarToDouble(m.getTranslateY())); // f } /////////////////////////////////////////////////////////////////////////////// @@ -1118,7 +1126,7 @@ void GraphicsContext::fillPath() extactShader(&paint, m_common->state.fillColorSpace, m_common->state.fillPattern.get(), - m_common->state.fillGradient.get(), spreadMethod()); + m_common->state.fillGradient.get()); GC2Canvas(this)->drawPath(*path, paint); } @@ -1134,7 +1142,7 @@ void GraphicsContext::strokePath() extactShader(&paint, m_common->state.strokeColorSpace, m_common->state.strokePattern.get(), - m_common->state.strokeGradient.get(), spreadMethod()); + m_common->state.strokeGradient.get()); GC2Canvas(this)->drawPath(*path, paint); } diff --git a/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp b/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp index 6a4c670..a57abc0 100644 --- a/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp +++ b/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp @@ -1,232 +1,56 @@ -/* - * Copyright 2007, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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. - */ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "config.h" #include "TransformationMatrix.h" -#include "FloatRect.h" -#include "IntRect.h" - -#include "android_graphics.h" +#include "SkiaUtils.h" namespace WebCore { -static const double deg2rad = 0.017453292519943295769; // pi/180 - -TransformationMatrix::TransformationMatrix() -{ - m_transform.reset(); -} - -TransformationMatrix::TransformationMatrix(const SkMatrix& mat) : m_transform(mat) {} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform.reset(); - - m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); - m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); - m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(tx)); - - m_transform.set(SkMatrix::kMScaleY, SkDoubleToScalar(d)); - m_transform.set(SkMatrix::kMSkewY, SkDoubleToScalar(c)); - m_transform.set(SkMatrix::kMTransY, SkDoubleToScalar(ty)); -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); - m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); - m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(tx)); - - m_transform.set(SkMatrix::kMScaleY, SkDoubleToScalar(d)); - m_transform.set(SkMatrix::kMSkewY, SkDoubleToScalar(c)); - m_transform.set(SkMatrix::kMTransY, SkDoubleToScalar(ty)); -} - -void TransformationMatrix::map(double x, double y, double *x2, double *y2) const -{ - SkPoint pt; - - m_transform.mapXY(SkDoubleToScalar(x), SkDoubleToScalar(y), &pt); - *x2 = SkScalarToDouble(pt.fX); - *y2 = SkScalarToDouble(pt.fY); -} - -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ - SkRect src, dst; - SkIRect ir; - - android_setrect(&src, rect); - m_transform.mapRect(&dst, src); - // we round out to mimic enclosingIntRect() - dst.roundOut(&ir); - - return IntRect(ir.fLeft, ir.fTop, ir.width(), ir.height()); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const -{ - SkRect r; - - android_setrect(&r, rect); - m_transform.mapRect(&r); - - return FloatRect(r.fLeft, r.fTop, r.width(), r.height()); -} - -bool TransformationMatrix::isIdentity() const -{ - return m_transform.isIdentity(); -} - -void TransformationMatrix::reset() -{ - m_transform.reset(); -} - -double TransformationMatrix::a() const -{ - return SkScalarToDouble(m_transform[0]); -} - -void TransformationMatrix::setA(double a) -{ - m_transform.set(0, SkDoubleToScalar(a)); -} - -double TransformationMatrix::b() const -{ - return SkScalarToDouble(m_transform[1]); -} - -void TransformationMatrix::setB(double b) -{ - m_transform.set(1, SkDoubleToScalar(b)); -} - -double TransformationMatrix::c() const -{ - return SkScalarToDouble(m_transform[3]); -} - -void TransformationMatrix::setC(double c) -{ - m_transform.set(3, SkDoubleToScalar(c)); -} - -double TransformationMatrix::d() const { - return SkScalarToDouble(m_transform[4]); -} - -void TransformationMatrix::setD(double d) -{ - m_transform.set(4, SkDoubleToScalar(d)); -} - -double TransformationMatrix::e() const -{ - return SkScalarToDouble(m_transform[2]); -} - -void TransformationMatrix::setE(double e) -{ - m_transform.set(2, SkDoubleToScalar(e)); -} - -double TransformationMatrix::f() const { - return SkScalarToDouble(m_transform[5]); -} -void TransformationMatrix::setF(double f) { - m_transform.set(5, SkDoubleToScalar(f)); -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - m_transform.preScale(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - m_transform.preRotate(SkDoubleToScalar(d)); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - m_transform.preTranslate(SkDoubleToScalar(tx), SkDoubleToScalar(ty)); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - m_transform.preSkew(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); - return *this; -} - -double TransformationMatrix::det() const -{ - return SkScalarToDouble(m_transform[SkMatrix::kMScaleX]) * SkScalarToDouble(m_transform[SkMatrix::kMScaleY]) - - SkScalarToDouble(m_transform[SkMatrix::kMSkewX]) * SkScalarToDouble(m_transform[SkMatrix::kMSkewY]); -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - // the constructor initializes inverse to the identity - TransformationMatrix inverse; - - // if we are not invertible, inverse will stay identity - m_transform.invert(&inverse.m_transform); - - return inverse; -} - TransformationMatrix::operator SkMatrix() const { - return m_transform; -} + SkMatrix result; -bool TransformationMatrix::operator==(const TransformationMatrix &m2) const -{ - return m_transform == m2.m_transform; -} + result.setScaleX(WebCoreDoubleToSkScalar(a())); + result.setSkewX(WebCoreDoubleToSkScalar(c())); + result.setTranslateX(WebCoreDoubleToSkScalar(e())); -TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) -{ - m_transform.setConcat(m2.m_transform, m_transform); - return *this; -} + result.setScaleY(WebCoreDoubleToSkScalar(d())); + result.setSkewY(WebCoreDoubleToSkScalar(b())); + result.setTranslateY(WebCoreDoubleToSkScalar(f())); -TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) -{ - TransformationMatrix cat; - - cat.m_transform.setConcat(m2.m_transform, m_transform); - return cat; + // FIXME: Set perspective properly. + result.setPerspX(0); + result.setPerspY(0); + result.set(SkMatrix::kMPersp2, SK_Scalar1); + return result; } -} +} // namespace WebCore diff --git a/WebCore/platform/graphics/cairo/FontCairo.cpp b/WebCore/platform/graphics/cairo/FontCairo.cpp index 9da9426..b23182d 100644 --- a/WebCore/platform/graphics/cairo/FontCairo.cpp +++ b/WebCore/platform/graphics/cairo/FontCairo.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,8 +30,11 @@ #include "Font.h" #include "GlyphBuffer.h" +#include "Gradient.h" #include "GraphicsContext.h" +#include "Pattern.h" #include "SimpleFontData.h" +#include "TransformationMatrix.h" namespace WebCore { @@ -78,18 +82,51 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons } if (context->textDrawingMode() & cTextFill) { - float red, green, blue, alpha; - fillColor.getRGBA(red, green, blue, alpha); - cairo_set_source_rgba(cr, red, green, blue, alpha); - + if (context->fillGradient()) { + cairo_set_source(cr, context->fillGradient()->platformGradient()); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else if (context->fillPattern()) { + TransformationMatrix affine; + cairo_set_source(cr, context->fillPattern()->createPlatformPattern(affine)); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else { + float red, green, blue, alpha; + fillColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); + } cairo_show_glyphs(cr, glyphs, numGlyphs); } if (context->textDrawingMode() & cTextStroke) { - Color strokeColor = context->strokeColor(); - float red, green, blue, alpha; - strokeColor.getRGBA(red, green, blue, alpha); - cairo_set_source_rgba(cr, red, green, blue, alpha); + if (context->strokeGradient()) { + cairo_set_source(cr, context->strokeGradient()->platformGradient()); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else if (context->strokePattern()) { + TransformationMatrix affine; + cairo_set_source(cr, context->strokePattern()->createPlatformPattern(affine)); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else { + Color strokeColor = context->strokeColor(); + float red, green, blue, alpha; + strokeColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); + } cairo_glyph_path(cr, glyphs, numGlyphs); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); diff --git a/WebCore/platform/graphics/cairo/GradientCairo.cpp b/WebCore/platform/graphics/cairo/GradientCairo.cpp index 7776424..72fb0c5 100644 --- a/WebCore/platform/graphics/cairo/GradientCairo.cpp +++ b/WebCore/platform/graphics/cairo/GradientCairo.cpp @@ -57,6 +57,22 @@ cairo_pattern_t* Gradient::platformGradient() ++stopIterator; } + switch (m_spreadMethod) { + case SpreadMethodPad: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_PAD); + break; + case SpreadMethodReflect: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_REFLECT); + break; + case SpreadMethodRepeat: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_REPEAT); + break; + } + + cairo_matrix_t matrix = m_gradientSpaceTransformation; + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(m_gradient, &matrix); + return m_gradient; } diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index ef748cf..35ebd3c 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -87,22 +87,6 @@ static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const cairo_fill(cr); } -static inline cairo_pattern_t* applySpreadMethod(cairo_pattern_t* pattern, GradientSpreadMethod spreadMethod) -{ - switch (spreadMethod) { - case SpreadMethodPad: - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); - break; - case SpreadMethodReflect: - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT); - break; - case SpreadMethodRepeat: - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - break; - } - return pattern; -} - GraphicsContext::GraphicsContext(PlatformGraphicsContext* cr) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate) @@ -122,7 +106,7 @@ TransformationMatrix GraphicsContext::getCTM() const cairo_t* cr = platformContext(); cairo_matrix_t m; cairo_get_matrix(cr, &m); - return m; + return TransformationMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); } cairo_t* GraphicsContext::platformContext() const @@ -463,7 +447,6 @@ void GraphicsContext::fillPath() } case GradientColorSpace: cairo_pattern_t* pattern = m_common->state.fillGradient->platformGradient(); - pattern = applySpreadMethod(pattern, spreadMethod()); cairo_set_source(cr, pattern); cairo_clip(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); @@ -501,7 +484,6 @@ void GraphicsContext::strokePath() } case GradientColorSpace: cairo_pattern_t* pattern = m_common->state.strokeGradient->platformGradient(); - pattern = applySpreadMethod(pattern, spreadMethod()); cairo_set_source(cr, pattern); if (m_common->state.globalAlpha < 1.0f) { cairo_push_group(cr); @@ -750,8 +732,8 @@ void GraphicsContext::concatCTM(const TransformationMatrix& transform) return; cairo_t* cr = m_data->cr; - const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); - cairo_transform(cr, matrix); + const cairo_matrix_t matrix = cairo_matrix_t(transform); + cairo_transform(cr, &matrix); m_data->concatCTM(transform); } diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index 535f70d..55b2e25 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -51,6 +51,7 @@ public: // NOTE: These may note be needed: review and remove once Cairo implementation is complete , m_hdc(0) , m_transparencyCount(0) + , m_shouldIncludeChildWindows(false) #endif { } @@ -94,6 +95,7 @@ public: #elif PLATFORM(WIN) HDC m_hdc; unsigned m_transparencyCount; + bool m_shouldIncludeChildWindows; #endif }; diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 3e06669..dff39b7 100644 --- a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) 2007 Holger Hans Peter Freyther <zecke@selfish.org> - * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> + * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,6 +30,7 @@ #include "Base64.h" #include "BitmapImage.h" +#include "Color.h" #include "GraphicsContext.h" #include "ImageData.h" #include "MIMETypeRegistry.h" @@ -126,17 +127,15 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4; for (int y = 0; y < numRows; ++y) { - unsigned char *row = dataSrc + stride * (y + originy); + unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * (y + originy)); for (int x = 0; x < numColumns; x++) { - uint32_t *pixel = (uint32_t *) row + x + originx; int basex = x * 4; - if (unsigned int alpha = (*pixel & 0xff000000) >> 24) { - destRows[basex] = (*pixel & 0x00ff0000) >> 16; - destRows[basex + 1] = (*pixel & 0x0000ff00) >> 8; - destRows[basex + 2] = (*pixel & 0x000000ff); - destRows[basex + 3] = alpha; - } else - reinterpret_cast<uint32_t*>(destRows + basex)[0] = pixel[0]; + unsigned* pixel = row + x + originx; + Color pixelColor = colorFromPremultipliedARGB(*pixel); + destRows[basex] = pixelColor.red(); + destRows[basex + 1] = pixelColor.green(); + destRows[basex + 2] = pixelColor.blue(); + destRows[basex + 3] = pixelColor.alpha(); } destRows += destBytesPerRow; } @@ -181,14 +180,15 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, con unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4; for (int y = 0; y < numRows; ++y) { - unsigned char *row = dataDst + stride * (y + desty); + unsigned* row = reinterpret_cast<unsigned*>(dataDst + stride * (y + desty)); for (int x = 0; x < numColumns; x++) { - uint32_t *pixel = (uint32_t *) row + x + destx; int basex = x * 4; - if (unsigned int alpha = srcRows[basex + 3]) { - *pixel = alpha << 24 | srcRows[basex] << 16 | srcRows[basex + 1] << 8 | srcRows[basex + 2]; - } else - pixel[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + unsigned* pixel = row + x + destx; + Color pixelColor(srcRows[basex], + srcRows[basex + 1], + srcRows[basex + 2], + srcRows[basex + 3]); + *pixel = premultipliedARGBFromColor(pixelColor); } srcRows += srcBytesPerRow; } diff --git a/WebCore/platform/graphics/cairo/ImageCairo.cpp b/WebCore/platform/graphics/cairo/ImageCairo.cpp index 2850488..224154e 100644 --- a/WebCore/platform/graphics/cairo/ImageCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageCairo.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,10 +30,11 @@ #if PLATFORM(CAIRO) -#include "TransformationMatrix.h" +#include "Color.h" #include "FloatRect.h" #include "GraphicsContext.h" #include "ImageObserver.h" +#include "TransformationMatrix.h" #include <cairo.h> #include <math.h> @@ -60,6 +62,7 @@ BitmapImage::BitmapImage(cairo_surface_t* surface, ImageObserver* observer) , m_repetitionCountStatus(Unknown) , m_repetitionsComplete(0) , m_isSolidColor(false) + , m_checkedForSolidColor(false) , m_animationFinished(true) , m_allDataReceived(true) , m_haveSize(true) @@ -180,8 +183,28 @@ void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, con void BitmapImage::checkForSolidColor() { - // FIXME: It's easy to implement this optimization. Just need to check the RGBA32 buffer to see if it is 1x1. m_isSolidColor = false; + m_checkedForSolidColor = true; + + if (frameCount() > 1) + return; + + cairo_surface_t* frameSurface = frameAtIndex(0); + if (!frameSurface) + return; + + ASSERT(cairo_surface_get_type(frameSurface) == CAIRO_SURFACE_TYPE_IMAGE); + + int width = cairo_image_surface_get_width(frameSurface); + int height = cairo_image_surface_get_height(frameSurface); + + if (width != 1 || height != 1) + return; + + unsigned* pixelColor = reinterpret_cast<unsigned*>(cairo_image_surface_get_data(frameSurface)); + m_solidColor = colorFromPremultipliedARGB(*pixelColor); + + m_isSolidColor = true; } } diff --git a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp index 6841599..c6d54f2 100644 --- a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp @@ -111,7 +111,7 @@ void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* delete m_decoder; m_decoder = 0; if (data) - setData(data, allDataReceived); + setData(data, allDataReceived); } bool ImageSource::initialized() const diff --git a/WebCore/platform/graphics/cairo/PatternCairo.cpp b/WebCore/platform/graphics/cairo/PatternCairo.cpp index 7d75db3..58c5d00 100644 --- a/WebCore/platform/graphics/cairo/PatternCairo.cpp +++ b/WebCore/platform/graphics/cairo/PatternCairo.cpp @@ -26,23 +26,26 @@ #include "config.h" #include "Pattern.h" -#include "TransformationMatrix.h" #include "GraphicsContext.h" +#include "TransformationMatrix.h" #include <cairo.h> namespace WebCore { -cairo_pattern_t* Pattern::createPlatformPattern(const TransformationMatrix& patternTransform) const +cairo_pattern_t* Pattern::createPlatformPattern(const TransformationMatrix&) const { cairo_surface_t* surface = tileImage()->nativeImageForCurrentFrame(); if (!surface) return 0; cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface); - const TransformationMatrix& inverse = patternTransform.inverse(); - const cairo_matrix_t* pattern_matrix = reinterpret_cast<const cairo_matrix_t*>(&inverse); - cairo_pattern_set_matrix(pattern, pattern_matrix); + + // cairo merges patter space and user space itself + cairo_matrix_t matrix = m_patternSpaceTransformation; + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + if (m_repeatX || m_repeatY) cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); return pattern; diff --git a/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp b/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp index b78620f..1b83a29 100644 --- a/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp +++ b/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp @@ -32,248 +32,18 @@ namespace WebCore { -static const double deg2rad = 0.017453292519943295769; // pi/180 - -TransformationMatrix::TransformationMatrix() -{ - cairo_matrix_init_identity(&m_transform); -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) -{ - cairo_matrix_init(&m_transform, a, b, c, d, tx, ty); -} - -TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) -{ - m_transform = matrix; -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) -{ - cairo_matrix_init(&m_transform, a, b, c, d, tx, ty); -} - -void TransformationMatrix::map(double x, double y, double* x2, double* y2) const -{ - *x2 = x; - *y2 = y; - cairo_matrix_transform_point(&m_transform, x2, y2); -} - -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ - FloatRect floatRect(rect); - FloatRect enclosingFloatRect = this->mapRect(floatRect); - - return enclosingIntRect(enclosingFloatRect); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const -{ - double rectMinX = rect.x(); - double rectMaxX = rect.x() + rect.width(); - double rectMinY = rect.y(); - double rectMaxY = rect.y() + rect.height(); - - double px = rectMinX; - double py = rectMinY; - cairo_matrix_transform_point(&m_transform, &px, &py); - - double enclosingRectMinX = px; - double enclosingRectMinY = py; - double enclosingRectMaxX = px; - double enclosingRectMaxY = py; - - px = rectMaxX; - py = rectMinY; - cairo_matrix_transform_point(&m_transform, &px, &py); - if (px < enclosingRectMinX) - enclosingRectMinX = px; - else if (px > enclosingRectMaxX) - enclosingRectMaxX = px; - if (py < enclosingRectMinY) - enclosingRectMinY = py; - else if (py > enclosingRectMaxY) - enclosingRectMaxY = py; - - px = rectMaxX; - py = rectMaxY; - cairo_matrix_transform_point(&m_transform, &px, &py); - if (px < enclosingRectMinX) - enclosingRectMinX = px; - else if (px > enclosingRectMaxX) - enclosingRectMaxX = px; - if (py < enclosingRectMinY) - enclosingRectMinY = py; - else if (py > enclosingRectMaxY) - enclosingRectMaxY = py; - - px = rectMinX; - py = rectMaxY; - cairo_matrix_transform_point(&m_transform, &px, &py); - if (px < enclosingRectMinX) - enclosingRectMinX = px; - else if (px > enclosingRectMaxX) - enclosingRectMaxX = px; - if (py < enclosingRectMinY) - enclosingRectMinY = py; - else if (py > enclosingRectMaxY) - enclosingRectMaxY = py; - - - double enclosingRectWidth = enclosingRectMaxX - enclosingRectMinX; - double enclosingRectHeight = enclosingRectMaxY - enclosingRectMinY; - - return FloatRect(enclosingRectMinX, enclosingRectMinY, enclosingRectWidth, enclosingRectHeight); -} - -bool TransformationMatrix::isIdentity() const -{ - return ((m_transform.xx == 1) && (m_transform.yy == 1) - && (m_transform.xy == 0) && (m_transform.yx == 0) - && (m_transform.x0 == 0) && (m_transform.y0 == 0)); -} - -double TransformationMatrix::a() const -{ - return m_transform.xx; -} - -void TransformationMatrix::setA(double a) -{ - m_transform.xx = a; -} - -double TransformationMatrix::b() const -{ - return m_transform.yx; -} - -void TransformationMatrix::setB(double b) -{ - m_transform.yx = b; -} - -double TransformationMatrix::c() const -{ - return m_transform.xy; -} - -void TransformationMatrix::setC(double c) -{ - m_transform.xy = c; -} - -double TransformationMatrix::d() const -{ - return m_transform.yy; -} - -void TransformationMatrix::setD(double d) -{ - m_transform.yy = d; -} - -double TransformationMatrix::e() const -{ - return m_transform.x0; -} - -void TransformationMatrix::setE(double e) -{ - m_transform.x0 = e; -} - -double TransformationMatrix::f() const -{ - return m_transform.y0; -} - -void TransformationMatrix::setF(double f) -{ - m_transform.y0 = f; -} - -void TransformationMatrix::reset() -{ - cairo_matrix_init_identity(&m_transform); -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - cairo_matrix_scale(&m_transform, sx, sy); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - cairo_matrix_rotate(&m_transform, d * deg2rad); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - cairo_matrix_translate(&m_transform, tx, ty); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - cairo_matrix_t shear; - cairo_matrix_init(&shear, 1, sy, sx, 1, 0, 0); - - cairo_matrix_t result; - cairo_matrix_multiply(&result, &shear, &m_transform); - m_transform = result; - - return *this; -} - -double TransformationMatrix::det() const -{ - return m_transform.xx * m_transform.yy - m_transform.xy * m_transform.yx; -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - if (!isInvertible()) return TransformationMatrix(); - - cairo_matrix_t result = m_transform; - cairo_matrix_invert(&result); - return TransformationMatrix(result); -} - TransformationMatrix::operator cairo_matrix_t() const { - return m_transform; -} + cairo_matrix_t m; -bool TransformationMatrix::operator== (const TransformationMatrix &m2) const -{ - return ((m_transform.xx == m2.m_transform.xx) - && (m_transform.yy == m2.m_transform.yy) - && (m_transform.xy == m2.m_transform.xy) - && (m_transform.yx == m2.m_transform.yx) - && (m_transform.x0 == m2.m_transform.x0) - && (m_transform.y0 == m2.m_transform.y0)); - -} - -TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) -{ - cairo_matrix_t result; - cairo_matrix_multiply(&result, &m_transform, &m2.m_transform); - m_transform = result; - - return *this; -} - -TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) -{ - cairo_matrix_t result; - cairo_matrix_multiply(&result, &m_transform, &m2.m_transform); - return result; + cairo_matrix_init (&m, + a(), + b(), + c(), + d(), + e(), + f()); + return m; } } diff --git a/WebCore/platform/graphics/cg/ColorCG.cpp b/WebCore/platform/graphics/cg/ColorCG.cpp index 48ce9f2..0465c0b 100644 --- a/WebCore/platform/graphics/cg/ColorCG.cpp +++ b/WebCore/platform/graphics/cg/ColorCG.cpp @@ -67,9 +67,9 @@ Color::Color(CGColorRef color) m_color = makeRGBA(r * 255, g * 255, b * 255, a * 255); } -#if !PLATFORM(MAC) +#if PLATFORM(WIN_OS) -CGColorRef cgColor(const Color& c) +CGColorRef createCGColor(const Color& c) { CGColorRef color = NULL; CMProfileRef prof = NULL; @@ -89,7 +89,7 @@ CGColorRef cgColor(const Color& c) return color; } -#endif // !PLATFORM(MAC) +#endif // PLATFORM(WIN_OS) } diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index 1cc55a4..4b8a555 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -504,6 +504,7 @@ void GraphicsContext::fillPath() CGContextEOClip(context); else CGContextClip(context); + CGContextConcatCTM(context, m_common->state.fillGradient->gradientSpaceTransform()); CGContextDrawShading(context, m_common->state.fillGradient->platformGradient()); CGContextRestoreGState(context); break; @@ -529,6 +530,7 @@ void GraphicsContext::strokePath() CGContextSaveGState(context); CGContextReplacePathWithStrokedPath(context); CGContextClip(context); + CGContextConcatCTM(context, m_common->state.strokeGradient->gradientSpaceTransform()); CGContextDrawShading(context, m_common->state.strokeGradient->platformGradient()); CGContextRestoreGState(context); break; @@ -552,6 +554,7 @@ void GraphicsContext::fillRect(const FloatRect& rect) case GradientColorSpace: CGContextSaveGState(context); CGContextClipToRect(context, rect); + CGContextConcatCTM(context, m_common->state.fillGradient->gradientSpaceTransform()); CGContextDrawShading(context, m_common->state.fillGradient->platformGradient()); CGContextRestoreGState(context); break; @@ -734,7 +737,7 @@ void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Col if (!color.isValid()) CGContextSetShadow(context, CGSizeMake(width, height), blurRadius); else { - CGColorRef colorCG = cgColor(color); + CGColorRef colorCG = createCGColor(color); CGContextSetShadowWithColor(context, CGSizeMake(width, height), blurRadius, @@ -907,7 +910,8 @@ void GraphicsContext::concatCTM(const TransformationMatrix& transform) TransformationMatrix GraphicsContext::getCTM() const { - return CGContextGetCTM(platformContext()); + CGAffineTransform t = CGContextGetCTM(platformContext()); + return TransformationMatrix(t.a, t.b, t.c, t.d, t.tx, t.ty); } FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) diff --git a/WebCore/platform/graphics/cg/ImageCG.cpp b/WebCore/platform/graphics/cg/ImageCG.cpp index 13c8c07..dbf1d85 100644 --- a/WebCore/platform/graphics/cg/ImageCG.cpp +++ b/WebCore/platform/graphics/cg/ImageCG.cpp @@ -73,6 +73,7 @@ BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer) , m_repetitionCountStatus(Unknown) , m_repetitionsComplete(0) , m_isSolidColor(false) + , m_checkedForSolidColor(false) , m_animationFinished(true) , m_allDataReceived(true) , m_haveSize(true) @@ -99,6 +100,7 @@ BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer) void BitmapImage::checkForSolidColor() { + m_checkedForSolidColor = true; if (frameCount() > 1) m_isSolidColor = false; else { diff --git a/WebCore/platform/graphics/cg/ImageSourceCG.cpp b/WebCore/platform/graphics/cg/ImageSourceCG.cpp index 0b276cc..c059985 100644 --- a/WebCore/platform/graphics/cg/ImageSourceCG.cpp +++ b/WebCore/platform/graphics/cg/ImageSourceCG.cpp @@ -58,7 +58,7 @@ void ImageSource::clear(bool, size_t, SharedBuffer* data, bool allDataReceived) m_decoder = 0; } if (data) - setData(data, allDataReceived); + setData(data, allDataReceived); } static CFDictionaryRef imageSourceOptions() diff --git a/WebCore/platform/graphics/cg/PatternCG.cpp b/WebCore/platform/graphics/cg/PatternCG.cpp index 2b9c12f..697bc57 100644 --- a/WebCore/platform/graphics/cg/PatternCG.cpp +++ b/WebCore/platform/graphics/cg/PatternCG.cpp @@ -50,12 +50,13 @@ static void patternReleaseCallback(void* info) static_cast<Image*>(info)->deref(); } -CGPatternRef Pattern::createPlatformPattern(const TransformationMatrix& transform) const +CGPatternRef Pattern::createPlatformPattern(const TransformationMatrix& userSpaceTransformation) const { IntRect tileRect = tileImage()->rect(); - TransformationMatrix patternTransform = transform; - patternTransform.scale(1, -1); + TransformationMatrix patternTransform = m_patternSpaceTransformation; + patternTransform.multiply(userSpaceTransformation); + patternTransform.scaleNonUniform(1, -1); patternTransform.translate(0, -tileRect.height()); // If FLT_MAX should also be used for xStep or yStep, nothing is rendered. Using fractions of FLT_MAX also diff --git a/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp b/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp index 9b3181a..568a6b3 100644 --- a/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp +++ b/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp @@ -28,187 +28,19 @@ #if PLATFORM(CG) +#include <CoreGraphics/CGAffineTransform.h> #include "FloatConversion.h" -#include "FloatRect.h" -#include "IntRect.h" - -#include <wtf/MathExtras.h> namespace WebCore { -TransformationMatrix::TransformationMatrix() - : m_transform(CGAffineTransformIdentity) -{ -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform = CGAffineTransformMake(narrowPrecisionToCGFloat(a), - narrowPrecisionToCGFloat(b), - narrowPrecisionToCGFloat(c), - narrowPrecisionToCGFloat(d), - narrowPrecisionToCGFloat(tx), - narrowPrecisionToCGFloat(ty)); -} - -TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& t) - : m_transform(t) -{ -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform = CGAffineTransformMake(narrowPrecisionToCGFloat(a), - narrowPrecisionToCGFloat(b), - narrowPrecisionToCGFloat(c), - narrowPrecisionToCGFloat(d), - narrowPrecisionToCGFloat(tx), - narrowPrecisionToCGFloat(ty)); -} - -void TransformationMatrix::map(double x, double y, double *x2, double *y2) const -{ - CGPoint result = CGPointApplyAffineTransform(CGPointMake(narrowPrecisionToCGFloat(x), narrowPrecisionToCGFloat(y)), m_transform); - *x2 = result.x; - *y2 = result.y; -} - -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ - return enclosingIntRect(CGRectApplyAffineTransform(CGRect(rect), m_transform)); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const -{ - return FloatRect(CGRectApplyAffineTransform(CGRect(rect), m_transform)); -} - -bool TransformationMatrix::isIdentity() const -{ - return CGAffineTransformIsIdentity(m_transform); -} - -double TransformationMatrix::a() const -{ - return m_transform.a; -} - -void TransformationMatrix::setA(double a) -{ - m_transform.a = narrowPrecisionToCGFloat(a); -} - -double TransformationMatrix::b() const -{ - return m_transform.b; -} - -void TransformationMatrix::setB(double b) -{ - m_transform.b = narrowPrecisionToCGFloat(b); -} - -double TransformationMatrix::c() const -{ - return m_transform.c; -} - -void TransformationMatrix::setC(double c) -{ - m_transform.c = narrowPrecisionToCGFloat(c); -} - -double TransformationMatrix::d() const -{ - return m_transform.d; -} - -void TransformationMatrix::setD(double d) -{ - m_transform.d = narrowPrecisionToCGFloat(d); -} - -double TransformationMatrix::e() const -{ - return m_transform.tx; -} - -void TransformationMatrix::setE(double e) -{ - m_transform.tx = narrowPrecisionToCGFloat(e); -} - -double TransformationMatrix::f() const -{ - return m_transform.ty; -} - -void TransformationMatrix::setF(double f) -{ - m_transform.ty = narrowPrecisionToCGFloat(f); -} - -void TransformationMatrix::reset() -{ - m_transform = CGAffineTransformIdentity; -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - m_transform = CGAffineTransformScale(m_transform, narrowPrecisionToCGFloat(sx), narrowPrecisionToCGFloat(sy)); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - m_transform = CGAffineTransformRotate(m_transform, narrowPrecisionToCGFloat(deg2rad(d))); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - m_transform = CGAffineTransformTranslate(m_transform, narrowPrecisionToCGFloat(tx), narrowPrecisionToCGFloat(ty)); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - CGAffineTransform shear = CGAffineTransformMake(1.0f, narrowPrecisionToCGFloat(sy), narrowPrecisionToCGFloat(sx), 1.0f, 0.0f, 0.0f); - m_transform = CGAffineTransformConcat(shear, m_transform); - return *this; -} - -double TransformationMatrix::det() const -{ - return m_transform.a * m_transform.d - m_transform.b * m_transform.c; -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - if (isInvertible()) - return TransformationMatrix(CGAffineTransformInvert(m_transform)); - return TransformationMatrix(); -} - -TransformationMatrix::operator PlatformTransformationMatrix() const -{ - return m_transform; -} - -bool TransformationMatrix::operator== (const TransformationMatrix &m2) const -{ - return CGAffineTransformEqualToTransform(m_transform, CGAffineTransform(m2)); -} - -TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) -{ - m_transform = CGAffineTransformConcat(m_transform, CGAffineTransform(m2)); - return *this; -} - -TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) +TransformationMatrix::operator CGAffineTransform() const { - return CGAffineTransformConcat(m_transform, CGAffineTransform(m2)); + return CGAffineTransformMake(narrowPrecisionToCGFloat(a()), + narrowPrecisionToCGFloat(b()), + narrowPrecisionToCGFloat(c()), + narrowPrecisionToCGFloat(d()), + narrowPrecisionToCGFloat(e()), + narrowPrecisionToCGFloat(f())); } } diff --git a/WebCore/platform/graphics/chromium/ColorChromium.cpp b/WebCore/platform/graphics/chromium/ColorChromium.cpp index 16ca17d..647169c 100644 --- a/WebCore/platform/graphics/chromium/ColorChromium.cpp +++ b/WebCore/platform/graphics/chromium/ColorChromium.cpp @@ -28,35 +28,15 @@ namespace WebCore { +#if !PLATFORM(DARWIN) +// On OS X, there's code to monitor changes in the focus color system setting. +// On Windows/Linux there is no equivalent system setting and therefore a static +// color is all we need. Color focusRingColor() { -// FIXME: This should be split up to ColorChromiumWin and ColorChromiumMac. -#if PLATFORM(DARWIN) - // To avoid the Mac Chromium build having to rebasline 500+ layout tests and - // continue to do this w/ new tests that get landed in WebKit, we want to - // run the layout tests w/ the same color that stock WebKit uses. - // - // TODO: For now we've hard coded the color that WebKit uses for layout - // tests. We need to revisit this and do either of the following: - // A. Fully honor the color from the UI, which means collecting the color - // (and change notifications) in the browser process, and messaging the - // color to the render process. - // B. Adding a "layout tests" flag, to control the orage vs. blue colors - // depending if we're running layout tests. - // To see the WebKit implementation of using the UI color and/or a flag for - // layout tests see WebKit/WebCore/platform/graphics/mac/ColorMac.mm. - // (Reality is we just need an api to override the focus color and both - // of the above are covered for what this file needs to provide, the - // two options would be details that happen in other places.) - - // From WebKit: - // static RGBA32 oldAquaFocusRingColorRGBA = 0xFF7DADD9; - static Color oldAquaFocusRingColor(0x7D, 0xAD, 0xD9, 0xFF); - return oldAquaFocusRingColor; -#else static Color focusRingColor(229, 151, 0, 255); return focusRingColor; -#endif } +#endif } // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/ColorChromiumMac.mm b/WebCore/platform/graphics/chromium/ColorChromiumMac.mm new file mode 100644 index 0000000..01dff7e --- /dev/null +++ b/WebCore/platform/graphics/chromium/ColorChromiumMac.mm @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Color.h" + +#import <AppKit/NSColor.h> +#import <wtf/Assertions.h> +#import <wtf/StdLibExtras.h> +#import <wtf/RetainPtr.h> + +namespace WebCore { + +Color focusRingColor() +{ + // To avoid the Mac Chromium build having to rebasline 500+ layout tests and + // continue to do this w/ new tests that get landed in WebKit, we want to + // run the layout tests w/ the same color that stock WebKit uses. + // + // FIXME: For now we've hard coded the color that WebKit uses for layout + // tests. We need to revisit this and do either of the following: + // A. Fully honor the color from the UI, which means collecting the color + // (and change notifications) in the browser process, and messaging the + // color to the render process. + // B. Adding a "layout tests" flag, to control the orage vs. blue colors + // depending if we're running layout tests. + // To see the WebKit implementation of using the UI color and/or a flag for + // layout tests see WebKit/WebCore/platform/graphics/mac/ColorMac.mm. + // (Reality is we just need an api to override the focus color and both + // of the above are covered for what this file needs to provide, the + // two options would be details that happen in other places.) + + // From WebKit: + // static RGBA32 oldAquaFocusRingColorRGBA = 0xFF7DADD9; + static Color oldAquaFocusRingColor(0x7D, 0xAD, 0xD9, 0xFF); + return oldAquaFocusRingColor; +} + +// createCGColor() and the functions it calls are verbatum copies of +// graphics/mac/ColorMac.mm. These are copied here so that we don't need to +// include ColorMac.mm in the Chromium build. +// FIXME: Check feasibility of using pure CG calls and unifying this copy with +// ColorMac.mm's under graphics/cg. + +NSColor* nsColor(const Color& color) +{ + unsigned c = color.rgb(); + switch (c) { + case 0: { + // Need this to avoid returning nil because cachedRGBAValues will default to 0. + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, clearColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:0.0f])); + return clearColor.get(); + } + case Color::black: { + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, blackColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:1.0f])); + return blackColor.get(); + } + case Color::white: { + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, whiteColor, ([NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f])); + return whiteColor.get(); + } + default: { + const int cacheSize = 32; + static unsigned cachedRGBAValues[cacheSize]; + static RetainPtr<NSColor>* cachedColors = new RetainPtr<NSColor>[cacheSize]; + + for (int i = 0; i != cacheSize; ++i) + if (cachedRGBAValues[i] == c) + return cachedColors[i].get(); + + NSColor* result = [NSColor colorWithDeviceRed:color.red() / 255.0f + green:color.green() / 255.0f + blue:color.blue() / 255.0f + alpha:color.alpha() /255.0f]; + + static int cursor; + cachedRGBAValues[cursor] = c; + cachedColors[cursor] = result; + if (++cursor == cacheSize) + cursor = 0; + return result; + } + } +} + +static CGColorRef CGColorFromNSColor(NSColor* color) +{ + // This needs to always use device colorspace so it can de-calibrate the color for + // CGColor to possibly recalibrate it. + NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + CGFloat red = [deviceColor redComponent]; + CGFloat green = [deviceColor greenComponent]; + CGFloat blue = [deviceColor blueComponent]; + CGFloat alpha = [deviceColor alphaComponent]; + const CGFloat components[4] = { red, green, blue, alpha }; + static CGColorSpaceRef deviceRGBColorSpace = CGColorSpaceCreateDeviceRGB(); + CGColorRef cgColor = CGColorCreate(deviceRGBColorSpace, components); + return cgColor; +} + +CGColorRef createCGColor(const Color& c) +{ + // We could directly create a CGColor here, but that would + // skip any RGB caching the nsColor method does. A direct + // creation could be investigated for a possible performance win. + return CGColorFromNSColor(nsColor(c)); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp index 03583a0..129776e 100644 --- a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp @@ -394,53 +394,18 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, cons family = panUniFonts[i]; data = getCachedFontPlatformData(font.fontDescription(), AtomicString(family, wcslen(family))); } - if (i < numFonts) // we found the font that covers this character ! + // When i-th font (0-base) in |panUniFonts| contains a character and + // we get out of the loop, |i| will be |i + 1|. That is, if only the + // last font in the array covers the character, |i| will be numFonts. + // So, we have to use '<=" rather than '<' to see if we found a font + // covering the character. + if (i <= numFonts) return getCachedFontData(data); return 0; } -const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName) -{ - // Note that mapping to Courier is removed because - // because it's a bitmap font on Windows. - // Alias Courier -> Courier New - static AtomicString courier("Courier"), courierNew("Courier New"); - if (equalIgnoringCase(familyName, courier)) - return courierNew; - - // Alias Times <-> Times New Roman. - static AtomicString times("Times"), timesNewRoman("Times New Roman"); - if (equalIgnoringCase(familyName, times)) - return timesNewRoman; - if (equalIgnoringCase(familyName, timesNewRoman)) - return times; - - // Alias Helvetica <-> Arial - static AtomicString arial("Arial"), helvetica("Helvetica"); - if (equalIgnoringCase(familyName, helvetica)) - return arial; - if (equalIgnoringCase(familyName, arial)) - return helvetica; - - // We block bitmap fonts altogether so that we have to - // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) - static AtomicString msSans("MS Sans Serif"); - static AtomicString microsoftSans("Microsoft Sans Serif"); - if (equalIgnoringCase(familyName, msSans)) - return microsoftSans; - - // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no - // 'Microsoft Sans Serif-equivalent' for Serif. - static AtomicString msSerif("MS Serif"); - if (equalIgnoringCase(familyName, msSerif)) - return timesNewRoman; - - // FIXME: should we map 'system' to something ('Tahoma') ? - return emptyAtom; -} - FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) { return 0; diff --git a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp index f187c55..89433e1 100644 --- a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp @@ -91,16 +91,6 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, return ret; } -const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName) -{ - notImplemented(); - - // This is just to stop GCC emitting a warning about returning a reference - // to a temporary variable - static AtomicString a; - return a; -} - FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) { return 0; diff --git a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp index 3cf18a6..1b71946 100644 --- a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -32,7 +32,6 @@ #include "config.h" #include "Font.h" -#include "TransformationMatrix.h" #include "ChromiumBridge.h" #include "FontFallbackList.h" #include "GlyphBuffer.h" @@ -40,6 +39,7 @@ #include "SimpleFontData.h" #include "SkiaFontWin.h" #include "SkiaUtils.h" +#include "TransparencyWin.h" #include "UniscribeHelperTextRun.h" #include "skia/ext/platform_canvas_win.h" @@ -49,122 +49,274 @@ namespace WebCore { -static bool windowsCanHandleTextDrawing(GraphicsContext* context) +namespace { + +bool canvasHasMultipleLayers(const SkCanvas* canvas) +{ + SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false); + iter.next(); // There is always at least one layer. + return !iter.done(); // There is > 1 layer if the the iterator can stil advance. +} + +class TransparencyAwareFontPainter { +public: + TransparencyAwareFontPainter(GraphicsContext*, const FloatPoint&); + ~TransparencyAwareFontPainter(); + +protected: + // Called by our subclass' constructor to initialize GDI if necessary. This + // is a separate function so it can be called after the subclass finishes + // construction (it calls virtual functions). + void init(); + + virtual IntRect estimateTextBounds() = 0; + + // Use the context from the transparency helper when drawing with GDI. It + // may point to a temporary one. + GraphicsContext* m_graphicsContext; + PlatformGraphicsContext* m_platformContext; + + FloatPoint m_point; + + // Set when Windows can handle the type of drawing we're doing. + bool m_useGDI; + + // These members are valid only when m_useGDI is set. + HDC m_hdc; + TransparencyWin m_transparency; + +private: + // Call when we're using GDI mode to initialize the TransparencyWin to help + // us draw GDI text. + void initializeForGDI(); + + bool m_createdTransparencyLayer; // We created a layer to give the font some alpha. +}; + +TransparencyAwareFontPainter::TransparencyAwareFontPainter(GraphicsContext* context, + const FloatPoint& point) + : m_graphicsContext(context) + , m_platformContext(context->platformContext()) + , m_point(point) + , m_useGDI(windowsCanHandleTextDrawing(context)) + , m_hdc(0) + , m_createdTransparencyLayer(false) +{ +} + +void TransparencyAwareFontPainter::init() { - // Check for non-translation transforms. Sometimes zooms will look better in - // Skia, and sometimes better in Windows. The main problem is that zooming - // in using Skia will show you the hinted outlines for the smaller size, - // which look weird. All else being equal, it's better to use Windows' text - // drawing, so we don't check for zooms. - const TransformationMatrix& matrix = context->getCTM(); - if (matrix.b() != 0 || matrix.c() != 0) // Check for skew. - return false; - - // Check for stroke effects. - if (context->platformContext()->getTextDrawingMode() != cTextFill) - return false; - - // Check for shadow effects. - if (context->platformContext()->getDrawLooper()) - return false; - - return true; + if (m_useGDI) + initializeForGDI(); } -// Skia equivalents to Windows text drawing functions. They -// will get the outlines from Windows and draw then using Skia using the given -// parameters in the paint arguments. This allows more complex effects and -// transforms to be drawn than Windows allows. -// -// These functions will be significantly slower than Windows GDI, and the text -// will look different (no ClearType), so use only when necessary. -// -// When you call a Skia* text drawing function, various glyph outlines will be -// cached. As a result, you should call SkiaWinOutlineCache::removePathsForFont -// when the font is destroyed so that the cache does not outlive the font (since -// the HFONTs are recycled). - -// Analog of the Windows GDI function DrawText, except using the given SkPaint -// attributes for the text. See above for more. -// -// Returns true of the text was drawn successfully. False indicates an error -// from Windows. -static bool skiaDrawText(HFONT hfont, - SkCanvas* canvas, - const SkPoint& point, - SkPaint* paint, - const WORD* glyphs, - const int* advances, - int numGlyphs) +void TransparencyAwareFontPainter::initializeForGDI() { - HDC dc = GetDC(0); - HGDIOBJ oldFont = SelectObject(dc, hfont); - - canvas->save(); - canvas->translate(point.fX, point.fY); - - for (int i = 0; i < numGlyphs; i++) { - const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); - if (!path) - return false; - canvas->drawPath(*path, *paint); - canvas->translate(advances[i], 0); + SkColor color = m_platformContext->effectiveFillColor(); + if (SkColorGetA(color) != 0xFF) { + // When the font has some transparency, apply it by creating a new + // transparency layer with that opacity applied. + m_createdTransparencyLayer = true; + m_graphicsContext->beginTransparencyLayer(SkColorGetA(color) / 255.0f); + // The color should be opaque now. + color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); } - canvas->restore(); + TransparencyWin::LayerMode layerMode; + IntRect layerRect; + if (m_platformContext->isDrawingToImageBuffer()) { + // Assume if we're drawing to an image buffer that the background + // is not opaque and we have to undo ClearType. We may want to + // enhance this to actually check, since it will often be opaque + // and we could do ClearType in that case. + layerMode = TransparencyWin::TextComposite; + layerRect = estimateTextBounds(); + + // The transparency helper requires that we draw text in black in + // this mode and it will apply the color. + m_transparency.setTextCompositeColor(color); + color = SkColorSetRGB(0, 0, 0); + } else if (canvasHasMultipleLayers(m_platformContext->canvas())) { + // When we're drawing a web page, we know the background is opaque, + // but if we're drawing to a layer, we still need extra work. + layerMode = TransparencyWin::OpaqueCompositeLayer; + layerRect = estimateTextBounds(); + } else { + // Common case of drawing onto the bottom layer of a web page: we + // know everything is opaque so don't need to do anything special. + layerMode = TransparencyWin::NoLayer; + } + m_transparency.init(m_graphicsContext, layerMode, TransparencyWin::KeepTransform, layerRect); - SelectObject(dc, oldFont); - ReleaseDC(0, dc); - return true; + // Set up the DC, using the one from the transparency helper. + m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint(); + SetTextColor(m_hdc, skia::SkColorToCOLORREF(color)); + SetBkMode(m_hdc, TRANSPARENT); } -static bool paintSkiaText(PlatformContextSkia* platformContext, - HFONT hfont, - int numGlyphs, - const WORD* glyphs, - const int* advances, - const SkPoint& origin) +TransparencyAwareFontPainter::~TransparencyAwareFontPainter() { - int textMode = platformContext->getTextDrawingMode(); - - // Filling (if necessary). This is the common case. - SkPaint paint; - platformContext->setupPaintForFilling(&paint); - paint.setFlags(SkPaint::kAntiAlias_Flag); - bool didFill = false; - if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) { - if (!skiaDrawText(hfont, platformContext->canvas(), origin, &paint, &glyphs[0], &advances[0], numGlyphs)) - return false; - didFill = true; - } + if (!m_useGDI) + return; // Nothing to do. + m_transparency.composite(); + if (m_createdTransparencyLayer) + m_graphicsContext->endTransparencyLayer(); + m_platformContext->canvas()->endPlatformPaint(); +} - // Stroking on top (if necessary). - if ((textMode & WebCore::cTextStroke) - && platformContext->getStrokeStyle() != NoStroke - && platformContext->getStrokeThickness() > 0) { - - paint.reset(); - platformContext->setupPaintForStroking(&paint, 0, 0); - paint.setFlags(SkPaint::kAntiAlias_Flag); - if (didFill) { - // If there is a shadow and we filled above, there will already be - // a shadow. We don't want to draw it again or it will be too dark - // and it will go on top of the fill. - // - // Note that this isn't strictly correct, since the stroke could be - // very thick and the shadow wouldn't account for this. The "right" - // thing would be to draw to a new layer and then draw that layer - // with a shadow. But this is a lot of extra work for something - // that isn't normally an issue. - paint.setLooper(0)->safeUnref(); - } +// Specialization for simple GlyphBuffer painting. +class TransparencyAwareGlyphPainter : public TransparencyAwareFontPainter { + public: + TransparencyAwareGlyphPainter(GraphicsContext*, + const SimpleFontData*, + const GlyphBuffer&, + int from, int numGlyphs, + const FloatPoint&); + ~TransparencyAwareGlyphPainter(); + + // Draws the partial string of glyphs, starting at |startAdvance| to the + // left of m_point. We express it this way so that if we're using the Skia + // drawing path we can use floating-point positioning, even though we have + // to use integer positioning in the GDI path. + bool drawGlyphs(int numGlyphs, const WORD* glyphs, const int* advances, int startAdvance) const; + + private: + virtual IntRect estimateTextBounds(); + + const SimpleFontData* m_font; + const GlyphBuffer& m_glyphBuffer; + int m_from; + int m_numGlyphs; + + // When m_useGdi is set, this stores the previous HFONT selected into the + // m_hdc so we can restore it. + HGDIOBJ m_oldFont; // For restoring the DC to its original state. +}; + +TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter( + GraphicsContext* context, + const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, + const FloatPoint& point) + : TransparencyAwareFontPainter(context, point) + , m_font(font) + , m_glyphBuffer(glyphBuffer) + , m_from(from) + , m_numGlyphs(numGlyphs) + , m_oldFont(0) +{ + init(); + + m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); +} - if (!skiaDrawText(hfont, platformContext->canvas(), origin, &paint, &glyphs[0], &advances[0], numGlyphs)) - return false; +TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter() +{ + if (m_useGDI) + ::SelectObject(m_hdc, m_oldFont); +} + + +// Estimates the bounding box of the given text. This is copied from +// FontCGWin.cpp, it is possible, but a lot more work, to get the precide +// bounds. +IntRect TransparencyAwareGlyphPainter::estimateTextBounds() +{ + int totalWidth = 0; + for (int i = 0; i < m_numGlyphs; i++) + totalWidth += lroundf(m_glyphBuffer.advanceAt(m_from + i)); + + return IntRect(m_point.x() - (m_font->ascent() + m_font->descent()) / 2, + m_point.y() - m_font->ascent() - m_font->lineGap(), + totalWidth + m_font->ascent() + m_font->descent(), + m_font->lineSpacing()); +} + +bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, + const WORD* glyphs, + const int* advances, + int startAdvance) const +{ + if (!m_useGDI) { + SkPoint origin = m_point; + origin.fX += startAdvance; + return paintSkiaText(m_graphicsContext, m_font->platformData().hfont(), + numGlyphs, glyphs, advances, 0, &origin); } - return true; + + // Windows' origin is the top-left of the bounding box, so we have + // to subtract off the font ascent to get it. + int x = lroundf(m_point.x() + startAdvance); + int y = lroundf(m_point.y() - m_font->ascent()); + return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); +} + + +class TransparencyAwareUniscribePainter : public TransparencyAwareFontPainter { + public: + TransparencyAwareUniscribePainter(GraphicsContext*, + const Font*, + const TextRun&, + int from, int to, + const FloatPoint&); + ~TransparencyAwareUniscribePainter(); + + // Uniscibe will draw directly into our buffer, so we need to expose our DC. + HDC hdc() const { return m_hdc; } + + private: + virtual IntRect estimateTextBounds(); + + const Font* m_font; + const TextRun& m_run; + int m_from; + int m_to; +}; + +TransparencyAwareUniscribePainter::TransparencyAwareUniscribePainter( + GraphicsContext* context, + const Font* font, + const TextRun& run, + int from, int to, + const FloatPoint& point) + : TransparencyAwareFontPainter(context, point) + , m_font(font) + , m_run(run) + , m_from(from) + , m_to(to) +{ + init(); } +TransparencyAwareUniscribePainter::~TransparencyAwareUniscribePainter() +{ +} + +IntRect TransparencyAwareUniscribePainter::estimateTextBounds() +{ + // This case really really sucks. There is no convenient way to estimate + // the bounding box. So we run Uniscribe twice. If we find this happens a + // lot, the way to fix it is to make the extra layer after the + // UniscribeHelper has measured the text. + IntPoint intPoint(lroundf(m_point.x()), + lroundf(m_point.y())); + + UniscribeHelperTextRun state(m_run, *m_font); + int left = lroundf(m_point.x()) + state.characterToX(m_from); + int right = lroundf(m_point.x()) + state.characterToX(m_to); + + // This algorithm for estimating how much extra space we need (the text may + // go outside the selection rect) is based roughly on + // TransparencyAwareGlyphPainter::estimateTextBounds above. + return IntRect(left - (m_font->ascent() + m_font->descent()) / 2, + m_point.y() - m_font->ascent() - m_font->lineGap(), + (right - left) + m_font->ascent() + m_font->descent(), + m_font->lineSpacing()); +} + +} // namespace + void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, @@ -172,51 +324,27 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, int numGlyphs, const FloatPoint& point) const { - PlatformGraphicsContext* context = graphicsContext->platformContext(); - - // Max buffer length passed to the underlying windows API. - const int kMaxBufferLength = 1024; - // Default size for the buffer. It should be enough for most of cases. - const int kDefaultBufferLength = 256; - - SkColor color = context->fillColor(); + SkColor color = graphicsContext->platformContext()->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. - if (!alpha && context->getStrokeStyle() == NoStroke) + if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke) return; - // Set up our graphics context. - HDC hdc = context->canvas()->beginPlatformPaint(); - HGDIOBJ oldFont = SelectObject(hdc, font->platformData().hfont()); - - // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. - // Enforce non-transparent color. - color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); - SetTextColor(hdc, skia::SkColorToCOLORREF(color)); - SetBkMode(hdc, TRANSPARENT); - - // Windows needs the characters and the advances in nice contiguous - // buffers, which we build here. - Vector<WORD, kDefaultBufferLength> glyphs; - Vector<int, kDefaultBufferLength> advances; - - // Compute the coordinate. The 'origin' represents the baseline, so we need - // to move it up to the top of the bounding square. - int x = static_cast<int>(point.x()); - int lineTop = static_cast<int>(point.y()) - font->ascent(); - - bool canUseGDI = windowsCanHandleTextDrawing(graphicsContext); + TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point); // We draw the glyphs in chunks to avoid having to do a heap allocation for // the arrays of characters and advances. Since ExtTextOut is the // lowest-level text output function on Windows, there should be little // penalty for splitting up the text. On the other hand, the buffer cannot // be bigger than 4094 or the function will fail. - int glyphIndex = 0; + const int kMaxBufferLength = 256; + Vector<WORD, kMaxBufferLength> glyphs; + Vector<int, kMaxBufferLength> advances; + int glyphIndex = 0; // The starting glyph of the current chunk. + int curAdvance = 0; // How far from the left the current chunk is. while (glyphIndex < numGlyphs) { - // how many chars will be in this chunk? + // How many chars will be in this chunk? int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); - glyphs.resize(curLen); advances.resize(curLen); @@ -227,17 +355,10 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, curWidth += advances[i]; } + // Actually draw the glyphs (with retry on failure). bool success = false; for (int executions = 0; executions < 2; ++executions) { - if (canUseGDI) - success = !!ExtTextOut(hdc, x, lineTop, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), curLen, &advances[0]); - else { - // Skia's text draing origin is the baseline, like WebKit, not - // the top, like Windows. - SkPoint origin = { x, point.y() }; - success = paintSkiaText(context, font->platformData().hfont(), numGlyphs, reinterpret_cast<const WORD*>(&glyphs[0]), &advances[0], origin); - } - + success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], curAdvance); if (!success && executions == 0) { // Ask the browser to load the font for us and retry. ChromiumBridge::ensureFontLoaded(font->platformData().hfont()); @@ -247,12 +368,8 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, } ASSERT(success); - - x += curWidth; + curAdvance += curWidth; } - - SelectObject(hdc, oldFont); - context->canvas()->endPlatformPaint(); } FloatRect Font::selectionRectForComplexText(const TextRun& run, @@ -283,13 +400,15 @@ void Font::drawComplexText(GraphicsContext* graphicsContext, PlatformGraphicsContext* context = graphicsContext->platformContext(); UniscribeHelperTextRun state(run, *this); - SkColor color = context->fillColor(); + SkColor color = graphicsContext->platformContext()->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. - if (!alpha) + if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke) return; - HDC hdc = context->canvas()->beginPlatformPaint(); + TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point); + + HDC hdc = painter.hdc(); // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. // Enforce non-transparent color. @@ -299,7 +418,9 @@ void Font::drawComplexText(GraphicsContext* graphicsContext, // Uniscribe counts the coordinates from the upper left, while WebKit uses // the baseline, so we have to subtract off the ascent. - state.draw(hdc, static_cast<int>(point.x()), static_cast<int>(point.y() - ascent()), from, to); + state.draw(graphicsContext, hdc, static_cast<int>(point.x()), + static_cast<int>(point.y() - ascent()), from, to); + context->canvas()->endPlatformPaint(); } diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp index 8f8df88..1e923ac 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp @@ -116,7 +116,7 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b // Streams the concatenation of a header and font data. class EOTStream { public: - EOTStream(const Vector<UInt8, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) + EOTStream(const Vector<uint8_t, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) : m_eotHeader(eotHeader) , m_fontData(fontData) , m_overlayDst(overlayDst) @@ -130,7 +130,7 @@ public: size_t read(void* buffer, size_t count); private: - const Vector<UInt8, 512>& m_eotHeader; + const Vector<uint8_t, 512>& m_eotHeader; const SharedBuffer* m_fontData; size_t m_overlayDst; size_t m_overlaySrc; @@ -200,7 +200,7 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) // TTLoadEmbeddedFont works only with Embedded OpenType (.eot) data, // so we need to create an EOT header and prepend it to the font data. - Vector<UInt8, 512> eotHeader; + Vector<uint8_t, 512> eotHeader; size_t overlayDst; size_t overlaySrc; size_t overlayLength; diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp index 7a3e614..2b7c562 100644 --- a/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -49,14 +49,6 @@ namespace WebCore { void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { - SkCanvas* canvas = gc->platformContext()->canvas(); - SkPaint paint; - - gc->platformContext()->setupPaintCommon(&paint); - font->platformData().setupPaint(&paint); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setColor(gc->fillColor().rgb()); - SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); @@ -78,7 +70,39 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, x += SkFloatToScalar(adv[i].width()); y += SkFloatToScalar(adv[i].height()); } - canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + + SkCanvas* canvas = gc->platformContext()->canvas(); + int textMode = gc->platformContext()->getTextDrawingMode(); + + // We draw text up to two times (once for fill, once for stroke). + if (textMode & cTextFill) { + SkPaint paint; + gc->platformContext()->setupPaintForFilling(&paint); + font->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->fillColor().rgb()); + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + } + + if ((textMode & cTextStroke) + && gc->platformContext()->getStrokeStyle() != NoStroke + && gc->platformContext()->getStrokeThickness() > 0) { + + SkPaint paint; + gc->platformContext()->setupPaintForStroking(&paint, 0, 0); + font->platformData().setupPaint(&paint); + paint.setFlags(SkPaint::kAntiAlias_Flag); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->strokeColor().rgb()); + + if (textMode & cTextFill) { + // If we also filled, we don't want to draw shadows twice. + // See comment in FontChromiumWin.cpp::paintSkiaText() for more details. + paint.setLooper(0)->safeUnref(); + } + + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + } } void Font::drawComplexText(GraphicsContext* context, const TextRun& run, diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp index 86f96ee..7b7d197 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp @@ -86,7 +86,7 @@ void FontPlatformData::setupPaint(SkPaint* paint) const { const float ts = m_textSize > 0 ? m_textSize : 12; - paint->setAntiAlias(false); + paint->setAntiAlias(true); paint->setSubpixelText(false); paint->setTextSize(SkFloatToScalar(ts)); paint->setTypeface(m_typeface); diff --git a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp index 4c5cf7b..31c5256 100644 --- a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp @@ -147,27 +147,17 @@ static bool fillBMPGlyphs(unsigned offset, // When this character should be a space, we ignore whatever the font // says and use a space. Otherwise, if fonts don't map one of these // space or zero width glyphs, we will get a box. - if (Font::treatAsSpace(c)) + if (Font::treatAsSpace(c)) { // Hard code the glyph indices for characters that should be // treated like spaces. glyph = initSpaceGlyph(dc, &spaceGlyph); - else if (Font::treatAsZeroWidthSpace(c) || c == 0x200B) { - // FIXME: change Font::treatAsZeroWidthSpace to use - // u_hasBinaryProperty, per jungshik's comment here: - // https://bugs.webkit.org/show_bug.cgi?id=20237#c6. - // Then the additional OR above won't be necessary. - glyph = initSpaceGlyph(dc, &spaceGlyph); - glyphFontData = fontData->zeroWidthFontData(); } else if (glyph == invalidGlyph) { // WebKit expects both the glyph index and FontData // pointer to be 0 if the glyph is not present glyph = 0; glyphFontData = 0; - } else { - if (SimpleFontData::isCJKCodePoint(c)) - glyphFontData = fontData->cjkWidthFontData(); + } else haveGlyphs = true; - } page->setGlyphDataForCharacter(offset + i, glyph, glyphFontData); } @@ -205,6 +195,7 @@ static bool fillNonBMPGlyphs(unsigned offset, fontData->m_font.scriptCache(), fontData->m_font.scriptFontProperties()); state.setInhibitLigate(true); + state.setDisableFontFallback(true); state.init(); for (unsigned i = 0; i < length; i++) { diff --git a/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h b/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h index 959147a..e8ba0ad 100644 --- a/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h +++ b/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h @@ -33,13 +33,13 @@ #if ENABLE(VIDEO) -#include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" namespace WebCore { -class MediaPlayerPrivate : public Noncopyable { +class MediaPlayerPrivate : public MediaPlayerPrivateInterface { public: - MediaPlayerPrivate(MediaPlayer*); + static void registerMediaEngine(MediaEngineRegistrar); ~MediaPlayerPrivate(); IntSize naturalSize() const; @@ -74,13 +74,10 @@ public: unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect&); + void setSize(const IntSize&); void paint(GraphicsContext*, const IntRect&); - static void getSupportedTypes(HashSet<String>&); - static bool isAvailable(); - // Public methods to be called by WebMediaPlayer FrameView* frameView(); void networkStateChanged(); @@ -90,6 +87,12 @@ public: void repaint(); private: + MediaPlayerPrivate(MediaPlayer*); + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + MediaPlayer* m_player; void* m_data; }; diff --git a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp deleted file mode 100644 index 798ee32..0000000 --- a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "ThemeHelperChromiumWin.h" - -#include "FloatRect.h" -#include "GraphicsContext.h" - -namespace WebCore { - -ThemeHelperWin::ThemeHelperWin(GraphicsContext* context, const IntRect& rect) - : m_orgContext(context) - , m_orgMatrix(context->getCTM()) - , m_orgRect(rect) -{ - if (m_orgMatrix.b() != 0 || m_orgMatrix.c() != 0) { // Check for skew. - // Complicated effects, make a copy and draw the bitmap there. - m_type = COPY; - m_rect.setSize(rect.size()); - - m_newBuffer.set(ImageBuffer::create(rect.size(), false).release()); - - // Theme drawing messes with the transparency. - // FIXME: Ideally, we would leave this transparent, but I was - // having problems with button drawing, so we fill with white. Buttons - // looked good with transparent here and no fixing up of the alpha - // later, but text areas didn't. This makes text areas look good but - // gives buttons a white halo. Is there a way to fix this? I think - // buttons actually have antialised edges which is just not possible - // to handle on a transparent background given that it messes with the - // alpha channel. - FloatRect newContextRect(0, 0, rect.width(), rect.height()); - GraphicsContext* newContext = m_newBuffer->context(); - newContext->setFillColor(Color::white); - newContext->fillRect(newContextRect); - - return; - } - - if (m_orgMatrix.a() != 1.0 || m_orgMatrix.d() != 1.0) { // Check for scale. - // Only a scaling is applied. - m_type = SCALE; - - // Save the transformed coordinates to draw. - m_rect = m_orgMatrix.mapRect(rect); - - m_orgContext->save(); - m_orgContext->concatCTM(m_orgContext->getCTM().inverse()); - return; - } - - // Nothing interesting. - m_rect = rect; - m_type = ORIGINAL; -} - -ThemeHelperWin::~ThemeHelperWin() -{ - switch (m_type) { - case SCALE: - m_orgContext->restore(); - break; - case COPY: { - // Copy the duplicate bitmap with our control to the original canvas. - FloatRect destRect(m_orgRect); - m_newBuffer->context()->platformContext()->canvas()-> - getTopPlatformDevice().fixupAlphaBeforeCompositing(); - m_orgContext->drawImage(m_newBuffer->image(), destRect); - break; - } - case ORIGINAL: - break; - } -} - -} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h deleted file mode 100644 index 1771fb4..0000000 --- a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ThemeHelperWin_h -#define ThemeHelperWin_h - -#include "TransformationMatrix.h" -#include "ImageBuffer.h" -#include "IntRect.h" -#include <wtf/OwnPtr.h> - -namespace WebCore { - -class GraphicsContext; -class IntRect; - -// Helps drawing theme elements like buttons and scroll bars. This will handle -// translations and scalings that Windows might not, by either making Windows -// draw the appropriate sized control, or by rendering it into an off-screen -// context and transforming it ourselves. -class ThemeHelperWin { - enum Type { - // Use the original canvas with no changes. This is the normal mode. - ORIGINAL, - - // Use the original canvas but scale the rectangle of the control so - // that it will be the correct size, undoing any scale already on the - // canvas. This will have the effect of just drawing the control bigger - // or smaller and not actually expanding or contracting the pixels in - // it. This usually looks better. - SCALE, - - // Make a copy of the control and then transform it ourselves after - // Windows draws it. This allows us to get complex effects. - COPY, - }; - -public: - // Prepares drawing a control with the given rect to the given context. - ThemeHelperWin(GraphicsContext* context, const IntRect& rect); - ~ThemeHelperWin(); - - // Returns the context to draw the control into, which may be the original - // or the copy, depending on the mode. - GraphicsContext* context() - { - return m_newBuffer.get() ? m_newBuffer->context() : m_orgContext; - } - - // Returns the rectangle in which to draw into the canvas() by Windows. - const IntRect& rect() { return m_rect; } - -private: - Type m_type; - - // The original canvas to wrote to. Not owned by this class. - GraphicsContext* m_orgContext; - TransformationMatrix m_orgMatrix; - IntRect m_orgRect; - - // When m_type == COPY, this will be a new surface owned by this class that - // represents the copy. - OwnPtr<ImageBuffer> m_newBuffer; - - // The control rectangle in the coordinate space of canvas(). - IntRect m_rect; -}; - -} // namespace WebCore - -#endif // ThemeHelperWin_h diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/WebCore/platform/graphics/chromium/TransparencyWin.cpp new file mode 100644 index 0000000..8c790af --- /dev/null +++ b/WebCore/platform/graphics/chromium/TransparencyWin.cpp @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include <windows.h> + +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "PlatformContextSkia.h" +#include "SimpleFontData.h" +#include "TransformationMatrix.h" +#include "TransparencyWin.h" + +#include "SkColorPriv.h" +#include "skia/ext/platform_canvas.h" + +namespace WebCore { + +namespace { + +// The maximum size in pixels of the buffer we'll keep around for drawing text +// into. Buffers larger than this will be destroyed when we're done with them. +const int maxCachedBufferPixelSize = 65536; + +inline skia::PlatformCanvas* canvasForContext(const GraphicsContext& context) +{ + return context.platformContext()->canvas(); +} + +inline const SkBitmap& bitmapForContext(const GraphicsContext& context) +{ + return canvasForContext(context)->getTopPlatformDevice().accessBitmap(false); +} + +void compositeToCopy(const GraphicsContext& sourceLayers, + GraphicsContext& destContext, + const TransformationMatrix& matrix) +{ + // Make a list of all devices. The iterator goes top-down, and we want + // bottom-up. Note that each layer can also have an offset in canvas + // coordinates, which is the (x, y) position. + struct DeviceInfo { + DeviceInfo(SkDevice* d, int lx, int ly) + : device(d) + , x(lx) + , y(ly) {} + SkDevice* device; + int x; + int y; + }; + Vector<DeviceInfo> devices; + SkCanvas* sourceCanvas = canvasForContext(sourceLayers); + SkCanvas::LayerIter iter(sourceCanvas, false); + while (!iter.done()) { + devices.append(DeviceInfo(iter.device(), iter.x(), iter.y())); + iter.next(); + } + + // Create a temporary canvas for the compositing into the destination. + SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext)); + SkCanvas destCanvas(*destBmp); + destCanvas.setMatrix(matrix); + + for (int i = devices.size() - 1; i >= 0; i--) { + const SkBitmap& srcBmp = devices[i].device->accessBitmap(false); + + SkRect destRect; + destRect.fLeft = devices[i].x; + destRect.fTop = devices[i].y; + destRect.fRight = destRect.fLeft + srcBmp.width(); + destRect.fBottom = destRect.fTop + srcBmp.height(); + + destCanvas.drawBitmapRect(srcBmp, 0, destRect); + } +} + +} // namespace + +// If either of these pointers is non-null, both must be valid and point to +// bitmaps of the same size. +class TransparencyWin::OwnedBuffers { +public: + OwnedBuffers(const IntSize& size, bool needReferenceBuffer) + { + m_destBitmap.adopt(ImageBuffer::create(size, false)); + + if (needReferenceBuffer) { + m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); + m_referenceBitmap.allocPixels(); + m_referenceBitmap.eraseARGB(0, 0, 0, 0); + } + } + + ImageBuffer* destBitmap() { return m_destBitmap.get(); } + + // This bitmap will be empty if you don't specify needReferenceBuffer to the + // constructor. + SkBitmap* referenceBitmap() { return &m_referenceBitmap; } + + // Returns whether the current layer will fix a buffer of the given size. + bool canHandleSize(const IntSize& size) const + { + return m_destBitmap->size().width() >= size.width() && m_destBitmap->size().height() >= size.height(); + } + +private: + // The destination bitmap we're drawing into. + OwnPtr<ImageBuffer> m_destBitmap; + + // This could be an ImageBuffer but this is an optimization. Since this is + // only ever used as a reference, we don't need to make a full + // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap + // is much faster since it's just a Malloc rather than a GDI call. + SkBitmap m_referenceBitmap; +}; + +TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0; + +TransparencyWin::TransparencyWin() + : m_destContext(0) + , m_orgTransform() + , m_layerMode(NoLayer) + , m_transformMode(KeepTransform) + , m_drawContext(0) + , m_savedOnDrawContext(false) + , m_layerBuffer(0) + , m_referenceBitmap(0) +{ +} + +TransparencyWin::~TransparencyWin() +{ + // This should be false, since calling composite() is mandatory. + ASSERT(!m_savedOnDrawContext); +} + +void TransparencyWin::composite() +{ + // Matches the save() in initializeNewTextContext (or the constructor for + // SCALE) to put the context back into the same state we found it. + if (m_savedOnDrawContext) { + m_drawContext->restore(); + m_savedOnDrawContext = false; + } + + switch (m_layerMode) { + case NoLayer: + break; + case OpaqueCompositeLayer: + case WhiteLayer: + compositeOpaqueComposite(); + break; + case TextComposite: + compositeTextComposite(); + break; + } +} + +void TransparencyWin::init(GraphicsContext* dest, + LayerMode layerMode, + TransformMode transformMode, + const IntRect& region) +{ + m_destContext = dest; + m_orgTransform = dest->getCTM(); + m_layerMode = layerMode; + m_transformMode = transformMode; + m_sourceRect = region; + + computeLayerSize(); + setupLayer(); + setupTransform(region); +} + +void TransparencyWin::computeLayerSize() +{ + if (m_transformMode == Untransform) { + // The meaning of the "transformed" source rect is a little ambigous + // here. The rest of the code doesn't care about it in the Untransform + // case since we're using our own happy coordinate system. So we set it + // to be the source rect since that matches how the code below actually + // uses the variable: to determine how to translate things to account + // for the offset of the layer. + m_transformedSourceRect = m_sourceRect; + m_layerSize = IntSize(m_sourceRect.width(), m_sourceRect.height()); + } else { + m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect); + m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height()); + } +} + +void TransparencyWin::setupLayer() +{ + switch (m_layerMode) { + case NoLayer: + setupLayerForNoLayer(); + break; + case OpaqueCompositeLayer: + setupLayerForOpaqueCompositeLayer(); + break; + case TextComposite: + setupLayerForTextComposite(); + break; + case WhiteLayer: + setupLayerForWhiteLayer(); + break; + } +} + +void TransparencyWin::setupLayerForNoLayer() +{ + m_drawContext = m_destContext; // Draw to the source context. +} + +void TransparencyWin::setupLayerForOpaqueCompositeLayer() +{ + initializeNewContext(); + + TransformationMatrix mapping; + mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); + if (m_transformMode == Untransform){ + // Compute the inverse mapping from the canvas space to the + // coordinate space of our bitmap. + mapping = m_orgTransform.inverse() * mapping; + } + compositeToCopy(*m_destContext, *m_drawContext, mapping); + + // Save the reference layer so we can tell what changed. + SkCanvas referenceCanvas(*m_referenceBitmap); + referenceCanvas.drawBitmap(bitmapForContext(*m_drawContext), 0, 0); + // Layer rect represents the part of the original layer. +} + +void TransparencyWin::setupLayerForTextComposite() +{ + ASSERT(m_transformMode == KeepTransform); + // Fall through to filling with white. + setupLayerForWhiteLayer(); +} + +void TransparencyWin::setupLayerForWhiteLayer() +{ + initializeNewContext(); + m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white); + // Layer rect represents the part of the original layer. +} + +void TransparencyWin::setupTransform(const IntRect& region) +{ + switch (m_transformMode) { + case KeepTransform: + setupTransformForKeepTransform(region); + break; + case Untransform: + setupTransformForUntransform(); + break; + case ScaleTransform: + setupTransformForScaleTransform(); + break; + } +} + +void TransparencyWin::setupTransformForKeepTransform(const IntRect& region) +{ + if (m_layerMode != NoLayer) { + // Need to save things since we're modifying the transform. + m_drawContext->save(); + m_savedOnDrawContext = true; + + // Account for the fact that the layer may be offset from the + // original. This only happens when we create a layer that has the + // same coordinate space as the parent. + TransformationMatrix xform; + xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); + + // We're making a layer, so apply the old transform to the new one + // so it's maintained. We know the new layer has the identity + // transform now, we we can just multiply it. + xform = m_orgTransform * xform; + m_drawContext->concatCTM(xform); + } + m_drawRect = m_sourceRect; +} + +void TransparencyWin::setupTransformForUntransform() +{ + ASSERT(m_layerMode != NoLayer); + // We now have a new layer with the identity transform, which is the + // Untransformed space we'll use for drawing. + m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); +} + +void TransparencyWin::setupTransformForScaleTransform() +{ + if (m_layerMode == NoLayer) { + // Need to save things since we're modifying the layer. + m_drawContext->save(); + m_savedOnDrawContext = true; + + // Undo the transform on the current layer when we're re-using the + // current one. + m_drawContext->concatCTM(m_drawContext->getCTM().inverse()); + + // We're drawing to the original layer with just a different size. + m_drawRect = m_transformedSourceRect; + } else { + // Just go ahead and use the layer's coordinate space to draw into. + // It will have the scaled size, and an identity transform loaded. + m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); + } +} + +void TransparencyWin::setTextCompositeColor(Color color) +{ + m_textCompositeColor = color; +} + +void TransparencyWin::initializeNewContext() +{ + int pixelSize = m_layerSize.width() * m_layerSize.height(); + if (pixelSize > maxCachedBufferPixelSize) { + // Create a 1-off buffer for drawing into. We only need the reference + // buffer if we're making an OpaqueCompositeLayer. + bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer; + m_ownedBuffers.set(new OwnedBuffers(m_layerSize, needReferenceBitmap)); + + m_layerBuffer = m_ownedBuffers->destBitmap(); + m_drawContext = m_layerBuffer->context(); + if (needReferenceBitmap) + m_referenceBitmap = m_ownedBuffers->referenceBitmap(); + return; + } + + if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) { + // We can re-use the existing buffer. We don't need to clear it since + // all layer modes will clear it in their initialization. + m_layerBuffer = m_cachedBuffers->destBitmap(); + m_drawContext = m_cachedBuffers->destBitmap()->context(); + bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0); + m_referenceBitmap = m_cachedBuffers->referenceBitmap(); + m_referenceBitmap->eraseARGB(0, 0, 0, 0); + return; + } + + // Create a new cached buffer. + if (m_cachedBuffers) + delete m_cachedBuffers; + m_cachedBuffers = new OwnedBuffers(m_layerSize, true); + + m_layerBuffer = m_cachedBuffers->destBitmap(); + m_drawContext = m_cachedBuffers->destBitmap()->context(); + m_referenceBitmap = m_cachedBuffers->referenceBitmap(); +} + +void TransparencyWin::compositeOpaqueComposite() +{ + SkCanvas* destCanvas = canvasForContext(*m_destContext); + destCanvas->save(); + + SkBitmap* bitmap = const_cast<SkBitmap*>( + &bitmapForContext(*m_layerBuffer->context())); + + // This function will be called for WhiteLayer as well, which we don't want + // to change. + if (m_layerMode == OpaqueCompositeLayer) { + // Fix up our bitmap, making it contain only the pixels which changed + // and transparent everywhere else. + SkAutoLockPixels sourceLock(*m_referenceBitmap); + SkAutoLockPixels lock(*bitmap); + for (int y = 0; y < bitmap->height(); y++) { + uint32_t* source = m_referenceBitmap->getAddr32(0, y); + uint32_t* dest = bitmap->getAddr32(0, y); + for (int x = 0; x < bitmap->width(); x++) { + // Clear out any pixels that were untouched. + if (dest[x] == source[x]) + dest[x] = 0; + else + dest[x] |= (0xFF << SK_A32_SHIFT); + } + } + } else + makeLayerOpaque(); + + SkRect destRect; + if (m_transformMode != Untransform) { + // We want to use Untransformed space. + // + // Note that we DON'T call m_layerBuffer->image() here. This actually + // makes a copy of the image, which is unnecessary and slow. Instead, we + // just draw the image from inside the destination context. + SkMatrix identity; + identity.reset(); + destCanvas->setMatrix(identity); + + destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom()); + } else + destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.right(), m_sourceRect.bottom()); + + SkPaint paint; + paint.setFilterBitmap(true); + paint.setAntiAlias(true); + + // Note that we need to specify the source layer subset, since the bitmap + // may have been cached and it could be larger than what we're using. + SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; + destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint); + destCanvas->restore(); +} + +void TransparencyWin::compositeTextComposite() +{ + const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopPlatformDevice().accessBitmap(true); + SkColor textColor = m_textCompositeColor.rgb(); + for (int y = 0; y < m_layerSize.height(); y++) { + uint32_t* row = bitmap.getAddr32(0, y); + for (int x = 0; x < m_layerSize.width(); x++) { + // The alpha is the average of the R, G, and B channels. + int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3; + + // Apply that alpha to the text color and write the result. + row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha)); + } + } + + // Now the layer has text with the proper color and opacity. + SkCanvas* destCanvas = canvasForContext(*m_destContext); + + // We want to use Untransformed space (see above) + SkMatrix identity; + identity.reset(); + destCanvas->setMatrix(identity); + SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom() }; + + // Note that we need to specify the source layer subset, since the bitmap + // may have been cached and it could be larger than what we're using. + SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; + destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, 0); + destCanvas->restore(); +} + +void TransparencyWin::makeLayerOpaque() +{ + SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()-> + canvas()->getTopPlatformDevice().accessBitmap(true)); + for (int y = 0; y < m_layerSize.height(); y++) { + uint32_t* row = bitmap.getAddr32(0, y); + for (int x = 0; x < m_layerSize.width(); x++) + row[x] |= 0xFF000000; + } +} + +} // namespace WebCore + diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.h b/WebCore/platform/graphics/chromium/TransparencyWin.h new file mode 100644 index 0000000..e1963b3 --- /dev/null +++ b/WebCore/platform/graphics/chromium/TransparencyWin.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TransparencyWin_h +#define TransparencyWin_h + +#include <windows.h> + +#include "ImageBuffer.h" +#include "Noncopyable.h" +#include "TransformationMatrix.h" +#include "wtf/OwnPtr.h" + +class SkBitmap; +class SkCanvas; + +namespace WebCore { + +class GraphicsContext; +class TransparencyWin_NoLayer_Test; +class TransparencyWin_WhiteLayer_Test; +class TransparencyWin_TextComposite_Test; +class TransparencyWin_OpaqueCompositeLayer_Test; + +// Helper class that abstracts away drawing ClearType text and Windows form +// controls either to the original context directly, or to an offscreen context +// that is composited later manually. This is to get around Windows' inability +// to handle the alpha channel, semitransparent text, and transformed form +// controls. +class TransparencyWin : public Noncopyable { +public: + enum LayerMode { + // No extra layer is created. Drawing will happen to the source. + // Valid only with KeepTransform and ScaleTransform. The region being + // drawn onto must be opaque, since the modified region will be forced + // to opaque when drawing is complete. + NoLayer, + + // Makes a temporary layer consisting of the composited layers below + // it. This result must be opaque. When complete, the result will be + // compared to the original, and the difference will be added to a thee + // destination layer. + // + // This mode only works if the lower layers are opque (normally the + // case for a web page) and layers are only drawn in the stack order, + // meaning you can never draw underneath a layer. + // + // This doesn't technically produce the correct answer in all cases. If + // you have an opaque base, a transparency layer, than a semitransparent + // drawing on top, the result will actually be blended in twice. But + // this isn't a very important case. This mode is used for form + // controls which are always opaque except for occationally some + // antialiasing. It means form control antialiasing will be too light in + // some cases, but only if you have extra layers. + OpaqueCompositeLayer, + + // Allows semitransparent text to be drawn on any background (even if it + // is itself semitransparent), but disables ClearType. + // + // It makes a trmporary layer filled with white. This is composited with + // the lower layer with a custom color applied to produce the result. + // The caller must draw the text in black, and set the desired final + // text color by calling setTextCompositeColor(). + // + // Only valid with KeepTransform, which is the only mode where drawing + // text in this fashion makes sense. + TextComposite, + + // Makes a temporary layer filled with white. When complete, the layer + // will be forced to be opqaue (since Windows may have messed up the + // alpha channel) and composited down. Any areas not drawn into will + // remain white. + // + // This is the mode of last resort. If the opacity of the final image + // is unknown and we can't do the text trick (since we know its color), + // then we have to live with potential white halos. This is used for + // form control drawing, for example. + WhiteLayer, + }; + + enum TransformMode { + // There are no changes to the transform. Use this when drawing + // horizontal text. The current transform must not have rotation. + KeepTransform, + + // Drawing happens in an Untransformed space, and then that bitmap is + // transformed according to the current context when it is copied down. + // Requires that a layer be created (layer mode is not NoLayer). + Untransform, + + // When the current transform only has a scaling factor applied and + // you're drawing form elements, use this parameter. This will unscale + // the coordinate space, so the OS will just draw the form controls + // larger or smaller depending on the destination size. + ScaleTransform, + }; + + // You MUST call init() below. + // |region| is expressed relative to the current transformation. + TransparencyWin(); + ~TransparencyWin(); + + // Initializes the members if you use the 0-argument constructor. Don't call + // this if you use the multiple-argument constructor. + void init(GraphicsContext* dest, + LayerMode layerMode, + TransformMode transformMode, + const IntRect& region); + + // Combines the source and destination bitmaps using the given mode. + void composite(); + + // Returns the context for drawing into, which may be the destination + // context, or a temporary one. + GraphicsContext* context() const { return m_drawContext; } + + PlatformGraphicsContext* platformContext() const { return m_drawContext->platformContext(); } + + // When the mode is TextComposite, this sets the color that the text will + // get. See the enum above for more. + void setTextCompositeColor(Color color); + + // Returns the input bounds translated into the destination space. This is + // not necessary for KeepTransform since the rectangle will be unchanged. + const IntRect& drawRect() { return m_drawRect; } + +private: + friend TransparencyWin_NoLayer_Test; + friend TransparencyWin_WhiteLayer_Test; + friend TransparencyWin_TextComposite_Test; + friend TransparencyWin_OpaqueCompositeLayer_Test; + + class OwnedBuffers; + + void computeLayerSize(); + + // Sets up a new layer, if any. setupLayer() will call the appopriate layer- + // specific helper. Must be called after computeLayerSize(); + void setupLayer(); + void setupLayerForNoLayer(); + void setupLayerForOpaqueCompositeLayer(); + void setupLayerForTextComposite(); + void setupLayerForWhiteLayer(); + + // Sets up the transformation on the newly created layer. setupTransform() + // will call the appropriate transform-specific helper. Must be called after + // setupLayer(). + void setupTransform(const IntRect& region); + void setupTransformForKeepTransform(const IntRect& region); + void setupTransformForUntransform(); + void setupTransformForScaleTransform(); + + void initializeNewContext(); + + void compositeOpaqueComposite(); + void compositeTextComposite(); + + // Fixes the alpha channel to make the region inside m_transformedRect + // opaque. + void makeLayerOpaque(); + + // The context our drawing will eventually end up in. + GraphicsContext* m_destContext; + + // The original transform from the destination context. + TransformationMatrix m_orgTransform; + + LayerMode m_layerMode; + TransformMode m_transformMode; + + // The rectangle we're drawing in the destination's coordinate space + IntRect m_sourceRect; + + // The source rectangle transformed into pixels in the final image. For + // Untransform this has no meaning, since the destination might not be a + // rectangle. + IntRect m_transformedSourceRect; + + // The size of the layer we created. If there's no layer, this is the size + // of the region we're using in the source. + IntSize m_layerSize; + + // The rectangle we're drawing to in the draw context's coordinate space. + // This will be the same as the source rectangle except for ScaleTransform + // where we create a new virtual coordinate space for the layer. + IntRect m_drawRect; + + // Points to the graphics context to draw text to, which will either be + // the original context or the copy, depending on our mode. + GraphicsContext* m_drawContext; + + // This flag is set when we call save() on the draw context during + // initialization. It allows us to avoid doing an extra save()/restore() + // when one is unnecessary. + bool m_savedOnDrawContext; + + // Used only when m_mode = TextComposite, this is the color that the text + // will end up being once we figure out the transparency. + Color m_textCompositeColor; + + // Layer we're drawing to. + ImageBuffer* m_layerBuffer; + + // When the layer type is OpaqueCompositeLayer, this will contain a copy + // of the original contents of the m_layerBuffer before Windows drew on it. + // It allows us to re-create what Windows did to the layer. It is an + // SkBitmap instead of an ImageBuffer because an SkBitmap is lighter-weight + // (ImageBuffers are also GDI surfaces, which we don't need here). + SkBitmap* m_referenceBitmap; + + // If the given size of bitmap can be cached, they will be stored here. Both + // the bitmap and the reference are guaranteed to be allocated if this + // member is non-null. + static OwnedBuffers* m_cachedBuffers; + + // If a buffer was too big to be cached, it will be created temporarily, and + // this member tracks its scope to make sure it gets deleted. Always use + // m_layerBuffer, which will either point to this object, or the statically + // cached one. Don't access directly. + OwnPtr<OwnedBuffers> m_ownedBuffers; +}; + +} // namespace WebCore + +#endif // TransaprencyWin_h diff --git a/WebCore/platform/graphics/chromium/UniscribeHelper.cpp b/WebCore/platform/graphics/chromium/UniscribeHelper.cpp index caeb959..39b0847 100644 --- a/WebCore/platform/graphics/chromium/UniscribeHelper.cpp +++ b/WebCore/platform/graphics/chromium/UniscribeHelper.cpp @@ -34,6 +34,9 @@ #include <windows.h> #include "FontUtilsChromiumWin.h" +#include "PlatformContextSkia.h" +#include "SkiaFontWin.h" +#include "SkPoint.h" #include <wtf/Assertions.h> namespace WebCore { @@ -58,7 +61,7 @@ static bool containsMissingGlyphs(WORD *glyphs, SCRIPT_FONTPROPERTIES* properties) { for (int i = 0; i < length; ++i) { - if (glyphs[i] == properties->wgDefault + if (glyphs[i] == properties->wgDefault || (glyphs[i] == properties->wgInvalid && glyphs[i] != properties->wgBlank)) return true; @@ -112,6 +115,8 @@ UniscribeHelper::UniscribeHelper(const UChar* input, , m_spaceWidth(0) , m_wordSpacing(0) , m_ascent(0) + , m_disableFontFallback(false) + { m_logfont.lfFaceName[0] = 0; } @@ -285,11 +290,13 @@ int UniscribeHelper::xToCharacter(int x) const return 0; } -void UniscribeHelper::draw(HDC dc, int x, int y, int from, int to) +void UniscribeHelper::draw(GraphicsContext* graphicsContext, + HDC dc, int x, int y, int from, int to) { HGDIOBJ oldFont = 0; int curX = x; bool firstRun = true; + bool useWindowsDrawing = windowsCanHandleTextDrawing(graphicsContext); for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { int itemIndex = m_screenOrder[screenIndex]; @@ -360,7 +367,7 @@ void UniscribeHelper::draw(HDC dc, int x, int y, int from, int to) // Actually draw the glyphs we found. int glyphCount = afterGlyph - fromGlyph; if (fromGlyph >= 0 && glyphCount > 0) { - // Account for the preceeding space we need to add to this run. We + // Account for the preceding space we need to add to this run. We // don't need to count for the following space because that will be // counted in advanceForItem below when we move to the next run. innerOffset += shaping.m_prePadding; @@ -377,30 +384,44 @@ void UniscribeHelper::draw(HDC dc, int x, int y, int from, int to) // Fonts with different ascents can be used to render different // runs. 'Across-runs' y-coordinate correction needs to be // adjusted for each font. - HRESULT hr = S_FALSE; + bool textOutOk = false; for (int executions = 0; executions < 2; ++executions) { - hr = ScriptTextOut(dc, shaping.m_scriptCache, - curX + innerOffset, - y - shaping.m_ascentOffset, - 0, 0, &item.a, 0, 0, - &shaping.m_glyphs[fromGlyph], - glyphCount, - &shaping.m_advance[fromGlyph], - justify, - &shaping.m_offsets[fromGlyph]); - if (S_OK != hr && 0 == executions) { - // If this ScriptTextOut is called from the renderer it - // might fail because the sandbox is preventing it from - // opening the font files. If we are running in the - // renderer, TryToPreloadFont is overridden to ask the - // browser to preload the font for us so we can access it. + if (useWindowsDrawing) { + HRESULT hr = ScriptTextOut(dc, shaping.m_scriptCache, + curX + innerOffset, + y - shaping.m_ascentOffset, + 0, 0, &item.a, 0, 0, + &shaping.m_glyphs[fromGlyph], + glyphCount, + &shaping.m_advance[fromGlyph], + justify, + &shaping.m_offsets[fromGlyph]); + ASSERT(S_OK == hr); + textOutOk = (hr == S_OK); + } else { + SkPoint origin; + origin.fX = curX + + innerOffset; + origin.fY = y + m_ascent - shaping.m_ascentOffset; + textOutOk = paintSkiaText(graphicsContext, + shaping.m_hfont, + glyphCount, + &shaping.m_glyphs[fromGlyph], + &shaping.m_advance[fromGlyph], + &shaping.m_offsets[fromGlyph], + &origin); + } + + if (!textOutOk && 0 == executions) { + // If TextOut is called from the renderer it might fail + // because the sandbox is preventing it from opening the + // font files. If we are running in the renderer, + // TryToPreloadFont is overridden to ask the browser to + // preload the font for us so we can access it. tryToPreloadFont(shaping.m_hfont); continue; } break; } - - ASSERT(S_OK == hr); } curX += advanceForItem(itemIndex); @@ -527,7 +548,10 @@ bool UniscribeHelper::shape(const UChar* input, HDC tempDC = 0; HGDIOBJ oldFont = 0; HRESULT hr; - bool lastFallbackTried = false; + // When used to fill up glyph pages for simple scripts in non-BMP, + // we don't want any font fallback in this class. The simple script + // font path can take care of font fallback. + bool lastFallbackTried = m_disableFontFallback; bool result; int generatedGlyphs = 0; @@ -557,7 +581,7 @@ bool UniscribeHelper::shape(const UChar* input, // PurifyMarkAsInitialized( // &shaping.m_glyphs[0], // sizeof(shaping.m_glyphs[0] * generatedGlyphs); - + ZeroMemory(&shaping.m_glyphs[0], sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size()); #endif @@ -587,7 +611,8 @@ bool UniscribeHelper::shape(const UChar* input, tempDC = 0; } - if (nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) { + if (!m_disableFontFallback && + nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) { // The primary font does not support this run. Try next font. // In case of web page rendering, they come from fonts specified in // CSS stylesheets. @@ -702,6 +727,13 @@ void UniscribeHelper::fillShapes() if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], shaping)) continue; + // At the moment, the only time m_disableFontFallback is set is + // when we look up glyph indices for non-BMP code ranges. So, + // we can skip the glyph placement. When that becomes not the case + // any more, we have to add a new flag to control glyph placement. + if (m_disableFontFallback) + continue; + // Compute placements. Note that offsets is documented incorrectly // and is actually an array. @@ -779,9 +811,6 @@ void UniscribeHelper::adjustSpaceAdvances() int glyphIndex = shaping.m_logs[i]; int currentAdvance = shaping.m_advance[glyphIndex]; - // Don't give zero-width spaces a width. - if (!currentAdvance) - continue; // currentAdvance does not include additional letter-spacing, but // space_width does. Here we find out how off we are from the diff --git a/WebCore/platform/graphics/chromium/UniscribeHelper.h b/WebCore/platform/graphics/chromium/UniscribeHelper.h index d291105..ffd57db 100644 --- a/WebCore/platform/graphics/chromium/UniscribeHelper.h +++ b/WebCore/platform/graphics/chromium/UniscribeHelper.h @@ -44,6 +44,8 @@ class UniscribeTest_TooBig_Test; // A gunit test for UniscribeHelper. namespace WebCore { +class GraphicsContext; + #define UNISCRIBE_HELPER_STACK_RUNS 8 #define UNISCRIBE_HELPER_STACK_CHARS 32 @@ -145,6 +147,16 @@ public: m_ascent = ascent; } + // When set to true, this class is used only to look up glyph + // indices for a range of Unicode characters without glyph placement. + // By default, it's false. This should be set to true when this + // class is used for glyph index look-up for non-BMP characters + // in GlyphPageNodeChromiumWin.cpp. + void setDisableFontFallback(bool disableFontFallback) + { + m_disableFontFallback = true; + } + // You must call this after setting any options but before doing any // other calls like asking for widths or drawing. void init() @@ -177,7 +189,8 @@ public: // be pre-set. // // The y position is the upper left corner, NOT the baseline. - void draw(HDC, int x, int y, int from, int to); + void draw(GraphicsContext* graphicsContext, HDC dc, int x, int y, int from, + int to); // Returns the first glyph assigned to the character at the given offset. // This function is used to retrieve glyph information when Uniscribe is @@ -378,6 +391,7 @@ private: int m_letterSpacing; int m_spaceWidth; int m_wordSpacing; + bool m_disableFontFallback; // Uniscribe breaks the text into Runs. These are one length of text that is // in one script and one direction. This array is in reading order. diff --git a/WebCore/platform/graphics/gtk/FontPlatformData.h b/WebCore/platform/graphics/gtk/FontPlatformData.h index efa5dd5..20c52e5 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformData.h +++ b/WebCore/platform/graphics/gtk/FontPlatformData.h @@ -74,6 +74,7 @@ public: FontPlatformData(float size, bool bold, bool italic); FontPlatformData(cairo_font_face_t* fontFace, int size, bool bold, bool italic); + FontPlatformData(const FontPlatformData&); ~FontPlatformData(); @@ -95,6 +96,7 @@ public: } bool operator==(const FontPlatformData&) const; + FontPlatformData& operator=(const FontPlatformData&); bool isHashTableDeletedValue() const { #if defined(USE_FREETYPE) return m_pattern == hashTableDeletedFontValue(); diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp index 17d789b..68685e9 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2009 Igalia S.L. * All rights reserved. * * This library is free software; you can redistribute it and/or @@ -161,6 +162,45 @@ FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, int size, bool b m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options); } +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) +{ + // Check for self-assignment. + if (this == &other) + return *this; + + m_size = other.m_size; + m_syntheticBold = other.m_syntheticBold; + m_syntheticOblique = other.m_syntheticOblique; + + if (other.m_scaledFont) + cairo_scaled_font_reference(other.m_scaledFont); + if (m_scaledFont) + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = other.m_scaledFont; + + if (other.m_pattern) + FcPatternReference(other.m_pattern); + if (m_pattern) + FcPatternDestroy(m_pattern); + m_pattern = other.m_pattern; + + if (m_fallbacks) { + FcFontSetDestroy(m_fallbacks); + // This will be re-created on demand. + m_fallbacks = 0; + } + + return *this; +} + +FontPlatformData::FontPlatformData(const FontPlatformData& other) + : m_pattern(0) + , m_fallbacks(0) + , m_scaledFont(0) +{ + *this = other; +} + bool FontPlatformData::init() { static bool initialized = false; @@ -176,6 +216,20 @@ bool FontPlatformData::init() FontPlatformData::~FontPlatformData() { + if (m_pattern && ((FcPattern*)-1 != m_pattern)) { + FcPatternDestroy(m_pattern); + m_pattern = 0; + } + + if (m_fallbacks) { + FcFontSetDestroy(m_fallbacks); + m_fallbacks = 0; + } + + if (m_scaledFont) { + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = 0; + } } bool FontPlatformData::isFixedPitch() diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp index be3fd43..88bf427 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp +++ b/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp @@ -4,6 +4,7 @@ * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2007 Holger Hans Peter Freyther * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * Copyright (C) 2009 Igalia S.L. * All rights reserved. * * This library is free software; you can redistribute it and/or @@ -46,8 +47,8 @@ namespace WebCore { - PangoFontMap* FontPlatformData::m_fontMap = 0; - GHashTable* FontPlatformData::m_hashTable = 0; +PangoFontMap* FontPlatformData::m_fontMap = 0; +GHashTable* FontPlatformData::m_hashTable = 0; FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName) : m_context(0) @@ -193,7 +194,20 @@ bool FontPlatformData::init() FontPlatformData::~FontPlatformData() { - // Destroy takes place in FontData::platformDestroy(). + if (m_font && m_font != reinterpret_cast<PangoFont*>(-1)) { + g_object_unref(m_font); + m_font = 0; + } + + if (m_context) { + g_object_unref(m_context); + m_context = 0; + } + + if (m_scaledFont) { + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = 0; + } } bool FontPlatformData::isFixedPitch() @@ -211,6 +225,45 @@ void FontPlatformData::setFont(cairo_t* cr) const cairo_set_scaled_font(cr, m_scaledFont); } +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) +{ + // Check for self-assignment. + if (this == &other) + return *this; + + m_size = other.m_size; + m_syntheticBold = other.m_syntheticBold; + m_syntheticOblique = other.m_syntheticOblique; + + if (other.m_scaledFont) + cairo_scaled_font_reference(other.m_scaledFont); + if (m_scaledFont) + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = other.m_scaledFont; + + if (other.m_font) + g_object_ref(other.m_font); + if (m_font) + g_object_unref(m_font); + m_font = other.m_font; + + if (other.m_context) + g_object_ref(other.m_context); + if (m_context) + g_object_unref(m_context); + m_context = other.m_context; + + return *this; +} + +FontPlatformData::FontPlatformData(const FontPlatformData& other) + : m_context(0) + , m_font(0) + , m_scaledFont(0) +{ + *this = other; +} + bool FontPlatformData::operator==(const FontPlatformData& other) const { if (m_font == other.m_font) diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp index 1f0cac6..8dd1333 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * @@ -101,6 +101,22 @@ gboolean mediaPlayerPrivateBufferingCallback(GstBus* bus, GstMessage* message, g return true; } +static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, MediaPlayerPrivate* playerPrivate) +{ + playerPrivate->repaint(); +} + +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_playBin(0) @@ -111,10 +127,10 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_isEndReached(false) , m_volume(0.5f) , m_networkState(MediaPlayer::Empty) - , m_readyState(MediaPlayer::DataUnavailable) + , m_readyState(MediaPlayer::HaveNothing) , m_startedPlaying(false) , m_isStreaming(false) - , m_rect(IntRect()) + , m_size(IntSize()) , m_visible(true) { @@ -140,15 +156,15 @@ MediaPlayerPrivate::~MediaPlayerPrivate() } } -void MediaPlayerPrivate::load(String url) +void MediaPlayerPrivate::load(const String& url) { LOG_VERBOSE(Media, "Load %s", url.utf8().data()); if (m_networkState != MediaPlayer::Loading) { m_networkState = MediaPlayer::Loading; m_player->networkStateChanged(); } - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } @@ -175,24 +191,28 @@ void MediaPlayerPrivate::pause() m_startedPlaying = false; } -float MediaPlayerPrivate::duration() +float MediaPlayerPrivate::duration() const { if (!m_playBin) return 0.0; - GstFormat fmt = GST_FORMAT_TIME; - gint64 len = 0; - - if (gst_element_query_duration(m_playBin, &fmt, &len)) - LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(len)); - else - LOG_VERBOSE(Media, "Duration query failed "); + GstFormat timeFormat = GST_FORMAT_TIME; + gint64 timeLength = 0; - if ((GstClockTime)len == GST_CLOCK_TIME_NONE) { + // FIXME: We try to get the duration, but we do not trust the + // return value of the query function only; the problem we are + // trying to work-around here is that pipelines in stream mode may + // not be able to figure out the duration, but still return true! + // See https://bugs.webkit.org/show_bug.cgi?id=24639. + if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeLength <= 0) { + LOG_VERBOSE(Media, "Time duration query failed."); m_isStreaming = true; return numeric_limits<float>::infinity(); } - return (float) (len / 1000000000.0); + + LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength)); + + return (float) (timeLength / 1000000000.0); // FIXME: handle 3.14.9.5 properly } @@ -288,7 +308,7 @@ bool MediaPlayerPrivate::seeking() const } // Returns the size of the video -IntSize MediaPlayerPrivate::naturalSize() +IntSize MediaPlayerPrivate::naturalSize() const { if (!hasVideo()) return IntSize(); @@ -302,7 +322,7 @@ IntSize MediaPlayerPrivate::naturalSize() return IntSize(x, y); } -bool MediaPlayerPrivate::hasVideo() +bool MediaPlayerPrivate::hasVideo() const { gint currentVideo = -1; if (m_playBin) @@ -355,17 +375,17 @@ int MediaPlayerPrivate::dataRate() const return 1; } -MediaPlayer::NetworkState MediaPlayerPrivate::networkState() +MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const { return m_networkState; } -MediaPlayer::ReadyState MediaPlayerPrivate::readyState() +MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const { return m_readyState; } -float MediaPlayerPrivate::maxTimeBuffered() +float MediaPlayerPrivate::maxTimeBuffered() const { notImplemented(); LOG_VERBOSE(Media, "maxTimeBuffered"); @@ -373,7 +393,7 @@ float MediaPlayerPrivate::maxTimeBuffered() return m_isStreaming ? 0 : maxTimeLoaded(); } -float MediaPlayerPrivate::maxTimeSeekable() +float MediaPlayerPrivate::maxTimeSeekable() const { // TODO LOG_VERBOSE(Media, "maxTimeSeekable"); @@ -383,7 +403,7 @@ float MediaPlayerPrivate::maxTimeSeekable() return maxTimeLoaded(); } -float MediaPlayerPrivate::maxTimeLoaded() +float MediaPlayerPrivate::maxTimeLoaded() const { // TODO LOG_VERBOSE(Media, "maxTimeLoaded"); @@ -391,7 +411,7 @@ float MediaPlayerPrivate::maxTimeLoaded() return duration(); } -unsigned MediaPlayerPrivate::bytesLoaded() +unsigned MediaPlayerPrivate::bytesLoaded() const { notImplemented(); LOG_VERBOSE(Media, "bytesLoaded"); @@ -404,14 +424,14 @@ unsigned MediaPlayerPrivate::bytesLoaded() return 1;//totalBytes() * maxTime / dur; } -bool MediaPlayerPrivate::totalBytesKnown() +bool MediaPlayerPrivate::totalBytesKnown() const { notImplemented(); LOG_VERBOSE(Media, "totalBytesKnown"); return totalBytes() > 0; } -unsigned MediaPlayerPrivate::totalBytes() +unsigned MediaPlayerPrivate::totalBytes() const { notImplemented(); LOG_VERBOSE(Media, "totalBytes"); @@ -455,12 +475,11 @@ void MediaPlayerPrivate::updateStates() gst_element_state_get_name(pending)); if (state == GST_STATE_READY) { - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveEnoughData; } else if (state == GST_STATE_PAUSED) { - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveEnoughData; } - if (m_networkState < MediaPlayer::Loaded) - m_networkState = MediaPlayer::Loaded; + m_networkState = MediaPlayer::Loaded; g_object_get(m_playBin, "source", &m_source, NULL); if (!m_source) @@ -478,12 +497,11 @@ void MediaPlayerPrivate::updateStates() gst_element_state_get_name(state), gst_element_state_get_name(pending)); if (state == GST_STATE_READY) { - m_readyState = MediaPlayer::CanPlay; + m_readyState = MediaPlayer::HaveFutureData; } else if (state == GST_STATE_PAUSED) { - m_readyState = MediaPlayer::CanPlay; + m_readyState = MediaPlayer::HaveCurrentData; } - if (m_networkState < MediaPlayer::LoadedMetaData) - m_networkState = MediaPlayer::LoadedMetaData; + m_networkState = MediaPlayer::Loading; break; default: LOG_VERBOSE(Media, "Else : %d", ret); @@ -491,7 +509,7 @@ void MediaPlayerPrivate::updateStates() } if (seeking()) - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveNothing; if (m_networkState != oldNetworkState) { LOG_VERBOSE(Media, "Network State Changed from %u to %u", @@ -540,19 +558,19 @@ void MediaPlayerPrivate::didEnd() void MediaPlayerPrivate::loadingFailed() { - if (m_networkState != MediaPlayer::LoadFailed) { - m_networkState = MediaPlayer::LoadFailed; + if (m_networkState != MediaPlayer::NetworkError) { + m_networkState = MediaPlayer::NetworkError; m_player->networkStateChanged(); } - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } } -void MediaPlayerPrivate::setRect(const IntRect& rect) +void MediaPlayerPrivate::setSize(const IntSize& size) { - m_rect = rect; + m_size = size; } void MediaPlayerPrivate::setVisible(bool visible) @@ -573,7 +591,7 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) if (!m_visible) return; - //TODO: m_rect vs rect? + //TODO: m_size vs rect? cairo_t* cr = context->platformContext(); cairo_save(cr); @@ -587,11 +605,18 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) { - // FIXME: do the real thing + // FIXME: query the engine to see what types are supported notImplemented(); types.add(String("video/x-theora+ogg")); } +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // FIXME: query the engine to see what types are supported + notImplemented(); + return type == "video/x-theora+ogg" ? (!codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; +} + void MediaPlayerPrivate::createGSTPlayBin(String url) { ASSERT(!m_playBin); @@ -613,6 +638,8 @@ void MediaPlayerPrivate::createGSTPlayBin(String url) g_object_set(m_playBin, "audio-sink", audioSink, NULL); g_object_set(m_playBin, "video-sink", m_videoSink, NULL); + g_signal_connect(m_videoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this); + setVolume(m_volume); } diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h index 3f08bc0..628f0ac 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * @@ -24,7 +24,7 @@ #if ENABLE(VIDEO) -#include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" #include "Timer.h" #include <gtk/gtk.h> @@ -44,20 +44,21 @@ namespace WebCore { gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); - class MediaPlayerPrivate : Noncopyable + class MediaPlayerPrivate : public MediaPlayerPrivateInterface { friend gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data); friend gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); friend gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); public: - MediaPlayerPrivate(MediaPlayer*); + + static void registerMediaEngine(MediaEngineRegistrar); ~MediaPlayerPrivate(); - IntSize naturalSize(); - bool hasVideo(); + IntSize naturalSize() const; + bool hasVideo() const; - void load(String url); + void load(const String &url); void cancelLoad(); void play(); @@ -66,7 +67,7 @@ namespace WebCore { bool paused() const; bool seeking() const; - float duration(); + float duration() const; float currentTime() const; void seek(float); void setEndTime(float); @@ -77,17 +78,17 @@ namespace WebCore { int dataRate() const; - MediaPlayer::NetworkState networkState(); - MediaPlayer::ReadyState readyState(); + MediaPlayer::NetworkState networkState() const; + MediaPlayer::ReadyState readyState() const; - float maxTimeBuffered(); - float maxTimeSeekable(); - unsigned bytesLoaded(); - bool totalBytesKnown(); - unsigned totalBytes(); + float maxTimeBuffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + bool totalBytesKnown() const; + unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect&); + void setSize(const IntSize&); void loadStateChanged(); void rateChanged(); @@ -99,15 +100,20 @@ namespace WebCore { void repaint(); void paint(GraphicsContext*, const IntRect&); - static void getSupportedTypes(HashSet<String>&); - static bool isAvailable() { return true; } private: + MediaPlayerPrivate(MediaPlayer*); + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable() { return true; } + void updateStates(); void cancelSeek(); void endPointTimerFired(Timer<MediaPlayerPrivate>*); - float maxTimeLoaded(); + float maxTimeLoaded() const; void startEndPointTimerIfNeeded(); void createGSTPlayBin(String url); @@ -124,8 +130,8 @@ namespace WebCore { MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; bool m_startedPlaying; - bool m_isStreaming; - IntRect m_rect; + mutable bool m_isStreaming; + IntSize m_size; bool m_visible; cairo_surface_t* m_surface; }; diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp index d076cb6..4203a3c 100644 --- a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp @@ -53,6 +53,13 @@ void SimpleFontData::platformInit() m_ascent = static_cast<int>(font_extents.ascent); m_descent = static_cast<int>(font_extents.descent); m_lineSpacing = static_cast<int>(font_extents.height); + // There seems to be some rounding error in cairo (or in how we + // use cairo) with some fonts, like DejaVu Sans Mono, which makes + // cairo report a height smaller than ascent + descent, which is + // wrong and confuses WebCore's layout system. Workaround this + // while we figure out what's going on. + if (m_lineSpacing < m_ascent + m_descent) + m_lineSpacing = m_ascent + m_descent; cairo_scaled_font_text_extents(m_font.m_scaledFont, "x", &text_extents); m_xHeight = text_extents.height; cairo_scaled_font_text_extents(m_font.m_scaledFont, " ", &text_extents); @@ -64,31 +71,13 @@ void SimpleFontData::platformDestroy() { delete m_smallCapsFontData; m_smallCapsFontData = 0; - - if (isCustomFont()) - return; - - if (m_font.m_pattern && ((FcPattern*)-1 != m_font.m_pattern)) { - FcPatternDestroy(m_font.m_pattern); - m_font.m_pattern = 0; - } - - if (m_font.m_fallbacks) { - FcFontSetDestroy(m_font.m_fallbacks); - m_font.m_fallbacks = 0; - } - - if (m_font.m_scaledFont) { - cairo_scaled_font_destroy(m_font.m_scaledFont); - m_font.m_scaledFont = 0; - } } SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const { if (!m_smallCapsFontData) { FontDescription desc = FontDescription(fontDescription); - desc.setSpecifiedSize(0.70f*fontDescription.computedSize()); + desc.setComputedSize(0.70f*fontDescription.computedSize()); const FontPlatformData* pdata = new FontPlatformData(desc, desc.family().family()); m_smallCapsFontData = new SimpleFontData(*pdata); } diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp index db8dd3b..e345a8c 100644 --- a/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp +++ b/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp @@ -52,6 +52,13 @@ void SimpleFontData::platformInit() m_ascent = static_cast<int>(font_extents.ascent); m_descent = static_cast<int>(font_extents.descent); m_lineSpacing = static_cast<int>(font_extents.height); + // There seems to be some rounding error in cairo (or in how we + // use cairo) with some fonts, like DejaVu Sans Mono, which makes + // cairo report a height smaller than ascent + descent, which is + // wrong and confuses WebCore's layout system. Workaround this + // while we figure out what's going on. + if (m_lineSpacing < m_ascent + m_descent) + m_lineSpacing = m_ascent + m_descent; cairo_scaled_font_text_extents(m_font.m_scaledFont, "x", &text_extents); m_xHeight = text_extents.height; cairo_scaled_font_text_extents(m_font.m_scaledFont, " ", &text_extents); @@ -61,24 +68,6 @@ void SimpleFontData::platformInit() void SimpleFontData::platformDestroy() { - if (!isCustomFont()) { - - if (m_font.m_font && m_font.m_font != reinterpret_cast<PangoFont*>(-1)) { - g_object_unref(m_font.m_font); - m_font.m_font = 0; - } - - if (m_font.m_context) { - g_object_unref (m_font.m_context); - m_font.m_context = 0; - } - - if (m_font.m_scaledFont) { - cairo_scaled_font_destroy(m_font.m_scaledFont); - m_font.m_scaledFont = 0; - } - } - delete m_smallCapsFontData; m_smallCapsFontData = 0; } diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp index 04df7ac..436841c 100644 --- a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp @@ -46,10 +46,17 @@ static GstElementDetails webkit_video_sink_details = (gchar*) "Alp Toker <alp@atoker.com>"); enum { + REPAINT_REQUESTED, + LAST_SIGNAL +}; + +enum { PROP_0, PROP_SURFACE }; +static guint webkit_video_sink_signals[LAST_SIGNAL] = { 0, }; + struct _WebKitVideoSinkPrivate { cairo_surface_t* surface; GAsyncQueue* async_queue; @@ -95,11 +102,10 @@ webkit_video_sink_init(WebKitVideoSink* sink, WebKitVideoSinkClass* klass) static gboolean webkit_video_sink_idle_func(gpointer data) { - WebKitVideoSinkPrivate* priv; + WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(data); + WebKitVideoSinkPrivate* priv = sink->priv; GstBuffer* buffer; - priv = (WebKitVideoSinkPrivate*)data; - if (!priv->async_queue) return FALSE; @@ -121,6 +127,8 @@ webkit_video_sink_idle_func(gpointer data) gst_buffer_unref(buffer); + g_signal_emit(sink, webkit_video_sink_signals[REPAINT_REQUESTED], 0); + return FALSE; } @@ -131,7 +139,7 @@ webkit_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer) WebKitVideoSinkPrivate* priv = sink->priv; g_async_queue_push(priv->async_queue, gst_buffer_ref(buffer)); - g_idle_add_full(G_PRIORITY_HIGH_IDLE, webkit_video_sink_idle_func, priv, NULL); + g_idle_add_full(G_PRIORITY_HIGH_IDLE, webkit_video_sink_idle_func, sink, NULL); return GST_FLOW_OK; } @@ -279,6 +287,15 @@ webkit_video_sink_class_init(WebKitVideoSinkClass* klass) gstbase_sink_class->stop = webkit_video_sink_stop; gstbase_sink_class->set_caps = webkit_video_sink_set_caps; + webkit_video_sink_signals[REPAINT_REQUESTED] = g_signal_new("repaint-requested", + G_TYPE_FROM_CLASS(klass), + (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + g_object_class_install_property( gobject_class, PROP_SURFACE, g_param_spec_pointer("surface", "surface", "Target cairo_surface_t*", diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm index 9b0f770..1c4350c 100644 --- a/WebCore/platform/graphics/mac/ColorMac.mm +++ b/WebCore/platform/graphics/mac/ColorMac.mm @@ -27,6 +27,7 @@ #import "Color.h" #import "ColorMac.h" +#import <AppKit/AppKit.h> #import <wtf/Assertions.h> #import <wtf/StdLibExtras.h> #import <wtf/RetainPtr.h> @@ -110,7 +111,7 @@ static CGColorRef CGColorFromNSColor(NSColor* color) return cgColor; } -CGColorRef cgColor(const Color& c) +CGColorRef createCGColor(const Color& c) { // We could directly create a CGColor here, but that would // skip any RGB caching the nsColor method does. A direct diff --git a/WebCore/platform/graphics/mac/FontCacheMac.mm b/WebCore/platform/graphics/mac/FontCacheMac.mm index 26d84cc..2202459 100644 --- a/WebCore/platform/graphics/mac/FontCacheMac.mm +++ b/WebCore/platform/graphics/mac/FontCacheMac.mm @@ -35,7 +35,8 @@ #import "FontPlatformData.h" #import "WebCoreSystemInterface.h" #import "WebFontCache.h" -#include <wtf/StdLibExtras.h> +#import <AppKit/AppKit.h> +#import <wtf/StdLibExtras.h> #ifdef BUILDING_ON_TIGER typedef int NSInteger; @@ -191,13 +192,11 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD actualTraits = [fontManager traitsOfFont:nsFont]; NSInteger actualWeight = [fontManager weightOfFont:nsFont]; - FontPlatformData* result = new FontPlatformData; + NSFont *platformFont = fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont]; + bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); + bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); - // Use the correct font for print vs. screen. - result->setFont(fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont]); - result->m_syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); - result->m_syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); - return result; + return new FontPlatformData(platformFont, syntheticBold, syntheticOblique); } } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp index 9aa4997..e40bbab 100644 --- a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp @@ -29,7 +29,8 @@ namespace WebCore { FontCustomPlatformData::~FontCustomPlatformData() { - ATSFontDeactivate(m_atsContainer, NULL, kATSOptionFlagsDefault); + if (m_atsContainer) + ATSFontDeactivate(m_atsContainer, NULL, kATSOptionFlagsDefault); CGFontRelease(m_cgFont); } @@ -42,8 +43,18 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) { ASSERT_ARG(buffer, buffer); - // Use ATS to activate the font. ATSFontContainerRef containerRef = 0; + ATSFontRef fontRef = 0; + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + RetainPtr<CFDataRef> bufferData(AdoptCF, buffer->createCFData()); + RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(bufferData.get())); + + CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider.get()); + if (!cgFontRef) + return 0; +#else + // Use ATS to activate the font. // The value "3" means that the font is private and can't be seen by anyone else. ATSFontActivateFromMemory((void*)buffer->data(), buffer->size(), 3, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &containerRef); @@ -58,7 +69,6 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) return 0; } - ATSFontRef fontRef = 0; ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1, &fontRef, NULL); if (!fontRef) { ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); @@ -77,6 +87,7 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); return 0; } +#endif // !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) return new FontCustomPlatformData(containerRef, fontRef, cgFontRef); } diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.h b/WebCore/platform/graphics/mac/FontCustomPlatformData.h index 1e73ae0..2c1222f 100644 --- a/WebCore/platform/graphics/mac/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.h @@ -22,6 +22,7 @@ #define FontCustomPlatformData_h #include "FontRenderingMode.h" +#include <CoreFoundation/CFBase.h> #include <wtf/Noncopyable.h> typedef struct CGFont* CGFontRef; diff --git a/WebCore/platform/graphics/mac/FontMac.mm b/WebCore/platform/graphics/mac/FontMac.mm index bef18d0..dc86c4b 100644 --- a/WebCore/platform/graphics/mac/FontMac.mm +++ b/WebCore/platform/graphics/mac/FontMac.mm @@ -29,6 +29,7 @@ #import "SimpleFontData.h" #import "WebCoreSystemInterface.h" #import "WebCoreTextRenderer.h" +#import <AppKit/AppKit.h> #define SYNTHETIC_OBLIQUE_ANGLE 14 diff --git a/WebCore/platform/graphics/mac/FontMacATSUI.mm b/WebCore/platform/graphics/mac/FontMacATSUI.mm index 52493e7..3794149 100644 --- a/WebCore/platform/graphics/mac/FontMacATSUI.mm +++ b/WebCore/platform/graphics/mac/FontMacATSUI.mm @@ -30,6 +30,7 @@ #import "Logging.h" #import "ShapeArabic.h" #import "SimpleFontData.h" +#import <AppKit/NSGraphicsContext.h> #import <wtf/OwnArrayPtr.h> #define SYNTHETIC_OBLIQUE_ANGLE 14 diff --git a/WebCore/platform/graphics/mac/FontPlatformData.h b/WebCore/platform/graphics/mac/FontPlatformData.h index 40a2dbd..e911867 100644 --- a/WebCore/platform/graphics/mac/FontPlatformData.h +++ b/WebCore/platform/graphics/mac/FontPlatformData.h @@ -1,8 +1,8 @@ /* * This file is part of the internal font implementation. - * It should not be included by source files outside it. + * It should not be included by source files outside of it. * - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -33,7 +33,6 @@ class NSFont; #endif typedef struct CGFont* CGFontRef; -typedef UInt32 ATSUFontID; #ifndef BUILDING_ON_TIGER typedef const struct __CTFont* CTFontRef; #endif @@ -42,6 +41,8 @@ typedef const struct __CTFont* CTFontRef; #include <objc/objc-auto.h> #include <wtf/RetainPtr.h> +typedef UInt32 ATSUFontID; + namespace WebCore { #ifndef BUILDING_ON_TIGER @@ -61,10 +62,15 @@ struct FontPlatformData { { } - FontPlatformData(NSFont * = 0, bool syntheticBold = false, bool syntheticOblique = false); + FontPlatformData(NSFont *nsFont, bool syntheticBold = false, bool syntheticOblique = false); - FontPlatformData(CGFontRef f, ATSUFontID fontID, float s, bool b , bool o) - : m_syntheticBold(b), m_syntheticOblique(o), m_atsuFontID(fontID), m_size(s), m_font(0), m_cgFont(f) + FontPlatformData(CGFontRef cgFont, ATSUFontID fontID, float size, bool syntheticBold, bool syntheticOblique) + : m_syntheticBold(syntheticBold) + , m_syntheticOblique(syntheticOblique) + , m_atsuFontID(fontID) + , m_size(size) + , m_font(0) + , m_cgFont(cgFont) { } diff --git a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm index 7cd9ab6..83da4a9 100644 --- a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm +++ b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm @@ -1,7 +1,7 @@ /* * This file is part of the internal font implementation. * - * Copyright (C) 2006-7 Apple Computer, Inc. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,21 +24,24 @@ #import "FontPlatformData.h" #import "WebCoreSystemInterface.h" +#import <AppKit/NSFont.h> namespace WebCore { -FontPlatformData::FontPlatformData(NSFont *f, bool b , bool o) -: m_syntheticBold(b), m_syntheticOblique(o), m_font(f) +FontPlatformData::FontPlatformData(NSFont *nsFont, bool syntheticBold, bool syntheticOblique) + : m_syntheticBold(syntheticBold) + , m_syntheticOblique(syntheticOblique) + , m_font(nsFont) { - if (f) - CFRetain(f); - m_size = f ? [f pointSize] : 0.0f; + if (nsFont) + CFRetain(nsFont); + m_size = nsFont ? [nsFont pointSize] : 0.0f; #ifndef BUILDING_ON_TIGER - m_cgFont.adoptCF(CTFontCopyGraphicsFont(toCTFontRef(f), 0)); - m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(f), 0); + m_cgFont.adoptCF(CTFontCopyGraphicsFont(toCTFontRef(nsFont), 0)); + m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(nsFont), 0); #else - m_cgFont = wkGetCGFontFromNSFont(f); - m_atsuFontID = wkGetNSFontATSUFontId(f); + m_cgFont = wkGetCGFontFromNSFont(nsFont); + m_atsuFontID = wkGetNSFontATSUFontId(nsFont); #endif } diff --git a/WebCore/platform/graphics/mac/GraphicsContextMac.mm b/WebCore/platform/graphics/mac/GraphicsContextMac.mm index ae829e2..4e11602 100644 --- a/WebCore/platform/graphics/mac/GraphicsContextMac.mm +++ b/WebCore/platform/graphics/mac/GraphicsContextMac.mm @@ -27,10 +27,13 @@ #import "GraphicsContext.h" #import "../cg/GraphicsContextPlatformPrivateCG.h" +#import <AppKit/AppKit.h> #import <wtf/StdLibExtras.h> #import "WebCoreSystemInterface.h" +@class NSColor; + // FIXME: More of this should use CoreGraphics instead of AppKit. // FIXME: More of this should move into GraphicsContextCG.cpp. @@ -47,7 +50,7 @@ void GraphicsContext::drawFocusRing(const Color& color) int radius = (focusRingWidth() - 1) / 2; int offset = radius + focusRingOffset(); - CGColorRef colorRef = color.isValid() ? cgColor(color) : 0; + CGColorRef colorRef = color.isValid() ? createCGColor(color) : 0; CGMutablePathRef focusRingPath = CGPathCreateMutable(); const Vector<IntRect>& rects = focusRingRects(); diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h new file mode 100644 index 0000000..3a692d3 --- /dev/null +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GraphicsLayerCA_h +#define GraphicsLayerCA_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayer.h" +#include <wtf/RetainPtr.h> + +@class WebAnimationDelegate; +@class WebLayer; + +namespace WebCore { + +class GraphicsLayerCA : public GraphicsLayer { +public: + + GraphicsLayerCA(GraphicsLayerClient*); + virtual ~GraphicsLayerCA(); + + virtual void setName(const String&); + + // for hosting this GraphicsLayer in a native layer hierarchy + virtual NativeLayer nativeLayer() const; + + 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 setPosition(const FloatPoint&); + virtual void setAnchorPoint(const FloatPoint3D&); + virtual void setSize(const FloatSize&); + + virtual void setTransform(const TransformationMatrix&); + + virtual void setChildrenTransform(const TransformationMatrix&); + + virtual void setPreserves3D(bool); + virtual void setMasksToBounds(bool); + virtual void setDrawsContent(bool); + + virtual void setBackgroundColor(const Color&, const Animation* anim = 0, double beginTime = 0); + virtual void clearBackgroundColor(); + + virtual void setContentsOpaque(bool); + virtual void setBackfaceVisibility(bool); + + // return true if we started an animation + virtual bool setOpacity(float, const Animation* anim = 0, double beginTime = 0); + + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + + virtual void suspendAnimations(); + virtual void resumeAnimations(); + + virtual bool animateTransform(const TransformValueList&, const IntSize&, const Animation*, double beginTime, bool isTransition); + virtual bool animateFloat(AnimatedPropertyID, const FloatValueList&, const Animation*, double beginTime); + + virtual void setContentsToImage(Image*); + virtual void setContentsToVideo(PlatformLayer*); + virtual void clearContents(); + + virtual void updateContentsRect(); + + virtual PlatformLayer* platformLayer() const; + +#ifndef NDEBUG + virtual void setDebugBackgroundColor(const Color&); + virtual void setDebugBorder(const Color&, float borderWidth); + virtual void setZPosition(float); +#endif + +private: + WebLayer* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } + WebLayer* hostLayerForSublayers() const; + WebLayer* layerForSuperlayer() const; + + WebLayer* animatedLayer(AnimatedPropertyID property) const + { + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); + } + + void setBasicAnimation(AnimatedPropertyID, TransformOperation::OperationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation*, double time); + void setKeyframeAnimation(AnimatedPropertyID, TransformOperation::OperationType, short index, void* keys, void* values, void* timingFunctions, bool isTransition, const Animation*, double time); + + virtual void removeAnimation(int index, bool reset); + + bool requiresTiledLayer(const FloatSize&) const; + void swapFromOrToTiledLayer(bool useTiledLayer); + + void setHasContentsLayer(bool); + void setContentsLayer(WebLayer*); + void setContentsLayerFlipped(bool); + + RetainPtr<WebLayer> m_layer; + RetainPtr<WebLayer> m_transformLayer; + RetainPtr<WebLayer> m_contentsLayer; + + RetainPtr<WebAnimationDelegate> m_animationDelegate; + + bool m_contentLayerForImageOrVideo; +}; + +} // namespace WebCore + + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayerCA_h diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm new file mode 100644 index 0000000..f3f2d7f --- /dev/null +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -0,0 +1,1540 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "GraphicsLayerCA.h" + +#import "Animation.h" +#import "BlockExceptions.h" +#import "CString.h" +#import "FloatConversion.h" +#import "FloatRect.h" +#import "Image.h" +#import "PlatformString.h" +#import <QuartzCore/QuartzCore.h> +#import "RotateTransformOperation.h" +#import "ScaleTransformOperation.h" +#import "SystemTime.h" +#import "TranslateTransformOperation.h" +#import "WebLayer.h" +#import "WebTiledLayer.h" +#import <wtf/CurrentTime.h> +#import <wtf/UnusedParam.h> + +using namespace std; + +#define HAVE_MODERN_QUARTZCORE (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + +namespace WebCore { + +// The threshold width or height above which a tiled layer will be used. This should be +// large enough to avoid tiled layers for most GraphicsLayers, but less than the OpenGL +// texture size limit on all supported hardware. +static const int cMaxPixelDimension = 2000; + +// The width and height of a single tile in a tiled layer. Should be large enough to +// avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough +// to keep the overall tile cost low. +static const int cTiledLayerTileSize = 512; + +// If we send a duration of 0 to CA, then it will use the default duration +// of 250ms. So send a very small value instead. +static const float cAnimationAlmostZeroDuration = 1e-3f; + +// CACurrentMediaTime() is a time since boot. These methods convert between that and +// WebCore time, which is system time (UTC). +static CFTimeInterval currentTimeToMediaTime(double t) +{ + return CACurrentMediaTime() + t - WTF::currentTime(); +} + +static double mediaTimeToCurrentTime(CFTimeInterval t) +{ + return WTF::currentTime() + t - CACurrentMediaTime(); +} + +} // namespace WebCore + +static NSString* const WebAnimationCSSPropertyKey = @"GraphicsLayerCA_property"; + +@interface WebAnimationDelegate : NSObject { + WebCore::GraphicsLayerCA* m_graphicsLayer; +} + +- (void)animationDidStart:(CAAnimation *)anim; +- (WebCore::GraphicsLayerCA*)graphicsLayer; +- (void)setLayer:(WebCore::GraphicsLayerCA*)graphicsLayer; + +@end + +@implementation WebAnimationDelegate + +- (void)animationDidStart:(CAAnimation *)animation +{ + if (!m_graphicsLayer) + return; + + double startTime = WebCore::mediaTimeToCurrentTime([animation beginTime]); + m_graphicsLayer->client()->notifyAnimationStarted(m_graphicsLayer, startTime); +} + +- (WebCore::GraphicsLayerCA*)graphicsLayer +{ + return m_graphicsLayer; +} + +- (void)setLayer:(WebCore::GraphicsLayerCA*)graphicsLayer +{ + m_graphicsLayer = graphicsLayer; +} + +@end + +namespace WebCore { + +static inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) +{ + toT3D.m11 = narrowPrecisionToFloat(t.m11()); + toT3D.m12 = narrowPrecisionToFloat(t.m12()); + toT3D.m13 = narrowPrecisionToFloat(t.m13()); + toT3D.m14 = narrowPrecisionToFloat(t.m14()); + toT3D.m21 = narrowPrecisionToFloat(t.m21()); + toT3D.m22 = narrowPrecisionToFloat(t.m22()); + toT3D.m23 = narrowPrecisionToFloat(t.m23()); + toT3D.m24 = narrowPrecisionToFloat(t.m24()); + toT3D.m31 = narrowPrecisionToFloat(t.m31()); + toT3D.m32 = narrowPrecisionToFloat(t.m32()); + toT3D.m33 = narrowPrecisionToFloat(t.m33()); + toT3D.m34 = narrowPrecisionToFloat(t.m34()); + toT3D.m41 = narrowPrecisionToFloat(t.m41()); + toT3D.m42 = narrowPrecisionToFloat(t.m42()); + toT3D.m43 = narrowPrecisionToFloat(t.m43()); + toT3D.m44 = narrowPrecisionToFloat(t.m44()); +} + +static NSValue* getTransformFunctionValue(const GraphicsLayer::TransformValue& transformValue, size_t index, const IntSize& size, TransformOperation::OperationType transformType) +{ + TransformOperation* op = (index >= transformValue.value()->operations().size()) ? 0 : transformValue.value()->operations()[index].get(); + + switch (transformType) { + case TransformOperation::ROTATE: + case TransformOperation::ROTATE_X: + case TransformOperation::ROTATE_Y: + return [NSNumber numberWithDouble:op ? deg2rad(static_cast<RotateTransformOperation*>(op)->angle()) : 0]; + case TransformOperation::SCALE_X: + return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->x() : 0]; + case TransformOperation::SCALE_Y: + return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->y() : 0]; + case TransformOperation::SCALE_Z: + return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->z() : 0]; + case TransformOperation::TRANSLATE_X: + return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->x(size) : 0]; + case TransformOperation::TRANSLATE_Y: + return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->y(size) : 0]; + case TransformOperation::TRANSLATE_Z: + return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->z(size) : 0]; + case TransformOperation::SCALE: + case TransformOperation::TRANSLATE: + case TransformOperation::SKEW_X: + case TransformOperation::SKEW_Y: + case TransformOperation::SKEW: + case TransformOperation::MATRIX: + case TransformOperation::SCALE_3D: + case TransformOperation::TRANSLATE_3D: + case TransformOperation::ROTATE_3D: + case TransformOperation::MATRIX_3D: + case TransformOperation::PERSPECTIVE: + case TransformOperation::IDENTITY: + case TransformOperation::NONE: { + TransformationMatrix t; + if (op) + op->apply(t, size); + CATransform3D cat; + copyTransform(cat, t); + return [NSValue valueWithCATransform3D:cat]; + } + } + + return 0; +} + +#if HAVE_MODERN_QUARTZCORE +static NSString* getValueFunctionNameForTransformOperation(TransformOperation::OperationType transformType) +{ + // Use literal strings to avoid link-time dependency on those symbols. + switch (transformType) { + case TransformOperation::ROTATE_X: + return @"rotateX"; // kCAValueFunctionRotateX; + case TransformOperation::ROTATE_Y: + return @"rotateY"; // kCAValueFunctionRotateY; + case TransformOperation::ROTATE: + return @"rotateZ"; // kCAValueFunctionRotateZ; + case TransformOperation::SCALE_X: + return @"scaleX"; // kCAValueFunctionScaleX; + case TransformOperation::SCALE_Y: + return @"scaleY"; // kCAValueFunctionScaleY; + case TransformOperation::SCALE_Z: + return @"scaleZ"; // kCAValueFunctionScaleZ; + case TransformOperation::TRANSLATE_X: + return @"translateX"; // kCAValueFunctionTranslateX; + case TransformOperation::TRANSLATE_Y: + return @"translateY"; // kCAValueFunctionTranslateY; + case TransformOperation::TRANSLATE_Z: + return @"translateZ"; // kCAValueFunctionTranslateZ; + default: + return nil; + } +} +#endif + +static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction) +{ + switch (timingFunction.type()) { + case LinearTimingFunction: + return [CAMediaTimingFunction functionWithName:@"linear"]; + case CubicBezierTimingFunction: + return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(timingFunction.x1()) :static_cast<float>(timingFunction.y1()) + :static_cast<float>(timingFunction.x2()) :static_cast<float>(timingFunction.y2())]; + } + return 0; +} + +#ifndef NDEBUG +static void setLayerBorderColor(PlatformLayer* layer, const Color& color) +{ + CGColorRef borderColor = createCGColor(color); + [layer setBorderColor:borderColor]; + CGColorRelease(borderColor); +} + +static void clearBorderColor(PlatformLayer* layer) +{ + [layer setBorderColor:nil]; +} +#endif + +static void setLayerBackgroundColor(PlatformLayer* layer, const Color& color) +{ + CGColorRef bgColor = createCGColor(color); + [layer setBackgroundColor:bgColor]; + CGColorRelease(bgColor); +} + +static void clearLayerBackgroundColor(PlatformLayer* layer) +{ + [layer setBackgroundColor:0]; +} + +static CALayer* getPresentationLayer(CALayer* layer) +{ + CALayer* presLayer = [layer presentationLayer]; + if (!presLayer) + presLayer = layer; + + return presLayer; +} + +static bool caValueFunctionSupported() +{ + static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; + return sHaveValueFunction; +} + +static bool forceSoftwareAnimation() +{ + static bool forceSoftwareAnimation = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreForceSoftwareAnimation"]; + return forceSoftwareAnimation; +} + +bool GraphicsLayer::graphicsContextsFlipped() +{ + return true; +} + +#ifndef NDEBUG +bool GraphicsLayer::showDebugBorders() +{ + static bool showDebugBorders = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerBorders"]; + return showDebugBorders; +} + +bool GraphicsLayer::showRepaintCounter() +{ + static bool showRepaintCounter = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerRepaintCounter"]; + return showRepaintCounter; +} +#endif + +static NSDictionary* nullActionsDictionary() +{ + NSNull* nullValue = [NSNull null]; + NSDictionary* actions = [NSDictionary dictionaryWithObjectsAndKeys: + nullValue, @"anchorPoint", + nullValue, @"bounds", + nullValue, @"contents", + nullValue, @"contentsRect", + nullValue, @"opacity", + nullValue, @"position", + nullValue, @"shadowColor", + nullValue, @"sublayerTransform", + nullValue, @"sublayers", + nullValue, @"transform", +#ifndef NDEBUG + nullValue, @"zPosition", +#endif + nil]; + return actions; +} + +GraphicsLayer* GraphicsLayer::createGraphicsLayer(GraphicsLayerClient* client) +{ + return new GraphicsLayerCA(client); +} + +GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) +: GraphicsLayer(client) +, m_contentLayerForImageOrVideo(false) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + m_layer.adoptNS([[WebLayer alloc] init]); + [m_layer.get() setLayerOwner:this]; + +#ifndef NDEBUG + updateDebugIndicators(); +#endif + + m_animationDelegate.adoptNS([[WebAnimationDelegate alloc] init]); + [m_animationDelegate.get() setLayer:this]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +GraphicsLayerCA::~GraphicsLayerCA() +{ + // Remove a inner layer if there is one. + clearContents(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // Clean up the WK layer. + if (m_layer) { + WebLayer* layer = m_layer.get(); + [layer setLayerOwner:nil]; + [layer removeFromSuperlayer]; + } + + if (m_transformLayer) + [m_transformLayer.get() removeFromSuperlayer]; + + // animationDidStart: can fire after this, so we need to clear out the layer on the delegate. + [m_animationDelegate.get() setLayer:0]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setName(const String& name) +{ + String longName = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + name; + GraphicsLayer::setName(longName); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setName:name]; + END_BLOCK_OBJC_EXCEPTIONS +} + +NativeLayer GraphicsLayerCA::nativeLayer() const +{ + return m_layer.get(); +} + +void GraphicsLayerCA::addChild(GraphicsLayer* childLayer) +{ + GraphicsLayer::addChild(childLayer); + + GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); + BEGIN_BLOCK_OBJC_EXCEPTIONS + [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::addChildAtIndex(GraphicsLayer* childLayer, int index) +{ + GraphicsLayer::addChildAtIndex(childLayer, index); + + GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); + BEGIN_BLOCK_OBJC_EXCEPTIONS + [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() atIndex:index]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + // FIXME: share code with base class + ASSERT(childLayer != this); + childLayer->removeFromParent(); + + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (sibling == m_children[i]) { + m_children.insert(i, childLayer); + found = true; + break; + } + } + childLayer->setParent(this); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); + GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(sibling); + if (found) + [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() below:siblingLayerCA->layerForSuperlayer()]; + else { + m_children.append(childLayer); + [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + // FIXME: share code with base class + ASSERT(childLayer != this); + childLayer->removeFromParent(); + + unsigned i; + bool found = false; + for (i = 0; i < m_children.size(); i++) { + if (sibling == m_children[i]) { + m_children.insert(i+1, childLayer); + found = true; + break; + } + } + childLayer->setParent(this); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); + GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(sibling); + if (found) { + [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() above:siblingLayerCA->layerForSuperlayer()]; + } else { + m_children.append(childLayer); + [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + // FIXME: share code with base class + ASSERT(!newChild->parent()); + + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (oldChild == m_children[i]) { + m_children[i] = newChild; + found = true; + break; + } + } + + if (found) { + oldChild->setParent(0); + + newChild->removeFromParent(); + newChild->setParent(this); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + GraphicsLayerCA* oldChildCA = static_cast<GraphicsLayerCA*>(oldChild); + GraphicsLayerCA* newChildCA = static_cast<GraphicsLayerCA*>(newChild); + [hostLayerForSublayers() replaceSublayer:oldChildCA->layerForSuperlayer() with:newChildCA->layerForSuperlayer()]; + END_BLOCK_OBJC_EXCEPTIONS + return true; + } + return false; +} + +void GraphicsLayerCA::removeFromParent() +{ + GraphicsLayer::removeFromParent(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [layerForSuperlayer() removeFromSuperlayer]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setPosition(const FloatPoint& point) +{ + // Don't short-circuit here, because position and anchor point are inter-dependent. + GraphicsLayer::setPosition(point); + + // Position is offset on the layer by the layer anchor point. + CGPoint posPoint = CGPointMake(m_position.x() + m_anchorPoint.x() * m_size.width(), + m_position.y() + m_anchorPoint.y() * m_size.height()); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [primaryLayer() setPosition:posPoint]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) +{ + // Don't short-circuit here, because position and anchor point are inter-dependent. + bool zChanged = (point.z() != m_anchorPoint.z()); + GraphicsLayer::setAnchorPoint(point); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + // set the value on the layer to the new transform. + [primaryLayer() setAnchorPoint:FloatPoint(point.x(), point.y())]; + + if (zChanged) { +#if HAVE_MODERN_QUARTZCORE + [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; +#endif + } + + // Position depends on anchor point, so update it now. + setPosition(m_position); + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setSize(const FloatSize& size) +{ + GraphicsLayer::setSize(size); + + CGRect rect = CGRectMake(0.0f, + 0.0f, + m_size.width(), + m_size.height()); + + CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (m_transformLayer) { + [m_transformLayer.get() setBounds:rect]; + + // the anchor of the contents layer is always at 0.5, 0.5, so the position + // is center-relative + [m_layer.get() setPosition:centerPoint]; + } + + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + [m_layer.get() setBounds:rect]; + + // Note that we don't resize m_contentsLayer. It's up the caller to do that. + + END_BLOCK_OBJC_EXCEPTIONS + + // if we've changed the bounds, we need to recalculate the position + // of the layer, taking anchor point into account + setPosition(m_position); +} + +void GraphicsLayerCA::setTransform(const TransformationMatrix& t) +{ + GraphicsLayer::setTransform(t); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + CATransform3D transform; + copyTransform(transform, t); + [primaryLayer() setTransform:transform]; + END_BLOCK_OBJC_EXCEPTIONS + + // Remove any old transition entries for transform. + removeAllAnimationsForProperty(AnimatedPropertyWebkitTransform); + + // Even if we don't have a transition in the list, the layer may still have one. + // This happens when we are setting the final transform value after an animation or + // transition has ended. In removeAnimation we toss the entry from the list but don't + // remove it from the list. That way we aren't in danger of displaying a stale transform + // in the time between removing the animation and setting the new unanimated value. We + // can't do this in removeAnimation because we don't know the new transform value there. + String keyPath = propertyIdToString(AnimatedPropertyWebkitTransform); + CALayer* layer = animatedLayer(AnimatedPropertyWebkitTransform); + + for (int i = 0; ; ++i) { + String animName = keyPath + "_" + String::number(i); + if (![layer animationForKey: animName]) + break; + [layer removeAnimationForKey:animName]; + } +} + +void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) +{ + if (t == m_childrenTransform) + return; + + GraphicsLayer::setChildrenTransform(t); + + CATransform3D transform; + copyTransform(transform, t); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + // Set the value on the layer to the new transform. + [primaryLayer() setSublayerTransform:transform]; + END_BLOCK_OBJC_EXCEPTIONS +} + +static void moveAnimation(AnimatedPropertyID property, CALayer* fromLayer, CALayer* toLayer) +{ + String keyPath = GraphicsLayer::propertyIdToString(property); + for (short index = 0; ; ++index) { + String animName = keyPath + "_" + String::number(index); + CAAnimation* anim = [fromLayer animationForKey:animName]; + if (!anim) + break; + + [anim retain]; + [fromLayer removeAnimationForKey:animName]; + [toLayer addAnimation:anim forKey:animName]; + [anim release]; + } +} + +static void moveSublayers(CALayer* fromLayer, CALayer* toLayer) +{ + NSArray* sublayersCopy = [[fromLayer sublayers] copy]; // Avoid mutation while enumerating, and keep the sublayers alive. + NSEnumerator* childrenEnumerator = [sublayersCopy objectEnumerator]; + + CALayer* layer; + while ((layer = [childrenEnumerator nextObject]) != nil) { + [layer removeFromSuperlayer]; + [toLayer addSublayer:layer]; + } + [sublayersCopy release]; +} + +void GraphicsLayerCA::setPreserves3D(bool preserves3D) +{ + GraphicsLayer::setPreserves3D(preserves3D); + + CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + CGPoint centerPoint = CGPointMake(0.5f, 0.5f); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + Class transformLayerClass = NSClassFromString(@"CATransformLayer"); + if (preserves3D && !m_transformLayer && transformLayerClass) { + // Create the transform layer. + m_transformLayer.adoptNS([[transformLayerClass alloc] init]); + + // Turn off default animations. + [m_transformLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + +#ifndef NDEBUG + [m_transformLayer.get() setName:[NSString stringWithFormat:@"Transform Layer CATransformLayer(%p) GraphicsLayer(%p)", m_transformLayer.get(), this]]; +#endif + // Copy the position from this layer. + [m_transformLayer.get() setBounds:[m_layer.get() bounds]]; + [m_transformLayer.get() setPosition:[m_layer.get() position]]; + [m_transformLayer.get() setAnchorPoint:[m_layer.get() anchorPoint]]; +#if HAVE_MODERN_QUARTZCORE + [m_transformLayer.get() setAnchorPointZ:[m_layer.get() anchorPointZ]]; +#endif + [m_transformLayer.get() setContentsRect:[m_layer.get() contentsRect]]; +#ifndef NDEBUG + [m_transformLayer.get() setZPosition:[m_layer.get() zPosition]]; +#endif + + // The contents layer is positioned at (0,0) relative to the transformLayer. + [m_layer.get() setPosition:point]; + [m_layer.get() setAnchorPoint:centerPoint]; +#ifndef NDEBUG + [m_layer.get() setZPosition:0.0f]; +#endif + + // Transfer the transform over. + [m_transformLayer.get() setTransform:[m_layer.get() transform]]; + [m_layer.get() setTransform:CATransform3DIdentity]; + + // Transfer the opacity from the old layer to the transform layer. + [m_transformLayer.get() setOpacity:m_opacity]; + [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_transformLayer.get()]; + [m_transformLayer.get() addSublayer:m_layer.get()]; + + moveAnimation(AnimatedPropertyWebkitTransform, m_layer.get(), m_transformLayer.get()); + moveSublayers(m_layer.get(), m_transformLayer.get()); + + } else if (!preserves3D && m_transformLayer) { + // Relace the transformLayer in the parent with this layer. + [m_layer.get() removeFromSuperlayer]; + [[m_transformLayer.get() superlayer] replaceSublayer:m_transformLayer.get() with:m_layer.get()]; + + moveAnimation(AnimatedPropertyWebkitTransform, m_transformLayer.get(), m_layer.get()); + moveSublayers(m_transformLayer.get(), m_layer.get()); + + // Reset the layer position and transform. + [m_layer.get() setPosition:[m_transformLayer.get() position]]; + [m_layer.get() setAnchorPoint:[m_transformLayer.get() anchorPoint]]; +#if HAVE_MODERN_QUARTZCORE + [m_layer.get() setAnchorPointZ:[m_transformLayer.get() anchorPointZ]]; +#endif + [m_layer.get() setContentsRect:[m_transformLayer.get() contentsRect]]; + [m_layer.get() setTransform:[m_transformLayer.get() transform]]; + [m_layer.get() setOpacity:[m_transformLayer.get() opacity]]; +#ifndef NDEBUG + [m_layer.get() setZPosition:[m_transformLayer.get() zPosition]]; +#endif + + // Release the transform layer. + m_transformLayer = 0; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) +{ + if (masksToBounds == m_masksToBounds) + return; + + GraphicsLayer::setMasksToBounds(masksToBounds); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setMasksToBounds:masksToBounds]; + END_BLOCK_OBJC_EXCEPTIONS + +#ifndef NDEBUG + updateDebugIndicators(); +#endif +} + +void GraphicsLayerCA::setDrawsContent(bool drawsContent) +{ + if (drawsContent != m_drawsContent) { + GraphicsLayer::setDrawsContent(drawsContent); + + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + // Clobber any existing content. If necessary, CA will create backing store on the next display. + [m_layer.get() setContents:nil]; + +#ifndef NDEBUG + updateDebugIndicators(); +#endif + END_BLOCK_OBJC_EXCEPTIONS + } +} + +void GraphicsLayerCA::setBackgroundColor(const Color& color, const Animation* transition, double beginTime) +{ + GraphicsLayer::setBackgroundColor(color, transition, beginTime); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + setHasContentsLayer(true); + + if (transition && !transition->isEmptyOrZeroDuration()) { + CALayer* presLayer = [m_contentsLayer.get() presentationLayer]; + // If we don't have a presentationLayer, just use the CALayer + if (!presLayer) + presLayer = m_contentsLayer.get(); + + // Get the current value of the background color from the layer + CGColorRef fromBackgroundColor = [presLayer backgroundColor]; + + CGColorRef bgColor = createCGColor(color); + setBasicAnimation(AnimatedPropertyBackgroundColor, TransformOperation::NONE, 0, fromBackgroundColor, bgColor, true, transition, beginTime); + CGColorRelease(bgColor); + } else { + removeAllAnimationsForProperty(AnimatedPropertyBackgroundColor); + setHasContentsLayer(true); + setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::clearBackgroundColor() +{ + if (!m_contentLayerForImageOrVideo) + setHasContentsLayer(false); + else + clearLayerBackgroundColor(m_contentsLayer.get()); +} + +void GraphicsLayerCA::setContentsOpaque(bool opaque) +{ + GraphicsLayer::setContentsOpaque(opaque); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setOpaque:m_contentsOpaque]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setBackfaceVisibility(bool visible) +{ + if (m_backfaceVisibility == visible) + return; + + GraphicsLayer::setBackfaceVisibility(visible); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setDoubleSided:visible]; + END_BLOCK_OBJC_EXCEPTIONS +} + +bool GraphicsLayerCA::setOpacity(float opacity, const Animation* transition, double beginTime) +{ + if (forceSoftwareAnimation()) + return false; + + float clampedOpacity = max(0.0f, min(opacity, 1.0f)); + + bool opacitiesDiffer = (m_opacity != clampedOpacity); + + GraphicsLayer::setOpacity(clampedOpacity, transition, beginTime); + + int animIndex = findAnimationEntry(AnimatedPropertyOpacity, 0); + + // If we don't have a transition just set the opacity + if (!transition || transition->isEmptyOrZeroDuration()) { + // Three cases: + // 1) no existing animation or transition: just set opacity + // 2) existing transition: clear it and set opacity + // 3) existing animation: just return + // + if (animIndex < 0 || m_animations[animIndex].isTransition()) { + BEGIN_BLOCK_OBJC_EXCEPTIONS + [primaryLayer() setOpacity:opacity]; + if (animIndex >= 0) { + removeAllAnimationsForProperty(AnimatedPropertyOpacity); + animIndex = -1; + } else { + String keyPath = propertyIdToString(AnimatedPropertyOpacity); + + // FIXME: using hardcoded '0' here. We should be clearing all animations. For now there is only 1. + String animName = keyPath + "_0"; + [animatedLayer(AnimatedPropertyOpacity) removeAnimationForKey:animName]; + } + END_BLOCK_OBJC_EXCEPTIONS + } else { + // We have an animation, so don't set the opacity directly. + return false; + } + } else { + // At this point, we know we have a transition. But if it is the same and + // the opacity value has not changed, don't do anything. + if (!opacitiesDiffer && + ((animIndex == -1) || + (m_animations[animIndex].isTransition() && *(m_animations[animIndex].animation()) == *transition))) { + return false; + } + } + + // If an animation is running, ignore this transition, but still save the value. + if (animIndex >= 0 && !m_animations[animIndex].isTransition()) + return false; + + bool didAnimate = false; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + NSNumber* fromOpacityValue = nil; + NSNumber* toOpacityValue = [NSNumber numberWithFloat:opacity]; + + if (transition && !transition->isEmptyOrZeroDuration()) { + CALayer* presLayer = getPresentationLayer(primaryLayer()); + float fromOpacity = [presLayer opacity]; + fromOpacityValue = [NSNumber numberWithFloat:fromOpacity]; + setBasicAnimation(AnimatedPropertyOpacity, TransformOperation::NONE, 0, fromOpacityValue, toOpacityValue, true, transition, beginTime); + didAnimate = true; + } + + END_BLOCK_OBJC_EXCEPTIONS + + return didAnimate; +} + +void GraphicsLayerCA::setNeedsDisplay() +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (drawsContent()) + [m_layer.get() setNeedsDisplay]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (drawsContent()) + [m_layer.get() setNeedsDisplayInRect:rect]; + + END_BLOCK_OBJC_EXCEPTIONS +} + + +bool GraphicsLayerCA::animateTransform(const TransformValueList& valueList, const IntSize& size, const Animation* anim, double beginTime, bool isTransition) +{ + if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) + return false; + + TransformValueList::FunctionList functionList; + bool isValid, hasBigRotation; + valueList.makeFunctionList(functionList, isValid, hasBigRotation); + + // We need to fall back to software animation if we don't have setValueFunction:, and + // we would need to animate each incoming transform function separately. This is the + // case if we have a rotation >= 180 or we have more than one transform function. + if ((hasBigRotation || functionList.size() > 1) && !caValueFunctionSupported()) + return false; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // Rules for animation: + // + // 1) If functionList is empty or we don't have a big rotation, we do a matrix animation. We could + // use component animation for lists without a big rotation, but there is no need to, and this + // is more efficient. + // + // 2) Otherwise we do a component hardware animation. + bool isMatrixAnimation = !isValid || !hasBigRotation; + + // Set transform to identity since we are animating components and we need the base + // to be the identity transform. + TransformationMatrix t; + CATransform3D toT3D; + copyTransform(toT3D, t); + [primaryLayer() setTransform:toT3D]; + + bool isKeyframe = valueList.size() > 2; + + // Iterate through the transform functions, sending an animation for each one. + for (int functionIndex = 0; ; ++functionIndex) { + if (functionIndex >= static_cast<int>(functionList.size()) && !isMatrixAnimation) + break; + + TransformOperation::OperationType opType = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[functionIndex]; + + if (isKeyframe) { + NSMutableArray* timesArray = [[NSMutableArray alloc] init]; + NSMutableArray* valArray = [[NSMutableArray alloc] init]; + NSMutableArray* tfArray = [[NSMutableArray alloc] init]; + + // Iterate through the keyframes, building arrays for the animation. + for (Vector<TransformValue>::const_iterator it = valueList.values().begin(); it != valueList.values().end(); ++it) { + const TransformValue& curValue = (*it); + + // fill in the key time and timing function + [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; + + const TimingFunction* tf = 0; + if (curValue.timingFunction()) + tf = curValue.timingFunction(); + else if (anim->isTimingFunctionSet()) + tf = &anim->timingFunction(); + + CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction(LinearTimingFunction)); + [tfArray addObject:timingFunction]; + + // fill in the function + if (isMatrixAnimation) { + TransformationMatrix t; + curValue.value()->apply(size, t); + CATransform3D cat; + copyTransform(cat, t); + [valArray addObject:[NSValue valueWithCATransform3D:cat]]; + } else + [valArray addObject:getTransformFunctionValue(curValue, functionIndex, size, opType)]; + } + + // We toss the last tfArray value because it has to one shorter than the others. + [tfArray removeLastObject]; + + setKeyframeAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, timesArray, valArray, tfArray, isTransition, anim, beginTime); + + [timesArray release]; + [valArray release]; + [tfArray release]; + } else { + // Is a transition + id fromValue, toValue; + + if (isMatrixAnimation) { + TransformationMatrix fromt, tot; + valueList.at(0).value()->apply(size, fromt); + valueList.at(1).value()->apply(size, tot); + + CATransform3D cat; + copyTransform(cat, fromt); + fromValue = [NSValue valueWithCATransform3D:cat]; + copyTransform(cat, tot); + toValue = [NSValue valueWithCATransform3D:cat]; + } else { + fromValue = getTransformFunctionValue(valueList.at(0), functionIndex, size, opType); + toValue = getTransformFunctionValue(valueList.at(1), functionIndex, size, opType); + } + + setBasicAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, fromValue, toValue, isTransition, anim, beginTime); + } + + if (isMatrixAnimation) + break; + } + + END_BLOCK_OBJC_EXCEPTIONS + return true; +} + +bool GraphicsLayerCA::animateFloat(AnimatedPropertyID property, const FloatValueList& valueList, const Animation* animation, double beginTime) +{ + if (forceSoftwareAnimation() || valueList.size() < 2) + return false; + + // if there is already is an animation for this property and it hasn't changed, ignore it. + int i = findAnimationEntry(property, 0); + if (i >= 0 && *m_animations[i].animation() == *animation) { + m_animations[i].setIsCurrent(); + return false; + } + + if (valueList.size() == 2) { + float fromVal = valueList.at(0).value(); + float toVal = valueList.at(1).value(); + if (isnan(toVal) && isnan(fromVal)) + return false; + + // initialize the property to 0 + [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; + setBasicAnimation(property, TransformOperation::NONE, 0, isnan(fromVal) ? nil : [NSNumber numberWithFloat:fromVal], isnan(toVal) ? nil : [NSNumber numberWithFloat:toVal], false, animation, beginTime); + return true; + } + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + NSMutableArray* timesArray = [[NSMutableArray alloc] init]; + NSMutableArray* valArray = [[NSMutableArray alloc] init]; + NSMutableArray* tfArray = [[NSMutableArray alloc] init]; + + for (unsigned i = 0; i < valueList.values().size(); ++i) { + const FloatValue& curValue = valueList.values()[i]; + [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; + [valArray addObject:[NSNumber numberWithFloat:curValue.value()]]; + + const TimingFunction* tf = 0; + if (curValue.timingFunction()) + tf = curValue.timingFunction(); + else if (animation->isTimingFunctionSet()) + tf = &animation->timingFunction(); + + CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); + [tfArray addObject:timingFunction]; + } + + // We toss the last tfArray value because it has to one shorter than the others. + [tfArray removeLastObject]; + + // Initialize the property to 0. + [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; + // Then set the animation. + setKeyframeAnimation(property, TransformOperation::NONE, 0, timesArray, valArray, tfArray, false, animation, beginTime); + + [timesArray release]; + [valArray release]; + [tfArray release]; + + END_BLOCK_OBJC_EXCEPTIONS + return true; +} + +void GraphicsLayerCA::setContentsToImage(Image* image) +{ + if (image) { + setHasContentsLayer(true); + + // FIXME: is image flipping really a property of the graphics context? + bool needToFlip = GraphicsLayer::graphicsContextsFlipped(); + CGPoint anchorPoint = needToFlip ? CGPointMake(0.0f, 1.0f) : CGPointZero; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + { + CGImageRef theImage = image->nativeImageForCurrentFrame(); + // FIXME: maybe only do trilinear if the image is being scaled down, + // but then what if the layer size changes? +#if HAVE_MODERN_QUARTZCORE + [m_contentsLayer.get() setMinificationFilter:kCAFilterTrilinear]; +#endif + if (needToFlip) { + CATransform3D flipper = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + [m_contentsLayer.get() setTransform:flipper]; + } + + [m_contentsLayer.get() setAnchorPoint:anchorPoint]; + [m_contentsLayer.get() setContents:(id)theImage]; + } + END_BLOCK_OBJC_EXCEPTIONS + } else + setHasContentsLayer(false); + + m_contentLayerForImageOrVideo = (image != 0); +} + +void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) +{ + setContentsLayer(videoLayer); + m_contentLayerForImageOrVideo = (videoLayer != 0); +} + +void GraphicsLayerCA::clearContents() +{ + if (m_contentLayerForImageOrVideo) { + setHasContentsLayer(false); + m_contentLayerForImageOrVideo = false; + } +} + +void GraphicsLayerCA::updateContentsRect() +{ + if (m_client && m_contentsLayer) { + IntRect contentRect = m_client->contentsBox(this); + + CGPoint point = CGPointMake(contentRect.x(), + contentRect.y()); + CGRect rect = CGRectMake(0.0f, + 0.0f, + contentRect.width(), + contentRect.height()); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_contentsLayer.get() setPosition:point]; + [m_contentsLayer.get() setBounds:rect]; + END_BLOCK_OBJC_EXCEPTIONS + } +} + +void GraphicsLayerCA::setBasicAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation* transition, double beginTime) +{ + ASSERT(fromVal || toVal); + + WebLayer* layer = animatedLayer(property); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // add an entry for this animation + addAnimationEntry(property, index, isTransition, transition); + + String keyPath = propertyIdToString(property); + String animName = keyPath + "_" + String::number(index); + + CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:keyPath]; + + double duration = transition->duration(); + if (duration <= 0) + duration = cAnimationAlmostZeroDuration; + + float repeatCount = transition->iterationCount(); + if (repeatCount == Animation::IterationCountInfinite) + repeatCount = FLT_MAX; + else if (transition->direction() == Animation::AnimationDirectionAlternate) + repeatCount /= 2; + + [basicAnim setDuration:duration]; + [basicAnim setRepeatCount:repeatCount]; + [basicAnim setAutoreverses:transition->direction()]; + [basicAnim setRemovedOnCompletion:NO]; + + // Note that currently transform is the only property which has animations + // with an index > 0. + [basicAnim setAdditive:property == AnimatedPropertyWebkitTransform]; + [basicAnim setFillMode:@"extended"]; +#if HAVE_MODERN_QUARTZCORE + if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) + [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; +#else + UNUSED_PARAM(operationType); +#endif + + // Set the delegate (and property value). + int prop = isTransition ? property : AnimatedPropertyInvalid; + [basicAnim setValue:[NSNumber numberWithInt:prop] forKey:WebAnimationCSSPropertyKey]; + [basicAnim setDelegate:m_animationDelegate.get()]; + + NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; + [basicAnim setBeginTime:bt]; + + if (fromVal) + [basicAnim setFromValue:reinterpret_cast<id>(fromVal)]; + if (toVal) + [basicAnim setToValue:reinterpret_cast<id>(toVal)]; + + const TimingFunction* tf = 0; + if (transition->isTimingFunctionSet()) + tf = &transition->timingFunction(); + + CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); + [basicAnim setTimingFunction:timingFunction]; + + // Send over the animation. + [layer removeAnimationForKey:animName]; + [layer addAnimation:basicAnim forKey:animName]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setKeyframeAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* keys, void* values, void* timingFunctions, + bool isTransition, const Animation* anim, double beginTime) +{ + PlatformLayer* layer = animatedLayer(property); + + // Add an entry for this animation (which may change beginTime). + addAnimationEntry(property, index, isTransition, anim); + + String keyPath = propertyIdToString(property); + String animName = keyPath + "_" + String::number(index); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:keyPath]; + + double duration = anim->duration(); + if (duration <= 0) + duration = cAnimationAlmostZeroDuration; + + float repeatCount = anim->iterationCount(); + if (repeatCount == Animation::IterationCountInfinite) + repeatCount = FLT_MAX; + else if (anim->direction() == Animation::AnimationDirectionAlternate) + repeatCount /= 2; + + [keyframeAnim setDuration:duration]; + [keyframeAnim setRepeatCount:repeatCount]; + [keyframeAnim setAutoreverses:anim->direction()]; + [keyframeAnim setRemovedOnCompletion:NO]; + + // The first animation is non-additive, all the rest are additive. + // Note that currently transform is the only property which has animations + // with an index > 0. + [keyframeAnim setAdditive:(property == AnimatedPropertyWebkitTransform) ? YES : NO]; + [keyframeAnim setFillMode:@"extended"]; +#if HAVE_MODERN_QUARTZCORE + if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) + [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; +#else + UNUSED_PARAM(operationType); +#endif + + [keyframeAnim setKeyTimes:reinterpret_cast<id>(keys)]; + [keyframeAnim setValues:reinterpret_cast<id>(values)]; + + // Set the delegate (and property value). + int prop = isTransition ? property : AnimatedPropertyInvalid; + [keyframeAnim setValue:[NSNumber numberWithInt: prop] forKey:WebAnimationCSSPropertyKey]; + [keyframeAnim setDelegate:m_animationDelegate.get()]; + + NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; + [keyframeAnim setBeginTime:bt]; + + // Set the timing functions, if any. + if (timingFunctions != nil) + [keyframeAnim setTimingFunctions:(id)timingFunctions]; + + // Send over the animation. + [layer removeAnimationForKey:animName]; + [layer addAnimation:keyframeAnim forKey:animName]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::suspendAnimations() +{ + double t = currentTimeToMediaTime(currentTime()); + [primaryLayer() setSpeed:0]; + [primaryLayer() setTimeOffset:t]; +} + +void GraphicsLayerCA::resumeAnimations() +{ + [primaryLayer() setSpeed:1]; + [primaryLayer() setTimeOffset:0]; +} + +void GraphicsLayerCA::removeAnimation(int index, bool reset) +{ + ASSERT(index >= 0); + + AnimatedPropertyID property = m_animations[index].property(); + + // Set the value of the property and remove the animation. + String keyPath = propertyIdToString(property); + String animName = keyPath + "_" + String::number(m_animations[index].index()); + CALayer* layer = animatedLayer(property); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // If we are not resetting, it means we are pausing. So we need to get the current presentation + // value into the property before we remove the animation. + if (!reset) { + // Put the current value into the property. + CALayer* presLayer = [layer presentationLayer]; + if (presLayer) + [layer setValue:[presLayer valueForKeyPath:keyPath] forKeyPath:keyPath]; + + // Make sure the saved values accurately reflect the value in the layer. + id val = [layer valueForKeyPath:keyPath]; + switch (property) { + case AnimatedPropertyWebkitTransform: + // FIXME: needs comment explaining why the m_transform is not obtained from the layer + break; + case AnimatedPropertyBackgroundColor: + m_backgroundColor = Color(reinterpret_cast<CGColorRef>(val)); + break; + case AnimatedPropertyOpacity: + m_opacity = [val floatValue]; + break; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + break; + } + } + + // If we have reached the end of an animation, we don't want to actually remove the + // animation from the CALayer. At some point we will be setting the property to its + // unanimated value and at that point we will remove the animation. That will avoid + // any flashing between the time the animation is removed and the property is set. + if (!reset || m_animations[index].isTransition()) + [layer removeAnimationForKey:animName]; + + END_BLOCK_OBJC_EXCEPTIONS + + // Remove the animation entry. + m_animations.remove(index); +} + +PlatformLayer* GraphicsLayerCA::hostLayerForSublayers() const +{ + return m_transformLayer ? m_transformLayer.get() : m_layer.get(); +} + +PlatformLayer* GraphicsLayerCA::layerForSuperlayer() const +{ + if (m_transformLayer) + return m_transformLayer.get(); + + return m_layer.get(); +} + +PlatformLayer* GraphicsLayerCA::platformLayer() const +{ + return primaryLayer(); +} + +#ifndef NDEBUG +void GraphicsLayerCA::setDebugBackgroundColor(const Color& color) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (color.isValid()) + setLayerBackgroundColor(m_layer.get(), color); + else + clearLayerBackgroundColor(m_layer.get()); + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (color.isValid()) { + setLayerBorderColor(m_layer.get(), color); + [m_layer.get() setBorderWidth:borderWidth]; + } else { + clearBorderColor(m_layer.get()); + [m_layer.get() setBorderWidth:0]; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setZPosition(float position) +{ + GraphicsLayer::setZPosition(position); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [primaryLayer() setZPosition:position]; + END_BLOCK_OBJC_EXCEPTIONS +} +#endif + +bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const +{ + if (!m_drawsContent) + return false; + + // FIXME: catch zero-size height or width here (or earlier)? + return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; +} + +void GraphicsLayerCA::swapFromOrToTiledLayer(bool userTiledLayer) +{ + if (userTiledLayer == m_usingTiledLayer) + return; + + CGSize tileSize = CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + RetainPtr<CALayer> oldLayer = m_layer.get(); + + Class layerClass = userTiledLayer ? [WebTiledLayer self] : [WebLayer self]; + m_layer.adoptNS([[layerClass alloc] init]); + + if (userTiledLayer) { + WebTiledLayer* tiledLayer = (WebTiledLayer*)m_layer.get(); + [tiledLayer setTileSize:tileSize]; + [tiledLayer setLevelsOfDetail:1]; + [tiledLayer setLevelsOfDetailBias:0]; + + if (GraphicsLayer::graphicsContextsFlipped()) + [tiledLayer setContentsGravity:@"bottomLeft"]; + else + [tiledLayer setContentsGravity:@"topLeft"]; + } + + [m_layer.get() setLayerOwner:this]; + [m_layer.get() setSublayers:[oldLayer.get() sublayers]]; + + [[oldLayer.get() superlayer] replaceSublayer:oldLayer.get() with:m_layer.get()]; + + [m_layer.get() setBounds:[oldLayer.get() bounds]]; + [m_layer.get() setPosition:[oldLayer.get() position]]; + [m_layer.get() setAnchorPoint:[oldLayer.get() anchorPoint]]; + [m_layer.get() setOpaque:[oldLayer.get() isOpaque]]; + [m_layer.get() setOpacity:[oldLayer.get() opacity]]; + [m_layer.get() setTransform:[oldLayer.get() transform]]; + [m_layer.get() setSublayerTransform:[oldLayer.get() sublayerTransform]]; + [m_layer.get() setDoubleSided:[oldLayer.get() isDoubleSided]]; +#ifndef NDEBUG + [m_layer.get() setZPosition:[oldLayer.get() zPosition]]; +#endif + +#ifndef NDEBUG + String name = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + m_name; + [m_layer.get() setName:name]; +#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()); + + // need to tell new layer to draw itself + setNeedsDisplay(); + + END_BLOCK_OBJC_EXCEPTIONS + + m_usingTiledLayer = userTiledLayer; + +#ifndef NDEBUG + updateDebugIndicators(); +#endif +} + +void GraphicsLayerCA::setHasContentsLayer(bool hasLayer) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (hasLayer && !m_contentsLayer) { + // create the inner layer + WebLayer* contentsLayer = [WebLayer layer]; +#ifndef NDEBUG + [contentsLayer setName:@"Contents Layer"]; +#endif + setContentsLayer(contentsLayer); + + } else if (!hasLayer && m_contentsLayer) + setContentsLayer(0); + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setContentsLayer(WebLayer* contentsLayer) +{ + if (contentsLayer == m_contentsLayer) + return; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (m_contentsLayer) { + [m_contentsLayer.get() removeFromSuperlayer]; + m_contentsLayer = 0; + } + + if (contentsLayer) { + // Turn off implicit animations on the inner layer. + [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + + m_contentsLayer.adoptNS([contentsLayer retain]); + [m_contentsLayer.get() setAnchorPoint:CGPointZero]; + [m_layer.get() addSublayer:m_contentsLayer.get()]; + + updateContentsRect(); + + // Set contents to nil if the layer does not draw its own content. + if (m_client && !drawsContent()) + [m_layer.get() setContents:nil]; + +#ifndef NDEBUG + if (showDebugBorders()) { + setLayerBorderColor(m_contentsLayer.get(), Color(0, 0, 128, 180)); + [m_contentsLayer.get() setBorderWidth:1.0f]; + } +#endif + } +#ifndef NDEBUG + updateDebugIndicators(); +#endif + + END_BLOCK_OBJC_EXCEPTIONS +} + +} // namespace WebCore + + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index 3f18ab4..677c31a 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,7 +28,7 @@ #if ENABLE(VIDEO) -#include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" #include "Timer.h" #include <wtf/RetainPtr.h> @@ -52,11 +52,28 @@ class WebCoreMovieObserver; namespace WebCore { -class MediaPlayerPrivate : Noncopyable { +class MediaPlayerPrivate : public MediaPlayerPrivateInterface { public: - MediaPlayerPrivate(MediaPlayer*); + static void registerMediaEngine(MediaEngineRegistrar); + ~MediaPlayerPrivate(); - + + void repaint(); + void loadStateChanged(); + void rateChanged(); + void sizeChanged(); + void timeChanged(); + void didEnd(); + +private: + MediaPlayerPrivate(MediaPlayer*); + + // engine support + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + static void getSupportedTypes(HashSet<String>& types); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + IntSize naturalSize() const; bool hasVideo() const; @@ -72,11 +89,12 @@ public: float duration() const; float currentTime() const; void seek(float time); - void setEndTime(float time); void setRate(float); void setVolume(float); - + + void setEndTime(float time); + int dataRate() const; MediaPlayer::NetworkState networkState() const { return m_networkState; } @@ -89,21 +107,10 @@ public: unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect& r); - - void loadStateChanged(); - void rateChanged(); - void sizeChanged(); - void timeChanged(); - void didEnd(); + void setSize(const IntSize&); - void repaint(); void paint(GraphicsContext*, const IntRect&); - - static void getSupportedTypes(HashSet<String>& types); - static bool isAvailable(); - -private: + void createQTMovie(const String& url); void setUpVideoRendering(); void tearDownVideoRendering(); @@ -117,10 +124,10 @@ private: void doSeek(); void cancelSeek(); void seekTimerFired(Timer<MediaPlayerPrivate>*); - void endPointTimerFired(Timer<MediaPlayerPrivate>*); float maxTimeLoaded() const; - void startEndPointTimerIfNeeded(); - void disableUnsupportedTracks(unsigned& enabledTrackCount); + void disableUnsupportedTracks(); + + bool metaDataAvailable() const { return m_qtMovie && m_readyState >= MediaPlayer::HaveMetadata; } MediaPlayer* m_player; RetainPtr<QTMovie> m_qtMovie; @@ -128,14 +135,15 @@ private: RetainPtr<QTVideoRendererWebKitOnly> m_qtVideoRenderer; RetainPtr<WebCoreMovieObserver> m_objcObserver; float m_seekTo; - float m_endTime; Timer<MediaPlayerPrivate> m_seekTimer; - Timer<MediaPlayerPrivate> m_endPointTimer; MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; bool m_startedPlaying; bool m_isStreaming; bool m_visible; + IntRect m_rect; + unsigned m_enabledTrackCount; + float m_duration; #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 a33c8d2..74a9ff9 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -69,6 +69,7 @@ SOFT_LINK_CLASS(QTKit, QTMovieView) SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *) +SOFT_LINK_POINTER(QTKit, QTMediaTypeMPEG, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *) @@ -95,6 +96,7 @@ SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, #define QTMediaTypeAttribute getQTMediaTypeAttribute() #define QTMediaTypeBase getQTMediaTypeBase() +#define QTMediaTypeMPEG getQTMediaTypeMPEG() #define QTMediaTypeSound getQTMediaTypeSound() #define QTMediaTypeText getQTMediaTypeText() #define QTMediaTypeVideo getQTMediaTypeVideo() @@ -155,24 +157,35 @@ using namespace std; namespace WebCore { -static const float endPointTimerInterval = 0.020f; - #ifdef BUILDING_ON_TIGER static const long minimumQuickTimeVersion = 0x07300000; // 7.3 #endif + +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this]) , m_seekTo(-1) - , m_endTime(numeric_limits<float>::infinity()) , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) - , m_endPointTimer(this, &MediaPlayerPrivate::endPointTimerFired) , m_networkState(MediaPlayer::Empty) - , m_readyState(MediaPlayer::DataUnavailable) + , m_readyState(MediaPlayer::HaveNothing) , m_startedPlaying(false) , m_isStreaming(false) , m_visible(false) + , m_rect() + , m_enabledTrackCount(0) + , m_duration(-1.0f) #if DRAW_FRAME_RATE , m_frameCountWhilePlaying(0) , m_timeStartedPlaying(0) @@ -270,7 +283,7 @@ void MediaPlayerPrivate::createQTMovieView() detachQTMovieView(); static bool addedCustomMethods = false; - if (!addedCustomMethods) { + if (!m_player->inMediaDocument() && !addedCustomMethods) { Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView"); ASSERT(QTMovieContentViewClass); @@ -281,9 +294,12 @@ void MediaPlayerPrivate::createQTMovieView() addedCustomMethods = true; } + // delay callbacks as we *will* get notifications during setup + [m_objcObserver.get() setDelayCallbacks:YES]; + m_qtMovieView.adoptNS([[QTMovieView alloc] init]); - setRect(m_player->rect()); - NSView* parentView = m_player->m_frameView->documentView(); + setSize(m_player->size()); + NSView* parentView = m_player->frameView()->documentView(); [parentView addSubview:m_qtMovieView.get()]; #ifdef BUILDING_ON_TIGER // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy @@ -303,6 +319,8 @@ void MediaPlayerPrivate::createQTMovieView() // Note that we expect mainThreadSetNeedsDisplay to be invoked only when synchronous drawing is requested. if (!m_player->inMediaDocument()) wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES); + + [m_objcObserver.get() setDelayCallbacks:NO]; } void MediaPlayerPrivate::detachQTMovieView() @@ -356,7 +374,7 @@ void MediaPlayerPrivate::destroyQTVideoRenderer() void MediaPlayerPrivate::setUpVideoRendering() { - if (!m_player->m_frameView || !m_qtMovie) + if (!m_player->frameView() || !m_qtMovie) return; if (m_player->inMediaDocument() || !QTVideoRendererClass() ) @@ -375,7 +393,7 @@ void MediaPlayerPrivate::tearDownVideoRendering() QTTime MediaPlayerPrivate::createQTTime(float time) const { - if (!m_qtMovie) + if (!metaDataAvailable()) return QTMakeTime(0, 600); long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue]; return QTMakeTime(time * timeScale, timeScale); @@ -387,12 +405,11 @@ void MediaPlayerPrivate::load(const String& url) m_networkState = MediaPlayer::Loading; m_player->networkStateChanged(); } - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } cancelSeek(); - m_endPointTimer.stop(); [m_objcObserver.get() setDelayCallbacks:YES]; @@ -404,7 +421,7 @@ void MediaPlayerPrivate::load(const String& url) void MediaPlayerPrivate::play() { - if (!m_qtMovie) + if (!metaDataAvailable()) return; m_startedPlaying = true; #if DRAW_FRAME_RATE @@ -413,12 +430,11 @@ void MediaPlayerPrivate::play() [m_objcObserver.get() setDelayCallbacks:YES]; [m_qtMovie.get() setRate:m_player->rate()]; [m_objcObserver.get() setDelayCallbacks:NO]; - startEndPointTimerIfNeeded(); } void MediaPlayerPrivate::pause() { - if (!m_qtMovie) + if (!metaDataAvailable()) return; m_startedPlaying = false; #if DRAW_FRAME_RATE @@ -427,12 +443,11 @@ void MediaPlayerPrivate::pause() [m_objcObserver.get() setDelayCallbacks:YES]; [m_qtMovie.get() stop]; [m_objcObserver.get() setDelayCallbacks:NO]; - m_endPointTimer.stop(); } float MediaPlayerPrivate::duration() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; QTTime time = [m_qtMovie.get() duration]; if (time.flags == kQTTimeIsIndefinite) @@ -442,22 +457,22 @@ float MediaPlayerPrivate::duration() const float MediaPlayerPrivate::currentTime() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; QTTime time = [m_qtMovie.get() currentTime]; - return min(static_cast<float>(time.timeValue) / time.timeScale, m_endTime); + return static_cast<float>(time.timeValue) / time.timeScale; } void MediaPlayerPrivate::seek(float time) { cancelSeek(); - if (!m_qtMovie) + if (!metaDataAvailable()) return; if (time > duration()) time = duration(); - + m_seekTo = time; if (maxTimeLoaded() >= m_seekTo) doSeek(); @@ -475,7 +490,7 @@ void MediaPlayerPrivate::doSeek() [m_qtMovie.get() setCurrentTime:qttime]; float timeAfterSeek = currentTime(); // restore playback only if not at end, othewise QTMovie will loop - if (timeAfterSeek < duration() && timeAfterSeek < m_endTime) + if (oldRate && timeAfterSeek < duration()) [m_qtMovie.get() setRate:oldRate]; cancelSeek(); [m_objcObserver.get() setDelayCallbacks:NO]; @@ -489,7 +504,7 @@ void MediaPlayerPrivate::cancelSeek() void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) { - if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) { + if (!metaDataAvailable()|| !seeking() || currentTime() == m_seekTo) { cancelSeek(); updateStates(); m_player->timeChanged(); @@ -508,67 +523,48 @@ void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) } } -void MediaPlayerPrivate::setEndTime(float time) +void MediaPlayerPrivate::setEndTime(float) { - m_endTime = time; - startEndPointTimerIfNeeded(); -} - -void MediaPlayerPrivate::startEndPointTimerIfNeeded() -{ - if (m_endTime < duration() && m_startedPlaying && !m_endPointTimer.isActive()) - m_endPointTimer.startRepeating(endPointTimerInterval); -} - -void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*) -{ - float time = currentTime(); - - // just do end for now - if (time >= m_endTime) { - pause(); - didEnd(); - } } bool MediaPlayerPrivate::paused() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return true; return [m_qtMovie.get() rate] == 0; } bool MediaPlayerPrivate::seeking() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return false; return m_seekTo >= 0; } IntSize MediaPlayerPrivate::naturalSize() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return IntSize(); return IntSize([[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]); } bool MediaPlayerPrivate::hasVideo() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return false; return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue]; } void MediaPlayerPrivate::setVolume(float volume) { - if (!m_qtMovie) + if (!metaDataAvailable()) return; [m_qtMovie.get() setVolume:volume]; } void MediaPlayerPrivate::setRate(float rate) { - if (!m_qtMovie) + if (!metaDataAvailable()) return; if (!paused()) [m_qtMovie.get() setRate:rate]; @@ -576,7 +572,7 @@ void MediaPlayerPrivate::setRate(float rate) int MediaPlayerPrivate::dataRate() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; return wkQTMovieDataRate(m_qtMovie.get()); } @@ -596,7 +592,7 @@ float MediaPlayerPrivate::maxTimeSeekable() const float MediaPlayerPrivate::maxTimeLoaded() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; return wkQTMovieMaxTimeLoaded(m_qtMovie.get()); } @@ -616,7 +612,7 @@ bool MediaPlayerPrivate::totalBytesKnown() const unsigned MediaPlayerPrivate::totalBytes() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue]; } @@ -639,52 +635,66 @@ void MediaPlayerPrivate::updateStates() MediaPlayer::ReadyState oldReadyState = m_readyState; long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError); - - if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData && !m_player->inMediaDocument()) { - unsigned enabledTrackCount; - disableUnsupportedTracks(enabledTrackCount); - // FIXME: We should differentiate between load errors and decode errors <rdar://problem/5605692> - if (!enabledTrackCount) + + if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata && !m_player->inMediaDocument()) { + disableUnsupportedTracks(); + if (!m_enabledTrackCount) loadState = QTMovieLoadStateError; } - // "Loaded" is reserved for fully buffered movies, never the case when streaming if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { - if (m_networkState < MediaPlayer::Loaded) - m_networkState = MediaPlayer::Loaded; - m_readyState = MediaPlayer::CanPlayThrough; + // "Loaded" is reserved for fully buffered movies, never the case when streaming + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::HaveEnoughData; } else if (loadState >= QTMovieLoadStatePlaythroughOK) { - if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) - m_networkState = MediaPlayer::LoadedFirstFrame; - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveFutureData; + m_networkState = MediaPlayer::Loading; } else if (loadState >= QTMovieLoadStatePlayable) { - if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) - m_networkState = MediaPlayer::LoadedFirstFrame; // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> - m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::CanPlay : MediaPlayer::DataUnavailable; + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; + m_networkState = MediaPlayer::Loading; } else if (loadState >= QTMovieLoadStateLoaded) { - if (m_networkState < MediaPlayer::LoadedMetaData) - m_networkState = MediaPlayer::LoadedMetaData; - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveMetadata; + m_networkState = MediaPlayer::Loading; } else if (loadState > QTMovieLoadStateError) { - if (m_networkState < MediaPlayer::Loading) - m_networkState = MediaPlayer::Loading; - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveNothing; + m_networkState = MediaPlayer::Loading; } else { - m_networkState = MediaPlayer::LoadFailed; - m_readyState = MediaPlayer::DataUnavailable; + float loaded = maxTimeLoaded(); + + if (!loaded) + m_readyState = MediaPlayer::HaveNothing; + + if (!m_enabledTrackCount) + m_networkState = MediaPlayer::FormatError; + else { + // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> + if (loaded > 0) + m_networkState = MediaPlayer::DecodeError; + else + m_readyState = MediaPlayer::HaveNothing; + } } if (seeking()) - m_readyState = MediaPlayer::DataUnavailable; - + m_readyState = MediaPlayer::HaveNothing; + if (m_networkState != oldNetworkState) m_player->networkStateChanged(); if (m_readyState != oldReadyState) m_player->readyStateChanged(); - if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::LoadedMetaData && m_player->visible()) + if (loadState >= QTMovieLoadStateLoaded && oldReadyState < MediaPlayer::HaveMetadata && m_player->visible()) setUpVideoRendering(); + + if (loadState >= QTMovieLoadStateLoaded) { + float dur = duration(); + if (dur != m_duration) { + if (m_duration != -1.0f) + m_player->durationChanged(); + m_duration = dur; + } + } } void MediaPlayerPrivate::loadStateChanged() @@ -695,10 +705,12 @@ void MediaPlayerPrivate::loadStateChanged() void MediaPlayerPrivate::rateChanged() { updateStates(); + m_player->rateChanged(); } void MediaPlayerPrivate::sizeChanged() { + m_player->sizeChanged(); } void MediaPlayerPrivate::timeChanged() @@ -709,7 +721,6 @@ void MediaPlayerPrivate::timeChanged() void MediaPlayerPrivate::didEnd() { - m_endPointTimer.stop(); m_startedPlaying = false; #if DRAW_FRAME_RATE m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; @@ -718,18 +729,19 @@ void MediaPlayerPrivate::didEnd() m_player->timeChanged(); } -void MediaPlayerPrivate::setRect(const IntRect& r) +void MediaPlayerPrivate::setSize(const IntSize& size) { if (!m_qtMovieView) return; + m_rect.setSize(size); if (m_player->inMediaDocument()) // We need the QTMovieView to be placed in the proper location for document mode. - [m_qtMovieView.get() setFrame:r]; + [m_qtMovieView.get() setFrame:m_rect]; else { // We don't really need the QTMovieView in any specific location so let's just get it out of the way // where it won't intercept events or try to bring up the context menu. - IntRect farAwayButCorrectSize(r); + IntRect farAwayButCorrectSize(m_rect); farAwayButCorrectSize.move(-1000000, -1000000); [m_qtMovieView.get() setFrame:farAwayButCorrectSize]; } @@ -740,7 +752,7 @@ void MediaPlayerPrivate::setVisible(bool b) if (m_visible != b) { m_visible = b; if (b) { - if (m_networkState >= MediaPlayer::LoadedMetaData) + if (m_readyState >= MediaPlayer::HaveMetadata) setUpVideoRendering(); } else tearDownVideoRendering(); @@ -789,13 +801,19 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) [NSGraphicsContext setCurrentContext:newContext]; [(id<WebKitVideoRenderingDetails>)qtVideoRenderer drawInRect:paintRect]; [NSGraphicsContext restoreGraphicsState]; - } else + } else { + if (m_player->inMediaDocument() && r != m_rect) { + // the QTMovieView needs to be placed in the proper location for document mode + m_rect = r; + [view setFrame:m_rect]; + } [view displayRectIgnoringOpacity:paintRect inContext:newContext]; + } #if DRAW_FRAME_RATE // Draw the frame rate only after having played more than 10 frames. if (m_frameCountWhilePlaying > 10) { - Frame* frame = m_player->m_frameView ? m_player->m_frameView->frame() : NULL; + 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; @@ -821,22 +839,42 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) [m_objcObserver.get() setDelayCallbacks:NO]; } -void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +static HashSet<String> mimeTypeCache() { - NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes]; - int count = [fileTypes count]; - for (int n = 0; n < count; n++) { - CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]); - RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL)); - if (!uti) - continue; - RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType)); - if (!mime) - continue; - types.add(mime.get()); + DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); + static bool typeListInitialized = false; + + if (!typeListInitialized) { + NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes]; + int count = [fileTypes count]; + for (int n = 0; n < count; n++) { + CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]); + RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL)); + if (!uti) + continue; + RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType)); + if (!mime) + continue; + cache.add(mime.get()); + } + typeListInitialized = true; } -} + return cache; +} + +void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +{ + types = mimeTypeCache(); +} + +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an + // extended MIME type yet + return mimeTypeCache().contains(type) ? (codecs && !codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; +} + bool MediaPlayerPrivate::isAvailable() { #ifdef BUILDING_ON_TIGER @@ -858,10 +896,10 @@ bool MediaPlayerPrivate::isAvailable() #endif } -void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) +void MediaPlayerPrivate::disableUnsupportedTracks() { if (!m_qtMovie) { - enabledTrackCount = 0; + m_enabledTrackCount = 0; return; } @@ -872,6 +910,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) allowedTrackTypes->add(QTMediaTypeSound); allowedTrackTypes->add(QTMediaTypeText); allowedTrackTypes->add(QTMediaTypeBase); + allowedTrackTypes->add(QTMediaTypeMPEG); allowedTrackTypes->add("clcp"); allowedTrackTypes->add("sbtl"); } @@ -879,7 +918,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) NSArray *tracks = [m_qtMovie.get() tracks]; unsigned trackCount = [tracks count]; - enabledTrackCount = trackCount; + m_enabledTrackCount = trackCount; for (unsigned trackIndex = 0; trackIndex < trackCount; trackIndex++) { // Grab the track at the current index. If there isn't one there, then // we can move onto the next one. @@ -907,7 +946,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) if (!allowedTrackTypes->contains(mediaType)) { // If this track type is not allowed, then we need to disable it. [track setEnabled:NO]; - --enabledTrackCount; + --m_enabledTrackCount; } // Disable chapter tracks. These are most likely to lead to trouble, as @@ -939,7 +978,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) // Disable the evil, evil track. [chapterTrack setEnabled:NO]; - --enabledTrackCount; + --m_enabledTrackCount; } } @@ -947,7 +986,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) @implementation WebCoreMovieObserver -- (id)initWithCallback:(MediaPlayerPrivate *)callback +- (id)initWithCallback:(MediaPlayerPrivate*)callback { m_callback = callback; return [super init]; diff --git a/WebCore/platform/graphics/mac/MediaPlayerProxy.h b/WebCore/platform/graphics/mac/MediaPlayerProxy.h new file mode 100644 index 0000000..6060484 --- /dev/null +++ b/WebCore/platform/graphics/mac/MediaPlayerProxy.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlayerProxy_h +#define MediaPlayerProxy_h + +#ifdef __OBJC__ +@class WebMediaPlayerProxy; +#else +class WebMediaPlayerProxy; +#endif + +enum MediaPlayerProxyNotificationType { + + MediaPlayerNotificationMediaValidated = 1, + MediaPlayerNotificationMediaFailedToValidate, + + MediaPlayerNotificationStartUsingNetwork, + MediaPlayerNotificationStopUsingNetwork, + + MediaPlayerNotificationEnteredFullScreen, + MediaPlayerNotificationExitedFullScreen, + + MediaPlayerNotificationReadyForInspection, + MediaPlayerNotificationReadyForPlayback, + MediaPlayerNotificationDidPlayToTheEnd, + + MediaPlayerNotificationPlaybackFailed, + + MediaPlayerNotificationStreamLikelyToKeepUp, + MediaPlayerNotificationStreamUnlikelyToKeepUp, + MediaPlayerNotificationStreamBufferFull, + MediaPlayerNotificationStreamRanDry, + MediaPlayerNotificationFileLoaded, + + MediaPlayerNotificationSizeDidChange, + MediaPlayerNotificationVolumeDidChange, + MediaPlayerNotificationMutedDidChange, + MediaPlayerNotificationTimeJumped, + + MediaPlayerNotificationPlayPauseButtonPressed, +}; + +#ifdef __OBJC__ +@interface NSObject (WebMediaPlayerProxy) + +- (int)_interfaceVersion; + +- (void)_disconnect; + +- (void)_load:(NSURL *)url; +- (void)_cancelLoad; + +- (void)_setPoster:(NSURL *)url; + +- (void)_play; +- (void)_pause; + +- (NSSize)_naturalSize; + +- (BOOL)_hasVideo; +- (BOOL)_hasAudio; + +- (NSTimeInterval)_duration; + +- (double)_currentTime; +- (void)_setCurrentTime:(double)time; +- (BOOL)_seeking; + +- (void)_setEndTime:(double)time; + +- (float)_rate; +- (void)_setRate:(float)rate; + +- (float)_volume; +- (void)_setVolume:(float)newVolume; + +- (BOOL)_muted; +- (void)_setMuted:(BOOL)muted; + +- (float)_maxTimeBuffered; +- (float)_maxTimeSeekable; +- (NSArray *)_bufferedTimeRanges; + +- (int)_dataRate; + +- (BOOL)_totalBytesKnown; +- (unsigned)_totalBytes; +- (unsigned)_bytesLoaded; + +- (NSArray *)_mimeTypes; + +@end +#endif + +#endif diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index 30dbf97..a3c10fa 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -38,6 +38,7 @@ #import "FontDescription.h" #import "SharedBuffer.h" #import "WebCoreSystemInterface.h" +#import <AppKit/AppKit.h> #import <ApplicationServices/ApplicationServices.h> #import <float.h> #import <unicode/uchar.h> diff --git a/WebCore/platform/graphics/mac/WebLayer.h b/WebCore/platform/graphics/mac/WebLayer.h new file mode 100644 index 0000000..b8b46ed --- /dev/null +++ b/WebCore/platform/graphics/mac/WebLayer.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebLayer_h +#define WebLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#import <QuartzCore/QuartzCore.h> + +namespace WebCore { + class GraphicsLayer; +} + +// Category implemented by WebLayer and WebTiledLayer. +@interface CALayer(WebLayerAdditions) + +- (void)setLayerOwner:(WebCore::GraphicsLayer*)layer; +- (WebCore::GraphicsLayer*)layerOwner; + +@end + +@interface WebLayer : CALayer +{ + WebCore::GraphicsLayer* m_layerOwner; +} + +// Class method allows us to share implementation across TiledLayerMac and WebLayer ++ (void)drawContents:(WebCore::GraphicsLayer*)layerContents ofLayer:(CALayer*)layer intoContext:(CGContextRef)context; + +@end + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebLayer_h diff --git a/WebCore/platform/graphics/mac/WebLayer.mm b/WebCore/platform/graphics/mac/WebLayer.mm new file mode 100644 index 0000000..267b5bc --- /dev/null +++ b/WebCore/platform/graphics/mac/WebLayer.mm @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebLayer.h" + +#import "GraphicsContext.h" +#import "GraphicsLayer.h" +#import <QuartzCore/QuartzCore.h> +#import "WebCoreTextRenderer.h" +#import <wtf/UnusedParam.h> + +using namespace WebCore; + +@implementation WebLayer + ++ (void)drawContents:(WebCore::GraphicsLayer*)layerContents ofLayer:(CALayer*)layer intoContext:(CGContextRef)context +{ + UNUSED_PARAM(layer); + CGContextSaveGState(context); + + if (layerContents && layerContents->client()) { + [NSGraphicsContext saveGraphicsState]; + + // Set up an NSGraphicsContext for the context, so that parts of AppKit that rely on + // the current NSGraphicsContext (e.g. NSCell drawing) get the right one. + NSGraphicsContext* layerContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]; + [NSGraphicsContext setCurrentContext:layerContext]; + + GraphicsContext graphicsContext(context); + + // It's important to get the clip from the context, because it may be significantly + // smaller than the layer bounds (e.g. tiled layers) + CGRect clipBounds = CGContextGetClipBoundingBox(context); + IntRect clip(enclosingIntRect(clipBounds)); + layerContents->paintGraphicsLayerContents(graphicsContext, clip); + + [NSGraphicsContext restoreGraphicsState]; + } +#ifndef NDEBUG + else { + ASSERT_NOT_REACHED(); + + // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color, + // so CA never makes backing store for it (which is what -setNeedsDisplay will do above). + CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f); + CGRect aBounds = [layer bounds]; + CGContextFillRect(context, aBounds); + } +#endif + + CGContextRestoreGState(context); + +#ifndef NDEBUG + if (layerContents && layerContents->showRepaintCounter()) { + bool isTiledLayer = [layer isKindOfClass:[CATiledLayer class]]; + + char text[16]; // that's a lot of repaints + snprintf(text, sizeof(text), "%d", layerContents->incrementRepaintCount()); + + CGAffineTransform a = CGContextGetCTM(context); + + CGContextSaveGState(context); + if (isTiledLayer) + CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 0.8f); + else + CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); + + CGRect aBounds = [layer bounds]; + + aBounds.size.width = 10 + 12 * strlen(text); + aBounds.size.height = 25; + CGContextFillRect(context, aBounds); + + CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 1.0f); + + CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0f, -1.0f)); + CGContextSelectFont(context, "Helvetica", 25, kCGEncodingMacRoman); + CGContextShowTextAtPoint(context, aBounds.origin.x + 3.0f, aBounds.origin.y + 20.0f, text, strlen(text)); + + CGContextRestoreGState(context); + } +#endif +} + +// Disable default animations +- (id<CAAction>)actionForKey:(NSString *)key +{ + UNUSED_PARAM(key); + return nil; +} + +// Implement this so presentationLayer can get our custom attributes +- (id)initWithLayer:(id)layer +{ + if ((self = [super initWithLayer:layer])) + m_layerOwner = [(WebLayer*)layer layerOwner]; + + return self; +} + +- (void)setNeedsDisplay +{ + if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) + [super setNeedsDisplay]; +} + +- (void)setNeedsDisplayInRect:(CGRect)dirtyRect +{ + if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) { + [super setNeedsDisplayInRect:dirtyRect]; + +#ifndef NDEBUG + if (m_layerOwner->showRepaintCounter()) { + CGRect bounds = [self bounds]; + [super setNeedsDisplayInRect:CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25)]; + } +#endif + } +} + +- (void)drawInContext:(CGContextRef)context +{ + [WebLayer drawContents:m_layerOwner ofLayer:self intoContext:context]; +} + +@end // implementation WebLayer + +#pragma mark - + +@implementation WebLayer(WebLayerAdditions) + +- (void)setLayerOwner:(GraphicsLayer*)aLayer +{ + m_layerOwner = aLayer; +} + +- (GraphicsLayer*)layerOwner +{ + return m_layerOwner; +} + +@end + +#pragma mark - + +#ifndef NDEBUG + +@implementation CALayer(ExtendedDescription) + +- (NSString*)_descriptionWithPrefix:(NSString*)inPrefix +{ + CGRect aBounds = [self bounds]; + CGPoint aPos = [self position]; + CATransform3D t = [self transform]; + + NSString* selfString = [NSString stringWithFormat:@"%@<%@ 0x%08x> \"%@\" bounds(%.1f, %.1f, %.1f, %.1f) pos(%.1f, %.1f), sublayers=%d masking=%d", + inPrefix, + [self class], + self, + [self name], + aBounds.origin.x, aBounds.origin.y, aBounds.size.width, aBounds.size.height, + aPos.x, aPos.y, + [[self sublayers] count], + [self masksToBounds]]; + + NSMutableString* curDesc = [NSMutableString stringWithString:selfString]; + + if ([[self sublayers] count] > 0) + [curDesc appendString:@"\n"]; + + NSString* sublayerPrefix = [inPrefix stringByAppendingString:@"\t"]; + + NSEnumerator* sublayersEnum = [[self sublayers] objectEnumerator]; + CALayer* curLayer; + while ((curLayer = [sublayersEnum nextObject])) + [curDesc appendString:[curLayer _descriptionWithPrefix:sublayerPrefix]]; + + if ([[self sublayers] count] == 0) + [curDesc appendString:@"\n"]; + + return curDesc; +} + +- (NSString*)extendedDescription +{ + return [self _descriptionWithPrefix:@""]; +} + +@end // implementation WebLayer(ExtendedDescription) + +#endif // NDEBUG + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/mac/WebTiledLayer.h b/WebCore/platform/graphics/mac/WebTiledLayer.h new file mode 100644 index 0000000..1c9144d --- /dev/null +++ b/WebCore/platform/graphics/mac/WebTiledLayer.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebTiledLayer_h +#define WebTiledLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebLayer.h" + +@interface WebTiledLayer : CATiledLayer +{ + WebCore::GraphicsLayer* m_layerOwner; +} + +// implements WebLayerAdditions + +@end + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebTiledLayer_h + diff --git a/WebCore/platform/graphics/mac/WebTiledLayer.mm b/WebCore/platform/graphics/mac/WebTiledLayer.mm new file mode 100644 index 0000000..1dd00ba --- /dev/null +++ b/WebCore/platform/graphics/mac/WebTiledLayer.mm @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebTiledLayer.h" + +#import "GraphicsContext.h" +#import "GraphicsLayer.h" +#import <wtf/UnusedParam.h> + +using namespace WebCore; + +@implementation WebTiledLayer + +// Set a zero duration for the fade in of tiles ++ (CFTimeInterval)fadeDuration +{ + return 0; +} + +// Make sure that tiles are drawn on the main thread ++ (BOOL)shouldDrawOnMainThread +{ + return YES; +} + +// Disable default animations +- (id<CAAction>)actionForKey:(NSString *)key +{ + UNUSED_PARAM(key); + return nil; +} + +// Implement this so presentationLayer can get our custom attributes +- (id)initWithLayer:(id)layer +{ + if ((self = [super initWithLayer:layer])) + m_layerOwner = [(WebLayer*)layer layerOwner]; + + return self; +} + +- (void)setNeedsDisplay +{ + if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) + [super setNeedsDisplay]; +} + +- (void)setNeedsDisplayInRect:(CGRect)dirtyRect +{ + if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) { + [super setNeedsDisplayInRect:dirtyRect]; + +#ifndef NDEBUG + if (m_layerOwner->showRepaintCounter()) { + CGRect bounds = [self bounds]; + [super setNeedsDisplayInRect:CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25)]; + } +#endif + } +} + +- (void)drawInContext:(CGContextRef)ctx +{ + [WebLayer drawContents:m_layerOwner ofLayer:self intoContext:ctx]; +} + +@end // implementation WebTiledLayer + +#pragma mark - + +@implementation WebTiledLayer(LayerMacAdditions) + +- (void)setLayerOwner:(GraphicsLayer*)aLayer +{ + m_layerOwner = aLayer; +} + +- (GraphicsLayer*)layerOwner +{ + return m_layerOwner; +} + +@end + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/win/OpenTypeUtilities.cpp b/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp index 1951320..16c3c00 100644 --- a/WebCore/platform/graphics/win/OpenTypeUtilities.cpp +++ b/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp @@ -49,9 +49,9 @@ struct EOTPrefix { unsigned fontDataSize; unsigned version; unsigned flags; - UInt8 fontPANOSE[10]; - UInt8 charset; - UInt8 italic; + uint8_t fontPANOSE[10]; + uint8_t charset; + uint8_t italic; unsigned weight; unsigned short fsType; unsigned short magicNumber; @@ -69,6 +69,15 @@ struct TableDirectoryEntry { BigEndianULong length; }; +#if !PLATFORM(CG) +// Fixed type is not defined on non-CG platforms. |version| in sfntHeader +// and headTable and |fontRevision| in headTable are of Fixed, but they're +// not actually refered to anywhere. Therefore, we just have to match +// the size (4 bytes). For the definition of Fixed type, see +// http://developer.apple.com/documentation/mac/Legacy/GXEnvironment/GXEnvironment-356.html#HEADING356-6. +typedef int32_t Fixed; +#endif + struct sfntHeader { Fixed version; BigEndianUShort numTables; @@ -95,9 +104,9 @@ struct OS2Table { BigEndianUShort strikeoutSize; BigEndianUShort strikeoutPosition; BigEndianUShort familyClass; - UInt8 panose[10]; + uint8_t panose[10]; BigEndianULong unicodeRange[4]; - UInt8 vendID[4]; + uint8_t vendID[4]; BigEndianUShort fsSelection; BigEndianUShort firstCharIndex; BigEndianUShort lastCharIndex; @@ -152,7 +161,7 @@ struct nameTable { #pragma pack() -static void appendBigEndianStringToEOTHeader(Vector<UInt8, 512>& eotHeader, const BigEndianUShort* string, unsigned short length) +static void appendBigEndianStringToEOTHeader(Vector<uint8_t, 512>& eotHeader, const BigEndianUShort* string, unsigned short length) { size_t size = eotHeader.size(); eotHeader.resize(size + length + 2 * sizeof(unsigned short)); @@ -165,7 +174,7 @@ static void appendBigEndianStringToEOTHeader(Vector<UInt8, 512>& eotHeader, cons dst[i] = 0; } -bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength) +bool getEOTHeader(SharedBuffer* fontData, Vector<uint8_t, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength) { overlayDst = 0; overlaySrc = 0; @@ -311,7 +320,7 @@ bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& appendBigEndianStringToEOTHeader(eotHeader, fullName, fullNameLength); unsigned short padding = 0; - eotHeader.append(reinterpret_cast<UInt8*>(&padding), sizeof(padding)); + eotHeader.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding)); prefix->eotSize = eotHeader.size() + fontData->size(); diff --git a/WebCore/platform/graphics/win/OpenTypeUtilities.h b/WebCore/platform/graphics/opentype/OpenTypeUtilities.h index ab35551..a67ffc7 100644 --- a/WebCore/platform/graphics/win/OpenTypeUtilities.h +++ b/WebCore/platform/graphics/opentype/OpenTypeUtilities.h @@ -33,7 +33,7 @@ namespace WebCore { class SharedBuffer; -bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength); +bool getEOTHeader(SharedBuffer* fontData, Vector<uint8_t, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength); HANDLE renameAndActivateFont(SharedBuffer*, const String&); } // namespace WebCore diff --git a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp index ea51fe8..f0dd3ea 100644 --- a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp +++ b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp @@ -61,12 +61,14 @@ FontPlatformData::FontPlatformData(const QFont& font, bool bold) { } +#if ENABLE(SVG_FONTS) FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) : m_size(size) , m_bold(bold) , m_oblique(oblique) { } +#endif FontPlatformData::FontPlatformData() : m_size(0.0f) diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp index deeea99..9ed5915 100644 --- a/WebCore/platform/graphics/qt/FontQt.cpp +++ b/WebCore/platform/graphics/qt/FontQt.cpp @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) Copyright (C) 2008 Holger Hans Peter Freyther + Copyright (C) 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -24,11 +25,18 @@ #include "FontFallbackList.h" #include "FontSelector.h" +#include "Gradient.h" #include "GraphicsContext.h" -#include <QTextLayout> -#include <QPainter> -#include <QFontMetrics> +#include "Pattern.h" +#include "TransformationMatrix.h" + +#include <QBrush> #include <QFontInfo> +#include <QFontMetrics> +#include <QPainter> +#include <QPainterPath> +#include <QPen> +#include <QTextLayout> #include <qalgorithms.h> #include <qdebug.h> @@ -37,20 +45,27 @@ #if QT_VERSION >= 0x040400 namespace WebCore { -static QString qstring(const TextRun& run) +static const QString qstring(const TextRun& run) +{ + //We don't detach + return QString::fromRawData((const QChar *)run.characters(), run.length()); +} + +static const QString fixSpacing(const QString &string) { - QString string((QChar *)run.characters(), run.length()); - QChar *uc = string.data(); + //Only detach if we're actually changing something + QString possiblyDetached = string; for (int i = 0; i < string.length(); ++i) { - if (Font::treatAsSpace(uc[i].unicode())) - uc[i] = 0x20; - else if (Font::treatAsZeroWidthSpace(uc[i].unicode())) - uc[i] = 0x200c; + 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 string; + return possiblyDetached; } - static QTextLine setupLayout(QTextLayout* layout, const TextRun& style) { int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; @@ -72,10 +87,32 @@ void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const Float to = run.length(); QPainter *p = ctx->platformContext(); - Color color = ctx->fillColor(); - p->setPen(QColor(color)); - QString string = qstring(run); + if (ctx->textDrawingMode() & cTextFill) { + if (ctx->fillGradient()) { + QBrush brush(*ctx->fillGradient()->platformGradient()); + brush.setTransform(ctx->fillGradient()->gradientSpaceTransform()); + p->setPen(QPen(brush, 0)); + } else if (ctx->fillPattern()) { + TransformationMatrix affine; + p->setPen(QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0)); + } else + p->setPen(QColor(ctx->fillColor())); + } + + if (ctx->textDrawingMode() & cTextStroke) { + if (ctx->strokeGradient()) { + QBrush brush(*ctx->strokeGradient()->platformGradient()); + brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform()); + p->setPen(QPen(brush, ctx->strokeThickness())); + } else if (ctx->strokePattern()) { + TransformationMatrix affine; + p->setPen(QPen(QBrush(ctx->strokePattern()->createPlatformPattern(affine)), ctx->strokeThickness())); + } else + p->setPen(QPen(QColor(ctx->strokeColor()), ctx->strokeThickness())); + } + + const QString string = fixSpacing(qstring(run)); // text shadow IntSize shadowSize; @@ -137,14 +174,20 @@ void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const Float p->drawText(pt, string, flags, run.padding()); p->restore(); } - p->drawText(pt, string, flags, run.padding()); + if (ctx->textDrawingMode() & cTextStroke) { + QPainterPath path; + path.addText(pt, font(), string); + p->strokePath(path, p->pen()); + } + if (ctx->textDrawingMode() & cTextFill) + p->drawText(pt, string, flags, run.padding()); } float Font::floatWidthForComplexText(const TextRun& run) const { if (!run.length()) return 0; - QString string = qstring(run); + const QString string = fixSpacing(qstring(run)); QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); int w = int(line.naturalTextWidth()); @@ -157,7 +200,7 @@ float Font::floatWidthForComplexText(const TextRun& run) const int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool includePartialGlyphs) const { - QString string = qstring(run); + const QString string = fixSpacing(qstring(run)); QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); return line.xToCursor(position); @@ -165,7 +208,7 @@ 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 { - QString string = qstring(run); + const QString string = fixSpacing(qstring(run)); QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); diff --git a/WebCore/platform/graphics/qt/GradientQt.cpp b/WebCore/platform/graphics/qt/GradientQt.cpp index a0edf8d..1e71f58 100644 --- a/WebCore/platform/graphics/qt/GradientQt.cpp +++ b/WebCore/platform/graphics/qt/GradientQt.cpp @@ -28,9 +28,10 @@ #include "Gradient.h" #include "CSSParser.h" -#include "NotImplemented.h" +#include "GraphicsContext.h" #include <QGradient> +#include <QPainter> namespace WebCore { @@ -66,12 +67,24 @@ QGradient* Gradient::platformGradient() ++stopIterator; } + switch(m_spreadMethod) { + case SpreadMethodPad: + m_gradient->setSpread(QGradient::PadSpread); + break; + case SpreadMethodReflect: + m_gradient->setSpread(QGradient::ReflectSpread); + break; + case SpreadMethodRepeat: + m_gradient->setSpread(QGradient::RepeatSpread); + break; + } + return m_gradient; } void Gradient::fill(GraphicsContext* context, const FloatRect& rect) { - notImplemented(); + context->platformContext()->fillRect(rect, *platformGradient()); } } //namespace diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 2e7cdcb..ccf4b06 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -51,6 +51,7 @@ #include "Pen.h" #include "NotImplemented.h" +#include <QBrush> #include <QDebug> #include <QGradient> #include <QPainter> @@ -152,22 +153,6 @@ static Qt::PenStyle toQPenStyle(StrokeStyle style) return Qt::NoPen; } -static inline QGradient applySpreadMethod(QGradient gradient, GradientSpreadMethod spreadMethod) -{ - switch (spreadMethod) { - case SpreadMethodPad: - gradient.setSpread(QGradient::PadSpread); - break; - case SpreadMethodReflect: - gradient.setSpread(QGradient::ReflectSpread); - break; - case SpreadMethodRepeat: - gradient.setSpread(QGradient::RepeatSpread); - break; - } - return gradient; -} - struct TransparencyLayer { TransparencyLayer(const QPainter* p, const QRect &rect) @@ -282,7 +267,11 @@ PlatformGraphicsContext* GraphicsContext::platformContext() const TransformationMatrix GraphicsContext::getCTM() const { - return platformContext()->combinedMatrix(); + QTransform matrix(platformContext()->combinedTransform()); + return TransformationMatrix(matrix.m11(), matrix.m12(), 0, matrix.m13(), + matrix.m21(), matrix.m22(), 0, matrix.m23(), + 0, 0, 1, 0, + matrix.m31(), matrix.m32(), 0, matrix.m33()); } void GraphicsContext::savePlatformState() @@ -295,7 +284,7 @@ void GraphicsContext::restorePlatformState() m_data->p()->restore(); if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) { - QMatrix matrix = m_common->state.pathTransform; + QTransform matrix = m_common->state.pathTransform; m_data->currentPath = m_data->currentPath * matrix; } } @@ -458,13 +447,21 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) if (paintingDisabled()) return; + StrokeStyle style = strokeStyle(); + Color color = strokeColor(); + if (style == NoStroke || !color.alpha()) + return; + + float width = strokeThickness(); + FloatPoint p1 = point1; FloatPoint p2 = point2; + bool isVerticalLine = (p1.x() == p2.x()); QPainter *p = m_data->p(); const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); - adjustLineToPixelBoundaries(p1, p2, strokeThickness(), strokeStyle()); + adjustLineToPixelBoundaries(p1, p2, width, style); IntSize shadowSize; int shadowBlur; @@ -477,8 +474,76 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) p->restore(); } + int patWidth = 0; + switch (style) { + case NoStroke: + case SolidStroke: + break; + case DottedStroke: + patWidth = (int)width; + break; + case DashedStroke: + patWidth = 3 * (int)width; + break; + } + + if (patWidth) { + p->save(); + + // Do a rect fill of our endpoints. This ensures we always have the + // appearance of being a border. We then draw the actual dotted/dashed line. + if (isVerticalLine) { + p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color)); + p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color)); + } else { + p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color)); + p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color)); + } + + // Example: 80 pixels with a width of 30 pixels. + // Remainder is 20. The maximum pixels of line we could paint + // will be 50 pixels. + int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width; + int remainder = distance % patWidth; + int coverage = distance - remainder; + int numSegments = coverage / patWidth; + + float patternOffset = 0.0f; + // Special case 1px dotted borders for speed. + if (patWidth == 1) + patternOffset = 1.0f; + else { + bool evenNumberOfSegments = numSegments % 2 == 0; + if (remainder) + evenNumberOfSegments = !evenNumberOfSegments; + if (evenNumberOfSegments) { + if (remainder) { + patternOffset += patWidth - remainder; + patternOffset += remainder / 2; + } else + patternOffset = patWidth / 2; + } else { + if (remainder) + patternOffset = (patWidth - remainder)/2; + } + } + + QVector<qreal> dashes; + dashes << qreal(patWidth) / width << qreal(patWidth) / width; + + QPen pen = p->pen(); + pen.setWidthF(width); + pen.setCapStyle(Qt::FlatCap); + pen.setDashPattern(dashes); + pen.setDashOffset(patternOffset / width); + p->setPen(pen); + } + p->drawLine(p1, p2); + if (patWidth) + p->restore(); + p->setRenderHint(QPainter::Antialiasing, antiAlias); } @@ -553,9 +618,9 @@ void GraphicsContext::fillPath() break; } case GradientColorSpace: - QGradient* gradient = m_common->state.fillGradient->platformGradient(); - *gradient = applySpreadMethod(*gradient, spreadMethod()); - p->fillPath(path, QBrush(*gradient)); + QBrush brush(*m_common->state.fillGradient->platformGradient()); + brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform()); + p->fillPath(path, brush); break; } m_data->currentPath = QPainterPath(); @@ -583,9 +648,9 @@ void GraphicsContext::strokePath() break; } case GradientColorSpace: { - QGradient* gradient = m_common->state.strokeGradient->platformGradient(); - *gradient = applySpreadMethod(*gradient, spreadMethod()); - pen.setBrush(QBrush(*gradient)); + QBrush brush(*m_common->state.strokeGradient->platformGradient()); + brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform()); + pen.setBrush(brush); p->setPen(pen); p->strokePath(path, pen); break; @@ -612,7 +677,9 @@ void GraphicsContext::fillRect(const FloatRect& rect) break; } case GradientColorSpace: - p->fillRect(rect, QBrush(*(m_common->state.fillGradient.get()->platformGradient()))); + QBrush brush(*m_common->state.fillGradient->platformGradient()); + brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform()); + p->fillRect(rect, brush); break; } m_data->currentPath = QPainterPath(); @@ -663,10 +730,7 @@ void GraphicsContext::clip(const FloatRect& rect) if (paintingDisabled()) return; - QPainter *p = m_data->p(); - if (p->clipRegion().isEmpty()) - p->setClipRect(rect); - else p->setClipRect(rect, Qt::IntersectClip); + m_data->p()->setClipRect(rect, Qt::IntersectClip); } void GraphicsContext::clipPath(WindRule clipRule) @@ -818,7 +882,7 @@ void GraphicsContext::clearRect(const FloatRect& rect) QPainter::CompositionMode currentCompositionMode = p->compositionMode(); if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) p->setCompositionMode(QPainter::CompositionMode_Source); - p->eraseRect(rect); + p->fillRect(rect, Qt::transparent); if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) p->setCompositionMode(currentCompositionMode); } @@ -939,7 +1003,7 @@ void GraphicsContext::translate(float x, float y) m_data->p()->translate(x, y); if (!m_data->currentPath.isEmpty()) { - QMatrix matrix; + QTransform matrix; m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y); m_common->state.pathTransform.translate(x, y); } @@ -961,7 +1025,7 @@ void GraphicsContext::rotate(float radians) m_data->p()->rotate(180/M_PI*radians); if (!m_data->currentPath.isEmpty()) { - QMatrix matrix; + QTransform matrix; m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians); m_common->state.pathTransform.rotate(radians); } @@ -975,9 +1039,9 @@ void GraphicsContext::scale(const FloatSize& s) m_data->p()->scale(s.width(), s.height()); if (!m_data->currentPath.isEmpty()) { - QMatrix matrix; + QTransform matrix; m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height()); - m_common->state.pathTransform.scale(s.width(), s.height()); + m_common->state.pathTransform.scaleNonUniform(s.width(), s.height()); } } @@ -1041,12 +1105,12 @@ void GraphicsContext::concatCTM(const TransformationMatrix& transform) if (paintingDisabled()) return; - m_data->p()->setMatrix(transform, true); + m_data->p()->setWorldTransform(transform, true); // Transformations to the context shouldn't transform the currentPath. // We have to undo every change made to the context from the currentPath to avoid wrong drawings. if (!m_data->currentPath.isEmpty() && transform.isInvertible()) { - QMatrix matrix = transform.inverse(); + QTransform matrix = transform.inverse(); m_data->currentPath = m_data->currentPath * matrix; m_common->state.pathTransform.multiply(transform); } diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index d4ab59f..d748305 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) 2008 Holger Hans Peter Freyther + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,10 +32,11 @@ #include "GraphicsContext.h" #include "ImageData.h" #include "MIMETypeRegistry.h" -#include "NotImplemented.h" #include "StillImageQt.h" #include <QBuffer> +#include <QColor> +#include <QImage> #include <QImageWriter> #include <QPainter> #include <QPixmap> @@ -79,15 +81,108 @@ Image* ImageBuffer::image() const return m_image.get(); } -PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect&) const +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const { - notImplemented(); - return 0; + PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); + unsigned char* data = result->data()->data()->data(); + + if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) + memset(data, 0, result->data()->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.x() + rect.width(); + if (endx > m_size.width()) + endx = m_size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.y() + rect.height(); + if (endy > m_size.height()) + endy = m_size.height(); + int numRows = endy - originy; + + QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + ASSERT(!image.isNull()); + + unsigned destBytesPerRow = 4 * rect.width(); + unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + QRgb value = image.pixel(x + originx, y + originy); + int basex = x * 4; + + destRows[basex] = qRed(value); + destRows[basex + 1] = qGreen(value); + destRows[basex + 2] = qBlue(value); + destRows[basex + 3] = qAlpha(value); + } + destRows += destBytesPerRow; + } + + return result; } -void ImageBuffer::putImageData(ImageData*, const IntRect&, const IntPoint&) +void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) { - notImplemented(); + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < m_size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= m_size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < m_size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= m_size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * source->width(); + + bool isPainting = m_data.m_painter->isActive(); + if (isPainting) + m_data.m_painter->end(); + + QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + + unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4; + for (int y = 0; y < numRows; ++y) { + quint32* scanLine = reinterpret_cast<quint32*>(image.scanLine(y + desty)); + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + scanLine[x + destx] = reinterpret_cast<quint32*>(srcRows + basex)[0]; + } + + srcRows += srcBytesPerRow; + } + + m_data.m_pixmap = QPixmap::fromImage(image); + + if (isPainting) + m_data.m_painter->begin(&m_data.m_pixmap); } // We get a mimeType here but QImageWriter does not support mimetypes but diff --git a/WebCore/platform/graphics/qt/ImageQt.cpp b/WebCore/platform/graphics/qt/ImageQt.cpp index 99062f9..3bc67ae 100644 --- a/WebCore/platform/graphics/qt/ImageQt.cpp +++ b/WebCore/platform/graphics/qt/ImageQt.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2006 Dirk Mueller <mueller@kde.org> * Copyright (C) 2006 Zack Rusin <zack@kde.org> * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ * * All rights reserved. * @@ -96,7 +97,26 @@ PassRefPtr<Image> Image::loadPlatformResource(const char* name) void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { - notImplemented(); + QPixmap* framePixmap = nativeImageForCurrentFrame(); + if (!framePixmap) // If it's too early we won't have an image yet. + return; + + QPixmap pixmap = *framePixmap; + QRect tr = QRectF(tileRect).toRect(); + if (tr.x() || tr.y() || tr.width() != pixmap.width() || tr.height() != pixmap.height()) { + pixmap = pixmap.copy(tr); + } + + QBrush b(pixmap); + b.setTransform(patternTransform); + ctxt->save(); + ctxt->setCompositeOperation(op); + QPainter* p = ctxt->platformContext(); + if (!pixmap.hasAlpha() && p->compositionMode() == QPainter::CompositionMode_SourceOver) + p->setCompositionMode(QPainter::CompositionMode_Source); + p->setBrushOrigin(phase); + p->fillRect(destRect, b); + ctxt->restore(); } void BitmapImage::initPlatformData() @@ -131,6 +151,9 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, QPainter* painter(ctxt->platformContext()); + if (!image->hasAlpha() && painter->compositionMode() == QPainter::CompositionMode_SourceOver) + painter->setCompositionMode(QPainter::CompositionMode_Source); + // Test using example site at // http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html painter->drawPixmap(dst, *image, src); @@ -138,33 +161,20 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, ctxt->restore(); } -void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, - const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) +void BitmapImage::checkForSolidColor() { - QPixmap* framePixmap = nativeImageForCurrentFrame(); - if (!framePixmap) // If it's too early we won't have an image yet. - return; + m_isSolidColor = false; + m_checkedForSolidColor = true; - QPixmap pixmap = *framePixmap; - QRect tr = QRectF(tileRect).toRect(); - if (tr.x() || tr.y() || tr.width() != pixmap.width() || tr.height() != pixmap.height()) { - pixmap = pixmap.copy(tr); - } + if (frameCount() > 1) + return; - QBrush b(pixmap); - b.setMatrix(patternTransform); - ctxt->save(); - ctxt->setCompositeOperation(op); - QPainter* p = ctxt->platformContext(); - p->setBrushOrigin(phase); - p->fillRect(destRect, b); - ctxt->restore(); -} + QPixmap* framePixmap = frameAtIndex(0); + if (!framePixmap || framePixmap->width() != 1 || framePixmap->height() != 1) + return; -void BitmapImage::checkForSolidColor() -{ - // FIXME: It's easy to implement this optimization. Just need to check the RGBA32 buffer to see if it is 1x1. - m_isSolidColor = false; + m_isSolidColor = true; + m_solidColor = QColor::fromRgba(framePixmap->toImage().pixel(0, 0)); } } diff --git a/WebCore/platform/graphics/qt/ImageSourceQt.cpp b/WebCore/platform/graphics/qt/ImageSourceQt.cpp index d62acc3..84de443 100644 --- a/WebCore/platform/graphics/qt/ImageSourceQt.cpp +++ b/WebCore/platform/graphics/qt/ImageSourceQt.cpp @@ -163,7 +163,7 @@ void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* delete m_decoder; m_decoder = 0; if (data) - setData(data, allDataReceived); + setData(data, allDataReceived); } } diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp index b1a48fb..c80d73b 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -76,7 +77,7 @@ namespace WebCore { MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_networkState(MediaPlayer::Empty) - , m_readyState(MediaPlayer::DataUnavailable) + , m_readyState(MediaPlayer::HaveNothing) , m_mediaObject(new MediaObject()) , m_videoWidget(new VideoWidget(0)) , m_audioOutput(new AudioOutput()) @@ -110,6 +111,18 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) connect(m_mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); } +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + + MediaPlayerPrivate::~MediaPlayerPrivate() { LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting videowidget"); @@ -131,6 +144,13 @@ void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&) notImplemented(); } +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // FIXME: do the real thing + notImplemented(); + return MediaPlayer::IsNotSupported; +} + bool MediaPlayerPrivate::hasVideo() const { bool hasVideo = m_mediaObject->hasVideo(); @@ -138,7 +158,7 @@ bool MediaPlayerPrivate::hasVideo() const return hasVideo; } -void MediaPlayerPrivate::load(String url) +void MediaPlayerPrivate::load(const String& url) { LOG(Media, "MediaPlayerPrivatePhonon::load(\"%s\")", url.utf8().data()); @@ -149,8 +169,8 @@ void MediaPlayerPrivate::load(String url) } // And we don't have any data yet - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } @@ -205,7 +225,7 @@ bool MediaPlayerPrivate::seeking() const float MediaPlayerPrivate::duration() const { - if (m_networkState < MediaPlayer::LoadedMetaData) + if (m_readyState < MediaPlayer::HaveMetadata) return 0.0f; float duration = m_mediaObject->totalTime() / 1000.0f; @@ -309,18 +329,19 @@ void MediaPlayerPrivate::updateStates() Phonon::State phononState = m_mediaObject->state(); if (phononState == Phonon::StoppedState) { - if (oldNetworkState < MediaPlayer::LoadedMetaData) { - m_networkState = MediaPlayer::LoadedMetaData; - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState < MediaPlayer::HaveMetadata) { + m_networkState = MediaPlayer::Loading; // FIXME: should this be MediaPlayer::Idle? + m_readyState = MediaPlayer::HaveMetadata; m_mediaObject->pause(); } } else if (phononState == Phonon::PausedState) { m_networkState = MediaPlayer::Loaded; - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveEnoughData; } else if (phononState == Phonon::ErrorState) { if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) { - m_networkState = MediaPlayer::LoadFailed; - m_readyState = MediaPlayer::DataUnavailable; + // FIXME: is it possile to differentiate between different types of errors + m_networkState = MediaPlayer::NetworkError; + m_readyState = MediaPlayer::HaveNothing; cancelLoad(); } else { m_mediaObject->pause(); @@ -328,7 +349,7 @@ void MediaPlayerPrivate::updateStates() } if (seeking()) - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveNothing; if (m_networkState != oldNetworkState) { const QMetaObject* metaObj = this->metaObject(); @@ -357,19 +378,18 @@ void MediaPlayerPrivate::setVisible(bool visible) m_videoWidget->setVisible(m_isVisible); } -void MediaPlayerPrivate::setRect(const IntRect& newRect) +void MediaPlayerPrivate::setSize(const IntSize& newSize) { if (!m_videoWidget) return; - LOG(Media, "MediaPlayerPrivatePhonon::setRect(%d,%d %dx%d)", - newRect.x(), newRect.y(), - newRect.width(), newRect.height()); + LOG(Media, "MediaPlayerPrivatePhonon::setSize(%d,%d)", + newSize.width(), newSize.height()); QRect currentRect = m_videoWidget->rect(); - if (newRect.width() != currentRect.width() || newRect.height() != currentRect.height()) - m_videoWidget->resize(newRect.width(), newRect.height()); + if (newSize.width() != currentRect.width() || newSize.height() != currentRect.height()) + m_videoWidget->resize(newSize.width(), newSize.height()); } IntSize MediaPlayerPrivate::naturalSize() const @@ -380,7 +400,7 @@ IntSize MediaPlayerPrivate::naturalSize() const return IntSize(); } - if (m_networkState < MediaPlayer::LoadedMetaData) { + if (m_readyState < MediaPlayer::HaveMetadata) { LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", 0, 0); return IntSize(); diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h index 1b20a84..9572d61 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -20,8 +21,7 @@ #ifndef MediaPlayerPrivatePhonon_h #define MediaPlayerPrivatePhonon_h -#include "MediaPlayer.h" -#include <wtf/Noncopyable.h> +#include "MediaPlayerPrivate.h" #include <QObject> #include <phononnamespace.h> @@ -40,31 +40,33 @@ QT_END_NAMESPACE namespace WebCore { - class MediaPlayerPrivate : public QObject, Noncopyable { + class MediaPlayerPrivate : public QObject, public MediaPlayerPrivateInterface { Q_OBJECT public: - MediaPlayerPrivate(MediaPlayer*); + static void registerMediaEngine(MediaEngineRegistrar); ~MediaPlayerPrivate(); // These enums are used for debugging Q_ENUMS(ReadyState NetworkState PhononState) enum ReadyState { - DataUnavailable, - CanShowCurrentFrame, - CanPlay, - CanPlayThrough + HaveNothing, + HaveMetadata, + HaveCurrentData, + HaveFutureData, + HaveEnoughData }; enum NetworkState { - Empty, - LoadFailed, - Loading, - LoadedMetaData, - LoadedFirstFrame, - Loaded + Empty, + Idle, + Loading, + Loaded, + FormatError, + NetworkError, + DecodeError }; enum PhononState { @@ -79,7 +81,7 @@ namespace WebCore { IntSize naturalSize() const; bool hasVideo() const; - void load(String url); + void load(const String &url); void cancelLoad(); void play(); @@ -109,11 +111,9 @@ namespace WebCore { unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect&); + void setSize(const IntSize&); void paint(GraphicsContext*, const IntRect&); - static void getSupportedTypes(HashSet<String>&); - static bool isAvailable() { return true; } protected: bool eventFilter(QObject*, QEvent*); @@ -130,6 +130,13 @@ namespace WebCore { void totalTimeChanged(qint64); private: + MediaPlayerPrivate(MediaPlayer*); + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable() { return true; } + void updateStates(); MediaPlayer* m_player; diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index bd0192c..a8a3ea2 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -1,6 +1,7 @@ /* - * Copyright (C) 2006 Zack Rusin <zack@kde.org> - * 2006 Rob Buis <buis@kde.org> + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * 2006 Rob Buis <buis@kde.org> + * 2009 Dirk Schulze <krit@webkit.org> * * All rights reserved. * @@ -36,7 +37,7 @@ #include "PlatformString.h" #include "StrokeStyleApplier.h" #include <QPainterPath> -#include <QMatrix> +#include <QTransform> #include <QString> #define _USE_MATH_DEFINES @@ -108,7 +109,7 @@ bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) void Path::translate(const FloatSize& size) { - QMatrix matrix; + QTransform matrix; matrix.translate(size.width(), size.height()); *m_path = (*m_path) * matrix; } @@ -161,9 +162,72 @@ void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) { - //FIXME: busted - qWarning("arcTo is busted"); - m_path->arcTo(p1.x(), p1.y(), p2.x(), p2.y(), radius, 90); + 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); + return; + } + + FloatPoint p1p0((p0.x() - p1.x()),(p0.y() - p1.y())); + FloatPoint p1p2((p2.x() - p1.x()),(p2.y() - p1.y())); + float p1p0_length = sqrtf(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); + float p1p2_length = sqrtf(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); + + 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); + return; + } + if (cos_phi == 1) { + // add infinite far away point + 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); + return; + } + + float tangent = radius / tan(acos(cos_phi) / 2); + float factor_p1p0 = tangent / p1p0_length; + FloatPoint t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); + + FloatPoint orth_p1p0(p1p0.y(), -p1p0.x()); + float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); + float factor_ra = radius / orth_p1p0_length; + + // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 + double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); + if (cos_alpha < 0.f) + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + + FloatPoint p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); + + // calculate angles for addArc + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + float sa = acos(orth_p1p0.x() / orth_p1p0_length); + if (orth_p1p0.y() < 0.f) + sa = 2 * piDouble - sa; + + // anticlockwise logic + bool anticlockwise = false; + + float factor_p1p2 = tangent / p1p2_length; + FloatPoint t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); + FloatPoint orth_p1p2((t_p1p2.x() - p.x()),(t_p1p2.y() - p.y())); + float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); + float ea = acos(orth_p1p2.x() / orth_p1p2_length); + if (orth_p1p2.y() < 0) + ea = 2 * piDouble - ea; + if ((sa > ea) && ((sa - ea) < piDouble)) + anticlockwise = true; + if ((sa < ea) && ((ea - sa) > piDouble)) + anticlockwise = true; + + m_path->lineTo(t_p1p0); + + addArc(p, radius, sa, ea, anticlockwise); } void Path::closeSubpath() @@ -316,7 +380,7 @@ void Path::apply(void* info, PathApplierFunction function) const void Path::transform(const TransformationMatrix& transform) { if (m_path) { - QMatrix mat = transform; + QTransform mat = transform; QPainterPath temp = mat.map(*m_path); delete m_path; m_path = new QPainterPath(temp); diff --git a/WebCore/platform/graphics/qt/PatternQt.cpp b/WebCore/platform/graphics/qt/PatternQt.cpp index 5b76841..b261613 100644 --- a/WebCore/platform/graphics/qt/PatternQt.cpp +++ b/WebCore/platform/graphics/qt/PatternQt.cpp @@ -31,14 +31,15 @@ namespace WebCore { -QBrush Pattern::createPlatformPattern(const TransformationMatrix& transform) const +QBrush Pattern::createPlatformPattern(const TransformationMatrix&) const { QPixmap* pixmap = tileImage()->nativeImageForCurrentFrame(); if (!pixmap) return QBrush(); + // Qt merges patter space and user space itself QBrush brush(*pixmap); - brush.setMatrix(transform); + brush.setTransform(m_patternSpaceTransformation); return brush; } diff --git a/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp b/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp index 47abd17..15f0cc5 100644 --- a/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp +++ b/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp @@ -31,170 +31,9 @@ namespace WebCore { -TransformationMatrix::TransformationMatrix() - : m_transform() -{ -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) - : m_transform(a, b, c, d, tx, ty) -{ -} - -TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) - : m_transform(matrix) -{ -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform.setMatrix(a, b, c, d, tx, ty); -} - -void TransformationMatrix::map(double x, double y, double* x2, double* y2) const -{ - qreal tx2, ty2; - m_transform.map(qreal(x), qreal(y), &tx2, &ty2); - *x2 = tx2; - *y2 = ty2; -} - -IntRect TransformationMatrix::mapRect(const IntRect& rect) const -{ - return m_transform.mapRect(rect); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect& rect) const -{ - return m_transform.mapRect(rect); -} - -bool TransformationMatrix::isIdentity() const -{ - return m_transform.isIdentity(); -} - -double TransformationMatrix::a() const -{ - return m_transform.m11(); -} - -void TransformationMatrix::setA(double a) -{ - m_transform.setMatrix(a, b(), c(), d(), e(), f()); -} - -double TransformationMatrix::b() const -{ - return m_transform.m12(); -} - -void TransformationMatrix::setB(double b) -{ - m_transform.setMatrix(a(), b, c(), d(), e(), f()); -} - -double TransformationMatrix::c() const -{ - return m_transform.m21(); -} - -void TransformationMatrix::setC(double c) -{ - m_transform.setMatrix(a(), b(), c, d(), e(), f()); -} - -double TransformationMatrix::d() const -{ - return m_transform.m22(); -} - -void TransformationMatrix::setD(double d) -{ - m_transform.setMatrix(a(), b(), c(), d, e(), f()); -} - -double TransformationMatrix::e() const -{ - return m_transform.dx(); -} - -void TransformationMatrix::setE(double e) -{ - m_transform.setMatrix(a(), b(), c(), d(), e, f()); -} - -double TransformationMatrix::f() const -{ - return m_transform.dy(); -} - -void TransformationMatrix::setF(double f) -{ - m_transform.setMatrix(a(), b(), c(), d(), e(), f); -} - -void TransformationMatrix::reset() -{ - m_transform.reset(); -} - -TransformationMatrix& TransformationMatrix::scale(double sx, double sy) -{ - m_transform.scale(sx, sy); - return *this; -} - -TransformationMatrix& TransformationMatrix::rotate(double d) -{ - m_transform.rotate(d); - return *this; -} - -TransformationMatrix& TransformationMatrix::translate(double tx, double ty) -{ - m_transform.translate(tx, ty); - return *this; -} - -TransformationMatrix& TransformationMatrix::shear(double sx, double sy) -{ - m_transform.shear(sx, sy); - return *this; -} - -double TransformationMatrix::det() const -{ - return m_transform.det(); -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - if(!isInvertible()) - return TransformationMatrix(); - - return m_transform.inverted(); -} - -TransformationMatrix::operator QMatrix() const -{ - return m_transform; -} - -bool TransformationMatrix::operator==(const TransformationMatrix& other) const -{ - return m_transform == other.m_transform; -} - -TransformationMatrix& TransformationMatrix::operator*=(const TransformationMatrix& other) -{ - m_transform *= other.m_transform; - return *this; -} - -TransformationMatrix TransformationMatrix::operator*(const TransformationMatrix& other) -{ - return m_transform * other.m_transform; +TransformationMatrix::operator QTransform() const +{ + return QTransform(m11(), m12(), m14(), m21(), m22(), m24(), m41(), m42(), m44()); } } diff --git a/WebCore/platform/graphics/skia/GradientSkia.cpp b/WebCore/platform/graphics/skia/GradientSkia.cpp index eff7c66..2d2000c 100644 --- a/WebCore/platform/graphics/skia/GradientSkia.cpp +++ b/WebCore/platform/graphics/skia/GradientSkia.cpp @@ -136,6 +136,19 @@ SkShader* Gradient::platformGradient() fillStops(m_stops.data(), m_stops.size(), pos, colors); + SkShader::TileMode tile = SkShader::kClamp_TileMode; + switch (m_spreadMethod) { + case SpreadMethodReflect: + tile = SkShader::kMirror_TileMode; + break; + case SpreadMethodRepeat: + tile = SkShader::kRepeat_TileMode; + break; + case SpreadMethodPad: + tile = SkShader::kClamp_TileMode; + break; + } + if (m_radial) { // FIXME: CSS radial Gradients allow an offset focal point (the // "start circle"), but skia doesn't seem to support that, so this just @@ -145,13 +158,16 @@ SkShader* Gradient::platformGradient() // description of the expected behavior. m_gradient = SkGradientShader::CreateRadial(m_p1, WebCoreFloatToSkScalar(m_r1), colors, pos, - static_cast<int>(countUsed), SkShader::kClamp_TileMode); + static_cast<int>(countUsed), tile); } else { SkPoint pts[2] = { m_p0, m_p1 }; m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, - static_cast<int>(countUsed), SkShader::kClamp_TileMode); + static_cast<int>(countUsed), tile); } + SkMatrix matrix = m_gradientSpaceTransformation; + m_gradient->setLocalMatrix(matrix); + return m_gradient; } diff --git a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp index e6c7783..376fa4b 100644 --- a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp +++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -36,6 +36,7 @@ #include "Color.h" #include "FloatRect.h" #include "Gradient.h" +#include "ImageBuffer.h" #include "IntRect.h" #include "NativeImageSkia.h" #include "NotImplemented.h" @@ -274,11 +275,6 @@ void GraphicsContext::endTransparencyLayer() { if (paintingDisabled()) return; - -#if PLATFORM(WIN_OS) - platformContext()->canvas()->getTopPlatformDevice(). - fixupAlphaBeforeCompositing(); -#endif platformContext()->canvas()->restore(); } @@ -406,8 +402,7 @@ void GraphicsContext::clipPath(WindRule clipRule) if (paintingDisabled()) return; - const SkPath* oldPath = platformContext()->currentPath(); - SkPath path(*oldPath); + SkPath path = platformContext()->currentPathInLocalCoordinates(); path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); platformContext()->canvas()->clipPath(path); } @@ -418,8 +413,9 @@ void GraphicsContext::clipToImageBuffer(const FloatRect& rect, if (paintingDisabled()) return; - // FIXME: This is needed for image masking and complex text fills. - notImplemented(); +#if defined(__linux__) || PLATFORM(WIN_OS) + platformContext()->beginLayerClippedToImage(rect, imageBuffer); +#endif } void GraphicsContext::concatCTM(const TransformationMatrix& xform) @@ -645,6 +641,9 @@ void GraphicsContext::drawLineForText(const IntPoint& pt, if (paintingDisabled()) return; + if (width <= 0) + return; + int thickness = SkMax32(static_cast<int>(strokeThickness()), 1); SkRect r; r.fLeft = SkIntToScalar(pt.x()); @@ -653,7 +652,9 @@ void GraphicsContext::drawLineForText(const IntPoint& pt, r.fBottom = r.fTop + SkIntToScalar(thickness); SkPaint paint; - paint.setColor(strokeColor().rgb()); + platformContext()->setupPaintForFilling(&paint); + // Text lines are drawn using the stroke color. + paint.setColor(platformContext()->effectiveStrokeColor()); platformContext()->canvas()->drawRect(r, paint); } @@ -664,9 +665,10 @@ void GraphicsContext::drawRect(const IntRect& rect) return; SkRect r = rect; - if (!isRectSkiaSafe(getCTM(), r)) + if (!isRectSkiaSafe(getCTM(), r)) { // See the fillRect below. ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } platformContext()->drawRect(r); } @@ -676,7 +678,7 @@ void GraphicsContext::fillPath() if (paintingDisabled()) return; - const SkPath& path = *platformContext()->currentPath(); + SkPath path = platformContext()->currentPathInLocalCoordinates(); if (!isPathSkiaSafe(getCTM(), path)) return; @@ -686,7 +688,7 @@ void GraphicsContext::fillPath() if (colorSpace == SolidColorSpace && !fillColor().alpha()) return; - platformContext()->setFillRule(state.fillRule == RULE_EVENODD ? + path.setFillType(state.fillRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); SkPaint paint; @@ -708,9 +710,10 @@ void GraphicsContext::fillRect(const FloatRect& rect) return; SkRect r = rect; - if (!isRectSkiaSafe(getCTM(), r)) + if (!isRectSkiaSafe(getCTM(), r)) { // See the other version of fillRect below. ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } const GraphicsContextState& state = m_common->state; ColorSpace colorSpace = state.fillColorSpace; @@ -775,6 +778,17 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, // See fillRect(). ClipRectToCanvas(*platformContext()->canvas(), r, &r); + if (topLeft.width() + topRight.width() > rect.width() + || bottomLeft.width() + bottomRight.width() > rect.width() + || topLeft.height() + bottomLeft.height() > rect.height() + || topRight.height() + bottomRight.height() > rect.height()) { + // Not all the radii fit, return a rect. This matches the behavior of + // Path::createRoundedRectangle. Without this we attempt to draw a round + // shadow for a square box. + fillRect(rect, color); + return; + } + SkPath path; addCornerArc(&path, r, topRight, 270); addCornerArc(&path, r, bottomRight, 0); @@ -784,12 +798,17 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, SkPaint paint; platformContext()->setupPaintForFilling(&paint); platformContext()->canvas()->drawPath(path, paint); - return fillRect(rect, color); } TransformationMatrix GraphicsContext::getCTM() const { - return platformContext()->canvas()->getTotalMatrix(); + const SkMatrix& m = platformContext()->canvas()->getTotalMatrix(); + return TransformationMatrix(SkScalarToDouble(m.getScaleX()), // a + SkScalarToDouble(m.getSkewY()), // b + SkScalarToDouble(m.getSkewX()), // c + SkScalarToDouble(m.getScaleY()), // d + SkScalarToDouble(m.getTranslateX()), // e + SkScalarToDouble(m.getTranslateY())); // f } FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) @@ -1050,7 +1069,7 @@ void GraphicsContext::strokePath() if (paintingDisabled()) return; - const SkPath& path = *platformContext()->currentPath(); + SkPath path = platformContext()->currentPathInLocalCoordinates(); if (!isPathSkiaSafe(getCTM(), path)) return; diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp index fdfcb85..5e90491 100644 --- a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -31,12 +31,14 @@ #include "config.h" #include "ImageBuffer.h" +#include "Base64.h" #include "BitmapImage.h" #include "BitmapImageSingleFrameSkia.h" #include "GraphicsContext.h" #include "ImageData.h" #include "NotImplemented.h" #include "PlatformContextSkia.h" +#include "PNGImageEncoder.h" #include "SkiaUtils.h" using namespace std; @@ -63,6 +65,9 @@ ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) m_data.m_platformContext.setCanvas(&m_data.m_canvas); m_context.set(new GraphicsContext(&m_data.m_platformContext)); +#if PLATFORM(WIN_OS) + m_context->platformContext()->setDrawingToImageBuffer(true); +#endif // Make the background transparent. It would be nice if this wasn't // required, but the canvas is currently filled with the magic transparency @@ -101,7 +106,7 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const ASSERT(context()); RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); - unsigned char* data = result->data()->data(); + unsigned char* data = result->data()->data()->data(); if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || @@ -188,7 +193,7 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, unsigned srcBytesPerRow = 4 * source->width(); - const unsigned char* srcRow = source->data()->data() + originY * srcBytesPerRow + originX * 4; + const unsigned char* srcRow = source->data()->data()->data() + originY * srcBytesPerRow + originX * 4; for (int y = 0; y < numRows; ++y) { uint32_t* destRow = bitmap.getAddr32(destX, destY + y); @@ -203,8 +208,18 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, String ImageBuffer::toDataURL(const String&) const { - notImplemented(); - return String(); + // Encode the image into a vector. + Vector<unsigned char> pngEncodedData; + PNGImageEncoder::encode(*context()->platformContext()->bitmap(), &pngEncodedData); + + // Convert it into base64. + Vector<char> base64EncodedData; + base64Encode(*reinterpret_cast<Vector<char>*>(&pngEncodedData), base64EncodedData); + // Append with a \0 so that it's a valid string. + base64EncodedData.append('\0'); + + // And the resulting string. + return String::format("data:image/png;base64,%s", base64EncodedData.data()); } } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageSkia.cpp b/WebCore/platform/graphics/skia/ImageSkia.cpp index 1123fe9..d7f2830 100644 --- a/WebCore/platform/graphics/skia/ImageSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSkia.cpp @@ -225,6 +225,7 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag { SkPaint paint; paint.setPorterDuffXfermode(compOp); + paint.setFilterBitmap(true); skia::PlatformCanvas* canvas = platformContext->canvas(); @@ -233,7 +234,6 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag SkScalarToFloat(destRect.width()), SkScalarToFloat(destRect.height())); if (resampling == RESAMPLE_AWESOME) { - paint.setFilterBitmap(false); drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); } else { // No resampling necessary, we can just draw the bitmap. We want to @@ -241,7 +241,6 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag // is something interesting going on with the matrix (like a rotation). // Note: for serialization, we will want to subset the bitmap first so // we don't send extra pixels. - paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint); } } @@ -401,6 +400,7 @@ void BitmapImage::invalidatePlatformData() void BitmapImage::checkForSolidColor() { + m_checkedForSolidColor = true; } void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, @@ -427,7 +427,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, paintSkBitmap(ctxt->platformContext(), *bm, enclosingIntRect(normSrcRect), - enclosingIntRect(normDstRect), + normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); } @@ -447,7 +447,7 @@ void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt, paintSkBitmap(ctxt->platformContext(), m_nativeImage, enclosingIntRect(normSrcRect), - enclosingIntRect(normDstRect), + normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); } diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp index f77620b..b5f7e1d 100644 --- a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp @@ -100,16 +100,16 @@ ImageSource::~ImageSource() void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) { - // TODO(darin): Figure out what to do with the |data| and |allDataReceived| params. - - if (destroyAll) { - delete m_decoder; - m_decoder = 0; + if (!destroyAll) { + if (m_decoder) + m_decoder->clearFrameBufferCache(clearBeforeFrame); return; } - if (m_decoder) - m_decoder->clearFrameBufferCache(clearBeforeFrame); + delete m_decoder; + m_decoder = 0; + if (data) + setData(data, allDataReceived); } bool ImageSource::initialized() const diff --git a/WebCore/platform/graphics/skia/PathSkia.cpp b/WebCore/platform/graphics/skia/PathSkia.cpp index ca99322..2700da8 100644 --- a/WebCore/platform/graphics/skia/PathSkia.cpp +++ b/WebCore/platform/graphics/skia/PathSkia.cpp @@ -274,7 +274,7 @@ static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context) SkPaint paint; context->platformContext()->setupPaintForStroking(&paint, 0, 0); SkPath boundingPath; - paint.getFillPath(context->platformContext()->currentPath(), &boundingPath); + paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath); SkRect r; boundingPath.computeBounds(&r, SkPath::kExact_BoundsType); return r; diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index 60dbbe0..6c633f2 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "GraphicsContext.h" +#include "ImageBuffer.h" #include "NativeImageSkia.h" #include "PlatformContextSkia.h" #include "SkiaUtils.h" @@ -45,10 +46,6 @@ #include <wtf/MathExtras.h> -#if defined(__linux__) -#include "GdkSkia.h" -#endif - // State ----------------------------------------------------------------------- // Encapsulates the additional painting state information we store for each @@ -86,6 +83,13 @@ struct PlatformContextSkia::State { // color to produce a new output color. SkColor applyAlpha(SkColor) const; +#if defined(__linux__) || PLATFORM(WIN_OS) + // 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. + WebCore::FloatRect m_clip; +#endif + private: // Not supported. void operator=(const State&); @@ -113,9 +117,28 @@ PlatformContextSkia::State::State() } PlatformContextSkia::State::State(const State& other) + : m_alpha(other.m_alpha) + , m_porterDuffMode(other.m_porterDuffMode) + , m_gradient(other.m_gradient) + , m_pattern(other.m_pattern) + , m_useAntialiasing(other.m_useAntialiasing) + , m_looper(other.m_looper) + , m_fillColor(other.m_fillColor) + , m_strokeStyle(other.m_strokeStyle) + , m_strokeColor(other.m_strokeColor) + , m_strokeThickness(other.m_strokeThickness) + , m_dashRatio(other.m_dashRatio) + , m_miterLimit(other.m_miterLimit) + , m_lineCap(other.m_lineCap) + , m_lineJoin(other.m_lineJoin) + , m_dash(other.m_dash) + , m_textDrawingMode(other.m_textDrawingMode) +#if defined(__linux__) || PLATFORM(WIN_OS) + , m_imageBufferClip(other.m_imageBufferClip) + , m_clip(other.m_clip) +#endif { - memcpy(this, &other, sizeof(State)); - + // Up the ref count of these. saveRef does nothing if 'this' is NULL. m_looper->safeRef(); m_dash->safeRef(); m_gradient->safeRef(); @@ -148,22 +171,16 @@ SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) : m_canvas(canvas) , m_stateStack(sizeof(State)) +#if PLATFORM(WIN_OS) + , m_drawingToImageBuffer(false) +#endif { m_stateStack.append(State()); m_state = &m_stateStack.last(); -#if defined(OS_LINUX) - m_gdkskia = m_canvas ? gdk_skia_new(m_canvas) : 0; -#endif } PlatformContextSkia::~PlatformContextSkia() { -#if defined(OS_LINUX) - if (m_gdkskia) { - g_object_unref(m_gdkskia); - m_gdkskia = 0; - } -#endif } void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) @@ -171,17 +188,72 @@ void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) m_canvas = canvas; } +#if PLATFORM(WIN_OS) +void PlatformContextSkia::setDrawingToImageBuffer(bool value) +{ + m_drawingToImageBuffer = value; +} + +bool PlatformContextSkia::isDrawingToImageBuffer() const +{ + return m_drawingToImageBuffer; +} +#endif + void PlatformContextSkia::save() { m_stateStack.append(*m_state); m_state = &m_stateStack.last(); +#if defined(__linux__) || PLATFORM(WIN_OS) + // 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(); +#endif + // Save our native canvas. canvas()->save(); } +#if defined(__linux__) || PLATFORM(WIN_OS) +void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect, + const WebCore::ImageBuffer* imageBuffer) +{ + // Skia doesn't support clipping to an image, so we create a layer. The next + // time restore is invoked the layer and |imageBuffer| are combined to + // create the resulting image. + m_state->m_clip = rect; + SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), + SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) }; + + 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. + const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap(); + if (!bitmap->pixelRef()) { + // The bitmap owns it's pixels. This happens when we've allocated the + // pixels in some way and assigned them directly to the bitmap (as + // happens when we allocate a DIB). In this case the assignment operator + // does not copy the pixels, rather the copied bitmap ends up + // referencing the same pixels. As the pixels may not live as long as we + // need it to, we copy the image. + bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config); + } else { + // If there is a pixel ref, we can safely use the assignment operator. + m_state->m_imageBufferClip = *bitmap; + } +} +#endif + void PlatformContextSkia::restore() { +#if defined(__linux__) || PLATFORM(WIN_OS) + if (!m_state->m_imageBufferClip.empty()) { + applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip); + canvas()->restore(); + } +#endif + m_stateStack.removeLast(); m_state = &m_stateStack.last(); @@ -200,12 +272,21 @@ void PlatformContextSkia::drawRect(SkRect rect) if (m_state->m_strokeStyle != WebCore::NoStroke && (m_state->m_strokeColor & 0xFF000000)) { - if (fillcolorNotTransparent) { - // This call is expensive so don't call it unnecessarily. - paint.reset(); - } - setupPaintForStroking(&paint, &rect, 0); - canvas()->drawRect(rect, paint); + // We do a fill of four rects to simulate the stroke of a border. + SkColor oldFillColor = m_state->m_fillColor; + if (oldFillColor != m_state->m_strokeColor) + setFillColor(m_state->m_strokeColor); + setupPaintForFilling(&paint); + SkRect topBorder = { rect.fLeft, rect.fTop, rect.width(), 1 }; + canvas()->drawRect(topBorder, paint); + SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.width(), 1 }; + canvas()->drawRect(bottomBorder, paint); + SkRect leftBorder = { rect.fLeft, rect.fTop + 1, 1, rect.height() - 2 }; + canvas()->drawRect(leftBorder, paint); + SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, 1, rect.height() - 2 }; + canvas()->drawRect(rightBorder, paint); + if (oldFillColor != m_state->m_strokeColor) + setFillColor(oldFillColor); } } @@ -239,10 +320,6 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i setupPaintCommon(paint); float width = m_state->m_strokeThickness; - // This allows dashing and dotting to work properly for hairline strokes. - if (width == 0) - width = 1; - paint->setColor(m_state->applyAlpha(m_state->m_strokeColor)); paint->setStyle(SkPaint::kStroke_Style); paint->setStrokeWidth(SkFloatToScalar(width)); @@ -250,9 +327,6 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i paint->setStrokeJoin(m_state->m_lineJoin); paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit)); - if (rect != 0 && (static_cast<int>(roundf(width)) & 1)) - rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); - if (m_state->m_dash) paint->setPathEffect(m_state->m_dash); else { @@ -267,7 +341,8 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i SkScalar dashLength; if (length) { // Determine about how many dashes or dots we should have. - int numDashes = length / roundf(width); + float roundedWidth = roundf(width); + int numDashes = roundedWidth ? (length / roundedWidth) : length; if (!(numDashes & 1)) numDashes++; // Make it odd so we end on a dash/dot. // Use the number of dashes to determine the length of a @@ -366,9 +441,14 @@ void PlatformContextSkia::setUseAntialiasing(bool enable) m_state->m_useAntialiasing = enable; } -SkColor PlatformContextSkia::fillColor() const +SkColor PlatformContextSkia::effectiveFillColor() const { - return m_state->m_fillColor; + return m_state->applyAlpha(m_state->m_fillColor); +} + +SkColor PlatformContextSkia::effectiveStrokeColor() const +{ + return m_state->applyAlpha(m_state->m_strokeColor); } void PlatformContextSkia::beginPath() @@ -378,7 +458,17 @@ void PlatformContextSkia::beginPath() void PlatformContextSkia::addPath(const SkPath& path) { - m_path.addPath(path); + m_path.addPath(path, m_canvas->getTotalMatrix()); +} + +SkPath PlatformContextSkia::currentPathInLocalCoordinates() const +{ + SkPath localPath = m_path; + const SkMatrix& matrix = m_canvas->getTotalMatrix(); + SkMatrix inverseMatrix; + matrix.invert(&inverseMatrix); + localPath.transform(inverseMatrix); + return localPath; } void PlatformContextSkia::setFillRule(SkPath::FillType fr) @@ -425,3 +515,14 @@ bool PlatformContextSkia::isPrinting() { return m_canvas->getTopPlatformDevice().IsVectorial(); } + +#if defined(__linux__) || PLATFORM(WIN_OS) +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 + // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping. + SkPaint paint; + paint.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode); + m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint); +} +#endif diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h index 78e9a19..8850a6a 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -43,8 +43,6 @@ #include <wtf/Vector.h> -typedef struct _GdkDrawable GdkSkia; - // This class holds the platform-specific state for GraphicsContext. We put // most of our Skia wrappers on this class. In theory, a lot of this stuff could // be moved to GraphicsContext directly, except that some code external to this @@ -73,9 +71,28 @@ public: // to the constructor. void setCanvas(skia::PlatformCanvas*); +#if PLATFORM(WIN_OS) + // 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 + // color and all text drawing needs to go to a layer so that the alpha is + // correctly updated. + void setDrawingToImageBuffer(bool); + bool isDrawingToImageBuffer() const; +#endif + void save(); void restore(); + // Begins a layer that is clipped to the image |imageBuffer| at the location + // |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) + void beginLayerClippedToImage(const WebCore::FloatRect&, + const WebCore::ImageBuffer*); +#endif + // Sets up the common flags on a paint for antialiasing, effects, etc. // This is implicitly called by setupPaintFill and setupPaintStroke, but // you may wish to call it directly sometimes if you don't want that other @@ -116,9 +133,15 @@ public: void beginPath(); void addPath(const SkPath&); - const SkPath* currentPath() const { return &m_path; } + SkPath currentPathInLocalCoordinates() const; + + // Returns the fill color. The returned color has it's alpha adjusted + // by the current alpha. + SkColor effectiveFillColor() const; - SkColor fillColor() const; + // Returns the stroke color. The returned color has it's alpha adjusted + // by the current alpha. + SkColor effectiveStrokeColor() const; skia::PlatformCanvas* canvas() { return m_canvas; } @@ -142,12 +165,13 @@ public: // possible quality. bool isPrinting(); -#if defined(__linux__) - // FIXME: should be camelCase. - GdkSkia* gdk_skia() const { return m_gdkskia; } +private: +#if defined(__linux__) || PLATFORM(WIN_OS) + // 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&); #endif -private: // Defines drawing style. struct State; @@ -161,12 +185,11 @@ private: // mStateStack.back(). State* m_state; - // Current path. + // Current path in global coordinates. SkPath m_path; -#if defined(__linux__) - // A pointer to a GDK Drawable wrapping of this Skia canvas - GdkSkia* m_gdkskia; +#if PLATFORM(WIN_OS) + bool m_drawingToImageBuffer; #endif }; diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.cpp b/WebCore/platform/graphics/skia/SkiaFontWin.cpp index 6e79a7e..d0cd4c5 100644 --- a/WebCore/platform/graphics/skia/SkiaFontWin.cpp +++ b/WebCore/platform/graphics/skia/SkiaFontWin.cpp @@ -31,8 +31,13 @@ #include "config.h" #include "SkiaFontWin.h" +#include "PlatformContextSkia.h" +#include "Gradient.h" +#include "Pattern.h" #include "SkCanvas.h" #include "SkPaint.h" +#include "SkShader.h" +#include "TransformationMatrix.h" #include <wtf/ListHashSet.h> #include <wtf/Vector.h> @@ -162,10 +167,10 @@ static bool getPathForGlyph(HDC dc, WORD glyph, SkPath* path) addPolyCurveToPath(polyCurve, path); curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx; } + path->close(); curGlyph += polyHeader->cb; } - path->close(); return true; } @@ -215,4 +220,152 @@ void SkiaWinOutlineCache::removePathsForFont(HFONT hfont) deleteOutline(outlineCache.find(*i)); } +bool windowsCanHandleTextDrawing(GraphicsContext* context) +{ + // Check for non-translation transforms. Sometimes zooms will look better in + // Skia, and sometimes better in Windows. The main problem is that zooming + // in using Skia will show you the hinted outlines for the smaller size, + // which look weird. All else being equal, it's better to use Windows' text + // drawing, so we don't check for zooms. + const TransformationMatrix& matrix = context->getCTM(); + if (matrix.b() != 0 || matrix.c() != 0) // Check for skew. + return false; + + // Check for stroke effects. + if (context->platformContext()->getTextDrawingMode() != cTextFill) + return false; + + // Check for gradients. + if (context->fillGradient() || context->strokeGradient()) + return false; + + // Check for patterns. + if (context->fillPattern() || context->strokePattern()) + return false; + + // Check for shadow effects. + if (context->platformContext()->getDrawLooper()) + return false; + + return true; +} + +// Draws the given text string using skia. Note that gradient or +// pattern may be NULL, in which case a solid colour is used. +static bool skiaDrawText(HFONT hfont, + HDC dc, + SkCanvas* canvas, + const SkPoint& point, + SkPaint* paint, + const TransformationMatrix& transformationMatrix, + Gradient* gradient, + Pattern* pattern, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + int numGlyphs) +{ + SkShader* shader = NULL; + if (gradient) + shader = gradient->platformGradient(); + else if (pattern) + shader = pattern->createPlatformPattern(transformationMatrix); + + paint->setShader(shader); + float x = point.fX, y = point.fY; + + for (int i = 0; i < numGlyphs; i++) { + const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); + if (!path) + return false; + + float offsetX = 0.0f, offsetY = 0.0f; + if (offsets && (offsets[i].du != 0 || offsets[i].dv != 0)) { + offsetX = offsets[i].du; + offsetY = offsets[i].dv; + } + + SkPath newPath; + newPath.addPath(*path, x + offsetX, y + offsetY); + canvas->drawPath(newPath, *paint); + + x += advances[i]; + } + + return true; +} + +bool paintSkiaText(GraphicsContext* context, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + const SkPoint* origin) +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont); + + PlatformContextSkia* platformContext = context->platformContext(); + int textMode = platformContext->getTextDrawingMode(); + + // Filling (if necessary). This is the common case. + SkPaint paint; + platformContext->setupPaintForFilling(&paint); + paint.setFlags(SkPaint::kAntiAlias_Flag); + bool didFill = false; + + if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) { + Gradient* fillGradient = 0; + Pattern* fillPattern = 0; + if (context->fillColorSpace() == GradientColorSpace) + fillGradient = context->fillGradient(); + else if (context->fillColorSpace() == PatternColorSpace) + fillPattern = context->fillPattern(); + if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, + context->getCTM(), fillGradient, fillPattern, + &glyphs[0], &advances[0], &offsets[0], numGlyphs)) + return false; + didFill = true; + } + + // Stroking on top (if necessary). + if ((textMode & WebCore::cTextStroke) + && platformContext->getStrokeStyle() != NoStroke + && platformContext->getStrokeThickness() > 0) { + + paint.reset(); + platformContext->setupPaintForStroking(&paint, 0, 0); + paint.setFlags(SkPaint::kAntiAlias_Flag); + if (didFill) { + // If there is a shadow and we filled above, there will already be + // a shadow. We don't want to draw it again or it will be too dark + // and it will go on top of the fill. + // + // Note that this isn't strictly correct, since the stroke could be + // very thick and the shadow wouldn't account for this. The "right" + // thing would be to draw to a new layer and then draw that layer + // with a shadow. But this is a lot of extra work for something + // that isn't normally an issue. + paint.setLooper(0)->safeUnref(); + } + + Gradient* strokeGradient = 0; + Pattern* strokePattern = 0; + if (context->strokeColorSpace() == GradientColorSpace) + strokeGradient = context->strokeGradient(); + else if (context->strokeColorSpace() == PatternColorSpace) + strokePattern = context->strokePattern(); + if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, + context->getCTM(), strokeGradient, strokePattern, + &glyphs[0], &advances[0], &offsets[0], numGlyphs)) + return false; + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + return true; +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.h b/WebCore/platform/graphics/skia/SkiaFontWin.h index 2adab39..0e0c953 100644 --- a/WebCore/platform/graphics/skia/SkiaFontWin.h +++ b/WebCore/platform/graphics/skia/SkiaFontWin.h @@ -32,8 +32,12 @@ #define SkiaWinOutlineCache_h #include <windows.h> +#include <usp10.h> +class GraphicsContext; class SkPath; +class SkPoint; +class PlatformContextSkia; namespace WebCore { @@ -49,6 +53,37 @@ private: SkiaWinOutlineCache(); }; +// The functions below are used for more complex font drawing (effects such as +// stroking and more complex transforms) than Windows supports directly. Since +// Windows drawing is faster you should use windowsCanHandleTextDrawing first to +// check if using Skia is required at all. +// Note that the text will look different (no ClearType) so this should only be +// used when necessary. +// +// When you call a Skia* text drawing function, various glyph outlines will be +// cached. As a result, you should call SkiaWinOutlineCache::removePathsForFont +// when the font is destroyed so that the cache does not outlive the font (since +// the HFONTs are recycled). +// +// Remember that Skia's text drawing origin is the baseline, like WebKit, not +// the top, like Windows. + +// Returns true if advanced font rendering is recommended. +bool windowsCanHandleTextDrawing(GraphicsContext* context); + +// Note that the offsets parameter is optional. If not NULL it represents a +// per glyph offset (such as returned by ScriptPlace Windows API function). +// +// Returns true of the text was drawn successfully. False indicates an error +// from Windows. +bool paintSkiaText(GraphicsContext* graphicsContext, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + const SkPoint* origin); + } // namespace WebCore #endif // SkiaWinOutlineCache_h diff --git a/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp index 1e2a194..2d0f9f8 100644 --- a/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp +++ b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp @@ -30,193 +30,28 @@ #include "config.h" #include "TransformationMatrix.h" -#include "FloatRect.h" -#include "IntRect.h" - #include "SkiaUtils.h" namespace WebCore { -TransformationMatrix::TransformationMatrix() -{ - m_transform.reset(); -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double e, double f) -{ - setMatrix(a, b, c, d, e, f); -} - -TransformationMatrix::TransformationMatrix(const SkMatrix& matrix) - : m_transform(matrix) -{ -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double e, double f) -{ - m_transform.reset(); - - m_transform.setScaleX(WebCoreDoubleToSkScalar(a)); - m_transform.setSkewX(WebCoreDoubleToSkScalar(c)); - m_transform.setTranslateX(WebCoreDoubleToSkScalar(e)); - - m_transform.setScaleY(WebCoreDoubleToSkScalar(d)); - m_transform.setSkewY(WebCoreDoubleToSkScalar(b)); - m_transform.setTranslateY(WebCoreDoubleToSkScalar(f)); -} - -void TransformationMatrix::map(double x, double y, double* x2, double* y2) const -{ - SkPoint src, dst; - src.set(WebCoreDoubleToSkScalar(x), WebCoreDoubleToSkScalar(y)); - m_transform.mapPoints(&dst, &src, 1); - - *x2 = SkScalarToDouble(dst.fX); - *y2 = SkScalarToDouble(dst.fY); -} - -IntRect TransformationMatrix::mapRect(const IntRect& src) const -{ - SkRect dst; - m_transform.mapRect(&dst, src); - return enclosingIntRect(dst); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect& src) const -{ - SkRect dst; - m_transform.mapRect(&dst, src); - return dst; -} - -bool TransformationMatrix::isIdentity() const -{ - return m_transform.isIdentity(); -} - -void TransformationMatrix::reset() -{ - m_transform.reset(); -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - m_transform.preScale(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - m_transform.preRotate(WebCoreDoubleToSkScalar(d), 0, 0); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - m_transform.preTranslate(WebCoreDoubleToSkScalar(tx), WebCoreDoubleToSkScalar(ty)); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - m_transform.preSkew(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0); - return *this; -} - -double TransformationMatrix::det() const -{ - return SkScalarToDouble(m_transform.getScaleX()) * SkScalarToDouble(m_transform.getScaleY()) - - SkScalarToDouble(m_transform.getSkewY()) * SkScalarToDouble(m_transform.getSkewX()); -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - TransformationMatrix inverse; - m_transform.invert(&inverse.m_transform); - return inverse; -} - TransformationMatrix::operator SkMatrix() const { - return m_transform; -} - -bool TransformationMatrix::operator==(const TransformationMatrix& m2) const -{ - return m_transform == m2.m_transform; -} + SkMatrix result; -TransformationMatrix &TransformationMatrix::operator*=(const TransformationMatrix& m2) -{ - m_transform.setConcat(m2.m_transform, m_transform); - return *this; -} + result.setScaleX(WebCoreDoubleToSkScalar(a())); + result.setSkewX(WebCoreDoubleToSkScalar(c())); + result.setTranslateX(WebCoreDoubleToSkScalar(e())); -TransformationMatrix TransformationMatrix::operator*(const TransformationMatrix& m2) -{ - TransformationMatrix cat; - cat.m_transform.setConcat(m2.m_transform, m_transform); - return cat; -} + result.setScaleY(WebCoreDoubleToSkScalar(d())); + result.setSkewY(WebCoreDoubleToSkScalar(b())); + result.setTranslateY(WebCoreDoubleToSkScalar(f())); -double TransformationMatrix::a() const -{ - return SkScalarToDouble(m_transform.getScaleX()); -} - -void TransformationMatrix::setA(double a) -{ - m_transform.setScaleX(WebCoreDoubleToSkScalar(a)); -} - -double TransformationMatrix::b() const -{ - return SkScalarToDouble(m_transform.getSkewY()); -} - -void TransformationMatrix::setB(double b) -{ - m_transform.setSkewY(WebCoreDoubleToSkScalar(b)); -} + // FIXME: Set perspective properly. + result.setPerspX(0); + result.setPerspY(0); + result.set(SkMatrix::kMPersp2, SK_Scalar1); -double TransformationMatrix::c() const -{ - return SkScalarToDouble(m_transform.getSkewX()); -} - -void TransformationMatrix::setC(double c) -{ - m_transform.setSkewX(WebCoreDoubleToSkScalar(c)); -} - -double TransformationMatrix::d() const -{ - return SkScalarToDouble(m_transform.getScaleY()); -} - -void TransformationMatrix::setD(double d) -{ - m_transform.setScaleY(WebCoreDoubleToSkScalar(d)); -} - -double TransformationMatrix::e() const -{ - return SkScalarToDouble(m_transform.getTranslateX()); -} - -void TransformationMatrix::setE(double e) -{ - m_transform.setTranslateX(WebCoreDoubleToSkScalar(e)); -} - -double TransformationMatrix::f() const -{ - return SkScalarToDouble(m_transform.getTranslateY()); -} - -void TransformationMatrix::setF(double f) -{ - m_transform.setTranslateY(WebCoreDoubleToSkScalar(f)); + return result; } } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp new file mode 100644 index 0000000..ab3413b --- /dev/null +++ b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Matrix3DTransformOperation.h" + +#include <algorithm> + +using namespace std; + +namespace WebCore { + +PassRefPtr<TransformOperation> Matrix3DTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + // Convert the TransformOperations into matrices + IntSize size; + TransformationMatrix fromT; + TransformationMatrix toT; + if (from) + from->apply(fromT, size); + + apply(toT, size); + + if (blendToIdentity) + swap(fromT, toT); + + toT.blend(fromT, progress); + return Matrix3DTransformOperation::create(toT); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h new file mode 100644 index 0000000..7430dbc --- /dev/null +++ b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Matrix3DTransformOperation_h +#define Matrix3DTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class Matrix3DTransformOperation : public TransformOperation { +public: + static PassRefPtr<Matrix3DTransformOperation> create(const TransformationMatrix& matrix) + { + return adoptRef(new Matrix3DTransformOperation(matrix)); + } + +private: + virtual bool isIdentity() const { return m_matrix.isIdentity(); } + + virtual OperationType getOperationType() const { return MATRIX_3D; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == MATRIX_3D; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + const Matrix3DTransformOperation* m = static_cast<const Matrix3DTransformOperation*>(&o); + return m_matrix == m->m_matrix; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize&) const + { + transform.multLeft(TransformationMatrix(m_matrix)); + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + + Matrix3DTransformOperation(const TransformationMatrix& mat) + { + m_matrix = mat; + } + + TransformationMatrix m_matrix; +}; + +} // namespace WebCore + +#endif // Matrix3DTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp b/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp index 153d96d..4934fa6 100644 --- a/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp @@ -24,6 +24,8 @@ #include <algorithm> +using namespace std; + namespace WebCore { PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) @@ -41,7 +43,7 @@ PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOp } if (blendToIdentity) - std::swap(fromT, toT); + swap(fromT, toT); toT.blend(fromT, progress); return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f()); diff --git a/WebCore/platform/graphics/transforms/MatrixTransformOperation.h b/WebCore/platform/graphics/transforms/MatrixTransformOperation.h index d272229..ee47a11 100644 --- a/WebCore/platform/graphics/transforms/MatrixTransformOperation.h +++ b/WebCore/platform/graphics/transforms/MatrixTransformOperation.h @@ -26,6 +26,7 @@ #define MatrixTransformOperation_h #include "TransformOperation.h" +#include "TransformationMatrix.h" namespace WebCore { @@ -36,8 +37,14 @@ public: return adoptRef(new MatrixTransformOperation(a, b, c, d, e, f)); } + static PassRefPtr<MatrixTransformOperation> create(const TransformationMatrix& t) + { + return adoptRef(new MatrixTransformOperation(t)); + } + private: virtual bool isIdentity() const { return m_a == 1 && m_b == 0 && m_c == 0 && m_d == 1 && m_e == 0 && m_f == 0; } + virtual OperationType getOperationType() const { return MATRIX; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == MATRIX; } @@ -53,7 +60,7 @@ private: virtual bool apply(TransformationMatrix& transform, const IntSize&) const { TransformationMatrix matrix(m_a, m_b, m_c, m_d, m_e, m_f); - transform = matrix * transform; + transform.multLeft(TransformationMatrix(matrix)); return false; } @@ -68,6 +75,16 @@ private: , m_f(f) { } + + MatrixTransformOperation(const TransformationMatrix& t) + : m_a(t.a()) + , m_b(t.b()) + , m_c(t.c()) + , m_d(t.d()) + , m_e(t.e()) + , m_f(t.f()) + { + } double m_a; double m_b; diff --git a/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp new file mode 100644 index 0000000..9fd03a1 --- /dev/null +++ b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PerspectiveTransformOperation.h" + +#include <algorithm> + +using namespace std; + +namespace WebCore { + +PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + if (blendToIdentity) + return PerspectiveTransformOperation::create(m_p + (1. - m_p) * progress); + + const PerspectiveTransformOperation* fromOp = static_cast<const PerspectiveTransformOperation*>(from); + double fromP = fromOp ? fromOp->m_p : 0; + double toP = m_p; + + TransformationMatrix fromT; + TransformationMatrix toT; + fromT.applyPerspective(fromP); + toT.applyPerspective(toP); + toT.blend(fromT, progress); + TransformationMatrix::DecomposedType decomp; + toT.decompose(decomp); + + return PerspectiveTransformOperation::create(decomp.perspectiveZ ? -1.0 / decomp.perspectiveZ : 0.0); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h new file mode 100644 index 0000000..a665f3e --- /dev/null +++ b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PerspectiveTransformOperation_h +#define PerspectiveTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class PerspectiveTransformOperation : public TransformOperation { +public: + static PassRefPtr<PerspectiveTransformOperation> create(double p) + { + return adoptRef(new PerspectiveTransformOperation(p)); + } + +private: + virtual bool isIdentity() const { return m_p == 0; } + virtual OperationType getOperationType() const { return PERSPECTIVE; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == PERSPECTIVE; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + const PerspectiveTransformOperation* p = static_cast<const PerspectiveTransformOperation*>(&o); + return m_p == p->m_p; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize&) const + { + transform.applyPerspective(m_p); + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + + PerspectiveTransformOperation(double p) + : m_p(p) + { + } + + double m_p; +}; + +} // namespace WebCore + +#endif // PerspectiveTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp b/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp index 4887cee..919d174 100644 --- a/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp @@ -22,6 +22,11 @@ #include "config.h" #include "RotateTransformOperation.h" +#include <algorithm> +#include <wtf/MathExtras.h> + +using namespace std; + namespace WebCore { PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) @@ -30,11 +35,61 @@ PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOp return this; if (blendToIdentity) - return RotateTransformOperation::create(m_angle - m_angle * progress, m_type); + return RotateTransformOperation::create(m_x, m_y, m_z, m_angle - m_angle * progress, m_type); const RotateTransformOperation* fromOp = static_cast<const RotateTransformOperation*>(from); - double fromAngle = fromOp ? fromOp->m_angle : 0; - return RotateTransformOperation::create(fromAngle + (m_angle - fromAngle) * progress, m_type); + + // Optimize for single axis rotation + if (!fromOp || (fromOp->m_x == 0 && fromOp->m_y == 0 && fromOp->m_z == 1) || + (fromOp->m_x == 0 && fromOp->m_y == 1 && fromOp->m_z == 0) || + (fromOp->m_x == 1 && fromOp->m_y == 0 && fromOp->m_z == 0)) { + double fromAngle = fromOp ? fromOp->m_angle : 0; + return RotateTransformOperation::create(fromOp ? fromOp->m_x : m_x, + fromOp ? fromOp->m_y : m_y, + fromOp ? fromOp->m_z : m_z, + fromAngle + (m_angle - fromAngle) * progress, m_type); + } + + const RotateTransformOperation* toOp = this; + + // Create the 2 rotation matrices + TransformationMatrix fromT; + TransformationMatrix toT; + fromT.rotate3d((float)(fromOp ? fromOp->m_x : 0), + (float)(fromOp ? fromOp->m_y : 0), + (float)(fromOp ? fromOp->m_z : 1), + (float)(fromOp ? fromOp->m_angle : 0)); + + toT.rotate3d((float)(toOp ? toOp->m_x : 0), + (float)(toOp ? toOp->m_y : 0), + (float)(toOp ? toOp->m_z : 1), + (float)(toOp ? toOp->m_angle : 0)); + + // Blend them + toT.blend(fromT, progress); + + // Extract the result as a quaternion + TransformationMatrix::DecomposedType decomp; + toT.decompose(decomp); + + // Convert that to Axis/Angle form + double x = -decomp.quaternionX; + double y = -decomp.quaternionY; + double z = -decomp.quaternionZ; + double length = sqrt(x * x + y * y + z * z); + double angle = 0; + + if (length > 0.00001) { + x /= length; + y /= length; + z /= length; + angle = rad2deg(acos(decomp.quaternionW) * 2); + } else { + x = 0; + y = 0; + z = 1; + } + return RotateTransformOperation::create(x, y, z, angle, ROTATE_3D); } } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/RotateTransformOperation.h b/WebCore/platform/graphics/transforms/RotateTransformOperation.h index adc6c4c..699ea43 100644 --- a/WebCore/platform/graphics/transforms/RotateTransformOperation.h +++ b/WebCore/platform/graphics/transforms/RotateTransformOperation.h @@ -33,10 +33,19 @@ class RotateTransformOperation : public TransformOperation { public: static PassRefPtr<RotateTransformOperation> create(double angle, OperationType type) { - return adoptRef(new RotateTransformOperation(angle, type)); + return adoptRef(new RotateTransformOperation(0, 0, 1, angle, type)); } + static PassRefPtr<RotateTransformOperation> create(double x, double y, double z, double angle, OperationType type) + { + return adoptRef(new RotateTransformOperation(x, y, z, angle, type)); + } + + double angle() const { return m_angle; } + +private: virtual bool isIdentity() const { return m_angle == 0; } + virtual OperationType getOperationType() const { return m_type; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } @@ -45,26 +54,30 @@ public: if (!isSameType(o)) return false; const RotateTransformOperation* r = static_cast<const RotateTransformOperation*>(&o); - return m_angle == r->m_angle; + return m_x == r->m_x && m_y == r->m_y && m_z == r->m_z && m_angle == r->m_angle; } virtual bool apply(TransformationMatrix& transform, const IntSize& /*borderBoxSize*/) const { - transform.rotate(m_angle); + transform.rotate3d(m_x, m_y, m_z, m_angle); return false; } virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); - double angle() const { return m_angle; } - -private: - RotateTransformOperation(double angle, OperationType type) - : m_angle(angle) + RotateTransformOperation(double x, double y, double z, double angle, OperationType type) + : m_x(x) + , m_y(y) + , m_z(z) + , m_angle(angle) , m_type(type) { + ASSERT(type == ROTATE_X || type == ROTATE_Y || type == ROTATE_Z || type == ROTATE_3D); } + double m_x; + double m_y; + double m_z; double m_angle; OperationType m_type; }; diff --git a/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp b/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp index 49a8fd8..45d119c 100644 --- a/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp @@ -30,12 +30,17 @@ PassRefPtr<TransformOperation> ScaleTransformOperation::blend(const TransformOpe return this; if (blendToIdentity) - return ScaleTransformOperation::create(m_x + (1. - m_x) * progress, m_y + (1. - m_y) * progress, m_type); + return ScaleTransformOperation::create(m_x + (1. - m_x) * progress, + m_y + (1. - m_y) * progress, + m_z + (1. - m_z) * progress, m_type); const ScaleTransformOperation* fromOp = static_cast<const ScaleTransformOperation*>(from); double fromX = fromOp ? fromOp->m_x : 1.; double fromY = fromOp ? fromOp->m_y : 1.; - return ScaleTransformOperation::create(fromX + (m_x - fromX) * progress, fromY + (m_y - fromY) * progress, m_type); + double fromZ = fromOp ? fromOp->m_z : 1.; + return ScaleTransformOperation::create(fromX + (m_x - fromX) * progress, + fromY + (m_y - fromY) * progress, + fromZ + (m_z - fromZ) * progress, m_type); } } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/ScaleTransformOperation.h b/WebCore/platform/graphics/transforms/ScaleTransformOperation.h index 289f8a1..a87bb3b 100644 --- a/WebCore/platform/graphics/transforms/ScaleTransformOperation.h +++ b/WebCore/platform/graphics/transforms/ScaleTransformOperation.h @@ -33,11 +33,21 @@ class ScaleTransformOperation : public TransformOperation { public: static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, OperationType type) { - return adoptRef(new ScaleTransformOperation(sx, sy, type)); + return adoptRef(new ScaleTransformOperation(sx, sy, 1, type)); } + static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, double sz, OperationType type) + { + return adoptRef(new ScaleTransformOperation(sx, sy, sz, type)); + } + + double x() const { return m_x; } + double y() const { return m_y; } + double z() const { return m_z; } + private: - virtual bool isIdentity() const { return m_x == 1 && m_y == 1; } + virtual bool isIdentity() const { return m_x == 1 && m_y == 1 && m_z == 1; } + virtual OperationType getOperationType() const { return m_type; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } @@ -46,26 +56,29 @@ private: if (!isSameType(o)) return false; const ScaleTransformOperation* s = static_cast<const ScaleTransformOperation*>(&o); - return m_x == s->m_x && m_y == s->m_y; + return m_x == s->m_x && m_y == s->m_y && m_z == s->m_z; } virtual bool apply(TransformationMatrix& transform, const IntSize&) const { - transform.scale(m_x, m_y); + transform.scale3d(m_x, m_y, m_z); return false; } virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); - ScaleTransformOperation(double sx, double sy, OperationType type) + ScaleTransformOperation(double sx, double sy, double sz, OperationType type) : m_x(sx) , m_y(sy) + , m_z(sz) , m_type(type) { + ASSERT(type == SCALE_X || type == SCALE_Y || type == SCALE_Z || type == SCALE || type == SCALE_3D); } double m_x; double m_y; + double m_z; OperationType m_type; }; diff --git a/WebCore/platform/graphics/transforms/TransformOperation.h b/WebCore/platform/graphics/transforms/TransformOperation.h index 65a0def..c610c4b 100644 --- a/WebCore/platform/graphics/transforms/TransformOperation.h +++ b/WebCore/platform/graphics/transforms/TransformOperation.h @@ -39,9 +39,16 @@ public: enum OperationType { SCALE_X, SCALE_Y, SCALE, TRANSLATE_X, TRANSLATE_Y, TRANSLATE, - ROTATE, + ROTATE, + ROTATE_Z = ROTATE, SKEW_X, SKEW_Y, SKEW, - MATRIX, IDENTITY, NONE + MATRIX, + SCALE_Z, SCALE_3D, + TRANSLATE_Z, TRANSLATE_3D, + ROTATE_X, ROTATE_Y, ROTATE_3D, + MATRIX_3D, + PERSPECTIVE, + IDENTITY, NONE }; virtual ~TransformOperation() { } @@ -51,12 +58,27 @@ public: virtual bool isIdentity() const = 0; + // Return true if the borderBoxSize was used in the computation, false otherwise. virtual bool apply(TransformationMatrix&, const IntSize& borderBoxSize) const = 0; virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) = 0; virtual OperationType getOperationType() const = 0; virtual bool isSameType(const TransformOperation&) const { return false; } + + bool is3DOperation() const + { + OperationType opType = getOperationType(); + return opType == SCALE_Z || + opType == SCALE_3D || + opType == TRANSLATE_Z || + opType == TRANSLATE_3D || + opType == ROTATE_X || + opType == ROTATE_Y || + opType == ROTATE_3D || + opType == MATRIX_3D || + opType == PERSPECTIVE; + } }; } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/TransformOperations.h b/WebCore/platform/graphics/transforms/TransformOperations.h index f929417..11605e8 100644 --- a/WebCore/platform/graphics/transforms/TransformOperations.h +++ b/WebCore/platform/graphics/transforms/TransformOperations.h @@ -47,6 +47,16 @@ public: m_operations[i]->apply(t, sz); } + // Return true if any of the operation types are 3D operation types (even if the + // values describe affine transforms) + bool has3DOperation() const + { + for (unsigned i = 0; i < m_operations.size(); ++i) + if (m_operations[i]->is3DOperation()) + return true; + return false; + } + Vector<RefPtr<TransformOperation> >& operations() { return m_operations; } const Vector<RefPtr<TransformOperation> >& operations() const { return m_operations; } diff --git a/WebCore/platform/graphics/transforms/TransformationMatrix.cpp b/WebCore/platform/graphics/transforms/TransformationMatrix.cpp index b48d572..4bcfdb2 100644 --- a/WebCore/platform/graphics/transforms/TransformationMatrix.cpp +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "TransformationMatrix.h" +#include "FloatPoint3D.h" #include "FloatRect.h" #include "FloatQuad.h" #include "IntRect.h" @@ -34,76 +35,467 @@ namespace WebCore { -static void affineTransformDecompose(const TransformationMatrix& matrix, double sr[9]) +// +// Supporting Math Functions +// +// This is a set of function from various places (attributed inline) to do things like +// inversion and decomposition of a 4x4 matrix. They are used throughout the code +// + +// +// Adapted from Matrix Inversion by Richard Carling, Graphics Gems <http://tog.acm.org/GraphicsGems/index.html>. + +// EULA: The Graphics Gems code is copyright-protected. In other words, you cannot claim the text of the code +// as your own and resell it. Using the code is permitted in any program, product, or library, non-commercial +// or commercial. Giving credit is not required, though is a nice gesture. The code comes as-is, and if there +// are any flaws or problems with any Gems code, nobody involved with Gems - authors, editors, publishers, or +// webmasters - are to be held responsible. Basically, don't be a jerk, and remember that anything free comes +// with no guarantee. + +typedef double Vector4[4]; +typedef double Vector3[3]; + +const double SMALL_NUMBER = 1.e-8; + +// inverse(original_matrix, inverse_matrix) +// +// calculate the inverse of a 4x4 matrix +// +// -1 +// A = ___1__ adjoint A +// det A + +// double = determinant2x2(double a, double b, double c, double d) +// +// calculate the determinant of a 2x2 matrix. + +static double determinant2x2(double a, double b, double c, double d) { - TransformationMatrix m(matrix); + return a * d - b * c; +} - // Compute scaling factors - double sx = sqrt(m.a() * m.a() + m.b() * m.b()); - double sy = sqrt(m.c() * m.c() + m.d() * m.d()); +// double = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3) +// +// Calculate the determinant of a 3x3 matrix +// in the form +// +// | a1, b1, c1 | +// | a2, b2, c2 | +// | a3, b3, c3 | - /* Compute cross product of transformed unit vectors. If negative, - one axis was flipped. */ +static double determinant3x3(double a1, double a2, double a3, double b1, double b2, double b3, double c1, double c2, double c3) +{ + return a1 * determinant2x2(b2, b3, c2, c3) + - b1 * determinant2x2(a2, a3, c2, c3) + + c1 * determinant2x2(a2, a3, b2, b3); +} - if (m.a() * m.d() - m.c() * m.b() < 0.0) { - // Flip axis with minimum unit vector dot product +// double = determinant4x4(matrix) +// +// calculate the determinant of a 4x4 matrix. - if (m.a() < m.d()) - sx = -sx; - else - sy = -sy; - } +static double determinant4x4(const TransformationMatrix::Matrix4& m) +{ + // Assign to individual variable names to aid selecting + // correct elements + + double a1 = m[0][0]; + double b1 = m[0][1]; + double c1 = m[0][2]; + double d1 = m[0][3]; + + double a2 = m[1][0]; + double b2 = m[1][1]; + double c2 = m[1][2]; + double d2 = m[1][3]; + + double a3 = m[2][0]; + double b3 = m[2][1]; + double c3 = m[2][2]; + double d3 = m[2][3]; + + double a4 = m[3][0]; + double b4 = m[3][1]; + double c4 = m[3][2]; + double d4 = m[3][3]; + + return a1 * determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) + - b1 * determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) + + c1 * determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) + - d1 * determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); +} + +// adjoint( original_matrix, inverse_matrix ) +// +// calculate the adjoint of a 4x4 matrix +// +// Let a denote the minor determinant of matrix A obtained by +// ij +// +// deleting the ith row and jth column from A. +// +// i+j +// Let b = (-1) a +// ij ji +// +// The matrix B = (b ) is the adjoint of A +// ij + +static void adjoint(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result) +{ + // Assign to individual variable names to aid + // selecting correct values + double a1 = matrix[0][0]; + double b1 = matrix[0][1]; + double c1 = matrix[0][2]; + double d1 = matrix[0][3]; - // Remove scale from matrix + double a2 = matrix[1][0]; + double b2 = matrix[1][1]; + double c2 = matrix[1][2]; + double d2 = matrix[1][3]; - m.scale(1.0 / sx, 1.0 / sy); + double a3 = matrix[2][0]; + double b3 = matrix[2][1]; + double c3 = matrix[2][2]; + double d3 = matrix[2][3]; - // Compute rotation + double a4 = matrix[3][0]; + double b4 = matrix[3][1]; + double c4 = matrix[3][2]; + double d4 = matrix[3][3]; - double angle = atan2(m.b(), m.a()); + // Row column labeling reversed since we transpose rows & columns + result[0][0] = determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4); + result[1][0] = - determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4); + result[2][0] = determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4); + result[3][0] = - determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); + + result[0][1] = - determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4); + result[1][1] = determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4); + result[2][1] = - determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4); + result[3][1] = determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4); + + result[0][2] = determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4); + result[1][2] = - determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4); + result[2][2] = determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4); + result[3][2] = - determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4); + + result[0][3] = - determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3); + result[1][3] = determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3); + result[2][3] = - determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3); + result[3][3] = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3); +} + +// Returns false if the matrix is not invertible +static bool inverse(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result) +{ + // Calculate the adjoint matrix + adjoint(matrix, result); - // Remove rotation from matrix + // Calculate the 4x4 determinant + // If the determinant is zero, + // then the inverse matrix is not unique. + double det = determinant4x4(matrix); - m.rotate(rad2deg(-angle)); + if (fabs(det) < SMALL_NUMBER) + return false; - // Return results + // Scale the adjoint matrix to get the inverse - sr[0] = sx; sr[1] = sy; sr[2] = angle; - sr[3] = m.a(); sr[4] = m.b(); - sr[5] = m.c(); sr[6] = m.d(); - sr[7] = m.e(); sr[8] = m.f(); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + result[i][j] = result[i][j] / det; + + return true; } -static void affineTransformCompose(TransformationMatrix& m, const double sr[9]) +// End of code adapted from Matrix Inversion by Richard Carling + +// Perform a decomposition on the passed matrix, return false if unsuccessful +// From Graphics Gems: unmatrix.c + +// Transpose rotation portion of matrix a, return b +static void transposeMatrix4(const TransformationMatrix::Matrix4& a, TransformationMatrix::Matrix4& b) { - m.setA(sr[3]); - m.setB(sr[4]); - m.setC(sr[5]); - m.setD(sr[6]); - m.setE(sr[7]); - m.setF(sr[8]); - m.rotate(rad2deg(sr[2])); - m.scale(sr[0], sr[1]); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + b[i][j] = a[j][i]; } -bool TransformationMatrix::isInvertible() const +// Multiply a homogeneous point by a matrix and return the transformed point +static void v4MulPointByMatrix(const Vector4 p, const TransformationMatrix::Matrix4& m, Vector4 result) { - return det() != 0.0; + result[0] = (p[0] * m[0][0]) + (p[1] * m[1][0]) + + (p[2] * m[2][0]) + (p[3] * m[3][0]); + result[1] = (p[0] * m[0][1]) + (p[1] * m[1][1]) + + (p[2] * m[2][1]) + (p[3] * m[3][1]); + result[2] = (p[0] * m[0][2]) + (p[1] * m[1][2]) + + (p[2] * m[2][2]) + (p[3] * m[3][2]); + result[3] = (p[0] * m[0][3]) + (p[1] * m[1][3]) + + (p[2] * m[2][3]) + (p[3] * m[3][3]); } -TransformationMatrix& TransformationMatrix::multiply(const TransformationMatrix& other) +static double v3Length(Vector3 a) { - return (*this) *= other; + return sqrt((a[0] * a[0]) + (a[1] * a[1]) + (a[2] * a[2])); } -TransformationMatrix& TransformationMatrix::scale(double s) +static void v3Scale(Vector3 v, double desiredLength) { - return scale(s, s); + double len = v3Length(v); + if (len != 0) { + double l = desiredLength / len; + v[0] *= l; + v[1] *= l; + v[2] *= l; + } } -TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy) +static double v3Dot(const Vector3 a, const Vector3 b) { - return scale(sx, sy); + return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); +} + +// Make a linear combination of two vectors and return the result. +// result = (a * ascl) + (b * bscl) +static void v3Combine(const Vector3 a, const Vector3 b, Vector3 result, double ascl, double bscl) +{ + result[0] = (ascl * a[0]) + (bscl * b[0]); + result[1] = (ascl * a[1]) + (bscl * b[1]); + result[2] = (ascl * a[2]) + (bscl * b[2]); +} + +// Return the cross product result = a cross b */ +static void v3Cross(const Vector3 a, const Vector3 b, Vector3 result) +{ + result[0] = (a[1] * b[2]) - (a[2] * b[1]); + result[1] = (a[2] * b[0]) - (a[0] * b[2]); + result[2] = (a[0] * b[1]) - (a[1] * b[0]); +} + +static bool decompose(const TransformationMatrix::Matrix4& mat, TransformationMatrix::DecomposedType& result) +{ + TransformationMatrix::Matrix4 localMatrix; + memcpy(localMatrix, mat, sizeof(TransformationMatrix::Matrix4)); + + // Normalize the matrix. + if (localMatrix[3][3] == 0) + return false; + + int i, j; + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + localMatrix[i][j] /= localMatrix[3][3]; + + // perspectiveMatrix is used to solve for perspective, but it also provides + // an easy way to test for singularity of the upper 3x3 component. + TransformationMatrix::Matrix4 perspectiveMatrix; + memcpy(perspectiveMatrix, localMatrix, sizeof(TransformationMatrix::Matrix4)); + for (i = 0; i < 3; i++) + perspectiveMatrix[i][3] = 0; + perspectiveMatrix[3][3] = 1; + + if (determinant4x4(perspectiveMatrix) == 0) + return false; + + // First, isolate perspective. This is the messiest. + if (localMatrix[0][3] != 0 || localMatrix[1][3] != 0 || localMatrix[2][3] != 0) { + // rightHandSide is the right hand side of the equation. + Vector4 rightHandSide; + rightHandSide[0] = localMatrix[0][3]; + rightHandSide[1] = localMatrix[1][3]; + rightHandSide[2] = localMatrix[2][3]; + rightHandSide[3] = localMatrix[3][3]; + + // Solve the equation by inverting perspectiveMatrix and multiplying + // rightHandSide by the inverse. (This is the easiest way, not + // necessarily the best.) + TransformationMatrix::Matrix4 inversePerspectiveMatrix, transposedInversePerspectiveMatrix; + inverse(perspectiveMatrix, inversePerspectiveMatrix); + transposeMatrix4(inversePerspectiveMatrix, transposedInversePerspectiveMatrix); + + Vector4 perspectivePoint; + v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspectivePoint); + + result.perspectiveX = perspectivePoint[0]; + result.perspectiveY = perspectivePoint[1]; + result.perspectiveZ = perspectivePoint[2]; + result.perspectiveW = perspectivePoint[3]; + + // Clear the perspective partition + localMatrix[0][3] = localMatrix[1][3] = localMatrix[2][3] = 0; + localMatrix[3][3] = 1; + } else { + // No perspective. + result.perspectiveX = result.perspectiveY = result.perspectiveZ = 0; + result.perspectiveW = 1; + } + + // Next take care of translation (easy). + result.translateX = localMatrix[3][0]; + localMatrix[3][0] = 0; + result.translateY = localMatrix[3][1]; + localMatrix[3][1] = 0; + result.translateZ = localMatrix[3][2]; + localMatrix[3][2] = 0; + + // Vector4 type and functions need to be added to the common set. + Vector3 row[3], pdum3; + + // Now get scale and shear. + for (i = 0; i < 3; i++) { + row[i][0] = localMatrix[i][0]; + row[i][1] = localMatrix[i][1]; + row[i][2] = localMatrix[i][2]; + } + + // Compute X scale factor and normalize first row. + result.scaleX = v3Length(row[0]); + v3Scale(row[0], 1.0); + + // Compute XY shear factor and make 2nd row orthogonal to 1st. + result.skewXY = v3Dot(row[0], row[1]); + v3Combine(row[1], row[0], row[1], 1.0, -result.skewXY); + + // Now, compute Y scale and normalize 2nd row. + result.scaleY = v3Length(row[1]); + v3Scale(row[1], 1.0); + result.skewXY /= result.scaleY; + + // Compute XZ and YZ shears, orthogonalize 3rd row. + result.skewXZ = v3Dot(row[0], row[2]); + v3Combine(row[2], row[0], row[2], 1.0, -result.skewXZ); + result.skewYZ = v3Dot(row[1], row[2]); + v3Combine(row[2], row[1], row[2], 1.0, -result.skewYZ); + + // Next, get Z scale and normalize 3rd row. + result.scaleZ = v3Length(row[2]); + v3Scale(row[2], 1.0); + result.skewXZ /= result.scaleZ; + result.skewYZ /= result.scaleZ; + + // At this point, the matrix (in rows[]) is orthonormal. + // Check for a coordinate system flip. If the determinant + // is -1, then negate the matrix and the scaling factors. + v3Cross(row[1], row[2], pdum3); + if (v3Dot(row[0], pdum3) < 0) { + for (i = 0; i < 3; i++) { + result.scaleX *= -1; + row[i][0] *= -1; + row[i][1] *= -1; + row[i][2] *= -1; + } + } + + // Now, get the rotations out, as described in the gem. + + // FIXME - Add the ability to return either quaternions (which are + // easier to recompose with) or Euler angles (rx, ry, rz), which + // are easier for authors to deal with. The latter will only be useful + // when we fix https://bugs.webkit.org/show_bug.cgi?id=23799, so I + // will leave the Euler angle code here for now. + + // ret.rotateY = asin(-row[0][2]); + // if (cos(ret.rotateY) != 0) { + // ret.rotateX = atan2(row[1][2], row[2][2]); + // ret.rotateZ = atan2(row[0][1], row[0][0]); + // } else { + // ret.rotateX = atan2(-row[2][0], row[1][1]); + // ret.rotateZ = 0; + // } + + double s, t, x, y, z, w; + + t = row[0][0] + row[1][1] + row[2][2] + 1.0; + + if (t > 1e-4) { + s = 0.5 / sqrt(t); + w = 0.25 / s; + x = (row[2][1] - row[1][2]) * s; + y = (row[0][2] - row[2][0]) * s; + z = (row[1][0] - row[0][1]) * s; + } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) { + s = sqrt (1.0 + row[0][0] - row[1][1] - row[2][2]) * 2.0; // S=4*qx + x = 0.25 * s; + y = (row[0][1] + row[1][0]) / s; + z = (row[0][2] + row[2][0]) / s; + w = (row[2][1] - row[1][2]) / s; + } else if (row[1][1] > row[2][2]) { + s = sqrt (1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0; // S=4*qy + x = (row[0][1] + row[1][0]) / s; + y = 0.25 * s; + z = (row[1][2] + row[2][1]) / s; + w = (row[0][2] - row[2][0]) / s; + } else { + s = sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0; // S=4*qz + x = (row[0][2] + row[2][0]) / s; + y = (row[1][2] + row[2][1]) / s; + z = 0.25 * s; + w = (row[1][0] - row[0][1]) / s; + } + + result.quaternionX = x; + result.quaternionY = y; + result.quaternionZ = z; + result.quaternionW = w; + + return true; +} + +// Perform a spherical linear interpolation between the two +// passed quaternions with 0 <= t <= 1 +static void slerp(double qa[4], const double qb[4], double t) +{ + double ax, ay, az, aw; + double bx, by, bz, bw; + double cx, cy, cz, cw; + double angle; + double th, invth, scale, invscale; + + ax = qa[0]; ay = qa[1]; az = qa[2]; aw = qa[3]; + bx = qb[0]; by = qb[1]; bz = qb[2]; bw = qb[3]; + + angle = ax * bx + ay * by + az * bz + aw * bw; + + if (angle < 0.0) { + ax = -ax; ay = -ay; + az = -az; aw = -aw; + angle = -angle; + } + + if (angle + 1.0 > .05) { + if (1.0 - angle >= .05) { + th = acos (angle); + invth = 1.0 / sin (th); + scale = sin (th * (1.0 - t)) * invth; + invscale = sin (th * t) * invth; + } else { + scale = 1.0 - t; + invscale = t; + } + } else { + bx = -ay; + by = ax; + bz = -aw; + bw = az; + scale = sin(piDouble * (.5 - t)); + invscale = sin (piDouble * t); + } + + cx = ax * scale + bx * invscale; + cy = ay * scale + by * invscale; + cz = az * scale + bz * invscale; + cw = aw * scale + bw * invscale; + + qa[0] = cx; qa[1] = cy; qa[2] = cz; qa[3] = cw; +} + +// End of Supporting Math Functions + +TransformationMatrix& TransformationMatrix::scale(double s) +{ + return scaleNonUniform(s, s); } TransformationMatrix& TransformationMatrix::rotateFromVector(double x, double y) @@ -113,93 +505,574 @@ TransformationMatrix& TransformationMatrix::rotateFromVector(double x, double y) TransformationMatrix& TransformationMatrix::flipX() { - return scale(-1.0f, 1.0f); + return scaleNonUniform(-1.0f, 1.0f); } TransformationMatrix& TransformationMatrix::flipY() { - return scale(1.0f, -1.0f); + return scaleNonUniform(1.0f, -1.0f); } -TransformationMatrix& TransformationMatrix::skew(double angleX, double angleY) +FloatPoint TransformationMatrix::projectPoint(const FloatPoint& p) const { - return shear(tan(deg2rad(angleX)), tan(deg2rad(angleY))); + // This is basically raytracing. We have a point in the destination + // plane with z=0, and we cast a ray parallel to the z-axis from that + // point to find the z-position at which it intersects the z=0 plane + // with the transform applied. Once we have that point we apply the + // inverse transform to find the corresponding point in the source + // space. + // + // Given a plane with normal Pn, and a ray starting at point R0 and + // with direction defined by the vector Rd, we can find the + // intersection point as a distance d from R0 in units of Rd by: + // + // d = -dot (Pn', R0) / dot (Pn', Rd) + + double x = p.x(); + double y = p.y(); + double z = -(m13() * x + m23() * y + m43()) / m33(); + + double outX = x * m11() + y * m21() + z * m31() + m41(); + double outY = x * m12() + y * m22() + z * m32() + m42(); + + double w = x * m14() + y * m24() + z * m34() + m44(); + if (w != 1 && w != 0) { + outX /= w; + outY /= w; + } + + return FloatPoint(static_cast<float>(outX), static_cast<float>(outY)); } -TransformationMatrix& TransformationMatrix::skewX(double angle) +FloatQuad TransformationMatrix::projectQuad(const FloatQuad& q) const { - return shear(tan(deg2rad(angle)), 0.0f); + FloatQuad projectedQuad; + projectedQuad.setP1(projectPoint(q.p1())); + projectedQuad.setP2(projectPoint(q.p2())); + projectedQuad.setP3(projectPoint(q.p3())); + projectedQuad.setP4(projectPoint(q.p4())); + return projectedQuad; } -TransformationMatrix& TransformationMatrix::skewY(double angle) +FloatPoint TransformationMatrix::mapPoint(const FloatPoint& p) const { - return shear(0.0f, tan(deg2rad(angle))); + double x, y; + multVecMatrix(p.x(), p.y(), x, y); + return FloatPoint(static_cast<float>(x), static_cast<float>(y)); } -TransformationMatrix makeMapBetweenRects(const FloatRect& source, const FloatRect& dest) +FloatPoint3D TransformationMatrix::mapPoint(const FloatPoint3D& p) const { - TransformationMatrix transform; - transform.translate(dest.x() - source.x(), dest.y() - source.y()); - transform.scale(dest.width() / source.width(), dest.height() / source.height()); - return transform; + double x, y, z; + multVecMatrix(p.x(), p.y(), p.z(), x, y, z); + return FloatPoint3D(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)); } IntPoint TransformationMatrix::mapPoint(const IntPoint& point) const { - double x2, y2; - map(point.x(), point.y(), &x2, &y2); + double x, y; + multVecMatrix(point.x(), point.y(), x, y); // Round the point. - return IntPoint(lround(x2), lround(y2)); + return IntPoint(lround(x), lround(y)); +} + +IntRect TransformationMatrix::mapRect(const IntRect &rect) const +{ + return enclosingIntRect(mapRect(FloatRect(rect))); } -FloatPoint TransformationMatrix::mapPoint(const FloatPoint& point) const +FloatRect TransformationMatrix::mapRect(const FloatRect& r) const { - double x2, y2; - map(point.x(), point.y(), &x2, &y2); + FloatQuad resultQuad = mapQuad(FloatQuad(r)); + return resultQuad.boundingBox(); +} - return FloatPoint(static_cast<float>(x2), static_cast<float>(y2)); +FloatQuad TransformationMatrix::mapQuad(const FloatQuad& q) const +{ + FloatQuad result; + result.setP1(mapPoint(q.p1())); + result.setP2(mapPoint(q.p2())); + result.setP3(mapPoint(q.p3())); + result.setP4(mapPoint(q.p4())); + return result; } -FloatQuad TransformationMatrix::mapQuad(const FloatQuad& quad) const +TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy) { - // FIXME: avoid 4 seperate library calls. Point mapping really needs - // to be platform-independent code. - return FloatQuad(mapPoint(quad.p1()), - mapPoint(quad.p2()), - mapPoint(quad.p3()), - mapPoint(quad.p4())); + TransformationMatrix mat; + mat.m_matrix[0][0] = sx; + mat.m_matrix[1][1] = sy; + + multLeft(mat); + return *this; } -void TransformationMatrix::blend(const TransformationMatrix& from, double progress) +TransformationMatrix& TransformationMatrix::scale3d(double sx, double sy, double sz) +{ + TransformationMatrix mat; + mat.m_matrix[0][0] = sx; + mat.m_matrix[1][1] = sy; + mat.m_matrix[2][2] = sz; + + multLeft(mat); + return *this; +} + +TransformationMatrix& TransformationMatrix::rotate3d(double x, double y, double z, double angle) +{ + // angles are in degrees. Switch to radians + angle = deg2rad(angle); + + angle /= 2.0f; + double sinA = sin(angle); + double cosA = cos(angle); + double sinA2 = sinA * sinA; + + // normalize + double length = sqrt(x * x + y * y + z * z); + if (length == 0) { + // bad vector, just use something reasonable + x = 0; + y = 0; + z = 1; + } else if (length != 1) { + x /= length; + y /= length; + z /= length; + } + + TransformationMatrix mat; + + // optimize case where axis is along major axis + if (x == 1.0f && y == 0.0f && z == 0.0f) { + mat.m_matrix[0][0] = 1.0f; + mat.m_matrix[0][1] = 0.0f; + mat.m_matrix[0][2] = 0.0f; + mat.m_matrix[1][0] = 0.0f; + mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2; + mat.m_matrix[1][2] = 2.0f * sinA * cosA; + mat.m_matrix[2][0] = 0.0f; + mat.m_matrix[2][1] = -2.0f * sinA * cosA; + mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + } else if (x == 0.0f && y == 1.0f && z == 0.0f) { + mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][1] = 0.0f; + mat.m_matrix[0][2] = -2.0f * sinA * cosA; + mat.m_matrix[1][0] = 0.0f; + mat.m_matrix[1][1] = 1.0f; + mat.m_matrix[1][2] = 0.0f; + mat.m_matrix[2][0] = 2.0f * sinA * cosA; + mat.m_matrix[2][1] = 0.0f; + mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + } else if (x == 0.0f && y == 0.0f && z == 1.0f) { + mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][1] = 2.0f * sinA * cosA; + mat.m_matrix[0][2] = 0.0f; + mat.m_matrix[1][0] = -2.0f * sinA * cosA; + mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2; + mat.m_matrix[1][2] = 0.0f; + mat.m_matrix[2][0] = 0.0f; + mat.m_matrix[2][1] = 0.0f; + mat.m_matrix[2][2] = 1.0f; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + } else { + double x2 = x*x; + double y2 = y*y; + double z2 = z*z; + + mat.m_matrix[0][0] = 1.0f - 2.0f * (y2 + z2) * sinA2; + mat.m_matrix[0][1] = 2.0f * (x * y * sinA2 + z * sinA * cosA); + mat.m_matrix[0][2] = 2.0f * (x * z * sinA2 - y * sinA * cosA); + mat.m_matrix[1][0] = 2.0f * (y * x * sinA2 - z * sinA * cosA); + mat.m_matrix[1][1] = 1.0f - 2.0f * (z2 + x2) * sinA2; + mat.m_matrix[1][2] = 2.0f * (y * z * sinA2 + x * sinA * cosA); + mat.m_matrix[2][0] = 2.0f * (z * x * sinA2 + y * sinA * cosA); + mat.m_matrix[2][1] = 2.0f * (z * y * sinA2 - x * sinA * cosA); + mat.m_matrix[2][2] = 1.0f - 2.0f * (x2 + y2) * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + } + multLeft(mat); + return *this; +} + +TransformationMatrix& TransformationMatrix::rotate3d(double rx, double ry, double rz) +{ + // angles are in degrees. Switch to radians + rx = deg2rad(rx); + ry = deg2rad(ry); + rz = deg2rad(rz); + + TransformationMatrix mat; + + rz /= 2.0f; + double sinA = sin(rz); + double cosA = cos(rz); + double sinA2 = sinA * sinA; + + mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][1] = 2.0f * sinA * cosA; + mat.m_matrix[0][2] = 0.0f; + mat.m_matrix[1][0] = -2.0f * sinA * cosA; + mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2; + mat.m_matrix[1][2] = 0.0f; + mat.m_matrix[2][0] = 0.0f; + mat.m_matrix[2][1] = 0.0f; + mat.m_matrix[2][2] = 1.0f; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + + TransformationMatrix rmat(mat); + + ry /= 2.0f; + sinA = sin(ry); + cosA = cos(ry); + sinA2 = sinA * sinA; + + mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][1] = 0.0f; + mat.m_matrix[0][2] = -2.0f * sinA * cosA; + mat.m_matrix[1][0] = 0.0f; + mat.m_matrix[1][1] = 1.0f; + mat.m_matrix[1][2] = 0.0f; + mat.m_matrix[2][0] = 2.0f * sinA * cosA; + mat.m_matrix[2][1] = 0.0f; + mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + + rmat.multLeft(mat); + + rx /= 2.0f; + sinA = sin(rx); + cosA = cos(rx); + sinA2 = sinA * sinA; + + mat.m_matrix[0][0] = 1.0f; + mat.m_matrix[0][1] = 0.0f; + mat.m_matrix[0][2] = 0.0f; + mat.m_matrix[1][0] = 0.0f; + mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2; + mat.m_matrix[1][2] = 2.0f * sinA * cosA; + mat.m_matrix[2][0] = 0.0f; + mat.m_matrix[2][1] = -2.0f * sinA * cosA; + mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + + rmat.multLeft(mat); + + multLeft(rmat); + return *this; +} + +TransformationMatrix& TransformationMatrix::translate(double tx, double ty) +{ + // FIXME: optimize to avoid matrix copy + TransformationMatrix mat; + mat.m_matrix[3][0] = tx; + mat.m_matrix[3][1] = ty; + + multLeft(mat); + return *this; +} + +TransformationMatrix& TransformationMatrix::translate3d(double tx, double ty, double tz) { - double srA[9], srB[9]; + // FIXME: optimize to avoid matrix copy + TransformationMatrix mat; + mat.m_matrix[3][0] = tx; + mat.m_matrix[3][1] = ty; + mat.m_matrix[3][2] = tz; - affineTransformDecompose(from, srA); - affineTransformDecompose(*this, srB); + multLeft(mat); + return *this; +} - // If x-axis of one is flipped, and y-axis of the other, convert to an unflipped rotation. - if ((srA[0] < 0.0 && srB[1] < 0.0) || (srA[1] < 0.0 && srB[0] < 0.0)) { - srA[0] = -srA[0]; - srA[1] = -srA[1]; - srA[2] += srA[2] < 0 ? piDouble : -piDouble; +TransformationMatrix& TransformationMatrix::translateRight(double tx, double ty) +{ + if (tx != 0) { + m_matrix[0][0] += m_matrix[0][3] * tx; + m_matrix[1][0] += m_matrix[1][3] * tx; + m_matrix[2][0] += m_matrix[2][3] * tx; + m_matrix[3][0] += m_matrix[3][3] * tx; } - // Don't rotate the long way around. - srA[2] = fmod(srA[2], 2.0 * piDouble); - srB[2] = fmod(srB[2], 2.0 * piDouble); + if (ty != 0) { + m_matrix[0][1] += m_matrix[0][3] * ty; + m_matrix[1][1] += m_matrix[1][3] * ty; + m_matrix[2][1] += m_matrix[2][3] * ty; + m_matrix[3][1] += m_matrix[3][3] * ty; + } - if (fabs(srA[2] - srB[2]) > piDouble) { - if (srA[2] > srB[2]) - srA[2] -= piDouble * 2.0; - else - srB[2] -= piDouble * 2.0; + return *this; +} + +TransformationMatrix& TransformationMatrix::translateRight3d(double tx, double ty, double tz) +{ + translateRight(tx, ty); + if (tz != 0) { + m_matrix[0][2] += m_matrix[0][3] * tz; + m_matrix[1][2] += m_matrix[1][3] * tz; + m_matrix[2][2] += m_matrix[2][3] * tz; + m_matrix[3][2] += m_matrix[3][3] * tz; } - for (int i = 0; i < 9; i++) - srA[i] = srA[i] + progress * (srB[i] - srA[i]); + return *this; +} + +TransformationMatrix& TransformationMatrix::skew(double sx, double sy) +{ + // angles are in degrees. Switch to radians + sx = deg2rad(sx); + sy = deg2rad(sy); + + TransformationMatrix mat; + mat.m_matrix[0][1] = tan(sy); // note that the y shear goes in the first row + mat.m_matrix[1][0] = tan(sx); // and the x shear in the second row + + multLeft(mat); + return *this; +} - affineTransformCompose(*this, srA); +TransformationMatrix& TransformationMatrix::applyPerspective(double p) +{ + TransformationMatrix mat; + if (p != 0) + mat.m_matrix[2][3] = -1/p; + + multLeft(mat); + return *this; +} + +// +// *this = mat * *this +// +TransformationMatrix& TransformationMatrix::multLeft(const TransformationMatrix& mat) +{ + Matrix4 tmp; + + tmp[0][0] = (mat.m_matrix[0][0] * m_matrix[0][0] + mat.m_matrix[0][1] * m_matrix[1][0] + + mat.m_matrix[0][2] * m_matrix[2][0] + mat.m_matrix[0][3] * m_matrix[3][0]); + tmp[0][1] = (mat.m_matrix[0][0] * m_matrix[0][1] + mat.m_matrix[0][1] * m_matrix[1][1] + + mat.m_matrix[0][2] * m_matrix[2][1] + mat.m_matrix[0][3] * m_matrix[3][1]); + tmp[0][2] = (mat.m_matrix[0][0] * m_matrix[0][2] + mat.m_matrix[0][1] * m_matrix[1][2] + + mat.m_matrix[0][2] * m_matrix[2][2] + mat.m_matrix[0][3] * m_matrix[3][2]); + tmp[0][3] = (mat.m_matrix[0][0] * m_matrix[0][3] + mat.m_matrix[0][1] * m_matrix[1][3] + + mat.m_matrix[0][2] * m_matrix[2][3] + mat.m_matrix[0][3] * m_matrix[3][3]); + + tmp[1][0] = (mat.m_matrix[1][0] * m_matrix[0][0] + mat.m_matrix[1][1] * m_matrix[1][0] + + mat.m_matrix[1][2] * m_matrix[2][0] + mat.m_matrix[1][3] * m_matrix[3][0]); + tmp[1][1] = (mat.m_matrix[1][0] * m_matrix[0][1] + mat.m_matrix[1][1] * m_matrix[1][1] + + mat.m_matrix[1][2] * m_matrix[2][1] + mat.m_matrix[1][3] * m_matrix[3][1]); + tmp[1][2] = (mat.m_matrix[1][0] * m_matrix[0][2] + mat.m_matrix[1][1] * m_matrix[1][2] + + mat.m_matrix[1][2] * m_matrix[2][2] + mat.m_matrix[1][3] * m_matrix[3][2]); + tmp[1][3] = (mat.m_matrix[1][0] * m_matrix[0][3] + mat.m_matrix[1][1] * m_matrix[1][3] + + mat.m_matrix[1][2] * m_matrix[2][3] + mat.m_matrix[1][3] * m_matrix[3][3]); + + tmp[2][0] = (mat.m_matrix[2][0] * m_matrix[0][0] + mat.m_matrix[2][1] * m_matrix[1][0] + + mat.m_matrix[2][2] * m_matrix[2][0] + mat.m_matrix[2][3] * m_matrix[3][0]); + tmp[2][1] = (mat.m_matrix[2][0] * m_matrix[0][1] + mat.m_matrix[2][1] * m_matrix[1][1] + + mat.m_matrix[2][2] * m_matrix[2][1] + mat.m_matrix[2][3] * m_matrix[3][1]); + tmp[2][2] = (mat.m_matrix[2][0] * m_matrix[0][2] + mat.m_matrix[2][1] * m_matrix[1][2] + + mat.m_matrix[2][2] * m_matrix[2][2] + mat.m_matrix[2][3] * m_matrix[3][2]); + tmp[2][3] = (mat.m_matrix[2][0] * m_matrix[0][3] + mat.m_matrix[2][1] * m_matrix[1][3] + + mat.m_matrix[2][2] * m_matrix[2][3] + mat.m_matrix[2][3] * m_matrix[3][3]); + + tmp[3][0] = (mat.m_matrix[3][0] * m_matrix[0][0] + mat.m_matrix[3][1] * m_matrix[1][0] + + mat.m_matrix[3][2] * m_matrix[2][0] + mat.m_matrix[3][3] * m_matrix[3][0]); + tmp[3][1] = (mat.m_matrix[3][0] * m_matrix[0][1] + mat.m_matrix[3][1] * m_matrix[1][1] + + mat.m_matrix[3][2] * m_matrix[2][1] + mat.m_matrix[3][3] * m_matrix[3][1]); + tmp[3][2] = (mat.m_matrix[3][0] * m_matrix[0][2] + mat.m_matrix[3][1] * m_matrix[1][2] + + mat.m_matrix[3][2] * m_matrix[2][2] + mat.m_matrix[3][3] * m_matrix[3][2]); + tmp[3][3] = (mat.m_matrix[3][0] * m_matrix[0][3] + mat.m_matrix[3][1] * m_matrix[1][3] + + mat.m_matrix[3][2] * m_matrix[2][3] + mat.m_matrix[3][3] * m_matrix[3][3]); + + setMatrix(tmp); + return *this; +} + +void TransformationMatrix::multVecMatrix(double x, double y, double& resultX, double& resultY) const +{ + resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0]; + resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1]; + double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3]; + if (w != 1 && w != 0) { + resultX /= w; + resultY /= w; + } +} + +void TransformationMatrix::multVecMatrix(double x, double y, double z, double& resultX, double& resultY, double& resultZ) const +{ + resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0] + z * m_matrix[2][0]; + resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1] + z * m_matrix[2][1]; + resultZ = m_matrix[3][2] + x * m_matrix[0][2] + y * m_matrix[1][2] + z * m_matrix[2][2]; + double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3] + z * m_matrix[2][3]; + if (w != 1 && w != 0) { + resultX /= w; + resultY /= w; + resultZ /= w; + } +} + +bool TransformationMatrix::isInvertible() const +{ + double det = WebCore::determinant4x4(m_matrix); + + if (fabs(det) < SMALL_NUMBER) + return false; + + return true; +} + +TransformationMatrix TransformationMatrix::inverse() const +{ + TransformationMatrix invMat; + + bool inverted = WebCore::inverse(m_matrix, invMat.m_matrix); + if (!inverted) + return TransformationMatrix(); + + return invMat; +} + +void TransformationMatrix::makeAffine() +{ + m_matrix[0][2] = 0; + m_matrix[0][3] = 0; + + 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][2] = 0; + m_matrix[3][3] = 1; +} + +static inline void blendFloat(double& from, double to, double progress) +{ + if (from != to) + from = from + (to - from) * progress; +} + +void TransformationMatrix::blend(const TransformationMatrix& from, double progress) +{ + if (from.isIdentity() && isIdentity()) + return; + + // decompose + DecomposedType fromDecomp; + DecomposedType toDecomp; + from.decompose(fromDecomp); + decompose(toDecomp); + + // interpolate + blendFloat(fromDecomp.scaleX, toDecomp.scaleX, progress); + blendFloat(fromDecomp.scaleY, toDecomp.scaleY, progress); + blendFloat(fromDecomp.scaleZ, toDecomp.scaleZ, progress); + blendFloat(fromDecomp.skewXY, toDecomp.skewXY, progress); + blendFloat(fromDecomp.skewXZ, toDecomp.skewXZ, progress); + blendFloat(fromDecomp.skewYZ, toDecomp.skewYZ, progress); + blendFloat(fromDecomp.translateX, toDecomp.translateX, progress); + blendFloat(fromDecomp.translateY, toDecomp.translateY, progress); + blendFloat(fromDecomp.translateZ, toDecomp.translateZ, progress); + blendFloat(fromDecomp.perspectiveX, toDecomp.perspectiveX, progress); + blendFloat(fromDecomp.perspectiveY, toDecomp.perspectiveY, progress); + blendFloat(fromDecomp.perspectiveZ, toDecomp.perspectiveZ, progress); + blendFloat(fromDecomp.perspectiveW, toDecomp.perspectiveW, progress); + + slerp(&fromDecomp.quaternionX, &toDecomp.quaternionX, progress); + + // recompose + recompose(fromDecomp); +} + +bool TransformationMatrix::decompose(DecomposedType& decomp) const +{ + if (isIdentity()) { + memset(&decomp, 0, sizeof(decomp)); + decomp.perspectiveW = 1; + decomp.scaleX = 1; + decomp.scaleY = 1; + decomp.scaleZ = 1; + } + + if (!WebCore::decompose(m_matrix, decomp)) + return false; + return true; +} + +void TransformationMatrix::recompose(const DecomposedType& decomp) +{ + makeIdentity(); + + // first apply perspective + m_matrix[0][3] = (float) decomp.perspectiveX; + m_matrix[1][3] = (float) decomp.perspectiveY; + m_matrix[2][3] = (float) decomp.perspectiveZ; + m_matrix[3][3] = (float) decomp.perspectiveW; + + // now translate + translate3d((float) decomp.translateX, (float) decomp.translateY, (float) decomp.translateZ); + + // apply rotation + double xx = decomp.quaternionX * decomp.quaternionX; + double xy = decomp.quaternionX * decomp.quaternionY; + double xz = decomp.quaternionX * decomp.quaternionZ; + double xw = decomp.quaternionX * decomp.quaternionW; + double yy = decomp.quaternionY * decomp.quaternionY; + double yz = decomp.quaternionY * decomp.quaternionZ; + double yw = decomp.quaternionY * decomp.quaternionW; + double zz = decomp.quaternionZ * decomp.quaternionZ; + double zw = decomp.quaternionZ * decomp.quaternionW; + + // Construct a composite rotation matrix from the quaternion values + TransformationMatrix rotationMatrix(1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0, + 2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw), 0, + 2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0, + 0, 0, 0, 1); + + multLeft(rotationMatrix); + + // now apply skew + if (decomp.skewYZ) { + TransformationMatrix tmp; + tmp.setM32((float) decomp.skewYZ); + multLeft(tmp); + } + + if (decomp.skewXZ) { + TransformationMatrix tmp; + tmp.setM31((float) decomp.skewXZ); + multLeft(tmp); + } + + if (decomp.skewXY) { + TransformationMatrix tmp; + tmp.setM21((float) decomp.skewXY); + multLeft(tmp); + } + + // finally, apply scale + scale3d((float) decomp.scaleX, (float) decomp.scaleY, (float) decomp.scaleZ); } } diff --git a/WebCore/platform/graphics/transforms/TransformationMatrix.h b/WebCore/platform/graphics/transforms/TransformationMatrix.h index db121d1..62e4eb8 100644 --- a/WebCore/platform/graphics/transforms/TransformationMatrix.h +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.h @@ -28,112 +28,293 @@ #if PLATFORM(CG) #include <CoreGraphics/CGAffineTransform.h> -typedef CGAffineTransform PlatformTransformationMatrix; -#elif PLATFORM(QT) -#include <QMatrix> -typedef QMatrix PlatformTransformationMatrix; #elif PLATFORM(CAIRO) #include <cairo.h> -typedef cairo_matrix_t PlatformTransformationMatrix; +#elif PLATFORM(QT) +#include <QTransform> #elif PLATFORM(SKIA) || PLATFORM(SGL) -#include "SkMatrix.h" -typedef SkMatrix PlatformTransformationMatrix; +#include <SkMatrix.h> #elif PLATFORM(WX) && USE(WXGC) -#include <wx/defs.h> #include <wx/graphics.h> -typedef wxGraphicsMatrix PlatformTransformationMatrix; #endif +#include <string.h> //for memcpy + namespace WebCore { class IntPoint; class IntRect; class FloatPoint; +class FloatPoint3D; class FloatRect; class FloatQuad; class TransformationMatrix { public: - TransformationMatrix(); - TransformationMatrix(double a, double b, double c, double d, double e, double f); -#if !PLATFORM(WX) || USE(WXGC) - TransformationMatrix(const PlatformTransformationMatrix&); -#endif + typedef double Matrix4[4][4]; - void setMatrix(double a, double b, double c, double d, double e, double f); - void map(double x, double y, double *x2, double *y2) const; + TransformationMatrix() { makeIdentity(); } + TransformationMatrix(const TransformationMatrix& t) { *this = t; } + TransformationMatrix(double a, double b, double c, double d, double e, double f) { setMatrix(a, b, c, d, e, f); } + TransformationMatrix(double m11, double m12, double m13, double m14, + double m21, double m22, double m23, double m24, + double m31, double m32, double m33, double m34, + double m41, double m42, double m43, double m44) + { + setMatrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44); + } - // Rounds the mapped point to the nearest integer value. - IntPoint mapPoint(const IntPoint&) const; + void setMatrix(double a, double b, double c, double d, double e, double f) + { + m_matrix[0][0] = a; m_matrix[0][1] = b; m_matrix[0][2] = 0; m_matrix[0][3] = 0; + m_matrix[1][0] = c; m_matrix[1][1] = d; 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][0] = e; m_matrix[3][1] = f; m_matrix[3][2] = 0; m_matrix[3][3] = 1; + } + + void setMatrix(double m11, double m12, double m13, double m14, + double m21, double m22, double m23, double m24, + double m31, double m32, double m33, double m34, + double m41, double m42, double m43, double m44) + { + m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13; m_matrix[0][3] = m14; + m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23; m_matrix[1][3] = m24; + m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33; m_matrix[2][3] = m34; + m_matrix[3][0] = m41; m_matrix[3][1] = m42; m_matrix[3][2] = m43; m_matrix[3][3] = m44; + } + + TransformationMatrix& operator =(const TransformationMatrix &t) + { + setMatrix(t.m_matrix); + return *this; + } + + TransformationMatrix& makeIdentity() + { + setMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return *this; + } + + bool isIdentity() 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][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && m_matrix[3][3] == 1; + } + + // This form preserves the double math from input to output + void map(double x, double y, double& x2, double& y2) const { multVecMatrix(x, y, x2, y2); } + // Map a 3D point through the transform, returning a 3D point. + FloatPoint3D mapPoint(const FloatPoint3D&) const; + + // Map a 2D point through the transform, returning a 2D point. + // Note that this ignores the z component, effectively projecting the point into the z=0 plane. FloatPoint mapPoint(const FloatPoint&) const; + // Like the version above, except that it rounds the mapped point to the nearest integer value. + IntPoint mapPoint(const IntPoint&) const; + + // If the matrix has 3D components, the z component of the result is + // dropped, effectively projecting the rect into the z=0 plane + FloatRect mapRect(const FloatRect&) const; + // Rounds the resulting mapped rectangle out. This is helpful for bounding // box computations but may not be what is wanted in other contexts. IntRect mapRect(const IntRect&) const; - FloatRect mapRect(const FloatRect&) const; - + // If the matrix has 3D components, the z component of the result is + // dropped, effectively projecting the quad into the z=0 plane FloatQuad mapQuad(const FloatQuad&) const; - bool isIdentity() const; + // Map a point on the z=0 plane into a point on + // the plane with with the transform applied, by extending + // a ray perpendicular to the source plane and computing + // the local x,y position of the point where that ray intersects + // with the destination plane. + FloatPoint projectPoint(const FloatPoint&) const; + // Projects the four corners of the quad + FloatQuad projectQuad(const FloatQuad&) const; - double a() const; - void setA(double a); + double m11() const { return m_matrix[0][0]; } + void setM11(double f) { m_matrix[0][0] = f; } + double m12() const { return m_matrix[0][1]; } + void setM12(double f) { m_matrix[0][1] = f; } + double m13() const { return m_matrix[0][2]; } + void setM13(double f) { m_matrix[0][2] = f; } + double m14() const { return m_matrix[0][3]; } + void setM14(double f) { m_matrix[0][3] = f; } + double m21() const { return m_matrix[1][0]; } + void setM21(double f) { m_matrix[1][0] = f; } + double m22() const { return m_matrix[1][1]; } + void setM22(double f) { m_matrix[1][1] = f; } + double m23() const { return m_matrix[1][2]; } + void setM23(double f) { m_matrix[1][2] = f; } + double m24() const { return m_matrix[1][3]; } + void setM24(double f) { m_matrix[1][3] = f; } + double m31() const { return m_matrix[2][0]; } + void setM31(double f) { m_matrix[2][0] = f; } + double m32() const { return m_matrix[2][1]; } + void setM32(double f) { m_matrix[2][1] = f; } + double m33() const { return m_matrix[2][2]; } + void setM33(double f) { m_matrix[2][2] = f; } + double m34() const { return m_matrix[2][3]; } + void setM34(double f) { m_matrix[2][3] = f; } + double m41() const { return m_matrix[3][0]; } + void setM41(double f) { m_matrix[3][0] = f; } + double m42() const { return m_matrix[3][1]; } + void setM42(double f) { m_matrix[3][1] = f; } + double m43() const { return m_matrix[3][2]; } + void setM43(double f) { m_matrix[3][2] = f; } + double m44() const { return m_matrix[3][3]; } + void setM44(double f) { m_matrix[3][3] = f; } + + double a() const { return m_matrix[0][0]; } + void setA(double a) { m_matrix[0][0] = a; } - double b() const; - void setB(double b); + double b() const { return m_matrix[0][1]; } + void setB(double b) { m_matrix[0][1] = b; } - double c() const; - void setC(double c); + double c() const { return m_matrix[1][0]; } + void setC(double c) { m_matrix[1][0] = c; } - double d() const; - void setD(double d); + double d() const { return m_matrix[1][1]; } + void setD(double d) { m_matrix[1][1] = d; } - double e() const; - void setE(double e); + double e() const { return m_matrix[3][0]; } + void setE(double e) { m_matrix[3][0] = e; } - double f() const; - void setF(double f); + double f() const { return m_matrix[3][1]; } + void setF(double f) { m_matrix[3][1] = f; } - void reset(); + // this = this * mat + TransformationMatrix& multiply(const TransformationMatrix& t) { return *this *= t; } - TransformationMatrix& multiply(const TransformationMatrix&); + // this = mat * this + TransformationMatrix& multLeft(const TransformationMatrix& mat); + TransformationMatrix& scale(double); - TransformationMatrix& scale(double sx, double sy); TransformationMatrix& scaleNonUniform(double sx, double sy); - TransformationMatrix& rotate(double d); + TransformationMatrix& scale3d(double sx, double sy, double sz); + + TransformationMatrix& rotate(double d) { return rotate3d(0, 0, d); } TransformationMatrix& rotateFromVector(double x, double y); + TransformationMatrix& rotate3d(double rx, double ry, double rz); + + // The vector (x,y,z) is normalized if it's not already. A vector of + // (0,0,0) uses a vector of (0,0,1). + TransformationMatrix& rotate3d(double x, double y, double z, double angle); + TransformationMatrix& translate(double tx, double ty); - TransformationMatrix& shear(double sx, double sy); + TransformationMatrix& translate3d(double tx, double ty, double tz); + + // translation added with a post-multiply + TransformationMatrix& translateRight(double tx, double ty); + TransformationMatrix& translateRight3d(double tx, double ty, double tz); + TransformationMatrix& flipX(); TransformationMatrix& flipY(); TransformationMatrix& skew(double angleX, double angleY); - TransformationMatrix& skewX(double angle); - TransformationMatrix& skewY(double angle); + TransformationMatrix& skewX(double angle) { return skew(angle, 0); } + TransformationMatrix& skewY(double angle) { return skew(0, angle); } + + TransformationMatrix& applyPerspective(double p); + bool hasPerspective() const { return m_matrix[2][3] != 0.0f; } - double det() const; bool isInvertible() const; + + // This method returns the identity matrix if it is not invertible. + // Use isInvertible() before calling this if you need to know. TransformationMatrix inverse() const; + // decompose the matrix into its component parts + typedef struct { + double scaleX, scaleY, scaleZ; + double skewXY, skewXZ, skewYZ; + double quaternionX, quaternionY, quaternionZ, quaternionW; + double translateX, translateY, translateZ; + double perspectiveX, perspectiveY, perspectiveZ, perspectiveW; + } DecomposedType; + + bool decompose(DecomposedType& decomp) const; + void recompose(const DecomposedType& decomp); + void blend(const TransformationMatrix& from, double progress); -#if !PLATFORM(WX) || USE(WXGC) - operator PlatformTransformationMatrix() const; -#endif + bool isAffine() const + { + return (m13() == 0 && m14() == 0 && m23() == 0 && m24() == 0 && + m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && m43() == 0 && m44() == 1); + } + + // Throw away the non-affine parts of the matrix (lossy!) + void makeAffine(); + + bool operator==(const TransformationMatrix& m2) const + { + return (m_matrix[0][0] == m2.m_matrix[0][0] && + m_matrix[0][1] == m2.m_matrix[0][1] && + m_matrix[0][2] == m2.m_matrix[0][2] && + m_matrix[0][3] == m2.m_matrix[0][3] && + m_matrix[1][0] == m2.m_matrix[1][0] && + m_matrix[1][1] == m2.m_matrix[1][1] && + m_matrix[1][2] == m2.m_matrix[1][2] && + m_matrix[1][3] == m2.m_matrix[1][3] && + m_matrix[2][0] == m2.m_matrix[2][0] && + m_matrix[2][1] == m2.m_matrix[2][1] && + m_matrix[2][2] == m2.m_matrix[2][2] && + m_matrix[2][3] == m2.m_matrix[2][3] && + m_matrix[3][0] == m2.m_matrix[3][0] && + m_matrix[3][1] == m2.m_matrix[3][1] && + m_matrix[3][2] == m2.m_matrix[3][2] && + m_matrix[3][3] == m2.m_matrix[3][3]); + } - bool operator==(const TransformationMatrix&) const; bool operator!=(const TransformationMatrix& other) const { return !(*this == other); } - TransformationMatrix& operator*=(const TransformationMatrix&); - TransformationMatrix operator*(const TransformationMatrix&); + + // *this = *this * t (i.e., a multRight) + TransformationMatrix& operator*=(const TransformationMatrix& t) + { + *this = *this * t; + return *this; + } + + // result = *this * t (i.e., a multRight) + TransformationMatrix operator*(const TransformationMatrix& t) + { + TransformationMatrix result = t; + result.multLeft(*this); + return result; + } -private: -#if !PLATFORM(WX) || USE(WXGC) - PlatformTransformationMatrix m_transform; +#if PLATFORM(CG) + operator CGAffineTransform() const; +#elif PLATFORM(CAIRO) + operator cairo_matrix_t() const; +#elif PLATFORM(QT) + operator QTransform() const; +#elif PLATFORM(SKIA) || PLATFORM(SGL) + operator SkMatrix() const; +#elif PLATFORM(WX) && USE(WXGC) + operator wxGraphicsMatrix() const; #endif -}; -TransformationMatrix makeMapBetweenRects(const FloatRect& source, const FloatRect& dest); +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)); + } + + Matrix4 m_matrix; +}; } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp b/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp index 47471c4..a8ad131 100644 --- a/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp @@ -30,12 +30,15 @@ PassRefPtr<TransformOperation> TranslateTransformOperation::blend(const Transfor return this; if (blendToIdentity) - return TranslateTransformOperation::create(Length(m_x.type()).blend(m_x, progress), Length(m_y.type()).blend(m_y, progress), m_type); + return TranslateTransformOperation::create(Length(m_x.type()).blend(m_x, progress), + Length(m_y.type()).blend(m_y, progress), + Length(m_z.type()).blend(m_z, progress), m_type); const TranslateTransformOperation* fromOp = static_cast<const TranslateTransformOperation*>(from); Length fromX = fromOp ? fromOp->m_x : Length(m_x.type()); Length fromY = fromOp ? fromOp->m_y : Length(m_y.type()); - return TranslateTransformOperation::create(m_x.blend(fromX, progress), m_y.blend(fromY, progress), m_type); + Length fromZ = fromOp ? fromOp->m_z : Length(m_z.type()); + return TranslateTransformOperation::create(m_x.blend(fromX, progress), m_y.blend(fromY, progress), m_z.blend(fromZ, progress), m_type); } } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/TranslateTransformOperation.h b/WebCore/platform/graphics/transforms/TranslateTransformOperation.h index 61ccbcc..a66cc3d 100644 --- a/WebCore/platform/graphics/transforms/TranslateTransformOperation.h +++ b/WebCore/platform/graphics/transforms/TranslateTransformOperation.h @@ -34,10 +34,21 @@ class TranslateTransformOperation : public TransformOperation { public: static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, OperationType type) { - return adoptRef(new TranslateTransformOperation(tx, ty, type)); + return adoptRef(new TranslateTransformOperation(tx, ty, Length(0, Fixed), type)); } - virtual bool isIdentity() const { return m_x.calcFloatValue(1) == 0 && m_y.calcFloatValue(1) == 0; } + static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, const Length& tz, OperationType type) + { + return adoptRef(new TranslateTransformOperation(tx, ty, tz, type)); + } + + double x(const IntSize& borderBoxSize) const { return m_x.calcFloatValue(borderBoxSize.width()); } + double y(const IntSize& borderBoxSize) const { return m_y.calcFloatValue(borderBoxSize.height()); } + double z(const IntSize&) const { return m_z.calcFloatValue(1); } + +private: + virtual bool isIdentity() const { return m_x.calcFloatValue(1) == 0 && m_y.calcFloatValue(1) == 0 && m_z.calcFloatValue(1) == 0; } + virtual OperationType getOperationType() const { return m_type; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } @@ -46,27 +57,29 @@ public: if (!isSameType(o)) return false; const TranslateTransformOperation* t = static_cast<const TranslateTransformOperation*>(&o); - return m_x == t->m_x && m_y == t->m_y; + return m_x == t->m_x && m_y == t->m_y && m_z == t->m_z; } virtual bool apply(TransformationMatrix& transform, const IntSize& borderBoxSize) const { - transform.translate(m_x.calcFloatValue(borderBoxSize.width()), m_y.calcFloatValue(borderBoxSize.height())); + transform.translate3d(x(borderBoxSize), y(borderBoxSize), z(borderBoxSize)); return m_x.type() == Percent || m_y.type() == Percent; } virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); -private: - TranslateTransformOperation(const Length& tx, const Length& ty, OperationType type) + TranslateTransformOperation(const Length& tx, const Length& ty, const Length& tz, OperationType type) : m_x(tx) , m_y(ty) + , m_z(tz) , m_type(type) { + ASSERT(type == TRANSLATE_X || type == TRANSLATE_Y || type == TRANSLATE_Z || type == TRANSLATE || type == TRANSLATE_3D); } Length m_x; Length m_y; + Length m_z; OperationType m_type; }; diff --git a/WebCore/platform/graphics/win/FontCGWin.cpp b/WebCore/platform/graphics/win/FontCGWin.cpp index 9ca95f3..feeb2ae 100644 --- a/WebCore/platform/graphics/win/FontCGWin.cpp +++ b/WebCore/platform/graphics/win/FontCGWin.cpp @@ -223,8 +223,6 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); } } else { - RetainPtr<CGMutablePathRef> path(AdoptCF, CGPathCreateMutable()); - XFORM xform; GetWorldTransform(hdc, &xform); TransformationMatrix hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy); @@ -233,16 +231,8 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0)); initialGlyphTransform.tx = 0; initialGlyphTransform.ty = 0; - CGAffineTransform glyphTranslation = CGAffineTransformIdentity; - - for (unsigned i = 0; i < numGlyphs; ++i) { - RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); - CGAffineTransform glyphTransform = CGAffineTransformConcat(initialGlyphTransform, glyphTranslation); - CGPathAddPath(path.get(), &glyphTransform, glyphPath.get()); - glyphTranslation = CGAffineTransformTranslate(glyphTranslation, gdiAdvances[i], 0); - } - CGContextRef cgContext = graphicsContext->platformContext(); + CGContextSaveGState(cgContext); BOOL fontSmoothingEnabled = false; @@ -252,26 +242,36 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData CGContextScaleCTM(cgContext, 1.0, -1.0); CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height())); - if (drawingMode & cTextFill) { - CGContextAddPath(cgContext, path.get()); - CGContextFillPath(cgContext); - if (font->m_syntheticBoldOffset) { - CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); - CGContextAddPath(cgContext, path.get()); + for (unsigned i = 0; i < numGlyphs; ++i) { + RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); + CGContextSaveGState(cgContext); + CGContextConcatCTM(cgContext, initialGlyphTransform); + + if (drawingMode & cTextFill) { + CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); - CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + if (font->m_syntheticBoldOffset) { + CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); + CGContextAddPath(cgContext, glyphPath.get()); + CGContextFillPath(cgContext); + CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + } } - } - if (drawingMode & cTextStroke) { - CGContextAddPath(cgContext, path.get()); - CGContextStrokePath(cgContext); - if (font->m_syntheticBoldOffset) { - CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); - CGContextAddPath(cgContext, path.get()); + if (drawingMode & cTextStroke) { + CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); - CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + if (font->m_syntheticBoldOffset) { + CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); + CGContextAddPath(cgContext, glyphPath.get()); + CGContextStrokePath(cgContext); + CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + } } + + CGContextRestoreGState(cgContext); + CGContextTranslateCTM(cgContext, gdiAdvances[i], 0); } + CGContextRestoreGState(cgContext); } @@ -298,8 +298,7 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); if (font->platformData().useGDI()) { - static bool canUsePlatformNativeGlyphs = wkCanUsePlatformNativeGlyphs(); - if (!canUsePlatformNativeGlyphs || !shouldUseFontSmoothing || (graphicsContext->textDrawingMode() & cTextStroke)) { + if (!shouldUseFontSmoothing || (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 9acc5a0..887bf79 100644 --- a/WebCore/platform/graphics/win/FontCacheWin.cpp +++ b/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -422,7 +422,24 @@ static HFONT createGDIFont(const AtomicString& family, LONG desiredWeight, bool matchData.m_chosen.lfQuality = DEFAULT_QUALITY; matchData.m_chosen.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - return CreateFontIndirect(&matchData.m_chosen); + HFONT result = CreateFontIndirect(&matchData.m_chosen); + if (!result) + return 0; + + HDC dc = GetDC(0); + SaveDC(dc); + SelectObject(dc, result); + WCHAR actualName[LF_FACESIZE]; + GetTextFace(dc, LF_FACESIZE, actualName); + RestoreDC(dc, -1); + ReleaseDC(0, dc); + + if (wcsicmp(matchData.m_chosen.lfFaceName, actualName)) { + DeleteObject(result); + result = 0; + } + + return result; } struct TraitsInFamilyProcData { diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp index ba8afe7..1ac3359 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -65,10 +65,7 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b ASSERT(m_fontReference); ASSERT(T2embedLibrary()); - static bool canUsePlatformNativeGlyphs = wkCanUsePlatformNativeGlyphs(); - LOGFONT _logFont; - - LOGFONT& logFont = canUsePlatformNativeGlyphs ? *static_cast<LOGFONT*>(malloc(sizeof(LOGFONT))) : _logFont; + LOGFONT& logFont = *static_cast<LOGFONT*>(malloc(sizeof(LOGFONT))); if (m_name.isNull()) TTGetNewFontName(&m_fontReference, logFont.lfFaceName, LF_FACESIZE, 0, 0); else @@ -90,8 +87,7 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b logFont.lfWeight = bold ? 700 : 400; HFONT hfont = CreateFontIndirect(&logFont); - if (canUsePlatformNativeGlyphs) - wkSetFontPlatformInfo(m_cgFont, &logFont, free); + wkSetFontPlatformInfo(m_cgFont, &logFont, free); return FontPlatformData(hfont, m_cgFont, size, bold, italic, renderingMode == AlternateRenderingMode); } @@ -120,7 +116,7 @@ size_t getBytesWithOffset(void *info, void* buffer, size_t offset, size_t count) // Streams the concatenation of a header and font data. class EOTStream { public: - EOTStream(const Vector<UInt8, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) + EOTStream(const Vector<uint8_t, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) : m_eotHeader(eotHeader) , m_fontData(fontData) , m_overlayDst(overlayDst) @@ -134,7 +130,7 @@ public: size_t read(void* buffer, size_t count); private: - const Vector<UInt8, 512>& m_eotHeader; + const Vector<uint8_t, 512>& m_eotHeader; const SharedBuffer* m_fontData; size_t m_overlayDst; size_t m_overlaySrc; @@ -210,7 +206,7 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) // TTLoadEmbeddedFont works only with Embedded OpenType (.eot) data, so we need to create an EOT header // and prepend it to the font data. - Vector<UInt8, 512> eotHeader; + Vector<uint8_t, 512> eotHeader; size_t overlayDst; size_t overlaySrc; size_t overlayLength; diff --git a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp index c7e59ab..59f7e5c 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp @@ -120,7 +120,7 @@ void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* ASSERT(m_cgFont); } } - if (m_useGDI && wkCanUsePlatformNativeGlyphs()) { + if (m_useGDI) { LOGFONT* logfont = static_cast<LOGFONT*>(malloc(sizeof(LOGFONT))); GetObject(font, sizeof(*logfont), logfont); wkSetFontPlatformInfo(m_cgFont.get(), logfont, free); diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index dccbe6c..da5b503 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -76,8 +76,6 @@ GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha) } } -bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } - // FIXME: Is it possible to merge getWindowsContext and createWindowsBitmap into a single API // suitable for all clients? HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) @@ -173,16 +171,6 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo m_data->restore(); } -void GraphicsContext::setShouldIncludeChildWindows(bool include) -{ - m_data->m_shouldIncludeChildWindows = include; -} - -bool GraphicsContext::shouldIncludeChildWindows() const -{ - return m_data->m_shouldIncludeChildWindows; -} - GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size) : m_hdc(0) , m_size(size) @@ -265,7 +253,7 @@ void GraphicsContext::drawFocusRing(const Color& color) float radius = (focusRingWidth() - 1) / 2.0f; int offset = radius + focusRingOffset(); - CGColorRef colorRef = color.isValid() ? cgColor(color) : 0; + CGColorRef colorRef = color.isValid() ? createCGColor(color) : 0; CGMutablePathRef focusRingPath = CGPathCreateMutable(); const Vector<IntRect>& rects = focusRingRects(); diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index 892d24a..1980d18 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -87,8 +87,6 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha return hdc; } -bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } - void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { // FIXME: We aren't really doing anything with the 'mayCreateBitmap' flag. This needs diff --git a/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/WebCore/platform/graphics/win/GraphicsContextWin.cpp index b3ebcb0..e4c5b04 100644 --- a/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -43,6 +43,18 @@ namespace WebCore { class SVGResourceImage; +bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } + +void GraphicsContext::setShouldIncludeChildWindows(bool include) +{ + m_data->m_shouldIncludeChildWindows = include; +} + +bool GraphicsContext::shouldIncludeChildWindows() const +{ + return m_data->m_shouldIncludeChildWindows; +} + void GraphicsContextPlatformPrivate::save() { if (!m_hdc) diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index 1b09e1f..c293f49 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -32,7 +32,10 @@ #include "KURL.h" #include "QTMovieWin.h" #include "ScrollView.h" +#include "StringHash.h" +#include <wtf/HashSet.h> #include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> #if DRAW_FRAME_RATE #include "Font.h" @@ -48,16 +51,25 @@ using namespace std; namespace WebCore { -static const double endPointTimerInterval = 0.020; - +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_seekTo(-1) , m_endTime(numeric_limits<float>::infinity()) , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) - , m_endPointTimer(this, &MediaPlayerPrivate::endPointTimerFired) , m_networkState(MediaPlayer::Empty) - , m_readyState(MediaPlayer::DataUnavailable) + , m_readyState(MediaPlayer::HaveNothing) + , m_enabledTrackCount(0) , m_startedPlaying(false) , m_isStreaming(false) #if DRAW_FRAME_RATE @@ -75,7 +87,8 @@ MediaPlayerPrivate::~MediaPlayerPrivate() void MediaPlayerPrivate::load(const String& url) { if (!QTMovieWin::initializeQuickTime()) { - m_networkState = MediaPlayer::LoadFailed; + // FIXME: is this the right error to return? + m_networkState = MediaPlayer::DecodeError; m_player->networkStateChanged(); return; } @@ -84,17 +97,16 @@ void MediaPlayerPrivate::load(const String& url) m_networkState = MediaPlayer::Loading; m_player->networkStateChanged(); } - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } cancelSeek(); - m_endPointTimer.stop(); m_qtMovie.set(new QTMovieWin(this)); m_qtMovie->load(url.characters(), url.length()); - m_qtMovie->setVolume(m_player->m_volume); - m_qtMovie->setVisible(m_player->m_visible); + m_qtMovie->setVolume(m_player->volume()); + m_qtMovie->setVisible(m_player->visible()); } void MediaPlayerPrivate::play() @@ -107,7 +119,6 @@ void MediaPlayerPrivate::play() #endif m_qtMovie->play(); - startEndPointTimerIfNeeded(); } void MediaPlayerPrivate::pause() @@ -119,7 +130,6 @@ void MediaPlayerPrivate::pause() m_timeStoppedPlaying = GetTickCount(); #endif m_qtMovie->pause(); - m_endPointTimer.stop(); } float MediaPlayerPrivate::duration() const @@ -156,11 +166,12 @@ void MediaPlayerPrivate::seek(float time) void MediaPlayerPrivate::doSeek() { float oldRate = m_qtMovie->rate(); - m_qtMovie->setRate(0); + if (oldRate) + m_qtMovie->setRate(0); m_qtMovie->setCurrentTime(m_seekTo); float timeAfterSeek = currentTime(); // restore playback only if not at end, othewise QTMovie will loop - if (timeAfterSeek < duration() && timeAfterSeek < m_endTime) + if (oldRate && timeAfterSeek < duration() && timeAfterSeek < m_endTime) m_qtMovie->setRate(oldRate); cancelSeek(); } @@ -195,22 +206,6 @@ void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) void MediaPlayerPrivate::setEndTime(float time) { m_endTime = time; - startEndPointTimerIfNeeded(); -} - -void MediaPlayerPrivate::startEndPointTimerIfNeeded() -{ - if (m_endTime < duration() && m_startedPlaying && !m_endPointTimer.isActive()) - m_endPointTimer.startRepeating(endPointTimerInterval); -} - -void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*) -{ - float time = currentTime(); - if (time >= m_endTime) { - pause(); - didEnd(); - } } bool MediaPlayerPrivate::paused() const @@ -325,42 +320,44 @@ void MediaPlayerPrivate::updateStates() long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError; - if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData && !m_player->inMediaDocument()) { - unsigned enabledTrackCount; - m_qtMovie->disableUnsupportedTracks(enabledTrackCount); - // FIXME: We should differentiate between load errors and decode errors <rdar://problem/5605692> - if (!enabledTrackCount) + if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata && !m_player->inMediaDocument()) { + m_qtMovie->disableUnsupportedTracks(m_enabledTrackCount); + if (!m_enabledTrackCount) loadState = QTMovieLoadStateError; } // "Loaded" is reserved for fully buffered movies, never the case when streaming if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { - if (m_networkState < MediaPlayer::Loaded) - m_networkState = MediaPlayer::Loaded; - m_readyState = MediaPlayer::CanPlayThrough; + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::HaveEnoughData; } else if (loadState >= QTMovieLoadStatePlaythroughOK) { - if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) - m_networkState = MediaPlayer::LoadedFirstFrame; - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveEnoughData; } else if (loadState >= QTMovieLoadStatePlayable) { - if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) - m_networkState = MediaPlayer::LoadedFirstFrame; - m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::CanPlay : MediaPlayer::DataUnavailable; + // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; } else if (loadState >= QTMovieLoadStateLoaded) { - if (m_networkState < MediaPlayer::LoadedMetaData) - m_networkState = MediaPlayer::LoadedMetaData; - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveMetadata; } else if (loadState > QTMovieLoadStateError) { - if (m_networkState < MediaPlayer::Loading) - m_networkState = MediaPlayer::Loading; - m_readyState = MediaPlayer::DataUnavailable; + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::HaveNothing; } else { - m_networkState = MediaPlayer::LoadFailed; - m_readyState = MediaPlayer::DataUnavailable; + float loaded = maxTimeLoaded(); + if (!loaded) + m_readyState = MediaPlayer::HaveNothing; + + if (!m_enabledTrackCount) + m_networkState = MediaPlayer::FormatError; + else { + // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> + if (loaded > 0) + m_networkState = MediaPlayer::DecodeError; + else + m_readyState = MediaPlayer::HaveNothing; + } } if (seeking()) - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveNothing; if (m_networkState != oldNetworkState) m_player->networkStateChanged(); @@ -371,7 +368,6 @@ void MediaPlayerPrivate::updateStates() void MediaPlayerPrivate::didEnd() { - m_endPointTimer.stop(); m_startedPlaying = false; #if DRAW_FRAME_RATE m_timeStoppedPlaying = GetTickCount(); @@ -380,10 +376,10 @@ void MediaPlayerPrivate::didEnd() m_player->timeChanged(); } -void MediaPlayerPrivate::setRect(const IntRect& r) +void MediaPlayerPrivate::setSize(const IntSize& size) { if (m_qtMovie) - m_qtMovie->setSize(r.width(), r.height()); + m_qtMovie->setSize(size.width(), size.height()); } void MediaPlayerPrivate::setVisible(bool b) @@ -403,7 +399,7 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) #if DRAW_FRAME_RATE if (m_frameCountWhilePlaying > 10) { - Frame* frame = m_player->m_frameView ? m_player->m_frameView->frame() : NULL; + 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; @@ -427,16 +423,30 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) #endif } -void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +static HashSet<String> mimeTypeCache() { - unsigned count = QTMovieWin::countSupportedTypes(); - for (unsigned n = 0; n < count; n++) { - const UChar* character; - unsigned len; - QTMovieWin::getSupportedType(n, character, len); - if (len) - types.add(String(character, len)); + DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ()); + static bool typeListInitialized = false; + + if (!typeListInitialized) { + unsigned count = QTMovieWin::countSupportedTypes(); + for (unsigned n = 0; n < count; n++) { + const UChar* character; + unsigned len; + QTMovieWin::getSupportedType(n, character, len); + if (len) + typeCache.add(String(character, len)); + } + + typeListInitialized = true; } + + return typeCache; +} + +void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +{ + types = mimeTypeCache(); } bool MediaPlayerPrivate::isAvailable() @@ -444,6 +454,13 @@ bool MediaPlayerPrivate::isAvailable() return QTMovieWin::initializeQuickTime(); } +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an + // extended MIME type + return mimeTypeCache().contains(type) ? (!codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; +} + void MediaPlayerPrivate::movieEnded(QTMovieWin* movie) { ASSERT(m_qtMovie.get() == movie); diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h index c4c893c..63aa62b 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,7 +28,7 @@ #if ENABLE(VIDEO) -#include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" #include "Timer.h" #include <QTMovieWin.h> #include <wtf/OwnPtr.h> @@ -44,9 +44,10 @@ class IntSize; class IntRect; class String; -class MediaPlayerPrivate : QTMovieWinClient, Noncopyable { +class MediaPlayerPrivate : public MediaPlayerPrivateInterface, public QTMovieWinClient { public: - MediaPlayerPrivate(MediaPlayer*); + static void registerMediaEngine(MediaEngineRegistrar); + ~MediaPlayerPrivate(); IntSize naturalSize() const; @@ -81,38 +82,42 @@ public: unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect&); + void setSize(const IntSize&); void loadStateChanged(); void didEnd(); void paint(GraphicsContext*, const IntRect&); - static void getSupportedTypes(HashSet<String>& types); - static bool isAvailable(); private: + MediaPlayerPrivate(MediaPlayer*); + void updateStates(); void doSeek(); void cancelSeek(); void seekTimerFired(Timer<MediaPlayerPrivate>*); - void endPointTimerFired(Timer<MediaPlayerPrivate>*); float maxTimeLoaded() const; - void startEndPointTimerIfNeeded(); virtual void movieEnded(QTMovieWin*); virtual void movieLoadStateChanged(QTMovieWin*); virtual void movieTimeChanged(QTMovieWin*); virtual void movieNewImageAvailable(QTMovieWin*); - + + // engine support + static MediaPlayerPrivateInterface* create(MediaPlayer*); + static void getSupportedTypes(HashSet<String>& types); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + MediaPlayer* m_player; OwnPtr<QTMovieWin> m_qtMovie; float m_seekTo; float m_endTime; Timer<MediaPlayerPrivate> m_seekTimer; - Timer<MediaPlayerPrivate> m_endPointTimer; MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; + unsigned m_enabledTrackCount; bool m_startedPlaying; bool m_isStreaming; #if DRAW_FRAME_RATE diff --git a/WebCore/platform/graphics/win/QTMovieWin.cpp b/WebCore/platform/graphics/win/QTMovieWin.cpp index 32aecd3..3f23698 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.cpp +++ b/WebCore/platform/graphics/win/QTMovieWin.cpp @@ -658,6 +658,12 @@ void QTMovieWin::disableUnsupportedTracks(unsigned& enabledTrackCount) continue; if (!allowedTrackTypes->contains(mediaType)) { + + // Different mpeg variants import as different track types so check for the "mpeg + // characteristic" instead of hard coding the (current) list of mpeg media types. + if (GetMovieIndTrackType(m_private->m_movie, 1, 'mpeg', movieTrackCharacteristic | movieTrackEnabledOnly)) + continue; + SetTrackEnabled(currentTrack, false); --enabledTrackCount; } diff --git a/WebCore/platform/graphics/win/QTMovieWinTimer.cpp b/WebCore/platform/graphics/win/QTMovieWinTimer.cpp index d0aa3e6..f6103ea 100644 --- a/WebCore/platform/graphics/win/QTMovieWinTimer.cpp +++ b/WebCore/platform/graphics/win/QTMovieWinTimer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2009 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -51,6 +51,9 @@ static LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wPara processingCustomTimerMessage = true; sharedTimerFiredFunction(); processingCustomTimerMessage = false; + } else if (message == WM_TIMER && wParam == timerID) { + stopSharedTimer(); + sharedTimerFiredFunction(); } else return DefWindowProc(hWnd, message, wParam, lParam); return 0; @@ -79,11 +82,6 @@ void setSharedTimerFiredFunction(void (*f)()) sharedTimerFiredFunction = f; } -static void CALLBACK timerFired(HWND, UINT, UINT_PTR, DWORD) -{ - sharedTimerFiredFunction(); -} - void setSharedTimerFireDelay(double interval) { ASSERT(sharedTimerFiredFunction); @@ -99,29 +97,27 @@ void setSharedTimerFireDelay(double interval) intervalInMS = (unsigned)interval; } - if (timerID) { - KillTimer(0, timerID); - timerID = 0; - } + stopSharedTimer(); + initializeOffScreenTimerWindow(); // We don't allow nested PostMessages, since the custom messages will effectively starve // painting and user input. (Win32 has a tri-level queue with application messages > // user input > WM_PAINT/WM_TIMER.) // In addition, if the queue contains input events that have been there since the last call to // GetQueueStatus, PeekMessage or GetMessage we favor timers. - if (intervalInMS < USER_TIMER_MINIMUM && processingCustomTimerMessage && - !LOWORD(::GetQueueStatus(QS_ALLINPUT))) { + if (intervalInMS < USER_TIMER_MINIMUM + && !processingCustomTimerMessage + && !LOWORD(::GetQueueStatus(QS_ALLINPUT))) { // Windows SetTimer does not allow timeouts smaller than 10ms (USER_TIMER_MINIMUM) - initializeOffScreenTimerWindow(); PostMessage(timerWindowHandle, timerFiredMessage, 0, 0); } else - timerID = SetTimer(0, 0, intervalInMS, timerFired); + timerID = SetTimer(timerWindowHandle, timerFiredMessage, intervalInMS, 0); } void stopSharedTimer() { if (timerID) { - KillTimer(0, timerID); + KillTimer(timerWindowHandle, timerID); timerID = 0; } } diff --git a/WebCore/platform/graphics/wx/FontPlatformData.h b/WebCore/platform/graphics/wx/FontPlatformData.h index d2394dc..c77968a 100644 --- a/WebCore/platform/graphics/wx/FontPlatformData.h +++ b/WebCore/platform/graphics/wx/FontPlatformData.h @@ -30,41 +30,59 @@ #define FontPlatformData_H #include "FontDescription.h" -#include "CString.h" #include "AtomicString.h" +#include "CString.h" #include "StringImpl.h" +#include <wtf/RefPtr.h> #include <wx/defs.h> #include <wx/font.h> namespace WebCore { +class FontHolder: public WTF::RefCounted<FontHolder> +{ +public: + FontHolder() + : m_font(0) + {} + + FontHolder(wxFont* font) + : m_font(font) + {} + + wxFont* font() { return m_font; } + +private: + wxFont* m_font; +}; + class FontPlatformData { public: enum FontState { UNINITIALIZED, DELETED, VALID }; FontPlatformData(WTF::HashTableDeletedValueType) - : m_fontState(DELETED) + : m_fontState(DELETED), + m_font(0) { } ~FontPlatformData(); - FontPlatformData(wxFont f) - : m_font(f) - , m_fontState(VALID) + FontPlatformData(const FontDescription&, const AtomicString&); + FontPlatformData(float size, bool bold, bool italic) + : m_fontState(UNINITIALIZED) + , m_font(0) { - m_fontHash = computeHash(); } - FontPlatformData(const FontDescription&, const AtomicString&); - FontPlatformData() : m_fontState(UNINITIALIZED) + , m_font(0) { } - wxFont font() const { - return m_font; + wxFont* font() const { + return m_font->font(); } unsigned hash() const { @@ -78,32 +96,26 @@ public: } } + unsigned computeHash() const; + bool operator==(const FontPlatformData& other) const { - if (m_fontState == VALID) - return other.m_fontState == VALID && m_font.IsOk() && other.m_font.IsOk() && m_font.IsSameAs(other.m_font); + if (m_font && m_fontState == VALID && other.m_fontState == VALID && other.m_font) { + wxFont* thisFont = m_font->font(); + wxFont* otherFont = other.m_font->font(); + return thisFont->IsOk() && otherFont->IsOk() && thisFont->IsSameAs(*otherFont); + } else return m_fontState == other.m_fontState; } bool isHashTableDeletedValue() const { return m_fontState == DELETED; } - unsigned computeHash() const { - ASSERT(m_font.IsOk()); - - // make a hash that is unique for this font, but not globally unique - that is, - // a font whose properties are equal should generate the same hash - uintptr_t hashCodes[6] = { m_font.GetPointSize(), m_font.GetFamily(), m_font.GetStyle(), - m_font.GetWeight(), m_font.GetUnderlined(), - StringImpl::computeHash(m_font.GetFaceName().mb_str(wxConvUTF8)) }; - - return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); - } + private: - wxFont m_font; - FontState m_fontState; - unsigned m_fontHash; + WTF::RefPtr<FontHolder> m_font; + FontState m_fontState; }; } diff --git a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp index 7162eab..ce5e40e 100644 --- a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp +++ b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp @@ -58,6 +58,9 @@ static wxFontWeight fontWeightToWxFontWeight(FontWeight weight) { if (weight >= FontWeight600) return wxFONTWEIGHT_BOLD; + + if (weight <= FontWeight300) + return wxFONTWEIGHT_LIGHT; return wxFONTWEIGHT_NORMAL; } @@ -78,30 +81,45 @@ FontPlatformData::FontPlatformData(const FontDescription& desc, const AtomicStri // this is a moot issue on Linux and Mac as they only accept the point argument. So, // we use the pixel size constructor on Windows, but we use point size on Linux and Mac. #if __WXMSW__ - m_font = wxFont( wxSize(0, -desc.computedPixelSize()), + m_font = new FontHolder(new wxFont( wxSize(0, -desc.computedPixelSize()), fontFamilyToWxFontFamily(desc.genericFamily()), italicToWxFontStyle(desc.italic()), fontWeightToWxFontWeight(desc.weight()), false, family.string() - ); + ) + ); #else - m_font = wxFont( desc.computedPixelSize(), + m_font = new FontHolder(new wxFont( desc.computedPixelSize(), fontFamilyToWxFontFamily(desc.genericFamily()), italicToWxFontStyle(desc.italic()), fontWeightToWxFontWeight(desc.weight()), false, family.string() - ); + ) + ); #endif m_fontState = VALID; - m_fontHash = computeHash(); } - + +unsigned FontPlatformData::computeHash() const { + wxFont* thisFont = m_font->font(); + ASSERT(thisFont && thisFont->IsOk()); + + // make a hash that is unique for this font, but not globally unique - that is, + // a font whose properties are equal should generate the same hash + uintptr_t hashCodes[6] = { thisFont->GetPointSize(), thisFont->GetFamily(), thisFont->GetStyle(), + thisFont->GetWeight(), thisFont->GetUnderlined(), + StringImpl::computeHash(thisFont->GetFaceName().mb_str(wxConvUTF8)) }; + + return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); +} + FontPlatformData::~FontPlatformData() { m_fontState = UNINITIALIZED; + m_font = 0; } } diff --git a/WebCore/platform/graphics/wx/ImageSourceWx.cpp b/WebCore/platform/graphics/wx/ImageSourceWx.cpp index d523354..fc8ce71 100644 --- a/WebCore/platform/graphics/wx/ImageSourceWx.cpp +++ b/WebCore/platform/graphics/wx/ImageSourceWx.cpp @@ -170,7 +170,7 @@ void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* delete m_decoder; m_decoder = 0; if (data) - setData(data, allDataReceived); + setData(data, allDataReceived); } NativeImagePtr ImageSource::createFrameAtIndex(size_t index) diff --git a/WebCore/platform/graphics/wx/ImageWx.cpp b/WebCore/platform/graphics/wx/ImageWx.cpp index e52e9ff..e1d435e 100644 --- a/WebCore/platform/graphics/wx/ImageWx.cpp +++ b/WebCore/platform/graphics/wx/ImageWx.cpp @@ -237,7 +237,7 @@ void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, c void BitmapImage::checkForSolidColor() { - + m_checkedForSolidColor = true; } void BitmapImage::invalidatePlatformData() diff --git a/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp b/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp index a509933..ab50518 100644 --- a/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp +++ b/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp @@ -45,14 +45,16 @@ namespace WebCore void SimpleFontData::platformInit() { - wxFont font = m_font.font(); - wxFontProperties props = wxFontProperties(&font); - m_ascent = props.GetAscent(); - m_descent = props.GetDescent(); - m_lineSpacing = props.GetLineSpacing(); - m_xHeight = props.GetXHeight(); - m_unitsPerEm = 1; // FIXME! - m_lineGap = props.GetLineGap(); + wxFont *font = m_font.font(); + if (font && font->IsOk()) { + wxFontProperties props = wxFontProperties(font); + m_ascent = props.GetAscent(); + m_descent = props.GetDescent(); + m_lineSpacing = props.GetLineSpacing(); + m_xHeight = props.GetXHeight(); + m_unitsPerEm = 1; // FIXME! + m_lineGap = props.GetLineGap(); + } } void SimpleFontData::platformDestroy() @@ -79,8 +81,8 @@ bool SimpleFontData::containsCharacters(const UChar* characters, int length) con void SimpleFontData::determinePitch() { - if (m_font.font().Ok()) - m_treatAsFixedPitch = m_font.font().IsFixedWidth(); + if (m_font.font() && m_font.font()->Ok()) + m_treatAsFixedPitch = m_font.font()->IsFixedWidth(); else m_treatAsFixedPitch = false; } @@ -89,7 +91,7 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { // TODO: fix this! Make GetTextExtents a method of wxFont in 2.9 int width = 10; - GetTextExtent(m_font.font(), (wxChar)glyph, &width, NULL); + GetTextExtent(*m_font.font(), (wxChar)glyph, &width, NULL); return width; } diff --git a/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp b/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp index e6a02b8..f21dc17 100644 --- a/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp +++ b/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp @@ -37,235 +37,14 @@ namespace WebCore { #if USE(WXGC) -TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) -{ - m_transform = matrix; -} -#endif - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double e, double f) -{ -#if USE(WXGC) - wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); - m_transform = renderer->CreateMatrix(); -#endif - setMatrix(a, b, c, d, e, f); -} - -TransformationMatrix::TransformationMatrix() -{ - // NB: If we ever support using Cairo backend on Win/Mac, this will need to be - // changed somehow (though I'm not sure how as we don't have a reference to the - // graphics context here. -#if USE(WXGC) - wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); - m_transform = renderer->CreateMatrix(); -#endif -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - notImplemented(); - return *this; -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double e, double f) -{ -#if USE(WXGC) - m_transform.Set(a, b, c, d, e, f); -#endif -} - -void TransformationMatrix::map(double x, double y, double *x2, double *y2) const -{ - notImplemented(); -} - -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ -#if USE(WXGC) - double x, y, width, height; - x = rect.x(); - y = rect.y(); - width = rect.width(); - height = rect.height(); - - m_transform.TransformPoint(&x, &y); - m_transform.TransformDistance(&width, &height); - return IntRect(x, y, width, height); -#endif - return IntRect(); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const -{ -#if USE(WXGC) - double x, y, width, height; - x = rect.x(); - y = rect.y(); - width = rect.width(); - height = rect.height(); - - m_transform.TransformPoint(&x, &y); - m_transform.TransformDistance(&width, &height); - return FloatRect(x, y, width, height); -#endif - return FloatRect(); -} - - -TransformationMatrix& TransformationMatrix::scale(double sx, double sy) -{ -#if USE(WXGC) - m_transform.Scale((wxDouble)sx, (wxDouble)sy); -#endif - return *this; -} - -void TransformationMatrix::reset() -{ - notImplemented(); -} - -TransformationMatrix& TransformationMatrix::rotate(double d) -{ -#if USE(WXGC) - m_transform.Rotate((wxDouble)d); -#endif - return *this; -} - -TransformationMatrix& TransformationMatrix::translate(double tx, double ty) -{ -#if USE(WXGC) - m_transform.Translate((wxDouble)tx, (wxDouble)ty); -#endif - return *this; -} - -TransformationMatrix& TransformationMatrix::shear(double sx, double sy) -{ - notImplemented(); - return *this; -} - -TransformationMatrix& TransformationMatrix::operator*=(const TransformationMatrix& other) -{ - notImplemented(); - return *this; -} - -bool TransformationMatrix::operator== (const TransformationMatrix &other) const -{ -#if USE(WXGC) - return m_transform.IsEqual((wxGraphicsMatrix)other); -#else - notImplemented(); - return true; -#endif -} - -TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &other) -{ - notImplemented(); - return *this; //m_transform * other.m_transform; -} - -double TransformationMatrix::det() const -{ - notImplemented(); - return 0; -} - -#if USE(WXGC) TransformationMatrix::operator wxGraphicsMatrix() const { - return m_transform; -} -#endif - -double TransformationMatrix::a() const -{ - double a = 0; -#if USE(WXGC) - m_transform.Get(&a); -#endif - return a; -} - -void TransformationMatrix::setA(double a) -{ - setMatrix(a, b(), c(), d(), e(), f()); -} - -double TransformationMatrix::b() const -{ - double b = 0; -#if USE(WXGC) - m_transform.Get(&b); -#endif - return b; -} - -void TransformationMatrix::setB(double b) -{ - setMatrix(a(), b, c(), d(), e(), f()); -} - -double TransformationMatrix::c() const -{ - double c = 0; -#if USE(WXGC) - m_transform.Get(&c); -#endif - return c; -} - -void TransformationMatrix::setC(double c) -{ - setMatrix(a(), b(), c, d(), e(), f()); -} - -double TransformationMatrix::d() const -{ - double d = 0; -#if USE(WXGC) - m_transform.Get(&d); -#endif - return d; -} - -void TransformationMatrix::setD(double d) -{ - setMatrix(a(), b(), c(), d, e(), f()); -} - -double TransformationMatrix::e() const -{ - double e = 0; -#if USE(WXGC) - m_transform.Get(&e); -#endif - return e; -} - -void TransformationMatrix::setE(double e) -{ - setMatrix(a(), b(), c(), d(), e, f()); + wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); + ASSERT(renderer); + + wxGraphicsMatrix matrix = renderer->CreateMatrix(a(), b(), c(), d(), e(), f()); + return matrix; } - -double TransformationMatrix::f() const -{ - double f = 0; -#if USE(WXGC) - m_transform.Get(&f); #endif - return f; -} - -void TransformationMatrix::setF(double f) -{ - setMatrix(a(), b(), c(), d(), e(), f); -} } |