Morphing on Your PC by Scott Anderson [LISTING ONE] /**************************************************************** * FILE: linecalc.c * DESC: Warping calculations and line-handling functions. * HISTORY: Created 3/11/1993 LAST CHANGED: 5/ 6/1993 * Copyright (c) 1993 by Scott Anderson ****************************************************************/ /* ----------------------INCLUDES----------------------------- */ #include #include #include #include #include #include #include #include #include "define.h" /* -----------------------MACROS------------------------------ */ #define PIXEL(p,x,y) (p->pixmap[y * (long) p->wide + x]) #define SQUARE(x) (((long) x)*(x)) /* ----------------------PROTOTYPES--------------------------- */ /**** line routines ****/ int xorLine(int x1, int y1, int x2, int y2); int getLine(int *argx1, int *argy1, int *argx2, int *argy2); int findPoint(LINE_LIST *lineList, int * line, int * point, int x, int y); int movePoint(); /**** warping and morphing routines ****/ int sumLines(PICTURE *picture, COLOR *color, LINE *origline, POINT *warp, LINE *warpline); float getSourceLoc(POINT *orig, LINE *origline, POINT *warp, LINE *warpline); int setLength(LINE *line); void setupScreen(PICTURE *pic, int editFlag); /* ----------------------EXTERNALS---------------------------- */ /* set from last picture loaded */ extern int Xmin, Ymin, Xmax, Ymax; extern int NumLines; extern LINE SrcLine[MAX_LINES]; extern LINE DstLine[MAX_LINES]; /* ----------------------GLOBAL DATA-------------------------- */ int TargFlag=0; /******** These are the basic warping calculations **********/ * FUNC: int sumLines(PICTURE *picture, COLOR *color, * LINE *origline, POINT *warp, LINE *warpline) * DESC: Sum and weight the contribution of each warping line *****************************************************************/ int sumLines(PICTURE *picture, COLOR *color, LINE *origline, POINT *warp, LINE *warpline) { int x, y; float weight, weightSum; float distance; int line; POINT orig; int paletteIndex; float deltaSumX = 0.0; float deltaSumY = 0.0; /* if no control lines, get an unwarped pixel */ if (NumLines == 0) orig = *warp; else { weightSum = 0.0; for (line = 0; line < NumLines; line++, origline++, warpline++) { distance = getSourceLoc(&orig,origline,warp,warpline); weight = 1/(.001+distance*distance); deltaSumX += (orig.x - warp->x) * weight; deltaSumY += (orig.y - warp->y) * weight; weightSum += weight; } orig.x = warp->x + deltaSumX / weightSum + .5; orig.y = warp->y + deltaSumY / weightSum + .5; } /* clip it to the nearest border pixel */ x = clip(orig.x, Xmin, Xmax); y = clip(orig.y, Ymin, Ymax); paletteIndex = PIXEL (picture, x, y); color->r = picture->pal.c[paletteIndex].r; color->g = picture->pal.c[paletteIndex].g; color->b = picture->pal.c[paletteIndex].b; return (paletteIndex); } /***************************************************************** * FUNC: float getSourceLoc(POINT *orig, LINE *origline, * POINT *warp, LINE *warpline) * DESC: For a given line, locate the corresponding warped pixel *****************************************************************/ float getSourceLoc(POINT *orig, LINE *origline, POINT *warp, LINE *warpline) { float fraction, fdist; int dx, dy; float distance; dx = warp->x - warpline->p[0].x; dy = warp->y - warpline->p[0].y; fraction = (dx * (long) warpline->delta_x + dy * (long) warpline->delta_y) / (float) (warpline->length_square); fdist = (dx * (long) -warpline->delta_y + dy * (long) warpline->delta_x) / (float) warpline->length; if (fraction <= 0 ) distance = sqrt(dx*(long) dx + dy * (long) dy); else if (fraction >= 1) { dx = warp->x - warpline->p[1].x; dy = warp->y - warpline->p[1].y; distance = sqrt(dx*(long) dx + dy * (long) dy); } else if (fdist >= 0) distance = fdist; else distance = -fdist; orig->x = origline->p[0].x + fraction * origline->delta_x - fdist * origline->delta_y / (float) origline->length + .5; orig->y = origline->p[0].y + fraction * origline->delta_y + fdist * origline->delta_x / (float) origline->length + .5; return distance; } /***************************************************************** * FUNC: int setLength(LINE *line) * DESC: Set the deltas, the length and the length squared for a given line. *****************************************************************/ int setLength (LINE *line) { line->delta_x = line->p[1].x - line->p[0].x; line->delta_y = line->p[1].y - line->p[0].y; line->length_square = SQUARE(line->delta_x) + SQUARE(line->delta_y); line->length = sqrt(line->length_square); } /********************* The line routines **********************/ * FUNC: int xorLine(int x1, int y1, int x2, int y2) * DESC: Draw a line on the screen using the XOR of the screen index. *****************************************************************/ int xorLine(int x1, int y1, int x2, int y2) { int oldcolor = _getcolor(); _setcolor(WHITE); /* Use white as the xor color */ _setwritemode(_GXOR); _moveto (x1,y1); _lineto (x2,y2); _setcolor(oldcolor); /* restore the old color */ } /***************************************************************** * FUNC: int getLine(int *argx1, int *argy1, int *argx2, int*argy2) * DESC: Input a line on the screen with the mouse. *****************************************************************/ int getLine (int *argx1, int *argy1, int *argx2, int *argy2) { int x1,y1, x2,y2; int oldx, oldy; int input; /* save the current mode */ short old_mode = _getwritemode(); /* get input until we have a real line, not just a point */ do { /* wait for button or key press */ while (!(input = mousePos (&x1, &y1))); if (input & KEYPRESS) { _setwritemode(old_mode); return 1; } oldx=x1, oldy=y1; hideMouse(); /* prime the pump with this dot */ xorLine (x1, y1, oldx, oldy); showMouse(); while (input = mousePos (&x2, &y2)) { /* rubber band a line while the mouse is dragged */ if (x2 != oldx || y2 != oldy) { hideMouse(); xorLine (x1, y1, oldx, oldy); xorLine (x1, y1, x2, y2); showMouse(); oldx=x2, oldy=y2; } } } while (x1 == x2 && y1 == y2); *argx1 = x1, *argy1 = y1; *argx2 = x2, *argy2 = y2; _setwritemode(old_mode); /* get out of XOR mode */ return (0); } /***************************************************************** * FUNC: int findPoint(LINE_LIST *lineList,int * line,int * point,int x, int y) * DESC: loop thru dstline and find point within GRAB_DISTANCE, * return 1 if found, 0 otherwise. *****************************************************************/ int findPoint (LINE_LIST *lineList, int * line, int * point, int x, int y) { int l, p; int minl, minp; long length; long minlength = SQUARE(640) + SQUARE(480); for (l = 0; l < lineList->number; l++) { for (p = 0; p <= 1; p++) { length = SQUARE(lineList->line[l].p[p].x - x) + SQUARE(lineList->line[l].p[p].y - y); if (length < minlength) { minlength = length; minl = l; minp = p; } } } if (minlength > GRAB_DISTANCE) return 0; *line = minl; *point = minp; return 1; } /***************************************************************** * FUNC: int movePoint(LINE_LIST *lineList) * DESC: Grab a point and move it. Return 1 when key is pressed, else return 0. *****************************************************************/ int movePoint(LINE_LIST *lineList) { int stuckx, stucky, movex,movey; int oldx, oldy; int input; int line, point; /* save the current mode */ short old_mode = _getwritemode(); do { /* keep getting input until we have a mouse button */ while (!(input = mousePos (&movex, &movey))); if (input & KEYPRESS) { _setwritemode(old_mode); return 1; } if (!findPoint(lineList, &line, &point, movex, movey)) { _setwritemode(old_mode); return 0; } /* establish fixed end point */ stuckx = lineList->line[line].p[1-point].x; stucky = lineList->line[line].p[1-point].y; oldx=movex, oldy=movey; hideMouse(); /* erase the old line */ xorLine (stuckx, stucky, lineList->line[line].p[point].x, lineList->line[line].p[point].y); /* and prime the pump with the new line */ xorLine (stuckx, stucky, oldx, oldy); showMouse(); while (input = mousePos (&movex, &movey)) { /* rubber band a line while the mouse is dragged */ if (movex != oldx || movey != oldy) { hideMouse(); xorLine (stuckx, stucky, oldx, oldy); xorLine (stuckx, stucky, movex, movey); showMouse(); oldx=movex, oldy=movey; } } } while (stuckx == movex && stucky == movey); lineList->line[line].p[point].x = movex; lineList->line[line].p[point].y = movey; _setwritemode(old_mode); /* get out of XOR mode */ return (0); } /***************************************************************** * FUNC: void createLines(PICTURE *pic, LINE_LIST *lineList) * DESC: create a list of line segments for a picture *****************************************************************/ void createLines(PICTURE *pic, LINE_LIST *lineList) { setupScreen(pic, 0); /* set for enter prompt */ initMouse(); showMouse(); for (lineList->number = 0;lineList->number < MAX_LINES; lineList->number++) { if (getLine(&lineList->line[lineList->number].p[0].x, &lineList->line[lineList->number].p[0].y, &lineList->line[lineList->number].p[1].x, &lineList->line[lineList->number].p[1].y)) break; } hideMouse(); } /***************************************************************** * FUNC: void editLines(PICTURE *pic, LINE_LIST *lineList) * DESC: move around some existing lines *****************************************************************/ void editLines(PICTURE *pic, LINE_LIST *lineList) { int segment; setupScreen(pic, 1); /* set for edit prompt */ initMouse(); for (segment = 0; segment < lineList->number; segment++) { xorLine(lineList->line[segment].p[0].x, lineList->line[segment].p[0].y, lineList->line[segment].p[1].x, lineList->line[segment].p[1].y); } showMouse(); /* move the endpoints around */ while(!movePoint(lineList)); hideMouse(); } /***************************************************************** * FUNC: void setupScreen(PICTURE *pic, int editFlag) * DESC: Print a message introducing the screen, wait for input, * then set the graphics mode and display the screen. *****************************************************************/ void setupScreen(PICTURE *pic, int editFlag) { static char *editMess[2] = {"enter", "edit"}; static char *targMess[2] = {"source", "target"}; setTextMode(); _settextposition(VTAB, HTAB); printf("When you are ready to %s the control lines", editMess[editFlag]); _settextposition(VTAB+2, HTAB); printf("for the %s image, press any key.", targMess[TargFlag]); waitForKey(); setGraphicsMode(); displayPicture(pic); } [LISTING TWO] /**************************************************************** * FILE: define.h * DESC: These are the main defines for dissolve, warp, morph, load and fix. * HISTORY: Created 1/11/1993 LAST CHANGED: 5/ 6/1993 * Copyright (c) 1993 by Scott Anderson ****************************************************************/ /* ----------------------DEFINES------------------------------ */ #define ON 1 #define OFF 0 #define MAX_TWEENS 99 /* Maximum tweens (2 digits) */ /* minus 2 digit tween# appended to end */ #define MAX_NAME_SIZE (8-2) #define HEADER_LEN 128 /* PCX header length */ /* Number of colors in the palette */ #define COLORS 256 /* bytes in palette (COLORS*3) */ #define PALETTE_SIZE (3*COLORS) /* Maximum number of morphing lines */ #define MAX_LINES 32 /* max number of pixels wide we handle */ #define MAX_WIDE 320 /* max number of pixels tall we handle */ #define MAX_TALL 200 /* Size of screen buffer */ #define MAX_BYTES (MAX_WIDE*(long) MAX_TALL) /* Number of components per color (RGB) */ #define COMPS 3 /* largest color component value */ #define MAX_COMP 32 /* the midpoint of the colors - for gray */ #define MID_COMP (MAX_COMP/2) /* enough to handle about 10 different palettes */ #define MAX_FREQ 1023 #define MAX_FILES 10 /* length of a file name including directory */ #define MAX_PATHLEN 80 #define ENTER 13 /* Keyboard values */ #define ESC 27 #define HTAB 18 /* Position for text messages */ #define VTAB 8 /* The mouse button & keyboard constants */ #define NO_BUTTON 0 #define LEFT_BUTTON 1 #define RIGHT_BUTTON 2 #define KEYPRESS 4 /* the square of min dist for grabbing pt */ #define GRAB_DISTANCE 25 /* Some of the graphics colors */ #define BLACK 0 #define WHITE 255 #define EXT_PCX ".PCX" /* pcx file extension */ /* primary line file holder extension */ #define EXT_LINE1 ".LN1" #define EXT_LINE2 ".LN2" /* aux file for warp lines */ #define ERROR -1 /* General-purpose error code */ typedef enum { NO_ERROR, /* first entry means everything is ok */ MEMORY_ERR, /* Not enough memory */ READ_OPEN_ERR, /* Couldn't open file for reading */ READ_ERR, /* Trouble reading the file */ WRITE_OPEN_ERR, /* Couldn't open the file for writing */ WRITE_ERR, /* Couldn't write the file */ MOUSE_ERR, /* No mouse driver found */ WRONG_PCX_FILE, /* PCX file format not supported yet */ READ_CONTENTS_ERR /* error in .LN file */ } ERR; /* -----------------------MACROS------------------------------ */ #define MIN(a,b) (((a)<(b)) ? (a) : (b)) #define PIXEL(p,x,y) (p->pixmap[y * (long) p->wide + x]) #define SQUARE(x) (((long) x)*(x)) /* ----------------------TYPEDEFS----------------------------- */ typedef struct { int x,y; /* the screen coordinates of the point */ } POINT; typedef struct { POINT p[2]; } LINE_SEGMENT; typedef struct { int number; /* number of segments to follow */ LINE_SEGMENT line[MAX_LINES]; char *filename; /* name of file holding the line list */ } LINE_LIST; typedef struct { POINT p[2]; /* the endpoints */ int delta_x, delta_y; /* x & y displacement */ float length; /* the precalculated length of the line */ long length_square; /* the length squared */ } LINE; typedef struct { /* red, green, and blue color components */ unsigned char r, g, b; } COLOR; typedef struct { COLOR c[COLORS]; /* a 256 entry palette */ } PALETTE; typedef struct { int xmin, ymin; /* the upper left corner */ int xmax, ymax; /* the lower right corner */ int wide, tall; /* the width and height */ int pal_id; /* an ID number for each palette */ PALETTE pal; /* the actual palette is here */ unsigned char far *pixmap; /* a pointer to the pixel map */ } PICTURE; typedef struct linko { struct linko *next; char *str; } LINKED_LIST; /* ----------------------PROTOTYPES--------------------------- */ /**** file handling routines ****/ extern PICTURE *loadPicture(char *filename); extern int loadPalette(FILE *fp, PALETTE *palette); extern int getBlock (unsigned char *byte, int *count, FILE *fp); extern int mustRead(FILE *fp, char *buf, int n); extern int saveScreen(PALETTE *pal); extern int putBlock(unsigned char num, unsigned char color, FILE *fp); extern int writeByte(unsigned char *byte, FILE *fp); /**** screen and color routines ****/ extern int defaultPalette(PALETTE *palette); extern int setPalette(PALETTE *palette); extern int displayPicture(PICTURE *picture); extern int displayNoPal(PICTURE *picture); extern int freePicture(PICTURE *pic); /**** mouse routines ****/ extern int initMouse(); extern int hideMouse(); extern int showMouse(); extern int mousePos(int *x, int *y); /**** general purpose routines ****/ extern int clip(int num, int min, int max); extern int quitCheck(); extern void quit(int err, char *name); extern int wait(int count); extern int waitForKey(); extern char lineAsk(char *name); /* ----------------------GLOBAL DATA-------------------------- */ extern int TargFlag; extern int Key; [LISTING THREE] /**************************************************************** * FILE: morph.c * DESC: Create a metamorphosing sequence between two given images. This * program lets you specify two files to morph, then prompts you for * control lines. It uses the lines to warp the underlying images a step * at a time, combine them, and optionally save them as numbered PCX files. * HISTORY: Created 1/13/1993 LAST CHANGED: 5/ 6/1993 * Copyright (c) 1993 by Scott Anderson ****************************************************************/ /* ----------------------INCLUDES----------------------------- */ #include #include #include #include #include #include #include #include #include "define.h" /* ----------------------DEFINES------------------------------ */ #define MORPH_TWEENS 1 /* ----------------------PROTOTYPES--------------------------- */ int tweenMorph(PICTURE *src, PICTURE *dst); /* ----------------------EXTERNALS---------------------------- */ /**** color routines ****/ extern int closestColor(int r, int g, int b, PALETTE *palPtr); extern void collapseColors(PALETTE *palPtr); /**** line routines ****/ extern int setLength(LINE *line); extern int sumLines(PICTURE *picture, COLOR *color, LINE *origline, POINT *warp, LINE *warpline); /**** io routines ****/ extern LINE_LIST *loadLines(char *filename, char *extension); extern void saveLines(char *filename, LINE_LIST *lineList, char *extension); /***** variables used to compute intermediate images ****/ /* number of colors in tweened image before reduction*/ extern int Ncolors; /* r, g, b frequency counter array */ extern unsigned int far Freq[MAX_COMP][MAX_COMP][MAX_COMP]; /* tweened images red, grn, and blu components*/ extern unsigned char far Red[MAX_WIDE][MAX_TALL]; extern unsigned char far Grn[MAX_WIDE][MAX_TALL]; extern unsigned char far Blu[MAX_WIDE][MAX_TALL]; extern PALETTE TweenPal; /* resulting palette */ /**** other variables ****/ extern char *OutFilename; /* set from last picture loaded */ extern int Xmin, Ymin, Xmax, Ymax; /* ID of palette currently being displayed */ extern int CurrentPal; /* ----------------------GLOBAL DATA-------------------------- */ PICTURE *Src; /* source & destination picture pointers */ PICTURE *Dst; LINE SrcLine[MAX_LINES]; LINE DstLine[MAX_LINES]; int Tweens; int NumLines; /***************************************************************** * FUNC: main (int argc, char *argv[]) * DESC: Read in a filename to load *****************************************************************/ main (int argc, char *argv[]) { int segment; LINE_LIST *lineSrcList; LINE_LIST *lineDstList; char answer; /* load the pcx file if one is given */ if ((3 > argc) || (argc > 5)) { printf("Usage: morph [ []]\n\n"); printf("Where: is the source PCX filename\n"); printf(" is the destination filename\n"); printf(" is the optional sequence size\n"); printf(" (the max is %d, the default is %d)\n", MAX_TWEENS, MORPH_TWEENS+2); printf(" is the optional output filename\n"); printf(" (defaults to no output)\n\n"); printf("Note: The output filename can be at most %d characters long.\n", MAX_NAME_SIZE); printf(" The PCX extension is added automatically, so don't\n"); printf(" include it in the filename.\n"); printf(" Morph only accepts PCX files with %d X %d resolution\n", MAX_WIDE, MAX_TALL); printf(" and %d colors.\n", COLORS); exit(0); } if (argc > 3) { /* subtract two from the series count to get the tweens * since the starting and ending frame are included. */ Tweens = clip (atoi(argv[3]) - 2, 1, MAX_TWEENS); if (argc > 4) OutFilename = argv[4]; } else Tweens = MORPH_TWEENS; printf("Loading the file %s\n", argv[1]); Src = loadPicture(argv[1]); if (Src == NULL) quit(MEMORY_ERR, ""); printf("Loading the file %s\n", argv[2]); Dst = loadPicture(argv[2]); if (Dst == NULL) quit(MEMORY_ERR, ""); lineSrcList = loadLines(argv[1], EXT_LINE1); if (lineSrcList->number != 0) { if (lineAsk(argv[1]) == ‘N') createLines(Src, lineSrcList); else editLines(Src, lineSrcList); } else createLines(Src, lineSrcList); TargFlag = 1; /* For the screen intro message */ NumLines = lineSrcList->number; if (NumLines) { lineDstList = loadLines(argv[2], EXT_LINE1); /* inconsistent warp target*/ if (lineDstList->number != NumLines) lineDstList->number = 0; if (lineDstList->number) { /* ask what he wants to do */ if (lineAsk(argv[2]) == ‘N') lineDstList->number = 0; } if (lineDstList->number == 0) { /* create a warp target */ /* copy the source lines */ lineDstList->number = NumLines; for (segment = 0; segment < NumLines; segment++) lineDstList->line[segment] = lineSrcList->line[segment]; } editLines(Dst, lineDstList); saveLines(argv[1], lineSrcList, EXT_LINE1); saveLines(argv[2], lineDstList, EXT_LINE1); beep(); for (segment = 0; segment < NumLines; segment++) { DstLine[segment].p[0]=lineDstList->line[segment].p[0]; DstLine[segment].p[1]=lineDstList->line[segment].p[1]; setLength(&DstLine[segment]); SrcLine[segment].p[0]=lineSrcList->line[segment].p[0]; SrcLine[segment].p[1]=lineSrcList->line[segment].p[1]; setLength(&SrcLine[segment]); } } tweenMorph(Src, Dst); setTextMode(); } /***************************************************************** * FUNC: int tweenMorph(PICTURE *src, PICTURE *dst) * DESC: calculate a pixel to plot, from the warping function *****************************************************************/ #define TOTAL_WEIGHT (100) /* Good for up to 99 tweens */ tweenMorph(PICTURE *src, PICTURE *dst) { int color; POINT warp; int x,y; COLOR scolor, dcolor; LINE warpLine[MAX_LINES]; int t, i, p; int r, g, b; unsigned int srcweight, srcpaletteindex; unsigned int dstweight, dstpaletteindex; displayPicture(src); saveScreen(&src->pal); /* src is on screen, now tween to the target */ for (t = 1; t <= Tweens; t++) { /* Tween the lines used to warp the images */ for (i = 0; i < NumLines; i++) { for (p = 0; p < 2; p++) { warpLine[i].p[p].x = SrcLine[i].p[p].x + ((DstLine[i].p[p].x - SrcLine[i].p[p].x) * t) /(Tweens+1); warpLine[i].p[p].y = SrcLine[i].p[p].y + ((DstLine[i].p[p].y - SrcLine[i].p[p].y) * t) /(Tweens+1); } setLength(&warpLine[i]); } dstweight = t * TOTAL_WEIGHT / (Tweens+1); srcweight = TOTAL_WEIGHT - dstweight; /* Zero out the buffers */ initFreq(); /* set background to black */ _fmemset(Red, 0, sizeof Red); _fmemset(Grn, 0, sizeof Grn); _fmemset(Blu, 0, sizeof Blu); /* Go through the screen and get warped source pixels */ for (warp.y = Ymin; warp.y <= Ymax; warp.y++) { if (quitCheck()) quit(0, ""); for (warp.x = Xmin; warp.x <= Xmax; warp.x++) { sumLines(src, &scolor, SrcLine, &warp, warpLine); sumLines(dst, &dcolor, DstLine, &warp, warpLine); r = (scolor.r * srcweight + dcolor.r * dstweight) / TOTAL_WEIGHT; g = (scolor.g * srcweight + dcolor.g * dstweight) / TOTAL_WEIGHT; b = (scolor.b * srcweight + dcolor.b * dstweight) / TOTAL_WEIGHT; if (Freq[r][g][b] == 0) /* A new color */ Ncolors++; /* Keep it to one byte */ if (Freq[r][g][b] < MAX_FREQ) Freq[r][g][b]++; /* put RGB components into temporary buffer */ Red[warp.x][warp.y] = r; Grn[warp.x][warp.y] = g; Blu[warp.x][warp.y] = b; } } collapseColors(&TweenPal); setPalette(&TweenPal); for (y = Ymin; y <= Ymax; y++) { if (quitCheck()) quit(0, ""); for (x = Xmin; x <= Xmax; x++) { color = closestColor( Red[x][y], Grn[x][y], Blu[x][y], &TweenPal); _setcolor (color); _setpixel (x, y); } } /* no output file name on command line */ if (!OutFilename) { beep(); waitForKey(); /* so pause to enjoy the pictures */ } else saveScreen(&TweenPal); } if (OutFilename) { /* save the last pic in this series */ CurrentPal = 0; /* force a new palette */ displayPicture(dst); saveScreen(&dst->pal); } } [LISTING FOUR] /**************************************************************** * FILE: load.c * DESC: This program loads a PCX file or a list of them. It crams as * many into memory as it can, then it flips quickly through them. * HISTORY: Created 1/13/1993 LAST CHANGED: 3/20/1993 * Copyright (c) 1993 by Scott Anderson ****************************************************************/ /* ----------------------INCLUDES----------------------------- */ #include #include #include #include #include #include #include "define.h" /* ----------------------EXTERNALS---------------------------- */ /* External functions */ extern int quitCheck(); extern LINKED_LIST *rootSequence(int argc, char *argv[]); /* External variables */ extern int Wait; extern int Key; extern int EndWait; /* ----------------------GLOBAL DATA-------------------------- */ PICTURE *Src[MAX_FILES]; /* source picture pointer */ /***************************************************************** * FUNC: main (int argc, char *argv[]) * DESC: Display the file or sequence passed on the command line. Read in as * many files as will fit in memory, then display them in a loop. *****************************************************************/ main (int argc, char *argv[]) { int file, fileNum; int direction; int i; LINKED_LIST *pcxList; LINKED_LIST *pcxListHead; if (argc == 1) { printf("Usage: load \n\n"); printf("Where: is the root name of a sequence\n"); exit(23); } setGraphicsMode(); file = 1; pcxListHead = rootSequence(argc, argv); for (pcxList=pcxListHead; pcxList; pcxList = pcxList->next) { Src[file] = loadPicture(pcxList->str); if (Src[file] == NULL) break; displayPicture(Src[file++]); } fileNum = file - 1; if (fileNum == 1) /* there's only one file */ waitForKey(); /* so wait for the user to quit */ else if (fileNum > 1) { file = 1; direction = 1; while (!(quitCheck())) { if ((file += direction) >= fileNum) direction = -1; if (file <= 1) direction = 1; displayPicture(Src[file]); if (EndWait && (file == 1 || file == fileNum)) wait(Wait); } } /* Reset to original mode, then quit */ setTextMode(); } [LISTING FIVE] /**************************************************************** * FILE: fix.c * DESC: This program inputs a list of pictures, creates a best * fit palette, remaps the pictures, and writes them out. * HISTORY: Created 1/13/1993 LAST CHANGED: 3/10/1993 * Copyright (c) 1993 by Scott Anderson ****************************************************************/ /* ----------------------INCLUDES----------------------------- */ #include #include #include #include #include #include #include #include #include "define.h" /* ----------------------EXTERNALS---------------------------- */ extern LINKED_LIST *rootSequence(int argc, char *argv[]); /**** color routines ****/ extern int closestColor(int r, int g, int b, PALETTE *pal); extern void collapseColors(PALETTE *palPtr); extern int mergePalette(PICTURE *pic); extern int remapPicture(PICTURE *picPtr, PALETTE *palPtr); /**** line routines ****/ extern int getLine(int *argx1, int *argy1, int *argx2, int *argy2); extern int movePoint(); extern int setLength(LINE *line); /**** other variables ****/ extern char *OutFilename; /* set from last picture loaded */ extern int Xmin, Ymin, Xmax, Ymax; /* ----------------------GLOBAL DATA-------------------------- */ PICTURE *Src; /* source & destination picture pointers */ /***** variables used to compute intermediate images ****/ /* number of colors in tweened image before reduction*/ extern int Ncolors; /* r, g, b frequency counter array */ extern unsigned int far Freq[MAX_COMP][MAX_COMP][MAX_COMP]; /* tweened images red, grn, and blu components*/ extern unsigned char far Red[MAX_WIDE][MAX_TALL]; extern unsigned char far Grn[MAX_WIDE][MAX_TALL]; extern unsigned char far Blu[MAX_WIDE][MAX_TALL]; extern PALETTE TweenPal; /* resulting palette */ /***************************************************************** * FUNC: main (int argc, char *argv[]) * DESC: Read in a list of filenames to load, change their palettes * to the best-fit palette, and write them out. *****************************************************************/ main (int argc, char *argv[]) { int file; LINKED_LIST *pcxList, *pcxListHead; /* load the pcx file if one is given */ if (argc < 3) { printf("Usage: fix \n\n"); printf("Where: is the input sequence name\n"); printf(" is the output sequence name\n"); exit(0); } OutFilename = argv[argc-1]; initFreq(); pcxListHead = rootSequence(argc-1, argv); for (pcxList = pcxListHead; pcxList; pcxList=pcxList->next) { printf("Loading the file %s\n", pcxList->str); Src = loadPicture(pcxList->str); if (Src == NULL) quit(MEMORY_ERR, ""); mergePalette(Src); freePicture(Src); } collapseColors(&TweenPal); setGraphicsMode(); setPalette(&TweenPal); for (pcxList = pcxListHead; pcxList; pcxList=pcxList->next) { Src = loadPicture(pcxList->str); if (Src == NULL) quit(MEMORY_ERR, ""); remapPicture(Src, &TweenPal); displayNoPal(Src); saveScreen(&TweenPal); freePicture(Src); } setTextMode(); } [LISTING SIX] /**************************************************************** * FILE: color.c * DESC: This file contains the color routines used by morph, dissolve and fix. * HISTORY: Created 3/18/1993 LAST CHANGED: 5/ 6/1993 * Copyright (c) 1992 by Scott Anderson ****************************************************************/ #include #include #include "define.h" /* ----------------------DEFINES------------------------------ */ /* ----------------------TYPEDEFS/STRUCTS--------------------- */ /* ----------------------PROTOTYPES--------------------------- */ int closestColor(int r, int g, int b, PALETTE *palPtr); void collapseColors(PALETTE *palPtr); int mergePalette(PICTURE *pic); int remapPicture(PICTURE *picPtr, PALETTE *palPtr); int initFreq(); /* ----------------------EXTERNALS---------------------------- */ /* set from last picture loaded */ extern int Xmin, Ymin, Xmax, Ymax; /* ----------------------GLOBAL DATA-------------------------- */ /* number of colors in tweened image before reduction*/ int Ncolors; /* r, g, b frequency counter array */ unsigned int far Freq[MAX_COMP][MAX_COMP][MAX_COMP]; /* tweened images red, grn, and blu components*/ unsigned char far Red[MAX_WIDE][MAX_TALL]; unsigned char far Grn[MAX_WIDE][MAX_TALL]; unsigned char far Blu[MAX_WIDE][MAX_TALL]; PALETTE TweenPal; /* resulting palette */ /***************************************************************** * FUNC: void collapseColors(PALETTE *palPtr) * DESC: Collapse the colors in the Freq table until * Ncolors < COLORS, then put it in the given color palette. *****************************************************************/ void collapseColors(PALETTE *palPtr) { int freqCutoff; int r, g, b; int index; int ncolors; static int freqCount[MAX_FREQ+1]; memset(freqCount, 0, sizeof freqCount); for (r = 0; r < MAX_COMP; r++) for (g = 0; g < MAX_COMP; g++) for (b = 0; b < MAX_COMP; b++) freqCount[Freq[r][g][b]]++; ncolors = 0; for (freqCutoff = COLORS-1; freqCutoff > 1; freqCutoff--) { ncolors += freqCount[freqCutoff]; if (ncolors > COLORS) break; } /* Collapse color space to 256 colors */ r = g = b = 0; while (Ncolors >= COLORS) { for (; r < MAX_COMP; r++, g=0) { for (; g < MAX_COMP; g++, b=0) { for (; b < MAX_COMP; b++) { if (Freq[r][g][b] && Freq[r][g][b] <= freqCutoff) goto castOut; /* the ultimate no no */ } } } r = g = b = 0; freqCutoff++; continue; castOut: Freq[r][g][b] = 0; /* just remove this low freq color */ Ncolors--; } /* build a palette out of all the remaining non zero freq's */ index = 0; for (r = 0; r < MAX_COMP; r++) for (g = 0; g < MAX_COMP; g++) for (b = 0; b < MAX_COMP; b++) /* we have a color we need to map */ if (Freq[r][g][b]) { palPtr->c[index].r = r; palPtr->c[index].g = g; palPtr->c[index].b = b; /* remember index in palette */ Freq[r][g][b] = index; index++; } } /***************************************************************** * FUNC: int closestColor(int r, int g, int b, PALETTE *palPtr) * DESC: return the palette index of the color closest to rgb. *****************************************************************/ int closestColor(int r, int g, int b, PALETTE *palPtr) { int index; int distance; int min_distance = 3200; /* a big number */ int min_index; /* The value in Freq is now the index into the color table */ if (Freq[r][g][b]) return Freq[r][g][b]; /* If zero, search for the closest color */ for (index = 1; index < Ncolors; index++) { /* this is really the distance squared, but it works */ distance = SQUARE (r - palPtr->c[index].r) + SQUARE (g - palPtr->c[index].g) + SQUARE (b - palPtr->c[index].b); if (distance < min_distance) { min_distance = distance; min_index = index; if (distance <= 2) break; /* close enough! */ } } /* New index - for future reference */ Freq[r][g][b] = min_index; return min_index; } /***************************************************************** * FUNC: int mergePalette(PICTURE *picPtr) * DESC: Merge a palette into Freq count table. *****************************************************************/ int mergePalette(PICTURE *picPtr) { int r, g, b; unsigned int pos; unsigned char index; PALETTE *palPtr = &picPtr->pal; unsigned char far *bufPtr = picPtr->pixmap; for (pos = 0; pos < MAX_BYTES; pos++) { index = *bufPtr++; r = palPtr->c[index].r; g = palPtr->c[index].g; b = palPtr->c[index].b; if (Freq[r][g][b] == 0) /* A new color */ Ncolors++; if (Freq[r][g][b] < MAX_FREQ) /* Keep it managable */ Freq[r][g][b]++; } } /***************************************************************** * FUNC: int remapPicture(PICTURE *picPtr, PALETTE *palPtr) * DESC: Remap a picture with a different palette. *****************************************************************/ int remapPicture(PICTURE *picPtr, PALETTE *palPtr) { int x, y; int index; int r, g, b; unsigned int pos; unsigned char lookup[COLORS]; unsigned char far *bufPtr; /* Create the cross-reference lookup table */ for (index = 0; index < COLORS; index++) { r = picPtr->pal.c[index].r; g = picPtr->pal.c[index].g; b = picPtr->pal.c[index].b; lookup[index] = closestColor(r, g, b, palPtr); } /* Save the new palette in the picture's palette */ for (index = 0; index < COLORS; index++) { picPtr->pal.c[index].r = palPtr->c[index].r; picPtr->pal.c[index].g = palPtr->c[index].g; picPtr->pal.c[index].b = palPtr->c[index].b; } /* Remap the individual pixels to point to the new colors */ for (bufPtr = picPtr->pixmap, pos = 0; pos < MAX_BYTES; bufPtr++, pos++) *bufPtr = lookup[*bufPtr]; } /***************************************************************** * FUNC: int initFreq() * DESC: zero out the frequency color space table *****************************************************************/ int initFreq() { int bytes = (sizeof Freq) / 2; _fmemset(Freq, 0, bytes); /* divide because of element size */ _fmemset(Freq+(bytes/sizeof *Freq), 0, bytes); /* Guarantee a black color */ Freq[0][0][0] = MAX_FREQ; /* a grey color */ Freq[MID_COMP-1][MID_COMP-1][MID_COMP-1] = MAX_FREQ; /* and a white color */ Freq[(long)MAX_COMP-1][MAX_COMP-1][MAX_COMP-1] = MAX_FREQ; Ncolors = 3; }