Bug 1228601 - [Part2] Swap width,height if necessary and apply rotation matrix while building layer.; r=mattwoodrow draft
authorKilik Kuo <kikuo@mozilla.com>
Thu, 12 May 2016 01:54:52 +0800
changeset 372687 7e64145e6101541690520a756ae6498ded52eebc
parent 372686 a9b294ee62b38bb950aa6a259b1874224e560c1c
child 372688 500fe45335a5346ba7934ec25abcf04fb4dbee9f
push id19561
push userkikuo@mozilla.com
push dateMon, 30 May 2016 03:00:41 +0000
reviewersmattwoodrow
bugs1228601
milestone49.0a1
Bug 1228601 - [Part2] Swap width,height if necessary and apply rotation matrix while building layer.; r=mattwoodrow MozReview-Commit-ID: Jlh6oRa32fj
dom/html/HTMLVideoElement.cpp
dom/html/HTMLVideoElement.h
layout/generic/nsVideoFrame.cpp
--- 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