rnd-20030411-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 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
26 static GC       font_clip_gc = None;
27
28 static void InitFontClipmasks()
29 {
30   static int last_num_fonts = 0;
31   XGCValues clip_gc_values;
32   unsigned long clip_gc_valuemask;
33   GC copy_clipmask_gc;
34   int i, j;
35
36   if (gfx.num_fonts == 0 || gfx.font_bitmap_info[0].bitmap == NULL)
37     return;
38
39   for (i=0; i < last_num_fonts; i++)
40   {
41     if (gfx.font_bitmap_info[i].clip_mask)
42     {
43       for (j=0; j < gfx.font_bitmap_info[i].last_num_chars; j++)
44         XFreePixmap(display, gfx.font_bitmap_info[i].clip_mask[j]);
45       free(gfx.font_bitmap_info[i].clip_mask);
46     }
47
48     gfx.font_bitmap_info[i].clip_mask = NULL;
49     gfx.font_bitmap_info[i].last_num_chars = 0;
50   }
51
52   last_num_fonts = gfx.num_fonts;
53
54   if (font_clip_gc)
55     XFreeGC(display, font_clip_gc);
56   font_clip_gc = None;
57
58   /* This stuff is needed because X11 (XSetClipOrigin(), to be precise) is
59      often very slow when preparing a masked XCopyArea() for big Pixmaps.
60      To prevent this, create small (tile-sized) mask Pixmaps which will then
61      be set much faster with XSetClipOrigin() and speed things up a lot. */
62
63   clip_gc_values.graphics_exposures = False;
64   clip_gc_valuemask = GCGraphicsExposures;
65   font_clip_gc = XCreateGC(display, window->drawable,
66                            clip_gc_valuemask, &clip_gc_values);
67
68   /* create graphic context structures needed for clipping */
69   clip_gc_values.graphics_exposures = False;
70   clip_gc_valuemask = GCGraphicsExposures;
71   copy_clipmask_gc = XCreateGC(display,
72                                gfx.font_bitmap_info[0].bitmap->clip_mask,
73                                clip_gc_valuemask, &clip_gc_values);
74
75   /* create only those clipping Pixmaps we really need */
76   for (i=0; i < gfx.num_fonts; i++)
77   {
78     if (gfx.font_bitmap_info[i].bitmap == NULL)
79       continue;
80
81     gfx.font_bitmap_info[i].clip_mask =
82       checked_calloc(gfx.font_bitmap_info[i].num_chars * sizeof(Pixmap));
83     gfx.font_bitmap_info[i].last_num_chars = gfx.font_bitmap_info[i].num_chars;
84
85     for (j=0; j < gfx.font_bitmap_info[i].num_chars; j++)
86     {
87       Bitmap *src_bitmap = gfx.font_bitmap_info[i].bitmap;
88       Pixmap src_pixmap = src_bitmap->clip_mask;
89       int xpos = j % gfx.font_bitmap_info[i].num_chars_per_line;
90       int ypos = j / gfx.font_bitmap_info[i].num_chars_per_line;
91       int width  = gfx.font_bitmap_info[i].width;
92       int height = gfx.font_bitmap_info[i].height;
93       int src_x = gfx.font_bitmap_info[i].src_x + xpos * width;
94       int src_y = gfx.font_bitmap_info[i].src_y + ypos * height;
95
96       gfx.font_bitmap_info[i].clip_mask[j] =
97         XCreatePixmap(display, window->drawable, width, height, 1);
98
99       XCopyArea(display, src_pixmap, gfx.font_bitmap_info[i].clip_mask[j],
100                 copy_clipmask_gc, src_x, src_y, width, height, 0, 0);
101     }
102   }
103
104   XFreeGC(display, copy_clipmask_gc);
105 }
106 #endif /* TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND */
107
108 void InitFontInfo(struct FontBitmapInfo *font_bitmap_info, int num_fonts,
109                   int (*select_font_function)(int))
110 {
111   gfx.num_fonts = num_fonts;
112   gfx.font_bitmap_info = font_bitmap_info;
113   gfx.select_font_function = select_font_function;
114
115 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
116   InitFontClipmasks();
117 #endif
118 }
119
120 int getFontWidth(int font_nr)
121 {
122   int font_bitmap_id = gfx.select_font_function(font_nr);
123
124   return gfx.font_bitmap_info[font_bitmap_id].width;
125 }
126
127 int getFontHeight(int font_nr)
128 {
129   int font_bitmap_id = gfx.select_font_function(font_nr);
130
131   return gfx.font_bitmap_info[font_bitmap_id].height;
132 }
133
134 static char getFontCharPosition(int font_nr, char c)
135 {
136   int font_bitmap_id = gfx.select_font_function(font_nr);
137   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
138   boolean default_font = (font->num_chars == DEFAULT_NUM_CHARS_PER_FONT);
139   int font_pos = c - 32;
140
141   /* map some special characters to their ascii values in default font */
142   if (default_font)
143     font_pos = MAP_FONT_ASCII(c) - 32;
144
145   /* this allows dynamic special characters together with special font */
146   if (font_pos < 0 || font_pos >= font->num_chars)
147     font_pos = 0;
148
149   return font_pos;
150 }
151
152 void getFontCharSource(int font_nr, char c, Bitmap **bitmap, int *x, int *y)
153 {
154   int font_bitmap_id = gfx.select_font_function(font_nr);
155   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
156   int font_pos = getFontCharPosition(font_nr, c);
157
158   *bitmap = font->bitmap;
159   *x = font->src_x + (font_pos % font->num_chars_per_line) * font->width;
160   *y = font->src_y + (font_pos / font->num_chars_per_line) * font->height;
161 }
162
163 void DrawInitText(char *text, int ypos, int font_nr)
164 {
165   if (window &&
166       gfx.num_fonts > 0 &&
167       gfx.font_bitmap_info[font_nr].bitmap != NULL)
168   {
169     int text_width = strlen(text) * getFontWidth(font_nr);
170
171     ClearRectangle(window, 0, ypos, video.width, getFontHeight(font_nr));
172     DrawTextExt(window, (video.width - text_width) / 2, ypos, text, font_nr,
173                 BLIT_OPAQUE);
174     FlushDisplay();
175   }
176 }
177
178 void DrawTextFCentered(int y, int font_nr, char *format, ...)
179 {
180   char buffer[MAX_OUTPUT_LINESIZE + 1];
181   va_list ap;
182
183   va_start(ap, format);
184   vsprintf(buffer, format, ap);
185   va_end(ap);
186
187   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
188     Error(ERR_EXIT, "string too long in DrawTextFCentered() -- aborting");
189
190   DrawText(gfx.sx + (gfx.sxsize - strlen(buffer) * getFontWidth(font_nr)) / 2,
191            gfx.sy + y, buffer, font_nr);
192 }
193
194 void DrawTextF(int x, int y, int font_nr, char *format, ...)
195 {
196   char buffer[MAX_OUTPUT_LINESIZE + 1];
197   va_list ap;
198
199   va_start(ap, format);
200   vsprintf(buffer, format, ap);
201   va_end(ap);
202
203   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
204     Error(ERR_EXIT, "string too long in DrawTextF() -- aborting");
205
206   DrawText(gfx.sx + x, gfx.sy + y, buffer, font_nr);
207 }
208
209 void DrawText(int x, int y, char *text, int font_nr)
210 {
211   int mask_mode = BLIT_OPAQUE;
212
213   if (DrawingOnBackground(x, y))
214     mask_mode = BLIT_ON_BACKGROUND;
215
216   DrawTextExt(drawto, x, y, text, font_nr, mask_mode);
217
218   if (x < gfx.dx)
219     redraw_mask |= REDRAW_FIELD;
220   else if (y < gfx.vy || gfx.vy == 0)
221     redraw_mask |= REDRAW_DOOR_1;
222 }
223
224 void DrawTextExt(DrawBuffer *dst_bitmap, int dst_x, int dst_y, char *text,
225                  int font_nr, int mask_mode)
226 {
227   int font_bitmap_id = gfx.select_font_function(font_nr);
228   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
229   int font_width = getFontWidth(font_nr);
230   int font_height = getFontHeight(font_nr);
231   Bitmap *src_bitmap;
232   int src_x, src_y;
233   char *text_ptr = text;
234
235   if (font->bitmap == NULL)
236     return;
237
238   /* add offset for drawing font characters */
239   dst_x += font->draw_x;
240   dst_y += font->draw_y;
241
242   while (*text_ptr)
243   {
244     char c = *text_ptr++;
245
246     getFontCharSource(font_nr, c, &src_bitmap, &src_x, &src_y);
247
248     if (mask_mode == BLIT_INVERSE)      /* special mode for text gadgets */
249     {
250       /* first step: draw solid colored rectangle (use "cursor" character) */
251       if (strlen(text) == 1)    /* only one char inverted => draw cursor */
252       {
253         Bitmap *cursor_bitmap;
254         int cursor_x, cursor_y;
255
256         getFontCharSource(font_nr, FONT_ASCII_CURSOR, &cursor_bitmap,
257                           &cursor_x, &cursor_y);
258
259         BlitBitmap(cursor_bitmap, dst_bitmap, cursor_x, cursor_y,
260                    font_width, font_height, dst_x, dst_y);
261       }
262
263 #if defined(TARGET_SDL)
264       /* second step: draw masked inverted character */
265       SDLCopyInverseMasked(src_bitmap, dst_bitmap, src_x, src_y,
266                            font_width, font_height, dst_x, dst_y);
267 #else
268       /* second step: draw masked black rectangle (use "space" character) */
269       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
270                     dst_x - src_x, dst_y - src_y);
271       BlitBitmapMasked(src_bitmap, dst_bitmap, 0, 0,
272                        font_width, font_height, dst_x, dst_y);
273 #endif
274     }
275     else if (mask_mode == BLIT_MASKED || mask_mode == BLIT_ON_BACKGROUND)
276     {
277       if (mask_mode == BLIT_ON_BACKGROUND)
278       {
279         /* clear font character background */
280         ClearRectangleOnBackground(dst_bitmap, dst_x, dst_y,
281                                    font_width, font_height);
282       }
283
284 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
285       /* use special font tile clipmasks */
286       {
287         int font_pos = getFontCharPosition(font_nr, c);
288
289         SetClipMask(src_bitmap, font_clip_gc, font->clip_mask[font_pos]);
290         SetClipOrigin(src_bitmap, font_clip_gc, dst_x, dst_y);
291       }
292 #else
293       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
294                     dst_x - src_x, dst_y - src_y);
295 #endif
296
297       BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y,
298                        font_width, font_height, dst_x, dst_y);
299     }
300     else        /* normal, non-masked font blitting */
301     {
302       BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y,
303                  font_width, font_height, dst_x, dst_y);
304     }
305
306     dst_x += font_width;
307   }
308 }