tools/clock/rleClock.c

Summary

Maintainability
Test Coverage
/*
 * rleClock
 * --------
 *
 * Generates a clock face with digital output above it, in Utah Raster toolkit
 * format.
 *
 * SYNOPSIS
 *    rleClock [options]
 *
 *    Writes an RLE file to stdout containins a clock face image according to
 *    the options.
 *
 * OPTIONS
 *
 *    Too many to describe.  Type "rleClock -help" for a listing.
 * 
 * AUTHOR
 *    Bob Brown        rlb@riacs.edu
 *    RIACS            415 694 5407
 *    Mail Stop 230-5
 *    NASA Ames Research Center
 *    Moffett Field
 *    CA  94035
 *
 *    First draft, December 3, 1987
 *    lineDots() written by Nancy Blachman Jan, 1987
 *    font.src derived from NBS fonts.
 */
static char rcsid[] = "$Header: /l/spencer/src/urt/tools/clock/RCS/rleClock.c,v 3.0.1.2 1992/04/30 14:19:38 spencer Exp $";
/*
rleClock()            Tag the file.
*/

#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include "rle.h"
#include <sys/types.h>
#include <time.h>
#include "font.h"

/*
 * Program parameters defaults
 *
 * Note: within the program, radius is on a scale of 0..1 and then converted in
 * the polar coordinate conversion routines.
 */

#define XSIZE 128
#define YTEXTSIZE 0
#define YCLOCKSIZE XSIZE
#define TICKS 12
#define DOTS 1
#define FORMATSTRING "%02l:%02b"
#define FACE_EDGE_COLOR { 255, 255, 255 }
#define HAND_COLOR { 255, 255, 255 }
#define TEXT_COLOR { 255, 255, 255 }
#define LITTLEHANDSCALE 12
#define BIGHANDSCALE 60

#define HANDWIDTH 0.075

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
typedef char bool;

/*
 * These bits define what is in the elements of the "Raster" array
 */

#define RAST_FACE_EDGE 1
#define RAST_FACE_MASK 2
#define RAST_LHAND_EDGE 4
#define RAST_LHAND_MASK 8
#define RAST_BHAND_EDGE 16
#define RAST_BHAND_MASK 32
#define RAST_TEXT 64
#define RAST_TEXT_BACK 128

/*
 * Type definitions
 */

typedef struct{
    unsigned char red, green, blue;
} color_t;

/*
 * Global variables
 */

color_t FaceEdgeColor = FACE_EDGE_COLOR;
color_t FaceColor;
color_t HandEdgeColor;
color_t HandColor = HAND_COLOR;
color_t TextColor = TEXT_COLOR;
color_t TextBackColor;

rle_pixel **RedLine;
rle_pixel **GreenLine;
rle_pixel **BlueLine;
rle_pixel **Raster;
rle_pixel **AlphaLine;

int XSize = XSIZE;
int YSize;
int YClockSize = YCLOCKSIZE;
int YTextSize = YTEXTSIZE;
int XRadius;
int YRadius;
int Ticks = TICKS;
bool DebugAlpha = FALSE;
bool Debug = FALSE;
int Dots = DOTS;
float BigHandValue;
float LittleHandValue;
float LittleHandScale = LITTLEHANDSCALE;
float BigHandScale = BIGHANDSCALE;
CONST_DECL char *FormatString = FORMATSTRING;

/*
 * External globals.
 */

extern move_t Moves[];
extern int Base[];

/*
 * Forward declarations
 */

