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