summaryrefslogtreecommitdiff
path: root/src/x11/xvertex.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/x11/xvertex.c')
-rw-r--r--src/x11/xvertex.c1440
1 files changed, 1440 insertions, 0 deletions
diff --git a/src/x11/xvertex.c b/src/x11/xvertex.c
new file mode 100644
index 0000000..87a05e9
--- /dev/null
+++ b/src/x11/xvertex.c
@@ -0,0 +1,1440 @@
+/* ********************************************************************** */
+
+/* xvertext 5.0, Copyright (c) 1993 Alan Richardson (mppa3@uk.ac.sussex.syma)
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation for any purpose and without fee is hereby granted, provided
+* that the above copyright notice appear in all copies and that both the
+* copyright notice and this permission notice appear in supporting
+* documentation. All work developed as a consequence of the use of
+* this program should duly acknowledge such use. No representations are
+* made about the suitability of this software for any purpose. It is
+* provided "as is" without express or implied warranty.
+*/
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "xvertex.h"
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* Make sure cache size is set */
+
+#ifndef CACHE_SIZE_LIMIT
+#define CACHE_SIZE_LIMIT 300
+#endif /*CACHE_SIZE_LIMIT */
+
+/* Make sure a cache method is specified */
+
+#ifndef CACHE_XIMAGES
+#ifndef CACHE_BITMAPS
+#define CACHE_BITMAPS
+#endif /*CACHE_BITMAPS*/
+#endif /*CACHE_XIMAGES*/
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* Debugging macros */
+
+#ifdef DEBUG
+static int debug=1;
+#else
+static int debug=0;
+#endif /*DEBUG*/
+
+#define DEBUG_PRINT1(a) if (debug) printf (a)
+#define DEBUG_PRINT2(a, b) if (debug) printf (a, b)
+#define DEBUG_PRINT3(a, b, c) if (debug) printf (a, b, c)
+#define DEBUG_PRINT4(a, b, c, d) if (debug) printf (a, b, c, d)
+#define DEBUG_PRINT5(a, b, c, d, e) if (debug) printf (a, b, c, d, e)
+
+
+/* ---------------------------------------------------------------------- */
+
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* A structure holding everything needed for a rotated string */
+
+typedef struct rotated_text_item_template {
+ Pixmap bitmap;
+ XImage *ximage;
+
+ char *text;
+ char *font_name;
+ Font fid;
+ double angle;
+ int align;
+ double magnify;
+
+ int cols_in;
+ int rows_in;
+ int cols_out;
+ int rows_out;
+
+ int nl;
+ int max_width;
+ double *corners_x;
+ double *corners_y;
+
+ long int size;
+ int cached;
+
+ struct rotated_text_item_template *next;
+} RotatedTextItem;
+
+static RotatedTextItem *first_text_item=NULL;
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* A structure holding current magnification and bounding box padding */
+
+static struct style_template {
+ double magnify;
+ int bbx_pad;
+} style={
+ 1.,
+ 0
+};
+
+
+/* ---------------------------------------------------------------------- */
+
+static RotatedTextItem *XRotCreateTextItem(Display *dpy, XFontStruct *font, double angle, char *text, int align);
+static void XRotAddToLinkedList(Display *dpy, RotatedTextItem *item);
+static XImage *XRotMagnifyImage(Display *dpy, XImage *ximage);
+static void XRotFreeTextItem(Display *dpy, RotatedTextItem *item);
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Routine to mimic `strdup()' (some machines don't have it) */
+/**************************************************************************/
+
+static char *my_strdup(const char *str)
+{
+ char *s;
+
+ if(str==NULL)
+ return NULL;
+
+ s=(char *)malloc((unsigned)(strlen(str)+1));
+ if(s!=NULL)
+ strcpy(s, str);
+
+ return s;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Routine to replace `strtok' : this one returns a zero length string if */
+/* it encounters two consecutive delimiters */
+/**************************************************************************/
+
+static char *my_strtok(char *str1, const char *str2)
+{
+ char *ret;
+ int i, j, stop;
+ static int start, len;
+ static char *stext;
+
+ if(str2==NULL)
+ return NULL;
+
+ /* initialise if str1 not NULL */
+ if(str1!=NULL)
+ {
+ start=0;
+ stext=str1;
+ len=strlen(str1);
+ }
+
+ /* run out of tokens ? */
+ if(start>=len)
+ return NULL;
+
+ /* loop through characters */
+ for(i=start; i<len; i++)
+ {
+ /* loop through delimiters */
+ stop=0;
+ for(j=0; j<strlen(str2); j++)
+ if(stext[i]==str2[j])
+ stop=1;
+
+ if(stop)
+ break;
+ }
+
+ stext[i]='\0';
+
+ ret=stext+start;
+
+ start=i+1;
+
+ return ret;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Create an XImage structure and allocate memory for it */
+/**************************************************************************/
+
+static XImage *MakeXImage(Display *dpy, int w, int h)
+{
+ XImage *I;
+ char *data;
+
+ /* reserve memory for image */
+ data=(char *)calloc((unsigned)(((w-1)/8+1)*h), 1);
+ if(data==NULL)
+ return NULL;
+
+ /* create the XImage */
+ I=XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 1, XYBitmap, 0, data, w, h, 8, 0);
+ if(I==NULL)
+ return NULL;
+
+ I->byte_order=I->bitmap_bit_order=MSBFirst;
+ return I;
+}
+
+/* ---------------------------------------------------------------------- */
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Draw a horizontal string in a quick fashion */
+/**************************************************************************/
+
+static int XRotDrawHorizontalString(Display *dpy, XFontStruct *font, Drawable drawable, GC gc, int x, int y, const char *text, int align, int bg)
+{
+ GC my_gc;
+ int nl=1, i;
+ int height;
+ int xp, yp;
+ char *str1, *str2, *str3;
+ char *str2_a="\0", *str2_b="\n\0";
+ int dir, asc, desc;
+ XCharStruct overall;
+
+ DEBUG_PRINT1("**\nHorizontal text.\n");
+
+ /* this gc has similar properties to the user's gc (including stipple) */
+ my_gc=XCreateGC(dpy, drawable, 0, 0);
+ XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCStipple|GCFillStyle| GCTileStipXOrigin|GCTileStipYOrigin|GCPlaneMask, my_gc);
+ XSetFont(dpy, my_gc, font->fid);
+
+ /* count number of sections in string */
+ if(align!=XR_LEFT)
+ for(i=0; i<strlen(text)-1; i++)
+ if(text[i]=='\n')
+ nl++;
+
+ /* ignore newline characters if not doing alignment */
+ if(align==XR_LEFT)
+ str2=str2_a;
+ else
+ str2=str2_b;
+
+ /* overall font height */
+ height=font->ascent+font->descent;
+
+ /* y position */
+ if(align==XR_TLEFT || align==XR_TCENTRE || align==XR_TRIGHT)
+ yp=y+font->ascent;
+ else if(align==XR_MLEFT || align==XR_MCENTRE || align==XR_MRIGHT)
+ yp=y-nl*height/2+font->ascent;
+ else if(align==XR_BLEFT || align==XR_BCENTRE || align==XR_BRIGHT)
+ yp=y-nl*height+font->ascent;
+ else
+ yp=y;
+
+ str1=my_strdup(text);
+ if(str1==NULL)
+ return 1;
+
+ str3=my_strtok(str1, str2);
+
+ /* loop through each section in the string */
+ do {
+ XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+ &overall);
+
+ /* where to draw section in x ? */
+ if(align==XR_TLEFT || align==XR_MLEFT || align==XR_BLEFT || align==XR_LEFT)
+ xp=x;
+ else if(align==XR_TCENTRE || align==XR_MCENTRE || align==XR_BCENTRE || align==XR_CENTRE)
+ xp=x-overall.rbearing/2;
+ else
+ xp=x-overall.rbearing;
+
+ /* draw string onto bitmap */
+ if(!bg)
+ XDrawString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
+ else
+ XDrawImageString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
+
+ /* move to next line */
+ yp+=height;
+
+ str3=my_strtok((char *)NULL, str2);
+ }
+ while(str3!=NULL);
+
+ free(str1);
+ XFreeGC(dpy, my_gc);
+
+ return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Query cache for a match with this font/text/angle/alignment */
+/* request, otherwise arrange for its creation */
+/**************************************************************************/
+
+static RotatedTextItem *XRotRetrieveFromCache(Display *dpy, XFontStruct *font, double angle, const char *text, int align)
+{
+ Font fid;
+ char *font_name=NULL;
+ unsigned long name_value;
+ RotatedTextItem *item=NULL;
+ RotatedTextItem *i1=first_text_item;
+
+ /* get font name, if it exists */
+ if(XGetFontProperty(font, XA_FONT, &name_value)) {
+ DEBUG_PRINT1("got font name OK\n");
+ font_name=XGetAtomName(dpy, name_value);
+ fid=0;
+ }
+#ifdef CACHE_FID
+ /* otherwise rely (unreliably?) on font ID */
+ else {
+ DEBUG_PRINT1("can't get fontname, caching FID\n");
+ font_name=NULL;
+ fid=font->fid;
+ }
+#else
+ /* not allowed to cache font ID's */
+ else {
+ DEBUG_PRINT1("can't get fontname, can't cache\n");
+ font_name=NULL;
+ fid=0;
+ }
+#endif /*CACHE_FID*/
+
+ /* look for a match in cache */
+
+ /* matching formula:
+ identical text;
+ identical fontname (if defined, font ID's if not);
+ angles close enough (<0.00001 here, could be smaller);
+ HORIZONTAL alignment matches, OR it's a one line string;
+ magnifications the same */
+
+ while(i1 && !item)
+ {
+ /* match everything EXCEPT fontname/ID */
+ if(strcmp(text, i1->text)==0 &&
+ fabs(angle-i1->angle)<0.00001 &&
+ style.magnify==i1->magnify &&
+ (i1->nl==1 ||
+ ((align==0)?9:(align-1))%3==
+ ((i1->align==0)?9:(i1->align-1))%3))
+ {
+
+ /* now match fontname/ID */
+ if(font_name!=NULL && i1->font_name!=NULL)
+ {
+ if(strcmp(font_name, i1->font_name)==0)
+ {
+ item=i1;
+ DEBUG_PRINT1("Matched against font names\n");
+ }
+ else
+ i1=i1->next;
+ }
+#ifdef CACHE_FID
+ else if(font_name==NULL && i1->font_name==NULL)
+ {
+ if(fid==i1->fid)
+ {
+ item=i1;
+ DEBUG_PRINT1("Matched against FID's\n");
+ }
+ else
+ i1=i1->next;
+ }
+#endif /*CACHE_FID*/
+ else
+ i1=i1->next;
+ }
+ else
+ i1=i1->next;
+ }
+
+ if(item)
+ DEBUG_PRINT1("**\nFound target in cache.\n");
+ if(!item)
+ DEBUG_PRINT1("**\nNo match in cache.\n");
+
+ /* no match */
+ if(!item)
+ {
+ /* create new item */
+ item=XRotCreateTextItem(dpy, font, angle, text, align);
+ if(!item)
+ return NULL;
+
+ /* record what it shows */
+ item->text=my_strdup(text);
+
+ /* fontname or ID */
+ if(font_name!=NULL)
+ {
+ item->font_name=my_strdup(font_name);
+ item->fid=0;
+ }
+ else
+ {
+ item->font_name=NULL;
+ item->fid=fid;
+ }
+
+ item->angle=angle;
+ item->align=align;
+ item->magnify=style.magnify;
+
+ /* cache it */
+ XRotAddToLinkedList(dpy, item);
+ }
+
+ if(font_name)
+ XFree(font_name);
+
+ /* if XImage is cached, need to recreate the bitmap */
+
+#ifdef CACHE_XIMAGES
+ {
+ GC depth_one_gc;
+
+ /* create bitmap to hold rotated text */
+ item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+ item->cols_out, item->rows_out, 1);
+
+ /* depth one gc */
+ depth_one_gc=XCreateGC(dpy, item->bitmap, 0, 0);
+ XSetBackground(dpy, depth_one_gc, 0);
+ XSetForeground(dpy, depth_one_gc, 1);
+
+ /* make the text bitmap from XImage */
+ XPutImage(dpy, item->bitmap, depth_one_gc, item->ximage, 0, 0, 0, 0,
+ item->cols_out, item->rows_out);
+
+ XFreeGC(dpy, depth_one_gc);
+ }
+#endif /*CACHE_XIMAGES*/
+
+ return item;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Create a rotated text item */
+/**************************************************************************/
+
+static RotatedTextItem *XRotCreateTextItem(Display *dpy, XFontStruct *font, double angle, char *text, int align)
+{
+ RotatedTextItem *item=NULL;
+ Pixmap canvas;
+ GC font_gc;
+ XImage *I_in;
+ register int i, j;
+ char *str1, *str2, *str3;
+ char *str2_a="\0", *str2_b="\n\0";
+ int height;
+ int byte_w_in, byte_w_out;
+ int xp, yp;
+ double sin_angle, cos_angle;
+ int it, jt;
+ double di, dj;
+ int ic=0;
+ double xl, xr, xinc;
+ int byte_out;
+ int dir, asc, desc;
+ XCharStruct overall;
+ int old_cols_in=0, old_rows_in=0;
+
+ /* allocate memory */
+ item=(RotatedTextItem *)malloc((unsigned)sizeof(RotatedTextItem));
+ if(!item)
+ return NULL;
+
+ /* count number of sections in string */
+ item->nl=1;
+ if(align!=XR_LEFT)
+ for(i=0; i<strlen(text)-1; i++)
+ if(text[i]=='\n')
+ item->nl++;
+
+ /* ignore newline characters if not doing alignment */
+ if(align==XR_LEFT)
+ str2=str2_a;
+ else
+ str2=str2_b;
+
+ /* find width of longest section */
+ str1=my_strdup(text);
+ if(str1==NULL)
+ return NULL;
+
+ str3=my_strtok(str1, str2);
+
+ XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+ &overall);
+
+ item->max_width=overall.rbearing;
+
+ /* loop through each section */
+ do
+ {
+ str3=my_strtok((char *)NULL, str2);
+
+ if(str3!=NULL)
+ {
+ XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+ &overall);
+
+ if(overall.rbearing>item->max_width)
+ item->max_width=overall.rbearing;
+ }
+ } while(str3!=NULL);
+
+ free(str1);
+
+ /* overall font height */
+ height=font->ascent+font->descent;
+
+ /* dimensions horizontal text will have */
+ item->cols_in=item->max_width;
+ item->rows_in=item->nl*height;
+
+ /* bitmap for drawing on */
+ canvas=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+ item->cols_in, item->rows_in, 1);
+
+ /* create a GC for the bitmap */
+ font_gc=XCreateGC(dpy, canvas, 0, 0);
+ XSetBackground(dpy, font_gc, 0);
+ XSetFont(dpy, font_gc, font->fid);
+
+ /* make sure the bitmap is blank */
+ XSetForeground(dpy, font_gc, 0);
+ XFillRectangle(dpy, canvas, font_gc, 0, 0,
+ item->cols_in+1, item->rows_in+1);
+ XSetForeground(dpy, font_gc, 1);
+
+ /* pre-calculate sin and cos */
+ sin_angle=sin(angle);
+ cos_angle=cos(angle);
+
+ if (fabs(sin_angle)==1.0) cos_angle=0;
+ if (fabs(cos_angle)==1.0) sin_angle=0;
+
+ /* text background will be drawn using XFillPolygon */
+ item->corners_x=
+ (double *)malloc((unsigned)(4*item->nl*sizeof(double)));
+ if(!item->corners_x)
+ return NULL;
+
+ item->corners_y=
+ (double *)malloc((unsigned)(4*item->nl*sizeof(double)));
+ if(!item->corners_y)
+ return NULL;
+
+ /* draw text horizontally */
+
+ /* start at top of bitmap */
+ yp=font->ascent;
+
+ str1=my_strdup(text);
+ if(str1==NULL)
+ return NULL;
+
+ str3=my_strtok(str1, str2);
+
+ /* loop through each section in the string */
+ do
+ {
+ XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc, &overall);
+
+ /* where to draw section in x ? */
+ if(align==XR_TLEFT || align==XR_MLEFT || align==XR_BLEFT || align==XR_LEFT)
+ xp=0;
+ else if(align==XR_TCENTRE || align==XR_MCENTRE || align==XR_BCENTRE || align==XR_CENTRE)
+ xp=(item->max_width-overall.rbearing)/2;
+ else
+ xp=item->max_width-overall.rbearing;
+
+ /* draw string onto bitmap */
+ XDrawString(dpy, canvas, font_gc, xp, yp, str3, strlen(str3));
+
+ /* keep a note of corner positions of this string */
+ item->corners_x[ic]=((double)xp-(double)item->cols_in/2)*style.magnify;
+ item->corners_y[ic]=((double)(yp-font->ascent)-(double)item->rows_in/2)
+ *style.magnify;
+ item->corners_x[ic+1]=item->corners_x[ic];
+ item->corners_y[ic+1]=item->corners_y[ic]+(double)height*style.magnify;
+ item->corners_x[item->nl*4-1-ic]=item->corners_x[ic]+
+ (double)overall.rbearing*style.magnify;
+ item->corners_y[item->nl*4-1-ic]=item->corners_y[ic];
+ item->corners_x[item->nl*4-2-ic]=
+ item->corners_x[item->nl*4-1-ic];
+ item->corners_y[item->nl*4-2-ic]=item->corners_y[ic+1];
+
+ ic+=2;
+
+ /* move to next line */
+ yp+=height;
+
+ str3=my_strtok((char *)NULL, str2);
+ } while(str3!=NULL);
+
+ free(str1);
+
+ /* create image to hold horizontal text */
+ I_in=MakeXImage(dpy, item->cols_in, item->rows_in);
+ if(I_in==NULL)
+ return NULL;
+
+ /* extract horizontal text */
+ XGetSubImage(dpy, canvas, 0, 0, item->cols_in, item->rows_in,
+ 1, XYPixmap, I_in, 0, 0);
+ I_in->format=XYBitmap;
+
+ /* magnify horizontal text */
+ if(style.magnify!=1.)
+ {
+ I_in=XRotMagnifyImage(dpy, I_in);
+
+ old_cols_in=item->cols_in;
+ old_rows_in=item->rows_in;
+ item->cols_in=(double)item->cols_in*style.magnify;
+ item->rows_in=(double)item->rows_in*style.magnify;
+ }
+
+ /* how big will rotated text be ? */
+ item->cols_out=fabs((double)item->rows_in*sin_angle) +
+ fabs((double)item->cols_in*cos_angle) +0.99999 +2;
+
+ item->rows_out=fabs((double)item->rows_in*cos_angle) +
+ fabs((double)item->cols_in*sin_angle) +0.99999 +2;
+
+ if(item->cols_out%2==0)
+ item->cols_out++;
+
+ if(item->rows_out%2==0)
+ item->rows_out++;
+
+ /* create image to hold rotated text */
+ item->ximage=MakeXImage(dpy, item->cols_out, item->rows_out);
+ if(item->ximage==NULL)
+ return NULL;
+
+ byte_w_in=(item->cols_in-1)/8+1;
+ byte_w_out=(item->cols_out-1)/8+1;
+
+ /* we try to make this bit as fast as possible - which is why it looks
+ a bit over-the-top */
+
+ /* vertical distance from centre */
+ dj=0.5-(double)item->rows_out/2;
+
+ /* where abouts does text actually lie in rotated image? */
+ if(angle==0 || angle==M_PI/2 || angle==M_PI || angle==3*M_PI/2)
+ {
+ xl=0;
+ xr=(double)item->cols_out;
+ xinc=0;
+ }
+ else if(angle<M_PI)
+ {
+ xl=(double)item->cols_out/2+
+ (dj-(double)item->rows_in/(2*cos_angle))/
+ tan(angle)-2;
+
+ xr=(double)item->cols_out/2+
+ (dj+(double)item->rows_in/(2*cos_angle))/
+ tan(angle)+2;
+
+ xinc=1./tan(angle);
+ }
+ else
+ {
+ xl=(double)item->cols_out/2+
+ (dj+(double)item->rows_in/(2*cos_angle))/
+ tan(angle)-2;
+
+ xr=(double)item->cols_out/2+
+ (dj-(double)item->rows_in/(2*cos_angle))/
+ tan(angle)+2;
+
+ xinc=1./tan(angle);
+ }
+
+ /* loop through all relevent bits in rotated image */
+ for(j=0; j<item->rows_out; j++)
+ {
+ /* no point re-calculating these every pass */
+ di=(double)((xl<0)?0:(int)xl)+0.5-(double)item->cols_out/2;
+ byte_out=(item->rows_out-j-1)*byte_w_out;
+
+ /* loop through meaningful columns */
+ for(i=((xl<0)?0:(int)xl);i<((xr>=item->cols_out)?item->cols_out:(int)xr); i++)
+ {
+ /* rotate coordinates */
+ it=(double)item->cols_in/2 + ( di*cos_angle + dj*sin_angle);
+ jt=(double)item->rows_in/2 - (-di*sin_angle + dj*cos_angle);
+
+ /* set pixel if required */
+ if(it>=0 && it<item->cols_in && jt>=0 && jt<item->rows_in)
+ if((I_in->data[jt*byte_w_in+it/8] & 128>>(it%8))>0)
+ item->ximage->data[byte_out+i/8]|=128>>i%8;
+
+ di+=1;
+ }
+
+ dj+=1;
+ xl+=xinc;
+ xr+=xinc;
+ }
+
+ XDestroyImage(I_in);
+
+ if(style.magnify!=1.)
+ {
+ item->cols_in=old_cols_in;
+ item->rows_in=old_rows_in;
+ }
+
+
+#ifdef CACHE_BITMAPS
+
+ /* create a bitmap to hold rotated text */
+ item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+ item->cols_out, item->rows_out, 1);
+
+ /* make the text bitmap from XImage */
+ XPutImage(dpy, item->bitmap, font_gc, item->ximage, 0, 0, 0, 0,
+ item->cols_out, item->rows_out);
+
+ XDestroyImage(item->ximage);
+
+#endif /*CACHE_BITMAPS*/
+
+ XFreeGC(dpy, font_gc);
+ XFreePixmap(dpy, canvas);
+
+ return item;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Adds a text item to the end of the cache, removing as many items */
+/* from the front as required to keep cache size below limit */
+/**************************************************************************/
+
+static void XRotAddToLinkedList(Display *dpy, RotatedTextItem *item)
+{
+
+ static long int current_size=0;
+ static RotatedTextItem *last=NULL;
+ RotatedTextItem *i1=first_text_item, *i2=NULL;
+
+#ifdef CACHE_BITMAPS
+
+ /* I don't know how much memory a pixmap takes in the server -
+ probably this + a bit more we can't account for */
+
+ item->size=((item->cols_out-1)/8+1)*item->rows_out;
+
+#else
+
+ /* this is pretty much the size of a RotatedTextItem */
+
+ item->size=((item->cols_out-1)/8+1)*item->rows_out +
+ sizeof(XImage) + strlen(item->text) +
+ item->nl*8*sizeof(double) + sizeof(RotatedTextItem);
+
+ if(item->font_name!=NULL)
+ item->size+=strlen(item->font_name);
+ else
+ item->size+=sizeof(Font);
+
+#endif /*CACHE_BITMAPS */
+
+#ifdef DEBUG
+ /* count number of items in cache, for debugging */
+ {
+ int i=0;
+
+ while(i1) {
+ i++;
+ i1=i1->next;
+ }
+ DEBUG_PRINT2("Cache has %d items.\n", i);
+ i1=first_text_item;
+ }
+#endif
+
+ DEBUG_PRINT4("current cache size=%ld, new item=%ld, limit=%d\n",
+ current_size, item->size, CACHE_SIZE_LIMIT*1024);
+
+ /* if this item is bigger than whole cache, forget it */
+ if(item->size>CACHE_SIZE_LIMIT*1024) {
+ DEBUG_PRINT1("Too big to cache\n\n");
+ item->cached=0;
+ return;
+ }
+
+ /* remove elements from cache as needed */
+ while(i1 && current_size+item->size>CACHE_SIZE_LIMIT*1024) {
+
+ DEBUG_PRINT2("Removed %ld bytes\n", i1->size);
+
+ if(i1->font_name!=NULL)
+ DEBUG_PRINT5(" (`%s'\n %s\n angle=%f align=%d)\n",
+ i1->text, i1->font_name, i1->angle, i1->align);
+
+#ifdef CACHE_FID
+ if(i1->font_name==NULL)
+ DEBUG_PRINT5(" (`%s'\n FID=%ld\n angle=%f align=%d)\n",
+ i1->text, i1->fid, i1->angle, i1->align);
+#endif /*CACHE_FID*/
+
+ current_size-=i1->size;
+
+ i2=i1->next;
+
+ /* free resources used by the unlucky item */
+ XRotFreeTextItem(dpy, i1);
+
+ /* remove it from linked list */
+ first_text_item=i2;
+ i1=i2;
+ }
+
+ /* add new item to end of linked list */
+ if(first_text_item==NULL) {
+ item->next=NULL;
+ first_text_item=item;
+ last=item;
+ }
+ else {
+ item->next=NULL;
+ last->next=item;
+ last=item;
+ }
+
+ /* new cache size */
+ current_size+=item->size;
+
+ item->cached=1;
+
+ DEBUG_PRINT1("Added item to cache.\n");
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Free the resources used by a text item */
+/**************************************************************************/
+
+static void XRotFreeTextItem(Display *dpy, RotatedTextItem *item)
+{
+ free(item->text);
+
+ if(item->font_name!=NULL)
+ free(item->font_name);
+
+ free((char *)item->corners_x);
+ free((char *)item->corners_y);
+
+#ifdef CACHE_BITMAPS
+ XFreePixmap(dpy, item->bitmap);
+#else
+ XDestroyImage(item->ximage);
+#endif /* CACHE_BITMAPS */
+
+ free((char *)item);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Magnify an XImage using bilinear interpolation */
+/**************************************************************************/
+
+static XImage *XRotMagnifyImage(Display *dpy, XImage *ximage)
+{
+ int i, j;
+ double x, y;
+ double u,t;
+ XImage *I_out;
+ int cols_in, rows_in;
+ int cols_out, rows_out;
+ register int i2, j2;
+ double z1, z2, z3, z4;
+ int byte_width_in, byte_width_out;
+ double mag_inv;
+
+ /* size of input image */
+ cols_in=ximage->width;
+ rows_in=ximage->height;
+
+ /* size of final image */
+ cols_out=(double)cols_in*style.magnify;
+ rows_out=(double)rows_in*style.magnify;
+
+ /* this will hold final image */
+ I_out=MakeXImage(dpy, cols_out, rows_out);
+ if(I_out==NULL)
+ return NULL;
+
+ /* width in bytes of input, output images */
+ byte_width_in=(cols_in-1)/8+1;
+ byte_width_out=(cols_out-1)/8+1;
+
+ /* for speed */
+ mag_inv=1./style.magnify;
+
+ y=0.;
+
+ /* loop over magnified image */
+ for(j2=0; j2<rows_out; j2++)
+ {
+ x=0;
+ j=y;
+
+ for(i2=0; i2<cols_out; i2++)
+ {
+ i=x;
+
+ /* bilinear interpolation - where are we on bitmap ? */
+ /* right edge */
+ if(i==cols_in-1 && j!=rows_in-1)
+ {
+ t=0;
+ u=y-(double)j;
+
+ z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+ z2=z1;
+ z3=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
+ z4=z3;
+ }
+ /* top edge */
+ else if(i!=cols_in-1 && j==rows_in-1)
+ {
+ t=x-(double)i;
+ u=0;
+
+ z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+ z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
+ z3=z2;
+ z4=z1;
+ }
+ /* top right corner */
+ else if(i==cols_in-1 && j==rows_in-1)
+ {
+ u=0;
+ t=0;
+
+ z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+ z2=z1;
+ z3=z1;
+ z4=z1;
+ }
+ /* somewhere `safe' */
+ else
+ {
+ t=x-(double)i;
+ u=y-(double)j;
+
+ z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+ z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
+ z3=(ximage->data[(j+1)*byte_width_in+(i+1)/8] &
+ 128>>((i+1)%8))>0;
+ z4=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
+ }
+
+ /* if interpolated value is greater than 0.5, set bit */
+ if(((1-t)*(1-u)*z1 + t*(1-u)*z2 + t*u*z3 + (1-t)*u*z4)>0.5)
+ I_out->data[j2*byte_width_out+i2/8]|=128>>i2%8;
+
+ x+=mag_inv;
+ }
+ y+=mag_inv;
+ }
+
+ /* destroy original */
+ XDestroyImage(ximage);
+
+ /* return big image */
+ return I_out;
+}
+
+
+
+
+/**************************************************************************/
+/* Return version/copyright information */
+/**************************************************************************/
+
+double XRotVersion(char* str, int n)
+{
+ if(str!=NULL)
+ strncpy(str, XV_COPYRIGHT, n);
+ return XV_VERSION;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Set the font magnification factor for all subsequent operations */
+/**************************************************************************/
+
+void XRotSetMagnification(double m)
+{
+ if(m>0.)
+ style.magnify=m;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Set the padding used when calculating bounding boxes */
+/**************************************************************************/
+
+void XRotSetBoundingBoxPad(int p)
+{
+ if(p>=0)
+ style.bbx_pad=p;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Calculate the bounding box some text will have when painted */
+/**************************************************************************/
+
+XPoint *XRotTextExtents(Display* dpy, XFontStruct* font, double angle, int x, int y, const char* text, int align)
+{
+ register int i;
+ char *str1, *str2, *str3;
+ char *str2_a="\0", *str2_b="\n\0";
+ int height;
+ double sin_angle, cos_angle;
+ int nl, max_width;
+ int cols_in, rows_in;
+ double hot_x, hot_y;
+ XPoint *xp_in, *xp_out;
+ int dir, asc, desc;
+ XCharStruct overall;
+
+ /* manipulate angle to 0<=angle<360 degrees */
+ while(angle<0)
+ angle+=360;
+
+ while(angle>360)
+ angle-=360;
+
+ angle*=M_PI/180;
+
+ /* count number of sections in string */
+ nl=1;
+ if(align!=XR_LEFT)
+ for(i=0; i<strlen(text)-1; i++)
+ if(text[i]=='\n')
+ nl++;
+
+ /* ignore newline characters if not doing alignment */
+ if(align==XR_LEFT)
+ str2=str2_a;
+ else
+ str2=str2_b;
+
+ /* find width of longest section */
+ str1=my_strdup(text);
+ if(str1==NULL)
+ return NULL;
+
+ str3=my_strtok(str1, str2);
+
+ XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+ &overall);
+
+ max_width=overall.rbearing;
+
+ /* loop through each section */
+ do
+ {
+ str3=my_strtok((char *)NULL, str2);
+
+ if(str3!=NULL)
+ {
+ XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+ &overall);
+
+ if(overall.rbearing>max_width)
+ max_width=overall.rbearing;
+ }
+ } while(str3!=NULL);
+
+ free(str1);
+
+ /* overall font height */
+ height=font->ascent+font->descent;
+
+ /* dimensions horizontal text will have */
+ cols_in=max_width;
+ rows_in=nl*height;
+
+ /* pre-calculate sin and cos */
+ sin_angle=sin(angle);
+ cos_angle=cos(angle);
+
+ /* y position */
+ if(align==XR_TLEFT || align==XR_TCENTRE || align==XR_TRIGHT)
+ hot_y=(double)rows_in/2*style.magnify;
+ else if(align==XR_MLEFT || align==XR_MCENTRE || align==XR_MRIGHT)
+ hot_y=0;
+ else if(align==XR_BLEFT || align==XR_BCENTRE || align==XR_BRIGHT)
+ hot_y=-(double)rows_in/2*style.magnify;
+ else
+ hot_y=-((double)rows_in/2-(double)font->descent)*style.magnify;
+
+ /* x position */
+ if(align==XR_TLEFT || align==XR_MLEFT || align==XR_BLEFT || align==XR_LEFT)
+ hot_x=-(double)max_width/2*style.magnify;
+ else if(align==XR_TCENTRE || align==XR_MCENTRE || align==XR_BCENTRE || align==XR_CENTRE)
+ hot_x=0;
+ else
+ hot_x=(double)max_width/2*style.magnify;
+
+ /* reserve space for XPoints */
+ xp_in=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
+ if(!xp_in)
+ return NULL;
+
+ xp_out=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
+ if(!xp_out)
+ return NULL;
+
+ /* bounding box when horizontal, relative to bitmap centre */
+ xp_in[0].x=-(double)cols_in*style.magnify/2-style.bbx_pad;
+ xp_in[0].y= (double)rows_in*style.magnify/2+style.bbx_pad;
+ xp_in[1].x= (double)cols_in*style.magnify/2+style.bbx_pad;
+ xp_in[1].y= (double)rows_in*style.magnify/2+style.bbx_pad;
+ xp_in[2].x= (double)cols_in*style.magnify/2+style.bbx_pad;
+ xp_in[2].y=-(double)rows_in*style.magnify/2-style.bbx_pad;
+ xp_in[3].x=-(double)cols_in*style.magnify/2-style.bbx_pad;
+ xp_in[3].y=-(double)rows_in*style.magnify/2-style.bbx_pad;
+ xp_in[4].x=xp_in[0].x;
+ xp_in[4].y=xp_in[0].y;
+
+ /* rotate and translate bounding box */
+ for(i=0; i<5; i++)
+ {
+ xp_out[i].x=(double)x + ( ((double)xp_in[i].x-hot_x)*cos_angle +
+ ((double)xp_in[i].y+hot_y)*sin_angle);
+
+ xp_out[i].y=(double)y + (-((double)xp_in[i].x-hot_x)*sin_angle +
+ ((double)xp_in[i].y+hot_y)*cos_angle);
+ }
+
+ free((char *)xp_in);
+
+ return xp_out;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/**************************************************************************/
+/* Aligns and paints a rotated string */
+/**************************************************************************/
+
+int XRotDrawString(Display* dpy, XFontStruct* font, double angle, Drawable drawable, GC gc, int x, int y, const char* text, int align, int bg)
+{
+ int i;
+ GC my_gc;
+ int xp, yp;
+ double hot_x, hot_y;
+ double hot_xp, hot_yp;
+ double sin_angle, cos_angle;
+ RotatedTextItem *item;
+ Pixmap bitmap_to_paint;
+
+ /* return early for NULL/empty strings */
+ if(text==NULL)
+ return 0;
+
+ if(strlen(text)==0)
+ return 0;
+
+ /* manipulate angle to 0<=angle<360 degrees */
+ while(angle<0)
+ angle+=360;
+
+ while(angle>=360)
+ angle-=360;
+
+ angle*=M_PI/180;
+
+ /* horizontal text made easy */
+ if(angle==0. && style.magnify==1.)
+ return(XRotDrawHorizontalString(dpy, font, drawable, gc, x, y, text, align, bg));
+
+ /* get a rotated bitmap */
+ item=XRotRetrieveFromCache(dpy, font, angle, text, align);
+ if(item==NULL)
+ return 0;
+
+ /* this gc has similar properties to the user's gc */
+ my_gc=XCreateGC(dpy, drawable, 0, 0);
+ XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCPlaneMask, my_gc);
+
+ /* alignment : which point (hot_x, hot_y) relative to bitmap centre
+ coincides with user's specified point? */
+
+ /* y position */
+ if(align==XR_TLEFT || align==XR_TCENTRE || align==XR_TRIGHT)
+ hot_y=(double)item->rows_in/2*style.magnify;
+ else if(align==XR_MLEFT || align==XR_MCENTRE || align==XR_MRIGHT)
+ hot_y=0;
+ else if(align==XR_BLEFT || align==XR_BCENTRE || align==XR_BRIGHT)
+ hot_y=-(double)item->rows_in/2*style.magnify;
+ else
+ hot_y=-((double)item->rows_in/2-(double)font->descent)*style.magnify;
+
+ /* x position */
+ if(align==XR_TLEFT || align==XR_MLEFT || align==XR_BLEFT || align==XR_LEFT)
+ hot_x=-(double)item->max_width/2*style.magnify;
+ else if(align==XR_TCENTRE || align==XR_MCENTRE || align==XR_BCENTRE || align==XR_CENTRE)
+ hot_x=0;
+ else
+ hot_x=(double)item->max_width/2*style.magnify;
+
+ /* pre-calculate sin and cos */
+ sin_angle=sin(angle);
+ cos_angle=cos(angle);
+
+ /* rotate hot_x and hot_y around bitmap centre */
+ hot_xp= hot_x*cos_angle - hot_y*sin_angle;
+ hot_yp= hot_x*sin_angle + hot_y*cos_angle;
+
+ /* text background will be drawn using XFillPolygon */
+ if(bg)
+ {
+ GC depth_one_gc;
+ XPoint *xpoints;
+ Pixmap empty_stipple;
+
+ /* reserve space for XPoints */
+ xpoints=(XPoint *)malloc((unsigned)(4*item->nl*sizeof(XPoint)));
+ if(!xpoints)
+ return 1;
+
+ /* rotate corner positions */
+ for(i=0; i<4*item->nl; i++)
+ {
+ xpoints[i].x=(double)x + ( (item->corners_x[i]-hot_x)*cos_angle +
+ (item->corners_y[i]+hot_y)*sin_angle);
+
+ xpoints[i].y=(double)y + (-(item->corners_x[i]-hot_x)*sin_angle +
+ (item->corners_y[i]+hot_y)*cos_angle);
+ }
+
+ /* we want to swap foreground and background colors here;
+ XGetGCValues() is only available in R4+ */
+
+ empty_stipple=XCreatePixmap(dpy, drawable, 1, 1, 1);
+
+ depth_one_gc=XCreateGC(dpy, empty_stipple, 0, 0);
+ XSetForeground(dpy, depth_one_gc, 0);
+ XFillRectangle(dpy, empty_stipple, depth_one_gc, 0, 0, 2, 2);
+
+ XSetStipple(dpy, my_gc, empty_stipple);
+ XSetFillStyle(dpy, my_gc, FillOpaqueStippled);
+
+ XFillPolygon(dpy, drawable, my_gc, xpoints, 4*item->nl, Nonconvex,
+ CoordModeOrigin);
+
+ /* free our resources */
+ free((char *)xpoints);
+ XFreeGC(dpy, depth_one_gc);
+ XFreePixmap(dpy, empty_stipple);
+ }
+
+ /* where should top left corner of bitmap go ? */
+ xp=(double)x-((double)item->cols_out/2 +hot_xp);
+ yp=(double)y-((double)item->rows_out/2 -hot_yp);
+
+ /* by default we draw the rotated bitmap, solid */
+ bitmap_to_paint=item->bitmap;
+
+ /* handle user stippling */
+#ifndef X11R3
+ {
+ GC depth_one_gc;
+ XGCValues values;
+ Pixmap new_bitmap, inverse;
+
+ /* try and get some GC properties */
+ if(XGetGCValues(dpy, gc,
+ GCStipple|GCFillStyle|GCForeground|GCBackground|
+ GCTileStipXOrigin|GCTileStipYOrigin,
+ &values))
+ {
+ /* only do this if stippling requested */
+ if((values.fill_style==FillStippled ||
+ values.fill_style==FillOpaqueStippled) && !bg)
+ {
+ /* opaque stipple: draw rotated text in background colour */
+ if(values.fill_style==FillOpaqueStippled)
+ {
+ XSetForeground(dpy, my_gc, values.background);
+ XSetFillStyle(dpy, my_gc, FillStippled);
+ XSetStipple(dpy, my_gc, item->bitmap);
+ XSetTSOrigin(dpy, my_gc, xp, yp);
+ XFillRectangle(dpy, drawable, my_gc, xp, yp,
+ item->cols_out, item->rows_out);
+ XSetForeground(dpy, my_gc, values.foreground);
+ }
+
+ /* this will merge the rotated text and the user's stipple */
+ new_bitmap=XCreatePixmap(dpy, drawable,
+ item->cols_out, item->rows_out, 1);
+
+ /* create a GC */
+ depth_one_gc=XCreateGC(dpy, new_bitmap, 0, 0);
+ XSetForeground(dpy, depth_one_gc, 1);
+ XSetBackground(dpy, depth_one_gc, 0);
+
+ /* set the relative stipple origin */
+ XSetTSOrigin(dpy, depth_one_gc,
+ values.ts_x_origin-xp, values.ts_y_origin-yp);
+
+ /* fill the whole bitmap with the user's stipple */
+ XSetStipple(dpy, depth_one_gc, values.stipple);
+ XSetFillStyle(dpy, depth_one_gc, FillOpaqueStippled);
+ XFillRectangle(dpy, new_bitmap, depth_one_gc,
+ 0, 0, item->cols_out, item->rows_out);
+
+ /* set stipple origin back to normal */
+ XSetTSOrigin(dpy, depth_one_gc, 0, 0);
+
+ /* this will contain an inverse copy of the rotated text */
+ inverse=XCreatePixmap(dpy, drawable,
+ item->cols_out, item->rows_out, 1);
+
+ /* invert text */
+ XSetFillStyle(dpy, depth_one_gc, FillSolid);
+ XSetFunction(dpy, depth_one_gc, GXcopyInverted);
+ XCopyArea(dpy, item->bitmap, inverse, depth_one_gc,
+ 0, 0, item->cols_out, item->rows_out, 0, 0);
+
+ /* now delete user's stipple everywhere EXCEPT on text */
+ XSetForeground(dpy, depth_one_gc, 0);
+ XSetBackground(dpy, depth_one_gc, 1);
+ XSetStipple(dpy, depth_one_gc, inverse);
+ XSetFillStyle(dpy, depth_one_gc, FillStippled);
+ XSetFunction(dpy, depth_one_gc, GXcopy);
+ XFillRectangle(dpy, new_bitmap, depth_one_gc,
+ 0, 0, item->cols_out, item->rows_out);
+
+ /* free resources */
+ XFreePixmap(dpy, inverse);
+ XFreeGC(dpy, depth_one_gc);
+
+ /* this is the new bitmap */
+ bitmap_to_paint=new_bitmap;
+ }
+ }
+ }
+#endif /*X11R3*/
+
+ /* paint text using stipple technique */
+ XSetFillStyle(dpy, my_gc, FillStippled);
+ XSetStipple(dpy, my_gc, bitmap_to_paint);
+ XSetTSOrigin(dpy, my_gc, xp, yp);
+ XFillRectangle(dpy, drawable, my_gc, xp, yp,
+ item->cols_out, item->rows_out);
+
+ /* free our resources */
+ XFreeGC(dpy, my_gc);
+
+ /* stippled bitmap no longer needed */
+ if(bitmap_to_paint!=item->bitmap)
+ XFreePixmap(dpy, bitmap_to_paint);
+
+#ifdef CACHE_XIMAGES
+ XFreePixmap(dpy, item->bitmap);
+#endif /*CACHE_XIMAGES*/
+
+ /* if item isn't cached, destroy it completely */
+ if(!item->cached)
+ XRotFreeTextItem(dpy,item);
+
+ /* we got to the end OK! */
+ return 0;
+}
+
+