]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/torgbv.c
cwfs: back to previous version
[plan9front.git] / sys / src / cmd / jpg / torgbv.c
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         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 }