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