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