xref: /plan9/sys/src/cmd/gs/src/gdevplan9.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /*
2  *		Copyright (c) 1998 by Lucent Technologies.
3  * Permission to use, copy, modify, and distribute this software for any
4  * purpose without fee is hereby granted, provided that this entire notice
5  * is included in all copies of any software which is or includes a copy
6  * or modification of this software.
7  *
8  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
9  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
10  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
11  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
12  */
13 
14 /*
15  * gdevplan9.c: gs device to generate plan9 bitmaps
16  * Russ Cox <rsc@plan9.bell-labs.com>, 3/25/98 (gdevifno)
17  * Updated to fit in the standard GS distribution, 5/14/98
18  * Added support for true-color bitmaps, 6/7/02
19  */
20 
21 #include "gdevprn.h"
22 #include "gsparam.h"
23 #include "gxlum.h"
24 #include "gxstdio.h"
25 #include <stdlib.h>
26 
27 #define nil ((void*)0)
28 enum {
29 	ERROR = -2
30 };
31 
32 typedef struct WImage WImage;
33 typedef struct Rectangle Rectangle;
34 typedef struct Point Point;
35 
36 struct Point {
37 	int x;
38 	int y;
39 };
40 
41 struct Rectangle {
42 	Point min;
43 	Point max;
44 };
45 private Point ZP = { 0, 0 };
46 
47 private WImage* initwriteimage(FILE *f, Rectangle r, char*, int depth);
48 private int writeimageblock(WImage *w, uchar *data, int ndata);
49 private int bytesperline(Rectangle, int);
50 private int rgb2cmap(int, int, int);
51 private long cmap2rgb(int);
52 
53 #define X_DPI	100
54 #define Y_DPI	100
55 
56 private dev_proc_map_rgb_color(plan9_rgb2cmap);
57 private dev_proc_map_color_rgb(plan9_cmap2rgb);
58 private dev_proc_open_device(plan9_open);
59 private dev_proc_close_device(plan9_close);
60 private dev_proc_print_page(plan9_print_page);
61 private dev_proc_put_params(plan9_put_params);
62 private dev_proc_get_params(plan9_get_params);
63 
64 typedef struct plan9_device_s {
65 	gx_device_common;
66 	gx_prn_device_common;
67 	int dither;
68 
69 	int ldepth;
70 	int lastldepth;
71 	int cmapcall;
72 } plan9_device;
73 
74 enum {
75 	Nbits = 8,
76 	Bitmask = (1<<Nbits)-1,
77 };
78 
79 private const gx_device_procs plan9_procs =
80 	prn_color_params_procs(plan9_open, gdev_prn_output_page, gdev_prn_close,
81 		plan9_rgb2cmap, plan9_cmap2rgb,
82 		gdev_prn_get_params, gdev_prn_put_params);
83 /*
84 		plan9_get_params, plan9_put_params);
85 */
86 
87 
88 plan9_device far_data gs_plan9_device =
89 { prn_device_body(plan9_device, plan9_procs, "plan9",
90 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
91 	X_DPI, Y_DPI,
92 	0,0,0,0,	/* margins */
93 	3,		/* 3 = RGB, 1 = gray, 4 = CMYK */
94 	Nbits*3,		/* # of bits per pixel */
95 	(1<<Nbits)-1,		/* # of distinct gray levels. */
96 	(1<<Nbits)-1,		/* # of distinct color levels. */
97 	1<<Nbits,		/* dither gray ramp size.  used in alpha? */
98 	1<<Nbits,    	/* dither color ramp size.  used in alpha? */
99 	plan9_print_page),
100 	1,
101 };
102 
103 /*
104  * ghostscript asks us how to convert between
105  * rgb and color map entries
106  */
107 private gx_color_index
plan9_rgb2cmap(gx_device * dev,gx_color_value * rgb)108 plan9_rgb2cmap(gx_device *dev, gx_color_value *rgb)
109 {
110 	gx_color_value r, g, b;
111 	int shift;
112 	plan9_device *idev;
113 	ulong red, green, blue;
114 
115 	r = rgb[0];
116 	g = rgb[1];
117 	b = rgb[2];
118 
119 	idev = (plan9_device*) dev;
120 
121 	shift = gx_color_value_bits - Nbits;
122 	red = r >> shift;
123 	green = g >> shift;
124 	blue = b >> shift;
125 
126 	/*
127 	 * we keep track of what ldepth bitmap this is by watching
128 	 * what colors gs asks for.
129 	 *
130 	 * one catch: sometimes print_page gets called more than one
131 	 * per page (for multiple copies) without cmap calls inbetween.
132 	 * if idev->cmapcall is 0 when print_page gets called, it uses
133 	 * the ldepth of the last page.
134 	 */
135 	if(red == green && green == blue) {
136 		if(red == 0 || red == Bitmask)
137 			;
138 		else if(red == Bitmask/3 || red == 2*Bitmask/3) {
139 			if(idev->ldepth < 1)
140 				idev->ldepth = 1;
141 		} else {
142 			if(idev->ldepth < 2)
143 				idev->ldepth = 2;
144 		}
145 	} else
146 		idev->ldepth = 3;
147 
148 	idev->cmapcall = 1;
149 	return (blue << (2*Nbits)) | (green << Nbits) | red;
150 }
151 
152 private int
plan9_cmap2rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])153 plan9_cmap2rgb(gx_device *dev, gx_color_index color,
154   gx_color_value rgb[3]) {
155 	int shift, i;
156 	plan9_device *idev;
157 
158 	if((ulong)color > 0xFFFFFF)
159 		return_error(gs_error_rangecheck);
160 
161 	idev = (plan9_device*) dev;
162 	shift = gx_color_value_bits - Nbits;
163 
164 	rgb[2] = ((color >> (2*Nbits)) & Bitmask) << shift;
165 	rgb[1] = ((color >> Nbits) & Bitmask) << shift;
166 	rgb[0] = (color & Bitmask) << shift;
167 
168 	return 0;
169 }
170 
171 private int
plan9_put_param_int(gs_param_list * plist,gs_param_name pname,int * pv,int minval,int maxval,int ecode)172 plan9_put_param_int(gs_param_list *plist, gs_param_name pname, int *pv,
173 	int minval, int maxval, int ecode)
174 {
175 	int code, value;
176 	switch(code = param_read_int(plist, pname, &value)) {
177 	default:
178 		return code;
179 
180 	case 1:
181 		return ecode;
182 
183 	case 0:
184 		if(value < minval || value > maxval)
185 			param_signal_error(plist, pname, gs_error_rangecheck);
186 		*pv = value;
187 		return (ecode < 0 ? ecode : 1);
188 	}
189 }
190 
191 private int
plan9_get_params(gx_device * pdev,gs_param_list * plist)192 plan9_get_params(gx_device *pdev, gs_param_list *plist)
193 {
194 	int code;
195 	plan9_device *idev;
196 
197 	idev = (plan9_device*) pdev;
198 //	printf("plan9_get_params dither %d\n", idev->dither);
199 
200 	if((code = gdev_prn_get_params(pdev, plist)) < 0
201 	 || (code = param_write_int(plist, "Dither", &idev->dither)) < 0)
202 		return code;
203 //	printf("getparams: dither=%d\n", idev->dither);
204 	return code;
205 }
206 
207 private int
plan9_put_params(gx_device * pdev,gs_param_list * plist)208 plan9_put_params(gx_device * pdev, gs_param_list * plist)
209 {
210 	int code;
211 	int dither;
212 	plan9_device *idev;
213 
214 //	printf("plan9_put_params\n");
215 
216 	idev = (plan9_device*)pdev;
217 	dither = idev->dither;
218 
219 	code = plan9_put_param_int(plist, "Dither", &dither, 0, 1, 0);
220 	if(code < 0)
221 		return code;
222 
223 	idev->dither = dither;
224 	return 0;
225 }
226 /*
227  * plan9_open() is supposed to initialize the device.
228  * there's not much to do.
229  */
230 extern void init_p9color(void);	/* in gdevifno.c */
231 private int
plan9_open(gx_device * dev)232 plan9_open(gx_device *dev)
233 {
234 	int code;
235 	plan9_device *idev;
236 
237 	idev = (plan9_device*) dev;
238 	idev->cmapcall = 0;
239 	idev->ldepth = 0;
240 
241 //	printf("plan9_open gs_plan9_device.dither = %d idev->dither = %d\n",
242 //		gs_plan9_device.dither, idev->dither);
243 	init_p9color();
244 
245 	return gdev_prn_open(dev);
246 }
247 
248 /*
249  * plan9_print_page() is called once for each page
250  * (actually once for each copy of each page, but we won't
251  * worry about that).
252  */
253 private int
plan9_print_page(gx_device_printer * pdev,FILE * f)254 plan9_print_page(gx_device_printer *pdev, FILE *f)
255 {
256 	char *chanstr;
257 	uchar *buf;	/* [8192*3*8/Nbits] BUG: malloc this */
258 	uchar *p;
259 	WImage *w;
260 	int bpl, y;
261 	int x, xmod;
262 	int ldepth;
263 	int ppb[] = {8, 4, 2, 1};	/* pixels per byte */
264 	int bpp[] = {1, 2, 4, 8};	/* bits per pixel */
265 	int gsbpl;
266 	int dither;
267 	int depth;
268 	ulong u;
269 	ushort us;
270 	Rectangle rect;
271 	plan9_device *idev;
272 	uchar *r;
273 
274 	gsbpl = gdev_prn_raster(pdev);
275 	buf = gs_malloc(pdev->memory, gsbpl, 1, "plan9_print_page");
276 
277 	if(buf == nil) {
278 		errprintf("out of memory\n");
279 		return_error(gs_error_Fatal);
280 	}
281 
282 	idev = (plan9_device *) pdev;
283 	if(idev->cmapcall) {
284 		idev->lastldepth = idev->ldepth;
285 		idev->ldepth = 0;
286 		idev->cmapcall = 0;
287 	}
288 	ldepth = idev->lastldepth;
289 	dither = idev->dither;
290 
291 	if(pdev->color_info.anti_alias.graphics_bits || pdev->color_info.anti_alias.text_bits)
292 		if(ldepth < 2)
293 			ldepth = 2;
294 
295 	chanstr = nil;
296 	depth = 0;
297 	switch(ldepth){
298 	case 0:
299 		chanstr = "k1";
300 		depth = 1;
301 		break;
302 	case 1:
303 		return_error(gs_error_Fatal);
304 	case 2:
305 		chanstr = "k4";
306 		depth = 4;
307 		break;
308 	case 3:
309 		chanstr = "r8g8b8";
310 		depth = 24;
311 		break;
312 	}
313 
314 //	printf("plan9_print_page dither %d ldepth %d idither %d\n", dither, ldepth, gs_plan9_device.dither);
315 	rect.min = ZP;
316 	rect.max.x = pdev->width;
317 	rect.max.y = pdev->height;
318 	bpl = bytesperline(rect, depth);
319 	w = initwriteimage(f, rect, chanstr, depth);
320 	if(w == nil) {
321 		errprintf("initwriteimage failed\n");
322 		return_error(gs_error_Fatal);
323 	}
324 
325 	/*
326 	 * i wonder if it is faster to put the switch around the for loops
327 	 * to save all the ldepth lookups.
328 	 */
329 	for(y=0; y<pdev->height; y++) {
330 		gdev_prn_get_bits(pdev, y, buf, &p);
331 		r = p+2;
332 		switch(depth){
333 		default:
334 			return_error(gs_error_Fatal);
335 		case 1:
336 			for(x=0; x<pdev->width; x++){
337 				if((x%8) == 0)
338 					p[x/8] = (*r>>4)&1;
339 				else
340 					p[x/8] = (p[x/8]<<1) | (*r>>4)&1;
341 				r += 3;
342 			}
343 			break;
344 		case 4:
345 			for(x=0; x<pdev->width; x++){
346 				if((x%2) == 0)
347 					p[x/2] = (*r>>4) & 0xF;
348 				else
349 					p[x/2] = (p[x/2]<<4) | ((*r>>4)&0xF);
350 				r += 3;
351 			}
352 			break;
353 		case 24:
354 			break;
355 		}
356 
357 		/* pad last byte over if we didn't fill it */
358 		xmod = pdev->width % ppb[ldepth];
359 		if(xmod && ldepth<3)
360 			p[(x-1)/ppb[ldepth]] <<= ((ppb[ldepth]-xmod)*bpp[ldepth]);
361 
362 		if(writeimageblock(w, p, bpl) == ERROR) {
363 			gs_free(pdev->memory, buf, gsbpl, 1, "plan9_print_page");
364 			return_error(gs_error_Fatal);
365 		}
366 	}
367 	if(writeimageblock(w, nil, 0) == ERROR) {
368 		gs_free(pdev->memory, buf, gsbpl, 1, "plan9_print_page");
369 		return_error(gs_error_Fatal);
370 	}
371 
372 	gs_free(pdev->memory, buf, gsbpl, 1, "plan9_print_page");
373 	return 0;
374 }
375 
376 /*
377  * this is a modified version of the image compressor
378  * from fb/bit2enc.  it is modified only in that it
379  * now compiles as part of gs.
380  */
381 
382 /*
383  * Compressed image file parameters
384  */
385 #define	NMATCH	3		/* shortest match possible */
386 #define	NRUN	(NMATCH+31)	/* longest match possible */
387 #define	NMEM	1024		/* window size */
388 #define	NDUMP	128		/* maximum length of dump */
389 #define	NCBLOCK	6000		/* size of compressed blocks */
390 
391 #define	HSHIFT	3	/* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */
392 #define	NHASH	(1<<(HSHIFT*NMATCH))
393 #define	HMASK	(NHASH-1)
394 #define	hupdate(h, c)	((((h)<<HSHIFT)^(c))&HMASK)
395 
396 typedef struct Dump	Dump;
397 typedef struct Hlist Hlist;
398 
399 struct Hlist{
400 	ulong p;
401 	Hlist *next, *prev;
402 };
403 
404 struct Dump {
405 	int ndump;
406 	uchar *dumpbuf;
407 	uchar buf[1+NDUMP];
408 };
409 
410 struct WImage {
411 	FILE *f;
412 
413 	/* image attributes */
414 	Rectangle origr, r;
415 	int bpl;
416 
417 	/* output buffer */
418 	uchar outbuf[NCBLOCK], *outp, *eout, *loutp;
419 
420 	/* sliding input window */
421 	/*
422 	 * ibase is the pointer to where the beginning of
423 	 * the input "is" in memory.  whenever we "slide" the
424 	 * buffer N bytes, what we are actually doing is
425 	 * decrementing ibase by N.
426 	 * the ulongs in the Hlist structures are just
427 	 * pointers relative to ibase.
428 	 */
429 	uchar *inbuf;	/* inbuf should be at least NMEM+NRUN+NMATCH long */
430 	uchar *ibase;
431 	int minbuf;	/* size of inbuf (malloc'ed bytes) */
432 	int ninbuf;	/* size of inbuf (filled bytes) */
433 	ulong line;	/* the beginning of the line we are currently encoding,
434 			 * relative to inbuf (NOT relative to ibase) */
435 
436 	/* raw dump buffer */
437 	Dump dump;
438 
439 	/* hash tables */
440 	Hlist hash[NHASH];
441 	Hlist chain[NMEM], *cp;
442 	int h;
443 	int needhash;
444 };
445 
446 private void
zerohash(WImage * w)447 zerohash(WImage *w)
448 {
449 	memset(w->hash, 0, sizeof(w->hash));
450 	memset(w->chain, 0, sizeof(w->chain));
451 	w->cp=w->chain;
452 	w->needhash = 1;
453 }
454 
455 private int
addbuf(WImage * w,uchar * buf,int nbuf)456 addbuf(WImage *w, uchar *buf, int nbuf)
457 {
458 	int n;
459 	if(buf == nil || w->outp+nbuf > w->eout) {
460 		if(w->loutp==w->outbuf){	/* can't really happen -- we checked line length above */
461 			errprintf("buffer too small for line\n");
462 			return ERROR;
463 		}
464 		n=w->loutp-w->outbuf;
465 		fprintf(w->f, "%11d %11d ", w->r.max.y, n);
466 		fwrite(w->outbuf, 1, n, w->f);
467 		w->r.min.y=w->r.max.y;
468 		w->outp=w->outbuf;
469 		w->loutp=w->outbuf;
470 		zerohash(w);
471 		return -1;
472 	}
473 
474 	memmove(w->outp, buf, nbuf);
475 	w->outp += nbuf;
476 	return nbuf;
477 }
478 
479 /* return 0 on success, -1 if buffer is full */
480 private int
flushdump(WImage * w)481 flushdump(WImage *w)
482 {
483 	int n = w->dump.ndump;
484 
485 	if(n == 0)
486 		return 0;
487 
488 	w->dump.buf[0] = 0x80|(n-1);
489 	if((n=addbuf(w, w->dump.buf, n+1)) == ERROR)
490 		return ERROR;
491 	if(n < 0)
492 		return -1;
493 	w->dump.ndump = 0;
494 	return 0;
495 }
496 
497 private void
updatehash(WImage * w,uchar * p,uchar * ep)498 updatehash(WImage *w, uchar *p, uchar *ep)
499 {
500 	uchar *q;
501 	Hlist *cp;
502 	Hlist *hash;
503 	int h;
504 
505 	hash = w->hash;
506 	cp = w->cp;
507 	h = w->h;
508 	for(q=p; q<ep; q++) {
509 		if(cp->prev)
510 			cp->prev->next = cp->next;
511 		cp->next = hash[h].next;
512 		cp->prev = &hash[h];
513 		cp->prev->next = cp;
514 		if(cp->next)
515 			cp->next->prev = cp;
516 		cp->p = q - w->ibase;
517 		if(++cp == w->chain+NMEM)
518 			cp = w->chain;
519 		if(&q[NMATCH] < &w->inbuf[w->ninbuf])
520 			h = hupdate(h, q[NMATCH]);
521 	}
522 	w->cp = cp;
523 	w->h = h;
524 }
525 
526 /*
527  * attempt to process a line of input,
528  * returning the number of bytes actually processed.
529  *
530  * if the output buffer needs to be flushed, we flush
531  * the buffer and return 0.
532  * otherwise we return bpl
533  */
534 private int
gobbleline(WImage * w)535 gobbleline(WImage *w)
536 {
537 	int runlen, n, offs;
538 	uchar *eline, *es, *best, *p, *s, *t;
539 	Hlist *hp;
540 	uchar buf[2];
541 	int rv;
542 
543 	if(w->needhash) {
544 		w->h = 0;
545 		for(n=0; n!=NMATCH; n++)
546 			w->h = hupdate(w->h, w->inbuf[w->line+n]);
547 		w->needhash = 0;
548 	}
549 	w->dump.ndump=0;
550 	eline=w->inbuf+w->line+w->bpl;
551 	for(p=w->inbuf+w->line;p!=eline;){
552 		es = (eline < p+NRUN) ? eline : p+NRUN;
553 
554 		best=nil;
555 		runlen=0;
556 		/* hash table lookup */
557 		for(hp=w->hash[w->h].next;hp;hp=hp->next){
558 			/*
559 			 * the next block is an optimization of
560 			 * for(s=p, t=w->ibase+hp->p; s<es && *s == *t; s++, t++)
561 			 * 	;
562 			 */
563 
564 			{	uchar *ss, *tt;
565 				s = p+runlen;
566 				t = w->ibase+hp->p+runlen;
567 				for(ss=s, tt=t; ss>=p && *ss == *tt; ss--, tt--)
568 					;
569 				if(ss < p)
570 					while(s<es && *s == *t)
571 						s++, t++;
572 			}
573 
574 			n = s-p;
575 
576 			if(n > runlen) {
577 				runlen = n;
578 				best = w->ibase+hp->p;
579 				if(p+runlen == es)
580 					break;
581 			}
582 		}
583 
584 		/*
585 		 * if we didn't find a long enough run, append to
586 		 * the raw dump buffer
587 		 */
588 		if(runlen<NMATCH){
589 			if(w->dump.ndump==NDUMP) {
590 				if((rv = flushdump(w)) == ERROR)
591 					return ERROR;
592 				if(rv < 0)
593 					return 0;
594 			}
595 			w->dump.dumpbuf[w->dump.ndump++]=*p;
596 			runlen=1;
597 		}else{
598 		/*
599 		 * otherwise, assuming the dump buffer is empty,
600 		 * add the compressed rep.
601 		 */
602 			if((rv = flushdump(w)) == ERROR)
603 				return ERROR;
604 			if(rv < 0)
605 				return 0;
606 			offs=p-best-1;
607 			buf[0] = ((runlen-NMATCH)<<2)|(offs>>8);
608 			buf[1] = offs&0xff;
609 			if(addbuf(w, buf, 2) < 0)
610 				return 0;
611 		}
612 
613 		/*
614 		 * add to hash tables what we just encoded
615 		 */
616 		updatehash(w, p, p+runlen);
617 		p += runlen;
618 	}
619 
620 	if((rv = flushdump(w)) == ERROR)
621 		return ERROR;
622 	if(rv < 0)
623 		return 0;
624 	w->line += w->bpl;
625 	w->loutp=w->outp;
626 	w->r.max.y++;
627 	return w->bpl;
628 }
629 
630 private uchar*
shiftwindow(WImage * w,uchar * data,uchar * edata)631 shiftwindow(WImage *w, uchar *data, uchar *edata)
632 {
633 	int n, m;
634 
635 	/* shift window over */
636 	if(w->line > NMEM) {
637 		n = w->line-NMEM;
638 		memmove(w->inbuf, w->inbuf+n, w->ninbuf-n);
639 		w->line -= n;
640 		w->ibase -= n;
641 		w->ninbuf -= n;
642 	}
643 
644 	/* fill right with data if available */
645 	if(w->minbuf > w->ninbuf && edata > data) {
646 		m = w->minbuf - w->ninbuf;
647 		if(edata-data < m)
648 			m = edata-data;
649 		memmove(w->inbuf+w->ninbuf, data, m);
650 		data += m;
651 		w->ninbuf += m;
652 	}
653 
654 	return data;
655 }
656 
657 private WImage*
initwriteimage(FILE * f,Rectangle r,char * chanstr,int depth)658 initwriteimage(FILE *f, Rectangle r, char *chanstr, int depth)
659 {
660 	WImage *w;
661 	int n, bpl;
662 
663 	bpl = bytesperline(r, depth);
664 	if(r.max.y <= r.min.y || r.max.x <= r.min.x || bpl <= 0) {
665 		errprintf("bad rectangle, ldepth");
666 		return nil;
667 	}
668 
669 	n = NMEM+NMATCH+NRUN+bpl*2;
670 	w = malloc(n+sizeof(*w));
671 	if(w == nil)
672 		return nil;
673 	w->inbuf = (uchar*) &w[1];
674 	w->ibase = w->inbuf;
675 	w->line = 0;
676 	w->minbuf = n;
677 	w->ninbuf = 0;
678 	w->origr = r;
679 	w->r = r;
680 	w->r.max.y = w->r.min.y;
681 	w->eout = w->outbuf+sizeof(w->outbuf);
682 	w->outp = w->loutp = w->outbuf;
683 	w->bpl = bpl;
684 	w->f = f;
685 	w->dump.dumpbuf = w->dump.buf+1;
686 	w->dump.ndump = 0;
687 	zerohash(w);
688 
689 	fprintf(f, "compressed\n%11s %11d %11d %11d %11d ",
690 		chanstr, r.min.x, r.min.y, r.max.x, r.max.y);
691 	return w;
692 }
693 
694 private int
writeimageblock(WImage * w,uchar * data,int ndata)695 writeimageblock(WImage *w, uchar *data, int ndata)
696 {
697 	uchar *edata;
698 
699 	if(data == nil) {	/* end of data, flush everything */
700 		while(w->line < w->ninbuf)
701 			if(gobbleline(w) == ERROR)
702 				return ERROR;
703 		addbuf(w, nil, 0);
704 		if(w->r.min.y != w->origr.max.y) {
705 			errprintf("not enough data supplied to writeimage\n");
706 		}
707 		free(w);
708 		return 0;
709 	}
710 
711 	edata = data+ndata;
712 	data = shiftwindow(w, data, edata);
713 	while(w->ninbuf >= w->line+w->bpl+NMATCH) {
714 		if(gobbleline(w) == ERROR)
715 			return ERROR;
716 		data = shiftwindow(w, data, edata);
717 	}
718 	if(data != edata) {
719 		fprintf(w->f, "data != edata.  uh oh\n");
720 		return ERROR; /* can't happen */
721 	}
722 	return 0;
723 }
724 
725 /*
726  * functions from the Plan9/Brazil drawing libraries
727  */
728 static
729 int
unitsperline(Rectangle r,int d,int bitsperunit)730 unitsperline(Rectangle r, int d, int bitsperunit)
731 {
732 	ulong l, t;
733 
734 	if(d <= 0 || d > 32)	/* being called wrong.  d is image depth. */
735 		abort();
736 
737 	if(r.min.x >= 0){
738 		l = (r.max.x*d+bitsperunit-1)/bitsperunit;
739 		l -= (r.min.x*d)/bitsperunit;
740 	}else{			/* make positive before divide */
741 		t = (-r.min.x*d+bitsperunit-1)/bitsperunit;
742 		l = t+(r.max.x*d+bitsperunit-1)/bitsperunit;
743 	}
744 	return l;
745 }
746 
747 int
wordsperline(Rectangle r,int d)748 wordsperline(Rectangle r, int d)
749 {
750 	return unitsperline(r, d, 8*sizeof(ulong));
751 }
752 
753 int
bytesperline(Rectangle r,int d)754 bytesperline(Rectangle r, int d)
755 {
756 	return unitsperline(r, d, 8);
757 }
758