Fix for PlgBlt, the XFORM matrix was calculated incorrectly

Alexander Almaleh sashoalm at gmail.com
Tue May 20 11:39:48 CDT 2014


I had tried to submit a patch for that 5 years
ago<http://www.winehq.org/pipermail/wine-devel/2009-December/080391.html>,
but couldn't figure out how to make a patch/test it, and eventually gave up
and forgot about it. But recently I remembered about it, and now I want to
try again.

So, this time I've prepared a test that can prove that the previous
implementation does not calculate the XFORM matrix correctly, and that my
patch will fix it.

The test is a VC6 project, you can find it at
https://drive.google.com/file/d/0B9PGUhmmnsm1S2VvZzc3WmhBY3c/edit?usp=sharing.
The test is a demo of PlgBlt that I found on
CodeProject<http://www.codeproject.com/Articles/17712/The-GDI-magic-Rendering-reflections-and-smooth-sha>.
You can compare how it works on XP and on Wine. For testing, I directly
inserted the original Wine implementation of PlgBlt, as well as my patched
version, into the VC6 project, and tested on that. This is because there
seems to be an additional problem with MaskBlt, which I'll try tackling
next.

I have screenshots of what the demo looks using Windows's
PlgBlt<http://imgur.com/q8qKGd7>,
and Wine's PlgBlt <http://imgur.com/smYEV6z>.

To test it, simply look at this function in the VC6 project:

BOOL MyPlgBlt( HDC hdcDest, const POINT *lpPoint,
                        HDC hdcSrc, INT nXSrc, INT nYSrc, INT nWidth,
                        INT nHeight, HBITMAP hbmMask, INT xMask, INT yMask)
{
//      return MyPlgBltWinePatched(hdcDest, lpPoint, hdcSrc, nXSrc, nYSrc,
nWidth, nHeight, hbmMask, xMask, yMask);
        return MyPlgBltWineUnpatched(hdcDest, lpPoint, hdcSrc, nXSrc,
nYSrc, nWidth, nHeight, hbmMask, xMask, yMask);
//      return PlgBlt(hdcDest, lpPoint, hdcSrc, nXSrc, nYSrc, nWidth,
nHeight, hbmMask, xMask, yMask);
}

Uncomment the function you want to test. If you need something else (like
binaries), just write.

And this is the patch itself. I've tried to keep the changes minimal, with
respect to white-space, etc. I've removed a "return FALSE", because I don't
calculate a determinant explicitly.

commit c9ca2cab58fa0cba3d6b24cfb2af507fd1c07227
Author: Alexander Almaleh <sashoalm at gmail.com>
Date:   Tue May 20 18:33:55 2014 +0300

    Fix for the calculation of the XFORM matrix in PlgBlt.

