diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2017-09-10 02:37:21 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2017-09-10 02:37:21 +0000 |
commit | 359ed5e3cc166eb83233b13da348ddd630cb5359 (patch) | |
tree | 4e8122c5595d0de94208ff6b611e00ee70e9eec0 | |
parent | a77773083d63fdf786f6f26fe382bd05db7de8e4 (diff) | |
download | netpbm-mirror-359ed5e3cc166eb83233b13da348ddd630cb5359.tar.gz netpbm-mirror-359ed5e3cc166eb83233b13da348ddd630cb5359.tar.xz netpbm-mirror-359ed5e3cc166eb83233b13da348ddd630cb5359.zip |
Fix various incorrect output problems
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@3054 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r-- | converter/other/ipdb.c | 4 | ||||
-rw-r--r-- | converter/other/pamtopdbimg.c | 152 | ||||
-rw-r--r-- | converter/other/pdbimgtopam.c | 33 | ||||
-rw-r--r-- | doc/HISTORY | 4 | ||||
-rw-r--r-- | lib/util/runlength.c | 67 | ||||
-rw-r--r-- | lib/util/runlength.h | 3 |
6 files changed, 120 insertions, 143 deletions
diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c index 1d72cc31..5e4dc82e 100644 --- a/converter/other/ipdb.c +++ b/converter/other/ipdb.c @@ -283,8 +283,8 @@ ipdb_image_alloc(const char * const name, if (w != 0 && h != 0) { MALLOCARRAY(imgP->data, w * h); - if (imgP->data) { - MEMSZERO(imgP->data); + if (imgP->data != NULL) { + memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h); } else failed = true; } diff --git a/converter/other/pamtopdbimg.c b/converter/other/pamtopdbimg.c index 6454292e..ce2f7659 100644 --- a/converter/other/pamtopdbimg.c +++ b/converter/other/pamtopdbimg.c @@ -38,6 +38,7 @@ #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" +#include "runlength.h" #include "pam.h" #include "ipdb.h" @@ -128,6 +129,8 @@ parseCommandLine(int argc, const char ** argv, cmdlineP->inputFileName = argv[1]; else pm_error("Program takes at most one argument: input file name"); + + free(option_def); } @@ -238,91 +241,6 @@ textWrite(TEXT * const textP, -typedef struct { - unsigned int match; - uint8_t buf[128]; - int mode; - size_t len; - size_t used; - uint8_t * p; -} RLE; -#define MODE_MATCH 0 -#define MODE_LIT 1 -#define MODE_NONE 2 - -#define reset(r) { \ - (r)->match = 0xffff; \ - (r)->mode = MODE_NONE; \ - (r)->len = 0; \ - } - - - -static void -putMatch(RLE * const rleP, - size_t const n) { - - *rleP->p++ = 0x80 + n - 1; - *rleP->p++ = rleP->match; - rleP->used += 2; - reset(rleP); -} - - - -static void -putLit(RLE * const rleP, - size_t const n) { - - *rleP->p++ = n - 1; - rleP->p = (uint8_t *)memcpy(rleP->p, rleP->buf, n) + n; - rleP->used += n + 1; - reset(rleP); -} - - - -static size_t -compress(const uint8_t * const inData, - size_t const n_in, - uint8_t * const out) { - - static void (*put[])(RLE *, size_t) = {putMatch, putLit}; - RLE rle; - size_t i; - const uint8_t * p; - - MEMSZERO(&rle); - rle.p = out; - reset(&rle); - - for (i = 0, p = &inData[0]; i < n_in; ++i, ++p) { - if (*p == rle.match) { - if (rle.mode == MODE_LIT && rle.len > 1) { - putLit(&rle, rle.len - 1); - ++rle.len; - rle.match = *p; - } - rle.mode = MODE_MATCH; - ++rle.len; - } else { - if (rle.mode == MODE_MATCH) - putMatch(&rle, rle.len); - rle.mode = MODE_LIT; - rle.match = *p; - rle.buf[rle.len++] = *p; - } - if (rle.len == 128) - put[rle.mode](&rle, rle.len); - } - if (rle.len != 0) - put[rle.mode](&rle, rle.len); - - return rle.used; -} - - - static void compressIfRequired(IPDB * const pdbP, int const comp, @@ -334,38 +252,29 @@ compressIfRequired(IPDB * const pdbP, *compressedSizeP = ipdb_img_size(pdbP->i); } else { int const uncompressedSz = ipdb_img_size(pdbP->i); - - /* Allocate for the worst case. */ - size_t const allocSz = (3 * uncompressedSz + 2)/2; - uint8_t * data; - - data = pdbP->i->data; + unsigned char * outbuf; + size_t compressedSz; - MALLOCARRAY(data, allocSz); - - if (data == NULL) - pm_error("Could not get %lu bytes of memory to decompress", - (unsigned long)allocSz); - else { - size_t compressedSz; - compressedSz = compress(pdbP->i->data, uncompressedSz, data); - if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) { - /* Return the uncompressed data */ - free(data); - *compressedDataP = pdbP->i->data; - *compressedSizeP = uncompressedSz; - } else { - pdbP->i->compressed = TRUE; - if (pdbP->i->type == IMG_GRAY16) - pdbP->i->version = 9; - else - pdbP->i->version = 1; - if (pdbP->t != NULL) - pdbP->t->r->offset -= uncompressedSz - compressedSz; - *compressedDataP = data; - *compressedSizeP = compressedSz; - } + pm_rlenc_allocoutbuf(&outbuf, uncompressedSz, PM_RLE_PALMPDB); + + pm_rlenc_compressbyte(pdbP->i->data, outbuf, PM_RLE_PALMPDB, + uncompressedSz, &compressedSz); + if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) { + /* Return the uncompressed data */ + free(outbuf); + *compressedDataP = pdbP->i->data; + *compressedSizeP = uncompressedSz; + } else { + pdbP->i->compressed = TRUE; + if (pdbP->i->type == IMG_GRAY16) + pdbP->i->version = 9; + else + pdbP->i->version = 1; + if (pdbP->t != NULL) + pdbP->t->r->offset -= uncompressedSz - compressedSz; + *compressedDataP = outbuf; + *compressedSizeP = compressedSz; } } } @@ -742,6 +651,14 @@ readtxt(IPDB * const pdbP, pm_error("stat of '%s' failed, errno = %d (%s)", noteFileName, errno, strerror(errno)); + /* The maximum size of a memory block that a Palm can allocate is 64K. + Abort with error if specified note file is any larger. + */ + + if (st.st_size + 1 >= 65535) + pm_error("Note file is too large: %lu bytes", + (unsigned long) st.st_size); + fP = pm_openr(noteFileName); MALLOCARRAY(fileContent, st.st_size + 1); @@ -758,6 +675,8 @@ readtxt(IPDB * const pdbP, pm_close(fP); + fileContent[st.st_size] = 0x00; /* add terminating NUL char */ + /* Chop of trailing newlines */ for (n = strlen(fileContent) - 1; n >= 0 && fileContent[n] == '\n'; --n) fileContent[n] = '\0'; @@ -787,6 +706,9 @@ main(int argc, const char **argv) { case MAYBE: comp = IPDB_COMPMAYBE; break; } + if (strlen(cmdline.title) > 31) + pm_error("Title too long. Max length is 31 characters."); + pdbP = ipdb_alloc(cmdline.title); if (pdbP == NULL) diff --git a/converter/other/pdbimgtopam.c b/converter/other/pdbimgtopam.c index 3cb4129a..67044109 100644 --- a/converter/other/pdbimgtopam.c +++ b/converter/other/pdbimgtopam.c @@ -97,6 +97,8 @@ parseCommandLine(int argc, const char ** argv, cmdlineP->inputFileName = argv[1]; else pm_error("Program takes at most one argument: input file name"); + + free(option_def); } @@ -211,7 +213,8 @@ readCompressed(IMAGE * const imgP, if (end_offset == UNKNOWN_OFFSET) { /* - * Read until EOF. Some of them have an extra zero byte + * Read sufficient amount for worst-case compressed image, + * or until EOF. Some of them have an extra zero byte * dangling off the end. I originally thought this was * an empty note record (even though there was no record * header for it); however, the release notes for Image @@ -221,12 +224,20 @@ readCompressed(IMAGE * const imgP, * this extra byte and ignore it by paying attention to * the image dimensions. */ - MALLOCARRAY(buffer, ipdb_img_size(imgP)); + size_t const maxCompressedSizeWithBloat = ipdb_img_size(imgP) * 2; + /* + * Provide a buffer large enough for the worst case. + * See note in lib/util/runlength.c . + * We do not use pm_rlenc_allocoutbuf() because there is no + * guarantee that the encoder that produced the image was + * efficient. + */ + MALLOCARRAY(buffer, maxCompressedSizeWithBloat); if (buffer == NULL) retval = ENOMEM; else { - dataSize = fread(buffer, 1, ipdb_img_size(imgP), fP); + dataSize = fread(buffer, 1, maxCompressedSizeWithBloat, fP); if (dataSize <= 0) retval = EIO; else @@ -307,6 +318,8 @@ imageReadData(FILE * const fileP, IMAGE * const imgP, uint32_t const end_offset) { + size_t const imageSize = ipdb_img_size(imgP); + int retval; size_t dataSize; uint8_t * buffer; @@ -318,14 +331,24 @@ imageReadData(FILE * const fileP, * Compressed data can cross row boundaries so we decompress * the data here to avoid messiness in the row access functions. */ - if (dataSize != ipdb_img_size(imgP)) { - decompress(buffer, dataSize, ipdb_img_size(imgP), &imgP->data); + if (dataSize < imageSize || imgP->version == 1) { + if (imgP->version == 0) + pm_message("Image header says raster data is uncompressed. " + "Encountered only %u instead of the " + "required %u bytes. Assuming compressed mode.", + (unsigned)dataSize, (unsigned)imageSize); + decompress(buffer, dataSize, imageSize, &imgP->data); if (imgP->data == NULL) retval = ENOMEM; else imgP->compressed = true; free(buffer); } else { + if (dataSize > imageSize) + pm_message("Image header says raster data is uncompressed. " + "Encountered %u instead of the required %u bytes. " + "Assuming uncompressed mode.", + (unsigned)dataSize, (unsigned)imageSize); imgP->compressed = false; imgP->data = buffer; /* Storage at 'buffer' now belongs to *imgP */ diff --git a/doc/HISTORY b/doc/HISTORY index d450d8b8..50dbef6a 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -36,6 +36,10 @@ not yet BJH Release 10.80.00 pgmmake: Fix bug: treats non-numeric gray-level argument as zero. Always broken (Pgmmake was new in Netpbm 10.32, February 2006). + pdbimgtopam, pamtopdbimg: fix various cases of incorrect output, + some always present (programs were new in Netpbm 10.52.00 + (October 2010)). + libnetpbm: pnm_parsecolorn(), pnm_parsecolor(): fix parsing of rgb: color specifications: yields value slightly too dim. Affects many programs. Broken in Netpbm 10.79 (June 2017). diff --git a/lib/util/runlength.c b/lib/util/runlength.c index e5c60db0..a4bb9057 100644 --- a/lib/util/runlength.c +++ b/lib/util/runlength.c @@ -12,8 +12,8 @@ documentation. This software is provided "as is" without express or implied warranty. - Functions pm_rlenc_byte() and pm_rlenc_word() are based on algorithm - originally by Robert A. Knop (rknop@mop.caltech.edu). + Functions pm_rlenc_compressbyte() and pm_rlenc_compressword() are based + on algorithm originally by Robert A. Knop (rknop@mop.caltech.edu). Those who wish to incorporate the code herein into their own programs are strongly discouraged from removing the comments within borders consisting @@ -41,12 +41,12 @@ code. This redundancy bloated the Netpbm package and made maintenance difficult. This maintenance difficulty surfaced as an issue when tests with valgrind revealed multiple memory-related problems in the above programs. - Indeed, just determining which programs do this compression by this method + Indeed, just determining which programs do compression by this method was not simple because of differences in terminology. "Packbits" is often called "run length encoding." In ppmtoilbm the name is "byterun1." - Today, all Netpbm programs that do Packbits compression use the facilities - in this file for it. + Today, all Netpbm programs that do Packbits compression with the exception + of pbmtoppa and pbmtogo use the facilities in this file for it. =============================================================================*/ #include <string.h> @@ -68,11 +68,11 @@ static const char * const errorUndefinedMode = In this simple run-length encoding scheme, compressed and uncompressed strings follow a single index or "flag" byte N. There are several - variations, differing in the format of the flag byte, maximum string length - and element size (byte or word). + variations, differing in the format of the flag byte, maximum string + length and element size (byte or word). - In the most widely used version, Packbits, the meaning of the flag byte N - is defined as follows: + In the most widely used version, Packbits, the meaning of the flag byte + N is defined as follows: 0-127 means the next N+1 bytes are uncompressed. 129-255 means the next byte is to be repeated 257-N times. @@ -99,7 +99,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf, Always encode 3-byte repeat sequences. Encode 2-byte repeat sequences only when they are at the start of the block. - This ensures that the output is never unnecessary bloated. + This ensures that the output is never unnecessarily bloated. Original algorithm by Robert A. Knop (rknop@mop.caltech.edu) -----------------------------------------------------------------------------*/ @@ -107,8 +107,17 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf, size_t inCurs, outCurs; - if (mode != PM_RLE_PACKBITS) - pm_error(errorUndefinedMode, mode); + int packBase; + int packSign; + + switch (mode) { + case PM_RLE_PACKBITS: + packBase = 257 ; packSign = -1; break; + case PM_RLE_PALMPDB: + packBase = 127 ; packSign = +1; break; + default: + pm_error(errorUndefinedMode, mode); + } for (inCurs = 0, outCurs = 0; inCurs < inSize; ) { if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) { @@ -121,8 +130,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf, count < maxRun; ++inCurs, ++count) ; - - outbuf[outCurs++] = (unsigned char)(257 - count); + outbuf[outCurs++] = (unsigned char) (packBase + packSign * count); outbuf[outCurs++] = inbuf[hold]; } else { /*Do a non-run*/ @@ -258,6 +266,8 @@ pm_rlenc_compressword(const uint16_t * const inbuf, number of flag bytes. The worst case happens when there are no repeat sequences in the input. + The encoder in this file is an efficient one. + The key to an efficient encoder is correct handling of short, inefficient sequences. The following algorithm (not actually used in this file) describes the idea. @@ -288,6 +298,16 @@ pm_rlenc_compressword(const uint16_t * const inbuf, - Whenever savings are larger than the added overhead, encode the run as a compressed block. + + ----------------------------------------------------------------------- + + MS-DOS/Windows BMP format note + + The compression method for BMP is a variant of packbits. + We could support the 8-bit version with some modifications to functions + in this file. Determining the worst-case output size of an efficient + coder is rather complicated because uncompressed blocks may not be less + than three bytes long and are indicated by two-byte flags. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ @@ -318,15 +338,22 @@ pm_rlenc_maxbytes(size_t const inSize, /* number of elements */ switch (mode) { case PM_RLE_PACKBITS: - blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; break; + case PM_RLE_PALMPDB: + blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; + break; case PM_RLE_SGI8: - blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; break; - case PM_RLE_GRAPHON: case PM_RLE_PPA: - blockSize = 64; flagSize = 1; itemSize = 1; miscSize = 0; break; + blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; + break; + case PM_RLE_GRAPHON: + case PM_RLE_PPA: + blockSize = 64; flagSize = 1; itemSize = 1; miscSize = 0; + break; case PM_RLE_SGI16: - blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; break; + blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; + break; case PM_RLE_PALM16: - blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; break; + blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; + break; default: pm_error(errorUndefinedMode, mode); } diff --git a/lib/util/runlength.h b/lib/util/runlength.h index 4857ae61..de921650 100644 --- a/lib/util/runlength.h +++ b/lib/util/runlength.h @@ -18,7 +18,8 @@ enum pm_RleMode { PM_RLE_PACKBITS, /* most common mode */ PM_RLE_PPA, /* reserved */ PM_RLE_SGI8, /* reserved */ PM_RLE_SGI16, - PM_RLE_PALM16 + PM_RLE_PALM16, + PM_RLE_PALMPDB }; size_t |