gdkgl.c 26.7 KB
Newer Older
1
/* GDK - The GIMP Drawing Kit
Matthias Clasen's avatar
Matthias Clasen committed
2
 * Copyright (C) 2014 Red Hat, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include "gdkcairo.h"
#include "gdkglcontextprivate.h"

#include "gdkinternals.h"

#include <epoxy/gl.h>
#include <math.h>
27
#include <string.h>
28 29 30 31 32 33 34 35 36 37 38

static cairo_user_data_key_t direct_key;

void
gdk_cairo_surface_mark_as_direct (cairo_surface_t *surface,
                                  GdkWindow *window)
{
  cairo_surface_set_user_data (surface, &direct_key,
                               g_object_ref (window),  g_object_unref);
}

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
static const char *
get_vertex_type_name (int type)
{
  switch (type)
    {
    case GL_VERTEX_SHADER:
      return "vertex";
    case GL_GEOMETRY_SHADER:
      return "geometry";
    case GL_FRAGMENT_SHADER:
      return "fragment";
    }
  return "unknown";
}

54
static guint
55 56
create_shader (int         type,
               const char *code)
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
{
  guint shader;
  int status;

  shader = glCreateShader (type);
  glShaderSource (shader, 1, &code, NULL);
  glCompileShader (shader);

  glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
  if (status == GL_FALSE)
    {
      int log_len;
      char *buffer;

      glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &log_len);

      buffer = g_malloc (log_len + 1);
      glGetShaderInfoLog (shader, log_len, NULL, buffer);

      g_warning ("Compile failure in %s shader:\n%s\n", get_vertex_type_name (type), buffer);
      g_free (buffer);

      glDeleteShader (shader);

      return 0;
    }

  return shader;
}

87 88
static void
make_program (GdkGLContextProgram *program,
89 90
              const char          *vertex_shader_code,
              const char          *fragment_shader_code)
91
{
92
  guint vertex_shader, fragment_shader;
93 94 95 96
  int status;

  vertex_shader = create_shader (GL_VERTEX_SHADER, vertex_shader_code);
  if (vertex_shader == 0)
97
    return;
98 99 100 101 102

  fragment_shader = create_shader (GL_FRAGMENT_SHADER, fragment_shader_code);
  if (fragment_shader == 0)
    {
      glDeleteShader (vertex_shader);
103
      return;
104 105
    }

106 107 108
  program->program = glCreateProgram ();
  glAttachShader (program->program, vertex_shader);
  glAttachShader (program->program, fragment_shader);
109

110
  glLinkProgram (program->program);
111 112 113 114

  glDeleteShader (vertex_shader);
  glDeleteShader (fragment_shader);

115
  glGetProgramiv (program->program, GL_LINK_STATUS, &status);
116 117 118 119 120
  if (status == GL_FALSE)
    {
      int log_len;
      char *buffer;

121
      glGetProgramiv (program->program, GL_INFO_LOG_LENGTH, &log_len);
122 123

      buffer = g_malloc (log_len + 1);
124
      glGetProgramInfoLog (program->program, log_len, NULL, buffer);
125 126 127
      g_warning ("Linker failure: %s\n", buffer);
      g_free (buffer);

128
      glDeleteProgram (program->program);
129 130
    }

131 132 133
  program->position_location = glGetAttribLocation (program->program, "position");
  program->uv_location = glGetAttribLocation (program->program, "uv");
  program->map_location = glGetUniformLocation (program->program, "map");
134 135 136 137 138 139 140 141 142 143 144 145 146 147
}

static void
bind_vao (GdkGLContextPaintData *paint_data)
{
  if (paint_data->vertex_array_object == 0)
    {
      glGenVertexArrays (1, &paint_data->vertex_array_object);
      /* ATM we only use one VAO, so always bind it */
      glBindVertexArray (paint_data->vertex_array_object);
    }
}

