GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/formatPNG.c Lines: 1 242 0.4 %
Date: 2017-05-26 Branches: 0 142 0.0 %

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
#include <teemPng.h>
28
#if TEEM_PNG
29
#include <png.h>
30
#endif
31
32
#define MAGIC "\211PNG"
33
34
static int
35
_nrrdFormatPNG_available(void) {
36
37
#if TEEM_PNG
38
6
  return AIR_TRUE;
39
#else
40
  return AIR_FALSE;
41
#endif
42
}
43
44
static int
45
_nrrdFormatPNG_nameLooksLike(const char *filename) {
46
47
  return airEndsWith(filename, NRRD_EXT_PNG);
48
}
49
50
static int
51
_nrrdFormatPNG_fitsInto(const Nrrd *nrrd, const NrrdEncoding *encoding,
52
                        int useBiff) {
53
  static const char me[]="_nrrdFormatPNG_fitsInto";
54
55
#if !TEEM_PNG  /* ------------------------------------------- */
56
57
  AIR_UNUSED(nrrd);
58
  AIR_UNUSED(encoding);
59
  biffMaybeAddf(useBiff, NRRD,
60
                "%s: %s format not available in this Teem build",
61
                me, nrrdFormatPNG->name);
62
  return AIR_FALSE;
63
64
#else  /* ------------------------------------------- */
65
66
  int ret;
67
68
  if (!( nrrd && encoding )) {
69
    biffMaybeAddf(useBiff, NRRD, "%s: got NULL nrrd (%p) or encoding (%p)",
70
                  me, AIR_CVOIDP(nrrd), AIR_CVOIDP(encoding));
71
    return AIR_FALSE;
72
  }
73
  if (!( nrrdTypeUChar == nrrd->type || nrrdTypeUShort == nrrd->type )) {
74
    biffMaybeAddf(useBiff, NRRD,
75
                  "%s: type must be %s or %s (not %s)", me,
76
                  airEnumStr(nrrdType, nrrdTypeUChar),
77
                  airEnumStr(nrrdType, nrrdTypeUShort),
78
                  airEnumStr(nrrdType, nrrd->type));
79
    return AIR_FALSE;
80
  }
81
  /* else */
82
  /* encoding ignored- always gzip */
83
  if (2 == nrrd->dim) {
84
    /* its a gray-scale image */
85
    ret = AIR_TRUE;
86
  } else if (3 == nrrd->dim) {
87
    if (!( 1 == nrrd->axis[0].size
88
           || 2 == nrrd->axis[0].size
89
           || 3 == nrrd->axis[0].size
90
           || 4 == nrrd->axis[0].size )) {
91
      char stmp[AIR_STRLEN_SMALL];
92
      biffMaybeAddf(useBiff, NRRD,
93
                    "%s: 1st axis size is %s, not 1, 2, 3, or 4", me,
94
                    airSprintSize_t(stmp, nrrd->axis[0].size));
95
      return AIR_FALSE;
96
    }
97
    /* else */
98
    ret = AIR_TRUE;
99
  } else {
100
    biffMaybeAddf(useBiff, NRRD,
101
                  "%s: dimension is %d, not 2 or 3", me, nrrd->dim);
102
    return AIR_FALSE;
103
  }
104
  return ret;
105
106
#endif   /* ------------------------------------------- */
107
}
108
109
static int
110
_nrrdFormatPNG_contentStartsLike(NrrdIoState *nio) {
111
112
  return !strcmp(MAGIC, nio->line);
113
}
114
115
#if TEEM_PNG
116
static void
117
_nrrdErrorHandlerPNG (png_structp png, png_const_charp message)
118
{
119
  static const char me[]="_nrrdErrorHandlerPNG";
120
  /* add PNG error message to biff */
121
  biffAddf(NRRD, "%s: PNG error: %s", me, message);
122
  /* longjmp back to the setjmp, return 1 */
123
  longjmp(png_jmpbuf(png), 1);
124
}
125
126
static void
127
_nrrdWarningHandlerPNG (png_structp png, png_const_charp message)
128
{
129
  static const char me[]="_nrrdWarningHandlerPNG";
130
  AIR_UNUSED(png);
131
  /* add the png warning message to biff */
132
  biffAddf(NRRD, "%s: PNG warning: %s", me, message);
133
  /* no longjump, execution continues */
134
}
135
136
/* we need to use the file I/O callbacks on windows
137
   to make sure we can mix VC6 libpng with VC7 Teem */
