17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier #include <draw.h>
57dd7cddfSDavid du Colombier #include "imagefile.h"
67dd7cddfSDavid du Colombier
77dd7cddfSDavid du Colombier #include "rgbv.h"
87dd7cddfSDavid du Colombier #include "ycbcr.h"
97dd7cddfSDavid du Colombier
107dd7cddfSDavid du Colombier #define CLAMPOFF 128
117dd7cddfSDavid du Colombier
127dd7cddfSDavid du Colombier static int clamp[CLAMPOFF+256+CLAMPOFF];
137dd7cddfSDavid du Colombier static int inited;
147dd7cddfSDavid du Colombier
157dd7cddfSDavid du Colombier void*
_remaperror(char * fmt,...)167dd7cddfSDavid du Colombier _remaperror(char *fmt, ...)
177dd7cddfSDavid du Colombier {
187dd7cddfSDavid du Colombier va_list arg;
197dd7cddfSDavid du Colombier char buf[256];
207dd7cddfSDavid du Colombier
217dd7cddfSDavid du Colombier va_start(arg, fmt);
229a747e4fSDavid du Colombier vseprint(buf, buf+sizeof buf, fmt, arg);
237dd7cddfSDavid du Colombier va_end(arg);
247dd7cddfSDavid du Colombier
257dd7cddfSDavid du Colombier werrstr(buf);
267dd7cddfSDavid du Colombier return nil;
277dd7cddfSDavid du Colombier }
287dd7cddfSDavid du Colombier
297dd7cddfSDavid du Colombier Rawimage*
torgbv(Rawimage * i,int errdiff)307dd7cddfSDavid du Colombier torgbv(Rawimage *i, int errdiff)
317dd7cddfSDavid du Colombier {
327dd7cddfSDavid du Colombier int j, k, rgb, x, y, er, eg, eb, col, t;
337dd7cddfSDavid du Colombier int r, g, b, r1, g1, b1;
347dd7cddfSDavid du Colombier int *ered, *egrn, *eblu, *rp, *gp, *bp;
35*c35931e2SDavid du Colombier int bpc;
367dd7cddfSDavid du Colombier uint *map3;
377dd7cddfSDavid du Colombier uchar *closest;
387dd7cddfSDavid du Colombier Rawimage *im;
397dd7cddfSDavid du Colombier int dx, dy;
409a747e4fSDavid du Colombier char err[ERRMAX];
417dd7cddfSDavid du Colombier uchar *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic;
427dd7cddfSDavid du Colombier
437dd7cddfSDavid du Colombier err[0] = '\0';
449a747e4fSDavid du Colombier errstr(err, sizeof err); /* throw it away */
457dd7cddfSDavid du Colombier im = malloc(sizeof(Rawimage));
467dd7cddfSDavid du Colombier if(im == nil)
477dd7cddfSDavid du Colombier return nil;
487dd7cddfSDavid du Colombier memset(im, 0, sizeof(Rawimage));
497dd7cddfSDavid du Colombier im->chans[0] = malloc(i->chanlen);
507dd7cddfSDavid du Colombier if(im->chans[0] == nil){
517dd7cddfSDavid du Colombier free(im);
527dd7cddfSDavid du Colombier return nil;
537dd7cddfSDavid du Colombier }
547dd7cddfSDavid du Colombier im->r = i->r;
557dd7cddfSDavid du Colombier im->nchans = 1;
567dd7cddfSDavid du Colombier im->chandesc = CRGBV;
577dd7cddfSDavid du Colombier im->chanlen = i->chanlen;
587dd7cddfSDavid du Colombier
597dd7cddfSDavid du Colombier dx = i->r.max.x-i->r.min.x;
607dd7cddfSDavid du Colombier dy = i->r.max.y-i->r.min.y;
617dd7cddfSDavid du Colombier cmap = i->cmap;
627dd7cddfSDavid du Colombier
637dd7cddfSDavid du Colombier if(inited == 0){
647dd7cddfSDavid du Colombier inited = 1;
657dd7cddfSDavid du Colombier for(j=0; j<CLAMPOFF; j++)
667dd7cddfSDavid du Colombier clamp[j] = 0;
677dd7cddfSDavid du Colombier for(j=0; j<256; j++)
687dd7cddfSDavid du Colombier clamp[CLAMPOFF+j] = (j>>4);
697dd7cddfSDavid du Colombier for(j=0; j<CLAMPOFF; j++)
707dd7cddfSDavid du Colombier clamp[CLAMPOFF+256+j] = (255>>4);
717dd7cddfSDavid du Colombier }
727dd7cddfSDavid du Colombier
737dd7cddfSDavid du Colombier in = i->chans[0];
747dd7cddfSDavid du Colombier inp = in;
757dd7cddfSDavid du Colombier out = im->chans[0];
767dd7cddfSDavid du Colombier outp = out;
777dd7cddfSDavid du Colombier
787dd7cddfSDavid du Colombier ered = malloc((dx+1)*sizeof(int));
797dd7cddfSDavid du Colombier egrn = malloc((dx+1)*sizeof(int));
807dd7cddfSDavid du Colombier eblu = malloc((dx+1)*sizeof(int));
817dd7cddfSDavid du Colombier if(ered==nil || egrn==nil || eblu==nil){
827dd7cddfSDavid du Colombier free(im->chans[0]);
837dd7cddfSDavid du Colombier free(im);
847dd7cddfSDavid du Colombier free(ered);
857dd7cddfSDavid du Colombier free(egrn);
867dd7cddfSDavid du Colombier free(eblu);
877dd7cddfSDavid du Colombier return _remaperror("remap: malloc failed: %r");
887dd7cddfSDavid du Colombier }
897dd7cddfSDavid du Colombier memset(ered, 0, (dx+1)*sizeof(int));
907dd7cddfSDavid du Colombier memset(egrn, 0, (dx+1)*sizeof(int));
917dd7cddfSDavid du Colombier memset(eblu, 0, (dx+1)*sizeof(int));
927dd7cddfSDavid du Colombier
937dd7cddfSDavid du Colombier switch(i->chandesc){
947dd7cddfSDavid du Colombier default:
957dd7cddfSDavid du Colombier return _remaperror("remap: can't recognize channel type %d", i->chandesc);
967dd7cddfSDavid du Colombier case CRGB1:
977dd7cddfSDavid du Colombier if(cmap == nil)
987dd7cddfSDavid du Colombier return _remaperror("remap: image has no color map");
997dd7cddfSDavid du Colombier if(i->nchans != 1)
1007dd7cddfSDavid du Colombier return _remaperror("remap: can't handle nchans %d", i->nchans);
1017dd7cddfSDavid du Colombier for(j=1; j<=8; j++)
1027dd7cddfSDavid du Colombier if(i->cmaplen == 3*(1<<j))
1037dd7cddfSDavid du Colombier break;
1047dd7cddfSDavid du Colombier if(j > 8)
1057dd7cddfSDavid du Colombier return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3);
1067dd7cddfSDavid du Colombier if(i->cmaplen != 3*256){
1077dd7cddfSDavid du Colombier /* to avoid a range check in inner loop below, make a full-size cmap */
1087dd7cddfSDavid du Colombier memmove(cmap1, cmap, i->cmaplen);
1097dd7cddfSDavid du Colombier cmap = cmap1;
1107dd7cddfSDavid du Colombier }
1117dd7cddfSDavid du Colombier if(errdiff == 0){
1127dd7cddfSDavid du Colombier k = 0;
1137dd7cddfSDavid du Colombier for(j=0; j<256; j++){
1147dd7cddfSDavid du Colombier r = cmap[k]>>4;
1157dd7cddfSDavid du Colombier g = cmap[k+1]>>4;
1167dd7cddfSDavid du Colombier b = cmap[k+2]>>4;
1177dd7cddfSDavid du Colombier k += 3;
1187dd7cddfSDavid du Colombier map[j] = closestrgb[b+16*(g+16*r)];
1197dd7cddfSDavid du Colombier }
1207dd7cddfSDavid du Colombier for(j=0; j<i->chanlen; j++)
1217dd7cddfSDavid du Colombier out[j] = map[in[j]];
1227dd7cddfSDavid du Colombier }else{
1237dd7cddfSDavid du Colombier /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
1247dd7cddfSDavid du Colombier for(y=0; y<dy; y++){
1257dd7cddfSDavid du Colombier er = 0;
1267dd7cddfSDavid du Colombier eg = 0;
1277dd7cddfSDavid du Colombier eb = 0;
1287dd7cddfSDavid du Colombier rp = ered;
1297dd7cddfSDavid du Colombier gp = egrn;
1307dd7cddfSDavid du Colombier bp = eblu;
1317dd7cddfSDavid du Colombier for(x=0; x<dx; x++){
1327dd7cddfSDavid du Colombier cm = &cmap[3 * *inp++];
1337dd7cddfSDavid du Colombier r = cm[0] +*rp;
1347dd7cddfSDavid du Colombier g = cm[1] +*gp;
1357dd7cddfSDavid du Colombier b = cm[2] +*bp;
136e288d156SDavid du Colombier
137e288d156SDavid du Colombier /* sanity checks are new */
138e288d156SDavid du Colombier if(r >= 256+CLAMPOFF)
139e288d156SDavid du Colombier r = 0;
140e288d156SDavid du Colombier if(g >= 256+CLAMPOFF)
141e288d156SDavid du Colombier g = 0;
142e288d156SDavid du Colombier if(b >= 256+CLAMPOFF)
143e288d156SDavid du Colombier b = 0;
1447dd7cddfSDavid du Colombier r1 = clamp[r+CLAMPOFF];
1457dd7cddfSDavid du Colombier g1 = clamp[g+CLAMPOFF];
1467dd7cddfSDavid du Colombier b1 = clamp[b+CLAMPOFF];
147e288d156SDavid du Colombier if(r1 >= 16 || g1 >= 16 || b1 >= 16)
148e288d156SDavid du Colombier col = 0;
149e288d156SDavid du Colombier else
1507dd7cddfSDavid du Colombier col = closestrgb[b1+16*(g1+16*r1)];
1517dd7cddfSDavid du Colombier *outp++ = col;
1527dd7cddfSDavid du Colombier
1537dd7cddfSDavid du Colombier rgb = rgbmap[col];
1547dd7cddfSDavid du Colombier r -= (rgb>>16) & 0xFF;
1557dd7cddfSDavid du Colombier t = (3*r)>>4;
1567dd7cddfSDavid du Colombier *rp++ = t+er;
1577dd7cddfSDavid du Colombier *rp += t;
1587dd7cddfSDavid du Colombier er = r-3*t;
1597dd7cddfSDavid du Colombier
1607dd7cddfSDavid du Colombier g -= (rgb>>8) & 0xFF;
1617dd7cddfSDavid du Colombier t = (3*g)>>4;
1627dd7cddfSDavid du Colombier *gp++ = t+eg;
1637dd7cddfSDavid du Colombier *gp += t;
1647dd7cddfSDavid du Colombier eg = g-3*t;
1657dd7cddfSDavid du Colombier
1667dd7cddfSDavid du Colombier b -= rgb & 0xFF;
1677dd7cddfSDavid du Colombier t = (3*b)>>4;
1687dd7cddfSDavid du Colombier *bp++ = t+eb;
1697dd7cddfSDavid du Colombier *bp += t;
1707dd7cddfSDavid du Colombier eb = b-3*t;
1717dd7cddfSDavid du Colombier }
1727dd7cddfSDavid du Colombier }
1737dd7cddfSDavid du Colombier }
1747dd7cddfSDavid du Colombier break;
1757dd7cddfSDavid du Colombier
1767dd7cddfSDavid du Colombier case CYCbCr:
177*c35931e2SDavid du Colombier bpc = 1;
178*c35931e2SDavid du Colombier rpic = i->chans[0];
179*c35931e2SDavid du Colombier gpic = i->chans[1];
180*c35931e2SDavid du Colombier bpic = i->chans[2];
1817dd7cddfSDavid du Colombier closest = closestycbcr;
1827dd7cddfSDavid du Colombier map3 = ycbcrmap;
183*c35931e2SDavid du Colombier if(i->nchans != 3)
184*c35931e2SDavid du Colombier return _remaperror("remap: RGB image has %d channels", i->nchans);
1857dd7cddfSDavid du Colombier goto Threecolor;
1867dd7cddfSDavid du Colombier
1877dd7cddfSDavid du Colombier case CRGB:
188*c35931e2SDavid du Colombier bpc = 1;
189*c35931e2SDavid du Colombier rpic = i->chans[0];
190*c35931e2SDavid du Colombier gpic = i->chans[1];
191*c35931e2SDavid du Colombier bpic = i->chans[2];
192*c35931e2SDavid du Colombier if(i->nchans != 3)
193*c35931e2SDavid du Colombier return _remaperror("remap: RGB image has %d channels", i->nchans);
194*c35931e2SDavid du Colombier goto rgbgen;
195*c35931e2SDavid du Colombier
196*c35931e2SDavid du Colombier case CRGB24:
197*c35931e2SDavid du Colombier bpc = 3;
198*c35931e2SDavid du Colombier bpic = i->chans[0];
199*c35931e2SDavid du Colombier gpic = i->chans[0] + 1;
200*c35931e2SDavid du Colombier rpic = i->chans[0] + 2;
201*c35931e2SDavid du Colombier goto rgbgen;
202*c35931e2SDavid du Colombier
203*c35931e2SDavid du Colombier case CRGBA32:
204*c35931e2SDavid du Colombier bpc = 4;
205*c35931e2SDavid du Colombier /* i->chans[0]+0 is alpha */
206*c35931e2SDavid du Colombier bpic = i->chans[0] + 1;
207*c35931e2SDavid du Colombier gpic = i->chans[0] + 2;
208*c35931e2SDavid du Colombier rpic = i->chans[0] + 3;
209*c35931e2SDavid du Colombier
210*c35931e2SDavid du Colombier rgbgen:
2117dd7cddfSDavid du Colombier closest = closestrgb;
2127dd7cddfSDavid du Colombier map3 = rgbmap;
2137dd7cddfSDavid du Colombier
2147dd7cddfSDavid du Colombier Threecolor:
215*c35931e2SDavid du Colombier
2167dd7cddfSDavid du Colombier if(errdiff == 0){
217*c35931e2SDavid du Colombier outp = out;
218*c35931e2SDavid du Colombier for(j=0; j<i->chanlen; j+=bpc){
2197dd7cddfSDavid du Colombier r = rpic[j]>>4;
2207dd7cddfSDavid du Colombier g = gpic[j]>>4;
2217dd7cddfSDavid du Colombier b = bpic[j]>>4;
222*c35931e2SDavid du Colombier *outp++ = closest[b+16*(g+16*r)];
2237dd7cddfSDavid du Colombier }
2247dd7cddfSDavid du Colombier }else{
2257dd7cddfSDavid du Colombier /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
2267dd7cddfSDavid du Colombier for(y=0; y<dy; y++){
2277dd7cddfSDavid du Colombier er = 0;
2287dd7cddfSDavid du Colombier eg = 0;
2297dd7cddfSDavid du Colombier eb = 0;
2307dd7cddfSDavid du Colombier rp = ered;
2317dd7cddfSDavid du Colombier gp = egrn;
2327dd7cddfSDavid du Colombier bp = eblu;
2337dd7cddfSDavid du Colombier for(x=0; x<dx; x++){
234*c35931e2SDavid du Colombier r = *rpic + *rp;
235*c35931e2SDavid du Colombier g = *gpic + *gp;
236*c35931e2SDavid du Colombier b = *bpic + *bp;
237*c35931e2SDavid du Colombier rpic += bpc;
238*c35931e2SDavid du Colombier gpic += bpc;
239*c35931e2SDavid du Colombier bpic += bpc;
2407dd7cddfSDavid du Colombier /*
2417dd7cddfSDavid du Colombier * Errors can be uncorrectable if converting from YCbCr,
2427dd7cddfSDavid du Colombier * since we can't guarantee that an extremal value of one of
2437dd7cddfSDavid du Colombier * the components selects a color with an extremal value.
2447dd7cddfSDavid du Colombier * If we don't, the errors accumulate without bound. This
2457dd7cddfSDavid du Colombier * doesn't happen in RGB because the closest table can guarantee
2467dd7cddfSDavid du Colombier * a color on the edge of the gamut, producing a zero error in
2477dd7cddfSDavid du Colombier * that component. For the rotation YCbCr space, there may be
2487dd7cddfSDavid du Colombier * no color that can guarantee zero error at the edge.
2497dd7cddfSDavid du Colombier * Therefore we must clamp explicitly rather than by assuming
2507dd7cddfSDavid du Colombier * an upper error bound of CLAMPOFF. The performance difference
2517dd7cddfSDavid du Colombier * is miniscule anyway.
2527dd7cddfSDavid du Colombier */
2537dd7cddfSDavid du Colombier if(r < 0)
2547dd7cddfSDavid du Colombier r = 0;
2557dd7cddfSDavid du Colombier else if(r > 255)
2567dd7cddfSDavid du Colombier r = 255;
2577dd7cddfSDavid du Colombier if(g < 0)
2587dd7cddfSDavid du Colombier g = 0;
2597dd7cddfSDavid du Colombier else if(g > 255)
2607dd7cddfSDavid du Colombier g = 255;
2617dd7cddfSDavid du Colombier if(b < 0)
2627dd7cddfSDavid du Colombier b = 0;
2637dd7cddfSDavid du Colombier else if(b > 255)
2647dd7cddfSDavid du Colombier b = 255;
2657dd7cddfSDavid du Colombier r1 = r>>4;
2667dd7cddfSDavid du Colombier g1 = g>>4;
2677dd7cddfSDavid du Colombier b1 = b>>4;
2687dd7cddfSDavid du Colombier col = closest[b1+16*(g1+16*r1)];
2697dd7cddfSDavid du Colombier *outp++ = col;
2707dd7cddfSDavid du Colombier
2717dd7cddfSDavid du Colombier rgb = map3[col];
2727dd7cddfSDavid du Colombier r -= (rgb>>16) & 0xFF;
2737dd7cddfSDavid du Colombier t = (3*r)>>4;
2747dd7cddfSDavid du Colombier *rp++ = t+er;
2757dd7cddfSDavid du Colombier *rp += t;
2767dd7cddfSDavid du Colombier er = r-3*t;
2777dd7cddfSDavid du Colombier
2787dd7cddfSDavid du Colombier g -= (rgb>>8) & 0xFF;
2797dd7cddfSDavid du Colombier t = (3*g)>>4;
2807dd7cddfSDavid du Colombier *gp++ = t+eg;
2817dd7cddfSDavid du Colombier *gp += t;
2827dd7cddfSDavid du Colombier eg = g-3*t;
2837dd7cddfSDavid du Colombier
2847dd7cddfSDavid du Colombier b -= rgb & 0xFF;
2857dd7cddfSDavid du Colombier t = (3*b)>>4;
2867dd7cddfSDavid du Colombier *bp++ = t+eb;
2877dd7cddfSDavid du Colombier *bp += t;
2887dd7cddfSDavid du Colombier eb = b-3*t;
2897dd7cddfSDavid du Colombier }
2907dd7cddfSDavid du Colombier }
2917dd7cddfSDavid du Colombier }
2927dd7cddfSDavid du Colombier break;
2937dd7cddfSDavid du Colombier
294*c35931e2SDavid du Colombier case CYA16:
295*c35931e2SDavid du Colombier bpc = 2;
296*c35931e2SDavid du Colombier /* i->chans[0] + 0 is alpha */
297*c35931e2SDavid du Colombier rpic = i->chans[0] + 1;
298*c35931e2SDavid du Colombier goto greygen;
299*c35931e2SDavid du Colombier
3007dd7cddfSDavid du Colombier case CY:
301*c35931e2SDavid du Colombier bpc = 1;
302*c35931e2SDavid du Colombier rpic = i->chans[0];
3037dd7cddfSDavid du Colombier if(i->nchans != 1)
3047dd7cddfSDavid du Colombier return _remaperror("remap: Y image has %d chans", i->nchans);
305*c35931e2SDavid du Colombier
306*c35931e2SDavid du Colombier greygen:
3077dd7cddfSDavid du Colombier if(errdiff == 0){
308*c35931e2SDavid du Colombier for(j=0; j<i->chanlen; j+=bpc){
3097dd7cddfSDavid du Colombier r = rpic[j]>>4;
3107dd7cddfSDavid du Colombier *outp++ = closestrgb[r+16*(r+16*r)];
3117dd7cddfSDavid du Colombier }
3127dd7cddfSDavid du Colombier }else{
3137dd7cddfSDavid du Colombier /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
3147dd7cddfSDavid du Colombier for(y=0; y<dy; y++){
3157dd7cddfSDavid du Colombier er = 0;
3167dd7cddfSDavid du Colombier rp = ered;
3177dd7cddfSDavid du Colombier for(x=0; x<dx; x++){
318*c35931e2SDavid du Colombier r = *rpic + *rp;
319*c35931e2SDavid du Colombier rpic += bpc;
3207dd7cddfSDavid du Colombier r1 = clamp[r+CLAMPOFF];
3217dd7cddfSDavid du Colombier col = closestrgb[r1+16*(r1+16*r1)];
3227dd7cddfSDavid du Colombier *outp++ = col;
3237dd7cddfSDavid du Colombier
3247dd7cddfSDavid du Colombier rgb = rgbmap[col];
3257dd7cddfSDavid du Colombier r -= (rgb>>16) & 0xFF;
3267dd7cddfSDavid du Colombier t = (3*r)>>4;
3277dd7cddfSDavid du Colombier *rp++ = t+er;
3287dd7cddfSDavid du Colombier *rp += t;
3297dd7cddfSDavid du Colombier er = r-3*t;
3307dd7cddfSDavid du Colombier }
3317dd7cddfSDavid du Colombier }
3327dd7cddfSDavid du Colombier }
3337dd7cddfSDavid du Colombier break;
3347dd7cddfSDavid du Colombier }
3357dd7cddfSDavid du Colombier free(ered);
3367dd7cddfSDavid du Colombier free(egrn);
3377dd7cddfSDavid du Colombier free(eblu);
3387dd7cddfSDavid du Colombier return im;
3397dd7cddfSDavid du Colombier }
340