From 1fd361a1ea06e44286c213ca1f814f49306fdc43 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sat, 19 Aug 2006 03:12:28 +0000 Subject: Create Subversion repository git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- lib/Makefile | 278 ++++ lib/bitio.c | 207 +++ lib/bitio.h | 89 ++ lib/colorname.c | 208 +++ lib/colorname.h | 43 + lib/compile.h | 5 + lib/fileio.c | 170 +++ lib/fileio.h | 22 + lib/libpam.c | 1098 ++++++++++++++ lib/libpam.h | 17 + lib/libpamcolor.c | 102 ++ lib/libpammap.c | 662 +++++++++ lib/libpamn.c | 542 +++++++ lib/libpamread.c | 277 ++++ lib/libpamwrite.c | 397 +++++ lib/libpbm.h | 12 + lib/libpbm1.c | 62 + lib/libpbm2.c | 180 +++ lib/libpbm3.c | 282 ++++ lib/libpbmfont.c | 1136 +++++++++++++++ lib/libpbmvms.c | 734 ++++++++++ lib/libpgm.h | 24 + lib/libpgm1.c | 324 +++++ lib/libpgm2.c | 226 +++ lib/libpm.c | 1494 +++++++++++++++++++ lib/libpnm1.c | 222 +++ lib/libpnm2.c | 128 ++ lib/libpnm3.c | 393 +++++ lib/libppm.h | 15 + lib/libppm1.c | 328 +++++ lib/libppm2.c | 208 +++ lib/libppmcmap.c | 650 +++++++++ lib/libppmcolor.c | 710 +++++++++ lib/libppmd.c | 1177 +++++++++++++++ lib/libppmfloyd.c | 270 ++++ lib/libppmfuzzy.c | 434 ++++++ lib/libsystem.c | 319 +++++ lib/libsystem_dummy.c | 47 + lib/lum.h | 123 ++ lib/mkstemp.c | 8 + lib/pam.h | 490 +++++++ lib/pammap.h | 124 ++ lib/path.c | 468 ++++++ lib/pbm.h | 97 ++ lib/pbmfont.h | 75 + lib/pgm.h | 136 ++ lib/pm.h | 346 +++++ lib/pm_gamma.h | 68 + lib/pm_system.h | 43 + lib/pnm.h | 134 ++ lib/ppm.h | 300 ++++ lib/ppmcmap.h | 111 ++ lib/ppmdfont.c | 133 ++ lib/ppmdfont.h | 74 + lib/ppmdraw.h | 303 ++++ lib/ppmfloyd.h | 67 + lib/rgb.txt | 881 ++++++++++++ lib/standard.ppmdfont | Bin 0 -> 3899 bytes lib/standardppmdfont.c | 3177 +++++++++++++++++++++++++++++++++++++++++ lib/util/LICENSE.txt | 121 ++ lib/util/Makefile | 29 + lib/util/bitreverse.h | 46 + lib/util/filename.c | 26 + lib/util/filename.h | 7 + lib/util/intcode.h | 86 ++ lib/util/lexheader | 9 + lib/util/mallocvar.h | 107 ++ lib/util/nstring.c | 906 ++++++++++++ lib/util/nstring.h | 157 ++ lib/util/pm_c_util.h | 62 + lib/util/shhopt-1.1.6.lsm | 16 + lib/util/shhopt.README | 200 +++ lib/util/shhopt.c | 939 ++++++++++++ lib/util/shhopt.h | 240 ++++ lib/util/testnstring.c | 30 + lib/util/wordaccess.h | 65 + lib/util/wordaccess_64_le.h | 52 + lib/util/wordaccess_gcc3_be.h | 40 + lib/util/wordaccess_gcc3_le.h | 54 + lib/util/wordaccess_generic.h | 89 ++ 80 files changed, 23931 insertions(+) create mode 100644 lib/Makefile create mode 100644 lib/bitio.c create mode 100644 lib/bitio.h create mode 100644 lib/colorname.c create mode 100644 lib/colorname.h create mode 100644 lib/compile.h create mode 100644 lib/fileio.c create mode 100644 lib/fileio.h create mode 100644 lib/libpam.c create mode 100644 lib/libpam.h create mode 100644 lib/libpamcolor.c create mode 100644 lib/libpammap.c create mode 100644 lib/libpamn.c create mode 100644 lib/libpamread.c create mode 100644 lib/libpamwrite.c create mode 100644 lib/libpbm.h create mode 100644 lib/libpbm1.c create mode 100644 lib/libpbm2.c create mode 100644 lib/libpbm3.c create mode 100644 lib/libpbmfont.c create mode 100644 lib/libpbmvms.c create mode 100644 lib/libpgm.h create mode 100644 lib/libpgm1.c create mode 100644 lib/libpgm2.c create mode 100644 lib/libpm.c create mode 100644 lib/libpnm1.c create mode 100644 lib/libpnm2.c create mode 100644 lib/libpnm3.c create mode 100644 lib/libppm.h create mode 100644 lib/libppm1.c create mode 100644 lib/libppm2.c create mode 100644 lib/libppmcmap.c create mode 100644 lib/libppmcolor.c create mode 100644 lib/libppmd.c create mode 100644 lib/libppmfloyd.c create mode 100644 lib/libppmfuzzy.c create mode 100644 lib/libsystem.c create mode 100644 lib/libsystem_dummy.c create mode 100644 lib/lum.h create mode 100644 lib/mkstemp.c create mode 100644 lib/pam.h create mode 100644 lib/pammap.h create mode 100644 lib/path.c create mode 100644 lib/pbm.h create mode 100644 lib/pbmfont.h create mode 100644 lib/pgm.h create mode 100644 lib/pm.h create mode 100644 lib/pm_gamma.h create mode 100644 lib/pm_system.h create mode 100644 lib/pnm.h create mode 100644 lib/ppm.h create mode 100644 lib/ppmcmap.h create mode 100644 lib/ppmdfont.c create mode 100644 lib/ppmdfont.h create mode 100644 lib/ppmdraw.h create mode 100644 lib/ppmfloyd.h create mode 100644 lib/rgb.txt create mode 100644 lib/standard.ppmdfont create mode 100644 lib/standardppmdfont.c create mode 100644 lib/util/LICENSE.txt create mode 100644 lib/util/Makefile create mode 100644 lib/util/bitreverse.h create mode 100644 lib/util/filename.c create mode 100644 lib/util/filename.h create mode 100644 lib/util/intcode.h create mode 100644 lib/util/lexheader create mode 100644 lib/util/mallocvar.h create mode 100644 lib/util/nstring.c create mode 100644 lib/util/nstring.h create mode 100644 lib/util/pm_c_util.h create mode 100644 lib/util/shhopt-1.1.6.lsm create mode 100644 lib/util/shhopt.README create mode 100644 lib/util/shhopt.c create mode 100644 lib/util/shhopt.h create mode 100644 lib/util/testnstring.c create mode 100644 lib/util/wordaccess.h create mode 100644 lib/util/wordaccess_64_le.h create mode 100644 lib/util/wordaccess_gcc3_be.h create mode 100644 lib/util/wordaccess_gcc3_le.h create mode 100644 lib/util/wordaccess_generic.h (limited to 'lib') diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..bd8eccae --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,278 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = lib +VPATH=.:$(SRCDIR)/$(SUBDIR) +DLLTOOL=dlltool +include $(BUILDDIR)/Makefile.config + +ifeq ($(NETPBMLIBTYPE),unixstatic) +LIBNETPBM = libnetpbm.$(STATICLIBSUFFIX) +else +LIBNETPBM = $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).$(NETPBMLIBSUFFIX) +endif + +ifeq ($(STATICLIB_TOO),y) +EXTRA_STATICLIB = libnetpbm.$(STATICLIBSUFFIX) +else +EXTRA_STATICLIB = +endif + +ifeq ($(DONT_HAVE_PROCESS_MGMT),Y) + LIBSYSTEM = libsystem_dummy.o +else + LIBSYSTEM = libsystem.o +endif + +LIBOBJECTS = libpm.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 \ + libppmd.o ppmdfont.o standardppmdfont.o path.o \ + libppmfloyd.o \ + libpnm1.o libpnm2.o libpnm3.o \ + libpam.o libpamread.o libpamwrite.o \ + libpamn.o libpammap.o libpamcolor.o \ + $(LIBSYSTEM) \ + +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 + +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 + +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 + +# The following must go after Makefile.common because $(LIBNETPBM) may +# contain a reference to $(NETPBM_MAJOR_RELEASE). +.PHONY: libnetpbm +libnetpbm: $(LIBNETPBM) + +.PHONY: extra_staticlib +extra_staticlib: $(EXTRA_STATICLIB) + +#---------------------------------------------------------------------------- +# Following are rules for building shared libraries. +# Note that the user may specify a shared library as his "main" library +# type, but request a static library in addition. +#---------------------------------------------------------------------------- + +$(LIBOBJECTS): %.o: %.c importinc +# Note that the user may have configured -I options into CFLAGS. + $(CC) -c $(INCLUDES) -DNDEBUG $(CFLAGS) $(CFLAGS_SHLIB) \ + $(CFLAGS_PERSONAL) $(CADD) -o $@ $< + +MAJ = $(NETPBM_MAJOR_RELEASE) +MIN = $(NETPBM_MINOR_RELEASE) + +SONAME = libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ) + +ifeq ($(NETPBMLIBTYPE),irixshared) +# The libxxx.so link is needed to link the executables. +libnetpbm.$(NETPBMLIBSUFFIX): $(SONAME) + rm -f $@ + $(SYMLINK) $< $@ + +PERLPROG = print "sgi$(MAJ)." . join(":sgi$(MAJ) . ", (0..$(MIN))) . "\n" + +$(SONAME): \ + $(LIBOBJECTS) $(LIBOBJECTS_X) + $(LD) $(LDSHLIB) -o $@ $(LIBOBJECTS) $(LIBOBJECTS_X) \ + -lc \ + -soname libnetpbm.$(NETPBMLIBSUFFIX) \ + -set_version $(shell perl -e '$(PERLPROG)') \ + $(LADD) +endif + +ifeq ($(NETPBMLIBTYPE),unixshared) +# The libxxx.so link is needed to link the executables. +libnetpbm.$(NETPBMLIBSUFFIX): $(SONAME) + rm -f $@ + $(SYMLINK) $< $@ +# The $(SONAME) link is needed only to test the programs without +# installing the libraries (in that case, you also need to direct the +# dynamic linker to the source directories, e.g. set LD_LIBRARY_PATH). +$(SONAME): libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).$(MIN) + rm -f $@ + $(SYMLINK) $< $@ +libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).$(MIN): $(LIBOBJECTS) $(LIBOBJECTS_X) + $(LD) $(LDSHLIB) -o $@ $(LIBOBJECTS) $(LIBOBJECTS_X) \ + $(SHLIB_CLIB) -lm $(LADD) +endif + +ifeq ($(NETPBMLIBTYPE),dll) +ifeq ($(STATICLIB_TOO),y) +$(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll: $(LIBOBJECTS) $(LIBOBJECTS_X) libnetpbm.$(STATICLIBSUFFIX) +else +$(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll: $(LIBOBJECTS) $(LIBOBJECTS_X) +endif + $(LD) $(LDSHLIB) -Wl,--export-all-symbols \ + -Wl,-soname,$(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll \ + -Wl,--output-def,$(NETPBMSHLIBPREFIX)netpbm$(DLLVER).def \ + -Wl,--out-implib,libnetpbm.dll.a -o $@ $(LDFLAGS) \ + $(LIBOBJECTS) $(LIBOBJECTS_X) $(LDLIBS) $(LADD) +endif + +ifeq ($(NETPBMLIBTYPE),dylib) +libnetpbm.dylib: libnetpbm.$(MAJ).dylib + rm -f $@ + $(SYMLINK) $< $@ + +libnetpbm.$(MAJ).dylib: libnetpbm.$(MAJ).$(MIN).dylib + rm -f $@ + $(SYMLINK) $< $@ + +libnetpbm.$(MAJ).$(MIN).dylib: $(LIBOBJECTS) $(LIBOBJECTS_X) + $(LD) $(LDSHLIB) -o $@ $(LIBOBJECTS) $(LIBOBJECTS_X) \ + -lc $(LADD) +endif + +#-------------------------------------------------------------------------- +# The rule for building a static library is below (if needed). This is +# tricky because the user can be building the static library as his main +# library or in addition to some other kind of main library. In fact, +# he may have specified it both as the main library type and an +# additional library type. In that case, NETPBMLIBSUFFIX and +# STATICLIBSUFFIX are redundant -- we hope they're the same. +# +# We must not include a rule for static libraries if he doesn't want us +# to build any. The library name we come up with might conflict with +# the name of the library he actually is building. In fact, in that case +# STATICLIB_SUFFIX may just be arbitrary. +#----------------------------------------------------------------------------- +ifeq ($(NETPBMLIBTYPE),unixstatic) + BUILD_STATICLIB = y +else + ifeq ($(STATICLIB_TOO),y) + BUILD_STATICLIB = y + else + BUILD_STATICLIB = n + endif +endif + +ifeq ($(BUILD_STATICLIB),y) +libnetpbm.$(STATICLIBSUFFIX): $(LIBOBJECTS) $(LIBOBJECTS_X) + -rm -f $@ + ar rc $@ $(LIBOBJECTS) $(LIBOBJECTS_X) + -$(RANLIB) $@ +endif + + +# To avoid major hassles with having ppmdcfont available here, we just +# ship a pre-made standardppmfont.c, so this rule will not normally be +# used. +standardppmdfont.c:standard.ppmdfont + ppmdcfont <$< >$@ || (rm $@ && false) + +# 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 +# every build of "all" from the Netpbm top directory, but nowhere +# else, so it was really sloppy. + +compile.h: + $(SRCDIR)/buildtools/stamp-date >$@ || rm $@ + +$(LIBOBJECTS_X): FORCE + @if [ ! -d $(dir $@) ] ; then mkdir $(dir $@) ; fi + $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ + SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) + +libpm.o: compile.h + +# Install a shared library +# +.PHONY: install.lib +ifeq ($(NETPBMLIBTYPE),unixshared) +# install a Unix-style shared library +install.lib: $(PKGDIR)/lib $(PKGDIR)/link + cd $(PKGDIR)/lib ; rm -f libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).* + $(INSTALL) -c -m $(INSTALL_PERM_LIBD) \ + libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).$(MIN) $(PKGDIR)/lib/ + cd $(PKGDIR)/lib/ ; \ + rm -f libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ); \ + $(SYMLINK) libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).$(MIN) $(SONAME) +endif +ifeq ($(NETPBMLIBTYPE),dll) +#install a Windows DLL shared library +#Note that unlike Unix libraries, a Windows DLL must go into a directory +#that is in the PATH, so we use bin/ instead of lib/ +install.lib: $(PKGDIR)/bin + ( cd $(PKGDIR)/bin ; rm -f $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll ) + $(INSTALL) -c $(STRIPFLAG) -m $(INSTALL_PERM_LIBD) \ + $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll $(PKGDIR)/bin/ +endif +ifeq ($(NETPBMLIBTYPE),dylib) +# install a Darwin-style shared library +install.lib: $(PKGDIR)/lib + cd $(PKGDIR)/lib ; rm -f libnetpbm.*.dylib + $(INSTALL) -c -m $(INSTALL_PERM_LIBD) libnetpbm.$(MAJ).$(MIN).dylib \ + $(PKGDIR)/lib + cd $(PKGDIR)/lib ; \ + rm -f libnetpbm.$(MAJ).dylib; \ + $(SYMLINK) libnetpbm.$(MAJ).$(MIN).dylib libnetpbm.$(MAJ).dylib +endif + +.PHONY: install.hdr +install.hdr: $(INTERFACE_HEADERS:%=%_installhdr) +# You need to install the interface header files only if you are going to +# compile programs that use the Netpbm libraries. Alternatively, you may +# prefer not to "install" them, but just to access the Netpbm source +# directory when you compile your programs. + +%_installhdr: $(PKGDIR)/include + $(INSTALL) -c -m $(INSTALL_PERM_HDR) \ + $(SRCDIR)/lib/$(@:%_installhdr=%) $(PKGDIR)/include/; + +.PHONY: install.staticlib +install.staticlib: $(PKGDIR)/link + $(INSTALL) -c -m $(INSTALL_PERM_LIBS) libnetpbm.$(STATICLIBSUFFIX) \ + $(PKGDIR)/link + +# Install a shared library stub -- the ".so" file used at link time to +# prepare a program for dynamically linking a library at run time +.PHONY: install.sharedlibstub +install.sharedlibstub: $(PKGDIR)/link +ifeq ($(NETPBMLIBTYPE),unixshared) +# install the link-time (.so) links to the runtime libraries + cd $(PKGDIR)/link ; \ + rm -f libnetpbm.$(NETPBMLIBSUFFIX); \ + $(SYMLINK) ../lib/libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ) \ + libnetpbm.$(NETPBMLIBSUFFIX) +endif +ifeq ($(NETPBMLIBTYPE),dll) + $(INSTALL) -c -m $(INSTALL_PERM_LIBS) libnetpbm.dll.a $(PKGDIR)/link +endif +ifeq ($(NETPBMLIBTYPE),dylib) + cd $(PKGDIR)/link/ ; \ + rm -f libnetpbm.dylib; \ + $(SYMLINK) ../lib/libnetpbm.$(MAJ).$(MIN).dylib libnetpbm.dylib +endif + +clean: localclean + +.PHONY: localclean +localclean: + rm -f compile.h + +FORCE: diff --git a/lib/bitio.c b/lib/bitio.c new file mode 100644 index 00000000..ca1b55f9 --- /dev/null +++ b/lib/bitio.c @@ -0,0 +1,207 @@ +/*\ + * $Id: bitio.c,v 1.5 1992/11/24 19:36:46 dws Exp dws $ + * + * bitio.c - bitstream I/O + * + * Works for (sizeof(unsigned long)-1)*8 bits. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log: bitio.c,v $ + * Revision 1.5 1992/11/24 19:36:46 dws + * Added copyright. + * + * Revision 1.4 1992/11/17 03:37:50 dws + * updated comment + * + * Revision 1.3 1992/11/10 23:15:16 dws + * Removed superfluous code. + * + * Revision 1.2 1992/11/10 23:11:22 dws + * Generalized to handle more than one bitstream at once. + * + * Revision 1.1 1992/11/10 18:33:21 dws + * Initial revision + * +\*/ + +#include + +#include "bitio.h" + +struct bitstream +{ + FILE * + f; /* bytestream */ + unsigned long + bitbuf; /* bit buffer */ + int + nbitbuf; /* number of bits in 'bitbuf' */ + char + mode; +}; + +#define Mask(n) ((1<<(n))-1) + +#define BitPut(b,ul,n) ((b)->bitbuf = (((b)->bitbuf<<(n)) \ + |((ul)&Mask(n))), \ + (b)->nbitbuf += (n)) + +#define BitGet(b,n) (((b)->bitbuf>>((b)->nbitbuf-=(n))) & Mask(n)) + +/* + * pm_bitinit() - allocate and return a struct bitstream * for the + * given FILE*. + * + * mode must be one of "r" or "w", according to whether you will be + * reading from or writing to the struct bitstream *. + * + * Returns 0 on error. + */ + +struct bitstream * +pm_bitinit(FILE * const f, const char * const mode) { + + struct bitstream * ans; + + if (!f || !mode) + ans = NULL; + else if (strcmp(mode, "r") != 0 && strcmp(mode, "w") != 0) + ans = NULL; + else { + ans = (struct bitstream *)calloc(1, sizeof(struct bitstream)); + if (ans != NULL) { + ans->f = f; + ans->mode = *mode; + } + } + return ans; +} + +/* + * pm_bitfini() - deallocate the given struct bitstream *. + * + * You must call this after you are done with the struct bitstream *. + * + * It may flush some bits left in the buffer. + * + * Returns the number of bytes written, -1 on error. + */ + +int +pm_bitfini(b) + struct bitstream *b; +{ + int nbyte = 0; + + if(!b) + return -1; + + /* flush the output */ + if(b->mode == 'w') + { + /* flush the bits */ + if (b->nbitbuf < 0 || b->nbitbuf >= 8) + { + /* pm_bitwrite() didn't work */ + return -1; + } + + /* + * If we get to here, nbitbuf is 0..7 + */ + if(b->nbitbuf) + { + char c; + + BitPut(b, 0, (long)8-(b->nbitbuf)); + c = (char) BitGet(b, (long)8); + if(putc(c, b->f) == EOF) + { + return -1; + } + nbyte++; + } + } + + free(b); + return nbyte; +} + +/* + * pm_bitread() - read the next nbits into *val from the given file. + * + * The last pm_bitread() must be followed by a call to pm_bitfini(). + * + * Returns the number of bytes read, -1 on error. + */ + +int +pm_bitread(b, nbits, val) + struct bitstream *b; + unsigned long nbits; + unsigned long *val; +{ + int nbyte = 0; + int c; + + if(!b) + return -1; + + while (b->nbitbuf < nbits) + { + if((c = getc(b->f)) == EOF) + { + return -1; + } + nbyte++; + + BitPut(b, c, (long) 8); + } + + *val = BitGet(b, nbits); + return nbyte; +} + +/* + * pm_bitwrite() - write the low nbits of val to the given file. + * + * The last pm_bitwrite() must be followed by a call to pm_bitfini(). + * + * Returns the number of bytes written, -1 on error. + */ + +int +pm_bitwrite(b, nbits, val) + struct bitstream *b; + unsigned long nbits; + unsigned long val; +{ + int nbyte = 0; + char c; + + if(!b) + return -1; + + BitPut(b, val, nbits); + + while (b->nbitbuf >= 8) + { + c = (char) BitGet(b, (long)8); + + if(putc(c, b->f) == EOF) + { + return -1; + } + nbyte++; + } + + return nbyte; +} diff --git a/lib/bitio.h b/lib/bitio.h new file mode 100644 index 00000000..15fe0e8a --- /dev/null +++ b/lib/bitio.h @@ -0,0 +1,89 @@ +/*\ + * $Id: bitio.h,v 1.4 1992/11/24 19:37:02 dws Exp dws $ + * + * bitio.h - bitstream I/O + * + * Works for (sizeof(unsigned long)-1)*8 bits. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log: bitio.h,v $ + * Revision 1.4 1992/11/24 19:37:02 dws + * Added copyright + * + * Revision 1.3 1992/11/17 03:37:59 dws + * updated comment + * + * Revision 1.2 1992/11/10 23:10:22 dws + * Generalized to handle more than one bitstream at a time. + * + * Revision 1.1 1992/11/10 18:33:51 dws + * Initial revision + * +\*/ + +#ifndef _BITIO_H_ +#define _BITIO_H_ + +#include "pm.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +typedef struct bitstream *BITSTREAM; + +struct bitstream * +pm_bitinit(FILE * const f, const char * const mode); + +/* + * pm_bitfini() - deallocate the given BITSTREAM. + * + * You must call this after you are done with the BITSTREAM. + * + * It may flush some bits left in the buffer. + * + * Returns the number of bytes written, -1 on error. + */ + +int +pm_bitfini(BITSTREAM b); + +/* + * pm_bitread() - read the next nbits into *val from the given file. + * + * Returns the number of bytes read, -1 on error. + */ + +int +pm_bitread(BITSTREAM b, + unsigned long nbits, + unsigned long * val); + +/* + * pm_bitwrite() - write the low nbits of val to the given file. + * + * The last pm_bitwrite() must be followed by a call to pm_bitflush(). + * + * Returns the number of bytes written, -1 on error. + */ + +int +pm_bitwrite(BITSTREAM b, + unsigned long nbits, + unsigned long val); + +#ifdef __cplusplus +} +#endif +#endif /* _BITIO_H_ */ diff --git a/lib/colorname.c b/lib/colorname.c new file mode 100644 index 00000000..dcda3ab8 --- /dev/null +++ b/lib/colorname.c @@ -0,0 +1,208 @@ +/* colorname.c - colorname routines, not dependent on Netpbm formats +** +** Taken from libppm4.c May 2002. + +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include "pm_c_util.h" +#include +#include +#include +#include + +#include "nstring.h" +#include "colorname.h" + +static int lineNo; + +void +pm_canonstr(char * const str) { + + char * p; + for (p = str; *p; ) { + if (ISSPACE(*p)) { + strcpy(p, &(p[1])); + } else { + if (ISUPPER(*p)) + *p = tolower(*p); + ++p; + } + } +} + + + +FILE * +pm_openColornameFile(const char * const fileName, const int must_open) { +/*---------------------------------------------------------------------------- + Open the colorname dictionary file. Its file name is 'fileName', unless + 'fileName' is NULL. In that case, its file name is the value of the + environment variable whose name is RGB_ENV (e.g. "RGBDEF"). Except + if that environment variable is not set, it is RGB_DB1, RGB_DB2, + or RGB_DB3 (e.g. "/usr/lib/X11/rgb.txt"), whichever exists. + + 'must_open' is a logical: we must get the file open or die. If + 'must_open' is true and we can't open the file (e.g. it doesn't + 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) { + /* 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 " + "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); + } + } 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"); + if (f == NULL && must_open) + pm_error("Can't open the color names dictionary file '%s'. " + "errno = %d (%s)", fileName, errno, strerror(errno)); + + } + lineNo = 0; + return(f); +} + + + +struct colorfile_entry +pm_colorget(FILE * const f) { +/*---------------------------------------------------------------------------- + Get next color entry from the color name dictionary file 'f'. + + If eof or error, return a color entry with NULL for the color name. + + Otherwise, return color name in static storage within. +-----------------------------------------------------------------------------*/ + char buf[200]; + static char colorname[200]; + bool gotOne; + bool eof; + struct colorfile_entry retval; + char * rc; + + gotOne = FALSE; /* initial value */ + eof = FALSE; + while (!gotOne && !eof) { + lineNo++; + rc = fgets(buf, sizeof(buf), f); + if (rc == NULL) + eof = TRUE; + else { + if (buf[0] != '#' && buf[0] != '\n' && buf[0] != '!' && + buf[0] != '\0') { + if (sscanf(buf, "%ld %ld %ld %[^\n]", + &retval.r, &retval.g, &retval.b, colorname) + == 4 ) + gotOne = TRUE; + else { + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + pm_message("can't parse color names dictionary Line %d: " + "'%s'", + lineNo, buf); + } + } + } + } + if (gotOne) + retval.colorname = colorname; + else + retval.colorname = NULL; + return retval; +} + + + +void +pm_parse_dictionary_name(char const colorname[], + pixval const maxval, + int const closeOk, + pixel * const colorP) { + + FILE* f; + bool gotit; + bool colorfileExhausted; + struct colorfile_entry colorfile_entry; + char * canoncolor; + pixval r,g,b; + + f = pm_openColornameFile(NULL, TRUE); /* exits if error */ + canoncolor = strdup(colorname); + pm_canonstr(canoncolor); + gotit = FALSE; + colorfileExhausted = FALSE; + while (!gotit && !colorfileExhausted) { + colorfile_entry = pm_colorget(f); + if (colorfile_entry.colorname) { + pm_canonstr(colorfile_entry.colorname); + if (strcmp( canoncolor, colorfile_entry.colorname) == 0) + gotit = TRUE; + } else + colorfileExhausted = TRUE; + } + fclose(f); + + if (!gotit) + pm_error("unknown color '%s'", colorname); + + /* Rescale from [0..255] if necessary. */ + if (maxval != 255) { + r = colorfile_entry.r * maxval / 255; + g = colorfile_entry.g * maxval / 255; + b = colorfile_entry.b * maxval / 255; + + if (!closeOk) { + if (r * 255 / maxval != colorfile_entry.r || + g * 255 / maxval != colorfile_entry.g || + b * 255 / maxval != colorfile_entry.b) + pm_message("WARNING: color '%s' cannot be represented " + "exactly with a maxval of %u. " + "Approximating as (%u,%u,%u). " + "The color dictionary uses maxval 255, so that " + "maxval will always work.", + colorname, maxval, r, g, b); + } + } else { + r = colorfile_entry.r; + g = colorfile_entry.g; + b = colorfile_entry.b; + } + free(canoncolor); + + PPM_ASSIGN(*colorP, r, g, b); +} + + + diff --git a/lib/colorname.h b/lib/colorname.h new file mode 100644 index 00000000..d33980e4 --- /dev/null +++ b/lib/colorname.h @@ -0,0 +1,43 @@ +#ifndef COLORNAME_H +#define COLORNAME_H + +#include +#include +#include "ppm.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +enum colornameFormat {PAM_COLORNAME_ENGLISH = 0, + PAM_COLORNAME_HEXOK = 1}; + +struct colorfile_entry { + long r, g, b; + char * colorname; +}; + + + +void +pm_canonstr(char * const str); + +FILE * +pm_openColornameFile(const char * const fileName, const int must_open); + +struct colorfile_entry +pm_colorget(FILE * const f); + +void +pm_parse_dictionary_name(const char colorname[], + pixval const maxval, + int const closeOk, + pixel * const colorP); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/compile.h b/lib/compile.h new file mode 100644 index 00000000..4626f1ea --- /dev/null +++ b/lib/compile.h @@ -0,0 +1,5 @@ +/* This file tells the package when it was compiled */ +/* DO NOT EDIT - THIS FILE IS MAINTAINED AUTOMATICALLY */ +/* Created by the program 'stamp-date' */ +#define COMPILE_TIME "Thu Aug 17 03:34:22 GMT 2006" +#define COMPILED_BY "bryanh" diff --git a/lib/fileio.c b/lib/fileio.c new file mode 100644 index 00000000..01243f9d --- /dev/null +++ b/lib/fileio.c @@ -0,0 +1,170 @@ +/* fileio.c - routines to read elements from Netpbm image files +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include +#include + +#include "pm.h" +#include "fileio.h" + +char +pm_getc(FILE * const file) { + int ich; + char ch; + + ich = getc(file); + 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'); + } + return ch; +} + + + +/* This is useful for PBM files. It used to be used for PGM and PPM files + too, since the sample size was always one byte. Now, use pbm_getrawsample() + for PGM and PPM files. +*/ + +unsigned char +pm_getrawbyte(FILE * const file) { + int iby; + + iby = getc(file); + if (iby == EOF) + pm_error("EOF / read error reading a one-byte sample"); + return (unsigned char) iby; +} + + + +unsigned int +pm_getuint(FILE * const ifP) { +/*---------------------------------------------------------------------------- + Read an unsigned integer in ASCII decimal from the file stream + represented by 'ifP' and return its value. + + If there is nothing at the current position in the file stream that + can be interpreted as an unsigned integer, issue an error message + to stderr and abort the program. + + If the number at the current position in the file stream is too + great to be represented by an 'int' (Yes, I said 'int', not + 'unsigned int'), issue an error message to stderr and abort the + program. +-----------------------------------------------------------------------------*/ + char ch; + unsigned int i; + + do { + ch = pm_getc(ifP); + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + + if (ch < '0' || ch > '9') + pm_error("junk in file where an unsigned integer should be"); + + i = 0; + do { + unsigned int const digitVal = ch - '0'; + + if (i > INT_MAX/10 - digitVal) + pm_error("ASCII decimal integer in file is " + "too large to be processed. "); + i = i * 10 + digitVal; + ch = pm_getc(ifP); + } while (ch >= '0' && ch <= '9'); + + return i; +} + + + +unsigned int +pm_getraw(FILE * const file, + unsigned int const bytes) { + + unsigned int value; /* our return value */ + + if (bytes == 1) { + /* Here's a speedup for the common 1-byte sample case: */ + value = getc(file); + if (value == EOF) + pm_error("EOF/error reading 1 byte sample from file."); + } else { + /* This code works for bytes == 1..4 */ + /* We could speed this up by exploiting knowledge of the format of + an unsigned integer (i.e. endianness). Then we could just cast + the value as an array of characters instead of shifting and + masking. + */ + int shift; + unsigned char inval[4]; + int cursor; + int n_read; + + n_read = fread(inval, bytes, 1, file); + if (n_read < 1) + pm_error("EOF/error reading %d byte sample from file.", bytes); + value = 0; /* initial value */ + cursor = 0; + for (shift = (bytes-1)*8; shift >= 0; shift-=8) + value += inval[cursor++] << shift; + } + return(value); +} + + + +void +pm_putraw(FILE * const file, + unsigned int const value, + unsigned int const bytes) { + + if (bytes == 1) { + /* Here's a speedup for the common 1-byte sample case: */ + int rc; + rc = fputc(value, file); + if (rc == EOF) + pm_error("Error writing 1 byte sample to file."); + } else { + /* This code works for bytes == 1..4 */ + /* We could speed this up by exploiting knowledge of the format of + an unsigned integer (i.e. endianness). Then we could just cast + the value as an array of characters instead of shifting and + masking. + */ + int shift; + unsigned char outval[4]; + int cursor; + int n_written; + + cursor = 0; + for (shift = (bytes-1)*8; shift >= 0; shift-=8) { + outval[cursor++] = (value >> shift) & 0xFF; + } + n_written = fwrite(&outval, bytes, 1, file); + if (n_written == 0) + pm_error("Error writing %d byte sample to file.", bytes); + } +} + + + diff --git a/lib/fileio.h b/lib/fileio.h new file mode 100644 index 00000000..158da10a --- /dev/null +++ b/lib/fileio.h @@ -0,0 +1,22 @@ +#ifndef _NETPBM_FILEIO_H_ +#define _NETPBM_FILEIO_H_ + +char +pm_getc(FILE * const file); + +unsigned char +pm_getrawbyte(FILE * const file); + +unsigned int +pm_getuint(FILE * const file); + +unsigned int +pm_getraw(FILE * const file, + unsigned int const bytes); + +void +pm_putraw(FILE * const file, + unsigned int const value, + unsigned int const bytes); + +#endif diff --git a/lib/libpam.c b/lib/libpam.c new file mode 100644 index 00000000..bbfa7e32 --- /dev/null +++ b/lib/libpam.c @@ -0,0 +1,1098 @@ +/*---------------------------------------------------------------------------- + libpam.c +------------------------------------------------------------------------------ + These are the library functions, which belong in the libnetpbm library, + that deal with the PAM (Portable Arbitrary Format) image format. +-----------------------------------------------------------------------------*/ + +/* See libpm.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 +#include +#include +#include + +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "nstring.h" +#include "pam.h" +#include "ppm.h" +#include "libpbm.h" +#include "libpgm.h" +#include "libppm.h" +#include "colorname.h" +#include "fileio.h" + +#include "libpam.h" + + + +static unsigned int +allocationDepth(const struct pam * const pamP) { + + unsigned int retval; + + if (pamP->len >= PAM_STRUCT_SIZE(allocation_depth)) { + if (pamP->allocation_depth == 0) + retval = pamP->depth; + else { + if (pamP->depth > pamP->allocation_depth) + pm_error("'allocationDepth' (%u) is smaller than 'depth' (%u)", + pamP->allocation_depth, pamP->depth); + retval = pamP->allocation_depth; + } + } else + retval = pamP->depth; + return retval; +} + + + +static const char ** +pamCommentP(const struct pam * const pamP) { + + const char ** retval; + + if (pamP->len >= PAM_STRUCT_SIZE(comment_p)) + retval = pamP->comment_p; + else + retval = NULL; + + return retval; +} + + +static void +validateComputableSize(struct pam * const pamP) { +/*---------------------------------------------------------------------------- + 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. + + It is very normal to allocate space for a tuplerow, so we make sure + the size of a tuple row, in bytes, can be represented by an 'int'. + + 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 (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); +} + + + +tuple +pnm_allocpamtuple(const struct pam * const pamP) { + + tuple retval; + + retval = malloc(allocationDepth(pamP) * sizeof(retval[0])); + + if (retval == NULL) + pm_error("Out of memory allocating %u-plane tuple", + allocationDepth(pamP)); + + return retval; +} + + + +int +pnm_tupleequal(const struct pam * const pamP, + tuple const comparand, + tuple const comparator) { + + unsigned int plane; + bool equal; + + equal = TRUE; /* initial value */ + for (plane = 0; plane < pamP->depth; ++plane) + if (comparand[plane] != comparator[plane]) + equal = FALSE; + + return equal; +} + + + + +void +pnm_assigntuple(const struct pam * const pamP, + tuple const dest, + tuple const source) { + + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) { + dest[plane] = source[plane]; + } +} + + + +static void +scaleTuple(const struct pam * const pamP, + tuple const dest, + tuple const source, + sample const newmaxval) { + + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + dest[plane] = pnm_scalesample(source[plane], pamP->maxval, newmaxval); +} + + + +void +pnm_scaletuple(const struct pam * const pamP, + tuple const dest, + tuple const source, + sample const newmaxval) { + + scaleTuple(pamP, dest, source, newmaxval); +} + + + +void +pnm_createBlackTuple(const struct pam * const pamP, + tuple * const blackTupleP) { +/*---------------------------------------------------------------------------- + Create a "black" tuple. By that we mean a tuple all of whose elements + are zero. If it's an RGB, grayscale, or b&w pixel, that means it's black. +-----------------------------------------------------------------------------*/ + *blackTupleP = pnm_allocpamtuple(pamP); + if (pamP->format == PAM_FORMAT) { + /* In this format, we don't know the meaning of "black", so we + just punt. + */ + int i; + for (i = 0; i < pamP->depth; i++) + (*blackTupleP)[i] = 0; + } else { + xel black_xel; + black_xel = pnm_blackxel(pamP->maxval, pamP->format); + (*blackTupleP)[0] = PPM_GETR(black_xel); + (*blackTupleP)[1] = PPM_GETG(black_xel); + (*blackTupleP)[2] = PPM_GETB(black_xel); + } +} + + + +void +createBlackTuple(const struct pam * const pamP, + tuple * const blackTupleP) { + +/* This is poorly named, because it lacks the "pnm" prefix. But for some + reason, this is how we originally named this. So to maintain backward + compatibility with binaries that refer to "createBlackTuple", we define + this. The preferred name, pnm_createBlackTuple() was new in Netpbm 10.20, + January 2004. We should eventually retire createBlackTuple(). +*/ + pnm_createBlackTuple(pamP, blackTupleP); +} + + + +static tuple * +allocPamRow(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. +-----------------------------------------------------------------------------*/ + /* The tuple row data structure starts with 'width' pointers to + the tuples, immediately followed by the 'width' tuples + themselves. Each tuple consists of 'depth' samples. + */ + + int const bytesPerTuple = allocationDepth(pamP) * sizeof(sample); + tuple * tuplerow; + + tuplerow = malloc(pamP->width * (sizeof(tuple *) + bytesPerTuple)); + + if (tuplerow != NULL) { + /* Now we initialize the pointers to the individual tuples + to make this a regulation C two dimensional array. + */ + char * p; + unsigned int col; + + p = (char*) (tuplerow + pamP->width); /* location of Tuple 0 */ + for (col = 0; col < pamP->width; ++col) { + tuplerow[col] = (tuple) p; + p += bytesPerTuple; + } + } + return tuplerow; +} + + + +tuple * +pnm_allocpamrow(const struct pam * const pamP) { + + + tuple * const tuplerow = allocPamRow(pamP); + + if (tuplerow == 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, allocationDepth(pamP), sizeof(sample)); + + return tuplerow; +} + + + +static unsigned int +rowimagesize(const struct pam * const pamP) { + + /* If repeatedly calculating this turns out to be a significant + performance problem, we could keep this in struct pam like + bytes_per_sample. + */ + + if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) + return pbm_packed_bytes(pamP->width); + else + return (pamP->width * pamP->bytes_per_sample * pamP->depth); +} + + + +unsigned char * +pnm_allocrowimage(const struct pam * const pamP) { + + unsigned int const rowsize = rowimagesize(pamP); + unsigned int const overrunSpaceNeeded = 8; + /* This is the number of extra bytes of space libnetpbm needs to have + at the end of the buffer so it can use fast, lazy algorithms. + */ + unsigned int const size = rowsize + overrunSpaceNeeded; + + unsigned char * retval; + + retval = malloc(size); + + if (retval == NULL) + pm_error("Unable to allocate %u bytes for a row image buffer", + size); + + return retval; +} + + + +void +pnm_freerowimage(unsigned char * const rowimage) { + free(rowimage); +} + + + +void +pnm_scaletuplerow(const struct pam * const pamP, + tuple * const destRow, + tuple * const sourceRow, + sample const newMaxval) { + + if (pamP->maxval == newMaxval) { + /* Fast path for common case: no scaling needed */ + if (destRow != sourceRow) { + /* Fast path for common case: it's already what it needs to be */ + unsigned int col; + for (col = 0; col < pamP->width; ++col) + pnm_assigntuple(pamP, destRow[col], sourceRow[col]); + } + } else { + unsigned int col; + for (col = 0; col < pamP->width; ++col) + scaleTuple(pamP, destRow[col], sourceRow[col], newMaxval); + } +} + + + +tuple ** +pnm_allocpamarray(const struct pam * const pamP) { + + tuple **tuplearray; + + /* If the speed of this is ever an issue, it might be sped up a little + by allocating one large chunk. + */ + + MALLOCARRAY(tuplearray, pamP->height); + if (tuplearray == NULL) + pm_error("Out of memory allocating the row pointer section of " + "a %u row array", pamP->height); + else { + int row; + bool outOfMemory; + + outOfMemory = FALSE; + for (row = 0; row < pamP->height && !outOfMemory; ++row) { + tuplearray[row] = allocPamRow(pamP); + if (tuplearray[row] == NULL) { + unsigned int freerow; + outOfMemory = TRUE; + + for (freerow = 0; freerow < row; ++freerow) + pnm_freepamrow(tuplearray[row]); + } + } + if (outOfMemory) { + free(tuplearray); + + pm_error("Out of memory allocating the %u rows %u columns wide by " + "%u planes deep", pamP->height, pamP->width, + allocationDepth(pamP)); + } + } + return tuplearray; +} + + + +void +pnm_freepamarray(tuple ** const tuplearray, const struct pam * const pamP) { + + int row; + for (row = 0; row < pamP->height; row++) + pnm_freepamrow(tuplearray[row]); + + free(tuplearray); +} + + + +void +pnm_setminallocationdepth(struct pam * const pamP, + unsigned int const allocationDepth) { + + if (pamP->len < PAM_STRUCT_SIZE(allocation_depth)) + pm_error("Can't set minimum allocation depth in pam structure, " + "because the structure is only %u bytes long, and to " + "have an allocation_depth field, it must bea at least %u", + pamP->len, PAM_STRUCT_SIZE(allocation_depth)); + + pamP->allocation_depth = MAX(allocationDepth, pamP->depth); + + validateComputableSize(pamP); +} + + + +void +pnm_setpamrow(const struct pam * const pamP, + tuple * const tuplerow, + sample const value) { + + int col; + for (col = 0; col < pamP->width; ++col) { + int plane; + for (plane = 0; plane < pamP->depth; ++plane) + tuplerow[col][plane] = value; + } +} + + + + +#define MAX_LABEL_LENGTH 8 +#define MAX_VALUE_LENGTH 255 + +static void +parse_header_line(const char buffer[], char label[MAX_LABEL_LENGTH+1], + char value[MAX_VALUE_LENGTH+1]) { + + int buffer_curs; + + buffer_curs = 0; + /* Skip initial white space */ + while (ISSPACE(buffer[buffer_curs])) buffer_curs++; + + { + /* Read off label, put as much as will fit into label[] */ + int label_curs; + label_curs = 0; + while (!ISSPACE(buffer[buffer_curs]) && buffer[buffer_curs] != '\0') { + if (label_curs < MAX_LABEL_LENGTH) + label[label_curs++] = buffer[buffer_curs]; + buffer_curs++; + } + label[label_curs] = '\0'; /* null terminate it */ + } + + /* Skip white space between label and value */ + while (ISSPACE(buffer[buffer_curs])) buffer_curs++; + + /* copy value into value[] */ + strncpy(value, buffer+buffer_curs, MAX_VALUE_LENGTH+1); + + { + /* Remove trailing white space from value[] */ + int value_curs; + value_curs = strlen(value)-1; + while (value_curs >= 0 && ISSPACE(value[value_curs])) + value[value_curs--] = '\0'; + } +} + + + +struct headerSeen { +/*---------------------------------------------------------------------------- + This structure tells what we've seen so far in our progress through the + PAM header +-----------------------------------------------------------------------------*/ + bool width; + bool height; + bool depth; + bool maxval; + bool endhdr; +}; + + + +static void +process_header_line(char const buffer[], + struct pam * const pamP, + struct headerSeen * const headerSeenP) { +/*---------------------------------------------------------------------------- + Process a line from the PAM header. The line is buffer[], and it is not + a comment or blank. + + Put the value that the line defines in *pamP (unless it's ENDHDR). + + Update *headerSeenP with whatever we see. +-----------------------------------------------------------------------------*/ + char label[MAX_LABEL_LENGTH+1]; + char value[MAX_VALUE_LENGTH+1]; + + parse_header_line(buffer, label, value); + + if (strcmp(label, "ENDHDR") == 0) + headerSeenP->endhdr = TRUE; + else { + if (strcmp(label, "WIDTH") == 0 || + strcmp(label, "HEIGHT") == 0 || + strcmp(label, "DEPTH") == 0 || + strcmp(label, "MAXVAL") == 0) { + + if (strlen(value) == 0) + pm_error("Missing value for %s in PAM file header.", + label); + else { + char *endptr; + long int numeric_value; + errno = 0; /* Clear errno so we can detect strtol() failure */ + numeric_value = strtol(value, &endptr, 10); + if (errno != 0) + pm_error("Too-large value for %s in " + "PAM file header: '%s'", label, value); + if (*endptr != '\0') + pm_error("Non-numeric value for %s in " + "PAM file header: '%s'", label, value); + else if (numeric_value < 0) + pm_error("Negative value for %s in " + "PAM file header: '%s'", label, value); + } + } + + if (strcmp(label, "WIDTH") == 0) { + pamP->width = atoi(value); + headerSeenP->width = TRUE; + } else if (strcmp(label, "HEIGHT") == 0) { + pamP->height = atoi(value); + headerSeenP->height = TRUE; + } else if (strcmp(label, "DEPTH") == 0) { + pamP->depth = atoi(value); + headerSeenP->depth = TRUE; + } else if (strcmp(label, "MAXVAL") == 0) { + 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); + else { + strcat(pamP->tuple_type, "\n"); + strcat(pamP->tuple_type, value); + } + pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0'; + } else + pm_error("Unrecognized header line: '%s'. " + "Possible missing ENDHDR line?", label); + } +} + + + +static void +appendComment(char ** const commentsP, + const char * const commentHeader) { + + const char * const commentLine = &commentHeader[1]; + + size_t commentLen = strlen(*commentsP) + strlen(commentLine) + 1; + + assert(commentHeader[0] == '#'); + + REALLOCARRAY(*commentsP, commentLen); + + if (*commentsP == NULL) + pm_error("Couldn't get storage for %u characters of comments from " + "the PAM header", commentLen); + + strcat(*commentsP, commentLine); +} + + + +static void +disposeOfComments(const struct pam * const pamP, + const char * const comments) { + + const char ** const retP = pamCommentP(pamP); + + if (retP) + *retP = comments; + else + strfree(comments); +} + + + +static void +readpaminitrest(struct pam * const pamP) { +/*---------------------------------------------------------------------------- + Read the rest of the PAM header (after the first line -- the magic + number line). Fill in all the information in *pamP. +-----------------------------------------------------------------------------*/ + struct headerSeen headerSeen; + char * comments; + + headerSeen.width = FALSE; + headerSeen.height = FALSE; + headerSeen.depth = FALSE; + headerSeen.maxval = FALSE; + headerSeen.endhdr = FALSE; + + pamP->tuple_type[0] = '\0'; + + comments = strdup(""); + + { + int c; + /* Read off rest of 1st line -- probably just the newline after the + magic number + */ + while ((c = getc(pamP->file)) != -1 && c != '\n'); + } + + while (!headerSeen.endhdr) { + char buffer[256]; + char * rc; + rc = fgets(buffer, sizeof(buffer), pamP->file); + if (rc == NULL) + pm_error("EOF or error reading file while trying to read the " + "PAM header"); + else { + buffer[256-1-1] = '\n'; /* In case fgets() truncated */ + if (buffer[0] == '#') + appendComment(&comments, buffer); + else if (stripeq(buffer, "")); + /* Ignore it; it's a blank line */ + else + process_header_line(buffer, pamP, &headerSeen); + } + } + + disposeOfComments(pamP, comments); + + if (!headerSeen.height) + pm_error("No HEIGHT header line in PAM header"); + if (!headerSeen.width) + pm_error("No WIDTH header line in PAM header"); + if (!headerSeen.depth) + pm_error("No DEPTH header line in PAM header"); + if (!headerSeen.maxval) + pm_error("No MAXVAL header line in PAM header"); + + if (pamP->height == 0) + pm_error("HEIGHT value is zero in PAM header"); + if (pamP->width == 0) + pm_error("WIDTH value is zero in PAM header"); + if (pamP->depth == 0) + pm_error("DEPTH value is zero in PAM header"); + if (pamP->maxval == 0) + pm_error("MAXVAL value is zero in PAM header"); + if (pamP->maxval > PAM_OVERALL_MAXVAL) + pm_error("MAXVAL value (%lu) in PAM header is greater than %u", + pamP->maxval, PAM_OVERALL_MAXVAL); +} + + + +void +pnm_readpaminitrestaspnm(FILE * const fileP, + int * const colsP, + int * const rowsP, + gray * const maxvalP, + int * const formatP) { +/*---------------------------------------------------------------------------- + Read the rest of the PAM header (after the first line) and return + information as if it were PPM or PGM. + + Die if it isn't a PAM of the sort we can treat as PPM or PGM. +-----------------------------------------------------------------------------*/ + struct pam pam; + + pam.size = PAM_STRUCT_SIZE(tuple_type); + pam.file = fileP; + pam.len = sizeof(struct pam); + pam.format = PAM_FORMAT; + + readpaminitrest(&pam); + + + /* A PAM raster of depth 1 is identical to a PGM raster. A PAM + raster of depth 3 is identical to PPM raster. So + ppm_readppmrow() will be able to read the PAM raster as long as + the format it thinks it is (PGM or PPM) corresponds to the PAM + depth. Similar for pgm_readpgmrow(). + */ + switch (pam.depth) { + case 3: + *formatP = RPPM_FORMAT; + break; + case 1: + *formatP = RPGM_FORMAT; + break; + 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; + *maxvalP = (gray)pam.maxval; +} + + + +unsigned int +pnm_bytespersample(sample const maxval) { + + assert(sizeof(maxval) * 8 <= 32); + + if (maxval >> 8 == 0) return 1; + else if (maxval >> 16 == 0) return 2; + else if (maxval >> 24 == 0) return 3; + else return 4; +} + + + +void +pnm_readpaminit(FILE * const file, + struct pam * const pamP, + int const size) { + + if (size < PAM_STRUCT_SIZE(tuple_type)) + pm_error("pam object passed to pnm_readpaminit() is too small. " + "It must be large\n" + "enough to hold at least up to the " + "'tuple_type' member, but according\n" + "to the 'size' argument, it is only %d bytes long.", + size); + + pamP->size = size; + pamP->file = file; + pamP->len = MIN(pamP->size, sizeof(struct pam)); + + if (size >= PAM_STRUCT_SIZE(allocation_depth)) + pamP->allocation_depth = 0; + + /* Get magic number. */ + pamP->format = pm_readmagicnumber(file); + + switch (PAM_FORMAT_TYPE(pamP->format)) { + case PAM_TYPE: + readpaminitrest(pamP); + break; + case PPM_TYPE: { + pixval maxval; + ppm_readppminitrest(pamP->file, &pamP->width, &pamP->height, &maxval); + pamP->maxval = (sample) maxval; + pamP->depth = 3; + strcpy(pamP->tuple_type, PAM_PPM_TUPLETYPE); + if (pamCommentP(pamP)) + *pamP->comment_p = strdup(""); + } break; + + case PGM_TYPE: { + gray maxval; + pgm_readpgminitrest(pamP->file, &pamP->width, &pamP->height, &maxval); + pamP->maxval = (sample) maxval; + pamP->depth = 1; + strcpy(pamP->tuple_type, PAM_PGM_TUPLETYPE); + if (pamCommentP(pamP)) + *pamP->comment_p = strdup(""); + } break; + + case PBM_TYPE: + pbm_readpbminitrest(pamP->file, &pamP->width,&pamP->height); + pamP->maxval = (sample) 1; + pamP->depth = 1; + strcpy(pamP->tuple_type, PAM_PBM_TUPLETYPE); + if (pamCommentP(pamP)) + *pamP->comment_p = strdup(""); + break; + + default: + pm_error("bad magic number - not a PAM, PPM, PGM, or PBM file"); + } + + pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval); + pamP->plainformat = FALSE; + /* See below for complex explanation of why this is FALSE. */ + + validateComputableSize(pamP); +} + + + +static void +writeComments(const struct pam * const pamP) { +/*---------------------------------------------------------------------------- + Write comments for a PAM header, insofar as *pamP specifies comments. +-----------------------------------------------------------------------------*/ + const char ** const commentP = pamCommentP(pamP); + + if (commentP) { + const char * const comment = *commentP; + + const char * p; + bool startOfLine; + + for (p = &comment[0], startOfLine = TRUE; *p; ++p) { + if (startOfLine) + fputc('#', pamP->file); + + fputc(*p, pamP->file); + + if (*p == '\n') + startOfLine = TRUE; + else + startOfLine = FALSE; + } + if (!startOfLine) + fputc('\n', pamP->file); + } +} + + + +/* About the 'plainformat' member on image input operations: + + 'plainformat' is meaningless when reading an image, but we always + set it FALSE anyway. + + That's because it is common for a program to copy a pam structure + used for input as a pam structure for output, and just update the + few fields it cares about -- mainly 'file'. We want a program like + that to write a raw format image, and 'plainformat' in an output + pam structure is what determines whether it is raw or plain. So we + set it false here so that it is false in the copied output pam + structure. + + Before 10.32, we set 'plainformat' according to the + plainness of the input image, and thought it was a good + idea for a program that reads a plain format input image to + write a plain format output image. But that is not what + the older libnetpbm facilities (e.g. pnm_writepnm()) do, so + for compatibility, the pam facility shouldn't either. This + came to light as we converted programs from the pnm/pbm/ppm + facilities to pam. +*/ + +void +pnm_writepaminit(struct pam * const pamP) { + + const char * tupleType; + + if (pamP->size < pamP->len) + pm_error("pam object passed to pnm_writepaminit() is smaller " + "(%d bytes, according to its 'size' element) " + "than the amount of data in it " + "(%d bytes, according to its 'len' element).", + pamP->size, pamP->len); + + if (pamP->len < 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); + + if (pamP->maxval > PAM_OVERALL_MAXVAL) + pm_error("maxval (%lu) passed to pnm_writepaminit() " + "is greater than %u", pamP->maxval, PAM_OVERALL_MAXVAL); + + if (pamP->len < PAM_STRUCT_SIZE(tuple_type)) + tupleType = ""; + else + tupleType = pamP->tuple_type; + + pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval); + + switch (PAM_FORMAT_TYPE(pamP->format)) { + case PAM_TYPE: + if (pm_plain_output) + pm_error("There is no plain version of PAM. -plain option " + "is not allowed"); + fprintf(pamP->file, "P7\n"); + writeComments(pamP); + fprintf(pamP->file, "WIDTH %u\n", (unsigned)pamP->width); + fprintf(pamP->file, "HEIGHT %u\n", (unsigned)pamP->height); + fprintf(pamP->file, "DEPTH %u\n", pamP->depth); + fprintf(pamP->file, "MAXVAL %lu\n", pamP->maxval); + if (!stripeq(tupleType, "")) + fprintf(pamP->file, "TUPLTYPE %s\n", pamP->tuple_type); + fprintf(pamP->file, "ENDHDR\n"); + break; + case PPM_TYPE: + /* The depth must be exact, because pnm_writepamrow() is controlled + by it, without regard to format. + */ + if (pamP->depth != 3) + pm_error("pnm_writepaminit() got PPM format, but depth = %d " + "instead of 3, as required for PPM.", + pamP->depth); + if (pamP->maxval > PPM_OVERALLMAXVAL) + pm_error("pnm_writepaminit() got PPM format, but maxval = %ld, " + "which exceeds the maximum allowed for PPM: %d", + pamP->maxval, PPM_OVERALLMAXVAL); + ppm_writeppminit(pamP->file, pamP->width, pamP->height, + (pixval) pamP->maxval, pamP->plainformat); + break; + + case PGM_TYPE: + if (pamP->depth != 1) + pm_error("pnm_writepaminit() got PGM format, but depth = %d " + "instead of 1, as required for PGM.", + pamP->depth); + if (pamP->maxval > PGM_OVERALLMAXVAL) + pm_error("pnm_writepaminit() got PGM format, but maxval = %ld, " + "which exceeds the maximum allowed for PGM: %d", + pamP->maxval, PGM_OVERALLMAXVAL); + pgm_writepgminit(pamP->file, pamP->width, pamP->height, + (gray) pamP->maxval, pamP->plainformat); + break; + + case PBM_TYPE: + if (pamP->depth != 1) + pm_error("pnm_writepaminit() got PBM format, but depth = %d " + "instead of 1, as required for PBM.", + pamP->depth); + if (pamP->maxval != 1) + pm_error("pnm_writepaminit() got PBM format, but maxval = %ld " + "instead of 1, as required for PBM.", pamP->maxval); + pbm_writepbminit(pamP->file, pamP->width, pamP->height, + pamP->plainformat); + break; + + default: + pm_error("Invalid format passed to pnm_writepaminit(): %d", + pamP->format); + } +} + + + +void +pnm_checkpam(const struct pam * const pamP, + enum pm_check_type const checkType, + enum pm_check_code * const retvalP) { + + if (checkType != PM_CHECK_BASIC) { + if (retvalP) *retvalP = PM_CHECK_UNKNOWN_TYPE; + } else switch (PAM_FORMAT_TYPE(pamP->format)) { + case PAM_TYPE: { + pm_filepos const need_raster_size = + pamP->width * pamP->height * pamP->depth * pamP->bytes_per_sample; + pm_check(pamP->file, checkType, need_raster_size, retvalP); + } + break; + case PPM_TYPE: + pgm_check(pamP->file, checkType, pamP->format, + pamP->width, pamP->height, pamP->maxval, retvalP); + break; + case PGM_TYPE: + pgm_check(pamP->file, checkType, pamP->format, + pamP->width, pamP->height, pamP->maxval, retvalP); + break; + case PBM_TYPE: + pbm_check(pamP->file, checkType, pamP->format, + pamP->width, pamP->height, retvalP); + break; + default: + if (retvalP) *retvalP = PM_CHECK_UNCHECKABLE; + } +} + + + +void +pnm_maketuplergb(const struct pam * const pamP, + tuple const tuple) { + + if (allocationDepth(pamP) < 3) + pm_error("allocation depth %u passed to pnm_maketuplergb(). " + "Must be at least 3.", allocationDepth(pamP)); + + if (pamP->depth < 3) + tuple[2] = tuple[1] = tuple[0]; +} + + + +void +pnm_makerowrgb(const struct pam * const pamP, + tuple * const tuplerow) { + + if (pamP->depth < 3) { + unsigned int col; + + if (allocationDepth(pamP) < 3) + pm_error("allocation depth %u passed to pnm_makerowrgb(). " + "Must be at least 3.", allocationDepth(pamP)); + + if (strncmp(pamP->tuple_type, "RGB", 3) != 0) { + for (col = 0; col < pamP->width; ++col) { + tuple const thisTuple = tuplerow[col]; + thisTuple[2] = thisTuple[1] = thisTuple[0]; + } + } + } +} + + + +void +pnm_makearrayrgb(const struct pam * const pamP, + tuple ** const tuples) { + + if (pamP->depth < 3) { + unsigned int row; + if (allocationDepth(pamP) < 3) + pm_error("allocation depth %u passed to pnm_makearrayrgb(). " + "Must be at least 3.", allocationDepth(pamP)); + + for (row = 0; row < pamP->height; ++row) { + tuple * const tuplerow = tuples[row]; + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + tuple const thisTuple = tuplerow[col]; + thisTuple[2] = thisTuple[1] = thisTuple[0]; + } + } + } +} + + + +void +pnm_getopacity(const struct pam * const pamP, + bool * const haveOpacityP, + unsigned int * const opacityPlaneP) { + + /* Design note; If use of this information proliferates, we should + probably add it to struct pam as convenience values analogous to + bytes_per_sample. + */ + if (strcmp(pamP->tuple_type, "RGB_ALPHA") == 0) { + *haveOpacityP = TRUE; + *opacityPlaneP = PAM_TRN_PLANE; + } else if (strcmp(pamP->tuple_type, "GRAYSCALE_ALPHA") == 0) { + *haveOpacityP = TRUE; + *opacityPlaneP = PAM_GRAY_TRN_PLANE; + } else + *haveOpacityP = FALSE; +} + + + +/*============================================================================= + pm_system() Standard Input feeder and Standard Output accepter functions. +=============================================================================*/ + +void +pm_feed_from_pamtuples(int const pipeToFeedFd, + void * const feederParm) { + + struct pamtuples * const inputTuplesP = feederParm; + + struct pam outpam; + + outpam = *inputTuplesP->pamP; + outpam.file = fdopen(pipeToFeedFd, "w"); + + /* The following signals (and normally kills) the process with + SIGPIPE if the pipe does not take all 'inputLength' bytes. + */ + pnm_writepam(&outpam, *inputTuplesP->tuplesP); + + pm_close(outpam.file); +} + + + +void +pm_accept_to_pamtuples(int const pipeToSuckFd, + void * const accepterParm ) { + + struct pamtuples * const outputTuplesP = accepterParm; + + struct pam inpam; + + *outputTuplesP->tuplesP = + pnm_readpam(fdopen(pipeToSuckFd, "r"), &inpam, sizeof(inpam)); + + pm_close(inpam.file); +} diff --git a/lib/libpam.h b/lib/libpam.h new file mode 100644 index 00000000..4b8e6693 --- /dev/null +++ b/lib/libpam.h @@ -0,0 +1,17 @@ +/* This is the intra-libnetpbm interface header file for libpam.c +*/ + +#ifndef LIBPAM_H_INCLUDED +#define LIBPAM_H_INCLUDED + +#include "pam.h" + +void +pnm_readpaminitrestaspnm(FILE * const fileP, + int * const colsP, + int * const rowsP, + gray * const maxvalP, + int * const formatP); + +#endif + diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c new file mode 100644 index 00000000..e11415ca --- /dev/null +++ b/lib/libpamcolor.c @@ -0,0 +1,102 @@ +/*---------------------------------------------------------------------------- + libpamcolor.c +------------------------------------------------------------------------------ + These are the library functions, which belong in the libnetpbm library, + that deal with colors in the PAM image format. +-----------------------------------------------------------------------------*/ + +/* See libpbm.c for the complicated explanation of this 32/64 bit file + offset stuff. +*/ +#define _FILE_OFFSET_BITS 64 +#define _LARGE_FILES + +#include + +#include "pm_c_util.h" +#include "pam.h" +#include "ppm.h" + + +tuple +pnm_parsecolor(const char * const colorname, + sample const maxval) { + + tuple retval; + pixel color; + struct pam pam; + + pam.len = PAM_STRUCT_SIZE(bytes_per_sample); + pam.depth = 3; + pam.maxval = maxval; + pam.bytes_per_sample = pnm_bytespersample(maxval); + + retval = pnm_allocpamtuple(&pam); + + color = ppm_parsecolor(colorname, maxval); + + retval[PAM_RED_PLANE] = PPM_GETR(color); + retval[PAM_GRN_PLANE] = PPM_GETG(color); + retval[PAM_BLU_PLANE] = PPM_GETB(color); + + return retval; +} + + + +double pnm_lumin_factor[3] = {PPM_LUMINR, PPM_LUMING, PPM_LUMINB}; + +void +pnm_YCbCrtuple(tuple const tuple, + double * const YP, + double * const CbP, + double * const CrP) { +/*---------------------------------------------------------------------------- + Assuming that the tuple 'tuple' is of tupletype RGB, return the + Y/Cb/Cr representation of the color represented by the tuple. +-----------------------------------------------------------------------------*/ + int const red = (int)tuple[PAM_RED_PLANE]; + int const grn = (int)tuple[PAM_GRN_PLANE]; + int const blu = (int)tuple[PAM_BLU_PLANE]; + + *YP = (+ PPM_LUMINR * red + PPM_LUMING * grn + PPM_LUMINB * blu); + *CbP = (- 0.16874 * red - 0.33126 * grn + 0.50000 * blu); + *CrP = (+ 0.50000 * red - 0.41869 * grn - 0.08131 * blu); +} + + + +void +pnm_YCbCr_to_rgbtuple(const struct pam * const pamP, + tuple const tuple, + double const Y, + double const Cb, + double const Cr, + int * const overflowP) { + + double rgb[3]; + unsigned int plane; + bool overflow; + + rgb[PAM_RED_PLANE] = Y + 1.4022 * Cr + .5; + rgb[PAM_GRN_PLANE] = Y - 0.7145 * Cr - 0.3456 * Cb + .5; + rgb[PAM_BLU_PLANE] = Y + 1.7710 * Cb + .5; + + overflow = FALSE; /* initial assumption */ + + for (plane = 0; plane < 3; ++plane) { + if (rgb[plane] > pamP->maxval) { + overflow = TRUE; + tuple[plane] = pamP->maxval; + } else if (rgb[plane] < 0.0) { + overflow = TRUE; + tuple[plane] = 0; + } else + tuple[plane] = (sample)rgb[plane]; + } + if (overflowP) + *overflowP = overflow; +} + + + diff --git a/lib/libpammap.c b/lib/libpammap.c new file mode 100644 index 00000000..9e90ade0 --- /dev/null +++ b/lib/libpammap.c @@ -0,0 +1,662 @@ +/****************************************************************************** + libpammap.c +******************************************************************************* + + These are functions that deal with tuple hashes and tuple tables. + + Both tuple hashes and tuple tables let you associate an arbitrary + integer with a tuple value. A tuple hash lets you look up the one + integer value (if any) associated with a given tuple value, having + the low memory and execution time characteristics of a hash table. + A tuple table lets you scan all the values, being a table of elements + that consist of an ordered pair of a tuple value and integer. + +******************************************************************************/ + +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "pam.h" +#include "pammap.h" + + +#define HASH_SIZE 20023 + +unsigned int +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 hash; + const unsigned int hash_factor[] = {33023, 30013, 27011}; + + hash = 0; /* initial value */ + for (i = 0; i < MIN(pamP->depth, 3); ++i) { + hash += tuple[i] * hash_factor[i]; /* May overflow */ + } + hash %= HASH_SIZE; + return hash; +} + + + + +tuplehash +pnm_createtuplehash(void) { +/*---------------------------------------------------------------------------- + Create an empty tuple hash -- i.e. a hash table of zero length hash chains. +-----------------------------------------------------------------------------*/ + tuplehash retval; + unsigned int i; + + MALLOCARRAY(retval, HASH_SIZE); + + if (retval == NULL) + pm_error("Out of memory allocating tuple hash of size %u", + HASH_SIZE); + + for (i = 0; i < HASH_SIZE; ++i) + retval[i] = NULL; + + return retval; +} + + + +void +pnm_destroytuplehash(tuplehash const tuplehash) { + + int i; + + /* Free the chains */ + + for (i = 0; i < HASH_SIZE; ++i) { + struct tupleint_list_item * p; + struct tupleint_list_item * next; + + /* Walk this chain, freeing each element */ + for (p = tuplehash[i]; p; p = next) { + next = p->next; + + free(p); + } + } + + /* Free the table of chains */ + free(tuplehash); +} + + + + +static struct tupleint_list_item * +allocTupleIntListItem(struct pam * const pamP) { + + + /* This is complicated by the fact that the last element of a + tupleint_list_item is of variable length, because the last element + of _it_ is of variable length + */ + struct tupleint_list_item * retval; + + unsigned int const size = + sizeof(*retval) - sizeof(retval->tupleint.tuple) + + pamP->depth * sizeof(sample); + + retval = (struct tupleint_list_item *) malloc(size); + + return retval; +} + + + +void +pnm_addtotuplehash(struct pam * const pamP, + tuplehash const tuplehash, + tuple const tupletoadd, + int const value, + int * const fitsP) { +/*---------------------------------------------------------------------------- + Add a tuple value to the hash -- assume it isn't already there. + + Allocate new space for the tuple value and the hash chain element. + + If we can't allocate space for the new hash chain element, don't + change anything and return *fitsP = FALSE; +-----------------------------------------------------------------------------*/ + struct tupleint_list_item * const listItemP = allocTupleIntListItem(pamP); + if (listItemP == NULL) + *fitsP = FALSE; + else { + unsigned int const hashvalue = pnm_hashtuple(pamP, tupletoadd); + + *fitsP = TRUE; + + pnm_assigntuple(pamP, listItemP->tupleint.tuple, tupletoadd); + listItemP->tupleint.value = value; + listItemP->next = tuplehash[hashvalue]; + tuplehash[hashvalue] = listItemP; + } +} + + + +void +pnm_lookuptuple(struct pam * const pamP, + const tuplehash tuplehash, + const tuple searchval, + int * const foundP, + int * const retvalP) { + + unsigned int const hashvalue = pnm_hashtuple(pamP, searchval); + struct tupleint_list_item * p; + struct tupleint_list_item * found; + + found = NULL; /* None found yet */ + for (p = tuplehash[hashvalue]; p && !found; p = p->next) + if (pnm_tupleequal(pamP, p->tupleint.tuple, searchval)) { + found = p; + } + + if (found) { + *foundP = TRUE; + *retvalP = found->tupleint.value; + } else + *foundP = FALSE; +} + + + +static void +addColorOccurrenceToHash(tuple const color, + tuplehash const tuplefreqhash, + struct pam * const pamP, + unsigned int const maxsize, + unsigned int * const sizeP, + bool * const fullP) { + + unsigned int const hashvalue = pnm_hashtuple(pamP, color); + + struct tupleint_list_item *p; + + for (p = tuplefreqhash[hashvalue]; + p && !pnm_tupleequal(pamP, p->tupleint.tuple, color); + p = p->next); + + if (p) { + /* It's in the hash; just tally one more occurence */ + ++p->tupleint.value; + *fullP = FALSE; + } else { + /* It's not in the hash yet, so add it (if allowed) */ + ++(*sizeP); + if (maxsize > 0 && *sizeP > maxsize) + *fullP = TRUE; + else { + *fullP = FALSE; + p = allocTupleIntListItem(pamP); + if (p == NULL) + pm_error("out of memory computing hash table"); + pnm_assigntuple(pamP, p->tupleint.tuple, color); + p->tupleint.value = 1; + p->next = tuplefreqhash[hashvalue]; + tuplefreqhash[hashvalue] = p; + } + } +} + + + +void +pnm_addtuplefreqoccurrence(struct pam * const pamP, + tuple const value, + tuplehash const tuplefreqhash, + int * const firstOccurrenceP) { + + unsigned int const hashvalue = pnm_hashtuple(pamP, value); + + struct tupleint_list_item * p; + + for (p = tuplefreqhash[hashvalue]; + p && !pnm_tupleequal(pamP, p->tupleint.tuple, value); + p = p->next); + + if (p) { + /* It's in the hash; just tally one more occurence */ + ++p->tupleint.value; + *firstOccurrenceP = FALSE; + } else { + struct tupleint_list_item * p; + + /* It's not in the hash yet, so add it */ + *firstOccurrenceP = TRUE; + + p = allocTupleIntListItem(pamP); + if (p == NULL) + pm_error("out of memory computing hash table"); + + pnm_assigntuple(pamP, p->tupleint.tuple, value); + p->tupleint.value = 1; + p->next = tuplefreqhash[hashvalue]; + tuplefreqhash[hashvalue] = p; + } +} + + + +static void +computehashrecoverable(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + sample const newMaxval, + unsigned int * const sizeP, + tuplehash * const tuplefreqhashP, + tuple ** const rowbufferP, + tuple * const colorP) { +/*---------------------------------------------------------------------------- + This is computetuplefreqhash(), only it leaves a trail so that if it + happens to longjmp out because of a failed memory allocation, the + setjmp'er can cleanup whatever it had done so far. +-----------------------------------------------------------------------------*/ + unsigned int row; + struct pam freqPam; + bool full; + + freqPam = *pamP; + freqPam.maxval = newMaxval; + + *tuplefreqhashP = pnm_createtuplehash(); + *sizeP = 0; /* initial value */ + + *rowbufferP = pnm_allocpamrow(pamP); + + *colorP = pnm_allocpamtuple(&freqPam); + + full = FALSE; /* initial value */ + + /* Go through the entire raster, building a hash table of + tuple values. + */ + for (row = 0; row < pamP->height && !full; ++row) { + int col; + const tuple * tuplerow; /* The row of tuples we are processing */ + + if (tupleArray) + tuplerow = tupleArray[row]; + else { + pnm_readpamrow(pamP, *rowbufferP); + tuplerow = *rowbufferP; + } + for (col = 0; col < pamP->width && !full; ++col) { + pnm_scaletuple(pamP, *colorP, tuplerow[col], freqPam.maxval); + addColorOccurrenceToHash( + *colorP, *tuplefreqhashP, &freqPam, maxsize, sizeP, &full); + } + } + + pnm_freepamtuple(*colorP); *colorP = NULL; + pnm_freepamrow(*rowbufferP); *rowbufferP = NULL; + + if (full) { + pnm_destroytuplehash(*tuplefreqhashP); + *tuplefreqhashP = NULL; + } +} + + + +static tuplehash +computetuplefreqhash(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + sample const newMaxval, + unsigned int * const sizeP) { +/*---------------------------------------------------------------------------- + Compute a tuple frequency hash from a PAM. This is a hash that gives + you the number of times a given tuple value occurs in the PAM. You can + supply the input PAM in one of two ways: + + 1) a two-dimensional array of tuples tupleArray[][]; In this case, + 'tupleArray' is non-NULL. + + 2) an open PAM file, positioned to the raster. In this case, + 'tupleArray' is NULL. *pamP contains the file descriptor. + + We return with the file still open and its position undefined. + + In either case, *pamP contains parameters of the tuple array. + + Return the number of unique tuple values found as *sizeP. + + 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 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 + as sample value 25 in the hash. +-----------------------------------------------------------------------------*/ + tuplehash tuplefreqhash; + tuple * rowbuffer; /* malloc'ed */ + /* Buffer for a row read from the input file; undefined (but still + allocated) if input is not from a file. + */ + tuple color; + /* The color currently being added, scaled to the new maxval */ + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + /* Initialize to "none" for purposes of error recovery */ + tuplefreqhash = NULL; + 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 (color) + pnm_freepamtuple(color); + if (rowbuffer) + pnm_freepamrow(rowbuffer); + if (tuplefreqhash) + pnm_destroytuplehash(tuplefreqhash); + pm_longjmp(); + } + return tuplefreqhash; +} + + + +tuplehash +pnm_computetuplefreqhash(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + unsigned int * const sizeP) { +/*---------------------------------------------------------------------------- + Compute the tuple frequency hash for the tuple array tupleArray[][]. +-----------------------------------------------------------------------------*/ + return computetuplefreqhash(pamP, tupleArray, maxsize, pamP->maxval, + sizeP); +} + + + +tupletable +pnm_alloctupletable(const struct pam * const pamP, + unsigned int const size) { + + tupletable retval; + + if (UINT_MAX / sizeof(struct tupleint) < size) + pm_error("size %u is too big for arithmetic", size); + else { + unsigned int const mainTableSize = size * sizeof(struct tupleint *); + unsigned int const tupleIntSize = + sizeof(struct tupleint) - sizeof(sample) + + pamP->depth * sizeof(sample); + + /* To save the enormous amount of time it could take to allocate + each individual tuple, we do a trick here and allocate everything + as a single malloc block and suballocate internally. + */ + if ((UINT_MAX - mainTableSize) / tupleIntSize < size) + pm_error("size %u is too big for arithmetic", size); + else { + void * pool; + unsigned int i; + + pool = malloc(mainTableSize + size * tupleIntSize); + + retval = (tupletable) pool; + + for (i = 0; i < size; ++i) + retval[i] = (struct tupleint *) + ((char*)pool + mainTableSize + i * tupleIntSize); + } + } + return retval; +} + + + +void +pnm_freetupletable(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 + tupletable, but all the samples to which it points (e.g. + tupletable[0].tuple[0]) + */ + + free(tupletable); +} + + + +void +pnm_freetupletable2(struct pam * const pamP, + tupletable2 const tupletable) { + + pnm_freetupletable(pamP, tupletable.table); +} + + + +static tupletable +tuplehashtotable(const struct pam * const pamP, + tuplehash const tuplehash, + unsigned int const allocsize) { +/*---------------------------------------------------------------------------- + Create a tuple table containing the info from a tuple hash. Allocate + space in the table for 'allocsize' elements even if there aren't that + many tuple values in the input hash. That's so the caller has room + for expansion. + + Caller must ensure that 'allocsize' is at least as many tuple values + as there are in the input hash. + + We allocate new space for all the table contents; there are no pointers + in the table to tuples or anything else in existing space. +-----------------------------------------------------------------------------*/ + tupletable tupletable; + + tupletable = pnm_alloctupletable(pamP, allocsize); + + if (tupletable != NULL) { + unsigned int i, j; + /* Loop through the hash table. */ + j = 0; + for (i = 0; i < HASH_SIZE; ++i) { + /* Walk this hash chain */ + struct tupleint_list_item * p; + for (p = tuplehash[i]; p; p = p->next) { + assert(j < allocsize); + tupletable[j]->value = p->tupleint.value; + pnm_assigntuple(pamP, tupletable[j]->tuple, p->tupleint.tuple); + ++j; + } + } + } + return tupletable; +} + + + +tupletable +pnm_tuplehashtotable(const struct pam * const pamP, + tuplehash const tuplehash, + unsigned int const allocsize) { + + tupletable tupletable; + + tupletable = tuplehashtotable(pamP, tuplehash, allocsize); + + if (tupletable == NULL) + pm_error("out of memory generating tuple table"); + + return tupletable; +} + + + +tuplehash +pnm_computetupletablehash(struct pam * const pamP, + tupletable const tupletable, + unsigned int const tupletableSize) { +/*---------------------------------------------------------------------------- + Create a tuple hash containing indices into the tuple table + 'tupletable'. The hash index for the hash is the value of a tuple; + the hash value is the tuple table index for the element in the + tuple table that contains that tuple value. + + Assume there are no duplicate tuple values in the tuple table. + + We allocate space for the main hash table and all the elements of the + hash chains. +-----------------------------------------------------------------------------*/ + tuplehash tupletablehash; + unsigned int i; + bool fits; + + tupletablehash = pnm_createtuplehash(); + + fits = TRUE; /* initial assumption */ + for (i = 0; i < tupletableSize && fits; ++i) { + pnm_addtotuplehash(pamP, tupletablehash, + tupletable[i]->tuple, i, &fits); + } + if (!fits) { + pnm_destroytuplehash(tupletablehash); + pm_error("Out of memory computing tuple hash from tuple table"); + } + return tupletablehash; +} + + + +tupletable +pnm_computetuplefreqtable2(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + sample const newMaxval, + unsigned int * const countP) { +/*---------------------------------------------------------------------------- + Compute a tuple frequency table from a PAM image. This is an + array that tells how many times each tuple value occurs in the + image. + + Except for the format of the output, this function is the same as + computetuplefreqhash(). + + If there are more than 'maxsize' unique tuple values in tupleArray[][], + give up. + + Return the array in newly malloc'ed storage. Allocate space for + 'maxsize' entries even if there aren't that many distinct tuple + values in tupleArray[]. That's so the caller has room for + expansion. + + If 'maxsize' is zero, allocate exactly as much space as there are + distinct tuple values in tupleArray[], and don't give up no matter + how many tuple values we find (except, of course, we abort if we + can't get enough memory). + + Return the number of unique tuple values in tupleArray[][] as + *countP. + + + Scale the tuple values to a new maxval of 'newMaxval' before + processing them. E.g. if the input has maxval 100 and 'newMaxval' + is 50, and a particular tuple has sample value 50, it would be + listed as sample value 25 in the output table. This makes the + output table smaller and the processing time less. +-----------------------------------------------------------------------------*/ + tuplehash tuplefreqhash; + tupletable tuplefreqtable; + unsigned int uniqueCount; + + tuplefreqhash = computetuplefreqhash(pamP, tupleArray, maxsize, + newMaxval, &uniqueCount); + if (tuplefreqhash == NULL) + tuplefreqtable = NULL; + else { + unsigned int tableSize = (maxsize == 0 ? uniqueCount : maxsize); + assert(tableSize >= uniqueCount); + tuplefreqtable = tuplehashtotable(pamP, tuplefreqhash, tableSize); + pnm_destroytuplehash(tuplefreqhash); + if (tuplefreqtable == NULL) + pm_error("Out of memory generating tuple table"); + } + *countP = uniqueCount; + + return tuplefreqtable; +} + + + +tupletable +pnm_computetuplefreqtable(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + unsigned int * const sizeP) { + + return pnm_computetuplefreqtable2(pamP, tupleArray, maxsize, pamP->maxval, + sizeP); +} + + + +char* +pam_colorname(struct pam * const pamP, + tuple const color, + enum colornameFormat const format) { + + unsigned int r, g, b; + FILE* f; + static char colorname[200]; + + r = pnm_scalesample(color[PAM_RED_PLANE], pamP->maxval, 255); + g = pnm_scalesample(color[PAM_GRN_PLANE], pamP->maxval, 255); + b = pnm_scalesample(color[PAM_BLU_PLANE], pamP->maxval, 255); + + f = pm_openColornameFile(NULL, format == PAM_COLORNAME_ENGLISH); + if (f != NULL) { + unsigned int best_diff; + bool done; + + best_diff = 32767; + done = FALSE; + while (!done) { + struct colorfile_entry const ce = pm_colorget(f); + if (ce.colorname) { + unsigned int const this_diff = + abs((int)r - (int)ce.r) + + abs((int)g - (int)ce.g) + + abs((int)b - (int)ce.b); + + if (this_diff < best_diff) { + best_diff = this_diff; + strcpy(colorname, ce.colorname); + } + } else + done = TRUE; + } + fclose(f); + if (best_diff != 32767 && + (best_diff == 0 || format == PAM_COLORNAME_ENGLISH)) + return colorname; + } + + /* Color lookup failed, but caller is willing to take an X11-style + hex specifier, so return that. + */ + sprintf(colorname, "#%02x%02x%02x", r, g, b); + return colorname; +} diff --git a/lib/libpamn.c b/lib/libpamn.c new file mode 100644 index 00000000..c7610100 --- /dev/null +++ b/lib/libpamn.c @@ -0,0 +1,542 @@ +/*---------------------------------------------------------------------------- + libpamn.c +------------------------------------------------------------------------------ + These are the library functions, which belong in the libnetpbm library, + that deal with the PAM image format via maxval-normalized, floating point + sample values. +-----------------------------------------------------------------------------*/ + +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "pam.h" +#include "fileio.h" +#include "pm_gamma.h" + +#define EPSILON 1e-7 + + + +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 int bytes_per_tuple = pamP->depth * sizeof(samplen); + tuplen * tuplerown; + + /* The tuple row data structure starts with 'width' pointers to + the tuples, immediately followed by the 'width' tuples + themselves. Each tuple consists of 'depth' samples. + */ + + 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)); + + { + /* Now we initialize the pointers to the individual tuples to make this + a regulation C two dimensional array. + */ + + char *p; + int i; + + p = (char*) (tuplerown + pamP->width); /* location of Tuple 0 */ + for (i = 0; i < pamP->width; i++) { + tuplerown[i] = (tuplen) p; + p += bytes_per_tuple; + } + } + return(tuplerown); +} + + + +void +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 + 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) { + 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; + pbm_freerow(bitrow); + } 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); + + pnm_readpamrow(pamP, tuplerow); + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + tuplenrow[col][plane] = tuplerow[col][plane] * scaler; + } + 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) { + 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; + pbm_writepbmrow(pamP->file, bitrow, pamP->width, + pamP->format == PBM_FORMAT); + pbm_freerow(bitrow); + } else { + tuple * tuplerow; + int col; + + tuplerow = pnm_allocpamrow(pamP); + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + tuplerow[col][plane] = (sample) + (tuplenrow[col][plane] * pamP->maxval + 0.5); + } + pnm_writepamrow(pamP, tuplerow); + pnm_freepamrow(tuplerow); + } +} + + + +tuplen ** +pnm_allocpamarrayn(const struct pam * const pamP) { + + tuplen **tuplenarray; + int row; + + /* If the speed of this is ever an issue, it might be sped up a little + by allocating one large chunk. + */ + + 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); + } + return(tuplenarray); +} + + + +void +pnm_freepamarrayn(tuplen ** const tuplenarray, + const struct pam * const pamP) { + + int row; + for (row = 0; row < pamP->height; row++) + pnm_freepamrown(tuplenarray[row]); + + free(tuplenarray); +} + + + +tuplen** +pnm_readpamn(FILE * const file, + struct pam * const pamP, + int const size) { + + tuplen **tuplenarray; + int row; + + pnm_readpaminit(file, pamP, size); + + tuplenarray = pnm_allocpamarrayn(pamP); + + for (row = 0; row < pamP->height; row++) + pnm_readpamrown(pamP, tuplenarray[row]); + + return(tuplenarray); +} + + + +void +pnm_writepamn(struct pam * const pamP, + tuplen ** const tuplenarray) { + + int row; + + pnm_writepaminit(pamP); + + for (row = 0; row < pamP->height; row++) + pnm_writepamrown(pamP, tuplenarray[row]); +} + + + +void +pnm_normalizetuple(struct pam * const pamP, + tuple const tuple, + tuplen const tuplen) { + + unsigned int plane; + + for (plane = 0; plane < pamP->depth; ++plane) + tuplen[plane] = (samplen)tuple[plane] / pamP->maxval; +} + + + +void +pnm_unnormalizetuple(struct pam * const pamP, + tuplen const tuplen, + tuple const tuple) { + + unsigned int plane; + + for (plane = 0; plane < pamP->depth; ++plane) + tuple[plane] = tuplen[plane] * pamP->maxval + 0.5; +} + + + +void +pnm_normalizeRow(struct pam * const pamP, + const tuple * const tuplerow, + const pnm_transformMap * const transform, + tuplen * const tuplenrow) { + + 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. + */ + unsigned int plane; + + for (plane = 0; plane < pamP->depth; ++plane) { + if (transform && transform[plane]) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + sample const sample = tuplerow[col][plane]; + tuplenrow[col][plane] = transform[plane][sample]; + } + } else { + unsigned int col; + for (col = 0; col < pamP->width; ++col) + tuplenrow[col][plane] = tuplerow[col][plane] * scaler; + } + } +} + + + +static sample +reversemap(samplen const samplen, + pnm_transformMap const transformMap, + sample const maxval) { +/*---------------------------------------------------------------------------- + Find the integer sample value that maps to the normalized samplen value + 'samplen' through the map 'transformMap'. We interpret the map as + mapping the value N+1 to all the values transformMap[N] through + transformMap[N+1], and we expect transformMap[N+1] to be greater than + transformMap[N] for all N. +-----------------------------------------------------------------------------*/ + /* Do a binary search, since the values are in sorted (increasing) + order + */ + + sample low, high; + + low = 0; high = maxval; /* Consider whole range to start */ + + while (low < high) { + unsigned int const middle = (low + high) / 2; + + if (samplen < transformMap[middle]) + /* Restrict our consideration to the lower half of the range */ + high = middle; + else + /* Restrict our consideration to the upper half of the range */ + low = middle + 1; + } + return low; +} + + + +void +pnm_unnormalizeRow(struct pam * const pamP, + const tuplen * const tuplenrow, + const pnm_transformMap * const transform, + tuple * const tuplerow) { + + unsigned int plane; + + for (plane = 0; plane < pamP->depth; ++plane) { + if (transform && transform[plane]) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) + tuplerow[col][plane] = + reversemap(tuplenrow[col][plane], + transform[plane], pamP->maxval); + } else { + unsigned int col; + for (col = 0; col < pamP->width; ++col) + tuplerow[col][plane] = + tuplenrow[col][plane] * pamP->maxval + 0.5; + } + } +} + + + +typedef samplen (*gammaFunction)(samplen); + +static void +gammaCommon(struct pam * const pamP, + tuplen * const tuplenrow, + gammaFunction gammafn) { + + unsigned int plane; + unsigned int opacityPlane; + bool haveOpacity; + + pnm_getopacity(pamP, &haveOpacity, &opacityPlane); + + for (plane = 0; plane < pamP->depth; ++plane) { + if (haveOpacity && plane == opacityPlane) { + /* It's an opacity (alpha) plane, which means there is + no gamma adjustment in it. + */ + } else { + unsigned int col; + for (col = 0; col < pamP->width; ++col) + tuplenrow[col][plane] = gammafn(tuplenrow[col][plane]); + } + } +} + + + +void +pnm_gammarown(struct pam * const pamP, + tuplen * const tuplenrow) { + + gammaCommon(pamP, tuplenrow, &pm_gamma709); +} + + + +void +pnm_ungammarown(struct pam * const pamP, + tuplen * const tuplenrow) { + + gammaCommon(pamP, tuplenrow, &pm_ungamma709); +} + + + +enum applyUnapply {OPACITY_APPLY, OPACITY_UNAPPLY}; + +static void +applyopacityCommon(enum applyUnapply const applyUnapply, + struct pam * const pamP, + tuplen * const tuplenrow) { + + unsigned int opacityPlane; + bool haveOpacity; + + pnm_getopacity(pamP, &haveOpacity, &opacityPlane); + + if (haveOpacity) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) { + if (plane != opacityPlane) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + tuplen const thisTuple = tuplenrow[col]; + + switch (applyUnapply) { + case OPACITY_APPLY: + thisTuple[plane] *= thisTuple[opacityPlane]; + break; + case OPACITY_UNAPPLY: + if (thisTuple[opacityPlane] < EPSILON) { + /* There is no foreground here at all. So + the color plane values must be zero and + as output it makes absolutely no + difference what they are (they must be + multiplied by the opacity -- zero -- to + be used). + */ + assert(thisTuple[plane] < EPSILON); + } else + thisTuple[plane] /= thisTuple[opacityPlane]; + break; + } + } + } + } + } +} + + +void +pnm_applyopacityrown(struct pam * const pamP, + tuplen * const tuplenrow) { + + applyopacityCommon(OPACITY_APPLY, pamP, tuplenrow); + +} + + + +void +pnm_unapplyopacityrown(struct pam * const pamP, + tuplen * const tuplenrow) { + + applyopacityCommon(OPACITY_UNAPPLY, pamP, tuplenrow); +} + + + +static void +fillInMap(pnm_transformMap const ungammaTransformMap, + sample const maxval, + float const offset) { + + float const scaler = 1.0/maxval; /* divide only once, it's slow */ + + sample sample; + + /* Fill in the map */ + for (sample = 0; sample <= maxval; ++sample) { + samplen const samplen = (sample + offset) * scaler; + ungammaTransformMap[sample] = pm_ungamma709(samplen); + } +} + + + +static pnm_transformMap * +createUngammaMapOffset(const struct pam * const pamP, + float const offset) { +/*---------------------------------------------------------------------------- + Create a transform table that computes ungamma(arg+offset) for arg + in [0..maxval]; So with offset == 0, you get a function that can be + used in converting integer sample values to normalized ungamma'ed + samplen values. But with offset == 0.5, you get a function that + 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. +-----------------------------------------------------------------------------*/ + pnm_transformMap * retval; + pnm_transformMap ungammaTransformMap; + + MALLOCARRAY(retval, pamP->depth); + + if (retval != NULL) { + MALLOCARRAY(ungammaTransformMap, pamP->maxval+1); + + if (ungammaTransformMap != NULL) { + bool haveOpacity; + unsigned int opacityPlane; + unsigned int plane; + + pnm_getopacity(pamP, &haveOpacity, &opacityPlane); + + for (plane = 0; plane < pamP->depth; ++plane) { + if (haveOpacity && plane == opacityPlane) + retval[plane] = NULL; + else + retval[plane] = ungammaTransformMap; + } + fillInMap(ungammaTransformMap, pamP->maxval, offset); + } else { + free(retval); + retval = NULL; + } + } + return retval; +} + + + +pnm_transformMap * +pnm_createungammatransform(const struct pam * const pamP) { + + return createUngammaMapOffset(pamP, 0.0); +} + + + +pnm_transformMap * +pnm_creategammatransform(const struct pam * const pamP) { + + /* Since we're creating a map to be used backwards (you search for + the normalized value in the array, and the result is the array + index at which you found it), the gamma transform map is almost + identical to the ungamma transform map -- just with a 0.5 offset + to effect rounding. + */ + return createUngammaMapOffset(pamP, 0.5); +} + + + +void +pnm_freegammatransform(const pnm_transformMap * const transform, + const struct pam * const pamP) { + + unsigned int plane; + + for (plane = 0; plane < pamP->depth; ++plane) + if (transform[plane]) + free(transform[plane]); + + free((void*)transform); +} diff --git a/lib/libpamread.c b/lib/libpamread.c new file mode 100644 index 00000000..1b65745a --- /dev/null +++ b/lib/libpamread.c @@ -0,0 +1,277 @@ +/*---------------------------------------------------------------------------- + libpamread.c +------------------------------------------------------------------------------ + These are the library functions, which belong in the libnetpbm library, + that deal with reading the PAM (Portable Arbitrary Format) image format + raster (not the header). +-----------------------------------------------------------------------------*/ + +/* See libpm.c for the complicated explanation of this 32/64 bit file + offset stuff. +*/ +#define _FILE_OFFSET_BITS 64 +#define _LARGE_FILES + +#include +#include +#include + +#include "pam.h" +#include "fileio.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."); + + 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 + ; + } + } + pbm_freerow(bitrow); +} + + + +static void +readPlainNonPbmRow(const struct pam * const pamP, + tuple * const tuplerow) { + + int col; + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + if (tuplerow) + tuplerow[col][plane] = pm_getuint(pamP->file); + else + pm_getuint(pamP->file); /* read data and discard */ + } +} + + + +/* Though it is possible to simplify the bytesToSampleN() and + parsexNBpsRow() functions into a single routine that handles all + sample widths, we value efficiency higher here. Earlier versions + of Netpbm (before 10.25) did that, with a loop, and performance + suffered visibly. +*/ + +static __inline__ sample +bytes2ToSample(unsigned char buff[2]) { + + return (buff[0] << 8) + buff[1]; +} + + + +static __inline__ sample +bytes3ToSample(unsigned char buff[3]) { + return (buff[0] << 16) + (buff[1] << 8) + buff[2]; +} + + + +static __inline__ sample +bytes4ToSample(unsigned char buff[4]) { + + return (buff[0] << 24) + (buff[1] << 16) + (buff[2] << 8) + buff[3]; +} + + + +static void +parse1BpsRow(const struct pam * const pamP, + tuple * const tuplerow, + const unsigned char * const inbuf) { + + int col; + unsigned int bufferCursor; + + bufferCursor = 0; /* initial value */ + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + tuplerow[col][plane]= inbuf[bufferCursor++]; + } +} + + + +static void +parse2BpsRow(const struct pam * const pamP, + tuple * const tuplerow, + const unsigned char * const inbuf) { + + unsigned char (* const ib)[2] = (unsigned char (*)[2]) inbuf; + + int col; + unsigned int bufferCursor; + + bufferCursor = 0; /* initial value */ + + for (col=0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + tuplerow[col][plane] = bytes2ToSample(ib[bufferCursor++]); + } +} + + + +static void +parse3BpsRow(const struct pam * const pamP, + tuple * const tuplerow, + const unsigned char * const inbuf) { + + unsigned char (* const ib)[3] = (unsigned char (*)[3]) inbuf; + + int col; + unsigned int bufferCursor; + + bufferCursor = 0; /* initial value */ + + for (col=0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + tuplerow[col][plane] = bytes3ToSample(ib[bufferCursor++]); + } +} + + + +static void +parse4BpsRow(const struct pam * const pamP, + tuple * const tuplerow, + const unsigned char * const inbuf) { + + unsigned char (* const ib)[4] = (unsigned char (*)[4]) inbuf; + + int col; + unsigned int bufferCursor; + + bufferCursor = 0; /* initial value */ + + for (col=0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + tuplerow[col][plane] = bytes4ToSample(ib[bufferCursor++]); + } +} + + + +static void +readRawNonPbmRow(const struct pam * const pamP, + tuple * const tuplerow) { + + unsigned int const rowImageSize = + pamP->width * pamP->bytes_per_sample * pamP->depth; + + unsigned char * inbuf; + size_t bytesRead; + + inbuf = pnm_allocrowimage(pamP); + + bytesRead = fread(inbuf, 1, rowImageSize, pamP->file); + + if (bytesRead != rowImageSize) { + if (feof(pamP->file)) + pm_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); + } + } + pnm_freerowimage(inbuf); +} + + + +void +pnm_readpamrow(const struct pam * const pamP, + tuple * const tuplerow) { +/*---------------------------------------------------------------------------- + Read a row from the Netpbm image file into tuplerow[], at the + current file position. If 'tuplerow' is NULL, advance the file + pointer to the next row, but don't return the contents of the + current one. + + We assume the file is positioned to the beginning of a row of the + image's raster. +-----------------------------------------------------------------------------*/ + /* 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(). + */ + + /* Need a special case for raw PBM because it has multiple tuples (8) + packed into one byte. + */ + + switch (pamP->format) { + case PAM_FORMAT: + case RPPM_FORMAT: + case RPGM_FORMAT: + readRawNonPbmRow(pamP, tuplerow); + break; + case PPM_FORMAT: + case PGM_FORMAT: + readPlainNonPbmRow(pamP, tuplerow); + break; + case RPBM_FORMAT: + case PBM_FORMAT: + readPbmRow(pamP, tuplerow); + break; + default: + pm_error("Invalid 'format' member in PAM structure: %u", pamP->format); + } +} + + + +tuple ** +pnm_readpam(FILE * const fileP, + struct pam * const pamP, + int const size) { + + tuple **tuplearray; + int row; + + pnm_readpaminit(fileP, pamP, size); + + tuplearray = pnm_allocpamarray(pamP); + + for (row = 0; row < pamP->height; row++) + pnm_readpamrow(pamP, tuplearray[row]); + + return tuplearray; +} diff --git a/lib/libpamwrite.c b/lib/libpamwrite.c new file mode 100644 index 00000000..9184a4b5 --- /dev/null +++ b/lib/libpamwrite.c @@ -0,0 +1,397 @@ +/*---------------------------------------------------------------------------- + libpamwrite.c +------------------------------------------------------------------------------ + These are the library functions, which belong in the libnetpbm library, + that deal with writing the PAM (Portable Arbitrary Format) image format + raster (not the header). +-----------------------------------------------------------------------------*/ + +/* See libpm.c for the complicated explanation of this 32/64 bit file + offset stuff. +*/ +#define _FILE_OFFSET_BITS 64 +#define _LARGE_FILES + +#include +#include +#include +#include + +#include + +#include "pam.h" + + +static __inline__ unsigned int +samplesPerPlainLine(sample const maxval, + unsigned int const depth, + unsigned int const lineLength) { +/*---------------------------------------------------------------------------- + Return the minimum number of samples that should go in a line + 'lineLength' characters long in a plain format non-PBM PNM image + with depth 'depth' and maxval 'maxval'. + + Note that this number is just for aesthetics; the Netpbm formats allow + any number of samples per line. +-----------------------------------------------------------------------------*/ + unsigned int const digitsForMaxval = (unsigned int) + (log(maxval + 0.1 ) / log(10.0)); + /* Number of digits maxval has in decimal */ + /* +0.1 is an adjustment to overcome precision problems */ + unsigned int const fit = lineLength / (digitsForMaxval + 1); + /* Number of maxval-sized samples that fit in a line */ + unsigned int const retval = (fit > depth) ? (fit - (fit % depth)) : fit; + /* 'fit', rounded down to a multiple of depth, if possible */ + + return retval; +} + + + +static void +writePamPlainPbmRow(const struct pam * const pamP, + const tuple * const tuplerow) { + + int col; + unsigned int const samplesPerLine = 70; + + for (col = 0; col < pamP->width; ++col) + fprintf(pamP->file, + ((col+1) % samplesPerLine == 0 || col == pamP->width-1) + ? "%1u\n" : "%1u", + tuplerow[col][0] == PAM_PBM_BLACK ? PBM_BLACK : PBM_WHITE); +} + + + +static void +writePamPlainRow(const struct pam * const pamP, + const tuple * const tuplerow) { + + int const samplesPerLine = + samplesPerPlainLine(pamP->maxval, pamP->depth, 79); + + int col; + unsigned int samplesInCurrentLine; + /* number of samples written from start of line */ + + samplesInCurrentLine = 0; + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane){ + fprintf(pamP->file, "%lu ",tuplerow[col][plane]); + + ++samplesInCurrentLine; + + if (samplesInCurrentLine >= samplesPerLine) { + fprintf(pamP->file, "\n"); + samplesInCurrentLine = 0; + } + } + } + fprintf(pamP->file, "\n"); +} + + + +static void +formatPbmRow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP) { + + unsigned char accum; + int col; + + accum = 0; /* initial value */ + + for (col=0; col < pamP->width; ++col) { + accum |= + (tuplerow[col][0] == PAM_PBM_BLACK ? PBM_BLACK : PBM_WHITE) + << (7-col%8); + if (col%8 == 7) { + outbuf[col/8] = accum; + accum = 0; + } + } + if (pamP->width % 8 != 0) { + unsigned int const lastByteIndex = pamP->width/8; + outbuf[lastByteIndex] = accum; + *rowSizeP = lastByteIndex + 1; + } else + *rowSizeP = pamP->width/8; +} + + + +/* Though it is possible to simplify the sampleToBytesN() and + formatNBpsRow() functions into a single routine that handles all + sample widths, we value efficiency higher here. Earlier versions + of Netpbm (before 10.25) did that, with a loop, and performance + suffered visibly. +*/ + +static __inline__ void +sampleToBytes2(unsigned char buf[2], + sample const sampleval) { + + buf[0] = (sampleval >> 8) & 0xff; + buf[1] = (sampleval >> 0) & 0xff; +} + + + +static __inline__ void +sampleToBytes3(unsigned char buf[3], + sample const sampleval) { + + buf[0] = (sampleval >> 16) & 0xff; + buf[1] = (sampleval >> 8) & 0xff; + buf[2] = (sampleval >> 0) & 0xff; +} + + + +static __inline__ void +sampleToBytes4(unsigned char buf[4], + sample const sampleval) { + + buf[0] = (sampleval >> 24 ) & 0xff; + buf[1] = (sampleval >> 16 ) & 0xff; + buf[2] = (sampleval >> 8 ) & 0xff; + buf[3] = (sampleval >> 0 ) & 0xff; +} + + + +static __inline__ void +format1BpsRow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Create the image of a row in the raster of a raw format Netpbm + image that has one byte per sample (ergo not PBM). + + Put the image at *outbuf; put the number of bytes of it at *rowSizeP. +-----------------------------------------------------------------------------*/ + int col; + unsigned int bufferCursor; + + bufferCursor = 0; /* initial value */ + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane=0; plane < pamP->depth; ++plane) + outbuf[bufferCursor++] = (unsigned char)tuplerow[col][plane]; + } + *rowSizeP = pamP->width * 1 * pamP->depth; +} + + + +static __inline__ void +format2BpsRow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Analogous to format1BpsRow(). +-----------------------------------------------------------------------------*/ + unsigned char (* const ob)[2] = (unsigned char (*)[2]) outbuf; + + int col; + unsigned int bufferCursor; + + bufferCursor = 0; /* initial value */ + + for (col=0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + sampleToBytes2(ob[bufferCursor++], tuplerow[col][plane]); + } + + *rowSizeP = pamP->width * 2 * pamP->depth; +} + + + +static __inline__ void +format3BpsRow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Analogous to format1BpsRow(). +-----------------------------------------------------------------------------*/ + unsigned char (* const ob)[3] = (unsigned char (*)[3]) outbuf; + + int col; + unsigned int bufferCursor; + + bufferCursor = 0; /* initial value */ + + for (col=0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + sampleToBytes3(ob[bufferCursor++], tuplerow[col][plane]); + } + + *rowSizeP = pamP->width * 3 * pamP->depth; +} + + + +static __inline__ void +format4BpsRow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Analogous to format1BpsRow(). +-----------------------------------------------------------------------------*/ + unsigned char (* const ob)[4] = (unsigned char (*)[4]) outbuf; + + int col; + unsigned int bufferCursor; + + bufferCursor = 0; /* initial value */ + + for (col=0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + sampleToBytes4(ob[bufferCursor++], tuplerow[col][plane]); + } + + *rowSizeP = pamP->width * 4 * pamP->depth; +} + + + +void +pnm_formatpamrow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Create the image of a row in the raster of a raw (not plain) format + Netpbm image, as described by *pamP and tuplerow[]. Put the image + at *outbuf. + + 'outbuf' must be the address of space allocated with pnm_allocrowimage(). + + We return as *rowSizeP the number of bytes in the row image. +-----------------------------------------------------------------------------*/ + if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) + formatPbmRow(pamP, tuplerow, outbuf, rowSizeP); + else { + switch(pamP->bytes_per_sample){ + case 1: format1BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; + case 2: format2BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; + case 3: format3BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; + case 4: format4BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; + default: + pm_error("invalid bytes per sample passed to " + "pnm_formatpamrow(): %u", pamP->bytes_per_sample); + } + } +} + + + +static void +writePamRawRow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned int const count) { +/*---------------------------------------------------------------------------- + Write mutiple ('count') copies of the same row ('tuplerow') to the file, + in raw (not plain) format. +-----------------------------------------------------------------------------*/ + 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)); + } + pnm_freerowimage(outbuf); +} + + + +void +pnm_writepamrow(const struct pam * const pamP, + const tuple * const tuplerow) { + + /* 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(). + */ + + if (pm_plain_output || pamP->plainformat) { + switch (PAM_FORMAT_TYPE(pamP->format)) { + case PBM_TYPE: + writePamPlainPbmRow(pamP, tuplerow); + break; + case PGM_TYPE: + case PPM_TYPE: + writePamPlainRow(pamP, tuplerow); + break; + case PAM_TYPE: + /* pm_plain_output is impossible here due to assumption stated + above about pnm_writepaminit() having checked it. The + pamP->plainformat is meaningless for PAM. + */ + writePamRawRow(pamP, tuplerow, 1); + break; + default: + pm_error("Invalid 'format' value %u in pam structure", + pamP->format); + } + } else + writePamRawRow(pamP, tuplerow, 1); +} + + + +void +pnm_writepamrowmult(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned int const count) { +/*---------------------------------------------------------------------------- + Write mutiple ('count') copies of the same row ('tuplerow') to the file. +-----------------------------------------------------------------------------*/ + if (pm_plain_output || pamP->plainformat) { + unsigned int i; + for (i = 0; i < count; ++i) + pnm_writepamrow(pamP, tuplerow); + } else + /* Simple common case - use fastpath */ + writePamRawRow(pamP, tuplerow, count); +} + + + +void +pnm_writepam(struct pam * const pamP, + tuple ** const tuplearray) { + + int row; + + pnm_writepaminit(pamP); + + for (row = 0; row < pamP->height; ++row) + pnm_writepamrow(pamP, tuplearray[row]); +} diff --git a/lib/libpbm.h b/lib/libpbm.h new file mode 100644 index 00000000..827c9d7e --- /dev/null +++ b/lib/libpbm.h @@ -0,0 +1,12 @@ +/* This is the intra-libnetpbm interface header file for libpbm*.c +*/ + +#ifndef LIBPBM_H_INCLUDED +#define LIBPBM_H_INCLUDED + +void +pbm_readpbminitrest(FILE * file, + int * colsP, + int * rowsP); + +#endif diff --git a/lib/libpbm1.c b/lib/libpbm1.c new file mode 100644 index 00000000..8dd491a7 --- /dev/null +++ b/lib/libpbm1.c @@ -0,0 +1,62 @@ +/* libpbm1.c - pbm utility library part 1 +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +/* See libpm.c for the complicated explanation of this 32/64 bit file + offset stuff. +*/ +#define _FILE_OFFSET_BITS 64 +#define _LARGE_FILES + +#include +#include "pbm.h" +#include "libpbm.h" +#include "shhopt.h" + +void +pbm_init(int *argcP, char *argv[]) { + pm_proginit(argcP, argv); +} + + + +void +pbm_nextimage(FILE *file, int * const eofP) { + pm_nextimage(file, eofP); +} + + + +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) { + + if (rows < 0) + pm_error("Invalid number of rows passed to pbm_check(): %d", rows); + if (cols < 0) + pm_error("Invalid number of columns passed to pbm_check(): %d", cols); + + if (check_type != PM_CHECK_BASIC) { + if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE; + } else if (format != RPBM_FORMAT) { + if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; + } else { + pm_filepos const bytes_per_row = (cols+7)/8; + pm_filepos const need_raster_size = rows * bytes_per_row; +#ifdef LARGEFILEDEBUG + pm_message("pm_filepos passed to pm_check() is %u bytes", + sizeof(pm_filepos)); +#endif + pm_check(file, check_type, need_raster_size, retval_p); + } +} + diff --git a/lib/libpbm2.c b/lib/libpbm2.c new file mode 100644 index 00000000..d04328ef --- /dev/null +++ b/lib/libpbm2.c @@ -0,0 +1,180 @@ +/* libpbm2.c - pbm utility library part 2 +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pbm.h" +#include "libpbm.h" +#include "fileio.h" + +static bit +getbit (FILE * const file) { + char ch; + + do { + ch = pm_getc( file ); + } while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' ); + + if ( ch != '0' && ch != '1' ) + pm_error( "junk in file where bits should be" ); + + return ( ch == '1' ) ? 1 : 0; +} + + + +void +pbm_readpbminitrest( file, colsP, rowsP ) + FILE* file; + int* colsP; + int* rowsP; + { + /* Read size. */ + *colsP = (int)pm_getuint( file ); + *rowsP = (int)pm_getuint( file ); + + /* *colsP and *rowsP really should be unsigned int, but they come + from the time before unsigned ints (or at least from a person + trained in that tradition), so they are int. We could simply + consider negative numbers to mean values > INT_MAX/2 and much + code would just automatically work. But some code would fail + miserably. So we consider values that won't fit in an int to + be unprocessable. + */ + if (*colsP < 0) + pm_error("Number of columns in header is too large."); + if (*rowsP < 0) + pm_error("Number of columns in header is too large."); + } + +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; + + default: + pm_error( "bad magic number - not a pbm file" ); + } + } + +void +pbm_readpbmrow( file, bitrow, cols, format ) + FILE* file; + bit* bitrow; + int cols, format; + { + register int col, bitshift; + register bit* bP; + + switch ( format ) + { + case PBM_FORMAT: + for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) + *bP = getbit( file ); + break; + + case RPBM_FORMAT: { + register unsigned char item; + bitshift = -1; item = 0; /* item's value is meaningless here */ + for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) + { + if ( bitshift == -1 ) + { + item = pm_getrawbyte( file ); + bitshift = 7; + } + *bP = ( item >> bitshift ) & 1; + --bitshift; + } + } + break; + + default: + pm_error( "can't happen" ); + } + } + + + +void +pbm_readpbmrow_packed(FILE * const file, + unsigned char * const packed_bits, + int const cols, + int const format) { + + switch(format) { + case PBM_FORMAT: { + unsigned int col; + unsigned int byteIndex; + + /* We first clear the return buffer, then set ones where needed */ + for (byteIndex = 0; byteIndex < pbm_packed_bytes(cols); ++byteIndex) + packed_bits[byteIndex] = 0x00; + + for (col = 0; col < cols; ++col) { + unsigned char mask; + mask = getbit(file) << (7 - col % 8); + packed_bits[col / 8] |= mask; + } + } + break; + + case RPBM_FORMAT: { + int bytes_read; + bytes_read = fread(packed_bits, 1, pbm_packed_bytes(cols), file); + + if (bytes_read < pbm_packed_bytes(cols)) { + if (feof(file)) + if (bytes_read == 0) + pm_error("Attempt to read a raw PBM image row, but " + "no more rows left in file."); + else + pm_error("EOF in the middle of a raw PBM row."); + else + pm_error("I/O error reading raw PBM row"); + } + } + break; + + default: + pm_error( "Internal error in pbm_readpbmrow_packed." ); + } +} + + + +bit** +pbm_readpbm( file, colsP, rowsP ) + FILE* file; + int* colsP; + int* rowsP; + { + register bit** bits; + int format, row; + + pbm_readpbminit( file, colsP, rowsP, &format ); + + bits = pbm_allocarray( *colsP, *rowsP ); + + for ( row = 0; row < *rowsP; ++row ) + pbm_readpbmrow( file, bits[row], *colsP, format ); + + return bits; + } diff --git a/lib/libpbm3.c b/lib/libpbm3.c new file mode 100644 index 00000000..f1717f37 --- /dev/null +++ b/lib/libpbm3.c @@ -0,0 +1,282 @@ +/* libpbm3.c - pbm utility library part 3 +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pbm.h" +#include "libpbm.h" +#include "bitreverse.h" + +#if defined(__GNUC__) && (__GNUC__ >=3) && (__GNUC_MINOR__ >=1) && defined (__SSE__) +/* intel MMX-SSE enhancement for pbm_writepbmowraw() */ +/* GCC only. Turn on with -msse */ + +#define HAVE_MMX_SSE 1 +#else +#define HAVE_MMX_SSE 0 +#endif + + +void +pbm_writepbminit(FILE * const fileP, + int const cols, + int const rows, + int const forceplain) { + + if (!forceplain && !pm_plain_output) { + fprintf(fileP, "%c%c\n%d %d\n", PBM_MAGIC1, RPBM_MAGIC2, cols, rows); +#ifdef VMS + set_outfile_binary(); +#endif + } else + fprintf(fileP, "%c%c\n%d %d\n", PBM_MAGIC1, PBM_MAGIC2, cols, rows); +} + + + +static void +writePackedRawRow(FILE * const fileP, + const unsigned char * const packed_bits, + int const cols) { + + int bytesWritten; + bytesWritten = fwrite(packed_bits, 1, pbm_packed_bytes(cols), fileP); + if (bytesWritten < pbm_packed_bytes(cols)) + pm_error("I/O error writing packed row to raw PBM file."); +} + + + +static void +packBitsWithMmxSse(FILE * const fileP, + const bit * const bitrow, + unsigned char * const packedBits, + int const cols, + 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 + as *nextColP the number of the next column after the rightmost one we + packed. + + 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. + + We use 2 MMX registers (no SSE registers). + + The key machine instructions are: + + + PCMPEQB Packed CoMPare EQual Byte + + Compares 8 bytes in parallel + Result is x00 if equal, xFF if unequal 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 + + + 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))); + int col; + + di const zero64 = 0; /* to clear with PXOR */ + + for (col = 0; col < cols-7; 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; + unsigned char const blackMask = bitreverse[backwardBlackMask]; + + packedBits[col/8] = blackMask; + } + *nextColP = col; + + __builtin_ia32_emms(); + +#else + if (bitreverse == bitreverse) {}; /* avoid unused vbl compiler warning */ +#endif +} + + + +static void +packBitsGeneric(FILE * const fileP, + const bit * const bitrow, + unsigned char * const packedBits, + int const cols, + int * const nextColP) { +/*---------------------------------------------------------------------------- + Pack the bits of bitrow[] into byts 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. + + Don't use any special CPU facilities to do the packing. +-----------------------------------------------------------------------------*/ + int col; + + #define iszero(x) ((x) == 0 ? 0 : 1) + + for (col = 0; col < cols-7; 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 + ); + *nextColP = col; +} + + + +static void +writePbmRowRaw(FILE * const fileP, + const bit * const bitrow, + int const cols) { + + int nextCol; + + unsigned char * const packedBits = pbm_allocrow_packed(cols); + + if (HAVE_MMX_SSE) + packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol); + else + packBitsGeneric(fileP, bitrow, packedBits, cols, &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 + ; + + packedBits[col/8] = item; + } + + writePackedRawRow(fileP, packedBits, cols); + + pbm_freerow_packed(packedBits); +} + + + +static void +writePbmRowPlain(FILE * const fileP, + bit * const bitrow, + int const cols) { + + int col, charcount; + + charcount = 0; + for (col = 0; col < cols; ++col) { + if (charcount >= 70) { + putc('\n', fileP); + charcount = 0; + } + putc(bitrow[col] ? '1' : '0', fileP); + ++charcount; + } + putc('\n', fileP); +} + + + +void +pbm_writepbmrow(FILE * const fileP, + bit * const bitrow, + int const cols, + int const forceplain) { + + if (!forceplain && !pm_plain_output) + writePbmRowRaw(fileP, bitrow, cols); + else + writePbmRowPlain(fileP, bitrow, cols); +} + + + +void +pbm_writepbmrow_packed(FILE * const fileP, + const unsigned char * const packed_bits, + int const cols, + int const forceplain) { + + if (!forceplain && !pm_plain_output) + writePackedRawRow(fileP, packed_bits, cols); + else { + bit *bitrow; + int col; + + 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); + pbm_freerow(bitrow); + } +} + + + +void +pbm_writepbm(FILE * const fileP, + bit ** const bits, + int const cols, + int const rows, + int const forceplain) { + + int row; + + pbm_writepbminit(fileP, cols, rows, forceplain); + + for (row = 0; row < rows; ++row) + pbm_writepbmrow(fileP, bits[row], cols, forceplain); +} diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c new file mode 100644 index 00000000..c7b5a355 --- /dev/null +++ b/lib/libpbmfont.c @@ -0,0 +1,1136 @@ +/* libpbm5.c - pbm utility library part 5 +** +** Font routines. +** +** BDF font code Copyright 1993 by George Phillips. +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include + +#include "pm_c_util.h" +#include "nstring.h" +#include "pbm.h" +#include "pbmfont.h" +#include "mallocvar.h" + +static unsigned int const firstCodePoint = 32; + /* This is the code point of the first character in a pbmfont font. + In ASCII, it is a space. + */ + +static unsigned int const nCharsInFont = 96; + /* The number of characters in a pbmfont font. A pbmfont font defines + characters at position 32 (ASCII space) through 127, so that's 96. + */ + +/* The default font, packed in hex so this source file doesn't get huge. +** You can replace this with your own font using pbm_dumpfont(). +*/ +#define DEFAULTFONT_ROWS 155 +#define DEFAULTFONT_COLS 112 +static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = { + {0x00000000L,0x20000c00L,0x10000000L,0x00000000L}, + {0xc600a000L,0x42000810L,0x00000002L,0x00000063L}, + {0x6c00a000L,0x45000810L,0x00000002L,0x00000036L}, + {0x6c00a000L,0x88800808L,0xf2e1dee2L,0x00000036L}, + {0x54000000L,0x80000800L,0x11122442L,0x0000002aL}, + {0x54000001L,0x00000800L,0x11122442L,0x0000002aL}, + {0x54000001L,0x00000800L,0x11122282L,0x0000002aL}, + {0x44000102L,0x00000800L,0x11122382L,0x00000022L}, + {0xee000102L,0x00000800L,0x11e1e102L,0x00000077L}, + {0x00000204L,0x00000800L,0x11002102L,0x00000000L}, + {0x00000000L,0x00000c00L,0x11002102L,0x00000000L}, + {0x00000000L,0x003f8000L,0xe3807600L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x02000080L,0x00040000L,0x00120000L,0x00000001L}, + {0x04000082L,0x828e1838L,0x20210100L,0x00000002L}, + {0x04000082L,0x82912448L,0x20210100L,0x00000002L}, + {0x08000082L,0x8fd01940L,0x404087c2L,0x00000004L}, + {0x08000080L,0x050c0622L,0x00408102L,0x00000004L}, + {0x10000080L,0x05061874L,0x0040828fL,0x00008008L}, + {0x10000080L,0x1f912688L,0x00408002L,0x00000008L}, + {0x20000000L,0x0a11098cL,0x00408002L,0x00000010L}, + {0x20000080L,0x0a0e0672L,0x00210000L,0x00000010L}, + {0x40000000L,0x00040000L,0x00210000L,0x00000020L}, + {0x00000000L,0x00000000L,0x00120000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x004e0838L,0x7023e1cfL,0x00008000L}, + {0x00000000L,0x00913844L,0x88620208L,0x00008000L}, + {0x08000000L,0x00910844L,0x08a20401L,0x00000004L}, + {0x10000000L,0x01110844L,0x08a20401L,0x00000008L}, + {0x20000000L,0x01110808L,0x3123c781L,0x00000010L}, + {0x400003e0L,0x02110810L,0x0a202441L,0x00000020L}, + {0x20000000L,0x02110820L,0x0bf02442L,0x00000010L}, + {0x10008000L,0x04110844L,0x88242442L,0x00000008L}, + {0x08008002L,0x040e3e7cL,0x7073c382L,0x00000004L}, + {0x00010000L,0x08000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x0000e1c0L,0x00000000L,0x00000000L,0x00000000L}, + {0x00011220L,0x00000000L,0x70e38f87L,0x00000000L}, + {0x20011220L,0x00020020L,0x89108448L,0x00008010L}, + {0x10011220L,0x00040010L,0x09314448L,0x00008008L}, + {0x0800e221L,0x02083e08L,0x11514788L,0x00000004L}, + {0x040111e0L,0x00100004L,0x2153e448L,0x00000002L}, + {0x08011020L,0x00083e08L,0x213a2448L,0x00008004L}, + {0x10011040L,0x02040010L,0x01022448L,0x00008008L}, + {0x2000e381L,0x02020020L,0x20e77f87L,0x00000010L}, + {0x00000000L,0x04000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x3803e7efL,0xc73bbe3dL,0xdb863ce7L,0x0000001cL}, + {0x44011224L,0x48910808L,0x91036648L,0x00008022L}, + {0x4c011285L,0x48910808L,0xa1036648L,0x00008026L}, + {0x54011387L,0x081f0808L,0xc102a548L,0x0000802aL}, + {0x54011285L,0x09910808L,0xe102a548L,0x0000802aL}, + {0x4e011204L,0x08910848L,0x9112a4c8L,0x00008027L}, + {0x40011224L,0x08910848L,0x891224c8L,0x00008020L}, + {0x3803e7efL,0x073bbe31L,0xcff77e47L,0x0000001cL}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000003L,0x00000000L}, + {0x0003e1cfL,0x87bff7efL,0xdfbf77c2L,0x00000000L}, + {0x00013224L,0x48a4a244L,0x89122442L,0x00000000L}, + {0x00011224L,0x4824a244L,0xa8a14482L,0x00000000L}, + {0x00013227L,0x8e04226cL,0xa8414102L,0x00000000L}, + {0x0001e224L,0x83842228L,0xa8a08102L,0x00000000L}, + {0x00010224L,0x40842228L,0xd8a08242L,0x00000000L}, + {0x00010224L,0x48843638L,0x51108442L,0x00000000L}, + {0x0003c1ceL,0x6f1f1c10L,0x53b9c7c2L,0x00000000L}, + {0x00000060L,0x00000000L,0x00000002L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000003L,0x00000000L}, + {0xfe000000L,0x00000000L,0x00000000L,0x0000007fL}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00010180L,0x000000c0L,0x003001c0L,0x00000000L}, + {0x08008081L,0x00040040L,0x00100200L,0x00000004L}, + {0x10008082L,0x80040040L,0x00100200L,0x00000008L}, + {0x10004084L,0x40023c78L,0x70f1c7c7L,0x00004008L}, + {0x10004080L,0x00000244L,0x89122208L,0x00008008L}, + {0x20002080L,0x00001e44L,0x8113e208L,0x00008010L}, + {0x10002080L,0x00002244L,0x81120208L,0x00008008L}, + {0x10001080L,0x00002244L,0x89122208L,0x00008008L}, + {0x10001080L,0x00001db8L,0x70e9c787L,0x00008008L}, + {0x10000880L,0x00000000L,0x00000000L,0x00008008L}, + {0x08000180L,0x00000000L,0x00000000L,0x00008004L}, + {0x00000000L,0x1fc00000L,0x00000007L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00030080L,0x981c0000L,0x00000000L,0x00000000L}, + {0x20010000L,0x08040000L,0x00000000L,0x00000010L}, + {0x10010000L,0x08040000L,0x00000000L,0x00000008L}, + {0x10016387L,0x898474b8L,0x72e1d5c7L,0x00000008L}, + {0x10019080L,0x8a042a64L,0x89122208L,0x00008008L}, + {0x08011080L,0x8c042a44L,0x89122207L,0x00000004L}, + {0x10011080L,0x8a042a44L,0x89122200L,0x00008008L}, + {0x10011080L,0x89042a44L,0x89122208L,0x00008008L}, + {0x1003bbe0L,0x98dfebe6L,0x71e1e787L,0x00000008L}, + {0x10000000L,0x80000000L,0x01002000L,0x00000008L}, + {0x20000000L,0x80000000L,0x01002000L,0x00000010L}, + {0x00000007L,0x00000000L,0x03807000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00008000L,0x00000000L,0x10410000L,0x00000000L}, + {0x00008000L,0x00000000L,0x20408000L,0x00000000L}, + {0x0001f66eL,0xfdfbf77cL,0x20408000L,0x00000000L}, + {0x24008224L,0x488a2248L,0x20408240L,0x00000012L}, + {0x54008224L,0x4a842210L,0x40404540L,0x0000002aL}, + {0x48008222L,0x8a8a1420L,0x20408480L,0x00000024L}, + {0x00008a23L,0x85111c44L,0x20408000L,0x00000000L}, + {0x000071d1L,0x0531887cL,0x20408000L,0x00000000L}, + {0x00000000L,0x00000800L,0x20408000L,0x00000000L}, + {0x00000000L,0x00000800L,0x10410000L,0x00000000L}, + {0x00000000L,0x00003000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x20000c00L,0x10000000L,0x00000000L}, + {0xc600a000L,0x42000810L,0x00000002L,0x00000063L}, + {0x6c00a000L,0x45000810L,0x00000002L,0x00000036L}, + {0x6c00a000L,0x88800808L,0xf2e1dee2L,0x00000036L}, + {0x54000000L,0x80000800L,0x11122442L,0x0000002aL}, + {0x54000001L,0x00000800L,0x11122442L,0x0000002aL}, + {0x54000001L,0x00000800L,0x11122282L,0x0000002aL}, + {0x44000102L,0x00000800L,0x11122382L,0x00000022L}, + {0xee000102L,0x00000800L,0x11e1e102L,0x00000077L}, + {0x00000204L,0x00000800L,0x11002102L,0x00000000L}, + {0x00000000L,0x00000c00L,0x11002102L,0x00000000L}, + {0x00000000L,0x003f8000L,0xe3807600L,0x00000000L} + }; + +/* A default BDF font */ +/* Not as nicely compacted as the PBM font, oh well. */ + +static struct glyph _g[190] = { + { 1, 1, 0, 0, 3, "\0" }, + { 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\0\1" }, + { 3, 3, 1, 6, 5, "\1\0\1\1\0\1\1\0\1" }, + { 5, 8, 0, 0, 6, "\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0" }, + { 5, 11, 0, -1, 6, "\0\0\1\0\0\0\1\1\1\0\1\0\1\0\1\1\0\1\0\0\0\1\1\0\0\0\0\1\1\0\0\0\1\0\1\0\0\1\0\1\1\0\1\0\1\0\1\1\1\0\0\0\1\0\0" }, + { 8, 9, 0, 0, 9, "\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0\1\0\0\1\0\1\0\0\0\1\1\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0" }, + { 9, 9, 0, 0, 10, "\0\0\0\1\1\0\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\0\1\1\0\1\1\1\0\1\1\1\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\0\0\1\1\1\0\1\0\1\1\1\1\0\1\1\0" }, + { 2, 3, 1, 6, 4, "\1\1\0\1\1\0" }, + { 3, 12, 1, -3, 5, "\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1" }, + { 3, 12, 0, -3, 5, "\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0" }, + { 5, 5, 0, 4, 6, "\0\0\1\0\0\1\0\1\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0" }, + { 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0" }, + { 2, 3, 0, -2, 3, "\0\1\0\1\1\0" }, + { 5, 1, 1, 3, 8, "\1\1\1\1\1" }, + { 1, 1, 1, 0, 3, "\1" }, + { 3, 9, 0, 0, 3, "\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\1\0\1\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\0\1\1\0\1\1\1\0" }, + { 4, 9, 0, 0, 6, "\0\0\1\0\0\1\1\0\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\1\1\1\1\1" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\1\0\1\1\1\0" }, + { 5, 9, 0, 0, 6, "\0\0\0\1\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\1\1\1\1\0\0\0\1\0\0\0\0\1\0" }, + { 5, 9, 0, 0, 6, "\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\1\1\0\0\1\1\0\1\1\1\0" }, + { 5, 9, 0, 0, 6, "\0\0\0\1\1\0\1\1\0\0\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0" }, + { 5, 9, 0, 0, 6, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\1\0\0\0\1\0\0\0\1\1\0\1\1\0\0\0" }, + { 1, 6, 1, 0, 3, "\1\0\0\0\0\1" }, + { 2, 8, 0, -2, 3, "\0\1\0\0\0\0\0\0\0\0\0\1\0\1\1\0" }, + { 6, 5, 0, 1, 8, "\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1" }, + { 5, 3, 1, 2, 7, "\1\1\1\1\1\0\0\0\0\0\1\1\1\1\1" }, + { 6, 5, 1, 1, 8, "\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0" }, + { 4, 9, 0, 0, 5, "\0\1\1\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0" }, + { 10, 11, 1, -2, 11, "\0\0\0\0\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\1\1\0\1\0\1\1\0\0\1\0\0\1\0\0\1\1\0\1\0\0\0\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\1\0\1\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0" }, + { 9, 9, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\1\1\1\1\1\1\0" }, + { 7, 9, 0, 0, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0" }, + { 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" }, + { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0" }, + { 8, 9, 0, 0, 9, "\0\0\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\0\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 9, 0, 0, 9, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" }, + { 3, 9, 0, 0, 4, "\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 4, 9, 0, 0, 4, "\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\1\1\0\0" }, + { 8, 9, 0, 0, 8, "\1\1\1\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\1\1\1\0\0\1\1\1" }, + { 6, 9, 0, 0, 7, "\1\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\1\1\1\1\1\1" }, + { 11, 9, 0, 0, 11, "\1\1\0\0\0\0\0\0\0\1\1\0\1\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\1\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\0\1\0\0\1\1\1" }, + { 9, 9, 0, 0, 9, "\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" }, + { 8, 9, 0, 0, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 7, 9, 0, 0, 7, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" }, + { 8, 11, 0, -2, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\1\1" }, + { 8, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\0\1\1\1\1\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\0\1\1" }, + { 6, 9, 0, 0, 7, "\0\1\1\1\0\1\1\0\0\0\1\1\1\0\0\0\0\1\0\1\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\0\1\1\1\0" }, + { 7, 9, 0, 0, 7, "\1\1\1\1\1\1\1\1\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0" }, + { 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0" }, + { 12, 9, 0, 0, 12, "\1\1\1\0\1\1\1\0\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\1\0\0\0\0\0\1\0\1\0\1\0\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0" }, + { 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" }, + { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" }, + { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 3, 12, 1, -3, 5, "\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1" }, + { 3, 9, 0, 0, 3, "\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1" }, + { 3, 12, 0, -3, 5, "\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" }, + { 5, 5, 0, 4, 6, "\0\0\1\0\0\0\1\0\1\0\0\1\0\1\0\1\0\0\0\1\1\0\0\0\1" }, + { 6, 1, 0, -3, 6, "\1\1\1\1\1\1" }, + { 2, 3, 1, 6, 4, "\0\1\1\0\1\1" }, + { 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 0, 0, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0" }, + { 4, 6, 1, 0, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\0\1\1\0\0\0\0\1\0\0\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 3, 9, 0, 0, 3, "\0\0\1\0\1\0\0\1\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0" }, + { 5, 9, 1, -3, 6, "\0\1\1\1\1\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" }, + { 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" }, + { 3, 9, 0, 0, 3, "\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 2, 12, 0, -3, 3, "\0\1\0\0\0\0\1\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\1\0" }, + { 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\1" }, + { 3, 9, 0, 0, 3, "\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 9, 6, 0, 0, 9, "\1\0\1\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1\0\1\1" }, + { 6, 6, 0, 0, 6, "\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" }, + { 4, 6, 1, 0, 6, "\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 5, 9, 0, -3, 6, "\1\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" }, + { 5, 9, 1, -3, 6, "\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\1\1" }, + { 4, 6, 0, 0, 4, "\1\0\1\1\0\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1\0" }, + { 4, 6, 1, 0, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\1\0" }, + { 4, 7, 0, 0, 4, "\0\1\0\0\1\1\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" }, + { 6, 6, 0, 0, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 6, 0, 0, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0" }, + { 9, 6, 0, 0, 9, "\1\1\1\0\1\1\0\1\1\0\1\0\0\1\0\0\1\0\0\1\1\0\1\0\1\1\0\0\0\1\0\1\0\1\0\0\0\0\1\1\0\1\0\0\0\0\0\1\0\0\1\0\0\0" }, + { 5, 6, 1, 0, 6, "\1\1\0\1\1\0\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\1\0\1\1\0\1\1" }, + { 6, 9, 0, -3, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" }, + { 4, 6, 1, 0, 6, "\1\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1" }, + { 4, 12, 1, -3, 6, "\0\0\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" }, + { 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\1\1" }, + { 4, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\1\0\0" }, + { 6, 2, 0, 3, 7, "\0\1\1\0\0\1\1\0\0\1\1\0" }, + { 1, 9, 1, -3, 4, "\1\0\1\1\1\1\1\1\1" }, + { 5, 8, 1, -1, 6, "\0\0\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\1\0\0\1\0\1\1\1\0\1\0\0\0\0" }, + { 5, 9, 0, 0, 6, "\0\0\1\1\0\0\1\0\0\1\0\1\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\1\1\1\0\1\1" }, + { 6, 7, 1, 1, 7, "\1\0\0\0\0\1\0\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\0\1\0\0\0\0\1" }, + { 5, 9, 0, 0, 6, "\1\0\0\0\1\1\0\0\0\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\1\0" }, + { 1, 9, 1, 0, 3, "\1\1\1\0\0\1\1\1\1" }, + { 4, 12, 1, -3, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\1\1\0\1\0\1\1\1\0\0\1\1\0\0\1\1\1\0\1\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0" }, + { 3, 1, 0, 7, 3, "\1\0\1" }, + { 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0\1\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\1\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\0\1\1\1\0\0\0" }, + { 3, 6, 1, 3, 5, "\1\1\0\0\0\1\1\1\1\1\0\1\0\0\0\1\1\1" }, + { 5, 5, 1, 0, 7, "\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1" }, + { 6, 4, 1, 1, 8, "\1\1\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1" }, + { 4, 1, 1, 3, 6, "\1\1\1\1" }, + { 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\1\0\0\1\0\1\0\0\1\1\1\0\1\0\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\1\0\0\0" }, + { 4, 1, 0, 7, 4, "\1\1\1\1" }, + { 4, 4, 0, 5, 5, "\0\1\1\0\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 5, 7, 1, 0, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1" }, + { 4, 5, 0, 4, 4, "\0\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" }, + { 3, 5, 0, 4, 4, "\1\1\1\0\0\1\0\1\0\0\0\1\1\1\0" }, + { 2, 2, 1, 7, 4, "\0\1\1\0" }, + { 6, 9, 0, -3, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0" }, + { 6, 12, 0, -3, 7, "\0\1\1\1\1\1\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\0\1\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0" }, + { 1, 1, 1, 3, 3, "\1" }, + { 3, 3, 0, -3, 3, "\0\1\0\0\0\1\1\1\1" }, + { 3, 5, 0, 4, 4, "\0\1\0\1\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 6, 1, 3, 5, "\0\1\0\1\0\1\1\0\1\0\1\0\0\0\0\1\1\1" }, + { 5, 5, 0, 0, 7, "\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0" }, + { 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" }, + { 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\1\1\0\0\0\0\1\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\1\1" }, + { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" }, + { 4, 9, 0, -3, 5, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\1\1\0" }, + { 9, 12, 0, 0, 9, "\0\0\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 11, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 10, 9, 0, 0, 11, "\0\0\1\1\1\1\1\1\1\1\0\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\1\1\1\1\0\0\1\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\0\0\1\1\1\1\1\1" }, + { 7, 12, 0, -3, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\0\0" }, + { 7, 12, 0, 0, 8, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 7, 12, 0, 0, 8, "\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 7, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 7, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 3, 12, 0, 0, 4, "\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 12, 0, 0, 4, "\0\0\1\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 12, 0, 0, 4, "\0\1\0\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 11, 0, 0, 4, "\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" }, + { 8, 12, 0, 0, 9, "\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 9, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 11, 0, 0, 9, "\0\0\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 5, 5, 1, 1, 7, "\1\0\0\0\1\0\1\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\0\0\1" }, + { 9, 10, 0, 0, 9, "\0\0\0\0\0\0\0\0\1\0\0\1\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\1\0\0\1\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\1\0\0\1\0\1\1\1\1\0\0\0" }, + { 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 8, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" }, + { 7, 9, 0, 0, 7, "\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" }, + { 6, 9, 0, 0, 6, "\0\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\0\1\1\1\0\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 1, 0, 6, "\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 1, 0, 6, "\0\1\0\1\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 8, 6, 1, 0, 9, "\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\0\1\1\0\1\1\1\0" }, + { 4, 9, 1, -3, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0\0\1\0\0\0\0\1\0\1\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 3, 9, 0, 0, 3, "\1\0\0\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 9, 0, 0, 3, "\0\1\0\1\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 9, 0, 0, 3, "\0\1\0\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 8, 0, 0, 3, "\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 4, 9, 1, 0, 6, "\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 6, 9, 0, 0, 6, "\0\0\1\0\1\0\0\1\0\1\0\0\0\0\0\0\0\0\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" }, + { 4, 9, 1, 0, 6, "\0\1\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 4, 9, 1, 0, 6, "\0\1\0\1\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 4, 8, 1, 0, 6, "\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\1\0\0" }, + { 6, 7, 0, -1, 6, "\0\0\1\1\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\0\1\0\0\0\0\0" }, + { 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 9, 0, 0, 6, "\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 8, 0, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 12, 0, -3, 6, "\0\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" }, + { 5, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" }, + { 6, 11, 0, -3, 6, "\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" } +}; + +static struct font default_bdffont = { 14, 15, -1, -3, { + 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, + _g + 0, + _g + 1, + _g + 2, + _g + 3, + _g + 4, + _g + 5, + _g + 6, + _g + 7, + _g + 8, + _g + 9, + _g + 10, + _g + 11, + _g + 12, + _g + 13, + _g + 14, + _g + 15, + _g + 16, + _g + 17, + _g + 18, + _g + 19, + _g + 20, + _g + 21, + _g + 22, + _g + 23, + _g + 24, + _g + 25, + _g + 26, + _g + 27, + _g + 28, + _g + 29, + _g + 30, + _g + 31, + _g + 32, + _g + 33, + _g + 34, + _g + 35, + _g + 36, + _g + 37, + _g + 38, + _g + 39, + _g + 40, + _g + 41, + _g + 42, + _g + 43, + _g + 44, + _g + 45, + _g + 46, + _g + 47, + _g + 48, + _g + 49, + _g + 50, + _g + 51, + _g + 52, + _g + 53, + _g + 54, + _g + 55, + _g + 56, + _g + 57, + _g + 58, + _g + 59, + _g + 60, + _g + 61, + _g + 62, + _g + 63, + _g + 64, + _g + 65, + _g + 66, + _g + 67, + _g + 68, + _g + 69, + _g + 70, + _g + 71, + _g + 72, + _g + 73, + _g + 74, + _g + 75, + _g + 76, + _g + 77, + _g + 78, + _g + 79, + _g + 80, + _g + 81, + _g + 82, + _g + 83, + _g + 84, + _g + 85, + _g + 86, + _g + 87, + _g + 88, + _g + 89, + _g + 90, + _g + 91, + _g + 92, + _g + 93, + _g + 94, + 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, + _g + 95, + _g + 96, + _g + 97, + _g + 98, + _g + 99, + _g + 100, + _g + 101, + _g + 102, + _g + 103, + _g + 104, + _g + 105, + _g + 106, + _g + 107, + _g + 108, + _g + 109, + _g + 110, + _g + 111, + _g + 112, + _g + 113, + _g + 114, + _g + 115, + _g + 116, + _g + 117, + _g + 118, + _g + 119, + _g + 120, + _g + 121, + _g + 122, + _g + 123, + _g + 124, + _g + 125, + _g + 126, + _g + 127, + _g + 128, + _g + 129, + _g + 130, + _g + 131, + _g + 132, + _g + 133, + _g + 134, + _g + 135, + _g + 136, + _g + 137, + _g + 138, + _g + 139, + _g + 140, + _g + 141, + _g + 142, + _g + 143, + _g + 144, + _g + 145, + _g + 146, + _g + 147, + _g + 148, + _g + 149, + _g + 150, + _g + 151, + _g + 152, + _g + 153, + _g + 154, + _g + 155, + _g + 156, + _g + 157, + _g + 158, + _g + 159, + _g + 160, + _g + 161, + _g + 162, + _g + 163, + _g + 164, + _g + 165, + _g + 166, + _g + 167, + _g + 168, + _g + 169, + _g + 170, + _g + 171, + _g + 172, + _g + 173, + _g + 174, + _g + 175, + _g + 176, + _g + 177, + _g + 178, + _g + 179, + _g + 180, + _g + 181, + _g + 182, + _g + 183, + _g + 184, + _g + 185, + _g + 186, + _g + 187, + _g + 188, + _g + 189 + } +}; + + + +struct font * +pbm_defaultfont(const char * const name) { +/*---------------------------------------------------------------------------- + Generate the built-in font with name 'name'. +-----------------------------------------------------------------------------*/ + struct font * retval; + + if (strcmp(name, "bdf") == 0) + retval = &default_bdffont; + else { + bit** defaultfont; + unsigned int row; + + if (strcmp(name, "fixed") != 0) + pm_error( "built-in font name unknown, try 'bdf' or 'fixed'" ); + + 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) { + int scol; + unsigned long l; + + l = defaultfont_bits[row][col / 32]; /* initial value */ + + for (scol = MIN( col + 32, DEFAULTFONT_COLS) - 1; + scol >= (int)col; + --scol) { + if (l & 0x01) + defaultfont[row][scol] = 1; + else + defaultfont[row][scol] = 0; + l >>= 1; + } + } + } + + retval = + pbm_dissectfont(defaultfont, DEFAULTFONT_ROWS, DEFAULTFONT_COLS); + } + return retval; +} + + +struct font* +pbm_dissectfont(bit ** const font, + int const frows, + 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; + 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; + + /* Now convert to a general font */ + + MALLOCVAR(fn); + if ( fn == NULL ) + pm_error( "out of memory allocating font structure" ); + + fn->maxwidth = char_width; + fn->maxheight = char_height; + fn->x = fn->y = 0; + + fn->oldfont = font; + fn->frows = frows; + fn->fcols = fcols; + + /* Initialize all character positions to "undefined." Those that + are defined in the font will be filled in below. + */ + for (i = 0; i < 256; i++) + fn->glyph[i] = NULL; + + MALLOCARRAY(glyph, nCharsInFont); + if ( glyph == NULL ) + pm_error( "out of memory allocating glyphs" ); + + bmap = (char*) malloc( fn->maxwidth * fn->maxheight * nCharsInFont ); + if ( bmap == (char*) 0) + pm_error( "out of memory allocating glyph data" ); + + /* Now fill in the 0,0 coords. */ + row = cell_height * 2; + col = cell_width * 2; + for (i = 0; i < firstCodePoint; ++i) + fn->glyph[i] = NULL; + + for ( ch = 0; ch < nCharsInFont; ++ch ) { + glyph[ch].width = fn->maxwidth; + glyph[ch].height = fn->maxheight; + glyph[ch].x = glyph[ch].y = 0; + glyph[ch].xadd = cell_width; + + for ( r = 0; r < glyph[ch].height; ++r ) + for ( c = 0; c < glyph[ch].width; ++c ) + bmap[r * glyph[ch].width + c] = font[row + r][col + c]; + + glyph[ch].bmap = bmap; + bmap += glyph[ch].width * glyph[ch].height; + + fn->glyph[firstCodePoint + ch] = &glyph[ch]; + + col += cell_width; + if ( col >= cell_width * 14 ) { + col = cell_width * 2; + row += cell_height; + } + } + for (i = firstCodePoint + nCharsInFont; i < 256; ++i) + fn->glyph[i] = NULL; + + return fn; +} + + + +struct font* +pbm_loadfont(const char * const filename) +{ + FILE* fp; + struct font* fn; + char line[256]; + + fp = pm_openr( filename ); + fgets(line, 256, fp); + pm_close( fp ); + + if (line[0] == PBM_MAGIC1 && + (line[1] == PBM_MAGIC2 || line[1] == RPBM_MAGIC2)) { + return pbm_loadpbmfont( filename ); + } else if (!strncmp(line, "STARTFONT", 9)) { + if (!(fn = pbm_loadbdffont( filename ))) + pm_error( "could not load BDF font file" ); + return fn; + } else { + pm_error( "font file not in a recognized format "); + return NULL; /* should never reach here */ + } +} + + + +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 ); +} + +void +pbm_dumpfont( fn ) + struct font* fn; +{ + /* Dump out font as C source code. */ + int row, col, scol, lperrow; + unsigned long l; + + if (fn->oldfont) { + printf( "#define DEFAULTFONT_ROWS %d\n", fn->frows ); + printf( "#define DEFAULTFONT_COLS %d\n", fn->fcols ); + printf( "static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = {\n" ); + for ( row = 0; row < fn->frows; ++row ) + { + lperrow = 0; + for ( col = 0; col < fn->fcols; col += 32 ) + { + if ( lperrow == 0 ) + printf( " {" ); + else if ( lperrow % 6 == 0 ) + { + printf( ",\n " ); + lperrow = 0; + } + else + printf( "," ); + l = 0; + for ( scol = col; scol < MIN( col + 32, fn->fcols ); ++scol ) + { + l <<= 1; + if ( fn->oldfont[row][scol] ) + l |= 1; + } + printf( "0x%08lxL", l ); + ++lperrow; + } + printf( "}%s\n", row == fn->frows - 1 ? "" : "," ); + } + printf( " };\n" ); + } + else { + struct glyph* glyph; + int i, j, ng; + + ng = 0; + for (i = 0; i < 256; i++) + if (fn->glyph[i]) + ng++; + + printf("static struct glyph _g[%d] = {\n", ng); + for (i = 0; i < 256; i++) { + if (!(glyph = fn->glyph[i])) + continue; + + printf(" { %d, %d, %d, %d, %d, \"", glyph->width, glyph->height, + glyph->x, glyph->y, glyph->xadd); + + for (j = 0; j < glyph->width * glyph->height; j++) + if (glyph->bmap[j]) + printf("\\1"); + else + printf("\\0"); + + ng--; + printf("\" }%s\n", ng ? "," : ""); + } + printf("};\n"); + + printf("static struct font default_bdffont = { %d, %d, %d, %d, {\n", + fn->maxwidth, fn->maxheight, fn->x, fn->y); + + for (i = 0; i < 256; i++) { + if (fn->glyph[i]) + printf(" _g + %d", ng++); + else + printf(" 0"); + + if (i != 255) printf(","); + printf("\n"); + } + + printf(" }\n};\n"); + exit(0); + + } + +} + + +/* 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; + + if (!(fp = fopen(name, "rb"))) + return 0; + + expect("STARTFONT"); + + 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 (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; + } + 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; + + 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; + + *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; + } + } + + expect("ENDCHAR"); + + 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; + + numchar--; + } + } + } + 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; + } + vec[n++] = s; + if (n >= mk_max) + break; + while (*s && !ISSPACE(*s)) + s++; + } + vec[n] = 0; + return n; +} diff --git a/lib/libpbmvms.c b/lib/libpbmvms.c new file mode 100644 index 00000000..8a2a54f8 --- /dev/null +++ b/lib/libpbmvms.c @@ -0,0 +1,734 @@ +/*************************************************************************** + This file contains library routines needed to build Netpbm for VMS. + However, as of 2000.05.26, when these were split out of libpbm1.c + (where they were all switched by #ifdef VMS), there is no evidence + that anyone is building Netpbm for VMS. +****************************************************************************/ + +/* + * @(#)argproc.c 1.0 89/02/01 Mark Pizzolato (mark@infopiz.uucp) + */ + +#ifndef lint +char argproc_version[] = "@(#)argproc.c VMS uucp Version infopiz-1.0"; +#endif + +#include "includes.h" /* System include files, system dependent */ + + +/* + * getredirection() is intended to aid in porting C programs + * to VMS (Vax-11 C) which does not have '>' and '<' + * I/O redirection, along with a command line pipe mechanism + * using the '|' AND background command execution '&'. + * The piping mechanism will probably work with almost any 'filter' type + * of program. With suitable modification, it may useful for other + * portability problems as well. + * + * Author: Mark Pizzolato mark@infopiz.UUCP + */ +struct list_item + { + struct list_item *next; + char *value; + }; + +int +getredirection(ac, av) +int *ac; +char ***av; +/* + * Process vms redirection arg's. Exit if any error is seen. + * If getredirection() processes an argument, it is erased + * from the vector. getredirection() returns a new argc and argv value. + * In the event that a background command is requested (by a trailing "&"), + * this routine creates a background subprocess, and simply exits the program. + * + * Warning: do not try to simplify the code for vms. The code + * presupposes that getredirection() is called before any data is + * read from stdin or written to stdout. + * + * Normal usage is as follows: + * + * main(argc, argv) + * int argc; + * char *argv[]; + * { + * getredirection(&argc, &argv); + * } + */ +{ + int argc = *ac; /* Argument Count */ + char **argv = *av; /* Argument Vector */ + char *ap; /* Argument pointer */ + int j; /* argv[] index */ + extern int errno; /* Last vms i/o error */ + int item_count = 0; /* Count of Items in List */ + int new_file; /* flag, true if '>' used */ + struct list_item *list_head = 0; /* First Item in List */ + struct list_item *list_tail; /* Last Item in List */ + char *in = NULL; /* Input File Name */ + char *out = NULL; /* Output File Name */ + char *outmode = "w"; /* Mode to Open Output File */ + int cmargc = 0; /* Piped Command Arg Count */ + char **cmargv = NULL;/* Piped Command Arg Vector */ + stat_t statbuf; /* fstat buffer */ + + /* + * First handle the case where the last thing on the line ends with + * a '&'. This indicates the desire for the command to be run in a + * subprocess, so we satisfy that desire. + */ + ap = argv[argc-1]; + if (0 == strcmp("&", ap)) + exit(background_process(--argc, argv)); + if ('&' == ap[strlen(ap)-1]) + { + ap[strlen(ap)-1] = '\0'; + exit(background_process(argc, argv)); + } + /* + * Now we handle the general redirection cases that involve '>', '>>', + * '<', and pipes '|'. + */ + for (new_file = 0, j = 0; j < argc; ++j) + { + if (0 == strcmp("<", argv[j])) + { + if (j+1 >= argc) + { + errno = EINVAL; + perror("No input file"); + exit(EXIT_ERR); + } + in = argv[++j]; + continue; + } + if ('<' == *(ap = argv[j])) + { + in = 1 + ap; + continue; + } + if (0 == strcmp(">", ap)) + { + if (j+1 >= argc) + { + errno = EINVAL; + perror("No output file"); + exit(EXIT_ERR); + } + out = argv[++j]; + new_file = 1; + continue; + } + if ('>' == *ap) + { + if ('>' == ap[1]) + { + outmode = "a"; + if ('\0' == ap[2]) + out = argv[++j]; + else + out = 2 + ap; + } + else + { out = 1 + ap; new_file = 1; } + continue; + } + if (0 == strcmp("|", argv[j])) + { + if (j+1 >= argc) + { + errno = EPIPE; + perror("No command to Pipe to"); + exit(EXIT_ERR); + } + cmargc = argc-(j+1); + cmargv = &argv[j+1]; + argc = j; + outmode = "wb"; /* pipes are binary mode devices */ + continue; + } + if ('|' == *(ap = argv[j])) + { + ++argv[j]; + cmargc = argc-j; + cmargv = &argv[j]; + argc = j; + outmode = "wb"; /* pipes are binary mode devices */ + continue; + } + expand_wild_cards(ap, &list_head, &list_tail, &item_count); + } + /* + * Allocate and fill in the new argument vector, Some Unix's terminate + * the list with an extra null pointer. + */ + argv = *av = calloc(item_count+1, sizeof(char *)); + for (j = 0; j < item_count; ++j, list_head = list_head->next) + argv[j] = list_head->value; + *ac = item_count; + if (cmargv != NULL) + { + char subcmd[1024]; + static char *pipe_and_fork(); + + if (out != NULL) + { + errno = EINVAL; + perror("Invalid '|' and '>' specified"); + exit(EXIT_ERR); + } + strcpy(subcmd, cmargv[0]); + for (j = 1; j < cmargc; ++j) + { + strcat(subcmd, " \""); + strcat(subcmd, cmargv[j]); + strcat(subcmd, "\""); + } + out = pipe_and_fork(subcmd); + outmode = "wb"; + } + + /* Check for input from a pipe (mailbox) */ + + if(fstat(0, &statbuf) == 0){ + if(strncmp(statbuf.st_dev, "MB", 2) == 0 || + strncmp(statbuf.st_dev, "_MB", 3) == 0){ + + /* Input from a pipe, reopen it in binary mode to disable */ + /* carriage control processing. */ + + if (in != NULL){ + errno = EINVAL; + perror("Invalid '|' and '<' specified"); + exit(EXIT_ERR); + } + freopen(statbuf.st_dev, "rb", stdin); + } + } + else { + perror("fstat failed"); + exit(EXIT_ERR); + } + +#ifdef __ALPHA + /*, "mbc=32", "mbf=2"))) blows up on the ALPHA cbm 11/08/92 */ + if ((in != NULL) && (NULL == freopen(in, "r", stdin))) + { +#else + if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2"))) + { +#endif + perror(in); /* Can't find file */ + exit(EXIT_ERR); /* Is a fatal error */ + } +#ifdef __ALPHA + if ((out != NULL) && (NULL == freopen(out, outmode, stdout))) + { +#else + if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2"))) + { +#endif + perror(ap); /* Error, can't write or append */ + exit(EXIT_ERR); /* Is a fatal error */ + } + + if ( new_file ) { + /* + * We are making an explicit output file, fstat the file and + * declare exit handler to be able change the file to fixed length + * records if necessary. + */ + char fname[256]; + static int outfile_rundown(), check_outfile_filetype(); + static stat_t ofile_stat; + static struct exit_control_block { + struct exit_control_block *flink; + int (*exit_routine)(); + int arg_count; + int *status_address; /* arg 1 */ + stat_t *stat; /* arg 2 */ + int exit_status; + int skew[128]; + } exit_block = + { 0, outfile_rundown, 2, &exit_block.exit_status, &ofile_stat, 0 }; + + if ( fstat ( fileno(stdout), &ofile_stat ) == 0 ) + sys$dclexh ( &exit_block ); + else fprintf(stderr,"Error fstating stdout - %s\n", + strerror(errno,vaxc$errno) ); + + if ( fgetname ( stdout, fname, 0 ) ) check_outfile_filetype ( fname ); + } +#ifdef DEBUG + fprintf(stderr, "Arglist:\n"); + for (j = 0; j < *ac; ++j) + fprintf(stderr, "argv[%d] = '%s'\n", j, argv[j]); +#endif +} + +static int binary_outfile = 0; +void set_outfile_binary() { binary_outfile = 1; return; } + +/* + * Check if output file should be set to binary (fixed 512) on exit based + * upon the filetype. + */ +static int check_outfile_filetype ( name ) + char *name; +{ + char *binary_filetypes, *ext, *p, *t; + binary_filetypes = (char *) getenv ( "PBM_BINARY_FILETYPES" ); + if ( binary_filetypes == NULL ) return 0; + + ext = strchr ( name, '.' ); if ( ext == NULL ) return 0; + ext++; + t = strrchr ( name, '.' ); if ( t != NULL ) *t = '\0'; + + for ( p = binary_filetypes; *p != '\0'; p++ ) { + for ( t = p; + (*p != '\0' ) && (strchr ( ", ", *p ) == NULL); + p++ ) *p = toupper(*p); + *p = '\0'; + + if ( strcmp ( t, ext ) == 0 ) { + binary_outfile = 1; + break; + } + } + return binary_outfile; +} + + +/* + * Exit handler to set output file to binary on image exit. + */ +static int outfile_rundown ( reason, statbuf ) + int *reason; + stat_t *statbuf; +{ + int code, channel, status, LIB$GETDVI(), sys$assign(), sys$qiow(); + long int fib_desc[2], devchar; + short int iosb[4]; + $DESCRIPTOR(device, statbuf->st_dev); + struct fibdef fib; /* File information block (XQP) */ + struct atrdef atr[2]; + struct fat { + unsigned char rtype, ratattrib; + unsigned short int rsize, hiblk[2], efblk[2], ffbyte, maxrec, defext, gbc; + unsigned short int reserved[4], versions; + } rat; + + if ( !binary_outfile ) return 1; /* leave file alone */ + /* + * Assign channel to device listed in stat block and test if it is + * a directory structured device, returning if not. + */ + device.dsc$w_length = strlen ( statbuf->st_dev ); + status = sys$assign ( &device, &channel, 0, 0 ); + if ((status & 1) == 0) { fprintf(stderr, + "assign error to %s (%d)\n", device.dsc$a_pointer, status); + return status; } + code = DVI$_DEVCHAR; + status = LIB$GETDVI ( &code, &channel, 0, &devchar ); + if ((status & 1) == 0) { fprintf(stderr, "getdvi error: %d\n", status); + return status; } + if ( (devchar & DEV$M_DIR) == 0 ) return 1; + /* + * Build File Information Block and Atrribute block. + */ +#ifdef __ALPHA + fib.fib$w_fid[0] = statbuf->st_ino[0]; + fib.fib$w_fid[1] = statbuf->st_ino[1]; + fib.fib$w_fid[2] = statbuf->st_ino[2]; +#else + fib.fib$r_fid_overlay.fib$w_fid[0] = statbuf->st_ino[0]; + fib.fib$r_fid_overlay.fib$w_fid[1] = statbuf->st_ino[1]; + fib.fib$r_fid_overlay.fib$w_fid[2] = statbuf->st_ino[2]; +#endif + + atr[0].atr$w_size = sizeof(rat); + atr[0].atr$w_type = ATR$C_RECATTR; + atr[0].atr$l_addr = &rat; + atr[1].atr$w_size = 0; atr[1].atr$w_type = 0; + /* + * Perform access function with read_attributes sub-function. + */ + freopen ( "SYS$OUTPUT:", "a", stdout ); + fib_desc[0] = 10; fib_desc[1] = (long int) &fib; +#ifdef __ALPHA + fib.fib$l_acctl = FIB$M_WRITE; +#else + fib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_WRITE; +#endif + status = sys$qiow ( 0, channel, IO$_ACCESS|IO$M_ACCESS, + &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 ); + /* + * If status successful, update record byte and perform a MODIFY. + */ + if ( (status&1) == 1 ) status = iosb[0]; + if ( (status&1) == 1 ) { + rat.rtype = 1; /* fixed length records */ + rat.rsize = 512; /* 512 byte block recrods */ + rat.ratattrib = 0; /* Record attributes: none */ + + status = sys$qiow + ( 0, channel, IO$_MODIFY, &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 ); + if ( (status&1) == 1 ) status = iosb[0]; + } + sys$dassgn ( channel ); + if ( (status & 1) == 0 ) fprintf ( stderr, + "Failed to convert output file to binary format, status: %d\n", status); + return status; +} + + +static add_item(head, tail, value, count) +struct list_item **head; +struct list_item **tail; +char *value; +int *count; +{ + if (*head == 0) + { + if (NULL == (*head = calloc(1, sizeof(**head)))) + { + errno = ENOMEM; + perror(""); + exit(EXIT_ERR); + } + *tail = *head; + } + else + if (NULL == ((*tail)->next = calloc(1, sizeof(**head)))) + { + errno = ENOMEM; + perror(""); + exit(EXIT_ERR); + } + else + *tail = (*tail)->next; + (*tail)->value = value; + ++(*count); +} + +static expand_wild_cards(item, head, tail, count) +char *item; +struct ltem_list **head; +struct ltem_list **tail; +int *count; +{ +int expcount = 0; +int context = 0; +int status; +int status_value; +int had_version; +$DESCRIPTOR(filespec, item); +$DESCRIPTOR(defaultspec, "SYS$DISK:[]*.*;"); +$DESCRIPTOR(resultspec, ""); + + if (strcspn(item, "*%") == strlen(item)) + { + add_item(head, tail, item, count); + return; + } + resultspec.dsc$b_dtype = DSC$K_DTYPE_T; + resultspec.dsc$b_class = DSC$K_CLASS_D; + resultspec.dsc$a_pointer = NULL; + filespec.dsc$w_length = strlen(item); + /* + * Only return version specs, if the caller specified a version + */ + had_version = strchr(item, ';'); + while (1 == (1&lib$find_file(&filespec, &resultspec, &context, + &defaultspec, 0, &status_value, &0))) + { + char *string; + char *c; + + if (NULL == (string = calloc(1, resultspec.dsc$w_length+1))) + { + errno = ENOMEM; + perror(""); + exit(EXIT_ERR); + } + strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length); + string[resultspec.dsc$w_length] = '\0'; + if (NULL == had_version) + *((char *)strrchr(string, ';')) = '\0'; + /* + * Be consistent with what the C RTL has already done to the rest of + * the argv items and lowercase all of these names. + */ + for (c = string; *c; ++c) + if (isupper(*c)) + *c = tolower(*c); + add_item(head, tail, string, count); + ++expcount; + } + if (expcount == 0) + add_item(head, tail, item, count); + lib$sfree1_dd(&resultspec); + lib$find_file_end(&context); +} + +static int child_st[2]; /* Event Flag set when child process completes */ + +static short child_chan;/* I/O Channel for Pipe Mailbox */ + +static exit_handler(status) +int *status; +{ +short iosb[4]; + + if (0 == child_st[0]) + { +#ifdef DEBUG + fprintf(stderr, "Waiting for Child Process to Finnish . . .\n"); +#endif + fflush(stdout); /* Have to flush pipe for binary data to */ + /* terminate properly -- */ +#ifdef DEBUG + fprintf(stderr, " stdout flushed. . .\n"); +#endif + sys$qio(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0); +#ifdef DEBUG + fprintf(stderr, " Pipe terminated. . .\n"); +#endif + fclose(stdout); +#ifdef DEBUG + fprintf(stderr, " stdout closed. . .\n"); +#endif + sys$synch(0, child_st); + sys$dassgn(child_chan); + } +#ifdef DEBUG + fprintf(stderr, " sync done. . .\n"); +#endif +} + +#include /* System Information Definitions */ + +static sig_child(chan) +int chan; +{ +#ifdef DEBUG + fprintf(stderr, "Child Completion AST, st: %x\n", child_st[0] ); +#endif + if (child_st[0] == 0) + { child_st[0] = 1; } + sys$setef ( 0 ); +} + +static struct exit_control_block + { + struct exit_control_block *flink; + int (*exit_routine)(); + int arg_count; + int *status_address; + int exit_status; + } exit_block = + { + 0, + exit_handler, + 1, + &exit_block.exit_status, + 0 + }; + +static char *pipe_and_fork(cmd) +char *cmd; +{ + $DESCRIPTOR(cmddsc, cmd); + static char mbxname[64], ef = 0; + $DESCRIPTOR(mbxdsc, mbxname); + short iosb[4]; + int status; + int pid; + struct + { + short dna_buflen; + short dna_itmcod; + char *dna_buffer; + short *dna_retlen; + int listend; + } itmlst = + { + sizeof(mbxname), + DVI$_DEVNAM, + mbxname, + &mbxdsc.dsc$w_length, + 0 + }; + int mbxsize; + struct + { + short mbf_buflen; + short mbf_itmcod; + int *mbf_maxbuf; + short *mbf_retlen; + int listend; + } syiitmlst = + { + sizeof(mbxsize), + SYI$_MAXBUF, + &mbxsize, + 0, + 0 + }; + + cmddsc.dsc$w_length = strlen(cmd); + /* + * Get the SYSGEN parameter MAXBUF, and the smaller of it and 2048 as + * the size of the 'pipe' mailbox. + */ + if (1 == (1&(vaxc$errno = sys$getsyiw(0, 0, 0, &syiitmlst, iosb, 0, 0, 0)))) + vaxc$errno = iosb[0]; + if (0 == (1&vaxc$errno)) + { + errno = EVMSERR; + perror("Can't get SYSGEN parameter value for MAXBUF"); + exit(EXIT_ERR); + } + if (mbxsize > 2048) + mbxsize = 2048; + if (0 == (1&(vaxc$errno = sys$crembx(0, &child_chan, mbxsize, mbxsize, 0, 0, 0)))) + { + errno = EVMSERR; + perror("Can't create pipe mailbox"); + exit(EXIT_ERR); + } + if (1 == (1&(vaxc$errno = sys$getdviw(0, child_chan, 0, &itmlst, iosb, + 0, 0, 0)))) + vaxc$errno = iosb[0]; + if (0 == (1&vaxc$errno)) + { + errno = EVMSERR; + perror("Can't get pipe mailbox device name"); + exit(EXIT_ERR); + } + mbxname[mbxdsc.dsc$w_length] = '\0'; +#ifdef DEBUG + fprintf(stderr, "Pipe Mailbox Name = '%s'\n", mbxname); +#endif + if (0 == (1&(vaxc$errno = lib$spawn(&cmddsc, &mbxdsc, 0, &1, + 0, &pid, child_st, &ef, sig_child, + &child_chan)))) + { + errno = EVMSERR; + perror("Can't spawn subprocess"); + exit(EXIT_ERR); + } +#ifdef DEBUG + fprintf(stderr, "Subprocess's Pid = %08X\n", pid); +#endif + sys$dclexh(&exit_block); + return(mbxname); +} + +background_process(argc, argv) +int argc; +char **argv; +{ +char command[2048] = "$"; +$DESCRIPTOR(value, command); +$DESCRIPTOR(cmd, "BACKGROUND$COMMAND"); +$DESCRIPTOR(null, "NLA0:"); +int pid; + + strcat(command, argv[0]); + while (--argc) + { + strcat(command, " \""); + strcat(command, *(++argv)); + strcat(command, "\""); + } + value.dsc$w_length = strlen(command); + if (0 == (1&(vaxc$errno = lib$set_symbol(&cmd, &value)))) + { + errno = EVMSERR; + perror("Can't create symbol for subprocess command"); + exit(EXIT_ERR); + } + if (0 == (1&(vaxc$errno = lib$spawn(&cmd, &null, 0, &17, 0, &pid)))) + { + errno = EVMSERR; + perror("Can't spawn subprocess"); + exit(EXIT_ERR); + } +#ifdef DEBUG + fprintf(stderr, "%s\n", command); +#endif + fprintf(stderr, "%08X\n", pid); + return(EXIT_OK); +} + +/* got this off net.sources */ + +#ifdef VMS +#define index strchr +#endif /*VMS*/ + +/* + * get option letter from argument vector + */ +int opterr = 1, /* useless, never set or used */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define EMSG "" +#define tell(s) fputs(progname,stderr);fputs(s,stderr); \ + fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); + +getopt(nargc,nargv,ostr) +int nargc; +char **nargv, + *ostr; +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + char *index(); + char *progname; + + if(!*place) { /* update scanning pointer */ + if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF); + if (*place == '-') { /* found "--" */ + ++optind; + return(EOF); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) { + if(!*place) ++optind; +#ifdef VMS + progname = strrchr(nargv[0],']'); +#else + progname = rindex(nargv[0],'/'); +#endif + if (!progname) progname = nargv[0]; else progname++; + tell(": illegal option -- "); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) ++optind; + } + else { /* need an argument */ + if (*place) optarg = place; /* no white space */ + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; +#ifdef VMS + progname = strrchr(nargv[0],']'); +#else + progname = rindex(nargv[0],'/'); +#endif + if (!progname) progname = nargv[0]; else progname++; + tell(": option requires an argument -- "); + } + else optarg = nargv[optind]; /* white space */ + place = EMSG; + ++optind; + } + return(optopt); /* dump back option letter */ +} diff --git a/lib/libpgm.h b/lib/libpgm.h new file mode 100644 index 00000000..ba51f9a9 --- /dev/null +++ b/lib/libpgm.h @@ -0,0 +1,24 @@ +/* This is the intra-libnetpbm interface header file for libpgm*.c +*/ + +#ifndef LIBPGM_H_INCLUDED +#define LIBPGM_H_INCLUDED + +#include "pgm.h" + +void +pgm_readpgminitrest(FILE * const file, + int * const colsP, + 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 new file mode 100644 index 00000000..75fa365b --- /dev/null +++ b/lib/libpgm1.c @@ -0,0 +1,324 @@ +/* libpgm1.c - pgm utility library part 1 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +/* See libpbm.c for the complicated explanation of this 32/64 bit file + offset stuff. +*/ +#define _FILE_OFFSET_BITS 64 +#define _LARGE_FILES + +#include +#include +#include + +#include "pgm.h" +#include "libpgm.h" +#include "pbm.h" +#include "libpbm.h" +#include "pam.h" +#include "libpam.h" +#include "fileio.h" +#include "mallocvar.h" + + +gray * +pgm_allocrow(unsigned int const cols) { + + gray * grayrow; + + MALLOCARRAY(grayrow, cols); + + if (grayrow == NULL) + pm_error("Unable to allocate space for a %u-column gray row", cols); + + return grayrow; +} + + + +void +pgm_init(int * const argcP, + char ** const argv) { + + pbm_init( argcP, argv ); +} + + + +void +pgm_nextimage(FILE * const file, int * const eofP) { + pm_nextimage(file, eofP); +} + + + +void +pgm_readpgminitrest(FILE * const fileP, + int * const colsP, + int * const rowsP, + gray * const maxvalP) { + + gray maxval; + + /* Read size. */ + *colsP = (int)pm_getuint(fileP); + *rowsP = (int)pm_getuint(fileP); + + /* Read maxval. */ + maxval = pm_getuint(fileP); + if (maxval > PGM_OVERALLMAXVAL) + pm_error("maxval of input image (%u) is too large. " + "The maximum allowed by PGM is %u.", + maxval, PGM_OVERALLMAXVAL); + if (maxval == 0) + pm_error("maxval of input image is zero."); + + *maxvalP = maxval; +} + + + +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 +pgm_readpgminit(FILE * const fileP, + int * const colsP, + int * const rowsP, + gray * const maxvalP, + int * const formatP) { + + int realFormat; + + /* 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); + + /* Mathematically, it makes the most sense for the maxval of a PBM + file seen as a PGM to be 1. But we tried this for a while and + found that it causes unexpected results and frequent need for a + Pnmdepth stage to convert the maxval to 255. You see, when you + transform a PGM file in a way that causes interpolated gray shades, + there's no in-between value to use when maxval is 1. It's really + hard even to discover that your lack of Pnmdepth is your problem. + So we pick 255, which is the most common PGM maxval, and the highest + resolution you can get without increasing the size of the PGM + image. + + So this means some programs that are capable of exploiting the + bi-level nature of a PBM file must be PNM programs instead of PGM + programs. + */ + + *maxvalP = PGM_MAXMAXVAL; + break; + + case PAM_TYPE: + pnm_readpaminitrestaspnm(fileP, colsP, rowsP, maxvalP, formatP); + + if (PAM_FORMAT_TYPE(*formatP) != PGM_TYPE) + pm_error("Format of PAM input is not consistent with PGM"); + + break; + + default: + pm_error("bad magic number - not a pgm or pbm file"); + } + validateComputableSize(*colsP, *rowsP); +} + + + +gray +pgm_getrawsample(FILE * const file, + gray const maxval) { + + if (maxval < 256) { + /* The sample is just one byte. Read it. */ + return(pm_getrawbyte(file)); + } 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]); + } +} + + + +void +pgm_readpgmrow(FILE * const file, + gray * const grayrow, + int const cols, + gray const maxval, + int const format) { + + switch (format) { + case PGM_FORMAT: { + unsigned int col; + for (col = 0; col < cols; ++col) { + grayrow[col] = pm_getuint(file); + if (grayrow[col] > maxval) + pm_error("value out of bounds (%u > %u)", + grayrow[col], maxval); + } + } + 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); + } + 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); + } + break; + + default: + pm_error( "can't happen" ); + } +} + + + +gray ** +pgm_readpgm(FILE * const file, + int * const colsP, + int * const rowsP, + gray * const maxvalP) { + + gray** grays; + int row; + int format; + + pgm_readpgminit( file, colsP, rowsP, maxvalP, &format ); + + grays = pgm_allocarray( *colsP, *rowsP ); + + for ( row = 0; row < *rowsP; ++row ) + pgm_readpgmrow( file, grays[row], *colsP, *maxvalP, format ); + + return grays; +} + + + +void +pgm_check(FILE * const file, + enum pm_check_type const check_type, + int const format, + int const cols, + int const rows, + gray const maxval, + enum pm_check_code * const retval_p) { + + if (rows < 0) + pm_error("Invalid number of rows passed to pgm_check(): %d", rows); + if (cols < 0) + pm_error("Invalid number of columns passed to pgm_check(): %d", cols); + + if (check_type != PM_CHECK_BASIC) { + if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE; + } else if (PGM_FORMAT_TYPE(format) == PBM_TYPE) { + pbm_check(file, check_type, format, cols, rows, retval_p); + } else if (format != RPGM_FORMAT) { + if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; + } else { + pm_filepos const bytes_per_row = cols * (maxval > 255 ? 2 : 1); + 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 new file mode 100644 index 00000000..7a04f09b --- /dev/null +++ b/lib/libpgm2.c @@ -0,0 +1,226 @@ +/* libpgm2.c - pgm utility library part 2 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "pgm.h" +#include "libpgm.h" + + + +void +pgm_writepgminit(FILE * const fileP, + int const cols, + int const rows, + gray const maxval, + int const forceplain) { + + bool const plainFormat = forceplain || pm_plain_output; + + if (maxval > PGM_OVERALLMAXVAL && !plainFormat) + pm_error("too-large maxval passed to ppm_writepgminit(): %d.\n" + "Maximum allowed by the PGM format is %d.", + maxval, PGM_OVERALLMAXVAL); + + fprintf(fileP, "%c%c\n%d %d\n%d\n", + PGM_MAGIC1, + plainFormat || maxval >= 1<<16 ? PGM_MAGIC2 : RPGM_MAGIC2, + cols, rows, maxval ); +#ifdef VMS + if (!plainFormat) + set_outfile_binary(); +#endif +} + + + +static void +putus(unsigned short const n, + FILE * const fileP) { + + if (n >= 10) + putus(n / 10, fileP); + putc('0' + n % 10, fileP); +} + + + +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, + unsigned char * const rowBuffer) { + + /* single byte samples. */ + + unsigned int col; + unsigned int bufferCursor; + + bufferCursor = 0; + + for (col = 0; col < cols; ++col) + rowBuffer[bufferCursor++] = grayrow[col]; +} + + + + +static void +format2bpsRow(const gray * const grayrow, + unsigned int const cols, + unsigned char * const rowBuffer) { + + /* two byte samples. */ + + unsigned int col; + unsigned int bufferCursor; + + bufferCursor = 0; + + for (col = 0; col < cols; ++col) { + gray const val = grayrow[col]; + + rowBuffer[bufferCursor++] = val >> 8; + rowBuffer[bufferCursor++] = (unsigned char) val; + } +} + + + +static void +writepgmrowraw(FILE * const fileP, + const gray * const grayrow, + unsigned int const cols, + gray const maxval) { + + unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; + unsigned int const bytesPerRow = cols * bytesPerSample; + + unsigned char * rowBuffer; + ssize_t rc; + + MALLOCARRAY(rowBuffer, bytesPerRow); + + if (rowBuffer == NULL) + pm_error("Unable to allocate memory for row buffer " + "for %u columns", cols); + + if (maxval < 256) + format1bpsRow(grayrow, cols, rowBuffer); + else + format2bpsRow(grayrow, cols, rowBuffer); + + rc = fwrite(rowBuffer, 1, bytesPerRow, fileP); + + if (rc < 0) + pm_error("Error writing row. fwrite() errno=%d (%s)", + errno, strerror(errno)); + else if (rc != bytesPerRow) + pm_error("Error writing row. Short write of %u bytes " + "instead of %u", rc, bytesPerRow); + + free(rowBuffer); +} + + + +static void +writepgmrowplain(FILE * const fileP, + const gray * const grayrow, + unsigned int const cols, + gray const maxval) { + + int col, charcount; + + charcount = 0; + for (col = 0; col < cols; ++col) { + if (charcount >= 65) { + putc('\n', fileP); + charcount = 0; + } else if (charcount > 0) { + putc(' ', fileP); + ++charcount; + } +#ifdef DEBUG + if (grayrow[col] > maxval) + pm_error("value out of bounds (%u > %u)", grayrow[col], maxval); +#endif /*DEBUG*/ + putus((unsigned short)grayrow[col], fileP); + charcount += 3; + } + if (charcount > 0) + putc('\n', fileP); +} + + + +void +pgm_writepgmrow(FILE * const fileP, + const gray * const grayrow, + int const cols, + gray const maxval, + int const forceplain) { + + if (forceplain || pm_plain_output || maxval >= 1<<16) + writepgmrowplain(fileP, grayrow, cols, maxval); + else + writepgmrowraw(fileP, grayrow, cols, maxval); +} + + + +void +pgm_writepgm(FILE * const fileP, + gray ** const grays, + int const cols, + int const rows, + gray const maxval, + int const forceplain) { + + unsigned int row; + + pgm_writepgminit(fileP, cols, rows, maxval, forceplain); + + for (row = 0; row < rows; ++row) + pgm_writepgmrow(fileP, grays[row], cols, maxval, forceplain); +} diff --git a/lib/libpm.c b/lib/libpm.c new file mode 100644 index 00000000..2e563a09 --- /dev/null +++ b/lib/libpm.c @@ -0,0 +1,1494 @@ +/************************************************************************** + libpm.c +*************************************************************************** + This is the most fundamental Netpbm library. It contains routines + not specific to any particular Netpbm format. + + 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 */ + +#include +#include +#include +#include +#include +#ifdef __DJGPP__ + #include +#endif + +#include "pm_c_util.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). + */ + +int pm_plain_output; + /* Boolean: programs should produce output in plain format */ + +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 + don't normally return in that case). + + User sets this to something in his own extra-library context. + Libnetpbm routines that have something that needs to be cleaned up + preempt it. + + NULL, which is the default value, means when a libnetpbm function + encounters an error, it causes the process to exit. + */ + + + +void +pm_setjmpbuf(jmp_buf * const jmpbufP) { + pm_jmpbufP = jmpbufP; +} + + + +void +pm_setjmpbufsave(jmp_buf * const jmpbufP, + jmp_buf ** const oldJmpbufPP) { + + *oldJmpbufPP = pm_jmpbufP; + pm_jmpbufP = jmpbufP; +} + + + +void +pm_longjmp(void) { + + if (pm_jmpbufP) + longjmp(*pm_jmpbufP, 1); + else + exit(1); +} + + + +void +pm_usage(const char usage[]) { + pm_error("usage: %s %s", pm_progname, usage); +} + + + +void +pm_perror(const char reason[] ) { + + 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)); +} + + + +void PM_GNU_PRINTF_ATTR(1,2) +pm_message(const char format[], ...) { + + va_list args; + + va_start(args, format); + + if (pm_showmessages) { + fprintf(stderr, "%s: ", pm_progname); + vfprintf(stderr, format, args); + fputc('\n', stderr); + } + va_end(args); +} + + + +void PM_GNU_PRINTF_ATTR(1,2) +pm_error(const char format[], ...) { + va_list args; + + va_start(args, format); + + fprintf(stderr, "%s: ", pm_progname); + vfprintf(stderr, format, args); + fputc('\n', stderr); + va_end(args); + + pm_longjmp(); +} + + +/* Variable-sized arrays. */ + +char * +pm_allocrow(unsigned int const cols, + unsigned int const size) { + + char * itrow; + + if (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); + if (itrow == NULL) + pm_error("out of memory allocating a row"); + + return itrow; +} + + + +void +pm_freerow(char * const itrow) { + free(itrow); +} + + + +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. + + We use a special format where we tack on an extra element to the row + index to indicate the format of the array. + + We have two ways of allocating the space: fragmented and + unfragmented. In both, the row index (plus the extra element) is + in one block of memory. In the fragmented format, each row is + also in an independent memory block, and the extra row pointer is + NULL. In the unfragmented format, all the rows are in a single + block of memory called the row heap and the extra row pointer is + the address of that block. + + We use unfragmented format if possible, but if the allocation of the + row heap fails, we fall back to fragmented. +-----------------------------------------------------------------------------*/ + char** rowIndex; + char * rowheap; + + 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 */ + + for (row = 0; row < rows; ++row) + rowIndex[row] = &(rowheap[row * cols * size]); + } + return rowIndex; +} + + + +void +pm_freearray(char ** const rowIndex, + int const rows) { + + void * const rowheap = rowIndex[rows]; + + if (rowheap != NULL) + free(rowheap); + else { + unsigned int row; + for (row = 0; row < rows; ++row) + pm_freerow(rowIndex[row]); + } + free(rowIndex); +} + + + +/* Case-insensitive keyword matcher. */ + +int +pm_keymatch(char * const strarg, + const char * const keywordarg, + int const minchars) { + int len; + const char *keyword; + char *str; + + str = strarg; + keyword = keywordarg; + + len = strlen( str ); + if ( len < minchars ) + return 0; + while ( --len >= 0 ) + { + register char c1, c2; + + c1 = *str++; + c2 = *keyword++; + if ( c2 == '\0' ) + return 0; + if ( ISUPPER( c1 ) ) + c1 = tolower( c1 ); + if ( ISUPPER( c2 ) ) + c2 = tolower( c2 ); + if ( c1 != c2 ) + return 0; + } + return 1; +} + + +/* Log base two hacks. */ + +int +pm_maxvaltobits(int const maxval) { + if ( maxval <= 1 ) + return 1; + else if ( maxval <= 3 ) + return 2; + else if ( maxval <= 7 ) + return 3; + else if ( maxval <= 15 ) + return 4; + else if ( maxval <= 31 ) + return 5; + else if ( maxval <= 63 ) + return 6; + else if ( maxval <= 127 ) + return 7; + else if ( maxval <= 255 ) + return 8; + else if ( maxval <= 511 ) + return 9; + else if ( maxval <= 1023 ) + return 10; + else if ( maxval <= 2047 ) + return 11; + else if ( maxval <= 4095 ) + return 12; + else if ( maxval <= 8191 ) + return 13; + else if ( maxval <= 16383 ) + return 14; + else if ( maxval <= 32767 ) + return 15; + else if ( (long) maxval <= 65535L ) + return 16; + else + pm_error( "maxval of %d is too large!", maxval ); + return -1; /* Should never come here */ +} + +int +pm_bitstomaxval(int const bits) { + return ( 1 << bits ) - 1; +} + + +unsigned int PURE_FN_ATTR +pm_lcm(unsigned int const x, + unsigned int const y, + unsigned int const z, + unsigned int const limit) { +/*---------------------------------------------------------------------------- + Compute the least common multiple of 'x', 'y', and 'z'. If it's bigger than + 'limit', though, just return 'limit'. +-----------------------------------------------------------------------------*/ + unsigned int biggest; + unsigned int candidate; + + if (x == 0 || y == 0 || z == 0) + pm_error("pm_lcm(): Least common multiple of zero taken."); + + biggest = MAX(x, MAX(y,z)); + + candidate = biggest; + while (((candidate % x) != 0 || /* not a multiple of x */ + (candidate % y) != 0 || /* not a multiple of y */ + (candidate % z) != 0 ) && /* not a multiple of z */ + candidate <= limit) + candidate += biggest; + + if (candidate > limit) + candidate = limit; + + return candidate; +} + + +/* Initialization. */ + + +#ifdef VMS +static const char * +vmsProgname(int * const argcP, char * argv[]) { + char **temp_argv = argv; + int old_argc = *argcP; + int i; + const char * retval; + + getredirection( argcP, &temp_argv ); + if (*argcP > old_argc) { + /* Number of command line arguments has increased */ + fprintf( stderr, "Sorry!! getredirection() for VMS has " + "changed the argument list!!!\n"); + fprintf( stderr, "This is intolerable at the present time, " + "so we must stop!!!\n"); + exit(1); + } + for (i=0; i<*argcP; i++) + argv[i] = temp_argv[i]; + retval = strrchr( argv[0], '/'); + if ( retval == NULL ) retval = rindex( argv[0], ']'); + if ( retval == NULL ) retval = rindex( argv[0], '>'); + + return retval; +} +#endif + + + +void +pm_init(const char * const progname, unsigned int const flags) { +/*---------------------------------------------------------------------------- + Initialize static variables that Netpbm library routines use. + + Any user of Netpbm library routines is expected to call this at the + beginning of this program, before any other Netpbm library routines. + + A program may call this via pm_proginit() instead, though. +-----------------------------------------------------------------------------*/ + pm_setMessage(FALSE, NULL); + + pm_progname = progname; + +#ifdef O_BINARY +#ifdef HAVE_SETMODE + /* Set the stdin and stdout mode to binary. This means nothing on Unix, + but matters on Windows. + + Note that stdin and stdout aren't necessarily image files. In + particular, stdout is sometimes text for human consumption, + typically printed on the terminal. Binary mode isn't really + appropriate for that case. We do this setting here without + any knowledge of how stdin and stdout are being used because it is + easy. But we do make an exception for the case that we know the + file is a terminal, to get a little closer to doing the right + thing. + */ + if (!isatty(0)) setmode(0,O_BINARY); /* Standard Input */ + if (!isatty(1)) setmode(1,O_BINARY); /* Standard Output */ +#endif /* HAVE_SETMODE */ +#endif /* O_BINARY */ +} + + + +static void +showVersion(void) { + pm_message( "Using libnetpbm from Netpbm Version: %s", NETPBM_VERSION ); +#if defined(COMPILE_TIME) && defined(COMPILED_BY) + pm_message( "Compiled %s by user \"%s\"", + COMPILE_TIME, COMPILED_BY ); +#endif +#ifdef BSD + pm_message( "BSD defined" ); +#endif /*BSD*/ +#ifdef SYSV +#ifdef VMS + pm_message( "VMS & SYSV defined" ); +#else + pm_message( "SYSV defined" ); +#endif +#endif /*SYSV*/ +#ifdef MSDOS + pm_message( "MSDOS defined" ); +#endif /*MSDOS*/ +#ifdef AMIGA + pm_message( "AMIGA defined" ); +#endif /* AMIGA */ + { + const char * rgbdef; + pm_message( "RGB_ENV='%s'", RGBENV ); + rgbdef = getenv(RGBENV); + if( rgbdef ) + pm_message( "RGBENV= '%s' (env vbl set to '%s')", + RGBENV, rgbdef ); + else + pm_message( "RGBENV= '%s' (env vbl is unset)", RGBENV); + } +} + + + +static void +showNetpbmHelp(const char progname[]) { +/*---------------------------------------------------------------------------- + Tell the user where to get help for this program, assuming it is a Netpbm + program (a program that comes with the Netpbm package, as opposed to a + program that just uses the Netpbm libraries). + + Tell him to go to the URL listed in the Netpbm configuration file. + The Netpbm configuration file is the file named by the NETPBM_CONF + environment variable, or /etc/netpbm if there is no such environment + variable. + + If the configuration file doesn't exist or can't be read, or doesn't + contain a DOCURL value, tell him to go to a hardcoded source for + documentation. +-----------------------------------------------------------------------------*/ + const char * netpbmConfigFileName; + FILE * netpbmConfigFile; + char * docurl; + + if (getenv("NETPBM_CONF")) + netpbmConfigFileName = getenv("NETPBM_CONF"); + else + netpbmConfigFileName = "/etc/netpbm"; + + netpbmConfigFile = fopen(netpbmConfigFileName, "r"); + if (netpbmConfigFile == NULL) { + pm_message("Unable to open Netpbm configuration file '%s'. " + "Errno = %d (%s). " + "Use the NETPBM_CONF environment variable " + "to control the identity of the Netpbm configuration file.", + netpbmConfigFileName,errno, strerror(errno)); + docurl = NULL; + } else { + docurl = NULL; /* default */ + while (!feof(netpbmConfigFile) && !ferror(netpbmConfigFile)) { + char line[80+1]; + fgets(line, sizeof(line), netpbmConfigFile); + if (line[0] != '#') { + sscanf(line, "docurl=%s", docurl); + } + } + if (docurl == NULL) + pm_message("No 'docurl=' line in Netpbm configuration file '%s'.", + netpbmConfigFileName); + } + if (docurl == NULL) + pm_message("We have no reliable indication of where the Netpbm " + "documentation is, but try " + "http://netpbm.sourceforge.net or email " + "Bryan Henderson (bryanh@giraffe-data.com) for help."); + else + pm_message("This program is part of the Netpbm package. Find " + "documentation for it at %s/%s\n", docurl, progname); +} + + + +void +pm_proginit(int * const argcP, char * argv[]) { +/*---------------------------------------------------------------------------- + Do various initialization things that all programs in the Netpbm package, + and programs that emulate such programs, should do. + + This includes processing global options. + + This includes calling pm_init() to initialize the Netpbm libraries. +-----------------------------------------------------------------------------*/ + int argn, i; + const char * progname; + bool showmessages; + bool show_version; + /* We're supposed to just show the version information, then exit the + program. + */ + bool show_help; + /* We're supposed to just tell user where to get help, then exit the + program. + */ + + /* Extract program name. */ +#ifdef VMS + progname = vmsProgname(argcP, argv); +#else + progname = strrchr( argv[0], '/'); +#endif + if (progname == NULL) + progname = argv[0]; + else + ++progname; + + pm_init(progname, 0); + + /* Check for any global args. */ + showmessages = TRUE; + show_version = FALSE; + show_help = FALSE; + pm_plain_output = FALSE; + for (argn = 1; argn < *argcP; ++argn) { + if (pm_keymatch(argv[argn], "-quiet", 6) || + pm_keymatch(argv[argn], "--quiet", 7)) + showmessages = FALSE; + else if (pm_keymatch(argv[argn], "-version", 8) || + pm_keymatch(argv[argn], "--version", 9)) + show_version = TRUE; + else if (pm_keymatch(argv[argn], "-help", 5) || + pm_keymatch(argv[argn], "--help", 6) || + pm_keymatch(argv[argn], "-?", 2)) + show_help = TRUE; + else if (pm_keymatch(argv[argn], "-plain", 6) || + pm_keymatch(argv[argn], "--plain", 7)) + pm_plain_output = TRUE; + else + continue; + for (i = argn + 1; i <= *argcP; ++i) + argv[i - 1] = argv[i]; + --(*argcP); + } + + pm_setMessage((unsigned int) showmessages, NULL); + + if (show_version) { + showVersion(); + exit( 0 ); + } else if (show_help) { + pm_error("Use 'man %s' for help.", progname); + /* If we can figure out a way to distinguish Netpbm programs from + other programs using the Netpbm libraries, we can do better here. + */ + if (0) + showNetpbmHelp(progname); + exit(0); + } +} + + +void +pm_setMessage(int const newState, int * const oldStateP) { + + if (oldStateP) + *oldStateP = pm_showmessages; + + pm_showmessages = !!newState; +} + + +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. + + In the most ordinary case, this is simply the argument itself. + + But if the argument contains a slash, it is the part of the argument + 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, + but truncated at 64 characters. +-----------------------------------------------------------------------------*/ + static char retval[64+1]; + char *slash_pos; + + /* Chop any directories off the left end */ + slash_pos = strrchr(arg0, '/'); + + if (slash_pos == NULL) { + strncpy(retval, arg0, sizeof(retval)); + retval[sizeof(retval)-1] = '\0'; + } else { + strncpy(retval, slash_pos +1, sizeof(retval)); + retval[sizeof(retval)-1] = '\0'; + } + + /* Chop any .exe off the right end */ + if (strlen(retval) >= 4 && strcmp(retval+strlen(retval)-4, ".exe") == 0) + retval[strlen(retval)-4] = 0; + + return(retval); +} + + + +/* 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); + + strfree(tmpfile); + + return fileP; +} + + + +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."); +} + + + +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; + } + 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)); +} + + + +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_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; +} + + + diff --git a/lib/libpnm1.c b/lib/libpnm1.c new file mode 100644 index 00000000..82f99b93 --- /dev/null +++ b/lib/libpnm1.c @@ -0,0 +1,222 @@ +/* libpnm1.c - pnm utility library part 1 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include +#include + +#include "pnm.h" + +#include "ppm.h" +#include "libppm.h" + +#include "pgm.h" +#include "libpgm.h" + +#include "pbm.h" +#include "libpbm.h" + +#include "pam.h" +#include "libpam.h" + +#include "mallocvar.h" + + + +xel * +pnm_allocrow(unsigned int const cols) { + + xel * xelrow; + + MALLOCARRAY(xelrow, cols); + + if (xelrow == NULL) + pm_error("Unable to allocate space for a %u-column xel row", cols); + + return xelrow; +} + + + +void +pnm_init( argcP, argv ) + int* argcP; + char* argv[]; + { + ppm_init( argcP, argv ); + } + +void +pnm_nextimage(FILE *file, int * const eofP) { + pm_nextimage(file, eofP); +} + + + +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 +pnm_readpnminit(FILE * const fileP, + int * const colsP, + int * const rowsP, + xelval * const maxvalP, + int * const formatP) { + + int realFormat; + + /* Check magic number. */ + realFormat = pm_readmagicnumber(fileP); + switch (PAM_FORMAT_TYPE(realFormat)) { + case PPM_TYPE: { + pixval maxval; + *formatP = realFormat; + ppm_readppminitrest(fileP, colsP, rowsP, &maxval); + *maxvalP = maxval; + } + break; + + case PGM_TYPE: { + gray maxval; + + *formatP = realFormat; + pgm_readpgminitrest(fileP, colsP, rowsP, &maxval); + *maxvalP = maxval; + } + break; + + case PBM_TYPE: + *formatP = realFormat; + pbm_readpbminitrest(fileP, colsP, rowsP); + *maxvalP = 1; + break; + + case PAM_TYPE: { + gray maxval; + pnm_readpaminitrestaspnm(fileP, colsP, rowsP, &maxval, formatP); + *maxvalP = maxval; + } + break; + + default: + pm_error("bad magic number - not a ppm, pgm, or pbm file"); + } + validateComputableSize(*colsP, *rowsP); +} + + + +void +pnm_readpnmrow(FILE * const fileP, + xel * const xelrow, + int const cols, + xelval const maxval, + int const format) { + + switch (PNM_FORMAT_TYPE(format)) { + case PPM_TYPE: + 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 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; + + default: + pm_error("INTERNAL ERROR. Impossible format."); + } +} + + + +xel ** +pnm_readpnm(FILE * const fileP, + int * const colsP, + int * const rowsP, + xelval * const maxvalP, + int * const formatP) { + + xel ** xels; + int row; + + pnm_readpnminit(fileP, colsP, rowsP, maxvalP, formatP); + + xels = pnm_allocarray(*colsP, *rowsP); + + for (row = 0; row < *rowsP; ++row) + pnm_readpnmrow(fileP, xels[row], *colsP, *maxvalP, *formatP); + + return xels; +} + + + +void +pnm_check(FILE * const fileP, + enum pm_check_type const check_type, + int const format, + int const cols, + int const rows, + int const maxval, + enum pm_check_code * const retvalP) { + + switch (PNM_FORMAT_TYPE(format)) { + case PBM_TYPE: + pbm_check(fileP, check_type, format, cols, rows, retvalP); + break; + case PGM_TYPE: + pgm_check(fileP, check_type, format, cols, rows, maxval, retvalP); + break; + case PPM_TYPE: + ppm_check(fileP, check_type, format, cols, rows, maxval, retvalP); + break; + default: + pm_error("pnm_check() called with invalid format parameter"); + } +} diff --git a/lib/libpnm2.c b/lib/libpnm2.c new file mode 100644 index 00000000..aae78d52 --- /dev/null +++ b/lib/libpnm2.c @@ -0,0 +1,128 @@ +/* libpnm2.c - pnm utility library part 2 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pm_c_util.h" + +#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, + int const cols, + int const rows, + 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_writeppminit(fileP, cols, rows, (pixval) maxval, plainFormat); + break; + + case PGM_TYPE: + pgm_writepgminit(fileP, cols, rows, (gray) maxval, plainFormat); + break; + + case PBM_TYPE: + pbm_writepbminit(fileP, cols, rows, plainFormat); + break; + + default: + pm_error("invalid format argument received by pnm_writepnminit(): %d" + "PNM_FORMAT_TYPE(format) must be %d, %d, or %d", + format, PBM_TYPE, PGM_TYPE, PPM_TYPE); + } +} + + + +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; + + switch (PNM_FORMAT_TYPE(format)) { + case PPM_TYPE: + ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, + plainFormat); + break; + + case PGM_TYPE: { + gray* grayrow; + unsigned int col; + + grayrow = pgm_allocrow(cols); + + for (col = 0; col < cols; ++col) + grayrow[col] = PNM_GET1(xelrow[col]); + + pgm_writepgmrow(fileP, grayrow, cols, (gray) maxval, plainFormat); + + pgm_freerow( grayrow ); + } + break; + + case PBM_TYPE: { + bit* bitrow; + unsigned int col; + + bitrow = pbm_allocrow(cols); + + 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; + + default: + pm_error("invalid format argument received by pnm_writepnmrow(): %d" + "PNM_FORMAT_TYPE(format) must be %d, %d, or %d", + format, PBM_TYPE, PGM_TYPE, PPM_TYPE); + } +} + + + +void +pnm_writepnm(FILE * const fileP, + xel ** const xels, + int const cols, + int const rows, + xelval const maxval, + int const format, + int const forceplain) { + + unsigned int row; + + pnm_writepnminit(fileP, cols, rows, maxval, format, forceplain); + + for (row = 0; row < rows; ++row) + pnm_writepnmrow(fileP, xels[row], cols, maxval, format, forceplain); +} diff --git a/lib/libpnm3.c b/lib/libpnm3.c new file mode 100644 index 00000000..c9c9a1b0 --- /dev/null +++ b/lib/libpnm3.c @@ -0,0 +1,393 @@ +/* libpnm3.c - pnm utility library part 3 +** +** Copyright (C) 1989, 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "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__*/ +xel +pnm_backgroundxel( xels, cols, rows, maxval, format ) + xel** xels; + int cols, rows, format; + xelval maxval; +#endif /*__STDC__*/ + { + xel bgxel, ul, ur, ll, lr; + + /* Guess a good background value. */ + ul = xels[0][0]; + ur = xels[0][cols-1]; + 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; + 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; + + 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; + } + + 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__*/ + { + 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) ) + { + 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; + + 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; + } + + default: + 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; + + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + PPM_ASSIGN( x, maxval, maxval, maxval ); + break; + + case PGM_TYPE: + PNM_ASSIGN1( x, maxval ); + break; + + case PBM_TYPE: + PNM_ASSIGN1( x, maxval ); + break; + + default: + pm_error( "Invalid format passed to pnm_whitexel()"); + } + + return x; + } + +#if __STDC__ +xel +pnm_blackxel( xelval maxval, int format ) +#else /*__STDC__*/ +xel +pnm_blackxel( maxval, format ) + xelval maxval; + int format; +#endif /*__STDC__*/ + { + xel x; + + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + PPM_ASSIGN( x, 0, 0, 0 ); + break; + + case PGM_TYPE: + PNM_ASSIGN1( x, (xelval) 0 ); + break; + + case PBM_TYPE: + PNM_ASSIGN1( x, (xelval) 0 ); + break; + + default: + pm_error( "Invalid format passed to pnm_blackxel(): %d", format); + } + + return x; + } + +void +pnm_invertxel(xel* const xP, + xelval const maxval, + int const format) { + + switch (PNM_FORMAT_TYPE(format)) { + case PPM_TYPE: + PPM_ASSIGN(*xP, + maxval - PPM_GETR(*xP), + maxval - PPM_GETG(*xP), + maxval - PPM_GETB(*xP)); + break; + + case PGM_TYPE: + PNM_ASSIGN1(*xP, maxval - PNM_GET1(*xP)); + break; + + case PBM_TYPE: + PNM_ASSIGN1(*xP, (PNM_GET1(*xP) == 0) ? maxval : 0); + break; + + default: + pm_error("Invalid format passed to pnm_invertxel()"); + } +} + + + +#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; + + for ( row = 0; row < rows; ++row ) + pnm_promoteformatrow( + 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; + + if ( ( PNM_FORMAT_TYPE(format) == PPM_TYPE && + ( PNM_FORMAT_TYPE(newformat) == PGM_TYPE || + PNM_FORMAT_TYPE(newformat) == PBM_TYPE ) ) || + ( PNM_FORMAT_TYPE(format) == PGM_TYPE && + PNM_FORMAT_TYPE(newformat) == PBM_TYPE ) ) + pm_error( "pnm_promoteformatrow: can't promote downwards!" ); + + /* Are we promoting to the same type? */ + if ( PNM_FORMAT_TYPE(format) == PNM_FORMAT_TYPE(newformat) ) + { + if ( PNM_FORMAT_TYPE(format) == PBM_TYPE ) + return; + if ( newmaxval < maxval ) + pm_error( + "pnm_promoteformatrow: can't decrease maxval - try using pnmdepth" ); + if ( newmaxval == maxval ) + return; + /* Increase maxval. */ + switch ( PNM_FORMAT_TYPE(format) ) + { + case PGM_TYPE: + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + PNM_ASSIGN1( + *xP, (int) PNM_GET1(*xP) * newmaxval / maxval ); + break; + + case PPM_TYPE: + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + PPM_DEPTH( *xP, *xP, maxval, newmaxval ); + break; + + default: + pm_error( "Invalid old format passed to pnm_promoteformatrow()" ); + } + return; + } + + /* We must be promoting to a higher type. */ + switch ( PNM_FORMAT_TYPE(format) ) + { + case PBM_TYPE: + switch ( PNM_FORMAT_TYPE(newformat) ) + { + case PGM_TYPE: + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + if ( PNM_GET1(*xP) == 0 ) + PNM_ASSIGN1( *xP, 0 ); + else + PNM_ASSIGN1( *xP, newmaxval ); + break; + + case PPM_TYPE: + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + if ( PNM_GET1(*xP) == 0 ) + PPM_ASSIGN( *xP, 0, 0, 0 ); + else + PPM_ASSIGN( *xP, newmaxval, newmaxval, newmaxval ); + break; + + default: + pm_error( "Invalid new format passed to pnm_promoteformatrow()" ); + } + break; + + case PGM_TYPE: + switch ( PNM_FORMAT_TYPE(newformat) ) + { + case PPM_TYPE: + if ( newmaxval < maxval ) + pm_error( + "pnm_promoteformatrow: can't decrease maxval - try using pnmdepth" ); + if ( newmaxval == maxval ) + { + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + PPM_ASSIGN( + *xP, PNM_GET1(*xP), PNM_GET1(*xP), PNM_GET1(*xP) ); + } + else + { /* Increase maxval. */ + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + PPM_ASSIGN( + *xP, (int) PNM_GET1(*xP) * newmaxval / maxval, + (int) PNM_GET1(*xP) * newmaxval / maxval, + (int) PNM_GET1(*xP) * newmaxval / maxval ); + } + break; + + default: + pm_error( "Invalid new format passed to pnm_promoteformatrow()" ); + } + break; + + default: + pm_error( "Invalid old format passed to pnm_promoteformatrow()" ); + } + } + + +pixel +xeltopixel(xel const inputxel) { + + pixel outputpixel; + + PPM_ASSIGN(outputpixel, + PNM_GET1(inputxel), PNM_GET1(inputxel), PNM_GET1(inputxel)); + return outputpixel; +} diff --git a/lib/libppm.h b/lib/libppm.h new file mode 100644 index 00000000..ec2a173d --- /dev/null +++ b/lib/libppm.h @@ -0,0 +1,15 @@ +/* This is the intra-libnetpbm interface header file for libppm*.c +*/ + +#ifndef LIBPPM_H_INCLUDED +#define LIBPPM_H_INCLUDED + +#include "ppm.h" + +void +ppm_readppminitrest(FILE * const file, + int * const colsP, + int * const rowsP, + pixval * const maxvalP); + +#endif diff --git a/lib/libppm1.c b/lib/libppm1.c new file mode 100644 index 00000000..57a1db7d --- /dev/null +++ b/lib/libppm1.c @@ -0,0 +1,328 @@ +/* libppm1.c - ppm utility library part 1 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +/* See libpbm.c for the complicated explanation of this 32/64 bit file + offset stuff. +*/ +#define _FILE_OFFSET_BITS 64 +#define _LARGE_FILES + +#include +#include +#include +#include "ppm.h" +#include "libppm.h" +#include "pgm.h" +#include "libpgm.h" +#include "pbm.h" +#include "libpbm.h" +#include "pam.h" +#include "libpam.h" +#include "fileio.h" +#include "mallocvar.h" + + +pixel * +ppm_allocrow(unsigned int const cols) { + + pixel * pixelrow; + + MALLOCARRAY(pixelrow, cols); + + if (pixelrow == 0) + pm_error("Unable to allocate space for a %u-column pixel row", cols); + + return pixelrow; +} + + + +void +ppm_init( argcP, argv ) + int* argcP; + char* argv[]; + { + pgm_init( argcP, argv ); + } + +void +ppm_nextimage(FILE * const fileP, + int * const eofP) { + pm_nextimage(fileP, eofP); +} + + + +void +ppm_readppminitrest(FILE * const fileP, + int * const colsP, + int * const rowsP, + pixval * const maxvalP) { + unsigned int maxval; + + /* Read size. */ + *colsP = (int)pm_getuint(fileP); + *rowsP = (int)pm_getuint(fileP); + + /* Read maxval. */ + maxval = pm_getuint(fileP); + if (maxval > PPM_OVERALLMAXVAL) + pm_error("maxval of input image (%u) is too large. " + "The maximum allowed by the PPM is %u.", + maxval, PPM_OVERALLMAXVAL); + if (maxval == 0) + pm_error("maxval of input image is zero."); + + *maxvalP = maxval; +} + + + +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 +ppm_readppminit(FILE * const fileP, + int * const colsP, + int * const rowsP, + pixval * const maxvalP, + int * const formatP) { + + int realFormat; + + /* Check magic number. */ + realFormat = pm_readmagicnumber(fileP); + switch (PAM_FORMAT_TYPE(realFormat)) { + case PPM_TYPE: + *formatP = realFormat; + ppm_readppminitrest(fileP, colsP, rowsP, maxvalP); + break; + + case PGM_TYPE: + *formatP = realFormat; + pgm_readpgminitrest(fileP, colsP, rowsP, maxvalP); + break; + + case PBM_TYPE: + *formatP = realFormat; + *maxvalP = 1; + pbm_readpbminitrest(fileP, colsP, rowsP); + break; + + case PAM_TYPE: + pnm_readpaminitrestaspnm(fileP, colsP, rowsP, maxvalP, formatP); + break; + + default: + pm_error("bad magic number %d - not a PPM, PGM, PBM, or PAM file", + PAM_FORMAT_TYPE(*formatP)); + } + validateComputableSize(*colsP, *rowsP); +} + + + +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); + } + } + 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); + + if (rowBuffer == NULL) + pm_error("Unable to allocate memory for row buffer " + "for %u columns", cols); + + rc = fread(rowBuffer, 1, bytesPerRow, fileP); + + if (feof(fileP)) + pm_error("Unexpected EOF reading row of PPM image."); + else if (ferror(fileP)) + 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); + + 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); + } + } + free(rowBuffer); + } + break; + + case PGM_FORMAT: + case RPGM_FORMAT: { + gray * const grayrow = pgm_allocrow(cols); + unsigned int col; + + 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); + } + break; + + case PBM_FORMAT: + case RPBM_FORMAT: { + bit * const bitrow = pbm_allocrow(cols); + unsigned int col; + + 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); + } + break; + + default: + pm_error("Invalid format code"); + } +} + + + +pixel** +ppm_readppm(FILE * const fileP, + int * const colsP, + int * const rowsP, + pixval * const maxvalP) { + pixel** pixels; + int row; + int format; + + ppm_readppminit(fileP, colsP, rowsP, maxvalP, &format); + + pixels = ppm_allocarray(*colsP, *rowsP); + + for (row = 0; row < *rowsP; ++row) + ppm_readppmrow(fileP, pixels[row], *colsP, *maxvalP, format); + + return pixels; +} + + + +void +ppm_check(FILE * const fileP, + enum pm_check_type const check_type, + int const format, + int const cols, + int const rows, + pixval const maxval, + enum pm_check_code * const retval_p) { + + if (rows < 0) + pm_error("Invalid number of rows passed to ppm_check(): %d", rows); + if (cols < 0) + pm_error("Invalid number of columns passed to ppm_check(): %d", cols); + + if (check_type != PM_CHECK_BASIC) { + if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE; + } else if (PPM_FORMAT_TYPE(format) == PBM_TYPE) { + pbm_check(fileP, check_type, format, cols, rows, retval_p); + } else if (PPM_FORMAT_TYPE(format) == PGM_TYPE) { + pgm_check(fileP, check_type, format, cols, rows, maxval, retval_p); + } else if (format != RPPM_FORMAT) { + if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; + } else { + pm_filepos const bytes_per_row = cols * 3 * (maxval > 255 ? 2 : 1); + pm_filepos const need_raster_size = rows * bytes_per_row; + + pm_check(fileP, check_type, need_raster_size, retval_p); + } +} diff --git a/lib/libppm2.c b/lib/libppm2.c new file mode 100644 index 00000000..52c4ab16 --- /dev/null +++ b/lib/libppm2.c @@ -0,0 +1,208 @@ +/* libppm2.c - ppm utility library part 2 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include +#include + + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "ppm.h" +#include "libppm.h" + +void +ppm_writeppminit(FILE* const fileP, + int const cols, + int const rows, + pixval const maxval, + int const forceplain) { + + bool const plainFormat = forceplain || pm_plain_output; + + if (maxval > PPM_OVERALLMAXVAL && !plainFormat) + pm_error("too-large maxval passed to ppm_writeppminit(): %d." + "Maximum allowed by the PPM format is %d.", + maxval, PPM_OVERALLMAXVAL); + + fprintf(fileP, "%c%c\n%d %d\n%d\n", + PPM_MAGIC1, + plainFormat || maxval >= 1<<16 ? PPM_MAGIC2 : RPPM_MAGIC2, + cols, rows, maxval ); +#ifdef VMS + if (!plainFormat) + set_outfile_binary(); +#endif +} + + + +static void +putus(unsigned short const n, + FILE * const fileP) { + + if (n >= 10) + putus(n / 10, fileP); + putc('0' + n % 10, fileP); +} + + + +static void +format1bpsRow(const pixel * const pixelrow, + unsigned int const cols, + unsigned char * const rowBuffer) { + + /* single byte samples. */ + + unsigned int col; + unsigned int bufferCursor; + + bufferCursor = 0; + + for (col = 0; col < cols; ++col) { + rowBuffer[bufferCursor++] = PPM_GETR(pixelrow[col]); + rowBuffer[bufferCursor++] = PPM_GETG(pixelrow[col]); + rowBuffer[bufferCursor++] = PPM_GETB(pixelrow[col]); + } +} + + + + +static void +format2bpsRow(const pixel * const pixelrow, + unsigned int const cols, + unsigned char * const rowBuffer) { + + /* two byte samples. */ + + unsigned int col; + unsigned int bufferCursor; + + bufferCursor = 0; + + for (col = 0; col < cols; ++col) { + pixval const r = PPM_GETR(pixelrow[col]); + pixval const g = PPM_GETG(pixelrow[col]); + pixval const b = PPM_GETB(pixelrow[col]); + + rowBuffer[bufferCursor++] = r >> 8; + rowBuffer[bufferCursor++] = (unsigned char)r; + rowBuffer[bufferCursor++] = g >> 8; + rowBuffer[bufferCursor++] = (unsigned char)g; + rowBuffer[bufferCursor++] = b >> 8; + rowBuffer[bufferCursor++] = (unsigned char)b; + } +} + + + +static void +ppm_writeppmrowraw(FILE * const fileP, + const pixel * const pixelrow, + unsigned int const cols, + pixval const maxval ) { + + unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; + unsigned int const bytesPerRow = cols * 3 * bytesPerSample; + + unsigned char * rowBuffer; + ssize_t rc; + + MALLOCARRAY(rowBuffer, bytesPerRow); + + if (rowBuffer == NULL) + pm_error("Unable to allocate memory for row buffer " + "for %u columns", cols); + + if (maxval < 256) + format1bpsRow(pixelrow, cols, rowBuffer); + else + format2bpsRow(pixelrow, cols, rowBuffer); + + rc = fwrite(rowBuffer, 1, bytesPerRow, fileP); + + if (rc < 0) + pm_error("Error writing row. fwrite() errno=%d (%s)", + errno, strerror(errno)); + else if (rc != bytesPerRow) + pm_error("Error writing row. Short write of %u bytes " + "instead of %u", rc, bytesPerRow); + + free(rowBuffer); +} + + + + +static void +ppm_writeppmrowplain(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval) { + + unsigned int col; + unsigned int charcount; + + charcount = 0; + + for (col = 0; col < cols; ++col) { + if (charcount >= 65) { + putc('\n', fileP); + charcount = 0; + } else if (charcount > 0) { + putc(' ', fileP); + putc(' ', fileP); + charcount += 2; + } + putus(PPM_GETR(pixelrow[col]), fileP); + putc(' ', fileP); + putus(PPM_GETG(pixelrow[col]), fileP); + putc(' ', fileP); + putus(PPM_GETB(pixelrow[col]), fileP); + charcount += 11; + } + if (charcount > 0) + putc('\n', fileP); +} + + + +void +ppm_writeppmrow(FILE * const fileP, + 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); + else + ppm_writeppmrowraw(fileP, pixelrow, cols, maxval); +} + + + +void +ppm_writeppm(FILE * const file, + pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const forceplain) { + int row; + + ppm_writeppminit(file, cols, rows, maxval, forceplain); + + for (row = 0; row < rows; ++row) + ppm_writeppmrow(file, pixels[row], cols, maxval, forceplain); +} diff --git a/lib/libppmcmap.c b/lib/libppmcmap.c new file mode 100644 index 00000000..a9efccbc --- /dev/null +++ b/lib/libppmcmap.c @@ -0,0 +1,650 @@ +/* libppm3.c - ppm utility library part 3 +** +** Colormap routines. +** +** Copyright (C) 1989, 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" +#include "libppm.h" +#include "mallocvar.h" +#include "ppmcmap.h" + +#define HASH_SIZE 20023 + +#define ppm_hashpixel(p) ( ( ( (long) PPM_GETR(p) * 33023 + \ + (long) PPM_GETG(p) * 30013 + \ + (long) PPM_GETB(p) * 27011 ) \ + & 0x7fffffff ) % HASH_SIZE ) + +colorhist_vector +ppm_computecolorhist( pixel ** const pixels, + const int cols, const int rows, const int maxcolors, + int * const colorsP ) { +/*---------------------------------------------------------------------------- + Compute a color histogram for the image described by 'pixels', + 'cols', and 'rows'. I.e. a colorhist_vector containing an entry + for each color in the image and for each one the number of pixels + of that color (i.e. a color histogram). + + If 'maxcolors' is zero, make the output have 5 spare slots at the end + for expansion. + + If 'maxcolors' is nonzero, make the output have 'maxcolors' slots in + it, and if there are more colors than that in the image, don't return + anything except a NULL pointer. +-----------------------------------------------------------------------------*/ + colorhash_table cht; + colorhist_vector chv; + + cht = ppm_computecolorhash(pixels, cols, rows, maxcolors, colorsP); + if (cht == NULL) + chv = NULL; + else { + chv = ppm_colorhashtocolorhist(cht, maxcolors); + ppm_freecolorhash(cht); + } + return chv; +} + + + +colorhist_vector +ppm_computecolorhist2(FILE * const ifp, + const int cols, const int rows, + const pixval maxval, const int format, + const int maxcolors, int * const colorsP ) { + + colorhash_table cht; + colorhist_vector chv; + + cht = ppm_computecolorhash2(ifp, cols, rows, maxval, format, + maxcolors, colorsP); + if (cht ==NULL) + return NULL; + chv = ppm_colorhashtocolorhist(cht, maxcolors); + ppm_freecolorhash(cht); + return chv; +} + + + +void +ppm_addtocolorhist( colorhist_vector chv, + int * const colorsP, const int maxcolors, + const pixel * const colorP, + const int value, const int position ) { + int i, j; + + /* Search colorhist for the color. */ + for ( i = 0; i < *colorsP; ++i ) + if ( PPM_EQUAL( chv[i].color, *colorP ) ) { + /* Found it - move to new slot. */ + if ( position > i ) { + for ( j = i; j < position; ++j ) + chv[j] = chv[j + 1]; + } else if ( position < i ) { + for ( j = i; j > position; --j ) + chv[j] = chv[j - 1]; + } + chv[position].color = *colorP; + chv[position].value = value; + return; + } + if ( *colorsP < maxcolors ) { + /* Didn't find it, but there's room to add it; so do so. */ + for ( i = *colorsP; i > position; --i ) + chv[i] = chv[i - 1]; + chv[position].color = *colorP; + chv[position].value = value; + ++(*colorsP); + } +} + + + +colorhash_table +ppm_alloccolorhash(void) { + colorhash_table cht; + int i; + + MALLOCARRAY(cht, HASH_SIZE); + 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: + + 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. +-----------------------------------------------------------------------------*/ + 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 */ + + rowbuffer = ppm_allocrow(cols); + + /* Go through the entire image, building a hash table of colors. */ + for (row = 0; row < rows; ++row) { + int col; + pixel * pixelrow; /* The row of pixels we are processing */ + + if (ifp) { + ppm_readppmrow(ifp, rowbuffer, cols, maxval, format); + pixelrow = rowbuffer; + } else + pixelrow = pixels[row]; + + for (col = 0; col < cols; ++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 = chl->next); + + if (chl) + 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 { + MALLOCVAR(chl); + if (chl == NULL) + pm_error("out of memory computing hash table"); + chl->ch.color = apixel; + chl->ch.value = 1; + chl->next = cht[hash]; + cht[hash] = chl; + } + } + } + } + ppm_freerow(rowbuffer); + return cht; +} + + + +colorhash_table +ppm_computecolorhash(pixel ** const pixels, + const int cols, const int rows, + const int maxcolors, int * const colorsP) { + + return computecolorhash(pixels, cols, rows, maxcolors, colorsP, + NULL, 0, 0); +} + + + +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 ) { + + return computecolorhash(NULL, cols, rows, maxcolors, colorsP, + ifp, maxval, format); +} + + + +int +ppm_addtocolorhash(colorhash_table const cht, + const pixel * const colorP, + int const value) { +/*---------------------------------------------------------------------------- + Add color *colorP to the color hash 'cht' with associated value 'value'. + + Assume the color is not already in the hash. +-----------------------------------------------------------------------------*/ + int retval; + colorhist_list chl; + + MALLOCVAR(chl); + if (chl == NULL) + retval = -1; + else { + int const hash = ppm_hashpixel(*colorP); + + chl->ch.color = *colorP; + chl->ch.value = value; + chl->next = cht[hash]; + cht[hash] = chl; + retval = 0; + } + return retval; +} + + + +void +ppm_delfromcolorhash(colorhash_table const cht, + const pixel * const colorP) { +/*---------------------------------------------------------------------------- + Delete the color *colorP from the colorhahs 'cht', if it's there. +-----------------------------------------------------------------------------*/ + int hash; + colorhist_list * chlP; + + hash = ppm_hashpixel(*colorP); + for (chlP = &cht[hash]; *chlP; chlP = &(*chlP)->next) { + if (PPM_EQUAL((*chlP)->ch.color, *colorP)) { + /* chlP points to a pointer to the hash chain element we want + to remove. + */ + colorhist_list const chl = *chlP; + *chlP = chl->next; + free(chl); + return; + } + } +} + + + +static unsigned int +colorHashSize(colorhash_table const cht) { +/*---------------------------------------------------------------------------- + Return the number of colors in the colorhash table 'cht' +-----------------------------------------------------------------------------*/ + unsigned int nColors; + /* Number of colors found so far */ + int i; + /* Loop through the hash table. */ + nColors = 0; + for (i = 0; i < HASH_SIZE; ++i) { + colorhist_list chl; + for (chl = cht[i]; chl; chl = chl->next) + ++nColors; + } + return nColors; +} + + + +colorhist_vector +ppm_colorhashtocolorhist(colorhash_table const cht, int const maxcolors) { + + colorhist_vector chv; + colorhist_list chl; + unsigned int chvSize; + + if (maxcolors == 0) + /* We leave space for 5 more colors so caller can add in special + colors like background color and transparent color. + */ + chvSize = colorHashSize(cht) + 5; + else + /* Caller is responsible for making sure there are no more + than 'maxcolors' colors in the colorhash table. NOTE: + Before March 2002, the maxcolors == 0 invocation didn't + exist. + */ + chvSize = maxcolors; + + /* Collate the hash table into a simple colorhist array. */ + MALLOCARRAY(chv, chvSize); + if (chv == NULL) + pm_error("out of memory generating histogram"); + + { + int i, j; + /* Loop through the hash table. */ + j = 0; + for (i = 0; i < HASH_SIZE; ++i) + for (chl = cht[i]; chl; chl = chl->next) { + /* Add the new entry. */ + chv[j] = chl->ch; + ++j; + } + } + return chv; +} + + + +colorhash_table +ppm_colorhisttocolorhash(colorhist_vector const chv, + int const colors) { + colorhash_table cht; + int i, hash; + pixel color; + colorhist_list chl; + + 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; + } + return cht; +} + + + +int +ppm_lookupcolor(colorhash_table const cht, + const pixel * const colorP) { + int hash; + colorhist_list chl; + + hash = ppm_hashpixel(*colorP); + for (chl = cht[hash]; chl; chl = chl->next) + if (PPM_EQUAL(chl->ch.color, *colorP)) + return chl->ch.value; + + return -1; +} + + + +void +ppm_freecolorhist(colorhist_vector const chv) { + free(chv); +} + + + +void +ppm_freecolorhash(colorhash_table const cht) { + + int i; + colorhist_list chl, chlnext; + + for (i = 0; i < HASH_SIZE; ++i) + for (chl = cht[i]; chl != (colorhist_list) 0; chl = chlnext) { + chlnext = chl->next; + free(chl); + } + free(cht); +} + + +/***************************************************************************** + The following "color row" routines are taken from Ingo Wilken's ilbm + package, dated December 1994. Since they're only used by ppmtoilbm + and ilbmtoppm today, they aren't documented or well maintained, but + they seem pretty useful and ought to be used in other programs. + + -Bryan 2001.03.10 +****************************************************************************/ + +/* libcmap2.c - support routines for color rows +** +** Copyright (C) 1994 Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +colorhash_table +ppm_colorrowtocolorhash(colorrow, ncolors) + pixel *colorrow; + int ncolors; +{ + colorhash_table cht; + int i; + + cht = ppm_alloccolorhash(); + for( i = 0; i < ncolors; i++ ) { + if( ppm_lookupcolor(cht, &colorrow[i]) < 0 ) { + if( ppm_addtocolorhash(cht, &colorrow[i], i) < 0 ) + pm_error("out of memory adding to hash table"); + } + } + return cht; +} + + +pixel * +ppm_computecolorrow(pixels, cols, rows, maxcolors, ncolorsP) + pixel **pixels; + int cols, rows, maxcolors; + int *ncolorsP; +{ + int ncolors, row, col; + colorhash_table cht; + pixel *pixrow; + + pixrow = ppm_allocrow(maxcolors); + cht = ppm_alloccolorhash(); ncolors = 0; + for( row = 0; row < rows; row++ ) { + for( col = 0; col < cols; col++ ) { + if( ppm_lookupcolor(cht, &pixels[row][col]) < 0 ) { + if( ncolors >= maxcolors ) { + ppm_freerow(pixrow); + pixrow = (pixel *)0; + ncolors = -1; + goto fail; + } + if( ppm_addtocolorhash(cht, &pixels[row][col], ncolors) < 0 ) + pm_error("out of memory adding to hash table"); + pixrow[ncolors] = pixels[row][col]; + ++ncolors; + } + } + } +fail: + ppm_freecolorhash(cht); + + *ncolorsP = ncolors; + return pixrow; +} + + +pixel * +ppm_mapfiletocolorrow(file, maxcolors, ncolorsP, maxvalP) + FILE *file; + int maxcolors; + int *ncolorsP; + pixval *maxvalP; +{ + int cols, rows, format, row, col, ncolors; + pixel *pixrow, *temprow; + colorhash_table cht; + + pixrow = ppm_allocrow(maxcolors); + + ppm_readppminit(file, &cols, &rows, maxvalP, &format); + temprow = ppm_allocrow(cols); + cht = ppm_alloccolorhash(); ncolors = 0; + for( row = 0; row < rows; row++ ) { + ppm_readppmrow(file, temprow, cols, *maxvalP, format); + for( col = 0; col < cols; col++ ) { + if( ppm_lookupcolor(cht, &temprow[col]) < 0 ) { + if( ncolors >= maxcolors ) { + ppm_freerow(pixrow); + pixrow = (pixel *)0; + ncolors = -1; + goto fail; + } + if( ppm_addtocolorhash(cht, &temprow[col], ncolors) < 0 ) + pm_error("out of memory adding to hash table"); + pixrow[ncolors] = temprow[col]; + ++ncolors; + } + } + } +fail: + ppm_freecolorhash(cht); + ppm_freerow(temprow); + + *ncolorsP = ncolors; + return pixrow; +} + + +static int +pixel_cmp(const void * const a, const void * const b) { + const pixel *p1 = (const pixel *)a, *p2 = (const pixel *)b; + int diff; + + diff = PPM_GETR(*p1) - PPM_GETR(*p2); + if( diff == 0 ) { + diff = PPM_GETG(*p1) - PPM_GETG(*p2); + if( diff == 0 ) + diff = PPM_GETB(*p1) - PPM_GETB(*p2); + } + return diff; +} + +static int (*custom_cmp)(pixel *, pixel *); + +static int +custom_stub(const void * const a, const void * const b) { + return (*custom_cmp)((pixel *)a, (pixel *)b); +} + + +void +ppm_sortcolorrow(pixel * const colorrow, const int ncolors, + int (*cmpfunc)(pixel *, pixel *)) { + + if( cmpfunc ) { + custom_cmp = cmpfunc; + qsort((void *)colorrow, ncolors, sizeof(pixel), custom_stub); + } else + qsort((void *)colorrow, ncolors, sizeof(pixel), pixel_cmp); +} + + + +int +ppm_addtocolorrow(colorrow, ncolorsP, maxcolors, pixelP) + pixel *colorrow; + int *ncolorsP; + int maxcolors; + pixel *pixelP; +{ + int i; + pixval r, g, b; + + r = PPM_GETR(*pixelP); + g = PPM_GETG(*pixelP); + b = PPM_GETB(*pixelP); + + for( i = 0; i < *ncolorsP; i++ ) { + if( PPM_GETR(colorrow[i]) == r && + PPM_GETG(colorrow[i]) == g && + PPM_GETB(colorrow[i]) == b ) + return i; + } + + i = *ncolorsP; + if( i >= maxcolors ) + return -1; + colorrow[i] = *pixelP; + ++*ncolorsP; + return i; +} + + +int +ppm_findclosestcolor(const pixel * const colormap, + int const ncolors, + const pixel * const pP) { + + /* Search colormap for closest match. */ + + int i; + int ind; + unsigned int bestDist; + + bestDist = UINT_MAX; + ind = -1; + + for(i = 0; i < ncolors && bestDist > 0; ++i) { + unsigned int const dist = PPM_DISTANCE(*pP, colormap[i]); + + if (dist < bestDist ) { + ind = i; + bestDist = dist; + } + } + return ind; +} + + +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; + + ppm_writeppminit(ofp, ncolors, 1, maxval, 1); + for( i = 0; i < ncolors; i++ ) + ppm_writeppmrow(ofp, &colormap[i], 1, maxval, 1); +} + + + diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c new file mode 100644 index 00000000..ff1a1e67 --- /dev/null +++ b/lib/libppmcolor.c @@ -0,0 +1,710 @@ +/* +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include +#include +#include +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "ppm.h" +#include "colorname.h" + + +static void +computeHexTable(int hexit[]) { + int i; + for ( i = 0; i < 256; ++i ) + hexit[i] = 1234567890; + hexit['0'] = 0; + hexit['1'] = 1; + hexit['2'] = 2; + hexit['3'] = 3; + hexit['4'] = 4; + hexit['5'] = 5; + hexit['6'] = 6; + hexit['7'] = 7; + hexit['8'] = 8; + hexit['9'] = 9; + hexit['a'] = hexit['A'] = 10; + hexit['b'] = hexit['B'] = 11; + hexit['c'] = hexit['C'] = 12; + hexit['d'] = hexit['D'] = 13; + hexit['e'] = hexit['E'] = 14; + hexit['f'] = hexit['F'] = 15; +} + + + +static long +invRgbnorm(pixval const rgb, + pixval const maxval, + unsigned int const hexDigits) { +/*---------------------------------------------------------------------------- + This is the inverse of 'rgbnorm', below. +-----------------------------------------------------------------------------*/ + long retval; + + switch (hexDigits) { + case 1: + retval = (long)((double) rgb * 15 / maxval + 0.5); + break; + case 2: + retval = (long) ((double) rgb * 255 / maxval + 0.5); + break; + case 3: + retval = (long) ((double) rgb * 4095 / maxval + 0.5); + break; + case 4: + retval = (long) ((double) rgb * 65535UL / maxval + 0.5); + break; + default: + pm_message("Internal error in invRgbnorm()"); + abort(); + } + return retval; +} + + + +static pixval +rgbnorm(long const rgb, + pixval const maxval, + unsigned int const hexDigitCount, + bool const closeOk, + const char * const colorname) { +/*---------------------------------------------------------------------------- + Normalize the color (r, g, or b) value 'rgb', which was specified + with 'hexDigitCount' digits, to a maxval of 'maxval'. If the + number of digits isn't valid, issue an error message and identify + the complete color color specification in error as 'colorname'. + + For example, if the user says 0ff/000/000 and the maxval is 100, + then rgb is 0xff, n is 3, and our result is + 0xff / (16**3-1) * 100 = 6. + +-----------------------------------------------------------------------------*/ + pixval retval; + + assert(hexDigitCount > 0); + + switch (hexDigitCount) { + case 1: + retval = (pixval)((double) rgb * maxval / 15 + 0.5); + break; + case 2: + retval = (pixval) ((double) rgb * maxval / 255 + 0.5); + break; + case 3: + retval = (pixval) ((double) rgb * maxval / 4095 + 0.5); + break; + case 4: + retval = (pixval) ((double) rgb * maxval / 65535L + 0.5); + break; + default: + pm_error("color specifier '%s' has too many digits", colorname); + } + + if (!closeOk) { + long const newrgb = invRgbnorm(retval, maxval, hexDigitCount); + if (newrgb != rgb) + pm_message("WARNING: Component 0x%lx of color '%s' " + "cannot be represented precisely with maxval %u. " + "Approximating as %u.", + rgb, colorname, maxval, retval); + } + return retval; +} + + + +static void +parseNewHexX11(const char colorname[], + pixval const maxval, + bool const closeOk, + pixel * const colorP) { + + int hexit[256]; + + const char* cp; + pixval r,g,b; + pixval rNorm, gNorm, bNorm; + + int i; + + computeHexTable(hexit); + + cp = colorname + 4; + + for (i = 0, r = 0; *cp != '/'; ++i, ++cp) + r = r * 16 + hexit[(int)*cp]; + rNorm = rgbnorm(r, maxval, i, closeOk, colorname); + + for (i = 0, g = 0,++cp; *cp != '/'; ++i, ++cp ) + g = g * 16 + hexit[(int)*cp]; + gNorm = rgbnorm(g, maxval, i, closeOk, colorname); + + for (i = 0, b = 0, ++cp; *cp != '\0'; ++i, ++cp ) + b = b * 16 + hexit[(int)*cp]; + bNorm = rgbnorm(b, maxval, i, closeOk, colorname); + + PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm); +} + + + +static void +parseNewDecX11(char const colorname[], + pixval const maxval, + bool const closeOk, + pixel * const colorP) { + + float const epsilon = 1.0/65536.0; + float fr, fg, fb; + pixval rNorm, gNorm, bNorm; + + if (sscanf( colorname, "rgbi:%f/%f/%f", &fr, &fg, &fb) != 3) + pm_error("invalid color specifier '%s'", colorname); + if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 + || fb < 0.0 || fb > 1.0) + pm_error("invalid color specifier '%s' - " + "values must be between 0.0 and 1.0", colorname ); + + rNorm = fr * maxval + 0.5; + gNorm = fg * maxval + 0.5; + bNorm = fb * maxval + 0.5; + + if (!closeOk) { + if (fabs((double)rNorm/maxval - fr) > epsilon || + fabs((double)gNorm/maxval - fg) > epsilon || + fabs((double)bNorm/maxval - fb) > epsilon) + pm_message("WARNING: Color '%s' cannot be represented " + "precisely with maxval %u. " + "Approximating as (%u,%u,%u).", + colorname, maxval, rNorm, gNorm, bNorm); + } + PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm); +} + + + +static void +parseOldX11(const char colorname[], + pixval const maxval, + bool const closeOk, + pixel * const colorP) { + + int hexit[256]; + long r,g,b; + pixval rNorm, gNorm, bNorm; + + computeHexTable(hexit); + + switch (strlen(colorname) - 1 /* (Number of hex digits) */) { + case 3: + r = hexit[(int)colorname[1]]; + g = hexit[(int)colorname[2]]; + b = hexit[(int)colorname[3]]; + rNorm = rgbnorm(r, maxval, 1, closeOk, colorname); + gNorm = rgbnorm(g, maxval, 1, closeOk, colorname); + bNorm = rgbnorm(b, maxval, 1, closeOk, colorname); + break; + + case 6: + r = (hexit[(int)colorname[1]] << 4 ) + hexit[(int)colorname[2]]; + g = (hexit[(int)colorname[3]] << 4 ) + hexit[(int)colorname[4]]; + b = (hexit[(int)colorname[5]] << 4 ) + hexit[(int)colorname[6]]; + rNorm = rgbnorm(r, maxval, 2, closeOk, colorname); + gNorm = rgbnorm(g, maxval, 2, closeOk, colorname); + bNorm = rgbnorm(b, maxval, 2, closeOk, colorname); + break; + + case 9: + r = (hexit[(int)colorname[1]] << 8) + + (hexit[(int)colorname[2]] << 4) + + (hexit[(int)colorname[3]] << 0); + g = (hexit[(int)colorname[4]] << 8) + + (hexit[(int)colorname[5]] << 4) + + (hexit[(int)colorname[6]] << 0); + b = (hexit[(int)colorname[7]] << 8) + + (hexit[(int)colorname[8]] << 4) + + (hexit[(int)colorname[9]] << 0); + rNorm = rgbnorm(r, maxval, 3, closeOk, colorname); + gNorm = rgbnorm(g, maxval, 3, closeOk, colorname); + bNorm = rgbnorm(b, maxval, 3, closeOk, colorname); + break; + + case 12: + r = (hexit[(int)colorname[1]] << 12) + + (hexit[(int)colorname[2]] << 8) + + (hexit[(int)colorname[3]] << 4) + hexit[(int)colorname[4]]; + g = (hexit[(int)colorname[5]] << 12) + + (hexit[(int)colorname[6]] << 8) + + (hexit[(int)colorname[7]] << 4) + hexit[(int)colorname[8]]; + b = (hexit[(int)colorname[9]] << 12) + + (hexit[(int)colorname[10]] << 8) + + (hexit[(int)colorname[11]] << 4) + hexit[(int)colorname[12]]; + rNorm = rgbnorm(r, maxval, 4, closeOk, colorname); + gNorm = rgbnorm(g, maxval, 4, closeOk, colorname); + bNorm = rgbnorm(b, maxval, 4, closeOk, colorname); + break; + + default: + pm_error("invalid color specifier '%s'", colorname); + } + PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm); +} + + + + +static void +parseOldX11Dec(const char colorname[], + pixval const maxval, + bool const closeOk, + pixel * const colorP) { + + float const epsilon = 1.0/65536.0; + + float fr, fg, fb; + pixval rNorm, gNorm, bNorm; + + if (sscanf(colorname, "%f,%f,%f", &fr, &fg, &fb) != 3) + pm_error("invalid color specifier '%s'", colorname); + if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 + || fb < 0.0 || fb > 1.0) + pm_error("invalid color specifier '%s' - " + "values must be between 0.0 and 1.0", colorname ); + + rNorm = fr * maxval + 0.5; + gNorm = fg * maxval + 0.5; + bNorm = fb * maxval + 0.5; + + if (!closeOk) { + if (fabs((float)rNorm/maxval - fr) > epsilon || + fabs((float)gNorm/maxval - fg) > epsilon || + fabs((float)bNorm/maxval - fb) > epsilon) + pm_message("WARNING: Color '%s' cannot be represented " + "precisely with maxval %u. " + "Approximating as (%u,%u,%u).", + colorname, maxval, rNorm, gNorm, bNorm); + } + PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm); +} + + + +pixel +ppm_parsecolor2(const char * const colorname, + pixval const maxval, + int const closeOk) { + + pixel color; + + if (strncmp(colorname, "rgb:", 4) == 0) + /* It's a new-X11-style hexadecimal rgb specifier. */ + parseNewHexX11(colorname, maxval, closeOk, &color); + else if (strncmp(colorname, "rgbi:", 5) == 0) + /* It's a new-X11-style decimal/float rgb specifier. */ + parseNewDecX11(colorname, maxval, closeOk, &color); + else if (colorname[0] == '#') + /* It's an old-X11-style hexadecimal rgb specifier. */ + parseOldX11(colorname, maxval, closeOk, &color); + else if ((colorname[0] >= '0' && colorname[0] <= '9') || + colorname[0] == '.') + /* It's an old-style decimal/float rgb specifier. */ + parseOldX11Dec(colorname, maxval, closeOk, &color); + else + /* Must be a name from the X-style rgb file. */ + pm_parse_dictionary_name(colorname, maxval, closeOk, &color); + + return color; +} + + + +pixel +ppm_parsecolor(const char * const colorname, + pixval const maxval) { + + return ppm_parsecolor2(colorname, maxval, TRUE); +} + + + +char* +ppm_colorname(const pixel* const colorP, + pixval const maxval, + int const hexok) { + + int r, g, b; + FILE* f; + static char colorname[200]; + + if (maxval == 255) { + r = PPM_GETR(*colorP); + g = PPM_GETG(*colorP); + b = PPM_GETB(*colorP); + } else { + r = (int) PPM_GETR(*colorP) * 255 / (int) maxval; + g = (int) PPM_GETG(*colorP) * 255 / (int) maxval; + b = (int) PPM_GETB(*colorP) * 255 / (int) maxval; + } + + f = pm_openColornameFile(NULL, !hexok); + if (f != NULL) { + int best_diff, this_diff; + bool eof; + + best_diff = 32767; + eof = FALSE; + while (!eof && best_diff > 0 ) { + struct colorfile_entry const ce = pm_colorget(f); + if (ce.colorname) { + this_diff = abs(r - ce.r) + abs(g - ce.g) + abs(b - ce.b); + if (this_diff < best_diff) { + best_diff = this_diff; + strcpy(colorname, ce.colorname); + } + } else + eof = TRUE; + } + fclose(f); + if (best_diff != 32767 && (best_diff == 0 || ! hexok)) + return colorname; + } + + /* Color lookup failed, but caller is willing to take an X11-style + hex specifier, so return that. + */ + sprintf(colorname, "#%02x%02x%02x", r, g, b); + return colorname; +} + + + +#define MAXCOLORNAMES 1000u + +static void +processColorfileEntry(struct colorfile_entry const ce, + colorhash_table const cht, + const char ** const colornames, + pixel * const colors, + unsigned int * const colornameIndexP) { + + if (*colornameIndexP >= MAXCOLORNAMES) + pm_error("Too many colors in colorname dictionary. " + "Max allowed is %u", MAXCOLORNAMES); + else { + pixel color; + + PPM_ASSIGN(color, ce.r, ce.g, ce.b); + + if (ppm_lookupcolor(cht, &color) >= 0) { + /* The color is already in the hash, which means we saw it + earlier in the file. We prefer the first name that the + file gives for each color, so we just ignore the + current entry. + */ + } 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); + } + } +} + + + +static void +readcolordict(const char * const fileName, + bool const mustOpen, + unsigned int * const nColorsP, + const char ** const colornames, + pixel * const colors, + colorhash_table const cht) { + + FILE * colorFile; + + colorFile = pm_openColornameFile(fileName, mustOpen); + + if (colorFile != NULL) { + unsigned int colornameIndex; + bool done; + + colornameIndex = 0; /* initial value */ + done = FALSE; + while (!done) { + struct colorfile_entry const ce = pm_colorget(colorFile); + + if (!ce.colorname) + done = TRUE; + else + processColorfileEntry(ce, cht, colornames, colors, + &colornameIndex); + } + + *nColorsP = colornameIndex; + + while (colornameIndex < MAXCOLORNAMES) + colornames[colornameIndex++] = NULL; + + fclose(colorFile); + } +} + + + +void +ppm_readcolordict(const char * const fileName, + int const mustOpen, + unsigned int * const nColorsP, + const char *** const colornamesP, + pixel ** const colorsP, + colorhash_table * const chtP) { + + colorhash_table cht; + const char ** colornames; + pixel * colors; + unsigned int nColors; + + cht = ppm_alloccolorhash(); + + MALLOCARRAY(colornames, MAXCOLORNAMES); + + colors = ppm_allocrow(MAXCOLORNAMES); + + if (colornames == NULL) + pm_error("Unable to allocate space for colorname table."); + + readcolordict(fileName, mustOpen, &nColors, colornames, colors, cht); + + 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; +} + + + +void +ppm_readcolornamefile(const char * const fileName, + int const mustOpen, + colorhash_table * const chtP, + const char *** const colornamesP) { + + ppm_readcolordict(fileName, mustOpen, NULL, colornamesP, NULL, chtP); +} + + + +void +ppm_freecolornames(const char ** const colornames) { + + unsigned int i; + + for (i = 0; i < MAXCOLORNAMES; ++i) + if (colornames[i]) + free((char *)colornames[i]); + + free(colornames); +} + + + +static unsigned int +nonnegative(unsigned int const arg) { + + if ((int)(arg) < 0) + return 0; + else + return arg; +} + + + +pixel +ppm_color_from_ycbcr(unsigned int const y, + int const cb, + int const cr) { +/*---------------------------------------------------------------------------- + Return the color that has luminance 'y', blue chrominance 'cb', and + red chrominance 'cr'. + + The 3 input values can be on any scale (as long as it's the same + scale for all 3) and the maxval of the returned pixel value is the + same as that for the input values. + + Rounding may cause an output value to be greater than the maxval. +-----------------------------------------------------------------------------*/ + pixel retval; + + PPM_ASSIGN(retval, + y + 1.4022 * cr, + nonnegative(y - 0.7145 * cr - 0.3456 * cb), + y + 1.7710 * cb + ); + + return retval; +} + + + +pixel +ppm_color_from_hsv(struct hsv const hsv, + pixval const maxval) { + + pixel retval; + double R, G, B; + + if (hsv.s == 0) { + R = hsv.v; + G = hsv.v; + B = hsv.v; + } else { + unsigned int const sectorSize = 60; + /* Color wheel is divided into six 60 degree sectors. */ + unsigned int const sector = (hsv.h/sectorSize); + /* The sector in which our color resides. Value is in 0..5 */ + double const f = (hsv.h - sector*sectorSize)/60; + /* The fraction of the way the color is from one side of + our sector to the other side, going clockwise. Value is + in [0, 1). + */ + double const m = (hsv.v * (1 - hsv.s)); + double const n = (hsv.v * (1 - (hsv.s * f))); + double const k = (hsv.v * (1 - (hsv.s * (1 - f)))); + + switch (sector) { + case 0: + R = hsv.v; + G = k; + B = m; + break; + case 1: + R = n; + G = hsv.v; + B = m; + break; + case 2: + R = m; + G = hsv.v; + B = k; + break; + case 3: + R = m; + G = n; + B = hsv.v; + break; + case 4: + R = k; + G = m; + B = hsv.v; + break; + case 5: + R = hsv.v; + G = m; + B = n; + break; + default: + pm_error("Invalid H value passed to color_from_HSV: %f", hsv.h); + } + } + PPM_ASSIGN(retval, + ROUNDU(R * maxval), + ROUNDU(G * maxval), + ROUNDU(B * maxval)); + + return retval; +} + + + +struct hsv +ppm_hsv_from_color(pixel const color, + pixval const maxval) { + + double const epsilon = 1e-5; + + double const R = (double)PPM_GETR(color) / maxval; + double const G = (double)PPM_GETG(color) / maxval; + double const B = (double)PPM_GETB(color) / maxval; + + enum hueSector {SECTOR_RED, SECTOR_GRN, SECTOR_BLU}; + enum hueSector hueSector; + + struct hsv retval; + double range; + + if (R >= G) { + if (R >= B) { + hueSector = SECTOR_RED; + retval.v = R; + } else { + hueSector = SECTOR_BLU; + retval.v = B; + } + } else { + if (G >= B) { + hueSector = SECTOR_GRN; + retval.v = G; + } else { + hueSector = SECTOR_BLU; + retval.v = B; + } + } + + range = retval.v - MIN(R, MIN(G, B)); + + if (retval.v < epsilon) + retval.s = 0.0; + else + retval.s = range/retval.v; + + if (range < epsilon) + /* It's gray, so hue really has no meaning. We arbitrarily pick 0 */ + retval.h = 0.0; + else { + double const cr = (retval.v - R) / range; + double const cg = (retval.v - G) / range; + double const cb = (retval.v - B) / range; + + double angle; /* hue angle, in range -30 - +330 */ + + switch(hueSector) { + case SECTOR_RED: angle = 0.0 + 60.0 * (cb - cg); break; + case SECTOR_GRN: angle = 120.0 + 60.0 * (cr - cb); break; + case SECTOR_BLU: angle = 240.0 + 60.0 * (cg - cr); break; + } + retval.h = angle >= 0.0 ? angle : 360 + angle; + } + + return retval; +} + + diff --git a/lib/libppmd.c b/lib/libppmd.c new file mode 100644 index 00000000..6c764ca1 --- /dev/null +++ b/lib/libppmd.c @@ -0,0 +1,1177 @@ +/* +** +** This library module contains the ppmdraw routines. +** +** Copyright (C) 1989, 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** The character drawing routines are by John Walker +** Copyright (C) 1994 by John Walker, kelvin@fourmilab.ch +*/ + +#include +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "ppm.h" +#include "ppmdfont.h" +#include "ppmdraw.h" + +typedef int qsort_compare(const void *, const void *); + /* A compare function to pass to stdlib.h's qsort() */ + + +#define DDA_SCALE 8192 + +struct penpos { + int x; + int 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) { +/*---------------------------------------------------------------------------- + Draw a single point, assuming that it is within the bounds of the + image. +-----------------------------------------------------------------------------*/ + if (drawproc == PPMD_NULLDRAWPROC) { + const pixel * const pixelP = clientdata; + + assert(x >= 0); assert(x < cols); + assert(y >= 0); assert(y < rows); + + pixels[y][x] = *pixelP; + } else + drawproc(pixels, cols, rows, maxval, x, y, clientdata); +} + + + +void +ppmd_point_drawproc(pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x, + int const y, + const void* const clientdata) { + + if (x >= 0 && x < cols && y >= 0 && y < rows) + pixels[y][x] = *((pixel*)clientdata); +} + + +/* Simple fill routine. */ + +void +ppmd_filledrectangle(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x, + int const y, + int const width, + int const height, + ppmd_drawproc drawProc, + const void * const clientdata) { + + int cx, cy, cwidth, cheight, row; + + /* Clip. */ + cx = x; + cy = y; + cwidth = width; + cheight = height; + + if (cx < 0) { + cx = 0; + cwidth += x; + } + if (cy < 0) { + cy = 0; + cheight += y; + } + if (cx + cwidth > cols) + cwidth = cols - cx; + + if (cy + cheight > rows) + cheight = rows - cy; + + /* Draw. */ + for (row = cy; row < cy + cheight; ++row) { + unsigned int col; + for (col = cx; col < cx + cwidth; ++col) + drawPoint(drawProc, clientdata, + pixels, cols, rows, maxval, col, row); + } +} + + +/* Outline drawing stuff. */ + +static int linetype = PPMD_LINETYPE_NORMAL; + +int +ppmd_setlinetype(int const type) { + + int old; + + old = linetype; + linetype = type; + return old; +} + + + +static bool lineclip = TRUE; + + + +int +ppmd_setlineclip(int const newSetting) { + + bool previousSetting; + + previousSetting = lineclip; + + lineclip = newSetting; + + return previousSetting; +} + + + +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) { +/*---------------------------------------------------------------------------- + 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). + + Iff this is not possible because the entire line described is + outside the frame, return *nolineP == true. + + The frame is 'cols' columns starting at 0, by 'rows' rows starting + at 0. +-----------------------------------------------------------------------------*/ + + int cx0, cy0; + bool noLine; + + cx0 = x0; /* initial value */ + cy0 = y0; /* initial value */ + noLine = FALSE; /* initial value */ + + /* Clip End 0 of the line horizontally */ + if (cx0 < 0) { + if (x1 < 0) + noLine = TRUE; + else { + cy0 = cy0 + (y1 - cy0) * (-cx0) / (x1 - cx0); + cx0 = 0; + } + } else if (cx0 >= cols) { + if (x1 >= cols) + noLine = TRUE; + else { + cy0 = cy0 + (y1 - cy0) * (cols - 1 - cx0) / (x1 - cx0); + cx0 = cols - 1; + } + } + + /* Clip End 0 of the line vertically */ + if (cy0 < 0) { + if (y1 < 0) + noLine = TRUE; + else { + cx0 = cx0 + (x1 - cx0) * (-cy0) / (y1 - cy0); + cy0 = 0; + } + } else if (cy0 >= rows) { + if (y1 >= rows) + noLine = TRUE; + else { + cx0 = cx0 + (x1 - cx0) * (rows - 1 - cy0) / (y1 - cy0); + cy0 = rows - 1; + } + } + + *cx0P = cx0; + *cy0P = cy0; + *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) { +/*---------------------------------------------------------------------------- + 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). + + This is guaranteed to be possible, since we already know at least one + point (i.e. (x0, y0)) is in the frame. + + The frame is 'cols' columns starting at 0, by 'rows' rows starting + at 0. +-----------------------------------------------------------------------------*/ + int cx1, cy1; + + assert(x0 >= 0 && y0 < cols); + assert(y0 >= 0 && y0 < rows); + + /* Clip End 1 of the line horizontally */ + cx1 = x1; /* initial value */ + cy1 = y1; /* initial value */ + + if (cx1 < 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) { + /* 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; + } + + /* Clip End 1 of the line vertically */ + if (cy1 < 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) { + /* 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; + } + + *cx1P = cx1; + *cy1P = cy1; +} + + + +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) { +/*---------------------------------------------------------------------------- + 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' + + The clipped line goes from (*cx0P, *cy0P) to (*cx1P, *cy1P). + + 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; + /* 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); + + if (!noLine) { + /* (cx0, cy0) is in the frame: */ + assert(cx0 >= 0 && cy0 < cols); + assert(cy0 >= 0 && cy0 < rows); + + clipEnd1(cx0, cy0, x1, y1, cols, rows, &cx1, &cy1); + } + + *cx0P = cx0; + *cy0P = cy0; + *cx1P = cx1; + *cy1P = cy1; + *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) { +/*---------------------------------------------------------------------------- + Draw a line that is more horizontal than vertical. + + Don't clip. + + 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) + dx = 1; + else + dx = -1; + dy = (y1 - y0) * DDA_SCALE / abs(x1 - x0); + prevrow = row = y0; + srow = row * DDA_SCALE + DDA_SCALE / 2; + col = x0; + for ( ; ; ) { + if (linetype == PPMD_LINETYPE_NODIAGS && row != prevrow) { + drawPoint(drawProc, clientdata, + pixels, cols, rows, maxval, col, prevrow); + prevrow = row; + } + drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, col, row); + if (col == x1) + break; + srow += dy; + row = srow / DDA_SCALE; + col += dx; + } +} + + + +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) { +/*---------------------------------------------------------------------------- + Draw a line that is more vertical than horizontal. + + Don't clip. + + Assume the line has distinct start and end points (i.e. it's at least + two points). +-----------------------------------------------------------------------------*/ + /* Loop over Y domain. */ + + long dx, scol; + int dy, col, row, prevcol; + + if (y1 > y0) + dy = 1; + else + dy = -1; + dx = (x1 - x0) * DDA_SCALE / abs(y1 - y0); + row = y0; + prevcol = col = x0; + scol = col * DDA_SCALE + DDA_SCALE / 2; + for ( ; ; ) { + if (linetype == PPMD_LINETYPE_NODIAGS && col != prevcol) { + drawPoint(drawProc, clientdata, + pixels, cols, rows, maxval, prevcol, row); + prevcol = col; + } + drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, col, row); + if (row == y1) + break; + row += dy; + scol += dx; + col = scol / DDA_SCALE; + } +} + + + +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; + bool noLine; /* There's no line left after clipping */ + + if (lineclip) { + clipLine(x0, y0, x1, y1, cols, rows, &cx0, &cy0, &cx1, &cy1, &noLine); + } else { + cx0 = x0; + cy0 = y0; + cx1 = x1; + cy1 = y1; + noLine = FALSE; + } + + if (noLine) { + /* Nothing to draw */ + } else if (cx0 == cx1 && cy0 == cy1) { + /* 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); + } else { + /* Draw, using a simple DDA. */ + if (abs(cx1 - cx0) > abs(cy1 - cy0)) + drawShallowLine(drawProc, clientdata, pixels, cols, rows, maxval, + cx0, cy0, cx1, cy1); + else + drawSteepLine(drawProc, clientdata, pixels, cols, rows, maxval, + cx0, cy0, cx1, cy1); + } +} + + + +#define SPLINE_THRESH 3 +void +ppmd_spline3(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, + 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 ); +} + + + +void +ppmd_polyspline(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x0, + int const y0, + int const nc, + int * const xc, + int * const yc, + int const x1, + int const y1, + 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_spline3( + pixels, cols, rows, maxval, x, y, xc[nc - 1], yc[nc - 1], x1, y1, + drawProc, clientdata ); +} + + + +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) { + + pm_error("ppmd_spline4() has not been written yet!"); + +} + + + +void +ppmd_circle(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const cx, + 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; + + x0 = x = radius; + y0 = y = 0; + sx = x * DDA_SCALE + DDA_SCALE / 2; + sy = y * DDA_SCALE + DDA_SCALE / 2; + e = DDA_SCALE / radius; + 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; + drawPoint(drawProc, clientdata, + pixels, cols, rows, maxval, x + cx, y + cy); + } + } + while (nopointsyet || x != x0 || y != y0); +} + + + +/* Arbitrary fill stuff. */ + +typedef struct +{ + short x; + short y; + short edge; +} coord; + +typedef struct fillobj { + int n; + int size; + int curedge; + int segstart; + int ydir; + int startydir; + coord * coords; +} fillobj; + +#define SOME 1000 + +static int oldclip; + +struct fillobj * +ppmd_fill_create(void) { + + fillobj * fillObjP; + + MALLOCVAR(fillObjP); + if (fillObjP == NULL) + pm_error("out of memory allocating a fillhandle"); + fillObjP->n = 0; + fillObjP->size = SOME; + MALLOCARRAY(fillObjP->coords, fillObjP->size); + if (fillObjP->coords == NULL) + pm_error("out of memory allocating a fillhandle"); + fillObjP->curedge = 0; + + /* Turn off line clipping. */ + /* UGGH! We must eliminate this global variable */ + oldclip = ppmd_setlineclip(0); + + return fillObjP; +} + + + +void +ppmd_fill_destroy(struct fillobj * fillObjP) { + + free(fillObjP->coords); + free(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) { + + fillobj * fh; + coord * cp; + coord * ocp; + + fh = (fillobj*) clientdata; + + 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 ) + return; + } + + /* Ok, these are new; check if there's 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" ); + } + + /* Check for extremum and set the edge number. */ + if (fh->n == 0) { + /* Start first segment. */ + fh->segstart = fh->n; + fh->ydir = 0; + fh->startydir = 0; + } else { + int dx, dy; + + dx = x - ocp->x; + dy = y - ocp->y; + if (dx < -1 || dx > 1 || dy < -1 || dy > 1) { + /* Segment break. Close off old one. */ + if (fh->startydir != 0 && fh->ydir != 0) + if (fh->startydir == fh->ydir) { + /* Oops, first edge and last edge are the same. + Renumber the first edge in the old segment. + */ + coord * fcp; + int oldedge; + + fcp = &(fh->coords[fh->segstart]); + oldedge = fcp->edge; + for ( ; fcp->edge == oldedge; ++fcp ) + fcp->edge = ocp->edge; + } + /* And start new segment. */ + ++fh->curedge; + fh->segstart = fh->n; + fh->ydir = 0; + fh->startydir = 0; + } else { + /* Segment continues. */ + if (dy != 0) { + if (fh->ydir != 0 && fh->ydir != dy) { + /* Direction changed. Insert a fake coord, old + position but new edge number. + */ + ++fh->curedge; + cp = &fh->coords[fh->n]; + cp->x = ocp->x; + cp->y = ocp->y; + cp->edge = fh->curedge; + ++fh->n; + } + fh->ydir = dy; + if (fh->startydir == 0) + fh->startydir = dy; + } + } + } + + /* Save this coord. */ + cp = &fh->coords[fh->n]; + cp->x = x; + cp->y = y; + cp->edge = fh->curedge; + ++fh->n; +} + + + + +static qsort_compare yx_compare; + +static int +yx_compare(const void * const c1Arg, + const void * const c2Arg) { + + const coord * const c1P = c1Arg; + const coord * const c2P = c2Arg; + + int retval; + + if (c1P->y > c2P->y) + retval = 1; + else if (c1P->y < c2P->y) + retval = -1; + else if (c1P->x > c2P->x) + retval = 1; + else if (c1P->x < c2P->x) + retval = -1; + else + retval = 0; + + return retval; +} + + + +void +ppmd_fill(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + struct fillobj * const fh, + ppmd_drawproc drawProc, + const void * const clientdata) { + + int pedge; + int i, edge, lx, rx, py; + coord * cp; + bool eq; + bool leftside; + + /* Close off final segment. */ + if (fh->n > 0 && fh->startydir != 0 && fh->ydir != 0) { + if (fh->startydir == fh->ydir) { + /* Oops, first edge and last edge are the same. */ + coord * fcp; + int lastedge, oldedge; + + lastedge = fh->coords[fh->n - 1].edge; + fcp = &(fh->coords[fh->segstart]); + oldedge = fcp->edge; + for ( ; fcp->edge == oldedge; ++fcp ) + fcp->edge = lastedge; + } + } + /* Restore clipping now. */ + ppmd_setlineclip(oldclip); + + /* Sort the coords by Y, secondarily by X. */ + qsort((char*) fh->coords, fh->n, sizeof(coord), yx_compare); + + /* Find equal coords with different edge numbers, and swap if necessary. */ + edge = -1; + for (i = 0; i < fh->n; ++i) { + cp = &fh->coords[i]; + if (i > 1 && eq && cp->edge != edge && cp->edge == pedge) { + /* Swap .-1 and .-2. */ + coord t; + + t = fh->coords[i-1]; + fh->coords[i-1] = fh->coords[i-2]; + fh->coords[i-2] = t; + } + if (i > 0) { + if (cp->x == lx && cp->y == py) { + eq = TRUE; + if (cp->edge != edge && cp->edge == pedge) { + /* Swap . and .-1. */ + coord t; + + t = *cp; + *cp = fh->coords[i-1]; + fh->coords[i-1] = t; + } + } else + eq = FALSE; + } + lx = cp->x; + py = cp->y; + pedge = edge; + edge = cp->edge; + } + + /* Ok, now run through the coords filling spans. */ + for (i = 0; i < fh->n; ++i) { + cp = &fh->coords[i]; + if (i == 0) { + lx = rx = cp->x; + py = cp->y; + edge = cp->edge; + leftside = TRUE; + } else { + if (cp->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; + edge = cp->edge; + leftside = TRUE; + } else { + if (cp->edge == edge) { + /* Continuation of side. */ + rx = cp->x; + } else { + /* Edge changed. Is it a span? */ + if (leftside) { + rx = cp->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; + leftside = TRUE; + } + edge = cp->edge; + } + } + } + } +} + + + +/* Table used to look up sine of angles from 0 through 90 degrees. + The value returned is the sine * 65536. Symmetry is used to + obtain sine and cosine for arbitrary angles using this table. */ + +static long sintab[] = { + 0, 1143, 2287, 3429, 4571, 5711, 6850, 7986, 9120, 10252, 11380, + 12504, 13625, 14742, 15854, 16961, 18064, 19160, 20251, 21336, + 22414, 23486, 24550, 25606, 26655, 27696, 28729, 29752, 30767, + 31772, 32768, 33753, 34728, 35693, 36647, 37589, 38521, 39440, + 40347, 41243, 42125, 42995, 43852, 44695, 45525, 46340, 47142, + 47929, 48702, 49460, 50203, 50931, 51643, 52339, 53019, 53683, + 54331, 54963, 55577, 56175, 56755, 57319, 57864, 58393, 58903, + 59395, 59870, 60326, 60763, 61183, 61583, 61965, 62328, 62672, + 62997, 63302, 63589, 63856, 64103, 64331, 64540, 64729, 64898, + 65047, 65176, 65286, 65376, 65446, 65496, 65526, 65536 +}; + +static int extleft, exttop, extright, extbottom; /* To accumulate extents */ + +/* LINTLIBRARY */ + +/* 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. */ + + if (deg < 0) { + deg = (360 - ((- deg) % 360)) % 360; + } else if (deg >= 360) { + deg = deg % 360; + } + + /* Now look up from table according to quadrant. */ + + if (deg <= 90) { + return sintab[deg]; + } else if (deg <= 180) { + return sintab[180 - deg]; + } else if (deg <= 270) { + return -sintab[deg - 180]; + } + return -sintab[360 - 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))) + +#define Scalef 21 /* Font design size */ +#define Descend 9 /* Descender offset */ + + + +static void +drawGlyph(const struct ppmd_glyph * const glyphP, + int * const xP, + int const y, + pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + int const height, + int const xpos, + int const ypos, + long const rotcos, + long const rotsin, + ppmd_drawproc 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. +-----------------------------------------------------------------------------*/ + struct penpos penPos; + 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; + + for (commandNum = 0; + commandNum < glyphP->header.commandCount; + ++commandNum) { + + const struct ppmd_glyphCommand * const commandP = + &glyphP->commandList[commandNum]; + + switch (commandP->verb) { + case CMD_NOOP: + 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. + */ + + 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_line(pixels, cols, rows, maxval, tx1, ty1, tx2, ty2, + drawProc, clientdata); + + penPos.x = nx; + penPos.y = ny; + } + break; + case CMD_MOVEPEN: + penPos.x = x + SCHAR(commandP->x); + penPos.y = y + SCHAR(commandP->y); + break; + } + } + x += glyphP->header.skipAfter; + + *xP = x; +} + + +/* 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) { + + const struct ppmd_font * const fontP = ppmd_get_font(); + long rotsin, rotcos; + int x, y; + const char * s; + + x = y = 0; + rotsin = isin(-angle); + rotcos = icos(-angle); + + s = sArg; + while (*s) { + unsigned char const ch = *s++; + + if (ch >= fontP->header.firstCodePoint && + ch < fontP->header.firstCodePoint + fontP->header.characterCount) { + + const struct ppmd_glyph * const glyphP = + &fontP->glyphTable[ch - fontP->header.firstCodePoint]; + + drawGlyph(glyphP, &x, y, pixels, cols, rows, maxval, + height, xpos, ypos, rotcos, rotsin, + drawProc, clientdata); + } else if (ch == '\n') { + /* Move to the left edge of the next line down */ + y += Scalef + Descend; + x = 0; + } + } +} + +/* EXTENTS_DRAWPROC -- Drawproc which just accumulates the extents + rectangle bounding the text. */ + +static void +extents_drawproc (pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x, + int const y, + const void * const clientdata) +{ + extleft = MIN(extleft, x); + exttop = MIN(exttop, y); + extright = MAX(extright, x); + extbottom = MAX(extbottom, y); +} + + +/* PPMD_TEXT_BOX -- Calculate extents rectangle for a given piece of + text. For most applications where extents are + needed, angle should be zero to obtain the + unrotated extents. If you need the extents box + for post-rotation text, however, you can set angle + nonzero and it will be calculated correctly. +*/ + +void +ppmd_text_box(int const height, + int const angle, + const char * const s, + int * const left, + int * const top, + int * const right, + int * const bottom) +{ + extleft = 32767; + exttop = 32767; + extright = -32767; + extbottom = -32767; + ppmd_text(NULL, 32767, 32767, 255, 1000, 1000, height, angle, s, + extents_drawproc, NULL); + *left = extleft - 1000; + *top = exttop - 1000; + *right = extright - 1000; + *bottom = extbottom - 1000; +} diff --git a/lib/libppmfloyd.c b/lib/libppmfloyd.c new file mode 100644 index 00000000..071c3c36 --- /dev/null +++ b/lib/libppmfloyd.c @@ -0,0 +1,270 @@ +/* +These functions were taken from Ingo Wilken's ilbm package by Bryan +Henderson on 01.03.10. Because ppmtoilbm and ilbmtoppm are the only +programs that will use these in the foreseeable future, they remain +lightly documented and tested. + +But they look like they would be useful in other Netpbm programs that +do Floyd-Steinberg. +*/ + + + +/* libfloyd.c - generic Floyd-Steinberg error distribution routines for PBMPlus +** +** Copyright (C) 1994 Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" +#include "ppmfloyd.h" +#include "mallocvar.h" + + + +static void +fs_adjust(ppm_fs_info * const fi, + int const col) { + + int const errcol = col+1; + pixel * const pP = &(fi->pixrow[col]); + pixval const maxval = fi->maxval; + + long r, g, b; + + /* Use Floyd-Steinberg errors to adjust actual color. */ + r = fi->thisrederr [errcol]; if( r < 0 ) r -= 8; else r += 8; r /= 16; + g = fi->thisgreenerr[errcol]; if( g < 0 ) g -= 8; else g += 8; g /= 16; + b = fi->thisblueerr [errcol]; if( b < 0 ) b -= 8; else b += 8; b /= 16; + + r += PPM_GETR(*pP); if ( r < 0 ) r = 0; else if ( r > maxval ) r = maxval; + g += PPM_GETG(*pP); if ( g < 0 ) g = 0; else if ( g > maxval ) g = maxval; + b += PPM_GETB(*pP); if ( b < 0 ) b = 0; else if ( b > maxval ) b = maxval; + + PPM_ASSIGN(*pP, r, g, b); + fi->red = r; fi->green = g; fi->blue = b; +} + + + +static ppm_fs_info * +allocateFi(int const cols) { + + ppm_fs_info * fi; + + MALLOCVAR(fi); + + if (fi != NULL) { + MALLOCARRAY(fi->thisrederr , cols + 2); + MALLOCARRAY(fi->thisgreenerr, cols + 2); + MALLOCARRAY(fi->thisblueerr , cols + 2); + MALLOCARRAY(fi->nextrederr , cols + 2); + MALLOCARRAY(fi->nextgreenerr, cols + 2); + MALLOCARRAY(fi->nextblueerr , cols + 2); + + if (fi->thisrederr == NULL || + fi->thisgreenerr == NULL || + fi->thisblueerr == NULL || + fi->nextrederr == NULL || + fi->nextgreenerr == NULL || + fi->nextblueerr == NULL) + pm_error("out of memory allocating " + "Floyd-Steinberg control structure"); + } else + pm_error("out of memory allocating Floyd-Steinberg control structure"); + + return(fi); +} + + + +ppm_fs_info * +ppm_fs_init(int cols, pixval maxval, int flags) { + + ppm_fs_info *fi; + + fi = allocateFi(cols); + + fi->lefttoright = 1; + fi->cols = cols; + fi->maxval = maxval; + fi->flags = flags; + + if( flags & FS_RANDOMINIT ) { + unsigned int i; + srand((int)(time(0) ^ getpid())); + 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; + } + } + else { + unsigned int i; + + for( i = 0; i < cols + 2; i++ ) + fi->thisrederr[i] = fi->thisgreenerr[i] = + fi->thisblueerr[i] = 0; + } + return fi; +} + + + +void +ppm_fs_free(fi) + ppm_fs_info *fi; +{ + if( fi ) { + free(fi->thisrederr); free(fi->thisgreenerr); free(fi->thisblueerr); + free(fi->nextrederr); free(fi->nextgreenerr); free(fi->nextblueerr); + free(fi); + } +} + + +int +ppm_fs_startrow(fi, pixrow) + ppm_fs_info *fi; + pixel *pixrow; +{ + register int col; + + if( !fi ) + return 0; + + fi->pixrow = pixrow; + + for( col = 0; col < fi->cols + 2; col++ ) + fi->nextrederr[col] = fi->nextgreenerr[col] = fi->nextblueerr[col] = 0; + + if( fi->lefttoright ) { + fi->col_end = fi->cols; + col = 0; + } + else { + fi->col_end = -1; + col = fi->cols - 1; + } + fs_adjust(fi, col); + return col; +} + + +int +ppm_fs_next(fi, col) + ppm_fs_info *fi; + int col; +{ + if( !fi ) + ++col; + else { + if( fi->lefttoright ) + ++col; + else + --col; + if( col == fi->col_end ) + col = fi->cols; + else + fs_adjust(fi, col); + } + return col; +} + + +void +ppm_fs_endrow(fi) + ppm_fs_info *fi; +{ + long *tmp; + + if( fi ) { + tmp = fi->thisrederr; fi->thisrederr = fi->nextrederr; fi->nextrederr = tmp; + tmp = fi->thisgreenerr; fi->thisgreenerr = fi->nextgreenerr; fi->nextgreenerr = tmp; + tmp = fi->thisblueerr; fi->thisblueerr = fi->nextblueerr; fi->nextblueerr = tmp; + if( fi->flags & FS_ALTERNATE ) + fi->lefttoright = !(fi->lefttoright); + } +} + + +void +ppm_fs_update(fi, col, pP) + ppm_fs_info *fi; + int col; + pixel *pP; +{ + if( fi ) + ppm_fs_update3(fi, col, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP)); +} + + +void +ppm_fs_update3(ppm_fs_info * const fi, + int const col, + pixval const r, + pixval const g, + pixval const b) { + + int const errcol = col + 1; + long err; + + if (fi) { + long const rerr = (long)(fi->red) - (long)r; + long const gerr = (long)(fi->green) - (long)g; + long const berr = (long)(fi->blue) - (long)b; + + if ( fi->lefttoright ) { + long two_err; + + two_err = 2*rerr; + err = rerr; fi->nextrederr[errcol+1] += err; /* 1/16 */ + err += two_err; fi->nextrederr[errcol-1] += err; /* 3/16 */ + err += two_err; fi->nextrederr[errcol ] += err; /* 5/16 */ + err += two_err; fi->thisrederr[errcol+1] += err; /* 7/16 */ + + two_err = 2*gerr; + err = gerr; fi->nextgreenerr[errcol+1] += err; /* 1/16 */ + err += two_err; fi->nextgreenerr[errcol-1] += err; /* 3/16 */ + err += two_err; fi->nextgreenerr[errcol ] += err; /* 5/16 */ + err += two_err; fi->thisgreenerr[errcol+1] += err; /* 7/16 */ + + two_err = 2*berr; + err = berr; fi->nextblueerr[errcol+1] += err; /* 1/16 */ + err += two_err; fi->nextblueerr[errcol-1] += err; /* 3/16 */ + err += two_err; fi->nextblueerr[errcol ] += err; /* 5/16 */ + err += two_err; fi->thisblueerr[errcol+1] += err; /* 7/16 */ + } + else { + long two_err; + + two_err = 2*rerr; + err = rerr; fi->nextrederr[errcol-1] += err; /* 1/16 */ + err += two_err; fi->nextrederr[errcol+1] += err; /* 3/16 */ + err += two_err; fi->nextrederr[errcol ] += err; /* 5/16 */ + err += two_err; fi->thisrederr[errcol-1] += err; /* 7/16 */ + + two_err = 2*gerr; + err = gerr; fi->nextgreenerr[errcol-1] += err; /* 1/16 */ + err += two_err; fi->nextgreenerr[errcol+1] += err; /* 3/16 */ + err += two_err; fi->nextgreenerr[errcol ] += err; /* 5/16 */ + err += two_err; fi->thisgreenerr[errcol-1] += err; /* 7/16 */ + + two_err = 2*berr; + err = berr; fi->nextblueerr[errcol-1] += err; /* 1/16 */ + err += two_err; fi->nextblueerr[errcol+1] += err; /* 3/16 */ + err += two_err; fi->nextblueerr[errcol ] += err; /* 5/16 */ + err += two_err; fi->thisblueerr[errcol-1] += err; /* 7/16 */ + } + } +} + + + diff --git a/lib/libppmfuzzy.c b/lib/libppmfuzzy.c new file mode 100644 index 00000000..6127d5d5 --- /dev/null +++ b/lib/libppmfuzzy.c @@ -0,0 +1,434 @@ +/*============================================================================= + This file contains fuzzy color matching code. + + It is all based on algorithms and methods by Kenan Kalajdzic + in 2006. +=============================================================================*/ + +#include "pm_c_util.h" +#include "nstring.h" +#include "ppm.h" + +typedef double fzLog; + /* fuzzy logic truth value */ + +/*---------------------------------------------------------------------------- + Member functions +-----------------------------------------------------------------------------*/ + +static fzLog +memberS(fzLog const x1, + fzLog const x2, + fzLog const x) { + + fzLog retval; + + if (x <= x1) + retval = 0; + else if (x > x1 && x <= x2) + retval = (x - x1) / (x2 - x1); + else + retval = 1; + + return retval; +} + + + +static fzLog +memberZ(fzLog const x1, + fzLog const x2, + fzLog const x) { + + fzLog retval; + + if (x <= x1) + retval = 1; + else if (x > x1 && x <= x2) + retval = (x2 - x) / (x2 - x1); + else + retval = 0; + + return retval; +} + + + +static fzLog +memberTrapez(fzLog const x1, + fzLog const x2, + fzLog const x3, + fzLog const x4, + fzLog const x) { + + fzLog retval; + + if (x <= x1 || x > x4) + retval = 0; + else if (x > x1 && x <= x2) + retval = (x - x1) / (x2 - x1); + else if (x > x2 && x <= x3) + retval = 1; + else + retval = (x4 - x) / (x4 - x3); + + return retval; +} + + + +/*---------------------------------------------------------------------------- + Hue membership functions +-----------------------------------------------------------------------------*/ + +static fzLog +hueIsAround000(double const hue) { + + return memberZ(10, 30, hue); +} + + + +static fzLog +hueIsAround015(double const hue) { + + return memberZ(30, 40, hue); +} + + + +static fzLog +hueIsAround030(double const hue) { + + return memberTrapez(10, 30, 40, 60, hue); +} + + + +static fzLog +hueIsAround060(double const hue) { + + return memberTrapez(40, 60, 60, 80, hue); +} + + + +static fzLog +hueIsAround120(double const hue) { + + return memberTrapez(60, 80, 150, 180, hue); +} + + + +static fzLog +hueIsAround180(double const hue) { + + return memberTrapez(150, 180, 240, 260, hue); +} + + + +static fzLog +hueIsAround270(double const hue) { + + return memberTrapez(240, 260, 290, 310, hue); +} + + + +static fzLog +hueIsAround320(double const hue) { + + return memberTrapez(290, 310, 320, 350, hue); +} + + + +static fzLog +hueIsAround360(double const hue) { + + return memberS(320, 350, hue); +} + + + +/*---------------------------------------------------------------------------- + Saturation membership functions +-----------------------------------------------------------------------------*/ + +static fzLog +satIsVeryLow(double const sat) { + + return memberZ(0.02, 0.1, sat); +} + + + +static fzLog +satIsLow(double const sat) { + return memberTrapez(0.02, 0.1, 0.2, 0.3, sat); +} + + + +static fzLog +satIsMedium(double const sat) { + + return memberTrapez(0.2, 0.3, 0.6, 0.7, sat); +} + + + +static fzLog +satIsHigh(double const sat) { + + return memberS(0.6, 0.7, sat); +} + + + +/*---------------------------------------------------------------------------- + Value membership functions +-----------------------------------------------------------------------------*/ + +static fzLog +valIsVeryLow(double const val) { + + return memberZ(0.1, 0.2, val); +} + + + +static fzLog +valIsLow(double const val) { + + return memberTrapez(0.1, 0.2, 0.3, 0.6, val); +} + + + +static fzLog +valIsMedium(double const val) { + + return memberTrapez(0.3, 0.6, 0.7, 0.8, val); +} + + + +static fzLog +valIsHigh(double const val) { + + return memberS(0.7, 0.8, val); +} + + + +/*---------------------------------------------------------------------------- + Fuzzy logic functions +-----------------------------------------------------------------------------*/ + +static fzLog +fzAnd(fzLog const opLeft, + fzLog const opRight) { + + return MIN(opLeft, opRight); +} + + + +static fzLog +fzOr(fzLog const opLeft, + fzLog const opRight) { + + return MAX(opLeft, opRight); +} + + + +static fzLog +fzNot(fzLog const op) { + return 1.0 - op; +} + + + +/*---------------------------------------------------------------------------- + Fuzzy color matching +-----------------------------------------------------------------------------*/ + +static void +matchBk(pixel const color, + pixval const maxval, + fzLog (* const bkMatchP)[BKCOLOR_COUNT]) { + + struct hsv const hsv = ppm_hsv_from_color(color, maxval); + + fzLog const satVeryLow = satIsVeryLow(hsv.s); + fzLog const satLow = satIsLow(hsv.s); + 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 hueAround000 = hueIsAround000(hsv.h); + fzLog const hueAround015 = hueIsAround015(hsv.h); + fzLog const hueAround030 = hueIsAround030(hsv.h); + fzLog const hueAround060 = hueIsAround060(hsv.h); + fzLog const hueAround120 = hueIsAround120(hsv.h); + fzLog const hueAround180 = hueIsAround180(hsv.h); + fzLog const hueAround270 = hueIsAround270(hsv.h); + fzLog const hueAround320 = hueIsAround320(hsv.h); + fzLog const hueAround360 = hueIsAround360(hsv.h); + + (*bkMatchP)[BKCOLOR_BLACK] = + fzAnd(fzOr(satVeryLow, satLow), valVeryLow); + + (*bkMatchP)[BKCOLOR_GRAY] = + fzAnd(fzOr(satVeryLow, satLow), fzOr(valLow, valMedium)); + + (*bkMatchP)[BKCOLOR_WHITE] = + fzAnd(fzOr(satVeryLow, satLow), valHigh); + + (*bkMatchP)[BKCOLOR_RED] = + fzAnd(fzAnd(fzOr(hueAround000, hueAround360), fzNot(satVeryLow)), + fzOr(valMedium, valHigh) + ); + + (*bkMatchP)[BKCOLOR_ORANGE] = + fzAnd(fzAnd(hueAround030, fzOr(satMedium, satHigh)), + fzOr(valMedium, valHigh) + ); + + (*bkMatchP)[BKCOLOR_YELLOW] = + fzAnd(fzAnd(hueAround060, fzOr(satMedium, satHigh)), + fzOr(valMedium, valHigh) + ); + + (*bkMatchP)[BKCOLOR_GREEN] = + fzAnd(fzAnd(hueAround120, fzNot(satVeryLow)), + fzOr(valMedium, valHigh) + ); + + (*bkMatchP)[BKCOLOR_BLUE] = + fzAnd(fzAnd(hueAround180, fzAnd(fzNot(satVeryLow), fzNot(satLow))), + fzOr(valMedium, valHigh) + ); + + (*bkMatchP)[BKCOLOR_VIOLET] = + fzAnd(fzAnd(hueAround270, fzNot(satVeryLow)), + fzOr(valMedium, valHigh) + ); + + (*bkMatchP)[BKCOLOR_PURPLE] = + fzAnd(fzAnd(hueAround320, fzNot(satVeryLow)), + fzOr(valMedium, valHigh) + ); + + (*bkMatchP)[BKCOLOR_BROWN] = + fzAnd(fzOr(hueAround015, hueAround360), + fzAnd(fzNot(satVeryLow), fzNot(valHigh)) + ); +} + + + +bk_color +ppm_bk_color_from_color(pixel const color, + pixval const maxval) { + + fzLog bkmatch[BKCOLOR_COUNT]; + bk_color i; + bk_color bestSoFar; + fzLog bestMatch; + + matchBk(color, maxval, &bkmatch); + + for (i = 0, bestSoFar = 0, bestMatch = 0.0; i < BKCOLOR_COUNT; ++i) { + if (bkmatch[i] > bestMatch) { + bestSoFar = i; + bestMatch = bkmatch[i]; + } + } + return bestSoFar; +} + + + +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 */ + {255, 128, 0}, /* BKCOLOR_ORANGE */ + {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 */ +}; + + + +pixel +ppm_color_from_bk_color(bk_color const bkColor, + pixval const maxval) { + + pixel const color255 = bkColorMap[bkColor]; + + pixel retval; + + if (maxval != 255) { + PPM_DEPTH(retval, color255, 255, maxval); + } else + retval = color255; + + return retval; +} + + + +static const char * const bkColorNameMap[BKCOLOR_COUNT] = { + "black", + "gray", + "white", + "red", + "orange", + "yellow", + "green", + "blue", + "violet", + "purple", + "brown" +}; + + + +bk_color +ppm_bk_color_from_name(const char * const name) { + + bk_color i; + + for (i = 0; i < BKCOLOR_COUNT; ++i) { + if (STREQ(name, bkColorNameMap[i])) + return i; + } + pm_error("Invalid Berlin-Kay color name: '%s'", name); + return 0; /* quiet compiler warning */ +} + + + +const char * +ppm_name_from_bk_color(bk_color const bkColor) { + + if (bkColor >= BKCOLOR_COUNT) + pm_error("Invalid color passed to name_from_bk_color(): %u", + bkColor); + + return bkColorNameMap[bkColor]; +} diff --git a/lib/libsystem.c b/lib/libsystem.c new file mode 100644 index 00000000..e0d62178 --- /dev/null +++ b/lib/libsystem.c @@ -0,0 +1,319 @@ +/*============================================================================= + pm_system +=============================================================================== + This is the library subroutine pm_system(). It is just like Standard C + Library system(), except that you can supply routines for it to run to + generate the Standard Input for the executed shell command and to accept + the Standard Output from it. system(), by contrast, always sets up the + current Standard Input and Standard Output as the Standard Input and + Standard Output of the shell command. + + By Bryan Henderson, San Jose CA 2002.12.14. + + Contributed to the public domain. +=============================================================================*/ +#define _XOPEN_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "pm.h" +#include "pm_system.h" + +#define STDIN 0 +#define STDOUT 1 + + +static void +execProgram(const char * const shellCommand, + int const inputPipeFd, + int const outputPipeFd) { +/*---------------------------------------------------------------------------- + Run the shell command 'shellCommand', supplying to the shell + 'inputPipeFd' as its Standard Input and 'outputPipeFd' as its + Standard Output. + + But leave Standard Input and Standard Output as we found them. +-----------------------------------------------------------------------------*/ + int stdinSaveFd, stdoutSaveFd; + int rc; + + /* Make inputPipeFd Standard Input. + Make outputPipeFd Standard Output. + */ + stdinSaveFd = dup(STDIN); + stdoutSaveFd = dup(STDOUT); + + close(STDIN); + close(STDOUT); + + dup2(inputPipeFd, STDIN); + dup2(outputPipeFd, STDOUT); + + rc = execl("/bin/sh", "sh", "-c", shellCommand, NULL); + + close(STDIN); + close(STDOUT); + dup2(stdinSaveFd, STDIN); + dup2(stdoutSaveFd, STDOUT); + close(stdinSaveFd); + close(stdoutSaveFd); + + if (rc < 0) + pm_error("Unable to exec the shell. Errno=%d (%s)", + errno, strerror(errno)); + else + pm_error("INTERNAL ERROR. execl() returns, but does not fail."); +} + + + +static void +createPipeFeeder(void pipeFeederRtn(int, void *), + void * const feederParm, + int * const fdP, + pid_t * const pidP) { +/*---------------------------------------------------------------------------- + Create a process and a pipe. Have the process run program + 'pipeFeederRtn' to fill the pipe and return the file descriptor of the + other end of the pipe as *fdP. +-----------------------------------------------------------------------------*/ + int pipeToFeed[2]; + pid_t feederPid; + + pipe(pipeToFeed); + feederPid = fork(); + if (feederPid < 0) { + pm_error("fork() of stdin feeder failed. errno=%d (%s)", + errno, strerror(errno)); + } else if (feederPid == 0) { + /* This is the child -- the stdin feeder process */ + close(pipeToFeed[0]); + (*pipeFeederRtn)(pipeToFeed[1], feederParm); + exit(0); + } + else { + /* This is the parent */ + close(pipeToFeed[1]); + *fdP = pipeToFeed[0]; + *pidP = feederPid; + } +} + + + +static void +spawnProcessor(const char * const shellCommand, + 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. +-----------------------------------------------------------------------------*/ + int stdoutpipe[2]; + pid_t processorpid; + + pipe(stdoutpipe); + + processorpid = fork(); + if (processorpid < 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]); + + execProgram(shellCommand, stdinFd, stdoutpipe[1]); + + close(stdinFd); + close(stdoutpipe[1]); + pm_error("INTERNAL ERROR: execProgram() returns."); + } else { + /* The parent */ + close(stdoutpipe[1]); + *stdoutFdP = stdoutpipe[0]; + *pidP = processorpid; + } +} + + +static void +cleanupProcessorProcess(pid_t const processorPid) { + + int status; + waitpid(processorPid, &status, 0); + if (status != 0) + pm_message("Shell process ended abnormally. " + "completion code = %d", status); +} + + + +static void +cleanupFeederProcess(pid_t const feederPid) { + int status; + + waitpid(feederPid, &status, 0); + + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) == SIGPIPE) + pm_message("WARNING: " + "Standard Input feeder process was terminated by a " + "SIGPIPE signal because the shell command closed its " + "Standard Input before the Standard Input feeder was " + "through feeding it."); + else + pm_message("WARNING: " + "Standard Input feeder was terminated by a Signal %d.", + WTERMSIG(status)); + } + else if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + pm_message("WARNING: " + "Standard Input feeder process ended abnormally. " + "exit status = %d", WEXITSTATUS(status)); + } else + pm_message("WARNING: " + "Unrecognized process completion status from " + "Standard Input feeder: %d", status); +} + + + +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. +-----------------------------------------------------------------------------*/ + + /* 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. + + 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. + + But if 'stdoutFeeder' is NULL, we just run the shell in this + process. + + So there can be 1, 2, or 3 processes involved depending on + parameters. + */ + + int shellStdinFd; + pid_t feederPid; + + if (stdinFeeder) + createPipeFeeder(stdinFeeder, feederParm, &shellStdinFd, &feederPid); + else { + shellStdinFd = STDIN; + feederPid = 0; + } + + if (stdoutAccepter) { + int shellStdoutFd; + pid_t processorPid; + + /* Make a child process to run the shell and pipe back to us its + Standard Output + */ + spawnProcessor(shellCommand, shellStdinFd, + &shellStdoutFd, &processorPid); + + /* Dispose of the stdout from that shell */ + (*stdoutAccepter)(shellStdoutFd, accepterParm); + close(shellStdoutFd); + + cleanupProcessorProcess(processorPid); + } else { + /* Run a child process for the shell that sends its Standard Output + to our Standard Output + */ + int const stdinSaveFd = dup(STDIN); + int rc; + + dup2(shellStdinFd, STDIN); + + rc = system(shellCommand); + + close(STDIN); + dup2(stdinSaveFd, STDIN); + + 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); + } + + if (feederPid) + cleanupFeederProcess(feederPid); +} + + + + +void +pm_feed_from_memory(int const pipeToFeedFd, + void * const feederParm) { + + struct bufferDesc * const inputBufferP = feederParm; + + FILE * const outfile = fdopen(pipeToFeedFd, "w"); + + int bytesTransferred; + + /* The following signals (and normally kills) the process with + SIGPIPE if the pipe does not take all 'size' bytes. + */ + bytesTransferred = + fwrite(inputBufferP->buffer, 1, inputBufferP->size, outfile); + + if (inputBufferP->bytesTransferredP) + *(inputBufferP->bytesTransferredP) = bytesTransferred; + + fclose(outfile); +} + + + +void +pm_accept_to_memory(int const pipetosuckFd, + void * const accepterParm ) { + + struct bufferDesc * const outputBufferP = accepterParm; + + FILE * const infile = fdopen(pipetosuckFd, "r"); + + int bytesTransferred; + + bytesTransferred = + fread(outputBufferP->buffer, 1, outputBufferP->size, infile); + + fclose(infile); + + if (outputBufferP->bytesTransferredP) + *(outputBufferP->bytesTransferredP) = bytesTransferred; +} diff --git a/lib/libsystem_dummy.c b/lib/libsystem_dummy.c new file mode 100644 index 00000000..afe20dde --- /dev/null +++ b/lib/libsystem_dummy.c @@ -0,0 +1,47 @@ +/*============================================================================= + libsystem_dummy.c +=============================================================================== + This is a dummy version of libsystem.c, for use on systems that don't + have the kind of process control that libsystem.c needs. + + With this module, program will build cleanly, but if a program actually + calls pm_system(), it will die with an error message saying that + the facility is not available. +=============================================================================*/ + +#include + +#include "pm.h" +#include "pm_system.h" + +void +pm_system(void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm, + const char * const shellCommand) { + + pm_error("This program wants to run another program using pm_system() in " + "the libnetpbm library, but libnetpbm was built without " + "the pm_system() facility -- probably because this system " + "doesn't have the process management facilities pm_system() " + "requires."); +} + + +void +pm_feed_from_memory(int const pipeToFeedFd, + void * const feederParm) { + + assert(FALSE); /* Can't ever run, since pm_system() is a dummy */ +} + + + +void +pm_accept_to_memory(int const pipetosuckFd, + void * const accepterParm) { + + assert(FALSE); /* Can't ever run, since pm_system() is a dummy */ +} + diff --git a/lib/lum.h b/lib/lum.h new file mode 100644 index 00000000..34d3866e --- /dev/null +++ b/lib/lum.h @@ -0,0 +1,123 @@ + /* Lookup tables for fast RGB -> luminance calculation. */ + static int times77[256] = { + 0, 77, 154, 231, 308, 385, 462, 539, + 616, 693, 770, 847, 924, 1001, 1078, 1155, + 1232, 1309, 1386, 1463, 1540, 1617, 1694, 1771, + 1848, 1925, 2002, 2079, 2156, 2233, 2310, 2387, + 2464, 2541, 2618, 2695, 2772, 2849, 2926, 3003, + 3080, 3157, 3234, 3311, 3388, 3465, 3542, 3619, + 3696, 3773, 3850, 3927, 4004, 4081, 4158, 4235, + 4312, 4389, 4466, 4543, 4620, 4697, 4774, 4851, + 4928, 5005, 5082, 5159, 5236, 5313, 5390, 5467, + 5544, 5621, 5698, 5775, 5852, 5929, 6006, 6083, + 6160, 6237, 6314, 6391, 6468, 6545, 6622, 6699, + 6776, 6853, 6930, 7007, 7084, 7161, 7238, 7315, + 7392, 7469, 7546, 7623, 7700, 7777, 7854, 7931, + 8008, 8085, 8162, 8239, 8316, 8393, 8470, 8547, + 8624, 8701, 8778, 8855, 8932, 9009, 9086, 9163, + 9240, 9317, 9394, 9471, 9548, 9625, 9702, 9779, + 9856, 9933, 10010, 10087, 10164, 10241, 10318, 10395, + 10472, 10549, 10626, 10703, 10780, 10857, 10934, 11011, + 11088, 11165, 11242, 11319, 11396, 11473, 11550, 11627, + 11704, 11781, 11858, 11935, 12012, 12089, 12166, 12243, + 12320, 12397, 12474, 12551, 12628, 12705, 12782, 12859, + 12936, 13013, 13090, 13167, 13244, 13321, 13398, 13475, + 13552, 13629, 13706, 13783, 13860, 13937, 14014, 14091, + 14168, 14245, 14322, 14399, 14476, 14553, 14630, 14707, + 14784, 14861, 14938, 15015, 15092, 15169, 15246, 15323, + 15400, 15477, 15554, 15631, 15708, 15785, 15862, 15939, + 16016, 16093, 16170, 16247, 16324, 16401, 16478, 16555, + 16632, 16709, 16786, 16863, 16940, 17017, 17094, 17171, + 17248, 17325, 17402, 17479, 17556, 17633, 17710, 17787, + 17864, 17941, 18018, 18095, 18172, 18249, 18326, 18403, + 18480, 18557, 18634, 18711, 18788, 18865, 18942, 19019, + 19096, 19173, 19250, 19327, 19404, 19481, 19558, 19635 }; + static int times150[256] = { + 0, 150, 300, 450, 600, 750, 900, 1050, + 1200, 1350, 1500, 1650, 1800, 1950, 2100, 2250, + 2400, 2550, 2700, 2850, 3000, 3150, 3300, 3450, + 3600, 3750, 3900, 4050, 4200, 4350, 4500, 4650, + 4800, 4950, 5100, 5250, 5400, 5550, 5700, 5850, + 6000, 6150, 6300, 6450, 6600, 6750, 6900, 7050, + 7200, 7350, 7500, 7650, 7800, 7950, 8100, 8250, + 8400, 8550, 8700, 8850, 9000, 9150, 9300, 9450, + 9600, 9750, 9900, 10050, 10200, 10350, 10500, 10650, + 10800, 10950, 11100, 11250, 11400, 11550, 11700, 11850, + 12000, 12150, 12300, 12450, 12600, 12750, 12900, 13050, + 13200, 13350, 13500, 13650, 13800, 13950, 14100, 14250, + 14400, 14550, 14700, 14850, 15000, 15150, 15300, 15450, + 15600, 15750, 15900, 16050, 16200, 16350, 16500, 16650, + 16800, 16950, 17100, 17250, 17400, 17550, 17700, 17850, + 18000, 18150, 18300, 18450, 18600, 18750, 18900, 19050, + 19200, 19350, 19500, 19650, 19800, 19950, 20100, 20250, + 20400, 20550, 20700, 20850, 21000, 21150, 21300, 21450, + 21600, 21750, 21900, 22050, 22200, 22350, 22500, 22650, + 22800, 22950, 23100, 23250, 23400, 23550, 23700, 23850, + 24000, 24150, 24300, 24450, 24600, 24750, 24900, 25050, + 25200, 25350, 25500, 25650, 25800, 25950, 26100, 26250, + 26400, 26550, 26700, 26850, 27000, 27150, 27300, 27450, + 27600, 27750, 27900, 28050, 28200, 28350, 28500, 28650, + 28800, 28950, 29100, 29250, 29400, 29550, 29700, 29850, + 30000, 30150, 30300, 30450, 30600, 30750, 30900, 31050, + 31200, 31350, 31500, 31650, 31800, 31950, 32100, 32250, + 32400, 32550, 32700, 32850, 33000, 33150, 33300, 33450, + 33600, 33750, 33900, 34050, 34200, 34350, 34500, 34650, + 34800, 34950, 35100, 35250, 35400, 35550, 35700, 35850, + 36000, 36150, 36300, 36450, 36600, 36750, 36900, 37050, + 37200, 37350, 37500, 37650, 37800, 37950, 38100, 38250 }; + static int times29[256] = { + 0, 29, 58, 87, 116, 145, 174, 203, + 232, 261, 290, 319, 348, 377, 406, 435, + 464, 493, 522, 551, 580, 609, 638, 667, + 696, 725, 754, 783, 812, 841, 870, 899, + 928, 957, 986, 1015, 1044, 1073, 1102, 1131, + 1160, 1189, 1218, 1247, 1276, 1305, 1334, 1363, + 1392, 1421, 1450, 1479, 1508, 1537, 1566, 1595, + 1624, 1653, 1682, 1711, 1740, 1769, 1798, 1827, + 1856, 1885, 1914, 1943, 1972, 2001, 2030, 2059, + 2088, 2117, 2146, 2175, 2204, 2233, 2262, 2291, + 2320, 2349, 2378, 2407, 2436, 2465, 2494, 2523, + 2552, 2581, 2610, 2639, 2668, 2697, 2726, 2755, + 2784, 2813, 2842, 2871, 2900, 2929, 2958, 2987, + 3016, 3045, 3074, 3103, 3132, 3161, 3190, 3219, + 3248, 3277, 3306, 3335, 3364, 3393, 3422, 3451, + 3480, 3509, 3538, 3567, 3596, 3625, 3654, 3683, + 3712, 3741, 3770, 3799, 3828, 3857, 3886, 3915, + 3944, 3973, 4002, 4031, 4060, 4089, 4118, 4147, + 4176, 4205, 4234, 4263, 4292, 4321, 4350, 4379, + 4408, 4437, 4466, 4495, 4524, 4553, 4582, 4611, + 4640, 4669, 4698, 4727, 4756, 4785, 4814, 4843, + 4872, 4901, 4930, 4959, 4988, 5017, 5046, 5075, + 5104, 5133, 5162, 5191, 5220, 5249, 5278, 5307, + 5336, 5365, 5394, 5423, 5452, 5481, 5510, 5539, + 5568, 5597, 5626, 5655, 5684, 5713, 5742, 5771, + 5800, 5829, 5858, 5887, 5916, 5945, 5974, 6003, + 6032, 6061, 6090, 6119, 6148, 6177, 6206, 6235, + 6264, 6293, 6322, 6351, 6380, 6409, 6438, 6467, + 6496, 6525, 6554, 6583, 6612, 6641, 6670, 6699, + 6728, 6757, 6786, 6815, 6844, 6873, 6902, 6931, + 6960, 6989, 7018, 7047, 7076, 7105, 7134, 7163, + 7192, 7221, 7250, 7279, 7308, 7337, 7366, 7395 }; + +/* The ppm_fastlumin() macro is a way to compute luminosity without + floating point arithmetic. On modern computers, floating point often isn't + any slower, so you may prefer ppm_lumin() in ppm.h. + + ppm_fastlumin() works only with maxval <= 255, because the multiplication + tables go up only that high. + + In the following arithmetic, note that shifting right 8 bits is the same + as dividing by (77+150+29), but some compilers may generate faster code + for >>8 than for /(77+150+29). +*/ +static __inline__ pixval +ppm_fastlumin(pixel const p) { + + return + (times77[PPM_GETR(p)] + + times150[PPM_GETG(p)] + + times29[PPM_GETB(p)] + + 128) /* round off */ + >> 8; +} + diff --git a/lib/mkstemp.c b/lib/mkstemp.c new file mode 100644 index 00000000..cd9140c7 --- /dev/null +++ b/lib/mkstemp.c @@ -0,0 +1,8 @@ +#include +#include +#include + +#include "pm_c_util.h" + + + diff --git a/lib/pam.h b/lib/pam.h new file mode 100644 index 00000000..97d5b3cb --- /dev/null +++ b/lib/pam.h @@ -0,0 +1,490 @@ +/*---------------------------------------------------------------------------- + These are declarations for use with the Portable Arbitrary Map (PAM) + format and the Netpbm library functions specific to them. +-----------------------------------------------------------------------------*/ + +#ifndef PAM_H +#define PAM_H + +#include "pm.h" +#include "pnm.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +typedef unsigned long sample; + /* Regardless of the capacity of "unsigned long", a sample is always + less than 1 << 16. This is essential for some code to avoid + arithmetic overflows. + */ + +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. +*/ + + /* 'size' and 'len' are necessary in order to provide forward and + backward compatibility between library functions and calling programs + as this structure grows. + */ + unsigned int size; + /* The storage size of this entire structure, in bytes */ + unsigned int len; + /* 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' + */ + FILE * file; + int format; + /* The format code of the raw image. This is PAM_FORMAT + unless the PAM image is really a view of a PBM, PGM, or PPM + image. Then it's PBM_FORMAT, RPBM_FORMAT, etc. + */ + unsigned int plainformat; + /* Logical: On output, use the 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 + convenience of programs that copy an input pam structure for + use with output. + + Before Netpbm 10.32, this was rather different. It simply + described for convenience the plainness of the format indicated + by 'format'. + */ + int height; /* Height of image in rows */ + int width; + /* Width of image in number of columns (tuples per row) */ + unsigned int depth; + /* Depth of image (number of samples in each tuple). */ + sample maxval; /* Maximum defined value for a sample */ + unsigned int bytes_per_sample; + /* Number of bytes used to represent each sample in the image file. + Note that this is strictly a function of 'maxval'. It is in a + a separate member for computational speed. + */ + char tuple_type[256]; + /* The tuple type string from the image header. If the PAM image + is really a view of a PBM, PGM, or PPM image, the value is + PAM_PBM_TUPLETYPE, PAM_PGM_TUPLETYPE, or PAM_PPM_TUPLETYPE, + respectively. + */ + unsigned int allocation_depth; + /* The number of samples for which memory is allocated for any + 'tuple' type associated with this PAM structure. This must + be at least as great as 'depth'. Only the first 'depth' of + the samples of a tuple are meaningful. + + The purpose of this is to make it possible for a program to + change the type of a tuple to one with more or fewer + planes. + + 0 means the allocation depth is the same as the image depth. + */ + const char ** comment_p; + /* Pointer to a pointer to a NUL-terminated ASCII string of + comments. When reading an image, this contains the + comments from the image's PAM header; when writing, the + image gets these as comments, right after the magic number + line. The individual comments are delimited by newlines + and are in the same order as in the PAM header. + + On output, NULL means no comments. + + On input, libnetpbm mallocs storage for the comments and placed + the pointer at *comment_p. Caller must free it. NULL means + libnetpbm does not return comments and does not allocate any + storage. + */ +}; + +#define PAM_HAVE_ALLOCATION_DEPTH 1 +#define PAM_HAVE_COMMENT_P 1 + +/* PAM_STRUCT_SIZE(x) tells you how big a struct pam is up through the + member named x. This is useful in conjunction with the 'len' value + to determine which fields are present in the structure. +*/ +#define PAM_MEMBER_OFFSET(mbrname) \ + ((unsigned int)(char*)&((struct pam *)0)->mbrname) +#define PAM_MEMBER_SIZE(mbrname) \ + sizeof(((struct pam *)0)->mbrname) +#define PAM_STRUCT_SIZE(mbrname) \ + (PAM_MEMBER_OFFSET(mbrname) + PAM_MEMBER_SIZE(mbrname)) + +#define PAM_BLACK 0 +#define PAM_BW_WHITE 1 + +#define PAM_PBM_TUPLETYPE "BLACKANDWHITE" +#define PAM_PGM_TUPLETYPE "GRAYSCALE" +#define PAM_PPM_TUPLETYPE "RGB" + +#define PAM_PBM_BLACK PAM_BLACK +#define PAM_PBM_WHITE PAM_BW_WHITE + /* These are values of samples in a PAM image that represents a black + and white bitmap image. They are the values of black and white, + respectively. For example, if you use pnm_readpamrow() to read a + row from a PBM file, the black pixels get returned as + PAM_PBM_BLACK. + */ + +#define PAM_RED_PLANE 0 +#define PAM_GRN_PLANE 1 +#define PAM_BLU_PLANE 2 + /* These are plane numbers for the 3 planes of a PAM image that + represents an RGB image (tuple type is "RGB"). So + if 'pixel' is a tuple returned by pnmreadpamrow(), then + pixel[PAM_GRN_PLANE] is the value of the green sample in that + pixel. + */ +#define PAM_TRN_PLANE 3 + /* A PAM with "RGB_ALPHA" tuple type has this 4th plane + for transparency. 0 = transparent, maxval = opaque. + */ +#define PAM_GRAY_TRN_PLANE 1 + /* For a "GRAYSCALE" tuple type, this is the transparency plane */ + +typedef sample *tuple; + /* A tuple in a PAM. This is an array such that tuple[i-1] is the + ith sample (element) in the tuple. It's dimension is the depth + of the image (see pam.depth above). + */ + +#define PAM_OVERALL_MAXVAL 65535 + +/* Note: xv uses the same "P7" signature for its thumbnail images (it + started using it years before PAM and unbeknownst to the designer + of PAM). But these images are still easily distinguishable from + PAMs +*/ +#define PAM_MAGIC1 'P' +#define PAM_MAGIC2 '7' +#define PAM_FORMAT (PAM_MAGIC1 * 256 + PAM_MAGIC2) +#define PAM_TYPE PAM_FORMAT + +/* Macro for turning a format number into a type number. */ + +#define PAM_FORMAT_TYPE(f) ((f) == PAM_FORMAT ? PAM_TYPE : PPM_FORMAT_TYPE(f)) + +struct pamtuples { + struct pam * pamP; + tuple *** tuplesP; +}; + + +typedef float * pnm_transformMap; + /* This is an array of transform maps. transform[N] is the + array that is the map for Plane N. + + Transform maps define a transformation between PAM sample value + to normalized libnetpbm "samplen" value, i.e. what you get back + from pnm_readpamrown() or pass to pnm_writepamrown(). + Typically, it's a gamma transfer function generated by + pnm_creategammatransform() or pnm_createungammatransform(). + + NULL for any transform means just plain normalization -- divide + the PAM sample value by the maxval to get the samplen, multiply + samplen by the maxval and round to get PAM sample value. + + NULL for map table, or 'transform' member not present (pam + structure is too small to contain it) means ALL transforms + are plain normalization. + + Each transform map is an array indexed by a PAM sample + value, containing 'float' values. So it must have 'maxval' + entries. The sample -> samplen tranformation is just the + obvious table lookup. The samplen -> sample transformation is + more complicated -- if the samplen value is between map[N] + and map[N+1], then the sample value is N. And only transforms + where map[N+1] > map[N] are allowed. + */ + +/* Declarations of library functions. */ + +/* We don't have a specific PAM function for init and nextimage, because + one can simply use pnm_init() and pnm_nextimage() from pnm.h. +*/ + +unsigned int +pnm_bytespersample(sample const maxval); + +int +pnm_tupleequal(const struct pam * const pamP, + tuple const comparand, + tuple const comparator); + +void +pnm_assigntuple(const struct pam * const pamP, + tuple const dest, + tuple const source); + +static __inline__ sample +pnm_scalesample(sample const source, + sample const oldmaxval, + sample const newmaxval) { + + if (oldmaxval == newmaxval) + /* Fast path for common case */ + return source; + else + return (source * newmaxval + (oldmaxval/2)) / oldmaxval; +} + + + +void +pnm_scaletuple(const struct pam * const pamP, + tuple const dest, + tuple const source, + sample const newmaxval); + +void +pnm_scaletuplerow(const struct pam * const pamP, + tuple * const destRow, + tuple * const sourceRow, + sample const newMaxval); + +void +pnm_maketuplergb(const struct pam * const pamP, + tuple const tuple); + +void +pnm_makerowrgb(const struct pam * const pamP, + tuple * const tuplerow); + +void +pnm_makearrayrgb(const struct pam * const pamP, + tuple ** const tuples); + +void +pnm_getopacity(const struct pam * const pamP, + int * const haveOpacityP, + unsigned int * const opacityPlaneP); + +void +pnm_createBlackTuple(const struct pam * const pamP, tuple * const blackTupleP); + +void +createBlackTuple(const struct pam * const pamP, tuple * const blackTupleP); + + +tuple +pnm_allocpamtuple(const struct pam * const pamP); + +#define pnm_freepamtuple(tuple) pm_freerow((char*) tuple) + +tuple * +pnm_allocpamrow(const struct pam * const pamP); + +#define pnm_freepamrow(tuplerow) pm_freerow((char*) tuplerow) + +tuple ** +pnm_allocpamarray(const struct pam * const pamP); + +void +pnm_freepamarray(tuple ** const tuplearray, const struct pam * const pamP); + +void +pnm_setminallocationdepth(struct pam * const pamP, + unsigned int const allocationDepth); + +void +pnm_setpamrow(const struct pam * const pam, + tuple * const tuplerow, + sample const value); + +unsigned char * +pnm_allocrowimage(const struct pam * const pamP); + +void +pnm_freerowimage(unsigned char * const rowimage); + +void +pnm_readpaminit(FILE * const file, + struct pam * const pamP, + int const size); + +void +pnm_readpamrow(const struct pam * const pamP, tuple* const tuplerow); + +tuple ** +pnm_readpam(FILE * const file, + struct pam * const pamP, + int const size); + +void +pnm_writepaminit(struct pam * const pamP); + +void +pnm_formatpamrow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP); + +void +pnm_writepamrow(const struct pam * const pamP, const tuple * const tuplerow); + +void +pnm_writepamrowmult(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned int const rptcnt); + +void +pnm_writepam(struct pam * const pamP, tuple ** const tuplearray); + +void +pnm_checkpam(const struct pam * const pamP, + enum pm_check_type const checkType, + enum pm_check_code * const retvalP); + +/*---------------------------------------------------------------------------- + Facilities for working with maxval-normalized samples. Such samples + are floating point quantities in the range 0..1. + + This is just a working format; there is no Netpbm image format that + has normalized samples. +-----------------------------------------------------------------------------*/ +typedef float samplen; + +typedef samplen *tuplen; + /* Same as 'tuple', except using normalized samples. */ + +tuplen * +pnm_allocpamrown(const struct pam * const pamP); + +#define pnm_freepamrown(tuplenrow) pm_freerow((char*) tuplenrow) + +tuplen * +pnm_allocpamrown(const struct pam * const pamP); + +void +pnm_readpamrown(const struct pam * const pamP, + tuplen * const tuplenrow); + +void +pnm_writepamrown(const struct pam * const pamP, + const tuplen * const tuplenrow); + +tuplen ** +pnm_allocpamarrayn(const struct pam * const pamP); + +void +pnm_freepamarrayn(tuplen ** const tuplenarray, + const struct pam * const pamP); + +tuplen** +pnm_readpamn(FILE * const file, + struct pam * const pamP, + int const size); + +void +pnm_writepamn(struct pam * const pamP, + tuplen ** const tuplenarray); + + +void +pnm_normalizetuple(struct pam * const pamP, + tuple const tuple, + tuplen const tuplen); + +void +pnm_unnormalizetuple(struct pam * const pamP, + tuplen const tuplen, + tuple const tuple); + +void +pnm_normalizeRow(struct pam * const pamP, + const tuple * const tuplerow, + const pnm_transformMap * const transform, + tuplen * const tuplenrow); + +void +pnm_unnormalizeRow(struct pam * const pamP, + const tuplen * const tuplenrow, + const pnm_transformMap * const transform, + tuple * const tuplerow); + +/*---------------------------------------------------------------------------- + Facilities for working with visual images in particular +-----------------------------------------------------------------------------*/ + + +void +pnm_gammarown(struct pam * const pamP, + tuplen * const row); + +void +pnm_ungammarown(struct pam * const pamP, + tuplen * const row); + +void +pnm_applyopacityrown(struct pam * const pamP, + tuplen * const tuplenrow); + +void +pnm_unapplyopacityrown(struct pam * const pamP, + tuplen * const tuplenrow); + +pnm_transformMap * +pnm_creategammatransform(const struct pam * const pamP); + +void +pnm_freegammatransform(const pnm_transformMap * const transform, + const struct pam * const pamP); + +pnm_transformMap * +pnm_createungammatransform(const struct pam * const pamP); + +#define pnm_freeungammatransform pnm_freegammatransform; + +tuple +pnm_parsecolor(const char * const colorname, + sample const maxval); + +extern double +pnm_lumin_factor[3]; + +void +pnm_YCbCrtuple(const tuple tuple, + double * const YP, double * const CbP, double * const CrP); + +void +pnm_YCbCr_to_rgbtuple(const struct pam * const pamP, + tuple const tuple, + double const Y, + double const Cb, + double const Cr, + int * const overflowP); + +#define pnm_rgbtupleisgray(tuple) \ + ((tuple)[PAM_RED_PLANE] == (tuple)[PAM_GRN_PLANE] && \ + (tuple)[PAM_RED_PLANE] == (tuple)[PAM_BLU_PLANE]) + +/*---------------------------------------------------------------------------- + These are meant for passing to pm_system() as Standard Input feeder + and Standard Output accepter. +-----------------------------------------------------------------------------*/ + +void +pm_feed_from_pamtuples(int const pipeToFeedFd, + void * const feederParm); + +void +pm_accept_to_pamtuples(int const pipeToSuckFd, + void * const accepterParm); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/pammap.h b/lib/pammap.h new file mode 100644 index 00000000..fa054deb --- /dev/null +++ b/lib/pammap.h @@ -0,0 +1,124 @@ +/****************************************************************************** + pammap.h +******************************************************************************* + + Interface header file for hash table and lookup table pam functions + in libpnm. + +******************************************************************************/ + +#ifndef PAMMAP_H +#define PAMMAP_H +#include "colorname.h" +#include "pam.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +struct tupleint { + /* An ordered pair of a tuple value and an integer, such as you + would find in a tuple table or tuple hash. + + Note that this is a variable length structure. + */ + int value; + sample tuple[1]; + /* This is actually a variable size array -- its size is the + depth of the tuple in question. Some compilers do not let us + declare a variable length array. + */ +}; +typedef struct tupleint ** tupletable; + +typedef struct { + unsigned int size; + tupletable table; +} tupletable2; + +struct tupleint_list_item { + struct tupleint_list_item * next; + struct tupleint tupleint; +}; +typedef struct tupleint_list_item * tupleint_list; + +typedef tupleint_list * tuplehash; + +unsigned int +pnm_hashtuple(struct pam * const pamP, tuple const tuple); + +void +pnm_addtotuplehash(struct pam * const pamP, + tuplehash const tuplehash, + tuple const tuple, + int const value, + int * const fitsP); + +void +pnm_addtuplefreqoccurrence(struct pam * const pamP, + tuple const value, + tuplehash const tuplefreqhash, + int * const firstOccurrenceP); + +void +pnm_lookuptuple(struct pam * const pamP, const tuplehash tuplehash, + const tuple searchval, + int * const foundP, int * const retvalP); + +tupletable +pnm_alloctupletable(const struct pam * const pamP, unsigned int const size); + +void +pnm_freetupletable(struct pam * const pamP, tupletable const tupletable); + +void +pnm_freetupletable2(struct pam * const pamP, tupletable2 const tupletable); + +tuplehash +pnm_createtuplehash(void); + +void +pnm_destroytuplehash(tuplehash const tuplehash); + +tupletable +pnm_computetuplefreqtable(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + unsigned int * const sizeP); + +tupletable +pnm_computetuplefreqtable2(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + sample const newMaxval, + unsigned int * const sizeP); + +tuplehash +pnm_computetuplefreqhash(struct pam * const pamP, + tuple ** const tupleArray, + unsigned int const maxsize, + unsigned int * const sizeP); + +tuplehash +pnm_computetupletablehash(struct pam * const pamP, + tupletable const tupletable, + unsigned int const tupletableSize); + +tupletable +pnm_tuplehashtotable(const struct pam * const pamP, + tuplehash const tuplehash, + unsigned int const maxsize); + +char* +pam_colorname(struct pam * const pamP, + tuple const color, + enum colornameFormat const format); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/path.c b/lib/path.c new file mode 100644 index 00000000..79985109 --- /dev/null +++ b/lib/path.c @@ -0,0 +1,468 @@ +/* This library module contains "ppmdraw" routines for drawing paths. + + By Bryan Henderson San Jose CA 06.05.24. + Contributed to the public domain. + + I actually wrote this before I knew ppmd_fill() already existed. + ppmd_fill() is more general. But path.c is probably faster and is + better code, so I'm keeping it for now. +*/ + +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "ppm.h" +#include "ppmdfont.h" +#include "ppmdraw.h" + + + +/* This is the algorithm we use to fill a path. + + A path is a set of points that form a closed figure. The last point + and first point are identical. But the path may cross itself other + places too. Our job is to color the interior of that figure. + + We do it with horizontal lines. We follow the path around from + start to finish, visiting every point in it. We remember in a + stack the points we've been to. As long as we keep going in the + same vertical direction, that stack grows. When we turn around and + visit a row that we've been to before, we drop a horizontal line of + fill color from where we are now to where we were the last time we + visited that row, and then remove that entry from the stack. Note + that because we go one point at a time, the entry on the stack for + the row we're at now will always be on the top of stack. + + Note that the points on the stack always have consecutive row + numbers, monotonically increasing or decreasing, whichever is the + direction we started out in. + + This goes on, with the stack alternately growing and shrinking as + the path turns to head up and down, until we get back to the row + where we started. At this point, the stack becomes empty following + the algorithm in the previous paragraph. If the path crosses over + and keeps going, we just start filling the stack again, with the + entries now going in the opposite direction from before -- e.g. + if the path started out heading downward, the points in the stack + mononotically increased in row number; after crossing back over, + the points in the stack monotonically decrease in row number. + + + It's probably more efficient to use vertical lines than horizontal + ones when the image is tall and narrow. But we're not that + sophisticated. +*/ + + +/* NOTE NOTE NOTE + + In all the path logic below, we call the direction of increasing row + number "up" because we think of the raster as standard Cartesian + plane. So visualize the image as upside down in the first quadrant + of the Cartesian plane -- the real top left corner of the image is at + the origin. +*/ + + + +static bool +pointEqual(ppmd_point const comparator, + ppmd_point const comparand) { + + return comparator.x == comparand.x && comparator.y == comparand.y; +} + + + +static int +vertDisp(ppmd_point const begPoint, + ppmd_point const endPoint) { +/*---------------------------------------------------------------------------- + Return the vertical displacement of 'endPoint' with respect to + 'begPoint' -- How much higher 'endPoint' is than 'begPoint'. +-----------------------------------------------------------------------------*/ + return endPoint.y - begPoint.y; +} + + + +static int +horzDisp(ppmd_point const begPoint, + ppmd_point const endPoint) { +/*---------------------------------------------------------------------------- + Return the horizontal displacement of 'endPoint' with respect to + 'begPoint' -- How much further right 'endPoint' is than 'begPoint'. +-----------------------------------------------------------------------------*/ + return endPoint.x - begPoint.x; +} + + + +static bool +isOnLineSeg(ppmd_point const here, + ppmd_point const begPoint, + ppmd_point const endPoint) { + + return + here.y >= MIN(begPoint.y, endPoint.y) && + here.y <= MAX(begPoint.y, endPoint.y) && + here.x >= MIN(begPoint.x, endPoint.x) && + here.x <= MAX(begPoint.x, endPoint.x); +} + + + +static double +lineSlope(ppmd_point const begPoint, + ppmd_point const endPoint) { +/*---------------------------------------------------------------------------- + Return the slope of the line that begins at 'begPoint' and ends at + 'endPoint'. + + Positive slope means as row number increases, column number increases. +-----------------------------------------------------------------------------*/ + return (double)vertDisp(begPoint, endPoint) / horzDisp(begPoint, endPoint); +} + + + +typedef struct { +/*---------------------------------------------------------------------------- + A complicated structure for tracking the state of a the filling + algorithm. See description of algorithm above. +-----------------------------------------------------------------------------*/ + ppmd_point * stack; + /* An array which holds the fundamental stack. The bottom of the + stack is element 0. It grows consecutively up. + */ + unsigned int topOfStack; + /* Index in stack[] of the top of the entry which will hold next + _next_ element pushed onto the stack. + */ + unsigned int stackSize; + /* Number of elements in stack[] -- i.e. maximum height of stack */ + int step; + /* -1 or 1. -1 means each new point pushed on the stack is one + unit below the previous one; 1 means each new point pushed on + the stack is one unit above the previous one. + */ +} fillStack; + + + +static void +createStack(fillStack ** const stackPP) { +/*---------------------------------------------------------------------------- + Create an empty fill stack. +-----------------------------------------------------------------------------*/ + fillStack * stackP; + + MALLOCVAR_NOFAIL(stackP); + + stackP->stackSize = 1024; + + MALLOCARRAY(stackP->stack, stackP->stackSize); + + if (stackP->stack == NULL) + pm_error("Could not allocate memory for a fill stack of %u points", + stackP->stackSize); + + stackP->topOfStack = 0; + + stackP->step = +1; /* arbitrary choice */ + + *stackPP = stackP; +} + + + +static void +destroyStack(fillStack * const stackP) { + + free(stackP->stack); + + free(stackP); +} + + + +static ppmd_point +topOfStack(fillStack * const stackP) { + + assert(stackP->topOfStack > 0); + + return stackP->stack[stackP->topOfStack-1]; +} + + + +static bool +stackIsEmpty(fillStack * const stackP) { + + return stackP->topOfStack == 0; +} + + + +static bool +inStackDirection(fillStack * const stackP, + ppmd_point const point) { +/*---------------------------------------------------------------------------- + Return true iff the point 'point' is is a step in the current stack + direction from the point on the top of the stack. I.e. we could push + this point onto the stack without violating the monotonic property. +-----------------------------------------------------------------------------*/ + if (stackIsEmpty(stackP)) + return true; + else + return topOfStack(stackP).y + stackP->step == point.y; +} + + + +static bool +againstStackDirection(fillStack * const stackP, + ppmd_point const point) { +/*---------------------------------------------------------------------------- + Return true iff the point 'point' is a step in the other direction + form the current stack direction. +-----------------------------------------------------------------------------*/ + if (stackIsEmpty(stackP)) + return false; + else + return topOfStack(stackP).y - stackP->step == point.y; +} + + + +static bool +isLateralFromTopOfStack(fillStack * const stackP, + ppmd_point const point) { +/*---------------------------------------------------------------------------- + Return true iff the point 'point' is laterally across from the point + on the top of the stack. +-----------------------------------------------------------------------------*/ + if (stackIsEmpty(stackP)) + return false; + else + return point.y == topOfStack(stackP).y; +} + + + +static void +pushStack(fillStack * const stackP, + ppmd_point const newPoint) { + + assert(inStackDirection(stackP, newPoint)); + + if (stackP->topOfStack >= stackP->stackSize) { + stackP->stackSize *= 2; + + REALLOCARRAY(stackP->stack, stackP->stackSize); + + if (stackP->stack == NULL) + pm_error("Could not allocate memory for a fill stack of %u points", + stackP->stackSize); + } + assert(stackP->topOfStack < stackP->stackSize); + + stackP->stack[stackP->topOfStack++] = newPoint; +pm_message("pushed (%u, %u) at %u", newPoint.x, newPoint.y, stackP->topOfStack-1); +} + + + +static ppmd_point +popStack(fillStack * const stackP) { + + ppmd_point retval; + + assert(stackP->topOfStack < stackP->stackSize); + + retval = stackP->stack[--stackP->topOfStack]; +pm_message("popped (%u, %u) at %u", retval.x, retval.y, stackP->topOfStack); + return retval; +} + + + +static void +replaceTopOfStack(fillStack * const stackP, + ppmd_point const point) { + + assert(stackP->topOfStack > 0); + + stackP->stack[stackP->topOfStack-1] = point; +} + + + +static void +reverseStackDirection(fillStack * const stackP) { + + stackP->step *= -1; +} + + + +static void +drawFillLine(ppmd_point const begPoint, + ppmd_point const endPoint, + pixel ** const pixels, + pixel const color) { + + unsigned int leftCol, rghtCol; + unsigned int col; + unsigned int row; + + /* Fill lines are always horizontal */ + + assert(begPoint.y == endPoint.y); + +pm_message("filling from (%u, %u) to (%u, %u)", begPoint.x, begPoint.y, endPoint.x, endPoint.y); + row = begPoint.y; + + if (begPoint.x <= endPoint.x) { + leftCol = begPoint.x; + rghtCol = endPoint.x; + } else { + leftCol = endPoint.x; + rghtCol = begPoint.x; + } + + for (col = leftCol; col <= rghtCol; ++col) + pixels[row][col] = color; +} + + + +static void +fillPoint(fillStack * const stackP, + ppmd_point const point, + pixel ** const pixels, + pixel const color) { +/*---------------------------------------------------------------------------- + Follow the outline of the figure to the point 'point', which is adjacent + to the point most recently added. + + Fill the image in 'pixels' with color 'color' and update *stackP as + required. +-----------------------------------------------------------------------------*/ +pm_message("filling point (%u, %u)", point.x, point.y); + if (inStackDirection(stackP, point)) { + pushStack(stackP, point); + pixels[point.y][point.x] = color; + } else { + if (againstStackDirection(stackP, point)) + popStack(stackP); + + if (stackIsEmpty(stackP)) { + reverseStackDirection(stackP); + pushStack(stackP, point); + } else { + assert(isLateralFromTopOfStack(stackP, point)); + + drawFillLine(topOfStack(stackP), point, pixels, color); + replaceTopOfStack(stackP, point); + } + } +} + + + +static void +fillLeg(ppmd_point const begPoint, + ppmd_point const endPoint, + fillStack * const stackP, + pixel ** const pixels, + pixel const color) { +/*---------------------------------------------------------------------------- + Follow the leg which is a straight line segment from 'begPoint' + through 'endPoint', filling the raster 'pixels' with color 'color', + according to *stackP as we go. + + We update *stackP accordingly. + + A leg starts where the leg before it ends, so we skip the first point + in the line segment. +-----------------------------------------------------------------------------*/ + assert(!stackIsEmpty(stackP)); + + if (endPoint.y == begPoint.y) + /* Line is horizontal; We need just the end point. */ + fillPoint(stackP, endPoint, pixels, color); + else { + double const invSlope = 1/lineSlope(begPoint, endPoint); + int const step = endPoint.y > begPoint.y ? +1 : -1; + + ppmd_point here; + + here = begPoint; + + while (here.y != endPoint.y) { + here.y += step; + here.x = ROUNDU(begPoint.x + vertDisp(begPoint, here) * invSlope); + + assert(isOnLineSeg(here, begPoint, endPoint)); + + fillPoint(stackP, here, pixels, color); + } + } +} + + + +void +ppmd_fill_path(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_path * const pathP, + pixel const color) { +/*---------------------------------------------------------------------------- + Draw a path which defines a closed figure (or multiple closed figures) + and fill it in. + + *pathP describes the path. 'color' is the color with which to fill. + + 'pixels' is the canvas on which to draw the figure; its dimensions are + 'cols' x 'rows'. + + 'maxval' is the maxval for 'color' and for 'pixels'. + + Fail (abort the program) if the path does not end on the same point at + which it began. +-----------------------------------------------------------------------------*/ + ppmd_point prevVertex; + fillStack * stackP; + unsigned int legNumber; + + createStack(&stackP); + + prevVertex = pathP->begPoint; + pushStack(stackP, pathP->begPoint); + + for (legNumber = 0; legNumber < pathP->legCount; ++legNumber) { + ppmd_pathleg * const legP = &pathP->legs[legNumber]; + ppmd_point const nextVertex = legP->u.linelegparms.end; + + if (prevVertex.y >= rows || nextVertex.y >= rows) + pm_error("Path extends below the image."); + if (prevVertex.x >= cols || nextVertex.x >= cols) + pm_error("Path extends off the image to the right."); + + fillLeg(prevVertex, nextVertex, stackP, pixels, color); + + prevVertex = nextVertex; + } + if (!pointEqual(prevVertex, pathP->begPoint)) + pm_error("Failed to fill a path -- the path is not closed " + "(i.e. it doesn't end up at the same point where it began)"); + + assert(pointEqual(popStack(stackP), pathP->begPoint)); + assert(stackIsEmpty(stackP)); + + destroyStack(stackP); +} diff --git a/lib/pbm.h b/lib/pbm.h new file mode 100644 index 00000000..1591c77f --- /dev/null +++ b/lib/pbm.h @@ -0,0 +1,97 @@ +#ifndef PBM_H_INCLUDED +#define PBM_H_INCLUDED + +#include "pm.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +typedef unsigned char bit; +#define PBM_WHITE 0 +#define PBM_BLACK 1 + + +/* Magic constants. */ + +#define PBM_MAGIC1 'P' +#define PBM_MAGIC2 '1' +#define RPBM_MAGIC2 '4' +#define PBM_FORMAT (PBM_MAGIC1 * 256 + PBM_MAGIC2) +#define RPBM_FORMAT (PBM_MAGIC1 * 256 + RPBM_MAGIC2) +#define PBM_TYPE PBM_FORMAT + + +/* Macro for turning a format number into a type number. */ + +#define PBM_FORMAT_TYPE(f) \ + ((f) == PBM_FORMAT || (f) == RPBM_FORMAT ? PBM_TYPE : -1) + + +/* Declarations of routines. */ + +void pbm_init ARGS(( int* argcP, char* argv[] )); +void +pbm_nextimage(FILE *file, int * const eofP); + +#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) +#define pbm_allocrow_packed(cols) \ + ((unsigned char *) pm_allocrow(pbm_packed_bytes(cols), \ + sizeof(unsigned char))) +#define pbm_freerow_packed(packed_bits) \ + pm_freerow((char *) packed_bits) +#define pbm_allocarray_packed(cols, rows) ((unsigned char **) \ + pm_allocarray(pbm_packed_bytes(cols), rows, sizeof(unsigned char))) +#define pbm_freearray_packed(packed_bits, rows) \ + pm_freearray((char **) packed_bits, rows) + +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_writepbminit(FILE * const fileP, + int const cols, + int const rows, + int const forceplain); + +void +pbm_writepbm(FILE * const fileP, + bit ** const bits, + int const cols, + int const rows, + int const forceplain); + +void +pbm_writepbmrow(FILE * const fileP, + bit * const bitrow, + int const cols, + int const forceplain); + +void +pbm_writepbmrow_packed(FILE * const fileP, + const unsigned char * const packed_bits, + int const cols, + int const forceplain); + +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); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/pbmfont.h b/lib/pbmfont.h new file mode 100644 index 00000000..432aea3c --- /dev/null +++ b/lib/pbmfont.h @@ -0,0 +1,75 @@ +/* pbmfont.h - header file for font routines in libpbm +*/ + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +struct glyph { + /* A glyph consists of white borders and the "central glyph" which + can be anything, but normally does not have white borders because + it's more efficient to represent white borders explicitly. + */ + int width, height; + /* The dimensions of the central glyph, i.e. the 'bmap' array */ + 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; + /* Height in pixels of the white bottom border of this glyph */ + int xadd; + /* Width of glyph -- white left border plus central glyph + plus white right border + */ + 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 + 'height' is 40 pixels and it's a rectangle that is black on + the top half and white on the bottom, this is an array of + 800 bytes, with the first 400 having value 0x01 and the + last 400 having value 0x00. + */ +}; + +struct font { + /* This describes a combination of font and character set. Given + an code point in the range 0..255, this structure describes the + glyph for that character. + */ + int maxwidth, maxheight; + int x; + /* ?? Not used by Pbmtext */ + int y; + /* Amount of white space that should be added between lines of + this font. + */ + 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; +}; + +struct font* pbm_defaultfont(const char* const which); +struct font* +pbm_dissectfont(bit ** const font, + int const frows, + 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 )); + +#ifdef __cplusplus +} +#endif diff --git a/lib/pgm.h b/lib/pgm.h new file mode 100644 index 00000000..86935307 --- /dev/null +++ b/lib/pgm.h @@ -0,0 +1,136 @@ +/* pgm.h - header file for libpgm portable graymap library +*/ + +#ifndef _PGM_H_ +#define _PGM_H_ + +#include "pbm.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +/* The following definition has nothing to do with the format of a PGM file */ +typedef unsigned int gray; + +/* Since April 2000, we are capable of reading and generating raw + (binary) PGM files with maxvals up to 65535. However, before that + the maximum (as usually implemented) was 255, and people still want + to generate files with a maxval of no more than 255 in most cases + (because then old Netpbm programs can process them, and they're + only half as big). + + So we keep PGM_MAXMAXVAL = 255, even though it's kind of a misnomer. + + Note that one could always write a file with maxval > PGM_MAXMAXVAL and + it would just go into plain (text) format instead of raw (binary) format. + Along with the expansion to 16 bit raw files, we took away that ability. + Unless you specify 'forceplain' on the pgm_writepgminit() call, it will + fail if you specify a maxval > PGM_OVERALLMAXVAL. I made this design + decision because I don't think anyone really wants to get a plain format + file with samples larger than 65535 in it. However, it should be possible + just to increase PGM_OVERALLMAXVAL and get that old function back for + maxvals that won't fit in 16 bits. I think the only thing really + constraining PGM_OVERALLMAXVAL is the size of the 'gray' data structure, + which is generally 32 bits. +*/ + +#define PGM_OVERALLMAXVAL 65535 +#define PGM_MAXMAXVAL 255 + + +/* Magic constants. */ + +#define PGM_MAGIC1 'P' +#define PGM_MAGIC2 '2' +#define RPGM_MAGIC2 '5' +#define PGM_FORMAT (PGM_MAGIC1 * 256 + PGM_MAGIC2) +#define RPGM_FORMAT (PGM_MAGIC1 * 256 + RPGM_MAGIC2) +#define PGM_TYPE PGM_FORMAT + +/* For the alpha-mask variant of PGM: */ +#define PGM_TRANSPARENT 0 + +/* Macro for turning a format number into a type number. */ + +#define PGM_FORMAT_TYPE(f) ((f) == PGM_FORMAT || (f) == RPGM_FORMAT ? PGM_TYPE : PBM_FORMAT_TYPE(f)) + + +/* Declarations of routines. */ + +void +pgm_init(int * const argcP, + char ** const argv); + +gray * +pgm_allocrow(unsigned int const cols); + +#define pgm_freerow(grayrow) free(grayrow) + +#define pgm_allocarray( cols, rows ) \ + ((gray**) pm_allocarray( cols, rows, sizeof(gray) )) +#define pgm_freearray( grays, rows ) pm_freearray( (char**) grays, rows ) + +gray ** +pgm_readpgm(FILE * const file, + int * const colsP, + int * const rowsP, + gray * const maxvalP); + +void +pgm_readpgminit(FILE * const file, + int * const colsP, + int * const rowsP, + gray * const maxvalP, + int * const formatP); + +void +pgm_readpgmrow(FILE * const file, + gray * const grayrow, + int const cols, + gray const maxval, + int const format); + +void +pgm_writepgminit(FILE * const fileP, + int const cols, + int const rows, + gray const maxval, + int const forceplain); + +void +pgm_writepgmrow(FILE * const fileP, + const gray * const grayrow, + int const cols, + gray const maxval, + int const forceplain); + +void +pgm_writepgm(FILE * const fileP, + gray ** const grays, + int const cols, + int const rows, + gray const maxval, + int const forceplain); + +void +pgm_nextimage(FILE * const file, int * const eofP); + +void +pgm_check(FILE * const file, + enum pm_check_type const check_type, + int const format, + int const cols, + int const rows, + gray const maxval, + enum pm_check_code * const retval_p); + +#ifdef __cplusplus +} +#endif + + +#endif /*_PGM_H_*/ diff --git a/lib/pm.h b/lib/pm.h new file mode 100644 index 00000000..040a6a4b --- /dev/null +++ b/lib/pm.h @@ -0,0 +1,346 @@ +/* pm.h - interface to format-independent part of libpbm. +** +** Copyright (C) 1988, 1989, 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#ifndef PM_H_INCLUDED +#define PM_H_INCLUDED + +#include "pm_config.h" + +#include +#include +#include +#include +#include +#include + +#ifdef VMS +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + + +/* Definitions to make Netpbm programs work with either ANSI C or C + Classic. + + This is obsolete, as all compilers recognize the ANSI syntax now. + + We are slowly removing all the ARGS invocations from the programs + (and replacing them with explicit ANSI syntax), but we have a lot + of programs where we have removed ARGS from the definition but not + the prototype, and we have discovered that the Sun compiler + considers the resulting mismatch between definition and prototype + to be an error. So we make ARGS create the ANSI syntax + unconditionally to avoid having to fix all those mismatches. */ + +#if 0 +#if __STDC__ +#define ARGS(alist) alist +#else /*__STDC__*/ +#define ARGS(alist) () +#define const +#endif /*__STDC__*/ +#endif +#define ARGS(alist) alist + + +/* PM_GNU_PRINTF_ATTR lets the GNU compiler check pm_message() and pm_error() + calls to be sure the arguments match the format string, thus preventing + runtime segmentation faults and incorrect messages. +*/ +#ifdef __GNUC__ +#define PM_GNU_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) +#else +#define PM_GNU_PRINTF_ATTR +#endif + + +/* PURE_FN_ATTR is the attribute you add to a function declaration + that indicates it's a true function -- has no side effects and return + value is not influenced by anything except its arguments. +*/ +#ifdef __GNUC__ +#define PURE_FN_ATTR __attribute__ ((const)) +#else +#define PURE_FN_ATTR +#endif + +typedef struct { + /* Coordinates of a pixel within an image. Row 0 is the top row. + Column 0 is the left column. + */ + unsigned int row; + unsigned int col; +} pm_pixelcoord; + +extern int pm_plain_output; + /* Output functions are to produce plain (as opposed to raw) format + regardless of their 'plainformat' arguments. + */ + +void +pm_init(const char * const progname, unsigned int const flags); + +void +pm_proginit(int* const argcP, char* argv[]); + +void +pm_setMessage(int const newState, int * const oldStateP); + +FILE * +pm_tmpfile(void); + +void +pm_make_tmpfile(FILE ** const filePP, + const char ** const filenameP); + +void +pm_nextimage(FILE * const file, int * const eofP); + +/* Variable-sized arrays definitions. */ + +char** +pm_allocarray (int const cols, int const rows, int const size ); + +char * +pm_allocrow(unsigned int const cols, + unsigned int const size); + +void +pm_freearray (char** const its, int const rows); + +void +pm_freerow(char* const itrow); + + +/* Obsolete -- use shhopt instead */ +int +pm_keymatch (char* const str, const char* const keyword, int const minchars); + + +int PURE_FN_ATTR +pm_maxvaltobits(int const maxval); + +int PURE_FN_ATTR +pm_bitstomaxval(int const bits); + +unsigned int PURE_FN_ATTR +pm_lcm (unsigned int const x, + unsigned int const y, + unsigned int const z, + unsigned int const limit); + +void +pm_setjmpbuf(jmp_buf * const jmpbufP); + +void +pm_setjmpbufsave(jmp_buf * const jmpbufP, + jmp_buf ** const oldJmpbufPP); + +void +pm_longjmp(void); + +void PM_GNU_PRINTF_ATTR(1,2) +pm_message (const char format[], ...); + +void PM_GNU_PRINTF_ATTR(1,2) +pm_error (const char reason[], ...); + +/* Obsolete - use helpful error message instead */ +void +pm_perror (const char reason[]); + +/* Obsolete - use shhopt and user's manual instead */ +void +pm_usage (const char usage[]); + +FILE* +pm_openr (const char* const name); + +FILE* +pm_openw (const char* const name); + +FILE * +pm_openr_seekable(const char name[]); + +void +pm_close (FILE* const f); + +void +pm_closer (FILE* const f); + +void +pm_closew (FILE* const f); + + + +void +pm_readchar(FILE * const ifP, + char * const cP); + +static __inline__ void +pm_readcharu(FILE * const ifP, + unsigned char * const cP) { + pm_readchar(ifP, (char *) cP); +} + +void +pm_writechar(FILE * const ofP, + char const c); + +static __inline__ void +pm_writecharu(FILE * const ofP, + unsigned char const c) { + pm_writechar(ofP, (char) c); +} + +int +pm_readbigshort(FILE * const ifP, + short * const sP); + +static __inline__ int +pm_readbigshortu(FILE* const ifP, + unsigned short * const sP) { + return pm_readbigshort(ifP, (short *) sP); +} + +int +pm_writebigshort(FILE * const ofP, + short const s); + +static __inline__ int +pm_writebigshortu(FILE * const ofP, + unsigned short const s) { + return pm_writebigshort(ofP, (short) s); +} + +int +pm_readbiglong(FILE * const ifP, + long * const lP); + +static __inline__ int +pm_readbiglongu(FILE * const ifP, + unsigned long * const lP) { + return pm_readbiglong(ifP, (long *) lP); +} + +int +pm_writebiglong(FILE * const ofP, + long const l); + +static __inline__ int +pm_writebiglongu(FILE * const ofP, + unsigned long const l) { + return pm_writebiglong(ofP, (long) l); +} + +int +pm_readlittleshort(FILE * const ifP, + short * const sP); + +static __inline__ int +pm_readlittleshortu(FILE * const ifP, + unsigned short * const sP) { + return pm_readlittleshort(ifP, (short *) sP); +} + +int +pm_writelittleshort(FILE * const ofP, + short const s); + +static __inline__ int +pm_writelittleshortu(FILE * const ofP, + unsigned short const s) { + return pm_writelittleshort(ofP, (short) s); +} + +int +pm_readlittlelong(FILE * const ifP, + long * const lP); + +static __inline__ int +pm_readlittlelongu(FILE * const ifP, + unsigned long * const lP) { + return pm_readlittlelong(ifP, (long *) lP); +} + +int +pm_writelittlelong(FILE * const ofP, + long const l); + +static __inline__ int +pm_writelittlelongu(FILE * const ofP, + unsigned long const l) { + return pm_writelittlelong(ofP, (long) l); +} + +int +pm_readmagicnumber(FILE * const ifP); + +char* +pm_read_unknown_size(FILE * const ifP, + long * const buf); + +short +pm_bs_short(short const s); + +long +pm_bs_long(long const l); + +unsigned int +pm_tell(FILE * const fileP); + +void +pm_tell2(FILE * const fileP, + void * const fileposP, + unsigned int const fileposSize); + +void +pm_seek2(FILE * const fileP, + const pm_filepos * const fileposP, + unsigned int const fileposSize); + +void +pm_seek(FILE * const fileP, unsigned long filepos); + +enum pm_check_code { + PM_CHECK_OK, + PM_CHECK_UNKNOWN_TYPE, + PM_CHECK_TOO_LONG, + PM_CHECK_UNCHECKABLE, + PM_CHECK_TOO_SHORT +}; + +enum pm_check_type { + PM_CHECK_BASIC +}; + +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); + +char * +pm_arg0toprogname(const char arg0[]); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/pm_gamma.h b/lib/pm_gamma.h new file mode 100644 index 00000000..92b34145 --- /dev/null +++ b/lib/pm_gamma.h @@ -0,0 +1,68 @@ +#ifndef _PM_GAMMA_H_ +#define _PM_GAMMA_H_ + +#include "pm_config.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +static __inline__ float +pm_gamma709(float const intensity) { + + /* Here are parameters of the gamma transfer function + for the Netpbm formats. This is CIE Rec 709. + + This transfer function is linear for sample values 0 .. .018 + and an exponential for larger sample values. + The exponential is slightly stretched and translated, though, + unlike the popular pure exponential gamma transfer function. + */ + float const gamma = 2.2; + float const oneOverGamma = 1.0 / gamma; + float const linearCutoff = 0.018; + float const linearExpansion = + (1.099 * pow(linearCutoff, oneOverGamma) - 0.099) / linearCutoff; + + float brightness; + + if (intensity < linearCutoff) + brightness = intensity * linearExpansion; + else + brightness = 1.099 * pow(intensity, oneOverGamma) - 0.099; + + return brightness; +} + + + +static __inline__ float +pm_ungamma709(float const brightness) { + + /* These are the same parameters as in pm_gamma, above */ + + float const gamma = 2.2; + float const oneOverGamma = 1.0 / gamma; + float const linearCutoff = 0.018; + float const linearExpansion = + (1.099 * pow(linearCutoff, oneOverGamma) - 0.099) / linearCutoff; + + float intensity; + + if (brightness < linearCutoff * linearExpansion) + intensity = brightness / linearExpansion; + else + intensity = pow((brightness + 0.099) / 1.099, gamma); + + return intensity; +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/pm_system.h b/lib/pm_system.h new file mode 100644 index 00000000..0605f888 --- /dev/null +++ b/lib/pm_system.h @@ -0,0 +1,43 @@ +#ifndef PM_SYSTEM_H_INCLUDED +#define PM_SYSTEM_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + + +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; + unsigned char * buffer; + unsigned int * bytesTransferredP; +}; + + +/* The following are a Standard Input feeder and a Standard Output accepter + for pm_system(). +*/ +void +pm_feed_from_memory(int const pipeToFeedFd, + void * const feederParm); + +void +pm_accept_to_memory(int const pipetosuckFd, + void * const accepterParm); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/pnm.h b/lib/pnm.h new file mode 100644 index 00000000..d3b6f84f --- /dev/null +++ b/lib/pnm.h @@ -0,0 +1,134 @@ +/* pnm.h - header file for libpnm portable anymap library +*/ + +#ifndef _PNM_H_ +#define _PNM_H_ + +#include "ppm.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + + +typedef pixel xel; +typedef pixval xelval; +#define PNM_OVERALLMAXVAL PPM_OVERALLMAXVAL +#define PNM_MAXMAXVAL PPM_MAXMAXVAL +#define PNM_GET1(x) PPM_GETB(x) +#define PNM_ASSIGN1(x,v) PPM_ASSIGN(x,0,0,v) +#define PNM_ASSIGN(x,r,g,b) PPM_ASSIGN(x,r,g,b) +#define PNM_EQUAL(x,y) PPM_EQUAL(x,y) +#define PNM_FORMAT_TYPE(f) PPM_FORMAT_TYPE(f) +#define PNM_DEPTH(newp,p,oldmaxval,newmaxval) \ + PPM_DEPTH(newp,p,oldmaxval,newmaxval) + +/* Declarations of routines. */ + +void pnm_init ARGS(( int* argcP, char* argv[] )); + +void +pnm_nextimage(FILE *file, int * const eofP); + +xel * +pnm_allocrow(unsigned int const cols); + +#define pnm_freerow(xelrow) free(xelrow) + +#define pnm_allocarray( cols, rows ) \ + ((xel**) pm_allocarray( cols, rows, sizeof(xel) )) +#define pnm_freearray( xels, rows ) pm_freearray( (char**) xels, rows ) + + +void +pnm_readpnminit(FILE * const fileP, + int * const colsP, + int * const rowsP, + xelval * const maxvalP, + int * const formatP); + +void +pnm_readpnmrow(FILE * const fileP, + xel * const xelrow, + int const cols, + xelval const maxval, + int const format); + +xel ** +pnm_readpnm(FILE * const fileP, + int * const colsP, + int * const rowsP, + xelval * const maxvalP, + int * const formatP); + +void +pnm_check(FILE * const fileP, + enum pm_check_type const check_type, + int const format, + int const cols, + int const rows, + int const maxval, + enum pm_check_code * const retvalP); + + +void +pnm_writepnminit(FILE * const fileP, + int const cols, + int const rows, + xelval const maxval, + int const format, + int const forceplain); + +void +pnm_writepnmrow(FILE * const fileP, + xel * const xelrow, + int const cols, + xelval const maxval, + int const format, + int const forceplain); + +void +pnm_writepnm(FILE * const fileP, + xel ** const xels, + int const cols, + int const rows, + xelval const maxval, + int const format, + int const forceplain); + +xel +pnm_backgroundxel (xel** xels, int cols, int rows, xelval maxval, int format); + +xel +pnm_backgroundxelrow (xel* xelrow, int cols, xelval maxval, int format); + +xel +pnm_whitexel (xelval maxval, int format); + +xel +pnm_blackxel(xelval maxval, int format); + +void +pnm_invertxel(xel * const x, + xelval const maxval, + int const format); + +void +pnm_promoteformat(xel** xels, int cols, int rows, xelval maxval, int format, + xelval newmaxval, int newformat); +void +pnm_promoteformatrow(xel* xelrow, int cols, xelval maxval, int format, + xelval newmaxval, int newformat); + +pixel +xeltopixel(xel const inputxel); + +#ifdef __cplusplus +} +#endif + + +#endif /*_PNM_H_*/ diff --git a/lib/ppm.h b/lib/ppm.h new file mode 100644 index 00000000..033330b9 --- /dev/null +++ b/lib/ppm.h @@ -0,0 +1,300 @@ +/* Interface header file for PPM-related functions in libnetpbm */ + +#ifndef _PPM_H_ +#define _PPM_H_ + +#include "pgm.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +typedef gray pixval; + +/* These are just for use in this header file */ +#define PPM_MAX(a,b) ((a) > (b) ? (a) : (b)) +#define PPM_MIN(a,b) ((a) < (b) ? (a) : (b)) + + +#define PPM_OVERALLMAXVAL PGM_OVERALLMAXVAL +#define PPM_MAXMAXVAL PGM_MAXMAXVAL +typedef struct { + pixval r, g, b; +} pixel; +#define PPM_GETR(p) ((p).r) +#define PPM_GETG(p) ((p).g) +#define PPM_GETB(p) ((p).b) + +/************* added definitions *****************/ +#define PPM_PUTR(p,red) ((p).r = (red)) +#define PPM_PUTG(p,grn) ((p).g = (grn)) +#define PPM_PUTB(p,blu) ((p).b = (blu)) +/**************************************************/ + +#define PPM_ASSIGN(p,red,grn,blu) \ + do { (p).r = (red); (p).g = (grn); (p).b = (blu); } while (0) +#define PPM_EQUAL(p,q) \ + ( (p).r == (q).r && (p).g == (q).g && (p).b == (q).b ) + + +/* Magic constants. */ + +#define PPM_MAGIC1 'P' +#define PPM_MAGIC2 '3' +#define RPPM_MAGIC2 '6' +#define PPM_FORMAT (PPM_MAGIC1 * 256 + PPM_MAGIC2) +#define RPPM_FORMAT (PPM_MAGIC1 * 256 + RPPM_MAGIC2) +#define PPM_TYPE PPM_FORMAT + + +#include "ppmcmap.h" + +/* Macro for turning a format number into a type number. */ + +#define PPM_FORMAT_TYPE(f) \ + ((f) == PPM_FORMAT || (f) == RPPM_FORMAT ? PPM_TYPE : PGM_FORMAT_TYPE(f)) + + +/* Declarations of routines. */ + +void ppm_init(int * argcP, char* argv[]); + +#define ppm_allocarray(cols, rows) \ + ((pixel**) pm_allocarray(cols, rows, sizeof(pixel))) + +pixel * +ppm_allocrow(unsigned int const cols); + +#define ppm_freearray(pixels, rows) pm_freearray((char**) pixels, rows) + +#define ppm_freerow(pixelrow) free(pixelrow); + +pixel** +ppm_readppm(FILE * const fileP, + int * const colsP, + int * const rowsP, + pixval * const maxvalP); + +void +ppm_readppminit(FILE * const fileP, + int * const colsP, + int * const rowsP, + pixval * const maxvalP, + int * const formatP); + +void +ppm_readppmrow(FILE* const fileP, + pixel* const pixelrow, + int const cols, + pixval const maxval, + int const format); + +void +ppm_writeppm(FILE * const fileP, + pixel** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const forceplain); + +void +ppm_writeppminit(FILE* const fileP, + int const cols, + int const rows, + pixval const maxval, + int const forceplain); + +void +ppm_writeppmrow(FILE * const fileP, + pixel * const pixelrow, + int const cols, + pixval const maxval, + int const forceplain); + +void +ppm_check(FILE * const fileP, + enum pm_check_type const check_type, + int const format, + int const cols, + int const rows, + pixval const maxval, + enum pm_check_code * const retval_p); + +void +ppm_nextimage(FILE * const fileP, + int * const eofP); + +pixel +ppm_parsecolor(const char * const colorname, + pixval const maxval); + +pixel +ppm_parsecolor2(const char * const colorname, + pixval const maxval, + int const closeOk); + +char* +ppm_colorname(const pixel* const colorP, + pixval const maxval, + int const hexok); + +void +ppm_readcolordict(const char * const fileName, + int const mustOpen, + unsigned int * const nColorsP, + const char *** const colornamesP, + pixel ** const colorsP, + colorhash_table * const chtP); + +void +ppm_readcolornamefile(const char * const fileName, + int const mustOpen, + colorhash_table * const chtP, + const char *** const colornamesP); + +void +ppm_freecolornames(const char ** const colornames); + +#define PPM_ISGRAY(pixel) \ + (PPM_GETR(pixel) == PPM_GETG(pixel) && PPM_GETR(pixel) == PPM_GETB(pixel)) + +/* Color scaling macro -- to make writing ppmtowhatever easier. */ + +#define PPM_DEPTH(newp,p,oldmaxval,newmaxval) \ + PPM_ASSIGN( (newp), \ + ( (int) PPM_GETR(p) * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval), \ + ( (int) PPM_GETG(p) * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval), \ + ( (int) PPM_GETB(p) * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval) ) + +#define PPM_SQR(x) (x)*(x) + +static __inline__ unsigned int +PPM_DISTANCE(pixel const p1, + pixel const p2) { + return ( + PPM_SQR(PPM_GETR(p1)-PPM_GETR(p2)) + + PPM_SQR(PPM_GETG(p1)-PPM_GETG(p2)) + + PPM_SQR(PPM_GETB(p1)-PPM_GETB(p2)) ); +} +#undef PPM_SQR + +/* Note that because a sample can be at most 1 << 16 - 1, PPM_DISTANCE + is less than UINT_MAX. That means you can use UINT_MAX as an infinite + distance in some applications. +*/ + +/* Luminance, Chrominance macros. */ + +/* The following are weights of the red, green, and blue components + respectively in the luminance of a color. Actually, it's "luma," + not luminance, the difference being that luminance would be a linear + combination of intensities, whereas luma is a linear combination of + gamma-adjusted intensities, as you would find in a Netpbm image. + + These are from ITU-R BT.601.5. That means they probably aren't technically + right for use with PPM images, because the PPM spec says ITU-R BT.709. + The two are similar, though. +*/ +#define PPM_LUMINR (0.2989) +#define PPM_LUMING (0.5866) +#define PPM_LUMINB (0.1145) + +#define PPM_LUMIN(p) ( PPM_LUMINR * PPM_GETR(p) \ + + PPM_LUMING * PPM_GETG(p) \ + + PPM_LUMINB * PPM_GETB(p) ) +#define PPM_CHROM_B(p) ( -0.16874 * PPM_GETR(p) \ + - 0.33126 * PPM_GETG(p) \ + + 0.5 * PPM_GETB(p) ) +#define PPM_CHROM_R(p) ( 0.5 * PPM_GETR(p) \ + - 0.41869 * PPM_GETG(p) \ + - 0.08131 * PPM_GETB(p) ) + +pixel +ppm_color_from_ycbcr(unsigned int const y, + int const cb, + int const cr); + +/* Hue/Saturation/Value calculations */ + +struct hsv { + double h; /* hue (degrees) 0..360 */ + double s; /* saturation (0-1) */ + double v; /* value (0-1) */ +}; + +pixel +ppm_color_from_hsv(struct hsv const hsv, + pixval const maxval); + +struct hsv +ppm_hsv_from_color(pixel const color, + pixval const maxval); + +static __inline pixval +ppm_colorvalue(pixel const p) { +/*---------------------------------------------------------------------------- + The color value (V is HSV) as a pixval +-----------------------------------------------------------------------------*/ + return PPM_MAX(PPM_GETR(p), PPM_MAX(PPM_GETG(p), PPM_GETB(p))); +} + +static __inline pixval +ppm_saturation(pixel const p, + pixval const maxval) { +/*---------------------------------------------------------------------------- + The saturation, as a pixval (i.e. if saturation is 50% and maxval + is 100, this is 50). +-----------------------------------------------------------------------------*/ + pixval const maxIntensity = + PPM_MAX(PPM_GETR(p), PPM_MAX(PPM_GETG(p), PPM_GETB(p))); + pixval const minIntensity = + PPM_MIN(PPM_GETR(p), PPM_MIN(PPM_GETG(p), PPM_GETB(p))); + pixval const range = maxIntensity - minIntensity; + + return (pixval)((unsigned long)range * maxval / maxIntensity); +} + +typedef enum { + /* A color from the set of universally understood colors developed + by Brent Berlin and Paul Kay + */ + BKCOLOR_BLACK = 0, + BKCOLOR_GRAY, + BKCOLOR_WHITE, + BKCOLOR_RED, + BKCOLOR_ORANGE, + BKCOLOR_YELLOW, + BKCOLOR_GREEN, + BKCOLOR_BLUE, + BKCOLOR_VIOLET, + BKCOLOR_PURPLE, + BKCOLOR_BROWN +} bk_color; + +#define BKCOLOR_COUNT (BKCOLOR_BROWN+1) + +bk_color +ppm_bk_color_from_color(pixel const color, + pixval const maxval); + +pixel +ppm_color_from_bk_color(bk_color const bkColor, + pixval const maxval); + +bk_color +ppm_bk_color_from_name(const char * const name); + +const char * +ppm_name_from_bk_color(bk_color const bkColor); + +#ifdef __cplusplus +} +#endif + +#undef PPM_MIN +#undef PPM_MAX + +#endif /*_PPM_H_*/ diff --git a/lib/ppmcmap.h b/lib/ppmcmap.h new file mode 100644 index 00000000..b44dcbea --- /dev/null +++ b/lib/ppmcmap.h @@ -0,0 +1,111 @@ +#ifndef PPMCMAP_INCLUDED +#define PPMCMAP_INCLUDED +/* ppmcmap.h - header file for colormap routines in libppm +*/ + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + + +/* Color histogram stuff. */ + +typedef struct colorhist_item* colorhist_vector; +struct colorhist_item { + pixel color; + int value; +}; + +typedef struct colorhist_list_item* colorhist_list; +struct colorhist_list_item { + struct colorhist_item ch; + colorhist_list next; +}; + +colorhist_vector +ppm_computecolorhist( pixel ** const pixels, + const int cols, const int rows, const int maxcolors, + int * const colorsP ); +colorhist_vector +ppm_computecolorhist2(FILE * const ifp, + const int cols, const int rows, + const pixval maxval, const int format, + 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 ); + +void +ppm_freecolorhist(colorhist_vector const chv); + + +/* Color hash table stuff. */ + +typedef colorhist_list* colorhash_table; + +colorhash_table +ppm_computecolorhash( pixel ** const pixels, + const int cols, const int rows, + const int maxcolors, int * const colorsP ); + +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); + +int +ppm_lookupcolor(colorhash_table const cht, + const pixel * const colorP ); + +colorhist_vector +ppm_colorhashtocolorhist(colorhash_table const cht, + int const maxcolors); + +colorhash_table +ppm_colorhisttocolorhash(colorhist_vector const chv, + int const colors); + +int +ppm_addtocolorhash(colorhash_table const cht, + const pixel * const colorP, + int const value); + +void +ppm_delfromcolorhash(colorhash_table const cht, + const pixel * const colorP); + + +colorhash_table +ppm_alloccolorhash(void); + +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)); + +int +ppm_findclosestcolor(const pixel * const colormap, + int const ncolors, + const pixel * const pP); + +/* standard sort function for ppm_sortcolorrow() */ +#define PPM_STDSORT (int (*)(pixel *, pixel *))0 +#endif + +#ifdef __cplusplus +} +#endif diff --git a/lib/ppmdfont.c b/lib/ppmdfont.c new file mode 100644 index 00000000..a378f79c --- /dev/null +++ b/lib/ppmdfont.c @@ -0,0 +1,133 @@ +#include +#include + +#include "pm.h" +#include "mallocvar.h" +#include "ppmdfont.h" + + +extern struct ppmd_font ppmd_standardfont; + +static const struct ppmd_font * currentFontP = &ppmd_standardfont; + +static void +readGlyphHeader(FILE * const ifP, + struct ppmd_glyphHeader * const glyphHeaderP) { + + glyphHeaderP->commandCount = fgetc(ifP); + glyphHeaderP->skipBefore = fgetc(ifP); + glyphHeaderP->skipAfter = fgetc(ifP); +} + + + +static void +readGlyphCommand(FILE * const ifP, + struct ppmd_glyphCommand * const glyphCommandP) { + + glyphCommandP->verb = fgetc(ifP); + glyphCommandP->x = fgetc(ifP); + glyphCommandP->y = fgetc(ifP); +} + + + +static void +readCharacter(FILE * const ifP, + struct ppmd_glyph * const glyphP) { + + unsigned int commandNum; + struct ppmd_glyphCommand * commandList; + + readGlyphHeader(ifP, &glyphP->header); + + MALLOCARRAY(commandList, glyphP->header.commandCount); + + if (commandList == NULL) + pm_error("Insufficient memory to create a %u-command " + "command list.", glyphP->header.commandCount); + + for (commandNum = 0; + commandNum < glyphP->header.commandCount; + ++commandNum) { + + readGlyphCommand(ifP, &commandList[commandNum]); + } + glyphP->commandList = commandList; +} + + + +static void +readFontHeader(FILE * const ifP, + struct ppmd_fontHeader * const fontHeaderP) { + + fread(&fontHeaderP->signature, 1, sizeof(fontHeaderP->signature), ifP); + fontHeaderP->format = fgetc(ifP); + fontHeaderP->characterCount = fgetc(ifP); + fontHeaderP->firstCodePoint = fgetc(ifP); +} + + + +void +ppmd_set_font(const struct ppmd_font * const newFontP) { + + currentFontP = newFontP; +} + + + +const struct ppmd_font * +ppmd_get_font(void) { + + return currentFontP; +} + + + +void +ppmd_read_font(FILE * const ifP, + const struct ppmd_font ** const fontPP) { + + unsigned int relativeCodePoint; + struct ppmd_glyph * glyphTable; + struct ppmd_font * fontP; + + MALLOCVAR(fontP); + if (fontP == NULL) + pm_error("Insufficient memory for font header"); + + readFontHeader(ifP, &fontP->header); + + MALLOCARRAY(glyphTable, fontP->header.characterCount); + if (glyphTable == NULL) + pm_error("Insufficient memory to store %u characters", + fontP->header.characterCount); + + for (relativeCodePoint = 0; + relativeCodePoint < fontP->header.characterCount; + ++relativeCodePoint) { + + readCharacter(ifP, &glyphTable[relativeCodePoint]); + } + fontP->glyphTable = glyphTable; + *fontPP = fontP; +} + + + +void +ppmd_free_font(const struct ppmd_font * const fontP) { + + unsigned int relativeCodePoint; + + for (relativeCodePoint = 0; + relativeCodePoint < fontP->header.characterCount; + ++relativeCodePoint) { + + free((void*)fontP->glyphTable[relativeCodePoint].commandList); + } + free((void*)fontP->glyphTable); + free((void*)fontP); +} diff --git a/lib/ppmdfont.h b/lib/ppmdfont.h new file mode 100644 index 00000000..329386ff --- /dev/null +++ b/lib/ppmdfont.h @@ -0,0 +1,74 @@ +#ifndef PPMDFONT_INCLUDED +#define PPMDFONT_INCLUDED + +#include + +/* A font file has the following format, with proper packing: + + struct ppmd_fontHeader fontHeader; + struct { + struct ppmd_glyphHeader glyphHeader; + struct ppmd_glyphCommand glyphCommand[N]; + } glyph[M] + + Where: + M is fontHeader.characterCount + N is glyphHeader.commandCount + + glyph[i] is the glyph for code point Q, + where i = Q - fontHeader.firstCodePoint + +*/ + +struct ppmd_fontHeader { + char signature[8]; /* "ppmdfont" */ + unsigned char format; /* 0x01 */ + unsigned char characterCount; + /* Number of characters in this font */ + unsigned char firstCodePoint; + /* lowest code point in the font */ +}; + +struct ppmd_glyphHeader { + unsigned char commandCount; + /* Number of struct glyphCommand that follow */ + unsigned char skipBefore; + unsigned char skipAfter; +}; + +enum ppmd_glyphCommandVerb {CMD_NOOP = 0, + CMD_DRAWLINE = 1, + CMD_MOVEPEN = 2 +}; + +struct ppmd_glyphCommand { + enum ppmd_glyphCommandVerb verb; + unsigned char x; + unsigned char y; +}; + +struct ppmd_glyph { + struct ppmd_glyphHeader header; + const struct ppmd_glyphCommand * commandList; +}; + +struct ppmd_font { + struct ppmd_fontHeader header; + const struct ppmd_glyph * glyphTable; +}; + +void +ppmd_set_font(const struct ppmd_font * const newFontP); + +const struct ppmd_font * +ppmd_get_font(void); + +void +ppmd_read_font(FILE * const ifP, + const struct ppmd_font ** const fontPP); + +void +ppmd_free_font(const struct ppmd_font * const fontP); + + +#endif diff --git a/lib/ppmdraw.h b/lib/ppmdraw.h new file mode 100644 index 00000000..9efe51b9 --- /dev/null +++ b/lib/ppmdraw.h @@ -0,0 +1,303 @@ +/* ppmdraw.h - header file for simple drawing routines in libppm +** +** Simple, yes, and also fairly slow if the truth be told; but also very +** flexible and powerful. +** +** The two basic concepts are the drawproc and clientdata. All the drawing +** routines take a drawproc that does the actual drawing. A drawproc draws +** a single point, and it looks like this: +*/ + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + + +typedef enum { + PPMD_PATHLEG_LINE +} ppmd_pathlegtype; + +typedef struct { + unsigned int x; + unsigned int y; +} ppmd_point; + +struct ppmd_linelegparms { + ppmd_point end; +}; + +typedef struct { +/*---------------------------------------------------------------------------- + A leg of a ppmd_path. +-----------------------------------------------------------------------------*/ + ppmd_pathlegtype type; + union { + struct ppmd_linelegparms linelegparms; + } u; +} ppmd_pathleg; + +typedef struct { +/*---------------------------------------------------------------------------- + A closed path +-----------------------------------------------------------------------------*/ + unsigned int version; + /* Must be zero. For future expansion. */ + ppmd_point begPoint; + unsigned int legCount; + /* Number of legs in the path; i.e. size of 'legs' array */ + size_t legSize; + /* Size of storage occupied by one ppmd_pathleg. I.e. + sizeof(ppmd_pathleg). Used for + binary backward compatibility between callers and libppmd + as the definition of ppmd_pathleg changes. + */ + ppmd_pathleg * legs; +} ppmd_path; + + + +typedef void ppmd_drawproc(pixel **, int, int, pixval, int, int, const void *); + +ppmd_drawproc ppmd_point_drawproc; + +/* +** So, you call a drawing routine, e.g. ppmd_line(), and pass it a drawproc; +** it calls the drawproc for each point it wants to draw. Why so complicated? +** Because you can define your own drawprocs to do more interesting things than +** simply draw the point. For example, you could make one that calls back into +** another drawing routine, say ppmd_circle() to draw a circle at each point +** of a line. +** +** Slow? Well sure, we're talking results here, not realtime. You can do +** tricks with this arrangement that you couldn't even think of before. +** Still, to speed things up for the 90% case you can use this: +*/ +#define PPMD_NULLDRAWPROC NULL +/* +** Just like ppmd_point_drawproc() it simply draws the point, but it's done +** inline, and clipping is assumed to be handled at a higher level. +** +** Now, what about clientdata. Well, it's an arbitrary pointer, and can +** mean something different to each different drawproc. For the above two +** drawprocs, clientdata should be a pointer to a pixel holding the color +** to be drawn. Other drawprocs can use it to point to something else, +** e.g. some structure to be modified, or they can ignore it. +*/ + + +/* Outline drawing routines. Lines, splines, circles, etc. */ + +int +ppmd_setlinetype(int const type); + +#define PPMD_LINETYPE_NORMAL 0 +#define PPMD_LINETYPE_NODIAGS 1 +/* If you set NODIAGS, all pixels drawn by ppmd_line() will be 4-connected +** instead of 8-connected; in other words, no diagonals. This is useful +** for some applications, for example when you draw many parallel lines +** and you want them to fit together without gaps. +*/ + +int +ppmd_setlineclip(int const clip); + +#define ppmd_setlineclipping(x) ppmd_setlineclip(x) +/* Normally, ppmd_line() clips to the edges of the pixmap. You can use this +** routine to disable the clipping, for example if you are using a drawproc +** that wants to do its own clipping. +*/ + +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); +/* Draws a line from (x0, y0) to (x1, y1). +*/ + +void +ppmd_spline3(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, + 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_polyspline(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x0, + int const y0, + int const nc, + int * const xc, + int * const yc, + int const x1, + 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. + */ + +void +ppmd_circle(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const cx, + int const cy, + int const radius, + ppmd_drawproc drawProc, + const void * const clientdata); + /* Draws a circle centered at (cx, cy) with the specified radius. */ + + +/* Simple filling routines. */ + +void +ppmd_filledrectangle(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int const x, + int const y, + int const width, + int const height, + ppmd_drawproc drawproc, + const void * const clientdata ); + /* Fills in the rectangle [x, y, width, height]. */ + + +void +ppmd_fill_path(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + ppmd_path * const pathP, + pixel const color); + /* Fills in a closed path. Not much different from ppmd_fill(), + but with a different interface. + */ + + +/* Arbitrary filling routines. With these you can fill any outline that +** you can draw with the outline routines. +*/ + +struct fillobj; + +struct fillobj * +ppmd_fill_create(void); + /* Returns a blank fillhandle. */ + +void +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); + /* Use this drawproc to trace the outline you want filled. Use + the fillhandle as the clientdata. + */ +void +ppmd_fill(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + struct fillobj * const fh, + ppmd_drawproc drawProc, + const void * const clientdata); + +/* Once you've traced the outline, give the fillhandle to this routine to +** do the actual drawing. As usual, it takes a drawproc and clientdata; +** you could define drawprocs to do stipple fills and such. +*/ + +/* 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, + void (*drawprocP)(pixel**, int, int, pixval, int, int, const void*), + 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 + drawproc and clientdata are passed to ppmd_line() which performs the + actual drawing. +*/ + +void +ppmd_text_box(int const height, + int const angle, + const char * const s, + int * const leftP, + int * const topP, + int * const rightP, + int * const bottomP); +/* Calculates the extents box for text drawn by ppm_text with the given + string, size, and orientation. Most extent box calculations should use + an angle specification of zero to calculate the unrotated box enclosing + the text. If you need the extents of rotated text, however, you can + call ppmd_text_box with a nonzero angle. +*/ + +#ifdef __cplusplus +} +#endif diff --git a/lib/ppmfloyd.h b/lib/ppmfloyd.h new file mode 100644 index 00000000..e16ad651 --- /dev/null +++ b/lib/ppmfloyd.h @@ -0,0 +1,67 @@ +/* These declarations were supposed to be in the libfloyd.h file in the ilbm + package, but that file was missing, so I made them up myself. + - Bryan 01.03.10. +*/ + + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +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; + int lefttoright; + int cols; + pixval maxval; + int flags; + pixel *pixrow; + int col_end; + pixval red, green, blue; +}; + +typedef struct ppm_fs_info ppm_fs_info; + +/* Bitmasks for ppm_fs_info.flags */ +#define FS_RANDOMINIT 0x01 +#define FS_ALTERNATE 0x02 + +ppm_fs_info * +ppm_fs_init(int cols, pixval maxval, int flags); + +void +ppm_fs_free(ppm_fs_info *fi); + +int +ppm_fs_startrow(ppm_fs_info *fi, pixel *pixrow); + +int +ppm_fs_next(ppm_fs_info *fi, int col); + +void +ppm_fs_endrow(ppm_fs_info *fi); + +void +ppm_fs_update( ppm_fs_info *fi, int col, pixel *pP); + + +void +ppm_fs_update3(ppm_fs_info * const fi, + int const col, + pixval const r, + pixval const g, + pixval const b); + +#ifdef __cplusplus +} +#endif diff --git a/lib/rgb.txt b/lib/rgb.txt new file mode 100644 index 00000000..ad4d9d87 --- /dev/null +++ b/lib/rgb.txt @@ -0,0 +1,881 @@ +# The colors in this color dictionary are from the following sources, in +# order. Some color names are defined multiple times, and sometimes +# with different colors. Many colors have multiple names. + +# - Netpbm originals +# - Crayola crayons, as determined by John Thomas at Tektronix +# - Hollasch at Microsoft Research +# - HTML 4.0 +# - Some HTML extension that Internet Explorer understands +# - XFree86 rgb.txt ca. 2001, derived from MIT X11 + +# More details on the sources are above each group. + +# The preferred form of names is The X Window System standard, all one +# word with the initial letter of each word capitalized. But programs that +# look up in this dictionary should convert the target color name and each +# name in this dictionary to all lower case and remove spaces before +# comparing. +# +# Numbered colors (e.g. blue4) are deprecated, but many are here for +# compatibility with X. + +# These are the 72 colors from Crayola crayons, as determined by +# John Thomas at Tektronix +# (taken from http://www.swiss.ai.mit.edu/~jaffer/Color/thomas.txt on +# 2002.09.03, with all the grayN removed). + + 0 0 0 Black +255 255 255 White +255 0 0 Red + 0 255 0 Green + 0 0 255 Blue + 0 255 255 Cyan +255 0 211 Magenta +255 255 0 Yellow +255 138 0 Orange +159 211 0 GreenYellow + 0 255 159 Spring Green + 0 138 255 SkyBlue +148 0 211 Violet +255 0 148 VioletRed +105 105 105 DimGray +174 174 174 Gray +174 174 174 Grey +211 211 211 LightGrey +211 211 211 LightGray +105 105 105 DimGrey +199 21 133 MediumVioletRed +114 33 188 BlueViolet +218 107 212 Orchid +172 77 166 MediumOrchid +106 37 102 DarkOrchid +103 7 72 Maroon + 76 46 87 Plum +146 62 112 Thistle +171 197 255 LightBlue + 61 98 208 MediumBlue +100 149 237 CornflowerBlue + 0 0 142 NavyBlue + 0 0 142 Navy + 12 62 99 MidnightBlue + 72 209 204 Turquoise + 62 172 181 MediumTurquoise + 29 111 117 DarkTurquoise + 52 152 202 LightSteelBlue + 55 121 153 SteelBlue +126 125 160 CadetBlue +117 134 190 SlateBlue + 95 109 154 MediumSlateBlue + 51 62 99 DarkSlateBlue + 60 64 74 DarkSlateGrey + 60 64 74 DarkSlateGray + 0 83 0 DarkGreen + 79 79 47 DarkOliveGreen + 85 192 52 ForestGreen +107 142 35 MediumForestGreen + 46 155 28 LimeGreen + 60 141 35 MediumSpringGreen +152 255 152 PaleGreen + 43 167 112 SeaGreen + 27 134 86 MediumSeaGreen + 41 171 151 Aquamarine + 21 135 118 MediumAquamarine + 75 211 0 YellowGreen +254 197 68 Gold +184 134 11 MediumGoldenrod +218 165 32 Goldenrod +229 199 117 Wheat +189 167 107 Khaki +176 155 125 Tan +178 143 86 SandyBrown +142 107 35 Sienna +103 67 0 Brown +101 46 46 IndianRed +255 174 185 Pink +248 137 117 Coral +248 109 104 Salmon +226 65 42 OrangeRed +136 18 13 Firebrick + +# The following 192 colors are from +# http://www.research.microsoft.com/~hollasch/cgindex/color/colors.txt +# on 2002.09.02. See examples of them at +# http://www.swiss.ai.mit.edu/~jaffer/Color/hollasch.pdf + +250 235 215 AntiqueWhite +240 255 255 Azure +255 228 196 Bisque +255 235 205 BlanchedAlmond +255 248 220 Cornsilk +252 230 201 Eggshell +255 250 240 FloralWhite +220 220 220 Gainsboro +248 248 255 GhostWhite +240 255 240 Honeydew +255 255 240 Ivory +230 230 250 Lavender +255 240 245 LavenderBlush +255 250 205 LemonChiffon +250 240 230 Linen +245 255 250 MintCream +255 228 225 MistyRose +255 228 181 Moccasin +255 222 173 NavajoWhite +253 245 230 OldLace +255 239 213 PapayaWhip +255 218 185 PeachPuff +255 245 238 Seashell +255 250 250 Snow +216 191 216 Thistle +252 255 240 TitaniumWhite +245 222 179 Wheat +255 255 255 White +245 245 245 WhiteSmoke +253 248 255 ZincWhite +128 138 135 ColdGrey +105 105 105 DimGrey +192 192 192 Grey +211 211 211 LightGrey +112 128 144 SlateGrey + 47 79 79 SlateGreyDark +119 136 153 SlateGreyLight +128 128 105 WarmGrey + 0 0 0 Black + 41 36 33 IvoryBlack + 46 71 59 Lamp Black +227 38 54 AlizarinCrimson +156 102 31 Brick +227 23 13 CadmiumRedDeep +255 127 80 Coral +240 128 128 CoralLight +255 20 147 DeepPink +212 61 26 EnglishRed +178 34 34 Firebrick +227 18 48 GeraniumLake +255 105 180 HotPink +176 23 31 IndianRed +255 160 122 LightSalmon +227 46 48 MadderLakeDeep +176 48 96 Maroon +255 192 203 Pink +255 182 193 PinkLight +135 38 87 Raspberry +255 0 0 Red +227 54 56 RoseMadder +250 128 114 Salmon +255 99 71 Tomato +212 26 31 VenetianRed +163 148 128 Beige +128 42 42 Brown +219 41 41 BrownMadder +135 66 31 BrownOchre +222 184 135 Burlywood +138 54 15 BurntSienna +138 51 36 BurntUmber +210 105 30 Chocolate +115 61 26 DeepOchre +255 125 64 Flesh +255 87 33 FleshOchre +199 120 38 GoldOchre +255 61 13 GreenishUmber +240 230 140 Khaki +189 183 107 KhakiDark +245 245 220 LightBeige +205 133 63 Peru +188 143 143 RosyBrown +199 97 20 RawSienna +115 74 18 RawUmber + 94 38 18 Sepia +160 82 45 Sienna +139 69 19 SaddleBrown +244 164 96 SandyBrown +210 180 140 Tan + 94 38 5 VanDykeBrown +255 97 3 CadmiumOrange +255 3 13 CadmiumRedLight +237 145 33 Carrot +255 140 0 DarkOrange +150 69 20 MarsOrange +227 112 26 MarsYellow +255 128 0 Orange +255 69 0 OrangeRed +227 130 23 YellowOchre +255 168 36 AureolineYellow +227 207 87 Banana +255 227 3 CadmiumLemon +255 153 18 CadmiumYellow +255 176 15 CadmiumYellowLight +255 215 0 Gold +218 165 32 Goldenrod +184 134 11 GoldenrodDark +250 250 210 GoldenrodLight +238 232 170 GoldenrodPale +238 221 130 LightGoldenrod +227 168 105 Melon +255 168 18 NaplesYellowDeep +255 255 0 Yellow +255 255 224 YellowLight +127 255 0 Chartreuse +102 128 20 ChromeOxideGreen + 97 179 41 CinnabarGreen + 61 145 64 CobaltGreen + 0 201 87 EmeraldGreen + 34 139 34 ForestGreen + 0 255 0 Green + 0 100 0 GreenDark +152 251 152 GreenPale +173 255 47 GreenYellow +124 252 0 LawnGreen + 50 205 50 LimeGreen +189 252 201 Mint + 59 94 43 Olive +107 142 35 OliveDrab + 85 107 47 OliveGreenDark + 10 201 43 PermanentGreen + 48 128 20 SapGreen + 46 139 87 SeaGreen +143 188 143 SeaGreenDark + 60 179 113 SeaGreenMedium + 32 178 170 SeaGreenLight + 0 255 127 SpringGreen + 0 250 154 SpringGreenMedium + 56 94 15 TerreVerte +110 255 112 ViridianLight +154 205 50 YellowGreen +127 255 212 Aquamarine +102 205 170 AquamarineMedium + 0 255 255 Cyan +224 255 255 CyanWhite + 64 224 208 Turquoise + 0 206 209 TurquoiseDark + 72 209 204 TurquoiseMedium +175 238 238 TurquoisePale +240 248 255 AliceBlue + 0 0 255 Blue +173 216 230 BlueLight + 0 0 205 BlueMedium + 95 158 160 Cadet + 61 89 171 Cobalt +100 149 237 Cornflower + 5 184 204 Cerulean + 30 144 255 DodgerBlue + 46 8 84 Indigo + 3 168 158 ManganeseBlue + 25 25 112 MidnightBlue + 0 0 128 Navy + 51 161 201 Peacock +176 224 230 PowderBlue + 65 105 225 RoyalBlue +106 90 205 SlateBlue + 72 61 139 SlateBlueDark +132 112 255 SlateBlueLight +123 104 238 SlateBlueMedium +135 206 235 SkyBlue + 0 191 255 SkyBlueDeep +135 206 250 SkyBlueLight + 70 130 180 SteelBlue +176 196 222 SteelBlueLight + 0 199 140 TurquoiseBlue + 18 10 143 Ultramarine +138 43 226 BlueViolet +145 33 158 CobaltVioletDeep +255 0 255 Magenta +218 112 214 Orchid +153 50 204 OrchidDark +186 85 211 OrchidMedium +219 38 69 PermanentRedViolet +221 160 221 Plum +160 32 240 Purple +147 112 219 PurpleMedium + 92 36 110 UltramarineViolet +143 94 153 Violet +148 0 211 VioletDark +208 32 144 VioletRed +199 21 133 VioletRedMedium +219 112 147 VioletRedPale +# +# From HTML 4.0 specification. These are colors you can specify by name +# in HTML tags. +# +# Actually, these are the colors as if HTML's sRGB values were actually +# Rec. 709. E.g. HTML 4.0 says silver is r=192, g=192, b=192, in the +# SRGB system. +# This file, which by definition contains Rec. 709 values, shows the same +# three numbers (192,192,192). So this file defines a slightly different +# color than HTML does. +# +# There's some extension to HTML, apparently not blessed by W3C, that adds +# all the MIT colors (see above). Internet Explorer can render all of them. + +000 000 000 Black +192 192 192 Silver +128 128 128 Gray +255 255 255 White +128 000 000 Maroon +255 000 000 Red +128 000 128 Purple +255 000 255 Fuchsia +000 128 000 Green +000 255 000 Lime +128 128 000 Olive +255 255 000 Yellow +000 000 128 Navy +000 000 255 Blue +000 128 128 Teal +000 255 255 Aqua + +# These are from XFree86 rgb.txt ca. 2001. We have extracted the color names +# that aren't already included above and also converted all the names to the +# standard form and eliminated duplicates. +# +# The original file started with this line: +# +#! $XConsortium: rgb.txt,v 10.41 94/02/20 18:39:36 rws Exp $ +112 128 144 SlateGray +119 136 153 LightSlateGray +119 136 153 LightSlateGrey +211 211 211 light grey +100 149 237 CornflowerBlue +132 112 255 LightSlateBlue + 0 191 255 DeepSkyBlue +135 206 250 LightSkyBlue +175 238 238 PaleTurquoise +224 255 255 LightCyan +143 188 143 DarkSeaGreen + 32 178 170 LightSeaGreen +189 183 107 DarkKhaki +238 232 170 PaleGoldenrod +250 250 210 LightGoldenrodYellow +255 255 224 LightYellow +233 150 122 DarkSalmon +240 128 128 LightCoral +255 182 193 LightPink +219 112 147 PaleVioletRed +148 0 211 DarkViolet +147 112 219 MediumPurple +255 250 250 Snow1 +238 233 233 Snow2 +205 201 201 Snow3 +139 137 137 Snow4 +255 245 238 Seashell1 +238 229 222 Seashell2 +205 197 191 Seashell3 +139 134 130 Seashell4 +255 239 219 AntiqueWhite1 +238 223 204 AntiqueWhite2 +205 192 176 AntiqueWhite3 +139 131 120 AntiqueWhite4 +255 228 196 Bisque1 +238 213 183 Bisque2 +205 183 158 Bisque3 +139 125 107 Bisque4 +255 218 185 PeachPuff1 +238 203 173 PeachPuff2 +205 175 149 PeachPuff3 +139 119 101 PeachPuff4 +255 222 173 NavajoWhite1 +238 207 161 NavajoWhite2 +205 179 139 NavajoWhite3 +139 121 94 NavajoWhite4 +255 250 205 LemonChiffon1 +238 233 191 LemonChiffon2 +205 201 165 LemonChiffon3 +139 137 112 LemonChiffon4 +255 248 220 Cornsilk1 +238 232 205 Cornsilk2 +205 200 177 Cornsilk3 +139 136 120 Cornsilk4 +255 255 240 Ivory1 +238 238 224 Ivory2 +205 205 193 Ivory3 +139 139 131 Ivory4 +240 255 240 Honeydew1 +224 238 224 Honeydew2 +193 205 193 Honeydew3 +131 139 131 Honeydew4 +255 240 245 LavenderBlush1 +238 224 229 LavenderBlush2 +205 193 197 LavenderBlush3 +139 131 134 LavenderBlush4 +255 228 225 MistyRose1 +238 213 210 MistyRose2 +205 183 181 MistyRose3 +139 125 123 MistyRose4 +240 255 255 Azure1 +224 238 238 Azure2 +193 205 205 Azure3 +131 139 139 Azure4 +131 111 255 SlateBlue1 +122 103 238 SlateBlue2 +105 89 205 SlateBlue3 + 71 60 139 SlateBlue4 + 72 118 255 RoyalBlue1 + 67 110 238 RoyalBlue2 + 58 95 205 RoyalBlue3 + 39 64 139 RoyalBlue4 + 0 0 255 Blue1 + 0 0 238 Blue2 + 0 0 205 Blue3 + 0 0 139 Blue4 + 30 144 255 DodgerBlue1 + 28 134 238 DodgerBlue2 + 24 116 205 DodgerBlue3 + 16 78 139 DodgerBlue4 + 99 184 255 SteelBlue1 + 92 172 238 SteelBlue2 + 79 148 205 SteelBlue3 + 54 100 139 SteelBlue4 + 0 191 255 DeepSkyBlue1 + 0 178 238 DeepSkyBlue2 + 0 154 205 DeepSkyBlue3 + 0 104 139 DeepSkyBlue4 +135 206 255 SkyBlue1 +126 192 238 SkyBlue2 +108 166 205 SkyBlue3 + 74 112 139 SkyBlue4 +176 226 255 LightSkyBlue1 +164 211 238 LightSkyBlue2 +141 182 205 LightSkyBlue3 + 96 123 139 LightSkyBlue4 +198 226 255 SlateGray1 +185 211 238 SlateGray2 +159 182 205 SlateGray3 +108 123 139 SlateGray4 +202 225 255 LightSteelBlue1 +188 210 238 LightSteelBlue2 +162 181 205 LightSteelBlue3 +110 123 139 LightSteelBlue4 +191 239 255 LightBlue1 +178 223 238 LightBlue2 +154 192 205 LightBlue3 +104 131 139 LightBlue4 +224 255 255 LightCyan1 +209 238 238 LightCyan2 +180 205 205 LightCyan3 +122 139 139 LightCyan4 +187 255 255 PaleTurquoise1 +174 238 238 PaleTurquoise2 +150 205 205 PaleTurquoise3 +102 139 139 PaleTurquoise4 +152 245 255 CadetBlue1 +142 229 238 CadetBlue2 +122 197 205 CadetBlue3 + 83 134 139 CadetBlue4 + 0 245 255 Turquoise1 + 0 229 238 Turquoise2 + 0 197 205 Turquoise3 + 0 134 139 Turquoise4 + 0 255 255 Cyan1 + 0 238 238 Cyan2 + 0 205 205 Cyan3 + 0 139 139 Cyan4 +151 255 255 DarkSlateGray1 +141 238 238 DarkSlateGray2 +121 205 205 DarkSlateGray3 + 82 139 139 DarkSlateGray4 +127 255 212 Aquamarine1 +118 238 198 Aquamarine2 +102 205 170 Aquamarine3 + 69 139 116 Aquamarine4 +193 255 193 DarkSeaGreen1 +180 238 180 DarkSeaGreen2 +155 205 155 DarkSeaGreen3 +105 139 105 DarkSeaGreen4 + 84 255 159 SeaGreen1 + 78 238 148 SeaGreen2 + 67 205 128 SeaGreen3 + 46 139 87 SeaGreen4 +154 255 154 PaleGreen1 +144 238 144 PaleGreen2 +124 205 124 PaleGreen3 + 84 139 84 PaleGreen4 + 0 255 127 SpringGreen1 + 0 238 118 SpringGreen2 + 0 205 102 SpringGreen3 + 0 139 69 SpringGreen4 + 0 255 0 Green1 + 0 238 0 Green2 + 0 205 0 Green3 + 0 139 0 Green4 +127 255 0 Chartreuse1 +118 238 0 Chartreuse2 +102 205 0 Chartreuse3 + 69 139 0 Chartreuse4 +192 255 62 OliveDrab1 +179 238 58 OliveDrab2 +154 205 50 OliveDrab3 +105 139 34 OliveDrab4 +202 255 112 DarkOliveGreen1 +188 238 104 DarkOliveGreen2 +162 205 90 DarkOliveGreen3 +110 139 61 DarkOliveGreen4 +255 246 143 Khaki1 +238 230 133 Khaki2 +205 198 115 Khaki3 +139 134 78 Khaki4 +255 236 139 LightGoldenrod1 +238 220 130 LightGoldenrod2 +205 190 112 LightGoldenrod3 +139 129 76 LightGoldenrod4 +255 255 224 LightYellow1 +238 238 209 LightYellow2 +205 205 180 LightYellow3 +139 139 122 LightYellow4 +255 255 0 Yellow1 +238 238 0 Yellow2 +205 205 0 Yellow3 +139 139 0 Yellow4 +255 215 0 Gold1 +238 201 0 Gold2 +205 173 0 Gold3 +139 117 0 Gold4 +255 193 37 Goldenrod1 +238 180 34 Goldenrod2 +205 155 29 Goldenrod3 +139 105 20 Goldenrod4 +255 185 15 DarkGoldenrod1 +238 173 14 DarkGoldenrod2 +205 149 12 DarkGoldenrod3 +139 101 8 DarkGoldenrod4 +255 193 193 RosyBrown1 +238 180 180 RosyBrown2 +205 155 155 RosyBrown3 +139 105 105 RosyBrown4 +255 106 106 IndianRed1 +238 99 99 IndianRed2 +205 85 85 IndianRed3 +139 58 58 IndianRed4 +255 130 71 Sienna1 +238 121 66 Sienna2 +205 104 57 Sienna3 +139 71 38 Sienna4 +255 211 155 Burlywood1 +238 197 145 Burlywood2 +205 170 125 Burlywood3 +139 115 85 Burlywood4 +255 231 186 Wheat1 +238 216 174 Wheat2 +205 186 150 Wheat3 +139 126 102 Wheat4 +255 165 79 Tan1 +238 154 73 Tan2 +205 133 63 Tan3 +139 90 43 Tan4 +255 127 36 Chocolate1 +238 118 33 Chocolate2 +205 102 29 Chocolate3 +139 69 19 Chocolate4 +255 48 48 Firebrick1 +238 44 44 Firebrick2 +205 38 38 Firebrick3 +139 26 26 Firebrick4 +255 64 64 Brown1 +238 59 59 Brown2 +205 51 51 Brown3 +139 35 35 Brown4 +255 140 105 Salmon1 +238 130 98 Salmon2 +205 112 84 Salmon3 +139 76 57 Salmon4 +255 160 122 LightSalmon1 +238 149 114 LightSalmon2 +205 129 98 LightSalmon3 +139 87 66 LightSalmon4 +255 165 0 Orange1 +238 154 0 Orange2 +205 133 0 Orange3 +139 90 0 Orange4 +255 127 0 DarkOrange1 +238 118 0 DarkOrange2 +205 102 0 DarkOrange3 +139 69 0 DarkOrange4 +255 114 86 Coral1 +238 106 80 Coral2 +205 91 69 Coral3 +139 62 47 Coral4 +255 99 71 Tomato1 +238 92 66 Tomato2 +205 79 57 Tomato3 +139 54 38 Tomato4 +255 69 0 OrangeRed1 +238 64 0 OrangeRed2 +205 55 0 OrangeRed3 +139 37 0 OrangeRed4 +255 0 0 Red1 +238 0 0 Red2 +205 0 0 Red3 +139 0 0 Red4 +255 20 147 DeepPink1 +238 18 137 DeepPink2 +205 16 118 DeepPink3 +139 10 80 DeepPink4 +255 110 180 HotPink1 +238 106 167 HotPink2 +205 96 144 HotPink3 +139 58 98 HotPink4 +255 181 197 Pink1 +238 169 184 Pink2 +205 145 158 Pink3 +139 99 108 Pink4 +255 174 185 LightPink1 +238 162 173 LightPink2 +205 140 149 LightPink3 +139 95 101 LightPink4 +255 130 171 PaleVioletRed1 +238 121 159 PaleVioletRed2 +205 104 137 PaleVioletRed3 +139 71 93 PaleVioletRed4 +255 52 179 Maroon1 +238 48 167 Maroon2 +205 41 144 Maroon3 +139 28 98 Maroon4 +255 62 150 VioletRed1 +238 58 140 VioletRed2 +205 50 120 VioletRed3 +139 34 82 VioletRed4 +255 0 255 Magenta1 +238 0 238 Magenta2 +205 0 205 Magenta3 +139 0 139 Magenta4 +255 131 250 Orchid1 +238 122 233 Orchid2 +205 105 201 Orchid3 +139 71 137 Orchid4 +255 187 255 Plum1 +238 174 238 Plum2 +205 150 205 Plum3 +139 102 139 Plum4 +224 102 255 MediumOrchid1 +209 95 238 MediumOrchid2 +180 82 205 MediumOrchid3 +122 55 139 MediumOrchid4 +191 62 255 DarkOrchid1 +178 58 238 DarkOrchid2 +154 50 205 DarkOrchid3 +104 34 139 DarkOrchid4 +155 48 255 Purple1 +145 44 238 Purple2 +125 38 205 Purple3 + 85 26 139 Purple4 +171 130 255 MediumPurple1 +159 121 238 MediumPurple2 +137 104 205 MediumPurple3 + 93 71 139 MediumPurple4 +255 225 255 Thistle1 +238 210 238 Thistle2 +205 181 205 Thistle3 +139 123 139 Thistle4 + 0 0 0 Gray0 + 0 0 0 Grey0 + 3 3 3 Gray1 + 3 3 3 Grey1 + 5 5 5 Gray2 + 5 5 5 Grey2 + 8 8 8 Gray3 + 8 8 8 Grey3 + 10 10 10 Gray4 + 10 10 10 Grey4 + 13 13 13 Gray5 + 13 13 13 Grey5 + 15 15 15 Gray6 + 15 15 15 Grey6 + 18 18 18 Gray7 + 18 18 18 Grey7 + 20 20 20 Gray8 + 20 20 20 Grey8 + 23 23 23 Gray9 + 23 23 23 Grey9 + 26 26 26 Gray10 + 26 26 26 Grey10 + 28 28 28 Gray11 + 28 28 28 Grey11 + 31 31 31 Gray12 + 31 31 31 Grey12 + 33 33 33 Gray13 + 33 33 33 Grey13 + 36 36 36 Gray14 + 36 36 36 Grey14 + 38 38 38 Gray15 + 38 38 38 Grey15 + 41 41 41 Gray16 + 41 41 41 Grey16 + 43 43 43 Gray17 + 43 43 43 Grey17 + 46 46 46 Gray18 + 46 46 46 Grey18 + 48 48 48 Gray19 + 48 48 48 Grey19 + 51 51 51 Gray20 + 51 51 51 Grey20 + 54 54 54 Gray21 + 54 54 54 Grey21 + 56 56 56 Gray22 + 56 56 56 Grey22 + 59 59 59 Gray23 + 59 59 59 Grey23 + 61 61 61 Gray24 + 61 61 61 Grey24 + 64 64 64 Gray25 + 64 64 64 Grey25 + 66 66 66 Gray26 + 66 66 66 Grey26 + 69 69 69 Gray27 + 69 69 69 Grey27 + 71 71 71 Gray28 + 71 71 71 Grey28 + 74 74 74 Gray29 + 74 74 74 Grey29 + 77 77 77 Gray30 + 77 77 77 Grey30 + 79 79 79 Gray31 + 79 79 79 Grey31 + 82 82 82 Gray32 + 82 82 82 Grey32 + 84 84 84 Gray33 + 84 84 84 Grey33 + 87 87 87 Gray34 + 87 87 87 Grey34 + 89 89 89 Gray35 + 89 89 89 Grey35 + 92 92 92 Gray36 + 92 92 92 Grey36 + 94 94 94 Gray37 + 94 94 94 Grey37 + 97 97 97 Gray38 + 97 97 97 Grey38 + 99 99 99 Gray39 + 99 99 99 Grey39 +102 102 102 Gray40 +102 102 102 Grey40 +105 105 105 Gray41 +105 105 105 Grey41 +107 107 107 Gray42 +107 107 107 Grey42 +110 110 110 Gray43 +110 110 110 Grey43 +112 112 112 Gray44 +112 112 112 Grey44 +115 115 115 Gray45 +115 115 115 Grey45 +117 117 117 Gray46 +117 117 117 Grey46 +120 120 120 Gray47 +120 120 120 Grey47 +122 122 122 Gray48 +122 122 122 Grey48 +125 125 125 Gray49 +125 125 125 Grey49 +127 127 127 Gray50 +127 127 127 Grey50 +130 130 130 Gray51 +130 130 130 Grey51 +133 133 133 Gray52 +133 133 133 Grey52 +135 135 135 Gray53 +135 135 135 Grey53 +138 138 138 Gray54 +138 138 138 Grey54 +140 140 140 Gray55 +140 140 140 Grey55 +143 143 143 Gray56 +143 143 143 Grey56 +145 145 145 Gray57 +145 145 145 Grey57 +148 148 148 Gray58 +148 148 148 Grey58 +150 150 150 Gray59 +150 150 150 Grey59 +153 153 153 Gray60 +153 153 153 Grey60 +156 156 156 Gray61 +156 156 156 Grey61 +158 158 158 Gray62 +158 158 158 Grey62 +161 161 161 Gray63 +161 161 161 Grey63 +163 163 163 Gray64 +163 163 163 Grey64 +166 166 166 Gray65 +166 166 166 Grey65 +168 168 168 Gray66 +168 168 168 Grey66 +171 171 171 Gray67 +171 171 171 Grey67 +173 173 173 Gray68 +173 173 173 Grey68 +176 176 176 Gray69 +176 176 176 Grey69 +179 179 179 Gray70 +179 179 179 Grey70 +181 181 181 Gray71 +181 181 181 Grey71 +184 184 184 Gray72 +184 184 184 Grey72 +186 186 186 Gray73 +186 186 186 Grey73 +189 189 189 Gray74 +189 189 189 Grey74 +191 191 191 Gray75 +191 191 191 Grey75 +194 194 194 Gray76 +194 194 194 Grey76 +196 196 196 Gray77 +196 196 196 Grey77 +199 199 199 Gray78 +199 199 199 Grey78 +201 201 201 Gray79 +201 201 201 Grey79 +204 204 204 Gray80 +204 204 204 Grey80 +207 207 207 Gray81 +207 207 207 Grey81 +209 209 209 Gray82 +209 209 209 Grey82 +212 212 212 Gray83 +212 212 212 Grey83 +214 214 214 Gray84 +214 214 214 Grey84 +217 217 217 Gray85 +217 217 217 Grey85 +219 219 219 Gray86 +219 219 219 Grey86 +222 222 222 Gray87 +222 222 222 Grey87 +224 224 224 Gray88 +224 224 224 Grey88 +227 227 227 Gray89 +227 227 227 Grey89 +229 229 229 Gray90 +229 229 229 Grey90 +232 232 232 Gray91 +232 232 232 Grey91 +235 235 235 Gray92 +235 235 235 Grey92 +237 237 237 Gray93 +237 237 237 Grey93 +240 240 240 Gray94 +240 240 240 Grey94 +242 242 242 Gray95 +242 242 242 Grey95 +245 245 245 Gray96 +245 245 245 Grey96 +247 247 247 Gray97 +247 247 247 Grey97 +250 250 250 Gray98 +250 250 250 Grey98 +252 252 252 Gray99 +252 252 252 Grey99 +255 255 255 Gray100 +255 255 255 Grey100 +169 169 169 DarkGrey +169 169 169 DarkGray +0 0 139 DarkBlue +0 139 139 DarkCyan +139 0 139 DarkMagenta +139 0 0 DarkRed +144 238 144 LightGreen + + +# These were more or less invented for use with Netpbm: +255 255 255 D65 diff --git a/lib/standard.ppmdfont b/lib/standard.ppmdfont new file mode 100644 index 00000000..124e1d73 Binary files /dev/null and b/lib/standard.ppmdfont differ diff --git a/lib/standardppmdfont.c b/lib/standardppmdfont.c new file mode 100644 index 00000000..aa4707e2 --- /dev/null +++ b/lib/standardppmdfont.c @@ -0,0 +1,3177 @@ +/* THIS FILE WAS GENERATED BY 'ppmdcfont' from a ppmfont file. */ + +#include "ppmdfont.h" + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_33[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 2 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 7 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 7 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_34[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 243 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 249 } + , + {/* glyphCommand */ CMD_MOVEPEN, 10, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 243 } + , + {/* glyphCommand */ CMD_DRAWLINE, 10, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 10, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 249 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_35[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 1, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 16 } + , + {/* glyphCommand */ CMD_MOVEPEN, 7, 240 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 16 } + , + {/* glyphCommand */ CMD_MOVEPEN, 250, 253 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 253 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 3 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 3 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_36[26] = { + {/* glyphCommand */ CMD_MOVEPEN, 254, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 13 } + , + {/* glyphCommand */ CMD_MOVEPEN, 2, 240 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 13 } + , + {/* glyphCommand */ CMD_MOVEPEN, 7, 247 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_37[31] = { + {/* glyphCommand */ CMD_MOVEPEN, 9, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 252, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 244 } + , + {/* glyphCommand */ CMD_MOVEPEN, 5, 2 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 2 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_38[34] = { + {/* glyphCommand */ CMD_MOVEPEN, 10, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 10, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 246, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 246, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 10, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 10, 7 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_39[7] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 250 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_40[10] = { + {/* glyphCommand */ CMD_MOVEPEN, 4, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 242 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 11 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 14 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_41[10] = { + {/* glyphCommand */ CMD_MOVEPEN, 252, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 242 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 11 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 14 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_42[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 6 } + , + {/* glyphCommand */ CMD_MOVEPEN, 251, 253 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 3 } + , + {/* glyphCommand */ CMD_MOVEPEN, 5, 253 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 3 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_43[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 247, 0 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 0 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_44[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 10 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 12 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 13 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_45[2] = { + {/* glyphCommand */ CMD_MOVEPEN, 247, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 0 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_46[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 7 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_47[2] = { + {/* glyphCommand */ CMD_MOVEPEN, 9, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_48[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 255, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 244 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_49[4] = { + {/* glyphCommand */ CMD_MOVEPEN, 252, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_50[14] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_51[15] = { + {/* glyphCommand */ CMD_MOVEPEN, 251, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 5 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_52[6] = { + {/* glyphCommand */ CMD_MOVEPEN, 3, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 2 } + , + {/* glyphCommand */ CMD_MOVEPEN, 3, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_53[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 5, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 5 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_54[23] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 2 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_55[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 7, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 244 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_56[29] = { + {/* glyphCommand */ CMD_MOVEPEN, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_57[23] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_58[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 251 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 7 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 7 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_59[14] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 251 } + , + {/* glyphCommand */ CMD_MOVEPEN, 1, 8 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 10 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 12 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 13 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_60[3] = { + {/* glyphCommand */ CMD_MOVEPEN, 8, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_61[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 247, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 253 } + , + {/* glyphCommand */ CMD_MOVEPEN, 247, 3 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 3 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_62[3] = { + {/* glyphCommand */ CMD_MOVEPEN, 248, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_63[20] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 2 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 7 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 7 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_64[55] = { + {/* glyphCommand */ CMD_MOVEPEN, 5, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 1 } + , + {/* glyphCommand */ CMD_MOVEPEN, 255, 249 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 4 } + , + {/* glyphCommand */ CMD_MOVEPEN, 5, 249 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 10, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 11, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 11, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 10, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 9, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 246, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 246, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 6 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 249 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 4 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_65[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 251, 2 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 2 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_66[23] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 254 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_67[18] = { + {/* glyphCommand */ CMD_MOVEPEN, 8, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 4 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_68[15] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_69[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 250, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 244 } + , + {/* glyphCommand */ CMD_MOVEPEN, 250, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 254 } + , + {/* glyphCommand */ CMD_MOVEPEN, 250, 9 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_70[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 250, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 244 } + , + {/* glyphCommand */ CMD_MOVEPEN, 250, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 254 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_71[22] = { + {/* glyphCommand */ CMD_MOVEPEN, 8, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 1 } + , + {/* glyphCommand */ CMD_MOVEPEN, 3, 1 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 1 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_72[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 7, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 254 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_73[2] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_74[10] = { + {/* glyphCommand */ CMD_MOVEPEN, 4, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 2 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_75[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 7, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 2 } + , + {/* glyphCommand */ CMD_MOVEPEN, 254, 253 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_76[3] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_77[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 248, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 248, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 8, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 8, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_78[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 7, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_79[21] = { + {/* glyphCommand */ CMD_MOVEPEN, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_80[13] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 255 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_81[24] = { + {/* glyphCommand */ CMD_MOVEPEN, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } + , + {/* glyphCommand */ CMD_MOVEPEN, 1, 5 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 11 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_82[16] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 254 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_83[20] = { + {/* glyphCommand */ CMD_MOVEPEN, 7, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_84[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 244 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_85[10] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 244 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_86[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 248, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 8, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_87[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 246, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 10, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_88[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 7, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_89[6] = { + {/* glyphCommand */ CMD_MOVEPEN, 248, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 8, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 254 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_90[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 7, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 244 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 244 } + , + {/* glyphCommand */ CMD_MOVEPEN, 249, 9 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_91[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 253, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 16 } + , + {/* glyphCommand */ CMD_MOVEPEN, 254, 240 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 16 } + , + {/* glyphCommand */ CMD_MOVEPEN, 253, 240 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 240 } + , + {/* glyphCommand */ CMD_MOVEPEN, 253, 16 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_92[2] = { + {/* glyphCommand */ CMD_MOVEPEN, 9, 16 } + , + {/* glyphCommand */ CMD_DRAWLINE, 247, 240 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_93[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 2, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 16 } + , + {/* glyphCommand */ CMD_MOVEPEN, 3, 240 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 16 } + , + {/* glyphCommand */ CMD_MOVEPEN, 252, 240 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 240 } + , + {/* glyphCommand */ CMD_MOVEPEN, 252, 16 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_94[7] = { + {/* glyphCommand */ CMD_MOVEPEN, 248, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 2 } + , + {/* glyphCommand */ CMD_MOVEPEN, 248, 2 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 2 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_95[2] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 20, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_96[7] = { + {/* glyphCommand */ CMD_MOVEPEN, 1, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 248 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_97[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_98[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 250, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_99[14] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_100[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_101[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_102[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 5, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 253, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 251 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_103[22] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 11 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 14 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 15 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 16 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 16 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 15 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_104[10] = { + {/* glyphCommand */ CMD_MOVEPEN, 251, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 251, 255 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_105[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 255, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 243 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 244 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_106[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 243 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 244 } + , + {/* glyphCommand */ CMD_MOVEPEN, 1, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 12 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 15 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 16 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_107[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 251, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 5, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 5 } + , + {/* glyphCommand */ CMD_MOVEPEN, 255, 1 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_108[2] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_109[18] = { + {/* glyphCommand */ CMD_MOVEPEN, 245, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 245, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 245, 255 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 248, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 255 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 8, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 10, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 11, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 11, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_110[10] = { + {/* glyphCommand */ CMD_MOVEPEN, 251, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 251, 255 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_111[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_112[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 16 } + , + {/* glyphCommand */ CMD_MOVEPEN, 250, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_113[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 16 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 254 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_114[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 253, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 253, 1 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 251 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_115[17] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 6 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_116[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 253, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 251 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_117[10] = { + {/* glyphCommand */ CMD_MOVEPEN, 251, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 5 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_118[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_119[11] = { + {/* glyphCommand */ CMD_MOVEPEN, 248, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 8, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 4, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_120[5] = { + {/* glyphCommand */ CMD_MOVEPEN, 251, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_121[9] = { + {/* glyphCommand */ CMD_MOVEPEN, 250, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 13 } + , + {/* glyphCommand */ CMD_DRAWLINE, 252, 15 } + , + {/* glyphCommand */ CMD_DRAWLINE, 250, 16 } + , + {/* glyphCommand */ CMD_DRAWLINE, 249, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_122[8] = { + {/* glyphCommand */ CMD_MOVEPEN, 6, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 251, 9 } + , + {/* glyphCommand */ CMD_MOVEPEN, 251, 251 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 251 } + , + {/* glyphCommand */ CMD_MOVEPEN, 251, 9 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 6, 9 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_123[39] = { + {/* glyphCommand */ CMD_MOVEPEN, 2, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 241 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 242 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 255 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 241 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 243 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 253, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 11 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 13 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 15 } + , + {/* glyphCommand */ CMD_MOVEPEN, 255, 1 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 10 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 12 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 14 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 15 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_124[2] = { + {/* glyphCommand */ CMD_MOVEPEN, 0, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_125[39] = { + {/* glyphCommand */ CMD_MOVEPEN, 254, 240 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 241 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 242 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 244 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 246 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 249 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 255 } + , + {/* glyphCommand */ CMD_MOVEPEN, 0, 241 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 243 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 245 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 247 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 248 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 250 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 4 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 6 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 9 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 11 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 13 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 15 } + , + {/* glyphCommand */ CMD_MOVEPEN, 1, 1 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 3 } + , + {/* glyphCommand */ CMD_DRAWLINE, 255, 5 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 7 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 8 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 10 } + , + {/* glyphCommand */ CMD_DRAWLINE, 2, 12 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 14 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 15 } + , + {/* glyphCommand */ CMD_DRAWLINE, 254, 16 } +}; + +struct ppmd_glyphCommand const ppmd_standardfont_glyphTable_cmd_126[23] = { + {/* glyphCommand */ CMD_MOVEPEN, 2, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 0, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 251 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 252 } + , + {/* glyphCommand */ CMD_DRAWLINE, 11, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 13, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 15, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 17, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 18, 254 } + , + {/* glyphCommand */ CMD_MOVEPEN, 2, 0 } + , + {/* glyphCommand */ CMD_NOOP, 0, 0 } + , + {/* glyphCommand */ CMD_DRAWLINE, 1, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 3, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 5, 253 } + , + {/* glyphCommand */ CMD_DRAWLINE, 7, 254 } + , + {/* glyphCommand */ CMD_DRAWLINE, 11, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 13, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 15, 2 } + , + {/* glyphCommand */ CMD_DRAWLINE, 17, 1 } + , + {/* glyphCommand */ CMD_DRAWLINE, 18, 255 } + , + {/* glyphCommand */ CMD_DRAWLINE, 18, 252 } +}; + +struct ppmd_glyph const ppmd_standardfont_glyphTable[95] = { + { /* glyph */ + { /* header */ 0, 0, 21} + , + NULL + } + , + { /* glyph */ + { /* header */ 8, 251, 5} + , + ppmd_standardfont_glyphTable_cmd_33 + } + , + { /* glyph */ + { /* header */ 17, 253, 15} + , + ppmd_standardfont_glyphTable_cmd_34 + } + , + { /* glyph */ + { /* header */ 11, 246, 11} + , + ppmd_standardfont_glyphTable_cmd_35 + } + , + { /* glyph */ + { /* header */ 26, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_36 + } + , + { /* glyph */ + { /* header */ 31, 244, 12} + , + ppmd_standardfont_glyphTable_cmd_37 + } + , + { /* glyph */ + { /* header */ 34, 243, 13} + , + ppmd_standardfont_glyphTable_cmd_38 + } + , + { /* glyph */ + { /* header */ 7, 251, 5} + , + ppmd_standardfont_glyphTable_cmd_39 + } + , + { /* glyph */ + { /* header */ 10, 249, 7} + , + ppmd_standardfont_glyphTable_cmd_40 + } + , + { /* glyph */ + { /* header */ 10, 249, 7} + , + ppmd_standardfont_glyphTable_cmd_41 + } + , + { /* glyph */ + { /* header */ 8, 248, 8} + , + ppmd_standardfont_glyphTable_cmd_42 + } + , + { /* glyph */ + { /* header */ 5, 243, 13} + , + ppmd_standardfont_glyphTable_cmd_43 + } + , + { /* glyph */ + { /* header */ 8, 251, 5} + , + ppmd_standardfont_glyphTable_cmd_44 + } + , + { /* glyph */ + { /* header */ 2, 243, 13} + , + ppmd_standardfont_glyphTable_cmd_45 + } + , + { /* glyph */ + { /* header */ 5, 251, 5} + , + ppmd_standardfont_glyphTable_cmd_46 + } + , + { /* glyph */ + { /* header */ 2, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_47 + } + , + { /* glyph */ + { /* header */ 17, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_48 + } + , + { /* glyph */ + { /* header */ 4, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_49 + } + , + { /* glyph */ + { /* header */ 14, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_50 + } + , + { /* glyph */ + { /* header */ 15, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_51 + } + , + { /* glyph */ + { /* header */ 6, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_52 + } + , + { /* glyph */ + { /* header */ 17, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_53 + } + , + { /* glyph */ + { /* header */ 23, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_54 + } + , + { /* glyph */ + { /* header */ 5, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_55 + } + , + { /* glyph */ + { /* header */ 29, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_56 + } + , + { /* glyph */ + { /* header */ 23, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_57 + } + , + { /* glyph */ + { /* header */ 11, 251, 5} + , + ppmd_standardfont_glyphTable_cmd_58 + } + , + { /* glyph */ + { /* header */ 14, 251, 5} + , + ppmd_standardfont_glyphTable_cmd_59 + } + , + { /* glyph */ + { /* header */ 3, 244, 12} + , + ppmd_standardfont_glyphTable_cmd_60 + } + , + { /* glyph */ + { /* header */ 5, 243, 13} + , + ppmd_standardfont_glyphTable_cmd_61 + } + , + { /* glyph */ + { /* header */ 3, 244, 12} + , + ppmd_standardfont_glyphTable_cmd_62 + } + , + { /* glyph */ + { /* header */ 20, 247, 9} + , + ppmd_standardfont_glyphTable_cmd_63 + } + , + { /* glyph */ + { /* header */ 55, 243, 14} + , + ppmd_standardfont_glyphTable_cmd_64 + } + , + { /* glyph */ + { /* header */ 8, 247, 9} + , + ppmd_standardfont_glyphTable_cmd_65 + } + , + { /* glyph */ + { /* header */ 23, 245, 10} + , + ppmd_standardfont_glyphTable_cmd_66 + } + , + { /* glyph */ + { /* header */ 18, 246, 11} + , + ppmd_standardfont_glyphTable_cmd_67 + } + , + { /* glyph */ + { /* header */ 15, 245, 10} + , + ppmd_standardfont_glyphTable_cmd_68 + } + , + { /* glyph */ + { /* header */ 11, 246, 9} + , + ppmd_standardfont_glyphTable_cmd_69 + } + , + { /* glyph */ + { /* header */ 8, 246, 8} + , + ppmd_standardfont_glyphTable_cmd_70 + } + , + { /* glyph */ + { /* header */ 22, 246, 11} + , + ppmd_standardfont_glyphTable_cmd_71 + } + , + { /* glyph */ + { /* header */ 8, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_72 + } + , + { /* glyph */ + { /* header */ 2, 252, 4} + , + ppmd_standardfont_glyphTable_cmd_73 + } + , + { /* glyph */ + { /* header */ 10, 248, 8} + , + ppmd_standardfont_glyphTable_cmd_74 + } + , + { /* glyph */ + { /* header */ 8, 245, 10} + , + ppmd_standardfont_glyphTable_cmd_75 + } + , + { /* glyph */ + { /* header */ 3, 246, 7} + , + ppmd_standardfont_glyphTable_cmd_76 + } + , + { /* glyph */ + { /* header */ 11, 244, 12} + , + ppmd_standardfont_glyphTable_cmd_77 + } + , + { /* glyph */ + { /* header */ 8, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_78 + } + , + { /* glyph */ + { /* header */ 21, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_79 + } + , + { /* glyph */ + { /* header */ 13, 245, 10} + , + ppmd_standardfont_glyphTable_cmd_80 + } + , + { /* glyph */ + { /* header */ 24, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_81 + } + , + { /* glyph */ + { /* header */ 16, 245, 10} + , + ppmd_standardfont_glyphTable_cmd_82 + } + , + { /* glyph */ + { /* header */ 20, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_83 + } + , + { /* glyph */ + { /* header */ 5, 248, 8} + , + ppmd_standardfont_glyphTable_cmd_84 + } + , + { /* glyph */ + { /* header */ 10, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_85 + } + , + { /* glyph */ + { /* header */ 5, 247, 9} + , + ppmd_standardfont_glyphTable_cmd_86 + } + , + { /* glyph */ + { /* header */ 11, 244, 12} + , + ppmd_standardfont_glyphTable_cmd_87 + } + , + { /* glyph */ + { /* header */ 5, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_88 + } + , + { /* glyph */ + { /* header */ 6, 247, 9} + , + ppmd_standardfont_glyphTable_cmd_89 + } + , + { /* glyph */ + { /* header */ 8, 246, 10} + , + ppmd_standardfont_glyphTable_cmd_90 + } + , + { /* glyph */ + { /* header */ 11, 249, 7} + , + ppmd_standardfont_glyphTable_cmd_91 + } + , + { /* glyph */ + { /* header */ 2, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_92 + } + , + { /* glyph */ + { /* header */ 11, 249, 7} + , + ppmd_standardfont_glyphTable_cmd_93 + } + , + { /* glyph */ + { /* header */ 7, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_94 + } + , + { /* glyph */ + { /* header */ 2, 253, 22} + , + ppmd_standardfont_glyphTable_cmd_95 + } + , + { /* glyph */ + { /* header */ 7, 251, 5} + , + ppmd_standardfont_glyphTable_cmd_96 + } + , + { /* glyph */ + { /* header */ 17, 247, 10} + , + ppmd_standardfont_glyphTable_cmd_97 + } + , + { /* glyph */ + { /* header */ 17, 246, 9} + , + ppmd_standardfont_glyphTable_cmd_98 + } + , + { /* glyph */ + { /* header */ 14, 247, 9} + , + ppmd_standardfont_glyphTable_cmd_99 + } + , + { /* glyph */ + { /* header */ 17, 247, 10} + , + ppmd_standardfont_glyphTable_cmd_100 + } + , + { /* glyph */ + { /* header */ 17, 247, 9} + , + ppmd_standardfont_glyphTable_cmd_101 + } + , + { /* glyph */ + { /* header */ 8, 251, 7} + , + ppmd_standardfont_glyphTable_cmd_102 + } + , + { /* glyph */ + { /* header */ 22, 247, 10} + , + ppmd_standardfont_glyphTable_cmd_103 + } + , + { /* glyph */ + { /* header */ 10, 247, 10} + , + ppmd_standardfont_glyphTable_cmd_104 + } + , + { /* glyph */ + { /* header */ 8, 252, 4} + , + ppmd_standardfont_glyphTable_cmd_105 + } + , + { /* glyph */ + { /* header */ 11, 251, 5} + , + ppmd_standardfont_glyphTable_cmd_106 + } + , + { /* glyph */ + { /* header */ 8, 247, 8} + , + ppmd_standardfont_glyphTable_cmd_107 + } + , + { /* glyph */ + { /* header */ 2, 252, 4} + , + ppmd_standardfont_glyphTable_cmd_108 + } + , + { /* glyph */ + { /* header */ 18, 241, 15} + , + ppmd_standardfont_glyphTable_cmd_109 + } + , + { /* glyph */ + { /* header */ 10, 247, 10} + , + ppmd_standardfont_glyphTable_cmd_110 + } + , + { /* glyph */ + { /* header */ 17, 247, 10} + , + ppmd_standardfont_glyphTable_cmd_111 + } + , + { /* glyph */ + { /* header */ 17, 246, 9} + , + ppmd_standardfont_glyphTable_cmd_112 + } + , + { /* glyph */ + { /* header */ 17, 247, 10} + , + ppmd_standardfont_glyphTable_cmd_113 + } + , + { /* glyph */ + { /* header */ 8, 249, 6} + , + ppmd_standardfont_glyphTable_cmd_114 + } + , + { /* glyph */ + { /* header */ 17, 248, 9} + , + ppmd_standardfont_glyphTable_cmd_115 + } + , + { /* glyph */ + { /* header */ 8, 251, 7} + , + ppmd_standardfont_glyphTable_cmd_116 + } + , + { /* glyph */ + { /* header */ 10, 247, 10} + , + ppmd_standardfont_glyphTable_cmd_117 + } + , + { /* glyph */ + { /* header */ 5, 248, 8} + , + ppmd_standardfont_glyphTable_cmd_118 + } + , + { /* glyph */ + { /* header */ 11, 245, 11} + , + ppmd_standardfont_glyphTable_cmd_119 + } + , + { /* glyph */ + { /* header */ 5, 248, 9} + , + ppmd_standardfont_glyphTable_cmd_120 + } + , + { /* glyph */ + { /* header */ 9, 248, 8} + , + ppmd_standardfont_glyphTable_cmd_121 + } + , + { /* glyph */ + { /* header */ 8, 248, 9} + , + ppmd_standardfont_glyphTable_cmd_122 + } + , + { /* glyph */ + { /* header */ 39, 249, 7} + , + ppmd_standardfont_glyphTable_cmd_123 + } + , + { /* glyph */ + { /* header */ 2, 252, 4} + , + ppmd_standardfont_glyphTable_cmd_124 + } + , + { /* glyph */ + { /* header */ 39, 249, 7} + , + ppmd_standardfont_glyphTable_cmd_125 + } + , + { /* glyph */ + { /* header */ 23, 255, 21} + , + ppmd_standardfont_glyphTable_cmd_126 + } +}; + + +struct ppmd_font const ppmd_standardfont = { + {/* .header */ + {'p','p','m','d','f','o','n','t'}, + 0x01, + 95, + 32 + } + , + /* .glyphTable: */ ppmd_standardfont_glyphTable +}; diff --git a/lib/util/LICENSE.txt b/lib/util/LICENSE.txt new file mode 100644 index 00000000..aeb06a7f --- /dev/null +++ b/lib/util/LICENSE.txt @@ -0,0 +1,121 @@ +The Frontier Artistic License Version 1.0 +Derived from the Artistic License at OpenSource.org. +Submitted to OpenSource.org for Open Source Initiative certification. + +Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to +make reasonable modifications. + +Definitions + + "Package" refers to the script, suite, file, or collection of + scripts, suites, and/or files distributed by the Copyright Holder, + and to derivatives of that Package created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes of + the Copyright Holder. + + "Copyright Holder" is whoever is named in the copyright statement + or statements for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the basis + of media cost, duplication charges, time of people involved, and + so on. (You will not be required to justify it to the Copyright + Holder, but only to the computing community at large as a market + that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it under + the same conditions they received it. + + +Terms + +1. You may make and give away verbatim copies of the source form of +the Standard Version of this Package without restriction, provided +that you duplicate all of the original copyright notices and +associated disclaimers. + +2. You may apply bug fixes, portability fixes, and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, +provided that you insert a prominent notice in each changed script, +suite, or file stating how and when you changed that script, suite, +or file, and provided that you do at least ONE of the following: + + a) Use the modified Package only within your corporation or + organization, or retain the modified Package solely for personal use. + + b) Place your modifications in the Public Domain or otherwise make + them Freely Available, such as by posting said modifications to Usenet + or an equivalent medium, or placing the modifications on a major archive + site such as ftp.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + c) Rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page (or equivalent) for each non-standard executable + that clearly documents how it differs from the Standard Version. + + d) Make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) Distribute a Standard Version of the executables and library + files, together with instructions (in the manual page or + equivalent) on where to get the Standard Version. + + b) Accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) Accompany any non-standard executables with their corresponding + Standard Version executables, give the non-standard executables + non-standard names, and clearly document the differences in manual + pages (or equivalent), together with instructions on where to get + the Standard Version. + + d) Make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of +this Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) +software distribution provided that you do not advertise this Package +as a product of your own. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whomever generated +them, and may be sold commercially, and may be aggregated with this +Package. + +7. Scripts, suites, or programs supplied by you that depend on or +otherwise make use of this Package shall not be considered part of +this Package. + +8. The name of the Copyright Holder may not be used to endorse or +promote products derived from this software without specific prior +written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End + + +http://www.spinwardstars.com/frontier/fal.html diff --git a/lib/util/Makefile b/lib/util/Makefile new file mode 100644 index 00000000..8f461f28 --- /dev/null +++ b/lib/util/Makefile @@ -0,0 +1,29 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = lib/util +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/Makefile.config + +INCLUDES = -I $(BUILDDIR) -I $(SRCDIR)/$(SUBDIR)/.. + +# 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 + +MERGE_OBJECTS = + +all: $(UTILOBJECTS) + +include $(SRCDIR)/Makefile.common + +$(UTILOBJECTS):%.o:%.c importinc + $(CC) -c $(INCLUDES) -DNDEBUG $(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/bitreverse.h b/lib/util/bitreverse.h new file mode 100644 index 00000000..b3f0ea13 --- /dev/null +++ b/lib/util/bitreverse.h @@ -0,0 +1,46 @@ +/* +** bitreverse.h +** +** This particular array seems to be useful in a lot of bitmap +** conversion programs. It's not used with standard libnetpbm PBM +** processing because bits are stored one per byte (or per word), for +** easier manipulation. But if you wanted to write, for example, a +** program to directly convert Sun raster format into X bitmaps, you +** could use this. It's also useful for the "packed PBM" libnetpbm +** functions. +** +** Of course, you could also use this fairly slick chunk of code: +** +** c = ((c >> 1) & 0x55) | ((c << 1) & 0xaa); +** c = ((c >> 2) & 0x33) | ((c << 2) & 0xcc); +** c = ((c >> 4) & 0x0f) | ((c << 4) & 0xf0); +*/ + +#ifndef _BITR_H_ +#define _BITR_H_ + +static unsigned char bitreverse[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, + 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, + 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, + 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, + 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, + 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, + 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, + 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, + 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, + 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, + 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, + 0x3f, 0xbf, 0x7f, 0xff}; + +#endif /*_BITR_H_*/ diff --git a/lib/util/filename.c b/lib/util/filename.c new file mode 100644 index 00000000..e3a9a89f --- /dev/null +++ b/lib/util/filename.c @@ -0,0 +1,26 @@ +#include "nstring.h" + +#include "filename.h" + +const char * +pm_basename(const char * const fileName) { +/*---------------------------------------------------------------------------- + Return the filename portion of a file name, e.g. "foo.ppm" from + "/home/bryanh/foo.ppm". + + Return it as a malloc'ed string. +-----------------------------------------------------------------------------*/ + unsigned int basenameStart; + unsigned int i; + const char * retval; + + basenameStart = 0; /* initial assumption */ + + for (i = 0; fileName[i]; ++i) { + if (fileName[i] == '/') + basenameStart = i+1; + } + asprintfN(&retval, "%s", &fileName[basenameStart]); + + return retval; +} diff --git a/lib/util/filename.h b/lib/util/filename.h new file mode 100644 index 00000000..f598fec1 --- /dev/null +++ b/lib/util/filename.h @@ -0,0 +1,7 @@ +#ifndef FILENAME_H_INCLUDED +#define FILENAME_H_INCLUDED + +const char * +pm_basename(const char * const fileName); + +#endif diff --git a/lib/util/intcode.h b/lib/util/intcode.h new file mode 100644 index 00000000..4d9c83aa --- /dev/null +++ b/lib/util/intcode.h @@ -0,0 +1,86 @@ +#ifndef INTCODE_H_INCLUDED +#define INTCODE_H_INCLUDED + +#include "pm_config.h" /* For uint32_t, BYTE_ORDER */ + +typedef struct { +/*---------------------------------------------------------------------------- + This is a big-endian representation of a 32 bit integer. I.e. + bytes[0] is the most significant 8 bits; bytes[3] is the least + significant 8 bits of the number in pure binary. + + On a big-endian machines, this is bit for bit identical to uint32_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[4]; +} bigend32; + + +unsigned int const pm_byteOrder = BYTE_ORDER; + + +static __inline__ uint32_t +pm_uintFromBigend32(bigend32 const arg) { + + uint32_t retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + bigend32 bigend; + uint32_t native; + } converter; + + converter.bigend = arg; + + retval = converter.native; + }; break; + case LITTLE_ENDIAN: { + retval = + (arg.bytes[0] << 24) | + (arg.bytes[1] << 16) | + (arg.bytes[2] << 8) | + (arg.bytes[3] << 0); + } break; + } + return retval; +} + + + +static __inline__ bigend32 +pm_bigendFromUint32(uint32_t const arg) { + + bigend32 retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + bigend32 bigend; + uint32_t native; + } converter; + + converter.native = arg; + + retval = converter.bigend; + } break; + case LITTLE_ENDIAN: { + uint32_t shift; + shift = arg; + 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 */ + } break; + } + return retval; +} + +#endif diff --git a/lib/util/lexheader b/lib/util/lexheader new file mode 100644 index 00000000..c5691660 --- /dev/null +++ b/lib/util/lexheader @@ -0,0 +1,9 @@ +/* This is the file 'lexheader'. It contains extra stuff needed by + parsers generated by lex. + + GNU Flex generates a parser that refers to the non-ansi C library + subroutine fileno(). In order to let it compile without warnings, + we define _XOPEN_SOURCE to declare that fact. +*/ +#define _XOPEN_SOURCE +/* END OF lexheader */ diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h new file mode 100644 index 00000000..a26d007b --- /dev/null +++ b/lib/util/mallocvar.h @@ -0,0 +1,107 @@ +/* These are some dynamic memory allocation facilities. They are essentially + an extension to C, as they do allocations with a cognizance of C + variables. You can use them to make C read more like a high level + language. + + Before including this, you must define an __inline__ macro if your + compiler doesn't recognize it as a keyword. +*/ + +#ifndef MALLOCVAR_INCLUDED +#define MALLOCVAR_INCLUDED + +#include "pm_config.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +static __inline__ void +mallocProduct(void ** const resultP, + unsigned int const factor1, + unsigned int const factor2) { +/*---------------------------------------------------------------------------- + malloc a space whose size in bytes is the product of 'factor1' and + 'factor2'. But if that size cannot be represented as an unsigned int, + return NULL without allocating anything. Also return NULL if the malloc + fails. + + If either factor is zero, malloc a single byte. + + Note that malloc() actually takes a size_t size argument, so the + proper test would be whether the size can be represented by size_t, + not unsigned int. But there is no reliable indication available to + us, like UINT_MAX, of what the limitations of size_t are. We + assume size_t is at least as expressive as unsigned int and that + nobody really needs to allocate more than 4GB of memory. +-----------------------------------------------------------------------------*/ + if (factor1 == 0 || factor2 == 0) + *resultP = malloc(1); + else { + if (UINT_MAX / factor2 < factor1) + *resultP = NULL; + else + *resultP = malloc(factor1 * factor2); + } +} + + + +static __inline__ void +reallocProduct(void ** const blockP, + unsigned int const factor1, + unsigned int const factor2) { + + if (UINT_MAX / factor2 < factor1) + *blockP = NULL; + else + *blockP = realloc(*blockP, factor1 * factor2); +} + + + +#define MALLOCARRAY(arrayName, nElements) do { \ + void * array; \ + mallocProduct(&array, nElements, sizeof(arrayName[0])); \ + arrayName = array; \ +} while (0) + +#define REALLOCARRAY(arrayName, nElements) { \ + void * array; \ + array = arrayName; \ + reallocProduct(&array, nElements, sizeof(arrayName[0])); \ + arrayName = array; \ +} while (0) + + +#define MALLOCARRAY_NOFAIL(arrayName, nElements) \ +do { \ + MALLOCARRAY(arrayName, nElements); \ + if ((arrayName) == NULL) \ + abort(); \ +} while(0) + +#define REALLOCARRAY_NOFAIL(arrayName, nElements) \ +do { \ + REALLOCARRAY(arrayName, nElements); \ + if ((arrayName) == NULL) \ + abort(); \ +} while(0) + + +#define MALLOCVAR(varName) \ + varName = malloc(sizeof(*varName)) + +#define MALLOCVAR_NOFAIL(varName) \ + do {if ((varName = malloc(sizeof(*varName))) == NULL) abort();} while(0) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/util/nstring.c b/lib/util/nstring.c new file mode 100644 index 00000000..702a3c44 --- /dev/null +++ b/lib/util/nstring.c @@ -0,0 +1,906 @@ +/* + * snprintf.c - a portable implementation of snprintf + * + + THIS MODULE WAS ADAPTED FOR NETPBM BY BRYAN HENDERSON ON 2002.03.24. + Bryan got the base from + http://www.ijs.si/software/snprintf/snprintf-2.2.tar.gz, but made + a lot of changes and additions. + + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * FEATURES + * - careful adherence to specs regarding flags, field width and precision; + * - good performance for large string handling (large format, large + * argument or large paddings). Performance is similar to system's sprintf + * and in several cases significantly better (make sure you compile with + * optimizations turned on, tell the compiler the code is strict ANSI + * if necessary to give it more freedom for optimizations); + * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * - written in standard ISO/ANSI C - requires an ANSI C compiler. + * + * IMPLEMENTED CONVERSION SPECIFIERS AND DATA TYPES + * + * This snprintf implements only the following conversion specifiers: + * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * with flags: '-', '+', ' ', '0' and '#'. + * An asterisk is acceptable for field width as well as precision. + * + * Length modifiers 'h' (short int), 'l' (long int), + * and 'll' (long long int) are implemented. + * + * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) + * with length modifiers (none or h, l, ll) is left to the system routine + * sprintf, but all handling of flags, field width and precision as well as + * c and s conversions is done very carefully by this portable routine. + * If a string precision (truncation) is specified (e.g. %.8s) it is + * guaranteed the string beyond the specified precision will not be referenced. + * + * Length modifiers h, l and ll are ignored for c and s conversions (you + * can't use data types wint_t and wchar_t). + * + * The following common synonyms for conversion characters are acceptable: + * - i is a synonym for d + * - D is a synonym for ld, explicit length modifiers are ignored + * - U is a synonym for lu, explicit length modifiers are ignored + * - O is a synonym for lo, explicit length modifiers are ignored + * The D, O and U conversion characters are nonstandard, they are accepted + * for backward compatibility only, and should not be used for new code. + * + * The following is specifically NOT implemented: + * - flag ' (thousands' grouping character) is recognized but ignored + * - numeric conversion specifiers: f, e, E, g, G and synonym F, + * as well as the new a and A conversion specifiers + * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) + * - wide character/string conversions: lc, ls, and nonstandard + * synonyms C and S + * - writeback of converted string length: conversion character n + * - the n$ specification for direct reference to n-th argument + * - locales + * + * It is permitted for str_m to be zero, and it is permitted to specify NULL + * pointer for resulting string argument if str_m is zero (as per ISO C99). + * + * The return value is the number of characters which would be generated + * for the given input, excluding the trailing null. If this value + * is greater or equal to str_m, not all characters from the result + * have been stored in str, output bytes beyond the (str_m-1) -th character + * are discarded. If str_m is greater than zero it is guaranteed + * the resulting string will be null-terminated. + * + * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + * but is different from some older and vendor implementations, + * and is also different from XPG, XSH5, SUSv2 specifications. + * For historical discussion on changes in the semantics and standards + * of snprintf see printf(3) man page in the Linux programmers manual. + * + * Routines asprintf and vasprintf return a pointer (in the ptr argument) + * to a buffer sufficiently large to hold the resulting string. This pointer + * should be passed to free(3) to release the allocated storage when it is + * no longer needed. If sufficient space cannot be allocated, these functions + * will return -1 and set ptr to be a NULL pointer. These two routines are a + * GNU C library extensions (glibc). + * + * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, + * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 + * characters into the allocated output string, the last character in the + * allocated buffer then gets the terminating null. If the formatted string + * length (the return value) is greater than or equal to the str_m argument, + * the resulting string was truncated and some of the formatted characters + * were discarded. These routines present a handy way to limit the amount + * of allocated memory to some sane value. + * + * AVAILABILITY + * http://www.ijs.si/software/snprintf/ + * + */ + + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pm.h" +#include "pm_c_util.h" + +#include "nstring.h" + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, \n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + + + +void +vsnprintfN(char * const str, + size_t const str_m, + const char * const fmt, + va_list ap, + size_t * const sizeP) { + + size_t str_l = 0; + const char *p = fmt; + + /* In contrast with POSIX, the ISO C99 now says that str can be + NULL and str_m can be 0. This is more useful than the old: + if (str_m < 1) return -1; + */ + + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would + be sufficient but the following code achieves better + performance for cases * where format string is long and + contains few conversions + */ + const char *q = strchr(p + 1,'%'); + size_t n = !q ? strlen(p) : (q - p); + if (str_l < str_m) { + size_t avail = str_m - str_l; + fast_memcpy(str + str_l, p, (n > avail ? avail : n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; + /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. + */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32]; + /* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; + /* string address in case of string argument */ + size_t str_arg_l; + /* natural field width of arg without padding and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c + conversion. N.B. standard explicitly states the char + argument for the c conversion is unsigned. + */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric + conversions as required by the precision or minimal + field width + */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits; + /* just to make compiler happy (defined but not used) */ + str_arg = NULL; + starting_p = p; + ++p; /* skip '%' */ + + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; break; + /* If both the ' ' and '+' flags appear, the ' ' + flag should be ignored + */ + case '#': alternate_form = 1; break; + case '\'': break; + } + ++p; + } + /* If the '0' and '-' flags both appear, the '0' flag + should be ignored. + */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; make sure + we treat argument like common implementations do + */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) + uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: Solaris 2.6 man page claims that in + this case the precision should be set to 0. + Digital Unix 4.0, HPUX 10 and BSD man page + claim that this case should be treated as + unspecified precision, which is what we do + here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; make + sure we treat argument like common + implementations do + */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) + uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { + /* double l = long long */ + length_modifier = 'l'; /* treat it as a single 'l' */ + p++; + } + } + fmt_spec = *p; + + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': + /* % behaves similar to 's' regarding flags and field widths */ + case 'c': + /* c behaves similar to 's' regarding flags and field widths */ + case 's': + /* wint_t and wchar_t not handled */ + length_modifier = '\0'; + /* the result of zero padding flag with non-numeric + conversion specifier is undefined. Solaris and + HPUX 10 does zero padding in this case, Digital + Unix and Linux does not. + */ + + zero_padding = 0; + /* turn zero padding off for string conversions */ + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; + /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) + /* make sure not to address string beyond the + specified precision !!! + */ + str_arg_l = 0; + else if (!precision_specified) + /* truncate string if necessary as requested by + precision + */ + str_arg_l = strlen(str_arg); + else if (precision == 0) + str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char * q = + memchr(str_arg, '\0', + precision <= 0x7fffffff ? + precision : 0x7fffffff); + str_arg_l = !q ? precision : (q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value + */ + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL + for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) + */ + + int int_arg = 0; + unsigned int uint_arg = 0; + /* defined only for length modifier h, or for no + length modifiers + */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other + conversion character (other than d, i, u, o, + x, or X) is ignored. + + Digital Unix: not specified, but seems to behave + as HPUX does. + + Solaris: If an h, l, or L appears before any + other conversion specifier (other than d, i, u, + o, x, or X), the behavior is + undefined. (Actually %hp converts only 16-bits + of address and %llp treats address as 64-bit + data which is incompatible with (void *) + argument on a 32-bit system). + */ + + length_modifier = '\0'; + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second + argument of char or short to va_arg, + because arguments seen by the called + function are not char or short. C converts + char and short arguments to int before + passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) + arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) + arg_sign = 1; + break; + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: For d, i, u, o, x, and X conversions, if + precision is specified, the '0' flag should be + ignored. This is so with Solaris 2.6, Digital UNIX + 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with + Perl. + */ + if (precision_specified) + zero_padding = 0; + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short + int)(-32768) + */ + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || + fmt_spec == 'X')) { + tmp[str_arg_l++] = '0'; + tmp[str_arg_l++] = fmt_spec; + } + /* alternate form should have no effect for p + conversion, but ... + */ + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) + precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0) { + /* converted to null string */ + /* When zero value is formatted with an explicit + precision 0, the resulting formatted string is + empty (d, i, u, o, x, X, p). + */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; + /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { + f[f_l++] = 'l'; f[f_l++] = 'l'; + } + else + f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') + str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); + break; + case 'l': + str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); + break; + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + str_arg_l += sprintf(tmp+str_arg_l, f, uint_arg); + break; + case 'l': + str_arg_l += sprintf(tmp+str_arg_l, f, ulong_arg); + break; + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point + */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { + size_t num_of_digits = + str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0')) { + /* assure leading zero for alternate-form + octal numbers + */ + if (!precision_specified || + precision < num_of_digits+1) { + /* precision is increased to force the + first character to be zero, except if a + zero value is formatted with an + explicit precision of zero + */ + precision = num_of_digits+1; + precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + int n = + min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + } break; + default: + /* unrecognized conversion specifier, keep format + string as-is + */ + zero_padding = 0; + /* turn zero padding off for non-numeric convers. */ + /* reset flags */ + justify_left = 1; + min_field_width = 0; + /* discard the unrecognized conversion, just keep the + unrecognized conversion character + */ + str_arg = p; + str_arg_l = 0; + if (*p) + /* include invalid conversion specifier unchanged + if not at end-of-string + */ + ++str_arg_l; + break; + } + if (*p) + p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by + min_field_width; this does not include the zero padding + in case of numerical conversions + */ + + if (!justify_left) { + /* left padding with blank or zero */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding ? '0' : ' '), + (n > avail ? avail : n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the + minimal field width for numeric conversions required? + */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, + force it to be copied later in its entirety + */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before + zero padding + */ + int n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision + or min field width + */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m - str_l; + fast_memset(str + str_l, '0', (n > avail ? avail : n)); + } + str_l += n; + } + } + /* insert formatted string (or as-is conversion specifier + for unknown conversions) + */ + { + int n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str + str_l, + str_arg + zero_padding_insertion_ind, + (n > avail ? avail : n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { + /* right blank padding to the field width */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } + if (str_m > 0) { + /* make sure the string is null-terminated even at the expense + of overwriting the last character (shouldn't happen, but + just in case) + */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + *sizeP = str_l; +} + + + +int +snprintfN(char * const dest, + size_t const str_m, + const char * const fmt, + ...) { + + size_t size; + va_list ap; + + va_start(ap, fmt); + + vsnprintfN(dest, str_m, fmt, ap, &size); + + va_end(ap); + + assert(size <= INT_MAX); + + return size; +} + + + +/* When a function that is supposed to return a malloc'ed string cannot + get the memory for it, it should return 'strsol'. That has a much + better effect on the caller, if the caller doesn't explicitly allow for + the out of memory case, than returning NULL. Note that it is very + rare for the system not to have enough memory to return a small string, + so it's OK to have somewhat nonsensical behavior when it happens. We + just don't want catastrophic behavior. + + 'strsol' is an external symbol, so if Caller wants to detect the + out-of-memory failure, he certainly can. +*/ +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, + ...) { + + va_list varargs; + + size_t dryRunLen; + + va_start(varargs, fmt); + + vsnprintfN(NULL, 0, fmt, varargs, &dryRunLen); + + va_end(varargs); + + if (dryRunLen + 1 < dryRunLen) + /* arithmetic overflow */ + *resultP = strsol; + else { + size_t const allocSize = dryRunLen + 1; + char * result; + result = malloc(allocSize); + if (result == NULL) + *resultP = strsol; + else { + va_list varargs; + size_t realLen; + + va_start(varargs, fmt); + + vsnprintfN(result, allocSize, fmt, varargs, &realLen); + + assert(realLen == dryRunLen); + va_end(varargs); + + *resultP = result; + } + } +} + + + +void +strfree(const char * const string) { + + if (string != strsol) + free((void *) string); +} + + + +const char * +strsepN(char ** const stringP, const char * const delim) { + const char * retval; + + if (stringP == NULL || *stringP == NULL) + retval = NULL; + else { + char * p; + + retval = *stringP; + + for (p = *stringP; *p && strchr(delim, *p) == NULL; ++p); + + if (*p) { + /* We hit a delimiter, not end-of-string. So null out the + delimiter and advance user's pointer to the next token + */ + *p++ = '\0'; + *stringP = p; + } else { + /* We ran out of string. So the end-of-string delimiter is + already there, and we set the user's pointer to NULL to + indicate there are no more tokens. + */ + *stringP = NULL; + } + } + return retval; +} + + + +int +stripeq(const char * const comparand, + const char * const comparator) { +/*---------------------------------------------------------------------------- + Compare two strings, ignoring leading and trailing white space. + + Return 1 (true) if the strings are identical; 0 (false) otherwise. +-----------------------------------------------------------------------------*/ + char *p, *q, *px, *qx; + char equal; + + /* Make p and q point to the first non-blank character in each string. + If there are no non-blank characters, make them point to the terminating + NULL. + */ + + p = (char *) comparand; + while (ISSPACE(*p)) p++; + q = (char *) comparator; + while (ISSPACE(*q)) q++; + + /* Make px and qx point to the last non-blank character in each string. + If there are no nonblank characters (which implies the string is + null), make them point to the terminating NULL. + */ + + if (*p == '\0') px = p; + else { + px = p + strlen(p) - 1; + while (ISSPACE(*px)) px--; + } + + if (*q == '\0') qx = q; + else { + qx = q + strlen(q) - 1; + while (ISSPACE(*qx)) qx--; + } + + equal = 1; /* initial assumption */ + + /* If the stripped strings aren't the same length, + we know they aren't equal + */ + if (px - p != qx - q) equal = 0; + + + while (p <= px) { + if (*p != *q) equal = 0; + p++; q++; + } + return equal; +} + + + +const char * +memmemN(const char * const haystack, + size_t const haystacklen, + const char * const needle, + size_t const needlelen) { + + /* This does the same as the function of the same name in the GNU + C library + */ + const char * p; + + for (p = haystack; p <= haystack + haystacklen - needlelen; ++p) + if (MEMEQ(p, needle, needlelen)) + return p; + + return NULL; +} diff --git a/lib/util/nstring.h b/lib/util/nstring.h new file mode 100644 index 00000000..9ed20051 --- /dev/null +++ b/lib/util/nstring.h @@ -0,0 +1,157 @@ +#ifndef _NSTRING_H +#define _NSTRING_H + +#include +#include +#include + +#include "pm.h" /* For PM_GNU_PRINTF_ATTR, __inline__ */ + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +/* Here is 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. +*/ +#define STRSCPY(A,B) \ + (strncpy((A), (B), sizeof(A)), *((A)+sizeof(A)-1) = '\0') +#define STRSCMP(A,B) \ + (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) + +#define MEMEQ(A, B, C) \ + (memcmp((A), (B), (C)) == 0) +#define MEMSZERO(A) \ + bzero((A), sizeof(A)) + + +static __inline__ int +streq(const char * const comparand, + const char * const comparator) { + + return strcmp(comparand, comparator) == 0; +} + + + +/* The standard C library routines isdigit(), for some weird + historical reason, does not take a character (type 'char') as its + argument. Instead it takes an integer. When the integer is a whole + number, it represents a character in the obvious way using the local + character set encoding. When the integer is negative, the results + are undefined. + + Passing a character to isdigit(), which expects an integer, results in + isdigit() sometimes getting a negative number. + + On some systems, when the integer is negative, it represents exactly + the character you want it to anyway (e.g. -1 is the character that is + encoded 0xFF). But on others, it does not. + + (The same is true of other routines like isdigit()). + + Therefore, we have the substitutes for isdigit() etc. that take an + actual character (type 'char') as an argument. +*/ + +#define ISALNUM(C) (isalnum((unsigned char)(C))) +#define ISALPHA(C) (isalpha((unsigned char)(C))) +#define ISCNTRL(C) (iscntrl((unsigned char)(C))) +#define ISDIGIT(C) (isdigit((unsigned char)(C))) +#define ISGRAPH(C) (isgraph((unsigned char)(C))) +#define ISLOWER(C) (islower((unsigned char)(C))) +#define ISPRINT(C) (isprint((unsigned char)(C))) +#define ISPUNCT(C) (ispunct((unsigned char)(C))) +#define ISSPACE(C) (isspace((unsigned char)(C))) +#define ISUPPER(C) (isupper((unsigned char)(C))) +#define ISXDIGIT(C) (isxdigit((unsigned char)(C))) +#define TOUPPER(C) ((char)toupper((unsigned char)(C))) + + +/* These are all private versions of commonly available standard C + library subroutines whose names are the same except with the N at + the end. Because not all standard C libraries have them all, + Netpbm must include them in its own libraries, and because some + standard C libraries have some of them, Netpbm must use different + names for them. + + The GNU C library has all of them. All but the oldest standard C libraries + have snprintf(). + + There are slight differences between the asprintf() family and that + found in other libraries: + + - There is no return value. + + - The returned string is a const char * instead of a char *. The + const is more correct. + + - If the function can't get the memory, it returns 'strsol', + which is a string that is in static memory that contains text + indicating an out of memory failure has occurred, intead of + NULL. This makes it much easier for programs to ignore this + possibility. + + strfree() is strictly a Netpbm invention, to allow proper type checking + when freeing storage allocated by the Netpbm asprintfN(). +*/ + +extern const char * const strsol; + +int +snprintfN(char * const dest, + size_t const str_m, + const char * const fmt, + ...) PM_GNU_PRINTF_ATTR(3,4); + +void +vsnprintfN(char * const str, + size_t const str_m, + const char * const fmt, + va_list ap, + size_t * const sizeP); + +void +asprintfN(const char ** const resultP, + const char * const fmt, + ...) PM_GNU_PRINTF_ATTR(2,3); + +void +strfree(const char * const string); + +const char * +strsepN(char ** const stringP, const char * const delim); + +int +stripeq(const char * const comparand, + const char * const comparator); + +const char * +memmemN(const char * const haystack, + size_t const haystacklen, + const char * const needle, + size_t const needlelen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h new file mode 100644 index 00000000..f21a2f82 --- /dev/null +++ b/lib/util/pm_c_util.h @@ -0,0 +1,62 @@ +#ifndef PM_C_UTIL_INCLUDED +#define PM_C_UTIL_INCLUDED + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#undef ABS +#define ABS(a) ((a) >= 0 ? (a) : -(a)) +#undef SGN +#define SGN(a) (((a)<0) ? -1 : 1) +#undef ODD +#define ODD(n) ((n) & 1) +#undef ROUND +#define ROUND(X) (((X) >= 0) ? (int)((X)+0.5) : (int)((X)-0.5)) +#undef ROUNDU +#define ROUNDU(X) ((unsigned int)((X)+0.5)) +#undef SQR +#define SQR(a) ((a)*(a)) + +/* NOTE: do not use "bool" as a type in an external interface. It could + have different definitions on either side of the interface. Even if both + sides include this interface header file, the conditional compilation + here means one side may use the typedef below and the other side may + use some other definition. For an external interface, be safe and just + use "int". +*/ + +/* We used to assume that if TRUE was defined, then bool was too. + However, we had a report on 2001.09.21 of a Tru64 system that had + TRUE but not bool and on 2002.03.21 of an AIX 4.3 system that was + likewise. So now we define bool all the time, unless the macro + HAVE_BOOL is defined. If someone is using the Netpbm libraries and + also another library that defines bool, he can either make the + other library define/respect HAVE_BOOL or just define HAVE_BOOL in + the file that includes pm_config.h or with a compiler option. Note + that C++ always has bool. + + A preferred way of getting booleans is . But it's not + available on all platforms, and it's easy to reproduce what it does + here. +*/ +#ifndef TRUE + #define TRUE 1 + #endif +#ifndef FALSE + #define FALSE 0 + #endif +/* C++ has a bool type and false and true constants built in. */ +#ifndef __cplusplus + #ifndef HAVE_BOOL + #define HAVE_BOOL 1 + typedef int bool; + #endif + #ifndef true + enum boolvalue {false=0, true=1}; + #endif + #endif + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +#endif diff --git a/lib/util/shhopt-1.1.6.lsm b/lib/util/shhopt-1.1.6.lsm new file mode 100644 index 00000000..7fba316e --- /dev/null +++ b/lib/util/shhopt-1.1.6.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: shhopt - library for parsing command line options. +Version: 1.1.6 +Entered-date: 10MAR00 +Description: C-functions for parsing command line options, both + traditional one-character options, and GNU'ish + --long-options. +Keywords: programming, library, lib, commandline, options +Author: sverrehu@online.no (Sverre H. Huseby) +Primary-site: http://home.sol.no/~sverrehu/pub-unix/ +Alternate-site: sunsite.unc.edu /pub/Linux/libs + shhopt-1.1.6.tar.gz +Platforms: Requires ANSI C-compiler. +Copying-policy: Artistic License + http://www.opensource.org/licenses/artistic-license.html +End diff --git a/lib/util/shhopt.README b/lib/util/shhopt.README new file mode 100644 index 00000000..2d241edf --- /dev/null +++ b/lib/util/shhopt.README @@ -0,0 +1,200 @@ +Shhopt was originally written by Sverre H. Huseby, and extended by +Bryan Henderson starting in March 2000 for use in Netpbm. + +Below is the README file from Huseby's package. + +The file LICENSE.TXT in this directory contains the license (the +Artistic License) under which Bryan took and redistributed Shhopt and +the license under which Bryan offers the modified Shhopt to others. + +Bryan made the following changes to shhopt for Netpbm. It is fully +backward compatible with the original. + +- OPT_FLOAT (floating point number) data type added + +- optParseOptions2() added. Advantages over optParseOptions(): You + can have a syntax where there is no such thing as a short option + (e.g. -a. Maybe stacked like -tanp). Then the long options can + have either 1 or 2 dashes (e.g. -width or --width). Of course, -w + could be an abbreviation of -width; that's not the same thing as a + short option. + +- optParseOptions3() added. Advantages over optParseOptions2(): + Tells you whether (how many times, actually) an option was + specified - no need to play games with defaults. Also, no need + to initialize an option value variable. + +- optStruct longName changed from char * to const char * to avoid + compiler warnings (with -Wwrite-strings) when you assign a string + literal to it (which is the normal case). + +- OPTENTRY/OPTENT3 macros added for declaring the option definition + array. + +- replace isdigit() with ISDIGIT() from Netpbm nstring.h so weird + 8-bit characters don't cause incorrect results. + +------------------------------------------------------------------------------ + + +shhopt - library for parsing command line options. +================================================== + +This is a set of functions for parsing command line options. Both +traditional one-character options, and GNU-style --long-options are +supported. + + +What separates this from traditional getopt? +-------------------------------------------- + +This library does more of the parsing for you. You set up a special +structure describing the names and types of the options you want your +program to support. In the structure you also give addresses of +variables to update or functions to call for the various +options. By calling optParseOptions, all options in argv are parsed +and removed from argv. What is left, are the non-optional arguments to +your program. + +The down-side of this, is that you won't be able to make a program +where the position of the options between the non-options are +significant. + +shhopt is distributed under the "Artistic license" (aka. the Perl +license), which IMHO gives more freedom than GPL or LGPL. For a copy +of the Artistic license, see + + http://www.opensource.org/licenses/artistic-license.html + +For more information on Open Source licenses, go to + + http://www.opensource.org/licenses/ + + +Usage +----- + +To see how to use this library, take a look at the sample program +example.c. + +A brief explanation: + +To parse your command line, you need to create and initialize an array +of optStruct's. Each element in the array describes a long and short +version of an option and specifies what type of option it is and how +to handle it. + +The structure fields (see also shhopt.h): + + `shortName' is the short option name without the leading '-'. + + `longName' is the long option name without the leading "--". + + `type' specifies what type of option this is. (Does it expect an + argument? Is it a flag? If it takes an argument, what type + should it be?) + + `arg' is either a function to be called with the argument from + the commandline, or a pointer to a location in which to store + the value. + + `flags' indicates whether `arg' points to a function or a storage + location. + +The different argument types: + + `OPT_END' flags this as the last element in the options array. + + `OPT_FLAG' indicates an option that takes no arguments. If `arg' is + not a function pointer, the value of `arg' will be set to 1 if + this flag is found on the command line. + + `OPT_STRING' expects a string argument. + + `OPT_INT' expects an int argument. + + `OPT_UINT' expects an unsigned int argument. + + `OPT_LONG' expects a long argument. + + `OPT_ULONG' expects an unsigned long argument. + +The different flag types: + + `OPT_CALLFUNC' indicates that `arg' is a function pointer. If this + is not given, `arg' is taken as a pointer to a variable. + + +Notes +----- + +* A dash (`-') by itself is not taken as any kind of an option, as + several programs use this to indicate the special files stdin and + stdout. It is thus left as a normal argument to the program. + +* Two dashes (`--') as an argument, is taken to mean that the rest of + the arguments should not be scanned for options. This simplifies + giving names of files that start with a dash. + +* Short (one-character) options accept parameters in two ways, either + directly following the option in the same argv-entry, or in the next + argv-entry: + + -sPARAMETER + -s PARAMETER + +* Long options accept parameters in two ways: + + --long-option=PARAMETER + --long-option PARAMETER + + To follow the GNU-tradition, your program documentation should use + the first form. + +* Several one-character options may be combined after a single + dash. If any of the options requires a parameter, the rest of the + string is taken as this parameter. If there is no "rest of the + string", the next argument is taken as the parameter. + +* There is no support for floating point (double) arguments to + options. This is to avoid unnecessary linking with the math + library. See example.c for how to get around this by writing a + function converting a string argument to a double (functions + strToDouble and doubleFunc). + + +Portability +----------- + +If your libc lacks strtoul, you will need to link with GNU's -liberty, +that may be found by anonymous ftp to ftp://ftp.gnu.org/pub/gnu/ + +The library has (more or less recently) been compiled and `tested' on +the following systems: + + IRIX Release 5.3 IP22 + GNU/Linux 2.2.11 + SunOS Release 4.1.3_U1 (-liberty needed) + ULTRIX V4.4 (Rev. 69) + +All compilations were done using GNU's gcc, and GNU's make. + + +Author +------ + +The program is written by + + Sverre H. Huseby sverrehu@online.no + Lofthusvn. 11 B http://home.sol.no/~sverrehu/ + N-0587 Oslo + Norway + +You can use and copy this for _free_, but I would be very happy if you +send me an E-mail and tell me that you use it. If you insist on paying +something, please donate some money to an organization that strives to +make the world a better place for everyone. + +I don't like bugs, so please help me removing them by reporting +whatever you find! + diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c new file mode 100644 index 00000000..7722b5d5 --- /dev/null +++ b/lib/util/shhopt.c @@ -0,0 +1,939 @@ +/*------------------------------------------------------------------------ + | FILE shhopt.c + | + | DESCRIPTION Functions for parsing command line arguments. Values + | of miscellaneous types may be stored in variables, + | or passed to functions as specified. + | + | REQUIREMENTS Some systems lack the ANSI C -function strtoul. If your + | system is one of those, you'll ned to write one yourself, + | or get the GNU liberty-library (from prep.ai.mit.edu). + | + | WRITTEN BY Sverre H. Huseby + +----------------------------------------------------------------------*/ + +/************************************************************************* + This is based on work by Sverre H. Huseby . + These functions are backward compatible with the 'shhopt' + distributed by Huseby. + + See the file README.shhopt for copy licensing information. +*************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" + +/*-----------------------------------------------------------------------+ +| PRIVATE DATA | ++-----------------------------------------------------------------------*/ + +static void optFatalFunc(const char *, ...); +static void (*optFatal)(const char *format, ...) = optFatalFunc; + +/*-----------------------------------------------------------------------+ +| PRIVATE FUNCTIONS | ++-----------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------ + | NAME optFatalFunc + | + | FUNCTION Show given message and abort the program. + | + | INPUT format, ... + | Arguments used as with printf(). + | + | RETURNS Never returns. The program is aborted. + */ +static void +optFatalFunc(const char *format, ...) +{ + va_list ap; + + fflush(stdout); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(99); +} + +/*------------------------------------------------------------------------ + | NAME optStructCount + | + | FUNCTION Get number of options in a optStruct. + | + | INPUT opt array of possible options. + | + | RETURNS Number of options in the given array. + | + | DESCRIPTION Count elements in an optStruct-array. The strcture must + | be ended using an element of type OPT_END. + */ +static int +optStructCount(const optEntry opt[]) +{ + int ret = 0; + + while (opt[ret].type != OPT_END && ret < 500) + ++ret; + return ret; +} + +/*------------------------------------------------------------------------ + | NAME optMatch + | + | FUNCTION Find a matching option. + | + | INPUT opt array of possible options. + | s string to match, without `-' or `--'. + | lng match long option, otherwise short. + | + | RETURNS Index to the option if found, -1 if not found. + | + | 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); + if (lng) { + if ((p = strchr(s, '=')) != NULL) + matchlen = p - s; + else + matchlen = strlen(s); + } + for (q = 0; q < nopt; q++) { + if (lng) { + if (!opt[q].longName) + continue; + if (strncmp(s, opt[q].longName, matchlen) == 0) + return q; + } else { + if (!opt[q].shortName) + continue; + if (*s == opt[q].shortName) + return q; + } + } + return -1; +} + +/*------------------------------------------------------------------------ + | NAME optString + | + | FUNCTION Return a (static) string with the option name. + | + | INPUT opt the option to stringify. + | lng is it a long option? + | + | RETURNS Pointer to static string. + */ +static char * +optString(const optEntry opte, int lng) +{ + static char ret[31]; + + if (lng) { + strcpy(ret, "--"); + strncpy(ret + 2, opte.longName, 28); + } else { + ret[0] = '-'; + ret[1] = opte.shortName; + ret[2] = '\0'; + } + return ret; +} + + + +static optEntry +optStructToEntry(const optStruct opt) { +/*---------------------------------------------------------------------------- + Return the information in 'opt' (an optStruct type) as an optEntry type. + optEntry is newer and has an additional field. +-----------------------------------------------------------------------------*/ + optEntry opte; + + opte.shortName = opt.shortName; + opte.longName = opt.longName; + opte.type = opt.type; + opte.arg = opt.arg; + opte.specified = NULL; + opte.flags = opt.flags; + + return(opte); +} + + + +static optEntry * +optStructTblToEntryTbl(const optStruct optStructTable[]) { +/*---------------------------------------------------------------------------- + Return a table of optEntry types containing the information in the + input table of optStruct types. + + Return it in newly malloc'ed storage. +-----------------------------------------------------------------------------*/ + int count; + /* Number of entries in input table, including OPT_END marker */ + int i; + + optEntry *optEntryTable; /* malloc'ed array */ + + /* Count the entries in optStructTable[] */ + for (i = 0; optStructTable[i].type != OPT_END && i < 500; i++); + count = i+1; + + optEntryTable = (optEntry *) malloc(count * sizeof(optEntry)); + if (optEntryTable) { + int i; + for (i = 0; i < count; i++) + optEntryTable[i] = optStructToEntry(optStructTable[i]); + } + return(optEntryTable); +} + + + + +/*------------------------------------------------------------------------ + | NAME optNeedsArgument + | + | FUNCTION Check if an option requires an argument. + | + | INPUT opt the option to check. + | + | RETURNS Boolean value. + */ +static int +optNeedsArgument(const optEntry opt) +{ + return opt.type == OPT_STRING + || opt.type == OPT_INT + || opt.type == OPT_UINT + || opt.type == OPT_LONG + || opt.type == OPT_ULONG + || opt.type == OPT_FLOAT + || opt.type == OPT_NAMELIST + ; +} + +/*------------------------------------------------------------------------ + | NAME argvRemove + | + | FUNCTION Remove an entry from an argv-array. + | + | INPUT argc pointer to number of options. + | argv array of option-/argument-strings. + | i index of option to remove. + | + | OUTPUT argc new argument count. + | argv array with given argument removed. + */ +static void +argvRemove(int *argc, char *argv[], int i) +{ + if (i >= *argc) + return; + while (i++ < *argc) + argv[i - 1] = argv[i]; + --*argc; +} + + + +static void +getToken(const char * const tokenStart, + char const delimiter, + const char ** const tokenP, + const char ** const nextP) { +/*---------------------------------------------------------------------------- + Find the token starting at 'tokenStart' up to but not including + the first 'delimiter' character or end of string. Return it in newly + malloced memory as *tokenP, NUL-terminated. + + Make *nextP point just past the token, i.e. to the delimiter or + end of string NUL character. + + Note that if the string is empty, or starts with the delimiter, + we return an empty string and *nextP == tokenStart, i.e. *nextP + doesn't necessarily advance. +-----------------------------------------------------------------------------*/ + char * token; + const char * cursor; + unsigned int charCount; + + /* Run through the token, counting characters */ + + charCount = 0; + cursor = tokenStart; + + while (*cursor != delimiter && *cursor != '\0') { + if (*cursor == '\\') { + ++cursor; + if (*cursor == '\0') + optFatal("string ends with an escape character (\\)"); + } + ++cursor; + ++charCount; + } + + token = malloc(charCount + 1); + if (token == NULL) + optFatal("Could not allocate %u bytes of memory to parse a string", + charCount + 1); + + /* Go back and do it again, this time copying the characters */ + charCount = 0; + cursor = tokenStart; + + while (*cursor != delimiter && *cursor != '\0') { + if (*cursor == '\\') + ++cursor; + + assert(*cursor != '\0'); + + token[charCount++] = *cursor++; + } + token[charCount] = '\0'; + + *tokenP = token; + *nextP = cursor; +} + + + +static void +parseNameList(const char * const listText, + struct optNameValue ** const listP) { + + unsigned int const maxOptionCount = 100; + + const char * cursor; + unsigned int optionCount; + struct optNameValue * list; + + MALLOCARRAY_NOFAIL(list, maxOptionCount+1); + + cursor = &listText[0]; /* initial value */ + + optionCount = 0; /* initial value */ + + while (optionCount < maxOptionCount && *cursor != '\0') { + const char * next; + struct optNameValue pair; + + getToken(cursor, '=', &pair.name, &next); + + cursor = next; + + if (*cursor == '\0') + optFatal("name=value option value ends prematurely. An equal " + "sign was expected following name '%s'", pair.name); + + assert(*cursor == '='); + ++cursor; + + getToken(cursor, ',', &pair.value, &next); + + cursor = next; + + list[optionCount++] = pair; + + if (*cursor != '\0') { + assert(*cursor == ','); + ++cursor; + } + } + list[optionCount].name = NULL; + list[optionCount].value = NULL; + + *listP = list; +} + + + +/*------------------------------------------------------------------------ + | NAME optExecute + | + | FUNCTION Perform the action of an option. + | + | INPUT opt element in array of defined options that + | applies to this option + | arg argument to option, if it applies. + | lng was the option given as a long option? + | + | RETURNS Nothing. Aborts in case of error. + */ +static void +optExecute(optEntry const opt, char *arg, int lng) +{ + if (opt.specified) + (*(opt.specified))++; + + switch (opt.type) { + case OPT_FLAG: + if (opt.arg) + *((int *) opt.arg) = 1; + break; + + case OPT_STRING: + if (opt.arg) + *((char **) opt.arg) = arg; + break; + + case OPT_INT: + case OPT_LONG: { + long tmp; + char *e; + + if (arg == NULL) + optFatal("internal error: optExecute() called with NULL argument " + "'%s'", optString(opt, lng)); + tmp = strtol(arg, &e, 10); + if (*e) + optFatal("invalid number `%s'", arg); + if (errno == ERANGE + || (opt.type == OPT_INT && (tmp > INT_MAX || tmp < INT_MIN))) + optFatal("number `%s' to `%s' out of range", + arg, optString(opt, lng)); + if (opt.type == OPT_INT) { + *((int *) opt.arg) = (int) tmp; + } else /* OPT_LONG */ { + if (opt.arg) + *((long *) opt.arg) = tmp; + } + } break; + + case OPT_UINT: + case OPT_ULONG: { + unsigned long tmp; + char *e; + + if (arg == NULL) + optFatal("internal error: optExecute() called with NULL argument " + "'%s'", optString(opt, lng)); + tmp = strtoul(arg, &e, 10); + if (*e) + optFatal("invalid number `%s'", arg); + if (errno == ERANGE + || (opt.type == OPT_UINT && tmp > UINT_MAX)) + optFatal("number `%s' to `%s' out of range", + arg, optString(opt, lng)); + if (opt.type == OPT_UINT) { + if (opt.arg) + *((unsigned *) opt.arg) = (unsigned) tmp; + } else /* OPT_ULONG */ { + if (opt.arg) + *((unsigned long *) opt.arg) = tmp; + } + } break; + case OPT_FLOAT: { + float tmp; + char *e; + + if (arg == NULL) + optFatal("internal error: optExecute() called with NULL argument " + "'%s'", optString(opt, lng)); + tmp = strtod(arg, &e); + if (*e) + optFatal("invalid floating point number `%s'", arg); + if (errno == ERANGE) + optFatal("floating point number `%s' to `%s' out of range", + arg, optString(opt, lng)); + if (opt.arg) + *((float *) opt.arg) = tmp; + } break; + case OPT_NAMELIST: { + if (arg == NULL) + optFatal("internal error: optExecute() called with NULL argument " + "'%s'", optString(opt, lng)); + + if (opt.arg) + parseNameList(arg, (struct optNameValue **)opt.arg); + + } break; + default: + break; + } +} + + + +/*-----------------------------------------------------------------------+ +| PUBLIC FUNCTIONS | ++-----------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------ + | NAME optSetFatalFunc + | + | FUNCTION Set function used to display error message and exit. + | + | SYNOPSIS #include "shhopt.h" + | void optSetFatalFunc(void (*f)(const char *, ...)); + | + | INPUT f function accepting printf()'like parameters, + | that _must_ abort the program. + */ +void +optSetFatalFunc(void (*f)(const char *, ...)) +{ + optFatal = f; +} + + +/*------------------------------------------------------------------------ + | NAME optParseOptions + | + | FUNCTION Parse commandline options. + | + | SYNOPSIS #include "shhopt.h" + | void optParseOptions(int *argc, char *argv[], + | optStruct opt[], int allowNegNum); + | + | INPUT argc Pointer to number of options. + | argv Array of option-/argument-strings. + | opt Array of possible options. + | allowNegNum + | a negative number is not to be taken as + | an option. + | + | OUTPUT argc new argument count. + | argv array with arguments removed. + | + | RETURNS Nothing. Aborts in case of error. + | + | DESCRIPTION This function checks each option in the argv-array + | against strings in the opt-array, and `executes' any + | matching action. Any arguments to the options are + | extracted and stored in the variables or passed to + | functions pointed to by entries in opt. + | + | Options and arguments used are removed from the argv- + | array, and argc is decreased accordingly. + | + | Any error leads to program abortion. + */ +void +optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum) +{ + int ai, /* argv index. */ + optarg, /* argv index of option argument, or -1 if none. */ + mi, /* Match index in opt. */ + done; + char *arg, /* Pointer to argument to an option. */ + *o, /* pointer to an option character */ + *p; + + optEntry *opt_table; /* malloc'ed array */ + + opt_table = optStructTblToEntryTbl(opt); + if (opt_table == NULL) + optFatal("Memory allocation failed (trying to allocate space for " + "new-format option table)"); + + /* + * Loop through all arguments. + */ + for (ai = 0; ai < *argc; ) { + /* + * "--" indicates that the rest of the argv-array does not + * contain options. + */ + if (strcmp(argv[ai], "--") == 0) { + argvRemove(argc, argv, ai); + break; + } + + if (allowNegNum && argv[ai][0] == '-' && ISDIGIT(argv[ai][1])) { + ++ai; + continue; + } else if (strncmp(argv[ai], "--", 2) == 0) { + /* long option */ + /* find matching option */ + if ((mi = optMatch(opt_table, argv[ai] + 2, 1)) < 0) + optFatal("unrecognized option `%s'", argv[ai]); + + /* possibly locate the argument to this option. */ + arg = NULL; + if ((p = strchr(argv[ai], '=')) != NULL) + arg = p + 1; + + /* does this option take an argument? */ + optarg = -1; + if (optNeedsArgument(opt_table[mi])) { + /* option needs an argument. find it. */ + if (!arg) { + if ((optarg = ai + 1) == *argc) + optFatal("option `%s' requires an argument", + optString(opt_table[mi], 1)); + arg = argv[optarg]; + } + } else { + if (arg) + optFatal("option `%s' doesn't allow an argument", + optString(opt_table[mi], 1)); + } + optExecute(opt_table[mi], arg, 1); + /* remove option and any argument from the argv-array. */ + if (optarg >= 0) + argvRemove(argc, argv, ai); + argvRemove(argc, argv, ai); + } else if (*argv[ai] == '-') { + /* A dash by itself is not considered an option. */ + if (argv[ai][1] == '\0') { + ++ai; + continue; + } + /* Short option(s) following */ + o = argv[ai] + 1; + done = 0; + optarg = -1; + while (*o && !done) { + /* find matching option */ + if ((mi = optMatch(opt_table, o, 0)) < 0) + optFatal("unrecognized option `-%c'", *o); + + /* does this option take an argument? */ + optarg = -1; + arg = NULL; + if (optNeedsArgument(opt_table[mi])) { + /* option needs an argument. find it. */ + arg = o + 1; + if (!*arg) { + if ((optarg = ai + 1) == *argc) + optFatal("option `%s' requires an argument", + optString(opt_table[mi], 0)); + arg = argv[optarg]; + } + done = 1; + } + /* perform the action of this option. */ + optExecute(opt_table[mi], arg, 0); + ++o; + } + /* remove option and any argument from the argv-array. */ + if (optarg >= 0) + argvRemove(argc, argv, ai); + argvRemove(argc, argv, ai); + } else { + /* a non-option argument */ + ++ai; + } + } + free(opt_table); +} + + +static void +parse_short_option_token(char *argv[], const int argc, const int ai, + const optEntry opt_table[], + int * const tokens_consumed_p) { + + char *o; /* A short option character */ + char *arg; + int mi; /* index into option table */ + unsigned char processed_arg; /* boolean */ + /* We processed an argument to one of the one-character options. + This necessarily means there are no more options in this token + to process. + */ + + *tokens_consumed_p = 1; /* initial assumption */ + + o = argv[ai] + 1; + processed_arg = 0; /* initial value */ + while (*o && !processed_arg) { + /* find matching option */ + if ((mi = optMatch(opt_table, o, 0)) < 0) + optFatal("unrecognized option `-%c'", *o); + + /* does this option take an argument? */ + if (optNeedsArgument(opt_table[mi])) { + /* option needs an argument. find it. */ + arg = o + 1; + if (!*arg) { + if (ai + 1 >= argc) + optFatal("option `%s' requires an argument", + optString(opt_table[mi], 0)); + arg = argv[ai+1]; + (*tokens_consumed_p)++; + } + processed_arg = 1; + } else + arg = NULL; + /* perform the action of this option. */ + optExecute(opt_table[mi], arg, 0); + ++o; + } +} + + + +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) { + + char *equals_arg; + /* The argument of an option, included in the same token, after a + "=". NULL if no "=" in the token. + */ + char *arg; /* The argument of an option; NULL if none */ + int mi; /* index into option table */ + + /* The current token is an option, and its name starts at + Index 'namepos' in the argument. + */ + *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]); + + /* possibly locate the argument to this option. */ + { + char *p; + if ((p = strchr(argv[ai], '=')) != NULL) + equals_arg = p + 1; + else + equals_arg = NULL; + } + /* does this option take an argument? */ + if (optNeedsArgument(opt_table[mi])) { + /* option needs an argument. find it. */ + if (equals_arg) + arg = equals_arg; + else { + if (ai + 1 == argc) + optFatal("option `%s' requires an argument", + optString(opt_table[mi], 1)); + arg = argv[ai+1]; + (*tokens_consumed_p)++; + } + } else { + if (equals_arg) + optFatal("option `%s' doesn't allow an argument", + optString(opt_table[mi], 1)); + else + arg = NULL; + } + /* perform the action of this option. */ + optExecute(opt_table[mi], arg, 1); +} + + + +/*------------------------------------------------------------------------ + | NAME optParseOptions2 + | + | FUNCTION Parse commandline options. + | + | SYNOPSIS #include "shhopt.h" + | void optParseOptions2(int *argc, char *argv[], + | optStruct2 opt, unsigned long flags); + | + | INPUT argc Pointer to number of options. + | argv Array of option-/argument-strings. + | opt Structure describing option syntax. + | flags Result is undefined if not zero. + | For future expansion. + | + | OUTPUT argc new argument count. + | argv array with arguments removed. + | + | RETURNS Nothing. Aborts in case of error. + | + | DESCRIPTION This function checks each option in the argv-array + | against strings in the opt-array, and `executes' any + | matching action. Any arguments to the options are + | extracted and stored in the variables or passed to + | functions pointed to by entries in opt. + | + | This differs from optParseOptions in that it accepts + | long options with just one hyphen and doesn't accept + | any short options. It also has accomodations for + | future expansion. + | + | Options and arguments used are removed from the argv- + | array, and argc is decreased accordingly. + | + | Any error leads to program abortion. + */ +void +optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, + const unsigned long flags) +/*---------------------------------------------------------------------------- + This does the same thing as optParseOptions3(), except that there is no + "specified" return value. + + This function exists for backward compatibility. +-----------------------------------------------------------------------------*/ + +{ + optStruct3 opt3; + + opt3.short_allowed = opt.short_allowed; + opt3.allowNegNum = opt.allowNegNum; + opt3.opt_table = optStructTblToEntryTbl(opt.opt_table); + + if (opt3.opt_table == NULL) + optFatal("Memory allocation failed (trying to allocate space for " + "new-format option table)"); + + optParseOptions3(argc_p, argv, opt3, sizeof(opt3), flags); + + free(opt3.opt_table); +} + + + + +static void +zero_specified(optEntry opt_table[]) { +/*---------------------------------------------------------------------------- + Set all the "number of times specified" return values identified in the + option table opt_table[] to zero. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; opt_table[i].type != OPT_END; i++) { + if (opt_table[i].specified) + *(opt_table[i].specified) = 0; + } +} + + + +/*------------------------------------------------------------------------ + | NAME optParseOptions3 + | + | FUNCTION Parse commandline options. + | + | INPUT argc Pointer to number of options. + | argv Array of option-/argument-strings. + | opt Structure describing option syntax. + | optStructSize + | Size of "opt" (since the caller may be older + | than this function, it may be using a structure + | with fewer fields than exist today. We use this + | parameter to handle those older callers). + | flags Result is undefined if not zero. + | For future expansion. + | + | OUTPUT argc new argument count. + | argv array with arguments removed. + | + | Areas pointed to by pointers in 'opt' get updated with + | option values and counts. + | + | RETURNS Nothing. Aborts in case of error. + | + | DESCRIPTION This function checks each option in the argv-array + | against strings in the opt-array, and `executes' any + | matching action. Any arguments to the options are + | extracted and stored in the variables or passed to + | functions pointed to by entries in opt. + | + | This differs from optParseOptions in that it accepts + | long options with just one hyphen and doesn't accept + | any short options. It also has accomodations for + | future expansion. + | + | Options and arguments used are removed from the argv- + | array, and argc is decreased accordingly. + | + | Any error leads to program abortion. + */ +void +optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, + const unsigned int optStructSize, const unsigned long flags) +{ + int ai; /* argv index. */ + int tokens_consumed; + unsigned char no_more_options; /* boolean */ + /* We've encountered the "no more options" token */ + + zero_specified(opt.opt_table); + + /* + * Loop through all arguments. + */ + no_more_options = 0; /* initial value */ + for (ai = 0; ai < *argc_p; ) { + if (no_more_options) + /* Can't be an option -- there aren't any more */ + ai++; + else if (argv[ai][0] != '-') + /* Can't be an option -- doesn't start with a dash */ + ai++; + else { + /* It starts with a dash -- could be an option */ + if (argv[ai][1] == '\0') { + /* A dash by itself is not considered an option. */ + ++ai; + tokens_consumed = 0; + } else if (opt.allowNegNum && ISDIGIT(argv[ai][1])) { + /* It's a negative number parameter, not an option */ + ++ai; + tokens_consumed = 0; + } else if (argv[ai][1] == '-') { + /* It starts with -- */ + if (argv[ai][2] == '\0') { + /* The entire thing is "--". That means no more options */ + tokens_consumed = 1; + no_more_options = 1; + } else + /* It's an option that starts with "--" */ + parse_long_option(argv, *argc_p, ai, 2, + opt.opt_table, &tokens_consumed); + } else { + if (opt.short_allowed) { + /* It's a cluster of (one or more) short options */ + parse_short_option_token(argv, *argc_p, ai, + opt.opt_table, &tokens_consumed); + } else { + /* It's a long option that starts with "-" */ + parse_long_option(argv, *argc_p, ai, 1, + opt.opt_table, &tokens_consumed); + } + + } + /* remove option and any argument from the argv-array. */ + { + int i; + for (i = 0; i < tokens_consumed; i++) + argvRemove(argc_p, argv, ai); + } + } + } +} + + + +void +optDestroyNameValueList(struct optNameValue * const list) { + + unsigned int i; + + for (i = 0; list[i].name; ++i) { + strfree(list[i].name); + strfree(list[i].value); + } + + free(list); +} diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h new file mode 100644 index 00000000..fd15b53c --- /dev/null +++ b/lib/util/shhopt.h @@ -0,0 +1,240 @@ +/*============================================================================== +HERE IS AN EXAMPLE OF THE USE OF SHHOPT: + + +#include +int +main ( int argc, char **argv ) { + + int help_flag = 7; + unsigned int help_spec =7; + unsigned int height_spec =7; + unsigned int name_spec= 7; + char *name= "initial"; + int height=7; + int verbose_flag=7; + int debug_flag=7; + struct optNameValue * optlist; + + optStruct3 opt; + unsigned int option_def_index = 0; + optEntry *option_def = malloc(100*sizeof(option_def[0])); + + OPTENT3(0, "help", OPT_FLAG, &help_flag, &help_spec, 0); + OPTENT3(0, "height", OPT_INT, &height, &height_spec, 0); + OPTENT3('n', "name", OPT_STRING, &name, &name_spec, 0); + OPTENT3('v', "verbose", OPT_FLAG, &verbose_flag, NULL, 0); + OPTENT3('g', "debug", OPT_FLAG, &debug_flag, NULL, 0); + OPTENT3(0, "options", OPT_NAMELIST, &optlist, NULL, 0); + + opt.opt_table = option_def; + opt.short_allowed = 1; + opt.allowNegNum = 1; + + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + + + printf("argc=%d\n", argc); + printf("help_flag=%d\n", help_flag); + printf("help_spec=%d\n", help_spec); + printf("height=%d\n", height); + printf("height_spec=%d\n", height_spec); + printf("name='%s'\n", name); + printf("name_spec=%d\n", name_spec); + printf("verbose_flag=%d\n", verbose_flag); + printf("debug_flag=%d\n", verbose_flag); + + if (optlist) { + unsigned int i; + while (optlist[i].name) { + printf("option '%s' = '%s'\n", optlist[i].name, optlist[i].value); + ++i; + } + optDestroyNameValueList(optlist); + } else + printf("No -options\n"); +} + +Now run this program with something like + + myprog -vg --name=Bryan --hei 4 "My first argument" --help + + or do it with opt.short_allowed=0 and + + myprog -v /etc/passwd -name=Bryan --hei=4 + + +========================================================================*/ + + +#ifndef SHHOPT_H +#define SHHOPT_H + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +/* constants for recognized option types. */ +typedef enum { + OPT_END, /* nothing. used as ending element. */ + OPT_FLAG, /* no argument following. sets variable to 1. */ + OPT_STRING, /* string argument. */ + OPT_INT, /* signed integer argument. */ + OPT_UINT, /* unsigned integer argument. */ + OPT_LONG, /* signed long integer argument. */ + OPT_ULONG, /* unsigned long integer argument. */ + OPT_FLOAT, /* floating point argument. */ + OPT_NAMELIST /* list like "key1=val1,key2=val2" */ +} optArgType; + + +typedef struct { + /* This structure describes a single program option in a form for + use by the optParseOptions() or optParseOptions2() function. + */ + char shortName; /* short option name. */ + const char *longName; /* long option name, not including '--'. */ + optArgType type; /* option type. */ + void *arg; /* pointer to variable to fill with argument, + * or pointer to function if type == OPT_FUNC. */ + int flags; /* modifier flags. */ +} optStruct; + +typedef struct { + /* This structure describes a single program option in a form for + use by the optParseOptions3() function. + */ + char shortName; /* short option name. */ + const char *longName; /* long option name, not including '--' or '-' */ + optArgType type; /* option type. */ + void *arg; + /* pointer to variable in which to return option's argument (or TRUE + if it's a flag option), or pointer to function if + type == OPT_FUNC. If the option is specified multiple times, only + the rightmost one affects this return value. + */ + unsigned int *specified; + /* pointer to variable in which to return the number of times that + the option was specified. If NULL, don't return anything. + */ + int flags; /* modifier flags. */ +} optEntry; + + +typedef struct { + /* This structure describes the options of a program in a form for + use with the optParseOptions2() function. + */ + unsigned char short_allowed; /* boolean */ + /* The syntax may include short (i.e. one-character) options. + These options may be stacked within a single token (e.g. + -abc = -a -b -c). If this value is not true, the short option + member of the option table entry is meaningless and long + options may have either one or two dashes. + */ + unsigned char allowNegNum; /* boolean */ + /* Anything that starts with - and then a digit is a numeric + parameter, not an option + */ + optStruct *opt_table; +} optStruct2; + +typedef struct { + /* Same as optStruct2, but for optParseOptions3() */ + unsigned char short_allowed; + unsigned char allowNegNum; + optEntry *opt_table; +} optStruct3; + + +/* You can use OPTENTRY to assign a value to a dynamically or automatically + allocated optStruct structure with minimal typing and easy readability. + + Here is an example: + + unsigned int option_def_index = 0; + optStruct *option_def = malloc(100*sizeof(optStruct)); + OPTENTRY('h', "help", OPT_FLAG, &help_flag, 0); + OPTENTRY(0, "alphaout", OPT_STRING, &alpha_filename, 0); +*/ + +/* If you name your variables option_def and option_def_index like in the + example above, everything's easy. If you want to use OPTENTRY with other + variables, define macros OPTION_DEF and OPTION_DEF_INDEX before calling + OPTENTRY. +*/ + +#ifndef OPTION_DEF +#define OPTION_DEF option_def +#endif +#ifndef OPTION_DEF_INDEX +#define OPTION_DEF_INDEX option_def_index +#endif + +#define OPTENTRY(shortvalue,longvalue,typevalue,outputvalue,flagvalue) {\ + OPTION_DEF[OPTION_DEF_INDEX].shortName = (shortvalue); \ + OPTION_DEF[OPTION_DEF_INDEX].longName = (longvalue); \ + OPTION_DEF[OPTION_DEF_INDEX].type = (typevalue); \ + OPTION_DEF[OPTION_DEF_INDEX].arg = (outputvalue); \ + OPTION_DEF[OPTION_DEF_INDEX].flags = (flagvalue); \ + OPTION_DEF[++OPTION_DEF_INDEX].type = OPT_END; \ + } + +/* 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). + + Here is an example: + + unsigned int option_def_index = 0; + optEntry *option_def = malloc(100*sizeof(optEntry)); + OPTENT3('h', "help", OPT_FLAG, &help_flag, 0); + OPTENT3(0, "alphaout", OPT_STRING, &alpha_filename, 0); +*/ + +#define OPTENT3(shortvalue,longvalue,typevalue,outputvalue,specifiedvalue, \ + flagvalue) {\ + OPTION_DEF[OPTION_DEF_INDEX].specified = (specifiedvalue); \ + OPTENTRY(shortvalue, longvalue, typevalue, outputvalue, flagvalue) \ + } + + +struct optNameValue { + const char * name; + const char * value; +}; + + + +void optSetFatalFunc(void (*f)(const char *, ...)); +void optParseOptions(int *argc, char *argv[], + optStruct opt[], int allowNegNum); +void +optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, + const unsigned long flags); +void +optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, + const unsigned int optStructSize, const unsigned long flags); + +void +optDestroyNameValueList(struct optNameValue * const list); + +#ifdef __cplusplus +} +#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/testnstring.c b/lib/util/testnstring.c new file mode 100644 index 00000000..87e6139e --- /dev/null +++ b/lib/util/testnstring.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +#include "nstring.h" + +#define true (1) +#define false (0) + +int +main(int argc, char **argv) { + + char snprintfNResult[80]; + const char * asprintfNResult; + + printf("Hello world.\n"); + + snprintfN(snprintfNResult, 19, "snprintfN test truncated"); + + printf("snprintfN result='%s'\n", snprintfNResult); + + asprintfN(&asprintfNResult, "asprintf'ed string %d %u %s", + 5, 89, "substring"); + + printf("asprintfN result='%s'\n", asprintfNResult); + + strfree(asprintfNResult); + return 0; +} diff --git a/lib/util/wordaccess.h b/lib/util/wordaccess.h new file mode 100644 index 00000000..28963aee --- /dev/null +++ b/lib/util/wordaccess.h @@ -0,0 +1,65 @@ +#ifndef WORDACCESS_H_INCLUDED +#define WORDACCESS_H_INCLUDED + +/* These are facilities for accessing data in C programs in ways that + exploit the way the machine defines words in order to squeeze out + speed and CPU efficiency. + + In particular, routines in this file exploit the endianness of the + machine and use explicit machine instructions to access C + variables. + + A word is the amount of data that fits in a register; the amount of + data that a single machine instruction can process. For example, + on IA32, a word is 32 bits because a single load or store + instruction moves that many bits and a single add instruction + operates on that many bits. + + + These facilities revolve around two data types: wordInt and + wordIntBytes. + + wordint is an unsigned integer with precision (size) of one word. + It is just the number -- nothing is implied about how it is + represented in memory. + + wordintBytes is an array of bytes that represent a word-sized + unsigned integer. x[0] is the high order 8 digits of the binary + coding of the integer, x[1] the next highest 8 digits, etc. + Note that it has big-endian form, regardless of what endianness the + underlying machine uses. + + The actual size of word differs by machine. Usually it is 32 or 64 + bits. Logically it can be as small as one byte. Fixed bit sequences + in each program impose a lower limit of word width. For example, the + longest bit sequence in pbmtog3 has 13 bits, so an 8-bit word won't + work with that. + + We also assume that a char is 8 bits. +*/ +#if (!defined(WORDACCESS_GENERIC) \ + && defined(__GNUC__) && defined(__GLIBC__) \ + && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) ) + + #if BYTE_ORDER==BIG_ENDIAN /* defined by GCC */ + + #include "wordaccess_gcc3_be.h" + + #elif defined(__ia64__) || defined(__amd64__) || defined(__x86_64__) + /* all these macros are defined by GCC */ + + #include "wordaccess_64_le.h" + + #else + + #include "wordaccess_gcc3_le.h" + + #endif + +#else + + #include "wordaccess_generic.h" + +#endif + +#endif diff --git a/lib/util/wordaccess_64_le.h b/lib/util/wordaccess_64_le.h new file mode 100644 index 00000000..2343b6d4 --- /dev/null +++ b/lib/util/wordaccess_64_le.h @@ -0,0 +1,52 @@ +/*============================================================================= + 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 /* See note above on bswap_64 */ + +typedef uint64_t wordint; +typedef unsigned char wordintBytes[sizeof(wordint)]; + +static __inline__ wordint +bytesToWordint(wordintBytes bytes) { + return ((wordint) bswap_64(*(wordint *)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((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"); +} diff --git a/lib/util/wordaccess_gcc3_be.h b/lib/util/wordaccess_gcc3_be.h new file mode 100644 index 00000000..6f5d86fc --- /dev/null +++ b/lib/util/wordaccess_gcc3_be.h @@ -0,0 +1,40 @@ +/*============================================================================= + This file is the part of wordaccess.h for use under these + conditions: + + * 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; +typedef unsigned char wordintBytes[sizeof(wordint)]; + +static __inline__ wordint +bytesToWordint(wordintBytes bytes) { + return *((wordint *)bytes); +} + + + +static __inline__ void +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 new file mode 100644 index 00000000..7db218db --- /dev/null +++ b/lib/util/wordaccess_gcc3_le.h @@ -0,0 +1,54 @@ +/*============================================================================= + + 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 +#include + +/* + 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 new file mode 100644 index 00000000..7f27ef74 --- /dev/null +++ b/lib/util/wordaccess_generic.h @@ -0,0 +1,89 @@ +/*============================================================================= + + This file is the part of wordaccess.h for use under these + conditions: + + * Compilers other than GCC + * GCC before version 3.4 + * c libraries other than Glibc + * Specified by the user with WORDACCESS_GENERIC +=============================================================================*/ + +typedef uint32_t wordint; +typedef unsigned char wordintBytes[sizeof(wordint)]; + +static __inline__ wordint +bytesToWordint(wordintBytes const bytes) { + wordint retval; + unsigned int i; + + /* 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; +} + + + +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; +} + + + +static __inline__ unsigned int +wordintClz(wordint const x) { + return clz32(x); +} -- cgit 1.4.1