Blame converter/ppm/ppmtoacad.c

Packit 78deda
/*
Packit 78deda
Packit 78deda
      Convert a portable pixmap to an AutoCAD slide or DXB file
Packit 78deda
Packit 78deda
    Author:
Packit 78deda
            John Walker
Packit 78deda
            Autodesk SA
Packit 78deda
            Avenue des Champs-Montants 14b
Packit 78deda
            CH-2074 MARIN
Packit 78deda
            Switzerland
Packit 78deda
            Usenet: kelvin@Autodesk.com
Packit 78deda
            Fax:    038/33 88 15
Packit 78deda
            Voice:  038/33 76 33
Packit 78deda
Packit 78deda
    Permission  to  use, copy, modify, and distribute this software and
Packit 78deda
    its documentation  for  any  purpose  and  without  fee  is  hereby
Packit 78deda
    granted,  without any conditions or restrictions.  This software is
Packit 78deda
    provided "as is" without express or implied warranty.
Packit 78deda
Packit 78deda
*/
Packit 78deda
Packit 78deda
#include <stdio.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "ppm.h"
Packit 78deda
Packit 78deda
#define EOS     '\0'
Packit 78deda
Packit 78deda
#define MAXHIST         32767         /* Color histogram maximum size */
Packit 78deda
Packit 78deda
static pixel **pixels;                /* Input pixel map */
Packit 78deda
static colorhash_table cht;           /* Color hash table */
Packit 78deda
static int curcol = -1;               /* Current slide output color */
Packit 78deda
static int polymode = FALSE;          /* Output filled polygons ? */
Packit 78deda
static int dxbmode = FALSE;           /* Output .dxb format ? */
Packit 78deda
static int bgcol = -1;                /* Screen background color */
Packit 78deda
static double aspect = 1.0;           /* Pixel aspect ratio correction */
Packit 78deda
static int gamut = 256;               /* Output color gamut */
Packit 78deda
Packit 78deda
#include "autocad.h"                  /* AutoCAD standard color assignments */
Packit 78deda
Packit 78deda
/* prototypes */
Packit 78deda
static void outrun ARGS((int color, int ysize, int y, int xstart, int xend));
Packit 78deda
static void slideout ARGS((int xdots, int ydots, int ncolors,
Packit 78deda
        unsigned char *red, unsigned char *green, unsigned char *blue));
Packit 78deda
Packit 78deda
Packit 78deda
/*  OUTRUN  --  Output a run of pixels. */
Packit 78deda
Packit 78deda
static void outrun(color, ysize, y, xstart, xend)
Packit 78deda
  int color, ysize, y, xstart, xend;
