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