зеркало из https://github.com/mozilla/gecko-dev.git
Bug 624647 part 1: Add utility method nsLayoutUtils::ComputeObjectDestRect() to handle object-fit/object-position. r=seth
This commit is contained in:
Родитель
6ea2fe785e
Коммит
9f4cb9be39
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsCharTraits.h"
|
||||
|
@ -3464,6 +3465,218 @@ nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect,
|
|||
return resultRect;
|
||||
}
|
||||
|
||||
enum ObjectDimensionType { eWidth, eHeight };
|
||||
static nscoord
|
||||
ComputeMissingDimension(const nsSize& aDefaultObjectSize,
|
||||
const nsSize& aIntrinsicRatio,
|
||||
const Maybe<nscoord>& aSpecifiedWidth,
|
||||
const Maybe<nscoord>& aSpecifiedHeight,
|
||||
ObjectDimensionType aDimensionToCompute)
|
||||
{
|
||||
// The "default sizing algorithm" computes the missing dimension as follows:
|
||||
// (source: http://dev.w3.org/csswg/css-images-3/#default-sizing )
|
||||
|
||||
// 1. "If the object has an intrinsic aspect ratio, the missing dimension of
|
||||
// the concrete object size is calculated using the intrinsic aspect
|
||||
// ratio and the present dimension."
|
||||
if (aIntrinsicRatio.width > 0 && aIntrinsicRatio.height > 0) {
|
||||
// Fill in the missing dimension using the intrinsic aspect ratio.
|
||||
nscoord knownDimensionSize;
|
||||
float ratio;
|
||||
if (aDimensionToCompute == eWidth) {
|
||||
knownDimensionSize = *aSpecifiedHeight;
|
||||
ratio = aIntrinsicRatio.width / aIntrinsicRatio.height;
|
||||
} else {
|
||||
knownDimensionSize = *aSpecifiedWidth;
|
||||
ratio = aIntrinsicRatio.height / aIntrinsicRatio.width;
|
||||
}
|
||||
return NSCoordSaturatingNonnegativeMultiply(knownDimensionSize, ratio);
|
||||
}
|
||||
|
||||
// 2. "Otherwise, if the missing dimension is present in the object’s
|
||||
// intrinsic dimensions, [...]"
|
||||
// NOTE: *Skipping* this case, because we already know it's not true -- we're
|
||||
// in this function because the missing dimension is *not* present in
|
||||
// the object's intrinsic dimensions.
|
||||
|
||||
// 3. "Otherwise, the missing dimension of the concrete object size is taken
|
||||
// from the default object size. "
|
||||
return (aDimensionToCompute == eWidth) ?
|
||||
aDefaultObjectSize.width : aDefaultObjectSize.height;
|
||||
}
|
||||
|
||||
/*
|
||||
* This computes & returns the concrete object size of replaced content, if
|
||||
* that content were to be rendered with "object-fit: none". (Or, if the
|
||||
* element has neither an intrinsic height nor width, this method returns an
|
||||
* empty Maybe<> object.)
|
||||
*
|
||||
* As specced...
|
||||
* http://dev.w3.org/csswg/css-images-3/#valdef-object-fit-none
|
||||
* ..we use "the default sizing algorithm with no specified size,
|
||||
* and a default object size equal to the replaced element's used width and
|
||||
* height."
|
||||
*
|
||||
* The default sizing algorithm is described here:
|
||||
* http://dev.w3.org/csswg/css-images-3/#default-sizing
|
||||
* Quotes in the function-impl are taken from that ^ spec-text.
|
||||
*
|
||||
* Per its final bulleted section: since there's no specified size,
|
||||
* we run the default sizing algorithm using the object's intrinsic size in
|
||||
* place of the specified size. But if the object has neither an intrinsic
|
||||
* height nor an intrinsic width, then we instead return without populating our
|
||||
* outparam, and we let the caller figure out the size (using a contain
|
||||
* constraint).
|
||||
*/
|
||||
static Maybe<nsSize>
|
||||
MaybeComputeObjectFitNoneSize(const nsSize& aDefaultObjectSize,
|
||||
const IntrinsicSize& aIntrinsicSize,
|
||||
const nsSize& aIntrinsicRatio)
|
||||
{
|
||||
// "If the object has an intrinsic height or width, its size is resolved as
|
||||
// if its intrinsic dimensions were given as the specified size."
|
||||
//
|
||||
// So, first we check if we have an intrinsic height and/or width:
|
||||
Maybe<nscoord> specifiedWidth;
|
||||
if (aIntrinsicSize.width.GetUnit() == eStyleUnit_Coord) {
|
||||
specifiedWidth.emplace(aIntrinsicSize.width.GetCoordValue());
|
||||
}
|
||||
|
||||
Maybe<nscoord> specifiedHeight;
|
||||
if (aIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
|
||||
specifiedHeight.emplace(aIntrinsicSize.height.GetCoordValue());
|
||||
}
|
||||
|
||||
Maybe<nsSize> noneSize; // (the value we'll return)
|
||||
if (specifiedWidth || specifiedHeight) {
|
||||
// We have at least one specified dimension; use whichever dimension is
|
||||
// specified, and compute the other one using our intrinsic ratio, or (if
|
||||
// no valid ratio) using the default object size.
|
||||
noneSize.emplace();
|
||||
|
||||
noneSize->width = specifiedWidth ?
|
||||
*specifiedWidth :
|
||||
ComputeMissingDimension(aDefaultObjectSize, aIntrinsicRatio,
|
||||
specifiedWidth, specifiedHeight,
|
||||
eWidth);
|
||||
|
||||
noneSize->height = specifiedHeight ?
|
||||
*specifiedHeight :
|
||||
ComputeMissingDimension(aDefaultObjectSize, aIntrinsicRatio,
|
||||
specifiedWidth, specifiedHeight,
|
||||
eHeight);
|
||||
}
|
||||
// [else:] "Otherwise [if there's neither an intrinsic height nor width], its
|
||||
// size is resolved as a contain constraint against the default object size."
|
||||
// We'll let our caller do that, to share code & avoid redundant
|
||||
// computations; so, we return w/out populating noneSize.
|
||||
return noneSize;
|
||||
}
|
||||
|
||||
// Computes the concrete object size to render into, as described at
|
||||
// http://dev.w3.org/csswg/css-images-3/#concrete-size-resolution
|
||||
static nsSize
|
||||
ComputeConcreteObjectSize(const nsSize& aConstraintSize,
|
||||
const IntrinsicSize& aIntrinsicSize,
|
||||
const nsSize& aIntrinsicRatio,
|
||||
uint8_t aObjectFit)
|
||||
{
|
||||
// Handle default behavior (filling the container) w/ fast early return.
|
||||
// (Also: if there's no valid intrinsic ratio, then we have the "fill"
|
||||
// behavior & just use the constraint size.)
|
||||
if (MOZ_LIKELY(aObjectFit == NS_STYLE_OBJECT_FIT_FILL) ||
|
||||
aIntrinsicRatio.width == 0 ||
|
||||
aIntrinsicRatio.height == 0) {
|
||||
return aConstraintSize;
|
||||
}
|
||||
|
||||
// The type of constraint to compute (cover/contain), if needed:
|
||||
Maybe<nsImageRenderer::FitType> fitType;
|
||||
|
||||
Maybe<nsSize> noneSize;
|
||||
if (aObjectFit == NS_STYLE_OBJECT_FIT_NONE ||
|
||||
aObjectFit == NS_STYLE_OBJECT_FIT_SCALE_DOWN) {
|
||||
noneSize = MaybeComputeObjectFitNoneSize(aConstraintSize, aIntrinsicSize,
|
||||
aIntrinsicRatio);
|
||||
if (!noneSize || aObjectFit == NS_STYLE_OBJECT_FIT_SCALE_DOWN) {
|
||||
// Need to compute a 'CONTAIN' constraint (either for the 'none' size
|
||||
// itself, or for comparison w/ the 'none' size to resolve 'scale-down'.)
|
||||
fitType.emplace(nsImageRenderer::CONTAIN);
|
||||
}
|
||||
} else if (aObjectFit == NS_STYLE_OBJECT_FIT_COVER) {
|
||||
fitType.emplace(nsImageRenderer::COVER);
|
||||
} else if (aObjectFit == NS_STYLE_OBJECT_FIT_CONTAIN) {
|
||||
fitType.emplace(nsImageRenderer::CONTAIN);
|
||||
}
|
||||
|
||||
Maybe<nsSize> constrainedSize;
|
||||
if (fitType) {
|
||||
constrainedSize.emplace(
|
||||
nsImageRenderer::ComputeConstrainedSize(aConstraintSize,
|
||||
aIntrinsicRatio,
|
||||
*fitType));
|
||||
}
|
||||
|
||||
// Now, we should have all the sizing information that we need.
|
||||
switch (aObjectFit) {
|
||||
// skipping NS_STYLE_OBJECT_FIT_FILL; we handled it w/ early-return.
|
||||
case NS_STYLE_OBJECT_FIT_CONTAIN:
|
||||
case NS_STYLE_OBJECT_FIT_COVER:
|
||||
MOZ_ASSERT(constrainedSize);
|
||||
return *constrainedSize;
|
||||
|
||||
case NS_STYLE_OBJECT_FIT_NONE:
|
||||
if (noneSize) {
|
||||
return *noneSize;
|
||||
}
|
||||
MOZ_ASSERT(constrainedSize);
|
||||
return *constrainedSize;
|
||||
|
||||
case NS_STYLE_OBJECT_FIT_SCALE_DOWN:
|
||||
MOZ_ASSERT(constrainedSize);
|
||||
if (noneSize) {
|
||||
constrainedSize->width =
|
||||
std::min(constrainedSize->width, noneSize->width);
|
||||
constrainedSize->height =
|
||||
std::min(constrainedSize->height, noneSize->height);
|
||||
}
|
||||
return *constrainedSize;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected enum value for 'object-fit'");
|
||||
return aConstraintSize; // fall back to (default) 'fill' behavior
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ nsRect
|
||||
nsLayoutUtils::ComputeObjectDestRect(const nsRect& aConstraintRect,
|
||||
const IntrinsicSize& aIntrinsicSize,
|
||||
const nsSize& aIntrinsicRatio,
|
||||
const nsStylePosition* aStylePos)
|
||||
{
|
||||
// Step 1: Figure out our "concrete object size"
|
||||
// (the size of the region we'll actually draw our image's pixels into).
|
||||
nsSize concreteObjectSize =
|
||||
ComputeConcreteObjectSize(aConstraintRect.Size(), aIntrinsicSize,
|
||||
aIntrinsicRatio, aStylePos->mObjectFit);
|
||||
|
||||
// Step 2: Figure out how to align that region in the element's content-box.
|
||||
nsPoint imageTopLeftPt, imageAnchorPt;
|
||||
nsImageRenderer::ComputeObjectAnchorPoint(aStylePos->mObjectPosition,
|
||||
aConstraintRect.Size(),
|
||||
concreteObjectSize,
|
||||
&imageTopLeftPt, &imageAnchorPt);
|
||||
// Right now, we're with respect to aConstraintRect's top-left point. We add
|
||||
// that point here, to convert to the same broader coordinate space that
|
||||
// aConstraintRect is in.
|
||||
imageTopLeftPt += aConstraintRect.TopLeft();
|
||||
imageAnchorPt += aConstraintRect.TopLeft();
|
||||
|
||||
// XXXdholbert Per bug 1098417, we should be returning imageAnchorPt here,
|
||||
// and our caller should make sure it's pixel-aligned.
|
||||
return nsRect(imageTopLeftPt, concreteObjectSize);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame,
|
||||
nsFontMetrics** aFontMetrics,
|
||||
|
|
|
@ -117,6 +117,7 @@ class nsLayoutUtils
|
|||
typedef mozilla::dom::DOMRectList DOMRectList;
|
||||
typedef mozilla::layers::Layer Layer;
|
||||
typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
|
||||
typedef mozilla::IntrinsicSize IntrinsicSize;
|
||||
typedef mozilla::gfx::SourceSurface SourceSurface;
|
||||
typedef mozilla::gfx::Color Color;
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
@ -1079,6 +1080,32 @@ public:
|
|||
nsIFrame* aFrame,
|
||||
uint32_t aFlags = 0);
|
||||
|
||||
/**
|
||||
* Computes the destination rect that a given replaced element should render
|
||||
* into, based on its CSS 'object-fit' and 'object-position' properties.
|
||||
*
|
||||
* @param aConstraintRect The constraint rect that we have at our disposal,
|
||||
* which would e.g. be exactly filled by the image
|
||||
* if we had "object-fit: fill".
|
||||
* @param aIntrinsicSize The replaced content's intrinsic size, as reported
|
||||
* by nsIFrame::GetIntrinsicSize().
|
||||
* @param aIntrinsicRatio The replaced content's intrinsic ratio, as reported
|
||||
* by nsIFrame::GetIntrinsicRatio().
|
||||
* @param aStylePos The nsStylePosition struct that contains the 'object-fit'
|
||||
* and 'object-position' values that we should rely on.
|
||||
* (This should usually be the nsStylePosition for the
|
||||
* replaced element in question, but not always. For
|
||||
* example, a <video>'s poster-image has a dedicated
|
||||
* anonymous element & child-frame, but we should still use
|
||||
* the <video>'s 'object-fit' and 'object-position' values.)
|
||||
* @return The nsRect into which we should render the replaced content (using
|
||||
* the same coordinate space as the passed-in aConstraintRect).
|
||||
*/
|
||||
static nsRect ComputeObjectDestRect(const nsRect& aConstraintRect,
|
||||
const IntrinsicSize& aIntrinsicSize,
|
||||
const nsSize& aIntrinsicRatio,
|
||||
const nsStylePosition* aStylePos);
|
||||
|
||||
/**
|
||||
* Get the font metrics corresponding to the frame's style data.
|
||||
* @param aFrame the frame
|
||||
|
|
Загрузка…
Ссылка в новой задаче