[comp.graphics] 24bit to 8bit image for sun and iris

jevans@.ucalgary.ca (David Jevans) (09/28/88)

I have received so many requests for the source for my program which
will display a 24bit color image on an 8bit color sun that I have
decided to post it to the net.  A few things have changed since last
week however:

1) I am posting the source for a version that was hacked over by
   another researcher here.  This code has been optimised a little
   but there have been two major changes:
	a) it supports a color sun or an 8bitplane iris
	b) it supports my simple format and our run-length-encoded format.

2) It seems there may be a bug somewhere in the octree reduction code.

3) The code will attempt to read a file with the 8bit colortable entries
   etc.  If it cannot find this file it will read the 24bit image and
   create this file and then display it.  This means that once you have
   displayed a picture once, the 256 elem colortable does not need to
   be re-calculated.

Due to these mods the code has grown (almost doubled) in length.  There
are #ifdefs throughout, so if you only want a sun version or only an
iris version you can spend a few minutes in emacs...

Also, someone told me that the program bombed when it got halfway through
displaying his file.  It seems it thought it couldn't reduce the octree
any further.  This of course means that there could be a bug in the
octree reduction code but I am sorry, I have no time to look into it.
Use the code at your own risk.  If anyone finds and fixes it why not
post it to the net?  The paper which describes the algorithm is in
the proceedings of Computer Graphics International 1988, published by
Springer-Verlag.

BTW it should only take a few minutes for anyone to change the program
to use their image format... the files are read in in main() at the
bottom of the program.

----- makefile ----- cut here ----- makefile ----- cut here ----- makefile --
DEBUG = -g

all: irisb_8_ras

irisb: irisb_8_cd irisb_8_ras

sun3: sun3_8_cd sun3_8_ras

sun4: sun4_8_cd sun4_8_ras

irisb_8_cd: 8_bit.c
	cc $(DEBUG) -DCDISP -DIRIS -o irisb_8_cd 8_bit.c -lgl

irisb_8_ras: 8_bit.c
	cc $(DEBUG) -DRAS -DIRIS -o irisb_8_ras 8_bit.c -lgl

sun3_8_cd: 8_bit.c
	cc $(DEBUG) -DCDISP -DSUN3 -o sun3_8_cd 8_bit.c -lpixrect

sun3_8_ras: 8_bit.c
	cc $(DEBUG) -DRAS -DSUN3 -o sun3_8_ras 8_bit.c -lpixrect

sun4_8_cd: 8_bit.c
	cc $(DEBUG) -DCDISP -DSUN4 -o sun4_8_cd 8_bit.c

sun4_8_ras: 8_bit.c
	cc $(DEBUG) -DRAS -DSUN4 -o sun4_8_ras 8_bit.c

----- 8_bit.c ----- cut here ----- 8_bit.c ----- cut here ----- 8_bit.c --
/*************************************************************************/
/* Used to cdisp & .ras format files to an 8 bitplane device such as a
/* colour sun or Iris B (also make intermediate files on sun4 for wails)
/* Original algorithm implemented by Dave Jevans
/* Hacked for 8bit iris etc. by Chris Bone
/*************************************************************************/

/* clever includes */
#include <stdio.h>
#ifdef IRIS
#include <gl.h>
#include <device.h>
#endif
#ifdef SUN3
#include <pixrect/pixrect_hs.h>
#endif

/* wiggy types */
#ifdef SUN4
typedef unsigned char RGBvalue;
typedef unsigned short Colorindex;
#endif
#ifdef SUN3
typedef unsigned char RGBvalue;
typedef unsigned short Colorindex;
#endif

/* fancy extensions */
#ifdef CDISP
#define EXTENSION ".8_bit_cdisp"
#endif
#ifdef RAS
#define EXTENSION ".8_bit_ras"
#endif

#define MAXCOLORS 256

/* pixrect for screen */
#ifdef SUN3
struct pixrect *screen;
#endif

struct node {
    RGBvalue leaf;
    RGBvalue rgb[3];
    int numtimes;
    int index;
    struct node *octrees[8];
    struct node *next;
};

