/* libpgm2.c - pgm utility library part 2 ** ** Copyright (C) 1989 by Jef Poskanzer. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. */ #include #include #include "netpbm/pm_c_util.h" #include "netpbm/mallocvar.h" #include "libpgm.h" #include "pgm.h" void pgm_writepgminit(FILE * const fileP, int const cols, int const rows, gray const maxval, int const forceplain) { bool const plainFormat = forceplain || pm_plain_output; /* For Caller's convenience, we include validating computability of the image parameters, since Caller may be using them in arithmetic after our return. */ pgm_validateComputableSize(cols, rows); pgm_validateComputableMaxval(maxval); if (maxval > PGM_OVERALLMAXVAL && !plainFormat) pm_error("too-large maxval passed to ppm_writepgminit(): %d.\n" "Maximum allowed by the PGM format is %d.", maxval, PGM_OVERALLMAXVAL); fprintf(fileP, "%c%c\n%d %d\n%d\n", PGM_MAGIC1, plainFormat || maxval >= 1<<16 ? PGM_MAGIC2 : RPGM_MAGIC2, cols, rows, maxval ); } static void putus(unsigned short const n, FILE * const fileP) { if (n >= 10) putus(n / 10, fileP); putc('0' + n % 10, fileP); } static void format1bpsRow(const gray * const grayrow, unsigned int const cols, unsigned char * const rowBuffer) { /* single byte samples. */ unsigned int col; unsigned int bufferCursor; bufferCursor = 0; for (col = 0; col < cols; ++col) rowBuffer[bufferCursor++] = grayrow[col]; } static void format2bpsRow(const gray * const grayrow, unsigned int const cols, unsigned char * const rowBuffer) { /* two byte samples. */ unsigned int col; unsigned int bufferCursor; bufferCursor = 0; for (col = 0; col < cols; ++col) { gray const val = grayrow[col]; rowBuffer[bufferCursor++] = val >> 8; rowBuffer[bufferCursor++] = (unsigned char) val; } } static void writepgmrowraw(FILE * const fileP, const gray * const grayrow, unsigned int const cols, gray const maxval) { unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; unsigned int const bytesPerRow = cols * bytesPerSample; unsigned char * rowBuffer; ssize_t rc; MALLOCARRAY(rowBuffer, bytesPerRow); if (rowBuffer == NULL) pm_error("Unable to allocate memory for row buffer " "for %u columns", cols); if (maxval < 256) format1bpsRow(grayrow, cols, rowBuffer); else format2bpsRow(grayrow, cols, rowBuffer); rc = fwrite(rowBuffer, 1, bytesPerRow, fileP); if (rc < 0) pm_error("Error writing row. fwrite() errno=%d (%s)", errno, strerror(errno)); else { size_t const bytesWritten = rc; if (bytesWritten != bytesPerRow) pm_error("Error writing row. Short write of %u bytes " "instead of %u", (unsigned)bytesWritten, bytesPerRow); } free(rowBuffer); } static void writepgmrowplain(FILE * const fileP, const gray * const grayrow, unsigned int const cols, gray const maxval) { int col, charcount; charcount = 0; for (col = 0; col < cols; ++col) { if (charcount >= 65) { putc('\n', fileP); charcount = 0; } else if (charcount > 0) { putc(' ', fileP); ++charcount; } #ifdef DEBUG if (grayrow[col] > maxval) pm_error("value out of bounds (%u > %u)", grayrow[col], maxval); #endif /*DEBUG*/ putus((unsigned short)grayrow[col], fileP); charcount += 3; } if (charcount > 0) putc('\n', fileP); } void pgm_writepgmrow(FILE * const fileP, const gray * const grayrow, int const cols, gray const maxval, int const forceplain) { if (forceplain || pm_plain_output || maxval >= 1<<16) writepgmrowplain(fileP, grayrow, cols, maxval); else writepgmrowraw(fileP, grayrow, cols, maxval); } void pgm_writepgm(FILE * const fileP, gray ** const grays, int const cols, int const rows, gray const maxval, int const forceplain) { unsigned int row; pgm_writepgminit(fileP, cols, rows, maxval, forceplain); for (row = 0; row < rows; ++row) pgm_writepgmrow(fileP, grays[row], cols, maxval, forceplain); }