From de7ac5d16dd39779adcc89f0482a98401487eaea Mon Sep 17 00:00:00 2001 From: giraffedata Date: Mon, 20 Dec 2021 02:09:15 +0000 Subject: Add pnm_formatpamtuples, pnm_writepamrowpart git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@4214 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- doc/HISTORY | 2 + lib/libpam.c | 22 +++++- lib/libpamwrite.c | 205 +++++++++++++++++++++++++++++++++++++++++------------- lib/pam.h | 26 +++++++ lib/pm.h | 3 + lib/pmfileio.c | 8 +++ 6 files changed, 215 insertions(+), 51 deletions(-) diff --git a/doc/HISTORY b/doc/HISTORY index e6afc31a..7b9d8901 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -34,6 +34,8 @@ not yet BJH Release 10.97.00 pbmclean: Fix overallocation of memory (waste). + libnetbm: Add pnm_writepamrowpart, pnm_formatPamtuples. + libnetpbm: When validating computable size of width and height, allow for adding up to 10 instead of 2, to account for rounding up to a multiple of 8 in processing bit maps. diff --git a/lib/libpam.c b/lib/libpam.c index 5bc9e007..b24f230e 100644 --- a/lib/libpam.c +++ b/lib/libpam.c @@ -421,6 +421,20 @@ pnm_setpamrow(const struct pam * const pamP, +static void +setSeekableAndRasterPos(struct pam * const pamP) { + + if (pamP->size >= PAM_STRUCT_SIZE(is_seekable)) + pamP->is_seekable = pm_is_seekable(pamP->file); + + if (pamP->size >= PAM_STRUCT_SIZE(raster_pos)) { + if (pamP->is_seekable) + pm_tell2(pamP->file, &pamP->raster_pos, sizeof(pamP->raster_pos)); + } +} + + + #define MAX_LABEL_LENGTH 8 #define MAX_VALUE_LENGTH 255 @@ -950,6 +964,8 @@ pnm_readpaminit(FILE * const file, pamP->plainformat = FALSE; /* See below for complex explanation of why this is FALSE. */ + setSeekableAndRasterPos(pamP); + interpretTupleType(pamP); validateComputableSize(pamP); @@ -1058,8 +1074,6 @@ pnm_writepaminit(struct pam * const pamP) { interpretTupleType(pamP); - pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(opacity_plane)); - switch (PAM_FORMAT_TYPE(pamP->format)) { case PAM_TYPE: /* See explanation below of why we ignore 'pm_plain_output' here. */ @@ -1118,6 +1132,10 @@ pnm_writepaminit(struct pam * const pamP) { pm_error("Invalid format passed to pnm_writepaminit(): %d", pamP->format); } + + setSeekableAndRasterPos(pamP); + + pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(raster_pos)); } diff --git a/lib/libpamwrite.c b/lib/libpamwrite.c index 7edc90dc..0e1ff469 100644 --- a/lib/libpamwrite.c +++ b/lib/libpamwrite.c @@ -71,7 +71,7 @@ writePamPlainPbmRow(const struct pam * const pamP, static void writePamPlainRow(const struct pam * const pamP, - const tuple * const tuplerow) { + const tuple * const tuplerow) { int const samplesPerLine = samplesPerPlainLine(pamP->maxval, pamP->depth, 79); @@ -101,17 +101,25 @@ writePamPlainRow(const struct pam * const pamP, static void -formatPbmRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +formatPbm(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Create the image of 'nTuple' consecutive tuples of a row in the raster of a + raw format PBM image. + Put the image at *outbuf; put the number of bytes of it at *rowSizeP. +-----------------------------------------------------------------------------*/ unsigned char accum; int col; + assert(nTuple <= pamP->width); + accum = 0; /* initial value */ - for (col=0; col < pamP->width; ++col) { + for (col=0; col < nTuple; ++col) { accum |= (tuplerow[col][0] == PAM_PBM_BLACK ? PBM_BLACK : PBM_WHITE) << (7-col%8); @@ -120,12 +128,12 @@ formatPbmRow(const struct pam * const pamP, accum = 0; } } - if (pamP->width % 8 != 0) { - unsigned int const lastByteIndex = pamP->width/8; + if (nTuple % 8 != 0) { + unsigned int const lastByteIndex = nTuple/8; outbuf[lastByteIndex] = accum; *rowSizeP = lastByteIndex + 1; } else - *rowSizeP = pamP->width/8; + *rowSizeP = nTuple/8; } @@ -171,36 +179,40 @@ sampleToBytes4(unsigned char buf[4], static __inline__ void -format1BpsRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +format1Bps(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { /*---------------------------------------------------------------------------- - Create the image of a row in the raster of a raw format Netpbm - image that has one byte per sample (ergo not PBM). + Create the image of 'nTuple' consecutive tuples of a row in the raster of a + raw format Netpbm image that has one byte per sample (ergo not PBM). Put the image at *outbuf; put the number of bytes of it at *rowSizeP. -----------------------------------------------------------------------------*/ int col; unsigned int bufferCursor; + assert(nTuple <= pamP->width); + bufferCursor = 0; /* initial value */ - for (col = 0; col < pamP->width; ++col) { + for (col = 0; col < nTuple; ++col) { unsigned int plane; for (plane=0; plane < pamP->depth; ++plane) outbuf[bufferCursor++] = (unsigned char)tuplerow[col][plane]; } - *rowSizeP = pamP->width * 1 * pamP->depth; + *rowSizeP = nTuple * 1 * pamP->depth; } static __inline__ void -format2BpsRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +format2Bps(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { /*---------------------------------------------------------------------------- Analogous to format1BpsRow(). -----------------------------------------------------------------------------*/ @@ -209,24 +221,27 @@ format2BpsRow(const struct pam * const pamP, int col; unsigned int bufferCursor; + assert(nTuple <= pamP->width); + bufferCursor = 0; /* initial value */ - for (col=0; col < pamP->width; ++col) { + for (col=0; col < nTuple; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) sampleToBytes2(ob[bufferCursor++], tuplerow[col][plane]); } - *rowSizeP = pamP->width * 2 * pamP->depth; + *rowSizeP = nTuple * 2 * pamP->depth; } static __inline__ void -format3BpsRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +format3Bps(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { /*---------------------------------------------------------------------------- Analogous to format1BpsRow(). -----------------------------------------------------------------------------*/ @@ -235,24 +250,27 @@ format3BpsRow(const struct pam * const pamP, int col; unsigned int bufferCursor; + assert(nTuple <= pamP->width); + bufferCursor = 0; /* initial value */ - for (col=0; col < pamP->width; ++col) { + for (col=0; col < nTuple; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) sampleToBytes3(ob[bufferCursor++], tuplerow[col][plane]); } - *rowSizeP = pamP->width * 3 * pamP->depth; + *rowSizeP = nTuple * 3 * pamP->depth; } static __inline__ void -format4BpsRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +format4Bps(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { /*---------------------------------------------------------------------------- Analogous to format1BpsRow(). -----------------------------------------------------------------------------*/ @@ -261,41 +279,49 @@ format4BpsRow(const struct pam * const pamP, int col; unsigned int bufferCursor; + assert(nTuple <= pamP->width); + bufferCursor = 0; /* initial value */ - for (col=0; col < pamP->width; ++col) { + for (col=0; col < nTuple; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) sampleToBytes4(ob[bufferCursor++], tuplerow[col][plane]); } - *rowSizeP = pamP->width * 4 * pamP->depth; + *rowSizeP = nTuple * 4 * pamP->depth; } void -pnm_formatpamrow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { -/*---------------------------------------------------------------------------- - Create the image of a row in the raster of a raw (not plain) format - Netpbm image, as described by *pamP and tuplerow[]. Put the image - at *outbuf. +pnm_formatpamtuples(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- Create the image of 'nTuple' consecutive tuples of a row in the raster of a + raw (not plain) format Netpbm image, as described by *pamP and tuplerow[]. + Put the image at *outbuf. 'outbuf' must be the address of space allocated with pnm_allocrowimage(). - We return as *rowSizeP the number of bytes in the row image. + We return as *rowSizeP the number of bytes in the image. -----------------------------------------------------------------------------*/ + if (nTuple > pamP->width) { + pm_error("pnm_formatpamtuples called to write more tuples (%u) " + "than the width of a row (%u)", + nTuple, pamP->width); + } + if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) - formatPbmRow(pamP, tuplerow, outbuf, rowSizeP); + formatPbm(pamP, tuplerow, outbuf, nTuple, rowSizeP); else { switch(pamP->bytes_per_sample){ - case 1: format1BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; - case 2: format2BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; - case 3: format3BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; - case 4: format4BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; + case 1: format1Bps(pamP, tuplerow, outbuf, nTuple, rowSizeP); break; + case 2: format2Bps(pamP, tuplerow, outbuf, nTuple, rowSizeP); break; + case 3: format3Bps(pamP, tuplerow, outbuf, nTuple, rowSizeP); break; + case 4: format4Bps(pamP, tuplerow, outbuf, nTuple, rowSizeP); break; default: pm_error("invalid bytes per sample passed to " "pnm_formatpamrow(): %u", pamP->bytes_per_sample); @@ -305,6 +331,19 @@ pnm_formatpamrow(const struct pam * const pamP, +void +pnm_formatpamrow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Same as 'pnm_formatpamtuples', except formats an entire row. +-----------------------------------------------------------------------------*/ + pnm_formatpamtuples(pamP, tuplerow, outbuf, pamP->width, rowSizeP); +} + + + static void writePamRawRow(const struct pam * const pamP, const tuple * const tuplerow, @@ -397,6 +436,74 @@ pnm_writepamrowmult(const struct pam * const pamP, +void +pnm_writepamrowpart(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned int const firstRow, + unsigned int const firstCol, + unsigned int const rowCt, + unsigned int const colCt) { +/*---------------------------------------------------------------------------- + Write part of multiple consecutive rows to the file. + + For each of 'rowCt' consecutive rows starting at 'firstRow', write the + 'colCt' columns starting at 'firstCol'. The tuples to write are those in + 'tuplerow', starting at the beginning of 'tuplerow'. + + Fail if the file is not seekable (or not known to be seekable) or the + output format is not raw (i.e. is plain) or the output format is PBM. +-----------------------------------------------------------------------------*/ + unsigned int const bytesPerTuple = pamP->depth * pamP->bytes_per_sample; + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + unsigned int tupleImageSize; + unsigned char * outbuf; /* malloc'ed */ + + if (pamP->len < PAM_STRUCT_SIZE(raster_pos) || !pamP->raster_pos) + pm_error("pnm_writepamrowpart called on nonseekable file"); + + if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) + pm_error("pnm_witepamrowpart called for PBM image"); + + if (pm_plain_output || pamP->plainformat) + pm_error("pnm_writepamrowpart called for plain format image"); + + outbuf = pnm_allocrowimage(pamP); + + pnm_formatpamtuples(pamP, tuplerow, outbuf, colCt, &tupleImageSize); + + if (setjmp(jmpbuf) != 0) { + pnm_freerowimage(outbuf); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = firstRow; row < firstRow + rowCt; ++row) { + pm_filepos const firstTuplePos = + pamP->raster_pos + + (row * pamP->width + firstCol) * bytesPerTuple; + size_t bytesWritten; + + pm_seek2(pamP->file, &firstTuplePos, sizeof(firstTuplePos)); + + bytesWritten = fwrite(outbuf, 1, tupleImageSize, pamP->file); + + if (bytesWritten != tupleImageSize) + pm_error("fwrite() failed to write %u image tuples " + "to the file. errno=%d (%s)", + colCt, errno, strerror(errno)); + } + pm_setjmpbuf(origJmpbufP); + } + pnm_freerowimage(outbuf); +} + + + void pnm_writepam(struct pam * const pamP, tuple ** const tuplearray) { diff --git a/lib/pam.h b/lib/pam.h index aebf529a..88b8c2bd 100644 --- a/lib/pam.h +++ b/lib/pam.h @@ -136,6 +136,18 @@ struct pam { /* The plane number of the opacity plane; meaningless if 'haveOpacity' is false or 'visual' is false. */ + int is_seekable; /* boolean */ + /* The file 'file' is seekable -- you can set the position of next + reading or writing to anything and any time. + + If libnetpbm cannot tell if it is seekable or not, this is false. + */ + pm_filepos raster_pos; + /* The file position of the raster (which is also the end of the + header). + + Meaningless if 'is_seekable' is false. + */ }; #define PAM_HAVE_ALLOCATION_DEPTH 1 @@ -340,6 +352,12 @@ pnm_readpam(FILE * const file, void pnm_writepaminit(struct pam * const pamP); +void +pnm_formatpamtuples(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP); void pnm_formatpamrow(const struct pam * const pamP, const tuple * const tuplerow, @@ -354,6 +372,14 @@ pnm_writepamrowmult(const struct pam * const pamP, const tuple * const tuplerow, unsigned int const rptcnt); +void +pnm_writepamrowpart(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned int const firstRow, + unsigned int const firstCol, + unsigned int const rowCt, + unsigned int const colCt); + void pnm_writepam(struct pam * const pamP, tuple ** const tuplearray); diff --git a/lib/pm.h b/lib/pm.h index 3fc92fb4..b3c3d202 100644 --- a/lib/pm.h +++ b/lib/pm.h @@ -386,6 +386,9 @@ pm_bs_short(short const s); long pm_bs_long(long const l); +int +pm_is_seekable(FILE * const fileP); + unsigned int pm_tell(FILE * const fileP); diff --git a/lib/pmfileio.c b/lib/pmfileio.c index 68e649aa..1ed71f18 100644 --- a/lib/pmfileio.c +++ b/lib/pmfileio.c @@ -978,6 +978,14 @@ pm_bs_long(long const l) { +int +pm_is_seekable(FILE * const fP) { + + return isSeekable(fP) ? 1 : 0; +} + + + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wduplicated-cond" -- cgit 1.4.1