#ifdef USE_PROTOTYPES
void main(int argc, char *argv[]);
void ifImageSet(int i, int j, int value, color_t *color);
void drawHand(double place, double scale, double radius, int mask, int edge);
void rasterAddBits(int mask, int match, int value);
void polarLine(double r0, double a0, double r1, double a1, int arg1, int arg2);
void setDot(int x, int y, int arg1, int arg2);
int polarToX(double fRadius, double angle);
int polarToY(double fRadius, double angle);
double radians(double degrees);
rle_pixel **rasterAllocate(int height, int width);
void rasterWrite(FILE *fd);
void lineDots(int x0, int y0, int x1, int y1, void (*func)(), int arg1, int arg2);
void procargs(int argc, char *argv[]);
bool argGiven(char *argVar);
void usageExit(char *pgm);
void charMinMaxWidth(int ch, int *min, int *max);
void charMinMaxHeight(int ch, int *min, int *max);
char *formatInterp(CONST_DECL char *str);
void drawText(void);
void areaFlood(int firstX, int firstY, int mask, int match, int value);
void stackPush(int x, int y, int dir);
int stackPop(void);
#else
void main();
void ifImageSet();
void drawHand();
void rasterAddBits();
void polarLine();
void setDot();
int polarToX();
int polarToY();
double radians();
rle_pixel **rasterAllocate();
void rasterWrite();
void lineDots();
void procargs();
bool argGiven();
void usageExit();
void charMinMaxWidth();
void charMinMaxHeight();
char *formatInterp();
void drawText();
void areaFlood();
void stackPush();
int stackPop();
#endif
char **gargv;

void
main (argc, argv)
int    argc;
char    *argv[];
{
    int             i, j;
    float           theta;
    time_t        now;
    struct tm      *tm, *localtime();
    bool haveFaceColor;
    bool haveHandEdgeColor;
    bool haveTextBackColor;

    gargv = argv;

    procargs(argc, argv);
    YRadius = (YClockSize - Dots) / 2;
    XRadius = (XSize - Dots) / 2;
    YSize = YClockSize + YTextSize + Dots + Dots;
    if (!argGiven((char *) &LittleHandValue) || !argGiven((char *) &BigHandValue)) {
    (void)time(&now);
    tm = localtime(&now);

    if (!argGiven((char *) &BigHandValue))
        BigHandValue = (float) tm->tm_min;
    if (!argGiven((char *) &LittleHandValue))
        LittleHandValue = (float) ((tm->tm_hour % 12)) + BigHandValue / 60.0;
    }
    /*
     * Allocate the storage for the raster 
     */

    RedLine = (rle_pixel **) rasterAllocate(YSize, XSize);
    GreenLine = (rle_pixel **) rasterAllocate(YSize, XSize);
    BlueLine = (rle_pixel **) rasterAllocate(YSize, XSize);
    AlphaLine = (rle_pixel **) rasterAllocate(YSize, XSize);
    Raster = (rle_pixel **) rasterAllocate(YSize, XSize);

    /*
     * Initialize the raster to the background color 
     */

    for (i = 0; i < YSize; i++) {
    for (j = 0; j < XSize; j++) {
        Raster[i][j] = 0;
    }
    }

    /*
     * Draw the clock face as a circle with tick marks 
     */

    for (i = 0; i < 360; i++) {
    polarLine(1.0, (float) i, 1.0, (float) (i + 1), RAST_FACE_EDGE, Dots);
    }
    for (i = 0; i < Ticks; i++) {
    theta = (float) i *360.0 / (float) Ticks;

    polarLine(1.0, theta, 0.85, theta, RAST_FACE_EDGE, Dots);
    }

    /*
     * Compute the RAST_FACE_MASK portion - includes what is inside the
     * dial face plus the dial face itself.  So first flood the inside, and
     * then OR in the stuff under the face lines 
     */

    areaFlood(polarToX(0.0, 0.0), polarToY(0.0, 0.0), RAST_FACE_EDGE, 0, RAST_FACE_MASK);
    rasterAddBits(RAST_FACE_EDGE, RAST_FACE_EDGE, RAST_FACE_MASK);

    /*
     * Draw the hands and the text... 
     */

    drawHand(BigHandValue, BigHandScale, 0.85, RAST_BHAND_MASK, RAST_BHAND_EDGE);
    drawHand(LittleHandValue, LittleHandScale, 0.60, RAST_LHAND_MASK, RAST_LHAND_EDGE);
    if (YTextSize > 0) {
    drawText();
    }
    /*
     * Compose the clock image from the generated raster and program
     * arguments 
     */

    haveFaceColor = argGiven((char *)&FaceColor);
    haveHandEdgeColor = argGiven((char *)&HandEdgeColor);
    haveTextBackColor = argGiven((char *)&TextBackColor);
    for (i = 0; i < YSize; i++) {
    for (j = 0; j < XSize; j++) {
        if (haveFaceColor) {
        ifImageSet(i, j, RAST_FACE_MASK, &FaceColor);
        }
        ifImageSet(i, j, RAST_FACE_EDGE, &FaceEdgeColor);
        ifImageSet(i, j, RAST_LHAND_MASK|RAST_BHAND_MASK, &HandColor);
        if (haveHandEdgeColor) {
        ifImageSet(i, j, RAST_LHAND_EDGE|RAST_BHAND_EDGE, &HandEdgeColor);
        }
        if (haveTextBackColor) {
        ifImageSet(i, j, RAST_TEXT_BACK, &TextBackColor);
        }
        ifImageSet(i, j, RAST_TEXT, &TextColor);

        /*
         * Now compute the Alpha channel
         */

            if ( (haveFaceColor && (Raster[i][j]&RAST_FACE_MASK)!=0)
         || ((Raster[i][j]&(RAST_FACE_EDGE|RAST_LHAND_MASK|RAST_BHAND_MASK))!=0)
         || (haveTextBackColor && (Raster[i][j] & RAST_TEXT_BACK)!=0)
         || ((Raster[i][j] & RAST_TEXT)!=0)) {
        AlphaLine[i][j] = 255;
        } else {
        AlphaLine[i][j] = 0;
        }
    }
    }

    /*
     * Dump the raster file to stdout... 
     */

    rasterWrite(stdout);

    exit(0);
}