static void
148
use_texture_2d_program (GdkGLContextPaintData *paint_data)
149
{
150
  static const char *vertex_shader_code_150 =
151
    "#version 150\n"
152
    "uniform sampler2D map;"
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
    "in vec2 position;\n"
    "in vec2 uv;\n"
    "out vec2 vUv;\n"
    "void main() {\n"
    "  gl_Position = vec4(position, 0, 1);\n"
    "  vUv = uv;\n"
    "}\n";
  static const char *fragment_shader_code_150 =
    "#version 150\n"
    "in vec2 vUv;\n"
    "out vec4 vertexColor;\n"
    "uniform sampler2D map;\n"
    "void main() {\n"
    "  vertexColor = texture2D (map, vUv);\n"
    "}\n";
  static const char *vertex_shader_code_130 =
    "#version 130\n"
    "uniform sampler2D map;"
171 172 173 174 175 176 177
    "attribute vec2 position;\n"
    "attribute vec2 uv;\n"
    "varying vec2 vUv;\n"
    "void main() {\n"
    "  gl_Position = vec4(position, 0, 1);\n"
    "  vUv = uv;\n"
    "}\n";
178 179
  static const char *fragment_shader_code_130 =
    "#version 130\n"
180 181 182 183 184 185
    "varying vec2 vUv;\n"
    "uniform sampler2D map;\n"
    "void main() {\n"
    "  gl_FragColor = texture2D (map, vUv);\n"
    "}\n";

186 187 188 189 190 191 192
  const char *vertex_shader_code = paint_data->is_legacy
                                 ? vertex_shader_code_130
                                 : vertex_shader_code_150;
  const char *fragment_shader_code = paint_data->is_legacy
                                   ? fragment_shader_code_130
                                   : fragment_shader_code_150;

193 194
  if (paint_data->texture_2d_quad_program.program == 0)
    make_program (&paint_data->texture_2d_quad_program, vertex_shader_code, fragment_shader_code);
195

196
  if (paint_data->current_program != &paint_data->texture_2d_quad_program)
197
    {
198 199
      paint_data->current_program = &paint_data->texture_2d_quad_program;
      glUseProgram (paint_data->current_program->program);
200 201 202
    }
}

203
static void
204
use_texture_rect_program (GdkGLContextPaintData *paint_data)
205
{
206
  static const char *vertex_shader_code_150 =
207
    "#version 150\n"
208
    "uniform sampler2DRect map;\n"
209 210 211 212 213 214 215
    "attribute vec2 position;\n"
    "attribute vec2 uv;\n"
    "varying vec2 vUv;\n"
    "void main() {\n"
    "  gl_Position = vec4(position, 0, 1);\n"
    "  vUv = uv;\n"
    "}\n";
216
  static const char *fragment_shader_code_150 =
217
    "#version 150\n"
218 219 220 221 222
    "varying vec2 vUv;\n"
    "uniform sampler2DRect map;\n"
    "void main() {\n"
    "  gl_FragColor = texture2DRect (map, vUv);\n"
    "}\n";
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
  static const char *vertex_shader_code_130 =
    "#version 130\n"
    "uniform sampler2DRect map;\n"
    "attribute vec2 position;\n"
    "attribute vec2 uv;\n"
    "varying vec2 vUv;\n"
    "void main() {\n"
    "  gl_Position = vec4(position, 0, 1);\n"
    "  vUv = uv;\n"
    "}\n";
  static const char *fragment_shader_code_130 =
    "#version 130\n"
    "varying vec2 vUv;\n"
    "uniform sampler2DRect map;\n"
    "void main() {\n"
    "  gl_FragColor = texture2DRect (map, vUv);\n"
    "}\n";

  const char *vertex_shader_code = paint_data->is_legacy
                                 ? vertex_shader_code_130
                                 : vertex_shader_code_150;
  const char *fragment_shader_code = paint_data->is_legacy
                                   ? fragment_shader_code_130
                                   : fragment_shader_code_150;
247

248 249
  if (paint_data->texture_rect_quad_program.program == 0)
    make_program (&paint_data->texture_rect_quad_program, vertex_shader_code, fragment_shader_code);
250

251
  if (paint_data->current_program != &paint_data->texture_rect_quad_program)
252
    {
253 254
      paint_data->current_program = &paint_data->texture_rect_quad_program;
      glUseProgram (paint_data->current_program->program);
255 256 257
    }
}

258
void
259 260 261 262
gdk_gl_texture_quads (GdkGLContext *paint_context,
                      guint texture_target,
                      int n_quads,
                      GdkTexturedQuad *quads)
