Beispiel #1
0
// cairo_public cairo_region_t *
// cairo_region_copy (const cairo_region_t *original);
static int l_cairo_region_copy (lua_State* L)
{
    const cairo_region_t *original = get_cairo_region_t (L, 1);
    cairo_region_t *v = cairo_region_copy (original);
    lua_pushlightuserdata(L, v);
    return 1;
}
static void
set_unobscured_region (MetaShapedTexture *self,
                       cairo_region_t    *unobscured_region)
{
  MetaShapedTexturePrivate *priv = self->priv;

  g_clear_pointer (&priv->unobscured_region, (GDestroyNotify) cairo_region_destroy);
  if (unobscured_region)
    {
      guint width, height;

      if (priv->texture)
        {
          width = priv->tex_width;
          height = priv->tex_height;
        }
      else
        {
          width = priv->fallback_width;
          height = priv->fallback_height;
        }

      cairo_rectangle_int_t bounds = { 0, 0, width, height };
      priv->unobscured_region = cairo_region_copy (unobscured_region);
      cairo_region_intersect_rectangle (priv->unobscured_region, &bounds);
    }
}
Beispiel #3
0
cairo_region_t *
meta_region_transform (cairo_region_t       *region,
                       MetaMonitorTransform  transform,
                       int                   width,
                       int                   height)
{
  int n_rects, i;
  cairo_rectangle_int_t *rects;
  cairo_region_t *transformed_region;

  if (transform == META_MONITOR_TRANSFORM_NORMAL)
    return cairo_region_copy (region);

  n_rects = cairo_region_num_rectangles (region);

  rects = g_new0 (cairo_rectangle_int_t, n_rects);
  for (i = 0; i < n_rects; i++)
    {
      cairo_region_get_rectangle (region, i, &rects[i]);

      meta_rectangle_transform (&rects[i],
                                transform,
                                width,
                                height,
                                &rects[i]);
    }

  transformed_region = cairo_region_create_rectangles (rects, n_rects);

  g_free (rects);

  return transformed_region;
}
Beispiel #4
0
cairo_region_t *
meta_region_scale (cairo_region_t *region, int scale)
{
  int n_rects, i;
  cairo_rectangle_int_t *rects;
  cairo_region_t *scaled_region;

  if (scale == 1)
    return cairo_region_copy (region);

  n_rects = cairo_region_num_rectangles (region);

  rects = g_malloc (sizeof(cairo_rectangle_int_t) * n_rects);
  for (i = 0; i < n_rects; i++)
    {
      cairo_region_get_rectangle (region, i, &rects[i]);
      rects[i].x *= scale;
      rects[i].y *= scale;
      rects[i].width *= scale;
      rects[i].height *= scale;
    }

  scaled_region = cairo_region_create_rectangles (rects, n_rects);

  g_free (rects);

  return scaled_region;
}
Beispiel #5
0
cairo_region_t *
meta_region_scale_double (cairo_region_t       *region,
                          double                scale,
                          MetaRoundingStrategy  rounding_strategy)
{
  int n_rects, i;
  cairo_rectangle_int_t *rects;
  cairo_region_t *scaled_region;

  g_return_val_if_fail (scale > 0.0, NULL);

  if (scale == 1.0)
    return cairo_region_copy (region);

  n_rects = cairo_region_num_rectangles (region);

  rects = g_malloc (sizeof(cairo_rectangle_int_t) * n_rects);
  for (i = 0; i < n_rects; i++)
    {
      cairo_region_get_rectangle (region, i, &rects[i]);

      meta_rectangle_scale_double (&rects[i], scale, rounding_strategy,
                                   &rects[i]);
    }

  scaled_region = cairo_region_create_rectangles (rects, n_rects);

  g_free (rects);

  return scaled_region;
}
Beispiel #6
0
/**
 * 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;
}
Beispiel #7
0
    wxRegionRefData(const wxRegionRefData& refData)
        : wxGDIRefData()
    {
#ifdef __WXGTK3__
        m_region = cairo_region_copy(refData.m_region);
#else
        m_region = gdk_region_copy(refData.m_region);
#endif
    }
static void
set_clip_region (MetaShapedTexture *self,
                 cairo_region_t    *clip_region)
{
  MetaShapedTexturePrivate *priv = self->priv;

  g_clear_pointer (&priv->clip_region, (GDestroyNotify) cairo_region_destroy);
  if (clip_region)
    priv->clip_region = cairo_region_copy (clip_region);
}
Beispiel #9
0
static void
set_clip_region (MetaBackgroundActor *self,
                 cairo_region_t      *clip_region)
{
  MetaBackgroundActorPrivate *priv = self->priv;

  g_clear_pointer (&priv->clip_region, (GDestroyNotify) cairo_region_destroy);
  if (clip_region)
    priv->clip_region = cairo_region_copy (clip_region);
}
Beispiel #10
0
/* 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);
}
static cairo_status_t
_cairo_clip_path_reapply_clip_path_translate (cairo_clip_t      *clip,
					      cairo_clip_path_t *other_path,
					      int tx, int ty)
{
    cairo_status_t status;
    cairo_clip_path_t *clip_path;

    if (other_path->prev != NULL) {
        status = _cairo_clip_path_reapply_clip_path_translate (clip,
							       other_path->prev,
							       tx, ty);
	if (unlikely (status))
	    return status;
    }

    clip_path = _cairo_clip_path_create (clip);
    if (unlikely (clip_path == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    status = _cairo_path_fixed_init_copy (&clip_path->path,
					  &other_path->path);
    if (unlikely (status)) {
	_cairo_clip_path_destroy (clip_path);
	return status;
    }

    _cairo_path_fixed_translate (&clip_path->path,
				 _cairo_fixed_from_int (tx),
				 _cairo_fixed_from_int (ty));

    clip_path->fill_rule = other_path->fill_rule;
    clip_path->tolerance = other_path->tolerance;
    clip_path->antialias = other_path->antialias;

    clip_path->flags = other_path->flags;
    if (other_path->region != NULL) {
	clip_path->region = cairo_region_copy (other_path->region);
	cairo_region_translate (clip_path->region, tx, ty);
    }
    clip_path->surface = cairo_surface_reference (other_path->surface);

    clip_path->extents = other_path->extents;
    clip_path->extents.x += tx;
    clip_path->extents.y += ty;

    return CAIRO_STATUS_SUCCESS;
}
Beispiel #12
0
cairo_region_t *
meta_region_crop_and_scale (cairo_region_t *region,
                            ClutterRect    *src_rect,
                            int             dst_width,
                            int             dst_height)
{
  int n_rects, i;
  cairo_rectangle_int_t *rects;
  cairo_region_t *viewport_region;

  if (src_rect->size.width == dst_width &&
      src_rect->size.height == dst_height &&
      roundf (src_rect->origin.x) == src_rect->origin.x &&
      roundf (src_rect->origin.y) == src_rect->origin.y)
    {
      viewport_region = cairo_region_copy (region);

      if (src_rect->origin.x != 0 || src_rect->origin.y != 0)
        {
          cairo_region_translate (viewport_region,
                                  (int) src_rect->origin.x,
                                  (int) src_rect->origin.y);
        }

      return viewport_region;
    }

  n_rects = cairo_region_num_rectangles (region);

  rects = g_new0 (cairo_rectangle_int_t, n_rects);
  for (i = 0; i < n_rects; i++)
    {
      cairo_region_get_rectangle (region, i, &rects[i]);

      meta_rectangle_crop_and_scale (&rects[i],
                                     src_rect,
                                     dst_width,
                                     dst_height,
                                     &rects[i]);
    }

  viewport_region = cairo_region_create_rectangles (rects, n_rects);

  g_free (rects);

  return viewport_region;
}
Beispiel #13
0
void
gtk_snapshot_push_rounded_clip (GtkSnapshot          *snapshot,
                                const GskRoundedRect *bounds,
                                const char           *name,
                                ...)
{
  GskRoundedRect *real_bounds;
  cairo_region_t *clip;
  cairo_rectangle_int_t rect;
  char *str;

  real_bounds = g_slice_new (GskRoundedRect);
  gsk_rounded_rect_init_copy (real_bounds, bounds);
  gsk_rounded_rect_offset (real_bounds, snapshot->state->translate_x, snapshot->state->translate_y);

  if (name)
    {
      va_list args;

      va_start (args, name);
      str = g_strdup_vprintf (name, args);
      va_end (args);
    }
  else
    str = NULL;
  
  rectangle_init_from_graphene (&rect, &real_bounds->bounds);
  if (snapshot->state->clip_region)
    {
      clip = cairo_region_copy (snapshot->state->clip_region);
      cairo_region_intersect_rectangle (clip, &rect);
    }
  else
    {
      clip = cairo_region_create_rectangle (&rect);
    }
  snapshot->state = gtk_snapshot_state_new (snapshot->state,
                                            str,
                                            clip,
                                            snapshot->state->translate_x,
                                            snapshot->state->translate_y,
                                            gtk_snapshot_collect_rounded_clip,
                                            real_bounds);

  cairo_region_destroy (clip);
}
Beispiel #14
0
GtkInspectorRecording *
gtk_inspector_render_recording_new (gint64                timestamp,
                                    GskProfiler          *profiler,
                                    const GdkRectangle   *area,
                                    const cairo_region_t *clip_region,
                                    GskRenderNode        *node)
{
  GtkInspectorRenderRecording *recording;

  recording = g_object_new (GTK_TYPE_INSPECTOR_RENDER_RECORDING,
                            "timestamp", timestamp,
                            NULL);

  collect_profiler_info (recording, profiler);
  recording->area = *area;
  recording->clip_region = cairo_region_copy (clip_region);
  recording->node = gsk_render_node_ref (node);

  return GTK_INSPECTOR_RECORDING (recording);
}
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;
}
Beispiel #16
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);

}