cnv/rletopaint.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.
 */
/* 
 * rletopaint.c - Convert an RLE file to macpaint format, using dithering.
 * 
 * Author:    John W. Peterson
 *         Computer Science Dept.
 *         University of Utah
 * Date:    Thu Oct 16 1986
 * Copyright (c) 1986, John W. Peterson
 *
 * Byte compression code (compress_line) by Jim Schimpf.
 *
 * Usage is:
 *   rle2paint [-l] [-i] [-g [gamma]] [-o outfile.paint] [infile.rle] 
 *
 * -l        Uses a linear color map in converting the RLE image
 * -r        Invert the bits in the MacPaint output.
 * -g [gamma]    Use a gamma map of [gamma] in converting the output
 *        (default is 2.0)
 * infile.rle    If specified, the input RLE file.  Default stdin.
 * -o outfile    If specified, the MacPaint file will be written here.
 *        Default stdout.
 *
 * Note that the image should be flipped vertically (by rleflip, e.g.),
 * as the two formats number their scanlines oppositely.
 */
static char rcsid[] = "$Header: /tmp_mnt/n/itn/hendrix/u/spencer/RCS/rletopaint.c,v 3.0.1.3 1992/04/30 14:00:05 spencer Exp spencer $";
/*
rletopaint()                Tag the file.
*/


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

#define MIN(x,y) ( (x) < (y) ? (x) : (y) )

/* Scanline storage & RLE row pointers */
rle_hdr hdr;
rle_pixel ** rows;

int gammamap[256];
unsigned char rawbits[73];    /* Uncompressed MacPaint pixels */
unsigned char squishedbits[74]; /* Compressed data */
int inverse_flag = 0;        /* If true then invert image */

/* Gray scale dither table */
int dmgray[8][8] = {   0 ,128 , 32 ,160 ,  8 ,136 , 40 ,168 ,
                 192 , 64 ,224 , 96 ,200 , 72 ,232 ,104 ,
                  48 ,176 , 16 ,144 , 56 ,184 , 24 ,152 ,
                 240 ,112 ,208 , 80 ,248 ,120 ,216 , 88 ,
                  12 ,140 , 44 ,172 ,  4 ,132 , 36 ,164 ,
                 204 , 76 ,236 ,108 ,196 , 68 ,228 ,100 ,
                  60 ,188 , 28 ,156 , 52 ,220 , 20 ,148 ,
                 252 ,124 ,220 , 92 ,244 ,116 ,212 ,84   } ;

int compress_line();
void write_paint_line(), bytes_to_bits();

void
main(argc, argv)
int argc;
char *argv[];
{
    int i;
    int databytes;
    float gam = 2.0;        /* Default gamma */
    int gamma_flag = 0, oflag = 0;
    int linear_flag = 1;
    char * fname = NULL, * out_fname = NULL;
    FILE * out_fp;

    if ( scanargs( argc, argv, "% r%- l%- g%-gamma%f o%-outfile!s infile%s",
          &inverse_flag, &linear_flag,
          &gamma_flag, &gam, &oflag, &out_fname, &fname ) == 0)
    exit(-1);

    if (gamma_flag) linear_flag = 0;
    /* Initialize header. */
    hdr = *rle_hdr_init( (rle_hdr *)NULL );
    rle_names( &hdr, cmd_name( argv ), fname, 0 );

    hdr.rle_file = rle_open_f(hdr.cmd, fname, "r");
    rle_get_setup_ok( &hdr, NULL, NULL );

    out_fp = rle_open_f(hdr.cmd, out_fname, "w");
    
    /* set up rows to point to our copy of the scanline */
    if (rle_row_alloc( &hdr, &rows ))
    {
    fprintf(stderr,"%s: malloc failed\n", hdr.cmd);
    exit(-2);
    }

    /* Compute a gamma correction map and tables */
    for ( i = 0; i < 256; i++ )
    {
    if (linear_flag)
        gammamap[i] = i;
    else
        gammamap[i] = (int)(0.5 + 255 * pow( i / 255.0, 1.0/gam ));
    }

    /* Output MacPaint header */
    for (i = 0; i < 512; i++)
    putc('\0', out_fp);     /* Null tells MacPaint to ignore header */

    for (i = 0; i < 72; i++)    /* Kludge - pad bottom with blank lines */
    rawbits[i] = 0;

    /* If the picture doesn't start at the bottom, output blank lines
     * to fill the space */

    databytes = compress_line();     /* Blank compressed data */
    for (i = 0; i < hdr.ymin; i++)
    write_paint_line(databytes, out_fp);

    for (i = hdr.ymin; i <= MIN( hdr.ymax, 719 ); i++)
    {
    rle_getrow( &hdr, rows );
        bytes_to_bits( i );
    write_paint_line(compress_line(), out_fp);
    }

    /* Finish up unused picture space */
    if (hdr.ymax < 719)
    {
    for (i = 0; i < 72; i++) /* Kludge - pad bottom with blank lines */
    rawbits[i] = 0;
    
    databytes = compress_line();     /* Blank compressed data */
    for (i = hdr.ymax+1; i < 720; i++)
        write_paint_line(databytes, out_fp);
    }
}
    
    
/* Write a scanline to the macpaint file. */
void
write_paint_line(num, fp)
int num;
FILE *fp;
{
    int j;
    for (j = 0; j < num; j++)
    putc(squishedbits[j], fp);
}

