代码之家  ›  专栏  ›  技术社区  ›  AlwaysLearningNewStuff

使用坐标空间和变换滚动和缩放增强的Windows图元文件

  •  0
  • AlwaysLearningNewStuff  · 技术社区  · 9 年前

    简介:

    我正在构建显示 .emf (增强的Windows图元文件)。

    如果图像不能放在窗口内,将使用滚动条显示不可见的部分。

    由于我正在处理图元文件,所以我也尝试添加缩放。

    相关信息:

    我正在玩 .emf(电动势) 内存设备上下文中的文件(来自磁盘)(使用 CreateCompatibleDC API)。然后我使用 BitBlt API将该图像传输到主窗口的客户端区域。我这么做是为了避免闪烁。

    阅读MSDN,我找到了有关 Using Coordinate Spaces and Transformations 并立即意识到它解决我缩放/滚动元文件的任务的潜力。

    问题:

    我不知道如何使用前面提到的API在内存设备上下文中缩放/滚动图元文件,所以我可以 比特布莱特 这是我第一次处理这类任务)。

    我解决问题的努力:

    我曾经尝试过 XFORM 矩阵实现缩放,如下所示:

    case WM_ERASEBKGND: // prevents flickering
        return 1L;
    case WM_PAINT:
    {
        hdc = BeginPaint(hWnd, &ps);
    
        // get main window's client rectangle
        RECT rc = { 0 };
        GetClientRect(hWnd, &rc);
        // fill rectangle with gray brush
        // this is necessery because I have bypassed WM_ERASEBKGND,
        // see above
        FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
        // OK, here is where I tried to tamper with the APIs 
        // I mentioned earlier
        // my goal would be to scale EMF down by half
        int prevGraphicsMode = SetGraphicsMode(hdc, GM_ADVANCED);
    
        XFORM zoomMatrix = { 0 };
    
        zoomMatrix.eDx = 0;  
        zoomMatrix.eDy = 0;
        zoomMatrix.eM11 = 0.5;
        zoomMatrix.eM12 = 0;
        zoomMatrix.eM21 = 0;
        zoomMatrix.eM22 = 0.5;
        // apply zooming factor
        SetWorldTransform(hdc, &zoomMatrix);
        // draw image
        HENHMETAFILE hemf = GetEnhMetaFile(L".\\Example.emf");
        PlayEnhMetaFile(hdc, hemf, &rc);
        DeleteEnhMetaFile(hemf);
        // restore original graphics mode
        SetGraphicsMode(hdc, prevGraphicsMode);
        // all done, end painting
        EndPaint(hWnd, &ps);
    }
        return 0L;
    

    enter image description here

    在上面的片段中,元文件被适当地缩放,并从客户端区域的左上角播放。

    我没有费心维护纵横比或图像居中。我现在的主要目标是找出如何使用 XFORM格式 矩阵缩放图元文件。

    到目前为止还不错,至少我是这么想的。

    我尝试对内存设备上下文执行上述操作,但当 BitBlit 在图像中我得到了可怕的像素化,并且 比特Blit ted图像未正确缩放。

    enter image description here

    下面是再现上述图像的小片段:

    static HDC memDC;  // in WndProc
    static HBITMAP bmp, bmpOld;  // // in WndProc; needed for proper cleanup
    
    case WM_CREATE:
    {
        HDC hdc = GetDC(hwnd);
        // create memory device context
        memDC = CreateCompatibleDC(hdc);
        // get main window's client rectangle
        RECT rc = { 0 };
        GetClientRect(hwnd, &rc);
        // create bitmap that we will draw on
        bmp = CreateCompatibleBitmap(hdc, rc.right - rc.left, rc.bottom - rc.top);
        // select bitmap into memory device context
        bmpOld = (HBITMAP)SelectObject( memDC, bmp );
        // fill rectangle with gray brush
        FillRect(memDC, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
        // scale EMF down by half
        int prevGraphicsMode = SetGraphicsMode(memDC, GM_ADVANCED);
    
        XFORM zoomMatrix = { 0 };
    
        zoomMatrix.eDx = 0;  
        zoomMatrix.eDy = 0;
        zoomMatrix.eM11 = 0.5;
        zoomMatrix.eM12 = 0;
        zoomMatrix.eM21 = 0;
        zoomMatrix.eM22 = 0.5;
        // apply zooming factor
        SetWorldTransform(memDC, &zoomMatrix);
        // draw image
        HENHMETAFILE hemf = GetEnhMetaFile(L".\\Example.emf");
        PlayEnhMetaFile(memDC, hemf, &rc);
        DeleteEnhMetaFile(hemf);
        // restore original graphics mode
        SetGraphicsMode(memDC, prevGraphicsMode);
        // all done end paint
        ReleaseDC(hwnd, hdc);
    }
        return 0L;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rc = {0};
        GetClientRect(hwnd, &rc);
        BitBlt(hdc, 0, 0, 
            rc.right - rc.left, 
            rc.bottom - rc.top,
            memDC, 0, 0, SRCCOPY);
        EndPaint(hwnd, &ps);
    }
        return 0L;
    case WM_DESTROY:
        SelectObject(memDC, bmpOld);
        DeleteObject(bmp);
        DeleteDC(memDC);
        PostQuitMessage(0);
        break;
    

    在仔细阅读了 比特Blit 我发现了以下重要部分:

    如果源设备上下文中存在其他转换(并且匹配的转换在目标设备上下文中无效),则根据需要拉伸、压缩或旋转目标设备上下文的矩形。

    使用 ModifyWorldTransform(memDC, NULL, MWT_IDENTITY); 作为用户 乔纳森·波特 建议修复此问题。

    就缩放而言,这些是我的尝试。然后我尝试使用 SetWindowOrgEx 以及类似的API。

    我用很少的实验就成功地移动了图像,但我还没有完全理解为什么会这样,以及如何这样做。

    原因是我无法完全理解窗口和视口原点等术语。这对我来说太抽象了。当我写这篇文章时,我再次重读,并试图自己解决这个问题。

    问题:

    • 如何使用API(从上面添加的链接)在内存DC中缩放/滚动/缩放和滚动图元文件 比特布莱特 它在主窗口设备上下文中?

    评论:

    我意识到代码示例可能很大,所以我没有问任何问题。我不希望人们为我编写代码,而是帮助我理解我必须做什么。我只需要完全掌握以我需要的方式应用上述API的概念。因此,如果合适,答案/注释可以包括指令和小伪代码。我意识到这个问题可能很宽泛,所以如果你能帮助我用建设性的批评和评论来缩小我的问题,我将不胜感激。

    感谢您阅读本文、提供帮助和理解。

    顺致敬意,

    1 回复  |  直到 9 年前
        1
  •  2
  •   Jonathan Potter    9 年前

    根据 SetWorldTransform 文档:

    无法重置设备的图形模式 上下文设置为默认的GM_COMPATIBLE模式,除非 转换已首先重置为默认标识 转型

    因此,即使您正在尝试重置图形模式,使用此调用:

    SetGraphicsMode(memDC, prevGraphicsMode);
    

    这实际上不起作用,因为您不是首先将转换重置为身份转换。在 SetGraphicsMode 调用重置转换并将DC返回到正常映射:

    ModifyWorldTransform(memDC, NULL, MWT_IDENTITY); 
    SetGraphicsMode(memDC, prevGraphicsMode);