Bug 596451 part E - Implement transparency using DIBs with builtin transparency. Flash at least does this correctly, and since nobody else really uses windowless transparency, this is going to be the Way It Is. r-pending=jimm/karlt
authorBenjamin Smedberg <benjamin@smedbergs.us>
Wed, 27 Oct 2010 09:03:09 -0400
changeset 57212 37188e297b073acf5d8319dddf5e8c1aa15722f3
parent 57211 425bd6be7289f7439c3968fb6afbd407aa80c0fe
child 57213 88f740a74afc33cc44ee08cbab6175f2f7b0e575
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs596451
milestone2.0b8pre
Bug 596451 part E - Implement transparency using DIBs with builtin transparency. Flash at least does this correctly, and since nobody else really uses windowless transparency, this is going to be the Way It Is. r-pending=jimm/karlt
dom/plugins/PPluginInstance.ipdl
dom/plugins/PluginInstanceChild.cpp
dom/plugins/PluginInstanceParent.cpp
gfx/ipc/SharedDIBSurface.cpp
gfx/ipc/SharedDIBSurface.h
gfx/ipc/SharedDIBWin.cpp
gfx/ipc/SharedDIBWin.h
modules/plugin/test/mochitest/test_painting.html
--- a/dom/plugins/PPluginInstance.ipdl
+++ b/dom/plugins/PPluginInstance.ipdl
@@ -64,16 +64,17 @@ struct SurfaceDescriptorX11 {
   int XID;
   int xrenderPictID;
   gfxIntSize size;
 };
 
 struct SurfaceDescriptorWin {
   WindowsSharedMemoryHandle handle;
   gfxIntSize size;
+  bool transparent;
 };
 
 union SurfaceDescriptor {
   Shmem;
   SurfaceDescriptorX11;
   SurfaceDescriptorWin;
   // Descriptor can be null here in case
   // 1) of first Show call (prevSurface is null)
--- a/dom/plugins/PluginInstanceChild.cpp
+++ b/dom/plugins/PluginInstanceChild.cpp
@@ -1420,17 +1420,17 @@ PluginInstanceChild::SharedSurfaceSetWin
     if (!aWindow.surfaceHandle) {
         if (!mSharedSurfaceDib.IsValid()) {
             return false;
         }
     }
     else {
         // Attach to the new shared surface parent handed us.
         if (NS_FAILED(mSharedSurfaceDib.Attach((SharedDIB::Handle)aWindow.surfaceHandle,
-                                               aWindow.width, aWindow.height)))
+                                               aWindow.width, aWindow.height, false)))
           return false;
         // Free any alpha extraction resources if needed. This will be reset
         // the next time it's used.
         AlphaExtractCacheRelease();
     }
       
     // NPRemoteWindow's origin is the origin of our shared dib.
     mWindow.x      = 0;
@@ -2110,17 +2110,17 @@ PluginInstanceChild::CreateOptSurface(vo
 #endif
 
 #ifdef XP_WIN
     if (mSurfaceType == gfxASurface::SurfaceTypeWin32 ||
         mSurfaceType == gfxASurface::SurfaceTypeD2D) {
 
         SharedDIBSurface* s = new SharedDIBSurface();
         if (!s->Create(reinterpret_cast<HDC>(mWindow.window),
-                       mWindow.width, mWindow.height))
+                       mWindow.width, mWindow.height, mIsTransparent))
             return false;
 
         mCurrentSurface = s;
         return true;
     }
 
     NS_RUNTIMEABORT("Shared-memory drawing not expected on Windows.");
 #endif
@@ -2575,17 +2575,17 @@ PluginInstanceChild::ShowPluginFrame()
         XSync(mWsInfo.display, False);
     } else
 #endif
 #ifdef XP_WIN
     if (SharedDIBSurface::IsSharedDIBSurface(mCurrentSurface)) {
         base::SharedMemoryHandle handle = NULL;
         SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get());
         s->ShareToProcess(PluginModuleChild::current()->OtherProcess(), &handle);
