Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/kubernetes/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在Windows中平滑调整窗口大小(使用Direct2D 1.1)?_C++_Windows_Winapi_Paint_Direct2d - Fatal编程技术网

C++ 在Windows中平滑调整窗口大小(使用Direct2D 1.1)?

C++ 在Windows中平滑调整窗口大小(使用Direct2D 1.1)?,c++,windows,winapi,paint,direct2d,C++,Windows,Winapi,Paint,Direct2d,在windows中调整窗口的大小并不像我希望的那样“平滑”,这让我很恼火(windows程序通常都是这样,而不仅仅是我自己的。Visual Studio就是一个很好的例子)。它让操作系统及其程序感觉“脆弱”和“廉价”(是的,我关心程序和用户界面的感觉,就像我关心关车门的声音和感觉一样。这反映了构建质量),在我看来,这会影响整个用户体验,并最终影响品牌的感知 窗口内容的重画根本跟不上调整大小期间鼠标的移动。每当我调整窗口大小时,都会出现“断断续续”/“闪烁”的效果,这似乎是由于在绘制新的、调整大小

在windows中调整窗口的大小并不像我希望的那样“平滑”,这让我很恼火(windows程序通常都是这样,而不仅仅是我自己的。Visual Studio就是一个很好的例子)。它让操作系统及其程序感觉“脆弱”和“廉价”(是的,我关心程序和用户界面的感觉,就像我关心关车门的声音和感觉一样。这反映了构建质量),在我看来,这会影响整个用户体验,并最终影响品牌的感知

窗口内容的重画根本跟不上调整大小期间鼠标的移动。每当我调整窗口大小时,都会出现“断断续续”/“闪烁”的效果,这似乎是由于在绘制新的、调整大小的内容之前,在新的、调整大小的窗口框架中重新绘制了窗口先前的大小内容

我正在构建一个使用Direct2D 1.1绘制UI的Win32应用程序(x64),考虑到Direct2D的速度,我认为在2014年的操作系统中应该没有必要出现这种瑕疵。我自己也使用Windows 8.1,但目标是Windows 7及更高版本的应用程序

当最大化一个小窗口时,“以前的大小”效果尤其明显(因为窗口大小的差异足够大,以便在较大窗口的左上角短暂闪烁的旧内容图像与随后在其上绘制的新内容图像形成对比)

