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.
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
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);