0

我通过稍微修改 Microsoft 提供的Magnification API 示例创建了一个应用程序,以实现对在放大镜窗口中捕获和显示的每个帧的自定义转换。

我用 将MagSetImageScalingCallbackmy 函数设置为回调。调用回调没有问题,并且可以轻松地操作源图像和目标图像,因为原始位作为指针(srcdata 和 destdata)传递给回调。

使用设置为 16 ms (~60Hz) 的计时器刷新窗口。使用InvalidateRectAPI 调用刷新。问题是当放大镜窗口出现闪烁时。这种情况尤其发生在开始菜单出现时,如果启用“Peek”或每次在前台有一个具有动态内容的窗口时。

我试图拦截WM_ERASEBKGNDInvalidateRect使用 FALSE 作为第三个参数调用,但这没有帮助。我试图UpdateWindow在无效之前添加一个调用,但没有任何改变。

Windows 10 附带的放大镜应用程序没有同样的问题。我想知道为什么会发生这种情况以及如何摆脱闪烁。

为了重现问题,请从上面的链接下载 Magnification API 示例,然后将文件MagnifierSample.cpp中的内容替换为以下源代码:

// Ensure that the following definition is in effect before winuser.h is included.
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501    
#endif

#include <windows.h>
#include <wincodec.h>
#include <magnification.h>

// For simplicity, the sample uses a constant magnification factor.
#define RESTOREDWINDOWSTYLES WS_POPUP

// Global variables and strings.
HINSTANCE           hInst;
const TCHAR         WindowClassName[]= TEXT("MagnifierWindow");
const TCHAR         WindowTitle[]= TEXT("Screen Magnifier Sample");
const UINT          timerInterval = 16; // close to the refresh rate @60hz
HWND                hwndMag;
HWND                hwndHost;
RECT                magWindowRect;
RECT                hostWindowRect;

// Forward declarations.
ATOM                RegisterHostWindowClass(HINSTANCE hInstance);
BOOL                SetupMagnifier(HINSTANCE hinst);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
void CALLBACK       UpdateMagWindow(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
BOOL                isFullScreen = FALSE;

//
// FUNCTION: WinMain()
//
// PURPOSE: Entry point for the application.
//
int APIENTRY WinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE /*hPrevInstance*/,
                     _In_ LPSTR     /*lpCmdLine*/,
                     _In_ int       nCmdShow)
{
    if (FALSE == MagInitialize())
    {
        return 0;
    }
    if (FALSE == SetupMagnifier(hInstance))
    {
        return 0;
    }

    ShowWindow(hwndHost, nCmdShow);
    UpdateWindow(hwndHost);

    // Create a timer to update the control.
    UINT_PTR timerId = SetTimer(hwndHost, 0, timerInterval, UpdateMagWindow);

    // Main message loop.
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Shut down.
    KillTimer(NULL, timerId);
    MagUninitialize();
    return (int) msg.wParam;
}

//
// FUNCTION: HostWndProc()
//
// PURPOSE: Window procedure for the window that hosts the magnifier control.
//
LRESULT CALLBACK HostWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) 
    {
        case WM_ERASEBKGND:
            return TRUE;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;

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

    return 0;  
}

//
//  FUNCTION: RegisterHostWindowClass()
//
//  PURPOSE: Registers the window class for the window that contains the magnification control.
//
ATOM RegisterHostWindowClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex = {};

    wcex.cbSize = sizeof(WNDCLASSEX); 
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = HostWndProc;
    wcex.hInstance      = hInstance;
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(1 + COLOR_BTNFACE);
    wcex.lpszClassName  = WindowClassName;

    return RegisterClassEx(&wcex);
}