void
ifImageSet(i, j, value, color)
int i, j, value;
color_t *color;
{
    if (Raster[i][j] & value) {
    RedLine[i][j] = color->red;
    GreenLine[i][j] = color->green;
    BlueLine[i][j] = color->blue;
    }
}

void
drawHand(place, scale, radius, mask, edge)
double place;
double scale;
double radius;
int mask, edge;
{
    float angle;
    angle = place / scale * 360;
    polarLine(HANDWIDTH,    angle+180.0,  HANDWIDTH,    angle-90.0,  edge, 1);
    polarLine(HANDWIDTH,    angle-90.0,   radius,       angle,       edge, 1);
    polarLine(radius,       angle,        HANDWIDTH,    angle+90.0,  edge, 1);
    polarLine(HANDWIDTH,    angle+90.0,   HANDWIDTH,    angle+180.0, edge, 1);
    areaFlood(polarToX(0.0, 0.0), polarToY(0.0, 0.0), edge, 0,
    mask);
    polarLine(HANDWIDTH,    angle+180.0,  HANDWIDTH,    angle-90.0,  edge, Dots);
    polarLine(HANDWIDTH,    angle-90.0,   radius,       angle,       edge, Dots);
    polarLine(radius,       angle,        HANDWIDTH,    angle+90.0,  edge, Dots);
    polarLine(HANDWIDTH,    angle+90.0,   HANDWIDTH,    angle+180.0, edge, Dots);
    rasterAddBits(edge, edge, mask);
}

void
rasterAddBits(mask, match, value)
int mask, match, value;
{
    int i, j;

    for (i = 0 ; i < YSize; i++) {
    for (j = 0; j < XSize; j++) {
        if ( (Raster[i][j]&mask) == match )
        Raster[i][j] |= value;
    }
    }
}

void
polarLine(r0, a0, r1, a1, arg1, arg2)
double r0, a0, r1, a1;
int arg1, arg2;
{
    lineDots(polarToX(r0, a0), 
         polarToY(r0, a0), 
         polarToX(r1, a1), 
         polarToY(r1, a1), 
         setDot, arg1, arg2);
}

/*
 * setDot
 * ------
 *
 * Draw a dot (actually a square) or a certain size.  This is called from
 * lineDots to draw the dots that comprise a line.
 */

void
setDot(x, y, arg1, arg2)
int x, y, arg1, arg2;
{
    int i, j;

if(Debug)fprintf(stderr, "Setting %d, %d\n", x, y);
    for (i = 0; i < arg2; i++) {
    for (j = 0; j < arg2; j++) {
        Raster[y+i][x+j] |= arg1;
    }
    }
}


/*
 * polar conversion
 * ----------------
 *
 * These routines convert from polar coordinates to cartesian coordinates in
 * the clock part of the pixel rectangle.
 */

int
polarToX(fRadius, angle)
double fRadius;
double angle;
{
    return (int)(fRadius * sin(radians(angle)) * XRadius) + XSize/2;
}

