torgbv.c (6443B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <draw.h> 5 #include "imagefile.h" 6 7 #include "rgbv.h" 8 #include "ycbcr.h" 9 10 #define CLAMPOFF 128 11 12 static int clamp[CLAMPOFF+256+CLAMPOFF]; 13 static int inited; 14 15 void* 16 _remaperror(char *fmt, ...) 17 { 18 va_list arg; 19 char buf[256]; 20 21 va_start(arg, fmt); 22 vseprint(buf, buf+sizeof buf, fmt, arg); 23 va_end(arg); 24 25 werrstr(buf); 26 return nil; 27 } 28 29 Rawimage* 30 torgbv(Rawimage *i, int errdiff) 31 { 32 int j, k, rgb, x, y, er, eg, eb, col, t; 33 int r, g, b, r1, g1, b1; 34 int *ered, *egrn, *eblu, *rp, *gp, *bp; 35 uint *map3; 36 uchar *closest; 37 Rawimage *im; 38 int dx, dy; 39 char err[ERRMAX]; 40 uchar *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic; 41 42 err[0] = '\0'; 43 errstr(err, sizeof err); /* throw it away */ 44 im = malloc(sizeof(Rawimage)); 45 if(im == nil) 46 return nil; 47 memset(im, 0, sizeof(Rawimage)); 48 im->chans[0] = malloc(i->chanlen); 49 if(im->chans[0] == nil){ 50 free(im); 51 return nil; 52 } 53 im->r = i->r; 54 im->nchans = 1; 55 im->chandesc = CRGBV; 56 im->chanlen = i->chanlen; 57 58 dx = i->r.max.x-i->r.min.x; 59 dy = i->r.max.y-i->r.min.y; 60 cmap = i->cmap; 61 62 if(inited == 0){ 63 inited = 1; 64 for(j=0; j<CLAMPOFF; j++) 65 clamp[j] = 0; 66 for(j=0; j<256; j++) 67 clamp[CLAMPOFF+j] = (j>>4); 68 for(j=0; j<CLAMPOFF; j++) 69 clamp[CLAMPOFF+256+j] = (255>>4); 70 } 71 72 in = i->chans[0]; 73 inp = in; 74 out = im->chans[0]; 75 outp = out; 76 77 ered = malloc((dx+1)*sizeof(int)); 78 egrn = malloc((dx+1)*sizeof(int)); 79 eblu = malloc((dx+1)*sizeof(int)); 80 if(ered==nil || egrn==nil || eblu==nil){ 81 free(im->chans[0]); 82 free(im); 83 free(ered); 84 free(egrn); 85 free(eblu); 86 return _remaperror("remap: malloc failed: %r"); 87 } 88 memset(ered, 0, (dx+1)*sizeof(int)); 89 memset(egrn, 0, (dx+1)*sizeof(int)); 90 memset(eblu, 0, (dx+1)*sizeof(int)); 91 92 switch(i->chandesc){ 93 default: 94 return _remaperror("remap: can't recognize channel type %d", i->chandesc); 95 case CRGB1: 96 if(cmap == nil) 97 return _remaperror("remap: image has no color map"); 98 if(i->nchans != 1) 99 return _remaperror("remap: can't handle nchans %d", i->nchans); 100 for(j=1; j<=8; j++) 101 if(i->cmaplen == 3*(1<<j)) 102 break; 103 if(j > 8) 104 return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3); 105 if(i->cmaplen != 3*256){ 106 /* to avoid a range check in inner loop below, make a full-size cmap */ 107 memmove(cmap1, cmap, i->cmaplen); 108 cmap = cmap1; 109 } 110 if(errdiff == 0){ 111 k = 0; 112 for(j=0; j<256; j++){ 113 r = cmap[k]>>4; 114 g = cmap[k+1]>>4; 115 b = cmap[k+2]>>4; 116 k += 3; 117 map[j] = closestrgb[b+16*(g+16*r)]; 118 } 119 for(j=0; j<i->chanlen; j++) 120 out[j] = map[in[j]]; 121 }else{ 122 /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ 123 for(y=0; y<dy; y++){ 124 er = 0; 125 eg = 0; 126 eb = 0; 127 rp = ered; 128 gp = egrn; 129 bp = eblu; 130 for(x=0; x<dx; x++){ 131 cm = &cmap[3 * *inp++]; 132 r = cm[0] +*rp; 133 g = cm[1] +*gp; 134 b = cm[2] +*bp; 135 136 /* sanity checks are new */ 137 if(r >= 256+CLAMPOFF) 138 r = 0; 139 if(g >= 256+CLAMPOFF) 140 g = 0; 141 if(b >= 256+CLAMPOFF) 142 b = 0; 143 r1 = clamp[r+CLAMPOFF]; 144 g1 = clamp[g+CLAMPOFF]; 145 b1 = clamp[b+CLAMPOFF]; 146 if(r1 >= 16 || g1 >= 16 || b1 >= 16) 147 col = 0; 148 else 149 col = closestrgb[b1+16*(g1+16*r1)]; 150 *outp++ = col; 151 152 rgb = rgbmap[col]; 153 r -= (rgb>>16) & 0xFF; 154 t = (3*r)>>4; 155 *rp++ = t+er; 156 *rp += t; 157 er = r-3*t; 158 159 g -= (rgb>>8) & 0xFF; 160 t = (3*g)>>4; 161 *gp++ = t+eg; 162 *gp += t; 163 eg = g-3*t; 164 165 b -= rgb & 0xFF; 166 t = (3*b)>>4; 167 *bp++ = t+eb; 168 *bp += t; 169 eb = b-3*t; 170 } 171 } 172 } 173 break; 174 175 case CYCbCr: 176 closest = closestycbcr; 177 map3 = ycbcrmap; 178 goto Threecolor; 179 180 case CRGB: 181 closest = closestrgb; 182 map3 = rgbmap; 183 184 Threecolor: 185 if(i->nchans != 3) 186 return _remaperror("remap: RGB image has %d channels", i->nchans); 187 rpic = i->chans[0]; 188 gpic = i->chans[1]; 189 bpic = i->chans[2]; 190 if(errdiff == 0){ 191 for(j=0; j<i->chanlen; j++){ 192 r = rpic[j]>>4; 193 g = gpic[j]>>4; 194 b = bpic[j]>>4; 195 out[j] = closest[b+16*(g+16*r)]; 196 } 197 }else{ 198 /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ 199 for(y=0; y<dy; y++){ 200 er = 0; 201 eg = 0; 202 eb = 0; 203 rp = ered; 204 gp = egrn; 205 bp = eblu; 206 for(x=0; x<dx; x++){ 207 r = *rpic++ + *rp; 208 g = *gpic++ + *gp; 209 b = *bpic++ + *bp; 210 /* 211 * Errors can be uncorrectable if converting from YCbCr, 212 * since we can't guarantee that an extremal value of one of 213 * the components selects a color with an extremal value. 214 * If we don't, the errors accumulate without bound. This 215 * doesn't happen in RGB because the closest table can guarantee 216 * a color on the edge of the gamut, producing a zero error in 217 * that component. For the rotation YCbCr space, there may be 218 * no color that can guarantee zero error at the edge. 219 * Therefore we must clamp explicitly rather than by assuming 220 * an upper error bound of CLAMPOFF. The performance difference 221 * is miniscule anyway. 222 */ 223 if(r < 0) 224 r = 0; 225 else if(r > 255) 226 r = 255; 227 if(g < 0) 228 g = 0; 229 else if(g > 255) 230 g = 255; 231 if(b < 0) 232 b = 0; 233 else if(b > 255) 234 b = 255; 235 r1 = r>>4; 236 g1 = g>>4; 237 b1 = b>>4; 238 col = closest[b1+16*(g1+16*r1)]; 239 *outp++ = col; 240 241 rgb = map3[col]; 242 r -= (rgb>>16) & 0xFF; 243 t = (3*r)>>4; 244 *rp++ = t+er; 245 *rp += t; 246 er = r-3*t; 247 248 g -= (rgb>>8) & 0xFF; 249 t = (3*g)>>4; 250 *gp++ = t+eg; 251 *gp += t; 252 eg = g-3*t; 253 254 b -= rgb & 0xFF; 255 t = (3*b)>>4; 256 *bp++ = t+eb; 257 *bp += t; 258 eb = b-3*t; 259 } 260 } 261 } 262 break; 263 264 case CY: 265 if(i->nchans != 1) 266 return _remaperror("remap: Y image has %d chans", i->nchans); 267 rpic = i->chans[0]; 268 if(errdiff == 0){ 269 for(j=0; j<i->chanlen; j++){ 270 r = rpic[j]>>4; 271 *outp++ = closestrgb[r+16*(r+16*r)]; 272 } 273 }else{ 274 /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ 275 for(y=0; y<dy; y++){ 276 er = 0; 277 rp = ered; 278 for(x=0; x<dx; x++){ 279 r = *inp++ + *rp; 280 r1 = clamp[r+CLAMPOFF]; 281 col = closestrgb[r1+16*(r1+16*r1)]; 282 *outp++ = col; 283 284 rgb = rgbmap[col]; 285 r -= (rgb>>16) & 0xFF; 286 t = (3*r)>>4; 287 *rp++ = t+er; 288 *rp += t; 289 er = r-3*t; 290 } 291 } 292 } 293 break; 294 } 295 free(ered); 296 free(egrn); 297 free(eblu); 298 return im; 299 }