gdi32: SetWinMetaFileBits

Michael Kaufmann hallo at michael-kaufmann.ch
Tue Jun 20 09:02:37 CDT 2006


This is an improved version of this patch: 
http://www.winehq.org/pipermail/wine-patches/2006-February/024461.html

Changelog:
- SetWinMetaFileBits: Use the whole device surface if the METAFILEPICT 
parameter is NULL
- Also use the whole device surface if one of the extents is zero or 
negative and the mapping mode is MM_ANISOTROPIC or MM_ISOTROPIC
- New tests

-------------- next part --------------
Index: dlls/gdi/enhmetafile.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/enhmetafile.c,v
retrieving revision 1.21
diff -u -r1.21 enhmetafile.c
--- dlls/gdi/enhmetafile.c	23 May 2006 12:47:58 -0000	1.21
+++ dlls/gdi/enhmetafile.c	20 Jun 2006 13:28:39 -0000
@@ -1339,10 +1339,10 @@
         if (!lpScaleViewportExtEx->xNum || !lpScaleViewportExtEx->xDenom || 
             !lpScaleViewportExtEx->yNum || !lpScaleViewportExtEx->yDenom)
             break;
-        info->vportExtX = (info->vportExtX * lpScaleViewportExtEx->xNum) / 
-                                  lpScaleViewportExtEx->xDenom;
-        info->vportExtY = (info->vportExtY * lpScaleViewportExtEx->yNum) / 
-                                  lpScaleViewportExtEx->yDenom;
+        info->vportExtX = MulDiv(info->vportExtX, lpScaleViewportExtEx->xNum,
+                                 lpScaleViewportExtEx->xDenom);
+        info->vportExtY = MulDiv(info->vportExtY, lpScaleViewportExtEx->yNum,
+                                 lpScaleViewportExtEx->yDenom);
         if (info->vportExtX == 0) info->vportExtX = 1;
         if (info->vportExtY == 0) info->vportExtY = 1;
         if (info->mode == MM_ISOTROPIC)
@@ -1364,10 +1364,10 @@
         if (!lpScaleWindowExtEx->xNum || !lpScaleWindowExtEx->xDenom || 
             !lpScaleWindowExtEx->xNum || !lpScaleWindowExtEx->yDenom)
             break;
-        info->wndExtX = (info->wndExtX * lpScaleWindowExtEx->xNum) / 
-                                  lpScaleWindowExtEx->xDenom;
-        info->wndExtY = (info->wndExtY * lpScaleWindowExtEx->yNum) / 
-                                  lpScaleWindowExtEx->yDenom;
+        info->wndExtX = MulDiv(info->wndExtX, lpScaleWindowExtEx->xNum,
+                               lpScaleWindowExtEx->xDenom);
+        info->wndExtY = MulDiv(info->wndExtY, lpScaleWindowExtEx->yNum,
+                               lpScaleWindowExtEx->yDenom);
         if (info->wndExtX == 0) info->wndExtX = 1;
         if (info->wndExtY == 0) info->wndExtY = 1;
         if (info->mode == MM_ISOTROPIC)
@@ -2627,6 +2627,7 @@
     RECT rc, *prcFrame = NULL;
     gdi_mf_comment *mfcomment;
     UINT mfcomment_size;
+    LONG mm, xExt, yExt;
 
     TRACE("(%d, %p, %p, %p)\n", cbBuffer, lpbBuffer, hdcRef, lpmfp);
 
@@ -2641,19 +2642,45 @@
         hdcRef = hdcdisp = CreateDCW(szDisplayW, NULL, NULL, NULL);
 
     if (lpmfp)
