1 #include "gskgliconcacheprivate.h"
2 #include "gskgltextureatlasprivate.h"
3 #include "gdk/gdktextureprivate.h"
4 #include "gdk/gdkmemorytextureprivate.h"
5 #include "gdk/gdkglcontextprivate.h"
6
7 #include <epoxy/gl.h>
8
9 #define MAX_FRAME_AGE 60
10
11 static void
icon_data_free(gpointer p)12 icon_data_free (gpointer p)
13 {
14 g_object_unref (((IconData *)p)->source_texture);
15 g_free (p);
16 }
17
18 GskGLIconCache *
gsk_gl_icon_cache_new(GdkDisplay * display,GskGLTextureAtlases * atlases)19 gsk_gl_icon_cache_new (GdkDisplay *display,
20 GskGLTextureAtlases *atlases)
21 {
22 GskGLIconCache *self;
23
24 self = g_new0 (GskGLIconCache, 1);
25
26 self->display = display;
27 self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
28 self->atlases = gsk_gl_texture_atlases_ref (atlases);
29 self->ref_count = 1;
30
31 return self;
32 }
33
34 GskGLIconCache *
gsk_gl_icon_cache_ref(GskGLIconCache * self)35 gsk_gl_icon_cache_ref (GskGLIconCache *self)
36 {
37 self->ref_count++;
38
39 return self;
40 }
41
42 void
gsk_gl_icon_cache_unref(GskGLIconCache * self)43 gsk_gl_icon_cache_unref (GskGLIconCache *self)
44 {
45 g_assert (self->ref_count > 0);
46
47 if (self->ref_count == 1)
48 {
49 gsk_gl_texture_atlases_unref (self->atlases);
50 g_hash_table_unref (self->icons);
51 g_free (self);
52 return;
53 }
54
55 self->ref_count--;
56 }
57
58 void
gsk_gl_icon_cache_begin_frame(GskGLIconCache * self,GPtrArray * removed_atlases)59 gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
60 GPtrArray *removed_atlases)
61 {
62 GHashTableIter iter;
63 GdkTexture *texture;
64 IconData *icon_data;
65
66 self->timestamp++;
67
68 /* Drop icons on removed atlases */
69 if (removed_atlases->len > 0)
70 {
71 guint dropped = 0;
72
73 g_hash_table_iter_init (&iter, self->icons);
74 while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
75 {
76 if (g_ptr_array_find (removed_atlases, icon_data->atlas, NULL))
77 {
78 g_hash_table_iter_remove (&iter);
79 dropped++;
80 }
81 }
82
83 GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d icons", dropped));
84 }
85
86 if (self->timestamp % MAX_FRAME_AGE == 0)
87 {
88 g_hash_table_iter_init (&iter, self->icons);
89 while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
90 {
91 if (!icon_data->accessed)
92 {
93 if (icon_data->used)
94 {
95 const int width = icon_data->source_texture->width;
96 const int height = icon_data->source_texture->height;
97 gsk_gl_texture_atlas_mark_unused (icon_data->atlas, width + 2, height + 2);
98 icon_data->used = FALSE;
99 }
100 }
101
102 icon_data->accessed = FALSE;
103 }
104
105 GSK_NOTE(GLYPH_CACHE, g_message ("%d icons cached", g_hash_table_size (self->icons)));
106 }
107 }
108
109 void
gsk_gl_icon_cache_lookup_or_add(GskGLIconCache * self,GdkTexture * texture,const IconData ** out_icon_data)110 gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
111 GdkTexture *texture,
112 const IconData **out_icon_data)
113 {
114 IconData *icon_data = g_hash_table_lookup (self->icons, texture);
115
116 if (icon_data)
117 {
118 if (!icon_data->used)
119 {
120 gsk_gl_texture_atlas_mark_used (icon_data->atlas, texture->width + 2, texture->height + 2);
121 icon_data->used = TRUE;
122 }
123 icon_data->accessed = TRUE;
124
125 *out_icon_data = icon_data;
126 return;
127 }
128
129 /* texture not on any atlas yet. Find a suitable one. */
130 {
131 const int width = texture->width;
132 const int height = texture->height;
133 GskGLTextureAtlas *atlas = NULL;
134 int packed_x = 0;
135 int packed_y = 0;
136 cairo_surface_t *surface;
137 unsigned char *surface_data;
138 unsigned char *pixel_data;
139 guchar *free_data = NULL;
140 guint gl_format;
141 guint gl_type;
142
143 gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
144
145 icon_data = g_new0 (IconData, 1);
146 icon_data->atlas = atlas;
147 icon_data->accessed = TRUE;
148 icon_data->used = TRUE;
149 icon_data->texture_id = atlas->texture_id;
150 icon_data->source_texture = g_object_ref (texture);
151 icon_data->x = (float)(packed_x + 1) / atlas->width;
152 icon_data->y = (float)(packed_y + 1) / atlas->width;
153 icon_data->x2 = icon_data->x + (float)width / atlas->width;
154 icon_data->y2 = icon_data->y + (float)height / atlas->height;
155
156 g_hash_table_insert (self->icons, texture, icon_data);
157
158 /* actually upload the texture */
159 surface = gdk_texture_download_surface (texture);
160 surface_data = cairo_image_surface_get_data (surface);
161 gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
162 "Uploading texture");
163
164 if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
165 {
166 pixel_data = free_data = g_malloc (width * height * 4);
167 gdk_memory_convert (pixel_data, width * 4,
168 GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
169 surface_data, cairo_image_surface_get_stride (surface),
170 GDK_MEMORY_DEFAULT, width, height);
171 gl_format = GL_RGBA;
172 gl_type = GL_UNSIGNED_BYTE;
173 }
174 else
175 {
176 pixel_data = surface_data;
177 gl_format = GL_BGRA;
178 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
179 }
180
181 glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
182
183 glTexSubImage2D (GL_TEXTURE_2D, 0,
184 packed_x + 1, packed_y + 1,
185 width, height,
186 gl_format, gl_type,
187 pixel_data);
188 /* Padding top */
189 glTexSubImage2D (GL_TEXTURE_2D, 0,
190 packed_x + 1, packed_y,
191 width, 1,
192 gl_format, gl_type,
193 pixel_data);
194 /* Padding left */
195 glTexSubImage2D (GL_TEXTURE_2D, 0,
196 packed_x, packed_y + 1,
197 1, height,
198 gl_format, gl_type,
199 pixel_data);
200 /* Padding top left */
201 glTexSubImage2D (GL_TEXTURE_2D, 0,
202 packed_x, packed_y,
203 1, 1,
204 gl_format, gl_type,
205 pixel_data);
206
207 /* Padding right */
208 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
209 glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
210 glTexSubImage2D (GL_TEXTURE_2D, 0,
211 packed_x + width + 1, packed_y + 1,
212 1, height,
213 gl_format, gl_type,
214 pixel_data);
215 /* Padding top right */
216 glTexSubImage2D (GL_TEXTURE_2D, 0,
217 packed_x + width + 1, packed_y,
218 1, 1,
219 gl_format, gl_type,
220 pixel_data);
221 /* Padding bottom */
222 glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
223 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
224 glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1);
225 glTexSubImage2D (GL_TEXTURE_2D, 0,
226 packed_x + 1, packed_y + 1 + height,
227 width, 1,
228 gl_format, gl_type,
229 pixel_data);
230 /* Padding bottom left */
231 glTexSubImage2D (GL_TEXTURE_2D, 0,
232 packed_x, packed_y + 1 + height,
233 1, 1,
234 gl_format, gl_type,
235 pixel_data);
236 /* Padding bottom right */
237 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
238 glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
239 glTexSubImage2D (GL_TEXTURE_2D, 0,
240 packed_x + 1 + width, packed_y + 1 + height,
241 1, 1,
242 gl_format, gl_type,
243 pixel_data);
244 /* Reset this */
245 glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
246 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
247 glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
248
249 gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
250
251 *out_icon_data = icon_data;
252
253 cairo_surface_destroy (surface);
254 g_free (free_data);
255
256 #if 0
257 {
258 static int k;
259 const int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
260 guchar *data = g_malloc (atlas->height * stride);
261 cairo_surface_t *s;
262 char *filename = g_strdup_printf ("atlas_%u_%d.png", atlas->texture_id, k++);
263
264 glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
265 glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
266 s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride);
267 cairo_surface_write_to_png (s, filename);
268
269 cairo_surface_destroy (s);
270 g_free (data);
271 g_free (filename);
272 }
273 #endif
274 }
275 }
276