138
#ifdef _WIN32
139
static void
140
_nrrdReadDataPNG (png_structp png, png_bytep data, png_size_t len)
141
{
142
  png_size_t read;
143
  read = (png_size_t)fread(data, (png_size_t)1, len, (FILE*)png_get_io_ptr(png));
144
  if (read != len) png_error(png, "file read error");
145
}
146
static void
147
_nrrdWriteDataPNG (png_structp png, png_bytep data, png_size_t len)
148
{
149
  png_size_t written;
150
  written = fwrite(data, 1, len, (FILE*)png_get_io_ptr(png));
151
  if (written != len) png_error(png, "file write error");
152
}
153
154
static void
155
_nrrdFlushDataPNG (png_structp png)
156
{
157
  FILE *io_ptr = png_get_io_ptr(png);
158
  if (io_ptr != NULL) fflush(io_ptr);
159
}
160
#endif /* _WIN32 */
161
#endif /* TEEM_PNG */
162
163
static int
164
_nrrdFormatPNG_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) {
165
  static const char me[]="_nrrdFormatPNG_read";
166
#if TEEM_PNG
167
  png_structp png;
168
  png_infop info;
169
  png_bytep *row;
170
  png_uint_32 width, height, rowsize, hi;
171
  png_text* txt;
172
  int depth, type, i, channels, numtxt, ret;
173
  int ntype, ndim;
174
  size_t nsize[3];
175
#endif /* TEEM_PNG */
176
177
  AIR_UNUSED(file);
178
  AIR_UNUSED(nrrd);
179
  if (!_nrrdFormatPNG_contentStartsLike(nio)) {
180
    biffAddf(NRRD, "%s: this doesn't look like a %s file", me,
181
             nrrdFormatPNG->name);
182
    return 1;
183
  }
184
185
#if TEEM_PNG
186
  /* create png struct with the error handlers above */
187
  png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
188
                               _nrrdErrorHandlerPNG,
189
                               _nrrdWarningHandlerPNG);
190
  if (png == NULL) {
191
    biffAddf(NRRD, "%s: failed to create PNG read struct", me);
192
    return 1;
193
  }
194
  /* create image info struct */
195
  info = png_create_info_struct(png);
196
  if (info == NULL) {
197
    png_destroy_read_struct(&png, NULL, NULL);
198
    biffAddf(NRRD, "%s: failed to create PNG image info struct", me);
199
    return 1;
200
  }
201
  /* set up png style error handling */
202
  if (setjmp(png_jmpbuf(png))) {
203
    /* the error is reported inside the handler,
204
       but we still need to clean up and return */
205
    png_destroy_read_struct(&png, &info, NULL);
206
    return 1;
207
  }
208
  /* initialize png I/O */
209
#ifdef _WIN32
210
  png_set_read_fn(png, (png_voidp)file, _nrrdReadDataPNG);
211
#else
212
  png_init_io(png, file);
213
#endif
214
  /* if we are here, we have already read 6 bytes from the file */
215
  png_set_sig_bytes(png, 6);
216
  /* png_read_info() returns all information from the file
217
     before the first data chunk */
218
  png_read_info(png, info);
219
  png_get_IHDR(png, info, &width, &height, &depth, &type,
220
               NULL, NULL, NULL);
221
  /* expand paletted colors into rgb triplets */
222
  if (type == PNG_COLOR_TYPE_PALETTE)
223
    png_set_palette_to_rgb(png);
224
  /* expand grayscale images to 8 bits from 1, 2, or 4 bits */
225
  if (type == PNG_COLOR_TYPE_GRAY && depth < 8)
226
#if PNG_LIBPNG_VER_MINOR > 1
227
    png_set_expand_gray_1_2_4_to_8(png);
228
#else
229
    png_set_expand(png);
