]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/glwidget.cpp
Adding patch_seam q3map2 regression test. Probably not fixable, but good to
[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 #include <pango/pangoft2.h>
38
39 #include "glwidget.h"
40 #include "qgl.h"
41
42 typedef int* attribs_t;
43 typedef const attribs_t* configs_iterator;
44
45 int config_rgba32[] = {
46   GDK_GL_RGBA,
47   GDK_GL_DOUBLEBUFFER,
48   GDK_GL_RED_SIZE, 8,
49   GDK_GL_BLUE_SIZE, 8,
50   GDK_GL_GREEN_SIZE, 8,
51   GDK_GL_ALPHA_SIZE, 8,
52   GDK_GL_ATTRIB_LIST_NONE,
53 };
54
55 int config_rgba[] = {
56   GDK_GL_RGBA,
57   GDK_GL_DOUBLEBUFFER,
58   GDK_GL_RED_SIZE, 1,
59   GDK_GL_BLUE_SIZE, 1,
60   GDK_GL_GREEN_SIZE, 1,
61   GDK_GL_ALPHA_SIZE, 1,
62   GDK_GL_ATTRIB_LIST_NONE,
63 };
64
65 const attribs_t configs[] = {
66   config_rgba32,
67   config_rgba,
68 };
69
70 GdkGLConfig* glconfig_new()
71 {
72   GdkGLConfig* glconfig = NULL;
73
74   for(configs_iterator i = configs, end = configs + 2; i != end && glconfig == NULL; ++i)
75   {
76     glconfig = gdk_gl_config_new(*i);
77   }
78
79   if(glconfig == NULL)
80   {
81     return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE));
82   }
83
84   return glconfig;
85 }
86
87 int config_rgba32_depth32[] = {
88   GDK_GL_RGBA,
89   GDK_GL_DOUBLEBUFFER,
90   GDK_GL_RED_SIZE, 8,
91   GDK_GL_BLUE_SIZE, 8,
92   GDK_GL_GREEN_SIZE, 8,
93   GDK_GL_ALPHA_SIZE, 8,
94   GDK_GL_DEPTH_SIZE, 32,
95   GDK_GL_ATTRIB_LIST_NONE,
96 };
97
98 int config_rgba32_depth24[] = {
99   GDK_GL_RGBA,
100   GDK_GL_DOUBLEBUFFER,
101   GDK_GL_RED_SIZE, 8,
102   GDK_GL_BLUE_SIZE, 8,
103   GDK_GL_GREEN_SIZE, 8,
104   GDK_GL_ALPHA_SIZE, 8,
105   GDK_GL_DEPTH_SIZE, 24,
106   GDK_GL_ATTRIB_LIST_NONE,
107 };
108
109 int config_rgba32_depth16[] = {
110   GDK_GL_RGBA,
111   GDK_GL_DOUBLEBUFFER,
112   GDK_GL_RED_SIZE, 8,
113   GDK_GL_BLUE_SIZE, 8,
114   GDK_GL_GREEN_SIZE, 8,
115   GDK_GL_ALPHA_SIZE, 8,
116   GDK_GL_DEPTH_SIZE, 16,
117   GDK_GL_ATTRIB_LIST_NONE,
118 };
119
120 int config_rgba32_depth[] = {
121   GDK_GL_RGBA,
122   GDK_GL_DOUBLEBUFFER,
123   GDK_GL_RED_SIZE, 8,
124   GDK_GL_BLUE_SIZE, 8,
125   GDK_GL_GREEN_SIZE, 8,
126   GDK_GL_ALPHA_SIZE, 8,
127   GDK_GL_DEPTH_SIZE, 1,
128   GDK_GL_ATTRIB_LIST_NONE,
129 };
130
131 int config_rgba_depth16[] = {
132   GDK_GL_RGBA,
133   GDK_GL_DOUBLEBUFFER,
134   GDK_GL_RED_SIZE, 1,
135   GDK_GL_BLUE_SIZE, 1,
136   GDK_GL_GREEN_SIZE, 1,
137   GDK_GL_ALPHA_SIZE, 1,
138   GDK_GL_DEPTH_SIZE, 16,
139   GDK_GL_ATTRIB_LIST_NONE,
140 };
141
142 int config_rgba_depth[] = {
143   GDK_GL_RGBA,
144   GDK_GL_DOUBLEBUFFER,
145   GDK_GL_RED_SIZE, 1,
146   GDK_GL_BLUE_SIZE, 1,
147   GDK_GL_GREEN_SIZE, 1,
148   GDK_GL_ALPHA_SIZE, 1,
149   GDK_GL_DEPTH_SIZE, 1,
150   GDK_GL_ATTRIB_LIST_NONE,
151 };
152
153 const attribs_t configs_with_depth[] =
154 {
155   config_rgba32_depth32,
156   config_rgba32_depth24,
157   config_rgba32_depth16,
158   config_rgba32_depth,
159   config_rgba_depth16,
160   config_rgba_depth,
161 };
162
163 GdkGLConfig* glconfig_new_with_depth()
164 {
165   GdkGLConfig* glconfig = NULL;
166
167   for(configs_iterator i = configs_with_depth, end = configs_with_depth + 6; i != end && glconfig == NULL; ++i)
168   {
169     glconfig = gdk_gl_config_new(*i);
170   }
171
172   if(glconfig == NULL)
173   {
174     return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE | GDK_GL_MODE_DEPTH));
175   }
176
177   return glconfig;
178 }
179
180 GtkWidget* WINAPI gtk_glwidget_new (gboolean zbuffer, GtkWidget* share)
181 {
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;
185
186   gtk_widget_set_gl_capability (drawing_area, glconfig, shared_context, TRUE, GDK_GL_RGBA_TYPE);
187
188   return drawing_area;
189 }
190
191 void WINAPI gtk_glwidget_destroy_context (GtkWidget *widget)
192 {
193 }
194
195 void WINAPI gtk_glwidget_create_context (GtkWidget *widget)
196 {
197 }
198
199 void WINAPI gtk_glwidget_swap_buffers (GtkWidget *widget)
200 {
201   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
202   gdk_gl_drawable_swap_buffers (gldrawable);
203 }
204
205 gboolean WINAPI gtk_glwidget_make_current (GtkWidget *widget)
206 {
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);
210 }
211
212
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.
216 #ifdef _WIN32
217 static const char font_string[] = "Courier bold";
218 static const int font_height = 9;
219 #else
220 static const char font_string[] = "Monospace";
221 static const int font_height = 10;
222 #endif
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;
228
229
230 // Units are pixels.  Returns a positive value [most likely].
231 int gtk_glwidget_font_ascent()
232 {
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");
236   }
237
238   return font_ascent;
239 }
240
241 // Units are pixels.  Returns a positive value [most likely].
242 int gtk_glwidget_font_descent()
243 {
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");
247   }
248
249   return font_descent;
250 }
251
252
253 void gtk_glwidget_create_font()
254 {
255   PangoFontDescription *font_desc;
256   PangoLayout *layout;
257   PangoRectangle log_rect;
258   int font_ascent_pango_units;
259   int font_descent_pango_units;
260
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");
264   }
265   _debug_font_created = 1;
266
267   // This call is deprecated so we'll have to fix it sometime.
268   ft2_context = pango_ft2_get_context(72, 72);
269
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);
274
275   layout = pango_layout_new(ft2_context);
276
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);
283 #else
284   font_ascent_pango_units = pango_layout_get_baseline(layout);
285 #endif
286
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;
290
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;
294 }
295
296 void gtk_glwidget_destroy_font()
297 {
298   if (!_debug_font_created) {
299     Error("Programming error: gtk_glwidget_destroy_font() called when font "
300           "does not exist");
301   }
302
303   font_ascent = -1;
304   font_descent = -1;
305   y_offset_bitmap_render_pango_units = -1;
306   g_object_unref(G_OBJECT(ft2_context));
307   _debug_font_created = 0;
308 }
309
310
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)
322 {
323   // The idea for this code initially came from the font-pangoft2.c example that comes with GtkGLExt.
324
325   PangoLayout *layout;
326   PangoRectangle log_rect;
327   FT_Bitmap bitmap;
328   unsigned char *begin_bitmap_buffer;
329   GLfloat color[4];
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;
338
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");
342   }
343
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);
348
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);
361
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);
372
373     qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
374     qglEnable(GL_BLEND);
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]);
380
381     qglDrawPixels(bitmap.width, bitmap.rows,
382                   GL_ALPHA, GL_UNSIGNED_BYTE, begin_bitmap_buffer);
383     g_free(begin_bitmap_buffer);
384
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);
393   }
394
395   g_object_unref(G_OBJECT(layout));
396 }
397
398 void gtk_glwidget_print_char(char s)
399 {
400   char str[2];
401   str[0] = s;
402   str[1] = '\0';
403   gtk_glwidget_print_string(str);
404 }