Bug 1319554 - allow WindowSurfaceX11Image to render to any visual format. r=rhunt
authorLee Salzman <lsalzman@mozilla.com>
Thu, 01 Dec 2016 12:15:58 -0500
changeset 325032 47e2650093d575c5042b43feb0575ef9678352b1
parent 325031 87e3edcefc5bc35b707dc9df00190fbf203b5c8d
child 325033 083ffc01652a13e80d12288074fd5dbc39621e93
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersrhunt
bugs1319554
milestone53.0a1
Bug 1319554 - allow WindowSurfaceX11Image to render to any visual format. r=rhunt MozReview-Commit-ID: JW4REAg1AKn
widget/WindowSurface.h
widget/gtk/WindowSurfaceProvider.cpp
widget/gtk/WindowSurfaceX11.cpp
widget/gtk/WindowSurfaceX11.h
widget/gtk/WindowSurfaceX11Image.cpp
widget/gtk/WindowSurfaceX11Image.h
widget/gtk/WindowSurfaceXRender.cpp
widget/gtk/WindowSurfaceXRender.h
--- a/widget/WindowSurface.h
+++ b/widget/WindowSurface.h
@@ -21,14 +21,17 @@ public:
   // Locks a region of the window for drawing, returning a draw target
   // capturing the bounds of the provided region.
   // Implementations must permit invocation from any thread.
   virtual already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) = 0;
 
   // Swaps the provided invalid region from the back buffer to the window.
   // Implementations must permit invocation from any thread.
   virtual void Commit(const LayoutDeviceIntRegion& aInvalidRegion) = 0;
+
+  // Whether the window surface represents a fallback method.
+  virtual bool IsFallback() const { return false; }
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // _MOZILLA_WIDGET_WINDOW_SURFACE_H
--- a/widget/gtk/WindowSurfaceProvider.cpp
+++ b/widget/gtk/WindowSurfaceProvider.cpp
@@ -88,19 +88,21 @@ WindowSurfaceProvider::StartRemoteDrawin
   if (!mWindowSurface) {
     mWindowSurface = CreateWindowSurface();
     if (!mWindowSurface)
       return nullptr;
   }
 
   *aBufferMode = BufferMode::BUFFER_NONE;
   RefPtr<DrawTarget> dt = nullptr;
