1 | /* gskgliconlibrary.c |
2 | * |
3 | * Copyright 2020 Christian Hergert <chergert@redhat.com> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * SPDX-License-Identifier: LGPL-2.1-or-later |
19 | */ |
20 | |
21 | #include "config.h" |
22 | |
23 | #include <gdk/gdkglcontextprivate.h> |
24 | #include <gdk/gdkmemoryformatprivate.h> |
25 | #include <gdk/gdkprofilerprivate.h> |
26 | #include <gdk/gdktextureprivate.h> |
27 | |
28 | #include "gskglcommandqueueprivate.h" |
29 | #include "gskgldriverprivate.h" |
30 | #include "gskgliconlibraryprivate.h" |
31 | |
32 | struct _GskGLIconLibrary |
33 | { |
34 | GskGLTextureLibrary parent_instance; |
35 | }; |
36 | |
37 | G_DEFINE_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK_TYPE_GL_TEXTURE_LIBRARY) |
38 | |
39 | GskGLIconLibrary * |
40 | gsk_gl_icon_library_new (GskGLDriver *driver) |
41 | { |
42 | g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL); |
43 | |
44 | return g_object_new (GSK_TYPE_GL_ICON_LIBRARY, |
45 | first_property_name: "driver" , driver, |
46 | NULL); |
47 | } |
48 | |
49 | static void |
50 | gsk_gl_icon_data_free (gpointer data) |
51 | { |
52 | GskGLIconData *icon_data = data; |
53 | |
54 | g_clear_object (&icon_data->source_texture); |
55 | g_slice_free (GskGLIconData, icon_data); |
56 | } |
57 | |
58 | static void |
59 | gsk_gl_icon_library_class_init (GskGLIconLibraryClass *klass) |
60 | { |
61 | } |
62 | |
63 | static void |
64 | gsk_gl_icon_library_init (GskGLIconLibrary *self) |
65 | { |
66 | GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self; |
67 | |
68 | tl->max_entry_size = 128; |
69 | gsk_gl_texture_library_set_funcs (self: tl, |
70 | NULL, NULL, NULL, |
71 | value_destroy: gsk_gl_icon_data_free); |
72 | } |
73 | |
74 | void |
75 | gsk_gl_icon_library_add (GskGLIconLibrary *self, |
76 | GdkTexture *key, |
77 | const GskGLIconData **out_value) |
78 | { |
79 | GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self; |
80 | G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME; |
81 | cairo_surface_t *surface; |
82 | GskGLIconData *icon_data; |
83 | guint8 *pixel_data; |
84 | guint8 *surface_data; |
85 | guint8 *free_data = NULL; |
86 | guint gl_format; |
87 | guint gl_type; |
88 | guint packed_x; |
89 | guint packed_y; |
90 | int width; |
91 | int height; |
92 | guint texture_id; |
93 | |
94 | g_assert (GSK_IS_GL_ICON_LIBRARY (self)); |
95 | g_assert (GDK_IS_TEXTURE (key)); |
96 | g_assert (out_value != NULL); |
97 | |
98 | width = key->width; |
99 | height = key->height; |
100 | |
101 | icon_data = gsk_gl_texture_library_pack (self: tl, |
102 | key, |
103 | valuelen: sizeof (GskGLIconData), |
104 | width, height, padding: 1, |
105 | out_packed_x: &packed_x, out_packed_y: &packed_y); |
106 | icon_data->source_texture = g_object_ref (key); |
107 | |
108 | /* actually upload the texture */ |
109 | surface = gdk_texture_download_surface (texture: key); |
110 | surface_data = cairo_image_surface_get_data (surface); |
111 | gdk_gl_context_push_debug_group_printf (context: gdk_gl_context_get_current (), |
112 | format: "Uploading texture" ); |
113 | |
114 | if (gdk_gl_context_get_use_es (context: gdk_gl_context_get_current ())) |
115 | { |
116 | pixel_data = free_data = g_malloc (n_bytes: width * height * 4); |
117 | gdk_memory_convert (dest_data: pixel_data, dest_stride: width * 4, |
118 | dest_format: GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, |
119 | src_data: surface_data, src_stride: cairo_image_surface_get_stride (surface), |
120 | GDK_MEMORY_DEFAULT, width, height); |
121 | gl_format = GL_RGBA; |
122 | gl_type = GL_UNSIGNED_BYTE; |
123 | } |
124 | else |
125 | { |
126 | pixel_data = surface_data; |
127 | gl_format = GL_BGRA; |
128 | gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; |
129 | } |
130 | |
131 | texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (d: icon_data); |
132 | |
133 | glBindTexture (GL_TEXTURE_2D, texture_id); |
134 | |
135 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
136 | packed_x + 1, packed_y + 1, |
137 | width, height, |
138 | gl_format, gl_type, |
139 | pixel_data); |
140 | /* Padding top */ |
141 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
142 | packed_x + 1, packed_y, |
143 | width, 1, |
144 | gl_format, gl_type, |
145 | pixel_data); |
146 | /* Padding left */ |
147 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
148 | packed_x, packed_y + 1, |
149 | 1, height, |
150 | gl_format, gl_type, |
151 | pixel_data); |
152 | /* Padding top left */ |
153 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
154 | packed_x, packed_y, |
155 | 1, 1, |
156 | gl_format, gl_type, |
157 | pixel_data); |
158 | |
159 | /* Padding right */ |
160 | glPixelStorei (GL_UNPACK_ROW_LENGTH, width); |
161 | glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); |
162 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
163 | packed_x + width + 1, packed_y + 1, |
164 | 1, height, |
165 | gl_format, gl_type, |
166 | pixel_data); |
167 | /* Padding top right */ |
168 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
169 | packed_x + width + 1, packed_y, |
170 | 1, 1, |
171 | gl_format, gl_type, |
172 | pixel_data); |
173 | /* Padding bottom */ |
174 | glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); |
175 | glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); |
176 | glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1); |
177 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
178 | packed_x + 1, packed_y + 1 + height, |
179 | width, 1, |
180 | gl_format, gl_type, |
181 | pixel_data); |
182 | /* Padding bottom left */ |
183 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
184 | packed_x, packed_y + 1 + height, |
185 | 1, 1, |
186 | gl_format, gl_type, |
187 | pixel_data); |
188 | /* Padding bottom right */ |
189 | glPixelStorei (GL_UNPACK_ROW_LENGTH, width); |
190 | glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); |
191 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
192 | packed_x + 1 + width, packed_y + 1 + height, |
193 | 1, 1, |
194 | gl_format, gl_type, |
195 | pixel_data); |
196 | /* Reset this */ |
197 | glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); |
198 | glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); |
199 | glPixelStorei (GL_UNPACK_SKIP_ROWS, 0); |
200 | |
201 | gdk_gl_context_pop_debug_group (context: gdk_gl_context_get_current ()); |
202 | |
203 | *out_value = icon_data; |
204 | |
205 | cairo_surface_destroy (surface); |
206 | g_free (mem: free_data); |
207 | |
208 | tl->driver->command_queue->n_uploads++; |
209 | |
210 | if (gdk_profiler_is_running ()) |
211 | { |
212 | char message[64]; |
213 | g_snprintf (string: message, n: sizeof message, format: "Size %dx%d" , width, height); |
214 | gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Upload Icon" , message); |
215 | } |
216 | } |
217 | |