File: FIX.C | Size: 14,413 bytes | Download file | Back to directory listing | BWPOW's homepage
/*********************************************/
/* FixPal 2.9                                */
/* Copyright (c) Marian Dvorsky              */
/* See FixPal.Txt for details                */
/*                                           */
/*             FixPal module                 */
/*********************************************/
 
#include "fixpal.h"
#include <stdarg.h>
#include <dos.h>
 
void unscale_palette(PALETTE *pal);
void scale_palette(PALETTE *pal);
 
/* Common pallete */
RGB common[65535];
 
unsigned int distances[65535];
 
/* New transparent color rgb */
RGB newtrans; int newtranb;
RGB oldtrans;
int count;
/* Methods used */
unsigned char method;
unsigned char findmethod;
 
/* Total colors in common palette */
unsigned int limit;
unsigned int colors;
unsigned int maxcolors;
unsigned int omaxcolors;
unsigned int transc;
signed int tolerance;
int remap;
int transb;
int bitmapno;
char bitmaps[255][255];
char palfile[255];
char logfile[255];
char inputpal[255];
char s[255];
FILE *filelog;
 
// Returns largest of three numbers
inline int max(int a, int b, int c)
{
  return(c>=(a>=b?a:b)?c:(a>=b?a:b));
}
 
/* Returns distance of 2 colors */
int distance(RGB c1,RGB c2)
{
  return ( (c1.r-c2.r)*(c1.r-c2.r) + (c1.g-c2.g)*(c1.g-c2.g) + (c1.b-c2.b)*(c1.b-c2.b));
}
 
void save_to_log(char *format, ...)
{
  char buf[256];
 
  if (strcmp(logfile,"") != 0)
  {
    va_list ap;
    va_start(ap, format);
    vsprintf(buf, format, ap);
    va_end(ap);
 
    fprintf(filelog,"%s\n",buf);
    fflush(filelog);
  }
}
 
/* Find color in common palette. Returns color */
int findcolor(int r,int g,int b)
{
  unsigned int x,a;
  int col = 0;
  int qual;
  RGB color;
  color.r = r;
  color.g = g;
  color.b = b;
  qual = 200000;
  for (x=0;x<=colors;x++){
    a = distance(color,common[x]);
    if (a < qual)
    {
      qual = a;
      col = x;
    }
  }
  save_to_log(" - finding color (%d,%d,%d). Found this one: (%d,%d,%d)",r,g,b,common[col].r,common[col].g,common[col].b);
/*  for (x=0;x<=colors;x++){
    if (max(abs(color.r-common[x].r),abs(color.g-common[x].g),abs(color.b-common[x].b)) < qual)
    {
      qual = max(abs(color.r-common[x].r),abs(color.g-common[x].g),abs(color.b-common[x].b));
      col = x;
    }
  }*/
  return(col);
}
 
/* Add color to common palette (do checking for same colors) */
void addcolor(int r,int g,int b)
{
  if (colors < limit){
    unsigned int x;
    int exists;
    exists = 0;
    for (x=0;x<=colors;x++){
      if ((r >= common[x].r-tolerance) && (r <= common[x].r+tolerance) &&
         (g >= common[x].g-tolerance) && (g <= common[x].g+tolerance) &&
         (b >= common[x].b-tolerance) && (b <= common[x].b+tolerance))
      {
        exists = 1;
        break;
      }
    }
    if (!exists){
      common[colors].r = r;
      common[colors].g = g;
      common[colors].b = b;
      save_to_log(" - new color found (r:%d,g:%d,b:%d)",r,g,b);
      colors++;
    }
  }
}
 
