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