Bug 624647 part 2: Honor "object-fit" & "object-position" in nsImageFrame, nsSubDocumentFrame, & nsVideoFrame. r=roc

This commit is contained in:
Daniel Holbert 2014-11-14 16:45:24 -08:00
parent 779bb4b2dd
commit da820542cf
4 changed files with 113 additions and 80 deletions

View File

@ -347,25 +347,36 @@ nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
bool
nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
{
// Set the translation components.
// First, figure out destRect (the rect we're rendering into).
// NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
// because GetInnerArea() might be smaller if we're fragmented, whereas
// mComputedSize has our full content-box size (which we need for
// ComputeObjectDestRect to work correctly).
nsRect constraintRect(GetInnerArea().TopLeft(), mComputedSize);
constraintRect.y -= GetContinuationOffset();
nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
mIntrinsicSize,
mIntrinsicRatio,
StylePosition());
// Set the translation components, based on destRect
// XXXbz does this introduce rounding errors because of the cast to
// float? Should we just manually add that stuff in every time
// instead?
nsRect innerArea = GetInnerArea();
aTransform.SetToTranslate(float(innerArea.x),
float(innerArea.y - GetContinuationOffset()));
aTransform.SetToTranslate(float(destRect.x),
float(destRect.y));
// Set the scale factors.
// Set the scale factors, based on destRect and intrinsic size.
if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
mIntrinsicSize.width.GetCoordValue() != 0 &&
mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
mIntrinsicSize.height.GetCoordValue() != 0 &&
mIntrinsicSize.width.GetCoordValue() != mComputedSize.width &&
mIntrinsicSize.height.GetCoordValue() != mComputedSize.height) {
mIntrinsicSize.width.GetCoordValue() != destRect.width &&
mIntrinsicSize.height.GetCoordValue() != destRect.height) {
aTransform.SetScale(float(mComputedSize.width) /
aTransform.SetScale(float(destRect.width) /
float(mIntrinsicSize.width.GetCoordValue()),
float(mComputedSize.height) /
float(destRect.height) /
float(mIntrinsicSize.height.GetCoordValue()));
return true;
}
@ -1489,9 +1500,18 @@ nsImageFrame::PaintImage(nsRenderingContext& 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 dest(inner.TopLeft(), mComputedSize);
dest.y -= GetContinuationOffset();
// NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
// because GetInnerArea() might be smaller if we're fragmented, whereas
// mComputedSize has our full content-box size (which we need for
// ComputeObjectDestRect to work correctly).
nsRect constraintRect(aPt + GetInnerArea().TopLeft(), mComputedSize);
constraintRect.y -= GetContinuationOffset();
nsRect dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
mIntrinsicSize,
mIntrinsicRatio,
StylePosition());
nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
PresContext(), aImage,
@ -1501,7 +1521,7 @@ nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
nsImageMap* map = GetImageMap();
if (map) {
gfxPoint devPixelOffset =
nsLayoutUtils::PointToGfxPoint(inner.TopLeft(),
nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
PresContext()->AppUnitsPerDevPixel());
AutoRestoreTransform autoRestoreTransform(drawTarget);
drawTarget->SetTransform(

View File

@ -273,6 +273,18 @@ nsSubDocumentFrame::GetSubdocumentSize()
} else {
docSizeAppUnits = GetContentRect().Size();
}
// Adjust subdocument size, according to 'object-fit' and the
// subdocument's intrinsic size and ratio.
nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
if (subDocRoot) {
nsRect destRect =
nsLayoutUtils::ComputeObjectDestRect(nsRect(nsPoint(), docSizeAppUnits),
subDocRoot->GetIntrinsicSize(),
subDocRoot->GetIntrinsicRatio(),
StylePosition());
docSizeAppUnits = destRect.Size();
}
return nsIntSize(presContext->AppUnitsToDevPixels(docSizeAppUnits.width),
presContext->AppUnitsToDevPixels(docSizeAppUnits.height));
}
@ -756,14 +768,27 @@ nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
nsPoint offset = nsPoint(aReflowState.ComputedPhysicalBorderPadding().left,
aReflowState.ComputedPhysicalBorderPadding().top);
nsSize innerSize(aDesiredSize.Width(), aDesiredSize.Height());
innerSize.width -= aReflowState.ComputedPhysicalBorderPadding().LeftRight();
innerSize.height -= aReflowState.ComputedPhysicalBorderPadding().TopBottom();
if (mInnerView) {
const nsMargin& bp = aReflowState.ComputedPhysicalBorderPadding();
nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(),
aDesiredSize.Height() - bp.TopBottom());
// Size & position the view according to 'object-fit' & 'object-position'.
nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
IntrinsicSize intrinsSize;
nsSize intrinsRatio;
if (subDocRoot) {
intrinsSize = subDocRoot->GetIntrinsicSize();
intrinsRatio = subDocRoot->GetIntrinsicRatio();
}
nsRect destRect =
nsLayoutUtils::ComputeObjectDestRect(nsRect(offset, innerSize),
intrinsSize, intrinsRatio,
StylePosition());
nsViewManager* vm = mInnerView->GetViewManager();
vm->MoveViewTo(mInnerView, offset.x, offset.y);
vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), innerSize), true);
vm->MoveViewTo(mInnerView, destRect.x, destRect.y);
vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true);
}
aDesiredSize.SetOverflowAreasToDesiredBounds();

