DIB Engine: implement AlphaBlend

From: Massimo Del Fedele <max@veneto.com>


---

 dlls/winedib.drv/bitblt.c            |   88 ++++++++--
 dlls/winedib.drv/dibdrv.h            |    2 
 dlls/winedib.drv/primitives.c        |   12 +
 dlls/winedib.drv/primitives_bitblt.c |  287 ++++++++++++++++++++++++++++++++++
 4 files changed, 370 insertions(+), 19 deletions(-)


diff --git a/dlls/winedib.drv/bitblt.c b/dlls/winedib.drv/bitblt.c
index 20cdcaa..c0227a0 100644
--- a/dlls/winedib.drv/bitblt.c
+++ b/dlls/winedib.drv/bitblt.c
@@ -150,34 +150,84 @@ BOOL DIBDRV_AlphaBlend( DIBDRVPHYSDEV *physDevDst, INT xDst, INT yDst, INT width
           xDst, yDst, widthDst, heightDst,
           physDevSrc, physDevSrc->hasDIB ? "DIB-" : "DDB", physDevSrc->hasDIB ? _DIBDRVBITMAP_GetFormatName(&physDevSrc->physBitmap) : "",
           xSrc, ySrc, widthSrc, heightSrc));
+          
 
-    if(physDevDst->hasDIB && physDevSrc->hasDIB)
+    /* if sizes are null or negative, returns false */
+    if(widthSrc <= 0 || heightSrc <= 0 || widthDst <= 0 || heightDst <= 0)
     {
-        /* DIB section selected in both source and dest DC, use DIB Engine */
-        ONCE(FIXME("STUB\n"));
-        res = TRUE;
+        res = FALSE;
+        goto fin;
     }
-    else if(!physDevDst->hasDIB && !physDevSrc->hasDIB)
+          
+    /* source sould be a 32 bit DIB */
+    if(!physDevSrc)
     {
-        /* DDB selected in noth source and dest DC, use X11 driver */
-        res =  _DIBDRV_GetDisplayDriver()->pAlphaBlend(physDevDst->X11PhysDev, xDst, yDst, widthDst, heightDst,
-                                                       physDevSrc->X11PhysDev, xSrc, ySrc, widthSrc, heightSrc,
-                                                       blendfn);
+        FIXME("Null source bitmap -- shouldn't happen\n");
+        res = FALSE;
+        goto fin;
     }
-    else if(physDevSrc->hasDIB)
+    else if(!physDevSrc->hasDIB)
     {
-        /* DIB on source, DDB on dest -- must convert source DIB to DDB and use X11 driver for blit */
-        ONCE(FIXME("TEMPORARY - fallback to X11 driver\n"));
-        res =  _DIBDRV_GetDisplayDriver()->pAlphaBlend(physDevDst->X11PhysDev, xDst, yDst, widthDst, heightDst,
-                                                   physDevSrc->X11PhysDev, xSrc, ySrc, widthSrc, heightSrc,
-                                                   blendfn);
+        FIXME("DDB source bitmap -- shouldn't happen\n");
+        res = FALSE;
+        goto fin;
     }
