1 /* -*- coding: utf-8 -*- */
2 /*
3 * font-pangoft2-tex.c:
4 * Simple example for text texture rendering with PangoFT2.
5 *
6 * written by Naofumi Yasufuku <naofumi@users.sourceforge.net>
7 */
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <math.h>
12
13 #include <gtk/gtk.h>
14 #include <pango/pangoft2.h>
15
16 #include <gtk/gtkgl.h>
17
18 #ifdef G_OS_WIN32
19 #define WIN32_LEAN_AND_MEAN 1
20 #include <windows.h>
21 #endif
22
23 #include <GL/gl.h>
24 #include <GL/glu.h>
25
26 #define TIMEOUT_INTERVAL 10
27
28 #define FOVY_2 20.0
29 #define Z_NEAR 3.0
30
31 static const char *text = "This text is rendered with Παν語FT2.";
32
33 static PangoContext *ft2_context = NULL;
34
35 typedef struct _TextTexture {
36 GLuint name;
37 GLsizei size;
38 GLvoid *texels;
39 } TextTexture;
40
41 static TextTexture text_texture = {
42 0, 0, NULL
43 };
44
45 static gboolean
gl_tex_create_texture(GLuint * texture)46 gl_tex_create_texture (GLuint *texture)
47 {
48 GLuint tex_name;
49 GLint size;
50 GLvoid *texels;
51
52 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &size);
53
54 do
55 {
56 GLint width;
57
58 glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA,
59 size, size, 0,
60 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
61
62 glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0,
63 GL_TEXTURE_WIDTH, &width);
64 if (width != 0)
65 break;
66
67 size >>= 1;
68 }
69 while (size > 0);
70
71 if (size == 0)
72 {
73 g_print ("There are not enough resources for the texture.\n");
74 return FALSE;
75 }
76
77 texels = g_malloc (size * size * 4);
78 memset (texels, 0, size * size * 4);
79
80 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
81
82 glGenTextures (1, &tex_name);
83
84 glBindTexture (GL_TEXTURE_2D, tex_name);
85 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
86 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
87 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
88 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
89 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
90 size, size, 0,
91 GL_RGBA, GL_UNSIGNED_BYTE,
92 texels);
93
94 text_texture.name = tex_name;
95 text_texture.size = size;
96 text_texture.texels = texels;
97
98 *texture = text_texture.name;
99
100 return TRUE;
101 }
102
103 static void
gl_tex_delete_texture(void)104 gl_tex_delete_texture (void)
105 {
106 glDeleteTextures (1, &text_texture.name);
107 g_free (text_texture.texels);
108 }
109
110 static gboolean
gl_tex_pango_ft2_render_layout(PangoLayout * layout,GLuint * texture,GLfloat * s0,GLfloat * s1,GLfloat * t0,GLfloat * t1)111 gl_tex_pango_ft2_render_layout (PangoLayout *layout,
112 GLuint *texture,
113 GLfloat *s0,
114 GLfloat *s1,
115 GLfloat *t0,
116 GLfloat *t1)
117 {
118 PangoRectangle logical_rect;
119 FT_Bitmap bitmap;
120 guint32 *t;
121 GLfloat color[4];
122 guint32 rgb;
123 GLfloat a;
124 guint8 *row, *row_end;
125 int i;
126
127 if (text_texture.size == 0)
128 return FALSE;
129
130 pango_layout_get_extents (layout, NULL, &logical_rect);
131 if (logical_rect.width == 0 || logical_rect.height == 0)
132 return FALSE;
133
134 bitmap.rows = PANGO_PIXELS (logical_rect.height);
135 bitmap.width = PANGO_PIXELS (logical_rect.width);
136
137 /* Ad hoc :-p */
138 if (bitmap.width > text_texture.size || bitmap.rows > text_texture.size)
139 return FALSE;
140
141 bitmap.pitch = bitmap.width;
142 bitmap.buffer = g_malloc (bitmap.rows * bitmap.width);
143 bitmap.num_grays = 256;
144 bitmap.pixel_mode = ft_pixel_mode_grays;
145
146 memset (bitmap.buffer, 0, bitmap.rows * bitmap.width);
147 pango_ft2_render_layout (&bitmap, layout,
148 PANGO_PIXELS (-logical_rect.x), 0);
149
150 glGetFloatv (GL_CURRENT_COLOR, color);
151 #if !defined(GL_VERSION_1_2) && G_BYTE_ORDER == G_LITTLE_ENDIAN
152 rgb = ((guint32) (color[0] * 255.0)) |
153 (((guint32) (color[1] * 255.0)) << 8) |
154 (((guint32) (color[2] * 255.0)) << 16);
155 #else
156 rgb = (((guint32) (color[0] * 255.0)) << 24) |
157 (((guint32) (color[1] * 255.0)) << 16) |
158 (((guint32) (color[2] * 255.0)) << 8);
159 #endif
160 a = color[3];
161
162 row = bitmap.buffer + bitmap.rows * bitmap.width; /* past-the-end */
163 row_end = bitmap.buffer; /* beginning */
164
165 t = (guint32 *) text_texture.texels;
166
167 if (a == 1.0)
168 {
169 do
170 {
171 row -= bitmap.width;
172 for (i = 0; i < bitmap.width; i++)
173 #if !defined(GL_VERSION_1_2) && G_BYTE_ORDER == G_LITTLE_ENDIAN
174 *t++ = rgb | (((guint32) row[i]) << 24);
175 #else
176 *t++ = rgb | ((guint32) row[i]);
177 #endif
178 }
179 while (row != row_end);
180 }
181 else
182 {
183 do
184 {
185 row -= bitmap.width;
186 for (i = 0; i < bitmap.width; i++)
187 #if !defined(GL_VERSION_1_2) && G_BYTE_ORDER == G_LITTLE_ENDIAN
188 *t++ = rgb | (((guint32) (a * row[i])) << 24);
189 #else
190 *t++ = rgb | ((guint32) (a * row[i]));
191 #endif
192 }
193 while (row != row_end);
194 }
195
196 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
197
198 glBindTexture (GL_TEXTURE_2D, text_texture.name);
199 #if !defined(GL_VERSION_1_2)
200 glTexSubImage2D (GL_TEXTURE_2D, 0,
201 0, 0, bitmap.width, bitmap.rows,
202 GL_RGBA, GL_UNSIGNED_BYTE,
203 text_texture.texels);
204 #else
205 glTexSubImage2D (GL_TEXTURE_2D, 0,
206 0, 0, bitmap.width, bitmap.rows,
207 GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,
208 text_texture.texels);
209 #endif
210
211 *texture = text_texture.name;
212
213 *s0 = 0.0;
214 *s1 = (GLfloat) bitmap.width / (GLfloat) text_texture.size;
215
216 *t0 = 0.0;
217 *t1 = (GLfloat) bitmap.rows / (GLfloat) text_texture.size;
218
219 g_free (bitmap.buffer);
220
221 return TRUE;
222 }
223
224 static void
realize(GtkWidget * widget,gpointer data)225 realize (GtkWidget *widget,
226 gpointer data)
227 {
228 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
229 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
230
231 GLUquadricObj *qobj;
232 static GLfloat light_diffuse[] = {1.0, 0.0, 0.0, 1.0};
233 static GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};
234
235 GLuint texture;
236
237 /*** OpenGL BEGIN ***/
238 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
239 return;
240
241 qobj = gluNewQuadric ();
242 gluQuadricDrawStyle (qobj, GLU_FILL);
243 glNewList (1, GL_COMPILE);
244 gluSphere (qobj, 1.0, 20, 20);
245 glEndList ();
246
247 glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse);
248 glLightfv (GL_LIGHT0, GL_POSITION, light_position);
249 glEnable (GL_LIGHTING);
250 glEnable (GL_LIGHT0);
251 glEnable (GL_DEPTH_TEST);
252
253 glClearColor (0.0, 0.0, 0.0, 0.0);
254 glClearDepth (1.0);
255
256 /* Create texture. */
257 gl_tex_create_texture (&texture);
258
259 gdk_gl_drawable_gl_end (gldrawable);
260 /*** OpenGL END ***/
261
262 /* Get PangoFT2 context. */
263 ft2_context = pango_ft2_get_context (72, 72);
264 }
265
266 static gboolean
configure_event(GtkWidget * widget,GdkEventConfigure * event,gpointer data)267 configure_event (GtkWidget *widget,
268 GdkEventConfigure *event,
269 gpointer data)
270 {
271 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
272 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
273
274 GLsizei w = widget->allocation.width;
275 GLsizei h = widget->allocation.height;
276
277 /*** OpenGL BEGIN ***/
278 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
279 return FALSE;
280
281 glViewport (0, 0, w, h);
282
283 glMatrixMode (GL_PROJECTION);
284 glLoadIdentity ();
285 gluPerspective (2.0 * FOVY_2,
286 (GLfloat) w / (GLfloat) h,
287 Z_NEAR,
288 2.5 * Z_NEAR);
289
290 glMatrixMode (GL_MODELVIEW);
291 glLoadIdentity ();
292 gluLookAt (0.0, 0.0, Z_NEAR,
293 0.0, 0.0, 0.0,
294 0.0, 1.0, 0.0);
295 glTranslatef (0.0, 0.0, -Z_NEAR);
296
297 gdk_gl_drawable_gl_end (gldrawable);
298 /*** OpenGL END ***/
299
300 return TRUE;
301 }
302
303 #define ANGLE 30.0
304 /* tan (ANGLE * PI / 180.0) */
305 #define TANGENT 0.57735
306
307 #define TEXT_Z_NEAR 2.0
308 #define TEXT_Z_FAR -5.0
309 #define TEXT_Z_DIFF 0.005
310
311 static GLfloat text_z = TEXT_Z_NEAR;
312
313 static gboolean animate = TRUE;
314
315 static gboolean
expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer data)316 expose_event (GtkWidget *widget,
317 GdkEventExpose *event,
318 gpointer data)
319 {
320 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
321 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
322
323 PangoContext *widget_context;
324 PangoFontDescription *font_desc;
325 PangoLayout *layout;
326
327 GLuint texture;
328 GLfloat s0, s1, t0, t1;
329 gboolean ret;
330
331 /* Font */
332 widget_context = gtk_widget_get_pango_context (widget);
333 font_desc = pango_context_get_font_description (widget_context);
334 pango_font_description_set_size (font_desc, 24 * PANGO_SCALE);
335 pango_context_set_font_description (ft2_context, font_desc);
336
337 /* Text layout */
338 layout = pango_layout_new (ft2_context);
339 pango_layout_set_width (layout, PANGO_SCALE * 200);
340 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
341 pango_layout_set_text (layout, text, -1);
342
343 /*** OpenGL BEGIN ***/
344 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
345 return FALSE;
346
347 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
348
349 glCallList (1);
350
351 /* Text color */
352 glColor3f (1.0, 0.9, 0.0);
353
354 /* Render text */
355 ret = gl_tex_pango_ft2_render_layout (layout,
356 &texture,
357 &s0, &s1, &t0, &t1);
358 if (ret)
359 {
360 glPushMatrix ();
361 glTranslatef (0.0, -text_z * TANGENT, text_z + 2.0);
362 glRotatef (ANGLE, 1.0, 0.0, 0.0);
363
364 glEnable (GL_TEXTURE_2D);
365 glEnable (GL_BLEND);
366 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
367
368 glBindTexture (GL_TEXTURE_2D, texture);
369 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
370 glBegin (GL_QUADS);
371 glTexCoord2f (s0, t0); glVertex3f (-1.0, 0.0, 1.0);
372 glTexCoord2f (s0, t1); glVertex3f (-1.0, 0.0, -1.0);
373 glTexCoord2f (s1, t1); glVertex3f ( 1.0, 0.0, -1.0);
374 glTexCoord2f (s1, t0); glVertex3f ( 1.0, 0.0, 1.0);
375 glEnd ();
376
377 glDisable (GL_BLEND);
378 glDisable (GL_TEXTURE_2D);
379
380 glPopMatrix ();
381 }
382
383 if (gdk_gl_drawable_is_double_buffered (gldrawable))
384 gdk_gl_drawable_swap_buffers (gldrawable);
385 else
386 glFlush ();
387
388 gdk_gl_drawable_gl_end (gldrawable);
389 /*** OpenGL END ***/
390
391 g_object_unref (G_OBJECT (layout));
392
393 return TRUE;
394 }
395
396 static void
unrealize(GtkWidget * widget,gpointer data)397 unrealize (GtkWidget *widget,
398 gpointer data)
399 {
400 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
401 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
402
403 /*** OpenGL BEGIN ***/
404 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
405 return;
406
407 gl_tex_delete_texture ();
408
409 gdk_gl_drawable_gl_end (gldrawable);
410 /*** OpenGL END ***/
411
412 g_object_unref (G_OBJECT (ft2_context));
413 pango_ft2_shutdown_display ();
414 }
415
416 static gboolean
timeout(GtkWidget * widget)417 timeout (GtkWidget *widget)
418 {
419 text_z -= TEXT_Z_DIFF;
420 if (text_z <= TEXT_Z_FAR)
421 text_z = TEXT_Z_NEAR;
422
423 /* Invalidate the whole window. */
424 gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
425
426 /* Update synchronously. */
427 gdk_window_process_updates (widget->window, FALSE);
428
429 return TRUE;
430 }
431
432 static guint timeout_id = 0;
433
434 static void
timeout_add(GtkWidget * widget)435 timeout_add (GtkWidget *widget)
436 {
437 if (timeout_id == 0)
438 {
439 timeout_id = g_timeout_add (TIMEOUT_INTERVAL,
440 (GSourceFunc) timeout,
441 widget);
442 }
443 }
444
445 static void
timeout_remove(GtkWidget * widget)446 timeout_remove (GtkWidget *widget)
447 {
448 if (timeout_id != 0)
449 {
450 g_source_remove (timeout_id);
451 timeout_id = 0;
452 }
453 }
454
455 static gboolean
map_event(GtkWidget * widget,GdkEvent * event,gpointer data)456 map_event (GtkWidget *widget,
457 GdkEvent *event,
458 gpointer data)
459 {
460 if (animate)
461 timeout_add (widget);
462
463 return TRUE;
464 }
465
466 static gboolean
unmap_event(GtkWidget * widget,GdkEvent * event,gpointer data)467 unmap_event (GtkWidget *widget,
468 GdkEvent *event,
469 gpointer data)
470 {
471 timeout_remove (widget);
472
473 return TRUE;
474 }
475
476 static gboolean
visibility_notify_event(GtkWidget * widget,GdkEventVisibility * event,gpointer data)477 visibility_notify_event (GtkWidget *widget,
478 GdkEventVisibility *event,
479 gpointer data)
480 {
481 if (animate)
482 {
483 if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
484 timeout_remove (widget);
485 else
486 timeout_add (widget);
487 }
488
489 return TRUE;
490 }
491
492 int
main(int argc,char * argv[])493 main (int argc,
494 char *argv[])
495 {
496 GdkGLConfig *glconfig;
497
498 GtkWidget *window;
499 GtkWidget *vbox;
500 GtkWidget *drawing_area;
501 GtkWidget *button;
502
503 /*
504 * Init GTK.
505 */
506
507 gtk_init (&argc, &argv);
508
509 /*
510 * Init GtkGLExt.
511 */
512
513 gtk_gl_init (&argc, &argv);
514
515 /*
516 * Configure OpenGL-capable visual.
517 */
518
519 /* Try double-buffered visual */
520 glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB |
521 GDK_GL_MODE_DEPTH |
522 GDK_GL_MODE_DOUBLE);
523 if (glconfig == NULL)
524 {
525 g_print ("*** Cannot find the double-buffered visual.\n");
526 g_print ("*** Trying single-buffered visual.\n");
527
528 /* Try single-buffered visual */
529 glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB |
530 GDK_GL_MODE_DEPTH);
531 if (glconfig == NULL)
532 {
533 g_print ("*** No appropriate OpenGL-capable visual found.\n");
534 exit (1);
535 }
536 }
537
538 /*
539 * Top-level window.
540 */
541
542 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
543 gtk_window_set_title (GTK_WINDOW (window), "font-pangoft2");
544
545 /* Get automatically redrawn if any of their children changed allocation. */
546 gtk_container_set_reallocate_redraws (GTK_CONTAINER (window), TRUE);
547
548 g_signal_connect (G_OBJECT (window), "delete_event",
549 G_CALLBACK (gtk_main_quit), NULL);
550
551 /*
552 * VBox.
553 */
554
555 vbox = gtk_vbox_new (FALSE, 0);
556 gtk_container_add (GTK_CONTAINER (window), vbox);
557 gtk_widget_show (vbox);
558
559 /*
560 * Drawing area for drawing OpenGL scene.
561 */
562
563 drawing_area = gtk_drawing_area_new ();
564 gtk_widget_set_size_request (drawing_area, 200, 200);
565
566 /* Set OpenGL-capability to the widget. */
567 gtk_widget_set_gl_capability (drawing_area,
568 glconfig,
569 NULL,
570 TRUE,
571 GDK_GL_RGBA_TYPE);
572
573 g_signal_connect_after (G_OBJECT (drawing_area), "realize",
574 G_CALLBACK (realize), NULL);
575 g_signal_connect (G_OBJECT (drawing_area), "configure_event",
576 G_CALLBACK (configure_event), NULL);
577 g_signal_connect (G_OBJECT (drawing_area), "expose_event",
578 G_CALLBACK (expose_event), NULL);
579 g_signal_connect (G_OBJECT (drawing_area), "unrealize",
580 G_CALLBACK (unrealize), NULL);
581
582 g_signal_connect (G_OBJECT (drawing_area), "map_event",
583 G_CALLBACK (map_event), NULL);
584 g_signal_connect (G_OBJECT (drawing_area), "unmap_event",
585 G_CALLBACK (unmap_event), NULL);
586 g_signal_connect (G_OBJECT (drawing_area), "visibility_notify_event",
587 G_CALLBACK (visibility_notify_event), NULL);
588
589 gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
590
591 gtk_widget_show (drawing_area);
592
593 /*
594 * Simple quit button.
595 */
596
597 button = gtk_button_new_with_label ("Quit");
598
599 g_signal_connect (G_OBJECT (button), "clicked",
600 G_CALLBACK (gtk_main_quit), NULL);
601
602 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
603
604 gtk_widget_show (button);
605
606 /*
607 * Show window.
608 */
609
610 gtk_widget_show (window);
611
612 /*
613 * Main loop.
614 */
615
616 gtk_main ();
617
618 return 0;
619 }
620