ultimatepp/uppsrc/Draw/DrawTextWin32.cpp
mdelfede d2b54f7989 changed svn layout
git-svn-id: svn://ultimatepp.org/upp/trunk@281 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2008-06-07 22:31:27 +00:00

417 lines
11 KiB
C++

#include "Draw.h"
NAMESPACE_UPP
#ifdef PLATFORM_WIN32
#define LLOG(x) // LOG(x)
#define LTIMING(x)
struct FontFaceInfo : Moveable<FontFaceInfo> {
String name;
dword info;
FontFaceInfo() { info = 0; }
};
static VectorMap<String, FontFaceInfo>& sFontFace()
{
static VectorMap<String, FontFaceInfo> s;
return s;
}
int Font::GetFaceCount()
{
if(!Draw::sFini) Draw::InitFonts();
return sFontFace().GetCount();
}
String Font::GetFaceName(int index) {
if(!Draw::sFini) Draw::InitFonts();
return index >= 0 && index < sFontFace().GetCount() ? sFontFace()[index].name
: Null;
}
dword Font::GetFaceInfo(int index) {
if(!Draw::sFini) Draw::InitFonts();
return index >= 0 && index < sFontFace().GetCount() ? sFontFace()[index].info
: 0;
}
int CALLBACK Draw::AddFace(const LOGFONT *logfont, const TEXTMETRIC *, dword type, LPARAM param)
{
#ifdef PLATFORM_WINCE
const wchar *facename = (const wchar *)param;
if(facename && _wcsicmp(logfont->lfFaceName, facename))
return 1;
#else
const char *facename = (const char *)param;
if(facename && stricmp(logfont->lfFaceName, facename))
return 1;
#endif
dword typ = 0;
if((logfont->lfPitchAndFamily & 3) == FIXED_PITCH)
typ |= Font::FIXEDPITCH;
if(type & TRUETYPE_FONTTYPE)
typ |= Font::SCALEABLE;
if(logfont->lfCharSet == SYMBOL_CHARSET)
typ |= Font::SYMBOLTYPE;
else
if(logfont->lfCharSet != 0)
typ |= Font::LOCAL;
#ifndef PLATFORM_WINCE
{
typedef DWORD (WINAPI *GGIW)(HDC, LPCWSTR, int, LPWORD, DWORD);
static GGIW fn;
ONCELOCK
if(HMODULE hDLL = LoadLibrary("gdi32"))
fn = (GGIW) GetProcAddress(hDLL, "GetGlyphIndicesW");
if(fn) {
HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL);
HFONT hfnt = (HFONT) CreateFontIndirect(logfont);
HFONT o = (HFONT) SelectObject(hdc, hfnt);
wchar wch[128];
WORD pos[128];
for(int i = 0; i < 128; i++)
wch[i] = i + 256;
(*fn)(hdc, (LPCWSTR) wch, 128, pos, 1);
SelectObject(hdc, o);
DeleteObject(hfnt);
DeleteDC(hdc);
int n = 0;
for(int i = 0; i < 128; i++)
if(pos[i] == 0xffff)
n++;
if(n > 10)
typ |= Font::COMPOSED;
}
}
#endif
#ifdef PLATFORM_WINCE
if(facename) {
FontFaceInfo& f = sFontFace().Add(WString(logfont->lfFaceName).ToString());
f.name = WString(facename).ToString();
return 0;
}
FontFaceInfo& f = sFontFace().Add(WString(logfont->lfFaceName).ToString());
f.name = FromSystemCharset(logfont->lfFaceName);
#else
if(facename) {
FontFaceInfo& f = sFontFace().Add(logfont->lfFaceName);
f.name = facename;
f.info = typ;
return 0;
}
FontFaceInfo& f = sFontFace().Add(logfont->lfFaceName);
f.name = FromSystemCharset(logfont->lfFaceName);
#endif
f.info |= typ;
return 1;
}
int Draw::EnumFace(HDC hdc, const char *face)
{
#ifdef PLATFORM_WINCE
return EnumFontFamilies(hdc, ToSystemCharset(face), AddFace, (LPARAM)~ToSystemCharset(face));
#else
return EnumFontFamilies(hdc, face, AddFace, (LPARAM)face);
#endif
}
void Draw::ForceFace(HDC hdc, const char *face, const char *aface)
{
if(!aface)
aface = "Arial";
if(EnumFace(hdc, face) && (aface == NULL || EnumFace(hdc, aface)))
Panic("Missing font " + String(face));
}
#ifdef PLATFORM_WINCE
const char *Draw::FontScreenSans = "Tahoma"; //TODO!
const char *Draw::FontScreenSerif = "Tahoma";
const char *Draw::FontScreenFixed = "Courier New";
const char *Draw::FontRoman = "Tahoma" ;
const char *Draw::FontArial = "Tahoma";
const char *Draw::FontCourier = "Tahoma";
const char *Draw::FontSymbol = "Tahoma";
const char *Draw::FontWingdings = "Tahoma";
const char *Draw::FontTahoma = "Tahoma";
#else
const char *Draw::FontScreenSans = "Arial";
const char *Draw::FontScreenSerif = "Times New Roman";
const char *Draw::FontScreenFixed = "Courier New";
const char *Draw::FontRoman = "Times New Roman" ;
const char *Draw::FontArial = "Arial";
const char *Draw::FontCourier = "Courier New";
const char *Draw::FontSymbol = "Symbol";
const char *Draw::FontWingdings = "WingDings";
const char *Draw::FontTahoma = "Tahoma";
#endif
void Draw::InitPlatformFonts() {
#ifdef PLATFORM_WINCE
HDC hdc = CreateDC(NULL, NULL, NULL, NULL);
#else
HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL);
#endif
ForceFace(hdc, FontScreenSans, NULL);
ForceFace(hdc, FontScreenSerif, FontScreenSans);
ForceFace(hdc, FontScreenSans, FontScreenSans);
ForceFace(hdc, FontScreenFixed, FontScreenSans);
ForceFace(hdc, FontRoman, FontScreenSans);
ForceFace(hdc, FontArial, FontScreenSans);
ForceFace(hdc, FontCourier, FontScreenSans);
ForceFace(hdc, FontSymbol, FontScreenSans);
ForceFace(hdc, FontWingdings, FontArial);
ForceFace(hdc, FontTahoma, FontArial);
EnumFace(hdc, NULL);
DeleteDC(hdc);
#ifdef PLATFORM_WINCE
SetStdFont(Arial(10));
#else
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
SetStdFont(Font(Font::FindFaceNameIndex(ncm.lfMenuFont.lfFaceName),
abs((int)ncm.lfMenuFont.lfHeight)));
#endif
}
FontInfo::Data::Data()
{
refcount = 1;
hfont = NULL;
for(int i = 0; i < 64; i++)
base[i] = NULL;
}
FontInfo::Data::~Data()
{
DrawLock __;
if(hfont)
DeleteObject(hfont);
for(int i = 0; i < 64; i++)
if(base[i]) delete[] base[i];
}
int sGetCW(HDC hdc, wchar *h, int n)
{
SIZE sz;
return GetTextExtentPoint32W(hdc, h, n, &sz) ? sz.cx : 0;
}
void FontInfo::Data::GetMetrics(CharMetrics *t, int from, int count)
{
DrawLock __;
HDC hdc = ScreenHDC();
HFONT ohfont = (HFONT) ::SelectObject(hdc, hfont);
if(from >= 8192) {
TIMING("GetMetricsSlow");
wchar h[3];
h[0] = 'x';
h[1] = 'x';
h[2] = 'x';
int w0 = sGetCW(hdc, h, 2);
for(int i = 0; i < count; i++) {
h[1] = from + i;
t[i].width = sGetCW(hdc, h, 3) - w0;
t[i].lspc = t[i].rspc = 0;
}
}
else {
bool abca = false, abcw = false;
Buffer<ABC> abc(count);
#ifdef PLATFORM_WINCE
if(scaleable)
abcw = ::GetCharABCWidths(hdc, from, from + count - 1, abc);
#else
if(scaleable && !(abcw = ::GetCharABCWidthsW(hdc, from, from + count - 1, abc)))
abca = ::GetCharABCWidthsA(hdc, from, from + count - 1, abc);
#endif
if(abcw)
{
for(ABC *s = abc, *lim = abc + count; s < lim; s++, t++)
{
t->width = s->abcA + s->abcB + s->abcC;
t->lspc = s->abcA;
t->rspc = s->abcC;
}
}
else
{
Buffer<int> wb(count);
#ifdef PLATFORM_WINCE
::GetCharWidth32(hdc, from, from + count - 1, wb);
#else
::GetCharWidthW(hdc, from, from + count - 1, wb);
#endif
for(int *s = wb, *lim = wb + count; s < lim; s++, t++)
{
t->width = *s - overhang;
if(abca)
{
ABC aa = abc[(byte)ToAscii(from++)];
t->lspc = aa.abcA;
t->rspc = aa.abcC;
}
else
t->lspc = t->rspc = 0;
}
}
}
::SelectObject(hdc, ohfont);
}
FontInfo Draw::Acquire(Font font, HDC hdc, int angle, int device)
{
DrawLock __;
if(IsNull(font))
font = StdFont();
if(font.GetFace() == 0)
font.Face(AStdFont.GetFace());
if(font.GetHeight() == 0)
font.Height(AStdFont.GetHeight());
// if(font.GetFace() >= sFontFace().GetCount())
// font.SetFace(Font::ARIAL);
FontInfo::Data *f, *fh;
f = fh = GetFontHash((font.GetHashValue() ^ angle ^ (device << 9)) % FONTHASH);
LLOG("Acquire " << font);
for(;;) {
f = f->GetNext(HASH);
if(f == fh) break;
if(f->font == font && f->angle == angle && f->device == device)
{
LLOG("Reusing " << f->font << " count:" << f->count);
if(f->InList(LRU)) {
f->Unlink(LRU);
FontCached--;
LLOG("Removing from cache " << f->font << " count:" << f->count <<
" cached:" << FontCached);
}
f->refcount++;
return f;
}
}
LLOG("New " << font);
LTIMING("Acquire New");
f = fh->InsertNext(HASH);
f->font = font;
f->angle = angle;
f->device = device;
byte chrset;
if((sFontFace()[font.GetFace()].info & Font::SCALEABLE) == 0)
chrset = DEFAULT_CHARSET;
else
if(sFontFace()[font.GetFace()].info & Font::SYMBOLTYPE)
chrset = SYMBOL_CHARSET;
else
chrset = DEFAULT_CHARSET;
#ifdef PLATFORM_WINCE
LOGFONT lfnt;
Zero(lfnt);
lfnt.lfHeight = font.GetHeight() ? -abs(font.GetHeight()) : -12;
lfnt.lfWeight = font.IsBold() ? FW_BOLD : FW_NORMAL;
lfnt.lfItalic = font.IsItalic();
lfnt.lfUnderline = font.IsUnderline();
lfnt.lfStrikeOut = font.IsStrikeout();
wcscpy(lfnt.lfFaceName, ToSystemCharset(font.GetFaceName()));
f->hfont = CreateFontIndirect(&lfnt);
#else
f->hfont = CreateFont(font.GetHeight() ? -abs(font.GetHeight()) : -12,
font.GetWidth(), angle, angle, font.IsBold() ? FW_BOLD : FW_NORMAL,
font.IsItalic(), font.IsUnderline(), font.IsStrikeout(),
chrset,
font.IsTrueTypeOnly() ? OUT_TT_ONLY_PRECIS : OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
font.IsNonAntiAliased() ? NONANTIALIASED_QUALITY
: DEFAULT_QUALITY,
DEFAULT_PITCH|FF_DONTCARE,
sFontFace().GetKey(font.GetFace()));
#endif
ASSERT(f->hfont);
TEXTMETRIC tm;
HFONT hfont = (HFONT) ::SelectObject(hdc, f->hfont);
::GetTextMetrics(hdc, &tm);
f->ascent = tm.tmAscent;
f->descent = tm.tmDescent;
f->external = tm.tmExternalLeading;
f->internal = tm.tmInternalLeading;
f->height = f->ascent + f->descent;
f->lineheight = f->height + f->external;
f->overhang = tm.tmOverhang;
f->avewidth = tm.tmAveCharWidth;
f->maxwidth = tm.tmMaxCharWidth;
f->firstchar = tm.tmFirstChar;
f->charcount = tm.tmLastChar - tm.tmFirstChar + 1;
f->default_char = tm.tmDefaultChar;
f->fixedpitch = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0;
f->scaleable = tm.tmPitchAndFamily & TMPF_TRUETYPE;
f->kerning.Clear();
if(f->scaleable) {
ABC abc;
GetCharABCWidths(hdc, 'o', 'o', &abc);
f->spacebefore = abc.abcA;
f->spaceafter = abc.abcC;
}
else
f->spacebefore = f->spaceafter = 0;
#ifndef PLATFORM_WINCE
int n = ::GetKerningPairs(hdc, 0, NULL);
if(n) {
Buffer<KERNINGPAIR> kp(n);
::GetKerningPairs(hdc, n, kp);
const KERNINGPAIR *p = kp;
while(n--) {
f->kerning.Add(MAKELONG(p->wFirst, p->wSecond), p->iKernAmount);
p++;
}
}
#endif
::SelectObject(hdc, hfont);
f->offset = Size(0, f->ascent);
if(angle) {
FontInfo font0 = Acquire(font, hdc, 0, device);
double sina, cosa;
SinCos(angle, sina, cosa);
f->offset.cx = fround(font0.GetAscent() * sina);
f->offset.cy = fround(font0.GetAscent() * cosa);
}
return FontInfo(f);
}
void Draw::DrawTextOp(int x, int y, int angle, const wchar *text, Font font, Color ink,
int n, const int *dx) {
while(n > 30000) {
DrawTextOp(x, y, angle, text, font, ink, 30000, dx);
if(dx) {
for(int i = 0; i < 30000; i++)
x += *dx++;
}
else
x += GetTextSize(text, font, 30000).cx;
n -= 30000;
text += 30000;
}
DrawLock __;
COLORREF cr = GetColor(ink);
if(cr != lastTextColor) {
LLOG("Setting text color: " << ink);
::SetTextColor(handle, lastTextColor = cr);
}
if(angle) {
SetFont(font, angle);
::ExtTextOutW(handle, x + lastFont.ptr->offset.cx, y + lastFont.ptr->offset.cy,
0, NULL, (const WCHAR *)text, n, dx);
}
else {
SetFont(font);
::ExtTextOutW(handle, x, y + lastFont.GetAscent(), 0, NULL, (const WCHAR *)text,
n, dx);
}
}
#endif
END_UPP_NAMESPACE