1 /* Copyright (C) 2006-2007 Artifex Software, Inc. 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
17 /* $Id: gdevwts.c 10078 2009-09-21 19:38:30Z ray $ */
18 /* ALPHA: Sample Device that provides WTS screening and IMDI color management */
19 /* TODO: this should be configurable */
20 #define LINK_ICC_NAME "link.icc"
21
22 #include "errno_.h"
23 #include "string_.h"
24
25 #include "gdevprn.h"
26 #include "gdevmem.h"
27 #include "gsdevice.h"
28 #include "gxfrac.h"
29 #include "gsht.h"
30 #include "gxwts.h"
31 #include "gswts.h"
32 #include "gxgetbit.h"
33
34 #include "icc.h"
35 #include "imdi.h"
36
37 /* Memory arg is included in ghostpcl branch but not main branch. */
38 #define GS_NOTE_ERROR(m, e) gs_note_error(e)
39
40 #ifndef X_DPI
41 # define X_DPI 72
42 #endif
43 #ifndef Y_DPI
44 # define Y_DPI 72
45 #endif
46
47 typedef struct {
48 wts_screen_t *wts;
49 byte *cell;
50 int width_padded;
51 } wts_cooked_halftone;
52
53 typedef struct gx_device_wts_s {
54 gx_device_common;
55 gx_prn_device_common;
56 wts_cooked_halftone wcooked[4];
57 } gx_device_wts;
58
59 static dev_proc_print_page(wtscmyk_print_page);
60
61 /* 8-bit-per-plane separated CMYK color. */
62
63 static const gx_device_procs wtscmyk_procs = {
64 gdev_prn_open, NULL, NULL, gdev_prn_output_page, gdev_prn_close,
65 NULL, cmyk_8bit_map_color_cmyk, NULL, NULL, NULL, NULL, NULL, NULL,
66 gdev_prn_get_params, gdev_prn_put_params,
67 cmyk_8bit_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
68 };
69
70 const gx_device_wts gs_wtscmyk_device = {
71 prn_device_body(gx_device_wts, wtscmyk_procs, "wtscmyk",
72 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
73 X_DPI, Y_DPI,
74 0, 0, 0, 0, /* Margins */
75 4, 32, 255, 255, 256, 256, wtscmyk_print_page)
76 };
77
78 /* RGB with imdi conversion to CMYK and wts halftoning */
79
80 static dev_proc_open_device(wtsimdi_open_device);
81 static dev_proc_close_device(wtsimdi_close_device);
82 static dev_proc_print_page(wtsimdi_print_page);
83 static dev_proc_create_buf_device(wtsimdi_create_buf_device);
84 static dev_proc_get_bits(wtsimdi_get_bits);
85 static dev_proc_get_bits_rectangle(wtsimdi_contone_get_bits_rectangle);
86 static dev_proc_get_bits_rectangle(wtsimdi_halftoned_get_bits_rectangle);
87
88 typedef struct cached_color_s {
89 gx_color_index color_index;
90 byte cmyk[4];
91 } cached_color;
92
93 #define COLOR_CACHE_SIZE 4096
94 /*
95 * Hash function should preserve low bits (for images) and MUST map white and
96 * black to different slots
97 */
98 #if COLOR_CACHE_SIZE == 256
99 # define COLOR_TO_CACHE_INDEX(color) (((color + 0x000001) ^ (color >> 8) ^ (color >> 16)) & 0xff)
100 #elif COLOR_CACHE_SIZE == 4096
101 # define COLOR_TO_CACHE_INDEX(color) ((color ^ (color>>4) ^ (color>>8)) & 0xfff)
102 #elif COLOR_CACHE_SIZE == 8192
103 # define COLOR_TO_CACHE_INDEX(color) (((color + 0x010204) ^ (((color << 24) | color) >> 11)) & 0x1fff)
104 #elif COLOR_CACHE_SIZE == 16384
105 # define COLOR_TO_CACHE_INDEX(color) (((color + 0x010204) ^ (((color << 24) | color) >> 10)) & 0x3fff)
106 #elif COLOR_CACHE_SIZE == 32768
107 # define COLOR_TO_CACHE_INDEX(color) (((color + 0x010204) ^ (((color << 24) | color) >> 9)) & 0x7fff)
108 #elif COLOR_CACHE_SIZE == 65536
109 # define COLOR_TO_CACHE_INDEX(color) (((color + 0x010204) ^ (((color << 24) | color) >> 8)) & 0xffff)
110 #else
111 # define COLOR_TO_CACHE_INDEX(color) 0
112 #endif
113
114 typedef struct gx_device_wtsimdi_s {
115 gx_device_common;
116 gx_prn_device_common;
117 wts_cooked_halftone wcooked[4];
118
119 icmFile *fp;
120 icc *icco;
121 icmLuBase *luo;
122 imdi *mdo;
123 cached_color *color_cache;
124 cached_color current_color;
125 #ifdef DEBUG
126 long color_cache_hit, color_cache_collision, cache_fill_empty, color_is_current;
127 #endif
128 } gx_device_wtsimdi;
129
130 static const gx_device_procs wtsimdi_procs =
131 {
132 wtsimdi_open_device, NULL, NULL, gdev_prn_output_page, wtsimdi_close_device,
133 gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
134 NULL, NULL, NULL, NULL, NULL, wtsimdi_get_bits,
135 gdev_prn_get_params, gdev_prn_put_params,
136 NULL, NULL, NULL, NULL, gx_page_device_get_page_device
137 };
138
139 const gx_device_wtsimdi gs_wtsimdi_device = {
140 prn_device_body(gx_device_wtsimdi, wtsimdi_procs, "wtsimdi",
141 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
142 X_DPI, Y_DPI,
143 0, 0, 0, 0, /* Margins */
144 3, 24, 255, 255, 256, 256, wtsimdi_print_page)
145 };
146
147 #if DUMMY_WTS_HALFTONE_LINE
148 static void
wts_halftone_line(void ** wts,int y,int width,int n_planes,long band_offset_x,long band_offset_y,byte ** dst,const byte * src)149 wts_halftone_line(void **wts, int y, int width, int n_planes,
150 long band_offset_x, long band_offset_y,
151 byte **dst, const byte *src)
152 {
153 int x;
154 int plane_ix;
155
156 for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
157 byte *dline = dst[plane_ix];
158 for (x = 0; x < width; x += 8) {
159 byte b = 0;
160 if (src[(x + 0) * n_planes + plane_ix] > 0x20) b |= 0x80;
161 if (src[(x + 1) * n_planes + plane_ix] > 0x40) b |= 0x40;
162 if (src[(x + 2) * n_planes + plane_ix] > 0x60) b |= 0x20;
163 if (src[(x + 3) * n_planes + plane_ix] > 0x80) b |= 0x10;
164 if (src[(x + 4) * n_planes + plane_ix] > 0xa0) b |= 0x08;
165 if (src[(x + 5) * n_planes + plane_ix] > 0xc0) b |= 0x04;
166 if (src[(x + 6) * n_planes + plane_ix] > 0xe0) b |= 0x02;
167 if (src[(x + 7) * n_planes + plane_ix] > 0xfe) b |= 0x01;
168 dline[x >> 3] = b;
169 }
170 }
171 }
172 #endif
173
174 static void
wts_halftone_line_16(wts_cooked_halftone * wch,int y,int width,int n_planes,long band_offset_x,long band_offset_y,byte ** dst,const byte * src)175 wts_halftone_line_16(wts_cooked_halftone *wch, int y, int width, int n_planes,
176 long band_offset_x, long band_offset_y,
177 byte **dst, const byte *src)
178 {
179 int x;
180 int plane_ix;
181 wts_screen_sample_t *samples;
182
183 for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
184 wts_screen_t *w = wch[plane_ix].wts;
185 byte *dline = dst[plane_ix];
186 int imax;
187
188 for (x = 0; x < width;) {
189 int i;
190 int n_samples;
191 int cx, cy;
192
193 wts_get_samples(w, band_offset_x + x, band_offset_y + y, &cx, &cy, &n_samples);
194 samples = w->samples + cy * w->cell_width + cx;
195
196 imax = min(width - x, n_samples);
197 for (i = 0; i < imax; i += 8) {
198 byte b = 0;
199 int src_ix = x * 4 + plane_ix;
200 #if 0
201 if (src[src_ix + 0 * 4] > (samples[i + 0] >> 7)) b |= 0x80;
202 if (src[src_ix + 1 * 4] > (samples[i + 1] >> 7)) b |= 0x40;
203 if (src[src_ix + 2 * 4] > (samples[i + 2] >> 7)) b |= 0x20;
204 if (src[src_ix + 3 * 4] > (samples[i + 3] >> 7)) b |= 0x10;
205 if (src[src_ix + 4 * 4] > (samples[i + 4] >> 7)) b |= 0x08;
206 if (src[src_ix + 5 * 4] > (samples[i + 5] >> 7)) b |= 0x04;
207 if (src[src_ix + 6 * 4] > (samples[i + 6] >> 7)) b |= 0x02;
208 if (src[src_ix + 7 * 4] > (samples[i + 7] >> 7)) b |= 0x01;
209 #else
210 #if 0
211 b |= (src[src_ix + 0 * 4] > (samples[i + 0] >> 7)) << 7;
212 b |= (src[src_ix + 1 * 4] > (samples[i + 1] >> 7)) << 6;
213 b |= (src[src_ix + 2 * 4] > (samples[i + 2] >> 7)) << 5;
214 b |= (src[src_ix + 3 * 4] > (samples[i + 3] >> 7)) << 4;
215 b |= (src[src_ix + 4 * 4] > (samples[i + 4] >> 7)) << 3;
216 b |= (src[src_ix + 5 * 4] > (samples[i + 5] >> 7)) << 2;
217 b |= (src[src_ix + 6 * 4] > (samples[i + 6] >> 7)) << 1;
218 b |= (src[src_ix + 7 * 4] > (samples[i + 7] >> 7)) << 0;
219 #else
220 b = (((unsigned int)(((int)(samples[i + 0] >> 7)) - ((int)src[src_ix + 0 * 4]))) >> 24) & 0x80;
221 b |= (((unsigned int)(((int)(samples[i + 1] >> 7)) - ((int)src[src_ix + 1 * 4]))) >> 24) & 0x40;
222 b |= (((unsigned int)(((int)(samples[i + 2] >> 7)) - ((int)src[src_ix + 2 * 4]))) >> 24) & 0x20;
223 b |= (((unsigned int)(((int)(samples[i + 3] >> 7)) - ((int)src[src_ix + 3 * 4]))) >> 24) & 0x10;
224 b |= (((unsigned int)(((int)(samples[i + 4] >> 7)) - ((int)src[src_ix + 4 * 4]))) >> 24) & 0x08;
225 b |= (((unsigned int)(((int)(samples[i + 5] >> 7)) - ((int)src[src_ix + 5 * 4]))) >> 24) & 0x04;
226 b |= (((unsigned int)(((int)(samples[i + 6] >> 7)) - ((int)src[src_ix + 6 * 4]))) >> 24) & 0x02;
227 b |= (((unsigned int)(((int)(samples[i + 7] >> 7)) - ((int)src[src_ix + 7 * 4]))) >> 24) & 0x01;
228 #endif
229 #endif
230 dline[x >> 3] = b;
231 x += 8;
232 }
233 }
234 }
235 }
236
237 static void
wts_halftone_line_8(wts_cooked_halftone * wch,int y,int width,int n_planes,long band_offset_x,long band_offset_y,byte * dst,const byte * src)238 wts_halftone_line_8(wts_cooked_halftone *wch, int y, int width, int n_planes,
239 long band_offset_x, long band_offset_y,
240 byte * dst, const byte * src)
241 {
242 int x;
243 int plane_ix;
244 byte *samples;
245 int halftoned_bytes = (width + 7) >> 3;
246
247 for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
248 wts_screen_t *w = wch[plane_ix].wts;
249 int width_padded = wch[plane_ix].width_padded;
250 byte * dline = dst + plane_ix * halftoned_bytes;
251 /*byte * dline = dst[plane_ix];*/
252 int imax;
253
254 for (x = 0; x < width;) {
255 int i;
256 int n_samples;
257 int cx, cy;
258
259 wts_get_samples(w, band_offset_x + x, band_offset_y + y, &cx, &cy, &n_samples);
260 samples = wch[plane_ix].cell + cy * width_padded + cx;
261
262 imax = min(width - x, n_samples);
263 for (i = 0; i < imax; i += 8) {
264 byte b = 0;
265 int src_ix = x * 4 + plane_ix;
266 b = (((unsigned int)(((int)(samples[i + 0])) - ((int)src[src_ix + 0 * 4]))) >> 24) & 0x80;
267 b |= (((unsigned int)(((int)(samples[i + 1])) - ((int)src[src_ix + 1 * 4]))) >> 24) & 0x40;
268 b |= (((unsigned int)(((int)(samples[i + 2])) - ((int)src[src_ix + 2 * 4]))) >> 24) & 0x20;
269 b |= (((unsigned int)(((int)(samples[i + 3])) - ((int)src[src_ix + 3 * 4]))) >> 24) & 0x10;
270 b |= (((unsigned int)(((int)(samples[i + 4])) - ((int)src[src_ix + 4 * 4]))) >> 24) & 0x08;
271 b |= (((unsigned int)(((int)(samples[i + 5])) - ((int)src[src_ix + 5 * 4]))) >> 24) & 0x04;
272 b |= (((unsigned int)(((int)(samples[i + 6])) - ((int)src[src_ix + 6 * 4]))) >> 24) & 0x02;
273 b |= (((unsigned int)(((int)(samples[i + 7])) - ((int)src[src_ix + 7 * 4]))) >> 24) & 0x01;
274 *dline++ = b;
275 x += 8;
276 }
277 }
278 }
279 }
280
281 static int
wts_load_halftone(gs_memory_t * mem,wts_cooked_halftone * wch,const char * fn)282 wts_load_halftone(gs_memory_t *mem, wts_cooked_halftone *wch, const char *fn)
283 {
284 FILE *f = fopen(fn, "rb");
285 int size;
286 byte *buf;
287 wts_screen_t *wts;
288 int width_padded;
289 byte *cooked_cell;
290 int y;
291
292 if (f == 0) return gs_error_undefinedfilename;
293 fseek(f, 0, 2);
294 size = ftell(f);
295 fseek(f, 0, 0);
296 buf = gs_malloc(mem, size, 1, "wts_load_halftone");
297 if (buf == 0) {
298 return gs_error_VMerror;
299 }
300 fread(buf, 1, size, f);
301 fclose(f);
302 wts = gs_wts_from_buf(buf, size);
303 gs_free(mem, buf, size, 1, "wts_load_halftone");
304 wch->wts = wts;
305 width_padded = wts->cell_width + 7;
306 wch->width_padded = width_padded;
307 cooked_cell = gs_malloc(mem, width_padded * wts->cell_height, 1,
308 "wts_load_halftone");
309 if (cooked_cell == 0) {
310 return gs_error_VMerror;
311 }
312
313 wch->cell = cooked_cell;
314 for (y = 0; y < wts->cell_height; y++) {
315 wts_screen_sample_t *line = &wts->samples[y * wts->cell_width];
316 byte *dstline = cooked_cell + y * width_padded;
317 int x;
318 for (x = 0; x < width_padded; x++) {
319 wts_screen_sample_t sample = line[x % wts->cell_width];
320 dstline[x] = (254 * (int)sample + 0x7fc0) / 0x8ff0;
321 }
322 }
323
324 #if 0
325 /* Note: we'll need to fix the free discipline here when we change it
326 in gswts.c */
327 free(wts->samples);
328 wts->samples = NULL;
329 #endif
330
331 return 0;
332 }
333
334 static int
wts_init_halftones(gx_device_wts * wdev,int n_planes)335 wts_init_halftones(gx_device_wts *wdev, int n_planes)
336 {
337 int i;
338 int code;
339
340 for (i = 0; i < n_planes; i++) {
341 if (wdev->wcooked[i].wts == 0) {
342 char wts_fn[256];
343
344 sprintf(wts_fn, "wts_plane_%d", i);
345 {
346 FILE *f;
347 if ((f=fopen(wts_fn,"r"))) {
348 fclose(f);
349 } else {
350 sprintf(wts_fn, "/usr/local/lib/ghostscript/wts_plane_%d", i);
351 }
352 }
353 code = wts_load_halftone(wdev->memory, &wdev->wcooked[i], wts_fn);
354 if (code < 0)
355 return gs_throw1(code, "could not open file '%s'", wts_fn);
356 }
357 }
358 return 0;
359 }
360
361 static int
wtscmyk_print_page(gx_device_printer * pdev,FILE * prn_stream)362 wtscmyk_print_page(gx_device_printer *pdev, FILE *prn_stream)
363 {
364 gx_device_wts *wdev = (gx_device_wts *)pdev;
365 int cmyk_bytes = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
366 /* Output bytes have to be padded to 16 bits. */
367 int y;
368 byte *cmyk_line = 0;
369 byte *data;
370 int code = 0;
371 int pbm_bytes;
372 int n_planes = pdev->color_info.num_components;
373 byte *dst = NULL;
374 FILE *ostream[4] = {0};
375 int i;
376
377 /* Initialize the wts halftones. */
378 code = wts_init_halftones(wdev, n_planes);
379 if (code < 0) goto out;
380
381 cmyk_line = (byte *)gs_malloc(pdev->memory, cmyk_bytes, 1, "wtscmyk_print_page(in)");
382 if (cmyk_line == 0) {
383 code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
384 goto out;
385 }
386 pbm_bytes = (pdev->width + 7) >> 3;
387 dst = gs_malloc(pdev->memory, pbm_bytes * n_planes, 1,
388 "wtscmyk_print_page");
389 if (dst == 0) {
390 code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
391 goto out;
392 }
393
394 /* Create additional output streams. */
395 for (i = 0; i < n_planes; i++) {
396 if (i == 0) {
397 ostream[i] = prn_stream;
398 } else {
399 char fn[256];
400 char plane_name[4] = "cmyk";
401 int fname_len = strlen(pdev->fname);
402
403 if (fname_len >= 5 && fname_len < 256) {
404 strcpy(fn, pdev->fname);
405 if (!strcmp(fn + fname_len - 5, "c.pbm"))
406 fn[fname_len - 5] = plane_name[i];
407 }
408 ostream[i] = fopen(fn, "wb");
409 }
410 fprintf(ostream[i], "P4\n%d %d\n", pdev->width, pdev->height);
411 }
412 #if 0
413 dprintf2(OPTIONAL_MEM(pdev->memory) "Output file name: %s %d\n", pdev->fname, sizeof(dst));
414 #endif
415
416 /* For each raster line */
417 for (y = 0; y < pdev->height; y++) {
418 code = gdev_prn_get_bits(pdev, y, cmyk_line, &data);
419 if (code < 0)
420 break; /* return the code below after cleanup */
421 wts_halftone_line_8(wdev->wcooked, y, pdev->width, n_planes,
422 wdev->band_offset_x, wdev->band_offset_y, dst, data);
423 for (i = 0; i < n_planes; i++)
424 if (ostream[i])
425 fwrite(dst + i * pbm_bytes, 1, pbm_bytes, ostream[i]);
426 }
427 out:
428 /* Clean up... */
429 gs_free(pdev->memory, cmyk_line, cmyk_bytes, 1, "wtscmyk_print_page(in)");
430 gs_free(pdev->memory, dst, pbm_bytes, 1, "wtscmyk_print_page");
431 for (i = 1; i < n_planes; i++) {
432 /* Don't close ostream[0], because gdev_prn_close will. */
433 if (ostream[i])
434 fclose(ostream[i]);
435 }
436 return code;
437 }
438
439 /* Code that follows is adapted from imdi device */
440
incurve(void * ctx,int ch,double val)441 static double incurve(void *ctx, int ch, double val)
442 {
443 return val;
444 }
445
outcurve(void * ctx,int ch,double val)446 static double outcurve(void *ctx, int ch, double val)
447 {
448 return val;
449 }
450
mdtable(void * ctx,double * outvals,double * invals)451 static void mdtable(void *ctx, double *outvals, double *invals)
452 {
453 icmLuBase *luo = ctx;
454 luo->lookup(luo, outvals, invals);
455 }
456
457 /*
458 * Open IMDI device.
459 * Load ICC device link profile (to map sRGB to FOGRA CMYK).
460 */
461
462 static int
wtsimdi_open_device(gx_device * dev)463 wtsimdi_open_device(gx_device *dev)
464 {
465 gx_device_wtsimdi *idev = (gx_device_wtsimdi*)dev;
466 int i, code;
467
468 icColorSpaceSignature ins, outs;
469 int inn, outn;
470 icmLuAlgType alg;
471
472 icmFile *fp;
473 icc *icco;
474 icmLuBase *luo;
475 imdi *mdo;
476 char link_icc_name[256];
477
478 /*
479 * We replace create_buf_device so we can replace copy_alpha
480 * for memory device, but not clist.
481 */
482 idev->printer_procs.buf_procs.create_buf_device =
483 wtsimdi_create_buf_device;
484 /* Open and read profile */
485
486 sprintf(link_icc_name, "%s", LINK_ICC_NAME);
487 {
488 FILE *f;
489 if ((f=fopen(link_icc_name,"r"))) {
490 fclose(f);
491 } else {
492 sprintf(link_icc_name, "/usr/local/lib/ghostscript/%s", LINK_ICC_NAME);
493 }
494 }
495
496 fp = new_icmFileStd_name((char *)link_icc_name, (char *)"rb");
497 if (!fp)
498 return gs_throw1(-1, "could not open file '%s'", link_icc_name);
499
500 icco = new_icc();
501 if (!icco)
502 return gs_throw(-1, "could not create ICC object");
503
504 code = icco->read(icco, fp, 0);
505 if (code != 0)
506 return gs_throw1(-1, "could not read ICC profile: %s", icco->err);
507
508 /* Get conversion object */
509
510 luo = icco->get_luobj(icco, icmFwd, icPerceptual, icmSigDefaultData, icmLuOrdNorm);
511 if (!luo)
512 return gs_throw1(-1, "could not create ICC conversion object: %s", icco->err);
513
514 luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL);
515
516 #ifdef DEBUG
517 dprintf3("%s -> %s [%s]\n",
518 icm2str(icmColorSpaceSignature, ins),
519 icm2str(icmColorSpaceSignature, outs),
520 icm2str(icmLuAlg, alg));
521 #endif
522
523 if (inn != 3)
524 return gs_throw1(-1, "profile must have 3 input channels. got %d.", inn);
525 if (outn != 4)
526 return gs_throw1(-1, "profile must have 4 output channels. got %d.", outn);
527
528 /* Create IMDI optimized lookup object */
529
530 mdo = new_imdi(inn, outn, pixint8, 0, pixint8, 0,
531 33, incurve, mdtable, outcurve, luo);
532 if (!mdo)
533 return gs_throw(-1, "new_imdi failed");
534
535 idev->fp = fp;
536 idev->icco = icco;
537 idev->luo = luo;
538 idev->mdo = mdo;
539 /* allocate at least 1 for sytems that return NULL if requested count is 0 */
540 idev->color_cache = (cached_color *)gs_malloc(idev->memory, max(COLOR_CACHE_SIZE, 1),
541 sizeof(cached_color), "wtsimdi_open_device(color_cache)");
542 if (idev->color_cache == NULL) {
543 return_error(gs_error_VMerror);
544 }
545 for (i=0; i<COLOR_CACHE_SIZE; i++) /* clear cache to empty */
546 idev->color_cache[i].color_index = gx_no_color_index;
547 idev->current_color.color_index = gx_no_color_index;
548
549 /* guarantee the device bands */
550 ((gx_device_printer *)dev)->space_params.banding_type = BandingAlways;
551 return gdev_prn_open(dev);
552 }
553
554
555 /*
556 * Close device and clean up ICC structures.
557 */
558
559 static int
wtsimdi_close_device(gx_device * dev)560 wtsimdi_close_device(gx_device *dev)
561 {
562 gx_device_wtsimdi *idev = (gx_device_wtsimdi*)dev;
563
564 idev->mdo->done(idev->mdo);
565 idev->luo->del(idev->luo);
566 idev->icco->del(idev->icco);
567 idev->fp->del(idev->fp);
568 gs_free(dev->memory, idev->color_cache, COLOR_CACHE_SIZE,
569 sizeof(cached_color), "wtsimdi_close_device(color_cache)");
570 return gdev_prn_close(dev);
571 }
572
573 /* Resolve a color to cmyk values, using the one-element cache. */
574 static int
wtsimdi_resolve_one(gx_device_wtsimdi * idev,gx_color_index color)575 wtsimdi_resolve_one(gx_device_wtsimdi *idev, gx_color_index color)
576 {
577 if (color != idev->current_color.color_index) { /* quick out for same color */
578 int hash = COLOR_TO_CACHE_INDEX(color); /* 24 bits down to cache index */
579
580 if (idev->color_cache[hash].color_index == color) {
581 /* cache hit */
582 #ifdef DEBUG
583 idev->color_cache_hit++;
584 #endif
585 idev->current_color = idev->color_cache[hash];
586 } else {
587 /* cache collision or empty, fill it */
588 int code;
589 int r = (color >> 16) & 0xff;
590 int g = (color >> 8) & 0xff;
591 int b = color & 0xff;
592 double rgb[3];
593 double cmyk[4];
594
595 #ifdef DEBUG
596 if (idev->color_cache[hash].color_index == gx_no_color_index)
597 idev->cache_fill_empty++;
598 else
599 idev->color_cache_collision++;
600 #endif
601 rgb[0] = r / 255.0;
602 rgb[1] = g / 255.0;
603 rgb[2] = b / 255.0;
604 code = idev->luo->lookup(idev->luo, cmyk, rgb);
605 if (code > 1)
606 return_error(gs_error_unknownerror);
607 idev->current_color.color_index = color;
608 idev->current_color.cmyk[0] = cmyk[0] * 255 + 0.5;
609 idev->current_color.cmyk[1] = cmyk[1] * 255 + 0.5;
610 idev->current_color.cmyk[2] = cmyk[2] * 255 + 0.5;
611 idev->current_color.cmyk[3] = cmyk[3] * 255 + 0.5;
612 idev->color_cache[hash] = idev->current_color;
613 }
614 }
615 #ifdef DEBUG
616 else
617 idev->color_is_current++;
618 #endif
619 return 0;
620 }
621
622 /* Fill a rectangle with a color. */
623 static int
wtsimdi_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)624 wtsimdi_fill_rectangle(gx_device * dev,
625 int x, int y, int w, int h, gx_color_index color)
626 {
627 gx_device_memory * const mdev = (gx_device_memory *)dev;
628 gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
629 ((mdev->target) ? (gx_device_wts *)(mdev->target)
630 : (gx_device_wts *)dev);
631 wts_cooked_halftone * wch = idev->wcooked;
632 int code, comp_value;
633 int halftoned_bytes = (idev-> width + 7) >> 3;
634 int width_padded, imax;
635 byte * dst, * base;
636 byte * samples;
637 uint raster, plane_ix;
638 int first_byte, end_x;
639
640 fit_fill(dev, x, y, w, h);
641
642 base = scan_line_base(mdev, y);
643 raster = mdev->raster;
644
645 end_x = x + w - 1;
646 first_byte = x >> 3;
647
648 /*
649 * Check if this is a new color. To minimize color conversion
650 * time, we keep a couple of values cached in the wtsimdi device.
651 */
652 if ((code = wtsimdi_resolve_one(idev, color)) < 0)
653 return code;
654
655 for (; h--; y++) {
656 base = scan_line_base(mdev, y);
657 for (plane_ix = 0; plane_ix < 4; plane_ix++) {
658 int nfill = (end_x >> 3) - first_byte;
659
660 width_padded = wch[plane_ix].width_padded;
661 dst = base + first_byte + plane_ix * halftoned_bytes;
662 comp_value = idev->current_color.cmyk[plane_ix];
663 if (comp_value == 0) {
664 if (nfill == 0) {
665 dst[0] &= (0xff << (8 - (x & 7))) |
666 ((1 << (7 - (end_x & 7))) - 1);
667 } else {
668 int i;
669
670 dst[0] &= (0xff << (8 - (x & 7)));
671 memset(&dst[1], 0, nfill-1);
672 dst[nfill] &= ((1 << (7 - (end_x & 7))) - 1);
673 }
674 } else if (comp_value == 0xff) {
675 if (nfill == 0) {
676 dst[0] |= ~((0xff << (8 - (x & 7))) |
677 ((1 << (7 - (end_x & 7))) - 1));
678 } else {
679 int i;
680
681 dst[0] |= ~(0xff << (8 - (x & 7)));
682 memset(&dst[1], 0xff, nfill-1);
683 dst[nfill] |= ~((1 << (7 - (end_x & 7))) - 1);
684 }
685 } else {
686 byte save_left = dst[0];
687 byte save_right = dst[nfill];
688 int i;
689
690 for (i = 0; i < nfill + 1;) {
691 int n_samples;
692 int cx, cy;
693 int j;
694
695 wts_get_samples(wch[plane_ix].wts, ((dev->band_offset_x + x) & -8) + (i << 3),
696 (dev->band_offset_y + y), &cx, &cy, &n_samples);
697 samples = wch[plane_ix].cell + cy * width_padded + cx;
698
699 imax = min((nfill + 1 - i) << 3, n_samples);
700 for (j = 0; j < imax; j += 8) {
701 int b;
702 b = (((unsigned int)(((int)(samples[j + 0])) - comp_value) >> 24)) & 0x80;
703 b |= (((unsigned int)(((int)(samples[j + 1])) - comp_value) >> 24)) & 0x40;
704 b |= (((unsigned int)(((int)(samples[j + 2])) - comp_value) >> 24)) & 0x20;
705 b |= (((unsigned int)(((int)(samples[j + 3])) - comp_value) >> 24)) & 0x10;
706 b |= (((unsigned int)(((int)(samples[j + 4])) - comp_value) >> 24)) & 0x08;
707 b |= (((unsigned int)(((int)(samples[j + 5])) - comp_value) >> 24)) & 0x04;
708 b |= (((unsigned int)(((int)(samples[j + 6])) - comp_value) >> 24)) & 0x02;
709 b |= (((unsigned int)(((int)(samples[j + 7])) - comp_value) >> 24)) & 0x01;
710 dst[i + (j >> 3)] = b;
711 }
712 dst[0] = (save_left & (0xff << (8 - (x & 7)))) |
713 (dst[0] & ~(0xff << (8 - (x & 7))));
714 dst[nfill] = (save_right & ((1 << (7 - (end_x & 7))) - 1)) |
715 (dst[nfill] & ~((1 << (7 - (end_x & 7))) - 1));
716 i += (j >> 3);
717 }
718 }
719 }
720 }
721 return 0;
722 }
723
724 static int
wtsimdi_copy_mono(gx_device * dev,const byte * data,int sourcex,int sraster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index zero,gx_color_index one)725 wtsimdi_copy_mono(gx_device * dev,
726 const byte * data, int sourcex, int sraster, gx_bitmap_id id,
727 int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
728 {
729 gx_device_memory * const mdev = (gx_device_memory *)dev;
730 gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
731 ((mdev->target) ? (gx_device_wts *)(mdev->target)
732 : (gx_device_wts *)dev);
733 wts_cooked_halftone * wch = idev->wcooked;
734 int code, comp_value;
735 int halftoned_bytes = (idev-> width + 7) >> 3;
736 int width_padded, imax;
737 byte * dst, * base;
738 byte * samples;
739 uint raster, plane_ix;
740 int first_byte, end_x;
741 const byte *src;
742 int sshift;
743
744 if (zero != gx_no_color_index)
745 return gx_default_copy_mono(dev, data, sourcex, sraster, id,
746 x, y, w, h, zero, one);
747 if (x < 0) {
748 sourcex -= x;
749 w += x;
750 x = 0;
751 }
752 src = data + ((sourcex - (x & 7)) >> 3);
753 if (y < 0) {
754 src -= sraster * y;
755 h += y;
756 y = 0;
757 }
758 fit_fill(dev, x, y, w, h);
759
760 base = scan_line_base(mdev, y);
761 raster = mdev->raster;
762
763 end_x = x + w - 1;
764 first_byte = x >> 3;
765
766 /*
767 * Check if this is a new color. To minimize color conversion
768 * time, we keep a couple of values cached in the wtsimdi device.
769 */
770 #define COPY_MONO_BLACK_IS_K
771 #define COPY_MONO_OPTIMIZATION
772
773 #ifdef COPY_MONO_BLACK_IS_K
774 if (one == 0) {
775 /* FIXME: This should rely on tag bits, but copy_mono black is (probably) only used for text */
776 idev->current_color.cmyk[0] =idev->current_color.cmyk[1] = idev->current_color.cmyk[2] = 0;
777 idev->current_color.cmyk[3] = 0xff; /* 100% K channel, 0% C, M, Y */
778 idev->current_color.color_index = 0;
779 } else
780 #endif
781 if ((code = wtsimdi_resolve_one(idev, one)) < 0)
782 return code;
783
784 sshift = 8 - ((sourcex - x) & 7);
785
786 for (; h--; y++) {
787 base = scan_line_base(mdev, y);
788 for (plane_ix = 0; plane_ix < 4; plane_ix++) {
789 int nfill = (end_x >> 3) - first_byte;
790 int i;
791 byte smask, save_left, save_right;
792
793 width_padded = wch[plane_ix].width_padded;
794 dst = base + first_byte + plane_ix * halftoned_bytes;
795 save_left = dst[0];
796 save_right = dst[nfill];
797 comp_value = idev->current_color.cmyk[plane_ix];
798 #ifdef COPY_MONO_OPTIMIZATION
799 if (comp_value == 0) {
800 for (i = 0; i < nfill + 1; i++) {
801 if ((smask = (src[i] << 8 | src[i + 1]) >> sshift) != 0)
802 dst[i] = dst[i] & ~smask;
803 }
804 } else if (comp_value == 0xff) {
805 for (i = 0; i < nfill + 1; i++) {
806 if ((smask = (src[i] << 8 | src[i + 1]) >> sshift) != 0)
807 dst[i] = smask | (dst[i] & ~smask) ;
808 }
809 } else
810 #endif
811 {
812 for (i = 0; i < nfill + 1;) {
813 int n_samples;
814 int cx, cy;
815 int j;
816
817 wts_get_samples(wch[plane_ix].wts, ((dev->band_offset_x + x) & -8) + (i << 3),
818 (dev->band_offset_y + y), &cx, &cy, &n_samples);
819 samples = wch[plane_ix].cell + cy * width_padded + cx;
820
821 imax = min((nfill + 1 - i) << 3, n_samples);
822 for (j = 0; j < imax; j += 8) {
823 smask = (src[i] << 8 | src[i + 1]) >> sshift;
824 if (smask) {
825 byte b;
826 b = (((unsigned int)(((int)(samples[j + 0])) - comp_value) >> 24)) & 0x80;
827 b |= (((unsigned int)(((int)(samples[j + 1])) - comp_value) >> 24)) & 0x40;
828 b |= (((unsigned int)(((int)(samples[j + 2])) - comp_value) >> 24)) & 0x20;
829 b |= (((unsigned int)(((int)(samples[j + 3])) - comp_value) >> 24)) & 0x10;
830 b |= (((unsigned int)(((int)(samples[j + 4])) - comp_value) >> 24)) & 0x08;
831 b |= (((unsigned int)(((int)(samples[j + 5])) - comp_value) >> 24)) & 0x04;
832 b |= (((unsigned int)(((int)(samples[j + 6])) - comp_value) >> 24)) & 0x02;
833 b |= (((unsigned int)(((int)(samples[j + 7])) - comp_value) >> 24)) & 0x01;
834 dst[i] = (b & smask) | (dst[i] & ~smask);
835 }
836 i++;
837 }
838 }
839 }
840 /* Restore edge areas on left and right that may have been overwritten */
841 dst[0] = (save_left & (0xff << (8 - (x & 7)))) |
842 (dst[0] & ~(0xff << (8 - (x & 7))));
843 dst[nfill] = (save_right & ((1 << (7 - (end_x & 7))) - 1)) |
844 (dst[nfill] & ~((1 << (7 - (end_x & 7))) - 1));
845 }
846 src += sraster;
847 }
848 return 0;
849 }
850
851
852 /*
853 * This is a clone of gx_default_get_bits except that we are adding
854 * GB_HALFTONED to the parameter options. This will tell the buffer
855 * device to halftone the data (if it is not already halftoned).
856 */
857 int
wtsimdi_get_bits(gx_device * dev,int y,byte * data,byte ** actual_data)858 wtsimdi_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
859 { /*
860 * Hand off to get_bits_rectangle, being careful to avoid a
861 * possible recursion loop.
862 */
863 dev_proc_get_bits((*save_get_bits)) = dev_proc(dev, get_bits);
864 gs_int_rect rect;
865 gs_get_bits_params_t params;
866 int code;
867
868 rect.p.x = 0, rect.p.y = y;
869 rect.q.x = dev->width, rect.q.y = y + 1;
870 params.options =
871 (actual_data ? GB_RETURN_POINTER : 0) | GB_RETURN_COPY |
872 (GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD |
873 /* No depth specified, we always use native colors. */
874 GB_PACKING_CHUNKY | GB_COLORS_NATIVE | GB_ALPHA_NONE |
875 GB_HALFTONED);
876 params.x_offset = 0;
877 params.raster = bitmap_raster(dev->width * dev->color_info.depth);
878 params.data[0] = data;
879 params.original_y = y;
880 set_dev_proc(dev, get_bits, gx_no_get_bits);
881 code = (*dev_proc(dev, get_bits_rectangle))
882 (dev, &rect, ¶ms, NULL);
883 if (actual_data)
884 *actual_data = params.data[0];
885 set_dev_proc(dev, get_bits, save_get_bits);
886 return code;
887 }
888
889 int
wtsimdi_halftoned_get_bits_rectangle(gx_device * dev,const gs_int_rect * prect,gs_get_bits_params_t * params,gs_int_rect ** unread)890 wtsimdi_halftoned_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
891 gs_get_bits_params_t * params, gs_int_rect ** unread)
892 {
893 /*
894 * We should only get to this routine if the caller wants the halftoned
895 * version of our output. In all other cases we should be in
896 * wtsimdi_contone_get_bits_rectangle.
897 */
898 if (!(params->options & GB_HALFTONED))
899 return_error(gs_error_unknownerror);
900 return mem_get_bits_rectangle(dev, prect, params, unread);
901 }
902
903
904 int
wtsimdi_contone_get_bits_rectangle(gx_device * dev,const gs_int_rect * prect,gs_get_bits_params_t * params,gs_int_rect ** unread)905 wtsimdi_contone_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
906 gs_get_bits_params_t * params, gs_int_rect ** unread)
907 {
908 /*
909 * Save the options since mem_get_bits_rectangle will change them to
910 * indicate what it actually did. We need the unmodified values.
911 */
912 gs_get_bits_options_t options = params->options;
913 int original_y = params->original_y;
914 byte * buffer = params->data[0];
915 int code = mem_get_bits_rectangle(dev, prect, params, unread);
916
917 if (code < 0)
918 return code;
919 if (options & GB_HALFTONED) {
920 gx_device_memory * const mdev = (gx_device_memory *)dev;
921 gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
922 ((mdev->target) ? (gx_device_wts *)(mdev->target)
923 : (gx_device_wts *)dev);
924 int width = dev->width;
925 int n_planes = 4;
926 int r_last = -1, g_last = -1, b_last = -1, r, g, b;
927 int x;
928 byte * src = params->data[0];
929 /* Note that the following relies on objects being allocated to
930 * at least a 4-byte boundary for efficiency on x86 and to prevent
931 * alignment errors on CPU's that gripe about it. We know this is
932 * true due to object headers requiring alignment. In the future
933 * if we want to remove this invariant, we can use 'obj_align_mod'
934 * to allocate a slightly larger buffer and offset 'cmyk_data'
935 * to the proper alignment within the buffer.
936 */
937 uint32_t * cmyk_buffer = gs_malloc(dev->memory,
938 (width + 7), sizeof(uint32_t),
939 "wtsimdi_contone_get_bits(cmyk_buffer)");
940 uint32_t* cmyk_data = cmyk_buffer;
941
942 if (cmyk_data == NULL)
943 return_error(gs_error_VMerror);
944
945 for (x = 0; x < width; x++) {
946 r = *src++;
947 g = *src++;
948 b = *src++;
949 if (r != r_last || g != g_last || b != b_last) {
950 gx_color_index color = (((r<<8) | g) << 8) | b;
951
952 r_last = r, g_last = g, b_last = b;
953 wtsimdi_resolve_one(idev, color);
954 }
955 *cmyk_data++ = *((uint32_t *)idev->current_color.cmyk);
956 }
957 wts_halftone_line_8(idev->wcooked, original_y, width, n_planes,
958 idev->band_offset_x, idev->band_offset_y, buffer, (const byte *)cmyk_buffer);
959 params->data[0] = buffer;
960 gs_free(dev->memory, cmyk_buffer, halftoned_bytes * n_planes, 1,
961 "wtsimdi_print_page(halftoned_data)");
962 }
963 return code;
964 }
965
966 /*
967 * We need to create custom memory buffer devices. We use the default
968 * create_buf_device routine and then we set our custom device procedures.
969 */
970 static int
wtsimdi_create_buf_device(gx_device ** pbdev,gx_device * target,int y,const gx_render_plane_t * render_plane,gs_memory_t * mem,gx_band_complexity_t * band_complexity)971 wtsimdi_create_buf_device(gx_device **pbdev, gx_device *target, int y,
972 const gx_render_plane_t *render_plane, gs_memory_t *mem,
973 gx_band_complexity_t *band_complexity)
974 {
975 int code = gx_default_create_buf_device(pbdev, target, y,
976 render_plane, mem, band_complexity);
977 /* Now set our custom device procedures. */
978 if (band_complexity && band_complexity->nontrivial_rops) {
979 set_dev_proc(*pbdev, get_bits_rectangle,
980 wtsimdi_contone_get_bits_rectangle);
981 } else {
982 set_dev_proc(*pbdev, get_bits_rectangle,
983 wtsimdi_halftoned_get_bits_rectangle);
984 set_dev_proc(*pbdev, fill_rectangle, wtsimdi_fill_rectangle);
985 set_dev_proc(*pbdev, copy_mono, wtsimdi_copy_mono);
986 /* All procedures which are defined as mem_true24_* need to be either
987 implemented or replaced with a default implementation. The following
988 three don't have significant usage in testing with Altona.
989 */
990 set_dev_proc(*pbdev, copy_alpha, gx_default_copy_alpha);
991 set_dev_proc(*pbdev, copy_color, gx_default_copy_color);
992 }
993 return code;
994 }
995
996 /*
997 * Create a row of output data. The output is the same as the pkmraw
998 * device. This a pseudo 1 bit CMYK output. Actually the output is
999 * 3 byte RGB with each byte being either 0 or 255.
1000 *
1001 * The input data is 1 bit per component CMYK. The data is separated
1002 * into planes.
1003 */
1004 static void
write_pkmraw_row(int width,byte * data,FILE * pstream)1005 write_pkmraw_row(int width, byte * data, FILE * pstream)
1006 {
1007 if (pstream == NULL)
1008 return;
1009 {
1010 int x, bit;
1011 int halftoned_bytes = (width + 7) >> 3;
1012 byte * cdata = data;
1013 byte * mdata = data + halftoned_bytes;
1014 byte * ydata = mdata + halftoned_bytes;
1015 byte * kdata = ydata + halftoned_bytes;
1016 byte c = *cdata++;
1017 byte m = *mdata++;
1018 byte y = *ydata++;
1019 byte k = *kdata++;
1020
1021 /*
1022 * Contrary to what the documentation implies, gcc compiles putc
1023 * as a procedure call. This is ridiculous, but since we can't
1024 * change it, we buffer groups of pixels ourselves and use fwrite.
1025 */
1026 for (bit = 7, x = 0; x < width;) {
1027 byte raw[80 * 3]; /* 80 is arbitrary, must be a multiple of 8 */
1028 int end = min(x + sizeof(raw) / 3, width);
1029 byte *outp = raw;
1030
1031 for (; x < end; x++) {
1032
1033 if ((k >> bit) & 1) {
1034 *outp++ = 0; /* Set output color = black */
1035 *outp++ = 0;
1036 *outp++ = 0;
1037 } else {
1038 *outp++ = 255 & (255 + ((c >> bit) & 1));
1039 *outp++ = 255 & (255 + ((m >> bit) & 1));
1040 *outp++ = 255 & (255 + ((y >> bit) & 1));
1041 }
1042 if (bit == 0) {
1043 c = *cdata++;
1044 m = *mdata++;
1045 y = *ydata++;
1046 k = *kdata++;
1047 bit = 7;
1048 } else
1049 bit--;
1050 }
1051 fwrite(raw, 1, outp - raw, pstream);
1052 }
1053 return;
1054 }
1055 }
1056
1057 /*
1058 * Output the page raster.
1059 */
1060
1061 static int
wtsimdi_print_page(gx_device_printer * pdev,FILE * prn_stream)1062 wtsimdi_print_page(gx_device_printer *pdev, FILE *prn_stream)
1063 {
1064 gx_device_wtsimdi *idev = (gx_device_wtsimdi*)pdev;
1065 int n_planes = 4;
1066 byte * halftoned_data;
1067 byte * halftoned_buffer = NULL;
1068 int halftoned_bytes, y;
1069 int code = 0;
1070 int width = pdev->width;
1071 int height = pdev->height;
1072 dev_proc_get_bits((*save_get_bits)) = dev_proc(pdev, get_bits);
1073 int output_is_nul = !strncmp(pdev->fname, "nul:", min(strlen(pdev->fname), 4)) ||
1074 !strncmp(pdev->fname, "/dev/null", min(strlen(pdev->fname), 9));
1075
1076 /*
1077 * The printer device setup changed the get_bits routine to
1078 * gx_default_get_bits. We want to use our own.
1079 */
1080 set_dev_proc(pdev, get_bits, wtsimdi_get_bits);
1081
1082 /*
1083 * Initialize the WTS halftones.
1084 */
1085 code = wts_init_halftones((gx_device_wts *)idev, n_planes);
1086 if (code < 0) goto cleanup;
1087
1088 /*
1089 * Allocate a buffer to hold the halftoned data. This is 1 bit per
1090 * component CMYK data.
1091 */
1092 halftoned_bytes = (width + 7) >> 3; /* bytes per component */
1093 halftoned_buffer = gs_malloc(pdev->memory, halftoned_bytes * n_planes, 1,
1094 "wtsimdi_print_page(halftoned_data)");
1095 if (halftoned_buffer == NULL) {
1096 code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
1097 goto cleanup;
1098 }
1099 #ifdef DEBUG
1100 /* Collect stats on each page. With the allocation now done once in 'open' the
1101 * statistics won't be the same if other pages have been printed */
1102 idev->color_cache_hit = idev->color_cache_collision =
1103 idev->color_is_current = idev->cache_fill_empty = 0;
1104 #endif
1105
1106 /* Initialize output file header. */
1107 if (!output_is_nul) {
1108 fprintf(prn_stream, "P6\n%d %d\n", width, height);
1109 fprintf(prn_stream, "# Image generated by %s %ld.%02ld (device=wtsimdi)\n",
1110 gs_program_name(), gs_revision_number() / 100,
1111 gs_revision_number() % 100);
1112 fprintf(prn_stream, "%d\n", 255);
1113 }
1114
1115 /*
1116 * Get raster data and then write data to output file.
1117 */
1118 for (y = 0; y < height; y++) {
1119 /*
1120 * The get_bit routines for our device returns a halftoned copy of
1121 * the output data. Print this data to the output file.
1122 */
1123 code = gdev_prn_get_bits(pdev, y, halftoned_buffer, &halftoned_data);
1124 if (code < 0)
1125 break; /* return code below after cleanup */
1126 if (!output_is_nul)
1127 write_pkmraw_row(width, halftoned_data, prn_stream);
1128 }
1129 cleanup:
1130 if (idev->color_cache != NULL) {
1131 #ifdef DEBUG
1132 {
1133 int i, unused_color_cache_slots;
1134
1135 for (i=0,unused_color_cache_slots=0; i<COLOR_CACHE_SIZE; i++)
1136 if (idev->color_cache[i].color_index == gx_no_color_index)
1137 unused_color_cache_slots++;
1138 if_debug5(':',"wtsimdi_print_page color cache stats:"
1139 " current=%ld, hits=%ld,"
1140 " collisions=%ld, fill=%ld, unused_slots=%d\n",
1141 idev->color_is_current, idev->color_cache_hit,
1142 idev->color_cache_collision, idev->cache_fill_empty,
1143 unused_color_cache_slots);
1144 }
1145 #endif
1146 }
1147 if (halftoned_buffer != NULL)
1148 gs_free(pdev->memory, halftoned_buffer, halftoned_bytes * n_planes, 1,
1149 "wtsimdi_print_page(halftoned_buffer)");
1150 set_dev_proc(pdev, get_bits, save_get_bits);
1151 return code;
1152 }
1153