-        currSurf = SurfaceDescriptorWin(handle, mCurrentSurface->GetSize());
+        currSurf = SurfaceDescriptorWin(handle, mCurrentSurface->GetSize(), mIsTransparent);
         s->Flush();
     } else
 #endif
     if (gfxSharedImageSurface::IsSharedImage(mCurrentSurface)) {
         currSurf = static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem();
     } else {
         NS_RUNTIMEABORT("Surface type is not remotable");
         return false;
--- a/dom/plugins/PluginInstanceParent.cpp
+++ b/dom/plugins/PluginInstanceParent.cpp
@@ -498,17 +498,17 @@ PluginInstanceParent::RecvShow(const NPR
             new gfxXlibSurface(DefaultScreenOfDisplay(DefaultXDisplay()),
                                xdesc.XID(), incFormat, xdesc.size());
     }
 #endif
 #ifdef XP_WIN
     else if (newSurface.type() == SurfaceDescriptor::TSurfaceDescriptorWin) {
         SurfaceDescriptorWin windesc = newSurface.get_SurfaceDescriptorWin();
         SharedDIBSurface* dibsurf = new SharedDIBSurface();
-        if (dibsurf->Attach(windesc.handle(), windesc.size().width, windesc.size().height))
+        if (dibsurf->Attach(windesc.handle(), windesc.size().width, windesc.size().height, windesc.transparent()))
             surface = dibsurf;
     }
 #endif
 
     mSentPaintNotification = PR_FALSE;
 
 #ifdef MOZ_X11
     if (mFrontSurface &&
@@ -1338,17 +1338,17 @@ PluginInstanceParent::SharedSurfaceSetWi
       // ok to paint
       aRemoteWindow.surfaceHandle = 0;
       return true;
     }
 
     // allocate a new shared surface
     SharedSurfaceRelease();
     if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast<HDC>(aWindow->window),
-                                           newPort.width, newPort.height)))
+                                           newPort.width, newPort.height, false)))
       return false;
 
     // save the new shared surface size we just allocated
     mSharedSize = newPort;
 
     base::SharedMemoryHandle handle;
     if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(mParent->ChildProcessHandle(), &handle)))
       return false;
--- a/gfx/ipc/SharedDIBSurface.cpp
+++ b/gfx/ipc/SharedDIBSurface.cpp
@@ -42,48 +42,53 @@
 namespace mozilla {
 namespace gfx {
 
 static const cairo_user_data_key_t SHAREDDIB_KEY = {0};
 
 static const long kBytesPerPixel = 4;
 
 bool
-SharedDIBSurface::Create(HDC adc, PRUint32 aWidth, PRUint32 aHeight)
+SharedDIBSurface::Create(HDC adc, PRUint32 aWidth, PRUint32 aHeight,
+                         bool aTransparent)
 {
-  nsresult rv = mSharedDIB.Create(adc, aWidth, aHeight);
+  nsresult rv = mSharedDIB.Create(adc, aWidth, aHeight, aTransparent);
   if (NS_FAILED(rv) || !mSharedDIB.IsValid())
     return false;
 
-  InitSurface(aWidth, aHeight);
+  InitSurface(aWidth, aHeight, aTransparent);
   return true;
 }
 
 bool
-SharedDIBSurface::Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight)
+SharedDIBSurface::Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight,
+                         bool aTransparent)
 {
-  nsresult rv = mSharedDIB.Attach(aHandle, aWidth, aHeight);
+  nsresult rv = mSharedDIB.Attach(aHandle, aWidth, aHeight, aTransparent);
   if (NS_FAILED(rv) || !mSharedDIB.IsValid())
     return false;
 
-  InitSurface(aWidth, aHeight);
+  InitSurface(aWidth, aHeight, aTransparent);
   return true;
 }
 
 void
-SharedDIBSurface::InitSurface(PRUint32 aWidth, PRUint32 aHeight)
+SharedDIBSurface::InitSurface(PRUint32 aWidth, PRUint32 aHeight,
+                              bool aTransparent)
 {
   // Windows DIBs are bottom-to-top by default, so the stride is negative
   // and the data is the beginning of the last row.
   long stride = -long(aWidth * kBytesPerPixel);
   unsigned char* data = reinterpret_cast<unsigned char*>(mSharedDIB.GetBits());
   data -= (aHeight - 1) * stride;
 
+  gfxImageFormat format = aTransparent ? ImageFormatARGB32 : ImageFormatRGB24;
+
   gfxImageSurface::InitWithData(data, gfxIntSize(aWidth, aHeight),
-                                stride, ImageFormatRGB24);
+                                stride, format);
 
   cairo_surface_set_user_data(mSurface, &SHAREDDIB_KEY, this, NULL);
 }
 
 bool
 SharedDIBSurface::IsSharedDIBSurface(gfxASurface* aSurface)
 {
   return aSurface &&
--- a/gfx/ipc/SharedDIBSurface.h
+++ b/gfx/ipc/SharedDIBSurface.h
@@ -55,22 +55,23 @@ public:
   typedef base::SharedMemoryHandle Handle;
 
   SharedDIBSurface() { }
   ~SharedDIBSurface() { }
 
   /**
    * Create this image surface backed by shared memory.
    */
-  bool Create(HDC adc, PRUint32 aWidth, PRUint32 aHeight);
+  bool Create(HDC adc, PRUint32 aWidth, PRUint32 aHeight, bool aTransparent);
 
   /**
    * Attach this surface to shared memory from another process.
    */
-  bool Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight);
+  bool Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight,
+              bool aTransparent);
 
   /**
    * After drawing to a surface via GDI, GDI must be flushed before the bitmap
    * is valid.
    */
   void Flush() { ::GdiFlush(); }
 
   HDC GetHDC() { return mSharedDIB.GetHDC(); }
