Bug 1151359 (Part 1) - Predict the size of nsImageFrame images before drawing. r=tn

This commit is contained in:
Seth Fowler 2015-07-19 19:30:35 -07:00
parent 7b3c3fd488
commit faeeebcacd
4 changed files with 118 additions and 17 deletions

View File

@ -1512,6 +1512,15 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage)
nsIDocument* doc = GetOurCurrentDoc();
if (doc && (mFrameCreateCalled || GetOurPrimaryFrame()) &&
(mVisibleCount > 0)) {
if (mVisibleCount == 1) {
// Since we're becoming visible, request a decode.
nsImageFrame* f = do_QueryFrame(GetOurPrimaryFrame());
if (f) {
f->MaybeDecodeForPredictedSize();
}
}
if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
mCurrentRequestFlags |= REQUEST_IS_TRACKED;
doc->AddImage(mCurrentRequest);

View File

@ -373,6 +373,10 @@ struct RenderTargetPixel {
* generally be represented in ScreenPixel units.
*/
struct ScreenPixel {
static nsIntSize ToUntyped(const ScreenIntSize& aSize) {
return nsIntSize(aSize.width, aSize.height);
}
static ScreenIntPoint FromUntyped(const nsIntPoint& aPoint) {
return ScreenIntPoint(aPoint.x, aPoint.y);
}

View File

@ -577,6 +577,10 @@ nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
}
} else {
// We've already gotten the initial reflow, and our size hasn't changed,
// so we're ready to request a decode.
MaybeDecodeForPredictedSize();
}
}
@ -682,12 +686,93 @@ nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
}
} else {
// We've already gotten the initial reflow, and our size hasn't changed,
// so we're ready to request a decode.
MaybeDecodeForPredictedSize();
}
// Update border+content to account for image change
InvalidateFrame();
}
}
void
nsImageFrame::MaybeDecodeForPredictedSize()
{
// Check that we're ready to decode.
if (!mImage) {
return; // Nothing to do yet.
}
if (mComputedSize.IsEmpty()) {
return; // We won't draw anything, so no point in decoding.
}
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
MOZ_ASSERT(imageLoader);
if (imageLoader->GetVisibleCount() == 0) {
return; // We're not visible, so don't decode.
}
// OK, we're ready to decode. Compute the scale to the screen...
nsIPresShell* presShell = PresContext()->GetPresShell();
LayoutDeviceToScreenScale2D resolutionToScreen(
presShell->GetCumulativeResolution()
* nsLayoutUtils::GetTransformToAncestorScale(this));
// ...and this frame's content box...
const nsPoint offset =
GetOffsetToCrossDoc(nsLayoutUtils::GetReferenceFrame(this));
const nsRect frameContentBox = GetInnerArea() + offset;
// ...and our predicted dest rect...
const int32_t factor = PresContext()->AppUnitsPerDevPixel();
const LayoutDeviceRect destRect =
LayoutDeviceRect::FromAppUnits(PredictedDestRect(frameContentBox), factor);
// ...and use them to compute our predicted size in screen pixels.
const ScreenSize predictedScreenSize = destRect.Size() * resolutionToScreen;
const ScreenIntSize predictedScreenIntSize = RoundedToInt(predictedScreenSize);
if (predictedScreenIntSize.IsEmpty()) {
return;
}
// Determine the optimal image size to use.
uint32_t flags = imgIContainer::FLAG_HIGH_QUALITY_SCALING
| imgIContainer::FLAG_ASYNC_NOTIFY;
GraphicsFilter filter = nsLayoutUtils::GetGraphicsFilterForFrame(this);
gfxSize gfxPredictedScreenSize = gfxSize(predictedScreenIntSize.width,
predictedScreenIntSize.height);
nsIntSize predictedImageSize =
mImage->OptimalImageSizeForDest(gfxPredictedScreenSize,
imgIContainer::FRAME_CURRENT,
filter, flags);
// Request a decode.
mImage->RequestDecodeForSize(predictedImageSize, flags);
}
nsRect
nsImageFrame::PredictedDestRect(const nsRect& aFrameContentBox)
{
// What is the rect painted by the image? It's the image's "dest rect" (the
// rect where a full copy of the image is mapped), clipped to the container's
// content box. So, we intersect those rects.
// Note: To get the "dest rect", we have to provide the "constraint rect"
// (which is the content-box, with the effects of fragmentation undone).
nsRect constraintRect(aFrameContentBox.TopLeft(), mComputedSize);
constraintRect.y -= GetContinuationOffset();
const nsRect destRect =
nsLayoutUtils::ComputeObjectDestRect(constraintRect,
mIntrinsicSize,
mIntrinsicRatio,
StylePosition());
return destRect.Intersect(aFrameContentBox);
}
void
nsImageFrame::EnsureIntrinsicSizeAndRatio()
{
@ -935,6 +1020,10 @@ nsImageFrame::Reflow(nsPresContext* aPresContext,
static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
nsRect& visualOverflow = aMetrics.VisualOverflow();
visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
} else {
// We've just reflowed and we should have an accurate size, so we're ready
// to request a decode.
MaybeDecodeForPredictedSize();
}
FinishAndStoreOverflow(&aMetrics);
@ -1475,30 +1564,14 @@ nsDisplayImage::GetContainer(LayerManager* aManager,
nsRect
nsDisplayImage::GetDestRect(bool* aSnap)
{
// OK, we want to return the entire region painted by the image. But what is
// that region? It's the image's "dest rect" (the rect where a full copy of
// the image is mapped), clipped to the container's content box (which is what
// GetBounds() returns). So, we grab those rects and intersect them.
bool snap = true;
const nsRect frameContentBox = GetBounds(&snap);
if (aSnap) {
*aSnap = snap;
}
// Note: To get the "dest rect", we have to provide the "constraint rect"
// (which is the content-box, with the effects of fragmentation undone).
nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
nsRect constraintRect(frameContentBox.TopLeft(),
imageFrame->mComputedSize);
constraintRect.y -= imageFrame->GetContinuationOffset();
const nsRect destRect =
nsLayoutUtils::ComputeObjectDestRect(constraintRect,
imageFrame->mIntrinsicSize,
imageFrame->mIntrinsicRatio,
imageFrame->StylePosition());
return destRect.Intersect(frameContentBox);
return imageFrame->PredictedDestRect(frameContentBox);
}
LayerState

View File

@ -219,6 +219,14 @@ protected:
const nsRect& aDirtyRect, imgIContainer* aImage,
uint32_t aFlags);
/**
* If we're ready to decode - that is, if our current request's image is
* available and our decoding heuristics are satisfied - then trigger a decode
* for our image at the size we predict it will be drawn next time it's
* painted.
*/
void MaybeDecodeForPredictedSize();
protected:
friend class nsImageListener;
friend class nsImageLoadingContent;
@ -235,6 +243,13 @@ protected:
/// Always sync decode our image when painting if @aForce is true.
void SetForceSyncDecoding(bool aForce) { mForceSyncDecoding = aForce; }
/**
* Computes the predicted dest rect that we'll draw into, in app units, based
* upon the provided frame content box. (The content box is what
* nsDisplayImage::GetBounds() returns.)
*/
nsRect PredictedDestRect(const nsRect& aFrameContentBox);
private:
// random helpers
inline void SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,