spice-gtk图片渲染显示

1、监听draw的画图信号,使用了cairo库来实现图片画到界面上去, 其中, Cairo是一套用于绘制2D矢量图形库。官网: cairographics.org
static void spice_display_init(SpiceDisplay *display)
{
    GtkWidget *widget = GTK_WIDGET(display);
    GtkWidget *area;
    SpiceDisplayPrivate *d;
    GtkTargetEntry targets = { "text/uri-list", 0, 0 };
    d = display->priv = spice_display_get_instance_private(display);
    d->stack = GTK_STACK(gtk_stack_new());
    gtk_container_add(GTK_CONTAINER(display), GTK_WIDGET(d->stack));
    //画的区域
    area = gtk_drawing_area_new();
    g_object_connect(area,
                     "signal::draw", draw_event, display,
                     "signal::realize", drawing_area_realize, display,
                     NULL);
    gtk_stack_add_named(d->stack, area, "draw-area");
    gtk_stack_set_visible_child(d->stack, area);
#if HAVE_EGL
    area = gtk_gl_area_new();
    gtk_gl_area_set_required_version(GTK_GL_AREA(area), 3, 2);
    gtk_gl_area_set_auto_render(GTK_GL_AREA(area), false);
    g_object_connect(area,
                     "signal::render", gl_area_render, display,
                     "signal::realize", gl_area_realize, display,
                     NULL);
    gtk_stack_add_named(d->stack, area, "gl-area");
#endif
    area = gtk_drawing_area_new();
    gtk_stack_add_named(d->stack, area, "gst-area");
    g_object_connect(area,
                     "signal::draw", gst_draw_event, display,
                     "signal::size-allocate", gst_size_allocate, display,
                     NULL);
    d->label = gtk_label_new(NULL);
    gtk_label_set_selectable(GTK_LABEL(d->label), true);
    gtk_stack_add_named(d->stack, d->label, "label");
    gtk_widget_show_all(widget);
    g_signal_connect(display, "grab-broken-event", G_CALLBACK(grab_broken), NULL);
    g_signal_connect(display, "grab-notify", G_CALLBACK(grab_notify), NULL);
    gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, &targets, 1, GDK_ACTION_COPY);
    g_signal_connect(display, "drag-data-received",
                     G_CALLBACK(drag_data_received_callback), NULL);
    g_signal_connect(display, "size-allocate", G_CALLBACK(size_allocate), NULL);
    gtk_widget_add_events(widget,
                          GDK_POINTER_MOTION_MASK |
                          GDK_BUTTON_PRESS_MASK |
                          GDK_BUTTON_RELEASE_MASK |
                          GDK_BUTTON_MOTION_MASK |
                          GDK_ENTER_NOTIFY_MASK |
                          GDK_LEAVE_NOTIFY_MASK |
                          GDK_KEY_PRESS_MASK |
                          /* on Wayland, only smooth-scroll events are emitted */
                          GDK_SMOOTH_SCROLL_MASK |
                          GDK_SCROLL_MASK);
    gtk_widget_set_can_focus(widget, true);
    gtk_event_box_set_above_child(GTK_EVENT_BOX(widget), true);
    d->grabseq = spice_grab_sequence_new_from_string("Control_L+Alt_L");
    d->activeseq = g_new0(gboolean, d->grabseq->nkeysyms);
#ifdef HAVE_WAYLAND_PROTOCOLS
    if GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(widget))
        spice_wayland_extensions_init(widget);
