// Copyright (C) 2006 Keita Mochizuki // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_IGG_FONT_RENDERER_H_ #define DLIB_IGG_FONT_RENDERER_H_ #include "../platform.h" #include "../gui_widgets.h" #include "../unicode.h" #include "../uintn.h" #include <map> #include <memory> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <locale.h> #if defined(WIN32) #include <windows.h> #include <mbstring.h> #elif defined(POSIX) #include <stdint.h> #include <stdio.h> #include <string.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xlocale.h> #endif namespace nativefont { // ---------------------------------------------------------------------------------------- namespace font_renderer { typedef dlib::uint8 byte; #ifdef WIN32 template <typename T> struct input2native_trait{ }; template <> struct input2native_trait<char>{ typedef char type_t; }; template <> struct input2native_trait<wchar_t>{ typedef wchar_t type_t; }; template <> struct input2native_trait<dlib::unichar>{ typedef wchar_t type_t; }; #endif // T : N : sizeof_source_type template <int N> struct size2inner_trait{ }; template <> struct size2inner_trait<1>{ typedef char type_t; }; template <> struct size2inner_trait<2>{ typedef dlib::uint16 type_t; }; template <> struct size2inner_trait<4>{ typedef dlib::unichar type_t; }; // ---------------------------------------------------------------------------------------- template <int N> struct create_helper{ }; template <> struct create_helper<1>{ typedef char type_t; type_t *istr; int len; create_helper(char *str){ len = (int)strlen(str); istr = str; } ~create_helper(){} }; template <> struct create_helper<2>{ typedef wchar_t type_t; type_t *istr; bool allocated; int len; create_helper(wchar_t *str){ allocated = false; len = (int)wcslen(str); istr = str; } create_helper(dlib::unichar *str){ allocated = true; len = 0; int unicount = 0; dlib::unichar *p = str; while(*p){ if (*p > 0xffff){ len += 2; }else{ len++; } unicount++; p++; } istr = new wchar_t[len+1]; for (int i = 0, wi = 0; i < unicount; ++i){ dlib::unichar high, low; if (str[i] > 0xffff){ dlib::unichar_to_surrogate_pair(str[i], high, low); istr[wi] = (wchar_t)high, istr[wi+1] = (wchar_t)low; wi += 2; }else{ istr[wi] = (wchar_t)str[i]; wi += 1; } } istr[len] = L'\0'; } ~create_helper(){ if (allocated) delete[] istr; } }; template <> struct create_helper<4>{ typedef wchar_t type_t; type_t *istr; int len; create_helper(dlib::unichar *str){ len = (int)wcslen((wchar_t *)str); istr = (type_t *)str; } ~create_helper(){} }; // ---------------------------------------------------------------------------------------- class font_renderer{ public: struct rgb_type{ byte r, g, b; rgb_type() : r(0), g(0), b(0){}; rgb_type(byte r_, byte g_, byte b_) : r(r_), g(g_), b(b_){}; }; private: byte *image; int width, height; void destroy(){ width = height = 0; delete image; image = 0; } struct vals_internal{ int width, height; #ifdef WIN32 COLORREF rgb2RGB(rgb_type &rgb){ return RGB(rgb.r, rgb.g, rgb.b); } HBITMAP hBmp, hBmpOld; HDC hDCBmp; BYTE *pixelint; HFONT hFont, hFontOld; HBRUSH hBrush; int pix_width_prev, pix_height_prev; bool first; int ascender, descender; int height_prev; char attribute_prev; template <typename T> void create(T *str, int height_want, bool italic, bool bold, bool fixed, rgb_type &background, rgb_type &foreground){ struct inner{ inline static BOOL GetTextExtentPoint32(HDC hDC, LPCSTR str, int len, LPSIZE lpsize){ return ::GetTextExtentPoint32A(hDC, str, len, lpsize); } inline static BOOL GetTextExtentPoint32(HDC hDC, LPCWSTR str, int len, LPSIZE lpsize){ return ::GetTextExtentPoint32W(hDC, str, len, lpsize); } inline static BOOL TextOut(HDC hDC, int nxstart, int nystart, LPCSTR str, int cbstr){ return ::TextOutA(hDC, nxstart, nystart, str, cbstr); } inline static BOOL TextOut(HDC hDC, int nxstart, int nystart, LPCWSTR str, int cbstr){ return ::TextOutW(hDC, nxstart, nystart, str, cbstr); } }; create_helper<sizeof(typename input2native_trait<T>::type_t)> ch(str); if (hDCBmp == NULL){ HWND hWnd = GetDesktopWindow(); HDC hDC = GetDC(hWnd); hDCBmp = CreateCompatibleDC(hDC); ReleaseDC(hWnd, hDC); } SetTextColor(hDCBmp, rgb2RGB(foreground)); SetBkColor(hDCBmp, rgb2RGB(background)); char attribute = (italic ? 1 : 0) | (bold ? 2 : 0) | (fixed ? 4 : 0); if (!hFont || height_prev != height || attribute != attribute_prev){ attribute_prev = attribute; height_prev = height_want; if (hFont){ SelectObject(hDCBmp, hFontOld); DeleteObject(hFont); } hFont = CreateFont(height_want, 0, 0, 0, bold ? FW_BOLD : FW_DONTCARE, italic ? TRUE : FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, fixed ? (FIXED_PITCH | FF_DONTCARE) : (VARIABLE_PITCH | FF_DONTCARE), NULL); hFontOld = (HFONT)SelectObject(hDCBmp, hFont); } { SIZE sz; inner::GetTextExtentPoint32(hDCBmp, ch.istr, ch.len, &sz); width = ((sz.cx + 3) / 4) * 4; height = sz.cy; } if (pix_width_prev < width || pix_height_prev < height){ if (hBmp){ SelectObject(hDCBmp, hBmpOld); DeleteObject(hBmp); } pix_width_prev = width * 2; pix_height_prev = height * 2; BITMAPINFO bi; ZeroMemory(&bi, sizeof(bi)); bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biBitCount = 24; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biWidth = pix_width_prev; bi.bmiHeader.biHeight = -pix_height_prev; hBmp = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (void **)&pixelint, NULL, 0); hBmpOld = (HBITMAP)SelectObject(hDCBmp, hBmp); } { HBRUSH hBrush = CreateSolidBrush(rgb2RGB(background)); RECT rc; rc.left = rc.top = 0; rc.right = pix_width_prev; rc.bottom = pix_height_prev; FillRect(hDCBmp, &rc, hBrush); } inner::TextOut(hDCBmp, 0, 0, ch.istr, ch.len); TEXTMETRICW tm; GetTextMetricsW(hDCBmp,&tm); ascender = tm.tmAscent; descender = tm.tmDescent; } template <typename T> vals_internal(T *str, int height_want, bool italic = false, bool bold = false, bool fixed = false, rgb_type background = rgb_type(), rgb_type foreground = rgb_type()){ first = true; hFont = NULL; hDCBmp = 0; hBmpOld = 0; hBmp = 0; hDCBmp = 0; pixelint = 0; pix_width_prev = pix_height_prev = 0; height_prev = -1; attribute_prev = 0; create(str, height_want, italic, bold, fixed, background, foreground); first = false; } inline int get_ascender(){ return ascender; } inline int get_descender(){ return descender; } inline void get_pixel(int x, int y, byte &r, byte &g, byte &b){ byte *p = pixelint + (y * pix_width_prev + x) * 3; r = *(p+2), g = *(p+1), b = *p; } void destroy(){ SelectObject(hDCBmp, hBmpOld); DeleteObject(hBmp); SelectObject(hDCBmp, hFontOld); DeleteObject(hFont); DeleteDC(hDCBmp); hFont = NULL; hDCBmp = 0; hBmpOld = 0; hBmp = 0; hDCBmp = 0; pixelint = 0; } ~vals_internal(){ destroy(); } #elif defined(POSIX) XImage *ximg; Display *d; GC gc; XFontSet fs; Pixmap pix; Colormap cmap; int ascender, descender; int pix_width_prev, pix_height_prev; char fontset_prev[256]; unsigned long rgb2color(rgb_type col, Display *d, Colormap &cmap){ XColor xcol; xcol.red = col.r * 257; xcol.green = col.g * 257; xcol.blue = col.b * 257; XAllocColor(d, cmap, &xcol); return xcol.pixel; } template <typename T> void create(T *str, int height_want, bool italic, bool bold, bool fixed, rgb_type background, rgb_type foreground){ struct inner{ inline static int XTextExtents (XFontSet fs, char *str, int len, XRectangle *ink, XRectangle *logical){ return XmbTextExtents(fs, str, len, ink, logical); } inline static int XTextExtents (XFontSet fs, wchar_t *str, int len, XRectangle *ink, XRectangle *logical){ return XwcTextExtents(fs, str, len, ink, logical); } inline static void XDrawString(Display *d, Window w, XFontSet fs, GC gc, int x, int y, char *str, int num_bytes){ XmbDrawString(d, w, fs, gc, x, y, str, num_bytes); } inline static void XDrawString(Display *d, Window w, XFontSet fs, GC gc, int x, int y, wchar_t *str, int num_bytes){ XwcDrawString(d, w, fs, gc, x, y, str, num_bytes); } }; create_helper<sizeof(T)> ch((typename size2inner_trait<sizeof(T)>::type_t *)str); setlocale(LC_CTYPE, ""); if (d == NULL){ d = XOpenDisplay(NULL); if (d == 0) { d = XOpenDisplay(":0.0"); if (d == 0) { throw dlib::gui_error("Unable to connect to the X display."); } } cmap = DefaultColormap(d, DefaultScreen(d)); } char fontset[256]; { char *p = fontset; p += sprintf(fontset, "-*-*-%s-%c-normal--%d-*-*-*-%c", bold ? "bold" : "medium", italic ? 'i' : 'r', height_want, fixed ? 'c' : 'p'); if (fixed){ sprintf(p, ",-*-*-%s-%c-normal--%d-*-*-*-m", bold ? "bold" : "medium", italic ? 'i' : 'r', height_want); } } bool equal_font; if (strcmp(fontset, fontset_prev) == 0){ equal_font = true; }else{ equal_font = false; strcpy(fontset_prev, fontset); } char **mlist; int mcount; char *def_str; if (!equal_font){ if (fs){ XFreeFontSet(d, fs); } fs = XCreateFontSet(d, fontset, &mlist, &mcount, &def_str); if (fs == NULL) throw dlib::gui_error("gui_error: XCreateFontSet() failure"); XFontSetExtents *extent; extent = XExtentsOfFontSet(fs); ascender = -extent->max_logical_extent.y; descender = extent->max_logical_extent.height - ascender; XFreeStringList(mlist); } XRectangle ink, logical; inner::XTextExtents (fs, ch.istr, ch.len, &ink, &logical); width = logical.width; height = height_want; if (pix == None || pix_width_prev < width || pix_height_prev < height){ if (pix != None){ XFreeGC(d, gc); XFreePixmap(d, pix); } pix_width_prev = width * 2; pix_height_prev = height * 2; pix = XCreatePixmap(d, DefaultRootWindow(d), pix_width_prev, pix_height_prev, XDefaultDepth(d, DefaultScreen(d))); gc = XCreateGC(d, pix, 0, NULL); } unsigned long backcolor = rgb2color(background, d, cmap); XSetForeground(d, gc, backcolor); XSetBackground(d, gc, backcolor); XFillRectangle(d, pix, gc, 0, 0, width, height); XSetForeground(d, gc, rgb2color(foreground, d, cmap)); inner::XDrawString(d, pix, fs, gc, 0, ascender, ch.istr, ch.len); if (ximg) XDestroyImage(ximg); ximg = XGetImage(d, pix, 0, 0, width, height, AllPlanes, ZPixmap ); } template <typename T> vals_internal(T *str, int height_want, bool italic = false, bool bold = false, bool fixed = false, rgb_type background = rgb_type(), rgb_type foreground = rgb_type()){ fontset_prev[0] = '\0'; ximg = NULL; d = NULL; pix = None; fs = NULL; ascender = descender = -1; pix_width_prev = pix_height_prev = -1; create(str, height_want, italic, bold, fixed, background, foreground); } inline int get_ascender(){ return ascender; } inline int get_descender(){ return descender; } std::map<unsigned long,rgb_type> col2rgb; rgb_type color2rgb(unsigned long color, Display *d, Colormap &cmap){ if (col2rgb.count(color)){ return col2rgb[color]; }else{ XColor xcol; xcol.pixel = color; XQueryColor(d, cmap, &xcol); rgb_type rgb_((byte)(xcol.red/257), (byte)(xcol.green/257), (byte)(xcol.blue/257)); col2rgb[color] = rgb_; return rgb_; } } inline void get_pixel(int x, int y, byte &r, byte &g, byte &b){ rgb_type c = color2rgb(XGetPixel(ximg,x,y), d, cmap); r = c.r, g = c.g, b = c.b; } ~vals_internal(){ XDestroyImage(ximg); XFreeGC(d, gc); XFreeFontSet(d, fs); XFreePixmap(d, pix); XCloseDisplay(d); } #endif }; struct image_size_setter{ void operator()(int&, int&){ } }; int ascender, descender; vals_internal *vi; public: font_renderer() : image(0), width(0), height(0){ ascender = descender = 0; vi = NULL; } 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)){ render(str, height_want, italic, bold, fixed, background, foreground); } template<typename T> void render(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)){ if (vi == NULL){ vi = new vals_internal(str, height_want, italic, bold, fixed, background, foreground); }else{ vi->create(str, height_want, italic, bold, fixed, background, foreground); } width = vi->width, height = vi->height; image = new byte[width * height * 3]; ascender = vi->get_ascender(); descender = vi->get_descender(); int h = height, w = width; for (int j = 0, i3 = 0; j < h; ++j){ for (int i = 0; i < w; ++i, i3 += 3){ vi->get_pixel(i, j, image[i3], image[i3+1], image[i3+2]); } } } ~font_renderer(){ if (vi) delete vi; destroy(); } int get_width(){ return width; } int get_height(){ return height; } inline int get_ascender(){ return ascender; } inline int get_descender(){ return descender; } const byte *get_image(){ return image; } }; } // ---------------------------------------------------------------------------------------- class native_font : public dlib::font { unsigned long ascender_; native_font(){ setlocale(LC_CTYPE, ""); ascender_ = 0; get_letter((int)('x')); } typedef std::map<int,dlib::letter *> letters_map_type; letters_map_type letters; font_renderer::font_renderer fl; public: virtual ~native_font() { // delete all the letter objects we have in our letters map letters_map_type::iterator i; for (i = letters.begin(); i != letters.end(); ++i) { delete i->second; } } virtual bool has_character ( dlib::unichar ch )const{ return (*this)[ch].width() > 0; } static const std::shared_ptr<font>& get_font ( ) { static std::shared_ptr<font> f(new native_font); return f; } virtual const dlib::letter& operator[] (dlib::unichar ch) const{ return (const_cast<native_font *>(this))->get_letter(ch); } dlib::letter& get_letter ( dlib::unichar ch ){ if (letters.count(ch)){ dlib::letter *l = letters.find(ch)->second; return *l; } dlib::unichar c[2]; c[0] = ch; c[1] = 0; fl.render(c, height(),false,false,true); if (ascender_ == 0){ ascender_ = fl.get_ascender(); } std::vector<dlib::letter::point> v; const font_renderer::byte *bp = fl.get_image(); for (int j = 0; j < fl.get_height(); ++j){ for (int i = 0; i < fl.get_width(); ++i, bp += 3){ if (*bp){ v.push_back(dlib::letter::point(i,j-ascender()+1)); } } } dlib::letter *l = new dlib::letter(fl.get_width(), (unsigned long)v.size()); letters.insert(std::make_pair(ch,l)); for (int i = 0; i < (int)v.size(); ++i){ (*l)[i] = v.at(i); } return *l; } virtual unsigned long height ( ) const { return 12; } virtual unsigned long ascender ( ) const { return ascender_; } virtual unsigned long left_overflow ( ) const { return 1; } virtual unsigned long right_overflow ( ) const { return 2; } }; // ---------------------------------------------------------------------------------------- } #endif // DLIB_IGG_FONT_RENDERER_H_