Bug 619500: Part 1. Default sizing for specified size of SVG images which have no constraints; r=dholbert r=seth

MozReview-Commit-ID: 8DI86w6Ni8T
This commit is contained in:
CJKu 2016-03-08 15:54:13 +08:00
parent ff383edaf7
commit 19414f3998
6 changed files with 96 additions and 31 deletions

View File

@ -137,11 +137,18 @@ private:
};
ClippedImage::ClippedImage(Image* aImage,
nsIntRect aClip)
nsIntRect aClip,
const Maybe<nsSize>& aSVGViewportSize)
: ImageWrapper(aImage)
, mClip(aClip)
{
MOZ_ASSERT(aImage != nullptr, "ClippedImage requires an existing Image");
MOZ_ASSERT_IF(aSVGViewportSize,
aImage->GetType() == imgIContainer::TYPE_VECTOR);
if (aSVGViewportSize) {
mSVGViewportSize = Some(aSVGViewportSize->ToNearestPixels(
nsPresContext::AppUnitsPerCSSPixel()));
}
}
ClippedImage::~ClippedImage()
@ -162,6 +169,15 @@ ClippedImage::ShouldClip()
// If there's a problem with the inner image we'll let it handle
// everything.
mShouldClip.emplace(false);
} else if (mSVGViewportSize && !mSVGViewportSize->IsEmpty()) {
// Clamp the clipping region to the size of the SVG viewport.
nsIntRect svgViewportRect(nsIntPoint(0,0), *mSVGViewportSize);
mClip = mClip.Intersect(svgViewportRect);
// If the clipping region is the same size as the SVG viewport size
// we don't have to do anything.
mShouldClip.emplace(!mClip.IsEqualInterior(svgViewportRect));
} else if (NS_SUCCEEDED(InnerImage()->GetWidth(&width)) && width > 0 &&
NS_SUCCEEDED(InnerImage()->GetHeight(&height)) && height > 0) {
// Clamp the clipping region to the size of the underlying image.
@ -420,8 +436,19 @@ ClippedImage::DrawSingleTile(gfxContext* aContext,
gfxRect clip(mClip.x, mClip.y, mClip.width, mClip.height);
nsIntSize size(aSize), innerSize(aSize);
if (NS_SUCCEEDED(InnerImage()->GetWidth(&innerSize.width)) &&
bool needScale = false;
if (mSVGViewportSize && !mSVGViewportSize->IsEmpty()) {
innerSize = *mSVGViewportSize;
needScale = true;
} else if (NS_SUCCEEDED(InnerImage()->GetWidth(&innerSize.width)) &&
NS_SUCCEEDED(InnerImage()->GetHeight(&innerSize.height))) {
needScale = true;
} else {
MOZ_ASSERT_UNREACHABLE(
"If ShouldClip() led us to draw then we should never get here");
}
if (needScale) {
double scaleX = aSize.width / clip.width;
double scaleY = aSize.height / clip.height;
@ -429,9 +456,6 @@ ClippedImage::DrawSingleTile(gfxContext* aContext,
clip.Scale(scaleX, scaleY);
size = innerSize;
size.Scale(scaleX, scaleY);
} else {
MOZ_ASSERT(false,
"If ShouldClip() led us to draw then we should never get here");
}
// We restrict our drawing to only the clipping region, and translate so that
@ -478,8 +502,17 @@ ClippedImage::OptimalImageSizeForDest(const gfxSize& aDest,
}
int32_t imgWidth, imgHeight;
if (NS_SUCCEEDED(InnerImage()->GetWidth(&imgWidth)) &&
bool needScale = false;
if (mSVGViewportSize && !mSVGViewportSize->IsEmpty()) {
imgWidth = mSVGViewportSize->width;
imgHeight = mSVGViewportSize->height;
needScale = true;
} else if (NS_SUCCEEDED(InnerImage()->GetWidth(&imgWidth)) &&
NS_SUCCEEDED(InnerImage()->GetHeight(&imgHeight))) {
needScale = true;
}
if (needScale) {
// To avoid ugly sampling artifacts, ClippedImage needs the image size to
// be chosen such that the clipping region lies on pixel boundaries.
@ -501,12 +534,12 @@ ClippedImage::OptimalImageSizeForDest(const gfxSize& aDest,
nsIntSize finalScale(ceil(double(innerDesiredSize.width) / imgWidth),
ceil(double(innerDesiredSize.height) / imgHeight));
return mClip.Size() * finalScale;
} else {
}
MOZ_ASSERT(false,
"If ShouldClip() led us to draw then we should never get here");
return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame, aFilter,
aFlags);
}
}
NS_IMETHODIMP_(nsIntRect)

View File

@ -64,7 +64,8 @@ public:
uint32_t aFlags) override;
protected:
ClippedImage(Image* aImage, nsIntRect aClip);
ClippedImage(Image* aImage, nsIntRect aClip,
const Maybe<nsSize>& aSVGViewportSize);
virtual ~ClippedImage();
@ -88,7 +89,8 @@ private:
nsIntRect mClip; // The region to clip to.
Maybe<bool> mShouldClip; // Memoized ShouldClip() if present.
Maybe<nsIntSize> mSVGViewportSize; // If we're clipping a VectorImage, this
// is the size of viewport of that image.
friend class DrawSingleTileCallback;
friend class ImageOps;
};

View File

@ -40,17 +40,19 @@ ImageOps::Freeze(imgIContainer* aImage)
}
/* static */ already_AddRefed<Image>
ImageOps::Clip(Image* aImage, nsIntRect aClip)
ImageOps::Clip(Image* aImage, nsIntRect aClip,
const Maybe<nsSize>& aSVGViewportSize)
{
RefPtr<Image> clippedImage = new ClippedImage(aImage, aClip);
RefPtr<Image> clippedImage = new ClippedImage(aImage, aClip, aSVGViewportSize);
return clippedImage.forget();
}
/* static */ already_AddRefed<imgIContainer>
ImageOps::Clip(imgIContainer* aImage, nsIntRect aClip)
ImageOps::Clip(imgIContainer* aImage, nsIntRect aClip,
const Maybe<nsSize>& aSVGViewportSize)
{
nsCOMPtr<imgIContainer> clippedImage =
new ClippedImage(static_cast<Image*>(aImage), aClip);
new ClippedImage(static_cast<Image*>(aImage), aClip, aSVGViewportSize);
return clippedImage.forget();
}

