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>
37 #include <pango/pangoft2.h>
42 typedef int* attribs_t;
43 typedef const attribs_t* configs_iterator;
45 int config_rgba32[] = {
52 GDK_GL_ATTRIB_LIST_NONE,
62 GDK_GL_ATTRIB_LIST_NONE,
65 const attribs_t configs[] = {
70 GdkGLConfig* glconfig_new()
72 GdkGLConfig* glconfig = NULL;
74 for(configs_iterator i = configs, end = configs + 2; i != end && glconfig == NULL; ++i)
76 glconfig = gdk_gl_config_new(*i);
81 return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE));
87 int config_rgba32_depth32[] = {
94 GDK_GL_DEPTH_SIZE, 32,
95 GDK_GL_ATTRIB_LIST_NONE,
98 int config_rgba32_depth24[] = {
103 GDK_GL_GREEN_SIZE, 8,
104 GDK_GL_ALPHA_SIZE, 8,
105 GDK_GL_DEPTH_SIZE, 24,
106 GDK_GL_ATTRIB_LIST_NONE,
109 int config_rgba32_depth16[] = {
114 GDK_GL_GREEN_SIZE, 8,
115 GDK_GL_ALPHA_SIZE, 8,
116 GDK_GL_DEPTH_SIZE, 16,
117 GDK_GL_ATTRIB_LIST_NONE,
120 int config_rgba32_depth[] = {
125 GDK_GL_GREEN_SIZE, 8,
126 GDK_GL_ALPHA_SIZE, 8,
127 GDK_GL_DEPTH_SIZE, 1,
128 GDK_GL_ATTRIB_LIST_NONE,
131 int config_rgba_depth16[] = {
136 GDK_GL_GREEN_SIZE, 1,
137 GDK_GL_ALPHA_SIZE, 1,
138 GDK_GL_DEPTH_SIZE, 16,
139 GDK_GL_ATTRIB_LIST_NONE,
142 int config_rgba_depth[] = {
147 GDK_GL_GREEN_SIZE, 1,
148 GDK_GL_ALPHA_SIZE, 1,
149 GDK_GL_DEPTH_SIZE, 1,
150 GDK_GL_ATTRIB_LIST_NONE,
153 const attribs_t configs_with_depth[] =
155 config_rgba32_depth32,
156 config_rgba32_depth24,
157 config_rgba32_depth16,
163 GdkGLConfig* glconfig_new_with_depth()
165 GdkGLConfig* glconfig = NULL;
167 for(configs_iterator i = configs_with_depth, end = configs_with_depth + 6; i != end && glconfig == NULL; ++i)
169 glconfig = gdk_gl_config_new(*i);
174 return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE | GDK_GL_MODE_DEPTH));
180 GtkWidget* WINAPI gtk_glwidget_new (gboolean zbuffer, GtkWidget* share)
182 GtkWidget* drawing_area = gtk_drawing_area_new();
183 GdkGLConfig* glconfig = (zbuffer) ? glconfig_new_with_depth() : glconfig_new();
184 GdkGLContext* shared_context = (share) ? gtk_widget_get_gl_context(share) : NULL;
186 gtk_widget_set_gl_capability (drawing_area, glconfig, shared_context, TRUE, GDK_GL_RGBA_TYPE);
191 void WINAPI gtk_glwidget_destroy_context (GtkWidget *widget)
195 void WINAPI gtk_glwidget_create_context (GtkWidget *widget)
199 void WINAPI gtk_glwidget_swap_buffers (GtkWidget *widget)
201 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
202 gdk_gl_drawable_swap_buffers (gldrawable);
205 gboolean WINAPI gtk_glwidget_make_current (GtkWidget *widget)
207 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
208 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
209 return gdk_gl_drawable_gl_begin (gldrawable, glcontext);
213 // Think about rewriting this font stuff to use OpenGL display lists and glBitmap().
214 // Bit maps together with display lists may offer a performance increase, but
215 // they would not allow antialiased fonts.
217 static const char font_string[] = "Courier bold";
218 static const int font_height = 9;
220 static const char font_string[] = "Monospace";
221 static const int font_height = 10;
223 static int font_ascent = -1;
224 static int font_descent = -1;
225 static int y_offset_bitmap_render_pango_units = -1;
226 static PangoContext *ft2_context = NULL;
227 static int _debug_font_created = 0;
230 // Units are pixels. Returns a positive value [most likely].
231 int gtk_glwidget_font_ascent()
233 if (!_debug_font_created) {
234 Error("Programming error: gtk_glwidget_font_ascent() called but font does not exist; "
235 "you should have called gtk_glwidget_create_font() first");
241 // Units are pixels. Returns a positive value [most likely].
242 int gtk_glwidget_font_descent()
244 if (!_debug_font_created) {
245 Error("Programming error: gtk_glwidget_font_descent() called but font does not exist; "
246 "you should have called gtk_glwidget_create_font() first");
253 void gtk_glwidget_create_font()
255 PangoFontDescription *font_desc;
257 PangoRectangle log_rect;
258 int font_ascent_pango_units;
259 int font_descent_pango_units;
261 if (_debug_font_created) {
262 Error("Programming error: gtk_glwidget_create_font() was already called; "
263 "you must call gtk_glwidget_destroy_font() before creating font again");
265 _debug_font_created = 1;
267 // This call is deprecated so we'll have to fix it sometime.
268 ft2_context = pango_ft2_get_context(72, 72);
270 font_desc = pango_font_description_from_string(font_string);
271 pango_font_description_set_size(font_desc, font_height * PANGO_SCALE);
272 pango_context_set_font_description(ft2_context, font_desc);
273 pango_font_description_free(font_desc);
275 layout = pango_layout_new(ft2_context);
277 // I don't believe that's standard preprocessor syntax?
278 #if !PANGO_VERSION_CHECK(1,22,0)
279 PangoLayoutIter *iter;
280 iter = pango_layout_get_iter(layout);
281 font_ascent_pango_units = pango_layout_iter_get_baseline(iter);
282 pango_layout_iter_free(iter);
284 font_ascent_pango_units = pango_layout_get_baseline(layout);
287 pango_layout_get_extents(layout, NULL, &log_rect);
288 g_object_unref(G_OBJECT(layout));
289 font_descent_pango_units = log_rect.height - font_ascent_pango_units;
291 font_ascent = PANGO_PIXELS_CEIL(font_ascent_pango_units);
292 font_descent = PANGO_PIXELS_CEIL(font_descent_pango_units);
293 y_offset_bitmap_render_pango_units = (font_ascent * PANGO_SCALE) - font_ascent_pango_units;
296 void gtk_glwidget_destroy_font()
298 if (!_debug_font_created) {
299 Error("Programming error: gtk_glwidget_destroy_font() called when font "
305 y_offset_bitmap_render_pango_units = -1;
306 g_object_unref(G_OBJECT(ft2_context));
307 _debug_font_created = 0;
311 // Renders the input text at the current location with the current color.
312 // The X position of the current location is used to place the left edge of the text image,
313 // where the text image bounds are defined as the logical extents of the line of text.
314 // The Y position of the current location is used to place the bottom of the text image.
315 // You should offset the Y position by the amount returned by gtk_glwidget_font_descent()
316 // if you want to place the baseline of the text image at the current Y position.
317 // Note: A problem with this function is that if the lower left corner of the text falls
318 // just a hair outside of the viewport (meaning the current raster position is invalid),
319 // then no text will be rendered. The solution to this is a very hacky one. You can search
320 // Google for "glDrawPixels clipping".
321 void gtk_glwidget_print_string(const char *s)
323 // The idea for this code initially came from the font-pangoft2.c example that comes with GtkGLExt.
326 PangoRectangle log_rect;
328 unsigned char *begin_bitmap_buffer;
330 GLint previous_unpack_alignment;
331 GLboolean previous_blend_enabled;
332 GLint previous_blend_func_src;
333 GLint previous_blend_func_dst;
334 GLfloat previous_red_bias;
335 GLfloat previous_green_bias;
336 GLfloat previous_blue_bias;
337 GLfloat previous_alpha_scale;
339 if (!_debug_font_created) {
340 Error("Programming error: gtk_glwidget_print_string() called but font does not exist; "
341 "you should have called gtk_glwidget_create_font() first");
344 layout = pango_layout_new(ft2_context);
345 pango_layout_set_width(layout, -1); // -1 no wrapping. All text on one line.
346 pango_layout_set_text(layout, s, -1); // -1 null-terminated string.
347 pango_layout_get_extents(layout, NULL, &log_rect);
349 if (log_rect.width > 0 && log_rect.height > 0) {
350 bitmap.rows = font_ascent + font_descent;
351 bitmap.width = PANGO_PIXELS_CEIL(log_rect.width);
352 bitmap.pitch = -bitmap.width; // Rendering it "upside down" for OpenGL.
353 begin_bitmap_buffer = (unsigned char *) g_malloc(bitmap.rows * bitmap.width);
354 memset(begin_bitmap_buffer, 0, bitmap.rows * bitmap.width);
355 bitmap.buffer = begin_bitmap_buffer + (bitmap.rows - 1) * bitmap.width; // See pitch above.
356 bitmap.num_grays = 0xff;
357 bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
358 pango_ft2_render_layout_subpixel(&bitmap, layout, -log_rect.x,
359 y_offset_bitmap_render_pango_units);
360 qglGetFloatv(GL_CURRENT_COLOR, color);
362 // Save state. I didn't see any OpenGL push/pop operations for these.
363 // Question: Is saving/restoring this state necessary? Being safe.
364 qglGetIntegerv(GL_UNPACK_ALIGNMENT, &previous_unpack_alignment);
365 previous_blend_enabled = qglIsEnabled(GL_BLEND);
366 qglGetIntegerv(GL_BLEND_SRC, &previous_blend_func_src);
367 qglGetIntegerv(GL_BLEND_DST, &previous_blend_func_dst);
368 qglGetFloatv(GL_RED_BIAS, &previous_red_bias);
369 qglGetFloatv(GL_GREEN_BIAS, &previous_green_bias);
370 qglGetFloatv(GL_BLUE_BIAS, &previous_blue_bias);
371 qglGetFloatv(GL_ALPHA_SCALE, &previous_alpha_scale);
373 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
375 qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
376 qglPixelTransferf(GL_RED_BIAS, color[0]);
377 qglPixelTransferf(GL_GREEN_BIAS, color[1]);
378 qglPixelTransferf(GL_BLUE_BIAS, color[2]);
379 qglPixelTransferf(GL_ALPHA_SCALE, color[3]);
381 qglDrawPixels(bitmap.width, bitmap.rows,
382 GL_ALPHA, GL_UNSIGNED_BYTE, begin_bitmap_buffer);
383 g_free(begin_bitmap_buffer);
385 // Restore state in reverse order of how we set it.
386 qglPixelTransferf(GL_ALPHA_SCALE, previous_alpha_scale);
387 qglPixelTransferf(GL_BLUE_BIAS, previous_blue_bias);
388 qglPixelTransferf(GL_GREEN_BIAS, previous_green_bias);
389 qglPixelTransferf(GL_RED_BIAS, previous_red_bias);
390 qglBlendFunc(previous_blend_func_src, previous_blend_func_dst);
391 if (!previous_blend_enabled) { qglDisable(GL_BLEND); }
392 qglPixelStorei(GL_UNPACK_ALIGNMENT, previous_unpack_alignment);
395 g_object_unref(G_OBJECT(layout));
398 void gtk_glwidget_print_char(char s)
403 gtk_glwidget_print_string(str);