1 #include "LRSDL_video.h"
2 #include "../nx.h"
3 #include "font.h"
4 #include "font.fdh"
5 
6 #include "libretro_shared.h"
7 
8 static int text_draw(int x, int y, const char *text, int spacing=0, NXFont *font=&whitefont);
9 
10 #ifdef FRONTEND_SUPPORTS_RGB565
11 #define RED_SHIFT 11
12 #define GREEN_SHIFT 5
13 #define BLUE_SHIFT 0
14 #elif defined(ABGR1555)
15 #define RED_SHIFT 0
16 #define GREEN_SHIFT 5
17 #define BLUE_SHIFT 10
18 #else
19 #define RED_SHIFT 10
20 #define GREEN_SHIFT 5
21 #define BLUE_SHIFT 0
22 #endif
23 
24 
25 static SDL_Surface *sdl_screen = NULL;
26 static SDL_Surface *shadesfc = NULL;
27 
28 static bool initialized = false;
29 static bool rendering = true;
30 static bool shrink_spaces = true;
31 static int fontheight = 0;
32 
33 NXFont whitefont;
34 NXFont greenfont;
35 NXFont bluefont;		// used for "F3:Options" text on pause screen
36 NXFont shadowfont;		// white letters w/ drop shadow
37 
38 #include "../libretro/bitmap_font.h"
39 
font_init(void)40 bool font_init(void)
41 {
42 	bool error = false;
43    LRSDL_RWops *rw = LRSDL_RWFromMem(font_bmp, sizeof(font_bmp));
44 
45 	// we'll be bypassing the NXSurface automatic scaling features
46 	// and drawing at the real resolution so we can get better-looking fonts.
47 	sdl_screen = screen->fSurface;
48 
49 	SDL_Surface *font = LRSDL_LoadBMP_RW(rw, 1);
50 	SetColorKey(font, SDL_SRCCOLORKEY, 0);
51 
52 	error |= whitefont.InitChars(font, 0xffffff);
53 	error |= greenfont.InitChars(font, 0xffffff); // Workaround for avoiding diacritics to show in green color
54 	error |= bluefont.InitCharsShadowed(font, 0xffffff, 0x000000); // Workaround for avoiding diacritics not showing on map location names
55 	error |= shadowfont.InitCharsShadowed(font, 0xffffff, 0x000000);
56 	error |= create_shade_sfc();
57 
58 	FreeSurface(font);
59 
60 	if (error) return 1;
61 
62 	fontheight = 0;
63 	for (char c = 'A'; c <= 'Z'; c++)
64 	{
65 		if (whitefont.letters[c]->h > fontheight)
66 			fontheight = whitefont.letters[c]->h;
67 	}
68 
69 	for (char c = 'a'; c <= 'z'; c++)
70 	{
71 		if (whitefont.letters[c]->h > fontheight)
72 			fontheight = whitefont.letters[c]->h;
73 	}
74 
75 	initialized = true;
76 	return 0;
77 }
78 
font_close(void)79 void font_close(void)
80 {
81 }
82 
font_reload()83 bool font_reload()
84 {
85 	if (!initialized)
86 		return 0;
87 
88 	whitefont.free();
89 	greenfont.free();
90 	bluefont.free();
91 	shadowfont.free();
92 
93 	return font_init();
94 }
95 
96 /*
97 void c------------------------------() {}
98 */
99 
NXFont()100 NXFont::NXFont()
101 {
102 	memset(letters, 0, sizeof(letters));
103 }
104 
~NXFont()105 NXFont::~NXFont()
106 {
107 	free();
108 }
109 
free()110 void NXFont::free()
111 {
112 	for(int i=0;i<NUM_LETTERS_RENDERED;i++)
113 	{
114 		if (letters[i])
115 			FreeSurface(letters[i]);
116 		letters[i] = NULL;
117 	}
118 }
119 
set_color(SDL_Surface * font,uint16_t color,uint16_t key)120 static void set_color(SDL_Surface *font, uint16_t color, uint16_t key)
121 {
122 	for (unsigned h = 0; h < font->h; h++)
123 	{
124 		uint16_t *pixels = (uint16_t*)font->pixels + h * (font->pitch / 2);
125 		for (unsigned w = 0; w < font->w; w++)
126 		{
127 			if (pixels[w] != key)
128 				pixels[w] = color;
129 		}
130 	}
131 }
132 
InitChars(SDL_Surface * font,uint32_t color)133 bool NXFont::InitChars(SDL_Surface *font, uint32_t color)
134 {
135 	SDL_Color fgcolor;
136 	SDL_Surface *letter;
137 
138 	fgcolor.r = (uint8_t)(color >> 16);
139 	fgcolor.g = (uint8_t)(color >> 8);
140 	fgcolor.b = (uint8_t)(color);
141 
142 	char str[2];
143 	str[1] = 0;
144 	uint16_t blue = 0x1f;
145 
146 	for(int i=1;i<NUM_LETTERS_RENDERED;i++)
147 	{
148 		str[0] = i;
149 
150       letter = (SDL_Surface*)AllocNewSurface(0, 6, 10);
151 
152 		SDL_Rect src = {0};
153 
154 		src.w = 5;
155 		src.h = 10;
156 		src.x = (i % 16) * 16;
157 		src.y = (i / 16) * 16;
158 
159 		SDL_Rect dst = {0};
160 		dst.w = letter->w;
161 		dst.h = letter->h;
162 
163 		SetColorKey(letter, SDL_SRCCOLORKEY, 0x1f);
164 		FillRectangle(letter, NULL, 0x1f);
165 
166 		DrawBlit(font, &src, letter, &dst);
167 
168 		uint16 color = fgcolor.r << RED_SHIFT
169 		| fgcolor.g << GREEN_SHIFT
170 		| fgcolor.b << BLUE_SHIFT;
171 
172 		set_color(letter, color, blue);
173 
174 		letters[i] = letter;
175 	}
176 
177 	return 0;
178 }
179 
180 // create a font with a drop-shadow (used for "MNA" stage-name displays)
InitCharsShadowed(SDL_Surface * font,uint32_t color,uint32_t shadowcolor)181 bool NXFont::InitCharsShadowed(SDL_Surface *font, uint32_t color, uint32_t shadowcolor)
182 {
183 	SDL_Color fgcolor, bgcolor;
184 	SDL_Surface *top, *bottom;
185 	SDL_Rect dstrect;
186 	const int offset = 2;
187 
188 	fgcolor.r = (uint8_t)(color >> 16);
189 	fgcolor.g = (uint8_t)(color >> 8);
190 	fgcolor.b = (uint8_t)(color);
191 
192 	bgcolor.r = (uint8_t)(shadowcolor >> 16);
193 	bgcolor.g = (uint8_t)(shadowcolor >> 8);
194 	bgcolor.b = (uint8_t)(shadowcolor);
195 
196 	char str[2];
197 	str[1] = 0;
198 
199 
200 	for(int i=1;i<NUM_LETTERS_RENDERED;i++)
201 	{
202 		str[0] = i;
203 
204 		uint16_t blue = 0x1f;
205 
206 		top = (SDL_Surface*)AllocNewSurface(0, 6, 10);
207 		bottom = (SDL_Surface*)AllocNewSurface(0, 6, 10);
208 
209 		FillRectangle(top, NULL, blue);
210 		FillRectangle(bottom, NULL, blue);
211 		SetColorKey(top, SDL_SRCCOLORKEY, blue);
212 		SetColorKey(bottom, SDL_SRCCOLORKEY, blue);
213 
214 		SDL_Rect src = {0};
215 		src.w = 5;
216 		src.h = 10;
217 		src.x = (i % 16) * 16;
218 		src.y = (i / 16) * 16;
219 
220 		SDL_Rect dst = {0};
221 		dst.w = top->w;
222 		dst.h = top->h;
223 
224 		DrawBlit(font, &src, top, &dst);
225 		DrawBlit(font, &src, bottom, &dst);
226 
227 		uint16_t color_fg = fgcolor.r << RED_SHIFT
228 		| fgcolor.g << GREEN_SHIFT
229 		| fgcolor.b << BLUE_SHIFT;
230 		uint16_t color_bg = bgcolor.r << RED_SHIFT
231 		| bgcolor.g << GREEN_SHIFT
232 		| bgcolor.b << BLUE_SHIFT;
233 
234 		set_color(top, color_fg, blue);
235 		set_color(bottom, color_bg, blue);
236 
237 		letters[i] = (SDL_Surface*)AllocNewSurface(0, top->w, top->h+offset);
238 
239 		SetColorKey(letters[i], SDL_SRCCOLORKEY, blue);
240 		FillRectangle(letters[i], NULL, blue);
241 
242 		dstrect.x = 0;
243 		dstrect.y = offset;
244 		DrawBlit(bottom, NULL, letters[i], &dstrect);
245 
246 		dstrect.x = 0;
247 		dstrect.y = 0;
248 		DrawBlit(top, NULL, letters[i], &dstrect);
249 	}
250 
251 	return 0;
252 }
253 
254 /*
255 void c------------------------------() {}
256 */
257 
258 // draw a text string
text_draw(int x,int y,const char * text,int spacing,NXFont * font)259 static int text_draw(int x, int y, const char *text, int spacing, NXFont *font)
260 {
261 	int orgx = x;
262 	int i;
263 	SDL_Rect dstrect;
264 
265 	for(i=0;text[i];i++)
266 	{
267 		char ch = text[i];
268 		SDL_Surface *letter = font->letters[ch];
269 
270 		if (ch == '=' && game.mode != GM_CREDITS)
271 		{
272 			if (rendering)
273 				draw_sprite((x), (y)+2, SPR_TEXTBULLET);
274 		}
275 		else if (rendering && ch != ' ' && letter)
276 		{
277 			// must set this every time, because SDL_BlitSurface overwrites
278 			// dstrect with final clipping rectangle.
279 			dstrect.x = x;
280 			dstrect.y = y;
281 			DrawBlit(letter, NULL, sdl_screen, &dstrect);
282 		}
283 
284 		if (spacing != 0)
285 		{	// fixed spacing
286 			x += spacing;
287 		}
288 		else
289 		{	// variable spacing
290 			if (ch == ' ' && shrink_spaces)
291 			{	// 10.5 px for spaces - make smaller than they really are - the default
292 				x += 6;
293 				if (i & 1) x++;
294 			}
295 			else
296 			{
297 				if (letter)
298 					x += letter->w;
299 			}
300 		}
301 	}
302 
303 	// return the final width of the text drawn
304 	return (x - orgx);
305 }
306 
307 
GetFontWidth(const char * text,int spacing,bool is_shaded)308 int GetFontWidth(const char *text, int spacing, bool is_shaded)
309 {
310 	int wd;
311 
312 	if (spacing)
313 		return (strlen(text) * spacing);
314 
315 	rendering = false;
316 	shrink_spaces = is_shaded;
317 
318 	wd = text_draw(0, 0, text, spacing);
319 
320 	rendering = true;
321 	shrink_spaces = true;
322 
323 	return (wd);
324 }
325 
GetFontHeight()326 int GetFontHeight()
327 {
328 	return fontheight;
329 }
330 
331 /*
332 void c------------------------------() {}
333 */
334 
335 // create the shadesfc, used by font_draw_shaded. It's just a big long black surface
336 // with 50% per-surface alpha applied, that we can use to darken the background.
create_shade_sfc(void)337 static bool create_shade_sfc(void)
338 {
339 	if (shadesfc)
340 		FreeSurface(shadesfc);
341 
342 	int ht = whitefont.letters['M']->h;
343 
344 	shadesfc = (SDL_Surface*)AllocNewSurface(SDL_SRCALPHA | SDL_SWSURFACE, SCREEN_WIDTH, ht);
345 
346 	if (!shadesfc)
347 		return 1;
348 
349 	FillRectangle(shadesfc, NULL, 0);
350 	LRSDL_SetAlpha(shadesfc, SDL_SRCALPHA, 128);
351 
352 	return 0;
353 }
354 
355 
font_draw(int x,int y,const char * text,int spacing,NXFont * font)356 int font_draw(int x, int y, const char *text, int spacing, NXFont *font)
357 {
358 	return (text_draw(x, y, text, spacing, font));
359 }
360 
361 // draw a text string with a 50% dark border around it
font_draw_shaded(int x,int y,const char * text,int spacing,NXFont * font)362 int font_draw_shaded(int x, int y, const char *text, int spacing, NXFont *font)
363 {
364 	SDL_Rect srcrect, dstrect;
365 	int wd;
366 
367 	// get full-res width of final text
368 	rendering = false;
369 	shrink_spaces = false;
370 
371 	srcrect.x = 0;
372 	srcrect.y = 0;
373 	srcrect.h = shadesfc->h;
374 	srcrect.w = text_draw(0, 0, text, spacing, font);
375 
376 	rendering = true;
377 
378 	// shade
379 	dstrect.x = x;
380 	dstrect.y = y;
381 	DrawBlit(shadesfc, &srcrect, sdl_screen, &dstrect);
382 
383 	// draw the text on top as normal
384 	wd = text_draw(x, y, text, spacing, font);
385 
386 	shrink_spaces = true;
387 	return (wd);
388 }
389 
390 
391 
392 
393 
394