static struct node *reducible[8], root;
static int numcolors = 0, numcolormap = 0;
RGBvalue lutr[256],lutg[256],lutb[256];
int do_gfx_stuff;

/* former functions... now macros for wails */

#define whichoctree(rgb, depth) (((rgb[0] & (1 << 7-depth)) >> 7-depth) << 1 | ((rgb[1] & (1 << 7-depth)) >> 7-depth)) << 1 | ((rgb[2] & (1 << 7-depth)) >> 7-depth)
#define makereducible(o,d) {o->next=reducible[d]; reducible[d]=o;}
#define findcolor(rgb) find(rgb,&root,0)

/*
    reduce the deepest octree nodes
*/

reduce()
{
    register depth;
    struct node *octree;
    register a, numoctrees, numtimes;
    int rgb[3],nt;

    for(depth = 7; depth >= 0; depth--)
	if(reducible[depth])
	    break;
    if(depth < 0){
	fprintf(stderr, "reduce: nothing to reduce, %d colors\n", numcolors);
	exit(-1);
    }
    octree = reducible[depth];
    reducible[depth] = octree->next;
    octree->next = NULL;	/* just to be safe */
    rgb[0] = rgb[1] = rgb[2] = 0;
    for(a = numoctrees = numtimes = 0; a < 8; a++)
	if(octree->octrees[a]){
	    numoctrees++;
	    numtimes += octree->octrees[a]->numtimes;
	    nt=octree->octrees[a]->numtimes;
	    rgb[0] += octree->octrees[a]->rgb[0] * nt;
	    rgb[1] += octree->octrees[a]->rgb[1] * nt;
	    rgb[2] += octree->octrees[a]->rgb[2] * nt;
	    numcolors--;
	    free(octree->octrees[a]);
	    octree->octrees[a] = NULL;	/* just to be safe */
	}
    if(numoctrees == 0){
	fprintf(stderr, "reduce: only %d to reduce! need >= 2.\n", numoctrees);
	exit(-1);
    }
    if(numtimes == 0){
	fprintf(stderr, "reduce: colors occured 0 times!?!\n");
	exit(-1);
    }
    octree->rgb[0] = rgb[0] / numtimes;
    octree->rgb[1] = rgb[1] / numtimes;
    octree->rgb[2] = rgb[2] / numtimes;
    octree->numtimes = numtimes;
    octree->leaf = 1;
    numcolors++;
}

/*
    insert a new color into the octree.  this may involve deepening the
    octree.
*/
struct node *insert(rgb, octree, depth,count)
RGBvalue rgb[3];
struct node *octree;
int depth;
int count;
{
    struct node *o;
    register a,wo;

    if(!octree){
	if(!(o = (struct node *)malloc(sizeof(struct node)))){
	    fprintf(stderr, "insert: out of mems\n");
	    exit(-1);
	}
	o->leaf = 1;
	o->rgb[0] = rgb[0];
	o->rgb[1] = rgb[1];
	o->rgb[2] = rgb[2];
	o->numtimes = count;
	numcolors++;
	return(o);
    }else if(octree->leaf){
	if(rgb[0] == octree->rgb[0] && rgb[1] == octree->rgb[1] && rgb[2] == octree->rgb[2]){
	    octree->numtimes+=count;
	    return(octree);
	}
	if(!(o = (struct node *)malloc(sizeof(struct node)))){
	    fprintf(stderr, "insert: out of mems\n");
	    exit(-1);
	}
	o->leaf = 0;
	for(a = 0; a < 8; a++)
	    o->octrees[a] = NULL;
	wo=whichoctree(octree->rgb,depth);
	o->octrees[wo]=insert(octree->rgb,o->octrees[wo],depth+1,octree->numtimes);
	numcolors--;
	free(octree);
	wo=whichoctree(rgb,depth);
	o->octrees[wo]=insert(rgb,o->octrees[wo],depth+1,count);
	makereducible(o,depth);
	return(o);
    } else{
	wo=whichoctree(rgb,depth);
	octree->octrees[wo]=insert(rgb,octree->octrees[wo],depth+1,count);
	return(octree);
    }
}

