【问题标题】:Preserve DrawingArea 'image' on draw signal在绘图信号上保留 DrawingArea 'image'
【发布时间】:2018-02-16 20:56:09
【问题描述】:

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

我以为我可以保存表面或上下文,但你不能只在 cairo 创建空表面,我不能使用 this->get_window()->create_cairo_surface() 创建它(其中 this 是从 Gtk::DrawingArea 继承的类的对象)因为在调用构造函数时,小部件还没有附加到任何窗口,所以它是一个空指针。我的意思是,我可以创建一些名为 you_are_added_to_window_create_cairo_surface() 的公共函数,但我真的不想这样做。

所以我真的不知道该怎么办,我不了解开罗。

如何保存或保存“画布”当前状态,以便实际绘制的内容仅应用于现有绘图?

这是我班级的回调函数:

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;
}

【问题讨论】:

    标签: c++ cairo gtkmm gtkmm3


    【解决方案1】:

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

    draw 函数并不意味着“我想添加一些绘图”。相反,它是“嘿,窗口系统不知道应该在这里绘制什么,请用内容填充”。这就是开罗表面被清除的原因。

    【讨论】:

    • 我想过这一点,但从长远来看,它不会造成 huuuuge 开销吗?我的意思是,我的应用程序不是为大图纸设计的,而是为小草图设计的,但无论如何......
    • 我什至不想想象一个人要画多长时间才能成为问题,但没关系:或者,您可以创建一个 cairo 图像表面,绘制到该表面并仅复制它在您的 on_draw 处理程序中显示。然而,当你的窗口被调整大小时你会怎么做呢?
    • 好的,你是对的 - 我尝试涂鸦大约 5 分钟,大约需要 150k 的内存。不是很好,但我什至没有尝试过优化内存。谢谢!
    【解决方案2】:

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

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

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

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

    // 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();
    

    每当我想保存表面时,即在画布上应用绘图时,我都会执行以下操作:

    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();
    

    这是重要的时刻。如果不调整分配坐标,您的画布将在每个表面保存和恢复时滑开。

    这样,我可以轻松地将我的绘图保存在画布上,从文件加载画布(因为我使用的是 ImageSurface),或者将其保存到文件中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-09
      • 2011-07-14
      • 2018-03-16
      • 1970-01-01
      • 2014-02-01
      • 2013-05-11
      • 2018-07-16
      相关资源
      最近更新 更多