GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/formatPNM.c Lines: 92 172 53.5 %
Date: 2017-05-26 Branches: 51 117 43.6 %

Line Branch Exec Source
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
#define MAGIC_P6 "P6"
28
#define MAGIC_P5 "P5"
29
#define MAGIC_P3 "P3"
30
#define MAGIC_P2 "P2"
31
32
static int
33
_nrrdFormatPNM_available(void) {
34
35
8
  return AIR_TRUE;
36
}
37
38
static int
39
_nrrdFormatPNM_nameLooksLike(const char *filename) {
40
41
3
  return (airEndsWith(filename, NRRD_EXT_PGM)
42
2
          || airEndsWith(filename, NRRD_EXT_PPM));
43
}
44
45
static int
46
_nrrdFormatPNM_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding,
47
                        int useBiff) {
48
  static const char me[]="_nrrdFormatPNM_fitsInto";
49
  int ret;
50
51
2
  if (!( nrrd && encoding )) {
52
    biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)",
53
                  me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding));
54
    return AIR_FALSE;
55
  }
56
1
  if (nrrdTypeUChar != nrrd->type) {
57
    biffMaybeAddf(useBiff, NRRD, "%s: type must be %s (not %s)", me,
58
                  airEnumStr(nrrdType, nrrdTypeUChar),
59
                  airEnumStr(nrrdType, nrrd->type));
60
    return AIR_FALSE;
61
  }
62

1
  if (!( nrrdEncodingRaw == encoding || nrrdEncodingAscii == encoding)) {
63
    biffMaybeAddf(useBiff, NRRD, "%s: encoding can only be %s or %s", me,
64
                  nrrdEncodingRaw->name, nrrdEncodingAscii->name);
65
    return AIR_FALSE;
66
  }
67
1
  if (2 == nrrd->dim) {
68
    /* its a gray-scale image */
69
    ret = 2;
70
1
  } else if (3 == nrrd->dim) {
71
    if (1 == nrrd->axis[0].size) {
72
      /* its a faux-3D image, really grayscale */
73
      ret = 2;
74
    } else if (3 == nrrd->axis[0].size) {
75
      /* its a real color image */
76
      ret = 3;
77
    } else {
78
      /* else its no good */
79
      char stmp[AIR_STRLEN_SMALL];
80
      biffMaybeAddf(useBiff, NRRD,
81
                    "%s: dim is 3, but 1st axis size is %s, not 1 or 3", me,
82
                    airSprintSize_t(stmp, nrrd->axis[0].size));
83
      return AIR_FALSE;
84
    }
85
  } else {
86
    biffMaybeAddf(useBiff, NRRD,
87
                  "%s: dimension is %d, not 2 or 3", me, nrrd->dim);
88
    return AIR_FALSE;
89
  }
90
1
  return ret;
