mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
492 lines
10 KiB
C++
492 lines
10 KiB
C++
#include "Draw.h"
|
|
|
|
NAMESPACE_UPP
|
|
|
|
|
|
#if defined(PLATFORM_WIN32) || defined(PLATFORM_X11)
|
|
|
|
#define LLOG(x) // LOG(x)
|
|
#define LTIMING(x) // TIMING(x)
|
|
|
|
bool Draw::sFini;
|
|
|
|
void Draw::SinCos(int angle, double& sina, double& cosa)
|
|
{
|
|
angle = angle % 3600;
|
|
switch(angle) {
|
|
case 0: sina = 0; cosa = 1; break;
|
|
case 900: sina = 1; cosa = 0; break;
|
|
case 1800: sina = 0; cosa = -1; break;
|
|
case 2700: sina = -1; cosa = 0; break;
|
|
default:
|
|
double a = angle * M_PI / 1800.0;
|
|
sina = sin(a);
|
|
cosa = cos(a);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool Draw::StdFontSizeSet;
|
|
Size Draw::StdFontSize;
|
|
Font Draw::AStdFont;
|
|
|
|
INITBLOCK {
|
|
RichValue<Font>::Register();
|
|
}
|
|
|
|
void Draw::InitFonts()
|
|
{
|
|
DrawLock __;
|
|
if(sFini) return;
|
|
sFini = true;
|
|
GetFontHash(0);
|
|
GetFontLru();
|
|
InitPlatformFonts();
|
|
}
|
|
|
|
int FontFilter(int c)
|
|
{
|
|
return c >= 'a' && c <= 'z' || c >= '0' && c <= '9' ? c : c >= 'A' && c <= 'Z' ? ToLower(c) : 0;
|
|
}
|
|
|
|
int Font::FindFaceNameIndex(const char *name) {
|
|
if(!Draw::sFini) Draw::InitFonts();
|
|
for(int i = 1; i < GetFaceCount(); i++)
|
|
if(GetFaceName(i) == name)
|
|
return i;
|
|
String n = Filter(name, FontFilter);
|
|
for(int i = 1; i < GetFaceCount(); i++)
|
|
if(Filter(GetFaceName(i), FontFilter) == n)
|
|
return i;
|
|
return 0;
|
|
}
|
|
|
|
Draw& ScreenInfo();
|
|
|
|
FontInfo Font::Info() const
|
|
{
|
|
DrawLock __;
|
|
return ScreenInfo().GetFontInfoW(*this);
|
|
}
|
|
|
|
void Draw::SetStdFont(Font font)
|
|
{
|
|
InitFonts();
|
|
AStdFont = font;
|
|
StdFontSizeSet = false;
|
|
}
|
|
|
|
Size Draw::GetStdFontSize()
|
|
{
|
|
if(!StdFontSizeSet) {
|
|
StdFontSizeSet = true;
|
|
FontInfo fi = AStdFont.Info();
|
|
FontInfo bfi = AStdFont().Bold().Info();
|
|
StdFontSize = Size(fi.GetAveWidth(), bfi.GetHeight());
|
|
}
|
|
return StdFontSize;
|
|
}
|
|
|
|
Font StdFont()
|
|
{
|
|
return Font(0, Draw::AStdFont.GetHeight());
|
|
}
|
|
|
|
String Font::GetFaceName() const {
|
|
if(IsNull()) return String();
|
|
if(GetFace() == 0)
|
|
return "STDFONT";
|
|
return GetFaceName(GetFace());
|
|
}
|
|
|
|
dword Font::GetFaceInfo() const {
|
|
if(IsNull()) return 0;
|
|
return GetFaceInfo(GetFace());
|
|
}
|
|
|
|
Font& Font::FaceName(const String& name) {
|
|
int n = FindFaceNameIndex(name);
|
|
Face(n < 0 ? 0xffff : n);
|
|
return *this;
|
|
}
|
|
|
|
void Font::Serialize(Stream& s) {
|
|
int version = 1;
|
|
s / version;
|
|
if(version >= 1) {
|
|
int f = GetFace();
|
|
if(f > COURIER)
|
|
f = -1;
|
|
s / f;
|
|
String name;
|
|
if(f < 0) {
|
|
name = GetFaceName();
|
|
s % name;
|
|
}
|
|
if(s.IsLoading())
|
|
if(f >= 0)
|
|
Face(f);
|
|
else
|
|
FaceName(name);
|
|
}
|
|
else {
|
|
String name = GetFaceName();
|
|
s % name;
|
|
if(s.IsLoading()) {
|
|
FaceName(name);
|
|
if(IsNull())
|
|
Face(COURIER);
|
|
}
|
|
}
|
|
s % flags % height % width;
|
|
}
|
|
|
|
template<>
|
|
String AsString(const Font& f) {
|
|
if(IsNull(f)) return "<null>";
|
|
String s = "<" + f.GetFaceName() + Format(":%d", f.GetHeight());
|
|
if(f.IsBold())
|
|
s += " Bold";
|
|
if(f.IsItalic())
|
|
s += " Italic";
|
|
if(f.IsUnderline())
|
|
s += " Underline";
|
|
if(f.IsStrikeout())
|
|
s += " Strikeout";
|
|
return s + '>';
|
|
}
|
|
|
|
int Draw::FontCacheMax = 32;
|
|
int Draw::FontCached;
|
|
|
|
void Draw::FreeFonts() {
|
|
FontCacheMax = FontCached = 0;
|
|
sFini = false;
|
|
for(int i = 0; i < FONTHASH; i++)
|
|
GetFontHash(i)->DeleteList(HASH);
|
|
}
|
|
|
|
FontInfo::FontInfo(const FontInfo& f)
|
|
{
|
|
Retain(f);
|
|
}
|
|
|
|
FontInfo& FontInfo::operator=(const FontInfo& f)
|
|
{
|
|
Release();
|
|
Retain(f);
|
|
return *this;
|
|
}
|
|
|
|
//# Pretty ugly code....
|
|
FontInfo::Data *Draw::GetFontHash(int i) {
|
|
static byte buff[FONTHASH * sizeof(FontLink)];
|
|
static FontLink *fonthash;
|
|
if(!fonthash) {
|
|
fonthash = (FontLink *)buff;
|
|
for(int i = 0; i < FONTHASH; i++)
|
|
fonthash[i].LinkSelfAll();
|
|
}
|
|
ASSERT(i >= 0 && i < FONTHASH);
|
|
return (FontInfo::Data *)&fonthash[i];
|
|
}
|
|
|
|
//# Pretty ugly code....
|
|
FontInfo::Data *Draw::GetFontLru() {
|
|
static byte buff[sizeof(FontLink)];
|
|
static FontLink *fontlru;
|
|
if(!fontlru)
|
|
fontlru = new(buff) FontLink;
|
|
return (FontInfo::Data *)fontlru;
|
|
}
|
|
|
|
bool FontInfo::IsEqual(byte _charset, Font f, int angle, int device) const
|
|
{
|
|
return ptr && ptr->font == f && ptr->angle == angle && ptr->device == device &&
|
|
charset == _charset;
|
|
}
|
|
|
|
FontInfo::CharMetrics FontInfo::GetCM(int c) const
|
|
{
|
|
// CharMetrics mm;
|
|
// ptr->GetMetrics(&mm, c, 1);
|
|
// return mm;
|
|
|
|
if(c < 0) c = (byte)c;
|
|
if(charset != CHARSET_UNICODE)
|
|
c = ToUnicode(c, charset);
|
|
ASSERT(c < 65536);
|
|
if(c >= 65536) {
|
|
CharMetrics h;
|
|
h.width = h.lspc = h.rspc = 0;
|
|
return h;
|
|
}
|
|
if(c < 2048)
|
|
return GetPage(c >> 5)[c & 31];
|
|
Mutex::Lock __(ptr->xmutex);
|
|
Kinfo& ki = ptr->kinfo.At((c >> 10) - 2);
|
|
if(!ki.flags) {
|
|
ki.flags = new byte[128];
|
|
memset(ki.flags, 0, 128);
|
|
ptr->GetMetrics(&ki.std, c, 1);
|
|
}
|
|
int fi = (c >> 3) & 127;
|
|
int fm = 1 << (c & 7);
|
|
if(ki.flags[fi] & fm)
|
|
return ki.std;
|
|
int q = ptr->xx.Find(c);
|
|
if(q >= 0)
|
|
return ptr->xx[q];
|
|
CharMetrics m;
|
|
ptr->GetMetrics(&m, c, 1);
|
|
if(m == ki.std)
|
|
ki.flags[fi] |= fm;
|
|
else
|
|
ptr->xx.Add(c, m);
|
|
return m;
|
|
}
|
|
|
|
int FontInfo::GetWidth(int c) const {
|
|
return GetCM(c).width;
|
|
}
|
|
|
|
int FontInfo::GetLeftSpace(int c) const {
|
|
return GetCM(c).lspc;
|
|
}
|
|
|
|
int FontInfo::GetRightSpace(int c) const {
|
|
return GetCM(c).rspc;
|
|
}
|
|
|
|
void Draw::Release(FontInfo::Data *font) {
|
|
DrawLock __;
|
|
ASSERT(font->refcount > 0);
|
|
LLOG("Release " << font->font << " count:" << font->count);
|
|
if(--font->refcount == 0) {
|
|
if(FontCacheMax == 0) {
|
|
delete font;
|
|
return;
|
|
}
|
|
FontInfo::Data *lru = GetFontLru();
|
|
font->LinkAfter(lru, LRU);
|
|
FontCached++;
|
|
LLOG("Placed to cache " << font->font << " cached:" << FontCached);
|
|
while(FontCached > FontCacheMax) {
|
|
ASSERT(lru->GetPrev(LRU) != lru);
|
|
FontCached--;
|
|
LLOG("Deleting from cache " << lru->GetPrev(LRU)->font <<
|
|
" cached: " << FontCached);
|
|
delete lru->GetPrev(LRU);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FontInfo::Release()
|
|
{
|
|
if(ptr) Draw::Release(ptr);
|
|
}
|
|
|
|
void FontInfo::Retain(const FontInfo& f)
|
|
{
|
|
DrawLock __;
|
|
ptr = f.ptr;
|
|
ptr->refcount++;
|
|
charset = f.charset;
|
|
}
|
|
|
|
FontInfo Draw::GetFontInfo(byte charset, Font font) {
|
|
DrawLock __;
|
|
LTIMING("GetFontInfo");
|
|
if(charset == CHARSET_DEFAULT)
|
|
charset = GetDefaultCharset();
|
|
if(lastFont.IsEqual(charset, font, 0, device))
|
|
return lastFont;
|
|
#ifdef PLATFORM_WIN32
|
|
HDC hdc = GetHandle();
|
|
FontInfo fi = Acquire(font, hdc ? hdc : ScreenHDC(), 0, device);
|
|
#endif
|
|
#ifdef PLATFORM_X11
|
|
FontInfo fi = Acquire(font, 0, device);
|
|
#endif
|
|
fi.charset = charset;
|
|
return fi;
|
|
}
|
|
|
|
FontInfo Draw::GetFontInfo(Font font)
|
|
{
|
|
return GetFontInfo(CHARSET_DEFAULT, font);
|
|
}
|
|
|
|
FontInfo Draw::GetFontInfoW(Font font)
|
|
{
|
|
return GetFontInfo(CHARSET_UNICODE, font);
|
|
}
|
|
|
|
void Draw::SetFont(Font font, int angle) {
|
|
DrawLock __;
|
|
LLOG("Set font: " << font << " face: " << font.GetFaceName());
|
|
if(lastFont && lastFont.IsEqual(CHARSET_UNICODE, font, angle, device))
|
|
return;
|
|
#ifdef PLATFORM_WIN32
|
|
lastFont = Acquire(font, GetHandle(), angle, device);
|
|
HFONT h = (HFONT) SelectObject(handle, lastFont.ptr->hfont);
|
|
if(!orgFont) orgFont = h;
|
|
#endif
|
|
#ifdef PLATFORM_X11
|
|
lastFont = Acquire(font, angle, device);
|
|
#endif
|
|
}
|
|
|
|
WString TextUnicode(const char *s, int n, byte cs, Font font)
|
|
{
|
|
if(n < 0)
|
|
n = (int)strlen(s);
|
|
if(font.GetFace() == Font::SYMBOL) {
|
|
WStringBuffer b(n);
|
|
wchar *t = b;
|
|
while(n > 0) {
|
|
*t++ = *s++;
|
|
n--;
|
|
}
|
|
return b;
|
|
}
|
|
else
|
|
return ToUnicode(s, n, cs);
|
|
}
|
|
|
|
FontInfo::CharMetrics *FontInfo::CreateMetricsPage(int page) const
|
|
{
|
|
DrawLock __;
|
|
CharMetrics *cm;
|
|
cm = new CharMetrics[32];
|
|
ptr->GetMetrics(cm, page << 5, 32);
|
|
if(page >= 8 && page < 12)
|
|
ComposeMetrics(ptr->font, cm, page);
|
|
return cm;
|
|
}
|
|
|
|
FontInfo::CharMetrics *FontInfo::GetPage(int page) const
|
|
{
|
|
ASSERT(page >= 0 && page < 64);
|
|
ONCELOCK_PTR(ptr->base[page], CreateMetricsPage(page));
|
|
return ptr->base[page];
|
|
}
|
|
|
|
void Draw::DrawText(int x, int y, int angle, const wchar *text, Font font,
|
|
Color ink, int n, const int *dx)
|
|
{
|
|
if(IsNull(ink)) return;
|
|
if(n < 0)
|
|
n = wstrlen(text);
|
|
ComposeText(x, y, angle, text, font, ink, n, dx);
|
|
}
|
|
|
|
// ----------------------------
|
|
|
|
void Draw::DrawText(int x, int y, const wchar *text, Font font,
|
|
Color ink, int n, const int *dx)
|
|
{
|
|
DrawText(x, y, 0, text, font, ink, n, dx);
|
|
}
|
|
|
|
// ---------------------------
|
|
|
|
void Draw::DrawText(int x, int y, int angle, const WString& text, Font font,
|
|
Color ink, const int *dx)
|
|
{
|
|
DrawText(x, y, angle, ~text, font, ink, text.GetLength(), dx);
|
|
}
|
|
|
|
void Draw::DrawText(int x, int y, const WString& text, Font font, Color ink, const int *dx)
|
|
{
|
|
DrawText(x, y, 0, text, font, ink, dx);
|
|
}
|
|
|
|
// ---------------------------
|
|
|
|
void Draw::DrawText(int x, int y, int angle, const char *text, byte charset, Font font,
|
|
Color ink, int n, const int *dx)
|
|
{
|
|
DrawText(x, y, angle, TextUnicode(text, n, charset, font), font, ink, dx);
|
|
}
|
|
|
|
void Draw::DrawText(int x, int y, const char *text, byte charset, Font font,
|
|
Color ink, int n, const int *dx)
|
|
{
|
|
DrawText(x, y, 0, text, charset, font, ink, n, dx);
|
|
}
|
|
|
|
// ---------------------------
|
|
|
|
void Draw::DrawText(int x, int y, int angle, const char *text,
|
|
Font font, Color ink, int n, const int *dx)
|
|
{
|
|
DrawText(x, y, angle, text, CHARSET_DEFAULT, font, ink, n, dx);
|
|
}
|
|
|
|
void Draw::DrawText(int x, int y, const char *text, Font font,
|
|
Color ink, int n, const int *dx)
|
|
{
|
|
DrawText(x, y, text, CHARSET_DEFAULT, font, ink, n, dx);
|
|
}
|
|
|
|
// ---------------------------
|
|
|
|
void Draw::DrawText(int x, int y, int angle, const String& text, Font font,
|
|
Color ink, const int *dx)
|
|
{
|
|
DrawText(x, y, angle, text, font, ink, text.GetLength(), dx);
|
|
}
|
|
|
|
void Draw::DrawText(int x, int y, const String& text, Font font, Color ink, const int *dx)
|
|
{
|
|
WString h = TextUnicode(text, text.GetLength(), CHARSET_DEFAULT, font);
|
|
DrawText(x, y, h, font, ink, h.GetLength(), dx);
|
|
}
|
|
|
|
// --------------------------
|
|
|
|
Size GetTextSize(const wchar *text, Font font, int n)
|
|
{
|
|
FontInfo fi = font.Info();
|
|
if(n < 0)
|
|
n = wstrlen(text);
|
|
Size sz;
|
|
sz.cx = 0;
|
|
const wchar *wtext = (const wchar *)text;
|
|
while(n > 0) {
|
|
sz.cx += fi[*wtext++];
|
|
n--;
|
|
}
|
|
sz.cy = fi.GetHeight();
|
|
return sz;
|
|
}
|
|
|
|
Size GetTextSize(const WString& text, Font font)
|
|
{
|
|
return GetTextSize(text, font, text.GetLength());
|
|
}
|
|
|
|
Size GetTextSize(const char *text, byte charset, Font font, int n)
|
|
{
|
|
return GetTextSize(TextUnicode(text, n, charset, font), font);
|
|
}
|
|
|
|
Size GetTextSize(const char *text, Font font, int n)
|
|
{
|
|
return GetTextSize(text, CHARSET_DEFAULT, font, n);
|
|
}
|
|
|
|
Size GetTextSize(const String& text, Font font)
|
|
{
|
|
return GetTextSize(text, font, text.GetLength());
|
|
}
|
|
|
|
Font Draw::GetStdFont()
|
|
{
|
|
return AStdFont;
|
|
}
|
|
|
|
#endif
|
|
|
|
END_UPP_NAMESPACE
|