1
/* This file is part of the GNU plotutils package. Copyright (C) 1995,
2
1996, 1997, 1998, 1999, 2000, 2005, Free Software Foundation, Inc.
4
The GNU plotutils package is free software. You may redistribute it
5
and/or modify it under the terms of the GNU General Public License as
6
published by the Free Software foundation; either version 2, or (at your
7
option) any later version.
9
The GNU plotutils package is distributed in the hope that it will be
10
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
General Public License for more details.
14
You should have received a copy of the GNU General Public License along
15
with the GNU plotutils package; see the file COPYING. If not, write to
16
the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17
Boston, MA 02110-1301, USA. */
19
/* This is the XAffText module, which was originally independent of
20
libplot. The header file that accompanies it is x_afftext.h.
22
To use the module independently of libplot, simply do not specify
23
"-DLIBPLOT" at compile time.
25
The module supplies two external functions, which are generalizations of
26
the core X11 function XDrawString: XAffDrawRotString and
27
XAffDrawAffString. They draw, respectively, a rotated text string and
28
(more generally) a matrix-transformed text string. In both cases a
29
specified core X font is used. The rotation angle and transformation
30
matrix are specified by the user. The matrix is passed as a 4-element
31
array, with the element ordering convention and sign convention being
32
those of the Matrix Extension to the XLFD (X Logical Font Description).
34
`XAffText' is an abbreviation of `X11 affinely transformed text'. The
35
module was inspired by Alan Richardson's xvertext module for displaying
36
rotated text strings in X11, using the core X fonts. It works in a
37
similar way. (It retrieves a bitmap from the X server into an XImage,
38
transforms the XImage, monochrome pixel by pixel, and sends it back to a
39
bitmap on the server, for use as a stipple.) But it supports arbitrary
40
transformation matrices, and pays extra attention to pixel-level
41
accuracy. It uses integer arithmetic when possible. */
43
#include "x_afftext.h"
47
#else /* not __cplusplus */
49
#endif /* not __cplusplus */
53
#include "sys-defines.h" /* plotutils-specific */
54
#include "extern.h" /* libplot-specific */
55
#else /* not LIBPLOT */
61
#define M_PI 3.14159265358979323846264
63
#define IMAX(a,b) ((a) > (b) ? (a) : (b))
64
#define IMIN(a,b) ((a) < (b) ? (a) : (b))
65
#define IROUND(x) ((int) ((x) > 0 ? (x) + 0.5 : (x) - 0.5))
66
#define _pl_xmalloc malloc
67
#define _pl_xcalloc calloc
68
#endif /* not LIBPLOT */
70
#define XAFF_XPROD(v,a) ((v).x * (a)[0] + (v).y * (a)[2])
71
#define XAFF_YPROD(v,a) ((v).x * (a)[1] + (v).y * (a)[3])
73
typedef struct XAffVectorStruct
78
typedef struct XAffRealVectorStruct
83
typedef struct XAffSizeStruct
88
typedef struct XAffAffinedTextStruct
90
Pixmap bitmap; /* depth-1 Pixmap, i.e., bitmap */
91
XAffSize size; /* bitmap size */
92
XAffVector origin; /* location of text origin within bitmap */
95
/* forward decls of internal ctors/dtors */
97
static XImage * XAffCreateXImage (Display *dpy, XAffSize size);
98
static void XAffFreeXImage (XImage *im);
100
static XAffAffinedText *XAffCreateAffinedText (Display *dpy, XFontStruct *font, double a[4], const char *text);
101
static void XAffFreeAffinedText (Display *dpy, XAffAffinedText *afftext);
103
/* other internal functions */
105
static int can_use_XDrawString (XFontStruct *font, double a[4], const char *text);
107
static void print_image (const XImage *im_in, XAffSize size);
110
/**************************************************************************/
111
/* Create/destroy a depth-1 XImage object */
112
/**************************************************************************/
115
XAffCreateXImage (Display *dpy, XAffSize size)
120
if (size.x == 0 || size.y == 0)
123
data = (char *)_pl_xcalloc(size.y * ((size.x + 7) / 8), 1);
127
im = XCreateImage (dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
128
(unsigned int)1, /* depth = 1 */
133
8, /* pad: quantum of each scanline */
134
0); /* scanlines contigous in memory */
138
im->bitmap_bit_order = MSBFirst;
139
im->byte_order = MSBFirst;
145
XAffFreeXImage (XImage *im)
151
/**************************************************************************/
152
/* Create/destroy an affinely transformed text string */
153
/**************************************************************************/
155
static XAffAffinedText *
156
XAffCreateAffinedText (Display *dpy, XFontStruct *font, double a[4], const char *text)
158
XAffAffinedText *afftext = NULL;
161
int direction, font_ascent, font_descent;
162
XAffSize size_in, size_out;
163
XAffRealVector corner_in[4];
164
XAffVector origin_in, origin_out;
165
XAffVector mincorner, maxcorner;
166
Pixmap bitmap_in, bitmap_out;
167
XImage *im_in, *im_out;
168
int scanline_len_in, scanline_len_out;
169
double aa[4], a_inverse[4], det;
172
/* allocate memory for new instance */
173
afftext = (XAffAffinedText *)_pl_xmalloc(sizeof(XAffAffinedText));
177
/* as passed, a[] is in the format used in the matrix LFD enhancement,
178
which assumes a right-handed coordinate system; so convert it to X11's
179
left-hand coordinate system (y grows downward) */
185
/* invert transformation matrix */
186
det = aa[0] * aa[3] - aa[1] * aa[2];
188
return NULL; /* don't support singular matrices */
189
a_inverse[0] = aa[3] / det;
190
a_inverse[1] = - aa[1] / det;
191
a_inverse[2] = - aa[2] / det;
192
a_inverse[3] = aa[0] / det;
194
/* to include all pixels in text, how large should bitmap be? */
195
XTextExtents (font, text, strlen (text),
196
&direction, &font_ascent, &font_descent, &bounds);
198
/* bitmap size, number-of-pixels by number-of-pixels */
199
size_in.x = - bounds.lbearing + bounds.rbearing;
200
size_in.y = bounds.ascent + bounds.descent;
202
/* within this bitmap, where is `origin' of text string? */
203
origin_in.x = - bounds.lbearing;
204
origin_in.y = bounds.ascent;
207
if (size_in.x == 0 || size_in.y == 0)
210
/* work around a possible bug: some X displays can't create pixmaps that
211
are only one pixel wide or high */
218
fprintf (stderr, "string \"%s\": lbearing=%hd, rbearing=%hd, ascent=%hd, descent=%hd\n", text, bounds.lbearing, bounds.rbearing, bounds.ascent, bounds.descent);
219
fprintf (stderr, "\tsize=(%u,%u), origin=(%d,%d)\n", size_in.x, size_in.y, origin_in.x, origin_in.y);
222
/* create bitmap for text, and lightweight gc */
223
bitmap_in = XCreatePixmap (dpy, DefaultRootWindow (dpy),
224
size_in.x, size_in.y, (unsigned int)1);
225
gc = XCreateGC (dpy, bitmap_in, (unsigned long)0, (XGCValues *)NULL);
226
XSetBackground (dpy, gc, (unsigned long)0);
227
XSetFont (dpy, gc, font->fid);
229
/* clear the bitmap */
230
XSetForeground (dpy, gc, (unsigned long)0);
231
XFillRectangle (dpy, bitmap_in, gc, 0, 0, size_in.x, size_in.y);
232
XSetForeground (dpy, gc, (unsigned long)1);
234
/* draw text onto bitmap */
235
XDrawString (dpy, bitmap_in, gc, origin_in.x, origin_in.y,
236
text, strlen (text));
238
/* create local image */
239
im_in = XAffCreateXImage (dpy, size_in);
243
/* copy bitmap to it */
244
XGetSubImage (dpy, bitmap_in, 0, 0, size_in.x, size_in.y,
245
(unsigned long)1, XYPixmap, im_in, 0, 0);
246
im_in->format = XYBitmap;
249
print_image (im_in, size_in);
252
/* free now-unneeded bitmap */
253
XFreePixmap (dpy, bitmap_in);
255
/* vertices of image, in real coordinates, if each pixel is taken to be a
257
corner_in[0].x = -0.5;
258
corner_in[0].y = -0.5;
259
corner_in[1].x = (int)size_in.x - 0.5;
260
corner_in[1].y = -0.5;
261
corner_in[2].x = (int)size_in.x - 0.5;
262
corner_in[2].y = (int)size_in.y - 0.5;
263
corner_in[3].x = -0.5;
264
corner_in[3].y = (int)size_in.y - 0.5;
266
/* compute vertices (in integer coordinates) of a rectangular array of
267
pixels that will snugly hold the affinely transformed image */
269
mincorner.x = mincorner.y = INT_MAX;
270
maxcorner.x = maxcorner.y = INT_MIN;
271
for (i = 0; i < 4; i++)
273
XAffRealVector v_shifted_in;
274
XAffVector corner_out[4];
276
v_shifted_in.x = corner_in[i].x - origin_in.x;
277
v_shifted_in.y = corner_in[i].y - origin_in.y;
279
corner_out[i].x = IROUND(XAFF_XPROD(v_shifted_in, aa)) + origin_in.x;
280
corner_out[i].y = IROUND(XAFF_YPROD(v_shifted_in, aa)) + origin_in.y;
282
mincorner.x = IMIN(mincorner.x, corner_out[i].x);
283
mincorner.y = IMIN(mincorner.y, corner_out[i].y);
285
maxcorner.x = IMAX(maxcorner.x, corner_out[i].x);
286
maxcorner.y = IMAX(maxcorner.y, corner_out[i].y);
288
size_out.x = maxcorner.x - mincorner.x + 1;
289
size_out.y = maxcorner.y - mincorner.y + 1;
291
origin_out.x = origin_in.x - mincorner.x;
292
origin_out.y = origin_in.y - mincorner.y;
294
/* work around a possible bug: some X displays can't create pixmaps that
295
are only one pixel wide or high */
302
fprintf (stderr, "size_in = (%u,%u)\n", size_in.x, size_in.y);
303
fprintf (stderr, "size_out = (%u,%u)\n", size_out.x, size_out.y);
304
fprintf (stderr, "origin_in = (%d,%d)\n", origin_in.x, origin_in.y);
305
fprintf (stderr, "origin_out = (%d,%d)\n", origin_out.x, origin_out.y);
308
/* create 2nd image, to hold affinely transformed text */
309
im_out = XAffCreateXImage (dpy, size_out);
313
/* copy from 1st image to this new one */
315
scanline_len_in = (size_in.x + 7) / 8;
316
scanline_len_out = (size_out.x + 7) / 8;
318
for (j = 0; j < (int)size_out.y; j++)
321
XAffVector v_in, v_out, v_shifted_out;
326
v_shifted_out.y = v_out.y + mincorner.y - origin_in.y;
328
for (i = 0; i < (int)size_out.x; i++)
331
v_shifted_out.x = v_out.x + mincorner.x - origin_in.x;
333
v_in.x = IROUND(XAFF_XPROD(v_shifted_out, a_inverse)) + origin_in.x;
334
v_in.y = IROUND(XAFF_YPROD(v_shifted_out, a_inverse)) + origin_in.y;
336
if ((!(v_in.x >= 0)) || (!(v_in.x < (int)size_in.x)) ||
337
(!(v_in.y >= 0)) || (!(v_in.y < (int)size_in.y)))
340
/* will be no more hits; so move to next scanline */
342
else /* move to next position on this scanline */
348
if (im_in->data[v_in.y * scanline_len_in + v_in.x / 8]
349
& (128 >> (v_in.x % 8)))
351
im_out->data[v_out.y * scanline_len_out + v_out.x / 8]
352
|= (128 >> (v_out.x % 8));
357
/* free now-unneeded 1st image */
358
XAffFreeXImage (im_in);
360
/* create bitmap to hold transformed text */
361
bitmap_out = XCreatePixmap (dpy, DefaultRootWindow (dpy),
362
size_out.x, size_out.y, (unsigned int)1);
364
/* copy transformed text from 2nd image */
365
XPutImage (dpy, bitmap_out, gc, im_out,
366
0, 0, 0, 0, size_out.x, size_out.y);
369
print_image (im_out, size_out);
372
/* free 2nd image and GC */
373
XAffFreeXImage (im_out);
376
/* fill in data members of instance */
377
afftext->bitmap = bitmap_out;
378
afftext->size = size_out;
379
afftext->origin = origin_out;
385
XAffFreeAffinedText (Display *dpy, XAffAffinedText *afftext)
387
XFreePixmap (dpy, afftext->bitmap);
391
/**************************************************************************/
392
/* Draw an affinely transformed text string */
393
/**************************************************************************/
396
XAffDrawAffString (Display *dpy, Drawable drawable, GC gc, XFontStruct *font, int x, int y, double a[4], const char *text)
398
XAffAffinedText *afftext;
401
if (text == NULL || strlen (text) == 0)
404
if (can_use_XDrawString (font, a, text))
405
/* a[] must be equal to, or near the identity matrix */
406
return XDrawString (dpy, drawable, gc, x, y, text, strlen (text));
408
/* construct annotated bitmap, containing affinely transformed text */
409
afftext = XAffCreateAffinedText (dpy, font, a, text);
413
/* copy gc from user's gc */
414
our_gc = XCreateGC (dpy, drawable, (unsigned long)0, (XGCValues *)NULL);
415
XCopyGC (dpy, gc, GCForeground|GCFunction|GCPlaneMask, our_gc);
417
/* use stipple drawing technique (screen-door patterning) */
418
XSetFillStyle (dpy, our_gc, FillStippled);
419
XSetStipple (dpy, our_gc, afftext->bitmap);
420
XSetTSOrigin (dpy, our_gc,
421
x - afftext->origin.x, y - afftext->origin.y);
422
XFillRectangle (dpy, drawable, our_gc,
423
x - afftext->origin.x, y - afftext->origin.y,
424
afftext->size.x, afftext->size.y);
427
XFreeGC (dpy, our_gc);
428
XAffFreeAffinedText (dpy, afftext);
433
/**************************************************************************/
434
/* Special case: draw a rotated text string */
435
/**************************************************************************/
438
XAffDrawRotString (Display *dpy, Drawable drawable, GC gc, XFontStruct *font, int x, int y, double angle, const char *text)
442
/* convert rotation angle to radians */
443
angle *= (M_PI / 180.0);
445
/* construct transformation matrix (using the XLFD-matrix-extension sign
446
convention for the off-diagonal elements) */
447
a[0] = + cos (angle);
448
a[1] = + sin (angle);
449
a[2] = - sin (angle);
450
a[3] = + cos (angle);
452
return XAffDrawAffString (dpy, drawable, gc, font, x, y, a, text);
455
/**************************************************************************/
456
/* Can simply use core XDrawString function rather than transforming the */
457
/* resulting bitmap? (Yes, if the matrix a[] is near the identity.) */
458
/**************************************************************************/
461
can_use_XDrawString (XFontStruct *font, double a[4], const char *text)
463
int direction, font_ascent, font_descent;
465
XAffVector corner_in[4], corner_out[4];
467
XAffVector origin_in;
468
int i, can_do_it = 1;
471
/* as passed, a[] is in the format used in the matrix LFD enhancement,
472
which assumes a right-handed coordinate system; so convert it to X11's
473
left-hand coordinate system (y grows downward) */
479
/* to include all pixels in text, how large should bitmap be? */
480
XTextExtents (font, text, strlen (text),
481
&direction, &font_ascent, &font_descent, &bounds);
483
/* bitmap size, number-of-pixels by number-of-pixels */
484
size_in.x = - bounds.lbearing + bounds.rbearing;
485
size_in.y = bounds.ascent + bounds.descent;
487
/* within this bitmap, where is `origin' of text string? */
488
origin_in.x = - bounds.lbearing;
489
origin_in.y = bounds.ascent;
491
/* corners in integer coordinates, relative to origin */
494
corner_in[1].x = size_in.x - 1;
496
corner_in[2].x = size_in.x - 1;
497
corner_in[2].y = size_in.y - 1;
499
corner_in[3].y = size_in.y - 1;
501
/* compute how corners are transformed by a[] */
502
for (i = 0; i < 4; i++)
504
XAffVector v_shifted_in;
506
v_shifted_in.x = corner_in[i].x - origin_in.x;
507
v_shifted_in.y = corner_in[i].y - origin_in.y;
509
corner_out[i].x = IROUND(XAFF_XPROD(v_shifted_in, aa)) + origin_in.x;
510
corner_out[i].y = IROUND(XAFF_YPROD(v_shifted_in, aa)) + origin_in.y;
512
if (corner_out[i].x != corner_in[i].x
513
|| corner_out[i].y != corner_in[i].y)
514
/* at least one corner moves, no good, alas */
524
/**************************************************************************/
525
/* Print an image to stderr (used for debugging, if -DDEBUG is specified)*/
526
/**************************************************************************/
530
print_image (const XImage *im, XAffSize size)
535
scanline_len = (size.x + 7) / 8;
537
for (j = 0; j < (int)size.y; j++)
539
for (i = 0; i < (int)size.x; i++)
541
if (im->data[j * scanline_len + i / 8] & (128 >> (i % 8)))
542
fprintf (stderr, "*");
544
fprintf (stderr, " ");
546
fprintf (stderr, "\n");