int
polarToY(fRadius, angle)
double fRadius;
double angle;
{
    return (int)(fRadius * cos(radians(angle)) * YRadius) + YRadius;
}

double
radians(degrees)
double degrees;
{
    return degrees/180.0 * 3.1415926;
}

/*
 * rasterAllocate
 * --------------
 *
 * Allocate a raster for a single color.
 */

rle_pixel **
rasterAllocate(height, width)
int height, width;
{
    rle_pixel **new, *row;
    int i;

    new = (rle_pixel **)calloc(height, sizeof(rle_pixel *));
    row = (rle_pixel *)calloc(height*width, sizeof(rle_pixel));
    for ( i=0 ; i<height ; i++) {
    new[i] = row;
    row += width;
    }
    return new;
}

/*
 * rasterWrite
 * -----------
 *
 * Dump the entire raster to a Utah RLE format file.
 */

void
rasterWrite(fd)
FILE *fd;
{
    rle_hdr         the_hdr;
    rle_pixel      *rows[4];
    int             i;

    the_hdr = *rle_hdr_init( (rle_hdr *)NULL );
    rle_names( &the_hdr, cmd_name( gargv ), NULL, 0 );

    RLE_SET_BIT(the_hdr, RLE_ALPHA);
    the_hdr.rle_file = fd;
    the_hdr.xmax = XSize;
    the_hdr.ymax = YSize;
    the_hdr.alpha = 1;
    rle_addhist( gargv, NULL, &the_hdr );

    rle_put_setup(&the_hdr);
    for (i = 0; i < YSize; i++) {
    rows[0] = AlphaLine[i];
    if (DebugAlpha) {
        rows[1] = AlphaLine[i];
        rows[2] = AlphaLine[i];
        rows[3] = AlphaLine[i];
    } else {
        rows[1] = RedLine[i];
        rows[2] = GreenLine[i];
        rows[3] = BlueLine[i];
    }
    rle_putrow(rows + 1, XSize, &the_hdr);
    }
    rle_close_f( the_hdr.rle_file );
}

/*
 * Bresenham's line drawing algorithm based on the general Bresenham
 * line drawing algorithm described in Rogers "Procedural Elements for
 * Computer Graphics" on page 40. 
 *
 * Written by    Nancy Blachman 
 *        CS 248A, Winter 
 *        Prof. Leo Guibas 
 *        Stanford University 
 *        January 13, 1987 
 *
 * This is why RIACS sent Nancy to grad school!
 */

void
lineDots(x0, y0, x1, y1, func, arg1, arg2)
int x0, y0, x1, y1;
void (*func)();
int arg1, arg2;
{
    int             e, x, y, delta_x, delta_y, tmp;
    bool            interchg = FALSE;
    int            dir_x, dir_y;
    int             two_dy, two_dx;    /* calculated outside of loop */
    register int    i;


    if (x0 == x1 && y0 == y1) {    /* is starting point = end point ? */
    (*func)(x0, y0, arg1, arg2);
    return;
    }
    x = x0;
    y = y0;

    delta_x = x1 - x0;
    delta_y = y1 - y0;

    delta_x = delta_x > 0 ? delta_x : -delta_x;    /* absolute value,
                         * abs(x1-x0) */
    delta_y = delta_y > 0 ? delta_y : -delta_y;    /* absolute value,
                         * abs(y1-x0) */

    dir_x = (x1 - x0) > 0 ? 1 : -1;    /* sign (x1 - x0) */
    dir_y = (y1 - y0) > 0 ? 1 : -1;    /* sign (y1 - y0) */

    if (delta_y > delta_x) {
    tmp = delta_x;
    delta_x = delta_y;
    delta_y = tmp;
    interchg = TRUE;
    }
    two_dx = 2 * delta_x;
    two_dy = 2 * delta_y;

    e = two_dy - delta_x;
    for (i = 1; i <= delta_x; ++i) {
    (*func) (x, y, arg1, arg2);
    while (e >= 0) {
        if (interchg)
        x += dir_x;
        else
        y += dir_y;
        e -= two_dx;
    }
    if (interchg)
        y += dir_y;
    else
        x += dir_x;
    e += two_dy;
    }
}

/*
 * procargs
 * --------
 *
 * Argument line parser.  This version is table driven and requires exact match
 * on the argument switches.
 */