/* Reads palette from file and adds colors to common palette */
void readpal(char *name,int attrib, int param)
{
  PALETTE pal;
  BITMAP *bmp;
  unsigned short int newcolors[256];
  unsigned int x,y;
  unsigned int col;
  int cd;  // color depth;
  bmp = load_bitmap(name,pal);
  save_to_log("Reading file %s.",name);
  cd = bitmap_color_depth(bmp);
  save_to_log(" - bitmap info: (%dx%dx%d)",bmp->w,bmp->h,cd);
  if (cd == 8)
  {
    for (x=0;x<=255;x++)
      newcolors[x] = -1;
    select_palette(pal);
  }
  for (y=0;y<bmp->h;y++)
  {
    for (x=0;x<bmp->w;x++)
    {
      col = getpixel(bmp,x,y);
      if ((!transb || transc!=col) && (cd!=8 || newcolors[col]!=0))
      {
        if (cd==8)
  	  newcolors[col] = 0;
        addcolor(getr_depth(cd,col),getg_depth(cd,col),getb_depth(cd,col));
      }
    }
  }
  destroy_bitmap(bmp);
  count++;
  printf("\r    %d files loaded (colors used: %d)",count,colors);
  fflush(stdout);
}
 
/* Modifies bitmap file */
void modifybmp(char *name,int attrib,int param)
{
  int newcolors[256];
  PALETTE inpal;
  PALETTE *outpal;
  BITMAP *bmp;
  int x,y,cd;
  unsigned int col,newcol;
  save_to_log("Modifying file %s.",name);
  bmp = load_bitmap(name,inpal);
  cd = bitmap_color_depth(bmp);
  save_to_log(" - color depth: %d",cd);
  if (cd==8)
  {
    for (x=0;x<=255;x++)
      newcolors[x] = -1;
    select_palette(inpal);
  }
  if (strcmp(palfile,"") == 0)
    outpal = (PALETTE*)&common;
  else
    outpal = (PALETTE*)&inpal;
 
  for (y=0;y<bmp->h;y++)
    for (x=0;x<bmp->w;x++)
    {
      col = getpixel(bmp,x,y);
      if ((transb) && (transc == col))
        newcol = 0;
      else
      {
        if (cd==8 && newcolors[col] != -1)
          newcol = newcolors[col];
        else
        {
          if (getr_depth(cd,col)==oldtrans.r && getg_depth(cd,col)==oldtrans.g && getb_depth(cd,col)==oldtrans.b)
          {
            newcol = 0;
          }
          else
          if (findmethod == 'a')
            newcol = findcolor(getr_depth(cd,col)>>2,getg_depth(cd,col)>>2,getb_depth(cd,col)>>2);
          else
            newcol = bestfit_color(common,inpal[col].r,inpal[col].g,inpal[col].b);
          if (cd==8)
            newcolors[col] = newcol;
        }
      }
      putpixel(bmp,x,y,newcol);
    }
  remove(name);
  bmp->vtable->color_depth = 8;
  save_bitmap(name,bmp,*outpal);
  destroy_bitmap(bmp);
  count++;
  printf("\r    %d files modified",count);
  fflush(stdout);
  /* Create backup files */
/*  char *fil;
  fil = (char*)malloc(255);
  strcpy(fil,name);
  fil[strlen(name)-1] = '~';
  rename(name,fil);
  free((void*)fil);*/
}
 
/* Writes syntax on the screen */
void write_syntax_fix(void)
{
  printf("  Syntax: fixpal.exe [method] infile [infile [infile]...] [options]\n");
  printf("          method - method you want to use (s,g,c)\n");
  printf("          infile - graphic file(s) in format tga,pcx,bmp (use wildcard\n");
  printf("                   or filelist (add '@' before filelist name))\n\n");
  printf("  Options:\n");
  printf("         -pfilename : save palette ONLY to specified file\n");
  printf("         -mcol : set max number of colors in palette\n");
  printf("         -fx : finding best color method (x = a,b)\n");
  printf("         -tr,g,b : set rgb structure of transparent color\n");
  printf("         -ntr,g,b : set rgb structure of new transparent color\n");
  printf("         -llogfile : creates logfile\n");
  printf("         -r : reduce hi/truecolor files to 8 bit before processing\n");
  printf("         for method s\n");
  printf("           -Tc : set original transparent color (can't combine with -t)\n");
  printf("           -sc : set color tolerance (0-255)\n");
  printf("         for method c\n");
  printf("           -ifilename : re-map bitmaps' color to palette in this file\n");
}
 
