home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / gbmsrc.zip / gbmcpal.c < prev    next >
C/C++ Source or Header  |  1999-01-02  |  21KB  |  876 lines

  1. /*
  2.  
  3. gbmcpal.c - Map to Common Palette
  4.  
  5. */
  6.  
  7. /*...sincludes:0:*/
  8. #include <stdio.h>
  9. #include <ctype.h>
  10. #include <string.h>
  11. #include <stddef.h>
  12. #include <stdlib.h>
  13. #include <stdarg.h>
  14. #include <memory.h>
  15. #include <malloc.h>
  16. #if defined(AIX) || defined(LINUX)
  17. #include <unistd.h>
  18. #else
  19. #include <io.h>
  20. #endif
  21. #include <fcntl.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #ifndef O_BINARY
  25. #define    O_BINARY    0
  26. #endif
  27. #include "gbm.h"
  28. #include "gbmhist.h"
  29. #include "gbmmcut.h"
  30.  
  31. /*...vgbm\46\h:0:*/
  32. /*...vgbmhist\46\h:0:*/
  33. /*...vgbmmcut\46\h:0:*/
  34. /*...e*/
  35.  
  36. static char progname[] = "gbmcpal";
  37.  
  38. /*...sfatal:0:*/
  39. static void fatal(const char *fmt, ...)
  40.     {
  41.     va_list    vars;
  42.     char s[256+1];
  43.  
  44.     va_start(vars, fmt);
  45.     vsprintf(s, fmt, vars);
  46.     va_end(vars);
  47.     fprintf(stderr, "%s: %s\n", progname, s);
  48.     exit(1);
  49.     }
  50. /*...e*/
  51. /*...susage:0:*/
  52. static void usage(void)
  53.     {
  54.     int ft, n_ft;
  55.  
  56.     fprintf(stderr, "usage: %s [-m map] [-v] n1 n2 n3 ifspec{,opt} ofspec{,opt}\n", progname);
  57.     fprintf(stderr, "flags: -m map         mapping to perform (default freq6:6:6:256)\n");
  58.     fprintf(stderr, "                      freqR:G:B:N       map all bitmaps to same palette, worked\n");
  59.     fprintf(stderr, "                                        out using frequency of use histogram\n");
  60.     fprintf(stderr, "                      mcutN             map all bitmaps to same palette, worked\n");
  61.     fprintf(stderr, "                                        out using median cut algorithm\n");
  62.     fprintf(stderr, "                      rofreqR:G:B:N:N2  map each bitmap to frequency palette,\n");
  63.     fprintf(stderr, "                                        reordered to minimise differences\n");
  64.     fprintf(stderr, "                                        between successive bitmaps\n");
  65.     fprintf(stderr, "                      romcutN:N2        map each bitmap to median cut palette,\n");
  66.     fprintf(stderr, "                                        reordered to minimise differences\n");
  67.     fprintf(stderr, "                                        between successive bitmaps\n");
  68.     fprintf(stderr, "                                        R,G,B are bits of red, green and blue\n");
  69.     fprintf(stderr, "                                        to keep, N is number of unique colours,\n");
  70.     fprintf(stderr, "                                        N2 is extra palette entries\n");
  71.     fprintf(stderr, "       -v             verbose mode\n");
  72.     fprintf(stderr, "       n1 n2 n3       for ( f=n1; f<n2; f+=n3 )\n");
  73.     fprintf(stderr, "       ifspec           printf(ifspec, f);\n");
  74.     fprintf(stderr, "       ofspec           printf(ofspec, f);\n");
  75.     fprintf(stderr, "                      filespecs are of the form fn.ext\n");
  76.     fprintf(stderr, "                      ext's are used to deduce desired bitmap file formats\n");
  77.  
  78.     gbm_init();
  79.     gbm_query_n_filetypes(&n_ft);
  80.     for ( ft = 0; ft < n_ft; ft++ )
  81.         {
  82.         GBMFT gbmft;
  83.  
  84.         gbm_query_filetype(ft, &gbmft);
  85.         fprintf(stderr, "                      %s when ext in [%s]\n",
  86.             gbmft.short_name, gbmft.extensions);
  87.         }
  88.     gbm_deinit();
  89.  
  90.     fprintf(stderr, "       opt's          bitmap format specific options\n");
  91.     fprintf(stderr, "   eg: %s -m mcut256 0 100 1 24bit%%03d.bmp 8bit%%03d.bmp\n", progname);
  92.  
  93.     exit(1);
  94.     }
  95. /*...e*/
  96. /*...ssame:0:*/
  97. static BOOLEAN same(const char *s1, const char *s2, int n)
  98.     {
  99.     for ( ; n--; s1++, s2++ )
  100.         if ( tolower(*s1) != tolower(*s2) )
  101.             return FALSE;
  102.     return TRUE;
  103.     }
  104. /*...e*/
  105. /*...smain:0:*/
  106. #define    CVT_FREQ   0
  107. #define    CVT_MCUT   1
  108. #define    CVT_ROFREQ 2
  109. #define    CVT_ROMCUT 3
  110.  
  111. static BOOLEAN verbose = FALSE;
  112.  
  113. /*...sget_masks:0:*/
  114. /*
  115. Returns TRUE if a set of masks given at map.
  116. Also sets *rm, *gm, *bm from these.
  117. Else returns FALSE.
  118. */
  119.  
  120. static byte mask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
  121.  
  122. static BOOLEAN get_masks(char *map, byte *rm, byte *gm, byte *bm)
  123.     {
  124.     if ( map[0] <  '0' || map[0] > '8' ||
  125.          map[1] != ':' ||
  126.          map[2] <  '0' || map[2] > '8' ||
  127.          map[3] != ':' ||
  128.          map[4] <  '0' || map[4] > '8' )
  129.         return FALSE;
  130.  
  131.     *rm = mask[map[0] - '0'];
  132.     *gm = mask[map[2] - '0'];
  133.     *bm = mask[map[4] - '0'];
  134.     return TRUE;
  135.     }
  136. /*...e*/
  137. /*...salloc_mem:0:*/
  138. static byte *alloc_mem(const GBM *gbm)
  139.     {
  140.     int stride, bytes;
  141.     byte *p;
  142.  
  143.     stride = ( ((gbm->w * gbm->bpp + 31)/32) * 4 );
  144.     bytes = stride * gbm->h;
  145.     if ( (p = malloc((size_t) bytes)) == NULL )
  146.         fatal("out of memory allocating %d bytes", bytes);
  147.  
  148.     return p;
  149.     }
  150. /*...e*/
  151. /*...sread_bitmap:0:*/
  152. static void read_bitmap(
  153.     const char *fn, const char *opt,
  154.     GBM *gbm, GBMRGB gbmrgb[], byte **data
  155.     )
  156.     {
  157.     int ft, fd;
  158.     GBM_ERR rc;
  159.  
  160.     if ( verbose )
  161.         {
  162.         if ( *opt != '\0' )
  163.             printf("Reading %s,%s\n", fn, opt);
  164.         else
  165.             printf("Reading %s\n", fn);
  166.         }
  167.  
  168.     if ( gbm_guess_filetype(fn, &ft) != GBM_ERR_OK )
  169.         fatal("can't guess bitmap file format for %s", fn);
  170.  
  171.     if ( (fd = gbm_io_open(fn, O_RDONLY|O_BINARY)) == -1 )
  172.         fatal("can't open %s", fn);
  173.  
  174.     if ( (rc = gbm_read_header(fn, fd, ft, gbm, opt)) != GBM_ERR_OK )
  175.         {
  176.         gbm_io_close(fd);
  177.         fatal("can't read header of %s: %s", fn, gbm_err(rc));
  178.         }
  179.  
  180.     if ( (rc = gbm_read_palette(fd, ft, gbm, gbmrgb)) != GBM_ERR_OK )
  181.         {
  182.         gbm_io_close(fd);
  183.         fatal("can't read palette of %s: %s", fn, gbm_err(rc));
  184.         }
  185.  
  186.     (*data) = alloc_mem(gbm);
  187.  
  188.     if ( (rc = gbm_read_data(fd, ft, gbm, (*data))) != GBM_ERR_OK )
  189.         {
  190.         free(*data);
  191.         gbm_io_close(fd);
  192.         fatal("can't read bitmap data of %s: %s", fn, gbm_err(rc));
  193.         }
  194.  
  195.     gbm_io_close(fd);
  196.     }
  197. /*...e*/
  198. /*...sread_bitmap_24:0:*/
  199. /*...sexpand_to_24bit:0:*/
  200. static void expand_to_24bit(GBM *gbm, GBMRGB *gbmrgb, byte **data)
  201.     {
  202.     int stride = ((gbm->w * gbm->bpp + 31)/32) * 4;
  203.     int new_stride = ((gbm->w * 3 + 3) & ~3);
  204.     int bytes, y;
  205.     byte *new_data;
  206.  
  207.     if ( gbm->bpp == 24 )
  208.         return;
  209.  
  210.     bytes = new_stride * gbm->h;
  211.     if ( (new_data = malloc((size_t) bytes)) == NULL )
  212.         fatal("out of memory allocating %d bytes", bytes);
  213.  
  214.     for ( y = 0; y < gbm->h; y++ )
  215.         {
  216.         byte    *src = *data + y * stride;
  217.         byte    *dest = new_data + y * new_stride;
  218.         int    x;
  219.  
  220.         switch ( gbm->bpp )
  221.             {
  222. /*...s1:24:*/
  223. case 1:
  224.     {
  225.     byte c;
  226.  
  227.     for ( x = 0; x < gbm->w; x++ )
  228.         {
  229.         if ( (x & 7) == 0 )
  230.             c = *src++;
  231.         else
  232.             c <<= 1;
  233.  
  234.         *dest++ = gbmrgb[c >> 7].b;
  235.         *dest++ = gbmrgb[c >> 7].g;
  236.         *dest++ = gbmrgb[c >> 7].r;
  237.         }
  238.     }
  239.     break;
  240. /*...e*/
  241. /*...s4:24:*/
  242. case 4:
  243.     for ( x = 0; x + 1 < gbm->w; x += 2 )
  244.         {
  245.         byte    c = *src++;
  246.  
  247.         *dest++ = gbmrgb[c >> 4].b;
  248.         *dest++ = gbmrgb[c >> 4].g;
  249.         *dest++ = gbmrgb[c >> 4].r;
  250.         *dest++ = gbmrgb[c & 15].b;
  251.         *dest++ = gbmrgb[c & 15].g;
  252.         *dest++ = gbmrgb[c & 15].r;
  253.         }
  254.  
  255.     if ( x < gbm->w )
  256.         {
  257.         byte    c = *src;
  258.  
  259.         *dest++ = gbmrgb[c >> 4].b;
  260.         *dest++ = gbmrgb[c >> 4].g;
  261.         *dest++ = gbmrgb[c >> 4].r;
  262.         }
  263.     break;
  264. /*...e*/
  265. /*...s8:24:*/
  266. case 8:
  267.     for ( x = 0; x < gbm->w; x++ )
  268.         {
  269.         byte    c = *src++;
  270.  
  271.         *dest++ = gbmrgb[c].b;
  272.         *dest++ = gbmrgb[c].g;
  273.         *dest++ = gbmrgb[c].r;
  274.         }
  275.     break;
  276. /*...e*/
  277.             }
  278.         }
  279.     free(*data);
  280.     *data = new_data;
  281.     gbm->bpp = 24;
  282.     }
  283. /*...e*/
  284.  
  285. static void read_bitmap_24(
  286.     const char *fn, const char *opt,
  287.     GBM *gbm, byte **data
  288.     )
  289.     {
  290.     GBMRGB gbmrgb[0x100];
  291.     read_bitmap(fn, opt, gbm, gbmrgb, data);
  292.     if ( gbm->bpp != 24 )
  293.         {
  294.         if ( verbose )
  295.             printf("Expanding to 24 bpp\n");
  296.         expand_to_24bit(gbm, gbmrgb, data);
  297.         }
  298.     }
  299. /*...e*/
  300. /*...sread_bitmap_24_f:0:*/
  301. static void read_bitmap_24_f(
  302.     const char *fn, int f, const char *opt,
  303.     GBM *gbm, byte **data
  304.     )
  305.     {
  306.     char fn_f[500+1];
  307.     sprintf(fn_f, fn, f);
  308.     read_bitmap_24(fn_f, opt, gbm, data);
  309.     }
  310. /*...e*/
  311. /*...swrite_bitmap:0:*/
  312. static void write_bitmap(
  313.     const char *fn, const char *opt,
  314.     const GBM *gbm, const GBMRGB gbmrgb[], const byte *data
  315.     )
  316.     {
  317.     int ft, fd, flag;
  318.     GBM_ERR rc;
  319.     GBMFT gbmft;
  320.  
  321.     if ( verbose )
  322.         {
  323.         if ( *opt != '\0' )
  324.             printf("Writing %s,%s\n", fn, opt);
  325.         else
  326.             printf("Writing %s\n", fn);
  327.         }
  328.  
  329.     if ( gbm_guess_filetype(fn, &ft) != GBM_ERR_OK )
  330.         fatal("can't guess bitmap file format for %s", fn);
  331.  
  332.     gbm_query_filetype(ft, &gbmft);
  333.     switch ( gbm->bpp )
  334.         {
  335.         case 24:    flag = GBM_FT_W24;    break;
  336.         case 8:        flag = GBM_FT_W8;    break;
  337.         case 4:        flag = GBM_FT_W4;    break;
  338.         case 1:        flag = GBM_FT_W1;    break;
  339.         }
  340.  
  341.     if ( (gbmft.flags & flag) == 0 )
  342.         fatal("output bitmap format %s does not support writing %d bpp data",
  343.             gbmft.short_name, gbm->bpp);
  344.  
  345.     if ( (fd = gbm_io_create(fn, O_WRONLY|O_BINARY)) == -1 )
  346.         fatal("can't create %s", fn);
  347.  
  348.     if ( (rc = gbm_write(fn, fd, ft, gbm, gbmrgb, data, opt)) != GBM_ERR_OK )
  349.         {
  350.         gbm_io_close(fd);
  351.         remove(fn);
  352.         fatal("can't write %s: %s", fn, gbm_err(rc));
  353.         }
  354.  
  355.     gbm_io_close(fd);
  356.     }
  357. /*...e*/
  358. /*...swrite_bitmap_f:0:*/
  359. static void write_bitmap_f(
  360.     const char *fn, int f, const char *opt,
  361.     const GBM *gbm, const GBMRGB gbmrgb[], const byte *data
  362.     )
  363.     {
  364.     char fn_f[500+1];
  365.     sprintf(fn_f, fn, f);
  366.     write_bitmap(fn_f, opt, gbm, gbmrgb, data);
  367.     }
  368. /*...e*/
  369. /*...sfreq_map:0:*/
  370. static void freq_map(
  371.     int first, int last, int step,
  372.     const char *fn_src, const char *opt_src,
  373.     const char *fn_dst, const char *opt_dst,
  374.     int ncols, byte rm, byte gm, byte bm
  375.     )
  376.     {
  377.     int f;
  378.     GBMHIST *hist;
  379.     GBMRGB gbmrgb[0x100];
  380.  
  381.     for ( ;; )
  382.         {
  383.         if ( verbose )
  384.             printf("Attempting to build histogram data with masks 0x%02x 0x%02x 0x%02x\n",
  385.                 rm, gm, bm);
  386.         if ( (hist = gbm_create_hist(rm, gm, bm)) == NULL )
  387.             fatal("can't create histogram data");
  388.         for ( f = first; f < last; f += step )
  389.             {
  390.             GBM gbm; byte *data;
  391.             BOOLEAN ok;
  392.             read_bitmap_24_f(fn_src, f, opt_src, &gbm, &data);
  393.             ok = gbm_add_to_hist(hist, &gbm, data);
  394.             free(data);
  395.             if ( !ok )
  396.                 {
  397.                 if ( verbose )
  398.                     printf("Too many colours\n");
  399.                 break;
  400.                 }
  401.             }
  402.         if ( f == last )
  403.             break;
  404.  
  405.         gbm_delete_hist(hist);
  406.  
  407.         if ( gm > rm )
  408.             gm <<= 1;
  409.         else if ( rm > bm )
  410.             rm <<= 1;
  411.         else
  412.             bm <<= 1;
  413.         }
  414.  
  415.     if ( verbose )
  416.         printf("Working out %d colour palette, based on histogram data\n", ncols);
  417.  
  418.     gbm_pal_hist(hist, gbmrgb, ncols);
  419.  
  420.     if ( verbose )
  421.         printf("Converting files to new optimal palette\n");
  422.  
  423.     for ( f = first; f < last; f += step )
  424.         {
  425.         GBM gbm; byte *data, *data8;
  426.         read_bitmap_24_f(fn_src, f, opt_src, &gbm, &data);
  427.         gbm.bpp = 8;
  428.         data8 = alloc_mem(&gbm);
  429.         if ( verbose )
  430.             printf("Mapping to optimal palette\n");
  431.         gbm_map_hist(hist, &gbm, data, data8);
  432.         free(data);
  433.         write_bitmap_f(fn_dst, f, opt_dst, &gbm, gbmrgb, data8);
  434.         free(data8);
  435.         }            
  436.  
  437.     gbm_delete_hist(hist);
  438.     }
  439. /*...e*/
  440. /*...smcut_map:0:*/
  441. static void mcut_map(
  442.     int first, int last, int step,
  443.     const char *fn_src, const char *opt_src,
  444.     const char *fn_dst, const char *opt_dst,
  445.     int ncols
  446.     )
  447.     {
  448.     int f;
  449.     GBMMCUT *mcut;
  450.     GBMRGB gbmrgb[0x100];
  451.  
  452.     if ( verbose )
  453.         printf("Attempting to build median cut statistics\n");
  454.  
  455.     if ( (mcut = gbm_create_mcut()) == NULL )
  456.         fatal("can't create median cut data");
  457.     for ( f = first; f < last; f += step )
  458.         {
  459.         GBM gbm; byte *data;
  460.         read_bitmap_24_f(fn_src, f, opt_src, &gbm, &data);
  461.         gbm_add_to_mcut(mcut, &gbm, data);
  462.         free(data);
  463.         }
  464.  
  465.     if ( verbose )
  466.         printf("Working out %d colour palette, based on median cut statistics\n", ncols);
  467.  
  468.     gbm_pal_mcut(mcut, gbmrgb, ncols);
  469.  
  470.     if ( verbose )
  471.         printf("Converting files to new optimal palette\n");
  472.  
  473.     for ( f = first; f < last; f += step )
  474.         {
  475.         GBM gbm; byte *data, *data8;
  476.         read_bitmap_24_f(fn_src, f, opt_src, &gbm, &data);
  477.         gbm.bpp = 8;
  478.         data8 = alloc_mem(&gbm);
  479.         if ( verbose )
  480.             printf("Mapping to optimal palette\n");
  481.         gbm_map_mcut(mcut, &gbm, data, data8);
  482.         free(data);
  483.         write_bitmap_f(fn_dst, f, opt_dst, &gbm, gbmrgb, data8);
  484.         free(data8);
  485.         }            
  486.  
  487.     gbm_delete_mcut(mcut);
  488.     }
  489. /*...e*/
  490. /*...srofreq_map\44\ romcut_map:0:*/
  491. /*
  492.  
  493. This code has been written primarily to support crude animation schemes.
  494. Imagine an animation which is a series of (palette, bitmap-bits) pairs.
  495.  
  496. If the displayer is unable to change both the palette and bitmap-bits in
  497. the vertical retrace interval, or if ping-pong double buffering is not
  498. available, there will be a short time where the new palette has been set, but
  499. the old bits are still on display. This causes a very disturbing flicker.
  500. (Similarly, this is true if the displaying program sets the bitmap-bits, and
  501. then the palette).
  502.  
  503. This code reorders the new palette, so that its entries are close to the
  504. previous palette. Old palette entries used by the most pixels are considered
  505. for this matching process first. Hence only small areas of the image flicker.
  506.  
  507. eg: old palette           = { red   , green      , light green, black       }
  508.     new palette           = { green , orange     , dark blue  , light green }
  509.     reordered new palette = { orange, light green, green      , dark blue   }
  510.  
  511.     Clearly red->orange is less offensive than red->green etc..
  512.  
  513. */
  514.  
  515. /*...sro_map:0:*/
  516. /*
  517.  
  518. The palettes returned using gbm_hist/mcut are sorted with most used first.
  519. (Index through MAP to get palette entries in PAL in order of frequency).
  520.  
  521. Map first image to palette PAL and bits BITS.
  522. For i = 0 to ncols-1
  523.   MAP[i] = i
  524. Write out image PAL and BITS
  525. For each subsequent image
  526.   Map image to PAL' and BITS'
  527.   For i = 0 to ncols-1
  528.     MAP'[i] = -1
  529.   For i = 0 to ncols-1
  530.     j = index of closest entry to PAL[MAP[i]] in PAL',
  531.       with MAP'[j] = -1
  532.     MAP'[j] = MAP[i];
  533.   For i = 0 to ncols-1
  534.     PAL[MAP'[i]] = PAL'[i]
  535.   For each pixel p
  536.     BITS'[p] = MAP'[BITS'[p]]
  537.   Write out PAL and BITS'
  538.   BITS = BITS'
  539.   MAP = MAP'
  540.  
  541. */
  542.  
  543. /*...scalc_mapP:0:*/
  544. /*
  545. For each entry in the old palette, starting with the most used, find the
  546. closest 'unclaimed' entry in the new palette, and 'claim' it.
  547. Thus, if you iterate though mapP[0..ncols-1] you get palette indexes
  548. of close entries in the old palette.
  549. */
  550.  
  551. static void calc_mapP(
  552.     const GBMRGB gbmrgb [], const word map [],
  553.           GBMRGB gbmrgbP[],       word mapP[],
  554.     int dists[],
  555.     int ncols
  556.     )
  557.     {
  558.     int i;
  559.  
  560.     if ( verbose )
  561.         printf("Reordering palette to cause least flicker\n");
  562.  
  563.     for ( i = 0; i < ncols; i++ )
  564.         mapP[i] = (word) 0xffff;
  565.  
  566.     /* Go through old palette entries, in descending freq order */
  567.     for ( i = 0; i < ncols; i++ )
  568.         {
  569.         const GBMRGB *p = &(gbmrgb[map[i]]);
  570.         int mindist = 255*255*3 + 1;
  571.         int j, minj;
  572.         /* Find closest entry in new palette */
  573.         for ( j = 0; j < ncols; j++ )
  574.             {
  575.             int dr = (int) ( (unsigned int) p->r - (unsigned int) gbmrgbP[j].r );
  576.             int dg = (int) ( (unsigned int) p->g - (unsigned int) gbmrgbP[j].g );
  577.             int db = (int) ( (unsigned int) p->b - (unsigned int) gbmrgbP[j].b );
  578.             int dist = dr*dr + dg*dg + db*db;
  579.             if ( dist < mindist && mapP[j] == (word) 0xffff )
  580.                 {
  581.                 minj = j;
  582.                 mindist = dist;
  583.                 }
  584.             }
  585.         dists[minj] = mindist;
  586.         mapP[minj] = map[i];
  587.         }
  588.     }
  589. /*...e*/
  590.  
  591. static void ro_map(
  592.     int first, int last, int step,
  593.     const char *fn_src, const char *opt_src,
  594.     const char *fn_dst, const char *opt_dst,
  595.     int ncols, int ncolsextra, byte rm, byte gm, byte bm,
  596.     void (*get)(
  597.         const char *fn, int f, const char *opt,
  598.         int ncols, byte rm, byte gm, byte bm,
  599.         GBM *gbm, GBMRGB gbmrgb[], byte **data8
  600.         )
  601.     )
  602.     {
  603.     GBM gbm; GBMRGB gbmrgb[0x100]; byte *data8;
  604.     word map[0x100], *extra = &(map[ncols]);
  605.     int i, f;
  606.  
  607.     if ( first >= last )
  608.         return;
  609.  
  610.     (*get)(fn_src, first, opt_src, ncols, rm, gm, bm, &gbm, gbmrgb, &data8);
  611.     for ( i = 0; i < ncols+ncolsextra; i++ )
  612.         map[i] = (word) i;
  613.  
  614.     write_bitmap_f(fn_dst, first, opt_dst, &gbm, gbmrgb, data8);
  615.  
  616.     for ( f = first + step; f < last; f += step )
  617.         {
  618.         GBM gbmP; GBMRGB gbmrgbP[0x100]; byte *data8P, *p;
  619.         word mapP[0x100]; int dists[0x100];
  620.         int x, y, stride;
  621.  
  622.         (*get)(fn_src, f, opt_src, ncols, rm, gm, bm, &gbmP, gbmrgbP, &data8P);
  623.         calc_mapP(gbmrgb, map, gbmrgbP, mapP, dists, ncols);
  624.  
  625. /*...shandle ncolsextra worst matches specially:16:*/
  626. {
  627. int j;
  628.  
  629. /* Find the ncolsextra worst palette changes */
  630.  
  631. for ( i = 0; i < ncolsextra; i++ )
  632.     {
  633.     int jmax, maxdist = -1;
  634.     for ( j = 0; j < ncols; j++ )
  635.         if ( dists[j] != -1 && dists[j] > maxdist )
  636.             {
  637.             jmax = j;
  638.             maxdist = dists[j];
  639.             }
  640.     dists[jmax] = -1;
  641.     }
  642.  
  643. /* Use extra palette entries for these instead */
  644.  
  645. for ( i = 0, j = 0; i < ncolsextra; i++, j++ )
  646.     {
  647.     word t;
  648.     while ( dists[j] != -1 )
  649.         j++;
  650.     t = mapP[j];        /* This is a bad palette entry */
  651.     mapP[j] = extra[i];    /* Use extra entry instead */
  652.     extra[i] = t;        /* So bad one is fair game next loop */
  653.     }
  654. }
  655. /*...e*/
  656.  
  657.         for ( i = 0; i < ncols; i++ )
  658.             gbmrgb[mapP[i]] = gbmrgbP[i];
  659.  
  660.         stride = ((gbmP.w+3)&~3);
  661.         for ( y = 0, p = data8P; y < gbmP.h; y++, p += stride )
  662.             for ( x = 0; x < gbmP.w; x++ )
  663.                 p[x] = (byte) mapP[p[x]];
  664.  
  665.         write_bitmap_f(fn_dst, f, opt_dst, &gbmP, gbmrgb, data8P);
  666.  
  667.         gbm = gbmP;
  668.         free(data8);
  669.         data8 = data8P;
  670.         memcpy(map, mapP, ncols * sizeof(word));
  671.         }
  672.  
  673.     free(data8);
  674.     }
  675. /*...e*/
  676. /*...srofreq_map:0:*/
  677. /*...sget_and_hist:0:*/
  678. static void get_and_hist(
  679.     const char *fn, int f, const char *opt,
  680.     int ncols, byte rm, byte gm, byte bm,
  681.     GBM *gbm, GBMRGB gbmrgb[], byte **data8
  682.     )
  683.     {
  684.     byte *data24;
  685.     read_bitmap_24_f(fn, f, opt, gbm, &data24);
  686.     gbm->bpp = 8;
  687.     (*data8) = alloc_mem(gbm);
  688.     if ( !gbm_hist(gbm, data24, gbmrgb, *data8, ncols, rm, gm, bm) )
  689.         fatal("can't compute histogram");
  690.     free(data24);
  691.     }
  692. /*...e*/
  693.  
  694. static void rofreq_map(
  695.     int first, int last, int step,
  696.     const char *fn_src, const char *opt_src,
  697.     const char *fn_dst, const char *opt_dst,
  698.     int ncols, int ncolsextra, byte rm, byte gm, byte bm
  699.     )
  700.     {
  701.     ro_map(
  702.         first, last, step,
  703.         fn_src, opt_src, fn_dst, opt_dst,
  704.         ncols, ncolsextra, rm, gm, bm,
  705.         get_and_hist
  706.         );
  707.     }
  708. /*...e*/
  709. /*...sromcut_map:0:*/
  710. /*...sget_and_mcut:0:*/
  711. static void get_and_mcut(
  712.     const char *fn, int f, const char *opt,
  713.     int ncols, byte rm, byte gm, byte bm,
  714.     GBM *gbm, GBMRGB gbmrgb[], byte **data8
  715.     )
  716.     {
  717.     byte *data24;
  718.     rm=rm; gm=gm; bm=bm; /* Suppress 'unused arg warning' */
  719.     read_bitmap_24_f(fn, f, opt, gbm, &data24);
  720.     gbm->bpp = 8;
  721.     (*data8) = alloc_mem(gbm);
  722.     if ( !gbm_mcut(gbm, data24, gbmrgb, *data8, ncols) )
  723.         fatal("can't perform median-cut");
  724.     free(data24);
  725.     }
  726. /*...e*/
  727.  
  728. static void romcut_map(
  729.     int first, int last, int step,
  730.     const char *fn_src, const char *opt_src,
  731.     const char *fn_dst, const char *opt_dst,
  732.     int ncols, int ncolsextra
  733.     )
  734.     {
  735.     ro_map(
  736.         first, last, step,
  737.         fn_src, opt_src, fn_dst, opt_dst,
  738.         ncols, ncolsextra, 0, 0, 0,
  739.         get_and_mcut
  740.         );
  741.     }
  742. /*...e*/
  743. /*...e*/
  744.  
  745. int main(int argc, char *argv[])
  746.     {
  747.     char *map = "freq6:6:6:256";
  748.     char fn_src[500+1], fn_dst[500+1], *opt_src, *opt_dst;
  749.     int i, m, ncols, ncolsextra, first, last, step;
  750.     byte rm, gm, bm;
  751.  
  752. /*...sprocess command line options:8:*/
  753. for ( i = 1; i < argc; i++ )
  754.     {
  755.     if ( argv[i][0] != '-' )
  756.         break;
  757.     switch ( argv[i][1] )
  758.         {
  759.         case 'm':    if ( ++i == argc )
  760.                     fatal("expected map argument");
  761.                 map = argv[i];
  762.                 break;
  763.         case 'v':    verbose = TRUE;
  764.                 break;
  765.         default:    usage();
  766.                 break;
  767.         }
  768.     }
  769. /*...e*/
  770. /*...sframes and filenames etc\46\:8:*/
  771. if ( i == argc )
  772.     usage();
  773. sscanf(argv[i++], "%d", &first);
  774.  
  775. if ( i == argc )
  776.     usage();
  777. sscanf(argv[i++], "%d", &last);
  778.  
  779. if ( i == argc )
  780.     usage();
  781. sscanf(argv[i++], "%d", &step);
  782.  
  783. if ( i == argc )
  784.     usage();
  785. strcpy(fn_src, argv[i++]);
  786. strcpy(fn_dst, ( i == argc ) ? fn_src : argv[i++]);
  787. if ( i < argc )
  788.     usage();
  789.  
  790. if ( (opt_src = strchr(fn_src, ',')) != NULL )
  791.     *opt_src++ = '\0';
  792. else
  793.     opt_src = "";
  794.  
  795. if ( (opt_dst = strchr(fn_dst, ',')) != NULL )
  796.     *opt_dst++ = '\0';
  797. else
  798.     opt_dst = "";
  799. /*...e*/
  800. /*...sdeduce mapping and bits per pixel etc\46\:8:*/
  801. if ( same(map, "freq", 4) )
  802.     {
  803.     m = CVT_FREQ;
  804.     if ( !get_masks(map + 4, &rm, &gm, &bm) )
  805.         fatal("freqR:G:B:N has bad/missing R:G:B");
  806.     if ( map[9] != ':' )
  807.         fatal("freqR:G:B:N has bad/missing :N");
  808.     sscanf(map + 10, "%i", &ncols);
  809.     if ( ncols < 1 || ncols > 256 )
  810.         fatal("freqR:G:B:N N number between 1 and 256 required");
  811.     }
  812. else if ( same(map, "mcut", 4) )
  813.     {
  814.     m = CVT_MCUT;
  815.     sscanf(map+4, "%i", &ncols);
  816.     if ( ncols < 1 || ncols > 256 )
  817.         fatal("mcutN N number between 1 and 256 required");
  818.     }
  819. else if ( same(map, "rofreq", 6) )
  820.     {
  821.     m = CVT_ROFREQ;
  822.     if ( !get_masks(map+6, &rm, &gm, &bm) )
  823.         fatal("rofreqR:G:B:N has bad/missing R:G:B");
  824.     if ( map[11] != ':' )
  825.         fatal("rofreqR:G:B:N has bad/missing :N:N2");
  826.     sscanf(map + 12, "%i:%i", &ncols, &ncolsextra);
  827.     if ( ncols+ncolsextra < 1 || ncols+ncolsextra > 256 )
  828.         fatal("rofreqR:G:B:N:N2 N+N2 must be between 1 and 256");
  829.     }
  830. else if ( same(map, "romcut", 6) )
  831.     {
  832.     m = CVT_ROMCUT;
  833.     sscanf(map+6, "%i:%i", &ncols, &ncolsextra);
  834.     if ( ncols+ncolsextra < 1 || ncols+ncolsextra > 256 )
  835.         fatal("mcutN:N2 N+N2 must be between 1 and 256");
  836.     }
  837. else
  838.     fatal("unrecognised mapping %s", map);
  839. /*...e*/
  840.  
  841.     gbm_init();
  842.  
  843.     switch ( m )
  844.         {
  845.         case CVT_FREQ:
  846.             freq_map(
  847.                 first, last, step,
  848.                 fn_src, opt_src, fn_dst, opt_dst,
  849.                 ncols, rm, gm, bm);
  850.             break;
  851.         case CVT_MCUT:
  852.             mcut_map(
  853.                 first, last, step,
  854.                 fn_src, opt_src, fn_dst, opt_dst,
  855.                 ncols);
  856.             break;
  857.         case CVT_ROFREQ:
  858.             rofreq_map(
  859.                 first, last, step,
  860.                 fn_src, opt_src, fn_dst, opt_dst,
  861.                 ncols, ncolsextra, rm, gm, bm);
  862.             break;
  863.         case CVT_ROMCUT:
  864.             romcut_map(
  865.                 first, last, step,
  866.                 fn_src, opt_src, fn_dst, opt_dst,
  867.                 ncols, ncolsextra);
  868.             break;
  869.         }
  870.  
  871.     gbm_deinit();
  872.  
  873.     return 0;
  874.     }
  875. /*...e*/
  876.