diff options
Diffstat (limited to 'unix/tkUnixFont.c')
-rw-r--r-- | unix/tkUnixFont.c | 283 |
1 files changed, 280 insertions, 3 deletions
diff --git a/unix/tkUnixFont.c b/unix/tkUnixFont.c index 405b19c..b069cd9 100644 --- a/unix/tkUnixFont.c +++ b/unix/tkUnixFont.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkUnixFont.c,v 1.38 2008/11/05 22:20:59 nijtmans Exp $ + * RCS: @(#) $Id: tkUnixFont.c,v 1.39 2008/11/22 18:08:51 dkf Exp $ */ #include "tkUnixInt.h" @@ -139,9 +139,9 @@ typedef struct UnixFont { */ typedef struct EncodingAlias { - const char *realName; /* The real name of the encoding to load if + const char *realName; /* The real name of the encoding to load if * the provided name matched the pattern. */ - const char *aliasPattern; /* Pattern for encoding name, of the form that + const char *aliasPattern; /* Pattern for encoding name, of the form that * is acceptable to Tcl_StringMatch. */ } EncodingAlias; @@ -3018,6 +3018,283 @@ GetEncodingAlias( return name; } +static inline XImage * +GetImageOfText( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + Tk_Font tkfont, /* Font in which characters will be drawn. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are not + * stripped out, they will be displayed as + * regular printing characters. */ + int numBytes, /* Number of bytes in string. */ + int *realWidthPtr, int *realHeightPtr) +{ + int width, height; + TkFont *fontPtr = (TkFont *) tkfont; + Pixmap bitmap; + GC bitmapGC; + XGCValues values; + XImage *image; + + (void) Tk_MeasureChars(tkfont, source, numBytes, -1, 0, &width); + height = fontPtr->fm.ascent + fontPtr->fm.descent; + + bitmap = Tk_GetPixmap(display, drawable, width, height, 1); + values.graphics_exposures = False; + values.foreground = BlackPixel(display, DefaultScreen(display)); + bitmapGC = XCreateGC(display, bitmap, GCGraphicsExposures|GCForeground, + &values); + XFillRectangle(display, bitmap, bitmapGC, 0, 0, width, height); + + values.font = Tk_FontId(tkfont); + values.foreground = WhitePixel(display, DefaultScreen(display)); + values.background = BlackPixel(display, DefaultScreen(display)); + XChangeGC(display, bitmapGC, GCFont|GCForeground|GCBackground, &values); + Tk_DrawChars(display, bitmap, bitmapGC, tkfont, source, numBytes, 0, + fontPtr->fm.ascent); + XFreeGC(display, bitmapGC); + + image = XGetImage(display, bitmap, 0, 0, width, height, AllPlanes, + ZPixmap); + Tk_FreePixmap(display, bitmap); + + *realWidthPtr = width; + *realHeightPtr = height; + return image; +} + +static inline XImage * +InitDestImage( + Display *display, + Drawable drawable, + int width, + int height, + Pixmap *bitmapPtr) +{ + Pixmap bitmap; + XImage *image; + GC bitmapGC; + XGCValues values; + + bitmap = Tk_GetPixmap(display, drawable, width, height, 1); + values.graphics_exposures = False; + values.foreground = BlackPixel(display, DefaultScreen(display)); + bitmapGC = XCreateGC(display, bitmap, GCGraphicsExposures|GCForeground, + &values); + XFillRectangle(display, bitmap, bitmapGC, 0, 0, width, height); + XFreeGC(display, bitmapGC); + + image = XGetImage(display, bitmap, 0, 0, width, height, AllPlanes, + ZPixmap); + *bitmapPtr = bitmap; + return image; +} + +void +TkpDrawAngledChars( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context for drawing characters. */ + Tk_Font tkfont, /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are not + * stripped out, they will be displayed as + * regular printing characters. */ + int numBytes, /* Number of bytes in string. */ + double x, double y, + double angle) +{ + if (angle == 0.0) { + Tk_DrawChars(display, drawable, gc, tkfont, source, numBytes, x, y); + } else { + double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0); + int bufHeight, bufWidth, srcWidth, srcHeight, i, j, dx, dy; + Pixmap buf; + XImage *srcImage = GetImageOfText(display, drawable, tkfont, source, + numBytes, &srcWidth, &srcHeight); + XImage *dstImage; + enum {Q0=1,R1,Q1,R2,Q2,R3,Q3} quadrant; + GC bwgc, cpgc; + XGCValues values; + int ascent = ((TkFont *) tkfont)->fm.ascent; + + /* + * First, work out what quadrant we are operating in. We also handle + * the rectilinear rotations as special cases. Conceptually, there's + * also R0 (angle == 0.0) but that has been already handled as a + * special case above. + * + * R1 + * Q1 | Q0 + * | + * R2 ----+---- R0 + * | + * Q2 | Q3 + * R3 + */ + + if (angle < 90.0) { + quadrant = Q0; + } else if (angle == 90.0) { + quadrant = R1; + } else if (angle < 180.0) { + quadrant = Q1; + } else if (angle == 180.0) { + quadrant = R2; + } else if (angle < 270.0) { + quadrant = Q2; + } else if (angle == 270.0) { + quadrant = R3; + } else { + quadrant = Q3; + } + + if (srcImage == NULL) { + return; + } + bufWidth = srcWidth*fabs(cosA) + srcHeight*fabs(sinA); + bufHeight = srcHeight*fabs(cosA) + srcWidth*fabs(sinA); + dstImage = InitDestImage(display, drawable, bufWidth,bufHeight, &buf); + if (dstImage == NULL) { + Tk_FreePixmap(display, buf); + XDestroyImage(srcImage); + return; + } + + /* + * Do the rotation, setting or resetting pixels in the destination + * image dependent on whether the corresponding pixel (after rotation + * to source image space) is set. + */ + + for (i=0 ; i<srcWidth ; i++) { + for (j=0 ; j<srcHeight ; j++) { + switch (quadrant) { + case Q0: + dx = floor(i*cosA + j*sinA + 0.5); + dy = floor(j*cosA + (srcWidth - i)*sinA + 0.5); + break; + case R1: + dx = j; + dy = srcWidth - i; + break; + case Q1: + dx = floor((i - srcWidth)*cosA + j*sinA + 0.5); + dy = floor((srcWidth-i)*sinA + (j-srcHeight)*cosA + 0.5); + break; + case R2: + dx = srcWidth - i; + dy = srcHeight - j; + break; + case Q2: + dx = floor((i-srcWidth)*cosA + (j-srcHeight)*sinA + 0.5); + dy = floor((j - srcHeight)*cosA - i*sinA + 0.5); + break; + case R3: + dx = srcHeight - j; + dy = i; + break; + default: + dx = floor(i*cosA + (j - srcHeight)*sinA + 0.5); + dy = floor(j*cosA - i*sinA + 0.5); + } + + if (dx < 0 || dy < 0 || dx >= bufWidth || dy >= bufHeight) { + continue; + } + XPutPixel(dstImage, dx, dy, + XGetPixel(dstImage,dx,dy) | XGetPixel(srcImage,i,j)); + } + } + XDestroyImage(srcImage); + + /* + * Schlep the data back to the Xserver. + */ + + values.function = GXcopy; + values.foreground = WhitePixel(display, DefaultScreen(display)); + values.background = BlackPixel(display, DefaultScreen(display)); + bwgc = XCreateGC(display, buf, GCFunction|GCForeground|GCBackground, + &values); + XPutImage(display, buf, bwgc, dstImage, 0,0, 0,0, bufWidth,bufHeight); + XFreeGC(display, bwgc); + XDestroyImage(dstImage); + + /* + * Calculate where we want to draw the text. + */ + + switch (quadrant) { + case Q0: + dx = x; + dy = y - srcWidth*sinA; + break; + case R1: + dx = x; + dy = y - srcWidth; + break; + case Q1: + dx = x + srcWidth*cosA; + dy = y + srcHeight*cosA - srcWidth*sinA; + break; + case R2: + dx = x - srcWidth; + dy = y - srcHeight; + break; + case Q2: + dx = x + srcWidth*cosA + srcHeight*sinA; + dy = y + srcHeight*cosA; + break; + case R3: + dx = x - srcHeight; + dy = y; + break; + default: + dx = x + srcHeight*sinA; + dy = y; + } + + /* + * Apply a correction to deal with the fact that we aren't told to + * draw from our top-left corner but rather from the left-end of our + * baseline. + */ + + dx -= ascent*sinA; + dy -= ascent*cosA; + + /* + * Transfer the text to the screen. This is done by using it as a mask + * and then drawing through that mask with the original drawing color. + */ + + values.function = GXcopy; + values.fill_style = FillSolid; + values.clip_mask = buf; + values.clip_x_origin = dx; + values.clip_y_origin = dy; + cpgc = XCreateGC(display, drawable, + GCFunction|GCFillStyle|GCClipMask|GCClipXOrigin|GCClipYOrigin, + &values); + XCopyGC(display, gc, GCForeground, cpgc); + XFillRectangle(display, drawable, cpgc, dx, dy, bufWidth, + bufHeight); + XFreeGC(display, cpgc); + + Tk_FreePixmap(display, buf); + return; + } +} + /* * Local Variables: * mode: c |