230
#endif
231
  /* expand paletted or rgb images with transparency to full alpha
232
     channels so the data will be available as rgba quartets */
233
  if (png_get_valid(png, info, PNG_INFO_tRNS))
234
    png_set_tRNS_to_alpha(png);
235
  /* fix endianness for 16 bit formats */
236
  if (depth > 8 && airMyEndian() == airEndianLittle)
237
    png_set_swap(png);
238
#if 0
239
  /* HEY GLK asks why is this commented out? */
240
  /* set up gamma correction */
241
  /* NOTE: screen_gamma is platform dependent,
242
     it can hardwired or set from a parameter/environment variable */
243
  if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
244
    /* if the image has sRGB info,
245
       pass in standard nrrd file gamma 1.0 */
246
    png_set_gamma(png_ptr, screen_gamma, 1.0);
247
  } else {
248
    double gamma;
249
    /* set image gamma if present */
250
    if (png_get_gAMA(png, info, &gamma))
251
      png_set_gamma(png, screen_gamma, gamma);
252
    else
253
      png_set_gamma(png, screen_gamma, 1.0);
254
  }
255
#endif
256
  /* update reader */
257
  png_read_update_info(png, info);
258
  /* allocate memory for the image data */
259
  ntype = depth > 8 ? nrrdTypeUShort : nrrdTypeUChar;
260
  switch (type) {
261
  case PNG_COLOR_TYPE_GRAY:
262
    ndim = 2; nsize[0] = width; nsize[1] = height;
263
    nsize[2] = 1;  /* to simplify code below */
264
    break;
265
  case PNG_COLOR_TYPE_GRAY_ALPHA:
266
    ndim = 3; nsize[0] = 2; nsize[1] = width; nsize[2] = height;
267
    break;
268
  case PNG_COLOR_TYPE_RGB:
269
    ndim = 3; nsize[0] = 3; nsize[1] = width; nsize[2] = height;
270
    break;
271
  case PNG_COLOR_TYPE_RGB_ALPHA:
272
    ndim = 3; nsize[0] = 4; nsize[1] = width; nsize[2] = height;
273
    break;
274
  case PNG_COLOR_TYPE_PALETTE:
275
    /* TODO: merge this with the outer switch, needs to be tested */
276
    channels = png_get_channels(png, info);
277
    if (channels < 2) {
278
      ndim = 2; nsize[0] = width; nsize[1] = height;
279
    } else {
280
      ndim = 3; nsize[0] = channels; nsize[1] = width; nsize[2] = height;
281
    }
282
    break;
283
  default:
284
    png_destroy_read_struct(&png, &info, NULL);
285
    biffAddf(NRRD, "%s: unknown png type: %d", me, type);
286
    return 1;
287
    break;
288
  }
289
  if (nio->oldData
290
      && (nio->oldDataSize
291
          == (size_t)(nrrdTypeSize[ntype]*nsize[0]*nsize[1]*nsize[2]))) {
292
    ret = nrrdWrap_nva(nrrd, nio->oldData, ntype, ndim, nsize);
293
  } else {
294
    ret = nrrdMaybeAlloc_nva(nrrd, ntype, ndim, nsize);
295
  }
296
  if (ret) {
297
    png_destroy_read_struct(&png, &info, NULL);
298
    biffAddf(NRRD, "%s: failed to allocate nrrd", me);
299
    return 1;
300
  }
301
  /* query row size */
302
  rowsize = png_get_rowbytes(png, info);
303
  /* check byte size */
304
  if (nrrdElementNumber(nrrd)*nrrdElementSize(nrrd) != height*rowsize) {
305
    png_destroy_read_struct(&png, &info, NULL);
306
    biffAddf(NRRD, "%s: size mismatch", me);
307
    return 1;
308
  }
309
  /* set up row pointers */
310
  row = (png_bytep*)malloc(sizeof(png_bytep)*height);
311
  for (hi=0; hi<height; hi++) {
312
    row[hi] = &((png_bytep)nrrd->data)[hi*rowsize];
313
  }
314
  /* read the entire image in one pass */
315
  png_read_image(png, row);
316
  /* read all text fields from the text chunk */
