rnd-20030107-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
19
20 /* ========================================================================= */
21 /* font functions                                                            */
22 /* ========================================================================= */
23
24 #define NUM_FONTS               2
25 #define NUM_FONT_COLORS         4
26 #define NUM_FONT_CHARS          (FONT_LINES_PER_FONT * FONT_CHARS_PER_LINE)
27
28 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
29 static GC       tile_clip_gc = None;
30 static Pixmap   tile_clipmask[NUM_FONTS][NUM_FONT_COLORS][NUM_FONT_CHARS];
31
32 static struct
33 {
34   Bitmap **bitmap;
35   int xsize, ysize;
36 } font_info[NUM_FONTS] =
37 {
38   { &font.bitmap_big,           FONT1_XSIZE, FONT1_YSIZE },
39   { &font.bitmap_medium,        FONT6_XSIZE, FONT6_YSIZE }
40 };
41
42 static void InitFontClipmasks()
43 {
44   static boolean clipmasks_initialized = FALSE;
45   boolean fonts_initialized = TRUE;
46   XGCValues clip_gc_values;
47   unsigned long clip_gc_valuemask;
48   GC copy_clipmask_gc;
49   int i, j, k;
50
51   for (i=0; i<NUM_FONTS; i++)
52     if (*font_info[i].bitmap == NULL)
53       fonts_initialized = FALSE;
54
55   if (!fonts_initialized)
56     return;
57
58   if (clipmasks_initialized)
59     for (i=0; i<NUM_FONTS; i++)
60       for (j=0; j<NUM_FONT_COLORS; j++)
61         for (k=0; k<NUM_FONT_CHARS; k++)
62           XFreePixmap(display, tile_clipmask[i][j][k]);
63
64   if (tile_clip_gc)
65     XFreeGC(display, tile_clip_gc);
66   tile_clip_gc = None;
67
68   /* This stuff is needed because X11 (XSetClipOrigin(), to be precise) is
69      often very slow when preparing a masked XCopyArea() for big Pixmaps.
70      To prevent this, create small (tile-sized) mask Pixmaps which will then
71      be set much faster with XSetClipOrigin() and speed things up a lot. */
72
73   clip_gc_values.graphics_exposures = False;
74   clip_gc_valuemask = GCGraphicsExposures;
75   tile_clip_gc = XCreateGC(display, window->drawable,
76                            clip_gc_valuemask, &clip_gc_values);
77
78   /* create graphic context structures needed for clipping */
79   clip_gc_values.graphics_exposures = False;
80   clip_gc_valuemask = GCGraphicsExposures;
81   copy_clipmask_gc = XCreateGC(display, (*font_info[0].bitmap)->clip_mask,
82                                clip_gc_valuemask, &clip_gc_values);
83
84   /* create only those clipping Pixmaps we really need */
85   for (i=0; i<NUM_FONTS; i++)
86     for (j=0; j<NUM_FONT_COLORS; j++)
87       for (k=0; k<NUM_FONT_CHARS; k++)
88   {
89     Bitmap *src_bitmap = *font_info[i].bitmap;
90     Pixmap src_pixmap = src_bitmap->clip_mask;
91     int xpos = k % FONT_CHARS_PER_LINE;
92     int ypos = k / FONT_CHARS_PER_LINE;
93     int xsize = font_info[i].xsize;
94     int ysize = font_info[i].ysize;
95     int src_x = xsize * xpos;
96     int src_y = ysize * (ypos + j * FONT_LINES_PER_FONT);
97
98     tile_clipmask[i][j][k] =
99       XCreatePixmap(display, window->drawable, xsize, ysize, 1);
100
101     XCopyArea(display, src_pixmap, tile_clipmask[i][j][k], copy_clipmask_gc,
102               src_x, src_y, xsize, ysize, 0, 0);
103   }
104
105   XFreeGC(display, copy_clipmask_gc);
106
107   clipmasks_initialized = TRUE;
108 }
109 #endif /* TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND */
110
111 void InitFontInfo(Bitmap *bitmap_initial,
112                   Bitmap *bitmap_big, Bitmap *bitmap_medium,
113                   Bitmap *bitmap_small, Bitmap *bitmap_tile)
114 {
115   font.bitmap_initial = bitmap_initial;
116   font.bitmap_big = bitmap_big;
117   font.bitmap_medium = bitmap_medium;
118   font.bitmap_small = bitmap_small;
119   font.bitmap_tile = bitmap_tile;
120
121 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
122   InitFontClipmasks();
123 #endif
124 }
125
126 int getFontWidth(int font_size, int font_type)
127 {
128   return (font_type == FC_SPECIAL1 ? FONT3_XSIZE :
129           font_type == FC_SPECIAL2 ? FONT4_XSIZE :
130           font_type == FC_SPECIAL3 ? FONT5_XSIZE :
131           font_size == FS_BIG ? FONT1_XSIZE :
132           font_size == FS_MEDIUM ? FONT6_XSIZE :
133           font_size == FS_SMALL ? FONT2_XSIZE :
134           FONT2_XSIZE);
135 }
136
137 int getFontHeight(int font_size, int font_type)
138 {
139   return (font_type == FC_SPECIAL1 ? FONT3_YSIZE :
140           font_type == FC_SPECIAL2 ? FONT4_YSIZE :
141           font_type == FC_SPECIAL3 ? FONT5_YSIZE :
142           font_size == FS_BIG ? FONT1_YSIZE :
143           font_size == FS_MEDIUM ? FONT6_YSIZE :
144           font_size == FS_SMALL ? FONT2_YSIZE :
145           FONT2_YSIZE);
146 }
147
148 void DrawInitText(char *text, int ypos, int color)
149 {
150   if (window && font.bitmap_initial)
151   {
152     ClearRectangle(window, 0, ypos, video.width, FONT2_YSIZE);
153     DrawTextExt(window, (video.width - strlen(text) * FONT2_XSIZE)/2,
154                 ypos, text, FS_INITIAL, color, FONT_OPAQUE);
155     FlushDisplay();
156   }
157 }
158
159 void DrawTextFCentered(int y, int font_type, char *format, ...)
160 {
161   char buffer[MAX_OUTPUT_LINESIZE + 1];
162   int font_width = getFontWidth(FS_SMALL, font_type);
163   va_list ap;
164
165   va_start(ap, format);
166   vsprintf(buffer, format, ap);
167   va_end(ap);
168
169   DrawText(gfx.sx + (gfx.sxsize - strlen(buffer) * font_width) / 2,
170            gfx.sy + y, buffer, FS_SMALL, font_type);
171 }
172
173 void DrawTextF(int x, int y, int font_type, char *format, ...)
174 {
175   char buffer[MAX_OUTPUT_LINESIZE + 1];
176   va_list ap;
177
178   va_start(ap, format);
179   vsprintf(buffer, format, ap);
180   va_end(ap);
181
182   DrawText(gfx.sx + x, gfx.sy + y, buffer, FS_SMALL, font_type);
183 }
184
185 void DrawText(int x, int y, char *text, int font_size, int font_type)
186 {
187   int mask_mode = FONT_OPAQUE;
188
189   if (DrawingOnBackground(x, y))
190     mask_mode = FONT_MASKED;
191
192   DrawTextExt(drawto, x, y, text, font_size, font_type, mask_mode);
193
194   if (x < gfx.dx)
195     redraw_mask |= REDRAW_FIELD;
196   else if (y < gfx.vy || gfx.vy == 0)
197     redraw_mask |= REDRAW_DOOR_1;
198 }
199
200 void DrawTextExt(DrawBuffer *bitmap, int x, int y, char *text,
201                  int font_size, int font_type, int mask_mode)
202 {
203   Bitmap *font_bitmap;
204   int font_width, font_height, font_starty;
205   boolean print_inverse = FALSE;
206
207   if (font_size != FS_BIG && font_size != FS_MEDIUM && font_size != FS_SMALL)
208     font_size = FS_INITIAL;
209   if (font_type < FC_RED || font_type > FC_SPECIAL3)
210     font_type = FC_RED;
211
212   font_width = getFontWidth(font_size, font_type);
213   font_height = getFontHeight(font_size, font_type);
214
215   font_bitmap = (font_type == FC_SPECIAL2       ? font.bitmap_tile      :
216                  font_size == FS_BIG            ? font.bitmap_big       :
217                  font_size == FS_MEDIUM         ? font.bitmap_medium    :
218                  font_size == FS_SMALL          ? font.bitmap_small     :
219                  font.bitmap_initial);
220
221   if (font_bitmap == NULL)
222     return;
223
224   if (font_type == FC_SPECIAL2)
225     font_starty = (font_size == FS_BIG ? 0 : FONT1_YSIZE) * 5;
226   else
227     font_starty = (font_type * (font_size == FS_BIG ? FONT1_YSIZE :
228                                 font_size == FS_MEDIUM ? FONT6_YSIZE :
229                                 font_size == FS_SMALL ? FONT2_YSIZE :
230                                 FONT2_YSIZE) *
231                    FONT_LINES_PER_FONT);
232
233   if (font_type == FC_SPECIAL3)
234     font_starty -= FONT2_YSIZE * FONT_LINES_PER_FONT;
235
236   while (*text)
237   {
238     char c = *text++;
239
240     if (c == '~' && font_size == FS_SMALL)
241     {
242       print_inverse = TRUE;
243       continue;
244     }
245
246     if (c >= 'a' && c <= 'z')
247       c = 'A' + (c - 'a');
248     else if (c == 'ä' || c == 'Ä')
249       c = 91;
250     else if (c == 'ö' || c == 'Ö')
251       c = 92;
252     else if (c == 'ü' || c == 'Ü')
253       c = 93;
254     else if (c == '[' || c == ']')      /* map to normal braces */
255       c = (c == '[' ? '(' : ')');
256     else if (c == '\\')                 /* bad luck ... */
257       c = '/';
258
259     if ((c >= 32 && c <= 95) || c == '°' || c == '´' || c == '|')
260     {
261       int src_x = ((c - 32) % FONT_CHARS_PER_LINE) * font_width;
262       int src_y = ((c - 32) / FONT_CHARS_PER_LINE) * font_height + font_starty;
263       int dest_x = x, dest_y = y;
264
265       if (c == '°' || c == '´' || c == '|')     /* map '°' and 'TM' signs */
266       {
267         if (font_type == FC_SPECIAL2)
268         {
269           src_x = (c == '°' ? 1 : c == '´' ? 2 : 3) * font_width;
270           src_y = 4 * font_height;
271         }
272         else
273         {
274           src_x = FONT_CHARS_PER_LINE * font_width;
275           src_y = (c == '°' ? 1 : c == '´' ? 2 : 3) * font_height +font_starty;
276         }
277       }
278
279       if (print_inverse)        /* special mode for text gadgets */
280       {
281         /* first step: draw solid colored rectangle (use "cursor" character) */
282         BlitBitmap(font_bitmap, bitmap, FONT_CHARS_PER_LINE * font_width,
283                    3 * font_height + font_starty,
284                    font_width, font_height, x, y);
285
286         /* second step: draw masked black rectangle (use "space" character) */
287         SetClipOrigin(font_bitmap, font_bitmap->stored_clip_gc,
288                       dest_x - src_x, dest_y - src_y);
289         BlitBitmapMasked(font_bitmap, bitmap,
290                          0, 0, font_width, font_height, dest_x, dest_y);
291       }
292       else if (mask_mode == FONT_MASKED)
293       {
294         /* clear font character background */
295         ClearRectangleOnBackground(bitmap, dest_x, dest_y,
296                                    font_width, font_height);
297
298 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
299         /* use special font tile clipmasks, if available */
300         if (font_size == FS_BIG || font_size == FS_MEDIUM)
301         {
302           int font_nr = (font_size == FS_BIG ? 0 : 1);
303           int font_char = (c >= 32 && c <= 95 ? c - 32 : 0);
304
305           SetClipMask(font_bitmap, tile_clip_gc,
306                       tile_clipmask[font_nr][font_type][font_char]);
307           SetClipOrigin(font_bitmap, tile_clip_gc, dest_x, dest_y);
308         }
309         else
310         {
311           SetClipOrigin(font_bitmap, font_bitmap->stored_clip_gc,
312                         dest_x - src_x, dest_y - src_y);
313         }
314 #else
315         SetClipOrigin(font_bitmap, font_bitmap->stored_clip_gc,
316                       dest_x - src_x, dest_y - src_y);
317 #endif
318
319         BlitBitmapMasked(font_bitmap, bitmap, src_x, src_y,
320                          font_width, font_height, dest_x, dest_y);
321       }
322       else      /* normal, non-masked font blitting */
323       {
324         BlitBitmap(font_bitmap, bitmap, src_x, src_y,
325                    font_width, font_height, dest_x, dest_y);
326       }
327     }
328
329     x += font_width;
330   }
331 }