View File

@ -42,10 +42,17 @@ public:
*
* @param aImage The existing image.
* @param aClip The rectangle to clip the image against.
* @param aSVGViewportSize The specific viewort size of aImage. Unless aImage
* is a vector image without intrinsic size, this
* argument should be pass as Nothing().
*/
static already_AddRefed<Image> Clip(Image* aImage, nsIntRect aClip);
static already_AddRefed<Image> Clip(Image* aImage, nsIntRect aClip,
const Maybe<nsSize>& aSVGViewportSize =
Nothing());
static already_AddRefed<imgIContainer> Clip(imgIContainer* aImage,
nsIntRect aClip);
nsIntRect aClip,
const Maybe<nsSize>& aSVGViewportSize =
Nothing());
/**
* Creates a version of an existing image which is rotated and/or flipped to

View File

@ -3700,7 +3700,16 @@ DrawBorderImage(nsPresContext* aPresContext,
nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]);
nsRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]);
if (subArea.IsEmpty())
continue;
nsIntRect intSubArea = subArea.ToOutsidePixels(nsPresContext::AppUnitsPerCSSPixel());
// intrinsicSize.CanComputeConcreteSize() return false means we can not
// read intrinsic size from aStyleBorder.mBorderImageSource.
// In this condition, we pass imageSize(a resolved size comes from
// default sizing algorithm) to renderer as the viewport size.
Maybe<nsSize> svgViewportSize = intrinsicSize.CanComputeConcreteSize() ?
Nothing() : Some(imageSize);
result &=
renderer.DrawBorderImageComponent(aPresContext,
@ -3710,7 +3719,8 @@ DrawBorderImage(nsPresContext* aPresContext,
intSubArea.width,
intSubArea.height),
fillStyleH, fillStyleV,
unitSize, j * (RIGHT + 1) + i);
unitSize, j * (RIGHT + 1) + i,
svgViewportSize);
}
}
@ -4785,7 +4795,9 @@ nsImageRenderer::PrepareImage()
// The cropped image is identical to the source image
mImageContainer.swap(srcImage);
} else {
nsCOMPtr<imgIContainer> subImage = ImageOps::Clip(srcImage, actualCropRect);
nsCOMPtr<imgIContainer> subImage = ImageOps::Clip(srcImage,
actualCropRect,
Nothing());
mImageContainer.swap(subImage);
}
}
@ -5254,7 +5266,8 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext,
uint8_t aHFill,
uint8_t aVFill,
const nsSize& aUnitSize,
uint8_t aIndex)
uint8_t aIndex,
const Maybe<nsSize>& aSVGViewportSize)
{
if (!IsReady()) {
NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
@ -5271,7 +5284,7 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext,
nsIntRect srcRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height);
if (mType == eStyleImageType_Image) {
if ((subImage = mImage->GetSubImage(aIndex)) == nullptr) {
subImage = ImageOps::Clip(mImageContainer, srcRect);
subImage = ImageOps::Clip(mImageContainer, srcRect, aSVGViewportSize);
mImage->SetSubImage(aIndex, subImage);
}
} else {
@ -5291,9 +5304,12 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext,
}
nsCOMPtr<imgIContainer> image(ImageOps::CreateFromDrawable(drawable));
subImage = ImageOps::Clip(image, srcRect);
subImage = ImageOps::Clip(image, srcRect, aSVGViewportSize);
}
MOZ_ASSERT_IF(aSVGViewportSize,
subImage->GetType() == imgIContainer::TYPE_VECTOR);
Filter filter = nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {

View File

@ -231,6 +231,10 @@ public:
* aIndex identifies the component: 0 1 2
* 3 4 5
* 6 7 8
* aSVGViewportSize The image size evaluated by default sizing algorithm.
* Pass Nothing() if we can read a valid viewport size or aspect-ratio from
* the drawing image directly, otherwise, pass Some() with viewport size
* evaluated from default sizing algorithm.
*/
DrawResult
DrawBorderImageComponent(nsPresContext* aPresContext,
@ -241,7 +245,8 @@ public:
uint8_t aHFill,
uint8_t aVFill,
const nsSize& aUnitSize,
uint8_t aIndex);
uint8_t aIndex,
const mozilla::Maybe<nsSize>& aSVGViewportSize);
bool IsRasterImage();
bool IsAnimatedImage();