Bug 602757. Part 4: Let nsDisplayBackground::GetOpaqueRegion return opaque regions for arbitrary repeat modes, background-clip, border-radius, etc. r=tnikkel,sr=dbaron,a=blocking

This commit is contained in:
Robert O'Callahan 2011-01-03 14:48:09 +13:00
Родитель a352316479
Коммит a6cb62365a
4 изменённых файлов: 161 добавлений и 41 удалений

Просмотреть файл

@ -2134,6 +2134,51 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
}
}
/**
* A struct representing all the information needed to paint a background
* image to some target, taking into account all CSS background-* properties.
* See PrepareBackgroundLayer.
*/
struct BackgroundLayerState {
/**
* @param aFlags some combination of nsCSSRendering::PAINTBG_* flags
*/
BackgroundLayerState(nsIFrame* aForFrame, const nsStyleImage* aImage, PRUint32 aFlags)
: mImageRenderer(aForFrame, aImage, aFlags) {}
/**
* The ImageRenderer that will be used to draw the background.
*/
ImageRenderer mImageRenderer;
/**
* A rectangle that one copy of the image tile is mapped onto. Same
* coordinate system as aBorderArea/aBGClipRect passed into
* PrepareBackgroundLayer.
*/
nsRect mDestArea;
/**
* The actual rectangle that should be filled with (complete or partial)
* image tiles. Same coordinate system as aBorderArea/aBGClipRect passed into
* PrepareBackgroundLayer.
*/
nsRect mFillArea;
/**
* The anchor point that should be snapped to a pixel corner. Same
* coordinate system as aBorderArea/aBGClipRect passed into
* PrepareBackgroundLayer.
*/
nsPoint mAnchor;
};
static BackgroundLayerState
PrepareBackgroundLayer(nsPresContext* aPresContext,
nsIFrame* aForFrame,
PRUint32 aFlags,
const nsRect& aBorderArea,
const nsRect& aBGClipRect,
const nsStyleBackground& aBackground,
const nsStyleBackground::Layer& aLayer);
void
nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@ -2304,9 +2349,13 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
}
}
if (!dirtyRectGfx.IsEmpty()) {
PaintBackgroundLayer(aPresContext, aRenderingContext, aForFrame, aFlags,
dirtyRect, aBorderArea, bgClipArea, *bg,
layer);
BackgroundLayerState state = PrepareBackgroundLayer(aPresContext, aForFrame,
aFlags, aBorderArea, bgClipArea, *bg, layer);
if (!state.mFillArea.IsEmpty()) {
state.mImageRenderer.Draw(aPresContext, aRenderingContext,
state.mDestArea, state.mFillArea,
state.mAnchor + aBorderArea.TopLeft(), dirtyRect);
}
}
}
}
@ -2333,16 +2382,14 @@ ScaleDimension(const nsStyleBackground::Size::Dimension& aDimension,
}
}
static void
PaintBackgroundLayer(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nsIFrame* aForFrame,
PRUint32 aFlags,
const nsRect& aDirtyRect, // intersected with aBGClipRect
const nsRect& aBorderArea,
const nsRect& aBGClipRect,
const nsStyleBackground& aBackground,
const nsStyleBackground::Layer& aLayer)
static BackgroundLayerState
PrepareBackgroundLayer(nsPresContext* aPresContext,
nsIFrame* aForFrame,
PRUint32 aFlags,
const nsRect& aBorderArea,
const nsRect& aBGClipRect,
const nsStyleBackground& aBackground,
const nsStyleBackground::Layer& aLayer)
{
/*
* The background properties we need to keep in mind when drawing background
@ -2400,12 +2447,14 @@ PaintBackgroundLayer(nsPresContext* aPresContext,
*/
PRUint32 irFlags = 0;
if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES)
if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) {
irFlags |= ImageRenderer::FLAG_SYNC_DECODE_IMAGES;
ImageRenderer imageRenderer(aForFrame, &aLayer.mImage, irFlags);
if (!imageRenderer.PrepareImage()) {
}
BackgroundLayerState state(aForFrame, &aLayer.mImage, irFlags);
if (!state.mImageRenderer.PrepareImage()) {
// There's no image or it's not ready to be painted.
return;
return state;
}
// Compute background origin area relative to aBorderArea now as we may need
@ -2469,7 +2518,7 @@ PaintBackgroundLayer(nsPresContext* aPresContext,
//
// relative to aBorderArea.TopLeft() (which is where the top-left
// of aForFrame's border-box will be rendered)
nsPoint imageTopLeft, anchor;
nsPoint imageTopLeft;
if (NS_STYLE_BG_ATTACHMENT_FIXED == aLayer.mAttachment) {
aPresContext->SetHasFixedBackgroundFrame();
@ -2514,9 +2563,9 @@ PaintBackgroundLayer(nsPresContext* aPresContext,
}
}
nsSize imageSize = imageRenderer.ComputeSize(bgPositioningArea.Size());
nsSize imageSize = state.mImageRenderer.ComputeSize(bgPositioningArea.Size());
if (imageSize.width <= 0 || imageSize.height <= 0)
return;
return state;
// Scale the image as specified for background-size and as required for
// proper background positioning when background-position is defined with
@ -2564,27 +2613,38 @@ PaintBackgroundLayer(nsPresContext* aPresContext,
// Compute the position of the background now that the background's size is
// determined.
ComputeBackgroundAnchorPoint(aLayer, bgPositioningArea.Size(), imageSize,
&imageTopLeft, &anchor);
&imageTopLeft, &state.mAnchor);
imageTopLeft += bgPositioningArea.TopLeft();
anchor += bgPositioningArea.TopLeft();
state.mAnchor += bgPositioningArea.TopLeft();
nsRect destArea(imageTopLeft + aBorderArea.TopLeft(), imageSize);
nsRect fillArea = destArea;
state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize);
state.mFillArea = state.mDestArea;
PRIntn repeat = aLayer.mRepeat;
PR_STATIC_ASSERT(NS_STYLE_BG_REPEAT_XY ==
(NS_STYLE_BG_REPEAT_X | NS_STYLE_BG_REPEAT_Y));
if (repeat & NS_STYLE_BG_REPEAT_X) {
fillArea.x = bgClipRect.x;
fillArea.width = bgClipRect.width;
state.mFillArea.x = bgClipRect.x;
state.mFillArea.width = bgClipRect.width;
}
if (repeat & NS_STYLE_BG_REPEAT_Y) {
fillArea.y = bgClipRect.y;
fillArea.height = bgClipRect.height;
state.mFillArea.y = bgClipRect.y;
state.mFillArea.height = bgClipRect.height;
}
fillArea.IntersectRect(fillArea, bgClipRect);
state.mFillArea.IntersectRect(state.mFillArea, bgClipRect);
return state;
}
imageRenderer.Draw(aPresContext, aRenderingContext, destArea, fillArea,
anchor + aBorderArea.TopLeft(), aDirtyRect);
nsRect
nsCSSRendering::GetBackgroundLayerRect(nsPresContext* aPresContext,
nsIFrame* aForFrame,
const nsRect& aBorderArea,
const nsStyleBackground& aBackground,
const nsStyleBackground::Layer& aLayer)
{
BackgroundLayerState state =
PrepareBackgroundLayer(aPresContext, aForFrame, 0, aBorderArea,
aBorderArea, aBackground, aLayer);
return state.mFillArea;
}
static void

