зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
a352316479
Коммит
a6cb62365a
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче