home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************
- * fboctree.c:
- ****************************************************************/
-
- /******
- Copyright (C) 1993 by Klaus Ehrenfried.
-
- Permission to use, copy, modify, and distribute this software
- is hereby granted, provided that the above copyright notice appears
- in all copies and that the software is available to all free of charge.
- The author disclaims all warranties with regard to this software,
- including all implied warranties of merchant-ability and fitness.
- The code is simply distributed as it is.
- *******/
-
- /*******
- This program is an implementation of the Octree algorithm described
- by Michael Gervautz and Werner Purgathofer (Technical University
- Vienna, Austria) in the article "A Simple Method for Color Quantization:
- Octree Quantization" published in Graphic Gems edited by Andrew Glassner
- pp. 287 ff.
- *******/
-
- #include <stdio.h>
- #include <math.h>
- #include <string.h>
-
- #include "fbm.h"
- #include <stdlib.h>
-
- #define PRNAM "fboctree"
- #define BIGVAL 0x10000000
-
- typedef unsigned char UBYTE;
- typedef unsigned long ULONG;
-
- typedef struct node *OCTREE;
-
- struct node
- {
- UBYTE leaf;
- UBYTE level;
- UBYTE color;
- UBYTE sub_count;
- ULONG pixels_low;
- ULONG pixels_high;
- ULONG red_sum_low;
- ULONG red_sum_high;
- ULONG green_sum_low;
- ULONG green_sum_high;
- ULONG blue_sum_low;
- ULONG blue_sum_high;
- OCTREE next_reduceable;
- OCTREE sub[8];
- OCTREE nlist;
- int index;
- };
-
- void search_colors(OCTREE tree);
- void clr_quantize(FBM *input, FBM *output, int colors);
- void add_to_octree(FBM *image);
-
- int write_bitmap (FBM *image,FILE *wfile, int type);
- int read_bitmap (FBM *image, char *fname);
- int alloc_fbm (FBM *image);
- void search_tree(OCTREE tree);
- int out_tree(char *treefile);
- int input_tree(char *treefile, int add_flag);
- int check_kennung(FILE *fp);
- void print_usage();
-
- #define MAXDEPTH 7
-
- #define RED 0
- #define GREEN 1
- #define BLUE 2
-
- #define MAXCOLORS 256
-
- #define TRUE 1
- #define FALSE 0
-
- static UBYTE palette[MAXCOLORS][3];
-
- static int octree_size;
- static int reduce_level;
- static int leaf_level;
- static int max_colors;
-
- static OCTREE basetree;
- static OCTREE reduce_list[MAXDEPTH + 1];
- static int tree_count;
- static OCTREE tlist;
- static UBYTE b_field[]={128,64,32,16,8,4,2,1};
-
- static double color_factor;
- static int color_count;
-
- #define MAX_KENN 20
- static char Kennung[]="#OCTREE_v0.2a";
-
- /****************************************************************
- * main
- ****************************************************************/
-
- void main (int argc, char *argv[])
- { FBM input, output; /* Images */
- int color_depth;
- int outtype;
- int add_flag, single_flag;
- int i;
- char *tfname, *ppa;
-
- /* Clear pointers */
- input.bm = input.cm = (UBYTE *) NULL;
- output.bm = output.cm = (UBYTE *) NULL;
-
- color_depth=8;
- max_colors=MAXCOLORS;
- outtype = DEF_8BIT;
- add_flag=0;
- single_flag=0;
- tfname=NULL;
-
- for (i=1; i < argc; i++)
- {
- ppa=argv[i];
- if (ppa[0] == '-')
- {
- switch (ppa[1])
- {
- case 'A': outtype = FMT_ATK; break;
- case 'B': outtype = FMT_FACE; break;
- case 'F': outtype = FMT_FBM; break;
- case 'G': outtype = FMT_GIF; break;
- case 'I': outtype = FMT_IFF; break;
- case 'L': outtype = FMT_LEAF; break;
- case 'M': outtype = FMT_MCP; break;
- case 'P': outtype = FMT_PBM; break;
- case 'R': outtype = FMT_RLE; break;
- case 'S': outtype = FMT_SUN; break;
- case 'T': outtype = FMT_TIFF; break;
- case 'X': outtype = FMT_X11; break;
- case 'Z': outtype = FMT_PCX; break;
- case 'a': add_flag = 1; break;
- case 'c': max_colors = atoi(&ppa[2]); break;
- case 'd': color_depth = atoi(&ppa[2]); break;
- case 's': single_flag = 1; break;
- default:
- fprintf(stderr,"Unknown option: %s\n",ppa);
- exit(1);
- }
- }
- }
-
- for (i=1; i < argc; i++)
- {
- ppa=argv[i];
- if (ppa[0] != '-')
- {
- if (tfname == NULL)
- {
- tfname=argv[i];
- }
- else
- {
- print_usage();
- exit(1);
- }
- }
- }
-
- /* Check arguments */
-
- if ((tfname == NULL && single_flag == 0) ||
- (add_flag == 1 && single_flag == 1))
- {
- print_usage();
- exit (1);
- }
-
- if (color_depth > 8 || color_depth < 2)
- {
- fprintf (stderr, "%s can only handle 2..8 color depth\n",PRNAM);
- exit (1);
- }
-
- if (max_colors > 256 || max_colors < 9)
- {
- fprintf (stderr, "%s can only handle 9..256 colors\n",PRNAM);
- exit (1);
- }
-
- for (i=0; i <= MAXDEPTH; i++)
- reduce_list[i]=NULL;
-
- basetree=NULL;
- reduce_level = MAXDEPTH;
- leaf_level = reduce_level + 1;
- octree_size=0;
-
- if (tfname != NULL)
- if (input_tree(tfname, add_flag) != 0) exit(1);
-
- if (! read_bitmap (&input, (char *) NULL))
- { exit (1); }
-
- if (input.hdr.planes != 3 || input.hdr.bits != 8)
- { fprintf (stderr, "fboctree can only handle 24bit RGB inputs\n");
- exit (1);
- }
-
- if (add_flag == 1 || single_flag == 1)
- {
- fprintf (stderr,"Scanning \"%s\" [%dx%d]\n",
- input.hdr.title, input.hdr.cols, input.hdr.rows);
- add_to_octree(&input);
- }
-
- if (add_flag == 1)
- {
- tree_count = 0;
- tlist=NULL;
- search_tree(basetree);
- fprintf(stderr,"Octree nodes : %d\n",tree_count);
-
- out_tree(tfname);
- exit(0);
- }
-
- fprintf (stderr,
- "Quantizing \"%s\" [%dx%d] with %d colors (%d bits/clr)\n",
- input.hdr.title, input.hdr.cols, input.hdr.rows, max_colors,
- color_depth);
-
- for (i = 0; i < MAXCOLORS; i++) /* init palette */
- palette[i][0] = palette[i][1] = palette[i][2] = 0;
-
- /* color 0 is always black !! */
-
- color_count = 1;
- color_factor = (double) ((1 << color_depth) - 1) / 0xFF;
- search_colors(basetree);
-
- /* Now build header for output bit map */
- output.hdr = input.hdr;
- output.hdr.planes = 1;
- output.hdr.clrlen = 3 * MAXCOLORS;
-
- /* Allocate space for output */
- alloc_fbm (&output);
-
- clr_quantize (&input, &output, MAXCOLORS);
-
- /* Write out the result */
- if (write_bitmap (&output, stdout, outtype))
- { exit (0); }
-
- exit (1);
- }
-
- /************************************************************************
- * print_usage *
- ************************************************************************/
-
- void print_usage()
- {
- fprintf(stderr,"Usage:\n");
- fprintf(stderr,
- " %s -s [-c<numcolors>] [-d<bits>] [-<type>] < rgb > mapped\n",
- PRNAM);
- fprintf(stderr," %s -a [-c<numcolors>] octree < rgb\n",PRNAM);
- fprintf(stderr," %s [-d<bits>] [-<type>] octree < rgb > mapped\n",PRNAM);
- }
-
- /************************************************************************
- * search_colors *
- ************************************************************************/
-
- void search_colors(OCTREE tree)
- {
- int j;
- double dhelp0, dhelp1;
-
- if (tree == NULL) return;
- if (tree->leaf || tree->level == leaf_level)
- {
- dhelp0=(double) tree->pixels_high * (double) BIGVAL
- +(double) tree->pixels_low;
- dhelp0=color_factor / dhelp0;
-
- dhelp1=
- (double) tree->red_sum_high * (double) BIGVAL
- +(double) tree->red_sum_low;
- palette[color_count][RED] = (char) (dhelp0 * dhelp1 + 0.5);
-
- dhelp1=
- (double) tree->green_sum_high * (double) BIGVAL
- +(double) tree->green_sum_low;
- palette[color_count][GREEN] = (char) (dhelp0 * dhelp1 + 0.5);
-
- dhelp1=
- (double) tree->blue_sum_high * (double) BIGVAL
- +(double) tree->blue_sum_low;
- palette[color_count][BLUE] = (char) (dhelp0 * dhelp1 + 0.5);
-
- tree->color = color_count;
- tree->leaf = TRUE;
- color_count++;
- }
- else
- {
- for (j = 0; j < 8; j++)
- search_colors(tree->sub[j]);
- }
- }
-
- /************************************************************************
- * add_to_octree *
- ************************************************************************/
-
- void add_to_octree(FBM *image)
- {
- UBYTE b_red, b_green, b_blue, branch, btest;
- UBYTE *p_red, *p_green, *p_blue;
- OCTREE tree, *p_tree;
- int i, ihelp, depth;
-
- p_red = image->bm;
- p_green = &p_red[image->hdr.plnlen];
- p_blue = &p_green[image->hdr.plnlen];
-
- for (i=0; i < image->hdr.plnlen; i++)
- {
- b_red = *(p_red++);
- b_green = *(p_green++);
- b_blue = *(p_blue++);
-
- p_tree = &basetree;
- depth = 0;
-
- while (p_tree != NULL)
- {
- if (*p_tree == NULL) /* init new node */
- {
- tree = (OCTREE) calloc(1, sizeof(struct node));
- if (tree == NULL)
- {
- printf("Out of memory");
- exit(1);
- }
- tree->level = depth;
- tree->leaf = (depth >= leaf_level);
- if (tree->leaf) octree_size++;
- *p_tree = tree;
- }
- else
- tree = *p_tree;
-
- tree->pixels_low++;
- tree->red_sum_low += b_red;
- tree->green_sum_low += b_green;
- tree->blue_sum_low += b_blue;
-
- if (tree->leaf == FALSE && depth < leaf_level)
- {
- btest=b_field[depth];
- branch=((b_red & btest) == 0) ? (UBYTE) 0 : (UBYTE) 4;
- if ((b_green & btest) != 0) branch += (UBYTE) 2;
- if ((b_blue & btest) != 0) branch += (UBYTE) 1;
-
- if (tree->sub[branch] == NULL)
- {
- tree->sub_count++;
- if (tree->sub_count == 2) /* add to reduce_list */
- {
- tree->next_reduceable = reduce_list[depth];
- reduce_list[depth] = tree;
- }
- }
- p_tree=&(tree->sub[branch]);
- depth++;
- }
- else
- p_tree = NULL;
- }
-
- if (octree_size >= max_colors) /* reduce octree */
- {
- ihelp = reduce_level;
- while (reduce_list[ihelp] == NULL) ihelp--;
- tree = reduce_list[ihelp];
- reduce_list[ihelp] = reduce_list[ihelp]->next_reduceable;
- tree->leaf = 1;
- octree_size = octree_size - tree->sub_count + 1;
- depth = tree->level;
- if (depth < reduce_level)
- {
- reduce_level = depth;
- leaf_level = reduce_level + 1;
- }
- }
- }
- }
-
- /************************************************************************
- * clr_quantize *
- ************************************************************************/
-
- void clr_quantize(FBM *input, FBM *output, int colors)
- {
- UBYTE b_red, b_green, b_blue, branch, btest;
- UBYTE *p_red, *p_green, *p_blue, *p_output;
- OCTREE tree;
- int i;
-
- p_red = input->bm;
- p_green = &p_red[input->hdr.plnlen];
- p_blue = &p_green[input->hdr.plnlen];
- p_output = output->bm;
-
- for (i=0; i < input->hdr.plnlen; i++)
- {
- b_red = *(p_red++);
- b_green = *(p_green++);
- b_blue = *(p_blue++);
-
- tree=basetree;
-
- while (tree->leaf == 0)
- {
- btest=b_field[tree->level];
- branch=((b_red & btest) == 0) ? (UBYTE) 0 : (UBYTE) 4;
- if ((b_green & btest) != 0) branch += (UBYTE) 2;
- if ((b_blue & btest) != 0) branch += (UBYTE) 1;
- tree=tree->sub[branch];
- }
-
- *(p_output++) = tree->color;
- }
-
- p_red = output->cm;
- p_green = &p_red[colors];
- p_blue = &p_green[colors];
-
- for (i=0; i < colors; i++)
- {
- *(p_red++) = palette[i][RED];
- *(p_green++) = palette[i][GREEN];
- *(p_blue++) = palette[i][BLUE];
- }
- }
-
- /************************************************************************
- * search tree *
- ************************************************************************/
-
- void search_tree(OCTREE tree)
- {
- OCTREE subtree;
- int i;
-
- tree->index=tree_count++;
- tree->nlist=tlist;
- tlist=tree;
-
- while (tree->pixels_low >= BIGVAL)
- {
- tree->pixels_low -= BIGVAL;
- tree->pixels_high++;
- }
-
- while (tree->red_sum_low >= BIGVAL)
- {
- tree->red_sum_low -= BIGVAL;
- tree->red_sum_high++;
- }
-
- while (tree->green_sum_low >= BIGVAL)
- {
- tree->green_sum_low -= BIGVAL;
- tree->green_sum_high++;
- }
-
- while (tree->blue_sum_low >= BIGVAL)
- {
- tree->blue_sum_low -= BIGVAL;
- tree->blue_sum_high++;
- }
-
- if (tree->leaf == FALSE && tree->level < leaf_level)
- {
- for (i=0; i < 8; i++)
- {
- subtree=tree->sub[i];
- if (subtree != NULL)
- search_tree(subtree);
- }
- }
- }
-
-
- /************************************************************************
- * out_tree *
- ************************************************************************/
-
- int out_tree(char *tfname)
- {
- FILE *fopen(), *output;
- OCTREE twork, thelp;
- int iwas,isub,i;
-
- fprintf(stderr,"Write octree file `%s'\n",tfname);
-
- if ((output = fopen(tfname, "w")) == NULL)
- {
- fprintf(stderr,"Error opening octree-file `%s'\n",tfname);
- return(1);
- }
-
- fprintf(output,"%s\n",Kennung);
- fprintf(output,"%d %d\n",tree_count,max_colors);
- fprintf(output,"%d %d %d\n",octree_size,reduce_level,leaf_level);
-
- for (i=0; i <= MAXDEPTH; i++)
- {
- thelp=reduce_list[i];
- iwas=(thelp == NULL) ? 0 : (thelp->index+1);
- fprintf(output," %X",iwas);
- }
-
- fprintf(output,"\n");
-
- twork=tlist;
- while (twork != NULL)
- {
- if (twork->leaf == FALSE)
- isub=twork->sub_count;
- else
- isub=0;
-
- fprintf(output,"%d %d %d %X %X %X %X %X %X %X %X",
- (int) twork->leaf,
- (int) twork->level,
- isub,
- twork->pixels_low,
- twork->pixels_high,
- twork->red_sum_low,
- twork->red_sum_high,
- twork->green_sum_low,
- twork->green_sum_high,
- twork->blue_sum_low,
- twork->blue_sum_high);
-
- if (isub != 0)
- {
- for (i=0; i< 8; i++)
- {
- thelp=twork->sub[i];
- iwas=(thelp == NULL) ? 0 : (thelp->index+1);
- fprintf(output," %X",iwas);
- }
- }
-
- thelp=twork->next_reduceable;
- iwas=(thelp == NULL) ? 0 : (thelp->index+1);
-
- fprintf(output," %X\n",iwas);
-
- twork=twork->nlist;
- }
-
- fclose(output);
- return(0);
- }
-
- /************************************************************************
- * input_tree *
- ************************************************************************/
-
- int input_tree(char *tfname, int add_flag)
- {
- FILE *fopen(), *input;
- OCTREE twork;
- int n,i,iwas,il;
-
- if ((input = fopen(tfname, "r")) == NULL)
- {
- if (add_flag == 1)
- {
- fprintf(stderr,"New octree file `%s'\n",tfname);
- return(0);
- }
- else
- {
- fprintf(stderr,"Can't open octree file `%s'\n",tfname);
- return(1);
- }
- }
-
- fprintf(stderr,"Read octree file `%s'\n",tfname);
- fflush(stderr);
-
- il=1;
- if (check_kennung(input) != 1) goto Rfail;
-
- il=2;
- if (fscanf(input,"%d",&tree_count) != 1) goto Rfail;
- if (fscanf(input,"%d",&max_colors) != 1) goto Rfail;
-
- il=3;
- if (fscanf(input,"%d",&octree_size) != 1) goto Rfail;
- if (fscanf(input,"%d",&reduce_level) != 1) goto Rfail;
- if (fscanf(input,"%d",&leaf_level) != 1) goto Rfail;
-
- tlist=(OCTREE) calloc(tree_count, sizeof(struct node));
- if (tlist == NULL)
- {
- fprintf(stderr,"Out of memory\n");
- fclose(input);
- return(1);
- }
-
- il=4;
- for (i=0; i <= MAXDEPTH; i++)
- {
- if (fscanf(input,"%X",&iwas) != 1) goto Rfail;
- if (iwas > tree_count)
- {
- fprintf(stderr,"Bad octree file (a): %d %d\n",i,iwas);
- goto Rfail;
- }
- if (iwas > 0)
- reduce_list[i]=&tlist[iwas-1];
- else
- reduce_list[i]=NULL;
- }
-
- for (n=tree_count-1; n >= 0; n--)
- {
- il++;
- twork=&tlist[n];
- twork->index = 0;
- if (fscanf(input,"%d",&iwas) != 1) goto Rfail; twork->leaf = iwas;
- if (fscanf(input,"%d",&iwas) != 1) goto Rfail; twork->level = iwas;
- if (fscanf(input,"%d",&iwas) != 1) goto Rfail; twork->sub_count = iwas;
-
- if (fscanf(input,"%lX %lX %lX %lX %lX %lX %lX %lX",
- &(twork->pixels_low),
- &(twork->pixels_high),
- &(twork->red_sum_low),
- &(twork->red_sum_high),
- &(twork->green_sum_low),
- &(twork->green_sum_high),
- &(twork->blue_sum_low),
- &(twork->blue_sum_high)) != 8) goto Rfail;
-
- if (twork->sub_count != 0)
- {
- for (i=0; i< 8; i++)
- {
- if (fscanf(input,"%X",&iwas) != 1) goto Rfail;
- if (iwas > tree_count)
- {
- fprintf(stderr,"Bad octree file (b): %d %d\n",n,iwas);
- goto Rfail;
- }
- if (iwas > 0)
- twork->sub[i] = &tlist[iwas-1];
- else
- twork->sub[i] = NULL;
- }
- }
- else
- {
- for (i=0; i< 8; i++)
- twork->sub[i] = NULL;
- }
-
- if (fscanf(input,"%X",&iwas) != 1) goto Rfail;
- if (iwas > tree_count)
- {
- fprintf(stderr,"Bad octree file (c): %d %d\n",n,iwas);
- goto Rfail;
- }
- if (iwas > 0)
- twork->next_reduceable = &tlist[iwas-1];
- else
- twork->next_reduceable = NULL;
- }
-
- fclose(input);
-
- basetree=tlist;
- return(0);
-
- Rfail:
-
- fprintf(stderr,"Error in line %d of file `%s'\n",il,tfname);
- fclose(input);
- return(1);
- }
-
- /****************************************************************
- * check_kennung
- ****************************************************************/
-
- int check_kennung(FILE *fp)
- {
- int c, i;
- char KennTest[MAX_KENN];
-
- i=0;
- while (1)
- {
- c=getc(fp);
- if (i < MAX_KENN) KennTest[i]=c;
- if ((c == '\n') || (c == EOF))
- {
- if (i < MAX_KENN)
- KennTest[i]='\0';
- else
- KennTest[MAX_KENN-1]='\0';
- break;
- }
- i++;
- }
-
- if (strcmp(Kennung,KennTest) != 0)
- {
- fprintf(stderr,"Wrong file format\n");
- return(0);
- }
- return(1);
- }
-