When drawing images, track full source and dest regions (unclipped) or use floats, to avoid rounding problems with advanced image resizing algorithms. b=371434 r=vlad r+sr=roc

git-svn-id: svn://10.0.0.236/trunk@222053 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
dbaron%dbaron.org
2007-03-20 00:58:20 +00:00
parent fea07c7cdc
commit e81955cfa2
23 changed files with 335 additions and 321 deletions

View File

@@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@@ -308,8 +309,10 @@ nsImageFrame::Init(nsIContent* aContent,
}
PRBool
nsImageFrame::RecalculateTransform(imgIContainer* aImage)
nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
{
NS_PRECONDITION(aImage, "null image");
PRBool intrinsicSizeChanged = PR_FALSE;
if (aImage) {
@@ -324,6 +327,12 @@ nsImageFrame::RecalculateTransform(imgIContainer* aImage)
}
}
return intrinsicSizeChanged;
}
void
nsImageFrame::RecalculateTransform()
{
// In any case, we need to translate this over appropriately. Set
// translation _before_ setting scaling so that it does not get
// scaled!
@@ -331,8 +340,9 @@ nsImageFrame::RecalculateTransform(imgIContainer* aImage)
// XXXbz does this introduce rounding errors because of the cast to
// float? Should we just manually add that stuff in every time
// instead?
mTransform.SetToTranslate(float(mBorderPadding.left),
float(mBorderPadding.top - GetContinuationOffset()));
nsRect innerArea = GetInnerArea();
mTransform.SetToTranslate(float(innerArea.x),
float(innerArea.y - GetContinuationOffset()));
// Set the scale factors
if (mIntrinsicSize.width != 0 && mIntrinsicSize.height != 0 &&
@@ -340,8 +350,6 @@ nsImageFrame::RecalculateTransform(imgIContainer* aImage)
mTransform.AddScale(float(mComputedSize.width) / float(mIntrinsicSize.width),
float(mComputedSize.height) / float(mIntrinsicSize.height));
}
return intrinsicSizeChanged;
}
/*
@@ -518,7 +526,7 @@ nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
return NS_OK;
}
RecalculateTransform(aImage);
UpdateIntrinsicSize(aImage);
// Now we need to reflow if we have an unconstrained size and have
// already gotten the initial reflow
@@ -577,6 +585,8 @@ nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
}
}
// XXX We really need to round this out, now that we're doing better
// image scaling!
nsRect r = SourceRectToDest(*aRect);
#ifdef DEBUG_decode
printf("Source rect (%d,%d,%d,%d) -> invalidate dest rect (%d,%d,%d,%d)\n",
@@ -620,7 +630,7 @@ nsImageFrame::OnStopDecode(imgIRequest *aRequest,
nsCOMPtr<imgIContainer> imageContainer;
aRequest->GetImage(getter_AddRefs(imageContainer));
NS_ASSERTION(imageContainer, "Successful load with no container?");
intrinsicSizeChanged = RecalculateTransform(imageContainer);
intrinsicSizeChanged = UpdateIntrinsicSize(imageContainer);
}
else {
// Have to size to 0,0 so that GetDesiredSize recalculates the size
@@ -686,7 +696,7 @@ nsImageFrame::EnsureIntrinsicSize(nsPresContext* aPresContext)
}
if (currentContainer) {
RecalculateTransform(currentContainer);
UpdateIntrinsicSize(currentContainer);
} else {
// image request is null or image size not known, probably an
// invalid image specified
@@ -698,7 +708,6 @@ nsImageFrame::EnsureIntrinsicSize(nsPresContext* aPresContext)
mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+(2*(ICON_PADDING+ALT_BORDER_WIDTH))),
nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+(2*(ICON_PADDING+ALT_BORDER_WIDTH))));
}
RecalculateTransform(nsnull);
}
}
}
@@ -721,36 +730,18 @@ nsImageFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
nsRect
nsImageFrame::GetInnerArea() const
{
nsRect r;
r.x = mBorderPadding.left;
r.y = GetPrevInFlow() ? 0 : mBorderPadding.top;
r.width = mRect.width - mBorderPadding.left - mBorderPadding.right;
r.height = mRect.height -
(GetPrevInFlow() ? 0 : mBorderPadding.top) -
(GetNextInFlow() ? 0 : mBorderPadding.bottom);
return r;
return GetContentRect() - GetPosition();
}
// get the offset into the content area of the image where aImg starts if it is a continuation.
nscoord
nsImageFrame::GetContinuationOffset(nscoord* aWidth) const
nsImageFrame::GetContinuationOffset() const
{
nscoord offset = 0;
if (aWidth) {
*aWidth = 0;
}
if (GetPrevInFlow()) {
for (nsIFrame* prevInFlow = GetPrevInFlow() ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) {
nsRect rect = prevInFlow->GetRect();
if (aWidth) {
*aWidth = rect.width;
}
offset += rect.height;
}
offset -= mBorderPadding.top;
offset = PR_MAX(0, offset);
for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
offset += f->GetContentRect().height;
}
NS_ASSERTION(offset >= 0, "bogus GetContentRect");
return offset;
}
@@ -810,26 +801,21 @@ nsImageFrame::Reflow(nsPresContext* aPresContext,
mState |= IMAGE_GOTINITIALREFLOW;
}
// Set our borderpadding so that if GetDesiredSize has to recalc the
// transform it can.
mBorderPadding = aReflowState.mComputedBorderPadding;
nsSize newSize(aReflowState.ComputedWidth(), aReflowState.mComputedHeight);
if (mComputedSize != newSize) {
mComputedSize = newSize;
RecalculateTransform(nsnull);
}
mComputedSize =
nsSize(aReflowState.ComputedWidth(), aReflowState.mComputedHeight);
RecalculateTransform();
aMetrics.width = mComputedSize.width;
aMetrics.height = mComputedSize.height;
// add borders and padding
aMetrics.width += mBorderPadding.left + mBorderPadding.right;
aMetrics.height += mBorderPadding.top + mBorderPadding.bottom;
aMetrics.width += aReflowState.mComputedBorderPadding.LeftRight();
aMetrics.height += aReflowState.mComputedBorderPadding.TopBottom();
if (GetPrevInFlow()) {
nscoord y = GetContinuationOffset(&aMetrics.width);
aMetrics.height -= y + mBorderPadding.top;
aMetrics.width = GetPrevInFlow()->GetSize().width;
nscoord y = GetContinuationOffset();
aMetrics.height -= y + aReflowState.mComputedBorderPadding.top;
aMetrics.height = PR_MAX(0, aMetrics.height);
}
@@ -1028,6 +1014,7 @@ struct nsRecessedBorder : public nsStyleBorder {
void
nsImageFrame::DisplayAltFeedback(nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
imgIRequest* aRequest,
nsPoint aPt)
{
@@ -1084,11 +1071,10 @@ nsImageFrame::DisplayAltFeedback(nsIRenderingContext& aRenderingContext,
}
if (imgCon) {
// draw it
nsRect source(0,0,size,size);
nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
inner.XMost() - size : inner.x,
inner.y, size, size);
aRenderingContext.DrawImage(imgCon, source, dest);
nsLayoutUtils::DrawImage(&aRenderingContext, imgCon, dest, aDirtyRect);
iconUsed = PR_TRUE;
}
}
@@ -1134,6 +1120,7 @@ static void PaintAltFeedback(nsIFrame* aFrame, nsIRenderingContext* aCtx,
{
nsImageFrame* f = NS_STATIC_CAST(nsImageFrame*, aFrame);
f->DisplayAltFeedback(*aCtx,
aDirtyRect,
IMAGE_OK(f->GetContent()->IntrinsicState(), PR_TRUE)
? nsImageFrame::gIconLoad->mLoadingImage
: nsImageFrame::gIconLoad->mBrokenImage,
@@ -1190,64 +1177,19 @@ nsImageFrame::PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
{
// Render the image into our content area (the area inside
// the borders and padding)
NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
nsRect inner = GetInnerArea() + aPt;
nsRect paintArea(inner);
nsRect clip;
clip.IntersectRect(inner, aDirtyRect);
nscoord offsetY = 0;
nsRect dest(inner.TopLeft(), mComputedSize);
dest.y -= GetContinuationOffset();
// if the image is split account for y-offset
if (GetPrevInFlow()) {
offsetY = GetContinuationOffset();
}
if (mIntrinsicSize == mComputedSize) {
// Find the actual rect to be painted to in the rendering context
paintArea.IntersectRect(paintArea, aDirtyRect);
// Rect in the image to paint
nsRect r(paintArea.x - inner.x,
paintArea.y - inner.y + offsetY,
paintArea.width,
paintArea.height);
aRenderingContext.DrawImage(aImage, r, paintArea);
} else {
// The computed size is the total size of all the continuations,
// including ourselves. Note that we're basically inverting
// mTransform here (would it too much to ask for
// nsTransform2D::Invert?), since we need to convert from
// rendering context coords to image coords...
nsTransform2D trans;
trans.SetToScale((float(mIntrinsicSize.width) / float(mComputedSize.width)),
(float(mIntrinsicSize.height) / float(mComputedSize.height)));
// XXXbz it looks like we should take
// IntersectRect(paintArea, aDirtyRect) here too, but things
// get very weird if I do that ....
// paintArea.IntersectRect(paintArea, aDirtyRect);
// dirty rect in image our coord size...
nsRect r(paintArea.x - inner.x,
paintArea.y - inner.y + offsetY,
paintArea.width,
paintArea.height);
// Transform that to image coords
trans.TransformCoord(&r.x, &r.y, &r.width, &r.height);
#ifdef DEBUG_decode
printf("IF draw src (%d,%d,%d,%d) -> dst (%d,%d,%d,%d)\n",
r.x, r.y, r.width, r.height, paintArea.x, paintArea.y,
paintArea.width, paintArea.height);
#endif
aRenderingContext.DrawImage(aImage, r, paintArea);
}
nsLayoutUtils::DrawImage(&aRenderingContext, aImage, dest, clip);
nsPresContext* presContext = GetPresContext();
nsImageMap* map = GetImageMap(presContext);
if (nsnull != map) {
nsRect inner = GetInnerArea() + aPt;
aRenderingContext.PushState();
aRenderingContext.SetColor(NS_RGB(0, 0, 0));
aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
@@ -1693,6 +1635,19 @@ mRect.height);
}
#endif
PRIntn
nsImageFrame::GetSkipSides() const
{
PRIntn skip = 0;
if (nsnull != GetPrevInFlow()) {
skip |= 1 << NS_SIDE_TOP;
}
if (nsnull != GetNextInFlow()) {
skip |= 1 << NS_SIDE_BOTTOM;
}
return skip;
}
NS_IMETHODIMP
nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
{