bradley@grip.cis.upenn.edu (John Bradley) (10/11/90)
Submitted-by: bradley@grip.cis.upenn.edu (John Bradley) Posting-number: Volume 9, Issue 79 Archive-name: xv/part04 -----------------------(cut here)--------------------------- #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./xv24to8.c` then echo "writting ./xv24to8.c" cat > ./xv24to8.c << '\BARFOO\' /* * xv24to8.c - contains the 24-to-8-bit Conv24to8 procedure * * The Conv24to8 procedure takes a pointer to a 24-bit image (loaded * previously). The image will be a w * h * 3 byte array of * bytes. The image will be arranged with 3 bytes per pixel (in order * R, G, and B), pixel 0 at the top left corner. (As normal.) * The procedure also takes a maximum number of colors to use (numcols) * * The Conv24to8 procedure will set up the following: it will allocate and * create 'pic', a pWIDE*pHIGH 8-bit picture. it will set up pWIDE and pHIGH. * it will load up the r[], g[], and b[] colormap arrays. it will NOT * calculate numcols, since the cmap sort procedure has to be called anyway * * Conv24to8 returns '0' if successful, '1' if it failed (presumably on a * malloc()) * * contains: * Cont24to8() * InitFSDTables() */ /* * Copyright 1989, 1990 by the University of Pennsylvania * * Permission to use, copy, and distribute for non-commercial purposes, * is hereby granted without fee, providing that the above copyright * notice appear in all copies and that both the copyright notice and this * permission notice appear in supporting documentation. * * The software may be modified for your own purposes, but modified versions * may not be distributed. * * This software is provided "as is" without any express or implied warranty. */ #include "xv.h" #define MAX_CMAP_SIZE 256 #define COLOR_DEPTH 8 #define MAX_COLOR 256 #define B_DEPTH 5 /* # bits/pixel to use */ #define B_LEN (1<<B_DEPTH) #define C_DEPTH 2 #define C_LEN (1<<C_DEPTH) /* # cells/color to use */ typedef struct colorbox { struct colorbox *next, *prev; int rmin,rmax, gmin,gmax, bmin,bmax; int total; } CBOX; typedef struct { int num_ents; int entries[MAX_CMAP_SIZE][2]; } CCELL; static byte *pic24; static int num_colors, WIDE, HIGH; static int histogram[B_LEN][B_LEN][B_LEN]; CBOX *freeboxes, *usedboxes; CCELL **ColorCells; static void get_histogram(); static CBOX *largest_box(); static void splitbox(); static void shrinkbox(); static void assign_color(); static CCELL *create_colorcell(); static void map_colortable(); static int quant_fsdither(); static int Quick24to8(); static int QuickCheck(); static byte tbl1[256], /* tables used in F-S Dithering */ tbl3[256], /* contain i/16, 3i/16, 5i/16, 7i/16, */ tbl5[256], /* (i=0-255) respectively */ tbl7[256]; /****************************/ int Conv24to8(p,w,h,nc) /****************************/ byte *p; int w,h,nc; { int i; CBOX *box_list, *ptr; /* copy arguments to local-global variables */ pic24 = p; pWIDE = WIDE = w; pHIGH = HIGH = h; num_colors = nc; /* allocate pic immediately, so that if we can't allocate it, we don't waste time running this algorithm */ pic = (byte *) malloc(WIDE * HIGH); if (pic == NULL) { fprintf(stderr,"%s: Conv24to8() - failed to allocate 'pic'\n",cmd); return(1); } /* quick check: if we're going to display a greyscale or 1-bit image, DON'T run this annoyingly time-consuming code. Just convert the 24-bit color image to an 8-bit greyscale. This takes virtually no time, by comparision, and it has the same effect. */ if (mono || nc==0) { byte *pp, *p24; for (i=0; i<256; i++) r[i]=g[i]=b[i]=i; /* greyscale colormap */ pp = pic; p24 = pic24; for (i=WIDE*HIGH; i>0; i--, pp++, p24+=3) *pp = (p24[0]*11 + p24[1]*16 + p24[2]*5) >> 5; /* pp=.33R+.5G+.17B */ return(0); } if (!noqcheck && QuickCheck(pic24,w,h,nc)) { /* see if it's a <256 color RGB pic */ SetISTR(ISTR_INFO,"No color compression was necessary.\n"); return 0; } else if (!slow24) { SetISTR(ISTR_INFO,"Doing quick 24-bit to 8-bit conversion."); return(Quick24to8(pic24,w,h)); } else SetISTR(ISTR_INFO,"Doing full 24-bit to 8-bit conversion."); /**** STEP 1: create empty boxes ****/ usedboxes = NULL; box_list = freeboxes = (CBOX *) malloc(num_colors * sizeof(CBOX)); if (box_list == NULL) { fprintf(stderr,"%s: Conv24to8() - failed to allocate 'freeboxes'\n",cmd); return(1); } for (i=0; i<num_colors; i++) { freeboxes[i].next = &freeboxes[i+1]; freeboxes[i].prev = &freeboxes[i-1]; } freeboxes[0].prev = NULL; freeboxes[num_colors-1].next = NULL; /**** STEP 2: get histogram, initialize first box ****/ ptr = freeboxes; freeboxes = ptr->next; if (freeboxes) freeboxes->prev = NULL; ptr->next = usedboxes; usedboxes = ptr; if (ptr->next) ptr->next->prev = ptr; get_histogram(ptr); /**** STEP 3: continually subdivide boxes until no more free boxes remain */ while (freeboxes) { ptr = largest_box(); if (ptr) splitbox(ptr); else break; } /**** STEP 4: assign colors to all boxes ****/ for (i=0, ptr=usedboxes; i<num_colors && ptr; i++, ptr=ptr->next) { assign_color(ptr, &r[i], &g[i], &b[i]); } /* We're done with the boxes now */ num_colors = i; free(box_list); box_list = freeboxes = usedboxes = NULL; /**** STEP 5: scan histogram and map all values to closest color */ /* 5a: create cell list as described in Heckbert[2] */ ColorCells = (CCELL **) calloc(C_LEN*C_LEN*C_LEN, sizeof(CCELL *)); /* 5b: create mapping from truncated pixel space to color table entries */ map_colortable(); /**** STEP 6: scan image, match input values to table entries */ i=quant_fsdither(); /* free everything that can be freed */ free(ColorCells); return i; } /****************************/ static void get_histogram(box) CBOX *box; /****************************/ { int i,j,r,g,b,*ptr; byte *p; box->rmin = box->gmin = box->bmin = 999; box->rmax = box->gmax = box->bmax = -1; box->total = WIDE * HIGH; /* zero out histogram */ ptr = &histogram[0][0][0]; for (i=B_LEN*B_LEN*B_LEN; i>0; i-- ) *ptr++ = 0; /* calculate histogram */ p = pic24; for (i=0; i<HIGH; i++) for (j=0; j<WIDE; j++) { r = (*p++) >> (COLOR_DEPTH-B_DEPTH); g = (*p++) >> (COLOR_DEPTH-B_DEPTH); b = (*p++) >> (COLOR_DEPTH-B_DEPTH); if (r < box->rmin) box->rmin = r; if (r > box->rmax) box->rmax = r; if (g < box->gmin) box->gmin = g; if (g > box->gmax) box->gmax = g; if (b < box->bmin) box->bmin = b; if (b > box->bmax) box->bmax = b; histogram[r][g][b]++; } } /******************************/ static CBOX *largest_box() /******************************/ { CBOX *tmp, *ptr; int size = -1; tmp = usedboxes; ptr = NULL; while (tmp) { if ( (tmp->rmax > tmp->rmin || tmp->gmax > tmp->gmin || tmp->bmax > tmp->bmin) && tmp->total > size ) { ptr = tmp; size = tmp->total; } tmp = tmp->next; } return(ptr); } /******************************/ static void splitbox(ptr) CBOX *ptr; /******************************/ { int hist2[B_LEN], first, last, i, rdel, gdel, bdel; CBOX *new; int *iptr, *histp, ir, ig, ib; int rmin,rmax,gmin,gmax,bmin,bmax; enum {RED,GREEN,BLUE} which; /* * see which axis is the largest, do a histogram along that * axis. Split at median point. Contract both new boxes to * fit points and return */ first = last = 0; /* shut RT hcc compiler up */ rmin = ptr->rmin; rmax = ptr->rmax; gmin = ptr->gmin; gmax = ptr->gmax; bmin = ptr->bmin; bmax = ptr->bmax; rdel = rmax - rmin; gdel = gmax - gmin; bdel = bmax - bmin; if (rdel>=gdel && rdel>=bdel) which = RED; else if (gdel>=bdel) which = GREEN; else which = BLUE; /* get histogram along longest axis */ switch (which) { case RED: histp = &hist2[rmin]; for (ir=rmin; ir<=rmax; ir++) { *histp = 0; for (ig=gmin; ig<=gmax; ig++) { iptr = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) { *histp += *iptr; ++iptr; } } ++histp; } first = rmin; last = rmax; break; case GREEN: histp = &hist2[gmin]; for (ig=gmin; ig<=gmax; ig++) { *histp = 0; for (ir=rmin; ir<=rmax; ir++) { iptr = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) { *histp += *iptr; ++iptr; } } ++histp; } first = gmin; last = gmax; break; case BLUE: histp = &hist2[bmin]; for (ib=bmin; ib<=bmax; ib++) { *histp = 0; for (ir=rmin; ir<=rmax; ir++) { iptr = &histogram[ir][gmin][ib]; for (ig=gmin; ig<=gmax; ig++) { *histp += *iptr; iptr += B_LEN; } } ++histp; } first = bmin; last = bmax; break; } /* find median point */ { int sum, sum2; histp = &hist2[first]; sum2 = ptr->total/2; histp = &hist2[first]; sum = 0; for (i=first; i<=last && (sum += *histp++)<sum2; i++); if (i==first) i++; } /* Create new box, re-allocate points */ new = freeboxes; freeboxes = new->next; if (freeboxes) freeboxes->prev = NULL; if (usedboxes) usedboxes->prev = new; new->next = usedboxes; usedboxes = new; { int sum1,sum2,j; histp = &hist2[first]; sum1 = 0; for (j = first; j < i; ++j) sum1 += *histp++; sum2 = 0; for (j = i; j <= last; ++j) sum2 += *histp++; new->total = sum1; ptr->total = sum2; } new->rmin = rmin; new->rmax = rmax; new->gmin = gmin; new->gmax = gmax; new->bmin = bmin; new->bmax = bmax; switch (which) { case RED: new->rmax = i-1; ptr->rmin = i; break; case GREEN: new->gmax = i-1; ptr->gmin = i; break; case BLUE: new->bmax = i-1; ptr->bmin = i; break; } shrinkbox(new); shrinkbox(ptr); } /****************************/ static void shrinkbox(box) CBOX *box; /****************************/ { int *histp,ir,ig,ib; int rmin,rmax,gmin,gmax,bmin,bmax; rmin = box->rmin; rmax = box->rmax; gmin = box->gmin; gmax = box->gmax; bmin = box->bmin; bmax = box->bmax; if (rmax>rmin) { for (ir=rmin; ir<=rmax; ir++) for (ig=gmin; ig<=gmax; ig++) { histp = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) if (*histp++ != 0) { box->rmin = rmin = ir; goto have_rmin; } } have_rmin: if (rmax>rmin) for (ir=rmax; ir>=rmin; --ir) for (ig=gmin; ig<=gmax; ig++) { histp = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) if (*histp++ != 0) { box->rmax = rmax = ir; goto have_rmax; } } } have_rmax: if (gmax>gmin) { for (ig=gmin; ig<=gmax; ig++) for (ir=rmin; ir<=rmax; ir++) { histp = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) if (*histp++ != 0) { box->gmin = gmin = ig; goto have_gmin; } } have_gmin: if (gmax>gmin) for (ig=gmax; ig>=gmin; --ig) for (ir=rmin; ir<=rmax; ir++) { histp = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) if (*histp++ != 0) { box->gmax = gmax = ig; goto have_gmax; } } } have_gmax: if (bmax>bmin) { for (ib=bmin; ib<=bmax; ib++) for (ir=rmin; ir<=rmax; ir++) { histp = &histogram[ir][gmin][ib]; for (ig=gmin; ig<=gmax; ig++) { if (*histp != 0) { box->bmin = bmin = ib; goto have_bmin; } histp += B_LEN; } } have_bmin: if (bmax>bmin) for (ib=bmax; ib>=bmin; --ib) for (ir=rmin; ir<=rmax; ir++) { histp = &histogram[ir][gmin][ib]; for (ig=gmin; ig<=gmax; ig++) { if (*histp != 0) { bmax = ib; goto have_bmax; } histp += B_LEN; } } } have_bmax: return; } /*******************************/ static void assign_color(ptr,rp,gp,bp) CBOX *ptr; byte *rp,*gp,*bp; /*******************************/ { *rp = ((ptr->rmin + ptr->rmax) << (COLOR_DEPTH - B_DEPTH)) / 2; *gp = ((ptr->gmin + ptr->gmax) << (COLOR_DEPTH - B_DEPTH)) / 2; *bp = ((ptr->bmin + ptr->bmax) << (COLOR_DEPTH - B_DEPTH)) / 2; } /*******************************/ static CCELL *create_colorcell(r1,g1,b1) int r1,g1,b1; /*******************************/ { register int i,tmp, dist; register CCELL *ptr; register byte *rp,*gp,*bp; int ir,ig,ib, mindist; ir = r1 >> (COLOR_DEPTH-C_DEPTH); ig = g1 >> (COLOR_DEPTH-C_DEPTH); ib = b1 >> (COLOR_DEPTH-C_DEPTH); r1 &= ~1 << (COLOR_DEPTH-C_DEPTH); g1 &= ~1 << (COLOR_DEPTH-C_DEPTH); b1 &= ~1 << (COLOR_DEPTH-C_DEPTH); ptr = (CCELL *) malloc(sizeof(CCELL)); *(ColorCells + ir*C_LEN*C_LEN + ig*C_LEN + ib) = ptr; ptr->num_ents = 0; /* step 1: find all colors inside this cell, while we're at it, find distance of centermost point to furthest corner */ mindist = 99999999; rp=r; gp=g; bp=b; for (i=0; i<num_colors; i++,rp++,gp++,bp++) if( *rp>>(COLOR_DEPTH-C_DEPTH) == ir && *gp>>(COLOR_DEPTH-C_DEPTH) == ig && *bp>>(COLOR_DEPTH-C_DEPTH) == ib) { ptr->entries[ptr->num_ents][0] = i; ptr->entries[ptr->num_ents][1] = 0; ++ptr->num_ents; tmp = *rp - r1; if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp; dist = tmp*tmp; tmp = *gp - g1; if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp; dist += tmp*tmp; tmp = *bp - b1; if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp; dist += tmp*tmp; if (dist < mindist) mindist = dist; } /* step 3: find all points within that distance to box */ rp=r; gp=g; bp=b; for (i=0; i<num_colors; i++,rp++,gp++,bp++) if (*rp >> (COLOR_DEPTH-C_DEPTH) != ir || *gp >> (COLOR_DEPTH-C_DEPTH) != ig || *bp >> (COLOR_DEPTH-C_DEPTH) != ib) { dist = 0; if ((tmp = r1 - *rp)>0 || (tmp = *rp - (r1 + MAX_COLOR/C_LEN-1)) > 0 ) dist += tmp*tmp; if( (tmp = g1 - *gp)>0 || (tmp = *gp - (g1 + MAX_COLOR/C_LEN-1)) > 0 ) dist += tmp*tmp; if( (tmp = b1 - *bp)>0 || (tmp = *bp - (b1 + MAX_COLOR/C_LEN-1)) > 0 ) dist += tmp*tmp; if( dist < mindist ) { ptr->entries[ptr->num_ents][0] = i; ptr->entries[ptr->num_ents][1] = dist; ++ptr->num_ents; } } /* sort color cells by distance, use cheap exchange sort */ { int n, next_n; n = ptr->num_ents - 1; while (n>0) { next_n = 0; for (i=0; i<n; ++i) { if (ptr->entries[i][1] > ptr->entries[i+1][1]) { tmp = ptr->entries[i][0]; ptr->entries[i][0] = ptr->entries[i+1][0]; ptr->entries[i+1][0] = tmp; tmp = ptr->entries[i][1]; ptr->entries[i][1] = ptr->entries[i+1][1]; ptr->entries[i+1][1] = tmp; next_n = i; } } n = next_n; } } return (ptr); } /***************************/ static void map_colortable() /***************************/ { int ir,ig,ib, *histp; CCELL *cell; histp = &histogram[0][0][0]; for (ir=0; ir<B_LEN; ir++) for (ig=0; ig<B_LEN; ig++) for (ib=0; ib<B_LEN; ib++) { if (*histp==0) *histp = -1; else { int i, j, tmp, d2, dist; cell = *(ColorCells + ( ((ir>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2) + ((ig>>(B_DEPTH-C_DEPTH)) << C_DEPTH) + (ib>>(B_DEPTH-C_DEPTH)) ) ); if (cell==NULL) cell = create_colorcell(ir<<(COLOR_DEPTH-B_DEPTH), ig<<(COLOR_DEPTH-B_DEPTH), ib<<(COLOR_DEPTH-B_DEPTH)); dist = 9999999; for (i=0; i<cell->num_ents && dist>cell->entries[i][1]; i++) { j = cell->entries[i][0]; d2 = r[j] - (ir << (COLOR_DEPTH-B_DEPTH)); d2 *= d2; tmp = g[j] - (ig << (COLOR_DEPTH-B_DEPTH)); d2 += tmp*tmp; tmp = b[j] - (ib << (COLOR_DEPTH-B_DEPTH)); d2 += tmp*tmp; if( d2 < dist ) { dist = d2; *histp = j; } } } histp++; } } /*****************************/ static int quant_fsdither() /*****************************/ { register int *thisptr, *nextptr; int *thisline, *nextline, *tmpptr; int r1, g1, b1, r2, g2, b2; int i, j, imax, jmax, oval; byte *inptr, *outptr, *tmpbptr; int lastline, lastpixel; imax = HIGH - 1; jmax = WIDE - 1; thisline = (int *) malloc(WIDE * 3 * sizeof(int)); nextline = (int *) malloc(WIDE * 3 * sizeof(int)); if (thisline == NULL || nextline == NULL) { fprintf(stderr,"%s: unable to allocate stuff for the 'dither' routine\n", cmd); return 1; } inptr = (byte *) pic24; outptr = (byte *) pic; /* get first line of picture */ for (j=WIDE * 3, tmpptr=nextline, tmpbptr=inptr; j; j--) *tmpptr++ = (int) *tmpbptr++; for (i=0; i<HIGH; i++) { /* swap thisline and nextline */ tmpptr = thisline; thisline = nextline; nextline = tmpptr; lastline = (i==imax); /* read in next line */ for (j=WIDE * 3, tmpptr=nextline; j; j--) *tmpptr++ = (int) *inptr++; /* dither this line and put it into the output picture */ thisptr = thisline; nextptr = nextline; for (j=0; j<WIDE; j++) { lastpixel = (j==jmax); r2 = *thisptr++; g2 = *thisptr++; b2 = *thisptr++; if (r2<0) r2=0; else if (r2>=MAX_COLOR) r2=MAX_COLOR-1; if (g2<0) g2=0; else if (g2>=MAX_COLOR) g2=MAX_COLOR-1; if (b2<0) b2=0; else if (b2>=MAX_COLOR) b2=MAX_COLOR-1; r1 = r2; g1 = g2; b1 = b2; r2 >>= (COLOR_DEPTH-B_DEPTH); g2 >>= (COLOR_DEPTH-B_DEPTH); b2 >>= (COLOR_DEPTH-B_DEPTH); if ( (oval=histogram[r2][g2][b2]) == -1) { int ci, cj, dist, d2, tmp; CCELL *cell; cell = *( ColorCells + ( ((r2>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2) + ((g2>>(B_DEPTH-C_DEPTH)) << C_DEPTH ) + (b2>>(B_DEPTH-C_DEPTH)) ) ); if (cell==NULL) cell = create_colorcell(r1,g1,b1); dist = 9999999; for (ci=0; ci<cell->num_ents && dist>cell->entries[ci][1]; ci++) { cj = cell->entries[ci][0]; d2 = (r[cj] >> (COLOR_DEPTH-B_DEPTH)) - r2; d2 *= d2; tmp = (g[cj] >> (COLOR_DEPTH-B_DEPTH)) - g2; d2 += tmp*tmp; tmp = (b[cj] >> (COLOR_DEPTH-B_DEPTH)) - b2; d2 += tmp*tmp; if (d2<dist) { dist = d2; oval = cj; } } histogram[r2][g2][b2] = oval; } *outptr++ = oval; r1 -= r[oval]; g1 -= g[oval]; b1 -= b[oval]; /* can't use tables because r1,g1,b1 go negative */ if (!lastpixel) { thisptr[0] += (r1*7)/16; thisptr[1] += (g1*7)/16; thisptr[2] += (b1*7)/16; } if (!lastline) { if (j) { nextptr[-3] += (r1*3)/16; nextptr[-2] += (g1*3)/16; nextptr[-1] += (b1*3)/16; } nextptr[0] += (r1*5)/16; nextptr[1] += (g1*5)/16; nextptr[2] += (b1*5)/16; if (!lastpixel) { nextptr[3] += r1/16; nextptr[4] += g1/16; nextptr[5] += b1/16; } nextptr += 3; } } } free(thisline); free(nextline); return 0; } /************************************/ static int Quick24to8(p24,w,h) byte *p24; int w,h; { /* floyd-steinberg dithering. * * ---- x 7/16 * 3/16 5/16 1/16 * */ /* called after 'pic' has been alloced, pWIDE,pHIGH set up, mono/1-bit checked already */ byte *pp; int r1, g1, b1; int *thisline, *nextline, *thisptr, *nextptr, *tmpptr; int i, j, rerr, gerr, berr, pwide3; int imax, jmax; pp = pic; pwide3 = w * 3; imax = h-1; jmax = w-1; /* load up colormap, 3 bits R, 3 bits G, 2 bits B (RRRGGGBB) */ #ifdef OLDCOLORS for (i=0; i<256; i++) { r[i] = i & 0xe0; g[i] = (i & 0x1c) << 3; b[i] = (i & 0x03) << 6; } #else for (i=0; i<256; i++) { r[i] = ((i&0xe0) * 255) / 0xe0; g[i] = ((i&0x1c) * 255) / 0x1c; b[i] = ((i&0x03) * 255) / 0x03; } #endif thisline = (int *) malloc(pwide3 * sizeof(int)); nextline = (int *) malloc(pwide3 * sizeof(int)); if (!thisline || !nextline) { fprintf(stderr,"%s: unable to allocate memory in Quick24to8()\n", cmd); return(1); } /* get first line of picture */ for (j=pwide3, tmpptr=nextline; j; j--) *tmpptr++ = (int) *p24++; for (i=0; i<h; i++) { tmpptr = thisline; thisline = nextline; nextline = tmpptr; /* swap */ if (i!=imax) /* get next line */ for (j=pwide3, tmpptr=nextline; j; j--) *tmpptr++ = (int) *p24++; for (j=0, thisptr=thisline, nextptr=nextline; j<w; j++,pp++) { r1 = *thisptr++; g1 = *thisptr++; b1 = *thisptr++; RANGE(r1,0,255); RANGE(g1,0,255); RANGE(b1,0,255); rerr = r1 & 0x1f; gerr = g1 & 0x1f; berr = b1 & 0x3f; *pp = (r1&0xe0) | ((g1>>3)&0x1c) | (b1>>6); if (j!=jmax) { /* adjust RIGHT pixel */ thisptr[0] += tbl7[rerr]; thisptr[1] += tbl7[gerr]; thisptr[2] += tbl7[berr]; } if (i!=imax) { /* do BOTTOM pixel */ nextptr[0] += tbl5[rerr]; nextptr[1] += tbl5[gerr]; nextptr[2] += tbl5[berr]; if (j>0) { /* do BOTTOM LEFT pixel */ nextptr[-3] += tbl3[rerr]; nextptr[-2] += tbl3[gerr]; nextptr[-1] += tbl3[berr]; } if (j!=jmax) { /* do BOTTOM RIGHT pixel */ nextptr[3] += tbl1[rerr]; nextptr[4] += tbl1[gerr]; nextptr[5] += tbl1[berr]; } nextptr += 3; } } } return 0; } /****************************/ void InitFSDTables() /****************************/ { int i; for (i=0; i<256; i++) { /* initialize Floyd-Steinberg division tables */ tbl1[i] = i/16; tbl3[i] = (3*i)/16; tbl5[i] = (5*i)/16; tbl7[i] = (7*i)/16; } } /****************************/ static int QuickCheck(pic24,w,h,maxcol) byte *pic24; int w,h,maxcol; { /* scans picture until it finds more than 'maxcol' different colors. If it finds more than 'maxcol' colors, it returns '0'. If it DOESN'T, it does the 24-to-8 conversion by simply sticking the colors it found into a colormap, and changing instances of a color in pic24 into colormap indicies (in pic) */ unsigned long colors[256],col; int i, nc, low, high, mid, k; byte *p, *pix; if (maxcol>256) maxcol = 256; /* put the first color in the table by hand */ nc = 0; for (i=w*h,p=pic24; i; i--) { col = (*p++ << 16); col += (*p++ << 8); col += *p++; /* binary search the 'colors' array to see if it's in there */ low = 0; high = nc-1; while (low <= high) { mid = (low+high)/2; if (col < colors[mid]) high = mid - 1; else if (col > colors[mid]) low = mid + 1; else break; } if (high < low) { /* didn't find color in list, add it. */ /* WARNING: this is an overlapped memory copy. memcpy doesn't do it correctly, hence 'bcopy', which claims to */ bcopy(&colors[low], &colors[low+1], (nc - low) * sizeof(unsigned long)); colors[low] = col; nc++; if (nc>maxcol) return 0; } } /* run through the data a second time, this time mapping pixel values in pic24 into colormap offsets into 'colors' */ for (i=w*h,p=pic24, pix=pic; i; i--,pix++) { col = (*p++ << 16); col += (*p++ << 8); col += *p++; /* binary search the 'colors' array. It *IS* in there */ low = 0; high = nc-1; while (low <= high) { mid = (low+high)/2; if (col < colors[mid]) high = mid - 1; else if (col > colors[mid]) low = mid + 1; else break; } if (high < low) { fprintf(stderr,"QuickCheck: impossible!\n"); exit(1); } *pix = mid; } /* and load up the 'desired colormap' */ for (i=0; i<nc; i++) { r[i] = colors[i]>>16; g[i] = (colors[i]>>8) & 0xff; b[i] = colors[i] & 0xff; } return 1; } \BARFOO\ else echo "will not over write ./xv24to8.c" fi if `test ! -s ./xvbutt.c` then echo "writting ./xvbutt.c" cat > ./xvbutt.c << '\BARFOO\' /* * xvbutt.c - regular and 'radio' pushbuttons * * callable functions: * * BTCreate() - create a button * BTSetActive() - change 'active' status of button * BTRedraw() - redraw button * BTTrack() - clicked in button. track until mouse up * * RBCreate() - create an RBUTT and append to supplied list * RBRedraw() - redraw one or all RBUTTs in a list * RBSelect() - change selected item in list of RBUTTs * RBWhich() - returns index of selected RBUTT in list * RBCount() - returns # of RBUTTs in list * RBSetActive() - sets active status of an RBUTT * RBClick() - finds clicked-on rb in a list * RBTrack() - tracks rb after click, until release */ /* * Copyright 1989, 1990 by the University of Pennsylvania * * Permission to use, copy, and distribute for non-commercial purposes, * is hereby granted without fee, providing that the above copyright * notice appear in all copies and that both the copyright notice and this * permission notice appear in supporting documentation. * * The software may be modified for your own purposes, but modified versions * may not be distributed. * * This software is provided "as is" without any express or implied warranty. */ #include "xv.h" #include "bitmaps.h" static int rbpixmade = 0; static Pixmap rbon, rboff, rbon1, rboff1; #ifdef __STDC__ static void drawRB(RBUTT *); #else static void drawRB(); #endif /**********************************************/ void BTCreate(bp,win,x,y,w,h,str,fg,bg) BUTT *bp; Window win; int x,y,w,h; char *str; unsigned long fg,bg; { bp->win = win; bp->x = x; bp->y = y; bp->w = w; bp->h = h; bp->str = str; bp->fg = fg; bp->bg = bg; bp->lit = 0; bp->active = 1; bp->toggle = 0; } /**********************************************/ void BTSetActive(bp,act) BUTT *bp; int act; { if (bp->active != act) { bp->active = act; BTRedraw(bp); } } /**********************************************/ void BTRedraw(bp) BUTT *bp; { int x,y,w,h,r; XPoint poly[9]; x = bp->x; y=bp->y; w=bp->w; h=bp->h; r=3; /* set up the polygon */ poly[0].x = x; poly[0].y = y+r; poly[1].x = x; poly[1].y = y+h-r; poly[2].x = x+r; poly[2].y = y+h; poly[3].x = x+w-r; poly[3].y = y+h; poly[4].x = x+w; poly[4].y = y+h-r; poly[5].x = x+w; poly[5].y = y+r; poly[6].x = x+w-r; poly[6].y = y; poly[7].x = x+r; poly[7].y = y; poly[8].x = x; poly[8].y = y+r; if (!bp->active) bp->lit = 0; /* sanity assertion */ if (bp->lit) XSetForeground(theDisp, theGC, bp->fg); else XSetForeground(theDisp, theGC, bp->bg); XFillPolygon(theDisp,bp->win,theGC,poly,9,Convex,CoordModeOrigin); XSetForeground(theDisp, theGC, bp->fg); XDrawLines(theDisp,bp->win,theGC,poly,9,CoordModeOrigin); if (!bp->active) { /* stipple the text if not active */ XSetFillStyle(theDisp, theGC, FillStippled); XSetStipple(theDisp, theGC, grayStip); } if (bp->lit) XSetForeground(theDisp, theGC, bp->bg); else XSetForeground(theDisp, theGC, bp->fg); XDrawString(theDisp, bp->win, theGC, CENTERX(mfinfo,x+w/2,bp->str), CENTERY(mfinfo,y+h/2), bp->str, strlen(bp->str)); if (!bp->active) XSetFillStyle(theDisp,theGC,FillSolid); /* back to norm */ } /**********************************************/ int BTTrack(bp) BUTT *bp; { /* called when we've gotten a click inside 'bp'. returns 1 if button was still selected lit when mouse was released. */ Window rW, cW; int x, y, rx, ry, rval, inval; unsigned int mask; if (!bp->active) return 0; /* inactive button */ bp->lit = !bp->lit; inval = bp->lit; BTRedraw(bp); XFlush(theDisp); Timer(75); /* long enough for turn on to be visible */ while (XQueryPointer(theDisp,bp->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) { if (!(mask & Button1Mask)) break; /* button released */ if (bp->lit!=inval && PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) { bp->lit = inval; BTRedraw(bp); XFlush(theDisp); } if (bp->lit==inval && !PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) { bp->lit = !inval; BTRedraw(bp); XFlush(theDisp); } } rval = (bp->lit == inval); if (bp->lit && !bp->toggle) { bp->lit = 0; BTRedraw(bp); XFlush(theDisp); } return(rval); } /***********************************************/ RBUTT *RBCreate(rblist, win, x,y,str, fg, bg) RBUTT *rblist; Window win; int x,y; char *str; unsigned long fg,bg; { /* mallocs an RBUTT, fills in the fields, and appends it to rblist if rblist is NULL, this is the first rb in the list. It will be made the 'selected' one Note: no need to check return status. It'll fatal error if it can't malloc */ RBUTT *rb, *rbptr; rb = (RBUTT *) malloc(sizeof(RBUTT)); if (!rb) FatalError("couldn't malloc RBUTT"); /* fill in the fields of the structure */ rb->win = win; rb->x = x; rb->y = y; rb->str = str; rb->selected = 0; rb->active = 1; rb->next = (RBUTT *) NULL; rb->fg = fg; rb->bg = bg; if (rblist) { /* append to end of list */ rbptr = rblist; while (rbptr->next) rbptr = rbptr->next; rbptr->next = rb; } else { /* this is the first one in the list. select it */ rb->selected = 1; } /* and, on an unrelated note, if the RB pixmaps haven't been created yet, do so. We'll be needing them, y'see... */ if (!rbpixmade) { rbon = XCreatePixmapFromBitmapData(theDisp, rootW, rb_on_bits, rb_on_width, rb_on_height, fg, bg, dispDEEP); rboff = XCreatePixmapFromBitmapData(theDisp, rootW, rb_off_bits, rb_off_width, rb_off_height, fg, bg, dispDEEP); rbon1 = XCreatePixmapFromBitmapData(theDisp, rootW, rb_on1_bits, rb_on1_width, rb_on1_height, fg, bg, dispDEEP); rboff1= XCreatePixmapFromBitmapData(theDisp, rootW, rb_off1_bits, rb_off1_width, rb_off1_height, fg, bg, dispDEEP); rbpixmade = 1; } return(rb); } /***********************************************/ void RBRedraw(rblist, num) RBUTT *rblist; int num; { /* redraws the 'num-th' RB in the list. if num < 0, redraws entire list */ RBUTT *rb; int i; /* point 'rb' at the appropriate RBUTT, *if* we're not drawing entire list */ if (num>=0) { i=0; rb=rblist; while (i!=num && rb) { rb = rb->next; i++; } if (!rb) return; /* num is out of range. do nothing */ drawRB(rb); } else { /* draw entire list */ rb = rblist; while (rb) { drawRB(rb); rb = rb->next; } } } static void drawRB(rb) RBUTT *rb; { /* draws the rb being pointed at */ if (!rb) return; /* rb = NULL */ XSetForeground(theDisp, theGC, rb->fg); XSetBackground(theDisp, theGC, rb->bg); if (rb->selected) XCopyArea(theDisp, rbon, rb->win, theGC, 0, 0, rb_on_width, rb_on_height, rb->x, rb->y); else XCopyArea(theDisp, rboff, rb->win, theGC, 0, 0, rb_on_width, rb_on_height, rb->x, rb->y); XDrawString(theDisp, rb->win, theGC, rb->x+rb_on_width+4, rb->y+rb_on_height/2 - CHIGH/2 + ASCENT,rb->str,strlen(rb->str)); /* if non-active, dim button and string */ if (!rb->active) { /* stipple the RB by drawing 'bg' where there's 1's in the stipple */ XSetFillStyle(theDisp, theGC, FillStippled); XSetStipple(theDisp, theGC, grayStip); XSetForeground(theDisp, theGC, rb->bg); XFillRectangle(theDisp,rb->win,theGC,rb->x,rb->y,rb_on_width,rb_on_height); XFillRectangle(theDisp,rb->win,theGC, rb->x + rb_on_width+4, rb->y+rb_on_height/2 - CHIGH/2, StringWidth(rb->str),CHIGH); XSetFillStyle(theDisp, theGC, FillSolid); } } /***********************************************/ void RBSelect(rblist, n) RBUTT *rblist; int n; { RBUTT *rbold, *rb; int i; /* makes rb #n the selected rb in the list. Does all redrawing. Does nothing if rb already selected */ /* get pointers to the currently selected rb and the desired rb */ rbold = rblist; while (rbold && !rbold->selected) rbold = rbold->next; if (!rbold) return; /* no currently selected item. shouldn't happen */ rb = rblist; i=0; while (rb && i!=n) {rb = rb->next; i++; } if (!rb) return; /* 'n' is out of range */ if (rb == rbold) return; /* 'n' is already selected. do nothing */ rbold->selected = 0; rb->selected = 1; drawRB(rbold); drawRB(rb); } /***********************************************/ int RBWhich(rblist) RBUTT *rblist; { int i; /* returns index of currently selected rb. if none, returns -1 */ i = 0; while (rblist && !rblist->selected) { rblist = rblist->next; i++; } if (!rblist) return -1; /* didn't find one */ return i; } /***********************************************/ int RBCount(rblist) RBUTT *rblist; { int i; /* returns # of rb's in the list */ i = 0; while (rblist) { rblist = rblist->next; i++; } return i; } /***********************************************/ void RBSetActive(rblist, n, act) RBUTT *rblist; int n,act; { RBUTT *rb; int i; /* sets 'active' status of rb #n. does redrawing */ rb=rblist; i=0; while (rb && i!=n) { rb = rb->next; i++; } if (!rb) return; /* n out of range. do nothing */ if (rb->active != act) { rb->active = act; drawRB(rb); } } /***********************************************/ int RBClick(rblist, mx, my) RBUTT *rblist; int mx,my; { int i; /* searches through rblist to see if mouse click at mx,my is in the clickable region of any of the rb's. If it finds one, it returns it's index in the list. If not, returns -1 */ i = 0; while (rblist) { if (PTINRECT(mx, my, rblist->x, rblist->y, rb_on_width, rb_on_height)) break; rblist = rblist->next; i++; } if (!rblist) return -1; return(i); } /***********************************************/ void RBTrack(rblist, n) RBUTT *rblist; int n; { RBUTT *rb; Window rW, cW; int i, x, y, rx, ry, lit; unsigned int mask; Pixmap litpix, darkpix; rb=rblist; i=0; while (rb && i!=n) { rb = rb->next; i++; } if (!rb) return; /* n out of range */ /* called once we've figured out that the mouse clicked in 'rb' */ if (!rb->active) return; if (rb->selected) { litpix = rbon1; darkpix = rbon; } else { litpix = rboff1; darkpix = rboff; } lit = 1; XCopyArea(theDisp, litpix, rb->win, theGC, 0, 0, rb_on_width, rb_on_height, rb->x, rb->y); XFlush(theDisp); Timer(75); /* give chance for 'turn on' to become visible */ while (XQueryPointer(theDisp,rb->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) { if (!(mask & Button1Mask)) break; /* button released */ if (!lit && PTINRECT(x, y, rb->x, rb->y, rb_on_width, rb_on_height)) { lit=1; XCopyArea(theDisp, litpix, rb->win, theGC, 0,0,rb_on_width,rb_on_height, rb->x, rb->y); XFlush(theDisp); } if (lit && !PTINRECT(x, y, rb->x, rb->y, rb_on_width, rb_on_height)) { lit=0; XCopyArea(theDisp, darkpix,rb->win,theGC, 0,0,rb_on_width,rb_on_height, rb->x, rb->y); XFlush(theDisp); } } if (lit) { XCopyArea(theDisp, darkpix, rb->win, theGC, 0, 0, rb_on_width, rb_on_height, rb->x, rb->y); RBSelect(rblist, n); } XFlush(theDisp); } \BARFOO\ else echo "will not over write ./xvbutt.c" fi if `test ! -s ./xvctrl.c` then echo "writting ./xvctrl.c" cat > ./xvctrl.c << '\BARFOO\' /* * xvctrl.c - Control box handling functions * * callable functions: * * CreateCtrl(geom) - creates the ctrlW window. Doesn't map it. * CtrlBox(vis) - random processing based on value of 'vis' * maps/unmaps window, etc. * RedrawCtrl(x,y,w,h) - called by 'expose' events * ClickCtrl(x,y) * DrawCtrlStr() - called to redraw 'ISTR_INFO' string in ctrlW * ScrollToCurrent() - called when 'curname' is changed * * LSCreate() - creates a listbox * LSRedraw() - redraws 'namelist' box * LSClick() - operates list box * LSNewData() - called when strings or number of them change * */ /* * Copyright 1989, 1990 by the University of Pennsylvania * * Permission to use, copy, and distribute for non-commercial purposes, * is hereby granted without fee, providing that the above copyright * notice appear in all copies and that both the copyright notice and this * permission notice appear in supporting documentation. * * The software may be modified for your own purposes, but modified versions * may not be distributed. * * This software is provided "as is" without any express or implied warranty. */ #include "xv.h" #include "bitmaps.h" #define DBLCLKTIME 500 /* double-click speed in milliseconds */ #define INACTIVE(lptr, item) ((lptr)->filetypes && (lptr)->dirsonly && \ (item) >= 0 && (item) < (lptr)->nstr && \ (lptr)->str[(item)][0] != C_DIR && \ (lptr)->str[(item)][0] != C_LNK) #define NLINES 9 /* # of lines in list control (keep odd) */ #define LISTW 330 #define BUTTW 60 #define BUTTH 19 static int listh; /* height of list/scrl controls */ static int ptop; /* y-coord of top of button area in ctrlW */ static Pixmap fifoPix, chrPix, dirPix, blkPix, lnkPix, sockPix, regPix; #ifdef __STDC__ static void drawSel(LIST *, int); static void RedrawNList(void); #else static void drawSel(), RedrawNList(); #endif /***************************************************/ void CreateCtrl(geom) char *geom; { int i; ctrlW = CreateWindow("xv controls", geom, CTRLWIDE,CTRLHIGH,infofg,infobg); if (!ctrlW) FatalError("can't create controls window!"); grayTile = XCreatePixmapFromBitmapData(theDisp, ctrlW, gray25_bits, gray25_width, gray25_height, infofg, infobg, dispDEEP); grayStip = XCreatePixmapFromBitmapData(theDisp, ctrlW, gray50_bits, gray50_width, gray50_height, 1, 0, 1); fifoPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_fifo_bits, i_fifo_width, i_fifo_height, 1, 0, 1); chrPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_chr_bits, i_chr_width, i_chr_height, 1,0,1); dirPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_dir_bits, i_dir_width, i_dir_height, 1,0,1); blkPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_blk_bits, i_blk_width, i_blk_height, 1,0,1); lnkPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_lnk_bits, i_lnk_width, i_lnk_height, 1,0,1); sockPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_sock_bits, i_sock_width, i_sock_height, 1,0,1); regPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_reg_bits, i_reg_width, i_reg_height, 1,0,1); XSetWindowBackgroundPixmap(theDisp, ctrlW, grayTile); /* create doo-wahs */ listh = LINEHIGH * NLINES; LSCreate(&nList, ctrlW, 10, 10+CHIGH+3, LISTW, listh, NLINES, dispnames, numnames, infofg, infobg, RedrawNList, 0, 0); ptop = CTRLHIGH - (2*BUTTH + 3*8); i = listh-BUTTH; BTCreate(&but[BNEXT], ctrlW, 368, nList.y+(i*0)/4, 60, BUTTH, "Next", infofg, infobg); BTCreate(&but[BPREV], ctrlW, 368, nList.y+(i*1)/4, 60, BUTTH, "Previous", infofg, infobg); BTCreate(&but[BINFO], ctrlW, 368, nList.y+(i*2)/4, 60, BUTTH, "Info", infofg, infobg); BTCreate(&but[BSAVE], ctrlW, 368, nList.y+(i*3)/4, 60, BUTTH, "Save", infofg, infobg); BTCreate(&but[BQUIT], ctrlW, 368, nList.y+(i*4)/4, 60, BUTTH, "Quit", infofg, infobg); BTCreate(&but[BCROP], ctrlW, 10, ptop+8, BUTTW, BUTTH, "Crop", infofg, infobg); BTCreate(&but[BUNCROP], ctrlW, 10, ptop + BUTTH + 2*8, BUTTW, BUTTH, "UnCrop", infofg, infobg); BTCreate(&but[BMAX], ctrlW, 10+(CTRLWIDE-20-BUTTW)/5, ptop+8, BUTTW, BUTTH, "Max Size", infofg, infobg); BTCreate(&but[BNORM], ctrlW, 10+(CTRLWIDE-20-BUTTW)/5, ptop + BUTTH + 2*8, BUTTW, BUTTH, "Normal", infofg, infobg); BTCreate(&but[BUP2], ctrlW, 10+(2*(CTRLWIDE-20-BUTTW))/5, ptop+8, BUTTW, BUTTH, "Dbl Size", infofg, infobg); BTCreate(&but[BDN2], ctrlW, 10+(2*(CTRLWIDE-20-BUTTW))/5, ptop+BUTTH+2*8, BUTTW, BUTTH, "Half Size", infofg, infobg); BTCreate(&but[BUP10], ctrlW, 10+(3*(CTRLWIDE-20-BUTTW))/5, ptop+8, BUTTW, BUTTH, "+10%", infofg, infobg); BTCreate(&but[BDN10], ctrlW, 10+(3*(CTRLWIDE-20-BUTTW))/5, ptop+BUTTH+2*8, BUTTW, BUTTH, "-10%", infofg, infobg); BTCreate(&but[B4BY3], ctrlW, 10+(4*(CTRLWIDE-20-BUTTW))/5, ptop+8, BUTTW, BUTTH, "4x3", infofg, infobg); BTCreate(&but[BASPECT], ctrlW, 10+(4*(CTRLWIDE-20-BUTTW))/5, ptop+BUTTH+2*8, BUTTW, BUTTH, "Aspect", infofg, infobg); BTCreate(&but[BROTATE], ctrlW, 10+(5*(CTRLWIDE-20-BUTTW))/5, ptop+8, BUTTW, BUTTH, "Rotate", infofg, infobg); BTCreate(&but[BGAMMA], ctrlW, 10+(5*(CTRLWIDE-20-BUTTW))/5, ptop+BUTTH+2*8, BUTTW, BUTTH, "Gamma", infofg, infobg); XMapSubwindows(theDisp, ctrlW); } /***************************************************/ void CtrlBox(vis) int vis; { if (vis) XMapRaised(theDisp, ctrlW); else XUnmapWindow(theDisp, ctrlW); ctrlUp = vis; } /***************************************************/ void RedrawCtrl(x,y,w,h) int x,y,w,h; { char foo[40]; int i; XRectangle xr; xr.x = x; xr.y = y; xr.width = w; xr.height = h; XSetClipRectangles(theDisp, theGC, 0,0, &xr, 1, Unsorted); XSetForeground(theDisp, theGC, infofg); XSetBackground(theDisp, theGC, infobg); XDrawLine(theDisp, ctrlW, theGC, 0, ptop, CTRLWIDE, ptop); if (numnames>1) sprintf(foo,"%d files",numnames); else strcpy(foo,"1 file"); XSetForeground(theDisp, theGC, infobg); XFillRectangle(theDisp,ctrlW, theGC, 10+1,5+1,StringWidth(foo)+4,CHIGH+2); XSetForeground(theDisp,theGC,infofg); XDrawRectangle(theDisp,ctrlW, theGC, 10,5,StringWidth(foo)+5,CHIGH+3); XDrawString(theDisp, ctrlW, theGC, 10+3, 5+ASCENT+2, foo, strlen(foo)); for (i=0; i<NBUTTS; i++) BTRedraw(&but[i]); DrawCtrlStr(); XSetClipMask(theDisp, theGC, None); } /***************************************************/ void DrawCtrlStr() { int y; char *st; y = ptop - (CHIGH + 2); st = GetISTR(ISTR_INFO); XSetForeground(theDisp, theGC, infobg); XFillRectangle(theDisp, ctrlW, theGC, 0, y-1, CTRLWIDE, CHIGH+3); XSetForeground(theDisp, theGC, infofg); XDrawLine(theDisp, ctrlW, theGC, 0, y-2, CTRLWIDE, y-2); XDrawString(theDisp, ctrlW, theGC, 10, y+ASCENT, st, strlen(st)); } /***************************************************/ int ClickCtrl(x,y) int x,y; { BUTT *bp; int i; for (i=0; i<NBUTTS; i++) { bp = &but[i]; if (PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) break; } if (i<NBUTTS) { /* found one */ if (BTTrack(bp)) return (i); /* and it was clicked */ } return -1; } /***************************************************/ void ScrollToCurrent() { /* called when 'curname' is changed by anything (next/prev buttons, wait timeout, whatever. IF curname is already visible, just redraws list to reflect changed selection. If not, trys to adjust 'liststart' so that curname will appear in the center of the list window */ int halfway; nList.selected = curname; /* if (nList.selected >= nList.scrl.val && nList.selected < nList.scrl.val + nList.nlines) LSRedraw(&nList); */ if (nList.selected > nList.scrl.val && nList.selected < nList.scrl.val + nList.nlines-1) LSRedraw(&nList); else { halfway = (nList.nlines)/2; /* offset to the halfway pt. of the list */ SCSetVal(&nList.scrl, nList.selected - halfway); } } /***************************************************/ static void RedrawNList() { LSRedraw(&nList); } /***************** LIST STUFF *********************/ /***************************************************/ void LSCreate(lp, win, x, y, w, h, nlines, strlist, nstr, fg, bg, fptr, typ, donly) LIST *lp; Window win; int x,y,w,h,nlines,nstr,typ,donly; unsigned long fg, bg; char **strlist; /* a pointer to a list of strings */ void (*fptr)(); { lp->win = XCreateSimpleWindow(theDisp,win,x,y,w,h,1,infofg,infobg); if (!lp->win) FatalError("can't create list window!"); lp->x = x; lp->y = y; lp->w = w; lp->h = h; lp->fg = fg; lp->bg = bg; lp->str = strlist; lp->nstr = nstr; lp->selected = 0; lp->nlines = nlines; lp->filetypes= typ; lp->dirsonly = donly; XSelectInput(theDisp, lp->win, ExposureMask | ButtonPressMask); SCCreate(&lp->scrl, win, x+w, y, 1, h, 0, nstr-nlines, curname, nlines-1, fg, bg, fptr); } /***************************************************/ void LSNewData(lp, strlist, nstr) LIST *lp; char **strlist; int nstr; { lp->str = strlist; lp->nstr = nstr; lp->selected = 0; SCSetRange(&lp->scrl, 0, nstr - lp->nlines, 0, lp->nlines-1); } /***************************************************/ static void drawSel(lp,j) LIST *lp; int j; { int i, inactive; unsigned long fg, bg; inactive = INACTIVE(lp,j); i = j - lp->scrl.val; if (i<0 || i>=lp->nlines) return; /* off screen */ if (j == lp->selected && !inactive && j<lp->nstr) { fg = lp->bg; bg = lp->fg; } /* invert */ else { fg = lp->fg; bg = lp->bg; } XSetForeground(theDisp, theGC, bg); XFillRectangle(theDisp, lp->win, theGC, 0,i*LINEHIGH, lp->w, LINEHIGH); if (j>=0 && j<lp->nstr) { /* only draw string if valid */ /* make non-dirs inactive, if dirsonly and filetypes */ XSetForeground(theDisp, theGC, fg); XSetBackground(theDisp, theGC, bg); if (!lp->filetypes) XDrawString(theDisp, lp->win, theGC, 3, i*LINEHIGH + ASCENT + 1, lp->str[j], strlen(lp->str[j])); else { int ypos = i*LINEHIGH + (LINEHIGH - i_fifo_height)/2; if (lp->str[j][0] == C_FIFO) XCopyPlane(theDisp, fifoPix, lp->win, theGC, 0, 0, i_fifo_width, i_fifo_height, 3, ypos, 1L); else if (lp->str[j][0] == C_CHR) XCopyPlane(theDisp, chrPix, lp->win, theGC, 0, 0, i_chr_width, i_chr_height, 3, ypos, 1L); else if (lp->str[j][0] == C_DIR) XCopyPlane(theDisp, dirPix, lp->win, theGC, 0, 0, i_dir_width, i_dir_height, 3, ypos, 1L); else if (lp->str[j][0] == C_BLK) XCopyPlane(theDisp, blkPix, lp->win, theGC, 0, 0, i_blk_width, i_blk_height, 3, ypos, 1L); else if (lp->str[j][0] == C_LNK) XCopyPlane(theDisp, lnkPix, lp->win, theGC, 0, 0, i_lnk_width, i_lnk_height, 3, ypos, 1L); else if (lp->str[j][0] == C_SOCK) XCopyPlane(theDisp, sockPix, lp->win, theGC, 0, 0, i_sock_width, i_sock_height, 3, ypos, 1L); else /* lp->str[j][0] == C_REG */ XCopyPlane(theDisp, regPix, lp->win, theGC, 0, 0, i_reg_width, i_reg_height, 3, ypos, 1L); XDrawString(theDisp, lp->win, theGC, 3 + i_fifo_width + 3, i*LINEHIGH + ASCENT + 1, lp->str[j]+1, strlen(lp->str[j]+1)); #ifdef STIPPLE if (inactive) { /* stipple the icon by drawing 'bg' where there's 1's in the stipple */ XSetFillStyle(theDisp, theGC, FillStippled); XSetStipple(theDisp, theGC, grayStip); XSetForeground(theDisp, theGC, bg); XSetBackground(theDisp, theGC, fg); XFillRectangle(theDisp,lp->win,theGC,3,i*LINEHIGH,lp->w,LINEHIGH); XSetForeground(theDisp, theGC, fg); XSetFillStyle(theDisp, theGC, FillSolid); } #endif STIPPLE } } } /***************************************************/ void LSRedraw(lp) LIST *lp; { int i; for (i = lp->scrl.val; i < lp->scrl.val + lp->nlines; i++) drawSel(lp,i); } /***************************************************/ int LSClick(lp,ev) LIST *lp; XButtonEvent *ev; { /* returns '-1' normally. returns 0 -> numnames-1 for a goto */ Window rW, cW; int rx, ry, x, y, sel, oldsel; unsigned int mask; static Time lasttime=0; static int lastsel = -1; x = ev->x; y = ev->y; sel = lp->scrl.val + y/LINEHIGH; if (sel >= lp->nstr) sel = lp->selected; /* see if it's a double click */ if (ev->time - lasttime < DBLCLKTIME && sel==lastsel && (lp->scrl.val + y/LINEHIGH) < lp->nstr && !INACTIVE(lp,sel)) { return (sel); } lasttime = ev->time; lastsel = sel; /* if not clicked on selected, turn off selected and select new one */ if (sel != lp->selected) { oldsel = lp->selected; lp->selected = sel; drawSel(lp,sel); drawSel(lp,oldsel); XFlush(theDisp); } while (XQueryPointer(theDisp,lp->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) { if (!(mask & Button1Mask)) break; /* button released */ if (y<0) { /* scroll up in list */ if (lp->scrl.val > lp->scrl.min) { lp->selected = lp->scrl.val - 1; SCSetVal(&lp->scrl, lp->scrl.val - 1); Timer(100); } } else if (y > lp->h) { /* scroll down in list */ if (lp->scrl.val < lp->scrl.max) { lp->selected = lp->scrl.val + lp->nlines; if (lp->selected >= lp->nstr) lp->selected = lp->nstr - 1; SCSetVal(&lp->scrl, lp->scrl.val + 1); Timer(100); } } else { sel = lp->scrl.val + y/LINEHIGH; if (sel >= lp->nstr) sel = lp->nstr - 1; if (sel != lp->selected && sel >= lp->scrl.val && sel < lp->scrl.val + lp->nlines) { /* dragged to another on current page */ oldsel = lp->selected; lp->selected = sel; drawSel(lp, sel); drawSel(lp, oldsel); XFlush(theDisp); } } } return(-1); } \BARFOO\ else echo "will not over write ./xvctrl.c" fi echo "Finished archive 4 of 8" exit dan ---------------------------------------------------- O'Reilly && Associates argv@sun.com / argv@ora.com Opinions expressed reflect those of the author only.