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