|
|
|
|
|
|
|
#ifndef DLIB_FONTs_ |
|
#define DLIB_FONTs_ |
|
|
|
#include <memory> |
|
#include <string> |
|
|
|
#include "fonts_abstract.h" |
|
#include "../algs.h" |
|
#include "../serialize.h" |
|
#include "../unicode.h" |
|
#include "../array.h" |
|
#include "../array2d.h" |
|
#include "../threads.h" |
|
|
|
namespace dlib |
|
{ |
|
|
|
|
|
|
|
class letter |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public: |
|
struct point |
|
{ |
|
point (){} |
|
|
|
point ( |
|
signed char x_, |
|
signed char y_ |
|
) : |
|
x(x_), |
|
y(y_) |
|
{} |
|
|
|
signed char x; |
|
signed char y; |
|
}; |
|
|
|
letter ( |
|
) : |
|
points(0), |
|
w(0), |
|
count(0) |
|
{} |
|
|
|
letter ( |
|
unsigned short width_, |
|
unsigned short point_count |
|
) : |
|
points(new point[point_count]), |
|
w(width_), |
|
count(point_count) |
|
{} |
|
|
|
~letter( |
|
) |
|
{ |
|
if (points) |
|
delete [] points; |
|
} |
|
|
|
unsigned short width ( |
|
) const { return w; } |
|
|
|
unsigned short num_of_points ( |
|
) const { return count;} |
|
|
|
point& operator[] ( |
|
unsigned short i |
|
) |
|
{ |
|
DLIB_ASSERT (i < num_of_points(), |
|
"\tvoid letter::operator[]()" |
|
<< "\n\ti: " << i |
|
<< "\n\tnum_of_points(): " << num_of_points() ); |
|
return points[i]; |
|
} |
|
|
|
const point& operator[] ( |
|
unsigned short i |
|
) const |
|
{ |
|
DLIB_ASSERT (i < num_of_points(), |
|
"\tvoid letter::operator[]()" |
|
<< "\n\ti: " << i |
|
<< "\n\tnum_of_points(): " << num_of_points() ); |
|
return points[i]; |
|
} |
|
|
|
friend void serialize ( |
|
const letter& item, |
|
std::ostream& out |
|
); |
|
|
|
friend void deserialize ( |
|
letter& item, |
|
std::istream& in |
|
); |
|
|
|
void swap ( |
|
letter& item |
|
) |
|
{ |
|
exchange(points, item.points); |
|
exchange(w, item.w); |
|
exchange(count, item.count); |
|
} |
|
|
|
private: |
|
|
|
letter(letter&); |
|
letter& operator=(letter&); |
|
|
|
point* points; |
|
unsigned short w; |
|
unsigned short count; |
|
}; |
|
|
|
inline void swap ( |
|
letter& a, |
|
letter& b |
|
) { a.swap(b); } |
|
|
|
|
|
|
|
class font |
|
{ |
|
public: |
|
virtual ~font() {} |
|
|
|
virtual bool has_character ( |
|
unichar ch |
|
)const=0; |
|
bool has_character(char ch) const { return this->has_character(zero_extend_cast<unichar>(ch)); } |
|
bool has_character(wchar_t ch) const { return this->has_character(zero_extend_cast<unichar>(ch)); } |
|
|
|
const letter& operator[] (char ch) const { return (*this)[zero_extend_cast<unichar>(ch)]; }; |
|
const letter& operator[] (wchar_t ch)const { return (*this)[zero_extend_cast<unichar>(ch)]; }; |
|
|
|
virtual const letter& operator[] ( |
|
unichar ch |
|
)const=0; |
|
|
|
virtual unsigned long height ( |
|
) const = 0; |
|
|
|
virtual unsigned long ascender ( |
|
) const = 0; |
|
|
|
virtual unsigned long left_overflow ( |
|
) const = 0; |
|
|
|
virtual unsigned long right_overflow ( |
|
) const = 0; |
|
|
|
|
|
|
|
template <typename T, typename traits, typename alloc> |
|
void compute_size ( |
|
const std::basic_string<T,traits,alloc>& str, |
|
unsigned long& width, |
|
unsigned long& height, |
|
typename std::basic_string<T,traits,alloc>::size_type first = 0, |
|
typename std::basic_string<T,traits,alloc>::size_type last = (std::basic_string<T,traits,alloc>::npos) |
|
) const |
|
{ |
|
typedef std::basic_string<T,traits,alloc> string; |
|
DLIB_ASSERT ( (last == string::npos) || (first <= last && last < str.size()) , |
|
"\tvoid font::compute_size()" |
|
<< "\n\tlast == string::npos: " << ((last == string::npos)?"true":"false") |
|
<< "\n\tfirst: " << (unsigned long)first |
|
<< "\n\tlast: " << (unsigned long)last |
|
<< "\n\tstr.size(): " << (unsigned long)str.size() ); |
|
|
|
unsigned long line_width = 0; |
|
unsigned long newlines = 0; |
|
width = 0; |
|
height = 0; |
|
|
|
if (str.size()) |
|
{ |
|
if (last == string::npos) |
|
last = str.size()-1; |
|
const font& f = *this; |
|
|
|
for (typename string::size_type i = first; i <= last; ++i) |
|
{ |
|
|
|
if (str[i] == '\r') |
|
continue; |
|
|
|
if (str[i] == '\n') |
|
{ |
|
++newlines; |
|
width = std::max(width,line_width); |
|
line_width = 0; |
|
} |
|
else |
|
{ |
|
if (is_combining_char(str[i]) == false) |
|
line_width += f[str[i]].width(); |
|
} |
|
} |
|
width = std::max(width,line_width); |
|
|
|
height = (newlines+1)*f.height(); |
|
width += f.left_overflow() + f.right_overflow(); |
|
} |
|
} |
|
|
|
|
|
|
|
template <typename C, typename T, typename traits, typename alloc, typename pixel_type> |
|
void draw_string ( |
|
const C& c, |
|
const rectangle& rect, |
|
const std::basic_string<T,traits,alloc>& str, |
|
const pixel_type& color, |
|
typename std::basic_string<T,traits,alloc>::size_type first = 0, |
|
typename std::basic_string<T,traits,alloc>::size_type last = (std::basic_string<T,traits,alloc>::npos), |
|
const rectangle area_ = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), |
|
std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) |
|
) const |
|
{ |
|
typedef std::basic_string<T,traits,alloc> string; |
|
DLIB_ASSERT ( (last == string::npos) || (first <= last && last < str.size()) , |
|
"\tvoid font::draw_string()" |
|
<< "\n\tlast == string::npos: " << ((last == string::npos)?"true":"false") |
|
<< "\n\tfirst: " << (unsigned long)first |
|
<< "\n\tlast: " << (unsigned long)last |
|
<< "\n\tstr.size(): " << (unsigned long)str.size() ); |
|
|
|
rectangle area = rect.intersect(c).intersect(area_); |
|
if (area.is_empty() || str.size() == 0) |
|
return; |
|
|
|
if (last == string::npos) |
|
last = str.size()-1; |
|
|
|
const font& f = *this; |
|
|
|
long y_offset = rect.top() + f.ascender() - 1; |
|
|
|
long pos = rect.left()+f.left_overflow(); |
|
for (typename string::size_type i = first; i <= last; ++i) |
|
{ |
|
|
|
if (str[i] == '\r') |
|
continue; |
|
|
|
|
|
|
|
|
|
if(is_combining_char(str[i]) && |
|
pos > rect.left() + static_cast<long>(f.left_overflow())) |
|
{ |
|
pos -= f[str[i]].width(); |
|
} |
|
|
|
if (str[i] == '\n') |
|
{ |
|
y_offset += f.height(); |
|
pos = rect.left()+f.left_overflow(); |
|
continue; |
|
} |
|
|
|
|
|
if (area.bottom() + static_cast<long>(f.height()) < y_offset) |
|
{ |
|
|
|
break; |
|
} |
|
else if (area.left() > pos - static_cast<long>(f.left_overflow()) && |
|
pos + static_cast<long>(f[str[i]].width() + f.right_overflow()) < area.left() ) |
|
{ |
|
pos += f[str[i]].width(); |
|
continue; |
|
} |
|
else if (area.right() + static_cast<long>(f.right_overflow()) < pos) |
|
{ |
|
|
|
|
|
continue; |
|
} |
|
|
|
|
|
|
|
|
|
const letter& l = f[str[i]]; |
|
for (unsigned short i = 0; i < l.num_of_points(); ++i) |
|
{ |
|
const long x = l[i].x + pos; |
|
const long y = l[i].y + y_offset; |
|
|
|
|
|
if (area.contains(x,y)) |
|
{ |
|
assign_pixel(c[y-c.top()][x-c.left()], color); |
|
} |
|
} |
|
|
|
pos += l.width(); |
|
} |
|
} |
|
template <typename C, typename T, typename traits, typename alloc> |
|
void draw_string ( |
|
const C& c, |
|
const rectangle& rect, |
|
const std::basic_string<T,traits,alloc>& str |
|
) const |
|
{ |
|
draw_string(c,rect, str, 0, 0, (std::basic_string<T,traits,alloc>::npos), |
|
rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), |
|
std::numeric_limits<long>::max(), std::numeric_limits<long>::max())); |
|
} |
|
|
|
|
|
|
|
template <typename T, typename traits, typename alloc> |
|
const rectangle compute_cursor_rect ( |
|
const rectangle& rect, |
|
const std::basic_string<T,traits,alloc>& str, |
|
unsigned long index, |
|
typename std::basic_string<T,traits,alloc>::size_type first = 0, |
|
typename std::basic_string<T,traits,alloc>::size_type last = (std::basic_string<T,traits,alloc>::npos) |
|
) const |
|
{ |
|
typedef std::basic_string<T,traits,alloc> string; |
|
DLIB_ASSERT ( (last == string::npos) || (first <= last && last < str.size()) , |
|
"\trectangle font::compute_cursor_rect()" |
|
<< "\n\tlast == string::npos: " << ((last == string::npos)?"true":"false") |
|
<< "\n\tfirst: " << (unsigned long)first |
|
<< "\n\tlast: " << (unsigned long)last |
|
<< "\n\tindex: " << index |
|
<< "\n\tstr.size(): " << (unsigned long)str.size() ); |
|
|
|
const font& f = *this; |
|
|
|
if (last == string::npos) |
|
last = str.size()-1; |
|
|
|
long x = f.left_overflow(); |
|
long y = 0; |
|
int count = 0; |
|
|
|
if (str.size() != 0) |
|
{ |
|
for (typename string::size_type i = first; i <= last && i < index; ++i) |
|
{ |
|
++count; |
|
if (str[i] == '\n') |
|
{ |
|
x = f.left_overflow(); |
|
y += f.height(); |
|
count = 0; |
|
} |
|
else if (is_combining_char(str[i]) == false && |
|
str[i] != '\r') |
|
{ |
|
x += f[str[i]].width(); |
|
} |
|
} |
|
} |
|
|
|
x += rect.left(); |
|
y += rect.top(); |
|
|
|
|
|
if (count == 0) |
|
--x; |
|
|
|
return rectangle(x,y,x,y+f.height()-1); |
|
} |
|
|
|
|
|
|
|
template <typename T, typename traits, typename alloc> |
|
unsigned long compute_cursor_pos ( |
|
const rectangle& rect, |
|
const std::basic_string<T,traits,alloc>& str, |
|
long x, |
|
long y, |
|
typename std::basic_string<T,traits,alloc>::size_type first = 0, |
|
typename std::basic_string<T,traits,alloc>::size_type last = (std::basic_string<T,traits,alloc>::npos) |
|
) const |
|
{ |
|
typedef std::basic_string<T,traits,alloc> string; |
|
DLIB_ASSERT ( (last == string::npos) || (first <= last && last < str.size()) , |
|
"\tunsigned long font::compute_cursor_pos()" |
|
<< "\n\tlast == string::npos: " << ((last == string::npos)?"true":"false") |
|
<< "\n\tfirst: " << (unsigned long)first |
|
<< "\n\tlast: " << (unsigned long)last |
|
<< "\n\tx: " << x |
|
<< "\n\ty: " << y |
|
<< "\n\tstr.size(): " << (unsigned long)str.size() ); |
|
const font& f = *this; |
|
|
|
|
|
if (str.size() == 0) |
|
return 0; |
|
else if (first >= str.size()) |
|
return static_cast<unsigned long>(str.size()); |
|
|
|
y -= rect.top(); |
|
x -= rect.left(); |
|
if (y < 0) |
|
y = 0; |
|
if (x < 0) |
|
x = 0; |
|
|
|
if (last == string::npos) |
|
last = str.size()-1; |
|
|
|
|
|
|
|
typename string::size_type pos = first; |
|
long line = 0; |
|
while (static_cast<unsigned long>(y) >= f.height()) |
|
{ |
|
++line; |
|
y -= f.height(); |
|
} |
|
|
|
|
|
for (typename string::size_type i = first; i <= last && line != 0; ++i) |
|
{ |
|
if (str[i] == '\n') |
|
{ |
|
--line; |
|
pos = i + 1; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
const typename string::size_type start_of_line = pos; |
|
|
|
|
|
long cur_x = f.left_overflow(); |
|
|
|
while (pos <= last) |
|
{ |
|
if (x <= cur_x || str[pos] == '\n') |
|
break; |
|
|
|
if (is_combining_char(str[pos]) == false && |
|
str[pos] != '\r') |
|
{ |
|
cur_x += f[str[pos]].width(); |
|
} |
|
++pos; |
|
} |
|
|
|
if (x <= cur_x) |
|
{ |
|
if (pos != start_of_line) |
|
{ |
|
|
|
|
|
const long width = f[str[pos-1]].width(); |
|
if (x < cur_x - width/2) |
|
--pos; |
|
} |
|
} |
|
return static_cast<unsigned long>(pos); |
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const std::shared_ptr<font> get_native_font (); |
|
|
|
|
|
|
|
class default_font : public font |
|
{ |
|
letter* l; |
|
|
|
|
|
default_font( |
|
); |
|
default_font(default_font&); |
|
default_font& operator=(default_font&); |
|
|
|
|
|
|
|
public: |
|
static const std::shared_ptr<font>& get_font ( |
|
) |
|
{ |
|
static mutex m; |
|
static std::shared_ptr<font> f; |
|
auto_mutex M(m); |
|
if (f.get() == 0) |
|
f.reset(new default_font); |
|
|
|
return f; |
|
} |
|
|
|
~default_font( |
|
) |
|
{ |
|
delete [] l; |
|
} |
|
|
|
unsigned long height ( |
|
) const { return 16; } |
|
|
|
unsigned long ascender ( |
|
) const { return 12; } |
|
|
|
unsigned long left_overflow ( |
|
) const { return 1; } |
|
|
|
unsigned long right_overflow ( |
|
) const { return 2; } |
|
|
|
bool has_character ( |
|
unichar ch |
|
)const |
|
{ |
|
if (ch < 256 && (l[ch].width() != 0 || l[ch].num_of_points() != 0)) |
|
return true; |
|
else |
|
return false; |
|
} |
|
|
|
const letter& operator[] ( |
|
unichar ch |
|
) const |
|
{ |
|
if(ch < 256) |
|
return l[ch]; |
|
return l[0]; |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
class bdf_font : public font |
|
{ |
|
|
|
public: |
|
bdf_font( long default_char_ = -1 ); |
|
|
|
long read_bdf_file( std::istream& in, unichar max_enc, unichar min_enc = 0 ); |
|
unsigned long height() const |
|
{ |
|
return fbb.height(); |
|
} |
|
unsigned long ascender() const |
|
{ |
|
return std::max( 0L, 1 - fbb.top() ); |
|
} |
|
unsigned long left_overflow() const |
|
{ |
|
return std::max( 0L, -fbb.left() ); |
|
} |
|
unsigned long right_overflow() const |
|
{ |
|
return right_overflow_; |
|
} |
|
const letter& operator[] ( unichar uch ) const |
|
{ |
|
if ( !has_character(uch) ) |
|
{ |
|
return gl[default_char]; |
|
} |
|
return gl[uch]; |
|
} |
|
|
|
bool has_character ( |
|
unichar ch |
|
)const |
|
{ |
|
if (ch < gl.size() && (gl[ch].width() != 0 || gl[ch].num_of_points() != 0)) |
|
return true; |
|
else |
|
return false; |
|
} |
|
|
|
void adjust_metrics(); |
|
private: |
|
|
|
bool bitmap_to_letter( array2d<char>& bitmap, unichar enc, unsigned long width, int x_offset, int y_offset ); |
|
|
|
array<letter> gl; |
|
unichar default_char; |
|
bool is_initialized; |
|
rectangle fbb; |
|
unsigned long right_overflow_; |
|
|
|
unsigned global_width; |
|
bool has_global_width; |
|
long specified_default_char; |
|
|
|
bdf_font( bdf_font& ); |
|
bdf_font& operator=( bdf_font& ); |
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
#ifdef NO_MAKEFILE |
|
#include "fonts.cpp" |
|
#endif |
|
|
|
#endif |
|
|
|
|