diff options
Diffstat (limited to 'lib')
68 files changed, 6274 insertions, 3141 deletions
diff --git a/lib/Makefile b/lib/Makefile index 2d49376d..67fac5b2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -5,7 +5,7 @@ endif SUBDIR = lib VPATH=.:$(SRCDIR)/$(SUBDIR) DLLTOOL=dlltool -include $(BUILDDIR)/Makefile.config +include $(BUILDDIR)/config.mk ifeq ($(NETPBMLIBTYPE),unixstatic) LIBNETPBM = libnetpbm.$(STATICLIBSUFFIX) @@ -25,7 +25,7 @@ else LIBSYSTEM = libsystem.o endif -LIBOBJECTS = libpm.o fileio.o bitio.o colorname.o \ +LIBOBJECTS = libpm.o pmfileio.o fileio.o bitio.o colorname.o \ libpbm1.o libpbm2.o libpbm3.o libpbmfont.o \ libpgm1.o libpgm2.o \ libppm1.o libppm2.o libppmcmap.o libppmcolor.o libppmfuzzy.o \ @@ -39,32 +39,37 @@ LIBOBJECTS = libpm.o fileio.o bitio.o colorname.o \ ifneq (${VMS}x,x) LIBOBJECTS += libpbmvms.o endif -# Library objects to be linked but not built by Makefile.common: -LIBOBJECTS_X = util/shhopt.o util/nstring.o util/filename.o +# Library objects to be linked but not built by common.mk: +LIBOBJECTS_X = \ + util/shhopt.o \ + util/nstring.o \ + util/vasprintf.o \ + util/filename.o \ + util/nsleep.o \ MANUALS3 = libnetpbm MANUALS5 = pbm pgm ppm pnm pam INTERFACE_HEADERS = pm.h pbm.h bitio.h pbmfont.h \ - pgm.h ppm.h ppm.h ppmcmap.h ppmfloyd.h colorname.h \ - pnm.h pam.h pammap.h util/shhopt.h util/nstring.h util/mallocvar.h \ - pm_system.h pm_gamma.h + pgm.h ppm.h ppmcmap.h ppmfloyd.h colorname.h \ + pnm.h pam.h pammap.h util/shhopt.h util/mallocvar.h \ + pm_system.h pm_gamma.h ppmdraw.h ppmdfont.h \ DATAFILES = rgb.txt .PHONY: all all: libnetpbm extra_staticlib -INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc - SUBDIRS = util SCRIPTS = BINARIES = OMIT_LIBRARY_RULE = 1 -include $(SRCDIR)/Makefile.common +include $(SRCDIR)/common.mk -# The following must go after Makefile.common because $(LIBNETPBM) may +INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc + +# The following must go after common.mk because $(LIBNETPBM) may # contain a reference to $(NETPBM_MAJOR_RELEASE). .PHONY: libnetpbm libnetpbm: $(LIBNETPBM) @@ -79,8 +84,8 @@ extra_staticlib: $(EXTRA_STATICLIB) #---------------------------------------------------------------------------- $(LIBOBJECTS): %.o: %.c importinc -# Note that the user may have configured -I options into CFLAGS. - $(CC) -c $(INCLUDES) -DNDEBUG $(CFLAGS) $(CFLAGS_SHLIB) \ +# Note that the user may have configured -I options into CPPFLAGS/CFLAGS. + $(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) $(CFLAGS_SHLIB) \ $(CFLAGS_PERSONAL) $(CADD) -o $@ $< MAJ = $(NETPBM_MAJOR_RELEASE) @@ -183,7 +188,7 @@ endif # ship a pre-made standardppmfont.c, so this rule will not normally be # used. standardppmdfont.c:standard.ppmdfont - ppmdcfont <$< >$@ || (rm $@ && false) + ppmdcfont <$< >$@ # Note that we create a new compile.h only for the first make after a # make clean. This is good enough. We used to do stamp-date for @@ -240,9 +245,11 @@ install.hdr: $(INTERFACE_HEADERS:%=%_installhdr) # prefer not to "install" them, but just to access the Netpbm source # directory when you compile your programs. -%_installhdr: $(PKGDIR)/include +.PHONY: $(INTERFACE_HEADERS:%=%_installhdr) + +$(INTERFACE_HEADERS:%=%_installhdr): $(PKGDIR)/include/netpbm $(INSTALL) -c -m $(INSTALL_PERM_HDR) \ - $(SRCDIR)/lib/$(@:%_installhdr=%) $(PKGDIR)/include/; + $(SRCDIR)/lib/$(@:%_installhdr=%) $(PKGDIR)/include/netpbm/ .PHONY: install.staticlib install.staticlib: $(PKGDIR)/link @@ -269,7 +276,7 @@ ifeq ($(NETPBMLIBTYPE),dylib) $(SYMLINK) ../lib/libnetpbm.$(MAJ).$(MIN).dylib libnetpbm.dylib endif -clean: localclean +distclean clean: localclean .PHONY: localclean localclean: diff --git a/lib/bitio.h b/lib/bitio.h index 15fe0e8a..dfc5a153 100644 --- a/lib/bitio.h +++ b/lib/bitio.h @@ -32,7 +32,7 @@ #ifndef _BITIO_H_ #define _BITIO_H_ -#include "pm.h" +#include <netpbm/pm.h> #ifdef __cplusplus extern "C" { @@ -86,4 +86,4 @@ pm_bitwrite(BITSTREAM b, #ifdef __cplusplus } #endif -#endif /* _BITIO_H_ */ +#endif diff --git a/lib/colorname.c b/lib/colorname.c index dcda3ab8..cfaf026a 100644 --- a/lib/colorname.c +++ b/lib/colorname.c @@ -43,6 +43,41 @@ pm_canonstr(char * const str) { +static void +openColornameFileSearch(const char * const searchPath, + FILE ** const filePP) { +/*---------------------------------------------------------------------------- + Open the color name file, finding it via the search path 'searchPath'. + + Return as *filePP the stream handle for it, but if we don't find it + (or just can open it) anywhere, return *filePP == NULL. +-----------------------------------------------------------------------------*/ + char * buffer; + + buffer = strdup(searchPath); + + if (buffer) { + char * cursor; + bool eol; + + cursor = &buffer[0]; + eol = FALSE; /* initial value */ + *filePP = NULL; /* initial value */ + while (!eol && !*filePP) { + const char * token; + token = strsepN(&cursor, ":"); + if (token) { + *filePP = fopen(token, "r"); + } else + eol = TRUE; + } + free(buffer); + } else + *filePP = NULL; +} + + + FILE * pm_openColornameFile(const char * const fileName, const int must_open) { /*---------------------------------------------------------------------------- @@ -57,31 +92,32 @@ pm_openColornameFile(const char * const fileName, const int must_open) { exist), exit the program with an error message. If 'must_open' is false and we can't open the file, just return a null pointer. -----------------------------------------------------------------------------*/ - const char *rgbdef; FILE *f; if (fileName == NULL) { - if ((rgbdef = getenv(RGBENV))==NULL) { + const char * rgbdef = getenv(RGBENV); + if (rgbdef) { + /* The environment variable is set */ + f = fopen(rgbdef, "r"); + if (f == NULL && must_open) + pm_error("Can't open the color names dictionary file " + "named %s, per the %s environment variable. " + "errno = %d (%s)", + rgbdef, RGBENV, errno, strerror(errno)); + } else { /* The environment variable isn't set, so try the hardcoded default color name dictionary locations. */ - if ((f = fopen(RGB_DB1, "r")) == NULL && - (f = fopen(RGB_DB2, "r")) == NULL && - (f = fopen(RGB_DB3, "r")) == NULL && must_open) { - pm_error("can't open color names dictionary file named " - "%s, %s, or %s " + openColornameFileSearch(RGB_DB_PATH, &f); + + if (f == NULL && must_open) { + pm_error("can't open color names dictionary file from the " + "path '%s' " "and Environment variable %s not set. Set %s to " "the pathname of your rgb.txt file or don't use " "color names.", - RGB_DB1, RGB_DB2, RGB_DB3, RGBENV, RGBENV); + RGB_DB_PATH, RGBENV, RGBENV); } - } else { - /* The environment variable is set */ - if ((f = fopen(rgbdef, "r")) == NULL && must_open) - pm_error("Can't open the color names dictionary file " - "named %s, per the %s environment variable. " - "errno = %d (%s)", - rgbdef, RGBENV, errno, strerror(errno)); } } else { f = fopen(fileName, "r"); diff --git a/lib/colorname.h b/lib/colorname.h index d33980e4..74583144 100644 --- a/lib/colorname.h +++ b/lib/colorname.h @@ -3,7 +3,7 @@ #include <string.h> #include <stdio.h> -#include "ppm.h" +#include <netpbm/ppm.h> #ifdef __cplusplus extern "C" { diff --git a/lib/dithers.h b/lib/dithers.h new file mode 100644 index 00000000..05531a32 --- /dev/null +++ b/lib/dithers.h @@ -0,0 +1,90 @@ +#ifndef DITHERS_H_INCLUDED +#define DITHERS_H_INCLUDED +/* +** dithers.h +** +** Here are some dithering matrices. They are all taken from "Digital +** Halftoning" by Robert Ulichney, MIT Press, ISBN 0-262-21009-6. +*/ + + +#if 0 +/* +** Order-6 ordered dithering matrix. Note that smaller ordered dithers +** have no advantage over larger ones, so use dither8 instead. +*/ +static int const dither6[8][8] = { + { 1, 59, 15, 55, 2, 56, 12, 52 }, + { 33, 17, 47, 31, 34, 18, 44, 28 }, + { 9, 49, 5, 63, 10, 50, 6, 60 }, + { 41, 25, 37, 21, 42, 26, 38, 22 }, + { 3, 57, 13, 53, 0, 58, 14, 54 }, + { 35, 19, 45, 29, 32, 16, 46, 30 }, + { 11, 51, 7, 61, 8, 48, 4, 62 }, + { 43, 27, 39, 23, 40, 24, 36, 20 } + }; +#endif + +/* Order-8 ordered dithering matrix. */ +static int const dither8[16][16] = { + { 1,235, 59,219, 15,231, 55,215, 2,232, 56,216, 12,228, 52,212}, + { 129, 65,187,123,143, 79,183,119,130, 66,184,120,140, 76,180,116}, + { 33,193, 17,251, 47,207, 31,247, 34,194, 18,248, 44,204, 28,244}, + { 161, 97,145, 81,175,111,159, 95,162, 98,146, 82,172,108,156, 92}, + { 9,225, 49,209, 5,239, 63,223, 10,226, 50,210, 6,236, 60,220}, + { 137, 73,177,113,133, 69,191,127,138, 74,178,114,134, 70,188,124}, + { 41,201, 25,241, 37,197, 21,255, 42,202, 26,242, 38,198, 22,252}, + { 169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86}, + { 3,233, 57,217, 13,229, 53,213, 0,234, 58,218, 14,230, 54,214}, + { 131, 67,185,121,141, 77,181,117,128, 64,186,122,142, 78,182,118}, + { 35,195, 19,249, 45,205, 29,245, 32,192, 16,250, 46,206, 30,246}, + { 163, 99,147, 83,173,109,157, 93,160, 96,144, 80,174,110,158, 94}, + { 11,227, 51,211, 7,237, 61,221, 8,224, 48,208, 4,238, 62,222}, + { 139, 75,179,115,135, 71,189,125,136, 72,176,112,132, 68,190,126}, + { 43,203, 27,243, 39,199, 23,253, 40,200, 24,240, 36,196, 20,254}, + { 171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84} +}; + +/* Order-3 clustered dithering matrix. */ +static int const cluster3[6][6] = { + { 9,11,10, 8, 6, 7}, + { 12,17,16, 5, 0, 1}, + { 13,14,15, 4, 3, 2}, + { 8, 6, 7, 9,11,10}, + { 5, 0, 1,12,17,16}, + { 4, 3, 2,13,14,15} +}; + +/* Order-4 clustered dithering matrix. */ +static int const cluster4[8][8] = { + { 18,20,19,16,13,11,12,15}, + { 27,28,29,22, 4, 3, 2, 9}, + { 26,31,30,21, 5, 0, 1,10}, + { 23,25,24,17, 8, 6, 7,14}, + { 13,11,12,15,18,20,19,16}, + { 4, 3, 2, 9,27,28,29,22}, + { 5, 0, 1,10,26,31,30,21}, + { 8, 6, 7,14,23,25,24,17} +}; + +/* Order-8 clustered dithering matrix. */ +static int const cluster8[16][16] = { + { 64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60}, + { 70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52}, + { 78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44}, + { 88,110,123,124,125,118,107, 85, 39, 17, 4, 3, 2, 9, 20, 42}, + { 89,111,122,127,126,117,106, 84, 38, 16, 5, 0, 1, 10, 21, 43}, + { 79,102,119,121,120,113, 97, 82, 48, 25, 8, 6, 7, 14, 30, 45}, + { 71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53}, + { 65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61}, + { 63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67}, + { 57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75}, + { 49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83}, + { 39, 17, 4, 3, 2, 9, 20, 42, 88,110,123,124,125,118,107, 85}, + { 38, 16, 5, 0, 1, 10, 21, 43, 89,111,122,127,126,117,106, 84}, + { 48, 25, 8, 6, 7, 14, 30, 45, 79,102,119,121,120,113, 97, 82}, + { 56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74}, + { 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66} +}; + +#endif diff --git a/lib/fileio.c b/lib/fileio.c index 01243f9d..300ae303 100644 --- a/lib/fileio.c +++ b/lib/fileio.c @@ -17,23 +17,23 @@ #include "fileio.h" char -pm_getc(FILE * const file) { +pm_getc(FILE * const fileP) { int ich; char ch; - ich = getc(file); + ich = getc(fileP); if (ich == EOF) pm_error("EOF / read error reading a byte"); ch = (char) ich; if (ch == '#') { do { - ich = getc(file); - if (ich == EOF) - pm_error("EOF / read error reading a byte"); - ch = (char) ich; - } while (ch != '\n' && ch != '\r'); - } + ich = getc(fileP); + if (ich == EOF) + pm_error("EOF / read error reading a byte"); + ch = (char) ich; + } while (ch != '\n' && ch != '\r'); + } return ch; } @@ -76,7 +76,7 @@ pm_getuint(FILE * const ifP) { do { ch = pm_getc(ifP); - } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); if (ch < '0' || ch > '9') pm_error("junk in file where an unsigned integer should be"); @@ -85,10 +85,18 @@ pm_getuint(FILE * const ifP) { do { unsigned int const digitVal = ch - '0'; - if (i > INT_MAX/10 - digitVal) + if (i > INT_MAX/10) pm_error("ASCII decimal integer in file is " "too large to be processed. "); - i = i * 10 + digitVal; + + i *= 10; + + if (i > INT_MAX - digitVal) + pm_error("ASCII decimal integer in file is " + "too large to be processed. "); + + i += digitVal; + ch = pm_getc(ifP); } while (ch >= '0' && ch <= '9'); @@ -165,6 +173,3 @@ pm_putraw(FILE * const file, pm_error("Error writing %d byte sample to file.", bytes); } } - - - diff --git a/lib/fileio.h b/lib/fileio.h index 158da10a..586c8265 100644 --- a/lib/fileio.h +++ b/lib/fileio.h @@ -1,5 +1,7 @@ -#ifndef _NETPBM_FILEIO_H_ -#define _NETPBM_FILEIO_H_ +#ifndef FILEIO_H_INCLUDED +#define FILEIO_H_INCLUDED + +#include <stdio.h> char pm_getc(FILE * const file); diff --git a/lib/libpam.c b/lib/libpam.c index 656f5361..ab75fab6 100644 --- a/lib/libpam.c +++ b/lib/libpam.c @@ -5,7 +5,7 @@ that deal with the PAM (Portable Arbitrary Format) image format. -----------------------------------------------------------------------------*/ -/* See libpm.c for the complicated explanation of this 32/64 bit file +/* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 @@ -86,24 +86,32 @@ validateComputableSize(struct pam * const pamP) { Another common operation is adding 1 or 2 to the highest row, column, or plane number in the image, so we make sure that's possible. -----------------------------------------------------------------------------*/ - unsigned int const depth = allocationDepth(pamP); - - if (depth > INT_MAX/sizeof(sample)) - pm_error("image depth (%u) too large to be processed", depth); - else if (pamP->width > 0 && depth * sizeof(sample) > INT_MAX/pamP->width) - pm_error("image width and depth (%u, %u) too large " - "to be processed.", pamP->width, depth); - else if (pamP->width * (depth * sizeof(sample)) > - INT_MAX - depth * sizeof(tuple *)) - pm_error("image width and depth (%u, %u) too large " - "to be processed.", pamP->width, depth); - - if (depth > INT_MAX - 2) - pm_error("image depth (%u) too large to be processed", depth); - if (pamP->width > INT_MAX - 2) - pm_error("image width (%u) too large to be processed", pamP->width); - if (pamP->height > INT_MAX - 2) - pm_error("image height (%u) too large to be processed", pamP->height); + if (pamP->width == 0) + pm_error("Width is zero. Image must be at least one pixel wide"); + else if (pamP->height == 0) + pm_error("Height is zero. Image must be at least one pixel high"); + else { + unsigned int const depth = allocationDepth(pamP); + + if (depth > INT_MAX/sizeof(sample)) + pm_error("image depth (%u) too large to be processed", depth); + else if (depth * sizeof(sample) > INT_MAX/pamP->width) + pm_error("image width and depth (%u, %u) too large " + "to be processed.", pamP->width, depth); + else if (pamP->width * (depth * sizeof(sample)) > + INT_MAX - depth * sizeof(tuple *)) + pm_error("image width and depth (%u, %u) too large " + "to be processed.", pamP->width, depth); + + if (depth > INT_MAX - 2) + pm_error("image depth (%u) too large to be processed", depth); + if (pamP->width > INT_MAX - 2) + pm_error("image width (%u) too large to be processed", + pamP->width); + if (pamP->height > INT_MAX - 2) + pm_error("image height (%u) too large to be processed", + pamP->height); + } } @@ -532,16 +540,20 @@ process_header_line(char const buffer[], pamP->maxval = atoi(value); headerSeenP->maxval = TRUE; } else if (strcmp(label, "TUPLTYPE") == 0) { - int len = strlen(pamP->tuple_type); - if (len + strlen(value) + 1 > sizeof(pamP->tuple_type)-1) - pm_error("TUPLTYPE value too long in PAM header"); - if (len == 0) - strcpy(pamP->tuple_type, value); + if (strlen(value) == 0) + pm_error("TUPLTYPE header does not have any tuple type text"); else { - strcat(pamP->tuple_type, "\n"); - strcat(pamP->tuple_type, value); + size_t const oldLen = strlen(pamP->tuple_type); + if (oldLen + strlen(value) + 1 > sizeof(pamP->tuple_type)-1) + pm_error("TUPLTYPE value too long in PAM header"); + if (oldLen == 0) + strcpy(pamP->tuple_type, value); + else { + strcat(pamP->tuple_type, " "); + strcat(pamP->tuple_type, value); + } + pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0'; } - pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0'; } else pm_error("Unrecognized header line: '%s'. " "Possible missing ENDHDR line?", label); @@ -670,10 +682,10 @@ pnm_readpaminitrestaspnm(FILE * const fileP, -----------------------------------------------------------------------------*/ struct pam pam; - pam.size = sizeof(struct pam); - pam.file = fileP; - pam.len = PAM_STRUCT_SIZE(tuple_type); - pam.format = PAM_FORMAT; + pam.size = sizeof(struct pam); + pam.file = fileP; + pam.len = PAM_STRUCT_SIZE(tuple_type); + pam.format = PAM_FORMAT; readpaminitrest(&pam); @@ -691,12 +703,11 @@ pnm_readpaminitrestaspnm(FILE * const fileP, case 1: *formatP = RPGM_FORMAT; break; - default: { + default: pm_error("Cannot treat PAM image as PPM or PGM, " "because its depth (%u) " "is not 1 or 3.", pam.depth); } - } *colsP = pam.width; *rowsP = pam.height; @@ -775,7 +786,8 @@ pnm_readpaminit(FILE * const file, break; default: - pm_error("bad magic number - not a PAM, PPM, PGM, or PBM file"); + pm_error("bad magic number 0x%x - not a PAM, PPM, PGM, or PBM file", + pamP->format); } pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval); @@ -848,18 +860,22 @@ pnm_writepaminit(struct pam * const pamP) { if (pamP->size < pamP->len) pm_error("pam object passed to pnm_writepaminit() is smaller " - "(%d bytes, according to its 'size' element) " + "(%u bytes, according to its 'size' element) " "than the amount of data in it " - "(%d bytes, according to its 'len' element).", + "(%u bytes, according to its 'len' element).", pamP->size, pamP->len); - if (pamP->len < PAM_STRUCT_SIZE(bytes_per_sample)) + if (pamP->size < PAM_STRUCT_SIZE(bytes_per_sample)) pm_error("pam object passed to pnm_writepaminit() is too small. " "It must be large\n" "enough to hold at least up through the " "'bytes_per_sample' member, but according\n" - "to its 'len' member, it is only %d bytes long.", - pamP->len); + "to its 'size' member, it is only %u bytes long.", + pamP->size); + if (pamP->len < PAM_STRUCT_SIZE(maxval)) + pm_error("pam object must contain members at least through 'maxval', " + "but according to the 'len' member, it is only %u bytes " + "long.", pamP->len); if (pamP->maxval > PAM_OVERALL_MAXVAL) pm_error("maxval (%lu) passed to pnm_writepaminit() " @@ -870,6 +886,8 @@ pnm_writepaminit(struct pam * const pamP) { else tupleType = pamP->tuple_type; + if (pamP->len < PAM_STRUCT_SIZE(bytes_per_sample)) + pamP->len = PAM_STRUCT_SIZE(bytes_per_sample); pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval); switch (PAM_FORMAT_TYPE(pamP->format)) { diff --git a/lib/libpam.h b/lib/libpam.h index 4b8e6693..9f8a34d0 100644 --- a/lib/libpam.h +++ b/lib/libpam.h @@ -4,7 +4,7 @@ #ifndef LIBPAM_H_INCLUDED #define LIBPAM_H_INCLUDED -#include "pam.h" +#include "pgm.h" void pnm_readpaminitrestaspnm(FILE * const fileP, diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c index e11415ca..b64f8963 100644 --- a/lib/libpamcolor.c +++ b/lib/libpamcolor.c @@ -5,12 +5,16 @@ that deal with colors in the PAM image format. -----------------------------------------------------------------------------*/ -/* See libpbm.c for the complicated explanation of this 32/64 bit file +/* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 #define _LARGE_FILES +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <string.h> #include <limits.h> #include "pm_c_util.h" @@ -44,6 +48,34 @@ pnm_parsecolor(const char * const colorname, +const char * +pnm_colorname(struct pam * const pamP, + tuple const color, + int const hexok) { + + const char * retval; + pixel colorp; + char * colorname; + + if (pamP->depth < 3) + PPM_ASSIGN(colorp, color[0], color[0], color[0]); + else + PPM_ASSIGN(colorp, + color[PAM_RED_PLANE], + color[PAM_GRN_PLANE], + color[PAM_BLU_PLANE]); + + colorname = ppm_colorname(&colorp, pamP->maxval, hexok); + + retval = strdup(colorname); + if (retval == NULL) + pm_error("Couldn't get memory for color name string"); + + return retval; +} + + + double pnm_lumin_factor[3] = {PPM_LUMINR, PPM_LUMING, PPM_LUMINB}; void diff --git a/lib/libpammap.c b/lib/libpammap.c index 023a65a0..6fea0eb9 100644 --- a/lib/libpammap.c +++ b/lib/libpammap.c @@ -25,12 +25,13 @@ #define HASH_SIZE 20023 unsigned int -pnm_hashtuple(struct pam * const pamP, tuple const tuple) { +pnm_hashtuple(struct pam * const pamP, + tuple const tuple) { /*---------------------------------------------------------------------------- Return the hash value of the tuple 'tuple' -- i.e. an index into a hash table. -----------------------------------------------------------------------------*/ - int i; + unsigned int i; unsigned int hash; const unsigned int hash_factor[] = {33023, 30013, 27011}; @@ -252,6 +253,7 @@ static void computehashrecoverable(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, + unsigned int const newDepth, sample const newMaxval, unsigned int * const sizeP, tuplehash * const tuplefreqhashP, @@ -268,13 +270,16 @@ computehashrecoverable(struct pam * const pamP, freqPam = *pamP; freqPam.maxval = newMaxval; + freqPam.depth = newDepth; + + assert(freqPam.depth <= pamP->depth); *tuplefreqhashP = pnm_createtuplehash(); *sizeP = 0; /* initial value */ *rowbufferP = pnm_allocpamrow(pamP); - *colorP = pnm_allocpamtuple(&freqPam); + *colorP = pnm_allocpamtuple(pamP); full = FALSE; /* initial value */ @@ -282,7 +287,7 @@ computehashrecoverable(struct pam * const pamP, tuple values. */ for (row = 0; row < pamP->height && !full; ++row) { - int col; + unsigned int col; const tuple * tuplerow; /* The row of tuples we are processing */ if (tupleArray) @@ -313,6 +318,7 @@ static tuplehash computetuplefreqhash(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, + unsigned int const newDepth, sample const newMaxval, unsigned int * const sizeP) { /*---------------------------------------------------------------------------- @@ -335,6 +341,10 @@ computetuplefreqhash(struct pam * const pamP, However, if the number of unique tuple values is greater than 'maxsize', return a null return value and *sizeP undefined. + The tuple values that index the hash have depth 'newDepth'. We look at + only the first 'newDepth' planes of the input. Caler must ensure that + the input has at least that many planes. + The tuple values that index the hash are scaled to a new maxval of 'newMaxval'. E.g. if the input has maxval 100 and 'newMaxval' is 50, and a particular tuple has sample value 50, it would be counted @@ -355,19 +365,20 @@ computetuplefreqhash(struct pam * const pamP, rowbuffer = NULL; color = NULL; - if (setjmp(jmpbuf) == 0) { - pm_setjmpbufsave(&jmpbuf, &origJmpbufP); - computehashrecoverable(pamP, tupleArray, maxsize, newMaxval, sizeP, - &tuplefreqhash, &rowbuffer, &color); - pm_setjmpbuf(origJmpbufP); - } else { + if (setjmp(jmpbuf) != 0) { if (color) pnm_freepamtuple(color); if (rowbuffer) pnm_freepamrow(rowbuffer); if (tuplefreqhash) pnm_destroytuplehash(tuplefreqhash); + pm_setjmpbuf(origJmpbufP); pm_longjmp(); + } else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + computehashrecoverable(pamP, tupleArray, maxsize, newDepth, newMaxval, + sizeP, &tuplefreqhash, &rowbuffer, &color); + pm_setjmpbuf(origJmpbufP); } return tuplefreqhash; } @@ -382,7 +393,8 @@ pnm_computetuplefreqhash(struct pam * const pamP, /*---------------------------------------------------------------------------- Compute the tuple frequency hash for the tuple array tupleArray[][]. -----------------------------------------------------------------------------*/ - return computetuplefreqhash(pamP, tupleArray, maxsize, pamP->maxval, + return computetuplefreqhash(pamP, tupleArray, maxsize, + pamP->depth, pamP->maxval, sizeP); } @@ -446,8 +458,9 @@ pnm_alloctupletable(const struct pam * const pamP, alloctupletable(pamP, size, &retval, &error); if (error) { + pm_errormsg("%s", error); strfree(error); - pm_error("Failed to allocation tuple table of size %u", size); + pm_longjmp(); } return retval; } @@ -455,8 +468,8 @@ pnm_alloctupletable(const struct pam * const pamP, void -pnm_freetupletable(struct pam * const pamP, - tupletable const tupletable) { +pnm_freetupletable(const struct pam * const pamP, + tupletable const tupletable) { /* Note that the address 'tupletable' is, to the operating system, the address of a larger block of memory that contains not only @@ -470,8 +483,8 @@ pnm_freetupletable(struct pam * const pamP, void -pnm_freetupletable2(struct pam * const pamP, - tupletable2 const tupletable) { +pnm_freetupletable2(const struct pam * const pamP, + tupletable2 const tupletable) { pnm_freetupletable(pamP, tupletable.table); } @@ -500,8 +513,9 @@ tuplehashtotable(const struct pam * const pamP, alloctupletable(pamP, allocsize, &tupletable, &error); if (error) { + pm_errormsg("%s", error); strfree(error); - pm_error("Failed to allocate table table of size %u", allocsize); + pm_longjmp(); } else { unsigned int i, j; /* Loop through the hash table. */ @@ -575,9 +589,10 @@ pnm_computetupletablehash(struct pam * const pamP, tupletable -pnm_computetuplefreqtable2(struct pam * const pamP, +pnm_computetuplefreqtable3(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, + unsigned int const newDepth, sample const newMaxval, unsigned int * const countP) { /*---------------------------------------------------------------------------- @@ -604,6 +619,9 @@ pnm_computetuplefreqtable2(struct pam * const pamP, Return the number of unique tuple values in tupleArray[][] as *countP. + The tuples in the table have depth 'newDepth'. We look at + only the first 'newDepth' planes of the input. If the input doesn't + have that many planes, we throw an error. Scale the tuple values to a new maxval of 'newMaxval' before processing them. E.g. if the input has maxval 100 and 'newMaxval' @@ -615,8 +633,13 @@ pnm_computetuplefreqtable2(struct pam * const pamP, tupletable tuplefreqtable; unsigned int uniqueCount; + if (newDepth > pamP->depth) + pm_error("pnm_computetuplefreqtable3 called with 'newDepth' " + "argument (%u) greater than input depth (%u)", + newDepth, pamP->depth); + tuplefreqhash = computetuplefreqhash(pamP, tupleArray, maxsize, - newMaxval, &uniqueCount); + newDepth, newMaxval, &uniqueCount); if (tuplefreqhash == NULL) tuplefreqtable = NULL; else { @@ -635,6 +658,20 @@ pnm_computetuplefreqtable2(struct pam * const pamP, tupletable +pnm_computetuplefreqtable2(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + sample const newMaxval, + unsigned int * const countP) { + + return + pnm_computetuplefreqtable3(pamP, tupleArray, maxsize, + pamP->depth, newMaxval, countP); +} + + + +tupletable pnm_computetuplefreqtable(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, diff --git a/lib/libpamn.c b/lib/libpamn.c index c7610100..fe004e91 100644 --- a/lib/libpamn.c +++ b/lib/libpamn.c @@ -10,6 +10,7 @@ #include "pm_c_util.h" #include "mallocvar.h" +#include "nstring.h" #include "pam.h" #include "fileio.h" #include "pm_gamma.h" @@ -18,15 +19,19 @@ -tuplen * -pnm_allocpamrown(const struct pam * const pamP) { +static void +allocpamrown(const struct pam * const pamP, + tuplen ** const tuplerownP, + const char ** const errorP) { /*---------------------------------------------------------------------------- We assume that the dimensions of the image are such that arithmetic overflow will not occur in our calculations. NOTE: pnm_readpaminit() ensures this assumption is valid. -----------------------------------------------------------------------------*/ - const int bytes_per_tuple = pamP->depth * sizeof(samplen); + int const bytes_per_tuple = pamP->depth * sizeof(samplen); + tuplen * tuplerown; + const char * error; /* The tuple row data structure starts with 'width' pointers to the tuples, immediately followed by the 'width' tuples @@ -35,64 +40,106 @@ pnm_allocpamrown(const struct pam * const pamP) { tuplerown = malloc(pamP->width * (sizeof(tuplen *) + bytes_per_tuple)); if (tuplerown == NULL) - pm_error("Out of memory allocating space for a tuple row of\n" - "%d tuples by %d samples per tuple by %d bytes per sample.", - pamP->width, pamP->depth, sizeof(samplen)); - - { + asprintfN(&error, "Out of memory allocating space for a tuple row of" + "%u tuples by %u samples per tuple by %u bytes per sample.", + pamP->width, pamP->depth, sizeof(samplen)); + else { /* Now we initialize the pointers to the individual tuples to make this a regulation C two dimensional array. */ - char *p; - int i; + unsigned char * p; + unsigned int i; - p = (char*) (tuplerown + pamP->width); /* location of Tuple 0 */ - for (i = 0; i < pamP->width; i++) { + p = (unsigned char*) (tuplerown + pamP->width); + /* location of Tuple 0 */ + for (i = 0; i < pamP->width; ++i) { tuplerown[i] = (tuplen) p; p += bytes_per_tuple; } + *errorP = NULL; + *tuplerownP = tuplerown; } - return(tuplerown); } -void -pnm_readpamrown(const struct pam * const pamP, - tuplen * const tuplenrow) { +tuplen * +pnm_allocpamrown(const struct pam * const pamP) { +/*---------------------------------------------------------------------------- + We assume that the dimensions of the image are such that arithmetic + overflow will not occur in our calculations. NOTE: pnm_readpaminit() + ensures this assumption is valid. +-----------------------------------------------------------------------------*/ + const char * error; + tuplen * tuplerown; - /* For speed, we don't check any of the inputs for consistency - here (unless it's necessary to avoid crashing). Any consistency - checking should have been done by a prior call to - pnm_writepaminit(). - */ - assert(pamP->maxval != 0); + allocpamrown(pamP, &tuplerown, &error); - /* Need a special case for raw PBM because it has multiple tuples (8) - packed into one byte. - */ - if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) { - int col; - bit *bitrow; - if (pamP->depth != 1) - pm_error("Invalid pam structure passed to pnm_readpamrow(). " - "It says PBM format, but 'depth' member is not 1."); - bitrow = pbm_allocrow(pamP->width); - pbm_readpbmrow(pamP->file, bitrow, pamP->width, pamP->format); - for (col = 0; col < pamP->width; col++) - tuplenrow[col][0] = - bitrow[col] == PBM_BLACK ? 0.0 : 1.0; + if (error) { + pm_errormsg("pnm_allocpamrown() failed. %s", error); + strfree(error); + pm_longjmp(); + } + + return tuplerown; +} + + + +static void +readpbmrow(const struct pam * const pamP, + tuplen * const tuplenrow) { + + bit * bitrow; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + bitrow = pbm_allocrow(pamP->width); + + if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pbm_readpbmrow(pamP->file, bitrow, pamP->width, pamP->format); + + for (col = 0; col < pamP->width; ++col) + tuplenrow[col][0] = bitrow[col] == PBM_BLACK ? 0.0 : 1.0; + + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} + + + +static void +readpamrow(const struct pam * const pamP, + tuplen * const tuplenrow) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + tuple * tuplerow; + + tuplerow = pnm_allocpamrow(pamP); + + if (setjmp(jmpbuf) != 0) { + pnm_freepamrow(tuplerow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); } else { float const scaler = 1.0 / pamP->maxval; /* Note: multiplication is faster than division, so we divide once here so we can multiply many times later. */ - int col; - tuple * tuplerow; - tuplerow = pnm_allocpamrow(pamP); + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pnm_readpamrow(pamP, tuplerow); for (col = 0; col < pamP->width; ++col) { @@ -100,15 +147,16 @@ pnm_readpamrown(const struct pam * const pamP, for (plane = 0; plane < pamP->depth; ++plane) tuplenrow[col][plane] = tuplerow[col][plane] * scaler; } - pnm_freepamrow(tuplerow); + pm_setjmpbuf(origJmpbufP); } + pnm_freepamrow(tuplerow); } void -pnm_writepamrown(const struct pam * const pamP, - const tuplen * const tuplenrow) { +pnm_readpamrown(const struct pam * const pamP, + tuplen * const tuplenrow) { /* For speed, we don't check any of the inputs for consistency here (unless it's necessary to avoid crashing). Any consistency @@ -121,20 +169,66 @@ pnm_writepamrown(const struct pam * const pamP, packed into one byte. */ if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) { - int col; - bit *bitrow; - bitrow = pbm_allocrow(pamP->width); - for (col = 0; col < pamP->width; col++) - bitrow[col] = - tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE; + if (pamP->depth != 1) + pm_error("Invalid pam structure passed to pnm_readpamrow(). " + "It says PBM format, but 'depth' member is not 1."); + + readpbmrow(pamP, tuplenrow); + } else + readpamrow(pamP, tuplenrow); +} + + + +static void +writepbmrow(const struct pam * const pamP, + const tuplen * const tuplenrow) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(pamP->width); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (col = 0; col < pamP->width; ++col) + bitrow[col] = tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE; pbm_writepbmrow(pamP->file, bitrow, pamP->width, pamP->format == PBM_FORMAT); - pbm_freerow(bitrow); + + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} + + + +static void +writepamrow(const struct pam * const pamP, + const tuplen * const tuplenrow) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + tuple * tuplerow; + + tuplerow = pnm_allocpamrow(pamP); + + if (setjmp(jmpbuf) != 0) { + pnm_freepamrow(tuplerow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); } else { - tuple * tuplerow; - int col; + unsigned int col; - tuplerow = pnm_allocpamrow(pamP); + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (col = 0; col < pamP->width; ++col) { unsigned int plane; @@ -143,8 +237,32 @@ pnm_writepamrown(const struct pam * const pamP, (tuplenrow[col][plane] * pamP->maxval + 0.5); } pnm_writepamrow(pamP, tuplerow); - pnm_freepamrow(tuplerow); + + pm_setjmpbuf(origJmpbufP); } + pnm_freepamrow(tuplerow); +} + + + +void +pnm_writepamrown(const struct pam * const pamP, + const tuplen * const tuplenrow) { + + /* For speed, we don't check any of the inputs for consistency + here (unless it's necessary to avoid crashing). Any consistency + checking should have been done by a prior call to + pnm_writepaminit(). + */ + assert(pamP->maxval != 0); + + /* Need a special case for raw PBM because it has multiple tuples (8) + packed into one byte. + */ + if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) + writepbmrow(pamP, tuplenrow); + else + writepamrow(pamP, tuplenrow); } @@ -152,8 +270,8 @@ pnm_writepamrown(const struct pam * const pamP, tuplen ** pnm_allocpamarrayn(const struct pam * const pamP) { - tuplen **tuplenarray; - int row; + tuplen ** tuplenarray; + const char * error; /* If the speed of this is ever an issue, it might be sped up a little by allocating one large chunk. @@ -161,12 +279,33 @@ pnm_allocpamarrayn(const struct pam * const pamP) { MALLOCARRAY(tuplenarray, pamP->height); if (tuplenarray == NULL) - pm_error("Out of memory allocating the row pointer section of " - "a %u row array", pamP->height); - - for (row = 0; row < pamP->height; row++) { - tuplenarray[row] = pnm_allocpamrown(pamP); + asprintfN(&error, + "Out of memory allocating the row pointer section of " + "a %u row array", pamP->height); + else { + unsigned int rowsDone; + + rowsDone = 0; + error = NULL; + + while (rowsDone < pamP->height && !error) { + allocpamrown(pamP, &tuplenarray[rowsDone], &error); + if (!error) + ++rowsDone; + } + if (error) { + unsigned int row; + for (row = 0; row < rowsDone; ++row) + pnm_freepamrown(tuplenarray[rowsDone]); + free(tuplenarray); + } + } + if (error) { + pm_errormsg("pnm_allocpamarrayn() failed. %s", error); + strfree(error); + pm_longjmp(); } + return(tuplenarray); } @@ -191,16 +330,28 @@ pnm_readpamn(FILE * const file, int const size) { tuplen **tuplenarray; - int row; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; pnm_readpaminit(file, pamP, size); tuplenarray = pnm_allocpamarrayn(pamP); - for (row = 0; row < pamP->height; row++) - pnm_readpamrown(pamP, tuplenarray[row]); + if (setjmp(jmpbuf) != 0) { + pnm_freepamarrayn(tuplenarray, pamP); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; - return(tuplenarray); + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < pamP->height; ++row) + pnm_readpamrown(pamP, tuplenarray[row]); + + pm_setjmpbuf(origJmpbufP); + } + return tuplenarray; } @@ -209,11 +360,11 @@ void pnm_writepamn(struct pam * const pamP, tuplen ** const tuplenarray) { - int row; + unsigned int row; pnm_writepaminit(pamP); - for (row = 0; row < pamP->height; row++) + for (row = 0; row < pamP->height; ++row) pnm_writepamrown(pamP, tuplenarray[row]); } @@ -473,6 +624,8 @@ createUngammaMapOffset(const struct pam * const pamP, can be used in a reverse lookup to convert normalized ungamma'ed samplen values to integer sample values. The 0.5 effectively does the rounding. + + This never throws an error. Return value NULL means failed. -----------------------------------------------------------------------------*/ pnm_transformMap * retval; pnm_transformMap ungammaTransformMap; diff --git a/lib/libpamread.c b/lib/libpamread.c index 1b65745a..f4d85493 100644 --- a/lib/libpamread.c +++ b/lib/libpamread.c @@ -6,7 +6,7 @@ raster (not the header). -----------------------------------------------------------------------------*/ -/* See libpm.c for the complicated explanation of this 32/64 bit file +/* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 @@ -16,32 +16,48 @@ #include <limits.h> #include <assert.h> -#include "pam.h" #include "fileio.h" +#include "nstring.h" +#include "pam.h" static void readPbmRow(const struct pam * const pamP, tuple * const tuplerow) { - unsigned char *bitrow; if (pamP->depth != 1) pm_error("Invalid pam structure passed to pnm_readpamrow(). " "It says PBM format, but 'depth' member is not 1."); + else { + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + unsigned char * bitrow; + + bitrow = (unsigned char *) pbm_allocrow(pbm_packed_bytes(pamP->width)); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width, + pamP->format); - bitrow = (unsigned char *) pbm_allocrow(pbm_packed_bytes(pamP->width)); - pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width, pamP->format); - - if (tuplerow) { - int col; - for (col = 0; col < pamP->width; ++col) { - tuplerow[col][0] = - ( ((bitrow[col/8] >> (7-col%8)) & 1 ) == PBM_BLACK) - ? PAM_PBM_BLACK : PAM_PBM_WHITE - ; + if (tuplerow) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + tuplerow[col][0] = + ( ((bitrow[col/8] >> (7-col%8)) & 1 ) == PBM_BLACK) + ? PAM_PBM_BLACK : PAM_PBM_WHITE + ; + } + } + pm_setjmpbuf(origJmpbufP); } - } - pbm_freerow(bitrow); + pbm_freerow(bitrow); + } } @@ -185,6 +201,7 @@ readRawNonPbmRow(const struct pam * const pamP, unsigned char * inbuf; size_t bytesRead; + const char * error; inbuf = pnm_allocrowimage(pamP); @@ -192,25 +209,34 @@ readRawNonPbmRow(const struct pam * const pamP, if (bytesRead != rowImageSize) { if (feof(pamP->file)) - pm_error("End of file encountered when trying to read a row from " - "input file."); + asprintfN(&error, + "End of file encountered when trying to read a row from " + "input file."); else - pm_error("Error reading a row from input file. " - "fread() fails with errno=%d (%s)", - errno, strerror(errno)); - } - if (tuplerow) { - switch (pamP->bytes_per_sample) { - case 1: parse1BpsRow(pamP, tuplerow, inbuf); break; - case 2: parse2BpsRow(pamP, tuplerow, inbuf); break; - case 3: parse3BpsRow(pamP, tuplerow, inbuf); break; - case 4: parse4BpsRow(pamP, tuplerow, inbuf); break; - default: - pm_error("invalid bytes per sample passed to " - "pnm_formatpamrow(): %u", pamP->bytes_per_sample); + asprintfN(&error, "Error reading a row from input file. " + "fread() fails with errno=%d (%s)", + errno, strerror(errno)); + } else { + error = NULL; /* initial assumption */ + if (tuplerow) { + switch (pamP->bytes_per_sample) { + case 1: parse1BpsRow(pamP, tuplerow, inbuf); break; + case 2: parse2BpsRow(pamP, tuplerow, inbuf); break; + case 3: parse3BpsRow(pamP, tuplerow, inbuf); break; + case 4: parse4BpsRow(pamP, tuplerow, inbuf); break; + default: + asprintfN(&error, "invalid bytes per sample passed to " + "pnm_formatpamrow(): %u", pamP->bytes_per_sample); + } } } pnm_freerowimage(inbuf); + + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } } @@ -263,15 +289,27 @@ pnm_readpam(FILE * const fileP, struct pam * const pamP, int const size) { - tuple **tuplearray; - int row; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + tuple ** tuplearray; pnm_readpaminit(fileP, pamP, size); tuplearray = pnm_allocpamarray(pamP); - for (row = 0; row < pamP->height; row++) - pnm_readpamrow(pamP, tuplearray[row]); - + if (setjmp(jmpbuf) != 0) { + pnm_freepamarray(tuplearray, pamP); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < pamP->height; ++row) + pnm_readpamrow(pamP, tuplearray[row]); + + pm_setjmpbuf(origJmpbufP); + } return tuplearray; } diff --git a/lib/libpamwrite.c b/lib/libpamwrite.c index 9184a4b5..dd319d4a 100644 --- a/lib/libpamwrite.c +++ b/lib/libpamwrite.c @@ -6,7 +6,7 @@ raster (not the header). -----------------------------------------------------------------------------*/ -/* See libpm.c for the complicated explanation of this 32/64 bit file +/* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 @@ -308,22 +308,33 @@ writePamRawRow(const struct pam * const pamP, Write mutiple ('count') copies of the same row ('tuplerow') to the file, in raw (not plain) format. -----------------------------------------------------------------------------*/ + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; unsigned int rowImageSize; - unsigned char * outbuf; /* malloc'ed */ - unsigned int i; outbuf = pnm_allocrowimage(pamP); pnm_formatpamrow(pamP, tuplerow, outbuf, &rowImageSize); - for (i = 0; i < count; ++i) { - size_t bytesWritten; - - bytesWritten = fwrite(outbuf, 1, rowImageSize, pamP->file); - if (bytesWritten != rowImageSize) - pm_error("fwrite() failed to write an image row to the file. " - "errno=%d (%s)", errno, strerror(errno)); + if (setjmp(jmpbuf) != 0) { + pnm_freerowimage(outbuf); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int i; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (i = 0; i < count; ++i) { + size_t bytesWritten; + + bytesWritten = fwrite(outbuf, 1, rowImageSize, pamP->file); + if (bytesWritten != rowImageSize) + pm_error("fwrite() failed to write an image row to the file. " + "errno=%d (%s)", errno, strerror(errno)); + } + pm_setjmpbuf(origJmpbufP); } pnm_freerowimage(outbuf); } diff --git a/lib/libpbm1.c b/lib/libpbm1.c index 8dd491a7..fc20071c 100644 --- a/lib/libpbm1.c +++ b/lib/libpbm1.c @@ -10,20 +10,41 @@ ** implied warranty. */ -/* See libpm.c for the complicated explanation of this 32/64 bit file +/* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 #define _LARGE_FILES #include <stdio.h> -#include "pbm.h" -#include "libpbm.h" + +#include "pm_c_util.h" +#include "mallocvar.h" #include "shhopt.h" +#include "pbm.h" + + + +bit * +pbm_allocrow(unsigned int const cols) { + + bit * bitrow; + + MALLOCARRAY(bitrow, cols); + + if (bitrow == NULL) + pm_error("Unable to allocate space for a %u-column bit row", cols); + + return bitrow; +} + + void -pbm_init(int *argcP, char *argv[]) { - pm_proginit(argcP, argv); +pbm_init(int * const argcP, + char ** const argv) { + + pm_proginit(argcP, (const char **)argv); } @@ -60,3 +81,92 @@ pbm_check(FILE * file, const enum pm_check_type check_type, } } + + +static unsigned int +bitpop8(unsigned char const x) { +/*---------------------------------------------------------------------------- + Return the number of 1 bits in 'x' +-----------------------------------------------------------------------------*/ +static unsigned int const p[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; + + return p[x]; +} + + + +static int +bitpop(const unsigned char * const packedRow, + unsigned int const cols) { +/*---------------------------------------------------------------------------- + Return the number of 1 bits in 'packedRow'. +-----------------------------------------------------------------------------*/ + unsigned int const colByteCnt = pbm_packed_bytes(cols); + unsigned int const fullByteCnt = cols/8; + + unsigned int i; + unsigned int sum; + + sum = 0; /* initial value */ + + for (i = 0; i < fullByteCnt; ++i) + sum += bitpop8(packedRow[i]); + + if (colByteCnt > fullByteCnt) + sum += bitpop8(packedRow[i] >> (8-cols%8)); + + return sum; +} + + + +bit +pbm_backgroundbitrow(unsigned const char * const packedBits, + unsigned int const cols, + unsigned int const offset) { +/*---------------------------------------------------------------------------- + PBM version of pnm_backgroundxelrow() with additional offset parameter. + When offset == 0, produces the same return value as does + pnm_backgroundxelrow(promoted_bitrow, cols, ...) +-----------------------------------------------------------------------------*/ + const unsigned char * const row = &packedBits[offset/8]; + unsigned int const rs = offset % 8; + unsigned int const last = pbm_packed_bytes(cols + rs) - 1; + + unsigned int retval; + + unsigned int firstbit, lastbit; + unsigned int totalBitpop, headBitpop; + + firstbit = (row[0] >> (7-rs)) & 0x01; + lastbit = (row[last] >> (7- (cols+rs-1)%8)) & 0x01; + + if (firstbit == lastbit) + retval = firstbit; + else { + totalBitpop = bitpop(row, cols + rs); + headBitpop = (rs == 0) ? 0 : bitpop(row, rs); + + if (totalBitpop - headBitpop >= cols/2) + retval = PBM_BLACK; + else + retval = PBM_WHITE; + } + return retval; +} diff --git a/lib/libpbm2.c b/lib/libpbm2.c index d04328ef..a8e4b0f6 100644 --- a/lib/libpbm2.c +++ b/lib/libpbm2.c @@ -10,9 +10,12 @@ ** implied warranty. */ +#include <limits.h> + #include "pbm.h" #include "libpbm.h" #include "fileio.h" +#include "pam.h" static bit getbit (FILE * const file) { @@ -54,25 +57,65 @@ pbm_readpbminitrest( file, colsP, rowsP ) pm_error("Number of columns in header is too large."); } + + +static void +validateComputableSize(unsigned int const cols, + unsigned int const rows) { +/*---------------------------------------------------------------------------- + Validate that the dimensions of the image are such that it can be + processed in typical ways on this machine without worrying about + overflows. Note that in C, arithmetic is always modulus + arithmetic, so if your values are too big, the result is not what + you expect. That failed expectation can be disastrous if you use + it to allocate memory. + + A common operation is adding 1 or 2 to the highest row or + column number in the image, so we make sure that's possible. +-----------------------------------------------------------------------------*/ + if (cols > INT_MAX - 2) + pm_error("image width (%u) too large to be processed", cols); + if (rows > INT_MAX - 2) + pm_error("image height (%u) too large to be processed", rows); +} + + + void -pbm_readpbminit( file, colsP, rowsP, formatP ) - FILE* file; - int* colsP; - int* rowsP; - int* formatP; - { - /* Check magic number. */ - *formatP = pm_readmagicnumber( file ); - switch ( PBM_FORMAT_TYPE(*formatP) ) - { - case PBM_TYPE: - pbm_readpbminitrest( file, colsP, rowsP ); - break; +pbm_readpbminit(FILE * const ifP, + int * const colsP, + int * const rowsP, + int * const formatP) { + + *formatP = pm_readmagicnumber(ifP); + + switch (PAM_FORMAT_TYPE(*formatP)) { + case PBM_TYPE: + pbm_readpbminitrest(ifP, colsP, rowsP); + break; + + case PGM_TYPE: + pm_error("The input file is a PGM, not a PBM. You may want to " + "convert it to PBM with 'pamditherbw | pamtopnm' or " + "'pamthreshold | pamtopnm'"); + + case PPM_TYPE: + pm_error("The input file is a PPM, not a PBM. You may want to " + "convert it to PBM with 'ppmtopgm', 'pamditherbw', and " + "'pamtopnm'"); + case PAM_TYPE: + pm_error("The input file is a PAM, not a PBM. " + "If it is a black and white image, you can convert it " + "to PBM with 'pamtopnm'"); + break; default: - pm_error( "bad magic number - not a pbm file" ); - } + pm_error("bad magic number - not a Netpbm file"); } + validateComputableSize(*colsP, *rowsP); +} + + void pbm_readpbmrow( file, bitrow, cols, format ) @@ -114,8 +157,8 @@ pbm_readpbmrow( file, bitrow, cols, format ) void -pbm_readpbmrow_packed(FILE * const file, - unsigned char * const packed_bits, +pbm_readpbmrow_packed(FILE * const fileP, + unsigned char * const packedBits, int const cols, int const format) { @@ -126,22 +169,22 @@ pbm_readpbmrow_packed(FILE * const file, /* We first clear the return buffer, then set ones where needed */ for (byteIndex = 0; byteIndex < pbm_packed_bytes(cols); ++byteIndex) - packed_bits[byteIndex] = 0x00; + packedBits[byteIndex] = 0x00; for (col = 0; col < cols; ++col) { unsigned char mask; - mask = getbit(file) << (7 - col % 8); - packed_bits[col / 8] |= mask; + mask = getbit(fileP) << (7 - col % 8); + packedBits[col / 8] |= mask; } } break; case RPBM_FORMAT: { int bytes_read; - bytes_read = fread(packed_bits, 1, pbm_packed_bytes(cols), file); + bytes_read = fread(packedBits, 1, pbm_packed_bytes(cols), fileP); if (bytes_read < pbm_packed_bytes(cols)) { - if (feof(file)) + if (feof(fileP)) if (bytes_read == 0) pm_error("Attempt to read a raw PBM image row, but " "no more rows left in file."); @@ -160,6 +203,65 @@ pbm_readpbmrow_packed(FILE * const file, +void +pbm_readpbmrow_bitoffset(FILE * const ifP, + unsigned char * const packedBits, + int const cols, + int const format, + unsigned int const offset) { +/*---------------------------------------------------------------------------- + Read PBM packed bitrow from file 'ifP' (raster format given by + 'cols' and 'format') and shift right 'offset' bits. + + Read it into packedBits[], preserving surrounding image data. + + Logic not tested for negative offsets. +-----------------------------------------------------------------------------*/ + unsigned int const rsh = offset % 8; + unsigned int const lsh = (8 - rsh) % 8; + unsigned char * const window = &packedBits[offset/8]; + /* Area of packed row buffer into which we read the image data. + Aligned to nearest byte boundary to the left, so the first + few bits might contain original data, not output. + */ + unsigned int const last = pbm_packed_bytes(cols+rsh) - 1; + /* Position within window of rightmost byte after shift */ + + /* The original leftmost and rightmost chars. */ + unsigned char const origHead = window[0]; + unsigned char const origEnd = window[last]; + + pbm_readpbmrow_packed(ifP, window, cols, format); + + if (rsh > 0) { + /* Target slot doesn't start on byte boundary; right-shift. */ + unsigned char carryover; + unsigned int i; + + carryover = (origHead >> lsh) << lsh; + + for (i = 0; i <= last; ++i) { + unsigned char const t = window[i] << lsh; + window[i] = carryover | window[i] >> rsh; + carryover = t; + } + } + + if ((cols + rsh) % 8 > 0) { + /* Adjust rightmost char */ + unsigned int const trs = (cols + rsh) % 8; + unsigned int const tls = 8 - trs; + unsigned char const rightBits = + ((unsigned char)(origEnd << trs) >> trs); + unsigned char const leftBits = + ((unsigned char)(window[last] >> tls) << tls); + + window[last] = leftBits | rightBits; + } +} + + + bit** pbm_readpbm( file, colsP, rowsP ) FILE* file; diff --git a/lib/libpbm3.c b/lib/libpbm3.c index 9c9bbd25..9200d30e 100644 --- a/lib/libpbm3.c +++ b/lib/libpbm3.c @@ -10,32 +10,23 @@ ** implied warranty. */ +#include <assert.h> + +#include "pm_c_util.h" #include "pbm.h" -#include "libpbm.h" + +#if HAVE_GCC_MMXSSE #include "bitreverse.h" +#endif -/* HAVE_MMX_SSE means we have the means to use MMX and SSE CPU facilities - to make PBM raster processing faster. +/* HAVE_GCC_MMXSSE means we have the means to use MMX and SSE CPU facilities + to make PBM raster processing faster. GCC only. The GNU Compiler -msse option makes SSE available. + For x86-32 with MMX/SSE, "-msse" must be explicitly given. + For x86-64 and AMD64, "-msse" is on by default. */ -#if defined(__GNUC__) && \ - (__GNUC__ * 100 + __GNUC_MINOR__ >= 301) && \ - (__GNUC__ * 100 + __GNUC_MINOR__ < 403) && \ - defined (__SSE__) -/* GCC 4.3 does have the facility, but it is different from what this - code knows how to use. In particular, the calls to - __builtin_ia32_pcmpeqb() and __builtin_ia32_pmovmskb() fail to - compile, with complaints of improper argument types. -*/ - -#define HAVE_MMX_SSE 1 -#else -#define HAVE_MMX_SSE 0 -#endif - - void pbm_writepbminit(FILE * const fileP, int const cols, @@ -65,13 +56,13 @@ writePackedRawRow(FILE * const fileP, } - +#if HAVE_GCC_MMXSSE static void packBitsWithMmxSse(FILE * const fileP, const bit * const bitrow, unsigned char * const packedBits, - int const cols, - int * const nextColP) { + unsigned int const cols, + unsigned int * const nextColP) { /*---------------------------------------------------------------------------- Pack the bits of bitrow[] into bytes at 'packedBits'. Going left to right, stop when there aren't enough bits left to fill a whole byte. Return @@ -81,7 +72,6 @@ packBitsWithMmxSse(FILE * const fileP, Use the Pentium MMX and SSE facilities to pack the bits quickly, but perform the exact same function as the simpler packBitsGeneric(). -----------------------------------------------------------------------------*/ -#if HAVE_MMX_SSE /* We use MMX/SSE facilities that operate on 8 bytes at once to pack the bits quickly. @@ -91,45 +81,37 @@ packBitsWithMmxSse(FILE * const fileP, The key machine instructions are: - PCMPEQB Packed CoMPare EQual Byte + PCMPGTB Packed CoMPare Greater Than Byte Compares 8 bytes in parallel - Result is x00 if equal, xFF if unequal for each byte + Result is x00 if greater than, xFF if not for each byte PMOVMSKB Packed MOVe MaSK Byte Result is a byte of the MSBs of 8 bytes - x00 xFF x00 xFF xFF xFF x00 x00 --> 01011100B = 0x5C - + x00 xFF x00 xFF xFF xFF x00 x00 --> 01011100B = 0x5C + + The result is actually a 32 bit int, but the higher bits are + always 0. (0x0000005C in the above case) EMMS Empty MMx State Free MMX registers - - Here's a one-statement version of the code in our foor loop. It's harder - to read, but if we find out this generates more efficient code, we could - use this. - - packedBits[col/8] - = bitreverse [ ~ (unsigned char) __builtin_ia32_pmovmskb ( - __builtin_ia32_pcmpeqb ( *(v8qi*) (&bitrow[col]), *(v8qi*) &zero64) - ) ]; */ - typedef int v8qi __attribute__ ((mode(V8QI))); - typedef int di __attribute__ ((mode(DI))); - di const zero64 = 0; /* to clear with PXOR */ + typedef char v8qi __attribute__ ((vector_size(8))); + typedef int di __attribute__ ((mode(DI))); unsigned int col; + v8qi const zero64 =(v8qi)((di)0); /* clear to zero */ for (col = 0; col + 7 < cols; col += 8) { + v8qi const compare = - __builtin_ia32_pcmpeqb(*(v8qi*) (&bitrow[col]), *(v8qi*) &zero64); - unsigned char const backwardWhiteMask = (unsigned char) - __builtin_ia32_pmovmskb(compare); - unsigned char const backwardBlackMask = ~backwardWhiteMask; + __builtin_ia32_pcmpgtb(*(v8qi*) (&bitrow[col]), (v8qi) zero64); + uint32_t const backwardBlackMask = __builtin_ia32_pmovmskb(compare); unsigned char const blackMask = bitreverse[backwardBlackMask]; packedBits[col/8] = blackMask; @@ -138,9 +120,20 @@ packBitsWithMmxSse(FILE * const fileP, __builtin_ia32_emms(); +} #else - if (bitreverse == bitreverse) {}; /* avoid unused vbl compiler warning */ +/* Avoid undefined function warning; never actually called */ + +#define packBitsWithMmxSse(a,b,c,d,e) packBitsGeneric(a,b,c,d,e) #endif + + + + +static unsigned int +bitValue(unsigned char const byteValue) { + + return byteValue == 0 ? 0 : 1; } @@ -149,10 +142,10 @@ static void packBitsGeneric(FILE * const fileP, const bit * const bitrow, unsigned char * const packedBits, - int const cols, - int * const nextColP) { + unsigned int const cols, + unsigned int * const nextColP) { /*---------------------------------------------------------------------------- - Pack the bits of bitrow[] into byts at 'packedBits'. Going left to right, + Pack the bits of bitrow[] into bytes at 'packedBits'. Going left to right, stop when there aren't enough bits left to fill a whole byte. Return as *nextColP the number of the next column after the rightmost one we packed. @@ -161,18 +154,16 @@ packBitsGeneric(FILE * const fileP, -----------------------------------------------------------------------------*/ unsigned int col; - #define iszero(x) ((x) == 0 ? 0 : 1) - for (col = 0; col + 7 < cols; col += 8) packedBits[col/8] = ( - iszero(bitrow[col+0]) << 7 | - iszero(bitrow[col+1]) << 6 | - iszero(bitrow[col+2]) << 5 | - iszero(bitrow[col+3]) << 4 | - iszero(bitrow[col+4]) << 3 | - iszero(bitrow[col+5]) << 2 | - iszero(bitrow[col+6]) << 1 | - iszero(bitrow[col+7]) << 0 + bitValue(bitrow[col+0]) << 7 | + bitValue(bitrow[col+1]) << 6 | + bitValue(bitrow[col+2]) << 5 | + bitValue(bitrow[col+3]) << 4 | + bitValue(bitrow[col+4]) << 3 | + bitValue(bitrow[col+5]) << 2 | + bitValue(bitrow[col+6]) << 1 | + bitValue(bitrow[col+7]) << 0 ); *nextColP = col; } @@ -180,50 +171,72 @@ packBitsGeneric(FILE * const fileP, static void +packPartialBytes(const bit * const bitrow, + unsigned int const cols, + unsigned int const nextCol, + unsigned char * const packedBits) { + + /* routine for partial byte at the end of packedBits[] + Prior to addition of the above enhancement, + this method was used for the entire process + */ + + unsigned int col; + int bitshift; + unsigned char item; + + bitshift = 7; /* initial value */ + item = 0; /* initial value */ + for (col = nextCol; col < cols; ++col, --bitshift) + if (bitrow[col] != 0) + item |= 1 << bitshift; + + packedBits[col/8] = item; +} + + + +static void writePbmRowRaw(FILE * const fileP, const bit * const bitrow, int const cols) { - int nextCol; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + unsigned char * packedBits; - unsigned char * const packedBits = pbm_allocrow_packed(cols); + packedBits = pbm_allocrow_packed(cols); - if (HAVE_MMX_SSE) - packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol); - else - packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol); + if (setjmp(jmpbuf) != 0) { + pbm_freerow_packed(packedBits); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int nextCol; - /* routine for partial byte at the end of packed_bits[] - Prior to addition of the above enhancement, - this method was used for the entire process - */ - - if (cols % 8 > 0) { - int col; - int bitshift; - unsigned char item; - - bitshift = 7; /* initial value */ - item = 0; /* initial value */ - for (col = nextCol; col < cols; ++col, --bitshift ) - if (bitrow[col] !=0) - item |= 1 << bitshift - ; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + if (HAVE_GCC_MMXSSE) + packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol); + else + packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol); + + if (cols % 8 > 0) + packPartialBytes(bitrow, cols, nextCol, packedBits); - packedBits[col/8] = item; + writePackedRawRow(fileP, packedBits, cols); + + pm_setjmpbuf(origJmpbufP); } - - writePackedRawRow(fileP, packedBits, cols); - pbm_freerow_packed(packedBits); } static void -writePbmRowPlain(FILE * const fileP, - bit * const bitrow, - int const cols) { +writePbmRowPlain(FILE * const fileP, + const bit * const bitrow, + int const cols) { int col, charcount; @@ -242,10 +255,10 @@ writePbmRowPlain(FILE * const fileP, void -pbm_writepbmrow(FILE * const fileP, - bit * const bitrow, - int const cols, - int const forceplain) { +pbm_writepbmrow(FILE * const fileP, + const bit * const bitrow, + int const cols, + int const forceplain) { if (!forceplain && !pm_plain_output) writePbmRowRaw(fileP, bitrow, cols); @@ -257,28 +270,119 @@ pbm_writepbmrow(FILE * const fileP, void pbm_writepbmrow_packed(FILE * const fileP, - const unsigned char * const packed_bits, + const unsigned char * const packedBits, int const cols, int const forceplain) { if (!forceplain && !pm_plain_output) - writePackedRawRow(fileP, packed_bits, cols); + writePackedRawRow(fileP, packedBits, cols); else { - bit *bitrow; - int col; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; bitrow = pbm_allocrow(cols); - for (col = 0; col < cols; ++col) - bitrow[col] = - packed_bits[col/8] & (0x80 >> (col%8)) ? PBM_BLACK : PBM_WHITE; - writePbmRowPlain(fileP, bitrow, cols); + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (col = 0; col < cols; ++col) + bitrow[col] = + packedBits[col/8] & (0x80 >> (col%8)) ? + PBM_BLACK : PBM_WHITE; + + writePbmRowPlain(fileP, bitrow, cols); + + pm_setjmpbuf(origJmpbufP); + } pbm_freerow(bitrow); } } +static unsigned char +leftBits(unsigned char const x, + unsigned int const n) { +/*---------------------------------------------------------------------------- + Clear rightmost (8-n) bits, retain leftmost (=high) n bits. +-----------------------------------------------------------------------------*/ + unsigned char buffer; + + assert(n < 8); + + buffer = x; + + buffer >>= (8-n); + buffer <<= (8-n); + + return buffer; +} + + + +void +pbm_writepbmrow_bitoffset(FILE * const fileP, + unsigned char * const packedBits, + unsigned int const cols, + int const format, + unsigned int const offset) { +/*---------------------------------------------------------------------------- + Write PBM row from a packed bit buffer 'packedBits, starting at the + specified offset 'offset' in the buffer. + + We destroy the buffer. +-----------------------------------------------------------------------------*/ + unsigned int const rsh = offset % 8; + unsigned int const lsh = (8 - rsh) % 8; + unsigned int const csh = cols % 8; + unsigned char * const window = &packedBits[offset/8]; + /* Area of packed row buffer from which we take the image data. + Aligned to nearest byte boundary to the left, so the first + few bits might be irrelvant. + + Also our work buffer, in which we shift bits and from which we + ultimately write the bits to the file. + */ + unsigned int const colByteCnt = pbm_packed_bytes(cols); + unsigned int const last = colByteCnt - 1; + /* Position within window of rightmost byte after shift */ + + bool const carryover = (csh == 0 || rsh + csh > 8); + /* TRUE: Input comes from colByteCnt bytes and one extra byte. + FALSE: Input comes from colByteCnt bytes. For example: + TRUE: xxxxxxii iiiiiiii iiiiiiii iiixxxxx cols=21, offset=6 + FALSE: xiiiiiii iiiiiiii iiiiiixx ________ cols=21, offset=1 + + We treat these differently for in the FALSE case the byte after + last (indicated by ________) may not exist. + */ + + if (rsh > 0) { + unsigned int const shiftBytes = carryover ? colByteCnt : colByteCnt-1; + + unsigned int i; + for (i = 0; i < shiftBytes; ++i) + window[i] = window[i] << rsh | window[i+1] >> lsh; + + if (!carryover) + window[last] = window[last] << rsh; + } + + if (csh > 0) + window[last] = leftBits(window[last], csh); + + pbm_writepbmrow_packed(fileP, window, cols, 0); +} + + + void pbm_writepbm(FILE * const fileP, bit ** const bits, diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c index c7b5a355..902eaf0d 100644 --- a/lib/libpbmfont.c +++ b/lib/libpbmfont.c @@ -14,6 +14,7 @@ ** implied warranty. */ +#include <assert.h> #include <string.h> #include "pm_c_util.h" @@ -667,9 +668,9 @@ pbm_defaultfont(const char * const name) { unsigned int row; if (strcmp(name, "fixed") != 0) - pm_error( "built-in font name unknown, try 'bdf' or 'fixed'" ); + pm_error( "built-in font name unknown, try 'bdf' or 'fixed'"); - defaultfont = pbm_allocarray( DEFAULTFONT_COLS, DEFAULTFONT_ROWS ); + defaultfont = pbm_allocarray(DEFAULTFONT_COLS, DEFAULTFONT_ROWS); for (row = 0; row < DEFAULTFONT_ROWS; ++row) { unsigned int col; for (col = 0; col < DEFAULTFONT_COLS; col += 32) { @@ -689,104 +690,200 @@ pbm_defaultfont(const char * const name) { } } } - retval = - pbm_dissectfont(defaultfont, DEFAULTFONT_ROWS, DEFAULTFONT_COLS); + pbm_dissectfont((const bit **)defaultfont, + DEFAULTFONT_ROWS, DEFAULTFONT_COLS); } return retval; } + +static void +findFirstBlankRow(const bit ** const font, + unsigned int const fcols, + unsigned int const frows, + unsigned int * const browP) { + + unsigned int row; + bool foundBlank; + + for (row = 0, foundBlank = false; row < frows / 6 && !foundBlank; ++row) { + unsigned int col; + bit col0Value = font[row][0]; + bool rowIsBlank; + rowIsBlank = true; /* initial assumption */ + for (col = 1; col < fcols; ++col) + if (font[row][col] != col0Value) + rowIsBlank = false; + + if (rowIsBlank) { + foundBlank = true; + *browP = row; + } + } + + if (!foundBlank) + pm_error("couldn't find blank pixel row in font"); +} + + + +static void +findFirstBlankCol(const bit ** const font, + unsigned int const fcols, + unsigned int const frows, + unsigned int * const bcolP) { + + unsigned int col; + bool foundBlank; + + for (col = 0, foundBlank = false; col < fcols / 6 && !foundBlank; ++col) { + unsigned int row; + bit row0Value = font[0][col]; + bool colIsBlank; + colIsBlank = true; /* initial assumption */ + for (row = 1; row < frows; ++row) + if (font[row][col] != row0Value) + colIsBlank = false; + + if (colIsBlank) { + foundBlank = true; + *bcolP = col; + } + } + + if (!foundBlank) + pm_error("couldn't find blank pixel column in font"); +} + + + +static void +computeCharacterSize(const bit ** const font, + unsigned int const fcols, + unsigned int const frows, + unsigned int * const cellWidthP, + unsigned int * const cellHeightP, + unsigned int * const charWidthP, + unsigned int * const charHeightP) { + + unsigned int firstBlankRow; + unsigned int firstBlankCol; + unsigned int heightLast11Rows; + + findFirstBlankRow(font, fcols, frows, &firstBlankRow); + + findFirstBlankCol(font, fcols, frows, &firstBlankCol); + + heightLast11Rows = frows - firstBlankRow; + + if (heightLast11Rows % 11 != 0) + pm_error("The rows of characters in the font do not appear to " + "be all the same height. The last 11 rows are %u pixel " + "rows high (from pixel row %u up to %u), " + "which is not a multiple of 11.", + heightLast11Rows, firstBlankRow, frows); + else { + unsigned int widthLast15Cols; + + *cellHeightP = heightLast11Rows / 11; + + widthLast15Cols = fcols - firstBlankCol; + + if (widthLast15Cols % 15 != 0) + pm_error("The columns of characters in the font do not appear to " + "be all the same width. " + "The last 15 columns are %u pixel " + "columns wide (from pixel col %u up to %u), " + "which is not a multiple of 15.", + widthLast15Cols, firstBlankCol, fcols); + else { + *cellWidthP = widthLast15Cols / 15; + + *charWidthP = firstBlankCol; + *charHeightP = firstBlankRow; + } + } +} + + + struct font* -pbm_dissectfont(bit ** const font, - int const frows, - int const fcols) { +pbm_dissectfont(const bit ** const font, + unsigned int const frows, + unsigned int const fcols) { /* - ** This routine expects a font bitmap representing the following text: - ** - ** (0,0) - ** M ",/^_[`jpqy| M - ** - ** / !"#$%&'()*+ / - ** < ,-./01234567 < - ** > 89:;<=>?@ABC > - ** @ DEFGHIJKLMNO @ - ** _ PQRSTUVWXYZ[ _ - ** { \]^_`abcdefg { - ** } hijklmnopqrs } - ** ~ tuvwxyz{|}~ ~ - ** - ** M ",/^_[`jpqy| M - ** - ** The bitmap must be cropped exactly to the edges. - ** - ** The border you see is irrelevant. The 12 x 8 array in the center is - ** the font. The top left character there belongs to code point 0, and - ** the code points increase in standard reading order, so the bottom - ** right character is code point 127. You can't define code points - ** < 32 or > 127 with this font format. - ** - ** The dissection works by finding the first blank row and column; that - ** gives the height and width of the maximum-sized character, which is - ** not too useful. But the distance from there to the opposite side is - ** an integral multiple of the cell size, and that's what we need. Then - ** it's just a matter of filling in all the coordinates. - ** - ** The difference between cell_height, cell_width and char_height, - ** char_width is that the first is the size of the cell including - ** spacing, while the second is just the actual maximum-size character. - */ - int cell_width, cell_height, char_width, char_height; - int brow, bcol, row, col, d, ch, r, c, i; - struct font* fn; - struct glyph* glyph; + This routine expects a font bitmap representing the following text: + + (0,0) + M ",/^_[`jpqy| M + + / !"#$%&'()*+ / + < ,-./01234567 < + > 89:;<=>?@ABC > + @ DEFGHIJKLMNO @ + _ PQRSTUVWXYZ[ _ + { \]^_`abcdefg { + } hijklmnopqrs } + ~ tuvwxyz{|}~ ~ + + M ",/^_[`jpqy| M + + The bitmap must be cropped exactly to the edges. + + The characters in the border you see are irrelevant except for + character size compuations. The 12 x 8 array in the center is + the font. The top left character there belongs to code point + 0, and the code points increase in standard reading order, so + the bottom right character is code point 127. You can't define + code points < 32 or > 127 with this font format. + + The characters in the top and bottom border rows must include a + character with the lowest reach of any in the font (e.g. "y", + "_") and one with the highest reach (e.g. '"'). The characters + in the left and right border columns must include characters + with the rightmost and leftmost reach of any in the font + (e.g. "M" for both). + + The border must be separated from the font by one blank text + row or text column. + + The dissection works by finding the first blank row and column; + i.e the lower right corner of the "M" in the upper left corner + of the matrix. That gives the height and width of the + maximum-sized character, which is not too useful. But the + distance from there to the opposite side is an integral + multiple of the cell size, and that's what we need. Then it's + just a matter of filling in all the coordinates. */ + + unsigned int cellWidth, cellHeight; + /* Dimensions in pixels of each cell of the font -- that + includes the glyph and the white space above and to the + right of it. Each cell is a tile of the font image. The + top character row and left character row don't count -- + those cells are smaller because they are missing the white + space. + */ + unsigned int charWidth, charHeight; + /* Maximum dimensions of glyph itself, inside its cell */ + + int row, col, ch, r, c, i; + struct font * fn; + struct glyph * glyph; char* bmap; - bit b; - - /* Find first blank row. */ - for ( brow = 0; brow < frows / 6; ++brow ) { - b = font[brow][0]; - for ( col = 1; col < fcols; ++col ) - if ( font[brow][col] != b ) - goto nextrow; - goto gotblankrow; - nextrow: ; - } - pm_error( "couldn't find blank row in font" ); - - gotblankrow: - /* Find first blank col. */ - for ( bcol = 0; bcol < fcols / 8; ++bcol ) { - b = font[0][bcol]; - for ( row = 1; row < frows; ++row ) - if ( font[row][bcol] != b ) - goto nextcol; - goto gotblankcol; - nextcol: ; - } - pm_error( "couldn't find blank col in font" ); - - gotblankcol: - /* Now compute character cell size. */ - d = frows - brow; - cell_height = d / 11; - if ( cell_height * 11 != d ) - pm_error( "problem computing character cell height" ); - d = fcols - bcol; - cell_width = d / 15; - if ( cell_width * 15 != d ) - pm_error( "problem computing character cell width" ); - char_height = brow; - char_width = bcol; + + computeCharacterSize(font, fcols, frows, + &cellWidth, &cellHeight, &charWidth, &charHeight); /* Now convert to a general font */ MALLOCVAR(fn); - if ( fn == NULL ) - pm_error( "out of memory allocating font structure" ); + if (fn == NULL) + pm_error("out of memory allocating font structure"); - fn->maxwidth = char_width; - fn->maxheight = char_height; + fn->maxwidth = charWidth; + fn->maxheight = charHeight; fn->x = fn->y = 0; fn->oldfont = font; @@ -808,8 +905,8 @@ pbm_dissectfont(bit ** const font, pm_error( "out of memory allocating glyph data" ); /* Now fill in the 0,0 coords. */ - row = cell_height * 2; - col = cell_width * 2; + row = cellHeight * 2; + col = cellWidth * 2; for (i = 0; i < firstCodePoint; ++i) fn->glyph[i] = NULL; @@ -817,7 +914,7 @@ pbm_dissectfont(bit ** const font, glyph[ch].width = fn->maxwidth; glyph[ch].height = fn->maxheight; glyph[ch].x = glyph[ch].y = 0; - glyph[ch].xadd = cell_width; + glyph[ch].xadd = cellWidth; for ( r = 0; r < glyph[ch].height; ++r ) for ( c = 0; c < glyph[ch].width; ++c ) @@ -828,10 +925,10 @@ pbm_dissectfont(bit ** const font, fn->glyph[firstCodePoint + ch] = &glyph[ch]; - col += cell_width; - if ( col >= cell_width * 14 ) { - col = cell_width * 2; - row += cell_height; + col += cellWidth; + if ( col >= cellWidth * 14 ) { + col = cellWidth * 2; + row += cellHeight; } } for (i = firstCodePoint + nCharsInFont; i < 256; ++i) @@ -868,18 +965,21 @@ pbm_loadfont(const char * const filename) -struct font* pbm_loadpbmfont(const char * const filename) -{ - FILE* ifp; - bit** font; +struct font * +pbm_loadpbmfont(const char * const filename) { + + FILE * ifP; + bit ** font; int fcols, frows; - ifp = pm_openr( filename ); - font = pbm_readpbm( ifp, &fcols, &frows ); - pm_close( ifp ); - return pbm_dissectfont( font, frows, fcols ); + ifP = pm_openr(filename); + font = pbm_readpbm(ifP, &fcols, &frows); + pm_close(ifP); + return pbm_dissectfont((const bit **)font, frows, fcols); } + + void pbm_dumpfont( fn ) struct font* fn; @@ -971,166 +1071,400 @@ pbm_dumpfont( fn ) /* Routines for loading a BDF font file */ -static int readline ARGS((FILE* fp, char* buf, char* arg[])); - -#define expect(str) if (readline(fp, line, arg) < 0 || strcmp(arg[0], (str))) \ - { fclose(fp); return 0; } -struct font* pbm_loadbdffont(const char * const name) -{ - FILE* fp; - char line[1024], *arg[32], *hex; - char *b; - int n, numchar, hdig, encoding; - struct font* font; - struct glyph* glyph; +static unsigned int +mk_argvn(char * const s, + const char ** const vec, + unsigned int const mk_max) { - if (!(fp = fopen(name, "rb"))) - return 0; + int n; + char * p; - expect("STARTFONT"); + p = &s[0]; + n = 0; - MALLOCVAR(font); - if (font == NULL) - pm_error("no memory for font"); - font->oldfont = 0; - { - /* Initialize all characters to nonexistent; we will fill the ones we - find in the bdf file later. - */ - int i; - for (i = 0; i < 256; i++) - font->glyph[i] = NULL; + while (*p) { + if (ISSPACE(*p)) + *p++ = '\0'; + else { + vec[n++] = p; + if (n >= mk_max) + break; + while (*p && !ISSPACE(*p)) + ++p; + } } + vec[n] = NULL; - while (readline(fp, line, arg) >= 0) { - if (!strcmp(arg[0], "COMMENT")) - continue; - if (!strcmp(arg[0], "SIZE")) - continue; - - if (!strcmp(arg[0], "STARTPROPERTIES")) { - n = atoi(arg[1]); - for (; n > 0 && readline(fp, line, arg) >= 0; n--) - ; - } - else if (!strcmp(arg[0], "FONTBOUNDINGBOX")) { - font->maxwidth = atoi(arg[1]); - font->maxheight = atoi(arg[2]); - font->x = atoi(arg[3]); - font->y = atoi(arg[4]); - } - else if (!strcmp(arg[0], "ENDFONT")) { - fclose(fp); - return font; + return n; +} + + + +static int +readline(FILE * const fp, + char * const buf, + const char ** const arg) { + + int retval; + char * rc; + + rc = fgets(buf, 1024, fp); + if (rc == NULL) + retval = -1; + else + retval = mk_argvn(buf, arg, 32); + + return retval; +} + + + +static void +readBitmap(FILE * const fp, + unsigned int const glyphWidth, + unsigned int const glyphHeight, + const char * const charName, + unsigned char * const bmap) { + + int n; + unsigned int bmapIndex; + + bmapIndex = 0; + + for (n = glyphHeight; n > 0; --n) { + int i; /* dot counter */ + int rc; + char * hex; + char line[1024]; + const char * arg[32]; + + rc = readline(fp, line, arg); + + if (rc < 0) + pm_error("End of file in bitmap for character '%s' in BDF " + "font file.", charName); + + hex = line; + for (i = glyphWidth; i > 0; i -= 4) { + char hdig; + unsigned int hdigValue; + hdig = *hex++; + if (hdig >= '0' && hdig <= '9') + hdigValue = hdig - '0'; + else if (hdig >= 'a' && hdig <= 'f') + hdigValue = 10 + (hdig - 'a'); + else if (hdig >= 'A' && hdig <= 'F') + hdigValue = 10 + (hdig - 'A'); + else + pm_error("Invalid hex digit '%c' in line '%s' of " + "bitmap for character '%s' " + "in BDF font file", hdig, line, charName); + + if (i > 0) + bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0; + if (i > 1) + bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0; + if (i > 2) + bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0; + if (i > 3) + bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0; } - else if (!strcmp(arg[0], "CHARS")) { - numchar = atoi(arg[1]); - while (numchar > 0) { - if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } - if (!strcmp(arg[0], "COMMENT")) - continue; - if (strcmp(arg[0], "STARTCHAR")) { fclose(fp); return 0; } - MALLOCVAR(glyph); - if (glyph == NULL) - pm_error("no memory for font glyph"); - - expect("ENCODING"); - if ((encoding = atoi(arg[1])) < 0) { - if (arg[2]) - encoding = atoi(arg[2]); - else { - while (readline(fp, line, arg) >= 0) - if (!strcmp(arg[0], "ENDCHAR")) - break; + } +} + + + +static void +createBmap(unsigned int const glyphWidth, + unsigned int const glyphHeight, + FILE * const fp, + const char * const charName, + const char ** const bmapP) { + + char line[1024]; + const char * arg[32]; + unsigned char * bmap; + int rc; + + if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight) + pm_error("Ridiculously large glyph"); + + MALLOCARRAY(bmap, glyphWidth * glyphHeight); + + if (!bmap) + pm_error("no memory for font glyph byte map"); + + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file encountered reading font glyph byte map from " + "BDF font file."); + + if (streq(arg[0], "ATTRIBUTES")) { + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file encountered after ATTRIBUTES in BDF " + "font file."); + } + if (!streq(arg[0], "BITMAP")) + pm_error("'%s' found where BITMAP expected in definition of " + "character '%s' in BDF font file.", line, charName); + + assert(streq(arg[0], "BITMAP")); + + readBitmap(fp, glyphWidth, glyphHeight, charName, bmap); + + *bmapP = (char *)bmap; +} + + + +static void +expect(FILE * const fp, + const char * const expected, + const char ** const arg) { + + char line[1024]; + int rc; + + rc = readline(fp, line, arg); + + if (rc < 0) + pm_error("EOF in BDF font file where '%s' expected", expected); + else if (!streq(arg[0], expected)) + pm_error("'%s' where '%s' expected in BDF font file", + line, expected); +} + + + +static void +skipCharacter(FILE * const fp) { +/*---------------------------------------------------------------------------- + In BDF font file 'fp', skip through the end of the character we are + presently in. +-----------------------------------------------------------------------------*/ + bool endChar; - numchar--; - continue; - } - } - expect("SWIDTH"); - expect("DWIDTH"); - glyph->xadd = atoi(arg[1]); - expect("BBX"); - glyph->width = atoi(arg[1]); - glyph->height = atoi(arg[2]); - glyph->x = atoi(arg[3]); - glyph->y = atoi(arg[4]); - - b = (char*)malloc(glyph->width * glyph->height * sizeof(char)); - glyph->bmap = b; - if (!glyph->bmap) - pm_error("no memory for font glyph byte map"); - - if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } - if (!strcmp(arg[0], "ATTRIBUTES")) - if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } - - for (n = glyph->height; n > 0; n--) { - int i; /* dot counter */ - if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } - hex = line; - for (i = glyph->width; i > 0; i -= 4) { - hdig = *hex++; - if (hdig >= '0' && hdig <= '9') - hdig -= '0'; - else if (hdig >= 'a' && hdig <= 'f') - hdig -= 'a' - 10; - else if (hdig >= 'A' && hdig <= 'F') - hdig -= 'A' - 10; + endChar = FALSE; - *b++ = hdig & 8 ? 1 : 0; - if (i > 1) *b++ = hdig & 4 ? 1 : 0; - if (i > 2) *b++ = hdig & 2 ? 1 : 0; - if (i > 3) *b++ = hdig & 1; - } - } + while (!endChar) { + char line[1024]; + const char * arg[32]; + int rc; + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file in the middle of a character (before " + "ENDCHAR) in BDF font file."); + endChar = streq(arg[0], "ENDCHAR"); + } +} + + + +static void +validateEncoding(const char ** const arg, + unsigned int * const codepointP, + bool * const badCodepointP) { +/*---------------------------------------------------------------------------- + With arg[] being the ENCODING statement from the font, return as + *codepointP the codepoint that it indicates (code point is the character + code, e.g. in ASCII, 48 is '0'). + + But if the statement doesn't give an acceptable codepoint return + *badCodepointP == TRUE. +-----------------------------------------------------------------------------*/ + + bool gotCodepoint; + bool badCodepoint; + unsigned int codepoint; + + if (atoi(arg[1]) >= 0) { + codepoint = atoi(arg[1]); + gotCodepoint = true; + } else { + if (arg[2]) { + codepoint = atoi(arg[2]); + gotCodepoint = true; + } else + gotCodepoint = false; + } + if (gotCodepoint) { + if (codepoint > 255) + badCodepoint = true; + else + badCodepoint = false; + } else + badCodepoint = true; + + *badCodepointP = badCodepoint; + *codepointP = codepoint; +} + + + + +static void +processCharsLine(FILE * const fp, + const char ** const arg, + struct font * const fontP) { - expect("ENDCHAR"); + unsigned int const nCharacters = atoi(arg[1]); - if (encoding < 256) - /* We ignore any characters with codes that don't fit - in 8 bits. We may want to change this someday. - */ - font->glyph[encoding] = glyph; + unsigned int nCharsDone; - numchar--; + nCharsDone = 0; + + while (nCharsDone < nCharacters) { + char line[1024]; + const char * arg[32]; + int rc; + + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file after CHARS reading BDF font file"); + + if (streq(arg[0], "COMMENT")) { + /* ignore */ + } else if (!streq(arg[0], "STARTCHAR")) + pm_error("no STARTCHAR after CHARS in BDF font file"); + else { + const char * const charName = arg[1]; + struct glyph * glyphP; + unsigned int codepoint; + bool badCodepoint; + + assert(streq(arg[0], "STARTCHAR")); + + MALLOCVAR(glyphP); + + if (glyphP == NULL) + pm_error("no memory for font glyph for '%s' character", + charName); + + { + const char * arg[32]; + expect(fp, "ENCODING", arg); + + validateEncoding(arg, &codepoint, &badCodepoint); + } + if (badCodepoint) + skipCharacter(fp); + else { + { + const char * arg[32]; + expect(fp, "SWIDTH", arg); + } + { + const char * arg[32]; + + expect(fp, "DWIDTH", arg); + glyphP->xadd = atoi(arg[1]); + } + { + const char * arg[32]; + + expect(fp, "BBX", arg); + glyphP->width = atoi(arg[1]); + glyphP->height = atoi(arg[2]); + glyphP->x = atoi(arg[3]); + glyphP->y = atoi(arg[4]); + } + createBmap(glyphP->width, glyphP->height, fp, charName, + &glyphP->bmap); + + { + const char * arg[32]; + expect(fp, "ENDCHAR", arg); + } + assert(codepoint < 256); /* Ensured by validateEncoding() */ + + fontP->glyph[codepoint] = glyphP; } + ++nCharsDone; } } - return font; } -static int readline(fp, buf, arg) -FILE* fp; -char* buf; -char* arg[]; -{ - if (!fgets(buf, 1024, fp)) - return -1; - - return mk_argvn(buf, arg, 32); -} -int mk_argvn(s, vec, mk_max) -char* s; -char* vec[]; -int mk_max; -{ - int n; - n = 0; - while (*s) { - if (ISSPACE(*s)) { - *s++ = '\0'; - continue; +static void +processFontLine(FILE * const fp, + const char * const line, + const char ** const arg, + struct font * const fontP, + bool * const endOfFontP) { + + *endOfFontP = FALSE; /* initial assumption */ + + if (streq(arg[0], "COMMENT")) { + /* ignore */ + } else if (streq(arg[0], "SIZE")) { + /* ignore */ + } else if (streq(arg[0], "STARTPROPERTIES")) { + unsigned int const propCount = atoi(arg[1]); + unsigned int i; + for (i = 0; i < propCount; ++i) { + char line[1024]; + const char * arg[32]; + int rc; + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file after STARTPROPERTIES in BDF font file"); } - vec[n++] = s; - if (n >= mk_max) - break; - while (*s && !ISSPACE(*s)) - s++; + } else if (streq(arg[0], "FONTBOUNDINGBOX")) { + fontP->maxwidth = atoi(arg[1]); + fontP->maxheight = atoi(arg[2]); + fontP->x = atoi(arg[3]); + fontP->y = atoi(arg[4]); + } else if (streq(arg[0], "ENDFONT")) { + *endOfFontP = true; + } else if (!strcmp(arg[0], "CHARS")) + processCharsLine(fp, arg, fontP); +} + + + +struct font * +pbm_loadbdffont(const char * const name) { + + FILE * fp; + char line[1024]; + const char * arg[32]; + struct font * fontP; + bool endOfFont; + + fp = fopen(name, "rb"); + if (!fp) + pm_error("Unable to open BDF font file name '%s'. errno=%d (%s)", + name, errno, strerror(errno)); + + expect(fp, "STARTFONT", arg); + + MALLOCVAR(fontP); + if (fontP == NULL) + pm_error("no memory for font"); + + fontP->oldfont = 0; + { + /* Initialize all characters to nonexistent; we will fill the ones we + find in the bdf file later. + */ + unsigned int i; + for (i = 0; i < 256; i++) + fontP->glyph[i] = NULL; } - vec[n] = 0; - return n; + fontP->x = fontP->y = 0; + + endOfFont = FALSE; + + while (!endOfFont) { + int rc; + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file before ENDFONT statement in BDF font file"); + + processFontLine(fp, line, arg, fontP, &endOfFont); + } + return fontP; } diff --git a/lib/libpgm.h b/lib/libpgm.h index ba51f9a9..7523faaf 100644 --- a/lib/libpgm.h +++ b/lib/libpgm.h @@ -12,13 +12,4 @@ pgm_readpgminitrest(FILE * const file, int * const rowsP, gray * const maxvalP); -gray -pgm_getrawsample(FILE * const file, - gray const maxval); - -void -pgm_writerawsample(FILE * const fileP, - gray const val, - gray const maxval); - #endif diff --git a/lib/libpgm1.c b/lib/libpgm1.c index 75fa365b..4d93e4be 100644 --- a/lib/libpgm1.c +++ b/lib/libpgm1.c @@ -10,7 +10,7 @@ ** implied warranty. */ -/* See libpbm.c for the complicated explanation of this 32/64 bit file +/* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 @@ -28,6 +28,7 @@ #include "libpam.h" #include "fileio.h" #include "mallocvar.h" +#include "nstring.h" gray * @@ -49,13 +50,14 @@ void pgm_init(int * const argcP, char ** const argv) { - pbm_init( argcP, argv ); + pbm_init(argcP, argv); } void -pgm_nextimage(FILE * const file, int * const eofP) { +pgm_nextimage(FILE * const file, + int * const eofP) { pm_nextimage(file, eofP); } @@ -121,11 +123,6 @@ pgm_readpgminit(FILE * const fileP, /* Check magic number. */ realFormat = pm_readmagicnumber(fileP); switch (PAM_FORMAT_TYPE(realFormat)) { - case PGM_TYPE: - *formatP = realFormat; - pgm_readpgminitrest(fileP, colsP, rowsP, maxvalP); - break; - case PBM_TYPE: *formatP = realFormat; pbm_readpbminitrest(fileP, colsP, rowsP); @@ -149,6 +146,15 @@ pgm_readpgminit(FILE * const fileP, *maxvalP = PGM_MAXMAXVAL; break; + case PGM_TYPE: + *formatP = realFormat; + pgm_readpgminitrest(fileP, colsP, rowsP, maxvalP); + break; + + case PPM_TYPE: + pm_error("Input file is a PPM, which this program cannot process. " + "You may want to convert it to PGM with 'ppmtopgm'"); + case PAM_TYPE: pnm_readpaminitrestaspnm(fileP, colsP, rowsP, maxvalP, formatP); @@ -158,40 +164,106 @@ pgm_readpgminit(FILE * const fileP, break; default: - pm_error("bad magic number - not a pgm or pbm file"); + pm_error("bad magic number - not a Netpbm file"); } validateComputableSize(*colsP, *rowsP); } -gray -pgm_getrawsample(FILE * const file, - gray const maxval) { +static void +readRpgmRow(FILE * const fileP, + gray * const grayrow, + int const cols, + gray const maxval, + int const format) { + + unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; + int const bytesPerRow = cols * bytesPerSample; + + unsigned char * rowBuffer; + const char * error; + + MALLOCARRAY(rowBuffer, bytesPerRow); + if (rowBuffer == NULL) + asprintfN(&error, "Unable to allocate memory for row buffer " + "for %u columns", cols); + else { + ssize_t rc; + rc = fread(rowBuffer, 1, bytesPerRow, fileP); + if (rc == 0) + asprintfN(&error, "Error reading row. fread() errno=%d (%s)", + errno, strerror(errno)); + else if (rc != bytesPerRow) + asprintfN(&error, "Error reading row. Short read of %u bytes " + "instead of %u", rc, bytesPerRow); + else { + error = NULL; + if (maxval < 256) { + unsigned int col; + for (col = 0; col < cols; ++col) + grayrow[col] = (gray)rowBuffer[col]; + } else { + unsigned int bufferCursor; + unsigned int col; + + bufferCursor = 0; /* Start at beginning of rowBuffer[] */ + + for (col = 0; col < cols; ++col) { + gray g; + + g = rowBuffer[bufferCursor++] << 8; + g |= rowBuffer[bufferCursor++]; + + grayrow[col] = g; + } + } + } + free(rowBuffer); + } + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } +} + + - if (maxval < 256) { - /* The sample is just one byte. Read it. */ - return(pm_getrawbyte(file)); +static void +readPbmRow(FILE * const fileP, + gray * const grayrow, + int const cols, + gray const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(cols); + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); } else { - /* The sample is two bytes. Read both. */ - unsigned char byte_pair[2]; - size_t pairs_read; - - pairs_read = fread(&byte_pair, 2, 1, file); - if (pairs_read == 0) - pm_error("EOF /read error while reading a long sample"); - /* This could be a few instructions faster if exploited the internal - format (i.e. endianness) of a pixval. Then we might be able to - skip the shifting and oring. - */ - return((byte_pair[0]<<8) | byte_pair[1]); + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pbm_readpbmrow(fileP, bitrow, cols, format); + for (col = 0; col < cols; ++col) + grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0; + + pm_setjmpbuf(origJmpbufP); } + pbm_freerow(bitrow); } void -pgm_readpgmrow(FILE * const file, +pgm_readpgmrow(FILE * const fileP, gray * const grayrow, int const cols, gray const maxval, @@ -201,7 +273,7 @@ pgm_readpgmrow(FILE * const file, case PGM_FORMAT: { unsigned int col; for (col = 0; col < cols; ++col) { - grayrow[col] = pm_getuint(file); + grayrow[col] = pm_getuint(fileP); if (grayrow[col] > maxval) pm_error("value out of bounds (%u > %u)", grayrow[col], maxval); @@ -209,86 +281,56 @@ pgm_readpgmrow(FILE * const file, } break; - case RPGM_FORMAT: { - unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; - 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); - - rc = fread(rowBuffer, 1, bytesPerRow, file); - if (rc == 0) - pm_error("Error reading row. fread() errno=%d (%s)", - errno, strerror(errno)); - else if (rc != bytesPerRow) - pm_error("Error reading row. Short read of %u bytes " - "instead of %u", rc, bytesPerRow); - - if (maxval < 256) { - unsigned int col; - for (col = 0; col < cols; ++col) - grayrow[col] = (gray)rowBuffer[col]; - } else { - unsigned int bufferCursor; - unsigned int col; - - bufferCursor = 0; /* Start at beginning of rowBuffer[] */ - - for (col = 0; col < cols; ++col) { - gray g; - - g = rowBuffer[bufferCursor++] << 8; - g |= rowBuffer[bufferCursor++]; - - grayrow[col] = g; - } - } - free(rowBuffer); - } + case RPGM_FORMAT: + readRpgmRow(fileP, grayrow, cols, maxval, format); break; case PBM_FORMAT: - case RPBM_FORMAT: { - bit * bitrow; - int col; - - bitrow = pbm_allocrow(cols); - pbm_readpbmrow( file, bitrow, cols, format ); - for (col = 0; col < cols; ++col) - grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0; - pbm_freerow(bitrow); - } + case RPBM_FORMAT: + readPbmRow(fileP, grayrow, cols, maxval, format); break; default: - pm_error( "can't happen" ); + pm_error("can't happen"); } } gray ** -pgm_readpgm(FILE * const file, +pgm_readpgm(FILE * const fileP, int * const colsP, int * const rowsP, gray * const maxvalP) { - gray** grays; - int row; + gray ** grays; + int rows, cols; + gray maxval; int format; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; - pgm_readpgminit( file, colsP, rowsP, maxvalP, &format ); + pgm_readpgminit(fileP, &cols, &rows, &maxval, &format); - grays = pgm_allocarray( *colsP, *rowsP ); - - for ( row = 0; row < *rowsP; ++row ) - pgm_readpgmrow( file, grays[row], *colsP, *maxvalP, format ); + grays = pgm_allocarray(cols, rows); + + if (setjmp(jmpbuf) != 0) { + pgm_freearray(grays, rows); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < rows; ++row) + pgm_readpgmrow(fileP, grays[row], cols, maxval, format); + + pm_setjmpbuf(origJmpbufP); + } + *colsP = cols; + *rowsP = rows; + *maxvalP = maxval; return grays; } @@ -319,6 +361,5 @@ pgm_check(FILE * const file, pm_filepos const need_raster_size = rows * bytes_per_row; pm_check(file, check_type, need_raster_size, retval_p); - } } diff --git a/lib/libpgm2.c b/lib/libpgm2.c index 7a04f09b..650d2cb5 100644 --- a/lib/libpgm2.c +++ b/lib/libpgm2.c @@ -16,7 +16,6 @@ #include "pm_c_util.h" #include "mallocvar.h" #include "pgm.h" -#include "libpgm.h" @@ -57,35 +56,6 @@ putus(unsigned short const n, -void -pgm_writerawsample(FILE * const fileP, - gray const val, - gray const maxval) { - - if (maxval < 256) { - /* Samples fit in one byte, so write just one byte */ - int rc; - rc = putc(val, fileP); - if (rc == EOF) - pm_error("Error writing single byte sample to file"); - } else { - /* Samples are too big for one byte, so write two */ - int n_written; - unsigned char outval[2]; - /* We could save a few instructions if we exploited the internal - format of a gray, i.e. its endianness. Then we might be able - to skip the shifting and anding. - */ - outval[0] = val >> 8; - outval[1] = val & 0xFF; - n_written = fwrite(&outval, 2, 1, fileP); - if (n_written == 0) - pm_error("Error writing double byte sample to file"); - } -} - - - static void format1bpsRow(const gray * const grayrow, unsigned int const cols, diff --git a/lib/libpm.c b/lib/libpm.c index 2e563a09..1089da3c 100644 --- a/lib/libpm.c +++ b/lib/libpm.c @@ -1,69 +1,45 @@ /************************************************************************** libpm.c *************************************************************************** - This is the most fundamental Netpbm library. It contains routines - not specific to any particular Netpbm format. + This file contains fundamental libnetpbm services. Some of the subroutines in this library are intended and documented for use by Netpbm users, but most of them are just used by other Netpbm library subroutines. - - Before May 2001, this function was served by the libpbm library - (in addition to being the library for handling the PBM format). - **************************************************************************/ -#define _SVID_SOURCE - /* Make sure P_tmpdir is defined in GNU libc 2.0.7 (_XOPEN_SOURCE 500 - does it in other libc's). pm_config.h defines TMPDIR as P_tmpdir - in some environments. - */ -#define _XOPEN_SOURCE 500 /* Make sure ftello, fseeko are defined */ -#define _LARGEFILE_SOURCE 1 /* Make sure ftello, fseeko are defined */ -#define _LARGEFILE64_SOURCE 1 -#define _FILE_OFFSET_BITS 64 - /* This means ftello() is really ftello64() and returns a 64 bit file - position. Unless the C library doesn't have ftello64(), in which - case ftello() is still just ftello(). - - Likewise for all the other C library file functions. - And off_t and fpos_t are 64 bit types instead of 32. Consequently, - pm_filepos_t might be 64 bits instead of 32. - */ -#define _LARGE_FILES - /* This does for AIX what _FILE_OFFSET_BITS=64 does for GNU */ -#define _LARGE_FILE_API - /* This makes the the x64() functions available on AIX */ +#define _XOPEN_SOURCE 500 /* Make sure ftello, fseeko are defined */ +#include <unistd.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <errno.h> #include <setjmp.h> -#ifdef __DJGPP__ - #include <io.h> -#endif +#include <time.h> +#include <limits.h> #include "pm_c_util.h" +#include "mallocvar.h" #include "version.h" #include "compile.h" #include "nstring.h" #include "shhopt.h" -#include "mallocvar.h" + #include "pm.h" /* The following are set by pm_init(), then used by subsequent calls to other pm_xxx() functions. */ -static const char* pm_progname; -static bool pm_showmessages; - /* Programs should display informational messages (because the user didn't - specify the --quiet option). - */ +const char * pm_progname; int pm_plain_output; /* Boolean: programs should produce output in plain format */ +static bool pm_showmessages; + /* Programs should display informational messages (because the user didn't + specify the --quiet option). + */ static jmp_buf * pm_jmpbufP = NULL; /* A description of the point to which the program should hyperjump if a libnetpbm function encounters an error (libnetpbm functions @@ -76,6 +52,17 @@ static jmp_buf * pm_jmpbufP = NULL; NULL, which is the default value, means when a libnetpbm function encounters an error, it causes the process to exit. */ +static pm_usererrormsgfn * userErrorMsgFn = NULL; + /* A function to call to issue an error message. + + NULL means use the library default: print to Standard Error + */ + +static pm_usermessagefn * userMessageFn = NULL; + /* A function to call to issue an error message. + + NULL means use the library default: print to Standard Error + */ @@ -108,20 +95,24 @@ pm_longjmp(void) { void -pm_usage(const char usage[]) { - pm_error("usage: %s %s", pm_progname, usage); +pm_setusererrormsgfn(pm_usererrormsgfn * fn) { + + userErrorMsgFn = fn; } void -pm_perror(const char reason[] ) { +pm_setusermessagefn(pm_usermessagefn * fn) { - if (reason != NULL && strlen(reason) != 0) - pm_error("%s - errno=%d (%s)", reason, errno, strerror(errno)); - else - pm_error("Something failed with errno=%d (%s)", - errno, strerror(errno)); + userMessageFn = fn; +} + + + +void +pm_usage(const char usage[]) { + pm_error("usage: %s %s", pm_progname, usage); } @@ -134,43 +125,90 @@ pm_message(const char format[], ...) { va_start(args, format); if (pm_showmessages) { - fprintf(stderr, "%s: ", pm_progname); - vfprintf(stderr, format, args); - fputc('\n', stderr); + const char * msg; + vasprintfN(&msg, format, args); + + if (userMessageFn) + userMessageFn(msg); + else + fprintf(stderr, "%s: %s\n", pm_progname, msg); + + strfree(msg); } va_end(args); } +static void +errormsg(const char * const msg) { + + if (userErrorMsgFn) + userErrorMsgFn(msg); + else + fprintf(stderr, "%s: %s\n", pm_progname, msg); +} + + + +void PM_GNU_PRINTF_ATTR(1,2) +pm_errormsg(const char format[], ...) { + + va_list args; + const char * msg; + + va_start(args, format); + + vasprintfN(&msg, format, args); + + errormsg(msg); + + strfree(msg); + + va_end(args); +} + + + void PM_GNU_PRINTF_ATTR(1,2) pm_error(const char format[], ...) { va_list args; + const char * msg; va_start(args, format); - fprintf(stderr, "%s: ", pm_progname); - vfprintf(stderr, format, args); - fputc('\n', stderr); + vasprintfN(&msg, format, args); + + errormsg(msg); + + strfree(msg); + va_end(args); pm_longjmp(); } -/* Variable-sized arrays. */ -char * +static void * +mallocz(size_t const size) { + + return malloc(MAX(1, size)); +} + + + +void * pm_allocrow(unsigned int const cols, unsigned int const size) { - char * itrow; + unsigned char * itrow; - if (UINT_MAX / cols < size) + if (cols != 0 && UINT_MAX / cols < size) pm_error("Arithmetic overflow multiplying %u by %u to get the " "size of a row to allocate.", cols, size); - itrow = malloc(cols * size); + itrow = mallocz(cols * size); if (itrow == NULL) pm_error("out of memory allocating a row"); @@ -180,14 +218,70 @@ pm_allocrow(unsigned int const cols, void -pm_freerow(char * const itrow) { +pm_freerow(void * const itrow) { free(itrow); } -char** -pm_allocarray(int const cols, int const rows, int const size ) { +static void +allocarrayNoHeap(unsigned char ** const rowIndex, + unsigned int const cols, + unsigned int const rows, + unsigned int const size, + const char ** const errorP) { + + if (cols != 0 && UINT_MAX / cols < size) + asprintfN(errorP, + "Arithmetic overflow multiplying %u by %u to get the " + "size of a row to allocate.", cols, size); + else { + unsigned int rowsDone; + + rowsDone = 0; + *errorP = NULL; + + while (rowsDone < rows && !*errorP) { + unsigned char * const rowSpace = mallocz(cols * size); + if (rowSpace == NULL) + asprintfN(errorP, + "Unable to allocate a %u-column by %u byte row", + cols, size); + else + rowIndex[rowsDone++] = rowSpace; + } + if (*errorP) { + unsigned int row; + for (row = 0; row < rowsDone; ++row) + free(rowIndex[row]); + } + } +} + + + +static unsigned char * +allocRowHeap(unsigned int const cols, + unsigned int const rows, + unsigned int const size) { + + unsigned char * retval; + + if (cols != 0 && rows != 0 && UINT_MAX / cols / rows < size) + /* Too big even to request the memory ! */ + retval = NULL; + else + retval = mallocz(rows * cols * size); + + return retval; +} + + + +char ** +pm_allocarray(int const cols, + int const rows, + int const size ) { /*---------------------------------------------------------------------------- Allocate an array of 'rows' rows of 'cols' columns each, with each element 'size' bytes. @@ -206,38 +300,46 @@ pm_allocarray(int const cols, int const rows, int const size ) { We use unfragmented format if possible, but if the allocation of the row heap fails, we fall back to fragmented. -----------------------------------------------------------------------------*/ - char** rowIndex; - char * rowheap; + unsigned char ** rowIndex; + const char * error; MALLOCARRAY(rowIndex, rows + 1); if (rowIndex == NULL) - pm_error("out of memory allocating row index (%u rows) for an array", - rows); - rowheap = malloc(rows * cols * size); - if (rowheap == NULL) { - /* We couldn't get the whole heap in one block, so try fragmented - format. - */ - unsigned int row; - - rowIndex[rows] = NULL; /* Declare it fragmented format */ - - for (row = 0; row < rows; ++row) { - rowIndex[row] = pm_allocrow(cols, size); - if (rowIndex[row] == NULL) - pm_error("out of memory allocating Row %u " - "(%u columns, %u bytes per tuple) " - "of an array", row, cols, size); - } - } else { - /* It's unfragmented format */ - unsigned int row; - rowIndex[rows] = rowheap; /* Declare it unfragmented format */ + asprintfN(&error, + "out of memory allocating row index (%u rows) for an array", + rows); + else { + unsigned char * rowheap; - for (row = 0; row < rows; ++row) - rowIndex[row] = &(rowheap[row * cols * size]); + rowheap = allocRowHeap(cols, rows, size); + + if (rowheap) { + /* It's unfragmented format */ + + rowIndex[rows] = rowheap; /* Declare it unfragmented format */ + + if (rowheap) { + unsigned int row; + + for (row = 0; row < rows; ++row) + rowIndex[row] = &(rowheap[row * cols * size]); + } + error = NULL; + } else { + /* We couldn't get the whole heap in one block, so try fragmented + format. + */ + rowIndex[rows] = NULL; /* Declare it fragmented format */ + + allocarrayNoHeap(rowIndex, cols, rows, size, &error); + } } - return rowIndex; + if (error) { + pm_errormsg("Couldn't allocate %u-row array. %s", rows, error); + strfree(error); + pm_longjmp(); + } + return (char **)rowIndex; } @@ -263,12 +365,12 @@ pm_freearray(char ** const rowIndex, /* Case-insensitive keyword matcher. */ int -pm_keymatch(char * const strarg, +pm_keymatch(const char * const strarg, const char * const keywordarg, int const minchars) { int len; - const char *keyword; - char *str; + const char * keyword; + const char * str; str = strarg; keyword = keywordarg; @@ -406,7 +508,8 @@ vmsProgname(int * const argcP, char * argv[]) { void -pm_init(const char * const progname, unsigned int const flags) { +pm_init(const char * const progname, + unsigned int const flags) { /*---------------------------------------------------------------------------- Initialize static variables that Netpbm library routines use. @@ -537,7 +640,7 @@ showNetpbmHelp(const char progname[]) { void -pm_proginit(int * const argcP, char * argv[]) { +pm_proginit(int * const argcP, const char * argv[]) { /*---------------------------------------------------------------------------- Do various initialization things that all programs in the Netpbm package, and programs that emulate such programs, should do. @@ -624,12 +727,13 @@ pm_setMessage(int const newState, int * const oldStateP) { } + char * pm_arg0toprogname(const char arg0[]) { /*---------------------------------------------------------------------------- Given a value for argv[0] (a command name or file name passed to a program in the standard C calling sequence), return the name of the - Netpbm program to which is refers. + Netpbm program to which it refers. In the most ordinary case, this is simply the argument itself. @@ -637,7 +741,7 @@ pm_arg0toprogname(const char arg0[]) { after the last slash, and if there is a .exe on it (as there is for DJGPP), that is removed. - The return value is in static storage within. It is null-terminated, + The return value is in static storage within. It is NUL-terminated, but truncated at 64 characters. -----------------------------------------------------------------------------*/ static char retval[64+1]; @@ -663,831 +767,63 @@ pm_arg0toprogname(const char arg0[]) { -/* File open/close that handles "-" as stdin/stdout and checks errors. */ - -FILE* -pm_openr(const char * const name) { - FILE* f; - - if (strcmp(name, "-") == 0) - f = stdin; - else { -#ifndef VMS - f = fopen(name, "rb"); -#else - f = fopen(name, "r", "ctx=stm"); -#endif - if (f == NULL) - pm_error("Unable to open file '%s' for reading. " - "fopen() returns errno %d (%s)", - name, errno, strerror(errno)); - } - return f; -} - - - -FILE* -pm_openw(const char * const name) { - FILE* f; - - if (strcmp(name, "-") == 0) - f = stdout; - else { -#ifndef VMS - f = fopen(name, "wb"); -#else - f = fopen(name, "w", "mbc=32", "mbf=2"); /* set buffer factors */ -#endif - if (f == NULL) - pm_error("Unable to open file '%s' for writing. " - "fopen() returns errno %d (%s)", - name, errno, strerror(errno)); - } - return f; -} - - - -static const char * -tmpDir(void) { -/*---------------------------------------------------------------------------- - Return the name of the directory in which we should create temporary - files. - - The name is a constant in static storage. ------------------------------------------------------------------------------*/ - const char * tmpdir; - /* running approximation of the result */ - - tmpdir = getenv("TMPDIR"); /* Unix convention */ - - if (!tmpdir || strlen(tmpdir) == 0) - tmpdir = getenv("TMP"); /* Windows convention */ - - if (!tmpdir || strlen(tmpdir) == 0) - tmpdir = getenv("TEMP"); /* Windows convention */ - - if (!tmpdir || strlen(tmpdir) == 0) - tmpdir = TMPDIR; - - return tmpdir; -} - - - -static int -mkstempx(char * const filenameBuffer) { -/*---------------------------------------------------------------------------- - This is meant to be equivalent to POSIX mkstemp(). - - On some old systems, mktemp() is a security hazard that allows a hacker - to read or write our temporary file or cause us to read or write some - unintended file. On other systems, mkstemp() does not exist. - - A Windows/mingw environment is one which doesn't have mkstemp() - (2006.06.15). - - We assume that if a system doesn't have mkstemp() that its mktemp() - is safe, or that the total situation is such that the problems of - mktemp() are not a problem for the user. ------------------------------------------------------------------------------*/ - int retval; - int fd; - unsigned int attempts; - bool gotFile; - bool error; - - for (attempts = 0, gotFile = FALSE, error = FALSE; - !gotFile && !error && attempts < 100; - ++attempts) { - - char * rc; - rc = mktemp(filenameBuffer); - - if (rc == NULL) - error = TRUE; - else { - int rc; - - rc = open(filenameBuffer, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); - - if (rc == 0) { - fd = rc; - gotFile = TRUE; - } else { - if (errno == EEXIST) { - /* We'll just have to keep trying */ - } else - error = TRUE; - } - } - } - if (gotFile) - retval = fd; - else - retval = -1; - - return retval; -} - - - -static int -mkstemp2(char * const filenameBuffer) { - -#if HAVE_MKSTEMP - if (0) - mkstempx(NULL); /* defeat compiler unused function warning */ - return mkstemp(filenameBuffer); -#else - return mkstempx(filenameBuffer); -#endif -} - - - -void -pm_make_tmpfile(FILE ** const filePP, - const char ** const filenameP) { - - int fd; - FILE * fileP; - const char * filenameTemplate; - char * filenameBuffer; /* malloc'ed */ - unsigned int fnamelen; - const char * tmpdir; - const char * dirseparator; - - fnamelen = strlen (pm_progname) + 10; /* "/" + "_XXXXXX\0" */ - - tmpdir = tmpDir(); - - if (tmpdir[strlen(tmpdir) - 1] == '/') - dirseparator = ""; - else - dirseparator = "/"; - - asprintfN(&filenameTemplate, "%s%s%s%s", - tmpdir, dirseparator, pm_progname, "_XXXXXX"); - - if (filenameTemplate == NULL) - pm_error("Unable to allocate storage for temporary file name"); - - filenameBuffer = strdup(filenameTemplate); - - fd = mkstemp2(filenameBuffer); - - if (fd < 0) - pm_error("Unable to create temporary file according to name " - "pattern '%s'. mkstemp() failed with " - "errno %d (%s)", filenameTemplate, errno, strerror(errno)); - else { - fileP = fdopen(fd, "w+b"); - - if (fileP == NULL) - pm_error("Unable to create temporary file. fdopen() failed " - "with errno %d (%s)", errno, strerror(errno)); - } - strfree(filenameTemplate); - - *filenameP = filenameBuffer; - *filePP = fileP; -} - - - -FILE * -pm_tmpfile(void) { - - FILE * fileP; - const char * tmpfile; - - pm_make_tmpfile(&fileP, &tmpfile); - - unlink(tmpfile); +unsigned int +pm_randseed(void) { - strfree(tmpfile); + return time(NULL) ^ getpid(); - return fileP; } -FILE * -pm_openr_seekable(const char name[]) { +unsigned int +pm_parse_width(const char * const arg) { /*---------------------------------------------------------------------------- - Open the file named by name[] such that it is seekable (i.e. it can be - rewound and read in multiple passes with fseek()). - - If the file is actually seekable, this reduces to the same as - pm_openr(). If not, we copy the named file to a temporary file - and return that file's stream descriptor. - - We use a file that the operating system recognizes as temporary, so - it picks the filename and deletes the file when Caller closes it. + Return the image width represented by the decimal ASCIIZ string + 'arg'. Fail if it doesn't validly represent a width or represents + a width that can't be conveniently used in computation. -----------------------------------------------------------------------------*/ - int stat_rc; - int seekable; /* logical: file is seekable */ - struct stat statbuf; - FILE * original_file; - FILE * seekable_file; - - original_file = pm_openr((char *) name); - - /* I would use fseek() to determine if the file is seekable and - be a little more general than checking the type of file, but I - don't have reliable information on how to do that. I have seen - streams be partially seekable -- you can, for example seek to - 0 if the file is positioned at 0 but you can't actually back up - to 0. I have seen documentation that says the errno for an - unseekable stream is EBADF and in practice seen ESPIPE. - - On the other hand, regular files are always seekable and even if - some other file is, it doesn't hurt much to assume it isn't. - */ + unsigned int width; + const char * error; - stat_rc = fstat(fileno(original_file), &statbuf); - if (stat_rc == 0 && S_ISREG(statbuf.st_mode)) - seekable = TRUE; - else - seekable = FALSE; + interpret_uint(arg, &width, &error); - if (seekable) { - seekable_file = original_file; + if (error) { + pm_error("'%s' is invalid as an image width. %s", arg, error); + strfree(error); } else { - seekable_file = pm_tmpfile(); - - /* Copy the input into the temporary seekable file */ - while (!feof(original_file) && !ferror(original_file) - && !ferror(seekable_file)) { - char buffer[4096]; - int bytes_read; - bytes_read = fread(buffer, 1, sizeof(buffer), original_file); - fwrite(buffer, 1, bytes_read, seekable_file); - } - if (ferror(original_file)) - pm_error("Error reading input file into temporary file. " - "Errno = %s (%d)", strerror(errno), errno); - if (ferror(seekable_file)) - pm_error("Error writing input into temporary file. " - "Errno = %s (%d)", strerror(errno), errno); - pm_close(original_file); - { - int seek_rc; - seek_rc = fseek(seekable_file, 0, SEEK_SET); - if (seek_rc != 0) - pm_error("fseek() failed to rewind temporary file. " - "Errno = %s (%d)", strerror(errno), errno); - } - } - return seekable_file; -} - - - -void -pm_close(FILE * const f) { - fflush(f); - if (ferror(f)) - pm_message("A file read or write error occurred at some point"); - if (f != stdin) - if (fclose(f) != 0) - pm_error("close of file failed with errno %d (%s)", - errno, strerror(errno)); -} - - - -/* The pnmtopng package uses pm_closer() and pm_closew() instead of - pm_close(), apparently because the 1999 Pbmplus package has them. - I don't know what the difference is supposed to be. -*/ - -void -pm_closer(FILE * const f) { - pm_close(f); -} - - - -void -pm_closew(FILE * const f) { - pm_close(f); -} - - - -/* Endian I/O. - - Before Netpbm 10.27 (March 2005), these would return failure on EOF - or I/O failure. For backward compatibility, they still have the return - code, but it is always zero and the routines abort the program in case - of EOF or I/O failure. A program that wants to handle failure differently - must use lower level (C library) interfaces. But that level of detail - is uncharacteristic of a Netpbm program; the ease of programming that - comes with not checking a return code is more Netpbm. - - It is also for historical reasons that these return signed values, - when clearly unsigned would make more sense. -*/ - - - -static void -abortWithReadError(FILE * const ifP) { - - if (feof(ifP)) - pm_error("Unexpected end of input file"); - else - pm_error("Error (not EOF) reading file."); -} - - - -void -pm_readchar(FILE * const ifP, - char * const cP) { - - int c; - - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - - *cP = c; -} - - - -void -pm_writechar(FILE * const ofP, - char const c) { - - putc(c, ofP); -} - - - -int -pm_readbigshort(FILE * const ifP, - short * const sP) { - int c; - - unsigned short s; - - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - s = (c & 0xff) << 8; - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - s |= c & 0xff; - - *sP = s; - - return 0; -} - - - -int -pm_writebigshort(FILE * const ofP, - short const s) { - - putc((s >> 8) & 0xff, ofP); - putc(s & 0xff, ofP); - - return 0; -} - - - -int -pm_readbiglong(FILE * const ifP, - long * const lP) { - - int c; - unsigned long l; - - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - l = c << 24; - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - l |= c << 16; - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - l |= c << 8; - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - l |= c; - - *lP = l; - - return 0; -} - - - -int -pm_writebiglong(FILE * const ofP, - long const l) { - - putc((l >> 24) & 0xff, ofP); - putc((l >> 16) & 0xff, ofP); - putc((l >> 8) & 0xff, ofP); - putc((l >> 0) & 0xff, ofP); - - return 0; -} - - - -int -pm_readlittleshort(FILE * const ifP, - short * const sP) { - int c; - unsigned short s; - - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - s = c & 0xff; - - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - s |= (c & 0xff) << 8; - - *sP = s; - - return 0; -} - - - -int -pm_writelittleshort(FILE * const ofP, - short const s) { - - putc((s >> 0) & 0xff, ofP); - putc((s >> 8) & 0xff, ofP); - - return 0; -} - - - -int -pm_readlittlelong(FILE * const ifP, - long * const lP) { - int c; - unsigned long l; - - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - l = c; - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - l |= c << 8; - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - l |= c << 16; - c = getc(ifP); - if (c == EOF) - abortWithReadError(ifP); - l |= c << 24; - - *lP = l; - - return 0; -} - - - -int -pm_writelittlelong(FILE * const ofP, - long const l) { - - putc((l >> 0) & 0xff, ofP); - putc((l >> 8) & 0xff, ofP); - putc((l >> 16) & 0xff, ofP); - putc((l >> 24) & 0xff, ofP); - - return 0; -} - - - -int -pm_readmagicnumber(FILE * const ifP) { - - int ich1, ich2; - - ich1 = getc(ifP); - ich2 = getc(ifP); - if (ich1 == EOF || ich2 == EOF) - pm_error( "Error reading magic number from Netpbm image stream. " - "Most often, this " - "means your input file is empty." ); - - return ich1 * 256 + ich2; -} - - - -/* Read a file of unknown size to a buffer. Return the number of bytes - read. Allocate more memory as we need it. The calling routine has - to free() the buffer. - - Oliver Trepte, oliver@fysik4.kth.se, 930613 */ - -#define PM_BUF_SIZE 16384 /* First try this size of the buffer, then - double this until we reach PM_MAX_BUF_INC */ -#define PM_MAX_BUF_INC 65536 /* Don't allocate more memory in larger blocks - than this. */ - -char * -pm_read_unknown_size(FILE * const file, - long * const nread) { - long nalloc; - char * buf; - bool eof; - - *nread = 0; - nalloc = PM_BUF_SIZE; - MALLOCARRAY(buf, nalloc); - - eof = FALSE; /* initial value */ - - while(!eof) { - int val; - - if (*nread >= nalloc) { /* We need a larger buffer */ - if (nalloc > PM_MAX_BUF_INC) - nalloc += PM_MAX_BUF_INC; - else - nalloc += nalloc; - REALLOCARRAY_NOFAIL(buf, nalloc); - } - - val = getc(file); - if (val == EOF) - eof = TRUE; - else - buf[(*nread)++] = val; + if (width > INT_MAX-10) + pm_error("Width %u is too large for computations.", width); + if (width == 0) + pm_error("Width argument must be a positive number. You " + "specified 0."); } - return buf; -} - - - -union cheat { - uint32n l; - short s; - unsigned char c[4]; -}; - - - -short -pm_bs_short(short const s) { - union cheat u; - unsigned char t; - - u.s = s; - t = u.c[0]; - u.c[0] = u.c[1]; - u.c[1] = t; - return u.s; -} - - - -long -pm_bs_long(long const l) { - union cheat u; - unsigned char t; - - u.l = l; - t = u.c[0]; - u.c[0] = u.c[3]; - u.c[3] = t; - t = u.c[1]; - u.c[1] = u.c[2]; - u.c[2] = t; - return u.l; -} - - - -void -pm_tell2(FILE * const fileP, - void * const fileposP, - unsigned int const fileposSize) { -/*---------------------------------------------------------------------------- - Return the current file position as *filePosP, which is a buffer - 'fileposSize' bytes long. Abort the program if error, including if - *fileP isn't a file that has a position. ------------------------------------------------------------------------------*/ - /* Note: FTELLO() is either ftello() or ftell(), depending on the - capabilities of the underlying C library. It is defined in - pm_config.h. ftello(), in turn, may be either ftell() or - ftello64(), as implemented by the C library. - */ - pm_filepos const filepos = FTELLO(fileP); - if (filepos < 0) - pm_error("ftello() to get current file position failed. " - "Errno = %s (%d)\n", strerror(errno), errno); - - if (fileposSize == sizeof(pm_filepos)) { - pm_filepos * const fileposP_filepos = fileposP; - *fileposP_filepos = filepos; - } else if (fileposSize == sizeof(long)) { - if (sizeof(pm_filepos) > sizeof(long) && - filepos >= (pm_filepos) 1 << (sizeof(long)*8)) - pm_error("File size is too large to represent in the %u bytes " - "that were provided to pm_tell2()", fileposSize); - else { - long * const fileposP_long = fileposP; - *fileposP_long = (long)filepos; - } - } else - pm_error("File position size passed to pm_tell() is invalid: %u. " - "Valid sizes are %u and %u", - fileposSize, sizeof(pm_filepos), sizeof(long)); + return width; } unsigned int -pm_tell(FILE * const fileP) { - - long filepos; - - pm_tell2(fileP, &filepos, sizeof(filepos)); - - return filepos; -} - - - -void -pm_seek2(FILE * const fileP, - const pm_filepos * const fileposP, - unsigned int const fileposSize) { -/*---------------------------------------------------------------------------- - Position file *fileP to position *fileposP. Abort if error, including - if *fileP isn't a seekable file. ------------------------------------------------------------------------------*/ - if (fileposSize == sizeof(pm_filepos)) - /* Note: FSEEKO() is either fseeko() or fseek(), depending on the - capabilities of the underlying C library. It is defined in - pm_config.h. fseeko(), in turn, may be either fseek() or - fseeko64(), as implemented by the C library. - */ - FSEEKO(fileP, *fileposP, SEEK_SET); - else if (fileposSize == sizeof(long)) { - long const fileposLong = *(long *)fileposP; - fseek(fileP, fileposLong, SEEK_SET); - } else - pm_error("File position size passed to pm_seek() is invalid: %u. " - "Valid sizes are %u and %u", - fileposSize, sizeof(pm_filepos), sizeof(long)); -} - - - -void -pm_seek(FILE * const fileP, unsigned long filepos) { +pm_parse_height(const char * const arg) { /*---------------------------------------------------------------------------- + Same as pm_parse_width(), but for height. -----------------------------------------------------------------------------*/ + unsigned int height; + const char * error; - pm_filepos fileposBuff; - - fileposBuff = filepos; - - pm_seek2(fileP, &fileposBuff, sizeof(fileposBuff)); -} - - + interpret_uint(arg, &height, &error); -void -pm_nextimage(FILE * const file, int * const eofP) { -/*---------------------------------------------------------------------------- - Position the file 'file' to the next image in the stream, assuming it is - now positioned just after the current image. I.e. read off any white - space at the end of the current image's raster. Note that the raw formats - don't permit such white space, but this routine tolerates it anyway, - because the plain formats do permit white space after the raster. - - Iff there is no next image, return *eofP == TRUE. - - Note that in practice, we will not normally see white space here in - a plain PPM or plain PGM stream because the routine to read a - sample from the image reads one character of white space after the - sample in order to know where the sample ends. There is not - normally more than one character of white space (a newline) after - the last sample in the raster. But plain PBM is another story. No white - space is required between samples of a plain PBM image. But the raster - normally ends with a newline nonetheless. Since the sample reading code - will not have read that newline, it is there for us to read now. ------------------------------------------------------------------------------*/ - bool eof; - bool nonWhitespaceFound; - - eof = FALSE; - nonWhitespaceFound = FALSE; - - while (!eof && !nonWhitespaceFound) { - int c; - c = getc(file); - if (c == EOF) { - if (feof(file)) - eof = TRUE; - else - pm_error("File error on getc() to position to image"); - } else { - if (!isspace(c)) { - int rc; - - nonWhitespaceFound = TRUE; - - /* Have to put the non-whitespace character back in - the stream -- it's part of the next image. - */ - rc = ungetc(c, file); - if (rc == EOF) - pm_error("File error doing ungetc() " - "to position to image."); - } - } + if (error) { + pm_error("'%s' is invalid as an image height. %s", arg, error); + strfree(error); + } else { + if (height > INT_MAX-10) + pm_error("Height %u is too large for computations.", height); + if (height == 0) + pm_error("Height argument must be a positive number. You " + "specified 0."); } - *eofP = eof; -} - - - -void -pm_check(FILE * const file, - enum pm_check_type const check_type, - pm_filepos const need_raster_size, - enum pm_check_code * const retval_p) { -/*---------------------------------------------------------------------------- - This is not defined for use outside of libnetpbm. ------------------------------------------------------------------------------*/ - struct stat statbuf; - pm_filepos curpos; /* Current position of file; -1 if none */ - int rc; - -#ifdef LARGEFILEDEBUG - pm_message("pm_filepos received by pm_check() is %u bytes.", - sizeof(pm_filepos)); -#endif - /* Note: FTELLO() is either ftello() or ftell(), depending on the - capabilities of the underlying C library. It is defined in - pm_config.h. ftello(), in turn, may be either ftell() or - ftello64(), as implemented by the C library. - */ - curpos = FTELLO(file); - if (curpos >= 0) { - /* This type of file has a current position */ - - rc = fstat(fileno(file), &statbuf); - if (rc != 0) - pm_error("fstat() failed to get size of file, though ftello() " - "successfully identified\n" - "the current position. Errno=%s (%d)", - strerror(errno), errno); - else if (!S_ISREG(statbuf.st_mode)) { - /* Not a regular file; we can't know its size */ - if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; - } else { - pm_filepos const have_raster_size = statbuf.st_size - curpos; - - if (have_raster_size < need_raster_size) - pm_error("File has invalid format. The raster should " - "contain %u bytes, but\n" - "the file ends after only %u bytes.", - (unsigned int) need_raster_size, - (unsigned int) have_raster_size); - else if (have_raster_size > need_raster_size) { - if (retval_p) *retval_p = PM_CHECK_TOO_LONG; - } else { - if (retval_p) *retval_p = PM_CHECK_OK; - } - } - } else - if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; + return height; } diff --git a/lib/libpnm1.c b/lib/libpnm1.c index 82f99b93..536e5dc4 100644 --- a/lib/libpnm1.c +++ b/lib/libpnm1.c @@ -133,6 +133,74 @@ pnm_readpnminit(FILE * const fileP, +static void +readpgmrow(FILE * const fileP, + xel * const xelrow, + int const cols, + xelval const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + gray * grayrow; + + grayrow = pgm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pgm_freerow(grayrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pgm_readpgmrow(fileP, grayrow, cols, (gray) maxval, format); + + for (col = 0; col < cols; ++col) + PNM_ASSIGN1(xelrow[col], grayrow[col]); + + pm_setjmpbuf(origJmpbufP); + } + pgm_freerow(grayrow); +} + + + +static void +readpbmrow(FILE * const fileP, + xel * const xelrow, + int const cols, + xelval const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pbm_readpbmrow(fileP, bitrow, cols, format); + + for (col = 0; col < cols; ++col) + PNM_ASSIGN1(xelrow[col], bitrow[col] == PBM_BLACK ? 0 : maxval); + + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} + + + void pnm_readpnmrow(FILE * const fileP, xel * const xelrow, @@ -145,28 +213,13 @@ pnm_readpnmrow(FILE * const fileP, ppm_readppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, format); break; - case PGM_TYPE: { - gray * grayrow; - unsigned int col; - - grayrow = pgm_allocrow(cols); - pgm_readpgmrow(fileP, grayrow, cols, (gray) maxval, format); - for (col = 0; col < cols; ++col) - PNM_ASSIGN1(xelrow[col], grayrow[col]); - pgm_freerow(grayrow); - } - break; + case PGM_TYPE: + readpgmrow(fileP, xelrow, cols, maxval, format); + break; - case PBM_TYPE: { - bit * bitrow; - unsigned int col; - bitrow = pbm_allocrow(cols); - pbm_readpbmrow(fileP, bitrow, cols, format); - for (col = 0; col < cols; ++col) - PNM_ASSIGN1(xelrow[col], bitrow[col] == PBM_BLACK ? 0: maxval); - pbm_freerow(bitrow); - } - break; + case PBM_TYPE: + readpbmrow(fileP, xelrow, cols, maxval, format); + break; default: pm_error("INTERNAL ERROR. Impossible format."); @@ -182,15 +235,35 @@ pnm_readpnm(FILE * const fileP, xelval * const maxvalP, int * const formatP) { + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + int cols, rows; + xelval maxval; + int format; xel ** xels; - int row; - pnm_readpnminit(fileP, colsP, rowsP, maxvalP, formatP); + pnm_readpnminit(fileP, &cols, &rows, &maxval, &format); + + xels = pnm_allocarray(cols, rows); - xels = pnm_allocarray(*colsP, *rowsP); + if (setjmp(jmpbuf) != 0) { + pnm_freearray(xels, rows); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; - for (row = 0; row < *rowsP; ++row) - pnm_readpnmrow(fileP, xels[row], *colsP, *maxvalP, *formatP); + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < rows; ++row) + pnm_readpnmrow(fileP, xels[row], cols, maxval, format); + + pm_setjmpbuf(origJmpbufP); + } + *colsP = cols; + *rowsP = rows; + *maxvalP = maxval; + *formatP = format; return xels; } diff --git a/lib/libpnm2.c b/lib/libpnm2.c index aae78d52..01ffa389 100644 --- a/lib/libpnm2.c +++ b/lib/libpnm2.c @@ -15,13 +15,10 @@ #include "pnm.h" #include "ppm.h" -#include "libppm.h" #include "pgm.h" -#include "libpgm.h" #include "pbm.h" -#include "libpbm.h" void pnm_writepnminit(FILE * const fileP, @@ -55,51 +52,97 @@ pnm_writepnminit(FILE * const fileP, -void -pnm_writepnmrow(FILE * const fileP, - xel * const xelrow, - int const cols, - xelval const maxval, - int const format, - int const forceplain) { - - bool const plainFormat = forceplain || pm_plain_output; +static void +writepgmrow(FILE * const fileP, + const xel * const xelrow, + unsigned int const cols, + xelval const maxval, + int const format, + bool const plainFormat) { - switch (PNM_FORMAT_TYPE(format)) { - case PPM_TYPE: - ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, - plainFormat); - break; - - case PGM_TYPE: { - gray* grayrow; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + gray * grayrow; + + grayrow = pgm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pgm_freerow(grayrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { unsigned int col; - grayrow = pgm_allocrow(cols); - + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + for (col = 0; col < cols; ++col) grayrow[col] = PNM_GET1(xelrow[col]); - + pgm_writepgmrow(fileP, grayrow, cols, (gray) maxval, plainFormat); - pgm_freerow( grayrow ); + pm_setjmpbuf(origJmpbufP); } - break; + pgm_freerow(grayrow); +} + + - case PBM_TYPE: { - bit* bitrow; +static void +writepbmrow(FILE * const fileP, + const xel * const xelrow, + unsigned int const cols, + bool const plainFormat) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { unsigned int col; - bitrow = pbm_allocrow(cols); + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (col = 0; col < cols; ++col) bitrow[col] = PNM_GET1(xelrow[col]) == 0 ? PBM_BLACK : PBM_WHITE; - + pbm_writepbmrow(fileP, bitrow, cols, plainFormat); - pbm_freerow(bitrow); - } - break; + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} + + + +void +pnm_writepnmrow(FILE * const fileP, + const xel * const xelrow, + int const cols, + xelval const maxval, + int const format, + int const forceplain) { + + bool const plainFormat = forceplain || pm_plain_output; + + switch (PNM_FORMAT_TYPE(format)) { + case PPM_TYPE: + ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, + plainFormat); + break; + + case PGM_TYPE: + writepgmrow(fileP, xelrow, cols, maxval, format, plainFormat); + break; + + case PBM_TYPE: + writepbmrow(fileP, xelrow, cols, plainFormat); + break; default: pm_error("invalid format argument received by pnm_writepnmrow(): %d" diff --git a/lib/libpnm3.c b/lib/libpnm3.c index 99d35dc0..c41a2108 100644 --- a/lib/libpnm3.c +++ b/lib/libpnm3.c @@ -11,27 +11,54 @@ */ #include "pnm.h" - #include "ppm.h" -#include "libppm.h" - #include "pgm.h" -#include "libpgm.h" - #include "pbm.h" -#include "libpbm.h" -#if __STDC__ -xel -pnm_backgroundxel( xel** xels, int cols, int rows, xelval maxval, int format ) -#else /*__STDC__*/ + + +static xel +mean4(int const format, + xel const a, + xel const b, + xel const c, + xel const d) { +/*---------------------------------------------------------------------------- + Return cartesian mean of the 4 colors. +-----------------------------------------------------------------------------*/ + xel retval; + + switch (PNM_FORMAT_TYPE(format)) { + case PPM_TYPE: + PPM_ASSIGN( + retval, + (PPM_GETR(a) + PPM_GETR(b) + PPM_GETR(c) + PPM_GETR(d)) / 4, + (PPM_GETG(a) + PPM_GETG(b) + PPM_GETG(c) + PPM_GETG(d)) / 4, + (PPM_GETB(a) + PPM_GETB(b) + PPM_GETB(c) + PPM_GETB(d)) / 4); + break; + + case PGM_TYPE: + case PBM_TYPE: + PNM_ASSIGN1( + retval, + (PNM_GET1(a) + PNM_GET1(b) + PNM_GET1(c) + PNM_GET1(d))/4); + break; + + default: + pm_error( "Invalid format passed to pnm_backgroundxel()"); + } + return retval; +} + + + xel -pnm_backgroundxel( xels, cols, rows, maxval, format ) - xel** xels; - int cols, rows, format; - xelval maxval; -#endif /*__STDC__*/ - { +pnm_backgroundxel(xel** const xels, + int const cols, + int const rows, + xelval const maxval, + int const format) { + xel bgxel, ul, ur, ll, lr; /* Guess a good background value. */ @@ -40,192 +67,143 @@ pnm_backgroundxel( xels, cols, rows, maxval, format ) ll = xels[rows-1][0]; lr = xels[rows-1][cols-1]; - /* First check for three corners equal. */ - if ( PNM_EQUAL( ul, ur ) && PNM_EQUAL( ur, ll ) ) - bgxel = ul; - else if ( PNM_EQUAL( ul, ur ) && PNM_EQUAL( ur, lr ) ) - bgxel = ul; - else if ( PNM_EQUAL( ul, ll ) && PNM_EQUAL( ll, lr ) ) - bgxel = ul; - else if ( PNM_EQUAL( ur, ll ) && PNM_EQUAL( ll, lr ) ) - bgxel = ur; - /* Nope, check for two corners equal. */ - else if ( PNM_EQUAL( ul, ur ) || PNM_EQUAL( ul, ll ) || - PNM_EQUAL( ul, lr ) ) - bgxel = ul; - else if ( PNM_EQUAL( ur, ll ) || PNM_EQUAL( ur, lr ) ) - bgxel = ur; - else if ( PNM_EQUAL( ll, lr ) ) - bgxel = ll; + /* We first recognize three corners equal. If not, we look for any + two. If not, we just average all four. + */ + if (PNM_EQUAL(ul, ur) && PNM_EQUAL(ur, ll)) + bgxel = ul; + else if (PNM_EQUAL(ul, ur) && PNM_EQUAL(ur, lr)) + bgxel = ul; + else if (PNM_EQUAL(ul, ll) && PNM_EQUAL(ll, lr)) + bgxel = ul; + else if (PNM_EQUAL(ur, ll) && PNM_EQUAL(ll, lr)) + bgxel = ur; + else if (PNM_EQUAL(ul, ur)) + bgxel = ul; + else if (PNM_EQUAL(ul, ll)) + bgxel = ul; + else if (PNM_EQUAL(ul, lr)) + bgxel = ul; + else if (PNM_EQUAL(ur, ll)) + bgxel = ur; + else if (PNM_EQUAL(ur, lr)) + bgxel = ur; + else if (PNM_EQUAL(ll, lr)) + bgxel = ll; else - { - /* Nope, we have to average the four corners. This breaks the - ** rules of pnm, but oh well. Let's try to do it portably. */ - switch ( PNM_FORMAT_TYPE(format) ) - { - case PPM_TYPE: - PPM_ASSIGN( bgxel, - (PPM_GETR(ul) + PPM_GETR(ur) + PPM_GETR(ll) + PPM_GETR(lr)) / 4, - (PPM_GETG(ul) + PPM_GETG(ur) + PPM_GETG(ll) + PPM_GETG(lr)) / 4, - (PPM_GETB(ul) + PPM_GETB(ur) + PPM_GETB(ll) + PPM_GETB(lr)) / 4 ); - break; + bgxel = mean4(format, ul, ur, ll, lr); - case PGM_TYPE: - { - gray gul, gur, gll, glr; - gul = (gray) PNM_GET1( ul ); - gur = (gray) PNM_GET1( ur ); - gll = (gray) PNM_GET1( ll ); - glr = (gray) PNM_GET1( lr ); - PNM_ASSIGN1( bgxel, ( ( gul + gur + gll + glr ) / 4 ) ); - break; - } + return bgxel; +} - case PBM_TYPE: - pm_error( - "pnm_backgroundxel: four bits no two of which equal each other??" ); - default: - pm_error( "Invalid format passed to pnm_backgroundxel()"); - } - } - return bgxel; - } - -#if __STDC__ -xel -pnm_backgroundxelrow( xel* xelrow, int cols, xelval maxval, int format ) -#else /*__STDC__*/ xel -pnm_backgroundxelrow( xelrow, cols, maxval, format ) - xel* xelrow; - int cols, format; - xelval maxval; -#endif /*__STDC__*/ - { +pnm_backgroundxelrow(xel * const xelrow, + int const cols, + xelval const maxval, + int const format) { + xel bgxel, l, r; /* Guess a good background value. */ l = xelrow[0]; r = xelrow[cols-1]; - /* First check for both corners equal. */ - if ( PNM_EQUAL( l, r ) ) - bgxel = l; - else - { - /* Nope, we have to average the two corners. This breaks the - ** rules of pnm, but oh well. Let's try to do it portably. */ - switch ( PNM_FORMAT_TYPE(format) ) - { + if (PNM_EQUAL(l, r)) + /* Both corners are same color, so that's the background color, + without any extra computation. + */ + bgxel = l; + else { + /* Corners are different, so use cartesian mean of them */ + switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: - PPM_ASSIGN(bgxel, - (PPM_GETR(l) + PPM_GETR(r)) / 2, - (PPM_GETG(l) + PPM_GETG(r)) / 2, - (PPM_GETB(l) + PPM_GETB(r)) / 2 - ); - break; + PPM_ASSIGN(bgxel, + (PPM_GETR(l) + PPM_GETR(r)) / 2, + (PPM_GETG(l) + PPM_GETG(r)) / 2, + (PPM_GETB(l) + PPM_GETB(r)) / 2 + ); + break; case PGM_TYPE: - { - gray gl, gr; - gl = (gray) PNM_GET1( l ); - gr = (gray) PNM_GET1( r ); - PNM_ASSIGN1( bgxel, ( ( gl + gr ) / 2 ) ); - break; - } - - case PBM_TYPE: - { - int col, blacks; - - /* One black, one white. Gotta count. */ - for ( col = 0, blacks = 0; col < cols; ++col ) - { - if ( PNM_GET1( xelrow[col] ) == 0 ) - ++blacks; - } - if ( blacks >= cols / 2 ) - PNM_ASSIGN1( bgxel, 0 ); - else - PNM_ASSIGN1( bgxel, maxval ); - break; + PNM_ASSIGN1(bgxel, (PNM_GET1(l) + PNM_GET1(r)) / 2); + break; + + case PBM_TYPE: { + unsigned int col, blackCnt; + + /* One black, one white. Gotta count. */ + for (col = 0, blackCnt = 0; col < cols; ++col) { + if (PNM_GET1(xelrow[col] ) == 0) + ++blackCnt; + } + if (blackCnt >= cols / 2) + PNM_ASSIGN1(bgxel, 0); + else + PNM_ASSIGN1(bgxel, maxval); + break; } default: - pm_error( "Invalid format passed to pnm_backgroundxelrow()"); + pm_error("Invalid format passed to pnm_backgroundxelrow()"); } } return bgxel; - } +} + + -#if __STDC__ -xel -pnm_whitexel( xelval maxval, int format ) -#else /*__STDC__*/ xel -pnm_whitexel( maxval, format ) - xelval maxval; - int format; -#endif /*__STDC__*/ - { - xel x; +pnm_whitexel(xelval const maxval, + int const format) { - switch ( PNM_FORMAT_TYPE(format) ) - { + xel retval; + + switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: - PPM_ASSIGN( x, maxval, maxval, maxval ); - break; + PPM_ASSIGN(retval, maxval, maxval, maxval); + break; case PGM_TYPE: - PNM_ASSIGN1( x, maxval ); - break; - case PBM_TYPE: - PNM_ASSIGN1( x, maxval ); - break; + PNM_ASSIGN1(retval, maxval); + break; default: - pm_error( "Invalid format passed to pnm_whitexel()"); + pm_error("Invalid format %d passed to pnm_whitexel()", format); } - return x; - } + return retval; +} + + -#if __STDC__ -xel -pnm_blackxel( xelval maxval, int format ) -#else /*__STDC__*/ xel -pnm_blackxel( maxval, format ) - xelval maxval; - int format; -#endif /*__STDC__*/ - { - xel x; +pnm_blackxel(xelval const maxval, + int const format) { - switch ( PNM_FORMAT_TYPE(format) ) - { + xel retval; + + switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: - PPM_ASSIGN( x, 0, 0, 0 ); - break; + PPM_ASSIGN(retval, 0, 0, 0); + break; case PGM_TYPE: - PNM_ASSIGN1( x, (xelval) 0 ); - break; - case PBM_TYPE: - PNM_ASSIGN1( x, (xelval) 0 ); - break; + PNM_ASSIGN1(retval, 0); + break; default: - pm_error( "Invalid format passed to pnm_blackxel(): %d", format); + pm_error("Invalid format %d passed to pnm_blackxel()", format); } + + return retval; +} + - return x; - } void pnm_invertxel(xel* const xP, @@ -255,16 +233,8 @@ pnm_invertxel(xel* const xP, -#if __STDC__ void pnm_promoteformat( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat ) -#else /*__STDC__*/ -void -pnm_promoteformat( xels, cols, rows, maxval, format, newmaxval, newformat ) - xel** xels; - xelval maxval, newmaxval; - int cols, rows, format, newformat; -#endif /*__STDC__*/ { int row; @@ -273,16 +243,8 @@ pnm_promoteformat( xels, cols, rows, maxval, format, newmaxval, newformat ) xels[row], cols, maxval, format, newmaxval, newformat ); } -#if __STDC__ void pnm_promoteformatrow( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat ) -#else /*__STDC__*/ -void -pnm_promoteformatrow( xelrow, cols, maxval, format, newmaxval, newformat ) - xel* xelrow; - xelval maxval, newmaxval; - int cols, format, newformat; -#endif /*__STDC__*/ { register int col; register xel* xP; @@ -412,3 +374,45 @@ pnm_xeltopixel(xel const inputxel, return outputpixel; } + + + +xel +pnm_parsecolorxel(const char * const colorName, + xelval const maxval, + int const format) { + + pixel const bgColor = ppm_parsecolor(colorName, maxval); + + xel retval; + + switch(PNM_FORMAT_TYPE(format)) { + case PPM_TYPE: + PNM_ASSIGN(retval, + PPM_GETR(bgColor), PPM_GETG(bgColor), PPM_GETB(bgColor)); + break; + case PGM_TYPE: + if (PPM_ISGRAY(bgColor)) + PNM_ASSIGN1(retval, PPM_GETB(bgColor)); + else + pm_error("Non-gray color '%s' specified for a " + "grayscale (PGM) image", + colorName); + break; + case PBM_TYPE: + if (PPM_EQUAL(bgColor, ppm_whitepixel(maxval))) + PNM_ASSIGN1(retval, maxval); + else if (PPM_EQUAL(bgColor, ppm_blackpixel())) + PNM_ASSIGN1(retval, 0); + else + pm_error ("Color '%s', which is neither black nor white, " + "specified for a black and white (PBM) image", + colorName); + break; + default: + pm_error("Invalid format code %d passed to pnm_parsecolorxel()", + format); + } + + return retval; +} diff --git a/lib/libppm1.c b/lib/libppm1.c index 57a1db7d..ea929908 100644 --- a/lib/libppm1.c +++ b/lib/libppm1.c @@ -10,7 +10,7 @@ ** implied warranty. */ -/* See libpbm.c for the complicated explanation of this 32/64 bit file +/* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 @@ -19,6 +19,7 @@ #include <string.h> #include <stdio.h> #include <errno.h> + #include "ppm.h" #include "libppm.h" #include "pgm.h" @@ -29,6 +30,7 @@ #include "libpam.h" #include "fileio.h" #include "mallocvar.h" +#include "nstring.h" pixel * @@ -133,7 +135,8 @@ ppm_readppminit(FILE * const fileP, case PBM_TYPE: *formatP = realFormat; - *maxvalP = 1; + /* See comment in pgm_readpgminit() about this maxval */ + *maxvalP = PPM_MAXMAXVAL; pbm_readpbminitrest(fileP, colsP, rowsP); break; @@ -150,124 +153,211 @@ ppm_readppminit(FILE * const fileP, -void -ppm_readppmrow(FILE* const fileP, - pixel* const pixelrow, - int const cols, - pixval const maxval, - int const format) { - - switch (format) { - case PPM_FORMAT: { - unsigned int col; - for (col = 0; col < cols; ++col) { - pixval const r = pm_getuint(fileP); - pixval const g = pm_getuint(fileP); - pixval const b = pm_getuint(fileP); - - if (r > maxval) - pm_error("Red sample value %u is greater than maxval (%u)", - r, maxval); - if (g > maxval) - pm_error("Green sample value %u is greater than maxval (%u)", - g, maxval); - if (b > maxval) - pm_error("Blue sample value %u is greater than maxval (%u)", - b, maxval); - - PPM_ASSIGN(pixelrow[col], r, g, b); - } +static void +readPpmRow(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval, + int const format) { + + unsigned int col; + + for (col = 0; col < cols; ++col) { + pixval const r = pm_getuint(fileP); + pixval const g = pm_getuint(fileP); + pixval const b = pm_getuint(fileP); + + if (r > maxval) + pm_error("Red sample value %u is greater than maxval (%u)", + r, maxval); + if (g > maxval) + pm_error("Green sample value %u is greater than maxval (%u)", + g, maxval); + if (b > maxval) + pm_error("Blue sample value %u is greater than maxval (%u)", + b, maxval); + + PPM_ASSIGN(pixelrow[col], r, g, b); } - break; +} - /* For PAM, we require a depth of 3, which means the raster format - is identical to Raw PPM! How convenient. - */ - case PAM_FORMAT: - case RPPM_FORMAT: { - unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; - unsigned int const bytesPerRow = cols * 3 * bytesPerSample; - - unsigned int bufferCursor; - unsigned char * rowBuffer; - ssize_t rc; - MALLOCARRAY(rowBuffer, bytesPerRow); + +static void +readRppmRow(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval, + int const format) { + + unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; + unsigned int const bytesPerRow = cols * 3 * bytesPerSample; - if (rowBuffer == NULL) - pm_error("Unable to allocate memory for row buffer " - "for %u columns", cols); + unsigned char * rowBuffer; + const char * error; + + MALLOCARRAY(rowBuffer, bytesPerRow); + + if (rowBuffer == NULL) + asprintfN(&error, "Unable to allocate memory for row buffer " + "for %u columns", cols); + else { + ssize_t rc; rc = fread(rowBuffer, 1, bytesPerRow, fileP); if (feof(fileP)) - pm_error("Unexpected EOF reading row of PPM image."); + asprintfN(&error, "Unexpected EOF reading row of PPM image."); else if (ferror(fileP)) - pm_error("Error reading row. fread() errno=%d (%s)", - errno, strerror(errno)); + asprintfN(&error, "Error reading row. fread() errno=%d (%s)", + errno, strerror(errno)); else if (rc != bytesPerRow) - pm_error("Error reading row. Short read of %u bytes " - "instead of %u", rc, bytesPerRow); - - bufferCursor = 0; /* start at beginning of rowBuffer[] */ + asprintfN(&error, "Error reading row. Short read of %u bytes " + "instead of %u", rc, bytesPerRow); + else { + unsigned int bufferCursor; + + error = NULL; + + bufferCursor = 0; /* start at beginning of rowBuffer[] */ - if (bytesPerSample == 1) { - unsigned int col; - for (col = 0; col < cols; ++col) { - pixval const r = rowBuffer[bufferCursor++]; - pixval const g = rowBuffer[bufferCursor++]; - pixval const b = rowBuffer[bufferCursor++]; - PPM_ASSIGN(pixelrow[col], r, g, b); - } - } else { - /* two byte samples */ - unsigned int col; - for (col = 0; col < cols; ++col) { - pixval r, g, b; - - r = rowBuffer[bufferCursor++] << 8; - r |= rowBuffer[bufferCursor++]; - - g = rowBuffer[bufferCursor++] << 8; - g |= rowBuffer[bufferCursor++]; - - b = rowBuffer[bufferCursor++] << 8; - b |= rowBuffer[bufferCursor++]; - - PPM_ASSIGN(pixelrow[col], r, g, b); + if (bytesPerSample == 1) { + unsigned int col; + for (col = 0; col < cols; ++col) { + pixval const r = rowBuffer[bufferCursor++]; + pixval const g = rowBuffer[bufferCursor++]; + pixval const b = rowBuffer[bufferCursor++]; + PPM_ASSIGN(pixelrow[col], r, g, b); + } + } else { + /* two byte samples */ + unsigned int col; + for (col = 0; col < cols; ++col) { + pixval r, g, b; + + r = rowBuffer[bufferCursor++] << 8; + r |= rowBuffer[bufferCursor++]; + + g = rowBuffer[bufferCursor++] << 8; + g |= rowBuffer[bufferCursor++]; + + b = rowBuffer[bufferCursor++] << 8; + b |= rowBuffer[bufferCursor++]; + + PPM_ASSIGN(pixelrow[col], r, g, b); + } } } free(rowBuffer); } - break; + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } +} - case PGM_FORMAT: - case RPGM_FORMAT: { - gray * const grayrow = pgm_allocrow(cols); + + +static void +readPgmRow(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + gray * grayrow; + + grayrow = pgm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pgm_freerow(grayrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pgm_readpgmrow(fileP, grayrow, cols, maxval, format); + for (col = 0; col < cols; ++col) { pixval const g = grayrow[col]; PPM_ASSIGN(pixelrow[col], g, g, g); } - pgm_freerow(grayrow); + pm_setjmpbuf(origJmpbufP); } - break; + pgm_freerow(grayrow); +} - case PBM_FORMAT: - case RPBM_FORMAT: { - bit * const bitrow = pbm_allocrow(cols); + + +static void +readPbmRow(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { unsigned int col; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + pbm_readpbmrow(fileP, bitrow, cols, format); + for (col = 0; col < cols; ++col) { pixval const g = (bitrow[col] == PBM_WHITE) ? maxval : 0; PPM_ASSIGN(pixelrow[col], g, g, g); } - pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); } - break; + pbm_freerow(bitrow); +} + + + +void +ppm_readppmrow(FILE * const fileP, + pixel * const pixelrow, + int const cols, + pixval const maxval, + int const format) { + + switch (format) { + case PPM_FORMAT: + readPpmRow(fileP, pixelrow, cols, maxval, format); + break; + + /* For PAM, we require a depth of 3, which means the raster format + is identical to Raw PPM! How convenient. + */ + case PAM_FORMAT: + case RPPM_FORMAT: + readRppmRow(fileP, pixelrow, cols, maxval, format); + break; + + case PGM_FORMAT: + case RPGM_FORMAT: + readPgmRow(fileP, pixelrow, cols, maxval, format); + break; + + case PBM_FORMAT: + case RPBM_FORMAT: + readPbmRow(fileP, pixelrow, cols, maxval, format); + break; default: pm_error("Invalid format code"); @@ -281,17 +371,36 @@ ppm_readppm(FILE * const fileP, int * const colsP, int * const rowsP, pixval * const maxvalP) { - pixel** pixels; - int row; + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + pixel ** pixels; + int cols, rows; + pixval maxval; int format; - ppm_readppminit(fileP, colsP, rowsP, maxvalP, &format); + ppm_readppminit(fileP, &cols, &rows, &maxval, &format); - pixels = ppm_allocarray(*colsP, *rowsP); + pixels = ppm_allocarray(cols, rows); - for (row = 0; row < *rowsP; ++row) - ppm_readppmrow(fileP, pixels[row], *colsP, *maxvalP, format); + if (setjmp(jmpbuf) != 0) { + ppm_freearray(pixels, rows); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < rows; ++row) + ppm_readppmrow(fileP, pixels[row], cols, maxval, format); + + *colsP = cols; + *rowsP = rows; + *maxvalP = maxval; + + pm_setjmpbuf(origJmpbufP); + } return pixels; } diff --git a/lib/libppm2.c b/lib/libppm2.c index 52c4ab16..3bf74bd4 100644 --- a/lib/libppm2.c +++ b/lib/libppm2.c @@ -17,7 +17,6 @@ #include "pm_c_util.h" #include "mallocvar.h" #include "ppm.h" -#include "libppm.h" void ppm_writeppminit(FILE* const fileP, @@ -145,10 +144,10 @@ ppm_writeppmrowraw(FILE * const fileP, static void -ppm_writeppmrowplain(FILE * const fileP, - pixel * const pixelrow, - unsigned int const cols, - pixval const maxval) { +ppm_writeppmrowplain(FILE * const fileP, + const pixel * const pixelrow, + unsigned int const cols, + pixval const maxval) { unsigned int col; unsigned int charcount; @@ -178,11 +177,11 @@ ppm_writeppmrowplain(FILE * const fileP, void -ppm_writeppmrow(FILE * const fileP, - pixel * const pixelrow, - int const cols, - pixval const maxval, - int const forceplain) { +ppm_writeppmrow(FILE * const fileP, + const pixel * const pixelrow, + int const cols, + pixval const maxval, + int const forceplain) { if (forceplain || pm_plain_output || maxval >= 1<<16) ppm_writeppmrowplain(fileP, pixelrow, cols, maxval); diff --git a/lib/libppmcmap.c b/lib/libppmcmap.c index a9efccbc..055bfc8b 100644 --- a/lib/libppmcmap.c +++ b/lib/libppmcmap.c @@ -12,9 +12,10 @@ ** implied warranty. */ -#include "ppm.h" -#include "libppm.h" +#include "pm_c_util.h" +#include "nstring.h" #include "mallocvar.h" +#include "ppm.h" #include "ppmcmap.h" #define HASH_SIZE 20023 @@ -110,94 +111,124 @@ ppm_addtocolorhist( colorhist_vector chv, -colorhash_table -ppm_alloccolorhash(void) { +static colorhash_table +alloccolorhash(void) { colorhash_table cht; int i; MALLOCARRAY(cht, HASH_SIZE); + if (cht) { + for (i = 0; i < HASH_SIZE; ++i) + cht[i] = NULL; + } + return cht; +} + + + +colorhash_table +ppm_alloccolorhash(void) { + colorhash_table cht; + + cht = alloccolorhash(); + if (cht == NULL) pm_error( "out of memory allocating hash table" ); - for (i = 0; i < HASH_SIZE; ++i) - cht[i] = NULL; - return cht; } -static colorhash_table -computecolorhash(pixel ** const pixels, - const int cols, const int rows, - const int maxcolors, int * const colorsP, - FILE * const ifp, pixval const maxval, int const format) { -/*---------------------------------------------------------------------------- - Compute a color histogram from an image. The input is one of two types: +static void +readppmrow(FILE * const fileP, + pixel * const pixelrow, + int const cols, + pixval const maxval, + int const format, + const char ** const errorP) { - 1) a two-dimensional array of pixels 'pixels'; In this case, 'pixels' - is non-NULL and 'ifp' is NULL. + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + if (setjmp(jmpbuf) != 0) { + pm_setjmpbuf(origJmpbufP); + asprintfN(errorP, "Failed to read row of image."); + } else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); - 2) an open file, positioned to the image data. In this case, - 'pixels' is NULL and 'ifp' is non-NULL. ifp is the stream - descriptor for the input file, and 'maxval' and 'format' are - parameters of the image data in it. - - We return with the file still open and its position undefined. + ppm_readppmrow(fileP, pixelrow, cols, maxval, format); - In either case, the image is 'cols' by 'rows'. + *errorP = NULL; /* Would have longjmped if anything went wrong */ + + pm_setjmpbuf(origJmpbufP); + } +} - Return the number of colors found as *colorsP. - However, if 'maxcolors' is nonzero and the number of colors is - greater than 'maxcolors', return a null return value and *colorsP - undefined. + +static void +buildHashTable(FILE * const ifP, + pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + int const format, + unsigned int const maxcolors, + colorhash_table const cht, + pixel * const rowbuffer, + int * const nColorsP, + bool * const tooManyColorsP, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Look at all the colors in the file *ifP or array pixels[][] and add + them to the hash table 'cht'. + + Even if we fail, we may add some colors to 'cht'. + + As soon as we've seen more that 'maxcolors' colors, we quit. In that + case, only, we return *tooManyColorsP == true. That is not a failure. + 'maxcolors' == 0 means infinity. -----------------------------------------------------------------------------*/ - colorhash_table cht; - int row; - pixel * rowbuffer; /* malloc'ed */ - /* Buffer for a row read from the input file; undefined (but still - allocated) if input is not from a file. - */ - - cht = ppm_alloccolorhash( ); - *colorsP = 0; /* initial value */ + unsigned int row; + unsigned int nColors; - rowbuffer = ppm_allocrow(cols); + nColors = 0; /* initial value */ + *tooManyColorsP = FALSE; /* initial value */ + *errorP = NULL; /* initial value */ /* Go through the entire image, building a hash table of colors. */ - for (row = 0; row < rows; ++row) { - int col; + for (row = 0; row < rows && !*tooManyColorsP && !*errorP; ++row) { + unsigned int col; pixel * pixelrow; /* The row of pixels we are processing */ - if (ifp) { - ppm_readppmrow(ifp, rowbuffer, cols, maxval, format); + if (ifP) { + readppmrow(ifP, rowbuffer, cols, maxval, format, errorP); pixelrow = rowbuffer; } else pixelrow = pixels[row]; - for (col = 0; col < cols; ++col) { + for (col = 0; col < cols && !*tooManyColorsP && !*errorP; ++col) { const pixel apixel = pixelrow[col]; const int hash = ppm_hashpixel(apixel); colorhist_list chl; for (chl = cht[hash]; - chl != (colorhist_list) 0 && - !PPM_EQUAL(chl->ch.color, apixel); + chl && !PPM_EQUAL(chl->ch.color, apixel); chl = chl->next); if (chl) - chl->ch.value++; + ++chl->ch.value; else { /* It's not in the hash yet, so add it (if allowed) */ - ++(*colorsP); - if (maxcolors > 0 && *colorsP > maxcolors) { - ppm_freecolorhash(cht); - return NULL; - } else { + ++nColors; + if (maxcolors > 0 && nColors > maxcolors) + *tooManyColorsP = TRUE; + else { MALLOCVAR(chl); if (chl == NULL) - pm_error("out of memory computing hash table"); + asprintfN(errorP, + "out of memory computing hash table"); chl->ch.color = apixel; chl->ch.value = 1; chl->next = cht[hash]; @@ -206,31 +237,124 @@ computecolorhash(pixel ** const pixels, } } } - ppm_freerow(rowbuffer); - return cht; + *nColorsP = nColors; +} + + + +static void +computecolorhash(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + unsigned int const maxcolors, + int * const nColorsP, + FILE * const ifP, + pixval const maxval, + int const format, + colorhash_table * const chtP, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Compute a color histogram from an image. The input is one of two types: + + 1) a two-dimensional array of pixels 'pixels'; In this case, 'pixels' + is non-NULL and 'ifP' is NULL. + + 2) an open file, positioned to the image data. In this case, + 'pixels' is NULL and 'ifP' is non-NULL. ifP is the stream + descriptor for the input file, and 'maxval' and 'format' are + parameters of the image data in it. + + We return with the file still open and its position undefined. + + In either case, the image is 'cols' by 'rows'. + + Return the number of colors found as *colorsP. + + However, if 'maxcolors' is nonzero and the number of colors is + greater than 'maxcolors', return a null return value and *colorsP + undefined. +-----------------------------------------------------------------------------*/ + pixel * rowbuffer; /* malloc'ed */ + /* Buffer for a row read from the input file; undefined (but still + allocated) if input is not from a file. + */ + + MALLOCARRAY(rowbuffer, cols); + + if (rowbuffer == NULL) + asprintfN(errorP, "Unable to allocate %u-column row buffer.", cols); + else { + colorhash_table cht; + bool tooManyColors; + + cht = alloccolorhash(); + + if (cht == NULL) + asprintfN(errorP, "Unable to allocate color hash."); + else { + buildHashTable(ifP, pixels, cols, rows, maxval, format, maxcolors, + cht, rowbuffer, + nColorsP, &tooManyColors, errorP); + + if (tooManyColors) { + ppm_freecolorhash(cht); + *chtP = NULL; + } else + *chtP = cht; + + if (*errorP) + ppm_freecolorhash(cht); + } + free(rowbuffer); + } } colorhash_table ppm_computecolorhash(pixel ** const pixels, - const int cols, const int rows, - const int maxcolors, int * const colorsP) { + int const cols, + int const rows, + int const maxcolors, + int * const colorsP) { - return computecolorhash(pixels, cols, rows, maxcolors, colorsP, - NULL, 0, 0); + colorhash_table cht; + const char * error; + + computecolorhash(pixels, cols, rows, maxcolors, colorsP, + NULL, 0, 0, &cht, &error); + + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } + return cht; } colorhash_table -ppm_computecolorhash2(FILE * const ifp, - const int cols, const int rows, - const pixval maxval, const int format, - const int maxcolors, int * const colorsP ) { +ppm_computecolorhash2(FILE * const ifP, + int const cols, + int const rows, + pixval const maxval, + int const format, + int const maxcolors, + int * const colorsP ) { - return computecolorhash(NULL, cols, rows, maxcolors, colorsP, - ifp, maxval, format); + colorhash_table cht; + const char * error; + + computecolorhash(NULL, cols, rows, maxcolors, colorsP, + ifP, maxval, format, &cht, &error); + + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } + return cht; } @@ -353,30 +477,50 @@ ppm_colorhashtocolorhist(colorhash_table const cht, int const maxcolors) { colorhash_table ppm_colorhisttocolorhash(colorhist_vector const chv, int const colors) { + + colorhash_table retval; colorhash_table cht; - int i, hash; - pixel color; - colorhist_list chl; + const char * error; - cht = ppm_alloccolorhash( ); /* Initializes to NULLs */ - - for (i = 0; i < colors; ++i) { - color = chv[i].color; - hash = ppm_hashpixel(color); - for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next) - if (PPM_EQUAL(chl->ch.color, color)) - pm_error( - "same color found twice - %d %d %d", PPM_GETR(color), - PPM_GETG(color), PPM_GETB(color) ); - MALLOCVAR(chl); - if (chl == NULL) - pm_error("out of memory"); - chl->ch.color = color; - chl->ch.value = i; - chl->next = cht[hash]; - cht[hash] = chl; + cht = alloccolorhash( ); /* Initializes to NULLs */ + if (cht == NULL) + asprintfN(&error, "Unable to allocate color hash"); + else { + unsigned int i; + + for (i = 0, error = NULL; i < colors && !error; ++i) { + pixel const color = chv[i].color; + int const hash = ppm_hashpixel(color); + + colorhist_list chl; + + for (chl = cht[hash]; chl && !error; chl = chl->next) + if (PPM_EQUAL(chl->ch.color, color)) + asprintfN(&error, "same color found twice: (%u %u %u)", + PPM_GETR(color), + PPM_GETG(color), + PPM_GETB(color)); + MALLOCVAR(chl); + if (chl == NULL) + asprintfN(&error, "out of memory"); + else { + chl->ch.color = color; + chl->ch.value = i; + chl->next = cht[hash]; + cht[hash] = chl; + } + } + if (error) + ppm_freecolorhash(cht); } - return cht; + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } else + retval = cht; + + return retval; } @@ -629,15 +773,7 @@ ppm_findclosestcolor(const pixel * const colormap, void -#if __STDC__ ppm_colorrowtomapfile(FILE *ofp, pixel *colormap, int ncolors, pixval maxval) -#else -ppm_colorrowtomapfile(ofp, colormap, ncolors, maxval) - FILE *ofp; - pixel *colormap; - int ncolors; - pixval maxval; -#endif { int i; diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c index 8ecfd80d..7e324185 100644 --- a/lib/libppmcolor.c +++ b/lib/libppmcolor.c @@ -19,6 +19,7 @@ #include "pm_c_util.h" #include "mallocvar.h" +#include "nstring.h" #include "ppm.h" #include "colorname.h" @@ -144,8 +145,8 @@ parseHexDigits(const char * const string, unsigned int digitCount; pixval n; - digitCount = 0; - n = 0; + digitCount = 0; /* initial value */ + n = 0; /* initial value */ while (string[digitCount] != delim) { char const digit = string[digitCount]; if (digit == '\0') @@ -248,22 +249,6 @@ parseNewDecX11(char const colorname[], -static bool -isHexString(char const string[], - int const hexit[]) { - - bool retval; - const char * p; - - for (p = &string[0], retval = true; *p && retval == true; ++p) { - if (hexit[(unsigned int)*p] == -1) - retval = false; - } - return retval; -} - - - static void parseOldX11(char const colorname[], pixval const maxval, @@ -279,7 +264,7 @@ parseOldX11(char const colorname[], computeHexTable(hexit); - if (!isHexString(&colorname[1], hexit)) + if (!strishex(&colorname[1])) pm_error("Non-hexadecimal characters in #-type color specification"); switch (strlen(colorname) - 1 /* (Number of hex digits) */) { @@ -414,13 +399,13 @@ ppm_parsecolor(const char * const colorname, -char* -ppm_colorname(const pixel* const colorP, - pixval const maxval, - int const hexok) { +char * +ppm_colorname(const pixel * const colorP, + pixval const maxval, + int const hexok) { int r, g, b; - FILE* f; + FILE * f; static char colorname[200]; if (maxval == 255) { @@ -460,8 +445,7 @@ ppm_colorname(const pixel* const colorP, hex specifier, so return that. */ sprintf(colorname, "#%02x%02x%02x", r, g, b); - return colorname; -} + return colorname;} @@ -472,11 +456,12 @@ processColorfileEntry(struct colorfile_entry const ce, colorhash_table const cht, const char ** const colornames, pixel * const colors, - unsigned int * const colornameIndexP) { + unsigned int * const colornameIndexP, + const char ** const errorP) { if (*colornameIndexP >= MAXCOLORNAMES) - pm_error("Too many colors in colorname dictionary. " - "Max allowed is %u", MAXCOLORNAMES); + asprintfN(errorP, "Too many colors in colorname dictionary. " + "Max allowed is %u", MAXCOLORNAMES); else { pixel color; @@ -488,13 +473,17 @@ processColorfileEntry(struct colorfile_entry const ce, file gives for each color, so we just ignore the current entry. */ + *errorP = NULL; } else { ppm_addtocolorhash(cht, &color, *colornameIndexP); colornames[*colornameIndexP] = strdup(ce.colorname); colors[*colornameIndexP] = color; if (colornames[*colornameIndexP] == NULL) - pm_error("Unable to allocate space for color name"); - ++(*colornameIndexP); + asprintfN(errorP, "Unable to allocate space for color name"); + else { + *errorP = NULL; + ++(*colornameIndexP); + } } } } @@ -502,39 +491,173 @@ processColorfileEntry(struct colorfile_entry const ce, static void -readcolordict(const char * const fileName, +openColornameFile(const char * const fileName, + bool const mustOpen, + FILE ** const filePP, + const char ** const errorP) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + if (setjmp(jmpbuf) != 0) { + asprintfN(errorP, "Failed to open color name file"); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + *filePP = pm_openColornameFile(fileName, mustOpen); + + *errorP = NULL; /* Would have longjmped if there were a problem */ + + pm_setjmpbuf(origJmpbufP); + } +} + + + +static void +readOpenColorFile(FILE * const colorFileP, + unsigned int * const nColorsP, + const char ** const colornames, + pixel * const colors, + colorhash_table const cht, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Read the color dictionary file *colorFileP and add the colors in it + to colornames[], colors[], and 'cht'. + + We may add colors to 'cht' even if we fail. +-----------------------------------------------------------------------------*/ + unsigned int nColorsDone; + bool done; + + nColorsDone = 0; + done = FALSE; + *errorP = NULL; + + while (!done && !*errorP) { + struct colorfile_entry const ce = pm_colorget(colorFileP); + + if (!ce.colorname) + done = TRUE; + else + processColorfileEntry(ce, cht, colornames, colors, + &nColorsDone, errorP); + } + if (!*errorP) { + *nColorsP = nColorsDone; + + while (nColorsDone < MAXCOLORNAMES) + colornames[nColorsDone++] = NULL; + } + + if (*errorP) { + unsigned int colorIndex; + + for (colorIndex = 0; colorIndex < nColorsDone; ++colorIndex) + strfree(colornames[colorIndex]); + } +} + + + +static colorhash_table +allocColorHash(void) { + + colorhash_table cht; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + if (setjmp(jmpbuf) != 0) + cht = NULL; + else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + cht = ppm_alloccolorhash(); + } + pm_setjmpbuf(origJmpbufP); + + return cht; +} + + + +static void +readColorFile(const char * const fileName, bool const mustOpen, unsigned int * const nColorsP, const char ** const colornames, - pixel * const colors, - colorhash_table const cht) { + pixel * const colors, + colorhash_table const cht, + const char ** const errorP) { - FILE * colorFile; + FILE * colorFileP; - colorFile = pm_openColornameFile(fileName, mustOpen); + openColornameFile(fileName, mustOpen, &colorFileP, errorP); + if (!*errorP) { + if (colorFileP == NULL) { + /* Couldn't open it, but Caller says treat same as + empty file + */ + *nColorsP = 0; + *errorP = NULL; + } else { + readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht, + errorP); + + fclose(colorFileP); + } + } +} - if (colorFile != NULL) { - unsigned int colornameIndex; - bool done; + - colornameIndex = 0; /* initial value */ - done = FALSE; - while (!done) { - struct colorfile_entry const ce = pm_colorget(colorFile); +static void +readcolordict(const char * const fileName, + bool const mustOpen, + unsigned int * const nColorsP, + const char *** const colornamesP, + pixel ** const colorsP, + colorhash_table * const chtP, + const char ** const errorP) { - if (!ce.colorname) - done = TRUE; - else - processColorfileEntry(ce, cht, colornames, colors, - &colornameIndex); - } + const char ** colornames; - *nColorsP = colornameIndex; + MALLOCARRAY(colornames, MAXCOLORNAMES); - while (colornameIndex < MAXCOLORNAMES) - colornames[colornameIndex++] = NULL; + if (colornames == NULL) + asprintfN(errorP, "Unable to allocate space for colorname table."); + else { + pixel * colors; - fclose(colorFile); + MALLOCARRAY(colors, MAXCOLORNAMES); + + if (colors == NULL) + asprintfN(errorP, "Unable to allocate space for color table."); + else { + colorhash_table cht; + + cht = allocColorHash(); + + if (cht == NULL) + asprintfN(errorP, "Unable to allocate space for color hash"); + else { + readColorFile(fileName, mustOpen, + nColorsP, colornames, colors, cht, + errorP); + + if (*errorP) + ppm_freecolorhash(cht); + else + *chtP = cht; + } + if (*errorP) + free(colors); + else + *colorsP = colors; + } + if (*errorP) + free(colornames); + else + *colornamesP = colornames; } } @@ -552,32 +675,31 @@ ppm_readcolordict(const char * const fileName, const char ** colornames; pixel * colors; unsigned int nColors; + const char * error; - cht = ppm_alloccolorhash(); - - MALLOCARRAY(colornames, MAXCOLORNAMES); - - colors = ppm_allocrow(MAXCOLORNAMES); + readcolordict(fileName, mustOpen, &nColors, &colornames, &colors, &cht, + &error); - if (colornames == NULL) - pm_error("Unable to allocate space for colorname table."); - - readcolordict(fileName, mustOpen, &nColors, colornames, colors, cht); - - if (chtP) - *chtP = cht; - else + if (error) { + pm_errormsg("%s", error); + strfree(error); ppm_freecolorhash(cht); - if (colornamesP) - *colornamesP = colornames; - else - ppm_freecolornames(colornames); - if (colorsP) - *colorsP = colors; - else - ppm_freerow(colors); - if (nColorsP) - *nColorsP = nColors; + } else { + if (chtP) + *chtP = cht; + else + ppm_freecolorhash(cht); + if (colornamesP) + *colornamesP = colornames; + else + ppm_freecolornames(colornames); + if (colorsP) + *colorsP = colors; + else + ppm_freerow(colors); + if (nColorsP) + *nColorsP = nColors; + } } diff --git a/lib/libppmd.c b/lib/libppmd.c index 892794a5..3b4c9e7c 100644 --- a/lib/libppmd.c +++ b/lib/libppmd.c @@ -32,6 +32,70 @@ struct penpos { int y; }; +struct rectangle { + /* ((0,0),(0,0)) means empty. */ + /* 'lr' is guaranteed not to be left of or above 'ul' */ + struct penpos ul; + struct penpos lr; +}; + +static struct rectangle const emptyRectangle = { + {0, 0}, + {0, 0}, +}; + + +static ppmd_point +makePoint(int const x, + int const y) { + + return ppmd_makePoint(x, y); +} + + + +static ppmd_point +middlePoint(ppmd_point const a, + ppmd_point const b) { + + ppmd_point retval; + + retval.x = (a.x + b.x) / 2; + retval.y = (a.y + b.y) / 2; + + return retval; +} + + + +static bool +pointsEqual(ppmd_point const a, + ppmd_point const b) { + + return a.x == b.x && a.y == b.y; +} + + + +static bool +pointIsWithinBounds(ppmd_point const p, + unsigned int const cols, + unsigned int const rows) { + + return (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows); +} + + + +static ppmd_point +vectorSum(ppmd_point const a, + ppmd_point const b) { + + return makePoint(a.x + b.x, a.y + b.y); +} + + + static long int const DDA_SCALE = 8192; #define PPMD_MAXCOORD 32767 @@ -53,28 +117,35 @@ static long int const DDA_SCALE = 8192; -static void -ppmd_validateCoords(int const x, - int const y) { +void +ppmd_validateCoord(int const c) { + + if (c < -PPMD_MAXCOORD || c > PPMD_MAXCOORD) + pm_error("Coordinate out of bounds: %d", c); +} + + + +void +ppmd_validatePoint(ppmd_point const p) { - if (x < -PPMD_MAXCOORD || x > PPMD_MAXCOORD) - pm_error("x coordinate out of bounds: %d", x); + if (p.x < -PPMD_MAXCOORD || p.x > PPMD_MAXCOORD) + pm_error("x coordinate of (%d, %d) out of bounds", p.x, p.y); - if (y < -PPMD_MAXCOORD || y > PPMD_MAXCOORD) - pm_error("y coordinate out of bounds: %d", y); + if (p.y < -PPMD_MAXCOORD || p.y > PPMD_MAXCOORD) + pm_error("y coordinate of (%d, %d) out of bounds", p.x, p.y); } static void -drawPoint(ppmd_drawproc drawproc, - const void * const clientdata, - pixel ** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const x, - int const y) { +drawPoint(ppmd_drawprocp drawproc, + const void * const clientdata, + pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const p) { /*---------------------------------------------------------------------------- Draw a single point, assuming that it is within the bounds of the image. @@ -82,12 +153,66 @@ drawPoint(ppmd_drawproc drawproc, if (drawproc == PPMD_NULLDRAWPROC) { const pixel * const pixelP = clientdata; - assert(x >= 0); assert(x < cols); - assert(y >= 0); assert(y < rows); + assert(p.x >= 0); assert(p.x < cols); + assert(p.y >= 0); assert(p.y < rows); - pixels[y][x] = *pixelP; + pixels[p.y][p.x] = *pixelP; } else - drawproc(pixels, cols, rows, maxval, x, y, clientdata); + drawproc(pixels, cols, rows, maxval, p, clientdata); +} + + + +struct drawProcXY { + ppmd_drawproc * drawProc; + const void * clientData; +}; + +static struct drawProcXY +makeDrawProcXY(ppmd_drawproc * const drawProc, + const void * const clientData) { + + struct drawProcXY retval; + + retval.drawProc = drawProc; + retval.clientData = clientData; + + return retval; +} + + + +static ppmd_drawprocp drawProcPointXY; + +static void +drawProcPointXY(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const p, + const void * const clientdata) { + + const struct drawProcXY * const xyP = clientdata; + + if (xyP->drawProc == PPMD_NULLDRAWPROC) + drawPoint(PPMD_NULLDRAWPROC, xyP->clientData, + pixels, cols, rows, maxval, p); + else + xyP->drawProc(pixels, cols, rows, maxval, p.x, p.y, xyP->clientData); +} + + + +void +ppmd_point_drawprocp(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const p, + const void * const clientdata) { + + if (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows) + pixels[p.y][p.x] = *((pixel*)clientdata); } @@ -101,12 +226,42 @@ ppmd_point_drawproc(pixel** const pixels, int const y, const void* const clientdata) { - if (x >= 0 && x < cols && y >= 0 && y < rows) - pixels[y][x] = *((pixel*)clientdata); + ppmd_point p; + + p.x = x; + p.y = y; + + ppmd_point_drawprocp(pixels, cols, rows, maxval, p, clientdata); +} + + + +static void +findRectangleIntersection(struct rectangle const rect1, + struct rectangle const rect2, + struct rectangle * const intersectionP) { +/*---------------------------------------------------------------------------- + Find the intersection between rectangles 'rect1' and 'rect2'. + Return it as *intersectionP. +-----------------------------------------------------------------------------*/ + struct penpos tentativeUl, tentativeLr; + + tentativeUl.x = MAX(rect1.ul.x, rect2.ul.x); + tentativeUl.y = MAX(rect1.ul.y, rect2.ul.y); + tentativeLr.x = MIN(rect1.lr.x, rect2.lr.x); + tentativeLr.y = MIN(rect1.lr.y, rect2.lr.y); + + if (tentativeLr.x <= tentativeUl.x || + tentativeLr.y <= tentativeUl.y) { + /* No intersection */ + *intersectionP = emptyRectangle; + } else { + intersectionP->ul = tentativeUl; + intersectionP->lr = tentativeLr; + } } -/* Simple fill routine. */ void ppmd_filledrectangle(pixel ** const pixels, @@ -117,37 +272,43 @@ ppmd_filledrectangle(pixel ** const pixels, int const y, int const width, int const height, - ppmd_drawproc drawProc, + ppmd_drawproc drawProc, const void * const clientdata) { - int cx, cy, cwidth, cheight, row; + struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata); - /* Clip. */ - cx = x; - cy = y; - cwidth = width; - cheight = height; + struct rectangle image, request, intersection; + unsigned int row; - if (cx < 0) { - cx = 0; - cwidth += x; - } - if (cy < 0) { - cy = 0; - cheight += y; - } - if (cx + cwidth > cols) - cwidth = cols - cx; + if (width < 0) + pm_error("negative width %d passed to ppmd_filledrectangle", width); + if (height < 0) + pm_error("negative height %d passed to ppmd_filledrectangle", height); + if (cols < 0) + pm_error("negative image width %d passed to ppmd_filledrectangle", + cols); + if (rows < 0) + pm_error("negative image height %d passed to ppmd_filledrectangle", + rows); - if (cy + cheight > rows) - cheight = rows - cy; + request.ul.x = x; + request.ul.y = y; + request.lr.x = x + width; + request.lr.y = y + height; + + image.ul.x = 0; + image.ul.y = 0; + image.lr.x = cols; + image.lr.y = rows; + + findRectangleIntersection(image, request, &intersection); /* Draw. */ - for (row = cy; row < cy + cheight; ++row) { - int col; - for (col = cx; col < cx + cwidth; ++col) - drawPoint(drawProc, clientdata, - pixels, cols, rows, maxval, col, row); + for (row = intersection.ul.y; row < intersection.lr.y; ++row) { + unsigned int col; + for (col = intersection.ul.x; col < intersection.lr.x; ++col) + drawPoint(drawProcPointXY, &xy, + pixels, cols, rows, maxval, makePoint(col, row)); } } @@ -187,20 +348,16 @@ ppmd_setlineclip(int const newSetting) { static void -clipEnd0(int const x0, - int const y0, - int const x1, - int const y1, - int const cols, - int const rows, - int * const cx0P, - int * const cy0P, - bool * const noLineP) { +clipEnd0(ppmd_point const p0, + ppmd_point const p1, + int const cols, + int const rows, + ppmd_point * const c0P, + bool * const noLineP) { /*---------------------------------------------------------------------------- - Given a line that goes from (x0, y0) to (x1, y1), where any of - these coordinates may be anywhere in space -- not just in the frame, - clip the (x0, y0) end to bring it into the frame. - Return the clipped-to location as (*cx0P, *cy0P). + Given a line that goes from p0 to p1, where any of these coordinates may be + anywhere in space -- not just in the frame, clip the p0 end to bring it + into the frame. Return the clipped-to location as *c0P. Iff this is not possible because the entire line described is outside the frame, return *nolineP == true. @@ -208,45 +365,43 @@ clipEnd0(int const x0, The frame is 'cols' columns starting at 0, by 'rows' rows starting at 0. -----------------------------------------------------------------------------*/ - - int cx0, cy0; + ppmd_point c0; bool noLine; - cx0 = x0; /* initial value */ - cy0 = y0; /* initial value */ + c0 = p0; /* initial value */ noLine = FALSE; /* initial value */ /* Clip End 0 of the line horizontally */ - if (cx0 < 0) { - if (x1 < 0) + if (c0.x < 0) { + if (p1.x < 0) noLine = TRUE; else { - cy0 = cy0 + (y1 - cy0) * (-cx0) / (x1 - cx0); - cx0 = 0; + c0.y = c0.y + (p1.y - c0.y) * (-c0.x) / (p1.x - c0.x); + c0.x = 0; } - } else if (cx0 >= cols) { - if (x1 >= cols) + } else if (c0.x >= cols) { + if (p1.x >= cols) noLine = TRUE; else { - cy0 = cy0 + (y1 - cy0) * (cols - 1 - cx0) / (x1 - cx0); - cx0 = cols - 1; + c0.y = c0.y + (p1.y - c0.y) * (cols - 1 - c0.x) / (p1.x - c0.x); + c0.x = cols - 1; } } /* Clip End 0 of the line vertically */ - if (cy0 < 0) { - if (y1 < 0) + if (c0.y < 0) { + if (p1.y < 0) noLine = TRUE; else { - cx0 = cx0 + (x1 - cx0) * (-cy0) / (y1 - cy0); - cy0 = 0; + c0.x = c0.x + (p1.x - c0.x) * (-c0.y) / (p1.y - c0.y); + c0.y = 0; } - } else if (cy0 >= rows) { - if (y1 >= rows) + } else if (c0.y >= rows) { + if (p1.y >= rows) noLine = TRUE; else { - cx0 = cx0 + (x1 - cx0) * (rows - 1 - cy0) / (y1 - cy0); - cy0 = rows - 1; + c0.x = c0.x + (p1.x - c0.x) * (rows - 1 - c0.y) / (p1.y - c0.y); + c0.y = rows - 1; } } @@ -254,140 +409,124 @@ clipEnd0(int const x0, horizontally. If so, we know the other endpoint is also out of frame horizontally and the line misses the frame entirely. */ - if (cx0 < 0 || cx0 >= cols) { - assert(x1 < 0 || x1 >= cols); + if (c0.x < 0 || c0.x >= cols) { + assert(p1.x < 0 || p1.x >= cols); noLine = TRUE; } - *cx0P = cx0; - *cy0P = cy0; + *c0P = c0; *noLineP = noLine; } static void -clipEnd1(int const x0, - int const y0, - int const x1, - int const y1, - int const cols, - int const rows, - int * const cx1P, - int * const cy1P) { +clipEnd1(ppmd_point const p0, + ppmd_point const p1, + int const cols, + int const rows, + ppmd_point * const c1P) { /*---------------------------------------------------------------------------- - Given a line that goes from (x0, y0) to (x1, y1), where (x0, y0) is - within the frame, but (x1, y1) can be anywhere in space, clip the - (x1, y1) end to bring it into the frame. Return the clipped-to - location as (*cx1P, *cy1P). + Given a line that goes from p0 to p1, where p0 is within the frame, but p1 + can be anywhere in space, clip the p1 end to bring it into the frame. + Return the clipped-to location as *c1P. - This is guaranteed to be possible, since we already know at least one - point (i.e. (x0, y0)) is in the frame. + This is guaranteed to be possible, since we already know at least one point + (i.e. p0) is in the frame. The frame is 'cols' columns starting at 0, by 'rows' rows starting at 0. -----------------------------------------------------------------------------*/ - int cx1, cy1; + ppmd_point c1; - assert(x0 >= 0 && y0 < cols); - assert(y0 >= 0 && y0 < rows); + assert(p1.x >= 0 && p0.y < cols); + assert(p1.y >= 0 && p0.y < rows); /* Clip End 1 of the line horizontally */ - cx1 = x1; /* initial value */ - cy1 = y1; /* initial value */ + c1 = p1; /* initial value */ - if (cx1 < 0) { + if (c1.x < 0) { /* We know the line isn't vertical, since End 0 is in the frame and End 1 is left of frame. */ - cy1 = cy1 + (y0 - cy1) * (-cx1) / (x0 - cx1); - cx1 = 0; - } else if (cx1 >= cols) { + c1.y = c1.y + (p0.y - c1.y) * (-c1.x) / (p0.x - c1.x); + c1.x = 0; + } else if (c1.x >= cols) { /* We know the line isn't vertical, since End 0 is in the frame and End 1 is right of frame. */ - cy1 = cy1 + (y0 - cy1) * (cols - 1 - cx1) / (x0 - cx1); - cx1 = cols - 1; + c1.y = c1.y + (p0.y - c1.y) * (cols - 1 - c1.x) / (p0.x - c1.x); + c1.x = cols - 1; } /* Clip End 1 of the line vertically */ - if (cy1 < 0) { + if (c1.y < 0) { /* We know the line isn't horizontal, since End 0 is in the frame and End 1 is above frame. */ - cx1 = cx1 + (x0 - cx1) * (-cy1) / (y0 - cy1); - cy1 = 0; - } else if (cy1 >= rows) { + c1.x = c1.x + (p0.x - c1.x) * (-c1.y) / (p0.y - c1.y); + c1.y = 0; + } else if (c1.y >= rows) { /* We know the line isn't horizontal, since End 0 is in the frame and End 1 is below frame. */ - cx1 = cx1 + (x0 - cx1) * (rows - 1 - cy1) / (y0 - cy1); - cy1 = rows - 1; + c1.x = c1.x + (p0.x - c1.x) * (rows - 1 - c1.y) / (p0.y - c1.y); + c1.y = rows - 1; } - *cx1P = cx1; - *cy1P = cy1; + *c1P = c1; } static void -clipLine(int const x0, - int const y0, - int const x1, - int const y1, - int const cols, - int const rows, - int * const cx0P, - int * const cy0P, - int * const cx1P, - int * const cy1P, - bool * const noLineP) { +clipLine(ppmd_point const p0, + ppmd_point const p1, + int const cols, + int const rows, + ppmd_point * const c0P, + ppmd_point * const c1P, + bool * const noLineP) { /*---------------------------------------------------------------------------- - Clip the line that goes from (x0, y0) to (x1, y1) so that none of it - is outside the boundaries of the raster with width 'cols' and height - 'rows' + Clip the line that goes from p0 to p1 so that none of it is outside the + boundaries of the raster with width 'cols' and height 'rows' - The clipped line goes from (*cx0P, *cy0P) to (*cx1P, *cy1P). + The clipped line goes from *c0P to *c1P. But if the entire line is outside the boundaries (i.e. we clip the entire line), return *noLineP true and the other values undefined. -----------------------------------------------------------------------------*/ - int cx0, cy0, cx1, cy1; + ppmd_point c0, c1; /* The line we successively modify. Starts out as the input line and ends up as the output line. */ bool noLine; - clipEnd0(x0, y0, x1, y1, cols, rows, &cx0, &cy0, &noLine); + clipEnd0(p0, p1, cols, rows, &c0, &noLine); if (!noLine) { - /* (cx0, cy0) is in the frame: */ - assert(cx0 >= 0 && cy0 < cols); - assert(cy0 >= 0 && cy0 < rows); + /* p0 is in the frame: */ + assert(c0.x >= 0 && c0.x < cols); + assert(c0.y >= 0 && c0.y < rows); - clipEnd1(cx0, cy0, x1, y1, cols, rows, &cx1, &cy1); + clipEnd1(c0, p1, cols, rows, &c1); } - *cx0P = cx0; - *cy0P = cy0; - *cx1P = cx1; - *cy1P = cy1; + *c0P = c0; + *c1P = c1; *noLineP = noLine; } static void -drawShallowLine(ppmd_drawproc drawProc, - const void * const clientdata, - pixel ** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const x0, - int const y0, - int const x1, - int const y1) { +drawShallowLine(ppmd_drawprocp drawProc, + const void * const clientdata, + pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const p0, + ppmd_point const p1) { /*---------------------------------------------------------------------------- Draw a line that is more horizontal than vertical. @@ -396,27 +535,27 @@ drawShallowLine(ppmd_drawproc drawProc, Assume the line has distinct start and end points (i.e. it's at least two points). -----------------------------------------------------------------------------*/ - /* Loop over X domain. */ long dy, srow; int dx, col, row, prevrow; - if (x1 > x0) + if (p1.x > p0.x) dx = 1; else dx = -1; - dy = (y1 - y0) * DDA_SCALE / abs(x1 - x0); - prevrow = row = y0; + dy = (p1.y - p0.y) * DDA_SCALE / abs(p1.x - p0.x); + prevrow = row = p0.y; srow = row * DDA_SCALE + DDA_SCALE / 2; - col = x0; + col = p0.x; for ( ; ; ) { if (linetype == PPMD_LINETYPE_NODIAGS && row != prevrow) { drawPoint(drawProc, clientdata, - pixels, cols, rows, maxval, col, prevrow); + pixels, cols, rows, maxval, makePoint(col, prevrow)); prevrow = row; } - drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, col, row); - if (col == x1) + drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, + makePoint(col, row)); + if (col == p1.x) break; srow += dy; row = srow / DDA_SCALE; @@ -427,16 +566,14 @@ drawShallowLine(ppmd_drawproc drawProc, static void -drawSteepLine(ppmd_drawproc drawProc, - const void * const clientdata, - pixel ** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const x0, - int const y0, - int const x1, - int const y1) { +drawSteepLine(ppmd_drawprocp drawProc, + const void * const clientdata, + pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const p0, + ppmd_point const p1) { /*---------------------------------------------------------------------------- Draw a line that is more vertical than horizontal. @@ -450,22 +587,23 @@ drawSteepLine(ppmd_drawproc drawProc, long dx, scol; int dy, col, row, prevcol; - if (y1 > y0) + if (p1.y > p0.y) dy = 1; else dy = -1; - dx = (x1 - x0) * DDA_SCALE / abs(y1 - y0); - row = y0; - prevcol = col = x0; + dx = (p1.x - p0.x) * DDA_SCALE / abs(p1.y - p0.y); + row = p0.y; + prevcol = col = p0.x; scol = col * DDA_SCALE + DDA_SCALE / 2; for ( ; ; ) { if (linetype == PPMD_LINETYPE_NODIAGS && col != prevcol) { drawPoint(drawProc, clientdata, - pixels, cols, rows, maxval, prevcol, row); + pixels, cols, rows, maxval, makePoint(prevcol, row)); prevcol = col; } - drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, col, row); - if (row == y1) + drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, + makePoint(col, row)); + if (row == p1.y) break; row += dy; scol += dx; @@ -476,55 +614,128 @@ drawSteepLine(ppmd_drawproc drawProc, void -ppmd_line(pixel ** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const x0, - int const y0, - int const x1, - int const y1, - ppmd_drawproc drawProc, - const void * const clientdata) { - - int cx0, cy0, cx1, cy1; +ppmd_linep(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const p0, + ppmd_point const p1, + ppmd_drawprocp drawProc, + const void * const clientdata) { + + ppmd_point c0, c1; bool noLine; /* There's no line left after clipping */ - ppmd_validateCoords(cols, rows); - ppmd_validateCoords(x0, y0); - ppmd_validateCoords(x1, y1); + ppmd_validateCoord(cols); + ppmd_validateCoord(rows); + ppmd_validatePoint(p0); + ppmd_validatePoint(p1); if (lineclip) { - clipLine(x0, y0, x1, y1, cols, rows, &cx0, &cy0, &cx1, &cy1, &noLine); + clipLine(p0, p1, cols, rows, &c0, &c1, &noLine); } else { - cx0 = x0; - cy0 = y0; - cx1 = x1; - cy1 = y1; + c0 = p0; + c1 = p1; noLine = FALSE; } if (noLine) { /* Nothing to draw */ - } else if (cx0 == cx1 && cy0 == cy1) { + } else if (pointsEqual(c0, c1)) { /* This line is just a point. Because there aren't two distinct endpoints, we have a special case. */ - drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, cx0, cy0); + drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, c0); } else { /* Draw, using a simple DDA. */ - if (abs(cx1 - cx0) > abs(cy1 - cy0)) + if (abs(c1.x - c0.x) > abs(c1.y - c0.y)) drawShallowLine(drawProc, clientdata, pixels, cols, rows, maxval, - cx0, cy0, cx1, cy1); + c0, c1); else drawSteepLine(drawProc, clientdata, pixels, cols, rows, maxval, - cx0, cy0, cx1, cy1); + c0, c1); + } +} + + +void +ppmd_line(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x0, + int const y0, + int const x1, + int const y1, + ppmd_drawproc drawProc, + const void * const clientData) { + + struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData); + + ppmd_linep(pixels, cols, rows, maxval, + makePoint(x0, y0), makePoint(x1, y1), drawProcPointXY, &xy); +} + + + +static unsigned int + distanceFromLine(ppmd_point const p, + ppmd_point const l0, + ppmd_point const l1) { +/*---------------------------------------------------------------------------- + Compute, sort of, the distance between point 'p' and the line through + 'l0' and 'l1'. + + I don't really know the signficance of this measurement. +-----------------------------------------------------------------------------*/ + + ppmd_point const middle = middlePoint(l0, l1); + + return (abs(p.x - middle.x) + abs(p.y - middle.y)); +} + + + + +void +ppmd_spline3p(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const p0, + ppmd_point const ctl, + ppmd_point const p1, + ppmd_drawprocp drawProc, + const void * const clientdata) { + + static unsigned int const splineThresh = 3; + /* The limit of recursion */ + + if (distanceFromLine(ctl, p0, p1) <= splineThresh) { + /* The control point is pretty close to the straight line that + joins the endpoints, so we'll just draw a straight line. + */ + ppmd_linep( + pixels, cols, rows, maxval, p0, p1, drawProc, clientdata); + } else { + /* We want some curvature, so pick a point (b) sort of between the + two endpoints and the control point and then draw a spline + between each of the endpoints and (b): + */ + ppmd_point const a = middlePoint(p0, ctl); + ppmd_point const c = middlePoint(ctl, p1); + ppmd_point const b = middlePoint(a, c); + + ppmd_spline3p( + pixels, cols, rows, maxval, p0, a, b, drawProc, clientdata); + + ppmd_spline3p( + pixels, cols, rows, maxval, b, c, p1, drawProc, clientdata); } } -#define SPLINE_THRESH 3 void ppmd_spline3(pixel ** const pixels, int const cols, @@ -539,35 +750,44 @@ ppmd_spline3(pixel ** const pixels, ppmd_drawproc drawProc, const void * const clientdata) { - register int xa, ya, xb, yb, xc, yc, xp, yp; - - xa = ( x0 + x1 ) / 2; - ya = ( y0 + y1 ) / 2; - xc = ( x1 + x2 ) / 2; - yc = ( y1 + y2 ) / 2; - xb = ( xa + xc ) / 2; - yb = ( ya + yc ) / 2; - - xp = ( x0 + xb ) / 2; - yp = ( y0 + yb ) / 2; - if ( abs( xa - xp ) + abs( ya - yp ) > SPLINE_THRESH ) - ppmd_spline3( - pixels, cols, rows, maxval, x0, y0, xa, ya, xb, yb, drawProc, - clientdata ); - else - ppmd_line( - pixels, cols, rows, maxval, x0, y0, xb, yb, drawProc, clientdata); - - xp = ( x2 + xb ) / 2; - yp = ( y2 + yb ) / 2; - if ( abs( xc - xp ) + abs( yc - yp ) > SPLINE_THRESH ) - ppmd_spline3( - pixels, cols, rows, maxval, xb, yb, xc, yc, x2, y2, drawProc, - clientdata ); - else - ppmd_line( - pixels, cols, rows, maxval, xb, yb, x2, y2, - drawProc, clientdata ); + struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata); + + ppmd_spline3p(pixels, cols, rows, maxval, + makePoint(x0, y0), + makePoint(x1, y1), + makePoint(x2, y2), + drawProcPointXY, &xy); +} + + + +void +ppmd_polysplinep(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const p0, + unsigned int const nc, + ppmd_point * const c, + ppmd_point const p1, + ppmd_drawprocp drawProc, + const void * const clientdata) { + + ppmd_point p; + + unsigned int i; + + assert(nc > 0); + + p = p0; + for (i = 0; i < nc - 1; ++i) { + ppmd_point const n = middlePoint(c[i], c[i+1]); + ppmd_spline3p( + pixels, cols, rows, maxval, p, c[i], n, drawProc, clientdata); + p = n; + } + ppmd_spline3p( + pixels, cols, rows, maxval, p, c[nc - 1], p1, drawProc, clientdata); } @@ -587,45 +807,130 @@ ppmd_polyspline(pixel ** const pixels, ppmd_drawproc drawProc, const void * const clientdata) { - register int i, x, y, xn, yn; - - x = x0; - y = y0; - for ( i = 0; i < nc - 1; ++i ) - { - xn = ( xc[i] + xc[i + 1] ) / 2; - yn = ( yc[i] + yc[i + 1] ) / 2; - ppmd_spline3( - pixels, cols, rows, maxval, x, y, xc[i], yc[i], xn, yn, drawProc, - clientdata ); - x = xn; - y = yn; + ppmd_point const p1 = makePoint(x1, y1); + ppmd_point const p0 = makePoint(x0, y0); + struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata); + + ppmd_point p; + unsigned int i; + + p = p0; /* initial value */ + + assert(nc > 0); + + for (i = 0; i < nc - 1; ++i) { + ppmd_point const n = middlePoint(makePoint(xc[i], yc[i]), + makePoint(xc[i+1], yc[i+1])); + ppmd_spline3p( + pixels, cols, rows, maxval, p, makePoint(xc[i], yc[i]), n, + drawProcPointXY, &xy); + p = n; } - ppmd_spline3( - pixels, cols, rows, maxval, x, y, xc[nc - 1], yc[nc - 1], x1, y1, - drawProc, clientdata ); + ppmd_spline3p( + pixels, cols, rows, maxval, p, makePoint(xc[nc - 1], yc[nc - 1]), p1, + drawProcPointXY, &xy); } void -ppmd_spline4(pixel ** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const x0, - int const y0, - int const x1, - int const y1, - int const x2, - int const y2, - int const x3, - int const y3, - ppmd_drawproc drawproc, - const void * const clientdata) { +ppmd_spline4p(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const endPt0, + ppmd_point const endPt1, + ppmd_point const ctlPt0, + ppmd_point const ctlPt1, + ppmd_drawprocp drawproc, + const void * const clientdata) { +/*---------------------------------------------------------------------------- + Draw a cubic spline from 'endPt0' to 'endPt1', using 'ctlPt0' and + 'ctlPt1' as control points in the classic way: a line through + 'endPt0' and 'ctlPt0' is tangent to the curve at 'entPt0' and the + length of that line controls "enthusiasm," whatever that is. + Same for 'endPt1' and 'ctlPt1'. +-----------------------------------------------------------------------------*/ + + pm_error("ppmd_spline4p() has not been written yet!"); + +} + + + +void +ppmd_circlep(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const center, + unsigned int const radius, + ppmd_drawprocp drawProc, + const void * const clientData) { +/*---------------------------------------------------------------------------- + If lineclip mode is on, draw only points within the image. + If lineclip is off, "draw" all points (by designated drawproc). Note + that the drawproc can't actually draw a point outside the image, but + it might maintain state that is affected by imaginary points outside + the image. + + Initial point is 3 o'clock. +-----------------------------------------------------------------------------*/ + if (radius >= DDA_SCALE) + pm_error("Error drawing circle. Radius %d is too large.", radius); + + ppmd_validateCoord(center.x + radius); + ppmd_validateCoord(center.y + radius); + ppmd_validateCoord(center.x - radius); + ppmd_validateCoord(center.y - radius); + + if (radius > 0) { + long const e = DDA_SCALE / radius; + + ppmd_point const p0 = makePoint(radius, 0); /* 3 o'clock */ + /* The starting point around the circle, assuming (0, 0) center */ + ppmd_point p; + /* Current drawing position in the circle, assuming (0,0) center */ + bool onFirstPoint; + bool prevPointExists; + ppmd_point prevPoint; + /* Previous drawing position, assuming (0, 0) center*/ + long sx, sy; /* 'p', scaled by DDA_SCALE */ + + p = p0; + + sx = p.x * DDA_SCALE + DDA_SCALE / 2; + sy = p.y * DDA_SCALE + DDA_SCALE / 2; + + onFirstPoint = TRUE; + prevPointExists = FALSE; + + while (onFirstPoint || !pointsEqual(p, p0)) { + pm_message("Doing point (%d, %d)", p.x, p.y); + + if (prevPointExists && pointsEqual(p, prevPoint)) { + /* We're on the same point we were on last time (we moved less + than a point's worth). Just keep moving. + */ + } else { + ppmd_point const imagePoint = vectorSum(center,p); + if (!lineclip || pointIsWithinBounds(imagePoint, cols, rows)) + drawPoint(drawProc, clientData, + pixels, cols, rows, maxval, imagePoint); + + prevPoint = p; + prevPointExists = TRUE; + } - pm_error("ppmd_spline4() has not been written yet!"); + if (!pointsEqual(p, p0)) + onFirstPoint = FALSE; + sx += e * sy / DDA_SCALE; + sy -= e * sx / DDA_SCALE; + p = makePoint(sx / DDA_SCALE, sy / DDA_SCALE); + pm_message("next point is (%d, %d)", p.x, p.y); + } + } } @@ -639,54 +944,16 @@ ppmd_circle(pixel ** const pixels, int const cy, int const radius, ppmd_drawproc drawProc, - const void * const clientdata) { - - int x0, y0, x, y, prevx, prevy, nopointsyet; - long sx, sy, e; + const void * const clientData) { if (radius < 0) - pm_error("Error drawing circle. Radius must be positive: %d", radius); - else if (radius == 0) - return; - else if (radius >= DDA_SCALE) - pm_error("Error drawing circle. Radius too large: %d", radius); - - ppmd_validateCoords(cx + radius, cy + radius); - ppmd_validateCoords(cx - radius, cy - radius); - - x0 = x = radius; - y0 = y = 0; - sx = x * DDA_SCALE + DDA_SCALE / 2; - sy = y * DDA_SCALE + DDA_SCALE / 2; - e = DDA_SCALE / radius; - - /* If lineclip is on, draw only points within pixmap. - Initial point is 3 o'clock. - If lineclip is off, "draw" all points (by designated drawproc). - */ + pm_error("Error drawing circle. Radius %d is negative.", radius); + else { + struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData); - if ((x + cx >= 0 && x + cx < cols && y + cy >= 0 && y + cy < rows) || - !lineclip) - drawPoint(drawProc, clientdata, - pixels, cols, rows, maxval, x + cx, y + cy); - nopointsyet = 1; - - do { - prevx = x; - prevy = y; - sx += e * sy / DDA_SCALE; - sy -= e * sx / DDA_SCALE; - x = sx / DDA_SCALE; - y = sy / DDA_SCALE; - if (x != prevx || y != prevy) { - nopointsyet = 0; - if ((x + cx >= 0 && x + cx < cols && y + cy >= 0 && y + cy < rows) - || !lineclip) - drawPoint(drawProc, clientdata, - pixels, cols, rows, maxval, x + cx, y + cy); - } + ppmd_circlep(pixels, cols, rows, maxval, makePoint(cx, cy), radius, + drawProcPointXY, &xy); } - while (nopointsyet || x != x0 || y != y0); } @@ -695,8 +962,7 @@ ppmd_circle(pixel ** const pixels, typedef struct { - int x; - int y; + ppmd_point point; int edge; } coord; @@ -762,14 +1028,14 @@ ppmd_fill_destroy(struct fillobj * fillObjP) { } + void -ppmd_fill_drawproc(pixel** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const x, - int const y, - const void * const clientdata) { +ppmd_fill_drawprocp(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const p, + const void * const clientdata) { fillobj * fh; coord * cp; @@ -777,19 +1043,19 @@ ppmd_fill_drawproc(pixel** const pixels, fh = (fillobj*) clientdata; + /* If these are the same coords we saved last time, don't bother. */ if (fh->n > 0) { - /* If these are the same coords we saved last time, don't bother. */ - ocp = &(fh->coords[fh->n - 1]); - if ( x == ocp->x && y == ocp->y ) + ppmd_point const lastPoint = fh->coords[fh->n - 1].point; + if (pointsEqual(p, lastPoint)) return; } - /* Ok, these are new; check if there's room for two more coords. */ + /* Ok, these are new; make room for two more coords. */ if (fh->n + 1 >= fh->size) { fh->size += SOME; REALLOCARRAY(fh->coords, fh->size); if (fh->coords == NULL) - pm_error( "out of memory enlarging a fillhandle" ); + pm_error("out of memory enlarging a fillhandle"); ocp = &(fh->coords[fh->n - 1]); } @@ -803,8 +1069,8 @@ ppmd_fill_drawproc(pixel** const pixels, } else { int dx, dy; - dx = x - ocp->x; - dy = y - ocp->y; + dx = p.x - ocp->point.x; + dy = p.y - ocp->point.y; if (dx < -1 || dx > 1 || dy < -1 || dy > 1) { /* Segment break. Close off old one. */ if (fh->startydir != 0 && fh->ydir != 0) @@ -836,8 +1102,7 @@ ppmd_fill_drawproc(pixel** const pixels, */ ++fh->curedge; cp = &fh->coords[fh->n]; - cp->x = ocp->x; - cp->y = ocp->y; + cp->point = ocp->point; cp->edge = fh->curedge; ++fh->n; } @@ -850,14 +1115,28 @@ ppmd_fill_drawproc(pixel** const pixels, /* Save this coord. */ cp = &fh->coords[fh->n]; - cp->x = x; - cp->y = y; + cp->point = p; cp->edge = fh->curedge; ++fh->n; } +void +ppmd_fill_drawproc(pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x, + int const y, + const void * const clientData) { + + ppmd_fill_drawprocp(pixels, cols, rows, maxval, makePoint(x, y), + clientData); +} + + + #ifndef LITERAL_FN_DEF_MATCH static qsort_comparison_fn yx_compare; @@ -870,15 +1149,18 @@ yx_compare(const void * const c1Arg, const coord * const c1P = c1Arg; const coord * const c2P = c2Arg; + ppmd_point const p1 = c1P->point; + ppmd_point const p2 = c2P->point; + int retval; - if (c1P->y > c2P->y) + if (p1.y > p2.y) retval = 1; - else if (c1P->y < c2P->y) + else if (p1.y < p2.y) retval = -1; - else if (c1P->x > c2P->x) + else if (p1.x > p2.x) retval = 1; - else if (c1P->x < c2P->x) + else if (p1.x < p2.x) retval = -1; else retval = 0; @@ -937,7 +1219,7 @@ ppmd_fill(pixel ** const pixels, fh->coords[i-2] = t; } if (i > 0) { - if (cp->x == lx && cp->y == py) { + if (cp->point.x == lx && cp->point.y == py) { eq = TRUE; if (cp->edge != edge && cp->edge == pedge) { /* Swap . and .-1. */ @@ -950,8 +1232,8 @@ ppmd_fill(pixel ** const pixels, } else eq = FALSE; } - lx = cp->x; - py = cp->y; + lx = cp->point.x; + py = cp->point.y; pedge = edge; edge = cp->edge; } @@ -960,35 +1242,35 @@ ppmd_fill(pixel ** const pixels, for (i = 0; i < fh->n; ++i) { cp = &fh->coords[i]; if (i == 0) { - lx = rx = cp->x; - py = cp->y; + lx = rx = cp->point.x; + py = cp->point.y; edge = cp->edge; leftside = TRUE; } else { - if (cp->y != py) { + if (cp->point.y != py) { /* Row changed. Emit old span and start a new one. */ ppmd_filledrectangle( pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1, drawProc, clientdata); - lx = rx = cp->x; - py = cp->y; + lx = rx = cp->point.x; + py = cp->point.y; edge = cp->edge; leftside = TRUE; } else { if (cp->edge == edge) { /* Continuation of side. */ - rx = cp->x; + rx = cp->point.x; } else { /* Edge changed. Is it a span? */ if (leftside) { - rx = cp->x; + rx = cp->point.x; leftside = FALSE; } else { /* Got a span to fill. */ ppmd_filledrectangle( pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1, drawProc, clientdata); - lx = rx = cp->x; + lx = rx = cp->point.x; leftside = TRUE; } edge = cp->edge; @@ -1024,12 +1306,7 @@ static int extleft, exttop, extright, extbottom; /* To accumulate extents */ /* ISIN -- Return sine of an angle in integral degrees. The value returned is 65536 times the sine. */ -#if __STDC__ static long isin(int deg) -#else - static long isin(deg) - int deg; -#endif { /* Domain reduce to 0 to 360 degrees. */ @@ -1054,17 +1331,31 @@ static long isin(int deg) /* ICOS -- Return cosine of an angle in integral degrees. The value returned is 65536 times the cosine. */ -#if __STDC__ static long icos(int deg) -#else - static long icos(deg) - int deg; -#endif { return isin(deg + 90); } -#define SCHAR(x) (u = (x), (((u) & 0x80) ? ((u) | (-1 ^ 0xFF)) : (u))) +static int +twosCompByteValue(unsigned char const c) { +/*---------------------------------------------------------------------------- + E.g. if 'c' is 0x5, return 5. If 'c' is 0xF0, return -16. +-----------------------------------------------------------------------------*/ + return (char)c; +} + +static int +glyphSkipBefore(const struct ppmd_glyph * const glyphP) { + + return twosCompByteValue(glyphP->header.skipBefore); +} + +static ppmd_point +commandPoint(const struct ppmd_glyphCommand * const commandP) { + + return makePoint(twosCompByteValue(commandP->x), + twosCompByteValue(commandP->y)); +} #define Scalef 21 /* Font design size */ #define Descend 9 /* Descender offset */ @@ -1073,39 +1364,29 @@ static long icos(int deg) static void drawGlyph(const struct ppmd_glyph * const glyphP, - int * const xP, - int const y, + ppmd_point * const glyphCornerP, pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, int const height, - int const xpos, - int const ypos, + ppmd_point const pos, long const rotcos, long const rotsin, - ppmd_drawproc drawProc, + ppmd_drawprocp drawProc, const void * const clientdata ) { /*---------------------------------------------------------------------------- - *xP is the column number of the left side of the glyph in the - output upon entry, and we update it to the left side of the next - glyph. - - 'y' is the row number of either the top or the bottom of the glyph - (I can't tell which right now) in the output. + *pP is either the top left or bottom left corner of the glyph cell + in the output upon entry, and we update it so as to move to the left + edge of the next glyph cell. -----------------------------------------------------------------------------*/ - struct penpos penPos; + ppmd_point const glyphCorner = *glyphCornerP; + ppmd_point p; unsigned int commandNum; - int x; - int u; /* Used by the SCHAR macro */ - - x = *xP; /* initial value */ - - x -= SCHAR(glyphP->header.skipBefore); - penPos.x = x; - penPos.y = y; + p = makePoint(glyphCorner.x - glyphSkipBefore(glyphP), glyphCorner.y); + /* initial value */ for (commandNum = 0; commandNum < glyphP->header.commandCount; @@ -1119,79 +1400,70 @@ drawGlyph(const struct ppmd_glyph * const glyphP, break; case CMD_DRAWLINE: { - int const nx = x + SCHAR(commandP->x); - int const ny = y + SCHAR(commandP->y); - - int mx1, my1, mx2, my2; - int tx1, ty1, tx2, ty2; - - /* Note that up until this moment we've been - working in an arbitrary model co-ordinate - system with fixed size and no rotation. - Before drawing the stroke, transform to - viewing co-ordinates to honour the height - and angle specifications. + ppmd_point const n = vectorSum(p, commandPoint(commandP)); + int const mx1 = (p.x * height) / Scalef; + int const my1 = ((p.y - Descend) * height) / Scalef; + int const mx2 = (n.x * height) / Scalef; + int const my2 = ((n.y - Descend) * height) / Scalef; + + /* Note that all points above are with reference to an arbitrary + model co-ordinate system with fixed size and no rotation. + Following are the points that honor the height and angle + specifications. */ - - mx1 = (penPos.x * height) / Scalef; - my1 = ((penPos.y - Descend) * height) / Scalef; - mx2 = (nx * height) / Scalef; - my2 = ((ny - Descend) * height) / Scalef; - tx1 = xpos + (mx1 * rotcos - my1 * rotsin) / 65536; - ty1 = ypos + (mx1 * rotsin + my1 * rotcos) / 65536; - tx2 = xpos + (mx2 * rotcos - my2 * rotsin) / 65536; - ty2 = ypos + (mx2 * rotsin + my2 * rotcos) / 65536; - - ppmd_validateCoords(tx1, ty1); - ppmd_validateCoords(tx2, ty2); + ppmd_point const t1 = + makePoint(pos.x + (mx1 * rotcos - my1 * rotsin) / 65536, + pos.y + (mx1 * rotsin + my1 * rotcos) / 65536); + ppmd_point const t2 = + makePoint(pos.x + (mx2 * rotcos - my2 * rotsin) / 65536, + pos.y + (mx2 * rotsin + my2 * rotcos) / 65536); - ppmd_line(pixels, cols, rows, maxval, tx1, ty1, tx2, ty2, - drawProc, clientdata); + ppmd_validatePoint(t1); + ppmd_validatePoint(t2); + + ppmd_linep(pixels, cols, rows, maxval, t1, t2, + drawProc, clientdata); - penPos.x = nx; - penPos.y = ny; + p = n; } - break; + break; case CMD_MOVEPEN: - penPos.x = x + SCHAR(commandP->x); - penPos.y = y + SCHAR(commandP->y); + p = vectorSum(p, commandPoint(commandP)); break; } } - x += glyphP->header.skipAfter; + p.x += glyphP->header.skipAfter; - *xP = x; + *glyphCornerP = makePoint(p.x + glyphP->header.skipAfter, glyphCorner.y); } -/* PPMD_TEXT -- Draw the zero-terminated string s, with its baseline - starting at point (x, y), inclined by angle degrees to - the X axis, with letters height pixels high (descenders - will extend below the baseline). The supplied drawproc - and cliendata are passed to ppmd_line which performs the - actual drawing. */ void -ppmd_text(pixel** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const xpos, - int const ypos, - int const height, - int const angle, - const char * const sArg, - ppmd_drawproc drawProc, - const void * const clientdata) { - +ppmd_textp(pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const pos, + int const height, + int const angle, + const char * const sArg, + ppmd_drawprocp drawProc, + const void * const clientdata) { +/*---------------------------------------------------------------------------- + Draw the zero-terminated string s, with its baseline starting at point + 'pos', inclined by angle degrees to the X axis, with letters height pixels + high (descenders will extend below the baseline). We pass the supplied + drawproc and clientdata to ppmd_linep, which performs the actual drawing. +-----------------------------------------------------------------------------*/ const struct ppmd_font * const fontP = ppmd_get_font(); long rotsin, rotcos; - int x, y; + ppmd_point p; const char * s; - ppmd_validateCoords(xpos, ypos); + ppmd_validatePoint(pos); - x = y = 0; + p = makePoint(0, 0); rotsin = isin(-angle); rotcos = icos(-angle); @@ -1205,19 +1477,42 @@ ppmd_text(pixel** const pixels, const struct ppmd_glyph * const glyphP = &fontP->glyphTable[ch - fontP->header.firstCodePoint]; - ppmd_validateCoords(x, y); + ppmd_validatePoint(p); - drawGlyph(glyphP, &x, y, pixels, cols, rows, maxval, - height, xpos, ypos, rotcos, rotsin, + drawGlyph(glyphP, &p, pixels, cols, rows, maxval, + height, pos, rotcos, rotsin, drawProc, clientdata); } else if (ch == '\n') { /* Move to the left edge of the next line down */ - y += Scalef + Descend; - x = 0; + p.y += Scalef + Descend; + p.x = 0; } } } + + +void +ppmd_text(pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const xpos, + int const ypos, + int const height, + int const angle, + const char * const sArg, + ppmd_drawproc drawProc, + const void * const clientData) { + + struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData); + + ppmd_textp(pixels, cols, rows, maxval, makePoint(xpos, ypos), + height, angle, sArg, drawProcPointXY, &xy); +} + + + /* EXTENTS_DRAWPROC -- Drawproc which just accumulates the extents rectangle bounding the text. */ diff --git a/lib/libppmfloyd.c b/lib/libppmfloyd.c index 071c3c36..ec6256ff 100644 --- a/lib/libppmfloyd.c +++ b/lib/libppmfloyd.c @@ -85,35 +85,36 @@ allocateFi(int const cols) { ppm_fs_info * -ppm_fs_init(int cols, pixval maxval, int flags) { +ppm_fs_init(unsigned int const cols, + pixval const maxval, + unsigned int const flags) { - ppm_fs_info *fi; + ppm_fs_info * fiP; - fi = allocateFi(cols); + fiP = allocateFi(cols); - fi->lefttoright = 1; - fi->cols = cols; - fi->maxval = maxval; - fi->flags = flags; - - if( flags & FS_RANDOMINIT ) { + fiP->lefttoright = 1; + fiP->cols = cols; + fiP->maxval = maxval; + fiP->flags = flags; + + if (flags & FS_RANDOMINIT) { unsigned int i; - srand((int)(time(0) ^ getpid())); - for( i = 0; i < cols +2; i++ ) { + srand(pm_randseed()); + for (i = 0; i < cols +2; ++i) { /* random errors in [-1..+1] */ - fi->thisrederr[i] = rand() % 32 - 16; - fi->thisgreenerr[i] = rand() % 32 - 16; - fi->thisblueerr[i] = rand() % 32 - 16; + fiP->thisrederr[i] = rand() % 32 - 16; + fiP->thisgreenerr[i] = rand() % 32 - 16; + fiP->thisblueerr[i] = rand() % 32 - 16; } - } - else { + } else { unsigned int i; - for( i = 0; i < cols + 2; i++ ) - fi->thisrederr[i] = fi->thisgreenerr[i] = - fi->thisblueerr[i] = 0; + for (i = 0; i < cols + 2; ++i) + fiP->thisrederr[i] = fiP->thisgreenerr[i] = + fiP->thisblueerr[i] = 0; } - return fi; + return fiP; } diff --git a/lib/libppmfuzzy.c b/lib/libppmfuzzy.c index 6127d5d5..2a54e896 100644 --- a/lib/libppmfuzzy.c +++ b/lib/libppmfuzzy.c @@ -84,7 +84,7 @@ memberTrapez(fzLog const x1, static fzLog hueIsAround000(double const hue) { - return memberZ(10, 30, hue); + return memberZ(10, 20, hue); } @@ -92,7 +92,7 @@ hueIsAround000(double const hue) { static fzLog hueIsAround015(double const hue) { - return memberZ(30, 40, hue); + return memberZ(20, 40, hue); } @@ -100,7 +100,7 @@ hueIsAround015(double const hue) { static fzLog hueIsAround030(double const hue) { - return memberTrapez(10, 30, 40, 60, hue); + return memberTrapez(10, 20, 40, 60, hue); } @@ -108,7 +108,7 @@ hueIsAround030(double const hue) { static fzLog hueIsAround060(double const hue) { - return memberTrapez(40, 60, 60, 80, hue); + return memberTrapez(40, 50, 60, 70, hue); } @@ -116,7 +116,7 @@ hueIsAround060(double const hue) { static fzLog hueIsAround120(double const hue) { - return memberTrapez(60, 80, 150, 180, hue); + return memberTrapez(60, 70, 150, 180, hue); } @@ -160,14 +160,14 @@ hueIsAround360(double const hue) { static fzLog satIsVeryLow(double const sat) { - return memberZ(0.02, 0.1, sat); + return memberZ(0.03, 0.08, sat); } static fzLog satIsLow(double const sat) { - return memberTrapez(0.02, 0.1, 0.2, 0.3, sat); + return memberTrapez(0.03, 0.08, 0.17, 0.2, sat); } @@ -175,7 +175,7 @@ satIsLow(double const sat) { static fzLog satIsMedium(double const sat) { - return memberTrapez(0.2, 0.3, 0.6, 0.7, sat); + return memberTrapez(0.17, 0.2, 0.6, 0.8, sat); } @@ -183,7 +183,7 @@ satIsMedium(double const sat) { static fzLog satIsHigh(double const sat) { - return memberS(0.6, 0.7, sat); + return memberS(0.6, 0.8, sat); } @@ -195,7 +195,7 @@ satIsHigh(double const sat) { static fzLog valIsVeryLow(double const val) { - return memberZ(0.1, 0.2, val); + return memberZ(0.05, 0.2, val); } @@ -203,7 +203,7 @@ valIsVeryLow(double const val) { static fzLog valIsLow(double const val) { - return memberTrapez(0.1, 0.2, 0.3, 0.6, val); + return memberTrapez(0.05, 0.2, 0.25, 0.3, val); } @@ -211,7 +211,7 @@ valIsLow(double const val) { static fzLog valIsMedium(double const val) { - return memberTrapez(0.3, 0.6, 0.7, 0.8, val); + return memberTrapez(0.25, 0.3, 0.6, 0.7, val); } @@ -219,7 +219,15 @@ valIsMedium(double const val) { static fzLog valIsHigh(double const val) { - return memberS(0.7, 0.8, val); + return memberTrapez(0.6, 0.7, 0.95, 0.97, val); +} + + + +static fzLog +valIsVeryHigh(double const val) { + + return memberS(0.95, 0.97, val); } @@ -269,10 +277,11 @@ matchBk(pixel const color, fzLog const satMedium = satIsMedium(hsv.s); fzLog const satHigh = satIsHigh(hsv.s); - fzLog const valVeryLow = valIsVeryLow(hsv.v); - fzLog const valLow = valIsLow(hsv.v); - fzLog const valMedium = valIsMedium(hsv.v); - fzLog const valHigh = valIsHigh(hsv.v); + fzLog const valVeryLow = valIsVeryLow(hsv.v); + fzLog const valLow = valIsLow(hsv.v); + fzLog const valMedium = valIsMedium(hsv.v); + fzLog const valHigh = valIsHigh(hsv.v); + fzLog const valVeryHigh = valIsVeryHigh(hsv.v); fzLog const hueAround000 = hueIsAround000(hsv.h); fzLog const hueAround015 = hueIsAround015(hsv.h); @@ -285,13 +294,13 @@ matchBk(pixel const color, fzLog const hueAround360 = hueIsAround360(hsv.h); (*bkMatchP)[BKCOLOR_BLACK] = - fzAnd(fzOr(satVeryLow, satLow), valVeryLow); + fzAnd(fzOr(satVeryLow, satLow), fzOr(valVeryLow, valLow)); (*bkMatchP)[BKCOLOR_GRAY] = - fzAnd(fzOr(satVeryLow, satLow), fzOr(valLow, valMedium)); + fzAnd(satVeryLow, fzAnd(fzNot(valVeryLow), fzNot(valVeryHigh))); (*bkMatchP)[BKCOLOR_WHITE] = - fzAnd(fzOr(satVeryLow, satLow), valHigh); + fzAnd(satVeryLow, valVeryHigh); (*bkMatchP)[BKCOLOR_RED] = fzAnd(fzAnd(fzOr(hueAround000, hueAround360), fzNot(satVeryLow)), @@ -300,38 +309,40 @@ matchBk(pixel const color, (*bkMatchP)[BKCOLOR_ORANGE] = fzAnd(fzAnd(hueAround030, fzOr(satMedium, satHigh)), - fzOr(valMedium, valHigh) + fzOr(fzOr(valMedium, valHigh), valVeryHigh) ); (*bkMatchP)[BKCOLOR_YELLOW] = fzAnd(fzAnd(hueAround060, fzOr(satMedium, satHigh)), - fzOr(valMedium, valHigh) + fzOr(valHigh, valVeryHigh) ); (*bkMatchP)[BKCOLOR_GREEN] = - fzAnd(fzAnd(hueAround120, fzNot(satVeryLow)), - fzOr(valMedium, valHigh) + fzAnd(fzAnd(hueAround120, fzOr(satMedium, satHigh)), + fzAnd(fzNot(valVeryLow), fzNot(valLow)) ); (*bkMatchP)[BKCOLOR_BLUE] = - fzAnd(fzAnd(hueAround180, fzAnd(fzNot(satVeryLow), fzNot(satLow))), - fzOr(valMedium, valHigh) + fzAnd(fzAnd(hueAround180, fzNot(satVeryLow)), + fzNot(valVeryLow) ); (*bkMatchP)[BKCOLOR_VIOLET] = - fzAnd(fzAnd(hueAround270, fzNot(satVeryLow)), + fzAnd(fzAnd(hueAround270, fzOr(satMedium, satHigh)), fzOr(valMedium, valHigh) ); (*bkMatchP)[BKCOLOR_PURPLE] = - fzAnd(fzAnd(hueAround320, fzNot(satVeryLow)), + fzAnd(fzAnd(hueAround320, fzOr(satMedium, satHigh)), fzOr(valMedium, valHigh) ); (*bkMatchP)[BKCOLOR_BROWN] = - fzAnd(fzOr(hueAround015, hueAround360), - fzAnd(fzNot(satVeryLow), fzNot(valHigh)) - ); + fzOr( + fzAnd(fzOr(hueAround015, hueAround360), + fzAnd(fzNot(satVeryLow), fzOr(valLow, valMedium))), + fzAnd(hueAround015, satLow) + ); } @@ -359,17 +370,17 @@ ppm_bk_color_from_color(pixel const color, static pixel const bkColorMap[BKCOLOR_COUNT] = { - { 0, 0, 0}, /* BKCOLOR_BLACK */ {174, 174, 174}, /* BKCOLOR_GRAY */ - {255, 255, 255}, /* BKCOLOR_WHITE */ - {255, 0, 0}, /* BKCOLOR_RED */ + {128, 42, 42}, /* BKCOLOR_BROWN */ {255, 128, 0}, /* BKCOLOR_ORANGE */ + {255, 0, 0}, /* BKCOLOR_RED */ {255, 255, 0}, /* BKCOLOR_YELLOW */ { 0, 255, 0}, /* BKCOLOR_GREEN */ { 0, 0, 255}, /* BKCOLOR_BLUE */ {143, 94, 153}, /* BKCOLOR_VIOLET */ {160, 32, 240}, /* BKCOLOR_PURPLE */ - {128, 42, 42} /* BKCOLOR_BROWN */ + {255, 255, 255}, /* BKCOLOR_WHITE */ + { 0, 0, 0} /* BKCOLOR_BLACK */ }; @@ -393,17 +404,17 @@ ppm_color_from_bk_color(bk_color const bkColor, static const char * const bkColorNameMap[BKCOLOR_COUNT] = { - "black", "gray", - "white", - "red", + "brown", "orange", + "red", "yellow", "green", "blue", "violet", "purple", - "brown" + "white", + "black" }; @@ -414,7 +425,7 @@ ppm_bk_color_from_name(const char * const name) { bk_color i; for (i = 0; i < BKCOLOR_COUNT; ++i) { - if (STREQ(name, bkColorNameMap[i])) + if (streq(name, bkColorNameMap[i])) return i; } pm_error("Invalid Berlin-Kay color name: '%s'", name); diff --git a/lib/libsystem.c b/lib/libsystem.c index 560f0a9c..f0a1996a 100644 --- a/lib/libsystem.c +++ b/lib/libsystem.c @@ -14,6 +14,7 @@ =============================================================================*/ #define _XOPEN_SOURCE +#include <stdarg.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> @@ -22,6 +23,8 @@ #include <signal.h> #include <sys/wait.h> +#include "pm_c_util.h" +#include "mallocvar.h" #include "pm.h" #include "pm_system.h" @@ -30,21 +33,25 @@ static void -execProgram(const char * const shellCommand, - int const inputPipeFd, - int const outputPipeFd) { +execProgram(const char * const progName, + const char ** const argArray, + int const stdinFd, + int const stdoutFd) { /*---------------------------------------------------------------------------- - Run the shell command 'shellCommand', supplying to the shell - 'inputPipeFd' as its Standard Input and 'outputPipeFd' as its + Run the program 'progName' with arguments argArray[], in a child process + with 'stdinFd' as its Standard Input and 'stdoutFd' as its Standard Output. But leave Standard Input and Standard Output as we found them. + + Note that stdinFd or stdoutFd may actually be Standard Input and + Standard Output already. -----------------------------------------------------------------------------*/ int stdinSaveFd, stdoutSaveFd; int rc; - /* Make inputPipeFd Standard Input. - Make outputPipeFd Standard Output. + /* Make stdinFd Standard Input. + Make stdoutFd Standard Output. */ stdinSaveFd = dup(STDIN); stdoutSaveFd = dup(STDOUT); @@ -52,10 +59,10 @@ execProgram(const char * const shellCommand, close(STDIN); close(STDOUT); - dup2(inputPipeFd, STDIN); - dup2(outputPipeFd, STDOUT); + dup2(stdinFd, STDIN); + dup2(stdoutFd, STDOUT); - rc = execl("/bin/sh", "sh", "-c", shellCommand, NULL); + rc = execvp(progName, (char **)argArray); close(STDIN); close(STDOUT); @@ -65,10 +72,12 @@ execProgram(const char * const shellCommand, close(stdoutSaveFd); if (rc < 0) - pm_error("Unable to exec the shell. Errno=%d (%s)", - errno, strerror(errno)); + pm_error("Unable to exec '%s' " + "(i.e. the program did not run at all). " + "execvp() errno=%d (%s)", + progName, errno, strerror(errno)); else - pm_error("INTERNAL ERROR. execl() returns, but does not fail."); + pm_error("INTERNAL ERROR. execvp() returns, but does not fail."); } @@ -84,21 +93,21 @@ createPipeFeeder(void pipeFeederRtn(int, void *), other end of the pipe as *fdP. -----------------------------------------------------------------------------*/ int pipeToFeed[2]; - pid_t feederPid; + pid_t rc; pipe(pipeToFeed); - feederPid = fork(); - if (feederPid < 0) { + rc = fork(); + if (rc < 0) { pm_error("fork() of stdin feeder failed. errno=%d (%s)", errno, strerror(errno)); - } else if (feederPid == 0) { + } else if (rc == 0) { /* This is the child -- the stdin feeder process */ close(pipeToFeed[0]); (*pipeFeederRtn)(pipeToFeed[1], feederParm); exit(0); - } - else { + } else { /* This is the parent */ + pid_t const feederPid = rc; close(pipeToFeed[1]); *fdP = pipeToFeed[0]; *pidP = feederPid; @@ -108,39 +117,59 @@ createPipeFeeder(void pipeFeederRtn(int, void *), static void -spawnProcessor(const char * const shellCommand, - int const stdinFd, - int * const stdoutFdP, - pid_t * const pidP) { +spawnProcessor(const char * const progName, + const char ** const argArray, + int const stdinFd, + int * const stdoutFdP, + pid_t * const pidP) { /*---------------------------------------------------------------------------- - Create a process to run a shell that runs command 'shellCommand'. - Pass file descriptor 'stdinFd' to the shell as Standard Input. - Set up a pipe and pass it to the shell as Standard Output. Return - as *stdoutFdP the file descriptor of the other end of that pipe, - from which Caller can suck the shell's Standard Output. + Create a process to run program 'progName' with arguments + argArray[] (terminated by NULL element). Pass file descriptor + 'stdinFd' to the shell as Standard Input. + + if 'stdoutFdP' is NULL, have that process write its Standard Output to + the current process' Standard Output. + + If 'stdoutFdP' is non-NULL, set up a pipe and pass it to the new + process as Standard Output. Return as *stdoutFdP the file + descriptor of the other end of that pipe, from which Caller can + suck the program's Standard Output. -----------------------------------------------------------------------------*/ + bool const pipeStdout = !stdoutFdP; int stdoutpipe[2]; - pid_t processorpid; - - pipe(stdoutpipe); + pid_t rc; - processorpid = fork(); - if (processorpid < 0) { + if (pipeStdout) + pipe(stdoutpipe); + + rc = fork(); + if (rc < 0) { pm_error("fork() of processor process failed. errno=%d (%s)\n", errno, strerror(errno)); - } else if (processorpid == 0) { - /* The second child */ - close(stdoutpipe[0]); + } else if (rc == 0) { + /* The program child */ + + int stdoutFd; + + if (pipeStdout) { + close(stdoutpipe[0]); + stdoutFd = stdoutpipe[1]; + } else + stdoutFd = STDOUT; - execProgram(shellCommand, stdinFd, stdoutpipe[1]); + execProgram(progName, argArray, stdinFd, stdoutFd); close(stdinFd); close(stdoutpipe[1]); pm_error("INTERNAL ERROR: execProgram() returns."); } else { /* The parent */ - close(stdoutpipe[1]); - *stdoutFdP = stdoutpipe[0]; + pid_t const processorpid = rc; + + if (pipeStdout) { + close(stdoutpipe[1]); + *stdoutFdP = stdoutpipe[0]; + } *pidP = processorpid; } } @@ -190,94 +219,150 @@ cleanupFeederProcess(pid_t const feederPid) { void -pm_system(void stdinFeeder(int, void *), - void * const feederParm, - void stdoutAccepter(int, void *), - void * const accepterParm, - const char * const shellCommand) { +pm_system_vp(const char * const progName, + const char ** const argArray, + void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm) { /*---------------------------------------------------------------------------- - Run a shell and have it run command 'shellCommand'. Feed its - Standard Input with a pipe, which is fed by the routine - 'stdinFeeder' with parameter 'feederParm'. Process its Standard - Output with the routine 'stdoutAccepter' with parameter 'accepterParm'. + Run a program in a child process. Feed its Standard Input with a + pipe, which is fed by the routine 'stdinFeeder' with parameter + 'feederParm'. Process its Standard Output with the routine + 'stdoutAccepter' with parameter 'accepterParm'. - But if 'stdinFeeder' is NULL, just feed the shell our own Standard + But if 'stdinFeeder' is NULL, just feed the program our own Standard Input. And if 'stdoutFeeder' is NULL, just send its Standard Output to our own Standard Output. -----------------------------------------------------------------------------*/ /* If 'stdinFeeder' is non-NULL, we create a child process to run - 'stdinFeeder' and create a pipe between from that process as the - shell's Standard Input. + 'stdinFeeder' and create a pipe from that process as the + program's Standard Input. + + We create another child process to run the program. - If 'stdoutFeeder' is non-NULL, we create a child process to run - the shell and create a pipe between the shell's Standard Output - and this process, and then this process runs 'stdoutAccepter' - to read the data from that pipe. + If 'stdoutFeeder' is non-NULL, we create a pipe between the + program process and the current process and have the program + write its Standard Output to that pipe. The current process + runs 'stdoutAccepter' to read the data from that pipe. - But if 'stdoutFeeder' is NULL, we just run the shell in this - process. + But if 'stdoutFeeder' is NULL, we just tell the program process + to write to the current process' Standard Output. - So there can be 1, 2, or 3 processes involved depending on - parameters. + So there are two processes when stdinFeeder is NULL and three when + stdinFeeder is non-null. */ - int shellStdinFd; + int progStdinFd; pid_t feederPid; + pid_t processorPid; if (stdinFeeder) - createPipeFeeder(stdinFeeder, feederParm, &shellStdinFd, &feederPid); + createPipeFeeder(stdinFeeder, feederParm, &progStdinFd, &feederPid); else { - shellStdinFd = STDIN; + progStdinFd = STDIN; feederPid = 0; } if (stdoutAccepter) { - int shellStdoutFd; - pid_t processorPid; + int progStdoutFd; - /* Make a child process to run the shell and pipe back to us its + /* Make a child process to run the program and pipe back to us its Standard Output */ - spawnProcessor(shellCommand, shellStdinFd, - &shellStdoutFd, &processorPid); + spawnProcessor(progName, argArray, progStdinFd, + &progStdoutFd, &processorPid); - /* The shell process has cloned our 'shellStdinFd'; we have no + /* The child process has cloned our 'progStdinFd'; we have no more use for our copy. */ - close(shellStdinFd); - /* Dispose of the stdout from that shell */ - (*stdoutAccepter)(shellStdoutFd, accepterParm); - close(shellStdoutFd); - - cleanupProcessorProcess(processorPid); + close(progStdinFd); + /* Dispose of the stdout from that child */ + (*stdoutAccepter)(progStdoutFd, accepterParm); + close(progStdoutFd); } else { - /* Run a child process for the shell that sends its Standard Output + /* Run a child process for the program that sends its Standard Output to our Standard Output */ - int const stdinSaveFd = dup(STDIN); - int rc; + spawnProcessor(progName, argArray, STDIN, NULL, &processorPid); + } + + cleanupProcessorProcess(processorPid); + + if (feederPid) + cleanupFeederProcess(feederPid); +} - dup2(shellStdinFd, STDIN); - - rc = system(shellCommand); - close(STDIN); - dup2(stdinSaveFd, STDIN); + +void +pm_system_lp(const char * const progName, + void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm, + ...) { +/*---------------------------------------------------------------------------- + same as pm_system_vp() except with arguments as variable arguments + instead of an array. +-----------------------------------------------------------------------------*/ + va_list args; + bool endOfArgs; + const char ** argArray; + unsigned int n; + + va_start(args, accepterParm); + + endOfArgs = FALSE; + argArray = NULL; + + for (endOfArgs = FALSE, argArray = NULL, n = 0; + !endOfArgs; + ) { + const char * const arg = va_arg(args, const char *); - if (rc < 0) - pm_error("Unable to invoke the shell. Errno=%d (%s)", - errno, strerror(errno)); - else if (rc != 0) - pm_message("WARNING: Shell process completion code = %d", rc); + REALLOCARRAY(argArray, n+1); + + argArray[n++] = arg; + + if (!arg) + endOfArgs = TRUE; } - if (feederPid) - cleanupFeederProcess(feederPid); + va_end(args); + + pm_system_vp(progName, argArray, + stdinFeeder, feederParm, stdoutAccepter, accepterParm); + + free(argArray); } +void +pm_system(void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm, + const char * const shellCommand) { +/*---------------------------------------------------------------------------- + Run a shell and have it run command 'shellCommand'. Feed its + Standard Input with a pipe, which is fed by the routine + 'stdinFeeder' with parameter 'feederParm'. Process its Standard + Output with the routine 'stdoutAccepter' with parameter 'accepterParm'. + + But if 'stdinFeeder' is NULL, just feed the shell our own Standard + Input. And if 'stdoutFeeder' is NULL, just send its Standard Output + to our own Standard Output. +-----------------------------------------------------------------------------*/ + + pm_system_lp("/bin/sh", + stdinFeeder, feederParm, stdoutAccepter, accepterParm, + "sh", "-c", shellCommand, NULL); +} + + void pm_feed_from_memory(int const pipeToFeedFd, diff --git a/lib/libsystem_dummy.c b/lib/libsystem_dummy.c index afe20dde..2787ce67 100644 --- a/lib/libsystem_dummy.c +++ b/lib/libsystem_dummy.c @@ -15,6 +15,24 @@ #include "pm_system.h" void +pm_system_vp(const char * const progName, + const char ** const argArray, + void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm) { +} + +void +pm_system_lp(const char * const progName, + void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm, + ...) { +} + +void pm_system(void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), diff --git a/lib/mkstemp.c b/lib/mkstemp.c deleted file mode 100644 index cd9140c7..00000000 --- a/lib/mkstemp.c +++ /dev/null @@ -1,8 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> -#include <fcntl.h> - -#include "pm_c_util.h" - - - diff --git a/lib/pam.h b/lib/pam.h index 3bad05e8..c28c5c2c 100644 --- a/lib/pam.h +++ b/lib/pam.h @@ -6,8 +6,8 @@ #ifndef PAM_H #define PAM_H -#include "pm.h" -#include "pnm.h" +#include <netpbm/pm.h> +#include <netpbm/pnm.h> #ifdef __cplusplus extern "C" { @@ -23,16 +23,16 @@ typedef unsigned long sample; */ struct pam { -/* This structure describes an open PAM image file. It consists - entirely of information that belongs in the header of a PAM image - and filesystem information. It does not contain any state - information about the processing of that image. - - This is not considered to be an opaque object. The user of Netbpm - libraries is free to access and set any of these fields whenever - appropriate. The structure exists to make coding of function calls - easy. -*/ + /* This structure describes an open PAM image file. It consists + entirely of information that belongs in the header of a PAM image + and filesystem information. It does not contain any state + information about the processing of that image. + + This is not considered to be an opaque object. The user of Netbpm + libraries is free to access and set any of these fields whenever + appropriate. The structure exists to make coding of function calls + easy. + */ /* 'size' and 'len' are necessary in order to provide forward and backward compatibility between library functions and calling programs @@ -44,7 +44,9 @@ struct pam { /* The length, in bytes, of the information in this structure. The information starts in the first byte and is contiguous. This cannot be greater than 'size' - */ + + Use PAM_STRUCT_SIZE() to compute or interpret a value for this. + */ FILE * file; int format; /* The format code of the raw image. This is PAM_FORMAT @@ -52,7 +54,7 @@ struct pam { image. Then it's PBM_FORMAT, RPBM_FORMAT, etc. */ unsigned int plainformat; - /* Logical: On output, use the plain version of the format type + /* Logical: On output, use plain version of the format type indicated by 'format'. Otherwise, use the raw version. (i.e., on output, the plainness information in 'format' is irrelevant). Input functions set this to FALSE, for the @@ -102,7 +104,7 @@ struct pam { On output, NULL means no comments. - On input, libnetpbm mallocs storage for the comments and placed + On input, libnetpbm mallocs storage for the comments and places the pointer at *comment_p. Caller must free it. NULL means libnetpbm does not return comments and does not allocate any storage. @@ -457,6 +459,11 @@ tuple pnm_parsecolor(const char * const colorname, sample const maxval); +const char * +pnm_colorname(struct pam * const pamP, + tuple const color, + int const hexok); + extern double pnm_lumin_factor[3]; diff --git a/lib/pammap.h b/lib/pammap.h index fa054deb..c9c1deed 100644 --- a/lib/pammap.h +++ b/lib/pammap.h @@ -9,8 +9,8 @@ #ifndef PAMMAP_H #define PAMMAP_H -#include "colorname.h" -#include "pam.h" +#include <netpbm/colorname.h> +#include <netpbm/pam.h> #ifdef __cplusplus extern "C" { @@ -72,10 +72,12 @@ tupletable pnm_alloctupletable(const struct pam * const pamP, unsigned int const size); void -pnm_freetupletable(struct pam * const pamP, tupletable const tupletable); +pnm_freetupletable(const struct pam * const pamP, + tupletable const tupletable); void -pnm_freetupletable2(struct pam * const pamP, tupletable2 const tupletable); +pnm_freetupletable2(const struct pam * const pamP, + tupletable2 const tupletable); tuplehash pnm_createtuplehash(void); @@ -96,6 +98,14 @@ pnm_computetuplefreqtable2(struct pam * const pamP, sample const newMaxval, unsigned int * const sizeP); +tupletable +pnm_computetuplefreqtable3(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + unsigned int const newDepth, + sample const newMaxval, + unsigned int * const countP); + tuplehash pnm_computetuplefreqhash(struct pam * const pamP, tuple ** const tupleArray, diff --git a/lib/pbm.h b/lib/pbm.h index 1591c77f..24574d07 100644 --- a/lib/pbm.h +++ b/lib/pbm.h @@ -1,7 +1,7 @@ #ifndef PBM_H_INCLUDED #define PBM_H_INCLUDED -#include "pm.h" +#include <netpbm/pm.h> #ifdef __cplusplus extern "C" { @@ -33,13 +33,18 @@ typedef unsigned char bit; /* Declarations of routines. */ -void pbm_init ARGS(( int* argcP, char* argv[] )); +void +pbm_init(int * const argcP, + char ** const argv); + void pbm_nextimage(FILE *file, int * const eofP); +bit * +pbm_allocrow(unsigned int const cols); + #define pbm_allocarray(cols, rows) \ ((bit**) pm_allocarray(cols, rows, sizeof(bit))) -#define pbm_allocrow(cols) ((bit*) pm_allocrow(cols, sizeof(bit))) #define pbm_freearray(bits, rows) pm_freearray((char**) bits, rows) #define pbm_freerow(bitrow) pm_freerow((char*) bitrow) #define pbm_packed_bytes(cols) (((cols)+7)/8) @@ -56,9 +61,19 @@ pbm_nextimage(FILE *file, int * const eofP); bit** pbm_readpbm(FILE* file, int* colsP, int* rowsP); void pbm_readpbminit(FILE* file, int* colsP, int* rowsP, int* formatP); void pbm_readpbmrow(FILE* file, bit* bitrow, int cols, int format); -void pbm_readpbmrow_packed( - FILE* const file, unsigned char * const packed_bits, - const int cols, const int format); + +void +pbm_readpbmrow_packed(FILE * const file, + unsigned char * const packedBits, + int const cols, + int const format); + +void +pbm_readpbmrow_bitoffset(FILE * const fileP, + unsigned char * const packedBits, + int const cols, + int const format, + unsigned int const offset); void pbm_writepbminit(FILE * const fileP, @@ -74,10 +89,10 @@ pbm_writepbm(FILE * const fileP, int const forceplain); void -pbm_writepbmrow(FILE * const fileP, - bit * const bitrow, - int const cols, - int const forceplain); +pbm_writepbmrow(FILE * const fileP, + const bit * const bitrow, + int const cols, + int const forceplain); void pbm_writepbmrow_packed(FILE * const fileP, @@ -86,10 +101,22 @@ pbm_writepbmrow_packed(FILE * const fileP, int const forceplain); void +pbm_writepbmrow_bitoffset(FILE * const ifP, + unsigned char * const packedBits, + unsigned int const cols, + int const format, + unsigned int const offset); + +void pbm_check(FILE * file, const enum pm_check_type check_type, const int format, const int cols, const int rows, enum pm_check_code * const retval_p); +bit +pbm_backgroundbitrow(const unsigned char * const packedBits, + unsigned int const cols, + unsigned int const offset); + #ifdef __cplusplus } #endif diff --git a/lib/pbmfont.h b/lib/pbmfont.h index 432aea3c..d287bc6c 100644 --- a/lib/pbmfont.h +++ b/lib/pbmfont.h @@ -13,22 +13,23 @@ struct glyph { can be anything, but normally does not have white borders because it's more efficient to represent white borders explicitly. */ - int width, height; + unsigned int width; + unsigned int height; /* The dimensions of the central glyph, i.e. the 'bmap' array */ - int x; + unsigned int x; /* Width in pixels of the white left border of this glyph. This can actually be negative to indicate that the central glyph backs up over the previous character in the line. In that case, if there is no previous character in the line, it is as if 'x' is 0. */ - int y; + unsigned int y; /* Height in pixels of the white bottom border of this glyph */ - int xadd; + unsigned int xadd; /* Width of glyph -- white left border plus central glyph plus white right border */ - const char * bmap; + const char * bmap; /* The raster of the central glyph. It is an 'width' by 'height' array in row major order, with each byte being 1 for black; 0 for white. E.g. if 'width' is 20 pixels and @@ -44,31 +45,30 @@ struct font { an code point in the range 0..255, this structure describes the glyph for that character. */ - int maxwidth, maxheight; - int x; + int maxwidth, maxheight; + int x; /* ?? Not used by Pbmtext */ int y; /* Amount of white space that should be added between lines of - this font. + this font. Can be negative. */ - struct glyph * glyph[256]; + struct glyph * glyph[256]; /* glyph[i] is the glyph for code point i */ - bit** oldfont; - /* for compatibility with old pbmtext routines */ - /* oldfont is 0 if the font is BDF derived */ - int fcols, frows; + const bit ** oldfont; + /* for compatibility with old pbmtext routines */ + /* oldfont is NULL if the font is BDF derived */ + int fcols, frows; }; struct font* pbm_defaultfont(const char* const which); struct font* -pbm_dissectfont(bit ** const font, - int const frows, - int const fcols); +pbm_dissectfont(const bit ** const font, + unsigned int const frows, + unsigned int const fcols); struct font* pbm_loadfont(const char * const filename); struct font* pbm_loadpbmfont(const char * const filename); struct font* pbm_loadbdffont(const char * const filename); -void pbm_dumpfont ARGS(( struct font* fn )); -int mk_argvn ARGS(( char* s, char* vec[], int max )); +void pbm_dumpfont(struct font * const fnP); #ifdef __cplusplus } diff --git a/lib/pgm.h b/lib/pgm.h index 86935307..2de8d531 100644 --- a/lib/pgm.h +++ b/lib/pgm.h @@ -4,7 +4,8 @@ #ifndef _PGM_H_ #define _PGM_H_ -#include "pbm.h" +#include <netpbm/pm.h> +#include <netpbm/pbm.h> #ifdef __cplusplus extern "C" { @@ -68,7 +69,7 @@ pgm_init(int * const argcP, gray * pgm_allocrow(unsigned int const cols); -#define pgm_freerow(grayrow) free(grayrow) +#define pgm_freerow(grayrow) pm_freerow(grayrow) #define pgm_allocarray( cols, rows ) \ ((gray**) pm_allocarray( cols, rows, sizeof(gray) )) diff --git a/lib/pm.h b/lib/pm.h index 9c3c087e..4ba20aa5 100644 --- a/lib/pm.h +++ b/lib/pm.h @@ -13,7 +13,7 @@ #ifndef PM_H_INCLUDED #define PM_H_INCLUDED -#include "pm_config.h" +#include <netpbm/pm_config.h> #include <sys/types.h> #include <ctype.h> @@ -91,12 +91,13 @@ extern int pm_plain_output; /* Output functions are to produce plain (as opposed to raw) format regardless of their 'plainformat' arguments. */ +extern const char * pm_progname; void pm_init(const char * const progname, unsigned int const flags); void -pm_proginit(int* const argcP, char* argv[]); +pm_proginit(int * const argcP, const char * argv[]); void pm_setMessage(int const newState, int * const oldStateP); @@ -104,11 +105,18 @@ pm_setMessage(int const newState, int * const oldStateP); FILE * pm_tmpfile(void); +int +pm_tmpfile_fd(void); + void pm_make_tmpfile(FILE ** const filePP, const char ** const filenameP); void +pm_make_tmpfile_fd(int * const fdP, + const char ** const filenameP); + +void pm_nextimage(FILE * const file, int * const eofP); /* Variable-sized arrays definitions. */ @@ -116,7 +124,7 @@ pm_nextimage(FILE * const file, int * const eofP); char** pm_allocarray (int const cols, int const rows, int const size ); -char * +void * pm_allocrow(unsigned int const cols, unsigned int const size); @@ -124,12 +132,14 @@ void pm_freearray (char** const its, int const rows); void -pm_freerow(char* const itrow); +pm_freerow(void * const row); /* Obsolete -- use shhopt instead */ int -pm_keymatch (char* const str, const char* const keyword, int const minchars); +pm_keymatch(const char * const str, + const char * const keyword, + int const minchars); int PURE_FN_ATTR @@ -154,15 +164,25 @@ pm_setjmpbufsave(jmp_buf * const jmpbufP, void pm_longjmp(void); + +typedef void pm_usermessagefn(const char * msg); + +void +pm_setusermessagefn(pm_usermessagefn * fn); + +typedef void pm_usererrormsgfn(const char * msg); + +void +pm_setusererrormsgfn(pm_usererrormsgfn * fn); + void PM_GNU_PRINTF_ATTR(1,2) pm_message (const char format[], ...); void PM_GNU_PRINTF_ATTR(1,2) -pm_error (const char reason[], ...); +pm_errormsg(const char format[], ...); -/* Obsolete - use helpful error message instead */ -void -pm_perror (const char reason[]); +void PM_GNU_PRINTF_ATTR(1,2) +pm_error (const char reason[], ...); /* Obsolete - use shhopt and user's manual instead */ void @@ -335,9 +355,23 @@ pm_check(FILE * const file, pm_filepos const need_raster_size, enum pm_check_code * const retval_p); +void +pm_drain(FILE * const fileP, + unsigned int const limit, + unsigned int * const bytesReadP); + char * pm_arg0toprogname(const char arg0[]); +unsigned int +pm_randseed(void); + +unsigned int +pm_parse_width(const char * const arg); + +unsigned int +pm_parse_height(const char * const arg); + #ifdef __cplusplus } #endif diff --git a/lib/pm_gamma.h b/lib/pm_gamma.h index 92b34145..6630e05c 100644 --- a/lib/pm_gamma.h +++ b/lib/pm_gamma.h @@ -1,7 +1,7 @@ #ifndef _PM_GAMMA_H_ #define _PM_GAMMA_H_ -#include "pm_config.h" +#include <netpbm/pm_config.h> #include <math.h> diff --git a/lib/pm_system.h b/lib/pm_system.h index 0605f888..a7560f48 100644 --- a/lib/pm_system.h +++ b/lib/pm_system.h @@ -10,13 +10,28 @@ extern "C" { void +pm_system_vp(const char * const progName, + const char ** const argArray, + void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm); + +void +pm_system_lp(const char * const progName, + void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm, + ...); + +void pm_system(void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm, const char * const shellCommand); - struct bufferDesc { /* This is just a parameter for the routines below */ unsigned int size; diff --git a/lib/pmfileio.c b/lib/pmfileio.c new file mode 100644 index 00000000..585b0d0f --- /dev/null +++ b/lib/pmfileio.c @@ -0,0 +1,941 @@ +/************************************************************************** + pmfileio.c +*************************************************************************** + This file contains fundamental file I/O stuff for libnetpbm. + These are external functions, unlike 'fileio.c', but are not + particular to any Netpbm format. +**************************************************************************/ +#define _SVID_SOURCE + /* Make sure P_tmpdir is defined in GNU libc 2.0.7 (_XOPEN_SOURCE 500 + does it in other libc's). pm_config.h defines TMPDIR as P_tmpdir + in some environments. + */ +#define _XOPEN_SOURCE 500 /* Make sure ftello, fseeko are defined */ +#define _LARGEFILE_SOURCE 1 /* Make sure ftello, fseeko are defined */ +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + /* This means ftello() is really ftello64() and returns a 64 bit file + position. Unless the C library doesn't have ftello64(), in which + case ftello() is still just ftello(). + + Likewise for all the other C library file functions. + + And off_t and fpos_t are 64 bit types instead of 32. Consequently, + pm_filepos_t might be 64 bits instead of 32. + */ +#define _LARGE_FILES + /* This does for AIX what _FILE_OFFSET_BITS=64 does for GNU */ +#define _LARGE_FILE_API + /* This makes the the x64() functions available on AIX */ + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#ifdef __DJGPP__ + #include <io.h> +#endif + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "nstring.h" + +#include "pm.h" + + + +/* File open/close that handles "-" as stdin/stdout and checks errors. */ + +FILE* +pm_openr(const char * const name) { + FILE* f; + + if (strcmp(name, "-") == 0) + f = stdin; + else { +#ifndef VMS + f = fopen(name, "rb"); +#else + f = fopen(name, "r", "ctx=stm"); +#endif + if (f == NULL) + pm_error("Unable to open file '%s' for reading. " + "fopen() returns errno %d (%s)", + name, errno, strerror(errno)); + } + return f; +} + + + +FILE* +pm_openw(const char * const name) { + FILE* f; + + if (strcmp(name, "-") == 0) + f = stdout; + else { +#ifndef VMS + f = fopen(name, "wb"); +#else + f = fopen(name, "w", "mbc=32", "mbf=2"); /* set buffer factors */ +#endif + if (f == NULL) + pm_error("Unable to open file '%s' for writing. " + "fopen() returns errno %d (%s)", + name, errno, strerror(errno)); + } + return f; +} + + + +static const char * +tmpDir(void) { +/*---------------------------------------------------------------------------- + Return the name of the directory in which we should create temporary + files. + + The name is a constant in static storage. +-----------------------------------------------------------------------------*/ + const char * tmpdir; + /* running approximation of the result */ + + tmpdir = getenv("TMPDIR"); /* Unix convention */ + + if (!tmpdir || strlen(tmpdir) == 0) + tmpdir = getenv("TMP"); /* Windows convention */ + + if (!tmpdir || strlen(tmpdir) == 0) + tmpdir = getenv("TEMP"); /* Windows convention */ + + if (!tmpdir || strlen(tmpdir) == 0) + tmpdir = TMPDIR; + + return tmpdir; +} + + + +static int +mkstempx(char * const filenameBuffer) { +/*---------------------------------------------------------------------------- + This is meant to be equivalent to POSIX mkstemp(). + + On some old systems, mktemp() is a security hazard that allows a hacker + to read or write our temporary file or cause us to read or write some + unintended file. On other systems, mkstemp() does not exist. + + A Windows/mingw environment is one which doesn't have mkstemp() + (2006.06.15). + + We assume that if a system doesn't have mkstemp() that its mktemp() + is safe, or that the total situation is such that the problems of + mktemp() are not a problem for the user. +-----------------------------------------------------------------------------*/ + int retval; + int fd; + unsigned int attempts; + bool gotFile; + bool error; + + for (attempts = 0, gotFile = FALSE, error = FALSE; + !gotFile && !error && attempts < 100; + ++attempts) { + + char * rc; + rc = mktemp(filenameBuffer); + + if (rc == NULL) + error = TRUE; + else { + int rc; + + rc = open(filenameBuffer, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + + if (rc == 0) { + fd = rc; + gotFile = TRUE; + } else { + if (errno == EEXIST) { + /* We'll just have to keep trying */ + } else + error = TRUE; + } + } + } + if (gotFile) + retval = fd; + else + retval = -1; + + return retval; +} + + + +static int +mkstemp2(char * const filenameBuffer) { + +#if HAVE_MKSTEMP + if (0) + mkstempx(NULL); /* defeat compiler unused function warning */ + return mkstemp(filenameBuffer); +#else + return mkstempx(filenameBuffer); +#endif +} + + + +static void +makeTmpfileWithTemplate(const char * const filenameTemplate, + int * const fdP, + const char ** const filenameP, + const char ** const errorP) { + + char * filenameBuffer; /* malloc'ed */ + + filenameBuffer = strdup(filenameTemplate); + + if (filenameBuffer == NULL) + asprintfN(errorP, "Unable to allocate storage for temporary " + "file name"); + else { + int rc; + + rc = mkstemp2(filenameBuffer); + + if (rc < 0) + asprintfN(errorP, + "Unable to create temporary file according to name " + "pattern '%s'. mkstemp() failed with errno %d (%s)", + filenameTemplate, errno, strerror(errno)); + else { + *fdP = rc; + *filenameP = filenameBuffer; + *errorP = NULL; + } + if (*errorP) + strfree(filenameBuffer); + } +} + + + +void +pm_make_tmpfile_fd(int * const fdP, + const char ** const filenameP) { + + const char * filenameTemplate; + unsigned int fnamelen; + const char * tmpdir; + const char * dirseparator; + const char * error; + + fnamelen = strlen(pm_progname) + 10; /* "/" + "_XXXXXX\0" */ + + tmpdir = tmpDir(); + + if (tmpdir[strlen(tmpdir) - 1] == '/') + dirseparator = ""; + else + dirseparator = "/"; + + asprintfN(&filenameTemplate, "%s%s%s%s", + tmpdir, dirseparator, pm_progname, "_XXXXXX"); + + if (filenameTemplate == NULL) + asprintfN(&error, + "Unable to allocate storage for temporary file name"); + else { + makeTmpfileWithTemplate(filenameTemplate, fdP, filenameP, &error); + + strfree(filenameTemplate); + } + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } +} + + + +void +pm_make_tmpfile(FILE ** const filePP, + const char ** const filenameP) { + + int fd; + + pm_make_tmpfile_fd(&fd, filenameP); + + *filePP = fdopen(fd, "w+b"); + + if (*filePP == NULL) { + close(fd); + unlink(*filenameP); + strfree(*filenameP); + + pm_error("Unable to create temporary file. " + "fdopen() failed with errno %d (%s)", + errno, strerror(errno)); + } +} + + + +FILE * +pm_tmpfile(void) { + + FILE * fileP; + const char * tmpfile; + + pm_make_tmpfile(&fileP, &tmpfile); + + unlink(tmpfile); + + strfree(tmpfile); + + return fileP; +} + + + +int +pm_tmpfile_fd(void) { + + int fd; + const char * tmpfile; + + pm_make_tmpfile_fd(&fd, &tmpfile); + + unlink(tmpfile); + + strfree(tmpfile); + + return fd; +} + + + +FILE * +pm_openr_seekable(const char name[]) { +/*---------------------------------------------------------------------------- + Open the file named by name[] such that it is seekable (i.e. it can be + rewound and read in multiple passes with fseek()). + + If the file is actually seekable, this reduces to the same as + pm_openr(). If not, we copy the named file to a temporary file + and return that file's stream descriptor. + + We use a file that the operating system recognizes as temporary, so + it picks the filename and deletes the file when Caller closes it. +-----------------------------------------------------------------------------*/ + int stat_rc; + int seekable; /* logical: file is seekable */ + struct stat statbuf; + FILE * original_file; + FILE * seekable_file; + + original_file = pm_openr((char *) name); + + /* I would use fseek() to determine if the file is seekable and + be a little more general than checking the type of file, but I + don't have reliable information on how to do that. I have seen + streams be partially seekable -- you can, for example seek to + 0 if the file is positioned at 0 but you can't actually back up + to 0. I have seen documentation that says the errno for an + unseekable stream is EBADF and in practice seen ESPIPE. + + On the other hand, regular files are always seekable and even if + some other file is, it doesn't hurt much to assume it isn't. + */ + + stat_rc = fstat(fileno(original_file), &statbuf); + if (stat_rc == 0 && S_ISREG(statbuf.st_mode)) + seekable = TRUE; + else + seekable = FALSE; + + if (seekable) { + seekable_file = original_file; + } else { + seekable_file = pm_tmpfile(); + + /* Copy the input into the temporary seekable file */ + while (!feof(original_file) && !ferror(original_file) + && !ferror(seekable_file)) { + char buffer[4096]; + int bytes_read; + bytes_read = fread(buffer, 1, sizeof(buffer), original_file); + fwrite(buffer, 1, bytes_read, seekable_file); + } + if (ferror(original_file)) + pm_error("Error reading input file into temporary file. " + "Errno = %s (%d)", strerror(errno), errno); + if (ferror(seekable_file)) + pm_error("Error writing input into temporary file. " + "Errno = %s (%d)", strerror(errno), errno); + pm_close(original_file); + { + int seek_rc; + seek_rc = fseek(seekable_file, 0, SEEK_SET); + if (seek_rc != 0) + pm_error("fseek() failed to rewind temporary file. " + "Errno = %s (%d)", strerror(errno), errno); + } + } + return seekable_file; +} + + + +void +pm_close(FILE * const f) { + fflush(f); + if (ferror(f)) + pm_message("A file read or write error occurred at some point"); + if (f != stdin) + if (fclose(f) != 0) + pm_error("close of file failed with errno %d (%s)", + errno, strerror(errno)); +} + + + +/* The pnmtopng package uses pm_closer() and pm_closew() instead of + pm_close(), apparently because the 1999 Pbmplus package has them. + I don't know what the difference is supposed to be. +*/ + +void +pm_closer(FILE * const f) { + pm_close(f); +} + + + +void +pm_closew(FILE * const f) { + pm_close(f); +} + + + +/* Endian I/O. + + Before Netpbm 10.27 (March 2005), these would return failure on EOF + or I/O failure. For backward compatibility, they still have the return + code, but it is always zero and the routines abort the program in case + of EOF or I/O failure. A program that wants to handle failure differently + must use lower level (C library) interfaces. But that level of detail + is uncharacteristic of a Netpbm program; the ease of programming that + comes with not checking a return code is more Netpbm. + + It is also for historical reasons that these return signed values, + when clearly unsigned would make more sense. +*/ + + + +static void +abortWithReadError(FILE * const ifP) { + + if (feof(ifP)) + pm_error("Unexpected end of input file"); + else + pm_error("Error (not EOF) reading file."); +} + + + +static unsigned char +getcNofail(FILE * const ifP) { + + int c; + + c = getc(ifP); + + if (c == EOF) + abortWithReadError(ifP); + + return (unsigned char)c; +} + + + +void +pm_readchar(FILE * const ifP, + char * const cP) { + + *cP = (char)getcNofail(ifP); +} + + + +void +pm_writechar(FILE * const ofP, + char const c) { + + putc(c, ofP); +} + + + +int +pm_readbigshort(FILE * const ifP, + short * const sP) { + + unsigned short s; + + s = getcNofail(ifP) << 8; + s |= getcNofail(ifP) << 0; + + *sP = s; + + return 0; +} + + + +int +pm_writebigshort(FILE * const ofP, + short const s) { + + putc((s >> 8) & 0xff, ofP); + putc(s & 0xff, ofP); + + return 0; +} + + + +int +pm_readbiglong(FILE * const ifP, + long * const lP) { + + unsigned long l; + + l = getcNofail(ifP) << 24; + l |= getcNofail(ifP) << 16; + l |= getcNofail(ifP) << 8; + l |= getcNofail(ifP) << 0; + + *lP = l; + + return 0; +} + + + +int +pm_writebiglong(FILE * const ofP, + long const l) { + + putc((l >> 24) & 0xff, ofP); + putc((l >> 16) & 0xff, ofP); + putc((l >> 8) & 0xff, ofP); + putc((l >> 0) & 0xff, ofP); + + return 0; +} + + + +int +pm_readlittleshort(FILE * const ifP, + short * const sP) { + unsigned short s; + + s = getcNofail(ifP) << 0; + s |= getcNofail(ifP) << 8; + + *sP = s; + + return 0; +} + + + +int +pm_writelittleshort(FILE * const ofP, + short const s) { + + putc((s >> 0) & 0xff, ofP); + putc((s >> 8) & 0xff, ofP); + + return 0; +} + + + +int +pm_readlittlelong(FILE * const ifP, + long * const lP) { + unsigned long l; + + l = getcNofail(ifP) << 0; + l |= getcNofail(ifP) << 8; + l |= getcNofail(ifP) << 16; + l |= getcNofail(ifP) << 24; + + *lP = l; + + return 0; +} + + + +int +pm_writelittlelong(FILE * const ofP, + long const l) { + + putc((l >> 0) & 0xff, ofP); + putc((l >> 8) & 0xff, ofP); + putc((l >> 16) & 0xff, ofP); + putc((l >> 24) & 0xff, ofP); + + return 0; +} + + + +int +pm_readmagicnumber(FILE * const ifP) { + + int ich1, ich2; + + ich1 = getc(ifP); + ich2 = getc(ifP); + if (ich1 == EOF || ich2 == EOF) + pm_error( "Error reading magic number from Netpbm image stream. " + "Most often, this " + "means your input file is empty." ); + + return ich1 * 256 + ich2; +} + + + +/* Read a file of unknown size to a buffer. Return the number of bytes + read. Allocate more memory as we need it. The calling routine has + to free() the buffer. + + Oliver Trepte, oliver@fysik4.kth.se, 930613 +*/ + +#define PM_BUF_SIZE 16384 /* First try this size of the buffer, then + double this until we reach PM_MAX_BUF_INC */ +#define PM_MAX_BUF_INC 65536 /* Don't allocate more memory in larger blocks + than this. */ + +char * +pm_read_unknown_size(FILE * const file, + long * const nread) { + long nalloc; + char * buf; + bool eof; + + *nread = 0; + nalloc = PM_BUF_SIZE; + MALLOCARRAY(buf, nalloc); + + eof = FALSE; /* initial value */ + + while(!eof) { + int val; + + if (*nread >= nalloc) { /* We need a larger buffer */ + if (nalloc > PM_MAX_BUF_INC) + nalloc += PM_MAX_BUF_INC; + else + nalloc += nalloc; + REALLOCARRAY_NOFAIL(buf, nalloc); + } + + val = getc(file); + if (val == EOF) + eof = TRUE; + else + buf[(*nread)++] = val; + } + return buf; +} + + + +union cheat { + uint32_t l; + short s; + unsigned char c[4]; +}; + + + +short +pm_bs_short(short const s) { + union cheat u; + unsigned char t; + + u.s = s; + t = u.c[0]; + u.c[0] = u.c[1]; + u.c[1] = t; + return u.s; +} + + + +long +pm_bs_long(long const l) { + union cheat u; + unsigned char t; + + u.l = l; + t = u.c[0]; + u.c[0] = u.c[3]; + u.c[3] = t; + t = u.c[1]; + u.c[1] = u.c[2]; + u.c[2] = t; + return u.l; +} + + + +void +pm_tell2(FILE * const fileP, + void * const fileposP, + unsigned int const fileposSize) { +/*---------------------------------------------------------------------------- + Return the current file position as *filePosP, which is a buffer + 'fileposSize' bytes long. Abort the program if error, including if + *fileP isn't a file that has a position. +-----------------------------------------------------------------------------*/ + /* Note: FTELLO() is either ftello() or ftell(), depending on the + capabilities of the underlying C library. It is defined in + pm_config.h. ftello(), in turn, may be either ftell() or + ftello64(), as implemented by the C library. + */ + pm_filepos const filepos = FTELLO(fileP); + if (filepos < 0) + pm_error("ftello() to get current file position failed. " + "Errno = %s (%d)\n", strerror(errno), errno); + + if (fileposSize == sizeof(pm_filepos)) { + pm_filepos * const fileposP_filepos = fileposP; + *fileposP_filepos = filepos; + } else if (fileposSize == sizeof(long)) { + if (sizeof(pm_filepos) > sizeof(long) && + filepos >= (pm_filepos) 1 << (sizeof(long)*8)) + pm_error("File size is too large to represent in the %u bytes " + "that were provided to pm_tell2()", fileposSize); + else { + long * const fileposP_long = fileposP; + *fileposP_long = (long)filepos; + } + } else + pm_error("File position size passed to pm_tell() is invalid: %u. " + "Valid sizes are %u and %u", + fileposSize, (unsigned int)sizeof(pm_filepos), + (unsigned int) sizeof(long)); +} + + + +unsigned int +pm_tell(FILE * const fileP) { + + long filepos; + + pm_tell2(fileP, &filepos, sizeof(filepos)); + + return filepos; +} + + + +void +pm_seek2(FILE * const fileP, + const pm_filepos * const fileposP, + unsigned int const fileposSize) { +/*---------------------------------------------------------------------------- + Position file *fileP to position *fileposP. Abort if error, including + if *fileP isn't a seekable file. +-----------------------------------------------------------------------------*/ + if (fileposSize == sizeof(pm_filepos)) + /* Note: FSEEKO() is either fseeko() or fseek(), depending on the + capabilities of the underlying C library. It is defined in + pm_config.h. fseeko(), in turn, may be either fseek() or + fseeko64(), as implemented by the C library. + */ + FSEEKO(fileP, *fileposP, SEEK_SET); + else if (fileposSize == sizeof(long)) { + long const fileposLong = *(long *)fileposP; + fseek(fileP, fileposLong, SEEK_SET); + } else + pm_error("File position size passed to pm_seek() is invalid: %u. " + "Valid sizes are %u and %u", + fileposSize, (unsigned int)sizeof(pm_filepos), + (unsigned int) sizeof(long)); +} + + + +void +pm_seek(FILE * const fileP, unsigned long filepos) { +/*---------------------------------------------------------------------------- +-----------------------------------------------------------------------------*/ + + pm_filepos fileposBuff; + + fileposBuff = filepos; + + pm_seek2(fileP, &fileposBuff, sizeof(fileposBuff)); +} + + + +void +pm_nextimage(FILE * const file, int * const eofP) { +/*---------------------------------------------------------------------------- + Position the file 'file' to the next image in the stream, assuming it is + now positioned just after the current image. I.e. read off any white + space at the end of the current image's raster. Note that the raw formats + don't permit such white space, but this routine tolerates it anyway, + because the plain formats do permit white space after the raster. + + Iff there is no next image, return *eofP == TRUE. + + Note that in practice, we will not normally see white space here in + a plain PPM or plain PGM stream because the routine to read a + sample from the image reads one character of white space after the + sample in order to know where the sample ends. There is not + normally more than one character of white space (a newline) after + the last sample in the raster. But plain PBM is another story. No white + space is required between samples of a plain PBM image. But the raster + normally ends with a newline nonetheless. Since the sample reading code + will not have read that newline, it is there for us to read now. +-----------------------------------------------------------------------------*/ + bool eof; + bool nonWhitespaceFound; + + eof = FALSE; + nonWhitespaceFound = FALSE; + + while (!eof && !nonWhitespaceFound) { + int c; + c = getc(file); + if (c == EOF) { + if (feof(file)) + eof = TRUE; + else + pm_error("File error on getc() to position to image"); + } else { + if (!isspace(c)) { + int rc; + + nonWhitespaceFound = TRUE; + + /* Have to put the non-whitespace character back in + the stream -- it's part of the next image. + */ + rc = ungetc(c, file); + if (rc == EOF) + pm_error("File error doing ungetc() " + "to position to image."); + } + } + } + *eofP = eof; +} + + + +void +pm_check(FILE * const file, + enum pm_check_type const check_type, + pm_filepos const need_raster_size, + enum pm_check_code * const retval_p) { +/*---------------------------------------------------------------------------- + This is not defined for use outside of libnetpbm. +-----------------------------------------------------------------------------*/ + struct stat statbuf; + pm_filepos curpos; /* Current position of file; -1 if none */ + int rc; + +#ifdef LARGEFILEDEBUG + pm_message("pm_filepos received by pm_check() is %u bytes.", + sizeof(pm_filepos)); +#endif + /* Note: FTELLO() is either ftello() or ftell(), depending on the + capabilities of the underlying C library. It is defined in + pm_config.h. ftello(), in turn, may be either ftell() or + ftello64(), as implemented by the C library. + */ + curpos = FTELLO(file); + if (curpos >= 0) { + /* This type of file has a current position */ + + rc = fstat(fileno(file), &statbuf); + if (rc != 0) + pm_error("fstat() failed to get size of file, though ftello() " + "successfully identified\n" + "the current position. Errno=%s (%d)", + strerror(errno), errno); + else if (!S_ISREG(statbuf.st_mode)) { + /* Not a regular file; we can't know its size */ + if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; + } else { + pm_filepos const have_raster_size = statbuf.st_size - curpos; + + if (have_raster_size < need_raster_size) + pm_error("File has invalid format. The raster should " + "contain %u bytes, but\n" + "the file ends after only %u bytes.", + (unsigned int) need_raster_size, + (unsigned int) have_raster_size); + else if (have_raster_size > need_raster_size) { + if (retval_p) *retval_p = PM_CHECK_TOO_LONG; + } else { + if (retval_p) *retval_p = PM_CHECK_OK; + } + } + } else + if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; +} + + + +void +pm_drain(FILE * const fileP, + unsigned int const limit, + unsigned int * const bytesReadP) { +/*---------------------------------------------------------------------------- + Read bytes from *fileP until EOF and return as *bytesReadP how many there + were. + + But don't read any more than 'limit'. + + This is a good thing to call after reading an input file to be sure you + didn't leave some input behind, which could mean you didn't properly + interpret the file. +-----------------------------------------------------------------------------*/ + unsigned int bytesRead; + bool eof; + + for (bytesRead = 0, eof = false; !eof && bytesRead < limit;) { + + int rc; + + rc = fgetc(fileP); + + eof = (rc == EOF); + if (!eof) + ++bytesRead; + } + *bytesReadP = bytesRead; +} diff --git a/lib/pnm.h b/lib/pnm.h index ed6983f4..e4bd34a8 100644 --- a/lib/pnm.h +++ b/lib/pnm.h @@ -4,7 +4,8 @@ #ifndef _PNM_H_ #define _PNM_H_ -#include "ppm.h" +#include <netpbm/pm.h> +#include <netpbm/ppm.h> #ifdef __cplusplus extern "C" { @@ -28,7 +29,9 @@ typedef pixval xelval; /* Declarations of routines. */ -void pnm_init ARGS(( int* argcP, char* argv[] )); +void +pnm_init(int * const argcP, + char ** const argv); void pnm_nextimage(FILE *file, int * const eofP); @@ -36,7 +39,7 @@ pnm_nextimage(FILE *file, int * const eofP); xel * pnm_allocrow(unsigned int const cols); -#define pnm_freerow(xelrow) free(xelrow) +#define pnm_freerow(xelrow) pm_freerow(xelrow) #define pnm_allocarray( cols, rows ) \ ((xel**) pm_allocarray( cols, rows, sizeof(xel) )) @@ -83,12 +86,12 @@ pnm_writepnminit(FILE * const fileP, int const forceplain); void -pnm_writepnmrow(FILE * const fileP, - xel * const xelrow, - int const cols, - xelval const maxval, - int const format, - int const forceplain); +pnm_writepnmrow(FILE * const fileP, + const xel * const xelrow, + int const cols, + xelval const maxval, + int const format, + int const forceplain); void pnm_writepnm(FILE * const fileP, @@ -100,13 +103,13 @@ pnm_writepnm(FILE * const fileP, int const forceplain); xel -pnm_backgroundxel (xel** xels, int cols, int rows, xelval maxval, int format); +pnm_backgroundxel(xel** xels, int cols, int rows, xelval maxval, int format); xel -pnm_backgroundxelrow (xel* xelrow, int cols, xelval maxval, int format); +pnm_backgroundxelrow(xel* xelrow, int cols, xelval maxval, int format); xel -pnm_whitexel (xelval maxval, int format); +pnm_whitexel(xelval maxval, int format); xel pnm_blackxel(xelval maxval, int format); @@ -127,6 +130,11 @@ pixel pnm_xeltopixel(xel const inputxel, int const format); +xel +pnm_parsecolorxel(const char * const colorName, + xelval const maxval, + int const format); + #ifdef __cplusplus } #endif diff --git a/lib/ppm.h b/lib/ppm.h index 033330b9..0695295f 100644 --- a/lib/ppm.h +++ b/lib/ppm.h @@ -3,7 +3,8 @@ #ifndef _PPM_H_ #define _PPM_H_ -#include "pgm.h" +#include <netpbm/pm.h> +#include <netpbm/pgm.h> #ifdef __cplusplus extern "C" { @@ -50,7 +51,7 @@ typedef struct { #define PPM_TYPE PPM_FORMAT -#include "ppmcmap.h" +#include <netpbm/ppmcmap.h> /* Macro for turning a format number into a type number. */ @@ -58,7 +59,22 @@ typedef struct { ((f) == PPM_FORMAT || (f) == RPPM_FORMAT ? PPM_TYPE : PGM_FORMAT_TYPE(f)) -/* Declarations of routines. */ +static __inline__ pixel +ppm_whitepixel(pixval maxval) { + + pixel retval; + PPM_ASSIGN(retval, maxval, maxval, maxval); + + return retval; +} + +static __inline__ pixel +ppm_blackpixel(void) { + + pixel const retval = {0, 0, 0}; + + return retval; +} void ppm_init(int * argcP, char* argv[]); @@ -70,7 +86,7 @@ ppm_allocrow(unsigned int const cols); #define ppm_freearray(pixels, rows) pm_freearray((char**) pixels, rows) -#define ppm_freerow(pixelrow) free(pixelrow); +#define ppm_freerow(pixelrow) pm_freerow(pixelrow); pixel** ppm_readppm(FILE * const fileP, @@ -108,11 +124,11 @@ ppm_writeppminit(FILE* const fileP, int const forceplain); void -ppm_writeppmrow(FILE * const fileP, - pixel * const pixelrow, - int const cols, - pixval const maxval, - int const forceplain); +ppm_writeppmrow(FILE * const fileP, + const pixel * const pixelrow, + int const cols, + pixval const maxval, + int const forceplain); void ppm_check(FILE * const fileP, @@ -259,22 +275,25 @@ ppm_saturation(pixel const p, typedef enum { /* A color from the set of universally understood colors developed - by Brent Berlin and Paul Kay + by Brent Berlin and Paul Kay. + + Algorithms in libnetpbm depend on the numerical representations + of these values being as follows. */ - BKCOLOR_BLACK = 0, - BKCOLOR_GRAY, - BKCOLOR_WHITE, - BKCOLOR_RED, + BKCOLOR_GRAY = 0, + BKCOLOR_BROWN, BKCOLOR_ORANGE, + BKCOLOR_RED, BKCOLOR_YELLOW, BKCOLOR_GREEN, BKCOLOR_BLUE, BKCOLOR_VIOLET, BKCOLOR_PURPLE, - BKCOLOR_BROWN + BKCOLOR_WHITE, + BKCOLOR_BLACK } bk_color; -#define BKCOLOR_COUNT (BKCOLOR_BROWN+1) +#define BKCOLOR_COUNT (BKCOLOR_BLACK+1) bk_color ppm_bk_color_from_color(pixel const color, diff --git a/lib/ppmcmap.h b/lib/ppmcmap.h index b44dcbea..dd3e5c14 100644 --- a/lib/ppmcmap.h +++ b/lib/ppmcmap.h @@ -36,10 +36,10 @@ ppm_computecolorhist2(FILE * const ifp, const int maxcolors, int * const colorsP ); void -ppm_addtocolorhist( colorhist_vector chv, - int * const colorsP, const int maxcolors, - const pixel * const colorP, - const int value, const int position ); +ppm_addtocolorhist(colorhist_vector chv, + int * const colorsP, const int maxcolors, + const pixel * const colorP, + const int value, const int position ); void ppm_freecolorhist(colorhist_vector const chv); @@ -89,13 +89,39 @@ void ppm_freecolorhash(colorhash_table const cht); -colorhash_table ppm_colorrowtocolorhash ARGS((pixel *colorrow, int ncolors)); -pixel * ppm_computecolorrow ARGS((pixel **pixels, int cols, int rows, int maxcolors, int *ncolorsP)); -pixel * ppm_mapfiletocolorrow ARGS((FILE *file, int maxcolors, int *ncolorsP, pixval *maxvalP)); -void ppm_colorrowtomapfile ARGS((FILE *ofp, pixel *colormap, int ncolors, pixval maxval)); -void ppm_sortcolorrow (pixel * const colorrow, const int ncolors, - int (*cmpfunc)(pixel *, pixel *) ); -int ppm_addtocolorrow ARGS((pixel *colorrow, int *ncolorsP, int maxcolors, pixel *pixelP)); +colorhash_table +ppm_colorrowtocolorhash(pixel * const colorrow, + int const ncolors); + +pixel * +ppm_computecolorrow(pixel ** const pixels, + int const cols, + int const rows, + int const maxcolors, + int * const ncolorsP); + +pixel * +ppm_mapfiletocolorrow(FILE * const fileP, + int const maxcolors, + int * const ncolorsP, + pixval * const maxvalP); + +void +ppm_colorrowtomapfile(FILE * const ofP, + pixel * const colormap, + int const ncolors, + pixval const maxval); + +void +ppm_sortcolorrow(pixel * const colorrow, + int const ncolors, + int (*cmpfunc)(pixel *, pixel *)); + +int +ppm_addtocolorrow(pixel * const colorrow, + int * const ncolorsP, + int const maxcolors, + pixel * const pixelP); int ppm_findclosestcolor(const pixel * const colormap, diff --git a/lib/ppmdraw.h b/lib/ppmdraw.h index 7441cc43..d7a02e79 100644 --- a/lib/ppmdraw.h +++ b/lib/ppmdraw.h @@ -16,15 +16,33 @@ extern "C" { #endif +typedef struct { + int x; + int y; +} ppmd_point; + +static __inline__ ppmd_point +ppmd_makePoint(int const x, + int const y) { + + ppmd_point p; + + p.x = x; + p.y = y; + + return p; +} + +void +ppmd_validateCoord(int const c); + +void +ppmd_validatePoint(ppmd_point const p); + typedef enum { PPMD_PATHLEG_LINE } ppmd_pathlegtype; -typedef struct { - unsigned int x; - unsigned int y; -} ppmd_point; - struct ppmd_linelegparms { ppmd_point end; }; @@ -59,8 +77,11 @@ typedef struct { -typedef void ppmd_drawproc(pixel ** const, int const, int const, pixval const, int const, int const, const void *const); +typedef void ppmd_drawprocp(pixel **, unsigned int, unsigned int, + pixval, ppmd_point, const void *); +typedef void ppmd_drawproc(pixel **, int, int, pixval, int, int, const void *); +ppmd_drawprocp ppmd_point_drawprocp; ppmd_drawproc ppmd_point_drawproc; /* @@ -111,6 +132,18 @@ ppmd_setlineclip(int const clip); */ void +ppmd_linep(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const p0, + ppmd_point const p1, + ppmd_drawprocp drawProc, + const void * const clientdata); + + /* Draws a line from p0 to p1. */ + +void ppmd_line(pixel** const pixels, int const cols, int const rows, @@ -121,8 +154,23 @@ ppmd_line(pixel** const pixels, int const y1, ppmd_drawproc drawproc, const void * const clientdata); -/* Draws a line from (x0, y0) to (x1, y1). -*/ + /* Draws a line from (x0, y0) to (x1, y1). */ + +void +ppmd_spline3p(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const p0, + ppmd_point const p1, + ppmd_point const p2, + ppmd_drawprocp drawProc, + const void * const clientdata); + + /* Draws a three-point spline from p0 to p2, with p1 + as the control point. All drawing is done via ppmd_linep(), + so the routines that control it control ppmd_spline3p() as well. + */ void ppmd_spline3(pixel ** const pixels, @@ -137,9 +185,22 @@ ppmd_spline3(pixel ** const pixels, int const y2, ppmd_drawproc drawproc, const void * const clientdata); - /* Draws a three-point spline from (x0, y0) to (x2, y2), with (x1, - y1) as the control point. All drawing is done via ppmd_line(), - so the routines that control it control ppmd_spline3() as well. + +void +ppmd_polysplinep(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const p0, + unsigned int const nc, + ppmd_point * const c, + ppmd_point const p1, + ppmd_drawprocp drawProc, + const void * const clientdata); + + /* Draws a bunch of splines end to end. p0 and p1 are the initial and + final points, and the c[] are the intermediate control points. nc is + the number of these control points. */ void @@ -156,32 +217,29 @@ ppmd_polyspline(pixel ** const pixels, int const y1, ppmd_drawproc drawProc, const void * const clientdata); - /* Draws a bunch of splines end to end. (x0, y0) and (x1, y1) are - the initial and final points, and the xc and yc are the - intermediate control points. nc is the number of these control - points. - */ void -ppmd_spline4(pixel ** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const x0, - int const y0, - int const x1, - int const y1, - int const x2, - int const y2, - int const x3, - int const y3, - ppmd_drawproc drawproc, - const void * const clientdata); - /* Draws a four-point spline from (x0, y0) to (x3, y3), with (x1, - y1) and (x2, y2) as the control points. All drawing is done - via ppmd_line(), so the routines that control it control this - as well. - */ +ppmd_spline4p(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const endPt0, + ppmd_point const endPt1, + ppmd_point const ctlPt0, + ppmd_point const ctlPt1, + ppmd_drawprocp drawproc, + const void * const clientdata); + +void +ppmd_circlep(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const center, + unsigned int const radius, + ppmd_drawprocp drawProc, + const void * const clientData); + /* Draws a circle centered at 'center' with radius 'radius' */ void ppmd_circle(pixel ** const pixels, @@ -193,7 +251,6 @@ ppmd_circle(pixel ** const pixels, int const radius, ppmd_drawproc drawProc, const void * const clientdata); - /* Draws a circle centered at (cx, cy) with the specified radius. */ /* Simple filling routines. */ @@ -242,16 +299,25 @@ char * ppmd_fill_init(void); void -ppmd_fill_drawproc(pixel ** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const x, - int const y, - const void * const clientdata); +ppmd_fill_drawprocp(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ppmd_point const p, + const void * const clientdata); /* Use this drawproc to trace the outline you want filled. Use the fillhandle as the clientdata. */ + +void +ppmd_fill_drawproc(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x, + int const y, + const void * const clientdata); + void ppmd_fill(pixel ** const pixels, int const cols, @@ -269,17 +335,29 @@ ppmd_fill(pixel ** const pixels, /* Text drawing routines. */ void -ppmd_text(pixel** const pixels, - int const cols, - int const rows, - pixval const maxval, - int const xpos, - int const ypos, - int const height, - int const angle, - const char * const sArg, - ppmd_drawproc, - const void* const clientdata); +ppmd_textp(pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_point const pos, + int const height, + int const angle, + const char * const sArg, + ppmd_drawprocp drawProc, + const void * const clientdata); + +void +ppmd_text(pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const xpos, + int const ypos, + int const height, + int const angle, + const char * const sArg, + ppmd_drawproc drawProc, + const void * const clientdata); /* Draws the null-terminated string 's' left justified at the point ('x', 'y'). The text will be 'height' pixels high and will be aligned on a baseline inclined 'angle' degrees with the X axis. The supplied diff --git a/lib/ppmfloyd.h b/lib/ppmfloyd.h index e16ad651..264fc0b6 100644 --- a/lib/ppmfloyd.h +++ b/lib/ppmfloyd.h @@ -15,17 +15,17 @@ struct ppm_fs_info { /* thisXerr and nextXerr are dynamically allocated arrays each of whose dimension is the width of the image plus 2 */ - long *thisrederr; - long *thisgreenerr; - long *thisblueerr; - long *nextrederr; - long *nextgreenerr; - long *nextblueerr; + long * thisrederr; + long * thisgreenerr; + long * thisblueerr; + long * nextrederr; + long * nextgreenerr; + long * nextblueerr; int lefttoright; int cols; pixval maxval; int flags; - pixel *pixrow; + pixel * pixrow; int col_end; pixval red, green, blue; }; @@ -37,7 +37,9 @@ typedef struct ppm_fs_info ppm_fs_info; #define FS_ALTERNATE 0x02 ppm_fs_info * -ppm_fs_init(int cols, pixval maxval, int flags); +ppm_fs_init(unsigned int const cols, + pixval const maxval, + unsigned int const flags); void ppm_fs_free(ppm_fs_info *fi); diff --git a/lib/util/Makefile b/lib/util/Makefile index 8f461f28..8b3fa1c1 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -5,25 +5,23 @@ endif SUBDIR = lib/util VPATH=.:$(SRCDIR)/$(SUBDIR) -include $(BUILDDIR)/Makefile.config - -INCLUDES = -I $(BUILDDIR) -I $(SRCDIR)/$(SUBDIR)/.. +include $(BUILDDIR)/config.mk # nstring is required for asprintf(), etc. Also some systems don't have # snprintf(), e.g. Solaris 2.5.1. 2002.03.29. -UTILOBJECTS = shhopt.o nstring.o filename.o +UTILOBJECTS = shhopt.o nstring.o vasprintf.o filename.o nsleep.o MERGE_OBJECTS = all: $(UTILOBJECTS) -include $(SRCDIR)/Makefile.common +include $(SRCDIR)/common.mk + +INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc $(UTILOBJECTS):%.o:%.c importinc - $(CC) -c $(INCLUDES) -DNDEBUG $(CFLAGS) $(CFLAGS_SHLIB) \ + $(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) $(CFLAGS_SHLIB) \ $(CFLAGS_PERSONAL) $(CADD) -o $@ $< testnstring: test.c nstring.h nstring.o $(CC) $(CFLAGS) $(CADD) -o $@ nstring.o $< - -include Makefile.depend diff --git a/lib/util/bitarith.h b/lib/util/bitarith.h new file mode 100644 index 00000000..4ed6c4c3 --- /dev/null +++ b/lib/util/bitarith.h @@ -0,0 +1,42 @@ +#ifndef BITARITH_H_INCLUDED +#define BITARITH_H_INCLUDED + +#include "pm_config.h" + +static __inline__ unsigned char +pm_byteLeftBits(unsigned char const x, + unsigned int const n) { +/*---------------------------------------------------------------------------- + Clear rightmost (8-n) bits, retain leftmost (=high) n bits. + + Return arbitrary value if n > 8. +-----------------------------------------------------------------------------*/ + unsigned char retval; + + retval = x; + retval >>= (8-n); + retval <<= (8-n); + + return retval; +} + + + +static __inline__ unsigned char +pm_byteRightBits(unsigned char const x, + unsigned int const n){ +/*---------------------------------------------------------------------------- + Return rightmost (=low) n bits of x. + + Return arbitrary value if n > 8. +-----------------------------------------------------------------------------*/ + unsigned char retval; + + retval = x; + retval <<= (8-n); + retval >>= (8-n); + + return retval; +} + +#endif diff --git a/lib/util/floatcode.h b/lib/util/floatcode.h new file mode 100644 index 00000000..99aec256 --- /dev/null +++ b/lib/util/floatcode.h @@ -0,0 +1,185 @@ +#ifndef FLOATCODE_H_INCLUDED +#define FLOATCODE_H_INCLUDED + +#include "pm_config.h" /* BYTE_ORDER */ + +unsigned int const pm_byteOrder = BYTE_ORDER; + + +typedef struct { +/*---------------------------------------------------------------------------- + This is a big-endian representation of a 32 bit floating point number. + I.e. bytes[0] contains the sign bit, etc. + + On a big-endian machines, this is bit for bit identical to 'float'. + On a little-endian machine, it isn't. + + This is an important data type because decent file formats use + big-endian -- they don't care if some CPU happens to use some other + code for its own work. +-----------------------------------------------------------------------------*/ + unsigned char bytes[4]; +} pm_bigendFloat; + + +static __inline__ float +pm_floatFromBigendFloat(pm_bigendFloat const arg) { + + uint32_t retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + pm_bigendFloat bigend; + float native; + } converter; + + converter.bigend = arg; + + retval = converter.native; + }; break; + case LITTLE_ENDIAN: { + union { + unsigned char bytes[4]; + float native; + } converter; + + converter.bytes[0] = arg.bytes[3]; + converter.bytes[1] = arg.bytes[2]; + converter.bytes[2] = arg.bytes[1]; + converter.bytes[3] = arg.bytes[0]; + + retval = converter.native; + } break; + } + return retval; +} + + + +static __inline__ pm_bigendFloat +pm_bigendFloatFromFloat(float const arg) { + + pm_bigendFloat retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + pm_bigendFloat bigend; + float native; + } converter; + + converter.native = arg; + + retval = converter.bigend; + } break; + case LITTLE_ENDIAN: { + union { + unsigned char bytes[4]; + float native; + } converter; + + converter.native = arg; + + retval.bytes[0] = converter.bytes[3]; + retval.bytes[1] = converter.bytes[2]; + retval.bytes[2] = converter.bytes[1]; + retval.bytes[3] = converter.bytes[0]; + } break; + } + return retval; +} + +typedef struct { +/*---------------------------------------------------------------------------- + This is a big-endian representation of a 64 bit floating point number. + I.e. bytes[0] contains the sign bit, etc. + + On a big-endian machines, this is bit for bit identical to 'float'. + On a little-endian machine, it isn't. + + This is an important data type because decent file formats use + big-endian -- they don't care if some CPU happens to use some other + code for its own work. +-----------------------------------------------------------------------------*/ + unsigned char bytes[8]; +} pm_bigendDouble; + + +static __inline__ double +pm_doubleFromBigendDouble(pm_bigendDouble const arg) { + + uint32_t retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + pm_bigendDouble bigend; + double native; + } converter; + + converter.bigend = arg; + + retval = converter.native; + }; break; + case LITTLE_ENDIAN: { + union { + unsigned char bytes[4]; + double native; + } converter; + + converter.bytes[0] = arg.bytes[7]; + converter.bytes[1] = arg.bytes[6]; + converter.bytes[2] = arg.bytes[5]; + converter.bytes[3] = arg.bytes[4]; + converter.bytes[4] = arg.bytes[3]; + converter.bytes[5] = arg.bytes[2]; + converter.bytes[6] = arg.bytes[1]; + converter.bytes[7] = arg.bytes[0]; + + retval = converter.native; + } break; + } + return retval; +} + + + +static __inline__ pm_bigendDouble +pm_bigendDoubleFromDouble(double const arg) { + + pm_bigendDouble retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + pm_bigendDouble bigend; + double native; + } converter; + + converter.native = arg; + + retval = converter.bigend; + } break; + case LITTLE_ENDIAN: { + union { + unsigned char bytes[4]; + double native; + } converter; + + converter.native = arg; + + retval.bytes[0] = converter.bytes[7]; + retval.bytes[1] = converter.bytes[6]; + retval.bytes[2] = converter.bytes[5]; + retval.bytes[3] = converter.bytes[4]; + retval.bytes[4] = converter.bytes[3]; + retval.bytes[5] = converter.bytes[2]; + retval.bytes[6] = converter.bytes[1]; + retval.bytes[7] = converter.bytes[0]; + } break; + } + return retval; +} + +#endif diff --git a/lib/util/intcode.h b/lib/util/intcode.h index 4d9c83aa..dd42f669 100644 --- a/lib/util/intcode.h +++ b/lib/util/intcode.h @@ -1,7 +1,78 @@ #ifndef INTCODE_H_INCLUDED #define INTCODE_H_INCLUDED -#include "pm_config.h" /* For uint32_t, BYTE_ORDER */ +#include "pm_config.h" /* For uint32_t, BYTE_ORDER, HAVE_INT64 */ + +static unsigned int const pm_byteOrder = BYTE_ORDER; + +typedef struct { +/*---------------------------------------------------------------------------- + This is a big-endian representation of a 16 bit integer. I.e. + bytes[0] is the most significant 8 bits; bytes[1] is the least + significant 8 bits of the number in pure binary. + + On a big-endian machines, this is bit for bit identical to uint16_t. + On a little-endian machine, it isn't. + + This is an important data type because decent file formats use + big-endian -- they don't care if some CPU happens to use some other + code for its own work. +-----------------------------------------------------------------------------*/ + unsigned char bytes[2]; +} bigend16; + + +static __inline__ uint16_t +pm_uintFromBigend16(bigend16 const arg) { + + uint16_t retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + bigend16 bigend; + uint16_t native; + } converter; + converter.bigend = arg; + retval = converter.native; + } break; + + default: { + retval = + (arg.bytes[0] << 8) | + (arg.bytes[1] << 0); + } + } + return retval; +} + + + +static __inline__ bigend16 +pm_bigendFromUint16(uint16_t const arg) { + + bigend16 retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + bigend16 bigend; + uint16_t native; + } converter; + converter.native = arg; + retval = converter.bigend; + } break; + + default: { + uint16_t shift; + shift = arg; + retval.bytes[1] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[0] = shift; /* Takes lower 8 bits */ + } + } + return retval; +} typedef struct { /*---------------------------------------------------------------------------- @@ -20,9 +91,6 @@ typedef struct { } bigend32; -unsigned int const pm_byteOrder = BYTE_ORDER; - - static __inline__ uint32_t pm_uintFromBigend32(bigend32 const arg) { @@ -34,19 +102,30 @@ pm_uintFromBigend32(bigend32 const arg) { bigend32 bigend; uint32_t native; } converter; - converter.bigend = arg; - retval = converter.native; - }; break; + } break; + +#if HAVE_GCC_BSWAP case LITTLE_ENDIAN: { + /* Use GCC built-in */ + union { + bigend32 bigend; + uint32_t native; + } converter; + converter.bigend = arg; + retval = __builtin_bswap32(converter.native); + } break; +#endif + + default: retval = (arg.bytes[0] << 24) | (arg.bytes[1] << 16) | (arg.bytes[2] << 8) | (arg.bytes[3] << 0); - } break; } + return retval; } @@ -63,12 +142,23 @@ pm_bigendFromUint32(uint32_t const arg) { bigend32 bigend; uint32_t native; } converter; - converter.native = arg; - retval = converter.bigend; } break; + +#if HAVE_GCC_BSWAP case LITTLE_ENDIAN: { + /* Use GCC built-in */ + union { + bigend32 bigend; + uint32_t native; + } converter; + converter.native = __builtin_bswap32(arg); + retval = converter.bigend; + } break; +#endif + + default: { uint32_t shift; shift = arg; retval.bytes[3] = shift; /* Takes lower 8 bits */ @@ -78,9 +168,127 @@ pm_bigendFromUint32(uint32_t const arg) { retval.bytes[1] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[0] = shift; /* Takes lower 8 bits */ + } + } + return retval; +} + + +#if HAVE_INT64 + +typedef struct { +/*---------------------------------------------------------------------------- + This is a big-endian representation of a 64 bit integer. I.e. + bytes[0] is the most significant 8 bits; bytes[7] is the least + significant 8 bits of the number in pure binary. + + On a big-endian machines, this is bit for bit identical to uint64_t. + On a little-endian machine, it isn't. + + This is an important data type because decent file formats use + big-endian -- they don't care if some CPU happens to use some other + code for its own work. + + uint64_t is supported only when available. +-----------------------------------------------------------------------------*/ + unsigned char bytes[8]; +} bigend64; + + +static __inline__ uint64_t +pm_uintFromBigend64(bigend64 const arg) { + + uint64_t retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + bigend64 bigend; + uint64_t native; + } converter; + converter.bigend = arg; + retval = converter.native; } break; + +#if HAVE_GCC_BSWAP + case LITTLE_ENDIAN: { + /* Use GCC built-in */ + union { + bigend64 bigend; + uint64_t native; + } converter; + converter.bigend = arg; + retval = __builtin_bswap64(converter.native); + } break; +#endif + default: + retval = + ((uint64_t)arg.bytes[0] << 56) | ((uint64_t)arg.bytes[1] << 48) | + ((uint64_t)arg.bytes[2] << 40) | ((uint64_t)arg.bytes[3] << 32) | + ((uint64_t)arg.bytes[4] << 24) | ((uint64_t)arg.bytes[5] << 16) | + ((uint64_t)arg.bytes[6] << 8) | ((uint64_t)arg.bytes[7] << 0); } return retval; } + + +static __inline__ bigend64 +pm_bigendFromUint64(uint64_t const arg) { + + bigend64 retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + bigend64 bigend; + uint64_t native; + } converter; + converter.native = arg; + retval = converter.bigend; + } break; + +#if HAVE_GCC_BSWAP + case LITTLE_ENDIAN: { + + /* Use GCC built-in */ + union { + bigend64 bigend; + uint64_t native; + } converter; + converter.native = __builtin_bswap64(arg); + retval = converter.bigend; + } break; #endif + + default: { + uint64_t shift; + shift = arg; + retval.bytes[7] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[6] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[5] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[4] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[3] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[2] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[1] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[0] = shift; /* Takes lower 8 bits */ + } + } + return retval; +} + +#endif /* HAVE_INT64 */ + +#endif + + + + + diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h index a26d007b..1f2be127 100644 --- a/lib/util/mallocvar.h +++ b/lib/util/mallocvar.h @@ -57,11 +57,22 @@ static __inline__ void reallocProduct(void ** const blockP, unsigned int const factor1, unsigned int const factor2) { + + void * const oldBlockP = *blockP; + + void * newBlockP; if (UINT_MAX / factor2 < factor1) - *blockP = NULL; + newBlockP = NULL; else - *blockP = realloc(*blockP, factor1 * factor2); + newBlockP = realloc(oldBlockP, factor1 * factor2); + + if (newBlockP) + *blockP = newBlockP; + else { + free(oldBlockP); + *blockP = NULL; + } } @@ -72,10 +83,12 @@ reallocProduct(void ** const blockP, arrayName = array; \ } while (0) -#define REALLOCARRAY(arrayName, nElements) { \ +#define REALLOCARRAY(arrayName, nElements) do { \ void * array; \ array = arrayName; \ reallocProduct(&array, nElements, sizeof(arrayName[0])); \ + if (!array) \ + free(arrayName); \ arrayName = array; \ } while (0) diff --git a/lib/util/nsleep.c b/lib/util/nsleep.c new file mode 100644 index 00000000..943b8c77 --- /dev/null +++ b/lib/util/nsleep.c @@ -0,0 +1,27 @@ +#ifdef WIN32 + #include <windows.h> + #include <process.h> +#else + #include <unistd.h> +#endif + +#include "nsleep.h" + + + +void +sleepN(unsigned int const milliseconds) { + +#ifdef WIN32 + SleepEx(milliseconds, TRUE); +#else + + /* We could use usleep() here if millisecond resolution is really + important, but since Netpbm has no need for it today, we don't + want to deal with the possibility that usleep() doesn't exist. + 08.08.01. + */ + + sleep((milliseconds + 999)/1000); +#endif +} diff --git a/lib/util/nsleep.h b/lib/util/nsleep.h new file mode 100644 index 00000000..372b8008 --- /dev/null +++ b/lib/util/nsleep.h @@ -0,0 +1,7 @@ +#ifndef NSLEEP_H_INCLUDED +#define NSLEEP_H_INCLUDED + +void +sleepN(unsigned int const milliseconds); + +#endif diff --git a/lib/util/nstring.c b/lib/util/nstring.c index 702a3c44..0fa78c7a 100644 --- a/lib/util/nstring.c +++ b/lib/util/nstring.c @@ -740,15 +740,6 @@ const char * const strsol = "NO MEMORY TO CREATE STRING!"; -/* We would like to have vasprintfN(), but it is difficult because you - can't run through a va_list twice, which we would want to do: once - to measure the length; once actually to build the string. On some - machines, you can simply make two copies of the va_list variable in - normal C fashion, but on others you need va_copy, which is a - relatively recent invention. In particular, the simple va_list copy - failed on an AMD64 Gcc Linux system in March 2006. -*/ - void PM_GNU_PRINTF_ATTR(2,3) asprintfN(const char ** const resultP, const char * const fmt, @@ -887,20 +878,80 @@ stripeq(const char * const comparand, -const char * -memmemN(const char * const haystack, +const void * +memmemN(const void * const haystackArg, size_t const haystacklen, - const char * const needle, + const void * const needleArg, size_t const needlelen) { + const unsigned char * const haystack = haystackArg; + const unsigned char * const needle = needleArg; + /* This does the same as the function of the same name in the GNU C library */ - const char * p; + const unsigned char * p; for (p = haystack; p <= haystack + haystacklen - needlelen; ++p) - if (MEMEQ(p, needle, needlelen)) + if (memeq(p, needle, needlelen)) return p; return NULL; } + + + +bool +strishex(const char * const subject) { + + bool retval; + unsigned int i; + + retval = TRUE; /* initial assumption */ + + for (i = 0; i < strlen(subject); ++i) + if (!ISXDIGIT(subject[i])) + retval = FALSE; + + return retval; +} + + + +void +interpret_uint(const char * const string, + unsigned int * const valueP, + const char ** const errorP) { + + if (string[0] == '\0') + asprintfN(errorP, "Null string."); + else { + /* strtoul() does a bizarre thing where if the number is out + of range, it returns a clamped value but tells you about it + by setting errno = ERANGE. If it is not out of range, + strtoul() leaves errno alone. + */ + char * tail; + unsigned long ulongValue; + + errno = 0; /* So we can tell if strtoul() overflowed */ + + ulongValue = strtoul(string, &tail, 10); + + if (tail[0] != '\0') + asprintfN(errorP, "Non-digit stuff in string: %s", tail); + else if (errno == ERANGE) + asprintfN(errorP, "Number too large"); + else if (ulongValue > UINT_MAX) + asprintfN(errorP, "Number too large"); + else if (string[0] == '-') + asprintfN(errorP, "Negative number"); + /* Sleazy code; string may have leading spaces. */ + else { + *valueP = ulongValue; + *errorP = NULL; + } + } +} + + diff --git a/lib/util/nstring.h b/lib/util/nstring.h index 9ed20051..53f1e4c0 100644 --- a/lib/util/nstring.h +++ b/lib/util/nstring.h @@ -5,6 +5,7 @@ #include <string.h> #include <ctype.h> +#include "pm_c_util.h" #include "pm.h" /* For PM_GNU_PRINTF_ATTR, __inline__ */ #ifdef __cplusplus @@ -14,7 +15,7 @@ extern "C" { } /* to fake out automatic code indenters */ #endif -/* Here is are string functions that respect the size of the array +/* Here are string functions that respect the size of the array into which you are copying -- E.g. STRSCPY truncates the source string as required so that it fits, with the terminating null, in the destination array. @@ -25,22 +26,18 @@ extern "C" { (strncmp((A), (B), sizeof(A))) #define STRSCAT(A,B) \ (strncpy(A+strlen(A), B, sizeof(A)-strlen(A)), *((A)+sizeof(A)-1) = '\0') - -#define STREQ(A, B) \ - (strcmp((A), (B)) == 0) -#define STRNEQ(A, B, C) \ - (strncmp((A), (B), (C)) == 0) -#define STRCASEEQ(A, B) \ - (strcasecmp((A), (B)) == 0) -#define STRNCASEEQ(A, B, C) \ - (strncasecmp((A), (B), (C)) == 0) #define STRSEQ(A, B) \ - (strncmp((A), (B), sizeof(A)) == 0) + (strneq((A), (B), sizeof(A))) + +#define MEMEQ(a,b,c) (memcmp(a, b, c) == 0) + +#define MEMSEQ(a,b) (memeq(a, b, sizeof(*(a))) == 0) + +#define MEMSSET(a,b) (memset(a, b, sizeof(*(a)))) -#define MEMEQ(A, B, C) \ - (memcmp((A), (B), (C)) == 0) -#define MEMSZERO(A) \ - bzero((A), sizeof(A)) +#define MEMSCPY(a,b) (memcpy(a, b, sizeof(*(a)))) + +#define MEMSZERO(a) (MEMSSET(a, 0)) static __inline__ int @@ -50,6 +47,42 @@ streq(const char * const comparand, return strcmp(comparand, comparator) == 0; } +static __inline__ int +strneq(const char * const comparand, + const char * const comparator, + size_t const size) { + + return strncmp(comparand, comparator, size) == 0; +} + +static __inline__ int +memeq(const void * const comparand, + const void * const comparator, + size_t const size) { + + return memcmp(comparand, comparator, size) == 0; +} + +/* The Standard C Library may not declare strcasecmp() if the including + source file doesn't request BSD functions, with _BSD_SOURCE. So + we don't define functions that use strcasecmp() in that case. +*/ +#ifdef _BSD_SOURCE +static __inline__ int +strcaseeq(const char * const comparand, + const char * const comparator) { + + return strcasecmp(comparand, comparator) == 0; +} + +static __inline__ int +strncaseeq(const char * const comparand, + const char * const comparator, + size_t const size) { + + return strncasecmp(comparand, comparator, size) == 0; +} +#endif /* The standard C library routines isdigit(), for some weird @@ -134,6 +167,11 @@ asprintfN(const char ** const resultP, const char * const fmt, ...) PM_GNU_PRINTF_ATTR(2,3); +void +vasprintfN(const char ** const resultP, + const char * const format, + va_list args); + void strfree(const char * const string); @@ -144,12 +182,20 @@ int stripeq(const char * const comparand, const char * const comparator); -const char * -memmemN(const char * const haystack, +const void * +memmemN(const void * const haystackArg, size_t const haystacklen, - const char * const needle, + const void * const needleArg, size_t const needlelen); +bool +strishex(const char * const subject); + +void +interpret_uint(const char * const string, + unsigned int * const valueP, + const char ** const errorP); + #ifdef __cplusplus } #endif diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h index f21a2f82..07913f30 100644 --- a/lib/util/pm_c_util.h +++ b/lib/util/pm_c_util.h @@ -15,6 +15,16 @@ #define ROUND(X) (((X) >= 0) ? (int)((X)+0.5) : (int)((X)-0.5)) #undef ROUNDU #define ROUNDU(X) ((unsigned int)((X)+0.5)) + +/* ROUNDUP rounds up to a specified multiple. E.g. ROUNDUP(22, 8) == 24 */ + +#undef ROUNDUP +#define ROUNDUP(X,M) (((X)+(M)-1)/(M)*(M)) +#undef ROUNDDN +#define ROUNDDN(X,M) ((X)/(M)*(M)) + +#define ROUNDDIV(DIVIDEND,DIVISOR) (((DIVIDEND) + (DIVISOR)/2)/(DIVISOR)) + #undef SQR #define SQR(a) ((a)*(a)) diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c index 7722b5d5..718186fa 100644 --- a/lib/util/shhopt.c +++ b/lib/util/shhopt.c @@ -89,9 +89,13 @@ optStructCount(const optEntry opt[]) return ret; } + + +static int +optMatch(optEntry const opt[], + const char * const s, + int const lng) { /*------------------------------------------------------------------------ - | NAME optMatch - | | FUNCTION Find a matching option. | | INPUT opt array of possible options. @@ -103,35 +107,39 @@ optStructCount(const optEntry opt[]) | DESCRIPTION Short options are matched from the first character in | the given string. */ -static int -optMatch(const optEntry opt[], const char *s, int lng) -{ - int nopt, q, matchlen = 0; - const char *p; - nopt = optStructCount(opt); + unsigned int const nopt = optStructCount(opt); + + unsigned int q; + unsigned int matchlen; + const char * p; + + matchlen = 0; /* initial value */ + if (lng) { if ((p = strchr(s, '=')) != NULL) matchlen = p - s; else matchlen = strlen(s); } - for (q = 0; q < nopt; q++) { + for (q = 0; q < nopt; ++q) { if (lng) { - if (!opt[q].longName) - continue; - if (strncmp(s, opt[q].longName, matchlen) == 0) - return q; + if (opt[q].longName) { + if (strncmp(s, opt[q].longName, matchlen) == 0) + return q; + } } else { - if (!opt[q].shortName) - continue; - if (*s == opt[q].shortName) - return q; + if (opt[q].shortName) { + if (s[0] == opt[q].shortName) + return q; + } } } return -1; } + + /*------------------------------------------------------------------------ | NAME optString | @@ -422,13 +430,17 @@ optExecute(optEntry const opt, char *arg, int lng) case OPT_UINT: case OPT_ULONG: { unsigned long tmp; - char *e; + char * tailPtr; if (arg == NULL) optFatal("internal error: optExecute() called with NULL argument " "'%s'", optString(opt, lng)); - tmp = strtoul(arg, &e, 10); - if (*e) + + if (arg[0] == '-' || arg[1] == '+') + optFatal("unsigned number '%s' has a sign ('%c')", + arg, arg[0]); + tmp = strtoul(arg, &tailPtr, 10); + if (*tailPtr) optFatal("invalid number `%s'", arg); if (errno == ERANGE || (opt.type == OPT_UINT && tmp > UINT_MAX)) @@ -643,7 +655,15 @@ static void parse_short_option_token(char *argv[], const int argc, const int ai, const optEntry opt_table[], int * const tokens_consumed_p) { +/*---------------------------------------------------------------------------- + Parse a cluster of short options, e.g. -walne . + + The last option in the cluster might take an argument, and we parse + that as well. e.g. -cf myfile or -cfmyfile . + argv[] and argc describe the whole program argument set. 'ai' is the + index of the argument that is the short option cluster. +-----------------------------------------------------------------------------*/ char *o; /* A short option character */ char *arg; int mi; /* index into option table */ @@ -685,11 +705,60 @@ parse_short_option_token(char *argv[], const int argc, const int ai, static void -parse_long_option(char *argv[], const int argc, const int ai, - const int namepos, - const optEntry opt_table[], - int * const tokens_consumed_p) { +fatalUnrecognizedLongOption(const char * const optionName, + optEntry const optTable[]) { + + unsigned int const nopt = optStructCount(optTable); + + unsigned int q; + + char optList[1024]; + + optList[0] = '\0'; /* initial value */ + + for (q = 0; + q < nopt && strlen(optList) + 1 <= sizeof(optList); + ++q) { + + const optEntry * const optEntryP = &optTable[q]; + const char * entry; + + if (optEntryP->longName) + asprintfN(&entry, "-%s ", optEntryP->longName); + else + asprintfN(&entry, "-%c ", optEntryP->shortName); + + strncat(optList, entry, sizeof(optList) - strlen(optList) - 1); + + strfree(entry); + + if (strlen(optList) + 1 == sizeof(optList)) { + /* Buffer is full. Overwrite end of list with ellipsis */ + strcpy(&optList[sizeof(optList) - 4], "..."); + } + } + optFatal("unrecognized option '%s'. Recognized options are: %s", + optionName, optList); +} + + + +static void +parse_long_option(char * const argv[], + int const argc, + int const ai, + int const namepos, + optEntry const opt_table[], + int * const tokens_consumed_p) { +/*---------------------------------------------------------------------------- + Parse a long option, e.g. -verbose or --verbose. + + The option might take an argument, and we parse + that as well. e.g. -file=myfile or -file myfile . + argv[] and argc describe the whole program argument set. 'ai' is the + index of the argument that is the long option. +-----------------------------------------------------------------------------*/ char *equals_arg; /* The argument of an option, included in the same token, after a "=". NULL if no "=" in the token. @@ -703,8 +772,8 @@ parse_long_option(char *argv[], const int argc, const int ai, *tokens_consumed_p = 1; /* initial assumption */ /* find matching option */ if ((mi = optMatch(opt_table, &argv[ai][namepos], 1)) < 0) - optFatal("unrecognized option `%s'", argv[ai]); - + fatalUnrecognizedLongOption(argv[ai], opt_table); + /* possibly locate the argument to this option. */ { char *p; @@ -727,7 +796,8 @@ parse_long_option(char *argv[], const int argc, const int ai, } } else { if (equals_arg) - optFatal("option `%s' doesn't allow an argument", + optFatal("option `%s' doesn't allow an argument, but you " + "have specified it in the form name=value", optString(opt_table[mi], 1)); else arg = NULL; diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h index fd15b53c..99096a76 100644 --- a/lib/util/shhopt.h +++ b/lib/util/shhopt.h @@ -1,4 +1,4 @@ -/*============================================================================== +/*============================================================================= HERE IS AN EXAMPLE OF THE USE OF SHHOPT: @@ -6,6 +6,8 @@ HERE IS AN EXAMPLE OF THE USE OF SHHOPT: int main ( int argc, char **argv ) { + // initial values here are just to demonstrate what gets set and + // what doesn't by the code below. int help_flag = 7; unsigned int help_spec =7; unsigned int height_spec =7; @@ -186,12 +188,13 @@ typedef struct { /* OPTENT3 is the same as OPTENTRY except that it also sets the "specified" element of the table entry (so it assumes OPTION_DEF is a table of - optEntry instead of optStruct). + optEntry instead of optStruct). It sets it to the number of times that + the option appears in the command line. Here is an example: unsigned int option_def_index = 0; - optEntry *option_def = malloc(100*sizeof(optEntry)); + MALLOCARRAY_NOFAIL(option_def, 100); OPTENT3('h', "help", OPT_FLAG, &help_flag, 0); OPTENT3(0, "alphaout", OPT_STRING, &alpha_filename, 0); */ @@ -202,6 +205,8 @@ typedef struct { OPTENTRY(shortvalue, longvalue, typevalue, outputvalue, flagvalue) \ } +#define OPTENTINIT OPTION_DEF[0].type = OPT_END + struct optNameValue { const char * name; @@ -227,14 +232,4 @@ optDestroyNameValueList(struct optNameValue * const list); } #endif -/* Here's a hack to help us introduce pm_c_util.h. That should be - #included in nearly every Netpbm program, but we're too lazy to insert - it into hundreds of files right now. But shhopt.h is included into - most of those files, so we #include it here! - - Before 2005.12.03, the stuff that is now in pm_c_util.h was in pm.h, - but that's a bad place for it because pm.h is an external header file. -*/ -#include "pm_c_util.h" - #endif diff --git a/lib/util/vasprintf.c b/lib/util/vasprintf.c new file mode 100644 index 00000000..9d8fe590 --- /dev/null +++ b/lib/util/vasprintf.c @@ -0,0 +1,59 @@ +#define _GNU_SOURCE + /* Due to conditional compilation, this is GNU source only if the C library + is GNU. + */ +#include <stdlib.h> +#include <string.h> + +#include "nstring.h" + +#if defined(__GNUC__) && !defined(__MINGW32__) + #define HAVE_VASPRINTF 1 +#else + #define HAVE_VASPRINTF 0 +#endif + +void +vasprintfN(const char ** const resultP, + const char * const format, + va_list varargs) { + + char * result; + +#if HAVE_VASPRINTF + vasprintf(&result, format, varargs); + + if (result == NULL) + *resultP = strsol; + else + *resultP = result; +#else + /* We have a big compromise here. To do this right, without a + huge amount of work, we need to go through the variable + arguments twice: once to determine how much memory to allocate, + and once to format the string. On some machines, you can + simply make two copies of the va_list variable in normal C + fashion, but on others you need va_copy, which is a relatively + recent invention. In particular, the simple va_list copy + failed on an AMD64 Gcc Linux system in March 2006. + + So instead, we just allocate 4K and truncate or waste as + necessary. + */ + size_t const allocSize = 4096; + result = malloc(allocSize); + + if (result == NULL) + *resultP = strsol; + else { + size_t realLen; + + vsnprintfN(result, allocSize, format, varargs, &realLen); + + if (realLen >= allocSize) + strcpy(result + allocSize - 15, "<<<TRUNCATED"); + + *resultP = result; + } +#endif +} diff --git a/lib/util/wordaccess.h b/lib/util/wordaccess.h index 28963aee..2eaa2b24 100644 --- a/lib/util/wordaccess.h +++ b/lib/util/wordaccess.h @@ -36,28 +36,39 @@ work with that. We also assume that a char is 8 bits. + + HAVE_GCC_BITCOUNT and HAVE_GCC_BSWAP are set in pm_config.h + + BITS_PER_LONG is the number of bits in long int. */ -#if (!defined(WORDACCESS_GENERIC) \ - && defined(__GNUC__) && defined(__GLIBC__) \ - && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) ) - #if BYTE_ORDER==BIG_ENDIAN /* defined by GCC */ +#include "pm_config.h" - #include "wordaccess_gcc3_be.h" +#if (!defined(WORDACCESS_GENERIC) && HAVE_GCC_BITCOUNT ) - #elif defined(__ia64__) || defined(__amd64__) || defined(__x86_64__) - /* all these macros are defined by GCC */ + #if BYTE_ORDER == BIG_ENDIAN /* See pm_config.h */ + /* Sun Sparc 64, etc */ + #include "wordaccess_gcc3_be.h" + #elif (BITS_PER_LONG == 64) + /* AMD Athlon 64, Intel x86_64, Intel Itanium, etc. */ #include "wordaccess_64_le.h" - #else + #elif (BITS_PER_LONG == 32) + /* Intel x86_32 (80386, 80486, Pentium), etc. */ + #include "wordaccess_generic.h" - #include "wordaccess_gcc3_le.h" + #else + /* Extremely rare case. + If long is neither 32 nor 64 bits, (say, 128) it comes here. + */ + #define WORDACCESS_GENERIC + #include "wordaccess_generic.h" #endif #else - + /* Non GCC, GCC prior to v.3.4 or WORDACCESS_GENERIC defined */ #include "wordaccess_generic.h" #endif diff --git a/lib/util/wordaccess_64_le.h b/lib/util/wordaccess_64_le.h index 4bb52b2e..4d148ad2 100644 --- a/lib/util/wordaccess_64_le.h +++ b/lib/util/wordaccess_64_le.h @@ -2,26 +2,21 @@ This file is the part of wordaccess.h for use under these conditions: - * GCC (>=3.4), GLIBC - * 64 bit Little-Endian machines (IA64, X86-64, AMD64) -=============================================================================*/ - -/* - 64 bit hton and ntoh do not exist. Here we use bswap_64. - - While bswap_64 works on 64 bit data, __builtin_clzl works on "long" which - may or may not be 64 bits. Code provided to find the right data type and - file off any extra when necessary. -*/ - -#include <byteswap.h> /* See note above on bswap_64 */ + * GCC (>=3.4) (__builtin_clz appears in GCC 3.4) + * Little-Endian machines (IA64, X86-64, AMD64) + * 64 bit long +=============================================================================*/ + +#include "intcode.h" + typedef uint64_t wordint; typedef unsigned char wordintBytes[sizeof(wordint)]; + static __inline__ wordint -bytesToWordint(wordintBytes bytes) { - return ((wordint) bswap_64(*(wordint *)bytes)); +bytesToWordint(wordintBytes const bytes) { + return (wordint) pm_uintFromBigend64(*(bigend64*)bytes); } @@ -29,24 +24,5 @@ bytesToWordint(wordintBytes bytes) { static __inline__ void wordintToBytes(wordintBytes * const bytesP, wordint const wordInt) { - *(wordint *)bytesP = bswap_64(wordInt); -} - - - -static __inline__ unsigned int -wordintClz(wordint const x){ - - unsigned int s; - - if (x == 0) - return sizeof(wordint) * 8; - - /* Find the data type closest to 64 bits, and file off any extra. */ - else if ((s=sizeof(long int)) >= 8) - return (__builtin_clzl((long int)x << (s - 8) * 8)); - else if ((s=sizeof(long long int)) >= 8) - return (__builtin_clzll((long long int)x << (s - 8) * 8)); - else - pm_error("Long long int is less than 64 bits on this machine"); + *(bigend64*)bytesP = pm_bigendFromUint64(wordInt); } diff --git a/lib/util/wordaccess_gcc3_be.h b/lib/util/wordaccess_gcc3_be.h index 6f5d86fc..5aa63521 100644 --- a/lib/util/wordaccess_gcc3_be.h +++ b/lib/util/wordaccess_gcc3_be.h @@ -4,16 +4,6 @@ * GCC (>=3.4), GLIBC * Big-Endian machines - - __builtin_clz is available on GCC 3.4 and above - - Note that the clz scheme does not work and requires adjustment - if long type does not make use of all bits for data storage. - - This is unlikely. According to GNU MP (http://www.swox.com/gmp/), - in rare cases such as Cray, there are smaller data types that take up - the same space as long, but leave the higher bits silent. Currently, - there are no known such cases for data type long. *===========================================================================*/ typedef unsigned long int wordint; @@ -31,10 +21,3 @@ wordintToBytes(wordintBytes * const bytesP, wordint const wordInt) { *(wordint *)bytesP = wordInt; } - - - -static __inline__ unsigned int -wordintClz(wordint const x) { - return (x==0 ? sizeof(wordint)*8 : __builtin_clzl(x)); -} diff --git a/lib/util/wordaccess_gcc3_le.h b/lib/util/wordaccess_gcc3_le.h deleted file mode 100644 index 7db218db..00000000 --- a/lib/util/wordaccess_gcc3_le.h +++ /dev/null @@ -1,54 +0,0 @@ -/*============================================================================= - - This file is the part of wordaccess.h for use under these - conditions: - - * GCC (>=3.4), GLIBC - * 32 bit Little-Endian machines (intel MPUs 80386, Pentium, etc.) - * Other non-Big-Endian machines (very rare) - -=============================================================================*/ - -typedef uint32_t wordint; -typedef unsigned char wordintBytes[sizeof(wordint)]; - -#include <sys/types.h> -#include <netinet/in.h> - -/* - Here we use the more widely used functions htonl and ntohl instead of - bswap_32. This makes possible the handling of weird byte ordering - (neither Big-Endian nor Little-Endian) schemes, if any. -*/ - -static __inline__ wordint -bytesToWordint(wordintBytes const bytes) { - return (wordint) ntohl(*(wordint *)bytes); -} - - - -static __inline__ void -wordintToBytes(wordintBytes * const bytesP, - wordint const wordInt) { - - *(wordint *)bytesP = htonl(wordInt); -} - - - -static __inline__ unsigned int -wordintClz(wordint const x) { - - /* Find the data type closest to 32 bits, and file off any extra. */ - - if (x == 0) - return sizeof(wordint) * 8; - else if (sizeof(int) >= 4) - return __builtin_clz((int)x << (sizeof(int) - 4) * 8); - else if (sizeof(long int) >= 4) - return __builtin_clzl((long int)x << (sizeof(long int) - 4) * 8); - else - pm_error("Long int is less than 32 bits on this machine"); -} - diff --git a/lib/util/wordaccess_generic.h b/lib/util/wordaccess_generic.h index 7f27ef74..94cc8124 100644 --- a/lib/util/wordaccess_generic.h +++ b/lib/util/wordaccess_generic.h @@ -5,85 +5,25 @@ * Compilers other than GCC * GCC before version 3.4 - * c libraries other than Glibc * Specified by the user with WORDACCESS_GENERIC =============================================================================*/ +#include "intcode.h" + typedef uint32_t wordint; typedef unsigned char wordintBytes[sizeof(wordint)]; + static __inline__ wordint -bytesToWordint(wordintBytes const bytes) { - wordint retval; - unsigned int i; +bytesToWordint(wordintBytes const bytes) { - /* Note that 'bytes' is a pointer, due to C array degeneration. - That means sizeof(bytes) isn't what you think it is. - */ - - for (i = 1, retval = bytes[0]; i < sizeof(wordint); ++i) { - retval = (retval << 8) + bytes[i]; - } - return retval; + return (wordint) pm_uintFromBigend32( * (bigend32*) bytes); } - static __inline__ void wordintToBytes(wordintBytes * const bytesP, - wordint const wordInt) { - - wordint buffer; - int i; - - for (i = sizeof(*bytesP)-1, buffer = wordInt; i >= 0; --i) { - (*bytesP)[i] = buffer & 0xFF; - buffer >>= 8; - } -} - -static unsigned char const clz8[256]= { - 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - - - -static __inline__ unsigned int -clz16(wordint const x) { - if (x >> 8 != 0) - return clz8[x >> 8]; - else - return clz8[x] + 8; -} - - - -static __inline__ unsigned int -clz32(wordint const x) { - if (x >> 16 != 0) - return clz16(x >> 16); - else - return clz16(x) +16; -} - - + wordint const wordInt) { -static __inline__ unsigned int -wordintClz(wordint const x) { - return clz32(x); + * (bigend32*) bytesP = pm_bigendFromUint32((uint32_t)wordInt); } diff --git a/lib/util/wordintclz.h b/lib/util/wordintclz.h new file mode 100644 index 00000000..32e6ade8 --- /dev/null +++ b/lib/util/wordintclz.h @@ -0,0 +1,93 @@ +#ifndef WORDINTCLZ_H_INCLUDED +#define WORDINTCLZ_H_INCLUDED + +#if (!defined(WORDACCESS_GENERIC) && HAVE_GCC_BITCOUNT ) +/* + Compiler is GCC and has __builtin_clz() + wordint is long + + __builtin_clz is available on GCC 3.4 and above + + Note that the clz scheme does not work and requires adjustment + if long type does not make use of all bits for data storage. + + This is unlikely. According to GNU MP (http://www.swox.com/gmp/), + in rare cases such as Cray, there are smaller data types that take up + the same space as long, but leave the higher bits silent. + Currently, there are no known such cases for data type long. + */ + +static __inline__ unsigned int +wordintClz(wordint const x){ + + assert(sizeof(unsigned long int) == sizeof(wordint)); + + if (x == 0) + return sizeof(wordint) * 8; + else + return (__builtin_clzl( (unsigned long int) x )); +} + +#else + +/* wordint is uint32_t: exactly 32 bits wide */ + +static unsigned char const clz8[256]= { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + + +static __inline__ unsigned int +clz16(wordint const x) { + + if (x >> 8 != 0) + return clz8[x >> 8]; + else + return clz8[x] + 8; +} + + + +static __inline__ unsigned int +clz32(wordint const x) { + + if (x >> 16 != 0) + return clz16(x >> 16); + else + return clz16(x) + 16; +} + + + +static __inline__ unsigned int +wordintClz(wordint const x) { + + assert(sizeof(wordint) == 4); + + return clz32(x); +} + +/* Another way to calculate leading zeros: + x == 0 ? 32 : 31 - floor(log(x)/log(2)) + (Beware: insufficient precision will cause errors) +*/ + +#endif + +#endif |