1 /*
2  * xvsmooth.c - smoothing/color dither routines for XV
3  *
4  *  Contains:
5  *            byte *SmoothResize(src8, swide, shigh, dwide, dhigh,
6  *                               rmap, gmap, bmap, rdmap, gdmap, bdmap, maplen)
7  *            byte *Smooth24(pic824, is24, swide, shigh, dwide, dhigh,
8  *                               rmap, gmap, bmap)
9  *            byte *DoColorDither(pic24, pic8, w, h, rmap,gmap,bmap,
10  *                                rdisp, gdisp, bdisp, maplen)
11  *            byte *Do332ColorDither(pic24, pic8, w, h, rmap,gmap,bmap,
12  *                                rdisp, gdisp, bdisp, maplen)
13  */
14 
15 #include "copyright.h"
16 
17 #include "xv.h"
18 
19 static int smoothX  PARM((byte *, byte *, int, int, int, int, int,
20 			  byte *, byte *, byte *));
21 static int smoothY  PARM((byte *, byte *, int, int, int, int, int,
22 			  byte *, byte *, byte *));
23 static int smoothXY PARM((byte *, byte *, int, int, int, int, int,
24 			  byte *, byte *, byte *));
25 
26 
27 /***************************************************/
SmoothResize(srcpic8,swide,shigh,dwide,dhigh,rmap,gmap,bmap,rdmap,gdmap,bdmap,maplen)28 byte *SmoothResize(srcpic8, swide, shigh, dwide, dhigh,
29 		   rmap, gmap, bmap, rdmap, gdmap, bdmap, maplen)
30      byte *srcpic8, *rmap, *gmap, *bmap, *rdmap, *gdmap, *bdmap;
31      int   swide, shigh, dwide, dhigh, maplen;
32 {
33   /* generic interface to Smooth and ColorDither code.
34      given an 8-bit-per, swide * shigh image with colormap rmap,gmap,bmap,
35      will generate a new 8-bit-per, dwide * dhigh image, which is dithered
36      using colors found in rdmap, gdmap, bdmap arrays */
37 
38   /* returns ptr to a dwide*dhigh array of bytes, or NULL on failure */
39 
40   byte *pic24, *pic8;
41 
42   pic24 = Smooth24(srcpic8, 0, swide, shigh, dwide, dhigh, rmap, gmap, bmap);
43 
44   if (pic24) {
45     pic8 = DoColorDither(pic24, NULL, dwide, dhigh, rmap, gmap, bmap,
46 			 rdmap, gdmap, bdmap, maplen);
47     free(pic24);
48     return pic8;
49   }
50 
51   return (byte *) NULL;
52 }
53 
54 
55 
56 /***************************************************/
Smooth24(pic824,is24,swide,shigh,dwide,dhigh,rmap,gmap,bmap)57 byte *Smooth24(pic824, is24, swide, shigh, dwide, dhigh, rmap, gmap, bmap)
58      byte *pic824, *rmap, *gmap, *bmap;
59      int   is24, swide, shigh, dwide, dhigh;
60 {
61   /* does a SMOOTH resize from pic824 (which is either a swide*shigh, 8-bit
62      pic, with colormap rmap,gmap,bmap OR a swide*shigh, 24-bit image, based
63      on whether 'is24' is set) into a dwide * dhigh 24-bit image
64 
65      returns a dwide*dhigh 24bit image, or NULL on failure (malloc) */
66   /* rmap,gmap,bmap should be 'desired' colors */
67 
68   byte *pic24, *pp;
69   int  *cxtab, *pxtab;
70   int   y1Off, cyOff;
71   int   ex, ey, cx, cy, px, py, apx, apy, x1, y1;
72   int   cA, cB, cC, cD;
73   int   pA, pB, pC, pD;
74   int   retval, bperpix;
75 
76   cA = cB = cC = cD = 0;
77   pp = pic24 = (byte *) malloc((size_t) (dwide * dhigh * 3));
78   if (!pic24) {
79     fprintf(stderr,"unable to malloc pic24 in 'Smooth24()'\n");
80     return pic24;
81   }
82 
83   bperpix = (is24) ? 3 : 1;
84 
85   /* decide which smoothing routine to use based on type of expansion */
86   if      (dwide <  swide && dhigh <  shigh)
87     retval = smoothXY(pic24, pic824, is24, swide, shigh, dwide, dhigh,
88 		      rmap, gmap, bmap);
89 
90   else if (dwide <  swide && dhigh >= shigh)
91     retval = smoothX (pic24, pic824, is24, swide, shigh, dwide, dhigh,
92 		      rmap, gmap, bmap);
93 
94   else if (dwide >= swide && dhigh <  shigh)
95     retval = smoothY (pic24, pic824, is24, swide, shigh, dwide, dhigh,
96 		      rmap, gmap, bmap);
97 
98   else {
99     /* dwide >= swide && dhigh >= shigh */
100 
101     /* cx,cy = original pixel in pic824.  px,py = relative position
102        of pixel ex,ey inside of cx,cy as percentages +-50%, +-50%.
103        0,0 = middle of pixel */
104 
105     /* we can save a lot of time by precomputing cxtab[] and pxtab[], both
106        dwide arrays of ints that contain values for the equations:
107          cx = (ex * swide) / dwide;
108          px = ((ex * swide * 128) / dwide) - (cx * 128) - 64; */
109 
110     cxtab = (int *) malloc(dwide * sizeof(int));
111     if (!cxtab) { free(pic24);  return NULL; }
112 
113     pxtab = (int *) malloc(dwide * sizeof(int));
114     if (!pxtab) { free(pic24);  free(cxtab);  return NULL; }
115 
116     for (ex=0; ex<dwide; ex++) {
117       cxtab[ex] = (ex * swide) / dwide;
118       pxtab[ex] = (((ex * swide)* 128) / dwide)
119 	           - (cxtab[ex] * 128) - 64;
120     }
121 
122     for (ey=0; ey<dhigh; ey++) {
123       byte *pptr, rA, gA, bA, rB, gB, bB, rC, gC, bC, rD, gD, bD;
124 
125       ProgressMeter(0, (dhigh)-1, ey, "Smooth");
126 
127       cy = (ey * shigh) / dhigh;
128       py = (((ey * shigh) * 128) / dhigh) - (cy * 128) - 64;
129       if (py<0) { y1 = cy-1;  if (y1<0) y1=0; }
130            else { y1 = cy+1;  if (y1>shigh-1) y1=shigh-1; }
131 
132       cyOff = cy * swide * bperpix;    /* current line */
133       y1Off = y1 * swide * bperpix;    /* up or down one line, depending */
134 
135       if ((ey&15) == 0) WaitCursor();
136 
137       for (ex=0; ex<dwide; ex++) {
138 	rA = rB = rC = rD = gA = gB = gC = gD = bA = bB = bC = bD = 0;
139 
140 	cx = cxtab[ex];
141 	px = pxtab[ex];
142 
143 	if (px<0) { x1 = cx-1;  if (x1<0) x1=0; }
144 	     else { x1 = cx+1;  if (x1>swide-1) x1=swide-1; }
145 
146 	if (is24) {
147 	  pptr = pic824 + y1Off + x1*bperpix;   /* corner pixel */
148 	  rA = *pptr++;  gA = *pptr++;  bA = *pptr++;
149 
150 	  pptr = pic824 + y1Off + cx*bperpix;   /* up/down center pixel */
151 	  rB = *pptr++;  gB = *pptr++;  bB = *pptr++;
152 
153 	  pptr = pic824 + cyOff + x1*bperpix;   /* left/right center pixel */
154 	  rC = *pptr++;  gC = *pptr++;  bC = *pptr++;
155 
156 	  pptr = pic824 + cyOff + cx*bperpix;   /* center pixel */
157 	  rD = *pptr++;  gD = *pptr++;  bD = *pptr++;
158 	}
159 	else {  /* 8-bit picture */
160 	  cA = pic824[y1Off + x1];   /* corner pixel */
161 	  cB = pic824[y1Off + cx];   /* up/down center pixel */
162 	  cC = pic824[cyOff + x1];   /* left/right center pixel */
163 	  cD = pic824[cyOff + cx];   /* center pixel */
164 	}
165 
166 	/* quick check */
167 	if (!is24 && cA == cB && cB == cC && cC == cD) {
168 	  /* set this pixel to the same color as in pic8 */
169 	  *pp++ = rmap[cD];  *pp++ = gmap[cD];  *pp++ = bmap[cD];
170 	}
171 
172 	else {
173 	  /* compute weighting factors */
174 	  apx = abs(px);  apy = abs(py);
175 	  pA = (apx * apy) >> 7; /* div 128 */
176 	  pB = (apy * (128 - apx)) >> 7; /* div 128 */
177 	  pC = (apx * (128 - apy)) >> 7; /* div 128 */
178 	  pD = 128 - (pA + pB + pC);
179 
180 	  if (is24) {
181 	    *pp++ = (((int) (pA * rA))>>7) + (((int) (pB * rB))>>7) +
182 	            (((int) (pC * rC))>>7) + (((int) (pD * rD))>>7);
183 
184 	    *pp++ = (((int) (pA * gA))>>7) + (((int) (pB * gB))>>7) +
185 	            (((int) (pC * gC))>>7) + (((int) (pD * gD))>>7);
186 
187 	    *pp++ = (((int) (pA * bA))>>7) + (((int) (pB * bB))>>7) +
188 	            (((int) (pC * bC))>>7) + (((int) (pD * bD))>>7);
189 	  }
190 	  else {  /* 8-bit pic */
191 	    *pp++ = (((int)(pA * rmap[cA]))>>7) + (((int)(pB * rmap[cB]))>>7) +
192 	            (((int)(pC * rmap[cC]))>>7) + (((int)(pD * rmap[cD]))>>7);
193 
194 	    *pp++ = (((int)(pA * gmap[cA]))>>7) + (((int)(pB * gmap[cB]))>>7) +
195 	            (((int)(pC * gmap[cC]))>>7) + (((int)(pD * gmap[cD]))>>7);
196 
197 	    *pp++ = (((int)(pA * bmap[cA]))>>7) + (((int)(pB * bmap[cB]))>>7) +
198   	            (((int)(pC * bmap[cC]))>>7) + (((int)(pD * bmap[cD]))>>7);
199 	  }
200 	}
201       }
202     }
203 
204     free(cxtab);
205     free(pxtab);
206     retval = 0;    /* okay */
207   }
208 
209   if (retval) {    /* one of the Smooth**() methods failed */
210     free(pic24);
211     pic24 = (byte *) NULL;
212   }
213 
214   return pic24;
215 }
216 
217 
218 
219 
220 /***************************************************/
smoothX(pic24,pic824,is24,swide,shigh,dwide,dhigh,rmap,gmap,bmap)221 static int smoothX(pic24, pic824, is24, swide, shigh, dwide, dhigh,
222 		   rmap, gmap, bmap)
223 byte *pic24, *pic824, *rmap, *gmap, *bmap;
224 int   is24, swide, shigh, dwide, dhigh;
225 {
226   byte *cptr, *cptr1;
227   int  i, j;
228   int  *lbufR, *lbufG, *lbufB;
229   int  pixR, pixG, pixB, bperpix;
230   int  pcnt0, pcnt1, lastpix, pixcnt, thisline, ypcnt;
231   int  *pixarr, *paptr;
232 
233   /* returns '0' if okay, '1' if failed (malloc) */
234 
235   /* for case where pic8 is shrunk horizontally and stretched vertically
236      maps pic8 into an dwide * dhigh 24-bit picture.  Only works correctly
237      when swide>=dwide and shigh<=dhigh */
238 
239 
240   /* malloc some arrays */
241   lbufR  = (int *) calloc((size_t) swide,   sizeof(int));
242   lbufG  = (int *) calloc((size_t) swide,   sizeof(int));
243   lbufB  = (int *) calloc((size_t) swide,   sizeof(int));
244   pixarr = (int *) calloc((size_t) swide+1, sizeof(int));
245 
246   if (!lbufR || !lbufG || !lbufB || !pixarr) {
247     if (lbufR)  free(lbufR);
248     if (lbufG)  free(lbufG);
249     if (lbufB)  free(lbufB);
250     if (pixarr) free(pixarr);
251     return 1;
252   }
253 
254   bperpix = (is24) ? 3 : 1;
255 
256   for (j=0; j<=swide; j++)
257     pixarr[j] = (j*dwide + (15*swide)/16) / swide;
258 
259   cptr = pic824;  cptr1 = cptr + swide * bperpix;
260 
261   for (i=0; i<dhigh; i++) {
262     ProgressMeter(0, (dhigh)-1, i, "Smooth");
263     if ((i&15) == 0) WaitCursor();
264 
265     ypcnt = (((i*shigh)<<6) / dhigh) - 32;
266     if (ypcnt<0) ypcnt = 0;
267 
268     pcnt1 = ypcnt & 0x3f;                     /* 64ths of NEXT line to use */
269     pcnt0 = 64 - pcnt1;                       /* 64ths of THIS line to use */
270 
271     thisline = ypcnt>>6;
272 
273     cptr  = pic824 + thisline * swide * bperpix;
274     if (thisline+1 < shigh) cptr1 = cptr + swide * bperpix;
275     else cptr1 = cptr;
276 
277     if (is24) {
278       for (j=0; j<swide; j++) {
279 	lbufR[j] = ((int) ((*cptr++ * pcnt0) + (*cptr1++ * pcnt1))) >> 6;
280 	lbufG[j] = ((int) ((*cptr++ * pcnt0) + (*cptr1++ * pcnt1))) >> 6;
281 	lbufB[j] = ((int) ((*cptr++ * pcnt0) + (*cptr1++ * pcnt1))) >> 6;
282       }
283     }
284     else {  /* 8-bit input pic */
285       for (j=0; j<swide; j++, cptr++, cptr1++) {
286 	lbufR[j] = ((int)((rmap[*cptr]* pcnt0) + (rmap[*cptr1]* pcnt1))) >> 6;
287 	lbufG[j] = ((int)((gmap[*cptr]* pcnt0) + (gmap[*cptr1]* pcnt1))) >> 6;
288 	lbufB[j] = ((int)((bmap[*cptr]* pcnt0) + (bmap[*cptr1]* pcnt1))) >> 6;
289       }
290     }
291 
292     pixR = pixG = pixB = pixcnt = lastpix = 0;
293 
294     for (j=0, paptr=pixarr; j<=swide; j++,paptr++) {
295       if (*paptr != lastpix) {   /* write a pixel to pic24 */
296 	if (!pixcnt) pixcnt = 1;    /* this NEVER happens:  quiets compilers */
297 	*pic24++ = pixR / pixcnt;
298 	*pic24++ = pixG / pixcnt;
299 	*pic24++ = pixB / pixcnt;
300 	lastpix = *paptr;
301 	pixR = pixG = pixB = pixcnt = 0;
302       }
303 
304       if (j<swide) {
305 	pixR += lbufR[j];
306 	pixG += lbufG[j];
307 	pixB += lbufB[j];
308 	pixcnt++;
309       }
310     }
311   }
312 
313   free(lbufR);  free(lbufG);  free(lbufB);  free(pixarr);
314   return 0;
315 }
316 
317 
318 
319 
320 
321 
322 /***************************************************/
smoothY(pic24,pic824,is24,swide,shigh,dwide,dhigh,rmap,gmap,bmap)323 static int smoothY(pic24, pic824, is24, swide, shigh, dwide, dhigh,
324 		   rmap, gmap, bmap)
325 byte *pic24, *pic824, *rmap, *gmap, *bmap;
326 int   is24, swide, shigh, dwide, dhigh;
327 {
328   byte *clptr, *cptr, *cptr1;
329   int  i, j, bperpix;
330   int  *lbufR, *lbufG, *lbufB, *pct0, *pct1, *cxarr, *cxptr;
331   int  lastline, thisline, linecnt;
332   int  retval;
333 
334 
335   /* returns '0' if okay, '1' if failed (malloc) */
336 
337   /* for case where pic8 is shrunk vertically and stretched horizontally
338      maps pic8 into a dwide * dhigh 24-bit picture.  Only works correctly
339      when swide<=dwide and shigh>=dhigh */
340 
341   retval = 0;   /* no probs, yet... */
342 
343   bperpix = (is24) ? 3 : 1;
344 
345   lbufR = lbufG = lbufB = pct0 = pct1 = cxarr = NULL;
346   lbufR = (int *) calloc((size_t) dwide, sizeof(int));
347   lbufG = (int *) calloc((size_t) dwide, sizeof(int));
348   lbufB = (int *) calloc((size_t) dwide, sizeof(int));
349   pct0  = (int *) calloc((size_t) dwide, sizeof(int));
350   pct1  = (int *) calloc((size_t) dwide, sizeof(int));
351   cxarr = (int *) calloc((size_t) dwide, sizeof(int));
352 
353   if (!lbufR || !lbufG || !lbufB || !pct0 || ! pct1 || !cxarr) {
354     retval = 1;
355     goto smyexit;
356   }
357 
358 
359 
360   for (i=0; i<dwide; i++) {                /* precompute some handy tables */
361     int cx64;
362     cx64 = (((i * swide) << 6) / dwide) - 32;
363     if (cx64<0) cx64 = 0;
364     pct1[i] = cx64 & 0x3f;
365     pct0[i] = 64 - pct1[i];
366     cxarr[i] = cx64 >> 6;
367   }
368 
369 
370   lastline = linecnt = 0;
371 
372   for (i=0, clptr=pic824; i<=shigh; i++, clptr+=swide*bperpix) {
373     ProgressMeter(0, shigh, i, "Smooth");
374     if ((i&15) == 0) WaitCursor();
375 
376     thisline = (i * dhigh + (15*shigh)/16) / shigh;
377 
378     if (thisline != lastline) {  /* copy a line to pic24 */
379       for (j=0; j<dwide; j++) {
380 	*pic24++ = lbufR[j] / linecnt;
381 	*pic24++ = lbufG[j] / linecnt;
382 	*pic24++ = lbufB[j] / linecnt;
383       }
384 
385       xvbzero( (char *) lbufR, dwide * sizeof(int));  /* clear out line bufs */
386       xvbzero( (char *) lbufG, dwide * sizeof(int));
387       xvbzero( (char *) lbufB, dwide * sizeof(int));
388       linecnt = 0;  lastline = thisline;
389     }
390 
391 
392     for (j=0, cxptr=cxarr; j<dwide; j++, cxptr++) {
393       cptr  = clptr + *cxptr * bperpix;
394       if (*cxptr < swide-1) cptr1 = cptr + 1*bperpix;
395                        else cptr1 = cptr;
396 
397       if (is24) {
398 	lbufR[j] += ((int)((*cptr++ * pct0[j]) + (*cptr1++ * pct1[j]))) >> 6;
399 	lbufG[j] += ((int)((*cptr++ * pct0[j]) + (*cptr1++ * pct1[j]))) >> 6;
400 	lbufB[j] += ((int)((*cptr++ * pct0[j]) + (*cptr1++ * pct1[j]))) >> 6;
401       }
402       else {  /* 8-bit input pic */
403 	lbufR[j] += ((int)((rmap[*cptr]*pct0[j])+(rmap[*cptr1]*pct1[j]))) >> 6;
404 	lbufG[j] += ((int)((gmap[*cptr]*pct0[j])+(gmap[*cptr1]*pct1[j]))) >> 6;
405 	lbufB[j] += ((int)((bmap[*cptr]*pct0[j])+(bmap[*cptr1]*pct1[j]))) >> 6;
406       }
407     }
408 
409     linecnt++;
410   }
411 
412 
413  smyexit:
414   if (lbufR) free(lbufR);
415   if (lbufG) free(lbufG);
416   if (lbufB) free(lbufB);
417   if (pct0)  free(pct0);
418   if (pct1)  free(pct1);
419   if (cxarr) free(cxarr);
420 
421   return retval;
422 }
423 
424 
425 
426 
427 
428 
429 /***************************************************/
smoothXY(pic24,pic824,is24,swide,shigh,dwide,dhigh,rmap,gmap,bmap)430 static int smoothXY(pic24, pic824, is24, swide, shigh, dwide, dhigh,
431 		    rmap, gmap, bmap)
432 byte *pic24, *pic824, *rmap, *gmap, *bmap;
433 int   is24, swide, shigh, dwide, dhigh;
434 {
435   byte *cptr;
436   int  i,j;
437   int  *lbufR, *lbufG, *lbufB;
438   int  pixR, pixG, pixB, bperpix;
439   int  lastline, thisline, lastpix, linecnt, pixcnt;
440   int  *pixarr, *paptr;
441 
442 
443   /* returns '0' if okay, '1' if failed (malloc) */
444 
445   /* shrinks pic8 into a dwide * dhigh 24-bit picture.  Only works correctly
446      when swide>=dwide and shigh>=dhigh (ie, the picture is shrunk on both
447      axes) */
448 
449 
450   /* malloc some arrays */
451   lbufR  = (int *) calloc((size_t) swide,   sizeof(int));
452   lbufG  = (int *) calloc((size_t) swide,   sizeof(int));
453   lbufB  = (int *) calloc((size_t) swide,   sizeof(int));
454   pixarr = (int *) calloc((size_t) swide+1, sizeof(int));
455   if (!lbufR || !lbufG || !lbufB || !pixarr) {
456     if (lbufR)  free(lbufR);
457     if (lbufG)  free(lbufG);
458     if (lbufB)  free(lbufB);
459     if (pixarr) free(pixarr);
460     return 1;
461   }
462 
463   bperpix = (is24) ? 3 : 1;
464 
465   for (j=0; j<=swide; j++)
466     pixarr[j] = (j*dwide + (15*swide)/16) / swide;
467 
468   lastline = linecnt = pixR = pixG = pixB = 0;
469   cptr = pic824;
470 
471   for (i=0; i<=shigh; i++) {
472     ProgressMeter(0, shigh, i, "Smooth");
473     if ((i&15) == 0) WaitCursor();
474 
475     thisline = (i * dhigh + (15*shigh)/16 ) / shigh;
476 
477     if ((thisline != lastline)) {      /* copy a line to pic24 */
478       pixR = pixG = pixB = pixcnt = lastpix = 0;
479 
480       for (j=0, paptr=pixarr; j<=swide; j++,paptr++) {
481 	if (*paptr != lastpix) {                 /* write a pixel to pic24 */
482 	  if (!pixcnt) pixcnt = 1;    /* NEVER happens: quiets compilers */
483 	  *pic24++ = (pixR/linecnt) / pixcnt;
484 	  *pic24++ = (pixG/linecnt) / pixcnt;
485 	  *pic24++ = (pixB/linecnt) / pixcnt;
486 	  lastpix = *paptr;
487 	  pixR = pixG = pixB = pixcnt = 0;
488 	}
489 
490 	if (j<swide) {
491 	  pixR += lbufR[j];
492 	  pixG += lbufG[j];
493 	  pixB += lbufB[j];
494 	  pixcnt++;
495 	}
496       }
497 
498       lastline = thisline;
499       xvbzero( (char *) lbufR, swide * sizeof(int));  /* clear out line bufs */
500       xvbzero( (char *) lbufG, swide * sizeof(int));
501       xvbzero( (char *) lbufB, swide * sizeof(int));
502       linecnt = 0;
503     }
504 
505     if (i<shigh) {
506       if (is24) {
507 	for (j=0; j<swide; j++) {
508 	  lbufR[j] += *cptr++;
509 	  lbufG[j] += *cptr++;
510 	  lbufB[j] += *cptr++;
511 	}
512       }
513       else {
514 	for (j=0; j<swide; j++, cptr++) {
515 	  lbufR[j] += rmap[*cptr];
516 	  lbufG[j] += gmap[*cptr];
517 	  lbufB[j] += bmap[*cptr];
518 	}
519       }
520 
521       linecnt++;
522     }
523   }
524 
525   free(lbufR);  free(lbufG);  free(lbufB);  free(pixarr);
526   return 0;
527 }
528 
529 
530 
531 
532 /********************************************/
DoColorDither(pic24,pic8,w,h,rmap,gmap,bmap,rdisp,gdisp,bdisp,maplen)533 byte *DoColorDither(pic24, pic8, w, h, rmap, gmap, bmap,
534 		    rdisp, gdisp, bdisp, maplen)
535      byte *pic24, *pic8, *rmap, *gmap, *bmap, *rdisp, *gdisp, *bdisp;
536      int   w, h, maplen;
537 {
538   /* takes a 24 bit picture, of size w*h, dithers with the colors in
539      rdisp, gdisp, bdisp (which have already been allocated),
540      and generates an 8-bit w*h image, which it returns.
541      ignores input value 'pic8'
542      returns NULL on error
543 
544      note: the rdisp,gdisp,bdisp arrays should be the 'displayed' colors,
545      not the 'desired' colors
546 
547      if pic24 is NULL, uses the passed-in pic8 (an 8-bit image) as
548      the source, and the rmap,gmap,bmap arrays as the desired colors */
549 
550   byte *np, *ep, *newpic;
551   short *cache;
552   int r2, g2, b2;
553   int *thisline, *nextline, *thisptr, *nextptr, *tmpptr;
554   int  i, j, rerr, gerr, berr, pwide3;
555   int  imax, jmax;
556   int key;
557   long cnt1, cnt2;
558   int fserrmap[512];   /* -255 .. 0 .. +255 */
559 
560   /* compute somewhat non-linear floyd-steinberg error mapping table */
561   for (i=j=0; i<=0x40; i++,j++)
562     { fserrmap[256+i] = j;  fserrmap[256-i] = -j; }
563   for (     ; i<0x80; i++, j += !(i&1) ? 1 : 0)
564     { fserrmap[256+i] = j;  fserrmap[256-i] = -j; }
565   for (     ; i<=0xff; i++)
566     { fserrmap[256+i] = j;  fserrmap[256-i] = -j; }
567 
568 
569   cnt1 = cnt2 = 0;
570   pwide3 = w*3;  imax = h-1;  jmax = w-1;
571   ep = (pic24) ? pic24 : pic8;
572 
573   /* attempt to malloc things */
574   newpic = (byte *)  malloc((size_t) (w * h));
575   cache  = (short *) calloc((size_t) (2<<14), sizeof(short));
576   thisline = (int *) malloc(pwide3 * sizeof(int));
577   nextline = (int *) malloc(pwide3 * sizeof(int));
578   if (!cache || !newpic || !thisline || !nextline) {
579     if (newpic)   free(newpic);
580     if (cache)    free(cache);
581     if (thisline) free(thisline);
582     if (nextline) free(nextline);
583 
584     return (byte *) NULL;
585   }
586 
587   np = newpic;
588 
589   /* get first line of picture */
590 
591   if (pic24) {
592     for (j=pwide3, tmpptr=nextline; j; j--, ep++)
593       *tmpptr++ = (int) *ep;
594   }
595   else {
596     for (j=w, tmpptr=nextline; j; j--, ep++) {
597       *tmpptr++ = (int) rmap[*ep];
598       *tmpptr++ = (int) gmap[*ep];
599       *tmpptr++ = (int) bmap[*ep];
600     }
601   }
602 
603 
604   for (i=0; i<h; i++) {
605     ProgressMeter(0, h-1, i, "Dither");
606     if ((i&15) == 0) WaitCursor();
607 
608     tmpptr = thisline;  thisline = nextline;  nextline = tmpptr;   /* swap */
609 
610     if (i!=imax) {  /* get next line */
611       if (!pic24)
612 	for (j=w, tmpptr=nextline; j; j--, ep++) {
613 	  *tmpptr++ = (int) rmap[*ep];
614 	  *tmpptr++ = (int) gmap[*ep];
615 	  *tmpptr++ = (int) bmap[*ep];
616 	}
617       else
618 	for (j=pwide3, tmpptr=nextline; j; j--, ep++) *tmpptr++ = (int) *ep;
619     }
620 
621     /* dither a line */
622     for (j=0, thisptr=thisline, nextptr=nextline; j<w; j++,np++) {
623       int k, d, mind, closest;
624 
625       r2 = *thisptr++;  g2 = *thisptr++;  b2 = *thisptr++;
626 
627       /* map r2,g2,b2 components (could be outside 0..255 range)
628 	 into 0..255 range */
629 
630       if (r2<0 || g2<0 || b2<0) {   /* are there any negatives in RGB? */
631 	if (r2<g2) { if (r2<b2) k = 0; else k = 2; }
632 	else { if (g2<b2) k = 1; else k = 2; }
633 
634 	switch (k) {
635 	case 0:  g2 -= r2;  b2 -= r2;  d = (abs(r2) * 3) / 2;    /* RED */
636 	         r2 = 0;
637 	         g2 = (g2>d) ? g2 - d : 0;
638 	         b2 = (b2>d) ? b2 - d : 0;
639 	         break;
640 
641 	case 1:  r2 -= g2;  b2 -= g2;  d = (abs(g2) * 3) / 2;    /* GREEN */
642 	         r2 = (r2>d) ? r2 - d : 0;
643 	         g2 = 0;
644 	         b2 = (b2>d) ? b2 - d : 0;
645 	         break;
646 
647 	case 2:  r2 -= b2;  g2 -= b2;  d = (abs(b2) * 3) / 2;    /* BLUE */
648 	         r2 = (r2>d) ? r2 - d : 0;
649 	         g2 = (g2>d) ? g2 - d : 0;
650 	         b2 = 0;
651 	         break;
652 	}
653       }
654 
655       if (r2>255 || g2>255 || b2>255) {   /* any overflows in RGB? */
656 	if (r2>g2) { if (r2>b2) k = 0; else k = 2; }
657               else { if (g2>b2) k = 1; else k = 2; }
658 
659 	switch (k) {
660 	case 0:   g2 = (g2*255)/r2;  b2 = (b2*255)/r2;  r2=255;  break;
661 	case 1:   r2 = (r2*255)/g2;  b2 = (b2*255)/g2;  g2=255;  break;
662 	case 2:   r2 = (r2*255)/b2;  g2 = (g2*255)/b2;  b2=255;  break;
663 	}
664       }
665 
666       key = ((r2&0xf8)<<6) | ((g2&0xf8)<<1) | (b2>>4);
667       if (key >= (2<<14)) FatalError("'key' overflow in DoColorDither()");
668 
669       if (cache[key]) { *np = (byte) (cache[key] - 1);  cnt1++;	}
670       else {
671 	/* not in cache, have to search the colortable */
672 	cnt2++;
673 
674         mind = 10000;
675 	for (k=closest=0; k<maplen && mind>7; k++) {
676 	  d = abs(r2 - rdisp[k])
677 	    + abs(g2 - gdisp[k])
678 	    + abs(b2 - bdisp[k]);
679 	  if (d<mind) { mind = d;  closest = k; }
680 	}
681 	cache[key] = closest + 1;
682 	*np = closest;
683       }
684 
685 
686       /* propogate the error */
687       rerr = r2 - rdisp[*np];
688       gerr = g2 - gdisp[*np];
689       berr = b2 - bdisp[*np];
690 
691 
692       RANGE(rerr, -255, 255);
693       RANGE(gerr, -255, 255);
694       RANGE(berr, -255, 255);
695       rerr = fserrmap[256+rerr];
696       gerr = fserrmap[256+gerr];
697       berr = fserrmap[256+berr];
698 
699 
700 
701       if (j!=jmax) {  /* adjust RIGHT pixel */
702 	thisptr[0] += (rerr*7)/16;
703 	thisptr[1] += (gerr*7)/16;
704 	thisptr[2] += (berr*7)/16;
705       }
706 
707       if (i!=imax) {	/* do BOTTOM pixel */
708 	nextptr[0] += (rerr*5)/16;
709 	nextptr[1] += (gerr*5)/16;
710 	nextptr[2] += (berr*5)/16;
711 
712 	if (j>0) {  /* do BOTTOM LEFT pixel */
713 	  nextptr[-3] += (rerr*3)/16;
714 	  nextptr[-2] += (gerr*3)/16;
715 	  nextptr[-1] += (berr*3)/16;
716 	}
717 
718 	if (j!=jmax) {  /* do BOTTOM RIGHT pixel */
719 	  nextptr[3] += rerr/16;
720 	  nextptr[4] += gerr/16;
721 	  nextptr[5] += berr/16;
722 	}
723 	nextptr += 3;
724       }
725     }
726   }
727 
728 
729   free(thisline);  free(nextline);
730   free(cache);
731 
732   return newpic;
733 }
734 
735 
736 
737 /********************************************/
Do332ColorDither(pic24,pic8,w,h,rmap,gmap,bmap,rdisp,gdisp,bdisp,maplen)738 byte *Do332ColorDither(pic24, pic8, w, h, rmap, gmap, bmap,
739 		    rdisp, gdisp, bdisp, maplen)
740      byte *pic24, *pic8, *rmap, *gmap, *bmap, *rdisp, *gdisp, *bdisp;
741      int   w, h, maplen;
742 {
743   /* some sort of color dither optimized for the 332 std cmap */
744 
745   /* takes a 24 bit picture, of size w*h, dithers with the colors in
746      rdisp, gdisp, bdisp (which have already been allocated),
747      and generates an 8-bit w*h image, which it returns.
748      ignores input value 'pic8'
749      returns NULL on error
750 
751      note: the rdisp,gdisp,bdisp arrays should be the 'displayed' colors,
752      not the 'desired' colors
753 
754      if pic24 is NULL, uses the passed-in pic8 (an 8-bit image) as
755      the source, and the rmap,gmap,bmap arrays as the desired colors */
756 
757   byte *np, *ep, *newpic;
758   int r2, g2, b2;
759   int *thisline, *nextline, *thisptr, *nextptr, *tmpptr;
760   int  i, j, rerr, gerr, berr, pwide3;
761   int  imax, jmax;
762   long cnt1, cnt2;
763   int  fserrmap[512];   /* -255 .. 0 .. +255 */
764 
765   /* compute somewhat non-linear floyd-steinberg error mapping table */
766   for (i=j=0; i<=0x40; i++,j++)
767     { fserrmap[256+i] = j;  fserrmap[256-i] = -j; }
768   for (     ; i<0x80; i++, j += !(i&1) ? 1 : 0)
769     { fserrmap[256+i] = j;  fserrmap[256-i] = -j; }
770   for (     ; i<=0xff; i++)
771     { fserrmap[256+i] = j;  fserrmap[256-i] = -j; }
772 
773 
774   cnt1 = cnt2 = 0;
775   pwide3 = w*3;  imax = h-1;  jmax = w-1;
776 
777   /* attempt to malloc things */
778   newpic   = (byte *) malloc((size_t) (w * h));
779   thisline = (int *)  malloc(pwide3 * sizeof(int));
780   nextline = (int *)  malloc(pwide3 * sizeof(int));
781   if (!newpic || !thisline || !nextline) {
782     if (newpic)   free(newpic);
783     if (thisline) free(thisline);
784     if (nextline) free(nextline);
785 
786     return (byte *) NULL;
787   }
788 
789   np = newpic;
790   ep = (pic24) ? pic24 : pic8;
791 
792 
793   /* get first line of picture */
794 
795   if (pic24) {
796     for (j=pwide3, tmpptr=nextline; j; j--, ep++) *tmpptr++ = (int) *ep;
797   }
798   else {
799     for (j=w, tmpptr=nextline; j; j--, ep++) {
800       *tmpptr++ = (int) rmap[*ep];
801       *tmpptr++ = (int) gmap[*ep];
802       *tmpptr++ = (int) bmap[*ep];
803     }
804   }
805 
806 
807   for (i=0; i<h; i++) {
808     np = newpic + i*w;
809     ProgressMeter(0, h-1, i, "Dither");
810     if ((i&127) == 0) WaitCursor();
811 
812     tmpptr = thisline;  thisline = nextline;  nextline = tmpptr;   /* swap */
813 
814     if (i!=imax) {  /* get next line */
815       if (!pic24)
816 	for (j=w, tmpptr=nextline; j; j--, ep++) {
817 	  *tmpptr++ = (int) rmap[*ep];
818 	  *tmpptr++ = (int) gmap[*ep];
819 	  *tmpptr++ = (int) bmap[*ep];
820 	}
821       else
822 	for (j=pwide3, tmpptr=nextline; j; j--, ep++) *tmpptr++ = (int) *ep;
823     }
824 
825 
826     /* dither a line, doing odd-lines right-to-left (serpentine) */
827     thisptr = (i&1) ? thisline + w*3 - 3 : thisline;
828     nextptr = (i&1) ? nextline + w*3 - 3 : nextline;
829     if (i&1) np += w-1;
830 
831 
832     for (j=0; j<w; j++) {
833       int rb,gb,bb;
834 
835       r2 = *thisptr++;  g2 = *thisptr++;  b2 = *thisptr++;
836       if (i&1) thisptr -= 6;  /* move left */
837 
838       rb = (r2 + 0x10);    /* round top 3 bits */
839       RANGE(rb,0,255);
840       rb = rb & 0xe0;
841 
842       gb = (g2 + 0x10);    /* round 3 bits */
843       RANGE(gb,0,255);
844       gb = gb & 0xe0;
845 
846       bb = (b2 + 0x20);    /* round 2 bits */
847       RANGE(bb,0,255);
848       bb = bb & 0xc0;
849 
850 
851       *np = rb | (gb>>3) | (bb>>6);
852 
853       /* propogate the error */
854       rerr = r2 - rdisp[*np];
855       gerr = g2 - gdisp[*np];
856       berr = b2 - bdisp[*np];
857 
858 
859       RANGE(rerr, -255, 255);
860       RANGE(gerr, -255, 255);
861       RANGE(berr, -255, 255);
862       rerr = fserrmap[256+rerr];
863       gerr = fserrmap[256+gerr];
864       berr = fserrmap[256+berr];
865 
866 
867       if (j!=jmax) {  /* adjust LEFT/RIGHT pixel */
868 	thisptr[0] += (rerr/2);  rerr -= (rerr/2);
869 	thisptr[1] += (gerr/2);  gerr -= (gerr/2);
870 	thisptr[2] += (berr/2);  berr -= (berr/2);
871       }
872 
873       if (i!=imax) { /* adjust BOTTOM pixel */
874 	nextptr[0] += rerr;    /* possibly all err if we're at l/r edge */
875 	nextptr[1] += gerr;
876 	nextptr[2] += berr;
877       }
878 
879       if (i&1) { nextptr -= 3;  np--; }
880           else { nextptr += 3;  np++; }
881     }
882   }
883 
884 
885   free(thisline);  free(nextline);
886 
887   return newpic;
888 }
889 
890 
891 
892