#include <stdlib.h>
# include <ft2build.h>
# include FT_FREETYPE_H
# include FT_GLYPH_H
#include "TGX11TTF.h"
#include "TClass.h"
#include "TEnv.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/xpm.h>
struct RXColor:XColor{};
struct RVisual:Visual{};
struct RXImage:XImage{};
#ifdef R__HAS_XFT
#include "THashTable.h"
#include "TRefCnt.h"
#include <X11/Xft/Xft.h>
class TXftFontData : public TNamed, public TRefCnt {
public:
XFontStruct *fFontStruct;
XftFont *fXftFont;
TXftFontData(FontStruct_t font, XftFont *xftfont, const char *name) :
TNamed(name, ""), TRefCnt(), fXftFont(xftfont)
{
SetRefCount(1);
fFontStruct = (XFontStruct*)font;
}
~TXftFontData()
{
if (fFontStruct) ((TGX11*)gVirtualX)->DeleteFont((FontStruct_t)fFontStruct);
if (fXftFont) XftFontClose((Display*)gVirtualX->GetDisplay(), fXftFont);
}
};
class TXftFontHash {
public:
THashTable *fList;
TXftFontHash() { fList = new THashTable(50); }
TXftFontData *FindByName(const char *name)
{
return (TXftFontData*)fList->FindObject(name);
}
TXftFontData *FindByStruct(FontStruct_t font)
{
TIter next(fList);
TXftFontData *d = 0;
while ((d = (TXftFontData*) next())) {
if (d->fFontStruct == (XFontStruct*)font) {
return d;
}
}
return 0;
}
TXftFontData *FindByHandle(FontH_t id)
{
TIter next(fList);
TXftFontData *d = 0;
while ((d = (TXftFontData*) next())) {
if (d->fFontStruct->fid == id) {
return d;
}
}
return 0;
}
void AddFont(TXftFontData *data)
{
fList->Add(data);
}
void FreeFont(TXftFontData *data)
{
if (data->RemoveReference() > 0) return;
fList->Remove(data);
delete data;
}
};
#endif // R__HAS_XFT
class TTFX11Init {
public:
TTFX11Init() { TGX11TTF::Activate(); }
};
static TTFX11Init gTTFX11Init;
ClassImp(TGX11TTF)
TGX11TTF::TGX11TTF(const TGX11 &org) : TGX11(org)
{
SetName("X11TTF");
SetTitle("ROOT interface to X11 with TrueType fonts");
if (!TTF::fgInit) TTF::Init();
fHasTTFonts = kTRUE;
fAlign.x = 0;
fAlign.y = 0;
#ifdef R__HAS_XFT
fXftFontHash = 0;
if (gEnv->GetValue("X11.UseXft", 0)) {
fXftFontHash = new TXftFontHash();
}
#endif
}
void TGX11TTF::Activate()
{
if (gVirtualX && dynamic_cast<TGX11*>(gVirtualX)) {
TGX11 *oldg = (TGX11 *) gVirtualX;
gVirtualX = new TGX11TTF(*oldg);
delete oldg;
}
}
Bool_t TGX11TTF::Init(void *display)
{
Bool_t r = TGX11::Init(display);
if (fDepth > 8) {
TTF::SetSmoothing(kTRUE);
} else {
TTF::SetSmoothing(kFALSE);
}
return r;
}
void TGX11TTF::Align(void)
{
EAlign align = (EAlign) fTextAlign;
if (align == kTLeft || align == kTCenter || align == kTRight) {
fAlign.y = TTF::fgAscent;
} else if (align == kMLeft || align == kMCenter || align == kMRight) {
fAlign.y = TTF::fgAscent/2;
} else {
fAlign.y = 0;
}
if (align == kTRight || align == kMRight || align == kBRight) {
fAlign.x = TTF::fgWidth;
} else if (align == kTCenter || align == kMCenter || align == kBCenter) {
fAlign.x = TTF::fgWidth/2;
} else {
fAlign.x = 0;
}
FT_Vector_Transform(&fAlign, TTF::fgRotMatrix);
fAlign.x = fAlign.x >> 6;
fAlign.y = fAlign.y >> 6;
}
void TGX11TTF::DrawImage(FT_Bitmap *source, ULong_t fore, ULong_t back,
RXImage *xim, Int_t bx, Int_t by)
{
UChar_t d = 0, *s = source->buffer;
if (TTF::fgSmoothing) {
static RXColor col[5];
RXColor *bcol = 0;
XColor *bc;
Int_t x, y;
if (back == (ULong_t) -1 && (UInt_t)source->width) {
ULong_t r, g, b;
Int_t dots, dotcnt;
const Int_t maxdots = 50000;
dots = Int_t(source->width * source->rows);
dots = dots > maxdots ? maxdots : dots;
bcol = new RXColor[dots];
if (!bcol) return;
bc = bcol;
dotcnt = 0;
for (y = 0; y < (int) source->rows; y++) {
for (x = 0; x < (int) source->width; x++, bc++) {
bc->pixel = XGetPixel(xim, bx + x, by + y);
bc->flags = DoRed | DoGreen | DoBlue;
if (++dotcnt >= maxdots) break;
}
}
QueryColors(fColormap, bcol, dots);
r = g = b = 0;
bc = bcol;
dotcnt = 0;
for (y = 0; y < (int) source->rows; y++) {
for (x = 0; x < (int) source->width; x++, bc++) {
r += bc->red;
g += bc->green;
b += bc->blue;
if (++dotcnt >= maxdots) break;
}
}
if (dots != 0) {
r /= dots;
g /= dots;
b /= dots;
}
bc = &col[0];
if (bc->red == r && bc->green == g && bc->blue == b)
bc->pixel = back;
else {
bc->pixel = ~back;
bc->red = (UShort_t) r;
bc->green = (UShort_t) g;
bc->blue = (UShort_t) b;
}
}
delete [] bcol;
if (fore != col[4].pixel || back != col[0].pixel) {
col[4].pixel = fore;
col[4].flags = DoRed|DoGreen|DoBlue;
if (back != (ULong_t) -1) {
col[3].pixel = back;
col[3].flags = DoRed | DoGreen | DoBlue;
QueryColors(fColormap, &col[3], 2);
col[0] = col[3];
} else {
QueryColors(fColormap, &col[4], 1);
}
for (x = 3; x > 0; x--) {
col[x].red = (col[4].red *x + col[0].red *(4-x)) /4;
col[x].green = (col[4].green*x + col[0].green*(4-x)) /4;
col[x].blue = (col[4].blue *x + col[0].blue *(4-x)) /4;
if (!AllocColor(fColormap, &col[x])) {
Warning("DrawImage", "cannot allocate smoothing color");
col[x].pixel = col[x+1].pixel;
}
}
}
for (y = 0; y < (int) source->rows; y++) {
for (x = 0; x < (int) source->width; x++) {
d = *s++ & 0xff;
d = ((d + 10) * 5) / 256;
if (d > 4) d = 4;
if (d && x < (int) source->width) {
ULong_t p = col[d].pixel;
XPutPixel(xim, bx + x, by + y, p);
}
}
}
} else {
UChar_t* row=s;
for (int y = 0; y < (int) source->rows; y++) {
int n = 0;
s = row;
for (int x = 0; x < (int) source->width; x++) {
if (n == 0) d = *s++;
if (TESTBIT(d,7-n))
XPutPixel(xim, bx + x, by + y, fore);
if (++n == (int) kBitsPerByte) n = 0;
}
row += source->pitch;
}
}
}
void TGX11TTF::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn,
const char *text, ETextMode mode)
{
if (!fHasTTFonts) {
TGX11::DrawText(x, y, angle, mgn, text, mode);
} else {
if (!TTF::fgInit) TTF::Init();
TTF::SetRotationMatrix(angle);
TTF::PrepareString(text);
TTF::LayoutGlyphs();
Align();
RenderString(x, y, mode);
}
}
void TGX11TTF::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn,
const wchar_t *text, ETextMode mode)
{
if (!fHasTTFonts) {
TGX11::DrawText(x, y, angle, mgn, text, mode);
} else {
if (!TTF::fgInit) TTF::Init();
TTF::SetRotationMatrix(angle);
TTF::PrepareString(text);
TTF::LayoutGlyphs();
Align();
RenderString(x, y, mode);
}
}
RXImage *TGX11TTF::GetBackground(Int_t x, Int_t y, UInt_t w, UInt_t h)
{
Window_t cws = GetCurrentWindow();
UInt_t width;
UInt_t height;
Int_t xy;
gVirtualX->GetWindowSize(cws, xy, xy, width, height);
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
if (x+w > width) w = width - x;
if (y+h > height) h = height - y;
return (RXImage*)XGetImage((Display*)fDisplay, cws, x, y, w, h, AllPlanes, ZPixmap);
}
Bool_t TGX11TTF::IsVisible(Int_t x, Int_t y, UInt_t w, UInt_t h)
{
Window_t cws = GetCurrentWindow();
UInt_t width;
UInt_t height;
Int_t xy;
gVirtualX->GetWindowSize(cws, xy, xy, width, height);
if ((int)w == 0 || (int)h == 0) return kFALSE;
if (x + (int)w <= 0 || x >= (int)width) return kFALSE;
if (y + (int)h <= 0 || y >= (int)height) return kFALSE;
if (w > 10*width) return kFALSE;
if (h > 10*height) return kFALSE;
return kTRUE;
}
void TGX11TTF::RenderString(Int_t x, Int_t y, ETextMode mode)
{
TTGlyph* glyph = TTF::fgGlyphs;
GC *gc;
Int_t Xoff = 0; if (TTF::GetBox().xMin < 0) Xoff = -TTF::GetBox().xMin;
Int_t Yoff = 0; if (TTF::GetBox().yMin < 0) Yoff = -TTF::GetBox().yMin;
Int_t w = TTF::GetBox().xMax + Xoff;
Int_t h = TTF::GetBox().yMax + Yoff;
Int_t x1 = x-Xoff-fAlign.x;
Int_t y1 = y+Yoff+fAlign.y-h;
if (!IsVisible(x1, y1, w, h)) return;
UInt_t depth = fDepth;
XImage *xim = 0;
xim = XCreateImage((Display*)fDisplay, fVisual,
depth, ZPixmap, 0, 0, w, h,
depth == 24 ? 32 : (depth==15?16:depth), 0);
if (!xim) return;
xim->data = (char *) malloc(xim->bytes_per_line * h);
memset(xim->data, 0, xim->bytes_per_line * h);
ULong_t bg;
XGCValues values;
gc = (GC*)GetGC(3);
if (!gc) {
Error("DrawText", "error getting Graphics Context");
return;
}
XGetGCValues((Display*)fDisplay, *gc, GCForeground | GCBackground, &values);
if (mode == kClear) {
XImage *bim = GetBackground(x1, y1, w, h);
if (!bim) {
Error("DrawText", "error getting background image");
return;
}
Int_t xo = 0, yo = 0;
if (x1 < 0) xo = -x1;
if (y1 < 0) yo = -y1;
for (int yp = 0; yp < (int) bim->height; yp++) {
for (int xp = 0; xp < (int) bim->width; xp++) {
ULong_t pixel = XGetPixel(bim, xp, yp);
XPutPixel(xim, xo+xp, yo+yp, pixel);
}
}
XDestroyImage(bim);
bg = (ULong_t) -1;
} else {
XAddPixel(xim, values.background);
bg = values.background;
}
glyph = TTF::fgGlyphs;
for (int n = 0; n < TTF::fgNumGlyphs; n++, glyph++) {
if (FT_Glyph_To_Bitmap(&glyph->fImage,
TTF::fgSmoothing ? ft_render_mode_normal
: ft_render_mode_mono,
0, 1 )) continue;
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph->fImage;
FT_Bitmap* source = &bitmap->bitmap;
Int_t bx, by;
bx = bitmap->left+Xoff;
by = h - bitmap->top-Yoff;
DrawImage(source, values.foreground, bg, (RXImage*)xim, bx, by);
}
Window_t cws = GetCurrentWindow();
gc = (GC*)GetGC(6);
if (gc) XPutImage((Display*)fDisplay, cws, *gc, xim, 0, 0, x1, y1, w, h);
XDestroyImage(xim);
}
void TGX11TTF::SetTextFont(Font_t fontnumber)
{
fTextFont = fontnumber;
if (!fHasTTFonts) {
TGX11::SetTextFont(fontnumber);
} else {
TTF::SetTextFont(fontnumber);
}
}
Int_t TGX11TTF::SetTextFont(char *fontname, ETextSetMode mode)
{
if (!fHasTTFonts) {
return TGX11::SetTextFont(fontname, mode);
} else {
return TTF::SetTextFont(fontname);
}
}
void TGX11TTF::SetTextSize(Float_t textsize)
{
fTextSize = textsize;
if (!fHasTTFonts) {
TGX11::SetTextSize(textsize);
} else {
TTF::SetTextSize(textsize);
}
}
#ifdef R__HAS_XFT
FontStruct_t TGX11TTF::LoadQueryFont(const char *font_name)
{
if (!fXftFontHash) {
return TGX11::LoadQueryFont(font_name);
}
TXftFontData *data = fXftFontHash->FindByName(font_name);
if (data) {
data->AddReference();
return (FontStruct_t)data->fFontStruct;
}
FontStruct_t font = TGX11::LoadQueryFont(font_name);
if (!font) {
return font;
}
XftFont *xftfont = XftFontOpenXlfd((Display*)fDisplay, fScreenNumber, font_name);
data = new TXftFontData(font, xftfont, font_name);
fXftFontHash->AddFont(data);
return font;
}
void TGX11TTF::DeleteFont(FontStruct_t fs)
{
if (!fXftFontHash) {
TGX11::DeleteFont(fs);
return;
}
TXftFontData *data = fXftFontHash->FindByStruct(fs);
if (!data) {
TGX11::DeleteFont(fs);
return;
}
fXftFontHash->FreeFont(data);
}
Int_t TGX11TTF::TextWidth(FontStruct_t font, const char *s, Int_t len)
{
if (!fXftFontHash) {
return TGX11::TextWidth(font, s, len);
}
TXftFontData *data = fXftFontHash->FindByStruct(font);
if (!data) {
return TGX11::TextWidth(font, s, len);
}
XftFont *xftfont = data->fXftFont;
if (!xftfont) {
return TGX11::TextWidth(font, s, len);
}
XGlyphInfo glyph_info;
XftTextExtents8((Display*)fDisplay, xftfont, (XftChar8 *)s, len, &glyph_info);
return glyph_info.xOff;
}
void TGX11TTF::GetFontProperties(FontStruct_t font, Int_t &max_ascent, Int_t &max_descent)
{
if (!fXftFontHash) {
TGX11::GetFontProperties(font, max_ascent, max_descent);
return;
}
TXftFontData *data = fXftFontHash->FindByStruct(font);
if (!data) {
TGX11::GetFontProperties(font, max_ascent, max_descent);
return;
}
XftFont *xftfont = data->fXftFont;
if (!xftfont) {
TGX11::GetFontProperties(font, max_ascent, max_descent);
return;
}
max_ascent = xftfont->ascent;
max_descent = xftfont->descent;
}
void TGX11TTF::DrawString(Drawable_t xwindow, GContext_t gc, Int_t x, Int_t y,
const char *text, Int_t len)
{
XftDraw *xftdraw;
XftColor xftcolor;
XColor xcolor;
XftFont *xftfont;
if (!xwindow) {
return;
}
if (!gc) {
return;
}
if (!text || (len < 1) || !text[0]) {
return;
}
if (!fXftFontHash) {
TGX11::DrawString(xwindow, gc, x, y, text, len);
return;
}
GCValues_t gval;
gval.fMask = kGCForeground | kGCBackground | kGCFont;
GetGCValues(gc, gval);
TXftFontData *data = fXftFontHash->FindByHandle(gval.fFont);
if (!data) {
TGX11::DrawString(xwindow, gc, x, y, text, len);
return;
}
xftfont = data->fXftFont;
if (!xftfont) {
TGX11::DrawString(xwindow, gc, x, y, text, len);
return;
}
Window droot;
Int_t dx,dy;
UInt_t bwidth, width, height, depth;
XGetGeometry((Display*)fDisplay, (Drawable)xwindow, &droot, &dx, &dy,
&width, &height, &bwidth, &depth);
if (depth <= 1) {
TGX11::DrawString(xwindow, gc, x, y, text, len);
return;
}
memset(&xcolor, 0, sizeof(xcolor));
xcolor.pixel = gval.fForeground;
XQueryColor((Display*)fDisplay, fColormap, &xcolor);
xftdraw = XftDrawCreate((Display*)fDisplay, (Drawable)xwindow, fVisual, fColormap);
if (!xftdraw) {
TGX11::DrawString(xwindow, gc, x, y, text, len);
return;
}
xftcolor.color.red = xcolor.red;
xftcolor.color.green = xcolor.green;
xftcolor.color.blue = xcolor.blue;
xftcolor.color.alpha = 0xffff;
xftcolor.pixel = gval.fForeground;
XftDrawString8(xftdraw, &xftcolor, xftfont, x, y, (XftChar8 *)text, len);
XftDrawDestroy(xftdraw);
}
#endif // R__HAS_XFT