1 // Copyright (C) 2006 Keita Mochizuki
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 #ifndef DLIB_IGG_FONT_RENDERER_H_
4 #define DLIB_IGG_FONT_RENDERER_H_
5 #ifndef DLIB_NO_GUI_SUPPORT
6 
7 #include "../platform.h"
8 
9 
10 #include "../gui_widgets/fonts.h"
11 #include "../unicode.h"
12 #include "../uintn.h"
13 
14 #include <map>
15 #include <memory>
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <locale.h>
21 
22 #if defined(WIN32)
23 #include <windows.h>
24 #include <mbstring.h>
25 #elif defined(DLIB_POSIX)
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <X11/Xlocale.h>
32 #endif
33 
34 namespace nativefont
35 {
36 // ----------------------------------------------------------------------------------------
37 
38     namespace font_renderer
39     {
40         typedef dlib::uint8 byte;
41 
42 
43 #ifdef WIN32
44         template <typename T> struct input2native_trait{
45         };
46         template <> struct input2native_trait<char>{
47             typedef char type_t;
48         };
49         template <> struct input2native_trait<wchar_t>{
50             typedef wchar_t type_t;
51         };
52         template <> struct input2native_trait<dlib::unichar>{
53             typedef wchar_t type_t;
54         };
55 #endif
56         // T : N : sizeof_source_type
57         template <int N> struct size2inner_trait{
58         };
59         template <> struct size2inner_trait<1>{
60             typedef char type_t;
61         };
62         template <> struct size2inner_trait<2>{
63             typedef dlib::uint16 type_t;
64         };
65         template <> struct size2inner_trait<4>{
66             typedef dlib::unichar type_t;
67         };
68 
69 
70 // ----------------------------------------------------------------------------------------
71 
72         template <int N> struct create_helper{ };
73         template <> struct create_helper<1>{
74             typedef char type_t;
75             type_t *istr;
76             int len;
77             create_helper(char *str){
78                 len = (int)strlen(str);
79                 istr = str;
80             }
81             ~create_helper(){}
82         };
83         template <> struct create_helper<2>{
84             typedef wchar_t type_t;
85             type_t *istr;
86             bool allocated;
87             int len;
88             create_helper(wchar_t *str){
89                 allocated = false;
90                 len = (int)wcslen(str);
91                 istr = str;
92             }
93             create_helper(dlib::unichar *str){
94                 allocated = true;
95                 len = 0;
96                 int unicount = 0;
97                 dlib::unichar *p = str;
98                 while(*p){
99                     if (*p > 0xffff){
100                         len += 2;
101                     }else{
102                         len++;
103                     }
104                     unicount++;
105                     p++;
106                 }
107                 istr = new wchar_t[len+1];
108                 for (int i = 0, wi = 0; i < unicount; ++i){
109                     dlib::unichar high, low;
110                     if (str[i] > 0xffff){
111                         dlib::unichar_to_surrogate_pair(str[i], high, low);
112                         istr[wi] = (wchar_t)high, istr[wi+1] = (wchar_t)low;
113                         wi += 2;
114                     }else{
115                         istr[wi] = (wchar_t)str[i];
116                         wi += 1;
117                     }
118                 }
119                 istr[len] = L'\0';
120             }
121 
122             ~create_helper(){
123                 if (allocated) delete[] istr;
124             }
125         };
126         template <> struct create_helper<4>{
127             typedef wchar_t type_t;
128             type_t *istr;
129             int len;
130             create_helper(dlib::unichar *str){
131                 len = (int)wcslen((wchar_t *)str);
132                 istr = (type_t *)str;
133             }
134             ~create_helper(){}
135         };
136 
137 // ----------------------------------------------------------------------------------------
138 
139         class font_renderer{
140         public:
141 
142             struct rgb_type{
143                 byte r, g, b;
144                 rgb_type() : r(0), g(0), b(0){};
145                 rgb_type(byte r_, byte g_, byte b_) : r(r_), g(g_), b(b_){};
146             };
147         private:
148 
149             byte *image;
150             int width, height;
151             void destroy(){
152                 width = height = 0;
153                 delete image;
154                 image = 0;
155             }
156             struct vals_internal{
157                 int width, height;
158 #ifdef WIN32
159                 COLORREF rgb2RGB(rgb_type &rgb){
160                     return RGB(rgb.r, rgb.g, rgb.b);
161                 }
162                 HBITMAP hBmp, hBmpOld;
163                 HDC hDCBmp;
164                 BYTE *pixelint;
165                 HFONT hFont, hFontOld;
166                 HBRUSH hBrush;
167                 int pix_width_prev, pix_height_prev;
168                 bool first;
169                 int ascender, descender;
170                 int height_prev;
171                 char attribute_prev;
172 
173                 template <typename T> void create(T *str, int height_want, bool italic, bool bold, bool fixed, rgb_type &background, rgb_type &foreground){
174                     struct inner{
175                         inline static BOOL GetTextExtentPoint32(HDC hDC, LPCSTR str, int len, LPSIZE lpsize){
176                             return ::GetTextExtentPoint32A(hDC, str, len, lpsize);
177                         }
178                         inline static BOOL GetTextExtentPoint32(HDC hDC, LPCWSTR str, int len, LPSIZE lpsize){
179                             return ::GetTextExtentPoint32W(hDC, str, len, lpsize);
180                         }
181                         inline static BOOL TextOut(HDC hDC, int nxstart, int nystart, LPCSTR str, int cbstr){
182                             return ::TextOutA(hDC, nxstart, nystart, str, cbstr);
183                         }
184                         inline static BOOL TextOut(HDC hDC, int nxstart, int nystart, LPCWSTR str, int cbstr){
185                             return ::TextOutW(hDC, nxstart, nystart, str, cbstr);
186                         }
187                     };
188 
189                     create_helper<sizeof(typename input2native_trait<T>::type_t)> ch(str);
190 
191                     if (hDCBmp == NULL){
192                         HWND hWnd = GetDesktopWindow();
193                         HDC hDC = GetDC(hWnd);
194                         hDCBmp = CreateCompatibleDC(hDC);
195                         ReleaseDC(hWnd, hDC);
196                     }
197                     SetTextColor(hDCBmp, rgb2RGB(foreground));
198                     SetBkColor(hDCBmp, rgb2RGB(background));
199 
200                     char attribute = (italic ? 1 : 0) | (bold ? 2 : 0) | (fixed ? 4 : 0);
201                     if (!hFont || height_prev != height || attribute != attribute_prev){
202                         attribute_prev = attribute;
203                         height_prev = height_want;
204                         if (hFont){
205                             SelectObject(hDCBmp, hFontOld);
206                             DeleteObject(hFont);
207                         }
208                         hFont = CreateFont(height_want, 0, 0, 0, bold ? FW_BOLD : FW_DONTCARE, italic ? TRUE : FALSE,
209                                            FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
210                                            fixed ? (FIXED_PITCH | FF_DONTCARE) : (VARIABLE_PITCH | FF_DONTCARE), NULL);
211                         hFontOld = (HFONT)SelectObject(hDCBmp, hFont);
212                     }
213 
214                     {
215                         SIZE sz;
216                         inner::GetTextExtentPoint32(hDCBmp, ch.istr, ch.len, &sz);
217                         width = ((sz.cx + 3) / 4) * 4;
218                         height = sz.cy;
219                     }
220 
221                     if (pix_width_prev < width || pix_height_prev < height){
222                         if (hBmp){
223                             SelectObject(hDCBmp, hBmpOld);
224                             DeleteObject(hBmp);
225                         }
226                         pix_width_prev = width * 2;
227                         pix_height_prev = height * 2;
228                         BITMAPINFO bi;
229                         ZeroMemory(&bi, sizeof(bi));
230                         bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
231                         bi.bmiHeader.biBitCount = 24;
232                         bi.bmiHeader.biPlanes = 1;
233                         bi.bmiHeader.biWidth = pix_width_prev;
234                         bi.bmiHeader.biHeight = -pix_height_prev;
235                         hBmp = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (void **)&pixelint, NULL, 0);
236                         hBmpOld = (HBITMAP)SelectObject(hDCBmp, hBmp);
237                     }
238 
239                     {
240                         HBRUSH hBrush = CreateSolidBrush(rgb2RGB(background));
241                         RECT rc;
242                         rc.left = rc.top = 0;
243                         rc.right = pix_width_prev;
244                         rc.bottom = pix_height_prev;
245                         FillRect(hDCBmp, &rc, hBrush);
246                     }
247 
248                     inner::TextOut(hDCBmp, 0, 0, ch.istr, ch.len);
249                     TEXTMETRICW tm;
250                     GetTextMetricsW(hDCBmp,&tm);
251                     ascender = tm.tmAscent;
252                     descender = tm.tmDescent;
253                 }
254 
255                 template <typename T> vals_internal(T *str, int height_want, bool italic = false,
256                     bool bold = false, bool fixed = false, rgb_type background = rgb_type(), rgb_type foreground = rgb_type()){
257                     first = true;
258                     hFont = NULL;
259                     hDCBmp = 0;
260                     hBmpOld = 0;
261                     hBmp = 0;
262                     hDCBmp = 0;
263                     pixelint = 0;
264                     pix_width_prev = pix_height_prev = 0;
265                     height_prev = -1;
266                     attribute_prev = 0;
267                     create(str, height_want, italic, bold, fixed, background, foreground);
268                     first = false;
269                 }
270 
271                 inline int get_ascender(){
272                     return ascender;
273                 }
274 
275                 inline int get_descender(){
276                     return descender;
277                 }
278 
279                 inline void get_pixel(int x, int y, byte &r, byte &g, byte &b){
280                     byte *p = pixelint + (y * pix_width_prev + x) * 3;
281                     r = *(p+2), g = *(p+1), b = *p;
282                 }
283 
284                 void destroy(){
285                     SelectObject(hDCBmp, hBmpOld);
286                     DeleteObject(hBmp);
287                     SelectObject(hDCBmp, hFontOld);
288                     DeleteObject(hFont);
289                     DeleteDC(hDCBmp);
290                     hFont = NULL;
291                     hDCBmp = 0;
292                     hBmpOld = 0;
293                     hBmp = 0;
294                     hDCBmp = 0;
295                     pixelint = 0;
296                 }
297                 ~vals_internal(){
298                     destroy();
299                 }
300 #elif defined(DLIB_POSIX)
301                 XImage *ximg;
302                 Display *d;
303                 GC gc;
304                 XFontSet fs;
305                 Pixmap pix;
306                 Colormap cmap;
307                 int ascender, descender;
308                 int pix_width_prev, pix_height_prev;
309                 char fontset_prev[256];
310                 unsigned long rgb2color(rgb_type col, Display *d, Colormap &cmap){
311                     XColor xcol;
312                     xcol.red = col.r * 257;
313                     xcol.green = col.g * 257;
314                     xcol.blue = col.b * 257;
315                     XAllocColor(d, cmap, &xcol);
316                     return xcol.pixel;
317                 }
318                 template <typename T> void create(T *str, int height_want, bool italic, bool bold, bool fixed, rgb_type background, rgb_type foreground){
319                     struct inner{
320                         inline static int XTextExtents (XFontSet fs, char *str, int len, XRectangle *ink, XRectangle *logical){
321                             return XmbTextExtents(fs, str, len, ink, logical);
322                         }
323                         inline static int XTextExtents (XFontSet fs, wchar_t *str, int len, XRectangle *ink, XRectangle *logical){
324                             return XwcTextExtents(fs, str, len, ink, logical);
325                         }
326                         inline static void XDrawString(Display *d, Window w, XFontSet fs, GC gc, int x, int y, char *str, int num_bytes){
327                             XmbDrawString(d, w, fs, gc, x, y, str, num_bytes);
328                         }
329                         inline static void XDrawString(Display *d, Window w, XFontSet fs, GC gc, int x, int y, wchar_t *str, int num_bytes){
330                             XwcDrawString(d, w, fs, gc, x, y, str, num_bytes);
331                         }
332                     };
333                     create_helper<sizeof(T)> ch((typename size2inner_trait<sizeof(T)>::type_t *)str);
334                     setlocale(LC_CTYPE, "");
335                     if (d == NULL){
336                         d = XOpenDisplay(NULL);
337                         if (d == 0)
338                         {
339                             d = XOpenDisplay(":0.0");
340                             if (d == 0)
341                             {
342                                 throw dlib::gui_error("Unable to connect to the X display.");
343                             }
344                         }
345 
346                         cmap = DefaultColormap(d, DefaultScreen(d));
347                     }
348                     char fontset[256];
349                     {
350                         char *p = fontset;
351                         p += sprintf(fontset, "-*-*-%s-%c-normal--%d-*-*-*-%c",
352                                      bold ? "bold" : "medium", italic ? 'i' : 'r', height_want, fixed ? 'c' : 'p');
353                         if (fixed){
354                             sprintf(p, ",-*-*-%s-%c-normal--%d-*-*-*-m",
355                                     bold ? "bold" : "medium", italic ? 'i' : 'r', height_want);
356                         }
357                     }
358                     bool equal_font;
359                     if (strcmp(fontset, fontset_prev) == 0){
360                         equal_font = true;
361                     }else{
362                         equal_font = false;
363                         strcpy(fontset_prev, fontset);
364                     }
365 
366                     char **mlist;
367                     int mcount;
368                     char *def_str;
369                     if (!equal_font){
370                         if (fs){
371                             XFreeFontSet(d, fs);
372                         }
373                         fs = XCreateFontSet(d, fontset, &mlist, &mcount, &def_str);
374                         if (fs == NULL)
375                            throw dlib::gui_error("gui_error: XCreateFontSet() failure");
376 
377                         XFontSetExtents *extent;
378                         extent = XExtentsOfFontSet(fs);
379                         ascender = -extent->max_logical_extent.y;
380                         descender = extent->max_logical_extent.height - ascender;
381                         XFreeStringList(mlist);
382                     }
383                     XRectangle ink, logical;
384                     inner::XTextExtents (fs, ch.istr, ch.len, &ink, &logical);
385                     width = logical.width;
386                     height = height_want;
387 
388                     if (pix == None || pix_width_prev < width || pix_height_prev < height){
389                         if (pix != None){
390                             XFreeGC(d, gc);
391                             XFreePixmap(d, pix);
392                         }
393                         pix_width_prev = width * 2;
394                         pix_height_prev = height * 2;
395                         pix = XCreatePixmap(d, DefaultRootWindow(d), pix_width_prev, pix_height_prev, XDefaultDepth(d, DefaultScreen(d)));
396                         gc = XCreateGC(d, pix, 0, NULL);
397                     }
398 
399                     unsigned long backcolor = rgb2color(background, d, cmap);
400                     XSetForeground(d, gc, backcolor);
401                     XSetBackground(d, gc, backcolor);
402                     XFillRectangle(d, pix, gc, 0, 0, width, height);
403                     XSetForeground(d, gc, rgb2color(foreground, d, cmap));
404                     inner::XDrawString(d, pix, fs, gc, 0, ascender, ch.istr, ch.len);
405 
406                     if (ximg) XDestroyImage(ximg);
407                     ximg = XGetImage(d, pix, 0, 0, width, height, AllPlanes, ZPixmap );
408                 }
409 
410                 template <typename T> vals_internal(T *str, int height_want, bool italic = false,
411                     bool bold = false, bool fixed = false, rgb_type background = rgb_type(), rgb_type foreground = rgb_type()){
412                     fontset_prev[0] = '\0';
413                     ximg = NULL;
414                     d = NULL;
415                     pix = None;
416                     fs = NULL;
417                     ascender = descender = -1;
418                     pix_width_prev = pix_height_prev = -1;
419                     create(str, height_want, italic, bold, fixed, background, foreground);
420                 }
421 
422                 inline int get_ascender(){
423                     return ascender;
424                 }
425 
426                 inline int get_descender(){
427                     return descender;
428                 }
429 
430                 std::map<unsigned long,rgb_type> col2rgb;
431                 rgb_type color2rgb(unsigned long color, Display *d, Colormap &cmap){
432                     if (col2rgb.count(color)){
433                         return col2rgb[color];
434                     }else{
435                         XColor xcol;
436                         xcol.pixel = color;
437                         XQueryColor(d, cmap, &xcol);
438                         rgb_type rgb_((byte)(xcol.red/257), (byte)(xcol.green/257), (byte)(xcol.blue/257));
439                         col2rgb[color] = rgb_;
440                         return rgb_;
441                     }
442                 }
443                 inline void get_pixel(int x, int y, byte &r, byte &g, byte &b){
444                     rgb_type c = color2rgb(XGetPixel(ximg,x,y), d, cmap);
445                     r = c.r, g = c.g, b = c.b;
446                 }
447 
448                 ~vals_internal(){
449                     XDestroyImage(ximg);
450 
451                     XFreeGC(d, gc);
452                     XFreeFontSet(d, fs);
453                     XFreePixmap(d, pix);
454                     XCloseDisplay(d);
455                 }
456 #endif
457             };
458 
459             struct image_size_setter{
460                 void operator()(int&, int&){
461                 }
462             };
463 
464             int ascender, descender;
465             vals_internal *vi;
466         public:
467             font_renderer() : image(0), width(0), height(0){
468                 ascender = descender = 0;
469                 vi = NULL;
470             }
471 
472             template<typename T> font_renderer(T *str, int height_want, bool italic = false, bool bold = false, bool fixed = false,         rgb_type background = rgb_type(0,0,0), rgb_type foreground = rgb_type(255,255,255)){
473                 render(str, height_want, italic, bold, fixed, background, foreground);
474             }
475 
476             template<typename T> void render(T *str, int height_want,
477                                              bool italic = false, bool bold = false, bool fixed = false,
478                                              rgb_type background = rgb_type(0,0,0), rgb_type foreground = rgb_type(255,255,255)){
479                 if (vi == NULL){
480                     vi = new vals_internal(str, height_want, italic, bold, fixed, background, foreground);
481                 }else{
482                     vi->create(str, height_want, italic, bold, fixed, background, foreground);
483                 }
484                 width = vi->width, height = vi->height;
485                 image = new byte[width * height * 3];
486                 ascender = vi->get_ascender();
487                 descender = vi->get_descender();
488 
489                 int h = height, w = width;
490                 for (int j = 0, i3 = 0; j < h; ++j){
491                     for (int i = 0; i < w; ++i, i3 += 3){
492                         vi->get_pixel(i, j, image[i3], image[i3+1], image[i3+2]);
493                     }
494                 }
495             }
496 
497             ~font_renderer(){
498                 if (vi) delete vi;
499                 destroy();
500             }
501             int get_width(){
502                 return width;
503             }
504             int get_height(){
505                 return height;
506             }
507             inline int get_ascender(){
508                 return ascender;
509             }
510             inline int get_descender(){
511                 return descender;
512             }
513 
514             const byte *get_image(){
515                 return image;
516             }
517         };
518     }
519 
520 // ----------------------------------------------------------------------------------------
521 
522     class native_font : public dlib::font
523     {
524         unsigned long ascender_;
525         native_font(){
526             setlocale(LC_CTYPE, "");
527             ascender_ = 0;
528             get_letter((int)('x'));
529         }
530         typedef std::map<int,dlib::letter *> letters_map_type;
531         letters_map_type letters;
532         font_renderer::font_renderer fl;
533     public:
534 
535         virtual ~native_font()
536         {
537             // delete all the letter objects we have in our letters map
538             letters_map_type::iterator i;
539             for (i = letters.begin(); i != letters.end(); ++i)
540             {
541                 delete i->second;
542             }
543         }
544 
545         virtual bool has_character (
546             dlib::unichar ch
547         )const{
548             return (*this)[ch].width() > 0;
549         }
550 
551         static const std::shared_ptr<font>& get_font (
552         )
553         {
554             static std::shared_ptr<font> f(new native_font);
555             return f;
556         }
557 
558         virtual const dlib::letter& operator[] (dlib::unichar ch) const{
559             return (const_cast<native_font *>(this))->get_letter(ch);
560         }
561 
562         dlib::letter& get_letter (
563             dlib::unichar ch
564         ){
565             if (letters.count(ch)){
566                 dlib::letter *l = letters.find(ch)->second;
567                 return *l;
568             }
569 
570             dlib::unichar c[2];
571             c[0] = ch;
572             c[1] = 0;
573 
574             fl.render(c, height(),false,false,true);
575             if (ascender_ == 0){
576                 ascender_ = fl.get_ascender();
577             }
578             std::vector<dlib::letter::point> v;
579             const font_renderer::byte *bp = fl.get_image();
580             for (int j = 0; j < fl.get_height(); ++j){
581                 for (int i = 0; i < fl.get_width(); ++i, bp += 3){
582                     if (*bp){
583                         v.push_back(dlib::letter::point(i,j-ascender()+1));
584                     }
585                 }
586             }
587             dlib::letter *l = new dlib::letter(fl.get_width(), (unsigned long)v.size());
588 
589             letters.insert(std::make_pair(ch,l));
590             for (int i = 0; i < (int)v.size(); ++i){
591                 (*l)[i] = v.at(i);
592             }
593             return *l;
594         }
595 
596         virtual unsigned long height (
597         ) const { return 12; }
598 
599         virtual unsigned long ascender (
600         ) const { return ascender_; }
601 
602         virtual unsigned long left_overflow (
603         ) const { return 1; }
604 
605         virtual unsigned long right_overflow (
606         ) const { return 2; }
607     };
608 
609 // ----------------------------------------------------------------------------------------
610 
611 }
612 
613 #endif // DLIB_NO_GUI_SUPPORT
614 #endif // DLIB_IGG_FONT_RENDERER_H_
615 
616