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(){
71 GdkGLConfig* glconfig = NULL;
73 for ( configs_iterator i = configs, end = configs + 2; i != end && glconfig == NULL; ++i )
75 glconfig = gdk_gl_config_new( *i );
78 if ( glconfig == NULL ) {
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(){
162 GdkGLConfig* glconfig = NULL;
164 for ( configs_iterator i = configs_with_depth, end = configs_with_depth + 6; i != end && glconfig == NULL; ++i )
166 glconfig = gdk_gl_config_new( *i );
169 if ( glconfig == NULL ) {
170 return gdk_gl_config_new_by_mode( (GdkGLConfigMode)( GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE | GDK_GL_MODE_DEPTH ) );
176 GtkWidget* WINAPI gtk_glwidget_new( gboolean zbuffer, GtkWidget* share ){
177 GtkWidget* drawing_area = gtk_drawing_area_new();
178 GdkGLConfig* glconfig = ( zbuffer ) ? glconfig_new_with_depth() : glconfig_new();
179 GdkGLContext* shared_context = ( share ) ? gtk_widget_get_gl_context( share ) : NULL;
181 gtk_widget_set_gl_capability( drawing_area, glconfig, shared_context, TRUE, GDK_GL_RGBA_TYPE );
186 void WINAPI gtk_glwidget_destroy_context( GtkWidget *widget ){
189 void WINAPI gtk_glwidget_create_context( GtkWidget *widget ){
192 void WINAPI gtk_glwidget_swap_buffers( GtkWidget *widget ){
193 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable( widget );
194 gdk_gl_drawable_swap_buffers( gldrawable );
197 gboolean WINAPI gtk_glwidget_make_current( GtkWidget *widget ){
198 GdkGLContext *glcontext = gtk_widget_get_gl_context( widget );
199 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable( widget );
200 return gdk_gl_drawable_gl_begin( gldrawable, glcontext );
204 // Think about rewriting this font stuff to use OpenGL display lists and glBitmap().
205 // Bit maps together with display lists may offer a performance increase, but
206 // they would not allow antialiased fonts.
208 static const char font_string[] = "Courier bold";
209 static const int font_height = 9;
211 static const char font_string[] = "Monospace";
212 static const int font_height = 10;
214 static int font_ascent = -1;
215 static int font_descent = -1;
216 static int y_offset_bitmap_render_pango_units = -1;
217 static PangoContext *ft2_context = NULL;
218 static int _debug_font_created = 0;
221 // Units are pixels. Returns a positive value [most likely].
222 int gtk_glwidget_font_ascent(){
223 if ( !_debug_font_created ) {
224 Error( "Programming error: gtk_glwidget_font_ascent() called but font does not exist; "
225 "you should have called gtk_glwidget_create_font() first" );
231 // Units are pixels. Returns a positive value [most likely].
232 int gtk_glwidget_font_descent(){
233 if ( !_debug_font_created ) {
234 Error( "Programming error: gtk_glwidget_font_descent() called but font does not exist; "
235 "you should have called gtk_glwidget_create_font() first" );
242 void gtk_glwidget_create_font(){
243 PangoFontDescription *font_desc;
245 PangoRectangle log_rect;
246 int font_ascent_pango_units;
247 int font_descent_pango_units;
249 if ( _debug_font_created ) {
250 Error( "Programming error: gtk_glwidget_create_font() was already called; "
251 "you must call gtk_glwidget_destroy_font() before creating font again" );
253 _debug_font_created = 1;
255 // This call is deprecated so we'll have to fix it sometime.
256 ft2_context = pango_ft2_get_context( 72, 72 );
258 font_desc = pango_font_description_from_string( font_string );
259 pango_font_description_set_size( font_desc, font_height * PANGO_SCALE );
260 pango_context_set_font_description( ft2_context, font_desc );
261 pango_font_description_free( font_desc );
263 layout = pango_layout_new( ft2_context );
265 // I don't believe that's standard preprocessor syntax?
266 #if !PANGO_VERSION_CHECK( 1,22,0 )
267 PangoLayoutIter *iter;
268 iter = pango_layout_get_iter( layout );
269 font_ascent_pango_units = pango_layout_iter_get_baseline( iter );
270 pango_layout_iter_free( iter );
272 font_ascent_pango_units = pango_layout_get_baseline( layout );
275 pango_layout_get_extents( layout, NULL, &log_rect );
276 g_object_unref( G_OBJECT( layout ) );
277 font_descent_pango_units = log_rect.height - font_ascent_pango_units;
279 font_ascent = PANGO_PIXELS_CEIL( font_ascent_pango_units );
280 font_descent = PANGO_PIXELS_CEIL( font_descent_pango_units );
281 y_offset_bitmap_render_pango_units = ( font_ascent * PANGO_SCALE ) - font_ascent_pango_units;
284 void gtk_glwidget_destroy_font(){
285 if ( !_debug_font_created ) {
286 Error( "Programming error: gtk_glwidget_destroy_font() called when font "
292 y_offset_bitmap_render_pango_units = -1;
293 g_object_unref( G_OBJECT( ft2_context ) );
294 _debug_font_created = 0;
298 // Renders the input text at the current location with the current color.
299 // The X position of the current location is used to place the left edge of the text image,
300 // where the text image bounds are defined as the logical extents of the line of text.
301 // The Y position of the current location is used to place the bottom of the text image.
302 // You should offset the Y position by the amount returned by gtk_glwidget_font_descent()
303 // if you want to place the baseline of the text image at the current Y position.
304 // Note: A problem with this function is that if the lower left corner of the text falls
305 // just a hair outside of the viewport (meaning the current raster position is invalid),
306 // then no text will be rendered. The solution to this is a very hacky one. You can search
307 // Google for "glDrawPixels clipping".
308 void gtk_glwidget_print_string( const char *s ){
309 // The idea for this code initially came from the font-pangoft2.c example that comes with GtkGLExt.
312 PangoRectangle log_rect;
314 unsigned char *begin_bitmap_buffer;
316 GLint previous_unpack_alignment;
317 GLboolean previous_blend_enabled;
318 GLint previous_blend_func_src;
319 GLint previous_blend_func_dst;
320 GLfloat previous_red_bias;
321 GLfloat previous_green_bias;
322 GLfloat previous_blue_bias;
323 GLfloat previous_alpha_scale;
325 if ( !_debug_font_created ) {
326 Error( "Programming error: gtk_glwidget_print_string() called but font does not exist; "
327 "you should have called gtk_glwidget_create_font() first" );
330 layout = pango_layout_new( ft2_context );
331 pango_layout_set_width( layout, -1 ); // -1 no wrapping. All text on one line.
332 pango_layout_set_text( layout, s, -1 ); // -1 null-terminated string.
333 pango_layout_get_extents( layout, NULL, &log_rect );
335 if ( log_rect.width > 0 && log_rect.height > 0 ) {
336 bitmap.rows = font_ascent + font_descent;
337 bitmap.width = PANGO_PIXELS_CEIL( log_rect.width );
338 bitmap.pitch = -bitmap.width; // Rendering it "upside down" for OpenGL.
339 begin_bitmap_buffer = (unsigned char *) g_malloc( bitmap.rows * bitmap.width );
340 memset( begin_bitmap_buffer, 0, bitmap.rows * bitmap.width );
341 bitmap.buffer = begin_bitmap_buffer + ( bitmap.rows - 1 ) * bitmap.width; // See pitch above.
342 bitmap.num_grays = 0xff;
343 bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
344 pango_ft2_render_layout_subpixel( &bitmap, layout, -log_rect.x,
345 y_offset_bitmap_render_pango_units );
346 qglGetFloatv( GL_CURRENT_COLOR, color );
348 // Save state. I didn't see any OpenGL push/pop operations for these.
349 // Question: Is saving/restoring this state necessary? Being safe.
350 qglGetIntegerv( GL_UNPACK_ALIGNMENT, &previous_unpack_alignment );
351 previous_blend_enabled = qglIsEnabled( GL_BLEND );
352 qglGetIntegerv( GL_BLEND_SRC, &previous_blend_func_src );
353 qglGetIntegerv( GL_BLEND_DST, &previous_blend_func_dst );
354 qglGetFloatv( GL_RED_BIAS, &previous_red_bias );
355 qglGetFloatv( GL_GREEN_BIAS, &previous_green_bias );
356 qglGetFloatv( GL_BLUE_BIAS, &previous_blue_bias );
357 qglGetFloatv( GL_ALPHA_SCALE, &previous_alpha_scale );
359 qglPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
360 qglEnable( GL_BLEND );
361 qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
362 qglPixelTransferf( GL_RED_BIAS, color[0] );
363 qglPixelTransferf( GL_GREEN_BIAS, color[1] );
364 qglPixelTransferf( GL_BLUE_BIAS, color[2] );
365 qglPixelTransferf( GL_ALPHA_SCALE, color[3] );
367 qglDrawPixels( bitmap.width, bitmap.rows,
368 GL_ALPHA, GL_UNSIGNED_BYTE, begin_bitmap_buffer );
369 g_free( begin_bitmap_buffer );
371 // Restore state in reverse order of how we set it.
372 qglPixelTransferf( GL_ALPHA_SCALE, previous_alpha_scale );
373 qglPixelTransferf( GL_BLUE_BIAS, previous_blue_bias );
374 qglPixelTransferf( GL_GREEN_BIAS, previous_green_bias );
375 qglPixelTransferf( GL_RED_BIAS, previous_red_bias );
376 qglBlendFunc( previous_blend_func_src, previous_blend_func_dst );
377 if ( !previous_blend_enabled ) {
378 qglDisable( GL_BLEND );
380 qglPixelStorei( GL_UNPACK_ALIGNMENT, previous_unpack_alignment );
383 g_object_unref( G_OBJECT( layout ) );
386 void gtk_glwidget_print_char( char s ){
390 gtk_glwidget_print_string( str );