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 831