xref: /plan9-contrib/sys/src/cmd/gs/src/gxwts.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1*593dc095SDavid du Colombier /* Copyright (C) 2002 artofcode LLC.  All rights reserved.
2*593dc095SDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
5*593dc095SDavid 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.
9*593dc095SDavid 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.
15*593dc095SDavid du Colombier */
16*593dc095SDavid du Colombier /*$Id: gxwts.c,v 1.5 2002/11/02 07:31:45 raph Exp $ */
17*593dc095SDavid du Colombier /* Rendering using Well Tempered Screening. */
18*593dc095SDavid du Colombier #include "stdpre.h"
19*593dc095SDavid du Colombier #include "memory_.h" /* for memcmp */
20*593dc095SDavid du Colombier #include <stdlib.h> /* for malloc */
21*593dc095SDavid du Colombier #include "gx.h"
22*593dc095SDavid du Colombier #include "gxstate.h"
23*593dc095SDavid du Colombier #include "gsht.h"
24*593dc095SDavid du Colombier #include "math_.h"
25*593dc095SDavid du Colombier #include "gserrors.h"
26*593dc095SDavid du Colombier #include "gxdcolor.h"
27*593dc095SDavid du Colombier #include "gxdevcli.h"
28*593dc095SDavid du Colombier #include "gxdht.h"
29*593dc095SDavid du Colombier #include "gxwts.h"
30*593dc095SDavid du Colombier 
31*593dc095SDavid du Colombier #define GXWTS_USE_DOUBLE
32*593dc095SDavid du Colombier 
33*593dc095SDavid du Colombier #ifndef UNIT_TEST
34*593dc095SDavid du Colombier /* device color type for wts. */
35*593dc095SDavid du Colombier 
36*593dc095SDavid du Colombier /* todo: trace and relocate pointers */
37*593dc095SDavid du Colombier gs_private_st_simple(st_dc_wts, gx_device_color, "dc_wts");
38*593dc095SDavid du Colombier private dev_color_proc_save_dc(gx_dc_wts_save_dc);
39*593dc095SDavid du Colombier private dev_color_proc_get_dev_halftone(gx_dc_wts_get_dev_halftone);
40*593dc095SDavid du Colombier private dev_color_proc_load(gx_dc_wts_load);
41*593dc095SDavid du Colombier private dev_color_proc_fill_rectangle(gx_dc_wts_fill_rectangle);
42*593dc095SDavid du Colombier private dev_color_proc_equal(gx_dc_wts_equal);
43*593dc095SDavid du Colombier private dev_color_proc_write(gx_dc_wts_write);
44*593dc095SDavid du Colombier private dev_color_proc_read(gx_dc_wts_read);
45*593dc095SDavid du Colombier private dev_color_proc_get_nonzero_comps(gx_dc_wts_get_nonzero_comps);
46*593dc095SDavid du Colombier const gx_device_color_type_t gx_dc_type_data_wts = {
47*593dc095SDavid du Colombier     &st_dc_wts,
48*593dc095SDavid du Colombier     gx_dc_wts_save_dc, gx_dc_wts_get_dev_halftone,
49*593dc095SDavid du Colombier     gx_dc_ht_get_phase,
50*593dc095SDavid du Colombier     gx_dc_wts_load, gx_dc_wts_fill_rectangle,
51*593dc095SDavid du Colombier     gx_dc_default_fill_masked, gx_dc_wts_equal,
52*593dc095SDavid du Colombier     gx_dc_wts_write, gx_dc_wts_read,
53*593dc095SDavid du Colombier     gx_dc_wts_get_nonzero_comps
54*593dc095SDavid du Colombier };
55*593dc095SDavid du Colombier #undef gx_dc_type_wts
56*593dc095SDavid du Colombier const gx_device_color_type_t *const gx_dc_type_wts =
57*593dc095SDavid du Colombier &gx_dc_type_data_wts;
58*593dc095SDavid du Colombier #endif
59*593dc095SDavid du Colombier 
60*593dc095SDavid du Colombier /* Low-level implementation follows. */
61*593dc095SDavid du Colombier 
62*593dc095SDavid du Colombier /**
63*593dc095SDavid du Colombier  * mul_shr_16: Multiply and shift right 16.
64*593dc095SDavid du Colombier  * @a: 32-bit signed number.
65*593dc095SDavid du Colombier  * @b: 32-bit signed number.
66*593dc095SDavid du Colombier  *
67*593dc095SDavid du Colombier  * Multiply @a and @b, then shift right 16 bits. Allow intermediate value
68*593dc095SDavid du Colombier  * to overflow 32 bits.
69*593dc095SDavid du Colombier  *
70*593dc095SDavid du Colombier  * Return value: result.
71*593dc095SDavid du Colombier  **/
72*593dc095SDavid du Colombier #ifdef GXWTS_USE_DOUBLE
73*593dc095SDavid du Colombier private int
mul_shr_16(int a,int b)74*593dc095SDavid du Colombier mul_shr_16 (int a, int b)
75*593dc095SDavid du Colombier {
76*593dc095SDavid du Colombier   return (int)floor(((double) a) * ((double) b) * (1.0 / (1 << 16)));
77*593dc095SDavid du Colombier }
78*593dc095SDavid du Colombier #else
79*593dc095SDavid du Colombier #error todo: supply mul_shr_16 based on 64 bit integer type
80*593dc095SDavid du Colombier #endif
81*593dc095SDavid du Colombier 
82*593dc095SDavid du Colombier /* Implementation of wts_get_samples for rational cells. */
83*593dc095SDavid du Colombier #if 0
84*593dc095SDavid du Colombier private int
85*593dc095SDavid du Colombier wts_get_samples_rat(const wts_screen_t *ws, int x, int y,
86*593dc095SDavid du Colombier 		    wts_screen_sample_t **samples, int *p_nsamples)
87*593dc095SDavid du Colombier {
88*593dc095SDavid du Colombier     int d = y / ws->cell_height;
89*593dc095SDavid du Colombier     int r = y % ws->cell_height;
90*593dc095SDavid du Colombier     int x_ix = ((d * ws->cell_shift) + x) % ws->cell_width;
91*593dc095SDavid du Colombier     *p_nsamples = ws->cell_width - x_ix;
92*593dc095SDavid du Colombier     *samples = ws->samples + x_ix + r * ws->cell_width;
93*593dc095SDavid du Colombier     return 0;
94*593dc095SDavid du Colombier }
95*593dc095SDavid du Colombier #endif
96*593dc095SDavid du Colombier 
97*593dc095SDavid du Colombier /* Implementation of wts_get_samples for Screen J. */
98*593dc095SDavid du Colombier private int
wts_get_samples_j(const wts_screen_t * ws,int x,int y,wts_screen_sample_t ** samples,int * p_nsamples)99*593dc095SDavid du Colombier wts_get_samples_j(const wts_screen_t *ws, int x, int y,
100*593dc095SDavid du Colombier 		  wts_screen_sample_t **samples, int *p_nsamples)
101*593dc095SDavid du Colombier {
102*593dc095SDavid du Colombier     const wts_screen_j_t *wsj = (const wts_screen_j_t *)ws;
103*593dc095SDavid du Colombier     /* int d = y / ws->cell_height; */
104*593dc095SDavid du Colombier     int y_ix = y;
105*593dc095SDavid du Colombier     int x_ix = x;
106*593dc095SDavid du Colombier     double pad = (wsj->pa) * (1.0 / (1 << 16));
107*593dc095SDavid du Colombier     double pbd = (wsj->pb) * (1.0 / (1 << 16));
108*593dc095SDavid du Colombier     double afrac = x * pad;
109*593dc095SDavid du Colombier     double bfrac = x * pbd;
110*593dc095SDavid du Colombier     int acount = (int)floor(afrac);
111*593dc095SDavid du Colombier     int bcount = (int)floor(bfrac);
112*593dc095SDavid du Colombier     int ccount = mul_shr_16(y, wsj->pc);
113*593dc095SDavid du Colombier     int dcount = mul_shr_16(y, wsj->pd);
114*593dc095SDavid du Colombier     int nsamples;
115*593dc095SDavid du Colombier 
116*593dc095SDavid du Colombier     x_ix += acount * wsj->XA + bcount * wsj->XB +
117*593dc095SDavid du Colombier 	ccount * wsj->XC + dcount * wsj->XD;
118*593dc095SDavid du Colombier     y_ix += acount * wsj->YA + bcount * wsj->YB +
119*593dc095SDavid du Colombier 	ccount * wsj->YC + dcount * wsj->YD;
120*593dc095SDavid du Colombier 
121*593dc095SDavid du Colombier     x_ix += (y_ix / ws->cell_height) * ws->cell_shift;
122*593dc095SDavid du Colombier     x_ix %= ws->cell_width;
123*593dc095SDavid du Colombier     y_ix %= ws->cell_height;
124*593dc095SDavid du Colombier 
125*593dc095SDavid du Colombier     nsamples = ws->cell_width - x_ix;
126*593dc095SDavid du Colombier     if (floor (afrac + (nsamples - 1) * pad) > acount)
127*593dc095SDavid du Colombier 	nsamples = (int)ceil((acount + 1 - afrac) / pad);
128*593dc095SDavid du Colombier 
129*593dc095SDavid du Colombier     if (floor (bfrac + (nsamples - 1) * pbd) > bcount)
130*593dc095SDavid du Colombier 	nsamples = (int)ceil((bcount + 1 - bfrac) / pbd);
131*593dc095SDavid du Colombier #if 0
132*593dc095SDavid du Colombier     printf("get_samples: (%d, %d) -> (%d, %d) %d (cc=%d)\n",
133*593dc095SDavid du Colombier 	   x, y, x_ix, y_ix, nsamples, ccount);
134*593dc095SDavid du Colombier #endif
135*593dc095SDavid du Colombier     *p_nsamples = nsamples;
136*593dc095SDavid du Colombier     *samples = ws->samples + x_ix + y_ix * ws->cell_width;
137*593dc095SDavid du Colombier     return 0;
138*593dc095SDavid du Colombier }
139*593dc095SDavid du Colombier 
140*593dc095SDavid du Colombier private int
wts_screen_h_offset(int x,double p1,int m1,int m2)141*593dc095SDavid du Colombier wts_screen_h_offset(int x, double p1, int m1, int m2)
142*593dc095SDavid du Colombier {
143*593dc095SDavid du Colombier     /* todo: this is a linear search; constant time should be feasible */
144*593dc095SDavid du Colombier     double running_p = 0;
145*593dc095SDavid du Colombier     int width_so_far;
146*593dc095SDavid du Colombier     int this_width;
147*593dc095SDavid du Colombier 
148*593dc095SDavid du Colombier     for (width_so_far = 0;; width_so_far += this_width) {
149*593dc095SDavid du Colombier 	running_p += p1;
150*593dc095SDavid du Colombier 	if (running_p >= 0.5) {
151*593dc095SDavid du Colombier 	    this_width = m1;
152*593dc095SDavid du Colombier 	    running_p -= 1;
153*593dc095SDavid du Colombier 	} else {
154*593dc095SDavid du Colombier 	    this_width = m2;
155*593dc095SDavid du Colombier 	}
156*593dc095SDavid du Colombier 	if (width_so_far + this_width > x)
157*593dc095SDavid du Colombier 	    break;
158*593dc095SDavid du Colombier     }
159*593dc095SDavid du Colombier     return x - width_so_far + (this_width == m1 ? 0 : m1);
160*593dc095SDavid du Colombier }
161*593dc095SDavid du Colombier 
162*593dc095SDavid du Colombier /* Implementation of wts_get_samples for Screen H. */
163*593dc095SDavid du Colombier private int
wts_get_samples_h(const wts_screen_t * ws,int x,int y,wts_screen_sample_t ** samples,int * p_nsamples)164*593dc095SDavid du Colombier wts_get_samples_h(const wts_screen_t *ws, int x, int y,
165*593dc095SDavid du Colombier 		  wts_screen_sample_t **samples, int *p_nsamples)
166*593dc095SDavid du Colombier {
167*593dc095SDavid du Colombier     const wts_screen_h_t *wsh = (const wts_screen_h_t *)ws;
168*593dc095SDavid du Colombier     int x_ix = wts_screen_h_offset(x, wsh->px,
169*593dc095SDavid du Colombier 				   wsh->x1, ws->cell_width - wsh->x1);
170*593dc095SDavid du Colombier     int y_ix = wts_screen_h_offset(y, wsh->py,
171*593dc095SDavid du Colombier 				   wsh->y1, ws->cell_height - wsh->y1);
172*593dc095SDavid du Colombier     *p_nsamples = (x_ix >= wsh->x1 ? ws->cell_width : wsh->x1) - x_ix;
173*593dc095SDavid du Colombier     *samples = ws->samples + x_ix + y_ix * ws->cell_width;
174*593dc095SDavid du Colombier     return 0;
175*593dc095SDavid du Colombier }
176*593dc095SDavid du Colombier 
177*593dc095SDavid du Colombier /**
178*593dc095SDavid du Colombier  * wts_get_samples: Get samples from Well Tempered Screening cell.
179*593dc095SDavid du Colombier  * @ws: Well Tempered Screening cell.
180*593dc095SDavid du Colombier  * @x: X coordinate of starting point.
181*593dc095SDavid du Colombier  * @y: Y coordinate of starting point.
182*593dc095SDavid du Colombier  * @samples: Where to store pointer to samples.
183*593dc095SDavid du Colombier  * @p_nsamples: Where to store number of valid samples.
184*593dc095SDavid du Colombier  *
185*593dc095SDavid du Colombier  * Finds samples from the cell for use in halftoning. On success,
186*593dc095SDavid du Colombier  * @p_nsamples is set to the number of valid samples, ie for 0 <= i <
187*593dc095SDavid du Colombier  * nsamples, samples[i] is a valid sample for coordinate (x + i, y).
188*593dc095SDavid du Colombier  * p_nsamples is guaranteed to at least 1. The samples in @samples
189*593dc095SDavid du Colombier  * are valid for the lifetime of the cell, or until the next garbage
190*593dc095SDavid du Colombier  * collection, whichever comes first.
191*593dc095SDavid du Colombier  *
192*593dc095SDavid du Colombier  * Todo: describe meaning of wts_screen_sample_t (particularly edge
193*593dc095SDavid du Colombier  * cases).
194*593dc095SDavid du Colombier  *
195*593dc095SDavid du Colombier  * Note: may want to add a "cursor" to the api as an optimization. It
196*593dc095SDavid du Colombier  * can wait, though.
197*593dc095SDavid du Colombier  *
198*593dc095SDavid du Colombier  * Return value: 0 on success.
199*593dc095SDavid du Colombier  **/
200*593dc095SDavid du Colombier int
wts_get_samples(const wts_screen_t * ws,int x,int y,wts_screen_sample_t ** samples,int * p_nsamples)201*593dc095SDavid du Colombier wts_get_samples(const wts_screen_t *ws, int x, int y,
202*593dc095SDavid du Colombier 		wts_screen_sample_t **samples, int *p_nsamples)
203*593dc095SDavid du Colombier {
204*593dc095SDavid du Colombier     if (ws->type == WTS_SCREEN_J)
205*593dc095SDavid du Colombier 	return wts_get_samples_j(ws, x, y, samples, p_nsamples);
206*593dc095SDavid du Colombier     if (ws->type == WTS_SCREEN_H)
207*593dc095SDavid du Colombier 	return wts_get_samples_h(ws, x, y, samples, p_nsamples);
208*593dc095SDavid du Colombier     else
209*593dc095SDavid du Colombier 	return -1;
210*593dc095SDavid du Colombier }
211*593dc095SDavid du Colombier 
212*593dc095SDavid du Colombier /* Device color methods follow. */
213*593dc095SDavid du Colombier 
214*593dc095SDavid du Colombier private void
gx_dc_wts_save_dc(const gx_device_color * pdevc,gx_device_color_saved * psdc)215*593dc095SDavid du Colombier gx_dc_wts_save_dc(const gx_device_color * pdevc, gx_device_color_saved * psdc)
216*593dc095SDavid du Colombier {
217*593dc095SDavid du Colombier     psdc->type = pdevc->type;
218*593dc095SDavid du Colombier     memcpy( psdc->colors.wts.levels,
219*593dc095SDavid du Colombier             pdevc->colors.wts.levels,
220*593dc095SDavid du Colombier             sizeof(psdc->colors.wts.levels) );
221*593dc095SDavid du Colombier     psdc->phase = pdevc->phase;
222*593dc095SDavid du Colombier }
223*593dc095SDavid du Colombier 
224*593dc095SDavid du Colombier private const gx_device_halftone *
gx_dc_wts_get_dev_halftone(const gx_device_color * pdevc)225*593dc095SDavid du Colombier gx_dc_wts_get_dev_halftone(const gx_device_color * pdevc)
226*593dc095SDavid du Colombier {
227*593dc095SDavid du Colombier     return pdevc->colors.wts.w_ht;
228*593dc095SDavid du Colombier }
229*593dc095SDavid du Colombier 
230*593dc095SDavid du Colombier private int
gx_dc_wts_load(gx_device_color * pdevc,const gs_imager_state * pis,gx_device * ignore_dev,gs_color_select_t select)231*593dc095SDavid du Colombier gx_dc_wts_load(gx_device_color *pdevc, const gs_imager_state * pis,
232*593dc095SDavid du Colombier 	       gx_device *ignore_dev, gs_color_select_t select)
233*593dc095SDavid du Colombier {
234*593dc095SDavid du Colombier     return 0;
235*593dc095SDavid du Colombier }
236*593dc095SDavid du Colombier 
237*593dc095SDavid du Colombier /**
238*593dc095SDavid du Colombier  * wts_draw: Draw a halftoned shade into a 1 bit deep buffer.
239*593dc095SDavid du Colombier  * @ws: WTS screen.
240*593dc095SDavid du Colombier  * @shade: Gray shade to draw.
241*593dc095SDavid du Colombier  * @data: Destination buffer.
242*593dc095SDavid du Colombier  * @data_raster: Rowstride for destination buffer.
243*593dc095SDavid du Colombier  * @x, @y, @w, @h: coordinates of rectangle to draw.
244*593dc095SDavid du Colombier  *
245*593dc095SDavid du Colombier  * This is close to an implementation of the "draw" method for the
246*593dc095SDavid du Colombier  * gx_ht_order class. Currently, only WTS screens implement this
247*593dc095SDavid du Colombier  * method, and only WTS device colors invoke it. However, implementing
248*593dc095SDavid du Colombier  * this for legacy order objects is probably a good idea, to improve
249*593dc095SDavid du Colombier  * halftoning performance as the cell size scales up.
250*593dc095SDavid du Colombier  *
251*593dc095SDavid du Colombier  * However, it's not exactly an implementation of the "draw" method
252*593dc095SDavid du Colombier  * for the gx_ht_order class because the "self" type would need to be
253*593dc095SDavid du Colombier  * gx_ht_order. Currently, however, device colors don't hold a pointer
254*593dc095SDavid du Colombier  * to the order object. Some amount of refactoring seems to be in
255*593dc095SDavid du Colombier  * order.
256*593dc095SDavid du Colombier  *
257*593dc095SDavid du Colombier  * Return value: 0 on success.
258*593dc095SDavid du Colombier  **/
259*593dc095SDavid du Colombier private int
wts_draw(wts_screen_t * ws,wts_screen_sample_t shade,byte * data,int data_raster,int x,int y,int w,int h)260*593dc095SDavid du Colombier wts_draw(wts_screen_t *ws, wts_screen_sample_t shade,
261*593dc095SDavid du Colombier 	 byte *data, int data_raster,
262*593dc095SDavid du Colombier 	 int x, int y, int w, int h)
263*593dc095SDavid du Colombier {
264*593dc095SDavid du Colombier     int xo, yo;
265*593dc095SDavid du Colombier     unsigned char *line_start = data;
266*593dc095SDavid du Colombier 
267*593dc095SDavid du Colombier     for (yo = 0; yo < h; yo++) {
268*593dc095SDavid du Colombier 	unsigned char *line_ptr = line_start;
269*593dc095SDavid du Colombier 	int mask = 0x80;
270*593dc095SDavid du Colombier 	unsigned char b = 0;
271*593dc095SDavid du Colombier 	int imax;
272*593dc095SDavid du Colombier 
273*593dc095SDavid du Colombier 	for (xo = 0; xo < w; xo += imax) {
274*593dc095SDavid du Colombier 	    wts_screen_sample_t *samples;
275*593dc095SDavid du Colombier 	    int n_samples, i;
276*593dc095SDavid du Colombier 
277*593dc095SDavid du Colombier 	    wts_get_samples(ws, x + xo, y + yo, &samples, &n_samples);
278*593dc095SDavid du Colombier 	    imax = min(w - xo, n_samples);
279*593dc095SDavid du Colombier 	    for (i = 0; i < imax; i++) {
280*593dc095SDavid du Colombier 		if (shade > samples[i])
281*593dc095SDavid du Colombier 		    b |= mask;
282*593dc095SDavid du Colombier 		mask >>= 1;
283*593dc095SDavid du Colombier 		if (mask == 0) {
284*593dc095SDavid du Colombier 		    *line_ptr++ = b;
285*593dc095SDavid du Colombier 		    b = 0;
286*593dc095SDavid du Colombier 		    mask = 0x80;
287*593dc095SDavid du Colombier 		}
288*593dc095SDavid du Colombier 	    }
289*593dc095SDavid du Colombier 	}
290*593dc095SDavid du Colombier 	if (mask != 0x80)
291*593dc095SDavid du Colombier 	    *line_ptr = b;
292*593dc095SDavid du Colombier 	line_start += data_raster;
293*593dc095SDavid du Colombier     }
294*593dc095SDavid du Colombier     return 0;
295*593dc095SDavid du Colombier }
296*593dc095SDavid du Colombier 
297*593dc095SDavid du Colombier /**
298*593dc095SDavid du Colombier  * Special case implementation for one component. When we do plane_mask,
299*593dc095SDavid du Colombier  * we'll want to generalize this to handle any single-bit plane_mask.
300*593dc095SDavid du Colombier  **/
301*593dc095SDavid du Colombier private int
gx_dc_wts_fill_rectangle_1(const gx_device_color * pdevc,int x,int y,int w,int h,gx_device * dev,gs_logical_operation_t lop,const gx_rop_source_t * source)302*593dc095SDavid du Colombier gx_dc_wts_fill_rectangle_1(const gx_device_color *pdevc,
303*593dc095SDavid du Colombier 			   int x, int y, int w, int h,
304*593dc095SDavid du Colombier 			   gx_device *dev, gs_logical_operation_t lop,
305*593dc095SDavid du Colombier 			   const gx_rop_source_t *source)
306*593dc095SDavid du Colombier {
307*593dc095SDavid du Colombier     /* gx_rop_source_t no_source; */
308*593dc095SDavid du Colombier     int tile_raster = ((w + 31) & -32) >> 3;
309*593dc095SDavid du Colombier     int tile_size = tile_raster * h;
310*593dc095SDavid du Colombier     unsigned char *tile_data;
311*593dc095SDavid du Colombier     int code = 0;
312*593dc095SDavid du Colombier     gx_ht_order_component *components = pdevc->colors.wts.w_ht->components;
313*593dc095SDavid du Colombier     wts_screen_t *ws = components[0].corder.wts;
314*593dc095SDavid du Colombier     wts_screen_sample_t shade = pdevc->colors.wts.levels[0];
315*593dc095SDavid du Colombier     gx_color_index color0, color1;
316*593dc095SDavid du Colombier 
317*593dc095SDavid du Colombier     color0 = dev->color_info.separable_and_linear == GX_CINFO_SEP_LIN ? 0 :
318*593dc095SDavid du Colombier 	pdevc->colors.wts.plane_vector[1];
319*593dc095SDavid du Colombier     color1 = pdevc->colors.wts.plane_vector[0];
320*593dc095SDavid du Colombier 
321*593dc095SDavid du Colombier     tile_data = malloc(tile_size);
322*593dc095SDavid du Colombier 
323*593dc095SDavid du Colombier     wts_draw(ws, shade, tile_data, tile_raster, x, y, w, h);
324*593dc095SDavid du Colombier 
325*593dc095SDavid du Colombier     /* See gx_dc_ht_binary_fill_rectangle() for explanation. */
326*593dc095SDavid du Colombier     if (dev->color_info.depth > 1)
327*593dc095SDavid du Colombier 	lop &= ~lop_T_transparent;
328*593dc095SDavid du Colombier 
329*593dc095SDavid du Colombier     /* Interesting question: should data_x be (x & 7), rather than 0,
330*593dc095SDavid du Colombier        to improve alignment? */
331*593dc095SDavid du Colombier     if (source == NULL && lop_no_S_is_T(lop))
332*593dc095SDavid du Colombier 	code = (*dev_proc(dev, copy_mono))
333*593dc095SDavid du Colombier 	    (dev, tile_data, 0, tile_raster, gx_no_bitmap_id,
334*593dc095SDavid du Colombier 	     x, y, w, h, color0, color1);
335*593dc095SDavid du Colombier 
336*593dc095SDavid du Colombier     free(tile_data);
337*593dc095SDavid du Colombier     return code;
338*593dc095SDavid du Colombier }
339*593dc095SDavid du Colombier 
340*593dc095SDavid du Colombier private int
gx_dc_wts_write(const gx_device_color * pdevc,const gx_device_color_saved * psdc,const gx_device * dev,byte * pdata,uint * psize)341*593dc095SDavid du Colombier gx_dc_wts_write(
342*593dc095SDavid du Colombier     const gx_device_color *         pdevc,
343*593dc095SDavid du Colombier     const gx_device_color_saved *   psdc,
344*593dc095SDavid du Colombier     const gx_device *               dev,
345*593dc095SDavid du Colombier     byte *                          pdata,
346*593dc095SDavid du Colombier     uint *                          psize )
347*593dc095SDavid du Colombier {
348*593dc095SDavid du Colombier     /* not yet implemented */
349*593dc095SDavid du Colombier     return_error(gs_error_unknownerror);
350*593dc095SDavid du Colombier }
351*593dc095SDavid du Colombier 
352*593dc095SDavid du Colombier private int
gx_dc_wts_read(gx_device_color * pdevc,const gs_imager_state * pis,const gx_device_color * prior_devc,const gx_device * dev,const byte * pdata,uint size,gs_memory_t * mem)353*593dc095SDavid du Colombier gx_dc_wts_read(
354*593dc095SDavid du Colombier     gx_device_color *       pdevc,
355*593dc095SDavid du Colombier     const gs_imager_state * pis,
356*593dc095SDavid du Colombier     const gx_device_color * prior_devc,
357*593dc095SDavid du Colombier     const gx_device *       dev,
358*593dc095SDavid du Colombier     const byte *            pdata,
359*593dc095SDavid du Colombier     uint                    size,
360*593dc095SDavid du Colombier     gs_memory_t *           mem )
361*593dc095SDavid du Colombier {
362*593dc095SDavid du Colombier     /* not yet implemented */
363*593dc095SDavid du Colombier     return_error(gs_error_unknownerror);
364*593dc095SDavid du Colombier }
365*593dc095SDavid du Colombier 
366*593dc095SDavid du Colombier 
367*593dc095SDavid du Colombier /**
368*593dc095SDavid du Colombier  * wts_repack_tile_4: Repack four 1-bit tiles into chunky nibbles.
369*593dc095SDavid du Colombier  * Note: argument list will change. plane_mask and base_color will
370*593dc095SDavid du Colombier  * probably get added as an optimization.
371*593dc095SDavid du Colombier  *
372*593dc095SDavid du Colombier  * Note: we round w up to an even value. We're counting on the
373*593dc095SDavid du Colombier  * subsequent copy_color to ignore any extra bits.
374*593dc095SDavid du Colombier  **/
375*593dc095SDavid du Colombier private void
wts_repack_tile_4(unsigned char * ctile_data,int ctile_raster,const unsigned char ** tile_data,int tile_raster,const gx_color_index * plane_vector,bool invert,int w,int h)376*593dc095SDavid du Colombier wts_repack_tile_4(unsigned char *ctile_data, int ctile_raster,
377*593dc095SDavid du Colombier 		  const unsigned char **tile_data, int tile_raster,
378*593dc095SDavid du Colombier 		  const gx_color_index *plane_vector, bool invert,
379*593dc095SDavid du Colombier 		  int w, int h)
380*593dc095SDavid du Colombier {
381*593dc095SDavid du Colombier     int y;
382*593dc095SDavid du Colombier     int tile_idx_start = 0;
383*593dc095SDavid du Colombier     unsigned char *ctile_start = ctile_data;
384*593dc095SDavid du Colombier     byte inv_byte = invert ? 0xff : 0;
385*593dc095SDavid du Colombier 
386*593dc095SDavid du Colombier     for (y = 0; y < h; y++) {
387*593dc095SDavid du Colombier 	int x;
388*593dc095SDavid du Colombier 	int tile_idx = tile_idx_start;
389*593dc095SDavid du Colombier 
390*593dc095SDavid du Colombier 	for (x = 0; x < w; x += 2) {
391*593dc095SDavid du Colombier 	    byte b = 0;
392*593dc095SDavid du Colombier 	    byte m0 = 0x80 >> (x & 6);
393*593dc095SDavid du Colombier 	    byte m1 = m0 >> 1;
394*593dc095SDavid du Colombier 	    byte td;
395*593dc095SDavid du Colombier 
396*593dc095SDavid du Colombier 	    td = tile_data[0][tile_idx] ^ inv_byte;
397*593dc095SDavid du Colombier 	    if (td & m0) b |= plane_vector[0] << 4;
398*593dc095SDavid du Colombier 	    if (td & m1) b |= plane_vector[0];
399*593dc095SDavid du Colombier 
400*593dc095SDavid du Colombier 	    td = tile_data[1][tile_idx] ^ inv_byte;
401*593dc095SDavid du Colombier 	    if (td & m0) b |= plane_vector[1] << 4;
402*593dc095SDavid du Colombier 	    if (td & m1) b |= plane_vector[1];
403*593dc095SDavid du Colombier 
404*593dc095SDavid du Colombier 	    td = tile_data[2][tile_idx] ^ inv_byte;
405*593dc095SDavid du Colombier 	    if (td & m0) b |= plane_vector[2] << 4;
406*593dc095SDavid du Colombier 	    if (td & m1) b |= plane_vector[2];
407*593dc095SDavid du Colombier 
408*593dc095SDavid du Colombier 	    td = tile_data[3][tile_idx] ^ inv_byte;
409*593dc095SDavid du Colombier 	    if (td & m0) b |= plane_vector[3] << 4;
410*593dc095SDavid du Colombier 	    if (td & m1) b |= plane_vector[3];
411*593dc095SDavid du Colombier 
412*593dc095SDavid du Colombier 	    if ((x & 6) == 6)
413*593dc095SDavid du Colombier 		tile_idx++;
414*593dc095SDavid du Colombier 	    ctile_start[x >> 1] = b;
415*593dc095SDavid du Colombier 	}
416*593dc095SDavid du Colombier 	tile_idx_start += tile_raster;
417*593dc095SDavid du Colombier 	ctile_start += ctile_raster;
418*593dc095SDavid du Colombier     }
419*593dc095SDavid du Colombier }
420*593dc095SDavid du Colombier 
421*593dc095SDavid du Colombier /* Special case implementation for four components. Intermediate color
422*593dc095SDavid du Colombier  * to the order objecttile (for copy_color) is packed 2 to a byte.
423*593dc095SDavid du Colombier  *
424*593dc095SDavid du Colombier  * Looking at this code, it should generalize to more than four
425*593dc095SDavid du Colombier  * components. Probably the repack code should get factored out.
426*593dc095SDavid du Colombier  */
427*593dc095SDavid du Colombier private int
gx_dc_wts_fill_rectangle_4(const gx_device_color * pdevc,int x,int y,int w,int h,gx_device * dev,gs_logical_operation_t lop,const gx_rop_source_t * source)428*593dc095SDavid du Colombier gx_dc_wts_fill_rectangle_4(const gx_device_color *pdevc,
429*593dc095SDavid du Colombier 			   int x, int y, int w, int h,
430*593dc095SDavid du Colombier 			   gx_device *dev, gs_logical_operation_t lop,
431*593dc095SDavid du Colombier 			   const gx_rop_source_t *source)
432*593dc095SDavid du Colombier {
433*593dc095SDavid du Colombier     int num_comp = pdevc->colors.wts.num_components;
434*593dc095SDavid du Colombier     /* gx_rop_source_t no_source; */
435*593dc095SDavid du Colombier 
436*593dc095SDavid du Colombier     int tile_raster = ((w + 31) & -32) >> 3;
437*593dc095SDavid du Colombier     int tile_size = tile_raster * h;
438*593dc095SDavid du Colombier     unsigned char *tile_data[4];
439*593dc095SDavid du Colombier 
440*593dc095SDavid du Colombier     int ctile_raster = ((w + 7) & -8) >> 1;
441*593dc095SDavid du Colombier     int ctile_size = ctile_raster * h;
442*593dc095SDavid du Colombier     unsigned char *ctile_data;
443*593dc095SDavid du Colombier 
444*593dc095SDavid du Colombier     int code = 0;
445*593dc095SDavid du Colombier     bool invert = 0 && dev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE;
446*593dc095SDavid du Colombier     int i;
447*593dc095SDavid du Colombier 
448*593dc095SDavid du Colombier     for (i = 0; i < num_comp; i++) {
449*593dc095SDavid du Colombier 	wts_screen_sample_t shade = pdevc->colors.wts.levels[i];
450*593dc095SDavid du Colombier 	gx_ht_order_component *components = pdevc->colors.wts.w_ht->components;
451*593dc095SDavid du Colombier 	wts_screen_t *ws = components[i].corder.wts;
452*593dc095SDavid du Colombier 
453*593dc095SDavid du Colombier 	tile_data[i] = malloc(tile_size);
454*593dc095SDavid du Colombier 	wts_draw(ws, shade, tile_data[i], tile_raster, x, y, w, h);
455*593dc095SDavid du Colombier     }
456*593dc095SDavid du Colombier 
457*593dc095SDavid du Colombier     ctile_data = malloc(ctile_size);
458*593dc095SDavid du Colombier     wts_repack_tile_4(ctile_data, ctile_raster,
459*593dc095SDavid du Colombier     		  (const unsigned char **)tile_data, tile_raster,
460*593dc095SDavid du Colombier 		      pdevc->colors.wts.plane_vector, invert, w, h);
461*593dc095SDavid du Colombier 
462*593dc095SDavid du Colombier     /* See gx_dc_ht_binary_fill_rectangle() for explanation. */
463*593dc095SDavid du Colombier     if (dev->color_info.depth > 1)
464*593dc095SDavid du Colombier 	lop &= ~lop_T_transparent;
465*593dc095SDavid du Colombier 
466*593dc095SDavid du Colombier     if (source == NULL && lop_no_S_is_T(lop))
467*593dc095SDavid du Colombier 	code = (*dev_proc(dev, copy_color))
468*593dc095SDavid du Colombier 	    (dev, ctile_data, 0, ctile_raster, gx_no_bitmap_id,
469*593dc095SDavid du Colombier 	     x, y, w, h);
470*593dc095SDavid du Colombier 
471*593dc095SDavid du Colombier     free(ctile_data);
472*593dc095SDavid du Colombier     for (i = 0; i < num_comp; i++) {
473*593dc095SDavid du Colombier 	free(tile_data[i]);
474*593dc095SDavid du Colombier     }
475*593dc095SDavid du Colombier 
476*593dc095SDavid du Colombier     return code;
477*593dc095SDavid du Colombier }
478*593dc095SDavid du Colombier 
479*593dc095SDavid du Colombier private int
gx_dc_wts_fill_rectangle(const gx_device_color * pdevc,int x,int y,int w,int h,gx_device * dev,gs_logical_operation_t lop,const gx_rop_source_t * source)480*593dc095SDavid du Colombier gx_dc_wts_fill_rectangle(const gx_device_color *pdevc,
481*593dc095SDavid du Colombier 			 int x, int y, int w, int h,
482*593dc095SDavid du Colombier 			 gx_device *dev, gs_logical_operation_t lop,
483*593dc095SDavid du Colombier 			 const gx_rop_source_t *source)
484*593dc095SDavid du Colombier {
485*593dc095SDavid du Colombier     int num_comp = pdevc->colors.wts.num_components;
486*593dc095SDavid du Colombier 
487*593dc095SDavid du Colombier     if (num_comp == 1)
488*593dc095SDavid du Colombier 	return gx_dc_wts_fill_rectangle_1(pdevc, x, y, w, h, dev, lop, source);
489*593dc095SDavid du Colombier     else if (num_comp <= 4)
490*593dc095SDavid du Colombier 	return gx_dc_wts_fill_rectangle_4(pdevc, x, y, w, h, dev, lop, source);
491*593dc095SDavid du Colombier     else
492*593dc095SDavid du Colombier 	return -1;
493*593dc095SDavid du Colombier }
494*593dc095SDavid du Colombier 
495*593dc095SDavid du Colombier /* Compare two wts colors for equality. */
496*593dc095SDavid du Colombier private int
gx_dc_wts_equal(const gx_device_color * pdevc1,const gx_device_color * pdevc2)497*593dc095SDavid du Colombier gx_dc_wts_equal(const gx_device_color *pdevc1,
498*593dc095SDavid du Colombier 		const gx_device_color *pdevc2)
499*593dc095SDavid du Colombier {
500*593dc095SDavid du Colombier     uint num_comp = pdevc1->colors.wts.num_components;
501*593dc095SDavid du Colombier 
502*593dc095SDavid du Colombier     if (pdevc2->type != pdevc1->type ||
503*593dc095SDavid du Colombier 	pdevc1->phase.x != pdevc2->phase.x ||
504*593dc095SDavid du Colombier 	pdevc1->phase.y != pdevc2->phase.y ||
505*593dc095SDavid du Colombier 	num_comp != pdevc2->colors.wts.num_components
506*593dc095SDavid du Colombier 	)
507*593dc095SDavid du Colombier 	return false;
508*593dc095SDavid du Colombier     return
509*593dc095SDavid du Colombier 	!memcmp(pdevc1->colors.wts.levels,
510*593dc095SDavid du Colombier 		pdevc2->colors.wts.levels,
511*593dc095SDavid du Colombier 		num_comp * sizeof(pdevc1->colors.wts.levels[0]));
512*593dc095SDavid du Colombier }
513*593dc095SDavid du Colombier 
514*593dc095SDavid du Colombier /*
515*593dc095SDavid du Colombier  * Get the nonzero components of a wts halftone. This is used to
516*593dc095SDavid du Colombier  * distinguish components that are given zero intensity due to halftoning
517*593dc095SDavid du Colombier  * from those for which the original color intensity was in fact zero.
518*593dc095SDavid du Colombier  */
519*593dc095SDavid du Colombier int
gx_dc_wts_get_nonzero_comps(const gx_device_color * pdevc,const gx_device * dev_ignored,gx_color_index * pcomp_bits)520*593dc095SDavid du Colombier gx_dc_wts_get_nonzero_comps(
521*593dc095SDavid du Colombier     const gx_device_color * pdevc,
522*593dc095SDavid du Colombier     const gx_device *       dev_ignored,
523*593dc095SDavid du Colombier     gx_color_index *        pcomp_bits )
524*593dc095SDavid du Colombier {
525*593dc095SDavid du Colombier     int                     i, ncomps =  pdevc->colors.wts.num_components;
526*593dc095SDavid du Colombier     gx_color_index comp_bits = 0; /* todo: plane_mask */
527*593dc095SDavid du Colombier 
528*593dc095SDavid du Colombier     for (i = 0; i < ncomps; i++) {
529*593dc095SDavid du Colombier         if (pdevc->colors.wts.levels[i] != 0)
530*593dc095SDavid du Colombier             comp_bits |= ((gx_color_index)1) << i;
531*593dc095SDavid du Colombier     }
532*593dc095SDavid du Colombier     *pcomp_bits = comp_bits;
533*593dc095SDavid du Colombier 
534*593dc095SDavid du Colombier     return 0;
535*593dc095SDavid du Colombier }
536