-    else /* if(physDevDst->hasDIB) */
+    
+    if(physDevDst->hasDIB)
     {
-        /* DDB on source, DIB on dest -- must convert source DDB to DIB and use the engine for blit */
-        ONCE(FIXME("STUB\n"));
-        res = TRUE; 
+        /* DIB section selected in dest DC, use DIB Engine */
+        MAYBE(TRACE("Blending DIB->DIB\n"));
+        res = physDevDst->physBitmap.funcs->AlphaBlend(physDevDst, xDst, yDst, widthDst, heightDst,
+                                                       physDevSrc, xSrc, ySrc, widthSrc, heightSrc, blendfn);
+    }
+    else
+    {
+        /* DDB selected on dest DC -- must double-convert */
+        HBITMAP tmpDIB, stock;
+        HDC tmpDC;
+        MAYBE(TRACE("Blending DIB->DDB\n"));
+        
+        /* converts dest DDB onto a temporary DIB -- just the needed part */
+        tmpDIB = _DIBDRV_ConvertDevDDBtoDIB(physDevDst->hdc, physDevSrc->hdc, xDst, yDst, widthDst, heightDst);
+        if(!tmpDIB)
+        {
+            ERR("Couldn't convert dest DDB to DIB\n");
+            res = FALSE;
+            goto fin;
+        }
+        
+        /* selects the temporary DIB into a temporary DC */
+        tmpDC = CreateCompatibleDC(physDevDst->hdc);
+        if(!tmpDC)
+        {
+            ERR("Couldn't create temporary DC\n");
+            DeleteObject(tmpDIB);
+            res = FALSE;
+            goto fin;
+        }
+        stock = SelectObject(tmpDC, tmpDIB);
+        if(!stock)
+        {
+            ERR("Couldn't select temporary DIB into temporary DC\n");
+            DeleteDC(tmpDC);
+            DeleteObject(tmpDIB);
+            res = FALSE;
+            goto fin;
+        }
+        
+        /* blends source DIB onto temp DIB and re-blits onto dest DC */
+        res = GdiAlphaBlend(tmpDC, 0, 0, widthDst, heightDst, physDevSrc->hdc, xSrc, ySrc, widthSrc, heightSrc, blendfn);
+        if(!res)
+            MAYBE(TRACE("AlphaBlend failed\n"));
+        else
+            res = BitBlt(physDevDst->hdc, xDst, yDst, widthDst, heightDst, tmpDC, 0, 0, SRCCOPY);
+            
+        /* frees resources */
+        SelectObject(tmpDC, stock);
+        DeleteDC(tmpDC);
+        DeleteObject(tmpDIB);        
     }
+fin:
     return res;
 }
 
diff --git a/dlls/winedib.drv/dibdrv.h b/dlls/winedib.drv/dibdrv.h
index 773941e..c801d96 100644
--- a/dlls/winedib.drv/dibdrv.h
+++ b/dlls/winedib.drv/dibdrv.h
@@ -94,6 +94,8 @@ typedef struct _DIBDRV_PRIMITIVE_FUNCS
     BOOL  (* PutLine)          (      struct _DIBDRVBITMAP *bmp, int line, int startx, int width, void *buf);
     
     /* BitBlt primitives */
