/* @(#)special.c 1.11 89/12/12 * * Special transformations used by the popi program. * * Popi was originally written by Gerard J. Holzmann - AT&T Bell Labs. * This version is based on the code in his Prentice Hall book, * "Beyond Photography - the digital darkroom," ISBN 0-13-074410-7, * which is copyright (c) 1988 by Bell Telephone Laboratories, Inc. * * Permission is given to distribute these extensions, as long as these * introductory messages are not removed, and no monies are exchanged. * * No responsibility is taken for any errors or inaccuracies inherent * either to the comments or the code of this program, but if reported * (see README file) then an attempt will be made to fix them. */ /* Special transformations from chapter 6 of BP. * * The way this is done is fairly nasty at the moment, * but it does work. */ #ifndef AMIGA # include #endif /* AMIGA */ #include #include #ifdef BSD # include #endif /* BSD */ #include "popi.h" #define GAMMA 7.5 /* default gamma for matte() */ #define TILESIZE 25 /* default tile size for tile() */ #define N 3 #define New src[CURNEW].pix /* prototypes for local functions */ struct SRC * parseimg P((void)); bool parsefname P((void)); void oil P((void)); void shear P((void)); void slice P((void)); void tile P((void)); void melt P((void)); void matte P((void)); void genps P((void)); void genepson P((void)); void list P((void)); void readimg P((void)); void writeimg P((void)); void freeimg P((void)); void displayimg P((void)); void CloseLog P((void)); void dolog P((void)); void debug P((void)); void undo P((void)); void verbose P((void)); void trunc P((void)); /* * convenience function, since most of these routines have * an image as their 1st parameter. */ static struct SRC * parseimg() { lex(); if (lat == '\n' || lat == ')') { pushback(lat); return &src[CUROLD]; } if (lat != INAME) { SPRINTF(ErrBuf, "Expected image name"); error(ERR_PARSE); return (struct SRC *) 0; } return &src[lexval + 1]; } static bool parsefname() { lex(); if (lat == '\n') pushback(lat); if (lat != FNAME) { SPRINTF(ErrBuf, "Expected file name"); error(ERR_PARSE); return FALSE; } return TRUE; } static void oil() { register int x, y; register int dx, dy; pixel_t mfp; static int *histo = 0; struct SRC *srcp; pixel_t **img; if ((srcp = parseimg()) == (struct SRC *) 0) return; img = srcp->pix; if ( histo == (int *) 0 && (histo = (int *) LINT_CAST(Emalloc((unsigned)Zsize * sizeof (int)))) == (int *) 0 ) return; if (disp_active) disp_imgstart(); for (y = N; y < Ysize-N; y++) { for (x = N; x < Xsize-N; x++) { for (dx = 0; dx < Zsize; dx++) histo[dx] = 0; for (dy = y-N; dy <= y+N; dy++) for (dx = x-N; dx <= x+N; dx++) histo[img[dy][dx]]++; for (dx = dy = 0; dx < Zsize; dx++) if (histo[dx] > dy) { dy = histo[dx]; mfp = (pixel_t) dx; } New[y][x] = mfp; } if (disp_active) disp_putline(New[y], y); disp_percentdone(y * 100 / (Ysize-1)); } if (disp_active) disp_imgend(); SwapOldNew(); } static void shear() { register int x, y, r; int dx, dy; static int *yshift = 0; struct SRC *srcp; pixel_t **img; if ((srcp = parseimg()) == (struct SRC *) 0) return; img = srcp->pix; if ( yshift == 0 && (yshift = (int *) LINT_CAST(Emalloc((unsigned)Xsize * sizeof(int)))) == 0 ) return; if (disp_active) disp_imgstart(); for (x = r = 0; x < Xsize; x++) { if (RANDOM % Zsize < 128) r--; else r++; yshift[x] = r; } for (y = 0; y < Ysize; y++) { if (RANDOM % Zsize < 128) r--; else r++; for (x = 0; x < Xsize; x++) { dx = x + r; dy = y + yshift[x]; if (dx >= Xsize || dy >= Ysize || dx < 0 || dy < 0) continue; New[y][x] = img[dy][dx]; } if (disp_active) disp_putline(New[y], y); disp_percentdone(y * 100 / (Ysize-1)); } if (disp_active) disp_imgend(); SwapOldNew(); } static void slice() { register int x, y, r; int dx, dy; struct SRC *srcp; pixel_t **img; static int *xshift = 0, *yshift = 0; if ((srcp = parseimg()) == (struct SRC *) 0) return; img = srcp->pix; if ( xshift == 0 && (xshift = (int *) LINT_CAST(Emalloc((unsigned)Ysize * sizeof (int)))) == 0 ) return; if ( yshift == 0 && (yshift = (int *) LINT_CAST(Emalloc((unsigned)Xsize * sizeof (int)))) == 0 ) return; if (disp_active) disp_imgstart(); for (x = dx = 0 ; x < Xsize; x++) { if (dx == 0) { r = (RANDOM & 63) - 32; dx = 8 + RANDOM & 31; } else dx--; yshift[x] = r; } for (y = dy = 0; y < Ysize; y++) { if (dy == 0) { r = (RANDOM & 63) - 32; dy = 8 + RANDOM & 31; } else dy--; xshift[y] = r; } for (y = 0; y < Ysize; y++) { for (x = 0; x < Xsize; x++) { dx = x + xshift[y]; dy = y + yshift[x]; if (dx < Xsize && dy < Ysize && dx >= 0 && dy >= 0) New[y][x] = img[dy][dx]; } if (disp_active) disp_putline(New[y], y); disp_percentdone(y * 100 / (Ysize-1)); } if (disp_active) disp_imgend(); SwapOldNew(); } static void tile() { register int x, y, dx, dy; int ox, oy, nx, ny, TileSize = TILESIZE; struct SRC *srcp; pixel_t **img; if ((srcp = parseimg()) == (struct SRC *) 0) return; img = srcp->pix; for (y = 0; y < Ysize-TileSize; y += TileSize) { for (x = 0; x < Xsize-TileSize; x += TileSize) { ox = (RANDOM & 31) - 16 ; /* Displacement. */ oy = (RANDOM & 31) - 16; for (dy = y; dy < y+TileSize; dy++) for (dx = x; dx < x+TileSize; dx++) { nx = dx + ox; ny = dy + oy; if (nx >= Xsize || ny >= Ysize || nx < 0 || ny < 0) continue; New[ny][nx] = img[dy][dx]; } } disp_percentdone(y * 100 / (Ysize-1)); } if (disp_active) { disp_imgstart(); for (y = 0; y < Ysize; y++) disp_putline(New[y], y); disp_imgend(); } SwapOldNew(); } /* * Note: affects source image in situ. * Buffers not swapped after processing. */ static void melt() { register int x, y, k, NumPixels; pixel_t val; struct SRC *srcp; pixel_t **img; if ((srcp = parseimg()) == (struct SRC *) 0) return; img = srcp->pix; NumPixels = Xsize * Ysize; for (k = 0; k < NumPixels; k++) { x = RANDOM % Xsize; y = RANDOM % (Ysize - 1); while (y < Ysize-1 && img[y][x] <= img[y+1][x]) { val = img[y][x]; img[y][x] = img[y+1][x]; img[y+1][x] = val; y++; } disp_percentdone(k * 100 / (NumPixels-1)); } if (disp_active) { disp_imgstart(); for (y = 0; y < Ysize; y++) disp_putline(img[y], y); disp_imgend(); } } static void matte() { struct SRC *srcp; pixel_t **img; double gamma; register x, y; static pixel_t *lookup = (pixel_t *) 0; static double lastgamma = 0.0; DEBUG((Debug, "matte()\n")); if ((srcp = parseimg()) == (struct SRC *) 0) return; img = srcp->pix; lex(); if (lat == '\n' || lat == ')') { gamma = GAMMA; pushback(lat); } else gamma = lexval + lexfract; if ( lookup == 0 && (lookup = (pixel_t *) Emalloc((unsigned)Zsize)) == (pixel_t *) 0 ) return; if (lastgamma != gamma) { for (x = 0; x < Zsize; ++x) lookup[x] = ((double)Zmax * pow(x / (double)Zmax, gamma) < 3.0) ? Zmax : 0; lastgamma = gamma; } if (disp_active) disp_imgstart(); for (y = 0; y < Ysize; y++) { for (x = 0; x < Xsize; x++) New[y][x] = lookup[img[y][x]]; if (disp_active) disp_putline(New[y], y); disp_percentdone(y * 100 / (Ysize-1)); } if (disp_active) disp_imgend(); } static void genps() { int x, y; FILE *ostr; struct SRC *srcp; pixel_t **img, *ip; time_t time P((time_t *)), clock; #if unix char *getlogin P((void)); #endif /* unix */ if (!parsefname()) return; if ((ostr = EfopenW(text)) == NULL) return; if ((srcp = parseimg()) == (struct SRC *) 0) return; img = srcp->pix; clock = time((time_t *) 0); FPRINTF(ostr, "%%!PS-Adobe-1.0\n"); FPRINTF(ostr, "%%%%Title: popi bit image '%s'\n", srcp->str); FPRINTF(ostr, "%%%%DocumentFonts:\n"); FPRINTF(ostr, "%%%%Creator: popi\n"); FPRINTF(ostr, "%%%%CreationDate: %s", ctime(&clock)); /* includes \n */ FPRINTF(ostr, "%%%%Pages: 1\n"); #if unix FPRINTF(ostr, "%%%%For: %s\n", getlogin()); #endif /* unix */ FPRINTF(ostr, "%%%%EndComments\n"); FPRINTF(ostr, "clippath pathbbox pop pop translate\n"); FPRINTF(ostr, "clippath pathbbox pop exch pop exch sub dup scale\n"); FPRINTF(ostr, "/picstr %d string def\n", Xsize); FPRINTF(ostr, "/doimage {\n"); FPRINTF(ostr, "%d %d %d [ %d 0 0 -%d 0 %d ]\n", Xsize, Ysize, BITSPERPIXEL, Xsize, Ysize, Ysize); FPRINTF(ostr, "{currentfile picstr readhexstring pop}image}bind def\n"); FPRINTF(ostr, "%%%%EndProlog\n%%%%Page 0 0\ndoimage\n"); for(y = 0; y < Ysize; ++y) { ip = &img[y][0]; for(x = 0; x < Xsize; ++x) { if (x % 40 == 0) /* formatting only */ PUTC('\n', ostr); FPRINTF(ostr, "%02x", *ip++); } PUTC('\n', ostr); } FPRINTF(ostr, "showpage\n"); Efclose(ostr); } /* * Although this is set up to use one particular graphics mode * of a 24-pin printer, everything should be generic enough * that it can be changed to work with a different graphics * mode on an 8-pin printer, just by changing a few numbers. */ static void genepson() { struct SRC *srcp; char *PinVals; pixel_t *PinThresh, ThreshStep; FILE *ostr; register int x; register pixel_t **img; int y, yn; int pin; int BytesPerChunk, PinsPerPixel, BytesPerColumn, yPixelsPerByte, xPinsPerPixel, yPinsPerPixel; if (parsefname() == 0) return; if ((ostr = EfopenW(text)) == NULL) return; if ((srcp = parseimg()) == (struct SRC *) 0) return; img = srcp->pix; /* printer specific */ xPinsPerPixel = 4; yPinsPerPixel = 4; BytesPerColumn = 24 / BITSINBYTE; PinsPerPixel = xPinsPerPixel * yPinsPerPixel; BytesPerChunk = xPinsPerPixel * BytesPerColumn; yPixelsPerByte = BITSINBYTE / yPinsPerPixel; /* Must be whole number of pixels (y direction) per byte. */ assert(yPinsPerPixel * yPixelsPerByte == BITSINBYTE); /* Reallocate these each time, as changing the print mode * may change the sizes of these arrays. */ if ( (PinVals = Emalloc((unsigned)BytesPerChunk)) == 0 || (PinThresh = (pixel_t *) Emalloc((unsigned)PinsPerPixel * sizeof(pixel_t))) == 0 ) return; ThreshStep = (pixel_t) (Zsize / (PinsPerPixel + 1)); for (pin = 0; pin < PinsPerPixel; ++pin) PinThresh[pin] = (pixel_t) ((pin + 1) * ThreshStep); for (y = 0; y < Ysize; y = yn) { /* printer specific */ /* * This print line is width Xsize pixels, and (Xsize * xPinsPerPixel) * pin positions (dots on the page). * No. of dots vertical is (BytesPerColumn * BITSINBYTE) * which is yPinsPerPixel times the no. of image scanlines. */ FPRINTF(ostr, "\033*%c%c%c", 39, /* 180 dpi in both directions */ (Xsize * xPinsPerPixel) % 256, (Xsize * xPinsPerPixel) / 256 ); for (x = 0; x < Xsize; ++x) { register int ycur; int ByteCount, xpin; char *dp; /* Clear the PinVals array */ for ( ByteCount = 0, dp = PinVals; ByteCount < BytesPerChunk; ++ByteCount ) *dp++ = 0; dp = PinVals; /* For each byte-sized row of the print head, * collect 1 pixel width of data. */ for ( ByteCount = 0, dp = PinVals, ycur = y; ByteCount < BytesPerColumn; ++ByteCount, dp += xPinsPerPixel ) { register unsigned char bit; yn = ycur + yPixelsPerByte; if (yn > Ysize) yn = Ysize; /* For the current byte row of the print-head * (ie yPixelsPerByte image scanlines), * collect a pixel width of data. */ for (bit = 0x80; ycur < yn; ++ycur) { pixel_t val; int ypin; val = img[ycur][x]; /* Now use an appropriate no. of pins to simulate * the greyscale value. */ for ( ypin = 0, pin = 0; ypin < yPinsPerPixel; ++ypin ) { for (xpin = 0; xpin < xPinsPerPixel; ++xpin, ++pin) { if (val < PinThresh[pin]) dp[xpin] |= bit; } /* xpin == xPinsPerPixel */ bit >>= 1; } /* ypin == YpinsPerPixel */ } /* ycur == y */ } /* ByteCount == BytesPerColumn */ /* We have set up enough columns for a single pixel in * the x direction. Now print them in the correct order. */ for (xpin = 0; xpin < xPinsPerPixel; ++xpin) { for (ByteCount = 0; ByteCount < BytesPerColumn; ++ByteCount) { PUTC(PinVals[ByteCount * xPinsPerPixel + xpin], ostr); } } /* xpin == xPinsPerPixel */ } /* x == Xsize */ /* Printer specific */ FPRINTF(ostr, "\033J%c\r", 24); } /* y == Ysize */ Efclose(ostr); free(PinVals); free((char *) PinThresh); } static void list() { showfiles(); } /* * #read "filename" [imagename] */ static void readimg() { char filename[512], *imgname = (char *) 0; if (parsefname() == 0) return; STRCPY(filename, text); lex(); if (lat == '\n' || lat == ')') { pushback(lat); } else if (lat != NAME && lat != INAME) { SPRINTF(ErrBuf, "Expected image name"); error(ERR_PARSE); } else imgname = text; getpix(filename, imgname); } static void writeimg() { struct SRC *srcp; if (parsefname() == 0) return; if ((srcp = parseimg()) == (struct SRC *) 0) return; putpix(srcp, text); } static void freeimg() { struct SRC *srcp; if ((srcp = parseimg()) == (struct SRC *) 0) return; if (srcp == &src[CUROLD] || srcp == &src[CURNEW]) { SPRINTF(ErrBuf, "Cannot free 'old' or 'new'"); error(0); return; } ImgFree(srcp); } static void displayimg() { pixel_t **img; int y; lex(); if (lat == '+') { disp_active = 1; return; } if (lat == '-') { disp_active = 0; return; } if (lat == '\n') { pushback(lat); img = src[CUROLD].pix; } else if (lat == INAME) img = src[lexval + 1].pix; else if (lat == NEW) img = src[CURNEW].pix; else { SPRINTF(ErrBuf, "Expected +, - or image name"); error(ERR_PARSE); return; } disp_imgstart(); for (y = 0; y < Ysize; y++) disp_putline(img[y], y); disp_imgend(); } static void CloseLog() { if (LogStr == NULL) return; FPRINTF(LogStr, "\n---\n"); FCLOSE(LogStr); LogStr = NULL; } void OpenLog() { time_t time(), clock; CloseLog(); if ((LogStr = fopen(LogFile, "a")) == NULL) { SPRINTF(ErrBuf, "Can't open log file '%s' - logging is off", LogFile); error(ERR_SYS); return; } clock = time((time_t *) 0); FPRINTF(LogStr, "==>> %s", ctime(&clock)); /* includes '\n' */ } static void dolog() { static char *buf = (char *) 0; lex(); if (lat == '+') OpenLog(); else if (lat == '-') CloseLog(); else if (lat == FNAME) { if (buf == (char *) 0 && (buf = Emalloc((unsigned int) 512)) == (char *) 0) return; STRCPY(buf, text); LogFile = buf; OpenLog(); } else pushback(lat); if (LogStr) PRINTF("Logging is active on file '%s'\n", LogFile); else PRINTF("Logging is off\n"); } static void debug() { static char *buf = (char *) 0; lex(); if (lat == '+') OpenLog(); else if (lat == '-') CloseLog(); else if (lat == FNAME) { if (buf == (char *) 0 && (buf = Emalloc((unsigned int) 512)) == (char *) 0) return; STRCPY(buf, text); LogFile = buf; OpenLog(); } else pushback(lat); if (LogStr) PRINTF("Logging is active on file '%s'\n", LogFile); else PRINTF("Logging is off\n"); } static char *HelpMsg[] = { "binary ops: ** * / % + - << >> < > >= <= == != & ^ | && ||", "funcs: sin(deg) cos(deg) atan(y, x) log(val) sqrt(val) abs(val) rand()", "values: x y r a X Y R A Z", "special funcs", "\t#read \"filename\"", "\t#write \"filename\"", "\t#genps \"filename\" [image]", "\t#display [image]", "\t#display +", "\t#display -", (char *) 0 }; void help() { PrStrs(HelpMsg); } static void undo() { SwapOldNew(); } static void verbose() { lex(); if (lat == '+') Verbose = 1; else if (lat == '-') Verbose = 0; else pushback(lat); PRINTF("Verbose is %s\n", Verbose ? "on" : "off"); } static void trunc() { lex(); if (lat == '+') Truncate = 1; else if (lat == '-') Truncate = 0; else pushback(lat); PRINTF("Truncation is %s\n", Truncate ? "on" : "off"); } struct SpecialOp { char *name; void (*func) P((void)); }; static struct SpecialOp SpecialOps[] = { { "matte", matte }, { "oil", oil }, { "slice", slice }, { "shear", shear }, { "tile", tile }, { "melt", melt }, { "read", readimg }, { "write", writeimg }, { "genps", genps }, { "genepson", genepson }, { "list", list }, { "display", displayimg }, { "debug", debug }, { "version", version }, { "verbose", verbose }, { "truncate", trunc }, { "undo", undo }, { "help", help }, { "free", freeimg }, { "logfile", dolog }, { (char *) 0, (void (*) P((void)) ) 0 } }; void special() { struct SpecialOp *sp; DEBUG((Debug, "special\n")); lex(); if (! (lat == NAME || isalpha(lat))) { SPRINTF(ErrBuf, "Expected name of special operation"); error(ERR_PARSE); return; } sp = SpecialOps; for (sp = SpecialOps; sp->name && strcmp(text, sp->name) != 0; ++sp) ; if (! sp->name) { SPRINTF(ErrBuf, "Special operation '%s' unrecognised", text); error(ERR_PARSE); return; } DEBUG((Debug, "calling func '%s'\n", sp->name)); (*(sp->func))(); if (lat != '\n') lex(); if (lat != '\n') { SPRINTF(ErrBuf, "Tokens after special command ignored"); error(ERR_WARN); } assert(lat == '\n'); }