【问题标题】:How to draw in C++ with Cairo to a window using GDI (instead of creating an image)?如何在 C++ 中使用 Cairo 绘制到使用 GDI 的窗口(而不是创建图像)?
【发布时间】:2013-04-16 21:00:28
【问题描述】:

我设法找到了这段代码 sn-p 并用 Cairo 编译它:

#define LIBCAIRO_EXPORTS
#include <cairo/cairo.h>
#include <cairo/cairo-win32.h>

int main(int argc, char** argv)
{
    cairo_surface_t *surface;
    cairo_t *cr;
    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 240, 80);
    cr = cairo_create (surface);

    cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL,
                                         CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size (cr, 32.0);
    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
    cairo_move_to (cr, 10.0, 50.0);
    cairo_show_text (cr, "Hello, World");
    cairo_destroy (cr);
    cairo_surface_write_to_png (surface, "hello.png");
    cairo_surface_destroy (surface);

    return 0;
}

如您所见,它创建了一个带有文本“Hello World”的图像并将其保存在驱动器上。 如何创建一个 win32 表面并改为绘制到窗口?

我使用失败:cairo_win32_surface_create

它需要hdc,我不知道那是什么。我尝试阅读一些教程,但似乎没有一个可以引导您完成打印到新窗口。

我找到了这个链接: http://windrealm.org/cairo-gdi/

它有一个工作演示,但它使用int WINAPI WinMain。我不想用那个。

【问题讨论】:

  • HDC 是设备上下文的句柄,它基本上是一个虚拟画布,其状态包括选定的笔、选定的画笔和选定的位图。我希望这至少会有所帮助。
  • 如果你不熟悉 Windows GDI 开发,你可能想看看here。您可能对Device ContextsBitmaps 最感兴趣
  • 我发现这个链接显示了如何使用除 win32 gdi 之外的所有内容:zetcode.com/gfx/cairo/cairobackends
  • 我还发现这似乎部分回答了我的问题:windrealm.org/cairo-gdi 但它使用了我希望避免的 winmain。有可能吗?
  • 您可以从控制台程序创建一个窗口,但您必须运行一个消息循环才能正确操作它。从使用 winmain 的示例开始会更容易。

标签: c++ c cairo


【解决方案1】:

有几种方法可以在 Windows 中获取设备上下文句柄。例如,WM_PAINT 处理程序通常会调用BeginPaint 来获取设备上下文,将内容更新到窗口,然后调用EndPaint。在无法使用BeginPaintEndPaint 的情况下(例如在WM_PAINT 消息之外),您可以使用GetDC 更新窗口,然后调用ReleaseDC

BeginPaintGetDC 返回的设备上下文允许您直接绘制到窗口。但是,有时您想直接绘制到位图。在这种情况下,您将调用CreateCompatibleDC,选择您要绘制的位图,进行绘制,然后调用DeleteDC

在您的情况下,您正在寻找类似于以下内容的内容:

HDC dc = GetDC(windowHandle);
cairo_win32_surface_create(dc);
ReleaseDC(windowHandle, dc);

如果您不想在此阶段创建窗口(因为您只是保存图像),您可能可以使用桌面窗口来获取设备上下文。

HWND windowHandle = GetDesktopWindow();
HDC dc = GetDC(windowHandle);
cairo_win32_surface_create(dc);
ReleaseDC(windowHandle, dc);

还有其他调用可用于获取或创建设备上下文。你可以找到这些和相关函数的列表here

创建和使用窗口会涉及更多内容,因此我将向您提供基本步骤和一些示例代码,您可以使用这些代码进行操作。创建和使用窗口...

  • RegisterClass注册一个窗口类
  • 使用CreateWindowCreateWindowEx 创建窗口
  • 通过调用GetMessageTranslateMessageDispatchMessage 使用消息泵处理消息

此外,您还必须实现一个函数来处理窗口消息的处理,例如WM_PAINT

注意:以下代码未经测试,但应该是正确的。

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if(message == WM_PAINT)
    {
        HDC dc;
        PAINTSTRUCT ps;
        dc = BeginPaint(hwnd, &ps);

        // do your drawing here

        EndPaint(hwnd, &ps);
    }

    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szClassName[] = TEXT("DrawSurfaceClass");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;

    ///////////////////////////////////////////////////////////
    //  Register a window "class"
    ///////////////////////////////////////////////////////////
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = WndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInstance;
    wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground =(HBRUSH)COLOR_WINDOW;
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = szClassName;

    if(!RegisterClass(&wndclass))
    {
        //  error
        return 1;
    }

    ///////////////////////////////////////////////////////////
    //  Create the window and display it (if iCmdShow says so)
    ///////////////////////////////////////////////////////////
    hwnd = CreateWindow(
        szAppName,
        TEXT("Draw Surface"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);


    ///////////////////////////////////////////////////////////
    //  Run the message pump so the window proc recieves events
    ///////////////////////////////////////////////////////////
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

【讨论】:

  • +1,但在 WM_PAINT 之外绘画通常是个坏主意。
  • 如果你仔细看这个问题,你会发现它是一个没有消息循环的控制台程序。 WM_PAINT 是正确的,但完整的答案比这要复杂得多。
  • 什么是窗口句柄?我看过很多次提到“hwnd”。如何获得?
  • @MarkRansom 控制台应用程序不会阻止您使用消息泵或阻止您创建窗口。我正在编辑它以包含窗口创建和消息循环。
【解决方案2】:

我发现初学者打开一个窗口并在其中画一些线的最简单方法是下载和设置 gtk+ 并使用 ZetCode 的教程。

您可能需要从这里下载 GTK: http://gtk.hexchat.org/

设置 Gtk+: Using GTK+ in Visual C++

开罗教程: http://zetcode.com/gfx/cairo/

记得将所有 GTK 的 dll 文件包含在你编译的可执行文件所在的目录中。

【讨论】:

    猜你喜欢
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 2010-10-23
    • 2013-06-05
    • 2021-11-09
    • 1970-01-01
    相关资源
    最近更新 更多