зеркало из https://github.com/mozilla/gecko-dev.git
Bug 548372 - Part2. background-repeat round/space rendering. r=mstange
This commit is contained in:
Родитель
a045541027
Коммит
6710f05dd7
|
@ -2046,9 +2046,13 @@ nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
// We can skip painting the background color if a background image is opaque.
|
||||
nsStyleImageLayers::Repeat repeat = bg->BottomLayer().mRepeat;
|
||||
bool xFullRepeat = repeat.mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT ||
|
||||
repeat.mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_ROUND;
|
||||
bool yFullRepeat = repeat.mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT ||
|
||||
repeat.mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_ROUND;
|
||||
if (aDrawBackgroundColor &&
|
||||
bg->BottomLayer().mRepeat.mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT &&
|
||||
bg->BottomLayer().mRepeat.mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT &&
|
||||
xFullRepeat && yFullRepeat &&
|
||||
bg->BottomLayer().mImage.IsOpaque() &&
|
||||
bg->BottomLayer().mBlendMode == NS_STYLE_BLEND_NORMAL) {
|
||||
aDrawBackgroundColor = false;
|
||||
|
@ -3102,7 +3106,8 @@ nsCSSRendering::PaintBackgroundWithSC(const PaintBGParams& aParams,
|
|||
aParams.renderingCtx,
|
||||
state.mDestArea, state.mFillArea,
|
||||
state.mAnchor + paintBorderArea.TopLeft(),
|
||||
clipState.mDirtyRect);
|
||||
clipState.mDirtyRect,
|
||||
state.mRepeatSize);
|
||||
|
||||
if (co != CompositionOp::OP_OVER) {
|
||||
ctx->SetOp(CompositionOp::OP_OVER);
|
||||
|
@ -3225,6 +3230,19 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext,
|
|||
return bgPositioningArea;
|
||||
}
|
||||
|
||||
// Implementation of the formula for computation of background-repeat round
|
||||
// See http://dev.w3.org/csswg/css3-background/#the-background-size
|
||||
// This function returns the adjusted size of the background image.
|
||||
static nscoord
|
||||
ComputeRoundedSize(nscoord aCurrentSize, nscoord aPositioningSize)
|
||||
{
|
||||
float repeatCount = NS_roundf(float(aPositioningSize) / float(aCurrentSize));
|
||||
if (repeatCount < 1.0f) {
|
||||
return aPositioningSize;
|
||||
}
|
||||
return nscoord(NS_lround(float(aPositioningSize) / repeatCount));
|
||||
}
|
||||
|
||||
// Apply the CSS image sizing algorithm as it applies to background images.
|
||||
// See http://www.w3.org/TR/css3-background/#the-background-size .
|
||||
// aIntrinsicSize is the size that the background image 'would like to be'.
|
||||
|
@ -3232,8 +3250,11 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext,
|
|||
static nsSize
|
||||
ComputeDrawnSizeForBackground(const CSSSizeOrRatio& aIntrinsicSize,
|
||||
const nsSize& aBgPositioningArea,
|
||||
const nsStyleImageLayers::Size& aLayerSize)
|
||||
const nsStyleImageLayers::Size& aLayerSize,
|
||||
uint8_t aXRepeat, uint8_t aYRepeat)
|
||||
{
|
||||
nsSize imageSize;
|
||||
|
||||
// Size is dictated by cover or contain rules.
|
||||
if (aLayerSize.mWidthType == nsStyleImageLayers::Size::eContain ||
|
||||
aLayerSize.mWidthType == nsStyleImageLayers::Size::eCover) {
|
||||
|
@ -3241,25 +3262,82 @@ ComputeDrawnSizeForBackground(const CSSSizeOrRatio& aIntrinsicSize,
|
|||
aLayerSize.mWidthType == nsStyleImageLayers::Size::eCover
|
||||
? nsImageRenderer::COVER
|
||||
: nsImageRenderer::CONTAIN;
|
||||
return nsImageRenderer::ComputeConstrainedSize(aBgPositioningArea,
|
||||
aIntrinsicSize.mRatio,
|
||||
fitType);
|
||||
imageSize = nsImageRenderer::ComputeConstrainedSize(aBgPositioningArea,
|
||||
aIntrinsicSize.mRatio,
|
||||
fitType);
|
||||
} else {
|
||||
// No cover/contain constraint, use default algorithm.
|
||||
CSSSizeOrRatio specifiedSize;
|
||||
if (aLayerSize.mWidthType == nsStyleImageLayers::Size::eLengthPercentage) {
|
||||
specifiedSize.SetWidth(
|
||||
aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea));
|
||||
}
|
||||
if (aLayerSize.mHeightType == nsStyleImageLayers::Size::eLengthPercentage) {
|
||||
specifiedSize.SetHeight(
|
||||
aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea));
|
||||
}
|
||||
|
||||
imageSize = nsImageRenderer::ComputeConcreteSize(specifiedSize,
|
||||
aIntrinsicSize,
|
||||
aBgPositioningArea);
|
||||
}
|
||||
|
||||
// No cover/contain constraint, use default algorithm.
|
||||
CSSSizeOrRatio specifiedSize;
|
||||
if (aLayerSize.mWidthType == nsStyleImageLayers::Size::eLengthPercentage) {
|
||||
specifiedSize.SetWidth(
|
||||
aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea));
|
||||
}
|
||||
if (aLayerSize.mHeightType == nsStyleImageLayers::Size::eLengthPercentage) {
|
||||
specifiedSize.SetHeight(
|
||||
aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea));
|
||||
// See https://www.w3.org/TR/css3-background/#background-size .
|
||||
// "If 'background-repeat' is 'round' for one (or both) dimensions, there is a second
|
||||
// step. The UA must scale the image in that dimension (or both dimensions) so that
|
||||
// it fits a whole number of times in the background positioning area."
|
||||
// "If 'background-repeat' is 'round' for one dimension only and if 'background-size'
|
||||
// is 'auto' for the other dimension, then there is a third step: that other dimension
|
||||
// is scaled so that the original aspect ratio is restored."
|
||||
bool isRepeatRoundInBothDimensions = aXRepeat == NS_STYLE_IMAGELAYER_REPEAT_ROUND &&
|
||||
aYRepeat == NS_STYLE_IMAGELAYER_REPEAT_ROUND;
|
||||
|
||||
// Calculate the rounded size only if the background-size computation
|
||||
// returned a correct size for the image.
|
||||
if (imageSize.width && aXRepeat == NS_STYLE_IMAGELAYER_REPEAT_ROUND) {
|
||||
imageSize.width = ComputeRoundedSize(imageSize.width, aBgPositioningArea.width);
|
||||
if (!isRepeatRoundInBothDimensions &&
|
||||
aLayerSize.mHeightType == nsStyleImageLayers::Size::DimensionType::eAuto) {
|
||||
// Restore intrinsic rato
|
||||
if (aIntrinsicSize.mRatio.width) {
|
||||
float scale = float(aIntrinsicSize.mRatio.height) / aIntrinsicSize.mRatio.width;
|
||||
imageSize.height = NSCoordSaturatingNonnegativeMultiply(imageSize.width, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nsImageRenderer::ComputeConcreteSize(specifiedSize,
|
||||
aIntrinsicSize,
|
||||
aBgPositioningArea);
|
||||
// Calculate the rounded size only if the background-size computation
|
||||
// returned a correct size for the image.
|
||||
if (imageSize.height && aYRepeat == NS_STYLE_IMAGELAYER_REPEAT_ROUND) {
|
||||
imageSize.height = ComputeRoundedSize(imageSize.height, aBgPositioningArea.height);
|
||||
if (!isRepeatRoundInBothDimensions &&
|
||||
aLayerSize.mWidthType == nsStyleImageLayers::Size::DimensionType::eAuto) {
|
||||
// Restore intrinsic rato
|
||||
if (aIntrinsicSize.mRatio.height) {
|
||||
float scale = float(aIntrinsicSize.mRatio.width) / aIntrinsicSize.mRatio.height;
|
||||
imageSize.width = NSCoordSaturatingNonnegativeMultiply(imageSize.height, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imageSize;
|
||||
}
|
||||
|
||||
/* ComputeSpacedRepeatSize
|
||||
* aImageDimension: the image width/height
|
||||
* aAvailableSpace: the background positioning area width/height
|
||||
* aRepeatSize: the image size plus gap size of app units for use as spacing
|
||||
*/
|
||||
static nscoord
|
||||
ComputeSpacedRepeatSize(nscoord aImageDimension,
|
||||
nscoord aAvailableSpace) {
|
||||
float ratio = aAvailableSpace / aImageDimension;
|
||||
|
||||
if (ratio < 2.0f) { // If you can't repeat at least twice, then don't repeat.
|
||||
return aImageDimension;
|
||||
} else {
|
||||
return (aAvailableSpace - aImageDimension) / (NSToIntFloor(ratio) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
nsBackgroundLayerState
|
||||
|
@ -3384,14 +3462,20 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext,
|
|||
}
|
||||
}
|
||||
|
||||
// Scale the image as specified for background-size and as required for
|
||||
// proper background positioning when background-position is defined with
|
||||
// percentages.
|
||||
int repeatX = aLayer.mRepeat.mXRepeat;
|
||||
int repeatY = aLayer.mRepeat.mYRepeat;
|
||||
|
||||
// Scale the image as specified for background-size and background-repeat.
|
||||
// Also as required for proper background positioning when background-position
|
||||
// is defined with percentages.
|
||||
CSSSizeOrRatio intrinsicSize = state.mImageRenderer.ComputeIntrinsicSize();
|
||||
nsSize bgPositionSize = bgPositioningArea.Size();
|
||||
nsSize imageSize = ComputeDrawnSizeForBackground(intrinsicSize,
|
||||
bgPositionSize,
|
||||
aLayer.mSize);
|
||||
aLayer.mSize,
|
||||
repeatX,
|
||||
repeatY);
|
||||
|
||||
if (imageSize.width <= 0 || imageSize.height <= 0)
|
||||
return state;
|
||||
|
||||
|
@ -3403,21 +3487,45 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext,
|
|||
nsImageRenderer::ComputeObjectAnchorPoint(aLayer.mPosition,
|
||||
bgPositionSize, imageSize,
|
||||
&imageTopLeft, &state.mAnchor);
|
||||
state.mRepeatSize = imageSize;
|
||||
if (repeatX == NS_STYLE_IMAGELAYER_REPEAT_SPACE) {
|
||||
state.mRepeatSize.width = ComputeSpacedRepeatSize(imageSize.width,
|
||||
bgPositionSize.width);
|
||||
if (state.mRepeatSize.width > imageSize.width) {
|
||||
imageTopLeft.x = 0;
|
||||
state.mAnchor.x = 0;
|
||||
} else {
|
||||
repeatX = NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT;
|
||||
}
|
||||
}
|
||||
|
||||
if (repeatY == NS_STYLE_IMAGELAYER_REPEAT_SPACE) {
|
||||
state.mRepeatSize.height = ComputeSpacedRepeatSize(imageSize.height,
|
||||
bgPositionSize.height);
|
||||
if (state.mRepeatSize.height > imageSize.height) {
|
||||
imageTopLeft.y = 0;
|
||||
state.mAnchor.y = 0;
|
||||
} else {
|
||||
repeatY = NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT;
|
||||
}
|
||||
}
|
||||
|
||||
imageTopLeft += bgPositioningArea.TopLeft();
|
||||
state.mAnchor += bgPositioningArea.TopLeft();
|
||||
|
||||
state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize);
|
||||
state.mFillArea = state.mDestArea;
|
||||
int repeatX = aLayer.mRepeat.mXRepeat;
|
||||
int repeatY = aLayer.mRepeat.mYRepeat;
|
||||
|
||||
ExtendMode repeatMode = ExtendMode::CLAMP;
|
||||
if (repeatX == NS_STYLE_IMAGELAYER_REPEAT_REPEAT) {
|
||||
if (repeatX == NS_STYLE_IMAGELAYER_REPEAT_REPEAT ||
|
||||
repeatX == NS_STYLE_IMAGELAYER_REPEAT_ROUND ||
|
||||
repeatX == NS_STYLE_IMAGELAYER_REPEAT_SPACE) {
|
||||
state.mFillArea.x = bgClipRect.x;
|
||||
state.mFillArea.width = bgClipRect.width;
|
||||
repeatMode = ExtendMode::REPEAT_X;
|
||||
}
|
||||
if (repeatY == NS_STYLE_IMAGELAYER_REPEAT_REPEAT) {
|
||||
if (repeatY == NS_STYLE_IMAGELAYER_REPEAT_REPEAT ||
|
||||
repeatY == NS_STYLE_IMAGELAYER_REPEAT_ROUND ||
|
||||
repeatY == NS_STYLE_IMAGELAYER_REPEAT_SPACE) {
|
||||
state.mFillArea.y = bgClipRect.y;
|
||||
state.mFillArea.height = bgClipRect.height;
|
||||
|
||||
|
@ -5122,6 +5230,7 @@ nsImageRenderer::Draw(nsPresContext* aPresContext,
|
|||
const nsRect& aDest,
|
||||
const nsRect& aFill,
|
||||
const nsPoint& aAnchor,
|
||||
const nsSize& aRepeatSize,
|
||||
const CSSIntRect& aSrc)
|
||||
{
|
||||
if (!IsReady()) {
|
||||
|
@ -5158,7 +5267,8 @@ nsImageRenderer::Draw(nsPresContext* aPresContext,
|
|||
nsLayoutUtils::DrawBackgroundImage(*ctx,
|
||||
aPresContext,
|
||||
mImageContainer, imageSize, filter,
|
||||
aDest, aFill, aAnchor, aDirtyRect,
|
||||
aDest, aFill, aRepeatSize,
|
||||
aAnchor, aDirtyRect,
|
||||
ConvertImageRendererToDrawFlags(mFlags),
|
||||
mExtendMode);
|
||||
break;
|
||||
|
@ -5255,7 +5365,8 @@ nsImageRenderer::DrawBackground(nsPresContext* aPresContext,
|
|||
const nsRect& aDest,
|
||||
const nsRect& aFill,
|
||||
const nsPoint& aAnchor,
|
||||
const nsRect& aDirty)
|
||||
const nsRect& aDirty,
|
||||
const nsSize& aRepeatSize)
|
||||
{
|
||||
if (!IsReady()) {
|
||||
NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
|
||||
|
@ -5267,7 +5378,7 @@ nsImageRenderer::DrawBackground(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
return Draw(aPresContext, aRenderingContext,
|
||||
aDirty, aDest, aFill, aAnchor,
|
||||
aDirty, aDest, aFill, aAnchor, aRepeatSize,
|
||||
CSSIntRect(0, 0,
|
||||
nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
|
||||
nsPresContext::AppUnitsToIntCSSPixels(mSize.height)));
|
||||
|
@ -5431,7 +5542,7 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext,
|
|||
: aFill;
|
||||
|
||||
return Draw(aPresContext, aRenderingContext, aDirtyRect, destTile,
|
||||
aFill, destTile.TopLeft(), aSrc);
|
||||
aFill, destTile.TopLeft(), nsSize(0, 0), aSrc);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -217,7 +217,8 @@ public:
|
|||
const nsRect& aDest,
|
||||
const nsRect& aFill,
|
||||
const nsPoint& aAnchor,
|
||||
const nsRect& aDirty);
|
||||
const nsRect& aDirty,
|
||||
const nsSize& aRepeatSize);
|
||||
|
||||
/**
|
||||
* Draw the image to a single component of a border-image style rendering.
|
||||
|
@ -273,10 +274,11 @@ private:
|
|||
const nsRect& aDest,
|
||||
const nsRect& aFill,
|
||||
const nsPoint& aAnchor,
|
||||
const nsSize& aRepeatSize,
|
||||
const mozilla::CSSIntRect& aSrc);
|
||||
|
||||
/**
|
||||
* Helper method for creating a gfxDrawable from mPaintServerFrame or
|
||||
* Helper method for creating a gfxDrawable from mPaintServerFrame or
|
||||
* mImageElementSurface.
|
||||
* Requires mType is eStyleImageType_Element.
|
||||
* Returns null if we cannot create the drawable.
|
||||
|
@ -336,6 +338,11 @@ struct nsBackgroundLayerState {
|
|||
* PrepareImageLayer.
|
||||
*/
|
||||
nsPoint mAnchor;
|
||||
/**
|
||||
* The background-repeat property space keyword computes the
|
||||
* repeat size which is image size plus spacing.
|
||||
*/
|
||||
nsSize mRepeatSize;
|
||||
};
|
||||
|
||||
struct nsCSSRendering {
|
||||
|
|
|
@ -2912,6 +2912,8 @@ nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
|
|||
(!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
|
||||
const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mImage.mLayers[mLayer];
|
||||
if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL &&
|
||||
layer.mRepeat.mXRepeat != NS_STYLE_IMAGELAYER_REPEAT_SPACE &&
|
||||
layer.mRepeat.mYRepeat != NS_STYLE_IMAGELAYER_REPEAT_SPACE &&
|
||||
layer.mClip != NS_STYLE_IMAGELAYER_CLIP_TEXT) {
|
||||
result = GetInsideClipRegion(this, layer.mClip, mBounds, mBackgroundRect);
|
||||
}
|
||||
|
|
|
@ -6678,7 +6678,6 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
|
|||
region, svgViewportSize);
|
||||
}
|
||||
|
||||
|
||||
static DrawResult
|
||||
DrawImageInternal(gfxContext& aContext,
|
||||
nsPresContext* aPresContext,
|
||||
|
@ -6920,6 +6919,7 @@ nsLayoutUtils::DrawBackgroundImage(gfxContext& aContext,
|
|||
Filter aGraphicsFilter,
|
||||
const nsRect& aDest,
|
||||
const nsRect& aFill,
|
||||
const nsSize& aRepeatSize,
|
||||
const nsPoint& aAnchor,
|
||||
const nsRect& aDirty,
|
||||
uint32_t aImageFlags,
|
||||
|
@ -6934,9 +6934,29 @@ nsLayoutUtils::DrawBackgroundImage(gfxContext& aContext,
|
|||
|
||||
SVGImageContext svgContext(aImageSize, Nothing());
|
||||
|
||||
return DrawImageInternal(aContext, aPresContext, aImage,
|
||||
aGraphicsFilter, aDest, aFill, aAnchor,
|
||||
aDirty, &svgContext, aImageFlags, aExtendMode);
|
||||
/* Fast path when there is no need for image spacing */
|
||||
if (aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height) {
|
||||
return DrawImageInternal(aContext, aPresContext, aImage,
|
||||
aGraphicsFilter, aDest, aFill, aAnchor,
|
||||
aDirty, &svgContext, aImageFlags, aExtendMode);
|
||||
}
|
||||
|
||||
nsPoint firstTilePos = aDest.TopLeft() +
|
||||
nsPoint(NSToIntFloor(float(aFill.x - aDest.x) / aRepeatSize.width) * aRepeatSize.width,
|
||||
NSToIntFloor(float(aFill.y - aDest.y) / aRepeatSize.height) * aRepeatSize.height);
|
||||
for (int32_t i = firstTilePos.x; i < aFill.XMost(); i += aRepeatSize.width) {
|
||||
for (int32_t j = firstTilePos.y; j < aFill.YMost(); j += aRepeatSize.height) {
|
||||
nsRect dest(i, j, aDest.width, aDest.height);
|
||||
DrawResult result = DrawImageInternal(aContext, aPresContext, aImage, aGraphicsFilter,
|
||||
dest, dest, aAnchor, aDirty, &svgContext,
|
||||
aImageFlags, ExtendMode::CLAMP);
|
||||
if (result != DrawResult::SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DrawResult::SUCCESS;
|
||||
}
|
||||
|
||||
/* static */ DrawResult
|
||||
|
|
|
@ -1735,8 +1735,13 @@ public:
|
|||
* the image is a vector image being rendered at
|
||||
* that size.)
|
||||
* @param aDest The position and scaled area where one copy of
|
||||
* the image should be drawn.
|
||||
* the image should be drawn. This area represents
|
||||
* the image itself in its correct position as defined
|
||||
* with the background-position css property.
|
||||
* @param aFill The area to be filled with copies of the image.
|
||||
* @param aRepeatSize The distance between the positions of two subsequent
|
||||
* repeats of the image. Sizes larger than aDest.Size()
|
||||
* create gaps between the images.
|
||||
* @param aAnchor A point in aFill which we will ensure is
|
||||
* pixel-aligned in the output.
|
||||
* @param aDirty Pixels outside this area may be skipped.
|
||||
|
@ -1750,6 +1755,7 @@ public:
|
|||
Filter aGraphicsFilter,
|
||||
const nsRect& aDest,
|
||||
const nsRect& aFill,
|
||||
const nsSize& aRepeatSize,
|
||||
const nsPoint& aAnchor,
|
||||
const nsRect& aDirty,
|
||||
uint32_t aImageFlags,
|
||||
|
|
Загрузка…
Ссылка в новой задаче