@@ -79,15 +80,15 @@ public:
     return mSharedDIB.ShareToProcess(aChildProcess, aChildHandle);
   }
 
   static bool IsSharedDIBSurface(gfxASurface* aSurface);
 
 private:
   SharedDIBWin mSharedDIB;
 
-  void InitSurface(PRUint32 aWidth, PRUint32 aHeight);
+  void InitSurface(PRUint32 aWidth, PRUint32 aHeight, bool aTransparent);
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // mozilla_gfx_SharedDIBSurface_h
--- a/gfx/ipc/SharedDIBWin.cpp
+++ b/gfx/ipc/SharedDIBWin.cpp
@@ -73,84 +73,93 @@ SharedDIBWin::Close()
   mOldObj = mSharedBmp = NULL;
 
   SharedDIB::Close();
 
   return NS_OK;
 }
 
 nsresult
-SharedDIBWin::Create(HDC aHdc, PRUint32 aWidth, PRUint32 aHeight)
+SharedDIBWin::Create(HDC aHdc, PRUint32 aWidth, PRUint32 aHeight,
+                     bool aTransparent)
 {
   Close();
 
   // create the offscreen shared dib
-  BITMAPINFOHEADER bmih;
-  PRUint32 size = SetupBitmapHeader(aWidth, aHeight, &bmih);
+  BITMAPV4HEADER bmih;
+  PRUint32 size = SetupBitmapHeader(aWidth, aHeight, aTransparent, &bmih);
 
   nsresult rv = SharedDIB::Create(size);
   if (NS_FAILED(rv))
     return rv;
 
   if (NS_FAILED(SetupSurface(aHdc, &bmih))) {
     Close();
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
-SharedDIBWin::Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight)
+SharedDIBWin::Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight,
+                     bool aTransparent)
 {
   Close();
 
-  BITMAPINFOHEADER bmih;
-  SetupBitmapHeader(aWidth, aHeight, &bmih);
+  BITMAPV4HEADER bmih;
+  SetupBitmapHeader(aWidth, aHeight, aTransparent, &bmih);
 
   nsresult rv = SharedDIB::Attach(aHandle, 0);
   if (NS_FAILED(rv))
     return rv;
 
   if (NS_FAILED(SetupSurface(NULL, &bmih))) {
     Close();
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 PRUint32
-SharedDIBWin::SetupBitmapHeader(PRUint32 aWidth, PRUint32 aHeight, BITMAPINFOHEADER *aHeader)
+SharedDIBWin::SetupBitmapHeader(PRUint32 aWidth, PRUint32 aHeight,
+                                bool aTransparent, BITMAPV4HEADER *aHeader)
 {
-  memset((void*)aHeader, 0, sizeof(BITMAPINFOHEADER));
-  aHeader->biSize        = sizeof(BITMAPINFOHEADER);
-  aHeader->biWidth       = aWidth;
-  aHeader->biHeight      = aHeight;
-  aHeader->biPlanes      = 1;
-  aHeader->biBitCount    = 32;
-  aHeader->biCompression = BI_RGB;
+  memset((void*)aHeader, 0, sizeof(BITMAPV4HEADER));
+  aHeader->bV4Size          = sizeof(BITMAPV4HEADER);
+  aHeader->bV4Width         = aWidth;
+  aHeader->bV4Height        = aHeight;
+  aHeader->bV4Planes        = 1;
+  aHeader->bV4BitCount      = 32;
+  aHeader->bV4V4Compression = BI_BITFIELDS;
+  aHeader->bV4RedMask       = 0x00FF0000;
+  aHeader->bV4GreenMask     = 0x0000FF00;
+  aHeader->bV4BlueMask      = 0x000000FF;
 
-  return (sizeof(BITMAPINFOHEADER) + (aHeader->biHeight * aHeader->biWidth * kBytesPerPixel));
+  if (aTransparent)
+    aHeader->bV4AlphaMask     = 0xFF000000;
+
+  return (sizeof(BITMAPV4HEADER) + (aHeader->bV4Height * aHeader->bV4Width * kBytesPerPixel));
 }
 
 nsresult
-SharedDIBWin::SetupSurface(HDC aHdc, BITMAPINFOHEADER *aHdr)
+SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER *aHdr)
 {
   mSharedHdc = ::CreateCompatibleDC(aHdc);
 
   if (!mSharedHdc)
     return NS_ERROR_FAILURE;
 
   mSharedBmp = ::CreateDIBSection(mSharedHdc,
                                   (BITMAPINFO*)aHdr,
                                   DIB_RGB_COLORS,
                                   &mBitmapBits,
                                   mShMem->handle(),
-                                  (unsigned long)sizeof(BITMAPINFOHEADER));
+                                  (unsigned long)sizeof(BITMAPV4HEADER));
   if (!mSharedBmp)
     return NS_ERROR_FAILURE;
 
   mOldObj = SelectObject(mSharedHdc, mSharedBmp);
 
   return NS_OK;
 }
 
