/* Copyright (C) 1995, 1996 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevstc2.c,v 1.2.6.1.2.1 2003/01/17 00:49:01 giles Exp $*/ /* Epson Stylus-Color Printer-Driver */ /*** This file holds two implementations of the Floyd-Steinberg error diffusion-algorithm. This algorithms are intended for high quality printing in conjunction with the PostScript-Header stcolor.ps: gs -sDEVICE=stcolor stcolor.ps ... Most prominent option is -sDithering=xxx, to select the algorithm: fsmono - monochrome Floyd-Steinberg fsrgb - 3-Component Floyd-Steinberg fsx4 - 4-Component Floyd-Steinberg (Bad results) fscmyk - Modified 4-Component Floyd-Steinberg (Algorithmically identical with hscmyk, but slower) ***/ #include "gdevstc.h" #include /* for rand */ /* Both algorithms require an error-buffer of 3 + 3*num_components +1*scan long-items. and must consequently set up to work with longs. It is just a Floyd-Steinberg-algorithm applied to each component. */ /* * Due to the -selfdefined- ugly coding of the output-data, we need * some conversion. But since this includes the black-separation, I * did not change the definition. * * This algorithm stores the 1st component in the LSB, thus it * reverts the order used by the basic driver. */ static const byte grayvals[2] = { 0, BLACK }; static const byte rgbvals[8] = { 0, RED, GREEN, RED|GREEN, BLUE, BLUE|RED, BLUE|GREEN, BLUE|RED|GREEN}; static const byte cmykvals[16] = { 0, CYAN,MAGENTA,CYAN|MAGENTA,YELLOW,YELLOW|CYAN,YELLOW|MAGENTA,BLACK, BLACK,BLACK, BLACK, BLACK, BLACK, BLACK, BLACK,BLACK}; static const byte *const pixelconversion[5] = { NULL, grayvals, NULL, rgbvals, cmykvals}; int stc_fs(stcolor_device *sdev,int npixel,byte *bin,byte *bbuf,byte *out) { long *in = (long *) bin; long *buf = (long *) bbuf; /* ============================================================= */ if(npixel > 0) { /* npixel > 0 -> scanline-processing */ /* ============================================================= */ int bstep,pstart,pstop,pstep,p; long spotsize,threshold,*errc,*errv; const byte *pixel2stc; if(buf[0] >= 0) { /* run forward */ buf[0] = -1; bstep = 1; pstep = sdev->color_info.num_components; pstart = 0; pstop = npixel * pstep; } else { /* run backward */ buf[0] = 1; bstep = -1; pstep = -sdev->color_info.num_components; pstop = pstep; pstart = (1-npixel) * pstep; out += npixel-1; } /* forward / backward */ /* --------------------------------------------------------------------- */ if(in == NULL) return 0; /* almost ignore the 'white calls' */ /* --------------------------------------------------------------------- */ spotsize = buf[1]; threshold = buf[2]; errc = buf+3; errv = errc + 2*sdev->color_info.num_components; pixel2stc = pixelconversion[sdev->color_info.num_components]; for(p = pstart; p != pstop; p += pstep) { /* loop over pixels */ int c; /* component-number */ int pixel; /* internal pxel-value */ pixel = 0; for(c = 0; c < sdev->color_info.num_components; c++) { /* comp */ long cv; /* component value */ cv = in[p+c] + errv[p+c] + errc[c] - ((errc[c]+4)>>3); if(cv > threshold) { pixel |= 1<>4); /* 3/16 */ errv[p+c ] = ((5*cv )>>4) /* 5/16 */ + ((errc[c]+4)>>3); /* 1/16 (rest) */ errc[c] = cv /* 8/16 (neu) */ - ((5*cv )>>4) - ((3*cv+8)>>4); } /* comp */ *out = pixel2stc[pixel]; out += bstep; } /* loop over pixels */ /* ============================================================= */ } else { /* npixel <= 0 -> initialisation */ /* ============================================================= */ int i,i2do; long rand_max; double offset,scale; /* * check wether the number of components is valid */ if((sdev->color_info.num_components < 0) || (sdev->color_info.num_components >= countof(pixelconversion)) || (pixelconversion[sdev->color_info.num_components] == NULL)) return -1; /* * check wether stcdither & TYPE are correct */ if(( sdev->stc.dither == NULL) || ((sdev->stc.dither->flags & STC_TYPE) != STC_LONG)) return -2; /* * check wether the buffer-size is sufficiently large */ if(((sdev->stc.dither->flags/STC_SCAN) < 1) || ( sdev->stc.dither->bufadd < (3 + 3*sdev->color_info.num_components))) return -3; /* * must neither have STC_DIRECT nor STC_WHITE */ if(sdev->stc.dither->flags & (STC_DIRECT | STC_WHITE)) return -4; /* * compute initial values */ /* -- direction */ buf[0] = 1; /* -- "spotsize" */ scale = sdev->stc.dither->minmax[1]; buf[1] = scale + (scale > 0.0 ? 0.5 : -0.5); /* -- "threshold" */ offset = sdev->stc.dither->minmax[0]; scale -= offset; if((offset+0.5*scale) > 0.0) buf[2] = offset + 0.5*scale + 0.5; else buf[2] = offset + 0.5*scale - 0.5; /* * random values, that do not exceed half of normal value */ i2do = sdev->color_info.num_components * (3-npixel); rand_max = 0; if(sdev->stc.flags & STCDFLAG0) { for(i = 0; i < i2do; ++i) buf[i+3] = 0; } else { for(i = 0; i < i2do; ++i) { buf[i+3] = rand(); if(buf[i+3] > rand_max) rand_max = buf[i+3]; } scale = (double) buf[1] / (double) rand_max; for(i = 0; i < sdev->color_info.num_components; ++ i) buf[i+3] = 0.25000*scale*(buf[i+3]-rand_max/2); for( ; i < i2do; ++i) /* includes 2 additional pixels ! */ buf[i+3] = 0.28125*scale*(buf[i+3]-rand_max/2); } /* ============================================================= */ } /* scanline-processing or initialisation */ /* ============================================================= */ return 0; } /* * Experimental CMYK-Algorithm */ int stc_fscmyk(stcolor_device *sdev,int npixel,byte *bin,byte *bbuf,byte *out) { long *in = (long *) bin; long *buf = (long *) bbuf; /* ============================================================= */ if(npixel > 0) { /* npixel > 0 -> scanline-processing */ /* ============================================================= */ int bstep,pstart,pstop,pstep,p; long spotsize,threshold,*errc,*errv; if(buf[0] >= 0) { /* run forward */ buf[0] = -1; bstep = 1; pstep = 4; pstart = 0; pstop = npixel * pstep; } else { /* run backward */ buf[0] = 1; bstep = -1; pstep = -4; pstop = pstep; pstart = (1-npixel) * pstep; out += npixel-1; } /* forward / backward */ spotsize = buf[1]; threshold = buf[2]; errc = buf+3; errv = errc + 2*4; for(p = 0; p < 4; ++p) errc[p] = 0; for(p = pstart; p != pstop; p += pstep) { /* loop over pixels */ int c; /* component-number */ int pixel; /* internal pxel-value */ long cv,k; /* * Black is treated first, with conventional Floyd-Steinberg */ k = in[p+3]; cv = k + errv[p+3] + errc[3] - ((errc[3]+4)>>3); if(cv > threshold) { pixel = BLACK; cv -= spotsize; } else { pixel = 0; } errv[p+3-pstep] += ((3*cv+8)>>4); /* 3/16 */ errv[p+3 ] = ((5*cv )>>4) /* 5/16 */ + ((errc[3]+4)>>3); /* 1/16 (rest) */ errc[3] = cv /* 8/16 (neu) */ - ((5*cv )>>4) - ((3*cv+8)>>4); /* * color-handling changes with black fired or not */ if(pixel) { /* -------- firing of black causes all colors to fire too */ for(c = 0; c < 3; ++c) { cv = in[p+c] > k ? in[p+c] : k; cv += errv[p+c] + errc[c] - ((errc[c]+4)>>3)-spotsize; if(cv <= (threshold-spotsize)) cv = threshold-spotsize+1; errv[p+c-pstep] += ((3*cv+8)>>4); /* 3/16 */ errv[p+c ] = ((5*cv )>>4) /* 5/16 */ + ((errc[c]+4)>>3); /* 1/16 (rest) */ errc[c] = cv /* 8/16 (neu) */ - ((5*cv )>>4) - ((3*cv+8)>>4); } } else { /* -------- if black did not fire, only colors w. larger values may fire */ for(c = 0; c < 3; ++c) { cv = in[p+c]; if(cv > k) { /* May Fire */ cv += errv[p+c] + errc[c] - ((errc[c]+4)>>3); if(cv > threshold) { cv -= spotsize; pixel |= CYAN>>c; } } else { /* Must not fire */ cv = k + errv[p+c] + errc[c] - ((errc[c]+4)>>3); if(cv > threshold ) cv = threshold; } errv[p+c-pstep] += ((3*cv+8)>>4); /* 3/16 */ errv[p+c ] = ((5*cv )>>4) /* 5/16 */ + ((errc[c]+4)>>3); /* 1/16 (rest) */ errc[c] = cv /* 8/16 (neu) */ - ((5*cv )>>4) - ((3*cv+8)>>4); } } *out = pixel; out += bstep; } /* loop over pixels */ /* ============================================================= */ } else { /* npixel <= 0 -> initialisation */ /* ============================================================= */ int i,i2do; long rand_max; double offset,scale; /* * check wether the number of components is valid */ if(sdev->color_info.num_components != 4) return -1; /* * check wether stcdither & TYPE are correct */ if(( sdev->stc.dither == NULL) || ((sdev->stc.dither->flags & STC_TYPE) != STC_LONG)) return -2; /* * check wether the buffer-size is sufficiently large */ if(((sdev->stc.dither->flags/STC_SCAN) < 1) || ( sdev->stc.dither->bufadd < (3 + 3*sdev->color_info.num_components))) return -3; /* * must neither have STC_DIRECT nor STC_WHITE */ if(sdev->stc.dither->flags & (STC_DIRECT | STC_WHITE)) return -4; /* * compute initial values */ /* -- direction */ buf[0] = 1; /* -- "spotsize" */ scale = sdev->stc.dither->minmax[1]; buf[1] = scale + (scale > 0.0 ? 0.5 : -0.5); /* -- "threshold" */ offset = sdev->stc.dither->minmax[0]; scale -= offset; if(sdev->stc.flags & STCDFLAG1) { buf[2] = (sdev->stc.extv[0][sdev->stc.sizv[0]-1] - sdev->stc.extv[0][0]) * scale / 2.0 + offset; } else { if((offset+0.5*scale) > 0.0) buf[2] = offset + 0.5*scale + 0.5; else buf[2] = offset + 0.5*scale - 0.5; } /* * random values, that do not exceed half of normal value */ i2do = sdev->color_info.num_components * (3-npixel); rand_max = 0; if(sdev->stc.flags & STCDFLAG0) { for(i = 0; i < i2do; ++i) buf[i+3] = 0; } else { for(i = 0; i < i2do; ++i) { buf[i+3] = rand(); if(buf[i+3] > rand_max) rand_max = buf[i+3]; } scale = (double) buf[1] / (double) rand_max; for(i = 0; i < sdev->color_info.num_components; ++ i) buf[i+3] = 0.25000*scale*(buf[i+3]-rand_max/2); for( ; i < i2do; ++i) /* includes 2 additional pixels ! */ buf[i+3] = 0.28125*scale*(buf[i+3]-rand_max/2); } /* ============================================================= */ } /* scanline-processing or initialisation */ /* ============================================================= */ return 0; }