/* These functions were taken from Ingo Wilken's ilbm package by Bryan Henderson on 01.03.10. Because ppmtoilbm and ilbmtoppm are the only programs that will use these in the foreseeable future, they remain lightly documented and tested. But they look like they would be useful in other Netpbm programs that do Floyd-Steinberg. */ /* libfloyd.c - generic Floyd-Steinberg error distribution routines for PBMPlus ** ** Copyright (C) 1994 Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) ** ** 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" #include "ppmfloyd.h" #include "mallocvar.h" static void fs_adjust ARGS((ppm_fs_info *fi, int col)); static ppm_fs_info * allocateFi(int const cols) { ppm_fs_info * fi; MALLOCVAR(fi); if (fi != NULL) { MALLOCARRAY(fi->thisrederr , cols + 2); MALLOCARRAY(fi->thisgreenerr, cols + 2); MALLOCARRAY(fi->thisblueerr , cols + 2); MALLOCARRAY(fi->nextrederr , cols + 2); MALLOCARRAY(fi->nextgreenerr, cols + 2); MALLOCARRAY(fi->nextblueerr , cols + 2); if (fi->thisrederr == NULL || fi->thisgreenerr == NULL || fi->thisblueerr == NULL || fi->nextrederr == NULL || fi->nextgreenerr == NULL || fi->nextblueerr == NULL) pm_error("out of memory allocating " "Floyd-Steinberg control structure"); } else pm_error("out of memory allocating Floyd-Steinberg control structure"); return(fi); } ppm_fs_info * ppm_fs_init(int cols, pixval maxval, int flags) { ppm_fs_info *fi; fi = allocateFi(cols); fi->lefttoright = 1; fi->cols = cols; fi->maxval = maxval; fi->flags = flags; if( flags & FS_RANDOMINIT ) { unsigned int i; srand((int)(time(0) ^ getpid())); for( i = 0; i < cols +2; i++ ) { /* random errors in [-1..+1] */ fi->thisrederr[i] = rand() % 32 - 16; fi->thisgreenerr[i] = rand() % 32 - 16; fi->thisblueerr[i] = rand() % 32 - 16; } } else { unsigned int i; for( i = 0; i < cols + 2; i++ ) fi->thisrederr[i] = fi->thisgreenerr[i] = fi->thisblueerr[i] = 0; } return fi; } void ppm_fs_free(fi) ppm_fs_info *fi; { if( fi ) { free(fi->thisrederr); free(fi->thisgreenerr); free(fi->thisblueerr); free(fi->nextrederr); free(fi->nextgreenerr); free(fi->nextblueerr); free(fi); } } int ppm_fs_startrow(fi, pixrow) ppm_fs_info *fi; pixel *pixrow; { register int col; if( !fi ) return 0; fi->pixrow = pixrow; for( col = 0; col < fi->cols + 2; col++ ) fi->nextrederr[col] = fi->nextgreenerr[col] = fi->nextblueerr[col] = 0; if( fi->lefttoright ) { fi->col_end = fi->cols; col = 0; } else { fi->col_end = -1; col = fi->cols - 1; } fs_adjust(fi, col); return col; } int ppm_fs_next(fi, col) ppm_fs_info *fi; int col; { if( !fi ) ++col; else { if( fi->lefttoright ) ++col; else --col; if( col == fi->col_end ) col = fi->cols; else fs_adjust(fi, col); } return col; } void ppm_fs_endrow(fi) ppm_fs_info *fi; { long *tmp; if( fi ) { tmp = fi->thisrederr; fi->thisrederr = fi->nextrederr; fi->nextrederr = tmp; tmp = fi->thisgreenerr; fi->thisgreenerr = fi->nextgreenerr; fi->nextgreenerr = tmp; tmp = fi->thisblueerr; fi->thisblueerr = fi->nextblueerr; fi->nextblueerr = tmp; if( fi->flags & FS_ALTERNATE ) fi->lefttoright = !(fi->lefttoright); } } void ppm_fs_update(fi, col, pP) ppm_fs_info *fi; int col; pixel *pP; { if( fi ) ppm_fs_update3(fi, col, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP)); } void ppm_fs_update3(ppm_fs_info *fi, int col, pixval r, pixval g, pixval b) { register long rerr, gerr, berr, err; int errcol = col+1; if( !fi ) return; rerr = (long)(fi->red) - r; gerr = (long)(fi->green) - g; berr = (long)(fi->blue) - b; if( fi->lefttoright ) { register long two_err; two_err = 2*rerr; err = rerr; fi->nextrederr[errcol+1] += err; /* 1/16 */ err += two_err; fi->nextrederr[errcol-1] += err; /* 3/16 */ err += two_err; fi->nextrederr[errcol ] += err; /* 5/16 */ err += two_err; fi->thisrederr[errcol+1] += err; /* 7/16 */ two_err = 2*gerr; err = gerr; fi->nextgreenerr[errcol+1] += err; /* 1/16 */ err += two_err; fi->nextgreenerr[errcol-1] += err; /* 3/16 */ err += two_err; fi->nextgreenerr[errcol ] += err; /* 5/16 */ err += two_err; fi->thisgreenerr[errcol+1] += err; /* 7/16 */ two_err = 2*berr; err = berr; fi->nextblueerr[errcol+1] += err; /* 1/16 */ err += two_err; fi->nextblueerr[errcol-1] += err; /* 3/16 */ err += two_err; fi->nextblueerr[errcol ] += err; /* 5/16 */ err += two_err; fi->thisblueerr[errcol+1] += err; /* 7/16 */ } else { register long two_err; two_err = 2*rerr; err = rerr; fi->nextrederr[errcol-1] += err; /* 1/16 */ err += two_err; fi->nextrederr[errcol+1] += err; /* 3/16 */ err += two_err; fi->nextrederr[errcol ] += err; /* 5/16 */ err += two_err; fi->thisrederr[errcol-1] += err; /* 7/16 */ two_err = 2*gerr; err = gerr; fi->nextgreenerr[errcol-1] += err; /* 1/16 */ err += two_err; fi->nextgreenerr[errcol+1] += err; /* 3/16 */ err += two_err; fi->nextgreenerr[errcol ] += err; /* 5/16 */ err += two_err; fi->thisgreenerr[errcol-1] += err; /* 7/16 */ two_err = 2*berr; err = berr; fi->nextblueerr[errcol-1] += err; /* 1/16 */ err += two_err; fi->nextblueerr[errcol+1] += err; /* 3/16 */ err += two_err; fi->nextblueerr[errcol ] += err; /* 5/16 */ err += two_err; fi->thisblueerr[errcol-1] += err; /* 7/16 */ } } static void fs_adjust(fi, col) ppm_fs_info *fi; int col; { register long r, g, b; register pixel *pP; int errcol = col+1; pixval maxval = fi->maxval; pP = &(fi->pixrow[col]); /* Use Floyd-Steinberg errors to adjust actual color. */ r = fi->thisrederr [errcol]; if( r < 0 ) r -= 8; else r += 8; r /= 16; g = fi->thisgreenerr[errcol]; if( g < 0 ) g -= 8; else g += 8; g /= 16; b = fi->thisblueerr [errcol]; if( b < 0 ) b -= 8; else b += 8; b /= 16; r += PPM_GETR(*pP); if ( r < 0 ) r = 0; else if ( r > maxval ) r = maxval; g += PPM_GETG(*pP); if ( g < 0 ) g = 0; else if ( g > maxval ) g = maxval; b += PPM_GETB(*pP); if ( b < 0 ) b = 0; else if ( b > maxval ) b = maxval; PPM_ASSIGN(*pP, r, g, b); fi->red = r; fi->green = g; fi->blue = b; }