struct {
    bool show;
    CONST_DECL char *arg;
    enum { INT, FLOAT, STRING, BOOL, COLOR, HELP, TEXT } type;
    CONST_DECL char *description;
    CONST_DECL char *value;
    bool given;
} Args[] ={
    { TRUE,  "-x",  INT,    "Image width in pixels",              (char *)&XSize }, 
    { TRUE,  "-cy", INT,    "Clock image height in pixels",       (char *)&YClockSize },
    { TRUE,  "-ty", INT,    "Text image height in pixels",        (char *)&YTextSize },
    { TRUE,  "-help", HELP, "Prints this help message",           NULL },

    { TRUE,  "-bv", FLOAT,  "Big hand value",                     (char *)&BigHandValue }, 
    { TRUE,  "-bs", FLOAT,  "Big hand full scale value",          (char *)&BigHandScale }, 
    { TRUE,  "-lv", FLOAT,  "Little hand value",                  (char *)&LittleHandValue },
    { TRUE,  "-ls", FLOAT,  "Little hand full scale value",       (char *)&LittleHandScale },
    { TRUE,  "-t",  INT,    "Number of ticks around the face",    (char *)&Ticks },
    { TRUE,  "-lw", INT,    "Line width in pixels",               (char *)&Dots },

    { TRUE,  "-fc", COLOR,  "Clock face edges color",             (char *)&FaceEdgeColor }, 
    { TRUE,  "-Fc", COLOR,  "Clock face background color",        (char *)&FaceColor }, 
    { TRUE,  "",    TEXT,   " - if omitted, then the clock is transparent" },  
    { TRUE,  "-hc", COLOR,  "Clock hands edges color",            (char *)&HandEdgeColor },
    { TRUE,  "",    TEXT,   " - if omitted,  no hand edges shown" }, 
    { TRUE,  "-Hc", COLOR,  "Clock hands fill color",             (char *)&HandColor }, 
    { TRUE,  "-tc", COLOR,  "Text color",              (char *)&TextColor}, 
    { TRUE,  "-Tc", COLOR,  "Text background color",              (char *)&TextBackColor}, 
    { TRUE,  "",    TEXT,   " - if omitted, then the text is transparent" },  
    { TRUE,  "-tf", STRING, "Text area format string",               (char *)&FormatString }, 
    { FALSE, "-Xm", BOOL,   "Output the alpha channel on RGB",    (char *)&DebugAlpha },
    { FALSE, "-D",  BOOL,   "Turn on debugging",              (char *)&Debug },
    { FALSE, NULL }
};

void
procargs(argc, argv)
int argc;
char *argv[];
{
    int arg, i;
    color_t *color;

    for ( arg = 1 ; arg<argc ; arg++) {
    for ( i=0 ; Args[i].arg != NULL ; i++) {
        if (Args[i].type != TEXT && strcmp(argv[arg], Args[i].arg) == 0) {
        break;
        }
    }
    if (Args[i].arg==NULL) {
        fprintf(stderr, "Unknown argument: \"%s\"\n", argv[arg]);
        usageExit(argv[0]);
    }
    Args[i].given = TRUE;
    switch (Args[i].type) {
    case HELP:
        usageExit(argv[0]);
        break;
    case INT:
        arg += 1;
        *(int *)Args[i].value = atoi(argv[arg]);
        break;
    case FLOAT:
        arg += 1;
        *(float *)Args[i].value = atof(argv[arg]);
        break;
    case BOOL:
        *(bool *)Args[i].value = TRUE;
        break;
    case STRING:
        arg += 1;
        *(char **)Args[i].value = (char *)malloc(strlen(argv[arg])+1);
        strcpy(*(char **)Args[i].value, argv[arg]);
        break;
    case COLOR:
        color = (color_t *)Args[i].value;
        if ( arg+3 >= argc || !isdigit(argv[arg+1][0]) 
                           || !isdigit(argv[arg+2][0]) 
                   || !isdigit(argv[arg+3][0])) {
        fprintf(stderr, "%s: %s takes three numeric arguments\n", argv[0],
            Args[i].arg);
        usageExit(argv[0]);
        }
        color->red = atoi(argv[arg+1]);
        color->green = atoi(argv[arg+2]);
        color->blue = atoi(argv[arg+3]);
        arg += 3;
        break;
    default:
        break;
    }
    }
}
bool argGiven(argVar)
char *argVar;
{
    int i;

    for ( i=0 ; Args[i].arg != NULL ; i++) {
    if (Args[i].value == argVar) {
        return Args[i].given;
    }
    }
    return FALSE;
}