+    BOOL  (* AlphaBlend)       (      struct _DIBDRVPHYSDEV *physDevDst, int xDst, int yDst, int widthDst, int heightDst,
+                                const struct _DIBDRVPHYSDEV *physDevSrc, int xSrc, int ySrc, int widthSrc, int heightSrc, BLENDFUNCTION blendFn );
     BOOL  (* BitBlt)           (      struct _DIBDRVPHYSDEV *physDevDst, int xDst, int yDst, int width, int height,
                                 const struct _DIBDRVPHYSDEV *physDevSrc, int xSrc, int ySrc, DWORD rop );
     BOOL  (* StretchBlt)       (      struct _DIBDRVPHYSDEV *physDevDst, int xDst, int yDst, int widthDst, int heightDst,
diff --git a/dlls/winedib.drv/primitives.c b/dlls/winedib.drv/primitives.c
index cbad239..a2fa04a 100644
--- a/dlls/winedib.drv/primitives.c
+++ b/dlls/winedib.drv/primitives.c
@@ -133,6 +133,10 @@ BOOL _DIBDRV_StretchBlt_generic(DIBDRVPHYSDEV *physDevDst, INT xDst, INT yDst,
                     INT widthDst, INT heightDst, const DIBDRVPHYSDEV *physDevSrc,
                     INT xSrc, INT ySrc, int widthSrc, int heightSrc, DWORD rop);
 
+BOOL _DIBDRV_AlphaBlend_generic(DIBDRVPHYSDEV *physDevDst, int xDst, int yDst,
+                    int widthDst, int heightDst, const DIBDRVPHYSDEV *physDevSrc,
+                    int xSrc, int ySrc, int widthSrc, int heightSrc, BLENDFUNCTION blendFn );
+                    
 /* ------------------------------------------------------------*/
 /*               FREETYPE FONT BITMAP BLITTING                 */
 void _DIBDRV_freetype_blit_8888        (DIBDRVPHYSDEV *dib, int x, int y, FT_Bitmap *bmp);
@@ -156,6 +160,7 @@ DIBDRV_PRIMITIVE_FUNCS DIBDRV_funcs_DIB32_RGB =
     _DIBDRV_SolidVLine32,
     _DIBDRV_GetLine32_RGB,
     _DIBDRV_PutLine32_RGB,
+    _DIBDRV_AlphaBlend_generic,
     _DIBDRV_BitBlt_generic,
     _DIBDRV_StretchBlt_generic,
     _DIBDRV_freetype_blit_32_RGB
@@ -172,6 +177,7 @@ DIBDRV_PRIMITIVE_FUNCS DIBDRV_funcs_DIB32_BITFIELDS =
     _DIBDRV_SolidVLine32,
     _DIBDRV_GetLine32_BITFIELDS,
     _DIBDRV_PutLine32_BITFIELDS,
+    _DIBDRV_AlphaBlend_generic,
     _DIBDRV_BitBlt_generic,
     _DIBDRV_StretchBlt_generic,
     _DIBDRV_freetype_blit_32_BITFIELDS
@@ -188,6 +194,7 @@ DIBDRV_PRIMITIVE_FUNCS DIBDRV_funcs_DIB24 =
     _DIBDRV_SolidVLine24,
     _DIBDRV_GetLine24,
     _DIBDRV_PutLine24,
+    _DIBDRV_AlphaBlend_generic,
     _DIBDRV_BitBlt_generic,
     _DIBDRV_StretchBlt_generic,
     _DIBDRV_freetype_blit_24
@@ -204,6 +211,7 @@ DIBDRV_PRIMITIVE_FUNCS DIBDRV_funcs_DIB16_RGB =
     _DIBDRV_SolidVLine16,
     _DIBDRV_GetLine16_RGB,
     _DIBDRV_PutLine16_RGB,
+    _DIBDRV_AlphaBlend_generic,
     _DIBDRV_BitBlt_generic,
     _DIBDRV_StretchBlt_generic,
     _DIBDRV_freetype_blit_16_RGB
@@ -220,6 +228,7 @@ DIBDRV_PRIMITIVE_FUNCS DIBDRV_funcs_DIB16_BITFIELDS =
     _DIBDRV_SolidVLine16,
     _DIBDRV_GetLine16_BITFIELDS,
     _DIBDRV_PutLine16_BITFIELDS,
+    _DIBDRV_AlphaBlend_generic,
     _DIBDRV_BitBlt_generic,
     _DIBDRV_StretchBlt_generic,
     _DIBDRV_freetype_blit_16_BITFIELDS
@@ -236,6 +245,7 @@ DIBDRV_PRIMITIVE_FUNCS DIBDRV_funcs_DIB8 =
     _DIBDRV_SolidVLine8,
     _DIBDRV_GetLine8,
     _DIBDRV_PutLine8,
+    _DIBDRV_AlphaBlend_generic,
     _DIBDRV_BitBlt_generic,
     _DIBDRV_StretchBlt_generic,
     _DIBDRV_freetype_blit_8
@@ -252,6 +262,7 @@ DIBDRV_PRIMITIVE_FUNCS DIBDRV_funcs_DIB4 =
     _DIBDRV_SolidVLine4,
     _DIBDRV_GetLine4,
     _DIBDRV_PutLine4,
+    _DIBDRV_AlphaBlend_generic,
     _DIBDRV_BitBlt_generic,
     _DIBDRV_StretchBlt_generic,
     _DIBDRV_freetype_blit_4
@@ -268,6 +279,7 @@ DIBDRV_PRIMITIVE_FUNCS DIBDRV_funcs_DIB1 =
     _DIBDRV_SolidVLine1,
     _DIBDRV_GetLine1,
     _DIBDRV_PutLine1,
+    _DIBDRV_AlphaBlend_generic,
     _DIBDRV_BitBlt_generic,
     _DIBDRV_StretchBlt_generic,
     _DIBDRV_freetype_blit_1
diff --git a/dlls/winedib.drv/primitives_bitblt.c b/dlls/winedib.drv/primitives_bitblt.c
index da48352..7540dad 100644
--- a/dlls/winedib.drv/primitives_bitblt.c
+++ b/dlls/winedib.drv/primitives_bitblt.c
@@ -80,6 +80,293 @@ static void StretchLine(DWORD *dst, int dstWidth, DWORD *src, int srcWidth)
         memcpy(dst, src, 4 * srcWidth);
 }
 