263
{
Chun-wei Fan's avatar
Chun-wei Fan committed
264
  GdkGLContextPaintData *paint_data  = gdk_gl_context_get_paint_data (paint_context);
265
  GdkGLContextProgram *program;
266
  GdkWindow *window = gdk_gl_context_get_window (paint_context);
267 268 269
  int window_scale = gdk_window_get_scale_factor (window);
  float w = gdk_window_get_width (window) * window_scale;
  float h = gdk_window_get_height (window) * window_scale;
270
  int i;
271
  float *vertex_buffer_data;
272 273 274 275 276 277

  bind_vao (paint_data);

  if (paint_data->tmp_vertex_buffer == 0)
    glGenBuffers(1, &paint_data->tmp_vertex_buffer);

278
  if (texture_target == GL_TEXTURE_RECTANGLE_ARB)
279
    use_texture_rect_program (paint_data);
280
  else
281 282 283
    use_texture_2d_program (paint_data);

  program = paint_data->current_program;
284 285

  glActiveTexture (GL_TEXTURE0);
286
  glUniform1i(program->map_location, 0); /* Use texture unit 0 */
287

288 289
  glEnableVertexAttribArray (program->position_location);
  glEnableVertexAttribArray (program->uv_location);
290
  glBindBuffer (GL_ARRAY_BUFFER, paint_data->tmp_vertex_buffer);
291 292

  glVertexAttribPointer (program->position_location, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, NULL);
293
  glVertexAttribPointer (program->uv_location, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void *) (sizeof(float) * 2));
294

295 296 297 298 299 300 301 302
#define VERTEX_SIZE 4

#define QUAD_N_VERTICES 6

#define QUAD_SIZE (VERTEX_SIZE * QUAD_N_VERTICES)

  vertex_buffer_data = g_new (float, n_quads * QUAD_SIZE);

303 304 305
  for (i = 0; i < n_quads; i++)
    {
      GdkTexturedQuad *quad = &quads[i];
306 307 308
      float vertex_data[] = {
        (quad->x1 * 2) / w - 1, (quad->y1 * 2) / h - 1, quad->u1, quad->v1,
        (quad->x1 * 2) / w - 1, (quad->y2 * 2) / h - 1, quad->u1, quad->v2,
309
        (quad->x2 * 2) / w - 1, (quad->y1 * 2) / h - 1, quad->u2, quad->v1,
310

311 312
        (quad->x2 * 2) / w - 1, (quad->y2 * 2) / h - 1, quad->u2, quad->v2,
        (quad->x1 * 2) / w - 1, (quad->y2 * 2) / h - 1, quad->u1, quad->v2,
313
        (quad->x2 * 2) / w - 1, (quad->y1 * 2) / h - 1, quad->u2, quad->v1,
314 315
      };

316 317
      float *vertex = &vertex_buffer_data[i * QUAD_SIZE];
      memcpy (vertex, vertex_data, sizeof(vertex_data));
318 319
    }

320 321 322 323 324
  glBufferData (GL_ARRAY_BUFFER, sizeof(float) * n_quads * QUAD_SIZE, vertex_buffer_data, GL_STREAM_DRAW);
  glDrawArrays (GL_TRIANGLES, 0, n_quads * QUAD_N_VERTICES);

  g_free (vertex_buffer_data);

325 326
  glDisableVertexAttribArray (program->position_location);
  glDisableVertexAttribArray (program->uv_location);
327 328
}

329 330 331
/* x,y,width,height describes a rectangle in the gl render buffer
   coordinate space, and its top left corner is drawn at the current
   position according to the cairo translation. */
332