91
1
}
92
93
int
94
_nrrdFormatPNM_contentStartsLike(NrrdIoState *nio) {
95
96
12
  return (!strcmp(MAGIC_P6, nio->line)
97
8
          || !strcmp(MAGIC_P5, nio->line)
98
4
          || !strcmp(MAGIC_P3, nio->line)
99
4
          || !strcmp(MAGIC_P2, nio->line));
100
}
101
102
static int
103
_nrrdFormatPNM_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) {
104
  static const char me[]="_nrrdFormatPNM_read";
105
  const char *fs;
106
  char *perr;
107
4
  int color, got, want, ret, val[5], sx, sy, max, magic;
108
2
  unsigned int i, llen;
109
110
2
  if (!_nrrdFormatPNM_contentStartsLike(nio)) {
111
    biffAddf(NRRD, "%s: this doesn't look like a %s file", me,
112
             nrrdFormatPNM->name);
113
    return 1;
114
  }
115
2
  nrrd->type = nrrdTypeUChar;
116
2
  if (!strcmp(MAGIC_P6, nio->line)) {
117
    magic = 6;
118
2
  } else if (!strcmp(MAGIC_P5, nio->line)) {
119
    magic = 5;
120
2
  } else if (!strcmp(MAGIC_P3, nio->line)) {
121
    magic = 3;
122
  } else if (!strcmp(MAGIC_P2, nio->line)) {
123
    magic = 2;
124
  } else {
125
    fprintf(stderr, "%s: PANIC: magic \"%s\" not handled\n", me, nio->line);
126
    biffAddf(NRRD, "%s: PANIC: magic \"%s\" not handled\n", me, nio->line);
127
    return 1;
128
  }
129
130

2
  switch(magic) {
131
  case 2:
132
    color = AIR_FALSE;
133
    nio->encoding = nrrdEncodingAscii;
134
    nrrd->dim = 2;
135
    break;
136
  case 3:
137
    color = AIR_TRUE;
138
    nio->encoding = nrrdEncodingAscii;
139
    nrrd->dim = 3;
140
    break;
141
  case 5:
142
    color = AIR_FALSE;
143
2
    nio->encoding = nrrdEncodingRaw;
144
2
    nrrd->dim = 2;
145
2
    break;
146
  case 6:
147
    color = AIR_TRUE;
148
    nio->encoding = nrrdEncodingRaw;
149
    nrrd->dim = 3;
150
    break;
151
  default:
152
    biffAddf(NRRD, "%s: sorry, PNM magic %d not implemented", me, magic);
153
    return 1;
154
    break;
155
  }
156
2
  val[0] = val[1] = val[2] = 0;
157
  got = 0;
158
  want = 3;
159
24
  while (got < want) {
160
20
    nio->pos = 0;
161
20
    if (_nrrdOneLine(&llen, nio, file)) {
162
      biffAddf(NRRD, "%s: failed to get line from PNM header", me);
163
      return 1;
164
    }
165
20
    if (!(0 < llen)) {
166
      biffAddf(NRRD, "%s: hit EOF in header with %d of %d ints parsed",
167
               me, got, want);
168
      return 1;
169
    }
170
20
    if ('#' == nio->line[0]) {
171
16
      if (strncmp(nio->line, NRRD_PNM_COMMENT, strlen(NRRD_PNM_COMMENT))) {
172
        /* this is a generic comment */
173
        ret = 0;
174
6
        goto plain;
175
      }
176
      /* else this PNM comment is trying to tell us something */
177
10
      nio->pos = AIR_CAST(int, strlen(NRRD_PNM_COMMENT));
178
10
      nio->pos += AIR_CAST(int, strspn(nio->line + nio->pos, _nrrdFieldSep));
179
10
      ret = _nrrdReadNrrdParseField(nio, AIR_FALSE);
180
10
      if (!ret) {
181
        if (1 <= nrrdStateVerboseIO) {
182
          fprintf(stderr, "(%s: unparsable field \"%s\" --> plain comment)\n",
183
                  me, nio->line);
184
        }
185
        goto plain;
186
      }
187
10
      if (nrrdField_comment == ret) {
188
        ret = 0;
189
        goto plain;
190
      }
191
10
      fs = airEnumStr(nrrdField, ret);
192
10
      if (!_nrrdFieldValidInImage[ret]) {
193
        if (1 <= nrrdStateVerboseIO) {
194
          fprintf(stderr, "(%s: field \"%s\" (not allowed in PNM) "
195
                  "--> plain comment)\n", me, fs);
196
        }
197
        ret = 0;
198
        goto plain;
199
      }
200
20
      if (!nio->seen[ret]
201
20
          && nrrdFieldInfoParse[ret](file, nrrd, nio, AIR_TRUE)) {
202
        perr = biffGetDone(NRRD);
203
        if (1 <= nrrdStateVerboseIO) {
204
          fprintf(stderr, "(%s: unparsable info for field \"%s\" "
205
                  "--> plain comment:\n%s)\n", me, fs, perr);
206
        }
207
        free(perr);
208
        ret = 0;
209
        goto plain;
210
      }
211
10
      nio->seen[ret] = AIR_TRUE;
212
    plain:
213
16
      if (!ret) {
214
6
        if (nrrdCommentAdd(nrrd, nio->line+1)) {
215
          biffAddf(NRRD, "%s: couldn't add comment", me);
216
          return 1;
217
        }
218
      }
219
      continue;
220
    }
221
222
4
    if (3 == sscanf(nio->line, "%d%d%d", val+got+0, val+got+1, val+got+2)) {
223
      got += 3;
224
      continue;
225
    }
226
4
    if (2 == sscanf(nio->line, "%d%d", val+got+0, val+got+1)) {
227
2
      got += 2;
228
2
      continue;
229
    }
230
2
    if (1 == sscanf(nio->line, "%d", val+got+0)) {
231
2
      got += 1;
232
2
      continue;
233
    }
234
235
    /* else, we couldn't parse ANY numbers on this line, which is okay
236
       as long as the line contains nothing but white space */
237
    for (i=0; (i<=strlen(nio->line)-1
238
               && isspace(AIR_INT(nio->line[i]))); i++)
239
      ;
240
    if (i != strlen(nio->line)) {
241
      biffAddf(NRRD, "%s: \"%s\" has no integers but isn't just whitespace",
242
               me, nio->line);
243
      return 1;
244
    }
245
  }
246
  /* this assumes 3 == want */
247
2
  sx = val[0];
248
2
  sy = val[1];
249
2
  max = val[2];
250
2
  if (!(sx > 0 && sy > 0 && max > 0)) {
251
    biffAddf(NRRD, "%s: sx,sy,max of %d,%d,%d has problem", me, sx, sy, max);
252
    return 1;
253
  }
254
2
  if (255 != max) {
255
    biffAddf(NRRD, "%s: sorry, can only deal with max value 255 (not %d)",
256
             me, max);
257
    return 1;
258
  }
259
260
  /* we know what we need in order to set nrrd fields and read data */
261

4
  if (color) {
262
    nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize,
263
                       AIR_CAST(size_t, 3),
264
2
                       AIR_CAST(size_t, sx),
265
2
                       AIR_CAST(size_t, sy));
266
  } else {
267
2
    nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize,
268
                       AIR_CAST(size_t, sx),
269
                       AIR_CAST(size_t, sy));
