[gs-cvs] rev 7977 - branches/gs-esp-gpl-merger/src

till at ghostscript.com till at ghostscript.com
Mon May 14 12:08:01 PDT 2007


Author: till
Date: 2007-05-14 12:08:00 -0700 (Mon, 14 May 2007)
New Revision: 7977

Modified:
   branches/gs-esp-gpl-merger/src/gdevwts.c
Log:
Merged with trunk up to rev 7976.


Modified: branches/gs-esp-gpl-merger/src/gdevwts.c
===================================================================
--- branches/gs-esp-gpl-merger/src/gdevwts.c	2007-05-14 18:51:03 UTC (rev 7976)
+++ branches/gs-esp-gpl-merger/src/gdevwts.c	2007-05-14 19:08:00 UTC (rev 7977)
@@ -1,1043 +1,1043 @@
-/* Copyright (C) 2006 artofcode LLC.  All rights reserved.
-  
-  This software is provided AS-IS with no warranty, either express or
-  implied.
-  
-  This software is distributed under license and may not be copied,
-  modified or distributed except as expressly authorized under the terms
-  of the license contained in the file LICENSE in this distribution.
-  
-  For more information about licensing, please refer to
-  http://www.ghostscript.com/licensing/. For information on
-  commercial licensing, go to http://www.artifex.com/licensing/ or
-  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
-  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
-*/
-
-/* $Id: gdevwts.c 6300 2005-12-28 19:56:24Z giles $ */
-/* ALPHA: Sample Device that provides WTS screening and IMDI color management */
-/* TODO: this should be configurable */
-#define LINK_ICC_NAME	"../../link.icc"
-
-#include "errno_.h"
-#include "string_.h"
-
-#include "gdevprn.h"
-#include "gsdevice.h"
-#include "gxfrac.h"
-#include "gsht.h"
-#include "gxwts.h"
-#include "gswts.h"
-#include "gxgetbit.h"
-
-#include "icc.h"
-#include "imdi.h"
-
-/* Memory arg is included in ghostpcl branch but not main branch. */
-#define GS_NOTE_ERROR(m, e) gs_note_error(e)
-
-#ifndef X_DPI
-#  define X_DPI 72
-#endif
-#ifndef Y_DPI
-#  define Y_DPI 72
-#endif
-
-typedef struct {
-    wts_screen_t *wts;
-    byte *cell;
-    int width_padded;
-} wts_cooked_halftone;
-
-typedef struct gx_device_wts_s {
-    gx_device_common;
-    gx_prn_device_common;
-    wts_cooked_halftone wcooked[4];
-} gx_device_wts;
-
-private dev_proc_print_page(wtscmyk_print_page);
-
-/* 8-bit-per-plane separated CMYK color. */
-
-private const gx_device_procs wtscmyk_procs = {
-    gdev_prn_open, NULL, NULL, gdev_prn_output_page, gdev_prn_close,
-    NULL, cmyk_8bit_map_color_cmyk, NULL, NULL, NULL, NULL, NULL, NULL,
-    gdev_prn_get_params, gdev_prn_put_params,
-    cmyk_8bit_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
-};
-
-const gx_device_wts gs_wtscmyk_device = {
-    prn_device_body(gx_device_wts, wtscmyk_procs, "wtscmyk",
-			DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
-			X_DPI, Y_DPI,
-			0, 0, 0, 0,	/* Margins */
-			4, 32, 255, 255, 256, 256, wtscmyk_print_page)
-};
-
-/* RGB with imdi conversion to CMYK and wts halftoning */
-
-private dev_proc_open_device(wtsimdi_open_device);
-private dev_proc_close_device(wtsimdi_close_device);
-private dev_proc_print_page(wtsimdi_print_page);
-private dev_proc_create_buf_device(wtsimdi_create_buf_device);
-private dev_proc_get_bits(wtsimdi_get_bits);
-private dev_proc_get_bits_rectangle(wtsimdi_contone_get_bits_rectangle);
-private dev_proc_get_bits_rectangle(wtsimdi_halftoned_get_bits_rectangle);
-
-typedef struct cached_color_s {
-    gx_color_index color_index;
-    byte cmyk[4];
-} cached_color;
-
-typedef struct gx_device_wtsimdi_s {
-    gx_device_common;
-    gx_prn_device_common;
-    wts_cooked_halftone wcooked[4];
-
-    icmFile *fp;
-    icc *icco;
-    icmLuBase *luo;
-    imdi *mdo;
-    cached_color zero, one;
-} gx_device_wtsimdi;
-
-private const gx_device_procs wtsimdi_procs =
-{
-    wtsimdi_open_device, NULL, NULL, gdev_prn_output_page, wtsimdi_close_device,
-    gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
-    NULL, NULL, NULL, NULL, NULL, wtsimdi_get_bits,
-    gdev_prn_get_params, gdev_prn_put_params,
-    NULL, NULL, NULL, NULL, gx_page_device_get_page_device
-};
-
-const gx_device_wtsimdi gs_wtsimdi_device = {
-    prn_device_body(gx_device_wtsimdi, wtsimdi_procs, "wtsimdi",
-			DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
-			X_DPI, Y_DPI,
-			0, 0, 0, 0,	/* Margins */
-			3, 24, 255, 255, 256, 256, wtsimdi_print_page)
-};
-
-private void
-wts_halftone_line(void **wts, int y, int width, int n_planes,
-		  byte **dst, const byte *src)
-{
-    int x;
-    int plane_ix;
-
-    for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
-	byte *dline = dst[plane_ix];
-	for (x = 0; x < width; x += 8) {
-	    byte b = 0;
-	    if (src[(x + 0) * n_planes + plane_ix] > 0x20) b |= 0x80;
-	    if (src[(x + 1) * n_planes + plane_ix] > 0x40) b |= 0x40;
-	    if (src[(x + 2) * n_planes + plane_ix] > 0x60) b |= 0x20;
-	    if (src[(x + 3) * n_planes + plane_ix] > 0x80) b |= 0x10;
-	    if (src[(x + 4) * n_planes + plane_ix] > 0xa0) b |= 0x08;
-	    if (src[(x + 5) * n_planes + plane_ix] > 0xc0) b |= 0x04;
-	    if (src[(x + 6) * n_planes + plane_ix] > 0xe0) b |= 0x02;
-	    if (src[(x + 7) * n_planes + plane_ix] > 0xfe) b |= 0x01;
-	    dline[x >> 3] = b;
-	}
-    }
-}
-
-private void
-wts_halftone_line_16(wts_cooked_halftone *wch, int y, int width, int n_planes,
-		     byte **dst, const byte *src)
-{
-    int x;
-    int plane_ix;
-    wts_screen_sample_t *samples;
-
-    for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
-	wts_screen_t *w = wch[plane_ix].wts;
-	byte *dline = dst[plane_ix];
-	int imax;
-
-	for (x = 0; x < width;) {
-	    int i;
-	    int n_samples;
-	    int cx, cy;
-
-	    wts_get_samples(w, x, y, &cx, &cy, &n_samples);
-	    samples = w->samples + cy * w->cell_width + cx;
-
-	    imax = min(width - x, n_samples);
-	    for (i = 0; i < imax; i += 8) {
-		byte b = 0;
-		int src_ix = x * 4 + plane_ix;
-#if 0
-		if (src[src_ix + 0 * 4] > (samples[i + 0] >> 7)) b |= 0x80;
-		if (src[src_ix + 1 * 4] > (samples[i + 1] >> 7)) b |= 0x40;
-		if (src[src_ix + 2 * 4] > (samples[i + 2] >> 7)) b |= 0x20;
-		if (src[src_ix + 3 * 4] > (samples[i + 3] >> 7)) b |= 0x10;
-		if (src[src_ix + 4 * 4] > (samples[i + 4] >> 7)) b |= 0x08;
-		if (src[src_ix + 5 * 4] > (samples[i + 5] >> 7)) b |= 0x04;
-		if (src[src_ix + 6 * 4] > (samples[i + 6] >> 7)) b |= 0x02;
-		if (src[src_ix + 7 * 4] > (samples[i + 7] >> 7)) b |= 0x01;
-#else
-#if 0
-		b |= (src[src_ix + 0 * 4] > (samples[i + 0] >> 7)) << 7;
-		b |= (src[src_ix + 1 * 4] > (samples[i + 1] >> 7)) << 6;
-		b |= (src[src_ix + 2 * 4] > (samples[i + 2] >> 7)) << 5;
-		b |= (src[src_ix + 3 * 4] > (samples[i + 3] >> 7)) << 4;
-		b |= (src[src_ix + 4 * 4] > (samples[i + 4] >> 7)) << 3;
-		b |= (src[src_ix + 5 * 4] > (samples[i + 5] >> 7)) << 2;
-		b |= (src[src_ix + 6 * 4] > (samples[i + 6] >> 7)) << 1;
-		b |= (src[src_ix + 7 * 4] > (samples[i + 7] >> 7)) << 0;
-#else
-		b = (((unsigned int)(((int)(samples[i + 0] >> 7)) - ((int)src[src_ix + 0 * 4]))) >> 24) & 0x80;
-		b |= (((unsigned int)(((int)(samples[i + 1] >> 7)) - ((int)src[src_ix + 1 * 4]))) >> 24) & 0x40;
-		b |= (((unsigned int)(((int)(samples[i + 2] >> 7)) - ((int)src[src_ix + 2 * 4]))) >> 24) & 0x20;
-		b |= (((unsigned int)(((int)(samples[i + 3] >> 7)) - ((int)src[src_ix + 3 * 4]))) >> 24) & 0x10;
-		b |= (((unsigned int)(((int)(samples[i + 4] >> 7)) - ((int)src[src_ix + 4 * 4]))) >> 24) & 0x08;
-		b |= (((unsigned int)(((int)(samples[i + 5] >> 7)) - ((int)src[src_ix + 5 * 4]))) >> 24) & 0x04;
-		b |= (((unsigned int)(((int)(samples[i + 6] >> 7)) - ((int)src[src_ix + 6 * 4]))) >> 24) & 0x02;
-		b |= (((unsigned int)(((int)(samples[i + 7] >> 7)) - ((int)src[src_ix + 7 * 4]))) >> 24) & 0x01;
-#endif
-#endif
-		dline[x >> 3] = b;
-		x += 8;
-	    }
-	}
-    }
-}
-
-private void
-wts_halftone_line_8(wts_cooked_halftone *wch, int y, int width, int n_planes,
-		    byte * dst, const byte * src)
-{
-    int x;
-    int plane_ix;
-    byte *samples;
-    int halftoned_bytes = (width + 7) >> 3;
-
-    for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
-	wts_screen_t *w = wch[plane_ix].wts;
-	int width_padded = wch[plane_ix].width_padded;
-	byte * dline = dst + plane_ix * halftoned_bytes;
-	//byte * dline = dst[plane_ix];
-	int imax;
-
-	for (x = 0; x < width;) {
-	    int i;
-	    int n_samples;
-	    int cx, cy;
-
-	    wts_get_samples(w, x, y, &cx, &cy, &n_samples);
-	    samples = wch[plane_ix].cell + cy * width_padded + cx;
-
-	    imax = min(width - x, n_samples);
-	    for (i = 0; i < imax; i += 8) {
-		byte b = 0;
-		int src_ix = x * 4 + plane_ix;
-		b = (((unsigned int)(((int)(samples[i + 0])) - ((int)src[src_ix + 0 * 4]))) >> 24) & 0x80;
-		b |= (((unsigned int)(((int)(samples[i + 1])) - ((int)src[src_ix + 1 * 4]))) >> 24) & 0x40;
-		b |= (((unsigned int)(((int)(samples[i + 2])) - ((int)src[src_ix + 2 * 4]))) >> 24) & 0x20;
-		b |= (((unsigned int)(((int)(samples[i + 3])) - ((int)src[src_ix + 3 * 4]))) >> 24) & 0x10;
-		b |= (((unsigned int)(((int)(samples[i + 4])) - ((int)src[src_ix + 4 * 4]))) >> 24) & 0x08;
-		b |= (((unsigned int)(((int)(samples[i + 5])) - ((int)src[src_ix + 5 * 4]))) >> 24) & 0x04;
-		b |= (((unsigned int)(((int)(samples[i + 6])) - ((int)src[src_ix + 6 * 4]))) >> 24) & 0x02;
-		b |= (((unsigned int)(((int)(samples[i + 7])) - ((int)src[src_ix + 7 * 4]))) >> 24) & 0x01;
-		*dline++ = b;
-		x += 8;
-	    }
-	}
-    }
-}
-
-private int
-wts_load_halftone(gs_memory_t *mem, wts_cooked_halftone *wch, const char *fn)
-{
-    FILE *f = fopen(fn, "rb");
-    int size;
-    byte *buf;
-    wts_screen_t *wts;
-    int width_padded;
-    byte *cooked_cell;
-    int y;
-
-    if (f == 0) return gs_error_undefinedfilename;
-    fseek(f, 0, 2);
-    size = ftell(f);
-    fseek(f, 0, 0);
-    buf = gs_malloc(mem, size, 1, "wts_load_halftone");
-    if (buf == 0) {
-	return gs_error_VMerror;
-    }
-    fread(buf, 1, size, f);
-    fclose(f);
-    wts = gs_wts_from_buf(buf);
-    gs_free(mem, buf, size, 1, "wts_load_halftone");
-    wch->wts = wts;
-    width_padded = wts->cell_width + 7;
-    wch->width_padded = width_padded;
-    cooked_cell = gs_malloc(mem, width_padded * wts->cell_height, 1,
-			    "wts_load_halftone");
-    if (cooked_cell == 0) {
-	return gs_error_VMerror;
-    }
-
-    wch->cell = cooked_cell;
-    for (y = 0; y < wts->cell_height; y++) {
-	wts_screen_sample_t *line = &wts->samples[y * wts->cell_width];
-	byte *dstline = cooked_cell + y * width_padded;
-	int x;
-	for (x = 0; x < width_padded; x++) {
-	    wts_screen_sample_t sample = line[x % wts->cell_width];
-	    dstline[x] = (254 * (int)sample + 0x7fc0) / 0x8ff0;
-	}
-    }
-
-#if 0
-    /* Note: we'll need to fix the free discipline here when we change it
-       in gswts.c */
-    free(wts->samples);
-    wts->samples = NULL;
-#endif
-
-    return 0;
-}
-
-private int
-wts_init_halftones(gx_device_wts *wdev, int n_planes)
-{
-    int i;
-    int code;
-
-    for (i = 0; i < n_planes; i++) {
-	if (wdev->wcooked[i].wts == 0) {
-	    char wts_fn[256];
-
-	    sprintf(wts_fn, "wts_plane_%d", i);
-	    code = wts_load_halftone(wdev->memory, &wdev->wcooked[i], wts_fn);
-	    if (code < 0)
-		return code;
-	}
-    }
-    return 0;
-}
-
-private int
-wtscmyk_print_page(gx_device_printer *pdev, FILE *prn_stream)
-{
-    gx_device_wts *wdev = (gx_device_wts *)pdev;
-    int cmyk_bytes = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
-    /* Output bytes have to be padded to 16 bits. */
-    int y;
-    char *cmyk_line = 0;
-    byte *data;
-    int code = 0;
-    int pbm_bytes;
-    int n_planes = pdev->color_info.num_components;
-    byte *dst;
-    FILE *ostream[4] = {0};
-    int i;
-
-    /* Initialize the wts halftones. */
-    wts_init_halftones(wdev, n_planes);
-
-    cmyk_line = gs_malloc(pdev->memory, cmyk_bytes, 1, "wtscmyk_print_page(in)");
-    if (cmyk_line == 0) {
-	code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
-	goto out;
-    }
-    pbm_bytes = (pdev->width + 7) >> 3;
-    dst = gs_malloc(pdev->memory, pbm_bytes * n_planes, 1,
-		    "wtscmyk_print_page");
-    if (dst == 0) {
-	code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
-	goto out;
-    }
-
-    /* Create additional output streams. */
-    for (i = 0; i < n_planes; i++) {
-	if (i == 0) {
-	    ostream[i] = prn_stream;
-	} else {
-	    char fn[256];
-	    char plane_name[4] = "cmyk";
-	    int fname_len = strlen(pdev->fname);
-
-	    if (fname_len >= 5 && fname_len < 256) {
-		strcpy(fn, pdev->fname);
-		if (!strcmp(fn + fname_len - 5, "c.pbm"))
-		    fn[fname_len - 5] = plane_name[i];
-	    }
-	    ostream[i] = fopen(fn, "wb");
-	}
-	fprintf(ostream[i], "P4\n%d %d\n", pdev->width, pdev->height);
-    }
-#if 0
-    dprintf2(OPTIONAL_MEM(pdev->memory) "Output file name: %s %d\n", pdev->fname, sizeof(dst));
-#endif
-
-    /* For each raster line */
-    for (y = 0; y < pdev->height; y++) {
-	gdev_prn_get_bits(pdev, y, cmyk_line, &data);
-	wts_halftone_line_8(wdev->wcooked, y, pdev->width, n_planes, dst, data);
-	for (i = 0; i < n_planes; i++)
-	    if (ostream[i])
-		fwrite(dst + i * pbm_bytes, 1, pbm_bytes, ostream[i]);
-    }
-out:
-    /* Clean up... */
-    gs_free(pdev->memory, cmyk_line, cmyk_bytes, 1, "wtscmyk_print_page(in)");
-    gs_free(pdev->memory, dst, pbm_bytes, 1, "wtscmyk_print_page");
-    for (i = 1; i < n_planes; i++) {
-	/* Don't close ostream[0], because gdev_prn_close will. */
-	if (ostream[i])
-	    fclose(ostream[i]);
-    }
-    return code;
-}
-
-/* Code that follows is adapted from imdi device */
-
-private double incurve(void *ctx, int ch, double val)
-{
-    return val;
-}
-
-private double outcurve(void *ctx, int ch, double val)
-{
-    return val;
-}
-
-private void mdtable(void *ctx, double *outvals, double *invals)
-{
-    icmLuBase *luo = ctx;
-    luo->lookup(luo, outvals, invals);
-}
-
-/*
- * Open IMDI device.
- * Load ICC device link profile (to map sRGB to FOGRA CMYK).
- */
-
-private int
-wtsimdi_open_device(gx_device *dev)
-{
-    gx_device_wtsimdi *idev = (gx_device_wtsimdi*)dev;
-    int code;
-
-    icColorSpaceSignature ins, outs;
-    int inn, outn;
-    icmLuAlgType alg;
-
-    icmFile *fp;
-    icc *icco;
-    icmLuBase *luo;
-    imdi *mdo;
-
-    /*
-     * We replace create_buf_device so we can replace copy_alpha 
-     * for memory device, but not clist.
-     */
-    idev->printer_procs.buf_procs.create_buf_device = 
-	wtsimdi_create_buf_device;
-    /* Open and read profile */
-
-    fp = new_icmFileStd_name(LINK_ICC_NAME, "rb");
-    if (!fp)
-	return gs_throw1(-1, "could not open file '%s'", LINK_ICC_NAME);
-
-    icco = new_icc();
-    if (!icco)
-	return gs_throw(-1, "could not create ICC object");
-
-    code = icco->read(icco, fp, 0);
-    if (code != 0)
-	return gs_throw1(-1, "could not read ICC profile: %s", icco->err);
-
-    /* Get conversion object */
-
-    luo = icco->get_luobj(icco, icmFwd, icPerceptual, icmSigDefaultData, icmLuOrdNorm);
-    if (!luo)
-	return gs_throw1(-1, "could not create ICC conversion object: %s", icco->err);
-    
-    luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL);
-
-    dprintf3("%s -> %s [%s]\n",
-	    icm2str(icmColorSpaceSignature, ins),
-	    icm2str(icmColorSpaceSignature, outs),
-	    icm2str(icmLuAlg, alg));
-
-    if (inn != 3)
-	return gs_throw1(-1, "profile must have 3 input channels. got %d.", inn);
-    if (outn != 4)
-	return gs_throw1(-1, "profile must have 4 output channels. got %d.", outn);
-
-    /* Create IMDI optimized lookup object */
-
-    mdo = new_imdi(inn, outn, pixint8, 0, pixint8, 0,
-			 33, incurve, mdtable, outcurve, luo);
-    if (!mdo)
-	return gs_throw(-1, "new_imdi failed");
-
-    idev->fp = fp;
-    idev->icco = icco;
-    idev->luo = luo;
-    idev->mdo = mdo;
-    idev->zero.color_index = gx_no_color_index;
-    idev->one.color_index = gx_no_color_index;
-
-    /* guarantee the device bands */
-    ((gx_device_printer *)dev)->space_params.banding_type = BandingAlways;
-    return gdev_prn_open(dev);
-}
-
-
-/*
- * Close device and clean up ICC structures.
- */
-
-private int
-wtsimdi_close_device(gx_device *dev)
-{
-    gx_device_wtsimdi *idev = (gx_device_wtsimdi*)dev;
-
-    idev->mdo->done(idev->mdo);
-    idev->luo->del(idev->luo);
-    idev->icco->del(idev->icco);
-    idev->fp->del(idev->fp);
-
-    return gdev_prn_close(dev);
-}
-
-/* Resolve a color to cmyk values, using the one-element cache. */
-private int
-wtsimdi_resolve_one(gx_device_wtsimdi *idev, gx_color_index color)
-{
-    if (color != idev->one.color_index) {
-	int code;
-	int r = (color >> 16) & 0xff;
-	int g = (color >> 8) & 0xff;
-	int b = color & 0xff;
-	double rgb[3];
-	double cmyk[4];
-
-        rgb[0] = r / 255.0;
-        rgb[1] = g / 255.0;
-        rgb[2] = b / 255.0;
-        code = idev->luo->lookup(idev->luo, cmyk, rgb);
-        if (code > 1)
-	    return_error(gs_error_unknownerror);
-	idev->one.color_index = color;
-        idev->one.cmyk[0] = cmyk[0] * 255 + 0.5;
-        idev->one.cmyk[1] = cmyk[1] * 255 + 0.5;
-        idev->one.cmyk[2] = cmyk[2] * 255 + 0.5;
-        idev->one.cmyk[3] = cmyk[3] * 255 + 0.5;
-    }
-    return 0;
-}
-
-/* Fill a rectangle with a color. */
-private int
-wtsimdi_fill_rectangle(gx_device * dev,
-			  int x, int y, int w, int h, gx_color_index color)
-{
-    gx_device_memory * const mdev = (gx_device_memory *)dev;
-    gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
-	    ((mdev->target) ? (gx_device_wts *)(mdev->target)
-			    : dev);
-    wts_cooked_halftone * wch = idev->wcooked;
-    int n_planes = 4;
-    int code, comp_value;
-    int halftoned_bytes = (idev-> width + 7) >> 3;
-    int width_padded, imax;
-    byte * dst, * base, halftoned_data;
-    byte * samples;
-    uint raster, plane_ix, bit_loc, xi;
-    int first_byte, end_x;
-
-    fit_fill(dev, x, y, w, h);
-
-    base = scan_line_base(mdev, y);
-    raster = mdev->raster;
-
-    end_x = x + w - 1;
-    first_byte = x >> 3;
-
-    /*
-     * Check if this is a new color.  To minimize color conversion
-     * time, we keep a couple of values cached in the wtsimdi device.
-     */
-    if ((code = wtsimdi_resolve_one(idev, color)) < 0)
-	return code;
-
-    for (; h--; y++) {
-        base = scan_line_base(mdev, y);
-        for (plane_ix = 0; plane_ix < 4; plane_ix++) {
-	    int nfill = (end_x >> 3) - first_byte;
-
-	    width_padded = wch[plane_ix].width_padded;
-	    dst = base + first_byte + plane_ix * halftoned_bytes;
-	    comp_value = idev->one.cmyk[plane_ix];
-	    if (comp_value == 0) {
-		if (nfill == 0) {
-		    dst[0] &= (0xff << (8 - (x & 7))) |
-			((1 << (7 - (end_x & 7))) - 1);
-		} else {
-		    int i;
-
-		    dst[0] &= (0xff << (8 - (x & 7)));
-		    for (i = 1; i < nfill; i++)
-			dst[i] = 0;
-		    dst[i] &= ((1 << (7 - (end_x & 7))) - 1);
-		}
-	    } else if (comp_value == 0xff) {
-		if (nfill == 0) {
-		    dst[0] |= ~((0xff << (8 - (x & 7))) |
-			((1 << (7 - (end_x & 7))) - 1));
-		} else {
-		    int i;
-
-		    dst[0] |= ~(0xff << (8 - (x & 7)));
-		    for (i = 1; i < nfill; i++)
-			dst[i] = 0xff;
-		    dst[i] |= ~((1 << (7 - (end_x & 7))) - 1);
-		}
-	    } else {
-		byte save_left = dst[0];
-		byte save_right = dst[nfill];
-		int i;
-
-		for (i = 0; i < nfill + 1;) {
-		    int n_samples;
-		    int cx, cy;
-		    int j;
-
-		    wts_get_samples(wch[plane_ix].wts, (x & -8) + (i << 3), y, &cx, &cy, &n_samples);
-		    samples = wch[plane_ix].cell + cy * width_padded + cx;
-
-		    imax = min((nfill + 1 - i) << 3, n_samples);
-		    for (j = 0; j < imax; j += 8) {
-			int b;
-			b = (((unsigned int)(((int)(samples[j + 0])) - comp_value) >> 24)) & 0x80;
-			b |= (((unsigned int)(((int)(samples[j + 1])) - comp_value) >> 24)) & 0x40;
-			b |= (((unsigned int)(((int)(samples[j + 2])) - comp_value) >> 24)) & 0x20;
-			b |= (((unsigned int)(((int)(samples[j + 3])) - comp_value) >> 24)) & 0x10;
-			b |= (((unsigned int)(((int)(samples[j + 4])) - comp_value) >> 24)) & 0x08;
-			b |= (((unsigned int)(((int)(samples[j + 5])) - comp_value) >> 24)) & 0x04;
-			b |= (((unsigned int)(((int)(samples[j + 6])) - comp_value) >> 24)) & 0x02;
-			b |= (((unsigned int)(((int)(samples[j + 7])) - comp_value) >> 24)) & 0x01;
-			dst[i + (j >> 3)] = b;
-		    }
-		    dst[0] = (save_left & (0xff << (8 - (x & 7)))) |
-			(dst[0] & ~(0xff << (8 - (x & 7))));
-		    dst[nfill] = (save_right & ((1 << (7 - (end_x & 7))) - 1)) |
-			(dst[nfill] & ~((1 << (7 - (end_x & 7))) - 1));
-		    i += (j >> 3);
-		}
-	    }
-        }
-    }
-    return 0;
-}
-
-private 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)
-{
-    gx_device_memory * const mdev = (gx_device_memory *)dev;
-    gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
-	    ((mdev->target) ? (gx_device_wts *)(mdev->target)
-			    : dev);
-    wts_cooked_halftone * wch = idev->wcooked;
-    int n_planes = 4;
-    int code, comp_value;
-    int halftoned_bytes = (idev-> width + 7) >> 3;
-    int width_padded, imax;
-    byte * dst, * base, halftoned_data;
-    byte * samples;
-    uint raster, plane_ix, bit_loc, xi;
-    int first_byte, end_x;
-    const byte *src;
-    int sshift;
-
-    if (zero != gx_no_color_index)
-	return gx_default_copy_mono(dev, data, sourcex, sraster, id,
-				    x, y, w, h, zero, one);
-    if (x < 0) {
-	sourcex -= x;
-	w += x;
-	x = 0;
-    }
-    src = data + ((sourcex - (x & 7)) >> 3);
-    if (y < 0) {
-	src -= sraster * y;
-	h += y;
-	y = 0;
-    }
-    fit_fill(dev, x, y, w, h);
-
-    base = scan_line_base(mdev, y);
-    raster = mdev->raster;
-
-    end_x = x + w - 1;
-    first_byte = x >> 3;
-
-    /*
-     * Check if this is a new color.  To minimize color conversion
-     * time, we keep a couple of values cached in the wtsimdi device.
-     */
-    if ((code = wtsimdi_resolve_one(idev, one)) < 0)
-	return code;
-
-    sshift = 8 - ((sourcex - x) & 7);
-
-    for (; h--; y++) {
-        base = scan_line_base(mdev, y);
-        for (plane_ix = 0; plane_ix < 4; plane_ix++) {
-	    int nfill = (end_x >> 3) - first_byte;
-
-	    width_padded = wch[plane_ix].width_padded;
-	    dst = base + first_byte + plane_ix * halftoned_bytes;
-	    comp_value = idev->one.cmyk[plane_ix];
-	    if (0 && comp_value == 0) {
-		/* TODO: these cases should be optimized, avoiding a screen */
-		if (nfill == 0) {
-		    dst[0] &= (0xff << (8 - (x & 7))) |
-			((1 << (7 - (end_x & 7))) - 1);
-		} else {
-		    int i;
-
-		    dst[0] &= (0xff << (8 - (x & 7)));
-		    for (i = 1; i < nfill; i++)
-			dst[i] = 0;
-		    dst[i] &= ((1 << (7 - (end_x & 7))) - 1);
-		}
-	    } else if (0 && comp_value == 0xff) {
-		if (nfill == 0) {
-		    dst[0] |= ~((0xff << (8 - (x & 7))) |
-			((1 << (7 - (end_x & 7))) - 1));
-		} else {
-		    int i;
-
-		    dst[0] |= ~(0xff << (8 - (x & 7)));
-		    for (i = 1; i < nfill; i++)
-			dst[i] = 0xff;
-		    dst[i] |= ~((1 << (7 - (end_x & 7))) - 1);
-		}
-	    } else {
-		byte save_left = dst[0];
-		byte save_right = dst[nfill];
-		int i;
-
-		for (i = 0; i < nfill + 1;) {
-		    int n_samples;
-		    int cx, cy;
-		    int j;
-
-		    wts_get_samples(wch[plane_ix].wts, (x & -8) + (i << 3), y, &cx, &cy, &n_samples);
-		    samples = wch[plane_ix].cell + cy * width_padded + cx;
-
-		    imax = min((nfill + 1 - i) << 3, n_samples);
-		    for (j = 0; j < imax; j += 8) {
-			byte smask = (src[i] << 8 | src[i + 1]) >> sshift;
-			if (smask) {
-			    byte b;
-			    b = (((unsigned int)(((int)(samples[j + 0])) - comp_value) >> 24)) & 0x80;
-			    b |= (((unsigned int)(((int)(samples[j + 1])) - comp_value) >> 24)) & 0x40;
-			    b |= (((unsigned int)(((int)(samples[j + 2])) - comp_value) >> 24)) & 0x20;
-			    b |= (((unsigned int)(((int)(samples[j + 3])) - comp_value) >> 24)) & 0x10;
-			    b |= (((unsigned int)(((int)(samples[j + 4])) - comp_value) >> 24)) & 0x08;
-			    b |= (((unsigned int)(((int)(samples[j + 5])) - comp_value) >> 24)) & 0x04;
-			    b |= (((unsigned int)(((int)(samples[j + 6])) - comp_value) >> 24)) & 0x02;
-			    b |= (((unsigned int)(((int)(samples[j + 7])) - comp_value) >> 24)) & 0x01;
-			    dst[i] = (b & smask) | (dst[i] & ~smask);
-			}
-			i++;
-		    }
-		    dst[0] = (save_left & (0xff << (8 - (x & 7)))) |
-			(dst[0] & ~(0xff << (8 - (x & 7))));
-		    dst[nfill] = (save_right & ((1 << (7 - (end_x & 7))) - 1)) |
-			(dst[nfill] & ~((1 << (7 - (end_x & 7))) - 1));
-		}
-	    }
-        }
-	src += sraster;
-    }
-    return 0;
-}
-
-
-/*
- * This is a clone of gx_default_get_bits except that we are adding
- * GB_HALFTONED to the parameter options.  This will tell the buffer
- * device to halftone the data (if it is not already halftoned).
- */
-wtsimdi_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
-{	/*
-	 * Hand off to get_bits_rectangle, being careful to avoid a
-	 * possible recursion loop.
-	 */
-    dev_proc_get_bits((*save_get_bits)) = dev_proc(dev, get_bits);
-    gs_int_rect rect;
-    gs_get_bits_params_t params;
-    int code;
-
-    rect.p.x = 0, rect.p.y = y;
-    rect.q.x = dev->width, rect.q.y = y + 1;
-    params.options =
-	(actual_data ? GB_RETURN_POINTER : 0) | GB_RETURN_COPY |
-	(GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD |
-    /* No depth specified, we always use native colors. */
-	 GB_PACKING_CHUNKY | GB_COLORS_NATIVE | GB_ALPHA_NONE |
-	 GB_HALFTONED);
-    params.x_offset = 0;
-    params.raster = bitmap_raster(dev->width * dev->color_info.depth);
-    params.data[0] = data;
-    params.original_y = y;
-    set_dev_proc(dev, get_bits, gx_no_get_bits);
-    code = (*dev_proc(dev, get_bits_rectangle))
-	(dev, &rect, &params, NULL);
-    if (actual_data)
-	*actual_data = params.data[0];
-    set_dev_proc(dev, get_bits, save_get_bits);
-    return code;
-}
-
-int
-wtsimdi_halftoned_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
-		       gs_get_bits_params_t * params, gs_int_rect ** unread)
-{
-    /*
-     * We should only get to this routine if the caller wants the halftoned
-     * version of our output.  In all other cases we should be in
-     * wtsimdi_contone_get_bits_rectangle.
-     */
-    if (!(params->options & GB_HALFTONED))
-	return_error(gs_error_unknownerror);
-    return mem_get_bits_rectangle(dev, prect, params, unread);
-}
-
-
-int
-wtsimdi_contone_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
-		       gs_get_bits_params_t * params, gs_int_rect ** unread)
-{
-    /*
-     * Save the options since mem_get_bits_rectangle will change them to
-     * indicate what it actually did.  We need the unmodified values.
-     */
-    gs_get_bits_options_t options = params->options;
-    int original_y = params->original_y;
-    byte * buffer = params->data[0];
-    int code = mem_get_bits_rectangle(dev, prect, params, unread);
-
-    if (code < 0)
-	return code;
-    if (options & GB_HALFTONED) {
-	gx_device_memory * const mdev = (gx_device_memory *)dev;
-        gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
-	    ((mdev->target) ? (gx_device_wts *)(mdev->target)
-			    : dev);
-	int width = dev->width;
-	int n_planes = 4;
-	int r_last = -1, g_last = -1, b_last = -1, r, g, b;
-	int c, m, y, k, x;
-	double rgb[3];
-	double cmyk[4];
-	byte * src = params->data[0];
-        byte * cmyk_buffer = gs_malloc(dev->memory,
-				(width + 7) * n_planes, 1,
-	   			"wtsimdi_contone_get_bits(cmyk_buffer)");
-	byte * cmyk_data = cmyk_buffer;
-
-        if (cmyk_data == NULL)
-	    return_error(gs_error_VMerror);
-	/*
-	 */
-	for (x = 0; x < width; x++) {
-	    r = *src++;
-	    g = *src++;
-	    b = *src++;
-	    if (r != r_last || g != g_last || b != b_last) {
-		r_last = r, g_last = g, b_last = b;
-	        rgb[0] = r * (1 / 255.0);
-	        rgb[1] = g * (1 / 255.0);
-	        rgb[2] = b * (1 / 255.0);
-	        code = idev->luo->lookup(idev->luo, cmyk, rgb);
-	        if (code > 1)
-		    return_error(gs_error_unknownerror);
-	        c = cmyk[0] * 255 + 0.5;
-	        m = cmyk[1] * 255 + 0.5;
-	        y = cmyk[2] * 255 + 0.5;
-	        k = cmyk[3] * 255 + 0.5;
-	    }
-	    *cmyk_data++ = c;
-	    *cmyk_data++ = m;
-	    *cmyk_data++ = y;
-	    *cmyk_data++ = k;
-	}
-	wts_halftone_line_8(idev->wcooked, original_y, width,
-					    n_planes, buffer, cmyk_buffer);
-	params->data[0] = buffer;
-	gs_free(dev->memory, cmyk_buffer, halftoned_bytes * n_planes, 1,
-		       	"wtsimdi_print_page(halftoned_data)");
-    }
-    return code;
-}
-
-/*
- * We need to create custom memory buffer devices.  We use the default
- * create_buf_device routine and then we set our custom device procedures.
- */
-private int 
-wtsimdi_create_buf_device(gx_device **pbdev, gx_device *target,
-   const gx_render_plane_t *render_plane, gs_memory_t *mem,
-   gx_band_complexity_t *band_complexity)
-{
-    gx_device_printer *ptarget = (gx_device_printer *)target;
-    int code = gx_default_create_buf_device(pbdev, target, 
-	render_plane, mem, band_complexity);
-    /* Now set our custom device procedures. */
-    if (band_complexity && band_complexity->nontrivial_rops) {
-	set_dev_proc(*pbdev, get_bits_rectangle,
-		     wtsimdi_contone_get_bits_rectangle);
-    } else {
-	set_dev_proc(*pbdev, get_bits_rectangle, 
-		     wtsimdi_halftoned_get_bits_rectangle);
-	set_dev_proc(*pbdev, fill_rectangle, wtsimdi_fill_rectangle);
-	set_dev_proc(*pbdev, copy_mono, wtsimdi_copy_mono);
-	/* All procedures which are defined as mem_true24_* need to be either
-	   implemented or replaced with a default implementation. The following
-	   three don't have significant usage in testing with Altona.
-	*/
-	set_dev_proc(*pbdev, strip_copy_rop, gx_default_strip_copy_rop);
-	set_dev_proc(*pbdev, copy_alpha, gx_default_copy_alpha);
-	set_dev_proc(*pbdev, copy_color, gx_default_copy_color);
-    }
-    return code;
-}
-
-/*
- * Create a row of output data.  The output is the same as the pkmraw
- * device.  This a pseudo 1 bit CMYK output.  Actually the output is
- * 3 byte RGB with each byte being either 0 or 1.
- *
- * The input data is 1 bit per component CMYK.  The data is separated
- * into planes.
- */
-write_pkmraw_row(int width, byte * data, FILE * pstream)
-{
-#define noSKIP_OUTPUT
-#ifndef SKIP_OUTPUT
-    int x, bit;
-    int halftoned_bytes = (width + 7) >> 3;
-    byte * cdata = data;
-    byte * mdata = data + halftoned_bytes;
-    byte * ydata = mdata + halftoned_bytes;
-    byte * kdata = ydata + halftoned_bytes;
-    byte c = *cdata++;
-    byte m = *mdata++;
-    byte y = *ydata++;
-    byte k = *kdata++;
-
-    /*
-     * Contrary to what the documentation implies, gcc compiles putc
-     * as a procedure call.  This is ridiculous, but since we can't
-     * change it, we buffer groups of pixels ourselves and use fwrite.
-     */
-    for (bit = 7, x = 0; x < width;) {
-	byte raw[80 * 3];	/* 80 is arbitrary, must be a multiple of 8 */
-	int end = min(x + sizeof(raw) / 3, width);
-	byte *outp = raw;
-
-	for (; x < end; x++) {
-
-	    if ((k >> bit) & 1) {
-		*outp++ = 0;	/* Set output color = black */
-		*outp++ = 0;
-		*outp++ = 0;
-	    } else {
-		*outp++ = 1 - ((c >> bit) & 1);
-		*outp++ = 1 - ((m >> bit) & 1);
-		*outp++ = 1 - ((y >> bit) & 1);
-	    }
-	    if (bit == 0) {
-	        c = *cdata++;
-	        m = *mdata++;
-	        y = *ydata++;
-	        k = *kdata++;
-	        bit = 7;
-	    } else
-	        bit--;
-	}
-	fwrite(raw, 1, outp - raw, pstream);
-    }
-#endif
-    return 0;
-}
-
-/*
- * Output the page raster.
- */
-
-private int
-wtsimdi_print_page(gx_device_printer *pdev, FILE *prn_stream)
-{
-    gx_device_wtsimdi *idev = (gx_device_wtsimdi*)pdev;
-    int n_planes = 4;
-    byte * halftoned_data;
-    byte * halftoned_buffer;
-    int halftoned_bytes, y;
-    int code = 0;
-    int width = pdev->width;
-    int height = pdev->height;
-    dev_proc_get_bits((*save_get_bits)) = dev_proc(pdev, get_bits);
-
-    /*
-     * The printer device setup changed the get_bits routine to
-     * gx_default_get_bits.  We want to use our own.
-     */
-    set_dev_proc(pdev, get_bits, wtsimdi_get_bits);
-
-    /*
-     * Initialize the WTS halftones.
-     */
-    wts_init_halftones((gx_device_wts *)idev, n_planes);
-
-    /*
-     * Allocate a buffer to hold the halftoned data.  This is 1 bit per
-     * component CMYK data.
-     */
-    halftoned_bytes = (width + 7) >> 3;	/* bytes per component */
-    halftoned_buffer = gs_malloc(pdev->memory, halftoned_bytes * n_planes, 1,
-			       	"wtsimdi_print_page(halftoned_data)");
-    if (halftoned_buffer == NULL) {
-	code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
-	goto cleanup;
-    }
-
-    /* Initialize output file header. */
-    fprintf(prn_stream, "P6\n%d %d\n", width, height);
-    fprintf(prn_stream, "# Image generated by %s %d.%02d (device=wtsimdi)\n",
-	   	gs_program_name(), gs_revision_number() / 100,
-	   			gs_revision_number() % 100);
-    fprintf(prn_stream, "%d\n", 1);
-
-    /*
-     * Get raster data and then write data to output file.
-     */
-    for (y = 0; y < height; y++) {
-	/*
-	 * The get_bit routines for our device returns a halftoned copy of
-	 * the output data.  Print this data to the output file.
-	 */
-	gdev_prn_get_bits(pdev, y, halftoned_buffer, &halftoned_data);
-#if 1
-	write_pkmraw_row(width, halftoned_data, prn_stream);
-#endif
-    }
-cleanup:
-    if (halftoned_buffer != NULL)
-	gs_free(pdev->memory, halftoned_buffer, halftoned_bytes * n_planes, 1,
-		       	"wtsimdi_print_page(halftoned_buffer)");
-    set_dev_proc(pdev, get_bits, save_get_bits);
-    return 0;
-}
+/* Copyright (C) 2006-2007 artofcode LLC.  All rights reserved.
+
+  This software is provided AS-IS with no warranty, either express or
+  implied.
+  
+  This software is distributed under license and may not be copied,
+  modified or distributed except as expressly authorized under the terms
+  of the license contained in the file LICENSE in this distribution.
+  
+  For more information about licensing, please refer to
+  http://www.ghostscript.com/licensing/. For information on
+  commercial licensing, go to http://www.artifex.com/licensing/ or
+  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
+  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
+*/
+
+/* $Id$ */
+/* ALPHA: Sample Device that provides WTS screening and IMDI color management */
+/* TODO: this should be configurable */
+#define LINK_ICC_NAME	"../../link.icc"
+
+#include "errno_.h"
+#include "string_.h"
+
+#include "gdevprn.h"
+#include "gsdevice.h"
+#include "gxfrac.h"
+#include "gsht.h"
+#include "gxwts.h"
+#include "gswts.h"
+#include "gxgetbit.h"
+
+#include "icc.h"
+#include "imdi.h"
+
+/* Memory arg is included in ghostpcl branch but not main branch. */
+#define GS_NOTE_ERROR(m, e) gs_note_error(e)
+
+#ifndef X_DPI
+#  define X_DPI 72
+#endif
+#ifndef Y_DPI
+#  define Y_DPI 72
+#endif
+
+typedef struct {
+    wts_screen_t *wts;
+    byte *cell;
+    int width_padded;
+} wts_cooked_halftone;
+
+typedef struct gx_device_wts_s {
+    gx_device_common;
+    gx_prn_device_common;
+    wts_cooked_halftone wcooked[4];
+} gx_device_wts;
+
+private dev_proc_print_page(wtscmyk_print_page);
+
+/* 8-bit-per-plane separated CMYK color. */
+
+private const gx_device_procs wtscmyk_procs = {
+    gdev_prn_open, NULL, NULL, gdev_prn_output_page, gdev_prn_close,
+    NULL, cmyk_8bit_map_color_cmyk, NULL, NULL, NULL, NULL, NULL, NULL,
+    gdev_prn_get_params, gdev_prn_put_params,
+    cmyk_8bit_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
+};
+
+const gx_device_wts gs_wtscmyk_device = {
+    prn_device_body(gx_device_wts, wtscmyk_procs, "wtscmyk",
+			DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+			X_DPI, Y_DPI,
+			0, 0, 0, 0,	/* Margins */
+			4, 32, 255, 255, 256, 256, wtscmyk_print_page)
+};
+
+/* RGB with imdi conversion to CMYK and wts halftoning */
+
+private dev_proc_open_device(wtsimdi_open_device);
+private dev_proc_close_device(wtsimdi_close_device);
+private dev_proc_print_page(wtsimdi_print_page);
+private dev_proc_create_buf_device(wtsimdi_create_buf_device);
+private dev_proc_get_bits(wtsimdi_get_bits);
+private dev_proc_get_bits_rectangle(wtsimdi_contone_get_bits_rectangle);
+private dev_proc_get_bits_rectangle(wtsimdi_halftoned_get_bits_rectangle);
+
+typedef struct cached_color_s {
+    gx_color_index color_index;
+    byte cmyk[4];
+} cached_color;
+
+typedef struct gx_device_wtsimdi_s {
+    gx_device_common;
+    gx_prn_device_common;
+    wts_cooked_halftone wcooked[4];
+
+    icmFile *fp;
+    icc *icco;
+    icmLuBase *luo;
+    imdi *mdo;
+    cached_color zero, one;
+} gx_device_wtsimdi;
+
+private const gx_device_procs wtsimdi_procs =
+{
+    wtsimdi_open_device, NULL, NULL, gdev_prn_output_page, wtsimdi_close_device,
+    gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
+    NULL, NULL, NULL, NULL, NULL, wtsimdi_get_bits,
+    gdev_prn_get_params, gdev_prn_put_params,
+    NULL, NULL, NULL, NULL, gx_page_device_get_page_device
+};
+
+const gx_device_wtsimdi gs_wtsimdi_device = {
+    prn_device_body(gx_device_wtsimdi, wtsimdi_procs, "wtsimdi",
+			DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+			X_DPI, Y_DPI,
+			0, 0, 0, 0,	/* Margins */
+			3, 24, 255, 255, 256, 256, wtsimdi_print_page)
+};
+
+private void
+wts_halftone_line(void **wts, int y, int width, int n_planes,
+		  byte **dst, const byte *src)
+{
+    int x;
+    int plane_ix;
+
+    for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
+	byte *dline = dst[plane_ix];
+	for (x = 0; x < width; x += 8) {
+	    byte b = 0;
+	    if (src[(x + 0) * n_planes + plane_ix] > 0x20) b |= 0x80;
+	    if (src[(x + 1) * n_planes + plane_ix] > 0x40) b |= 0x40;
+	    if (src[(x + 2) * n_planes + plane_ix] > 0x60) b |= 0x20;
+	    if (src[(x + 3) * n_planes + plane_ix] > 0x80) b |= 0x10;
+	    if (src[(x + 4) * n_planes + plane_ix] > 0xa0) b |= 0x08;
+	    if (src[(x + 5) * n_planes + plane_ix] > 0xc0) b |= 0x04;
+	    if (src[(x + 6) * n_planes + plane_ix] > 0xe0) b |= 0x02;
+	    if (src[(x + 7) * n_planes + plane_ix] > 0xfe) b |= 0x01;
+	    dline[x >> 3] = b;
+	}
+    }
+}
+
+private void
+wts_halftone_line_16(wts_cooked_halftone *wch, int y, int width, int n_planes,
+		     byte **dst, const byte *src)
+{
+    int x;
+    int plane_ix;
+    wts_screen_sample_t *samples;
+
+    for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
+	wts_screen_t *w = wch[plane_ix].wts;
+	byte *dline = dst[plane_ix];
+	int imax;
+
+	for (x = 0; x < width;) {
+	    int i;
+	    int n_samples;
+	    int cx, cy;
+
+	    wts_get_samples(w, x, y, &cx, &cy, &n_samples);
+	    samples = w->samples + cy * w->cell_width + cx;
+
+	    imax = min(width - x, n_samples);
+	    for (i = 0; i < imax; i += 8) {
+		byte b = 0;
+		int src_ix = x * 4 + plane_ix;
+#if 0
+		if (src[src_ix + 0 * 4] > (samples[i + 0] >> 7)) b |= 0x80;
+		if (src[src_ix + 1 * 4] > (samples[i + 1] >> 7)) b |= 0x40;
+		if (src[src_ix + 2 * 4] > (samples[i + 2] >> 7)) b |= 0x20;
+		if (src[src_ix + 3 * 4] > (samples[i + 3] >> 7)) b |= 0x10;
+		if (src[src_ix + 4 * 4] > (samples[i + 4] >> 7)) b |= 0x08;
+		if (src[src_ix + 5 * 4] > (samples[i + 5] >> 7)) b |= 0x04;
+		if (src[src_ix + 6 * 4] > (samples[i + 6] >> 7)) b |= 0x02;
+		if (src[src_ix + 7 * 4] > (samples[i + 7] >> 7)) b |= 0x01;
+#else
+#if 0
+		b |= (src[src_ix + 0 * 4] > (samples[i + 0] >> 7)) << 7;
+		b |= (src[src_ix + 1 * 4] > (samples[i + 1] >> 7)) << 6;
+		b |= (src[src_ix + 2 * 4] > (samples[i + 2] >> 7)) << 5;
+		b |= (src[src_ix + 3 * 4] > (samples[i + 3] >> 7)) << 4;
+		b |= (src[src_ix + 4 * 4] > (samples[i + 4] >> 7)) << 3;
+		b |= (src[src_ix + 5 * 4] > (samples[i + 5] >> 7)) << 2;
+		b |= (src[src_ix + 6 * 4] > (samples[i + 6] >> 7)) << 1;
+		b |= (src[src_ix + 7 * 4] > (samples[i + 7] >> 7)) << 0;
+#else
+		b = (((unsigned int)(((int)(samples[i + 0] >> 7)) - ((int)src[src_ix + 0 * 4]))) >> 24) & 0x80;
+		b |= (((unsigned int)(((int)(samples[i + 1] >> 7)) - ((int)src[src_ix + 1 * 4]))) >> 24) & 0x40;
+		b |= (((unsigned int)(((int)(samples[i + 2] >> 7)) - ((int)src[src_ix + 2 * 4]))) >> 24) & 0x20;
+		b |= (((unsigned int)(((int)(samples[i + 3] >> 7)) - ((int)src[src_ix + 3 * 4]))) >> 24) & 0x10;
+		b |= (((unsigned int)(((int)(samples[i + 4] >> 7)) - ((int)src[src_ix + 4 * 4]))) >> 24) & 0x08;
+		b |= (((unsigned int)(((int)(samples[i + 5] >> 7)) - ((int)src[src_ix + 5 * 4]))) >> 24) & 0x04;
+		b |= (((unsigned int)(((int)(samples[i + 6] >> 7)) - ((int)src[src_ix + 6 * 4]))) >> 24) & 0x02;
+		b |= (((unsigned int)(((int)(samples[i + 7] >> 7)) - ((int)src[src_ix + 7 * 4]))) >> 24) & 0x01;
+#endif
+#endif
+		dline[x >> 3] = b;
+		x += 8;
+	    }
+	}
+    }
+}
+
+private void
+wts_halftone_line_8(wts_cooked_halftone *wch, int y, int width, int n_planes,
+		    byte * dst, const byte * src)
+{
+    int x;
+    int plane_ix;
+    byte *samples;
+    int halftoned_bytes = (width + 7) >> 3;
+
+    for (plane_ix = 0; plane_ix < n_planes; plane_ix++) {
+	wts_screen_t *w = wch[plane_ix].wts;
+	int width_padded = wch[plane_ix].width_padded;
+	byte * dline = dst + plane_ix * halftoned_bytes;
+	/*byte * dline = dst[plane_ix];*/
+	int imax;
+
+	for (x = 0; x < width;) {
+	    int i;
+	    int n_samples;
+	    int cx, cy;
+
+	    wts_get_samples(w, x, y, &cx, &cy, &n_samples);
+	    samples = wch[plane_ix].cell + cy * width_padded + cx;
+
+	    imax = min(width - x, n_samples);
+	    for (i = 0; i < imax; i += 8) {
+		byte b = 0;
+		int src_ix = x * 4 + plane_ix;
+		b = (((unsigned int)(((int)(samples[i + 0])) - ((int)src[src_ix + 0 * 4]))) >> 24) & 0x80;
+		b |= (((unsigned int)(((int)(samples[i + 1])) - ((int)src[src_ix + 1 * 4]))) >> 24) & 0x40;
+		b |= (((unsigned int)(((int)(samples[i + 2])) - ((int)src[src_ix + 2 * 4]))) >> 24) & 0x20;
+		b |= (((unsigned int)(((int)(samples[i + 3])) - ((int)src[src_ix + 3 * 4]))) >> 24) & 0x10;
+		b |= (((unsigned int)(((int)(samples[i + 4])) - ((int)src[src_ix + 4 * 4]))) >> 24) & 0x08;
+		b |= (((unsigned int)(((int)(samples[i + 5])) - ((int)src[src_ix + 5 * 4]))) >> 24) & 0x04;
+		b |= (((unsigned int)(((int)(samples[i + 6])) - ((int)src[src_ix + 6 * 4]))) >> 24) & 0x02;
+		b |= (((unsigned int)(((int)(samples[i + 7])) - ((int)src[src_ix + 7 * 4]))) >> 24) & 0x01;
+		*dline++ = b;
+		x += 8;
+	    }
+	}
+    }
+}
+
+private int
+wts_load_halftone(gs_memory_t *mem, wts_cooked_halftone *wch, const char *fn)
+{
+    FILE *f = fopen(fn, "rb");
+    int size;
+    byte *buf;
+    wts_screen_t *wts;
+    int width_padded;
+    byte *cooked_cell;
+    int y;
+
+    if (f == 0) return gs_error_undefinedfilename;
+    fseek(f, 0, 2);
+    size = ftell(f);
+    fseek(f, 0, 0);
+    buf = gs_malloc(mem, size, 1, "wts_load_halftone");
+    if (buf == 0) {
+	return gs_error_VMerror;
+    }
+    fread(buf, 1, size, f);
+    fclose(f);
+    wts = gs_wts_from_buf(buf);
+    gs_free(mem, buf, size, 1, "wts_load_halftone");
+    wch->wts = wts;
+    width_padded = wts->cell_width + 7;
+    wch->width_padded = width_padded;
+    cooked_cell = gs_malloc(mem, width_padded * wts->cell_height, 1,
+			    "wts_load_halftone");
+    if (cooked_cell == 0) {
+	return gs_error_VMerror;
+    }
+
+    wch->cell = cooked_cell;
+    for (y = 0; y < wts->cell_height; y++) {
+	wts_screen_sample_t *line = &wts->samples[y * wts->cell_width];
+	byte *dstline = cooked_cell + y * width_padded;
+	int x;
+	for (x = 0; x < width_padded; x++) {
+	    wts_screen_sample_t sample = line[x % wts->cell_width];
+	    dstline[x] = (254 * (int)sample + 0x7fc0) / 0x8ff0;
+	}
+    }
+
+#if 0
+    /* Note: we'll need to fix the free discipline here when we change it
+       in gswts.c */
+    free(wts->samples);
+    wts->samples = NULL;
+#endif
+
+    return 0;
+}
+
+private int
+wts_init_halftones(gx_device_wts *wdev, int n_planes)
+{
+    int i;
+    int code;
+
+    for (i = 0; i < n_planes; i++) {
+	if (wdev->wcooked[i].wts == 0) {
+	    char wts_fn[256];
+
+	    sprintf(wts_fn, "wts_plane_%d", i);
+	    code = wts_load_halftone(wdev->memory, &wdev->wcooked[i], wts_fn);
+	    if (code < 0)
+		return code;
+	}
+    }
+    return 0;
+}
+
+private int
+wtscmyk_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+    gx_device_wts *wdev = (gx_device_wts *)pdev;
+    int cmyk_bytes = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+    /* Output bytes have to be padded to 16 bits. */
+    int y;
+    char *cmyk_line = 0;
+    byte *data;
+    int code = 0;
+    int pbm_bytes;
+    int n_planes = pdev->color_info.num_components;
+    byte *dst;
+    FILE *ostream[4] = {0};
+    int i;
+
+    /* Initialize the wts halftones. */
+    wts_init_halftones(wdev, n_planes);
+
+    cmyk_line = gs_malloc(pdev->memory, cmyk_bytes, 1, "wtscmyk_print_page(in)");
+    if (cmyk_line == 0) {
+	code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
+	goto out;
+    }
+    pbm_bytes = (pdev->width + 7) >> 3;
+    dst = gs_malloc(pdev->memory, pbm_bytes * n_planes, 1,
+		    "wtscmyk_print_page");
+    if (dst == 0) {
+	code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
+	goto out;
+    }
+
+    /* Create additional output streams. */
+    for (i = 0; i < n_planes; i++) {
+	if (i == 0) {
+	    ostream[i] = prn_stream;
+	} else {
+	    char fn[256];
+	    char plane_name[4] = "cmyk";
+	    int fname_len = strlen(pdev->fname);
+
+	    if (fname_len >= 5 && fname_len < 256) {
+		strcpy(fn, pdev->fname);
+		if (!strcmp(fn + fname_len - 5, "c.pbm"))
+		    fn[fname_len - 5] = plane_name[i];
+	    }
+	    ostream[i] = fopen(fn, "wb");
+	}
+	fprintf(ostream[i], "P4\n%d %d\n", pdev->width, pdev->height);
+    }
+#if 0
+    dprintf2(OPTIONAL_MEM(pdev->memory) "Output file name: %s %d\n", pdev->fname, sizeof(dst));
+#endif
+
+    /* For each raster line */
+    for (y = 0; y < pdev->height; y++) {
+	gdev_prn_get_bits(pdev, y, cmyk_line, &data);
+	wts_halftone_line_8(wdev->wcooked, y, pdev->width, n_planes, dst, data);
+	for (i = 0; i < n_planes; i++)
+	    if (ostream[i])
+		fwrite(dst + i * pbm_bytes, 1, pbm_bytes, ostream[i]);
+    }
+out:
+    /* Clean up... */
+    gs_free(pdev->memory, cmyk_line, cmyk_bytes, 1, "wtscmyk_print_page(in)");
+    gs_free(pdev->memory, dst, pbm_bytes, 1, "wtscmyk_print_page");
+    for (i = 1; i < n_planes; i++) {
+	/* Don't close ostream[0], because gdev_prn_close will. */
+	if (ostream[i])
+	    fclose(ostream[i]);
+    }
+    return code;
+}
+
+/* Code that follows is adapted from imdi device */
+
+private double incurve(void *ctx, int ch, double val)
+{
+    return val;
+}
+
+private double outcurve(void *ctx, int ch, double val)
+{
+    return val;
+}
+
+private void mdtable(void *ctx, double *outvals, double *invals)
+{
+    icmLuBase *luo = ctx;
+    luo->lookup(luo, outvals, invals);
+}
+
+/*
+ * Open IMDI device.
+ * Load ICC device link profile (to map sRGB to FOGRA CMYK).
+ */
+
+private int
+wtsimdi_open_device(gx_device *dev)
+{
+    gx_device_wtsimdi *idev = (gx_device_wtsimdi*)dev;
+    int code;
+
+    icColorSpaceSignature ins, outs;
+    int inn, outn;
+    icmLuAlgType alg;
+
+    icmFile *fp;
+    icc *icco;
+    icmLuBase *luo;
+    imdi *mdo;
+
+    /*
+     * We replace create_buf_device so we can replace copy_alpha 
+     * for memory device, but not clist.
+     */
+    idev->printer_procs.buf_procs.create_buf_device = 
+	wtsimdi_create_buf_device;
+    /* Open and read profile */
+
+    fp = new_icmFileStd_name(LINK_ICC_NAME, "rb");
+    if (!fp)
+	return gs_throw1(-1, "could not open file '%s'", LINK_ICC_NAME);
+
+    icco = new_icc();
+    if (!icco)
+	return gs_throw(-1, "could not create ICC object");
+
+    code = icco->read(icco, fp, 0);
+    if (code != 0)
+	return gs_throw1(-1, "could not read ICC profile: %s", icco->err);
+
+    /* Get conversion object */
+
+    luo = icco->get_luobj(icco, icmFwd, icPerceptual, icmSigDefaultData, icmLuOrdNorm);
+    if (!luo)
+	return gs_throw1(-1, "could not create ICC conversion object: %s", icco->err);
+    
+    luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL);
+
+    dprintf3("%s -> %s [%s]\n",
+	    icm2str(icmColorSpaceSignature, ins),
+	    icm2str(icmColorSpaceSignature, outs),
+	    icm2str(icmLuAlg, alg));
+
+    if (inn != 3)
+	return gs_throw1(-1, "profile must have 3 input channels. got %d.", inn);
+    if (outn != 4)
+	return gs_throw1(-1, "profile must have 4 output channels. got %d.", outn);
+
+    /* Create IMDI optimized lookup object */
+
+    mdo = new_imdi(inn, outn, pixint8, 0, pixint8, 0,
+			 33, incurve, mdtable, outcurve, luo);
+    if (!mdo)
+	return gs_throw(-1, "new_imdi failed");
+
+    idev->fp = fp;
+    idev->icco = icco;
+    idev->luo = luo;
+    idev->mdo = mdo;
+    idev->zero.color_index = gx_no_color_index;
+    idev->one.color_index = gx_no_color_index;
+
+    /* guarantee the device bands */
+    ((gx_device_printer *)dev)->space_params.banding_type = BandingAlways;
+    return gdev_prn_open(dev);
+}
+
+
+/*
+ * Close device and clean up ICC structures.
+ */
+
+private int
+wtsimdi_close_device(gx_device *dev)
+{
+    gx_device_wtsimdi *idev = (gx_device_wtsimdi*)dev;
+
+    idev->mdo->done(idev->mdo);
+    idev->luo->del(idev->luo);
+    idev->icco->del(idev->icco);
+    idev->fp->del(idev->fp);
+
+    return gdev_prn_close(dev);
+}
+
+/* Resolve a color to cmyk values, using the one-element cache. */
+private int
+wtsimdi_resolve_one(gx_device_wtsimdi *idev, gx_color_index color)
+{
+    if (color != idev->one.color_index) {
+	int code;
+	int r = (color >> 16) & 0xff;
+	int g = (color >> 8) & 0xff;
+	int b = color & 0xff;
+	double rgb[3];
+	double cmyk[4];
+
+        rgb[0] = r / 255.0;
+        rgb[1] = g / 255.0;
+        rgb[2] = b / 255.0;
+        code = idev->luo->lookup(idev->luo, cmyk, rgb);
+        if (code > 1)
+	    return_error(gs_error_unknownerror);
+	idev->one.color_index = color;
+        idev->one.cmyk[0] = cmyk[0] * 255 + 0.5;
+        idev->one.cmyk[1] = cmyk[1] * 255 + 0.5;
+        idev->one.cmyk[2] = cmyk[2] * 255 + 0.5;
+        idev->one.cmyk[3] = cmyk[3] * 255 + 0.5;
+    }
+    return 0;
+}
+
+/* Fill a rectangle with a color. */
+private int
+wtsimdi_fill_rectangle(gx_device * dev,
+			  int x, int y, int w, int h, gx_color_index color)
+{
+    gx_device_memory * const mdev = (gx_device_memory *)dev;
+    gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
+	    ((mdev->target) ? (gx_device_wts *)(mdev->target)
+			    : dev);
+    wts_cooked_halftone * wch = idev->wcooked;
+    int n_planes = 4;
+    int code, comp_value;
+    int halftoned_bytes = (idev-> width + 7) >> 3;
+    int width_padded, imax;
+    byte * dst, * base, halftoned_data;
+    byte * samples;
+    uint raster, plane_ix, bit_loc, xi;
+    int first_byte, end_x;
+
+    fit_fill(dev, x, y, w, h);
+
+    base = scan_line_base(mdev, y);
+    raster = mdev->raster;
+
+    end_x = x + w - 1;
+    first_byte = x >> 3;
+
+    /*
+     * Check if this is a new color.  To minimize color conversion
+     * time, we keep a couple of values cached in the wtsimdi device.
+     */
+    if ((code = wtsimdi_resolve_one(idev, color)) < 0)
+	return code;
+
+    for (; h--; y++) {
+        base = scan_line_base(mdev, y);
+        for (plane_ix = 0; plane_ix < 4; plane_ix++) {
+	    int nfill = (end_x >> 3) - first_byte;
+
+	    width_padded = wch[plane_ix].width_padded;
+	    dst = base + first_byte + plane_ix * halftoned_bytes;
+	    comp_value = idev->one.cmyk[plane_ix];
+	    if (comp_value == 0) {
+		if (nfill == 0) {
+		    dst[0] &= (0xff << (8 - (x & 7))) |
+			((1 << (7 - (end_x & 7))) - 1);
+		} else {
+		    int i;
+
+		    dst[0] &= (0xff << (8 - (x & 7)));
+		    for (i = 1; i < nfill; i++)
+			dst[i] = 0;
+		    dst[i] &= ((1 << (7 - (end_x & 7))) - 1);
+		}
+	    } else if (comp_value == 0xff) {
+		if (nfill == 0) {
+		    dst[0] |= ~((0xff << (8 - (x & 7))) |
+			((1 << (7 - (end_x & 7))) - 1));
+		} else {
+		    int i;
+
+		    dst[0] |= ~(0xff << (8 - (x & 7)));
+		    for (i = 1; i < nfill; i++)
+			dst[i] = 0xff;
+		    dst[i] |= ~((1 << (7 - (end_x & 7))) - 1);
+		}
+	    } else {
+		byte save_left = dst[0];
+		byte save_right = dst[nfill];
+		int i;
+
+		for (i = 0; i < nfill + 1;) {
+		    int n_samples;
+		    int cx, cy;
+		    int j;
+
+		    wts_get_samples(wch[plane_ix].wts, (x & -8) + (i << 3), y, &cx, &cy, &n_samples);
+		    samples = wch[plane_ix].cell + cy * width_padded + cx;
+
+		    imax = min((nfill + 1 - i) << 3, n_samples);
+		    for (j = 0; j < imax; j += 8) {
+			int b;
+			b = (((unsigned int)(((int)(samples[j + 0])) - comp_value) >> 24)) & 0x80;
+			b |= (((unsigned int)(((int)(samples[j + 1])) - comp_value) >> 24)) & 0x40;
+			b |= (((unsigned int)(((int)(samples[j + 2])) - comp_value) >> 24)) & 0x20;
+			b |= (((unsigned int)(((int)(samples[j + 3])) - comp_value) >> 24)) & 0x10;
+			b |= (((unsigned int)(((int)(samples[j + 4])) - comp_value) >> 24)) & 0x08;
+			b |= (((unsigned int)(((int)(samples[j + 5])) - comp_value) >> 24)) & 0x04;
+			b |= (((unsigned int)(((int)(samples[j + 6])) - comp_value) >> 24)) & 0x02;
+			b |= (((unsigned int)(((int)(samples[j + 7])) - comp_value) >> 24)) & 0x01;
+			dst[i + (j >> 3)] = b;
+		    }
+		    dst[0] = (save_left & (0xff << (8 - (x & 7)))) |
+			(dst[0] & ~(0xff << (8 - (x & 7))));
+		    dst[nfill] = (save_right & ((1 << (7 - (end_x & 7))) - 1)) |
+			(dst[nfill] & ~((1 << (7 - (end_x & 7))) - 1));
+		    i += (j >> 3);
+		}
+	    }
+        }
+    }
+    return 0;
+}
+
+private 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)
+{
+    gx_device_memory * const mdev = (gx_device_memory *)dev;
+    gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
+	    ((mdev->target) ? (gx_device_wts *)(mdev->target)
+			    : dev);
+    wts_cooked_halftone * wch = idev->wcooked;
+    int n_planes = 4;
+    int code, comp_value;
+    int halftoned_bytes = (idev-> width + 7) >> 3;
+    int width_padded, imax;
+    byte * dst, * base, halftoned_data;
+    byte * samples;
+    uint raster, plane_ix, bit_loc, xi;
+    int first_byte, end_x;
+    const byte *src;
+    int sshift;
+
+    if (zero != gx_no_color_index)
+	return gx_default_copy_mono(dev, data, sourcex, sraster, id,
+				    x, y, w, h, zero, one);
+    if (x < 0) {
+	sourcex -= x;
+	w += x;
+	x = 0;
+    }
+    src = data + ((sourcex - (x & 7)) >> 3);
+    if (y < 0) {
+	src -= sraster * y;
+	h += y;
+	y = 0;
+    }
+    fit_fill(dev, x, y, w, h);
+
+    base = scan_line_base(mdev, y);
+    raster = mdev->raster;
+
+    end_x = x + w - 1;
+    first_byte = x >> 3;
+
+    /*
+     * Check if this is a new color.  To minimize color conversion
+     * time, we keep a couple of values cached in the wtsimdi device.
+     */
+    if ((code = wtsimdi_resolve_one(idev, one)) < 0)
+	return code;
+
+    sshift = 8 - ((sourcex - x) & 7);
+
+    for (; h--; y++) {
+        base = scan_line_base(mdev, y);
+        for (plane_ix = 0; plane_ix < 4; plane_ix++) {
+	    int nfill = (end_x >> 3) - first_byte;
+
+	    width_padded = wch[plane_ix].width_padded;
+	    dst = base + first_byte + plane_ix * halftoned_bytes;
+	    comp_value = idev->one.cmyk[plane_ix];
+	    if (0 && comp_value == 0) {
+		/* TODO: these cases should be optimized, avoiding a screen */
+		if (nfill == 0) {
+		    dst[0] &= (0xff << (8 - (x & 7))) |
+			((1 << (7 - (end_x & 7))) - 1);
+		} else {
+		    int i;
+
+		    dst[0] &= (0xff << (8 - (x & 7)));
+		    for (i = 1; i < nfill; i++)
+			dst[i] = 0;
+		    dst[i] &= ((1 << (7 - (end_x & 7))) - 1);
+		}
+	    } else if (0 && comp_value == 0xff) {
+		if (nfill == 0) {
+		    dst[0] |= ~((0xff << (8 - (x & 7))) |
+			((1 << (7 - (end_x & 7))) - 1));
+		} else {
+		    int i;
+
+		    dst[0] |= ~(0xff << (8 - (x & 7)));
+		    for (i = 1; i < nfill; i++)
+			dst[i] = 0xff;
+		    dst[i] |= ~((1 << (7 - (end_x & 7))) - 1);
+		}
+	    } else {
+		byte save_left = dst[0];
+		byte save_right = dst[nfill];
+		int i;
+
+		for (i = 0; i < nfill + 1;) {
+		    int n_samples;
+		    int cx, cy;
+		    int j;
+
+		    wts_get_samples(wch[plane_ix].wts, (x & -8) + (i << 3), y, &cx, &cy, &n_samples);
+		    samples = wch[plane_ix].cell + cy * width_padded + cx;
+
+		    imax = min((nfill + 1 - i) << 3, n_samples);
+		    for (j = 0; j < imax; j += 8) {
+			byte smask = (src[i] << 8 | src[i + 1]) >> sshift;
+			if (smask) {
+			    byte b;
+			    b = (((unsigned int)(((int)(samples[j + 0])) - comp_value) >> 24)) & 0x80;
+			    b |= (((unsigned int)(((int)(samples[j + 1])) - comp_value) >> 24)) & 0x40;
+			    b |= (((unsigned int)(((int)(samples[j + 2])) - comp_value) >> 24)) & 0x20;
+			    b |= (((unsigned int)(((int)(samples[j + 3])) - comp_value) >> 24)) & 0x10;
+			    b |= (((unsigned int)(((int)(samples[j + 4])) - comp_value) >> 24)) & 0x08;
+			    b |= (((unsigned int)(((int)(samples[j + 5])) - comp_value) >> 24)) & 0x04;
+			    b |= (((unsigned int)(((int)(samples[j + 6])) - comp_value) >> 24)) & 0x02;
+			    b |= (((unsigned int)(((int)(samples[j + 7])) - comp_value) >> 24)) & 0x01;
+			    dst[i] = (b & smask) | (dst[i] & ~smask);
+			}
+			i++;
+		    }
+		    dst[0] = (save_left & (0xff << (8 - (x & 7)))) |
+			(dst[0] & ~(0xff << (8 - (x & 7))));
+		    dst[nfill] = (save_right & ((1 << (7 - (end_x & 7))) - 1)) |
+			(dst[nfill] & ~((1 << (7 - (end_x & 7))) - 1));
+		}
+	    }
+        }
+	src += sraster;
+    }
+    return 0;
+}
+
+
+/*
+ * This is a clone of gx_default_get_bits except that we are adding
+ * GB_HALFTONED to the parameter options.  This will tell the buffer
+ * device to halftone the data (if it is not already halftoned).
+ */
+wtsimdi_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
+{	/*
+	 * Hand off to get_bits_rectangle, being careful to avoid a
+	 * possible recursion loop.
+	 */
+    dev_proc_get_bits((*save_get_bits)) = dev_proc(dev, get_bits);
+    gs_int_rect rect;
+    gs_get_bits_params_t params;
+    int code;
+
+    rect.p.x = 0, rect.p.y = y;
+    rect.q.x = dev->width, rect.q.y = y + 1;
+    params.options =
+	(actual_data ? GB_RETURN_POINTER : 0) | GB_RETURN_COPY |
+	(GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD |
+    /* No depth specified, we always use native colors. */
+	 GB_PACKING_CHUNKY | GB_COLORS_NATIVE | GB_ALPHA_NONE |
+	 GB_HALFTONED);
+    params.x_offset = 0;
+    params.raster = bitmap_raster(dev->width * dev->color_info.depth);
+    params.data[0] = data;
+    params.original_y = y;
+    set_dev_proc(dev, get_bits, gx_no_get_bits);
+    code = (*dev_proc(dev, get_bits_rectangle))
+	(dev, &rect, &params, NULL);
+    if (actual_data)
+	*actual_data = params.data[0];
+    set_dev_proc(dev, get_bits, save_get_bits);
+    return code;
+}
+
+int
+wtsimdi_halftoned_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+		       gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+    /*
+     * We should only get to this routine if the caller wants the halftoned
+     * version of our output.  In all other cases we should be in
+     * wtsimdi_contone_get_bits_rectangle.
+     */
+    if (!(params->options & GB_HALFTONED))
+	return_error(gs_error_unknownerror);
+    return mem_get_bits_rectangle(dev, prect, params, unread);
+}
+
+
+int
+wtsimdi_contone_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+		       gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+    /*
+     * Save the options since mem_get_bits_rectangle will change them to
+     * indicate what it actually did.  We need the unmodified values.
+     */
+    gs_get_bits_options_t options = params->options;
+    int original_y = params->original_y;
+    byte * buffer = params->data[0];
+    int code = mem_get_bits_rectangle(dev, prect, params, unread);
+
+    if (code < 0)
+	return code;
+    if (options & GB_HALFTONED) {
+	gx_device_memory * const mdev = (gx_device_memory *)dev;
+        gx_device_wtsimdi * idev = (gx_device_wtsimdi *)
+	    ((mdev->target) ? (gx_device_wts *)(mdev->target)
+			    : dev);
+	int width = dev->width;
+	int n_planes = 4;
+	int r_last = -1, g_last = -1, b_last = -1, r, g, b;
+	int c, m, y, k, x;
+	double rgb[3];
+	double cmyk[4];
+	byte * src = params->data[0];
+        byte * cmyk_buffer = gs_malloc(dev->memory,
+				(width + 7) * n_planes, 1,
+	   			"wtsimdi_contone_get_bits(cmyk_buffer)");
+	byte * cmyk_data = cmyk_buffer;
+
+        if (cmyk_data == NULL)
+	    return_error(gs_error_VMerror);
+	/*
+	 */
+	for (x = 0; x < width; x++) {
+	    r = *src++;
+	    g = *src++;
+	    b = *src++;
+	    if (r != r_last || g != g_last || b != b_last) {
+		r_last = r, g_last = g, b_last = b;
+	        rgb[0] = r * (1 / 255.0);
+	        rgb[1] = g * (1 / 255.0);
+	        rgb[2] = b * (1 / 255.0);
+	        code = idev->luo->lookup(idev->luo, cmyk, rgb);
+	        if (code > 1)
+		    return_error(gs_error_unknownerror);
+	        c = cmyk[0] * 255 + 0.5;
+	        m = cmyk[1] * 255 + 0.5;
+	        y = cmyk[2] * 255 + 0.5;
+	        k = cmyk[3] * 255 + 0.5;
+	    }
+	    *cmyk_data++ = c;
+	    *cmyk_data++ = m;
+	    *cmyk_data++ = y;
+	    *cmyk_data++ = k;
+	}
+	wts_halftone_line_8(idev->wcooked, original_y, width,
+					    n_planes, buffer, cmyk_buffer);
+	params->data[0] = buffer;
+	gs_free(dev->memory, cmyk_buffer, halftoned_bytes * n_planes, 1,
+		       	"wtsimdi_print_page(halftoned_data)");
+    }
+    return code;
+}
+
+/*
+ * We need to create custom memory buffer devices.  We use the default
+ * create_buf_device routine and then we set our custom device procedures.
+ */
+private int 
+wtsimdi_create_buf_device(gx_device **pbdev, gx_device *target,
+   const gx_render_plane_t *render_plane, gs_memory_t *mem,
+   gx_band_complexity_t *band_complexity)
+{
+    gx_device_printer *ptarget = (gx_device_printer *)target;
+    int code = gx_default_create_buf_device(pbdev, target, 
+	render_plane, mem, band_complexity);
+    /* Now set our custom device procedures. */
+    if (band_complexity && band_complexity->nontrivial_rops) {
+	set_dev_proc(*pbdev, get_bits_rectangle,
+		     wtsimdi_contone_get_bits_rectangle);
+    } else {
+	set_dev_proc(*pbdev, get_bits_rectangle, 
+		     wtsimdi_halftoned_get_bits_rectangle);
+	set_dev_proc(*pbdev, fill_rectangle, wtsimdi_fill_rectangle);
+	set_dev_proc(*pbdev, copy_mono, wtsimdi_copy_mono);
+	/* All procedures which are defined as mem_true24_* need to be either
+	   implemented or replaced with a default implementation. The following
+	   three don't have significant usage in testing with Altona.
+	*/
+	set_dev_proc(*pbdev, strip_copy_rop, gx_default_strip_copy_rop);
+	set_dev_proc(*pbdev, copy_alpha, gx_default_copy_alpha);
+	set_dev_proc(*pbdev, copy_color, gx_default_copy_color);
+    }
+    return code;
+}
+
+/*
+ * Create a row of output data.  The output is the same as the pkmraw
+ * device.  This a pseudo 1 bit CMYK output.  Actually the output is
+ * 3 byte RGB with each byte being either 0 or 1.
+ *
+ * The input data is 1 bit per component CMYK.  The data is separated
+ * into planes.
+ */
+write_pkmraw_row(int width, byte * data, FILE * pstream)
+{
+#define noSKIP_OUTPUT
+#ifndef SKIP_OUTPUT
+    int x, bit;
+    int halftoned_bytes = (width + 7) >> 3;
+    byte * cdata = data;
+    byte * mdata = data + halftoned_bytes;
+    byte * ydata = mdata + halftoned_bytes;
+    byte * kdata = ydata + halftoned_bytes;
+    byte c = *cdata++;
+    byte m = *mdata++;
+    byte y = *ydata++;
+    byte k = *kdata++;
+
+    /*
+     * Contrary to what the documentation implies, gcc compiles putc
+     * as a procedure call.  This is ridiculous, but since we can't
+     * change it, we buffer groups of pixels ourselves and use fwrite.
+     */
+    for (bit = 7, x = 0; x < width;) {
+	byte raw[80 * 3];	/* 80 is arbitrary, must be a multiple of 8 */
+	int end = min(x + sizeof(raw) / 3, width);
+	byte *outp = raw;
+
+	for (; x < end; x++) {
+
+	    if ((k >> bit) & 1) {
+		*outp++ = 0;	/* Set output color = black */
+		*outp++ = 0;
+		*outp++ = 0;
+	    } else {
+		*outp++ = 1 - ((c >> bit) & 1);
+		*outp++ = 1 - ((m >> bit) & 1);
+		*outp++ = 1 - ((y >> bit) & 1);
+	    }
+	    if (bit == 0) {
+	        c = *cdata++;
+	        m = *mdata++;
+	        y = *ydata++;
+	        k = *kdata++;
+	        bit = 7;
+	    } else
+	        bit--;
+	}
+	fwrite(raw, 1, outp - raw, pstream);
+    }
+#endif
+    return 0;
+}
+
+/*
+ * Output the page raster.
+ */
+
+private int
+wtsimdi_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+    gx_device_wtsimdi *idev = (gx_device_wtsimdi*)pdev;
+    int n_planes = 4;
+    byte * halftoned_data;
+    byte * halftoned_buffer;
+    int halftoned_bytes, y;
+    int code = 0;
+    int width = pdev->width;
+    int height = pdev->height;
+    dev_proc_get_bits((*save_get_bits)) = dev_proc(pdev, get_bits);
+
+    /*
+     * The printer device setup changed the get_bits routine to
+     * gx_default_get_bits.  We want to use our own.
+     */
+    set_dev_proc(pdev, get_bits, wtsimdi_get_bits);
+
+    /*
+     * Initialize the WTS halftones.
+     */
+    wts_init_halftones((gx_device_wts *)idev, n_planes);
+
+    /*
+     * Allocate a buffer to hold the halftoned data.  This is 1 bit per
+     * component CMYK data.
+     */
+    halftoned_bytes = (width + 7) >> 3;	/* bytes per component */
+    halftoned_buffer = gs_malloc(pdev->memory, halftoned_bytes * n_planes, 1,
+			       	"wtsimdi_print_page(halftoned_data)");
+    if (halftoned_buffer == NULL) {
+	code = GS_NOTE_ERROR(pdev->memory, gs_error_VMerror);
+	goto cleanup;
+    }
+
+    /* Initialize output file header. */
+    fprintf(prn_stream, "P6\n%d %d\n", width, height);
+    fprintf(prn_stream, "# Image generated by %s %d.%02d (device=wtsimdi)\n",
+	   	gs_program_name(), gs_revision_number() / 100,
+	   			gs_revision_number() % 100);
+    fprintf(prn_stream, "%d\n", 1);
+
+    /*
+     * Get raster data and then write data to output file.
+     */
+    for (y = 0; y < height; y++) {
+	/*
+	 * The get_bit routines for our device returns a halftoned copy of
+	 * the output data.  Print this data to the output file.
+	 */
+	gdev_prn_get_bits(pdev, y, halftoned_buffer, &halftoned_data);
+#if 1
+	write_pkmraw_row(width, halftoned_data, prn_stream);
+#endif
+    }
+cleanup:
+    if (halftoned_buffer != NULL)
+	gs_free(pdev->memory, halftoned_buffer, halftoned_bytes * n_planes, 1,
+		       	"wtsimdi_print_page(halftoned_buffer)");
+    set_dev_proc(pdev, get_bits, save_get_bits);
+    return 0;
+}


Property changes on: branches/gs-esp-gpl-merger/src/gdevwts.c
___________________________________________________________________
Name: svn:keywords
   + Id



More information about the gs-cvs mailing list