317
  numtxt = png_get_text(png, info, &txt, NULL);
318
  for (i=0; i<numtxt; i++) {
319
    if (!strcmp(txt[i].key, NRRD_PNG_FIELD_KEY)) {
320
      nio->pos = 0;
321
      /* Reading PNGs teaches Gordon that his scheme for parsing nrrd header
322
         information is inappropriately specific to reading PNMs and NRRDs,
323
         since in this case the text from which we parse a nrrd field
324
         descriptor did NOT come from a line of text as read by
325
         _nrrdOneLine */
326
      nio->line = (char *)airFree(nio->line);
327
      nio->line = airStrdup(txt[i].text);
328
      ret = _nrrdReadNrrdParseField(nio, AIR_FALSE);
329
      if (ret) {
330
        const char* fs = airEnumStr(nrrdField, ret);
331
        if (nrrdField_comment == ret) {
332
          ret = 0;
333
          goto plain;
334
        }
335
        if (!_nrrdFieldValidInImage[ret]) {
336
          if (1 <= nrrdStateVerboseIO) {
337
            fprintf(stderr, "(%s: field \"%s\" (not allowed in PNG) "
338
                    "--> plain comment)\n", me, fs);
339
          }
340
          ret = 0;
341
          goto plain;
342
        }
343
        if (!nio->seen[ret]
344
            && nrrdFieldInfoParse[ret](file, nrrd, nio, AIR_FALSE)) {
345
          if (1 <= nrrdStateVerboseIO) {
346
            fprintf(stderr, "(%s: unparsable info \"%s\" for field \"%s\" "
347
                    "--> plain comment)\n", me, nio->line + nio->pos, fs);
348
          }
349
          ret = 0;
350
          goto plain;
351
        }
352
        nio->seen[ret] = AIR_TRUE;
353
      plain:
354
        if (!ret) {
355
          if (nrrdCommentAdd(nrrd, nio->line)) {
356
            png_destroy_read_struct(&png, &info, NULL);
357
            biffAddf(NRRD, "%s: couldn't add comment", me);
358
            return 1;
359
          }
360
        }
361
      }
362
    } else if (!strcmp(txt[i].key, NRRD_PNG_COMMENT_KEY)) {
363
      char *p, *c;
364
      c = airStrtok(txt[i].text, "\n", &p);
365
      while (c) {
366
        if (nrrdCommentAdd(nrrd, c)) {
367
          png_destroy_read_struct(&png, &info, NULL);
368
          biffAddf(NRRD, "%s: couldn't add comment", me);
369
          return 1;
370
        }
371
        c = airStrtok(NULL, "\n", &p);
372
      }
373
    } else {
374
      if (nrrdKeyValueAdd(nrrd, txt[i].key, txt[i].text)) {
375
        png_destroy_read_struct(&png, &info, NULL);
376
        biffAddf(NRRD, "%s: couldn't add key/value pair", me);
377
        return 1;
378
      }
379
    }
380
  }
381
  /* finish reading */
382
  png_read_end(png, info);
383
  /* clean up */
384
  row = (png_byte**)airFree(row);
385
  png_destroy_read_struct(&png, &info, NULL);
386
387
  return 0;
388
#else
389
  biffAddf(NRRD, "%s: Sorry, this nrrd not compiled with PNG enabled", me);
390
  return 1;
391
#endif
392
}
393
394
static int
395
_nrrdFormatPNG_write(FILE *file, const Nrrd *nrrd, NrrdIoState *nio) {
396
  static const char me[]="_nrrdFormatPNG_write";
397
#if TEEM_PNG
398
  int fi, depth, type, csize;
399
  unsigned int jj, numtxt, txtidx;
400
  png_structp png;
401
  png_infop info;
402
  png_bytep *row;
403
  png_uint_32 width, height, rowsize, hi;
404
  png_text *txt;
405
  char *key, *value;
406
407
  /* no need to check type and format, done in FitsInFormat */
408
  /* create png struct with the error handlers above */
409
  png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
410
                                _nrrdErrorHandlerPNG,
411
                                _nrrdWarningHandlerPNG);
