blob: b8a95b48a47c7af03e4741e7be9d2cfeca271a7c [file] [log] [blame]
/**
* 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 <errno.h>
#include <stddef.h>
#include <stdio.h>
#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;
}