Bug 841192. Part 2: Move FrameLayerBuilder::Clip to DisplayItemClip. r=mattwoodrow

--HG--
extra : rebase_source : 2950e5dd64e217a406ede2dc9f6682b5e0540975
This commit is contained in:
Robert O'Callahan 2013-03-04 22:55:59 +13:00
parent 6709e4eafc
commit 8d47c65ff4
6 changed files with 433 additions and 390 deletions

View File

@ -0,0 +1,272 @@
/* -*- Mode: C++; tab-width: 20; 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 "DisplayItemClip.h"
#include "gfxContext.h"
#include "nsPresContext.h"
#include "nsDisplayList.h"
#include "nsCSSRendering.h"
#include "nsLayoutUtils.h"
namespace mozilla {
DisplayItemClip::DisplayItemClip(const DisplayItemClip& aOther, nsDisplayItem* aClipItem)
: mRoundedClipRects(aOther.mRoundedClipRects),
mHaveClipRect(true)
{
nsDisplayItem::Type type = aClipItem->GetType();
NS_ABORT_IF_FALSE(type == nsDisplayItem::TYPE_CLIP ||
type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT,
"unexpected display item type");
nsDisplayClip* item = static_cast<nsDisplayClip*>(aClipItem);
// Always intersect with mClipRect, even if we're going to add a
// rounded rect.
if (aOther.mHaveClipRect) {
mClipRect.IntersectRect(aOther.mClipRect, item->GetClipRect());
} else {
mClipRect = item->GetClipRect();
}
if (type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
RoundedRect *rr = mRoundedClipRects.AppendElement();
if (rr) {
rr->mRect = item->GetClipRect();
static_cast<nsDisplayClipRoundedRect*>(item)->GetRadii(rr->mRadii);
}
}
// FIXME: Optimize away excess rounded rectangles due to the new addition.
}
void
DisplayItemClip::ApplyTo(gfxContext* aContext,
nsPresContext* aPresContext,
uint32_t aBegin, uint32_t aEnd)
{
int32_t A2D = aPresContext->AppUnitsPerDevPixel();
ApplyRectTo(aContext, A2D);
ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd);
}
void
DisplayItemClip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const
{
aContext->NewPath();
gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D);
aContext->Rectangle(clip, true);
aContext->Clip();
}
void
DisplayItemClip::ApplyRoundedRectsTo(gfxContext* aContext,
int32_t A2D,
uint32_t aBegin, uint32_t aEnd) const
{
aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
for (uint32_t i = aBegin; i < aEnd; ++i) {
AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[i]);
aContext->Clip();
}
}
void
DisplayItemClip::DrawRoundedRectsTo(gfxContext* aContext,
int32_t A2D,
uint32_t aBegin, uint32_t aEnd) const
{
aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
if (aEnd - aBegin == 0)
return;
// If there is just one rounded rect we can just fill it, if there are more then we
// must clip the rest to get the intersection of clips
ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd - 1);
AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[aEnd - 1]);
aContext->Fill();
}
void
DisplayItemClip::AddRoundedRectPathTo(gfxContext* aContext,
int32_t A2D,
const RoundedRect &aRoundRect) const
{
gfxCornerSizes pixelRadii;
nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii);
gfxRect clip = nsLayoutUtils::RectToGfxRect(aRoundRect.mRect, A2D);
clip.Round();
clip.Condition();
aContext->NewPath();
aContext->RoundedRectangle(clip, pixelRadii);
}
nsRect
DisplayItemClip::ApproximateIntersect(const nsRect& aRect) const
{
nsRect r = aRect;
if (mHaveClipRect) {
r.IntersectRect(r, mClipRect);
}
for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
i < iEnd; ++i) {
const RoundedRect &rr = mRoundedClipRects[i];
nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r);
r = rgn.GetLargestRectangle();
}
return r;
}
// Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter)
// and radii aXRadius, aYRadius.
bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint,
nscoord aYRadius, nscoord aYCenter, nscoord aYPoint)
{
float scaledX = float(aXPoint - aXCenter) / float(aXRadius);
float scaledY = float(aYPoint - aYCenter) / float(aYRadius);
return scaledX * scaledX + scaledY * scaledY < 1.0f;
}
bool
DisplayItemClip::IsRectClippedByRoundedCorner(const nsRect& aRect) const
{
if (mRoundedClipRects.IsEmpty())
return false;
nsRect rect;
rect.IntersectRect(aRect, NonRoundedIntersection());
for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
i < iEnd; ++i) {
const RoundedRect &rr = mRoundedClipRects[i];
// top left
if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X] &&
rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y]) {
if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_LEFT_X],
rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X],
rect.x,
rr.mRadii[NS_CORNER_TOP_LEFT_Y],
rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y],
rect.y)) {
return true;
}
}
// top right
if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X] &&
rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y]) {
if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_RIGHT_X],
rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X],
rect.XMost(),
rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
rect.y)) {
return true;
}
}
// bottom left
if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X] &&
rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y]) {
if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
rect.x,
rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
rect.YMost())) {
return true;
}
}
// bottom right
if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X] &&
rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y]) {
if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
rect.XMost(),
rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
rect.YMost())) {
return true;
}
}
}
return false;
}
nsRect
DisplayItemClip::NonRoundedIntersection() const
{
NS_ASSERTION(mHaveClipRect, "Must have a clip rect!");
nsRect result = mClipRect;
for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
i < iEnd; ++i) {
result.IntersectRect(result, mRoundedClipRects[i].mRect);
}
return result;
}
nsRect
DisplayItemClip::ApplyNonRoundedIntersection(const nsRect& aRect) const
{
if (!mHaveClipRect) {
return aRect;
}
nsRect result = aRect.Intersect(mClipRect);
for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
i < iEnd; ++i) {
result.Intersect(mRoundedClipRects[i].mRect);
}
return result;
}
void
DisplayItemClip::RemoveRoundedCorners()
{
if (mRoundedClipRects.IsEmpty())
return;
mClipRect = NonRoundedIntersection();
mRoundedClipRects.Clear();
}
static void
AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, nsRegion* aOut)
{
if (aR1.IsEqualInterior(aR2))
return;
nsRegion r;
r.Xor(aR1, aR2);
aOut->Or(*aOut, r);
}
void
DisplayItemClip::AddOffsetAndComputeDifference(const nsPoint& aOffset,
const nsRect& aBounds,
const DisplayItemClip& aOther,
const nsRect& aOtherBounds,
nsRegion* aDifference)
{
if (mHaveClipRect != aOther.mHaveClipRect ||
mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) {
aDifference->Or(*aDifference, aBounds);
aDifference->Or(*aDifference, aOtherBounds);
return;
}
if (mHaveClipRect) {
AccumulateRectDifference((mClipRect + aOffset).Intersect(aBounds),
aOther.mClipRect.Intersect(aOtherBounds),
aDifference);
}
for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) {
// The corners make it tricky so we'll just add both rects here.
aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds));
aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds));
}
}
}
}

View File

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 20; 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/. */
#ifndef DISPLAYITEMCLIP_H_
#define DISPLAYITEMCLIP_H_
#include "nsRect.h"
#include "nsTArray.h"
#include "nsStyleConsts.h"
class gfxContext;
class nsDisplayItem;
class nsPresContext;
class nsRegion;
namespace mozilla {
/**
* An DisplayItemClip represents the intersection of an optional rectangle
* with a list of rounded rectangles (which is often empty), all in appunits.
* It can represent everything CSS clipping can do to an element (except for
* SVG clip-path), including no clipping at all.
*/
struct DisplayItemClip {
struct RoundedRect {
nsRect mRect;
// Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
nscoord mRadii[8];
RoundedRect operator+(const nsPoint& aOffset) const {
RoundedRect r = *this;
r.mRect += aOffset;
return r;
}
bool operator==(const RoundedRect& aOther) const {
if (!mRect.IsEqualInterior(aOther.mRect)) {
return false;
}
NS_FOR_CSS_HALF_CORNERS(corner) {
if (mRadii[corner] != aOther.mRadii[corner]) {
return false;
}
}
return true;
}
bool operator!=(const RoundedRect& aOther) const {
return !(*this == aOther);
}
};
nsRect mClipRect;
nsTArray<RoundedRect> mRoundedClipRects;
bool mHaveClipRect;
// Constructs a DisplayItemClip that does no clipping at all.
DisplayItemClip() : mHaveClipRect(false) {}
// Construct as the intersection of aOther and aClipItem.
DisplayItemClip(const DisplayItemClip& aOther, nsDisplayItem* aClipItem);
// Apply this |DisplayItemClip| to the given gfxContext. Any saving of state
// or clearing of other clips must be done by the caller.
// See aBegin/aEnd note on ApplyRoundedRectsTo.
void ApplyTo(gfxContext* aContext, nsPresContext* aPresContext,
uint32_t aBegin = 0, uint32_t aEnd = UINT32_MAX);
void ApplyRectTo(gfxContext* aContext, int32_t A2D) const;
// Applies the rounded rects in this Clip to aContext
// Will only apply rounded rects from aBegin (inclusive) to aEnd
// (exclusive) or the number of rounded rects, whichever is smaller.
void ApplyRoundedRectsTo(gfxContext* aContext, int32_t A2DPRInt32,
uint32_t aBegin, uint32_t aEnd) const;
// Draw (fill) the rounded rects in this clip to aContext
void DrawRoundedRectsTo(gfxContext* aContext, int32_t A2D,
uint32_t aBegin, uint32_t aEnd) const;
// 'Draw' (create as a path, does not stroke or fill) aRoundRect to aContext
void AddRoundedRectPathTo(gfxContext* aContext, int32_t A2D,
const RoundedRect &aRoundRect) const;
// Return a rectangle contained in the intersection of aRect with this
// clip region. Tries to return the largest possible rectangle, but may
// not succeed.
nsRect ApproximateIntersect(const nsRect& aRect) const;
// Returns false if aRect is definitely not clipped by a rounded corner in
// this clip. Returns true if aRect is clipped by a rounded corner in this
// clip or it can not be quickly determined that it is not clipped by a
// rounded corner in this clip.
bool IsRectClippedByRoundedCorner(const nsRect& aRect) const;
// Intersection of all rects in this clip ignoring any rounded corners.
nsRect NonRoundedIntersection() const;
// Intersect the given rects with all rects in this clip, ignoring any
// rounded corners.
nsRect ApplyNonRoundedIntersection(const nsRect& aRect) const;
// Gets rid of any rounded corners in this clip.
void RemoveRoundedCorners();
// Adds the difference between Intersect(*this + aPoint, aBounds) and
// Intersect(aOther, aOtherBounds) to aDifference (or a bounding-box thereof).
void AddOffsetAndComputeDifference(const nsPoint& aPoint, const nsRect& aBounds,
const DisplayItemClip& aOther, const nsRect& aOtherBounds,
nsRegion* aDifference);
bool operator==(const DisplayItemClip& aOther) const {
return mHaveClipRect == aOther.mHaveClipRect &&
(!mHaveClipRect || mClipRect.IsEqualInterior(aOther.mClipRect)) &&
mRoundedClipRects == aOther.mRoundedClipRects;
}
bool operator!=(const DisplayItemClip& aOther) const {
return !(*this == aOther);
}
};
}
#endif /* DISPLAYITEMCLIP_H_ */

