diff options
author | dkf <donal.k.fellows@manchester.ac.uk> | 2008-11-22 18:08:51 (GMT) |
---|---|---|
committer | dkf <donal.k.fellows@manchester.ac.uk> | 2008-11-22 18:08:51 (GMT) |
commit | 08d210474dcf15c239160ca885bd7799ac618787 (patch) | |
tree | a79e10925b1fe14d042ef56ea012b2e1e56407de /generic | |
parent | 76c04ee36744e2a988a33f9d8ccb9d3b7e2359c2 (diff) | |
download | tk-08d210474dcf15c239160ca885bd7799ac618787.zip tk-08d210474dcf15c239160ca885bd7799ac618787.tar.gz tk-08d210474dcf15c239160ca885bd7799ac618787.tar.bz2 |
TIP#119 implementation. [Patch 1611359]
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tkCanvText.c | 298 | ||||
-rw-r--r-- | generic/tkFont.c | 388 | ||||
-rw-r--r-- | generic/tkInt.h | 28 |
3 files changed, 612 insertions, 102 deletions
diff --git a/generic/tkCanvText.c b/generic/tkCanvText.c index efa2b70..474ed25 100644 --- a/generic/tkCanvText.c +++ b/generic/tkCanvText.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: tkCanvText.c,v 1.32 2008/11/09 20:51:28 nijtmans Exp $ + * RCS: @(#) $Id: tkCanvText.c,v 1.33 2008/11/22 18:08:51 dkf Exp $ */ #include <stdio.h> @@ -57,6 +57,8 @@ typedef struct TextItem { * means no word-wrap. */ int underline; /* Index of character to put underline beneath * or -1 for no underlining. */ + double angle; /* What angle, in degrees, to draw the text + * at. */ /* * Fields whose values are derived from the current values of the @@ -66,18 +68,18 @@ typedef struct TextItem { int numChars; /* Length of text in characters. */ int numBytes; /* Length of text in bytes. */ Tk_TextLayout textLayout; /* Cached text layout information. */ - int leftEdge; /* Pixel location of the left edge of the text - * item; where the left border of the text - * layout is drawn. */ - int rightEdge; /* Pixel just to right of right edge of area - * of text item. Used for selecting up to end - * of line. */ + int actualWidth; /* Width of text as computed. Used to make + * selections of wrapped text display + * right. */ + double drawOrigin[2]; /* Where we start drawing from. */ GC gc; /* Graphics context for drawing text. */ GC selTextGC; /* Graphics context for selected text. */ GC cursorOffGC; /* If not None, this gives a graphics context * to use to draw the insertion cursor when * it's off. Used if the selection and * insertion cursor colors are the same. */ + double sine; /* Sine of angle field. */ + double cosine; /* Cosine of angle field. */ } TextItem; /* @@ -101,6 +103,8 @@ static Tk_ConfigSpec configSpecs[] = { NULL, Tk_Offset(TextItem, activeStipple), TK_CONFIG_NULL_OK}, {TK_CONFIG_ANCHOR, "-anchor", NULL, NULL, "center", Tk_Offset(TextItem, anchor), TK_CONFIG_DONT_SET_DEFAULT}, + {TK_CONFIG_DOUBLE, "-angle", NULL, NULL, + "0.0", Tk_Offset(TextItem, angle), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_COLOR, "-disabledfill", NULL, NULL, NULL, Tk_Offset(TextItem, disabledColor), TK_CONFIG_NULL_OK}, {TK_CONFIG_BITMAP, "-disabledstipple", NULL, NULL, @@ -199,6 +203,8 @@ Tk_ItemType tkTextType = { TextDeleteChars, /* dTextProc */ NULL, /* nextPtr */ }; + +#define ROUND(d) ((int) floor((d) + 0.5)) /* *-------------------------------------------------------------- @@ -258,15 +264,18 @@ CreateText( textPtr->text = NULL; textPtr->width = 0; textPtr->underline = -1; + textPtr->angle = 0.0; textPtr->numChars = 0; textPtr->numBytes = 0; textPtr->textLayout = NULL; - textPtr->leftEdge = 0; - textPtr->rightEdge = 0; + textPtr->actualWidth = 0; + textPtr->drawOrigin[0] = textPtr->drawOrigin[1] = 0.0; textPtr->gc = None; textPtr->selTextGC = None; textPtr->cursorOffGC = None; + textPtr->sine = 0.0; + textPtr->cosine = 1.0; /* * Process the arguments to fill in the item record. Only 1 (list) or 2 (x @@ -325,14 +334,14 @@ TextCoords( if (objc == 0) { Tcl_Obj *obj = Tcl_NewObj(); - Tcl_Obj *subobj = Tcl_NewDoubleObj(textPtr->x); + Tcl_ListObjAppendElement(interp, obj, subobj); subobj = Tcl_NewDoubleObj(textPtr->y); Tcl_ListObjAppendElement(interp, obj, subobj); Tcl_SetObjResult(interp, obj); } else if (objc < 3) { - if (objc==1) { + if (objc == 1) { if (Tcl_ListObjGetElements(interp, objv[0], &objc, (Tcl_Obj ***) &objv) != TCL_OK) { return TCL_ERROR; @@ -415,24 +424,24 @@ ConfigureText( itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT; } - if(state == TK_STATE_NULL) { + if (state == TK_STATE_NULL) { state = Canvas(canvas)->canvas_state; } color = textPtr->color; stipple = textPtr->stipple; if (Canvas(canvas)->currentItemPtr == itemPtr) { - if (textPtr->activeColor!=NULL) { + if (textPtr->activeColor != NULL) { color = textPtr->activeColor; } - if (textPtr->activeStipple!=None) { + if (textPtr->activeStipple != None) { stipple = textPtr->activeStipple; } - } else if (state==TK_STATE_DISABLED) { - if (textPtr->disabledColor!=NULL) { + } else if (state == TK_STATE_DISABLED) { + if (textPtr->disabledColor != NULL) { color = textPtr->disabledColor; } - if (textPtr->disabledStipple!=None) { + if (textPtr->disabledStipple != None) { stipple = textPtr->disabledStipple; } } @@ -513,6 +522,22 @@ ConfigureText( textPtr->insertPos = textPtr->numChars; } + /* + * Restrict so that 0.0 <= angle < 360.0, and then recompute the cached + * sine and cosine of the angle. Note that fmod() can produce negative + * results, and we try to avoid negative zero as well. + */ + + textPtr->angle = fmod(textPtr->angle, 360.0); + if (textPtr->angle < 0.0) { + textPtr->angle += 360.0; + } + if (textPtr->angle == 0.0) { + textPtr->angle = 0.0; + } + textPtr->sine = sin(textPtr->angle * PI/180.0); + textPtr->cosine = cos(textPtr->angle * PI/180.0); + ComputeTextBbox(canvas, textPtr); return TCL_OK; } @@ -603,10 +628,11 @@ ComputeTextBbox( TextItem *textPtr) /* Item whose bbox is to be recomputed. */ { Tk_CanvasTextInfo *textInfoPtr; - int leftX, topY, width, height, fudge; + int leftX, topY, width, height, fudge, i; Tk_State state = textPtr->header.state; + double x[4], y[4], dx[4], dy[4], sinA, cosA, tmp; - if(state == TK_STATE_NULL) { + if (state == TK_STATE_NULL) { state = Canvas(canvas)->canvas_state; } @@ -624,8 +650,11 @@ ComputeTextBbox( * bounding box for the text item. */ - leftX = (int) floor(textPtr->x + 0.5); - topY = (int) floor(textPtr->y + 0.5); + leftX = ROUND(textPtr->x); + topY = ROUND(textPtr->y); + for (i=0 ; i<4 ; i++) { + dx[i] = dy[i] = 0.0; + } switch (textPtr->anchor) { case TK_ANCHOR_NW: case TK_ANCHOR_N: @@ -636,12 +665,18 @@ ComputeTextBbox( case TK_ANCHOR_CENTER: case TK_ANCHOR_E: topY -= height / 2; + for (i=0 ; i<4 ; i++) { + dy[i] = -height / 2; + } break; case TK_ANCHOR_SW: case TK_ANCHOR_S: case TK_ANCHOR_SE: topY -= height; + for (i=0 ; i<4 ; i++) { + dy[i] = -height; + } break; } switch (textPtr->anchor) { @@ -654,17 +689,27 @@ ComputeTextBbox( case TK_ANCHOR_CENTER: case TK_ANCHOR_S: leftX -= width / 2; + for (i=0 ; i<4 ; i++) { + dx[i] = -width / 2; + } break; case TK_ANCHOR_NE: case TK_ANCHOR_E: case TK_ANCHOR_SE: leftX -= width; + for (i=0 ; i<4 ; i++) { + dx[i] = -width; + } break; } - textPtr->leftEdge = leftX; - textPtr->rightEdge = leftX + width; + textPtr->actualWidth = width; + + sinA = textPtr->sine; + cosA = textPtr->cosine; + textPtr->drawOrigin[0] = textPtr->x + dx[0]*cosA + dy[0]*sinA; + textPtr->drawOrigin[1] = textPtr->y + dy[0]*cosA - dx[0]*sinA; /* * Last of all, update the bounding box for the item. The item's bounding @@ -677,10 +722,50 @@ ComputeTextBbox( if (textInfoPtr->selBorderWidth > fudge) { fudge = textInfoPtr->selBorderWidth; } - textPtr->header.x1 = leftX - fudge; - textPtr->header.y1 = topY; - textPtr->header.x2 = leftX + width + fudge; - textPtr->header.y2 = topY + height; + + /* + * Apply the rotation before computing the bounding box. + */ + + dx[0] -= fudge; + dx[1] += width + fudge; + dx[2] += width + fudge; + dy[2] += height; + dx[3] -= fudge; + dy[3] += height; + for (i=0 ; i<4 ; i++) { + x[i] = textPtr->x + dx[i] * cosA + dy[i] * sinA; + y[i] = textPtr->y + dy[i] * cosA - dx[i] * sinA; + } + + /* + * Convert to a rectilinear bounding box. + */ + + for (i=1,tmp=x[0] ; i<4 ; i++) { + if (x[i] < tmp) { + tmp = x[i]; + } + } + textPtr->header.x1 = ROUND(tmp); + for (i=1,tmp=y[0] ; i<4 ; i++) { + if (y[i] < tmp) { + tmp = y[i]; + } + } + textPtr->header.y1 = ROUND(tmp); + for (i=1,tmp=x[0] ; i<4 ; i++) { + if (x[i] > tmp) { + tmp = x[i]; + } + } + textPtr->header.x2 = ROUND(tmp); + for (i=1,tmp=y[0] ; i<4 ; i++) { + if (y[i] > tmp) { + tmp = y[i]; + } + } + textPtr->header.y2 = ROUND(tmp); } /* @@ -720,16 +805,16 @@ DisplayCanvText( textPtr = (TextItem *) itemPtr; textInfoPtr = textPtr->textInfoPtr; - if(state == TK_STATE_NULL) { + if (state == TK_STATE_NULL) { state = Canvas(canvas)->canvas_state; } stipple = textPtr->stipple; if (Canvas(canvas)->currentItemPtr == itemPtr) { - if (textPtr->activeStipple!=None) { + if (textPtr->activeStipple != None) { stipple = textPtr->activeStipple; } - } else if (state==TK_STATE_DISABLED) { - if (textPtr->disabledStipple!=None) { + } else if (state == TK_STATE_DISABLED) { + if (textPtr->disabledStipple != None) { stipple = textPtr->disabledStipple; } } @@ -750,6 +835,8 @@ DisplayCanvText( selFirstChar = -1; selLastChar = 0; /* lint. */ + Tk_CanvasDrawableCoords(canvas, textPtr->drawOrigin[0], + textPtr->drawOrigin[1], &drawableX, &drawableY); if (textInfoPtr->selItemPtr == itemPtr) { selFirstChar = textInfoPtr->selectFirst; @@ -780,20 +867,30 @@ DisplayCanvText( x = xFirst; height = hFirst; for (y = yFirst ; y <= yLast; y += height) { + int dx1, dy1, dx2, dy2; + double s = textPtr->sine, c = textPtr->cosine; + XPoint points[4]; + if (y == yLast) { width = xLast + wLast - x; } else { - width = textPtr->rightEdge - textPtr->leftEdge - x; + width = textPtr->actualWidth - x; } - Tk_CanvasDrawableCoords(canvas, - (double) (textPtr->leftEdge + x - - textInfoPtr->selBorderWidth), - (double) (textPtr->header.y1 + y), - &drawableX, &drawableY); - Tk_Fill3DRectangle(Tk_CanvasTkwin(canvas), drawable, - textInfoPtr->selBorder, drawableX, drawableY, - width + 2 * textInfoPtr->selBorderWidth, - height, textInfoPtr->selBorderWidth, TK_RELIEF_RAISED); + dx1 = x - textInfoPtr->selBorderWidth; + dy1 = y; + dx2 = width + 2 * textInfoPtr->selBorderWidth; + dy2 = height; + points[0].x = drawableX + dx1*c + dy1*s; + points[0].y = drawableY + dy1*c - dx1*s; + points[1].x = drawableX + (dx1+dx2)*c + dy1*s; + points[1].y = drawableY + dy1*c - (dx1+dx2)*s; + points[2].x = drawableX + (dx1+dx2)*c + (dy1+dy2)*s; + points[2].y = drawableY + (dy1+dy2)*c - (dx1+dx2)*s; + points[3].x = drawableX + dx1*c + (dy1+dy2)*s; + points[3].y = drawableY + (dy1+dy2)*c - dx1*s; + Tk_Fill3DPolygon(Tk_CanvasTkwin(canvas), drawable, + textInfoPtr->selBorder, points, 4, + textInfoPtr->selBorderWidth, TK_RELIEF_RAISED); x = 0; } } @@ -811,18 +908,28 @@ DisplayCanvText( if ((textInfoPtr->focusItemPtr == itemPtr) && (textInfoPtr->gotFocus)) { if (Tk_CharBbox(textPtr->textLayout, textPtr->insertPos, &x, &y, NULL, &height)) { - Tk_CanvasDrawableCoords(canvas, - (double) (textPtr->leftEdge + x - - (textInfoPtr->insertWidth / 2)), - (double) (textPtr->header.y1 + y), - &drawableX, &drawableY); - Tk_SetCaretPos(Tk_CanvasTkwin(canvas), drawableX, drawableY, + int dx1, dy1, dx2, dy2; + double s = textPtr->sine, c = textPtr->cosine; + XPoint points[4]; + + dx1 = x - (textInfoPtr->insertWidth / 2); + dy1 = y; + dx2 = textInfoPtr->insertWidth; + dy2 = height; + points[0].x = drawableX + dx1*c + dy1*s; + points[0].y = drawableY + dy1*c - dx1*s; + points[1].x = drawableX + (dx1+dx2)*c + dy1*s; + points[1].y = drawableY + dy1*c - (dx1+dx2)*s; + points[2].x = drawableX + (dx1+dx2)*c + (dy1+dy2)*s; + points[2].y = drawableY + (dy1+dy2)*c - (dx1+dx2)*s; + points[3].x = drawableX + dx1*c + (dy1+dy2)*s; + points[3].y = drawableY + (dy1+dy2)*c - dx1*s; + + Tk_SetCaretPos(Tk_CanvasTkwin(canvas), points[0].x, points[0].y, height); if (textInfoPtr->cursorOn) { - Tk_Fill3DRectangle(Tk_CanvasTkwin(canvas), drawable, - textInfoPtr->insertBorder, - drawableX, drawableY, - textInfoPtr->insertWidth, height, + Tk_Fill3DPolygon(Tk_CanvasTkwin(canvas), drawable, + textInfoPtr->insertBorder, points, 4, textInfoPtr->insertBorderWidth, TK_RELIEF_RAISED); } else if (textPtr->cursorOffGC != None) { /* @@ -832,10 +939,8 @@ DisplayCanvText( * where both may be drawn in the same color. */ - XFillRectangle(display, drawable, textPtr->cursorOffGC, - drawableX, drawableY, - (unsigned) textInfoPtr->insertWidth, - (unsigned) height); + XFillPolygon(display, drawable, textPtr->cursorOffGC, + points, 4, Convex, CoordModeOrigin); } } } @@ -850,23 +955,24 @@ DisplayCanvText( * anti-aliasing colors would blend together. */ - Tk_CanvasDrawableCoords(canvas, (double) textPtr->leftEdge, - (double) textPtr->header.y1, &drawableX, &drawableY); - if ((selFirstChar >= 0) && (textPtr->selTextGC != textPtr->gc)) { - Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout, - drawableX, drawableY, 0, selFirstChar); - Tk_DrawTextLayout(display, drawable, textPtr->selTextGC, - textPtr->textLayout, drawableX, drawableY, selFirstChar, - selLastChar + 1); - Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout, - drawableX, drawableY, selLastChar + 1, -1); + TkDrawAngledTextLayout(display, drawable, textPtr->gc, + textPtr->textLayout, drawableX, drawableY, textPtr->angle, + 0, selFirstChar); + TkDrawAngledTextLayout(display, drawable, textPtr->selTextGC, + textPtr->textLayout, drawableX, drawableY, textPtr->angle, + selFirstChar, selLastChar + 1); + TkDrawAngledTextLayout(display, drawable, textPtr->gc, + textPtr->textLayout, drawableX, drawableY, textPtr->angle, + selLastChar + 1, -1); } else { - Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout, - drawableX, drawableY, 0, -1); + TkDrawAngledTextLayout(display, drawable, textPtr->gc, + textPtr->textLayout, drawableX, drawableY, textPtr->angle, + 0, -1); } - Tk_UnderlineTextLayout(display, drawable, textPtr->gc, textPtr->textLayout, - drawableX, drawableY, textPtr->underline); + TkUnderlineAngledTextLayout(display, drawable, textPtr->gc, + textPtr->textLayout, drawableX, drawableY, textPtr->angle, + textPtr->underline); if (stipple != None) { XSetTSOrigin(display, textPtr->gc, 0, 0); @@ -1076,15 +1182,17 @@ TextToPoint( { TextItem *textPtr; Tk_State state = itemPtr->state; - double value; + double value, px, py; if (state == TK_STATE_NULL) { state = Canvas(canvas)->canvas_state; } textPtr = (TextItem *) itemPtr; + px = pointPtr[0] - textPtr->drawOrigin[0]; + py = pointPtr[1] - textPtr->drawOrigin[1]; value = (double) Tk_DistanceToTextLayout(textPtr->textLayout, - (int) pointPtr[0] - textPtr->leftEdge, - (int) pointPtr[1] - textPtr->header.y1); + (int) px*textPtr->cosine - py*textPtr->sine, + (int) py*textPtr->cosine + px*textPtr->sine); if ((state == TK_STATE_HIDDEN) || (textPtr->color == NULL) || (textPtr->text == NULL) || (*textPtr->text == 0)) { @@ -1128,11 +1236,12 @@ TextToArea( } textPtr = (TextItem *) itemPtr; - return Tk_IntersectTextLayout(textPtr->textLayout, - (int) (rectPtr[0] + 0.5) - textPtr->leftEdge, - (int) (rectPtr[1] + 0.5) - textPtr->header.y1, + return TkIntersectAngledTextLayout(textPtr->textLayout, + (int) (rectPtr[0] + 0.5) - textPtr->drawOrigin[0], + (int) (rectPtr[1] + 0.5) - textPtr->drawOrigin[1], (int) (rectPtr[2] - rectPtr[0] + 0.5), - (int) (rectPtr[3] - rectPtr[1] + 0.5)); + (int) (rectPtr[3] - rectPtr[1] + 0.5), + textPtr->angle); } /* @@ -1261,7 +1370,7 @@ GetTextIndex( *indexPtr = textInfoPtr->selectLast; } else if (c == '@') { int x, y; - double tmp; + double tmp, c = textPtr->cosine, s = textPtr->sine; char *end, *p; p = string+1; @@ -1276,9 +1385,9 @@ GetTextIndex( goto badIndex; } y = (int) ((tmp < 0) ? tmp - 0.5 : tmp + 0.5); - *indexPtr = Tk_PointToChar(textPtr->textLayout, - x + canvasPtr->scrollX1 - textPtr->leftEdge, - y + canvasPtr->scrollY1 - textPtr->header.y1); + x += canvasPtr->scrollX1 - textPtr->drawOrigin[0]; + y += canvasPtr->scrollY1 - textPtr->drawOrigin[1]; + *indexPtr = Tk_PointToChar(textPtr->textLayout, x*c-y*s, y*c+x*s); } else if (Tcl_GetIntFromObj(NULL, obj, indexPtr) == TCL_OK) { if (*indexPtr < 0) { *indexPtr = 0; @@ -1420,10 +1529,9 @@ TextToPostscript( * being created. */ { TextItem *textPtr = (TextItem *) itemPtr; - int x, y; + double x, y; Tk_FontMetrics fm; const char *justify; - char buffer[500]; XColor *color; Pixmap stipple; Tk_State state = itemPtr->state; @@ -1437,17 +1545,17 @@ TextToPostscript( textPtr->text == NULL || *textPtr->text == 0) { return TCL_OK; } else if (Canvas(canvas)->currentItemPtr == itemPtr) { - if (textPtr->activeColor!=NULL) { + if (textPtr->activeColor != NULL) { color = textPtr->activeColor; } - if (textPtr->activeStipple!=None) { + if (textPtr->activeStipple != None) { stipple = textPtr->activeStipple; } - } else if (state==TK_STATE_DISABLED) { - if (textPtr->disabledColor!=NULL) { + } else if (state == TK_STATE_DISABLED) { + if (textPtr->disabledColor != NULL) { color = textPtr->disabledColor; } - if (textPtr->disabledStipple!=None) { + if (textPtr->disabledStipple != None) { stipple = textPtr->disabledStipple; } } @@ -1467,9 +1575,8 @@ TextToPostscript( Tcl_AppendResult(interp, "} bind def\n", NULL); } - sprintf(buffer, "%.15g %.15g [\n", textPtr->x, - Tk_CanvasPsY(canvas, textPtr->y)); - Tcl_AppendResult(interp, buffer, NULL); + Tcl_AppendPrintfToObj(Tcl_GetObjResult(interp), "%.15g %.15g %.15g [\n", + textPtr->angle, textPtr->x, Tk_CanvasPsY(canvas, textPtr->y)); Tk_TextLayoutToPostscript(interp, textPtr->textLayout); @@ -1492,15 +1599,10 @@ TextToPostscript( } Tk_GetFontMetrics(textPtr->tkfont, &fm); - sprintf(buffer, "] %d ", fm.linespace); - Tcl_AppendResult(interp, buffer, NULL); - Tcl_PrintDouble(NULL, x / -2.0, buffer); - Tcl_AppendResult(interp, buffer, " ", NULL); - Tcl_PrintDouble(NULL, y / 2.0, buffer); - Tcl_AppendResult(interp, buffer, NULL); - sprintf(buffer, " %s %s DrawText\n", justify, + Tcl_AppendPrintfToObj(Tcl_GetObjResult(interp), + "] %d %g %g %s %s DrawText\n", + fm.linespace, x / -2.0, y / 2.0, justify, ((stipple == None) ? "false" : "true")); - Tcl_AppendResult(interp, buffer, NULL); return TCL_OK; } diff --git a/generic/tkFont.c b/generic/tkFont.c index a0fac74..c3b0e72 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkFont.c,v 1.48 2008/11/02 09:54:02 nijtmans Exp $ + * RCS: @(#) $Id: tkFont.c,v 1.49 2008/11/22 18:08:51 dkf Exp $ */ #include "tkInt.h" @@ -888,7 +888,7 @@ RecomputeWidgets( Tk_GetClassProc(winPtr->classProcsPtr, worldChangedProc); if (proc != NULL) { - (*proc)(winPtr->instanceData); + proc(winPtr->instanceData); } /* @@ -1347,7 +1347,7 @@ SetFontFromAny( Tcl_GetString(objPtr); typePtr = objPtr->typePtr; if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) { - (*typePtr->freeIntRepProc)(objPtr); + typePtr->freeIntRepProc(objPtr); } objPtr->typePtr = &tkFontObjType; objPtr->internalRep.twoPtrValue.ptr1 = NULL; @@ -2310,6 +2310,69 @@ Tk_DrawTextLayout( } } +void +TkDrawAngledTextLayout( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context to use for drawing text. */ + Tk_TextLayout layout, /* Layout information, from a previous call to + * Tk_ComputeTextLayout(). */ + int x, int y, /* Upper-left hand corner of rectangle in + * which to draw (pixels). */ + double angle, + int firstChar, /* The index of the first character to draw + * from the given text item. 0 specfies the + * beginning. */ + int lastChar) /* The index just after the last character to + * draw from the given text item. A number < 0 + * means to draw all characters. */ +{ + TextLayout *layoutPtr = (TextLayout *) layout; + int i, numDisplayChars, drawX; + const char *firstByte, *lastByte; + LayoutChunk *chunkPtr; + double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0); + + if (layoutPtr == NULL) { + return; + } + + if (lastChar < 0) { + lastChar = 100000000; + } + chunkPtr = layoutPtr->chunks; + for (i = 0; i < layoutPtr->numChunks; i++) { + numDisplayChars = chunkPtr->numDisplayChars; + if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) { + double dx, dy; + + if (firstChar <= 0) { + drawX = 0; + firstChar = 0; + firstByte = chunkPtr->start; + } else { + firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar); + Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, + firstByte - chunkPtr->start, -1, 0, &drawX); + } + if (lastChar < numDisplayChars) { + numDisplayChars = lastChar; + } + lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars); + dx = cosA * (chunkPtr->x + drawX) + sinA * (chunkPtr->y); + dy = -sinA * (chunkPtr->x + drawX) + cosA * (chunkPtr->y); + TkpDrawAngledChars(display, drawable, gc, layoutPtr->tkfont, + firstByte, lastByte - firstByte, x+dx, y+dy, angle); + } + firstChar -= chunkPtr->numChars; + lastChar -= chunkPtr->numChars; + if (lastChar <= 0) { + break; + } + chunkPtr++; + } +} + /* *--------------------------------------------------------------------------- * @@ -2358,6 +2421,68 @@ Tk_UnderlineTextLayout( } } +void +TkUnderlineAngledTextLayout( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context to use for drawing text. */ + Tk_TextLayout layout, /* Layout information, from a previous call to + * Tk_ComputeTextLayout(). */ + int x, int y, /* Upper-left hand corner of rectangle in + * which to draw (pixels). */ + double angle, + int underline) /* Index of the single character to underline, + * or -1 for no underline. */ +{ + int xx, yy, width, height; + + if (angle == 0.0) { + Tk_UnderlineTextLayout(display, drawable, gc, layout, x,y, underline); + return; + } + + if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0) + && (width != 0)) { + TextLayout *layoutPtr = (TextLayout *) layout; + TkFont *fontPtr = (TkFont *) layoutPtr->tkfont; + double sinA = sin(angle*PI/180), cosA = cos(angle*PI/180); + double dy = yy + fontPtr->fm.ascent + fontPtr->underlinePos; + XPoint points[5]; + + /* + * Note that we're careful to only round a double value once, which + * minimizes roundoff errors. + */ + + points[0].x = x + round(xx*cosA + dy*sinA); + points[0].y = y + round(dy*cosA - xx*sinA); + points[1].x = x + round(xx*cosA + dy*sinA + width*cosA); + points[1].y = y + round(dy*cosA - xx*sinA - width*sinA); + if (fontPtr->underlineHeight == 1) { + /* + * Thin underlines look better when rotated when drawn as a line + * rather than a rectangle; the rasterizer copes better. + */ + + XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin); + } else { + points[2].x = x + round(xx*cosA + dy*sinA + width*cosA + - fontPtr->underlineHeight*sinA); + points[2].y = y + round(dy*cosA - xx*sinA - width*sinA + + fontPtr->underlineHeight*cosA); + points[3].x = x + round(xx*cosA + dy*sinA + - fontPtr->underlineHeight*sinA); + points[3].y = y + round(dy*cosA - xx*sinA + + fontPtr->underlineHeight*cosA); + points[4].x = points[0].x; + points[4].y = points[0].y; + XFillPolygon(display, drawable, gc, points, 5, Complex, + CoordModeOrigin); + XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin); + } + } +} + /* *--------------------------------------------------------------------------- * @@ -2806,6 +2931,263 @@ Tk_IntersectTextLayout( /* *--------------------------------------------------------------------------- * + * TkIntersectAngledTextLayout -- + * + * Determines whether a text layout that has been turned by an angle + * about its top-left coordinae lies entirely inside, entirely outside, + * or overlaps a given rectangle. Non-displaying space characters that + * occur at the end of individual lines in the text layout are ignored + * for intersection calculations. + * + * Results: + * The return value is -1 if the text layout is entirely outside of the + * rectangle, 0 if it overlaps, and 1 if it is entirely inside of the + * rectangle. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static inline int +PointInQuadrilateral( + double qx[], + double qy[], + double x, + double y) +{ + int i; + + for (i=0 ; i<4 ; i++) { + double sideDX = qx[(i+1)%4] - qx[i]; + double sideDY = qy[(i+1)%4] - qy[i]; + double dx = x - qx[i]; + double dy = y - qy[i]; + + if (sideDX*dy < sideDY*dx) { + return 0; + } + } + return 1; +} + +static inline int +sign( + double value) +{ + if (value < 0.0) return -1; + if (value > 0.0) return 1; + return 0; +} + +static inline int +SidesIntersect( + double ax1, double ay1, double ax2, double ay2, + double bx1, double by1, double bx2, double by2) +{ +#if 0 +/* http://www.freelunchdesign.com/cgi-bin/codwiki.pl?DiscussionTopics/CollideMeUpBaby */ + + double a1, b1, c1, a2, b2, c2, r1, r2, r3, r4, denom; + + a1 = ay2 - ay1; + b1 = ax1 - ax2; + c1 = (ax2 * ay1) - (ax1 * ay2); + r3 = (a1 * bx1) + (b1 * by1) + c1; + r4 = (a1 * bx2) + (b1 * by2) + c1; + if ((r3 != 0.0) && (r4 != 0.0) && (r3*r4 > 0.0)) { + return 0; + } + + a2 = by2 - by1; + b2 = bx1 - bx2; + c2 = (bx2 * by1) - (bx1 * by2); + r1 = (a2 * ax1) + (b2 * ay1) + c2; + r2 = (a2 * ax2) + (b2 * ay2) + c2; + if ((r1 != 0.0) && (r2 != 0.0) && (r1*r2 > 0.0)) { + return 0; + } + + denom = (a1 * b2) - (a2 * b1); + return (denom != 0.0); +#else + /* + * A more efficient version. Two line segments intersect if, when seen + * from the perspective of one line, the two endpoints of the other + * segment lie on opposite sides of the line, and vice versa. "Lie on + * opposite sides" is computed by taking the cross products and seeing if + * they are of opposite signs. + */ + + double dx, dy, dx1, dy1; + + dx = ax2 - ax1; + dy = ay2 - ay1; + dx1 = bx1 - ax1; + dy1 = by1 - ay1; + if ((dx*dy1-dy*dx1 > 0.0) == (dx*(by2-ay1)-dy*(bx2-ax1) > 0.0)) { + return 0; + } + dx = bx2 - bx1; + dy = by2 - by1; + if ((dy*dx1-dx*dy1 > 0.0) == (dx*(ay2-by1)-dy*(ax2-bx1) > 0.0)) { + return 0; + } + return 1; +#endif +} + +int +TkIntersectAngledTextLayout( + Tk_TextLayout layout, /* Layout information, from a previous call to + * Tk_ComputeTextLayout(). */ + int x, int y, /* Upper-left hand corner, in pixels, of + * rectangular area to compare with text + * layout. Coordinates are with respect to the + * upper-left hand corner of the text layout + * itself. */ + int width, int height, /* The width and height of the above + * rectangular area, in pixels. */ + double angle) +{ + int i, x1, y1, x2, y2; + TextLayout *layoutPtr; + LayoutChunk *chunkPtr; + TkFont *fontPtr; + double c = cos(angle * PI/180.0), s = sin(angle * PI/180.0); + double rx[4], ry[4]; + + if (angle == 0.0) { + return Tk_IntersectTextLayout(layout, x, y, width, height); + } + + /* + * Compute the coordinates of the rectangle, rotated into text layout + * space. + */ + + rx[0] = x*c - y*s; + ry[0] = y*c + x*s; + rx[1] = (x+width)*c - y*s; + ry[1] = y*c + (x+width)*s; + rx[2] = (x+width)*c - (y+height)*s; + ry[2] = (y+height)*c + (x+width)*s; + rx[3] = x*c - (y+height)*s; + ry[3] = (y+height)*c + x*s; + + /* + * Want to know if all chunks are inside the rectangle, or if there is any + * overlap. First, we check to see if all chunks are inside; if and only + * if they are, we're in the "inside" case. + */ + + layoutPtr = (TextLayout *) layout; + chunkPtr = layoutPtr->chunks; + fontPtr = (TkFont *) layoutPtr->tkfont; + + for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) { + if (chunkPtr->start[0] == '\n') { + /* + * Newline characters are not counted when computing area + * intersection (but tab characters would still be considered). + */ + + continue; + } + + x1 = chunkPtr->x; + y1 = chunkPtr->y - fontPtr->fm.ascent; + x2 = chunkPtr->x + chunkPtr->displayWidth; + y2 = chunkPtr->y + fontPtr->fm.descent; + if ( !PointInQuadrilateral(rx, ry, x1, y1) || + !PointInQuadrilateral(rx, ry, x2, y1) || + !PointInQuadrilateral(rx, ry, x2, y2) || + !PointInQuadrilateral(rx, ry, x1, y2)) { + goto notInside; + } + } + return 1; + + /* + * Next, check to see if all the points of the rectangle are inside a + * single chunk; if they are, we're in an "overlap" case. + */ + + notInside: + chunkPtr = layoutPtr->chunks; + + for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) { + double cx[4], cy[4]; + if (chunkPtr->start[0] == '\n') { + /* + * Newline characters are not counted when computing area + * intersection (but tab characters would still be considered). + */ + + continue; + } + + cx[0] = cx[3] = chunkPtr->x; + cy[0] = cy[1] = chunkPtr->y - fontPtr->fm.ascent; + cx[1] = cx[2] = chunkPtr->x + chunkPtr->displayWidth; + cy[2] = cy[3] = chunkPtr->y + fontPtr->fm.descent; + if ( !PointInQuadrilateral(cx, cy, rx[0], ry[0]) || + !PointInQuadrilateral(cx, cy, rx[1], ry[1]) || + !PointInQuadrilateral(cx, cy, rx[2], ry[2]) || + !PointInQuadrilateral(cx, cy, rx[3], ry[3])) { + goto notReverseInside; + } + } + return 0; + + /* + * If we're overlapping now, we must be partially in and out of at least + * one chunk. If that is the case, there must be one line segment of the + * rectangle that is touching or crossing a line segment of a chunk. + */ + + notReverseInside: + chunkPtr = layoutPtr->chunks; + + for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) { + int j; + + if (chunkPtr->start[0] == '\n') { + /* + * Newline characters are not counted when computing area + * intersection (but tab characters would still be considered). + */ + + continue; + } + + x1 = chunkPtr->x; + y1 = chunkPtr->y - fontPtr->fm.ascent; + x2 = chunkPtr->x + chunkPtr->displayWidth; + y2 = chunkPtr->y + fontPtr->fm.descent; + + for (j=0 ; j<4 ; j++) { + int k = (j+1) % 4; + if ( SidesIntersect(rx[j],ry[j], rx[k],ry[k], x1,y1, x2,y1) || + SidesIntersect(rx[j],ry[j], rx[k],ry[k], x2,y1, x2,y2) || + SidesIntersect(rx[j],ry[j], rx[k],ry[k], x2,y2, x1,y2) || + SidesIntersect(rx[j],ry[j], rx[k],ry[k], x1,y2, x1,y1)) { + return 0; + } + } + } + + /* + * They must be wholly non-overlapping. + */ + + return -1; +} + +/* + *--------------------------------------------------------------------------- + * * Tk_TextLayoutToPostscript -- * * Outputs the contents of a text layout in Postscript format. The set of diff --git a/generic/tkInt.h b/generic/tkInt.h index 10fff59..5fe85e1 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: $Id: tkInt.h,v 1.92 2008/11/09 21:53:39 nijtmans Exp $ + * RCS: $Id: tkInt.h,v 1.93 2008/11/22 18:08:51 dkf Exp $ */ #ifndef _TKINT @@ -956,6 +956,19 @@ MODULE_SCOPE TkMainInfo *tkMainWindowList; MODULE_SCOPE Tk_ImageType tkPhotoImageType; MODULE_SCOPE Tcl_HashTable tkPredefBitmapTable; +/* + * The definition of pi, at least from the perspective of double-precision + * floats. + */ + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/* + * Exported internals. + */ + #include "tkIntDecls.h" #ifdef BUILD_tk @@ -1214,6 +1227,19 @@ MODULE_SCOPE void TkpCreateBusy(Tk_FakeWin *winPtr, Tk_Window tkRef, Window *parentPtr, Tk_Window tkParent, TkBusy busy); +MODULE_SCOPE void TkDrawAngledTextLayout(Display *display, + Drawable drawable, GC gc, Tk_TextLayout layout, + int x, int y, double angle, int firstChar, + int lastChar); +MODULE_SCOPE void TkpDrawAngledChars(Display *display,Drawable drawable, + GC gc, Tk_Font tkfont, const char *source, + int numBytes, double x, double y, double angle); +MODULE_SCOPE void TkUnderlineAngledTextLayout(Display *display, + Drawable drawable, GC gc, Tk_TextLayout layout, + int x, int y, double angle, int underline); +MODULE_SCOPE int TkIntersectAngledTextLayout(Tk_TextLayout layout, + int x,int y, int width, int height, double angle); + /* * Unsupported commands. */ |