diff --git a/dlls/gdi32/bitblt.c b/dlls/gdi32/bitblt.c
index 4750d1b..1de25b9 100644
--- a/dlls/gdi32/bitblt.c
+++ b/dlls/gdi32/bitblt.c
@@ -992,22 +992,32 @@ BOOL WINAPI PlgBlt( HDC hdcDest, const POINT *lpPoint,
                         HDC hdcSrc, INT nXSrc, INT nYSrc, INT nWidth,
                         INT nHeight, HBITMAP hbmMask, INT xMask, INT yMask)
 {
+    /* we need to use floating-point precision when calculating the
+     * XFORM matrix to avoid loss of precision due to rounding */
+    typedef struct tagPOINTLF
+    {
+        double x, y;
+    } POINTLF;
+
     int oldgMode;
     /* parallelogram coords */
-    POINT plg[3];
+    POINTLF plg[3];
     /* rect coords */
-    POINT rect[3];
+    POINTLF rect[3];
     XFORM xf;
     XFORM SrcXf;
     XFORM oldDestXf;
-    double det;
+    int ii;

     /* save actual mode, set GM_ADVANCED */
     oldgMode = SetGraphicsMode(hdcDest,GM_ADVANCED);
     if (oldgMode == 0)
         return FALSE;

-    memcpy(plg,lpPoint,sizeof(POINT)*3);
+    for (ii = 0; ii < 3; ii++) {
+        plg[ii].x = lpPoint[ii].x;
+        plg[ii].y = lpPoint[ii].y;
+    }
     rect[0].x = nXSrc;
     rect[0].y = nYSrc;
     rect[1].x = nXSrc + nWidth;
@@ -1015,33 +1025,39 @@ BOOL WINAPI PlgBlt( HDC hdcDest, const POINT
*lpPoint,
     rect[2].x = nXSrc;
     rect[2].y = nYSrc + nHeight;
     /* calc XFORM matrix to transform hdcDest -> hdcSrc (parallelogram to
rectangle) */
-    /* determinant */
-    det = rect[1].x*(rect[2].y - rect[0].y) - rect[2].x*(rect[1].y -
rect[0].y) - rect[0].x*(rect[2].y - rect[1].y);
-
-    if (fabs(det) < 1e-5)
-    {
-        SetGraphicsMode(hdcDest,oldgMode);
-        return FALSE;
-    }

     TRACE("hdcSrc=%p %d,%d,%dx%d -> hdcDest=%p %d,%d,%d,%d,%d,%d\n",
         hdcSrc, nXSrc, nYSrc, nWidth, nHeight, hdcDest, plg[0].x,
plg[0].y, plg[1].x, plg[1].y, plg[2].x, plg[2].y);

     /* X components */
-    xf.eM11 = (plg[1].x*(rect[2].y - rect[0].y) - plg[2].x*(rect[1].y -
rect[0].y) - plg[0].x*(rect[2].y - rect[1].y)) / det;
-    xf.eM21 = (rect[1].x*(plg[2].x - plg[0].x) - rect[2].x*(plg[1].x -
plg[0].x) - rect[0].x*(plg[2].x - plg[1].x)) / det;
-    xf.eDx  = (rect[0].x*(rect[1].y*plg[2].x - rect[2].y*plg[1].x) -
-               rect[1].x*(rect[0].y*plg[2].x - rect[2].y*plg[0].x) +
-               rect[2].x*(rect[0].y*plg[1].x - rect[1].y*plg[0].x)
-               ) / det;
+    xf.eM11 = -(rect[1].y*plg[2].x+rect[0].y*(plg[1].x-plg[2].x)
+ -rect[2].y*plg[1].x+plg[0].x*(rect[2].y-rect[1].y))
+ /(rect[0].y*(rect[2].x-rect[1].x)-rect[1].y*rect[2].x+
+ rect[2].y*rect[1].x+(rect[1].y-rect[2].y)*rect[0].x);
+    xf.eM21 = (plg[0].x*(rect[2].x-rect[1].x)-plg[1].x*rect[2].x
+ +plg[2].x*rect[1].x+(plg[1].x-plg[2].x)*rect[0].x)
+ /(rect[0].y*(rect[2].x-rect[1].x)-rect[1].y*rect[2].x
+ +rect[2].y*rect[1].x+(rect[1].y-rect[2].y)*rect[0].x);
+    xf.eDx = -(rect[0].y*(plg[2].x*rect[1].x-plg[1].x*rect[2].x)
+ +plg[0].x*(rect[1].y*rect[2].x-rect[2].y*rect[1].x)
+ +(rect[2].y*plg[1].x-rect[1].y*plg[2].x)*rect[0].x)
+ /(rect[0].y*(rect[2].x-rect[1].x)-rect[1].y*rect[2].x+rect[2].y
+ *rect[1].x+(rect[1].y-rect[2].y)*rect[0].x);

     /* Y components */
-    xf.eM12 = (plg[1].y*(rect[2].y - rect[0].y) - plg[2].y*(rect[1].y -
rect[0].y) - plg[0].y*(rect[2].y - rect[1].y)) / det;
-    xf.eM22 = (plg[1].x*(rect[2].y - rect[0].y) - plg[2].x*(rect[1].y -
rect[0].y) - plg[0].x*(rect[2].y - rect[1].y)) / det;
-    xf.eDy  = (rect[0].x*(rect[1].y*plg[2].y - rect[2].y*plg[1].y) -
-               rect[1].x*(rect[0].y*plg[2].y - rect[2].y*plg[0].y) +
-               rect[2].x*(rect[0].y*plg[1].y - rect[1].y*plg[0].y)
-               ) / det;
+    xf.eM12 = -(rect[1].y*(plg[2].y-plg[0].y)+rect[0].y*(plg[1].y
+ -plg[2].y)+rect[2].y*(plg[0].y-plg[1].y))/(rect[0].y*(rect[2].x
+ -rect[1].x)-rect[1].y*rect[2].x+rect[2].y*rect[1].x+(rect[1].y
+ -rect[2].y)*rect[0].x);
+    xf.eM22 = ((plg[0].y-plg[1].y)*rect[2].x+(plg[2].y-plg[0].y)*rect[1].x
+ +(plg[1].y-plg[2].y)*rect[0].x) /(rect[0].y*(rect[2].x-rect[1].x)
+ -rect[1].y*rect[2].x+rect[2].y*rect[1].x+(rect[1].y-rect[2].y)
+ *rect[0].x);
+    xf.eDy = (rect[0].y*(plg[1].y*rect[2].x-plg[2].y*rect[1].x)
+ -rect[1].y*plg[0].y*rect[2].x+rect[2].y*plg[0].y*rect[1].x
+ +(rect[1].y*plg[2].y-rect[2].y*plg[1].y)*rect[0].x)/(rect[0].y
+ *(rect[2].x-rect[1].x)-rect[1].y*rect[2].x+rect[2].y*rect[1].x
+ +(rect[1].y-rect[2].y)*rect[0].x);

     GetWorldTransform(hdcSrc,&SrcXf);
     CombineTransform(&xf,&xf,&SrcXf);
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20140520/7d3864f6/attachment-0001.html>


More information about the wine-devel mailing list