1/* GDK - 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 "gdkcairo.h"
21
22#include "gdkglcontextprivate.h"
23#include "gdksurfaceprivate.h"
24
25#include <epoxy/gl.h>
26#include <math.h>
27#include <string.h>
28
29/* x,y,width,height describes a rectangle in the gl render buffer
30 coordinate space, and its top left corner is drawn at the current
31 position according to the cairo translation. */
32
33/**
34 * gdk_cairo_draw_from_gl:
35 * @cr: a cairo context
36 * @surface: The surface we're rendering for (not necessarily into)
37 * @source: The GL ID of the source buffer
38 * @source_type: The type of the @source
39 * @buffer_scale: The scale-factor that the @source buffer is allocated for
40 * @x: The source x position in @source to start copying from in GL coordinates
41 * @y: The source y position in @source to start copying from in GL coordinates
42 * @width: The width of the region to draw
43 * @height: The height of the region to draw
44 *
45 * The main way to not draw GL content in GTK.
46 *
47 * It takes a render buffer ID (@source_type == GL_RENDERBUFFER) or a texture
48 * id (@source_type == GL_TEXTURE) and draws it onto @cr with an OVER operation,
49 * respecting the current clip. The top left corner of the rectangle specified
50 * by @x, @y, @width and @height will be drawn at the current (0,0) position of
51 * the `cairo_t`.
52 *
53 * This will work for *all* `cairo_t`, as long as @surface is realized, but the
54 * fallback implementation that reads back the pixels from the buffer may be
55 * used in the general case. In the case of direct drawing to a surface with
56 * no special effects applied to @cr it will however use a more efficient
57 * approach.
58 *
59 * For GL_RENDERBUFFER the code will always fall back to software for buffers
60 * with alpha components, so make sure you use GL_TEXTURE if using alpha.
61 *
62 * Calling this may change the current GL context.
63 *
64 * Deprecated: 4.6: The function is overly complex and produces broken output
65 * in various combinations of arguments. If you want to draw with GL textures
66 * in GTK, use [ctor@Gdk.GLTexture.new]; if you want to use that texture in
67 * Cairo, use [method@Gdk.Texture.download] to download the data into a Cairo
68 * image surface.
69 */
70void
71gdk_cairo_draw_from_gl (cairo_t *cr,
72 GdkSurface *surface,
73 int source,
74 int source_type,
75 int buffer_scale,
76 int x,
77 int y,
78 int width,
79 int height)
80{
81 GdkGLContext *paint_context;
82 cairo_surface_t *image;
83 guint framebuffer;
84 int alpha_size = 0;
85 int major, minor, version;
86 gboolean es_use_bgra = FALSE;
87
88 paint_context = gdk_surface_get_paint_gl_context (surface, NULL);
89 if (paint_context == NULL)
90 {
91 g_warning ("gdk_cairo_draw_gl_render_buffer failed - no paint context");
92 return;
93 }
94
95 es_use_bgra = gdk_gl_context_use_es_bgra (context: paint_context);
96
97 gdk_gl_context_make_current (context: paint_context);
98
99 glGenFramebuffers (1, &framebuffer);
100
101 if (source_type == GL_RENDERBUFFER)
102 {
103 glBindRenderbuffer (GL_RENDERBUFFER, source);
104 glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE, &alpha_size);
105 }
106 else if (source_type == GL_TEXTURE)
107 {
108 glBindTexture (GL_TEXTURE_2D, source);
109
110 if (gdk_gl_context_get_use_es (context: paint_context))
111 alpha_size = 1;
112 else
113 glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &alpha_size);
114 }
115 else
116 {
117 g_warning ("Unsupported gl source type %d\n", source_type);
118 return;
119 }
120
121 gdk_gl_context_get_version (context: paint_context, major: &major, minor: &minor);
122 version = major * 100 + minor;
123
124 /* TODO: Use glTexSubImage2D() and do a row-by-row copy to replace
125 * the GL_UNPACK_ROW_LENGTH support
126 */
127 if (gdk_gl_context_get_use_es (context: paint_context) &&
128 !(version >= 300 || gdk_gl_context_has_unpack_subimage (context: paint_context)))
129 return;
130
131 /* TODO: avoid reading back non-required data due to dest clip */
132 image = cairo_surface_create_similar_image (other: cairo_get_target (cr),
133 format: (alpha_size == 0) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32,
134 width, height);
135
136 cairo_surface_set_device_scale (surface: image, x_scale: buffer_scale, y_scale: buffer_scale);
137
138 glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
139
140 if (source_type == GL_RENDERBUFFER)
141 {
142 /* Create a framebuffer with the source renderbuffer and
143 make it the current target for reads */
144 glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
145 GL_RENDERBUFFER, source);
146 }
147 else
148 {
149 glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
150 GL_TEXTURE_2D, source, 0);
151 }
152
153 glPixelStorei (GL_PACK_ALIGNMENT, 4);
154 glPixelStorei (GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride (surface: image) / 4);
155
156 /* The implicit format conversion is going to make this path slower */
157 if (!gdk_gl_context_get_use_es (context: paint_context))
158 glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
159 cairo_image_surface_get_data (surface: image));
160 else
161 glReadPixels (x, y, width, height, es_use_bgra ? GL_BGRA : GL_RGBA, GL_UNSIGNED_BYTE,
162 cairo_image_surface_get_data (surface: image));
163
164 glPixelStorei (GL_PACK_ROW_LENGTH, 0);
165
166 glBindFramebuffer (GL_FRAMEBUFFER, 0);
167
168 glDeleteFramebuffers (1, &framebuffer);
169
170 cairo_surface_mark_dirty (surface: image);
171
172 cairo_set_source_surface (cr, surface: image, x: 0, y: 0);
173 cairo_set_operator (cr, op: CAIRO_OPERATOR_OVER);
174 cairo_paint (cr);
175
176 cairo_surface_destroy (surface: image);
177}
178

source code of gtk/gdk/gdkgl.c