Bug 1228601 - [Part2] Swap width,height if necessary and apply rotation matrix while building layer.; r=mattwoodrow
MozReview-Commit-ID: Jlh6oRa32fj
--- a/dom/html/HTMLVideoElement.cpp
+++ b/dom/html/HTMLVideoElement.cpp
@@ -55,18 +55,31 @@ nsresult HTMLVideoElement::GetVideoSize(
if (!mMediaInfo.HasVideo()) {
return NS_ERROR_FAILURE;
}
if (mDisableVideo) {
return NS_ERROR_FAILURE;
}
- size->height = mMediaInfo.mVideo.mDisplay.height;
- size->width = mMediaInfo.mVideo.mDisplay.width;
+ switch (mMediaInfo.mVideo.mRotation) {
+ case VideoInfo::Rotation::kDegree_90:
+ case VideoInfo::Rotation::kDegree_270: {
+ size->width = mMediaInfo.mVideo.mDisplay.height;
+ size->height = mMediaInfo.mVideo.mDisplay.width;
+ break;
+ }
+ case VideoInfo::Rotation::kDegree_0:
+ case VideoInfo::Rotation::kDegree_180:
+ default: {
+ size->height = mMediaInfo.mVideo.mDisplay.height;
+ size->width = mMediaInfo.mVideo.mDisplay.width;
+ break;
+ }
+ }
return NS_OK;
}
bool
HTMLVideoElement::ParseAttribute(int32_t aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult)
--- a/dom/html/HTMLVideoElement.h
+++ b/dom/html/HTMLVideoElement.h
@@ -71,22 +71,41 @@ public:
void SetHeight(uint32_t aValue, ErrorResult& aRv)
{
SetUnsignedIntAttr(nsGkAtoms::height, aValue, 0, aRv);
}
uint32_t VideoWidth() const
{
- return mMediaInfo.HasVideo() ? mMediaInfo.mVideo.mDisplay.width : 0;
+ if (mMediaInfo.HasVideo()) {
+ if (mMediaInfo.mVideo.mRotation == VideoInfo::Rotation::kDegree_90 ||
+ mMediaInfo.mVideo.mRotation == VideoInfo::Rotation::kDegree_270) {
+ return mMediaInfo.mVideo.mDisplay.height;
+ }
+ return mMediaInfo.mVideo.mDisplay.width;
+ }
+ return 0;
}
uint32_t VideoHeight() const
{
- return mMediaInfo.HasVideo() ? mMediaInfo.mVideo.mDisplay.height : 0;
+ if (mMediaInfo.HasVideo()) {
+ if (mMediaInfo.mVideo.mRotation == VideoInfo::Rotation::kDegree_90 ||
+ mMediaInfo.mVideo.mRotation == VideoInfo::Rotation::kDegree_270) {
+ return mMediaInfo.mVideo.mDisplay.width;
+ }
+ return mMediaInfo.mVideo.mDisplay.height;
+ }
+ return 0;
+ }
+
+ VideoInfo::Rotation RotationDegrees() const
+ {
+ return mMediaInfo.mVideo.mRotation;
}
void GetPoster(nsAString& aValue)
{
GetURIAttr(nsGkAtoms::poster, nullptr, aValue);
}
void SetPoster(const nsAString& aValue, ErrorResult& aRv)
{
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -36,16 +36,49 @@ using namespace mozilla::gfx;
nsIFrame*
NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsVideoFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsVideoFrame)
+// A matrix to obtain a correct-rotated video frame.
+static Matrix
+ComputeRotationMatrix(gfxFloat aRotatedWidth,
+ gfxFloat aRotatedHeight,
+ VideoInfo::Rotation aDegrees)
+{
+ Matrix shiftVideoCenterToOrigin;
+ if (aDegrees == VideoInfo::Rotation::kDegree_90 ||
+ aDegrees == VideoInfo::Rotation::kDegree_270) {
+ shiftVideoCenterToOrigin = Matrix::Translation(-aRotatedHeight / 2.0,
+ -aRotatedWidth / 2.0);
+ } else {
+ shiftVideoCenterToOrigin = Matrix::Translation(-aRotatedWidth / 2.0,
+ -aRotatedHeight / 2.0);
+ }
+
+ Matrix rotation = Matrix::Rotation(gfx::Float(aDegrees / 180.0 * M_PI));
+ Matrix shiftLeftTopToOrigin = Matrix::Translation(aRotatedWidth / 2.0,
+ aRotatedHeight / 2.0);
+ return shiftVideoCenterToOrigin * rotation * shiftLeftTopToOrigin;
+}
+
+static void
+SwapScaleWidthHeightForRotation(IntSize& aSize, VideoInfo::Rotation aDegrees)
+{
+ if (aDegrees == VideoInfo::Rotation::kDegree_90 ||
+ aDegrees == VideoInfo::Rotation::kDegree_270) {
+ int32_t tmpWidth = aSize.width;
+ aSize.width = aSize.height;
+ aSize.height = tmpWidth;
+ }
+}
+
nsVideoFrame::nsVideoFrame(nsStyleContext* aContext)
: nsContainerFrame(aContext)
{
EnableVisibilityTracking();
}
nsVideoFrame::~nsVideoFrame()
{
@@ -167,17 +200,17 @@ nsVideoFrame::BuildLayer(nsDisplayListBu
if (NS_FAILED(element->GetVideoSize(&videoSizeInPx)) ||
area.IsEmpty()) {
return nullptr;
}
RefPtr<ImageContainer> container = element->GetImageContainer();
if (!container)
return nullptr;
-
+
// Retrieve the size of the decoded video frame, before being scaled
// by pixel aspect ratio.
mozilla::gfx::IntSize frameSize = container->GetCurrentSize();
if (frameSize.width == 0 || frameSize.height == 0) {
// No image, or zero-sized image. No point creating a layer.
return nullptr;
}
@@ -195,33 +228,43 @@ nsVideoFrame::BuildLayer(nsDisplayListBu
aspectRatio,
StylePosition());
gfxRect destGFXRect = PresContext()->AppUnitsToGfxUnits(dest);
destGFXRect.Round();
if (destGFXRect.IsEmpty()) {
return nullptr;
}
+
+ VideoInfo::Rotation rotationDeg = element->RotationDegrees();
IntSize scaleHint(static_cast<int32_t>(destGFXRect.Width()),
static_cast<int32_t>(destGFXRect.Height()));
+ // scaleHint is set regardless of rotation, so swap w/h if needed.
+ SwapScaleWidthHeightForRotation(scaleHint, rotationDeg);
container->SetScaleHint(scaleHint);
RefPtr<ImageLayer> layer = static_cast<ImageLayer*>
(aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
if (!layer) {
layer = aManager->CreateImageLayer();
if (!layer)
return nullptr;
}
layer->SetContainer(container);
layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
// Set a transform on the layer to draw the video in the right place
gfxPoint p = destGFXRect.TopLeft() + aContainerParameters.mOffset;
- Matrix transform = Matrix::Translation(p.x, p.y);
+
+ Matrix preTransform = ComputeRotationMatrix(destGFXRect.Width(),
+ destGFXRect.Height(),
+ rotationDeg);
+
+ Matrix transform = preTransform * Matrix::Translation(p.x, p.y);
+
layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
layer->SetScaleToSize(scaleHint, ScaleMode::STRETCH);
RefPtr<Layer> result = layer.forget();
return result.forget();
}
class DispatchResizeToControls : public Runnable
{
@@ -351,17 +394,17 @@ public:
{
MOZ_COUNT_CTOR(nsDisplayVideo);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayVideo() {
MOZ_COUNT_DTOR(nsDisplayVideo);
}
#endif
-
+
NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO)
// It would be great if we could override GetOpaqueRegion to return nonempty here,
// but it's probably not safe to do so in general. Video frames are
// updated asynchronously from decoder threads, and it's possible that
// we might have an opaque video frame when GetOpaqueRegion is called, but
// when we come to paint, the video frame is transparent or has gone
// away completely (e.g. because of a decoder error). The problem would