This is xvalg.c in view mode; [Download] [Up]
/* * xvalg.c - image manipulation algorithms (Blur, etc.) * * Author: John Bradley, University of Pennsylvania * (bradley@cis.upenn.edu) * * Contains: * void AlgInit(); * void DoAlg(int algnum); * static void NoAlg(); * static void Blur(int size); * static void EdgeDetect(int tinfoil); */ /* Copyright Notice * ================ * Copyright 1989, 1990, 1991, 1992, 1993 by John Bradley * * Permission to use, copy, and distribute XV in its entirety, for * non-commercial purposes, is hereby granted without fee, provided that * this license information and copyright notice appear in all copies. * * Note that distributing XV 'bundled' in with ANY product is considered * to be a 'commercial purpose'. * * Also note that any copies of XV that are distributed MUST be built * and/or configured to be in their 'unregistered copy' mode, so that it * is made obvious to the user that XV is shareware, and that they should * consider donating, or at least reading this License Info. * * The software may be modified for your own purposes, but modified * versions may NOT be distributed without prior consent of the author. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the author be held liable for any damages * arising from the use of this software. * * If you would like to do something with XV that this copyright * prohibits (such as distributing it with a commercial product, * using portions of the source in some other program, etc.), please * contact the author (preferably via email). Arrangements can * probably be worked out. * * XV is shareware for PERSONAL USE only. You may use XV for your own * amusement, and if you find it nifty, useful, generally cool, or of * some value to you, your non-deductable donation would be greatly * appreciated. $25 is the suggested donation, though, of course, * larger donations are quite welcome. Folks who donate $25 or more * can receive a Real Nice bound copy of the XV manual for no extra * charge. * * Commercial, government, and institutional users MUST register their * copies of XV, for the exceedingly REASONABLE price of just $25 per * workstation/X terminal. Site licenses are available for those who * wish to run XV on a large number of machines. Contact the author * for more details. * * The author may be contacted via: * US Mail: John Bradley * 1053 Floyd Terrace * Bryn Mawr, PA 19010 * * Phone: (215) 898-8813 * EMail: bradley@cis.upenn.edu */ #include "xv.h" #ifdef __STDC__ static void NoAlg(void); static void Blur(int); static void EdgeDetect(int); static void OilPaint(void); static void saveOrigPic(void); static void doConvolv(byte *, int,int, int *,int,int,int, byte *, int,int); static void doBlurConvolv(byte *, int, int, byte *, int); static void doEdgeConvolv(byte *, int, int, byte *, int); static void doOilPaint(byte *, int, int, byte *, int); static int start24bitAlg(byte **, byte **); static void end24bitAlg(byte *, byte *); #else static void NoAlg(), Blur(), EdgeDetect(), OilPaint(), saveOrigPic(); static void doConvolv(), doBlurConvolv(), doEdgeConvolv(), doOilPaint(); static int start24bitAlg(); static void end24bitAlg(); #endif static byte *origPic = (byte *) NULL; static int origPicType; static byte origrmap[256], origgmap[256], origbmap[256]; #undef TIMING_TEST #ifdef TIMING_TEST #include <sys/resource.h> #endif /***************************/ static void printUTime(str) char *str; { #ifdef TIMING_TEST int i; struct rusage ru; i = getrusage(RUSAGE_SELF, &ru); fprintf(stderr,"%s: utime = %ld.%ld seconds\n", str, ru.ru_utime.tv_sec, ru.ru_utime.tv_usec); #endif } /************************************************************/ void AlgInit() { /* called whenver an image file is loaded. disposes of origPic if neccessary, and points it to null */ if (origPic) free(origPic); origPic = (byte *) NULL; algMB.dim[ALG_NONE] = 1; /* can't undo when init'ed already */ } /************************/ void DoAlg(anum) int anum; { /* called with a value from the algMB button. Executes the specified algorithm */ switch (anum) { case ALG_NONE: NoAlg(); break; case ALG_BLUR3: Blur(3); break; case ALG_BLUR5: Blur(5); break; case ALG_BLUR7: Blur(7); break; case ALG_EDGE: EdgeDetect(0); break; case ALG_TINF: EdgeDetect(1); break; case ALG_OIL: OilPaint(); break; } algMB.dim[ALG_NONE] = (anum == ALG_NONE); } /************************/ static void NoAlg() { int i; /* restore original picture */ if (!origPic) return; /* none to restore */ WaitCursor(); KillOldPics(); /* toss the old pic/cpic/epic/theImage away */ picType = origPicType; Set824Menus(picType); pic = origPic; origPic = NULL; if (picType == PIC8) { for (i=0; i<256; i++) { rMap[i] = origrmap[i]; gMap[i] = origgmap[i]; bMap[i] = origbmap[i]; } } InstallNewPic(); } /************************/ static void Blur(n) int n; { /* runs a n*n convolution mask (all 1's) over 'pic', producing a 24-bit version. Then calls 24to8 to generate a new 8-bit image, and installs it. Note that 'n' must be odd for things to work properly */ byte *pic24, *tmpPic; int *blurMatrix, i; WaitCursor(); SetISTR(ISTR_INFO, "Blurring with %dx%d convolution mask...",n,n); if (start24bitAlg(&pic24, &tmpPic)) return; doBlurConvolv(pic24, pWIDE,pHIGH, tmpPic, n); end24bitAlg(pic24, tmpPic); } /************************/ static void EdgeDetect(tinfoil) int tinfoil; { byte *pic24, *p24, *tmpPic; char *str; int i, v, maxv; WaitCursor(); if (tinfoil) str = "Doing cheesy embossing effect..."; else str = "Doing edge detection..."; SetISTR(ISTR_INFO, str); if (start24bitAlg(&pic24, &tmpPic)) return; if (tinfoil) { /* fill tmpPic with gray128 */ int i; byte *tp; for (i=0, tp=tmpPic; i<pWIDE*pHIGH*3; i++) *tp++ = 128; } doEdgeConvolv(pic24, pWIDE, pHIGH, tmpPic, (tinfoil) ? 0 : 1); if (tinfoil) { /* mono-ify tmpPic */ for (i=0, p24=tmpPic; i<pWIDE*pHIGH; i++,p24+=3) { v = MONO(p24[0], p24[1], p24[2]); RANGE(v,0,255); p24[0] = p24[1] = p24[2] = (byte) v; } } SetISTR(ISTR_INFO, "%snormalizing...", str); /* compute maximum value */ for (i=0, maxv=0, p24=tmpPic; i<pWIDE*pHIGH; i++,p24+=3) { v = MONO(p24[0], p24[1], p24[2]); if (v>maxv) maxv = v; } for (i=0, p24=tmpPic; i<pWIDE*pHIGH*3; i++) { v = (((int) *p24) * 255) / maxv; RANGE(v,0,255); *p24++ = (byte) v; } end24bitAlg(pic24, tmpPic); } /************************/ static void OilPaint() { byte *pic24, *tmpPic; WaitCursor(); SetISTR(ISTR_INFO, "Doing oilpaint effect..."); if (start24bitAlg(&pic24, &tmpPic)) return; doOilPaint(pic24, pWIDE, pHIGH, tmpPic, 7); end24bitAlg(pic24, tmpPic); } /************************/ static void doConvolv(pic24, w, h, fil, filw, filh, maxv, results, inset, absolute) byte *pic24, *results; int w, h, *fil, filw, filh, maxv, inset, absolute; { /* general purpose convolution routine does convolution, adding results into 'results' array (clips at 0,255) note: if 'inset' is true, it only does convolution on pixels where the entire mask is 'on-screen' If absolute, adds absolute value of rsum,gsum,bsum to results pic */ byte *p24, *rp; int i,x,y,x1,y1,x0,y0,rsum,gsum,bsum,count,w0,h0,w2,h2; printUTime("start of doConvolv"); x0 = y0 = 0; w0 = w; h0 = h; w2 = filw/2; h2 = filh/2; if (inset) { x0 = filw/2; y0 = filh/2; w0 -= (filw/2); h0 -= (filh/2); } for (y=y0; y<h0; y++) { if ((y & 15) == 0) WaitCursor(); for (x=x0, rp=results + (y*w + x)*3; x<w0; x++) { /* compute weighted average for pic24[x,y] */ rsum = gsum = bsum = 0; count = 0; for (i=0, y1=y-h2; y1<=y+h2; y1++) { if (y1>=0 && y1<h) { p24 = pic24 + y1*w*3 + (x-filw/2)*3; for (x1=x-w2; x1<=x+w2; x1++, i++) { if (fil[i] && x1>=0 && x1<w) { rsum += (fil[i] * *p24++); gsum += (fil[i] * *p24++); bsum += (fil[i] * *p24++); count++; } else p24 += 3; } } } rsum = rsum / (count * maxv); gsum = gsum / (count * maxv); bsum = bsum / (count * maxv); if (absolute) { if (rsum<0) rsum = -rsum; if (gsum<0) gsum = -gsum; if (bsum<0) bsum = -bsum; } rsum += rp[0]; RANGE(rsum,0,255); gsum += rp[1]; RANGE(gsum,0,255); bsum += rp[2]; RANGE(bsum,0,255); *rp++ = (byte) rsum; *rp++ = (byte) gsum; *rp++ = (byte) bsum; } } printUTime("end of doConvolv"); } /************************/ static void doBlurConvolv(pic24, w, h, results, n) byte *pic24, *results; int w, h, n; { /* convolves with an n*n array, consisting of only 1's. n must be odd */ register byte *p24; register int rsum,gsum,bsum; byte *rp; int i,j,k,x,y,x1,y1,count,n2; printUTime("start of blurConvolv"); n2 = n/2; for (y=0; y<h; y++) { if ((y & 15) == 0) WaitCursor(); p24 = pic24 + y*w*3; rp = results + y*w*3; for (x=0; x<w; x++) { rsum = gsum = bsum = 0; count = 0; for (y1=y-n2; y1<=y+n2; y1++) { if (y1>=0 && y1<h) { p24 = pic24 + y1*w*3 +(x-n2)*3; for (x1=x-n2; x1<=x+n2; x1++) { if (x1>=0 && x1<w) { rsum += *p24++; gsum += *p24++; bsum += *p24++; count++; } else p24 += 3; } } } rsum = rsum / count; gsum = gsum / count; bsum = bsum / count; RANGE(rsum,0,255); RANGE(gsum,0,255); RANGE(bsum,0,255); *rp++ = (byte) rsum; *rp++ = (byte) gsum; *rp++ = (byte) bsum; } } printUTime("end of blurConvolv"); } /************************/ static void doEdgeConvolv(pic24, w, h, results, absolute) byte *pic24, *results; int w, h, absolute; { /* convolves with two edge detection masks (vertical and horizontal) simultaneously, adding results together. The two masks are (hard coded): -1 0 1 -1 -1 -1 -2 -1 0 -1 0 1 and 0 0 0 = -1 0 1 -1 0 1 1 1 1 0 1 2 Also, only does pixels in which the masks fit fully onto the picture (no pesky boundary conditionals) If absolute, adds absolute value of rsum,gsum,bsum to results pic */ register byte *p24; register int bperlin,rsum,gsum,bsum; byte *rp; int i, x,y; printUTime("start of edgeConvolv"); bperlin = w * 3; for (y=1; y<h-1; y++) { if ((y & 63) == 0) WaitCursor(); rp = results + (y*w + 1)*3; p24 = pic24 + (y*w + 1)*3; for (x=1; x<w-1; x++, p24+=3) { /* compute weighted average for *p24 (pic24[x,y]) */ rsum = gsum = bsum = 0; rsum -= (p24[-bperlin-3] * 2); /* top left */ gsum -= (p24[-bperlin-2] * 2); bsum -= (p24[-bperlin-1] * 2); rsum -= p24[-bperlin]; /* top mid */ gsum -= p24[-bperlin+1]; bsum -= p24[-bperlin+2]; rsum -= p24[-3]; /* mid left */ gsum -= p24[-2]; bsum -= p24[-1]; rsum += p24[3]; /* mid right */ gsum += p24[4]; bsum += p24[5]; rsum += p24[bperlin]; /* bottom mid */ gsum += p24[bperlin+1]; bsum += p24[bperlin+2]; rsum += (p24[bperlin+3] * 2); /* bottom right */ gsum += (p24[bperlin+4] * 2); bsum += (p24[bperlin+5] * 2); rsum = rsum / 8; gsum = gsum / 8; bsum = bsum / 8; if (absolute) { if (rsum<0) rsum = -rsum; if (gsum<0) gsum = -gsum; if (bsum<0) bsum = -bsum; } rsum += rp[0]; RANGE(rsum,0,255); gsum += rp[1]; RANGE(gsum,0,255); bsum += rp[2]; RANGE(bsum,0,255); *rp++ = (byte) rsum; *rp++ = (byte) gsum; *rp++ = (byte) bsum; } } printUTime("end of edgeConvolv"); } /************************/ static void doOilPaint(pic24, w, h, results, n) byte *pic24, *results; int w, h, n; { /* does an 'oil transfer', as described in the book "Beyond Photography", by Holzmann, chapter 4, photo 7. It is a sort of localized smearing. The following algorithm (but no actual code) was borrowed from 'pgmoil.c', written by Wilson Bent (whb@hoh-2.att.com), and distributed as part of the PBMPLUS package. for each pixel in the image (assume, for a second, a grayscale image), compute a histogram of the n*n rectangle centered on the pixel. replace the pixel with the color that had the greatest # of hits in the histogram. Note that 'n' should be odd. I've modified the algorithm to do this same thing for all three planes of a 24-bit image (jhb, 8/14/92) */ register byte *pp; register int bperlin,rsum,gsum,bsum; byte *rp, *p24, *plin; int i,j,k,x,y,n2,rval,rcnt,gval,gcnt,bval,bcnt; int rhist[256], ghist[256], bhist[256]; printUTime("start of doOilPaint"); bperlin = w * 3; n2 = n/2; p24 = pic24 - n2*w*3 - n2*3; /* follows top-left corner of mask */ rp = results; for (y=0; y<h; y++) { if ((y & 15) == 0) WaitCursor(); for (x=0; x<w; x++) { /* compute histogram of (on-screen hunk of) n*n region centered around x,y, for each plane */ pp = plin = p24; rval = rcnt = gval = gcnt = bval = bcnt = 0; for (i=0; i<64; i++) rhist[i] = ghist[i] = bhist[i] = 0; for (i=y-n2; i<y+n2; i++) { if (i>=0 && i<h) { for (j=x-n2; j<x+n2; j++) { if (j>=0 && j<w) { k = ++rhist[*pp>>2]; if (k > rcnt) { rval = *pp; rcnt = k; } pp++; k = ++ghist[*pp>>2]; if (k > gcnt) { gval = *pp; gcnt = k; } pp++; k = ++bhist[*pp>>2]; if (k > bcnt) { bval = *pp; bcnt = k; } pp++; } else pp += 3; } } plin += bperlin; pp = plin; } *rp++ = (byte) rval; *rp++ = (byte) gval; *rp++ = (byte) bval; p24 += 3; } } printUTime("end of doOilPaint"); } /***********************************************/ static int start24bitAlg(pic24, tmpPic) byte **pic24, **tmpPic; { /* generates a 24-bit version of 'pic', if neccessary, and also mallocs * a pWIDE*pHIGH*3 24-bit output pic. * * Returns '1' if there's some sort of screwup, '0' if cool */ if (picType == PIC8) { *pic24 = Conv8to24(pic, pWIDE, pHIGH, rMap, gMap, bMap); if (!*pic24) { SetCursors(-1); return 1; } } else *pic24 = pic; /* need to create another w*h*3 pic to hold results */ *tmpPic = (byte *) calloc(pWIDE * pHIGH * 3, 1); if (!(*tmpPic)) { SetCursors(-1); ErrPopUp("Unable to malloc() tmp 24-bit image in start24bitAlg()", "\nTough!"); if (picType == PIC8) free(*pic24); return 1; } return 0; } /***********************************************/ static void end24bitAlg(pic24, outPic) byte *pic24, *outPic; { /* given pic24, and outPic, which has the new 24-bit image, installs it */ saveOrigPic(); /* also kills pic/cpic/epic/egampic/theImage, NOT pic24 */ xvbcopy(outPic, pic24, pWIDE*pHIGH*3); /* copy results into pic24 */ free(outPic); if (picType == PIC8) { pic = Conv24to8(pic24, pWIDE, pHIGH, ncols, rMap,gMap,bMap); free(pic24); if (!pic) { SetCursors(-1); ErrPopUp("Some sort of failure occured in 24to8 conversion\n", "Damn!"); NoAlg(); return; } } else pic = pic24; InstallNewPic(); } /************************/ static void saveOrigPic() { /* saves original picture into origPic, if it hasn't already been done. This allows us to undo algorithms... Also, frees all pics, (except 'pic', if we're in PIC24 mode) */ int i; FreeEpic(); if (cpic && cpic != pic) free(cpic); xvDestroyImage(theImage); theImage = NULL; cpic = NULL; if (!origPic) { /* make a backup copy of 'pic' */ origPic = (byte *) malloc(pWIDE * pHIGH * ((picType==PIC8) ? 1 : 3)); if (!origPic) FatalError("out of memory in 'saveOrigPic()'"); xvbcopy(pic, origPic, pWIDE * pHIGH * ((picType==PIC8) ? 1 : 3)); origPicType = picType; if (picType == PIC8) { for (i=0; i<256; i++) { /* save old colormap */ origrmap[i] = rorg[i]; origgmap[i] = gorg[i]; origbmap[i] = borg[i]; } } } if (picType != PIC24) { /* kill pic, as well */ if (pic) free(pic); pic = NULL; } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.