Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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