tools/to8.c

Summary

Maintainability
Test Coverage
/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is 
 * preserved on all copies.
 * 
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the 
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 */
/* 
 * to8.c - Convert color images to 8 bit dithered.
 * 
 * Author:    Spencer W. Thomas
 *         Computer Science Dept.
 *         University of Utah
 * Date:    Fri Feb 28 1986
 * Copyright (c) 1986, University of Utah
 * 
 */
#ifndef lint
static char rcs_ident[] = "$Header: /tmp_mnt/n/itn/hendrix/u/spencer/RCS/to8.c,v 3.0.1.4 1992/04/30 14:14:43 spencer Exp spencer $";
#endif

#include <stdio.h>
#include <math.h>
#include "rle.h"

void init_color(), map_scanline();

short map[3][256];            /* Output color map. */
int colmap[216][3];
rle_pixel ** in_cmap;        /* color map in input file */

int dm16[16][16];


int modN[256], divN[256];

double disp_gam = 2.5;
double img_gam = 1.0;

/*****************************************************************
 * TAG( main )
 * 
 * Usage: to8 [-{iI} gamma] [-g gamma] [-o outfile] [infile]
 * 
 * Inputs:
 *    -i gamma:    Specify gamma of image. (default 1.0)
 *    -I gamma:    Specify gamma of display image was computed for.
 *     to8 will also read picture comments from the input file to determine
 *            the image gamma.  These are
 *    image_gamma=    gamma of image (equivalent to -i)
 *    display_gamma=    gamma of display image was computed for.
 *            Command line arguments override values in the file.
 *        
 *    -g gamma:    Specify gamma of display. (default 2.5)
 *     infile:        Input (color) RLE file.  Stdin used if not
 *            specified.
 * Outputs:
 *     outfile:    Output dithered RLE file.  Stdout used
 *            if not specified.
 * Assumptions:
 *    [None]
 * Algorithm:
 *    [None]
 */

