From 1ffe94b441654ba146cfe2fd10965b4aed43c03c Mon Sep 17 00:00:00 2001
From: Roderick Colenbrander <roderick@roderick-laptop.(none)>
Date: Sun, 19 Jul 2009 18:44:21 +0200
Subject: [PATCH] Add support for dibsections in depths other than 1-bit / screen_depth when XRender is around.

---
 dlls/winex11.drv/dib.c     |   11 +++++-
 dlls/winex11.drv/x11drv.h  |    1 +
 dlls/winex11.drv/xrender.c |   84 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 95 insertions(+), 1 deletions(-)

diff --git a/dlls/winex11.drv/dib.c b/dlls/winex11.drv/dib.c
index 07ce9e1..3e88cbe 100644
--- a/dlls/winex11.drv/dib.c
+++ b/dlls/winex11.drv/dib.c
@@ -4683,6 +4683,7 @@ HBITMAP CDECL X11DRV_CreateDIBSection( X11DRV_PDEVICE *physDev, HBITMAP hbitmap,
 {
     X_PHYSBITMAP *physBitmap;
     DIBSECTION dib;
+    int redMask, greenMask, blueMask;
 
     if (!(physBitmap = X11DRV_init_phys_bitmap( hbitmap ))) return 0;
     physBitmap->status = DIB_Status_None;
@@ -4699,7 +4700,7 @@ HBITMAP CDECL X11DRV_CreateDIBSection( X11DRV_PDEVICE *physDev, HBITMAP hbitmap,
 
     /* create pixmap and X image */
     wine_tsx11_lock();
-    physBitmap->pixmap_depth = (dib.dsBm.bmBitsPixel == 1) ? 1 : screen_depth;
+    physBitmap->pixmap_depth = X11DRV_XRender_GetDIBDepth(&dib, &redMask, &greenMask, &blueMask);
     physBitmap->pixmap = XCreatePixmap( gdi_display, root_window, dib.dsBm.bmWidth,
                                         dib.dsBm.bmHeight, physBitmap->pixmap_depth );
 #ifdef HAVE_LIBXXSHM
@@ -4713,6 +4714,14 @@ HBITMAP CDECL X11DRV_CreateDIBSection( X11DRV_PDEVICE *physDev, HBITMAP hbitmap,
     wine_tsx11_unlock();
     if (!physBitmap->pixmap || !physBitmap->image) return 0;
 
+    /* When XRender is around and used, we also support dibsections in other formats like 16-bit. In these
+     * cases we need to override the mask of XImages. The reason is that during XImage creation the masks are
+     * derived from a 24-bit visual (no 16-bit ones are around when X runs at 24-bit). SetImageBits and other
+     * functions rely on the color masks for proper color conversion, so we need to override the masks here. */
+    physBitmap->image->red_mask = redMask;
+    physBitmap->image->green_mask = greenMask;
+    physBitmap->image->blue_mask = blueMask;
+
       /* install fault handler */
     InitializeCriticalSection( &physBitmap->lock );
     physBitmap->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": X_PHYSBITMAP.lock");
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 10af183..65a3a89 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -267,6 +267,7 @@ extern void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE*);
 extern BOOL X11DRV_XRender_ExtTextOut(X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
 				      const RECT *lprect, LPCWSTR wstr,
 				      UINT count, const INT *lpDx);
+extern int X11DRV_XRender_GetDIBDepth(const DIBSECTION *dib, int *redMask, int *greenMask, int *blueMask);
 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
                                       Pixmap pixmap, GC gc,
                                       INT xSrc, INT ySrc,
diff --git a/dlls/winex11.drv/xrender.c b/dlls/winex11.drv/xrender.c
index 3bf37e4..31b9ce7 100644
--- a/dlls/winex11.drv/xrender.c
+++ b/dlls/winex11.drv/xrender.c
@@ -427,10 +427,55 @@ static WineXRenderFormat *get_xrender_format(WXRFormat format)
     return NULL;
 }
 
+static WineXRenderFormat *get_xrender_format_from_dibsection(const DIBSECTION *dib)
+{
+    int redMask=0, greenMask=0, blueMask=0;
+    int i;
+
+    if(dib->dsBm.bmBitsPixel == 1)
+        return get_xrender_format(WXR_FORMAT_MONO);
+
+    /* Paletted formats aren't supported using XRender */
+    if(dib->dsBm.bmBitsPixel <= 8)
+        return NULL;
+
+    redMask   = dib->dsBitfields[0];
+    greenMask = dib->dsBitfields[1];
+    blueMask  = dib->dsBitfields[2];
+
+    /* Try to locate a format which matches the specification of the dibsection. */
+    for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
+    {
+        if( dib->dsBm.bmBitsPixel == wxr_formats_template[i].depth && 
+            redMask   == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
+            greenMask == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
+            blueMask  == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue) )
+
+        {
+            /* When we reach this stage the format was found in our template table but this doesn't mean that
+             * the Xserver also supports this format (e.g. its depth might be too low). The call below verifies that.
+             */
+            return get_xrender_format(wxr_formats_template[i].wxr_format);
+        }
+    }
+
+    /* Format not found */
+    return NULL;
+}
+
 static WineXRenderFormat *get_xrender_format_from_pdevice(X11DRV_PDEVICE *physDev)
 {
     WXRFormat format;
+    DIBSECTION dib;
 
+    /* When the physdev is associated with a dibsection, use get_xrender_from_dibsection */
+    if (physDev->bitmap && GetObjectW( physDev->bitmap->hbitmap, sizeof(dib), &dib ) == sizeof(dib))
+    {
+        WineXRenderFormat *fmt = get_xrender_format_from_dibsection(&dib);
+        if(fmt) return fmt;
+    }
+
+    /* We are dealing with a DIB in a format for which conversion is required or with a DDB / window */
     switch(physDev->depth)
     {
         case 1:
@@ -817,6 +862,36 @@ void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
     return;
 }
 
+int X11DRV_XRender_GetDIBDepth(const DIBSECTION *dib, int *redMask, int *greenMask, int *blueMask)
+{
+    WineXRenderFormat *fmt;
+
+    /* By default derive the masks from the default visual */
+    *redMask   = visual->red_mask;
+    *greenMask = visual->green_mask;
+    *blueMask  = visual->blue_mask;
+
+    /* Fall back to the generic code when XRender isn't around */
+    if(!X11DRV_XRender_Installed)
+        return (dib->dsBm.bmBitsPixel == 1) ? 1 : screen_depth;
+
+    /* Common formats should be in our picture format table. If a format isn't around
+     * fall back to 1-bit / screen_depth. */
+    fmt = get_xrender_format_from_dibsection(dib);
+    if(fmt)
+    {
+        *redMask   = fmt->pict_format->direct.redMask << fmt->pict_format->direct.red;
+        *greenMask = fmt->pict_format->direct.greenMask << fmt->pict_format->direct.green;
+        *blueMask  = fmt->pict_format->direct.blueMask << fmt->pict_format->direct.blue;
+        return fmt->pict_format->depth;
+    }
+    else
+    {
+        WARN("Unhandled dibsection format bpp=%d, redMask=%x, greenMask=%x, blueMask=%x\n", dib->dsBm.bmBitsPixel, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
+        return (dib->dsBm.bmBitsPixel == 1) ? 1 : screen_depth;
+    }
+}
+
 /***********************************************************************
  *   X11DRV_XRender_UpdateDrawable
  *
@@ -2108,6 +2183,15 @@ BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flag
   return FALSE;
 }
 
+int X11DRV_XRender_GetDIBDepth(const DIBSECTION *dib, int *redMask, int *greenMask, int *blueMask)
+{
+    /* By default derive the masks from the default visual */
+    *redMask   = visual->red_mask;
+    *greenMask = visual->green_mask;
+    *blueMask  = visual->blue_mask;
+    return (dib->dsBm.bmBitsPixel == 1) ? 1 : screen_depth;
+}
+
 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
 {
   assert(0);
-- 
1.6.0.4

