diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 0ced0a227bae..2a9e9d7de2bd 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -2114,7 +2114,8 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList, continue; } - if (item->IsInvalid()) { + nsRect invalid; + if (item->IsInvalid(invalid)) { ownLayer->SetInvalidRectToVisibleRegion(); } @@ -2252,8 +2253,10 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, ThebesDisplayItemLayerUserData* data = static_cast(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData)); - // If the frame is marked as invalidated then we want to invalidate both the old and new bounds, - // otherwise we only want to invalidate the changed areas. + // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to + // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas. + // If we do get an invalid rect, then we want to add this on top of the change areas. + nsRect invalid; nsRegion combined; if (!oldLayer) { // This item is being added for the first time, invalidate its entire area. @@ -2262,7 +2265,7 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, #ifdef DEBUG_INVALIDATIONS printf("Display item type %s(%p) added to layer %p!\n", aItem->Name(), f, aNewLayer); #endif - } else if (aItem->IsInvalid()) { + } else if (aItem->IsInvalid(invalid) && invalid.IsEmpty()) { // Either layout marked item as needing repainting, invalidate the entire old and new areas. combined.Or(geometry->ComputeInvalidationRegion(), oldGeometry->ComputeInvalidationRegion()); #ifdef DEBUG_INVALIDATIONS @@ -2277,6 +2280,9 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, oldClip->AddOffsetAndComputeDifference(shift, oldGeometry->ComputeInvalidationRegion(), aClip, geometry->ComputeInvalidationRegion(), &combined); + + // Add in any rect that the frame specified + combined = combined.Or(combined, invalid); #ifdef DEBUG_INVALIDATIONS if (!combined.IsEmpty()) { printf("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), f, aNewLayer); diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 5d52010c07da..2b65a211417d 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -774,7 +774,11 @@ public: * Checks if the frame(s) owning this display item have been marked as invalid, * and needing repainting. */ - virtual bool IsInvalid() { return mFrame ? mFrame->IsInvalid() : false; } + virtual bool IsInvalid(nsRect& aRect) { + bool result = mFrame ? mFrame->IsInvalid(aRect) : false; + aRect += ToReferenceFrame(); + return result; + } /** * Creates and initializes an nsDisplayItemGeometry object that retains the current @@ -2005,17 +2009,21 @@ public: { aFrames->AppendElements(mMergedFrames); } - virtual bool IsInvalid() + virtual bool IsInvalid(nsRect& aRect) { - if (mFrame->IsInvalid()) { + if (mFrame->IsInvalid(aRect) && aRect.IsEmpty()) { return true; } + nsRect temp; for (uint32_t i = 0; i < mMergedFrames.Length(); i++) { - if (mMergedFrames[i]->IsInvalid()) { + if (mMergedFrames[i]->IsInvalid(temp) && temp.IsEmpty()) { + aRect.SetEmpty(); return true; } + aRect = aRect.Union(temp); } - return false; + aRect += ToReferenceFrame(); + return !aRect.IsEmpty(); } NS_DISPLAY_DECL_NAME("WrapList", TYPE_WRAP_LIST) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 08f6081326f7..988b4caf14c5 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4806,12 +4806,50 @@ nsIFrame::InvalidateFrame() if (!parent) { SchedulePaint(); } + if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) { + Properties().Delete(InvalidationRect()); + RemoveStateBits(NS_FRAME_HAS_INVALID_RECT); + } +} + +void +nsIFrame::InvalidateFrameWithRect(const nsRect& aRect) +{ + bool alreadyInvalid = false; + if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) { + InvalidateFrame(); + } else { + alreadyInvalid = true; + } + + nsRect *rect = static_cast(Properties().Get(InvalidationRect())); + if (!rect) { + if (alreadyInvalid) { + return; + } + rect = new nsRect(); + Properties().Set(InvalidationRect(), rect); + AddStateBits(NS_FRAME_HAS_INVALID_RECT); + } + + *rect = rect->Union(aRect); } bool -nsIFrame::IsInvalid() +nsIFrame::IsInvalid(nsRect& aRect) { - return HasAnyStateBits(NS_FRAME_NEEDS_PAINT); + if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) { + return false; + } + + if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) { + nsRect *rect = static_cast(Properties().Get(InvalidationRect())); + NS_ASSERTION(rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!"); + aRect = *rect; + } else { + aRect.SetEmpty(); + } + return true; } void diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 8636eb93d1ff..afeef0a60eea 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -319,6 +319,10 @@ typedef uint64_t nsFrameState; // have no display items. #define NS_FRAME_ALL_DESCENDANTS_NEED_PAINT NS_FRAME_STATE_BIT(51) +// Frame is marked as NS_FRAME_NEEDS_PAINT and also has an explicit +// rect stored to invalidate. +#define NS_FRAME_HAS_INVALID_RECT NS_FRAME_STATE_BIT(52) + // Box layout bits #define NS_STATE_IS_HORIZONTAL NS_FRAME_STATE_BIT(22) #define NS_STATE_IS_DIRECTION_NORMAL NS_FRAME_STATE_BIT(31) @@ -955,6 +959,8 @@ public: NS_DECLARE_FRAME_PROPERTY(CachedBackgroundImage, DestroySurface) + NS_DECLARE_FRAME_PROPERTY(InvalidationRect, DestroyRect) + /** * Return the distance between the border edge of the frame and the * margin edge of the frame. Like GetRect(), returns the dimensions @@ -2179,6 +2185,15 @@ public: * container types. */ virtual void InvalidateFrame(); + + /** + * Same as InvalidateFrame(), but only mark a fixed rect as needing + * repainting. + * + * @param aRect The rect to invalidate, relative to the TopLeft of the + * frame's border box. + */ + virtual void InvalidateFrameWithRect(const nsRect& aRect); /** * Calls InvalidateFrame() on all frames descendant frames (including @@ -2192,8 +2207,13 @@ public: /** * Checks if a frame has had InvalidateFrame() called on it since the * last paint. + * + * If true, then the invalid rect is returned in aRect, with an + * empty rect meaning all pixels drawn by this frame should be + * invalidated. + * If false, aRect is left unchanged. */ - bool IsInvalid(); + bool IsInvalid(nsRect& aRect); /** * Check if any frame within the frame subtree (including this frame) diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 288b209e7df4..2f17f12b7c2b 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -587,12 +587,16 @@ nsImageFrame::OnDataAvailable(imgIRequest *aRequest, // from if (!aCurrentFrame) return NS_OK; - + #ifdef DEBUG_decode printf("Source rect (%d,%d,%d,%d)\n", aRect->x, aRect->y, aRect->width, aRect->height); #endif - InvalidateFrame(); + if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) { + InvalidateFrame(); + } else { + InvalidateFrameWithRect(SourceRectToDest(*aRect)); + } return NS_OK; } @@ -671,8 +675,11 @@ nsImageFrame::FrameChanged(imgIRequest *aRequest, return NS_OK; } - // Update border+content to account for image change - InvalidateFrame(); + if (aDirtyRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) { + InvalidateFrame(); + } else { + InvalidateFrameWithRect(SourceRectToDest(*aDirtyRect)); + } return NS_OK; } diff --git a/layout/svg/nsSVGOuterSVGFrame.h b/layout/svg/nsSVGOuterSVGFrame.h index 49a2d0e0b4c8..5f61a34b343b 100644 --- a/layout/svg/nsSVGOuterSVGFrame.h +++ b/layout/svg/nsSVGOuterSVGFrame.h @@ -155,7 +155,8 @@ public: void ClearInvalidRegion() { mInvalidRegion.SetEmpty(); } const nsRegion& GetInvalidRegion() { - if (!IsInvalid()) { + nsRect rect; + if (!IsInvalid(rect)) { mInvalidRegion.SetEmpty(); } return mInvalidRegion;