rnd-20030404-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   gfx.inverse_text_color = WHITE_PIXEL;
119
120 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
121   InitFontClipmasks();
122 #endif
123 }
124
125 void SetInverseTextColor(Pixel inverse_text_color)
126 {
127   gfx.inverse_text_color = inverse_text_color;
128 }
129
130 int getFontWidth(int font_nr)
131 {
132   int font_bitmap_id = gfx.select_font_function(font_nr);
133
134   return gfx.font_bitmap_info[font_bitmap_id].width;
135 }
136
137 int getFontHeight(int font_nr)
138 {
139   int font_bitmap_id = gfx.select_font_function(font_nr);
140
141   return gfx.font_bitmap_info[font_bitmap_id].height;
142 }
143
144 boolean getFontChar(int font_nr, char c, int *src_x, int *src_y)
145 {
146   int font_bitmap_id = gfx.select_font_function(font_nr);
147   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
148
149   if ((c >= 32 && c <= 95) || c == '°' || c == '´' || c == '|')
150   {
151     *src_x = font->src_x + ((c - 32) % FONT_CHARS_PER_LINE) * font->width;
152     *src_y = font->src_y + ((c - 32) / FONT_CHARS_PER_LINE) * font->height;
153
154     /* map '°' and 'TM' signs and cursor */
155     if (c == '°' || c == '´' || c == '|')
156     {
157       *src_x = font->src_x + FONT_CHARS_PER_LINE * font->width;
158       *src_y = font->src_y + (c == '°' ? 1 : c == '´' ? 2 : 3) * font->height;
159     }
160
161     return TRUE;
162   }
163
164   return FALSE;
165 }
166
167 void DrawInitText(char *text, int ypos, int font_nr)
168 {
169   if (window &&
170       gfx.num_fonts > 0 &&
171       gfx.font_bitmap_info[font_nr].bitmap != NULL)
172   {
173     int text_width = strlen(text) * getFontWidth(font_nr);
174
175     ClearRectangle(window, 0, ypos, video.width, getFontHeight(font_nr));
176     DrawTextExt(window, (video.width - text_width) / 2, ypos, text, font_nr,
177                 BLIT_OPAQUE);
178     FlushDisplay();
179   }
180 }
181
182 void DrawTextFCentered(int y, int font_nr, char *format, ...)
183 {
184   char buffer[MAX_OUTPUT_LINESIZE + 1];
185   va_list ap;
186
187   va_start(ap, format);
188   vsprintf(buffer, format, ap);
189   va_end(ap);
190
191   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
192     Error(ERR_EXIT, "string too long in DrawTextFCentered() -- aborting");
193
194   DrawText(gfx.sx + (gfx.sxsize - strlen(buffer) * getFontWidth(font_nr)) / 2,
195            gfx.sy + y, buffer, font_nr);
196 }
197
198 void DrawTextF(int x, int y, int font_nr, char *format, ...)
199 {
200   char buffer[MAX_OUTPUT_LINESIZE + 1];
201   va_list ap;
202
203   va_start(ap, format);
204   vsprintf(buffer, format, ap);
205   va_end(ap);
206
207   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
208     Error(ERR_EXIT, "string too long in DrawTextF() -- aborting");
209
210   DrawText(gfx.sx + x, gfx.sy + y, buffer, font_nr);
211 }
212
213 void DrawText(int x, int y, char *text, int font_nr)
214 {
215   int mask_mode = BLIT_OPAQUE;
216
217   if (DrawingOnBackground(x, y))
218     mask_mode = BLIT_MASKED;
219
220   DrawTextExt(drawto, x, y, text, font_nr, mask_mode);
221
222   if (x < gfx.dx)
223     redraw_mask |= REDRAW_FIELD;
224   else if (y < gfx.vy || gfx.vy == 0)
225     redraw_mask |= REDRAW_DOOR_1;
226 }
227
228 void DrawTextExt(DrawBuffer *dst_bitmap, int dst_x, int dst_y, char *text,
229                  int font_nr, int mask_mode)
230 {
231   int font_bitmap_id = gfx.select_font_function(font_nr);
232   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
233   boolean print_inverse = FALSE;
234   boolean print_inverse_cursor = FALSE;
235   int src_x, src_y;
236
237   if (font->bitmap == NULL)
238     return;
239
240   /* add offset for drawing font characters */
241   dst_x += font->draw_x;
242   dst_y += font->draw_y;
243
244   while (*text)
245   {
246     char c = *text++;
247
248     if (c == '~')
249     {
250       print_inverse = TRUE;
251       if (strlen(text) == 1)
252         print_inverse_cursor = TRUE;
253
254       continue;
255     }
256
257     if (c >= 'a' && c <= 'z')
258       c = 'A' + (c - 'a');
259     else if (c == 'ä' || c == 'Ä')
260       c = 91;
261     else if (c == 'ö' || c == 'Ö')
262       c = 92;
263     else if (c == 'ü' || c == 'Ü')
264       c = 93;
265     else if (c == '[' || c == ']')      /* map to normal braces */
266       c = (c == '[' ? '(' : ')');
267     else if (c == '\\')                 /* bad luck ... */
268       c = '/';
269
270     if (getFontChar(font_nr, c, &src_x, &src_y))
271     {
272       if (print_inverse)        /* special mode for text gadgets */
273       {
274 #if defined(TARGET_SDL)
275         /* blit normally (non-masked) */
276         BlitBitmap(font->bitmap, dst_bitmap, src_x, src_y,
277                    font->width, font->height, dst_x, dst_y);
278
279         /* invert character */
280         SDLInvertArea(dst_bitmap, dst_x, dst_y, font->width, font->height,
281                       gfx.inverse_text_color);
282 #else
283         /* first step: draw solid colored rectangle (use "cursor" character) */
284         if (print_inverse_cursor)
285           BlitBitmap(font->bitmap, dst_bitmap,
286                      font->src_x + FONT_CHARS_PER_LINE * font->width,
287                      font->src_y + 3 * font->height,
288                      font->width, font->height, dst_x, dst_y);
289
290         /* second step: draw masked black rectangle (use "space" character) */
291         SetClipOrigin(font->bitmap, font->bitmap->stored_clip_gc,
292                       dst_x - src_x, dst_y - src_y);
293         BlitBitmapMasked(font->bitmap, dst_bitmap,
294                          0, 0, font->width, font->height, dst_x, dst_y);
295 #endif
296       }
297       else if (mask_mode == BLIT_MASKED)
298       {
299         /* clear font character background */
300         ClearRectangleOnBackground(dst_bitmap, dst_x, dst_y,
301                                    font->width, font->height);
302
303 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
304         /* use special font tile clipmasks */
305         {
306           int font_char = (c >= 32 && c <= 95 ? c - 32 : 0);
307
308           SetClipMask(font->bitmap, font_clip_gc,
309                       font->clip_mask[font_char]);
310           SetClipOrigin(font->bitmap, font_clip_gc, dst_x, dst_y);
311         }
312 #else
313         SetClipOrigin(font->bitmap, font->bitmap->stored_clip_gc,
314                       dst_x - src_x, dst_y - src_y);
315 #endif
316
317         BlitBitmapMasked(font->bitmap, dst_bitmap, src_x, src_y,
318                          font->width, font->height, dst_x, dst_y);
319       }
320       else      /* normal, non-masked font blitting */
321       {
322         BlitBitmap(font->bitmap, dst_bitmap, src_x, src_y,
323                    font->width, font->height, dst_x, dst_y);
324       }
325     }
326
327     dst_x += font->width;
328   }
329 }