Files
magic/graphics/grTk3.c
T
Darryl L. Miles 0b29b1cc12 fix GrTextSizePtr API interface return type consistency
commit 86b5d591d from 20241004 by me, modified return type of
GrTextSize API in the graphics from 'void' to 'int' to convey
and error scenario to indicate when 'r' was not filled in for
the caller, but this is a multi graphics driver API interface
so requires all downstream graphics engines to also support
the return type change.

This API can error and indicates when 'Rect *r' was
successfully updated.
2025-07-27 12:45:51 -04:00

565 lines
14 KiB
C

/* grTk3.c -
*
* Copyright 2003 Open Circuit Design, Inc., for MultiGiG Ltd.
*
* This file contains additional functions to manipulate an X window system
* color display. Included here are device-dependent routines to draw and
* erase text and draw a grid.
*
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <X11/Xlib.h>
/* C99 compat */
#include <X11/Xutil.h>
#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "graphics/graphicsInt.h"
#include "textio/textio.h"
#include "utils/signals.h"
#include "utils/utils.h"
#include "utils/hash.h"
#include "utils/malloc.h"
#include "graphics/grTkInt.h"
#include "graphics/grTkCommon.h"
#include "database/fonts.h"
/* C99 compat */
#include "database/database.h"
/*---------------------------------------------------------
* grtkDrawGrid:
* grxDrawGrid adds a grid to the grid layer, using the current
* write mask and color.
*
* Results:
* TRUE is returned normally. However, if the grid gets too small
* to be useful, then nothing is drawn and FALSE is returned.
*
* Side Effects: None.
*---------------------------------------------------------
*/
#define GR_NUM_GRIDS 64
bool
grtkDrawGrid (prect, outline, clip)
Rect *prect; /* A rectangle that forms the template
* for the grid. Note: in order to maintain
* precision for the grid, the rectangle
* coordinates are specified in units of
* screen coordinates multiplied by SUBPIXEL.
*/
int outline; /* the outline style */
Rect *clip; /* a clipping rectangle */
{
int xsize, ysize;
int x, y;
int xstart, ystart;
XSegment seg[GR_NUM_GRIDS];
int snum, low, hi, shifted;
xsize = prect->r_xtop - prect->r_xbot;
ysize = prect->r_ytop - prect->r_ybot;
if (!xsize || !ysize || GRID_TOO_SMALL(xsize, ysize))
return FALSE;
xstart = prect->r_xbot % xsize;
while (xstart < clip->r_xbot << SUBPIXELBITS) xstart += xsize;
ystart = prect->r_ybot % ysize;
while (ystart < clip->r_ybot << SUBPIXELBITS) ystart += ysize;
grtkSetLineStyle(outline);
snum = 0;
low = grMagicToX(clip->r_ybot);
hi = grMagicToX(clip->r_ytop);
for (x = xstart; x < (clip->r_xtop + 1) << SUBPIXELBITS; x += xsize)
{
if (snum == GR_NUM_GRIDS)
{
XDrawSegments(grXdpy, grCurrent.windowid, grGCDraw, seg, snum);
snum = 0;
}
shifted = x >> SUBPIXELBITS;
seg[snum].x1 = shifted;
seg[snum].y1 = low;
seg[snum].x2 = shifted;
seg[snum].y2 = hi;
snum++;
}
XDrawSegments(grXdpy, grCurrent.windowid, grGCDraw, seg, snum);
snum = 0;
low = clip->r_xbot;
hi = clip->r_xtop;
for (y = ystart; y < (clip->r_ytop + 1) << SUBPIXELBITS; y += ysize)
{
if (snum == GR_NUM_GRIDS)
{
XDrawSegments(grXdpy, grCurrent.windowid, grGCDraw, seg, snum);
snum = 0;
}
shifted = grMagicToX(y >> SUBPIXELBITS);
seg[snum].x1 = low;
seg[snum].y1 = shifted;
seg[snum].x2 = hi;
seg[snum].y2 = shifted;
snum++;
}
XDrawSegments(grXdpy, grCurrent.windowid, grGCDraw, seg, snum);
return TRUE;
}
/*---------------------------------------------------------
* grtkSetCharSize:
* This local routine sets the character size in the display,
* if necessary.
*
* Results: None.
*
* Side Effects: None.
*---------------------------------------------------------
*/
void
grtkSetCharSize (size)
int size; /* Width of characters, in pixels (6 or 8). */
{
grCurrent.fontSize = size;
switch (size)
{
case GR_TEXT_DEFAULT:
case GR_TEXT_SMALL:
grCurrent.font = grSmallFont;
break;
case GR_TEXT_MEDIUM:
grCurrent.font = grMediumFont;
break;
case GR_TEXT_LARGE:
grCurrent.font = grLargeFont;
break;
case GR_TEXT_XLARGE:
grCurrent.font = grXLargeFont;
break;
default:
TxError("%s%d\n", "grtkSetCharSize: Unknown character size ",
size );
break;
}
}
/*
* ----------------------------------------------------------------------------
* GrTkTextSize --
*
* Determine the size of a text string.
*
* Results:
* Returns 0 when 'r' updated, otherwise -1 on error (no side-effects).
*
* Side effects:
* A rectangle is filled in that is the size of the text in pixels.
* The origin (0, 0) of this rectangle is located on the baseline
* at the far left side of the string.
* ----------------------------------------------------------------------------
*/
int
GrTkTextSize(text, size, r)
char *text;
int size;
Rect *r;
{
Tk_FontMetrics overall;
Tk_Font font;
int width;
switch (size) {
case GR_TEXT_DEFAULT:
case GR_TEXT_SMALL:
font = grSmallFont;
break;
case GR_TEXT_MEDIUM:
font = grMediumFont;
break;
case GR_TEXT_LARGE:
font = grLargeFont;
break;
case GR_TEXT_XLARGE:
font = grXLargeFont;
break;
default:
font = NULL;
TxError("%s%d\n", "GrTkTextSize: Unknown character size ",
size );
break;
}
if (font == NULL) return -1;
Tk_GetFontMetrics(font, &overall);
width = Tk_TextWidth(font, text, strlen(text));
r->r_ytop = overall.ascent;
r->r_ybot = -overall.descent;
r->r_xtop = width;
r->r_xbot = 0;
return 0;
}
/*
* ----------------------------------------------------------------------------
* GrTkReadPixel --
*
* Read one pixel from the screen.
*
* Results:
* An integer containing the pixel's color.
*
* Side effects:
* none.
*
* ----------------------------------------------------------------------------
*/
int
GrTkReadPixel (w, x, y)
MagWindow *w;
int x,y; /* the location of a pixel in screen coords */
{
XImage *image;
unsigned long value;
XWindowAttributes att;
XGetWindowAttributes(grXdpy, grCurrent.windowid, &att);
if ( x < 0 || x >= att.width || grMagicToX(y) < 0 || grMagicToX(y) >= att.height) return(0);
image = XGetImage(grXdpy, grCurrent.windowid, x, grMagicToX(y), 1, 1,
~0, ZPixmap);
value = XGetPixel(image, 0, 0);
return (value & ((1 << grDisplay.depth) - 1));
}
/*
* ----------------------------------------------------------------------------
* GrTkBitBlt --
*
* Copy information from one part of the screen to the other.
*
* Results:
* None.
*
* Side effects:
* changes the screen.
* ----------------------------------------------------------------------------
*/
void
GrTkBitBlt(r, p)
Rect *r;
Point *p;
{
Window wind = grCurrent.windowid;
XCopyArea(grXdpy, wind, wind, grGCCopy,
r->r_xbot, grMagicToX(r->r_ytop),
r->r_xtop - r->r_xbot + 1, r->r_ytop - r->r_ybot + 1,
p->p_x, grMagicToX(p->p_y));
}
/*
* ----------------------------------------------------------------------------
* grtkRectConvert --
* Convert a magic rectangle into an X11 rectangle
* (Both passed as pointers).
*
* Results:
* None.
*
* Side effects:
* Coordinate-converted rectangle values placed in XRectangle pointer
* xr.
* ----------------------------------------------------------------------------
*/
void
grtkRectConvert(mr, xr)
Rect *mr; /* source rectangle pointer */
XRectangle *xr; /* destination rectangle pointer */
{
xr->x = mr->r_xbot;
xr->y = grMagicToX(mr->r_ytop);
xr->width = mr->r_xtop - mr->r_xbot + 1;
xr->height = mr->r_ytop - mr->r_ybot + 1;
}
/*
*---------------------------------------------------------
* grtkFontText:
*
* This routine is a fancier version of grtkPutText used for
* drawing vector outline fonts from the fontList records.
*
*---------------------------------------------------------
*/
void
grtkFontText(text, font, size, rotate, pos, clip, obscure)
char *text;
int font;
int size; /* pixel size of the text */
int rotate; /* text rotation */
Point *pos; /* text base position */
Rect *clip;
LinkedRect *obscure;
{
char *tptr;
FontChar *ccur, *clist;
Point *coffset, *tp, loffset, locoffset, corners[4], lpos;
Rect *cbbox, charbbox, *frect;
int np, i, j, w, h, llx, lly, baseline;
XPoint *xp;
Pixmap pxm;
double fscale, scx, scy, tmpx, tmpy, rrad, cr, sr;
static GC fontgc = (GC)NULL;
frect = &DBFontList[font]->mf_extents;
fscale = (double)size / (double)frect->r_ytop;
rrad = (double)rotate * 0.0174532925;
cr = cos(rrad);
sr = sin(rrad);
lpos = GeoOrigin;
/* 1st pass: find the descent of the string */
baseline = 0;
for (tptr = text; *tptr != '\0'; tptr++)
{
DBFontChar(font, *tptr, NULL, NULL, &cbbox);
if (cbbox->r_ybot < -baseline)
baseline = -cbbox->r_ybot;
}
baseline = (int)((double)baseline * fscale);
for (tptr = text; *tptr != '\0'; tptr++)
{
scx = (double)lpos.p_x * fscale;
scy = (double)lpos.p_y * fscale;
tmpx = scx * cr + scy * sr;
tmpy = scy * cr - scx * sr;
loffset.p_x = pos->p_x + (int)round(tmpx);
loffset.p_y = grMagicToX(pos->p_y + baseline) + (int)round(tmpy);
DBFontChar(font, *tptr, &clist, &coffset, &cbbox);
np = 0;
for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
np += ccur->fc_numpoints;
xp = (XPoint *)mallocMagic(np * sizeof(XPoint));
j = 0;
for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
{
tp = ccur->fc_points;
for (i = 0; i < ccur->fc_numpoints; i++, j++)
{
scx = (double)tp[i].p_x * fscale;
scy = (double)tp[i].p_y * fscale;
tmpx = scx * cr - scy * sr;
tmpy = scx * sr + scy * cr;
xp[j].x = (int)round(tmpx);
xp[j].y = (int)round(tmpy);
/* Initialize bounding box */
if (j == 0)
{
charbbox.r_xbot = charbbox.r_xtop = xp[j].x;
charbbox.r_ybot = charbbox.r_ytop = xp[j].y;
}
else
{
if (xp[j].x < charbbox.r_xbot)
charbbox.r_xbot = xp[j].x;
else if (xp[j].x > charbbox.r_xtop)
charbbox.r_xtop = xp[j].x;
if (xp[j].y < charbbox.r_ybot)
charbbox.r_ybot = xp[j].y;
else if (xp[j].y > charbbox.r_ytop)
charbbox.r_ytop = xp[j].y;
}
}
}
/* Create a bitmap */
w = charbbox.r_xtop - charbbox.r_xbot + 1;
h = charbbox.r_ytop - charbbox.r_ybot + 1;
/* Adjust all points to the bounding box origin, and invert Y */
for (j = 0; j < np; j++)
{
xp[j].x -= charbbox.r_xbot;
xp[j].y = charbbox.r_ytop - xp[j].y;
}
pxm = XCreatePixmap(grXdpy, grCurrent.windowid, w, h, 1);
if (fontgc == (GC)NULL)
{
XGCValues values;
values.foreground = 0;
values.background = 0;
fontgc = XCreateGC(grXdpy, pxm, GCForeground | GCBackground, &values);
}
locoffset.p_x = loffset.p_x + charbbox.r_xbot;
locoffset.p_y = loffset.p_y - charbbox.r_ytop;
XSetForeground(grXdpy, fontgc, 0);
XSetFunction(grXdpy, fontgc, GXcopy);
XFillRectangle(grXdpy, pxm, fontgc, 0, 0, w, h);
XSetFunction(grXdpy, fontgc, GXxor);
XSetForeground(grXdpy, fontgc, 1);
j = 0;
for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
{
np = ccur->fc_numpoints;
XFillPolygon(grXdpy, pxm, fontgc, &xp[j], np, Complex, CoordModeOrigin);
j += np;
}
freeMagic((char *)xp);
XSetClipMask(grXdpy, grGCText, pxm);
XSetClipOrigin(grXdpy, grGCText, locoffset.p_x, locoffset.p_y);
XFillRectangle(grXdpy, grCurrent.windowid, grGCText, locoffset.p_x,
locoffset.p_y, w, h);
lpos.p_x += coffset->p_x;
lpos.p_y += coffset->p_y;
XFreePixmap(grXdpy, pxm);
}
}
/*
*---------------------------------------------------------
* grtkPutText:
*
* This routine puts a chunk of text on the screen in the current
* color, size, etc. The caller must ensure that it fits on
* the screen -- no clipping is done except to the obscuring rectangle
* list and the clip rectangle.
*
* Results:
* none.
*
* Side Effects:
* The text is drawn on the screen.
*
*---------------------------------------------------------
*/
void
grtkPutText (text, pos, clip, obscure)
char *text; /* The text to be drawn. */
Point *pos; /* A point located at the leftmost point of
* the baseline for this string.
*/
Rect *clip; /* A rectangle to clip against */
LinkedRect *obscure; /* A list of obscuring rectangles */
{
Rect location;
Rect overlap;
Rect textrect;
LinkedRect *ob;
void grtkGeoSub();
if (grCurrent.font == NULL) return;
if (GrTkTextSize(text, grCurrent.fontSize, &textrect) < 0) return;
location.r_xbot = pos->p_x + textrect.r_xbot;
location.r_xtop = pos->p_x + textrect.r_xtop;
location.r_ybot = pos->p_y + textrect.r_ybot;
location.r_ytop = pos->p_y + textrect.r_ytop;
/* erase parts of the bitmap that are obscured */
for (ob = obscure; ob != NULL; ob = ob->r_next)
{
if (GEO_TOUCH(&ob->r_r, &location))
{
overlap = location;
GeoClip(&overlap, &ob->r_r);
grtkGeoSub(&location, &overlap);
}
}
overlap = location;
GeoClip(&overlap, clip);
/* copy the text to the color screen */
if ((overlap.r_xbot < overlap.r_xtop)&&(overlap.r_ybot <= overlap.r_ytop))
{
XRectangle xr;
grtkRectConvert(&overlap, &xr);
XSetClipRectangles(grXdpy, grGCText, 0, 0, &xr, 1, Unsorted);
XSetFont(grXdpy, grGCText, Tk_FontId(grCurrent.font));
Tk_DrawChars(grXdpy, grCurrent.windowid, grGCText,
grCurrent.font, text, strlen(text),
pos->p_x, grMagicToX(pos->p_y));
}
}
/*
*---------------------------------------------------------
* grtkGeoSub:
* Return the tallest sub-rectangle of r not obscured
* by area "area" must be within r.
*
* Results:
* None.
*
* Side effects:
* Source rectangle "r" is modified.
*
*---------------------------------------------------------
*/
void
grtkGeoSub(r, area)
Rect *r; /* Rectangle to be subtracted from. */
Rect *area; /* Area to be subtracted. */
{
if (r->r_xbot == area->r_xbot) r->r_xbot = area->r_xtop;
else
if (r->r_xtop == area->r_xtop) r->r_xtop = area->r_xbot;
else
if (r->r_ybot <= area->r_ybot) r->r_ybot = area->r_ytop;
else
if (r->r_ytop == area->r_ytop) r->r_ytop = area->r_ybot;
else
r->r_xtop = area->r_xbot;
}