1 
2 /*
3  * bltText.c --
4  *
5  *	This module implements multi-line, rotate-able text for the BLT toolkit.
6  *
7  * Copyright 1993-1998 Lucent Technologies, Inc.
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that the copyright notice and warranty
13  * disclaimer appear in supporting documentation, and that the names
14  * of Lucent Technologies any of their entities not be used in
15  * advertising or publicity pertaining to distribution of the software
16  * without specific, written prior permission.
17  *
18  * Lucent Technologies disclaims all warranties with regard to this
19  * software, including all implied warranties of merchantability and
20  * fitness.  In no event shall Lucent Technologies be liable for any
21  * special, indirect or consequential damages or any damages
22  * whatsoever resulting from loss of use, data or profits, whether in
23  * an action of contract, negligence or other tortuous action, arising
24  * out of or in connection with the use or performance of this
25  * software.
26  */
27 
28 #include "bltInt.h"
29 #include <X11/Xutil.h>
30 #include "bltImage.h"
31 
32 #define WINDEBUG	0
33 
34 static Blt_HashTable bitmapGCTable;
35 static int initialized;
36 
Blt_TextLayoutValue(TextLayout * textPtr,Tcl_DString * dStr)37 int Blt_TextLayoutValue( TextLayout *textPtr, Tcl_DString *dStr) {
38     register TextFragment *fragPtr;
39     register int i;
40 
41     fragPtr = textPtr->fragArr;
42     for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
43         if (i) {
44             Tcl_DStringAppend(dStr, "\n", -1);
45         }
46         Tcl_DStringAppend(dStr, fragPtr->text, fragPtr->count);
47     }
48     return i;
49 }
50 
51 void
Blt_AverageImage(im,w,h,d)52 Blt_AverageImage(im, w, h, d)
53     XImage *im;
54     unsigned int w, h, d;
55 {
56     /* the image will end up with 1 or 0 in the first plane, rest zero */
57     int i, j, k;
58     unsigned int d2;
59     unsigned long pixel, pixelave;
60 
61     d2 = (3*d)/4;
62     for (i=0 ; i<w ; i++) {
63         for (j=0 ; j<h ; j++) {
64             pixel = XGetPixel(im, i, j);
65             pixelave = pixel & 1;
66             for (k=1 ; k<d ; k++) {
67                 pixel >>= 1;
68                 pixelave += pixel & 1;
69             }
70             pixelave = (pixelave + d2)/d;
71             XPutPixel(im, i, j, pixelave);
72         }
73     }
74     return;
75 }
76 
77 #ifndef WIN32
78 static void
Blt_DrawCharsBitmap(display,bitmap,w,width,height,gc,font,x,y,textPtr)79 Blt_DrawCharsBitmap(display, bitmap, w, width, height, gc, font, x, y, textPtr)
80     Display *display;
81     Drawable bitmap;
82     Window w;
83     unsigned int width, height;
84     GC gc;
85     Tk_Font font;
86     register int x, y;
87     TextLayout *textPtr;
88 {
89     register TextFragment *fragPtr;
90     register int i, j;
91 
92     XImage *bitmapimage, *pixmapimage;
93     GC gcsave, gcp;
94     Window winr;
95     int xr, yr;
96     unsigned int wr, hr, br, dr;
97     Pixmap pixmap;
98     unsigned long whitepixel;
99     XGCValues gcvalues ;
100     unsigned long valuemask, pixel ;
101 
102     /* save the GC */
103     gcsave = XCreateGC(display, bitmap, 0, NULL);
104     XCopyGC(display, gc, 0, gcsave);
105     /* create a pixmap to draw chars to */
106     XGetGeometry(display, w, &winr, &xr, &yr, &wr, &hr, &br, &dr);
107     pixmap = Tk_GetPixmap(display, winr, width, height, dr);
108     /* get a GC for the pixmap, black(0) background, white(1) chars */
109     valuemask = (GCForeground|GCBackground) ;
110     gcvalues.foreground = gcvalues.background = 0 ;
111     gcp = XCreateGC(display, pixmap, valuemask, &gcvalues);
112     XSetForeground(display, gcp, 0) ;
113     XFillRectangle(display, pixmap, gcp, 0, 0, width, height);
114     whitepixel = XWhitePixel(display, 0);
115     XSetForeground(display, gcp, whitepixel) ;
116     /* draw white chars to the pixmap */
117     fragPtr = textPtr->fragArr;
118     for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
119         Tk_DrawChars(display, pixmap, gcp, font, fragPtr->text,
120             fragPtr->count, x + fragPtr->x, y + fragPtr->y);
121     }
122     XFlush(display);
123     /* get the pixmap image */
124     pixmapimage = XGetImage(display, pixmap, 0, 0, width, height,
125         AllPlanes, XYPixmap);
126     /* average the image to get the char in a single bit plane */
127     Blt_AverageImage(pixmapimage, width, height, dr);
128     /* get an image for the bitmap */
129     bitmapimage = XGetImage(display, bitmap, 0, 0, width, height,
130 	 1, XYPixmap);
131     /* copy the first bit plane from the averaged pixmap to the bitmap */
132     for (i=0 ; i<width ; i++) {
133         for(j=0 ; j<height ; j++) {
134             pixel = XGetPixel(pixmapimage, i, j);
135             XPutPixel(bitmapimage, i, j, pixel);
136         }
137     }
138     /* but the bitmapimage into the bitmap with gc fg=1 bg=0 clipmask=None */
139     XSetForeground(display, gc, 1);
140     XSetBackground(display, gc, 0);
141     XSetClipMask(display, gc, None);
142     XPutImage(display, bitmap, gc, bitmapimage, 0, 0, 0, 0, width, height);
143     /* reset gc and clean up */
144     XCopyGC(display, gcsave, 0, gc);
145     XFreeGC(display, gcp);
146     XFreeGC(display, gcsave);
147     XDestroyImage(bitmapimage);
148     XDestroyImage(pixmapimage);
149     Tk_FreePixmap(display, pixmap);
150     return;
151 }
152 #endif
153 
154 static void
DrawTextLayout(display,drawable,gc,font,x,y,textPtr,tsPtr)155 DrawTextLayout(display, drawable, gc, font, x, y, textPtr, tsPtr)
156     Display *display;
157     Drawable drawable;
158     GC gc;
159     Tk_Font font;
160     register int x, y;		/* Origin of text */
161     TextLayout *textPtr;
162     TextStyle *tsPtr;
163 {
164     register TextFragment *fragPtr;
165     register int i;
166     int n, u, ml, noxft = 0;
167 
168     n = 0, ml = 3000;
169     fragPtr = textPtr->fragArr;
170     /* TODO: Windows Tk_DrawChars() uses only gc font!!! */
171     for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
172        /* extern int tclWarnVar[];
173        noxft = tclWarnVar[23]; */
174 #if HAVE_UTF
175         if (textPtr->rotated == 0  || noxft ) {
176             Tk_DrawChars(display, drawable, gc, font, fragPtr->text,
177 	       fragPtr->count, x + fragPtr->x, y + fragPtr->y);
178 	} else {
179 #ifdef WIN32
180         Tk_DrawChars(display, drawable, gc, font, fragPtr->text,
181 	    fragPtr->count, x + fragPtr->x, y + fragPtr->y);
182 #else
183 	   /*XDrawString(display, drawable, gc, x + fragPtr->x, y + fragPtr->y,
184 	       fragPtr->text, fragPtr->count);*/
185 
186     /*
187      * Tk_DrawChars no longer works on bitmaps for antialiased fonts
188      * Fix this by checking for a bitmap and calling code that
189      * DrawChars to bitmap
190      */
191     Window w;
192     int xd, yd;
193     unsigned int wd, hd, bd, dd;
194     XGetGeometry(display, drawable, &w, &xd, &yd, &wd, &hd, &bd, &dd);
195     if ( dd == 1 ) {
196       Blt_DrawCharsBitmap(display, drawable, w, wd, hd, gc, font,
197 			  x, y, textPtr);
198       return;
199     }
200     fragPtr = textPtr->fragArr;
201     for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
202         Tk_DrawChars(display, drawable, gc, font, fragPtr->text,
203 		     fragPtr->count, x + fragPtr->x, y + fragPtr->y);
204      }
205 #endif
206    }
207 #else
208 	XDrawString(display, drawable, gc, x + fragPtr->x, y + fragPtr->y,
209 	    fragPtr->text, fragPtr->count);
210 #endif /*HAVE_UTF*/
211         u = tsPtr->underline;
212         if (u >= 0 && u < (n + fragPtr->count) && fragPtr->count>0) {
213             int cw = (fragPtr->width / fragPtr->count);
214             int x1a = x + fragPtr->x + (cw * (u - n));
215             int y1a = y + fragPtr->y + 2;
216             int x2a = x1a + cw;
217             int y2a = y1a;
218             XDrawLine(display, drawable, gc, x1a, y1a, x2a, y2a);
219         }
220         if (i>ml) break;
221         n += fragPtr->count;
222     }
223 }
224 
225 /*
226  * -----------------------------------------------------------------
227  *
228  * Blt_GetTextLayout --
229  *
230  *	Get the extents of a possibly multiple-lined text string.
231  *
232  * Results:
233  *	Returns via *widthPtr* and *heightPtr* the dimensions of
234  *	the text string.
235  *
236  * -----------------------------------------------------------------
237  */
238 static TextLayout *
bltGetTextLayoutStr(string,tsPtr,store)239 bltGetTextLayoutStr(string, tsPtr, store)
240     char string[];
241     TextStyle *tsPtr;
242     int store; /* Store a copy of string. */
243 {
244     int maxHeight, maxWidth;
245     int count;			/* Count # of characters on each line */
246     int nFrags;
247     int width;			/* Running dimensions of the text */
248     TextFragment *fragPtr;
249     TextLayout *textPtr;
250     int lineHeight;
251     int size, len, maxW, maxH, maxC;
252     register char *p;
253     register int i;
254     Tk_FontMetrics fontMetrics;
255 
256     maxW = ((1<<(8*sizeof(fragPtr->width)))/2-101);
257     maxH = ((1<<(8*sizeof(textPtr->height)))/2-101);
258     maxC = ((1<<(8*sizeof(fragPtr->count)))/2-101);
259     Tk_GetFontMetrics(tsPtr->font, &fontMetrics);
260     lineHeight = fontMetrics.linespace +
261 	tsPtr->leader + tsPtr->shadow.offset;
262     nFrags = 0;
263     for (p = string; *p != '\0'; p++) {
264 	if (*p == '\n') {
265 	    nFrags++;
266 	}
267     }
268     if ((p != string) && (*(p - 1) != '\n')) {
269 	nFrags++;
270     }
271     len = strlen(string);
272     size = sizeof(TextLayout) + (sizeof(TextFragment) * (nFrags - 1));
273     if (store) {
274         size += len+1;
275     }
276     textPtr = Blt_Calloc(1, size);
277     if (store) {
278         p = (char*)textPtr;
279         strcpy(p+size-len-1, string);
280         string = p+size-len-1;
281     }
282     if (tsPtr->theta != 0.0) {
283 #ifndef WIN32
284         /* XFT bug crashes X with rotated text. */
285         textPtr->rotated = 1;
286 #endif
287     }
288     textPtr->nFrags = nFrags;
289     nFrags = count = 0;
290     width = maxWidth = 0;
291     maxHeight = tsPtr->padTop;
292     fragPtr = textPtr->fragArr;
293     for (p = string; *p != '\0'; p++) {
294 	if (*p == '\n') {
295 	    if (count > 0) {
296 		width = Tk_TextWidth(tsPtr->font, string, count) +
297 		    tsPtr->shadow.offset;
298 		if (width >= maxW) {
299 		    width = maxW;
300 		}
301 		if (width > maxWidth) {
302 		    maxWidth = width;
303 		}
304 	    }
305 	    fragPtr->width = width;
306 	    fragPtr->count = (count>maxC?maxC:count);
307 	    fragPtr->y = maxHeight + fontMetrics.ascent;
308 	    fragPtr->text = string;
309 	    fragPtr++;
310 	    nFrags++;
311 	    maxHeight += lineHeight;
312 	    string = p + 1;	/* Start the string on the next line */
313 	    count = 0;		/* Reset to indicate the start of a new line */
314 	    if (maxHeight>0xff00 && (sizeof(tsPtr->height)==2)) break;
315 	    continue;
316 	}
317 	count++;
318     }
319     if (nFrags < textPtr->nFrags) {
320 	width = Tk_TextWidth(tsPtr->font, string, count) + tsPtr->shadow.offset;
321         if (width >= maxW) {
322             width = maxW;
323         }
324 	if (width > maxWidth) {
325 	    maxWidth = width;
326 	}
327 	fragPtr->width = width;
328 	fragPtr->count = (count>maxC?maxC:count);
329 	fragPtr->y = maxHeight + fontMetrics.ascent;
330 	fragPtr->text = string;
331 	maxHeight += lineHeight;
332 	nFrags++;
333     }
334     maxHeight += tsPtr->padBottom;
335     maxWidth += PADDING(tsPtr->padX);
336     fragPtr = textPtr->fragArr;
337     for (i = 0; i < nFrags; i++, fragPtr++) {
338 	switch (tsPtr->justify) {
339 	default:
340 	case TK_JUSTIFY_LEFT:
341 	    /* No offset for left justified text strings */
342 	    fragPtr->x = tsPtr->padLeft;
343 	    break;
344 	case TK_JUSTIFY_RIGHT:
345 	    fragPtr->x = (maxWidth - fragPtr->width) - tsPtr->padRight;
346 	    break;
347 	case TK_JUSTIFY_CENTER:
348 	    fragPtr->x = (maxWidth - fragPtr->width) / 2;
349 	    break;
350 	}
351     }
352     textPtr->width = maxWidth;
353     if (maxHeight >= maxH) {
354         maxHeight = maxH;
355     }
356     textPtr->height = maxHeight - tsPtr->leader;
357     return textPtr;
358 }
359 
360 TextLayout *
Blt_GetTextLayoutStr(string,tsPtr)361 Blt_GetTextLayoutStr(string, tsPtr)
362     char string[];
363     TextStyle *tsPtr;
364 {
365     return bltGetTextLayoutStr(string, tsPtr, 1);
366 }
367 
368 TextLayout *
Blt_GetTextLayout(string,tsPtr)369 Blt_GetTextLayout(string, tsPtr)
370     char string[];
371     TextStyle *tsPtr;
372 {
373     return bltGetTextLayoutStr(string, tsPtr, 0);
374 }
375 
376 /*
377  * -----------------------------------------------------------------
378  *
379  * Blt_GetTextExtents --
380  *
381  *	Get the extents of a possibly multiple-lined text string.
382  *
383  * Results:
384  *	Returns via *widthPtr* and *heightPtr* the dimensions of
385  *	the text string.
386  *
387  * -----------------------------------------------------------------
388  */
389 void
Blt_GetTextExtents(tsPtr,string,widthPtr,heightPtr)390 Blt_GetTextExtents(tsPtr, string, widthPtr, heightPtr)
391     TextStyle *tsPtr;
392     char *string;
393     int *widthPtr, *heightPtr;
394 {
395     int count;			/* Count # of characters on each line */
396     int width, height;
397     int w, lineHeight;
398     register char *p;
399     Tk_FontMetrics fontMetrics;
400 
401     if (string == NULL) {
402 	return;			/* NULL string? */
403     }
404     Tk_GetFontMetrics(tsPtr->font, &fontMetrics);
405     lineHeight = fontMetrics.linespace + tsPtr->leader + tsPtr->shadow.offset;
406     count = 0;
407     width = height = 0;
408     for (p = string; *p != '\0'; p++) {
409 	if (*p == '\n') {
410 	    if (count > 0) {
411 		w = Tk_TextWidth(tsPtr->font, string, count) +
412 		    tsPtr->shadow.offset;
413 		if (w > width) {
414 		    width = w;
415 		}
416 	    }
417 	    height += lineHeight;
418 	    string = p + 1;	/* Start the string on the next line */
419 	    count = 0;		/* Reset to indicate the start of a new line */
420 	    continue;
421 	}
422 	count++;
423     }
424     if ((count > 0) && (*(p - 1) != '\n')) {
425 	height += lineHeight;
426 	w = Tk_TextWidth(tsPtr->font, string, count) + tsPtr->shadow.offset;
427 	if (w > width) {
428 	    width = w;
429 	}
430     }
431     *widthPtr = width + PADDING(tsPtr->padX);
432     *heightPtr = height + PADDING(tsPtr->padY);
433 }
434 
435 /*
436  * -----------------------------------------------------------------
437  *
438  * Blt_GetBoundingBox
439  *
440  *	Computes the dimensions of the bounding box surrounding a
441  *	rectangle rotated about its center.  If pointArr isn't NULL,
442  *	the coordinates of the rotated rectangle are also returned.
443  *
444  *	The dimensions are determined by rotating the rectangle, and
445  *	doubling the maximum x-coordinate and y-coordinate.
446  *
447  *		w = 2 * maxX,  h = 2 * maxY
448  *
449  *	Since the rectangle is centered at 0,0, the coordinates of
450  *	the bounding box are (-w/2,-h/2 w/2,-h/2, w/2,h/2 -w/2,h/2).
451  *
452  *  		0 ------- 1
453  *  		|         |
454  *  		|    x    |
455  *  		|         |
456  *  		3 ------- 2
457  *
458  * Results:
459  *	The width and height of the bounding box containing the
460  *	rotated rectangle are returned.
461  *
462  * -----------------------------------------------------------------
463  */
464 void
Blt_GetBoundingBox(width,height,theta,rotWidthPtr,rotHeightPtr,bbox)465 Blt_GetBoundingBox(width, height, theta, rotWidthPtr, rotHeightPtr, bbox)
466     int width, height;		/* Unrotated region */
467     double theta;		/* Rotation of box */
468     double *rotWidthPtr, *rotHeightPtr;	/* (out) Bounding box region */
469     Point2D *bbox;		/* (out) Points of the rotated box */
470 {
471     register int i;
472     double sinTheta, cosTheta;
473     double xMax, yMax;
474     register double x, y;
475     Point2D corner[4];
476 
477     theta = FMOD(theta, 360.0);
478     if (FMOD(theta, (double)90.0) == 0.0) {
479 	int ll, ur, ul, lr;
480 	double rotWidth, rotHeight;
481 	int quadrant;
482 
483 	/* Handle right-angle rotations specifically */
484 
485 	quadrant = (int)(theta / 90.0);
486 	switch (quadrant) {
487 	case ROTATE_270:	/* 270 degrees */
488 	    ul = 3, ur = 0, lr = 1, ll = 2;
489 	    rotWidth = (double)height;
490 	    rotHeight = (double)width;
491 	    break;
492 	case ROTATE_90:		/* 90 degrees */
493 	    ul = 1, ur = 2, lr = 3, ll = 0;
494 	    rotWidth = (double)height;
495 	    rotHeight = (double)width;
496 	    break;
497 	case ROTATE_180:	/* 180 degrees */
498 	    ul = 2, ur = 3, lr = 0, ll = 1;
499 	    rotWidth = (double)width;
500 	    rotHeight = (double)height;
501 	    break;
502 	default:
503 	case ROTATE_0:		/* 0 degrees */
504 	    ul = 0, ur = 1, lr = 2, ll = 3;
505 	    rotWidth = (double)width;
506 	    rotHeight = (double)height;
507 	    break;
508 	}
509 	if (bbox != NULL) {
510 	    x = rotWidth * 0.5;
511 	    y = rotHeight * 0.5;
512 	    bbox[ll].x = bbox[ul].x = -x;
513 	    bbox[ur].y = bbox[ul].y = -y;
514 	    bbox[lr].x = bbox[ur].x = x;
515 	    bbox[ll].y = bbox[lr].y = y;
516 	}
517 	*rotWidthPtr = rotWidth;
518 	*rotHeightPtr = rotHeight;
519 	return;
520     }
521     /* Set the four corners of the rectangle whose center is the origin */
522 
523     corner[1].x = corner[2].x = (double)width *0.5;
524     corner[0].x = corner[3].x = -corner[1].x;
525     corner[2].y = corner[3].y = (double)height *0.5;
526     corner[0].y = corner[1].y = -corner[2].y;
527 
528     theta = (-theta / 180.0) * M_PI;
529     sinTheta = sin(theta), cosTheta = cos(theta);
530     xMax = yMax = 0.0;
531 
532     /* Rotate the four corners and find the maximum X and Y coordinates */
533 
534     for (i = 0; i < 4; i++) {
535 	x = (corner[i].x * cosTheta) - (corner[i].y * sinTheta);
536 	y = (corner[i].x * sinTheta) + (corner[i].y * cosTheta);
537 	if (x > xMax) {
538 	    xMax = x;
539 	}
540 	if (y > yMax) {
541 	    yMax = y;
542 	}
543 	if (bbox != NULL) {
544 	    bbox[i].x = x;
545 	    bbox[i].y = y;
546 	}
547     }
548 
549     /*
550      * By symmetry, the width and height of the bounding box are
551      * twice the maximum x and y coordinates.
552      */
553     *rotWidthPtr = xMax + xMax;
554     *rotHeightPtr = yMax + yMax;
555 }
556 
557 /*
558  * -----------------------------------------------------------------
559  *
560  * Blt_TranslateAnchor --
561  *
562  * 	Translate the coordinates of a given bounding box based
563  *	upon the anchor specified.  The anchor indicates where
564  *	the given xy position is in relation to the bounding box.
565  *
566  *  		nw --- n --- ne
567  *  		|            |
568  *  		w   center   e
569  *  		|            |
570  *  		sw --- s --- se
571  *
572  * 	The coordinates returned are translated to the origin of the
573  * 	bounding box (suitable for giving to XCopyArea, XCopyPlane, etc.)
574  *
575  * Results:
576  *	The translated coordinates of the bounding box are returned.
577  *
578  * -----------------------------------------------------------------
579  */
580 void
Blt_TranslateAnchor(x,y,width,height,anchor,transXPtr,transYPtr)581 Blt_TranslateAnchor(x, y, width, height, anchor, transXPtr, transYPtr)
582     int x, y;			/* Window coordinates of anchor */
583     int width, height;		/* Extents of the bounding box */
584     Tk_Anchor anchor;		/* Direction of the anchor */
585     int *transXPtr, *transYPtr;
586 {
587     switch (anchor) {
588     case TK_ANCHOR_NW:		/* Upper left corner */
589 	break;
590     case TK_ANCHOR_W:		/* Left center */
591 	y -= (height / 2);
592 	break;
593     case TK_ANCHOR_SW:		/* Lower left corner */
594 	y -= height;
595 	break;
596     case TK_ANCHOR_N:		/* Top center */
597 	x -= (width / 2);
598 	break;
599     case TK_ANCHOR_CENTER:	/* Center */
600 	x -= (width / 2);
601 	y -= (height / 2);
602 	break;
603     case TK_ANCHOR_S:		/* Bottom center */
604 	x -= (width / 2);
605 	y -= height;
606 	break;
607     case TK_ANCHOR_NE:		/* Upper right corner */
608 	x -= width;
609 	break;
610     case TK_ANCHOR_E:		/* Right center */
611 	x -= width;
612 	y -= (height / 2);
613 	break;
614     case TK_ANCHOR_SE:		/* Lower right corner */
615 	x -= width;
616 	y -= height;
617 	break;
618     }
619     *transXPtr = x;
620     *transYPtr = y;
621 }
622 
623 /*
624  * -----------------------------------------------------------------
625  *
626  * Blt_TranslatePoint --
627  *
628  * 	Translate the coordinates of a given bounding box based
629  *	upon the anchor specified.  The anchor indicates where
630  *	the given xy position is in relation to the bounding box.
631  *
632  *  		nw --- n --- ne
633  *  		|            |
634  *  		w   center   e
635  *  		|            |
636  *  		sw --- s --- se
637  *
638  * 	The coordinates returned are translated to the origin of the
639  * 	bounding box (suitable for giving to XCopyArea, XCopyPlane, etc.)
640  *
641  * Results:
642  *	The translated coordinates of the bounding box are returned.
643  *
644  * -----------------------------------------------------------------
645  */
646 Point2D
Blt_TranslatePoint(pointPtr,width,height,anchor)647 Blt_TranslatePoint(pointPtr, width, height, anchor)
648     Point2D *pointPtr;		/* Window coordinates of anchor */
649     int width, height;		/* Extents of the bounding box */
650     Tk_Anchor anchor;		/* Direction of the anchor */
651 {
652     Point2D trans;
653 
654     trans = *pointPtr;
655     switch (anchor) {
656     case TK_ANCHOR_NW:		/* Upper left corner */
657 	break;
658     case TK_ANCHOR_W:		/* Left center */
659 	trans.y -= (height * 0.5);
660 	break;
661     case TK_ANCHOR_SW:		/* Lower left corner */
662 	trans.y -= height;
663 	break;
664     case TK_ANCHOR_N:		/* Top center */
665 	trans.x -= (width * 0.5);
666 	break;
667     case TK_ANCHOR_CENTER:	/* Center */
668 	trans.x -= (width * 0.5);
669 	trans.y -= (height * 0.5);
670 	break;
671     case TK_ANCHOR_S:		/* Bottom center */
672 	trans.x -= (width * 0.5);
673 	trans.y -= height;
674 	break;
675     case TK_ANCHOR_NE:		/* Upper right corner */
676 	trans.x -= width;
677 	break;
678     case TK_ANCHOR_E:		/* Right center */
679 	trans.x -= width;
680 	trans.y -= (height * 0.5);
681 	break;
682     case TK_ANCHOR_SE:		/* Lower right corner */
683 	trans.x -= width;
684 	trans.y -= height;
685 	break;
686     }
687     return trans;
688 }
689 
690 /*
691  * -----------------------------------------------------------------
692  *
693  * Blt_CreateTextBitmap --
694  *
695  *	Draw a bitmap, using the the given window coordinates
696  *	as an anchor for the text bounding box.
697  *
698  * Results:
699  *	Returns the bitmap representing the text string.
700  *
701  * Side Effects:
702  *	Bitmap is drawn using the given font and GC in the
703  *	drawable at the given coordinates, anchor, and rotation.
704  *
705  * -----------------------------------------------------------------
706  */
707 Pixmap
Blt_CreateTextBitmap(tkwin,textPtr,tsPtr,bmWidthPtr,bmHeightPtr)708 Blt_CreateTextBitmap(tkwin, textPtr, tsPtr, bmWidthPtr, bmHeightPtr)
709     Tk_Window tkwin;
710     TextLayout *textPtr;	/* Text string to draw */
711     TextStyle *tsPtr;		/* Text attributes: rotation, color, font,
712 				 * linespacing, justification, etc. */
713     int *bmWidthPtr;
714     int *bmHeightPtr;		/* Extents of rotated text string */
715 {
716     int width, height;
717     Pixmap bitmap;
718     Display *display;
719     Window root;
720     GC gc;
721 #ifdef WIN32
722     HDC hDC;
723     TkWinDCState state;
724 #endif
725     display = Tk_Display(tkwin);
726 
727     width = textPtr->width;
728     height = textPtr->height;
729 
730     /* Create a temporary bitmap to contain the text string */
731     root = RootWindow(display, Tk_ScreenNumber(tkwin));
732     bitmap = Tk_GetPixmap(display, root, width, height, 1);
733     assert(bitmap != None);
734     if (bitmap == None) {
735 	return None;		/* Can't allocate pixmap. */
736     }
737     /* Clear the pixmap and draw the text string into it */
738     gc = Blt_GetBitmapGC(tkwin);
739 #ifdef WIN32
740     hDC = TkWinGetDrawableDC(display, bitmap, &state);
741     PatBlt(hDC, 0, 0, width, height, WHITENESS);
742     TkWinReleaseDrawableDC(bitmap, hDC, &state);
743 #else
744     XSetForeground(display, gc, 0);
745     XFillRectangle(display, bitmap, gc, 0, 0, width, height);
746 #endif /* WIN32 */
747 
748     XSetFont(display, gc, Tk_FontId(tsPtr->font));
749     XSetForeground(display, gc, 1);
750     DrawTextLayout(display, bitmap, gc, tsPtr->font, 0, 0, textPtr, tsPtr);
751 
752 #ifdef WIN32
753     /*
754      * Under Win32 when drawing into a bitmap, the bits are
755      * reversed. Which is why we are inverting the bitmap here.
756      */
757     hDC = TkWinGetDrawableDC(display, bitmap, &state);
758     PatBlt(hDC, 0, 0, width, height, DSTINVERT);
759     TkWinReleaseDrawableDC(bitmap, hDC, &state);
760 #endif
761     if (tsPtr->theta != 0.0) {
762 	Pixmap rotBitmap;
763 
764 	/* Replace the text pixmap with a rotated one */
765 
766 	rotBitmap = Blt_RotateBitmap(tkwin, bitmap, width, height,
767 		tsPtr->theta, bmWidthPtr, bmHeightPtr);
768 	assert(rotBitmap);
769 	if (rotBitmap != None) {
770 	    Tk_FreePixmap(display, bitmap);
771 	    return rotBitmap;
772 	}
773     }
774     *bmWidthPtr = textPtr->width, *bmHeightPtr = textPtr->height;
775     return bitmap;
776 }
777 
778 /*LINTLIBRARY*/
779 void
Blt_InitTextStyle(tsPtr)780 Blt_InitTextStyle(tsPtr)
781     TextStyle *tsPtr;
782 {
783     /* Initialize these attributes to zero */
784     tsPtr->activeColor = (XColor *)NULL;
785     tsPtr->anchor = TK_ANCHOR_CENTER;
786     tsPtr->color = (XColor *)NULL;
787     tsPtr->font = NULL;
788     tsPtr->justify = TK_JUSTIFY_CENTER;
789     tsPtr->leader = 0;
790     tsPtr->padLeft = tsPtr->padRight = 0;
791     tsPtr->padTop = tsPtr->padBottom = 0;
792     tsPtr->shadow.color = (XColor *)NULL;
793     tsPtr->shadow.offset = 0;
794     tsPtr->state = 0;
795     tsPtr->theta = 0.0;
796     tsPtr->underline = -1;
797 }
798 
799 void
Blt_SetDrawTextStyle(tsPtr,font,gc,normalColor,activeColor,shadowColor,theta,anchor,justify,leader,shadowOffset)800 Blt_SetDrawTextStyle(tsPtr, font, gc, normalColor, activeColor, shadowColor,
801 	theta, anchor, justify, leader, shadowOffset)
802     TextStyle *tsPtr;
803     Tk_Font font;
804     GC gc;
805     XColor *normalColor, *activeColor, *shadowColor;
806     double theta;
807     Tk_Anchor anchor;
808     Tk_Justify justify;
809     int leader, shadowOffset;
810 {
811     Blt_InitTextStyle(tsPtr);
812     tsPtr->activeColor = activeColor;
813     tsPtr->anchor = anchor;
814     tsPtr->color = normalColor;
815     tsPtr->font = font;
816     tsPtr->gc = gc;
817     tsPtr->justify = justify;
818     tsPtr->leader = leader;
819     tsPtr->shadow.color = shadowColor;
820     tsPtr->shadow.offset = shadowOffset;
821     tsPtr->theta = theta;
822 }
823 
824 void
Blt_SetPrintTextStyle(tsPtr,font,fgColor,activeColor,shadowColor,theta,anchor,justify,leader,shadowOffset)825 Blt_SetPrintTextStyle(tsPtr, font, fgColor, activeColor, shadowColor, theta,
826 	anchor, justify, leader, shadowOffset)
827     TextStyle *tsPtr;
828     Tk_Font font;
829     XColor *fgColor, *activeColor, *shadowColor;
830     double theta;
831     Tk_Anchor anchor;
832     Tk_Justify justify;
833     int leader, shadowOffset;
834 {
835     Blt_InitTextStyle(tsPtr);
836     tsPtr->color = fgColor;
837     tsPtr->activeColor = activeColor;
838     tsPtr->shadow.color = shadowColor;
839     tsPtr->font = font;
840     tsPtr->theta = theta;
841     tsPtr->anchor = anchor;
842     tsPtr->justify = justify;
843     tsPtr->leader = leader;
844     tsPtr->shadow.offset = shadowOffset;
845 }
846 
847 /*
848  * -----------------------------------------------------------------
849  *
850  * Blt_DrawTextLayout --
851  *
852  *	Draw a text string, possibly rotated, using the the given
853  *	window coordinates as an anchor for the text bounding box.
854  *	If the text is not rotated, simply use the X text drawing
855  *	routines. Otherwise, generate a bitmap of the rotated text.
856  *
857  * Results:
858  *	Returns the x-coordinate to the right of the text.
859  *
860  * Side Effects:
861  *	Text string is drawn using the given font and GC at the
862  *	the given window coordinates.
863  *
864  *      The Stipple, FillStyle, and TSOrigin fields of the GC are
865  *      modified for rotated text.  This assumes the GC is private,
866  *      *not* shared (via Tk_GetGC)
867  *
868  * -----------------------------------------------------------------
869  */
870 void
Blt_DrawTextLayout(tkwin,drawable,textPtr,tsPtr,x,y)871 Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y)
872     Tk_Window tkwin;
873     Drawable drawable;
874     TextLayout *textPtr;
875     TextStyle *tsPtr;		/* Text attribute information */
876     int x, y;			/* Window coordinates to draw text */
877 {
878     int width, height;
879     double theta;
880     Display *display;
881     Pixmap bitmap;
882     int active;
883 
884     display = Tk_Display(tkwin);
885     theta = FMOD(tsPtr->theta, (double)360.0);
886     if (theta < 0.0) {
887 	theta += 360.0;
888     }
889     active = tsPtr->state & STATE_ACTIVE;
890     if (theta == 0.0) {
891 
892 	/*
893 	 * This is the easy case of no rotation. Simply draw the text
894 	 * using the standard drawing routines.  Handle offset printing
895 	 * for engraved (disabled) and shadowed text.
896 	 */
897 	width = textPtr->width, height = textPtr->height;
898 	Blt_TranslateAnchor(x, y, width, height, tsPtr->anchor, &x, &y);
899 	if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
900 	    TkBorder *borderPtr = (TkBorder *) tsPtr->border;
901 	    XColor *color1, *color2;
902 
903 	    color1 = borderPtr->lightColorPtr, color2 = borderPtr->darkColorPtr;
904 	    if (tsPtr->state & STATE_EMPHASIS) {
905 		XColor *hold;
906 
907 		hold = color1, color1 = color2, color2 = hold;
908 	    }
909 	    if (color1 != NULL) {
910 		XSetForeground(display, tsPtr->gc, color1->pixel);
911 	    }
912 	    DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x + 1,
913 		y + 1, textPtr, tsPtr);
914 	    if (color2 != NULL) {
915 		XSetForeground(display, tsPtr->gc, color2->pixel);
916 	    }
917 	    DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x, y,
918 		textPtr, tsPtr);
919 
920 	    /* Reset the foreground color back to its original setting,
921 	     * so not to invalidate the GC cache. */
922 	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
923 
924 	    return;		/* Done */
925 	}
926 	if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
927 	    XSetForeground(display, tsPtr->gc, tsPtr->shadow.color->pixel);
928 	    DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font,
929 		   x + tsPtr->shadow.offset, y + tsPtr->shadow.offset, textPtr,
930 		   tsPtr);
931 	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
932 	}
933 	if (active) {
934 	    XSetForeground(display, tsPtr->gc, tsPtr->activeColor->pixel);
935 	}
936 	DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x, y,
937 		textPtr, tsPtr);
938 	if (active) {
939 	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
940 	}
941 	return;			/* Done */
942     }
943 #ifdef WIN32
944     if (Blt_DrawRotatedText(display, drawable, x, y, theta, tsPtr, textPtr)) {
945 	return;
946     }
947 #endif
948     /*
949      * Rotate the text by writing the text into a bitmap and rotating
950      * the bitmap.  Set the clip mask and origin in the GC first.  And
951      * make sure we restore the GC because it may be shared.
952      */
953     tsPtr->theta = theta;
954     bitmap = Blt_CreateTextBitmap(tkwin, textPtr, tsPtr, &width, &height);
955     if (bitmap == None) {
956 	return;
957     }
958     Blt_TranslateAnchor(x, y, width, height, tsPtr->anchor, &x, &y);
959 #ifdef notdef
960     theta = FMOD(theta, (double)90.0);
961 #endif
962     XSetClipMask(display, tsPtr->gc, bitmap);
963 
964     if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
965 	TkBorder *borderPtr = (TkBorder *) tsPtr->border;
966 	XColor *color1, *color2;
967 
968 	color1 = borderPtr->lightColorPtr, color2 = borderPtr->darkColorPtr;
969 	if (tsPtr->state & STATE_EMPHASIS) {
970 	    XColor *hold;
971 
972 	    hold = color1, color1 = color2, color2 = hold;
973 	}
974 	if (color1 != NULL) {
975 	    XSetForeground(display, tsPtr->gc, color1->pixel);
976 	}
977 	XSetClipOrigin(display, tsPtr->gc, x + 1, y + 1);
978 	XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width,
979 		height, x + 1, y + 1, 1);
980 	if (color2 != NULL) {
981 	    XSetForeground(display, tsPtr->gc, color2->pixel);
982 	}
983 	XSetClipOrigin(display, tsPtr->gc, x, y);
984 	XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width,
985 		height, x, y, 1);
986 	XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
987     } else {
988 	if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
989 	    XSetClipOrigin(display, tsPtr->gc, x + tsPtr->shadow.offset,
990 			   y + tsPtr->shadow.offset);
991 	    XSetForeground(display, tsPtr->gc, tsPtr->shadow.color->pixel);
992 	    XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width,
993 		height, x + tsPtr->shadow.offset, y + tsPtr->shadow.offset, 1);
994 	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
995 	}
996 	if (active) {
997 	    XSetForeground(display, tsPtr->gc, tsPtr->activeColor->pixel);
998 	}
999 	XSetClipOrigin(display, tsPtr->gc, x, y);
1000 	XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width, height,
1001 		x, y, 1);
1002 	if (active) {
1003 	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
1004 	}
1005     }
1006     XSetClipMask(display, tsPtr->gc, None);
1007     Tk_FreePixmap(display, bitmap);
1008 }
1009 
1010 void
Blt_DrawText2(tkwin,drawable,string,tsPtr,x,y,areaPtr)1011 Blt_DrawText2(tkwin, drawable, string, tsPtr, x, y, areaPtr)
1012     Tk_Window tkwin;
1013     Drawable drawable;
1014     char string[];
1015     TextStyle *tsPtr;		/* Text attribute information */
1016     int x, y;			/* Window coordinates to draw text */
1017     Dim2D *areaPtr;
1018 {
1019     TextLayout *textPtr;
1020     int width, height;
1021     double theta;
1022 
1023     if ((string == NULL) || (*string == '\0')) {
1024 	return;			/* Empty string, do nothing */
1025     }
1026     textPtr = Blt_GetTextLayout(string, tsPtr);
1027     Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y);
1028     theta = FMOD(tsPtr->theta, (double)360.0);
1029     if (theta < 0.0) {
1030 	theta += 360.0;
1031     }
1032     width = textPtr->width;
1033     height = textPtr->height;
1034     if (theta != 0.0) {
1035 	double rotWidth, rotHeight;
1036 
1037 	Blt_GetBoundingBox(width, height, theta, &rotWidth, &rotHeight,
1038 	   (Point2D *)NULL);
1039 	width = ROUND(rotWidth);
1040 	height = ROUND(rotHeight);
1041     }
1042     areaPtr->width = width;
1043     areaPtr->height = height;
1044     Blt_Free(textPtr);
1045 }
1046 
1047 void
Blt_DrawText(tkwin,drawable,string,tsPtr,x,y)1048 Blt_DrawText(tkwin, drawable, string, tsPtr, x, y)
1049     Tk_Window tkwin;
1050     Drawable drawable;
1051     char string[];
1052     TextStyle *tsPtr;		/* Text attribute information */
1053     int x, y;			/* Window coordinates to draw text */
1054 {
1055     TextLayout *textPtr;
1056 
1057     if ((string == NULL) || (*string == '\0')) {
1058 	return;			/* Empty string, do nothing */
1059     }
1060     textPtr = Blt_GetTextLayout(string, tsPtr);
1061     Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y);
1062     tsPtr->width = textPtr->width;
1063     tsPtr->height = textPtr->height;
1064     Blt_Free(textPtr);
1065 }
1066 
1067 GC
Blt_GetBitmapGC(tkwin)1068 Blt_GetBitmapGC(tkwin)
1069     Tk_Window tkwin;
1070 {
1071     int isNew;
1072     GC gc;
1073     Display *display;
1074     Blt_HashEntry *hPtr;
1075 
1076     if (!initialized) {
1077 	Blt_InitHashTable(&bitmapGCTable, BLT_ONE_WORD_KEYS);
1078 	initialized = TRUE;
1079     }
1080     display = Tk_Display(tkwin);
1081 
1082     /* Changed "(char *)display" to "Tk_PathName(tkwin)"  to create a   */
1083     /* unique key in the bitmapGCTable hash table.                      */
1084     hPtr = Blt_CreateHashEntry(&bitmapGCTable, Tk_PathName(tkwin), &isNew);
1085 
1086     if (isNew) {
1087 	Pixmap bitmap;
1088 	XGCValues gcValues;
1089 	unsigned long gcMask;
1090 	Window root;
1091 
1092 	root = RootWindow(display, Tk_ScreenNumber(tkwin));
1093 	bitmap = Tk_GetPixmap(display, root, 1, 1, 1);
1094 	gcValues.foreground = gcValues.background = 0;
1095 	gcMask = (GCForeground | GCBackground);
1096 	gc = Blt_GetPrivateGCFromDrawable(display, bitmap, gcMask, &gcValues);
1097 	Tk_FreePixmap(display, bitmap);
1098 	Blt_SetHashValue(hPtr, gc);
1099     } else {
1100 	gc = (GC)Blt_GetHashValue(hPtr);
1101     }
1102     return gc;
1103 }
1104 
1105 void
Blt_ResetTextStyle(tkwin,tsPtr)1106 Blt_ResetTextStyle(tkwin, tsPtr)
1107     Tk_Window tkwin;
1108     TextStyle *tsPtr;
1109 {
1110     GC newGC;
1111     XGCValues gcValues;
1112     unsigned long gcMask;
1113 
1114     gcMask = GCFont;
1115     gcValues.font = Tk_FontId(tsPtr->font);
1116     if (tsPtr->color != NULL) {
1117 	gcMask |= GCForeground;
1118 	gcValues.foreground = tsPtr->color->pixel;
1119     }
1120     newGC = Tk_GetGC(tkwin, gcMask, &gcValues);
1121     if (tsPtr->gc != NULL) {
1122 	Tk_FreeGC(Tk_Display(tkwin), tsPtr->gc);
1123     }
1124     tsPtr->gc = newGC;
1125 }
1126 
1127 void
Blt_FreeTextStyle(display,tsPtr)1128 Blt_FreeTextStyle(display, tsPtr)
1129     Display *display;
1130     TextStyle *tsPtr;
1131 {
1132     if (tsPtr->gc != NULL) {
1133 	Tk_FreeGC(display, tsPtr->gc);
1134     }
1135 }
1136 
1137 void
Blt_DeleteAxisLabelsGC(tkwin)1138 Blt_DeleteAxisLabelsGC(tkwin)
1139     Tk_Window tkwin;
1140 {
1141     /* int isNew; */
1142     Display *display;
1143     GC gc;
1144     Blt_HashEntry *hPtr;
1145 
1146     /* This routine is called by GraphEventProc when a        */
1147     /* "destroy" command is issued to destroy a graph window. */
1148 
1149     /* Initialize hashtable "bitmapGCTable" if not done already.      */
1150     /* (Note: Aborts in "Blt_FindHashEntry" if table does not exist.) */
1151     if (!initialized) {
1152 	Blt_InitHashTable(&bitmapGCTable, BLT_ONE_WORD_KEYS);
1153 	initialized = TRUE;
1154     }
1155 
1156     /* Search for entry in table. */
1157     hPtr = Blt_FindHashEntry(&bitmapGCTable, Tk_PathName(tkwin));
1158 
1159     /* If entry found, free GC resource and delete entry in hash table. */
1160     if (hPtr) {
1161        display = Tk_Display(tkwin);
1162        gc = (GC)Blt_GetHashValue(hPtr);
1163        XFreeGC(display,gc);
1164        /* This needs to be done when the toplevel window is destroyed  */
1165        /* to prevent "X-Window err: BadMatch 70 (X_PolyFillRectangle)" */
1166        /* if the same toplevel window is displayed on a different      */
1167        /* screen of the same workstation.                              */
1168        Blt_DeleteHashEntry(&bitmapGCTable, hPtr);
1169     }
1170 }
1171