Bug 449142. Scale video so that it fits in the content-box of the element and has the same aspect ratio as the video source. Also adds some Ogg video reftests. r=doublec,sr=dbaron
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 29 Dec 2008 21:20:27 +1300
changeset 23156 cc039e5c24ce5d9d1b3a77a8cdc496a34cc1a692
parent 23155 abef302b61be021d31d810c279ba3351e7cd62c3
child 23157 cfa7563faecbebc85b4db2dc27135a3e820a0586
push idunknown
push userunknown
push dateunknown
reviewersdoublec, dbaron
bugs449142
milestone1.9.2a1pre
Bug 449142. Scale video so that it fits in the content-box of the element and has the same aspect ratio as the video source. Also adds some Ogg video reftests. r=doublec,sr=dbaron
content/media/video/src/nsMediaDecoder.cpp
layout/generic/nsVideoFrame.cpp
layout/reftests/ogg-video/aspect-ratio-1-ref.html
layout/reftests/ogg-video/aspect-ratio-1a.html
layout/reftests/ogg-video/aspect-ratio-1b.html
layout/reftests/ogg-video/aspect-ratio-2-ref.html
layout/reftests/ogg-video/aspect-ratio-2a.html
layout/reftests/ogg-video/aspect-ratio-2b.html
layout/reftests/ogg-video/basic-1-ref.html
layout/reftests/ogg-video/basic-1.html
layout/reftests/ogg-video/black140x100.ogv
layout/reftests/ogg-video/empty-1-ref.html
layout/reftests/ogg-video/empty-1a.html
layout/reftests/ogg-video/empty-1b.html
layout/reftests/ogg-video/reftest.list
layout/reftests/ogg-video/zoomed-1-ref.html
layout/reftests/ogg-video/zoomed-1.html
layout/reftests/reftest.list
--- a/content/media/video/src/nsMediaDecoder.cpp
+++ b/content/media/video/src/nsMediaDecoder.cpp
@@ -200,16 +200,19 @@ void nsMediaDecoder::Paint(gfxContext* a
     return;
 
   nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
   if (!pat)
     return;
 
   // Make the source image fill the rectangle completely
   pat->SetMatrix(gfxMatrix().Scale(mRGBWidth/aRect.Width(), mRGBHeight/aRect.Height()));
+  // Set PAD mode so that when the video is being scaled, we do not sample
+  // outside the bounds of the video image.
+  pat->SetExtend(gfxPattern::EXTEND_PAD);
 
   /* Draw RGB surface onto frame */
   aContext->NewPath();
   aContext->PixelSnappedRectangleAndSetPattern(aRect, pat);
   aContext->Fill();
 
 #ifdef DEBUG_FRAME_RATE
   {
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -118,32 +118,50 @@ nsVideoFrame::Destroy()
 }
 
 PRBool
 nsVideoFrame::IsLeaf() const
 {
   return PR_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 =
+    PR_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);
+}
+
 void
 nsVideoFrame::PaintVideo(nsIRenderingContext& aRenderingContext,
                          const nsRect& aDirtyRect, nsPoint aPt) 
 {
+  nsRect area = GetContentRect() - GetPosition() + aPt;
+  nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
+  nsIntSize videoSize = element->GetVideoSize(nsIntSize(0, 0));
+  if (videoSize.width <= 0 || videoSize.height <= 0 || area.IsEmpty())
+    return;
+
   gfxContext* ctx = static_cast<gfxContext*>(aRenderingContext.GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
-  // TODO: handle the situation where the frame size is not the same as the
-  // video size, by drawing to the largest rectangle that fits in the frame
-  // whose aspect ratio equals the video's aspect ratio
-  nsRect area = GetContentRect() - GetPosition() + aPt;
   nsPresContext* presContext = PresContext();
   gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x), 
                       presContext->AppUnitsToGfxUnits(area.y), 
                       presContext->AppUnitsToGfxUnits(area.width), 
                       presContext->AppUnitsToGfxUnits(area.height));
 
-  nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
+  r = CorrectForAspectRatio(r, videoSize);
   element->Paint(ctx, r);
 }
 
 NS_IMETHODIMP
 nsVideoFrame::Reflow(nsPresContext*           aPresContext,
                      nsHTMLReflowMetrics&     aMetrics,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/aspect-ratio-1-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body style="background:white;">
+<div style="width:140px; height:100px; position:relative; left:100px; top:100px; background:black;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/aspect-ratio-1a.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body style="background:white;">
+<video id="v" src="black140x100.ogv"
+       style="width:340px; height:100px; position:relative; top:100px;"
+       onloadeddata="document.documentElement.removeAttribute('class')"></video>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/aspect-ratio-1b.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body style="background:white;">
+<video id="v" src="black140x100.ogv"
+       style="width:140px; height:300px; position:relative; left:100px;"
+       onloadeddata="document.documentElement.removeAttribute('class')"></video>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/aspect-ratio-2-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body style="background:white;">
+<div style="width:280px; height:200px; position:relative; left:200px; top:200px; background:black;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/aspect-ratio-2a.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body style="background:white;">
+<video id="v" src="black140x100.ogv"
+       style="width:680px; height:200px; position:relative; top:200px;"
+       onloadeddata="document.documentElement.removeAttribute('class')"></video>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/aspect-ratio-2b.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body style="background:white;">
+<video id="v" src="black140x100.ogv"
+       style="width:280px; height:600px; position:relative; left:200px;"
+       onloadeddata="document.documentElement.removeAttribute('class')"></video>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/basic-1-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body style="background:white;">
+<div style="width:140px; height:100px; background:black;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/basic-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body style="background:white;">
+<video id="v" src="black140x100.ogv"
+       onloadeddata="document.documentElement.removeAttribute('class')"></video>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ab146ebe285f56fb04cf1affc3f2fd7dc8bc11b9
GIT binary patch
literal 2871
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
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/empty-1-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html>
+<body style="background:white;">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/empty-1a.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body style="background:white;">
+<video id="v" src="black140x100.ogv" style="width:0"
+       onloadeddata="document.documentElement.removeAttribute('class')"></video>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/empty-1b.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body style="background:white;">
+<video id="v" src="black140x100.ogv" style="height:0"
+       onloadeddata="document.documentElement.removeAttribute('class')"></video>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/reftest.list
@@ -0,0 +1,8 @@
+== aspect-ratio-1a.html aspect-ratio-1-ref.html
+== aspect-ratio-1b.html aspect-ratio-1-ref.html
+== aspect-ratio-2a.html aspect-ratio-2-ref.html
+== aspect-ratio-2b.html aspect-ratio-2-ref.html
+== basic-1.html basic-1-ref.html
+== empty-1a.html empty-1-ref.html
+== empty-1b.html empty-1-ref.html
+== zoomed-1.html zoomed-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/zoomed-1-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body style="background:white; margin:0;">
+<div style="width:210px; height:150px; background:black;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ogg-video/zoomed-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait" reftest-zoom="1.5">
+<body style="background:white; margin:0">
+<video id="v" src="black140x100.ogv"
+       onloadeddata="document.documentElement.removeAttribute('class')"></video>
+</body>
+</html>
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -78,16 +78,19 @@ include line-breaking/reftest.list
 include mathml/reftest.list
 
 # native-theme/
 include native-theme/reftest.list
 
 # object/
 include object/reftest.list
 
+# ogg-video/
+include ogg-video/reftest.list
+
 # percent-overflow-sizing/
 include percent-overflow-sizing/reftest.list
 
 # pixel-rounding/
 include pixel-rounding/reftest.list
 
 # printing
 include printing/reftest.list