412
  if (png == NULL) {
413
    biffAddf(NRRD, "%s: failed to create PNG write struct (compiled with "
414
             "PNG_LIBPNG_VER_STRING=" PNG_LIBPNG_VER_STRING ")", me);
415
    return 1;
416
  }
417
  /* create image info struct */
418
  info = png_create_info_struct(png);
419
  if (info == NULL) {
420
    png_destroy_write_struct(&png, NULL);
421
    biffAddf(NRRD, "%s: failed to create PNG image info struct", me);
422
    return 1;
423
  }
424
  /* set up error png style error handling */
425
  if (setjmp(png_jmpbuf(png)))
426
  {
427
    /* the error is reported inside the error handler,
428
       but we still need to clean up an return with an error */
429
    png_destroy_write_struct(&png, &info);
430
    return 1;
431
  }
432
  /* initialize png I/O */
433
#ifdef _WIN32
434
  png_set_write_fn(png, file, _nrrdWriteDataPNG, _nrrdFlushDataPNG);
435
#else
436
  png_init_io(png, file);
437
#endif
438
  /* calculate depth, width, height, and row size */
439
  depth = nrrd->type == nrrdTypeUChar ? 8 : 16;
440
  switch (nrrd->dim) {
441
    char stmp[AIR_STRLEN_SMALL];
442
    case 2: /* g only */
443
    width = nrrd->axis[0].size;
444
    height = nrrd->axis[1].size;
445
    type = PNG_COLOR_TYPE_GRAY;
446
    rowsize = width*nrrdElementSize(nrrd);
447
    break;
448
    case 3: /* g, ga, rgb, rgba */
449
    width = nrrd->axis[1].size;
450
    height = nrrd->axis[2].size;
451
    rowsize = width*nrrd->axis[0].size*nrrdElementSize(nrrd);
452
    switch (nrrd->axis[0].size) {
453
      case 1:
454
      type = PNG_COLOR_TYPE_GRAY;
455
      break;
456
      case 2:
457
      type = PNG_COLOR_TYPE_GRAY_ALPHA;
458
      break;
459
      case 3:
460
      type = PNG_COLOR_TYPE_RGB;
461
      break;
462
      case 4:
463
      type = PNG_COLOR_TYPE_RGB_ALPHA;
464
      break;
465
      default:
466
      png_destroy_write_struct(&png, &info);
467
      biffAddf(NRRD, "%s: nrrd->axis[0].size (%s) not compatible with PNG", me,
468
               airSprintSize_t(stmp, nrrd->axis[0].size));
469
      return 1;
470
      break;
471
    }
472
    break;
473
    default:
474
    png_destroy_write_struct(&png, &info);
475
    biffAddf(NRRD, "%s: dimension (%d) not compatible with PNG",
476
             me, nrrd->dim);
477
    return 1;
478
    break;
479
  }
480
  /* set image header info */
481
  png_set_IHDR(png, info, width, height, depth, type,
482
               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
483
               PNG_FILTER_TYPE_BASE);
484
  /* calculate numtxt and allocate txt[] array */
485
  numtxt = 0;
486
  for (fi=nrrdField_unknown+1; fi<nrrdField_last; fi++) {
487
    if (_nrrdFieldValidInImage[fi] && _nrrdFieldInteresting(nrrd, nio, fi)) {
488
      numtxt++;
489
    }
490
  }
491
  for (jj=0; jj<nrrdKeyValueSize(nrrd); jj++) {
492
    nrrdKeyValueIndex(nrrd, &key, &value, jj);
493
    /* HEY: why is the NULL check needed?? */
494
    if (NULL != key && NULL != value) {
495
      numtxt++;
496
    }
497
    free(key);
498
    free(value);
499
    key = NULL;
500
    value = NULL;
501
  }
502
  if (nrrd->cmtArr->len > 0) {
503
    /* all comments are put into single text field */
504
    numtxt += 1;
505
  }