void
usageExit(pgm)
char *pgm;
{
    int             i;

    fprintf(stderr, "Usage: %s [args]\n", pgm);
    for (i = 0; Args[i].arg != NULL; i++) {
    if (Args[i].show) {
        fprintf(stderr, "\t%s", Args[i].arg);
        switch (Args[i].type) {
        case INT:
        fprintf(stderr, " INT");
        break;
        case FLOAT:
        fprintf(stderr, " FLOAT");
        break;
        case BOOL:
        break;
        case STRING:
        fprintf(stderr, " STR");
        break;
        case COLOR:
        fprintf(stderr, " RED GREEN BLUE");
        break;
        default:
        break;
        }
        fprintf(stderr, " ... %s\n", Args[i].description);
    }
    }
    exit(1);

}

/*
 * charMinMax{Width,Height}
 * ------------------------
 *
 * Compute the minimum and maximum width and height of a character as defined
 * in the font.
 */

void
charMinMaxWidth(ch, min, max)
int ch;
int *min, *max;
{
    int epos, pos;

    if (!isprint(ch)) {
    *min = *max = 0;
    return;
    }
    if (ch == ' ') {
    ch = 'n';
    }
    *min = 999;
    *max = -999;
    epos = Base[(int)ch - 33 + 1];
    pos = Base[(int)ch - 33];
    for (; pos < epos; pos++) {
    if (Moves[pos].x > *max) {
        *max = Moves[pos].x;
    }
    if (Moves[pos].x < *min) {
        *min = Moves[pos].x;
    }
    }
}

void
charMinMaxHeight(ch, min, max)
int ch;
int *min, *max;
{
    int epos, pos;

    if (!isprint(ch)) {
    *min = *max = 0;
    return;
    }
    if (ch == ' ') {
    ch = 'n';
    }
    *min = 999;
    *max = -999;
    epos = Base[(int)ch - 33 + 1];
    pos = Base[(int)ch - 33];
    for (; pos < epos; pos++) {
    if (Moves[pos].y > *max) {
        *max = Moves[pos].y;
    }
    if (Moves[pos].y < *min) {
        *min = Moves[pos].y;
    }
    }
}

/*
 * formatInterp
 * ------------
 *
 * Interpret the format string - returns the value string as specified.
 */

char *
formatInterp(str)
CONST_DECL char *str;
{
    char *buf, *bufp;
    int state;
    static char outBuf[1024];
    char tmpBuf[1024];

    buf = (char *)malloc(strlen(str)*2+1);
    bufp = buf;
    state = 0;
    strcpy(outBuf, "");
    while ( *str != 0) {
    switch (state) {
    case 0:
        *bufp++ = *str;
        if (*str == '%') {
        state = 1;
        }
        break;
    case 1:
        if (isdigit(*str) || *str == '.') {
        *bufp++ = *str;
        } else {
        if ( *str=='B' || *str=='L') {
            *bufp++ = 'f';
            *bufp = 0;
            sprintf(tmpBuf, buf, *str == 'B' ? (float)BigHandValue
                             : (float)LittleHandValue);
        } else if ( *str=='b' || *str=='l') {
            *bufp++ = 'd';
            *bufp = 0;
            sprintf(tmpBuf, buf, *str == 'b' ? (int)BigHandValue
                             : (int)LittleHandValue);
        } else {
            *bufp++ = *str;
            *bufp = 0;
            strcpy(tmpBuf, buf);
        }
        strcat(outBuf, tmpBuf);
        bufp = buf;
        state = 0;
        }
        break;
    }
    str++;
    }
    *bufp = 0;
    strcat(outBuf, buf);
    free(buf);
    return outBuf;
}

/*
 * drawText
 * --------
 *
 * Draw vectors for the given tect string, in the TEXT area of the raster.
 */

#define CHARPAD 25