Просмотреть файл

@ -256,6 +256,17 @@ struct nsCSSRendering {
PRUint32 aFlags,
nsRect* aBGClipRect = nsnull);
/**
* Returns the rectangle covered by the given background layer image, taking
* into account background positioning, sizing, and repetition, but not
* clipping.
*/
static nsRect GetBackgroundLayerRect(nsPresContext* aPresContext,
nsIFrame* aForFrame,
const nsRect& aBorderArea,
const nsStyleBackground& aBackground,
const nsStyleBackground::Layer& aLayer);
/**
* Called by the presShell when painting is finished, so we can clear our
* inline background data cache.

Просмотреть файл

@ -872,6 +872,44 @@ nsDisplayBackground::ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bgSC);
}
nsRegion
nsDisplayBackground::GetInsideClipRegion(PRUint8 aClip, const nsRect& aRect)
{
nsRegion result;
if (aRect.IsEmpty())
return result;
nscoord radii[8];
nsRect clipRect;
PRBool haveRadii;
switch (aClip) {
case NS_STYLE_BG_CLIP_BORDER:
haveRadii = mFrame->GetBorderRadii(radii);
clipRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
break;
case NS_STYLE_BG_CLIP_PADDING:
haveRadii = mFrame->GetPaddingBoxBorderRadii(radii);
clipRect = mFrame->GetPaddingRect() - mFrame->GetPosition() + ToReferenceFrame();
break;
case NS_STYLE_BG_CLIP_CONTENT:
haveRadii = mFrame->GetContentBoxBorderRadii(radii);
clipRect = mFrame->GetContentRect() - mFrame->GetPosition() + ToReferenceFrame();
break;
default:
NS_NOTREACHED("Unknown clip type");
return result;
}
if (haveRadii) {
result = nsLayoutUtils::RoundedRectIntersectRect(clipRect, radii, aRect);
} else {
nsRect r;
r.IntersectRect(clipRect, aRect);
result = r;
}
return result;
}
nsRegion
nsDisplayBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
PRBool* aForceTransparentSurface) {
@ -893,26 +931,35 @@ nsDisplayBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
}
nsStyleContext* bgSC;
nsPresContext* presContext = mFrame->PresContext();
if (!nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bgSC))
return result;
const nsStyleBackground* bg = bgSC->GetStyleBackground();
const nsStyleBackground::Layer& bottomLayer = bg->BottomLayer();
// bottom layer's clip is used for the color
if (bottomLayer.mClip != NS_STYLE_BG_CLIP_BORDER ||
nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius))
return result;
nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
if (NS_GET_A(bg->mBackgroundColor) == 255 &&
!nsCSSRendering::IsCanvasFrame(mFrame)) {
result = GetBounds(aBuilder);
return result;
result = GetInsideClipRegion(bottomLayer.mClip, borderBox);
}
if (bottomLayer.mRepeat == NS_STYLE_BG_REPEAT_XY &&
bottomLayer.mImage.IsOpaque()) {
result = GetBounds(aBuilder);
// For policies other than EACH_BOX, don't try to optimize here, since
// this could easily lead to O(N^2) behavior inside InlineBackgroundData,
// which expects frames to be sent to it in content order, not reverse
// content order which we'll produce here.
// Of course, if there's only one frame in the flow, it doesn't matter.
if (bg->mBackgroundInlinePolicy == NS_STYLE_BG_INLINE_POLICY_EACH_BOX ||
(!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
const nsStyleBackground::Layer& layer = bg->mLayers[i];
if (layer.mImage.IsOpaque()) {
nsRect r = nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame,
borderBox, *bg, layer);
result.Or(result, GetInsideClipRegion(layer.mClip, r));
}
}
}
return result;
}

Просмотреть файл

@ -1413,6 +1413,8 @@ public:
virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
NS_DISPLAY_DECL_NAME("Background", TYPE_BACKGROUND)
protected:
nsRegion GetInsideClipRegion(PRUint8 aClip, const nsRect& aRect);
/* Used to cache mFrame->IsThemed() since it isn't a cheap call */
PRPackedBool mIsThemed;
nsITheme::Transparency mThemeTransparency;