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