Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}