static gboolean get_selection_bounds (EvView *view, EvViewSelection *selection, gint *start_offset, gint *end_offset) { cairo_rectangle_int_t rect; gint start, end; if (!selection->covered_region || cairo_region_is_empty (selection->covered_region)) return FALSE; cairo_region_get_rectangle (selection->covered_region, 0, &rect); start = _ev_view_get_caret_cursor_offset_at_doc_point (view, selection->page, rect.x / view->scale, (rect.y + (rect.height / 2)) / view->scale); if (start == -1) return FALSE; cairo_region_get_rectangle (selection->covered_region, cairo_region_num_rectangles (selection->covered_region) - 1, &rect); end = _ev_view_get_caret_cursor_offset_at_doc_point (view, selection->page, (rect.x + rect.width) / view->scale, (rect.y + (rect.height / 2)) / view->scale); if (end == -1) return FALSE; *start_offset = start; *end_offset = end; return TRUE; }
// cairo_public cairo_bool_t // cairo_region_is_empty (const cairo_region_t *region); static int l_cairo_region_is_empty (lua_State* L) { const cairo_region_t *region = get_cairo_region_t (L, 1); cairo_bool_t v = cairo_region_is_empty (region); lua_pushboolean(L, v); return 1; }
cairo_int_status_t _cairo_clip_get_region (cairo_clip_t *clip, cairo_region_t **region) { cairo_int_status_t status; if (clip->all_clipped) goto CLIPPED; assert (clip->path != NULL); status = _cairo_clip_path_to_region (clip->path); if (status) return status; if (cairo_region_is_empty (clip->path->region)) { _cairo_clip_set_all_clipped (clip); goto CLIPPED; } if (region) *region = clip->path->region; return CAIRO_STATUS_SUCCESS; CLIPPED: if (region) *region = NULL; return CAIRO_INT_STATUS_NOTHING_TO_DO; }
static gboolean byzanz_recorder_snapshot (ByzanzRecorder *recorder) { cairo_surface_t *surface; cairo_region_t *invalid; GTimeVal tv; if (!recorder->recording) return FALSE; if (recorder->next_image_source != 0) return FALSE; invalid = byzanz_recorder_get_invalid_region (recorder); if (cairo_region_is_empty (invalid)) { cairo_region_destroy (invalid); return FALSE; } surface = byzanz_recorder_create_snapshot (recorder, invalid); g_get_current_time (&tv); cairo_region_translate (invalid, -recorder->area.x, -recorder->area.y); g_signal_emit (recorder, signals[IMAGE], 0, surface, invalid, &tv); cairo_surface_destroy (surface); cairo_region_destroy (invalid); recorder->next_image_source = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE, BYZANZ_RECORDER_FRAME_RATE_MS, byzanz_recorder_next_image, recorder, NULL); return TRUE; }
/** * gsk_renderer_render: * @renderer: a #GskRenderer * @root: a #GskRenderNode * @region: (nullable): the #cairo_region_t that must be redrawn or %NULL * for the whole window * * Renders the scene graph, described by a tree of #GskRenderNode instances, * ensuring that the given @region gets redrawn. * * Renderers must ensure that changes of the contents given by the @root * node as well as the area given by @region are redrawn. They are however * free to not redraw any pixel outside of @region if they can guarantee that * it didn't change. * * The @renderer will acquire a reference on the #GskRenderNode tree while * the rendering is in progress. */ void gsk_renderer_render (GskRenderer *renderer, GskRenderNode *root, const cairo_region_t *region) { GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer); cairo_region_t *clip; g_return_if_fail (GSK_IS_RENDERER (renderer)); g_return_if_fail (priv->is_realized); g_return_if_fail (GSK_IS_RENDER_NODE (root)); g_return_if_fail (priv->root_node == NULL); if (region == NULL || priv->prev_node == NULL || GSK_RENDERER_DEBUG_CHECK (renderer, FULL_REDRAW)) { clip = cairo_region_create_rectangle (&(GdkRectangle) { 0, 0, gdk_surface_get_width (priv->surface), gdk_surface_get_height (priv->surface) }); } else { clip = cairo_region_copy (region); gsk_render_node_diff (priv->prev_node, root, clip); if (cairo_region_is_empty (clip)) { cairo_region_destroy (clip); return; } } priv->root_node = gsk_render_node_ref (root); GSK_RENDERER_GET_CLASS (renderer)->render (renderer, root, clip); #ifdef G_ENABLE_DEBUG if (GSK_RENDERER_DEBUG_CHECK (renderer, RENDERER)) { GString *buf = g_string_new ("*** Frame stats ***\n\n"); gsk_profiler_append_counters (priv->profiler, buf); g_string_append_c (buf, '\n'); gsk_profiler_append_timers (priv->profiler, buf); g_string_append_c (buf, '\n'); g_print ("%s\n***\n\n", buf->str); g_string_free (buf, TRUE); } #endif g_clear_pointer (&priv->prev_node, gsk_render_node_unref); cairo_region_destroy (clip); priv->prev_node = priv->root_node; priv->root_node = NULL; }
bool wxRegion::IsEmpty() const { #ifdef __WXGTK3__ return m_refData == NULL || cairo_region_is_empty(M_REGIONDATA->m_region); #else return m_refData == NULL || gdk_region_empty(M_REGIONDATA->m_region); #endif }
static void _gtk_pixel_cache_repaint (GtkPixelCache *cache, GdkWindow *window, GtkPixelCacheDrawFunc draw, cairo_rectangle_int_t *view_rect, cairo_rectangle_int_t *canvas_rect, gpointer user_data) { cairo_t *backing_cr; cairo_region_t *region_dirty = cache->surface_dirty; cache->surface_dirty = NULL; if (cache->surface && region_dirty && !cairo_region_is_empty (region_dirty)) { backing_cr = cairo_create (cache->surface); gdk_cairo_region (backing_cr, region_dirty); cairo_clip (backing_cr); cairo_translate (backing_cr, -cache->surface_x - canvas_rect->x - view_rect->x, -cache->surface_y - canvas_rect->y - view_rect->y); cairo_save (backing_cr); cairo_set_source_rgba (backing_cr, 0.0, 0, 0, 0.0); cairo_set_operator (backing_cr, CAIRO_OPERATOR_SOURCE); cairo_paint (backing_cr); cairo_restore (backing_cr); cairo_save (backing_cr); draw (backing_cr, user_data); cairo_restore (backing_cr); #ifdef G_ENABLE_DEBUG if (GTK_DISPLAY_DEBUG_CHECK (gdk_window_get_display (window), PIXEL_CACHE)) { GdkRGBA colors[] = { { 1, 0, 0, 0.08}, { 0, 1, 0, 0.08}, { 0, 0, 1, 0.08}, { 1, 0, 1, 0.08}, { 1, 1, 0, 0.08}, { 0, 1, 1, 0.08}, }; static int current_color = 0; gdk_cairo_set_source_rgba (backing_cr, &colors[(current_color++) % G_N_ELEMENTS (colors)]); cairo_paint (backing_cr); } #endif cairo_destroy (backing_cr); } if (region_dirty) cairo_region_destroy (region_dirty); }
gboolean byzanz_serialize (GOutputStream * stream, guint64 msecs, cairo_surface_t * surface, const cairo_region_t * region, GCancellable * cancellable, GError ** error) { guint i, stride; cairo_rectangle_int_t rect, extents; guchar *data; guint32 n; int y, n_rects; g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE); g_return_val_if_fail ((surface == NULL) == (region == NULL), FALSE); g_return_val_if_fail (region == NULL || !cairo_region_is_empty (region), FALSE); if (!g_output_stream_write_all (stream, &msecs, sizeof (guint64), NULL, cancellable, error)) return FALSE; if (surface == 0) { n = 0; return g_output_stream_write_all (stream, &n, sizeof (guint32), NULL, cancellable, error); } n = n_rects = cairo_region_num_rectangles (region); if (!g_output_stream_write_all (stream, &n, sizeof (guint32), NULL, cancellable, error)) return FALSE; for (i = 0; i < n; i++) { gint32 ints[4]; cairo_region_get_rectangle (region, i, &rect); ints[0] = rect.x, ints[1] = rect.y, ints[2] = rect.width, ints[3] = rect.height; g_assert (sizeof (ints) == 16); if (!g_output_stream_write_all (stream, ints, sizeof (ints), NULL, cancellable, error)) return FALSE; } stride = cairo_image_surface_get_stride (surface); cairo_region_get_extents (region, &extents); for (i = 0; i < n; i++) { cairo_region_get_rectangle (region, i, &rect); data = cairo_image_surface_get_data (surface) + stride * (rect.y - extents.y) + sizeof (guint32) * (rect.x - extents.x); for (y = 0; y < rect.height; y++) { if (!g_output_stream_write_all (G_OUTPUT_STREAM (stream), data, rect.width * sizeof (guint32), NULL, cancellable, error)) return FALSE; data += stride; } } return TRUE; }
/* Region is in canvas coordinates */ void _gtk_pixel_cache_invalidate (GtkPixelCache *cache, cairo_region_t *region) { cairo_rectangle_int_t r; cairo_region_t *free_region = NULL; if (cache->surface == NULL || (region != NULL && cairo_region_is_empty (region))) return; if (region == NULL) { r.x = cache->surface_x; r.y = cache->surface_y; r.width = cache->surface_w; r.height = cache->surface_h; free_region = region = cairo_region_create_rectangle (&r); } if (cache->surface_dirty == NULL) { cache->surface_dirty = cairo_region_copy (region); cairo_region_translate (cache->surface_dirty, -cache->surface_x, -cache->surface_y); } else { cairo_region_translate (region, -cache->surface_x, -cache->surface_y); cairo_region_union (cache->surface_dirty, region); cairo_region_translate (region, cache->surface_x, cache->surface_y); } if (free_region) cairo_region_destroy (free_region); r.x = 0; r.y = 0; r.width = cache->surface_w; r.height = cache->surface_h; cairo_region_intersect_rectangle (cache->surface_dirty, &r); }
gboolean draw(gpointer data) { if (cairo_region_is_empty(lualock.updates_needed)) return TRUE; cairo_rectangle_int_t extents; cairo_region_get_extents(lualock.updates_needed, &extents); cairo_t *cr = cairo_create(lualock.surface_buf); cairo_rectangle(cr, extents.x, extents.y, extents.width, extents.height); cairo_clip(cr); cairo_set_source_rgba(cr, 0, 0, 0, 0); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_surface(cr, lualock.bg_surface, 0, 0); cairo_paint(cr); for (guint i = 0; i < lualock.layers->len; i++) { layer_t *layer = g_ptr_array_index(lualock.layers, i); if (!layer->show) continue; cairo_save(cr); cairo_translate(cr, layer->x, layer->y); cairo_scale(cr, layer->scale_x, layer->scale_y); cairo_rotate(cr, layer->angle); cairo_set_source_surface(cr, layer->surface, 0, 0); cairo_paint(cr); cairo_restore(cr); } draw_password_mask(); cairo_translate(cr, lualock.style.x, lualock.style.y); cairo_set_source_surface(cr, lualock.pw_surface, 0, 0); cairo_paint(cr); cairo_destroy(cr); cairo_t *crw = gdk_cairo_create(lualock.win); cairo_set_source_surface(crw, lualock.surface_buf, 0, 0); cairo_set_operator(crw, CAIRO_OPERATOR_SOURCE); cairo_paint(crw); cairo_destroy(crw); clear_updates(); return TRUE; }
void _gdk_x11_window_process_expose (GdkWindow *window, gulong serial, GdkRectangle *area) { cairo_region_t *invalidate_region = cairo_region_create_rectangle (area); GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (window)); if (display_x11->translate_queue) { GList *tmp_list = display_x11->translate_queue->head; while (tmp_list) { GdkWindowQueueItem *item = tmp_list->data; GList *next = tmp_list->next; /* an overflow-safe (serial < item->serial) */ if (serial - item->serial > (gulong) G_MAXLONG) { if (item->window == window) cairo_region_subtract (invalidate_region, item->antiexpose_area); } else { queue_delete_link (display_x11->translate_queue, tmp_list); queue_item_free (item); } tmp_list = next; } } if (!cairo_region_is_empty (invalidate_region)) _gdk_window_invalidate_for_expose (window, invalidate_region); cairo_region_destroy (invalidate_region); }
static cairo_status_t _combine_region (cairo_surface_t *surface, const cairo_region_t *region, const cairo_rectangle_int_t *extents) { cairo_region_t clear_region; cairo_status_t status; _cairo_region_init_rectangle (&clear_region, extents); status = cairo_region_subtract (&clear_region, region); if (unlikely (status)) return status; if (! cairo_region_is_empty (&clear_region)) { cairo_region_translate (&clear_region, -extents->x, -extents->y); status = _cairo_surface_fill_region (surface, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear_region); } _cairo_region_fini (&clear_region); return status; }
/* Warning: This call modifies the coordinates of traps */ static cairo_status_t _clip_and_composite_trapezoids (const cairo_pattern_t *src, cairo_operator_t op, cairo_surface_t *dst, cairo_traps_t *traps, cairo_clip_t *clip, cairo_antialias_t antialias) { cairo_status_t status; cairo_region_t *trap_region = NULL; cairo_region_t *clear_region = NULL; cairo_rectangle_int_t extents; cairo_composite_traps_info_t traps_info; if (_cairo_operator_bounded_by_mask (op) && traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_get_extents (dst, &extents); if (unlikely (status)) return status; status = _cairo_traps_extract_region (traps, &trap_region); if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) return status; if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t trap_extents; if (trap_region) { status = _cairo_clip_intersect_to_region (clip, trap_region); if (unlikely (status)) goto out; cairo_region_get_extents (trap_region, &trap_extents); } else { cairo_box_t trap_box; _cairo_traps_extents (traps, &trap_box); _cairo_box_round_to_rectangle (&trap_box, &trap_extents); } if (! _cairo_rectangle_intersect (&extents, &trap_extents)) { status = CAIRO_STATUS_SUCCESS; goto out; } status = _cairo_clip_intersect_to_rectangle (clip, &extents); if (unlikely (status)) goto out; } else { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; if (trap_region && !clip_surface) { /* If we optimize drawing with an unbounded operator to * _cairo_surface_fill_rectangles() or to drawing with a * clip region, then we have an additional region to clear. */ clear_region = cairo_region_create_rectangle (&extents); status = cairo_region_status (clear_region); if (unlikely (status)) goto out; status = _cairo_clip_intersect_to_region (clip, clear_region); if (unlikely (status)) goto out; cairo_region_get_extents (clear_region, &extents); status = cairo_region_subtract (clear_region, trap_region); if (unlikely (status)) goto out; if (cairo_region_is_empty (clear_region)) { cairo_region_destroy (clear_region); clear_region = NULL; } } else { status = _cairo_clip_intersect_to_rectangle (clip, &extents); } } if (unlikely (status)) goto out; if (trap_region) { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && !clip_surface) { const cairo_color_t *color; if (op == CAIRO_OPERATOR_CLEAR) { color = CAIRO_COLOR_TRANSPARENT; } else { color = &((cairo_solid_pattern_t *)src)->color; } /* Solid rectangles special case */ status = _cairo_surface_fill_region (dst, op, color, trap_region); if (!status && clear_region) { status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, clear_region); } goto out; } if ((_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE) || !clip_surface) { /* For a simple rectangle, we can just use composite(), for more * rectangles, we have to set a clip region. The cost of rasterizing * trapezoids is pretty high for most backends currently, so it's * worthwhile even if a region is needed. * * If we have a clip surface, we set it as the mask; this only works * for bounded operators other than SOURCE; for unbounded operators, * clip and mask cannot be interchanged. For SOURCE, the operator * as implemented by the backends is different in its handling * of the mask then what we want. * * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has * more than rectangle and the destination doesn't support clip * regions. In that case, we fall through. */ status = _composite_trap_region (clip, src, op, dst, trap_region, &extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { if (!status && clear_region) status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, clear_region); goto out; } } } traps_info.traps = traps; traps_info.antialias = antialias; status = _clip_and_composite (clip, op, src, _composite_traps_draw_func, &traps_info, dst, &extents); out: if (trap_region) cairo_region_destroy (trap_region); if (clear_region) cairo_region_destroy (clear_region); return status; }
/* 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); } }
/** * 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); }
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; }
JOY_GNUC_HOT void joy_gfx3d_window_submit(JoyBubble *self, GFX3D_NATIVE_Display display, GFX3D_NATIVE_Surface fb, cairo_region_t *area) { g_return_if_fail(JOY_IS_GFX3D_WINDOW(self)); g_return_if_fail(display); g_return_if_fail(fb); struct Private *priv = GET_PRIVATE(self); if (G_UNLIKELY(!priv->image)) { return; } gint x = joy_bubble_get_x(self); gint y = joy_bubble_get_y(self); cairo_region_intersect(priv->expose, priv->area); cairo_region_intersect(priv->expose, area); if (cairo_region_is_empty(priv->expose)) { return; } GFX3D_NATIVE_Surface surface = GFX3D_Image_Get_NATIVE_Surface(priv->image); gint n = cairo_region_num_rectangles(priv->expose); gdouble alpha = joy_bubble_get_alpha(self); if (1. == alpha) { GFX3D_NATIVE_Blit_Blend_Generic_Parms_t parameters = { { GFX3D_NATIVE_BLIT_BLEND_CONST_eOne, GFX3D_NATIVE_BLIT_BLEND_CONST_eSourceColor, 0, GFX3D_NATIVE_BLIT_BLEND_CONST_eDestinationColor, GFX3D_NATIVE_BLIT_BLEND_CONST_eInverseSourceAlpha, 0, GFX3D_NATIVE_BLIT_BLEND_CONST_eZero }, { GFX3D_NATIVE_BLIT_BLEND_CONST_eOne, GFX3D_NATIVE_BLIT_BLEND_CONST_eSourceAlpha, 0, GFX3D_NATIVE_BLIT_BLEND_CONST_eDestinationAlpha, GFX3D_NATIVE_BLIT_BLEND_CONST_eInverseSourceAlpha, 0, GFX3D_NATIVE_BLIT_BLEND_CONST_eZero, }, 0, 0, 0, GFX3D_NATIVE_BLIT_FilterCoeffs_eNone, GFX3D_NATIVE_BLIT_FilterCoeffs_eNone, 0, 0 }; for (gint i = 0; i < n; ++i) { cairo_rectangle_int_t rect; cairo_region_get_rectangle(priv->expose, i, &rect); GFX3D_NATIVE_Rect dst = { rect.x + x, rect.y + y, rect.width, rect.height }; GFX3D_NATIVE_Blit_Blend_Generic(display, fb, &dst, surface, (gpointer)&rect, fb, &dst, ¶meters); } } else { GFX3D_NATIVE_Blit_Blend_Generic_Parms_t parameters = { { GFX3D_NATIVE_BLIT_BLEND_CONST_eConstantAlpha, GFX3D_NATIVE_BLIT_BLEND_CONST_eSourceColor, 0, GFX3D_NATIVE_BLIT_BLEND_CONST_eDestinationColor, GFX3D_NATIVE_BLIT_BLEND_CONST_eInverseSourceAlpha, 0, GFX3D_NATIVE_BLIT_BLEND_CONST_eZero }, { GFX3D_NATIVE_BLIT_BLEND_CONST_eOne, GFX3D_NATIVE_BLIT_BLEND_CONST_eSourceAlpha, 0, GFX3D_NATIVE_BLIT_BLEND_CONST_eDestinationAlpha, GFX3D_NATIVE_BLIT_BLEND_CONST_eInverseSourceAlpha, 0, GFX3D_NATIVE_BLIT_BLEND_CONST_eZero, }, (gint)(0xff * alpha) << 24, 0, 0, GFX3D_NATIVE_BLIT_FilterCoeffs_eNone, GFX3D_NATIVE_BLIT_FilterCoeffs_eNone, 0, 0 }; for (gint i = 0; i < n; ++i) { cairo_rectangle_int_t rect; cairo_region_get_rectangle(priv->expose, i, &rect); GFX3D_NATIVE_Rect dst = { rect.x + x, rect.y + y, rect.width, rect.height }; GFX3D_NATIVE_Blit_Blend_Generic(display, fb, &dst, surface, (gpointer)&rect, fb, &dst, ¶meters); } } cairo_region_subtract(priv->expose, priv->expose); }
bool Region::IsEmpty () { return cairo_region_is_empty (cairo_region); }