/* pnmmalias.c - antialias a portable anymap.
**
** Copyright (C) 1992 by Alberto Accomazzi, Smithsonian Astrophysical
** Observatory.
**
** 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"

int
main( argc, argv )
    int argc;
    char* argv[];
    {
    FILE* ifp;
    xel* xelrow[3];
    xel* newxelrow;
    pixel bgcolorppm, fgcolorppm;
    register xel* xpP;
    register xel* xP;
    register xel* xnP;
    register xel* nxP;
    xel bgcolor, fgcolor;
    int argn, rows, cols, format, newformat, bgflag, fgflag, bgonly, fgonly;
    int bgalias, fgalias;
    register int row, col, i;
    double fmask[9], weight;
    xelval maxval, value, valuer, valueg, valueb;
    char* usage = "[-bgcolor <color>] [-fgcolor <color>] [-bonly] [-fonly] [-balias] [-falias] [-weight <w>] [pnmfile]";

    pnm_init( &argc, argv );

    bgonly = fgonly = 0;
    bgalias = fgalias = 0;
    weight = 1./3.;
    argn = 1;
    PPM_ASSIGN( bgcolorppm, 0, 0, 0);
    PPM_ASSIGN( fgcolorppm, 0, 0, 0);

    while ( argn < argc && argv[argn][0] == '-' )
        {
        if ( pm_keymatch( argv[argn], "-fgcolor", 3 ) ) 
	    {
	    if ( ++argn >= argc ) 
		pm_usage( usage );
	    else 
		fgcolorppm = ppm_parsecolor( argv[argn], PPM_MAXMAXVAL );
	    }
        else if ( pm_keymatch( argv[argn], "-bgcolor", 3 ) ) 
	    {
	    if ( ++argn >= argc ) 
		pm_usage( usage );
	    else 
		bgcolorppm = ppm_parsecolor( argv[argn], PPM_MAXMAXVAL );
	    }
        else if ( pm_keymatch( argv[argn], "-weight", 2 ) ) 
	    {
	    if ( ++argn >= argc ) 
		pm_usage( usage );
	    else if ( sscanf( argv[argn], "%lf", &weight ) != 1 )
		    pm_usage( usage );
	    else if ( weight >= 1. || weight <= 0. )
		{
		pm_message( "weight factor w must be 0.0 < w < 1.0" );
		pm_usage( usage );
		}
	    }
  	else if ( pm_keymatch( argv[argn], "-bonly", 3 ) )
	    bgonly = 1;
  	else if ( pm_keymatch( argv[argn], "-fonly", 3 ) )
	    fgonly = 1;
  	else if ( pm_keymatch( argv[argn], "-balias", 3 ) )
	    bgalias = 1;
  	else if ( pm_keymatch( argv[argn], "-falias", 3 ) )
	    fgalias = 1;
  	else if ( pm_keymatch( argv[argn], "-bfalias", 3 ) )
	    bgalias = fgalias = 0;
  	else if ( pm_keymatch( argv[argn], "-fbalias", 3 ) )
	    bgalias = fgalias = 0;
        else
            pm_usage( usage );
        ++argn;
        }

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

    if ( argn != argc )
	pm_usage( usage );

    /* normalize mask elements */
    fmask[4] = weight;
    fmask[0] = fmask[1] = fmask[2] = fmask[3] = ( 1.0 - weight ) / 8.0;
    fmask[5] = fmask[6] = fmask[7] = fmask[8] = ( 1.0 - weight ) / 8.0;

    /* use larger value for better results */
    pnm_pbmmaxval = PNM_MAXMAXVAL;
    pnm_readpnminit( ifp, &cols, &rows, &maxval, &format );
   
    xelrow[0] = pnm_allocrow( cols );
    xelrow[1] = pnm_allocrow( cols );
    xelrow[2] = pnm_allocrow( cols );
    newxelrow = pnm_allocrow( cols );

    /* Promote PBM files to PGM. */
    if ( PNM_FORMAT_TYPE(format) == PBM_TYPE )
	{
        newformat = PGM_TYPE;
	pm_message( "promoting from PBM to PGM" );
	}
    else
        newformat = format;

    /* Figure out foreground pixel value if none was given */
    if (PPM_GETR(fgcolorppm) == 0 && PPM_GETG(fgcolorppm) == 0 && 
	PPM_GETB(fgcolorppm) == 0 )
	{
    	if ( PNM_FORMAT_TYPE(newformat) == PGM_TYPE )
	    PNM_ASSIGN1( fgcolor, maxval );
	else 
	    PPM_ASSIGN( fgcolor, maxval, maxval, maxval );
	}
    else 
	{
    	if ( PNM_FORMAT_TYPE(newformat) == PGM_TYPE )
	    PNM_ASSIGN1( fgcolor, PPM_GETR( fgcolorppm ) );
	else 
	    fgcolor = fgcolorppm;
	}

    if (PPM_GETR(bgcolorppm) != 0 || PPM_GETG(bgcolorppm) != 0 || 
	PPM_GETB(bgcolorppm) != 0 )
	{
    	if ( PNM_FORMAT_TYPE(newformat) == PGM_TYPE )
	    PNM_ASSIGN1( bgcolor, PPM_GETR( bgcolorppm) );
	else 
	    bgcolor = bgcolorppm;
	}
    else
	{
    	if ( PNM_FORMAT_TYPE(newformat) == PGM_TYPE )
	    PNM_ASSIGN1( bgcolor, 0 );
	else 
	    PPM_ASSIGN( bgcolor, 0, 0, 0 );
	}


    pnm_readpnmrow( ifp, xelrow[0], cols, maxval, format );
    pnm_readpnmrow( ifp, xelrow[1], cols, maxval, format );
    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
    pnm_writepnmrow( stdout, xelrow[0], cols, maxval, newformat, 0 );

    for ( row = 1; row < rows - 1; ++row )
	{
	pnm_readpnmrow( ifp, xelrow[(row+1)%3], cols, maxval, format );
	newxelrow[0] = xelrow[row%3][0];

	for ( col = 1, xpP = (xelrow[(row-1)%3] + 1), xP = (xelrow[row%3] + 1),
		xnP = (xelrow[(row+1)%3] + 1), nxP = (newxelrow+1); 
		col < cols - 1; ++col, ++xpP, ++xP, ++xnP, ++nxP ) 
	    {
	    /* Do anti-aliasing here: see if pixel is at the border of a
	     * background or foreground stepwise side */
	    bgflag = (PNM_EQUAL(*xpP,bgcolor) && PNM_EQUAL(*(xP+1),bgcolor)) ||
		     (PNM_EQUAL(*(xP+1),bgcolor) && PNM_EQUAL(*xnP,bgcolor)) ||
		     (PNM_EQUAL(*xnP,bgcolor) && PNM_EQUAL(*(xP-1),bgcolor)) ||
		     (PNM_EQUAL(*(xP-1),bgcolor) && PNM_EQUAL(*xpP,bgcolor));
	    fgflag = (PNM_EQUAL(*xpP,fgcolor) && PNM_EQUAL(*(xP+1),fgcolor)) ||
		     (PNM_EQUAL(*(xP+1),fgcolor) && PNM_EQUAL(*xnP,fgcolor)) ||
		     (PNM_EQUAL(*xnP,fgcolor) && PNM_EQUAL(*(xP-1),fgcolor)) ||
		     (PNM_EQUAL(*(xP-1),fgcolor) && PNM_EQUAL(*xpP,fgcolor)); 

	    /* Reset flags if anti-aliasing is to be done on foreground
	     * or background pixels only */
	    if ( ( bgonly && PNM_EQUAL( *xP, fgcolor ) ) ||
		 ( fgonly && PNM_EQUAL( *xP, bgcolor ) ) ) 
		bgflag = fgflag = 0;

	    if ( ( bgflag && bgalias ) || ( fgflag && fgalias ) || 
		 ( bgflag && fgflag ) )
		switch( PNM_FORMAT_TYPE( newformat ) )
		    {	
		    case PGM_TYPE:
			value = PNM_GET1(*(xpP-1)) * fmask[0] +
				PNM_GET1(*(xpP  )) * fmask[1] +	
				PNM_GET1(*(xpP+1)) * fmask[2] +
				PNM_GET1(*(xP -1)) * fmask[3] +
				PNM_GET1(*(xP   )) * fmask[4] +
				PNM_GET1(*(xP +1)) * fmask[5] +
				PNM_GET1(*(xnP-1)) * fmask[6] +
				PNM_GET1(*(xnP  )) * fmask[7] +
				PNM_GET1(*(xnP+1)) * fmask[8] +
				0.5;
			PNM_ASSIGN1( *nxP, value );
		        break;
		    default:
			valuer= PPM_GETR(*(xpP-1)) * fmask[0] +
				PPM_GETR(*(xpP  )) * fmask[1] +	
				PPM_GETR(*(xpP+1)) * fmask[2] +
				PPM_GETR(*(xP -1)) * fmask[3] +
				PPM_GETR(*(xP   )) * fmask[4] +
				PPM_GETR(*(xP +1)) * fmask[5] +
				PPM_GETR(*(xnP-1)) * fmask[6] +
				PPM_GETR(*(xnP  )) * fmask[7] +
				PPM_GETR(*(xnP+1)) * fmask[8] +
				0.5;
			valueg= PPM_GETG(*(xpP-1)) * fmask[0] +
				PPM_GETG(*(xpP  )) * fmask[1] +	
				PPM_GETG(*(xpP+1)) * fmask[2] +
				PPM_GETG(*(xP -1)) * fmask[3] +
				PPM_GETG(*(xP   )) * fmask[4] +
				PPM_GETG(*(xP +1)) * fmask[5] +
				PPM_GETG(*(xnP-1)) * fmask[6] +
				PPM_GETG(*(xnP  )) * fmask[7] +
				PPM_GETG(*(xnP+1)) * fmask[8] +
				0.5;
			valueb= PPM_GETB(*(xpP-1)) * fmask[0] +
				PPM_GETB(*(xpP  )) * fmask[1] +	
				PPM_GETB(*(xpP+1)) * fmask[2] +
				PPM_GETB(*(xP -1)) * fmask[3] +
				PPM_GETB(*(xP   )) * fmask[4] +
				PPM_GETB(*(xP +1)) * fmask[5] +
				PPM_GETB(*(xnP-1)) * fmask[6] +
				PPM_GETB(*(xnP  )) * fmask[7] +
				PPM_GETB(*(xnP+1)) * fmask[8] +
				0.5;
			PPM_ASSIGN( *nxP, valuer, valueg, valueb );
			break;
		    }
	    else
		*nxP = *xP;
	    }

	newxelrow[cols-1] = xelrow[row%3][cols-1];
	pnm_writepnmrow( stdout, newxelrow, cols, maxval, newformat, 0 );

    	}
	    
    pnm_writepnmrow( stdout, xelrow[row%3], cols, maxval, newformat, 0 );

    pm_close( ifp );
    exit ( 0 );
    }

