xref: /plan9-contrib/sys/src/cmd/jpg/readppm.c (revision 9863c128e160e47d0bf3a33658496e0e3d0bd48e)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <ctype.h>
6 #include "imagefile.h"
7 
8 Rawimage *readppm(Biobuf*, Rawimage*);
9 
10 /*
11  * fetch a non-comment character.
12  */
13 static
14 int
Bgetch(Biobufhdr * b)15 Bgetch(Biobufhdr *b)
16 {
17 	int c;
18 
19 	for(;;) {
20 		c = Bgetc(b);
21 		if(c == '#') {
22 			while((c = Bgetc(b)) != Beof && c != '\n')
23 				;
24 		}
25 		return c;
26 	}
27 }
28 
29 /*
30  * fetch a nonnegative decimal integer.
31  */
32 static
33 int
Bgetint(Biobufhdr * b)34 Bgetint(Biobufhdr *b)
35 {
36 	int c;
37 	int i;
38 
39 	while((c = Bgetch(b)) != Beof && !isdigit(c))
40 		;
41 	if(c == Beof)
42 		return -1;
43 
44 	i = 0;
45 	do {
46 		i = i*10 + (c-'0');
47 	} while((c = Bgetch(b)) != Beof && isdigit(c));
48 
49 	return i;
50 }
51 
52 static
53 int
Bgetdecimalbit(Biobufhdr * b)54 Bgetdecimalbit(Biobufhdr *b)
55 {
56 	int c;
57 	while((c = Bgetch(b)) != Beof && c != '0' && c != '1')
58 		;
59 	if(c == Beof)
60 		return -1;
61 	return c == '1';
62 }
63 
64 static int bitc, nbit;
65 
66 static
67 int
Bgetbit(Biobufhdr * b)68 Bgetbit(Biobufhdr *b)
69 {
70 	if(nbit == 0) {
71 		nbit = 8;
72 		bitc = Bgetc(b);
73 		if(bitc == -1)
74 			return -1;
75 	}
76 	nbit--;
77 	return (bitc >> nbit) & 0x1;
78 }
79 
80 static
81 void
Bflushbit(Biobufhdr *)82 Bflushbit(Biobufhdr*)
83 {
84 	nbit = 0;
85 }
86 
87 
88 Rawimage**
readpixmap(int fd,int colorspace)89 readpixmap(int fd, int colorspace)
90 {
91 	Rawimage **array, *a;
92 	Biobuf b;
93 	char buf[ERRMAX];
94 	int i;
95 	char *e;
96 
97 	USED(colorspace);
98 	if(Binit(&b, fd, OREAD) < 0)
99 		return nil;
100 
101 	werrstr("");
102 	e = "out of memory";
103 	if((array = malloc(sizeof *array)) == nil)
104 		goto Error;
105 	if((array[0] = malloc(sizeof *array[0])) == nil)
106 		goto Error;
107 	memset(array[0], 0, sizeof *array[0]);
108 
109 	for(i=0; i<3; i++)
110 		array[0]->chans[i] = nil;
111 
112 	e = "bad file format";
113 	switch(Bgetc(&b)) {
114 	case 'P':
115 		Bungetc(&b);
116 		a = readppm(&b, array[0]);
117 		break;
118 	default:
119 		a = nil;
120 		break;
121 	}
122 	if(a == nil)
123 		goto Error;
124 	array[0] = a;
125 
126 	return array;
127 
128 Error:
129 	if(array)
130 		free(array[0]);
131 	free(array);
132 
133 	errstr(buf, sizeof buf);
134 	if(buf[0] == 0)
135 		strcpy(buf, e);
136 	errstr(buf, sizeof buf);
137 
138 	return nil;
139 }
140 
141 typedef struct Pix	Pix;
142 struct Pix {
143 	char magic;
144 	int	maxcol;
145 	int	(*fetch)(Biobufhdr*);
146 	int	nchan;
147 	int	chandesc;
148 	int	invert;
149 	void	(*flush)(Biobufhdr*);
150 };
151 
152 static Pix pix[] = {
153 	{ '1', 1, Bgetdecimalbit, 1, CY, 1, nil },	/* portable bitmap */
154 	{ '4', 1, Bgetbit, 1, CY, 1, Bflushbit },	/* raw portable bitmap */
155 	{ '2', 0, Bgetint, 1, CY, 0, nil },	/* portable greymap */
156 	{ '5', 0, Bgetc, 1, CY, 0, nil },	/* raw portable greymap */
157 	{ '3', 0, Bgetint, 3, CRGB, 0, nil },	/* portable pixmap */
158 	{ '6', 0, Bgetc, 3, CRGB, 0, nil },	/* raw portable pixmap */
159 	{ 0 },
160 };
161 
162 Rawimage*
readppm(Biobuf * b,Rawimage * a)163 readppm(Biobuf *b, Rawimage *a)
164 {
165 	int i, ch, wid, ht, r, c;
166 	int maxcol, nchan, invert;
167 	int (*fetch)(Biobufhdr*);
168 	uchar *rgb[3];
169 	char buf[ERRMAX];
170 	char *e;
171 	Pix *p;
172 
173 	e = "bad file format";
174 	if(Bgetc(b) != 'P')
175 		goto Error;
176 
177 	c = Bgetc(b);
178 	for(p=pix; p->magic; p++)
179 		if(p->magic == c)
180 			break;
181 	if(p->magic == 0)
182 		goto Error;
183 
184 
185 	wid = Bgetint(b);
186 	ht = Bgetint(b);
187 	if(wid <= 0 || ht <= 0)
188 		goto Error;
189 	a->r = Rect(0,0,wid,ht);
190 
191 	maxcol = p->maxcol;
192 	if(maxcol == 0) {
193 		maxcol = Bgetint(b);
194 		if(maxcol <= 0)
195 			goto Error;
196 	}
197 
198 	e = "out of memory";
199 	for(i=0; i<p->nchan; i++)
200 		if((rgb[i] = a->chans[i] = malloc(wid*ht)) == nil)
201 			goto Error;
202 	a->nchans = p->nchan;
203 	a->chanlen = wid*ht;
204 	a->chandesc = p->chandesc;
205 
206 	e = "error reading file";
207 
208 	fetch = p->fetch;
209 	nchan = p->nchan;
210 	invert = p->invert;
211 	for(r=0; r<ht; r++) {
212 		for(c=0; c<wid; c++) {
213 			for(i=0; i<nchan; i++) {
214 				if((ch = (*fetch)(b)) < 0)
215 					goto Error;
216 				if(invert)
217 					ch = maxcol - ch;
218 				*rgb[i]++ = (ch * 255)/maxcol;
219 			}
220 		}
221 		if(p->flush)
222 			(*p->flush)(b);
223 	}
224 
225 	return a;
226 
227 Error:
228 	errstr(buf, sizeof buf);
229 	if(buf[0] == 0)
230 		strcpy(buf, e);
231 	errstr(buf, sizeof buf);
232 
233 	for(i=0; i<3; i++)
234 		free(a->chans[i]);
235 	free(a->cmap);
236 	return nil;
237 }
238