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 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 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 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 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 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 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 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 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 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 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 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 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* 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* 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 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 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 748 wordsperline(Rectangle r, int d) 749 { 750 return unitsperline(r, d, 8*sizeof(ulong)); 751 } 752 753 int 754 bytesperline(Rectangle r, int d) 755 { 756 return unitsperline(r, d, 8); 757 } 758