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