View File

@ -153,32 +153,18 @@ nsVideoFrame::IsLeaf() const
return true;
}
// Return the largest rectangle that fits in aRect and has the
// same aspect ratio as aRatio, centered at the center of aRect
static gfxRect
CorrectForAspectRatio(const gfxRect& aRect, const nsIntSize& aRatio)
{
NS_ASSERTION(aRatio.width > 0 && aRatio.height > 0 && !aRect.IsEmpty(),
"Nothing to draw");
// Choose scale factor that scales aRatio to just fit into aRect
gfxFloat scale =
std::min(aRect.Width()/aRatio.width, aRect.Height()/aRatio.height);
gfxSize scaledRatio(scale*aRatio.width, scale*aRatio.height);
gfxPoint topLeft((aRect.Width() - scaledRatio.width)/2,
(aRect.Height() - scaledRatio.height)/2);
return gfxRect(aRect.TopLeft() + topLeft, scaledRatio);
}
already_AddRefed<Layer>
nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsDisplayItem* aItem,
const ContainerLayerParameters& aContainerParameters)
{
nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
nsSize contentBoxSize = GetContentRectRelativeToSelf().Size();
HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
nsIntSize videoSize;
if (NS_FAILED(element->GetVideoSize(&videoSize)) || area.IsEmpty()) {
nsIntSize videoSizeInPx;
if (NS_FAILED(element->GetVideoSize(&videoSizeInPx)) ||
contentBoxSize.IsEmpty()) {
return nullptr;
}
@ -194,21 +180,31 @@ nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
return nullptr;
}
// Compute the rectangle in which to paint the video. We need to use
// the largest rectangle that fills our content-box and has the
// correct aspect ratio.
nsPresContext* presContext = PresContext();
gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
presContext->AppUnitsToGfxUnits(area.y),
presContext->AppUnitsToGfxUnits(area.width),
presContext->AppUnitsToGfxUnits(area.height));
r = CorrectForAspectRatio(r, videoSize);
r.Round();
if (r.IsEmpty()) {
// Convert video size from pixel units into app units, to get an aspect-ratio
// (which has to be represented as a nsSize) and an IntrinsicSize that we
// can pass to ComputeObjectRenderRect.
nsSize aspectRatio(nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.width),
nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.height));
IntrinsicSize intrinsicSize;
intrinsicSize.width.SetCoordValue(aspectRatio.width);
intrinsicSize.height.SetCoordValue(aspectRatio.height);
nsRect contentBoxRect(
GetContentRectRelativeToSelf().TopLeft() + aItem->ToReferenceFrame(),
contentBoxSize);
nsRect dest = nsLayoutUtils::ComputeObjectDestRect(contentBoxRect,
intrinsicSize,
aspectRatio,
StylePosition());
gfxRect destGFXRect = PresContext()->AppUnitsToGfxUnits(dest);
destGFXRect.Round();
if (destGFXRect.IsEmpty()) {
return nullptr;
}
IntSize scaleHint(static_cast<int32_t>(r.Width()),
static_cast<int32_t>(r.Height()));
IntSize scaleHint(static_cast<int32_t>(destGFXRect.Width()),
static_cast<int32_t>(destGFXRect.Height()));
container->SetScaleHint(scaleHint);
nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
@ -222,10 +218,10 @@ nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
layer->SetContainer(container);
layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
// Set a transform on the layer to draw the video in the right place
gfxPoint p = r.TopLeft() + aContainerParameters.mOffset;
gfxPoint p = destGFXRect.TopLeft() + aContainerParameters.mOffset;
Matrix transform = Matrix::Translation(p.x, p.y);
layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
layer->SetScaleToSize(IntSize(r.width, r.height), ScaleMode::STRETCH);
layer->SetScaleToSize(scaleHint, ScaleMode::STRETCH);
nsRefPtr<Layer> result = layer.forget();
return result.forget();
}
@ -287,35 +283,20 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
aMetrics.Width(),
aMetrics.Height());
uint32_t posterHeight, posterWidth;
nsSize scaledPosterSize(0, 0);
nsSize computedArea(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
nsPoint posterTopLeft(0, 0);
nsCOMPtr<nsIDOMHTMLImageElement> posterImage = do_QueryInterface(mPosterImage);
if (!posterImage) {
return;
nsRect posterRenderRect;
if (ShouldDisplayPoster()) {
posterRenderRect =
nsRect(nsPoint(mBorderPadding.left, mBorderPadding.top),
nsSize(aReflowState.ComputedWidth(),
aReflowState.ComputedHeight()));
}
posterImage->GetNaturalHeight(&posterHeight);
posterImage->GetNaturalWidth(&posterWidth);
if (ShouldDisplayPoster() && posterHeight && posterWidth) {
gfxFloat scale =
std::min(static_cast<float>(computedArea.width)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterWidth)),
static_cast<float>(computedArea.height)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterHeight)));
gfxSize scaledRatio = gfxSize(scale*posterWidth, scale*posterHeight);
scaledPosterSize.width = nsPresContext::CSSPixelsToAppUnits(static_cast<float>(scaledRatio.width));
scaledPosterSize.height = nsPresContext::CSSPixelsToAppUnits(static_cast<int32_t>(scaledRatio.height));
}
kidReflowState.SetComputedWidth(scaledPosterSize.width);
kidReflowState.SetComputedHeight(scaledPosterSize.height);
posterTopLeft.x = ((computedArea.width - scaledPosterSize.width) / 2) + mBorderPadding.left;
posterTopLeft.y = ((computedArea.height - scaledPosterSize.height) / 2) + mBorderPadding.top;
kidReflowState.SetComputedWidth(posterRenderRect.width);
kidReflowState.SetComputedHeight(posterRenderRect.height);
ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState,
posterTopLeft.x, posterTopLeft.y, 0, aStatus);
FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowState,
posterTopLeft.x, posterTopLeft.y, 0);
posterRenderRect.x, posterRenderRect.y, 0, aStatus);
FinishReflowChild(imageFrame, aPresContext,
kidDesiredSize, &kidReflowState,
posterRenderRect.x, posterRenderRect.y, 0);
} else if (child->GetContent() == mVideoControls) {
// Reflow the video controls frame.
nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext);

View File

@ -720,6 +720,13 @@ video {
object-fit: contain;
}
video > img:-moz-native-anonymous {
/* Video poster images should render with the video element's "object-fit" &
"object-position" properties */
object-fit: inherit !important;
object-position: inherit !important;
}
audio:not([controls]) {
display: none;
}