SGI: Development

Hello Xft -- an anti-aliased font rendering example

I finally found a sliver of time to do something I've been itchin' to try. I wrote a very small app that uses libXft and freetype2 to draw anti-aliased text in a X window. It is pure X and doesn't require Xt or any toolkit like Motif, GTK+ or Qt. Basically, Keith Packard has replaced the core X font calls with equivalents. Here's the little app, which I post here because there doesn't seem to be anything like it on the web. The closest is here: http://www.keithp.com/~keithp/render/Xft.tutorial

xfthello.c

Code: Select all

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef HAVE_XFT
#include <Xft/Xft.h>
#endif

#define DEFAULT_BDWIDTH 1 /* border width */

#ifdef HAVE_XFT
char *text = "Hello Xft";
#else
char *text = "Hello X11";
#endif

Display *dpy;

int main( int argc, char *argv[] )
{
int                     ytext, wtext, htext, gravity, geo_mask;
int                     Done = 0, highlight = 0, force_expose = 0;
XSizeHints              xsh;
XWMHints                xwmh;
unsigned long           fgpix, bgpix;
Window                  mainW;
XSetWindowAttributes    xswa;
#ifdef HAVE_XFT
XftFont                 *font;
XftDraw                 *xftdraw;
XRenderColor            xrcolor;
XftColor                xftcolor, hlcolor;
XGlyphInfo              extents;
#else
GC                      gc;
XGCValues               gcvals;
XFontStruct             *font;
#endif
XEvent                  event;
char                    user_geo[80];

/* open connection to X display */
dpy = XOpenDisplay( NULL );

/* set up font */
#ifdef HAVE_XFT
font = XftFontOpenName( dpy, DefaultScreen( dpy ), "morpheus-18" );
#else
font = XLoadQueryFont( dpy, "-*-helvetica-medium-r-normal--20-*-*-*-p-100-iso8859-1" );
#endif

/* colors */
fgpix = BlackPixel( dpy, DefaultScreen( dpy ) );
bgpix = WhitePixel( dpy, DefaultScreen( dpy ) );

/* position and size of top window (XSizeHints) */
#ifdef HAVE_XFT
XftTextExtents8( dpy, font, (XftChar8 *)text, strlen(text), &extents );
ytext = extents.height - extents.y;
wtext = extents.width - extents.x;
#else
ytext = font->max_bounds.ascent + font->max_bounds.descent;
wtext = font->max_bounds.width / 2 + XTextWidth( font, text, strlen(text) + 4 );
#endif
htext = ytext + 4;
xsh.flags = ( PPosition | PSize | PMinSize );
xsh.height = htext + 10;
xsh.min_height = xsh.height;
xsh.width = wtext;
xsh.min_width = xsh.width;
xsh.x = 50;
xsh.y = 50;

/* construct a geometry string */
sprintf( user_geo, "%dx%d+%d+%d", xsh.width, xsh.height, xsh.x, xsh.y );

/* process geometry specification */
geo_mask = XWMGeometry( dpy, DefaultScreen(dpy), user_geo, NULL/*def_geo*/,
DEFAULT_BDWIDTH, &xsh, &xsh.x, &xsh.y, &xsh.width, &xsh.height,
&gravity );

/* check geometry bitmask and set size hints */
if ( geo_mask & (XValue|YValue) ) xsh.flags |= USPosition;
if ( geo_mask & (WidthValue|HeightValue) ) xsh.flags |= USSize;

/* create top level window */
mainW = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), xsh.x, xsh.y,
xsh.width, xsh.height, DEFAULT_BDWIDTH, fgpix, bgpix );

/* set window manager properties */
XSetStandardProperties( dpy, mainW, "xhello", "xhello", None, argv, argc, &xsh );

/* set window manager hints */
xwmh.flags = (InputHint|StateHint);
xwmh.input = False;
xwmh.initial_state = NormalState;
XSetWMHints( dpy, mainW, &xwmh );

