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>
36 #include <pango/pangoft2.h>
40 typedef int* attribs_t;
41 typedef const attribs_t* configs_iterator;
43 int config_rgba32[] = {
50 GDK_GL_ATTRIB_LIST_NONE,
60 GDK_GL_ATTRIB_LIST_NONE,
63 const attribs_t configs[] = {
68 GdkGLConfig* glconfig_new()
70 GdkGLConfig* glconfig = NULL;
72 for(configs_iterator i = configs, end = configs + 2; i != end && glconfig == NULL; ++i)
74 glconfig = gdk_gl_config_new(*i);
79 return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE));
85 int config_rgba32_depth32[] = {
92 GDK_GL_DEPTH_SIZE, 32,
93 GDK_GL_ATTRIB_LIST_NONE,
96 int config_rgba32_depth24[] = {
101 GDK_GL_GREEN_SIZE, 8,
102 GDK_GL_ALPHA_SIZE, 8,
103 GDK_GL_DEPTH_SIZE, 24,
104 GDK_GL_ATTRIB_LIST_NONE,
107 int config_rgba32_depth16[] = {
112 GDK_GL_GREEN_SIZE, 8,
113 GDK_GL_ALPHA_SIZE, 8,
114 GDK_GL_DEPTH_SIZE, 16,
115 GDK_GL_ATTRIB_LIST_NONE,
118 int config_rgba32_depth[] = {
123 GDK_GL_GREEN_SIZE, 8,
124 GDK_GL_ALPHA_SIZE, 8,
125 GDK_GL_DEPTH_SIZE, 1,
126 GDK_GL_ATTRIB_LIST_NONE,
129 int config_rgba_depth16[] = {
134 GDK_GL_GREEN_SIZE, 1,
135 GDK_GL_ALPHA_SIZE, 1,
136 GDK_GL_DEPTH_SIZE, 16,
137 GDK_GL_ATTRIB_LIST_NONE,
140 int config_rgba_depth[] = {
145 GDK_GL_GREEN_SIZE, 1,
146 GDK_GL_ALPHA_SIZE, 1,
147 GDK_GL_DEPTH_SIZE, 1,
148 GDK_GL_ATTRIB_LIST_NONE,
151 const attribs_t configs_with_depth[] =
153 config_rgba32_depth32,
154 config_rgba32_depth24,
155 config_rgba32_depth16,
161 GdkGLConfig* glconfig_new_with_depth()
163 GdkGLConfig* glconfig = NULL;
165 for(configs_iterator i = configs_with_depth, end = configs_with_depth + 6; i != end && glconfig == NULL; ++i)
167 glconfig = gdk_gl_config_new(*i);
172 return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE | GDK_GL_MODE_DEPTH));
178 GtkWidget* WINAPI gtk_glwidget_new (gboolean zbuffer, GtkWidget* share)
180 GtkWidget* drawing_area = gtk_drawing_area_new();
181 GdkGLConfig* glconfig = (zbuffer) ? glconfig_new_with_depth() : glconfig_new();
182 GdkGLContext* shared_context = (share) ? gtk_widget_get_gl_context(share) : NULL;
184 gtk_widget_set_gl_capability (drawing_area, glconfig, shared_context, TRUE, GDK_GL_RGBA_TYPE);
189 void WINAPI gtk_glwidget_destroy_context (GtkWidget *widget)
193 void WINAPI gtk_glwidget_create_context (GtkWidget *widget)
197 void WINAPI gtk_glwidget_swap_buffers (GtkWidget *widget)
199 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
200 gdk_gl_drawable_swap_buffers (gldrawable);
203 gboolean WINAPI gtk_glwidget_make_current (GtkWidget *widget)
205 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
206 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
207 return gdk_gl_drawable_gl_begin (gldrawable, glcontext);
211 // Think about rewriting this font stuff to use OpenGL display lists and bit maps.
212 // Bit maps together with display lists may offer a performance increase, but
213 // they would not allow antialiased fonts.
214 static const char font_string[] = "Monospace";
215 static const int font_height = 10;
216 static int font_ascent = -1;
217 static int font_descent = -1;
218 static int y_offset_bitmap_render_pango_units = -1;
219 static PangoContext *ft2_context = NULL;
220 static int _debug_font_created = 0;
223 // Units are pixels. Returns a positive value [most likely].
224 int gtk_glwidget_font_ascent()
226 if (!_debug_font_created) {
227 Error("Programming error: gtk_glwidget_font_ascent() called but font does not exist; "
228 "you should have called gtk_glwidget_create_font() first");
234 // Units are pixels. Returns a positive value [most likely].
235 int gtk_glwidget_font_descent()
237 if (!_debug_font_created) {
238 Error("Programming error: gtk_glwidget_font_descent() called but font does not exist; "
239 "you should have called gtk_glwidget_create_font() first");
245 void gtk_glwidget_create_font()
247 PangoFontDescription *font_desc;
249 PangoRectangle log_rect;
250 int font_ascent_pango_units;
251 int font_descent_pango_units;
253 if (_debug_font_created) {
254 Error("Programming error: gtk_glwidget_create_font() was already called; "
255 "you must call gtk_glwidget_destroy_font() before creating font again");
257 _debug_font_created = 1;
259 // This call is deprecated so we'll have to fix it sometime.
260 ft2_context = pango_ft2_get_context(72, 72);
262 font_desc = pango_font_description_from_string(font_string);
263 pango_font_description_set_size(font_desc, font_height * PANGO_SCALE);
264 pango_context_set_font_description(ft2_context, font_desc);
265 pango_font_description_free(font_desc);
267 layout = pango_layout_new(ft2_context);
268 #if !PANGO_VERSION_CHECK(1,22,0)
269 PangoLayoutIter *iter;
270 iter = pango_layout_get_iter(layout);
271 font_ascent_pango_units = pango_layout_iter_get_baseline(iter);
272 pango_layout_iter_free(iter);
274 font_ascent_pango_units = pango_layout_get_baseline(layout);
276 pango_layout_get_extents(layout, NULL, &log_rect);
277 g_object_unref(G_OBJECT(layout));
278 font_descent_pango_units = log_rect.height - font_ascent_pango_units;
280 font_ascent = PANGO_PIXELS_CEIL(font_ascent_pango_units);
281 font_descent = PANGO_PIXELS_CEIL(font_descent_pango_units);
282 y_offset_bitmap_render_pango_units = (font_ascent * PANGO_SCALE) - font_ascent_pango_units;
285 void gtk_glwidget_destroy_font()
287 if (!_debug_font_created) {
288 Error("Programming error: gtk_glwidget_destroy_font() called when font "
294 y_offset_bitmap_render_pango_units = -1;
295 g_object_unref(G_OBJECT(ft2_context));
296 _debug_font_created = 0;
300 // Renders the input text at the current location with the current color.
301 // The X position of the current location is used to place the left edge of the text image,
302 // where the text image bounds are defined as the logical extents of the line of text.
303 // The Y position of the current location is used to place the bottom of the text image.
304 // You should offset the Y position by the amount returned by gtk_glwidget_font_descent()
305 // if you want to place the baseline of the text image at the current Y position.
306 // Note: A problem with this function is that if the lower left corner of the text falls
307 // just a hair outside of the viewport (meaning the current raster position is invalid),
308 // then no text will be rendered. The solution to this is a very hacky one. You can search
309 // Google for "glDrawPixels clipping".
310 void gtk_glwidget_print_string(const char *s)
312 // Much of this code is copied from the font-pangoft2.c example that comes with GtkGLExt.
315 PangoRectangle ink_rect;
316 PangoRectangle log_rect;
323 guint8 *row, *row_end;
325 GLint previous_unpack_alignment;
326 GLboolean previous_blend_enabled;
327 GLint previous_blend_src;
328 GLint previous_blend_dst;
331 if (!_debug_font_created) {
332 Error("Programming error: gtk_glwidget_print_string() called but font does not exist; "
333 "you should have called gtk_glwidget_create_font() first");
336 layout = pango_layout_new(ft2_context);
337 pango_layout_set_width(layout, -1); // -1 no wrapping. All text on one line.
338 pango_layout_set_text(layout, s, -1); // -1 null-terminated string.
339 pango_layout_get_extents(layout, &ink_rect, &log_rect);
341 if (log_rect.width > 0 && log_rect.height > 0) {
342 bitmap.rows = font_ascent + font_descent;
343 bitmap.width = PANGO_PIXELS_CEIL(log_rect.width);
344 bitmap.pitch = bitmap.width;
345 bitmap.buffer = g_malloc(bitmap.rows * bitmap.width);
346 bitmap.num_grays = 0xff;
347 bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
348 memset(bitmap.buffer, 0, bitmap.rows * bitmap.width);
349 pango_ft2_render_layout_subpixel(&bitmap, layout, -log_rect.x,
350 y_offset_bitmap_render_pango_units);
352 pixels = g_malloc(bitmap.rows * bitmap.width * 4);
353 p = (guint32 *) pixels;
354 qglGetFloatv(GL_CURRENT_COLOR, color);
355 #if !defined(GL_VERSION_1_2) && G_BYTE_ORDER == G_LITTLE_ENDIAN
357 (((guint32) (color[0] * 255.0)) << 0) |
358 (((guint32) (color[1] * 255.0)) << 8) |
359 (((guint32) (color[2] * 255.0)) << 16);
362 (((guint32) (color[0] * 255.0)) << 24) |
363 (((guint32) (color[1] * 255.0)) << 16) |
364 (((guint32) (color[2] * 255.0)) << 8);
368 row = bitmap.buffer + bitmap.rows * bitmap.width; // Past the end.
369 row_end = bitmap.buffer; // Beginning.
374 for (i = 0; i < bitmap.width; i++) {
375 #if !defined(GL_VERSION_1_2) && G_BYTE_ORDER == G_LITTLE_ENDIAN
376 *p++ = rgb | (((guint32) row[i]) << 24);
378 *p++ = rgb | ((guint32) row[i]);
381 } while (row != row_end);
384 else { // Translucent. Much less efficient, so try to avoid.
387 for (i = 0; i < bitmap.width; i++) {
388 #if !defined(GL_VERSION_1_2) && G_BYTE_ORDER == G_LITTLE_ENDIAN
389 *p++ = rgb | (((guint32) (alpha * row[i])) << 24);
391 *p++ = rgb | ((guint32) (alpha * row[i]));
394 } while (row != row_end);
397 // Save state. I didn't see any OpenGL push/pop operations for these.
398 // Question: Is saving/restoring this state necessary?
399 qglGetIntegerv(GL_UNPACK_ALIGNMENT, &previous_unpack_alignment);
400 previous_blend_enabled = qglIsEnabled(GL_BLEND);
401 qglGetIntegerv(GL_BLEND_SRC, &previous_blend_src);
402 qglGetIntegerv(GL_BLEND_DST, &previous_blend_dst);
404 qglPixelStorei(GL_UNPACK_ALIGNMENT, 4);
406 qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
408 #if !defined(GL_VERSION_1_2)
409 qglDrawPixels(bitmap.width, bitmap.rows,
410 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
412 qglDrawPixels(bitmap.width, bitmap.rows,
413 GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, pixels);
416 // Restore state in reverse order of how we set it.
417 qglBlendFunc(previous_blend_src, previous_blend_dst);
418 if (!previous_blend_enabled) { qglDisable(GL_BLEND); }
419 qglPixelStorei(GL_UNPACK_ALIGNMENT, previous_unpack_alignment);
421 g_free(bitmap.buffer);
425 g_object_unref(G_OBJECT(layout));
428 void gtk_glwidget_print_char(char s)
433 gtk_glwidget_print_string(str);