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 glBitmap().
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);
271 PangoLayoutIter *iter;
272 iter = pango_layout_get_iter(layout);
273 font_ascent_pango_units = pango_layout_iter_get_baseline(iter);
274 pango_layout_iter_free(iter);
278 // I don't believe that's standard preprocessor syntax?
279 #if !PANGO_VERSION_CHECK(1,22,0)
280 PangoLayoutIter *iter;
281 iter = pango_layout_get_iter(layout);
282 font_ascent_pango_units = pango_layout_iter_get_baseline(iter);
283 pango_layout_iter_free(iter);
285 font_ascent_pango_units = pango_layout_get_baseline(layout);
290 pango_layout_get_extents(layout, NULL, &log_rect);
291 g_object_unref(G_OBJECT(layout));
292 font_descent_pango_units = log_rect.height - font_ascent_pango_units;
294 font_ascent = PANGO_PIXELS_CEIL(font_ascent_pango_units);
295 font_descent = PANGO_PIXELS_CEIL(font_descent_pango_units);
296 y_offset_bitmap_render_pango_units = (font_ascent * PANGO_SCALE) - font_ascent_pango_units;
299 void gtk_glwidget_destroy_font()
301 if (!_debug_font_created) {
302 Error("Programming error: gtk_glwidget_destroy_font() called when font "
308 y_offset_bitmap_render_pango_units = -1;
309 g_object_unref(G_OBJECT(ft2_context));
310 _debug_font_created = 0;
314 // Renders the input text at the current location with the current color.
315 // The X position of the current location is used to place the left edge of the text image,
316 // where the text image bounds are defined as the logical extents of the line of text.
317 // The Y position of the current location is used to place the bottom of the text image.
318 // You should offset the Y position by the amount returned by gtk_glwidget_font_descent()
319 // if you want to place the baseline of the text image at the current Y position.
320 // Note: A problem with this function is that if the lower left corner of the text falls
321 // just a hair outside of the viewport (meaning the current raster position is invalid),
322 // then no text will be rendered. The solution to this is a very hacky one. You can search
323 // Google for "glDrawPixels clipping".
324 void gtk_glwidget_print_string(const char *s)
326 // The idea for this code initially came from the font-pangoft2.c example that comes with GtkGLExt.
329 PangoRectangle log_rect;
331 unsigned char *begin_bitmap_buffer;
333 GLint previous_unpack_alignment;
334 GLboolean previous_blend_enabled;
335 GLint previous_blend_func_src;
336 GLint previous_blend_func_dst;
337 GLfloat previous_red_bias;
338 GLfloat previous_green_bias;
339 GLfloat previous_blue_bias;
340 GLfloat previous_alpha_scale;
342 if (!_debug_font_created) {
343 Error("Programming error: gtk_glwidget_print_string() called but font does not exist; "
344 "you should have called gtk_glwidget_create_font() first");
347 layout = pango_layout_new(ft2_context);
348 pango_layout_set_width(layout, -1); // -1 no wrapping. All text on one line.
349 pango_layout_set_text(layout, s, -1); // -1 null-terminated string.
350 pango_layout_get_extents(layout, NULL, &log_rect);
352 if (log_rect.width > 0 && log_rect.height > 0) {
353 bitmap.rows = font_ascent + font_descent;
354 bitmap.width = PANGO_PIXELS_CEIL(log_rect.width);
355 bitmap.pitch = -bitmap.width; // Rendering it "upside down" for OpenGL.
356 begin_bitmap_buffer = (unsigned char *) g_malloc(bitmap.rows * bitmap.width);
357 memset(begin_bitmap_buffer, 0, bitmap.rows * bitmap.width);
358 bitmap.buffer = begin_bitmap_buffer + (bitmap.rows - 1) * bitmap.width; // See pitch above.
359 bitmap.num_grays = 0xff;
360 bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
361 pango_ft2_render_layout_subpixel(&bitmap, layout, -log_rect.x,
362 y_offset_bitmap_render_pango_units);
363 qglGetFloatv(GL_CURRENT_COLOR, color);
365 // Save state. I didn't see any OpenGL push/pop operations for these.
366 // Question: Is saving/restoring this state necessary? Being safe.
367 qglGetIntegerv(GL_UNPACK_ALIGNMENT, &previous_unpack_alignment);
368 previous_blend_enabled = qglIsEnabled(GL_BLEND);
369 qglGetIntegerv(GL_BLEND_SRC, &previous_blend_func_src);
370 qglGetIntegerv(GL_BLEND_DST, &previous_blend_func_dst);
371 qglGetFloatv(GL_RED_BIAS, &previous_red_bias);
372 qglGetFloatv(GL_GREEN_BIAS, &previous_green_bias);
373 qglGetFloatv(GL_BLUE_BIAS, &previous_blue_bias);
374 qglGetFloatv(GL_ALPHA_SCALE, &previous_alpha_scale);
376 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
378 qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
379 qglPixelTransferf(GL_RED_BIAS, color[0]);
380 qglPixelTransferf(GL_GREEN_BIAS, color[1]);
381 qglPixelTransferf(GL_BLUE_BIAS, color[2]);
382 qglPixelTransferf(GL_ALPHA_SCALE, color[3]);
384 qglDrawPixels(bitmap.width, bitmap.rows,
385 GL_ALPHA, GL_UNSIGNED_BYTE, begin_bitmap_buffer);
386 g_free(begin_bitmap_buffer);
388 // Restore state in reverse order of how we set it.
389 qglPixelTransferf(GL_ALPHA_SCALE, previous_alpha_scale);
390 qglPixelTransferf(GL_BLUE_BIAS, previous_blue_bias);
391 qglPixelTransferf(GL_GREEN_BIAS, previous_green_bias);
392 qglPixelTransferf(GL_RED_BIAS, previous_red_bias);
393 qglBlendFunc(previous_blend_func_src, previous_blend_func_dst);
394 if (!previous_blend_enabled) { qglDisable(GL_BLEND); }
395 qglPixelStorei(GL_UNPACK_ALIGNMENT, previous_unpack_alignment);
398 g_object_unref(G_OBJECT(layout));
401 void gtk_glwidget_print_char(char s)
406 gtk_glwidget_print_string(str);