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