void for_each_file_in_filelist(char *name,void (*callback)())
{
  FILE *f;
  char fil[255];
  f = fopen(name,"r");
  if (f)
  {
    while (!feof(f))
    {
      fscanf(f,"%s\n",fil);
      if (exists(fil))
        (*callback)(fil,0,0);
    }
    fclose(f);
  }
}
 
/* Reads palettes of all files specified */
void read_palettes(void)
{
  int x;
  printf(" - reading palettes...\n");
  count=0;
 
  for (x=0; x<bitmapno;x++)
    if (bitmaps[x][0] == '@')
      for_each_file_in_filelist(bitmaps[x],readpal);
    else
      for_each_file(bitmaps[x],FA_ARCH,readpal,0);
  printf("\n");
  if (count == 0)
  {
    printf("     no files found !\n");
    exit(255);
  }
//  printf("    Total colors: %d\n",colors);
}
 
/* Modifies structure of bitmap (replace old colors with new) */
void modify_bitmaps(void)
{
  int x;
  count=0;
  printf(" - modifying bitmaps...\n");
 
  for (x=0; x<bitmapno;x++)
  {
    if (bitmaps[x][0] == '@')
      for_each_file_in_filelist(bitmaps[x],modifybmp);
    else
      for_each_file(bitmaps[x],FA_ARCH,modifybmp,0);
  }
  printf("\n");
}
 
/* Writes palette to file */
void write_palette(void)
{
  BITMAP *bmp;
  printf(" - writing palette...\n");
  bmp = create_bitmap(1,1);
  save_bitmap(palfile,bmp,common);
  destroy_bitmap(bmp);
}
 
/* Set default options */
void default_options_fix()
{
  maxcolors = 256;
  /* Color 0 is ALWAYS RGB 0,0,0 ! */
  common[0].r = 0;
  common[0].g = 0;
  common[0].b = 0;
  colors = 1;
  remap=1;
  strcpy(palfile,"");
  set_color_conversion(COLORCONV_NONE);
  strcpy(logfile,"");
  strcpy(inputpal,"");
  newtranb = 0;
  method = 'g';
  tolerance = 0;
  findmethod = 'a';
  transb = 0;
  bitmapno = 0;
}
 
/* Read FixPal options */
void read_options_fix(int argc, char *argv[])
{
  int x,setted=0;
  char s[255];
  sscanf(argv[1],"%s",&s);
  if ((s[0] != 's') && (s[0] != 'c') && (s[0] != 'g') && (strlen(s)==1))
  {
    printf("Invalid method specified: %c.\n",s[0]);
    exit(255);
  }
  if (strlen(s)==1)
    method = s[0];
  for (x=1;x<=argc-1;x++)
  {
    if ((argv[x][0] != '-') && (strlen(argv[x])>1))
    {
      if (bitmapno == 254)
      {
        printf("Too many wildcards/files/filelists ! (Max 255) \n");
        exit(0);
      }
      strcpy(bitmaps[bitmapno],argv[x]);
      bitmapno++;
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 'p'))
    {
      sscanf(argv[x]+2,"%s",palfile);
      remap=0;
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 'l'))
    {
      sscanf(argv[x]+2,"%s",logfile);
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 'i'))
    {
      if (method != 'c')
      {
        printf("-i parameter only allowed in c method!\n");
        exit(255);
      }
      sscanf(argv[x]+2,"%s",inputpal);
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 'm'))
    {
      sscanf(argv[x]+2,"%d",&maxcolors);
      if ((maxcolors > 256) || (maxcolors <= 0))
      {
        printf("Invalid number of colors: %d.\n",maxcolors);
        exit(1);
      }
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 'r'))
    {
      set_color_conversion(COLORCONV_REDUCE_TO_256);
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 's'))
    {
      if (method != 's')
      {
        printf("-s parameter only allowed in s method!\n");
        exit(255);
      }
      sscanf(argv[x]+2,"%d",&tolerance);
      if ((tolerance > 255) || (tolerance <= 0))
      {
        printf("Invalid tolerance: %d.\n",tolerance);
        exit(1);
      }
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 't'))
    {
      if (method != 's' && method != 'g')
      {
        printf("-t parameter only allowed in s and g method!\n");
        exit(255);
      }
      if (setted)
      {
        printf("You can't combine parameter -t with -T\n");
        exit(0);
      }
      sscanf(argv[x]+2,"%d,%d,%d",&common[0].r,&common[0].g,&common[0].b);
      setted = 1;
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 'f'))
    {
      sscanf(argv[x]+2,"%c",&findmethod);
      if (findmethod != 'a' && findmethod != 'b')
      {
        printf("Invalid finding method: %c\n",findmethod);
        exit(255);
      }
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 'T'))
    {
      if (method != 's')
      {
        printf("-T parameter only allowed in s method!\n");
        exit(255);
      }
      if (setted)
      {
        printf("You can't combine parameter -T with -t\n");
        exit(0);
      }
      sscanf(argv[x]+2,"%d",&transc);
      setted = 1;
      transb = 1;
    }
    if ((argv[x][0] == '-') && (argv[x][1] == 'n') && (argv[x][2] == 't'))
    {
      if (method != 's' && method != 'g')
      {
        printf("-nt parameter only allowed in s and g method!\n");
        exit(255);
      }
      sscanf(argv[x]+3,"%d,%d,%d",&newtrans.r,&newtrans.g,&newtrans.b);
      newtranb=1;
    }
  }
  if (!remap &&	(strcmp(palfile,"") == 0))
  {
	printf("You have to specify output palette file (-p parameter).\n");
	exit(255);
  }
}
 
