rnd-20030223-2-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[0].bitmap == NULL)
39     return;
40
41   if (!clipmasks_initialized)
42   {
43     for (i=0; i < gfx.num_fonts; i++)
44       gfx.font[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[i].clip_mask)
52       for (j=0; j < NUM_FONT_CHARS; j++)
53         XFreePixmap(display, gfx.font[i].clip_mask[j]);
54     free(gfx.font[i].clip_mask);
55
56     gfx.font[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, gfx.font[0].bitmap->clip_mask,
77                                clip_gc_valuemask, &clip_gc_values);
78
79   /* create only those clipping Pixmaps we really need */
80   for (i=0; i < gfx.num_fonts; i++)
81   {
82     if (gfx.font[i].bitmap == NULL)
83       continue;
84
85     gfx.font[i].clip_mask = checked_calloc(NUM_FONT_CHARS * sizeof(Pixmap));
86
87     for (j=0; j < NUM_FONT_CHARS; j++)
88     {
89       Bitmap *src_bitmap = gfx.font[i].bitmap;
90       Pixmap src_pixmap = src_bitmap->clip_mask;
91       int xpos = j % FONT_CHARS_PER_LINE;
92       int ypos = j / FONT_CHARS_PER_LINE;
93       int width  = gfx.font[i].width;
94       int height = gfx.font[i].height;
95       int src_x = gfx.font[i].src_x + xpos * width;
96       int src_y = gfx.font[i].src_y + ypos * height;
97
98       gfx.font[i].clip_mask[j] =
99         XCreatePixmap(display, window->drawable, width, height, 1);
100
101       XCopyArea(display, src_pixmap, gfx.font[i].clip_mask[j],
102                 copy_clipmask_gc, src_x, src_y, width, height, 0, 0);
103     }
104   }
105
106   XFreeGC(display, copy_clipmask_gc);
107 }
108 #endif /* TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND */
109
110 void InitFontInfo(struct FontBitmapInfo *font_info, int num_fonts)
111 {
112   gfx.num_fonts = num_fonts;
113   gfx.font = font_info;
114
115 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
116   InitFontClipmasks();
117 #endif
118 }
119
120 int getFontWidth(int font_nr)
121 {
122   return gfx.font[font_nr].width;
123 }
124
125 int getFontHeight(int font_nr)
126 {
127   return gfx.font[font_nr].height;
128 }
129
130 void DrawInitText(char *text, int ypos, int font_nr)
131 {
132   if (window && gfx.num_fonts > 0 && gfx.font[font_nr].bitmap != NULL)
133   {
134     int text_width = strlen(text) * gfx.font[font_nr].width;
135
136     ClearRectangle(window, 0, ypos, video.width, gfx.font[font_nr].height);
137     DrawTextExt(window, (video.width - text_width) / 2, ypos, text, font_nr,
138                 FONT_OPAQUE);
139     FlushDisplay();
140   }
141 }
142
143 void DrawTextFCentered(int y, int font, char *format, ...)
144 {
145   char buffer[MAX_OUTPUT_LINESIZE + 1];
146   va_list ap;
147
148   va_start(ap, format);
149   vsprintf(buffer, format, ap);
150   va_end(ap);
151
152   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
153     Error(ERR_EXIT, "string too long in DrawTextFCentered() -- aborting");
154
155   DrawText(gfx.sx + (gfx.sxsize - strlen(buffer) * gfx.font[font].width) / 2,
156            gfx.sy + y, buffer, font);
157 }
158
159 void DrawTextF(int x, int y, int font, char *format, ...)
160 {
161   char buffer[MAX_OUTPUT_LINESIZE + 1];
162   va_list ap;
163
164   va_start(ap, format);
165   vsprintf(buffer, format, ap);
166   va_end(ap);
167
168   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
169     Error(ERR_EXIT, "string too long in DrawTextF() -- aborting");
170
171   DrawText(gfx.sx + x, gfx.sy + y, buffer, font);
172 }
173
174 void DrawText(int x, int y, char *text, int font)
175 {
176   int mask_mode = FONT_OPAQUE;
177
178   if (DrawingOnBackground(x, y))
179     mask_mode = FONT_MASKED;
180
181   DrawTextExt(drawto, x, y, text, font, mask_mode);
182
183   if (x < gfx.dx)
184     redraw_mask |= REDRAW_FIELD;
185   else if (y < gfx.vy || gfx.vy == 0)
186     redraw_mask |= REDRAW_DOOR_1;
187 }
188
189 void DrawTextExt(DrawBuffer *dst_bitmap, int dst_x, int dst_y, char *text,
190                  int font_nr, int mask_mode)
191 {
192   struct FontBitmapInfo *font = &gfx.font[font_nr];
193   boolean print_inverse = FALSE;
194
195   if (font->bitmap == NULL)
196     return;
197
198   /* add offset for drawing font characters */
199   dst_x += font->draw_x;
200   dst_y += font->draw_y;
201
202   while (*text)
203   {
204     char c = *text++;
205
206     if (c == '~')
207     {
208       print_inverse = TRUE;
209       continue;
210     }
211
212     if (c >= 'a' && c <= 'z')
213       c = 'A' + (c - 'a');
214     else if (c == 'ä' || c == 'Ä')
215       c = 91;
216     else if (c == 'ö' || c == 'Ö')
217       c = 92;
218     else if (c == 'ü' || c == 'Ü')
219       c = 93;
220     else if (c == '[' || c == ']')      /* map to normal braces */
221       c = (c == '[' ? '(' : ')');
222     else if (c == '\\')                 /* bad luck ... */
223       c = '/';
224
225     if ((c >= 32 && c <= 95) || c == '°' || c == '´' || c == '|')
226     {
227       int src_x= font->src_x + ((c - 32) % FONT_CHARS_PER_LINE) * font->width;
228       int src_y= font->src_y + ((c - 32) / FONT_CHARS_PER_LINE) * font->height;
229
230       if (c == '°' || c == '´' || c == '|')     /* map '°' and 'TM' signs */
231       {
232         src_x = font->src_x + FONT_CHARS_PER_LINE * font->width;
233         src_y = font->src_y + (c == '°' ? 1 : c == '´' ? 2 : 3) * font->height;
234       }
235
236       if (print_inverse)        /* special mode for text gadgets */
237       {
238         /* first step: draw solid colored rectangle (use "cursor" character) */
239         BlitBitmap(font->bitmap, dst_bitmap,
240                    font->src_x + FONT_CHARS_PER_LINE * font->width,
241                    font->src_y + 3 * font->height,
242                    font->width, font->height, dst_x, dst_y);
243
244         /* second step: draw masked black rectangle (use "space" character) */
245         SetClipOrigin(font->bitmap, font->bitmap->stored_clip_gc,
246                       dst_x - src_x, dst_y - src_y);
247         BlitBitmapMasked(font->bitmap, dst_bitmap,
248                          0, 0, font->width, font->height, dst_x, dst_y);
249       }
250       else if (mask_mode == FONT_MASKED)
251       {
252         /* clear font character background */
253         ClearRectangleOnBackground(dst_bitmap, dst_x, dst_y,
254                                    font->width, font->height);
255
256 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
257         /* use special font tile clipmasks */
258         {
259           int font_char = (c >= 32 && c <= 95 ? c - 32 : 0);
260
261           SetClipMask(font->bitmap, font_clip_gc,
262                       font->clip_mask[font_char]);
263           SetClipOrigin(font->bitmap, font_clip_gc, dst_x, dst_y);
264         }
265 #else
266         SetClipOrigin(font->bitmap, font->bitmap->stored_clip_gc,
267                       dst_x - src_x, dst_y - src_y);
268 #endif
269
270         BlitBitmapMasked(font->bitmap, dst_bitmap, src_x, src_y,
271                          font->width, font->height, dst_x, dst_y);
272       }
273       else      /* normal, non-masked font blitting */
274       {
275         BlitBitmap(font->bitmap, dst_bitmap, src_x, src_y,
276                    font->width, font->height, dst_x, dst_y);
277       }
278     }
279
280     dst_x += font->width;
281   }
282 }