1 module unde.font;
2 
3 import derelict.sdl2.sdl;
4 import derelict.sdl2.ttf;
5 version(FreeType)
6 {
7     import derelict.freetype.ft;
8 }
9 
10 import unde.global_state;
11 import unde.lib;
12 
13 import core.exception;
14 import core.memory;
15 
16 import std.math;
17 import std.stdio;
18 import std.conv;
19 import std..string;
20 
21 version(Windows)
22 {
23 import berkeleydb.all: ssize_t;
24 }
25 
26 
27 struct CharSize
28 {
29     string chr;
30     int size;
31     SDL_Color color;
32     size_t font;
33 }
34 
35 struct LineSize
36 {
37     string line;
38     int size;
39     int line_width;
40     int line_height;
41     SDL_Color color;
42     ushort[] attrs;
43     ssize_t start_pos;
44     ssize_t end_pos;
45 }
46 
47 enum Attr
48 {
49     Black,
50     Red,
51     Green,
52     Brown,
53     Blue,
54     Magenta,
55     Cyan,
56     White,
57     Bold = 0x100,
58     Underscore = 0x200,
59     HalfBright = 0x400,
60     Blink = 0x800,
61     Color = 0x1000,
62 }
63 
64 class Font
65 {
66     SDL_Renderer *renderer;
67     version(FreeType)
68     {
69         FT_Library  library;
70         FT_Face[5] face;
71     }
72     TTF_Font *[16][5]font;
73     SDL_Texture*[16] attr_textures;
74 
75     long last_chars_cache_use;
76     Texture_Tick[CharSize] chars_cache;
77     long last_lines_cache_use;
78     Texture_Tick[LineSize] lines_cache;
79     
80     auto
81     get_char_from_cache(in string chr, in int size, in SDL_Color color, size_t f = 0)
82     {
83         auto charsize = CharSize(chr.idup(), size, color, f);
84         auto st = charsize in chars_cache;
85         if (st)
86         {
87             st.tick = SDL_GetTicks();
88             last_chars_cache_use = SDL_GetTicks();
89         }
90         else
91         {
92             version(FreeType)
93             {
94                 auto glyph_index = FT_Get_Char_Index(face[f], to!dchar(chr), FT_LOAD_RENDER);//FT_Get_Char_Index( face[0], to!dchar(chr) );
95             }
96             else
97             {
98                 int glyph_index;
99                 dchar dchr; wchar wchr;
100                 try
101                 {
102                     dchr = to!dchar(chr);
103                     wchr = to!wchar(dchr);
104                     glyph_index = TTF_GlyphIsProvided(font[f][size], wchr);
105                 }
106                 catch (Exception e)
107                 {
108                 }
109             }
110             SDL_Surface *surface;
111             if (glyph_index > 0)
112             {
113                 if (size < 9)
114                 {
115                     surface = TTF_RenderUTF8_Solid(
116                             font[f][size], chr.toStringz(),
117                             color );
118                 }
119                 else
120                 {
121                     surface = TTF_RenderUTF8_Blended(
122                             font[f][size], chr.toStringz(),
123                             color );
124                 }
125             }
126             else
127             {
128                 if (size < 9)
129                 {
130                     surface = TTF_RenderUTF8_Solid(
131                             font[4][size], chr.toStringz(),
132                             color );
133                 }
134                 else
135                 {
136                     surface = TTF_RenderUTF8_Blended(
137                         font[4][size], chr.toStringz(),
138                         color );
139                 }
140             }
141 
142             auto texture =
143                 SDL_CreateTextureFromSurface(renderer, surface);
144 
145             if (surface)
146             {
147                 chars_cache[charsize] = Texture_Tick(surface.w, surface.h, [], texture, SDL_GetTicks());
148                 SDL_FreeSurface(surface);
149                 last_chars_cache_use = SDL_GetTicks();
150                 st = charsize in chars_cache;
151             }
152         }
153 
154         return st;
155     }
156 
157     /* EN: clear cache from old entries
158        RU: очистить кеш от старых элементов */
159     void
160     clear_chars_cache()
161     {
162         int cleared = 0;
163         foreach(k, v; chars_cache)
164         {
165             if (v.tick < last_chars_cache_use - 30_000)
166             {
167                 cleared++;
168                 if (v.texture) SDL_DestroyTexture(v.texture);
169                 if ( !chars_cache.remove(k) )
170                 {
171                     writefln("Can't remove chars cache key %s", k);
172                 }
173                 //writefln("v.tick = %s < %s. Remove key %s",
174                 //        v.tick, gs.last_pict_cache_use - 300_000, k);
175             }
176         }
177         if (cleared) 
178         {
179             //writefln("Cleared %d objects from lines cache", cleared);
180             GC.collect();
181         }
182     }
183 
184     public SDL_Rect
185     get_size_of_line(inout (char)[] text,
186             int size, long line_width, int line_height,
187             SDL_Color color)
188     {
189         text ~= " ";
190         if (text.length > 0 && text[$-1] == '\r') text = text[0..$-1];
191         int lines = 1;
192 
193         int line_ax = 0;
194         int line_ay = 0;
195 
196         SDL_Rect rect = SDL_Rect(0, 0, 1, 1);
197         for (size_t i = 0; i < text.length; i += text.mystride(i))
198         {
199             auto chrlen = text.mystride(i);
200             if (i+chrlen >= text.length) chrlen = 1;
201             string chr = text[i..i+chrlen].idup();
202             //writefln("chr = %s", chr);
203             auto st = get_char_from_cache(chr, size, color);
204             if (!st) continue;
205 
206             if (line_width > 0)
207             {
208                 if (line_ax + st.w > line_width || chr == "\n")
209                 {
210                     if (line_ax > rect.w) rect.w = line_ax;
211                     line_ax = 0;
212                     line_ay += line_height;
213                     lines++;
214                     if (chr == "\n") continue;
215                 }
216             }
217 
218             line_ax += st.w;
219         }
220 
221         if (line_ax > rect.w) rect.w = line_ax;
222         rect.h = lines*line_height;
223         return rect;
224     }
225 
226     public SDL_Rect
227     get_size_of_line(int cols, int rows,
228             int size, SDL_Color color)
229     {
230         SDL_Rect rect = SDL_Rect(0, 0, 1, 1);
231         auto st = get_char_from_cache(" ", size, color);
232         if (!st) return rect;
233 
234         rect.w = cols*st.w;
235         rect.h = rows*st.h;
236 
237         return rect;
238     }
239 
240     auto
241     get_line_from_cache(string text, 
242             int size, int line_width, int line_height, SDL_Color color, 
243             ushort[] attrs = null, ssize_t start_pos=-1, ssize_t end_pos=-1)
244     {
245         auto linesize = LineSize(text.idup(), size, line_width, line_height, 
246                 color, attrs.dup(), start_pos, end_pos);
247         auto tt = linesize in lines_cache;
248         if (tt)
249         {
250             tt.tick = SDL_GetTicks();
251             last_lines_cache_use = SDL_GetTicks();
252         }
253         else
254         {
255             auto rect = get_size_of_line(text, size, line_width, line_height, color);
256 
257             if (rect.w > 8192) rect.w = 8192;
258             if (rect.h > 8192) rect.h = 8192;
259             auto texture = SDL_CreateTexture(renderer,
260                     SDL_PIXELFORMAT_ARGB8888,
261                     SDL_TEXTUREACCESS_TARGET,
262                     rect.w,
263                     rect.h);
264             if( !texture )
265             {
266                 throw new Exception(format("get_line_from_cache: Error while creating texture: %s",
267                         SDL_GetError().to!string() ));
268             }
269 
270             auto old_texture = SDL_GetRenderTarget(renderer);
271             int r = SDL_SetRenderTarget(renderer, texture);
272             if (r < 0)
273             {
274                 throw new Exception(format("get_line_from_cache: Error while set render target texture: %s",
275                         SDL_GetError().to!string() ));
276             }
277 
278             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
279             r = SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
280             if (r < 0)
281             {
282                 writefln("Can't SDL_SetRenderDrawColor: %s",
283                         to!string(SDL_GetError()));
284             }
285             r = SDL_RenderClear(renderer);
286             if (r < 0)
287             {
288                 throw new Exception(format("Error while clear renderer: %s",
289                         SDL_GetError().to!string() ));
290             }
291 
292             text ~= " ";
293             if (text.length > 0 && text[$-1] == '\r') text = text[0..$-1];
294 
295             long line_ax = 0;
296             long line_ay = 0;
297 
298             SDL_Rect[] chars = [];
299 
300             ssize_t index;
301             ssize_t attrs_i;
302                 //writefln("text=%s", text);
303                 //writefln("attrs=%s", attrs);
304             for (size_t i=0; i < text.length; i+=text.mystride(i), attrs_i++)
305             {
306                 auto chrlen = text.mystride(i);
307                 if (i+chrlen >= text.length) chrlen = 1;
308                 string chr = text[i..i+chrlen].idup();
309                 SDL_Color real_color = color;
310                 ushort attr = (Attr.Black<<4 | Attr.White);
311                 if ( attrs && attrs_i < attrs.length )
312                     attr = attrs[attrs_i];
313                 else
314                     attr = Attr.Black<<4 | Attr.White;
315                 if (i >= start_pos && i <= end_pos)
316                     attr = attr & 0x0F | (Attr.Blue << 4);
317                 if ( attr != (Attr.Black<<4 | Attr.White) && !(attr & Attr.Color) )
318                 {
319                     switch (attr & 0x0F)
320                     {
321                         case Attr.Black:
322                             real_color = SDL_Color(0x00, 0x00, 0x00, 0xFF);
323                             break;
324                         case Attr.Red:
325                             real_color = SDL_Color(0xFF, 0x00, 0x00, 0xFF);
326                             break;
327                         case Attr.Green:
328                             real_color = SDL_Color(0x00, 0xFF, 0x00, 0xFF);
329                             break;
330                         case Attr.Brown:
331                             real_color = SDL_Color(0xFF, 0xFF, 0x30, 0xFF);
332                             break;
333                         case Attr.Blue:
334                             real_color = SDL_Color(0x00, 0x00, 0xFF, 0xFF);
335                             break;
336                         case Attr.Magenta:
337                             real_color = SDL_Color(0xFF, 0x00, 0xFF, 0xFF);
338                             break;
339                         case Attr.Cyan:
340                             real_color = SDL_Color(0x00, 0xFF, 0xFF, 0xFF);
341                             break;
342                         case Attr.White:
343                             real_color = SDL_Color(0xFF, 0xFF, 0xFF, 0xFF);
344                             break;
345                         default:
346                             break;
347                     }
348                 }
349                 auto st = get_char_from_cache(chr, size, real_color, (attr&Attr.Bold?1:0));
350                 if (!st) continue;
351 
352                 SDL_Rect dst;
353                 dst.x = cast(int)line_ax;
354                 dst.y = cast(int)line_ay;
355                 dst.w = st.w;
356                 dst.h = st.h;
357 
358                 chars.length = i+1;
359                 chars[i] = dst;
360 
361                 if (line_width > 0)
362                 {
363                     if (line_ax + st.w > line_width || chr == "\n")
364                     {
365                         line_ax = 0;
366                         line_ay += line_height;
367                         if (chr == "\n") continue;
368                     }
369                 }
370                 /*writefln("%s - %s, %s, %s, %s",
371                         line, dst.x, dst.y, dst.w, dst.h);*/
372 
373                 dst.x = cast(int)line_ax;
374                 dst.y = cast(int)line_ay;
375                 dst.w = st.w;
376                 dst.h = st.h;
377 
378                 if ( ((attr & 0xF0)>>4) != Attr.Black )
379                 {
380                     r = SDL_RenderCopyEx(renderer, attr_textures[(attr & 0xF0)>>4], null, &dst, 0,
381                                 null, SDL_FLIP_NONE);
382                     if (r < 0)
383                     {
384                         writefln( "draw_line(): Error while render copy: %s", SDL_GetError().to!string() );
385                     }
386                 }
387 
388                 r = SDL_RenderCopyEx(renderer, st.texture, null, &dst, 0,
389                             null, SDL_FLIP_NONE);
390                 if (r < 0)
391                 {
392                     writefln( "draw_line(): Error while render copy: %s", SDL_GetError().to!string() );
393                 }
394 
395                 if (attr & Attr.Underscore)
396                 {
397                     dst.y += dst.h - 2;
398                     dst.h = 1;
399 
400                     r = SDL_RenderCopyEx(renderer, attr_textures[attr & 0xF], null, &dst, 0,
401                                 null, SDL_FLIP_NONE);
402                     if (r < 0)
403                     {
404                         writefln( "draw_line(): Error while render copy: %s", SDL_GetError().to!string() );
405                     }
406                 }
407 
408                 line_ax += st.w;
409             }
410 
411             r = SDL_SetRenderTarget(renderer, old_texture);
412             if (r < 0)
413             {
414                 throw new Exception(format("get_line_from_cache: Error while restore render target old_texture: %s",
415                         SDL_GetError().to!string() ));
416             }
417 
418             lines_cache[linesize] = Texture_Tick(rect.w, rect.h, chars, texture, SDL_GetTicks());
419             last_lines_cache_use = SDL_GetTicks();
420             tt = linesize in lines_cache;
421         }
422 
423         return tt;
424     }
425 
426     auto
427     get_line_from_cache(dchar[] text, int cols, int rows,
428             int size, int line_height, SDL_Color color, ushort[] attrs = null,
429             ssize_t start_pos=-1, ssize_t end_pos=-1)
430     {
431         auto linesize = LineSize(to!(string)(text.idup()), size, cols*rows, 
432                 line_height, color, attrs.dup(), start_pos, end_pos);
433         auto tt = linesize in lines_cache;
434         if (tt)
435         {
436             tt.tick = SDL_GetTicks();
437             last_lines_cache_use = SDL_GetTicks();
438         }
439         else
440         {
441             auto rect = get_size_of_line(cols, rows, size, color);
442 
443             if (rect.w > 8192) rect.w = 8192;
444             if (rect.h > 8192) rect.h = 8192;
445             auto texture = SDL_CreateTexture(renderer,
446                     SDL_PIXELFORMAT_ARGB8888,
447                     SDL_TEXTUREACCESS_TARGET,
448                     rect.w,
449                     rect.h);
450             if( !texture )
451             {
452                 throw new Exception(format("get_line_from_cache: Error while creating texture: %s",
453                         SDL_GetError().to!string() ));
454             }
455 
456             auto old_texture = SDL_GetRenderTarget(renderer);
457             int r = SDL_SetRenderTarget(renderer, texture);
458             if (r < 0)
459             {
460                 throw new Exception(format("get_line_from_cache: Error while set render target texture: %s",
461                         SDL_GetError().to!string() ));
462             }
463 
464             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
465             r = SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
466             if (r < 0)
467             {
468                 writefln("Can't SDL_SetRenderDrawColor: %s",
469                         to!string(SDL_GetError()));
470             }
471             r = SDL_RenderClear(renderer);
472             if (r < 0)
473             {
474                 throw new Exception(format("Error while clear renderer: %s",
475                         SDL_GetError().to!string() ));
476             }
477 
478             long line_ax = 0;
479             long line_ay = 0;
480 
481             SDL_Rect[] chars = [];
482             chars.length = text.length;
483 
484             ssize_t index;
485                 //writefln("text=%s", text);
486                 //writefln("attrs=%s", attrs);
487             for (size_t i=0; i < text.length; i++)
488             {
489                 string chr = to!string(text[i]);
490                 SDL_Color real_color = color;
491                 ushort attr = (Attr.Black<<4 | Attr.White);
492                 if ( attrs )
493                     attr = attrs[i];
494                 else attr = Attr.Black<<4 | Attr.White;
495                 if (i >= start_pos && i <= end_pos)
496                     attr = attr & 0x0F | (Attr.Blue << 4);
497                 if ( attr != (Attr.Black<<4 | Attr.White) && !(attr & Attr.Color) )
498                 {
499                     switch (attr & 0x0F)
500                     {
501                         case Attr.Black:
502                             real_color = SDL_Color(0x00, 0x00, 0x00, 0xFF);
503                             break;
504                         case Attr.Red:
505                             real_color = SDL_Color(0xFF, 0x00, 0x00, 0xFF);
506                             break;
507                         case Attr.Green:
508                             real_color = SDL_Color(0x00, 0xFF, 0x00, 0xFF);
509                             break;
510                         case Attr.Brown:
511                             real_color = SDL_Color(0xFF, 0xFF, 0x30, 0xFF);
512                             break;
513                         case Attr.Blue:
514                             real_color = SDL_Color(0x80, 0x80, 0xFF, 0xFF);
515                             break;
516                         case Attr.Magenta:
517                             real_color = SDL_Color(0xFF, 0x00, 0xFF, 0xFF);
518                             break;
519                         case Attr.Cyan:
520                             real_color = SDL_Color(0x00, 0xFF, 0xFF, 0xFF);
521                             break;
522                         case Attr.White:
523                             real_color = SDL_Color(0xFF, 0xFF, 0xFF, 0xFF);
524                             break;
525                         default:
526                             break;
527                     }
528                 }
529                 auto st = get_char_from_cache(chr, size, real_color, (attr&Attr.Bold?1:0));
530                 if (!st) continue;
531 
532                 if (i > 0 && i%cols == 0)
533                 {
534                     line_ax = 0;
535                     line_ay += line_height;
536                 }
537 
538                 SDL_Rect dst;
539                 dst.x = cast(int)line_ax;
540                 dst.y = cast(int)line_ay;
541                 dst.w = st.w;
542                 dst.h = st.h;
543 
544                 chars[i] = dst;
545 
546                 if ( ((attr & 0xF0)>>4) != Attr.Black )
547                 {
548                     r = SDL_RenderCopyEx(renderer, attr_textures[(attr & 0xF0)>>4], null, &dst, 0,
549                                 null, SDL_FLIP_NONE);
550                     if (r < 0)
551                     {
552                         writefln( "draw_line(): Error while render copy: %s", SDL_GetError().to!string() );
553                     }
554                 }
555 
556                 r = SDL_RenderCopyEx(renderer, st.texture, null, &dst, 0,
557                             null, SDL_FLIP_NONE);
558                 if (r < 0)
559                 {
560                     writefln( "draw_line(): Error while render copy: %s", SDL_GetError().to!string() );
561                 }
562 
563                 if ( attr & Attr.Underscore )
564                 {
565                     dst.y += dst.h - 4;
566                     dst.h = 1;
567 
568                     r = SDL_RenderCopyEx(renderer, attr_textures[attr & 0xF], null, &dst, 0,
569                                 null, SDL_FLIP_NONE);
570                     if (r < 0)
571                     {
572                         writefln( "draw_line(): Error while render copy: %s", SDL_GetError().to!string() );
573                     }
574                 }
575 
576                 line_ax += st.w;
577             }
578 
579             r = SDL_SetRenderTarget(renderer, old_texture);
580             if (r < 0)
581             {
582                 throw new Exception(format("get_line_from_cache: Error while restore render target old_texture: %s",
583                         SDL_GetError().to!string() ));
584             }
585 
586             lines_cache[linesize] = Texture_Tick(rect.w, rect.h, chars, texture, SDL_GetTicks());
587             last_lines_cache_use = SDL_GetTicks();
588             tt = linesize in lines_cache;
589         }
590 
591         return tt;
592     }
593 
594     /* EN: clear cache from old entries
595        RU: очистить кеш от старых элементов */
596     void
597     clear_lines_cache()
598     {
599         int cleared;
600         foreach(k, v; lines_cache)
601         {
602             if (v.tick < last_lines_cache_use - 30_000)
603             {
604                 cleared++;
605                 if (v.texture) SDL_DestroyTexture(v.texture);
606                 if (!lines_cache.remove(k))
607                 {
608                     writefln("NOT DELETED key %s", k.line);
609                     writefln("k in lines_cache %s", k in lines_cache);
610                 }
611                 //writefln("v.tick = %s < %s. Remove key %s",
612                 //        v.tick, last_lines_cache_use - 30_000, k);
613             }
614         }
615         if (cleared) 
616         {
617             //writefln("Cleared %d objects from lines cache", cleared);
618             GC.collect();
619         }
620     }
621 
622     this(SDL_Renderer *renderer)
623     {
624         this.renderer = renderer;
625 
626         version(FreeType)
627         {
628             DerelictFT.load();
629             auto err = FT_Init_FreeType(&library);
630             if (err != 0)
631             {
632                 throw new Exception(format("FT_Init_FreeType: %s\n",
633                             err));
634             }
635         }
636 
637         DerelictSDL2ttf.load();
638 
639         if(TTF_Init()==-1) {
640             throw new Exception(format("TTF_Init: %s\n",
641                         TTF_GetError().to!string()));
642         }
643 
644         version (linux)
645         {
646             version(Mageia)
647             {
648 		string[] font_list = ["/usr/share/fonts/TTF/liberation/LiberationMono-Regular.ttf",
649 			"/usr/share/fonts/TTF/liberation/LiberationMono-Bold.ttf",
650 			"/usr/share/fonts/TTF/liberation/LiberationMono-Italic.ttf",
651 			"/usr/share/fonts/TTF/liberation/LiberationMono-BoldItalic.ttf",
652 			"/usr/share/fonts/gdouros-symbola/Symbola.ttf"];
653 	    }
654 	    else version(Manjaro)
655             {
656 		string[] font_list = ["/usr/share/fonts/TTF/LiberationMono-Regular.ttf",
657 			"/usr/share/fonts/TTF/LiberationMono-Bold.ttf",
658 			"/usr/share/fonts/TTF/LiberationMono-Italic.ttf",
659 			"/usr/share/fonts/TTF/LiberationMono-BoldItalic.ttf",
660 			"/usr/share/fonts/TTF/Symbola.ttf"];
661 	    }
662 	    else version(OpenSUSE)
663             {
664 		string[] font_list = ["/usr/share/fonts/truetype/LiberationMono-Regular.ttf",
665 			"/usr/share/fonts/truetype/LiberationMono-Bold.ttf",
666 			"/usr/share/fonts/truetype/LiberationMono-Italic.ttf",
667 			"/usr/share/fonts/truetype/LiberationMono-BoldItalic.ttf",
668 			"/usr/share/fonts/truetype/Symbola.ttf"];
669             }
670             else version(Fedora)
671             {
672                 string[] font_list = ["/usr/share/fonts/liberation/LiberationMono-Regular.ttf",
673                     "/usr/share/fonts/liberation/LiberationMono-Bold.ttf",
674                     "/usr/share/fonts/liberation/LiberationMono-Italic.ttf",
675                     "/usr/share/fonts/liberation/LiberationMono-BoldItalic.ttf",
676                     "/usr/share/fonts/dejavu/DejaVuSans.ttf"];
677             }
678             else version (Ubuntu_14_04)
679             {
680                 string[] font_list = ["/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
681                     "/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf",
682                     "/usr/share/fonts/truetype/liberation/LiberationMono-Italic.ttf",
683                     "/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf",
684                     "/usr/share/fonts/truetype/ttf-ancient-scripts/Symbola605.ttf"];
685             }
686             else
687             {
688                 string[] font_list = ["/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
689                     "/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf",
690                     "/usr/share/fonts/truetype/liberation/LiberationMono-Italic.ttf",
691                     "/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf",
692                     "/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf"];
693             }
694         }
695         else version (Windows)
696         {
697             string[] font_list = ["C:\\Windows\\Fonts\\cour.ttf",
698                 "C:\\Windows\\Fonts\\cour.ttf",
699                 "C:\\Windows\\Fonts\\cour.ttf",
700                 "C:\\Windows\\Fonts\\cour.ttf",
701 		// Good fonts: MS GOTHIC, Segoe UI Emoji, Segoe UI Symbol
702                 "C:\\Windows\\Fonts\\seguisym.ttf"];
703         }
704 
705         // fonts with sizes 6, 8, 11, 16, 23, 32, 45, 64, 91, 128, 181
706         foreach(f, fontname; font_list)
707         {
708             foreach(i; 5..16)
709             {
710                 font[f][i]=TTF_OpenFont(
711                         fontname.toStringz(),
712                         cast(int)round(SQRT2^^i));
713                 if(!font[f][i]) {
714                     throw new Exception(format("TTF_OpenFont: %s\n",
715                             TTF_GetError().to!string()));
716                 }
717             }
718 
719             version(FreeType)
720             {
721                 err = FT_New_Face( library, toStringz(fontname.dup), 0, &face[f] );
722                 if (err) 
723                 {
724                     throw new Exception(format("FT_Open_Face 1: %s\n",
725                                 err));
726                 }
727             }
728         }
729 
730         SDL_Surface* surface = SDL_CreateRGBSurface(0,
731                 1,
732                 1,
733                 32, 0x00FF0000, 0X0000FF00, 0X000000FF, 0XFF000000);
734 
735         for (auto c = 0; c < 16; c++)
736         {
737             SDL_Color back_color;
738             switch (c)
739             {
740                 case Attr.Black:
741                     back_color = SDL_Color(0x00, 0x00, 0x00, 0xFF);
742                     break;
743                 case Attr.Red:
744                     back_color = SDL_Color(0xFF, 0x00, 0x00, 0xFF);
745                     break;
746                 case Attr.Green:
747                     back_color = SDL_Color(0x00, 0xFF, 0x00, 0xFF);
748                     break;
749                 case Attr.Brown:
750                     back_color = SDL_Color(0x80, 0x80, 0x00, 0xFF);
751                     break;
752                 case Attr.Blue:
753                     back_color = SDL_Color(0x00, 0x00, 0xFF, 0xFF);
754                     break;
755                 case Attr.Magenta:
756                     back_color = SDL_Color(0xFF, 0x00, 0xFF, 0xFF);
757                     break;
758                 case Attr.Cyan:
759                     back_color = SDL_Color(0x00, 0x80, 0x80, 0xFF);
760                     break;
761                 case Attr.White:
762                     back_color = SDL_Color(0x80, 0x80, 0x80, 0xFF);
763                     break;
764                 case 8+Attr.Black:
765                     back_color = SDL_Color(0x00, 0x00, 0x00, 0xFF);
766                     break;
767                 case 8+Attr.Red:
768                     back_color = SDL_Color(0xFF, 0x00, 0x00, 0xFF);
769                     break;
770                 case 8+Attr.Green:
771                     back_color = SDL_Color(0x00, 0xFF, 0x00, 0xFF);
772                     break;
773                 case 8+Attr.Brown:
774                     back_color = SDL_Color(0x80, 0x80, 0x00, 0xFF);
775                     break;
776                 case 8+Attr.Blue:
777                     back_color = SDL_Color(0x00, 0x00, 0xFF, 0xFF);
778                     break;
779                 case 8+Attr.Magenta:
780                     back_color = SDL_Color(0xFF, 0x00, 0xFF, 0xFF);
781                     break;
782                 case 8+Attr.Cyan:
783                     back_color = SDL_Color(0x00, 0xFF, 0xFF, 0xFF);
784                     break;
785                 case 8+Attr.White:
786                     back_color = SDL_Color(0xFF, 0xFF, 0xFF, 0xFF);
787                     break;
788                 default:
789                     break;
790             }
791 
792             (cast(uint*) surface.pixels)[0] = (back_color.a<<24) | 
793                 (back_color.r << 16) | (back_color.g << 8) | back_color.b;
794 
795             attr_textures[c] =
796                 SDL_CreateTextureFromSurface(renderer, surface);
797         }
798 
799         SDL_FreeSurface(surface);
800     }
801 
802     ~this()
803     {
804         version(FreeType)
805         {
806             foreach(f; 0..5)
807             {
808                 FT_Done_Face( face[f] );
809             }
810         }
811 
812         foreach(f; 0..5)
813         {
814             foreach(i; 5..16)
815             {
816                 TTF_CloseFont(font[f][i]);
817             }
818         }
819 
820         for (auto c = Attr.Black; c <= Attr.White; c++)
821         {
822             SDL_DestroyTexture(attr_textures[c]);
823         }
824 
825         TTF_Quit();
826     }
827 }