#ifdef HAVE_XFT
/* Xft draw context */
xftdraw = XftDrawCreate( dpy, mainW, DefaultVisual(dpy,DefaultScreen(dpy)),
DefaultColormap( dpy, DefaultScreen(dpy) ) );
/* Xft text color */
xrcolor.red = 0x0;
xrcolor.green = 0x0;
xrcolor.blue = 0x0;
xrcolor.alpha = 0xffff;
XftColorAllocValue( dpy, DefaultVisual(dpy,DefaultScreen(dpy)),
DefaultColormap( dpy, DefaultScreen(dpy) ), &xrcolor, &xftcolor );
/* Xft highlight color */
xrcolor.red = 0xafff;
xrcolor.green = 0xafff;
xrcolor.blue = 0xffff;
xrcolor.alpha = 0xffff;
XftColorAllocValue( dpy, DefaultVisual(dpy,DefaultScreen(dpy)),
DefaultColormap( dpy, DefaultScreen(dpy) ), &xrcolor, &hlcolor );
#else
/* create graphics context */
gcvals.font = font->fid;
gcvals.foreground = fgpix;
gcvals.background = bgpix;
gc = XCreateGC( dpy, mainW, (GCFont|GCForeground|GCBackground), &gcvals );
#endif

/* set window attributes */
xswa.colormap = DefaultColormap( dpy, DefaultScreen(dpy) );
xswa.bit_gravity = CenterGravity;
XChangeWindowAttributes( dpy, mainW, (CWColormap|CWBitGravity), &xswa );

/* select inputs */
XSelectInput( dpy, mainW, ExposureMask|ButtonPressMask|EnterWindowMask|LeaveWindowMask );

/* make window visible */
XMapWindow( dpy, mainW );
printf("click on the window to exit\n");

/* retrieve and process events */
while ( !Done ) {
XNextEvent( dpy, &event );
if ( event.xany.window == mainW ) {
switch ( event.type ) {
case EnterNotify:
case LeaveNotify:
if ( event.type==EnterNotify) highlight = 1;
else highlight = 0;
force_expose = 1;
case Expose:
if ( event.xexpose.count == 0 || force_expose ) {
int            x, y, itmp;
unsigned int   w, h, utmp;
Window         wtmp;

if ( XGetGeometry( dpy, mainW, &wtmp, &itmp, &itmp, &w, &h, &utmp, &utmp )==0 )
break;
XClearWindow( dpy, mainW );
#ifdef HAVE_XFT
x = ( w - extents.width ) / 2;
y = htext + ( h - htext + extents.height )/2;
if (highlight) XftDrawRect( xftdraw, &hlcolor, x, y-extents.height, wtext, extents.height  );
XftDrawString8( xftdraw, &xftcolor, font, x, y, (XftChar8 *)text, strlen(text) );
#else
x = ( w - XTextWidth( font, text, strlen(text) ) ) / 2;
y = htext + ( h - htext + font->max_bounds.ascent - font->max_bounds.descent )/2;
XDrawString( dpy, mainW, gc, x, y, text, strlen(text) );
#endif
force_expose = 0;
}
break;
case ButtonPress:
Done = 1;
printf("good-bye!\n");
break;
default:
break;
}
}
}

/* close connection to display */
#ifdef HAVE_XFT
XftDrawDestroy( xftdraw );
XftColorFree( dpy, DefaultVisual(dpy,DefaultScreen(dpy)),
DefaultColormap( dpy, DefaultScreen(dpy) ), &xftcolor );
XftColorFree( dpy, DefaultVisual(dpy,DefaultScreen(dpy)),
DefaultColormap( dpy, DefaultScreen(dpy) ), &hlcolor );
#else
XFreeGC( dpy, gc );
#endif
XDestroyWindow( dpy, mainW );
XCloseDisplay( dpy );

return(0);
}



and the (GNU) Makefile that will built it.

Code: Select all

SHELL = /bin/sh

