summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp')
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp514
1 files changed, 514 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
new file mode 100644
index 000000000..da0b40e36
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
@@ -0,0 +1,514 @@
+/*
+ Copyright (C) 2010 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 "TextureMapperLayer.h"
+
+#include "stdio.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "GraphicsLayerTextureMapper.h"
+#include "ImageBuffer.h"
+#include "MathExtras.h"
+
+namespace WebCore {
+
+TextureMapperLayer* toTextureMapperLayer(GraphicsLayer* layer)
+{
+ return layer ? toGraphicsLayerTextureMapper(layer)->layer() : 0;
+}
+
+TextureMapperLayer* TextureMapperLayer::rootLayer()
+{
+ if (m_effectTarget)
+ return m_effectTarget->rootLayer();
+ if (m_parent)
+ return m_parent->rootLayer();
+ return this;
+}
+
+void TextureMapperLayer::setTransform(const TransformationMatrix& matrix)
+{
+ m_transform.setLocalTransform(matrix);
+}
+
+void TextureMapperLayer::clearBackingStoresRecursive()
+{
+ m_backingStore.clear();
+ m_contentsLayer = 0;
+ for (size_t i = 0; i < m_children.size(); ++i)
+ m_children[i]->clearBackingStoresRecursive();
+ if (m_state.maskLayer)
+ m_state.maskLayer->clearBackingStoresRecursive();
+}
+
+void TextureMapperLayer::computeTransformsRecursive()
+{
+ if (m_size.isEmpty() && m_state.masksToBounds)
+ return;
+
+ // Compute transforms recursively on the way down to leafs.
+ TransformationMatrix parentTransform;
+ if (m_parent)
+ parentTransform = m_parent->m_transform.combinedForChildren();
+ else if (m_effectTarget)
+ parentTransform = m_effectTarget->m_transform.combined();
+ m_transform.combineTransforms(parentTransform);
+
+ m_state.visible = m_state.backfaceVisibility || m_transform.combined().inverse().m33() >= 0;
+
+ if (m_parent && m_parent->m_state.preserves3D)
+ m_centerZ = m_transform.combined().mapPoint(FloatPoint3D(m_size.width() / 2, m_size.height() / 2, 0)).z();
+
+ if (m_state.maskLayer)
+ m_state.maskLayer->computeTransformsRecursive();
+ if (m_state.replicaLayer)
+ m_state.replicaLayer->computeTransformsRecursive();
+ for (size_t i = 0; i < m_children.size(); ++i)
+ m_children[i]->computeTransformsRecursive();
+
+ // Reorder children if needed on the way back up.
+ if (m_state.preserves3D)
+ sortByZOrder(m_children, 0, m_children.size());
+}
+
+void TextureMapperLayer::updateBackingStore(TextureMapper* textureMapper, GraphicsLayer* layer)
+{
+ if (!layer || !textureMapper)
+ return;
+
+ if (!m_shouldUpdateBackingStoreFromLayer)
+ return;
+
+ if (!m_state.drawsContent || m_size.isEmpty()) {
+ m_backingStore.clear();
+ return;
+ }
+
+ IntRect dirtyRect = enclosingIntRect(m_state.needsDisplay ? layerRect() : m_state.needsDisplayRect);
+ if (dirtyRect.isEmpty())
+ return;
+
+ if (!m_backingStore)
+ m_backingStore = TextureMapperTiledBackingStore::create();
+
+#if PLATFORM(QT)
+ ASSERT(dynamic_cast<TextureMapperTiledBackingStore*>(m_backingStore.get()));
+#endif
+
+ // Paint the entire dirty rect into an image buffer. This ensures we only paint once.
+ OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(dirtyRect.size());
+ GraphicsContext* context = imageBuffer->context();
+ context->setImageInterpolationQuality(textureMapper->imageInterpolationQuality());
+ context->setTextDrawingMode(textureMapper->textDrawingMode());
+ context->translate(-dirtyRect.x(), -dirtyRect.y());
+ layer->paintGraphicsLayerContents(*context, dirtyRect);
+
+ RefPtr<Image> image;
+
+#if PLATFORM(QT)
+ image = imageBuffer->copyImage(DontCopyBackingStore);
+#else
+ // FIXME: support DontCopyBackingStore in non-Qt ports that use TextureMapper.
+ image = imageBuffer->copyImage(CopyBackingStore);
+#endif
+
+ static_cast<TextureMapperTiledBackingStore*>(m_backingStore.get())->updateContents(textureMapper, image.get(), m_size, dirtyRect, BitmapTexture::BGRAFormat);
+}
+
+void TextureMapperLayer::paint()
+{
+ computeTransformsRecursive();
+
+ TextureMapperPaintOptions options;
+ options.textureMapper = m_textureMapper;
+ options.textureMapper->bindSurface(0);
+ paintRecursive(options);
+}
+
+void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options)
+{
+ // We apply the following transform to compensate for painting into a surface, and then apply the offset so that the painting fits in the target rect.
+ TransformationMatrix transform =
+ TransformationMatrix(options.transform)
+ .multiply(m_transform.combined())
+ .translate(options.offset.width(), options.offset.height());
+
+ float opacity = options.opacity;
+ RefPtr<BitmapTexture> mask = options.mask;
+
+ if (m_backingStore)
+ m_backingStore->paintToTextureMapper(options.textureMapper, layerRect(), transform, opacity, mask.get());
+
+ if (m_contentsLayer)
+ m_contentsLayer->paintToTextureMapper(options.textureMapper, m_state.contentsRect, transform, opacity, mask.get());
+}
+
+int TextureMapperLayer::compareGraphicsLayersZValue(const void* a, const void* b)
+{
+ TextureMapperLayer* const* layerA = static_cast<TextureMapperLayer* const*>(a);
+ TextureMapperLayer* const* layerB = static_cast<TextureMapperLayer* const*>(b);
+ return int(((*layerA)->m_centerZ - (*layerB)->m_centerZ) * 1000);
+}
+
+void TextureMapperLayer::sortByZOrder(Vector<TextureMapperLayer* >& array, int first, int last)
+{
+ qsort(array.data(), array.size(), sizeof(TextureMapperLayer*), compareGraphicsLayersZValue);
+}
+
+void TextureMapperLayer::paintSelfAndChildren(const TextureMapperPaintOptions& options)
+{
+ paintSelf(options);
+
+ if (m_children.isEmpty())
+ return;
+
+ bool shouldClip = m_state.masksToBounds && !m_state.preserves3D;
+ if (shouldClip)
+ options.textureMapper->beginClip(TransformationMatrix(options.transform).multiply(m_transform.combined()), layerRect());
+
+ for (int i = 0; i < m_children.size(); ++i)
+ m_children[i]->paintRecursive(options);
+
+ if (shouldClip)
+ options.textureMapper->endClip();
+}
+
+IntRect TextureMapperLayer::intermediateSurfaceRect()
+{
+ // FIXME: Add an inverse transform to LayerTransform.
+ return intermediateSurfaceRect(m_transform.combined().inverse());
+}
+
+IntRect TextureMapperLayer::intermediateSurfaceRect(const TransformationMatrix& matrix)
+{
+ IntRect rect;
+ TransformationMatrix localTransform = TransformationMatrix(matrix).multiply(m_transform.combined());
+ rect = enclosingIntRect(localTransform.mapRect(layerRect()));
+ if (!m_state.masksToBounds && !m_state.maskLayer) {
+ for (size_t i = 0; i < m_children.size(); ++i)
+ rect.unite(m_children[i]->intermediateSurfaceRect(matrix));
+ }
+
+ if (m_state.replicaLayer)
+ rect.unite(m_state.replicaLayer->intermediateSurfaceRect(matrix));
+
+ return rect;
+}
+
+bool TextureMapperLayer::shouldPaintToIntermediateSurface() const
+{
+#if ENABLE(CSS_FILTERS)
+ if (m_state.filters.size())
+ return true;
+#endif
+ bool hasOpacity = m_opacity < 0.99;
+ bool hasChildren = !m_children.isEmpty();
+ bool hasReplica = !!m_state.replicaLayer;
+ bool hasMask = !!m_state.maskLayer;
+
+ // We don't use two-pass blending for preserves-3d, that's in sync with Safari.
+ if (m_state.preserves3D)
+ return false;
+
+ // We should use an intermediate surface when blending several items with an ancestor opacity.
+ // Tested by compositing/reflections/reflection-opacity.html
+ if (hasOpacity && (hasChildren || hasReplica))
+ return true;
+
+ // We should use an intermediate surface with a masked ancestor.
+ // In the case of replicas the mask is applied before replicating.
+ // Tested by compositing/masks/masked-ancestor.html
+ if (hasMask && hasChildren && !hasReplica)
+ return true;
+
+ return false;
+}
+
+bool TextureMapperLayer::isVisible() const
+{
+ if (m_size.isEmpty() && (m_state.masksToBounds || m_state.maskLayer || m_children.isEmpty()))
+ return false;
+ if (!m_state.visible || m_opacity < 0.01)
+ return false;
+ return true;
+}
+
+void TextureMapperLayer::paintSelfAndChildrenWithReplica(const TextureMapperPaintOptions& options)
+{
+ if (m_state.replicaLayer) {
+ TextureMapperPaintOptions replicaOptions(options);
+ // We choose either the content's mask or the replica's mask.
+ // FIXME: blend the two if both exist.
+ if (m_state.replicaLayer->m_state.maskLayer)
+ replicaOptions.mask = m_state.replicaLayer->m_state.maskLayer->texture();
+
+ replicaOptions.transform
+ .multiply(m_state.replicaLayer->m_transform.combined())
+ .multiply(m_transform.combined().inverse());
+ paintSelfAndChildren(replicaOptions);
+ }
+
+ paintSelfAndChildren(options);
+}
+
+#if ENABLE(CSS_FILTERS)
+static PassRefPtr<BitmapTexture> applyFilters(const FilterOperations& filters, TextureMapper* textureMapper, BitmapTexture* source, IntRect& targetRect)
+{
+ if (!filters.size())
+ return source;
+
+ RefPtr<BitmapTexture> filterSurface(source);
+ int leftOutset, topOutset, bottomOutset, rightOutset;
+ if (filters.hasOutsets()) {
+ filters.getOutsets(topOutset, rightOutset, bottomOutset, leftOutset);
+ IntRect unfilteredTargetRect(targetRect);
+ targetRect.move(std::max(0, -leftOutset), std::max(0, -topOutset));
+ targetRect.expand(leftOutset + rightOutset, topOutset + bottomOutset);
+ targetRect.unite(unfilteredTargetRect);
+ filterSurface = textureMapper->acquireTextureFromPool(targetRect.size());
+ }
+
+ filterSurface->applyFilters(*source, filters);
+ return filterSurface;
+}
+#endif
+
+void TextureMapperLayer::paintRecursive(const TextureMapperPaintOptions& options)
+{
+ if (!isVisible())
+ return;
+
+ float opacity = options.opacity * m_opacity;
+ RefPtr<BitmapTexture> maskTexture = m_state.maskLayer ? m_state.maskLayer->texture() : 0;
+
+ TextureMapperPaintOptions paintOptions(options);
+ paintOptions.mask = maskTexture.get();
+ IntRect surfaceRect;
+
+ RefPtr<BitmapTexture> surface;
+
+ if (!shouldPaintToIntermediateSurface()) {
+ paintOptions.opacity = opacity;
+ paintSelfAndChildrenWithReplica(paintOptions);
+ return;
+ }
+
+ // Prepare a surface to paint into.
+ // We paint into the surface ignoring the opacity/transform of the current layer.
+ surfaceRect = intermediateSurfaceRect();
+ surface = options.textureMapper->acquireTextureFromPool(surfaceRect.size());
+ options.textureMapper->bindSurface(surface.get());
+ paintOptions.opacity = 1;
+
+ // We have to use combinedForChildren() and not combined(), otherwise preserve-3D doesn't work.
+ paintOptions.transform = m_transform.combinedForChildren().inverse();
+ paintOptions.offset = -IntSize(surfaceRect.x(), surfaceRect.y());
+
+ paintSelfAndChildrenWithReplica(paintOptions);
+
+ // If we painted the replica, the mask is already applied so we don't need to paint it again.
+ if (m_state.replicaLayer)
+ maskTexture = 0;
+
+#if ENABLE(CSS_FILTERS)
+ surface = applyFilters(m_state.filters, options.textureMapper, surface.get(), surfaceRect);
+#endif
+
+ options.textureMapper->bindSurface(options.surface.get());
+ TransformationMatrix targetTransform =
+ TransformationMatrix(options.transform)
+ .multiply(m_transform.combined())
+ .translate(options.offset.width(), options.offset.height());
+ options.textureMapper->drawTexture(*surface.get(), surfaceRect, targetTransform, opacity, maskTexture.get());
+}
+
+TextureMapperLayer::~TextureMapperLayer()
+{
+ for (int i = m_children.size() - 1; i >= 0; --i)
+ m_children[i]->m_parent = 0;
+
+ if (m_parent)
+ m_parent->m_children.remove(m_parent->m_children.find(this));
+}
+
+void TextureMapperLayer::syncCompositingState(GraphicsLayerTextureMapper* graphicsLayer, int options)
+{
+ syncCompositingState(graphicsLayer, rootLayer()->m_textureMapper, options);
+}
+
+void TextureMapperLayer::syncCompositingStateSelf(GraphicsLayerTextureMapper* graphicsLayer, TextureMapper* textureMapper)
+{
+ int changeMask = graphicsLayer->changeMask();
+
+ if (changeMask == NoChanges && graphicsLayer->m_animations.isEmpty())
+ return;
+
+ if (changeMask & ParentChange) {
+ TextureMapperLayer* newParent = toTextureMapperLayer(graphicsLayer->parent());
+ if (newParent != m_parent) {
+ // Remove layer from current from child list first.
+ if (m_parent) {
+ size_t index = m_parent->m_children.find(this);
+ m_parent->m_children.remove(index);
+ m_parent = 0;
+ }
+ // Set new layer parent and add layer to the parents child list.
+ if (newParent) {
+ m_parent = newParent;
+ m_parent->m_children.append(this);
+ }
+ }
+ }
+
+ if (changeMask & ChildrenChange) {
+ // Clear children parent pointer to avoid unsync and crash on layer delete.
+ for (size_t i = 0; i < m_children.size(); i++)
+ m_children[i]->m_parent = 0;
+
+ m_children.clear();
+ for (size_t i = 0; i < graphicsLayer->children().size(); ++i) {
+ TextureMapperLayer* child = toTextureMapperLayer(graphicsLayer->children()[i]);
+ if (!child)
+ continue;
+ m_children.append(child);
+ child->m_parent = this;
+ }
+ }
+
+ m_size = graphicsLayer->size();
+
+ if (changeMask & MaskLayerChange) {
+ if (TextureMapperLayer* layer = toTextureMapperLayer(graphicsLayer->maskLayer()))
+ layer->m_effectTarget = this;
+ }
+
+ if (changeMask & ReplicaLayerChange) {
+ if (TextureMapperLayer* layer = toTextureMapperLayer(graphicsLayer->replicaLayer()))
+ layer->m_effectTarget = this;
+ }
+
+ if (changeMask & AnimationChange)
+ m_animations = graphicsLayer->m_animations;
+
+ m_state.maskLayer = toTextureMapperLayer(graphicsLayer->maskLayer());
+ m_state.replicaLayer = toTextureMapperLayer(graphicsLayer->replicaLayer());
+ m_state.pos = graphicsLayer->position();
+ m_state.anchorPoint = graphicsLayer->anchorPoint();
+ m_state.size = graphicsLayer->size();
+ m_state.contentsRect = graphicsLayer->contentsRect();
+ m_state.transform = graphicsLayer->transform();
+ m_state.contentsRect = graphicsLayer->contentsRect();
+ m_state.preserves3D = graphicsLayer->preserves3D();
+ m_state.masksToBounds = graphicsLayer->masksToBounds();
+ m_state.drawsContent = graphicsLayer->drawsContent();
+ m_state.contentsOpaque = graphicsLayer->contentsOpaque();
+ m_state.backfaceVisibility = graphicsLayer->backfaceVisibility();
+ m_state.childrenTransform = graphicsLayer->childrenTransform();
+ m_state.opacity = graphicsLayer->opacity();
+#if ENABLE(CSS_FILTERS)
+ m_state.filters = graphicsLayer->filters();
+#endif
+
+ m_state.needsDisplay = m_state.needsDisplay || graphicsLayer->needsDisplay();
+ if (!m_state.needsDisplay)
+ m_state.needsDisplayRect.unite(graphicsLayer->needsDisplayRect());
+ m_contentsLayer = graphicsLayer->contentsLayer();
+
+ m_transform.setPosition(m_state.pos);
+ m_transform.setAnchorPoint(m_state.anchorPoint);
+ m_transform.setSize(m_state.size);
+ m_transform.setFlattening(!m_state.preserves3D);
+ m_transform.setChildrenTransform(m_state.childrenTransform);
+}
+
+bool TextureMapperLayer::descendantsOrSelfHaveRunningAnimations() const
+{
+ if (m_animations.hasRunningAnimations())
+ return true;
+
+ for (size_t i = 0; i < m_children.size(); ++i) {
+ if (m_children[i]->descendantsOrSelfHaveRunningAnimations())
+ return true;
+ }
+
+ return false;
+}
+
+void TextureMapperLayer::syncAnimations()
+{
+ m_animations.apply(this);
+ if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyWebkitTransform))
+ setTransform(m_state.transform);
+ if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyOpacity))
+ setOpacity(m_state.opacity);
+}
+
+void TextureMapperLayer::syncAnimationsRecursively()
+{
+ syncAnimations();
+
+ for (int i = m_children.size() - 1; i >= 0; --i)
+ m_children[i]->syncAnimationsRecursively();
+}
+
+void TextureMapperLayer::syncCompositingState(GraphicsLayerTextureMapper* graphicsLayer, TextureMapper* textureMapper, int options)
+{
+ if (!textureMapper)
+ return;
+
+ if (graphicsLayer && !(options & ComputationsOnly)) {
+ syncCompositingStateSelf(graphicsLayer, textureMapper);
+ graphicsLayer->didSynchronize();
+ }
+
+ if (graphicsLayer && m_state.maskLayer) {
+ m_state.maskLayer->syncCompositingState(toGraphicsLayerTextureMapper(graphicsLayer->maskLayer()), textureMapper);
+
+ // A mask layer has its parent's size by default, in case it's not set specifically.
+ if (m_state.maskLayer->m_size.isEmpty())
+ m_state.maskLayer->m_size = m_size;
+ }
+
+ if (m_state.replicaLayer)
+ m_state.replicaLayer->syncCompositingState(toGraphicsLayerTextureMapper(graphicsLayer->replicaLayer()), textureMapper);
+
+ syncAnimations();
+ updateBackingStore(textureMapper, graphicsLayer);
+
+ if (!(options & TraverseDescendants))
+ options = ComputationsOnly;
+
+ if (graphicsLayer) {
+ Vector<GraphicsLayer*> children = graphicsLayer->children();
+ for (int i = children.size() - 1; i >= 0; --i) {
+ TextureMapperLayer* layer = toTextureMapperLayer(children[i]);
+ if (!layer)
+ continue;
+ layer->syncCompositingState(toGraphicsLayerTextureMapper(children[i]), textureMapper, options);
+ }
+ } else {
+ for (int i = m_children.size() - 1; i >= 0; --i)
+ m_children[i]->syncCompositingState(0, textureMapper, options);
+ }
+}
+
+}
+#endif