#endif
}
2、spice-lib告诉spice-gtk显示区域进行刷新
static void invalidate(SpiceChannel *channel, gint x, gint y, gint w, gint h, gpointer data)
{
    SpiceDisplay *display = data;
    SpiceDisplayPrivate *d = display->priv;
    int display_x, display_y;
    int x1, y1, x2, y2;
    double s;
    gint scale_factor;
    GdkRectangle rect = {
        .x = x,
        .y = y,
        .width = w,
        .height = h
    };
#if HAVE_EGL
    set_egl_enabled(display, false);
#endif
    if (!gtk_widget_get_window(GTK_WIDGET(display)))
        return;
    if (!gdk_rectangle_intersect(&rect, &d->area, &rect))
        return;
    if (d->canvas.convert)
        do_color_convert(display, &rect);
    scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(display));
    spice_display_get_scaling(display, &s,
                              &display_x, &display_y,
                              NULL, NULL);
    display_x /= scale_factor;
    display_y /= scale_factor;
    if (s * scale_factor > 1) {
        rect.x -= 1;
        rect.y -= 1;
        rect.width += 2;
        rect.height += 2;
    }
    x1 = floor ((rect.x - d->area.x) * s) / scale_factor;
    y1 = floor ((rect.y - d->area.y) * s) / scale_factor;
    x2 = ceil ((rect.x - d->area.x + rect.width) * s) / scale_factor;
    y2 = ceil ((rect.y - d->area.y + rect.height) * s) / scale_factor;
    queue_draw_area(display,
                    display_x + x1, display_y + y1,
                    x2 - x1, y2 - y1);
}
放入将刷新的区域(x,y,width和height)告诉gtk主线程的画图队列
static void queue_draw_area(SpiceDisplay *display, gint x, gint y,
                            gint width, gint height)
{
    if (!gtk_widget_get_has_window(GTK_WIDGET(display))) {
        GtkAllocation allocation;
        gtk_widget_get_allocation(GTK_WIDGET(display), &allocation);
        x += allocation.x;
        y += allocation.y;
    }
    //触发调用signal::draw信号
    gtk_widget_queue_draw_area(GTK_WIDGET(display),x, y, width, height);
}
3、gtk主线程调用画图事件回调函数 draw_event
static gboolean draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
{
    SpiceDisplay *display = SPICE_DISPLAY(data);
    SpiceDisplayPrivate *d = display->priv;
    g_return_val_if_fail(d != NULL, false);
#if HAVE_EGL
    if (egl_enabled(d) &&
        g_str_equal(gtk_stack_get_visible_child_name(d->stack), "draw-area")) {
        spice_egl_update_display(display);
        return false;
    }
#endif
    if (d->mark == 0 || d->canvas.data == NULL ||
        d->area.width == 0 || d->area.height == 0)
        return false;
    spice_cairo_draw_event(display, cr);
    update_mouse_pointer(display);
    return true;
}
4、调用cairo库最终画图片,图片数据从 d->canvas.surface中获取
G_GNUC_INTERNAL
void spice_cairo_draw_event(SpiceDisplay *display, cairo_t *cr)
{
    SpiceDisplayPrivate *d = display->priv;
    cairo_rectangle_int_t rect;
    cairo_region_t *region;
    double s;
    int x, y;
    int ww, wh;
    int w, h;
    gint scale_factor;
    scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(display));
    spice_display_get_scaling(display, &s, &x, &y, &w, &h);
    /* convert physical pixel to logical */
    x /= scale_factor;
    y /= scale_factor;
    w /= scale_factor;
    h /= scale_factor;
    ww = gtk_widget_get_allocated_width(GTK_WIDGET(display));
    wh = gtk_widget_get_allocated_height(GTK_WIDGET(display));
    /* We need to paint the bg color around the image */
    rect.x = 0;
    rect.y = 0;
    rect.width = ww;
    rect.height = wh;
    region = cairo_region_create_rectangle(&rect);
    /* Optionally cut out the inner area where the pixmap
       will be drawn. This avoids 'flashing' since we're
       not double-buffering. */
    if (d->canvas.surface) {
        rect.x = x;
        rect.y = y;
        rect.width = w;
        rect.height = h;
        cairo_region_subtract_rectangle(region, &rect);
    }
    gdk_cairo_region (cr, region);
    cairo_region_destroy (region);
    /* Need to set a real solid color, because the default is usually
       transparent these days, and non-double buffered windows can't
       render transparently */
    cairo_set_source_rgb (cr, 0, 0, 0);
    cairo_fill(cr);
    /* Draw the display */
    if (d->canvas.surface) {
        cairo_translate(cr, x, y);
        cairo_rectangle(cr, 0, 0, w, h);
        cairo_scale(cr, s, s);
        if (!d->canvas.convert)
            cairo_translate(cr, -d->area.x, -d->area.y);
        cairo_set_source_surface(cr, d->canvas.surface, 0, 0);
        cairo_fill(cr);
        if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER &&
            d->mouse_guest_x != -1 && d->mouse_guest_y != -1 &&
            !d->show_cursor &&
            spice_gtk_session_get_pointer_grabbed(d->gtk_session)) {
            cairo_surface_t *surface = d->cursor_surface;
            if (surface != NULL) {
                cairo_set_source_surface(cr, surface,
                                         (double)(d->mouse_guest_x - d->mouse_hotspot.x) / scale_factor,
                                         (double)(d->mouse_guest_y - d->mouse_hotspot.y) / scale_factor);
                cairo_paint(cr);
            }
        }
    }
}
5、绑定数据
5.1、在更新图片创建cairo对象(spice-widget-cairo.c)
static void update_area(SpiceDisplay *display,
                        gint x, gint y, gint width, gint height)
{
    SpiceDisplayPrivate *d = display->priv;
    GdkRectangle primary;
    DISPLAY_DEBUG(display, "update area +%d+%d %dx%d", x, y, width, height);
    d->area = (GdkRectangle) {
        .x = x,
        .y = y,
        .width = width,
        .height = height
    };
#if HAVE_EGL
    if (egl_enabled(d)) {
        const SpiceGlScanout *so =
            spice_display_channel_get_gl_scanout(d->display);
        g_return_if_fail(so != NULL);
        primary = (GdkRectangle) {
            .width = so->width,
            .height = so->height
        };
    } else
#endif
    {
        primary = (GdkRectangle) {
            .width = d->canvas.width,
            .height = d->canvas.height
        };
    }
    DISPLAY_DEBUG(display, "primary: %dx%d", primary.width, primary.height);
    if (!gdk_rectangle_intersect(&primary, &d->area, &d->area)) {
        DISPLAY_DEBUG(display, "The monitor area is not intersecting primary surface");
        memset(&d->area, '\0', sizeof(d->area));
        set_monitor_ready(display, false);
        return;
    }
    if (!egl_enabled(d)) {
        spice_cairo_image_destroy(display);
        if (gtk_widget_get_realized(GTK_WIDGET(display)))
            update_image(display);
    }
    update_size_request(display);
    set_monitor_ready(display, true);
}
5.2、创建cairo的image
static void update_image(SpiceDisplay *display)
{
    SpiceDisplayPrivate *d = display->priv;
    spice_cairo_image_create(display);
    if (d->canvas.convert)
        do_color_convert(display, &d->area);
}
5.3、cairo_image_surface_create_for_data通过 d->canvas.data 数据创建cairo的操作对象 d->canvas.surface
G_GNUC_INTERNAL
int spice_cairo_image_create(SpiceDisplay *display)
{
    SpiceDisplayPrivate *d = display->priv;
    gint scale_factor;
    if (d->canvas.surface != NULL)
        return 0;
    if (d->canvas.format == SPICE_SURFACE_FMT_16_555 ||
        d->canvas.format == SPICE_SURFACE_FMT_16_565) {
        d->canvas.convert = TRUE;
        d->canvas.data = g_malloc0(d->area.width * d->area.height * 4);
        d->canvas.surface = cairo_image_surface_create_for_data
            (d->canvas.data, CAIRO_FORMAT_RGB24,
             d->area.width, d->area.height, d->area.width * 4);
    } else {
        d->canvas.convert = FALSE;
        d->canvas.surface = cairo_image_surface_create_for_data
            (d->canvas.data, CAIRO_FORMAT_RGB24,
             d->canvas.width, d->canvas.height, d->canvas.stride);
    }
    scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(display));
    cairo_surface_set_device_scale(d->canvas.surface, scale_factor, scale_factor);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cai742925624/article/details/128497488
gtk