]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/glwidget.cpp
Updating notes on regression tests. They are "mostly sort of fixed". Won't
[xonotic/netradiant.git] / radiant / glwidget.cpp
1 /*
2 Copyright (c) 2001, Loki software, inc.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
10
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.
14
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
17 written permission.
18
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.
29 */
30
31 // OpenGL widget based on GtkGLExt
32
33 #include "stdafx.h"
34
35 #include <gtk/gtkgl.h>
36
37 #ifndef _WIN32
38 #include <pango/pangoft2.h>
39 #endif
40
41 #include "glwidget.h"
42 #include "qgl.h"
43
44 typedef int* attribs_t;
45 typedef const attribs_t* configs_iterator;
46
47 int config_rgba32[] = {
48   GDK_GL_RGBA,
49   GDK_GL_DOUBLEBUFFER,
50   GDK_GL_RED_SIZE, 8,
51   GDK_GL_BLUE_SIZE, 8,
52   GDK_GL_GREEN_SIZE, 8,
53   GDK_GL_ALPHA_SIZE, 8,
54   GDK_GL_ATTRIB_LIST_NONE,
55 };
56
57 int config_rgba[] = {
58   GDK_GL_RGBA,
59   GDK_GL_DOUBLEBUFFER,
60   GDK_GL_RED_SIZE, 1,
61   GDK_GL_BLUE_SIZE, 1,
62   GDK_GL_GREEN_SIZE, 1,
63   GDK_GL_ALPHA_SIZE, 1,
64   GDK_GL_ATTRIB_LIST_NONE,
65 };
66
67 const attribs_t configs[] = {
68   config_rgba32,
69   config_rgba,
70 };
71
72 GdkGLConfig* glconfig_new()
73 {
74   GdkGLConfig* glconfig = NULL;
75
76   for(configs_iterator i = configs, end = configs + 2; i != end && glconfig == NULL; ++i)
77   {
78     glconfig = gdk_gl_config_new(*i);
79   }
80
81   if(glconfig == NULL)
82   {
83     return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE));
84   }
85
86   return glconfig;
87 }
88
89 int config_rgba32_depth32[] = {
90   GDK_GL_RGBA,
91   GDK_GL_DOUBLEBUFFER,
92   GDK_GL_RED_SIZE, 8,
93   GDK_GL_BLUE_SIZE, 8,
94   GDK_GL_GREEN_SIZE, 8,
95   GDK_GL_ALPHA_SIZE, 8,
96   GDK_GL_DEPTH_SIZE, 32,
97   GDK_GL_ATTRIB_LIST_NONE,
98 };
99
100 int config_rgba32_depth24[] = {
101   GDK_GL_RGBA,
102   GDK_GL_DOUBLEBUFFER,
103   GDK_GL_RED_SIZE, 8,
104   GDK_GL_BLUE_SIZE, 8,
105   GDK_GL_GREEN_SIZE, 8,
106   GDK_GL_ALPHA_SIZE, 8,
107   GDK_GL_DEPTH_SIZE, 24,
108   GDK_GL_ATTRIB_LIST_NONE,
109 };
110
111 int config_rgba32_depth16[] = {
112   GDK_GL_RGBA,
113   GDK_GL_DOUBLEBUFFER,
114   GDK_GL_RED_SIZE, 8,
115   GDK_GL_BLUE_SIZE, 8,
116   GDK_GL_GREEN_SIZE, 8,
117   GDK_GL_ALPHA_SIZE, 8,
118   GDK_GL_DEPTH_SIZE, 16,
119   GDK_GL_ATTRIB_LIST_NONE,
120 };
121
122 int config_rgba32_depth[] = {
123   GDK_GL_RGBA,
124   GDK_GL_DOUBLEBUFFER,
125   GDK_GL_RED_SIZE, 8,
126   GDK_GL_BLUE_SIZE, 8,
127   GDK_GL_GREEN_SIZE, 8,
128   GDK_GL_ALPHA_SIZE, 8,
129   GDK_GL_DEPTH_SIZE, 1,
130   GDK_GL_ATTRIB_LIST_NONE,
131 };
132
133 int config_rgba_depth16[] = {
134   GDK_GL_RGBA,
135   GDK_GL_DOUBLEBUFFER,
136   GDK_GL_RED_SIZE, 1,
137   GDK_GL_BLUE_SIZE, 1,
138   GDK_GL_GREEN_SIZE, 1,
139   GDK_GL_ALPHA_SIZE, 1,
140   GDK_GL_DEPTH_SIZE, 16,
141   GDK_GL_ATTRIB_LIST_NONE,
142 };
143
144 int config_rgba_depth[] = {
145   GDK_GL_RGBA,
146   GDK_GL_DOUBLEBUFFER,
147   GDK_GL_RED_SIZE, 1,
148   GDK_GL_BLUE_SIZE, 1,
149   GDK_GL_GREEN_SIZE, 1,
150   GDK_GL_ALPHA_SIZE, 1,
151   GDK_GL_DEPTH_SIZE, 1,
152   GDK_GL_ATTRIB_LIST_NONE,
153 };
154
155 const attribs_t configs_with_depth[] =
156 {
157   config_rgba32_depth32,
158   config_rgba32_depth24,
159   config_rgba32_depth16,
160   config_rgba32_depth,
161   config_rgba_depth16,
162   config_rgba_depth,
163 };
164
165 GdkGLConfig* glconfig_new_with_depth()
166 {
167   GdkGLConfig* glconfig = NULL;
168
169   for(configs_iterator i = configs_with_depth, end = configs_with_depth + 6; i != end && glconfig == NULL; ++i)
170   {
171     glconfig = gdk_gl_config_new(*i);
172   }
173
174   if(glconfig == NULL)
175   {
176     return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE | GDK_GL_MODE_DEPTH));
177   }
178
179   return glconfig;
180 }
181
182 GtkWidget* WINAPI gtk_glwidget_new (gboolean zbuffer, GtkWidget* share)
183 {
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;
187
188   gtk_widget_set_gl_capability (drawing_area, glconfig, shared_context, TRUE, GDK_GL_RGBA_TYPE);
189
190   return drawing_area;
191 }
192
193 void WINAPI gtk_glwidget_destroy_context (GtkWidget *widget)
194 {
195 }
196
197 void WINAPI gtk_glwidget_create_context (GtkWidget *widget)
198 {
199 }
200
201 void WINAPI gtk_glwidget_swap_buffers (GtkWidget *widget)
202 {
203   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
204   gdk_gl_drawable_swap_buffers (gldrawable);
205 }
206
207 gboolean WINAPI gtk_glwidget_make_current (GtkWidget *widget)
208 {
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);
212 }
213
214
215 #ifdef _WIN32
216
217 GLuint font_list_base;
218 static gchar font_string[] = "courier 8";
219 static gint font_height;
220 static int font_created = 0;
221
222 #else
223
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;
234
235 #endif
236
237
238 // Units are pixels.  Returns a positive value [most likely].
239 int gtk_glwidget_font_ascent()
240 {
241 #ifdef _WIN32
242
243   return 6; // Approximation.
244
245 #else
246
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");
250   }
251
252   return font_ascent;
253
254 #endif
255 }
256
257 // Units are pixels.  Returns a positive value [most likely].
258 int gtk_glwidget_font_descent()
259 {
260 #ifdef _WIN32
261
262   return 0; // Approximation.
263
264 #else
265
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");
269   }
270
271   return font_descent;
272
273 #endif
274 }
275
276 #ifdef _WIN32
277
278 void gtk_glwidget_create_font_win_internal()
279 {
280   if (font_created) return;
281   font_created = 1;
282
283   PangoFontDescription *font_desc;
284   PangoFont *font;
285   PangoFontMetrics *font_metrics;
286
287   font_list_base = qglGenLists (256);
288
289   font_desc = pango_font_description_from_string (font_string);
290
291   font = gdk_gl_font_use_pango_font (font_desc, 0, 256, font_list_base);
292
293   if(font != NULL)
294   {
295     font_metrics = pango_font_get_metrics (font, NULL);
296
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);
300
301     pango_font_metrics_unref (font_metrics);
302   }
303
304   pango_font_description_free (font_desc);
305 }
306
307 #endif
308
309 void gtk_glwidget_create_font()
310 {
311 #ifdef _WIN32
312
313   // Do nothing.
314
315 #else
316
317   PangoFontDescription *font_desc;
318   PangoLayout *layout;
319   PangoRectangle log_rect;
320   int font_ascent_pango_units;
321   int font_descent_pango_units;
322
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");
326   }
327   _debug_font_created = 1;
328
329   // This call is deprecated so we'll have to fix it sometime.
330   ft2_context = pango_ft2_get_context(72, 72);
331
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);
336
337   layout = pango_layout_new(ft2_context);
338
339 #ifdef _WIN32
340
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);
345
346 #else
347
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);
354 #else
355   font_ascent_pango_units = pango_layout_get_baseline(layout);
356 #endif
357
358 #endif
359
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;
363
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;
367
368 #endif
369 }
370
371 void gtk_glwidget_destroy_font()
372 {
373 #ifdef _WIN32
374
375   // Do nothing.
376
377 #else
378
379   if (!_debug_font_created) {
380     Error("Programming error: gtk_glwidget_destroy_font() called when font "
381           "does not exist");
382   }
383
384   font_ascent = -1;
385   font_descent = -1;
386   y_offset_bitmap_render_pango_units = -1;
387   g_object_unref(G_OBJECT(ft2_context));
388   _debug_font_created = 0;
389
390 #endif
391 }
392
393
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)
405 {
406 #ifdef _WIN32
407
408   gtk_glwidget_create_font_win_internal();
409   qglListBase(font_list_base);
410   qglCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s);
411
412 #else
413
414   // The idea for this code initially came from the font-pangoft2.c example that comes with GtkGLExt.
415
416   PangoLayout *layout;
417   PangoRectangle log_rect;
418   FT_Bitmap bitmap;
419   unsigned char *begin_bitmap_buffer;
420   GLfloat color[4];
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;
429
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");
433   }
434
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);
439
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);
452
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);
463
464     qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
465     qglEnable(GL_BLEND);
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]);
471
472     qglDrawPixels(bitmap.width, bitmap.rows,
473                   GL_ALPHA, GL_UNSIGNED_BYTE, begin_bitmap_buffer);
474     g_free(begin_bitmap_buffer);
475
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);
484   }
485
486   g_object_unref(G_OBJECT(layout));
487
488 #endif
489 }
490
491 void gtk_glwidget_print_char(char s)
492 {
493 #ifdef _WIN32
494
495   gtk_glwidget_create_font_win_internal();
496   qglListBase(font_list_base);
497   qglCallLists(1, GL_UNSIGNED_BYTE, (unsigned char *) &s);
498
499 #else
500
501   char str[2];
502   str[0] = s;
503   str[1] = '\0';
504   gtk_glwidget_print_string(str);
505
506 #endif
507 }