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