void
drawText()
{
    char           *string;
    int             i, j, xsize, ysize, min, max, basex;
    int             curx, cury, x, y, pos, epos, charBasex;
    float           scale, scalex;

    string = formatInterp(FormatString);
    xsize = CHARPAD+Dots;
    ysize = 0;
    for (i = 0; string[i] != 0; i++) {
    charMinMaxWidth(string[i], &min, &max);
    xsize += (max - min) + CHARPAD+Dots;
    charMinMaxHeight(string[i], &min, &max);
    if (ysize < (max - min)+Dots) {
        ysize = max - min+Dots;
    }
    }
    scale = (float) YTextSize / (float) ysize;
    scalex = (float) XSize / (float) xsize;
    if (scale > scalex) {
    scale = scalex;
    }
    basex = (XSize - (int) ((float)xsize * scale)) / 2;
    curx = cury = 0;
    charBasex = CHARPAD;
    for (i = 0; string[i] != 0; i++) {
    if (isprint(string[i]) && string[i] != ' ') {
        charMinMaxWidth(string[i], &min, &max);
        epos = Base[((int) (string[i])) - 33 + 1];
        for (pos = Base[(int) string[i] - 33]; pos < epos; pos++) {
        x = basex + (int) (scale * (charBasex + Moves[pos].x + (-min)));
        y = (int) (scale * Moves[pos].y);
        if (Moves[pos].type == 'n') {
            lineDots(curx, YClockSize + Dots + 1 + cury, x, YClockSize + Dots + 1 + y, setDot, RAST_TEXT, Dots);
        }
        curx = x;
        cury = y;
        }
    }
    charBasex += (max-min) + CHARPAD + Dots;
    }
    x = basex + (int)(scale * charBasex);
    if (x > XSize) {
    x = XSize;
    }
    y = scale * ysize + YClockSize+Dots+1;
    if (y+Dots > YSize) {
    y = YSize-Dots;
    }
    for (i = YClockSize+Dots; i < y+Dots ; i++) {
    for (j = basex; j < x; j++) {
        Raster[i][j] |= RAST_TEXT_BACK;
    }
    }
}

/*
 * areaFlood
 * ---------
 *
 * A flooding algorithm for painting regions of the clock
 */

typedef struct {
    short x, y;
    int dir;
} _urt_stack;

#define NORTH 0
#define WEST 1
#define SOUTH 2
#define EAST 3
struct {
    _urt_stack *s;
    int    top;
    int allocked;
} Stack;

int XMove[4] = {0, 1, 0, -1};
int YMove[4] = {1, 0, -1, 0};

void
areaFlood(firstX, firstY, mask, match, value)
int firstX, firstY;
int mask, match, value;
{
    register _urt_stack *sp;

    Stack.s = (_urt_stack *) calloc(256, sizeof(_urt_stack));
    Stack.allocked = 256;
    Stack.top = -1;
    stackPush(firstX, firstY, NORTH);

    while (Stack.top >= 0) {
    sp = &Stack.s[Stack.top];
    if ((Raster[sp->y][sp->x]&mask)==match && (Raster[sp->y][sp->x]&value)!=value) {
        Raster[sp->y][sp->x] |= value;
        if (Debug)
        fprintf(stderr, "Marking %d, %d at stack %d\n", sp->x, sp->y, Stack.top);
        stackPush(sp->x + XMove[sp->dir], sp->y + YMove[sp->dir],
              NORTH);
    } else {
        do {
        if (stackPop())
            break;
        sp = &Stack.s[Stack.top];
        sp->dir++;
        } while (sp->dir >= 4);
        if (Stack.top >= 0)
        stackPush(sp->x + XMove[sp->dir], sp->y + YMove[sp->dir],
              NORTH);
    }
    }
}

void
stackPush(x, y, dir)
int x, y, dir;
{
    if (++Stack.top >= Stack.allocked) {
        Stack.allocked += 256;
        Stack.s = (_urt_stack *) realloc(Stack.s, Stack.allocked * sizeof(_urt_stack));
if(Debug)fprintf(stderr, "Stack growing to %d\n", Stack.allocked);
    }
    Stack.s[Stack.top].x = x;
    Stack.s[Stack.top].y = y;
    Stack.s[Stack.top].dir = dir;
}

int
stackPop()
{
    Stack.top -= 1;
    return Stack.top < 0;
}