+    {
         TRACE("mm = %ld %ldx%ld\n", lpmfp->mm, lpmfp->xExt, lpmfp->yExt);
-    
-    if (lpmfp && (lpmfp->mm == MM_ISOTROPIC || lpmfp->mm == MM_ANISOTROPIC))
+
+        mm = lpmfp->mm;
+        xExt = lpmfp->xExt;
+        yExt = lpmfp->yExt;
+    }
+    else
     {
-        rc.left = rc.top = 0;
-        rc.right = lpmfp->xExt;
-        rc.bottom = lpmfp->yExt;
-        prcFrame = &rc;
+        TRACE("lpmfp == NULL\n");
+
+        /* Use the whole device surface */
+        mm = MM_ANISOTROPIC;
+        xExt = 0;
+        yExt = 0;
+    }
+
+    if (mm == MM_ISOTROPIC || mm == MM_ANISOTROPIC)
+    {
+        if (xExt < 0 || yExt < 0)
+        {
+          /* Use the whole device surface */
+          xExt = 0;
+          yExt = 0;
+        }
+
+        /* Use the x and y extents as the frame box */
+        if (xExt && yExt)
+        {
+            rc.left = rc.top = 0;
+            rc.right = xExt;
+            rc.bottom = yExt;
+            prcFrame = &rc;
+        }
     }
 
     if(!(hdc = CreateEnhMetaFileW(hdcRef, NULL, prcFrame, NULL)))
     {
-        ERR("CreateEnhMetaFile fails?\n");
+        ERR("CreateEnhMetaFile failed\n");
         goto end;
     }
 
@@ -2676,23 +2703,33 @@
         HeapFree(GetProcessHeap(), 0, mfcomment);
     }
 
-    if(lpmfp && lpmfp->mm != MM_TEXT)
-        SetMapMode(hdc, lpmfp->mm);
+    if (mm != MM_TEXT)
+        SetMapMode(hdc, mm);
 
-    if (lpmfp && (lpmfp->mm == MM_ISOTROPIC || lpmfp->mm == MM_ANISOTROPIC))
+    if (mm == MM_ISOTROPIC || mm == MM_ANISOTROPIC)
     {
-        INT horzres, vertres, horzsize, vertsize, xext, yext;
+        INT horzsize, vertsize, horzres, vertres;
 
-        horzres = GetDeviceCaps(hdcRef, HORZRES);
-        vertres = GetDeviceCaps(hdcRef, VERTRES);
         horzsize = GetDeviceCaps(hdcRef, HORZSIZE);
         vertsize = GetDeviceCaps(hdcRef, VERTSIZE);
+        horzres = GetDeviceCaps(hdcRef, HORZRES);
+        vertres = GetDeviceCaps(hdcRef, VERTRES);
+
+        if (!xExt || !yExt)
+        {
+            /* Use the whole device surface */
+           xExt = horzres;
+           yExt = vertres;
+        }
+        else
+        {
+            xExt = MulDiv(xExt, horzres, 100 * horzsize);
+            yExt = MulDiv(yExt, vertres, 100 * vertsize);
+        }
 
         /* set the initial viewport:window ratio as 1:1 */
-        xext = lpmfp->xExt*horzres/(100*horzsize);
-        yext = lpmfp->yExt*vertres/(100*vertsize);
-        SetViewportExtEx(hdc, xext, yext, NULL);
-        SetWindowExtEx(hdc,   xext, yext, NULL);
+        SetViewportExtEx(hdc, xExt, yExt, NULL);
+        SetWindowExtEx(hdc,   xExt, yExt, NULL);
     }
 
     PlayMetaFile(hdc, hmf);
Index: dlls/gdi/tests/metafile.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/tests/metafile.c,v
retrieving revision 1.23
diff -u -r1.23 metafile.c
--- dlls/gdi/tests/metafile.c	23 May 2006 12:48:00 -0000	1.23
+++ dlls/gdi/tests/metafile.c	20 Jun 2006 13:28:41 -0000
@@ -1299,6 +1299,217 @@
     }
 }
 