这似乎是正在发生的事情:

  • (假设屏幕上有一个完全渲染的窗口,大小为500 x 500像素)
  • 我将窗口最大化:
  • 窗框最大化
  • 旧的500 x 500内容将在新框架中绘制,然后
  • ..最大化的窗口用大小适当的内容重新绘制
  • 我想知道是否有任何方法可以缓解这一问题(例如,通过截取Windows消息来摆脱步骤4),并避免在新内容最终重新呈现之前,用旧内容以新大小重新绘制窗口。就像Windows在要求我用WM_PAINT消息或类似消息提供更新内容之前,使用它已有的任何图形重新绘制自己的窗口

    能做到吗

    编辑:似乎
    WM_WINDOWPOSCHANGING
    /
    WM_size
    提供了对新尺寸数据的“早期访问”,但我仍然没有成功抑制旧内容的绘制

    我的
    WndProc
    如下所示:

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_ERASEBKGND:
            return 1;
        case WM_PAINT:
            PAINTSTRUCT ps;
            BeginPaint(hWnd, &ps);
            D2DRender();
            EndPaint(hWnd, &ps);
            return 0;
        case WM_SIZE:
            if (DeviceContext && wParam != SIZE_MINIMIZED)
            {
                D2DResizeTargetBitmap();
                D2DRender();
            }
            return 0;
        case WM_DISPLAYCHANGE:
            D2DRender();
            return 0;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    
    该窗口未设置
    CS\u HREDRAW
    CS\u VREDRAW
    。swapchain是双缓冲的,
    Present
    调用是在SyncInterval=0的情况下进行的


    我知道,与静态窗口表面上的普通重画相比,每次窗口大小更改时重新创建swapchain缓冲区确实会产生一些开销。然而,“口吃”并不是由此引起的,因为即使在禁用缓冲区大小调整并且在窗口大小调整过程中简单地缩放现有窗口内容时也会发生这种情况(尽管这确实使其更好地跟上鼠标移动)。

    尽管您的目标值得称赞,我怀疑,任何这样做的企图都会以你和Windows之间的争斗而告终——你不会赢(尽管你可能会设法以体面的方式赢得平局)。很抱歉是否定的。

    将WM_SETREDRAW设置为FALSE,进行大小调整,然后重新启用绘图,使窗口无效,操作系统将使其无效


    当从列表中选择不同的项目时,我已经对按钮启用和禁用按钮执行了此操作,而不是对整个窗口执行此操作。

    如果您有一个固定大小(与全屏分辨率相同)的无边框子窗口(仅在父窗口内呈现的类型),该怎么办,您应该会得到更平滑的结果,因为没有内存重新分配(我认为这是导致紧张的原因)

    如果它仍然不完美,请查看WM_大小和WM_大小,并检查您是否可以对它们施展魔法。例如,在WM_SIZING上,您可以返回true,告诉Windows您处理了消息(保持窗口不变),然后使用WM_SIZING提供的大小将UI重新呈现到缓冲区,完成后,您可以发送自己的WM_SIZING,但在WPARAM中有一个被操纵的未使用位(及其以前的内容)这就告诉你你有一个预渲染的缓冲区,你可以直接将其删除。从msdn上的WM_尺寸文档来看,WPARAM应该有一些位供您使用


    希望这能有所帮助。

    这是我想到的最好的方法,而且调整的很好,虽然backbuffer blitting会导致一些边缘闪烁,还没有用DX或OGL进行测试,但它在硬件加速方面应该会更好。它有点笨重,但可以作为概念的证明

    如果可以在不使用MDI的情况下剪裁画布,那会更好,比如使用位掩码缓冲区

    我不满意的一件事是子窗口的位置坐标,因为它们可能不适用于所有系统,但通过组合使用GetSystemMetrics调用来获取边框和标题大小应该可以解决这一问题

    /* Smooth resizing of GDI+ MDI window
     * 
     * Click window to resize, hit Escape or Alt+F4 to quit
     * 
     * Character type is set to multibyte
     * Project->Properties->Config Properties->General->Character Set = Multibyte
     * 
     * Pritam 2014 */
    
    
    // Includes
    #include <Windows.h>
    #include <gdiplus.h>
    #pragma comment (lib,"Gdiplus.lib")
    using namespace Gdiplus;
    
    
    // Max resolution
    #define XRES 1600
    #define YRES 900
    
    
    // Globals
    bool resizing = false;
    HWND parent, child;        // child is the canvas window, parent provides clipping of child
    Bitmap * buffer;
    
    
    // Render
    void Render() {
    
        // Get parent client size
        RECT rc;
        GetClientRect(parent, &rc);
    
        // Draw backbuffer
        Graphics * g = Graphics::FromImage(buffer);
    
            // Clear buffer
            g->Clear(Color(100, 100, 100));
    
            // Gray border
            Pen pen(Color(255, 180, 180, 180));
            g->DrawRectangle(&pen, 10, 10, rc.right - 20, rc.bottom - 20);
            pen.SetColor(Color(255, 0, 0, 0));
            g->DrawRectangle(&pen, 0, 0, rc.right - 1, rc.bottom - 1);
    
        // Draw buffer to screen
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(child, &ps);
        Graphics graphics(hdc);
    
            graphics.DrawImage(buffer, Point(0, 0));
    
        // Free
        EndPaint(child, &ps);
    }
    
    
    // MDI Callback
    LRESULT CALLBACK MDICallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
        switch(message) {
        case WM_LBUTTONDOWN:
            resizing = true; // Start resizing
            return 0;
            break;
        case WM_KEYDOWN:
            if(wparam == VK_ESCAPE) { // Exit on escape
                PostQuitMessage(0);
            }
            TranslateMessage((const MSG *)&message);
            return 0;
            break;
        case WM_PAINT:
            Render();
            return 0;
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
            break;
        }
    
        return DefMDIChildProc(hwnd, message, wparam, lparam);
    }
    
    
    // Parent window callback
    LRESULT CALLBACK WndCallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
        return DefFrameProc(hwnd, child, message, wparam, lparam);
    }
    
    
    // Create windows
    bool CreateWindows(void) {
    
        // Parent class
        WNDCLASSEX wndclass;
        ZeroMemory(&wndclass, sizeof(wndclass)); wndclass.cbSize = sizeof(wndclass);
    
            wndclass.style = CS_NOCLOSE;
            wndclass.lpfnWndProc = WndCallback;
            wndclass.hInstance = GetModuleHandle(NULL);
            wndclass.lpszClassName = "WNDCALLBACKPARENT";
            wndclass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
            wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    
        if(!RegisterClassEx(&wndclass)) return false;
    
            // MDI class
            wndclass.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
            wndclass.lpfnWndProc = MDICallback;
            wndclass.lpszClassName = "MDICALLBACKCANVAS";
    
        if(!RegisterClassEx(&wndclass)) return false;
    
    
        // Parent window styles
        DWORD style = WS_POPUP | WS_CLIPCHILDREN;
        DWORD exstyle = 0;
    
            // Set initial window size and position
            RECT rc;
            rc.right = 640;
            rc.bottom = 480;
    
            AdjustWindowRectEx(&rc, style, false, exstyle);
    
            rc.left = 20;
            rc.top = 20;
    
        // Create window
        if(!(parent = CreateWindowEx(exstyle, "MDICLIENT", "MDI Resize", style, rc.left, rc.top, rc.right, rc.bottom, NULL, NULL, wndclass.hInstance, NULL))) return false;
    
    
        // MDI window styles
        style = MDIS_ALLCHILDSTYLES;
        exstyle = WS_EX_MDICHILD;
    
            // Set MDI size
            rc.left = - 8; // The sizes occupied by borders and caption, if position is not correctly set an ugly caption will appear
            rc.top = - 30;
            rc.right = XRES;
            rc.bottom = YRES;
            AdjustWindowRectEx(&rc, style, false, exstyle);
    
        // Create MDI child window
        if(!(child = CreateWindowEx(exstyle, "MDICALLBACKCANVAS", "", style, rc.left, rc.top, rc.right, rc.bottom, parent, NULL, wndclass.hInstance, NULL))) return 8;
    
            // Finalize
            ShowWindow(child, SW_SHOW);
            ShowWindow(parent, SW_SHOWNORMAL);
    
        // Success
        return true;
    }
    
    
    // Resize
    void Resize(void) {
    
        // Init
        RECT rc, rcmdi;
        GetClientRect(child, &rcmdi); // Use mdi window size to set max resize for parent
        GetWindowRect(parent, &rc);
    
        // Get mouse position
        POINT mp;
        GetCursorPos(&mp);
    
            // Set new size
            rc.right = mp.x - rc.left + 10;
            rc.bottom = mp.y - rc.top + 10;
    
            // Apply min & max size
            if(rc.right < 240) rc.right = 240; if(rc.bottom < 180) rc.bottom = 180;
            if(rc.right > rcmdi.right) rc.right = rcmdi.right; if(rc.bottom > rcmdi.bottom) rc.bottom = rcmdi.bottom;
    
        // Update window size
        SetWindowPos(parent, NULL, rc.left, rc.top, rc.right, rc.bottom, SWP_NOZORDER | SWP_NOMOVE);
    
            // Make sure client is entirely repainted
            GetClientRect(child, &rc);
            InvalidateRect(child, &rc, false);
            UpdateWindow(child);
    
        // Stop resizing if mousebutton is up
        if(!(GetKeyState(VK_LBUTTON) & 1 << (sizeof(short) * 8 - 1)))
            resizing = false;
    }
    
    
    // Main
    int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE pinstance, LPSTR cmdline, int cmdshow) {
    
        // Initiate GDI+
        ULONG_PTR gdiplusToken;
        GdiplusStartupInput gdiplusStartupInput;
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
        buffer = new Bitmap(XRES, YRES, PixelFormat24bppRGB);
    
        // Create windows
        if(!CreateWindows()) return 1;
    
    
        // Main loop
        bool running = true;
        MSG message;
        while(running) {
    
            // Check message or pass them on to window callback
            if(PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
                if(message.message == WM_QUIT) {
                    running = false;
                } else {
                    if(!TranslateMDISysAccel(child, &message)) {
                        TranslateMessage(&message);
                        DispatchMessage(&message);
                    }
                }
            }
    
            // Resize
            if(resizing)
                Resize();
    
            // Sleep a millisecond to spare the CPU
            Sleep(1);
        }
    
    
        // Free memmory and exit
        delete buffer;
        GdiplusShutdown(gdiplusToken);
        return 0;
    }
    
    /*平滑调整GDI+MDI窗口的大小
    * 
    *单击窗口以调整大小,单击Escape或Alt+F4退出
    * 
    *字符类型设置为多字节
    *项目->属性->配置属性->常规->字符集=多字节
    * 
    *Pritam 2014*/
    //包括
    #包括
    #包括
    #pragma注释(lib,“Gdiplus.lib”)
    使用名称空间Gdiplus;
    //最大分辨率
    #定义XRES 1600
    #定义YRES 900
    //全球的
    bool-resizing=false;
    HWND父项,子项;//子窗口是画布窗口,父窗口提供子窗口的剪辑
    位图*缓冲区;
    //渲染
    void Render(){
    //获取父客户端大小
    RECT-rc;
    GetClientRect(父级和rc);
    //拉回缓冲器
    Graphics*g=Graphics::FromImage(缓冲区);
    //清除缓冲区
    g->Clear(颜色(100100100));
    //灰色边框
    钢笔(科罗拉多州)
    
    // Escape to quit, left mousebutton to move window, right mousebutton to resize.
    // And again char set must be multibyte
    
    // Include
    #include <Windows.h>
    #include <gdiplus.h>
    #pragma comment (lib,"Gdiplus.lib")
    using namespace Gdiplus;
    
    
    // Globals
    Bitmap * backbuffer;
    int xres, yres;
    bool move, size;
    POINT framePos, frameSize, mouseOffset;
    
    // Renders the backbuffer
    void Render(void) {
        if(!backbuffer) return;
    
        // Clear window with mask color
        Graphics * gfx = Graphics::FromImage(backbuffer);
        gfx->Clear(Color(255, 0, 255));
    
        // Draw stuff
        SolidBrush brush(Color(120, 120, 120));
        gfx->FillRectangle(&brush, framePos.x, framePos.y, frameSize.x, frameSize.y);
    }
    
    // Paints the backbuffer to window
    void Paint(HWND hwnd) {
        if(!hwnd) return;
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        Graphics gfx(hdc);
        gfx.DrawImage(backbuffer, Point(0, 0));
        EndPaint(hwnd, &ps);
    }
    
    
    void HandleMove(HWND hwnd) {
    
        // Get mouse position
        POINT mouse;
        GetCursorPos(&mouse);
    
        // Update frame position
        framePos.x = mouse.x - mouseOffset.x;
        framePos.y = mouse.y - mouseOffset.y;
    
        // Redraw buffer and invalidate & update window
        Render();
        InvalidateRect(hwnd, NULL, false);
        UpdateWindow(hwnd);
    
        // Stop move
        if(!(GetKeyState(VK_LBUTTON) & 1 << (sizeof(short) * 8 - 1)))
            move = false;
    }
    
    void HandleSize(HWND hwnd) {
    
        // Get mouse position
        POINT mouse;
        GetCursorPos(&mouse);
    
        // Update frame size
        frameSize.x = mouse.x + mouseOffset.x - framePos.x;
        frameSize.y = mouse.y + mouseOffset.y - framePos.y;
    
        //frameSize.x = mouse.x + mouseOffset.x;
        //frameSize.y = mouse.y + mouseOffset.y;
    
        // Redraw buffer and invalidate & update window
        Render();
        InvalidateRect(hwnd, NULL, false);
        UpdateWindow(hwnd);
    
        // Stop size
        if(!(GetKeyState(VK_RBUTTON) & 1 << (sizeof(short) * 8 - 1)))
            size = false;
    }
    
    
    LRESULT CALLBACK WindowCallback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    
        POINTS p;
    
        switch(msg) {
        case WM_KEYDOWN:
            if(wparam == VK_ESCAPE) PostQuitMessage(0);
            return 0;
            break;
        case WM_LBUTTONDOWN:
            p = MAKEPOINTS(lparam); // Get mouse coords
            mouseOffset.x = p.x - framePos.x;
            mouseOffset.y = p.y - framePos.y;
            move = true;
            break;
        case WM_RBUTTONDOWN:
            p = MAKEPOINTS(lparam);
            mouseOffset.x = framePos.x + frameSize.x - p.x;
            mouseOffset.y = framePos.y + frameSize.y - p.y;
            size = true;
            break;
        case WM_PAINT:
            Paint(hwnd);
            return 0;
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
            break;
        }
        return DefWindowProc(hwnd, msg, wparam, lparam);
    }
    
    
    // Main
    int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE pinstance, LPSTR cmdline, int cmdshow) {
    
        // Init resolution, frame
        xres = GetSystemMetrics(SM_CXSCREEN);
        yres = GetSystemMetrics(SM_CYSCREEN);
    
        move = false; size = false;
        framePos.x = 100; framePos.y = 80;
        frameSize.x = 320; frameSize.y = 240;
        mouseOffset.x = 0; mouseOffset.y = 0;
    
        // Initiate GDI+
        ULONG_PTR gdiplusToken;
        GdiplusStartupInput gdiplusStartupInput;
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
        // Init backbuffer
        backbuffer = ::new Bitmap(xres, yres, PixelFormat24bppRGB);
        Render();
    
    
        // Window class
        WNDCLASSEX wc; ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc);
    
        wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
        wc.lpfnWndProc = WindowCallback;
        wc.hInstance = GetModuleHandle(NULL);
        wc.lpszClassName = "SingleResizeCLASS";
        wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
        wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    
        if(!RegisterClassEx(&wc)) return 1;
    
    
        // Create window
        HWND hwnd;
        DWORD style = WS_POPUP;
        DWORD exstyle = WS_EX_LAYERED;
        if(!(hwnd = CreateWindowEx(exstyle, wc.lpszClassName, "Resize", style, 0, 0, xres, yres, NULL, NULL, wc.hInstance, NULL)))
            return 2;
    
            // Make window fully transparent to avoid the display of unpainted window
            SetLayeredWindowAttributes(hwnd, 0, 0, LWA_ALPHA);
    
        // Finalize
        ShowWindow(hwnd, SW_SHOWNORMAL);
        UpdateWindow(hwnd);
    
        // Make window fully opaque, and set color mask key
        SetLayeredWindowAttributes(hwnd, RGB(255, 0, 255), 0, LWA_COLORKEY);
    
    
        // Main loop
        MSG msg;
        bool running = true;
        while(running) {
    
            // Check message
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                if(msg.message == WM_QUIT) {
                    running = false;
                } else {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
    
            // Move or size frame
            if(move) { HandleMove(hwnd); }
            if(size) { HandleSize(hwnd); }
    
            Sleep(1);
        }
    
        // Free memory
        ::delete backbuffer;
        backbuffer = NULL;
        GdiplusShutdown(gdiplusToken);
    
        // Exit
        return 0;
    }