506
  if (0 == numtxt) {
507
    txt = NULL;
508
  } else {
509
    txt = AIR_CAST(png_text *, calloc(numtxt, sizeof(png_text)));
510
    /* add nrrd fields to the text chunk */
511
    csize = 0;
512
    txtidx = 0;
513
    for (fi=nrrdField_unknown+1; fi<nrrdField_last; fi++) {
514
      if (_nrrdFieldValidInImage[fi] && _nrrdFieldInteresting(nrrd, nio, fi)) {
515
        txt[txtidx].key = airStrdup(NRRD_PNG_FIELD_KEY);
516
        txt[txtidx].compression = PNG_TEXT_COMPRESSION_NONE;
517
        _nrrdSprintFieldInfo(&(txt[txtidx].text), "", nrrd, nio, fi,
518
                             (3 == nrrd->dim && 1 == nrrd->axis[0].size));
519
        txtidx++;
520
      }
521
    }
522
    /* add nrrd key/value pairs to the chunk */
523
    for (jj=0; jj<nrrdKeyValueSize(nrrd); jj++) {
524
      nrrdKeyValueIndex(nrrd, &key, &value, jj);
525
      if (NULL != key && NULL != value) {
526
        txt[txtidx].key = key;
527
        txt[txtidx].text = value;
528
        txt[txtidx].compression = PNG_TEXT_COMPRESSION_NONE;
529
        txtidx++;
530
      }
531
    }
532
    /* add nrrd comments as a single text field */
533
    if (nrrd->cmtArr->len > 0) {
534
      txt[txtidx].key = airStrdup(NRRD_PNG_COMMENT_KEY);
535
      txt[txtidx].compression = PNG_TEXT_COMPRESSION_NONE;
536
      for (jj=0; jj<nrrd->cmtArr->len; jj++) {
537
        csize += airStrlen(nrrd->cmt[jj]) + 1;
538
      }
539
      txt[txtidx].text = (png_charp)malloc(csize + 1);
540
      txt[txtidx].text[0] = 0;
541
      for (jj=0; jj<nrrd->cmtArr->len; jj++) {
542
        strcat(txt[txtidx].text, nrrd->cmt[jj]);
543
        strcat(txt[txtidx].text, "\n");
544
      }
545
      txtidx++;
546
    }
547
    png_set_text(png, info, txt, numtxt);
548
  }
549
  /* write header */
550
  png_write_info(png, info);
551
  /* fix endianness for 16 bit formats */
552
  if (depth > 8 && airMyEndian() == airEndianLittle) {
553
    png_set_swap(png);
554
  }
555
  /* set up row pointers */
556
  row = (png_bytep*)malloc(sizeof(png_bytep)*height);
557
  for (hi=0; hi<height; hi++) {
558
    row[hi] = &((png_bytep)nrrd->data)[hi*rowsize];
559
  }
560
  png_set_rows(png, info, row);
561
  /* write the entire image in one pass */
562
  png_write_image(png, row);
563
  /* finish writing */
564
  png_write_end(png, info);
565
  /* clean up */
566
  if (txt) {
567
    for (jj=0; jj<numtxt; jj++) {
568
      txt[jj].key = (char *)airFree(txt[jj].key);
569
      txt[jj].text = (char *)airFree(txt[jj].text);
570
    }
571
    free(txt);
572
  }
573
  row = (png_byte**)airFree(row);
574
  png_destroy_write_struct(&png, &info);
575
576
  return 0;
577
#else
578
  AIR_UNUSED(file);
579
  AIR_UNUSED(nrrd);
580
  AIR_UNUSED(nio);
581
  biffAddf(NRRD, "%s: Sorry, this nrrd not compiled with PNG enabled", me);
582
  return 1;
583
#endif
584
}
585
586
const NrrdFormat
587
_nrrdFormatPNG = {
588
  "PNG",
589
  AIR_TRUE,   /* isImage */
590
  AIR_TRUE,   /* readable */
591
  AIR_FALSE,  /* usesDIO */
592
  _nrrdFormatPNG_available,
593
  _nrrdFormatPNG_nameLooksLike,
594
  _nrrdFormatPNG_fitsInto,
595
  _nrrdFormatPNG_contentStartsLike,
596
  _nrrdFormatPNG_read,
597
  _nrrdFormatPNG_write
598
};
599
600
const NrrdFormat *const
601
nrrdFormatPNG = &_nrrdFormatPNG;