Esempio n. 1
0
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);
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
// 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;
}
Esempio n. 4
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;
    }
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
/* 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);
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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;
}
Esempio n. 12
0
/* 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;
}
Esempio n. 13
0
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);
}
Esempio n. 14
0
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;
}
Esempio n. 15
0
/**
 * 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);

}