static BOOL CALLBACK myCallBack(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty) {

    UINT row;
    UINT column;
    BYTE *matrix;

    memset(destdata, 0, destheader.cbSize);
    matrix = (BYTE *) destdata;

    //Set alpha bits for the resulting image to 255 (fully opaque)
    for (row = 0; row < destheader.height; row++) {

        for (column = 3; column < destheader.width; column += 4) {

            matrix[(row * destheader.width) + column] = 0xFF;
        }
    }

    Sleep(20);
    return TRUE;
}


//
// FUNCTION: SetupMagnifier
//
// PURPOSE: Creates the windows and initializes magnification.
//
BOOL SetupMagnifier(HINSTANCE hinst)
{
    // Set bounds of host window according to screen size.
    hostWindowRect.top = 0;
    hostWindowRect.bottom = GetSystemMetrics(SM_CYSCREEN);
    hostWindowRect.left = 0;
    hostWindowRect.right = GetSystemMetrics(SM_CXSCREEN);

    // Create the host window.
    RegisterHostWindowClass(hinst);
    hwndHost = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED, WindowClassName, WindowTitle, RESTOREDWINDOWSTYLES, 0, 0, hostWindowRect.right, hostWindowRect.bottom, NULL, NULL, hInst, NULL);

    if (!hwndHost)
    {
        return FALSE;
    }

    // Make the window opaque.
    SetLayeredWindowAttributes(hwndHost, 0, 255, LWA_ALPHA);
    SetWindowLong(hwndHost, GWL_EXSTYLE, GetWindowLong(hwndHost, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_NOACTIVATE);

    // Create a magnifier control that fills the client area.
    magWindowRect = hostWindowRect;
    hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"), WS_CHILD | WS_VISIBLE, magWindowRect.left, magWindowRect.top, magWindowRect.right, magWindowRect.bottom, hwndHost, NULL, hInst, NULL );

    if (!hwndMag)
    {
        return FALSE;
    }

    MagSetImageScalingCallback(hwndMag, &myCallBack);
    // Set the source rectangle for the magnifier control.
    MagSetWindowSource(hwndMag, magWindowRect);

    return 1;  
}


//
// FUNCTION: UpdateMagWindow()
//
// PURPOSE: Sets the source rectangle and updates the window. Called by a timer.
//
void CALLBACK UpdateMagWindow(HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR /*idEvent*/, DWORD /*dwTime*/)
{
    // Reclaim topmost status, to prevent unmagnified menus from remaining in view. 
    SetWindowPos(hwndHost, HWND_TOPMOST, 0, 0, magWindowRect.right, magWindowRect.bottom, SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);

    // Force redraw.
    InvalidateRect(hwndMag, NULL, FALSE);
}

请注意,我添加了设置回调的代码

BOOL ret = MagSetImageScalingCallback(hwndMag, &myCallBack);

然后我创建了这个回调:

static BOOL CALLBACK myCallBack(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty) 

Sleep语句“模拟”了我的自定义转换所花费的时间。

如果您使用 Edge 打开 youtube 视频,然后运行​​MagnifierSample.exe可执行文件,您应该会看到一个黑屏闪烁,当屏幕闪烁时,您应该看到放大镜窗口后面的内容(黑屏)。这正是我的应用程序中正在发生的事情。睡眠值设置为 20,但我不知道回调实际需要多长时间。我只是猜测它可能需要超过 16 毫秒。

要重现该问题,请执行以下步骤:

  1. 手动运行 MagnifierSample.exe(不要使用调试器运行它)
  2. 您应该会看到黑屏
  3. 按窗口键
  4. 在前台设置一个带有视频或动态内容的窗口
  5. 点击黑色窗口让任务栏消失
  6. 您应该看到屏幕有时会闪烁

是否可以设置类似于MagSetImageScalingCallback普通窗口句柄的回调?我知道我可以使用 WindowProc 回调来拦截发送到窗口的消息但是我无权访问 rawbits,除非我使用CreateDIBSectionSelectObject等等......但当时图像已经发送到目标窗口,我没有机会改造它。

4

0 回答 0