Bug 1372118 - Part4. Support canvas and video. draft
authorEthan Lin <ethlin@mozilla.com>
Mon, 26 Jun 2017 22:55:29 -0700
changeset 600610 a9416c994f0897bd1fff29e36738108e377c9802
parent 600609 05e57b81674df475395a50f84a67a13d22a43fa0
child 600611 7c18e960be81441388af734c2e112afe34672c6f
push id65803
push userbmo:ethlin@mozilla.com
push dateTue, 27 Jun 2017 16:20:34 +0000
bugs1372118
milestone56.0a1
Bug 1372118 - Part4. Support canvas and video. MozReview-Commit-ID: 79JNEEZaPXU
gfx/layers/wr/WebRenderLayerManager.h
layout/generic/nsHTMLCanvasFrame.cpp
layout/generic/nsHTMLCanvasFrame.h
layout/generic/nsVideoFrame.cpp
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -59,16 +59,17 @@ public:
   }
 
   enum { ALLOW_MEMMOVE = true };
 
   FrameDisplayItemKey mKey;
 };
 
 class WMImageData;
+class WMCanvasData;
 
 class WMData
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(WMData)
 
   virtual WMAnimationData* AsAnimationData() { return nullptr; }
   virtual WMImageData* AsImageData() { return nullptr; }
@@ -96,16 +97,36 @@ public:
   wr::MaybeExternalImageId mExternalImageId;
   Maybe<wr::ImageKey> mKey;
   RefPtr<ImageClient> mImageClient;
   CompositableType mImageClientTypeContainer;
   Maybe<wr::PipelineId> mPipelineId;
   RefPtr<ImageContainer> mContainer;
 };
 
+
+class WMCanvasData : public WMData
+{
+public:
+  virtual WMCanvasData* AsCanvasData() override { return this; }
+  virtual TYPE GetType() override { return TYPE::CANVAS; }
+  static TYPE Type() { return TYPE::CANVAS; }
+
+  void EmptyTransaction();
+  void SetLayer(Layer* aLayer) { mLayer = aLayer; }
+  already_AddRefed<Layer> GetLayer()
+  {
+    RefPtr<Layer> res = mLayer;
+    return res.forget();
+  }
+
+protected:
+  RefPtr<Layer> mLayer;
+};
+
 class WebRenderLayerManager final : public LayerManager
 {
   typedef nsTArray<RefPtr<Layer> > LayerRefArray;
 
 public:
   explicit WebRenderLayerManager(nsIWidget* aWidget);
   void Initialize(PCompositorBridgeChild* aCBChild, wr::PipelineId aLayersId, TextureFactoryIdentifier* aTextureFactoryIdentifier);
 
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -11,16 +11,19 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsDisplayList.h"
 #include "nsLayoutUtils.h"
 #include "nsStyleUtil.h"
 #include "ImageLayers.h"
 #include "Layers.h"
 #include "ActiveLayerTracker.h"
+#include "mozilla/layers/StackingContextHelper.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "mozilla/layers/WebRenderLayer.h"
 
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 
@@ -109,19 +112,46 @@ public:
     nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame());
     return f->GetInnerArea() + ToReferenceFrame();
   }
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override
   {
+    CanvasLayer* oldLayer = static_cast<CanvasLayer*>
+      (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+
     return static_cast<nsHTMLCanvasFrame*>(mFrame)->
-      BuildLayer(aBuilder, aManager, this, aContainerParameters);
+      BuildLayer(aBuilder, aManager, this, aContainerParameters, oldLayer);
   }
+
+   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                        const StackingContextHelper& aSc,
+                                        nsTArray<WebRenderParentCommand>& aParentCommands,
+                                        mozilla::layers::WebRenderLayerManager* aManager,
+                                        nsDisplayListBuilder* aDisplayListBuilder) override
+   {
+     RefPtr<WMCanvasData> canvasData = aManager->CreateOrRecycleWMData<WMCanvasData>(this);
+     ContainerLayerParameters containerParameters;
+     containerParameters.mOffset.x = aSc.Origin().x;
+     containerParameters.mOffset.y = aSc.Origin().y;
+     RefPtr<Layer> oldLayer = canvasData->GetLayer();
+     RefPtr<Layer> layer = static_cast<nsHTMLCanvasFrame*>(mFrame)->BuildLayer(aDisplayListBuilder,
+                                                                               aManager,
+                                                                               this,
+                                                                               containerParameters,
+                                                                               oldLayer);
+     canvasData->SetLayer(layer);
+     if (layer) {
+       WebRenderLayer::ToWebRenderLayer(layer)->RenderLayer(aBuilder, aSc);
+     }
+     return true;
+   }
+
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) override
   {
     if (HTMLCanvasElement::FromContent(mFrame->GetContent())->ShouldForceInactiveLayer(aManager))
       return LAYER_INACTIVE;
 
     // If compositing is cheap, just do that
@@ -316,31 +346,30 @@ nsHTMLCanvasFrame::GetInnerArea() const
   r.height = mRect.height - bp.top - bp.bottom;
   return r;
 }
 
 already_AddRefed<Layer>
 nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
                               nsDisplayItem* aItem,
