diff options
Diffstat (limited to 'WebCore/platform/graphics')
47 files changed, 2314 insertions, 614 deletions
diff --git a/WebCore/platform/graphics/Color.cpp b/WebCore/platform/graphics/Color.cpp index d98b202..17a5d38 100644 --- a/WebCore/platform/graphics/Color.cpp +++ b/WebCore/platform/graphics/Color.cpp @@ -178,6 +178,18 @@ Color::Color(const char* name) } } +String Color::serialized() const +{ + if (alpha() == 0xFF) + return String::format("#%02x%02x%02x", red(), green(), blue()); + + // Match Gecko ("0.0" for zero, 5 decimals for anything else) + if (!alpha()) + return String::format("rgba(%u, %u, %u, 0.0)", red(), green(), blue()); + + return String::format("rgba(%u, %u, %u, %.5f)", red(), green(), blue(), alpha() / 255.0f); +} + String Color::name() const { if (alpha() < 0xFF) diff --git a/WebCore/platform/graphics/Color.h b/WebCore/platform/graphics/Color.h index b8d19e0..a6412d6 100644 --- a/WebCore/platform/graphics/Color.h +++ b/WebCore/platform/graphics/Color.h @@ -80,7 +80,11 @@ public: 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*); - + + // Returns the color serialized according to HTML5 + // - http://www.whatwg.org/specs/web-apps/current-work/#serialization-of-a-color + String serialized() const; + String name() const; void setNamedColor(const String&); diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp index 2f7c216..7682011 100644 --- a/WebCore/platform/graphics/Font.cpp +++ b/WebCore/platform/graphics/Font.cpp @@ -26,6 +26,7 @@ #include "FloatRect.h" #include "FontCache.h" +#include "FontTranscoder.h" #include "IntPoint.h" #include "GlyphBuffer.h" #include "WidthIterator.h" @@ -60,6 +61,7 @@ Font::Font() : m_letterSpacing(0) , m_wordSpacing(0) , m_isPlatformFont(false) + , m_needsTranscoding(false) { } @@ -68,6 +70,7 @@ Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) , m_letterSpacing(letterSpacing) , m_wordSpacing(wordSpacing) , m_isPlatformFont(false) + , m_needsTranscoding(fontTranscoder().needsTranscoding(family().family().string())) { } @@ -76,6 +79,7 @@ Font::Font(const FontPlatformData& fontData, bool isPrinterFont) , m_letterSpacing(0) , m_wordSpacing(0) , m_isPlatformFont(true) + , m_needsTranscoding(fontTranscoder().needsTranscoding(family().family().string())) { m_fontDescription.setUsePrinterFont(isPrinterFont); m_fontList->setPlatformFont(fontData); @@ -87,6 +91,7 @@ Font::Font(const Font& other) , m_letterSpacing(other.m_letterSpacing) , m_wordSpacing(other.m_wordSpacing) , m_isPlatformFont(other.m_isPlatformFont) + , m_needsTranscoding(fontTranscoder().needsTranscoding(family().family().string())) { } @@ -97,6 +102,7 @@ Font& Font::operator=(const Font& other) m_letterSpacing = other.m_letterSpacing; m_wordSpacing = other.m_wordSpacing; m_isPlatformFont = other.m_isPlatformFont; + m_needsTranscoding = other.m_needsTranscoding; return *this; } diff --git a/WebCore/platform/graphics/Font.h b/WebCore/platform/graphics/Font.h index ef305c5..6633a50 100644 --- a/WebCore/platform/graphics/Font.h +++ b/WebCore/platform/graphics/Font.h @@ -219,12 +219,15 @@ public: SVGFontElement* svgFont() const; #endif + bool needsTranscoding() const { return m_needsTranscoding; } + private: FontDescription m_fontDescription; mutable RefPtr<FontFallbackList> m_fontList; short m_letterSpacing; short m_wordSpacing; bool m_isPlatformFont; + bool m_needsTranscoding; }; inline Font::~Font() diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index ab88987..4eb4c6b 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -41,6 +41,10 @@ #include <QtGlobal> #endif +#if USE(GSTREAMER) +#include "MediaPlayerPrivateGStreamer.h" +#endif + #if PLATFORM(MAC) #include "MediaPlayerPrivateQTKit.h" #elif OS(WINCE) && !PLATFORM(QT) @@ -48,8 +52,6 @@ #elif PLATFORM(WIN) #include "MediaPlayerPrivateQuickTimeVisualContext.h" #include "MediaPlayerPrivateQuicktimeWin.h" -#elif PLATFORM(GTK) -#include "MediaPlayerPrivateGStreamer.h" #elif PLATFORM(QT) // QtMultimedia support is disabled currently. #if 1 || (QT_VERSION < 0x040700) @@ -167,10 +169,11 @@ static Vector<MediaPlayerFactory*>& installedMediaEngines() enginesQueried = true; #if USE(GSTREAMER) MediaPlayerPrivateGStreamer::registerMediaEngine(addMediaEngine); -#else +#endif + #if PLATFORM(WIN) MediaPlayerPrivateQuickTimeVisualContext::registerMediaEngine(addMediaEngine); -#endif +#elif !PLATFORM(GTK) // FIXME: currently all the MediaEngines are named // MediaPlayerPrivate. This code will need an update when bug // 36663 is adressed. diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 9abbb15..f825b57 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -54,6 +54,8 @@ class QTMovieVisualContext; namespace WebCore { +class GStreamerGWorld; + // Structure that will hold every native // types supported by the current media player. // We have to do that has multiple media players @@ -63,13 +65,15 @@ struct PlatformMedia { None, QTMovieType, QTMovieGWorldType, - QTMovieVisualContextType + QTMovieVisualContextType, + GStreamerGWorldType, } type; union { QTMovie* qtMovie; QTMovieGWorld* qtMovieGWorld; QTMovieVisualContext* qtMovieVisualContext; + GStreamerGWorld* gstreamerGWorld; } media; }; diff --git a/WebCore/platform/graphics/brew/IconBrew.cpp b/WebCore/platform/graphics/brew/IconBrew.cpp new file mode 100644 index 0000000..3345111 --- /dev/null +++ b/WebCore/platform/graphics/brew/IconBrew.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com> + * Copyright (C) 2009-2010 Company 100, Inc. + * + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include "Icon.h" + +#include "GraphicsContext.h" +#include "IntRect.h" +#include "NotImplemented.h" +#include "PlatformString.h" + +namespace WebCore { + +Icon::~Icon() +{ + notImplemented(); +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +{ + notImplemented(); + return 0; +} + +void Icon::paint(GraphicsContext*, const IntRect&) +{ + notImplemented(); +} + +} // namespace WebCore + diff --git a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp index ececd13..53f4a52 100644 --- a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp @@ -128,9 +128,10 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD break; } } - // if we fall out of the loop, it's ok for name to still be 0 - } - else { // convert the name to utf8 + if (!name) + name = ""; + } else { + // convert the name to utf8 s = family.string().utf8(); name = s.data(); } diff --git a/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp b/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp index 259cc0c..095ded2 100644 --- a/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp @@ -101,12 +101,6 @@ GraphicsLayerChromium::GraphicsLayerChromium(GraphicsLayerClient* client) GraphicsLayerChromium::~GraphicsLayerChromium() { - // Clean up the Skia layer. - if (m_layer) - m_layer->removeFromSuperlayer(); - - if (m_transformLayer) - m_transformLayer->removeFromSuperlayer(); } void GraphicsLayerChromium::setName(const String& inName) @@ -485,6 +479,11 @@ void GraphicsLayerChromium::updateLayerPreserves3D() void GraphicsLayerChromium::updateLayerDrawsContent() { + // Since only layers that draw content have a valid context + // we need to call updateGraphicsContext() here to make sure one + // gets created. + m_layer->drawsContentUpdated(); + if (m_drawsContent) m_layer->setNeedsDisplay(); @@ -533,7 +532,8 @@ void GraphicsLayerChromium::updateContentsRect() if (!m_contentsLayer) return; - m_contentsLayer->setPosition(FloatPoint(m_contentsRect.x(), m_contentsRect.y())); + // The position of the layer is the center of quad. + m_contentsLayer->setPosition(FloatPoint(m_contentsRect.x() + m_contentsRect.width() / 2, m_contentsRect.y() + m_contentsRect.height() / 2)); m_contentsLayer->setBounds(IntSize(m_contentsRect.width(), m_contentsRect.height())); } diff --git a/WebCore/platform/graphics/chromium/LayerChromium.cpp b/WebCore/platform/graphics/chromium/LayerChromium.cpp index 05be15c..8fb28ef 100644 --- a/WebCore/platform/graphics/chromium/LayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/LayerChromium.cpp @@ -55,9 +55,10 @@ LayerChromium::LayerChromium(LayerType type, GraphicsLayerChromium* owner) , m_borderWidth(0) , m_borderColor(0, 0, 0, 0) , m_backgroundColor(0, 0, 0, 0) + , m_anchorPoint(0.5, 0.5) , m_anchorPointZ(0) , m_clearsContext(false) - , m_doubleSided(false) + , m_doubleSided(true) , m_edgeAntialiasingMask(0) , m_hidden(false) , m_masksToBounds(false) @@ -69,9 +70,10 @@ LayerChromium::LayerChromium(LayerType type, GraphicsLayerChromium* owner) , m_skiaContext(0) , m_graphicsContext(0) , m_geometryFlipped(false) + , m_contentsDirty(false) , m_contents(0) + , m_hasContext(false) { - updateGraphicsContext(m_backingStoreRect); } LayerChromium::~LayerChromium() @@ -79,14 +81,30 @@ LayerChromium::~LayerChromium() // Our superlayer should be holding a reference to us so there should be no // way for us to be destroyed while we still have a superlayer. ASSERT(!superlayer()); + + // Remove the superlayer reference from all sublayers. + removeAllSublayers(); } -void LayerChromium::updateGraphicsContext(const IntSize& size) +void LayerChromium::updateGraphicsContext() { + // If the layer doesn't draw anything (e.g. it's a container layer) then we + // don't create a canvas / context for it. The root layer is a special + // case as even if it's marked as a container layer it does actually have + // content that it draws. + RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client()); + if (!drawsContent() && !(this == rootLayer())) { + m_graphicsContext.clear(); + m_skiaContext.clear(); + m_canvas.clear(); + m_hasContext = false; + return; + } + #if PLATFORM(SKIA) // Create new canvas and context. OwnPtr takes care of freeing up // the old ones. - m_canvas = new skia::PlatformCanvas(size.width(), size.height(), false); + m_canvas = new skia::PlatformCanvas(m_backingStoreSize.width(), m_backingStoreSize.height(), false); m_skiaContext = new PlatformContextSkia(m_canvas.get()); // This is needed to get text to show up correctly. Without it, @@ -95,31 +113,32 @@ void LayerChromium::updateGraphicsContext(const IntSize& size) m_skiaContext->setDrawingToImageBuffer(true); m_graphicsContext = new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(m_skiaContext.get())); + + m_hasContext = true; + m_contentsDirty = true; #else #error "Need to implement for your platform." #endif - // The backing store allocated for a layer can be smaller than the layer's bounds. - // This is mostly true for the root layer whose backing store is sized based on the visible - // portion of the layer rather than the actual page size. - m_backingStoreRect = size; + + return; +} + +void LayerChromium::drawsContentUpdated() +{ + // Create a drawing context if the layer now draws content + // or delete the existing context if the layer doesn't draw + // content anymore. + updateGraphicsContext(); } void LayerChromium::updateContents() { RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client()); - if (backing && !backing->paintingGoesToWindow()) + if (backing && !backing->paintingGoesToWindow() && drawsContent()) m_owner->paintGraphicsLayerContents(*m_graphicsContext, IntRect(0, 0, m_bounds.width(), m_bounds.height())); -} -void LayerChromium::drawDebugBorder() -{ - m_graphicsContext->setStrokeColor(m_borderColor, DeviceColorSpace); - m_graphicsContext->setStrokeThickness(m_borderWidth); - m_graphicsContext->drawLine(IntPoint(0, 0), IntPoint(m_bounds.width(), 0)); - m_graphicsContext->drawLine(IntPoint(0, 0), IntPoint(0, m_bounds.height())); - m_graphicsContext->drawLine(IntPoint(m_bounds.width(), 0), IntPoint(m_bounds.width(), m_bounds.height())); - m_graphicsContext->drawLine(IntPoint(0, m_bounds.height()), IntPoint(m_bounds.width(), m_bounds.height())); + m_contentsDirty = false; } void LayerChromium::setContents(NativeImagePtr contents) @@ -128,6 +147,7 @@ void LayerChromium::setContents(NativeImagePtr contents) if (m_contents == contents) return; m_contents = contents; + m_contentsDirty = true; } void LayerChromium::setNeedsCommit() @@ -201,28 +221,29 @@ int LayerChromium::indexOfSublayer(const LayerChromium* reference) return -1; } -void LayerChromium::setBackingStoreRect(const IntSize& rect) +// This method can be called to overide the size of the backing store +// used for the layer. It's typically called on the root layer to limit +// its size to the actual visible size. +void LayerChromium::setBackingStoreSize(const IntSize& size) { - if (m_backingStoreRect == rect) + if (m_backingStoreSize == size) return; - updateGraphicsContext(rect); + m_backingStoreSize = size; + updateGraphicsContext(); + setNeedsCommit(); } -void LayerChromium::setBounds(const IntSize& rect) +void LayerChromium::setBounds(const IntSize& size) { - if (rect == m_bounds) + if (m_bounds == size) return; - m_bounds = rect; + m_bounds = size; + m_backingStoreSize = size; // Re-create the canvas and associated contexts. - updateGraphicsContext(m_bounds); - - // Layer contents need to be redrawn as the backing surface - // was recreated above. - updateContents(); - + updateGraphicsContext(); setNeedsCommit(); } @@ -270,8 +291,11 @@ LayerChromium* LayerChromium::superlayer() const void LayerChromium::setNeedsDisplay(const FloatRect& dirtyRect) { - // Redraw the contents of the layer. - updateContents(); + // Simply mark the contents as dirty. The actual redraw will + // happen when it's time to do the compositing. + // FIXME: Should only update the dirty rect instead of marking + // the entire layer dirty. + m_contentsDirty = true; setNeedsCommit(); } diff --git a/WebCore/platform/graphics/chromium/LayerChromium.h b/WebCore/platform/graphics/chromium/LayerChromium.h index 5b93f77..1b807a5 100644 --- a/WebCore/platform/graphics/chromium/LayerChromium.h +++ b/WebCore/platform/graphics/chromium/LayerChromium.h @@ -151,6 +151,7 @@ public: bool geometryFlipped() const { return m_geometryFlipped; } void updateContents(); + bool contentsDirty() { return m_contentsDirty; } void setContents(NativeImagePtr contents); NativeImagePtr contents() const { return m_contents; } @@ -158,9 +159,13 @@ public: skia::PlatformCanvas* platformCanvas() { return m_canvas.get(); } GraphicsContext* graphicsContext() { return m_graphicsContext.get(); } - void setBackingStoreRect(const IntSize&); + void setBackingStoreSize(const IntSize&); - void drawDebugBorder(); + bool drawsContent() { return m_owner && m_owner->drawsContent(); } + + // This method should be called every time the status drawsContent() + // status changes to ensure that the internal graphics context is in sync. + void drawsContentUpdated(); private: LayerChromium(LayerType, GraphicsLayerChromium* owner); @@ -184,7 +189,8 @@ private: // Re-creates the canvas and graphics context. This method // must be called every time the layer is resized. Only layers - void updateGraphicsContext(const IntSize&); + // that do drawing and the root layer get a context. + void updateGraphicsContext(); Vector<RefPtr<LayerChromium> > m_sublayers; LayerChromium* m_superlayer; @@ -195,11 +201,12 @@ private: OwnPtr<PlatformContextSkia> m_skiaContext; OwnPtr<GraphicsContext> m_graphicsContext; #endif + bool m_hasContext; LayerType m_layerType; IntSize m_bounds; - IntSize m_backingStoreRect; + IntSize m_backingStoreSize; FloatPoint m_position; FloatPoint m_anchorPoint; Color m_backgroundColor; @@ -223,6 +230,8 @@ private: bool m_geometryFlipped; bool m_needsDisplayOnBoundsChange; + bool m_contentsDirty; + ContentsGravityType m_contentsGravity; NativeImagePtr m_contents; String m_name; diff --git a/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp b/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp index 722c80c..fbdb9e2 100644 --- a/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp +++ b/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp @@ -34,105 +34,682 @@ #if USE(ACCELERATED_COMPOSITING) #include "LayerRendererChromium.h" +#include "GLES2Context.h" #include "LayerChromium.h" +#include "NotImplemented.h" +#include "Page.h" +#if PLATFORM(SKIA) +#include "NativeImageSkia.h" #include "PlatformContextSkia.h" -#include "skia/ext/platform_canvas.h" +#endif + +#include <GLES2/gl2.h> namespace WebCore { -PassOwnPtr<LayerRendererChromium> LayerRendererChromium::create() +static WTFLogChannel LogLayerRenderer = { 0x00000000, "LayerRenderer", WTFLogChannelOn }; + +static void checkGLError() +{ +#ifndef NDEBUG + GLenum error = glGetError(); + if (error) + LOG_ERROR("GL Error: %d " , error); +#endif +} + +static GLuint loadShader(GLenum type, const char* shaderSource) +{ + GLuint shader = glCreateShader(type); + if (!shader) + return 0; + glShaderSource(shader, 1, &shaderSource, 0); + glCompileShader(shader); + GLint compiled; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + glDeleteShader(shader); + return 0; + } + return shader; +} + +static GLuint loadShaderProgram(const char* vertexShaderSource, const char* fragmentShaderSource) +{ + GLuint vertexShader; + GLuint fragmentShader; + GLuint programObject; + GLint linked; + vertexShader = loadShader(GL_VERTEX_SHADER, vertexShaderSource); + if (!vertexShader) + return 0; + fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderSource); + if (!fragmentShader) { + glDeleteShader(vertexShader); + return 0; + } + programObject = glCreateProgram(); + if (!programObject) + return 0; + glAttachShader(programObject, vertexShader); + glAttachShader(programObject, fragmentShader); + glLinkProgram(programObject); + glGetProgramiv(programObject, GL_LINK_STATUS, &linked); + if (!linked) { + glDeleteProgram(programObject); + return 0; + } + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + return programObject; +} + +static void toGLMatrix(float* flattened, const TransformationMatrix& m) +{ + flattened[0] = m.m11(); + flattened[1] = m.m12(); + flattened[2] = m.m13(); + flattened[3] = m.m14(); + flattened[4] = m.m21(); + flattened[5] = m.m22(); + flattened[6] = m.m23(); + flattened[7] = m.m24(); + flattened[8] = m.m31(); + flattened[9] = m.m32(); + flattened[10] = m.m33(); + flattened[11] = m.m34(); + flattened[12] = m.m41(); + flattened[13] = m.m42(); + flattened[14] = m.m43(); + flattened[15] = m.m44(); +} + +static TransformationMatrix orthoMatrix(float left, float right, float bottom, float top, float nearZ, float farZ) { - return new LayerRendererChromium(); + float deltaX = right - left; + float deltaY = top - bottom; + float deltaZ = farZ - nearZ; + TransformationMatrix ortho; + if (!deltaX || !deltaY || !deltaZ) + return ortho; + ortho.setM11(2.0f / deltaX); + ortho.setM41(-(right + left) / deltaX); + ortho.setM22(2.0f / deltaY); + ortho.setM42(-(top + bottom) / deltaY); + ortho.setM33(-2.0f / deltaZ); + ortho.setM43(-(nearZ + farZ) / deltaZ); + return ortho; } -LayerRendererChromium::LayerRendererChromium() +// Creates a GL texture object to be used for transfering the layer's bitmap into. +static GLuint createLayerTexture() +{ + GLuint textureId = 0; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); + // Do basic linear filtering on resize. + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // NPOT textures in GL ES only work when the wrap mode is set to GL_CLAMP_TO_EDGE. + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return textureId; +} + + +PassOwnPtr<LayerRendererChromium> LayerRendererChromium::create(Page* page) +{ + return new LayerRendererChromium(page); +} + +LayerRendererChromium::LayerRendererChromium(Page* page) : m_rootLayer(0) , m_needsDisplay(false) + , m_layerProgramObject(0) + , m_borderProgramObject(0) + , m_scrollProgramObject(0) + , m_positionLocation(0) + , m_texCoordLocation(1) + , m_page(page) + , m_rootLayerTextureWidth(0) + , m_rootLayerTextureHeight(0) { + m_quadVboIds[Vertices] = m_quadVboIds[LayerElements] = 0; + m_hardwareCompositing = (initGL() && initializeSharedGLObjects()); } LayerRendererChromium::~LayerRendererChromium() { + if (m_hardwareCompositing) { + makeContextCurrent(); + glDeleteBuffers(3, m_quadVboIds); + glDeleteProgram(m_layerProgramObject); + glDeleteProgram(m_scrollProgramObject); + glDeleteProgram(m_borderProgramObject); + } } -void LayerRendererChromium::updateLayerContents() +void LayerRendererChromium::drawTexturedQuad(const TransformationMatrix& matrix, float width, float height, float opacity, bool scrolling) { - if (m_rootLayer) - updateLayerContentsRecursive(m_rootLayer.get()); + static GLfloat glMatrix[16]; + + TransformationMatrix renderMatrix = matrix; + + // Apply a scaling factor to size the quad from 1x1 to its intended size. + renderMatrix.scale3d(width, height, 1); + + // Apply the projection matrix before sending the transform over to the shader. + renderMatrix.multiply(m_projectionMatrix); + + toGLMatrix(&glMatrix[0], renderMatrix); + + int matrixLocation = (scrolling ? m_scrollMatrixLocation : m_matrixLocation); + glUniformMatrix4fv(matrixLocation, 1, false, &glMatrix[0]); + + if (!scrolling) + glUniform1f(m_alphaLocation, opacity); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); } -#if PLATFORM(SKIA) -void LayerRendererChromium::drawLayersInCanvas(skia::PlatformCanvas* canvas, const IntRect& clipRect) + +// Updates the contents of the root layer texture that fall inside the updateRect +// and re-composits all sublayers. +void LayerRendererChromium::drawLayers(const IntRect& updateRect, const IntRect& visibleRect, + const IntRect& contentRect, const IntPoint& scrollPosition) { + ASSERT(m_hardwareCompositing); + if (!m_rootLayer) return; - canvas->save(); - canvas->clipRect(SkRect(clipRect)); + // If the size of the visible area has changed then allocate a new texture + // to store the contents of the root layer and adjust the projection matrix + // and viewport. + makeContextCurrent(); + + glBindTexture(GL_TEXTURE_2D, m_rootLayerTextureId); + + unsigned int visibleRectWidth = visibleRect.width(); + unsigned int visibleRectHeight = visibleRect.height(); + if (visibleRectWidth != m_rootLayerTextureWidth || visibleRectHeight != m_rootLayerTextureHeight) { + m_rootLayerTextureWidth = visibleRect.width(); + m_rootLayerTextureHeight = visibleRect.height(); + + m_projectionMatrix = orthoMatrix(0, visibleRectWidth + 0.5, visibleRectHeight + 0.5, 0, -1000, 1000); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_rootLayerTextureWidth, m_rootLayerTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + + // The GL viewport covers the entire visible area, including the scrollbars. + glViewport(0, 0, visibleRectWidth, visibleRectHeight); + + // The layer, scroll and debug border shaders all use the same vertex attributes + // so we can bind them only once. + glBindBuffer(GL_ARRAY_BUFFER, m_quadVboIds[Vertices]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_quadVboIds[LayerElements]); + GLuint offset = 0; + glVertexAttribPointer(m_positionLocation, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(offset)); + offset += 3 * sizeof(GLfloat); + glVertexAttribPointer(m_texCoordLocation, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(offset)); + glEnableVertexAttribArray(m_positionLocation); + glEnableVertexAttribArray(m_texCoordLocation); + glActiveTexture(GL_TEXTURE0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); - // First composite the root layer into the canvas. - canvas->drawBitmap(m_rootLayer->platformCanvas()->getDevice()->accessBitmap(false), 0, 0, 0); + IntPoint scrollDelta = toPoint(scrollPosition - m_scrollPosition); + // Scroll only when the updateRect contains pixels for the newly uncovered region to avoid flashing. + if ((scrollDelta.x() && updateRect.width() >= abs(scrollDelta.x()) && updateRect.height() >= contentRect.height()) + || (scrollDelta.y() && updateRect.height() >= abs(scrollDelta.y()) && updateRect.width() >= contentRect.width())) { + // Scrolling works as follows: We render a quad with the current root layer contents + // translated by the amount the page has scrolled since the last update and then read the + // pixels of the content area (visible area excluding the scroll bars) back into the + // root layer texture. The newly exposed area is subesquently filled as usual with + // the contents of the updateRect. + TransformationMatrix scrolledLayerMatrix; + scrolledLayerMatrix.translate3d((int)floorf(0.5 * visibleRect.width() + 0.5) - scrollDelta.x(), + (int)floorf(0.5 * visibleRect.height() + 0.5) + scrollDelta.y(), 0); + scrolledLayerMatrix.scale3d(1, -1, 1); + + // Switch shaders to avoid RGB swizzling. + glUseProgram(m_scrollProgramObject); + glUniform1i(m_scrollSamplerLocation, 0); + + drawTexturedQuad(scrolledLayerMatrix, visibleRect.width(), visibleRect.height(), 1, true); + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, contentRect.width(), contentRect.height()); + + checkGLError(); + m_scrollPosition = scrollPosition; + } + + // FIXME: The following check should go away when the compositor renders independently from its own thread. + // Ignore a 1x1 update rect at (0, 0) as that's used a way to kick off a redraw for the compositor. + if (!(!updateRect.x() && !updateRect.y() && updateRect.width() == 1 && updateRect.height() == 1)) { + // Update the root layer texture. + ASSERT((updateRect.x() + updateRect.width() <= m_rootLayerTextureWidth) + && (updateRect.y() + updateRect.height() <= m_rootLayerTextureHeight)); + +#if PLATFORM(SKIA) + // Get the contents of the updated rect. + const SkBitmap bitmap = m_rootLayer->platformCanvas()->getDevice()->accessBitmap(false); + int rootLayerWidth = bitmap.width(); + int rootLayerHeight = bitmap.height(); + ASSERT(rootLayerWidth == updateRect.width() && rootLayerHeight == updateRect.height()); + void* pixels = bitmap.getPixels(); - // Account for the scroll offset before compositing the remaining layers. - // Note that the root layer's painting takes into account the scroll offset already. - canvas->translate(-m_scrollFrame.fLeft, -m_scrollFrame.fTop); + checkGLError(); + // Copy the contents of the updated rect to the root layer texture. + glTexSubImage2D(GL_TEXTURE_2D, 0, updateRect.x(), updateRect.y(), updateRect.width(), updateRect.height(), GL_RGBA, GL_UNSIGNED_BYTE, pixels); + checkGLError(); +#else +#error Must port to your platform +#endif + } - float opacity = 1.0f; + glClearColor(0, 0, 1, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Render the root layer using a quad that takes up the entire visible area of the window. + glUseProgram(m_layerProgramObject); + glUniform1i(m_samplerLocation, 0); + TransformationMatrix layerMatrix; + layerMatrix.translate3d(visibleRect.width() / 2, visibleRect.height() / 2, 0); + drawTexturedQuad(layerMatrix, visibleRect.width(), visibleRect.height(), 1, false); + + // If culling is enabled then we will cull the backface. + glCullFace(GL_BACK); + // The orthographic projection is setup such that Y starts at zero and + // increases going down the page so we need to adjust the winding order of + // front facing triangles. + glFrontFace(GL_CW); + + // The shader used to render layers returns pre-multiplied alpha colors + // so we need to send the blending mode appropriately. + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + checkGLError(); + + // FIXME: Need to prevent composited layers from drawing over the scroll + // bars. + + // FIXME: Sublayers need to be sorted in Z to get the correct transparency effect. + + // Translate all the composited layers by the scroll position. + TransformationMatrix matrix; + matrix.translate3d(-m_scrollPosition.x(), -m_scrollPosition.y(), 0); + float opacity = 1; const Vector<RefPtr<LayerChromium> >& sublayers = m_rootLayer->getSublayers(); for (size_t i = 0; i < sublayers.size(); i++) - drawLayerInCanvasRecursive(canvas, sublayers[i].get(), opacity); + compositeLayersRecursive(sublayers[i].get(), matrix, opacity, visibleRect); - canvas->restore(); + glFlush(); + m_gles2Context->swapBuffers(); m_needsDisplay = false; } -void LayerRendererChromium::drawLayerInCanvasRecursive(skia::PlatformCanvas* canvas, LayerChromium* layer, float opacity) +// Returns the id of the texture currently associated with the layer or +// -1 if the id hasn't been registered yet. +int LayerRendererChromium::getTextureId(LayerChromium* layer) { - // Guarantees that the canvas is restored to a known state on destruction. - SkAutoCanvasRestore autoRestoreCanvas(canvas, true); + TextureIdMap::iterator textureId = m_textureIdMap.find(layer); + if (textureId != m_textureIdMap.end()) + return textureId->second; - FloatPoint position = layer->position(); - FloatPoint anchorPoint = layer->anchorPoint(); - SkMatrix transform = layer->transform().toAffineTransform(); + return -1; +} + +// Allocates a new texture for the layer and registers it in the textureId map. +// FIXME: We will need to come up with a more sophisticated allocation strategy here. +// FIXME: We need to free up the associated texture upon layer destruction. +int LayerRendererChromium::assignTextureForLayer(LayerChromium* layer) +{ + GLuint textureId = createLayerTexture(); + + // FIXME: Check that textureId is valid + m_textureIdMap.set(layer, textureId); + return textureId; +} + +bool LayerRendererChromium::freeLayerTexture(LayerChromium* layer) +{ + TextureIdMap::iterator textureId = m_textureIdMap.find(layer); + if (textureId == m_textureIdMap.end()) + return false; + // Free up the texture. + glDeleteTextures(1, &(textureId->second)); + m_textureIdMap.remove(textureId); + return true; +} + +// Draws a debug border around the layer's bounds. +void LayerRendererChromium::drawDebugBorder(LayerChromium* layer, const TransformationMatrix& matrix) +{ + static GLfloat glMatrix[16]; + Color borderColor = layer->borderColor(); + if (!borderColor.alpha()) + return; + + glUseProgram(m_borderProgramObject); + TransformationMatrix renderMatrix = matrix; IntSize bounds = layer->bounds(); + renderMatrix.scale3d(bounds.width(), bounds.height(), 1); + renderMatrix.multiply(m_projectionMatrix); + toGLMatrix(&glMatrix[0], renderMatrix); + glUniformMatrix4fv(m_borderMatrixLocation, 1, false, &glMatrix[0]); - canvas->translate(position.x(), position.y()); + glUniform4f(m_borderColorLocation, borderColor.red() / 255.0, + borderColor.green() / 255.0, + borderColor.blue() / 255.0, + 1); - SkScalar tx = SkScalarMul(anchorPoint.x(), bounds.width()); - SkScalar ty = SkScalarMul(anchorPoint.y(), bounds.height()); - canvas->translate(tx, ty); - canvas->concat(transform); - canvas->translate(-tx, -ty); + glLineWidth(layer->borderWidth()); - // The position we get is for the center of the layer, but - // drawBitmap starts at the upper-left corner, and therefore - // we need to adjust our transform. - canvas->translate(-0.5f * bounds.width(), -0.5f * bounds.height()); + // The indices for the line are stored in the same array as the triangle indices. + glDrawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, (void*)(6 * sizeof(unsigned short))); + checkGLError(); - layer->drawDebugBorder(); + // Switch back to the shader program used for layer contents. + glUseProgram(m_layerProgramObject); +} + +// Returns true if any part of the layer falls within the visibleRect +bool LayerRendererChromium::isLayerVisible(LayerChromium* layer, const TransformationMatrix& matrix, const IntRect& visibleRect) +{ + // Form the matrix used by the shader to map the corners of the layer's + // bounds into clip space. + TransformationMatrix renderMatrix = matrix; + renderMatrix.scale3d(layer->bounds().width(), layer->bounds().height(), 1); + renderMatrix.multiply(m_projectionMatrix); + + FloatRect layerRect(-0.5, -0.5, 1, 1); + FloatRect mappedRect = renderMatrix.mapRect(layerRect); + + // The layer is visible if it intersects any part of a rectangle whose origin + // is at (-1, -1) and size is 2x2. + return mappedRect.intersects(FloatRect(-1, -1, 2, 2)); +} + +void LayerRendererChromium::compositeLayersRecursive(LayerChromium* layer, const TransformationMatrix& matrix, float opacity, const IntRect& visibleRect) +{ + static GLfloat glMatrix[16]; - SkPaint opacityPaint; + // Compute the new matrix transformation that will be applied to this layer and + // all its sublayers. + // The basic transformation chain for the layer is (using the Matrix x Vector order): + // M = M[p] * T[l] * T[a] * M[l] * T[-a] + // Where M[p] is the parent matrix passed down to the function + // T[l] is the translation of the layer's center + // T[a] and T[-a] is a translation/inverse translation by the anchor point + // M[l] is the layer's matrix + // Note that the final matrix used by the shader for the layer is P * M * S . This final product + // is effectively computed in drawTexturedQuad(). + // Where: P is the projection matrix + // M is the layer's matrix computed above + // S is the scale adjustment (to scale up to the layer size) + IntSize bounds = layer->bounds(); + FloatPoint anchorPoint = layer->anchorPoint(); + FloatPoint position = layer->position(); + float anchorX = (anchorPoint.x() - 0.5) * bounds.width(); + float anchorY = (0.5 - anchorPoint.y()) * bounds.height(); + + // M = M[p] + TransformationMatrix localMatrix = matrix; + // M = M[p] * T[l] + localMatrix.translate3d(position.x(), position.y(), 0); + // M = M[p] * T[l] * T[a] + localMatrix.translate3d(anchorX, anchorY, 0); + // M = M[p] * T[l] * T[a] * M[l] + localMatrix.multLeft(layer->transform()); + // M = M[p] * T[l] * T[a] * M[l] * T[-a] + localMatrix.translate3d(-anchorX, -anchorY, 0); + + bool skipLayer = false; + if (bounds.width() > 2048 || bounds.height() > 2048) { + LOG(LayerRenderer, "Skipping layer with size %d %d", bounds.width(), bounds.height()); + skipLayer = true; + } + + // Calculate the layer's opacity. opacity *= layer->opacity(); - opacityPaint.setAlpha(opacity * 255); - canvas->drawBitmap(layer->platformCanvas()->getDevice()->accessBitmap(false), 0, 0, &opacityPaint); + bool layerVisible = isLayerVisible(layer, localMatrix, visibleRect); + + // Note that there are two types of layers: + // 1. Layers that have their own GraphicsContext and can draw their contents on demand (layer->drawsContent() == true). + // 2. Layers that are just containers of images/video/etc that don't own a GraphicsContext (layer->contents() == true). + if ((layer->drawsContent() || layer->contents()) && !skipLayer && layerVisible) { + int textureId = getTextureId(layer); + // If no texture has been created for the layer yet then create one now. + if (textureId == -1) + textureId = assignTextureForLayer(layer); + + // Redraw the contents of the layer if necessary. + if ((layer->drawsContent() || layer->contents()) && layer->contentsDirty()) { + // Update the contents of the layer before taking a snapshot. For layers that + // are simply containers, the following call just clears the dirty flag but doesn't + // actually do any draws/copies. + layer->updateContents(); + + const SkBitmap* skiaBitmap = 0; + void* pixels = 0; + if (layer->drawsContent()) { // Layer has its own GraphicsContext. + // The contents of the layer are stored in the canvas associated with it. + const SkBitmap& bitmap = layer->platformCanvas()->getDevice()->accessBitmap(false); + skiaBitmap = &bitmap; + } else { // Layer is a container. + // The layer contains an Image. + NativeImageSkia* skiaImage = static_cast<NativeImageSkia*>(layer->contents()); + skiaBitmap = skiaImage; + } + + ASSERT(skiaBitmap); + SkBitmap::Config skiaConfig = skiaBitmap->config(); + // FIXME: must support more image configurations. + if (skiaConfig == SkBitmap::kARGB_8888_Config) { + SkAutoLockPixels lock(*skiaBitmap); + int bitmapWidth = skiaBitmap->width(); + int bitmapHeight = skiaBitmap->height(); + int rowBytes = skiaBitmap->rowBytes(); + ASSERT(rowBytes == bitmapWidth * 4); + + // Copy the layer contents into the texture. + glBindTexture(GL_TEXTURE_2D, textureId); + void* pixels = skiaBitmap->getPixels(); + if (pixels) { + // FIXME. We can be smart here and call glTexSubImage2D if the new bitmap has the same + // size as the old one which will save us one unecessary allocation / deallocation. + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmapWidth, bitmapHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + checkGLError(); + } + } + } + + if (layer->doubleSided()) + glDisable(GL_CULL_FACE); + else + glEnable(GL_CULL_FACE); + + glBindTexture(GL_TEXTURE_2D, textureId); + + drawTexturedQuad(localMatrix, bounds.width(), bounds.height(), opacity, false); + } + // Draw the debug border if there is one. + drawDebugBorder(layer, localMatrix); + + // Apply the sublayer transform. + localMatrix.multLeft(layer->sublayerTransform()); + + // The origin of the sublayers is actually the left top corner of the layer + // instead of the center. The matrix passed down to the sublayers is therefore: + // M[s] = M * T[-center] + localMatrix.translate3d(-bounds.width() * 0.5, -bounds.height() * 0.5, 0); const Vector<RefPtr<LayerChromium> >& sublayers = layer->getSublayers(); for (size_t i = 0; i < sublayers.size(); i++) - drawLayerInCanvasRecursive(canvas, sublayers[i].get(), opacity); + compositeLayersRecursive(sublayers[i].get(), localMatrix, opacity, visibleRect); } -#endif // PLATFORM(SKIA) -void LayerRendererChromium::updateLayerContentsRecursive(LayerChromium* layer) +bool LayerRendererChromium::makeContextCurrent() { - layer->updateContents(); + return m_gles2Context->makeCurrent(); +} - const Vector<RefPtr<LayerChromium> >& sublayers = layer->getSublayers(); - for (size_t i = 0; i < sublayers.size(); i++) - updateLayerContentsRecursive(sublayers[i].get()); +bool LayerRendererChromium::initGL() +{ + m_gles2Context = GLES2Context::create(m_page); + + if (!m_gles2Context) + return false; + + return true; } +// Binds the given attribute name to a common location across all three programs +// used by the compositor. This allows the code to bind the attributes only once +// even when switching between programs. +void LayerRendererChromium::bindCommonAttribLocation(int location, char* attribName) +{ + glBindAttribLocation(m_layerProgramObject, location, attribName); + glBindAttribLocation(m_borderProgramObject, location, attribName); + glBindAttribLocation(m_scrollProgramObject, location, attribName); +} + +bool LayerRendererChromium::initializeSharedGLObjects() +{ + // Shaders for drawing the layer contents. + char vertexShaderString[] = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform mat4 matrix; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + char fragmentShaderString[] = + // FIXME: Re-introduce precision qualifier when we need GL ES shaders. + "//precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(s_texture, v_texCoord); \n" + " gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w) * alpha; \n" + "} \n"; + + // Fragment shader used for rendering the scrolled root layer quad. It differs + // from fragmentShaderString in that it doesn't swizzle the colors and doesn't + // take an alpha value. + char scrollFragmentShaderString[] = + // FIXME: Re-introduce precision qualifier when we need GL ES shaders. + "//precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(s_texture, v_texCoord); \n" + " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w); \n" + "} \n"; + + // Shaders for drawing the debug borders around the layers. + char borderVertexShaderString[] = + "attribute vec4 a_position; \n" + "uniform mat4 matrix; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + "} \n"; + char borderFragmentShaderString[] = + // FIXME: Re-introduce precision qualifier when we need GL ES shaders. + "//precision mediump float; \n" + "uniform vec4 color; \n" + "void main() \n" + "{ \n" + " gl_FragColor = color; \n" + "} \n"; + + GLfloat vertices[] = { -0.5f, 0.5f, 0.0f, // Position 0 + 0.0f, 1.0f, // TexCoord 0 + -0.5f, -0.5f, 0.0f, // Position 1 + 0.0f, 0.0f, // TexCoord 1 + 0.5f, -0.5f, 0.0f, // Position 2 + 1.0f, 0.0f, // TexCoord 2 + 0.5f, 0.5f, 0.0f, // Position 3 + 1.0f, 1.0f // TexCoord 3 + }; + GLushort indices[] = { 0, 1, 2, 0, 2, 3, // The two triangles that make up the layer quad. + 0, 1, 2, 3}; // A line path for drawing the layer border. + + makeContextCurrent(); + m_layerProgramObject = loadShaderProgram(vertexShaderString, fragmentShaderString); + if (!m_layerProgramObject) { + LOG_ERROR("Failed to create shader program for layers"); + return false; + } + + m_scrollProgramObject = loadShaderProgram(vertexShaderString, scrollFragmentShaderString); + if (!m_scrollProgramObject) { + LOG_ERROR("Failed to create shader program for scrolling layer"); + return false; + } + + m_borderProgramObject = loadShaderProgram(borderVertexShaderString, borderFragmentShaderString); + if (!m_borderProgramObject) { + LOG_ERROR("Failed to create shader program for debug borders"); + return false; + } + + // Specify the attrib location for the position and make it the same for all three programs to + // avoid binding re-binding the vertex attributes. + bindCommonAttribLocation(m_positionLocation, "a_position"); + bindCommonAttribLocation(m_texCoordLocation, "a_texCoord"); + + checkGLError(); + + // Re-link the shaders to get the new attrib location to take effect. + glLinkProgram(m_layerProgramObject); + glLinkProgram(m_borderProgramObject); + glLinkProgram(m_scrollProgramObject); + + checkGLError(); + + // Get locations of uniforms for the layer content shader program. + m_samplerLocation = glGetUniformLocation(m_layerProgramObject, "s_texture"); + m_matrixLocation = glGetUniformLocation(m_layerProgramObject, "matrix"); + m_alphaLocation = glGetUniformLocation(m_layerProgramObject, "alpha"); + + m_scrollMatrixLocation = glGetUniformLocation(m_scrollProgramObject, "matrix"); + m_scrollSamplerLocation = glGetUniformLocation(m_scrollProgramObject, "s_texture"); + + // Get locations of uniforms for the debug border shader program. + m_borderMatrixLocation = glGetUniformLocation(m_borderProgramObject, "matrix"); + m_borderColorLocation = glGetUniformLocation(m_borderProgramObject, "color"); + + glGenBuffers(3, m_quadVboIds); + glBindBuffer(GL_ARRAY_BUFFER, m_quadVboIds[Vertices]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_quadVboIds[LayerElements]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + // Create a texture object to hold the contents of the root layer. + m_rootLayerTextureId = createLayerTexture(); + if (m_rootLayerTextureId == -1) { + LOG_ERROR("Failed to create texture for root layer"); + return false; + } + // Turn off filtering for the root layer to avoid blurring from the repeated + // writes and reads to the framebuffer that happen while scrolling. + glBindTexture(GL_TEXTURE_2D, m_rootLayerTextureId); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return true; +} } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/chromium/LayerRendererChromium.h b/WebCore/platform/graphics/chromium/LayerRendererChromium.h index 7eb429f..498678e 100644 --- a/WebCore/platform/graphics/chromium/LayerRendererChromium.h +++ b/WebCore/platform/graphics/chromium/LayerRendererChromium.h @@ -36,45 +36,98 @@ #include "IntRect.h" #include "LayerChromium.h" +#include "SkBitmap.h" +#include <wtf/HashMap.h> #include <wtf/Noncopyable.h> #include <wtf/PassOwnPtr.h> #include <wtf/Vector.h> -namespace skia { -class PlatformCanvas; -} - namespace WebCore { +class GLES2Context; +class Page; + +// Class that handles drawing of composited render layers using GL. class LayerRendererChromium : public Noncopyable { public: - static PassOwnPtr<LayerRendererChromium> create(); + static PassOwnPtr<LayerRendererChromium> create(Page* page); - LayerRendererChromium(); + LayerRendererChromium(Page* page); ~LayerRendererChromium(); -#if PLATFORM(SKIA) - void drawLayersInCanvas(skia::PlatformCanvas*, const IntRect& clipRect); -#endif - void updateLayerContents(); + // Updates the contents of the root layer that fall inside the updateRect and recomposites + // all the layers. + void drawLayers(const IntRect& updateRect, const IntRect& visibleRect, const IntRect& contentRect, const IntPoint& scrollPosition); void setRootLayer(PassRefPtr<LayerChromium> layer) { m_rootLayer = layer; } LayerChromium* rootLayer() { return m_rootLayer.get(); } void setNeedsDisplay() { m_needsDisplay = true; } - void setScrollFrame(SkIRect& scrollFrame) { m_scrollFrame = scrollFrame; } + // Frees the texture associated with the given layer. + bool freeLayerTexture(LayerChromium*); + + bool hardwareCompositing() const { return m_hardwareCompositing; } private: -#if PLATFORM(SKIA) - void drawLayerInCanvasRecursive(skia::PlatformCanvas*, LayerChromium*, float opacity); -#endif - void updateLayerContentsRecursive(LayerChromium*); + void compositeLayersRecursive(LayerChromium*, const TransformationMatrix&, float opacity, const IntRect& visibleRect); + + void drawDebugBorder(LayerChromium*, const TransformationMatrix&); + + void drawTexturedQuad(const TransformationMatrix& matrix, float width, float height, float opacity, bool scrolling); + + bool isLayerVisible(LayerChromium*, const TransformationMatrix&, const IntRect& visibleRect); + + void bindCommonAttribLocation(int location, char* attribName); + + enum VboIds { Vertices, LayerElements }; + + // These are here only temporarily and should be removed once we switch over to GGL + bool initGL(); + bool makeContextCurrent(); + + bool initializeSharedGLObjects(); + int getTextureId(LayerChromium*); + int assignTextureForLayer(LayerChromium*); + + // GL shader program object IDs. + unsigned int m_layerProgramObject; + unsigned int m_borderProgramObject; + unsigned int m_scrollProgramObject; + + unsigned int m_rootLayerTextureId; + int m_rootLayerTextureWidth; + int m_rootLayerTextureHeight; + + // Shader uniform and attribute locations. + const int m_positionLocation; + const int m_texCoordLocation; + int m_samplerLocation; + int m_matrixLocation; + int m_alphaLocation; + int m_scrollMatrixLocation; + int m_scrollSamplerLocation; + + int m_borderMatrixLocation; + int m_borderColorLocation; + + unsigned int m_quadVboIds[3]; + TransformationMatrix m_projectionMatrix; RefPtr<LayerChromium> m_rootLayer; bool m_needsDisplay; - SkIRect m_scrollFrame; + IntPoint m_scrollPosition; + bool m_hardwareCompositing; + + // Map associating layers with textures ids used by the GL compositor. + typedef HashMap<LayerChromium*, unsigned int> TextureIdMap; + TextureIdMap m_textureIdMap; + + OwnPtr<GLES2Context> m_gles2Context; + + // The WebCore Page that the compositor renders into. + Page* m_page; }; } diff --git a/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp new file mode 100644 index 0000000..c5022f9 --- /dev/null +++ b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "GStreamerGWorld.h" + +#include "MediaPlayerPrivateGStreamer.h" + +using namespace std; + +namespace WebCore { + +PassRefPtr<GStreamerGWorld> GStreamerGWorld::createGWorld(MediaPlayerPrivateGStreamer* player) +{ + return adoptRef(new GStreamerGWorld(player)); +} + +GStreamerGWorld::GStreamerGWorld(MediaPlayerPrivateGStreamer* player) + : m_player(player) +{ +} + +GStreamerGWorld::~GStreamerGWorld() +{ +} + +} diff --git a/WebCore/platform/graphics/gstreamer/GStreamerGWorld.h b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.h new file mode 100644 index 0000000..b626298 --- /dev/null +++ b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef GStreamerGWorld_h +#define GStreamerGWorld_h + +#if ENABLE(VIDEO) + +#include "RefCounted.h" +#include "RefPtr.h" +#include <glib.h> + + +namespace WebCore { + +class MediaPlayerPrivateGStreamer; + + +class GStreamerGWorld : public RefCounted<GStreamerGWorld> { + +public: + static PassRefPtr<GStreamerGWorld> createGWorld(MediaPlayerPrivateGStreamer*); + ~GStreamerGWorld(); + +private: + GStreamerGWorld(MediaPlayerPrivateGStreamer*); + MediaPlayerPrivateGStreamer* m_player; +}; + +} +#endif +#endif diff --git a/WebCore/platform/graphics/gstreamer/ImageGStreamer.h b/WebCore/platform/graphics/gstreamer/ImageGStreamer.h index 2e97b4d..3d6d74a 100644 --- a/WebCore/platform/graphics/gstreamer/ImageGStreamer.h +++ b/WebCore/platform/graphics/gstreamer/ImageGStreamer.h @@ -54,6 +54,10 @@ class ImageGStreamer : public RefCounted<ImageGStreamer> { cairo_surface_t* m_surface; #endif +#if PLATFORM(MAC) + ImageGStreamer(GstBuffer*&, IntSize); +#endif + }; } diff --git a/WebCore/platform/graphics/gstreamer/ImageGStreamerCG.mm b/WebCore/platform/graphics/gstreamer/ImageGStreamerCG.mm new file mode 100644 index 0000000..421b90f --- /dev/null +++ b/WebCore/platform/graphics/gstreamer/ImageGStreamerCG.mm @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "ImageGStreamer.h" + +using namespace WebCore; + +PassRefPtr<ImageGStreamer> ImageGStreamer::createImage(GstBuffer* buffer) +{ + int width = 0, height = 0; + GstCaps* caps = gst_buffer_get_caps(buffer); + GstVideoFormat format; + if (!gst_video_format_parse_caps(caps, &format, &width, &height)) { + gst_caps_unref(caps); + return NULL; + } + + return adoptRef(new ImageGStreamer(buffer, IntSize(width, height))); +} + +ImageGStreamer::ImageGStreamer(GstBuffer*& buffer, IntSize size) + : m_image(0) +{ + ASSERT(GST_BUFFER_SIZE(buffer)); + + RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(0, static_cast<UInt8*>(GST_BUFFER_DATA(buffer)), GST_BUFFER_SIZE(buffer), kCFAllocatorNull)); + RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData(data.get())); + static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGImageRef frameImage = CGImageCreate(size.width(), size.height(), 8, 32, size.width()*4, colorSpace, + kCGBitmapByteOrder32Little | kCGImageAlphaFirst, provider.get(), 0, false, kCGRenderingIntentDefault); + m_image = BitmapImage::create(frameImage); +} + +ImageGStreamer::~ImageGStreamer() +{ + if (m_image) + m_image.clear(); + + m_image = 0; +} diff --git a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 8f6c9e0..d619e14 100644 --- a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -34,6 +34,7 @@ #include "Frame.h" #include "FrameView.h" #include "GOwnPtrGStreamer.h" +#include "GStreamerGWorld.h" #include "GraphicsContext.h" #include "GraphicsTypes.h" #include "ImageGStreamer.h" @@ -1348,6 +1349,15 @@ bool MediaPlayerPrivateGStreamer::supportsFullscreen() const return true; } + +PlatformMedia MediaPlayerPrivateGStreamer::platformMedia() const +{ + PlatformMedia p; + p.type = PlatformMedia::GStreamerGWorldType; + p.media.gstreamerGWorld = m_gstGWorld.get(); + return p; +} + void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload) { ASSERT(m_playBin); @@ -1372,6 +1382,8 @@ void MediaPlayerPrivateGStreamer::createGSTPlayBin() ASSERT(!m_playBin); m_playBin = gst_element_factory_make("playbin2", "play"); + m_gstGWorld = GStreamerGWorld::createGWorld(this); + GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin)); gst_bus_add_signal_watch(bus); g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this); diff --git a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h index 06519fa..81f90b8 100644 --- a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h +++ b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h @@ -43,6 +43,7 @@ class GraphicsContext; class IntSize; class IntRect; class String; +class GStreamerGWorld; gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data); void mediaPlayerPrivateVolumeChangedCallback(GObject* element, GParamSpec* pspec, gpointer data); @@ -116,6 +117,7 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface { bool hasSingleSecurityOrigin() const; bool supportsFullscreen() const; + PlatformMedia platformMedia() const; GstElement* pipeline() const { return m_playBin; } bool pipelineReset() const { return m_resetPipeline; } @@ -172,6 +174,7 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface { MediaPlayer::Preload m_preload; bool m_delayingLoad; bool m_mediaDurationKnown; + RefPtr<GStreamerGWorld> m_gstGWorld; }; } diff --git a/WebCore/platform/graphics/gtk/ImageGtk.cpp b/WebCore/platform/graphics/gtk/ImageGtk.cpp index daa70ef..3de8495 100644 --- a/WebCore/platform/graphics/gtk/ImageGtk.cpp +++ b/WebCore/platform/graphics/gtk/ImageGtk.cpp @@ -158,7 +158,7 @@ PassRefPtr<Image> Image::loadPlatformResource(const char* name) fileName = getThemeIconFileName(GTK_STOCK_MISSING_IMAGE, 16); if (fileName.isNull()) { gchar* imagename = g_strdup_printf("%s.png", name); - gchar* glibFileName = g_build_filename(DATA_DIR, "webkit-1.0", "images", imagename, 0); + gchar* glibFileName = g_build_filename(DATA_DIR, "webkit-1.0", "images", imagename, NULL); fileName = glibFileName; g_free(imagename); g_free(glibFileName); diff --git a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp index 3b3158f..80c0d03 100644 --- a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp +++ b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp @@ -840,10 +840,21 @@ void GraphicsContext3D::releaseShaderCompiler() void GraphicsContext3D::renderbufferStorage(unsigned long target, unsigned long internalformat, unsigned long width, unsigned long height) { ensureContext(m_contextObj); - if (internalformat == DEPTH_STENCIL) + switch (internalformat) { + case DEPTH_STENCIL: internalformat = GL_DEPTH24_STENCIL8_EXT; - else if (internalformat == DEPTH_COMPONENT16) + break; + case DEPTH_COMPONENT16: internalformat = GL_DEPTH_COMPONENT; + break; + case RGBA4: + case RGB5_A1: + internalformat = GL_RGBA; + break; + case RGB565: + internalformat = GL_RGB; + break; + } ::glRenderbufferStorageEXT(target, internalformat, width, height); } diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h index 49aebba..26a5de6 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.h +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.h @@ -165,6 +165,8 @@ private: void commitLayerChangesBeforeSublayers(); void commitLayerChangesAfterSublayers(); + FloatSize constrainedSize() const; + bool requiresTiledLayer(const FloatSize&) const; void swapFromOrToTiledLayer(bool useTiledLayer); diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm index 294c82f..bd01353 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -1050,6 +1050,8 @@ void GraphicsLayerCA::updateSublayerList() void GraphicsLayerCA::updateLayerPosition() { + // FIXME: if constrained the size, the position will be wrong. Fixing this is not trivial. + // 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()); @@ -1098,6 +1100,11 @@ void GraphicsLayerCA::updateLayerSize() if (needTiledLayer != m_usingTiledLayer) swapFromOrToTiledLayer(needTiledLayer); + if (m_usingTiledLayer) { + FloatSize sizeToUse = constrainedSize(); + rect = CGRectMake(0, 0, sizeToUse.width(), sizeToUse.height()); + } + [m_layer.get() setBounds:rect]; if (LayerMap* layerCloneMap = m_layerClones.get()) { LayerMap::const_iterator end = layerCloneMap->end(); @@ -2144,6 +2151,29 @@ void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) END_BLOCK_OBJC_EXCEPTIONS } +FloatSize GraphicsLayerCA::constrainedSize() const +{ + float tileColumns = ceilf(m_size.width() / cTiledLayerTileSize); + float tileRows = ceilf(m_size.height() / cTiledLayerTileSize); + double numTiles = tileColumns * tileRows; + + FloatSize constrainedSize = m_size; + const unsigned cMaxTileCount = 512; + while (numTiles > cMaxTileCount) { + // Constrain the wider dimension. + if (constrainedSize.width() >= constrainedSize.height()) { + tileColumns = max(floorf(cMaxTileCount / tileRows), 1.0f); + constrainedSize.setWidth(tileColumns * cTiledLayerTileSize); + } else { + tileRows = max(floorf(cMaxTileCount / tileColumns), 1.0f); + constrainedSize.setHeight(tileRows * cTiledLayerTileSize); + } + numTiles = tileColumns * tileRows; + } + + return constrainedSize; +} + bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const { if (!m_drawsContent) diff --git a/WebCore/platform/graphics/openvg/PainterOpenVG.cpp b/WebCore/platform/graphics/openvg/PainterOpenVG.cpp index 7d60ca5..b2f2302 100644 --- a/WebCore/platform/graphics/openvg/PainterOpenVG.cpp +++ b/WebCore/platform/graphics/openvg/PainterOpenVG.cpp @@ -834,7 +834,7 @@ void PainterOpenVG::clipPath(const Path& path, PainterOpenVG::ClipOperation mask vgSeti(VG_FILL_RULE, toVGFillRule(clipRule)); vgRenderToMask(path.platformPath()->vgPath(), VG_FILL_PATH, (VGMaskOperation) maskOp); ASSERT_VG_NO_ERROR(); -#elseif +#else notImplemented(); #endif } diff --git a/WebCore/platform/graphics/openvg/PathOpenVG.cpp b/WebCore/platform/graphics/openvg/PathOpenVG.cpp index 2c366ee..7e67036 100644 --- a/WebCore/platform/graphics/openvg/PathOpenVG.cpp +++ b/WebCore/platform/graphics/openvg/PathOpenVG.cpp @@ -459,8 +459,8 @@ void Path::transform(const AffineTransform& transformation) delete m_path; m_path = dst; - m_path->m_currentPoint = transform.mapPoint(m_path->m_currentPoint); - m_path->m_subpathStartPoint = transform.mapPoint(m_path->m_subpathStartPoint); + m_path->m_currentPoint = transformation.mapPoint(m_path->m_currentPoint); + m_path->m_subpathStartPoint = transformation.mapPoint(m_path->m_subpathStartPoint); } diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 0100b72..69121c8 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -167,10 +167,13 @@ static inline Qt::FillRule toQtFillRule(WindRule rule) } struct TransparencyLayer : FastAllocBase { - TransparencyLayer(const QPainter* p, const QRect &rect) + TransparencyLayer(const QPainter* p, const QRect &rect, qreal opacity, QPixmap& alphaMask) : pixmap(rect.width(), rect.height()) + , opacity(opacity) + , alphaMask(alphaMask) + , saveCounter(1) // see the comment for saveCounter { - offset = rect.topLeft(); + offset = p->transform().mapRect(rect).topLeft(); pixmap.fill(Qt::transparent); painter.begin(&pixmap); painter.setRenderHint(QPainter::Antialiasing, p->testRenderHint(QPainter::Antialiasing)); @@ -182,7 +185,9 @@ struct TransparencyLayer : FastAllocBase { painter.setFont(p->font()); if (painter.paintEngine()->hasFeature(QPaintEngine::PorterDuff)) painter.setCompositionMode(p->compositionMode()); - painter.setClipPath(p->clipPath()); + // if the path is an empty region, this assignment disables all painting + if (!p->clipPath().isEmpty()) + painter.setClipPath(p->clipPath()); } TransparencyLayer() @@ -193,6 +198,11 @@ struct TransparencyLayer : FastAllocBase { QPoint offset; QPainter painter; qreal opacity; + // for clipToImageBuffer + QPixmap alphaMask; + // saveCounter is only used in combination with alphaMask + // otherwise, its value is unspecified + int saveCounter; private: TransparencyLayer(const TransparencyLayer &) {} TransparencyLayer & operator=(const TransparencyLayer &) { return *this; } @@ -217,6 +227,9 @@ public: bool antiAliasingForRectsAndLines; QStack<TransparencyLayer*> layers; + // Counting real layers. Required by inTransparencyLayer() calls + // For example, layers with valid alphaMask are not real layers + int layerCount; QPainter* redirect; // reuse this brush for solid color (to prevent expensive QBrush construction) @@ -235,6 +248,7 @@ private: GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p) { painter = p; + layerCount = 0; redirect = 0; solidColor = QBrush(Qt::black); @@ -289,11 +303,17 @@ AffineTransform GraphicsContext::getCTM() const void GraphicsContext::savePlatformState() { + if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull()) + ++m_data->layers.top()->saveCounter; m_data->p()->save(); } void GraphicsContext::restorePlatformState() { + if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull()) + if (!--m_data->layers.top()->saveCounter) + endTransparencyLayer(); + m_data->p()->restore(); if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) { @@ -678,7 +698,7 @@ void GraphicsContext::addPath(const Path& path) bool GraphicsContext::inTransparencyLayer() const { - return !m_data->layers.isEmpty(); + return m_data->layerCount; } PlatformPath* GraphicsContext::currentPath() @@ -837,10 +857,9 @@ void GraphicsContext::beginTransparencyLayer(float opacity) w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2); h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2); - TransparencyLayer * layer = new TransparencyLayer(m_data->p(), QRect(x, y, w, h)); - - layer->opacity = opacity; - m_data->layers.push(layer); + QPixmap emptyAlphaMask; + m_data->layers.push(new TransparencyLayer(m_data->p(), QRect(x, y, w, h), opacity, emptyAlphaMask)); + ++m_data->layerCount; } void GraphicsContext::endTransparencyLayer() @@ -849,6 +868,12 @@ void GraphicsContext::endTransparencyLayer() return; TransparencyLayer* layer = m_data->layers.pop(); + if (!layer->alphaMask.isNull()) { + layer->painter.resetTransform(); + layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + layer->painter.drawPixmap(QPoint(), layer->alphaMask); + } else + --m_data->layerCount; // see the comment for layerCount layer->painter.end(); QPainter* p = m_data->p(); @@ -1086,9 +1111,21 @@ void GraphicsContext::clipOutEllipseInRect(const IntRect& rect) } } -void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) +void GraphicsContext::clipToImageBuffer(const FloatRect& floatRect, const ImageBuffer* image) { - notImplemented(); + if (paintingDisabled()) + return; + + QPixmap* nativeImage = image->image()->nativeImageForCurrentFrame(); + if (!nativeImage) + return; + + IntRect rect(floatRect); + QPixmap alphaMask = *nativeImage; + if (alphaMask.width() != rect.width() || alphaMask.height() != rect.height()) + alphaMask = alphaMask.scaled(rect.width(), rect.height()); + + m_data->layers.push(new TransparencyLayer(m_data->p(), rect, 1.0, alphaMask)); } void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp index aac1164..9f36b2a 100644 --- a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -43,23 +43,25 @@ namespace WebCore { +#ifndef QT_NO_GRAPHICSEFFECT class MaskEffectQt : public QGraphicsEffect { public: MaskEffectQt(QObject* parent, QGraphicsItem* maskLayer) - : QGraphicsEffect(parent) - , m_maskLayer(maskLayer) + : QGraphicsEffect(parent) + , m_maskLayer(maskLayer) { } void draw(QPainter* painter) { - // this is a modified clone of QGraphicsOpacityEffect. - // It's more efficient to do it this way because - // (a) we don't need the QBrush abstraction - we always end up using QGraphicsItem::paint from the mask layer + // This is a modified clone of QGraphicsOpacityEffect. + // It's more efficient to do it this way because: + // (a) We don't need the QBrush abstraction - we always end up using QGraphicsItem::paint + // from the mask layer. // (b) QGraphicsOpacityEffect detaches the pixmap, which is inefficient on OpenGL. QPixmap maskPixmap(sourceBoundingRect().toAlignedRect().size()); - // we need to do this so the pixmap would have hasAlpha() + // We need to do this so the pixmap would have hasAlpha(). maskPixmap.fill(Qt::transparent); QPainter maskPainter(&maskPixmap); QStyleOptionGraphicsItem option; @@ -67,11 +69,12 @@ public: maskPainter.setRenderHints(painter->renderHints(), true); m_maskLayer->paint(&maskPainter, &option, 0); maskPainter.end(); + QPoint offset; QPixmap srcPixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, QGraphicsEffect::NoPad); - // we have to use another intermediate pixmap, to make sure the mask applies only to this item - // and doesn't modify pixels already painted into this paint-device + // We have to use another intermediate pixmap, to make sure the mask applies only to this item + // and doesn't modify pixels already painted into this paint-device. QPixmap pixmap(srcPixmap.size()); pixmap.fill(Qt::transparent); @@ -79,25 +82,29 @@ public: return; QPainter pixmapPainter(&pixmap); + pixmapPainter.setRenderHints(painter->renderHints()); pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source); - // We use drawPixmap rather than detaching, because it's more efficient on OpenGL + + // We use drawPixmap rather than detaching, because it's more efficient on OpenGL. pixmapPainter.drawPixmap(0, 0, srcPixmap); pixmapPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); pixmapPainter.drawPixmap(0, 0, maskPixmap); + pixmapPainter.end(); painter->drawPixmap(offset, pixmap); } QGraphicsItem* m_maskLayer; }; +#endif // QT_NO_GRAPHICSEFFECT class GraphicsLayerQtImpl : public QGraphicsObject { Q_OBJECT public: - // this set of flags help us defer which properties of the layer have been - // modified by the compositor, so we can know what to look for in the next flush + // This set of flags help us defer which properties of the layer have been + // modified by the compositor, so we can know what to look for in the next flush. enum ChangeMask { NoChanges = 0, @@ -129,7 +136,7 @@ public: DistributesOpacityChange = (1L << 20) }; - // the compositor lets us special-case images and colors, so we try to do so + // The compositor lets us special-case images and colors, so we try to do so. enum StaticContentType { HTMLContentType, PixmapContentType, ColorContentType, MediaContentType, Canvas3DContentType}; const GraphicsLayerQtImpl* rootLayer() const; @@ -142,34 +149,36 @@ public: virtual QRectF boundingRect() const; virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); - // We manage transforms ourselves because transform-origin acts differently in webkit and in Qt, and we need it as a fallback in case we encounter an un-invertible matrix + // We manage transforms ourselves because transform-origin acts differently in webkit and in Qt, + // and we need it as a fallback in case we encounter an un-invertible matrix. void setBaseTransform(const TransformationMatrix&); void updateTransform(); // let the compositor-API tell us which properties were changed void notifyChange(ChangeMask); - // actual rendering of the web-content into a QPixmap - // We prefer to use our own caching because it gives us a higher level of granularity than QGraphicsItem cache modes - - // sometimes we need to cache the contents even `though the item needs to be updated, e.g. when the background-color is changed. + // Actual rendering of the web-content into a QPixmap: + // We prefer to use our own caching because it gives us a higher level of granularity than + // QGraphicsItem cache modes - Sometimes we need to cache the contents even though the item + // needs to be updated, e.g. when the background-color is changed. // TODO: investigate if QGraphicsItem caching can be improved to support that out of the box. QPixmap recache(const QRegion&); - // called when the compositor is ready for us to show the changes on screen - // this is called indirectly from ChromeClientQt::setNeedsOneShotDrawingSynchronization - // (meaning the sync would happen together with the next draw) - // or ChromeClientQt::scheduleCompositingLayerSync (meaning the sync will happen ASAP) + // Called when the compositor is ready for us to show the changes on screen. + // This is called indirectly from ChromeClientQt::setNeedsOneShotDrawingSynchronization + // (meaning the sync would happen together with the next draw) or + // ChromeClientQt::scheduleCompositingLayerSync (meaning the sync will happen ASAP) void flushChanges(bool recursive = true, bool forceTransformUpdate = false); public slots: - // we need to notify the client (aka the layer compositor) when the animation actually starts + // We need to notify the client (ie. the layer compositor) when the animation actually starts. void notifyAnimationStarted(); - // we notify WebCore of a layer changed asynchronously; otherwise we end up calling flushChanges too often. + // We notify WebCore of a layer changed asynchronously; otherwise we end up calling flushChanges too often. void notifySyncRequired(); signals: - // optimization: we don't want to use QTimer::singleShot + // Optimization: Avoid using QTimer::singleShot(). void notifyAnimationStartedAsync(); public: @@ -179,21 +188,27 @@ public: TransformationMatrix m_transformRelativeToRootLayer; bool m_transformAnimationRunning; bool m_opacityAnimationRunning; +#ifndef QT_NO_GRAPHICSEFFECT QWeakPointer<MaskEffectQt> m_maskEffect; +#endif struct ContentData { QPixmap pixmap; QRegion regionToUpdate; bool updateAll; + QColor contentsBackgroundColor; QColor backgroundColor; + QWeakPointer<QGraphicsObject> mediaLayer; StaticContentType contentType; + float opacity; + ContentData() - : updateAll(false) - , contentType(HTMLContentType) - , opacity(1.f) + : updateAll(false) + , contentType(HTMLContentType) + , opacity(1.f) { } @@ -232,9 +247,16 @@ public: bool backfaceVisibility: 1; bool distributeOpacity: 1; bool align: 2; - State(): maskLayer(0), opacity(1.f), preserves3D(false), masksToBounds(false), - drawsContent(false), contentsOpaque(false), backfaceVisibility(false), - distributeOpacity(false) + + State() + : maskLayer(0) + , opacity(1.f) + , preserves3D(false) + , masksToBounds(false) + , drawsContent(false) + , contentsOpaque(false) + , backfaceVisibility(false) + , distributeOpacity(false) { } } m_state; @@ -248,6 +270,18 @@ public: #endif }; +inline GraphicsLayerQtImpl* toGraphicsLayerQtImpl(QGraphicsItem* item) +{ + ASSERT(item); + return qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject()); +} + +inline GraphicsLayerQtImpl* toGraphicsLayerQtImpl(QGraphicsObject* item) +{ + ASSERT(item); + return qobject_cast<GraphicsLayerQtImpl*>(item); +} + GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) : QGraphicsObject(0) , m_layer(newLayer) @@ -258,11 +292,11 @@ GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) , m_gc3D(0) #endif { - // we use graphics-view for compositing, not for interactivity + // We use graphics-view for compositing-only, not for interactivity. setAcceptedMouseButtons(Qt::NoButton); - // we need to have the item enabled, or else wheel events are not - // passed to the parent class implementation of wheelEvent, where - // they are ignored and passed to the item below. + + // We need to have the item enabled, or else wheel events are not passed to the parent class + // implementation of wheelEvent, where they are ignored and passed to the item below. setEnabled(true); connect(this, SIGNAL(notifyAnimationStartedAsync()), this, SLOT(notifyAnimationStarted()), Qt::QueuedConnection); @@ -270,12 +304,12 @@ GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) GraphicsLayerQtImpl::~GraphicsLayerQtImpl() { - // the compositor manages item lifecycle - we don't want the graphics-view - // system to automatically delete our items - + // The compositor manages lifecycle of item, so we do not want the graphicsview system to delete + // our items automatically. const QList<QGraphicsItem*> children = childItems(); - for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) { - if (QGraphicsItem* item = *it) { + QList<QGraphicsItem*>::const_iterator cit; + for (cit = children.begin(); cit != children.end(); ++cit) { + if (QGraphicsItem* item = *cit) { if (scene()) scene()->removeItem(item); item->setParentItem(0); @@ -283,8 +317,9 @@ GraphicsLayerQtImpl::~GraphicsLayerQtImpl() } #ifndef QT_NO_ANIMATION - // we do, however, own the animations... - for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_animations.begin(); it != m_animations.end(); ++it) + // We do, however, own the animations. + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_animations.begin(); it != m_animations.end(); ++it) if (QAbstractAnimation* anim = it->data()) delete anim; #endif @@ -292,13 +327,11 @@ GraphicsLayerQtImpl::~GraphicsLayerQtImpl() const GraphicsLayerQtImpl* GraphicsLayerQtImpl::rootLayer() const { - if (const GraphicsLayerQtImpl* parent = qobject_cast<const GraphicsLayerQtImpl*>(parentObject())) + if (const GraphicsLayerQtImpl* parent = toGraphicsLayerQtImpl(parentObject())) return parent->rootLayer(); return this; } - - QPixmap GraphicsLayerQtImpl::recache(const QRegion& regionToUpdate) { if (!m_layer->drawsContent()) @@ -329,7 +362,7 @@ QPixmap GraphicsLayerQtImpl::recache(const QRegion& regionToUpdate) painter.setCompositionMode(QPainter::CompositionMode_SourceOver); m_layer->paintGraphicsLayerContents(gc, region.boundingRect()); - m_backingStoreKey = QPixmapCache::insert(pixmap); + m_backingStoreKey = QPixmapCache::insert(pixmap); return pixmap; } @@ -340,14 +373,16 @@ void GraphicsLayerQtImpl::updateTransform() TransformationMatrix localTransform; - GraphicsLayerQtImpl* parent = qobject_cast<GraphicsLayerQtImpl*>(parentObject()); + GraphicsLayerQtImpl* parent = toGraphicsLayerQtImpl(parentObject()); - // webkit has relative-to-size originPoint, graphics-view has a pixel originPoint, here we convert - // we have to manage this ourselves because QGraphicsView's transformOrigin is incompatible + // WebCore has relative-to-size originPoint, where as the QGraphicsView has a pixel originPoint. + // Thus, we need to convert here as we have to manage this outselves due to the fact that the + // transformOrigin of the graphicsview is imcompatible. const qreal originX = m_state.anchorPoint.x() * m_size.width(); const qreal originY = m_state.anchorPoint.y() * m_size.height(); - // We ignore QGraphicsItem::pos completely, and use only transforms - because we have to maintain that ourselves for 3D. + // We ignore QGraphicsItem::pos completely, and use transforms only, due to the fact that we + // have to maintain that ourselves for 3D. localTransform .translate3d(originX + m_state.pos.x(), originY + m_state.pos.y(), m_state.anchorPoint.z()) .multLeft(m_baseTransform) @@ -378,29 +413,32 @@ void GraphicsLayerQtImpl::updateTransform() m_transformRelativeToRootLayer.setM43(0); } - // Apply perspective for the use of this item's children. Perspective is always applied from the item's center. - if (!m_state.childrenTransform.isIdentity()) + // Apply perspective for the use of this item's children. Perspective is always applied from the item's + // center. + if (!m_state.childrenTransform.isIdentity()) { m_transformRelativeToRootLayer .translate(m_size.width() / 2, m_size.height() /2) .multLeft(m_state.childrenTransform) .translate(-m_size.width() / 2, -m_size.height() /2); + } bool inverseOk = true; - // Use QTransform::inverse to extrapolate the relative transform of this item, based on the parent's transform relative to - // the root layer and the desired transform for this item relative to the root layer. + // Use QTransform::inverse to extrapolate the relative transform of this item, based on the parent's + // transform relative to the root layer and the desired transform for this item relative to the root layer. const QTransform parentTransform = parent ? parent->itemTransform(rootLayer()) : QTransform(); const QTransform transform2D = QTransform(m_transformRelativeToRootLayer) * parentTransform.inverted(&inverseOk); - // In rare cases the transformation cannot be inversed - in that case we don't apply the transformation at all, otherwise we'd flicker. - // FIXME: This should be amended when Qt moves to a real 3D scene-graph. + // In rare cases the transformation cannot be inversed - in that case we don't apply the transformation at + // all, otherwise we'd flicker. FIXME: This should be amended when Qt moves to a real 3D scene-graph. if (!inverseOk) return; setTransform(transform2D); const QList<QGraphicsItem*> children = childItems(); - for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) - if (GraphicsLayerQtImpl* layer= qobject_cast<GraphicsLayerQtImpl*>((*it)->toGraphicsObject())) + QList<QGraphicsItem*>::const_iterator it; + for (it = children.begin(); it != children.end(); ++it) + if (GraphicsLayerQtImpl* layer= toGraphicsLayerQtImpl(*it)) layer->updateTransform(); } @@ -414,7 +452,7 @@ QPainterPath GraphicsLayerQtImpl::opaqueArea() const { QPainterPath painterPath; - // we try out best to return the opaque area, maybe it will help graphics-view render less items + // We try out best to return the opaque area, maybe it will help graphics-view render less items. if (m_currentContent.backgroundColor.isValid() && m_currentContent.backgroundColor.alpha() == 0xff) painterPath.addRect(boundingRect()); else { @@ -422,7 +460,6 @@ QPainterPath GraphicsLayerQtImpl::opaqueArea() const || (m_currentContent.contentType == ColorContentType && m_currentContent.contentsBackgroundColor.alpha() == 0xff) || (m_currentContent.contentType == MediaContentType) || (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlpha())) { - painterPath.addRect(m_state.contentsRect); } } @@ -481,14 +518,14 @@ void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask) void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform) { - // this is the bulk of the work. understanding what the compositor is trying to achieve, - // what graphics-view can do, and trying to find a sane common-grounds + // This is the bulk of the work. understanding what the compositor is trying to achieve, what + // graphicsview can do, and trying to find a sane common-ground. if (!m_layer || m_changeMask == NoChanges) goto afterLayerChanges; if (m_currentContent.contentType == HTMLContentType && (m_changeMask & ParentChange)) { - // the WebCore compositor manages item ownership. We have to make sure - // graphics-view doesn't try to snatch that ownership... + // The WebCore compositor manages item ownership. We have to make sure graphicsview doesn't + // try to snatch that ownership. if (!m_layer->parent() && !parentItem()) setParentItem(0); else if (m_layer && m_layer->parent() && m_layer->parent()->nativeLayer() != parentItem()) @@ -496,8 +533,8 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform } if (m_changeMask & ChildrenChange) { - // we basically do an XOR operation on the list of current children - // and the list of wanted children, and remove/add + // We basically do an XOR operation on the list of current children and the list of wanted + // children, and remove/add. QSet<QGraphicsItem*> newChildren; const Vector<GraphicsLayer*> newChildrenVector = (m_layer->children()); newChildren.reserve(newChildrenVector.size()); @@ -509,31 +546,38 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren; const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren; - for (QSet<QGraphicsItem*>::const_iterator it = childrenToAdd.begin(); it != childrenToAdd.end(); ++it) + QSet<QGraphicsItem*>::const_iterator it; + for (it = childrenToAdd.begin(); it != childrenToAdd.end(); ++it) { if (QGraphicsItem* w = *it) w->setParentItem(this); + } - for (QSet<QGraphicsItem*>::const_iterator it = childrenToRemove.begin(); it != childrenToRemove.end(); ++it) - if (GraphicsLayerQtImpl* w = qobject_cast<GraphicsLayerQtImpl*>((*it)->toGraphicsObject())) + QSet<QGraphicsItem*>::const_iterator rit; + for (rit = childrenToRemove.begin(); rit != childrenToRemove.end(); ++rit) { + if (GraphicsLayerQtImpl* w = toGraphicsLayerQtImpl(*rit)) w->setParentItem(0); + } - // children are ordered by z-value, let graphics-view know. - for (size_t i = 0; i < newChildrenVector.size(); ++i) + // Children are ordered by z-value, let graphicsview know. + for (size_t i = 0; i < newChildrenVector.size(); ++i) { if (newChildrenVector[i]->platformLayer()) newChildrenVector[i]->platformLayer()->setZValue(i); + } } if (m_changeMask & MaskLayerChange) { - // we can't paint here, because we don't know if the mask layer - // itself is ready... we'll have to wait till this layer tries to paint + // We can't paint here, because we don't know if the mask layer itself is ready... we'll have + // to wait till this layer tries to paint. setFlag(ItemClipsChildrenToShape, m_layer->maskLayer() || m_layer->masksToBounds()); +#ifndef QT_NO_GRAPHICSEFFECT setGraphicsEffect(0); if (m_layer->maskLayer()) { - if (GraphicsLayerQtImpl* mask = qobject_cast<GraphicsLayerQtImpl*>(m_layer->maskLayer()->platformLayer()->toGraphicsObject())) { + if (GraphicsLayerQtImpl* mask = toGraphicsLayerQtImpl(m_layer->maskLayer()->platformLayer())) { mask->m_maskEffect = new MaskEffectQt(this, mask); setGraphicsEffect(mask->m_maskEffect.data()); } } +#endif } if (m_changeMask & SizeChange) { @@ -542,14 +586,15 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform m_size = QSizeF(m_layer->size().width(), m_layer->size().height()); } } - // FIXME: this is a hack, due to a probable QGraphicsScene bug when rapidly modifying the perspective - // but without this line we get graphic artifacts + + // FIXME: This is a hack, due to a probable QGraphicsScene bug when rapidly modifying the perspective + // but without this line we get graphic artifacts. if ((m_changeMask & ChildrenTransformChange) && m_state.childrenTransform != m_layer->childrenTransform()) if (scene()) scene()->update(); if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange | BackfaceVisibilityChange | PositionChange)) { - // due to the differences between the way WebCore handles transforms and the way Qt handles transforms, + // Due to the differences between the way WebCore handles transforms and the way Qt handles transforms, // all these elements affect the transforms of all the descendants. forceUpdateTransform = true; } @@ -559,20 +604,21 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform case PixmapContentType: update(); setFlag(ItemHasNoContents, false); - break; + case MediaContentType: setFlag(ItemHasNoContents, true); m_pendingContent.mediaLayer.data()->setParentItem(this); break; case ColorContentType: - if (m_pendingContent.contentType != m_currentContent.contentType || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor) + if (m_pendingContent.contentType != m_currentContent.contentType + || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor) update(); m_state.drawsContent = false; setFlag(ItemHasNoContents, false); - // we only use ItemUsesExtendedStyleOption for HTML content - colors don't gain much from that anyway + // Only use ItemUsesExtendedStyleOption for HTML content as colors don't gain much from that. setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false); break; @@ -608,9 +654,7 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform } } - if ((m_changeMask & MasksToBoundsChange) - && m_state.masksToBounds != m_layer->masksToBounds()) { - + if ((m_changeMask & MasksToBoundsChange) && m_state.masksToBounds != m_layer->masksToBounds()) { setFlag(QGraphicsItem::ItemClipsToShape, m_layer->masksToBounds()); setFlag(QGraphicsItem::ItemClipsChildrenToShape, m_layer->masksToBounds()); } @@ -618,18 +662,22 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform if ((m_changeMask & ContentsOpaqueChange) && m_state.contentsOpaque != m_layer->contentsOpaque()) prepareGeometryChange(); +#ifndef QT_NO_GRAPHICSEFFECT if (m_maskEffect) m_maskEffect.data()->update(); - else if (m_changeMask & DisplayChange) { - // Recache now: all the content is ready and we don't want to wait until the paint event. We only need to do this for HTML content, - // there's no point in caching directly composited content like images or solid rectangles. + else if (m_changeMask & DisplayChange) { + // Recache now: all the content is ready and we don't want to wait until the paint event. + // We only need to do this for HTML content, there's no point in caching directly composited + // content like images or solid rectangles. if (m_pendingContent.contentType == HTMLContentType) recache(m_pendingContent.regionToUpdate); update(m_pendingContent.regionToUpdate.boundingRect()); m_pendingContent.regionToUpdate = QRegion(); } +#endif - if ((m_changeMask & BackgroundColorChange) && (m_pendingContent.backgroundColor != m_currentContent.backgroundColor)) + if ((m_changeMask & BackgroundColorChange) + && (m_pendingContent.backgroundColor != m_currentContent.backgroundColor)) update(); m_state.maskLayer = m_layer->maskLayer(); @@ -655,30 +703,30 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform m_pendingContent.regionToUpdate = QRegion(); m_changeMask = NoChanges; - afterLayerChanges: if (forceUpdateTransform) updateTransform(); if (!recursive) - return; + return; QList<QGraphicsItem*> children = childItems(); if (m_state.maskLayer) children.append(m_state.maskLayer->platformLayer()); - for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) { - if (QGraphicsItem* item = *it) - if (GraphicsLayerQtImpl* layer = qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject())) + QList<QGraphicsItem*>::const_iterator it; + for (it = children.begin(); it != children.end(); ++it) { + if (QGraphicsItem* item = *it) { + if (GraphicsLayerQtImpl* layer = toGraphicsLayerQtImpl(item)) layer->flushChanges(true, forceUpdateTransform); + } } } void GraphicsLayerQtImpl::notifyAnimationStarted() { - // WebCore notifies javascript when the animation starts - // here we're letting it know - m_layer->client()->notifyAnimationStarted(m_layer, WTF::currentTime()); + // WebCore notifies javascript when the animation starts. Here we're letting it know. + m_layer->client()->notifyAnimationStarted(m_layer, /* DOM time */ WTF::currentTime()); } GraphicsLayerQt::GraphicsLayerQt(GraphicsLayerClient* client) @@ -691,75 +739,85 @@ GraphicsLayerQt::~GraphicsLayerQt() { } -// this is the hook for WebCore compositor to know that Qt implements compositing with GraphicsLayerQt +// This is the hook for WebCore compositor to know that Qt implements compositing with GraphicsLayerQt. PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) { return new GraphicsLayerQt(client); } -// reimp from GraphicsLayer.h: Qt is top-down +/* \reimp (GraphicsLayer.h): Qt is top-down +*/ GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayer::compositingCoordinatesOrientation() { return CompositingCoordinatesTopDown; } -// reimp from GraphicsLayer.h: we'll need to update the whole display, and we can't count on the current size because it might change +/* \reimp (GraphicsLayer.h): The current size might change, thus we need to update the whole display. +*/ void GraphicsLayerQt::setNeedsDisplay() { m_impl->m_pendingContent.regionToUpdate = QRegion(QRect(QPoint(0, 0), QSize(size().width(), size().height()))); m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect) { - m_impl->m_pendingContent.regionToUpdate|= QRectF(rect).toAlignedRect(); + m_impl->m_pendingContent.regionToUpdate |= QRectF(rect).toAlignedRect(); m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setName(const String& name) { m_impl->setObjectName(name); GraphicsLayer::setName(name); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setParent(GraphicsLayer* layer) { m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange); GraphicsLayer::setParent(layer); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ bool GraphicsLayerQt::setChildren(const Vector<GraphicsLayer*>& children) { m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); return GraphicsLayer::setChildren(children); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::addChild(GraphicsLayer* layer) { m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); GraphicsLayer::addChild(layer); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::addChildAtIndex(GraphicsLayer* layer, int index) { GraphicsLayer::addChildAtIndex(layer, index); m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling) { GraphicsLayer::addChildAbove(layer, sibling); m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling) { @@ -767,7 +825,8 @@ void GraphicsLayerQt::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ bool GraphicsLayerQt::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) { if (GraphicsLayer::replaceChild(oldChild, newChild)) { @@ -778,7 +837,8 @@ bool GraphicsLayerQt::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newCh return false; } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::removeFromParent() { if (parent()) @@ -786,7 +846,8 @@ void GraphicsLayerQt::removeFromParent() GraphicsLayer::removeFromParent(); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setMaskLayer(GraphicsLayer* value) { if (value == maskLayer()) @@ -795,7 +856,8 @@ void GraphicsLayerQt::setMaskLayer(GraphicsLayer* value) m_impl->notifyChange(GraphicsLayerQtImpl::MaskLayerChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setPosition(const FloatPoint& value) { if (value == position()) @@ -804,7 +866,8 @@ void GraphicsLayerQt::setPosition(const FloatPoint& value) m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& value) { if (value == anchorPoint()) @@ -813,7 +876,8 @@ void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& value) m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setSize(const FloatSize& value) { if (value == size()) @@ -822,7 +886,8 @@ void GraphicsLayerQt::setSize(const FloatSize& value) m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setTransform(const TransformationMatrix& value) { if (value == transform()) @@ -831,7 +896,8 @@ void GraphicsLayerQt::setTransform(const TransformationMatrix& value) m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& value) { if (value == childrenTransform()) @@ -840,7 +906,8 @@ void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& value) m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenTransformChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setPreserves3D(bool value) { if (value == preserves3D()) @@ -849,7 +916,8 @@ void GraphicsLayerQt::setPreserves3D(bool value) m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setMasksToBounds(bool value) { if (value == masksToBounds()) @@ -858,7 +926,8 @@ void GraphicsLayerQt::setMasksToBounds(bool value) m_impl->notifyChange(GraphicsLayerQtImpl::MasksToBoundsChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setDrawsContent(bool value) { if (value == drawsContent()) @@ -867,7 +936,8 @@ void GraphicsLayerQt::setDrawsContent(bool value) GraphicsLayer::setDrawsContent(value); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setBackgroundColor(const Color& value) { if (value == m_impl->m_pendingContent.backgroundColor) @@ -877,7 +947,8 @@ void GraphicsLayerQt::setBackgroundColor(const Color& value) m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::clearBackgroundColor() { if (!m_impl->m_pendingContent.backgroundColor.isValid()) @@ -887,7 +958,8 @@ void GraphicsLayerQt::clearBackgroundColor() m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setContentsOpaque(bool value) { if (value == contentsOpaque()) @@ -896,7 +968,8 @@ void GraphicsLayerQt::setContentsOpaque(bool value) GraphicsLayer::setContentsOpaque(value); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setBackfaceVisibility(bool value) { if (value == backfaceVisibility()) @@ -905,7 +978,8 @@ void GraphicsLayerQt::setBackfaceVisibility(bool value) m_impl->notifyChange(GraphicsLayerQtImpl::BackfaceVisibilityChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setOpacity(float value) { if (value == opacity()) @@ -914,7 +988,8 @@ void GraphicsLayerQt::setOpacity(float value) m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setContentsRect(const IntRect& value) { if (value == contentsRect()) @@ -923,7 +998,8 @@ void GraphicsLayerQt::setContentsRect(const IntRect& value) m_impl->notifyChange(GraphicsLayerQtImpl::ContentsRectChange); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setContentsToImage(Image* image) { m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange); @@ -935,12 +1011,13 @@ void GraphicsLayerQt::setContentsToImage(Image* image) m_impl->m_pendingContent.pixmap = *pxm; m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::PixmapContentType; return; - } + } } m_impl->m_pendingContent.pixmap = QPixmap(); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setContentsBackgroundColor(const Color& color) { m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange); @@ -978,54 +1055,60 @@ void GraphicsLayerQt::setContentsToMedia(PlatformLayer* media) GraphicsLayer::setContentsToMedia(media); } - -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setGeometryOrientation(CompositingCoordinatesOrientation orientation) { m_impl->notifyChange(GraphicsLayerQtImpl::GeometryOrientationChange); GraphicsLayer::setGeometryOrientation(orientation); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::setContentsOrientation(CompositingCoordinatesOrientation orientation) { m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOrientationChange); GraphicsLayer::setContentsOrientation(orientation); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::distributeOpacity(float o) { m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange); m_impl->m_state.distributeOpacity = true; } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ float GraphicsLayerQt::accumulatedOpacity() const { return m_impl->effectiveOpacity(); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ void GraphicsLayerQt::syncCompositingState() { m_impl->flushChanges(); GraphicsLayer::syncCompositingState(); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) + */ NativeLayer GraphicsLayerQt::nativeLayer() const { return m_impl.get(); } -// reimp from GraphicsLayer.h +/* \reimp (GraphicsLayer.h) +*/ PlatformLayer* GraphicsLayerQt::platformLayer() const { return m_impl.get(); } -// now we start dealing with WebCore animations translated to Qt animations +// Now we start dealing with WebCore animations translated to Qt animations template <typename T> struct KeyframeValueQt { @@ -1033,7 +1116,8 @@ struct KeyframeValueQt { T value; }; -// we copy this from the AnimationBase.cpp +/* Copied from AnimationBase.cpp +*/ static inline double solveEpsilon(double duration) { return 1.0 / (200.0 * duration); @@ -1045,10 +1129,13 @@ static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, q return bezier.solve(t, solveEpsilon(duration)); } -// we want the timing function to be as close as possible to what the web-developer intended, so we're using the same function used by WebCore when compositing is disabled -// Using easing-curves would probably work for some of the cases, but wouldn't really buy us anything as we'd have to convert the bezier function back to an easing curve static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, double duration) { + // We want the timing function to be as close as possible to what the web-developer intended, so + // we're using the same function used by WebCore when compositing is disabled. Using easing-curves + // would probably work for some of the cases, but wouldn't really buy us anything as we'd have to + // convert the bezier function back to an easing curve. + if (timingFunction.type() == LinearTimingFunction) return progress; if (timingFunction.type() == CubicBezierTimingFunction) { @@ -1061,7 +1148,8 @@ static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qr return progress; } -// helper functions to safely get a value out of WebCore's AnimationValue* +// Helper functions to safely get a value out of WebCore's AnimationValue*. + static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, TransformOperations& transformOperations) { transformOperations = TransformOperations(); @@ -1078,7 +1166,8 @@ static void webkitAnimationToQtAnimationValue(const AnimationValue* animationVal } #ifndef QT_NO_ANIMATION -// we put a bit of the functionality in a base class to allow casting and to save some code size +// We put a bit of the functionality in a base class to allow casting and to save some code size. + class AnimationQtBase : public QAbstractAnimation { public: AnimationQtBase(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) @@ -1098,7 +1187,7 @@ public: { QAbstractAnimation::updateState(newState, oldState); - // for some reason I have do this asynchronously - or the animation won't work + // For some reason we have do this asynchronously - or the animation won't work. if (newState == Running && oldState == Stopped && m_layer.data()) m_layer.data()->notifyAnimationStartedAsync(); } @@ -1111,23 +1200,23 @@ public: bool m_isAlternate; AnimatedPropertyID m_webkitPropertyID; - // we might need this in case the same animation is added again (i.e. resumed by WebCore) + // We might need this in case the same animation is added again (i.e. resumed by WebCore). const Animation* m_webkitAnimation; QString m_keyframesName; bool m_fillsForwards; }; -// we'd rather have a templatized QAbstractAnimation than QPropertyAnimation / QVariantAnimation; +// We'd rather have a templatized QAbstractAnimation than QPropertyAnimation / QVariantAnimation; // Since we know the types that we're dealing with, the QObject/QProperty/QVariant abstraction -// buys us very little in this case, for too much overhead +// buys us very little in this case, for too much overhead. template <typename T> class AnimationQt : public AnimationQtBase { public: AnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) - :AnimationQtBase(layer, values, boxSize, anim, name) + : AnimationQtBase(layer, values, boxSize, anim, name) { - // copying those WebCore structures is not trivial, we have to do it like this + // Copying those WebCore structures is not trivial, we have to do it like this. for (size_t i = 0; i < values.size(); ++i) { const AnimationValue* animationValue = values.at(i); KeyframeValueQt<T> keyframeValue; @@ -1142,7 +1231,7 @@ public: protected: - // this is the part that differs between animated properties + // This is the part that differs between animated properties. virtual void applyFrame(const T& fromValue, const T& toValue, qreal progress) = 0; virtual void updateCurrentTime(int currentTime) @@ -1158,18 +1247,18 @@ protected: if (m_keyframeValues.isEmpty()) return; - // we find the current from-to keyframes in our little map + // Find the current from-to keyframes in our little map. typename QMap<qreal, KeyframeValueQt<T> >::iterator it = m_keyframeValues.find(progress); - // we didn't find an exact match, we try the closest match (lower bound) + // We didn't find an exact match, we try the closest match (lower bound). if (it == m_keyframeValues.end()) it = m_keyframeValues.lowerBound(progress)-1; - // we didn't find any match - we use the first keyframe + // We didn't find any match; use the first keyframe. if (it == m_keyframeValues.end()) it = m_keyframeValues.begin(); - typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it+1; + typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it + 1; if (it2 == m_keyframeValues.end()) it2 = it; const KeyframeValueQt<T>& fromKeyframe = it.value(); @@ -1179,11 +1268,10 @@ protected: const T& fromValue = fromKeyframe.value; const T& toValue = toKeyframe.value; - // now we have a source keyframe, origin keyframe and a timing function - // we can now process the progress and apply the frame - progress = (!progress || progress == 1 || it.key() == it2.key()) - ? progress - : applyTimingFunction(timingFunc, (progress - it.key()) / (it2.key() - it.key()), duration()); + // Now we have a source keyframe, origin keyframe and a timing function. + // We can now process the progress and apply the frame. + progress = (!progress || progress == 1 || it.key() == it2.key()) ? + progress : applyTimingFunction(timingFunc, (progress - it.key()) / (it2.key() - it.key()), duration()); applyFrame(fromValue, toValue, progress); } @@ -1203,10 +1291,10 @@ public: setCurrentTime(1); } - // the idea is that we let WebCore manage the transform-operations - // and Qt just manages the animation heartbeat and the bottom-line QTransform - // we get the performance not by using QTransform instead of TransformationMatrix, but by proper caching of - // items that are expensive for WebCore to render. We want the rest to be as close to WebCore's idea as possible. + // The idea is that we let WebCore manage the transform operations and Qt just manage the + // animation heartbeat and the bottom-line QTransform. We gain performance, not by using + // Transform instead of TransformationMatrix, but by proper caching of items that are + // expensive for WebCore to render. We want the rest to be as close to WebCore's idea as possible. virtual void applyFrame(const TransformOperations& sourceOperations, const TransformOperations& targetOperations, qreal progress) { TransformationMatrix transformMatrix; @@ -1216,10 +1304,12 @@ public: if (sourceOperationCount) { if (targetOperations.size() != sourceOperationCount) validTransformLists = false; - else - for (size_t j = 0; j < sourceOperationCount && validTransformLists; ++j) + else { + for (size_t j = 0; j < sourceOperationCount && validTransformLists; ++j) { if (!sourceOperations.operations()[j]->isSameType(*targetOperations.operations()[j])) validTransformLists = false; + } + } } if (validTransformLists) { @@ -1230,9 +1320,9 @@ public: transformMatrix.blend(m_sourceMatrix, progress); } + m_layer.data()->m_layer->setTransform(transformMatrix); + // We force the actual opacity change, otherwise it would be ignored because of the animation. m_layer.data()->setBaseTransform(transformMatrix); - if (m_fillsForwards) - m_layer.data()->m_layer->setTransform(m_layer.data()->m_baseTransform); } virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) @@ -1240,10 +1330,11 @@ public: AnimationQtBase::updateState(newState, oldState); if (!m_layer) return; + m_layer.data()->flushChanges(true); - // to increase FPS, we use a less accurate caching mechanism while animation is going on - // this is a UX choice that should probably be customizable + // To increase FPS, we use a less accurate caching mechanism while animation is going on + // this is a UX choice that should probably be customizable. if (newState == QAbstractAnimation::Running) { m_sourceMatrix = m_layer.data()->m_layer->transform(); m_layer.data()->m_transformAnimationRunning = true; @@ -1260,7 +1351,7 @@ public: class OpacityAnimationQt : public AnimationQt<qreal> { public: - OpacityAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) + OpacityAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString& name) : AnimationQt<qreal>(layer, values, boxSize, anim, name) { } @@ -1272,16 +1363,16 @@ public: } virtual void applyFrame(const qreal& fromValue, const qreal& toValue, qreal progress) { - qreal opacity = qBound(qreal(0), fromValue + (toValue-fromValue)*progress, qreal(1)); + qreal opacity = qBound(qreal(0), fromValue + (toValue - fromValue) * progress, qreal(1)); - // FIXME: this is a hack, due to a probable QGraphicsScene bug. + // FIXME: This is a hack, due to a probable QGraphicsScene bug. // Without this the opacity change doesn't always have immediate effect. if (!m_layer.data()->opacity() && opacity) m_layer.data()->scene()->update(); + m_layer.data()->m_layer->setOpacity(opacity); + // We force the actual opacity change, otherwise it would be ignored because of the animation. m_layer.data()->setOpacity(opacity); - if (m_fillsForwards) - m_layer.data()->m_layer->setOpacity(opacity); } virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) @@ -1306,8 +1397,9 @@ bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSiz AnimationQtBase* newAnim = 0; - // fixed: we might already have the Qt animation object associated with this WebCore::Animation object - for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + // Fixed: we might already have the Qt animation object associated with this WebCore::Animation object. + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { if (*it) { AnimationQtBase* curAnimation = static_cast<AnimationQtBase*>(it->data()); if (curAnimation && curAnimation->m_webkitAnimation == anim) @@ -1327,17 +1419,17 @@ bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSiz return false; } - // we make sure WebCore::Animation and QAnimation are on the same terms + // We make sure WebCore::Animation and QAnimation are on the same terms. newAnim->setLoopCount(anim->iterationCount()); newAnim->m_fillsForwards = anim->fillsForwards(); m_impl->m_animations.append(QWeakPointer<QAbstractAnimation>(newAnim)); QObject::connect(&m_impl->m_suspendTimer, SIGNAL(timeout()), newAnim, SLOT(resume())); } - // flush now or flicker... + // Flush now to avoid flicker. m_impl->flushChanges(false); - // when fill-mode is backwards/both, we set the value to 0 before the delay takes place + // Qhen fill-mode is backwards/both, we set the value to 0 before the delay takes place. if (anim->fillsBackwards()) newAnim->setCurrentTime(0); @@ -1346,10 +1438,10 @@ bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSiz else newAnim->start(); - // we synchronize the animation's clock to WebCore's timeOffset + // We synchronize the animation's clock to WebCore's timeOffset. newAnim->setCurrentTime(timeOffset * 1000); - // we don't need to manage the animation object's lifecycle: + // We don't need to manage the animation object's lifecycle: // WebCore would call removeAnimations when it's time to delete. return true; @@ -1357,43 +1449,48 @@ bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSiz void GraphicsLayerQt::removeAnimationsForProperty(AnimatedPropertyID id) { - for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { - if (*it) { - AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data()); - if (anim && anim->m_webkitPropertyID == id) { - // We need to stop the animation right away, or it might flicker before it's deleted. - anim->stop(); - anim->deleteLater(); - it = m_impl->m_animations.erase(it); - --it; - } + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (!(*it)) + continue; + + AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data()); + if (anim && anim->m_webkitPropertyID == id) { + // We need to stop the animation right away, or it might flicker before it's deleted. + anim->stop(); + anim->deleteLater(); + it = m_impl->m_animations.erase(it); + --it; } } } void GraphicsLayerQt::removeAnimationsForKeyframes(const String& name) { - for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { - if (*it) { - AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data()); - if (anim && anim->m_keyframesName == QString(name)) { - // We need to stop the animation right away, or it might flicker before it's deleted. - anim->stop(); - anim->deleteLater(); - it = m_impl->m_animations.erase(it); - --it; - } + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (!(*it)) + continue; + + AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data()); + if (anim && anim->m_keyframesName == QString(name)) { + // We need to stop the animation right away, or it might flicker before it's deleted. + anim->stop(); + anim->deleteLater(); + it = m_impl->m_animations.erase(it); + --it; } } } void GraphicsLayerQt::pauseAnimation(const String& name, double timeOffset) { - for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { if (!(*it)) continue; - AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data()); + AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data()); if (anim && anim->m_keyframesName == QString(name)) { // we synchronize the animation's clock to WebCore's timeOffset anim->setCurrentTime(timeOffset * 1000); @@ -1408,9 +1505,9 @@ void GraphicsLayerQt::suspendAnimations(double time) m_impl->m_suspendTimer.stop(); m_impl->m_suspendTimer.start(time * 1000); } else { - for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { - QAbstractAnimation* anim = it->data(); - if (anim) + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (QAbstractAnimation* anim = it->data()) anim->pause(); } } @@ -1420,9 +1517,9 @@ void GraphicsLayerQt::resumeAnimations() { if (m_impl->m_suspendTimer.isActive()) { m_impl->m_suspendTimer.stop(); - for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { - QAbstractAnimation* anim = (*it).data(); - if (anim) + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (QAbstractAnimation* anim = it->data()) anim->resume(); } } diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index 4b85a18..4245ee5 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -46,12 +46,19 @@ namespace WebCore { ImageBufferData::ImageBufferData(const IntSize& size) : m_pixmap(size) + , m_painter(0) { + if (m_pixmap.isNull()) + return; + m_pixmap.fill(QColor(Qt::transparent)); - QPainter* painter = new QPainter(&m_pixmap); + QPainter* painter = new QPainter; m_painter.set(painter); + if (!painter->begin(&m_pixmap)) + return; + // Since ImageBuffer is used mainly for Canvas, explicitly initialize // its painter's pen and brush with the corresponding canvas defaults // NOTE: keep in sync with CanvasRenderingContext2D::State @@ -72,8 +79,11 @@ ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace, bool& success) : m_data(size) , m_size(size) { + success = m_data.m_painter && m_data.m_painter->isActive(); + if (!success) + return; + m_context.set(new GraphicsContext(m_data.m_painter.get())); - success = true; } ImageBuffer::~ImageBuffer() diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index 4b0c21f..a7351a0 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -298,9 +298,10 @@ void Path::addArc(const FloatPoint& p, float r, float sar, float ear, bool antic span += ea - sa; } - // connect to the previous point by a straight line - m_path.lineTo(QPointF(xc + radius * cos(sar), - yc - radius * sin(sar))); + // If the path is empty, move to where the arc will start to avoid painting a line from (0,0) + // NOTE: QPainterPath::isEmpty() won't work here since it ignores a lone MoveToElement + if (!m_path.elementCount()) + m_path.arcMoveTo(xs, ys, width, height, sa); m_path.arcTo(xs, ys, width, height, sa, span); diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.cpp b/WebCore/platform/graphics/skia/SkiaFontWin.cpp index 58fa7d3..4abf914 100644 --- a/WebCore/platform/graphics/skia/SkiaFontWin.cpp +++ b/WebCore/platform/graphics/skia/SkiaFontWin.cpp @@ -77,15 +77,15 @@ struct CachedOutlineKeyHash { static const bool safeToCompareToEmptyOrDeleted = true; }; -typedef ListHashSet<CachedOutlineKey, CachedOutlineKeyHash> OutlineCache; +// The global number of glyph outlines we'll cache. +static const int outlineCacheSize = 256; + +typedef ListHashSet<CachedOutlineKey, outlineCacheSize+1, CachedOutlineKeyHash> OutlineCache; // FIXME: Convert from static constructor to accessor function. WebCore tries to // avoid global constructors to save on start-up time. static OutlineCache outlineCache; -// The global number of glyph outlines we'll cache. -static const int outlineCacheSize = 256; - static SkScalar FIXEDToSkScalar(FIXED fixed) { SkFixed skFixed; diff --git a/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp b/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp index 0065191..20d76ef 100644 --- a/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp +++ b/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp @@ -36,7 +36,8 @@ #include "Image.h" #include "PlatformString.h" #include "SystemTime.h" -#include "WKCACFLayer.h" +#include "WebLayer.h" +#include "WebTiledLayer.h" #include <wtf/CurrentTime.h> #include <wtf/StringExtras.h> #include <wtf/text/CString.h> @@ -45,115 +46,15 @@ using namespace std; namespace WebCore { -class WebLayer : public WKCACFLayer { -public: - static PassRefPtr<WKCACFLayer> create(LayerType layerType, GraphicsLayerCACF* owner) - { - return adoptRef(new WebLayer(layerType, owner)); - } - - virtual void setNeedsDisplay(const CGRect* dirtyRect) - { - if (m_owner) { - if (m_owner->showRepaintCounter()) { - CGRect layerBounds = bounds(); - CGRect repaintCounterRect = layerBounds; - // We assume a maximum of 4 digits and a font size of 22. - repaintCounterRect.size.width = 48; - repaintCounterRect.size.height = 25; - if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) - repaintCounterRect.origin.y = layerBounds.size.height - (layerBounds.origin.y + repaintCounterRect.size.height); - WKCACFLayer::setNeedsDisplay(&repaintCounterRect); - } - if (dirtyRect && m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { - CGRect flippedDirtyRect = *dirtyRect; - flippedDirtyRect.origin.y = bounds().size.height - (flippedDirtyRect.origin.y + flippedDirtyRect.size.height); - WKCACFLayer::setNeedsDisplay(&flippedDirtyRect); - return; - } - } - WKCACFLayer::setNeedsDisplay(dirtyRect); - } - - virtual void drawInContext(PlatformGraphicsContext* context) - { - if (!m_owner) - return; - - CGContextSaveGState(context); - - CGRect layerBounds = bounds(); - if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { - CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -layerBounds.size.height); - } - - if (m_owner->client()) { - GraphicsContext graphicsContext(context); - - // It's important to get the clip from the context, because it may be significantly - // smaller than the layer bounds (e.g. tiled layers) - CGRect clipBounds = CGContextGetClipBoundingBox(context); - IntRect clip(enclosingIntRect(clipBounds)); - m_owner->paintGraphicsLayerContents(graphicsContext, clip); - } -#ifndef NDEBUG - else { - ASSERT_NOT_REACHED(); - - // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color, - // so CA never makes backing store for it (which is what -setNeedsDisplay will do above). - CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f); - CGContextFillRect(context, layerBounds); - } -#endif - - if (m_owner->showRepaintCounter()) { - String text = String::format("%d", m_owner->incrementRepaintCount());; - - CGContextSaveGState(context); - CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); - - CGRect aBounds = layerBounds; - - aBounds.size.width = 10 + 12 * text.length(); - aBounds.size.height = 25; - CGContextFillRect(context, aBounds); - - FontDescription desc; - - NONCLIENTMETRICS metrics; - metrics.cbSize = sizeof(metrics); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); - FontFamily family; - family.setFamily(metrics.lfSmCaptionFont.lfFaceName); - desc.setFamily(family); - - desc.setComputedSize(22); - - Font font = Font(desc, 0, 0); - font.update(0); - - GraphicsContext cg(context); - cg.setFillColor(Color::black, DeviceColorSpace); - cg.drawText(font, TextRun(text), IntPoint(aBounds.origin.x + 3, aBounds.origin.y + 20)); - - CGContextRestoreGState(context); - } - - CGContextRestoreGState(context); - } - -protected: - WebLayer(LayerType layerType, GraphicsLayerCACF* owner) - : WKCACFLayer(layerType) - , m_owner(owner) - { - } +// The threshold width or height above which a tiled layer will be used. This should be +// large enough to avoid tiled layers for most GraphicsLayers, but less than the D3D +// texture size limit on all supported hardware. +static const int cMaxPixelDimension = 2000; -private: - GraphicsLayer* m_owner; -}; +// The width and height of a single tile in a tiled layer. Should be large enough to +// avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough +// to keep the overall tile cost low. +static const int cTiledLayerTileSize = 512; static inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) { @@ -537,6 +438,64 @@ void GraphicsLayerCACF::setDebugBorder(const Color& color, float borderWidth) } } +bool GraphicsLayerCACF::requiresTiledLayer(const FloatSize& size) const +{ + if (!m_drawsContent) + return false; + + // FIXME: catch zero-size height or width here (or earlier)? + return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; +} + +void GraphicsLayerCACF::swapFromOrToTiledLayer(bool useTiledLayer) +{ + if (useTiledLayer == m_usingTiledLayer) + return; + + CGSize tileSize = CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize); + + RefPtr<WKCACFLayer> oldLayer = m_layer; + if (useTiledLayer) + m_layer = WebTiledLayer::create(tileSize, this); + else + m_layer = WebLayer::create(WKCACFLayer::Layer, this); + + m_usingTiledLayer = useTiledLayer; + + if (useTiledLayer) { + if (GraphicsLayer::compositingCoordinatesOrientation() == GraphicsLayer::CompositingCoordinatesBottomUp) + m_layer->setContentsGravity(WKCACFLayer::BottomLeft); + else + m_layer->setContentsGravity(WKCACFLayer::TopLeft); + } + + m_layer->adoptSublayers(oldLayer.get()); + if (oldLayer->superlayer()) + oldLayer->superlayer()->replaceSublayer(oldLayer.get(), m_layer.get()); + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + updateMasksToBounds(); + updateContentsOpaque(); + updateBackfaceVisibility(); + updateLayerBackgroundColor(); + + updateOpacityOnLayer(); + +#ifndef NDEBUG + String name = String::format("CALayer(%p) GraphicsLayer(%p) %s", m_layer.get(), this, m_usingTiledLayer ? "[Tiled Layer] " : "") + m_name; + m_layer->setName(name); +#endif + + // need to tell new layer to draw itself + setNeedsDisplay(); + + updateDebugIndicators(); +} + GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayerCACF::defaultContentsOrientation() const { return CompositingCoordinatesTopDown; @@ -600,6 +559,10 @@ void GraphicsLayerCACF::updateLayerSize() m_layer->setPosition(centerPoint); } + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + m_layer->setBounds(rect); // Note that we don't resize m_contentsLayer. It's up the caller to do that. @@ -699,6 +662,10 @@ void GraphicsLayerCACF::updateLayerPreserves3D() void GraphicsLayerCACF::updateLayerDrawsContent() { + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + if (m_drawsContent) m_layer->setNeedsDisplay(); else diff --git a/WebCore/platform/graphics/win/GraphicsLayerCACF.h b/WebCore/platform/graphics/win/GraphicsLayerCACF.h index 8c3f848..171cdbf 100644 --- a/WebCore/platform/graphics/win/GraphicsLayerCACF.h +++ b/WebCore/platform/graphics/win/GraphicsLayerCACF.h @@ -98,6 +98,9 @@ private: WKCACFLayer* hostLayerForSublayers() const; WKCACFLayer* layerForSuperlayer() const; + bool requiresTiledLayer(const FloatSize&) const; + void swapFromOrToTiledLayer(bool useTiledLayer); + CompositingCoordinatesOrientation defaultContentsOrientation() const; void updateSublayerList(); void updateLayerPosition(); diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp index 87625d3..e1d457f 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp @@ -45,6 +45,7 @@ #include "StringHash.h" #include "TimeRanges.h" #include "Timer.h" +#include <CoreGraphics/CGContext.h> #include <Wininet.h> #include <wtf/CurrentTime.h> #include <wtf/HashSet.h> @@ -64,6 +65,8 @@ const CFStringRef kMinCoreVideoVersion = CFSTR("1.0.0.2"); namespace WebCore { +static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer); + SOFT_LINK_LIBRARY(Wininet) SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved), (lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved)) @@ -141,12 +144,13 @@ MediaPlayerPrivateQuickTimeVisualContext::MediaPlayerPrivateQuickTimeVisualConte MediaPlayerPrivateQuickTimeVisualContext::~MediaPlayerPrivateQuickTimeVisualContext() { tearDownVideoRendering(); + m_visualContext->setMovie(0); cancelCallOnMainThread(&VisualContextClient::retrieveCurrentImageProc, this); } bool MediaPlayerPrivateQuickTimeVisualContext::supportsFullscreen() const { - return true; + return false; } PlatformMedia MediaPlayerPrivateQuickTimeVisualContext::platformMedia() const @@ -284,6 +288,7 @@ void MediaPlayerPrivateQuickTimeVisualContext::load(const String& url) CFDictionaryRef options = QTMovieVisualContext::getCGImageOptions(); m_visualContext = adoptRef(new QTMovieVisualContext(m_visualContextClient.get(), options)); + m_visualContext->setMovie(m_movie.get()); } void MediaPlayerPrivateQuickTimeVisualContext::play() @@ -629,7 +634,20 @@ void MediaPlayerPrivateQuickTimeVisualContext::paint(GraphicsContext* p, const I if (m_qtVideoLayer) return; #endif + QTPixelBuffer buffer = m_visualContext->imageForTime(0); + if (buffer.pixelBufferRef()) { + CGImageRef image = CreateCGImageFromPixelBuffer(buffer); + + CGContextRef context = p->platformContext(); + CGContextSaveGState(context); + CGContextTranslateCTM(context, r.x(), r.y()); + CGContextTranslateCTM(context, 0, r.height()); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, CGRectMake(0, 0, r.width(), r.height()), image); + CGContextRestoreGState(context); + CGImageRelease(image); + } paintCompleted(*p, r); } @@ -654,16 +672,17 @@ void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::imageAvailab void MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*) { - retrieveCurrentImage(); + if (m_visualContext && m_visualContext->isImageAvailableForTime(0)) + retrieveCurrentImage(); } static CFDictionaryRef QTCFDictionaryCreateWithDataCallback(CFAllocatorRef allocator, const UInt8* bytes, CFIndex length) { - CFDataRef data = CFDataCreateWithBytesNoCopy(allocator, bytes, length, 0); + RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(allocator, bytes, length, kCFAllocatorNull)); if (!data) return 0; - return reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(allocator, data, kCFPropertyListImmutable, 0)); + return reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(allocator, data.get(), kCFPropertyListImmutable, 0)); } static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer) @@ -729,38 +748,29 @@ void MediaPlayerPrivateQuickTimeVisualContext::retrieveCurrentImage() return; #if USE(ACCELERATED_COMPOSITING) - if (!m_qtVideoLayer) - return; + if (m_qtVideoLayer) { - QTPixelBuffer buffer = m_visualContext->imageForTime(0); - if (!buffer.pixelBufferRef()) - return; + QTPixelBuffer buffer = m_visualContext->imageForTime(0); + if (!buffer.pixelBufferRef()) + return; - WKCACFLayer* layer = static_cast<WKCACFLayer*>(m_qtVideoLayer->platformLayer()); + WKCACFLayer* layer = static_cast<WKCACFLayer*>(m_qtVideoLayer->platformLayer()); - if (!buffer.lockBaseAddress()) { -#ifndef NDEBUG - // Debug QuickTime links against a non-Debug version of CoreFoundation, so the CFDictionary attached to the CVPixelBuffer cannot be directly passed on into the CAImageQueue without being converted to a non-Debug CFDictionary: - CFDictionaryRef attachments = QTCFDictionaryCreateCopyWithDataCallback(kCFAllocatorDefault, buffer.attachments(), &QTCFDictionaryCreateWithDataCallback); -#else - // However, this is unnecssesary in the non-Debug case: - CFDictionaryRef attachments = static_cast<CFDictionaryRef>(CFRetain(buffer.attachments())); -#endif + if (!buffer.lockBaseAddress()) { + CFTimeInterval imageTime = QTMovieVisualContext::currentHostTime(); - CFTimeInterval imageTime = QTMovieVisualContext::currentHostTime(); + CGImageRef image = CreateCGImageFromPixelBuffer(buffer); + layer->setContents(image); + CGImageRelease(image); - CGImageRef image = CreateCGImageFromPixelBuffer(buffer); - layer->setContents(image); - CGImageRelease(image); - - if (attachments) - CFRelease(attachments); + buffer.unlockBaseAddress(); + layer->rootLayer()->setNeedsRender(); + } + } else +#endif + m_player->repaint(); - buffer.unlockBaseAddress(); - layer->rootLayer()->setNeedsRender(); - } m_visualContext->task(); -#endif } static HashSet<String> mimeTypeCache() @@ -886,8 +896,6 @@ void MediaPlayerPrivateQuickTimeVisualContext::setUpVideoRendering() if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer) m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); #endif - - m_visualContext->setMovie(m_movie.get()); } void MediaPlayerPrivateQuickTimeVisualContext::tearDownVideoRendering() @@ -896,8 +904,6 @@ void MediaPlayerPrivateQuickTimeVisualContext::tearDownVideoRendering() if (m_qtVideoLayer) destroyLayerForMovie(); #endif - - m_visualContext->setMovie(0); } bool MediaPlayerPrivateQuickTimeVisualContext::hasSetUpVideoRendering() const diff --git a/WebCore/platform/graphics/win/QTMovieVisualContext.h b/WebCore/platform/graphics/win/QTMovieVisualContext.h index 7afe589..8846416 100644 --- a/WebCore/platform/graphics/win/QTMovieVisualContext.h +++ b/WebCore/platform/graphics/win/QTMovieVisualContext.h @@ -38,6 +38,7 @@ #include <WTF/OwnPtr.h> #include <WTF/RefCounted.h> +typedef const struct __CFDictionary* CFDictionaryRef; typedef struct OpaqueQTVisualContext* QTVisualContextRef; // QTCVTimeStamp is a struct containing only a CVTimeStamp. This is to diff --git a/WebCore/platform/graphics/win/QTPixelBuffer.cpp b/WebCore/platform/graphics/win/QTPixelBuffer.cpp index 657b68e..f874287 100644 --- a/WebCore/platform/graphics/win/QTPixelBuffer.cpp +++ b/WebCore/platform/graphics/win/QTPixelBuffer.cpp @@ -172,11 +172,6 @@ void QTPixelBuffer::getExtendedPixels(size_t* left, size_t* right, size_t* top, return CVPixelBufferGetExtendedPixels(m_pixelBuffer, left, right, top, bottom); } -CFDictionaryRef QTPixelBuffer::attachments() const -{ - return CVBufferGetAttachments(m_pixelBuffer, kCVAttachmentMode_ShouldPropagate); -} - void QTPixelBuffer::retainCallback(void* refcon) { CVPixelBufferRetain(static_cast<CVPixelBufferRef>(refcon)); diff --git a/WebCore/platform/graphics/win/QTPixelBuffer.h b/WebCore/platform/graphics/win/QTPixelBuffer.h index 22f8ba4..3af2197 100644 --- a/WebCore/platform/graphics/win/QTPixelBuffer.h +++ b/WebCore/platform/graphics/win/QTPixelBuffer.h @@ -38,7 +38,6 @@ typedef struct __CVBuffer *CVBufferRef; typedef CVBufferRef CVPixelBufferRef; typedef struct CGImage* CGImageRef; typedef int32_t CVReturn; -typedef const struct __CFDictionary * CFDictionaryRef; // QTPixelBuffer wraps QuickTime's implementation of CVPixelBuffer, so its functions are // safe to call within WebKit. @@ -75,7 +74,6 @@ public: size_t bytesPerRowOfPlane(size_t) const; void getExtendedPixels(size_t* left, size_t* right, size_t* top, size_t* bottom) const; - CFDictionaryRef attachments() const; // Generic CFRetain/CFRelease callbacks static void releaseCallback(void* refcon); diff --git a/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp b/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp index e97b530..1685a30 100644 --- a/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp +++ b/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp @@ -52,8 +52,8 @@ void WKCACFContextFlusher::addContext(CACFContextRef context) { ASSERT(context); - m_contexts.add(context); - CFRetain(context); + if (m_contexts.add(context).second) + CFRetain(context); } void WKCACFContextFlusher::removeContext(CACFContextRef context) diff --git a/WebCore/platform/graphics/win/WKCACFLayer.cpp b/WebCore/platform/graphics/win/WKCACFLayer.cpp index 4a0461d..3f332d8 100644 --- a/WebCore/platform/graphics/win/WKCACFLayer.cpp +++ b/WebCore/platform/graphics/win/WKCACFLayer.cpp @@ -200,12 +200,12 @@ bool WKCACFLayer::isTransformLayer() const void WKCACFLayer::addSublayer(PassRefPtr<WKCACFLayer> sublayer) { - insertSublayer(sublayer, numSublayers()); + insertSublayer(sublayer, sublayerCount()); } -void WKCACFLayer::insertSublayer(PassRefPtr<WKCACFLayer> sublayer, size_t index) +void WKCACFLayer::internalInsertSublayer(PassRefPtr<WKCACFLayer> sublayer, size_t index) { - index = min(index, numSublayers()); + index = min(index, sublayerCount()); sublayer->removeFromSuperlayer(); CACFLayerInsertSublayer(layer(), sublayer->layer(), index); setNeedsCommit(); @@ -218,7 +218,7 @@ void WKCACFLayer::insertSublayerAboveLayer(PassRefPtr<WKCACFLayer> sublayer, con return; } - int referenceIndex = indexOfSublayer(reference); + int referenceIndex = internalIndexOfSublayer(reference); if (referenceIndex == -1) { addSublayer(sublayer); return; @@ -234,7 +234,7 @@ void WKCACFLayer::insertSublayerBelowLayer(PassRefPtr<WKCACFLayer> sublayer, con return; } - int referenceIndex = indexOfSublayer(reference); + int referenceIndex = internalIndexOfSublayer(reference); if (referenceIndex == -1) { addSublayer(sublayer); return; @@ -251,7 +251,7 @@ void WKCACFLayer::replaceSublayer(WKCACFLayer* reference, PassRefPtr<WKCACFLayer if (reference == newLayer) return; - int referenceIndex = indexOfSublayer(reference); + int referenceIndex = internalIndexOfSublayer(reference); ASSERT(referenceIndex != -1); if (referenceIndex == -1) return; @@ -264,6 +264,25 @@ void WKCACFLayer::replaceSublayer(WKCACFLayer* reference, PassRefPtr<WKCACFLayer } } +size_t WKCACFLayer::internalSublayerCount() const +{ + CFArrayRef sublayers = CACFLayerGetSublayers(layer()); + return sublayers ? CFArrayGetCount(sublayers) : 0; +} + +void WKCACFLayer::adoptSublayers(WKCACFLayer* source) +{ + // We will use setSublayers() because it properly nulls + // out the superlayer pointer. + Vector<RefPtr<WKCACFLayer> > sublayers; + size_t n = source->sublayerCount(); + + for (size_t i = 0; i < n; ++i) + sublayers.append(source->internalSublayerAtIndex(i)); + + setSublayers(sublayers); +} + void WKCACFLayer::removeFromSuperlayer() { WKCACFLayer* superlayer = this->superlayer(); @@ -274,22 +293,25 @@ void WKCACFLayer::removeFromSuperlayer() superlayer->setNeedsCommit(); } -const WKCACFLayer* WKCACFLayer::sublayerAtIndex(int index) const +WKCACFLayer* WKCACFLayer::internalSublayerAtIndex(int index) const { CFArrayRef sublayers = CACFLayerGetSublayers(layer()); - if (index < 0 || CFArrayGetCount(sublayers) <= index) + if (!sublayers || index < 0 || CFArrayGetCount(sublayers) <= index) return 0; return layer(static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index)))); } -int WKCACFLayer::indexOfSublayer(const WKCACFLayer* reference) +int WKCACFLayer::internalIndexOfSublayer(const WKCACFLayer* reference) { CACFLayerRef ref = reference->layer(); if (!ref) return -1; CFArrayRef sublayers = CACFLayerGetSublayers(layer()); + if (!sublayers) + return -1; + size_t n = CFArrayGetCount(sublayers); for (size_t i = 0; i < n; ++i) @@ -321,19 +343,6 @@ void WKCACFLayer::setBounds(const CGRect& rect) setNeedsDisplay(); } -void WKCACFLayer::setFrame(const CGRect& rect) -{ - CGRect oldFrame = frame(); - if (CGRectEqualToRect(rect, oldFrame)) - return; - - CACFLayerSetFrame(layer(), rect); - setNeedsCommit(); - - if (m_needsDisplayOnBoundsChange) - setNeedsDisplay(); -} - void WKCACFLayer::setContentsGravity(ContentsGravityType type) { CACFLayerSetContentsGravity(layer(), toCACFContentsGravityType(type)); @@ -374,13 +383,13 @@ WKCACFLayer* WKCACFLayer::rootLayer() const return layer; } -void WKCACFLayer::removeAllSublayers() +void WKCACFLayer::internalRemoveAllSublayers() { CACFLayerSetSublayers(layer(), 0); setNeedsCommit(); } -void WKCACFLayer::setSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) +void WKCACFLayer::internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) { // Remove all the current sublayers and add the passed layers CACFLayerSetSublayers(layer(), 0); @@ -404,15 +413,9 @@ WKCACFLayer* WKCACFLayer::superlayer() const return WKCACFLayer::layer(super); } -void WKCACFLayer::setNeedsDisplay(const CGRect* dirtyRect) +void WKCACFLayer::internalSetNeedsDisplay(const CGRect* dirtyRect) { CACFLayerSetNeedsDisplay(layer(), dirtyRect); - setNeedsCommit(); -} - -void WKCACFLayer::setNeedsDisplay() -{ - setNeedsDisplay(0); } #ifndef NDEBUG @@ -502,12 +505,12 @@ void WKCACFLayer::printLayer(int indent) const } // Print sublayers if needed - int n = numSublayers(); + int n = sublayerCount(); if (n > 0) { printIndent(indent + 1); fprintf(stderr, "(sublayers\n"); for (int i = 0; i < n; ++i) - sublayerAtIndex(i)->printLayer(indent + 2); + internalSublayerAtIndex(i)->printLayer(indent + 2); printIndent(indent + 1); fprintf(stderr, ")\n"); diff --git a/WebCore/platform/graphics/win/WKCACFLayer.h b/WebCore/platform/graphics/win/WKCACFLayer.h index f1b2613..bdc427e 100644 --- a/WebCore/platform/graphics/win/WKCACFLayer.h +++ b/WebCore/platform/graphics/win/WKCACFLayer.h @@ -60,9 +60,14 @@ public: virtual ~WKCACFLayer(); virtual void setNeedsRender() { } + virtual void drawInContext(PlatformGraphicsContext*) { } - virtual void setNeedsDisplay(const CGRect* dirtyRect); - void setNeedsDisplay(); + + void setNeedsDisplay(const CGRect* dirtyRect = 0) + { + internalSetNeedsDisplay(dirtyRect); + setNeedsCommit(); + } // Makes this layer the root when the passed context is rendered void becomeRootLayerForContext(CACFContextRef); @@ -113,10 +118,18 @@ public: bool isTransformLayer() const; void addSublayer(PassRefPtr<WKCACFLayer> sublayer); - void insertSublayer(PassRefPtr<WKCACFLayer>, size_t index); void insertSublayerAboveLayer(PassRefPtr<WKCACFLayer>, const WKCACFLayer* reference); void insertSublayerBelowLayer(PassRefPtr<WKCACFLayer>, const WKCACFLayer* reference); void replaceSublayer(WKCACFLayer* reference, PassRefPtr<WKCACFLayer>); + void adoptSublayers(WKCACFLayer* source); + + void removeAllSublayers() { internalRemoveAllSublayers(); } + void setSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) { internalSetSublayers(sublayers); } + + void insertSublayer(PassRefPtr<WKCACFLayer> layer, size_t index) { internalInsertSublayer(layer, index); } + + size_t sublayerCount() const { return internalSublayerCount(); } + void removeFromSuperlayer(); WKCACFLayer* ancestorOrSelfWithSuperlayer(WKCACFLayer*) const; @@ -136,7 +149,7 @@ public: void setBorderWidth(CGFloat width) { CACFLayerSetBorderWidth(layer(), width); setNeedsCommit(); } CGFloat borderWidth() const { return CACFLayerGetBorderWidth(layer()); } - void setBounds(const CGRect&); + virtual void setBounds(const CGRect&); CGRect bounds() const { return CACFLayerGetBounds(layer()); } void setClearsContext(bool clears) { CACFLayerSetClearsContext(layer(), clears); setNeedsCommit(); } @@ -160,9 +173,6 @@ public: void setFilters(CFArrayRef filters) { CACFLayerSetFilters(layer(), filters); setNeedsCommit(); } CFArrayRef filters() const { return CACFLayerGetFilters(layer()); } - void setFrame(const CGRect&); - CGRect frame() const { return CACFLayerGetFrame(layer()); } - void setHidden(bool hidden) { CACFLayerSetHidden(layer(), hidden); setNeedsCommit(); } bool isHidden() const { return CACFLayerIsHidden(layer()); } @@ -206,10 +216,6 @@ public: void setSortsSublayers(bool sorts) { CACFLayerSetSortsSublayers(layer(), sorts); setNeedsCommit(); } bool sortsSublayers() const { return CACFLayerGetSortsSublayers(layer()); } - void removeAllSublayers(); - - void setSublayers(const Vector<RefPtr<WKCACFLayer> >&); - void setSublayerTransform(const CATransform3D& transform) { CACFLayerSetSublayerTransform(layer(), transform); setNeedsCommit(); } CATransform3D sublayerTransform() const { return CACFLayerGetSublayerTransform(layer()); } @@ -231,28 +237,30 @@ protected: void setNeedsCommit(); -private: CACFLayerRef layer() const { return m_layer.get(); } - size_t numSublayers() const - { - CFArrayRef sublayers = CACFLayerGetSublayers(layer()); - return sublayers ? CFArrayGetCount(sublayers) : 0; - } + // This should only be called from removeFromSuperlayer. + void removeSublayer(const WKCACFLayer*); + + // Methods to be overridden for sublayer and rendering management + virtual WKCACFLayer* internalSublayerAtIndex(int) const; - const WKCACFLayer* sublayerAtIndex(int) const; - // Returns the index of the passed layer in this layer's sublayers list // or -1 if not found - int indexOfSublayer(const WKCACFLayer*); + virtual int internalIndexOfSublayer(const WKCACFLayer*); - // This should only be called from removeFromSuperlayer. - void removeSublayer(const WKCACFLayer*); + virtual size_t internalSublayerCount() const; + virtual void internalInsertSublayer(PassRefPtr<WKCACFLayer>, size_t index); + virtual void internalRemoveAllSublayers(); + virtual void internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >&); + + virtual void internalSetNeedsDisplay(const CGRect* dirtyRect); #ifndef NDEBUG // Print this layer and its children to the console void printLayer(int indent) const; #endif +private: RetainPtr<CACFLayerRef> m_layer; bool m_needsDisplayOnBoundsChange; }; diff --git a/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp b/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp index 994d079..0ee61f3 100644 --- a/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp +++ b/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp @@ -198,7 +198,7 @@ bool WKCACFLayerRenderer::acceleratedCompositingAvailable() return available; } - OwnPtr<WKCACFLayerRenderer> testLayerRenderer = WKCACFLayerRenderer::create(); + OwnPtr<WKCACFLayerRenderer> testLayerRenderer = WKCACFLayerRenderer::create(0); testLayerRenderer->setHostWindow(testWindow); available = testLayerRenderer->createRenderer(); ::DestroyWindow(testWindow); @@ -215,16 +215,21 @@ void WKCACFLayerRenderer::didFlushContext(CACFContextRef context) window->renderSoon(); } -PassOwnPtr<WKCACFLayerRenderer> WKCACFLayerRenderer::create() +PassOwnPtr<WKCACFLayerRenderer> WKCACFLayerRenderer::create(WKCACFLayerRendererClient* client) { if (!acceleratedCompositingAvailable()) return 0; - return new WKCACFLayerRenderer; + return new WKCACFLayerRenderer(client); } -WKCACFLayerRenderer::WKCACFLayerRenderer() - : m_triedToCreateD3DRenderer(false) - , m_renderContext(0) +WKCACFLayerRenderer::WKCACFLayerRenderer(WKCACFLayerRendererClient* client) + : m_client(client) + , m_mightBeAbleToCreateDeviceLater(true) + , m_rootLayer(WKCACFRootLayer::create(this)) + , m_scrollLayer(WKCACFLayer::create(WKCACFLayer::Layer)) + , m_clipLayer(WKCACFLayer::create(WKCACFLayer::Layer)) + , m_context(AdoptCF, CACFContextCreate(0)) + , m_renderContext(static_cast<CARenderContext*>(CACFContextGetRenderContext(m_context.get()))) , m_renderer(0) , m_hostWindow(0) , m_renderTimer(this, &WKCACFLayerRenderer::renderTimerFired) @@ -233,6 +238,37 @@ WKCACFLayerRenderer::WKCACFLayerRenderer() , m_backingStoreDirty(false) , m_mustResetLostDeviceBeforeRendering(false) { + windowsForContexts().set(m_context.get(), this); + + // Under the root layer, we have a clipping layer to clip the content, + // that contains a scroll layer that we use for scrolling the content. + // The root layer is the size of the client area of the window. + // The clipping layer is the size of the WebView client area (window less the scrollbars). + // The scroll layer is the size of the root child layer. + // Resizing the window will change the bounds of the rootLayer and the clip layer and will not + // cause any repositioning. + // Scrolling will affect only the position of the scroll layer without affecting the bounds. + + m_rootLayer->setName("WKCACFLayerRenderer rootLayer"); + m_clipLayer->setName("WKCACFLayerRenderer clipLayer"); + m_scrollLayer->setName("WKCACFLayerRenderer scrollLayer"); + + m_rootLayer->addSublayer(m_clipLayer); + m_clipLayer->addSublayer(m_scrollLayer); + m_clipLayer->setMasksToBounds(true); + m_rootLayer->setAnchorPoint(CGPointMake(0, 0)); + m_scrollLayer->setAnchorPoint(CGPointMake(0, 1)); + m_clipLayer->setAnchorPoint(CGPointMake(0, 1)); + +#ifndef NDEBUG + CGColorRef debugColor = createCGColor(Color(255, 0, 0, 204)); + m_rootLayer->setBackgroundColor(debugColor); + CGColorRelease(debugColor); +#endif + + if (m_context) + m_rootLayer->becomeRootLayerForContext(m_context.get()); + #ifndef NDEBUG char* printTreeFlag = getenv("CA_PRINT_TREE"); m_printTree = printTreeFlag && atoi(printTreeFlag); @@ -306,10 +342,10 @@ void WKCACFLayerRenderer::setNeedsDisplay() bool WKCACFLayerRenderer::createRenderer() { - if (m_triedToCreateD3DRenderer) + if (m_d3dDevice || !m_mightBeAbleToCreateDeviceLater) return m_d3dDevice; - m_triedToCreateD3DRenderer = true; + m_mightBeAbleToCreateDeviceLater = false; D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); if (!d3d() || !::IsWindow(m_hostWindow)) @@ -326,9 +362,31 @@ bool WKCACFLayerRenderer::createRenderer() parameters.BackBufferHeight = 1; } + D3DCAPS9 d3dCaps; + if (FAILED(d3d()->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps))) + return false; + + DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE; + if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && d3dCaps.VertexProcessingCaps) + behaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; + else + behaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + COMPtr<IDirect3DDevice9> device; - if (FAILED(d3d()->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hostWindow, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, ¶meters, &device))) + if (FAILED(d3d()->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hostWindow, behaviorFlags, ¶meters, &device))) { + // In certain situations (e.g., shortly after waking from sleep), Direct3DCreate9() will + // return an IDirect3D9 for which IDirect3D9::CreateDevice will always fail. In case we + // have one of these bad IDirect3D9s, get rid of it so we'll fetch a new one the next time + // we want to call CreateDevice. + s_d3d->Release(); + s_d3d = 0; + + // Even if we don't have a bad IDirect3D9, in certain situations (e.g., shortly after + // waking from sleep), CreateDevice will fail, but will later succeed if called again. + m_mightBeAbleToCreateDeviceLater = true; + return false; + } // Now that we've created the IDirect3DDevice9 based on the capabilities we // got from the IDirect3D9 global object, we requery the device for its @@ -349,48 +407,10 @@ bool WKCACFLayerRenderer::createRenderer() m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection); - m_context.adoptCF(CACFContextCreate(0)); - windowsForContexts().set(m_context.get(), this); - - m_renderContext = static_cast<CARenderContext*>(CACFContextGetRenderContext(m_context.get())); m_renderer = CARenderOGLNew(&kCARenderDX9Callbacks, m_d3dDevice.get(), 0); - // Create the root hierarchy. - // Under the root layer, we have a clipping layer to clip the content, - // that contains a scroll layer that we use for scrolling the content. - // The root layer is the size of the client area of the window. - // The clipping layer is the size of the WebView client area (window less the scrollbars). - // The scroll layer is the size of the root child layer. - // Resizing the window will change the bounds of the rootLayer and the clip layer and will not - // cause any repositioning. - // Scrolling will affect only the position of the scroll layer without affecting the bounds. - - m_rootLayer = WKCACFRootLayer::create(this); - m_rootLayer->setName("WKCACFLayerRenderer rootLayer"); - - m_clipLayer = WKCACFLayer::create(WKCACFLayer::Layer); - m_clipLayer->setName("WKCACFLayerRenderer clipLayer"); - - m_scrollLayer = WKCACFLayer::create(WKCACFLayer::Layer); - m_scrollLayer->setName("WKCACFLayerRenderer scrollLayer"); - - m_rootLayer->addSublayer(m_clipLayer); - m_clipLayer->addSublayer(m_scrollLayer); - m_clipLayer->setMasksToBounds(true); - m_scrollLayer->setAnchorPoint(CGPointMake(0, 1)); - m_clipLayer->setAnchorPoint(CGPointMake(0, 1)); - -#ifndef NDEBUG - CGColorRef debugColor = createCGColor(Color(255, 0, 0, 204)); - m_rootLayer->setBackgroundColor(debugColor); - CGColorRelease(debugColor); -#endif - if (IsWindow(m_hostWindow)) - m_rootLayer->setFrame(bounds()); - - if (m_context) - m_rootLayer->becomeRootLayerForContext(m_context.get()); + m_rootLayer->setBounds(bounds()); return true; } @@ -398,6 +418,7 @@ bool WKCACFLayerRenderer::createRenderer() void WKCACFLayerRenderer::destroyRenderer() { if (m_context) { + CACFContextSetLayer(m_context.get(), 0); windowsForContexts().remove(m_context.get()); WKCACFContextFlusher::shared().removeContext(m_context.get()); } @@ -415,7 +436,7 @@ void WKCACFLayerRenderer::destroyRenderer() m_scrollLayer = 0; m_rootChildLayer = 0; - m_triedToCreateD3DRenderer = false; + m_mightBeAbleToCreateDeviceLater = true; } void WKCACFLayerRenderer::resize() @@ -428,7 +449,7 @@ void WKCACFLayerRenderer::resize() resetDevice(ChangedWindowSize); if (m_rootLayer) { - m_rootLayer->setFrame(bounds()); + m_rootLayer->setBounds(bounds()); WKCACFContextFlusher::shared().flushAllContexts(); updateScrollFrame(); } @@ -442,8 +463,8 @@ static void getDirtyRects(HWND window, Vector<CGRect>& outRects) if (!GetClientRect(window, &clientRect)) return; - HRGN region = CreateRectRgn(0, 0, 0, 0); - int regionType = GetUpdateRgn(window, region, false); + OwnPtr<HRGN> region(CreateRectRgn(0, 0, 0, 0)); + int regionType = GetUpdateRgn(window, region.get(), false); if (regionType != COMPLEXREGION) { RECT dirtyRect; if (GetUpdateRect(window, &dirtyRect, false)) @@ -451,10 +472,10 @@ static void getDirtyRects(HWND window, Vector<CGRect>& outRects) return; } - DWORD dataSize = GetRegionData(region, 0, 0); + DWORD dataSize = GetRegionData(region.get(), 0, 0); OwnArrayPtr<unsigned char> regionDataBuffer(new unsigned char[dataSize]); RGNDATA* regionData = reinterpret_cast<RGNDATA*>(regionDataBuffer.get()); - if (!GetRegionData(region, dataSize, regionData)) + if (!GetRegionData(region.get(), dataSize, regionData)) return; outRects.resize(regionData->rdh.nCount); @@ -462,8 +483,6 @@ static void getDirtyRects(HWND window, Vector<CGRect>& outRects) RECT* rect = reinterpret_cast<RECT*>(regionData->Buffer); for (size_t i = 0; i < outRects.size(); ++i, ++rect) outRects[i] = winRectToCGRect(*rect, clientRect); - - DeleteObject(region); } void WKCACFLayerRenderer::renderTimerFired(Timer<WKCACFLayerRenderer>*) @@ -473,8 +492,12 @@ void WKCACFLayerRenderer::renderTimerFired(Timer<WKCACFLayerRenderer>*) void WKCACFLayerRenderer::paint() { - if (!m_d3dDevice) + createRenderer(); + if (!m_d3dDevice) { + if (m_mightBeAbleToCreateDeviceLater) + renderSoon(); return; + } if (m_backingStoreDirty) { // If the backing store is still dirty when we are about to draw the @@ -500,6 +523,11 @@ void WKCACFLayerRenderer::render(const Vector<CGRect>& dirtyRects) return; } + if (m_client && !m_client->shouldRender()) { + renderSoon(); + return; + } + // Flush the root layer to the render tree. WKCACFContextFlusher::shared().flushAllContexts(); diff --git a/WebCore/platform/graphics/win/WKCACFLayerRenderer.h b/WebCore/platform/graphics/win/WKCACFLayerRenderer.h index 259c52f..1ff955a 100644 --- a/WebCore/platform/graphics/win/WKCACFLayerRenderer.h +++ b/WebCore/platform/graphics/win/WKCACFLayerRenderer.h @@ -49,12 +49,18 @@ namespace WebCore { class WKCACFRootLayer; +class WKCACFLayerRendererClient { +public: + virtual ~WKCACFLayerRendererClient() { } + virtual bool shouldRender() const = 0; +}; + // FIXME: Currently there is a WKCACFLayerRenderer for each WebView and each // has its own CARenderOGLContext and Direct3DDevice9, which is inefficient. // (https://bugs.webkit.org/show_bug.cgi?id=31855) class WKCACFLayerRenderer : public Noncopyable { public: - static PassOwnPtr<WKCACFLayerRenderer> create(); + static PassOwnPtr<WKCACFLayerRenderer> create(WKCACFLayerRendererClient*); ~WKCACFLayerRenderer(); static bool acceleratedCompositingAvailable(); @@ -77,7 +83,7 @@ protected: WKCACFLayer* rootLayer() const; private: - WKCACFLayerRenderer(); + WKCACFLayerRenderer(WKCACFLayerRendererClient*); void renderTimerFired(Timer<WKCACFLayerRenderer>*); @@ -94,10 +100,10 @@ private: void render(const Vector<CGRect>& dirtyRects = Vector<CGRect>()); void paint(); - bool m_triedToCreateD3DRenderer; + WKCACFLayerRendererClient* m_client; + bool m_mightBeAbleToCreateDeviceLater; COMPtr<IDirect3DDevice9> m_d3dDevice; RefPtr<WKCACFRootLayer> m_rootLayer; - RefPtr<WKCACFLayer> m_viewLayer; RefPtr<WKCACFLayer> m_scrollLayer; RefPtr<WKCACFLayer> m_rootChildLayer; RefPtr<WKCACFLayer> m_clipLayer; diff --git a/WebCore/platform/graphics/win/WebLayer.cpp b/WebCore/platform/graphics/win/WebLayer.cpp new file mode 100755 index 0000000..70a522d --- /dev/null +++ b/WebCore/platform/graphics/win/WebLayer.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "WebLayer.h" + +#include "Font.h" +#include "GraphicsLayer.h" + +namespace WebCore { + +using namespace std; + +void WebLayer::internalSetNeedsDisplay(const CGRect* dirtyRect) +{ + if (m_owner) { + if (m_owner->showRepaintCounter()) { + CGRect layerBounds = bounds(); + CGRect repaintCounterRect = layerBounds; + // We assume a maximum of 4 digits and a font size of 18. + repaintCounterRect.size.width = 80; + repaintCounterRect.size.height = 22; + if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) + repaintCounterRect.origin.y = layerBounds.size.height - (layerBounds.origin.y + repaintCounterRect.size.height); + WKCACFLayer::internalSetNeedsDisplay(&repaintCounterRect); + } + if (dirtyRect && m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { + CGRect flippedDirtyRect = *dirtyRect; + flippedDirtyRect.origin.y = bounds().size.height - (flippedDirtyRect.origin.y + flippedDirtyRect.size.height); + WKCACFLayer::internalSetNeedsDisplay(&flippedDirtyRect); + return; + } + } + + WKCACFLayer::internalSetNeedsDisplay(dirtyRect); +} + +void WebLayer::drawInContext(PlatformGraphicsContext* context) +{ + if (!m_owner) + return; + + CGContextSaveGState(context); + + CGRect layerBounds = bounds(); + if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM(context, 0, -layerBounds.size.height); + } + + if (m_owner->client()) { + GraphicsContext graphicsContext(context); + + // It's important to get the clip from the context, because it may be significantly + // smaller than the layer bounds (e.g. tiled layers) + CGRect clipBounds = CGContextGetClipBoundingBox(context); + IntRect clip(enclosingIntRect(clipBounds)); + m_owner->paintGraphicsLayerContents(graphicsContext, clip); + } +#ifndef NDEBUG + else { + ASSERT_NOT_REACHED(); + + // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color, + // so CA never makes backing store for it (which is what -setNeedsDisplay will do above). + CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f); + CGContextFillRect(context, layerBounds); + } +#endif + + if (m_owner->showRepaintCounter()) { + String text = String::format("%d", m_owner->incrementRepaintCount());; + + CGContextSaveGState(context); + + // Make the background of the counter the same as the border color, + // unless there is no border, then make it red + float borderWidth = CACFLayerGetBorderWidth(layer()); + if (borderWidth > 0) { + CGColorRef borderColor = CACFLayerGetBorderColor(layer()); + const CGFloat* colors = CGColorGetComponents(borderColor); + CGContextSetRGBFillColor(context, colors[0], colors[1], colors[2], colors[3]); + } else + CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); + + CGRect aBounds = layerBounds; + + aBounds.size.width = 10 + 10 * text.length(); + aBounds.size.height = 22; + CGContextFillRect(context, aBounds); + + FontDescription desc; + + NONCLIENTMETRICS metrics; + metrics.cbSize = sizeof(metrics); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); + FontFamily family; + family.setFamily(metrics.lfSmCaptionFont.lfFaceName); + desc.setFamily(family); + + desc.setComputedSize(18); + + Font font = Font(desc, 0, 0); + font.update(0); + + GraphicsContext cg(context); + cg.setFillColor(Color::black, DeviceColorSpace); + cg.drawText(font, TextRun(text), IntPoint(aBounds.origin.x + 5, aBounds.origin.y + 17)); + + CGContextRestoreGState(context); + } + + CGContextRestoreGState(context); +} + +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/win/WebLayer.h b/WebCore/platform/graphics/win/WebLayer.h new file mode 100755 index 0000000..8dab5cc --- /dev/null +++ b/WebCore/platform/graphics/win/WebLayer.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebLayer_h +#define WebLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "WKCACFLayer.h" + +namespace WebCore { + +class GraphicsLayer; + +class WebLayer : public WKCACFLayer { +public: + static PassRefPtr<WKCACFLayer> create(LayerType layerType, GraphicsLayer* owner) + { + return adoptRef(new WebLayer(layerType, owner)); + } + + virtual void drawInContext(PlatformGraphicsContext*); + +protected: + WebLayer(LayerType layerType, GraphicsLayer* owner) + : WKCACFLayer(layerType) + , m_owner(owner) + { + } + + virtual void internalSetNeedsDisplay(const CGRect* dirtyRect); + + GraphicsLayer* m_owner; +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebLayer_h diff --git a/WebCore/platform/graphics/win/WebTiledLayer.cpp b/WebCore/platform/graphics/win/WebTiledLayer.cpp new file mode 100755 index 0000000..0cf3f9d --- /dev/null +++ b/WebCore/platform/graphics/win/WebTiledLayer.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "WebTiledLayer.h" + +#include "GraphicsLayer.h" +#include "WKCACFLayerRenderer.h" + +namespace WebCore { + +using namespace std; + +void WebTiledLayer::tileDisplayCallback(CACFLayerRef layer, CGContextRef context) +{ + static_cast<WebTiledLayer*>(CACFLayerGetUserData(layer))->drawTile(layer, context); +} + +PassRefPtr<WebTiledLayer> WebTiledLayer::create(const CGSize& tileSize, GraphicsLayer* owner) +{ + ASSERT(WKCACFLayerRenderer::acceleratedCompositingAvailable()); + return adoptRef(new WebTiledLayer(tileSize, owner)); +} + +WebTiledLayer::WebTiledLayer(const CGSize& tileSize, GraphicsLayer* owner) + : WebLayer(WKCACFLayer::Layer, owner) + , m_tileSize(tileSize) + , m_constrainedSize(constrainedSize(bounds().size)) +{ + // Tiled layers are placed in a child layer that is always the first child of the TiledLayer + m_tileParent.adoptCF(CACFLayerCreate(kCACFLayer)); + CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); + + updateTiles(); +} + +WebTiledLayer::~WebTiledLayer() +{ +} + +void WebTiledLayer::setBounds(const CGRect& rect) +{ + if (CGRectEqualToRect(rect, bounds())) + return; + + WebLayer::setBounds(rect); + m_constrainedSize = constrainedSize(rect.size); + updateTiles(); +} + +void WebTiledLayer::internalSetNeedsDisplay(const CGRect* dirtyRect) +{ + // FIXME: Only setNeedsDisplay for tiles that are currently visible + int numTileLayers = tileCount(); + for (int i = 0; i < numTileLayers; ++i) + CACFLayerSetNeedsDisplay(tileAtIndex(i), dirtyRect); + + if (m_owner->showRepaintCounter()) { + CGRect layerBounds = bounds(); + CGRect indicatorRect = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, 80, 25); + CACFLayerSetNeedsDisplay(tileAtIndex(0), &indicatorRect); + } +} + +size_t WebTiledLayer::internalSublayerCount() const +{ + ASSERT(WebLayer::internalSublayerCount() > 0); + + // Subtract 1 to account for the tile parent layer + return WebLayer::internalSublayerCount() - 1; +} + +void WebTiledLayer::internalRemoveAllSublayers() +{ + // Restore the tile parent after removal + WebLayer::internalRemoveAllSublayers(); + CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); +} + +void WebTiledLayer::internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) +{ + // Preserve the tile parent after set + WebLayer::internalSetSublayers(sublayers); + CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); +} + +void WebTiledLayer::internalInsertSublayer(PassRefPtr<WKCACFLayer> layer, size_t index) +{ + // Add 1 to account for the tile parent layer + WebLayer::internalInsertSublayer(layer, index + 1); +} + +WKCACFLayer* WebTiledLayer::internalSublayerAtIndex(int i) const +{ + // Add 1 to account for the tile parent layer + return WebLayer::internalSublayerAtIndex(i + 1); +} + +int WebTiledLayer::internalIndexOfSublayer(const WKCACFLayer* layer) +{ + int i = WebLayer::internalIndexOfSublayer(layer); + + // Add 1 to account for the tile parent layer (but be safe about it) + return (i > 0) ? i - 1 : -1; +} + +CGSize WebTiledLayer::constrainedSize(const CGSize& size) const +{ + const int cMaxTileCount = 512; + const float cSqrtMaxTileCount = sqrtf(cMaxTileCount); + + CGSize constrainedSize = size; + + int tileColumns = ceilf(constrainedSize.width / m_tileSize.width); + int tileRows = ceilf(constrainedSize.height / m_tileSize.height); + int numTiles = tileColumns * tileRows; + + // If number of tiles vertically or horizontally is < sqrt(cMaxTileCount) + // just shorten the longer dimension. Otherwise shorten both dimensions + // according to the ratio of width to height + + if (numTiles > cMaxTileCount) { + if (tileRows < cSqrtMaxTileCount) + tileColumns = floorf(cMaxTileCount / tileRows); + else if (tileColumns < cSqrtMaxTileCount) + tileRows = floorf(cMaxTileCount / tileColumns); + else { + tileRows = ceilf(sqrtf(cMaxTileCount * constrainedSize.height / constrainedSize.width)); + tileColumns = floorf(cMaxTileCount / tileRows); + } + + constrainedSize.width = tileColumns * m_tileSize.width; + constrainedSize.height = tileRows * m_tileSize.height; + } + + return constrainedSize; +} + +void WebTiledLayer::addTile() +{ + RetainPtr<CACFLayerRef> newLayer(AdoptCF, CACFLayerCreate(kCACFLayer)); + CACFLayerSetAnchorPoint(newLayer.get(), CGPointMake(0, 1)); + CACFLayerSetUserData(newLayer.get(), this); + CACFLayerSetDisplayCallback(newLayer.get(), tileDisplayCallback); + + CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); + CACFLayerInsertSublayer(m_tileParent.get(), newLayer.get(), sublayers ? CFArrayGetCount(sublayers) : 0); + + if (m_owner->showDebugBorders()) { + CGColorRef borderColor = createCGColor(Color(128, 0, 128, 180)); + CACFLayerSetBorderColor(newLayer.get(), borderColor); + CGColorRelease(borderColor); + CACFLayerSetBorderWidth(newLayer.get(), 2); + } +} + +void WebTiledLayer::removeTile() +{ + CACFLayerRemoveFromSuperlayer(tileAtIndex(tileCount() - 1)); +} + +CACFLayerRef WebTiledLayer::tileAtIndex(int index) +{ + CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); + if (!sublayers || index < 0 || index >= tileCount() ) + return 0; + + return static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index))); +} + +int WebTiledLayer::tileCount() const +{ + CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); + return sublayers ? CFArrayGetCount(sublayers) : 0; +} + +void WebTiledLayer::updateTiles() +{ + // FIXME: In addition to redoing the number of tiles, we need to only render and have backing + // store for visible layers + int numTilesHorizontal = ceil(m_constrainedSize.width / m_tileSize.width); + int numTilesVertical = ceil(m_constrainedSize.height / m_tileSize.height); + int numTilesTotal = numTilesHorizontal * numTilesVertical; + + int numTilesToChange = numTilesTotal - tileCount(); + if (numTilesToChange >= 0) { + // Add new tiles + for (int i = 0; i < numTilesToChange; ++i) + addTile(); + } else { + // Remove old tiles + numTilesToChange = -numTilesToChange; + for (int i = 0; i < numTilesToChange; ++i) + removeTile(); + } + + // Set coordinates for all tiles + CFArrayRef tileArray = CACFLayerGetSublayers(m_tileParent.get()); + + for (int i = 0; i < numTilesHorizontal; ++i) { + for (int j = 0; j < numTilesVertical; ++j) { + CACFLayerRef tile = static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(tileArray, i * numTilesVertical + j))); + CACFLayerSetPosition(tile, CGPointMake(i * m_tileSize.width, j * m_tileSize.height)); + int width = min(m_tileSize.width, m_constrainedSize.width - i * m_tileSize.width); + int height = min(m_tileSize.height, m_constrainedSize.height - j * m_tileSize.height); + CACFLayerSetBounds(tile, CGRectMake(i * m_tileSize.width, j * m_tileSize.height, width, height)); + + // Flip Y to compensate for the flipping that happens during render to match the CG context coordinate space + CATransform3D transform = CATransform3DMakeScale(1, -1, 1); + CATransform3DTranslate(transform, 0, height, 0); + CACFLayerSetTransform(tile, transform); + +#ifndef NDEBUG + String name = "Tile (" + String::number(i) + "," + String::number(j) + ")"; + CACFLayerSetName(tile, RetainPtr<CFStringRef>(AdoptCF, name.createCFString()).get()); +#endif + } + } +} + +void WebTiledLayer::drawTile(CACFLayerRef tile, CGContextRef context) +{ + CGPoint tilePosition = CACFLayerGetPosition(tile); + CGRect tileBounds = CACFLayerGetBounds(tile); + + CGContextSaveGState(context); + + // Transform context to be at the origin of the parent layer + CGContextTranslateCTM(context, -tilePosition.x, -tilePosition.y); + + // Set the context clipping rectangle to the current tile + CGContextClipToRect(context, CGRectMake(tilePosition.x, tilePosition.y, tileBounds.size.width, tileBounds.size.height)); + + if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { + // If the layer is rendering top-down, it will flip the coordinates in y. Tiled layers are + // already flipping, so we need to undo that here. + CGContextTranslateCTM(context, 0, bounds().size.height); + CGContextScaleCTM(context, 1, -1); + } + + // Draw the tile + drawInContext(context); + + CGContextRestoreGState(context); +} + +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/win/WebTiledLayer.h b/WebCore/platform/graphics/win/WebTiledLayer.h new file mode 100755 index 0000000..ed61656 --- /dev/null +++ b/WebCore/platform/graphics/win/WebTiledLayer.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebTiledLayer_h +#define WebTiledLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "WebLayer.h" + +namespace WebCore { + +class WebTiledLayer : public WebLayer { +public: + static PassRefPtr<WebTiledLayer> create(const CGSize& tileSize, GraphicsLayer* owner); + + virtual ~WebTiledLayer(); + + virtual void setBounds(const CGRect&); + +protected: + WebTiledLayer(const CGSize& tileSize, GraphicsLayer* owner); + + // Overridden from WKCACFLayer + virtual WKCACFLayer* internalSublayerAtIndex(int) const; + virtual int internalIndexOfSublayer(const WKCACFLayer*); + + virtual size_t internalSublayerCount() const; + virtual void internalInsertSublayer(PassRefPtr<WKCACFLayer>, size_t index); + + virtual void internalRemoveAllSublayers(); + virtual void internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >&); + + virtual void internalSetNeedsDisplay(const CGRect* dirtyRect); + +private: + static void tileDisplayCallback(CACFLayerRef, CGContextRef); + void drawTile(CACFLayerRef, CGContextRef); + + CGSize constrainedSize(const CGSize& size) const; + + void addTile(); + void removeTile(); + CACFLayerRef tileAtIndex(int); + int tileCount() const; + + void updateTiles(); + + CGSize m_tileSize; + CGSize m_constrainedSize; + RetainPtr<CACFLayerRef> m_tileParent; +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebTiledLayer_h diff --git a/WebCore/platform/graphics/wx/FontCacheWx.cpp b/WebCore/platform/graphics/wx/FontCacheWx.cpp index 5b02bdb..c594975 100644 --- a/WebCore/platform/graphics/wx/FontCacheWx.cpp +++ b/WebCore/platform/graphics/wx/FontCacheWx.cpp @@ -48,13 +48,33 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, cons if (!fontData->containsCharacters(characters, length)) fontData = getLastResortFallbackFont(font.fontDescription()); - ASSERT(fontData->containsCharacters(characters, length)); + ASSERT(fontData); return fontData; } SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) { - return getCachedFontData(font.fontDescription(), font.family().family()); + SimpleFontData* simpleFontData = 0; +#if OS(DARWIN) + // Attempt to find an appropriate font using a match based on + // the presence of keywords in the the requested names. For example, we'll + // match any name that contains "Arabic" to Geeza Pro. + const FontFamily* currFamily = &font.fontDescription().family(); + while (currFamily && !simpleFontData) { + if (currFamily->family().length()) { + static String* matchWords[3] = { new String("Arabic"), new String("Pashto"), new String("Urdu") }; + DEFINE_STATIC_LOCAL(AtomicString, geezaStr, ("Geeza Pro")); + for (int j = 0; j < 3 && !simpleFontData; ++j) + if (currFamily->family().contains(*matchWords[j], false)) + simpleFontData = getCachedFontData(font.fontDescription(), geezaStr); + } + currFamily = currFamily->next(); + } +#endif + if (!simpleFontData) + simpleFontData = getCachedFontData(font.fontDescription(), font.family().family()); + + return simpleFontData; } SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) @@ -62,12 +82,13 @@ SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& font // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick // the default that the user would get without changing any prefs. SimpleFontData* fallback = 0; -#if OS(WINDOWS) || (OS(DARWIN) && !defined(BUILDING_ON_TIGER)) +#if OS(WINDOWS) static AtomicString fallbackName("Arial Unicode MS"); #else static AtomicString fallbackName("Times New Roman"); #endif fallback = getCachedFontData(fontDescription, fallbackName); + ASSERT(fallback); return fallback; } diff --git a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp index 6c5b300..2951223 100644 --- a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp +++ b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp @@ -105,6 +105,7 @@ FontPlatformData::FontPlatformData(const FontDescription& desc, const AtomicStri #else m_atsuFontID = m_font->font()->MacGetATSUFontID(); #endif + m_nsFont = 0; cacheNSFont(); #endif m_size = desc.computedPixelSize(); |