Ticket #36: text.c

File text.c, 17.9 KB (added by paul.wratt, 8 years ago)

Adds USE_XLIB in Init_text() to allow X Windows only or "both" when compiled

Line 
1/* vim:expandtab:ts=2 sw=2:
2*/
3/* Grafx2 - The Ultimate 256-color bitmap paint program
4
5 Copyright 2011 Pawel Góralski
6 Copyright 2008 Yves Rizoud
7 Copyright 2008 Franck Charlet
8 Copyright 2008 Adrien Destugues
9 Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
10
11 Grafx2 is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; version 2
14 of the License.
15
16 Grafx2 is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with Grafx2; if not, see <http://www.gnu.org/licenses/>
23*/
24
25// Pour désactiver le support TrueType, définir NOTTF
26// To disable TrueType support, define NOTTF
27
28#include <string.h>
29#include <stdlib.h>
30#include <ctype.h> // tolower()
31
32// TrueType
33#ifndef NOTTF
34#if defined(__macosx__)
35 #include <SDL_ttf/SDL_ttf.h>
36#else
37 #include <SDL_ttf.h>
38#endif
39
40#if defined(__CAANOO__) || defined(__WIZ__) || defined(__GP2X__)
41// No X11
42#elif defined(__linux__)
43 #include <X11/Xlib.h>
44#endif
45#endif
46
47#if defined(__macosx__)
48 #import <corefoundation/corefoundation.h>
49 #import <sys/param.h>
50#endif
51
52#include <SDL_image.h>
53#include "SFont.h"
54
55#include "struct.h"
56#include "global.h"
57#include "sdlscreen.h"
58#include "io.h"
59#include "errors.h"
60#include "windows.h"
61#include "misc.h"
62#include "setup.h"
63
64typedef struct T_Font
65{
66 char * Name;
67 int Is_truetype;
68 int Is_bitmap;
69 char Label[22];
70
71 // Liste chainée simple
72 struct T_Font * Next;
73 struct T_Font * Previous;
74} T_Font;
75// Liste chainée des polices de texte
76T_Font * font_list_start;
77int Nb_fonts;
78
79// Inspiré par Allegro
80#define EXTID(a,b,c) ((((a)&255)<<16) | (((b)&255)<<8) | (((c)&255)))
81#define EXTID4(a,b,c,d) ((((a)&255)<<24) | (((b)&255)<<16) | (((c)&255)<<8) | (((d)&255)))
82
83int Compare_fonts(T_Font * font_1, T_Font * font_2)
84{
85 if (font_1->Is_bitmap && !font_2->Is_bitmap)
86 return -1;
87 if (font_2->Is_bitmap && !font_1->Is_bitmap)
88 return 1;
89 return strcmp(font_1->Label, font_2->Label);
90}
91
92// Ajout d'une fonte à la liste.
93void Add_font(const char *name)
94{
95 char * font_name;
96 T_Font * font;
97 int size=strlen(name)+1;
98 int index;
99
100 // Détermination du type:
101
102#if defined(__macosx__)
103
104 if (size < 6) return;
105
106 char strFontName[512];
107 CFStringRef CFSFontName;// = CFSTR(name);
108
109 CFSFontName = CFStringCreateWithBytes(NULL, (UInt8 *) name, size - 1, kCFStringEncodingASCII, false);
110 // Fix some funny names
111 CFStringGetCString(CFSFontName, strFontName, 512, kCFStringEncodingASCII);
112
113 // Now we have a printable font name, use it
114 name = strFontName;
115
116#else
117 if (size<5 ||
118 name[size-5]!='.')
119 return;
120#endif
121
122 font = (T_Font *)malloc(sizeof(T_Font));
123
124 switch (EXTID(tolower(name[size-4]), tolower(name[size-3]), tolower(name[size-2])))
125 {
126 case EXTID('t','t','f'):
127 case EXTID('f','o','n'):
128 case EXTID('o','t','f'):
129 case EXTID('p','f','b'):
130 font->Is_truetype = 1;
131 font->Is_bitmap = 0;
132 break;
133 case EXTID('b','m','p'):
134 case EXTID('g','i','f'):
135 case EXTID('j','p','g'):
136 case EXTID('l','b','m'):
137 case EXTID('p','c','x'):
138 case EXTID('p','n','g'):
139 case EXTID('t','g','a'):
140 case EXTID('t','i','f'):
141 case EXTID('x','c','f'):
142 case EXTID('x','p','m'):
143 case EXTID('.','x','v'):
144 font->Is_truetype = 0;
145 font->Is_bitmap = 1;
146 break;
147 default:
148 #if defined(__macosx__)
149 if(strcasecmp(&name[size-6], "dfont") == 0)
150 {
151 font->Is_truetype = 1;
152 font->Is_bitmap = 0;
153 }
154 else
155 {
156 free(font);
157 return;
158 }
159 #else
160 free(font);
161 return;
162 #endif
163 }
164
165 font->Name = (char *)malloc(size);
166 strcpy(font->Name, name);
167 // Label
168 strcpy(font->Label, " ");
169 if (font->Is_truetype)
170 font->Label[17]=font->Label[18]='T'; // Logo TT
171 font_name=Find_last_separator(font->Name);
172 if (font_name==NULL)
173 font_name=font->Name;
174 else
175 font_name++;
176 for (index=0; index < 17 && font_name[index]!='\0' && font_name[index]!='.'; index++)
177 font->Label[index]=font_name[index];
178
179 // Gestion Liste
180 font->Next = NULL;
181 font->Previous = NULL;
182 if (font_list_start==NULL)
183 {
184 // Premiere (liste vide)
185 font_list_start = font;
186 Nb_fonts++;
187 }
188 else
189 {
190 int compare;
191 compare = Compare_fonts(font, font_list_start);
192 if (compare<=0)
193 {
194 if (compare==0 && !strcmp(font->Name, font_list_start->Name))
195 {
196 // Doublon
197 free(font->Name);
198 free(font);
199 return;
200 }
201 // Avant la premiere
202 font->Next=font_list_start;
203 font_list_start=font;
204 Nb_fonts++;
205 }
206 else
207 {
208 T_Font *searched_font;
209 searched_font=font_list_start;
210 while (searched_font->Next && (compare=Compare_fonts(font, searched_font->Next))>0)
211 searched_font=searched_font->Next;
212 // Après searched_font
213 if (compare==0 && strcmp(font->Name, searched_font->Next->Name)==0)
214 {
215 // Doublon
216 free(font->Name);
217 free(font);
218 return;
219 }
220 font->Next=searched_font->Next;
221 searched_font->Next=font;
222 Nb_fonts++;
223 }
224 }
225}
226
227
228// Trouve le nom d'une fonte par son numéro
229char * Font_name(int index)
230{
231 T_Font *font = font_list_start;
232 if (index<0 ||index>=Nb_fonts)
233 return "";
234 while (index--)
235 font = font->Next;
236 return font->Name;
237}
238
239
240// Trouve le libellé d'affichage d'une fonte par son numéro
241// Renvoie un pointeur sur un buffer statique de 20 caracteres.
242char * Font_label(int index)
243{
244 T_Font *font;
245 static char label[20];
246
247 strcpy(label, " ");
248
249 // Recherche de la fonte
250 font = font_list_start;
251 if (index<0 ||index>=Nb_fonts)
252 return label;
253 while (index--)
254 font = font->Next;
255
256 // Libellé
257 strcpy(label, font->Label);
258 return label;
259}
260
261
262// Vérifie si une fonte donnée est TrueType
263int TrueType_font(int index)
264{
265 T_Font *font = font_list_start;
266 if (index<0 ||index>=Nb_fonts)
267 return 0;
268 while (index--)
269 font = font->Next;
270 return font->Is_truetype;
271}
272
273
274
275// Initialisation à faire une fois au début du programme
276void Init_text(void)
277{
278 char directory_name[MAX_PATH_CHARACTERS];
279 #ifndef NOTTF
280 // Initialisation de TTF
281 TTF_Init();
282 #endif
283
284 // Initialisation des fontes
285 font_list_start = NULL;
286 Nb_fonts=0;
287 // Parcours du répertoire "fonts"
288 strcpy(directory_name, Data_directory);
289 strcat(directory_name, FONTS_SUBDIRECTORY);
290 For_each_file(directory_name, Add_font);
291
292 #if defined(__WIN32__)
293 // Parcours du répertoire systeme windows "fonts"
294 #ifndef NOTTF
295 {
296 char * WindowsPath=getenv("windir");
297 if (WindowsPath)
298 {
299 sprintf(directory_name, "%s\\FONTS", WindowsPath);
300 For_each_file(directory_name, Add_font);
301 }
302 }
303 #endif
304 #elif defined(__macosx__)
305 // Récupération de la liste des fonts avec fontconfig
306 #ifndef NOTTF
307
308
309 int i,number;
310 char home_dir[MAXPATHLEN];
311 char *font_path_list[3] = {
312 "/System/Library/Fonts",
313 "/Library/Fonts"
314 };
315 number = 3;
316 // Make sure we also search into the user's fonts directory
317 CFURLRef url = (CFURLRef) CFCopyHomeDirectoryURLForUser(NULL);
318 CFURLGetFileSystemRepresentation(url, true, (UInt8 *) home_dir, MAXPATHLEN);
319 strcat(home_dir, "/Library/Fonts");
320 font_path_list[2] = home_dir;
321
322 for(i=0;i<number;i++)
323 For_each_file(*(font_path_list+i),Add_font);
324
325 CFRelease(url);
326 #endif
327
328 #elif defined(__CAANOO__) || defined(__WIZ__) || defined(__GP2X__)
329 // No X11 : Only use fonts from Grafx2
330 #elif defined(__linux__)
331 #ifndef NOTTF
332// #define USE_XLIB
333
334 #ifdef USE_XLIB
335/*
336 the following X calls lock Grafx2 into X Windows.
337 Grafx2 will not run in console mode outside X.
338 Also this does not find the newer "built-ins".
339*/
340 {
341 int i,number;
342 Display* dpy = XOpenDisplay(NULL);
343 char** font_path_list = XGetFontPath(dpy,&number);
344 XCloseDisplay(dpy);
345
346 for(i=0;i<number;i++)
347 For_each_file(*(font_path_list+i),Add_font);
348
349 XFreeFontPath(font_path_list);
350 }
351 #else
352/*
353 on Linux SDL does NOT need X Windows to use TTF,
354 but there are so many places usable fonts can be
355 we need to search by hand "one step up" of known
356 possibilities.
357*/
358 {
359 int i,number;
360 char *font_path_list[7] = {
361 "/usr/share/fonts", // mostly under here
362 "/usr/local/share/fonts", // maybe here too
363 "/usr/local/share/lib/X11/fonts", // we check the following on other/older systems
364 "/usr/openwin/lib/X11/fonts",
365 "/usr/dt/config/xfonts"
366 };
367 number = 7;
368
369 font_path_list[5] = getenv("HOME");
370 strcat(font_path_list[5], "/.fonts"); // traditionally, current users can also have fonts
371
372 font_path_list[6] = getenv("HOME");
373 strcat(font_path_list[6], "/.local/share/fonts"); // new systems are beginning to use this
374
375 for(i=0;i<number;i++)
376 For_each_font_in_tree(*(font_path_list+i));
377
378 }
379
380 #endif
381
382 #endif
383 #elif defined(__amigaos4__) || defined(__amigaos__)
384 #ifndef NOTTF
385 For_each_file( "FONTS:_TrueType", Add_font );
386 #endif
387 #elif defined(__AROS__)
388 #ifndef NOTTF
389 For_each_file( "FONTS:TrueType", Add_font );
390 #endif
391 #elif defined(__BEOS__)
392 #ifndef NOTTF
393 For_each_file("/etc/fonts/ttfonts", Add_font);
394 #endif
395 #elif defined(__HAIKU__)
396 #ifndef NOTTF
397 For_each_file("/boot/system/data/fonts/ttfonts/", Add_font);
398 #endif
399 #elif defined(__SKYOS__)
400 #ifndef NOTTF
401 For_each_file("/boot/system/fonts", Add_font);
402 #endif
403 #elif defined(__MINT__)
404 #ifndef NOTTF
405 For_each_file("C:/BTFONTS", Add_font);
406 #endif
407
408 #endif
409}
410
411// Informe si texte.c a été compilé avec l'option de support TrueType ou pas.
412int TrueType_is_supported()
413{
414 #ifdef NOTTF
415 return 0;
416 #else
417 return 1;
418 #endif
419}
420
421
422#ifndef NOTTF
423byte *Render_text_TTF(const char *str, int font_number, int size, int antialias, int bold, int italic, int *width, int *height, T_Palette palette)
424{
425 TTF_Font *font;
426 SDL_Surface * text_surface;
427 byte * new_brush;
428 int style;
429
430 SDL_Color fg_color;
431 SDL_Color bg_color;
432
433 // Chargement de la fonte
434 font=TTF_OpenFont(Font_name(font_number), size);
435 if (!font)
436 {
437 return NULL;
438 }
439
440 // Style
441 style=0;
442 if (italic)
443 style|=TTF_STYLE_ITALIC;
444 if (bold)
445 style|=TTF_STYLE_BOLD;
446 TTF_SetFontStyle(font, style);
447
448 // Colors: Text will be generated as white on black.
449 fg_color.r=fg_color.g=fg_color.b=255;
450 bg_color.r=bg_color.g=bg_color.b=0;
451 // The following is alpha, supposedly unused
452 bg_color.unused=fg_color.unused=255;
453
454 // Text rendering: creates a 8bit surface with its dedicated palette
455 if (antialias)
456 text_surface=TTF_RenderText_Shaded(font, str, fg_color, bg_color );
457 else
458 text_surface=TTF_RenderText_Solid(font, str, fg_color);
459 if (!text_surface)
460 {
461 TTF_CloseFont(font);
462 return NULL;
463 }
464
465 new_brush=Surface_to_bytefield(text_surface, NULL);
466 if (!new_brush)
467 {
468 SDL_FreeSurface(text_surface);
469 TTF_CloseFont(font);
470 return NULL;
471 }
472
473 // Import palette
474 Get_SDL_Palette(text_surface->format->palette, palette);
475
476 if (antialias)
477 {
478 int black_col;
479 // Shaded text: X-Swap the color that is pure black with the BG color number,
480 // so that the brush is immediately 'transparent'
481
482 // Find black (c)
483 for (black_col=0; black_col<256; black_col++)
484 {
485 if (palette[black_col].R==0 && palette[black_col].G==0 && palette[black_col].B==0)
486 break;
487 } // If not found: c = 256 = 0 (byte)
488
489 if (black_col != Back_color)
490 {
491 int c;
492 byte colmap[256];
493 // Swap palette entries
494
495 SWAP_BYTES(palette[black_col].R, palette[Back_color].R)
496 SWAP_BYTES(palette[black_col].G, palette[Back_color].G)
497 SWAP_BYTES(palette[black_col].B, palette[Back_color].B)
498
499 // Define a colormap
500 for (c=0; c<256; c++)
501 colmap[c]=c;
502
503 // The swap
504 colmap[black_col]=Back_color;
505 colmap[Back_color]=black_col;
506
507 Remap_general_lowlevel(colmap, new_brush, new_brush, text_surface->w,text_surface->h, text_surface->w);
508
509 // Also, make the BG color in brush palette have same RGB values as
510 // the current BG color : this will help for remaps.
511 palette[Back_color].R=Main_palette[Back_color].R;
512 palette[Back_color].G=Main_palette[Back_color].G;
513 palette[Back_color].B=Main_palette[Back_color].B;
514 }
515 }
516 else
517 {
518 // Solid text: Was rendered as white on black. Now map colors:
519 // White becomes FG color, black becomes BG. 2-color palette.
520 // Exception: if BG==FG, FG will be set to black or white - any different color.
521 long index;
522 byte new_fore=Fore_color;
523
524 if (Fore_color==Back_color)
525 {
526 new_fore=Best_color_perceptual_except(Main_palette[Back_color].R, Main_palette[Back_color].G, Main_palette[Back_color].B, Back_color);
527 }
528
529 for (index=0; index < text_surface->w * text_surface->h; index++)
530 {
531 if (palette[*(new_brush+index)].G < 128)
532 *(new_brush+index)=Back_color;
533 else
534 *(new_brush+index)=new_fore;
535 }
536
537 // Now copy the current palette to brushe's, for consistency
538 // with the indices.
539 memcpy(palette, Main_palette, sizeof(T_Palette));
540
541 }
542 *width=text_surface->w;
543 *height=text_surface->h;
544 SDL_FreeSurface(text_surface);
545 TTF_CloseFont(font);
546 return new_brush;
547}
548#endif
549
550
551byte *Render_text_SFont(const char *str, int font_number, int *width, int *height, T_Palette palette)
552{
553 SFont_Font *font;
554 SDL_Surface * text_surface;
555 SDL_Surface *font_surface;
556 byte * new_brush;
557 SDL_Rect rectangle;
558
559 // Chargement de la fonte
560 font_surface=IMG_Load(Font_name(font_number));
561 if (!font_surface)
562 {
563 Verbose_message("Warning","Error loading font.\nThe file may be corrupt.");
564 return NULL;
565 }
566 // Font is 24bit: Perform a color reduction
567 if (font_surface->format->BitsPerPixel>8)
568 {
569 SDL_Surface * reduced_surface;
570 int x,y,color;
571 SDL_Color rgb;
572
573 reduced_surface=SDL_CreateRGBSurface(SDL_SWSURFACE, font_surface->w, font_surface->h, 8, 0, 0, 0, 0);
574 if (!reduced_surface)
575 {
576 SDL_FreeSurface(font_surface);
577 return NULL;
578 }
579 // Set the quick palette
580 for (color=0;color<256;color++)
581 {
582 rgb.r=((color & 0xE0)>>5)<<5;
583 rgb.g=((color & 0x1C)>>2)<<5;
584 rgb.b=((color & 0x03)>>0)<<6;
585 SDL_SetColors(reduced_surface, &rgb, color, 1);
586 }
587 // Perform reduction
588 for (y=0; y<font_surface->h; y++)
589 for (x=0; x<font_surface->w; x++)
590 {
591 SDL_GetRGB(Get_SDL_pixel_hicolor(font_surface, x, y), font_surface->format, &rgb.r, &rgb.g, &rgb.b);
592 color=((rgb.r >> 5) << 5) |
593 ((rgb.g >> 5) << 2) |
594 ((rgb.b >> 6));
595 Set_SDL_pixel_8(reduced_surface, x, y, color);
596 }
597
598 SDL_FreeSurface(font_surface);
599 font_surface=reduced_surface;
600 }
601 font=SFont_InitFont(font_surface);
602 if (!font)
603 {
604 DEBUG("Font init failed",1);
605 SDL_FreeSurface(font_surface);
606 return NULL;
607 }
608
609 // Calcul des dimensions
610 *height=SFont_TextHeight(font, str);
611 *width=SFont_TextWidth(font, str);
612 // Allocation d'une surface SDL
613 text_surface=SDL_CreateRGBSurface(SDL_SWSURFACE, *width, *height, 8, 0, 0, 0, 0);
614 // Copy palette
615 SDL_SetPalette(text_surface, SDL_LOGPAL, font_surface->format->palette->colors, 0, 256);
616 // Fill with transparent color
617 rectangle.x=0;
618 rectangle.y=0;
619 rectangle.w=*width;
620 rectangle.h=*height;
621 SDL_FillRect(text_surface, &rectangle, font->Transparent);
622 // Rendu du texte
623 SFont_Write(text_surface, font, 0, 0, str);
624 if (!text_surface)
625 {
626 DEBUG("Rendering failed",2);
627 SFont_FreeFont(font);
628 return NULL;
629 }
630
631 new_brush=Surface_to_bytefield(text_surface, NULL);
632 if (!new_brush)
633 {
634 DEBUG("Converting failed",3);
635 SDL_FreeSurface(text_surface);
636 SFont_FreeFont(font);
637 return NULL;
638 }
639
640 Get_SDL_Palette(font_surface->format->palette, palette);
641
642 // Swap current BG color with font's transparent color
643 if (font->Transparent != Back_color)
644 {
645 int c;
646 byte colmap[256];
647 // Swap palette entries
648
649 SWAP_BYTES(palette[font->Transparent].R, palette[Back_color].R)
650 SWAP_BYTES(palette[font->Transparent].G, palette[Back_color].G)
651 SWAP_BYTES(palette[font->Transparent].B, palette[Back_color].B)
652
653 // Define a colormap
654 for (c=0; c<256; c++)
655 colmap[c]=c;
656
657 // The swap
658 colmap[font->Transparent]=Back_color;
659 colmap[Back_color]=font->Transparent;
660
661 Remap_general_lowlevel(colmap, new_brush, new_brush, text_surface->w,text_surface->h, text_surface->w);
662
663 }
664
665 SDL_FreeSurface(text_surface);
666 SFont_FreeFont(font);
667
668 return new_brush;
669}
670
671// Crée une brosse à partir des paramètres de texte demandés.
672// Si cela réussit, la fonction place les dimensions dans width et height,
673// et retourne l'adresse du bloc d'octets.
674byte *Render_text(const char *str, int font_number, int size, int antialias, int bold, int italic, int *width, int *height, T_Palette palette)
675{
676 #ifdef NOTTF
677 (void) size; // unused
678 (void) antialias; // unused
679 (void) bold; // unused
680 (void) italic; // unused
681 #endif
682 T_Font *font = font_list_start;
683 int index=font_number;
684
685 // Verification type de la fonte
686 if (font_number<0 ||font_number>=Nb_fonts)
687 return NULL;
688
689 while (index--)
690 font = font->Next;
691 if (font->Is_truetype)
692 {
693 #ifndef NOTTF
694 return Render_text_TTF(str, font_number, size, antialias, bold, italic, width, height, palette);
695 #else
696 return NULL;
697 #endif
698 }
699 else
700 {
701 return Render_text_SFont(str, font_number, width, height, palette);
702 }
703}
704
705