xref: /plan9/sys/src/cmd/jpg/torgbv.c (revision c35931e294c3b1d49a408159df49beb0cfbb8c50)
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