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
inferno_rgb2cmap(gx_device * dev,gx_color_value rgb[3])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
inferno_cmap2rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])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
inferno_put_param_int(gs_param_list * plist,gs_param_name pname,int * pv,int minval,int maxval,int ecode)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
inferno_get_params(gx_device * pdev,gs_param_list * plist)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
inferno_put_params(gx_device * pdev,gs_param_list * plist)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
init_p9color(void)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
inferno_open(gx_device * dev)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
inferno_print_page(gx_device_printer * pdev,FILE * f)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
zerohash(WImage * w)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
addbuf(WImage * w,uchar * buf,int nbuf)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
flushdump(WImage * w)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
updatehash(WImage * w,uchar * p,uchar * ep)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
gobbleline(WImage * w)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*
shiftwindow(WImage * w,uchar * data,uchar * edata)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*
initwriteimage(FILE * f,Rectangle r,int ldepth)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
writeimageblock(WImage * w,uchar * data,int ndata)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
bytesperline(Rectangle r,int ld)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
rgb2cmap(int cr,int cg,int cb)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