File: | cdk/cdkgl.c |
Warning: | line 697, column 59 This statement is never executed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* CDK - The GIMP Drawing Kit |
2 | * Copyright (C) 2014 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include "config.h" |
19 | |
20 | #include "cdkcairo.h" |
21 | #include "cdkglcontextprivate.h" |
22 | |
23 | #include "cdkinternals.h" |
24 | |
25 | #ifdef CDK_WINDOWING_WIN32 |
26 | # include "win32/cdkwin32.h" |
27 | #endif |
28 | |
29 | #include <epoxy/gl.h> |
30 | #include <math.h> |
31 | #include <string.h> |
32 | |
33 | static cairo_user_data_key_t direct_key; |
34 | |
35 | void |
36 | cdk_cairo_surface_mark_as_direct (cairo_surface_t *surface, |
37 | CdkWindow *window) |
38 | { |
39 | cairo_surface_set_user_data (surface, &direct_key, |
40 | g_object_ref (window)((__typeof__ (window)) (g_object_ref) (window)), g_object_unref); |
41 | } |
42 | |
43 | static const char * |
44 | get_vertex_type_name (int type) |
45 | { |
46 | switch (type) |
47 | { |
48 | case GL_VERTEX_SHADER0x8B31: |
49 | return "vertex"; |
50 | case GL_GEOMETRY_SHADER0x8DD9: |
51 | return "geometry"; |
52 | case GL_FRAGMENT_SHADER0x8B30: |
53 | return "fragment"; |
54 | } |
55 | return "unknown"; |
56 | } |
57 | |
58 | static guint |
59 | create_shader (int type, |
60 | const char *code) |
61 | { |
62 | guint shader; |
63 | int status; |
64 | |
65 | shader = glCreateShaderepoxy_glCreateShader (type); |
66 | glShaderSourceepoxy_glShaderSource (shader, 1, &code, NULL((void*)0)); |
67 | glCompileShaderepoxy_glCompileShader (shader); |
68 | |
69 | glGetShaderivepoxy_glGetShaderiv (shader, GL_COMPILE_STATUS0x8B81, &status); |
70 | if (status == GL_FALSE0) |
71 | { |
72 | int log_len; |
73 | char *buffer; |
74 | |
75 | glGetShaderivepoxy_glGetShaderiv (shader, GL_INFO_LOG_LENGTH0x8B84, &log_len); |
76 | |
77 | buffer = g_malloc (log_len + 1); |
78 | glGetShaderInfoLogepoxy_glGetShaderInfoLog (shader, log_len, NULL((void*)0), buffer); |
79 | |
80 | g_warning ("Compile failure in %s shader:\n%s", get_vertex_type_name (type), buffer); |
81 | g_free (buffer); |
82 | |
83 | glDeleteShaderepoxy_glDeleteShader (shader); |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | return shader; |
89 | } |
90 | |
91 | static void |
92 | make_program (CdkGLContextProgram *program, |
93 | const char *vertex_shader_path, |
94 | const char *fragment_shader_path) |
95 | { |
96 | guint vertex_shader, fragment_shader; |
97 | GBytes *source; |
98 | int status; |
99 | |
100 | source = g_resources_lookup_data (vertex_shader_path, 0, NULL((void*)0)); |
101 | g_assert (source != NULL)do { if (source != ((void*)0)) ; else g_assertion_message_expr ("Cdk", "cdkgl.c", 101, ((const char*) (__func__)), "source != NULL" ); } while (0); |
102 | vertex_shader = create_shader (GL_VERTEX_SHADER0x8B31, g_bytes_get_data (source, NULL((void*)0))); |
103 | g_bytes_unref (source); |
104 | if (vertex_shader == 0) |
105 | return; |
106 | |
107 | source = g_resources_lookup_data (fragment_shader_path, 0, NULL((void*)0)); |
108 | g_assert (source != NULL)do { if (source != ((void*)0)) ; else g_assertion_message_expr ("Cdk", "cdkgl.c", 108, ((const char*) (__func__)), "source != NULL" ); } while (0); |
109 | fragment_shader = create_shader (GL_FRAGMENT_SHADER0x8B30, g_bytes_get_data (source, NULL((void*)0))); |
110 | g_bytes_unref (source); |
111 | if (fragment_shader == 0) |
112 | { |
113 | glDeleteShaderepoxy_glDeleteShader (vertex_shader); |
114 | return; |
115 | } |
116 | |
117 | program->program = glCreateProgramepoxy_glCreateProgram (); |
118 | glAttachShaderepoxy_glAttachShader (program->program, vertex_shader); |
119 | glAttachShaderepoxy_glAttachShader (program->program, fragment_shader); |
120 | |
121 | glLinkProgramepoxy_glLinkProgram (program->program); |
122 | |
123 | glDeleteShaderepoxy_glDeleteShader (vertex_shader); |
124 | glDeleteShaderepoxy_glDeleteShader (fragment_shader); |
125 | |
126 | glGetProgramivepoxy_glGetProgramiv (program->program, GL_LINK_STATUS0x8B82, &status); |
127 | if (status == GL_FALSE0) |
128 | { |
129 | int log_len; |
130 | char *buffer; |
131 | |
132 | glGetProgramivepoxy_glGetProgramiv (program->program, GL_INFO_LOG_LENGTH0x8B84, &log_len); |
133 | |
134 | buffer = g_malloc (log_len + 1); |
135 | glGetProgramInfoLogepoxy_glGetProgramInfoLog (program->program, log_len, NULL((void*)0), buffer); |
136 | g_warning ("Linker failure: %s\n", buffer); |
137 | g_free (buffer); |
138 | |
139 | glDeleteProgramepoxy_glDeleteProgram (program->program); |
140 | } |
141 | |
142 | program->position_location = glGetAttribLocationepoxy_glGetAttribLocation (program->program, "position"); |
143 | program->uv_location = glGetAttribLocationepoxy_glGetAttribLocation (program->program, "uv"); |
144 | program->map_location = glGetUniformLocationepoxy_glGetUniformLocation (program->program, "map"); |
145 | program->flip_location = glGetUniformLocationepoxy_glGetUniformLocation (program->program, "flipColors"); |
146 | } |
147 | |
148 | static void |
149 | bind_vao (CdkGLContextPaintData *paint_data) |
150 | { |
151 | if (paint_data->vertex_array_object == 0) |
152 | { |
153 | glGenVertexArraysepoxy_glGenVertexArrays (1, &paint_data->vertex_array_object); |
154 | /* ATM we only use one VAO, so always bind it */ |
155 | glBindVertexArrayepoxy_glBindVertexArray (paint_data->vertex_array_object); |
156 | } |
157 | } |
158 | |
159 | static void |
160 | use_texture_gles_program (CdkGLContextPaintData *paint_data) |
161 | { |
162 | if (paint_data->texture_2d_quad_program.program == 0) |
163 | make_program (&paint_data->texture_2d_quad_program, |
164 | "/org/ctk/libcdk/glsl/gles2-texture.vs.glsl", |
165 | "/org/ctk/libcdk/glsl/gles2-texture.fs.glsl"); |
166 | |
167 | if (paint_data->current_program != &paint_data->texture_2d_quad_program) |
168 | { |
169 | paint_data->current_program = &paint_data->texture_2d_quad_program; |
170 | glUseProgramepoxy_glUseProgram (paint_data->current_program->program); |
171 | } |
172 | } |
173 | |
174 | static void |
175 | use_texture_2d_program (CdkGLContextPaintData *paint_data) |
176 | { |
177 | const char *vertex_shader_path = paint_data->is_legacy |
178 | ? "/org/ctk/libcdk/glsl/gl2-texture-2d.vs.glsl" |
179 | : "/org/ctk/libcdk/glsl/gl3-texture-2d.vs.glsl"; |
180 | |
181 | const char *fragment_shader_path = paint_data->is_legacy |
182 | ? "/org/ctk/libcdk/glsl/gl2-texture-2d.fs.glsl" |
183 | : "/org/ctk/libcdk/glsl/gl3-texture-2d.fs.glsl"; |
184 | |
185 | if (paint_data->texture_2d_quad_program.program == 0) |
186 | make_program (&paint_data->texture_2d_quad_program, vertex_shader_path, fragment_shader_path); |
187 | |
188 | if (paint_data->current_program != &paint_data->texture_2d_quad_program) |
189 | { |
190 | paint_data->current_program = &paint_data->texture_2d_quad_program; |
191 | glUseProgramepoxy_glUseProgram (paint_data->current_program->program); |
192 | } |
193 | } |
194 | |
195 | static void |
196 | use_texture_rect_program (CdkGLContextPaintData *paint_data) |
197 | { |
198 | const char *vertex_shader_path = paint_data->is_legacy |
199 | ? "/org/ctk/libcdk/glsl/gl2-texture-rect.vs.glsl" |
200 | : "/org/ctk/libcdk/glsl/gl3-texture-rect.vs.glsl"; |
201 | |
202 | const char *fragment_shader_path = paint_data->is_legacy |
203 | ? "/org/ctk/libcdk/glsl/gl2-texture-rect.fs.glsl" |
204 | : "/org/ctk/libcdk/glsl/gl3-texture-rect.vs.glsl"; |
205 | |
206 | if (paint_data->texture_rect_quad_program.program == 0) |
207 | make_program (&paint_data->texture_rect_quad_program, vertex_shader_path, fragment_shader_path); |
208 | |
209 | if (paint_data->current_program != &paint_data->texture_rect_quad_program) |
210 | { |
211 | paint_data->current_program = &paint_data->texture_rect_quad_program; |
212 | glUseProgramepoxy_glUseProgram (paint_data->current_program->program); |
213 | } |
214 | } |
215 | |
216 | void |
217 | cdk_gl_texture_quads (CdkGLContext *paint_context, |
218 | guint texture_target, |
219 | int n_quads, |
220 | CdkTexturedQuad *quads, |
221 | gboolean flip_colors) |
222 | { |
223 | CdkGLContextPaintData *paint_data = cdk_gl_context_get_paint_data (paint_context); |
224 | CdkGLContextProgram *program; |
225 | CdkWindow *window = cdk_gl_context_get_window (paint_context); |
226 | int window_scale = cdk_window_get_scale_factor (window); |
227 | float w = cdk_window_get_width (window) * window_scale; |
228 | float h = cdk_window_get_height (window) * window_scale; |
229 | int i; |
230 | float *vertex_buffer_data; |
231 | |
232 | bind_vao (paint_data); |
233 | |
234 | if (paint_data->tmp_vertex_buffer == 0) |
235 | glGenBuffersepoxy_glGenBuffers(1, &paint_data->tmp_vertex_buffer); |
236 | |
237 | if (paint_data->use_es) |
238 | use_texture_gles_program (paint_data); |
239 | else |
240 | { |
241 | if (texture_target == GL_TEXTURE_RECTANGLE_ARB0x84F5) |
242 | use_texture_rect_program (paint_data); |
243 | else |
244 | use_texture_2d_program (paint_data); |
245 | } |
246 | |
247 | program = paint_data->current_program; |
248 | |
249 | /* Use texture unit 0 */ |
250 | glActiveTextureepoxy_glActiveTexture (GL_TEXTURE00x84C0); |
251 | glUniform1iepoxy_glUniform1i(program->map_location, 0); |
252 | |
253 | /* Flip 'R' and 'B' colors on GLES, if necessary */ |
254 | if (cdk_gl_context_get_use_es (paint_context)) |
255 | glUniform1iepoxy_glUniform1i (program->flip_location, flip_colors ? 1 : 0); |
256 | |
257 | glEnableVertexAttribArrayepoxy_glEnableVertexAttribArray (program->position_location); |
258 | glEnableVertexAttribArrayepoxy_glEnableVertexAttribArray (program->uv_location); |
259 | glBindBufferepoxy_glBindBuffer (GL_ARRAY_BUFFER0x8892, paint_data->tmp_vertex_buffer); |
260 | |
261 | glVertexAttribPointerepoxy_glVertexAttribPointer (program->position_location, 2, GL_FLOAT0x1406, GL_FALSE0, sizeof(float) * 4, NULL((void*)0)); |
262 | glVertexAttribPointerepoxy_glVertexAttribPointer (program->uv_location, 2, GL_FLOAT0x1406, GL_FALSE0, sizeof(float) * 4, (void *) (sizeof(float) * 2)); |
263 | |
264 | #define VERTEX_SIZE4 4 |
265 | |
266 | #define QUAD_N_VERTICES6 6 |
267 | |
268 | #define QUAD_SIZE(4 * 6) (VERTEX_SIZE4 * QUAD_N_VERTICES6) |
269 | |
270 | vertex_buffer_data = g_new (float, n_quads * QUAD_SIZE)((float *) g_malloc_n ((n_quads * (4 * 6)), sizeof (float))); |
271 | |
272 | for (i = 0; i < n_quads; i++) |
273 | { |
274 | CdkTexturedQuad *quad = &quads[i]; |
275 | float vertex_data[] = { |
276 | (quad->x1 * 2) / w - 1, (quad->y1 * 2) / h - 1, quad->u1, quad->v1, |
277 | (quad->x1 * 2) / w - 1, (quad->y2 * 2) / h - 1, quad->u1, quad->v2, |
278 | (quad->x2 * 2) / w - 1, (quad->y1 * 2) / h - 1, quad->u2, quad->v1, |
279 | |
280 | (quad->x2 * 2) / w - 1, (quad->y2 * 2) / h - 1, quad->u2, quad->v2, |
281 | (quad->x1 * 2) / w - 1, (quad->y2 * 2) / h - 1, quad->u1, quad->v2, |
282 | (quad->x2 * 2) / w - 1, (quad->y1 * 2) / h - 1, quad->u2, quad->v1, |
283 | }; |
284 | |
285 | float *vertex = &vertex_buffer_data[i * QUAD_SIZE(4 * 6)]; |
286 | memcpy (vertex, vertex_data, sizeof(vertex_data)); |
287 | } |
288 | |
289 | glBufferDataepoxy_glBufferData (GL_ARRAY_BUFFER0x8892, sizeof(float) * n_quads * QUAD_SIZE(4 * 6), vertex_buffer_data, GL_STREAM_DRAW0x88E0); |
290 | glDrawArraysepoxy_glDrawArrays (GL_TRIANGLES0x0004, 0, n_quads * QUAD_N_VERTICES6); |
291 | |
292 | g_free (vertex_buffer_data); |
293 | |
294 | glDisableVertexAttribArrayepoxy_glDisableVertexAttribArray (program->position_location); |
295 | glDisableVertexAttribArrayepoxy_glDisableVertexAttribArray (program->uv_location); |
296 | } |
297 | |
298 | /* x,y,width,height describes a rectangle in the gl render buffer |
299 | coordinate space, and its top left corner is drawn at the current |
300 | position according to the cairo translation. */ |
301 | |
302 | /** |
303 | * cdk_cairo_draw_from_gl: |
304 | * @cr: a cairo context |
305 | * @window: The window we're rendering for (not necessarily into) |
306 | * @source: The GL ID of the source buffer |
307 | * @source_type: The type of the @source |
308 | * @buffer_scale: The scale-factor that the @source buffer is allocated for |
309 | * @x: The source x position in @source to start copying from in GL coordinates |
310 | * @y: The source y position in @source to start copying from in GL coordinates |
311 | * @width: The width of the region to draw |
312 | * @height: The height of the region to draw |
313 | * |
314 | * This is the main way to draw GL content in CTK+. It takes a render buffer ID |
315 | * (@source_type == #GL_RENDERBUFFER) or a texture id (@source_type == #GL_TEXTURE) |
316 | * and draws it onto @cr with an OVER operation, respecting the current clip. |
317 | * The top left corner of the rectangle specified by @x, @y, @width and @height |
318 | * will be drawn at the current (0,0) position of the cairo_t. |
319 | * |
320 | * This will work for *all* cairo_t, as long as @window is realized, but the |
321 | * fallback implementation that reads back the pixels from the buffer may be |
322 | * used in the general case. In the case of direct drawing to a window with |
323 | * no special effects applied to @cr it will however use a more efficient |
324 | * approach. |
325 | * |
326 | * For #GL_RENDERBUFFER the code will always fall back to software for buffers |
327 | * with alpha components, so make sure you use #GL_TEXTURE if using alpha. |
328 | * |
329 | * Calling this may change the current GL context. |
330 | * |
331 | * Since: 3.16 |
332 | */ |
333 | void |
334 | cdk_cairo_draw_from_gl (cairo_t *cr, |
335 | CdkWindow *window, |
336 | int source, |
337 | int source_type, |
338 | int buffer_scale, |
339 | int x, |
340 | int y, |
341 | int width, |
342 | int height) |
343 | { |
344 | CdkGLContext *paint_context; |
345 | cairo_surface_t *image; |
346 | cairo_matrix_t matrix; |
347 | int dx, dy, window_scale; |
348 | gboolean trivial_transform; |
349 | cairo_surface_t *group_target; |
350 | CdkWindow *direct_window, *impl_window; |
351 | guint framebuffer; |
352 | int alpha_size = 0; |
353 | cairo_region_t *clip_region; |
354 | CdkGLContextPaintData *paint_data; |
355 | |
356 | impl_window = window->impl_window; |
357 | |
358 | window_scale = cdk_window_get_scale_factor (impl_window); |
359 | |
360 | paint_context = cdk_window_get_paint_gl_context (window, NULL((void*)0)); |
361 | if (paint_context == NULL((void*)0)) |
362 | { |
363 | g_warning ("cdk_cairo_draw_gl_render_buffer failed - no paint context"); |
364 | return; |
365 | } |
366 | |
367 | clip_region = cdk_cairo_region_from_clip (cr); |
368 | |
369 | cdk_gl_context_make_current (paint_context); |
370 | paint_data = cdk_gl_context_get_paint_data (paint_context); |
371 | |
372 | if (paint_data->tmp_framebuffer == 0) |
373 | glGenFramebuffersEXTepoxy_glGenFramebuffersEXT (1, &paint_data->tmp_framebuffer); |
374 | |
375 | if (source_type == GL_RENDERBUFFER0x8D41) |
376 | { |
377 | glBindRenderbufferepoxy_glBindRenderbuffer (GL_RENDERBUFFER0x8D41, source); |
378 | glGetRenderbufferParameterivepoxy_glGetRenderbufferParameteriv (GL_RENDERBUFFER0x8D41, GL_RENDERBUFFER_ALPHA_SIZE0x8D53, &alpha_size); |
379 | } |
380 | else if (source_type == GL_TEXTURE0x1702) |
381 | { |
382 | glBindTextureepoxy_glBindTexture (GL_TEXTURE_2D0x0DE1, source); |
383 | |
384 | if (cdk_gl_context_get_use_es (paint_context)) |
385 | alpha_size = 1; |
386 | else |
387 | glGetTexLevelParameterivepoxy_glGetTexLevelParameteriv (GL_TEXTURE_2D0x0DE1, 0, GL_TEXTURE_ALPHA_SIZE0x805F, &alpha_size); |
388 | } |
389 | else |
390 | { |
391 | g_warning ("Unsupported gl source type %d\n", source_type); |
392 | return; |
393 | } |
394 | |
395 | group_target = cairo_get_group_target (cr); |
396 | direct_window = cairo_surface_get_user_data (group_target, &direct_key); |
397 | |
398 | cairo_get_matrix (cr, &matrix); |
399 | |
400 | dx = matrix.x0; |
401 | dy = matrix.y0; |
402 | |
403 | /* Trivial == integer-only translation */ |
404 | trivial_transform = |
405 | (double)dx == matrix.x0 && (double)dy == matrix.y0 && |
406 | matrix.xx == 1.0 && matrix.xy == 0.0 && |
407 | matrix.yx == 0.0 && matrix.yy == 1.0; |
408 | |
409 | /* For direct paint of non-alpha renderbuffer, we can |
410 | just do a bitblit */ |
411 | if ((_cdk_gl_flags & CDK_GL_SOFTWARE_DRAW_GL) == 0 && |
412 | source_type == GL_RENDERBUFFER0x8D41 && |
413 | alpha_size == 0 && |
414 | direct_window != NULL((void*)0) && |
415 | direct_window->current_paint.use_gl && |
416 | cdk_gl_context_has_framebuffer_blit (paint_context) && |
417 | trivial_transform && |
418 | clip_region != NULL((void*)0)) |
419 | { |
420 | int unscaled_window_height; |
421 | int i; |
422 | |
423 | /* Create a framebuffer with the source renderbuffer and |
424 | make it the current target for reads */ |
425 | framebuffer = paint_data->tmp_framebuffer; |
426 | glBindFramebufferEXTepoxy_glBindFramebufferEXT (GL_FRAMEBUFFER_EXT0x8D40, framebuffer); |
427 | glFramebufferRenderbufferEXTepoxy_glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT0x8D40, GL_COLOR_ATTACHMENT0_EXT0x8CE0, |
428 | GL_RENDERBUFFER_EXT0x8D41, source); |
429 | glBindFramebufferEXTepoxy_glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT0x8CA9, 0); |
430 | |
431 | /* Translate to impl coords */ |
432 | cairo_region_translate (clip_region, dx, dy); |
433 | |
434 | glEnableepoxy_glEnable (GL_SCISSOR_TEST0x0C11); |
435 | |
436 | cdk_window_get_unscaled_size (impl_window, NULL((void*)0), &unscaled_window_height); |
437 | |
438 | /* We can use glDrawBuffer on OpenGL only; on GLES 2.0 we are already |
439 | * double buffered so we don't need it... |
440 | */ |
441 | if (!cdk_gl_context_get_use_es (paint_context)) |
442 | glDrawBufferepoxy_glDrawBuffer (GL_BACK0x0405); |
443 | else |
444 | { |
445 | int maj, min; |
446 | |
447 | cdk_gl_context_get_version (paint_context, &maj, &min); |
448 | |
449 | /* ... but on GLES 3.0 we can use the vectorized glDrawBuffers |
450 | * call. |
451 | */ |
452 | if ((maj * 100 + min) >= 300) |
453 | { |
454 | static const GLenum buffers[] = { GL_BACK0x0405 }; |
455 | |
456 | glDrawBuffersepoxy_glDrawBuffers (G_N_ELEMENTS (buffers)(sizeof (buffers) / sizeof ((buffers)[0])), buffers); |
457 | } |
458 | } |
459 | |
460 | #define FLIP_Y(_y) (unscaled_window_height - (_y)) |
461 | |
462 | for (i = 0; i < cairo_region_num_rectangles (clip_region); i++) |
463 | { |
464 | cairo_rectangle_int_t clip_rect, dest; |
465 | |
466 | cairo_region_get_rectangle (clip_region, i, &clip_rect); |
467 | clip_rect.x *= window_scale; |
468 | clip_rect.y *= window_scale; |
469 | clip_rect.width *= window_scale; |
470 | clip_rect.height *= window_scale; |
471 | |
472 | glScissorepoxy_glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height), |
473 | clip_rect.width, clip_rect.height); |
474 | |
475 | dest.x = dx * window_scale; |
476 | dest.y = dy * window_scale; |
477 | dest.width = width * window_scale / buffer_scale; |
478 | dest.height = height * window_scale / buffer_scale; |
479 | |
480 | if (cdk_rectangle_intersect (&clip_rect, &dest, &dest)) |
481 | { |
482 | int clipped_src_x = x + (dest.x - dx * window_scale); |
483 | int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale)); |
484 | glBlitFramebufferEXTepoxy_glBlitFramebufferEXT(clipped_src_x, clipped_src_y, |
485 | (clipped_src_x + dest.width), (clipped_src_y + dest.height), |
486 | dest.x, FLIP_Y(dest.y + dest.height), |
487 | dest.x + dest.width, FLIP_Y(dest.y), |
488 | GL_COLOR_BUFFER_BIT0x00004000, GL_NEAREST0x2600); |
489 | if (impl_window->current_paint.flushed_region) |
490 | { |
491 | cairo_rectangle_int_t flushed_rect; |
492 | |
493 | flushed_rect.x = dest.x / window_scale; |
494 | flushed_rect.y = dest.y / window_scale; |
495 | flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x; |
496 | flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y; |
497 | |
498 | cairo_region_union_rectangle (impl_window->current_paint.flushed_region, |
499 | &flushed_rect); |
500 | cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region, |
501 | &flushed_rect); |
502 | } |
503 | } |
504 | } |
505 | |
506 | glDisableepoxy_glDisable (GL_SCISSOR_TEST0x0C11); |
507 | |
508 | glBindFramebufferEXTepoxy_glBindFramebufferEXT (GL_FRAMEBUFFER_EXT0x8D40, 0); |
509 | |
510 | #undef FLIP_Y |
511 | |
512 | } |
513 | /* For direct paint of alpha or non-alpha textures we can use texturing */ |
514 | else if ((_cdk_gl_flags & CDK_GL_SOFTWARE_DRAW_GL) == 0 && |
515 | source_type == GL_TEXTURE0x1702 && |
516 | direct_window != NULL((void*)0) && |
517 | direct_window->current_paint.use_gl && |
518 | trivial_transform && |
519 | clip_region != NULL((void*)0)) |
520 | { |
521 | int unscaled_window_height; |
522 | GLint texture_width; |
523 | GLint texture_height; |
524 | int i, n_rects, n_quads; |
525 | CdkTexturedQuad *quads; |
526 | cairo_rectangle_int_t clip_rect; |
527 | |
528 | /* Translate to impl coords */ |
529 | cairo_region_translate (clip_region, dx, dy); |
530 | |
531 | if (alpha_size != 0) |
532 | { |
533 | cairo_region_t *opaque_region, *blend_region; |
534 | |
535 | opaque_region = cairo_region_copy (clip_region); |
536 | cairo_region_subtract (opaque_region, impl_window->current_paint.flushed_region); |
537 | cairo_region_subtract (opaque_region, impl_window->current_paint.need_blend_region); |
538 | |
539 | if (!cairo_region_is_empty (opaque_region)) |
540 | cdk_gl_texture_from_surface (impl_window->current_paint.surface, |
541 | opaque_region); |
542 | |
543 | blend_region = cairo_region_copy (clip_region); |
544 | cairo_region_intersect (blend_region, impl_window->current_paint.need_blend_region); |
545 | |
546 | glEnableepoxy_glEnable (GL_BLEND0x0BE2); |
547 | if (!cairo_region_is_empty (blend_region)) |
548 | cdk_gl_texture_from_surface (impl_window->current_paint.surface, |
549 | blend_region); |
550 | |
551 | cairo_region_destroy (opaque_region); |
552 | cairo_region_destroy (blend_region); |
553 | } |
554 | |
555 | glBindTextureepoxy_glBindTexture (GL_TEXTURE_2D0x0DE1, source); |
556 | |
557 | if (cdk_gl_context_get_use_es (paint_context)) |
558 | { |
559 | texture_width = width; |
560 | texture_height = height; |
561 | } |
562 | else |
563 | { |
564 | glGetTexLevelParameterivepoxy_glGetTexLevelParameteriv (GL_TEXTURE_2D0x0DE1, 0, GL_TEXTURE_WIDTH0x1000, &texture_width); |
565 | glGetTexLevelParameterivepoxy_glGetTexLevelParameteriv (GL_TEXTURE_2D0x0DE1, 0, GL_TEXTURE_HEIGHT0x1001, &texture_height); |
566 | } |
567 | |
568 | glTexParameteriepoxy_glTexParameteri (GL_TEXTURE_2D0x0DE1, GL_TEXTURE_WRAP_S0x2802, GL_REPEAT0x2901); |
569 | glTexParameteriepoxy_glTexParameteri (GL_TEXTURE_2D0x0DE1, GL_TEXTURE_WRAP_T0x2803, GL_REPEAT0x2901); |
570 | glTexParameteriepoxy_glTexParameteri (GL_TEXTURE_2D0x0DE1, GL_TEXTURE_MIN_FILTER0x2801, GL_NEAREST0x2600); |
571 | glTexParameteriepoxy_glTexParameteri (GL_TEXTURE_2D0x0DE1, GL_TEXTURE_MAG_FILTER0x2800, GL_NEAREST0x2600); |
572 | |
573 | glEnableepoxy_glEnable (GL_SCISSOR_TEST0x0C11); |
574 | |
575 | cdk_window_get_unscaled_size (impl_window, NULL((void*)0), &unscaled_window_height); |
576 | |
577 | #define FLIP_Y(_y) (unscaled_window_height - (_y)) |
578 | |
579 | cairo_region_get_extents (clip_region, &clip_rect); |
580 | |
581 | glScissorepoxy_glScissor (clip_rect.x * window_scale, FLIP_Y ((clip_rect.y + clip_rect.height) * window_scale), |
582 | clip_rect.width * window_scale, clip_rect.height * window_scale); |
583 | |
584 | n_quads = 0; |
585 | n_rects = cairo_region_num_rectangles (clip_region); |
586 | quads = g_new (CdkTexturedQuad, n_rects)((CdkTexturedQuad *) g_malloc_n ((n_rects), sizeof (CdkTexturedQuad ))); |
587 | for (i = 0; i < n_rects; i++) |
588 | { |
589 | cairo_rectangle_int_t dest; |
590 | |
591 | cairo_region_get_rectangle (clip_region, i, &clip_rect); |
592 | |
593 | clip_rect.x *= window_scale; |
594 | clip_rect.y *= window_scale; |
595 | clip_rect.width *= window_scale; |
596 | clip_rect.height *= window_scale; |
597 | |
598 | dest.x = dx * window_scale; |
599 | dest.y = dy * window_scale; |
600 | dest.width = width * window_scale / buffer_scale; |
601 | dest.height = height * window_scale / buffer_scale; |
602 | |
603 | if (cdk_rectangle_intersect (&clip_rect, &dest, &dest)) |
604 | { |
605 | int clipped_src_x = x + (dest.x - dx * window_scale); |
606 | int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale)); |
607 | CdkTexturedQuad quad = { |
608 | dest.x, FLIP_Y(dest.y), |
609 | dest.x + dest.width, FLIP_Y(dest.y + dest.height), |
610 | clipped_src_x / (float)texture_width, (clipped_src_y + dest.height) / (float)texture_height, |
611 | (clipped_src_x + dest.width) / (float)texture_width, clipped_src_y / (float)texture_height, |
612 | }; |
613 | |
614 | quads[n_quads++] = quad; |
615 | |
616 | if (impl_window->current_paint.flushed_region) |
617 | { |
618 | cairo_rectangle_int_t flushed_rect; |
619 | |
620 | flushed_rect.x = dest.x / window_scale; |
621 | flushed_rect.y = dest.y / window_scale; |
622 | flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x; |
623 | flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y; |
624 | |
625 | cairo_region_union_rectangle (impl_window->current_paint.flushed_region, |
626 | &flushed_rect); |
627 | cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region, |
628 | &flushed_rect); |
629 | } |
630 | } |
631 | } |
632 | |
633 | if (n_quads > 0) |
634 | cdk_gl_texture_quads (paint_context, GL_TEXTURE_2D0x0DE1, n_quads, quads, FALSE(0)); |
635 | |
636 | g_free (quads); |
637 | |
638 | if (alpha_size != 0) |
639 | glDisableepoxy_glDisable (GL_BLEND0x0BE2); |
640 | |
641 | #undef FLIP_Y |
642 | |
643 | } |
644 | else |
645 | { |
646 | /* Software fallback */ |
647 | int major, minor, version; |
648 | gboolean es_read_bgra = FALSE(0); |
649 | |
650 | #ifdef CDK_WINDOWING_WIN32 |
651 | /* on ANGLE GLES, we need to set the glReadPixel() format as GL_BGRA instead */ |
652 | if (CDK_WIN32_IS_GL_CONTEXT(paint_context)) |
653 | es_read_bgra = TRUE(!(0)); |
654 | #endif |
655 | |
656 | cdk_gl_context_get_version (paint_context, &major, &minor); |
657 | version = major * 100 + minor; |
658 | |
659 | /* TODO: Use glTexSubImage2D() and do a row-by-row copy to replace |
660 | * the GL_UNPACK_ROW_LENGTH support |
661 | */ |
662 | if (cdk_gl_context_get_use_es (paint_context) && |
663 | !(version >= 300 || cdk_gl_context_has_unpack_subimage (paint_context))) |
664 | goto out; |
665 | |
666 | /* TODO: avoid reading back non-required data due to dest clip */ |
667 | image = cairo_surface_create_similar_image (cairo_get_target (cr), |
668 | (alpha_size == 0) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32, |
669 | width, height); |
670 | |
671 | cairo_surface_set_device_scale (image, buffer_scale, buffer_scale); |
672 | |
673 | framebuffer = paint_data->tmp_framebuffer; |
674 | glBindFramebufferEXTepoxy_glBindFramebufferEXT (GL_FRAMEBUFFER_EXT0x8D40, framebuffer); |
675 | |
676 | if (source_type == GL_RENDERBUFFER0x8D41) |
677 | { |
678 | /* Create a framebuffer with the source renderbuffer and |
679 | make it the current target for reads */ |
680 | glFramebufferRenderbufferEXTepoxy_glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT0x8D40, GL_COLOR_ATTACHMENT0_EXT0x8CE0, |
681 | GL_RENDERBUFFER_EXT0x8D41, source); |
682 | } |
683 | else |
684 | { |
685 | glFramebufferTexture2DEXTepoxy_glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT0x8D40, GL_COLOR_ATTACHMENT0_EXT0x8CE0, |
686 | GL_TEXTURE_2D0x0DE1, source, 0); |
687 | } |
688 | |
689 | glPixelStoreiepoxy_glPixelStorei (GL_PACK_ALIGNMENT0x0D05, 4); |
690 | glPixelStoreiepoxy_glPixelStorei (GL_PACK_ROW_LENGTH0x0D02, cairo_image_surface_get_stride (image) / 4); |
691 | |
692 | /* The implicit format conversion is going to make this path slower */ |
693 | if (!cdk_gl_context_get_use_es (paint_context)) |
694 | glReadPixelsepoxy_glReadPixels (x, y, width, height, GL_BGRA0x80E1, GL_UNSIGNED_INT_8_8_8_8_REV0x8367, |
695 | cairo_image_surface_get_data (image)); |
696 | else |
697 | glReadPixelsepoxy_glReadPixels (x, y, width, height, es_read_bgra ? GL_BGRA0x80E1 : GL_RGBA0x1908, GL_UNSIGNED_BYTE0x1401, |
This statement is never executed | |
698 | cairo_image_surface_get_data (image)); |
699 | |
700 | glPixelStoreiepoxy_glPixelStorei (GL_PACK_ROW_LENGTH0x0D02, 0); |
701 | |
702 | glBindFramebufferEXTepoxy_glBindFramebufferEXT (GL_FRAMEBUFFER_EXT0x8D40, 0); |
703 | |
704 | cairo_surface_mark_dirty (image); |
705 | |
706 | /* Invert due to opengl having different origin */ |
707 | cairo_scale (cr, 1, -1); |
708 | cairo_translate (cr, 0, -height / buffer_scale); |
709 | |
710 | cairo_set_source_surface (cr, image, 0, 0); |
711 | cairo_set_operator (cr, CAIRO_OPERATOR_OVER); |
712 | cairo_paint (cr); |
713 | |
714 | cairo_surface_destroy (image); |
715 | } |
716 | |
717 | out: |
718 | if (clip_region) |
719 | cairo_region_destroy (clip_region); |
720 | |
721 | } |
722 | |
723 | /* This is always called with the paint context current */ |
724 | void |
725 | cdk_gl_texture_from_surface (cairo_surface_t *surface, |
726 | cairo_region_t *region) |
727 | { |
728 | CdkGLContext *paint_context; |
729 | cairo_surface_t *image; |
730 | double device_x_offset, device_y_offset; |
731 | cairo_rectangle_int_t rect, e; |
732 | int n_rects, i; |
733 | CdkWindow *window; |
734 | int unscaled_window_height; |
735 | unsigned int texture_id; |
736 | int window_scale; |
737 | double sx, sy; |
738 | float umax, vmax; |
739 | gboolean use_texture_rectangle; |
740 | guint target; |
741 | paint_context = cdk_gl_context_get_current (); |
742 | if ((_cdk_gl_flags & CDK_GL_SOFTWARE_DRAW_SURFACE) == 0 && |
743 | paint_context && |
744 | CDK_GL_CONTEXT_GET_CLASS (paint_context)((((CdkGLContextClass*) (((GTypeInstance*) ((paint_context))) ->g_class))))->texture_from_surface && |
745 | CDK_GL_CONTEXT_GET_CLASS (paint_context)((((CdkGLContextClass*) (((GTypeInstance*) ((paint_context))) ->g_class))))->texture_from_surface (paint_context, surface, region)) |
746 | return; |
747 | |
748 | /* Software fallback */ |
749 | use_texture_rectangle = cdk_gl_context_use_texture_rectangle (paint_context); |
750 | |
751 | window = cdk_gl_context_get_window (paint_context); |
752 | window_scale = cdk_window_get_scale_factor (window); |
753 | cdk_window_get_unscaled_size (window, NULL((void*)0), &unscaled_window_height); |
754 | |
755 | sx = sy = 1; |
756 | cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy); |
757 | |
758 | cairo_surface_get_device_offset (surface, |
759 | &device_x_offset, &device_y_offset); |
760 | |
761 | glGenTexturesepoxy_glGenTextures (1, &texture_id); |
762 | if (use_texture_rectangle) |
763 | target = GL_TEXTURE_RECTANGLE_ARB0x84F5; |
764 | else |
765 | target = GL_TEXTURE_2D0x0DE1; |
766 | |
767 | glBindTextureepoxy_glBindTexture (target, texture_id); |
768 | glEnableepoxy_glEnable (GL_SCISSOR_TEST0x0C11); |
769 | |
770 | glTexParameteriepoxy_glTexParameteri (target, GL_TEXTURE_WRAP_S0x2802, GL_CLAMP_TO_EDGE0x812F); |
771 | glTexParameteriepoxy_glTexParameteri (target, GL_TEXTURE_WRAP_T0x2803, GL_CLAMP_TO_EDGE0x812F); |
772 | glTexParameteriepoxy_glTexParameteri (target, GL_TEXTURE_MIN_FILTER0x2801, GL_NEAREST0x2600); |
773 | glTexParameteriepoxy_glTexParameteri (target, GL_TEXTURE_MAG_FILTER0x2800, GL_NEAREST0x2600); |
774 | |
775 | n_rects = cairo_region_num_rectangles (region); |
776 | |
777 | #define FLIP_Y(_y) (unscaled_window_height - (_y)) |
778 | |
779 | for (i = 0; i < n_rects; i++) |
780 | { |
781 | cairo_region_get_rectangle (region, i, &rect); |
782 | |
783 | glScissorepoxy_glScissor (rect.x * window_scale, FLIP_Y ((rect.y + rect.height) * window_scale), |
784 | rect.width * window_scale, rect.height * window_scale); |
785 | |
786 | e = rect; |
787 | e.x *= sx; |
788 | e.y *= sy; |
789 | e.x += (int)device_x_offset; |
790 | e.y += (int)device_y_offset; |
791 | e.width *= sx; |
792 | e.height *= sy; |
793 | image = cairo_surface_map_to_image (surface, &e); |
794 | |
795 | cdk_gl_context_upload_texture (paint_context, image, e.width, e.height, target); |
796 | |
797 | cairo_surface_unmap_image (surface, image); |
798 | |
799 | if (use_texture_rectangle) |
800 | { |
801 | umax = rect.width * sx; |
802 | vmax = rect.height * sy; |
803 | } |
804 | else |
805 | { |
806 | umax = 1.0; |
807 | vmax = 1.0; |
808 | } |
809 | |
810 | { |
811 | CdkTexturedQuad quad = { |
812 | rect.x * window_scale, FLIP_Y(rect.y * window_scale), |
813 | (rect.x + rect.width) * window_scale, FLIP_Y((rect.y + rect.height) * window_scale), |
814 | 0, 0, |
815 | umax, vmax, |
816 | }; |
817 | |
818 | /* We don't want to combine the quads here, because they have different textures. |
819 | * And we don't want to upload the unused source areas to make it one texture. */ |
820 | cdk_gl_texture_quads (paint_context, target, 1, &quad, TRUE(!(0))); |
821 | } |
822 | } |
823 | |
824 | #undef FLIP_Y |
825 | |
826 | glDisableepoxy_glDisable (GL_SCISSOR_TEST0x0C11); |
827 | glDeleteTexturesepoxy_glDeleteTextures (1, &texture_id); |
828 | } |