CFLAGS = -O0 -g -Wall
CPPFLAGS = -I. -I/usr/X11R6/include -L/usr/X11R6/lib
XFTFLAGS = -DHAVE_XFT -I/usr/nekoware/include -I/usr/nekoware/include/freetype2 -I/usr/nekoware/include/X11 -L/usr/nekoware/lib -Wl,-rpath -Wl,/usr/nekoware/lib

# uncomment for Apple OS X
#XFTFLAGS = -DHAVE_XFT -I/usr/X11R6/include/X11 -I/usr/X11R6/include/freetype2

LDFLAGS =  -L.  -L..
LIBS = -lX11
XFTLIBS = -lXft

%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $(XM) -o $@ $<

.PHONY: all
all: xhello xfthello

xhello: $(OBJS) xhello.c
$(CC) $(CFLAGS) $(CPPFLAGS) xhello.c -o xhello $(LDFLAGS) $(LIBS);

xfthello: $(OBJS) xhello.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(XFTFLAGS) xhello.c -o xfthello  $(LDFLAGS) $(XFTLIBS) $(LIBS);

.PHONY: clean
clean:
rm -f ./*.o; rm -f ./xhello ./xfthello;


Note: you have to uncomment one line if you want to build it on the Mac. Also, it builds two versions. The old X11 style fonts (xhello) and the new Xft anti-alised fonts (xfthello). Have a peek at the results.

Image

What am I going to do with this newly acquired knowledge?....Only time will let! :twisted:

Cheers!
Nice! Works under linux too :) .

Back when I first started my djv project, I had a couple alpha versions just using raw xlib and freetype for fonts. Lot's of fun. For various reasons I ended up going with qt, but more than once have regretted that decision :) .
I just googled my own post to try and remember how I did this. :)
Douglas A Young was an SGI employee who wrote a few books on Xt, Motif, etc programming, his code has always compiled on IRIX.

http://www.amazon.com/Douglas-A.-Young/ ... 187&sr=8-3

I have this book and the source code built on one of my machines here.. somewhere.

There is more example in IRIX itself in insight and those soft copy Oreilly books optional installs in IRIX 6.5... Volume 6B , etc...

You might want to change the file name to xhello.c or change the target and remember to put tabs not spaces before the $(CC) and rm lines...

R.
死の神はりんごだけ食べる

開いた括弧は必ず閉じる -- あるプログラマー

:Tezro: :Tezro: :Onyx2R: :Onyx2RE: :Onyx2: :O3x04R: :O3x0: :O200: :Octane: :Octane2: :O2: :O2: :Indigo2IMP: :PI: :PI: :1600SW: :1600SW: :Indy: :Indy: :Indy: :Indy: :Indy:
:hpserv: J5600, 2 x Mac, 3 x SUN, Alpha DS20E, Alpha 800 5/550, 3 x RS/6000, Amiga 4000 VideoToaster, Amiga4000 -030, 733MHz Sam440 AmigaOS 4.1 update 1.

Sold: :Indy: :Indy: :Indy: :Indigo: Tandem Himalaya S-Series Nonstop S72000 ServerNet.

Twitter @PymbleSoftware
Current Apps (iOS) -> https://itunes.apple.com/au/artist/pymb ... d553990081
(Android) https://play.google.com/store/apps/deve ... +Ltd&hl=en
(Onyx2) Cortex ---> http://www.facebook.com/pages/Cortex-th ... 11?sk=info
(0300s) Minnie ---> http://www.facebook.com/pages/Minnie-th ... 02?sk=info
Github ---> https://github.com/pymblesoftware
squeen wrote: I just googled my own post to try and remember how I did this. :)
Those are the times when I'm glad I try to capture/document things as I go. :D Or, all too often, they are the times I wail and lament that I'm not more consistent in capturing/documenting things as I go...
Then? :IRIS3130: ... Now? :O3x02L: :A3504L: - :A3502L: :1600SW: +MLA :Fuel: :Octane2: :Octane: :Indigo2IMP: ... Other: DEC :BA213: :BA123: Sun , DG AViiON , NeXT :Cube: