rnd-20030105-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 static GC       tile_clip_gc = None;
29 static Pixmap   tile_clipmask[NUM_FONTS][NUM_FONT_COLORS][NUM_FONT_CHARS];
30
31 static struct
32 {
33   Bitmap **bitmap;
34   int xsize, ysize;
35 } font_info[NUM_FONTS] =
36 {
37   { &font.bitmap_big,           FONT1_XSIZE, FONT1_YSIZE },
38   { &font.bitmap_medium,        FONT6_XSIZE, FONT6_YSIZE }
39 };
40
41 static void InitFontClipmasks()
42 {
43 #if defined(TARGET_X11_NATIVE)
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 */
110 }
111
112 void InitFontInfo(Bitmap *bitmap_initial,
113                   Bitmap *bitmap_big, Bitmap *bitmap_medium,
114                   Bitmap *bitmap_small, Bitmap *bitmap_tile)
115 {
116   font.bitmap_initial = bitmap_initial;
117   font.bitmap_big = bitmap_big;
118   font.bitmap_medium = bitmap_medium;
119   font.bitmap_small = bitmap_small;
120   font.bitmap_tile = bitmap_tile;
121
122   InitFontClipmasks();
123 }
124
125 int getFontWidth(int font_size, int font_type)
126 {
127   return (font_type == FC_SPECIAL1 ? FONT3_XSIZE :
128           font_type == FC_SPECIAL2 ? FONT4_XSIZE :
129           font_type == FC_SPECIAL3 ? FONT5_XSIZE :
130           font_size == FS_BIG ? FONT1_XSIZE :
131           font_size == FS_MEDIUM ? FONT6_XSIZE :
132           font_size == FS_SMALL ? FONT2_XSIZE :
133           FONT2_XSIZE);
134 }
135
136 int getFontHeight(int font_size, int font_type)
137 {
138   return (font_type == FC_SPECIAL1 ? FONT3_YSIZE :
139           font_type == FC_SPECIAL2 ? FONT4_YSIZE :
140           font_type == FC_SPECIAL3 ? FONT5_YSIZE :
141           font_size == FS_BIG ? FONT1_YSIZE :
142           font_size == FS_MEDIUM ? FONT6_YSIZE :
143           font_size == FS_SMALL ? FONT2_YSIZE :
144           FONT2_YSIZE);
145 }
146
147 void DrawInitText(char *text, int ypos, int color)
148 {
149   if (window && font.bitmap_initial)
150   {
151     ClearRectangle(window, 0, ypos, video.width, FONT2_YSIZE);
152     DrawTextExt(window, (video.width - strlen(text) * FONT2_XSIZE)/2,
153                 ypos, text, FS_INITIAL, color, FONT_OPAQUE);
154     FlushDisplay();
155   }
156 }
157
158 void DrawTextFCentered(int y, int font_type, char *format, ...)
159 {
160   char buffer[MAX_OUTPUT_LINESIZE + 1];
161   int font_width = getFontWidth(FS_SMALL, font_type);
162   va_list ap;
163
164   va_start(ap, format);
165   vsprintf(buffer, format, ap);
166   va_end(ap);
167
168   DrawText(gfx.sx + (gfx.sxsize - strlen(buffer) * font_width) / 2,
169            gfx.sy + y, buffer, FS_SMALL, font_type);
170 }
171
172 void DrawTextF(int x, int y, int font_type, char *format, ...)
173 {
174   char buffer[MAX_OUTPUT_LINESIZE + 1];
175   va_list ap;
176
177   va_start(ap, format);
178   vsprintf(buffer, format, ap);
179   va_end(ap);
180
181   DrawText(gfx.sx + x, gfx.sy + y, buffer, FS_SMALL, font_type);
182 }
183
184 void DrawText(int x, int y, char *text, int font_size, int font_type)
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_size, font_type, 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 *bitmap, int x, int y, char *text,
200                  int font_size, int font_type, int mask_mode)
201 {
202   Bitmap *font_bitmap;
203   int font_width, font_height, font_starty;
204   boolean print_inverse = FALSE;
205
206   if (font_size != FS_BIG && font_size != FS_MEDIUM && font_size != FS_SMALL)
207     font_size = FS_INITIAL;
208   if (font_type < FC_RED || font_type > FC_SPECIAL3)
209     font_type = FC_RED;
210
211   font_width = getFontWidth(font_size, font_type);
212   font_height = getFontHeight(font_size, font_type);
213
214   font_bitmap = (font_type == FC_SPECIAL2       ? font.bitmap_tile      :
215                  font_size == FS_BIG            ? font.bitmap_big       :
216                  font_size == FS_MEDIUM         ? font.bitmap_medium    :
217                  font_size == FS_SMALL          ? font.bitmap_small     :
218                  font.bitmap_initial);
219
220   if (font_bitmap == NULL)
221     return;
222
223   if (font_type == FC_SPECIAL2)
224     font_starty = (font_size == FS_BIG ? 0 : FONT1_YSIZE) * 5;
225   else
226     font_starty = (font_type * (font_size == FS_BIG ? FONT1_YSIZE :
227                                 font_size == FS_MEDIUM ? FONT6_YSIZE :
228                                 font_size == FS_SMALL ? FONT2_YSIZE :
229                                 FONT2_YSIZE) *
230                    FONT_LINES_PER_FONT);
231
232   if (font_type == FC_SPECIAL3)
233     font_starty -= FONT2_YSIZE * FONT_LINES_PER_FONT;
234
235   while (*text)
236   {
237     char c = *text++;
238
239     if (c == '~' && font_size == FS_SMALL)
240     {
241       print_inverse = TRUE;
242       continue;
243     }
244
245     if (c >= 'a' && c <= 'z')
246       c = 'A' + (c - 'a');
247     else if (c == 'ä' || c == 'Ä')
248       c = 91;
249     else if (c == 'ö' || c == 'Ö')
250       c = 92;
251     else if (c == 'ü' || c == 'Ü')
252       c = 93;
253     else if (c == '[' || c == ']')      /* map to normal braces */
254       c = (c == '[' ? '(' : ')');
255     else if (c == '\\')                 /* bad luck ... */
256       c = '/';
257
258     if ((c >= 32 && c <= 95) || c == '°' || c == '´' || c == '|')
259     {
260       int src_x = ((c - 32) % FONT_CHARS_PER_LINE) * font_width;
261       int src_y = ((c - 32) / FONT_CHARS_PER_LINE) * font_height + font_starty;
262       int dest_x = x, dest_y = y;
263
264       if (c == '°' || c == '´' || c == '|')     /* map '°' and 'TM' signs */
265       {
266         if (font_type == FC_SPECIAL2)
267         {
268           src_x = (c == '°' ? 1 : c == '´' ? 2 : 3) * font_width;
269           src_y = 4 * font_height;
270         }
271         else
272         {
273           src_x = FONT_CHARS_PER_LINE * font_width;
274           src_y = (c == '°' ? 1 : c == '´' ? 2 : 3) * font_height +font_starty;
275         }
276       }
277
278       if (print_inverse)        /* special mode for text gadgets */
279       {
280         /* first step: draw solid colored rectangle (use "cursor" character) */
281         BlitBitmap(font_bitmap, bitmap, FONT_CHARS_PER_LINE * font_width,
282                    3 * font_height + font_starty,
283                    font_width, font_height, x, y);
284
285         /* second step: draw masked black rectangle (use "space" character) */
286         SetClipOrigin(font_bitmap, font_bitmap->stored_clip_gc,
287                       dest_x - src_x, dest_y - src_y);
288         BlitBitmapMasked(font_bitmap, bitmap,
289                          0, 0, font_width, font_height, dest_x, dest_y);
290       }
291       else if (mask_mode == FONT_MASKED)
292       {
293         /* clear font character background */
294         BlitBitmap(gfx.background_bitmap, bitmap,
295                    dest_x - gfx.real_sx, dest_y - gfx.real_sy,
296                    font_width, font_height, dest_x, dest_y);
297
298         /* use special font tile clipmasks, if available */
299         if (font_size == FS_BIG || font_size == FS_MEDIUM)
300         {
301           int font_nr = (font_size == FS_BIG ? 0 : 1);
302           int font_char = (c >= 32 && c <= 95 ? c - 32 : 0);
303
304           SetClipMask(font_bitmap, tile_clip_gc,
305                       tile_clipmask[font_nr][font_type][font_char]);
306           SetClipOrigin(font_bitmap, tile_clip_gc, dest_x, dest_y);
307         }
308         else
309         {
310           SetClipOrigin(font_bitmap, font_bitmap->stored_clip_gc,
311                         dest_x - src_x, dest_y - src_y);
312         }
313
314         BlitBitmapMasked(font_bitmap, bitmap, src_x, src_y,
315                          font_width, font_height, dest_x, dest_y);
316       }
317       else      /* normal, non-masked font blitting */
318       {
319         BlitBitmap(font_bitmap, bitmap, src_x, src_y,
320                    font_width, font_height, dest_x, dest_y);
321       }
322     }
323
324     x += font_width;
325   }
326 }