/*
    search the octree for the nearest color to rgb[]
*/
int find(rgb, octree, depth)
RGBvalue rgb[3];
struct node *octree;
int depth;
{
    if(octree->leaf)
	return(octree->index);
    else {
        register wo;

	wo=whichoctree(rgb,depth);
	if(octree->octrees[wo] == NULL){
		if(octree->octrees[0]) return(find(rgb, octree->octrees[0], depth+1));
		if(octree->octrees[1]) return(find(rgb, octree->octrees[1], depth+1));
		if(octree->octrees[2]) return(find(rgb, octree->octrees[2], depth+1));
		if(octree->octrees[3]) return(find(rgb, octree->octrees[3], depth+1));
		if(octree->octrees[4]) return(find(rgb, octree->octrees[4], depth+1));
		if(octree->octrees[5]) return(find(rgb, octree->octrees[5], depth+1));
		if(octree->octrees[6]) return(find(rgb, octree->octrees[6], depth+1));
		if(octree->octrees[7]) return(find(rgb, octree->octrees[7], depth+1));
	    fprintf(stderr, "ERROR: find cannot find anything near %d %d %d\n",rgb[0], rgb[1], rgb[2]);
	    exit(-1);
	}
	return(find(rgb, octree->octrees[wo], depth+1));
    }
}

setcolortable(octree)
struct node *octree;
{
    register a;

    if(octree->leaf){
#ifdef IRIS
	mapcolor(numcolormap,(short)octree->rgb[0],(short)octree->rgb[1],(short)octree->rgb[2]);
#endif
#ifdef SUN3
	pr_putcolormap(screen,numcolormap,1, &octree->rgb[0], &octree->rgb[1], &octree->rgb[2]);
#endif
	lutr[numcolormap]=(RGBvalue)octree->rgb[0];
	lutg[numcolormap]=(RGBvalue)octree->rgb[1];
	lutb[numcolormap]=(RGBvalue)octree->rgb[2];
	octree->index = numcolormap;
	numcolormap++;
    } else
	for(a = 0; a < 8; a++)
	    if(octree->octrees[a])
		setcolortable(octree->octrees[a]);
}

clearscreen()
{
#ifdef SUN3
    pr_rop(screen,0,0,screen->pr_size.x,screen->pr_size.y,PIX_SRC,NULL,0,0);
#endif
#ifdef IRIS
    ginit();
    clear();
    onemap();
    gconfig();
    qdevice(QKEY);
    qreset();
#endif
}