View File

@ -275,7 +275,7 @@ public:
* if no clipping is required
*/
void ProcessDisplayItems(const nsDisplayList& aList,
FrameLayerBuilder::Clip& aClip,
DisplayItemClip& aClip,
uint32_t aFlags,
const nsIFrame* aForceActiveScrolledRoot = nullptr);
/**
@ -364,7 +364,7 @@ protected:
nsDisplayItem* aItem,
const nsIntRect& aVisibleRect,
const nsIntRect& aDrawRect,
const FrameLayerBuilder::Clip& aClip);
const DisplayItemClip& aClip);
const nsIFrame* GetActiveScrolledRoot() { return mActiveScrolledRoot; }
/**
@ -451,7 +451,7 @@ protected:
* by ThebesDisplayItemLayerUserData::GetCommonClipCount() - which may even be
* no part at all.
*/
FrameLayerBuilder::Clip mItemClip;
DisplayItemClip mItemClip;
/**
* The first mCommonClipCount rounded rectangle clips are identical for
* all items in the layer.
@ -465,7 +465,7 @@ protected:
* on items already in the layer (the first mCommonClipCount rounded rects
* in mItemClip).
*/
void UpdateCommonClipCount(const FrameLayerBuilder::Clip& aCurrentClip);
void UpdateCommonClipCount(const DisplayItemClip& aCurrentClip);
};
friend class ThebesLayerData;
@ -506,7 +506,7 @@ protected:
*/
void InvalidateForLayerChange(nsDisplayItem* aItem,
Layer* aNewLayer,
const FrameLayerBuilder::Clip& aClip,
const DisplayItemClip& aClip,
const nsPoint& aTopLeft,
nsDisplayItemGeometry *aGeometry);
/**
@ -542,7 +542,7 @@ protected:
ThebesLayerData* FindThebesLayerFor(nsDisplayItem* aItem,
const nsIntRect& aVisibleRect,
const nsIntRect& aDrawRect,
const FrameLayerBuilder::Clip& aClip,
const DisplayItemClip& aClip,
const nsIFrame* aActiveScrolledRoot,
const nsPoint& aTopLeft);
ThebesLayerData* GetTopThebesLayerData()
@ -560,7 +560,7 @@ protected:
* SetupMaskLayer will build a mask layer for only the first
* aRoundedRectClipCount rounded rects in aClip
*/
void SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
void SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
uint32_t aRoundedRectClipCount = UINT32_MAX);
bool ChooseActiveScrolledRoot(const nsDisplayList& aList,
@ -680,7 +680,7 @@ struct MaskLayerUserData : public LayerUserData
nsRefPtr<const MaskLayerImageCache::MaskLayerImageKey> mImageKey;
// properties of the mask layer; the mask layer may be re-used if these
// remain unchanged.
nsTArray<FrameLayerBuilder::Clip::RoundedRect> mRoundedClipRects;
nsTArray<DisplayItemClip::RoundedRect> mRoundedClipRects;
// scale from the masked layer which is applied to the mask
float mScaleX, mScaleY;
// The ContainerParameters offset which is applied to the mask's transform.
@ -837,7 +837,7 @@ InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsIntRegion& aRegion,
static void
InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsRect& aRect,
const FrameLayerBuilder::Clip& aClip,
const DisplayItemClip& aClip,
const nsIntPoint& aTranslation)
{
ThebesDisplayItemLayerUserData* data =
@ -1127,7 +1127,7 @@ FrameLayerBuilder::GetOldLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKe
Layer*
FrameLayerBuilder::GetOldLayerFor(nsDisplayItem* aItem,
nsDisplayItemGeometry** aOldGeometry,
Clip** aOldClip,
DisplayItemClip** aOldClip,
nsTArray<nsIFrame*>* aChangedFrames,
bool *aIsInvalid)
{
@ -1520,7 +1520,7 @@ ContainerState::FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex)
void
ContainerState::ThebesLayerData::UpdateCommonClipCount(
const FrameLayerBuilder::Clip& aCurrentClip)
const DisplayItemClip& aCurrentClip)
{
if (mCommonClipCount >= 0) {
int32_t end = std::min<int32_t>(aCurrentClip.mRoundedClipRects.Length(),
@ -1740,7 +1740,7 @@ ContainerState::ThebesLayerData::Accumulate(ContainerState* aState,
nsDisplayItem* aItem,
const nsIntRect& aVisibleRect,
const nsIntRect& aDrawRect,
const FrameLayerBuilder::Clip& aClip)
const DisplayItemClip& aClip)
{
if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
mForceTransparentSurface = true;
@ -1867,7 +1867,7 @@ ContainerState::ThebesLayerData*
ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
const nsIntRect& aVisibleRect,
const nsIntRect& aDrawRect,
const FrameLayerBuilder::Clip& aClip,
const DisplayItemClip& aClip,
const nsIFrame* aActiveScrolledRoot,
const nsPoint& aTopLeft)
{
@ -2051,7 +2051,7 @@ ContainerState::ChooseActiveScrolledRoot(const nsDisplayList& aList,
*/
void
ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
FrameLayerBuilder::Clip& aClip,
DisplayItemClip& aClip,
uint32_t aFlags,
const nsIFrame* aForceActiveScrolledRoot)
{
@ -2077,7 +2077,7 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
nsDisplayItem::Type type = item->GetType();
if (type == nsDisplayItem::TYPE_CLIP ||
type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
FrameLayerBuilder::Clip childClip(aClip, item);
DisplayItemClip childClip(aClip, item);
ProcessDisplayItems(*item->GetSameCoordinateSystemChildren(), childClip, aFlags, lastActiveScrolledRoot);
continue;
}
@ -2271,8 +2271,8 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
* @return True if the clip should be applied, false
* otherwise.
*/
static bool ComputeCombinedClip(const FrameLayerBuilder::Clip& aClip,
FrameLayerBuilder::Clip* aOldClip,
static bool ComputeCombinedClip(const DisplayItemClip& aClip,
DisplayItemClip* aOldClip,
const nsPoint& aShift,
nsRegion& aCombined)
{
@ -2294,7 +2294,7 @@ static bool ComputeCombinedClip(const FrameLayerBuilder::Clip& aClip,
void
ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem,
Layer* aNewLayer,
const FrameLayerBuilder::Clip& aClip,
const DisplayItemClip& aClip,
const nsPoint& aTopLeft,
nsDisplayItemGeometry *aGeometry)
{
@ -2303,7 +2303,7 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem,
NS_ASSERTION(aItem->GetPerFrameKey(),
"Display items that render using Thebes must have a key");
nsDisplayItemGeometry *oldGeometry = NULL;
FrameLayerBuilder::Clip* oldClip = NULL;
DisplayItemClip* oldClip = NULL;
nsAutoTArray<nsIFrame*,4> changedFrames;
bool isInvalid = false;
Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem, &oldGeometry, &oldClip, &changedFrames, &isInvalid);
@ -2405,7 +2405,7 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem,
void
FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer,
nsDisplayItem* aItem,
const Clip& aClip,
const DisplayItemClip& aClip,
nsIFrame* aContainerLayerFrame,
LayerState aLayerState,
const nsPoint& aTopLeft,
@ -2427,7 +2427,7 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer,
// We need to grab these before calling AddLayerDisplayItem because it will overwrite them.
nsRegion clip;
FrameLayerBuilder::Clip* oldClip = nullptr;
DisplayItemClip* oldClip = nullptr;
GetOldLayerFor(aItem, nullptr, &oldClip);
hasClip = ComputeCombinedClip(aClip, oldClip,
aTopLeft - thebesData->mLastActiveScrolledRootOrigin,
@ -2585,7 +2585,7 @@ FrameLayerBuilder::ClippedDisplayItem::~ClippedDisplayItem()
void
FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
nsDisplayItem* aItem,
const Clip& aClip,
const DisplayItemClip& aClip,
LayerState aLayerState,
const nsPoint& aTopLeft,
LayerManager* aManager,
@ -2962,7 +2962,7 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
aContainerFrame, aContainerItem,
containerLayer, scaleParameters);
Clip clip;
DisplayItemClip clip;
state.ProcessDisplayItems(aChildren, clip, stateFlags);
// Set CONTENT_COMPONENT_ALPHA if any of our children have it.
@ -3320,7 +3320,7 @@ FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
rc->Init(presContext->DeviceContext(), aContext);
Clip currentClip;
DisplayItemClip currentClip;
bool setClipRect = false;
for (i = 0; i < items.Length(); ++i) {
@ -3414,264 +3414,8 @@ FrameLayerBuilder::DumpRetainedLayerTree(LayerManager* aManager, FILE* aFile, bo
}
#endif
FrameLayerBuilder::Clip::Clip(const Clip& aOther, nsDisplayItem* aClipItem)
: mRoundedClipRects(aOther.mRoundedClipRects),
mHaveClipRect(true)
{
nsDisplayItem::Type type = aClipItem->GetType();
NS_ABORT_IF_FALSE(type == nsDisplayItem::TYPE_CLIP ||
type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT,
"unexpected display item type");
nsDisplayClip* item = static_cast<nsDisplayClip*>(aClipItem);
// Always intersect with mClipRect, even if we're going to add a
// rounded rect.
if (aOther.mHaveClipRect) {
mClipRect.IntersectRect(aOther.mClipRect, item->GetClipRect());
} else {
mClipRect = item->GetClipRect();
}
if (type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
RoundedRect *rr = mRoundedClipRects.AppendElement();
if (rr) {
rr->mRect = item->GetClipRect();
static_cast<nsDisplayClipRoundedRect*>(item)->GetRadii(rr->mRadii);
}
}
// FIXME: Optimize away excess rounded rectangles due to the new addition.
}
void
FrameLayerBuilder::Clip::ApplyTo(gfxContext* aContext,
nsPresContext* aPresContext,
uint32_t aBegin, uint32_t aEnd)
{
int32_t A2D = aPresContext->AppUnitsPerDevPixel();
ApplyRectTo(aContext, A2D);
ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd);
}
void
FrameLayerBuilder::Clip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const
{
aContext->NewPath();
gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D);
aContext->Rectangle(clip, true);
aContext->Clip();
}
void
FrameLayerBuilder::Clip::ApplyRoundedRectsTo(gfxContext* aContext,
int32_t A2D,
uint32_t aBegin, uint32_t aEnd) const
{
aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
for (uint32_t i = aBegin; i < aEnd; ++i) {
AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[i]);
aContext->Clip();
}
}
void
FrameLayerBuilder::Clip::DrawRoundedRectsTo(gfxContext* aContext,
int32_t A2D,
uint32_t aBegin, uint32_t aEnd) const
{
aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
if (aEnd - aBegin == 0)
return;
// If there is just one rounded rect we can just fill it, if there are more then we
// must clip the rest to get the intersection of clips
ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd - 1);
AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[aEnd - 1]);
aContext->Fill();
}
void
FrameLayerBuilder::Clip::AddRoundedRectPathTo(gfxContext* aContext,
int32_t A2D,
const RoundedRect &aRoundRect) const
{
gfxCornerSizes pixelRadii;
nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii);
gfxRect clip = nsLayoutUtils::RectToGfxRect(aRoundRect.mRect, A2D);
clip.Round();
clip.Condition();
aContext->NewPath();
aContext->RoundedRectangle(clip, pixelRadii);
}
nsRect
FrameLayerBuilder::Clip::ApproximateIntersect(const nsRect& aRect) const
{
nsRect r = aRect;
if (mHaveClipRect) {
r.IntersectRect(r, mClipRect);
}
for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
i < iEnd; ++i) {
const Clip::RoundedRect &rr = mRoundedClipRects[i];
nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r);
r = rgn.GetLargestRectangle();
}
return r;
}
// Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter)
// and radii aXRadius, aYRadius.
bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint,
nscoord aYRadius, nscoord aYCenter, nscoord aYPoint)
{
float scaledX = float(aXPoint - aXCenter) / float(aXRadius);
float scaledY = float(aYPoint - aYCenter) / float(aYRadius);
return scaledX * scaledX + scaledY * scaledY < 1.0f;
}
bool
FrameLayerBuilder::Clip::IsRectClippedByRoundedCorner(const nsRect& aRect) const
{
if (mRoundedClipRects.IsEmpty())
return false;
nsRect rect;
rect.IntersectRect(aRect, NonRoundedIntersection());
for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
i < iEnd; ++i) {
const Clip::RoundedRect &rr = mRoundedClipRects[i];
// top left
if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X] &&
rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y]) {
if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_LEFT_X],
rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X],
rect.x,
rr.mRadii[NS_CORNER_TOP_LEFT_Y],
rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y],
rect.y)) {
return true;
}
}
// top right
if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X] &&
rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y]) {
if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_RIGHT_X],
rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X],
rect.XMost(),
rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
rect.y)) {
return true;
}
}
// bottom left
if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X] &&
rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y]) {
if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
rect.x,
rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
rect.YMost())) {
return true;
}
}
// bottom right
if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X] &&
rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y]) {
if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
rect.XMost(),
rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
rect.YMost())) {
return true;
}
}
}
return false;
}
nsRect
FrameLayerBuilder::Clip::NonRoundedIntersection() const
{
NS_ASSERTION(mHaveClipRect, "Must have a clip rect!");
nsRect result = mClipRect;
for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
i < iEnd; ++i) {
result.IntersectRect(result, mRoundedClipRects[i].mRect);
}
return result;
}
nsRect
FrameLayerBuilder::Clip::ApplyNonRoundedIntersection(const nsRect& aRect) const
{
if (!mHaveClipRect) {
return aRect;
}
nsRect result = aRect.Intersect(mClipRect);
for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
i < iEnd; ++i) {
result.Intersect(mRoundedClipRects[i].mRect);
}
return result;
}
void
FrameLayerBuilder::Clip::RemoveRoundedCorners()
{
if (mRoundedClipRects.IsEmpty())
return;
mClipRect = NonRoundedIntersection();
mRoundedClipRects.Clear();
}
static void
AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, nsRegion* aOut)
{
if (aR1.IsEqualInterior(aR2))
return;
nsRegion r;
r.Xor(aR1, aR2);
aOut->Or(*aOut, r);
}
void
FrameLayerBuilder::Clip::AddOffsetAndComputeDifference(const nsPoint& aOffset,
const nsRect& aBounds,
const Clip& aOther,
const nsRect& aOtherBounds,
nsRegion* aDifference)
{
if (mHaveClipRect != aOther.mHaveClipRect ||
mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) {
aDifference->Or(*aDifference, aBounds);
aDifference->Or(*aDifference, aOtherBounds);
return;
}
if (mHaveClipRect) {
AccumulateRectDifference((mClipRect + aOffset).Intersect(aBounds),
aOther.mClipRect.Intersect(aOtherBounds),
aDifference);
}
for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) {
// The corners make it tricky so we'll just add both rects here.
aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds));
aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds));
}
}
}
gfxRect
CalculateBounds(const nsTArray<FrameLayerBuilder::Clip::RoundedRect>& aRects, int32_t A2D)
CalculateBounds(const nsTArray<DisplayItemClip::RoundedRect>& aRects, int32_t A2D)
{
nsRect bounds = aRects[0].mRect;
for (uint32_t i = 1; i < aRects.Length(); ++i) {
@ -3691,7 +3435,7 @@ SetClipCount(ThebesDisplayItemLayerUserData* aThebesData,
}
void
ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
ContainerState::SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
uint32_t aRoundedRectClipCount)
{
// if the number of clips we are going to mask has decreased, then aLayer might have

View File

@ -14,6 +14,7 @@
#include "nsDisplayListInvalidation.h"
#include "LayerTreeInvalidation.h"
#include "ImageLayers.h"
#include "DisplayItemClip.h"
class nsDisplayListBuilder;
class nsDisplayList;
@ -270,10 +271,9 @@ public:
* @param aManager If the layer is in the LAYER_INACTIVE state,
* then this is the temporary layer manager to draw with.
*/
struct Clip;
void AddLayerDisplayItem(Layer* aLayer,
nsDisplayItem* aItem,
const Clip& aClip,
const DisplayItemClip& aClip,
LayerState aLayerState,
const nsPoint& aTopLeft,
LayerManager* aManager,
@ -288,7 +288,7 @@ public:
*/
void AddThebesDisplayItem(ThebesLayer* aLayer,
nsDisplayItem* aItem,
const Clip& aClip,
const DisplayItemClip& aClip,
nsIFrame* aContainerLayerFrame,
LayerState aLayerState,
const nsPoint& aTopLeft,
@ -307,7 +307,7 @@ public:
*/
Layer* GetOldLayerFor(nsDisplayItem* aItem,
nsDisplayItemGeometry** aOldGeometry = nullptr,
Clip** aOldClip = nullptr,
DisplayItemClip** aOldClip = nullptr,
nsTArray<nsIFrame*>* aChangedFrames = nullptr,
bool *aIsInvalid = nullptr);
@ -361,103 +361,6 @@ public:
* DisplayItemData so we can retrieve the layer from within layout.
*/
void StoreOptimizedLayerForFrame(nsDisplayItem* aItem, Layer* aLayer);
/**
* Clip represents the intersection of an optional rectangle with a
* list of rounded rectangles.
*/
struct Clip {
struct RoundedRect {
nsRect mRect;
// Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
nscoord mRadii[8];
RoundedRect operator+(const nsPoint& aOffset) const {
RoundedRect r = *this;
r.mRect += aOffset;
return r;
}
bool operator==(const RoundedRect& aOther) const {
if (!mRect.IsEqualInterior(aOther.mRect)) {
return false;
}
NS_FOR_CSS_HALF_CORNERS(corner) {
if (mRadii[corner] != aOther.mRadii[corner]) {
return false;
}
}
return true;
}
bool operator!=(const RoundedRect& aOther) const {
return !(*this == aOther);
}
};
nsRect mClipRect;
nsTArray<RoundedRect> mRoundedClipRects;
bool mHaveClipRect;
Clip() : mHaveClipRect(false) {}
// Construct as the intersection of aOther and aClipItem.
Clip(const Clip& aOther, nsDisplayItem* aClipItem);
// Apply this |Clip| to the given gfxContext. Any saving of state
// or clearing of other clips must be done by the caller.
// See aBegin/aEnd note on ApplyRoundedRectsTo.
void ApplyTo(gfxContext* aContext, nsPresContext* aPresContext,
uint32_t aBegin = 0, uint32_t aEnd = UINT32_MAX);
void ApplyRectTo(gfxContext* aContext, int32_t A2D) const;
// Applies the rounded rects in this Clip to aContext
// Will only apply rounded rects from aBegin (inclusive) to aEnd
// (exclusive) or the number of rounded rects, whichever is smaller.
void ApplyRoundedRectsTo(gfxContext* aContext, int32_t A2DPRInt32,
uint32_t aBegin, uint32_t aEnd) const;
// Draw (fill) the rounded rects in this clip to aContext
void DrawRoundedRectsTo(gfxContext* aContext, int32_t A2D,
uint32_t aBegin, uint32_t aEnd) const;
// 'Draw' (create as a path, does not stroke or fill) aRoundRect to aContext
void AddRoundedRectPathTo(gfxContext* aContext, int32_t A2D,
const RoundedRect &aRoundRect) const;
// Return a rectangle contained in the intersection of aRect with this
// clip region. Tries to return the largest possible rectangle, but may
// not succeed.
nsRect ApproximateIntersect(const nsRect& aRect) const;
// Returns false if aRect is definitely not clipped by a rounded corner in
// this clip. Returns true if aRect is clipped by a rounded corner in this
// clip or it can not be quickly determined that it is not clipped by a
// rounded corner in this clip.
bool IsRectClippedByRoundedCorner(const nsRect& aRect) const;
// Intersection of all rects in this clip ignoring any rounded corners.
nsRect NonRoundedIntersection() const;
// Intersect the given rects with all rects in this clip, ignoring any
// rounded corners.
nsRect ApplyNonRoundedIntersection(const nsRect& aRect) const;
// Gets rid of any rounded corners in this clip.
void RemoveRoundedCorners();
// Adds the difference between Intersect(*this + aPoint, aBounds) and
// Intersect(aOther, aOtherBounds) to aDifference.
void AddOffsetAndComputeDifference(const nsPoint& aPoint, const nsRect& aBounds,
const Clip& aOther, const nsRect& aOtherBounds,
nsRegion* aDifference);
bool operator==(const Clip& aOther) const {
return mHaveClipRect == aOther.mHaveClipRect &&
(!mHaveClipRect || mClipRect.IsEqualInterior(aOther.mClipRect)) &&
mRoundedClipRects == aOther.mRoundedClipRects;
}
bool operator!=(const Clip& aOther) const {
return !(*this == aOther);
}
};
NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(LayerManagerDataProperty,
RemoveFrameFromLayerManager)
@ -523,7 +426,7 @@ public:
nsRefPtr<LayerManager> mInactiveManager;
nsAutoTArray<nsIFrame*, 1> mFrameList;
nsAutoPtr<nsDisplayItemGeometry> mGeometry;
Clip mClip;
DisplayItemClip mClip;
uint32_t mDisplayItemKey;
uint32_t mContainerLayerGeneration;
LayerState mLayerState;
@ -600,7 +503,7 @@ protected:
* mItem always has an underlying frame.
*/
struct ClippedDisplayItem {
ClippedDisplayItem(nsDisplayItem* aItem, const Clip& aClip, uint32_t aGeneration)
ClippedDisplayItem(nsDisplayItem* aItem, const DisplayItemClip& aClip, uint32_t aGeneration)
: mItem(aItem), mClip(aClip), mContainerLayerGeneration(aGeneration)
{
}
@ -616,7 +519,7 @@ protected:
*/
nsRefPtr<LayerManager> mInactiveLayerManager;
Clip mClip;
DisplayItemClip mClip;
uint32_t mContainerLayerGeneration;
};

View File

@ -58,10 +58,10 @@ EXPORTS_mozilla = \
$(NULL)
CPPSRCS = \
DisplayItemClip.cpp \
FrameLayerBuilder.cpp \
MaskLayerImageCache.cpp \
FramePropertyTable.cpp \
RestyleTracker.cpp \
MaskLayerImageCache.cpp \
nsCSSColorUtils.cpp \
nsCSSFrameConstructor.cpp \
nsCSSRendering.cpp \
@ -87,6 +87,7 @@ CPPSRCS = \
nsStyleSheetService.cpp \
PaintTracker.cpp \
PositionedEventTargeting.cpp \
RestyleTracker.cpp \
StackArena.cpp \
$(NULL)

View File

@ -7,6 +7,7 @@
#define MASKLAYERIMAGECACHE_H_
#include "FrameLayerBuilder.h"
#include "DisplayItemClip.h"
#include "nsPresContext.h"
namespace mozilla {
@ -37,13 +38,13 @@ public:
/**
* Representation of a rounded rectangle in device pixel coordinates, in
* contrast to FrameLayerBuilder::Clip::RoundedRect, which uses app units.
* contrast to DisplayItemClip::RoundedRect, which uses app units.
* In particular, our internal representation uses a gfxRect, rather than
* an nsRect, so this class is easier to use with transforms.
*/
struct PixelRoundedRect
{
PixelRoundedRect(const FrameLayerBuilder::Clip::RoundedRect& aRRect,
PixelRoundedRect(const DisplayItemClip::RoundedRect& aRRect,
nsPresContext* aPresContext)
: mRect(aPresContext->AppUnitsToGfxUnits(aRRect.mRect.x),
aPresContext->AppUnitsToGfxUnits(aRRect.mRect.y),