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 | */ |
70 | void |
71 | gdk_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 | |