This is rleClock.c in view mode; [Download] [Up]
/* * 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. */ #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; char *FormatString = FORMATSTRING; /* * External globals. */ extern move_t Moves[]; extern int Base[]; /* * Forward declarations */ int polarToX(); int polarToY(); float radians(); void setDot(); rle_pixel **rasterAllocate(); char *formatInterp(); bool argGiven(); char **gargv; 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); } 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; } } drawHand(place, scale, radius, mask, edge) float place; float scale; float 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); } 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; } } } polarLine(r0, a0, r1, a1, arg1, arg2) float 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) float fRadius; float angle; { return (int)(fRadius * sin(radians(angle)) * XRadius) + XSize/2; } int polarToY(fRadius, angle) float fRadius; float angle; { return (int)(fRadius * cos(radians(angle)) * YRadius) + YRadius; } float radians(degrees) float 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. */ rasterWrite(fd) FILE *fd; { rle_pixel *rows[4]; int i; RLE_SET_BIT(rle_dflt_hdr, RLE_RED); RLE_SET_BIT(rle_dflt_hdr, RLE_GREEN); RLE_SET_BIT(rle_dflt_hdr, RLE_BLUE); RLE_SET_BIT(rle_dflt_hdr, RLE_ALPHA); rle_dflt_hdr.rle_file = fd; rle_dflt_hdr.xmax = XSize; rle_dflt_hdr.ymax = YSize; rle_dflt_hdr.alpha = 1; rle_addhist( gargv, NULL, &rle_dflt_hdr ); rle_put_setup(&rle_dflt_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, &rle_dflt_hdr); } } /* * 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! */ 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; char *arg; enum { INT, FLOAT, STRING, BOOL, COLOR, HELP, TEXT } type; char *description; 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 }, NULL }; 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; } } } 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; } 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; } 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. */ charMinMaxWidth(ch, min, max) char 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; } } } charMinMaxHeight(ch, min, max) char 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) 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 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; } stack_t; #define NORTH 0 #define WEST 1 #define SOUTH 2 #define EAST 3 struct { stack_t *s; int top; int allocked; } Stack; int XMove[4] = {0, 1, 0, -1}; int YMove[4] = {1, 0, -1, 0}; areaFlood(firstX, firstY, mask, match, value) int firstX, firstY; int mask, match, value; { register stack_t *sp; Stack.s = (stack_t *) calloc(256, sizeof(stack_t)); 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); } } } stackPush(x, y, dir) int x, y, dir; { if (++Stack.top >= Stack.allocked) { Stack.allocked += 256; Stack.s = (stack_t *) realloc(Stack.s, Stack.allocked * sizeof(stack_t)); 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; } stackPop() { Stack.top -= 1; return Stack.top < 0; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.