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