+/* premultiply alpha channel on a line by a constant alpha
+   note : it seems that pixels are already premultiplied
+   by alpha channel content */
+static void PemultiplyLine(DWORD *dst, int width, BYTE constAlpha)
+{
+    int i = width;
+    BYTE *alphaPnt = (BYTE *)dst + 3;
+    
+    /* small optimization for 0 and 255 values of constAlpha */
+
+    /* fully transparent -- just sets all pix to 0 */
+    if(constAlpha == 0)
+    {
+        while(i--)
+            *dst++ = 0;
+        return;
+    }
+    
+    /* fully opaque, just do nothing */
+    if(constAlpha == 255)
+        return;
+
+    /* intermediate -- premultiply alpha values */
+    while(i--)
+    {
+        *alphaPnt = MulDiv(*alphaPnt, constAlpha, 255);
+        alphaPnt += 4;
+    }
+    return;
+        
+}
+
+/* alpha blends a source line onto a destination line
+   preconditions :
+   1) source and dest widths must be the same
+   2) source line should be already premultiplied by constant alpha */
+static void BlendLine(DWORD *dst, DWORD *src, int width)
+{
+    int i = width;
+    BYTE *blueDst  = (BYTE *)dst;
+    BYTE *greenDst = blueDst  + 1;
+    BYTE *redDst   = greenDst + 1;
+    BYTE *blueSrc  = (BYTE *)src;
+    BYTE *greenSrc = blueSrc  + 1;
+    BYTE *redSrc   = greenSrc + 1;
+    BYTE *alphaSrc = redSrc   + 1;
+    BYTE alpha;
+    
+    /* still don't know if it must take in account an eventual dest
+       alpha channel..... */
+    while(i--)
+    {
+        alpha = 255 - *alphaSrc;
+        
+        *blueDst  = *blueSrc  + MulDiv(*blueDst,  alpha, 255);
+        *greenDst = *greenSrc + MulDiv(*greenDst, alpha, 255);
+        *redDst   = *redSrc   + MulDiv(*redDst,   alpha, 255);
+
+        blueSrc  += 4;
+        greenSrc += 4;
+        redSrc   += 4;
+        alphaSrc += 4;
+        blueDst  += 4;
+        greenDst += 4;
+        redDst   += 4;
+    }
+
+}
+
+/* ------------------------------------------------------------*/
+/*                        ALPHABLEND PRIMITIVES                */
+BOOL _DIBDRV_AlphaBlend_generic(DIBDRVPHYSDEV *physDevDst, INT xDst, INT yDst,
+                    INT widthDst, INT heightDst, const DIBDRVPHYSDEV *physDevSrc,
+                    INT xSrc, INT ySrc, int widthSrc, int heightSrc, BLENDFUNCTION blendFn)
+{
+    /* flags indicating wether source should be stretched */
+    BOOL horStretch = (widthSrc != widthDst);
+    BOOL verStretch = (heightSrc != heightDst);
+    
+    /* constant alpha value */
+    BYTE constAlpha = blendFn.SourceConstantAlpha;
+    
+    /* source and dest bitmaps */
+    const DIBDRVBITMAP *srcBmp = &physDevSrc->physBitmap;
+    DIBDRVBITMAP *dstBmp = &physDevDst->physBitmap;
+    
+    /* source and destination line buffers */
+    DWORD *sBuf = HeapAlloc(GetProcessHeap(), 0, abs(srcBmp->stride));
+    DWORD *dBuf = HeapAlloc(GetProcessHeap(), 0, abs(dstBmp->stride));
+    
+    int ys = ySrc;
+    int yd = yDst;
+    int iLine;
+    int delta;
+
+    /* in order to optimize a bit, we divide the routine in 4 parts,
+       depending on stretching modes */
+    if(!horStretch && !verStretch)
+    {
+        /* simplest case, no stretching needed */
+        MAYBE(TRACE("No stretching\n"));
+        for(iLine = 0; iLine < heightSrc; iLine++, ys++, yd++)
+        {
+            /* load source and dest lines */
+            srcBmp->funcs->GetLine(srcBmp, ys, xSrc, widthSrc, sBuf);
+            dstBmp->funcs->GetLine(dstBmp, yd, xDst, widthDst, dBuf);
+            
+            /* premultiply source by constant and pixel alpha */
+            PemultiplyLine(sBuf, widthSrc, constAlpha);
+            
+            /* blends source on dest */
+            BlendLine(dBuf, sBuf, widthSrc);
+            
+            /* puts dest line back */
+            dstBmp->funcs->PutLine(dstBmp, yd, xDst, widthDst, dBuf);
+        }
+    }
+    else if (horStretch && !verStretch)
+    {
+        /* just horizontal stretching needed */
+        DWORD *strBuf = HeapAlloc(GetProcessHeap(), 0, abs(dstBmp->stride));
+        MAYBE(TRACE("Horizontal stretching\n"));
+
+        for(iLine = 0; iLine < heightSrc; iLine++, ys++, yd++)
+        {
+            /* load source and dest lines */
+            srcBmp->funcs->GetLine(srcBmp, ys, xSrc, widthSrc, sBuf);
+            dstBmp->funcs->GetLine(dstBmp, yd, xDst, widthDst, dBuf);
+            
+            /* stretch source line to match dest one */
+            StretchLine(strBuf, widthDst, sBuf, widthSrc);
+        
+            /* premultiply source by constant and pixel alpha */
+            PemultiplyLine(strBuf, widthDst, constAlpha);
+            
+            /* blends source on dest */
+            BlendLine(dBuf, sBuf, widthDst);
+            
+            /* puts dest line back */
+            dstBmp->funcs->PutLine(dstBmp, yd, xDst, widthDst, dBuf);
+        }
+        HeapFree(GetProcessHeap(), 0, strBuf);
+    }
+    else if (!horStretch && verStretch)
+    {
+        /* just vertical stretching needed */
+        MAYBE(TRACE("Vertical stretching\n"));
+
+        if(heightSrc > heightDst)
+        {
+            iLine = 0;
+            delta = 0;
+            while(iLine < heightDst)
+            {
+                /* load source and dest lines */
+                srcBmp->funcs->GetLine(srcBmp, ys, xSrc, widthSrc, sBuf);
+                dstBmp->funcs->GetLine(dstBmp, yd, xDst, widthDst, dBuf);
+
+                /* premultiply source by constant and pixel alpha */
+                PemultiplyLine(sBuf, widthSrc, constAlpha);
+                
+                /* blends source on dest */
+                BlendLine(dBuf, sBuf, widthDst);
+                
+                /* puts dest line back */
+                dstBmp->funcs->PutLine(dstBmp, yd, xDst, widthDst, dBuf);
+
+                while(delta < heightSrc)
+                {
+                    ys++;
+                    delta += heightDst;
+                }
+                delta -= heightSrc;
+                yd++;
+                iLine++;
+            }
+        }
+        else if(heightSrc < heightDst)
+        {
+            iLine = 0;
+            delta = 0;
+            while(iLine < heightSrc)
+            {
+                /* load source line */
+                srcBmp->funcs->GetLine(srcBmp, ys, xSrc, widthSrc, sBuf);
+                
+                /* premultiply source by constant and pixel alpha */
+                PemultiplyLine(sBuf, widthSrc, constAlpha);
+                
+                while(delta < heightDst)
+                {
+                    /* load dest line */
+                    dstBmp->funcs->GetLine(dstBmp, yd, xDst, widthDst, dBuf);
+                    
+                    /* blends source on dest */
+                    BlendLine(dBuf, sBuf, widthDst);
+                    
+                    /* puts dest line back */
+                    dstBmp->funcs->PutLine(dstBmp, yd, xDst, widthDst, dBuf);
+                    yd++;
+                    delta += heightSrc;
+                }
+                delta -= heightDst;
+                ys++;
+                iLine++;
+            }
+        }
+    }
+    else
+    {
+        DWORD *strBuf = HeapAlloc(GetProcessHeap(), 0, abs(dstBmp->stride));
+        /* both stretching needed -- generic case */
+        MAYBE(TRACE("Horizontal and vertical stretching\n"));
+
+        if(heightSrc > heightDst)
+        {
+            iLine = 0;
+            delta = 0;
+            while(iLine < heightDst)
+            {
+                /* load source and dest lines */
+                srcBmp->funcs->GetLine(srcBmp, ys, xSrc, widthSrc, sBuf);
+                dstBmp->funcs->GetLine(dstBmp, yd, xDst, widthDst, dBuf);
+
+                /* stretch source line to match dest one */
+                StretchLine(strBuf, widthDst, sBuf, widthSrc);
+
+                /* premultiply source by constant and pixel alpha */
+                PemultiplyLine(strBuf, widthDst, constAlpha);
+                
+                /* blends source on dest */
+                BlendLine(dBuf, strBuf, widthDst);
+                
+                /* puts dest line back */
+                dstBmp->funcs->PutLine(dstBmp, yd, xDst, widthDst, dBuf);
+
+                while(delta < heightSrc)
+                {
+                    ys++;
+                    delta += heightDst;
+                }
+                delta -= heightSrc;
+                yd++;
+                iLine++;
+            }
+        }
+        else if(heightSrc < heightDst)
+        {
+            iLine = 0;
+            delta = 0;
+            while(iLine < heightSrc)
+            {
+                /* load source line */
+                srcBmp->funcs->GetLine(srcBmp, ys, xSrc, widthSrc, sBuf);
+                
+                /* stretch source line to match dest one */
+                StretchLine(strBuf, widthDst, sBuf, widthSrc);
+
+                /* premultiply source by constant and pixel alpha */
+                PemultiplyLine(strBuf, widthDst, constAlpha);
+                
+                while(delta < heightDst)
+                {
+                    /* load dest line */
+                    dstBmp->funcs->GetLine(dstBmp, yd, xDst, widthDst, dBuf);
+                    
+                    /* blends source on dest */
+                    BlendLine(dBuf, strBuf, widthDst);
+                    
+                    /* puts dest line back */
+                    dstBmp->funcs->PutLine(dstBmp, yd, xDst, widthDst, dBuf);
+                    yd++;
+                    delta += heightSrc;
+                }
+                delta -= heightDst;
+                ys++;
+                iLine++;
+            }
+        }
+        HeapFree(GetProcessHeap(), 0, strBuf);
+    }
+    
+    HeapFree(GetProcessHeap(), 0, sBuf);
+    HeapFree(GetProcessHeap(), 0, dBuf);
+    return TRUE;
+}
+
 /* ------------------------------------------------------------*/
 /*                        BLITTING PRIMITIVES                  */
 BOOL _DIBDRV_BitBlt_generic(DIBDRVPHYSDEV *physDevDst, INT xDst, INT yDst,