270
  }
271
2
  if (!nio->skipData) {
272
2
    if (_nrrdCalloc(nrrd, nio, file)) {
273
      biffAddf(NRRD, "%s: couldn't allocate memory for data", me);
274
      return 1;
275
    }
276
2
    if (nio->encoding->read(file, nrrd->data, nrrdElementNumber(nrrd),
277
                            nrrd, nio)) {
278
      biffAddf(NRRD, "%s:", me);
279
      return 1;
280
    }
281
  } else {
282
    nrrd->data = NULL;
283
  }
284
285
2
  return 0;
286
2
}
287
288
static int
289
_nrrdFormatPNM_write(FILE *file, const Nrrd *_nrrd, NrrdIoState *nio) {
290
  static const char me[]="_nrrdFormatPNM_write";
291
  int color, sx, sy, magic, fi;
292
  unsigned int ci;
293
  Nrrd *nrrd;
294
  airArray *mop;
295
296
2
  mop = airMopNew();
297
1
  airMopAdd(mop, nrrd = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
298
1
  if (nrrdCopy(nrrd, _nrrd)) {
299
    biffAddf(NRRD, "%s: couldn't make private copy", me);
300
    airMopError(mop); return 1;
301
  }
302

1
  if (3 == nrrd->dim && 1 == nrrd->axis[0].size) {
303
    if (nrrdAxesDelete(nrrd, nrrd, 0)) {
304
      biffAddf(NRRD, "%s:", me);
305
      airMopError(mop); return 1;
306
    }
307
  }
308
1
  color = (3 == nrrd->dim);
309
1
  if (!color) {
310
1
    magic = (nrrdEncodingAscii == nio->encoding ? 2 : 5);
311
1
    sx = AIR_CAST(int, nrrd->axis[0].size);
312
1
    sy = AIR_CAST(int, nrrd->axis[1].size);
313
1
  } else {
314
1
    magic = (nrrdEncodingAscii == nio->encoding ? 3 : 6);
315
    sx = AIR_CAST(int, nrrd->axis[1].size);
316
    sy = AIR_CAST(int, nrrd->axis[2].size);
317
  }
318
319
1
  fprintf(file, "P%d\n", magic);
320
1
  fprintf(file, "%d %d\n", sx, sy);
321
66
  for (fi=nrrdField_unknown+1; fi<nrrdField_last; fi++) {
322
52
    if (_nrrdFieldValidInImage[fi]
323
52
        && _nrrdFieldInteresting(nrrd, nio, fi)) {
324
      /* dropAxis0 is always AIR_FALSE because of code
325
         above to delete a stub axis 0 */
326
5
      _nrrdFprintFieldInfo(file, NRRD_PNM_COMMENT, nrrd, nio, fi, AIR_FALSE);
327
5
    }
328
  }
329
8
  for (ci=0; ci<nrrd->cmtArr->len; ci++) {
330
3
    fprintf(file, "# %s\n", nrrd->cmt[ci]);
331
  }
332
1
  fprintf(file, "255\n");
333
334
1
  if (!nio->skipData) {
335
1
    if (nio->encoding->write(file, nrrd->data, nrrdElementNumber(nrrd),
336
                             nrrd, nio)) {
337
      biffAddf(NRRD, "%s:", me);
338
      airMopError(mop); return 1;
339
    }
340
  }
341
342
1
  airMopError(mop);
343
1
  return 0;
344
1
}
345
346
const NrrdFormat
347
_nrrdFormatPNM = {
348
  "PNM",
349
  AIR_TRUE,   /* isImage */
350
  AIR_TRUE,   /* readable */
351
  AIR_FALSE,  /* usesDIO */
352
  _nrrdFormatPNM_available,
353
  _nrrdFormatPNM_nameLooksLike,
354
  _nrrdFormatPNM_fitsInto,
355
  _nrrdFormatPNM_contentStartsLike,
356
  _nrrdFormatPNM_read,
357
  _nrrdFormatPNM_write
358
};
359
360
const NrrdFormat *const
361
nrrdFormatPNM = &_nrrdFormatPNM;