1/* gskglglyphlibrary.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
27#include "gskglcommandqueueprivate.h"
28#include "gskgldriverprivate.h"
29#include "gskglglyphlibraryprivate.h"
30
31#define MAX_GLYPH_SIZE 128
32
33G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
34
35GskGLGlyphLibrary *
36gsk_gl_glyph_library_new (GskGLDriver *driver)
37{
38 g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL);
39
40 return g_object_new (GSK_TYPE_GL_GLYPH_LIBRARY,
41 first_property_name: "driver", driver,
42 NULL);
43}
44
45static guint
46gsk_gl_glyph_key_hash (gconstpointer data)
47{
48 const GskGLGlyphKey *key = data;
49
50 /* We do not store the hash within the key because GHashTable will already
51 * store the hash value for us and so this is called only a single time per
52 * cached item. This saves an extra 4 bytes per GskGLGlyphKey which means on
53 * 64-bit, we fit nicely within 2 pointers (the smallest allocation size
54 * for GSlice).
55 */
56
57 return GPOINTER_TO_UINT (key->font) ^
58 key->glyph ^
59 (key->xshift << 24) ^
60 (key->yshift << 26) ^
61 key->scale;
62}
63
64static gboolean
65gsk_gl_glyph_key_equal (gconstpointer v1,
66 gconstpointer v2)
67{
68 return memcmp (s1: v1, s2: v2, n: sizeof (GskGLGlyphKey)) == 0;
69}
70
71static void
72gsk_gl_glyph_key_free (gpointer data)
73{
74 GskGLGlyphKey *key = data;
75
76 g_clear_object (&key->font);
77 g_slice_free (GskGLGlyphKey, key);
78}
79
80static void
81gsk_gl_glyph_value_free (gpointer data)
82{
83 g_slice_free (GskGLGlyphValue, data);
84}
85
86static void
87gsk_gl_glyph_library_begin_frame (GskGLTextureLibrary *library,
88 gint64 frame_id,
89 GPtrArray *removed_atlases)
90{
91 GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)library;
92
93 memset (s: self->front, c: 0, n: sizeof self->front);
94}
95
96static void
97gsk_gl_glyph_library_finalize (GObject *object)
98{
99 GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)object;
100
101 g_clear_pointer (&self->surface_data, g_free);
102
103 G_OBJECT_CLASS (gsk_gl_glyph_library_parent_class)->finalize (object);
104}
105
106static void
107gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
108{
109 GObjectClass *object_class = G_OBJECT_CLASS (klass);
110 GskGLTextureLibraryClass *library_class = GSK_GL_TEXTURE_LIBRARY_CLASS (klass);
111
112 object_class->finalize = gsk_gl_glyph_library_finalize;
113
114 library_class->begin_frame = gsk_gl_glyph_library_begin_frame;
115}
116
117static void
118gsk_gl_glyph_library_init (GskGLGlyphLibrary *self)
119{
120 GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
121
122 tl->max_entry_size = MAX_GLYPH_SIZE;
123 gsk_gl_texture_library_set_funcs (self: tl,
124 hash_func: gsk_gl_glyph_key_hash,
125 equal_func: gsk_gl_glyph_key_equal,
126 key_destroy: gsk_gl_glyph_key_free,
127 value_destroy: gsk_gl_glyph_value_free);
128}
129
130static cairo_surface_t *
131gsk_gl_glyph_library_create_surface (GskGLGlyphLibrary *self,
132 int stride,
133 int width,
134 int height,
135 int uwidth,
136 int uheight)
137{
138 cairo_surface_t *surface;
139 gsize n_bytes;
140
141 g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
142 g_assert (width > 0);
143 g_assert (height > 0);
144
145 n_bytes = stride * height;
146
147 if G_LIKELY (n_bytes > self->surface_data_len)
148 {
149 self->surface_data = g_realloc (mem: self->surface_data, n_bytes);
150 self->surface_data_len = n_bytes;
151 }
152
153 memset (s: self->surface_data, c: 0, n: n_bytes);
154 surface = cairo_image_surface_create_for_data (data: self->surface_data,
155 format: CAIRO_FORMAT_ARGB32,
156 width, height, stride);
157 cairo_surface_set_device_scale (surface, x_scale: width / (double)uwidth, y_scale: height / (double)uheight);
158
159 return surface;
160}
161
162static void
163render_glyph (cairo_surface_t *surface,
164 const GskGLGlyphKey *key,
165 const GskGLGlyphValue *value)
166{
167 cairo_t *cr;
168 PangoGlyphString glyph_string;
169 PangoGlyphInfo glyph_info;
170
171 g_assert (surface != NULL);
172
173 cr = cairo_create (target: surface);
174 cairo_set_source_rgba (cr, red: 1, green: 1, blue: 1, alpha: 1);
175
176 glyph_info.glyph = key->glyph;
177 glyph_info.geometry.width = value->ink_rect.width * 1024;
178 glyph_info.geometry.x_offset = (0.25 * key->xshift - value->ink_rect.x) * 1024;
179 glyph_info.geometry.y_offset = (0.25 * key->yshift - value->ink_rect.y) * 1024;
180
181 glyph_string.num_glyphs = 1;
182 glyph_string.glyphs = &glyph_info;
183
184 pango_cairo_show_glyph_string (cr, font: key->font, glyphs: &glyph_string);
185 cairo_destroy (cr);
186
187 cairo_surface_flush (surface);
188}
189
190static void
191gsk_gl_glyph_library_upload_glyph (GskGLGlyphLibrary *self,
192 const GskGLGlyphKey *key,
193 const GskGLGlyphValue *value,
194 int x,
195 int y,
196 int width,
197 int height,
198 int uwidth,
199 int uheight)
200{
201 GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
202 G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
203 cairo_surface_t *surface;
204 guchar *pixel_data;
205 guchar *free_data = NULL;
206 guint gl_format;
207 guint gl_type;
208 guint texture_id;
209 gsize stride;
210
211 g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
212 g_assert (key != NULL);
213 g_assert (value != NULL);
214
215 stride = cairo_format_stride_for_width (format: CAIRO_FORMAT_ARGB32, width);
216
217 gdk_gl_context_push_debug_group_printf (context: gdk_gl_context_get_current (),
218 format: "Uploading glyph %d",
219 key->glyph);
220
221 surface = gsk_gl_glyph_library_create_surface (self, stride, width, height, uwidth, uheight);
222 render_glyph (surface, key, value);
223
224 texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (d: value);
225
226 g_assert (texture_id > 0);
227
228 glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4);
229 glBindTexture (GL_TEXTURE_2D, texture_id);
230
231 if G_UNLIKELY (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
232 {
233 pixel_data = free_data = g_malloc (n_bytes: width * height * 4);
234 gdk_memory_convert (dest_data: pixel_data,
235 dest_stride: width * 4,
236 dest_format: GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
237 src_data: cairo_image_surface_get_data (surface),
238 src_stride: width * 4,
239 GDK_MEMORY_DEFAULT,
240 width, height);
241 gl_format = GL_RGBA;
242 gl_type = GL_UNSIGNED_BYTE;
243 }
244 else
245 {
246 pixel_data = cairo_image_surface_get_data (surface);
247 gl_format = GL_BGRA;
248 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
249 }
250
251 glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height,
252 gl_format, gl_type, pixel_data);
253 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
254
255 cairo_surface_destroy (surface);
256 g_free (mem: free_data);
257
258 gdk_gl_context_pop_debug_group (context: gdk_gl_context_get_current ());
259
260 tl->driver->command_queue->n_uploads++;
261
262 if (gdk_profiler_is_running ())
263 {
264 char message[64];
265 g_snprintf (string: message, n: sizeof message, format: "Size %dx%d", width, height);
266 gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Upload Glyph", message);
267 }
268}
269
270gboolean
271gsk_gl_glyph_library_add (GskGLGlyphLibrary *self,
272 GskGLGlyphKey *key,
273 const GskGLGlyphValue **out_value)
274{
275 GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
276 PangoRectangle ink_rect;
277 GskGLGlyphValue *value;
278 int width;
279 int height;
280 guint packed_x;
281 guint packed_y;
282
283 g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
284 g_assert (key != NULL);
285 g_assert (out_value != NULL);
286
287 pango_font_get_glyph_extents (font: key->font, glyph: key->glyph, ink_rect: &ink_rect, NULL);
288 pango_extents_to_pixels (inclusive: &ink_rect, NULL);
289
290 ink_rect.x -= 1;
291 ink_rect.width += 2;
292 ink_rect.y -= 1;
293 ink_rect.height += 2;
294
295 width = (int) ceil (x: ink_rect.width * key->scale / 1024.0);
296 height = (int) ceil (x: ink_rect.height * key->scale / 1024.0);
297
298 value = gsk_gl_texture_library_pack (self: tl,
299 key,
300 valuelen: sizeof *value,
301 width,
302 height,
303 padding: 1,
304 out_packed_x: &packed_x, out_packed_y: &packed_y);
305
306 memcpy (dest: &value->ink_rect, src: &ink_rect, n: sizeof ink_rect);
307
308 if (key->scale > 0 && width > 0 && height > 0)
309 gsk_gl_glyph_library_upload_glyph (self,
310 key,
311 value,
312 x: packed_x + 1,
313 y: packed_y + 1,
314 width,
315 height,
316 uwidth: ink_rect.width,
317 uheight: ink_rect.height);
318
319 *out_value = value;
320
321 return GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (d: value) != 0;
322}
323

source code of gtk/gsk/gl/gskglglyphlibrary.c