8 #define RGB2K(r,g,b) ((299*((ulong)(r))+587*((ulong)(g))+114*((ulong)(b)))/1000)
11 * This program tests the 'memimagedraw' primitive stochastically.
12 * It tests the combination aspects of it thoroughly, but since the
13 * three images it uses are disjoint, it makes no check of the
14 * correct behavior when images overlap. That is, however, much
15 * easier to get right and to test.
18 void drawonepixel(Memimage*, Point, Memimage*, Point, Memimage*, Point);
20 void verifyline(void);
21 void verifyrect(void);
22 void verifyrectrepl(int, int);
23 void putpixel(Memimage *img, Point pt, ulong nv);
24 ulong rgbatopix(uchar, uchar, uchar, uchar);
26 char *dchan, *schan, *mchan;
31 int dbpp; /* bits per pixel in destination */
32 int sbpp; /* bits per pixel in src */
33 int mbpp; /* bits per pixel in mask */
34 int dpm; /* pixel mask at high part of byte, in destination */
35 int nbytes; /* in destination */
52 main(int argc, char *argv[])
59 Xrange = atoi(ARGF());
62 Yrange = atoi(ARGF());
65 niters = atoi(ARGF());
76 case 3: mchan = argv[2];
77 case 2: schan = argv[1];
78 case 1: dchan = argv[0];
82 fprint(2, "usage: dtest [dchan [schan [mchan]]]\n");
86 // fmtinstall('b', numbconv); /* binary! */
88 fprint(2, "%s -x %d -y %d -s 0x%x %s %s %s\n", argv0, Xrange, Yrange, seed, dchan, schan, mchan);
91 dst = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(dchan));
92 src = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan));
93 mask = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan));
94 stmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan));
95 mtmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan));
96 ones = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan));
97 // print("chan %lux %lux %lux %lux %lux %lux\n", dst->chan, src->chan, mask->chan, stmp->chan, mtmp->chan, ones->chan);
98 if(dst==0 || src==0 || mask==0 || mtmp==0 || ones==0) {
100 fprint(2, "dtest: allocation failed: %r\n");
103 nbytes = (4*Xrange+4)*Yrange;
104 srcbits = malloc(nbytes);
105 dstbits = malloc(nbytes);
106 maskbits = malloc(nbytes);
107 savedstbits = malloc(nbytes);
108 if(dstbits==0 || srcbits==0 || maskbits==0 || savedstbits==0)
113 dpm = 0xFF ^ (0xFF>>dbpp);
114 memset(ones->data->bdata, 0xFF, ones->width*sizeof(ulong)*Yrange);
117 fprint(2, "dtest: verify single pixel operation\n");
120 fprint(2, "dtest: verify full line non-replicated\n");
123 fprint(2, "dtest: verify full rectangle non-replicated\n");
126 fprint(2, "dtest: verify full rectangle source replicated\n");
127 verifyrectrepl(1, 0);
129 fprint(2, "dtest: verify full rectangle mask replicated\n");
130 verifyrectrepl(0, 1);
132 fprint(2, "dtest: verify full rectangle source and mask replicated\n");
133 verifyrectrepl(1, 1);
139 * Dump out an ASCII representation of an image. The label specifies
140 * a list of characters to put at various points in the picture.
143 Bprintr5g6b5(Biobuf *bio, char*, ulong v)
149 Bprint(bio, "%.2x%.2x%.2x", r,g,b);
153 Bprintr5g5b5a1(Biobuf *bio, char*, ulong v)
160 Bprint(bio, "%.2x%.2x%.2x%.2x", r,g,b,a);
164 dumpimage(char *name, Memimage *img, void *vdata, Point labelpt)
170 void (*fmt)(Biobuf*, char*, ulong);
171 int npr, x, y, nb, bpp;
181 fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
185 fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
190 if(img->chan == RGB16)
193 fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
198 fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
202 fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
207 fprint(2, "bad format\n");
212 Binit(&b, 2, OWRITE);
215 Bprint(&b, "%s\t%d\tr %R clipr %R repl %d data %p *%P\n", name, r.min.x, r, img->clipr, (img->flags&Frepl) ? 1 : 0, vdata, labelpt);
216 mask = (1ULL<<bpp)-1;
217 // for(y=r.min.y; y<r.max.y; y++){
218 for(y=0; y<Yrange; y++){
221 p = data+(byteaddr(img, Pt(0,y))-(uchar*)img->data->bdata);
222 Bprint(&b, "%-4d\t", y);
223 // for(x=r.min.x; x<r.max.x; x++){
224 for(x=0; x<Xrange; x++){
228 if(x != 0 && (x%8)==0)
232 if(x==labelpt.x && y==labelpt.y){
241 v |= (ulong)(*p++) << nb;
245 // print("bpp %d v %.8lux mask %.8lux nb %d\n", bpp, v, mask, nb);
246 fmt(&b, arg, (v>>nb)&mask);
254 * Verify that the destination pixel has the specified value.
255 * The value is in the high bits of v, suitably masked, but must
256 * be extracted from the destination Memimage.
259 checkone(Point p, Point sp, Point mp)
264 delta = (uchar*)byteaddr(dst, p)-(uchar*)dst->data->bdata;
265 dp = (uchar*)dst->data->bdata+delta;
266 sdp = (uchar*)savedstbits+delta;
268 if(memcmp(dp, sdp, (dst->depth+7)/8) != 0) {
269 fprint(2, "dtest: one bad pixel drawing at dst %P from source %P mask %P\n", p, sp, mp);
270 fprint(2, " %.2ux %.2ux %.2ux %.2ux should be %.2ux %.2ux %.2ux %.2ux\n",
271 dp[0], dp[1], dp[2], dp[3], sdp[0], sdp[1], sdp[2], sdp[3]);
272 fprint(2, "addresses dst %p src %p mask %p\n", dp, byteaddr(src, sp), byteaddr(mask, mp));
273 dumpimage("src", src, src->data->bdata, sp);
274 dumpimage("mask", mask, mask->data->bdata, mp);
275 dumpimage("origdst", dst, dstbits, p);
276 dumpimage("dst", dst, dst->data->bdata, p);
277 dumpimage("gooddst", dst, savedstbits, p);
283 * Verify that the destination line has the same value as the saved line.
285 #define RECTPTS(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y
287 checkline(Rectangle r, Point sp, Point mp, int y, Memimage *stmp, Memimage *mtmp)
293 dp = wordaddr(dst, Pt(0, y));
294 saved = savedstbits + y*dst->width;
296 nb = Xrange/(8/dst->depth);
298 nb = Xrange*(dst->depth/8);
299 if(memcmp(dp, saved, nb) != 0){
300 fprint(2, "dtest: bad line at y=%d; saved %p dp %p\n", y, saved, dp);
301 fprint(2, "draw dst %R src %P mask %P\n", r, sp, mp);
302 dumpimage("src", src, src->data->bdata, sp);
303 if(stmp) dumpimage("stmp", stmp, stmp->data->bdata, sp);
304 dumpimage("mask", mask, mask->data->bdata, mp);
305 if(mtmp) dumpimage("mtmp", mtmp, mtmp->data->bdata, mp);
306 dumpimage("origdst", dst, dstbits, r.min);
307 dumpimage("dst", dst, dst->data->bdata, r.min);
308 dumpimage("gooddst", dst, savedstbits, r.min);
314 * Fill the bits of an image with random data.
315 * The Memimage parameter is used only to make sure
316 * the data is well formatted: only ucbits is written.
319 fill(Memimage *img, uchar *ucbits)
323 uchar alpha, r, g, b;
326 if((img->flags&Falpha) == 0){
327 up = (ushort*)ucbits;
328 for(i=0; i<nbytes/2; i++)
329 *up++ = lrand() >> 7;
331 *(uchar*)up = lrand() >> 7;
333 data = img->data->bdata;
334 img->data->bdata = ucbits;
336 for(x=img->r.min.x; x<img->r.max.x; x++)
337 for(y=img->r.min.y; y<img->r.max.y; y++){
339 r = rand()%(alpha+1);
340 g = rand()%(alpha+1);
341 b = rand()%(alpha+1);
342 putpixel(img, Pt(x,y), rgbatopix(r,g,b,alpha));
344 img->data->bdata = data;
350 * Mask is preset; do the rest
359 memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
360 memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange);
361 memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
363 dp.x = nrand(Xrange);
364 dp.y = nrand(Yrange);
366 sp.x = nrand(Xrange);
367 sp.y = nrand(Yrange);
369 mp.x = nrand(Xrange);
370 mp.y = nrand(Yrange);
372 drawonepixel(dst, dp, src, sp, mask, mp);
373 memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
374 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange);
376 memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
377 memimagedraw(dst, Rect(dp.x, dp.y, dp.x+1, dp.y+1), src, sp, mask, mp, SoverD);
378 memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
380 checkone(dp, sp, mp);
389 memset(maskbits, 0, nbytes);
390 for(i=0; i<niters; i++)
394 memset(maskbits, 0xFF, nbytes);
395 for(i=0; i<niters; i++)
399 for(i=0; i<niters; i++){
400 fill(mask, maskbits);
406 * Mask is preset; do the rest
411 Point sp, mp, tp, up;
417 memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
418 memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange);
419 memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
421 dr.min.x = nrand(Xrange-1);
422 dr.min.y = nrand(Yrange-1);
423 dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x);
424 dr.max.y = dr.min.y + 1;
426 sp.x = nrand(Xrange);
427 sp.y = nrand(Yrange);
429 mp.x = nrand(Xrange);
430 mp.y = nrand(Yrange);
434 for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
435 memimagedraw(dst, Rect(x, dr.min.y, x+1, dr.min.y+1), src, tp, mask, up, SoverD);
436 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange);
438 memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
440 memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
441 checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), dr.min.y, nil, nil);
450 memset(maskbits, 0xFF, nbytes);
451 for(i=0; i<niters; i++)
455 memset(maskbits, 0, nbytes);
456 for(i=0; i<niters; i++)
460 for(i=0; i<niters; i++){
461 fill(mask, maskbits);
467 * Mask is preset; do the rest
472 Point sp, mp, tp, up;
478 memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
479 memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange);
480 memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
482 dr.min.x = nrand(Xrange-1);
483 dr.min.y = nrand(Yrange-1);
484 dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x);
485 dr.max.y = dr.min.y + 1 + nrand(Yrange-1-dr.min.y);
487 sp.x = nrand(Xrange);
488 sp.y = nrand(Yrange);
490 mp.x = nrand(Xrange);
491 mp.y = nrand(Yrange);
495 for(y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++){
496 for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
497 memimagedraw(dst, Rect(x, y, x+1, y+1), src, tp, mask, up, SoverD);
501 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange);
503 memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
505 memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
506 for(y=0; y<Yrange; y++)
507 checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, nil, nil);
516 memset(maskbits, 0, nbytes);
517 for(i=0; i<niters; i++)
521 memset(maskbits, 0xFF, nbytes);
522 for(i=0; i<niters; i++)
526 for(i=0; i<niters; i++){
527 fill(mask, maskbits);
537 r.min.x = nrand(Xrange-1);
538 r.min.y = nrand(Yrange-1);
539 r.max.x = r.min.x + 1 + nrand(Xrange-1-r.min.x);
540 r.max.y = r.min.y + 1 + nrand(Yrange-1-r.min.y);
545 * Return coordinate corresponding to x withing range [minx, maxx)
548 tilexy(int minx, int maxx, int x)
552 sx = (x-minx) % (maxx-minx);
559 replicate(Memimage *i, Memimage *tmp)
564 /* choose the replication window (i->r) */
565 r.min.x = nrand(Xrange-1);
566 r.min.y = nrand(Yrange-1);
567 /* make it trivial more often than pure chance allows */
570 r.max.x = r.min.x + 2;
571 r.max.y = r.min.y + 2;
572 if(r.max.x < Xrange && r.max.y < Yrange)
576 r.max.x = r.min.x + 1;
577 r.max.y = r.min.y + 1;
580 if(r.min.x+3 >= Xrange)
583 r.max.x = r.min.x+3 + nrand(Xrange-(r.min.x+3));
585 if(r.min.y+3 >= Yrange)
588 r.max.y = r.min.y+3 + nrand(Yrange-(r.min.y+3));
590 assert(r.min.x >= 0);
591 assert(r.max.x <= Xrange);
592 assert(r.min.y >= 0);
593 assert(r.max.y <= Yrange);
594 /* copy from i to tmp so we have just the replicated bits */
595 nb = tmp->width*sizeof(ulong)*Yrange;
596 memset(tmp->data->bdata, 0, nb);
597 memimagedraw(tmp, r, i, r.min, ones, r.min, SoverD);
598 memmove(i->data->bdata, tmp->data->bdata, nb);
599 /* i is now a non-replicated instance of the replication */
600 /* replicate it by hand through tmp */
601 memset(tmp->data->bdata, 0, nb);
602 x = -(tilexy(r.min.x, r.max.x, 0)-r.min.x);
603 for(; x<Xrange; x+=Dx(r)){
604 y = -(tilexy(r.min.y, r.max.y, 0)-r.min.y);
605 for(; y<Yrange; y+=Dy(r)){
606 /* set r1 to instance of tile by translation */
609 r1.max.x = r1.min.x+Dx(r);
610 r1.max.y = r1.min.y+Dy(r);
611 memimagedraw(tmp, r1, i, r.min, ones, r.min, SoverD);
616 i->clipr = randrect();
617 // fprint(2, "replicate [[%d %d] [%d %d]] [[%d %d][%d %d]]\n", r.min.x, r.min.y, r.max.x, r.max.y,
618 // i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
619 tmp->clipr = i->clipr;
623 * Mask is preset; do the rest
626 verifyrectmaskrepl(int srcrepl, int maskrepl)
628 Point sp, mp, tp, up;
633 // print("verfrect %d %d\n", srcrepl, maskrepl);
634 src->flags &= ~Frepl;
635 src->r = Rect(0, 0, Xrange, Yrange);
637 stmp->flags &= ~Frepl;
638 stmp->r = Rect(0, 0, Xrange, Yrange);
639 stmp->clipr = src->r;
640 mask->flags &= ~Frepl;
641 mask->r = Rect(0, 0, Xrange, Yrange);
642 mask->clipr = mask->r;
643 mtmp->flags &= ~Frepl;
644 mtmp->r = Rect(0, 0, Xrange, Yrange);
645 mtmp->clipr = mask->r;
650 memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
651 memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange);
652 memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
655 replicate(src, stmp);
660 replicate(mask, mtmp);
667 sp.x = nrand(Xrange);
668 sp.y = nrand(Yrange);
670 mp.x = nrand(Xrange);
671 mp.y = nrand(Yrange);
673 DBG print("smalldraws\n");
674 for(tp.y=sp.y,up.y=mp.y,y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++)
675 for(tp.x=sp.x,up.x=mp.x,x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
676 memimagedraw(dst, Rect(x, y, x+1, y+1), s, tp, m, up, SoverD);
677 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange);
679 memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
681 DBG print("bigdraw\n");
682 memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
683 for(y=0; y<Yrange; y++)
684 checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, srcrepl?stmp:nil, maskrepl?mtmp:nil);
688 verifyrectrepl(int srcrepl, int maskrepl)
693 memset(maskbits, 0xFF, nbytes);
694 for(i=0; i<niters; i++)
695 verifyrectmaskrepl(srcrepl, maskrepl);
698 memset(maskbits, 0, nbytes);
699 for(i=0; i<niters; i++)
700 verifyrectmaskrepl(srcrepl, maskrepl);
703 for(i=0; i<niters; i++){
704 fill(mask, maskbits);
705 verifyrectmaskrepl(srcrepl, maskrepl);
710 * Trivial draw implementation.
711 * Color values are passed around as ulongs containing ααRRGGBB
715 * Convert v, which is nhave bits wide, into its nwant bits wide equivalent.
716 * Replicates to widen the value, truncates to narrow it.
719 replbits(ulong v, int nhave, int nwant)
722 for(; nhave<nwant; nhave*=2)
725 return v & ((1<<nwant)-1);
729 * Decode a pixel into the uchar* values.
732 pixtorgba(ulong v, uchar *r, uchar *g, uchar *b, uchar *a)
741 * Convert uchar channels into ulong pixel.
744 rgbatopix(uchar r, uchar g, uchar b, uchar a)
746 return (a<<24)|(r<<16)|(g<<8)|b;
750 * Retrieve the pixel value at pt in the image.
753 getpixel(Memimage *img, Point pt)
755 uchar r, g, b, a, *p;
756 int nbits, npack, bpp;
757 ulong v, c, rbits, bits;
760 a = ~0; /* default alpha is full */
762 p = byteaddr(img, pt);
763 v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
767 * Sub-byte greyscale pixels.
769 * We want to throw away the top pt.x%npack pixels and then use the next bpp bits
770 * in the bottom byte of v. This madness is due to having big endian bits
771 * but little endian bytes.
774 v >>= 8 - bpp*(pt.x%npack+1);
776 r = g = b = replbits(v, bpp, 8);
779 * General case. We need to parse the channel descriptor and do what it says.
780 * In all channels but the color map, we replicate to 8 bits because that's the
781 * precision that all calculations are done at.
783 * In the case of the color map, we leave the bits alone, in case a color map
784 * with less than 8 bits of index is used. This is currently disallowed, so it's
788 for(c=img->chan; c; c>>=8){
790 bits = v & ((1<<nbits)-1);
791 rbits = replbits(bits, nbits, 8);
810 p = img->cmap->cmap2rgb + 3*bits;
818 fprint(2, "unknown channel type %lud\n", TYPE(c));
823 return rgbatopix(r, g, b, a);
827 * Return the greyscale equivalent of a pixel.
830 getgrey(Memimage *img, Point pt)
833 pixtorgba(getpixel(img, pt), &r, &g, &b, &a);
834 return RGB2K(r, g, b);
838 * Return the value at pt in image, if image is interpreted
839 * as a mask. This means the alpha channel if present, else
840 * the greyscale or its computed equivalent.
843 getmask(Memimage *img, Point pt)
845 if(img->flags&Falpha)
846 return getpixel(img, pt)>>24;
848 return getgrey(img, pt);
854 * Write a pixel to img at point pt.
856 * We do this by reading a 32-bit little endian
857 * value from p and then writing it back
858 * after tweaking the appropriate bits. Because
859 * the data is little endian, we don't have to worry
860 * about what the actual depth is, as long as it is
864 putpixel(Memimage *img, Point pt, ulong nv)
866 uchar r, g, b, a, *p, *q;
867 ulong c, mask, bits, v;
868 int bpp, sh, npack, nbits;
870 pixtorgba(nv, &r, &g, &b, &a);
872 p = byteaddr(img, pt);
873 v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
875 DBG print("v %.8lux...", v);
878 * Sub-byte greyscale pixels. We need to skip the leftmost pt.x%npack pixels,
879 * which is equivalent to skipping the rightmost npack - pt.x%npack - 1 pixels.
882 sh = bpp*(npack - pt.x%npack - 1);
884 DBG print("repl %lux 8 %d = %lux...", bits, bpp, replbits(bits, 8, bpp));
885 bits = replbits(bits, 8, bpp);
887 DBG print("bits %lux mask %lux sh %d...", bits, mask, sh);
890 DBG print("(%lux & %lux) | (%lux & %lux)", v, ~mask, bits, mask);
891 v = (v & ~mask) | (bits & mask);
894 * General case. We need to parse the channel descriptor again.
897 for(c=img->chan; c; c>>=8){
910 bits = RGB2K(r, g, b);
919 q = img->cmap->rgb2cmap;
920 bits = q[(r>>4)*16*16+(g>>4)*16+(b>>4)];
924 fprint(2, "unknown channel type %lud\n", TYPE(c));
928 DBG print("repl %lux 8 %d = %lux...", bits, nbits, replbits(bits, 8, nbits));
930 bits = replbits(bits, 8, nbits);
932 DBG print("bits %lux mask %lux sh %d...", bits, mask, sh);
935 v = (v & ~mask) | (bits & mask);
939 DBG print("v %.8lux\n", v);
949 drawonepixel(Memimage *dst, Point dp, Memimage *src, Point sp, Memimage *mask, Point mp)
951 uchar m, M, sr, sg, sb, sa, sk, dr, dg, db, da, dk;
953 pixtorgba(getpixel(dst, dp), &dr, &dg, &db, &da);
954 pixtorgba(getpixel(src, sp), &sr, &sg, &sb, &sa);
955 m = getmask(mask, mp);
956 M = 255-(sa*m + 127)/255;
958 DBG print("dst %x %x %x %x src %x %x %x %x m %x = ", dr,dg,db,da, sr,sg,sb,sa, m);
959 if(dst->flags&Fgrey){
961 * We need to do the conversion to grey before the alpha calculation
962 * because the draw operator does this, and we need to be operating
963 * at the same precision so we get exactly the same answers.
965 sk = RGB2K(sr, sg, sb);
966 dk = RGB2K(dr, dg, db);
967 dk = (sk*m + dk*M + 127)/255;
969 da = (sa*m + da*M + 127)/255;
972 * True color alpha calculation treats all channels (including alpha)
973 * the same. It might have been nice to use an array, but oh well.
975 dr = (sr*m + dr*M + 127)/255;
976 dg = (sg*m + dg*M + 127)/255;
977 db = (sb*m + db*M + 127)/255;
978 da = (sa*m + da*M + 127)/255;
981 DBG print("%x %x %x %x\n", dr,dg,db,da);
982 putpixel(dst, dp, rgbatopix(dr, dg, db, da));