home *** CD-ROM | disk | FTP | other *** search
- /* ppmdither.c - Ordered dithering of a color ppm file to a specified number
- ** of primary shades.
- **
- ** Copyright (C) 1991 by Christos Zoulas.
- **
- ** 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"
-
- #define NC 256 /* Total number of colors */
- #define NS 256 /* Max number of shades in primary */
-
- typedef unsigned char ubyte;
-
- static int dith_nr = 5; /* number of red shades */
- static int dith_ng = 9; /* number of green shades */
- static int dith_nb = 5; /* number of blue shades */
- static int dith_nc = 225; /* total number of colors 5 x 9 x 5 */
- static int dith_dim = 4; /* dimension of the dither matrix */
- static int dith_dm2 = 16; /* dim square */
- static int **dith_mat; /* the dithering matrix */
-
- /* prototypes */
- static int dith_value ARGS((int y, int x, int size));
- static void dith_matrix ARGS((int dim));
- void dith_setup ARGS((int dim, int nr, int ng, int nb, pixel *ptab));
- int dith_color ARGS((float r, float g, float b));
- void dith_dither ARGS((int w, int h, register pixel *t, register pixel *i, register pixel *o));
- /* COLOR():
- * returns the index in the color table for the
- * r, g, b values specified.
- */
- #define COLOR(r,g,b) (((r) * dith_ng + (g)) * dith_nb + (b))
-
- /* LEVELS():
- * Returns the total number of levels after dithering.
- */
- #define LEVELS(s) (dith_dm2 * ((s) - 1) + 1)
-
- /* DITHER():
- * Returns the dithered color for a single primary.
- * p = the input pixel
- * d = entry in the dither matrix
- * s = the number of levels of the primary
- *
- */
- #define DITHER(p,d,s) ((ubyte) ((LEVELS(s) * (p) + (d)) / (dith_dm2 * NS)))
-
-
- /* dith_value():
- * Return the value of a dither matrix of size x size at x, y
- * [graphics gems, p. 714]
- */
- static int
- dith_value(y, x, size)
- int y, x, size;
- {
- register int d;
-
- /*
- * Think of d as the density. At every iteration, d is shifted
- * left one and a new bit is put in the low bit based on x and y.
- * If x is odd and y is even, or visa versa, then a bit is shifted in.
- * This generates the checkerboard pattern seen in dithering.
- * This quantity is shifted again and the low bit of y is added in.
- * This whole thing interleaves a checkerboard pattern and y's bits
- * which is what you want.
- */
- for (d = 0; size-- > 0; x >>= 1, y >>= 1)
- d = (d << 2) | (((x & 1) ^ (y & 1)) << 1) | (y & 1);
- return(d);
- } /* end dith_value */
-
-
- /* dith_matrix():
- * Form the dithering matrix for the dimension specified
- * (Scaled by NS)
- */
- static void
- dith_matrix(dim)
- int dim;
- {
- int x, y, *dat;
-
- dith_dim = (1 << dim);
- dith_dm2 = dith_dim * dith_dim;
-
- dith_mat = (int **) malloc((dith_dim * sizeof(int *)) + /* pointers */
- (dith_dm2 * sizeof(int))); /* data */
-
- if (dith_mat == NULL)
- pm_error("out of memory");
-
- dat = (int *) &dith_mat[dith_dim];
- for (y = 0; y < dith_dim; y++)
- dith_mat[y] = &dat[y * dith_dim];
-
- for (y = 0; y < dith_dim; y++) {
- for (x = 0; x < dith_dim; x++) {
- dith_mat[y][x] = NS * dith_value(y, x, dim);
- #ifdef DEBUG
- (void) fprintf(stderr, "%4d ", dith_mat[y][x]);
- #endif
- }
- #ifdef DEBUG
- (void) fprintf(stderr, "\n");
- #endif
- }
- } /* end dith_matrix */
-
-
- /* dith_setup():
- * Setup the dithering parameters, lookup table and dithering matrix
- */
- void
- dith_setup(dim, nr, ng, nb, ptab)
- int dim, nr, ng, nb;
- pixel *ptab;
- {
- register int r, g, b, i;
-
- dith_nr = nr;
- dith_ng = ng;
- dith_nb = nb;
- dith_nc = nr * ng * nb;
-
- if (dith_nc > NC)
- pm_error("too many shades %d, max %d", dith_nc, NC);
- if (dith_nr < 2)
- pm_error("too few shades for red, minimum of 2");
- if (dith_ng < 2)
- pm_error("too few shades for green, minimum of 2");
- if (dith_nb < 2)
- pm_error("too few shades for blue, minimum of 2");
-
- for (r = 0; r < dith_nr; r++)
- for (g = 0; g < dith_ng; g++)
- for (b = 0; b < dith_nb; b++) {
- i = COLOR(r,g,b);
- PPM_ASSIGN(ptab[COLOR(r,g,b)],
- (r * (NC-1) / (dith_nr - 1)),
- (g * (NC-1) / (dith_ng - 1)),
- (b * (NC-1) / (dith_nb - 1)));
- }
-
- dith_matrix(dim);
- } /* end dith_setup */
-
-
- /* dith_color():
- * Return the closest color index for the one we ask
- */
- int
- dith_color(r, g, b)
- float r, g, b;
- {
- int rr, gg, bb;
-
- rr = r * (dith_nr - 1);
- gg = g * (dith_ng - 1);
- bb = b * (dith_nb - 1);
- return((int) COLOR(rr, gg, bb));
- } /* end dith_color */
-
-
- /* dith_dither():
- * Dither height scanlines at a time
- */
- void
- dith_dither(w, h, t, i, o)
- int w, h;
- register pixel *t;
- register pixel *i;
- register pixel *o;
- {
- int y, dm = (dith_dim - 1);
- register int x, d;
- register int *m;
-
- for (y = 0; y < h; y++)
- for (m = dith_mat[y & dm], x = w; --x >= 0;i++) {
- d = m[x & dm];
- *o++ = t[COLOR(DITHER(PPM_GETR(*i), d, dith_nr),
- DITHER(PPM_GETG(*i), d, dith_ng),
- DITHER(PPM_GETB(*i), d, dith_nb))];
- }
- } /* end dith_dither */
-
-
- int
- main( argc, argv )
- int argc;
- char* argv[];
- {
- FILE* ifp;
- pixel ptab[256];
- pixel **ipixels, **opixels;
- int cols, rows;
- pixval maxval;
- int argn;
- char* usage =
- "[-dim <num>] [-red <num>] [-green <num>] [-blue <num>] [pbmfile]";
-
-
- ppm_init( &argc, argv );
-
- argn = 1;
-
- while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
- {
- if ( pm_keymatch( argv[argn], "-dim", 1) && argn + 1 < argc ) {
- argn++;
- if (sscanf(argv[argn], "%d", &dith_dim) != 1)
- pm_usage( usage );
- }
- else if ( pm_keymatch( argv[argn], "-red", 1 ) && argn + 1 < argc ) {
- argn++;
- if (sscanf(argv[argn], "%d", &dith_nr) != 1)
- pm_usage( usage );
- }
- else if ( pm_keymatch( argv[argn], "-green", 1 ) && argn + 1 < argc ) {
- argn++;
- if (sscanf(argv[argn], "%d", &dith_ng) != 1)
- pm_usage( usage );
- }
- else if ( pm_keymatch( argv[argn], "-blue", 1 ) && argn + 1 < argc ) {
- argn++;
- if (sscanf(argv[argn], "%d", &dith_nb) != 1)
- pm_usage( usage );
- }
- else
- pm_usage( usage );
- ++argn;
- }
-
- if ( argn != argc )
- {
- ifp = pm_openr( argv[argn] );
- ++argn;
- }
- else
- ifp = stdin;
-
- if ( argn != argc )
- pm_usage( usage );
-
- ipixels = ppm_readppm( ifp, &cols, &rows, &maxval );
- pm_close( ifp );
- opixels = ppm_allocarray(cols, rows);
- maxval = 255;
- dith_setup(dith_dim, dith_nr, dith_ng, dith_nb, ptab);
- dith_dither(cols, rows, ptab, &ipixels[0][0], &opixels[0][0]);
- ppm_writeppm(stdout, opixels, cols, rows, maxval, 0);
- pm_close(stdout);
- exit(0);
- }
-