draw.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
draw.c (56885B)
---
1 #include "u.h"
2 #include "lib.h"
3 #include "draw.h"
4 #include "memdraw.h"
5
6 int drawdebug;
7 static int tablesbuilt;
8
9 /* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */
10 #define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19)
11
12 /*
13 * For 16-bit values, x / 255 == (t = x+1, (t+(t>>8)) >> 8).
14 * We add another 127 to round to the nearest value rather
15 * than truncate.
16 *
17 * CALCxy does x bytewise calculations on y input images (x=1,4; y=1,2).
18 * CALC2x does two parallel 16-bit calculations on y input images (y=1,2).
19 */
20 #define CALC11(a, v, tmp) \
21 (tmp=(a)*(v)+128, (tmp+(tmp>>8))>>8)
22 #define CALC12(a1, v1, a2, v2, tmp) \
23 (tmp=(a1)*(v1)+(a2)*(v2)+128, (tmp+(tmp>>8))>>8)
24 #define MASK 0xFF00FF
25
26 #define CALC21(a, vvuu, tmp) \
27 (tmp=(a)*(vvuu)+0x00800080, ((tmp+((tmp>>8)&MASK))>>8)&MASK)
28
29 #define CALC41(a, rgba, tmp1, tmp2) \
30 (CALC21(a, rgba & MASK, tmp1) | \
31 (CALC21(a, (rgba>>8)&MASK, tmp2)<<8))
32
33 #define CALC22(a1, vvuu1, a2, vvuu2, tmp) \
34 (tmp=(a1)*(vvuu1)+(a2)*(vvuu2)+0x00800080, ((tmp+((tmp>>8)&MASK))>>8)&MASK)
35
36 #define CALC42(a1, rgba1, a2, rgba2, tmp1, tmp2) \
37 (CALC22(a1, rgba1 & MASK, a2, rgba2 & MASK, tmp1) | \
38 (CALC22(a1, (rgba1>>8) & MASK, a2, (rgba2>>8) & MASK, tmp2)<<8))
39
40 static void mktables(void);
41 typedef int Subdraw(Memdrawparam*);
42 static Subdraw chardraw, alphadraw, memoptdraw;
43
44 static Memimage* memones;
45 static Memimage* memzeros;
46 Memimage *memwhite;
47 Memimage *memblack;
48 Memimage *memtransparent;
49 Memimage *memopaque;
50
51 int __ifmt(Fmt*);
52
53 void
54 _memimageinit(void)
55 {
56 static int didinit = 0;
57
58 if(didinit)
59 return;
60
61 didinit = 1;
62
63
64 mktables();
65 _memmkcmap();
66
67 fmtinstall('R', Rfmt);
68 fmtinstall('P', Pfmt);
69 fmtinstall('b', __ifmt);
70
71 memones = allocmemimage(Rect(0,0,1,1), GREY1);
72 memones->flags |= Frepl;
73 memones->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
74 *byteaddr(memones, ZP) = ~0;
75
76 memzeros = allocmemimage(Rect(0,0,1,1), GREY1);
77 memzeros->flags |= Frepl;
78 memzeros->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
79 *byteaddr(memzeros, ZP) = 0;
80
81 if(memones == nil || memzeros == nil)
82 assert(0 /*cannot initialize memimage library */); /* RSC BUG */
83
84 memwhite = memones;
85 memblack = memzeros;
86 memopaque = memones;
87 memtransparent = memzeros;
88 }
89
90
91 #define DBG if(0)
92 Memdrawparam*
93 _memimagedrawsetup(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op)
94 {
95 static Memdrawparam par;
96
97 if(mask == nil)
98 mask = memopaque;
99
100 DBG print("memimagedraw %p/%luX %R @ %p %p/%luX %P %p/%luX %P... ", dst, dst->chan, r, dst->data->bdata, src, src->chan, p0, mask, mask->chan, p1);
101
102 if(drawclip(dst, &r, src, &p0, mask, &p1, &par.sr, &par.mr) == 0){
103 // if(drawdebug)
104 // iprint("empty clipped rectangle\n");
105 return nil;
106 }
107
108 if(op < Clear || op > SoverD){
109 // if(drawdebug)
110 // iprint("op out of range: %d\n", op);
111 return nil;
112 }
113
114 par.op = op;
115 par.dst = dst;
116 par.r = r;
117 par.src = src;
118 /* par.sr set by drawclip */
119 par.mask = mask;
120 /* par.mr set by drawclip */
121
122 par.state = 0;
123 if(src->flags&Frepl){
124 par.state |= Replsrc;
125 if(Dx(src->r)==1 && Dy(src->r)==1){
126 par.sval = pixelbits(src, src->r.min);
127 par.state |= Simplesrc;
128 par.srgba = _imgtorgba(src, par.sval);
129 par.sdval = _rgbatoimg(dst, par.srgba);
130 if((par.srgba&0xFF) == 0xFF)
131 par.state |= Fullsrc;
132 if((par.srgba&0xFF) == 0 && (op&DoutS)){
133 // if (drawdebug) iprint("fill with transparent source\n");
134 return nil; /* no-op successfully handled */
135 }
136 }
137 }
138
139 if(mask->flags & Frepl){
140 par.state |= Replmask;
141 if(Dx(mask->r)==1 && Dy(mask->r)==1){
142 par.mval = pixelbits(mask, mask->r.min);
143 if(par.mval == 0 && (op&DoutS)){
144 // if(drawdebug) iprint("fill with zero mask\n");
145 return nil; /* no-op successfully handled */
146 }
147 par.state |= Simplemask;
148 if(par.mval == ~0)
149 par.state |= Fullmask;
150 par.mrgba = _imgtorgba(mask, par.mval);
151 }
152 }
153
154 // if(drawdebug)
155 // iprint("dr %R sr %R mr %R...", r, par.sr, par.mr);
156 DBG print("draw dr %R sr %R mr %R %lux\n", r, par.sr, par.mr, par.state);
157
158 return ∥
159 }
160
161 void
162 _memimagedraw(Memdrawparam *par)
163 {
164 if(par == nil)
165 return;
166
167 /*
168 * Now that we've clipped the parameters down to be consistent, we
169 * simply try sub-drawing routines in order until we find one that was able
170 * to handle us. If the sub-drawing routine returns zero, it means it was
171 * unable to satisfy the request, so we do not return.
172 */
173
174 /*
175 * Hardware support. Each video driver provides this function,
176 * which checks to see if there is anything it can help with.
177 * There could be an if around this checking to see if dst is in video memory.
178 */
179 DBG print("test hwdraw\n");
180 if(hwdraw(par)){
181 //if(drawdebug) iprint("hw handled\n");
182 DBG print("hwdraw handled\n");
183 return;
184 }
185 /*
186 * Optimizations using memmove and memset.
187 */
188 DBG print("test memoptdraw\n");
189 if(memoptdraw(par)){
190 //if(drawdebug) iprint("memopt handled\n");
191 DBG print("memopt handled\n");
192 return;
193 }
194
195 /*
196 * Character drawing.
197 * Solid source color being painted through a boolean mask onto a high res image.
198 */
199 DBG print("test chardraw\n");
200 if(chardraw(par)){
201 //if(drawdebug) iprint("chardraw handled\n");
202 DBG print("chardraw handled\n");
203 return;
204 }
205
206 /*
207 * General calculation-laden case that does alpha for each pixel.
208 */
209 DBG print("do alphadraw\n");
210 alphadraw(par);
211 //if(drawdebug) iprint("alphadraw handled\n");
212 DBG print("alphadraw handled\n");
213 }
214 #undef DBG
215
216 /*
217 * Clip the destination rectangle further based on the properties of the
218 * source and mask rectangles. Once the destination rectangle is properly
219 * clipped, adjust the source and mask rectangles to be the same size.
220 * Then if source or mask is replicated, move its clipped rectangle
221 * so that its minimum point falls within the repl rectangle.
222 *
223 * Return zero if the final rectangle is null.
224 */
225 int
226 drawclip(Memimage *dst, Rectangle *r, Memimage *src, Point *p0, Memimage *mask, Point *p1, Rectangle *sr, Rectangle *mr)
227 {
228 Point rmin, delta;
229 int splitcoords;
230 Rectangle omr;
231
232 if(r->min.x>=r->max.x || r->min.y>=r->max.y)
233 return 0;
234 splitcoords = (p0->x!=p1->x) || (p0->y!=p1->y);
235 /* clip to destination */
236 rmin = r->min;
237 if(!rectclip(r, dst->r) || !rectclip(r, dst->clipr))
238 return 0;
239 /* move mask point */
240 p1->x += r->min.x-rmin.x;
241 p1->y += r->min.y-rmin.y;
242 /* move source point */
243 p0->x += r->min.x-rmin.x;
244 p0->y += r->min.y-rmin.y;
245 /* map destination rectangle into source */
246 sr->min = *p0;
247 sr->max.x = p0->x+Dx(*r);
248 sr->max.y = p0->y+Dy(*r);
249 /* sr is r in source coordinates; clip to source */
250 if(!(src->flags&Frepl) && !rectclip(sr, src->r))
251 return 0;
252 if(!rectclip(sr, src->clipr))
253 return 0;
254 /* compute and clip rectangle in mask */
255 if(splitcoords){
256 /* move mask point with source */
257 p1->x += sr->min.x-p0->x;
258 p1->y += sr->min.y-p0->y;
259 mr->min = *p1;
260 mr->max.x = p1->x+Dx(*sr);
261 mr->max.y = p1->y+Dy(*sr);
262 omr = *mr;
263 /* mr is now rectangle in mask; clip it */
264 if(!(mask->flags&Frepl) && !rectclip(mr, mask->r))
265 return 0;
266 if(!rectclip(mr, mask->clipr))
267 return 0;
268 /* reflect any clips back to source */
269 sr->min.x += mr->min.x-omr.min.x;
270 sr->min.y += mr->min.y-omr.min.y;
271 sr->max.x += mr->max.x-omr.max.x;
272 sr->max.y += mr->max.y-omr.max.y;
273 *p1 = mr->min;
274 }else{
275 if(!(mask->flags&Frepl) && !rectclip(sr, mask->r))
276 return 0;
277 if(!rectclip(sr, mask->clipr))
278 return 0;
279 *p1 = sr->min;
280 }
281
282 /* move source clipping back to destination */
283 delta.x = r->min.x - p0->x;
284 delta.y = r->min.y - p0->y;
285 r->min.x = sr->min.x + delta.x;
286 r->min.y = sr->min.y + delta.y;
287 r->max.x = sr->max.x + delta.x;
288 r->max.y = sr->max.y + delta.y;
289
290 /* move source rectangle so sr->min is in src->r */
291 if(src->flags&Frepl) {
292 delta.x = drawreplxy(src->r.min.x, src->r.max.x, sr->min.x) - sr->min.x;
293 delta.y = drawreplxy(src->r.min.y, src->r.max.y, sr->min.y) - sr->min.y;
294 sr->min.x += delta.x;
295 sr->min.y += delta.y;
296 sr->max.x += delta.x;
297 sr->max.y += delta.y;
298 }
299 *p0 = sr->min;
300
301 /* move mask point so it is in mask->r */
302 *p1 = drawrepl(mask->r, *p1);
303 mr->min = *p1;
304 mr->max.x = p1->x+Dx(*sr);
305 mr->max.y = p1->y+Dy(*sr);
306
307 assert(Dx(*sr) == Dx(*mr) && Dx(*mr) == Dx(*r));
308 assert(Dy(*sr) == Dy(*mr) && Dy(*mr) == Dy(*r));
309 assert(ptinrect(*p0, src->r));
310 assert(ptinrect(*p1, mask->r));
311 assert(ptinrect(r->min, dst->r));
312
313 return 1;
314 }
315
316 /*
317 * Conversion tables.
318 */
319 static uchar replbit[1+8][256]; /* replbit[x][y] is the replication of the x-bit quantity y to 8-bit depth */
320 static uchar conv18[256][8]; /* conv18[x][y] is the yth pixel in the depth-1 pixel x */
321 static uchar conv28[256][4]; /* ... */
322 static uchar conv48[256][2];
323
324 /*
325 * bitmap of how to replicate n bits to fill 8, for 1 ≤ n ≤ 8.
326 * the X's are where to put the bottom (ones) bit of the n-bit pattern.
327 * only the top 8 bits of the result are actually used.
328 * (the lower 8 bits are needed to get bits in the right place
329 * when n is not a divisor of 8.)
330 *
331 * Should check to see if its easier to just refer to replmul than
332 * use the precomputed values in replbit. On PCs it may well
333 * be; on machines with slow multiply instructions it probably isn't.
334 */
335 #define a ((((((((((((((((0
336 #define X *2+1)
337 #define _ *2)
338 static int replmul[1+8] = {
339 0,
340 a X X X X X X X X X X X X X X X X,
341 a _ X _ X _ X _ X _ X _ X _ X _ X,
342 a _ _ X _ _ X _ _ X _ _ X _ _ X _,
343 a _ _ _ X _ _ _ X _ _ _ X _ _ _ X,
344 a _ _ _ _ X _ _ _ _ X _ _ _ _ X _,
345 a _ _ _ _ _ X _ _ _ _ _ X _ _ _ _,
346 a _ _ _ _ _ _ X _ _ _ _ _ _ X _ _,
347 a _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X,
348 };
349 #undef a
350 #undef X
351 #undef _
352
353 static void
354 mktables(void)
355 {
356 int i, j, mask, sh, small;
357
358 if(tablesbuilt)
359 return;
360
361 fmtinstall('R', Rfmt);
362 fmtinstall('P', Pfmt);
363 tablesbuilt = 1;
364
365 /* bit replication up to 8 bits */
366 for(i=0; i<256; i++){
367 for(j=0; j<=8; j++){ /* j <= 8 [sic] */
368 small = i & ((1<<j)-1);
369 replbit[j][i] = (small*replmul[j])>>8;
370 }
371 }
372
373 /* bit unpacking up to 8 bits, only powers of 2 */
374 for(i=0; i<256; i++){
375 for(j=0, sh=7, mask=1; j<8; j++, sh--)
376 conv18[i][j] = replbit[1][(i>>sh)&mask];
377
378 for(j=0, sh=6, mask=3; j<4; j++, sh-=2)
379 conv28[i][j] = replbit[2][(i>>sh)&mask];
380
381 for(j=0, sh=4, mask=15; j<2; j++, sh-=4)
382 conv48[i][j] = replbit[4][(i>>sh)&mask];
383 }
384 }
385
386 static uchar ones = 0xff;
387
388 /*
389 * General alpha drawing case. Can handle anything.
390 */
391 typedef struct Buffer Buffer;
392 struct Buffer {
393 /* used by most routines */
394 uchar *red;
395 uchar *grn;
396 uchar *blu;
397 uchar *alpha;
398 uchar *grey;
399 uint32 *rgba;
400 int delta; /* number of bytes to add to pointer to get next pixel to the right */
401
402 /* used by boolcalc* for mask data */
403 uchar *m; /* ptr to mask data r.min byte; like p->bytermin */
404 int mskip; /* no. of left bits to skip in *m */
405 uchar *bm; /* ptr to mask data img->r.min byte; like p->bytey0s */
406 int bmskip; /* no. of left bits to skip in *bm */
407 uchar *em; /* ptr to mask data img->r.max.x byte; like p->bytey0e */
408 int emskip; /* no. of right bits to skip in *em */
409 };
410
411 typedef struct Param Param;
412 typedef Buffer Readfn(Param*, uchar*, int);
413 typedef void Writefn(Param*, uchar*, Buffer);
414 typedef Buffer Calcfn(Buffer, Buffer, Buffer, int, int, int);
415
416 enum {
417 MAXBCACHE = 16
418 };
419
420 /* giant rathole to customize functions with */
421 struct Param {
422 Readfn *replcall;
423 Readfn *greymaskcall;
424 Readfn *convreadcall;
425 Writefn *convwritecall;
426
427 Memimage *img;
428 Rectangle r;
429 int dx; /* of r */
430 int needbuf;
431 int convgrey;
432 int alphaonly;
433
434 uchar *bytey0s; /* byteaddr(Pt(img->r.min.x, img->r.min.y)) */
435 uchar *bytermin; /* byteaddr(Pt(r.min.x, img->r.min.y)) */
436 uchar *bytey0e; /* byteaddr(Pt(img->r.max.x, img->r.min.y)) */
437 int bwidth;
438
439 int replcache; /* if set, cache buffers */
440 Buffer bcache[MAXBCACHE];
441 uint32 bfilled;
442 uchar *bufbase;
443 int bufoff;
444 int bufdelta;
445
446 int dir;
447
448 int convbufoff;
449 uchar *convbuf;
450 Param *convdpar;
451 int convdx;
452 };
453
454 static Readfn greymaskread, replread, readptr;
455 static Writefn nullwrite;
456 static Calcfn alphacalc0, alphacalc14, alphacalc2810, alphacalc3679, alphacalc5, alphacalc11, alphacalcS;
457 static Calcfn boolcalc14, boolcalc236789, boolcalc1011;
458
459 static Readfn* readfn(Memimage*);
460 static Readfn* readalphafn(Memimage*);
461 static Writefn* writefn(Memimage*);
462
463 static Calcfn* boolcopyfn(Memimage*, Memimage*);
464 static Readfn* convfn(Memimage*, Param*, Memimage*, Param*, int*);
465
466 static Calcfn *alphacalc[Ncomp] =
467 {
468 alphacalc0, /* Clear */
469 alphacalc14, /* DoutS */
470 alphacalc2810, /* SoutD */
471 alphacalc3679, /* DxorS */
472 alphacalc14, /* DinS */
473 alphacalc5, /* D */
474 alphacalc3679, /* DatopS */
475 alphacalc3679, /* DoverS */
476 alphacalc2810, /* SinD */
477 alphacalc3679, /* SatopD */
478 alphacalc2810, /* S */
479 alphacalc11, /* SoverD */
480 };
481
482 static Calcfn *boolcalc[Ncomp] =
483 {
484 alphacalc0, /* Clear */
485 boolcalc14, /* DoutS */
486 boolcalc236789, /* SoutD */
487 boolcalc236789, /* DxorS */
488 boolcalc14, /* DinS */
489 alphacalc5, /* D */
490 boolcalc236789, /* DatopS */
491 boolcalc236789, /* DoverS */
492 boolcalc236789, /* SinD */
493 boolcalc236789, /* SatopD */
494 boolcalc1011, /* S */
495 boolcalc1011, /* SoverD */
496 };
497
498 /*
499 * Avoid standard Lock, QLock so that can be used in kernel.
500 */
501 typedef struct Dbuf Dbuf;
502 struct Dbuf
503 {
504 uchar *p;
505 int n;
506 Param spar, mpar, dpar;
507 int inuse;
508 };
509 static Dbuf dbuf[10];
510 extern int _tas(int*);
511
512 static Dbuf*
513 allocdbuf(void)
514 {
515 int i;
516
517 for(i=0; i<nelem(dbuf); i++){
518 if(dbuf[i].inuse)
519 continue;
520 if(!_tas(&dbuf[i].inuse))
521 return &dbuf[i];
522 }
523 return nil;
524 }
525
526 static void
527 getparam(Param *p, Memimage *img, Rectangle r, int convgrey, int needbuf, int *ndrawbuf)
528 {
529 int nbuf;
530
531 memset(p, 0, sizeof *p);
532
533 p->img = img;
534 p->r = r;
535 p->dx = Dx(r);
536 p->needbuf = needbuf;
537 p->convgrey = convgrey;
538
539 assert(img->r.min.x <= r.min.x && r.min.x < img->r.max.x);
540
541 p->bytey0s = byteaddr(img, Pt(img->r.min.x, img->r.min.y));
542 p->bytermin = byteaddr(img, Pt(r.min.x, img->r.min.y));
543 p->bytey0e = byteaddr(img, Pt(img->r.max.x, img->r.min.y));
544 p->bwidth = sizeof(uint32)*img->width;
545
546 assert(p->bytey0s <= p->bytermin && p->bytermin <= p->bytey0e);
547
548 if(p->r.min.x == p->img->r.min.x)
549 assert(p->bytermin == p->bytey0s);
550
551 nbuf = 1;
552 if((img->flags&Frepl) && Dy(img->r) <= MAXBCACHE && Dy(img->r) < Dy(r)){
553 p->replcache = 1;
554 nbuf = Dy(img->r);
555 }
556 p->bufdelta = 4*p->dx;
557 p->bufoff = *ndrawbuf;
558 *ndrawbuf += p->bufdelta*nbuf;
559 }
560
561 static void
562 clipy(Memimage *img, int *y)
563 {
564 int dy;
565
566 dy = Dy(img->r);
567 if(*y == dy)
568 *y = 0;
569 else if(*y == -1)
570 *y = dy-1;
571 assert(0 <= *y && *y < dy);
572 }
573
574 static void
575 dumpbuf(char *s, Buffer b, int n)
576 {
577 int i;
578 uchar *p;
579
580 print("%s", s);
581 for(i=0; i<n; i++){
582 print(" ");
583 if((p=b.grey)){
584 print(" k%.2uX", *p);
585 b.grey += b.delta;
586 }else{
587 if((p=b.red)){
588 print(" r%.2uX", *p);
589 b.red += b.delta;
590 }
591 if((p=b.grn)){
592 print(" g%.2uX", *p);
593 b.grn += b.delta;
594 }
595 if((p=b.blu)){
596 print(" b%.2uX", *p);
597 b.blu += b.delta;
598 }
599 }
600 if((p=b.alpha) != &ones){
601 print(" α%.2uX", *p);
602 b.alpha += b.delta;
603 }
604 }
605 print("\n");
606 }
607
608 /*
609 * For each scan line, we expand the pixels from source, mask, and destination
610 * into byte-aligned red, green, blue, alpha, and grey channels. If buffering is not
611 * needed and the channels were already byte-aligned (grey8, rgb24, rgba32, rgb32),
612 * the readers need not copy the data: they can simply return pointers to the data.
613 * If the destination image is grey and the source is not, it is converted using the NTSC
614 * formula.
615 *
616 * Once we have all the channels, we call either rgbcalc or greycalc, depending on
617 * whether the destination image is color. This is allowed to overwrite the dst buffer (perhaps
618 * the actual data, perhaps a copy) with its result. It should only overwrite the dst buffer
619 * with the same format (i.e. red bytes with red bytes, etc.) A new buffer is returned from
620 * the calculator, and that buffer is passed to a function to write it to the destination.
621 * If the buffer is already pointing at the destination, the writing function is a no-op.
622 */
623 #define DBG if(0)
624 static int
625 alphadraw(Memdrawparam *par)
626 {
627 int isgrey, starty, endy, op;
628 int needbuf, dsty, srcy, masky;
629 int y, dir, dx, dy, ndrawbuf;
630 uchar *drawbuf;
631 Buffer bsrc, bdst, bmask;
632 Readfn *rdsrc, *rdmask, *rddst;
633 Calcfn *calc;
634 Writefn *wrdst;
635 Memimage *src, *mask, *dst;
636 Rectangle r, sr, mr;
637 Dbuf *z;
638
639 r = par->r;
640 dx = Dx(r);
641 dy = Dy(r);
642
643 z = allocdbuf();
644 if(z == nil)
645 return 0;
646
647 src = par->src;
648 mask = par->mask;
649 dst = par->dst;
650 sr = par->sr;
651 mr = par->mr;
652 op = par->op;
653
654 isgrey = dst->flags&Fgrey;
655
656 /*
657 * Buffering when src and dst are the same bitmap is sufficient but not
658 * necessary. There are stronger conditions we could use. We could
659 * check to see if the rectangles intersect, and if simply moving in the
660 * correct y direction can avoid the need to buffer.
661 */
662 needbuf = (src->data == dst->data);
663
664 ndrawbuf = 0;
665 getparam(&z->spar, src, sr, isgrey, needbuf, &ndrawbuf);
666 getparam(&z->dpar, dst, r, isgrey, needbuf, &ndrawbuf);
667 getparam(&z->mpar, mask, mr, 0, needbuf, &ndrawbuf);
668
669 dir = (needbuf && byteaddr(dst, r.min) > byteaddr(src, sr.min)) ? -1 : 1;
670 z->spar.dir = z->mpar.dir = z->dpar.dir = dir;
671
672 /*
673 * If the mask is purely boolean, we can convert from src to dst format
674 * when we read src, and then just copy it to dst where the mask tells us to.
675 * This requires a boolean (1-bit grey) mask and lack of a source alpha channel.
676 *
677 * The computation is accomplished by assigning the function pointers as follows:
678 * rdsrc - read and convert source into dst format in a buffer
679 * rdmask - convert mask to bytes, set pointer to it
680 * rddst - fill with pointer to real dst data, but do no reads
681 * calc - copy src onto dst when mask says to.
682 * wrdst - do nothing
683 * This is slightly sleazy, since things aren't doing exactly what their names say,
684 * but it avoids a fair amount of code duplication to make this a case here
685 * rather than have a separate booldraw.
686 */
687 //if(drawdebug) iprint("flag %lud mchan %lux=?%x dd %d\n", src->flags&Falpha, mask->chan, GREY1, dst->depth);
688 if(!(src->flags&Falpha) && mask->chan == GREY1 && dst->depth >= 8 && op == SoverD){
689 //if(drawdebug) iprint("boolcopy...");
690 rdsrc = convfn(dst, &z->dpar, src, &z->spar, &ndrawbuf);
691 rddst = readptr;
692 rdmask = readfn(mask);
693 calc = boolcopyfn(dst, mask);
694 wrdst = nullwrite;
695 }else{
696 /* usual alphadraw parameter fetching */
697 rdsrc = readfn(src);
698 rddst = readfn(dst);
699 wrdst = writefn(dst);
700 calc = alphacalc[op];
701
702 /*
703 * If there is no alpha channel, we'll ask for a grey channel
704 * and pretend it is the alpha.
705 */
706 if(mask->flags&Falpha){
707 rdmask = readalphafn(mask);
708 z->mpar.alphaonly = 1;
709 }else{
710 z->mpar.greymaskcall = readfn(mask);
711 z->mpar.convgrey = 1;
712 rdmask = greymaskread;
713
714 /*
715 * Should really be above, but then boolcopyfns would have
716 * to deal with bit alignment, and I haven't written that.
717 *
718 * This is a common case for things like ellipse drawing.
719 * When there's no alpha involved and the mask is boolean,
720 * we can avoid all the division and multiplication.
721 */
722 if(mask->chan == GREY1 && !(src->flags&Falpha))
723 calc = boolcalc[op];
724 else if(op == SoverD && !(src->flags&Falpha))
725 calc = alphacalcS;
726 }
727 }
728
729 /*
730 * If the image has a small enough repl rectangle,
731 * we can just read each line once and cache them.
732 */
733 if(z->spar.replcache){
734 z->spar.replcall = rdsrc;
735 rdsrc = replread;
736 }
737 if(z->mpar.replcache){
738 z->mpar.replcall = rdmask;
739 rdmask = replread;
740 }
741
742 if(z->n < ndrawbuf){
743 free(z->p);
744 if((z->p = mallocz(ndrawbuf, 0)) == nil){
745 z->inuse = 0;
746 return 0;
747 }
748 z->n = ndrawbuf;
749 }
750 drawbuf = z->p;
751
752 /*
753 * Before we were saving only offsets from drawbuf in the parameter
754 * structures; now that drawbuf has been grown to accomodate us,
755 * we can fill in the pointers.
756 */
757 z->spar.bufbase = drawbuf+z->spar.bufoff;
758 z->mpar.bufbase = drawbuf+z->mpar.bufoff;
759 z->dpar.bufbase = drawbuf+z->dpar.bufoff;
760 z->spar.convbuf = drawbuf+z->spar.convbufoff;
761
762 if(dir == 1){
763 starty = 0;
764 endy = dy;
765 }else{
766 starty = dy-1;
767 endy = -1;
768 }
769
770 /*
771 * srcy, masky, and dsty are offsets from the top of their
772 * respective Rectangles. they need to be contained within
773 * the rectangles, so clipy can keep them there without division.
774 */
775 srcy = (starty + sr.min.y - src->r.min.y)%Dy(src->r);
776 masky = (starty + mr.min.y - mask->r.min.y)%Dy(mask->r);
777 dsty = starty + r.min.y - dst->r.min.y;
778
779 assert(0 <= srcy && srcy < Dy(src->r));
780 assert(0 <= masky && masky < Dy(mask->r));
781 assert(0 <= dsty && dsty < Dy(dst->r));
782
783 for(y=starty; y!=endy; y+=dir, srcy+=dir, masky+=dir, dsty+=dir){
784 clipy(src, &srcy);
785 clipy(dst, &dsty);
786 clipy(mask, &masky);
787
788 bsrc = rdsrc(&z->spar, z->spar.bufbase, srcy);
789 DBG print("[");
790 bmask = rdmask(&z->mpar, z->mpar.bufbase, masky);
791 DBG print("]\n");
792 bdst = rddst(&z->dpar, z->dpar.bufbase, dsty);
793 DBG dumpbuf("src", bsrc, dx);
794 DBG dumpbuf("mask", bmask, dx);
795 DBG dumpbuf("dst", bdst, dx);
796 bdst = calc(bdst, bsrc, bmask, dx, isgrey, op);
797 wrdst(&z->dpar, z->dpar.bytermin+dsty*z->dpar.bwidth, bdst);
798 }
799
800 z->inuse = 0;
801 return 1;
802 }
803 #undef DBG
804
805 static Buffer
806 alphacalc0(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op)
807 {
808 USED(grey);
809 USED(op);
810 USED(b1);
811 USED(b2);
812 memset(bdst.rgba, 0, dx*bdst.delta);
813 return bdst;
814 }
815
816 /*
817 * Do the channels in the buffers match enough
818 * that we can do word-at-a-time operations
819 * on the pixels?
820 */
821 static int
822 chanmatch(Buffer *bdst, Buffer *bsrc)
823 {
824 uchar *drgb, *srgb;
825
826 /*
827 * first, r, g, b must be in the same place
828 * in the rgba word.
829 */
830 drgb = (uchar*)bdst->rgba;
831 srgb = (uchar*)bsrc->rgba;
832 if(bdst->red - drgb != bsrc->red - srgb
833 || bdst->blu - drgb != bsrc->blu - srgb
834 || bdst->grn - drgb != bsrc->grn - srgb)
835 return 0;
836
837 /*
838 * that implies alpha is in the same place,
839 * if it is there at all (it might be == &ones).
840 * if the destination is &ones, we can scribble
841 * over the rgba slot just fine.
842 */
843 if(bdst->alpha == &ones)
844 return 1;
845
846 /*
847 * if the destination is not ones but the src is,
848 * then the simultaneous calculation will use
849 * bogus bytes from the src's rgba. no good.
850 */
851 if(bsrc->alpha == &ones)
852 return 0;
853
854 /*
855 * otherwise, alphas are in the same place.
856 */
857 return 1;
858 }
859
860 static Buffer
861 alphacalc14(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
862 {
863 Buffer obdst;
864 int fd, sadelta;
865 int i, sa, ma, q;
866 uint32 t, t1;
867
868 obdst = bdst;
869 sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
870 q = bsrc.delta == 4 && bdst.delta == 4 && chanmatch(&bdst, &bsrc);
871
872 for(i=0; i<dx; i++){
873 sa = *bsrc.alpha;
874 ma = *bmask.alpha;
875 fd = CALC11(sa, ma, t);
876 if(op == DoutS)
877 fd = 255-fd;
878
879 if(grey){
880 *bdst.grey = CALC11(fd, *bdst.grey, t);
881 bsrc.grey += bsrc.delta;
882 bdst.grey += bdst.delta;
883 }else{
884 if(q){
885 *bdst.rgba = CALC41(fd, *bdst.rgba, t, t1);
886 bsrc.rgba++;
887 bdst.rgba++;
888 bsrc.alpha += sadelta;
889 bmask.alpha += bmask.delta;
890 continue;
891 }
892 *bdst.red = CALC11(fd, *bdst.red, t);
893 *bdst.grn = CALC11(fd, *bdst.grn, t);
894 *bdst.blu = CALC11(fd, *bdst.blu, t);
895 bsrc.red += bsrc.delta;
896 bsrc.blu += bsrc.delta;
897 bsrc.grn += bsrc.delta;
898 bdst.red += bdst.delta;
899 bdst.blu += bdst.delta;
900 bdst.grn += bdst.delta;
901 }
902 if(bdst.alpha != &ones){
903 *bdst.alpha = CALC11(fd, *bdst.alpha, t);
904 bdst.alpha += bdst.delta;
905 }
906 bmask.alpha += bmask.delta;
907 bsrc.alpha += sadelta;
908 }
909 return obdst;
910 }
911
912 static Buffer
913 alphacalc2810(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
914 {
915 Buffer obdst;
916 int fs, sadelta;
917 int i, ma, da, q;
918 uint32 t, t1;
919
920 obdst = bdst;
921 sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
922 q = bsrc.delta == 4 && bdst.delta == 4 && chanmatch(&bdst, &bsrc);
923
924 for(i=0; i<dx; i++){
925 ma = *bmask.alpha;
926 da = *bdst.alpha;
927 if(op == SoutD)
928 da = 255-da;
929 fs = ma;
930 if(op != S)
931 fs = CALC11(fs, da, t);
932
933 if(grey){
934 *bdst.grey = CALC11(fs, *bsrc.grey, t);
935 bsrc.grey += bsrc.delta;
936 bdst.grey += bdst.delta;
937 }else{
938 if(q){
939 *bdst.rgba = CALC41(fs, *bsrc.rgba, t, t1);
940 bsrc.rgba++;
941 bdst.rgba++;
942 bmask.alpha += bmask.delta;
943 bdst.alpha += bdst.delta;
944 continue;
945 }
946 *bdst.red = CALC11(fs, *bsrc.red, t);
947 *bdst.grn = CALC11(fs, *bsrc.grn, t);
948 *bdst.blu = CALC11(fs, *bsrc.blu, t);
949 bsrc.red += bsrc.delta;
950 bsrc.blu += bsrc.delta;
951 bsrc.grn += bsrc.delta;
952 bdst.red += bdst.delta;
953 bdst.blu += bdst.delta;
954 bdst.grn += bdst.delta;
955 }
956 if(bdst.alpha != &ones){
957 *bdst.alpha = CALC11(fs, *bsrc.alpha, t);
958 bdst.alpha += bdst.delta;
959 }
960 bmask.alpha += bmask.delta;
961 bsrc.alpha += sadelta;
962 }
963 return obdst;
964 }
965
966 static Buffer
967 alphacalc3679(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
968 {
969 Buffer obdst;
970 int fs, fd, sadelta;
971 int i, sa, ma, da, q;
972 uint32 t, t1;
973
974 obdst = bdst;
975 sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
976 q = bsrc.delta == 4 && bdst.delta == 4 && chanmatch(&bdst, &bsrc);
977
978 for(i=0; i<dx; i++){
979 sa = *bsrc.alpha;
980 ma = *bmask.alpha;
981 da = *bdst.alpha;
982 if(op == SatopD)
983 fs = CALC11(ma, da, t);
984 else
985 fs = CALC11(ma, 255-da, t);
986 if(op == DoverS)
987 fd = 255;
988 else{
989 fd = CALC11(sa, ma, t);
990 if(op != DatopS)
991 fd = 255-fd;
992 }
993
994 if(grey){
995 *bdst.grey = CALC12(fs, *bsrc.grey, fd, *bdst.grey, t);
996 bsrc.grey += bsrc.delta;
997 bdst.grey += bdst.delta;
998 }else{
999 if(q){
1000 *bdst.rgba = CALC42(fs, *bsrc.rgba, fd, *bdst.rgba, t, t1);
1001 bsrc.rgba++;
1002 bdst.rgba++;
1003 bsrc.alpha += sadelta;
1004 bmask.alpha += bmask.delta;
1005 bdst.alpha += bdst.delta;
1006 continue;
1007 }
1008 *bdst.red = CALC12(fs, *bsrc.red, fd, *bdst.red, t);
1009 *bdst.grn = CALC12(fs, *bsrc.grn, fd, *bdst.grn, t);
1010 *bdst.blu = CALC12(fs, *bsrc.blu, fd, *bdst.blu, t);
1011 bsrc.red += bsrc.delta;
1012 bsrc.blu += bsrc.delta;
1013 bsrc.grn += bsrc.delta;
1014 bdst.red += bdst.delta;
1015 bdst.blu += bdst.delta;
1016 bdst.grn += bdst.delta;
1017 }
1018 if(bdst.alpha != &ones){
1019 *bdst.alpha = CALC12(fs, sa, fd, da, t);
1020 bdst.alpha += bdst.delta;
1021 }
1022 bmask.alpha += bmask.delta;
1023 bsrc.alpha += sadelta;
1024 }
1025 return obdst;
1026 }
1027
1028 static Buffer
1029 alphacalc5(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op)
1030 {
1031 USED(dx);
1032 USED(grey);
1033 USED(op);
1034 USED(b1);
1035 USED(b2);
1036 return bdst;
1037 }
1038
1039 static Buffer
1040 alphacalc11(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
1041 {
1042 Buffer obdst;
1043 int fd, sadelta;
1044 int i, sa, ma, q;
1045 uint32 t, t1;
1046
1047 USED(op);
1048 obdst = bdst;
1049 sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
1050 q = bsrc.delta == 4 && bdst.delta == 4 && chanmatch(&bdst, &bsrc);
1051
1052 for(i=0; i<dx; i++){
1053 sa = *bsrc.alpha;
1054 ma = *bmask.alpha;
1055 fd = 255-CALC11(sa, ma, t);
1056
1057 if(grey){
1058 *bdst.grey = CALC12(ma, *bsrc.grey, fd, *bdst.grey, t);
1059 bsrc.grey += bsrc.delta;
1060 bdst.grey += bdst.delta;
1061 }else{
1062 if(q){
1063 *bdst.rgba = CALC42(ma, *bsrc.rgba, fd, *bdst.rgba, t, t1);
1064 bsrc.rgba++;
1065 bdst.rgba++;
1066 bsrc.alpha += sadelta;
1067 bmask.alpha += bmask.delta;
1068 continue;
1069 }
1070 *bdst.red = CALC12(ma, *bsrc.red, fd, *bdst.red, t);
1071 *bdst.grn = CALC12(ma, *bsrc.grn, fd, *bdst.grn, t);
1072 *bdst.blu = CALC12(ma, *bsrc.blu, fd, *bdst.blu, t);
1073 bsrc.red += bsrc.delta;
1074 bsrc.blu += bsrc.delta;
1075 bsrc.grn += bsrc.delta;
1076 bdst.red += bdst.delta;
1077 bdst.blu += bdst.delta;
1078 bdst.grn += bdst.delta;
1079 }
1080 if(bdst.alpha != &ones){
1081 *bdst.alpha = CALC12(ma, sa, fd, *bdst.alpha, t);
1082 bdst.alpha += bdst.delta;
1083 }
1084 bmask.alpha += bmask.delta;
1085 bsrc.alpha += sadelta;
1086 }
1087 return obdst;
1088 }
1089
1090 /*
1091 not used yet
1092 source and mask alpha 1
1093 static Buffer
1094 alphacalcS0(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
1095 {
1096 Buffer obdst;
1097 int i;
1098
1099 USED(op);
1100 obdst = bdst;
1101 if(bsrc.delta == bdst.delta){
1102 memmove(bdst.rgba, bsrc.rgba, dx*bdst.delta);
1103 return obdst;
1104 }
1105 for(i=0; i<dx; i++){
1106 if(grey){
1107 *bdst.grey = *bsrc.grey;
1108 bsrc.grey += bsrc.delta;
1109 bdst.grey += bdst.delta;
1110 }else{
1111 *bdst.red = *bsrc.red;
1112 *bdst.grn = *bsrc.grn;
1113 *bdst.blu = *bsrc.blu;
1114 bsrc.red += bsrc.delta;
1115 bsrc.blu += bsrc.delta;
1116 bsrc.grn += bsrc.delta;
1117 bdst.red += bdst.delta;
1118 bdst.blu += bdst.delta;
1119 bdst.grn += bdst.delta;
1120 }
1121 if(bdst.alpha != &ones){
1122 *bdst.alpha = 255;
1123 bdst.alpha += bdst.delta;
1124 }
1125 }
1126 return obdst;
1127 }
1128 */
1129
1130 /* source alpha 1 */
1131 static Buffer
1132 alphacalcS(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
1133 {
1134 Buffer obdst;
1135 int fd;
1136 int i, ma;
1137 uint32 t;
1138
1139 USED(op);
1140 obdst = bdst;
1141
1142 for(i=0; i<dx; i++){
1143 ma = *bmask.alpha;
1144 fd = 255-ma;
1145
1146 if(grey){
1147 *bdst.grey = CALC12(ma, *bsrc.grey, fd, *bdst.grey, t);
1148 bsrc.grey += bsrc.delta;
1149 bdst.grey += bdst.delta;
1150 }else{
1151 *bdst.red = CALC12(ma, *bsrc.red, fd, *bdst.red, t);
1152 *bdst.grn = CALC12(ma, *bsrc.grn, fd, *bdst.grn, t);
1153 *bdst.blu = CALC12(ma, *bsrc.blu, fd, *bdst.blu, t);
1154 bsrc.red += bsrc.delta;
1155 bsrc.blu += bsrc.delta;
1156 bsrc.grn += bsrc.delta;
1157 bdst.red += bdst.delta;
1158 bdst.blu += bdst.delta;
1159 bdst.grn += bdst.delta;
1160 }
1161 if(bdst.alpha != &ones){
1162 *bdst.alpha = ma+CALC11(fd, *bdst.alpha, t);
1163 bdst.alpha += bdst.delta;
1164 }
1165 bmask.alpha += bmask.delta;
1166 }
1167 return obdst;
1168 }
1169
1170 static Buffer
1171 boolcalc14(Buffer bdst, Buffer b1, Buffer bmask, int dx, int grey, int op)
1172 {
1173 Buffer obdst;
1174 int i, ma, zero;
1175
1176 USED(b1);
1177
1178 obdst = bdst;
1179
1180 for(i=0; i<dx; i++){
1181 ma = *bmask.alpha;
1182 zero = ma ? op == DoutS : op == DinS;
1183
1184 if(grey){
1185 if(zero)
1186 *bdst.grey = 0;
1187 bdst.grey += bdst.delta;
1188 }else{
1189 if(zero)
1190 *bdst.red = *bdst.grn = *bdst.blu = 0;
1191 bdst.red += bdst.delta;
1192 bdst.blu += bdst.delta;
1193 bdst.grn += bdst.delta;
1194 }
1195 bmask.alpha += bmask.delta;
1196 if(bdst.alpha != &ones){
1197 if(zero)
1198 *bdst.alpha = 0;
1199 bdst.alpha += bdst.delta;
1200 }
1201 }
1202 return obdst;
1203 }
1204
1205 static Buffer
1206 boolcalc236789(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
1207 {
1208 Buffer obdst;
1209 int fs, fd;
1210 int i, ma, da, zero;
1211 uint32 t;
1212
1213 obdst = bdst;
1214 zero = !(op&1);
1215
1216 for(i=0; i<dx; i++){
1217 ma = *bmask.alpha;
1218 da = *bdst.alpha;
1219 fs = da;
1220 if(op&2)
1221 fs = 255-da;
1222 fd = 0;
1223 if(op&4)
1224 fd = 255;
1225
1226 if(grey){
1227 if(ma)
1228 *bdst.grey = CALC12(fs, *bsrc.grey, fd, *bdst.grey, t);
1229 else if(zero)
1230 *bdst.grey = 0;
1231 bsrc.grey += bsrc.delta;
1232 bdst.grey += bdst.delta;
1233 }else{
1234 if(ma){
1235 *bdst.red = CALC12(fs, *bsrc.red, fd, *bdst.red, t);
1236 *bdst.grn = CALC12(fs, *bsrc.grn, fd, *bdst.grn, t);
1237 *bdst.blu = CALC12(fs, *bsrc.blu, fd, *bdst.blu, t);
1238 }
1239 else if(zero)
1240 *bdst.red = *bdst.grn = *bdst.blu = 0;
1241 bsrc.red += bsrc.delta;
1242 bsrc.blu += bsrc.delta;
1243 bsrc.grn += bsrc.delta;
1244 bdst.red += bdst.delta;
1245 bdst.blu += bdst.delta;
1246 bdst.grn += bdst.delta;
1247 }
1248 bmask.alpha += bmask.delta;
1249 if(bdst.alpha != &ones){
1250 if(ma)
1251 *bdst.alpha = fs+CALC11(fd, da, t);
1252 else if(zero)
1253 *bdst.alpha = 0;
1254 bdst.alpha += bdst.delta;
1255 }
1256 }
1257 return obdst;
1258 }
1259
1260 static Buffer
1261 boolcalc1011(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
1262 {
1263 Buffer obdst;
1264 int i, ma, zero;
1265
1266 obdst = bdst;
1267 zero = !(op&1);
1268
1269 for(i=0; i<dx; i++){
1270 ma = *bmask.alpha;
1271
1272 if(grey){
1273 if(ma)
1274 *bdst.grey = *bsrc.grey;
1275 else if(zero)
1276 *bdst.grey = 0;
1277 bsrc.grey += bsrc.delta;
1278 bdst.grey += bdst.delta;
1279 }else{
1280 if(ma){
1281 *bdst.red = *bsrc.red;
1282 *bdst.grn = *bsrc.grn;
1283 *bdst.blu = *bsrc.blu;
1284 }
1285 else if(zero)
1286 *bdst.red = *bdst.grn = *bdst.blu = 0;
1287 bsrc.red += bsrc.delta;
1288 bsrc.blu += bsrc.delta;
1289 bsrc.grn += bsrc.delta;
1290 bdst.red += bdst.delta;
1291 bdst.blu += bdst.delta;
1292 bdst.grn += bdst.delta;
1293 }
1294 bmask.alpha += bmask.delta;
1295 if(bdst.alpha != &ones){
1296 if(ma)
1297 *bdst.alpha = 255;
1298 else if(zero)
1299 *bdst.alpha = 0;
1300 bdst.alpha += bdst.delta;
1301 }
1302 }
1303 return obdst;
1304 }
1305 /*
1306 * Replicated cached scan line read. Call the function listed in the Param,
1307 * but cache the result so that for replicated images we only do the work once.
1308 */
1309 static Buffer
1310 replread(Param *p, uchar *s, int y)
1311 {
1312 Buffer *b;
1313
1314 USED(s);
1315 b = &p->bcache[y];
1316 if((p->bfilled & (1<<y)) == 0){
1317 p->bfilled |= 1<<y;
1318 *b = p->replcall(p, p->bufbase+y*p->bufdelta, y);
1319 }
1320 return *b;
1321 }
1322
1323 /*
1324 * Alpha reading function that simply relabels the grey pointer.
1325 */
1326 static Buffer
1327 greymaskread(Param *p, uchar *buf, int y)
1328 {
1329 Buffer b;
1330
1331 b = p->greymaskcall(p, buf, y);
1332 b.alpha = b.grey;
1333 return b;
1334 }
1335
1336 #define DBG if(0)
1337 static Buffer
1338 readnbit(Param *p, uchar *buf, int y)
1339 {
1340 Buffer b;
1341 Memimage *img;
1342 uchar *repl, *r, *w, *ow, bits;
1343 memset(&b, 0, sizeof b); // shut up gcc
1344 int i, n, sh, depth, x, dx, npack, nbits;
1345
1346 b.rgba = (uint32*)buf;
1347 b.grey = w = buf;
1348 b.red = b.blu = b.grn = w;
1349 b.alpha = &ones;
1350 b.delta = 1;
1351
1352 dx = p->dx;
1353 img = p->img;
1354 depth = img->depth;
1355 repl = &replbit[depth][0];
1356 npack = 8/depth;
1357 sh = 8-depth;
1358
1359 /* copy from p->r.min.x until end of repl rectangle */
1360 x = p->r.min.x;
1361 n = dx;
1362 if(n > p->img->r.max.x - x)
1363 n = p->img->r.max.x - x;
1364
1365 r = p->bytermin + y*p->bwidth;
1366 DBG print("readnbit dx %d %p=%p+%d*%d, *r=%d fetch %d ", dx, r, p->bytermin, y, p->bwidth, *r, n);
1367 bits = *r++;
1368 nbits = 8;
1369 if((i=x&(npack-1))){
1370 DBG print("throwaway %d...", i);
1371 bits <<= depth*i;
1372 nbits -= depth*i;
1373 }
1374 for(i=0; i<n; i++){
1375 if(nbits == 0){
1376 DBG print("(%.2ux)...", *r);
1377 bits = *r++;
1378 nbits = 8;
1379 }
1380 *w++ = repl[bits>>sh];
1381 DBG print("bit %x...", repl[bits>>sh]);
1382 bits <<= depth;
1383 nbits -= depth;
1384 }
1385 dx -= n;
1386 if(dx == 0)
1387 return b;
1388
1389 assert(x+i == p->img->r.max.x);
1390
1391 /* copy from beginning of repl rectangle until where we were before. */
1392 x = p->img->r.min.x;
1393 n = dx;
1394 if(n > p->r.min.x - x)
1395 n = p->r.min.x - x;
1396
1397 r = p->bytey0s + y*p->bwidth;
1398 DBG print("x=%d r=%p...", x, r);
1399 bits = *r++;
1400 nbits = 8;
1401 if((i=x&(npack-1))){
1402 bits <<= depth*i;
1403 nbits -= depth*i;
1404 }
1405 DBG print("nbits=%d...", nbits);
1406 for(i=0; i<n; i++){
1407 if(nbits == 0){
1408 bits = *r++;
1409 nbits = 8;
1410 }
1411 *w++ = repl[bits>>sh];
1412 DBG print("bit %x...", repl[bits>>sh]);
1413 bits <<= depth;
1414 nbits -= depth;
1415 DBG print("bits %x nbits %d...", bits, nbits);
1416 }
1417 dx -= n;
1418 if(dx == 0)
1419 return b;
1420
1421 assert(dx > 0);
1422 /* now we have exactly one full scan line: just replicate the buffer itself until we are done */
1423 ow = buf;
1424 while(dx--)
1425 *w++ = *ow++;
1426
1427 return b;
1428 }
1429 #undef DBG
1430
1431 #define DBG if(0)
1432 static void
1433 writenbit(Param *p, uchar *w, Buffer src)
1434 {
1435 uchar *r;
1436 uint32 bits;
1437 int i, sh, depth, npack, nbits, x, ex;
1438
1439 assert(src.grey != nil && src.delta == 1);
1440
1441 x = p->r.min.x;
1442 ex = x+p->dx;
1443 depth = p->img->depth;
1444 npack = 8/depth;
1445
1446 i=x&(npack-1);
1447 bits = i ? (*w >> (8-depth*i)) : 0;
1448 nbits = depth*i;
1449 sh = 8-depth;
1450 r = src.grey;
1451
1452 for(; x<ex; x++){
1453 bits <<= depth;
1454 DBG print(" %x", *r);
1455 bits |= (*r++ >> sh);
1456 nbits += depth;
1457 if(nbits == 8){
1458 *w++ = bits;
1459 nbits = 0;
1460 }
1461 }
1462
1463 if(nbits){
1464 sh = 8-nbits;
1465 bits <<= sh;
1466 bits |= *w & ((1<<sh)-1);
1467 *w = bits;
1468 }
1469 DBG print("\n");
1470 return;
1471 }
1472 #undef DBG
1473
1474 static Buffer
1475 readcmap(Param *p, uchar *buf, int y)
1476 {
1477 Buffer b;
1478 int a, convgrey, copyalpha, dx, i, m;
1479 uchar *q, *cmap, *begin, *end, *r, *w;
1480 memset(&b, 0, sizeof b); // shut up gcc
1481
1482 begin = p->bytey0s + y*p->bwidth;
1483 r = p->bytermin + y*p->bwidth;
1484 end = p->bytey0e + y*p->bwidth;
1485 cmap = p->img->cmap->cmap2rgb;
1486 convgrey = p->convgrey;
1487 copyalpha = (p->img->flags&Falpha) ? 1 : 0;
1488
1489 w = buf;
1490 dx = p->dx;
1491 if(copyalpha){
1492 b.alpha = buf++;
1493 a = p->img->shift[CAlpha]/8;
1494 m = p->img->shift[CMap]/8;
1495 for(i=0; i<dx; i++){
1496 *w++ = r[a];
1497 q = cmap+r[m]*3;
1498 r += 2;
1499 if(r == end)
1500 r = begin;
1501 if(convgrey){
1502 *w++ = RGB2K(q[0], q[1], q[2]);
1503 }else{
1504 *w++ = q[2]; /* blue */
1505 *w++ = q[1]; /* green */
1506 *w++ = q[0]; /* red */
1507 }
1508 }
1509 }else{
1510 b.alpha = &ones;
1511 for(i=0; i<dx; i++){
1512 q = cmap+*r++*3;
1513 if(r == end)
1514 r = begin;
1515 if(convgrey){
1516 *w++ = RGB2K(q[0], q[1], q[2]);
1517 }else{
1518 *w++ = q[2]; /* blue */
1519 *w++ = q[1]; /* green */
1520 *w++ = q[0]; /* red */
1521 }
1522 }
1523 }
1524
1525 b.rgba = (uint32*)(buf-copyalpha);
1526
1527 if(convgrey){
1528 b.grey = buf;
1529 b.red = b.blu = b.grn = buf;
1530 b.delta = 1+copyalpha;
1531 }else{
1532 b.blu = buf;
1533 b.grn = buf+1;
1534 b.red = buf+2;
1535 b.grey = nil;
1536 b.delta = 3+copyalpha;
1537 }
1538 return b;
1539 }
1540
1541 static void
1542 writecmap(Param *p, uchar *w, Buffer src)
1543 {
1544 uchar *cmap, *red, *grn, *blu;
1545 int i, dx, delta;
1546
1547 cmap = p->img->cmap->rgb2cmap;
1548
1549 delta = src.delta;
1550 red= src.red;
1551 grn = src.grn;
1552 blu = src.blu;
1553
1554 dx = p->dx;
1555 for(i=0; i<dx; i++, red+=delta, grn+=delta, blu+=delta)
1556 *w++ = cmap[(*red>>4)*256+(*grn>>4)*16+(*blu>>4)];
1557 }
1558
1559 #define DBG if(0)
1560 static Buffer
1561 readbyte(Param *p, uchar *buf, int y)
1562 {
1563 Buffer b;
1564 Memimage *img;
1565 int dx, isgrey, convgrey, alphaonly, copyalpha, i, nb;
1566 uchar *begin, *end, *r, *w, *rrepl, *grepl, *brepl, *arepl, *krepl;
1567 uchar ured, ugrn, ublu;
1568 uint32 u;
1569
1570 img = p->img;
1571 begin = p->bytey0s + y*p->bwidth;
1572 r = p->bytermin + y*p->bwidth;
1573 end = p->bytey0e + y*p->bwidth;
1574
1575 w = buf;
1576 dx = p->dx;
1577 nb = img->depth/8;
1578
1579 convgrey = p->convgrey; /* convert rgb to grey */
1580 isgrey = img->flags&Fgrey;
1581 alphaonly = p->alphaonly;
1582 copyalpha = (img->flags&Falpha) ? 1 : 0;
1583
1584 DBG print("copyalpha %d alphaonly %d convgrey %d isgrey %d\n", copyalpha, alphaonly, convgrey, isgrey);
1585 /* if we can, avoid processing everything */
1586 if(!(img->flags&Frepl) && !convgrey && (img->flags&Fbytes)){
1587 memset(&b, 0, sizeof b);
1588 if(p->needbuf){
1589 memmove(buf, r, dx*nb);
1590 r = buf;
1591 }
1592 b.rgba = (uint32*)r;
1593 if(copyalpha)
1594 b.alpha = r+img->shift[CAlpha]/8;
1595 else
1596 b.alpha = &ones;
1597 if(isgrey){
1598 b.grey = r+img->shift[CGrey]/8;
1599 b.red = b.grn = b.blu = b.grey;
1600 }else{
1601 b.red = r+img->shift[CRed]/8;
1602 b.grn = r+img->shift[CGreen]/8;
1603 b.blu = r+img->shift[CBlue]/8;
1604 }
1605 b.delta = nb;
1606 return b;
1607 }
1608
1609 DBG print("2\n");
1610 rrepl = replbit[img->nbits[CRed]];
1611 grepl = replbit[img->nbits[CGreen]];
1612 brepl = replbit[img->nbits[CBlue]];
1613 arepl = replbit[img->nbits[CAlpha]];
1614 krepl = replbit[img->nbits[CGrey]];
1615
1616 for(i=0; i<dx; i++){
1617 u = r[0] | (r[1]<<8) | (r[2]<<16) | (r[3]<<24);
1618 if(copyalpha) {
1619 *w++ = arepl[(u>>img->shift[CAlpha]) & img->mask[CAlpha]];
1620 DBG print("a %x\n", w[-1]);
1621 }
1622
1623 if(isgrey)
1624 *w++ = krepl[(u >> img->shift[CGrey]) & img->mask[CGrey]];
1625 else if(!alphaonly){
1626 ured = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]];
1627 ugrn = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]];
1628 ublu = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]];
1629 if(convgrey){
1630 DBG print("g %x %x %x\n", ured, ugrn, ublu);
1631 *w++ = RGB2K(ured, ugrn, ublu);
1632 DBG print("%x\n", w[-1]);
1633 }else{
1634 *w++ = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]];
1635 *w++ = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]];
1636 *w++ = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]];
1637 }
1638 }
1639 r += nb;
1640 if(r == end)
1641 r = begin;
1642 }
1643
1644 b.alpha = copyalpha ? buf : &ones;
1645 b.rgba = (uint32*)buf;
1646 if(alphaonly){
1647 b.red = b.grn = b.blu = b.grey = nil;
1648 if(!copyalpha)
1649 b.rgba = nil;
1650 b.delta = 1;
1651 }else if(isgrey || convgrey){
1652 b.grey = buf+copyalpha;
1653 b.red = b.grn = b.blu = buf+copyalpha;
1654 b.delta = copyalpha+1;
1655 DBG print("alpha %x grey %x\n", b.alpha ? *b.alpha : 0xFF, *b.grey);
1656 }else{
1657 b.blu = buf+copyalpha;
1658 b.grn = buf+copyalpha+1;
1659 b.grey = nil;
1660 b.red = buf+copyalpha+2;
1661 b.delta = copyalpha+3;
1662 }
1663 return b;
1664 }
1665 #undef DBG
1666
1667 #define DBG if(0)
1668 static void
1669 writebyte(Param *p, uchar *w, Buffer src)
1670 {
1671 Memimage *img;
1672 int i, isalpha, isgrey, nb, delta, dx, adelta;
1673 uchar ff, *red, *grn, *blu, *grey, *alpha;
1674 uint32 u, mask;
1675
1676 img = p->img;
1677
1678 red = src.red;
1679 grn = src.grn;
1680 blu = src.blu;
1681 alpha = src.alpha;
1682 delta = src.delta;
1683 grey = src.grey;
1684 dx = p->dx;
1685
1686 nb = img->depth/8;
1687 mask = (nb==4) ? 0 : ~((1<<img->depth)-1);
1688
1689 isalpha = img->flags&Falpha;
1690 isgrey = img->flags&Fgrey;
1691 adelta = src.delta;
1692
1693 if(isalpha && (alpha == nil || alpha == &ones)){
1694 ff = 0xFF;
1695 alpha = &ff;
1696 adelta = 0;
1697 }
1698
1699 for(i=0; i<dx; i++){
1700 u = w[0] | (w[1]<<8) | (w[2]<<16) | (w[3]<<24);
1701 DBG print("u %.8lux...", u);
1702 u &= mask;
1703 DBG print("&mask %.8lux...", u);
1704 if(isgrey){
1705 u |= ((*grey >> (8-img->nbits[CGrey])) & img->mask[CGrey]) << img->shift[CGrey];
1706 DBG print("|grey %.8lux...", u);
1707 grey += delta;
1708 }else{
1709 u |= ((*red >> (8-img->nbits[CRed])) & img->mask[CRed]) << img->shift[CRed];
1710 u |= ((*grn >> (8-img->nbits[CGreen])) & img->mask[CGreen]) << img->shift[CGreen];
1711 u |= ((*blu >> (8-img->nbits[CBlue])) & img->mask[CBlue]) << img->shift[CBlue];
1712 red += delta;
1713 grn += delta;
1714 blu += delta;
1715 DBG print("|rgb %.8lux...", u);
1716 }
1717
1718 if(isalpha){
1719 u |= ((*alpha >> (8-img->nbits[CAlpha])) & img->mask[CAlpha]) << img->shift[CAlpha];
1720 alpha += adelta;
1721 DBG print("|alpha %.8lux...", u);
1722 }
1723
1724 w[0] = u;
1725 w[1] = u>>8;
1726 w[2] = u>>16;
1727 w[3] = u>>24;
1728 w += nb;
1729 }
1730 }
1731 #undef DBG
1732
1733 static Readfn*
1734 readfn(Memimage *img)
1735 {
1736 if(img->depth < 8)
1737 return readnbit;
1738 if(img->nbits[CMap] == 8)
1739 return readcmap;
1740 return readbyte;
1741 }
1742
1743 static Readfn*
1744 readalphafn(Memimage *m)
1745 {
1746 USED(m);
1747 return readbyte;
1748 }
1749
1750 static Writefn*
1751 writefn(Memimage *img)
1752 {
1753 if(img->depth < 8)
1754 return writenbit;
1755 if(img->chan == CMAP8)
1756 return writecmap;
1757 return writebyte;
1758 }
1759
1760 static void
1761 nullwrite(Param *p, uchar *s, Buffer b)
1762 {
1763 USED(p);
1764 USED(s);
1765 USED(b);
1766 }
1767
1768 static Buffer
1769 readptr(Param *p, uchar *s, int y)
1770 {
1771 Buffer b;
1772 uchar *q;
1773
1774 memset(&b, 0, sizeof b); // shut up gcc
1775 USED(s);
1776 q = p->bytermin + y*p->bwidth;
1777 b.red = q; /* ptr to data */
1778 b.grn = b.blu = b.grey = b.alpha = nil;
1779 b.rgba = (uint32*)q;
1780 b.delta = p->img->depth/8;
1781 return b;
1782 }
1783
1784 static Buffer
1785 boolmemmove(Buffer bdst, Buffer bsrc, Buffer b1, int dx, int i, int o)
1786 {
1787 USED(i);
1788 USED(o);
1789 USED(b1);
1790 USED(bsrc);
1791 memmove(bdst.red, bsrc.red, dx*bdst.delta);
1792 return bdst;
1793 }
1794
1795 static Buffer
1796 boolcopy8(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o)
1797 {
1798 uchar *m, *r, *w, *ew;
1799
1800 USED(i);
1801 USED(o);
1802 m = bmask.grey;
1803 w = bdst.red;
1804 r = bsrc.red;
1805 ew = w+dx;
1806 for(; w < ew; w++,r++)
1807 if(*m++)
1808 *w = *r;
1809 return bdst; /* not used */
1810 }
1811
1812 static Buffer
1813 boolcopy16(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o)
1814 {
1815 uchar *m;
1816 ushort *r, *w, *ew;
1817
1818 USED(i);
1819 USED(o);
1820 m = bmask.grey;
1821 w = (ushort*)bdst.red;
1822 r = (ushort*)bsrc.red;
1823 ew = w+dx;
1824 for(; w < ew; w++,r++)
1825 if(*m++)
1826 *w = *r;
1827 return bdst; /* not used */
1828 }
1829
1830 static Buffer
1831 boolcopy24(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o)
1832 {
1833 uchar *m;
1834 uchar *r, *w, *ew;
1835
1836 USED(i);
1837 USED(o);
1838 m = bmask.grey;
1839 w = bdst.red;
1840 r = bsrc.red;
1841 ew = w+dx*3;
1842 while(w < ew){
1843 if(*m++){
1844 *w++ = *r++;
1845 *w++ = *r++;
1846 *w++ = *r++;
1847 }else{
1848 w += 3;
1849 r += 3;
1850 }
1851 }
1852 return bdst; /* not used */
1853 }
1854
1855 static Buffer
1856 boolcopy32(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o)
1857 {
1858 uchar *m;
1859 uint32 *r, *w, *ew;
1860
1861 USED(i);
1862 USED(o);
1863 m = bmask.grey;
1864 w = (uint32*)bdst.red;
1865 r = (uint32*)bsrc.red;
1866 ew = w+dx;
1867 for(; w < ew; w++,r++)
1868 if(*m++)
1869 *w = *r;
1870 return bdst; /* not used */
1871 }
1872
1873 static Buffer
1874 genconv(Param *p, uchar *buf, int y)
1875 {
1876 Buffer b;
1877 int nb;
1878 uchar *r, *w, *ew;
1879
1880 /* read from source into RGB format in convbuf */
1881 b = p->convreadcall(p, p->convbuf, y);
1882
1883 /* write RGB format into dst format in buf */
1884 p->convwritecall(p->convdpar, buf, b);
1885
1886 if(p->convdx){
1887 nb = p->convdpar->img->depth/8;
1888 r = buf;
1889 w = buf+nb*p->dx;
1890 ew = buf+nb*p->convdx;
1891 while(w<ew)
1892 *w++ = *r++;
1893 }
1894
1895 b.red = buf;
1896 b.blu = b.grn = b.grey = b.alpha = nil;
1897 b.rgba = (uint32*)buf;
1898 b.delta = 0;
1899
1900 return b;
1901 }
1902
1903 static Readfn*
1904 convfn(Memimage *dst, Param *dpar, Memimage *src, Param *spar, int *ndrawbuf)
1905 {
1906 if(dst->chan == src->chan && !(src->flags&Frepl)){
1907 //if(drawdebug) iprint("readptr...");
1908 return readptr;
1909 }
1910
1911 if(dst->chan==CMAP8 && (src->chan==GREY1||src->chan==GREY2||src->chan==GREY4)){
1912 /* cheat because we know the replicated value is exactly the color map entry. */
1913 //if(drawdebug) iprint("Readnbit...");
1914 return readnbit;
1915 }
1916
1917 spar->convreadcall = readfn(src);
1918 spar->convwritecall = writefn(dst);
1919 spar->convdpar = dpar;
1920
1921 /* allocate a conversion buffer */
1922 spar->convbufoff = *ndrawbuf;
1923 *ndrawbuf += spar->dx*4;
1924
1925 if(spar->dx > Dx(spar->img->r)){
1926 spar->convdx = spar->dx;
1927 spar->dx = Dx(spar->img->r);
1928 }
1929
1930 //if(drawdebug) iprint("genconv...");
1931 return genconv;
1932 }
1933
1934 uint32
1935 _pixelbits(Memimage *i, Point pt)
1936 {
1937 uchar *p;
1938 uint32 val;
1939 int off, bpp, npack;
1940
1941 val = 0;
1942 p = byteaddr(i, pt);
1943 switch(bpp=i->depth){
1944 case 1:
1945 case 2:
1946 case 4:
1947 npack = 8/bpp;
1948 off = pt.x%npack;
1949 val = p[0] >> bpp*(npack-1-off);
1950 val &= (1<<bpp)-1;
1951 break;
1952 case 8:
1953 val = p[0];
1954 break;
1955 case 16:
1956 val = p[0]|(p[1]<<8);
1957 break;
1958 case 24:
1959 val = p[0]|(p[1]<<8)|(p[2]<<16);
1960 break;
1961 case 32:
1962 val = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
1963 break;
1964 }
1965 while(bpp<32){
1966 val |= val<<bpp;
1967 bpp *= 2;
1968 }
1969 return val;
1970 }
1971
1972 static Calcfn*
1973 boolcopyfn(Memimage *img, Memimage *mask)
1974 {
1975 if(mask->flags&Frepl && Dx(mask->r)==1 && Dy(mask->r)==1 && pixelbits(mask, mask->r.min)==~0)
1976 return boolmemmove;
1977
1978 switch(img->depth){
1979 case 8:
1980 return boolcopy8;
1981 case 16:
1982 return boolcopy16;
1983 case 24:
1984 return boolcopy24;
1985 case 32:
1986 return boolcopy32;
1987 default:
1988 assert(0 /* boolcopyfn */);
1989 }
1990 return nil;
1991 }
1992
1993 /*
1994 * Optimized draw for filling and scrolling; uses memset and memmove.
1995 */
1996 #if 0
1997 static void
1998 memsetb(void *vp, uchar val, int n)
1999 {
2000 uchar *p, *ep;
2001
2002 p = vp;
2003 ep = p+n;
2004 while(p<ep)
2005 *p++ = val;
2006 }
2007 #endif
2008
2009 static void
2010 memsets(void *vp, ushort val, int n)
2011 {
2012 ushort *p, *ep;
2013
2014 p = vp;
2015 ep = p+n;
2016 while(p<ep)
2017 *p++ = val;
2018 }
2019
2020 static void
2021 memsetl(void *vp, uint32 val, int n)
2022 {
2023 uint32 *p, *ep;
2024
2025 p = vp;
2026 ep = p+n;
2027 while(p<ep)
2028 *p++ = val;
2029 }
2030
2031 static void
2032 memset24(void *vp, uint32 val, int n)
2033 {
2034 uchar *p, *ep;
2035 uchar a,b,c;
2036
2037 p = vp;
2038 ep = p+3*n;
2039 a = val;
2040 b = val>>8;
2041 c = val>>16;
2042 while(p<ep){
2043 *p++ = a;
2044 *p++ = b;
2045 *p++ = c;
2046 }
2047 }
2048
2049 uint32
2050 _imgtorgba(Memimage *img, uint32 val)
2051 {
2052 uchar r, g, b, a;
2053 int nb, ov, v;
2054 uint32 chan;
2055 uchar *p;
2056
2057 a = 0xFF;
2058 r = g = b = 0xAA; /* garbage */
2059 for(chan=img->chan; chan; chan>>=8){
2060 nb = NBITS(chan);
2061 ov = v = val&((1<<nb)-1);
2062 val >>= nb;
2063
2064 while(nb < 8){
2065 v |= v<<nb;
2066 nb *= 2;
2067 }
2068 v >>= (nb-8);
2069
2070 switch(TYPE(chan)){
2071 case CRed:
2072 r = v;
2073 break;
2074 case CGreen:
2075 g = v;
2076 break;
2077 case CBlue:
2078 b = v;
2079 break;
2080 case CAlpha:
2081 a = v;
2082 break;
2083 case CGrey:
2084 r = g = b = v;
2085 break;
2086 case CMap:
2087 p = img->cmap->cmap2rgb+3*ov;
2088 r = *p++;
2089 g = *p++;
2090 b = *p;
2091 break;
2092 }
2093 }
2094 return (r<<24)|(g<<16)|(b<<8)|a;
2095 }
2096
2097 uint32
2098 _rgbatoimg(Memimage *img, uint32 rgba)
2099 {
2100 uint32 chan;
2101 int d, nb;
2102 uint32 v;
2103 uchar *p, r, g, b, a, m;
2104
2105 v = 0;
2106 r = rgba>>24;
2107 g = rgba>>16;
2108 b = rgba>>8;
2109 a = rgba;
2110 d = 0;
2111 for(chan=img->chan; chan; chan>>=8){
2112 nb = NBITS(chan);
2113 switch(TYPE(chan)){
2114 case CRed:
2115 v |= (r>>(8-nb))<<d;
2116 break;
2117 case CGreen:
2118 v |= (g>>(8-nb))<<d;
2119 break;
2120 case CBlue:
2121 v |= (b>>(8-nb))<<d;
2122 break;
2123 case CAlpha:
2124 v |= (a>>(8-nb))<<d;
2125 break;
2126 case CMap:
2127 p = img->cmap->rgb2cmap;
2128 m = p[(r>>4)*256+(g>>4)*16+(b>>4)];
2129 v |= (m>>(8-nb))<<d;
2130 break;
2131 case CGrey:
2132 m = RGB2K(r,g,b);
2133 v |= (m>>(8-nb))<<d;
2134 break;
2135 }
2136 d += nb;
2137 }
2138 // print("rgba2img %.8lux = %.*lux\n", rgba, 2*d/8, v);
2139 return v;
2140 }
2141
2142 #define DBG if(0)
2143 static int
2144 memoptdraw(Memdrawparam *par)
2145 {
2146 int m, y, dy, dx, op;
2147 uint32 v;
2148 Memimage *src;
2149 Memimage *dst;
2150
2151 dx = Dx(par->r);
2152 dy = Dy(par->r);
2153 src = par->src;
2154 dst = par->dst;
2155 op = par->op;
2156
2157 DBG print("state %lux mval %lux dd %d\n", par->state, par->mval, dst->depth);
2158 /*
2159 * If we have an opaque mask and source is one opaque pixel we can convert to the
2160 * destination format and just replicate with memset.
2161 */
2162 m = Simplesrc|Simplemask|Fullmask;
2163 if((par->state&m)==m && (par->srgba&0xFF) == 0xFF && (op ==S || op == SoverD)){
2164 uchar *dp, p[4];
2165 int d, dwid, ppb, np, nb;
2166 uchar lm, rm;
2167
2168 DBG print("memopt, dst %p, dst->data->bdata %p\n", dst, dst->data->bdata);
2169 dwid = dst->width*sizeof(uint32);
2170 dp = byteaddr(dst, par->r.min);
2171 v = par->sdval;
2172 DBG print("sdval %lud, depth %d\n", v, dst->depth);
2173 switch(dst->depth){
2174 case 1:
2175 case 2:
2176 case 4:
2177 for(d=dst->depth; d<8; d*=2)
2178 v |= (v<<d);
2179 ppb = 8/dst->depth; /* pixels per byte */
2180 m = ppb-1;
2181 /* left edge */
2182 np = par->r.min.x&m; /* no. pixels unused on left side of word */
2183 dx -= (ppb-np);
2184 nb = 8 - np * dst->depth; /* no. bits used on right side of word */
2185 lm = (1<<nb)-1;
2186 DBG print("np %d x %d nb %d lm %ux ppb %d m %ux\n", np, par->r.min.x, nb, lm, ppb, m);
2187
2188 /* right edge */
2189 np = par->r.max.x&m; /* no. pixels used on left side of word */
2190 dx -= np;
2191 nb = 8 - np * dst->depth; /* no. bits unused on right side of word */
2192 rm = ~((1<<nb)-1);
2193 DBG print("np %d x %d nb %d rm %ux ppb %d m %ux\n", np, par->r.max.x, nb, rm, ppb, m);
2194
2195 DBG print("dx %d Dx %d\n", dx, Dx(par->r));
2196 /* lm, rm are masks that are 1 where we should touch the bits */
2197 if(dx < 0){ /* just one byte */
2198 lm &= rm;
2199 for(y=0; y<dy; y++, dp+=dwid)
2200 *dp ^= (v ^ *dp) & lm;
2201 }else if(dx == 0){ /* no full bytes */
2202 if(lm)
2203 dwid--;
2204
2205 for(y=0; y<dy; y++, dp+=dwid){
2206 if(lm){
2207 DBG print("dp %p v %lux lm %ux (v ^ *dp) & lm %lux\n", dp, v, lm, (v^*dp)&lm);
2208 *dp ^= (v ^ *dp) & lm;
2209 dp++;
2210 }
2211 *dp ^= (v ^ *dp) & rm;
2212 }
2213 }else{ /* full bytes in middle */
2214 dx /= ppb;
2215 if(lm)
2216 dwid--;
2217 dwid -= dx;
2218
2219 for(y=0; y<dy; y++, dp+=dwid){
2220 if(lm){
2221 *dp ^= (v ^ *dp) & lm;
2222 dp++;
2223 }
2224 memset(dp, v, dx);
2225 dp += dx;
2226 *dp ^= (v ^ *dp) & rm;
2227 }
2228 }
2229 return 1;
2230 case 8:
2231 for(y=0; y<dy; y++, dp+=dwid)
2232 memset(dp, v, dx);
2233 return 1;
2234 case 16:
2235 p[0] = v; /* make little endian */
2236 p[1] = v>>8;
2237 v = *(ushort*)p;
2238 DBG print("dp=%p; dx=%d; for(y=0; y<%d; y++, dp+=%d)\nmemsets(dp, v, dx);\n",
2239 dp, dx, dy, dwid);
2240 for(y=0; y<dy; y++, dp+=dwid)
2241 memsets(dp, v, dx);
2242 return 1;
2243 case 24:
2244 for(y=0; y<dy; y++, dp+=dwid)
2245 memset24(dp, v, dx);
2246 return 1;
2247 case 32:
2248 p[0] = v; /* make little endian */
2249 p[1] = v>>8;
2250 p[2] = v>>16;
2251 p[3] = v>>24;
2252 v = *(uint32*)p;
2253 for(y=0; y<dy; y++, dp+=dwid)
2254 memsetl(dp, v, dx);
2255 return 1;
2256 default:
2257 assert(0 /* bad dest depth in memoptdraw */);
2258 }
2259 }
2260
2261 /*
2262 * If no source alpha, an opaque mask, we can just copy the
2263 * source onto the destination. If the channels are the same and
2264 * the source is not replicated, memmove suffices.
2265 */
2266 m = Simplemask|Fullmask;
2267 if((par->state&(m|Replsrc))==m && src->depth >= 8
2268 && src->chan == dst->chan && !(src->flags&Falpha) && (op == S || op == SoverD)){
2269 uchar *sp, *dp;
2270 long swid, dwid, nb;
2271 int dir;
2272
2273 if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min))
2274 dir = -1;
2275 else
2276 dir = 1;
2277
2278 swid = src->width*sizeof(uint32);
2279 dwid = dst->width*sizeof(uint32);
2280 sp = byteaddr(src, par->sr.min);
2281 dp = byteaddr(dst, par->r.min);
2282 if(dir == -1){
2283 sp += (dy-1)*swid;
2284 dp += (dy-1)*dwid;
2285 swid = -swid;
2286 dwid = -dwid;
2287 }
2288 nb = (dx*src->depth)/8;
2289 for(y=0; y<dy; y++, sp+=swid, dp+=dwid)
2290 memmove(dp, sp, nb);
2291 return 1;
2292 }
2293
2294 /*
2295 * If we have a 1-bit mask, 1-bit source, and 1-bit destination, and
2296 * they're all bit aligned, we can just use bit operators. This happens
2297 * when we're manipulating boolean masks, e.g. in the arc code.
2298 */
2299 if((par->state&(Simplemask|Simplesrc|Replmask|Replsrc))==0
2300 && dst->chan==GREY1 && src->chan==GREY1 && par->mask->chan==GREY1
2301 && (par->r.min.x&7)==(par->sr.min.x&7) && (par->r.min.x&7)==(par->mr.min.x&7)){
2302 uchar *sp, *dp, *mp;
2303 uchar lm, rm;
2304 long swid, dwid, mwid;
2305 int i, x, dir;
2306
2307 sp = byteaddr(src, par->sr.min);
2308 dp = byteaddr(dst, par->r.min);
2309 mp = byteaddr(par->mask, par->mr.min);
2310 swid = src->width*sizeof(uint32);
2311 dwid = dst->width*sizeof(uint32);
2312 mwid = par->mask->width*sizeof(uint32);
2313
2314 if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min)){
2315 dir = -1;
2316 }else
2317 dir = 1;
2318
2319 lm = 0xFF>>(par->r.min.x&7);
2320 rm = 0xFF<<(8-(par->r.max.x&7));
2321 dx -= (8-(par->r.min.x&7)) + (par->r.max.x&7);
2322
2323 if(dx < 0){ /* one byte wide */
2324 lm &= rm;
2325 if(dir == -1){
2326 dp += dwid*(dy-1);
2327 sp += swid*(dy-1);
2328 mp += mwid*(dy-1);
2329 dwid = -dwid;
2330 swid = -swid;
2331 mwid = -mwid;
2332 }
2333 for(y=0; y<dy; y++){
2334 *dp ^= (*dp ^ *sp) & *mp & lm;
2335 dp += dwid;
2336 sp += swid;
2337 mp += mwid;
2338 }
2339 return 1;
2340 }
2341
2342 dx /= 8;
2343 if(dir == 1){
2344 i = (lm!=0)+dx+(rm!=0);
2345 mwid -= i;
2346 swid -= i;
2347 dwid -= i;
2348 for(y=0; y<dy; y++, dp+=dwid, sp+=swid, mp+=mwid){
2349 if(lm){
2350 *dp ^= (*dp ^ *sp++) & *mp++ & lm;
2351 dp++;
2352 }
2353 for(x=0; x<dx; x++){
2354 *dp ^= (*dp ^ *sp++) & *mp++;
2355 dp++;
2356 }
2357 if(rm){
2358 *dp ^= (*dp ^ *sp++) & *mp++ & rm;
2359 dp++;
2360 }
2361 }
2362 return 1;
2363 }else{
2364 /* dir == -1 */
2365 i = (lm!=0)+dx+(rm!=0);
2366 dp += dwid*(dy-1)+i-1;
2367 sp += swid*(dy-1)+i-1;
2368 mp += mwid*(dy-1)+i-1;
2369 dwid = -dwid+i;
2370 swid = -swid+i;
2371 mwid = -mwid+i;
2372 for(y=0; y<dy; y++, dp+=dwid, sp+=swid, mp+=mwid){
2373 if(rm){
2374 *dp ^= (*dp ^ *sp--) & *mp-- & rm;
2375 dp--;
2376 }
2377 for(x=0; x<dx; x++){
2378 *dp ^= (*dp ^ *sp--) & *mp--;
2379 dp--;
2380 }
2381 if(lm){
2382 *dp ^= (*dp ^ *sp--) & *mp-- & lm;
2383 dp--;
2384 }
2385 }
2386 }
2387 return 1;
2388 }
2389 return 0;
2390 }
2391 #undef DBG
2392
2393 /*
2394 * Boolean character drawing.
2395 * Solid opaque color through a 1-bit greyscale mask.
2396 */
2397 #define DBG if(0)
2398 static int
2399 chardraw(Memdrawparam *par)
2400 {
2401 uint32 bits;
2402 int i, ddepth, dy, dx, x, bx, ex, y, npack, bsh, depth, op;
2403 uint32 v, maskwid, dstwid;
2404 uchar *wp, *rp, *q, *wc;
2405 ushort *ws;
2406 uint32 *wl;
2407 uchar sp[4];
2408 Rectangle r, mr;
2409 Memimage *mask, *src, *dst;
2410
2411 if(0) if(drawdebug) iprint("chardraw? mf %lux md %d sf %lux dxs %d dys %d dd %d ddat %p sdat %p\n",
2412 par->mask->flags, par->mask->depth, par->src->flags,
2413 Dx(par->src->r), Dy(par->src->r), par->dst->depth, par->dst->data, par->src->data);
2414
2415 mask = par->mask;
2416 src = par->src;
2417 dst = par->dst;
2418 r = par->r;
2419 mr = par->mr;
2420 op = par->op;
2421
2422 if((par->state&(Replsrc|Simplesrc|Fullsrc|Replmask)) != (Replsrc|Simplesrc|Fullsrc)
2423 || mask->depth != 1 || src->flags&Falpha || dst->depth<8 || dst->data==src->data
2424 || op != SoverD)
2425 return 0;
2426
2427 //if(drawdebug) iprint("chardraw...");
2428
2429 depth = mask->depth;
2430 maskwid = mask->width*sizeof(uint32);
2431 rp = byteaddr(mask, mr.min);
2432 npack = 8/depth;
2433 bsh = (mr.min.x % npack) * depth;
2434
2435 wp = byteaddr(dst, r.min);
2436 dstwid = dst->width*sizeof(uint32);
2437 DBG print("bsh %d\n", bsh);
2438 dy = Dy(r);
2439 dx = Dx(r);
2440
2441 ddepth = dst->depth;
2442
2443 /*
2444 * for loop counts from bsh to bsh+dx
2445 *
2446 * we want the bottom bits to be the amount
2447 * to shift the pixels down, so for n≡0 (mod 8) we want
2448 * bottom bits 7. for n≡1, 6, etc.
2449 * the bits come from -n-1.
2450 */
2451
2452 bx = -bsh-1;
2453 ex = -bsh-1-dx;
2454 bits=0;
2455 v = par->sdval;
2456
2457 /* make little endian */
2458 sp[0] = v;
2459 sp[1] = v>>8;
2460 sp[2] = v>>16;
2461 sp[3] = v>>24;
2462
2463 //print("sp %x %x %x %x\n", sp[0], sp[1], sp[2], sp[3]);
2464 for(y=0; y<dy; y++, rp+=maskwid, wp+=dstwid){
2465 q = rp;
2466 if(bsh)
2467 bits = *q++;
2468 switch(ddepth){
2469 case 8:
2470 //if(drawdebug) iprint("8loop...");
2471 wc = wp;
2472 for(x=bx; x>ex; x--, wc++){
2473 i = x&7;
2474 if(i == 8-1)
2475 bits = *q++;
2476 DBG print("bits %lux sh %d...", bits, i);
2477 if((bits>>i)&1)
2478 *wc = v;
2479 }
2480 break;
2481 case 16:
2482 ws = (ushort*)wp;
2483 v = *(ushort*)sp;
2484 for(x=bx; x>ex; x--, ws++){
2485 i = x&7;
2486 if(i == 8-1)
2487 bits = *q++;
2488 DBG print("bits %lux sh %d...", bits, i);
2489 if((bits>>i)&1)
2490 *ws = v;
2491 }
2492 break;
2493 case 24:
2494 wc = wp;
2495 for(x=bx; x>ex; x--, wc+=3){
2496 i = x&7;
2497 if(i == 8-1)
2498 bits = *q++;
2499 DBG print("bits %lux sh %d...", bits, i);
2500 if((bits>>i)&1){
2501 wc[0] = sp[0];
2502 wc[1] = sp[1];
2503 wc[2] = sp[2];
2504 }
2505 }
2506 break;
2507 case 32:
2508 wl = (uint32*)wp;
2509 v = *(uint32*)sp;
2510 for(x=bx; x>ex; x--, wl++){
2511 i = x&7;
2512 if(i == 8-1)
2513 bits = *q++;
2514 DBG iprint("bits %lux sh %d...", bits, i);
2515 if((bits>>i)&1)
2516 *wl = v;
2517 }
2518 break;
2519 }
2520 }
2521
2522 DBG print("\n");
2523 return 1;
2524 }
2525 #undef DBG
2526
2527
2528 /*
2529 * Fill entire byte with replicated (if necessary) copy of source pixel,
2530 * assuming destination ldepth is >= source ldepth.
2531 *
2532 * This code is just plain wrong for >8bpp.
2533 *
2534 uint32
2535 membyteval(Memimage *src)
2536 {
2537 int i, val, bpp;
2538 uchar uc;
2539
2540 unloadmemimage(src, src->r, &uc, 1);
2541 bpp = src->depth;
2542 uc <<= (src->r.min.x&(7/src->depth))*src->depth;
2543 uc &= ~(0xFF>>bpp);
2544 // pixel value is now in high part of byte. repeat throughout byte
2545 val = uc;
2546 for(i=bpp; i<8; i<<=1)
2547 val |= val>>i;
2548 return val;
2549 }
2550 *
2551 */
2552
2553 void
2554 _memfillcolor(Memimage *i, uint32 val)
2555 {
2556 uint32 bits;
2557 int d, y;
2558
2559 if(val == DNofill)
2560 return;
2561
2562 bits = _rgbatoimg(i, val);
2563 switch(i->depth){
2564 case 24: /* 24-bit images suck */
2565 for(y=i->r.min.y; y<i->r.max.y; y++)
2566 memset24(byteaddr(i, Pt(i->r.min.x, y)), bits, Dx(i->r));
2567 break;
2568 default: /* 1, 2, 4, 8, 16, 32 */
2569 for(d=i->depth; d<32; d*=2)
2570 bits = (bits << d) | bits;
2571 memsetl(wordaddr(i, i->r.min), bits, i->width*Dy(i->r));
2572 break;
2573 }
2574 }
2575