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