/* Load custom common palette from bitmap file */
void load_palette(void)
{
  BITMAP *bmp;
  bmp = load_bitmap(inputpal,common);
  if (bmp)
  {
    destroy_bitmap(bmp);
    colors = maxcolors;
  }
  else
  {
    printf("Palette file '%s' doesn't exists !\n",inputpal);
    exit(255);
  }
  scale_palette(&common);
}
 
 
/* Guess method. Reduces colors -> maxcolors */
void reduce_colors()
{
  unsigned int x,z,f,n,a,p;
  PALETTE newpal;
  unsigned int unique_colors=colors;
  if (colors <= maxcolors)
    return;
  colors=1;
  newpal[0]=common[0];
  for (z=1;z<maxcolors;z++)
  {
    // Find furthest color of it's nearest neighbor
    f = 0;
    p = 0;
    for (x=0;x<unique_colors;x++)
    {
      n = 200000;
      if (z==1)
      {
        distances[x] = distance(newpal[0],common[x]);
	n = distances[x];
      }
      else
      {
 	a = distance(newpal[z-1],common[x]);
        if (a < distances[x])
	{
	  n = a;
	  distances[x] = a;
	}
        n=distances[x];
      }
      if (f < n)
      {
 	f = n;
	p = x;
      }
    }
    newpal[z] = common[p];
  }
  colors = maxcolors;
  memcpy(common,newpal,colors*sizeof(RGB));
}
 
/* Begin of FixPal module */
void fixpal(int argc, char *argv[])
{
  struct date d;
  struct time t;
  default_options_fix();
  read_options_fix(argc,argv);
  if (strcmp(logfile,"") != 0)
  {
    filelog = fopen(logfile,"wt");
    getdate(&d);
    gettime(&t);
    save_to_log("Log file started %d/%d/%d %d:%d:%d",d.da_day,d.da_mon,d.da_year,t.ti_hour,t.ti_min,t.ti_sec);
  }
  /* Set colors limit */
  switch (method){
    case 's': limit = maxcolors; break;
    case 'g': limit = 65535; break;
  }
  /* Create common palette */
 
  oldtrans=common[0];
  switch (method){
    case 's':
    case 'g': read_palettes(); break;
    case 'c': load_palette(); break;
  }
 
  if (method=='g')
    reduce_colors();
 
  if (newtranb)
  {
    common[0].r = newtrans.r;
    common[0].g = newtrans.g;
    common[0].b = newtrans.b;
  }
  unscale_palette(&common);
  /* Final remap */
  if (remap)
    modify_bitmaps();
 
  /* Save palette if needed */
  if (strcmp(palfile,"") != 0)
    write_palette();
  if (strcmp(logfile,"") != 0)
    fclose(filelog);
}