main(argc, argv)
int argc;
char **argv;
{
    FILE *fp,*sbits;
    register x, y;
    register a, b, c;
    int xsize, ysize,crap,nread,count;
    RGBvalue col[3];
    RGBvalue colour,tmp_name[80];
    Colorindex *row;
    RGBvalue *rrow;

#ifdef SUN4
    do_gfx_stuff=0;
#else
    do_gfx_stuff=1;
#endif
    if(argc != 2 && argc !=3){
	fprintf(stderr, "usage: %s <filename> [-d]\n", argv[0]);
	exit(-1);
    }
/* this is gross but I am to lazy to do it better */
    if(argc==3 && argv[2][0]=='-' && argv[2][1]=='d')
	do_gfx_stuff=0;
    strcpy(tmp_name,argv[1]);
    if(fp=fopen(strcat(tmp_name,EXTENSION),"r")) {
#ifdef SUN4
	fprintf(stderr,"Can't do gfx on sun4\n");
	fclose(fp);
	exit(-1);
#endif
	fread(&xsize,sizeof(int),1,fp);
	fread(&ysize,sizeof(int),1,fp);
	row=(Colorindex *)malloc(xsize*sizeof(Colorindex));
	rrow=(RGBvalue *)malloc(xsize*sizeof(RGBvalue));
	fread(lutr,sizeof(RGBvalue),256,fp);
	fread(lutg,sizeof(RGBvalue),256,fp);
	fread(lutb,sizeof(RGBvalue),256,fp);
#ifdef SUN3
	screen=pr_open("/dev/fb");
#endif
	clearscreen();
#ifdef SUN3
	    pr_putcolormap(screen,0,256,lutr,lutg,lutb);
#endif
#ifdef IRIS
	for(x=0;x<256;x++)
	    mapcolor(x,lutr[x],lutg[x],lutb[x]);
#endif
#ifdef RAS
	x=y=0;
	crap=1;
	nread=0;
	do {
	    nread=fread(tmp_name,sizeof(RGBvalue),80,fp);
	    for(crap=0;crap<nread;crap+=2) {
		colour=tmp_name[crap];
		count=1+(int)tmp_name[crap+1];
		a=x+count;
		if(a>xsize)
		    a=xsize;
		for(b=x;b<=a;b++) {
		    row[b]=colour;
		}
		x+=count;
		while(x>xsize) {
#ifdef SUN3
		    for(a=0;a<xsize;a++)
			pr_put(screen,a,y,row[a]);
#endif
#ifdef IRIS
		    cmov2i(0,767-y);
		    writepixels(xsize,row);
#endif
		    x-=xsize;
		    a=x;
		    if(a>xsize)
			a=xsize;
		    for(b=0;b<=a;b++)
			row[b]=colour;
		    y++;
		}
	    }
	} while(nread==80);
#endif
#ifdef CDISP
	for(y = 0; y < ysize; y++) {
	    fread(rrow,sizeof(RGBvalue),xsize,fp);
	    for(x=0;x<xsize;x++) {
		row[x]=(Colorindex)rrow[x];
#ifdef SUN3
		    pr_put(screen,x,y,row[x]);
#endif
	    }
	    if(do_gfx_stuff) {
#ifdef IRIS
		cmov2i(0,767-y);
		writepixels((short)xsize,row);
#endif
	    }
	}
#endif
	fclose(fp);
	goto skip_stuff;
    }
    if(!(fp = fopen(argv[1], "r"))){
	fprintf(stderr, "could not open file %s\n", argv[1]);
	exit(-1);
    }
#ifdef CDISP
    if(fread(&xsize, sizeof(int), 1, fp) != 1)
	exit(-1);
    if(fread(&ysize, sizeof(int), 1, fp) != 1)
	exit(-1);
    for(y = 0; y < ysize; y++) {
	for(x = 0; x < xsize; x++) {
	    if(fread(col, sizeof(RGBvalue), 3, fp) != 3){
		ysize = y;
		break;
	    }
	    while(numcolors > MAXCOLORS-1)
		reduce();
	    insert(col, &root, 0,1);
	}
	printf("%d\n",ysize-y);
    }
#endif
#ifdef RAS
    if(fread(tmp_name,sizeof(RGBvalue),80,fp)!=80)
	exit(-1);
    if(strncmp(tmp_name,"gl  RASF",8) &&
	strncmp(tmp_name,"gl  RASf",8) &&
	strncmp(tmp_name,"gl  SHRT",8)) {
	fprintf(stderr,"<%s> Not in ras file format\n",argv[1]);
	exit(-1);
    }
    xsize= *(int *)(tmp_name+8);
    ysize= *(int *)(tmp_name+8+sizeof(int));
    if(*(int *)(tmp_name+8+sizeof(int)+sizeof(int))!=24) {
	fprintf(stderr,"This is for 24 bit plane source images\n");
	exit(-1);
    }
    x=y=0;
    crap=1;
    nread=0;
    do {
	nread=fread(tmp_name,sizeof(RGBvalue),80,fp);
	for(crap=0;crap<nread;crap+=4) {
	    col[0]=tmp_name[crap];
	    col[1]=tmp_name[crap+1];
	    col[2]=tmp_name[crap+2];
	    count=1+(int)tmp_name[crap+3];
	    while(numcolors>MAXCOLORS-1)
		reduce();
	    insert(col,&root,0,count);
	    x+=count;
	    while(x>xsize) {
		x-=xsize;
		y++;
		printf("%d\n",ysize-y);
	    }
	}
    } while(nread==80);
#endif
    fclose(fp);
    if(do_gfx_stuff) {
#ifdef SUN3
	    screen=pr_open("/dev/fb");
#endif
	    clearscreen();
    }
    setcolortable(&root);

    if(!(fp = fopen(argv[1], "r"))){
	fprintf(stderr, "could not read file %s\n", argv[1]);
	exit(-1);
    }
    row=(Colorindex *)malloc(xsize*sizeof(Colorindex));
    rrow=(RGBvalue *)malloc(xsize*sizeof(RGBvalue));
    if(sbits=fopen(strcat(argv[1],EXTENSION),"w")) {
	fwrite(&xsize,sizeof(int),1,sbits);
	fwrite(&ysize,sizeof(int),1,sbits);
	fwrite(lutr,sizeof(RGBvalue),256,sbits);
	fwrite(lutg,sizeof(RGBvalue),256,sbits);
	fwrite(lutb,sizeof(RGBvalue),256,sbits);
    }
#ifdef RAS
    fread(tmp_name,sizeof(RGBvalue),80,fp);
    xsize= *(int *)(tmp_name+8);
    ysize= *(int *)(tmp_name+8+sizeof(int));
    x=y=0;
    crap=1;
    nread=0;
    do {
	nread=fread(tmp_name,sizeof(RGBvalue),80,fp);
	for(crap=0;crap<nread;crap+=4) {
	    col[0]=tmp_name[crap];
	    col[1]=tmp_name[crap+1];
	    col[2]=tmp_name[crap+2];
	    count=1+(int)tmp_name[crap+3];
	    colour=(RGBvalue)findcolor(col);
	    if(sbits) {
		tmp_name[crap+2]=(RGBvalue)colour;
		fwrite(&tmp_name[crap+2],sizeof(RGBvalue),2,sbits);
	    }
	    a=x+count;
	    if(a>xsize)
		a=xsize;
	    for(b=x;b<=a;b++) {
		row[b]=colour;
	    }
	    x+=count;
	    while(x>xsize) {
#ifdef SUN3
		for(a=0;a<xsize;a++)
			pr_put(screen,a,y,row[a]);
#endif
#ifdef IRIS
		cmov2i(0,767-y);
		writepixels(xsize,row);
#endif
		x-=xsize;
		a=x;
		if(a>xsize)
			a=xsize;
		for(b=0;b<=a;b++)
			row[b]=colour;
		y++;
	    }
	}
    } while(nread==80);
#endif
#ifdef CDISP
    if(fread(&crap, sizeof(int), 1, fp) != 1)
	exit(-1);
    if(fread(&crap, sizeof(int), 1, fp) != 1)
	exit(-1);
    for(y = 0; y < ysize; y++) {
	for(x = 0; x < xsize; x++){
	    if(fread(col, sizeof(RGBvalue), 3, fp) != 3)
		exit(-1);
	    colour=(RGBvalue)findcolor(col);
	    row[x]=colour;
#ifdef SUN3
		pr_put(screen,x,y,colour);
#endif
	}
	if (do_gfx_stuff) {
#ifdef IRIS
		cmov2i(0,767-y);
		writepixels(xsize,row);
#endif
	}
	if(sbits) {
	    for(x=0;x<xsize;x++)
		rrow[x]=(RGBvalue)row[x];
	    fwrite(rrow,sizeof(RGBvalue),xsize,sbits);
	}
    }
#endif
    if(sbits)
	fclose(sbits);
    fclose(fp);
skip_stuff:
    if(do_gfx_stuff) {
#ifdef IRIS
	    while(!qtest());
	    unqdevice(QKEY);
	    greset();
	    gexit();
#endif
    }
    exit(0);
}

----- end ----- cut here ----- end ----- cut here ----- end ----- cut here --

What did you pay?  Nuthin!  What did you get?  Sumthin!  Don't grumble.

David Jevans
	"New sensations barely interest me"