xref: /plan9/sys/src/cmd/gs/src/gdevpsds.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
13ff48bf5SDavid du Colombier /* Copyright (C) 1997, 2000 Aladdin Enterprises.  All rights reserved.
27dd7cddfSDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
57dd7cddfSDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: gdevpsds.c,v 1.14 2005/02/26 18:07:43 igor Exp $ */
187dd7cddfSDavid du Colombier /* Image processing streams for PostScript and PDF writers */
197dd7cddfSDavid du Colombier #include "gx.h"
207dd7cddfSDavid du Colombier #include "memory_.h"
217dd7cddfSDavid du Colombier #include "gserrors.h"
227dd7cddfSDavid du Colombier #include "gxdcconv.h"
237dd7cddfSDavid du Colombier #include "gdevpsds.h"
24*593dc095SDavid du Colombier #include "gxbitmap.h"
25*593dc095SDavid du Colombier #include "gxcspace.h"
26*593dc095SDavid du Colombier #include "gsdcolor.h"
27*593dc095SDavid du Colombier #include "gscspace.h"
28*593dc095SDavid du Colombier #include "gxdevcli.h"
297dd7cddfSDavid du Colombier 
307dd7cddfSDavid du Colombier /* ---------------- Convert between 1/2/4/12 and 8 bits ---------------- */
317dd7cddfSDavid du Colombier 
327dd7cddfSDavid du Colombier gs_private_st_simple(st_1248_state, stream_1248_state, "stream_1248_state");
337dd7cddfSDavid du Colombier 
343ff48bf5SDavid du Colombier /* Initialize an expansion or reduction stream. */
353ff48bf5SDavid du Colombier int
s_1248_init(stream_1248_state * ss,int Columns,int samples_per_pixel)363ff48bf5SDavid du Colombier s_1248_init(stream_1248_state *ss, int Columns, int samples_per_pixel)
373ff48bf5SDavid du Colombier {
383ff48bf5SDavid du Colombier     ss->samples_per_row = Columns * samples_per_pixel;
393ff48bf5SDavid du Colombier     return ss->template->init((stream_state *)ss);
403ff48bf5SDavid du Colombier }
413ff48bf5SDavid du Colombier 
427dd7cddfSDavid du Colombier /* Initialize the state. */
437dd7cddfSDavid du Colombier private int
s_1_init(stream_state * st)447dd7cddfSDavid du Colombier s_1_init(stream_state * st)
457dd7cddfSDavid du Colombier {
467dd7cddfSDavid du Colombier     stream_1248_state *const ss = (stream_1248_state *) st;
477dd7cddfSDavid du Colombier 
487dd7cddfSDavid du Colombier     ss->left = ss->samples_per_row;
497dd7cddfSDavid du Colombier     ss->bits_per_sample = 1;
507dd7cddfSDavid du Colombier     return 0;
517dd7cddfSDavid du Colombier }
527dd7cddfSDavid du Colombier private int
s_2_init(stream_state * st)537dd7cddfSDavid du Colombier s_2_init(stream_state * st)
547dd7cddfSDavid du Colombier {
557dd7cddfSDavid du Colombier     stream_1248_state *const ss = (stream_1248_state *) st;
567dd7cddfSDavid du Colombier 
577dd7cddfSDavid du Colombier     ss->left = ss->samples_per_row;
587dd7cddfSDavid du Colombier     ss->bits_per_sample = 2;
597dd7cddfSDavid du Colombier     return 0;
607dd7cddfSDavid du Colombier }
617dd7cddfSDavid du Colombier private int
s_4_init(stream_state * st)627dd7cddfSDavid du Colombier s_4_init(stream_state * st)
637dd7cddfSDavid du Colombier {
647dd7cddfSDavid du Colombier     stream_1248_state *const ss = (stream_1248_state *) st;
657dd7cddfSDavid du Colombier 
667dd7cddfSDavid du Colombier     ss->left = ss->samples_per_row;
677dd7cddfSDavid du Colombier     ss->bits_per_sample = 4;
687dd7cddfSDavid du Colombier     return 0;
697dd7cddfSDavid du Colombier }
707dd7cddfSDavid du Colombier private int
s_12_init(stream_state * st)717dd7cddfSDavid du Colombier s_12_init(stream_state * st)
727dd7cddfSDavid du Colombier {
737dd7cddfSDavid du Colombier     stream_1248_state *const ss = (stream_1248_state *) st;
747dd7cddfSDavid du Colombier 
757dd7cddfSDavid du Colombier     ss->left = ss->samples_per_row;
767dd7cddfSDavid du Colombier     ss->bits_per_sample = 12;	/* not needed */
777dd7cddfSDavid du Colombier     return 0;
787dd7cddfSDavid du Colombier }
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier /* Process one buffer. */
817dd7cddfSDavid du Colombier #define BEGIN_1248\
827dd7cddfSDavid du Colombier 	stream_1248_state * const ss = (stream_1248_state *)st;\
837dd7cddfSDavid du Colombier 	const byte *p = pr->ptr;\
847dd7cddfSDavid du Colombier 	const byte *rlimit = pr->limit;\
857dd7cddfSDavid du Colombier 	byte *q = pw->ptr;\
867dd7cddfSDavid du Colombier 	byte *wlimit = pw->limit;\
877dd7cddfSDavid du Colombier 	uint left = ss->left;\
887dd7cddfSDavid du Colombier 	int status;\
897dd7cddfSDavid du Colombier 	int n
907dd7cddfSDavid du Colombier #define END_1248\
917dd7cddfSDavid du Colombier 	pr->ptr = p;\
927dd7cddfSDavid du Colombier 	pw->ptr = q;\
937dd7cddfSDavid du Colombier 	ss->left = left;\
947dd7cddfSDavid du Colombier 	return status
957dd7cddfSDavid du Colombier 
967dd7cddfSDavid du Colombier /* N-to-8 expansion */
977dd7cddfSDavid du Colombier #define FOREACH_N_8(in, nout)\
987dd7cddfSDavid du Colombier 	status = 0;\
997dd7cddfSDavid du Colombier 	for ( ; p < rlimit; left -= n, q += n, ++p ) {\
1007dd7cddfSDavid du Colombier 	  byte in = p[1];\
1017dd7cddfSDavid du Colombier 	  n = min(left, nout);\
1027dd7cddfSDavid du Colombier 	  if ( wlimit - q < n ) {\
1037dd7cddfSDavid du Colombier 	    status = 1;\
1047dd7cddfSDavid du Colombier 	    break;\
1057dd7cddfSDavid du Colombier 	  }\
1067dd7cddfSDavid du Colombier 	  switch ( n ) {\
1077dd7cddfSDavid du Colombier 	    case 0: left = ss->samples_per_row; --p; continue;
1087dd7cddfSDavid du Colombier #define END_FOREACH_N_8\
1097dd7cddfSDavid du Colombier 	  }\
1107dd7cddfSDavid du Colombier 	}
1117dd7cddfSDavid du Colombier private int
s_N_8_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)1127dd7cddfSDavid du Colombier s_N_8_process(stream_state * st, stream_cursor_read * pr,
1137dd7cddfSDavid du Colombier 	      stream_cursor_write * pw, bool last)
1147dd7cddfSDavid du Colombier {
1157dd7cddfSDavid du Colombier     BEGIN_1248;
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier     switch (ss->bits_per_sample) {
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 	case 1:{
1207dd7cddfSDavid du Colombier 		FOREACH_N_8(in, 8)
1217dd7cddfSDavid du Colombier 	case 8:
1227dd7cddfSDavid du Colombier 		q[8] = (byte) - (in & 1);
1237dd7cddfSDavid du Colombier 	case 7:
1247dd7cddfSDavid du Colombier 		q[7] = (byte) - ((in >> 1) & 1);
1257dd7cddfSDavid du Colombier 	case 6:
1267dd7cddfSDavid du Colombier 		q[6] = (byte) - ((in >> 2) & 1);
1277dd7cddfSDavid du Colombier 	case 5:
1287dd7cddfSDavid du Colombier 		q[5] = (byte) - ((in >> 3) & 1);
1297dd7cddfSDavid du Colombier 	case 4:
1307dd7cddfSDavid du Colombier 		q[4] = (byte) - ((in >> 4) & 1);
1317dd7cddfSDavid du Colombier 	case 3:
1327dd7cddfSDavid du Colombier 		q[3] = (byte) - ((in >> 5) & 1);
1337dd7cddfSDavid du Colombier 	case 2:
1347dd7cddfSDavid du Colombier 		q[2] = (byte) - ((in >> 6) & 1);
1357dd7cddfSDavid du Colombier 	case 1:
1367dd7cddfSDavid du Colombier 		q[1] = (byte) - (in >> 7);
1377dd7cddfSDavid du Colombier 		END_FOREACH_N_8;
1387dd7cddfSDavid du Colombier 	    }
1397dd7cddfSDavid du Colombier 	    break;
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier 	case 2:{
1427dd7cddfSDavid du Colombier 		static const byte b2[4] =
1437dd7cddfSDavid du Colombier 		{0x00, 0x55, 0xaa, 0xff};
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier 		FOREACH_N_8(in, 4)
1467dd7cddfSDavid du Colombier 	case 4:
1477dd7cddfSDavid du Colombier 		q[4] = b2[in & 3];
1487dd7cddfSDavid du Colombier 	case 3:
1497dd7cddfSDavid du Colombier 		q[3] = b2[(in >> 2) & 3];
1507dd7cddfSDavid du Colombier 	case 2:
1517dd7cddfSDavid du Colombier 		q[2] = b2[(in >> 4) & 3];
1527dd7cddfSDavid du Colombier 	case 1:
1537dd7cddfSDavid du Colombier 		q[1] = b2[in >> 6];
1547dd7cddfSDavid du Colombier 		END_FOREACH_N_8;
1557dd7cddfSDavid du Colombier 	    }
1567dd7cddfSDavid du Colombier 	    break;
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 	case 4:{
1597dd7cddfSDavid du Colombier 		static const byte b4[16] =
1607dd7cddfSDavid du Colombier 		{
1617dd7cddfSDavid du Colombier 		    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
1627dd7cddfSDavid du Colombier 		    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
1637dd7cddfSDavid du Colombier 		};
1647dd7cddfSDavid du Colombier 
1657dd7cddfSDavid du Colombier 		FOREACH_N_8(in, 2)
1667dd7cddfSDavid du Colombier 	case 2:
1677dd7cddfSDavid du Colombier 		q[2] = b4[in & 0xf];
1687dd7cddfSDavid du Colombier 	case 1:
1697dd7cddfSDavid du Colombier 		q[1] = b4[in >> 4];
1707dd7cddfSDavid du Colombier 		END_FOREACH_N_8;
1717dd7cddfSDavid du Colombier 	    }
1727dd7cddfSDavid du Colombier 	    break;
1737dd7cddfSDavid du Colombier 
1747dd7cddfSDavid du Colombier 	default:
1757dd7cddfSDavid du Colombier 	    return ERRC;
1767dd7cddfSDavid du Colombier     }
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier     END_1248;
1797dd7cddfSDavid du Colombier }
1807dd7cddfSDavid du Colombier 
1817dd7cddfSDavid du Colombier /* 12-to-8 "expansion" */
1827dd7cddfSDavid du Colombier private int
s_12_8_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)1837dd7cddfSDavid du Colombier s_12_8_process(stream_state * st, stream_cursor_read * pr,
1847dd7cddfSDavid du Colombier 	       stream_cursor_write * pw, bool last)
1857dd7cddfSDavid du Colombier {
1867dd7cddfSDavid du Colombier     BEGIN_1248;
1877dd7cddfSDavid du Colombier 
1887dd7cddfSDavid du Colombier     n = ss->samples_per_row;	/* misuse n to avoid a compiler warning */
1897dd7cddfSDavid du Colombier     status = 0;
1907dd7cddfSDavid du Colombier     for (; rlimit - p >= 2; ++q) {
1917dd7cddfSDavid du Colombier 	if (q >= wlimit) {
1927dd7cddfSDavid du Colombier 	    status = 1;
1937dd7cddfSDavid du Colombier 	    break;
1947dd7cddfSDavid du Colombier 	}
1957dd7cddfSDavid du Colombier 	if (left == 0)
1967dd7cddfSDavid du Colombier 	    left = n;
1977dd7cddfSDavid du Colombier 	if ((n - left) & 1) {
1987dd7cddfSDavid du Colombier 	    q[1] = (byte)((p[1] << 4) | (p[2] >> 4));
1997dd7cddfSDavid du Colombier 	    p += 2, --left;
2007dd7cddfSDavid du Colombier 	} else {
2017dd7cddfSDavid du Colombier 	    q[1] = *++p;
2027dd7cddfSDavid du Colombier 	    if (!--left)
2037dd7cddfSDavid du Colombier 		++p;
2047dd7cddfSDavid du Colombier 	}
2057dd7cddfSDavid du Colombier     }
2067dd7cddfSDavid du Colombier 
2077dd7cddfSDavid du Colombier     END_1248;
2087dd7cddfSDavid du Colombier }
2097dd7cddfSDavid du Colombier 
2107dd7cddfSDavid du Colombier 
2117dd7cddfSDavid du Colombier /* 8-to-N reduction */
2127dd7cddfSDavid du Colombier #define FOREACH_8_N(out, nin)\
2137dd7cddfSDavid du Colombier 	byte out;\
2147dd7cddfSDavid du Colombier 	status = 1;\
2157dd7cddfSDavid du Colombier 	for ( ; q < wlimit; left -= n, p += n, ++q ) {\
2167dd7cddfSDavid du Colombier 	  n = min(left, nin);\
2177dd7cddfSDavid du Colombier 	  if ( rlimit - p < n ) {\
2187dd7cddfSDavid du Colombier 	    status = 0;\
2197dd7cddfSDavid du Colombier 	    break;\
2207dd7cddfSDavid du Colombier 	  }\
2217dd7cddfSDavid du Colombier 	  out = 0;\
2227dd7cddfSDavid du Colombier 	  switch ( n ) {\
2237dd7cddfSDavid du Colombier 	    case 0: left = ss->samples_per_row; --q; continue;
2247dd7cddfSDavid du Colombier #define END_FOREACH_8_N\
2257dd7cddfSDavid du Colombier 	    q[1] = out;\
2267dd7cddfSDavid du Colombier 	  }\
2277dd7cddfSDavid du Colombier 	}
2287dd7cddfSDavid du Colombier private int
s_8_N_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)2297dd7cddfSDavid du Colombier s_8_N_process(stream_state * st, stream_cursor_read * pr,
2307dd7cddfSDavid du Colombier 	      stream_cursor_write * pw, bool last)
2317dd7cddfSDavid du Colombier {
2327dd7cddfSDavid du Colombier     BEGIN_1248;
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier     switch (ss->bits_per_sample) {
2357dd7cddfSDavid du Colombier 
2367dd7cddfSDavid du Colombier 	case 1:{
2377dd7cddfSDavid du Colombier 		FOREACH_8_N(out, 8)
2387dd7cddfSDavid du Colombier 	case 8:
2397dd7cddfSDavid du Colombier 		out = p[8] >> 7;
2407dd7cddfSDavid du Colombier 	case 7:
2417dd7cddfSDavid du Colombier 		out |= (p[7] >> 7) << 1;
2427dd7cddfSDavid du Colombier 	case 6:
2437dd7cddfSDavid du Colombier 		out |= (p[6] >> 7) << 2;
2447dd7cddfSDavid du Colombier 	case 5:
2457dd7cddfSDavid du Colombier 		out |= (p[5] >> 7) << 3;
2467dd7cddfSDavid du Colombier 	case 4:
2477dd7cddfSDavid du Colombier 		out |= (p[4] >> 7) << 4;
2487dd7cddfSDavid du Colombier 	case 3:
2497dd7cddfSDavid du Colombier 		out |= (p[3] >> 7) << 5;
2507dd7cddfSDavid du Colombier 	case 2:
2517dd7cddfSDavid du Colombier 		out |= (p[2] >> 7) << 6;
2527dd7cddfSDavid du Colombier 	case 1:
2537dd7cddfSDavid du Colombier 		out |= p[1] & 0x80;
2547dd7cddfSDavid du Colombier 		END_FOREACH_8_N;
2557dd7cddfSDavid du Colombier 	    }
2567dd7cddfSDavid du Colombier 	    break;
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier 	case 2:{
2597dd7cddfSDavid du Colombier 		FOREACH_8_N(out, 4)
2607dd7cddfSDavid du Colombier 	case 4:
2617dd7cddfSDavid du Colombier 		out |= p[4] >> 6;
2627dd7cddfSDavid du Colombier 	case 3:
2637dd7cddfSDavid du Colombier 		out |= (p[3] >> 6) << 2;
2647dd7cddfSDavid du Colombier 	case 2:
2657dd7cddfSDavid du Colombier 		out |= (p[2] >> 6) << 4;
2667dd7cddfSDavid du Colombier 	case 1:
2677dd7cddfSDavid du Colombier 		out |= p[1] & 0xc0;
2687dd7cddfSDavid du Colombier 		END_FOREACH_8_N;
2697dd7cddfSDavid du Colombier 	    }
2707dd7cddfSDavid du Colombier 	    break;
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier 	case 4:{
2737dd7cddfSDavid du Colombier 		FOREACH_8_N(out, 2)
2747dd7cddfSDavid du Colombier 	case 2:
2757dd7cddfSDavid du Colombier 		out |= p[2] >> 4;
2767dd7cddfSDavid du Colombier 	case 1:
2777dd7cddfSDavid du Colombier 		out |= p[1] & 0xf0;
2787dd7cddfSDavid du Colombier 		END_FOREACH_8_N;
2797dd7cddfSDavid du Colombier 	    }
2807dd7cddfSDavid du Colombier 	    break;
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier 	default:
2837dd7cddfSDavid du Colombier 	    return ERRC;
2847dd7cddfSDavid du Colombier     }
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier     END_1248;
2877dd7cddfSDavid du Colombier }
2887dd7cddfSDavid du Colombier 
2897dd7cddfSDavid du Colombier const stream_template s_1_8_template = {
2907dd7cddfSDavid du Colombier     &st_1248_state, s_1_init, s_N_8_process, 1, 8
2917dd7cddfSDavid du Colombier };
2927dd7cddfSDavid du Colombier const stream_template s_2_8_template = {
2937dd7cddfSDavid du Colombier     &st_1248_state, s_2_init, s_N_8_process, 1, 4
2947dd7cddfSDavid du Colombier };
2957dd7cddfSDavid du Colombier const stream_template s_4_8_template = {
2967dd7cddfSDavid du Colombier     &st_1248_state, s_4_init, s_N_8_process, 1, 2
2977dd7cddfSDavid du Colombier };
2987dd7cddfSDavid du Colombier const stream_template s_12_8_template = {
2997dd7cddfSDavid du Colombier     &st_1248_state, s_12_init, s_12_8_process, 1, 2
3007dd7cddfSDavid du Colombier };
3017dd7cddfSDavid du Colombier 
3027dd7cddfSDavid du Colombier const stream_template s_8_1_template = {
3037dd7cddfSDavid du Colombier     &st_1248_state, s_1_init, s_8_N_process, 8, 1
3047dd7cddfSDavid du Colombier };
3057dd7cddfSDavid du Colombier const stream_template s_8_2_template = {
3067dd7cddfSDavid du Colombier     &st_1248_state, s_2_init, s_8_N_process, 4, 1
3077dd7cddfSDavid du Colombier };
3087dd7cddfSDavid du Colombier const stream_template s_8_4_template = {
3097dd7cddfSDavid du Colombier     &st_1248_state, s_4_init, s_8_N_process, 2, 1
3107dd7cddfSDavid du Colombier };
3117dd7cddfSDavid du Colombier 
3123ff48bf5SDavid du Colombier /* ---------------- Color space conversion ---------------- */
3133ff48bf5SDavid du Colombier 
3143ff48bf5SDavid du Colombier /* ------ Convert CMYK to RGB ------ */
3157dd7cddfSDavid du Colombier 
3167dd7cddfSDavid du Colombier private_st_C2R_state();
3177dd7cddfSDavid du Colombier 
3183ff48bf5SDavid du Colombier /* Initialize a CMYK => RGB conversion stream. */
3193ff48bf5SDavid du Colombier int
s_C2R_init(stream_C2R_state * ss,const gs_imager_state * pis)3203ff48bf5SDavid du Colombier s_C2R_init(stream_C2R_state *ss, const gs_imager_state *pis)
3213ff48bf5SDavid du Colombier {
3223ff48bf5SDavid du Colombier     ss->pis = pis;
3233ff48bf5SDavid du Colombier     return 0;
3243ff48bf5SDavid du Colombier }
3253ff48bf5SDavid du Colombier 
3267dd7cddfSDavid du Colombier /* Set default parameter values (actually, just clear pointers). */
3277dd7cddfSDavid du Colombier private void
s_C2R_set_defaults(stream_state * st)3287dd7cddfSDavid du Colombier s_C2R_set_defaults(stream_state * st)
3297dd7cddfSDavid du Colombier {
3307dd7cddfSDavid du Colombier     stream_C2R_state *const ss = (stream_C2R_state *) st;
3317dd7cddfSDavid du Colombier 
3327dd7cddfSDavid du Colombier     ss->pis = 0;
3337dd7cddfSDavid du Colombier }
3347dd7cddfSDavid du Colombier 
3357dd7cddfSDavid du Colombier /* Process one buffer. */
3367dd7cddfSDavid du Colombier private int
s_C2R_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)3377dd7cddfSDavid du Colombier s_C2R_process(stream_state * st, stream_cursor_read * pr,
3387dd7cddfSDavid du Colombier 	      stream_cursor_write * pw, bool last)
3397dd7cddfSDavid du Colombier {
3407dd7cddfSDavid du Colombier     stream_C2R_state *const ss = (stream_C2R_state *) st;
3417dd7cddfSDavid du Colombier     const byte *p = pr->ptr;
3427dd7cddfSDavid du Colombier     const byte *rlimit = pr->limit;
3437dd7cddfSDavid du Colombier     byte *q = pw->ptr;
3447dd7cddfSDavid du Colombier     byte *wlimit = pw->limit;
3457dd7cddfSDavid du Colombier 
3467dd7cddfSDavid du Colombier     for (; rlimit - p >= 4 && wlimit - q >= 3; p += 4, q += 3) {
3477dd7cddfSDavid du Colombier 	byte bc = p[1], bm = p[2], by = p[3], bk = p[4];
3487dd7cddfSDavid du Colombier 	frac rgb[3];
3497dd7cddfSDavid du Colombier 
3507dd7cddfSDavid du Colombier 	color_cmyk_to_rgb(byte2frac(bc), byte2frac(bm), byte2frac(by),
3517dd7cddfSDavid du Colombier 			  byte2frac(bk), ss->pis, rgb);
3527dd7cddfSDavid du Colombier 	q[1] = frac2byte(rgb[0]);
3537dd7cddfSDavid du Colombier 	q[2] = frac2byte(rgb[1]);
3547dd7cddfSDavid du Colombier 	q[3] = frac2byte(rgb[2]);
3557dd7cddfSDavid du Colombier     }
3567dd7cddfSDavid du Colombier     pr->ptr = p;
3577dd7cddfSDavid du Colombier     pw->ptr = q;
3587dd7cddfSDavid du Colombier     return (rlimit - p < 4 ? 0 : 1);
3597dd7cddfSDavid du Colombier }
3607dd7cddfSDavid du Colombier 
3613ff48bf5SDavid du Colombier const stream_template s_C2R_template = {
3627dd7cddfSDavid du Colombier     &st_C2R_state, 0 /*NULL */ , s_C2R_process, 4, 3, 0, s_C2R_set_defaults
3637dd7cddfSDavid du Colombier };
3647dd7cddfSDavid du Colombier 
3653ff48bf5SDavid du Colombier /* ------ Convert any color space to Indexed ------ */
3663ff48bf5SDavid du Colombier 
3673ff48bf5SDavid du Colombier private_st_IE_state();
3683ff48bf5SDavid du Colombier private
3693ff48bf5SDavid du Colombier ENUM_PTRS_WITH(ie_state_enum_ptrs, stream_IE_state *st) return 0;
3703ff48bf5SDavid du Colombier case 0: return ENUM_OBJ(st->Decode);
3713ff48bf5SDavid du Colombier case 1: return ENUM_BYTESTRING(&st->Table);
3723ff48bf5SDavid du Colombier ENUM_PTRS_END
3733ff48bf5SDavid du Colombier private
RELOC_PTRS_WITH(ie_state_reloc_ptrs,stream_IE_state * st)3743ff48bf5SDavid du Colombier RELOC_PTRS_WITH(ie_state_reloc_ptrs, stream_IE_state *st)
3753ff48bf5SDavid du Colombier {
3763ff48bf5SDavid du Colombier     RELOC_VAR(st->Decode);
3773ff48bf5SDavid du Colombier     RELOC_BYTESTRING_VAR(st->Table);
3783ff48bf5SDavid du Colombier }
3793ff48bf5SDavid du Colombier RELOC_PTRS_END
3803ff48bf5SDavid du Colombier 
3813ff48bf5SDavid du Colombier /* Set defaults. */
3823ff48bf5SDavid du Colombier private void
s_IE_set_defaults(stream_state * st)3833ff48bf5SDavid du Colombier s_IE_set_defaults(stream_state * st)
3843ff48bf5SDavid du Colombier {
3853ff48bf5SDavid du Colombier     stream_IE_state *const ss = (stream_IE_state *) st;
3863ff48bf5SDavid du Colombier 
3873ff48bf5SDavid du Colombier     ss->Decode = 0;		/* clear pointers */
3883ff48bf5SDavid du Colombier     gs_bytestring_from_string(&ss->Table, 0, 0);
3893ff48bf5SDavid du Colombier }
3903ff48bf5SDavid du Colombier 
3913ff48bf5SDavid du Colombier /* Initialize the state. */
3923ff48bf5SDavid du Colombier private int
s_IE_init(stream_state * st)3933ff48bf5SDavid du Colombier s_IE_init(stream_state * st)
3943ff48bf5SDavid du Colombier {
3953ff48bf5SDavid du Colombier     stream_IE_state *const ss = (stream_IE_state *) st;
3963ff48bf5SDavid du Colombier     int key_index = (1 << ss->BitsPerIndex) * ss->NumComponents;
3973ff48bf5SDavid du Colombier     int i;
3983ff48bf5SDavid du Colombier 
3993ff48bf5SDavid du Colombier     if (ss->Table.data == 0 || ss->Table.size < key_index)
4003ff48bf5SDavid du Colombier 	return ERRC;		/****** WRONG ******/
4013ff48bf5SDavid du Colombier     /* Initialize Table with default values. */
4023ff48bf5SDavid du Colombier     memset(ss->Table.data, 0, ss->NumComponents);
4033ff48bf5SDavid du Colombier     ss->Table.data[ss->Table.size - 1] = 0;
4043ff48bf5SDavid du Colombier     for (i = 0; i < countof(ss->hash_table); ++i)
4053ff48bf5SDavid du Colombier 	ss->hash_table[i] = key_index;
4063ff48bf5SDavid du Colombier     ss->next_index = 0;
4073ff48bf5SDavid du Colombier     ss->in_bits_left = 0;
4083ff48bf5SDavid du Colombier     ss->next_component = 0;
4093ff48bf5SDavid du Colombier     ss->byte_out = 1;
4103ff48bf5SDavid du Colombier     ss->x = 0;
4113ff48bf5SDavid du Colombier     return 0;
4123ff48bf5SDavid du Colombier }
4133ff48bf5SDavid du Colombier 
4143ff48bf5SDavid du Colombier /* Process a buffer. */
4153ff48bf5SDavid du Colombier private int
s_IE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)4163ff48bf5SDavid du Colombier s_IE_process(stream_state * st, stream_cursor_read * pr,
4173ff48bf5SDavid du Colombier 	     stream_cursor_write * pw, bool last)
4183ff48bf5SDavid du Colombier {
4193ff48bf5SDavid du Colombier     stream_IE_state *const ss = (stream_IE_state *) st;
4203ff48bf5SDavid du Colombier     /* Constant values from the state */
4213ff48bf5SDavid du Colombier     const int bpc = ss->BitsPerComponent;
4223ff48bf5SDavid du Colombier     const int num_components = ss->NumComponents;
4233ff48bf5SDavid du Colombier     const int end_index = (1 << ss->BitsPerIndex) * num_components;
4243ff48bf5SDavid du Colombier     byte *const table = ss->Table.data;
4253ff48bf5SDavid du Colombier     byte *const key = table + end_index;
4263ff48bf5SDavid du Colombier     /* Dynamic values from the state */
4273ff48bf5SDavid du Colombier     uint byte_in = ss->byte_in;
4283ff48bf5SDavid du Colombier     int in_bits_left = ss->in_bits_left;
4293ff48bf5SDavid du Colombier     int next_component = ss->next_component;
4303ff48bf5SDavid du Colombier     uint byte_out = ss->byte_out;
4313ff48bf5SDavid du Colombier     /* Other dynamic values */
4323ff48bf5SDavid du Colombier     const byte *p = pr->ptr;
4333ff48bf5SDavid du Colombier     const byte *rlimit = pr->limit;
4343ff48bf5SDavid du Colombier     byte *q = pw->ptr;
4353ff48bf5SDavid du Colombier     byte *wlimit = pw->limit;
4363ff48bf5SDavid du Colombier     int status = 0;
4373ff48bf5SDavid du Colombier 
4383ff48bf5SDavid du Colombier     for (;;) {
4393ff48bf5SDavid du Colombier 	uint hash, reprobe;
4403ff48bf5SDavid du Colombier 	int i, index;
4413ff48bf5SDavid du Colombier 
4423ff48bf5SDavid du Colombier 	/* Check for a filled output byte. */
4433ff48bf5SDavid du Colombier 	if (byte_out >= 0x100) {
4443ff48bf5SDavid du Colombier 	    if (q >= wlimit) {
4453ff48bf5SDavid du Colombier 		status = 1;
4463ff48bf5SDavid du Colombier 		break;
4473ff48bf5SDavid du Colombier 	    }
4483ff48bf5SDavid du Colombier 	    *++q = (byte)byte_out;
4493ff48bf5SDavid du Colombier 	    byte_out = 1;
4503ff48bf5SDavid du Colombier 	}
4513ff48bf5SDavid du Colombier 	/* Acquire a complete input value. */
4523ff48bf5SDavid du Colombier 	while (next_component < num_components) {
4533ff48bf5SDavid du Colombier 	    const float *decode = &ss->Decode[next_component * 2];
4543ff48bf5SDavid du Colombier 	    int sample;
4553ff48bf5SDavid du Colombier 
4563ff48bf5SDavid du Colombier 	    if (in_bits_left == 0) {
4573ff48bf5SDavid du Colombier 		if (p >= rlimit)
4583ff48bf5SDavid du Colombier 		    goto out;
4593ff48bf5SDavid du Colombier 		byte_in = *++p;
4603ff48bf5SDavid du Colombier 		in_bits_left = 8;
4613ff48bf5SDavid du Colombier 	    }
4623ff48bf5SDavid du Colombier 	    /* An input sample can never span a byte boundary. */
4633ff48bf5SDavid du Colombier 	    in_bits_left -= bpc;
4643ff48bf5SDavid du Colombier 	    sample = (byte_in >> in_bits_left) & ((1 << bpc) - 1);
4653ff48bf5SDavid du Colombier 	    /* Scale the sample according to Decode. */
4663ff48bf5SDavid du Colombier 	    sample = (int)((decode[0] +
4673ff48bf5SDavid du Colombier 			    (sample / (float)((1 << bpc) - 1) *
4683ff48bf5SDavid du Colombier 			     (decode[1] - decode[0]))) * 255 + 0.5);
4693ff48bf5SDavid du Colombier 	    key[next_component++] =
4703ff48bf5SDavid du Colombier 		(sample < 0 ? 0 : sample > 255 ? 255 : (byte)sample);
4713ff48bf5SDavid du Colombier 	}
4723ff48bf5SDavid du Colombier 	/* Look up the input value. */
4733ff48bf5SDavid du Colombier 	for (hash = 0, i = 0; i < num_components; ++i)
4743ff48bf5SDavid du Colombier 	    hash = hash + 23 * key[i];  /* adhoc */
4753ff48bf5SDavid du Colombier 	reprobe = (hash / countof(ss->hash_table)) | 137;  /* adhoc */
4763ff48bf5SDavid du Colombier 	for (hash %= countof(ss->hash_table);
4773ff48bf5SDavid du Colombier 	     memcmp(table + ss->hash_table[hash], key, num_components);
4783ff48bf5SDavid du Colombier 	     hash = (hash + reprobe) % countof(ss->hash_table)
4793ff48bf5SDavid du Colombier 	     )
4803ff48bf5SDavid du Colombier 	    DO_NOTHING;
4813ff48bf5SDavid du Colombier 	index = ss->hash_table[hash];
4823ff48bf5SDavid du Colombier 	if (index == end_index) {
4833ff48bf5SDavid du Colombier 	    /* The match was on an empty entry. */
4843ff48bf5SDavid du Colombier 	    if (ss->next_index == end_index) {
4853ff48bf5SDavid du Colombier 		/* Too many different values. */
4863ff48bf5SDavid du Colombier 		status = ERRC;
4873ff48bf5SDavid du Colombier 		break;
4883ff48bf5SDavid du Colombier 	    }
4893ff48bf5SDavid du Colombier 	    ss->hash_table[hash] = index = ss->next_index;
4903ff48bf5SDavid du Colombier 	    ss->next_index += num_components;
4913ff48bf5SDavid du Colombier 	    memcpy(table + index, key, num_components);
4923ff48bf5SDavid du Colombier 	}
4933ff48bf5SDavid du Colombier 	byte_out = (byte_out << ss->BitsPerIndex) + index / num_components;
4943ff48bf5SDavid du Colombier 	next_component = 0;
4953ff48bf5SDavid du Colombier 	if (++(ss->x) == ss->Width) {
4963ff48bf5SDavid du Colombier 	    /* Handle input and output padding. */
4973ff48bf5SDavid du Colombier 	    in_bits_left = 0;
4983ff48bf5SDavid du Colombier 	    if (byte_out != 1)
4993ff48bf5SDavid du Colombier 		while (byte_out < 0x100)
5003ff48bf5SDavid du Colombier 		    byte_out <<= 1;
5013ff48bf5SDavid du Colombier 	    ss->x = 0;
5023ff48bf5SDavid du Colombier 	}
5033ff48bf5SDavid du Colombier     }
5043ff48bf5SDavid du Colombier out:
5053ff48bf5SDavid du Colombier     pr->ptr = p;
5063ff48bf5SDavid du Colombier     pw->ptr = q;
5073ff48bf5SDavid du Colombier     ss->byte_in = byte_in;
5083ff48bf5SDavid du Colombier     ss->in_bits_left = in_bits_left;
5093ff48bf5SDavid du Colombier     ss->next_component = next_component;
5103ff48bf5SDavid du Colombier     ss->byte_out = byte_out;
5113ff48bf5SDavid du Colombier     /* For simplicity, always update the record of the table size. */
5123ff48bf5SDavid du Colombier     ss->Table.data[ss->Table.size - 1] =
5133ff48bf5SDavid du Colombier 	(ss->next_index == 0 ? 0 :
5143ff48bf5SDavid du Colombier 	 ss->next_index / ss->NumComponents - 1);
5153ff48bf5SDavid du Colombier     return status;
5163ff48bf5SDavid du Colombier }
5173ff48bf5SDavid du Colombier 
5183ff48bf5SDavid du Colombier const stream_template s_IE_template = {
5193ff48bf5SDavid du Colombier     &st_IE_state, s_IE_init, s_IE_process, 1, 1,
5203ff48bf5SDavid du Colombier     0 /* NULL */, s_IE_set_defaults
5213ff48bf5SDavid du Colombier };
5223ff48bf5SDavid du Colombier 
5233ff48bf5SDavid du Colombier #if 0
5243ff48bf5SDavid du Colombier 
5253ff48bf5SDavid du Colombier /* Test code */
5263ff48bf5SDavid du Colombier void
5273ff48bf5SDavid du Colombier test_IE(void)
5283ff48bf5SDavid du Colombier {
5293ff48bf5SDavid du Colombier     const stream_template *const template = &s_IE_template;
5303ff48bf5SDavid du Colombier     stream_IE_state state;
5313ff48bf5SDavid du Colombier     stream_state *const ss = (stream_state *)&state;
5323ff48bf5SDavid du Colombier     static const float decode[6] = {1, 0, 1, 0, 1, 0};
5333ff48bf5SDavid du Colombier     static const byte in[] = {
5343ff48bf5SDavid du Colombier 	/*
5353ff48bf5SDavid du Colombier 	 * Each row is 3 pixels x 3 components x 4 bits.  Processing the
5363ff48bf5SDavid du Colombier 	 * first two rows doesn't cause an error; processing all 3 rows
5373ff48bf5SDavid du Colombier 	 * does.
5383ff48bf5SDavid du Colombier 	 */
5393ff48bf5SDavid du Colombier 	0x12, 0x35, 0x67, 0x9a, 0xb0,
5403ff48bf5SDavid du Colombier 	0x56, 0x7d, 0xef, 0x12, 0x30,
5413ff48bf5SDavid du Colombier 	0x88, 0x88, 0x88, 0x88, 0x80
5423ff48bf5SDavid du Colombier     };
5433ff48bf5SDavid du Colombier     byte table[3 * 5];
5443ff48bf5SDavid du Colombier     int n;
5453ff48bf5SDavid du Colombier 
5463ff48bf5SDavid du Colombier     template->set_defaults(ss);
5473ff48bf5SDavid du Colombier     state.BitsPerComponent = 4;
5483ff48bf5SDavid du Colombier     state.NumComponents = 3;
5493ff48bf5SDavid du Colombier     state.Width = 3;
5503ff48bf5SDavid du Colombier     state.BitsPerIndex = 2;
5513ff48bf5SDavid du Colombier     state.Decode = decode;
5523ff48bf5SDavid du Colombier     gs_bytestring_from_bytes(&state.Table, table, 0, sizeof(table));
5533ff48bf5SDavid du Colombier     for (n = 10; n <= 15; n += 5) {
5543ff48bf5SDavid du Colombier 	stream_cursor_read r;
5553ff48bf5SDavid du Colombier 	stream_cursor_write w;
5563ff48bf5SDavid du Colombier 	byte out[100];
5573ff48bf5SDavid du Colombier 	int status;
5583ff48bf5SDavid du Colombier 
5593ff48bf5SDavid du Colombier 	s_IE_init(ss);
5603ff48bf5SDavid du Colombier 	r.ptr = in; --r.ptr;
5613ff48bf5SDavid du Colombier 	r.limit = r.ptr + n;
5623ff48bf5SDavid du Colombier 	w.ptr = out; --w.ptr;
5633ff48bf5SDavid du Colombier 	w.limit = w.ptr + sizeof(out);
5643ff48bf5SDavid du Colombier 	memset(table, 0xcc, sizeof(table));
5653ff48bf5SDavid du Colombier 	memset(out, 0xff, sizeof(out));
5663ff48bf5SDavid du Colombier 	dprintf1("processing %d bytes\n", n);
5673ff48bf5SDavid du Colombier 	status = template->process(ss, &r, &w, true);
5683ff48bf5SDavid du Colombier 	dprintf3("%d bytes read, %d bytes written, status = %d\n",
5693ff48bf5SDavid du Colombier 		 (int)(r.ptr + 1 - in), (int)(w.ptr + 1 - out), status);
5703ff48bf5SDavid du Colombier 	debug_dump_bytes(table, table + sizeof(table), "table");
5713ff48bf5SDavid du Colombier 	debug_dump_bytes(out, w.ptr + 1, "out");
5723ff48bf5SDavid du Colombier     }
5733ff48bf5SDavid du Colombier }
5743ff48bf5SDavid du Colombier 
5753ff48bf5SDavid du Colombier #endif
5763ff48bf5SDavid du Colombier 
5777dd7cddfSDavid du Colombier /* ---------------- Downsampling ---------------- */
5787dd7cddfSDavid du Colombier 
5793ff48bf5SDavid du Colombier /* Return the number of samples after downsampling. */
5803ff48bf5SDavid du Colombier int
s_Downsample_size_out(int size_in,int factor,bool pad)5813ff48bf5SDavid du Colombier s_Downsample_size_out(int size_in, int factor, bool pad)
5823ff48bf5SDavid du Colombier {
5833ff48bf5SDavid du Colombier     return ((pad ? size_in + factor - 1 : size_in) / factor);
5843ff48bf5SDavid du Colombier }
5853ff48bf5SDavid du Colombier 
5867dd7cddfSDavid du Colombier private void
s_Downsample_set_defaults(register stream_state * st)5877dd7cddfSDavid du Colombier s_Downsample_set_defaults(register stream_state * st)
5887dd7cddfSDavid du Colombier {
5893ff48bf5SDavid du Colombier     stream_Downsample_state *const ss = (stream_Downsample_state *)st;
5907dd7cddfSDavid du Colombier 
5917dd7cddfSDavid du Colombier     s_Downsample_set_defaults_inline(ss);
5927dd7cddfSDavid du Colombier }
5937dd7cddfSDavid du Colombier 
5943ff48bf5SDavid du Colombier /* ------ Subsample ------ */
5957dd7cddfSDavid du Colombier 
5967dd7cddfSDavid du Colombier gs_private_st_simple(st_Subsample_state, stream_Subsample_state,
5977dd7cddfSDavid du Colombier 		     "stream_Subsample_state");
5987dd7cddfSDavid du Colombier 
5997dd7cddfSDavid du Colombier /* Initialize the state. */
6007dd7cddfSDavid du Colombier private int
s_Subsample_init(stream_state * st)6017dd7cddfSDavid du Colombier s_Subsample_init(stream_state * st)
6027dd7cddfSDavid du Colombier {
6037dd7cddfSDavid du Colombier     stream_Subsample_state *const ss = (stream_Subsample_state *) st;
6047dd7cddfSDavid du Colombier 
6057dd7cddfSDavid du Colombier     ss->x = ss->y = 0;
6067dd7cddfSDavid du Colombier     return 0;
6077dd7cddfSDavid du Colombier }
6087dd7cddfSDavid du Colombier 
6097dd7cddfSDavid du Colombier /* Process one buffer. */
6107dd7cddfSDavid du Colombier private int
s_Subsample_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)6117dd7cddfSDavid du Colombier s_Subsample_process(stream_state * st, stream_cursor_read * pr,
6127dd7cddfSDavid du Colombier 		    stream_cursor_write * pw, bool last)
6137dd7cddfSDavid du Colombier {
6147dd7cddfSDavid du Colombier     stream_Subsample_state *const ss = (stream_Subsample_state *) st;
6157dd7cddfSDavid du Colombier     const byte *p = pr->ptr;
6167dd7cddfSDavid du Colombier     const byte *rlimit = pr->limit;
6177dd7cddfSDavid du Colombier     byte *q = pw->ptr;
6187dd7cddfSDavid du Colombier     byte *wlimit = pw->limit;
6197dd7cddfSDavid du Colombier     int spp = ss->Colors;
6203ff48bf5SDavid du Colombier     int width = ss->WidthIn, height = ss->HeightIn;
6217dd7cddfSDavid du Colombier     int xf = ss->XFactor, yf = ss->YFactor;
6227dd7cddfSDavid du Colombier     int xf2 = xf / 2, yf2 = yf / 2;
6233ff48bf5SDavid du Colombier     int xlimit = (width / xf) * xf, ylimit = (height / yf) * yf;
6243ff48bf5SDavid du Colombier     int xlast =
6253ff48bf5SDavid du Colombier 	(ss->padX && xlimit < width ? xlimit + (width % xf) / 2 : -1);
6263ff48bf5SDavid du Colombier     int ylast =
6273ff48bf5SDavid du Colombier 	(ss->padY && ylimit < height ? ylimit + (height % yf) / 2 : -1);
6287dd7cddfSDavid du Colombier     int x = ss->x, y = ss->y;
6297dd7cddfSDavid du Colombier     int status = 0;
6307dd7cddfSDavid du Colombier 
6313ff48bf5SDavid du Colombier     if_debug4('w', "[w]subsample: x=%d, y=%d, rcount=%ld, wcount=%ld\n",
6323ff48bf5SDavid du Colombier 	      x, y, (long)(rlimit - p), (long)(wlimit - q));
6337dd7cddfSDavid du Colombier     for (; rlimit - p >= spp; p += spp) {
6343ff48bf5SDavid du Colombier 	if (((y % yf == yf2 && y < ylimit) || y == ylast) &&
6353ff48bf5SDavid du Colombier 	    ((x % xf == xf2 && x < xlimit) || x == xlast)
6363ff48bf5SDavid du Colombier 	    ) {
6377dd7cddfSDavid du Colombier 	    if (wlimit - q < spp) {
6387dd7cddfSDavid du Colombier 		status = 1;
6397dd7cddfSDavid du Colombier 		break;
6407dd7cddfSDavid du Colombier 	    }
6417dd7cddfSDavid du Colombier 	    memcpy(q + 1, p + 1, spp);
6427dd7cddfSDavid du Colombier 	    q += spp;
6437dd7cddfSDavid du Colombier 	}
6443ff48bf5SDavid du Colombier 	if (++x == width)
6453ff48bf5SDavid du Colombier 	    x = 0, ++y;
6467dd7cddfSDavid du Colombier     }
6473ff48bf5SDavid du Colombier     if_debug5('w',
6483ff48bf5SDavid du Colombier 	      "[w]subsample: x'=%d, y'=%d, read %ld, wrote %ld, status = %d\n",
6493ff48bf5SDavid du Colombier 	      x, y, (long)(p - pr->ptr), (long)(q - pw->ptr), status);
6507dd7cddfSDavid du Colombier     pr->ptr = p;
6517dd7cddfSDavid du Colombier     pw->ptr = q;
6527dd7cddfSDavid du Colombier     ss->x = x, ss->y = y;
6537dd7cddfSDavid du Colombier     return status;
6547dd7cddfSDavid du Colombier }
6557dd7cddfSDavid du Colombier 
6563ff48bf5SDavid du Colombier const stream_template s_Subsample_template = {
6577dd7cddfSDavid du Colombier     &st_Subsample_state, s_Subsample_init, s_Subsample_process, 4, 4,
6587dd7cddfSDavid du Colombier     0 /* NULL */, s_Downsample_set_defaults
6597dd7cddfSDavid du Colombier };
6607dd7cddfSDavid du Colombier 
6613ff48bf5SDavid du Colombier /* ------ Average ------ */
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier private_st_Average_state();
6647dd7cddfSDavid du Colombier 
6657dd7cddfSDavid du Colombier /* Set default parameter values (actually, just clear pointers). */
6667dd7cddfSDavid du Colombier private void
s_Average_set_defaults(stream_state * st)6677dd7cddfSDavid du Colombier s_Average_set_defaults(stream_state * st)
6687dd7cddfSDavid du Colombier {
6697dd7cddfSDavid du Colombier     stream_Average_state *const ss = (stream_Average_state *) st;
6707dd7cddfSDavid du Colombier 
6717dd7cddfSDavid du Colombier     s_Downsample_set_defaults(st);
6727dd7cddfSDavid du Colombier     /* Clear pointers */
6737dd7cddfSDavid du Colombier     ss->sums = 0;
6747dd7cddfSDavid du Colombier }
6757dd7cddfSDavid du Colombier 
6767dd7cddfSDavid du Colombier /* Initialize the state. */
6777dd7cddfSDavid du Colombier private int
s_Average_init(stream_state * st)6787dd7cddfSDavid du Colombier s_Average_init(stream_state * st)
6797dd7cddfSDavid du Colombier {
6807dd7cddfSDavid du Colombier     stream_Average_state *const ss = (stream_Average_state *) st;
6817dd7cddfSDavid du Colombier 
6827dd7cddfSDavid du Colombier     ss->sum_size =
6833ff48bf5SDavid du Colombier 	ss->Colors * ((ss->WidthIn + ss->XFactor - 1) / ss->XFactor);
6847dd7cddfSDavid du Colombier     ss->copy_size = ss->sum_size -
6853ff48bf5SDavid du Colombier 	(ss->padX || (ss->WidthIn % ss->XFactor == 0) ? 0 : ss->Colors);
6867dd7cddfSDavid du Colombier     ss->sums =
6877dd7cddfSDavid du Colombier 	(uint *)gs_alloc_byte_array(st->memory, ss->sum_size,
6887dd7cddfSDavid du Colombier 				    sizeof(uint), "Average sums");
6897dd7cddfSDavid du Colombier     if (ss->sums == 0)
6907dd7cddfSDavid du Colombier 	return ERRC;	/****** WRONG ******/
6917dd7cddfSDavid du Colombier     memset(ss->sums, 0, ss->sum_size * sizeof(uint));
6927dd7cddfSDavid du Colombier     return s_Subsample_init(st);
6937dd7cddfSDavid du Colombier }
6947dd7cddfSDavid du Colombier 
6957dd7cddfSDavid du Colombier /* Release the state. */
6967dd7cddfSDavid du Colombier private void
s_Average_release(stream_state * st)6977dd7cddfSDavid du Colombier s_Average_release(stream_state * st)
6987dd7cddfSDavid du Colombier {
6997dd7cddfSDavid du Colombier     stream_Average_state *const ss = (stream_Average_state *) st;
7007dd7cddfSDavid du Colombier 
7017dd7cddfSDavid du Colombier     gs_free_object(st->memory, ss->sums, "Average sums");
7027dd7cddfSDavid du Colombier }
7037dd7cddfSDavid du Colombier 
7047dd7cddfSDavid du Colombier /* Process one buffer. */
7057dd7cddfSDavid du Colombier private int
s_Average_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)7067dd7cddfSDavid du Colombier s_Average_process(stream_state * st, stream_cursor_read * pr,
7077dd7cddfSDavid du Colombier 		  stream_cursor_write * pw, bool last)
7087dd7cddfSDavid du Colombier {
7097dd7cddfSDavid du Colombier     stream_Average_state *const ss = (stream_Average_state *) st;
7107dd7cddfSDavid du Colombier     const byte *p = pr->ptr;
7117dd7cddfSDavid du Colombier     const byte *rlimit = pr->limit;
7127dd7cddfSDavid du Colombier     byte *q = pw->ptr;
7137dd7cddfSDavid du Colombier     byte *wlimit = pw->limit;
7147dd7cddfSDavid du Colombier     int spp = ss->Colors;
7153ff48bf5SDavid du Colombier     int width = ss->WidthIn;
7167dd7cddfSDavid du Colombier     int xf = ss->XFactor, yf = ss->YFactor;
7177dd7cddfSDavid du Colombier     int x = ss->x, y = ss->y;
7187dd7cddfSDavid du Colombier     uint *sums = ss->sums;
7197dd7cddfSDavid du Colombier     int status = 0;
7207dd7cddfSDavid du Colombier 
7217dd7cddfSDavid du Colombier top:
7227dd7cddfSDavid du Colombier     if (y == yf || (last && p >= rlimit && ss->padY && y != 0)) {
7237dd7cddfSDavid du Colombier 	/* We're copying averaged values to the output. */
7247dd7cddfSDavid du Colombier 	int ncopy = min(ss->copy_size - x, wlimit - q);
7257dd7cddfSDavid du Colombier 
7267dd7cddfSDavid du Colombier 	if (ncopy) {
7277dd7cddfSDavid du Colombier 	    int scale = xf * y;
7287dd7cddfSDavid du Colombier 
7297dd7cddfSDavid du Colombier 	    while (--ncopy >= 0)
7307dd7cddfSDavid du Colombier 		*++q = (byte) (sums[x++] / scale);
7317dd7cddfSDavid du Colombier 	}
7327dd7cddfSDavid du Colombier 	if (x < ss->copy_size) {
7337dd7cddfSDavid du Colombier 	    status = 1;
7347dd7cddfSDavid du Colombier 	    goto out;
7357dd7cddfSDavid du Colombier 	}
7367dd7cddfSDavid du Colombier 	/* Done copying. */
7377dd7cddfSDavid du Colombier 	x = y = 0;
7387dd7cddfSDavid du Colombier 	memset(sums, 0, ss->sum_size * sizeof(uint));
7397dd7cddfSDavid du Colombier     }
7407dd7cddfSDavid du Colombier     while (rlimit - p >= spp) {
7417dd7cddfSDavid du Colombier 	uint *bp = sums + x / xf * spp;
7427dd7cddfSDavid du Colombier 	int i;
7437dd7cddfSDavid du Colombier 
7447dd7cddfSDavid du Colombier 	for (i = spp; --i >= 0;)
7457dd7cddfSDavid du Colombier 	    *bp++ += *++p;
7467dd7cddfSDavid du Colombier 	if (++x == width) {
7477dd7cddfSDavid du Colombier 	    x = 0;
7487dd7cddfSDavid du Colombier 	    ++y;
7497dd7cddfSDavid du Colombier 	    goto top;
7507dd7cddfSDavid du Colombier 	}
7517dd7cddfSDavid du Colombier     }
7527dd7cddfSDavid du Colombier out:
7537dd7cddfSDavid du Colombier     pr->ptr = p;
7547dd7cddfSDavid du Colombier     pw->ptr = q;
7557dd7cddfSDavid du Colombier     ss->x = x, ss->y = y;
7567dd7cddfSDavid du Colombier     return status;
7577dd7cddfSDavid du Colombier }
7587dd7cddfSDavid du Colombier 
7593ff48bf5SDavid du Colombier const stream_template s_Average_template = {
7607dd7cddfSDavid du Colombier     &st_Average_state, s_Average_init, s_Average_process, 4, 4,
7617dd7cddfSDavid du Colombier     s_Average_release, s_Average_set_defaults
7627dd7cddfSDavid du Colombier };
763*593dc095SDavid du Colombier 
764*593dc095SDavid du Colombier /* ---------------- Image compression chooser ---------------- */
765*593dc095SDavid du Colombier 
766*593dc095SDavid du Colombier private_st_compr_chooser_state();
767*593dc095SDavid du Colombier 
768*593dc095SDavid du Colombier /* Initialize the state. */
769*593dc095SDavid du Colombier private int
s_compr_chooser_init(stream_state * st)770*593dc095SDavid du Colombier s_compr_chooser_init(stream_state * st)
771*593dc095SDavid du Colombier {
772*593dc095SDavid du Colombier     stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st;
773*593dc095SDavid du Colombier 
774*593dc095SDavid du Colombier     ss->choice = 0;
775*593dc095SDavid du Colombier     ss->width = ss->height = ss->depth = ss->bits_per_sample = 0;
776*593dc095SDavid du Colombier     ss->sample = 0;
777*593dc095SDavid du Colombier     ss->samples_count = 0;
778*593dc095SDavid du Colombier     ss->bits_left = 0;
779*593dc095SDavid du Colombier     ss->packed_data = 0;
780*593dc095SDavid du Colombier     ss->lower_plateaus = ss->upper_plateaus = 0;
781*593dc095SDavid du Colombier     ss->gradients = 0;
782*593dc095SDavid du Colombier     return 0;
783*593dc095SDavid du Colombier }
784*593dc095SDavid du Colombier 
785*593dc095SDavid du Colombier /* Set image dimensions. */
786*593dc095SDavid du Colombier int
s_compr_chooser_set_dimensions(stream_compr_chooser_state * ss,int width,int height,int depth,int bits_per_sample)787*593dc095SDavid du Colombier s_compr_chooser_set_dimensions(stream_compr_chooser_state * ss, int width,
788*593dc095SDavid du Colombier 		    int height, int depth, int bits_per_sample)
789*593dc095SDavid du Colombier {
790*593dc095SDavid du Colombier     ss->width = width;
791*593dc095SDavid du Colombier     ss->height = height;
792*593dc095SDavid du Colombier     ss->depth = depth;
793*593dc095SDavid du Colombier     ss->bits_per_sample = bits_per_sample;
794*593dc095SDavid du Colombier     ss->sample = gs_alloc_bytes(ss->memory, width * depth, "s_compr_chooser_set_dimensions");
795*593dc095SDavid du Colombier     if (ss->sample == 0)
796*593dc095SDavid du Colombier 	return_error(gs_error_VMerror);
797*593dc095SDavid du Colombier     return 0;
798*593dc095SDavid du Colombier }
799*593dc095SDavid du Colombier 
800*593dc095SDavid du Colombier /* Release state. */
801*593dc095SDavid du Colombier private void
s_compr_chooser_release(stream_state * st)802*593dc095SDavid du Colombier s_compr_chooser_release(stream_state * st)
803*593dc095SDavid du Colombier {
804*593dc095SDavid du Colombier     stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st;
805*593dc095SDavid du Colombier 
806*593dc095SDavid du Colombier     gs_free_object(ss->memory, ss->sample, "s_compr_chooser_release");
807*593dc095SDavid du Colombier }
808*593dc095SDavid du Colombier 
809*593dc095SDavid du Colombier /* Estimate a row for photo/lineart recognition. */
810*593dc095SDavid du Colombier private void
s_compr_chooser__estimate_row(stream_compr_chooser_state * const ss,byte * p)811*593dc095SDavid du Colombier s_compr_chooser__estimate_row(stream_compr_chooser_state *const ss, byte *p)
812*593dc095SDavid du Colombier {
813*593dc095SDavid du Colombier     /*	This function uses a statistical algorithm being not well defined.
814*593dc095SDavid du Colombier 
815*593dc095SDavid du Colombier 	We compute areas covered by gradients,
816*593dc095SDavid du Colombier 	separately with small width (line art)
817*593dc095SDavid du Colombier 	and with big width (photo).
818*593dc095SDavid du Colombier 	Making the choice based on the areas.
819*593dc095SDavid du Colombier 
820*593dc095SDavid du Colombier 	Note that we deal with horizontal frequencies only.
821*593dc095SDavid du Colombier 	Dealing with vertical ones would be too expensive.
822*593dc095SDavid du Colombier     */
823*593dc095SDavid du Colombier     const int delta = 256 / 16; /* about 1/16 of the color range */
824*593dc095SDavid du Colombier     const int max_lineart_boundary_width = 3; /* pixels */
825*593dc095SDavid du Colombier     const int max_gradient_constant = 10; /* pixels */
826*593dc095SDavid du Colombier     int i, j0 = 0, j1 = 0;
827*593dc095SDavid du Colombier     int w0 = p[0], w1 = p[0], v;
828*593dc095SDavid du Colombier     ulong plateau_count = 0, lower_plateaus = 0;
829*593dc095SDavid du Colombier     ulong upper_plateaus = 0, gradients = 0;
830*593dc095SDavid du Colombier     bool lower = false, upper = false;
831*593dc095SDavid du Colombier 
832*593dc095SDavid du Colombier     for (i = 1; i < ss->width; i++) {
833*593dc095SDavid du Colombier 	v = p[i];
834*593dc095SDavid du Colombier 	if (!lower) {
835*593dc095SDavid du Colombier 	    if (w1 < v) {
836*593dc095SDavid du Colombier 		if (!upper)
837*593dc095SDavid du Colombier 		    j1 = i - 1;
838*593dc095SDavid du Colombier 		w1 = v;
839*593dc095SDavid du Colombier 		upper = true;
840*593dc095SDavid du Colombier 	    } else if (w1 == v && j1 < i - max_gradient_constant)
841*593dc095SDavid du Colombier 		j1 = i - max_gradient_constant; /* inner constant plateaw */
842*593dc095SDavid du Colombier 	    else if (upper && w1 - delta > v) {
843*593dc095SDavid du Colombier 		/* end of upper plateau at w1-delta...w1 */
844*593dc095SDavid du Colombier 		for (j0 = i - 1; j0 > j1 && w1 - delta <= p[j0]; j0--) DO_NOTHING;
845*593dc095SDavid du Colombier 		/* upper plateau j0+1...i-1 */
846*593dc095SDavid du Colombier 		if(j0 > 0 && i < ss->width - 1) /* ignore sides */
847*593dc095SDavid du Colombier 		    upper_plateaus += i - j0;
848*593dc095SDavid du Colombier 		plateau_count ++;
849*593dc095SDavid du Colombier 		if (j0 > j1) {
850*593dc095SDavid du Colombier 		    /* upgrade j1...j0 */
851*593dc095SDavid du Colombier 		    if (j0 > j1 + max_lineart_boundary_width)
852*593dc095SDavid du Colombier 			gradients += j0 - j1;
853*593dc095SDavid du Colombier 		}
854*593dc095SDavid du Colombier 		j1 = i;
855*593dc095SDavid du Colombier 		upper = false;
856*593dc095SDavid du Colombier 		w0 = w1;
857*593dc095SDavid du Colombier 		continue;
858*593dc095SDavid du Colombier 	    }
859*593dc095SDavid du Colombier 	}
860*593dc095SDavid du Colombier 	if (!upper) {
861*593dc095SDavid du Colombier 	    if (w0 > v) {
862*593dc095SDavid du Colombier 		if (!lower)
863*593dc095SDavid du Colombier 		    j1 = i - 1;
864*593dc095SDavid du Colombier 		w0 = v;
865*593dc095SDavid du Colombier 		lower = true;
866*593dc095SDavid du Colombier 	    } else if (w0 == v && j1 < i - max_gradient_constant)
867*593dc095SDavid du Colombier 		j1 = i - max_gradient_constant; /* inner constant plateaw */
868*593dc095SDavid du Colombier 	    else if (lower && w0 + delta < v) {
869*593dc095SDavid du Colombier 		/* end of lower plateau at w0...w0+delta */
870*593dc095SDavid du Colombier 		for (j0 = i - 1; j0 > j1 && w0 + delta >= p[j0]; j0--) DO_NOTHING;
871*593dc095SDavid du Colombier 		/* lower plateau j0+1...i-1 */
872*593dc095SDavid du Colombier 		if(j0 > 0 && i < ss->width - 1) /* ignore sides */
873*593dc095SDavid du Colombier 		    lower_plateaus += i - j0;
874*593dc095SDavid du Colombier 		plateau_count ++;
875*593dc095SDavid du Colombier 		if (j0 > j1) {
876*593dc095SDavid du Colombier 		    /* downgrade j1...j0 */
877*593dc095SDavid du Colombier 		    if (j0 > j1 + max_lineart_boundary_width)
878*593dc095SDavid du Colombier 			gradients += j0 - j1;
879*593dc095SDavid du Colombier 		}
880*593dc095SDavid du Colombier 		j1 = i;
881*593dc095SDavid du Colombier 		lower = false;
882*593dc095SDavid du Colombier 		w1 = w0;
883*593dc095SDavid du Colombier 	    }
884*593dc095SDavid du Colombier 	}
885*593dc095SDavid du Colombier     }
886*593dc095SDavid du Colombier     if (plateau_count > ss->width / 6) {
887*593dc095SDavid du Colombier 	/*  Possibly a dithering, can't recognize.
888*593dc095SDavid du Colombier 	    It would be better to estimate frequency histogram rather than
889*593dc095SDavid du Colombier 	    rough quantity, but we hope that the simpler test can work fine.
890*593dc095SDavid du Colombier 	*/
891*593dc095SDavid du Colombier     } else if (!plateau_count) /* a pseudo-constant color through entire row */
892*593dc095SDavid du Colombier 	DO_NOTHING; /* ignore such lines */
893*593dc095SDavid du Colombier     else {
894*593dc095SDavid du Colombier 	int plateaus;
895*593dc095SDavid du Colombier 	ss->lower_plateaus += lower_plateaus;
896*593dc095SDavid du Colombier 	ss->upper_plateaus += upper_plateaus;
897*593dc095SDavid du Colombier 	ss->gradients += gradients;
898*593dc095SDavid du Colombier 	plateaus = min(ss->lower_plateaus, ss->upper_plateaus); /* (fore/back)ground */
899*593dc095SDavid du Colombier 	if (ss->gradients >= 10000 && ss->gradients > plateaus / 6)
900*593dc095SDavid du Colombier 	    ss->choice = 1; /* choice is made : photo */
901*593dc095SDavid du Colombier 	else if (plateaus >= 100000 && plateaus / 5000 >= ss->gradients)
902*593dc095SDavid du Colombier 	    ss->choice = 2; /* choice is made : lineart */
903*593dc095SDavid du Colombier     }
904*593dc095SDavid du Colombier }
905*593dc095SDavid du Colombier 
906*593dc095SDavid du Colombier /* Recognize photo/lineart. */
907*593dc095SDavid du Colombier private void
s_compr_chooser__recognize(stream_compr_chooser_state * ss)908*593dc095SDavid du Colombier s_compr_chooser__recognize(stream_compr_chooser_state * ss)
909*593dc095SDavid du Colombier {
910*593dc095SDavid du Colombier     int i;
911*593dc095SDavid du Colombier     byte *p = ss->sample;
912*593dc095SDavid du Colombier 
913*593dc095SDavid du Colombier     for (i = 0; i < ss->depth; i++, p += ss->width)
914*593dc095SDavid du Colombier 	s_compr_chooser__estimate_row(ss, p);
915*593dc095SDavid du Colombier     /* todo: make decision */
916*593dc095SDavid du Colombier }
917*593dc095SDavid du Colombier 
918*593dc095SDavid du Colombier /* Uppack data and recognize photo/lineart. */
919*593dc095SDavid du Colombier private void
s_compr_chooser__unpack_and_recognize(stream_compr_chooser_state * const ss,const byte * data,int length)920*593dc095SDavid du Colombier s_compr_chooser__unpack_and_recognize(stream_compr_chooser_state *const ss,
921*593dc095SDavid du Colombier 				      const byte *data, int length)
922*593dc095SDavid du Colombier {
923*593dc095SDavid du Colombier     /*
924*593dc095SDavid du Colombier      * Input samples are packed ABCABCABC..., but the sample[] array of
925*593dc095SDavid du Colombier      * unpacked values is stored AAA...BBB...CCC.  i counts samples within
926*593dc095SDavid du Colombier      * a pixel, multiplied by width; j counts pixels.
927*593dc095SDavid du Colombier      */
928*593dc095SDavid du Colombier     uint i = (ss->samples_count % ss->depth) * ss->width;
929*593dc095SDavid du Colombier     uint j = ss->samples_count / ss->depth;
930*593dc095SDavid du Colombier     const byte *p = data;
931*593dc095SDavid du Colombier     int l = length;
932*593dc095SDavid du Colombier 
933*593dc095SDavid du Colombier     while (l) {
934*593dc095SDavid du Colombier 	if (ss->bits_left < 8) {
935*593dc095SDavid du Colombier 	    uint k = (sizeof(ss->packed_data) * 8 - ss->bits_left) / 8;
936*593dc095SDavid du Colombier 
937*593dc095SDavid du Colombier 	    k = min(k, l);
938*593dc095SDavid du Colombier 	    for (; k; k--, l--, p++, ss->bits_left += 8)
939*593dc095SDavid du Colombier 		ss->packed_data = (ss->packed_data << 8) + *p;
940*593dc095SDavid du Colombier 	}
941*593dc095SDavid du Colombier 	while (ss->bits_left >= ss->bits_per_sample) {
942*593dc095SDavid du Colombier 	    uint k = ss->bits_left - ss->bits_per_sample;
943*593dc095SDavid du Colombier 	    ulong v = ss->packed_data >> k;
944*593dc095SDavid du Colombier 
945*593dc095SDavid du Colombier 	    ss->packed_data -= (v << k);
946*593dc095SDavid du Colombier 	    ss->bits_left -= ss->bits_per_sample;
947*593dc095SDavid du Colombier 	    if (ss->bits_per_sample > 8)
948*593dc095SDavid du Colombier 		v >>= ss->bits_per_sample - 8;
949*593dc095SDavid du Colombier 	    else
950*593dc095SDavid du Colombier 		v <<= 8 - ss->bits_per_sample;
951*593dc095SDavid du Colombier 	    ss->sample[i + j] = (byte)v;  /* scaled to 0...255 */
952*593dc095SDavid du Colombier 	    i += ss->width;
953*593dc095SDavid du Colombier 	    if (i >= ss->width * ss->depth)
954*593dc095SDavid du Colombier 		i = 0, j++;
955*593dc095SDavid du Colombier 	    ss->samples_count++;
956*593dc095SDavid du Colombier 	    if (ss->samples_count >= ss->width * ss->depth) {
957*593dc095SDavid du Colombier 		s_compr_chooser__recognize(ss);
958*593dc095SDavid du Colombier 		ss->packed_data = 0;
959*593dc095SDavid du Colombier 		ss->bits_left = 0;
960*593dc095SDavid du Colombier 		ss->samples_count = 0;
961*593dc095SDavid du Colombier 		i = j = 0;
962*593dc095SDavid du Colombier 	    }
963*593dc095SDavid du Colombier 	}
964*593dc095SDavid du Colombier     }
965*593dc095SDavid du Colombier }
966*593dc095SDavid du Colombier 
967*593dc095SDavid du Colombier /* Process a buffer. */
968*593dc095SDavid du Colombier private int
s_compr_chooser_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)969*593dc095SDavid du Colombier s_compr_chooser_process(stream_state * st, stream_cursor_read * pr,
970*593dc095SDavid du Colombier 	     stream_cursor_write * pw, bool last)
971*593dc095SDavid du Colombier {
972*593dc095SDavid du Colombier     stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st;
973*593dc095SDavid du Colombier     int l = pr->limit - pr->ptr;
974*593dc095SDavid du Colombier 
975*593dc095SDavid du Colombier     if (ss->width >= 3) /* Can't process narrow images. */
976*593dc095SDavid du Colombier 	s_compr_chooser__unpack_and_recognize(ss, pr->ptr + 1, l);
977*593dc095SDavid du Colombier     pr->ptr += l;
978*593dc095SDavid du Colombier     return 0;
979*593dc095SDavid du Colombier }
980*593dc095SDavid du Colombier 
981*593dc095SDavid du Colombier const stream_template s_compr_chooser_template = {
982*593dc095SDavid du Colombier     &st_compr_chooser_state, s_compr_chooser_init, s_compr_chooser_process, 1, 1,
983*593dc095SDavid du Colombier     s_compr_chooser_release, 0 /* NULL */
984*593dc095SDavid du Colombier };
985*593dc095SDavid du Colombier 
986*593dc095SDavid du Colombier /* Get choice */
987*593dc095SDavid du Colombier uint
s_compr_chooser__get_choice(stream_compr_chooser_state * ss,bool force)988*593dc095SDavid du Colombier s_compr_chooser__get_choice(stream_compr_chooser_state *ss, bool force)
989*593dc095SDavid du Colombier {
990*593dc095SDavid du Colombier     ulong plateaus = min(ss->lower_plateaus, ss->upper_plateaus);
991*593dc095SDavid du Colombier 
992*593dc095SDavid du Colombier     if (ss->choice)
993*593dc095SDavid du Colombier 	return ss->choice;
994*593dc095SDavid du Colombier     if (force) {
995*593dc095SDavid du Colombier 	if (ss->gradients > plateaus / 12) /* messenger16.pdf, page 3. */
996*593dc095SDavid du Colombier 	    return 1; /* photo */
997*593dc095SDavid du Colombier 	else if (plateaus / 5000 >= ss->gradients)
998*593dc095SDavid du Colombier 	    return 2; /* lineart */
999*593dc095SDavid du Colombier     }
1000*593dc095SDavid du Colombier     return 0;
1001*593dc095SDavid du Colombier }
1002*593dc095SDavid du Colombier 
1003*593dc095SDavid du Colombier /* ---------------- Am image color conversion filter ---------------- */
1004*593dc095SDavid du Colombier 
1005*593dc095SDavid du Colombier private_st_image_colors_state();
1006*593dc095SDavid du Colombier 
1007*593dc095SDavid du Colombier /* Initialize the state. */
1008*593dc095SDavid du Colombier private int
s_image_colors_init(stream_state * st)1009*593dc095SDavid du Colombier s_image_colors_init(stream_state * st)
1010*593dc095SDavid du Colombier {
1011*593dc095SDavid du Colombier     stream_image_colors_state *const ss = (stream_image_colors_state *) st;
1012*593dc095SDavid du Colombier 
1013*593dc095SDavid du Colombier     ss->width = ss->height = ss->depth = ss->bits_per_sample = 0;
1014*593dc095SDavid du Colombier     ss->output_bits_buffer = 0;
1015*593dc095SDavid du Colombier     ss->output_bits_buffered = 0;
1016*593dc095SDavid du Colombier     ss->output_depth = 1;
1017*593dc095SDavid du Colombier     ss->output_component_index = ss->output_depth;
1018*593dc095SDavid du Colombier     ss->output_bits_per_sample = 1;
1019*593dc095SDavid du Colombier     ss->output_component_bits_written = 0;
1020*593dc095SDavid du Colombier     ss->raster = 0;
1021*593dc095SDavid du Colombier     ss->row_bits = 0;
1022*593dc095SDavid du Colombier     ss->row_bits_passed = 0;
1023*593dc095SDavid du Colombier     ss->row_alignment_bytes = 0;
1024*593dc095SDavid du Colombier     ss->row_alignment_bytes_left = 0;
1025*593dc095SDavid du Colombier     ss->input_component_index = 0;
1026*593dc095SDavid du Colombier     ss->input_bits_buffer = 0;
1027*593dc095SDavid du Colombier     ss->input_bits_buffered = 0;
1028*593dc095SDavid du Colombier     ss->convert_color = 0;
1029*593dc095SDavid du Colombier     ss->pcs = 0;
1030*593dc095SDavid du Colombier     ss->pdev = 0;
1031*593dc095SDavid du Colombier     ss->pis = 0;
1032*593dc095SDavid du Colombier     return 0;
1033*593dc095SDavid du Colombier }
1034*593dc095SDavid du Colombier 
1035*593dc095SDavid du Colombier private int
s_image_colors_convert_color_to_mask(stream_image_colors_state * ss)1036*593dc095SDavid du Colombier s_image_colors_convert_color_to_mask(stream_image_colors_state *ss)
1037*593dc095SDavid du Colombier {
1038*593dc095SDavid du Colombier     int i, ii;
1039*593dc095SDavid du Colombier 
1040*593dc095SDavid du Colombier     for (i = ii = 0; i < ss->depth; i++, ii += 2)
1041*593dc095SDavid du Colombier 	if (ss->input_color[i] < ss->MaskColor[ii] ||
1042*593dc095SDavid du Colombier 	    ss->input_color[i] > ss->MaskColor[ii + 1])
1043*593dc095SDavid du Colombier 	    break;
1044*593dc095SDavid du Colombier     ss->output_color[0] = (i < ss->depth ? 1 : 0);
1045*593dc095SDavid du Colombier     return 0;
1046*593dc095SDavid du Colombier }
1047*593dc095SDavid du Colombier 
1048*593dc095SDavid du Colombier private int
s_image_colors_convert_to_device_color(stream_image_colors_state * ss)1049*593dc095SDavid du Colombier s_image_colors_convert_to_device_color(stream_image_colors_state * ss)
1050*593dc095SDavid du Colombier {
1051*593dc095SDavid du Colombier     gs_client_color cc;
1052*593dc095SDavid du Colombier     gx_device_color dc;
1053*593dc095SDavid du Colombier     int i, code;
1054*593dc095SDavid du Colombier     double v0 = (1 << ss->bits_per_sample) - 1;
1055*593dc095SDavid du Colombier     double v1 = (1 << ss->output_bits_per_sample) - 1;
1056*593dc095SDavid du Colombier 
1057*593dc095SDavid du Colombier     for (i = 0; i < ss->depth; i++)
1058*593dc095SDavid du Colombier 	cc.paint.values[i] = ss->input_color[i] *
1059*593dc095SDavid du Colombier 		(ss->Decode[i * 2 + 1] - ss->Decode[i * 2]) / v0 + ss->Decode[i * 2];
1060*593dc095SDavid du Colombier 
1061*593dc095SDavid du Colombier     code = ss->pcs->type->remap_color(&cc, ss->pcs, &dc, ss->pis,
1062*593dc095SDavid du Colombier 			      ss->pdev, gs_color_select_texture);
1063*593dc095SDavid du Colombier     if (code < 0)
1064*593dc095SDavid du Colombier 	return code;
1065*593dc095SDavid du Colombier     for (i = 0; i < ss->output_depth; i++) {
1066*593dc095SDavid du Colombier 	uint m = (1 << ss->pdev->color_info.comp_bits[i]) - 1;
1067*593dc095SDavid du Colombier 	uint w = (dc.colors.pure >> ss->pdev->color_info.comp_shift[i]) & m;
1068*593dc095SDavid du Colombier 
1069*593dc095SDavid du Colombier 	ss->output_color[i] = (uint)(v1 * w / m + 0.5);
1070*593dc095SDavid du Colombier     }
1071*593dc095SDavid du Colombier     return 0;
1072*593dc095SDavid du Colombier }
1073*593dc095SDavid du Colombier 
1074*593dc095SDavid du Colombier /* Set masc colors dimensions. */
1075*593dc095SDavid du Colombier void
s_image_colors_set_mask_colors(stream_image_colors_state * ss,uint * MaskColor)1076*593dc095SDavid du Colombier s_image_colors_set_mask_colors(stream_image_colors_state * ss, uint *MaskColor)
1077*593dc095SDavid du Colombier {
1078*593dc095SDavid du Colombier     ss->convert_color = s_image_colors_convert_color_to_mask;
1079*593dc095SDavid du Colombier     memcpy(ss->MaskColor, MaskColor, ss->depth * sizeof(MaskColor[0]) * 2);
1080*593dc095SDavid du Colombier }
1081*593dc095SDavid du Colombier 
1082*593dc095SDavid du Colombier /* Set image dimensions. */
1083*593dc095SDavid du Colombier void
s_image_colors_set_dimensions(stream_image_colors_state * ss,int width,int height,int depth,int bits_per_sample)1084*593dc095SDavid du Colombier s_image_colors_set_dimensions(stream_image_colors_state * ss,
1085*593dc095SDavid du Colombier 			       int width, int height, int depth, int bits_per_sample)
1086*593dc095SDavid du Colombier {
1087*593dc095SDavid du Colombier     ss->width = width;
1088*593dc095SDavid du Colombier     ss->height = height;
1089*593dc095SDavid du Colombier     ss->depth = depth;
1090*593dc095SDavid du Colombier     ss->bits_per_sample = bits_per_sample;
1091*593dc095SDavid du Colombier     ss->row_bits = bits_per_sample * depth * width;
1092*593dc095SDavid du Colombier     ss->raster = bitmap_raster(ss->row_bits);
1093*593dc095SDavid du Colombier     ss->row_alignment_bytes = 0; /* (ss->raster * 8 - ss->row_bits) / 8) doesn't work. */
1094*593dc095SDavid du Colombier }
1095*593dc095SDavid du Colombier 
1096*593dc095SDavid du Colombier void
s_image_colors_set_color_space(stream_image_colors_state * ss,gx_device * pdev,const gs_color_space * pcs,const gs_imager_state * pis,float * Decode)1097*593dc095SDavid du Colombier s_image_colors_set_color_space(stream_image_colors_state * ss, gx_device *pdev,
1098*593dc095SDavid du Colombier 			       const gs_color_space *pcs, const gs_imager_state *pis,
1099*593dc095SDavid du Colombier 			       float *Decode)
1100*593dc095SDavid du Colombier {
1101*593dc095SDavid du Colombier     ss->output_depth = pdev->color_info.num_components;
1102*593dc095SDavid du Colombier     ss->output_component_index = ss->output_depth;
1103*593dc095SDavid du Colombier     ss->output_bits_per_sample = pdev->color_info.comp_bits[0]; /* Same precision for all components. */
1104*593dc095SDavid du Colombier     ss->convert_color = s_image_colors_convert_to_device_color;
1105*593dc095SDavid du Colombier     ss->pdev = pdev;
1106*593dc095SDavid du Colombier     ss->pcs = pcs;
1107*593dc095SDavid du Colombier     ss->pis = pis;
1108*593dc095SDavid du Colombier     memcpy(ss->Decode, Decode, ss->depth * sizeof(Decode[0]) * 2);
1109*593dc095SDavid du Colombier }
1110*593dc095SDavid du Colombier 
1111*593dc095SDavid du Colombier 
1112*593dc095SDavid du Colombier /* Process a buffer. */
1113*593dc095SDavid du Colombier private int
s_image_colors_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)1114*593dc095SDavid du Colombier s_image_colors_process(stream_state * st, stream_cursor_read * pr,
1115*593dc095SDavid du Colombier 	     stream_cursor_write * pw, bool last)
1116*593dc095SDavid du Colombier {
1117*593dc095SDavid du Colombier     stream_image_colors_state *const ss = (stream_image_colors_state *) st;
1118*593dc095SDavid du Colombier 
1119*593dc095SDavid du Colombier     for (;;) {
1120*593dc095SDavid du Colombier 	if (pw->ptr >= pw->limit)
1121*593dc095SDavid du Colombier 	    return 1;
1122*593dc095SDavid du Colombier 	if (ss->row_bits_passed >= ss->row_bits) {
1123*593dc095SDavid du Colombier 	    ss->row_alignment_bytes_left = ss->row_alignment_bytes;
1124*593dc095SDavid du Colombier 	    ss->input_bits_buffered = 0;
1125*593dc095SDavid du Colombier 	    ss->input_bits_buffer = 0; /* Just to simplify the debugging. */
1126*593dc095SDavid du Colombier 	    if (ss->output_bits_buffered) {
1127*593dc095SDavid du Colombier 		*(++pw->ptr) = ss->output_bits_buffer;
1128*593dc095SDavid du Colombier 		ss->output_bits_buffered = 0;
1129*593dc095SDavid du Colombier 		ss->output_bits_buffer = 0;
1130*593dc095SDavid du Colombier 	    }
1131*593dc095SDavid du Colombier 	    ss->row_bits_passed = 0;
1132*593dc095SDavid du Colombier 	    continue;
1133*593dc095SDavid du Colombier 	}
1134*593dc095SDavid du Colombier 	if (ss->row_alignment_bytes_left) {
1135*593dc095SDavid du Colombier 	    uint k = pr->limit - pr->ptr;
1136*593dc095SDavid du Colombier 
1137*593dc095SDavid du Colombier 	    if (k > ss->row_alignment_bytes_left)
1138*593dc095SDavid du Colombier 		k = ss->row_alignment_bytes_left;
1139*593dc095SDavid du Colombier 	    pr->ptr += k;
1140*593dc095SDavid du Colombier 	    ss->row_alignment_bytes_left -= k;
1141*593dc095SDavid du Colombier 	    if (pr->ptr >= pr->limit)
1142*593dc095SDavid du Colombier 		return 0;
1143*593dc095SDavid du Colombier 	}
1144*593dc095SDavid du Colombier 	if (ss->output_component_index < ss->output_depth) {
1145*593dc095SDavid du Colombier 	    for (;ss->output_component_index < ss->output_depth;) {
1146*593dc095SDavid du Colombier 		uint fitting = (uint)(8 - ss->output_bits_buffered);
1147*593dc095SDavid du Colombier 		uint v, w, u, n, m;
1148*593dc095SDavid du Colombier 
1149*593dc095SDavid du Colombier 		if (pw->ptr >= pw->limit)
1150*593dc095SDavid du Colombier 		    return 1;
1151*593dc095SDavid du Colombier 		v = ss->output_color[ss->output_component_index];
1152*593dc095SDavid du Colombier 		n = ss->output_bits_per_sample - ss->output_component_bits_written; /* no. of bits left */
1153*593dc095SDavid du Colombier 		w = v - ((v >> n) << n); /* the current component without written bits. */
1154*593dc095SDavid du Colombier 		if (fitting > n)
1155*593dc095SDavid du Colombier 		    fitting = n; /* no. of bits to write. */
1156*593dc095SDavid du Colombier 		m = n - fitting; /* no. of bits will left. */
1157*593dc095SDavid du Colombier 		u = w >> m;  /* bits to write (near lsb). */
1158*593dc095SDavid du Colombier 		ss->output_bits_buffer |= u << (8 - ss->output_bits_buffered - fitting);
1159*593dc095SDavid du Colombier 		ss->output_bits_buffered += fitting;
1160*593dc095SDavid du Colombier 		if (ss->output_bits_buffered >= 8) {
1161*593dc095SDavid du Colombier 		    *(++pw->ptr) = ss->output_bits_buffer;
1162*593dc095SDavid du Colombier 		    ss->output_bits_buffered = 0;
1163*593dc095SDavid du Colombier 		    ss->output_bits_buffer = 0;
1164*593dc095SDavid du Colombier 		}
1165*593dc095SDavid du Colombier 		ss->output_component_bits_written += fitting;
1166*593dc095SDavid du Colombier 		if (ss->output_component_bits_written >= ss->output_bits_per_sample) {
1167*593dc095SDavid du Colombier 		    ss->output_component_index++;
1168*593dc095SDavid du Colombier 		    ss->output_component_bits_written = 0;
1169*593dc095SDavid du Colombier 		}
1170*593dc095SDavid du Colombier 	    }
1171*593dc095SDavid du Colombier 	    ss->row_bits_passed += ss->bits_per_sample * ss->depth;
1172*593dc095SDavid du Colombier 	    continue;
1173*593dc095SDavid du Colombier 	}
1174*593dc095SDavid du Colombier 	if (ss->input_bits_buffered < ss->bits_per_sample) {
1175*593dc095SDavid du Colombier 	    if (pr->ptr >= pr->limit)
1176*593dc095SDavid du Colombier 		return 0;
1177*593dc095SDavid du Colombier 	    ss->input_bits_buffer = (ss->input_bits_buffer << 8) | *++pr->ptr;
1178*593dc095SDavid du Colombier 	    ss->input_bits_buffered += 8;
1179*593dc095SDavid du Colombier 	    /* fixme: delay shifting the input ptr until input_bits_buffer is cleaned. */
1180*593dc095SDavid du Colombier 	}
1181*593dc095SDavid du Colombier 	if (ss->input_bits_buffered >= ss->bits_per_sample) {
1182*593dc095SDavid du Colombier 	    uint w;
1183*593dc095SDavid du Colombier 
1184*593dc095SDavid du Colombier 	    ss->input_bits_buffered -= ss->bits_per_sample;
1185*593dc095SDavid du Colombier 	    ss->input_color[ss->input_component_index] = w = ss->input_bits_buffer >> ss->input_bits_buffered;
1186*593dc095SDavid du Colombier 	    ss->input_bits_buffer &= ~(w << ss->input_bits_buffered);
1187*593dc095SDavid du Colombier 	    ss->input_component_index++;
1188*593dc095SDavid du Colombier 	    if (ss->input_component_index >= ss->depth) {
1189*593dc095SDavid du Colombier 		int code = ss->convert_color(ss);
1190*593dc095SDavid du Colombier 
1191*593dc095SDavid du Colombier 		if (code < 0)
1192*593dc095SDavid du Colombier 		    return ERRC;
1193*593dc095SDavid du Colombier 		ss->output_component_index = 0;
1194*593dc095SDavid du Colombier 		ss->input_component_index = 0;
1195*593dc095SDavid du Colombier 	    }
1196*593dc095SDavid du Colombier 	}
1197*593dc095SDavid du Colombier     }
1198*593dc095SDavid du Colombier }
1199*593dc095SDavid du Colombier 
1200*593dc095SDavid du Colombier const stream_template s__image_colors_template = {
1201*593dc095SDavid du Colombier     &st_stream_image_colors_state, s_image_colors_init, s_image_colors_process, 1, 1,
1202*593dc095SDavid du Colombier     NULL, NULL
1203*593dc095SDavid du Colombier };
1204*593dc095SDavid du Colombier 
1205