613 lines
23 KiB
C++
613 lines
23 KiB
C++
// 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_
|
|
|