/* ppmtopj.c - convert a portable pixmap to an HP 500c image
**
** modified by David Safford (d-safford@tamu.edu) from ppmtopj.c by
** Copyright (C) 1990 by Christos Zoulas (christos@ee.cornell.edu)
**
** 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 "ppm.h"

#ifdef A_FORCEPROTO     /* IUW */
static int compress_row(unsigned char *op, unsigned char *oe, unsigned char *cp);
#endif /* A_FORCEPROTO */

/*
 * XXX: Only 8.5 x 11 paper
 *    NOTE: the 500C cannot print on the left 1/4" or bottom 1/2"
 *          of the page.  For appearance, put 1/4" margins everywhere
 *          except for the 1/2" bottom margin.
 */
#define WIDTH     8.0
#define HEIGHT    10.25
#define DPI       300
#define XPIX      ((int) ((DPI * WIDTH + 7) / 8) << 3)
#define YPIX      ((int) ((DPI * HEIGHT + 7) / 8) << 3)

#define C_RESET                 "\033E"
#define C_IMAGE_WIDTH           "\033*r%dS"
#define C_DATA_PLANES           "\033*r%dU"
#define C_TRANS_MODE            "\033*b%dM"
#define C_TRANS_MODE_STD        0
#define C_TRANS_MODE_RLE        1
#define C_SEND_PLANE            "\033*b%dV"
#define C_LAST_PLANE            "\033*b%dW"
#define C_BEGIN_RASTER          "\033*r1A"
#define C_END_RASTER            "\033*rbC"
#define C_RESOLUTION            "\033*t%dR"
#define C_RESOLUTION_75DPI      750
#define C_RESOLUTION_100DPI     100
#define C_RESOLUTION_150DPI     150
#define C_RESOLUTION_300DPI     300
#define C_MOVE_ORIGIN           "\033*p%dx%dY"
#define C_PAPER_MODE            "\033&l0a0l1H" /* US,skip perf,paper tray */
#define C_EJECT_PAGE            "\033&l0H"

char *testimage;


/*
 * Run-length encoding for the PaintJet. We have pairs of <instances>
 * <value>, where instances goes from 0 (meaning one instance) to 255
 * If we are unlucky we can double the size of the image.
 */
static int
compress_row(op, oe, cp)
unsigned char *op, *oe, *cp;
{
    unsigned char *ce = cp;
    while ( op < oe ) {
        unsigned char px = *op++;
        unsigned char *pr = op;
        while ( op < oe && *op == px && op - pr < 255) op++;
        *ce++ = op - pr;
        *ce++ = px;
    }
    return ce - cp;
}

void main(argc, argv)
int argc;
char *argv[];
{
        pixel **pixels;
        FILE *ifp;
        int argn, rows, cols, colors, r, c, k, m, p;
        pixval maxval;
        int planes = 3;
        unsigned char *obuf, *op, *cbuf;
        int mode = C_TRANS_MODE_STD;
        int deciwidth = 0, deciheight = 0;
        int center = 0;
        int xoff = 0, yoff = 75; /* default is to skip top & left 1/4" */
        /*
         * XXX: Someday we could make this command line options.
         */
        int resolution = C_RESOLUTION_300DPI;

        char *usage = "[-center] [-xpos <pos>] [-ypos <pos>] [-rle] [ppmfile]";

        ppm_init( &argc, argv );

        argn = 1;
        while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
            {
            if ( pm_keymatch(argv[argn],"-xpos",2) && argn + 1 < argc )
                {
                ++argn;
                if ( sscanf( argv[argn], "%d",&xoff ) != 1 )
                    pm_usage( usage );
                }
            else if ( pm_keymatch(argv[argn],"-ypos",2) && argn + 1 < argc )
                {
                ++argn;
                if ( sscanf( argv[argn], "%d",&yoff ) != 1 )
                    pm_usage( usage );
                }
            else if (pm_keymatch(argv[argn],"-rle",2))
                mode = C_TRANS_MODE_RLE;
            else if (pm_keymatch(argv[argn],"-center",2))
                center = 1;
            else
                pm_usage( usage );
            ++argn;
            }

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

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

        pixels = ppm_readppm( ifp, &cols, &rows, &maxval );

        pm_close( ifp );
        obuf = (unsigned char *) pm_allocrow(cols, sizeof(unsigned char));
        cbuf = (unsigned char *) pm_allocrow(cols * 2, sizeof(unsigned char));

        if (cols > XPIX || rows > YPIX) {
            fprintf(stderr,"image too large for page\n");
            exit(1);
        }
        if (center) {
            xoff = (XPIX - cols) / 2;
            yoff = yoff + (YPIX - rows) / 2;
        }

        (void) printf(C_RESET);
        (void) printf(C_PAPER_MODE);
        (void) printf(C_END_RASTER);
        (void) printf(C_DATA_PLANES, 3);        /* sets RGB mode */
        (void) printf(C_RESOLUTION, resolution);
        (void) printf(C_IMAGE_WIDTH, cols);
        (void) printf(C_MOVE_ORIGIN,xoff,yoff);
        (void) printf(C_BEGIN_RASTER);
        (void) printf(C_TRANS_MODE, mode);


        for (r = 0; r < rows; r++) {
            for (p = 0; p < 3; p++) {
                switch (p) {
                case 0:
                    for (c = 0, op = &obuf[-1]; c < cols; c++) {
                        if ((k = (c & 7)) == 0)
                            *++op = 0;
                        if (PPM_GETR(pixels[r][c]) > maxval / 2)
                            *op |= 1 << (7 - k);
                    }
                    break;
                case 1:
                    for (c = 0, op = &obuf[-1]; c < cols; c++) {
                        if ((k = (c & 7)) == 0)
                            *++op = 0;
                        if (PPM_GETG(pixels[r][c]) > maxval / 2)
                            *op |= 1 << (7 - k);
                    }
                    break;
                case 2:
                    for (c = 0, op = &obuf[-1]; c < cols; c++) {
                        if ((k = (c & 7)) == 0)
                            *++op = 0;
                        if (PPM_GETB(pixels[r][c]) > maxval / 2)
                            *op |= 1 << (7 - k);
                    }
                    break;
                }
                ++op;
                if (mode == C_TRANS_MODE_RLE) {
                    k = compress_row(obuf, op, cbuf);
                    op = cbuf;
                }
                else {
                    k = op - obuf;
                    op = obuf;
                }
                (void) printf(p == 2 ? C_LAST_PLANE : C_SEND_PLANE, k);
                (void) fwrite(op, 1, k, stdout);
            }
        }
        (void) printf(C_END_RASTER);
        (void) printf(C_EJECT_PAGE);
        exit(0);
}