/*
 * Map a 24 bit scanline through to get dithered black and white, generating
 * a row of bits in rawbits.
 */
void
bytes_to_bits( y )
int y;
{
    register unsigned char *r, *g, *b;
    register int i, col, row;
    int pixel;
    int n = MIN(hdr.xmax, 576);
    int B = 0, W = 1;

    if (inverse_flag)
    {
    B = 1; W = 0;        /* Swap meaning of Black and White */
    }
    for (i = 0; i < 72; i++)
    rawbits[i] = 0;

    r = g = b = rows[0];
    if ( hdr.ncolors > 1 )
    g = rows[1];
    if ( hdr.ncolors > 2 )
    b = rows[2];
    for ( row = y % 8, col = 0, i = 0;
      i < n;
      i++, r++, g++, b++, col = ((col + 1) & 7) )
    {
    /* Conver to BW (uses YIQ/percentage xformation) */
    if ( hdr.ncolors > 2 )
        pixel = (35*gammamap[*r] + 55*gammamap[*g] + 10*gammamap[*b]) / 100;
    else if ( hdr.ncolors == 2 )
        pixel = (gammamap[*r] + gammamap[*g]) / 2;
    else
        pixel = gammamap[*r];
    if (pixel < 0) pixel += 256;

    /* Use dither to convert to zero/one */
    pixel = ((pixel > dmgray[col][row]) ? W : B);

    /* Compress scanline bytes to unencoded macpaint bits */
    rawbits[(i / 8)] |= pixel << (7-(i % 8));
    }
}
    
/*
 * Compress the bits in rawbits to run-length encoded bytes in squishedbits.
 *
 * Compression is as follows:
 *
 * <CNT><Byte><Byte>...    If <CNT>  < 0x80  i.e. <CNT> different bytes
 * <CNT><Byte>             If <CNT>  > 0x80  i.e. <CNT> repeated bytes
 *
 */

int
compress_line()
{
    int i,j,k = 0,cntpsn,count = 0;
    int flag;
    unsigned char pixel;
#define SAME 1
#define NOTSAME 0
                      
    i = 0;
    j = 2;
    if( rawbits[0] == rawbits[1] )
    {
    flag = SAME;
    cntpsn = 0;
    squishedbits[1] = rawbits[0];
    }
    else
    {
    flag = NOTSAME;
    cntpsn = 0;
    squishedbits[1] = rawbits[0];
    }

    while( i < 72 )
    {
    switch( flag )
    {
                                
        /*  Same case see how far the run goes then update stuff */

    case SAME:
        count = 0;
        for( k=i; k<72; k++ )
        {
        if( squishedbits[j-1] != rawbits[k] )
            break;
        else
            count++;
        }

        /* If count is zero then just skip all this stuff and try again
         * with the flag the other way
         */
        if( count != 0 )
        {

        /* Ok update the count and save the byte in the output line
         * NOTE: Count is just 2's compliment of the value
         */
        pixel = -1 * (count-1);
        squishedbits[cntpsn] = 0xff & pixel;

        /*  Set the flag for the other & advance j to next frame */
                       
        flag = NOTSAME;
        }
        else
        flag = NOTSAME;
        break;
                                
        /*  Not the same, look for a run of something if found quit */

    case NOTSAME:
        count = 0;
        for( k=i+1; k<72; k++ )
        {
        if( squishedbits[j-1] == rawbits[k] )
            break;
        else
        {
            count++;
            squishedbits[j++] = rawbits[k];
        }
        }
        /* If count is zero then skip all the updating stuff and just try
         * the other method
         */
        if( count != 0 )
        {
        if ( k == 72 )
            squishedbits[cntpsn] = count;
        else
        {
            squishedbits[cntpsn] = count - 1;

            /* Set the flag for the other and back up the psn
             * to get the start of the run.
             */
            
            k = k - 1;
            j--;
            flag = SAME;
        }
        }
        else
        flag = SAME;
        break;
    }


    /* End of loop update the positions of the count save lcn and
     * next character to look at 
     *   
     * Only do update on non zero counts 
     */
                                             
    if( count != 0 )
    {
        /* Sometimes 'compression' doesn't.  Check for this. */
        if ( j > k + 1 )
        {
        squishedbits[0] = k - 1;
        for ( j = 0; j < k; j++ )
            squishedbits[j + 1] = rawbits[j];
        j++;
        }
        cntpsn = j;
        squishedbits[++j] = rawbits[k];
        j++;
        i = k;
    }
    }

    return( j-2 );
}