xref: /plan9/sys/src/cmd/gs/src/gdevifno.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  *		Copyright (c) 1998 by Lucent Technologies.
37dd7cddfSDavid du Colombier  * Permission to use, copy, modify, and distribute this software for any
47dd7cddfSDavid du Colombier  * purpose without fee is hereby granted, provided that this entire notice
57dd7cddfSDavid du Colombier  * is included in all copies of any software which is or includes a copy
67dd7cddfSDavid du Colombier  * or modification of this software.
77dd7cddfSDavid du Colombier  *
87dd7cddfSDavid du Colombier  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
97dd7cddfSDavid du Colombier  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
107dd7cddfSDavid du Colombier  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
117dd7cddfSDavid du Colombier  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
127dd7cddfSDavid du Colombier  */
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier /*
157dd7cddfSDavid du Colombier  * gdevifno.c: gs device to generate inferno bitmaps
167dd7cddfSDavid du Colombier  * Russ Cox <rsc@plan9.bell-labs.com>, 3/25/98
177dd7cddfSDavid du Colombier  * Updated to fit in the standard GS distribution, 5/14/98
187dd7cddfSDavid du Colombier  */
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier #include "gdevprn.h"
217dd7cddfSDavid du Colombier #include "gsparam.h"
227dd7cddfSDavid du Colombier #include "gxlum.h"
237dd7cddfSDavid du Colombier #include <stdlib.h>
247dd7cddfSDavid du Colombier #undef printf
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier #define nil ((void*)0)
277dd7cddfSDavid du Colombier enum {
287dd7cddfSDavid du Colombier 	ERROR = -2
297dd7cddfSDavid du Colombier };
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier typedef struct WImage WImage;
327dd7cddfSDavid du Colombier typedef struct Rectangle Rectangle;
337dd7cddfSDavid du Colombier typedef struct Point Point;
347dd7cddfSDavid du Colombier 
357dd7cddfSDavid du Colombier struct Point {
367dd7cddfSDavid du Colombier 	int x;
377dd7cddfSDavid du Colombier 	int y;
387dd7cddfSDavid du Colombier };
397dd7cddfSDavid du Colombier 
407dd7cddfSDavid du Colombier struct Rectangle {
417dd7cddfSDavid du Colombier 	Point min;
427dd7cddfSDavid du Colombier 	Point max;
437dd7cddfSDavid du Colombier };
447dd7cddfSDavid du Colombier private Point ZP = { 0, 0 };
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier private WImage* initwriteimage(FILE *f, Rectangle r, int ldepth);
477dd7cddfSDavid du Colombier private int writeimageblock(WImage *w, uchar *data, int ndata);
487dd7cddfSDavid du Colombier private int bytesperline(Rectangle, int);
497dd7cddfSDavid du Colombier private int rgb2cmap(int, int, int);
507dd7cddfSDavid du Colombier private long cmap2rgb(int);
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier #define X_DPI	100
537dd7cddfSDavid du Colombier #define Y_DPI	100
547dd7cddfSDavid du Colombier 
557dd7cddfSDavid du Colombier private dev_proc_map_rgb_color(inferno_rgb2cmap);
567dd7cddfSDavid du Colombier private dev_proc_map_color_rgb(inferno_cmap2rgb);
577dd7cddfSDavid du Colombier private dev_proc_open_device(inferno_open);
587dd7cddfSDavid du Colombier private dev_proc_close_device(inferno_close);
597dd7cddfSDavid du Colombier private dev_proc_print_page(inferno_print_page);
607dd7cddfSDavid du Colombier private dev_proc_put_params(inferno_put_params);
617dd7cddfSDavid du Colombier private dev_proc_get_params(inferno_get_params);
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier typedef struct inferno_device_s {
647dd7cddfSDavid du Colombier 	gx_device_common;
657dd7cddfSDavid du Colombier 	gx_prn_device_common;
667dd7cddfSDavid du Colombier 	int dither;
677dd7cddfSDavid du Colombier 
687dd7cddfSDavid du Colombier 	int ldepth;
697dd7cddfSDavid du Colombier 	int lastldepth;
707dd7cddfSDavid du Colombier 	int cmapcall;
717dd7cddfSDavid du Colombier } inferno_device;
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier enum {
747dd7cddfSDavid du Colombier 	Nbits = 8,
757dd7cddfSDavid du Colombier 	Bitmask = (1<<Nbits)-1,
767dd7cddfSDavid du Colombier };
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier private const gx_device_procs inferno_procs =
797dd7cddfSDavid du Colombier 	prn_color_params_procs(inferno_open, gdev_prn_output_page, gdev_prn_close,
807dd7cddfSDavid du Colombier 		inferno_rgb2cmap, inferno_cmap2rgb,
817dd7cddfSDavid du Colombier 		gdev_prn_get_params, gdev_prn_put_params);
827dd7cddfSDavid du Colombier /*
837dd7cddfSDavid du Colombier 		inferno_get_params, inferno_put_params);
847dd7cddfSDavid du Colombier */
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier inferno_device far_data gs_inferno_device =
887dd7cddfSDavid du Colombier { prn_device_body(inferno_device, inferno_procs, "inferno",
897dd7cddfSDavid du Colombier 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
907dd7cddfSDavid du Colombier 	X_DPI, Y_DPI,
917dd7cddfSDavid du Colombier 	0,0,0,0,	/* margins */
927dd7cddfSDavid du Colombier 	3,		/* 3 = RGB, 1 = gray, 4 = CMYK */
937dd7cddfSDavid du Colombier 	Nbits*3,		/* # of bits per pixel */
947dd7cddfSDavid du Colombier 	(1<<Nbits)-1,		/* # of distinct gray levels. */
957dd7cddfSDavid du Colombier 	(1<<Nbits)-1,		/* # of distinct color levels. */
967dd7cddfSDavid du Colombier 	1<<Nbits,		/* dither gray ramp size.  used in alpha? */
977dd7cddfSDavid du Colombier 	1<<Nbits,    	/* dither color ramp size.  used in alpha? */
987dd7cddfSDavid du Colombier 	inferno_print_page),
997dd7cddfSDavid du Colombier 	1,
1007dd7cddfSDavid du Colombier };
1017dd7cddfSDavid du Colombier 
1027dd7cddfSDavid du Colombier /*
1037dd7cddfSDavid du Colombier  * ghostscript asks us how to convert between
1047dd7cddfSDavid du Colombier  * rgb and color map entries
1057dd7cddfSDavid du Colombier  */
1067dd7cddfSDavid du Colombier private gx_color_index
inferno_rgb2cmap(gx_device * dev,gx_color_value rgb[3])107*593dc095SDavid du Colombier inferno_rgb2cmap(gx_device *dev, gx_color_value rgb[3]) {
1087dd7cddfSDavid du Colombier 	int shift;
1097dd7cddfSDavid du Colombier 	inferno_device *idev;
1107dd7cddfSDavid du Colombier 	ulong red, green, blue;
1117dd7cddfSDavid du Colombier 
1127dd7cddfSDavid du Colombier 	idev = (inferno_device*) dev;
1137dd7cddfSDavid du Colombier 
1147dd7cddfSDavid du Colombier 	shift = gx_color_value_bits - Nbits;
115*593dc095SDavid du Colombier 	red = rgb[0] >> shift;
116*593dc095SDavid du Colombier 	green = rgb[1] >> shift;
117*593dc095SDavid du Colombier 	blue = rgb[2] >> shift;
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 	/*
1207dd7cddfSDavid du Colombier 	 * we keep track of what ldepth bitmap this is by watching
1217dd7cddfSDavid du Colombier 	 * what colors gs asks for.
1227dd7cddfSDavid du Colombier 	 *
1237dd7cddfSDavid du Colombier 	 * one catch: sometimes print_page gets called more than one
1247dd7cddfSDavid du Colombier 	 * per page (for multiple copies) without cmap calls inbetween.
1257dd7cddfSDavid du Colombier 	 * if idev->cmapcall is 0 when print_page gets called, it uses
1267dd7cddfSDavid du Colombier 	 * the ldepth of the last page.
1277dd7cddfSDavid du Colombier 	 */
1287dd7cddfSDavid du Colombier 	if(red == green && green == blue) {
1297dd7cddfSDavid du Colombier 		if(red == 0 || red == Bitmask)
1307dd7cddfSDavid du Colombier 			;
1317dd7cddfSDavid du Colombier 		else if(red == Bitmask/3 || red == 2*Bitmask/3) {
1327dd7cddfSDavid du Colombier 			if(idev->ldepth < 1)
1337dd7cddfSDavid du Colombier 				idev->ldepth = 1;
1347dd7cddfSDavid du Colombier 		} else {
1357dd7cddfSDavid du Colombier 			if(idev->ldepth < 2)
1367dd7cddfSDavid du Colombier 				idev->ldepth = 2;
1377dd7cddfSDavid du Colombier 		}
1387dd7cddfSDavid du Colombier 	} else
1397dd7cddfSDavid du Colombier 		idev->ldepth = 3;
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier 	idev->cmapcall = 1;
1427dd7cddfSDavid du Colombier 	return (blue << (2*Nbits)) | (green << Nbits) | red;
1437dd7cddfSDavid du Colombier }
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier private int
inferno_cmap2rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])146*593dc095SDavid du Colombier inferno_cmap2rgb(gx_device *dev, gx_color_index color,
147*593dc095SDavid du Colombier   gx_color_value rgb[3]) {
1487dd7cddfSDavid du Colombier 	int shift, i;
1497dd7cddfSDavid du Colombier 	inferno_device *idev;
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier 	if((ulong)color > 0xFFFFFF)
1527dd7cddfSDavid du Colombier 		return_error(gs_error_rangecheck);
1537dd7cddfSDavid du Colombier 
1547dd7cddfSDavid du Colombier 	idev = (inferno_device*) dev;
1557dd7cddfSDavid du Colombier 	shift = gx_color_value_bits - Nbits;
1567dd7cddfSDavid du Colombier 
1577dd7cddfSDavid du Colombier 	rgb[2] = ((color >> (2*Nbits)) & Bitmask) << shift;
1587dd7cddfSDavid du Colombier 	rgb[1] = ((color >> Nbits) & Bitmask) << shift;
1597dd7cddfSDavid du Colombier 	rgb[0] = (color & Bitmask) << shift;
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	return 0;
1627dd7cddfSDavid du Colombier }
1637dd7cddfSDavid du Colombier 
1647dd7cddfSDavid du Colombier private int
inferno_put_param_int(gs_param_list * plist,gs_param_name pname,int * pv,int minval,int maxval,int ecode)1657dd7cddfSDavid du Colombier inferno_put_param_int(gs_param_list *plist, gs_param_name pname, int *pv,
1667dd7cddfSDavid du Colombier 	int minval, int maxval, int ecode)
1677dd7cddfSDavid du Colombier {
1687dd7cddfSDavid du Colombier 	int code, value;
1697dd7cddfSDavid du Colombier 	switch(code = param_read_int(plist, pname, &value)) {
1707dd7cddfSDavid du Colombier 	default:
1717dd7cddfSDavid du Colombier 		return code;
1727dd7cddfSDavid du Colombier 
1737dd7cddfSDavid du Colombier 	case 1:
1747dd7cddfSDavid du Colombier 		return ecode;
1757dd7cddfSDavid du Colombier 
1767dd7cddfSDavid du Colombier 	case 0:
1777dd7cddfSDavid du Colombier 		if(value < minval || value > maxval)
1787dd7cddfSDavid du Colombier 			param_signal_error(plist, pname, gs_error_rangecheck);
1797dd7cddfSDavid du Colombier 		*pv = value;
1807dd7cddfSDavid du Colombier 		return (ecode < 0 ? ecode : 1);
1817dd7cddfSDavid du Colombier 	}
1827dd7cddfSDavid du Colombier }
1837dd7cddfSDavid du Colombier 
1847dd7cddfSDavid du Colombier private int
inferno_get_params(gx_device * pdev,gs_param_list * plist)1857dd7cddfSDavid du Colombier inferno_get_params(gx_device *pdev, gs_param_list *plist)
1867dd7cddfSDavid du Colombier {
1877dd7cddfSDavid du Colombier 	int code;
1887dd7cddfSDavid du Colombier 	inferno_device *idev;
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier 	idev = (inferno_device*) pdev;
1917dd7cddfSDavid du Colombier //	printf("inferno_get_params dither %d\n", idev->dither);
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier 	if((code = gdev_prn_get_params(pdev, plist)) < 0
1947dd7cddfSDavid du Colombier 	 || (code = param_write_int(plist, "Dither", &idev->dither)) < 0)
1957dd7cddfSDavid du Colombier 		return code;
1967dd7cddfSDavid du Colombier 	printf("getparams: dither=%d\n", idev->dither);
1977dd7cddfSDavid du Colombier 	return code;
1987dd7cddfSDavid du Colombier }
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier private int
inferno_put_params(gx_device * pdev,gs_param_list * plist)2017dd7cddfSDavid du Colombier inferno_put_params(gx_device * pdev, gs_param_list * plist)
2027dd7cddfSDavid du Colombier {
2037dd7cddfSDavid du Colombier 	int code;
2047dd7cddfSDavid du Colombier 	int dither;
2057dd7cddfSDavid du Colombier 	inferno_device *idev;
2067dd7cddfSDavid du Colombier 
2077dd7cddfSDavid du Colombier 	printf("inferno_put_params\n");
2087dd7cddfSDavid du Colombier 
2097dd7cddfSDavid du Colombier 	idev = (inferno_device*)pdev;
2107dd7cddfSDavid du Colombier 	dither = idev->dither;
2117dd7cddfSDavid du Colombier 
2127dd7cddfSDavid du Colombier 	code = inferno_put_param_int(plist, "Dither", &dither, 0, 1, 0);
2137dd7cddfSDavid du Colombier 	if(code < 0)
2147dd7cddfSDavid du Colombier 		return code;
2157dd7cddfSDavid du Colombier 
2167dd7cddfSDavid du Colombier 	idev->dither = dither;
2177dd7cddfSDavid du Colombier 	return 0;
2187dd7cddfSDavid du Colombier }
2197dd7cddfSDavid du Colombier 
2207dd7cddfSDavid du Colombier /*
2217dd7cddfSDavid du Colombier  * dithering tables courtesy of john hobby
2227dd7cddfSDavid du Colombier  */
2237dd7cddfSDavid du Colombier /* The following constants and tables define the mapping from fine-grained RGB
2247dd7cddfSDavid du Colombier    triplets to 8-bit values based on the standard Plan 9 color map.
2257dd7cddfSDavid du Colombier */
2267dd7cddfSDavid du Colombier #define Rlevs	16		/* number of levels to cut red value into */
2277dd7cddfSDavid du Colombier #define Glevs	16
2287dd7cddfSDavid du Colombier #define Blevs	16
2297dd7cddfSDavid du Colombier #define Mlevs	16
2307dd7cddfSDavid du Colombier #define Rfactor 1		/* multiple of red level in p9color[] index */
2317dd7cddfSDavid du Colombier #define Gfactor Rlevs
2327dd7cddfSDavid du Colombier #define Bfactor	(Rlevs*Glevs)
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier ulong p9color[Rlevs*Glevs*Blevs];	/* index blue most sig, red least sig */
2357dd7cddfSDavid du Colombier 
init_p9color(void)2367dd7cddfSDavid du Colombier void init_p9color(void)		/* init at run time since p9color[] is so big */
2377dd7cddfSDavid du Colombier {
2387dd7cddfSDavid du Colombier 	int r, g, b, o;
2397dd7cddfSDavid du Colombier 	ulong* cur = p9color;
2407dd7cddfSDavid du Colombier 	for (b=0; b<16; b++) {
2417dd7cddfSDavid du Colombier 	    for (g=0; g<16; g++) {
2427dd7cddfSDavid du Colombier 		int m0 = (b>g) ? b : g;
2437dd7cddfSDavid du Colombier 		for (r=0; r<16; r++) {
2447dd7cddfSDavid du Colombier 		    int V, M, rM, gM, bM, m;
2457dd7cddfSDavid du Colombier 		    int m1 = (r>m0) ? r : m0;
2467dd7cddfSDavid du Colombier 		    V=m1&3; M=(m1-V)<<1;
2477dd7cddfSDavid du Colombier 		    if (m1==0) m1=1;
2487dd7cddfSDavid du Colombier 		    m = m1 << 3;
2497dd7cddfSDavid du Colombier 		    rM=r*M; gM=g*M; bM=b*M;
2507dd7cddfSDavid du Colombier 		    *cur = 0;
2517dd7cddfSDavid du Colombier 		    for (o=7*m1; o>0; o-=2*m1) {
2527dd7cddfSDavid du Colombier 			int rr=(rM+o)/m, gg=(gM+o)/m, bb=(bM+o)/m;
2537dd7cddfSDavid du Colombier 			int ij = (rr<<6) + (V<<4) + ((V-rr+(gg<<2)+bb)&15);
2547dd7cddfSDavid du Colombier 			*cur = (*cur << 8) + 255-ij;
2557dd7cddfSDavid du Colombier 		    }
2567dd7cddfSDavid du Colombier 		    cur++;
2577dd7cddfSDavid du Colombier 		}
2587dd7cddfSDavid du Colombier 	    }
2597dd7cddfSDavid du Colombier 	}
2607dd7cddfSDavid du Colombier }
2617dd7cddfSDavid du Colombier 
2627dd7cddfSDavid du Colombier /*
2637dd7cddfSDavid du Colombier  * inferno_open() is supposed to initialize the device.
2647dd7cddfSDavid du Colombier  * there's not much to do.
2657dd7cddfSDavid du Colombier  */
2667dd7cddfSDavid du Colombier private int
inferno_open(gx_device * dev)267*593dc095SDavid du Colombier inferno_open(gx_device *dev)
2687dd7cddfSDavid du Colombier {
2697dd7cddfSDavid du Colombier 	int code;
2707dd7cddfSDavid du Colombier 	inferno_device *idev;
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier 	idev = (inferno_device*) dev;
2737dd7cddfSDavid du Colombier 	idev->cmapcall = 0;
2747dd7cddfSDavid du Colombier 	idev->ldepth = 0;
2757dd7cddfSDavid du Colombier 
2767dd7cddfSDavid du Colombier //	printf("inferno_open gs_inferno_device.dither = %d idev->dither = %d\n",
2777dd7cddfSDavid du Colombier //		gs_inferno_device.dither, idev->dither);
2787dd7cddfSDavid du Colombier 	init_p9color();
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier 	return gdev_prn_open(dev);
2817dd7cddfSDavid du Colombier }
2827dd7cddfSDavid du Colombier 
2837dd7cddfSDavid du Colombier /*
2847dd7cddfSDavid du Colombier  * inferno_print_page() is called once for each page
2857dd7cddfSDavid du Colombier  * (actually once for each copy of each page, but we won't
2867dd7cddfSDavid du Colombier  * worry about that).
2877dd7cddfSDavid du Colombier  */
2887dd7cddfSDavid du Colombier private int
inferno_print_page(gx_device_printer * pdev,FILE * f)289*593dc095SDavid du Colombier inferno_print_page(gx_device_printer *pdev, FILE *f)
2907dd7cddfSDavid du Colombier {
2917dd7cddfSDavid du Colombier 	uchar *buf;	/* [8192*3*8/Nbits] BUG: malloc this */
2927dd7cddfSDavid du Colombier 	uchar *p;
2937dd7cddfSDavid du Colombier 	WImage *w;
2947dd7cddfSDavid du Colombier 	int bpl, y;
2957dd7cddfSDavid du Colombier 	int x, xmod;
2967dd7cddfSDavid du Colombier 	int ldepth;
2977dd7cddfSDavid du Colombier 	int ppb[] = {8, 4, 2, 1};	/* pixels per byte */
2987dd7cddfSDavid du Colombier 	int bpp[] = {1, 2, 4, 8};	/* bits per pixel */
2997dd7cddfSDavid du Colombier 	int gsbpl;
3007dd7cddfSDavid du Colombier 	int dither;
3017dd7cddfSDavid du Colombier 	ulong u;
3027dd7cddfSDavid du Colombier 	ushort us;
3037dd7cddfSDavid du Colombier 	Rectangle rect;
3047dd7cddfSDavid du Colombier 	inferno_device *idev;
3057dd7cddfSDavid du Colombier 	ulong r, g, b;
3067dd7cddfSDavid du Colombier 
3077dd7cddfSDavid du Colombier 	gsbpl = gdev_prn_raster(pdev);
308*593dc095SDavid du Colombier 	buf = gs_malloc(pdev->memory, gsbpl, 1, "inferno_print_page");
3097dd7cddfSDavid du Colombier 
3107dd7cddfSDavid du Colombier 	if(buf == nil) {
311*593dc095SDavid du Colombier 		errprintf("out of memory\n");
3127dd7cddfSDavid du Colombier 		return_error(gs_error_Fatal);
3137dd7cddfSDavid du Colombier 	}
3147dd7cddfSDavid du Colombier 
3157dd7cddfSDavid du Colombier 	idev = (inferno_device *) pdev;
3167dd7cddfSDavid du Colombier 	if(idev->cmapcall) {
3177dd7cddfSDavid du Colombier 		idev->lastldepth = idev->ldepth;
3187dd7cddfSDavid du Colombier 		idev->ldepth = 0;
3197dd7cddfSDavid du Colombier 		idev->cmapcall = 0;
3207dd7cddfSDavid du Colombier 	}
3217dd7cddfSDavid du Colombier 	ldepth = idev->lastldepth;
3227dd7cddfSDavid du Colombier 	dither = idev->dither;
3237dd7cddfSDavid du Colombier 
3247dd7cddfSDavid du Colombier 	if(pdev->color_info.anti_alias.graphics_bits || pdev->color_info.anti_alias.text_bits)
3257dd7cddfSDavid du Colombier 		if(ldepth < 2)
3267dd7cddfSDavid du Colombier 			ldepth = 2;
3277dd7cddfSDavid du Colombier 
3287dd7cddfSDavid du Colombier //	printf("inferno_print_page dither %d ldepth %d idither %d\n", dither, ldepth, gs_inferno_device.dither);
3297dd7cddfSDavid du Colombier 	rect.min = ZP;
3307dd7cddfSDavid du Colombier 	rect.max.x = pdev->width;
3317dd7cddfSDavid du Colombier 	rect.max.y = pdev->height;
3327dd7cddfSDavid du Colombier 	bpl = bytesperline(rect, ldepth);
3337dd7cddfSDavid du Colombier 	w = initwriteimage(f, rect, ldepth);
3347dd7cddfSDavid du Colombier 	if(w == nil) {
335*593dc095SDavid du Colombier 		errprintf("initwriteimage failed\n");
3367dd7cddfSDavid du Colombier 		return_error(gs_error_Fatal);
3377dd7cddfSDavid du Colombier 	}
3387dd7cddfSDavid du Colombier 
3397dd7cddfSDavid du Colombier 	/*
3407dd7cddfSDavid du Colombier 	 * i wonder if it is faster to put the switch around the for loops
3417dd7cddfSDavid du Colombier 	 * to save all the ldepth lookups.
3427dd7cddfSDavid du Colombier 	 */
3437dd7cddfSDavid du Colombier 	for(y=0; y<pdev->height; y++) {
3447dd7cddfSDavid du Colombier 		gdev_prn_get_bits(pdev, y, buf, &p);
3457dd7cddfSDavid du Colombier 		for(x=0; x<pdev->width; x++) {
3467dd7cddfSDavid du Colombier 			b = p[3*x];
3477dd7cddfSDavid du Colombier 			g = p[3*x+1];
3487dd7cddfSDavid du Colombier 			r = p[3*x+2];
3497dd7cddfSDavid du Colombier 			us = ((b>>4) << 8) | ((g>>4) << 4) | (r>>4);
3507dd7cddfSDavid du Colombier 			switch(ldepth) {
3517dd7cddfSDavid du Colombier 			case 3:
3527dd7cddfSDavid du Colombier 				if(1 || dither){
3537dd7cddfSDavid du Colombier 					u = p9color[us];
3547dd7cddfSDavid du Colombier 					/* the ulong in p9color is a 2x2 matrix.  pull the entry
3557dd7cddfSDavid du Colombier 					 * u[x%2][y%2], more or less.
3567dd7cddfSDavid du Colombier 					 */
3577dd7cddfSDavid du Colombier 					p[x] = u >> (8*((y%2)+2*(x%2)));
3587dd7cddfSDavid du Colombier 				} else {
3597dd7cddfSDavid du Colombier 					p[x] = rgb2cmap(r, g, b);
3607dd7cddfSDavid du Colombier 				}
3617dd7cddfSDavid du Colombier 				break;
3627dd7cddfSDavid du Colombier 			case 2:
3637dd7cddfSDavid du Colombier 				us = ~us;
3647dd7cddfSDavid du Colombier 				if((x%2) == 0)
3657dd7cddfSDavid du Colombier 					p[x/2] = us & 0xf;
3667dd7cddfSDavid du Colombier 				else
3677dd7cddfSDavid du Colombier 					p[x/2] = (p[x/2]<<4)|(us&0xf);
3687dd7cddfSDavid du Colombier 				break;
3697dd7cddfSDavid du Colombier 			case 1:
3707dd7cddfSDavid du Colombier 				return_error(gs_error_Fatal);
3717dd7cddfSDavid du Colombier 			case 0:
3727dd7cddfSDavid du Colombier 				us = ~us;
3737dd7cddfSDavid du Colombier 				if((x%8) == 0)
3747dd7cddfSDavid du Colombier 					p[x/8] = us & 0x1;
3757dd7cddfSDavid du Colombier 				else
3767dd7cddfSDavid du Colombier 					p[x/8] = (p[x/8]<<1)|(us&0x1);
3777dd7cddfSDavid du Colombier 				break;
3787dd7cddfSDavid du Colombier 			}
3797dd7cddfSDavid du Colombier 		}
3807dd7cddfSDavid du Colombier 
3817dd7cddfSDavid du Colombier 		/* pad last byte over if we didn't fill it */
3827dd7cddfSDavid du Colombier 		xmod = pdev->width % ppb[ldepth];
3837dd7cddfSDavid du Colombier 		if(xmod)
3847dd7cddfSDavid du Colombier 			p[(x-1)/ppb[ldepth]] <<= ((ppb[ldepth]-xmod)*bpp[ldepth]);
3857dd7cddfSDavid du Colombier 		if(writeimageblock(w, p, bpl) == ERROR) {
386*593dc095SDavid du Colombier 			gs_free(pdev->memory, buf, gsbpl, 1, "inferno_print_page");
3877dd7cddfSDavid du Colombier 			return_error(gs_error_Fatal);
3887dd7cddfSDavid du Colombier 		}
3897dd7cddfSDavid du Colombier 	}
3907dd7cddfSDavid du Colombier 	if(writeimageblock(w, nil, 0) == ERROR) {
391*593dc095SDavid du Colombier 		gs_free(pdev->memory, buf, gsbpl, 1, "inferno_print_page");
3927dd7cddfSDavid du Colombier 		return_error(gs_error_Fatal);
3937dd7cddfSDavid du Colombier 	}
3947dd7cddfSDavid du Colombier 
395*593dc095SDavid du Colombier 	gs_free(pdev->memory, buf, gsbpl, 1, "inferno_print_page");
3967dd7cddfSDavid du Colombier 	return 0;
3977dd7cddfSDavid du Colombier }
3987dd7cddfSDavid du Colombier 
3997dd7cddfSDavid du Colombier /*
4007dd7cddfSDavid du Colombier  * this is a modified version of the image compressor
4017dd7cddfSDavid du Colombier  * from fb/bit2enc.  it is modified only in that it
4027dd7cddfSDavid du Colombier  * now compiles as part of gs.
4037dd7cddfSDavid du Colombier  */
4047dd7cddfSDavid du Colombier 
4057dd7cddfSDavid du Colombier /*
4067dd7cddfSDavid du Colombier  * Compressed image file parameters
4077dd7cddfSDavid du Colombier  */
4087dd7cddfSDavid du Colombier #define	NMATCH	3		/* shortest match possible */
4097dd7cddfSDavid du Colombier #define	NRUN	(NMATCH+31)	/* longest match possible */
4107dd7cddfSDavid du Colombier #define	NMEM	1024		/* window size */
4117dd7cddfSDavid du Colombier #define	NDUMP	128		/* maximum length of dump */
4127dd7cddfSDavid du Colombier #define	NCBLOCK	6000		/* size of compressed blocks */
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier #define	HSHIFT	3	/* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */
4157dd7cddfSDavid du Colombier #define	NHASH	(1<<(HSHIFT*NMATCH))
4167dd7cddfSDavid du Colombier #define	HMASK	(NHASH-1)
4177dd7cddfSDavid du Colombier #define	hupdate(h, c)	((((h)<<HSHIFT)^(c))&HMASK)
4187dd7cddfSDavid du Colombier 
4197dd7cddfSDavid du Colombier typedef struct Dump	Dump;
4207dd7cddfSDavid du Colombier typedef struct Hlist Hlist;
4217dd7cddfSDavid du Colombier 
4227dd7cddfSDavid du Colombier struct Hlist{
4237dd7cddfSDavid du Colombier 	ulong p;
4247dd7cddfSDavid du Colombier 	Hlist *next, *prev;
4257dd7cddfSDavid du Colombier };
4267dd7cddfSDavid du Colombier 
4277dd7cddfSDavid du Colombier struct Dump {
4287dd7cddfSDavid du Colombier 	int ndump;
4297dd7cddfSDavid du Colombier 	uchar *dumpbuf;
4307dd7cddfSDavid du Colombier 	uchar buf[1+NDUMP];
4317dd7cddfSDavid du Colombier };
4327dd7cddfSDavid du Colombier 
4337dd7cddfSDavid du Colombier struct WImage {
4347dd7cddfSDavid du Colombier 	FILE *f;
4357dd7cddfSDavid du Colombier 
4367dd7cddfSDavid du Colombier 	/* image attributes */
4377dd7cddfSDavid du Colombier 	Rectangle origr, r;
4387dd7cddfSDavid du Colombier 	int bpl;
4397dd7cddfSDavid du Colombier 
4407dd7cddfSDavid du Colombier 	/* output buffer */
4417dd7cddfSDavid du Colombier 	uchar outbuf[NCBLOCK], *outp, *eout, *loutp;
4427dd7cddfSDavid du Colombier 
4437dd7cddfSDavid du Colombier 	/* sliding input window */
4447dd7cddfSDavid du Colombier 	/*
4457dd7cddfSDavid du Colombier 	 * ibase is the pointer to where the beginning of
4467dd7cddfSDavid du Colombier 	 * the input "is" in memory.  whenever we "slide" the
4477dd7cddfSDavid du Colombier 	 * buffer N bytes, what we are actually doing is
4487dd7cddfSDavid du Colombier 	 * decrementing ibase by N.
4497dd7cddfSDavid du Colombier 	 * the ulongs in the Hlist structures are just
4507dd7cddfSDavid du Colombier 	 * pointers relative to ibase.
4517dd7cddfSDavid du Colombier 	 */
4527dd7cddfSDavid du Colombier 	uchar *inbuf;	/* inbuf should be at least NMEM+NRUN+NMATCH long */
4537dd7cddfSDavid du Colombier 	uchar *ibase;
4547dd7cddfSDavid du Colombier 	int minbuf;	/* size of inbuf (malloc'ed bytes) */
4557dd7cddfSDavid du Colombier 	int ninbuf;	/* size of inbuf (filled bytes) */
4567dd7cddfSDavid du Colombier 	ulong line;	/* the beginning of the line we are currently encoding,
4577dd7cddfSDavid du Colombier 			 * relative to inbuf (NOT relative to ibase) */
4587dd7cddfSDavid du Colombier 
4597dd7cddfSDavid du Colombier 	/* raw dump buffer */
4607dd7cddfSDavid du Colombier 	Dump dump;
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	/* hash tables */
4637dd7cddfSDavid du Colombier 	Hlist hash[NHASH];
4647dd7cddfSDavid du Colombier 	Hlist chain[NMEM], *cp;
4657dd7cddfSDavid du Colombier 	int h;
4667dd7cddfSDavid du Colombier 	int needhash;
4677dd7cddfSDavid du Colombier };
4687dd7cddfSDavid du Colombier 
4697dd7cddfSDavid du Colombier private void
zerohash(WImage * w)4707dd7cddfSDavid du Colombier zerohash(WImage *w)
4717dd7cddfSDavid du Colombier {
4727dd7cddfSDavid du Colombier 	memset(w->hash, 0, sizeof(w->hash));
4737dd7cddfSDavid du Colombier 	memset(w->chain, 0, sizeof(w->chain));
4747dd7cddfSDavid du Colombier 	w->cp=w->chain;
4757dd7cddfSDavid du Colombier 	w->needhash = 1;
4767dd7cddfSDavid du Colombier }
4777dd7cddfSDavid du Colombier 
4787dd7cddfSDavid du Colombier private int
addbuf(WImage * w,uchar * buf,int nbuf)4797dd7cddfSDavid du Colombier addbuf(WImage *w, uchar *buf, int nbuf)
4807dd7cddfSDavid du Colombier {
4817dd7cddfSDavid du Colombier 	int n;
4827dd7cddfSDavid du Colombier 	if(buf == nil || w->outp+nbuf > w->eout) {
4837dd7cddfSDavid du Colombier 		if(w->loutp==w->outbuf){	/* can't really happen -- we checked line length above */
484*593dc095SDavid du Colombier 			errprintf("buffer too small for line\n");
4857dd7cddfSDavid du Colombier 			return ERROR;
4867dd7cddfSDavid du Colombier 		}
4877dd7cddfSDavid du Colombier 		n=w->loutp-w->outbuf;
4887dd7cddfSDavid du Colombier 		fprintf(w->f, "%11d %11d ", w->r.max.y, n);
4897dd7cddfSDavid du Colombier 		fwrite(w->outbuf, 1, n, w->f);
4907dd7cddfSDavid du Colombier 		w->r.min.y=w->r.max.y;
4917dd7cddfSDavid du Colombier 		w->outp=w->outbuf;
4927dd7cddfSDavid du Colombier 		w->loutp=w->outbuf;
4937dd7cddfSDavid du Colombier 		zerohash(w);
4947dd7cddfSDavid du Colombier 		return -1;
4957dd7cddfSDavid du Colombier 	}
4967dd7cddfSDavid du Colombier 
4977dd7cddfSDavid du Colombier 	memmove(w->outp, buf, nbuf);
4987dd7cddfSDavid du Colombier 	w->outp += nbuf;
4997dd7cddfSDavid du Colombier 	return nbuf;
5007dd7cddfSDavid du Colombier }
5017dd7cddfSDavid du Colombier 
5027dd7cddfSDavid du Colombier /* return 0 on success, -1 if buffer is full */
5037dd7cddfSDavid du Colombier private int
flushdump(WImage * w)5047dd7cddfSDavid du Colombier flushdump(WImage *w)
5057dd7cddfSDavid du Colombier {
5067dd7cddfSDavid du Colombier 	int n = w->dump.ndump;
5077dd7cddfSDavid du Colombier 
5087dd7cddfSDavid du Colombier 	if(n == 0)
5097dd7cddfSDavid du Colombier 		return 0;
5107dd7cddfSDavid du Colombier 
5117dd7cddfSDavid du Colombier 	w->dump.buf[0] = 0x80|(n-1);
5127dd7cddfSDavid du Colombier 	if((n=addbuf(w, w->dump.buf, n+1)) == ERROR)
5137dd7cddfSDavid du Colombier 		return ERROR;
5147dd7cddfSDavid du Colombier 	if(n < 0)
5157dd7cddfSDavid du Colombier 		return -1;
5167dd7cddfSDavid du Colombier 	w->dump.ndump = 0;
5177dd7cddfSDavid du Colombier 	return 0;
5187dd7cddfSDavid du Colombier }
5197dd7cddfSDavid du Colombier 
5207dd7cddfSDavid du Colombier private void
updatehash(WImage * w,uchar * p,uchar * ep)5217dd7cddfSDavid du Colombier updatehash(WImage *w, uchar *p, uchar *ep)
5227dd7cddfSDavid du Colombier {
5237dd7cddfSDavid du Colombier 	uchar *q;
5247dd7cddfSDavid du Colombier 	Hlist *cp;
5257dd7cddfSDavid du Colombier 	Hlist *hash;
5267dd7cddfSDavid du Colombier 	int h;
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 	hash = w->hash;
5297dd7cddfSDavid du Colombier 	cp = w->cp;
5307dd7cddfSDavid du Colombier 	h = w->h;
5317dd7cddfSDavid du Colombier 	for(q=p; q<ep; q++) {
5327dd7cddfSDavid du Colombier 		if(cp->prev)
5337dd7cddfSDavid du Colombier 			cp->prev->next = cp->next;
5347dd7cddfSDavid du Colombier 		cp->next = hash[h].next;
5357dd7cddfSDavid du Colombier 		cp->prev = &hash[h];
5367dd7cddfSDavid du Colombier 		cp->prev->next = cp;
5377dd7cddfSDavid du Colombier 		if(cp->next)
5387dd7cddfSDavid du Colombier 			cp->next->prev = cp;
5397dd7cddfSDavid du Colombier 		cp->p = q - w->ibase;
5407dd7cddfSDavid du Colombier 		if(++cp == w->chain+NMEM)
5417dd7cddfSDavid du Colombier 			cp = w->chain;
5427dd7cddfSDavid du Colombier 		if(&q[NMATCH] < &w->inbuf[w->ninbuf])
5437dd7cddfSDavid du Colombier 			h = hupdate(h, q[NMATCH]);
5447dd7cddfSDavid du Colombier 	}
5457dd7cddfSDavid du Colombier 	w->cp = cp;
5467dd7cddfSDavid du Colombier 	w->h = h;
5477dd7cddfSDavid du Colombier }
5487dd7cddfSDavid du Colombier 
5497dd7cddfSDavid du Colombier /*
5507dd7cddfSDavid du Colombier  * attempt to process a line of input,
5517dd7cddfSDavid du Colombier  * returning the number of bytes actually processed.
5527dd7cddfSDavid du Colombier  *
5537dd7cddfSDavid du Colombier  * if the output buffer needs to be flushed, we flush
5547dd7cddfSDavid du Colombier  * the buffer and return 0.
5557dd7cddfSDavid du Colombier  * otherwise we return bpl
5567dd7cddfSDavid du Colombier  */
5577dd7cddfSDavid du Colombier private int
gobbleline(WImage * w)5587dd7cddfSDavid du Colombier gobbleline(WImage *w)
5597dd7cddfSDavid du Colombier {
5607dd7cddfSDavid du Colombier 	int runlen, n, offs;
5617dd7cddfSDavid du Colombier 	uchar *eline, *es, *best, *p, *s, *t;
5627dd7cddfSDavid du Colombier 	Hlist *hp;
5637dd7cddfSDavid du Colombier 	uchar buf[2];
5647dd7cddfSDavid du Colombier 	int rv;
5657dd7cddfSDavid du Colombier 
5667dd7cddfSDavid du Colombier 	if(w->needhash) {
5677dd7cddfSDavid du Colombier 		w->h = 0;
5687dd7cddfSDavid du Colombier 		for(n=0; n!=NMATCH; n++)
5697dd7cddfSDavid du Colombier 			w->h = hupdate(w->h, w->inbuf[w->line+n]);
5707dd7cddfSDavid du Colombier 		w->needhash = 0;
5717dd7cddfSDavid du Colombier 	}
5727dd7cddfSDavid du Colombier 	w->dump.ndump=0;
5737dd7cddfSDavid du Colombier 	eline=w->inbuf+w->line+w->bpl;
5747dd7cddfSDavid du Colombier 	for(p=w->inbuf+w->line;p!=eline;){
5757dd7cddfSDavid du Colombier 		es = (eline < p+NRUN) ? eline : p+NRUN;
5767dd7cddfSDavid du Colombier 
5777dd7cddfSDavid du Colombier 		best=nil;
5787dd7cddfSDavid du Colombier 		runlen=0;
5797dd7cddfSDavid du Colombier 		/* hash table lookup */
5807dd7cddfSDavid du Colombier 		for(hp=w->hash[w->h].next;hp;hp=hp->next){
5817dd7cddfSDavid du Colombier 			/*
5827dd7cddfSDavid du Colombier 			 * the next block is an optimization of
5837dd7cddfSDavid du Colombier 			 * for(s=p, t=w->ibase+hp->p; s<es && *s == *t; s++, t++)
5847dd7cddfSDavid du Colombier 			 * 	;
5857dd7cddfSDavid du Colombier 			 */
5867dd7cddfSDavid du Colombier 
5877dd7cddfSDavid du Colombier 			{	uchar *ss, *tt;
5887dd7cddfSDavid du Colombier 				s = p+runlen;
5897dd7cddfSDavid du Colombier 				t = w->ibase+hp->p+runlen;
5907dd7cddfSDavid du Colombier 				for(ss=s, tt=t; ss>=p && *ss == *tt; ss--, tt--)
5917dd7cddfSDavid du Colombier 					;
5927dd7cddfSDavid du Colombier 				if(ss < p)
5937dd7cddfSDavid du Colombier 					while(s<es && *s == *t)
5947dd7cddfSDavid du Colombier 						s++, t++;
5957dd7cddfSDavid du Colombier 			}
5967dd7cddfSDavid du Colombier 
5977dd7cddfSDavid du Colombier 			n = s-p;
5987dd7cddfSDavid du Colombier 
5997dd7cddfSDavid du Colombier 			if(n > runlen) {
6007dd7cddfSDavid du Colombier 				runlen = n;
6017dd7cddfSDavid du Colombier 				best = w->ibase+hp->p;
6027dd7cddfSDavid du Colombier 				if(p+runlen == es)
6037dd7cddfSDavid du Colombier 					break;
6047dd7cddfSDavid du Colombier 			}
6057dd7cddfSDavid du Colombier 		}
6067dd7cddfSDavid du Colombier 
6077dd7cddfSDavid du Colombier 		/*
6087dd7cddfSDavid du Colombier 		 * if we didn't find a long enough run, append to
6097dd7cddfSDavid du Colombier 		 * the raw dump buffer
6107dd7cddfSDavid du Colombier 		 */
6117dd7cddfSDavid du Colombier 		if(runlen<NMATCH){
6127dd7cddfSDavid du Colombier 			if(w->dump.ndump==NDUMP) {
6137dd7cddfSDavid du Colombier 				if((rv = flushdump(w)) == ERROR)
6147dd7cddfSDavid du Colombier 					return ERROR;
6157dd7cddfSDavid du Colombier 				if(rv < 0)
6167dd7cddfSDavid du Colombier 					return 0;
6177dd7cddfSDavid du Colombier 			}
6187dd7cddfSDavid du Colombier 			w->dump.dumpbuf[w->dump.ndump++]=*p;
6197dd7cddfSDavid du Colombier 			runlen=1;
6207dd7cddfSDavid du Colombier 		}else{
6217dd7cddfSDavid du Colombier 		/*
6227dd7cddfSDavid du Colombier 		 * otherwise, assuming the dump buffer is empty,
6237dd7cddfSDavid du Colombier 		 * add the compressed rep.
6247dd7cddfSDavid du Colombier 		 */
6257dd7cddfSDavid du Colombier 			if((rv = flushdump(w)) == ERROR)
6267dd7cddfSDavid du Colombier 				return ERROR;
6277dd7cddfSDavid du Colombier 			if(rv < 0)
6287dd7cddfSDavid du Colombier 				return 0;
6297dd7cddfSDavid du Colombier 			offs=p-best-1;
6307dd7cddfSDavid du Colombier 			buf[0] = ((runlen-NMATCH)<<2)|(offs>>8);
6317dd7cddfSDavid du Colombier 			buf[1] = offs&0xff;
6327dd7cddfSDavid du Colombier 			if(addbuf(w, buf, 2) < 0)
6337dd7cddfSDavid du Colombier 				return 0;
6347dd7cddfSDavid du Colombier 		}
6357dd7cddfSDavid du Colombier 
6367dd7cddfSDavid du Colombier 		/*
6377dd7cddfSDavid du Colombier 		 * add to hash tables what we just encoded
6387dd7cddfSDavid du Colombier 		 */
6397dd7cddfSDavid du Colombier 		updatehash(w, p, p+runlen);
6407dd7cddfSDavid du Colombier 		p += runlen;
6417dd7cddfSDavid du Colombier 	}
6427dd7cddfSDavid du Colombier 
6437dd7cddfSDavid du Colombier 	if((rv = flushdump(w)) == ERROR)
6447dd7cddfSDavid du Colombier 		return ERROR;
6457dd7cddfSDavid du Colombier 	if(rv < 0)
6467dd7cddfSDavid du Colombier 		return 0;
6477dd7cddfSDavid du Colombier 	w->line += w->bpl;
6487dd7cddfSDavid du Colombier 	w->loutp=w->outp;
6497dd7cddfSDavid du Colombier 	w->r.max.y++;
6507dd7cddfSDavid du Colombier 	return w->bpl;
6517dd7cddfSDavid du Colombier }
6527dd7cddfSDavid du Colombier 
6537dd7cddfSDavid du Colombier private uchar*
shiftwindow(WImage * w,uchar * data,uchar * edata)6547dd7cddfSDavid du Colombier shiftwindow(WImage *w, uchar *data, uchar *edata)
6557dd7cddfSDavid du Colombier {
6567dd7cddfSDavid du Colombier 	int n, m;
6577dd7cddfSDavid du Colombier 
6587dd7cddfSDavid du Colombier 	/* shift window over */
6597dd7cddfSDavid du Colombier 	if(w->line > NMEM) {
6607dd7cddfSDavid du Colombier 		n = w->line-NMEM;
6617dd7cddfSDavid du Colombier 		memmove(w->inbuf, w->inbuf+n, w->ninbuf-n);
6627dd7cddfSDavid du Colombier 		w->line -= n;
6637dd7cddfSDavid du Colombier 		w->ibase -= n;
6647dd7cddfSDavid du Colombier 		w->ninbuf -= n;
6657dd7cddfSDavid du Colombier 	}
6667dd7cddfSDavid du Colombier 
6677dd7cddfSDavid du Colombier 	/* fill right with data if available */
6687dd7cddfSDavid du Colombier 	if(w->minbuf > w->ninbuf && edata > data) {
6697dd7cddfSDavid du Colombier 		m = w->minbuf - w->ninbuf;
6707dd7cddfSDavid du Colombier 		if(edata-data < m)
6717dd7cddfSDavid du Colombier 			m = edata-data;
6727dd7cddfSDavid du Colombier 		memmove(w->inbuf+w->ninbuf, data, m);
6737dd7cddfSDavid du Colombier 		data += m;
6747dd7cddfSDavid du Colombier 		w->ninbuf += m;
6757dd7cddfSDavid du Colombier 	}
6767dd7cddfSDavid du Colombier 
6777dd7cddfSDavid du Colombier 	return data;
6787dd7cddfSDavid du Colombier }
6797dd7cddfSDavid du Colombier 
6807dd7cddfSDavid du Colombier private WImage*
initwriteimage(FILE * f,Rectangle r,int ldepth)6817dd7cddfSDavid du Colombier initwriteimage(FILE *f, Rectangle r, int ldepth)
6827dd7cddfSDavid du Colombier {
6837dd7cddfSDavid du Colombier 	WImage *w;
6847dd7cddfSDavid du Colombier 	int n, bpl;
6857dd7cddfSDavid du Colombier 
6867dd7cddfSDavid du Colombier 	bpl = bytesperline(r, ldepth);
6877dd7cddfSDavid du Colombier 	if(r.max.y <= r.min.y || r.max.x <= r.min.x || bpl <= 0) {
688*593dc095SDavid du Colombier 		errprintf("bad rectangle, ldepth");
6897dd7cddfSDavid du Colombier 		return nil;
6907dd7cddfSDavid du Colombier 	}
6917dd7cddfSDavid du Colombier 
6927dd7cddfSDavid du Colombier 	n = NMEM+NMATCH+NRUN+bpl*2;
6937dd7cddfSDavid du Colombier 	w = malloc(n+sizeof(*w));
6947dd7cddfSDavid du Colombier 	if(w == nil)
6957dd7cddfSDavid du Colombier 		return nil;
6967dd7cddfSDavid du Colombier 	w->inbuf = (uchar*) &w[1];
6977dd7cddfSDavid du Colombier 	w->ibase = w->inbuf;
6987dd7cddfSDavid du Colombier 	w->line = 0;
6997dd7cddfSDavid du Colombier 	w->minbuf = n;
7007dd7cddfSDavid du Colombier 	w->ninbuf = 0;
7017dd7cddfSDavid du Colombier 	w->origr = r;
7027dd7cddfSDavid du Colombier 	w->r = r;
7037dd7cddfSDavid du Colombier 	w->r.max.y = w->r.min.y;
7047dd7cddfSDavid du Colombier 	w->eout = w->outbuf+sizeof(w->outbuf);
7057dd7cddfSDavid du Colombier 	w->outp = w->loutp = w->outbuf;
7067dd7cddfSDavid du Colombier 	w->bpl = bpl;
7077dd7cddfSDavid du Colombier 	w->f = f;
7087dd7cddfSDavid du Colombier 	w->dump.dumpbuf = w->dump.buf+1;
7097dd7cddfSDavid du Colombier 	w->dump.ndump = 0;
7107dd7cddfSDavid du Colombier 	zerohash(w);
7117dd7cddfSDavid du Colombier 
7127dd7cddfSDavid du Colombier 	fprintf(f, "compressed\n%11d %11d %11d %11d %11d ",
7137dd7cddfSDavid du Colombier 		ldepth, r.min.x, r.min.y, r.max.x, r.max.y);
7147dd7cddfSDavid du Colombier 	return w;
7157dd7cddfSDavid du Colombier }
7167dd7cddfSDavid du Colombier 
7177dd7cddfSDavid du Colombier private int
writeimageblock(WImage * w,uchar * data,int ndata)7187dd7cddfSDavid du Colombier writeimageblock(WImage *w, uchar *data, int ndata)
7197dd7cddfSDavid du Colombier {
7207dd7cddfSDavid du Colombier 	uchar *edata;
7217dd7cddfSDavid du Colombier 
7227dd7cddfSDavid du Colombier 	if(data == nil) {	/* end of data, flush everything */
7237dd7cddfSDavid du Colombier 		while(w->line < w->ninbuf)
7247dd7cddfSDavid du Colombier 			if(gobbleline(w) == ERROR)
7257dd7cddfSDavid du Colombier 				return ERROR;
7267dd7cddfSDavid du Colombier 		addbuf(w, nil, 0);
7277dd7cddfSDavid du Colombier 		if(w->r.min.y != w->origr.max.y) {
728*593dc095SDavid du Colombier 			errprintf("not enough data supplied to writeimage\n");
7297dd7cddfSDavid du Colombier 		}
7307dd7cddfSDavid du Colombier 		free(w);
7317dd7cddfSDavid du Colombier 		return 0;
7327dd7cddfSDavid du Colombier 	}
7337dd7cddfSDavid du Colombier 
7347dd7cddfSDavid du Colombier 	edata = data+ndata;
7357dd7cddfSDavid du Colombier 	data = shiftwindow(w, data, edata);
7367dd7cddfSDavid du Colombier 	while(w->ninbuf >= w->line+w->bpl+NMATCH) {
7377dd7cddfSDavid du Colombier 		if(gobbleline(w) == ERROR)
7387dd7cddfSDavid du Colombier 			return ERROR;
7397dd7cddfSDavid du Colombier 		data = shiftwindow(w, data, edata);
7407dd7cddfSDavid du Colombier 	}
7417dd7cddfSDavid du Colombier 	if(data != edata) {
7427dd7cddfSDavid du Colombier 		fprintf(w->f, "data != edata.  uh oh\n");
7437dd7cddfSDavid du Colombier 		return ERROR; /* can't happen */
7447dd7cddfSDavid du Colombier 	}
7457dd7cddfSDavid du Colombier 	return 0;
7467dd7cddfSDavid du Colombier }
7477dd7cddfSDavid du Colombier 
7487dd7cddfSDavid du Colombier /*
7497dd7cddfSDavid du Colombier  * functions from the Plan9/Brazil drawing libraries
7507dd7cddfSDavid du Colombier  */
7517dd7cddfSDavid du Colombier private int
bytesperline(Rectangle r,int ld)7527dd7cddfSDavid du Colombier bytesperline(Rectangle r, int ld)
7537dd7cddfSDavid du Colombier {
7547dd7cddfSDavid du Colombier 	ulong ws, l, t;
7557dd7cddfSDavid du Colombier 	int bits = 8;
7567dd7cddfSDavid du Colombier 
7577dd7cddfSDavid du Colombier 	ws = bits>>ld;	/* pixels per unit */
7587dd7cddfSDavid du Colombier 	if(r.min.x >= 0){
7597dd7cddfSDavid du Colombier 		l = (r.max.x+ws-1)/ws;
7607dd7cddfSDavid du Colombier 		l -= r.min.x/ws;
7617dd7cddfSDavid du Colombier 	}else{			/* make positive before divide */
7627dd7cddfSDavid du Colombier 		t = (-r.min.x)+ws-1;
7637dd7cddfSDavid du Colombier 		t = (t/ws)*ws;
7647dd7cddfSDavid du Colombier 		l = (t+r.max.x+ws-1)/ws;
7657dd7cddfSDavid du Colombier 	}
7667dd7cddfSDavid du Colombier 	return l;
7677dd7cddfSDavid du Colombier }
7687dd7cddfSDavid du Colombier 
7697dd7cddfSDavid du Colombier private int
rgb2cmap(int cr,int cg,int cb)7707dd7cddfSDavid du Colombier rgb2cmap(int cr, int cg, int cb)
7717dd7cddfSDavid du Colombier {
7727dd7cddfSDavid du Colombier 	int r, g, b, v, cv;
7737dd7cddfSDavid du Colombier 
7747dd7cddfSDavid du Colombier 	if(cr < 0)
7757dd7cddfSDavid du Colombier 		cr = 0;
7767dd7cddfSDavid du Colombier 	else if(cr > 255)
7777dd7cddfSDavid du Colombier 		cr = 255;
7787dd7cddfSDavid du Colombier 	if(cg < 0)
7797dd7cddfSDavid du Colombier 		cg = 0;
7807dd7cddfSDavid du Colombier 	else if(cg > 255)
7817dd7cddfSDavid du Colombier 		cg = 255;
7827dd7cddfSDavid du Colombier 	if(cb < 0)
7837dd7cddfSDavid du Colombier 		cb = 0;
7847dd7cddfSDavid du Colombier 	else if(cb > 255)
7857dd7cddfSDavid du Colombier 		cb = 255;
7867dd7cddfSDavid du Colombier 	r = cr>>6;
7877dd7cddfSDavid du Colombier 	g = cg>>6;
7887dd7cddfSDavid du Colombier 	b = cb>>6;
7897dd7cddfSDavid du Colombier 	cv = cr;
7907dd7cddfSDavid du Colombier 	if(cg > cv)
7917dd7cddfSDavid du Colombier 		cv = cg;
7927dd7cddfSDavid du Colombier 	if(cb > cv)
7937dd7cddfSDavid du Colombier 		cv = cb;
7947dd7cddfSDavid du Colombier 	v = (cv>>4)&3;
7957dd7cddfSDavid du Colombier 	return 255-((((r<<2)+v)<<4)+(((g<<2)+b+v-r)&15));
7967dd7cddfSDavid du Colombier }
7977dd7cddfSDavid du Colombier 
7987dd7cddfSDavid du Colombier /*
7997dd7cddfSDavid du Colombier  * go the other way; not currently used.
8007dd7cddfSDavid du Colombier  *
8017dd7cddfSDavid du Colombier private long
8027dd7cddfSDavid du Colombier cmap2rgb(int c)
8037dd7cddfSDavid du Colombier {
8047dd7cddfSDavid du Colombier 	int j, num, den, r, g, b, v, rgb;
8057dd7cddfSDavid du Colombier 
8067dd7cddfSDavid du Colombier 	c = 255-c;
8077dd7cddfSDavid du Colombier 	r = c>>6;
8087dd7cddfSDavid du Colombier 	v = (c>>4)&3;
8097dd7cddfSDavid du Colombier 	j = (c-v+r)&15;
8107dd7cddfSDavid du Colombier 	g = j>>2;
8117dd7cddfSDavid du Colombier 	b = j&3;
8127dd7cddfSDavid du Colombier 	den=r;
8137dd7cddfSDavid du Colombier 	if(g>den)
8147dd7cddfSDavid du Colombier 		den=g;
8157dd7cddfSDavid du Colombier 	if(b>den)
8167dd7cddfSDavid du Colombier 		den=b;
8177dd7cddfSDavid du Colombier 	if(den==0) {
8187dd7cddfSDavid du Colombier 		v *= 17;
8197dd7cddfSDavid du Colombier 		rgb = (v<<16)|(v<<8)|v;
8207dd7cddfSDavid du Colombier 	}
8217dd7cddfSDavid du Colombier 	else{
8227dd7cddfSDavid du Colombier 		num=17*(4*den+v);
8237dd7cddfSDavid du Colombier 		rgb = ((r*num/den)<<16)|((g*num/den)<<8)|(b*num/den);
8247dd7cddfSDavid du Colombier 	}
8257dd7cddfSDavid du Colombier 	return rgb;
8267dd7cddfSDavid du Colombier }
8277dd7cddfSDavid du Colombier  *
8287dd7cddfSDavid du Colombier  *
8297dd7cddfSDavid du Colombier  */
8307dd7cddfSDavid du Colombier 
831