+static BOOL getConvertedFrameAndBounds(UINT buffer_size, BYTE * buffer, BOOL mfpIsNull,
+                                       LONG mm, LONG xExt, LONG yExt,
+                                       RECTL * rclBounds, RECTL * rclFrame)
+{
+  METAFILEPICT mfp;
+  METAFILEPICT * mfpPtr = NULL;
+  HENHMETAFILE emf;
+  ENHMETAHEADER header;
+  UINT res;
+
+  if (!mfpIsNull)
+  {
+    mfp.mm = mm;
+    mfp.xExt = xExt;
+    mfp.yExt = yExt;
+    mfpPtr = &mfp;
+  }
+
+  emf = SetWinMetaFileBits(buffer_size, buffer, NULL, mfpPtr);
+  ok(emf != NULL, "SetWinMetaFileBits failed\n");
+  if (!emf) return FALSE;
+  res = GetEnhMetaFileHeader(emf, sizeof(header), &header);
+  ok(res != 0, "GetEnhMetaHeader failed\n");
+  DeleteEnhMetaFile(emf);
+  if (!res) return FALSE;
+
+  *rclBounds = header.rclBounds;
+  *rclFrame = header.rclFrame;
+  return TRUE;
+}
+
+static void checkConvertedFrameAndBounds(UINT buffer_size, BYTE * buffer, BOOL mfpIsNull,
+                                         LONG mm, LONG xExt, LONG yExt,
+                                         RECTL * rclBoundsExpected, RECTL * rclFrameExpected)
+{
+  RECTL rclBounds, rclFrame;
+
+  if (getConvertedFrameAndBounds(buffer_size, buffer, mfpIsNull, mm, xExt, yExt, &rclBounds, &rclFrame))
+  {
+    char * msg;
+    char buf[64];
+
+    if (mfpIsNull)
+    {
+       msg = "mfp == NULL";
+    }
+    else
+    {
+      char * mm_str;
+      switch (mm)
+      {
+         case MM_ANISOTROPIC: mm_str = "MM_ANISOTROPIC"; break;
+         case MM_ISOTROPIC:   mm_str = "MM_ISOTROPIC"; break;
+         default:             mm_str = "Unexpected";
+      }
+      snprintf(buf, 64, "mm=%s, xExt=%ld, yExt=%ld", mm_str, xExt, yExt);
+      msg = buf;
+    }
+
+    ok(rclBounds.left == rclBoundsExpected->left, "rclBounds.left: Expected %ld, got %ld (%s)\n", rclBoundsExpected->left, rclBounds.left, msg);
+    ok(rclBounds.top == rclBoundsExpected->top, "rclBounds.top: Expected %ld, got %ld (%s)\n", rclBoundsExpected->top, rclBounds.top, msg);
+    ok(rclBounds.right == rclBoundsExpected->right, "rclBounds.right: Expected %ld, got %ld (%s)\n", rclBoundsExpected->right, rclBounds.right, msg);
+    ok(rclBounds.bottom == rclBoundsExpected->bottom, "rclBounds.bottom: Expected %ld, got %ld (%s)\n", rclBoundsExpected->bottom, rclBounds.bottom, msg);
+    ok(rclFrame.left == rclFrameExpected->left, "rclFrame.left: Expected %ld, got %ld (%s)\n", rclFrameExpected->left, rclFrame.left, msg);
+    ok(rclFrame.top == rclFrameExpected->top, "rclFrame.top: Expected %ld, got %ld (%s)\n", rclFrameExpected->top, rclFrame.top, msg);
+    ok(rclFrame.right == rclFrameExpected->right, "rclFrame.right: Expected %ld, got %ld (%s)\n", rclFrameExpected->right, rclFrame.right, msg);
+    ok(rclFrame.bottom == rclFrameExpected->bottom, "rclFrame.bottom: Expected %ld, got %ld (%s)\n", rclFrameExpected->bottom, rclFrame.bottom, msg);
+  }
+}
+
+static void test_SetWinMetaFileBits(void)
+{
+  HMETAFILE wmf;
+  HDC wmfDC;
+  BYTE * buffer;
+  UINT buffer_size;
+  RECT rect;
+  UINT res;
+  RECTL rclBoundsAnisotropic, rclFrameAnisotropic;
+  RECTL rclBoundsIsotropic, rclFrameIsotropic;
+  RECTL rclBounds, rclFrame;
+  HDC dc;
+  LONG diffx, diffy;
+
+  wmfDC = CreateMetaFile(NULL);
+  ok(wmfDC != NULL, "CreateMetaFile failed\n");
+  if (!wmfDC) return;
+
+  SetWindowExtEx(wmfDC, 100, 100, NULL);
+  rect.left = rect.top = 0;
+  rect.right = rect.bottom = 50;
+  FillRect(wmfDC, &rect, GetStockObject(BLACK_BRUSH));
+  wmf = CloseMetaFile(wmfDC);
+  ok(wmf != NULL, "Metafile creation failed\n");
+  if (!wmf) return;
+
+  buffer_size = GetMetaFileBitsEx(wmf, 0, NULL);
+  ok(buffer_size != 0, "GetMetaFileBitsEx failed\n");
+  if (buffer_size == 0)
+  {
+    DeleteMetaFile(wmf);
+    return;
+  }
+
+  buffer = (BYTE *)HeapAlloc(GetProcessHeap(), 0, buffer_size);
+  ok(buffer != NULL, "HeapAlloc failed\n");
+  if (!buffer)
+  {
+    DeleteMetaFile(wmf);
+    return;
+  }
+
+  res = GetMetaFileBitsEx(wmf, buffer_size, buffer);
+  ok(res == buffer_size, "GetMetaFileBitsEx failed\n");
+  DeleteMetaFile(wmf);
+  if (res != buffer_size)
+  {
+     HeapFree(GetProcessHeap(), 0, buffer);
+     return;
+  }
+
+  /* Get the reference bounds and frame */
+  getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 0, 0, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+  getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 0, 0,  &rclBoundsIsotropic, &rclFrameIsotropic);
+
+  ok(rclBoundsAnisotropic.left == 0 && rclBoundsAnisotropic.top == 0 &&
+     rclBoundsIsotropic.left == 0 && rclBoundsIsotropic.top == 0,
+     "SetWinMetaFileBits: Reference bounds: Left and top bound must be zero\n");
+
+  ok(rclBoundsAnisotropic.right >= rclBoundsIsotropic.right, "SetWinMetaFileBits: Reference bounds: Invalid right bound\n");
+  ok(rclBoundsAnisotropic.bottom >= rclBoundsIsotropic.bottom, "SetWinMetaFileBits: Reference bounds: Invalid bottom bound\n");
+  diffx = rclBoundsIsotropic.right - rclBoundsIsotropic.bottom;
+  if (diffx < 0) diffx = -diffx;
+  ok(diffx <= 1, "SetWinMetaFileBits (MM_ISOTROPIC): Reference bounds are not isotropic\n");
+
+  dc = CreateCompatibleDC(NULL);
+  todo_wine
+  {
+  ok(rclBoundsAnisotropic.right == GetDeviceCaps(dc, HORZRES) / 2 - 1 &&
+     rclBoundsAnisotropic.bottom == GetDeviceCaps(dc, VERTRES) / 2 - 1,
+     "SetWinMetaFileBits (MM_ANISOTROPIC): Reference bounds: The whole device surface must be used (%dx%d), but got (%ldx%ld)\n",
+     GetDeviceCaps(dc, HORZRES) / 2 - 1, GetDeviceCaps(dc, VERTRES) / 2 - 1, rclBoundsAnisotropic.right, rclBoundsAnisotropic.bottom);
+  }
+
+  /* Allow 1 mm difference (rounding errors) */
+  diffx = rclFrameAnisotropic.right / 100 - GetDeviceCaps(dc, HORZSIZE) / 2;
+  diffy = rclFrameAnisotropic.bottom / 100 - GetDeviceCaps(dc, VERTSIZE) / 2;
+  if (diffx < 0) diffx = -diffx;
+  if (diffy < 0) diffy = -diffy;
+  todo_wine
+  {
+  ok(diffx <= 1 && diffy <= 1,
+     "SetWinMetaFileBits (MM_ANISOTROPIC): Reference frame: The whole device surface must be used (%dx%d), but got (%ldx%ld)\n",
+     GetDeviceCaps(dc, HORZSIZE) / 2, GetDeviceCaps(dc, VERTSIZE) / 2, rclFrameAnisotropic.right / 100, rclFrameAnisotropic.bottom / 100);
+  }
+  DeleteDC(dc);
+
+  /* If the METAFILEPICT pointer is NULL, the MM_ANISOTROPIC mapping mode and the whole device surface are used */
+  checkConvertedFrameAndBounds(buffer_size, buffer, TRUE, 0, 0, 0, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+
+  /* If xExt or yExt is zero or negative, the whole device surface is used */
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 10000, 0, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 10000, 0, &rclBoundsIsotropic, &rclFrameIsotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 0, 10000, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 0, 10000, &rclBoundsIsotropic, &rclFrameIsotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, -10000, 0, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, -10000, 0, &rclBoundsIsotropic, &rclFrameIsotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 0, -10000, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 0, -10000, &rclBoundsIsotropic, &rclFrameIsotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, -10000, 10000, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, -10000, 10000, &rclBoundsIsotropic, &rclFrameIsotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 10000, -10000, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 10000, -10000, &rclBoundsIsotropic, &rclFrameIsotropic);
+
+  /* MSDN says that negative xExt and yExt values specify a ratio.
+     Check that this is wrong and the whole device surface is used */
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, -1000, -100, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+  checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, -1000, -100, &rclBoundsIsotropic, &rclFrameIsotropic);
+
+  /* Ordinary conversions */
+
+  if (getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 30000, 20000, &rclBounds, &rclFrame))
+  {
+    ok(rclFrame.left == 0 && rclFrame.top == 0 && rclFrame.right == 30000 && rclFrame.bottom == 20000,
+       "SetWinMetaFileBits (MM_ANISOTROPIC): rclFrame contains invalid values\n");
+    ok(rclBounds.left == 0 && rclBounds.top == 0 && rclBounds.right > rclBounds.bottom,
+       "SetWinMetaFileBits (MM_ANISOTROPIC): rclBounds contains invalid values\n");
+  }
+
+  if (getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 30000, 20000, &rclBounds, &rclFrame))
+  {
+    ok(rclFrame.left == 0 && rclFrame.top == 0 && rclFrame.right == 30000 && rclFrame.bottom == 20000,
+       "SetWinMetaFileBits (MM_ISOTROPIC): rclFrame contains invalid values\n");
+    ok(rclBounds.left == 0 && rclBounds.top == 0,
+       "SetWinMetaFileBits (MM_ISOTROPIC): rclBounds contains invalid values\n");
+
+    /* Wine has a rounding error */
+    diffx = rclBounds.right - rclBounds.bottom;
+    if (diffx < 0) diffx = -diffx;
+    ok(diffx <= 1, "SetWinMetaFileBits (MM_ISOTROPIC): rclBounds is not isotropic\n");
+  }
+
+  if (getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_HIMETRIC, 30000, 20000, &rclBounds, &rclFrame))
+  {
+    ok(rclFrame.right - rclFrame.left != 30000 && rclFrame.bottom - rclFrame.top != 20000,
+       "SetWinMetaFileBits: xExt and yExt must be ignored for mapping modes other than MM_ANISOTROPIC and MM_ISOTROPIC\n");
+  }
+
+  HeapFree(GetProcessHeap(), 0, buffer);
+}
+
 static BOOL (WINAPI *pGdiIsMetaPrintDC)(HDC);
 static BOOL (WINAPI *pGdiIsMetaFileDC)(HDC);
 static BOOL (WINAPI *pGdiIsPlayMetafileDC)(HDC);
@@ -1366,6 +1577,7 @@
 
     /* For metafile conversions */
     test_mf_conversions();
+    test_SetWinMetaFileBits();
 
     test_gdiis();
 }


More information about the wine-patches mailing list