-  if (!(dt = mWindowSurface->Lock(aInvalidRegion))) {
+  if (!(dt = mWindowSurface->Lock(aInvalidRegion)) &&
+      !mWindowSurface->IsFallback()) {
     gfxWarningOnce() << "Failed to lock WindowSurface, falling back to XPutImage backend.";
     mWindowSurface = MakeUnique<WindowSurfaceX11Image>(mXDisplay, mXWindow, mXVisual, mXDepth);
+    dt = mWindowSurface->Lock(aInvalidRegion);
   }
   return dt.forget();
 }
 
 void
 WindowSurfaceProvider::EndRemoteDrawingInRegion(gfx::DrawTarget* aDrawTarget,
                                               LayoutDeviceIntRegion& aInvalidRegion)
 {
--- a/widget/gtk/WindowSurfaceX11.cpp
+++ b/widget/gtk/WindowSurfaceX11.cpp
@@ -15,50 +15,17 @@ WindowSurfaceX11::WindowSurfaceX11(Displ
                                    Window aWindow,
                                    Visual* aVisual,
                                    unsigned int aDepth)
   : mDisplay(aDisplay)
   , mWindow(aWindow)
   , mVisual(aVisual)
   , mDepth(aDepth)
   , mFormat(GetVisualFormat(aVisual, aDepth))
-  , mGC(X11None)
 {
-  MOZ_ASSERT(mFormat != gfx::SurfaceFormat::UNKNOWN,
-             "Could not find SurfaceFormat for visual!");
-}
-
-WindowSurfaceX11::~WindowSurfaceX11()
-{
-  if (mGC != X11None)
-    XFreeGC(mDisplay, mGC);
-}
-
-void
-WindowSurfaceX11::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
-{
-  AutoTArray<XRectangle, 32> xrects;
-  xrects.SetCapacity(aInvalidRegion.GetNumRects());
-
-  for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
-    const mozilla::LayoutDeviceIntRect &r = iter.Get();
-    XRectangle xrect = { (short)r.x, (short)r.y, (unsigned short)r.width, (unsigned short)r.height };
-    xrects.AppendElement(xrect);
-  }
-
-  if (!mGC) {
-    mGC = XCreateGC(mDisplay, mWindow, 0, nullptr);
-    if (!mGC) {
-      NS_WARNING("Couldn't create X11 graphics context for window!");
-      return;
-    }
-  }
-
-  XSetClipRectangles(mDisplay, mGC, 0, 0, xrects.Elements(), xrects.Length(), YXBanded);
-  CommitToDrawable(mWindow, mGC, aInvalidRegion);
 }
 
 /* static */
 gfx::SurfaceFormat
 WindowSurfaceX11::GetVisualFormat(const Visual* aVisual, unsigned int aDepth)
 {
   switch (aDepth) {
   case 32:
--- a/widget/gtk/WindowSurfaceX11.h
+++ b/widget/gtk/WindowSurfaceX11.h
@@ -16,36 +16,24 @@
 
 namespace mozilla {
 namespace widget {
 
 class WindowSurfaceX11 : public WindowSurface {
 public:
   WindowSurfaceX11(Display* aDisplay, Window aWindow, Visual* aVisual,
                    unsigned int aDepth);
-  ~WindowSurfaceX11();
-
-  void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
-
-  virtual already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override = 0;
-
-  // Draws the buffered image onto aDest using the given GC.
-  // The GC provided has been clipped to aInvalidRegion.
-  virtual void CommitToDrawable(Drawable aDest, GC aGC,
-                                const LayoutDeviceIntRegion& aInvalidRegion) = 0;
 
 protected:
   static gfx::SurfaceFormat GetVisualFormat(const Visual* aVisual, unsigned int aDepth);
 
   Display* const mDisplay;
   const Window mWindow;
   Visual* const mVisual;
   const unsigned int mDepth;
   const gfx::SurfaceFormat mFormat;
-
-  GC mGC;
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // MOZ_X11
 #endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_H
--- a/widget/gtk/WindowSurfaceX11Image.cpp
+++ b/widget/gtk/WindowSurfaceX11Image.cpp
@@ -3,75 +3,117 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WindowSurfaceX11Image.h"
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/gfxVars.h"
 #include "gfxPlatform.h"
+#include "gfx2DGlue.h"
 
 namespace mozilla {
 namespace widget {
 
 WindowSurfaceX11Image::WindowSurfaceX11Image(Display* aDisplay,
                                              Window aWindow,
                                              Visual* aVisual,
                                              unsigned int aDepth)
   : WindowSurfaceX11(aDisplay, aWindow, aVisual, aDepth)
-  , mImage(nullptr)
 {
 }
 
 WindowSurfaceX11Image::~WindowSurfaceX11Image()
 {
-  if (mImage)
-    XDestroyImage(mImage);
 }
 
 already_AddRefed<gfx::DrawTarget>
 WindowSurfaceX11Image::Lock(const LayoutDeviceIntRegion& aRegion)
 {
   gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
   gfx::IntSize size(bounds.XMost(), bounds.YMost());
 
-  if (!mImage || mImage->width < size.width || mImage->height < size.height) {
-    if (mImage)
-      XDestroyImage(mImage);
-
-    int stride = gfx::GetAlignedStride<16>(size.width, gfx::BytesPerPixel(mFormat));
-    if (stride == 0) {
-        return nullptr;
-    }
-    char* data = static_cast<char*>(malloc(stride * size.height));
-    if (!data)
-      return nullptr;
-
-    mImage = XCreateImage(mDisplay, mVisual, mDepth, ZPixmap, 0,
-                          data, size.width, size.height,
-                          8 * gfx::BytesPerPixel(mFormat), stride);
+  if (!mWindowSurface || mWindowSurface->CairoStatus() ||
+      !(size <= mWindowSurface->GetSize())) {
+    mWindowSurface = new gfxXlibSurface(mDisplay, mWindow, mVisual, size);
+  }
+  if (mWindowSurface->CairoStatus()) {
+    return nullptr;
   }
 
-  if (!mImage)
-    return nullptr;
+  if (!mImageSurface || mImageSurface->CairoStatus() ||
+      !(size <= mImageSurface->GetSize())) {
+    gfxImageFormat format = SurfaceFormatToImageFormat(mFormat);
+    if (format == gfx::SurfaceFormat::UNKNOWN) {
+      format = mDepth == 32 ?
+                 gfx::SurfaceFormat::A8R8G8B8_UINT32 :
+                 gfx::SurfaceFormat::X8R8G8B8_UINT32;
+    }
+
+    mImageSurface = new gfxImageSurface(size, format);
+    if (mImageSurface->CairoStatus()) {
+      return nullptr;
+    }
+  }
 
-  unsigned char* data = (unsigned char*) mImage->data;
-  return gfxPlatform::CreateDrawTargetForData(data,
-                                              size,
-                                              mImage->bytes_per_line,
-                                              mFormat);
+  gfxImageFormat format = mImageSurface->Format();
+  // Cairo prefers compositing to BGRX instead of BGRA where possible.
+  if (format == gfx::SurfaceFormat::X8R8G8B8_UINT32) {
+    gfx::BackendType backend = gfxVars::ContentBackend();
+    if (!gfx::Factory::DoesBackendSupportDataDrawtarget(backend)) {
+#ifdef USE_SKIA
+      backend = gfx::BackendType::SKIA;
+#else
+      backend = gfx::BackendType::CAIRO;
+#endif
+    }
+    if (backend != gfx::BackendType::CAIRO) {
+      format = gfx::SurfaceFormat::A8R8G8B8_UINT32;
+    }
+  }
+
+  return gfxPlatform::CreateDrawTargetForData(mImageSurface->Data(),
+                                              mImageSurface->GetSize(),
+                                              mImageSurface->Stride(),
+                                              ImageFormatToSurfaceFormat(format));
 }
 
 void
-WindowSurfaceX11Image::CommitToDrawable(Drawable aDest, GC aGC,
-                                        const LayoutDeviceIntRegion& aInvalidRegion)
+WindowSurfaceX11Image::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
 {
-  MOZ_ASSERT(mImage, "Attempted to commit invalid surface!");
+  RefPtr<gfx::DrawTarget> dt =
+    gfx::Factory::CreateDrawTargetForCairoSurface(mWindowSurface->CairoSurface(),
+                                                  mWindowSurface->GetSize());
+  RefPtr<gfx::SourceSurface> surf =
+    gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(),
+                                                     mImageSurface->GetSize(),
+                                                     mImageSurface->Format());
+  if (!dt || !surf) {
+    return;
+  }
 
   gfx::IntRect bounds = aInvalidRegion.GetBounds().ToUnknownRect();
-  gfx::IntSize size(bounds.XMost(), bounds.YMost());
-  XPutImage(mDisplay, aDest, aGC, mImage, bounds.x, bounds.y,
-            bounds.x, bounds.y, size.width, size.height);
+  gfx::Rect rect(0, 0, bounds.XMost(), bounds.YMost());
+  if (rect.IsEmpty()) {
+    return;
+  }
+
+  uint32_t numRects = aInvalidRegion.GetNumRects();
+  if (numRects != 1) {
+    AutoTArray<IntRect, 32> rects;
+    rects.SetCapacity(numRects);
+    for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+      rects.AppendElement(iter.Get().ToUnknownRect());
+    }
+    dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+  }
+
+  dt->DrawSurface(surf, rect, rect);
+
+  if (numRects != 1) {
+    dt->PopClip();
+  }
 }
 
 }  // namespace widget
 }  // namespace mozilla
--- a/widget/gtk/WindowSurfaceX11Image.h
+++ b/widget/gtk/WindowSurfaceX11Image.h
@@ -5,31 +5,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
 #define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
 
 #ifdef MOZ_X11
 
 #include "WindowSurfaceX11.h"
+#include "gfxXlibSurface.h"
+#include "gfxImageSurface.h"
 
 namespace mozilla {
 namespace widget {
 
 class WindowSurfaceX11Image : public WindowSurfaceX11 {
 public:
   WindowSurfaceX11Image(Display* aDisplay, Window aWindow, Visual* aVisual,
                         unsigned int aDepth);
   ~WindowSurfaceX11Image();
 
   already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
-  void CommitToDrawable(Drawable aDest, GC aGC,
-                        const LayoutDeviceIntRegion& aInvalidRegion) override;
+  void Commit(const LayoutDeviceIntRegion& aInvalidRegion) override;
+  bool IsFallback() const override { return true; }
 
 private:
-  XImage* mImage;
+  RefPtr<gfxXlibSurface> mWindowSurface;
+  RefPtr<gfxImageSurface> mImageSurface;
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // MOZ_X11
 #endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
--- a/widget/gtk/WindowSurfaceXRender.cpp
+++ b/widget/gtk/WindowSurfaceXRender.cpp
@@ -14,46 +14,70 @@ namespace mozilla {
 namespace widget {
 
 WindowSurfaceXRender::WindowSurfaceXRender(Display* aDisplay,
                                            Window aWindow,
                                            Visual* aVisual,
                                            unsigned int aDepth)
   : WindowSurfaceX11(aDisplay, aWindow, aVisual, aDepth)
   , mXlibSurface(nullptr)
+  , mGC(X11None)
 {
 }
 
+WindowSurfaceXRender::~WindowSurfaceXRender()
+{
+  if (mGC != X11None) {
+    XFreeGC(mDisplay, mGC);
+  }
+}
+
 already_AddRefed<gfx::DrawTarget>
 WindowSurfaceXRender::Lock(const LayoutDeviceIntRegion& aRegion)
 {
   gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
   gfx::IntSize size(bounds.XMost(), bounds.YMost());
-  if (!mXlibSurface || mXlibSurface->CairoStatus() != 0 ||
-      mXlibSurface->GetSize().width < size.width ||
-      mXlibSurface->GetSize().height < size.height)
-  {
+  if (!mXlibSurface || mXlibSurface->CairoStatus() ||
+      !(size <= mXlibSurface->GetSize())) {
     mXlibSurface = gfxXlibSurface::Create(DefaultScreenOfDisplay(mDisplay),
                                           mVisual,
                                           size,
                                           mWindow);
   }
+  if (!mXlibSurface || mXlibSurface->CairoStatus()) {
+    return nullptr;
+  }
 
-  if (mXlibSurface && mXlibSurface->CairoStatus() == 0) {
-    return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mXlibSurface.get(), size);
-  }
-  return nullptr;
+  return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mXlibSurface, size);
 }
 
 void
-WindowSurfaceXRender::CommitToDrawable(Drawable aDest, GC aGC,
-                                       const LayoutDeviceIntRegion& aInvalidRegion)
+WindowSurfaceXRender::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
 {
+  AutoTArray<XRectangle, 32> xrects;
+  xrects.SetCapacity(aInvalidRegion.GetNumRects());
+
+  for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+    const LayoutDeviceIntRect &r = iter.Get();
+    XRectangle xrect = { (short)r.x, (short)r.y, (unsigned short)r.width, (unsigned short)r.height };
+    xrects.AppendElement(xrect);
+  }
+
+  if (!mGC) {
+    mGC = XCreateGC(mDisplay, mWindow, 0, nullptr);
+    if (!mGC) {
+      NS_WARNING("Couldn't create X11 graphics context for window!");
+      return;
+    }
+  }
+
+  XSetClipRectangles(mDisplay, mGC, 0, 0, xrects.Elements(), xrects.Length(), YXBanded);
+
   MOZ_ASSERT(mXlibSurface && mXlibSurface->CairoStatus() == 0,
              "Attempted to commit invalid surface!");
   gfx::IntRect bounds = aInvalidRegion.GetBounds().ToUnknownRect();
   gfx::IntSize size(bounds.XMost(), bounds.YMost());
-  XCopyArea(mDisplay, mXlibSurface->XDrawable(), aDest, aGC, bounds.x, bounds.y,
+  XCopyArea(mDisplay, mXlibSurface->XDrawable(), mWindow, mGC, bounds.x, bounds.y,
             size.width, size.height, bounds.x, bounds.y);
 }
 
 }  // namespace widget
 }  // namespace mozilla
--- a/widget/gtk/WindowSurfaceXRender.h
+++ b/widget/gtk/WindowSurfaceXRender.h
@@ -14,22 +14,23 @@
 
 namespace mozilla {
 namespace widget {
 
 class WindowSurfaceXRender : public WindowSurfaceX11 {
 public:
   WindowSurfaceXRender(Display* aDisplay, Window aWindow, Visual* aVisual,
                        unsigned int aDepth);
+  ~WindowSurfaceXRender();
 
   already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
-  void CommitToDrawable(Drawable aDest, GC aGC,
-                        const LayoutDeviceIntRegion& aInvalidRegion) override;
+  void Commit(const LayoutDeviceIntRegion& aInvalidRegion) override;
 
 private:
   RefPtr<gfxXlibSurface> mXlibSurface;
+  GC mGC;
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // MOZ_X11
 #endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_XRENDER_H