rnd-20030403-3-src
[rocksndiamonds.git] / src / libgame / text.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * text.c                                                   *
12 ***********************************************************/
13
14 #include <stdio.h>
15 #include <stdarg.h>
16
17 #include "text.h"
18 #include "misc.h"
19
20
21 /* ========================================================================= */
22 /* font functions                                                            */
23 /* ========================================================================= */
24
25 #define NUM_FONT_CHARS          (FONT_LINES_PER_FONT * FONT_CHARS_PER_LINE)
26
27 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
28 static GC       font_clip_gc = None;
29
30 static void InitFontClipmasks()
31 {
32   static boolean clipmasks_initialized = FALSE;
33   XGCValues clip_gc_values;
34   unsigned long clip_gc_valuemask;
35   GC copy_clipmask_gc;
36   int i, j;
37
38   if (gfx.num_fonts == 0 || gfx.font_bitmap_info[0].bitmap == NULL)
39     return;
40
41   if (!clipmasks_initialized)
42   {
43     for (i=0; i < gfx.num_fonts; i++)
44       gfx.font_bitmap_info[i].clip_mask = NULL;
45
46     clipmasks_initialized = TRUE;
47   }
48
49   for (i=0; i < gfx.num_fonts; i++)
50   {
51     if (gfx.font_bitmap_info[i].clip_mask)
52       for (j=0; j < NUM_FONT_CHARS; j++)
53         XFreePixmap(display, gfx.font_bitmap_info[i].clip_mask[j]);
54     free(gfx.font_bitmap_info[i].clip_mask);
55
56     gfx.font_bitmap_info[i].clip_mask = NULL;
57   }
58
59   if (font_clip_gc)
60     XFreeGC(display, font_clip_gc);
61   font_clip_gc = None;
62
63   /* This stuff is needed because X11 (XSetClipOrigin(), to be precise) is
64      often very slow when preparing a masked XCopyArea() for big Pixmaps.
65      To prevent this, create small (tile-sized) mask Pixmaps which will then
66      be set much faster with XSetClipOrigin() and speed things up a lot. */
67
68   clip_gc_values.graphics_exposures = False;
69   clip_gc_valuemask = GCGraphicsExposures;
70   font_clip_gc = XCreateGC(display, window->drawable,
71                            clip_gc_valuemask, &clip_gc_values);
72
73   /* create graphic context structures needed for clipping */
74   clip_gc_values.graphics_exposures = False;
75   clip_gc_valuemask = GCGraphicsExposures;
76   copy_clipmask_gc = XCreateGC(display,
77                                gfx.font_bitmap_info[0].bitmap->clip_mask,
78                                clip_gc_valuemask, &clip_gc_values);
79
80   /* create only those clipping Pixmaps we really need */
81   for (i=0; i < gfx.num_fonts; i++)
82   {
83     if (gfx.font_bitmap_info[i].bitmap == NULL)
84       continue;
85
86     gfx.font_bitmap_info[i].clip_mask =
87       checked_calloc(NUM_FONT_CHARS * sizeof(Pixmap));
88
89     for (j=0; j < NUM_FONT_CHARS; j++)
90     {
91       Bitmap *src_bitmap = gfx.font_bitmap_info[i].bitmap;
92       Pixmap src_pixmap = src_bitmap->clip_mask;
93       int xpos = j % FONT_CHARS_PER_LINE;
94       int ypos = j / FONT_CHARS_PER_LINE;
95       int width  = gfx.font_bitmap_info[i].width;
96       int height = gfx.font_bitmap_info[i].height;
97       int src_x = gfx.font_bitmap_info[i].src_x + xpos * width;
98       int src_y = gfx.font_bitmap_info[i].src_y + ypos * height;
99
100       gfx.font_bitmap_info[i].clip_mask[j] =
101         XCreatePixmap(display, window->drawable, width, height, 1);
102
103       XCopyArea(display, src_pixmap, gfx.font_bitmap_info[i].clip_mask[j],
104                 copy_clipmask_gc, src_x, src_y, width, height, 0, 0);
105     }
106   }
107
108   XFreeGC(display, copy_clipmask_gc);
109 }
110 #endif /* TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND */
111
112 void InitFontInfo(struct FontBitmapInfo *font_bitmap_info, int num_fonts,
113                   int (*select_font_function)(int))
114 {
115   gfx.num_fonts = num_fonts;
116   gfx.font_bitmap_info = font_bitmap_info;
117   gfx.select_font_function = select_font_function;
118
119 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
120   InitFontClipmasks();
121 #endif
122 }
123
124 int getFontWidth(int font_nr)
125 {
126   int font_bitmap_id = gfx.select_font_function(font_nr);
127
128   return gfx.font_bitmap_info[font_bitmap_id].width;
129 }
130
131 int getFontHeight(int font_nr)
132 {
133   int font_bitmap_id = gfx.select_font_function(font_nr);
134
135   return gfx.font_bitmap_info[font_bitmap_id].height;
136 }
137
138 boolean getFontChar(int font_nr, char c, int *src_x, int *src_y)
139 {
140   int font_bitmap_id = gfx.select_font_function(font_nr);
141   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
142
143   if ((c >= 32 && c <= 95) || c == '°' || c == '´' || c == '|')
144   {
145     *src_x = font->src_x + ((c - 32) % FONT_CHARS_PER_LINE) * font->width;
146     *src_y = font->src_y + ((c - 32) / FONT_CHARS_PER_LINE) * font->height;
147
148     /* map '°' and 'TM' signs and cursor */
149     if (c == '°' || c == '´' || c == '|')
150     {
151       *src_x = font->src_x + FONT_CHARS_PER_LINE * font->width;
152       *src_y = font->src_y + (c == '°' ? 1 : c == '´' ? 2 : 3) * font->height;
153     }
154
155     return TRUE;
156   }
157
158   return FALSE;
159 }
160
161 void DrawInitText(char *text, int ypos, int font_nr)
162 {
163   if (window &&
164       gfx.num_fonts > 0 &&
165       gfx.font_bitmap_info[font_nr].bitmap != NULL)
166   {
167     int text_width = strlen(text) * getFontWidth(font_nr);
168
169     ClearRectangle(window, 0, ypos, video.width, getFontHeight(font_nr));
170     DrawTextExt(window, (video.width - text_width) / 2, ypos, text, font_nr,
171                 BLIT_OPAQUE);
172     FlushDisplay();
173   }
174 }
175
176 void DrawTextFCentered(int y, int font_nr, char *format, ...)
177 {
178   char buffer[MAX_OUTPUT_LINESIZE + 1];
179   va_list ap;
180
181   va_start(ap, format);
182   vsprintf(buffer, format, ap);
183   va_end(ap);
184
185   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
186     Error(ERR_EXIT, "string too long in DrawTextFCentered() -- aborting");
187
188   DrawText(gfx.sx + (gfx.sxsize - strlen(buffer) * getFontWidth(font_nr)) / 2,
189            gfx.sy + y, buffer, font_nr);
190 }
191
192 void DrawTextF(int x, int y, int font_nr, char *format, ...)
193 {
194   char buffer[MAX_OUTPUT_LINESIZE + 1];
195   va_list ap;
196
197   va_start(ap, format);
198   vsprintf(buffer, format, ap);
199   va_end(ap);
200
201   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
202     Error(ERR_EXIT, "string too long in DrawTextF() -- aborting");
203
204   DrawText(gfx.sx + x, gfx.sy + y, buffer, font_nr);
205 }
206
207 void DrawText(int x, int y, char *text, int font_nr)
208 {
209   int mask_mode = BLIT_OPAQUE;
210
211   if (DrawingOnBackground(x, y))
212     mask_mode = BLIT_MASKED;
213
214   DrawTextExt(drawto, x, y, text, font_nr, mask_mode);
215
216   if (x < gfx.dx)
217     redraw_mask |= REDRAW_FIELD;
218   else if (y < gfx.vy || gfx.vy == 0)
219     redraw_mask |= REDRAW_DOOR_1;
220 }
221
222 void DrawTextExt(DrawBuffer *dst_bitmap, int dst_x, int dst_y, char *text,
223                  int font_nr, int mask_mode)
224 {
225   int font_bitmap_id = gfx.select_font_function(font_nr);
226   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
227   boolean print_inverse = FALSE;
228   boolean print_inverse_cursor = FALSE;
229   int src_x, src_y;
230
231   if (font->bitmap == NULL)
232     return;
233
234   /* add offset for drawing font characters */
235   dst_x += font->draw_x;
236   dst_y += font->draw_y;
237
238   while (*text)
239   {
240     char c = *text++;
241
242     if (c == '~')
243     {
244       print_inverse = TRUE;
245       if (strlen(text) == 1)
246         print_inverse_cursor = TRUE;
247
248       continue;
249     }
250
251     if (c >= 'a' && c <= 'z')
252       c = 'A' + (c - 'a');
253     else if (c == 'ä' || c == 'Ä')
254       c = 91;
255     else if (c == 'ö' || c == 'Ö')
256       c = 92;
257     else if (c == 'ü' || c == 'Ü')
258       c = 93;
259     else if (c == '[' || c == ']')      /* map to normal braces */
260       c = (c == '[' ? '(' : ')');
261     else if (c == '\\')                 /* bad luck ... */
262       c = '/';
263
264     if (getFontChar(font_nr, c, &src_x, &src_y))
265     {
266       if (print_inverse)        /* special mode for text gadgets */
267       {
268         /* first step: draw solid colored rectangle (use "cursor" character) */
269         if (print_inverse_cursor)
270           BlitBitmap(font->bitmap, dst_bitmap,
271                      font->src_x + FONT_CHARS_PER_LINE * font->width,
272                      font->src_y + 3 * font->height,
273                      font->width, font->height, dst_x, dst_y);
274
275         /* second step: draw masked black rectangle (use "space" character) */
276         SetClipOrigin(font->bitmap, font->bitmap->stored_clip_gc,
277                       dst_x - src_x, dst_y - src_y);
278         BlitBitmapMasked(font->bitmap, dst_bitmap,
279                          0, 0, font->width, font->height, dst_x, dst_y);
280       }
281       else if (mask_mode == BLIT_MASKED)
282       {
283         /* clear font character background */
284         ClearRectangleOnBackground(dst_bitmap, dst_x, dst_y,
285                                    font->width, font->height);
286
287 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
288         /* use special font tile clipmasks */
289         {
290           int font_char = (c >= 32 && c <= 95 ? c - 32 : 0);
291
292           SetClipMask(font->bitmap, font_clip_gc,
293                       font->clip_mask[font_char]);
294           SetClipOrigin(font->bitmap, font_clip_gc, dst_x, dst_y);
295         }
296 #else
297         SetClipOrigin(font->bitmap, font->bitmap->stored_clip_gc,
298                       dst_x - src_x, dst_y - src_y);
299 #endif
300
301         BlitBitmapMasked(font->bitmap, dst_bitmap, src_x, src_y,
302                          font->width, font->height, dst_x, dst_y);
303       }
304       else      /* normal, non-masked font blitting */
305       {
306         BlitBitmap(font->bitmap, dst_bitmap, src_x, src_y,
307                    font->width, font->height, dst_x, dst_y);
308       }
309     }
310
311     dst_x += font->width;
312   }
313 }