/* * 3to1 -- convert a 24 bit picture to 8 bits * also does color-map replacement on 8 bit inputs */ #include #include #include #include #define NCMAP 256 /* how many color map entries? */ #define NCOLOR 256 /* 0<=r,g,bNCMAP){ fprint(2, "%s: sorry, can't handle ncolor>%d\n", argv[0], NCMAP); exits("range error"); } in=picopen_r(infile); if(in==0){ perror(infile); exits("no input"); } s=strstr(in->chan, "rgb"); if(s==0){ mapped=1; s=strstr(in->chan, "m"); if(s==0){ fprint(2, "%s: can't convert CHAN=%s\n", argv[0], in->chan); exits("unk chan"); } if(in->cmap) memmove(icmap, in->cmap, 256*3); else for(y=0;y!=256;y++) icmap[y][0]=icmap[y][1]=icmap[y][2]=y; } else mapped=0; offs=s-in->chan; wid=PIC_WIDTH(in); hgt=PIC_HEIGHT(in); nchan=PIC_NCHAN(in); inbuf=malloc(wid*nchan); outbuf=malloc(wid); error=malloc((wid+1)*3*sizeof(short)); memset(error, 0, (wid+1)*3*sizeof(short)); if(inbuf==0 || outbuf==0 || error==0){ fprint(2, "%s: can't malloc\n", argv[0]); exits("no malloc"); } out=picopen_w("OUT", in->type, PIC_XOFFS(in), PIC_YOFFS(in), wid, hgt, "m", 0, (char *)cmap); initcindex(cmap, ncmap); eop=outbuf+wid; for(y=0;y!=hgt;y++){ picread(in, inbuf); if(flag['e']){ if(mapped){ for(op=outbuf,ip=inbuf+offs;op!=eop;op++,ip+=nchan) *op=fclook(icmap[*ip&255][0]&255, icmap[*ip&255][1]&255, icmap[*ip&255][2]&255); } else{ for(op=outbuf,ip=inbuf+offs;op!=eop;op++,ip+=nchan) *op=fclook(ip[0]&255, ip[1]&255, ip[2]&255); } } else if(mapped){ herror[0]=herror[1]=herror[2]=0; for(op=outbuf,ip=inbuf+offs,e=error+3;op!=eop;op++,ip+=nchan,e+=3){ *op=clook((icmap[*ip&255][0]&255)+e[0], (icmap[*ip&255][1]&255)+e[1], (icmap[*ip&255][2]&255)+e[2]); for(i=0;i!=3;i++){ t=(icmap[*ip&255][i]&255)-cmap[*op&255][i]; e[i]=3*t/8+herror[i]; herror[i]=t/4; e[i+3]+=t-(5*t/8); } } } else{ herror[0]=herror[1]=herror[2]=0; for(op=outbuf,ip=inbuf+offs,e=error+3;op!=eop;op++,ip+=nchan,e+=3){ *op=clook((ip[0]&255)+e[0], (ip[1]&255)+e[1], (ip[2]&255)+e[2]); for(i=0;i!=3;i++){ t=(ip[i]&255)-cmap[*op&255][i]; e[i]=3*t/8+herror[i]; herror[i]=t/4; e[i+3]+=t-(5*t/8); } } } picwrite(out, outbuf); } } /* * Color-map reverse indexing using a bucket list * * cindex[r>>RSH][g>>GSH][b>>BSH] points to a list of colors * that might be the closest to (r,g,b). */ #define RSH 4 /* tune this for speed */ #define GSH 4 /* tune this for speed */ #define BSH 4 /* tune this for speed */ #define NR (NCOLOR>>RSH) #define NG (NCOLOR>>GSH) #define NB (NCOLOR>>BSH) typedef struct Interval Interval; typedef struct Color Color; struct Interval{ int lo, hi; }; struct Color{ uchar r, g, b, i; Color *next; }; Color *cindex[NR][NG][NB]; /* * Look (r,g,b) up by indexing * cindex and walking through the list * of possible closest colors */ int clook(int r, int g, int b){ Color *cp, *bestp; int bestd, dr, dg, db, d; if(r<0) r=0; else if(r>=NCOLOR) r=NCOLOR-1; if(g<0) g=0; else if(g>=NCOLOR) g=NCOLOR-1; if(b<0) b=0; else if(b>=NCOLOR) b=NCOLOR-1; cp=cindex[r>>RSH][g>>GSH][b>>BSH]; bestp=cp; if(cp->next){ dr=cp->r-r; dg=cp->g-g; db=cp->b-b; bestd=dr*dr+dg*dg+db*db; while((cp=cp->next)!=0){ dr=cp->r-r; dg=cp->g-g; db=cp->b-b; d=dr*dr+dg*dg+db*db; if(di; } /* * Same as above, but without range check */ int fclook(int r, int g, int b){ Color *cp, *bestp; int bestd, dr, dg, db, d; cp=cindex[r>>RSH][g>>GSH][b>>BSH]; bestp=cp; if(cp->next){ dr=cp->r-r; dg=cp->g-g; db=cp->b-b; bestd=dr*dr+dg*dg+db*db; while((cp=cp->next)!=0){ dr=cp->r-r; dg=cp->g-g; db=cp->b-b; d=dr*dr+dg*dg+db*db; if(di; } /* * Interval addition */ Interval iadd(Interval a, Interval b){ a.lo+=b.lo; a.hi+=b.hi; return a; } /* * Interval square */ Interval isq(Interval a){ int t; if(a.hi<=0){ t=a.hi*a.hi; a.hi=a.lo*a.lo; a.lo=t; } else if(a.lo>=0){ a.lo*=a.lo; a.hi*=a.hi; } else{ if(-a.lo>=a.hi) a.hi=a.lo*a.lo; else a.hi*=a.hi; a.lo=0; } return a; } /* * interval ((r<=dg && dr>=db){ new.lo=r.lo; new.hi=r.lo+dr/2; rangemap(submap, nsubmap, new, g, b); new.lo=new.hi; new.hi=r.hi; rangemap(submap, nsubmap, new, g, b); } else if(dg>=db){ new.lo=g.lo; new.hi=g.lo+dg/2; rangemap(submap, nsubmap, r, new, b); new.lo=new.hi; new.hi=g.hi; rangemap(submap, nsubmap, r, new, b); } else{ new.lo=b.lo; new.hi=b.lo+db/2; rangemap(submap, nsubmap, r, g, new); new.lo=new.hi; new.hi=b.hi; rangemap(submap, nsubmap, r, g, new); } } Interval rrange={0,NR}, grange={0,NG}, brange={0,NB}; void initcindex(uchar input[NCMAP][3], int ncmap){ int i; Color map[NCMAP]; for(i=0;i!=ncmap;i++){ map[i].r=input[i][0]; map[i].g=input[i][1]; map[i].b=input[i][2]; map[i].i=i; } rangemap(map, ncmap, rrange, grange, brange); }