Packit 78deda
{
Packit 78deda
    if (color == 0) {
Packit 78deda
        return;                       /* Let screen background handle this */
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (curcol != color) {
Packit 78deda
        if (dxbmode) {
Packit 78deda
            putchar((char)136);
Packit 78deda
            (void) pm_writelittleshort(stdout, color);
Packit 78deda
        } else {
Packit 78deda
            (void) pm_writelittleshort(stdout, (short) 0xFF00 | color);
Packit 78deda
        }
Packit 78deda
        curcol = color;
Packit 78deda
    }
Packit 78deda
    if (polymode) {
Packit 78deda
        int v, yb = (ysize - y) + 1, yspan = 1;
Packit 78deda
Packit 78deda
        /* Since  we're emitting filled polygons,  let's scan downward
Packit 78deda
           in the pixmap and see if we can extend the run on this line
Packit 78deda
           vertically  as  well.   If  so, emit a polygon that handles
Packit 78deda
           both the horizontal and vertical run and clear  the  pixels
Packit 78deda
           in the subsequent lines to the background color. */
Packit 78deda
Packit 78deda
        for (v = y + 1; v <= ysize; v++) {
Packit 78deda
            int j, mismatch = FALSE;
Packit 78deda
Packit 78deda
            for (j = xstart; j <= xend; j++) {
Packit 78deda
                if (PPM_GETR(pixels[y][j]) != PPM_GETR(pixels[v][j])) {
Packit 78deda
                    mismatch = TRUE;
Packit 78deda
                    break;
Packit 78deda
                }
Packit 78deda
            }
Packit 78deda
            if (mismatch) {
Packit 78deda
                break;
Packit 78deda
            }
Packit 78deda
            for (j = xstart; j <= xend; j++) {
Packit 78deda
                PPM_ASSIGN(pixels[v][j], 0, 0, 0);
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        yspan = v - y;
Packit 78deda
Packit 78deda
        if (dxbmode) {
Packit 78deda
            putchar(11);              /* Solid */
Packit 78deda
            (void) pm_writelittleshort(
Packit 78deda
                stdout, (int) (xstart * aspect + 0.4999));
Packit 78deda
            (void) pm_writelittleshort(stdout, yb);
Packit 78deda
Packit 78deda
            (void) pm_writelittleshort(
Packit 78deda
                stdout, (int) ((xend + 1) * aspect + 0.4999));
Packit 78deda
            (void) pm_writelittleshort(stdout, yb);
Packit 78deda
Packit 78deda
            (void) pm_writelittleshort(
Packit 78deda
                stdout, (int) (xstart * aspect + 0.4999));
Packit 78deda
            (void) pm_writelittleshort(stdout, yb - yspan);
Packit 78deda
Packit 78deda
            (void) pm_writelittleshort(
Packit 78deda
                stdout, (int) ((xend + 1) * aspect + 0.4999));
Packit 78deda
            (void) pm_writelittleshort(stdout, yb - yspan);
Packit 78deda
        } else {
Packit 78deda
            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
Packit 78deda
              /* Solid fill header */
Packit 78deda
            (void) pm_writelittleshort(stdout, 4);      
Packit 78deda
              /* Vertices to follow */
Packit 78deda
            (void) pm_writelittleshort(stdout, -2);     /* Fill type */
Packit 78deda
Packit 78deda
            (void) pm_writelittleshort(stdout, (short)0xFD00); 
Packit 78deda
              /* Solid fill vertex */
Packit 78deda
            (void) pm_writelittleshort(stdout, xstart);
Packit 78deda
            (void) pm_writelittleshort(stdout, yb);
Packit 78deda
Packit 78deda
            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
Packit 78deda
              /* Solid fill vertex */
Packit 78deda
            (void) pm_writelittleshort(stdout, xend + 1);
Packit 78deda
            (void) pm_writelittleshort(stdout, yb);
Packit 78deda
Packit 78deda
            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
Packit 78deda
              /* Solid fill vertex */
Packit 78deda
            (void) pm_writelittleshort(stdout, xend + 1);
Packit 78deda
            (void) pm_writelittleshort(stdout, yb - yspan);
Packit 78deda
Packit 78deda
            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
Packit 78deda
              /* Solid fill vertex */
Packit 78deda
            (void) pm_writelittleshort(stdout, xstart);
Packit 78deda
            (void) pm_writelittleshort(stdout, yb - yspan);
Packit 78deda
Packit 78deda
            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
Packit 78deda
              /* Solid fill trailer */
Packit 78deda
            (void) pm_writelittleshort(stdout, 4); /* Vertices that precede */
Packit 78deda
            (void) pm_writelittleshort(stdout, -2);     /* Fill type */
Packit 78deda
        }
Packit 78deda
    } else {
Packit 78deda
        (void) pm_writelittleshort(stdout, xstart);     /* Vector:  From X */
Packit 78deda
        (void) pm_writelittleshort(stdout, ysize - y);  /*          From Y */
Packit 78deda
        (void) pm_writelittleshort(stdout, xend);       /*          To   X */
Packit 78deda
        (void) pm_writelittleshort(stdout, ysize - y);  /*          To   Y */
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
/*  SLIDEOUT  --  Write an AutoCAD slide.  */
Packit 78deda
Packit 78deda
static void slideout(xdots, ydots, ncolors, red, green, blue)
Packit 78deda
  int xdots, ydots, ncolors;
Packit 78deda
  unsigned char *red, *green, *blue;
Packit 78deda
{
Packit 78deda
    static char sldhead[18] = "AutoCAD Slide\r\n\32";
Packit 78deda
    static char dxbhead[20] = "AutoCAD DXB 1.0\r\n\32";
Packit 78deda
    unsigned char *acadmap;
Packit 78deda
    int i, xsize, ysize;
Packit 78deda
Packit 78deda
    /* If the user has specified a non-black screen background color,
Packit 78deda
       swap the screen background color into color  index  zero  and
Packit 78deda
       move  black into the slot previously occupied by the background
Packit 78deda
       color. */
Packit 78deda
Packit 78deda
    if (bgcol > 0) {
Packit 78deda
        acadcol[0][0] = acadcol[bgcol][0];
Packit 78deda
        acadcol[0][1] = acadcol[bgcol][1];
Packit 78deda
        acadcol[0][2] = acadcol[bgcol][2];
Packit 78deda
        acadcol[bgcol][0] = acadcol[bgcol][1] = acadcol[bgcol][2] = 0;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    acadmap = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char));
Packit 78deda
    xsize = polymode ? xdots : (xdots - 1);
Packit 78deda
    ysize = polymode ? ydots : (ydots - 1);
Packit 78deda
    if (dxbmode) {
Packit 78deda
        fwrite(dxbhead, 19, 1, stdout); /* DXB file header */
Packit 78deda
        putchar((char)135);                 /* Number mode */
Packit 78deda
        (void) pm_writelittleshort(stdout, 0);        /* ...short integers */
Packit 78deda
    } else {
Packit 78deda
        fwrite(sldhead, 17, 1, stdout); /* Slide file header */
Packit 78deda
        putchar(86);                  /* Number format indicator */
Packit 78deda
        putchar(2);                   /* File level number */
Packit 78deda
        (void) pm_writelittleshort(stdout, xsize); /* Max X co-ordinate value */
Packit 78deda
        (void) pm_writelittleshort(stdout, ysize); /* Max Y co-ordinate value */
Packit 78deda
                                      /* Aspect ratio indicator */
Packit 78deda
        (void) pm_writelittlelong(
Packit 78deda
            stdout, (long) ((((double) xsize) / ysize) * aspect * 1E7));
Packit 78deda
        (void) pm_writelittleshort(stdout, 2);        /* Polygon fill type */
Packit 78deda
        (void) pm_writelittleshort(stdout, 0x1234);   /* Byte order indicator */
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Now  build  a  mapping  from  the  image's computed color map
Packit 78deda
       indices to the closest representation  of  each  color  within
Packit 78deda
       AutoCAD's color repertoire. */
Packit 78deda
Packit 78deda
    for (i = 0; i < ncolors; i++) {
Packit 78deda
        int best, j;
Packit 78deda
        long dist = 3 * 256 * 256;
Packit 78deda
Packit 78deda
        for (j = 0; j < gamut; j++) {
Packit 78deda
            long dr = red[i] - acadcol[j][0],
Packit 78deda
                 dg = green[i] - acadcol[j][1],
Packit 78deda
                 db = blue[i] - acadcol[j][2];
Packit 78deda
            long tdist = dr * dr + dg * dg + db * db;
Packit 78deda
Packit 78deda
            if (tdist < dist) {
Packit 78deda
                dist = tdist;
Packit 78deda
                best = j;
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        acadmap[i] = best;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Swoop  over the entire map and replace each  RGB pixel with its
Packit 78deda
       closest  AutoCAD  color  representation.   We  do  this  in  a
Packit 78deda
       separate  pass  since it avoids repetitive mapping of pixels as
Packit 78deda
       we examine them for compression. */
Packit 78deda
Packit 78deda
    for (i = 0; i < ydots; i++) {
Packit 78deda
        int x;
Packit 78deda
Packit 78deda
        for (x = 0; x < xdots; x++) {
Packit 78deda
            PPM_ASSIGN(pixels[i][x],
Packit 78deda
                acadmap[ppm_lookupcolor(cht, &pixels[i][x])], 0 ,0);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Output a run-length encoded expression of the pixmap as AutoCAD
Packit 78deda
       vectors. */
Packit 78deda
Packit 78deda
    for (i = 0; i < ydots; i++) {
Packit 78deda
        int x, rx, rpix = -1, nrun = 0;
Packit 78deda
Packit 78deda
        for (x = 0; x < xdots; x++) {
Packit 78deda
            int pix = PPM_GETR(pixels[i][x]);
Packit 78deda
Packit 78deda
            if (pix != rpix) {
Packit 78deda
                if (nrun > 0) {
Packit 78deda
                    if (rpix > 0) {
Packit 78deda
                        outrun(rpix, ydots - 1, i, rx, x - 1);
Packit 78deda
                    }
Packit 78deda
                }
Packit 78deda
                rpix = pix;
Packit 78deda
                rx = x;
Packit 78deda
                nrun = 1;
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        if ((nrun > 0) && (rpix > 0)) {
Packit 78deda
            outrun(rpix, ydots - 1, i, rx, xdots - 1);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    if (dxbmode) {
Packit 78deda
        putchar(0);                   /* DXB end sentinel */
Packit 78deda
    } else {
Packit 78deda
        (void) pm_writelittleshort(stdout, (short) 0xFC00); 
Packit 78deda
          /* End of file marker */
Packit 78deda
    }
Packit 78deda
    pm_freerow((char *) acadmap);
Packit 78deda
}
Packit 78deda
Packit 78deda
/*  Main program.  */
Packit 78deda
Packit 78deda
int main(argc, argv)
Packit 78deda
  int argc;
Packit 78deda
  char* argv[];
Packit 78deda
{
Packit 78deda
    FILE *ifp;
Packit 78deda
    int argn, rows, cols, ncolors, i;
Packit 78deda
    int aspectspec = FALSE;
Packit 78deda
    pixval maxval;
Packit 78deda
    colorhist_vector chv;
Packit 78deda
    unsigned char *Red, *Green, *Blue;
Packit 78deda
    const char * const usage =
Packit 78deda
        "[-poly] [-dxb] [-white] [-background ]\n\
Packit 78deda
                  [-aspect <f>] [-8] [ppmfile]";
Packit 78deda
Packit 78deda
Packit 78deda
    ppm_init(&argc, argv);
Packit 78deda
Packit 78deda
    argn = 1;
Packit 78deda
    while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != EOS) {
Packit 78deda
        if (pm_keymatch(argv[argn], "-dxb", 2)) {
Packit 78deda
            dxbmode = polymode = TRUE;
Packit 78deda
        } else if (pm_keymatch(argv[argn], "-poly", 2)) {
Packit 78deda
            polymode = TRUE;
Packit 78deda
        } else if (pm_keymatch(argv[argn], "-white", 2)) {
Packit 78deda
            if (bgcol >= 0) {
Packit 78deda
                pm_error("already specified a background color");
Packit 78deda
            }
Packit 78deda
            bgcol = 7;
Packit 78deda
        } else if (pm_keymatch(argv[argn], "-background", 2)) {
Packit 78deda
            if (bgcol >= 0) {
Packit 78deda
                pm_error("already specified a background color");
Packit 78deda
            }
Packit 78deda
            argn++;
Packit 78deda
            if ((argn == argc) || (sscanf(argv[argn], "%d", &bgcol) != 1))
Packit 78deda
                pm_usage(usage);
Packit 78deda
            if (bgcol < 0) {
Packit 78deda
                pm_error("background color must be >= 0 and <= 255");
Packit 78deda
            }
Packit 78deda
        } else if (pm_keymatch(argv[argn], "-aspect", 2)) {
Packit 78deda
            if (aspectspec) {
Packit 78deda
                pm_error("already specified an aspect ratio");
Packit 78deda
            }
Packit 78deda
            argn++;
Packit 78deda
            if ((argn == argc) || (sscanf(argv[argn], "%lf", &aspect) != 1))
Packit 78deda
                pm_usage(usage);
Packit 78deda
            if (aspect <= 0.0) {
Packit 78deda
                pm_error("aspect ratio must be greater than 0");
Packit 78deda
            }
Packit 78deda
            aspectspec = TRUE;
Packit 78deda
        } else if (pm_keymatch(argv[argn], "-8", 2)) {
Packit 78deda
            gamut = 8;
Packit 78deda
        } else {
Packit 78deda
            pm_usage(usage);
Packit 78deda
        }
Packit 78deda
        argn++;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (argn < argc) {
Packit 78deda
        ifp = pm_openr(argv[argn]);
Packit 78deda
        argn++;
Packit 78deda
    } else {
Packit 78deda
        ifp = stdin;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (argn != argc) {
Packit 78deda
        pm_usage(usage);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
Packit 78deda
Packit 78deda
    pm_close(ifp);
Packit 78deda
Packit 78deda
    /* Figure out the colormap.  Logic for squeezing depth to limit the
Packit 78deda
       number of colors in the image was swiped from ppmquant.c. */
Packit 78deda
Packit 78deda
    while (TRUE) {
Packit 78deda
        int row, col;
Packit 78deda
        pixval newmaxval;
Packit 78deda
        pixel *pP;
Packit 78deda
Packit 78deda
        pm_message("computing colormap...");
Packit 78deda
        chv = ppm_computecolorhist(pixels, cols, rows, MAXHIST, &ncolors);
Packit 78deda
        if (chv != (colorhist_vector) 0)
Packit 78deda
            break;
Packit 78deda
        newmaxval = maxval / 2;
Packit 78deda
        pm_message(
Packit 78deda
        "scaling colors from maxval=%d to maxval=%d to improve clustering...",
Packit 78deda
                   maxval, newmaxval );
Packit 78deda
        for (row = 0; row < rows; ++row) {
Packit 78deda
            for (col = 0, pP = pixels[row]; col < cols; ++col, ++pP) {
Packit 78deda
                PPM_DEPTH(*pP, *pP, maxval, newmaxval);
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        maxval = newmaxval;
Packit 78deda
    }
Packit 78deda
    pm_message("%d colors found", ncolors);
Packit 78deda
Packit 78deda
    /* Scale the color map derived for the PPM file into one compatible
Packit 78deda
       with AutoCAD's convention of 8 bit intensities. */
Packit 78deda
Packit 78deda
    if (maxval != 255) {
Packit 78deda
        pm_message("maxval is not 255 - automatically rescaling colors");
Packit 78deda
    }
Packit 78deda
    Red = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char));
Packit 78deda
    Green = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char));
Packit 78deda
    Blue = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char));
Packit 78deda
Packit 78deda
    for (i = 0; i < ncolors; ++i) {
Packit 78deda
        if ( maxval == 255 ) {
Packit 78deda
            Red[i] = PPM_GETR(chv[i].color);
Packit 78deda
            Green[i] = PPM_GETG(chv[i].color);
Packit 78deda
            Blue[i] = PPM_GETB( chv[i].color );
Packit 78deda
        } else {
Packit 78deda
            Red[i] = ((int) PPM_GETR(chv[i].color) * 255) / maxval;
Packit 78deda
            Green[i] = ((int) PPM_GETG(chv[i].color) * 255) / maxval;
Packit 78deda
            Blue[i] = ((int) PPM_GETB(chv[i].color) * 255) / maxval;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* And make a hash table for fast lookup. */
Packit 78deda
Packit 78deda
    cht = ppm_colorhisttocolorhash(chv, ncolors);
Packit 78deda
    ppm_freecolorhist(chv);
Packit 78deda
Packit 78deda
    slideout(cols, rows, ncolors, Red, Green, Blue);
Packit 78deda
    pm_freerow((char *) Red);
Packit 78deda
    pm_freerow((char *) Green);
Packit 78deda
    pm_freerow((char *) Blue);
Packit 78deda
    exit(0);
Packit 78deda
}