refactoring font code to hopefully be closer to be able to exchange the font system...
[divverent/netradiant.git] / libs / gtkutil / glfont.cpp
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "glfont.h"
23 #include "igl.h"
24
25 // generic string printing with call lists
26 class GLFontCallList: public GLFont
27 {
28         GLuint m_displayList;
29         int m_pixelHeight;
30         int m_pixelAscent;
31         int m_pixelDescent;
32         public:
33         GLFontCallList(GLuint displayList, int asc, int desc, int pixelHeight) : m_displayList(displayList), m_pixelHeight(pixelHeight), m_pixelAscent(asc), m_pixelDescent(desc)
34         {
35         }
36         virtual ~GLFontCallList()
37         {
38                 glDeleteLists(m_displayList, 256);
39         }
40         void printString(const char *s)
41         {
42                 GlobalOpenGL().m_glListBase(m_displayList);
43                 GlobalOpenGL().m_glCallLists(GLsizei(strlen(s)), GL_UNSIGNED_BYTE, reinterpret_cast<const GLubyte*>(s));
44         }
45         virtual int getPixelAscent() const
46         {
47                 return m_pixelAscent;
48         }
49         virtual int getPixelDescent() const
50         {
51                 return m_pixelDescent;
52         }
53         virtual int getPixelHeight() const
54         {
55                 return m_pixelHeight;
56         }
57 };
58
59 #ifdef _WIN32
60         #include <windows.h>
61 #endif
62 #include "debugging/debugging.h"
63
64 // LordHavoc: this is code for direct Xlib bitmap character fetching, as an
65 // alternative to requiring gtkglarea, it was created due to a lack of this
66 // package on SuSE 9.x but this package is now commonly shipping in Linux
67 // distributions so this code may be unnecessary, feel free however to enable
68 // it when building packages for distros that do not ship with that package,
69 // or if you just prefer less dependencies...
70 #if 0
71
72 #include <X11/Xlib.h>
73 #include <gdk/gdkx.h>
74 #include <GL/glx.h>
75
76 GLFont *glfont_create(const char* font_string)
77 {
78   GLuint font_list_base;
79   XFontStruct *fontInfo;
80   Display *dpy = GDK_DISPLAY ();
81   unsigned int i, first, last, firstrow, lastrow;
82   int maxchars;
83   int firstbitmap;
84
85   fontInfo = XLoadQueryFont (dpy, "-*-fixed-*-*-*-*-8-*-*-*-*-*-*-*");
86   if (fontInfo == NULL)
87   {
88     // try to load other fonts
89     fontInfo = XLoadQueryFont (dpy, "-*-fixed-*-*-*-*-*-*-*-*-*-*-*-*");
90
91     // any font will do !
92     if (fontInfo == NULL)
93       fontInfo = XLoadQueryFont(dpy, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*");
94
95     if (fontInfo == NULL)
96       ERROR_MESSAGE("couldn't create font");
97   }
98
99   first = (int)fontInfo->min_char_or_byte2;
100   last = (int)fontInfo->max_char_or_byte2;
101   firstrow = (int)fontInfo->min_byte1;
102   lastrow = (int)fontInfo->max_byte1;
103   /*
104    * How many chars in the charset
105    */
106   maxchars = 256 * lastrow + last;
107   font_list_base = glGenLists(maxchars+1);
108   if (font_list_base == 0)
109   {
110     ERROR_MESSAGE( "couldn't create font" );
111   }
112
113   /*
114    * Get offset to first char in the charset
115    */
116   firstbitmap = 256 * firstrow + first;
117   /*
118    * for each row of chars, call glXUseXFont to build the bitmaps.
119    */
120
121   for(i=firstrow; i<=lastrow; i++)
122   {
123     glXUseXFont(fontInfo->fid, firstbitmap, last-first+1, font_list_base+firstbitmap);
124     firstbitmap += 256;
125   }
126
127 /*    *height = fontInfo->ascent + fontInfo->descent;
128     *width = fontInfo->max_bounds.width;  */
129   return new GLFontCallList(font_list_base, fontInfo->ascent, fontInfo->descent, fontInfo->ascent + fontInfo->descent);
130 }
131
132 #else
133
134 #include <gtk/gtkglwidget.h>
135
136 GLFont *glfont_create(const char* font_string)
137 {
138   GLuint font_list_base = glGenLists (256);
139   gint font_height = 0, font_ascent = 0, font_descent = 0;
140
141   PangoFontDescription* font_desc = pango_font_description_from_string (font_string);
142
143   PangoFont* font = gdk_gl_font_use_pango_font (font_desc, 0, 256, font_list_base);
144
145   if(font == 0)
146   {
147           pango_font_description_free (font_desc);
148           font_desc = pango_font_description_from_string ("fixed 8");
149           font = gdk_gl_font_use_pango_font (font_desc, 0, 256, font_list_base);
150   }
151
152   if(font == 0)
153   {
154           pango_font_description_free (font_desc);
155           font_desc = pango_font_description_from_string ("courier new 8");
156           font = gdk_gl_font_use_pango_font (font_desc, 0, 256, font_list_base);
157   }
158
159   if(font != 0)
160   {
161     PangoFontMetrics* font_metrics = pango_font_get_metrics (font, 0);
162
163     font_ascent = pango_font_metrics_get_ascent (font_metrics);
164         font_descent = pango_font_metrics_get_descent (font_metrics);
165         font_height = font_ascent + font_descent;
166
167     font_ascent = PANGO_PIXELS (font_ascent);
168     font_descent = PANGO_PIXELS (font_descent);
169     font_height = PANGO_PIXELS (font_height);
170
171     pango_font_metrics_unref (font_metrics);
172   }
173
174   pango_font_description_free (font_desc);
175
176   // fix for pango/gtkglext metrix bug
177   if(font_height > 256)
178           font_height = 16;
179
180   return new GLFontCallList(font_list_base, font_ascent, font_descent, font_height);
181 }
182 #endif
183
184 // new font code ripped from ZeroRadiant (not in use yet)
185
186 #include <pango/pangoft2.h>
187 #include <pango/pango-utils.h>
188
189 class GLFontInternal: public GLFont
190 {
191         const char *font_string;
192         int font_height;
193         int font_ascent;
194         int font_descent;
195         int y_offset_bitmap_render_pango_units;
196         PangoContext *ft2_context;
197         PangoFontMap *fontmap;
198
199         public:
200         GLFontInternal(const char *_font_string): font_string(_font_string)
201         {
202                 PangoFontDescription *font_desc;
203                 PangoLayout *layout;
204                 PangoRectangle log_rect;
205                 int font_ascent_pango_units;
206                 int font_descent_pango_units;
207
208 #if !PANGO_VERSION_CHECK(1,22,0)
209                 ft2_context = pango_ft2_get_context(72, 72);
210 #else
211                 fontmap = pango_ft2_font_map_new();
212                 pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(fontmap), 72, 72);
213                 ft2_context = pango_font_map_create_context(fontmap);
214 #endif
215
216                 font_desc = pango_font_description_from_string(font_string);
217                 //pango_font_description_set_size(font_desc, 10 * PANGO_SCALE);
218                 pango_context_set_font_description(ft2_context, font_desc);
219                 pango_font_description_free(font_desc);
220                 // TODO fallback to fixed 8, courier new 8
221
222                 layout = pango_layout_new(ft2_context);
223 #if !PANGO_VERSION_CHECK(1,22,0)
224                 PangoLayoutIter *iter;  
225                 iter = pango_layout_get_iter(layout);
226                 font_ascent_pango_units = pango_layout_iter_get_baseline(iter);
227                 pango_layout_iter_free(iter);
228 #else
229                 font_ascent_pango_units = pango_layout_get_baseline(layout);
230 #endif
231                 pango_layout_get_extents(layout, NULL, &log_rect);
232                 g_object_unref(G_OBJECT(layout));
233                 font_descent_pango_units = log_rect.height - font_ascent_pango_units;
234
235                 font_ascent = PANGO_PIXELS_CEIL(font_ascent_pango_units);
236                 font_descent = PANGO_PIXELS_CEIL(font_descent_pango_units);
237                 font_height = font_ascent + font_descent;
238                 y_offset_bitmap_render_pango_units = (font_ascent * PANGO_SCALE) - font_ascent_pango_units;
239         }
240
241         virtual ~GLFontInternal()
242         {
243                 g_object_unref(G_OBJECT(ft2_context));
244                 g_object_unref(G_OBJECT(fontmap));
245         }
246
247         // Renders the input text at the current location with the current color.
248         // The X position of the current location is used to place the left edge of the text image,
249         // where the text image bounds are defined as the logical extents of the line of text.
250         // The Y position of the current location is used to place the bottom of the text image.
251         // You should offset the Y position by the amount returned by gtk_glwidget_font_descent()
252         // if you want to place the baseline of the text image at the current Y position.
253         // Note: A problem with this function is that if the lower left corner of the text falls
254         // just a hair outside of the viewport (meaning the current raster position is invalid),
255         // then no text will be rendered.  The solution to this is a very hacky one.  You can search
256         // Google for "glDrawPixels clipping".
257         virtual void printString(const char *s)
258         {
259                 // The idea for this code initially came from the font-pangoft2.c example that comes with GtkGLExt.
260
261                 PangoLayout *layout;
262                 PangoRectangle log_rect;
263                 FT_Bitmap bitmap;
264                 unsigned char *begin_bitmap_buffer;
265                 GLfloat color[4];
266                 GLint previous_unpack_alignment;
267                 GLboolean previous_blend_enabled;
268                 GLint previous_blend_func_src;
269                 GLint previous_blend_func_dst;
270                 GLfloat previous_red_bias;
271                 GLfloat previous_green_bias;
272                 GLfloat previous_blue_bias;
273                 GLfloat previous_alpha_scale;
274
275                 layout = pango_layout_new(ft2_context);
276                 pango_layout_set_width(layout, -1); // -1 no wrapping.  All text on one line.
277                 pango_layout_set_text(layout, s, -1); // -1 null-terminated string.
278                 pango_layout_get_extents(layout, NULL, &log_rect);
279
280                 if (log_rect.width > 0 && log_rect.height > 0) {
281                         bitmap.rows = font_ascent + font_descent;
282                         bitmap.width = PANGO_PIXELS_CEIL(log_rect.width);
283                         bitmap.pitch = -bitmap.width; // Rendering it "upside down" for OpenGL.
284                         begin_bitmap_buffer = (unsigned char *) g_malloc(bitmap.rows * bitmap.width);
285                         memset(begin_bitmap_buffer, 0, bitmap.rows * bitmap.width);
286                         bitmap.buffer = begin_bitmap_buffer + (bitmap.rows - 1) * bitmap.width; // See pitch above.
287                         bitmap.num_grays = 0xff;
288                         bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
289                         pango_ft2_render_layout_subpixel(&bitmap, layout, -log_rect.x,
290                                         y_offset_bitmap_render_pango_units);
291                         GlobalOpenGL().m_glGetFloatv(GL_CURRENT_COLOR, color);
292
293                         // Save state.  I didn't see any OpenGL push/pop operations for these.
294                         // Question: Is saving/restoring this state necessary?  Being safe.
295                         GlobalOpenGL().m_glGetIntegerv(GL_UNPACK_ALIGNMENT, &previous_unpack_alignment);
296                         previous_blend_enabled = GlobalOpenGL().m_glIsEnabled(GL_BLEND);
297                         GlobalOpenGL().m_glGetIntegerv(GL_BLEND_SRC, &previous_blend_func_src);
298                         GlobalOpenGL().m_glGetIntegerv(GL_BLEND_DST, &previous_blend_func_dst);
299                         GlobalOpenGL().m_glGetFloatv(GL_RED_BIAS, &previous_red_bias);
300                         GlobalOpenGL().m_glGetFloatv(GL_GREEN_BIAS, &previous_green_bias);
301                         GlobalOpenGL().m_glGetFloatv(GL_BLUE_BIAS, &previous_blue_bias);
302                         GlobalOpenGL().m_glGetFloatv(GL_ALPHA_SCALE, &previous_alpha_scale);
303
304                         GlobalOpenGL().m_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
305                         GlobalOpenGL().m_glEnable(GL_BLEND);
306                         GlobalOpenGL().m_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
307                         GlobalOpenGL().m_glPixelTransferf(GL_RED_BIAS, color[0]);
308                         GlobalOpenGL().m_glPixelTransferf(GL_GREEN_BIAS, color[1]);
309                         GlobalOpenGL().m_glPixelTransferf(GL_BLUE_BIAS, color[2]);
310                         GlobalOpenGL().m_glPixelTransferf(GL_ALPHA_SCALE, color[3]);
311
312                         GlobalOpenGL().m_glDrawPixels(bitmap.width, bitmap.rows,
313                                         GL_ALPHA, GL_UNSIGNED_BYTE, begin_bitmap_buffer);
314                         g_free(begin_bitmap_buffer);
315
316                         // Restore state in reverse order of how we set it.
317                         GlobalOpenGL().m_glPixelTransferf(GL_ALPHA_SCALE, previous_alpha_scale);
318                         GlobalOpenGL().m_glPixelTransferf(GL_BLUE_BIAS, previous_blue_bias);
319                         GlobalOpenGL().m_glPixelTransferf(GL_GREEN_BIAS, previous_green_bias);
320                         GlobalOpenGL().m_glPixelTransferf(GL_RED_BIAS, previous_red_bias);
321                         GlobalOpenGL().m_glBlendFunc(previous_blend_func_src, previous_blend_func_dst);
322                         if (!previous_blend_enabled) { GlobalOpenGL().m_glDisable(GL_BLEND); }
323                         GlobalOpenGL().m_glPixelStorei(GL_UNPACK_ALIGNMENT, previous_unpack_alignment);
324                 }
325
326                 g_object_unref(G_OBJECT(layout));
327         }
328
329         virtual int getPixelAscent() const
330         {
331                 return font_ascent;
332         }
333         virtual int getPixelDescent() const
334         {
335                 return font_descent;
336         }
337         virtual int getPixelHeight() const
338         {
339                 return font_height;
340         }
341 };