1/* Pango/Font Rendering
2 *
3 * Demonstrates various aspects of font rendering,
4 * such as hinting, antialiasing and grid alignment.
5 *
6 * The demo lets you explore font rendering options
7 * interactively to get a feeling for they affect the
8 * shape and positioning of the glyphs.
9 */
10
11#include <gtk/gtk.h>
12
13static GtkWidget *window = NULL;
14static GtkWidget *font_button = NULL;
15static GtkWidget *entry = NULL;
16static GtkWidget *image = NULL;
17static GtkWidget *hinting = NULL;
18static GtkWidget *anti_alias = NULL;
19static GtkWidget *hint_metrics = NULL;
20static GtkWidget *up_button = NULL;
21static GtkWidget *down_button = NULL;
22static GtkWidget *text_radio = NULL;
23static GtkWidget *show_grid = NULL;
24static GtkWidget *show_extents = NULL;
25static GtkWidget *show_pixels = NULL;
26static GtkWidget *show_outlines = NULL;
27
28static PangoContext *context;
29
30static int scale = 7;
31static double pixel_alpha = 1.0;
32static double outline_alpha = 0.0;
33
34static void
35update_image (void)
36{
37 const char *text;
38 PangoFontDescription *desc;
39 PangoLayout *layout;
40 PangoRectangle ink, logical;
41 int baseline;
42 cairo_surface_t *surface;
43 cairo_t *cr;
44 GdkPixbuf *pixbuf;
45 GdkPixbuf *pixbuf2;
46 const char *hint;
47 cairo_font_options_t *fopt;
48 cairo_hint_style_t hintstyle;
49 cairo_hint_metrics_t hintmetrics;
50 cairo_antialias_t antialias;
51 cairo_path_t *path;
52
53 if (!context)
54 context = gtk_widget_create_pango_context (widget: image);
55
56 text = gtk_editable_get_text (GTK_EDITABLE (entry));
57 desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (font_button));
58
59 fopt = cairo_font_options_copy (original: pango_cairo_context_get_font_options (context));
60
61 hint = gtk_combo_box_get_active_id (GTK_COMBO_BOX (hinting));
62 hintstyle = CAIRO_HINT_STYLE_DEFAULT;
63 if (hint)
64 {
65 if (strcmp (s1: hint, s2: "none") == 0)
66 hintstyle = CAIRO_HINT_STYLE_NONE;
67 else if (strcmp (s1: hint, s2: "slight") == 0)
68 hintstyle = CAIRO_HINT_STYLE_SLIGHT;
69 else if (strcmp (s1: hint, s2: "medium") == 0)
70 hintstyle = CAIRO_HINT_STYLE_MEDIUM;
71 else if (strcmp (s1: hint, s2: "full") == 0)
72 hintstyle = CAIRO_HINT_STYLE_FULL;
73 }
74 cairo_font_options_set_hint_style (options: fopt, hint_style: hintstyle);
75
76 if (gtk_check_button_get_active (GTK_CHECK_BUTTON (hint_metrics)))
77 hintmetrics = CAIRO_HINT_METRICS_ON;
78 else
79 hintmetrics = CAIRO_HINT_METRICS_OFF;
80 cairo_font_options_set_hint_metrics (options: fopt, hint_metrics: hintmetrics);
81
82 if (gtk_check_button_get_active (GTK_CHECK_BUTTON (anti_alias)))
83 antialias = CAIRO_ANTIALIAS_GRAY;
84 else
85 antialias = CAIRO_ANTIALIAS_NONE;
86 cairo_font_options_set_antialias (options: fopt, antialias);
87
88 pango_context_set_round_glyph_positions (context, round_positions: hintmetrics == CAIRO_HINT_METRICS_ON);
89 pango_cairo_context_set_font_options (context, options: fopt);
90 cairo_font_options_destroy (options: fopt);
91 pango_context_changed (context);
92
93 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (text_radio)))
94 {
95 layout = pango_layout_new (context);
96 pango_layout_set_font_description (layout, desc);
97 pango_layout_set_text (layout, text, length: -1);
98 pango_layout_get_extents (layout, ink_rect: &ink, logical_rect: &logical);
99 baseline = pango_layout_get_baseline (layout);
100
101 pango_extents_to_pixels (inclusive: &ink, NULL);
102
103 surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, width: ink.width + 20, height: ink.height + 20);
104 cr = cairo_create (target: surface);
105 cairo_set_source_rgb (cr, red: 1, green: 1, blue: 1);
106 cairo_paint (cr);
107
108 cairo_set_source_rgba (cr, red: 0, green: 0, blue: 0, alpha: pixel_alpha);
109
110 cairo_move_to (cr, x: 10, y: 10);
111 pango_cairo_show_layout (cr, layout);
112
113 pango_cairo_layout_path (cr, layout);
114 path = cairo_copy_path (cr);
115
116 cairo_destroy (cr);
117 g_object_unref (object: layout);
118
119 pixbuf = gdk_pixbuf_get_from_surface (surface, src_x: 0, src_y: 0, width: cairo_image_surface_get_width (surface), height: cairo_image_surface_get_height (surface));
120 pixbuf2 = gdk_pixbuf_scale_simple (src: pixbuf, dest_width: gdk_pixbuf_get_width (pixbuf) * scale, dest_height: gdk_pixbuf_get_height (pixbuf) * scale, interp_type: GDK_INTERP_NEAREST);
121
122 g_object_unref (object: pixbuf);
123 cairo_surface_destroy (surface);
124
125 surface = cairo_image_surface_create_for_data (data: gdk_pixbuf_get_pixels (pixbuf: pixbuf2),
126 format: CAIRO_FORMAT_ARGB32,
127 width: gdk_pixbuf_get_width (pixbuf: pixbuf2),
128 height: gdk_pixbuf_get_height (pixbuf: pixbuf2),
129 stride: gdk_pixbuf_get_rowstride (pixbuf: pixbuf2));
130
131 cr = cairo_create (target: surface);
132 cairo_set_line_width (cr, width: 1);
133
134 if (gtk_check_button_get_active (GTK_CHECK_BUTTON (show_grid)))
135 {
136 int i;
137 cairo_set_source_rgba (cr, red: 0.2, green: 0, blue: 0, alpha: 0.2);
138 for (i = 1; i < ink.height + 20; i++)
139 {
140 cairo_move_to (cr, x: 0, y: scale * i - 0.5);
141 cairo_line_to (cr, x: scale * (ink.width + 20), y: scale * i - 0.5);
142 cairo_stroke (cr);
143 }
144 for (i = 1; i < ink.width + 20; i++)
145 {
146 cairo_move_to (cr, x: scale * i - 0.5, y: 0);
147 cairo_line_to (cr, x: scale * i - 0.5, y: scale * (ink.height + 20));
148 cairo_stroke (cr);
149 }
150 }
151
152 if (gtk_check_button_get_active (GTK_CHECK_BUTTON (show_extents)))
153 {
154 cairo_set_source_rgb (cr, red: 0, green: 0, blue: 1);
155
156 cairo_rectangle (cr,
157 x: scale * (10 + pango_units_to_double (i: logical.x)) - 0.5,
158 y: scale * (10 + pango_units_to_double (i: logical.y)) - 0.5,
159 width: scale * pango_units_to_double (i: logical.width) + 1,
160 height: scale * pango_units_to_double (i: logical.height) + 1);
161 cairo_stroke (cr);
162 cairo_move_to (cr, x: scale * (10 + pango_units_to_double (i: logical.x)) - 0.5,
163 y: scale * (10 + pango_units_to_double (i: baseline)) - 0.5);
164 cairo_line_to (cr, x: scale * (10 + pango_units_to_double (i: logical.x + logical.width)) + 1,
165 y: scale * (10 + pango_units_to_double (i: baseline)) - 0.5);
166 cairo_stroke (cr);
167 cairo_set_source_rgb (cr, red: 1, green: 0, blue: 0);
168 cairo_rectangle (cr,
169 x: scale * (10 + ink.x) - 0.5,
170 y: scale * (10 + ink.y) - 0.5,
171 width: scale * ink.width + 1,
172 height: scale * ink.height + 1);
173 cairo_stroke (cr);
174 }
175
176 for (int i = 0; i < path->num_data; i += path->data[i].header.length)
177 {
178 cairo_path_data_t *data = &path->data[i];
179 switch (data->header.type)
180 {
181 case CAIRO_PATH_CURVE_TO:
182 data[3].point.x *= scale; data[3].point.y *= scale;
183 data[2].point.x *= scale; data[2].point.y *= scale;
184 data[1].point.x *= scale; data[1].point.y *= scale;
185 break;
186 case CAIRO_PATH_LINE_TO:
187 case CAIRO_PATH_MOVE_TO:
188 data[1].point.x *= scale; data[1].point.y *= scale;
189 break;
190 case CAIRO_PATH_CLOSE_PATH:
191 break;
192 default:
193 g_assert_not_reached ();
194 }
195 }
196
197 cairo_set_source_rgba (cr, red: 0, green: 0, blue: 0, alpha: outline_alpha);
198 cairo_move_to (cr, x: scale * 20 - 0.5, y: scale * 20 - 0.5);
199 cairo_append_path (cr, path);
200 cairo_stroke (cr);
201
202 cairo_surface_destroy (surface);
203 cairo_destroy (cr);
204
205 cairo_path_destroy (path);
206 }
207 else
208 {
209 PangoLayoutIter *iter;
210 PangoLayoutRun *run;
211 PangoGlyphInfo *g;
212 int i, j;
213 GString *str;
214 gunichar ch;
215
216 if (*text == '\0')
217 text = " ";
218
219 ch = g_utf8_get_char (p: text);
220
221 str = g_string_new (init: "");
222
223 for (i = 0; i < 4; i++)
224 {
225 g_string_append_unichar (string: str, wc: ch);
226 g_string_append_unichar (string: str, wc: 0x200c);
227 }
228
229 layout = pango_layout_new (context);
230 pango_layout_set_font_description (layout, desc);
231 pango_layout_set_text (layout, text: str->str, length: -1);
232 g_string_free (string: str, TRUE);
233 pango_layout_get_extents (layout, ink_rect: &ink, logical_rect: &logical);
234 pango_extents_to_pixels (inclusive: &logical, NULL);
235
236 surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, width: logical.width * 3 / 2, height: 4*logical.height);
237 cr = cairo_create (target: surface);
238 cairo_set_source_rgb (cr, red: 1, green: 1, blue: 1);
239 cairo_paint (cr);
240
241 iter = pango_layout_get_iter (layout);
242 run = pango_layout_iter_get_run (iter);
243
244 cairo_set_source_rgb (cr, red: 0, green: 0, blue: 0);
245 for (i = 0; i < 4; i++)
246 {
247 g = &(run->glyphs->glyphs[2*i]);
248 g->geometry.width = PANGO_UNITS_ROUND (g->geometry.width * 3 / 2);
249 }
250
251 for (j = 0; j < 4; j++)
252 {
253 for (i = 0; i < 4; i++)
254 {
255 g = &(run->glyphs->glyphs[2*i]);
256 g->geometry.x_offset = i * (PANGO_SCALE / 4);
257 g->geometry.y_offset = j * (PANGO_SCALE / 4);
258 }
259
260 cairo_move_to (cr, x: 0, y: j * logical.height);
261 pango_cairo_show_layout (cr, layout);
262 }
263
264 cairo_destroy (cr);
265 pango_layout_iter_free (iter);
266 g_object_unref (object: layout);
267
268 pixbuf = gdk_pixbuf_get_from_surface (surface, src_x: 0, src_y: 0, width: cairo_image_surface_get_width (surface), height: cairo_image_surface_get_height (surface));
269 pixbuf2 = gdk_pixbuf_scale_simple (src: pixbuf, dest_width: gdk_pixbuf_get_width (pixbuf) * scale, dest_height: gdk_pixbuf_get_height (pixbuf) * scale, interp_type: GDK_INTERP_NEAREST);
270 g_object_unref (object: pixbuf);
271 cairo_surface_destroy (surface);
272 }
273
274 gtk_picture_set_pixbuf (self: GTK_PICTURE (ptr: image), pixbuf: pixbuf2);
275
276 g_object_unref (object: pixbuf2);
277
278 pango_font_description_free (desc);
279}
280
281static gboolean fading = FALSE;
282static double start_pixel_alpha;
283static double end_pixel_alpha;
284static double start_outline_alpha;
285static double end_outline_alpha;
286static gint64 start_time;
287static gint64 end_time;
288
289static double
290ease_out_cubic (double t)
291{
292 double p = t - 1;
293 return p * p * p + 1;
294}
295
296static gboolean
297change_alpha (GtkWidget *widget,
298 GdkFrameClock *clock,
299 gpointer user_data)
300{
301 gint64 now = g_get_monotonic_time ();
302 double t;
303
304 t = ease_out_cubic (t: (now - start_time) / (double) (end_time - start_time));
305
306 pixel_alpha = start_pixel_alpha + (end_pixel_alpha - start_pixel_alpha) * t;
307 outline_alpha = start_outline_alpha + (end_outline_alpha - start_outline_alpha) * t;
308
309 update_image ();
310
311 if (now >= end_time)
312 {
313 fading = FALSE;
314 return G_SOURCE_REMOVE;
315 }
316
317 return G_SOURCE_CONTINUE;
318}
319
320static void
321start_alpha_fade (void)
322{
323 gboolean pixels;
324 gboolean outlines;
325
326 if (fading)
327 return;
328
329 pixels = gtk_check_button_get_active (GTK_CHECK_BUTTON (show_pixels));
330 outlines = gtk_check_button_get_active (GTK_CHECK_BUTTON (show_outlines));
331
332 start_pixel_alpha = pixel_alpha;
333 if (pixels && outlines)
334 end_pixel_alpha = 0.5;
335 else if (pixels)
336 end_pixel_alpha = 1;
337 else
338 end_pixel_alpha = 0;
339
340 start_outline_alpha = outline_alpha;
341 if (outlines)
342 end_outline_alpha = 1.0;
343 else
344 end_outline_alpha = 0.0;
345
346 start_time = g_get_monotonic_time ();
347 end_time = start_time + G_TIME_SPAN_SECOND / 2;
348
349 fading = TRUE;
350 gtk_widget_add_tick_callback (widget: window, callback: change_alpha, NULL, NULL);
351}
352
353static void
354update_buttons (void)
355{
356 gtk_widget_set_sensitive (widget: up_button, sensitive: scale < 32);
357 gtk_widget_set_sensitive (widget: down_button, sensitive: scale > 1);
358}
359
360static gboolean
361scale_up (GtkWidget *widget,
362 GVariant *args,
363 gpointer user_data)
364{
365 scale += 1;
366 update_buttons ();
367 update_image ();
368 return TRUE;
369}
370
371static gboolean
372scale_down (GtkWidget *widget,
373 GVariant *args,
374 gpointer user_data)
375{
376 scale -= 1;
377 update_buttons ();
378 update_image ();
379 return TRUE;
380}
381
382GtkWidget *
383do_fontrendering (GtkWidget *do_widget)
384{
385 if (!window)
386 {
387 GtkBuilder *builder;
388
389 builder = gtk_builder_new_from_resource (resource_path: "/fontrendering/fontrendering.ui");
390 window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
391 gtk_window_set_display (GTK_WINDOW (window),
392 display: gtk_widget_get_display (widget: do_widget));
393 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window);
394 font_button = GTK_WIDGET (gtk_builder_get_object (builder, "font_button"));
395 up_button = GTK_WIDGET (gtk_builder_get_object (builder, "up_button"));
396 down_button = GTK_WIDGET (gtk_builder_get_object (builder, "down_button"));
397 entry = GTK_WIDGET (gtk_builder_get_object (builder, "entry"));
398 image = GTK_WIDGET (gtk_builder_get_object (builder, "image"));
399 hinting = GTK_WIDGET (gtk_builder_get_object (builder, "hinting"));
400 anti_alias = GTK_WIDGET (gtk_builder_get_object (builder, "antialias"));
401 hint_metrics = GTK_WIDGET (gtk_builder_get_object (builder, "hint_metrics"));
402 text_radio = GTK_WIDGET (gtk_builder_get_object (builder, "text_radio"));
403 show_grid = GTK_WIDGET (gtk_builder_get_object (builder, "show_grid"));
404 show_extents = GTK_WIDGET (gtk_builder_get_object (builder, "show_extents"));
405 show_pixels = GTK_WIDGET (gtk_builder_get_object (builder, "show_pixels"));
406 show_outlines = GTK_WIDGET (gtk_builder_get_object (builder, "show_outlines"));
407
408 g_signal_connect (up_button, "clicked", G_CALLBACK (scale_up), NULL);
409 g_signal_connect (down_button, "clicked", G_CALLBACK (scale_down), NULL);
410 g_signal_connect (entry, "notify::text", G_CALLBACK (update_image), NULL);
411 g_signal_connect (font_button, "notify::font-desc", G_CALLBACK (update_image), NULL);
412 g_signal_connect (hinting, "notify::active", G_CALLBACK (update_image), NULL);
413 g_signal_connect (anti_alias, "notify::active", G_CALLBACK (update_image), NULL);
414 g_signal_connect (hint_metrics, "notify::active", G_CALLBACK (update_image), NULL);
415 g_signal_connect (text_radio, "notify::active", G_CALLBACK (update_image), NULL);
416 g_signal_connect (show_grid, "notify::active", G_CALLBACK (update_image), NULL);
417 g_signal_connect (show_extents, "notify::active", G_CALLBACK (update_image), NULL);
418 g_signal_connect (show_pixels, "notify::active", G_CALLBACK (start_alpha_fade), NULL);
419 g_signal_connect (show_outlines, "notify::active", G_CALLBACK (start_alpha_fade), NULL);
420
421 update_image ();
422
423 g_object_unref (object: builder);
424 }
425
426 if (!gtk_widget_get_visible (widget: window))
427 gtk_widget_show (widget: window);
428 else
429 gtk_window_destroy (GTK_WINDOW (window));
430
431 return window;
432}
433

source code of gtk/demos/gtk-demo/fontrendering.c