xref: /plan9/sys/src/cmd/gs/src/gdevplan9.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
13ff48bf5SDavid du Colombier /*
23ff48bf5SDavid du Colombier  *		Copyright (c) 1998 by Lucent Technologies.
33ff48bf5SDavid du Colombier  * Permission to use, copy, modify, and distribute this software for any
43ff48bf5SDavid du Colombier  * purpose without fee is hereby granted, provided that this entire notice
53ff48bf5SDavid du Colombier  * is included in all copies of any software which is or includes a copy
63ff48bf5SDavid du Colombier  * or modification of this software.
73ff48bf5SDavid du Colombier  *
83ff48bf5SDavid du Colombier  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
93ff48bf5SDavid du Colombier  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
103ff48bf5SDavid du Colombier  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
113ff48bf5SDavid du Colombier  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
123ff48bf5SDavid du Colombier  */
133ff48bf5SDavid du Colombier 
143ff48bf5SDavid du Colombier /*
153ff48bf5SDavid du Colombier  * gdevplan9.c: gs device to generate plan9 bitmaps
163ff48bf5SDavid du Colombier  * Russ Cox <rsc@plan9.bell-labs.com>, 3/25/98 (gdevifno)
173ff48bf5SDavid du Colombier  * Updated to fit in the standard GS distribution, 5/14/98
183ff48bf5SDavid du Colombier  * Added support for true-color bitmaps, 6/7/02
193ff48bf5SDavid du Colombier  */
203ff48bf5SDavid du Colombier 
213ff48bf5SDavid du Colombier #include "gdevprn.h"
223ff48bf5SDavid du Colombier #include "gsparam.h"
233ff48bf5SDavid du Colombier #include "gxlum.h"
24*593dc095SDavid du Colombier #include "gxstdio.h"
253ff48bf5SDavid du Colombier #include <stdlib.h>
263ff48bf5SDavid du Colombier 
273ff48bf5SDavid du Colombier #define nil ((void*)0)
283ff48bf5SDavid du Colombier enum {
293ff48bf5SDavid du Colombier 	ERROR = -2
303ff48bf5SDavid du Colombier };
313ff48bf5SDavid du Colombier 
323ff48bf5SDavid du Colombier typedef struct WImage WImage;
333ff48bf5SDavid du Colombier typedef struct Rectangle Rectangle;
343ff48bf5SDavid du Colombier typedef struct Point Point;
353ff48bf5SDavid du Colombier 
363ff48bf5SDavid du Colombier struct Point {
373ff48bf5SDavid du Colombier 	int x;
383ff48bf5SDavid du Colombier 	int y;
393ff48bf5SDavid du Colombier };
403ff48bf5SDavid du Colombier 
413ff48bf5SDavid du Colombier struct Rectangle {
423ff48bf5SDavid du Colombier 	Point min;
433ff48bf5SDavid du Colombier 	Point max;
443ff48bf5SDavid du Colombier };
453ff48bf5SDavid du Colombier private Point ZP = { 0, 0 };
463ff48bf5SDavid du Colombier 
473ff48bf5SDavid du Colombier private WImage* initwriteimage(FILE *f, Rectangle r, char*, int depth);
483ff48bf5SDavid du Colombier private int writeimageblock(WImage *w, uchar *data, int ndata);
493ff48bf5SDavid du Colombier private int bytesperline(Rectangle, int);
503ff48bf5SDavid du Colombier private int rgb2cmap(int, int, int);
513ff48bf5SDavid du Colombier private long cmap2rgb(int);
523ff48bf5SDavid du Colombier 
533ff48bf5SDavid du Colombier #define X_DPI	100
543ff48bf5SDavid du Colombier #define Y_DPI	100
553ff48bf5SDavid du Colombier 
563ff48bf5SDavid du Colombier private dev_proc_map_rgb_color(plan9_rgb2cmap);
573ff48bf5SDavid du Colombier private dev_proc_map_color_rgb(plan9_cmap2rgb);
583ff48bf5SDavid du Colombier private dev_proc_open_device(plan9_open);
593ff48bf5SDavid du Colombier private dev_proc_close_device(plan9_close);
603ff48bf5SDavid du Colombier private dev_proc_print_page(plan9_print_page);
613ff48bf5SDavid du Colombier private dev_proc_put_params(plan9_put_params);
623ff48bf5SDavid du Colombier private dev_proc_get_params(plan9_get_params);
633ff48bf5SDavid du Colombier 
643ff48bf5SDavid du Colombier typedef struct plan9_device_s {
653ff48bf5SDavid du Colombier 	gx_device_common;
663ff48bf5SDavid du Colombier 	gx_prn_device_common;
673ff48bf5SDavid du Colombier 	int dither;
683ff48bf5SDavid du Colombier 
693ff48bf5SDavid du Colombier 	int ldepth;
703ff48bf5SDavid du Colombier 	int lastldepth;
713ff48bf5SDavid du Colombier 	int cmapcall;
723ff48bf5SDavid du Colombier } plan9_device;
733ff48bf5SDavid du Colombier 
743ff48bf5SDavid du Colombier enum {
753ff48bf5SDavid du Colombier 	Nbits = 8,
763ff48bf5SDavid du Colombier 	Bitmask = (1<<Nbits)-1,
773ff48bf5SDavid du Colombier };
783ff48bf5SDavid du Colombier 
793ff48bf5SDavid du Colombier private const gx_device_procs plan9_procs =
803ff48bf5SDavid du Colombier 	prn_color_params_procs(plan9_open, gdev_prn_output_page, gdev_prn_close,
813ff48bf5SDavid du Colombier 		plan9_rgb2cmap, plan9_cmap2rgb,
823ff48bf5SDavid du Colombier 		gdev_prn_get_params, gdev_prn_put_params);
833ff48bf5SDavid du Colombier /*
843ff48bf5SDavid du Colombier 		plan9_get_params, plan9_put_params);
853ff48bf5SDavid du Colombier */
863ff48bf5SDavid du Colombier 
873ff48bf5SDavid du Colombier 
883ff48bf5SDavid du Colombier plan9_device far_data gs_plan9_device =
893ff48bf5SDavid du Colombier { prn_device_body(plan9_device, plan9_procs, "plan9",
903ff48bf5SDavid du Colombier 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
913ff48bf5SDavid du Colombier 	X_DPI, Y_DPI,
923ff48bf5SDavid du Colombier 	0,0,0,0,	/* margins */
933ff48bf5SDavid du Colombier 	3,		/* 3 = RGB, 1 = gray, 4 = CMYK */
943ff48bf5SDavid du Colombier 	Nbits*3,		/* # of bits per pixel */
953ff48bf5SDavid du Colombier 	(1<<Nbits)-1,		/* # of distinct gray levels. */
963ff48bf5SDavid du Colombier 	(1<<Nbits)-1,		/* # of distinct color levels. */
973ff48bf5SDavid du Colombier 	1<<Nbits,		/* dither gray ramp size.  used in alpha? */
983ff48bf5SDavid du Colombier 	1<<Nbits,    	/* dither color ramp size.  used in alpha? */
993ff48bf5SDavid du Colombier 	plan9_print_page),
1003ff48bf5SDavid du Colombier 	1,
1013ff48bf5SDavid du Colombier };
1023ff48bf5SDavid du Colombier 
1033ff48bf5SDavid du Colombier /*
1043ff48bf5SDavid du Colombier  * ghostscript asks us how to convert between
1053ff48bf5SDavid du Colombier  * rgb and color map entries
1063ff48bf5SDavid du Colombier  */
1073ff48bf5SDavid du Colombier private gx_color_index
plan9_rgb2cmap(gx_device * dev,gx_color_value * rgb)108*593dc095SDavid du Colombier plan9_rgb2cmap(gx_device *dev, gx_color_value *rgb)
109*593dc095SDavid du Colombier {
110*593dc095SDavid du Colombier 	gx_color_value r, g, b;
1113ff48bf5SDavid du Colombier 	int shift;
1123ff48bf5SDavid du Colombier 	plan9_device *idev;
1133ff48bf5SDavid du Colombier 	ulong red, green, blue;
1143ff48bf5SDavid du Colombier 
115*593dc095SDavid du Colombier 	r = rgb[0];
116*593dc095SDavid du Colombier 	g = rgb[1];
117*593dc095SDavid du Colombier 	b = rgb[2];
118*593dc095SDavid du Colombier 
1193ff48bf5SDavid du Colombier 	idev = (plan9_device*) dev;
1203ff48bf5SDavid du Colombier 
1213ff48bf5SDavid du Colombier 	shift = gx_color_value_bits - Nbits;
1223ff48bf5SDavid du Colombier 	red = r >> shift;
1233ff48bf5SDavid du Colombier 	green = g >> shift;
1243ff48bf5SDavid du Colombier 	blue = b >> shift;
1253ff48bf5SDavid du Colombier 
1263ff48bf5SDavid du Colombier 	/*
1273ff48bf5SDavid du Colombier 	 * we keep track of what ldepth bitmap this is by watching
1283ff48bf5SDavid du Colombier 	 * what colors gs asks for.
1293ff48bf5SDavid du Colombier 	 *
1303ff48bf5SDavid du Colombier 	 * one catch: sometimes print_page gets called more than one
1313ff48bf5SDavid du Colombier 	 * per page (for multiple copies) without cmap calls inbetween.
1323ff48bf5SDavid du Colombier 	 * if idev->cmapcall is 0 when print_page gets called, it uses
1333ff48bf5SDavid du Colombier 	 * the ldepth of the last page.
1343ff48bf5SDavid du Colombier 	 */
1353ff48bf5SDavid du Colombier 	if(red == green && green == blue) {
1363ff48bf5SDavid du Colombier 		if(red == 0 || red == Bitmask)
1373ff48bf5SDavid du Colombier 			;
1383ff48bf5SDavid du Colombier 		else if(red == Bitmask/3 || red == 2*Bitmask/3) {
1393ff48bf5SDavid du Colombier 			if(idev->ldepth < 1)
1403ff48bf5SDavid du Colombier 				idev->ldepth = 1;
1413ff48bf5SDavid du Colombier 		} else {
1423ff48bf5SDavid du Colombier 			if(idev->ldepth < 2)
1433ff48bf5SDavid du Colombier 				idev->ldepth = 2;
1443ff48bf5SDavid du Colombier 		}
1453ff48bf5SDavid du Colombier 	} else
1463ff48bf5SDavid du Colombier 		idev->ldepth = 3;
1473ff48bf5SDavid du Colombier 
1483ff48bf5SDavid du Colombier 	idev->cmapcall = 1;
1493ff48bf5SDavid du Colombier 	return (blue << (2*Nbits)) | (green << Nbits) | red;
1503ff48bf5SDavid du Colombier }
1513ff48bf5SDavid du Colombier 
1523ff48bf5SDavid du Colombier private int
plan9_cmap2rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])153*593dc095SDavid du Colombier plan9_cmap2rgb(gx_device *dev, gx_color_index color,
154*593dc095SDavid du Colombier   gx_color_value rgb[3]) {
1553ff48bf5SDavid du Colombier 	int shift, i;
1563ff48bf5SDavid du Colombier 	plan9_device *idev;
1573ff48bf5SDavid du Colombier 
1583ff48bf5SDavid du Colombier 	if((ulong)color > 0xFFFFFF)
1593ff48bf5SDavid du Colombier 		return_error(gs_error_rangecheck);
1603ff48bf5SDavid du Colombier 
1613ff48bf5SDavid du Colombier 	idev = (plan9_device*) dev;
1623ff48bf5SDavid du Colombier 	shift = gx_color_value_bits - Nbits;
1633ff48bf5SDavid du Colombier 
1643ff48bf5SDavid du Colombier 	rgb[2] = ((color >> (2*Nbits)) & Bitmask) << shift;
1653ff48bf5SDavid du Colombier 	rgb[1] = ((color >> Nbits) & Bitmask) << shift;
1663ff48bf5SDavid du Colombier 	rgb[0] = (color & Bitmask) << shift;
1673ff48bf5SDavid du Colombier 
1683ff48bf5SDavid du Colombier 	return 0;
1693ff48bf5SDavid du Colombier }
1703ff48bf5SDavid du Colombier 
1713ff48bf5SDavid du Colombier private int
plan9_put_param_int(gs_param_list * plist,gs_param_name pname,int * pv,int minval,int maxval,int ecode)1723ff48bf5SDavid du Colombier plan9_put_param_int(gs_param_list *plist, gs_param_name pname, int *pv,
1733ff48bf5SDavid du Colombier 	int minval, int maxval, int ecode)
1743ff48bf5SDavid du Colombier {
1753ff48bf5SDavid du Colombier 	int code, value;
1763ff48bf5SDavid du Colombier 	switch(code = param_read_int(plist, pname, &value)) {
1773ff48bf5SDavid du Colombier 	default:
1783ff48bf5SDavid du Colombier 		return code;
1793ff48bf5SDavid du Colombier 
1803ff48bf5SDavid du Colombier 	case 1:
1813ff48bf5SDavid du Colombier 		return ecode;
1823ff48bf5SDavid du Colombier 
1833ff48bf5SDavid du Colombier 	case 0:
1843ff48bf5SDavid du Colombier 		if(value < minval || value > maxval)
1853ff48bf5SDavid du Colombier 			param_signal_error(plist, pname, gs_error_rangecheck);
1863ff48bf5SDavid du Colombier 		*pv = value;
1873ff48bf5SDavid du Colombier 		return (ecode < 0 ? ecode : 1);
1883ff48bf5SDavid du Colombier 	}
1893ff48bf5SDavid du Colombier }
1903ff48bf5SDavid du Colombier 
1913ff48bf5SDavid du Colombier private int
plan9_get_params(gx_device * pdev,gs_param_list * plist)1923ff48bf5SDavid du Colombier plan9_get_params(gx_device *pdev, gs_param_list *plist)
1933ff48bf5SDavid du Colombier {
1943ff48bf5SDavid du Colombier 	int code;
1953ff48bf5SDavid du Colombier 	plan9_device *idev;
1963ff48bf5SDavid du Colombier 
1973ff48bf5SDavid du Colombier 	idev = (plan9_device*) pdev;
1983ff48bf5SDavid du Colombier //	printf("plan9_get_params dither %d\n", idev->dither);
1993ff48bf5SDavid du Colombier 
2003ff48bf5SDavid du Colombier 	if((code = gdev_prn_get_params(pdev, plist)) < 0
2013ff48bf5SDavid du Colombier 	 || (code = param_write_int(plist, "Dither", &idev->dither)) < 0)
2023ff48bf5SDavid du Colombier 		return code;
203*593dc095SDavid du Colombier //	printf("getparams: dither=%d\n", idev->dither);
2043ff48bf5SDavid du Colombier 	return code;
2053ff48bf5SDavid du Colombier }
2063ff48bf5SDavid du Colombier 
2073ff48bf5SDavid du Colombier private int
plan9_put_params(gx_device * pdev,gs_param_list * plist)2083ff48bf5SDavid du Colombier plan9_put_params(gx_device * pdev, gs_param_list * plist)
2093ff48bf5SDavid du Colombier {
2103ff48bf5SDavid du Colombier 	int code;
2113ff48bf5SDavid du Colombier 	int dither;
2123ff48bf5SDavid du Colombier 	plan9_device *idev;
2133ff48bf5SDavid du Colombier 
214*593dc095SDavid du Colombier //	printf("plan9_put_params\n");
2153ff48bf5SDavid du Colombier 
2163ff48bf5SDavid du Colombier 	idev = (plan9_device*)pdev;
2173ff48bf5SDavid du Colombier 	dither = idev->dither;
2183ff48bf5SDavid du Colombier 
2193ff48bf5SDavid du Colombier 	code = plan9_put_param_int(plist, "Dither", &dither, 0, 1, 0);
2203ff48bf5SDavid du Colombier 	if(code < 0)
2213ff48bf5SDavid du Colombier 		return code;
2223ff48bf5SDavid du Colombier 
2233ff48bf5SDavid du Colombier 	idev->dither = dither;
2243ff48bf5SDavid du Colombier 	return 0;
2253ff48bf5SDavid du Colombier }
2263ff48bf5SDavid du Colombier /*
2273ff48bf5SDavid du Colombier  * plan9_open() is supposed to initialize the device.
2283ff48bf5SDavid du Colombier  * there's not much to do.
2293ff48bf5SDavid du Colombier  */
230*593dc095SDavid du Colombier extern void init_p9color(void);	/* in gdevifno.c */
2313ff48bf5SDavid du Colombier private int
plan9_open(gx_device * dev)232*593dc095SDavid du Colombier plan9_open(gx_device *dev)
2333ff48bf5SDavid du Colombier {
2343ff48bf5SDavid du Colombier 	int code;
2353ff48bf5SDavid du Colombier 	plan9_device *idev;
2363ff48bf5SDavid du Colombier 
2373ff48bf5SDavid du Colombier 	idev = (plan9_device*) dev;
2383ff48bf5SDavid du Colombier 	idev->cmapcall = 0;
2393ff48bf5SDavid du Colombier 	idev->ldepth = 0;
2403ff48bf5SDavid du Colombier 
2413ff48bf5SDavid du Colombier //	printf("plan9_open gs_plan9_device.dither = %d idev->dither = %d\n",
2423ff48bf5SDavid du Colombier //		gs_plan9_device.dither, idev->dither);
2433ff48bf5SDavid du Colombier 	init_p9color();
2443ff48bf5SDavid du Colombier 
2453ff48bf5SDavid du Colombier 	return gdev_prn_open(dev);
2463ff48bf5SDavid du Colombier }
2473ff48bf5SDavid du Colombier 
2483ff48bf5SDavid du Colombier /*
2493ff48bf5SDavid du Colombier  * plan9_print_page() is called once for each page
2503ff48bf5SDavid du Colombier  * (actually once for each copy of each page, but we won't
2513ff48bf5SDavid du Colombier  * worry about that).
2523ff48bf5SDavid du Colombier  */
2533ff48bf5SDavid du Colombier private int
plan9_print_page(gx_device_printer * pdev,FILE * f)254*593dc095SDavid du Colombier plan9_print_page(gx_device_printer *pdev, FILE *f)
2553ff48bf5SDavid du Colombier {
2563ff48bf5SDavid du Colombier 	char *chanstr;
2573ff48bf5SDavid du Colombier 	uchar *buf;	/* [8192*3*8/Nbits] BUG: malloc this */
2583ff48bf5SDavid du Colombier 	uchar *p;
2593ff48bf5SDavid du Colombier 	WImage *w;
2603ff48bf5SDavid du Colombier 	int bpl, y;
2613ff48bf5SDavid du Colombier 	int x, xmod;
2623ff48bf5SDavid du Colombier 	int ldepth;
2633ff48bf5SDavid du Colombier 	int ppb[] = {8, 4, 2, 1};	/* pixels per byte */
2643ff48bf5SDavid du Colombier 	int bpp[] = {1, 2, 4, 8};	/* bits per pixel */
2653ff48bf5SDavid du Colombier 	int gsbpl;
2663ff48bf5SDavid du Colombier 	int dither;
2673ff48bf5SDavid du Colombier 	int depth;
2683ff48bf5SDavid du Colombier 	ulong u;
2693ff48bf5SDavid du Colombier 	ushort us;
2703ff48bf5SDavid du Colombier 	Rectangle rect;
2713ff48bf5SDavid du Colombier 	plan9_device *idev;
2723ff48bf5SDavid du Colombier 	uchar *r;
2733ff48bf5SDavid du Colombier 
2743ff48bf5SDavid du Colombier 	gsbpl = gdev_prn_raster(pdev);
275*593dc095SDavid du Colombier 	buf = gs_malloc(pdev->memory, gsbpl, 1, "plan9_print_page");
2763ff48bf5SDavid du Colombier 
2773ff48bf5SDavid du Colombier 	if(buf == nil) {
278*593dc095SDavid du Colombier 		errprintf("out of memory\n");
2793ff48bf5SDavid du Colombier 		return_error(gs_error_Fatal);
2803ff48bf5SDavid du Colombier 	}
2813ff48bf5SDavid du Colombier 
2823ff48bf5SDavid du Colombier 	idev = (plan9_device *) pdev;
2833ff48bf5SDavid du Colombier 	if(idev->cmapcall) {
2843ff48bf5SDavid du Colombier 		idev->lastldepth = idev->ldepth;
2853ff48bf5SDavid du Colombier 		idev->ldepth = 0;
2863ff48bf5SDavid du Colombier 		idev->cmapcall = 0;
2873ff48bf5SDavid du Colombier 	}
2883ff48bf5SDavid du Colombier 	ldepth = idev->lastldepth;
2893ff48bf5SDavid du Colombier 	dither = idev->dither;
2903ff48bf5SDavid du Colombier 
2913ff48bf5SDavid du Colombier 	if(pdev->color_info.anti_alias.graphics_bits || pdev->color_info.anti_alias.text_bits)
2923ff48bf5SDavid du Colombier 		if(ldepth < 2)
2933ff48bf5SDavid du Colombier 			ldepth = 2;
2943ff48bf5SDavid du Colombier 
2953ff48bf5SDavid du Colombier 	chanstr = nil;
2963ff48bf5SDavid du Colombier 	depth = 0;
2973ff48bf5SDavid du Colombier 	switch(ldepth){
2983ff48bf5SDavid du Colombier 	case 0:
2993ff48bf5SDavid du Colombier 		chanstr = "k1";
3003ff48bf5SDavid du Colombier 		depth = 1;
3013ff48bf5SDavid du Colombier 		break;
3023ff48bf5SDavid du Colombier 	case 1:
3033ff48bf5SDavid du Colombier 		return_error(gs_error_Fatal);
3043ff48bf5SDavid du Colombier 	case 2:
3053ff48bf5SDavid du Colombier 		chanstr = "k4";
3063ff48bf5SDavid du Colombier 		depth = 4;
3073ff48bf5SDavid du Colombier 		break;
3083ff48bf5SDavid du Colombier 	case 3:
3093ff48bf5SDavid du Colombier 		chanstr = "r8g8b8";
3103ff48bf5SDavid du Colombier 		depth = 24;
3113ff48bf5SDavid du Colombier 		break;
3123ff48bf5SDavid du Colombier 	}
3133ff48bf5SDavid du Colombier 
3143ff48bf5SDavid du Colombier //	printf("plan9_print_page dither %d ldepth %d idither %d\n", dither, ldepth, gs_plan9_device.dither);
3153ff48bf5SDavid du Colombier 	rect.min = ZP;
3163ff48bf5SDavid du Colombier 	rect.max.x = pdev->width;
3173ff48bf5SDavid du Colombier 	rect.max.y = pdev->height;
3183ff48bf5SDavid du Colombier 	bpl = bytesperline(rect, depth);
3193ff48bf5SDavid du Colombier 	w = initwriteimage(f, rect, chanstr, depth);
3203ff48bf5SDavid du Colombier 	if(w == nil) {
321*593dc095SDavid du Colombier 		errprintf("initwriteimage failed\n");
3223ff48bf5SDavid du Colombier 		return_error(gs_error_Fatal);
3233ff48bf5SDavid du Colombier 	}
3243ff48bf5SDavid du Colombier 
3253ff48bf5SDavid du Colombier 	/*
3263ff48bf5SDavid du Colombier 	 * i wonder if it is faster to put the switch around the for loops
3273ff48bf5SDavid du Colombier 	 * to save all the ldepth lookups.
3283ff48bf5SDavid du Colombier 	 */
3293ff48bf5SDavid du Colombier 	for(y=0; y<pdev->height; y++) {
3303ff48bf5SDavid du Colombier 		gdev_prn_get_bits(pdev, y, buf, &p);
3313ff48bf5SDavid du Colombier 		r = p+2;
3323ff48bf5SDavid du Colombier 		switch(depth){
3333ff48bf5SDavid du Colombier 		default:
3343ff48bf5SDavid du Colombier 			return_error(gs_error_Fatal);
3353ff48bf5SDavid du Colombier 		case 1:
3363ff48bf5SDavid du Colombier 			for(x=0; x<pdev->width; x++){
3373ff48bf5SDavid du Colombier 				if((x%8) == 0)
3383ff48bf5SDavid du Colombier 					p[x/8] = (*r>>4)&1;
3393ff48bf5SDavid du Colombier 				else
3403ff48bf5SDavid du Colombier 					p[x/8] = (p[x/8]<<1) | (*r>>4)&1;
3413ff48bf5SDavid du Colombier 				r += 3;
3423ff48bf5SDavid du Colombier 			}
3433ff48bf5SDavid du Colombier 			break;
3443ff48bf5SDavid du Colombier 		case 4:
3453ff48bf5SDavid du Colombier 			for(x=0; x<pdev->width; x++){
3463ff48bf5SDavid du Colombier 				if((x%2) == 0)
3473ff48bf5SDavid du Colombier 					p[x/2] = (*r>>4) & 0xF;
3483ff48bf5SDavid du Colombier 				else
3493ff48bf5SDavid du Colombier 					p[x/2] = (p[x/2]<<4) | ((*r>>4)&0xF);
3503ff48bf5SDavid du Colombier 				r += 3;
3513ff48bf5SDavid du Colombier 			}
3523ff48bf5SDavid du Colombier 			break;
3533ff48bf5SDavid du Colombier 		case 24:
3543ff48bf5SDavid du Colombier 			break;
3553ff48bf5SDavid du Colombier 		}
3563ff48bf5SDavid du Colombier 
3573ff48bf5SDavid du Colombier 		/* pad last byte over if we didn't fill it */
3583ff48bf5SDavid du Colombier 		xmod = pdev->width % ppb[ldepth];
3593ff48bf5SDavid du Colombier 		if(xmod && ldepth<3)
3603ff48bf5SDavid du Colombier 			p[(x-1)/ppb[ldepth]] <<= ((ppb[ldepth]-xmod)*bpp[ldepth]);
3613ff48bf5SDavid du Colombier 
3623ff48bf5SDavid du Colombier 		if(writeimageblock(w, p, bpl) == ERROR) {
363*593dc095SDavid du Colombier 			gs_free(pdev->memory, buf, gsbpl, 1, "plan9_print_page");
3643ff48bf5SDavid du Colombier 			return_error(gs_error_Fatal);
3653ff48bf5SDavid du Colombier 		}
3663ff48bf5SDavid du Colombier 	}
3673ff48bf5SDavid du Colombier 	if(writeimageblock(w, nil, 0) == ERROR) {
368*593dc095SDavid du Colombier 		gs_free(pdev->memory, buf, gsbpl, 1, "plan9_print_page");
3693ff48bf5SDavid du Colombier 		return_error(gs_error_Fatal);
3703ff48bf5SDavid du Colombier 	}
3713ff48bf5SDavid du Colombier 
372*593dc095SDavid du Colombier 	gs_free(pdev->memory, buf, gsbpl, 1, "plan9_print_page");
3733ff48bf5SDavid du Colombier 	return 0;
3743ff48bf5SDavid du Colombier }
3753ff48bf5SDavid du Colombier 
3763ff48bf5SDavid du Colombier /*
3773ff48bf5SDavid du Colombier  * this is a modified version of the image compressor
3783ff48bf5SDavid du Colombier  * from fb/bit2enc.  it is modified only in that it
3793ff48bf5SDavid du Colombier  * now compiles as part of gs.
3803ff48bf5SDavid du Colombier  */
3813ff48bf5SDavid du Colombier 
3823ff48bf5SDavid du Colombier /*
3833ff48bf5SDavid du Colombier  * Compressed image file parameters
3843ff48bf5SDavid du Colombier  */
3853ff48bf5SDavid du Colombier #define	NMATCH	3		/* shortest match possible */
3863ff48bf5SDavid du Colombier #define	NRUN	(NMATCH+31)	/* longest match possible */
3873ff48bf5SDavid du Colombier #define	NMEM	1024		/* window size */
3883ff48bf5SDavid du Colombier #define	NDUMP	128		/* maximum length of dump */
3893ff48bf5SDavid du Colombier #define	NCBLOCK	6000		/* size of compressed blocks */
3903ff48bf5SDavid du Colombier 
3913ff48bf5SDavid du Colombier #define	HSHIFT	3	/* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */
3923ff48bf5SDavid du Colombier #define	NHASH	(1<<(HSHIFT*NMATCH))
3933ff48bf5SDavid du Colombier #define	HMASK	(NHASH-1)
3943ff48bf5SDavid du Colombier #define	hupdate(h, c)	((((h)<<HSHIFT)^(c))&HMASK)
3953ff48bf5SDavid du Colombier 
3963ff48bf5SDavid du Colombier typedef struct Dump	Dump;
3973ff48bf5SDavid du Colombier typedef struct Hlist Hlist;
3983ff48bf5SDavid du Colombier 
3993ff48bf5SDavid du Colombier struct Hlist{
4003ff48bf5SDavid du Colombier 	ulong p;
4013ff48bf5SDavid du Colombier 	Hlist *next, *prev;
4023ff48bf5SDavid du Colombier };
4033ff48bf5SDavid du Colombier 
4043ff48bf5SDavid du Colombier struct Dump {
4053ff48bf5SDavid du Colombier 	int ndump;
4063ff48bf5SDavid du Colombier 	uchar *dumpbuf;
4073ff48bf5SDavid du Colombier 	uchar buf[1+NDUMP];
4083ff48bf5SDavid du Colombier };
4093ff48bf5SDavid du Colombier 
4103ff48bf5SDavid du Colombier struct WImage {
4113ff48bf5SDavid du Colombier 	FILE *f;
4123ff48bf5SDavid du Colombier 
4133ff48bf5SDavid du Colombier 	/* image attributes */
4143ff48bf5SDavid du Colombier 	Rectangle origr, r;
4153ff48bf5SDavid du Colombier 	int bpl;
4163ff48bf5SDavid du Colombier 
4173ff48bf5SDavid du Colombier 	/* output buffer */
4183ff48bf5SDavid du Colombier 	uchar outbuf[NCBLOCK], *outp, *eout, *loutp;
4193ff48bf5SDavid du Colombier 
4203ff48bf5SDavid du Colombier 	/* sliding input window */
4213ff48bf5SDavid du Colombier 	/*
4223ff48bf5SDavid du Colombier 	 * ibase is the pointer to where the beginning of
4233ff48bf5SDavid du Colombier 	 * the input "is" in memory.  whenever we "slide" the
4243ff48bf5SDavid du Colombier 	 * buffer N bytes, what we are actually doing is
4253ff48bf5SDavid du Colombier 	 * decrementing ibase by N.
4263ff48bf5SDavid du Colombier 	 * the ulongs in the Hlist structures are just
4273ff48bf5SDavid du Colombier 	 * pointers relative to ibase.
4283ff48bf5SDavid du Colombier 	 */
4293ff48bf5SDavid du Colombier 	uchar *inbuf;	/* inbuf should be at least NMEM+NRUN+NMATCH long */
4303ff48bf5SDavid du Colombier 	uchar *ibase;
4313ff48bf5SDavid du Colombier 	int minbuf;	/* size of inbuf (malloc'ed bytes) */
4323ff48bf5SDavid du Colombier 	int ninbuf;	/* size of inbuf (filled bytes) */
4333ff48bf5SDavid du Colombier 	ulong line;	/* the beginning of the line we are currently encoding,
4343ff48bf5SDavid du Colombier 			 * relative to inbuf (NOT relative to ibase) */
4353ff48bf5SDavid du Colombier 
4363ff48bf5SDavid du Colombier 	/* raw dump buffer */
4373ff48bf5SDavid du Colombier 	Dump dump;
4383ff48bf5SDavid du Colombier 
4393ff48bf5SDavid du Colombier 	/* hash tables */
4403ff48bf5SDavid du Colombier 	Hlist hash[NHASH];
4413ff48bf5SDavid du Colombier 	Hlist chain[NMEM], *cp;
4423ff48bf5SDavid du Colombier 	int h;
4433ff48bf5SDavid du Colombier 	int needhash;
4443ff48bf5SDavid du Colombier };
4453ff48bf5SDavid du Colombier 
4463ff48bf5SDavid du Colombier private void
zerohash(WImage * w)4473ff48bf5SDavid du Colombier zerohash(WImage *w)
4483ff48bf5SDavid du Colombier {
4493ff48bf5SDavid du Colombier 	memset(w->hash, 0, sizeof(w->hash));
4503ff48bf5SDavid du Colombier 	memset(w->chain, 0, sizeof(w->chain));
4513ff48bf5SDavid du Colombier 	w->cp=w->chain;
4523ff48bf5SDavid du Colombier 	w->needhash = 1;
4533ff48bf5SDavid du Colombier }
4543ff48bf5SDavid du Colombier 
4553ff48bf5SDavid du Colombier private int
addbuf(WImage * w,uchar * buf,int nbuf)4563ff48bf5SDavid du Colombier addbuf(WImage *w, uchar *buf, int nbuf)
4573ff48bf5SDavid du Colombier {
4583ff48bf5SDavid du Colombier 	int n;
4593ff48bf5SDavid du Colombier 	if(buf == nil || w->outp+nbuf > w->eout) {
4603ff48bf5SDavid du Colombier 		if(w->loutp==w->outbuf){	/* can't really happen -- we checked line length above */
461*593dc095SDavid du Colombier 			errprintf("buffer too small for line\n");
4623ff48bf5SDavid du Colombier 			return ERROR;
4633ff48bf5SDavid du Colombier 		}
4643ff48bf5SDavid du Colombier 		n=w->loutp-w->outbuf;
4653ff48bf5SDavid du Colombier 		fprintf(w->f, "%11d %11d ", w->r.max.y, n);
4663ff48bf5SDavid du Colombier 		fwrite(w->outbuf, 1, n, w->f);
4673ff48bf5SDavid du Colombier 		w->r.min.y=w->r.max.y;
4683ff48bf5SDavid du Colombier 		w->outp=w->outbuf;
4693ff48bf5SDavid du Colombier 		w->loutp=w->outbuf;
4703ff48bf5SDavid du Colombier 		zerohash(w);
4713ff48bf5SDavid du Colombier 		return -1;
4723ff48bf5SDavid du Colombier 	}
4733ff48bf5SDavid du Colombier 
4743ff48bf5SDavid du Colombier 	memmove(w->outp, buf, nbuf);
4753ff48bf5SDavid du Colombier 	w->outp += nbuf;
4763ff48bf5SDavid du Colombier 	return nbuf;
4773ff48bf5SDavid du Colombier }
4783ff48bf5SDavid du Colombier 
4793ff48bf5SDavid du Colombier /* return 0 on success, -1 if buffer is full */
4803ff48bf5SDavid du Colombier private int
flushdump(WImage * w)4813ff48bf5SDavid du Colombier flushdump(WImage *w)
4823ff48bf5SDavid du Colombier {
4833ff48bf5SDavid du Colombier 	int n = w->dump.ndump;
4843ff48bf5SDavid du Colombier 
4853ff48bf5SDavid du Colombier 	if(n == 0)
4863ff48bf5SDavid du Colombier 		return 0;
4873ff48bf5SDavid du Colombier 
4883ff48bf5SDavid du Colombier 	w->dump.buf[0] = 0x80|(n-1);
4893ff48bf5SDavid du Colombier 	if((n=addbuf(w, w->dump.buf, n+1)) == ERROR)
4903ff48bf5SDavid du Colombier 		return ERROR;
4913ff48bf5SDavid du Colombier 	if(n < 0)
4923ff48bf5SDavid du Colombier 		return -1;
4933ff48bf5SDavid du Colombier 	w->dump.ndump = 0;
4943ff48bf5SDavid du Colombier 	return 0;
4953ff48bf5SDavid du Colombier }
4963ff48bf5SDavid du Colombier 
4973ff48bf5SDavid du Colombier private void
updatehash(WImage * w,uchar * p,uchar * ep)4983ff48bf5SDavid du Colombier updatehash(WImage *w, uchar *p, uchar *ep)
4993ff48bf5SDavid du Colombier {
5003ff48bf5SDavid du Colombier 	uchar *q;
5013ff48bf5SDavid du Colombier 	Hlist *cp;
5023ff48bf5SDavid du Colombier 	Hlist *hash;
5033ff48bf5SDavid du Colombier 	int h;
5043ff48bf5SDavid du Colombier 
5053ff48bf5SDavid du Colombier 	hash = w->hash;
5063ff48bf5SDavid du Colombier 	cp = w->cp;
5073ff48bf5SDavid du Colombier 	h = w->h;
5083ff48bf5SDavid du Colombier 	for(q=p; q<ep; q++) {
5093ff48bf5SDavid du Colombier 		if(cp->prev)
5103ff48bf5SDavid du Colombier 			cp->prev->next = cp->next;
5113ff48bf5SDavid du Colombier 		cp->next = hash[h].next;
5123ff48bf5SDavid du Colombier 		cp->prev = &hash[h];
5133ff48bf5SDavid du Colombier 		cp->prev->next = cp;
5143ff48bf5SDavid du Colombier 		if(cp->next)
5153ff48bf5SDavid du Colombier 			cp->next->prev = cp;
5163ff48bf5SDavid du Colombier 		cp->p = q - w->ibase;
5173ff48bf5SDavid du Colombier 		if(++cp == w->chain+NMEM)
5183ff48bf5SDavid du Colombier 			cp = w->chain;
5193ff48bf5SDavid du Colombier 		if(&q[NMATCH] < &w->inbuf[w->ninbuf])
5203ff48bf5SDavid du Colombier 			h = hupdate(h, q[NMATCH]);
5213ff48bf5SDavid du Colombier 	}
5223ff48bf5SDavid du Colombier 	w->cp = cp;
5233ff48bf5SDavid du Colombier 	w->h = h;
5243ff48bf5SDavid du Colombier }
5253ff48bf5SDavid du Colombier 
5263ff48bf5SDavid du Colombier /*
5273ff48bf5SDavid du Colombier  * attempt to process a line of input,
5283ff48bf5SDavid du Colombier  * returning the number of bytes actually processed.
5293ff48bf5SDavid du Colombier  *
5303ff48bf5SDavid du Colombier  * if the output buffer needs to be flushed, we flush
5313ff48bf5SDavid du Colombier  * the buffer and return 0.
5323ff48bf5SDavid du Colombier  * otherwise we return bpl
5333ff48bf5SDavid du Colombier  */
5343ff48bf5SDavid du Colombier private int
gobbleline(WImage * w)5353ff48bf5SDavid du Colombier gobbleline(WImage *w)
5363ff48bf5SDavid du Colombier {
5373ff48bf5SDavid du Colombier 	int runlen, n, offs;
5383ff48bf5SDavid du Colombier 	uchar *eline, *es, *best, *p, *s, *t;
5393ff48bf5SDavid du Colombier 	Hlist *hp;
5403ff48bf5SDavid du Colombier 	uchar buf[2];
5413ff48bf5SDavid du Colombier 	int rv;
5423ff48bf5SDavid du Colombier 
5433ff48bf5SDavid du Colombier 	if(w->needhash) {
5443ff48bf5SDavid du Colombier 		w->h = 0;
5453ff48bf5SDavid du Colombier 		for(n=0; n!=NMATCH; n++)
5463ff48bf5SDavid du Colombier 			w->h = hupdate(w->h, w->inbuf[w->line+n]);
5473ff48bf5SDavid du Colombier 		w->needhash = 0;
5483ff48bf5SDavid du Colombier 	}
5493ff48bf5SDavid du Colombier 	w->dump.ndump=0;
5503ff48bf5SDavid du Colombier 	eline=w->inbuf+w->line+w->bpl;
5513ff48bf5SDavid du Colombier 	for(p=w->inbuf+w->line;p!=eline;){
5523ff48bf5SDavid du Colombier 		es = (eline < p+NRUN) ? eline : p+NRUN;
5533ff48bf5SDavid du Colombier 
5543ff48bf5SDavid du Colombier 		best=nil;
5553ff48bf5SDavid du Colombier 		runlen=0;
5563ff48bf5SDavid du Colombier 		/* hash table lookup */
5573ff48bf5SDavid du Colombier 		for(hp=w->hash[w->h].next;hp;hp=hp->next){
5583ff48bf5SDavid du Colombier 			/*
5593ff48bf5SDavid du Colombier 			 * the next block is an optimization of
5603ff48bf5SDavid du Colombier 			 * for(s=p, t=w->ibase+hp->p; s<es && *s == *t; s++, t++)
5613ff48bf5SDavid du Colombier 			 * 	;
5623ff48bf5SDavid du Colombier 			 */
5633ff48bf5SDavid du Colombier 
5643ff48bf5SDavid du Colombier 			{	uchar *ss, *tt;
5653ff48bf5SDavid du Colombier 				s = p+runlen;
5663ff48bf5SDavid du Colombier 				t = w->ibase+hp->p+runlen;
5673ff48bf5SDavid du Colombier 				for(ss=s, tt=t; ss>=p && *ss == *tt; ss--, tt--)
5683ff48bf5SDavid du Colombier 					;
5693ff48bf5SDavid du Colombier 				if(ss < p)
5703ff48bf5SDavid du Colombier 					while(s<es && *s == *t)
5713ff48bf5SDavid du Colombier 						s++, t++;
5723ff48bf5SDavid du Colombier 			}
5733ff48bf5SDavid du Colombier 
5743ff48bf5SDavid du Colombier 			n = s-p;
5753ff48bf5SDavid du Colombier 
5763ff48bf5SDavid du Colombier 			if(n > runlen) {
5773ff48bf5SDavid du Colombier 				runlen = n;
5783ff48bf5SDavid du Colombier 				best = w->ibase+hp->p;
5793ff48bf5SDavid du Colombier 				if(p+runlen == es)
5803ff48bf5SDavid du Colombier 					break;
5813ff48bf5SDavid du Colombier 			}
5823ff48bf5SDavid du Colombier 		}
5833ff48bf5SDavid du Colombier 
5843ff48bf5SDavid du Colombier 		/*
5853ff48bf5SDavid du Colombier 		 * if we didn't find a long enough run, append to
5863ff48bf5SDavid du Colombier 		 * the raw dump buffer
5873ff48bf5SDavid du Colombier 		 */
5883ff48bf5SDavid du Colombier 		if(runlen<NMATCH){
5893ff48bf5SDavid du Colombier 			if(w->dump.ndump==NDUMP) {
5903ff48bf5SDavid du Colombier 				if((rv = flushdump(w)) == ERROR)
5913ff48bf5SDavid du Colombier 					return ERROR;
5923ff48bf5SDavid du Colombier 				if(rv < 0)
5933ff48bf5SDavid du Colombier 					return 0;
5943ff48bf5SDavid du Colombier 			}
5953ff48bf5SDavid du Colombier 			w->dump.dumpbuf[w->dump.ndump++]=*p;
5963ff48bf5SDavid du Colombier 			runlen=1;
5973ff48bf5SDavid du Colombier 		}else{
5983ff48bf5SDavid du Colombier 		/*
5993ff48bf5SDavid du Colombier 		 * otherwise, assuming the dump buffer is empty,
6003ff48bf5SDavid du Colombier 		 * add the compressed rep.
6013ff48bf5SDavid du Colombier 		 */
6023ff48bf5SDavid du Colombier 			if((rv = flushdump(w)) == ERROR)
6033ff48bf5SDavid du Colombier 				return ERROR;
6043ff48bf5SDavid du Colombier 			if(rv < 0)
6053ff48bf5SDavid du Colombier 				return 0;
6063ff48bf5SDavid du Colombier 			offs=p-best-1;
6073ff48bf5SDavid du Colombier 			buf[0] = ((runlen-NMATCH)<<2)|(offs>>8);
6083ff48bf5SDavid du Colombier 			buf[1] = offs&0xff;
6093ff48bf5SDavid du Colombier 			if(addbuf(w, buf, 2) < 0)
6103ff48bf5SDavid du Colombier 				return 0;
6113ff48bf5SDavid du Colombier 		}
6123ff48bf5SDavid du Colombier 
6133ff48bf5SDavid du Colombier 		/*
6143ff48bf5SDavid du Colombier 		 * add to hash tables what we just encoded
6153ff48bf5SDavid du Colombier 		 */
6163ff48bf5SDavid du Colombier 		updatehash(w, p, p+runlen);
6173ff48bf5SDavid du Colombier 		p += runlen;
6183ff48bf5SDavid du Colombier 	}
6193ff48bf5SDavid du Colombier 
6203ff48bf5SDavid du Colombier 	if((rv = flushdump(w)) == ERROR)
6213ff48bf5SDavid du Colombier 		return ERROR;
6223ff48bf5SDavid du Colombier 	if(rv < 0)
6233ff48bf5SDavid du Colombier 		return 0;
6243ff48bf5SDavid du Colombier 	w->line += w->bpl;
6253ff48bf5SDavid du Colombier 	w->loutp=w->outp;
6263ff48bf5SDavid du Colombier 	w->r.max.y++;
6273ff48bf5SDavid du Colombier 	return w->bpl;
6283ff48bf5SDavid du Colombier }
6293ff48bf5SDavid du Colombier 
6303ff48bf5SDavid du Colombier private uchar*
shiftwindow(WImage * w,uchar * data,uchar * edata)6313ff48bf5SDavid du Colombier shiftwindow(WImage *w, uchar *data, uchar *edata)
6323ff48bf5SDavid du Colombier {
6333ff48bf5SDavid du Colombier 	int n, m;
6343ff48bf5SDavid du Colombier 
6353ff48bf5SDavid du Colombier 	/* shift window over */
6363ff48bf5SDavid du Colombier 	if(w->line > NMEM) {
6373ff48bf5SDavid du Colombier 		n = w->line-NMEM;
6383ff48bf5SDavid du Colombier 		memmove(w->inbuf, w->inbuf+n, w->ninbuf-n);
6393ff48bf5SDavid du Colombier 		w->line -= n;
6403ff48bf5SDavid du Colombier 		w->ibase -= n;
6413ff48bf5SDavid du Colombier 		w->ninbuf -= n;
6423ff48bf5SDavid du Colombier 	}
6433ff48bf5SDavid du Colombier 
6443ff48bf5SDavid du Colombier 	/* fill right with data if available */
6453ff48bf5SDavid du Colombier 	if(w->minbuf > w->ninbuf && edata > data) {
6463ff48bf5SDavid du Colombier 		m = w->minbuf - w->ninbuf;
6473ff48bf5SDavid du Colombier 		if(edata-data < m)
6483ff48bf5SDavid du Colombier 			m = edata-data;
6493ff48bf5SDavid du Colombier 		memmove(w->inbuf+w->ninbuf, data, m);
6503ff48bf5SDavid du Colombier 		data += m;
6513ff48bf5SDavid du Colombier 		w->ninbuf += m;
6523ff48bf5SDavid du Colombier 	}
6533ff48bf5SDavid du Colombier 
6543ff48bf5SDavid du Colombier 	return data;
6553ff48bf5SDavid du Colombier }
6563ff48bf5SDavid du Colombier 
6573ff48bf5SDavid du Colombier private WImage*
initwriteimage(FILE * f,Rectangle r,char * chanstr,int depth)6583ff48bf5SDavid du Colombier initwriteimage(FILE *f, Rectangle r, char *chanstr, int depth)
6593ff48bf5SDavid du Colombier {
6603ff48bf5SDavid du Colombier 	WImage *w;
6613ff48bf5SDavid du Colombier 	int n, bpl;
6623ff48bf5SDavid du Colombier 
6633ff48bf5SDavid du Colombier 	bpl = bytesperline(r, depth);
6643ff48bf5SDavid du Colombier 	if(r.max.y <= r.min.y || r.max.x <= r.min.x || bpl <= 0) {
665*593dc095SDavid du Colombier 		errprintf("bad rectangle, ldepth");
6663ff48bf5SDavid du Colombier 		return nil;
6673ff48bf5SDavid du Colombier 	}
6683ff48bf5SDavid du Colombier 
6693ff48bf5SDavid du Colombier 	n = NMEM+NMATCH+NRUN+bpl*2;
6703ff48bf5SDavid du Colombier 	w = malloc(n+sizeof(*w));
6713ff48bf5SDavid du Colombier 	if(w == nil)
6723ff48bf5SDavid du Colombier 		return nil;
6733ff48bf5SDavid du Colombier 	w->inbuf = (uchar*) &w[1];
6743ff48bf5SDavid du Colombier 	w->ibase = w->inbuf;
6753ff48bf5SDavid du Colombier 	w->line = 0;
6763ff48bf5SDavid du Colombier 	w->minbuf = n;
6773ff48bf5SDavid du Colombier 	w->ninbuf = 0;
6783ff48bf5SDavid du Colombier 	w->origr = r;
6793ff48bf5SDavid du Colombier 	w->r = r;
6803ff48bf5SDavid du Colombier 	w->r.max.y = w->r.min.y;
6813ff48bf5SDavid du Colombier 	w->eout = w->outbuf+sizeof(w->outbuf);
6823ff48bf5SDavid du Colombier 	w->outp = w->loutp = w->outbuf;
6833ff48bf5SDavid du Colombier 	w->bpl = bpl;
6843ff48bf5SDavid du Colombier 	w->f = f;
6853ff48bf5SDavid du Colombier 	w->dump.dumpbuf = w->dump.buf+1;
6863ff48bf5SDavid du Colombier 	w->dump.ndump = 0;
6873ff48bf5SDavid du Colombier 	zerohash(w);
6883ff48bf5SDavid du Colombier 
6893ff48bf5SDavid du Colombier 	fprintf(f, "compressed\n%11s %11d %11d %11d %11d ",
6903ff48bf5SDavid du Colombier 		chanstr, r.min.x, r.min.y, r.max.x, r.max.y);
6913ff48bf5SDavid du Colombier 	return w;
6923ff48bf5SDavid du Colombier }
6933ff48bf5SDavid du Colombier 
6943ff48bf5SDavid du Colombier private int
writeimageblock(WImage * w,uchar * data,int ndata)6953ff48bf5SDavid du Colombier writeimageblock(WImage *w, uchar *data, int ndata)
6963ff48bf5SDavid du Colombier {
6973ff48bf5SDavid du Colombier 	uchar *edata;
6983ff48bf5SDavid du Colombier 
6993ff48bf5SDavid du Colombier 	if(data == nil) {	/* end of data, flush everything */
7003ff48bf5SDavid du Colombier 		while(w->line < w->ninbuf)
7013ff48bf5SDavid du Colombier 			if(gobbleline(w) == ERROR)
7023ff48bf5SDavid du Colombier 				return ERROR;
7033ff48bf5SDavid du Colombier 		addbuf(w, nil, 0);
7043ff48bf5SDavid du Colombier 		if(w->r.min.y != w->origr.max.y) {
705*593dc095SDavid du Colombier 			errprintf("not enough data supplied to writeimage\n");
7063ff48bf5SDavid du Colombier 		}
7073ff48bf5SDavid du Colombier 		free(w);
7083ff48bf5SDavid du Colombier 		return 0;
7093ff48bf5SDavid du Colombier 	}
7103ff48bf5SDavid du Colombier 
7113ff48bf5SDavid du Colombier 	edata = data+ndata;
7123ff48bf5SDavid du Colombier 	data = shiftwindow(w, data, edata);
7133ff48bf5SDavid du Colombier 	while(w->ninbuf >= w->line+w->bpl+NMATCH) {
7143ff48bf5SDavid du Colombier 		if(gobbleline(w) == ERROR)
7153ff48bf5SDavid du Colombier 			return ERROR;
7163ff48bf5SDavid du Colombier 		data = shiftwindow(w, data, edata);
7173ff48bf5SDavid du Colombier 	}
7183ff48bf5SDavid du Colombier 	if(data != edata) {
7193ff48bf5SDavid du Colombier 		fprintf(w->f, "data != edata.  uh oh\n");
7203ff48bf5SDavid du Colombier 		return ERROR; /* can't happen */
7213ff48bf5SDavid du Colombier 	}
7223ff48bf5SDavid du Colombier 	return 0;
7233ff48bf5SDavid du Colombier }
7243ff48bf5SDavid du Colombier 
7253ff48bf5SDavid du Colombier /*
7263ff48bf5SDavid du Colombier  * functions from the Plan9/Brazil drawing libraries
7273ff48bf5SDavid du Colombier  */
7283ff48bf5SDavid du Colombier static
7293ff48bf5SDavid du Colombier int
unitsperline(Rectangle r,int d,int bitsperunit)7303ff48bf5SDavid du Colombier unitsperline(Rectangle r, int d, int bitsperunit)
7313ff48bf5SDavid du Colombier {
7323ff48bf5SDavid du Colombier 	ulong l, t;
7333ff48bf5SDavid du Colombier 
7343ff48bf5SDavid du Colombier 	if(d <= 0 || d > 32)	/* being called wrong.  d is image depth. */
7353ff48bf5SDavid du Colombier 		abort();
7363ff48bf5SDavid du Colombier 
7373ff48bf5SDavid du Colombier 	if(r.min.x >= 0){
7383ff48bf5SDavid du Colombier 		l = (r.max.x*d+bitsperunit-1)/bitsperunit;
7393ff48bf5SDavid du Colombier 		l -= (r.min.x*d)/bitsperunit;
7403ff48bf5SDavid du Colombier 	}else{			/* make positive before divide */
7413ff48bf5SDavid du Colombier 		t = (-r.min.x*d+bitsperunit-1)/bitsperunit;
7423ff48bf5SDavid du Colombier 		l = t+(r.max.x*d+bitsperunit-1)/bitsperunit;
7433ff48bf5SDavid du Colombier 	}
7443ff48bf5SDavid du Colombier 	return l;
7453ff48bf5SDavid du Colombier }
7463ff48bf5SDavid du Colombier 
7473ff48bf5SDavid du Colombier int
wordsperline(Rectangle r,int d)7483ff48bf5SDavid du Colombier wordsperline(Rectangle r, int d)
7493ff48bf5SDavid du Colombier {
7503ff48bf5SDavid du Colombier 	return unitsperline(r, d, 8*sizeof(ulong));
7513ff48bf5SDavid du Colombier }
7523ff48bf5SDavid du Colombier 
7533ff48bf5SDavid du Colombier int
bytesperline(Rectangle r,int d)7543ff48bf5SDavid du Colombier bytesperline(Rectangle r, int d)
7553ff48bf5SDavid du Colombier {
7563ff48bf5SDavid du Colombier 	return unitsperline(r, d, 8);
7573ff48bf5SDavid du Colombier }
758