-                              const ContainerLayerParameters& aContainerParameters)
+                              const ContainerLayerParameters& aContainerParameters,
+                              Layer* aOldLayer)
 {
   nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
   HTMLCanvasElement* element = static_cast<HTMLCanvasElement*>(GetContent());
   nsIntSize canvasSizeInPx = GetCanvasSize();
 
   nsPresContext* presContext = PresContext();
   element->HandlePrintCallback(presContext->Type());
 
   if (canvasSizeInPx.width <= 0 || canvasSizeInPx.height <= 0 || area.IsEmpty())
     return nullptr;
 
-  CanvasLayer* oldLayer = static_cast<CanvasLayer*>
-    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
-  RefPtr<Layer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
+  RefPtr<Layer> layer = element->GetCanvasLayer(aBuilder, aOldLayer, aManager);
   if (!layer)
     return nullptr;
 
   IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSizeInPx);
   nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSizeInPx);
 
   nsRect dest =
     nsLayoutUtils::ComputeObjectDestRect(area, intrinsicSize, intrinsicRatio,
--- a/layout/generic/nsHTMLCanvasFrame.h
+++ b/layout/generic/nsHTMLCanvasFrame.h
@@ -46,17 +46,18 @@ public:
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
   already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                      LayerManager* aManager,
                                      nsDisplayItem* aItem,
-                                     const ContainerLayerParameters& aContainerParameters);
+                                     const ContainerLayerParameters& aContainerParameters,
+                                     Layer* aOldLayer);
 
   /* get the size of the canvas's image */
   nsIntSize GetCanvasSize();
 
   virtual nscoord GetMinISize(gfxContext *aRenderingContext) override;
   virtual nscoord GetPrefISize(gfxContext *aRenderingContext) override;
   virtual mozilla::IntrinsicSize GetIntrinsicSize() override;
   virtual nsSize GetIntrinsicRatio() override;
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -23,16 +23,19 @@
 #include "nsIImageLoadingContent.h"
 #include "nsContentUtils.h"
 #include "ImageContainer.h"
 #include "ImageLayers.h"
 #include "nsContentList.h"
 #include "nsStyleUtil.h"
 #include <algorithm>
 
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "mozilla/layers/StackingContextHelper.h"
+
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 nsIFrame*
 NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
@@ -427,16 +430,99 @@ public:
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayVideo() {
     MOZ_COUNT_DTOR(nsDisplayVideo);
   }
 #endif
 
   NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO)
 
+  virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                       const mozilla::layers::StackingContextHelper& aSc,
+                                       nsTArray<mozilla::layers::WebRenderParentCommand>& aParentCommands,
+                                       mozilla::layers::WebRenderLayerManager* aManager,
+                                       nsDisplayListBuilder* aDisplayListBuilder) override
+  {
+    nsRect area = Frame()->GetContentRectRelativeToSelf() + ToReferenceFrame();
+    HTMLVideoElement* element = static_cast<HTMLVideoElement*>(Frame()->GetContent());
+
+    nsIntSize videoSizeInPx;
+    if (NS_FAILED(element->GetVideoSize(&videoSizeInPx)) || area.IsEmpty()) {
+      return false;
+    }
+
+    RefPtr<ImageContainer> container = element->GetImageContainer();
+    if (!container) {
+      return false;
+    }
+
+    // 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 false;
+    }
+
+    // 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 dest = nsLayoutUtils::ComputeObjectDestRect(area,
+                                                       intrinsicSize,
+                                                       aspectRatio,
+                                                       Frame()->StylePosition());
+
+    gfxRect destGFXRect = Frame()->PresContext()->AppUnitsToGfxUnits(dest);
+    destGFXRect.Round();
+    if (destGFXRect.IsEmpty()) {
+      return false;
+    }
+
+    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);
+
+    LayerRect rect(destGFXRect.x, destGFXRect.y, destGFXRect.width, destGFXRect.height);
+    return aManager->PushImage(this, aBuilder, aSc, container, rect);
+
+//    RefPtr<ImageLayer> layer = static_cast<ImageLayer*>
+//      (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
+//    if (!layer) {
+//      layer = aManager->CreateImageLayer();
+//      if (!layer)
+//        return nullptr;
+//    }
+
+//    layer->SetContainer(container);
+//    layer->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(this));
+//    // Set a transform on the layer to draw the video in the right place
+//    gfxPoint p = destGFXRect.TopLeft() + aContainerParameters.mOffset;
+//
+//    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();
+
+  }
+
   // 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
   // be especially acute if we have off-main-thread rendering.