333
/**
334
 * gdk_cairo_draw_from_gl:
335 336
 * @cr: a cairo context
 * @window: The window we're rendering for (not necessarily into)
337
 * @source: The GL ID of the source buffer
338 339 340 341 342 343 344
 * @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
 *
345
 * This is the main way to draw GL content in GTK+. It takes a render buffer ID 
346 347
 * (@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.
348 349
 * 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.
350 351 352 353 354 355 356 357 358
 *
 * 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.
359
 *
360 361
 * Calling this may change the current GL context.
 *
362
 * Since: 3.16
363 364 365 366 367 368 369 370 371 372 373 374
 */
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)
{
375
  GdkGLContext *paint_context;
376 377 378 379 380 381
  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;
382 383
  guint framebuffer;
  int alpha_size = 0;
384
  cairo_region_t *clip_region;
385
  GdkGLContextPaintData *paint_data;
386 387 388 389 390

  impl_window = window->impl_window;

  window_scale = gdk_window_get_scale_factor (impl_window);

391 392
  paint_context = gdk_window_get_paint_gl_context (window, NULL);
  if (paint_context == NULL)
393 394 395 396 397 398 399
    {
      g_warning ("gdk_cairo_draw_gl_render_buffer failed - no paint context");
      return;
    }

  clip_region = gdk_cairo_region_from_clip (cr);

400 401
  gdk_gl_context_make_current (paint_context);
  paint_data = gdk_gl_context_get_paint_data (paint_context);
402 403 404

  if (paint_data->tmp_framebuffer == 0)
    glGenFramebuffersEXT (1, &paint_data->tmp_framebuffer);
405 406 407

  if (source_type == GL_RENDERBUFFER)
    {
408
      glBindRenderbuffer (GL_RENDERBUFFER, source);
409
      glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE,  &alpha_size);
410 411 412
    }
  else if (source_type == GL_TEXTURE)
    {
413 414
      glBindTexture (GL_TEXTURE_2D, source);

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
      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 */
439
  if ((_gdk_gl_flags & GDK_GL_SOFTWARE_DRAW_GL) == 0 &&
440
      source_type == GL_RENDERBUFFER &&
441 442 443 444 445 446
      alpha_size == 0 &&
      direct_window != NULL &&
      direct_window->current_paint.use_gl &&
      trivial_transform &&
      clip_region != NULL)
    {
447
      int unscaled_window_height;
448 449
      int i;

450 451
      /* Create a framebuffer with the source renderbuffer and
         make it the current target for reads */
452
      framebuffer = paint_data->tmp_framebuffer;
453 454 455 456 457
      glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer);
      glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                                    GL_RENDERBUFFER_EXT, source);
      glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, 0);

458 459 460 461 462
      /* Translate to impl coords */
      cairo_region_translate (clip_region, dx, dy);

      glEnable (GL_SCISSOR_TEST);

463
      gdk_window_get_unscaled_size (impl_window, NULL, &unscaled_window_height);
464 465
      glDrawBuffer (GL_BACK);

466
#define FLIP_Y(_y) (unscaled_window_height - (_y))
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513

      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);

514 515
      glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);

516 517 518 519
#undef FLIP_Y

    }
  /* For direct paint of alpha or non-alpha textures we can use texturing */
