Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include "imagefile.h"
7 #include "rgbv.h"
8 #include "ycbcr.h"
10 #define CLAMPOFF 128
12 static int clamp[CLAMPOFF+256+CLAMPOFF];
13 static int inited;
15 void*
16 _remaperror(char *fmt, ...)
17 {
18 va_list arg;
19 char buf[256];
21 va_start(arg, fmt);
22 vseprint(buf, buf+sizeof buf, fmt, arg);
23 va_end(arg);
25 werrstr(buf);
26 return nil;
27 }
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;
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;
58 dx = i->r.max.x-i->r.min.x;
59 dy = i->r.max.y-i->r.min.y;
60 cmap = i->cmap;
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 }
72 in = i->chans[0];
73 inp = in;
74 out = im->chans[0];
75 outp = out;
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));
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;
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)];
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;
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;
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;
159 g -= (rgb>>8) & 0xFF;
160 t = (3*g)>>4;
161 *gp++ = t+eg;
162 *gp += t;
163 eg = g-3*t;
165 b -= rgb & 0xFF;
166 t = (3*b)>>4;
167 *bp++ = t+eb;
168 *bp += t;
169 eb = b-3*t;
173 break;
175 case CYCbCr:
176 closest = closestycbcr;
177 map3 = ycbcrmap;
178 goto Threecolor;
180 case CRGB:
181 closest = closestrgb;
182 map3 = rgbmap;
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)];
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;
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;
248 g -= (rgb>>8) & 0xFF;
249 t = (3*g)>>4;
250 *gp++ = t+eg;
251 *gp += t;
252 eg = g-3*t;
254 b -= rgb & 0xFF;
255 t = (3*b)>>4;
256 *bp++ = t+eb;
257 *bp += t;
258 eb = b-3*t;
262 break;
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)];
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;
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;
293 break;
295 free(ered);
296 free(egrn);
297 free(eblu);
298 return im;