void
main( argc, argv )
int argc;
char **argv;
{
    char * infname = NULL, * outfname = NULL;
    char comment[80];        /* for gamma comment */
    FILE * outfile = stdout;
    int oflag = 0, y, nrow, iflag = 0, gflag = 0;
    rle_hdr     in_hdr, out_hdr;
    unsigned char ** scan, *outscan[2];
    unsigned char * buffer;
    int rle_cnt, rle_err;

    in_hdr = *rle_hdr_init( NULL );
    out_hdr = *rle_hdr_init( NULL );

    if ( scanargs( argc, argv,
           "% Ii%-gamma!F g%-gamma!F o%-outfile!s infile%s",
           &iflag, &img_gam, &gflag, &disp_gam,
           &oflag, &outfname, &infname ) == 0 )
    exit( 1 );
    in_hdr.rle_file = rle_open_f(cmd_name( argv ), infname, "r");
    rle_names( &in_hdr, cmd_name( argv ), infname, 0 );
    rle_names( &out_hdr, in_hdr.cmd, outfname, 0 );

    for ( rle_cnt = 0;
      (rle_err = rle_get_setup( &in_hdr )) == RLE_SUCCESS;
      rle_cnt++ )
    {

    if ( in_hdr.ncolors == 1 )
    {
        fprintf( stderr, "%s is already black & white\n",
             infname ? infname : "stdin" );
        exit( 1 );
    }
    if ( in_hdr.ncolors < 3 )
    {
        fprintf( stderr, "%s is not RGB",
             infname ? infname : "stdin" );
        exit( 1 );
    }

    /* If no image gamma on command line, check comments in file */
    if ( ! iflag )
    {
        char * v;
        if ( (v = rle_getcom( "image_gamma", &in_hdr )) != NULL )
        {
        img_gam = atof( v );
        /* Protect against bogus information */
        if ( img_gam == 0.0 )
            img_gam = 1.0;
        else
            img_gam = 1.0 / img_gam;
        }
        else if ( (v = rle_getcom( "display_gamma", &in_hdr )) != NULL )
        {
        img_gam = atof( v );
        /* Protect */
        if ( img_gam == 0.0 )
            img_gam = 1.0;
        }
    }

    (void)rle_hdr_cp( &in_hdr, &out_hdr );
    if ( rle_cnt == 0 )
        outfile = rle_open_f(cmd_name( argv ), outfname, "w");
    in_hdr.xmax -= in_hdr.xmin;
    in_hdr.xmin = 0;
    nrow = in_hdr.xmax + 1;
    buffer = (unsigned char *)malloc( nrow );
    RLE_CHECK_ALLOC( cmd_name( argv ), buffer, 0 );
    if ( rle_row_alloc( &in_hdr, &scan ) < 0 )
        RLE_CHECK_ALLOC( cmd_name( argv ), 0, 0 );

    if ( in_hdr.alpha )
    {
        outscan[0] = scan[-1];
    }
    outscan[1] = buffer;

    /* Use input color map, too */
    in_cmap = buildmap( &in_hdr, 3, img_gam, 1.0 );

    init_color();
    out_hdr.ncolors = 1;
    out_hdr.ncmap = 3;
    out_hdr.cmaplen = 8;    /* 256 entries */
    out_hdr.cmap = (rle_map *)map;
    /* Delete color map length comment, if present. */
    rle_delcom( "color_map_length", &out_hdr );

    /* Record gamma color map was computed for */
    sprintf( comment, "display_gamma=%g", disp_gam );
    rle_putcom( comment, &out_hdr );

    /*
     * Give it a background color of black, since the real background
     * will be dithered anyway.
     */
    if ( in_hdr.background != 0 )
    {
        out_hdr.bg_color = (int *)malloc( sizeof( int ) );
        RLE_CHECK_ALLOC( cmd_name( argv ), out_hdr.bg_color, 0 );
        out_hdr.bg_color[0] = 0;
    }

    out_hdr.rle_file = outfile;

    rle_addhist( argv, &in_hdr, &out_hdr );

    rle_put_setup( &out_hdr );

    while ( (y = rle_getrow( &in_hdr, scan )) <=
        in_hdr.ymax )
    {
        map_scanline( scan, nrow, y, buffer );
        rle_putrow( &outscan[1], nrow, &out_hdr );
    }

    rle_puteof( &out_hdr );

    rle_row_free( &in_hdr, scan );
    }

    /* Check for an error.  EOF or EMPTY is ok if at least one image
     * has been read.  Otherwise, print an error message.
     */
    if ( rle_cnt == 0 || (rle_err != RLE_EOF && rle_err != RLE_EMPTY) )
    rle_get_error( rle_err, cmd_name( argv ), infname );

    exit( 0 );
}


/*
 * Initialize the 8 bit color map.  Choice of
 * "alpha-1" (perceptual flavor) or linear maps
 */
void
init_color()
{
    int     i;

    dithermap( 6, disp_gam, colmap, divN, modN, dm16 );

    for (i = 0; i < 216; i++)
    {
    map[0][i] = colmap[i][0] << 8;
    map[1][i] = colmap[i][1] << 8;
    map[2][i] = colmap[i][2] << 8;
    }

}

/*
 * Map a scanline to 8 bits through the dither matrix.
 */
#define DMAP(v,x,y)    (modN[v]>dm16[x][y] ? divN[v] + 1 : divN[v])

void
map_scanline( rgb, n, y, line )
unsigned char *rgb[3], *line;
int n, y;
{
    register unsigned char *r, *g, *b;
    register int i, col, row;
    for ( row = y % 16, col = 0, i = 0, r = rgb[0], g = rgb[1], b = rgb[2];
          i < n; i++, r++, g++, b++, col = ((col + 1) & 15) )
        line[i] = DMAP(in_cmap[0][*r], col, row) +
              DMAP(in_cmap[1][*g], col, row) * 6 +
              DMAP(in_cmap[2][*b], col, row) * 36;
}