tools/to8.c
/*
* 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;
}