--- a/gfx/ipc/SharedDIBWin.h
+++ b/gfx/ipc/SharedDIBWin.h
@@ -49,38 +49,41 @@ namespace gfx {
 class SharedDIBWin : public SharedDIB
 {
 public:
   SharedDIBWin();
   ~SharedDIBWin();
 
   // Allocate a new win32 dib section compatible with an hdc. The dib will
   // be selected into the hdc on return.
-  nsresult Create(HDC aHdc, PRUint32 aWidth, PRUint32 aHeight);
+  nsresult Create(HDC aHdc, PRUint32 aWidth, PRUint32 aHeight,
+                  bool aTransparent);
 
   // Wrap a dib section around an existing shared memory object. aHandle should
   // point to a section large enough for the dib's memory, otherwise this call
   // will fail.
-  nsresult Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight);
+  nsresult Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight,
+                  bool aTransparent);
 
   // Destroy or release resources associated with this dib.
   nsresult Close();
 
   // Return the HDC of the shared dib.
   HDC GetHDC() { return mSharedHdc; }
 
   // Return the bitmap bits.
   void* GetBits() { return mBitmapBits; }
 
 private:
   HDC                 mSharedHdc;
   HBITMAP             mSharedBmp;
   HGDIOBJ             mOldObj;
   void*               mBitmapBits;
 
-  PRUint32 SetupBitmapHeader(PRUint32 aWidth, PRUint32 aHeight, BITMAPINFOHEADER *aHeader);
-  nsresult SetupSurface(HDC aHdc, BITMAPINFOHEADER *aHdr);
+  PRUint32 SetupBitmapHeader(PRUint32 aWidth, PRUint32 aHeight,
+                             bool aTransparent, BITMAPV4HEADER *aHeader);
+  nsresult SetupSurface(HDC aHdc, BITMAPV4HEADER *aHdr);
 };
 
 } // gfx
 } // mozilla
 
 #endif
--- a/modules/plugin/test/mochitest/test_painting.html
+++ b/modules/plugin/test/mochitest/test_painting.html
@@ -76,39 +76,25 @@ function show() {
   // Capturing an image (as in a reftest) would force a repaint and use
   // different paths for the image surface, so instead check the plugin's
   // paint count.
   waitForPaint(invalidate);
 }
 
 function invalidate() {
   var paintCount = 1;
-  try {
-    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-    if (Components.classes["@mozilla.org/gfx/info;1"].getService(Components.interfaces.nsIGfxInfo).D2DEnabled) {
-      paintCount = 2;
-    }
-  } catch(e) {}
-  
   is(clipped.getPaintCount(), paintCount, "partially clipped plugin painted once");  
   
   clipped.setColor("FF00FF00"); // plugin invalidates
 
   waitForPaint(done);
 }
 
 function done() {
   var paintCount = 2;
-  try {
-    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-    if (Components.classes["@mozilla.org/gfx/info;1"].getService(Components.interfaces.nsIGfxInfo).D2DEnabled) {
-      paintCount = 4;
-    }
-  } catch(e) {}
-  // With D2D we paint windowless plugin with 2 passes, 4 paints are expected.
   is(clipped.getPaintCount(), paintCount, "painted after invalidate");
 
   SimpleTest.finish();  
 }
 
 function waitForPaint(func) {
   paint_waiter.last_paint_count = paint_waiter.getPaintCount();
   // Ensure the waiter has been reflowed with zero height, so that this will