Attachment "rotate.patch" to
ticket [1611359f]
added by
dkf
2008-11-18 08:39:03.
Index: generic/tkCanvText.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkCanvText.c,v
retrieving revision 1.32
diff -u -r1.32 tkCanvText.c
--- generic/tkCanvText.c 9 Nov 2008 20:51:28 -0000 1.32
+++ generic/tkCanvText.c 18 Nov 2008 01:31:28 -0000
@@ -57,6 +57,8 @@
* 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 @@
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 @@
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,9 @@
TextDeleteChars, /* dTextProc */
NULL, /* nextPtr */
};
+
+#define PI 3.14159265358979323846
+#define ROUND(d) ((int) floor((d) + 0.5))
/*
*--------------------------------------------------------------
@@ -258,15 +265,18 @@
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 +335,14 @@
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 +425,24 @@
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 +523,10 @@
textPtr->insertPos = textPtr->numChars;
}
+ textPtr->angle = fmod(textPtr->angle, 360.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 +617,11 @@
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 +639,11 @@
* 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 +654,18 @@
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 +678,27 @@
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 +711,50 @@
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 +794,16 @@
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 +824,8 @@
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 +856,30 @@
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 +897,28 @@
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 +928,8 @@
* 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 +944,24 @@
* 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);
- }
- Tk_UnderlineTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
- drawableX, drawableY, textPtr->underline);
+ TkDrawAngledTextLayout(display, drawable, textPtr->gc,
+ textPtr->textLayout, drawableX, drawableY, textPtr->angle,
+ 0, -1);
+ }
+ TkUnderlineAngledTextLayout(display, drawable, textPtr->gc,
+ textPtr->textLayout, drawableX, drawableY, textPtr->angle,
+ textPtr->underline);
if (stipple != None) {
XSetTSOrigin(display, textPtr->gc, 0, 0);
@@ -1076,15 +1171,17 @@
{
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 +1225,12 @@
}
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 +1359,7 @@
*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 +1374,9 @@
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;
@@ -1423,7 +1521,6 @@
int x, y;
Tk_FontMetrics fm;
const char *justify;
- char buffer[500];
XColor *color;
Pixmap stipple;
Tk_State state = itemPtr->state;
@@ -1437,17 +1534,17 @@
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 +1564,8 @@
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 +1588,10 @@
}
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;
}
Index: generic/tkFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkFont.c,v
retrieving revision 1.48
diff -u -r1.48 tkFont.c
--- generic/tkFont.c 2 Nov 2008 09:54:02 -0000 1.48
+++ generic/tkFont.c 18 Nov 2008 01:31:32 -0000
@@ -354,6 +354,8 @@
NULL, /* updateStringProc */
SetFontFromAny /* setFromAnyProc */
};
+
+#define PI 3.14159265358979323846
/*
*---------------------------------------------------------------------------
@@ -888,7 +890,7 @@
Tk_GetClassProc(winPtr->classProcsPtr, worldChangedProc);
if (proc != NULL) {
- (*proc)(winPtr->instanceData);
+ proc(winPtr->instanceData);
}
/*
@@ -1347,7 +1349,7 @@
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 +2312,69 @@
}
}
+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 +2423,68 @@
}
}
+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 +2933,263 @@
/*
*---------------------------------------------------------------------------
*
+ * 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
Index: generic/tkInt.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkInt.h,v
retrieving revision 1.92
diff -u -r1.92 tkInt.h
--- generic/tkInt.h 9 Nov 2008 21:53:39 -0000 1.92
+++ generic/tkInt.h 18 Nov 2008 01:31:33 -0000
@@ -1214,6 +1214,19 @@
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.
*/
Index: library/prolog.ps
===================================================================
RCS file: /cvsroot/tktoolkit/tk/library/prolog.ps,v
retrieving revision 1.3
diff -u -r1.3 prolog.ps
--- library/prolog.ps 4 Feb 1999 20:59:48 -0000 1.3
+++ library/prolog.ps 18 Nov 2008 01:31:34 -0000
@@ -189,11 +189,12 @@
} if
} bind def
-% x y strings spacing xoffset yoffset justify stipple DrawText --
+% angle x y strings spacing xoffset yoffset justify stipple DrawText --
% This procedure does all of the real work of drawing text. The
% color and font must already have been set by the caller, and the
% following arguments must be on the stack:
%
+% angle - Angle through which to rotate text, in degrees.
% x, y - Coordinates at which to draw text.
% strings - An array of strings, one for each line of the text item,
% in order from top to bottom.
@@ -235,11 +236,12 @@
exch pop exch sub /height exch def pop
newpath
- % Translate coordinates first so that the origin is at the upper-left
- % corner of the text's bounding box. Remember that x and y for
- % positioning are still on the stack.
+ % Translate and rotatecoordinates first so that the origin is at the
+ % upper-left corner of the text's bounding box. Remember that angle, x and
+ % y for positioning are still on the stack.
translate
+ rotate
lineLength xoffset mul
strings length 1 sub spacing mul height add yoffset mul translate
Index: macosx/tkMacOSXFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/macosx/tkMacOSXFont.c,v
retrieving revision 1.40
diff -u -r1.40 tkMacOSXFont.c
--- macosx/tkMacOSXFont.c 2 Nov 2008 09:54:02 -0000 1.40
+++ macosx/tkMacOSXFont.c 18 Nov 2008 01:31:38 -0000
@@ -262,7 +262,10 @@
static void InitSystemFonts(TkMainInfo *mainPtr);
static int CreateNamedSystemFont(Tcl_Interp *interp, Tk_Window tkwin,
const char* name, TkFontAttributes *faPtr);
-
+static void DrawCharsInContext(Display *display,
+ Drawable drawable, GC gc, Tk_Font tkfont,
+ const char *source, int numBytes, int rangeStart,
+ int rangeLength, int x, int y, double angle);
/*
*-------------------------------------------------------------------------
@@ -1135,8 +1138,31 @@
int x, int y) /* Coordinates at which to place origin of the
* string when drawing. */
{
- TkpDrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
- 0, numBytes, x, y);
+ DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
+ 0, numBytes, x, y, 0.0);
+}
+
+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, /* Coordinates at which to place origin of
+ * string when drawing. */
+ double angle) /* What angle to put text at, in degrees. */
+{
+ DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
+ 0, numBytes, x, y, angle);
}
/*
@@ -1180,6 +1206,32 @@
* whole (not just the range) string when
* drawing. */
{
+ DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
+ rangeStart, rangeLength, x, y, 0.0);
+}
+
+static void
+DrawCharsInContext(
+ 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. */
+ int rangeStart, /* Index of first byte to draw. */
+ int rangeLength, /* Length of range to draw in bytes. */
+ int x, int y, /* Coordinates at which to place origin of the
+ * whole (not just the range) string when
+ * drawing. */
+ double angle)
+{
const MacFont * fontPtr = (const MacFont *) tkfont;
MacDrawable *macWin = (MacDrawable *) drawable;
Fixed fx, fy;
@@ -1242,6 +1294,14 @@
urstart = Tcl_NumUtfChars(source, rangeStart);
urlen = Tcl_NumUtfChars(source+rangeStart,rangeLength);
+ /* Rotate the coordinate system for Quarz drawing. */
+ if (drawingContext.context && angle != 0.0) {
+#define PI 3.14159265358979323846
+ CGContextTranslateCTM(drawingContext.context, x, y);
+ CGContextRotateCTM(drawingContext.context, angle * PI/180.0);
+ CGContextTranslateCTM(drawingContext.context, -x, -y);
+ }
+
ChkErr(ATSUDrawText, fontPtr->atsuLayout, lineOffset+urstart, urlen, fx,
fy);
Index: unix/tkUnixFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/unix/tkUnixFont.c,v
retrieving revision 1.38
diff -u -r1.38 tkUnixFont.c
--- unix/tkUnixFont.c 5 Nov 2008 22:20:59 -0000 1.38
+++ unix/tkUnixFont.c 18 Nov 2008 01:32:02 -0000
@@ -139,9 +139,9 @@
*/
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,284 @@
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 {
+#define PI 3.14159265358979323846
+ 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
Index: unix/tkUnixRFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/unix/tkUnixRFont.c,v
retrieving revision 1.26
diff -u -r1.26 tkUnixRFont.c
--- unix/tkUnixRFont.c 5 Nov 2008 22:20:59 -0000 1.26
+++ unix/tkUnixRFont.c 18 Nov 2008 01:32:03 -0000
@@ -16,10 +16,14 @@
#include <X11/Xft/Xft.h>
#include <ctype.h>
+#define PI 3.14159265358979323846
+
typedef struct {
XftFont *ftFont;
+ XftFont *ft0Font;
FcPattern *source;
FcCharSet *charset;
+ double angle;
} UnixFtFace;
typedef struct {
@@ -62,13 +66,15 @@
static XftFont *
GetFont(
UnixFtFont *fontPtr,
- FcChar32 ucs4)
+ FcChar32 ucs4,
+ double angle)
{
int i;
if (ucs4) {
for (i = 0; i < fontPtr->nfaces; i++) {
FcCharSet *charset = fontPtr->faces[i].charset;
+
if (charset && FcCharSetHasChar(charset, ucs4)) {
break;
}
@@ -79,34 +85,52 @@
} else {
i = 0;
}
- if (!fontPtr->faces[i].ftFont) {
- FcPattern *pat = FcFontRenderPrepare(0,
- fontPtr->pattern, fontPtr->faces[i].source);
- XftFont *ftFont = XftFontOpenPattern(fontPtr->display, pat);
+ if ((angle == 0.0 && !fontPtr->faces[i].ft0Font) || (angle != 0.0 &&
+ (!fontPtr->faces[i].ftFont || fontPtr->faces[i].angle != angle))){
+ FcPattern *pat = FcFontRenderPrepare(0, fontPtr->pattern,
+ fontPtr->faces[i].source);
+ double s = sin(angle*PI/180.0), c = cos(angle*PI/180.0);
+ FcMatrix mat = {c, -s, s, c};
+ XftFont *ftFont;
+ if (angle != 0.0) {
+ FcPatternAddMatrix(pat, FC_MATRIX, &mat);
+ }
+ ftFont = XftFontOpenPattern(fontPtr->display, pat);
if (!ftFont) {
/*
- * The previous call to XftFontOpenPattern() should not fail,
- * but sometimes does anyway. Usual cause appears to be
- * a misconfigured fontconfig installation; see [Bug 1090382].
- * Try a fallback:
+ * The previous call to XftFontOpenPattern() should not fail, but
+ * sometimes does anyway. Usual cause appears to be a
+ * misconfigured fontconfig installation; see [Bug 1090382]. Try a
+ * fallback:
*/
+
ftFont = XftFontOpen(fontPtr->display, fontPtr->screen,
- FC_FAMILY, FcTypeString, "sans",
- FC_SIZE, FcTypeDouble, 12.0,
- NULL);
+ FC_FAMILY, FcTypeString, "sans",
+ FC_SIZE, FcTypeDouble, 12.0,
+ FC_MATRIX, FcTypeMatrix, &mat,
+ NULL);
}
if (!ftFont) {
/*
- * The previous call should definitely not fail.
- * Impossible to proceed at this point.
+ * The previous call should definitely not fail. Impossible to
+ * proceed at this point.
*/
+
Tcl_Panic("Cannot find a usable font.");
}
- fontPtr->faces[i].ftFont = ftFont;
+ if (angle == 0.0) {
+ fontPtr->faces[i].ft0Font = ftFont;
+ } else {
+ if (fontPtr->faces[i].ftFont) {
+ XftFontClose(fontPtr->display, fontPtr->faces[i].ftFont);
+ }
+ fontPtr->faces[i].ftFont = ftFont;
+ fontPtr->faces[i].angle = angle;
+ }
}
- return fontPtr->faces[i].ftFont;
+ return (angle==0.0? fontPtr->faces[i].ft0Font : fontPtr->faces[i].ftFont);
}
/*
@@ -126,10 +150,10 @@
int weight, slant, size, pxsize;
double ptsize;
- (void)XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr);
+ (void) XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr);
if (XftPatternGetDouble(ftFont->pattern, XFT_SIZE, 0,
&ptsize) == XftResultMatch) {
- size = (int)ptsize;
+ size = (int) ptsize;
} else if (XftPatternGetInteger(ftFont->pattern, XFT_PIXEL_SIZE, 0,
&pxsize) == XftResultMatch) {
size = -pxsize;
@@ -165,7 +189,8 @@
* Fill in TkFontMetrics from an XftFont.
*/
-static void GetTkFontMetrics(
+static void
+GetTkFontMetrics(
XftFont *ftFont,
TkFontMetrics *fmPtr)
{
@@ -222,7 +247,7 @@
set = FcFontSort(0, pattern, FcTrue, NULL, &result);
if (!set) {
FcPatternDestroy(pattern);
- ckfree((char *)fontPtr);
+ ckfree((char *) fontPtr);
return NULL;
}
@@ -237,6 +262,7 @@
for (i = 0; i < set->nfont; i++) {
fontPtr->faces[i].ftFont = 0;
+ fontPtr->faces[i].ft0Font = 0;
fontPtr->faces[i].source = set->fonts[i];
if (FcPatternGetCharSet(set->fonts[i], FC_CHARSET, 0,
&charset) == FcResultMatch) {
@@ -244,6 +270,7 @@
} else {
fontPtr->faces[i].charset = 0;
}
+ fontPtr->faces[i].angle = 0.0;
}
fontPtr->display = Tk_Display(tkwin);
@@ -258,7 +285,8 @@
/*
* Fill in platform-specific fields of TkFont.
*/
- ftFont = GetFont(fontPtr, 0);
+
+ ftFont = GetFont(fontPtr, 0, 0.0);
fontPtr->font.fid = XLoadFont(Tk_Display(tkwin), "fixed");
GetTkFontAttributes(ftFont, &fontPtr->font.fa);
GetTkFontMetrics(ftFont, &fontPtr->font.fm);
@@ -272,19 +300,22 @@
{
Display *display = fontPtr->display;
int i;
- Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1, NULL,
- (ClientData) NULL);
+ Tk_ErrorHandler handler =
+ Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
for (i = 0; i < fontPtr->nfaces; i++) {
if (fontPtr->faces[i].ftFont) {
XftFontClose(fontPtr->display, fontPtr->faces[i].ftFont);
}
+ if (fontPtr->faces[i].ft0Font) {
+ XftFontClose(fontPtr->display, fontPtr->faces[i].ft0Font);
+ }
if (fontPtr->faces[i].charset) {
FcCharSetDestroy(fontPtr->faces[i].charset);
}
}
if (fontPtr->faces) {
- ckfree((char *)fontPtr->faces);
+ ckfree((char *) fontPtr->faces);
}
if (fontPtr->pattern) {
FcPatternDestroy(fontPtr->pattern);
@@ -432,14 +463,15 @@
resultPtr = Tcl_NewListObj(0, NULL);
list = XftListFonts(Tk_Display(tkwin), Tk_ScreenNumber(tkwin),
- (char*)0, /* pattern elements */
- XFT_FAMILY, (char*)0); /* fields */
+ (char *) 0, /* pattern elements */
+ XFT_FAMILY, (char*) 0); /* fields */
for (i = 0; i < list->nfont; i++) {
char *family, **familyPtr = &family;
+
if (XftPatternGetString(list->fonts[i], XFT_FAMILY, 0, familyPtr)
- == XftResultMatch)
- {
+ == XftResultMatch) {
Tcl_Obj *strPtr = Tcl_NewStringObj(family, -1);
+
Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
}
}
@@ -517,7 +549,7 @@
/* Structure describing the logical font */
FcChar32 ucs4 = (FcChar32) c;
/* UCS-4 character to map */
- XftFont *ftFont = GetFont(fontPtr, ucs4);
+ XftFont *ftFont = GetFont(fontPtr, ucs4, 0.0);
/* Actual font used to render the character */
GetTkFontAttributes(ftFont, faPtr);
@@ -566,7 +598,7 @@
Tcl_UniChar unichar;
clen = Tcl_UtfToUniChar(source, &unichar);
- c = (FcChar32)unichar;
+ c = (FcChar32) unichar;
if (clen <= 0) {
/*
@@ -592,7 +624,7 @@
#if DEBUG_FONTSEL
string[len++] = (char) c;
#endif /* DEBUG_FONTSEL */
- ftFont = GetFont(fontPtr, c);
+ ftFont = GetFont(fontPtr, c, 0.0);
XftTextExtents32(fontPtr->display, ftFont, &c, 1, &extents);
@@ -674,8 +706,8 @@
DefaultVisual(display, fontPtr->screen),
DefaultColormap(display, fontPtr->screen));
} else {
- Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1,
- NULL, (ClientData) NULL);
+ Tk_ErrorHandler handler =
+ Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
XftDrawChange(fontPtr->ftDraw, drawable);
Tk_DeleteErrorHandler(handler);
@@ -707,7 +739,7 @@
source += clen;
numBytes -= clen;
- ftFont = GetFont(fontPtr, c);
+ ftFont = GetFont(fontPtr, c, 0.0);
if (ftFont) {
specs[nspec].font = ftFont;
specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c);
@@ -729,3 +761,195 @@
XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color, specs, nspec);
}
}
+
+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, /* Coordinates at which to place origin of
+ * string when drawing. */
+ double angle) /* What angle to put text at, in degrees. */
+{
+ const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */
+ const int minCoord = -1000; /* Should be good enough... */
+ UnixFtFont *fontPtr = (UnixFtFont *) tkfont;
+ XGCValues values;
+ XColor xcolor;
+#ifdef XFT_HAS_FIXED_ROTATED_PLACEMENT
+ int clen, nglyph;
+ FT_UInt glyphs[NUM_SPEC];
+ XGlyphInfo metrics;
+ XftFont *currentFtFont;
+ int originX, originY;
+
+ if (fontPtr->ftDraw == 0) {
+#if DEBUG_FONTSEL
+ printf("Switch to drawable 0x%x\n", drawable);
+#endif /* DEBUG_FONTSEL */
+ fontPtr->ftDraw = XftDrawCreate(display, drawable,
+ DefaultVisual(display, fontPtr->screen),
+ DefaultColormap(display, fontPtr->screen));
+ } else {
+ Tk_ErrorHandler handler =
+ Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
+
+ XftDrawChange(fontPtr->ftDraw, drawable);
+ Tk_DeleteErrorHandler(handler);
+ }
+
+ XGetGCValues(display, gc, GCForeground, &values);
+ if (values.foreground != fontPtr->color.pixel) {
+ xcolor.pixel = values.foreground;
+ XQueryColor(display, DefaultColormap(display, fontPtr->screen),
+ &xcolor);
+ fontPtr->color.color.red = xcolor.red;
+ fontPtr->color.color.green = xcolor.green;
+ fontPtr->color.color.blue = xcolor.blue;
+ fontPtr->color.color.alpha = 0xffff;
+ fontPtr->color.pixel = values.foreground;
+ }
+
+ nglyph = 0;
+ currentFtFont = NULL;
+ originX = originY = 0; /* lint */
+
+ while (numBytes > 0 && x <= maxCoord && x >= minCoord && y <= maxCoord
+ && y >= minCoord) {
+ XftFont *ftFont;
+ FcChar32 c;
+
+ clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes);
+ if (clen <= 0) {
+ /*
+ * This should not happen, but it can.
+ */
+
+ return;
+ }
+ source += clen;
+ numBytes -= clen;
+
+ ftFont = GetFont(fontPtr, c, angle);
+ if (!ftFont) {
+ continue;
+ }
+
+ if (ftFont != currentFtFont || nglyph == NUM_SPEC) {
+ if (nglyph) {
+ /*
+ * We pass multiple glyphs at once to enable the code to
+ * perform better rendering of sub-pixel inter-glyph spacing.
+ * If only the current Xft implementation could make use of
+ * this information... but we'll be ready when it does!
+ */
+
+ XftDrawGlyphs(fontPtr->ftDraw, &fontPtr->color, currentFtFont,
+ originX, originY, glyphs, nglyph);
+ }
+ originX = (int) floor(x + 0.5);
+ originY = (int) floor(y + 0.5);
+ if (nglyph) {
+ XftGlyphExtents(fontPtr->display, currentFtFont, glyphs,
+ nglyph, &metrics);
+ nglyph = 0;
+ x += metrics.xOff;
+ y += metrics.yOff;
+ }
+ currentFtFont = ftFont;
+ }
+ glyphs[nglyph++] = XftCharIndex(fontPtr->display, ftFont, c);
+ }
+ if (nglyph) {
+ XftDrawGlyphs(fontPtr->ftDraw, &fontPtr->color, currentFtFont,
+ originX, originY, glyphs, nglyph);
+ }
+#else /* !XFT_HAS_FIXED_ROTATED_PLACEMENT */
+ int clen, nspec;
+ XftGlyphFontSpec specs[NUM_SPEC];
+ XGlyphInfo metrics;
+ double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0);
+
+ if (fontPtr->ftDraw == 0) {
+#if DEBUG_FONTSEL
+ printf("Switch to drawable 0x%x\n", drawable);
+#endif /* DEBUG_FONTSEL */
+ fontPtr->ftDraw = XftDrawCreate(display, drawable,
+ DefaultVisual(display, fontPtr->screen),
+ DefaultColormap(display, fontPtr->screen));
+ } else {
+ Tk_ErrorHandler handler =
+ Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
+
+ XftDrawChange(fontPtr->ftDraw, drawable);
+ Tk_DeleteErrorHandler(handler);
+ }
+ XGetGCValues(display, gc, GCForeground, &values);
+ if (values.foreground != fontPtr->color.pixel) {
+ xcolor.pixel = values.foreground;
+ XQueryColor(display, DefaultColormap(display, fontPtr->screen),
+ &xcolor);
+ fontPtr->color.color.red = xcolor.red;
+ fontPtr->color.color.green = xcolor.green;
+ fontPtr->color.color.blue = xcolor.blue;
+ fontPtr->color.color.alpha = 0xffff;
+ fontPtr->color.pixel = values.foreground;
+ }
+ nspec = 0;
+ while (numBytes > 0 && x <= maxCoord && x >= minCoord
+ && y <= maxCoord && y >= minCoord) {
+ XftFont *ftFont, *ft0Font;
+ FcChar32 c;
+
+ clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes);
+ if (clen <= 0) {
+ /*
+ * This should not happen, but it can.
+ */
+
+ return;
+ }
+ source += clen;
+ numBytes -= clen;
+
+ ftFont = GetFont(fontPtr, c, angle);
+ ft0Font = GetFont(fontPtr, c, 0.0);
+ if (ftFont && ft0Font) {
+ specs[nspec].font = ftFont;
+ specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c);
+ specs[nspec].x = (int) floor(x + 0.5);
+ specs[nspec].y = (int) floor(y + 0.5);
+ XftGlyphExtents(fontPtr->display, ft0Font, &specs[nspec].glyph, 1,
+ &metrics);
+ x += metrics.xOff*cosA + metrics.yOff*sinA;
+ y += metrics.yOff*cosA - metrics.xOff*sinA;
+ nspec++;
+ if (nspec == NUM_SPEC) {
+ XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color,
+ specs, nspec);
+ nspec = 0;
+ }
+ }
+ }
+ if (nspec) {
+ XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color, specs, nspec);
+ }
+#endif /* XFT_HAS_FIXED_ROTATED_PLACEMENT */
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */