/* rasttopnm.c - read a Sun rasterfile and produce a portable anymap
**
** Copyright (C) 1989, 1991 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/

#include "pnm.h"
#include "rast.h"

int
main( argc, argv )
    int argc;
    char* argv[];
    {
    FILE* ifp;
    struct rasterfile header;
    colormap_t pr_colormap;
    int grayscale;
    struct pixrect* pr;
    xel* xelrow;
    register xel* xP;
    int argn, rows, cols, format, depth, i, row, mask;
    register int col;
    xelval maxval;
    xel zero, one;
    int linesize;
    unsigned char* data;
    unsigned char* byteP;

    pnm_init( &argc, argv );

    argn = 1;

    if ( argn != argc )
	{
	ifp = pm_openr( argv[argn] );
	++argn;
	}
    else
	ifp = stdin;

    if ( argn != argc )
	pm_usage( "[rastfile]" );

    /* Read in the rasterfile.  First the header. */
    if ( pr_load_header( ifp, &header ) != 0 )
	pm_error( "unable to read in rasterfile header" );

    cols = header.ras_width;
    rows = header.ras_height;
    depth = header.ras_depth;

    if ( cols <= 0 )
	pm_error( "invalid cols: %d", cols );
    if ( rows <= 0 )
	pm_error( "invalid rows: %d", rows );

    /* If there is a color map, read it. */
    grayscale = 1;
    if ( header.ras_maplength != 0 )
	{
	if ( pr_load_colormap( ifp, &header, &pr_colormap ) != 0 )
	    pm_error( "unable to skip colormap data" );
	for ( i = 0; i < header.ras_maplength / 3; ++i )
	    if ( pr_colormap.map[0][i] != pr_colormap.map[1][i] ||
		 pr_colormap.map[1][i] != pr_colormap.map[2][i] )
		{
		grayscale = 0;
		break;
		}
	}

    /* Check the depth and color map. */
    switch ( depth )
	{
	case 1:
	if ( header.ras_maptype == RMT_NONE && header.ras_maplength == 0 )
	    {
	    maxval = 1;
	    format = PBM_TYPE;
	    PNM_ASSIGN1( zero, maxval );
	    PNM_ASSIGN1( one, 0 );
	    }
	else if ( header.ras_maptype == RMT_EQUAL_RGB &&
		  header.ras_maplength == 6 )
	    {
	    if ( grayscale )
		{
		maxval = 255;
		format = PGM_TYPE;
		PNM_ASSIGN1( zero, pr_colormap.map[0][0] );
		PNM_ASSIGN1( one, pr_colormap.map[0][1] );
		}
	    else
		{
		maxval = 255;
		format = PPM_TYPE;
		PPM_ASSIGN(
		    zero, pr_colormap.map[0][0], pr_colormap.map[1][0],
		    pr_colormap.map[2][0] );
		PPM_ASSIGN(
		    one, pr_colormap.map[0][1], pr_colormap.map[1][1],
		    pr_colormap.map[2][1] );
		}
	    }
	else
	    pm_error(
            "this depth-1 rasterfile has a non-standard colormap - "
            "type %ld length %ld",
            header.ras_maptype, header.ras_maplength );
	break;

	case 8:
	if ( grayscale )
	    {
	    maxval = 255;
	    format = PGM_TYPE;
	    }
	else if ( header.ras_maptype == RMT_EQUAL_RGB )
	    {
	    maxval = 255;
	    format = PPM_TYPE;
	    }
	else
	    pm_error(
            "this depth-8 rasterfile has a non-standard colormap - "
            "type %ld length %ld",
            header.ras_maptype, header.ras_maplength );
	break;

	case 24:
	case 32:
	if ( header.ras_maptype == RMT_NONE && header.ras_maplength == 0 )
	    ;
	else if ( header.ras_maptype == RMT_RAW || header.ras_maplength == 768 )
	    ;
	else
	    pm_error(
            "this depth-%d rasterfile has a non-standard colormap - "
            "type %ld length %ld",
            depth, header.ras_maptype, header.ras_maplength );
	maxval = 255;
	format = PPM_TYPE;
	break;

	default:
	pm_error(
	    "invalid depth: %d.  Can only handle depth 1, 8, 24, or 32.",
	    depth );
	}

    /* Now load the data.  The pixrect returned is a memory pixrect. */
    if ( ( pr = pr_load_image( ifp, &header, NULL ) ) == NULL )
	pm_error(
	    "unable to read in the image from the rasterfile" );

    linesize = ( (struct mpr_data*) pr->pr_data )->md_linebytes;
    data = ( (struct mpr_data*) pr->pr_data )->md_image;

    pm_close( ifp );

    /* Now write out the anymap. */
    pnm_writepnminit( stdout, cols, rows, maxval, format, 0 );
    xelrow = pnm_allocrow( cols );
    switch ( PNM_FORMAT_TYPE(format) )
        {
        case PBM_TYPE:
        pm_message( "writing PBM file" );
        break;

        case PGM_TYPE:
        pm_message( "writing PGM file" );
        break;

        case PPM_TYPE:
        pm_message( "writing PPM file" );
        break;

        default:
        pm_error( "shouldn't happen" );
        }

    for ( row = 0; row < rows; ++row )
	{
	byteP = data;
	switch ( depth )
	    {
	    case 1:
	    mask = 0x80;
	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
		{
		if ( mask == 0 )
		    {
		    ++byteP;
		    mask = 0x80;
		    }
		*xP = ( *byteP & mask ) ? one : zero;
		mask = mask >> 1;
		}
	    break;

	    case 8:
	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
		{
		if ( header.ras_maplength == 0 )
		    PNM_ASSIGN1( *xP, *byteP );
		else if ( grayscale )
		    PNM_ASSIGN1( *xP, pr_colormap.map[0][*byteP] );
		else
		    PPM_ASSIGN(
			*xP, pr_colormap.map[0][*byteP],
			pr_colormap.map[1][*byteP],
			pr_colormap.map[2][*byteP] );
		++byteP;
		}
	    break;

	    case 24:
	    case 32:
	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
		{
		register xelval r, g, b;

		if ( depth == 32 )
		    ++byteP;
		if ( header.ras_type == RT_FORMAT_RGB )
		    {
		    r = *byteP++;
		    g = *byteP++;
		    b = *byteP++;
		    }
		else
		    {
		    b = *byteP++;
		    g = *byteP++;
		    r = *byteP++;
		    }
		if ( header.ras_maplength == 0 )
		    PPM_ASSIGN( *xP, r, g, b );
		else
		    PPM_ASSIGN(
			*xP, pr_colormap.map[0][r], pr_colormap.map[1][g],
			pr_colormap.map[2][b] );
		}
	    break;

	    default:
	    pm_error( "can't happen" );
	    }
	data += linesize;
	pnm_writepnmrow( stdout, xelrow, cols, maxval, format, 0 );
	}

    pm_close( stdout );

    exit( 0 );
    }
