// cairo_public cairo_status_t // cairo_region_subtract_rectangle (cairo_region_t *dst, // const cairo_rectangle_int_t *rectangle); static int l_cairo_region_subtract_rectangle (lua_State* L) { cairo_region_t *dst = get_cairo_region_t (L, 1); const cairo_rectangle_int_t *rectangle = get_cairo_rectangle_int_t (L, 2); cairo_status_t v = cairo_region_subtract_rectangle (dst, rectangle); lua_pushinteger(L, v); return 1; }
static gboolean select_area_motion_notify (GtkWidget *window, GdkEventMotion *event, select_area_filter_data *data) { GdkRectangle draw_rect; if (!data->button_pressed) return TRUE; draw_rect.width = ABS (data->rect.x - event->x_root); draw_rect.height = ABS (data->rect.y - event->y_root); draw_rect.x = MIN (data->rect.x, event->x_root); draw_rect.y = MIN (data->rect.y, event->y_root); if (draw_rect.width <= 0 || draw_rect.height <= 0) { gtk_window_move (GTK_WINDOW (window), -100, -100); gtk_window_resize (GTK_WINDOW (window), 10, 10); return TRUE; } gtk_window_move (GTK_WINDOW (window), draw_rect.x, draw_rect.y); gtk_window_resize (GTK_WINDOW (window), draw_rect.width, draw_rect.height); /* We (ab)use app-paintable to indicate if we have an RGBA window */ if (!gtk_widget_get_app_paintable (window)) { GdkWindow *gdkwindow = gtk_widget_get_window (window); /* Shape the window to make only the outline visible */ if (draw_rect.width > 2 && draw_rect.height > 2) { cairo_region_t *region; cairo_rectangle_int_t region_rect = { 0, 0, draw_rect.width, draw_rect.height }; region = cairo_region_create_rectangle (®ion_rect); region_rect.x++; region_rect.y++; region_rect.width -= 2; region_rect.height -= 2; cairo_region_subtract_rectangle (region, ®ion_rect); gdk_window_shape_combine_region (gdkwindow, region, 0, 0); cairo_region_destroy (region); } else gdk_window_shape_combine_region (gdkwindow, NULL, 0, 0); } return TRUE; }
static void display_entry (MetaTabPopup *popup, TabEntry *te) { if (popup->current_selected_entry) { if (popup->outline) meta_select_image_unselect (META_SELECT_IMAGE (popup->current_selected_entry->widget)); else meta_select_workspace_unselect (META_SELECT_WORKSPACE (popup->current_selected_entry->widget)); } gtk_label_set_markup (GTK_LABEL (popup->label), te->title); if (popup->outline) meta_select_image_select (META_SELECT_IMAGE (te->widget)); else meta_select_workspace_select (META_SELECT_WORKSPACE (te->widget)); if (popup->outline) { GdkRectangle rect; GdkWindow *window; cairo_region_t *region; window = gtk_widget_get_window (popup->outline_window); /* Do stuff behind gtk's back */ gdk_window_hide (window); meta_core_increment_event_serial (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); rect = te->rect; rect.x = 0; rect.y = 0; gtk_window_move (GTK_WINDOW (popup->outline_window), te->rect.x, te->rect.y); gtk_window_resize (GTK_WINDOW (popup->outline_window), te->rect.width, te->rect.height); region = cairo_region_create_rectangle (&rect); cairo_region_subtract_rectangle (region, &te->inner_rect); gdk_window_shape_combine_region (gtk_widget_get_window (popup->outline_window), region, 0, 0); cairo_region_destroy (region); gdk_window_show_unraised (window); } /* Must be before we handle an expose for the outline window */ popup->current_selected_entry = te; }
static void wl_region_subtract (struct wl_client *client, struct wl_resource *resource, gint32 x, gint32 y, gint32 width, gint32 height) { MetaWaylandRegion *region = wl_resource_get_user_data (resource); cairo_rectangle_int_t rectangle = { x, y, width, height }; cairo_region_subtract_rectangle (region->region, &rectangle); }
static void meta_window_group_paint (ClutterActor *actor) { cairo_region_t *clip_region; cairo_region_t *unobscured_region; ClutterActorIter iter; ClutterActor *child; cairo_rectangle_int_t visible_rect, clip_rect; int paint_x_offset, paint_y_offset; int paint_x_origin, paint_y_origin; int actor_x_origin, actor_y_origin; MetaWindowGroup *window_group = META_WINDOW_GROUP (actor); MetaCompositor *compositor = window_group->screen->display->compositor; ClutterActor *stage = CLUTTER_STAGE (compositor->stage); /* Start off by treating all windows as completely unobscured, so damage anywhere * in a window queues redraws, but confine it more below. */ clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { if (META_IS_WINDOW_ACTOR (child)) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (child); meta_window_actor_set_unobscured_region (window_actor, NULL); } } /* Normally we expect an actor to be drawn at it's position on the screen. * However, if we're inside the paint of a ClutterClone, that won't be the * case and we need to compensate. We look at the position of the window * group under the current model-view matrix and the position of the actor. * If they are both simply integer translations, then we can compensate * easily, otherwise we give up. * * Possible cleanup: work entirely in paint space - we can compute the * combination of the model-view matrix with the local matrix for each child * actor and get a total transformation for that actor for how we are * painting currently, and never worry about how actors are positioned * on the stage. */ if (!painting_untransformed (window_group, &paint_x_origin, &paint_y_origin) || !meta_actor_is_untransformed (actor, &actor_x_origin, &actor_y_origin)) { CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); return; } paint_x_offset = paint_x_origin - actor_x_origin; paint_y_offset = paint_y_origin - actor_y_origin; visible_rect.x = visible_rect.y = 0; visible_rect.width = clutter_actor_get_width (stage); visible_rect.height = clutter_actor_get_height (stage); unobscured_region = cairo_region_create_rectangle (&visible_rect); /* Get the clipped redraw bounds from Clutter so that we can avoid * painting shadows on windows that don't need to be painted in this * frame. In the case of a multihead setup with mismatched monitor * sizes, we could intersect this with an accurate union of the * monitors to avoid painting shadows that are visible only in the * holes. */ clutter_stage_get_redraw_clip_bounds (stage, &clip_rect); clip_region = cairo_region_create_rectangle (&clip_rect); cairo_region_translate (clip_region, -paint_x_offset, -paint_y_offset); gboolean has_unredirected_window = compositor->unredirected_window != NULL; if (has_unredirected_window) { cairo_rectangle_int_t unredirected_rect; MetaWindow *window = meta_window_actor_get_meta_window (compositor->unredirected_window); meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect); cairo_region_subtract_rectangle (unobscured_region, &unredirected_rect); cairo_region_subtract_rectangle (clip_region, &unredirected_rect); } meta_window_group_cull_out (window_group, CLUTTER_ACTOR (compositor->unredirected_window), has_unredirected_window, unobscured_region, clip_region); cairo_region_destroy (unobscured_region); cairo_region_destroy (clip_region); CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); meta_window_group_reset_culling (window_group); }
static void meta_window_group_paint (ClutterActor *actor) { cairo_region_t *visible_region; ClutterActor *stage; cairo_rectangle_int_t visible_rect; GList *children, *l; int paint_x_origin, paint_y_origin; int actor_x_origin, actor_y_origin; int paint_x_offset, paint_y_offset; MetaWindowGroup *window_group = META_WINDOW_GROUP (actor); MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen); /* Normally we expect an actor to be drawn at it's position on the screen. * However, if we're inside the paint of a ClutterClone, that won't be the * case and we need to compensate. We look at the position of the window * group under the current model-view matrix and the position of the actor. * If they are both simply integer translations, then we can compensate * easily, otherwise we give up. * * Possible cleanup: work entirely in paint space - we can compute the * combination of the model-view matrix with the local matrix for each child * actor and get a total transformation for that actor for how we are * painting currently, and never worry about how actors are positioned * on the stage. */ if (!painting_untransformed (window_group, &paint_x_origin, &paint_y_origin) || !meta_actor_is_untransformed (actor, &actor_x_origin, &actor_y_origin)) { CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); return; } paint_x_offset = paint_x_origin - actor_x_origin; paint_y_offset = paint_y_origin - actor_y_origin; /* We walk the list from top to bottom (opposite of painting order), * and subtract the opaque area of each window out of the visible * region that we pass to the windows below. */ children = clutter_actor_get_children (actor); children = g_list_reverse (children); /* Get the clipped redraw bounds from Clutter so that we can avoid * painting shadows on windows that don't need to be painted in this * frame. In the case of a multihead setup with mismatched monitor * sizes, we could intersect this with an accurate union of the * monitors to avoid painting shadows that are visible only in the * holes. */ stage = clutter_actor_get_stage (actor); clutter_stage_get_redraw_clip_bounds (CLUTTER_STAGE (stage), &visible_rect); visible_region = cairo_region_create_rectangle (&visible_rect); if (info->unredirected_window != NULL) { cairo_rectangle_int_t unredirected_rect; MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window); meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect); cairo_region_subtract_rectangle (visible_region, &unredirected_rect); } for (l = children; l; l = l->next) { if (!CLUTTER_ACTOR_IS_VISIBLE (l->data)) continue; if (l->data == info->unredirected_window) continue; /* If an actor has effects applied, then that can change the area * it paints and the opacity, so we no longer can figure out what * portion of the actor is obscured and what portion of the screen * it obscures, so we skip the actor. * * This has a secondary beneficial effect: if a ClutterOffscreenEffect * is applied to an actor, then our clipped redraws interfere with the * caching of the FBO - even if we only need to draw a small portion * of the window right now, ClutterOffscreenEffect may use other portions * of the FBO later. So, skipping actors with effects applied also * prevents these bugs. * * Theoretically, we should check clutter_actor_get_offscreen_redirect() * as well for the same reason, but omitted for simplicity in the * hopes that no-one will do that. */ if (clutter_actor_has_effects (l->data)) continue; if (META_IS_WINDOW_ACTOR (l->data)) { MetaWindowActor *window_actor = l->data; int x, y; if (!meta_actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y)) continue; x += paint_x_offset; y += paint_y_offset; /* Temporarily move to the coordinate system of the actor */ cairo_region_translate (visible_region, - x, - y); meta_window_actor_set_visible_region (window_actor, visible_region); if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff) { cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor); if (obscured_region) cairo_region_subtract (visible_region, obscured_region); } meta_window_actor_set_visible_region_beneath (window_actor, visible_region); cairo_region_translate (visible_region, x, y); } else if (META_IS_BACKGROUND_ACTOR (l->data) || META_IS_BACKGROUND_GROUP (l->data)) { ClutterActor *background_actor = l->data; int x, y; if (!meta_actor_is_untransformed (CLUTTER_ACTOR (background_actor), &x, &y)) continue; x += paint_x_offset; y += paint_y_offset; cairo_region_translate (visible_region, - x, - y); if (META_IS_BACKGROUND_GROUP (background_actor)) meta_background_group_set_visible_region (META_BACKGROUND_GROUP (background_actor), visible_region); else meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (background_actor), visible_region); cairo_region_translate (visible_region, x, y); } } cairo_region_destroy (visible_region); CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); /* Now that we are done painting, unset the visible regions (they will * mess up painting clones of our actors) */ for (l = children; l; l = l->next) { if (META_IS_WINDOW_ACTOR (l->data)) { MetaWindowActor *window_actor = l->data; meta_window_actor_reset_visible_regions (window_actor); } else if (META_IS_BACKGROUND_ACTOR (l->data)) { MetaBackgroundActor *background_actor = l->data; meta_background_actor_set_visible_region (background_actor, NULL); } } g_list_free (children); }
/* We double buffer the image. It looks nice and it enables * grabbing the graph with the pointer and translating it. * We draw on our own larger surface and then copy part of that * to the gdk surface. * * We assume that this function is drawing to an exposed/showing * drawing area, so the status update will reflect the current * exposed/showing drawing area. */ void qp_graph_draw(struct qp_graph *gr, cairo_t *gdk_cr) { GtkAllocation allocation; if(gr->waiting_to_resize_draw && !gr->qp->shape) { //WARN("gr=%p gr->name=\"%s\" gr->ref_count=%d\n", gr, gr->name, gr->ref_count); cairo_set_source_rgba(gdk_cr, gr->background_color.r, gr->background_color.g, gr->background_color.b, gr->background_color.a); cairo_paint(gdk_cr); g_idle_add_full(G_PRIORITY_LOW, idle_callback, gr, NULL); /* fight qp_graph_destroy() race condition with flag */ ++gr->ref_count; /* We draw after the other widgets are drawn, in case drawing * takes a long time. This waiting also gives a chance * for the watch cursor to show. But that seems to only * show if the window had focus at the right time. */ return; } gtk_widget_get_allocation(gr->drawing_area, &allocation); if(gr->pixbuf_needs_draw) { cairo_t *db_cr; /* double buffer cr */ db_cr = cairo_create(gr->pixbuf_surface); graph_draw(gr, db_cr, gr->pixbuf_x, gr->pixbuf_y, gr->pixbuf_width, gr->pixbuf_height); cairo_destroy(db_cr); // debuging //cairo_surface_write_to_png(gr->pixbuf_surface, "x.png"); qp_win_set_status(gr->qp); } /* the GTK cairo_t *gdk_cr has no alpha bits so all the * alpha drawn to it will be smushed. */ //WARN("content=0x%lx\n", (unsigned long)cairo_get_target(gdk_cr)); if(!gr->qp->shape) { /* Not using the shape X11 extension */ /* This is where we go from the back buffer to the drawing area */ draw_from_pixbuf(gdk_cr, gr, allocation.width, allocation.height); if(gr->draw_zoom_box == 1) draw_zoom_box(gdk_cr, gr); if(gr->draw_value_pick) draw_value_pick_line(gdk_cr, gr, allocation.width, allocation.height); if(gr->pixbuf_needs_draw) { gdk_window_set_cursor(gtk_widget_get_window(gr->qp->window), NULL); gr->pixbuf_needs_draw = 0; // gr->qp->wait_warning_showing = 0; } } else { /* Use the X11 shape extension */ /* TODO: This is a resource pig. Fix it. */ cairo_region_t *reg_draw_area, *window_region; /* empty flag */ int empty; cairo_surface_t *mask_surface; GtkAllocation all; /* Make sure the surface is up to date */ //cairo_surface_flush(gr->pixbuf_surface); /* make a sub surface that is the size of the graph drawing area */ mask_surface = cairo_surface_create_for_rectangle(gr->pixbuf_surface, INT(gr->pixbuf_x+gr->grab_x), INT(gr->pixbuf_y+gr->grab_y), allocation.width, allocation.height); reg_draw_area = get_cairo_region_create_from_surface(gr, mask_surface, allocation.width, allocation.height); cairo_surface_destroy(mask_surface); cairo_region_translate(reg_draw_area, allocation.x, allocation.y); gtk_widget_get_allocation(gr->qp->window, &all); all.x = all.y = 0; window_region = cairo_region_create_rectangle(&all); cairo_region_subtract_rectangle(window_region, &allocation); empty = cairo_region_is_empty(reg_draw_area); if(!empty) cairo_region_union(window_region, reg_draw_area); cairo_region_destroy(reg_draw_area); /* window_region is a region with a hole in it the * size of the drawing area with the graph and grid added back. */ if(gr->draw_zoom_box && !empty) { cairo_rectangle_int_t rec; rec.x = allocation.x + gr->z_x; rec.y = allocation.y + gr->z_y; rec.width = gr->z_w; rec.height = gr->z_h; /* regions do not like negitive values or * maybe shapes do not like negitive values * in any case we keep width and height * positive */ if(rec.width < 0) { rec.width *= -1; rec.x -= rec.width; } if(rec.height < 0) { rec.height *= -1; rec.y -= rec.height; } cairo_region_union_rectangle(window_region, &rec); /* now we have the zoom box added to window_region */ } /* This is where we go from the back buffer to the drawing area */ draw_from_pixbuf(gdk_cr, gr, allocation.width, allocation.height); if(gr->draw_zoom_box) draw_zoom_box(gdk_cr, gr); if(gr->draw_value_pick) draw_value_pick_line(gdk_cr, gr, allocation.width, allocation.height); if(empty) { /* we have nothing to make a shape with */ if(gr->qp->last_shape_region) { cairo_region_destroy(gr->qp->last_shape_region); gr->qp->last_shape_region = NULL; } cairo_region_destroy(window_region); /* remove the old shape region */ gtk_widget_shape_combine_region(gr->qp->window, NULL); } else if(!gr->qp->last_shape_region || !cairo_region_equal(gr->qp->last_shape_region, window_region)) { // DEBUG("creating new shape region\n"); /* We need to undo the old shape first */ gtk_widget_shape_combine_region(gr->qp->window, NULL); gtk_widget_shape_combine_region(gr->qp->window, window_region); if(gr->qp->last_shape_region) cairo_region_destroy(gr->qp->last_shape_region); gr->qp->last_shape_region = window_region; } else cairo_region_destroy(window_region); gr->pixbuf_needs_draw = 0; gdk_window_set_cursor(gtk_widget_get_window(gr->qp->window), NULL); // debuging //cairo_surface_write_to_png(cairo_get_target(gdk_cr), "y.png"); } if(gr->qp->update_graph_detail && gr->qp->graph_detail) { gr->qp->update_graph_detail = 0; /* make the graph configure window show stuff about this graph */ qp_win_graph_detail_init(gr->qp); } }
static GeglTile * gimp_tile_handler_projection_validate (GeglTileSource *source, GeglTile *tile, gint x, gint y) { GimpTileHandlerProjection *projection; cairo_region_t *tile_region; cairo_rectangle_int_t tile_rect; projection = GIMP_TILE_HANDLER_PROJECTION (source); if (cairo_region_is_empty (projection->dirty_region)) return tile; tile_region = cairo_region_copy (projection->dirty_region); tile_rect.x = x * projection->tile_width; tile_rect.y = y * projection->tile_height; tile_rect.width = projection->tile_width; tile_rect.height = projection->tile_height; cairo_region_intersect_rectangle (tile_region, &tile_rect); if (! cairo_region_is_empty (tile_region)) { gint tile_bpp; gint tile_stride; gint n_rects; gint i; if (! tile) tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source), x, y, 0); cairo_region_subtract_rectangle (projection->dirty_region, &tile_rect); tile_bpp = babl_format_get_bytes_per_pixel (projection->format); tile_stride = tile_bpp * projection->tile_width; gegl_tile_lock (tile); n_rects = cairo_region_num_rectangles (tile_region); #if 0 g_printerr ("%d ", n_rects); #endif for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t blit_rect; cairo_region_get_rectangle (tile_region, i, &blit_rect); #if 0 g_printerr ("constructing projection at %d %d %d %d\n", blit_rect.x, blit_rect.y, blit_rect.width, blit_rect.height); #endif gegl_node_blit (projection->graph, 1.0, GEGL_RECTANGLE (blit_rect.x, blit_rect.y, blit_rect.width, blit_rect.height), projection->format, gegl_tile_get_data (tile) + (blit_rect.y % projection->tile_height) * tile_stride + (blit_rect.x % projection->tile_width) * tile_bpp, tile_stride, GEGL_ABYSS_NONE); } gegl_tile_unlock (tile); } cairo_region_destroy (tile_region); return tile; }
static void cd_update_input_shape (CairoDock *pDock) { if (pDock->pShapeBitmap != NULL) { CDPanelData *pData = pDock->pRendererData; g_return_if_fail (pData != NULL); gboolean bValidGroup = FALSE; // valid = non-empty gboolean bHasGroups = FALSE; double xi; double w = pData->fGroupGap - 4 * my_fPanelRadius; // all groups have the same width double h = pDock->iMaxDockHeight; // we use iMaxDockHeight instead of the actual window size, because at this time, the dock's window may not have its definite size. cairo_rectangle_int_t rect; GList *ic; Icon *pIcon; for (ic = pDock->icons; ic != NULL; ic = ic->next) { pIcon = ic->data; if (CAIRO_DOCK_ICON_TYPE_IS_SEPARATOR (pIcon)) { if (bValidGroup) { xi = pIcon->fXAtRest + 2 * my_fPanelRadius; // we let a few pixels to be able to grab the separtator, and to avoid leaving the dock too easily. if (pDock->container.bIsHorizontal) { rect.x = xi; rect.y = 0; rect.width = w; rect.height = h; cd_debug ("rect %d; %d", (int)rect.x, (int)rect.width); } else { rect.x = 0; rect.y = xi; rect.width = h; rect.height = w; } cairo_region_subtract_rectangle (pDock->pShapeBitmap, &rect); bValidGroup = FALSE; bHasGroups = TRUE; } } else { bValidGroup = TRUE; } } // if there is only a single group, make the outside transparent if (! bHasGroups) { // left screen edge -> first icon if (pDock->fAlign > 0) { xi = pDock->fAlign * pData->fGroupGap - my_fPanelRadius; if (pDock->container.bIsHorizontal) { rect.x = 0.; // left screen edge rect.y = 0; rect.width = xi; // to first icon rect.height = h; cd_debug ("rect %d; %d", (int)rect.x, (int)rect.width); } else { rect.x = 0; rect.y = 0.; rect.width = h; rect.height = xi; } cairo_region_subtract_rectangle (pDock->pShapeBitmap, &rect); } // last icon -> right screen edge if (pDock->fAlign < 1) { xi = pDock->iMaxDockWidth - (1 - pDock->fAlign) * pData->fGroupGap + my_fPanelRadius; if (pDock->container.bIsHorizontal) { rect.x = xi; // last icon rect.y = 0; rect.width = pDock->iMaxDockWidth - xi; // to right screen edge rect.height = h; cd_debug ("rect %d; %d", (int)rect.x, (int)rect.width); } else { rect.x = 0; rect.y = xi; rect.width = h; rect.height = pDock->iMaxDockWidth - xi; } cairo_region_subtract_rectangle (pDock->pShapeBitmap, &rect); } } } }
/** * gdk_cairo_draw_from_gl: * @cr: a cairo context * @window: The window we're rendering for (not necessarily into) * @source: The GL ID of the source buffer * @source_type: The type of the @source * @buffer_scale: The scale-factor that the @source buffer is allocated for * @x: The source x position in @source to start copying from in GL coordinates * @y: The source y position in @source to start copying from in GL coordinates * @width: The width of the region to draw * @height: The height of the region to draw * * This is the main way to draw GL content in GTK+. It takes a render buffer ID * (@source_type == #GL_RENDERBUFFER) or a texture id (@source_type == #GL_TEXTURE) * and draws it onto @cr with an OVER operation, respecting the current clip. * The top left corner of the rectangle specified by @x, @y, @width and @height * will be drawn at the current (0,0) position of the cairo_t. * * This will work for *all* cairo_t, as long as @window is realized, but the * fallback implementation that reads back the pixels from the buffer may be * used in the general case. In the case of direct drawing to a window with * no special effects applied to @cr it will however use a more efficient * approach. * * For #GL_RENDERBUFFER the code will always fall back to software for buffers * with alpha components, so make sure you use #GL_TEXTURE if using alpha. * * Calling this may change the current GL context. * * Since: 3.16 */ void gdk_cairo_draw_from_gl (cairo_t *cr, GdkWindow *window, int source, int source_type, int buffer_scale, int x, int y, int width, int height) { GdkGLContext *paint_context; cairo_surface_t *image; cairo_matrix_t matrix; int dx, dy, window_scale; gboolean trivial_transform; cairo_surface_t *group_target; GdkWindow *direct_window, *impl_window; guint framebuffer; int alpha_size = 0; cairo_region_t *clip_region; GdkGLContextPaintData *paint_data; impl_window = window->impl_window; window_scale = gdk_window_get_scale_factor (impl_window); paint_context = gdk_window_get_paint_gl_context (window, NULL); if (paint_context == NULL) { g_warning ("gdk_cairo_draw_gl_render_buffer failed - no paint context"); return; } clip_region = gdk_cairo_region_from_clip (cr); gdk_gl_context_make_current (paint_context); paint_data = gdk_gl_context_get_paint_data (paint_context); if (paint_data->tmp_framebuffer == 0) glGenFramebuffersEXT (1, &paint_data->tmp_framebuffer); if (source_type == GL_RENDERBUFFER) { glBindRenderbuffer (GL_RENDERBUFFER, source); glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE, &alpha_size); } else if (source_type == GL_TEXTURE) { glBindTexture (GL_TEXTURE_2D, source); glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &alpha_size); } else { g_warning ("Unsupported gl source type %d\n", source_type); return; } group_target = cairo_get_group_target (cr); direct_window = cairo_surface_get_user_data (group_target, &direct_key); cairo_get_matrix (cr, &matrix); dx = matrix.x0; dy = matrix.y0; /* Trivial == integer-only translation */ trivial_transform = (double)dx == matrix.x0 && (double)dy == matrix.y0 && matrix.xx == 1.0 && matrix.xy == 0.0 && matrix.yx == 0.0 && matrix.yy == 1.0; /* For direct paint of non-alpha renderbuffer, we can just do a bitblit */ if ((_gdk_gl_flags & GDK_GL_SOFTWARE_DRAW_GL) == 0 && source_type == GL_RENDERBUFFER && alpha_size == 0 && direct_window != NULL && direct_window->current_paint.use_gl && trivial_transform && clip_region != NULL) { int unscaled_window_height; int i; /* Create a framebuffer with the source renderbuffer and make it the current target for reads */ framebuffer = paint_data->tmp_framebuffer; glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer); glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, source); glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, 0); /* Translate to impl coords */ cairo_region_translate (clip_region, dx, dy); glEnable (GL_SCISSOR_TEST); gdk_window_get_unscaled_size (impl_window, NULL, &unscaled_window_height); glDrawBuffer (GL_BACK); #define FLIP_Y(_y) (unscaled_window_height - (_y)) for (i = 0; i < cairo_region_num_rectangles (clip_region); i++) { cairo_rectangle_int_t clip_rect, dest; cairo_region_get_rectangle (clip_region, i, &clip_rect); clip_rect.x *= window_scale; clip_rect.y *= window_scale; clip_rect.width *= window_scale; clip_rect.height *= window_scale; glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height), clip_rect.width, clip_rect.height); dest.x = dx * window_scale; dest.y = dy * window_scale; dest.width = width * window_scale / buffer_scale; dest.height = height * window_scale / buffer_scale; if (gdk_rectangle_intersect (&clip_rect, &dest, &dest)) { int clipped_src_x = x + (dest.x - dx * window_scale); int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale)); glBlitFramebufferEXT(clipped_src_x, clipped_src_y, (clipped_src_x + dest.width), (clipped_src_y + dest.height), dest.x, FLIP_Y(dest.y + dest.height), dest.x + dest.width, FLIP_Y(dest.y), GL_COLOR_BUFFER_BIT, GL_NEAREST); if (impl_window->current_paint.flushed_region) { cairo_rectangle_int_t flushed_rect; flushed_rect.x = dest.x / window_scale; flushed_rect.y = dest.y / window_scale; flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x; flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y; cairo_region_union_rectangle (impl_window->current_paint.flushed_region, &flushed_rect); cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region, &flushed_rect); } } } glDisable (GL_SCISSOR_TEST); glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); #undef FLIP_Y } /* For direct paint of alpha or non-alpha textures we can use texturing */ else if ((_gdk_gl_flags & GDK_GL_SOFTWARE_DRAW_GL) == 0 && source_type == GL_TEXTURE && direct_window != NULL && direct_window->current_paint.use_gl && trivial_transform && clip_region != NULL) { int unscaled_window_height; GLint texture_width; GLint texture_height; int i, n_rects, n_quads; GdkTexturedQuad *quads; cairo_rectangle_int_t clip_rect; /* Translate to impl coords */ cairo_region_translate (clip_region, dx, dy); if (alpha_size != 0) { cairo_region_t *opaque_region, *blend_region; opaque_region = cairo_region_copy (clip_region); cairo_region_subtract (opaque_region, impl_window->current_paint.flushed_region); cairo_region_subtract (opaque_region, impl_window->current_paint.need_blend_region); if (!cairo_region_is_empty (opaque_region)) gdk_gl_texture_from_surface (impl_window->current_paint.surface, opaque_region); blend_region = cairo_region_copy (clip_region); cairo_region_intersect (blend_region, impl_window->current_paint.need_blend_region); glEnable (GL_BLEND); if (!cairo_region_is_empty (blend_region)) gdk_gl_texture_from_surface (impl_window->current_paint.surface, blend_region); cairo_region_destroy (opaque_region); cairo_region_destroy (blend_region); } glBindTexture (GL_TEXTURE_2D, source); glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texture_width); glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texture_height); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glEnable (GL_SCISSOR_TEST); gdk_window_get_unscaled_size (impl_window, NULL, &unscaled_window_height); #define FLIP_Y(_y) (unscaled_window_height - (_y)) cairo_region_get_extents (clip_region, &clip_rect); glScissor (clip_rect.x * window_scale, FLIP_Y ((clip_rect.y + clip_rect.height) * window_scale), clip_rect.width * window_scale, clip_rect.height * window_scale); n_quads = 0; n_rects = cairo_region_num_rectangles (clip_region); quads = g_new (GdkTexturedQuad, n_rects); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t dest; cairo_region_get_rectangle (clip_region, i, &clip_rect); clip_rect.x *= window_scale; clip_rect.y *= window_scale; clip_rect.width *= window_scale; clip_rect.height *= window_scale; dest.x = dx * window_scale; dest.y = dy * window_scale; dest.width = width * window_scale / buffer_scale; dest.height = height * window_scale / buffer_scale; if (gdk_rectangle_intersect (&clip_rect, &dest, &dest)) { int clipped_src_x = x + (dest.x - dx * window_scale); int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale)); GdkTexturedQuad quad = { dest.x, FLIP_Y(dest.y), dest.x + dest.width, FLIP_Y(dest.y + dest.height), clipped_src_x / (float)texture_width, (clipped_src_y + dest.height) / (float)texture_height, (clipped_src_x + dest.width) / (float)texture_width, clipped_src_y / (float)texture_height, }; quads[n_quads++] = quad; if (impl_window->current_paint.flushed_region) { cairo_rectangle_int_t flushed_rect; flushed_rect.x = dest.x / window_scale; flushed_rect.y = dest.y / window_scale; flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x; flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y; cairo_region_union_rectangle (impl_window->current_paint.flushed_region, &flushed_rect); cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region, &flushed_rect); } } } if (n_quads > 0) gdk_gl_texture_quads (paint_context, GL_TEXTURE_2D, n_quads, quads); g_free (quads); if (alpha_size != 0) glDisable (GL_BLEND); #undef FLIP_Y } else { /* Software fallback */ /* TODO: avoid reading back non-required data due to dest clip */ image = cairo_surface_create_similar_image (cairo_get_target (cr), (alpha_size == 0) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32, width, height); cairo_surface_set_device_scale (image, buffer_scale, buffer_scale); framebuffer = paint_data->tmp_framebuffer; glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer); if (source_type == GL_RENDERBUFFER) { /* Create a framebuffer with the source renderbuffer and make it the current target for reads */ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, source); } else { glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, source, 0); } glPixelStorei (GL_PACK_ALIGNMENT, 4); glPixelStorei (GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride (image) / 4); glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data (image)); glPixelStorei (GL_PACK_ROW_LENGTH, 0); glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); cairo_surface_mark_dirty (image); /* Invert due to opengl having different origin */ cairo_scale (cr, 1, -1); cairo_translate (cr, 0, -height / buffer_scale); cairo_set_source_surface (cr, image, 0, 0); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); cairo_paint (cr); cairo_surface_destroy (image); } if (clip_region) cairo_region_destroy (clip_region); }
gboolean gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, GimpProgress *progress, const gchar *undo_desc, GeglNode *operation, GeglBuffer *dest_buffer, const GeglRectangle *dest_rect, GeglBuffer *cache, const GeglRectangle *valid_rects, gint n_valid_rects, gboolean cancellable) { GeglNode *gegl; GeglNode *dest_node; GeglRectangle rect = { 0, }; GeglProcessor *processor = NULL; gboolean progress_started = FALSE; gdouble value; gboolean cancel = FALSE; g_return_val_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer), FALSE); g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); g_return_val_if_fail (GEGL_IS_NODE (operation), FALSE); g_return_val_if_fail (GEGL_IS_BUFFER (dest_buffer), FALSE); g_return_val_if_fail (cache == NULL || GEGL_IS_BUFFER (cache), FALSE); g_return_val_if_fail (valid_rects == NULL || cache != NULL, FALSE); g_return_val_if_fail (valid_rects == NULL || n_valid_rects != 0, FALSE); if (dest_rect) { rect = *dest_rect; } else { rect = *GEGL_RECTANGLE (0, 0, gegl_buffer_get_width (dest_buffer), gegl_buffer_get_height (dest_buffer)); } gegl = gegl_node_new (); if (! gegl_node_get_parent (operation)) gegl_node_add_child (gegl, operation); if (src_buffer && gegl_node_has_pad (operation, "input")) { GeglNode *src_node; /* dup() because reading and writing the same buffer doesn't * work with area ops when using a processor. See bug #701875. */ if (progress && (src_buffer == dest_buffer)) src_buffer = gegl_buffer_dup (src_buffer); else g_object_ref (src_buffer); src_node = gegl_node_new_child (gegl, "operation", "gegl:buffer-source", "buffer", src_buffer, NULL); g_object_unref (src_buffer); gegl_node_connect_to (src_node, "output", operation, "input"); } dest_node = gegl_node_new_child (gegl, "operation", "gegl:write-buffer", "buffer", dest_buffer, NULL); gegl_node_connect_to (operation, "output", dest_node, "input"); if (progress) { processor = gegl_node_new_processor (dest_node, &rect); if (gimp_progress_is_active (progress)) { if (undo_desc) gimp_progress_set_text_literal (progress, undo_desc); progress_started = FALSE; cancellable = FALSE; } else { gimp_progress_start (progress, cancellable, "%s", undo_desc); if (cancellable) g_signal_connect (progress, "cancel", G_CALLBACK (gimp_gegl_apply_operation_cancel), &cancel); progress_started = TRUE; } } if (cache) { cairo_region_t *region; gint all_pixels; gint done_pixels = 0; gint n_rects; gint i; region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &rect); all_pixels = rect.width * rect.height; for (i = 0; i < n_valid_rects; i++) { gegl_buffer_copy (cache, valid_rects + i, GEGL_ABYSS_NONE, dest_buffer, valid_rects + i); cairo_region_subtract_rectangle (region, (cairo_rectangle_int_t *) valid_rects + i); done_pixels += valid_rects[i].width * valid_rects[i].height; if (progress) gimp_progress_set_value (progress, (gdouble) done_pixels / (gdouble) all_pixels); } n_rects = cairo_region_num_rectangles (region); for (i = 0; ! cancel && (i < n_rects); i++) { cairo_rectangle_int_t render_rect; cairo_region_get_rectangle (region, i, &render_rect); if (progress) { gint rect_pixels = render_rect.width * render_rect.height; #ifdef REUSE_PROCESSOR gegl_processor_set_rectangle (processor, (GeglRectangle *) &render_rect); #else g_object_unref (processor); processor = gegl_node_new_processor (dest_node, (GeglRectangle *) &render_rect); #endif while (! cancel && gegl_processor_work (processor, &value)) { gimp_progress_set_value (progress, ((gdouble) done_pixels + value * rect_pixels) / (gdouble) all_pixels); if (cancellable) while (! cancel && g_main_context_pending (NULL)) g_main_context_iteration (NULL, FALSE); } done_pixels += rect_pixels; } else { gegl_node_blit (dest_node, 1.0, (GeglRectangle *) &render_rect, NULL, NULL, 0, GEGL_BLIT_DEFAULT); } } cairo_region_destroy (region); } else { if (progress) { while (! cancel && gegl_processor_work (processor, &value)) { gimp_progress_set_value (progress, value); if (cancellable) while (! cancel && g_main_context_pending (NULL)) g_main_context_iteration (NULL, FALSE); } } else { gegl_node_blit (dest_node, 1.0, &rect, NULL, NULL, 0, GEGL_BLIT_DEFAULT); } } if (processor) g_object_unref (processor); g_object_unref (gegl); if (progress_started) { gimp_progress_end (progress); if (cancellable) g_signal_handlers_disconnect_by_func (progress, gimp_gegl_apply_operation_cancel, &cancel); } return ! cancel; }
G_GNUC_INTERNAL void spicex_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; spice_display_get_scaling(display, &s, &x, &y, &w, &h); gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(display)), &ww, &wh); /* 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->ximage) { 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->ximage) { cairo_translate(cr, x, y); cairo_rectangle(cr, 0, 0, w, h); cairo_scale(cr, s, s); if (!d->convert) cairo_translate(cr, -d->area.x, -d->area.y); cairo_set_source_surface(cr, d->ximage, 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)) { GdkPixbuf *image = d->mouse_pixbuf; if (image != NULL) { gdk_cairo_set_source_pixbuf(cr, image, d->mouse_guest_x - d->mouse_hotspot.x, d->mouse_guest_y - d->mouse_hotspot.y); cairo_paint(cr); } } } }