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