520
  else if ((_gdk_gl_flags & GDK_GL_SOFTWARE_DRAW_GL) == 0 &&
521
           source_type == GL_TEXTURE &&
522 523 524 525 526
           direct_window != NULL &&
           direct_window->current_paint.use_gl &&
           trivial_transform &&
           clip_region != NULL)
    {
527
      int unscaled_window_height;
528 529
      GLint texture_width;
      GLint texture_height;
530
      int i, n_rects, n_quads;
531 532
      GdkTexturedQuad *quads;
      cairo_rectangle_int_t clip_rect;
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559

      /* 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);
        }
560 561 562 563 564 565 566 567 568 569 570

      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);

571 572
      glEnable (GL_SCISSOR_TEST);

573
      gdk_window_get_unscaled_size (impl_window, NULL, &unscaled_window_height);
574

575
#define FLIP_Y(_y) (unscaled_window_height - (_y))
576

577 578
      cairo_region_get_extents (clip_region, &clip_rect);

579 580
      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);
581

582
      n_quads = 0;
583 584 585
      n_rects = cairo_region_num_rectangles (clip_region);
      quads = g_new (GdkTexturedQuad, n_rects);
      for (i = 0; i < n_rects; i++)
586
        {
587
          cairo_rectangle_int_t dest;
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604

          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));
605 606 607 608 609 610
              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,
              };
611

612
              quads[n_quads++] = quad;
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630

              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);
                }
            }
        }

631 632 633
      if (n_quads > 0)
        gdk_gl_texture_quads (paint_context, GL_TEXTURE_2D, n_quads, quads);

634 635
      g_free (quads);

636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
      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);

653
      framebuffer = paint_data->tmp_framebuffer;
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
      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);
        }

669 670 671 672 673 674 675 676
      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);

677 678
      glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
      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);
694

695 696
}

697
/* This is always called with the paint context current */
698 699
void
gdk_gl_texture_from_surface (cairo_surface_t *surface,
700
			     cairo_region_t  *region)
701
{
702
  GdkGLContext *paint_context;
703 704 705 706 707
  cairo_surface_t *image;
  double device_x_offset, device_y_offset;
  cairo_rectangle_int_t rect, e;
  int n_rects, i;
  GdkWindow *window;
708
  int unscaled_window_height;
709 710 711
  unsigned int texture_id;
  int window_scale;
  double sx, sy;
712 713 714
  float umax, vmax;
  gboolean use_texture_rectangle;
  guint target;
715
  paint_context = gdk_gl_context_get_current ();
716
  if ((_gdk_gl_flags & GDK_GL_SOFTWARE_DRAW_SURFACE) == 0 &&
717
      paint_context &&
718 719
      GDK_GL_CONTEXT_GET_CLASS (paint_context)->texture_from_surface &&
      GDK_GL_CONTEXT_GET_CLASS (paint_context)->texture_from_surface (paint_context, surface, region))
720 721 722
    return;

  /* Software fallback */
723
  use_texture_rectangle = gdk_gl_context_use_texture_rectangle (paint_context);
724

725
  window = gdk_gl_context_get_window (paint_context);
726
  window_scale = gdk_window_get_scale_factor (window);
727
  gdk_window_get_unscaled_size (window, NULL, &unscaled_window_height);
728 729 730 731 732 733 734 735

  sx = sy = 1;
  cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);

  cairo_surface_get_device_offset (surface,
                                   &device_x_offset, &device_y_offset);

  glGenTextures (1, &texture_id);
736 737 738 739 740 741
  if (use_texture_rectangle)
    target = GL_TEXTURE_RECTANGLE_ARB;
  else
    target = GL_TEXTURE_2D;

  glBindTexture (target, texture_id);
742
  glEnable (GL_SCISSOR_TEST);
743

744 745 746 747
  glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
748 749

  n_rects = cairo_region_num_rectangles (region);
750

751 752
#define FLIP_Y(_y) (unscaled_window_height - (_y))

753 754 755 756
  for (i = 0; i < n_rects; i++)
    {
      cairo_region_get_rectangle (region, i, &rect);

757
      glScissor (rect.x * window_scale, FLIP_Y ((rect.y + rect.height) * window_scale),
758 759 760 761 762 763 764 765 766 767 768
                 rect.width * window_scale, rect.height * window_scale);

      e = rect;
      e.x *= sx;
      e.y *= sy;
      e.x += (int)device_x_offset;
      e.y += (int)device_y_offset;
      e.width *= sx;
      e.height *= sy;
      image = cairo_surface_map_to_image (surface, &e);

769
      gdk_gl_context_upload_texture (paint_context, image, e.width, e.height, target);
770 771 772

      cairo_surface_unmap_image (surface, image);

773 774 775 776 777 778 779 780 781 782 783
      if (use_texture_rectangle)
        {
          umax = rect.width * sx;
          vmax = rect.height * sy;
        }
      else
        {
          umax = 1.0;
          vmax = 1.0;
        }

784 785
      {
        GdkTexturedQuad quad = {
786
          rect.x * window_scale, FLIP_Y(rect.y * window_scale),
787 788 789 790 791
          (rect.x + rect.width) * window_scale, FLIP_Y((rect.y + rect.height) * window_scale),
          0, 0,
          umax, vmax,
        };

792 793 794
        /* We don't want to combine the quads here, because they have different textures.
         * And we don't want to upload the unused source areas to make it one texture. */
        gdk_gl_texture_quads (paint_context, target, 1, &quad);
795
      }
796 797
    }

798 799
#undef FLIP_Y

800
  glDisable (GL_SCISSOR_TEST);
801 802
  glDeleteTextures (1, &texture_id);
}