1 |
|
|
/* |
2 |
|
|
Teem: Tools to process and visualize scientific data and images . |
3 |
|
|
Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago |
4 |
|
|
Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann |
5 |
|
|
Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah |
6 |
|
|
|
7 |
|
|
This library is free software; you can redistribute it and/or |
8 |
|
|
modify it under the terms of the GNU Lesser General Public License |
9 |
|
|
(LGPL) as published by the Free Software Foundation; either |
10 |
|
|
version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
The terms of redistributing and/or modifying this software also |
12 |
|
|
include exceptions to the LGPL that facilitate static linking. |
13 |
|
|
|
14 |
|
|
This library is distributed in the hope that it will be useful, |
15 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 |
|
|
Lesser General Public License for more details. |
18 |
|
|
|
19 |
|
|
You should have received a copy of the GNU Lesser General Public License |
20 |
|
|
along with this library; if not, write to Free Software Foundation, Inc., |
21 |
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
#include "nrrd.h" |
25 |
|
|
#include "privateNrrd.h" |
26 |
|
|
|
27 |
|
|
static int |
28 |
|
|
_nrrdFormatText_available(void) { |
29 |
|
|
|
30 |
|
4 |
return AIR_TRUE; |
31 |
|
|
} |
32 |
|
|
|
33 |
|
|
static int |
34 |
|
|
_nrrdFormatText_nameLooksLike(const char *fname) { |
35 |
|
|
|
36 |
|
|
return (airEndsWith(fname, NRRD_EXT_TEXT) |
37 |
|
|
|| airEndsWith(fname, ".text") |
38 |
|
|
|| airEndsWith(fname, ".ascii")); |
39 |
|
|
} |
40 |
|
|
|
41 |
|
|
static int |
42 |
|
|
_nrrdFormatText_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding, |
43 |
|
|
int useBiff) { |
44 |
|
|
static const char me[]="_nrrdFormatText_fitsInto"; |
45 |
|
|
|
46 |
|
|
AIR_UNUSED(encoding); |
47 |
|
|
/* encoding ignored- always ascii */ |
48 |
|
|
if (!(1 == nrrd->dim || 2 == nrrd->dim)) { |
49 |
|
|
biffMaybeAddf(useBiff, NRRD, "%s: dimension is %d, not 1 or 2", |
50 |
|
|
me, nrrd->dim); |
51 |
|
|
return AIR_FALSE; |
52 |
|
|
} |
53 |
|
|
if (nrrdTypeBlock == nrrd->type) { |
54 |
|
|
biffMaybeAddf(useBiff, NRRD, "%s: can't save blocks to plain text", me); |
55 |
|
|
return AIR_FALSE; |
56 |
|
|
} |
57 |
|
|
/* NOTE: type of array not guaranteed to survive */ |
58 |
|
|
return AIR_TRUE; |
59 |
|
|
} |
60 |
|
|
|
61 |
|
|
static int |
62 |
|
|
_nrrdFormatText_contentStartsLike(NrrdIoState *nio) { |
63 |
|
|
float oneFloat; |
64 |
|
|
|
65 |
|
|
return (NRRD_COMMENT_CHAR == nio->line[0] |
66 |
|
|
|| airParseStrF(&oneFloat, nio->line, _nrrdTextSep, 1)); |
67 |
|
|
} |
68 |
|
|
|
69 |
|
|
static int |
70 |
|
|
_nrrdFormatText_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { |
71 |
|
|
static const char me[]="_nrrdFormatText_read"; |
72 |
|
|
const char *fs; |
73 |
|
|
char *errS; |
74 |
|
|
unsigned int plen, llen; |
75 |
|
|
size_t line, sx, sy, size[NRRD_DIM_MAX]; |
76 |
|
|
int nret, fidx, settwo = 0, gotOnePerAxis = AIR_FALSE; |
77 |
|
|
/* fl: first line, al: all lines */ |
78 |
|
|
airArray *flArr, *alArr; |
79 |
|
|
float *fl, *al, oneFloat; |
80 |
|
|
airPtrPtrUnion appu; |
81 |
|
|
|
82 |
|
|
if (!_nrrdFormatText_contentStartsLike(nio)) { |
83 |
|
|
biffAddf(NRRD, "%s: this doesn't look like a %s file", me, |
84 |
|
|
nrrdFormatText->name); |
85 |
|
|
return 1; |
86 |
|
|
} |
87 |
|
|
|
88 |
|
|
/* this goofiness is just to leave the nrrd as we found it |
89 |
|
|
(specifically, nrrd->dim) when we hit an error */ |
90 |
|
|
#define UNSETTWO if (settwo) nrrd->dim = settwo |
91 |
|
|
|
92 |
|
|
/* we only get here with the first line already in nio->line */ |
93 |
|
|
line = 1; |
94 |
|
|
llen = AIR_CAST(unsigned int, strlen(nio->line)); |
95 |
|
|
|
96 |
|
|
if (0 == nrrd->dim) { |
97 |
|
|
settwo = nrrd->dim; |
98 |
|
|
nrrd->dim = 2; |
99 |
|
|
} |
100 |
|
|
/* first, we get through comments */ |
101 |
|
|
while (NRRD_COMMENT_CHAR == nio->line[0]) { |
102 |
|
|
nio->pos = 1; |
103 |
|
|
nio->pos += AIR_CAST(int, strspn(nio->line + nio->pos, _nrrdFieldSep)); |
104 |
|
|
fidx = _nrrdReadNrrdParseField(nio, AIR_FALSE); |
105 |
|
|
/* could we parse anything? */ |
106 |
|
|
if (!fidx) { |
107 |
|
|
/* being unable to parse a comment as a nrrd field is not |
108 |
|
|
any kind of error */ |
109 |
|
|
goto plain; |
110 |
|
|
} |
111 |
|
|
if (nrrdField_comment == fidx) { |
112 |
|
|
fidx = 0; |
113 |
|
|
goto plain; |
114 |
|
|
} |
115 |
|
|
fs = airEnumStr(nrrdField, fidx); |
116 |
|
|
if (!_nrrdFieldValidInText[fidx]) { |
117 |
|
|
if (1 <= nrrdStateVerboseIO) { |
118 |
|
|
fprintf(stderr, "(%s: field \"%s\" not allowed in plain text " |
119 |
|
|
"--> plain comment)\n", me, fs); |
120 |
|
|
} |
121 |
|
|
fidx = 0; |
122 |
|
|
goto plain; |
123 |
|
|
} |
124 |
|
|
/* when reading plain text, we simply ignore repetitions of a field */ |
125 |
|
|
if ((nrrdField_keyvalue == fidx || !nio->seen[fidx]) |
126 |
|
|
&& nrrdFieldInfoParse[fidx](file, nrrd, nio, AIR_TRUE)) { |
127 |
|
|
errS = biffGetDone(NRRD); |
128 |
|
|
if (1 <= nrrdStateVerboseIO) { |
129 |
|
|
fprintf(stderr, "%s: %s", me, errS); |
130 |
|
|
fprintf(stderr, "(%s: malformed field \"%s\" --> plain comment)\n", |
131 |
|
|
me, fs); |
132 |
|
|
} |
133 |
|
|
if (nrrdField_dimension == fidx) { |
134 |
|
|
/* "# dimension: 0" lead nrrd->dim being set to 0 */ |
135 |
|
|
nrrd->dim = 2; |
136 |
|
|
} |
137 |
|
|
free(errS); |
138 |
|
|
fidx = 0; |
139 |
|
|
goto plain; |
140 |
|
|
} |
141 |
|
|
if (nrrdField_dimension == fidx) { |
142 |
|
|
if (!(1 == nrrd->dim || 2 == nrrd->dim)) { |
143 |
|
|
if (1 <= nrrdStateVerboseIO) { |
144 |
|
|
fprintf(stderr, "(%s: plain text dimension can only be 1 or 2; " |
145 |
|
|
"resetting to 2)\n", me); |
146 |
|
|
} |
147 |
|
|
nrrd->dim = 2; |
148 |
|
|
} |
149 |
|
|
if (1 == nrrd->dim && gotOnePerAxis) { |
150 |
|
|
fprintf(stderr, "(%s: already parsed per-axis field, can't reset " |
151 |
|
|
"dimension to 1; resetting to 2)\n", me); |
152 |
|
|
nrrd->dim = 2; |
153 |
|
|
} |
154 |
|
|
} |
155 |
|
|
if (_nrrdFieldOnePerAxis[fidx]) { |
156 |
|
|
gotOnePerAxis = AIR_TRUE; |
157 |
|
|
} |
158 |
|
|
nio->seen[fidx] = AIR_TRUE; |
159 |
|
|
plain: |
160 |
|
|
if (!fidx) { |
161 |
|
|
if (nrrdCommentAdd(nrrd, nio->line + 1)) { |
162 |
|
|
biffAddf(NRRD, "%s: couldn't add comment", me); |
163 |
|
|
UNSETTWO; return 1; |
164 |
|
|
} |
165 |
|
|
} |
166 |
|
|
if (_nrrdOneLine(&llen, nio, file)) { |
167 |
|
|
biffAddf(NRRD, "%s: error getting a line", me); |
168 |
|
|
UNSETTWO; return 1; |
169 |
|
|
} |
170 |
|
|
if (!llen) { |
171 |
|
|
biffAddf(NRRD, "%s: hit EOF before any numbers parsed", me); |
172 |
|
|
UNSETTWO; return 1; |
173 |
|
|
} |
174 |
|
|
line++; |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
/* we supposedly have a line of numbers, see how many there are */ |
178 |
|
|
if (!airParseStrF(&oneFloat, nio->line, _nrrdTextSep, 1)) { |
179 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
180 |
|
|
biffAddf(NRRD, "%s: couldn't parse a single number on line %s", me, |
181 |
|
|
airSprintSize_t(stmp, line)); |
182 |
|
|
UNSETTWO; return 1; |
183 |
|
|
} |
184 |
|
|
appu.f = &fl; |
185 |
|
|
flArr = airArrayNew(appu.v, NULL, sizeof(float), _NRRD_TEXT_INCR); |
186 |
|
|
if (!flArr) { |
187 |
|
|
biffAddf(NRRD, "%s: couldn't create array for first line values", me); |
188 |
|
|
UNSETTWO; return 1; |
189 |
|
|
} |
190 |
|
|
for (sx=1; 1; sx++) { |
191 |
|
|
/* there is obviously a limit to the number of numbers that can |
192 |
|
|
be parsed from a single finite line of input text */ |
193 |
|
|
airArrayLenSet(flArr, AIR_CAST(unsigned int, sx)); |
194 |
|
|
if (!flArr->data) { |
195 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
196 |
|
|
biffAddf(NRRD, "%s: couldn't alloc space for %s values", me, |
197 |
|
|
airSprintSize_t(stmp, sx)); |
198 |
|
|
UNSETTWO; return 1; |
199 |
|
|
} |
200 |
|
|
if (sx > airParseStrF(fl, nio->line, _nrrdTextSep, AIR_CAST(unsigned int, sx))) { |
201 |
|
|
/* We asked for sx ints and got less. We know that we successfully |
202 |
|
|
got one value, so we did succeed in parsing sx-1 values */ |
203 |
|
|
sx--; |
204 |
|
|
break; |
205 |
|
|
} |
206 |
|
|
} |
207 |
|
|
flArr = airArrayNuke(flArr); |
208 |
|
|
if (1 == nrrd->dim && 1 != sx) { |
209 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
210 |
|
|
biffAddf(NRRD, "%s: wanted 1-D nrrd, but got %s values on 1st line", me, |
211 |
|
|
airSprintSize_t(stmp, sx)); |
212 |
|
|
UNSETTWO; return 1; |
213 |
|
|
} |
214 |
|
|
/* else sx == 1 when nrrd->dim == 1 */ |
215 |
|
|
|
216 |
|
|
/* now see how many more lines there are */ |
217 |
|
|
appu.f = &al; |
218 |
|
|
alArr = airArrayNew(appu.v, NULL, sx*sizeof(float), _NRRD_TEXT_INCR); |
219 |
|
|
if (!alArr) { |
220 |
|
|
biffAddf(NRRD, "%s: couldn't create data buffer", me); |
221 |
|
|
UNSETTWO; return 1; |
222 |
|
|
} |
223 |
|
|
sy = 0; |
224 |
|
|
while (llen) { |
225 |
|
|
airArrayLenIncr(alArr, 1); |
226 |
|
|
if (!alArr->data) { |
227 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
228 |
|
|
biffAddf(NRRD, "%s: couldn't create scanline of %s values", me, |
229 |
|
|
airSprintSize_t(stmp, sx)); |
230 |
|
|
UNSETTWO; return 1; |
231 |
|
|
} |
232 |
|
|
plen = airParseStrF(al + sy*sx, nio->line, _nrrdTextSep, AIR_CAST(unsigned int, sx)); |
233 |
|
|
if (sx > plen) { |
234 |
|
|
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; |
235 |
|
|
biffAddf(NRRD, "%s: could only parse %d values (not %s) on line %s", |
236 |
|
|
me, plen, airSprintSize_t(stmp1, sx), |
237 |
|
|
airSprintSize_t(stmp2, line)); |
238 |
|
|
UNSETTWO; return 1; |
239 |
|
|
} |
240 |
|
|
sy++; |
241 |
|
|
line++; |
242 |
|
|
if (_nrrdOneLine(&llen, nio, file)) { |
243 |
|
|
biffAddf(NRRD, "%s: error getting a line", me); |
244 |
|
|
UNSETTWO; return 1; |
245 |
|
|
} |
246 |
|
|
} |
247 |
|
|
/* |
248 |
|
|
fprintf(stderr, "%s: nrrd->dim = %d, sx = %d; sy = %d\n", |
249 |
|
|
me, nrrd->dim, sx, sy); |
250 |
|
|
*/ |
251 |
|
|
|
252 |
|
|
if (!( 1 == nrrd->dim || 2 == nrrd->dim )) { |
253 |
|
|
fprintf(stderr, "%s: PANIC about to save, but dim = %d\n", me, nrrd->dim); |
254 |
|
|
exit(1); |
255 |
|
|
} |
256 |
|
|
if (1 == nrrd->dim) { |
257 |
|
|
size[0] = sy; |
258 |
|
|
} else { |
259 |
|
|
size[0] = sx; |
260 |
|
|
size[1] = sy; |
261 |
|
|
} |
262 |
|
|
|
263 |
|
|
if (nio->oldData |
264 |
|
|
&& nio->oldDataSize == (size_t)(nrrdTypeSize[nrrdTypeFloat]*sx*sy)) { |
265 |
|
|
nret = nrrdWrap_nva(nrrd, nio->oldData, nrrdTypeFloat, nrrd->dim, size); |
266 |
|
|
} else { |
267 |
|
|
nret = nrrdMaybeAlloc_nva(nrrd, nrrdTypeFloat, nrrd->dim, size); |
268 |
|
|
} |
269 |
|
|
if (nret) { |
270 |
|
|
biffAddf(NRRD, "%s: couldn't create nrrd for plain text data", me); |
271 |
|
|
UNSETTWO; return 1; |
272 |
|
|
} |
273 |
|
|
memcpy(nrrd->data, al, sx*sy*sizeof(float)); |
274 |
|
|
|
275 |
|
|
alArr = airArrayNuke(alArr); |
276 |
|
|
return 0; |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
static int |
280 |
|
|
_nrrdFormatText_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) { |
281 |
|
|
char cmt[AIR_STRLEN_SMALL], buff[AIR_STRLEN_SMALL]; |
282 |
|
|
size_t I; |
283 |
|
|
int i, x, y, sx, sy; |
284 |
|
|
void *data; |
285 |
|
|
float val; |
286 |
|
|
|
287 |
|
|
sprintf(cmt, "%c ", NRRD_COMMENT_CHAR); |
288 |
|
|
if (!nio->bareText) { |
289 |
|
|
if (1 == nrrd->dim) { |
290 |
|
|
_nrrdFprintFieldInfo(file, cmt, nrrd, nio, nrrdField_dimension, |
291 |
|
|
AIR_FALSE); |
292 |
|
|
} |
293 |
|
|
for (i=1; i<=NRRD_FIELD_MAX; i++) { |
294 |
|
|
if (_nrrdFieldValidInText[i] |
295 |
|
|
&& nrrdField_dimension != i /* dimension is handled above */ |
296 |
|
|
&& _nrrdFieldInteresting(nrrd, nio, i)) { |
297 |
|
|
_nrrdFprintFieldInfo(file, cmt, nrrd, nio, i, AIR_FALSE); |
298 |
|
|
} |
299 |
|
|
} |
300 |
|
|
if (nrrdKeyValueSize(nrrd)) { |
301 |
|
|
unsigned int kvi; |
302 |
|
|
for (kvi=0; kvi<nrrd->kvpArr->len; kvi++) { |
303 |
|
|
_nrrdKeyValueWrite(file, NULL, "#", |
304 |
|
|
nrrd->kvp[0 + 2*kvi], |
305 |
|
|
nrrd->kvp[1 + 2*kvi]); |
306 |
|
|
} |
307 |
|
|
} |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
if (1 == nrrd->dim) { |
311 |
|
|
sx = 1; |
312 |
|
|
sy = AIR_CAST(int, nrrd->axis[0].size); |
313 |
|
|
} |
314 |
|
|
else { |
315 |
|
|
sx = AIR_CAST(int, nrrd->axis[0].size); |
316 |
|
|
sy = AIR_CAST(int, nrrd->axis[1].size); |
317 |
|
|
} |
318 |
|
|
data = nrrd->data; |
319 |
|
|
I = 0; |
320 |
|
|
for (y=0; y<sy; y++) { |
321 |
|
|
for (x=0; x<sx; x++) { |
322 |
|
|
val = nrrdFLookup[nrrd->type](data, I); |
323 |
|
|
nrrdSprint[nrrdTypeFloat](buff, &val); |
324 |
|
|
if (x) fprintf(file, " "); |
325 |
|
|
fprintf(file, "%s", buff); |
326 |
|
|
I++; |
327 |
|
|
} |
328 |
|
|
fprintf(file, "\n"); |
329 |
|
|
} |
330 |
|
|
|
331 |
|
|
return 0; |
332 |
|
|
} |
333 |
|
|
|
334 |
|
|
const NrrdFormat |
335 |
|
|
_nrrdFormatText = { |
336 |
|
|
"text", |
337 |
|
|
AIR_FALSE, /* isImage */ |
338 |
|
|
AIR_TRUE, /* readable */ |
339 |
|
|
AIR_FALSE, /* usesDIO */ |
340 |
|
|
_nrrdFormatText_available, |
341 |
|
|
_nrrdFormatText_nameLooksLike, |
342 |
|
|
_nrrdFormatText_fitsInto, |
343 |
|
|
_nrrdFormatText_contentStartsLike, |
344 |
|
|
_nrrdFormatText_read, |
345 |
|
|
_nrrdFormatText_write |
346 |
|
|
}; |
347 |
|
|
|
348 |
|
|
const NrrdFormat *const |
349 |
|
|
nrrdFormatText = &_nrrdFormatText; |