Rect Region::GetExtents () { cairo_rectangle_int_t extents; cairo_region_get_extents (cairo_region, &extents); return Rect (extents.x, extents.y, extents.width, extents.height); }
static cairo_surface_t * ensure_image_surface (cairo_surface_t *surface, const cairo_region_t *region) { cairo_rectangle_int_t extents; cairo_surface_t *image; cairo_t *cr; int i, num_rects; if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE) return surface; cairo_region_get_extents (region, &extents); image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, extents.width, extents.height); cairo_surface_set_device_offset (image, -extents.x, -extents.y); cr = cairo_create (image); cairo_set_source_surface (cr, surface, 0, 0); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); num_rects = cairo_region_num_rectangles (region); for (i = 0; i < num_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); cairo_rectangle (cr, rect.x, rect.y, rect.width, rect.height); } cairo_fill (cr); cairo_destroy (cr); cairo_surface_destroy (surface); return image; }
// cairo_public void // cairo_region_get_extents (const cairo_region_t *region, // cairo_rectangle_int_t *extents); static int l_cairo_region_get_extents (lua_State* L) { const cairo_region_t *region = get_cairo_region_t (L, 1); cairo_rectangle_int_t *extents = get_cairo_rectangle_int_t (L, 2); cairo_region_get_extents (region, extents); return 0; }
bool wxRegion::DoGetBox( wxCoord &x, wxCoord &y, wxCoord &w, wxCoord &h ) const { if ( m_refData ) { GdkRectangle rect; #ifdef __WXGTK3__ cairo_region_get_extents(M_REGIONDATA->m_region, &rect); #else gdk_region_get_clipbox( M_REGIONDATA->m_region, &rect ); #endif x = rect.x; y = rect.y; w = rect.width; h = rect.height; return true; } else { x = 0; y = 0; w = -1; h = -1; return false; } }
static gboolean pgd_selections_drawing_area_query_tooltip (GtkWidget *area, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, PgdSelectionsDemo *demo) { gboolean over_selection; if (!demo->selected_text) return FALSE; over_selection = cairo_region_contains_point (demo->selected_region, x / demo->scale, y / demo->scale); if (over_selection) { GdkRectangle selection_area; cairo_region_get_extents (demo->selected_region, (cairo_rectangle_int_t *)&selection_area); selection_area.x *= demo->scale; selection_area.y *= demo->scale; selection_area.width *= demo->scale; selection_area.height *= demo->scale; gtk_tooltip_set_text (tooltip, demo->selected_text); gtk_tooltip_set_tip_area (tooltip, &selection_area); return TRUE; } return FALSE; }
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; }
/* This computes a (clipped version) of the inverse of the region * and expands it by the given amount */ static cairo_region_t * expand_region_inverse (cairo_region_t *region, int x_amount, int y_amount, gboolean flip) { MetaRegionBuilder builder; MetaRegionIterator iter; cairo_rectangle_int_t extents; int last_x; meta_region_builder_init (&builder); cairo_region_get_extents (region, &extents); add_expanded_rect (&builder, extents.x, extents.y - 1, extents.width, 1, x_amount, y_amount, flip); add_expanded_rect (&builder, extents.x - 1, extents.y, 1, extents.height, x_amount, y_amount, flip); add_expanded_rect (&builder, extents.x + extents.width, extents.y, 1, extents.height, x_amount, y_amount, flip); add_expanded_rect (&builder, extents.x, extents.y + extents.height, extents.width, 1, x_amount, y_amount, flip); last_x = extents.x; for (meta_region_iterator_init (&iter, region); !meta_region_iterator_at_end (&iter); meta_region_iterator_next (&iter)) { if (iter.rectangle.x > last_x) add_expanded_rect (&builder, last_x, iter.rectangle.y, iter.rectangle.x - last_x, iter.rectangle.height, x_amount, y_amount, flip); if (iter.line_end) { if (extents.x + extents.width > iter.rectangle.x + iter.rectangle.width) add_expanded_rect (&builder, iter.rectangle.x + iter.rectangle.width, iter.rectangle.y, (extents.x + extents.width) - (iter.rectangle.x + iter.rectangle.width), iter.rectangle.height, x_amount, y_amount, flip); last_x = extents.x; } else last_x = iter.rectangle.x + iter.rectangle.width; } return meta_region_builder_finish (&builder); }
static cairo_surface_t * byzanz_recorder_create_snapshot (ByzanzRecorder *recorder, const cairo_region_t *invalid) { cairo_rectangle_int_t extents; cairo_surface_t *surface; cairo_t *cr; GSequenceIter *iter; int i, num_rects; cairo_region_get_extents (invalid, &extents); cr = gdk_cairo_create (recorder->window); surface = cairo_surface_create_similar (cairo_get_target (cr), CAIRO_CONTENT_COLOR, extents.width, extents.height); cairo_destroy (cr); cairo_surface_set_device_offset (surface, -extents.x, -extents.y); cr = cairo_create (surface); num_rects = cairo_region_num_rectangles (invalid); for (i = 0; i < num_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (invalid, i, &rect); cairo_rectangle (cr, rect.x, rect.y, rect.width, rect.height); } cairo_clip (cr); for (iter = g_sequence_get_begin_iter (recorder->layers); !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) { ByzanzLayer *layer = g_sequence_get (iter); ByzanzLayerClass *klass = BYZANZ_LAYER_GET_CLASS (layer); cairo_save (cr); klass->render (layer, cr); if (cairo_status (cr)) g_critical ("error capturing image: %s", cairo_status_to_string (cairo_status (cr))); cairo_restore (cr); } cairo_destroy (cr); surface = ensure_image_surface (surface, invalid); /* adjust device offset here - the layers work in GdkScreen coordinates, the rest * of the code works in coordinates realtive to the passed in area. */ cairo_surface_set_device_offset (surface, recorder->area.x - extents.x, recorder->area.y - extents.y); return surface; }
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; }
IntRect getPangoRegionExtents(PangoRegionType region) { cairo_rectangle_int_t rectangle; cairo_region_get_extents(region, &rectangle); return IntRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); }
cairo_status_t _cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface, cairo_clip_t *clip) { char stack[512]; cairo_rectangle_int_t extents; int num_rects; RGNDATA *data; size_t data_size; RECT *rects; int i; HRGN gdi_region; cairo_status_t status; cairo_region_t *region; /* The semantics we want is that any clip set by cairo combines * is intersected with the clip on device context that the * surface was created for. To implement this, we need to * save the original clip when first setting a clip on surface. */ assert (_cairo_clip_is_region (clip)); region = _cairo_clip_get_region (clip); if (region == NULL) return CAIRO_STATUS_SUCCESS; cairo_region_get_extents (region, &extents); num_rects = cairo_region_num_rectangles (region); /* XXX see notes in _cairo_win32_save_initial_clip -- * this code will interact badly with a HDC which had an initial * world transform -- we should probably manually transform the * region rects, because SelectClipRgn takes device units, not * logical units (unlike IntersectClipRect). */ data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); if (data_size > sizeof (stack)) { data = malloc (data_size); if (!data) return _cairo_error(CAIRO_STATUS_NO_MEMORY); } else data = (RGNDATA *)stack; data->rdh.dwSize = sizeof (RGNDATAHEADER); data->rdh.iType = RDH_RECTANGLES; data->rdh.nCount = num_rects; data->rdh.nRgnSize = num_rects * sizeof (RECT); data->rdh.rcBound.left = extents.x; data->rdh.rcBound.top = extents.y; data->rdh.rcBound.right = extents.x + extents.width; data->rdh.rcBound.bottom = extents.y + extents.height; rects = (RECT *)data->Buffer; for (i = 0; i < num_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); rects[i].left = rect.x; rects[i].top = rect.y; rects[i].right = rect.x + rect.width; rects[i].bottom = rect.y + rect.height; } gdi_region = ExtCreateRegion (NULL, data_size, data); if ((char *)data != stack) free (data); if (!gdi_region) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* AND the new region into our DC */ status = CAIRO_STATUS_SUCCESS; if (ExtSelectClipRgn (surface->win32.dc, gdi_region, RGN_AND) == ERROR) status = _cairo_win32_print_gdi_error (__FUNCTION__); DeleteObject (gdi_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; }
static void make_shadow (MetaShadow *shadow, cairo_region_t *region) { int d = get_box_filter_size (shadow->key.radius); int spread = get_shadow_spread (shadow->key.radius); cairo_rectangle_int_t extents; cairo_region_t *row_convolve_region; cairo_region_t *column_convolve_region; guchar *buffer; int buffer_width; int buffer_height; int x_offset; int y_offset; int n_rectangles, j, k; cairo_region_get_extents (region, &extents); /* In the case where top_fade >= 0 and the portion above the top * edge of the shape will be cropped, it seems like we could create * a smaller buffer and omit the top portion, but actually, in our * multi-pass blur algorithm, the blur into the area above the window * in the first pass will contribute back to the final pixel values * for the top pixels, so we create a buffer as if we weren't cropping * and only crop when creating the CoglTexture. */ buffer_width = extents.width + 2 * spread; buffer_height = extents.height + 2 * spread; /* Round up so we have aligned rows/columns */ buffer_width = (buffer_width + 3) & ~3; buffer_height = (buffer_height + 3) & ~3; /* Square buffer allows in-place swaps, which are roughly 70% faster, but we * don't want to over-allocate too much memory. */ if (buffer_height < buffer_width && buffer_height > (3 * buffer_width) / 4) buffer_height = buffer_width; if (buffer_width < buffer_height && buffer_width > (3 * buffer_height) / 4) buffer_width = buffer_height; buffer = g_malloc0 (buffer_width * buffer_height); /* Blurring with multiple box-blur passes is fast, but (especially for * large shadow sizes) we can improve efficiency by restricting the blur * to the region that actually needs to be blurred. */ row_convolve_region = meta_make_border_region (region, spread, spread, FALSE); column_convolve_region = meta_make_border_region (region, 0, spread, TRUE); /* Offsets between coordinates of the regions and coordinates in the buffer */ x_offset = spread; y_offset = spread; /* Step 1: unblurred image */ n_rectangles = cairo_region_num_rectangles (region); for (k = 0; k < n_rectangles; k++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, k, &rect); for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++) memset (buffer + buffer_width * j + x_offset + rect.x, 255, rect.width); } /* Step 2: swap rows and columns */ buffer = flip_buffer (buffer, buffer_width, buffer_height); /* Step 3: blur rows (really columns) */ blur_rows (column_convolve_region, y_offset, x_offset, buffer, buffer_height, buffer_width, d); /* Step 4: swap rows and columns */ buffer = flip_buffer (buffer, buffer_height, buffer_width); /* Step 5: blur rows */ blur_rows (row_convolve_region, x_offset, y_offset, buffer, buffer_width, buffer_height, d); /* Step 6: fade out the top, if applicable */ if (shadow->key.top_fade >= 0) { for (j = y_offset; j < y_offset + MIN (shadow->key.top_fade, extents.height + shadow->outer_border_bottom); j++) fade_bytes(buffer + j * buffer_width, buffer_width, j - y_offset, shadow->key.top_fade); } /* We offset the passed in pixels to crop off the extra area we allocated at the top * in the case of top_fade >= 0. We also account for padding at the left for symmetry * though that doesn't currently occur. */ shadow->texture = cogl_texture_new_from_data (shadow->outer_border_left + extents.width + shadow->outer_border_right, shadow->outer_border_top + extents.height + shadow->outer_border_bottom, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_A_8, COGL_PIXEL_FORMAT_ANY, buffer_width, (buffer + (y_offset - shadow->outer_border_top) * buffer_width + (x_offset - shadow->outer_border_left))); cairo_region_destroy (row_convolve_region); cairo_region_destroy (column_convolve_region); g_free (buffer); shadow->material = meta_create_texture_material (shadow->texture); }
gboolean byzanz_deserialize (GInputStream * stream, guint64 * msecs_out, cairo_surface_t ** surface_out, cairo_region_t ** region_out, GCancellable * cancellable, GError ** error) { guint i, stride; cairo_rectangle_int_t extents, *rects; cairo_region_t *region; cairo_surface_t *surface; guchar *data; guint32 n; int y; g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE); g_return_val_if_fail (msecs_out != NULL, FALSE); g_return_val_if_fail (surface_out != NULL, FALSE); g_return_val_if_fail (region_out != NULL, FALSE); if (!g_input_stream_read_all (stream, msecs_out, sizeof (guint64), NULL, cancellable, error) || !g_input_stream_read_all (stream, &n, sizeof (guint32), NULL, cancellable, error)) return FALSE; if (n == 0) { /* end of stream */ *surface_out = NULL; *region_out = NULL; return TRUE; } region = cairo_region_create (); rects = g_new (cairo_rectangle_int_t, n); surface = NULL; for (i = 0; i < n; i++) { gint ints[4]; if (!g_input_stream_read_all (stream, ints, sizeof (ints), NULL, cancellable, error)) goto fail; rects[i].x = ints[0]; rects[i].y = ints[1]; rects[i].width = ints[2]; rects[i].height = ints[3]; cairo_region_union_rectangle (region, &rects[i]); } cairo_region_get_extents (region, &extents); surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, extents.width, extents.height); cairo_surface_set_device_offset (surface, -extents.x, -extents.y); stride = cairo_image_surface_get_stride (surface); for (i = 0; i < n; i++) { data = cairo_image_surface_get_data (surface) + stride * (rects[i].y - extents.y) + sizeof (guint32) * (rects[i].x - extents.x); for (y = 0; y < rects[i].height; y++) { if (!g_input_stream_read_all (stream, data, rects[i].width * sizeof (guint32), NULL, cancellable, error)) goto fail; data += stride; } } g_free (rects); *region_out = region; *surface_out = surface; return TRUE; fail: if (surface) cairo_surface_destroy (surface); cairo_region_destroy (region); g_free (rects); return FALSE; }
/** * 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); }