/** * pnmout.c * PNM file output. * * Copyright (C) 2002-2008 Cosmin Truta. * This file is part of the pnmio library, distributed under the zlib license. * For conditions of distribution and use, see copyright notice in pnmio.h. **/ #include #include #include #include "pnmio.h" /** * Validates a PNM header structure and writes it to a file stream. * Returns 1 on success, 0 on validation failure, or -1 on output failure. **/ int pnm_fput_header(const pnm_struct *pnm_ptr, FILE *stream) { unsigned int format = pnm_ptr->format; unsigned int depth = pnm_ptr->depth; unsigned int width = pnm_ptr->width; unsigned int height = pnm_ptr->height; unsigned int maxval = pnm_ptr->maxval; int result; /* validate the info structure */ if (!pnm_is_valid(pnm_ptr)) return 0; /* write the PNM file header */ switch (format) { case PNM_P1: case PNM_P4: result = fprintf(stream, "P%c\n%u %u\n", format + '0', width, height); break; case PNM_P2: case PNM_P3: case PNM_P5: case PNM_P6: result = fprintf(stream, "P%c\n%u %u\n%u\n", format + '0', width, height, maxval); break; case PNM_P7: result = fprintf(stream, "P7\nDEPTH %u\nWIDTH %u\nHEIGHT %u\nMAXVAL %u\nENDHDR\n", depth, width, height, maxval); break; default: errno = EINVAL; return 0; } /* check the result */ return (result > 0) ? 1 : -1; } /** * Writes an array of PNM sample values to a file stream. * The values are written in the format specified by pnm_ptr->format. * The array length is pnm_ptr->depth * pnm_ptr->width * num_rows. * The validity check performed on the PNM structure is only partial. * Returns 1 on success, 0 on validation failure, or -1 on output failure. **/ int pnm_fput_values(const pnm_struct *pnm_ptr, const unsigned int *sample_values, unsigned int num_rows, FILE *stream) { unsigned int format = pnm_ptr->format; unsigned int depth = pnm_ptr->depth; unsigned int width = pnm_ptr->width; unsigned int maxval = pnm_ptr->maxval; size_t row_length = (size_t)depth * (size_t)width; size_t num_samples = num_rows * row_length; int ch, mask; size_t i, j; /* write the sample values */ switch (format) { case PNM_P1: for (i = j = 0; i < num_samples; ++i) { if (putc(((sample_values[i] != 0) ? '0' : '1'), stream) == EOF) break; if (++j == row_length) { j = 0; if (putc('\n', stream) == EOF) break; } } break; case PNM_P2: case PNM_P3: for (i = j = 0; i < num_samples; ++i) { if (++j == row_length) j = 0; if (fprintf(stream, (j == 0) ? "%u\n" : "%u ", sample_values[i]) <= 0) break; } break; case PNM_P4: for (i = j = 0; i < num_samples; ) { ch = 0; for (mask = 0x80; mask != 0; mask >>= 1) { if (sample_values[i++] == 0) ch |= mask; if (++j == row_length) { j = 0; break; } } if (putc(ch, stream) == EOF) break; } break; case PNM_P5: case PNM_P6: case PNM_P7: if (maxval <= 0xffU) /* 1 byte per sample */ { for (i = 0; i < num_samples; ++i) { if (putc(sample_values[i] & 0xff, stream) == EOF) break; } } else if (maxval <= 0xffffU) /* 2 bytes per sample */ { for (i = 0; i < num_samples; ++i) { if (putc((sample_values[i] >> 8) & 0xff, stream) == EOF || putc((sample_values[i]) & 0xff, stream) == EOF) break; } } #if PNM_UINT_BIT > 16 else if (maxval <= 0xffffffffU) /* 3 or 4 bytes per sample */ { for (i = 0; i < num_samples; ++i) { if (maxval > 0xffffffU) if (putc((sample_values[i] >> 24) & 0xff, stream) == EOF) break; if (putc((sample_values[i] >> 16) & 0xff, stream) == EOF || putc((sample_values[i] >> 8) & 0xff, stream) == EOF || putc((sample_values[i]) & 0xff, stream) == EOF) break; } } #endif else /* maxval > PNM_UINT_MAX */ { errno = EINVAL; return 0; } break; default: errno = EINVAL; return 0; } /* check the result */ return (i == num_samples) ? 1 : -1; } /** * Writes an array of sample bytes to a raw PNM file stream. * Multi-byte samples are stored in network order, as in the PNM stream. * The byte count is sample_size * pnm_ptr->depth * pnm_ptr->width * num_rows. * The validity check performed on the PNM structure is only partial. * Returns 1 on success, 0 on validation failure, or -1 on output failure. **/ int pnm_fput_bytes(const pnm_struct *pnm_ptr, const unsigned char *sample_bytes, size_t sample_size, unsigned int num_rows, FILE *stream) { unsigned int format = pnm_ptr->format; unsigned int depth = pnm_ptr->depth; unsigned int width = pnm_ptr->width; unsigned int maxval = pnm_ptr->maxval; size_t row_length = (size_t)depth * (size_t)width; size_t num_samples = num_rows * row_length; size_t raw_sample_size; int ch, mask; size_t i, j; /* validate the given sample size */ if (maxval <= 0xffU) raw_sample_size = 1; else if (maxval <= 0xffffU) raw_sample_size = 2; #if PNM_UINT_BIT > 16 else if (maxval <= 0xffffffU) raw_sample_size = 3; else if (maxval <= 0xffffffffU) raw_sample_size = 4; #endif else /* maxval > PNM_UINT_MAX */ raw_sample_size = !sample_size; if (raw_sample_size != sample_size) { errno = EINVAL; return 0; } /* write the raw sample bytes */ switch (format) { case PNM_P4: for (i = j = 0; i < num_samples; ) { ch = 0; for (mask = 0x80; mask != 0; mask >>= 1) { if (sample_bytes[i++] == 0) ch |= mask; if (++j == row_length) { j = 0; break; } } if (putc(ch, stream) == EOF) break; } break; case PNM_P5: case PNM_P6: case PNM_P7: i = fwrite(sample_bytes, sample_size, num_samples, stream); break; default: errno = EINVAL; return 0; } /* check the result */ return (i == num_samples) ? 1 : -1; }