2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
15 Neither the name of Loki software nor the names of its contributors may be used
16 to endorse or promote products derived from this software without specific prior
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 // OpenGL widget based on GtkGLExt
35 #include <gtk/gtkgl.h>
38 #include <pango/pangoft2.h>
44 typedef int* attribs_t;
45 typedef const attribs_t* configs_iterator;
47 int config_rgba32[] = {
54 GDK_GL_ATTRIB_LIST_NONE,
64 GDK_GL_ATTRIB_LIST_NONE,
67 const attribs_t configs[] = {
72 GdkGLConfig* glconfig_new()
74 GdkGLConfig* glconfig = NULL;
76 for(configs_iterator i = configs, end = configs + 2; i != end && glconfig == NULL; ++i)
78 glconfig = gdk_gl_config_new(*i);
83 return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE));
89 int config_rgba32_depth32[] = {
96 GDK_GL_DEPTH_SIZE, 32,
97 GDK_GL_ATTRIB_LIST_NONE,
100 int config_rgba32_depth24[] = {
105 GDK_GL_GREEN_SIZE, 8,
106 GDK_GL_ALPHA_SIZE, 8,
107 GDK_GL_DEPTH_SIZE, 24,
108 GDK_GL_ATTRIB_LIST_NONE,
111 int config_rgba32_depth16[] = {
116 GDK_GL_GREEN_SIZE, 8,
117 GDK_GL_ALPHA_SIZE, 8,
118 GDK_GL_DEPTH_SIZE, 16,
119 GDK_GL_ATTRIB_LIST_NONE,
122 int config_rgba32_depth[] = {
127 GDK_GL_GREEN_SIZE, 8,
128 GDK_GL_ALPHA_SIZE, 8,
129 GDK_GL_DEPTH_SIZE, 1,
130 GDK_GL_ATTRIB_LIST_NONE,
133 int config_rgba_depth16[] = {
138 GDK_GL_GREEN_SIZE, 1,
139 GDK_GL_ALPHA_SIZE, 1,
140 GDK_GL_DEPTH_SIZE, 16,
141 GDK_GL_ATTRIB_LIST_NONE,
144 int config_rgba_depth[] = {
149 GDK_GL_GREEN_SIZE, 1,
150 GDK_GL_ALPHA_SIZE, 1,
151 GDK_GL_DEPTH_SIZE, 1,
152 GDK_GL_ATTRIB_LIST_NONE,
155 const attribs_t configs_with_depth[] =
157 config_rgba32_depth32,
158 config_rgba32_depth24,
159 config_rgba32_depth16,
165 GdkGLConfig* glconfig_new_with_depth()
167 GdkGLConfig* glconfig = NULL;
169 for(configs_iterator i = configs_with_depth, end = configs_with_depth + 6; i != end && glconfig == NULL; ++i)
171 glconfig = gdk_gl_config_new(*i);
176 return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE | GDK_GL_MODE_DEPTH));
182 GtkWidget* WINAPI gtk_glwidget_new (gboolean zbuffer, GtkWidget* share)
184 GtkWidget* drawing_area = gtk_drawing_area_new();
185 GdkGLConfig* glconfig = (zbuffer) ? glconfig_new_with_depth() : glconfig_new();
186 GdkGLContext* shared_context = (share) ? gtk_widget_get_gl_context(share) : NULL;
188 gtk_widget_set_gl_capability (drawing_area, glconfig, shared_context, TRUE, GDK_GL_RGBA_TYPE);
193 void WINAPI gtk_glwidget_destroy_context (GtkWidget *widget)
197 void WINAPI gtk_glwidget_create_context (GtkWidget *widget)
201 void WINAPI gtk_glwidget_swap_buffers (GtkWidget *widget)
203 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
204 gdk_gl_drawable_swap_buffers (gldrawable);
207 gboolean WINAPI gtk_glwidget_make_current (GtkWidget *widget)
209 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
210 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
211 return gdk_gl_drawable_gl_begin (gldrawable, glcontext);
217 GLuint font_list_base;
218 static gchar font_string[] = "courier 8";
219 static gint font_height;
220 static int font_created = 0;
224 // Think about rewriting this font stuff to use OpenGL display lists and glBitmap().
225 // Bit maps together with display lists may offer a performance increase, but
226 // they would not allow antialiased fonts.
227 static const char font_string[] = "Monospace";
228 static const int font_height = 10;
229 static int font_ascent = -1;
230 static int font_descent = -1;
231 static int y_offset_bitmap_render_pango_units = -1;
232 static PangoContext *ft2_context = NULL;
233 static int _debug_font_created = 0;
238 // Units are pixels. Returns a positive value [most likely].
239 int gtk_glwidget_font_ascent()
243 return 6; // Approximation.
247 if (!_debug_font_created) {
248 Error("Programming error: gtk_glwidget_font_ascent() called but font does not exist; "
249 "you should have called gtk_glwidget_create_font() first");
257 // Units are pixels. Returns a positive value [most likely].
258 int gtk_glwidget_font_descent()
262 return 0; // Approximation.
266 if (!_debug_font_created) {
267 Error("Programming error: gtk_glwidget_font_descent() called but font does not exist; "
268 "you should have called gtk_glwidget_create_font() first");
278 void gtk_glwidget_create_font_win_internal()
280 if (font_created) return;
283 PangoFontDescription *font_desc;
285 PangoFontMetrics *font_metrics;
287 font_list_base = qglGenLists (256);
289 font_desc = pango_font_description_from_string (font_string);
291 font = gdk_gl_font_use_pango_font (font_desc, 0, 256, font_list_base);
295 font_metrics = pango_font_get_metrics (font, NULL);
297 font_height = pango_font_metrics_get_ascent (font_metrics) +
298 pango_font_metrics_get_descent (font_metrics);
299 font_height = PANGO_PIXELS (font_height);
301 pango_font_metrics_unref (font_metrics);
304 pango_font_description_free (font_desc);
309 void gtk_glwidget_create_font()
317 PangoFontDescription *font_desc;
319 PangoRectangle log_rect;
320 int font_ascent_pango_units;
321 int font_descent_pango_units;
323 if (_debug_font_created) {
324 Error("Programming error: gtk_glwidget_create_font() was already called; "
325 "you must call gtk_glwidget_destroy_font() before creating font again");
327 _debug_font_created = 1;
329 // This call is deprecated so we'll have to fix it sometime.
330 ft2_context = pango_ft2_get_context(72, 72);
332 font_desc = pango_font_description_from_string(font_string);
333 pango_font_description_set_size(font_desc, font_height * PANGO_SCALE);
334 pango_context_set_font_description(ft2_context, font_desc);
335 pango_font_description_free(font_desc);
337 layout = pango_layout_new(ft2_context);
341 PangoLayoutIter *iter;
342 iter = pango_layout_get_iter(layout);
343 font_ascent_pango_units = pango_layout_iter_get_baseline(iter);
344 pango_layout_iter_free(iter);
348 // I don't believe that's standard preprocessor syntax?
349 #if !PANGO_VERSION_CHECK(1,22,0)
350 PangoLayoutIter *iter;
351 iter = pango_layout_get_iter(layout);
352 font_ascent_pango_units = pango_layout_iter_get_baseline(iter);
353 pango_layout_iter_free(iter);
355 font_ascent_pango_units = pango_layout_get_baseline(layout);
360 pango_layout_get_extents(layout, NULL, &log_rect);
361 g_object_unref(G_OBJECT(layout));
362 font_descent_pango_units = log_rect.height - font_ascent_pango_units;
364 font_ascent = PANGO_PIXELS_CEIL(font_ascent_pango_units);
365 font_descent = PANGO_PIXELS_CEIL(font_descent_pango_units);
366 y_offset_bitmap_render_pango_units = (font_ascent * PANGO_SCALE) - font_ascent_pango_units;
371 void gtk_glwidget_destroy_font()
379 if (!_debug_font_created) {
380 Error("Programming error: gtk_glwidget_destroy_font() called when font "
386 y_offset_bitmap_render_pango_units = -1;
387 g_object_unref(G_OBJECT(ft2_context));
388 _debug_font_created = 0;
394 // Renders the input text at the current location with the current color.
395 // The X position of the current location is used to place the left edge of the text image,
396 // where the text image bounds are defined as the logical extents of the line of text.
397 // The Y position of the current location is used to place the bottom of the text image.
398 // You should offset the Y position by the amount returned by gtk_glwidget_font_descent()
399 // if you want to place the baseline of the text image at the current Y position.
400 // Note: A problem with this function is that if the lower left corner of the text falls
401 // just a hair outside of the viewport (meaning the current raster position is invalid),
402 // then no text will be rendered. The solution to this is a very hacky one. You can search
403 // Google for "glDrawPixels clipping".
404 void gtk_glwidget_print_string(const char *s)
408 gtk_glwidget_create_font_win_internal();
409 qglListBase(font_list_base);
410 qglCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s);
414 // The idea for this code initially came from the font-pangoft2.c example that comes with GtkGLExt.
417 PangoRectangle log_rect;
419 unsigned char *begin_bitmap_buffer;
421 GLint previous_unpack_alignment;
422 GLboolean previous_blend_enabled;
423 GLint previous_blend_func_src;
424 GLint previous_blend_func_dst;
425 GLfloat previous_red_bias;
426 GLfloat previous_green_bias;
427 GLfloat previous_blue_bias;
428 GLfloat previous_alpha_scale;
430 if (!_debug_font_created) {
431 Error("Programming error: gtk_glwidget_print_string() called but font does not exist; "
432 "you should have called gtk_glwidget_create_font() first");
435 layout = pango_layout_new(ft2_context);
436 pango_layout_set_width(layout, -1); // -1 no wrapping. All text on one line.
437 pango_layout_set_text(layout, s, -1); // -1 null-terminated string.
438 pango_layout_get_extents(layout, NULL, &log_rect);
440 if (log_rect.width > 0 && log_rect.height > 0) {
441 bitmap.rows = font_ascent + font_descent;
442 bitmap.width = PANGO_PIXELS_CEIL(log_rect.width);
443 bitmap.pitch = -bitmap.width; // Rendering it "upside down" for OpenGL.
444 begin_bitmap_buffer = (unsigned char *) g_malloc(bitmap.rows * bitmap.width);
445 memset(begin_bitmap_buffer, 0, bitmap.rows * bitmap.width);
446 bitmap.buffer = begin_bitmap_buffer + (bitmap.rows - 1) * bitmap.width; // See pitch above.
447 bitmap.num_grays = 0xff;
448 bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
449 pango_ft2_render_layout_subpixel(&bitmap, layout, -log_rect.x,
450 y_offset_bitmap_render_pango_units);
451 qglGetFloatv(GL_CURRENT_COLOR, color);
453 // Save state. I didn't see any OpenGL push/pop operations for these.
454 // Question: Is saving/restoring this state necessary? Being safe.
455 qglGetIntegerv(GL_UNPACK_ALIGNMENT, &previous_unpack_alignment);
456 previous_blend_enabled = qglIsEnabled(GL_BLEND);
457 qglGetIntegerv(GL_BLEND_SRC, &previous_blend_func_src);
458 qglGetIntegerv(GL_BLEND_DST, &previous_blend_func_dst);
459 qglGetFloatv(GL_RED_BIAS, &previous_red_bias);
460 qglGetFloatv(GL_GREEN_BIAS, &previous_green_bias);
461 qglGetFloatv(GL_BLUE_BIAS, &previous_blue_bias);
462 qglGetFloatv(GL_ALPHA_SCALE, &previous_alpha_scale);
464 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
466 qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
467 qglPixelTransferf(GL_RED_BIAS, color[0]);
468 qglPixelTransferf(GL_GREEN_BIAS, color[1]);
469 qglPixelTransferf(GL_BLUE_BIAS, color[2]);
470 qglPixelTransferf(GL_ALPHA_SCALE, color[3]);
472 qglDrawPixels(bitmap.width, bitmap.rows,
473 GL_ALPHA, GL_UNSIGNED_BYTE, begin_bitmap_buffer);
474 g_free(begin_bitmap_buffer);
476 // Restore state in reverse order of how we set it.
477 qglPixelTransferf(GL_ALPHA_SCALE, previous_alpha_scale);
478 qglPixelTransferf(GL_BLUE_BIAS, previous_blue_bias);
479 qglPixelTransferf(GL_GREEN_BIAS, previous_green_bias);
480 qglPixelTransferf(GL_RED_BIAS, previous_red_bias);
481 qglBlendFunc(previous_blend_func_src, previous_blend_func_dst);
482 if (!previous_blend_enabled) { qglDisable(GL_BLEND); }
483 qglPixelStorei(GL_UNPACK_ALIGNMENT, previous_unpack_alignment);
486 g_object_unref(G_OBJECT(layout));
491 void gtk_glwidget_print_char(char s)
495 gtk_glwidget_create_font_win_internal();
496 qglListBase(font_list_base);
497 qglCallLists(1, GL_UNSIGNED_BYTE, (unsigned char *) &s);
504 gtk_glwidget_print_string(str);