xref: /plan9/sys/src/cmd/jpg/readtga.c (revision 7cb50cffdc6fcb4499628912540435138e5f95f3)
1 /*
2  * TGA is a fairly dead standard, however in the video industry
3  * it is still used a little for test patterns and the like.
4  *
5  * Thus we ignore any alpha channels, and colour mapped images.
6  */
7 
8 #include <u.h>
9 #include <libc.h>
10 #include <bio.h>
11 #include <draw.h>
12 #include <ctype.h>
13 #include "imagefile.h"
14 
15 enum {
16 	HdrLen = 18,
17 };
18 
19 typedef struct {
20 	int idlen;		/* length of string after header */
21 	int cmaptype;		/* 1 =>  datatype = 1 => colourmapped */
22 	int datatype;		/* see below */
23 	int cmaporigin;		/* index of first entry in colour map */
24 	int cmaplen;		/* length of olour map */
25 	int cmapbpp;		/* bips per pixel of colour map: 16, 24, or 32 */
26 	int xorigin;		/* source image origin */
27 	int yorigin;
28 	int width;
29 	int height;
30 	int bpp;		/* bits per pixel of image: 16, 24, or 32 */
31 	int descriptor;
32 	uchar *cmap;		/* colour map (optional) */
33 } Tga;
34 
35 /*
36  * descriptor:
37  * d0-3 = number of attribute bits per pixel
38  * d4 	= reserved, always zero
39  * d6-7	= origin: 0=lower left, 1=upper left, 2=lower right, 3=upper right
40  * d8-9 = interleave: 0=progressive, 1=2 way, 3 = 4 way, 4 = reserved.
41  */
42 
43 char *datatype[] = {
44 	[0]	"No image data",
45 	[1]	"color mapped",
46 	[2]	"RGB",
47 	[3]	"B&W",
48 	[9]	"RLE color-mapped",
49 	[10]	"RLE RGB",
50 	[11]	"Compressed B&W",
51 	[32]	"Compressed color",
52 	[33]	"Quadtree compressed color",
53 };
54 
55 static int
Bgeti(Biobuf * bp)56 Bgeti(Biobuf *bp)
57 {
58 	int x, y;
59 
60 	if((x = Bgetc(bp)) < 0)
61 		return -1;
62 	if((y = Bgetc(bp)) < 0)
63 		return -1;
64 	return (y<<8)|x;
65 }
66 
67 static Tga *
rdhdr(Biobuf * bp)68 rdhdr(Biobuf *bp)
69 {
70 	int n;
71 	Tga *h;
72 
73 	if((h = malloc(sizeof(Tga))) == nil)
74 		return nil;
75 	if((h->idlen = Bgetc(bp)) == -1)
76 		return nil;
77 	if((h->cmaptype = Bgetc(bp)) == -1)
78 		return nil;
79 	if((h->datatype = Bgetc(bp)) == -1)
80 		return nil;
81 	if((h->cmaporigin = Bgeti(bp)) == -1)
82 		return nil;
83 	if((h->cmaplen = Bgeti(bp)) == -1)
84 		return nil;
85 	if((h->cmapbpp = Bgetc(bp)) == -1)
86 		return nil;
87 	if((h->xorigin = Bgeti(bp)) == -1)
88 		return nil;
89 	if((h->yorigin = Bgeti(bp)) == -1)
90 		return nil;
91 	if((h->width = Bgeti(bp)) == -1)
92 		return nil;
93 	if((h->height = Bgeti(bp)) == -1)
94 		return nil;
95 	if((h->bpp = Bgetc(bp)) == -1)
96 		return nil;
97 	if((h->descriptor = Bgetc(bp)) == -1)
98 		return nil;
99 
100 	/* skip over ID, usually empty anyway */
101 	if(Bseek(bp, h->idlen, 1) < 0){
102 		free(h);
103 		return nil;
104 	}
105 
106 	if(h->cmaptype == 0){
107 		h->cmap = 0;
108 		return h;
109 	}
110 
111 	n = (h->cmapbpp/8)*h->cmaplen;
112 	if((h->cmap = malloc(n)) == nil){
113 		free(h);
114 		return nil;
115 	}
116 	if(Bread(bp, h->cmap, n) != n){
117 		free(h);
118 		free(h->cmap);
119 		return nil;
120 	}
121 	return h;
122 }
123 
124 static int
luma(Biobuf * bp,uchar * l,int num)125 luma(Biobuf *bp, uchar *l, int num)
126 {
127 	return Bread(bp, l, num);
128 }
129 
130 static int
luma_rle(Biobuf * bp,uchar * l,int num)131 luma_rle(Biobuf *bp, uchar *l, int num)
132 {
133 	uchar len;
134 	int i, got;
135 
136 	for(got = 0; got < num; got += len){
137 		if(Bread(bp, &len, 1) != 1)
138 			break;
139 		if(len & 0x80){
140 			len &= 0x7f;
141 			len += 1;	/* run of zero is meaningless */
142 			if(luma(bp, l, 1) != 1)
143 				break;
144 			for(i = 0; i < len && got < num; i++)
145 				l[i+1] = *l;
146 		}
147 		else{
148 			len += 1;	/* raw block of zero is meaningless */
149 			if(luma(bp, l, len) != len)
150 				break;
151 		}
152 		l += len;
153 	}
154 	return got;
155 }
156 
157 
158 static int
rgba(Biobuf * bp,int bpp,uchar * r,uchar * g,uchar * b,int num)159 rgba(Biobuf *bp, int bpp, uchar *r, uchar *g, uchar *b, int num)
160 {
161 	int i;
162 	uchar x, y, buf[4];
163 
164 	switch(bpp){
165 	case 16:
166 		for(i = 0; i < num; i++){
167 			if(Bread(bp, buf, 2) != 2)
168 				break;
169 			x = buf[0];
170 			y = buf[1];
171 			*b++ = (x&0x1f)<<3;
172 			*g++ = ((y&0x03)<<6) | ((x&0xe0)>>2);
173 			*r++ = (y&0x1f)<<3;
174 		}
175 		break;
176 	case 24:
177 		for(i = 0; i < num; i++){
178 			if(Bread(bp, buf, 3) != 3)
179 				break;
180 			*b++ = buf[0];
181 			*g++ = buf[1];
182 			*r++ = buf[2];
183 		}
184 		break;
185 	case 32:
186 		for(i = 0; i < num; i++){
187 			if(Bread(bp, buf, 4) != 4)
188 				break;
189 			*b++ = buf[0];
190 			*g++ = buf[1];
191 			*r++ = buf[2];
192 		}
193 		break;
194 	default:
195 		i = 0;
196 		break;
197 	}
198 	return i;
199 }
200 
201 static int
rgba_rle(Biobuf * bp,int bpp,uchar * r,uchar * g,uchar * b,int num)202 rgba_rle(Biobuf *bp, int bpp, uchar *r, uchar *g, uchar *b, int num)
203 {
204 	uchar len;
205 	int i, got;
206 
207 	for(got = 0; got < num; got += len){
208 		if(Bread(bp, &len, 1) != 1)
209 			break;
210 		if(len & 0x80){
211 			len &= 0x7f;
212 			len += 1;	/* run of zero is meaningless */
213 			if(rgba(bp, bpp, r, g, b, 1) != 1)
214 				break;
215 			for(i = 0; i < len-1 && got < num; i++){
216 				r[i+1] = *r;
217 				g[i+1] = *g;
218 				b[i+1] = *b;
219 			}
220 		}
221 		else{
222 			len += 1;	/* raw block of zero is meaningless */
223 			if(rgba(bp, bpp, r, g, b, len) != len)
224 				break;
225 		}
226 		r += len;
227 		g += len;
228 		b += len;
229 	}
230 	return got;
231 }
232 
233 int
flip(Rawimage * ar)234 flip(Rawimage *ar)
235 {
236 	int w, h, c, l;
237 	uchar *t, *s, *d;
238 
239 	w = Dx(ar->r);
240 	h = Dy(ar->r);
241 	if((t = malloc(w)) == nil){
242 		werrstr("ReadTGA: no memory - %r\n");
243 		return -1;
244 	}
245 
246 	for(c = 0; c < ar->nchans; c++){
247 		s = ar->chans[c];
248 		d = ar->chans[c] + ar->chanlen - w;
249 		for(l = 0; l < (h/2); l++){
250 			memcpy(t, s, w);
251 			memcpy(s, d, w);
252 			memcpy(d, t, w);
253 			s += w;
254 			d -= w;
255 		}
256 	}
257 	free(t);
258 	return 0;
259 }
260 
261 int
reflect(Rawimage * ar)262 reflect(Rawimage *ar)
263 {
264 	int w, h, c, l, p;
265 	uchar t, *sol, *eol, *s, *d;
266 
267 	w = Dx(ar->r);
268 	h = Dy(ar->r);
269 
270 	for(c = 0; c < ar->nchans; c++){
271 		sol = ar->chans[c];
272 		eol = ar->chans[c] +w -1;
273 		for(l = 0; l < h; l++){
274 			s = sol;
275 			d = eol;
276 			for(p = 0; p < w/2; p++){
277 				t = *s;
278 				*s = *d;
279 				*d = t;
280 				s++;
281 				d--;
282 			}
283 			sol += w;
284 			eol += w;
285 		}
286 	}
287 	return 0;
288 }
289 
290 
291 Rawimage**
Breadtga(Biobuf * bp)292 Breadtga(Biobuf *bp)
293 {
294 	Tga *h;
295 	int n, c, num;
296 	uchar *r, *g, *b;
297 	Rawimage *ar, **array;
298 
299 	if((h = rdhdr(bp)) == nil){
300 		werrstr("ReadTGA: bad header %r");
301 		return nil;
302 	}
303 
304 	if(0){
305 		fprint(2, "idlen=%d\n", h->idlen);
306 		fprint(2, "cmaptype=%d\n", h->cmaptype);
307 		fprint(2, "datatype=%s\n", datatype[h->datatype]);
308 		fprint(2, "cmaporigin=%d\n", h->cmaporigin);
309 		fprint(2, "cmaplen=%d\n", h->cmaplen);
310 		fprint(2, "cmapbpp=%d\n", h->cmapbpp);
311 		fprint(2, "xorigin=%d\n", h->xorigin);
312 		fprint(2, "yorigin=%d\n", h->yorigin);
313 		fprint(2, "width=%d\n", h->width);
314 		fprint(2, "height=%d\n", h->height);
315 		fprint(2, "bpp=%d\n", h->bpp);
316 		fprint(2, "descriptor=%d\n", h->descriptor);
317 	}
318 
319 	array = nil;
320 	if((ar = calloc(sizeof(Rawimage), 1)) == nil){
321 		werrstr("ReadTGA: no memory - %r\n");
322 		goto Error;
323 	}
324 
325 	if((array = calloc(sizeof(Rawimage *), 2)) == nil){
326 		werrstr("ReadTGA: no memory - %r\n");
327 		goto Error;
328 	}
329 	array[0] = ar;
330 	array[1] = nil;
331 
332 	if(h->datatype == 3){
333 		ar->nchans = 1;
334 		ar->chandesc = CY;
335 	}
336 	else{
337 		ar->nchans = 3;
338 		ar->chandesc = CRGB;
339 	}
340 
341 	ar->chanlen = h->width*h->height;
342 	ar->r = Rect(0, 0, h->width, h->height);
343 	for (c = 0; c < ar->nchans; c++)
344 		if ((ar->chans[c] = malloc(h->width*h->height)) == nil){
345 			werrstr("ReadTGA: no memory - %r\n");
346 			goto Error;
347 		}
348 	r = ar->chans[0];
349 	g = ar->chans[1];
350 	b = ar->chans[2];
351 
352 	num = h->width*h->height;
353 	switch(h->datatype){
354 	case 2:
355 		if(rgba(bp, h->bpp, r, g, b, num) != num){
356 			werrstr("ReadTGA: decode fail - %r\n");
357 			goto Error;
358 		}
359 		break;
360 	case 3:
361 		if(luma(bp, r, num) != num){
362 			werrstr("ReadTGA: decode fail - %r\n");
363 			goto Error;
364 		}
365 		break;
366 	case 10:
367 		if((n = rgba_rle(bp, h->bpp, r, g, b, num)) != num){
368 			werrstr("ReadTGA: decode fail (%d!=%d) - %r\n", n, num);
369 			goto Error;
370 		}
371 		break;
372 	case 11:
373 		if(luma_rle(bp, r, num) != num){
374 			werrstr("ReadTGA: decode fail - %r\n");
375 			goto Error;
376 		}
377 		break;
378 	default:
379 		werrstr("ReadTGA: type=%d (%s) unsupported\n", h->datatype, datatype[h->datatype]);
380 		goto Error;
381  	}
382 
383 	if(h->xorigin != 0)
384 		reflect(ar);
385 	if(h->yorigin == 0)
386 		flip(ar);
387 
388 	free(h->cmap);
389 	free(h);
390 	return array;
391 Error:
392 
393 	if(ar)
394 		for (c = 0; c < ar->nchans; c++)
395 			free(ar->chans[c]);
396 	free(ar);
397 	free(array);
398 	free(h->cmap);
399 	free(h);
400 	return nil;
401 }
402 
403 Rawimage**
readtga(int fd)404 readtga(int fd)
405 {
406 	Rawimage * *a;
407 	Biobuf b;
408 
409 	if (Binit(&b, fd, OREAD) < 0)
410 		return nil;
411 	a = Breadtga(&b);
412 	Bterm(&b);
413 	return a;
414 }
415 
416 
417