/* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "GraphicsLayerTextureMapper.h" #include "CurrentTime.h" #include "FloatRect.h" #include "GraphicsContext.h" #include "HashMap.h" #include "Image.h" #include "RefCounted.h" #include "TextureMapper.h" #include "TextureMapperPlatformLayer.h" #include "Timer.h" #include "TransformOperations.h" #include "TranslateTransformOperation.h" #include "UnitBezier.h" #define DEBUG_TEXMAP_FPS 0 namespace WebCore { struct TexmapPaintOptions { BitmapTexture* surface; TextureMapper* textureMapper; GraphicsContext* context; TextureMapperNode* rootLayer; float opacity; IntRect scissorRect; IntRect visibleRect; bool isSurface; }; class TextureMapperCache { public: void mark(BitmapTexture* texture); class Entry { public: RefPtr texture; Entry() : previousCost(0) { } inline int calculateCost() const { if (!texture || !texture->isValid()) return 0; const IntSize textureSize = texture->size(); // an image's cost in bytes is width * height * bytes per pixel (4). return textureSize.width() * textureSize.height() * 4; } Entry(BitmapTexture* newTexture) : texture(newTexture) { } bool operator==(const Entry& other) const { return texture == other.texture; } int previousCost; }; TextureMapperCache() : m_totalCost(0) {} void purge(); Vector m_data; int m_totalCost; #ifndef TEXMAP_TEXTURE_CACHE_KBS #define TEXMAP_TEXTURE_CACHE_KBS 24 * 1024 #endif static const int MaxCost = TEXMAP_TEXTURE_CACHE_KBS * 1024; static const int PurgeAmount = MaxCost / 4; }; void TextureMapperCache::purge() { // If this is in the GL implementation, we need an active GL context, because we might call glDeleteTextures. const int size = m_data.size(); if (m_totalCost <= TextureMapperCache::MaxCost) return; // Ensure that we have the right count. It might be inaccurate if content changed size. // We only do this when we're actually ready to purge. m_totalCost = 0; for (int i = 0; i < size; ++i) m_totalCost += m_data[i].calculateCost(); for (int i = size-1; i >= 0 && m_totalCost > TextureMapperCache::MaxCost - TextureMapperCache::PurgeAmount; --i) { Entry& entry = m_data[i]; if (entry.texture->isLocked() || !entry.texture->isValid()) continue; m_totalCost -= entry.previousCost; entry.texture->destroy(); m_data.remove(i); } } void TextureMapperCache::mark(BitmapTexture* texture) { if (!texture || !texture->isValid()) return; Entry entry(texture); size_t index = m_data.find(entry); if (!index) return; if (index < m_data.size()) m_data.remove(index); const int cost = entry.calculateCost(); m_totalCost -= entry.previousCost; m_totalCost += (entry.previousCost = cost); m_data.prepend(entry); } TextureMapperCache gTextureMapperCache; class TextureMapperCacheLock { public: TextureMapperCacheLock(BitmapTexture* texture) : m_texture(texture) { if (m_texture) m_texture->lock(); } ~TextureMapperCacheLock() { if (m_texture) m_texture->unlock(); } private: RefPtr m_texture; }; class TextureMapperNode : public TextureMapperContentLayer { public: // This set of flags help us defer which properties of the layer have been // modified by the compositor, so we can know what to look for in the next flush. enum ChangeMask { NoChanges = 0, ParentChange = (1L << 0), ChildrenChange = (1L << 1), MaskLayerChange = (1L << 2), PositionChange = (1L << 3), AnchorPointChange = (1L << 4), SizeChange = (1L << 5), TransformChange = (1L << 6), ContentChange = (1L << 7), ContentsOrientationChange = (1L << 9), OpacityChange = (1L << 10), ContentsRectChange = (1L << 11), Preserves3DChange = (1L << 12), MasksToBoundsChange = (1L << 13), DrawsContentChange = (1L << 14), ContentsOpaqueChange = (1L << 15), BackfaceVisibilityChange = (1L << 16), ChildrenTransformChange = (1L << 17), DisplayChange = (1L << 18), BackgroundColorChange = (1L << 19), ReplicaLayerChange = (1L << 20) }; // The compositor lets us special-case images and colors, so we try to do so. enum StaticContentType { HTMLContentType, DirectImageContentType, ColorContentType, MediaContentType, Canvas3DContentType}; TextureMapperNode* rootLayer(); TextureMapperNode(GraphicsLayerTextureMapper* newLayer); virtual ~TextureMapperNode(); void clearDirectImage(); void computeTransformations(); IntSize nearestSurfaceSize() const; void computeReplicaTransform(); void computeLayerType(); void computeLocalTransform(); void flattenTo2DSpaceIfNecessary(); void initializeTextureMapper(TextureMapper*); void invalidateTransform(); void notifyChange(ChangeMask); void syncCompositingState(bool recurse); void performPostSyncOperations(); void setNeedsDisplay(); void setNeedsDisplayInRect(IntRect); virtual void cleanupTextureMapper(); void paintRecursive(TexmapPaintOptions options); void paintSelf(const TexmapPaintOptions& options); void uploadTextureFromContent(TextureMapper* textureMapper, const IntRect& visibleRect); int countDescendantsWithContent() const; bool hasSurfaceDescendants() const; IntSize size() const { return m_size; } virtual void setPlatformLayerClient(TextureMapperLayerClient*); virtual void paint(GraphicsContext*, const IntSize&, const IntRect& targetRect, const IntRect& exposedRect, const TransformationMatrix& transform, float opacity); static TextureMapperNode* toTextureMapperNode(GraphicsLayer*); public: GraphicsLayerTextureMapper* m_layer; const char* m_lastTextureMapperType; RefPtr m_lastTextureMapper; struct TransformData { TransformationMatrix base, target, replica, forDescendants, perspective, local; IntRect targetBoundingRect; float centerZ; bool dirty, localDirty, perspectiveDirty; IntRect boundingRectFromRoot; TransformData() : dirty(true), localDirty(true), perspectiveDirty(true) { } }; TransformData m_transforms; enum LayerType { DefaultLayer, RootLayer, ScissorLayer, ClipLayer, TransparencyLayer }; LayerType m_layerType; struct ContentData { IntRect needsDisplayRect; bool needsDisplay; Color backgroundColor; StaticContentType contentType; RefPtr image; TextureMapperVideoLayer* media; ContentData() : needsDisplay(false) , contentType(HTMLContentType) , image(0) , media(0) { } }; inline IntRect targetRect() const { return m_currentContent.contentType == HTMLContentType ? entireRect() : m_state.contentsRect; } inline IntRect entireRect() const { return IntRect(0, 0, m_size.width(), m_size.height()); } inline IntRect replicaRect() const { return m_layerType == TransparencyLayer ? IntRect(0, 0, m_nearestSurfaceSize.width(), m_nearestSurfaceSize.height()) : entireRect(); } RefPtr m_texture; RefPtr m_surface, m_replicaSurface; ContentData m_pendingContent; ContentData m_currentContent; Vector m_children; TextureMapperNode* m_parent; TextureMapperNode* m_effectTarget; int m_changeMask; IntSize m_size, m_nearestSurfaceSize; String m_name; TextureMapperLayerClient* m_platformClient; struct State { FloatPoint pos; FloatPoint3D anchorPoint; FloatSize size; TransformationMatrix transform; TransformationMatrix childrenTransform; Color backgroundColor; Color currentColor; GraphicsLayer::CompositingCoordinatesOrientation geoOrientation; GraphicsLayer::CompositingCoordinatesOrientation contentsOrientation; float opacity; IntRect contentsRect; int descendantsWithContent; TextureMapperNode* maskLayer; TextureMapperNode* replicaLayer; bool preserves3D; bool masksToBounds; bool drawsContent; bool contentsOpaque; bool backfaceVisibility; bool visible; bool dirty; bool tiled; bool hasSurfaceDescendants; State() : opacity(1.f) , descendantsWithContent(0) , maskLayer(0) , replicaLayer(0) , preserves3D(false) , masksToBounds(false) , drawsContent(false) , contentsOpaque(false) , backfaceVisibility(false) , visible(true) , dirty(true) , tiled(false) , hasSurfaceDescendants(false) { } }; State m_state; }; void TextureMapperNode::setNeedsDisplayInRect(IntRect rect) { if (m_platformClient) { if (m_state.hasSurfaceDescendants) { m_platformClient->setNeedsDisplay(); return; } rect.intersect(IntRect(0, 0, m_size.width(), m_size.height())); if (rect.isEmpty()) return; m_platformClient->setNeedsDisplayInRect(rect); return; } if (!m_parent) return; m_parent->setNeedsDisplayInRect(rect); } void TextureMapperNode::setNeedsDisplay() { if (m_effectTarget) m_effectTarget->setNeedsDisplay(); if (m_transforms.targetBoundingRect.isEmpty()) return; if (m_state.drawsContent || m_currentContent.contentType != HTMLContentType) setNeedsDisplayInRect(m_transforms.targetBoundingRect); } void TextureMapperNode::setPlatformLayerClient(TextureMapperLayerClient* client) { m_platformClient = client; } static int compareGraphicsLayersZValue(const void* a, const void* b) { typedef const TextureMapperNode* NodePtr; const NodePtr* nodeA = static_cast(a); const NodePtr* nodeB = static_cast(b); return int(((*nodeA)->m_transforms.centerZ - (*nodeB)->m_transforms.centerZ) * 1000); } inline static void sortByZOrder(Vector& array, int first, int last) { qsort(array.data(), array.size(), sizeof(TextureMapperNode*), compareGraphicsLayersZValue); } bool TextureMapperNode::hasSurfaceDescendants() const { if (m_layerType == ClipLayer || m_layerType == TransparencyLayer || m_state.replicaLayer) return true; const int size = m_children.size(); for (int i = 0; i < size; ++i) { if (TextureMapperNode* child = m_children[i]) { if (child->hasSurfaceDescendants()) return true; } } return false; } void TextureMapperNode::paint(GraphicsContext* context, const IntSize& size, const IntRect& targetRect, const IntRect& exposedRect, const TransformationMatrix& transform, float opacity) { ASSERT(m_layerType == RootLayer); if (m_size.isEmpty()) return; #if 0 WTF::StopWatch stopWatch; ("[TextureMapper] RootPaint!!\n"); #endif RefPtr textureMapper = TextureMapper::create(context); if (textureMapper->type() != m_lastTextureMapperType) gTextureMapperCache.m_data.clear(); m_lastTextureMapper = textureMapper; TexmapPaintOptions opt; opt.opacity = 1; opt.rootLayer = this; opt.scissorRect = targetRect; opt.visibleRect = exposedRect; opt.textureMapper = textureMapper.get(); opt.context = textureMapper->graphicsContext(); opt.surface = 0; paintRecursive(opt); if (textureMapper->allowSurfaceForRoot() || m_state.hasSurfaceDescendants) { textureMapper->bindSurface(0); textureMapper->paintToTarget(*m_surface.get(), size, transform, opacity * m_state.opacity, targetRect); } gTextureMapperCache.purge(); } int TextureMapperNode::countDescendantsWithContent() const { if (!m_state.visible || m_state.opacity < 0.001) return 0; int descendantsWithContent = (m_state.drawsContent || m_currentContent.contentType != HTMLContentType) ? 1 : 0; const int size = m_children.size(); for (int i = 0; i < size; ++i) { if (TextureMapperNode* child = m_children[i]) descendantsWithContent += child->countDescendantsWithContent(); } return descendantsWithContent; } inline TextureMapperNode* TextureMapperNode::toTextureMapperNode(GraphicsLayer* layer) { return layer ? static_cast(layer)->m_node.get() : 0; } void TextureMapperNode::computeLayerType() { // calculate layer type. A layer can be one of the following: // RootLayer: the top level. Draws to a framebuffer, and the target texture draws into the viewport. // only one layer is the root layer. // ScissorLayer: draws to the current framebuffer, and applies an extra scissor before drawing its children. // A scissor layer is a layer with children that masks to bounds, is not a transparency layer, and has a rectangular clip. // ClipLayer: creates a new framebuffer, the size of the layer, and then paints it to the enclosing BitmapTexture with the layer's transform/opacity. // A clip layer is a layer that masks to bounds, doesn't preserve 3D, has children, and has a transparency/mask or a non-rectangular transform. // TransparencyLayer: creates a new framebuffer idetical in size to the current framebuffer. Then draws the fb's texture to the current framebuffer with identity transform. // Used for layers with children and transparency/mask that preserve 3D or don't mask to bounds. // DefaultLayer: draws itself and its children directly to the current framebuffer. // any layer that doesn't conform to the other rules is a DefaultLayer. const bool selfHasContent = m_state.drawsContent || (m_currentContent.contentType != HTMLContentType); const bool hasDescendantsWithContent = m_state.descendantsWithContent - (selfHasContent ? 1 : 0); const bool hasTransparency = m_state.opacity < 0.99 || m_state.maskLayer; const bool hasReplica = m_state.replicaLayer; m_layerType = DefaultLayer; // Layer has no parent, it must be a root layer. if (!m_parent && !m_effectTarget) { m_layerType = RootLayer; return; } // A layer with no contents is always a default layer. if (!m_state.descendantsWithContent) return; // A layer with content-descendants and a mask is always a clip layer. if (hasDescendantsWithContent && m_state.maskLayer) { m_layerType = ClipLayer; return; } // A masks-to bounds layer can be a clip or a scissor layer. It's a scissor layer only if it has a trivial clip (identity or translation), or if it has transparency. // That's because a ClipLayer would create an intermediate drawing surface (FB) - we want to limit it to when it's actually necessary, i.e. transparency or non-trivial clip. if (m_state.masksToBounds && hasDescendantsWithContent) { if (hasTransparency || !m_state.transform.isIdentityOrTranslation() || m_parent->m_state.preserves3D) m_layerType = ClipLayer; else m_layerType = ScissorLayer; return; } // We use a transparency layer when we have two of the following 3: replica, transparency, descendants with contents. if ((hasReplica && hasDescendantsWithContent) || (hasReplica && hasTransparency) || (hasTransparency && m_state.descendantsWithContent > 1)) m_layerType = TransparencyLayer; } void TextureMapperNode::initializeTextureMapper(TextureMapper* textureMapper) { if (textureMapper->type() == m_lastTextureMapperType) return; m_surface = textureMapper->createTexture(); m_replicaSurface = textureMapper->createTexture(); m_texture = textureMapper->createTexture(); gTextureMapperCache.mark(m_texture.get()); m_lastTextureMapperType = textureMapper->type(); } TextureMapperNode::TextureMapperNode(GraphicsLayerTextureMapper* newLayer) : m_layer(newLayer) , m_lastTextureMapperType(0) , m_lastTextureMapper(0) , m_layerType(DefaultLayer) , m_surface(0) , m_parent(0) , m_effectTarget(0) , m_changeMask(NoChanges) , m_platformClient(0) { } TextureMapperNode* TextureMapperNode::rootLayer() { if (m_effectTarget) return m_effectTarget->rootLayer(); if (m_parent) return m_parent->rootLayer(); return this; } void TextureMapperNode::invalidateTransform() { m_transforms.dirty = true; if (m_layerType != ClipLayer) m_state.dirty = true; if (m_state.replicaLayer) m_state.replicaLayer->invalidateTransform(); const int size = m_children.size(); for (int i = 0; i < size; ++i) { if (TextureMapperNode* layer = m_children[i]) layer->invalidateTransform(); } } void TextureMapperNode::computeLocalTransform() { if (!m_transforms.localDirty) return; const float originX = m_state.anchorPoint.x() * m_size.width(); const float originY = m_state.anchorPoint.y() * m_size.height(); m_transforms.local = TransformationMatrix() .translate3d(originX + m_state.pos.x(), originY + m_state.pos.y(), m_state.anchorPoint.z()) .multLeft(m_state.transform) .translate3d(-originX, -originY, -m_state.anchorPoint.z()); m_transforms.localDirty = false; } void TextureMapperNode::flattenTo2DSpaceIfNecessary() { if (m_state.preserves3D) return; m_transforms.forDescendants.setM13(0); m_transforms.forDescendants.setM23(0); m_transforms.forDescendants.setM31(0); m_transforms.forDescendants.setM32(0); m_transforms.forDescendants.setM33(1); m_transforms.forDescendants.setM34(0); m_transforms.forDescendants.setM43(0); } IntSize TextureMapperNode::nearestSurfaceSize() const { if (m_layerType == ClipLayer || m_layerType == RootLayer) return m_surface && !m_surface->size().isEmpty() ? m_surface->size() : m_size; return m_parent->nearestSurfaceSize(); } void TextureMapperNode::computeReplicaTransform() { if (!m_state.replicaLayer) return; m_nearestSurfaceSize = nearestSurfaceSize(); if (m_layerType != TransparencyLayer) { m_transforms.replica = TransformationMatrix(m_transforms.target).multLeft(m_state.replicaLayer->m_transforms.local); return; } const float originX = m_transforms.target.m41(); const float originY = m_transforms.target.m42(); m_transforms.replica = TransformationMatrix() .translate(originX, originY) .multLeft(m_state.replicaLayer->m_transforms.local) .translate(-originX, -originY); } void TextureMapperNode::computeTransformations() { if (!m_transforms.dirty) return; m_transforms.dirty = false; if ((m_size.isEmpty() && m_state.masksToBounds)) return; TextureMapperNode* parent = m_parent; computeLocalTransform(); m_transforms.target = TransformationMatrix(parent ? parent->m_transforms.forDescendants : TransformationMatrix()).multLeft(m_transforms.local); m_transforms.forDescendants = (m_layerType == ClipLayer ? TransformationMatrix() : m_transforms.target); if (m_effectTarget) return; m_transforms.targetBoundingRect = IntRect(m_transforms.target.mapRect(entireRect())); if (m_state.replicaLayer) m_state.replicaLayer->computeTransformations(); flattenTo2DSpaceIfNecessary(); if (!m_state.backfaceVisibility && m_transforms.target.inverse().m33() < 0) { m_state.visible = false; return; } m_state.visible = true; if (parent && parent->m_state.preserves3D) m_transforms.centerZ = m_transforms.target.mapPoint(FloatPoint3D(m_size.width() / 2, m_size.height() / 2, 0)).z(); if (!m_children.size()) return; if (m_state.childrenTransform.isIdentity()) return; const FloatPoint centerPoint = FloatPoint(m_size.width() / 2, m_size.height() / 2); if (m_transforms.perspectiveDirty) m_transforms.perspective = TransformationMatrix() .translate(centerPoint.x(), centerPoint.y()) .multLeft(m_state.childrenTransform) .translate(-centerPoint.x(), -centerPoint.y()); m_transforms.perspectiveDirty = false; m_transforms.forDescendants.multLeft(m_transforms.perspective); } void TextureMapperNode::uploadTextureFromContent(TextureMapper* textureMapper, const IntRect& visibleRect) { if (m_size.isEmpty() || !m_layer) { m_texture->destroy(); return; } if (m_currentContent.contentType == DirectImageContentType) { if (m_currentContent.image) m_texture->setContentsToImage(m_currentContent.image.get()); return; } if (m_currentContent.contentType == MediaContentType) { if (!m_currentContent.media) return; m_texture->reset(m_size, true); PlatformGraphicsContext* platformContext = m_texture->beginPaintMedia(); GraphicsContext context(platformContext); m_currentContent.media->paint(&context); m_texture->endPaint(); return; } const bool needsReset = (m_texture->contentSize() != m_size) || !m_texture->isValid(); if ((m_currentContent.contentType != HTMLContentType) || (!m_currentContent.needsDisplay && m_currentContent.needsDisplayRect.isEmpty() && !needsReset)) return; WTF::StopWatch stopWatch; IntRect dirtyRect = IntRect(0, 0, m_size.width(), m_size.height()); if (!needsReset && !m_currentContent.needsDisplay) dirtyRect.intersect(m_currentContent.needsDisplayRect); if (needsReset) m_texture->reset(m_size, m_state.contentsOpaque); m_pendingContent.needsDisplayRect = IntRect(); { GraphicsContext context(m_texture->beginPaint(dirtyRect)); if (textureMapper && textureMapper->graphicsContext()) { GraphicsContext* originalContext = textureMapper->graphicsContext(); context.setImageInterpolationQuality(originalContext->imageInterpolationQuality()); context.setTextDrawingMode(originalContext->textDrawingMode()); } m_layer->paintGraphicsLayerContents(context, dirtyRect); } m_texture->endPaint(); { #if 0 LOG("[TextureMapper] Re-render(%d) layer(%p) %d::%d::%d (%dx%d) [%dms]\n", ++renderCount, this, needsReset, m_currentContent.needsDisplay, !m_currentContent.needsDisplayRect.isEmpty(), dirtyRect.width(), dirtyRect.height(), int(stopWatch.elapsed() * 1000)); static int renderCount = 0; m_texture->save(String().format("/tmp/layer_%d.png", renderCount)); #endif } m_currentContent.needsDisplay = false; } void TextureMapperNode::paintSelf(const TexmapPaintOptions& options) { if (!m_layer || m_size.isEmpty() || (!m_state.drawsContent && m_currentContent.contentType == HTMLContentType)) return; RefPtr maskTexture = m_state.maskLayer ? m_state.maskLayer->m_texture : 0; RefPtr replicaMaskTexture = 0; if (m_state.replicaLayer && m_state.replicaLayer->m_state.maskLayer) replicaMaskTexture = m_state.replicaLayer->m_state.maskLayer->m_texture; const float opacity = options.isSurface ? 1 : options.opacity; uploadTextureFromContent(options.textureMapper, options.visibleRect); if (m_state.replicaLayer && !options.isSurface) options.textureMapper->drawTexture(*m_texture.get(), replicaRect(), m_transforms.replica, opacity * m_state.replicaLayer->m_state.opacity, replicaMaskTexture ? replicaMaskTexture.get() : maskTexture.get()); const IntRect rect = m_layerType == ClipLayer ? entireRect() : targetRect(); const TransformationMatrix transform = m_layerType == ClipLayer ? TransformationMatrix() : m_transforms.target; options.textureMapper->drawTexture(*m_texture.get(), rect, transform, opacity, options.isSurface ? 0 : maskTexture.get()); } void TextureMapperNode::paintRecursive(TexmapPaintOptions options) { WTF::StopWatch stopWatch; bool isDirty = m_state.dirty; m_state.dirty = false; if ((m_size.isEmpty() && (m_state.masksToBounds || m_children.isEmpty())) || !m_state.visible || options.opacity < 0.01 || m_state.opacity < 0.01) return; initializeTextureMapper(options.textureMapper); computeReplicaTransform(); if (m_state.maskLayer) { m_state.maskLayer->initializeTextureMapper(options.textureMapper); m_state.maskLayer->m_state.dirty = false; } if (m_state.replicaLayer) { m_state.replicaLayer->initializeTextureMapper(options.textureMapper); m_state.replicaLayer->m_state.dirty = false; if (m_state.replicaLayer->m_state.maskLayer) { m_state.replicaLayer->m_state.maskLayer->initializeTextureMapper(options.textureMapper); m_state.replicaLayer->m_state.maskLayer->m_state.dirty = false; } } TextureMapperNode* replica = m_state.replicaLayer; const bool isSurface = (m_layerType == ClipLayer || m_layerType == TransparencyLayer || (m_layerType == RootLayer && (options.textureMapper->allowSurfaceForRoot() || m_state.hasSurfaceDescendants) )); if (isSurface) uploadTextureFromContent(options.textureMapper, options.visibleRect); const IntRect boundingRectfromNearestSurface = m_transforms.targetBoundingRect; options.opacity *= m_state.opacity; TexmapPaintOptions optionsForDescendants(options); optionsForDescendants.opacity = isSurface ? 1 : options.opacity; options.isSurface = isSurface; if (m_layerType == ClipLayer) { optionsForDescendants.visibleRect = TransformationMatrix().translate(-boundingRectfromNearestSurface.x(), -boundingRectfromNearestSurface.y()).mapRect(options.visibleRect); optionsForDescendants.scissorRect = IntRect(0, 0, m_size.width(), m_size.height()); } if (m_layerType == ScissorLayer) optionsForDescendants.scissorRect.intersect(m_transforms.targetBoundingRect); options.textureMapper->setClip(optionsForDescendants.scissorRect); TextureMapperCacheLock(m_texture.get()); TextureMapperCacheLock(m_surface.get()); TextureMapperCacheLock(m_replicaSurface.get()); gTextureMapperCache.purge(); if (isSurface) { ASSERT(m_surface); if (!m_surface->isValid()) isDirty = true; if (m_state.tiled) { m_surface->reset(options.visibleRect.size()); m_surface->setOffset(options.visibleRect.location()); } else if (isDirty) m_surface->reset(m_layerType == TransparencyLayer ? options.surface->size() : m_size); gTextureMapperCache.mark(m_surface.get()); options.textureMapper->bindSurface(m_surface.get()); optionsForDescendants.surface = m_surface.get(); } else if (m_surface) m_surface->destroy(); RefPtr maskTexture; RefPtr replicaMaskTexture; if (TextureMapperNode* mask = m_state.maskLayer) { mask->uploadTextureFromContent(options.textureMapper, options.visibleRect); maskTexture = mask->m_texture; } if (replica && replica->m_state.maskLayer) { replica->m_state.maskLayer->uploadTextureFromContent(options.textureMapper, options.visibleRect); replicaMaskTexture = replica->m_state.maskLayer->m_texture; } int childrenSize = m_children.size(); if (isDirty || !isSurface || m_state.tiled || !m_surface->isValid()) { bool didPaintSelf = false; if (!m_state.preserves3D || m_children.isEmpty()) { paintSelf(options); didPaintSelf = true; } if (m_children.isEmpty() && !isSurface) return; if (m_layerType == ScissorLayer) optionsForDescendants.scissorRect.intersect(m_transforms.target.mapRect(IntRect(0, 0, m_size.width(), m_size.height()))); for (int i = 0; i < childrenSize; ++i) { TextureMapperNode* layer = m_children[i]; if (!layer) continue; if (!didPaintSelf && layer->m_transforms.centerZ >= 0) { paintSelf(options); didPaintSelf = true; } layer->paintRecursive(optionsForDescendants); if (isSurface) { ASSERT(m_surface); gTextureMapperCache.mark(m_surface.get()); options.textureMapper->bindSurface(m_surface.get()); } } if (!didPaintSelf) { paintSelf(options); didPaintSelf = true; } } if (m_layerType == RootLayer || m_layerType == DefaultLayer || m_layerType == ScissorLayer) return; ASSERT(m_surface); BitmapTexture& texture = *m_surface.get(); if (replica) { ASSERT(m_replicaSurface); m_replicaSurface->reset(options.surface->size()); m_replicaSurface->setOffset(options.surface->offset()); gTextureMapperCache.mark(m_replicaSurface.get()); options.textureMapper->bindSurface(m_replicaSurface.get()); options.textureMapper->drawTexture(texture, replicaRect(), m_transforms.replica, replica->m_state.opacity, replicaMaskTexture ? replicaMaskTexture.get() : maskTexture.get()); options.textureMapper->drawTexture(texture, IntRect(IntPoint(0, 0), options.surface->size()), TransformationMatrix(), 1.0f, maskTexture.get()); options.textureMapper->bindSurface(options.surface); gTextureMapperCache.mark(options.surface); options.textureMapper->drawTexture(*m_replicaSurface.get(), IntRect(IntPoint(0, 0), options.surface->size()), TransformationMatrix(), options.opacity, 0); return; } options.textureMapper->bindSurface(options.surface); options.textureMapper->drawTexture(texture, m_layerType == TransparencyLayer ? IntRect(IntPoint(0, 0), options.surface->size()) : targetRect(), m_layerType == TransparencyLayer ? TransformationMatrix() : m_transforms.target, options.opacity, maskTexture.get()); gTextureMapperCache.mark(&texture); } void TextureMapperNode::cleanupTextureMapper() { if (m_texture) m_texture->destroy(); if (m_surface) m_surface->destroy(); if (m_replicaSurface) m_replicaSurface->destroy(); for (int i = 0; i < m_children.size(); ++i) { if (m_children[i]) m_children[i]->cleanupTextureMapper(); } if (m_lastTextureMapper) m_lastTextureMapper->cleanup(); } TextureMapperNode::~TextureMapperNode() { setNeedsDisplay(); { const int childrenSize = m_children.size(); for (int i = childrenSize-1; i >= 0; --i) { ASSERT(m_children[i]->m_parent == this); m_children[i]->m_parent = 0; } } if (m_parent) m_parent->m_children.remove(m_parent->m_children.find(this)); } void TextureMapperNode::notifyChange(ChangeMask changeMask) { m_changeMask |= changeMask; if (!m_layer->client()) return; m_layer->client()->notifySyncRequired(m_layer); } void TextureMapperNode::performPostSyncOperations() { const LayerType prevLayerType = m_layerType; computeLayerType(); if (prevLayerType != m_layerType) m_state.dirty = true; if (m_transforms.dirty) setNeedsDisplay(); computeTransformations(); if (m_state.maskLayer && !m_state.dirty) m_state.dirty = m_state.maskLayer->m_state.dirty; if (m_state.replicaLayer && !m_state.dirty) m_state.dirty = m_state.replicaLayer->m_state.dirty; const int size = m_children.size(); for (int i = size - 1; i >= 0; --i) { TextureMapperNode* layer = m_children[i]; layer->performPostSyncOperations(); if (!m_state.dirty) m_state.dirty = layer->m_state.dirty; } m_state.hasSurfaceDescendants = hasSurfaceDescendants(); if (m_state.dirty) m_state.descendantsWithContent = countDescendantsWithContent(); if (m_state.preserves3D) sortByZOrder(m_children, 0, size); if (m_state.dirty) setNeedsDisplay(); } void TextureMapperNode::syncCompositingState(bool recurse) { bool needsToInvalidateTransform = false; if (!m_layer) return; if (m_changeMask == NoChanges) goto afterCurrentLayerSync; setNeedsDisplay(); if (m_parent) m_parent->m_state.dirty = true; if (m_currentContent.contentType == HTMLContentType && (m_changeMask & ParentChange)) { // The WebCore compositor manages item ownership. We have to make sure graphicsview doesn't // try to snatch that ownership. if (!m_layer->parent()) m_parent = 0; else m_parent = toTextureMapperNode(m_layer->parent()); if (!m_layer->parent() && m_parent) { size_t index = m_parent->m_children.find(this); m_parent->m_children.remove(index); } } if (m_changeMask & ChildrenChange) { m_children.clear(); for (size_t i = 0; i < m_layer->children().size(); ++i) { if (TextureMapperNode* child = toTextureMapperNode(m_layer->children()[i])) { if (!child) continue; m_children.append(child); child->m_parent = this; } } m_state.dirty = true; } if (m_changeMask & (SizeChange | ContentsRectChange)) { IntSize wantedSize = IntSize(m_layer->size().width(), m_layer->size().height()); if (wantedSize.isEmpty() && m_pendingContent.contentType == HTMLContentType) wantedSize = IntSize(m_layer->contentsRect().width(), m_layer->contentsRect().height()); if (wantedSize != m_size) { m_size = IntSize(wantedSize.width(), wantedSize.height()); if (m_platformClient) m_platformClient->setSizeChanged(m_size); const bool needsTiling = m_size.width() > 2000 || m_size.height() > 2000; if (m_state.tiled != needsTiling) m_state.tiled = needsTiling; m_state.dirty = true; } } if (m_changeMask & MaskLayerChange) { if (TextureMapperNode* layer = toTextureMapperNode(m_layer->maskLayer())) layer->m_effectTarget = this; } if (m_changeMask & ReplicaLayerChange) { if (TextureMapperNode* layer = toTextureMapperNode(m_layer->replicaLayer())) layer->m_effectTarget = this; } if (m_changeMask & (TransformChange | SizeChange | AnchorPointChange | PositionChange)) m_transforms.localDirty = true; if (m_changeMask & (ChildrenTransformChange | SizeChange)) m_transforms.perspectiveDirty = true; if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange | ContentsRectChange | BackfaceVisibilityChange | PositionChange | MaskLayerChange | DrawsContentChange | ContentChange | ReplicaLayerChange)) { // 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. needsToInvalidateTransform = true; } if (m_changeMask & DisplayChange) m_state.dirty = true; m_state.maskLayer = toTextureMapperNode(m_layer->maskLayer()); m_state.replicaLayer = toTextureMapperNode(m_layer->replicaLayer()); m_state.pos = m_layer->position(); m_state.anchorPoint = m_layer->anchorPoint(); m_state.size = m_layer->size(); m_state.transform = m_layer->transform(); m_state.contentsRect = m_layer->contentsRect(); m_state.opacity = m_layer->opacity(); m_state.contentsRect = m_layer->contentsRect(); m_state.preserves3D = m_layer->preserves3D(); m_state.masksToBounds = m_layer->masksToBounds(); m_state.drawsContent = m_layer->drawsContent(); m_state.contentsOpaque = m_layer->contentsOpaque(); m_state.backfaceVisibility = m_layer->backfaceVisibility(); m_state.childrenTransform = m_layer->childrenTransform(); m_currentContent.contentType = m_pendingContent.contentType; m_currentContent.image = m_pendingContent.image; m_currentContent.media = m_pendingContent.media; m_currentContent.backgroundColor = m_pendingContent.backgroundColor; m_currentContent.needsDisplay = m_currentContent.needsDisplay || m_pendingContent.needsDisplay; m_currentContent.needsDisplayRect.unite(m_pendingContent.needsDisplayRect); m_pendingContent.needsDisplay = false; m_pendingContent.needsDisplayRect = IntRect(); m_changeMask = NoChanges; afterCurrentLayerSync: if (needsToInvalidateTransform) invalidateTransform(); if (m_state.maskLayer) { m_state.maskLayer->syncCompositingState(false); if (m_state.maskLayer->m_size.isEmpty()) m_state.maskLayer->m_size = m_size; } if (m_state.replicaLayer) m_state.replicaLayer->syncCompositingState(false); #if 0 if (m_state.dirty && m_texture && m_texture->allowOfflineTextureUpload()) uploadTextureFromContent(0); #endif if (!recurse) return; const int childrenSize = m_children.size(); for (int i = childrenSize-1; i >= 0; --i) m_children[i]->syncCompositingState(true); } GraphicsLayerTextureMapper::GraphicsLayerTextureMapper(GraphicsLayerClient* client) : GraphicsLayer(client) , m_node(new TextureMapperNode(this)) { } void GraphicsLayerTextureMapper::setName(const String& name) { m_node->m_name = name; } GraphicsLayerTextureMapper::~GraphicsLayerTextureMapper() { } /* \reimp (GraphicsLayer.h): The current size might change, thus we need to update the whole display. */ void GraphicsLayerTextureMapper::setNeedsDisplay() { m_node->m_pendingContent.needsDisplay = true; m_node->notifyChange(TextureMapperNode::DisplayChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setNeedsDisplayInRect(const FloatRect& rect) { if (m_node->m_pendingContent.needsDisplay) return; m_node->m_pendingContent.needsDisplayRect.unite(IntRect(rect)); m_node->notifyChange(TextureMapperNode::DisplayChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setParent(GraphicsLayer* layer) { m_node->notifyChange(TextureMapperNode::ParentChange); GraphicsLayer::setParent(layer); } /* \reimp (GraphicsLayer.h) */ bool GraphicsLayerTextureMapper::setChildren(const Vector& children) { m_node->notifyChange(TextureMapperNode::ChildrenChange); return GraphicsLayer::setChildren(children); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::addChild(GraphicsLayer* layer) { m_node->notifyChange(TextureMapperNode::ChildrenChange); GraphicsLayer::addChild(layer); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::addChildAtIndex(GraphicsLayer* layer, int index) { GraphicsLayer::addChildAtIndex(layer, index); m_node->notifyChange(TextureMapperNode::ChildrenChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling) { GraphicsLayer::addChildAbove(layer, sibling); m_node->notifyChange(TextureMapperNode::ChildrenChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling) { GraphicsLayer::addChildBelow(layer, sibling); m_node->notifyChange(TextureMapperNode::ChildrenChange); } /* \reimp (GraphicsLayer.h) */ bool GraphicsLayerTextureMapper::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) { if (GraphicsLayer::replaceChild(oldChild, newChild)) { m_node->notifyChange(TextureMapperNode::ChildrenChange); return true; } return false; } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::removeFromParent() { if (!parent()) return; m_node->notifyChange(TextureMapperNode::ParentChange); GraphicsLayer::removeFromParent(); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setMaskLayer(GraphicsLayer* value) { if (value == maskLayer()) return; GraphicsLayer::setMaskLayer(value); m_node->notifyChange(TextureMapperNode::MaskLayerChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setReplicatedByLayer(GraphicsLayer* value) { if (value == replicaLayer()) return; GraphicsLayer::setReplicatedByLayer(value); m_node->notifyChange(TextureMapperNode::ReplicaLayerChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setPosition(const FloatPoint& value) { if (value == position()) return; GraphicsLayer::setPosition(value); m_node->notifyChange(TextureMapperNode::PositionChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setAnchorPoint(const FloatPoint3D& value) { if (value == anchorPoint()) return; GraphicsLayer::setAnchorPoint(value); m_node->notifyChange(TextureMapperNode::AnchorPointChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setSize(const FloatSize& value) { if (value == size()) return; GraphicsLayer::setSize(value); m_node->notifyChange(TextureMapperNode::SizeChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setTransform(const TransformationMatrix& value) { if (value == transform()) return; GraphicsLayer::setTransform(value); m_node->notifyChange(TextureMapperNode::TransformChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setChildrenTransform(const TransformationMatrix& value) { if (value == childrenTransform()) return; GraphicsLayer::setChildrenTransform(value); m_node->notifyChange(TextureMapperNode::ChildrenTransformChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setPreserves3D(bool value) { if (value == preserves3D()) return; GraphicsLayer::setPreserves3D(value); m_node->notifyChange(TextureMapperNode::Preserves3DChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setMasksToBounds(bool value) { if (value == masksToBounds()) return; GraphicsLayer::setMasksToBounds(value); m_node->notifyChange(TextureMapperNode::MasksToBoundsChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setDrawsContent(bool value) { if (value == drawsContent()) return; m_node->notifyChange(TextureMapperNode::DrawsContentChange); GraphicsLayer::setDrawsContent(value); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setBackgroundColor(const Color& value) { if (value == m_node->m_pendingContent.backgroundColor) return; m_node->m_pendingContent.backgroundColor = value; GraphicsLayer::setBackgroundColor(value); m_node->notifyChange(TextureMapperNode::BackgroundColorChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::clearBackgroundColor() { if (!m_node->m_pendingContent.backgroundColor.isValid()) return; m_node->m_pendingContent.backgroundColor = Color(); GraphicsLayer::clearBackgroundColor(); m_node->notifyChange(TextureMapperNode::BackgroundColorChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setContentsOpaque(bool value) { if (value == contentsOpaque()) return; m_node->notifyChange(TextureMapperNode::ContentsOpaqueChange); GraphicsLayer::setContentsOpaque(value); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setBackfaceVisibility(bool value) { if (value == backfaceVisibility()) return; GraphicsLayer::setBackfaceVisibility(value); m_node->notifyChange(TextureMapperNode::BackfaceVisibilityChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setOpacity(float value) { if (value == opacity()) return; GraphicsLayer::setOpacity(value); m_node->notifyChange(TextureMapperNode::OpacityChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setContentsRect(const IntRect& value) { if (value == contentsRect()) return; GraphicsLayer::setContentsRect(value); m_node->notifyChange(TextureMapperNode::ContentsRectChange); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setContentsToImage(Image* image) { m_node->notifyChange(TextureMapperNode::ContentChange); m_node->m_pendingContent.contentType = image ? TextureMapperNode::DirectImageContentType : TextureMapperNode::HTMLContentType; m_node->m_pendingContent.image = image; GraphicsLayer::setContentsToImage(image); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setContentsBackgroundColor(const Color& color) { m_node->notifyChange(TextureMapperNode::ContentChange); m_node->m_pendingContent.contentType = TextureMapperNode::ColorContentType; m_node->m_pendingContent.backgroundColor = color; GraphicsLayer::setContentsBackgroundColor(color); } void GraphicsLayerTextureMapper::setContentsToMedia(PlatformLayer* media) { GraphicsLayer::setContentsToMedia(media); m_node->notifyChange(TextureMapperNode::ContentChange); m_node->m_pendingContent.contentType = media ? TextureMapperNode::MediaContentType : TextureMapperNode::HTMLContentType; if (media) m_node->m_pendingContent.media = static_cast(media); else m_node->m_pendingContent.media = 0; } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setContentsOrientation(CompositingCoordinatesOrientation orientation) { if (contentsOrientation() == orientation) return; m_node->notifyChange(TextureMapperNode::ContentsOrientationChange); GraphicsLayer::setContentsOrientation(orientation); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::syncCompositingStateForThisLayerOnly() { m_node->syncCompositingState(false); m_node->performPostSyncOperations(); } /* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::syncCompositingState() { GraphicsLayer::syncCompositingState(); m_node->syncCompositingState(true); m_node->performPostSyncOperations(); } /* \reimp (GraphicsLayer.h) */ NativeLayer GraphicsLayerTextureMapper::nativeLayer() const { return m_node.get(); } /* \reimp (GraphicsLayer.h) */ PlatformLayer* GraphicsLayerTextureMapper::platformLayer() const { return m_node.get(); } PassOwnPtr GraphicsLayer::create(GraphicsLayerClient* client) { return new GraphicsLayerTextureMapper(client); } }