/*-*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "LayerTreeInvalidation.h" #include "Layers.h" #include "ImageLayers.h" #include "gfxUtils.h" namespace mozilla { namespace layers { struct LayerPropertiesBase; LayerPropertiesBase* CloneLayerTreePropertiesInternal(Layer* aRoot); static nsIntRect TransformRect(const nsIntRect& aRect, const gfx3DMatrix& aTransform) { if (aRect.IsEmpty()) { return nsIntRect(); } gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height); rect = aTransform.TransformBounds(rect); rect.RoundOut(); nsIntRect intRect; if (!gfxUtils::GfxRectToIntRect(rect, &intRect)) { return nsIntRect(); } return intRect; } /** * Walks over this layer, and all descendant layers. * If any of these are a ContainerLayer that reports invalidations to a PresShell, * then report that the entire bounds have changed. */ static void NotifySubdocumentInvalidationRecursive(Layer* aLayer, NotifySubDocInvalidationFunc aCallback) { aLayer->ClearInvalidRect(); ContainerLayer* container = aLayer->AsContainerLayer(); if (aLayer->GetMaskLayer()) { NotifySubdocumentInvalidationRecursive(aLayer->GetMaskLayer(), aCallback); } if (!container) { return; } for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { NotifySubdocumentInvalidationRecursive(child, aCallback); } aCallback(container, container->GetVisibleRegion()); } struct LayerPropertiesBase : public LayerProperties { LayerPropertiesBase(Layer* aLayer) : mLayer(aLayer) , mMaskLayer(nullptr) , mVisibleRegion(aLayer->GetVisibleRegion()) , mTransform(aLayer->GetTransform()) , mOpacity(aLayer->GetOpacity()) , mUseClipRect(!!aLayer->GetClipRect()) { MOZ_COUNT_CTOR(LayerPropertiesBase); if (aLayer->GetMaskLayer()) { mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer()); } if (mUseClipRect) { mClipRect = *aLayer->GetClipRect(); } } LayerPropertiesBase() : mLayer(nullptr) , mMaskLayer(nullptr) { MOZ_COUNT_CTOR(LayerPropertiesBase); } ~LayerPropertiesBase() { MOZ_COUNT_DTOR(LayerPropertiesBase); } virtual nsIntRect ComputeDifferences(Layer* aRoot, NotifySubDocInvalidationFunc aCallback); virtual void MoveBy(const nsIntPoint& aOffset); nsIntRect ComputeChange(NotifySubDocInvalidationFunc aCallback) { bool transformChanged = mTransform != mLayer->GetTransform(); Layer* otherMask = mLayer->GetMaskLayer(); const nsIntRect* otherClip = mLayer->GetClipRect(); nsIntRect result; if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask || (mUseClipRect != !!otherClip) || mLayer->GetOpacity() != mOpacity || transformChanged) { result = OldTransformedBounds(); if (transformChanged) { result = result.Union(NewTransformedBounds()); } // If we don't have to generate invalidations separately for child // layers then we can just stop here since we've already invalidated the entire // old and new bounds. if (!aCallback) { ClearInvalidations(mLayer); return result; } } nsIntRegion visible; visible.Xor(mVisibleRegion, mLayer->GetVisibleRegion()); result = result.Union(TransformRect(visible.GetBounds(), mTransform)); result = result.Union(ComputeChangeInternal(aCallback)); result = result.Union(TransformRect(mLayer->GetInvalidRegion().GetBounds(), mTransform)); if (mMaskLayer && otherMask) { nsIntRect maskDiff = mMaskLayer->ComputeChange(aCallback); result = result.Union(TransformRect(maskDiff, mTransform)); } if (mUseClipRect && otherClip) { if (!mClipRect.IsEqualInterior(*otherClip)) { nsIntRegion tmp; tmp.Xor(mClipRect, *otherClip); result = result.Union(tmp.GetBounds()); } } mLayer->ClearInvalidRect(); return result; } nsIntRect NewTransformedBounds() { return TransformRect(mLayer->GetVisibleRegion().GetBounds(), mLayer->GetTransform()); } nsIntRect OldTransformedBounds() { return TransformRect(mVisibleRegion.GetBounds(), mTransform); } virtual nsIntRect ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback) { return nsIntRect(); } nsRefPtr mLayer; nsAutoPtr mMaskLayer; nsIntRegion mVisibleRegion; gfx3DMatrix mTransform; float mOpacity; nsIntRect mClipRect; bool mUseClipRect; }; struct ContainerLayerProperties : public LayerPropertiesBase { ContainerLayerProperties(ContainerLayer* aLayer) : LayerPropertiesBase(aLayer) { for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { mChildren.AppendElement(CloneLayerTreePropertiesInternal(child)); } } virtual nsIntRect ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback) { ContainerLayer* container = mLayer->AsContainerLayer(); nsIntRegion result; uint32_t i = 0; for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { if (i >= mChildren.Length() || child != mChildren[i]->mLayer) { // Child change. Invalidate the full areas. // TODO: We could be smarter here if non-overlapping children // swap order. result.Or(result, TransformRect(child->GetVisibleRegion().GetBounds(), child->GetTransform())); if (i < mChildren.Length()) { result.Or(result, mChildren[i]->OldTransformedBounds()); } if (aCallback) { NotifySubdocumentInvalidationRecursive(child, aCallback); } else { ClearInvalidations(child); } } else { // Same child, check for differences within the child result.Or(result, mChildren[i]->ComputeChange(aCallback)); } i++; } // Process remaining removed children. while (i < mChildren.Length()) { result.Or(result, mChildren[i]->OldTransformedBounds()); i++; } if (aCallback) { aCallback(container, result); } return TransformRect(result.GetBounds(), mLayer->GetTransform()); } nsAutoTArray,1> mChildren; }; struct ColorLayerProperties : public LayerPropertiesBase { ColorLayerProperties(ColorLayer *aLayer) : LayerPropertiesBase(aLayer) , mColor(aLayer->GetColor()) { } virtual nsIntRect ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback) { ColorLayer* color = static_cast(mLayer.get()); if (mColor != color->GetColor()) { return NewTransformedBounds(); } return nsIntRect(); } gfxRGBA mColor; }; struct ImageLayerProperties : public LayerPropertiesBase { ImageLayerProperties(ImageLayer* aImage) : LayerPropertiesBase(aImage) , mVisibleRegion(aImage->GetVisibleRegion()) , mContainer(aImage->GetContainer()) , mFilter(aImage->GetFilter()) , mScaleToSize(aImage->GetScaleToSize()) , mScaleMode(aImage->GetScaleMode()) { } virtual nsIntRect ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback) { ImageLayer* imageLayer = static_cast(mLayer.get()); if (!imageLayer->GetVisibleRegion().IsEqual(mVisibleRegion)) { nsIntRect result = NewTransformedBounds(); result = result.Union(OldTransformedBounds()); return result; } if (mContainer != imageLayer->GetContainer() || mFilter != imageLayer->GetFilter() || mScaleToSize != imageLayer->GetScaleToSize() || mScaleMode != imageLayer->GetScaleMode()) { return NewTransformedBounds(); } return nsIntRect(); } nsIntRegion mVisibleRegion; nsRefPtr mContainer; gfxPattern::GraphicsFilter mFilter; gfxIntSize mScaleToSize; ImageLayer::ScaleMode mScaleMode; }; LayerPropertiesBase* CloneLayerTreePropertiesInternal(Layer* aRoot) { if (!aRoot) { return new LayerPropertiesBase(); } switch (aRoot->GetType()) { case Layer::TYPE_CONTAINER: return new ContainerLayerProperties(aRoot->AsContainerLayer()); case Layer::TYPE_COLOR: return new ColorLayerProperties(static_cast(aRoot)); case Layer::TYPE_IMAGE: return new ImageLayerProperties(static_cast(aRoot)); default: return new LayerPropertiesBase(aRoot); } return nullptr; } /* static */ LayerProperties* LayerProperties::CloneFrom(Layer* aRoot) { return CloneLayerTreePropertiesInternal(aRoot); } /* static */ void LayerProperties::ClearInvalidations(Layer *aLayer) { aLayer->ClearInvalidRect(); if (aLayer->GetMaskLayer()) { ClearInvalidations(aLayer->GetMaskLayer()); } ContainerLayer* container = aLayer->AsContainerLayer(); if (!container) { return; } for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { ClearInvalidations(child); } } nsIntRect LayerPropertiesBase::ComputeDifferences(Layer* aRoot, NotifySubDocInvalidationFunc aCallback) { NS_ASSERTION(aRoot, "Must have a layer tree to compare against!"); if (mLayer != aRoot) { if (aCallback) { NotifySubdocumentInvalidationRecursive(aRoot, aCallback); } else { ClearInvalidations(aRoot); } nsIntRect result = TransformRect(aRoot->GetVisibleRegion().GetBounds(), aRoot->GetTransform()); result = result.Union(OldTransformedBounds()); return result; } else { return ComputeChange(aCallback); } } void LayerPropertiesBase::MoveBy(const nsIntPoint& aOffset) { mTransform.TranslatePost(gfxPoint3D(aOffset.x, aOffset.y, 0)); } } // namespace layers } // namespace mozilla