/*-*- 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<stdint.h> // for uint32_t#include"ImageContainer.h" // for ImageContainer#include"ImageLayers.h" // for ImageLayer, etc#include"Layers.h" // for Layer, ContainerLayer, etc#include"Units.h" // for ParentLayerIntRect#include"gfxColor.h" // for gfxRGBA#include"GraphicsFilter.h" // for GraphicsFilter#include"gfxRect.h" // for gfxRect#include"gfxUtils.h" // for gfxUtils#include"mozilla/gfx/BaseSize.h" // for BaseSize#include"mozilla/gfx/Point.h" // for IntSize#include"mozilla/mozalloc.h" // for operator new, etc#include"nsAutoPtr.h" // for nsRefPtr, nsAutoPtr, etc#include"nsDataHashtable.h" // for nsDataHashtable#include"nsDebug.h" // for NS_ASSERTION#include"nsHashKeys.h" // for nsPtrHashKey#include"nsISupportsImpl.h" // for Layer::AddRef, etc#include"nsRect.h" // for IntRect#include"nsTArray.h" // for nsAutoTArray, nsTArray_Implusingnamespacemozilla::gfx;namespacemozilla{namespacelayers{structLayerPropertiesBase;UniquePtr<LayerPropertiesBase>CloneLayerTreePropertiesInternal(Layer*aRoot,boolaIsMask=false);staticIntRectTransformRect(constIntRect&aRect,constMatrix4x4&aTransform){if(aRect.IsEmpty()){returnIntRect();}Rectrect(aRect.x,aRect.y,aRect.width,aRect.height);rect=aTransform.TransformBounds(rect);rect.RoundOut();IntRectintRect;if(!gfxUtils::GfxRectToIntRect(ThebesRect(rect),&intRect)){returnIntRect();}returnintRect;}staticvoidAddTransformedRegion(nsIntRegion&aDest,constnsIntRegion&aSource,constMatrix4x4&aTransform){nsIntRegionRectIteratoriter(aSource);constIntRect*r;while((r=iter.Next())){aDest.Or(aDest,TransformRect(*r,aTransform));}aDest.SimplifyOutward(20);}staticvoidAddRegion(nsIntRegion&aDest,constnsIntRegion&aSource){aDest.Or(aDest,aSource);aDest.SimplifyOutward(20);}/** * 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. */staticvoidNotifySubdocumentInvalidationRecursive(Layer*aLayer,NotifySubDocInvalidationFuncaCallback){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());}structLayerPropertiesBase:publicLayerProperties{explicitLayerPropertiesBase(Layer*aLayer):mLayer(aLayer),mMaskLayer(nullptr),mVisibleRegion(aLayer->GetVisibleRegion()),mInvalidRegion(aLayer->GetInvalidRegion()),mPostXScale(aLayer->GetPostXScale()),mPostYScale(aLayer->GetPostYScale()),mOpacity(aLayer->GetLocalOpacity()),mUseClipRect(!!aLayer->GetClipRect()){MOZ_COUNT_CTOR(LayerPropertiesBase);if(aLayer->GetMaskLayer()){mMaskLayer=CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(),true);}if(mUseClipRect){mClipRect=*aLayer->GetClipRect();}mTransform=aLayer->GetLocalTransform();}LayerPropertiesBase():mLayer(nullptr),mMaskLayer(nullptr){MOZ_COUNT_CTOR(LayerPropertiesBase);}~LayerPropertiesBase(){MOZ_COUNT_DTOR(LayerPropertiesBase);}virtualnsIntRegionComputeDifferences(Layer*aRoot,NotifySubDocInvalidationFuncaCallback,bool*aGeometryChanged);virtualvoidMoveBy(constIntPoint&aOffset);nsIntRegionComputeChange(NotifySubDocInvalidationFuncaCallback,bool&aGeometryChanged){booltransformChanged=!mTransform.FuzzyEqual(mLayer->GetLocalTransform())||mLayer->GetPostXScale()!=mPostXScale||mLayer->GetPostYScale()!=mPostYScale;Layer*otherMask=mLayer->GetMaskLayer();constMaybe<ParentLayerIntRect>&otherClip=mLayer->GetClipRect();nsIntRegionresult;if((mMaskLayer?mMaskLayer->mLayer:nullptr)!=otherMask||(mUseClipRect!=!!otherClip)||mLayer->GetLocalOpacity()!=mOpacity||transformChanged){aGeometryChanged=true;result=OldTransformedBounds();AddRegion(result,NewTransformedBounds());// We can't bail out early because we need to update mChildrenChanged.}AddRegion(result,ComputeChangeInternal(aCallback,aGeometryChanged));AddTransformedRegion(result,mLayer->GetInvalidRegion(),mTransform);if(mMaskLayer&&otherMask){AddTransformedRegion(result,mMaskLayer->ComputeChange(aCallback,aGeometryChanged),mTransform);}if(mUseClipRect&&otherClip){if(!mClipRect.IsEqualInterior(*otherClip)){aGeometryChanged=true;nsIntRegiontmp;tmp.Xor(ParentLayerIntRect::ToUntyped(mClipRect),ParentLayerIntRect::ToUntyped(*otherClip));AddRegion(result,tmp);}}mLayer->ClearInvalidRect();returnresult;}IntRectNewTransformedBounds(){returnTransformRect(mLayer->GetVisibleRegion().GetBounds(),mLayer->GetLocalTransform());}IntRectOldTransformedBounds(){returnTransformRect(mVisibleRegion.GetBounds(),mTransform);}virtualnsIntRegionComputeChangeInternal(NotifySubDocInvalidationFuncaCallback,bool&aGeometryChanged){returnIntRect();}nsRefPtr<Layer>mLayer;UniquePtr<LayerPropertiesBase>mMaskLayer;nsIntRegionmVisibleRegion;nsIntRegionmInvalidRegion;Matrix4x4mTransform;floatmPostXScale;floatmPostYScale;floatmOpacity;ParentLayerIntRectmClipRect;boolmUseClipRect;};structContainerLayerProperties:publicLayerPropertiesBase{explicitContainerLayerProperties(ContainerLayer*aLayer):LayerPropertiesBase(aLayer),mPreXScale(aLayer->GetPreXScale()),mPreYScale(aLayer->GetPreYScale()){for(Layer*child=aLayer->GetFirstChild();child;child=child->GetNextSibling()){mChildren.AppendElement(Move(CloneLayerTreePropertiesInternal(child)));}}virtualnsIntRegionComputeChangeInternal(NotifySubDocInvalidationFuncaCallback,bool&aGeometryChanged){ContainerLayer*container=mLayer->AsContainerLayer();nsIntRegionresult;boolchildrenChanged=false;if(mPreXScale!=container->GetPreXScale()||mPreYScale!=container->GetPreYScale()){aGeometryChanged=true;result=OldTransformedBounds();AddRegion(result,NewTransformedBounds());childrenChanged=true;// Can't bail out early, we need to update the child container layers}// A low frame rate is especially visible to users when scrolling, so we// particularly want to avoid unnecessary invalidation at that time. For us// here, that means avoiding unnecessary invalidation of child items when// other children are added to or removed from our container layer, since// that may be caused by children being scrolled in or out of view. We are// less concerned with children changing order.// TODO: Consider how we could avoid unnecessary invalidation when children// change order, and whether the overhead would be worth it.nsDataHashtable<nsPtrHashKey<Layer>,uint32_t>oldIndexMap(mChildren.Length());for(uint32_ti=0;i<mChildren.Length();++i){oldIndexMap.Put(mChildren[i]->mLayer,i);}uint32_ti=0;// cursor into the old child list mChildrenfor(Layer*child=container->GetFirstChild();child;child=child->GetNextSibling()){boolinvalidateChildsCurrentArea=false;if(i<mChildren.Length()){uint32_tchildsOldIndex;if(oldIndexMap.Get(child,&childsOldIndex)){if(childsOldIndex>=i){// Invalidate the old areas of layers that used to be between the// current |child| and the previous |child| that was also in the// old list mChildren (if any of those children have been reordered// rather than removed, we will invalidate their new area when we// encounter them in the new list):for(uint32_tj=i;j<childsOldIndex;++j){AddRegion(result,mChildren[j]->OldTransformedBounds());childrenChanged|=true;}// Invalidate any regions of the child that have changed:nsIntRegionregion=mChildren[childsOldIndex]->ComputeChange(aCallback,aGeometryChanged);i=childsOldIndex+1;if(!region.IsEmpty()){AddRegion(result,region);childrenChanged|=true;}}else{// We've already seen this child in mChildren (which means it must// have been reordered) and invalidated its old area. We need to// invalidate its new area too:invalidateChildsCurrentArea=true;}}else{// |child| is newinvalidateChildsCurrentArea=true;}}else{// |child| is new, or was reordered to a higher indexinvalidateChildsCurrentArea=true;}if(invalidateChildsCurrentArea){aGeometryChanged=true;AddTransformedRegion(result,child->GetVisibleRegion(),child->GetLocalTransform());if(aCallback){NotifySubdocumentInvalidationRecursive(child,aCallback);}else{ClearInvalidations(child);}}childrenChanged|=invalidateChildsCurrentArea;}// Process remaining removed children.while(i<mChildren.Length()){childrenChanged|=true;AddRegion(result,mChildren[i]->OldTransformedBounds());i++;}if(aCallback){aCallback(container,result);}if(childrenChanged){container->SetChildrenChanged(true);}result.Transform(gfx::To3DMatrix(mLayer->GetLocalTransform()));returnresult;}// The old list of children:nsAutoTArray<UniquePtr<LayerPropertiesBase>,1>mChildren;floatmPreXScale;floatmPreYScale;};structColorLayerProperties:publicLayerPropertiesBase{explicitColorLayerProperties(ColorLayer*aLayer):LayerPropertiesBase(aLayer),mColor(aLayer->GetColor()),mBounds(aLayer->GetBounds()){}virtualnsIntRegionComputeChangeInternal(NotifySubDocInvalidationFuncaCallback,bool&aGeometryChanged){ColorLayer*color=static_cast<ColorLayer*>(mLayer.get());if(mColor!=color->GetColor()){aGeometryChanged=true;returnNewTransformedBounds();}nsIntRegionboundsDiff;boundsDiff.Xor(mBounds,color->GetBounds());nsIntRegionresult;AddTransformedRegion(result,boundsDiff,mTransform);returnresult;}gfxRGBAmColor;IntRectmBounds;};structImageLayerProperties:publicLayerPropertiesBase{explicitImageLayerProperties(ImageLayer*aImage,boolaIsMask):LayerPropertiesBase(aImage),mContainer(aImage->GetContainer()),mFilter(aImage->GetFilter()),mScaleToSize(aImage->GetScaleToSize()),mScaleMode(aImage->GetScaleMode()),mIsMask(aIsMask){}virtualnsIntRegionComputeChangeInternal(NotifySubDocInvalidationFuncaCallback,bool&aGeometryChanged){ImageLayer*imageLayer=static_cast<ImageLayer*>(mLayer.get());if(!imageLayer->GetVisibleRegion().IsEqual(mVisibleRegion)){aGeometryChanged=true;IntRectresult=NewTransformedBounds();result=result.Union(OldTransformedBounds());returnresult;}ImageContainer*container=imageLayer->GetContainer();if(mContainer!=container||mFilter!=imageLayer->GetFilter()||mScaleToSize!=imageLayer->GetScaleToSize()||mScaleMode!=imageLayer->GetScaleMode()){aGeometryChanged=true;if(mIsMask){// Mask layers have an empty visible region, so we have to// use the image size instead.IntSizesize=container->GetCurrentSize();IntRectrect(0,0,size.width,size.height);returnTransformRect(rect,mLayer->GetLocalTransform());}else{returnNewTransformedBounds();}}returnIntRect();}nsRefPtr<ImageContainer>mContainer;GraphicsFiltermFilter;gfx::IntSizemScaleToSize;ScaleModemScaleMode;boolmIsMask;};UniquePtr<LayerPropertiesBase>CloneLayerTreePropertiesInternal(Layer*aRoot,boolaIsMask/* = false */){if(!aRoot){returnMakeUnique<LayerPropertiesBase>();}MOZ_ASSERT(!aIsMask||aRoot->GetType()==Layer::TYPE_IMAGE);switch(aRoot->GetType()){caseLayer::TYPE_CONTAINER:caseLayer::TYPE_REF:returnMakeUnique<ContainerLayerProperties>(aRoot->AsContainerLayer());caseLayer::TYPE_COLOR:returnMakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));caseLayer::TYPE_IMAGE:returnMakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot),aIsMask);default:returnMakeUnique<LayerPropertiesBase>(aRoot);}returnUniquePtr<LayerPropertiesBase>(nullptr);}/* static */UniquePtr<LayerProperties>LayerProperties::CloneFrom(Layer*aRoot){returnCloneLayerTreePropertiesInternal(aRoot);}/* static */voidLayerProperties::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);}}nsIntRegionLayerPropertiesBase::ComputeDifferences(Layer*aRoot,NotifySubDocInvalidationFuncaCallback,bool*aGeometryChanged=nullptr){NS_ASSERTION(aRoot,"Must have a layer tree to compare against!");if(mLayer!=aRoot){if(aCallback){NotifySubdocumentInvalidationRecursive(aRoot,aCallback);}else{ClearInvalidations(aRoot);}IntRectresult=TransformRect(aRoot->GetVisibleRegion().GetBounds(),aRoot->GetLocalTransform());result=result.Union(OldTransformedBounds());if(aGeometryChanged!=nullptr){*aGeometryChanged=true;}returnresult;}else{boolgeometryChanged=(aGeometryChanged!=nullptr)?*aGeometryChanged:false;nsIntRegioninvalid=ComputeChange(aCallback,geometryChanged);if(aGeometryChanged!=nullptr){*aGeometryChanged=geometryChanged;}returninvalid;}}voidLayerPropertiesBase::MoveBy(constIntPoint&aOffset){mTransform.PostTranslate(aOffset.x,aOffset.y,0);}}// namespace layers}// namespace mozilla