在绘图信号上保留 DrawingArea 'image'

Preserve DrawingArea 'image' on draw signal

我正在尝试制作简单的正方形,您可以在其中使用鼠标进行绘画。问题是,每当绘制信号发生时,cairo 表面似乎已完全清除。我理解这一点,因为在第一个 queue_draw() 白色背景消失后,我看到了我的 GTK 主题颜色(灰色)。

我以为我可以保存表面或上下文,但你不能只在开罗创建空表面,我不能使用 this->get_window()->create_cairo_surface() 创建它(其中 this 是 class 继承自 Gtk::DrawingArea) 因为当调用构造函数时,widget 还没有附加到任何 window,所以它是一个空指针。我的意思是,我可以创建一些名为 you_are_added_to_window_create_cairo_surface() 的 public 函数,但我真的不想这样做。

所以我真的不知道该怎么做,我对开罗有什么不了解。

如何保留或保存 'canvas' 当前状态,使实际绘制的内容仅应用于现有绘图?

这是我的回调函数 class:

bool MyDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context> & cr) {
    /* clear and fill background with white in the beginning */
    if (first_draw) {
        cr->save();
        cr->set_source_rgb(255.0, 255.0, 255.0);
        cr->paint();
        cr->restore();

        first_draw = false;
    }

    cr->save();

    cr->set_source_rgb(0.0, 0.0, 0.0);

    cr->begin_new_path();
    while (!dots_queue.empty()) {
        auto dot = dots_queue.front();
        cr->line_to(dot.first, dot.second);
        dots_queue.pop();
    }
    cr->close_path();
    cr->stroke();

    cr->restore();

    return false;
}

删除 first_draw 而不是 dots_queue.pop(),只需遍历 dots_queue 并每次都重绘它们。

draw 函数不适用于 "I want to add some drawing"。相反,它是 "hey, the windowing system has no idea what should be drawn here, please fill this with content"。这就是开罗表面被清除的原因。

因此,虽然存储所有动作都有效,但如果您试图让程序保存您的绘图,那真的不行,您将不得不使用第二个表面来保存所有内容。

我的解决方案结合了 Uli Schlachter 的两个答案。

首先,我有一个结构,我在其中存储自上次按下按钮到释放按钮的最后一次绘图操作。这让我可以实时显示诸如线条之类的东西,同时保持 canvas 干净。

其次,我将 canvas 上绘制的所有内容存储在一个表面上,它是这样创建的:

// this - is object of class, derived from DrawingArea
auto allocation = this->get_allocation(); 

this->surface = Cairo::ImageSurface::create(
    Cairo::Format::FORMAT_ARGB32,
    allocation.get_width(),
    allocation.get_height()
);

然后,在每个 draw 信号上,我这样恢复它:

cr->save();
cr->set_source(surface, 0.0, 0.0);
cr->paint();
cr->restore();

每当我想保存表面时,即将绘图应用于 canvas,我会执行以下操作:

Cairo::RefPtr<Cairo::Context> t_context = Cairo::Context::create(surface);
t_context->set_source(cr->get_target(), -allocation.get_x(), -allocation.get_y());
t_context->paint();

重要的时刻到了。如果不调整分配坐标,您的 canvas 将在每次表面保存和恢复时滑开。

有了它,我可以轻松地将绘图保存在 canvas、从文件加载 canvas(因为我使用的是 ImageSurface),或将其保存到文件。