From 19414f399825e782dbdfec665e678d406480c125 Mon Sep 17 00:00:00 2001 From: CJKu Date: Tue, 8 Mar 2016 15:54:13 +0800 Subject: [PATCH] Bug 619500: Part 1. Default sizing for specified size of SVG images which have no constraints; r=dholbert r=seth MozReview-Commit-ID: 8DI86w6Ni8T --- image/ClippedImage.cpp | 59 ++++++++++++++++++++++++++-------- image/ClippedImage.h | 10 +++--- image/ImageOps.cpp | 10 +++--- image/ImageOps.h | 15 ++++++--- layout/base/nsCSSRendering.cpp | 26 ++++++++++++--- layout/base/nsCSSRendering.h | 7 +++- 6 files changed, 96 insertions(+), 31 deletions(-) diff --git a/image/ClippedImage.cpp b/image/ClippedImage.cpp index 468eb65d01b3..57da47e9a299 100644 --- a/image/ClippedImage.cpp +++ b/image/ClippedImage.cpp @@ -137,11 +137,18 @@ private: }; ClippedImage::ClippedImage(Image* aImage, - nsIntRect aClip) + nsIntRect aClip, + const Maybe& 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)) && - NS_SUCCEEDED(InnerImage()->GetHeight(&innerSize.height))) { + 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)) && - NS_SUCCEEDED(InnerImage()->GetHeight(&imgHeight))) { + 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); } + + 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) diff --git a/image/ClippedImage.h b/image/ClippedImage.h index 6b16f57496fb..3e947652b026 100644 --- a/image/ClippedImage.h +++ b/image/ClippedImage.h @@ -64,7 +64,8 @@ public: uint32_t aFlags) override; protected: - ClippedImage(Image* aImage, nsIntRect aClip); + ClippedImage(Image* aImage, nsIntRect aClip, + const Maybe& aSVGViewportSize); virtual ~ClippedImage(); @@ -86,9 +87,10 @@ private: // If we are forced to draw a temporary surface, we cache it here. UniquePtr mCachedSurface; - nsIntRect mClip; // The region to clip to. - Maybe mShouldClip; // Memoized ShouldClip() if present. - + nsIntRect mClip; // The region to clip to. + Maybe mShouldClip; // Memoized ShouldClip() if present. + Maybe mSVGViewportSize; // If we're clipping a VectorImage, this + // is the size of viewport of that image. friend class DrawSingleTileCallback; friend class ImageOps; }; diff --git a/image/ImageOps.cpp b/image/ImageOps.cpp index 76a7bb968be0..db00e59e26d8 100644 --- a/image/ImageOps.cpp +++ b/image/ImageOps.cpp @@ -40,17 +40,19 @@ ImageOps::Freeze(imgIContainer* aImage) } /* static */ already_AddRefed -ImageOps::Clip(Image* aImage, nsIntRect aClip) +ImageOps::Clip(Image* aImage, nsIntRect aClip, + const Maybe& aSVGViewportSize) { - RefPtr clippedImage = new ClippedImage(aImage, aClip); + RefPtr clippedImage = new ClippedImage(aImage, aClip, aSVGViewportSize); return clippedImage.forget(); } /* static */ already_AddRefed -ImageOps::Clip(imgIContainer* aImage, nsIntRect aClip) +ImageOps::Clip(imgIContainer* aImage, nsIntRect aClip, + const Maybe& aSVGViewportSize) { nsCOMPtr clippedImage = - new ClippedImage(static_cast(aImage), aClip); + new ClippedImage(static_cast(aImage), aClip, aSVGViewportSize); return clippedImage.forget(); } diff --git a/image/ImageOps.h b/image/ImageOps.h index 9dcfc286f8ce..7a8e19be3441 100644 --- a/image/ImageOps.h +++ b/image/ImageOps.h @@ -40,12 +40,19 @@ public: /** * Creates a clipped version of an existing image. Animation is unaffected. * - * @param aImage The existing image. - * @param aClip The rectangle to clip the image against. + * @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 Clip(Image* aImage, nsIntRect aClip); + static already_AddRefed Clip(Image* aImage, nsIntRect aClip, + const Maybe& aSVGViewportSize = + Nothing()); static already_AddRefed Clip(imgIContainer* aImage, - nsIntRect aClip); + nsIntRect aClip, + const Maybe& aSVGViewportSize = + Nothing()); /** * Creates a version of an existing image which is rotated and/or flipped to diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index a1920c1d0375..80294ea2064a 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -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 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 subImage = ImageOps::Clip(srcImage, actualCropRect); + nsCOMPtr 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& 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 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)) { diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index 4dd06dde7929..179328804572 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -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& aSVGViewportSize); bool IsRasterImage(); bool IsAnimatedImage();