1 /*
2 SCREEN_DRAWING.C
3
4 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 and the "Aleph One" developers.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 This license is contained in the file "COPYING",
18 which is included with this source code; it is available online at
19 http://www.gnu.org/licenses/gpl.html
20
21 Monday, August 15, 1994 1:55:21 PM
22
23 Wednesday, August 24, 1994 12:50:20 AM (ajr)
24 added _right_justified for _draw_screen_text
25 Thursday, June 22, 1995 8:45:41 AM- note that we no longer hold your hand and set the port
26 for you. We have a grafptr and a restore ptr call.\
27
28 Apr 30, 2000 (Loren Petrich):
29 Added XML-parser support (actually, some days earlier, but had modified it
30 so as to have "interface" be defined in "game_window".
31
32 Jul 2, 2000 (Loren Petrich):
33 The HUD is now always buffered; it is lazily allocated
34
35 Oct 19, 2000 (Loren Petrich):
36 Added graceful degradation if get_shape_pixmap() returns NULL; CB had already done that
37 with the SDL version.
38
39 Dec 17, 2000 (Loren Petrich):
40 Added font-abstraction support (FontHandler.*)
41 */
42
43 #include "cseries.h"
44
45 #include "map.h"
46 #include "interface.h"
47 #include "shell.h"
48 #include "screen_drawing.h"
49 #include "fades.h"
50 #include "screen.h"
51
52 // LP addition: color and font parsers
53 #include "FontHandler.h"
54
55 #include "sdl_fonts.h"
56 #include <string.h>
57
58 #include <SDL_ttf.h>
59 #include "preferences.h"
60
61 #define clutSCREEN_COLORS 130
62 #define finfFONTS 128
63
64 extern TextSpec *_get_font_spec(short font_index);
65
66 /*
67 struct interface_font_info
68 {
69 TextSpec fonts[NUMBER_OF_INTERFACE_FONTS];
70 short heights[NUMBER_OF_INTERFACE_FONTS];
71 short line_spacing[NUMBER_OF_INTERFACE_FONTS];
72 };
73 */
74
75 /* --------- Globals. */
76 // LP change: hardcoding this quantity since we know how many we need
77 // Putting in the Moo definitions
78 static screen_rectangle interface_rectangles[NUMBER_OF_INTERFACE_RECTANGLES] =
79 {
80 {326, 300, 338, 473},
81 {464, 398, 475, 578},
82 {464, 181, 475, 361},
83 {338, 17, 0, 0},
84 {0, 0, 0, 0},
85 {352, 204, 454, 384},
86 {352, 384, 454, 596},
87 {179, 101, 210, 268},
88 {221, 25, 253, 238},
89 {263, 11, 294, 223},
90 {301, 38, 333, 236},
91 {304, 421, 331, 563},
92 {386, 231, 413, 406},
93 {345, 363, 372, 516},
94 {344, 83, 374, 271},
95 {206, 246, 347, 382},
96 // {264, 522, 291, 588}, // inf's bounds
97 // {263, 497, 294, 565}, // m2's bounds
98 {263, 500, 294, 585}, // adjusted to work with both m2 and inf
99 {0,0,0,0},
100 {0, 0, 0, 0},
101 {0, 0, 0, 0},
102 {0, 0, 320, 640},
103 {0, 0, 18, 640},
104 {302, 0, 320, 640},
105 {27, 72, 293, 568},
106 {27, 9, 293, 316},
107 {27, 324, 293, 631},
108 {27, 9, 293, 631},
109 {0, 0, 0, 0},
110 {0, 0, 0, 0},
111 {0, 0, 0, 0},
112 {0, 0, 0, 0}
113 };
114
set_about_alephone_rect(int width,int height)115 void set_about_alephone_rect(int width, int height)
116 {
117 if (!width || !height) return;
118
119 interface_rectangles[_about_alephone_rect].top = 480 - height;
120 interface_rectangles[_about_alephone_rect].left = 640 - width;
121 interface_rectangles[_about_alephone_rect].bottom = 480;
122 interface_rectangles[_about_alephone_rect].right = 640;
123 }
124
125 // static screen_rectangle *interface_rectangles;
126 // static CTabHandle screen_colors;
127 // LP change: now hardcoded and XML-changeable
128
129 // Copied off of original 'finf' resource
130 // static struct interface_font_info interface_fonts =
131 static FontSpecifier InterfaceFonts[NUMBER_OF_INTERFACE_FONTS] =
132 {
133 {"Monaco", 9, styleBold, 0, "#4"},
134 {"Monaco", 9, styleBold, 0, "#4"},
135 {"Monaco", 9, styleBold, 0, "#4"},
136 {"Monaco", 9, styleNormal,0, "#4"},
137 {"Courier", 12, styleNormal,0, "#22"},
138 {"Courier", 14, styleBold, 0, "#22"},
139 {"Monaco", 9, styleNormal,0, "#4"}
140 };
141
142 // LP change: hardcoding the interface and player colors,
143 // so as to banish the 'clut' resources
144 const int NumInterfaceColors = 26;
145 static rgb_color InterfaceColors[NumInterfaceColors] =
146 {
147 {0, 65535, 0},
148 {0, 5140, 0},
149 {0, 0, 0},
150
151 {0, 65535, 0},
152 {0, 12956, 0},
153 {0, 5100, 0},
154
155 {9216, 24320, 41728},
156 {65535, 0, 0},
157 {45056, 0, 24064},
158 {65535, 65535, 0},
159 {60000, 60000, 60000},
160 {62976, 22528, 0},
161 {3072, 0, 65535},
162 {0, 65535, 0},
163
164 {65535, 65535, 65535},
165 {0, 5140, 0},
166
167 {10000, 0, 0},
168 {65535, 0, 0},
169
170 {0, 65535, 0},
171 {65535, 65535, 65535},
172 {65535, 0, 0},
173 {0, 40000, 0},
174 {0, 45232, 51657},
175 {65535, 59367, 0},
176 {45000, 0, 0},
177 {3084, 0, 65535}
178 };
179
180 /* ------- Private prototypes */
181 static void load_interface_rectangles(void);
182 static void load_screen_interface_colors(void);
183
184 /* -------- Code */
initialize_screen_drawing(void)185 void initialize_screen_drawing(
186 void)
187 {
188 short loop;
189
190 /* Load the rectangles */
191 load_interface_rectangles();
192
193 /* Load the colors */
194 load_screen_interface_colors();
195
196 /* load the font stuff. */
197 for(loop=0; loop<NUMBER_OF_INTERFACE_FONTS; ++loop)
198 {
199 InterfaceFonts[loop].Init();
200 }
201 }
202
get_interface_rectangle(short index)203 screen_rectangle *get_interface_rectangle(short index)
204 {
205 assert(index>=0 && index<NUMBER_OF_INTERFACE_RECTANGLES);
206 return interface_rectangles + index;
207 }
208
get_interface_color(short index)209 rgb_color &get_interface_color(short index)
210 {
211 assert(index>=0 && index<NumInterfaceColors);
212 return InterfaceColors[index];
213 }
214
get_interface_font(short index)215 FontSpecifier &get_interface_font(short index)
216 {
217 assert(index >= 0 && index < NUMBER_OF_INTERFACE_FONTS);
218 return InterfaceFonts[index];
219 }
220
221
222
223 // Global variables
224 SDL_Surface *draw_surface = NULL; // Target surface for drawing commands
225 static SDL_Surface *old_draw_surface = NULL;
226
227
228 // Gets interface font and style;
229 // used in computer_interface.cpp
230 extern font_info *GetInterfaceFont(short font_index);
231 extern uint16 GetInterfaceStyle(short font_index);
232
233 bool draw_clip_rect_active = false; // Flag: clipping rect active
234 screen_rectangle draw_clip_rect; // Current clipping rectangle
235
236 // From screen_sdl.cpp
237 extern SDL_Surface *world_pixels, *HUD_Buffer, *Term_Buffer, *Intro_Buffer, *Map_Buffer;
238 extern bool intro_buffer_changed;
239
240 // Prototypes
241 extern TextSpec *_get_font_spec(short font_index);
242
243
244 /*
245 * Redirect drawing to screen or offscreen buffer
246 */
247
_set_port_to_screen_window(void)248 void _set_port_to_screen_window(void)
249 {
250 assert(old_draw_surface == NULL);
251 old_draw_surface = draw_surface;
252 draw_surface = MainScreenSurface();
253 }
254
_set_port_to_gworld(void)255 void _set_port_to_gworld(void)
256 {
257 assert(old_draw_surface == NULL);
258 old_draw_surface = draw_surface;
259 draw_surface = world_pixels;
260 }
261
_set_port_to_HUD(void)262 void _set_port_to_HUD(void)
263 {
264 assert(old_draw_surface == NULL);
265 old_draw_surface = draw_surface;
266 draw_surface = HUD_Buffer;
267 }
268
_restore_port(void)269 void _restore_port(void)
270 {
271 draw_surface = old_draw_surface;
272 old_draw_surface = NULL;
273 }
274
_set_port_to_term(void)275 void _set_port_to_term(void)
276 {
277 assert(old_draw_surface == NULL);
278 old_draw_surface = draw_surface;
279 draw_surface = Term_Buffer;
280 }
281
_set_port_to_intro(void)282 void _set_port_to_intro(void)
283 {
284 assert(old_draw_surface == NULL);
285 old_draw_surface = draw_surface;
286 draw_surface = Intro_Buffer;
287 intro_buffer_changed = true;
288 }
289
_set_port_to_map(void)290 void _set_port_to_map(void)
291 {
292 assert(old_draw_surface == NULL);
293 old_draw_surface = draw_surface;
294 draw_surface = Map_Buffer;
295 }
296
_set_port_to_custom(SDL_Surface * surface)297 void _set_port_to_custom(SDL_Surface *surface)
298 {
299 assert(old_draw_surface == NULL);
300 old_draw_surface = draw_surface;
301 draw_surface = surface;
302 }
303
304 /*
305 * Set clipping rectangle
306 */
307
set_drawing_clip_rectangle(short top,short left,short bottom,short right)308 void set_drawing_clip_rectangle(short top, short left, short bottom, short right)
309 {
310 if (top < 0)
311 draw_clip_rect_active = false;
312 else {
313 draw_clip_rect_active = true;
314 draw_clip_rect.top = top;
315 draw_clip_rect.left = left;
316 draw_clip_rect.bottom = bottom;
317 draw_clip_rect.right = right;
318 }
319 }
320
321
322 /*
323 * Draw shapes
324 */
325
_draw_screen_shape(shape_descriptor shape_id,screen_rectangle * destination,screen_rectangle * source)326 void _draw_screen_shape(shape_descriptor shape_id, screen_rectangle *destination, screen_rectangle *source)
327 {
328 // Convert rectangles
329 SDL_Rect src_rect;
330 if (source) {
331 src_rect.x = source->left;
332 src_rect.y = source->top;
333 src_rect.w = source->right - source->left;
334 src_rect.h = source->bottom - source->top;
335 }
336 SDL_Rect dst_rect = {destination->left, destination->top, destination->right - destination->left, destination->bottom - destination->top};
337
338 // Convert shape to surface
339 SDL_Surface *s = get_shape_surface(shape_id);
340 if (s == NULL)
341 return;
342
343 // if (draw_surface->format->BitsPerPixel == 8) {
344 // // SDL doesn't seem to be able to handle direct blits between 8-bit surfaces with different cluts
345 // SDL_Surface *s2 = SDL_DisplayFormat(s);
346 // SDL_FreeSurface(s);
347 // s = s2;
348 // }
349
350 // Blit the surface
351 SDL_BlitSurface(s, source ? &src_rect : NULL, draw_surface, &dst_rect);
352 if (draw_surface == MainScreenSurface())
353 MainScreenUpdateRects(1, &dst_rect);
354
355 // Free the surface
356 SDL_FreeSurface(s);
357 }
358
_draw_screen_shape_at_x_y(shape_descriptor shape_id,short x,short y)359 void _draw_screen_shape_at_x_y(shape_descriptor shape_id, short x, short y)
360 {
361 // Convert shape to surface
362 SDL_Surface *s = get_shape_surface(shape_id);
363 if (s == NULL)
364 return;
365
366 // if (draw_surface->format->BitsPerPixel == 8) {
367 // // SDL doesn't seem to be able to handle direct blits between 8-bit surfaces with different cluts
368 // SDL_Surface *s2 = SDL_DisplayFormat(s);
369 // SDL_FreeSurface(s);
370 // s = s2;
371 // }
372
373 // Setup destination rectangle
374 SDL_Rect dst_rect = {x, y, s->w, s->h};
375
376 // Blit the surface
377 SDL_BlitSurface(s, NULL, draw_surface, &dst_rect);
378 if (draw_surface == MainScreenSurface())
379 MainScreenUpdateRects(1, &dst_rect);
380
381 // Free the surface
382 SDL_FreeSurface(s);
383 }
384
385
386 /*
387 * Draw text
388 */
389
390 // Draw single glyph at given position in frame buffer, return glyph width
391 template <class T>
draw_glyph(uint8 c,int x,int y,T * p,int pitch,int clip_left,int clip_top,int clip_right,int clip_bottom,uint32 pixel,const sdl_font_info * font,bool oblique)392 inline static int draw_glyph(uint8 c, int x, int y, T *p, int pitch, int clip_left, int clip_top, int clip_right, int clip_bottom, uint32 pixel, const sdl_font_info *font, bool oblique)
393 {
394
395 int cpos = c - font->first_character;
396
397 // Calculate source and destination pointers (kerning, ascent etc.)
398 uint8 *src = font->pixmap + font->location_table[cpos];
399 int width = font->location_table[cpos + 1] - font->location_table[cpos];
400 int height = font->rect_height;
401 int advance = font->width_table[cpos * 2 + 1];
402 y -= font->ascent;
403 x += font->maximum_kerning + font->width_table[cpos * 2];
404 p += y * pitch / sizeof(T) + x;
405 if (oblique)
406 p += font->ascent / 2 - 1;
407
408 // Clip on top
409 if (y < clip_top) {
410 height -= clip_top - y;
411 if (height <= 0)
412 return advance;
413 p += (clip_top - y) * pitch / sizeof(T);
414 src += (clip_top - y) * font->bytes_per_row;
415 }
416
417 // Clip on bottom
418 if (y + height - 1 > clip_bottom) {
419 height -= y + height - 1 - clip_bottom;
420 if (height <= 0)
421 return advance;
422 }
423
424 // Clip on left
425 if (x < clip_left) {
426 width -= (clip_left - x);
427 if (width <= 0)
428 return advance;
429 p += (clip_left - x);
430 src += (clip_left - x);
431 }
432
433 // Clip on right
434 if (x + width - 1 > clip_right) {
435 width -= x + width - 1 - clip_right;
436 if (width <= 0)
437 return advance;
438 }
439
440 // Blit glyph to screen
441 for (int iy=0; iy<height; iy++) {
442 for (int ix=0; ix<width; ix++) {
443 if (src[ix])
444 p[ix] = pixel;
445 }
446 if (oblique && (iy % 2) == 1)
447 p--;
448 src += font->bytes_per_row;
449 p += pitch / sizeof(T);
450 }
451
452 return advance;
453 }
454
455 // Draw text at given position in frame buffer, return width
456 template <class T>
draw_text(const uint8 * text,size_t length,int x,int y,T * p,int pitch,int clip_left,int clip_top,int clip_right,int clip_bottom,uint32 pixel,const sdl_font_info * font,uint16 style)457 inline static int draw_text(const uint8 *text, size_t length, int x, int y, T *p, int pitch, int clip_left, int clip_top, int clip_right, int clip_bottom, uint32 pixel, const sdl_font_info *font, uint16 style)
458 {
459 bool oblique = ((style & styleItalic) != 0);
460 int total_width = 0;
461
462 uint8 c;
463 while (length--) {
464 c = *text++;
465 if (c < font->first_character || c > font->last_character)
466 continue;
467
468 int width;
469
470 width = draw_glyph(c, x, y, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
471 if (style & styleBold) {
472 draw_glyph(c, x + 1, y, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
473 width++;
474 }
475 if (style & styleUnderline) {
476 for (int i=0; i<width; i++)
477 p[y * pitch / sizeof(T) + x + i] = pixel;
478 }
479
480 total_width += width;
481 x += width;
482 }
483 return total_width;
484 }
485
486 // Draw text at given coordinates, return total width
_draw_text(SDL_Surface * s,const char * text,size_t length,int x,int y,uint32 pixel,uint16 style,bool) const487 int sdl_font_info::_draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool) const
488 {
489 // Get clipping rectangle
490 int clip_top, clip_bottom, clip_left, clip_right;
491 if (draw_clip_rect_active) {
492 clip_top = draw_clip_rect.top;
493 clip_bottom = draw_clip_rect.bottom - 1;
494 clip_left = draw_clip_rect.left;
495 clip_right = draw_clip_rect.right - 1;
496 } else {
497 clip_top = clip_left = 0;
498 clip_right = s->w - 1;
499 clip_bottom = s->h - 1;
500 }
501
502 if (SDL_MUSTLOCK (s)) {
503 if (SDL_LockSurface(s) < 0) return 0;
504 }
505 int width = 0;
506 switch (s->format->BytesPerPixel) {
507 case 1:
508 width = ::draw_text((const uint8 *)text, length, x, y, (pixel8 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, this, style);
509 break;
510 case 2:
511 width = ::draw_text((const uint8 *)text, length, x, y, (pixel16 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, this, style);
512 break;
513 case 4:
514 width = ::draw_text((const uint8 *)text, length, x, y, (pixel32 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, this, style);
515 break;
516 }
517 if (SDL_MUSTLOCK (s)) {
518 SDL_UnlockSurface(s);
519 }
520 if (s == MainScreenSurface())
521 MainScreenUpdateRect(x, y - ascent, text_width(text, style, false), rect_height);
522 return width;
523 }
524
_draw_text(SDL_Surface * s,const char * text,size_t length,int x,int y,uint32 pixel,uint16 style,bool utf8) const525 int ttf_font_info::_draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const
526 {
527 int clip_top, clip_bottom, clip_left, clip_right;
528 if (draw_clip_rect_active) {
529 clip_top = draw_clip_rect.top;
530 clip_bottom = draw_clip_rect.bottom;
531 clip_left = draw_clip_rect.left;
532 clip_right = draw_clip_rect.right;
533 } else {
534 clip_top = clip_left = 0;
535 clip_right = s->w;
536 clip_bottom = s->h;
537 }
538
539 SDL_Color c;
540 SDL_GetRGB(pixel, s->format, &c.r, &c.g, &c.b);
541 c.a = 0xff;
542 SDL_Surface *text_surface = 0;
543 if (utf8)
544 {
545 char *temp = process_printable(text, length);
546 if (environment_preferences->smooth_text)
547 text_surface = TTF_RenderUTF8_Blended(get_ttf(style), temp, c);
548 else
549 text_surface = TTF_RenderUTF8_Solid(get_ttf(style), temp, c);
550 }
551 else
552 {
553 uint16 *temp = process_macroman(text, length);
554 if (environment_preferences->smooth_text)
555 text_surface = TTF_RenderUNICODE_Blended(get_ttf(style), temp, c);
556 else
557 text_surface = TTF_RenderUNICODE_Solid(get_ttf(style), temp, c);
558 }
559 if (!text_surface) return 0;
560
561 SDL_Rect dst_rect;
562 dst_rect.x = x;
563 dst_rect.y = y - TTF_FontAscent(get_ttf(style));
564
565 if (draw_clip_rect_active)
566 {
567 SDL_Rect src_rect;
568 src_rect.x = 0;
569 src_rect.y = 0;
570
571 if (clip_top > dst_rect.y)
572 {
573 src_rect.y += dst_rect.y - clip_top;
574 }
575
576 if (clip_left > dst_rect.x)
577 {
578 src_rect.x += dst_rect.x - clip_left;
579 }
580
581 src_rect.w = (clip_right > dst_rect.x) ? clip_right - dst_rect.x : 0;
582 src_rect.h = (clip_bottom > dst_rect.y) ? clip_bottom - dst_rect.y : 0;
583
584 SDL_BlitSurface(text_surface, &src_rect, s, &dst_rect);
585 }
586 else
587 SDL_BlitSurface(text_surface, NULL, s, &dst_rect);
588
589 if (s == MainScreenSurface())
590 MainScreenUpdateRect(x, y - TTF_FontAscent(get_ttf(style)), text_width(text, style, utf8), TTF_FontHeight(get_ttf(style)));
591
592 int width = text_surface->w;
593 SDL_FreeSurface(text_surface);
594 return width;
595 }
596
draw_text(const char * text,int x,int y,uint32 pixel,const font_info * font,uint16 style)597 static void draw_text(const char *text, int x, int y, uint32 pixel, const font_info *font, uint16 style)
598 {
599 draw_text(draw_surface, text, strlen(text), x, y, pixel, font, style);
600 }
601
_draw_screen_text(const char * text,screen_rectangle * destination,short flags,short font_id,short text_color)602 void _draw_screen_text(const char *text, screen_rectangle *destination, short flags, short font_id, short text_color)
603 {
604 int x, y;
605
606 // Find font information
607 assert(font_id >= 0 && font_id < NUMBER_OF_INTERFACE_FONTS);
608 uint16 style = InterfaceFonts[font_id].Style;
609 const font_info *font = InterfaceFonts[font_id].Info;
610 if (font == NULL)
611 return;
612
613 // Get color
614 SDL_Color color;
615 _get_interface_color(text_color, &color);
616
617 // Copy the text to draw
618 char text_to_draw[256];
619 strncpy(text_to_draw, text, 256);
620 text_to_draw[255] = 0;
621
622 // Check for wrapping, and if it occurs, be recursive
623 if (flags & _wrap_text) {
624 int last_non_printing_character = 0, text_width = 0;
625 unsigned count = 0;
626 while (count < strlen(text_to_draw) && text_width < RECTANGLE_WIDTH(destination)) {
627 text_width += char_width(text_to_draw[count], font, style);
628 if (text_to_draw[count] == ' ')
629 last_non_printing_character = count;
630 count++;
631 }
632
633 if( count != strlen(text_to_draw)) {
634 char remaining_text_to_draw[256];
635 screen_rectangle new_destination;
636
637 // If we ever have to wrap text, we can't also center vertically. Sorry.
638 flags &= ~_center_vertical;
639 flags |= _top_justified;
640
641 // Pass the rest of it back in, recursively, on the next line
642 memcpy(remaining_text_to_draw, text_to_draw + last_non_printing_character + 1, strlen(text_to_draw + last_non_printing_character + 1) + 1);
643
644 new_destination = *destination;
645 new_destination.top += InterfaceFonts[font_id].LineSpacing;
646 _draw_screen_text(remaining_text_to_draw, &new_destination, flags, font_id, text_color);
647
648 // Now truncate our text to draw
649 text_to_draw[last_non_printing_character] = 0;
650 }
651 }
652
653 // Truncate text if necessary
654 int t_width = text_width(text_to_draw, font, style);
655 if (t_width > RECTANGLE_WIDTH(destination)) {
656 text_to_draw[trunc_text(text_to_draw, RECTANGLE_WIDTH(destination), font, style)] = 0;
657 t_width = text_width(text_to_draw, font, style);
658 }
659
660 // Horizontal positioning
661 if (flags & _center_horizontal)
662 x = destination->left + (((destination->right - destination->left) - t_width) / 2);
663 else if (flags & _right_justified)
664 x = destination->right - t_width;
665 else
666 x = destination->left;
667
668 // Vertical positioning
669 int t_height = InterfaceFonts[font_id].Height;
670 if (flags & _center_vertical) {
671 if (t_height > RECTANGLE_HEIGHT(destination))
672 y = destination->top;
673 else {
674 y = destination->bottom;
675 int offset = RECTANGLE_HEIGHT(destination) - t_height;
676 y -= (offset / 2) + (offset & 1) + 1;
677 }
678 } else if (flags & _top_justified) {
679 if (t_height > RECTANGLE_HEIGHT(destination))
680 y = destination->bottom;
681 else
682 y = destination->top + t_height;
683 } else
684 y = destination->bottom;
685
686 // Now draw it
687 draw_text(text_to_draw, x, y, SDL_MapRGB(draw_surface->format, color.r, color.g, color.b), font, style);
688 }
689
690 static TextSpec NullSpec = {0, 0, 0};
691
_get_font_spec(short font_index)692 TextSpec *_get_font_spec(short font_index)
693 {
694 return &NullSpec;
695 }
696
697 // Sets current font to this index of interface font;
698 // used in computer_interface.cpp
GetInterfaceFont(short font_index)699 font_info *GetInterfaceFont(short font_index)
700 {
701 assert(font_index>=0 && font_index<NUMBER_OF_INTERFACE_FONTS);
702
703 return static_cast<font_info*>(InterfaceFonts[font_index].Info);
704 }
705
706 // Gets the current font style;
707 // used in computer_interface.cpp
GetInterfaceStyle(short font_index)708 uint16 GetInterfaceStyle(short font_index)
709 {
710 assert(font_index>=0 && font_index<NUMBER_OF_INTERFACE_FONTS);
711
712 return InterfaceFonts[font_index].Style;
713 }
714
_get_font_line_height(short font_id)715 short _get_font_line_height(short font_id)
716 {
717 assert(font_id >= 0 && font_id < NUMBER_OF_INTERFACE_FONTS);
718 return InterfaceFonts[font_id].LineSpacing;
719 }
720
_text_width(const char * text,short font_id)721 short _text_width(const char *text, short font_id)
722 {
723 // Find font information
724 assert(font_id >= 0 && font_id < NUMBER_OF_INTERFACE_FONTS);
725 uint16 style = InterfaceFonts[font_id].Style;
726 const font_info *font = InterfaceFonts[font_id].Info;
727 if (font == NULL)
728 return 0;
729
730 // Calculate width
731 return text_width(text, font, style);
732 }
733
734
735 /*
736 * Draw rectangle
737 */
738
_fill_rect(screen_rectangle * rectangle,short color_index)739 void _fill_rect(screen_rectangle *rectangle, short color_index)
740 {
741 // Convert source rectangle
742 SDL_Rect r;
743 if (rectangle) {
744 r.x = rectangle->left;
745 r.y = rectangle->top;
746 r.w = rectangle->right - rectangle->left;
747 r.h = rectangle->bottom - rectangle->top;
748 }
749
750 // Get color
751 SDL_Color color;
752 _get_interface_color(color_index, &color);
753
754 // Fill rectangle
755 SDL_FillRect(draw_surface, rectangle ? &r : NULL, SDL_MapRGB(draw_surface->format, color.r, color.g, color.b));
756 if (draw_surface == MainScreenSurface()) {
757 if (rectangle)
758 MainScreenUpdateRects(1, &r);
759 else
760 MainScreenUpdateRect(0, 0, 0, 0);
761 }
762 }
763
_fill_screen_rectangle(screen_rectangle * rectangle,short color_index)764 void _fill_screen_rectangle(screen_rectangle *rectangle, short color_index)
765 {
766 _fill_rect(rectangle, color_index);
767 }
768
draw_rectangle(SDL_Surface * s,const SDL_Rect * rectangle,uint32 pixel)769 void draw_rectangle(SDL_Surface *s, const SDL_Rect *rectangle, uint32 pixel)
770 {
771 bool do_update = (s == MainScreenSurface());
772 SDL_Rect r = {rectangle->x, rectangle->y, rectangle->w, 1};
773 SDL_FillRect(s, &r, pixel);
774 if (do_update)
775 MainScreenUpdateRects(1, &r);
776 r.y += rectangle->h - 1;
777 SDL_FillRect(s, &r, pixel);
778 if (do_update)
779 MainScreenUpdateRects(1, &r);
780 r.y = rectangle->y;
781 r.w = 1;
782 r.h = rectangle->h;
783 SDL_FillRect(s, &r, pixel);
784 if (do_update)
785 MainScreenUpdateRects(1, &r);
786 r.x += rectangle->w - 1;
787 SDL_FillRect(s, &r, pixel);
788 if (do_update)
789 MainScreenUpdateRects(1, &r);
790 }
791
_frame_rect(screen_rectangle * rectangle,short color_index)792 void _frame_rect(screen_rectangle *rectangle, short color_index)
793 {
794 // Get color
795 SDL_Color color;
796 _get_interface_color(color_index, &color);
797 uint32 pixel = SDL_MapRGB(draw_surface->format, color.r, color.g, color.b);
798
799 // Draw rectangle
800 SDL_Rect r = {rectangle->left, rectangle->top, rectangle->right - rectangle->left, rectangle->bottom - rectangle->top};
801 draw_rectangle(draw_surface, &r, pixel);
802 }
803
_erase_screen(short color_index)804 void _erase_screen(short color_index)
805 {
806 _fill_rect(NULL, color_index);
807 }
808
809
810 /*
811 * Draw line
812 */
813
cs_code(const world_point2d * p,int clip_top,int clip_bottom,int clip_left,int clip_right)814 static inline uint8 cs_code(const world_point2d *p, int clip_top, int clip_bottom, int clip_left, int clip_right)
815 {
816 uint8 code = 0;
817 if (p->x < clip_left)
818 code |= 1;
819 if (p->x > clip_right)
820 code |= 2;
821 if (p->y < clip_top)
822 code |= 4;
823 if (p->y > clip_bottom)
824 code |= 8;
825 return code;
826 }
827
828 template <class T>
draw_thin_line_noclip(T * p,int pitch,const world_point2d * v1,const world_point2d * v2,uint32 pixel)829 static inline void draw_thin_line_noclip(T *p, int pitch, const world_point2d *v1, const world_point2d *v2, uint32 pixel)
830 {
831 int xdelta = v2->x - v1->x;
832 int ydelta = v2->y - v1->y;
833
834 if (abs(xdelta) > ydelta) { // X axis is major axis
835 int32 y = v1->y << 16;
836 int32 delta = (xdelta == 0 ? 0 : (ydelta << 16) / xdelta);
837 int x = v1->x;
838 p += v1->x;
839 if (xdelta < 0) { // Line going left
840 while (true) {
841 p[(y >> 16) * pitch / sizeof(T)] = pixel;
842 if (x == v2->x)
843 break;
844 x--;
845 p--;
846 y -= delta;
847 }
848 } else {
849 while (true) {
850 p[(y >> 16) * pitch / sizeof(T)] = pixel;
851 if (x == v2->x)
852 break;
853 x++;
854 p++;
855 y += delta;
856 }
857 }
858 } else { // Y axis is major axis
859 int32 x = v1->x << 16;
860 int32 delta = (ydelta == 0 ? 0 : (xdelta << 16) / ydelta);
861 p += v1->y * pitch / sizeof(T);
862 int y = v1->y;
863 while (true) {
864 p[x >> 16] = pixel;
865 if (y == v2->y)
866 break;
867 y++;
868 x += delta;
869 p += pitch / sizeof(T);
870 }
871 }
872 }
873
draw_line(SDL_Surface * s,const world_point2d * v1,const world_point2d * v2,uint32 pixel,int pen_size)874 void draw_line(SDL_Surface *s, const world_point2d *v1, const world_point2d *v2, uint32 pixel, int pen_size)
875 {
876 // Make line going downwards
877 if (v1->y > v2->y) {
878 const world_point2d *tmp = v1;
879 v1 = v2;
880 v2 = tmp;
881 }
882
883 if (pen_size == 1) {
884
885 // Thin line, clip with Cohen/Sutherland and draw with DDA
886
887 // Get clipping rectangle
888 int clip_top, clip_bottom, clip_left, clip_right;
889 if (draw_clip_rect_active) {
890 clip_top = draw_clip_rect.top;
891 clip_bottom = draw_clip_rect.bottom - 1;
892 clip_left = draw_clip_rect.left;
893 clip_right = draw_clip_rect.right - 1;
894 } else {
895 clip_top = clip_left = 0;
896 clip_right = s->w - 1;
897 clip_bottom = s->h - 1;
898 }
899
900 // Get codes for start/end points
901 uint8 code1 = cs_code(v1, clip_top, clip_bottom, clip_left, clip_right);
902 uint8 code2 = cs_code(v2, clip_top, clip_bottom, clip_left, clip_right);
903
904 world_point2d clip_start, clip_end;
905
906 clip_line:
907 if ((code1 | code2) == 0) {
908 if (SDL_MUSTLOCK(s)) {
909 if (SDL_LockSurface(s) < 0) return;
910 }
911
912 // Line completely visible, draw it
913 switch (s->format->BytesPerPixel) {
914 case 1:
915 draw_thin_line_noclip((pixel8 *)s->pixels, s->pitch, v1, v2, pixel);
916 break;
917 case 2:
918 draw_thin_line_noclip((pixel16 *)s->pixels, s->pitch, v1, v2, pixel);
919 break;
920 case 4:
921 draw_thin_line_noclip((pixel32 *)s->pixels, s->pitch, v1, v2, pixel);
922 break;
923 }
924 if (SDL_MUSTLOCK(s)) {
925 SDL_UnlockSurface(s);
926 }
927
928 } else if ((code1 & code2) == 0) {
929
930 // Line partially visible, clip it
931 #define clipx(p, clip, v, code) \
932 p.y = v1->y + (v2->y - v1->y) * (clip - v1->x) / (v2->x - v1->x); \
933 p.x = clip; \
934 v = &p; \
935 if (p.y < clip_top) \
936 code = 4; \
937 else if (p.y > clip_bottom) \
938 code = 8; \
939 else \
940 code = 0;
941
942 #define clipy(p, clip, v, code) \
943 p.x = v1->x + (v2->x - v1->x) * (clip - v1->y) / (v2->y - v1->y); \
944 p.y = clip; \
945 v = &p; \
946 if (p.x < clip_left) \
947 code = 1; \
948 else if (p.x > clip_right) \
949 code = 2; \
950 else \
951 code = 0;
952
953 if (code1) { // Clip start point
954 if (code1 & 1) { // Left
955 clipx(clip_start, clip_left, v1, code1);
956 } else if (code1 & 2) { // Right
957 clipx(clip_start, clip_right, v1, code1);
958 } else { // Top (bottom can't happen because the line goes downwards)
959 clipy(clip_start, clip_top, v1, code1);
960 }
961 } else { // Clip end point
962 if (code2 & 1) { // Left
963 clipx(clip_end, clip_left, v2, code2);
964 } else if (code2 & 2) { // Right
965 clipx(clip_end, clip_right, v2, code2);
966 } else { // Bottom (top can't happen because the line goes downwards)
967 clipy(clip_end, clip_bottom, v2, code2);
968 }
969 }
970
971 goto clip_line;
972 }
973
974 } else {
975
976 // Thick line: to emulate the QuickDraw behaviour of moving a
977 // rectangular pen along a line, we convert the line into a hexagon
978
979 // Calculate hexagon points
980 world_point2d hexagon[6];
981 hexagon[0].x = v1->x - pen_size / 2;
982 hexagon[1].x = hexagon[0].x + pen_size - 1;
983 hexagon[0].y = hexagon[1].y = v1->y - pen_size / 2;
984 hexagon[4].x = v2->x - pen_size / 2;
985 hexagon[3].x = hexagon[4].x + pen_size - 1;
986 hexagon[3].y = hexagon[4].y = v2->y - pen_size / 2 + pen_size - 1;
987 if (v1->x > v2->x) { // Line going to the left
988 hexagon[2].x = hexagon[1].x;
989 hexagon[2].y = hexagon[1].y + pen_size - 1;
990 hexagon[5].x = hexagon[4].x;
991 hexagon[5].y = hexagon[4].y - pen_size + 1;
992 if (v1->x - v2->y > v2->y - v1->y) // Pixels missing from polygon filler
993 draw_line(s, hexagon + 0, hexagon + 5, pixel, 1);
994 } else { // Line going to the right
995 hexagon[2].x = hexagon[3].x;
996 hexagon[2].y = hexagon[3].y - pen_size + 1;
997 hexagon[5].x = hexagon[0].x;
998 hexagon[5].y = hexagon[0].y + pen_size - 1;
999 if (v2->x - v1->y > v2->y - v1->y) // Pixels missing from polygon filler
1000 draw_line(s, hexagon + 1, hexagon + 2, pixel, 1);
1001 }
1002
1003 // Draw hexagon
1004 draw_polygon(s, hexagon, 6, pixel);
1005 }
1006 }
1007
1008
1009 /*
1010 * Draw clipped, filled, convex polygon
1011 */
1012
draw_polygon(SDL_Surface * s,const world_point2d * vertex_array,int vertex_count,uint32 pixel)1013 void draw_polygon(SDL_Surface *s, const world_point2d *vertex_array, int vertex_count, uint32 pixel)
1014 {
1015 if (vertex_count == 0)
1016 return;
1017
1018 // Reallocate temporary vertex lists if necessary
1019 static world_point2d *va1 = NULL, *va2 = NULL;
1020 static int max_vertices = 0;
1021 if (vertex_count > max_vertices) {
1022 delete[] va1;
1023 delete[] va2;
1024 va1 = new world_point2d[vertex_count * 2]; // During clipping, each vertex can become two vertices
1025 va2 = new world_point2d[vertex_count * 2];
1026 max_vertices = vertex_count;
1027 }
1028
1029 // Get clipping rectangle
1030 int clip_top, clip_bottom, clip_left, clip_right;
1031 if (draw_clip_rect_active) {
1032 clip_top = draw_clip_rect.top;
1033 clip_bottom = draw_clip_rect.bottom - 1;
1034 clip_left = draw_clip_rect.left;
1035 clip_right = draw_clip_rect.right - 1;
1036 } else {
1037 clip_top = clip_left = 0;
1038 clip_right = s->w - 1;
1039 clip_bottom = s->h - 1;
1040 }
1041
1042 // Clip polygon
1043 const world_point2d *v1, *v2;
1044 world_point2d *vp;
1045 world_point2d clip_point;
1046 int new_vertex_count;
1047
1048 #define clip_min(X, Y, clip, dst_array) \
1049 clip_point.Y = clip; \
1050 v1 = vertex_array + vertex_count - 1; \
1051 v2 = vertex_array; \
1052 vp = dst_array; \
1053 new_vertex_count = 0; \
1054 for (int i=0; i<vertex_count; i++, v1 = v2, v2++) { \
1055 if (v1->Y < clip) { \
1056 if (v2->Y < clip) { /* Edge completely clipped */ \
1057 continue; \
1058 } else { /* Clipped edge going down, find clip point */ \
1059 clip_point.X = v1->X + (v2->X - v1->X) * (clip - v1->Y) / (v2->Y - v1->Y); \
1060 *vp++ = clip_point; /* Add clip point to array */ \
1061 *vp++ = *v2; /* Add visible endpoint to array */ \
1062 new_vertex_count += 2; \
1063 } \
1064 } else { \
1065 if (v2->Y < clip) { /* Clipped edge going up, find clip point */ \
1066 clip_point.X = v2->X + (v1->X - v2->X) * (clip - v2->Y) / (v1->Y - v2->Y); \
1067 *vp++ = clip_point; /* Add clip point to array */ \
1068 new_vertex_count++; \
1069 } else { /* Edge completely visible, add endpoint to array */ \
1070 *vp++ = *v2; \
1071 new_vertex_count++; \
1072 } \
1073 } \
1074 } \
1075 vertex_count = new_vertex_count; \
1076 if (vertex_count == 0) \
1077 return; /* Polygon completely clipped */ \
1078 vertex_array = dst_array;
1079
1080 #define clip_max(X, Y, clip, dst_array) \
1081 clip_point.Y = clip; \
1082 v1 = vertex_array + vertex_count - 1; \
1083 v2 = vertex_array; \
1084 vp = dst_array; \
1085 new_vertex_count = 0; \
1086 for (int i=0; i<vertex_count; i++, v1 = v2, v2++) { \
1087 if (v1->Y < clip) { \
1088 if (v2->Y < clip) { /* Edge completely visible, add endpoint to array */ \
1089 *vp++ = *v2; \
1090 new_vertex_count++; \
1091 } else { /* Clipped edge going down, find clip point */ \
1092 clip_point.X = v1->X + (v2->X - v1->X) * (clip - v1->Y) / (v2->Y - v1->Y); \
1093 *vp++ = clip_point; /* Add clip point to array */ \
1094 new_vertex_count++; \
1095 } \
1096 } else { \
1097 if (v2->Y < clip) { /* Clipped edge going up, find clip point */ \
1098 clip_point.X = v2->X + (v1->X - v2->X) * (clip - v2->Y) / (v1->Y - v2->Y); \
1099 *vp++ = clip_point; /* Add clip point to array */ \
1100 *vp++ = *v2; /* Add visible endpoint to array */ \
1101 new_vertex_count += 2; \
1102 } else { /* Edge completely clipped */ \
1103 continue; \
1104 } \
1105 } \
1106 } \
1107 vertex_count = new_vertex_count; \
1108 if (vertex_count == 0) \
1109 return; /* Polygon completely clipped */ \
1110 vertex_array = dst_array;
1111
1112 clip_min(x, y, clip_top, va1);
1113 clip_max(x, y, clip_bottom, va2);
1114 clip_min(y, x, clip_left, va1);
1115 clip_max(y, x, clip_right, va2);
1116
1117 // Reallocate span list if necessary
1118 struct span_t {
1119 int left, right;
1120 };
1121 static span_t *span = NULL;
1122 static int max_spans = 0;
1123 if (!span || s->h > max_spans) {
1124 delete[] span;
1125 span = new span_t[s->h];
1126 max_spans = s->h;
1127 }
1128
1129 // Scan polygon edges and build span list
1130 v1 = vertex_array + vertex_count - 1;
1131 v2 = vertex_array;
1132 int xmin = INT16_MAX, xmax = INT16_MIN;
1133 int ymin = INT16_MAX, ymax = INT16_MIN;
1134 for (int i=0; i<vertex_count; i++, v1 = v2, v2++) {
1135
1136 if (v1->x < xmin) // Find minimum and maximum coordinates
1137 xmin = v1->x;
1138 if (v1->x > xmax)
1139 xmax = v1->x;
1140 if (v1->y < ymin)
1141 ymin = v1->y;
1142 if (v1->y > ymax)
1143 ymax = v1->y;
1144
1145 int x1x2 = v1->x - v2->x;
1146 int y1y2 = v1->y - v2->y;
1147
1148 if (y1y2 == 0) // Horizontal edge
1149 continue;
1150 else if (y1y2 < 0) { // Edge going down -> left span boundary
1151 int32 x = v1->x << 16; // 16.16 fixed point
1152 int32 delta = (x1x2 << 16) / y1y2;
1153 for (int y=v1->y; y<=v2->y; y++) {
1154 span[y].left = x >> 16;
1155 x += delta; // DDA line drawing
1156 }
1157 } else { // Edge going up -> right span boundary
1158 int32 x = v2->x << 16;
1159 int32 delta = (x1x2 << 16) / y1y2;
1160 for (int y=v2->y; y<=v1->y; y++) {
1161 span[y].right = x >> 16;
1162 x += delta; // Draw downwards to ensure that adjacent polygon fits perfectly
1163 }
1164 }
1165 }
1166
1167 // Fill spans
1168 SDL_Rect r = {0, 0, 0, 1};
1169 for (int y=ymin; y<=ymax; y++) {
1170 int left = span[y].left, right = span[y].right;
1171 if (left == right)
1172 continue;
1173 else if (left < right) {
1174 r.x = left;
1175 r.y = y;
1176 r.w = right - r.x + 1;
1177 } else {
1178 r.x = right;
1179 r.y = y;
1180 r.w = left - r.x + 1;
1181 }
1182 SDL_FillRect(s, &r, pixel);
1183 }
1184
1185 if (draw_surface == MainScreenSurface())
1186 MainScreenUpdateRect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
1187 }
1188
1189
1190 /*
1191 * Interface color management
1192 */
1193
_get_interface_color(size_t color_index,SDL_Color * color)1194 void _get_interface_color(size_t color_index, SDL_Color *color)
1195 {
1196 assert(color_index>=0 && color_index<NumInterfaceColors);
1197
1198 rgb_color &c = InterfaceColors[color_index];
1199 color->r = c.red >> 8;
1200 color->g = c.green >> 8;
1201 color->b = c.blue >> 8;
1202 color->a = 0xff;
1203 }
1204
1205 #define NUMBER_OF_PLAYER_COLORS 8
1206
_get_player_color(size_t color_index,RGBColor * color)1207 void _get_player_color(size_t color_index, RGBColor *color)
1208 {
1209 assert(color_index>=0 && color_index<NUMBER_OF_PLAYER_COLORS);
1210
1211 rgb_color &c = InterfaceColors[color_index + PLAYER_COLOR_BASE_INDEX];
1212 color->red = c.red;
1213 color->green = c.green;
1214 color->blue = c.blue;
1215 }
1216
_get_player_color(size_t color_index,SDL_Color * color)1217 void _get_player_color(size_t color_index, SDL_Color *color)
1218 {
1219 assert(color_index>=0 && color_index<NUMBER_OF_PLAYER_COLORS);
1220
1221 rgb_color &c = InterfaceColors[color_index + PLAYER_COLOR_BASE_INDEX];
1222 color->r = static_cast<Uint8>(c.red);
1223 color->g = static_cast<Uint8>(c.green);
1224 color->b = static_cast<Uint8>(c.blue);
1225 color->a = 0xff;
1226 }
1227
1228 /*
1229 * Rectangle XML parser
1230 */
1231
load_interface_rectangles(void)1232 static void load_interface_rectangles(void)
1233 {
1234 }
1235
load_screen_interface_colors(void)1236 static void load_screen_interface_colors(void)
1237 {
1238 }
1239
1240