Изменение размера окна вызывает размывание возле правой границы

Я создал стандартное приложение Windows Win32 в Visual Studio 2010. Единственное добавление, которое я сделал, - это вызов TextOut в обработчике WM_PAINT, который отображает алфавит (повторяется 4 раза по ширине) в позиции 0, 0.

Моя проблема в том, что когда я изменяю размер окна, расширяясь вправо, возникает некоторая ошибка рисования у правой боковой границы. Черные блоки отображаются во время изменения размера / рисования, как будто правый край растягивается. Результатом является странный черный эффект "смазывания" при изменении размера. Это происходит только во время изменения размера; как только я отпускаю мышь, окно выглядит правильно.

Я пробовал двойную буферизацию на DC памяти, но вижу тот же эффект. Я не использую какой-либо код темы Windows.

Единственный способ удалить эффект - это обработать WM_NCPAINT (и вернуть 0) - но, конечно, это означает, что граница не закрашена, что не будет приемлемым решением! Я упоминаю об этом на случай, если это поможет кому-нибудь с идеей.

Спасибо за любые идеи или помощь!

@Arx - Извини, я не прояснил себя. Когда я говорю, что границы размыты, я имел в виду правый край отображаемого текста, а не саму границу.

Это происходит, если я просто добавлю вызов TextOut в обработчик WM_PAINT. Обработка WM_ERASEBKGRND и установка кисти фона класса окна не имеет значения.

@ Дэвид - Извинения. Я вижу эффект после добавления только одной строки в стандартное приложение Win32 VS 2008, созданное мастером нового проекта - поэтому я не видел смысла в публикации более 200 строк кода с одной интересующей строкой:)

Я добавил эту строку в обработчик WM_PAINT:

TextOut (hdc, 0, 0, L"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ", 104);

Вот полная версия кода с добавленной двойной буферизацией. Размазывание текста с правой стороны (по краю окна) при расширении окна все еще происходит.

// win32_smearing_at_border.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "win32_smearing_at_border.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
TCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_WIN32_SMEARING_AT_BORDER, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32_SMEARING_AT_BORDER));

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style                  = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc        = WndProc;
    wcex.cbClsExtra         = 0;
    wcex.cbWndExtra         = 0;
    wcex.hInstance          = hInstance;
    wcex.hIcon                  = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32_SMEARING_AT_BORDER));
    wcex.hCursor                = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = 0; //(HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName       = MAKEINTRESOURCE(IDC_WIN32_SMEARING_AT_BORDER);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm                = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

  static HDC memory_dc;
  static HBITMAP memory_bmp;
  static HBITMAP oldbmp;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;

    case WM_ERASEBKGND:
    return 1;

    case WM_CREATE:
        // Create memory DC
    {
    HDC dc     = GetDC (hWnd);
        memory_dc  = CreateCompatibleDC (dc);
        memory_bmp = CreateCompatibleBitmap (dc, 2560, 1440); // arbitary values for testing, set to my current monitor size.
        oldbmp     = (HBITMAP)SelectObject(memory_dc, memory_bmp);
    TextOut (memory_dc, 0, 0, L"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ",104);
      ReleaseDC (hWnd, dc);
    }

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right-ps.rcPaint.left, ps.rcPaint.bottom-ps.rcPaint.top, memory_dc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
        EndPaint(hWnd, &ps);
        break;

    case WM_DESTROY:
        // Clean up memory DC
        SelectObject (memory_dc,oldbmp);
        DeleteObject (memory_bmp);
        DeleteDC     (memory_dc);

        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

Привет аркс

Большое спасибо за попытку кода - я ценю это. Приятно знать, что это работает для вас.

Но... Я испытываю очень странные результаты. У меня проблема с "размыванием" при работе на компьютере с Windows 7 x64. Однако, пробуя это на виртуальной машине Windows 7 (используя VMWare на той же машине), она работает отлично.

Так что на той же машине изначально она размывается, но практически нет. Я даже попробовал виртуальную машину Windows 8, и снова она работала нормально.

Я обнаружил, что если я выберу тему без Aero, все будет работать нормально (хотя без Aero это выглядит не очень хорошо). И все же на других машинах, где он работает, они выбирают темы Aero. Я действительно не понимаю.

Так что это не совсем решение. Я не хочу просить моих потенциальных клиентов отключить Aero.

Я пытался вызвать многие функции Dwm *, пытаясь найти разницу между рабочими и нерабочими машинами, но ничего не смог найти.

Если я вставлю этот код:

DWMNCRENDERINGPOLICY policy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(hWnd, 
                      DWMWA_NCRENDERING_POLICY, 
                      (void*)&policy, 
                      sizeof(DWMNCRENDERINGPOLICY));

Сразу после создания главного окна все снова работает нормально (хотя, опять же, не выглядит так хорошо без границы Aero).

Так что я действительно в растерянности относительно того, как я могу двигаться вперед. Любые идеи с благодарностью принимаются!

1 ответ

Я думаю, что смогу дать более подробное представление о том, откуда происходит это размытие / размытие и почему вы видите это более (или иначе) в Windows 8/10 Aero.

Тот факт, что ваш код имеет:

wcex.style = CS_HREDRAW | CS_VREDRAW;

это причина, почему вы не видели размывания / размытия на Win7. Это заставляет Windows заполнять новые открытые области окна, которые еще не были нарисованы вашим WM_PAINT, сплошным цветом, который не идеален, но не отвлекает.

Но под Windows 8/10 Aero все по-другому. Приложения не рисуют непосредственно на экране, а рисуют в закадровых буферах, которые затем создаются злым оконным менеджером DWM.exe. Оказывается, что DWM фактически добавляет еще один слой BitBltповедение поверх существующей устаревшей XP/Vista/7 BitBlt поведение, на которое влияет CS_HREDRAW | CS_VREDRAW,

Блиц-поведение DWM еще более сумасшедшее, потому что они не просто копируют клиентскую область, но фактически копируют пиксели по краям вашей старой клиентской области, чтобы создать новую.

К сожалению, заставить DWM не делать блиц намного сложнее, чем просто передать несколько дополнительных флагов.

У меня нет 100% -ного решения, но, пожалуйста, смотрите этот раздел вопросов и ответов для своего рода трюка с синхронизацией, который можно использовать для значительного уменьшения частоты, с которой DWM портится с вашей клиентской областью окна, что уменьшит размытие / размытие:

Как сгладить уродливый джиттер / мерцание / прыжок при изменении размеров окон, особенно перетаскивая левую / верхнюю границу (Win 7-10; bg, bitblt и DWM)?

Наслаждайтесь!

Другие вопросы по тегам