1 #include "stdlib.h"
2 #include "sofont.h"
3 #include "string.h"
4 
5 struct _SOFONT
6 {
7 	int     height;
8 	IIM_Surface *picture;
9 	int    *CharPos;
10 	int    *Spacing;
11 
12 	int     max_i, spacew, cursShift;
13 	Uint32  background;
14 };
15 
16 // protected
17 int     SoFont_DoStartNewChar (SoFont * font, Sint32 x);
18 void    SoFont_CleanSurface (SoFont * font);
19 
20 
21 int
SoFont_FontHeight(SoFont * font)22 SoFont_FontHeight (SoFont * font)
23 {
24 	return font->height;
25 }
26 
27 int
SoFont_getMinChar(SoFont * font)28 SoFont_getMinChar (SoFont * font)
29 {
30 	return START_CHAR;
31 }
32 
33 int
SoFont_getMaxChar(SoFont * font)34 SoFont_getMaxChar (SoFont * font)
35 {
36 	return font->max_i;
37 }
38 
39 
40 SoFont *
SoFont_new()41 SoFont_new ()
42 {
43 	SoFont *font = (SoFont *) malloc (sizeof (SoFont));
44 
45 	font->picture = NULL;
46 	font->CharPos = NULL;
47 	font->Spacing = NULL;
48 	font->height = 0;
49 	font->max_i = 0;
50 	font->spacew = 0;
51 	font->cursShift = 0;
52 	font->background = 0;
53 
54 	return font;
55 }
56 
57 void
SoFont_free(SoFont * font)58 SoFont_free (SoFont * font)
59 {
60 /*	if (font->picture)
61 		SDL_FreeSurface (font->picture); */
62 	if (font->CharPos)
63 		free (font->CharPos);
64 	if (font->Spacing)
65 		free (font->Spacing);
66 }
67 
68 // SoFontUtilities
69 
70 static Uint32
SoFontGetPixel(SDL_Surface * Surface,Sint32 X,Sint32 Y)71 SoFontGetPixel (SDL_Surface * Surface, Sint32 X, Sint32 Y)
72 {
73 	Uint8  *bits;
74 	Uint32  Bpp;
75 	Uint8   r, g, b;
76 
77 	if (!Surface) {
78 		fprintf (stderr, "SoFontGetPixel: No surface!\n");
79 		return 0;
80 	}
81 	if ((X < 0) || (X >= Surface->w)) {
82 		fprintf (stderr, "SoFontGetPixel: X (%d)" " out of range!\n", X);
83 		return 0;
84 	}
85 
86 	Bpp = Surface->format->BytesPerPixel;
87 
88 	bits = ((Uint8 *) Surface->pixels) + Y * Surface->pitch + X * Bpp;
89 
90 	switch (Bpp) {
91 	case 1:
92 		return *((Uint8 *) Surface->pixels + Y * Surface->pitch + X);
93 		break;
94 	case 2:
95 		return *((Uint16 *) Surface->pixels + Y * Surface->pitch / 2 + X);
96 		break;
97 	case 3:
98 		// Format/endian independent
99 		r = *((bits) + Surface->format->Rshift / 8);
100 		g = *((bits) + Surface->format->Gshift / 8);
101 		b = *((bits) + Surface->format->Bshift / 8);
102 		return SDL_MapRGB (Surface->format, r, g, b);
103 		break;
104 	case 4:
105 		return *((Uint32 *) Surface->pixels + Y * Surface->pitch / 4 + X);
106 		break;
107 	}
108 	fprintf (stderr, "SoFontGetPixel: Unsupported pixel format!\n");
109 	return 0;											// David (to get rid of warning)
110 }
111 
112 static void
SoFontSetPixel(SDL_Surface * Surface,Sint32 X,Sint32 Y,Uint32 c)113 SoFontSetPixel (SDL_Surface * Surface, Sint32 X, Sint32 Y, Uint32 c)
114 {
115 	Uint8  *bits;
116 	Uint32  Bpp;
117 	Uint8   r, g, b;
118 
119 	if (!Surface) {
120 		fprintf (stderr, "SoFontSetPixel: No surface!\n");
121 		return;
122 	}
123 	if ((X < 0) || (X >= Surface->w)) {
124 		fprintf (stderr, "SoFontSetPixel: X (%d)" " out of range!\n", X);
125 		return;
126 	}
127 
128 	Bpp = Surface->format->BytesPerPixel;
129 
130 	bits = ((Uint8 *) Surface->pixels) + Y * Surface->pitch + X * Bpp;
131 
132 	switch (Bpp) {
133 	case 1:
134 		*((Uint8 *) Surface->pixels + Y * Surface->pitch + X) = (Uint8) c;
135 		break;
136 	case 2:
137 		*((Uint16 *) Surface->pixels + Y * Surface->pitch / 2 + X) = (Uint16) c;
138 		break;
139 	case 3:
140 		// Format/endian independent
141 		SDL_GetRGB (c, Surface->format, &r, &g, &b);
142 		*((bits) + Surface->format->Rshift / 8) = r;
143 		*((bits) + Surface->format->Gshift / 8) = g;
144 		*((bits) + Surface->format->Bshift / 8) = b;
145 		break;
146 	case 4:
147 		*((Uint32 *) Surface->pixels + Y * Surface->pitch / 4 + X) = c;
148 		break;
149 	}
150 }
151 
152 #if 0
153 static void
154 clipx (SDL_Rect * srcrect, SDL_Rect * dstrect, SDL_Rect * clip)
155 {
156 	int     dwx;
157 
158 	// Use if destination have the same size than source.
159 	int     dx = clip->x - dstrect->x;
160 
161 	int     sw = srcrect->w;			// Because SDL_Rect.w are
162 
163 	// unsigned.
164 	int     dw = dstrect->w;
165 
166 
167 	if (dx > 0) {
168 		srcrect->x += dx;
169 		dstrect->x += dx;
170 
171 		sw -= dx;
172 		dw -= dx;
173 	}
174 
175 	dwx = (dstrect->x + dstrect->w) - (clip->x + clip->w);
176 
177 	if (dwx > 0) {
178 		sw -= dwx;
179 		dw -= dwx;
180 	}
181 
182 	if (sw > 0)
183 		srcrect->w = sw;
184 	else
185 		srcrect->w = 0;
186 
187 	if (dw > 0)
188 		dstrect->w = dw;
189 	else
190 		dstrect->w = 0;
191 }
192 #endif
193 
194 static void
sdcRects(SDL_Rect * source,SDL_Rect * destination,SDL_Rect clipping)195 sdcRects (SDL_Rect * source, SDL_Rect * destination, SDL_Rect clipping)
196 {
197 	int     dwx, dhy;
198 
199 	// Use if destination have the same size than source &
200 	// cliping on destination
201 	int     dx = clipping.x - destination->x;
202 	int     dy = clipping.y - destination->y;
203 
204 	int     sw = source->w;
205 	int     sh = source->h;
206 
207 	if (dx > 0) {
208 		source->x += dx;
209 		destination->x += dx;
210 
211 		sw -= dx;
212 		destination->w -= dx;
213 	}
214 	if (dy > 0) {
215 		source->y += dy;
216 		destination->y += dy;
217 
218 		sh -= dy;
219 		destination->h -= dy;
220 	}
221 
222 	dwx = (destination->x + destination->w) - (clipping.x + clipping.w);
223 	dhy = (destination->y + destination->h) - (clipping.y + clipping.h);
224 
225 	if (dwx > 0) {
226 		sw -= dwx;
227 		destination->w -= dwx;
228 	}
229 	if (dhy > 0) {
230 		sh -= dhy;
231 		destination->h -= dhy;
232 	}
233 
234 	if (sw > 0)
235 		source->w = sw;
236 	else
237 		source->w = 0;
238 
239 	if (sh > 0)
240 		source->h = sh;
241 	else
242 		source->h = 0;
243 
244 }
245 
246 // end of SoFontUtilities
247 
248 int
SoFont_DoStartNewChar(SoFont * font,Sint32 x)249 SoFont_DoStartNewChar (SoFont * font, Sint32 x)
250 {
251 	if (!font->picture)
252 		return 0;
253 	return SoFontGetPixel (font->picture->surf, x, 0) ==
254 		SDL_MapRGB (font->picture->surf->format, 255, 0, 255);
255 }
256 
257 void
SoFont_CleanSurface(SoFont * font)258 SoFont_CleanSurface (SoFont * font)
259 {
260 	int     x = 0, y = 0;
261 	Uint32  pix;
262 
263 	if (!font->picture)
264 		return;
265 
266 	pix = SDL_MapRGB (font->picture->surf->format, 255, 0, 255);
267 
268 	while (x < font->picture->w) {
269 		y = 0;
270 //Why clean the entire surface? IMHO, S[o]Font should only ever
271 //touch - or even care about - the very top pixel row.
272 //    while(y < picture->h)
273 //    {
274 		if (SoFontGetPixel (font->picture->surf, x, y) == pix)
275 			SoFontSetPixel (font->picture->surf, x, y, font->background);
276 //      y++;
277 //    }
278 		x++;
279 	}
280 }
281 
282 
283 int
SoFont_load(SoFont * font,IIM_Surface * FontSurface)284 SoFont_load (SoFont * font, IIM_Surface * FontSurface)
285 {
286 	int     x = 0, i = 0, p = 0, s = 0;
287 	int     cursBegin = 0;
288 	int     cursWidth = 0;
289 
290 	int     _CharPos[256];
291 	int     _Spacing[256];
292 
293 	if (!FontSurface) {
294 		fprintf (stderr, "SoFont recieved a NULL SDL_Surface\n");
295 		return 0;
296 	}
297 	font->picture = FontSurface;
298 	font->height = font->picture->h - 1;
299 	while (x < font->picture->w) {
300 		if (SoFont_DoStartNewChar (font, x)) {
301 			if (i)
302 				_Spacing[i - 1] = 1 + x - s;
303 			p = x;
304 			while ((x < font->picture->w - 1) && (SoFont_DoStartNewChar (font, x)))
305 				x++;
306 			s = x;
307 			// CharPos[i++] = (p + x) / 2;
308 			_CharPos[i++] = (p + x + 1) / 2;	// David, Kobo Deluxe
309 		}
310 		x++;
311 	}
312 	// Note that spacing is not needed for the last char,
313 	// as it's just used for the blit width calculation.
314 	if (i)
315 		_Spacing[i - 1] = 1 + x - s;
316 	_Spacing[i] = 0;
317 	_CharPos[i++] = font->picture->w;
318 
319 	font->max_i = START_CHAR + i - 1;
320 	font->background = SoFontGetPixel (font->picture->surf, 0, font->height);
321 	SDL_SetColorKey (font->picture->surf, SDL_SRCCOLORKEY, font->background);
322 	SoFont_CleanSurface (font);
323 
324 	font->CharPos = (int *) malloc (i * sizeof (int));
325 	font->Spacing = (int *) malloc (i * sizeof (int));
326 	memcpy (font->CharPos, _CharPos, i * sizeof (int));
327 	memcpy (font->Spacing, _Spacing, i * sizeof (int));
328 
329 	// We search for a smart space width:
330 	// Changed from "a", "A", "0" for Kobo Deluxe.
331 	// Spaces were *way* to wide! //David
332 	font->spacew = 0;
333 	if (!font->spacew)
334 		font->spacew = SoFont_TextWidth (font, "i") * 3 / 2;
335 	if (!font->spacew)
336 		font->spacew = SoFont_TextWidth (font, "I") * 3 / 2;
337 	if (!font->spacew)
338 		font->spacew = SoFont_TextWidth (font, ".") * 3 / 2;
339 	if (!font->spacew)
340 		font->spacew = font->CharPos[1] - font->CharPos[0];
341 
342 	// We search for a smart cursor position:
343 	font->cursShift = 0;
344 	if ('|' > font->max_i)
345 		return 1;										// No bar in this font!
346 
347 	if (font->background ==
348 			SoFontGetPixel (font->picture->surf, font->CharPos['|' - START_CHAR],
349 											font->height / 2)) {
350 		// Up to the first | color
351 		for (cursBegin = 0; cursBegin <= SoFont_TextWidth (font, "|");
352 				 cursBegin++)
353 				if (font->background != SoFontGetPixel (font->picture->surf,
354 																								font->CharPos['|' -
355 																															START_CHAR] +
356 																								cursBegin, font->height / 2))
357 				break;
358 		// Up to the end of the | color
359 		for (cursWidth = 0; cursWidth <= SoFont_TextWidth (font, "|");
360 				 cursWidth++)
361 				if (font->background == SoFontGetPixel (font->picture->surf,
362 																								font->CharPos['|' -
363 																															START_CHAR] +
364 																								cursBegin + cursWidth,
365 																								font->height / 2))
366 				break;
367 	}
368 	else {
369 		// Up to the end of the | color
370 		for (cursWidth = 0; cursWidth <= SoFont_TextWidth (font, "|");
371 				 cursWidth++)
372 				if (font->background == SoFontGetPixel (font->picture->surf,
373 																								font->CharPos['|' -
374 																															START_CHAR] +
375 																								cursWidth, font->height / 2))
376 				break;
377 	}
378 	font->cursShift = cursBegin + 1;	// cursWidth could be used if
379 	// image format changes.
380 
381 	return 1;
382 }
383 
384 void
SoFont_Refresh(SoFont * font)385 SoFont_Refresh(SoFont * font)
386 {
387 	font->background = SoFontGetPixel (font->picture->surf, 0, font->height);
388 	SDL_SetColorKey (font->picture->surf, SDL_SRCCOLORKEY, font->background);
389 	SoFont_CleanSurface (font);
390 }
391 
392 void
SoFont_PutString(SoFont * font,SDL_Surface * Surface,int x,int y,const char * text,SDL_Rect * clip)393 SoFont_PutString (SoFont * font, SDL_Surface * Surface, int x, int y,
394 									const char *text, SDL_Rect * clip)
395 {
396     int sx = x;
397 	int     ofs, i = 0;
398 	SDL_Rect srcrect, dstrect;
399 
400 	if ((!font->picture) || (!Surface) || (!text))
401 		return;
402 
403 	while (text[i] != '\0') {
404 		if (text[i] == ' ') {
405 			x += font->spacew;
406 			i++;
407 		}
408         else if (text[i] == '\n') {
409             x = sx;
410             y += font->picture->h + 2;
411             i++;
412         }
413 		else if ((text[i] >= START_CHAR) && (text[i] <= font->max_i)) {
414 			ofs = text[i] - START_CHAR;
415 			srcrect.w = dstrect.w = font->CharPos[ofs + 1] - font->CharPos[ofs];
416 			srcrect.h = dstrect.h = font->height;
417 			srcrect.x = font->CharPos[ofs];
418 			srcrect.y = 1;
419 			dstrect.x = x;
420 			dstrect.y = y;
421 			x += font->Spacing[ofs];
422 			if (clip)
423 				sdcRects (&srcrect, &dstrect, *clip);
424 			SDL_BlitSurface (font->picture->surf, &srcrect, Surface, &dstrect);
425 			i++;
426 		}
427 		else
428 			i++;											// other chars are ignored
429 	}
430 }
431 
432 void
SoFont_PutStringWithCursor(SoFont * font,SDL_Surface * Surface,int xs,int y,const char * text,int cursPos,SDL_Rect * clip,int showCurs)433 SoFont_PutStringWithCursor (SoFont * font, SDL_Surface * Surface, int xs,
434 														int y, const char *text, int cursPos,
435 														SDL_Rect * clip, int showCurs)
436 {
437 	int     ofs, i = 0, x = xs;
438 	SDL_Rect srcrect, dstrect;
439 
440 	if ((!font->picture) || (!Surface) || (!text))
441 		return;
442 	if ('|' > font->max_i)
443 		showCurs = 0;
444 
445 	// We want the cursor to appear under the main text.
446 	if (showCurs) {
447 		while (text[i] != '\0')
448 			if (i == cursPos)
449 				break;
450 			else if (text[i] == ' ') {
451 				x += font->spacew;
452 				i++;
453 			}
454 			else if ((text[i] >= START_CHAR)
455 							 && (text[i] <= font->max_i)) {
456 				ofs = text[i] - START_CHAR;
457 				x += font->Spacing[ofs];
458 				i++;
459 			}
460 			else
461 				i++;
462 		ofs = '|' - START_CHAR;
463 
464 		srcrect.w = dstrect.w = font->CharPos[ofs + 1] - font->CharPos[ofs];
465 		srcrect.h = dstrect.h = font->height;
466 		srcrect.x = font->CharPos[ofs];
467 		srcrect.y = 1;
468 		dstrect.x = x - font->cursShift;
469 		dstrect.y = y;
470 		if (clip)
471 			sdcRects (&srcrect, &dstrect, *clip);
472 		SDL_BlitSurface (font->picture->surf, &srcrect, Surface, &dstrect);
473 	}
474 	// Then the text:
475 	SoFont_PutString (font, Surface, xs, y, text, clip);
476 }
477 
478 
479 int
SoFont_TextWidth_MinMax(SoFont * font,const char * text,int min,int max)480 SoFont_TextWidth_MinMax (SoFont * font, const char *text, int min, int max)
481 {
482 	int     ofs, x = 0, i = min;
483 
484 	if (!font->picture)
485 		return 0;
486 	while ((text[i] != '\0') && (i < max)) {
487 		if (text[i] == ' ') {
488 			x += font->spacew;
489 			i++;
490 		}
491 		else if ((text[i] >= START_CHAR) && (text[i] <= font->max_i)) {
492 			ofs = text[i] - START_CHAR;
493 			x += font->Spacing[ofs];
494 			i++;
495 		}
496 		else
497 			i++;
498 	}
499 	return x;
500 }
501 
502 int
SoFont_TextWidth(SoFont * font,const char * text)503 SoFont_TextWidth (SoFont * font, const char *text)
504 {
505 	return SoFont_TextWidth_MinMax (font, text, 0, 255);
506 }
507 
508 
509 void
SoFont_XCenteredString(SoFont * font,SDL_Surface * Surface,int y,const char * text,SDL_Rect * clip)510 SoFont_XCenteredString (SoFont * font, SDL_Surface * Surface, int y,
511 												const char *text, SDL_Rect * clip)
512 {
513 	if (!font->picture)
514 		return;
515 	SoFont_PutString (font, Surface,
516 										Surface->w / 2 - SoFont_TextWidth (font, text) / 2, y,
517 										text, clip);
518 }
519 
520 void
SoFont_CenteredString_XY(SoFont * font,SDL_Surface * Surface,int x,int y,const char * text,SDL_Rect * clip)521 SoFont_CenteredString_XY (SoFont * font, SDL_Surface * Surface, int x, int y,
522 													const char *text, SDL_Rect * clip)
523 {
524 	if (!font->picture)
525 		return;
526 	SoFont_PutString (font, Surface, x - SoFont_TextWidth (font, text) / 2,
527 										y - font->height / 2, text, clip);
528 }
529 
530 void
SoFont_CenteredString(SoFont * font,SDL_Surface * Surface,const char * text,SDL_Rect * clip)531 SoFont_CenteredString (SoFont * font, SDL_Surface * Surface, const char *text,
532 											 SDL_Rect * clip)
533 {
534 	if (!font->picture)
535 		return;
536 	SoFont_CenteredString_XY (font, Surface, Surface->clip_rect.w / 2,
537 														Surface->clip_rect.h / 2, text, clip);
538 }
539 
540 void
SoFont_PutStringCleverCursor(SoFont * font,SDL_Surface * Surface,const char * text,int cursPos,SDL_Rect * r,SDL_Rect * clip,int showCurs)541 SoFont_PutStringCleverCursor (SoFont * font, SDL_Surface * Surface,
542 															const char *text, int cursPos, SDL_Rect * r,
543 															SDL_Rect * clip, int showCurs)
544 {
545 	int     w1, w2;
546 
547 	if ((!font->picture) || (!text))
548 		return;
549 
550 	w1 = SoFont_TextWidth_MinMax (font, text, 0, cursPos);
551 	w2 = SoFont_TextWidth (font, text);
552 
553 	if ((w2 < r->w) || (w1 < r->w / 2))
554 		SoFont_PutStringWithCursor (font, Surface, r->x,
555 																r->y + (r->h - font->height) / 2, text,
556 																cursPos, clip, showCurs);
557 	else if (w1 < w2 - r->w / 2)
558 		SoFont_PutStringWithCursor (font, Surface, r->x - w1 + r->w / 2,
559 																r->y + (r->h - font->height) / 2, text,
560 																cursPos, clip, showCurs);
561 	else
562 		SoFont_PutStringWithCursor (font, Surface, r->x - w2 + r->w,
563 																r->y + (r->h - font->height) / 2, text,
564 																cursPos, clip, showCurs);
565 }
566 
567 int
SoFont_TextCursorAt(SoFont * font,const char * text,int px)568 SoFont_TextCursorAt (SoFont * font, const char *text, int px)
569 {
570 	int     ofs, x = 0, i = 0, ax = 0;
571 
572 	if (!font->picture)
573 		return 0;
574 
575 	if (px <= 0)
576 		return 0;
577 
578 	while (text[i] != '\0') {
579 		if (text[i] == ' ') {
580 			x += font->spacew;
581 			i++;
582 		}
583 		else if ((text[i] >= START_CHAR) && (text[i] <= font->max_i)) {
584 			ofs = text[i] - START_CHAR;
585 			x += font->Spacing[ofs];
586 			i++;
587 		}
588 		else
589 			i++;
590 
591 		if (px < (ax + x) / 2)
592 			return (i - 1);
593 		ax = x;
594 	}
595 	return i;
596 }
597 
598 int
SoFont_CleverTextCursorAt(SoFont * font,const char * text,int px,int cursPos,SDL_Rect * r)599 SoFont_CleverTextCursorAt (SoFont * font, const char *text, int px,
600 													 int cursPos, SDL_Rect * r)
601 {
602 	int     w1, w2;
603 
604 	if ((!font->picture) || (!text))
605 		return 0;
606 	w1 = SoFont_TextWidth_MinMax (font, text, 0, cursPos);
607 	w2 = SoFont_TextWidth (font, text);
608 	if ((w2 < r->w) || (w1 < r->w / 2))
609 		return SoFont_TextCursorAt (font, text, px);
610 	else if (w1 < w2 - r->w / 2)
611 		return SoFont_TextCursorAt (font, text, px + w1 - (r->w / 2));
612 	else
613 		return SoFont_TextCursorAt (font, text, px + w2 - r->w);
614 }
615