advanced/0000755000175000001440000000000012347716326011477 5ustar ericusersadvanced/srcdir.mk0000644000175000001440000000136112347602011013300 0ustar ericusers# This is the version of srcdir.mk that gets used when you build in # the source tree. In contrast, when you build in a separate build tree, # you use a version of srcdir.mk that Configure created, which sets SRCDIR # to the location of the source tree. # This is designed to be included by the top level make file, with # SRCDIR being passed to all the submakes. That means that when you # build in a separate build tree, you must build from the top; you # can't go into a subdirectory and type "make". When you build in the # _source_ tree, local makes work because every local make file checks # whether SRCDIR is set, and if it isn't, sets it with the assumption # that the build directory and source directory are the same. SRCDIR = $(CURDIR) advanced/common.mk0000644000175000001440000006070312347602011013307 0ustar ericusers# This is a make file inclusion, to be included in all the # Netpbm make files. # This file is meant to contain rules that are substantially the same # in each of the pbm, pgm, ppm, and pnm subdirectory makes, to avoid # duplication of effort. # The following variables must be set in any make file that uses these # rules: # # SRCDIR: The top level directory of Netpbm source code. # BUILDDIR: The top level directory into which Netpbm is built (built, # not installed). # SUBDIR: The directory, relative to BUILDDIR, of the current directory. # It is also the directory, relative to SRCDIR, of source directory that # corresponds to the current directory. Note that you build in the # current directory, using files from the source directory. # SUBDIRS: list of subdirectories in which certain targets (e.g. 'clean') # should be made recursively. # PKGDIR_DEFAULT: The place to put the packaged stuff for 'make package' # if the user doesn't put "pkgdir=" on the Make command line. # PKGMANDIR: The subdirectory (e.g. "man" or "share/man" of the package # directory root in which man pages should be packaged. # OBJECTS: .o files to be built from .c files with the standard rule. # PORTBINARIES: list of conventional executables to be built with the standard # rule # MATHBINARIES: obsolete. # DATAFILES: list of files that should be installed in the "data" directory. # NETPBMLIBSUFFIX: the suffix, e.g. "so" for the main libraries we build, # whatever type they may be. # STATICLIBSUFFIX: the suffix, e.g. "a" on a static library. This need # not be defined if the user doesn't want to build a static libraries in # addition to the main libraries. # BINARIES: list of all the executables that need to be installed. # INSTALL: command to use to copy files to where they belong # INSTALL_PERM_BIN: file permissions for installed binaries # INSTALL_PERM_LIB: ...same for libraries # INSTALL_PERM_MAN: ...same for man pages # MERGE_OBJECTS: list of object files that go into the merged executable # from the current directory (not subdirectories). All of these are to # be built with the standard rule for merged objects. These names are # relative to the current make directory (must not start with / ). # MERGEBINARIES: list of the programs that, in a merge build, are invoked # via the merged Netpbm program # CC: C compiler command # CFLAGS_CONFIG: C compiler options from config.mk. # CFLAGS_TARGET: C compiler options for a particular target # LD: linker command # LINKERISCOMPILER: 'Y' if the linker invoked by LD is actually a compiler # front end, so takes linker options in a different format # LIBS or LOADLIBES: names of libraries to be added to all links # COMP_INCLUDES: Compiler option string to establish the search path for # component-specific include files when compiling things or computing # dependencies (make dep). Header files from this part of the search # path take precedence over general Netpbm header files and external # library header files. # EXTERN_INCLUDES: Like COMP_INCLUDES, but for external libraries, e.g. # libjpeg. All header files from the Netpbm source tree take precedence # over these. # In addition, there is CADD, which is extra C compilation options and # is intended to be set on a make command line (e.g. 'make CADD=-g') # for options that apply just to a particular build. # In addition, there is CFLAGS, which is extra C compilation options and is # expected to be set via the make command line for a particular build. # Likewise, LDFLAGS for link-edit options. # In addition, there is CFLAGS_PERSONAL, which is extra C # compilation options and is expected to be set via environment variable # for options that are particular to the person doing the build and not # specific to Netpbm. include $(SRCDIR)/version.mk # .DELETE_ON_ERROR is a special predefined Make target that says to delete # the target if a command in the rule for it fails. That's important, # because we don't want a half-made target sitting around looking like it's # fully made. .DELETE_ON_ERROR: # -I importinc/netpbm is a backward compatibility thing. Really, the source # file should refer to e.g. "netpbm/pam.h" but for historical reasons, most # refer to "pam.h" and we'll probably never have the energy to convert them # all. The reason the file exists as importinc/netpbm/pam.h rather than just # importinc/pam.h (as it did originally) is that it lives on a user's system # as , and therefore all _exported_ header files do say # ". NETPBM_INCLUDES := -Iimportinc -Iimportinc/netpbm -I$(SRCDIR)/$(SUBDIR) # -I. is needed when builddir != srcdir INCLUDES = -I. $(COMP_INCLUDES) $(NETPBM_INCLUDES) $(EXTERN_INCLUDES) ifeq ($(NETPBMLIBTYPE),unixstatic) NETPBMLIBFNAME = libnetpbm.$(STATICLIBSUFFIX) else NETPBMLIBFNAME = $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).$(NETPBMLIBSUFFIX) endif NETPBMLIB = $(BUILDDIR)/lib/$(NETPBMLIBFNAME) BUNDLED_URTLIB = $(BUILDDIR)/urt/librle.a # LIBS and LOADLIBES are commonly set as environment variables. # LOADLIBES is used by GNU Make's implicit .c->.o rule. LIBS is used by # GNU Autoconf. LDLIBS = $(LOADLIBES) $(LIBS) # 'pkgdir' is meant to be set on the make command line. Results are # disastrous if PKGDIR is a relative directory, and I don't know any # way to detect that case and fail, so I just add a '/' to the front # if it isn't already there. ifneq ($(pkgdir)x,x) PKGDIR = $(patsubst //%,/%, /$(pkgdir)) else PKGDIR = $(PKGDIR_DEFAULT) endif # 'resultdir', like 'pkgdir' is meant to be supplied from the make # command line. Unlike 'pkgdir' we allow relative paths. ifneq ($(resultdir)x,x) RESULTDIR = $(resultdir) else RESULTDIR = $(RESULTDIR_DEFAULT) endif #=========================================================================== # We build a directory full of symbolic links to the intra-Netpbm public # header files just so the compile commands don't have to be littered # with long -I's. #=========================================================================== # Note that the "root" headers are in the root of the build tree, not # the source tree. All generated headers are in the root directory and # all root directory headers are generated. IMPORTINC_ROOT_HEADERS := pm_config.h inttypes_netpbm.h version.h IMPORTINC_LIB_HEADERS := \ pm.h pbm.h pgm.h ppm.h pnm.h pam.h bitio.h pbmfont.h ppmcmap.h \ pammap.h colorname.h ppmfloyd.h ppmdraw.h pm_system.h ppmdfont.h \ pm_gamma.h lum.h dithers.h pamdraw.h IMPORTINC_LIB_UTIL_HEADERS := \ bitarith.h bitreverse.h filename.h intcode.h floatcode.h io.h \ matrix.h mallocvar.h \ nsleep.h nstring.h pm_c_util.h shhopt.h token.h \ wordaccess.h wordaccess_generic.h wordaccess_64_le.h \ wordaccess_be_aligned.h wordaccess_be_unaligned.h \ wordintclz.h IMPORTINC_HEADERS := \ $(IMPORTINC_ROOT_HEADERS) \ $(IMPORTINC_LIB_HEADERS) \ $(IMPORTINC_LIB_UTIL_HEADERS) IMPORTINC_ROOT_FILES := $(IMPORTINC_ROOT_HEADERS:%=importinc/netpbm/%) IMPORTINC_LIB_FILES := $(IMPORTINC_LIB_HEADERS:%=importinc/netpbm/%) IMPORTINC_LIB_UTIL_FILES := $(IMPORTINC_LIB_UTIL_HEADERS:%=importinc/netpbm/%) importinc: \ $(IMPORTINC_ROOT_FILES) \ $(IMPORTINC_LIB_FILES) \ $(IMPORTINC_LIB_UTIL_FILES) \ # The reason we mkdir importinc/netpbm every time instead of just having # importinc depend on it and a rule to make it is that as a dependency, it # would force importinc to rebuild when importinc/netpbm has a more recent # modification date, which it sometimes would. $(IMPORTINC_ROOT_FILES):importinc/netpbm/%:$(BUILDDIR)/% mkdir -p importinc/netpbm rm -f $@ $(SYMLINK) $< $@ $(IMPORTINC_LIB_FILES):importinc/netpbm/%:$(SRCDIR)/lib/% mkdir -p importinc/netpbm rm -f $@ $(SYMLINK) $< $@ $(IMPORTINC_LIB_UTIL_FILES):importinc/netpbm/%:$(SRCDIR)/lib/util/% mkdir -p importinc/netpbm rm -f $@ $(SYMLINK) $< $@ # We build the symbolic links to header files in the current directory # just so the compile commands don't have to be littered with -I's. bmp.h tga.h:%:$(SRCDIR)/converter/% rm -f $@ $(SYMLINK) $< $@ ifneq ($(OMIT_VERSION_H_RULE),1) $(BUILDDIR)/version.h: $(MAKE) -C $(dir $@) $(notdir $@) endif ifneq ($(OMIT_CONFIG_RULE),1) $(BUILDDIR)/config.mk: $(SRCDIR)/config.mk.in $(MAKE) -C $(dir $@) $(notdir $@) $(BUILDDIR)/pm_config.h: $(MAKE) -C $(dir $@) $(notdir $@) endif ifneq ($(OMIT_INTTYPES_RULE),1) $(BUILDDIR)/inttypes_netpbm.h: $(MAKE) -C $(dir $@) -f $(SRCDIR)/GNUmakefile $(notdir $@) endif # Note that any time you do a make on a fresh Netpbm source tree, # Make notices that 'config.mk', which the make files include, does not # exist and runs the "config.mk" target, which runs Configure. # If the "config" target were to run Configure as well, it would get run # twice in a row if you did a 'make config' on a fresh Netpbm source tree. # But we don't want to make "config" just a no-op, because someone might # try it after config.mk already exists, in order to make a new # config.mk. Issuing a message as follows seems to make sense in # both cases. .PHONY: config config: @echo "To reconfigure the build, run 'configure'" # Rule to make C source from lex source. %.c:%.l $(LEX) -t $< >$(notdir $@) # Rule to make regular object files, e.g. pnmtojpeg.o. # The NDEBUG macro says to build code that assumes there are no bugs. # This makes the code go faster. The main thing it does is tell the C library # to make assert() a no-op as opposed to generating code to check the # assertion and crash the program if it isn't really true. You can add # -UNDEBUG (in any of various ways) to override this. # CFLAGS_ALL = \ -DNDEBUG $(CPPFLAGS) $(CFLAGS_CONFIG) $(CFLAGS_TARGET) $(CFLAGS_PERSONAL) $(CFLAGS) $(CADD) ifeq ($(WANT_SSE),Y) # The only two compilers we've seen that have the SSE capabilities that # WANT_SSE requests are GCC and Clang, and they both have these options and # require them in order for to compile. On some systems # (x86_64, in our experience), these options are default, but on more # traditional systems, they are not. Note: __SSE2__ macro tells whether # -msse2 is in effect. CFLAGS_SSE = -msse -msse2 else CFLAGS_SSE = endif $(OBJECTS): %.o: %.c importinc ############################################################################# # Note that the user may have configured -I options into CFLAGS or CPPFLAGS. # Note about -o: There used to be systems that couldn't handle a space # between flag and value. But we found a Solaris gcc on 2003.09.02 that # actually fails _without_ the space (it invokes Solaris 'as' with the # following command, which generates a "no input filename" error: # '/usr/ccs/bin/as -V -Qy -s -o/tmp/hello.o /var/tmp/ccpiNnia.s') # This rule has had the space since way before that, so it looks like # the space is no longer a problem for anyone. ############################################################################# # # We have to get this all on one line to make make messages neat $(CC) -c $(INCLUDES) $(CFLAGS_ALL) -o $@ $< # libopt is a utility program used in the make file below. LIBOPT = $(BUILDDIR)/buildtools/libopt ifneq ($(OMIT_BUILDTOOL_RULE),1) $(LIBOPT) $(TYPEGEN): $(BUILDDIR)/buildtools $(MAKE) -C $(dir $@) -f $(SRCDIR)/buildtools/Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) endif ifneq ($(OMIT_LIBRARY_RULE),1) $(NETPBMLIB): $(BUILDDIR)/lib $(MAKE) -C $(dir $@) -f $(SRCDIR)/lib/Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) endif ifneq ($(OMIT_URT_RULE),1) $(BUNDLED_URTLIB): $(BUILDDIR)/urt $(MAKE) -C $(dir $@) -f $(SRCDIR)/urt/Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) endif # Here are some notes from Nelson H. F. Beebe on April 16, 2002: # # There are at least three incompatible kinds of command-line options # that tell the compiler to instruct the linker to save library paths in # the executable: # # -Wl,-rpath,/path/to/dir gcc, g++, FreeBSD, SGI, Sun compilers # -rpath /path/to/dir Compaq/DEC, SGI compilers # -Rdir:dir:dir Portland Group, Sun compilers # # Notice that SGI and Sun support two such flavors. # # Plus, Scott Schwartz observed on March 25, 2003 that while his # compiler understands -Wl, his linker does not understand -rpath. # His compiler is "Sun WorkShop 6 update 2 C 5.3 2001/05/15". # # Plus, Mike Saunders found in December 2003 that his Solaris 8 system # (uname -a says 'SunOS cannonball.method.cx 5.8 Generic_108528-14 # sun4u sparc SUNW,Ultra-1') with Gcc 2.95.3 requires the syntax # # -Wl,-R,/path/to/dir # # This is apparently because Gcc invokes this linker for Saunders: # # ld: Software Generation Utilities - Solaris Link Editors: 5.8-1.273 # # I'd say there are also Solaris systems where Gcc invokes the GNU linker # and then the option would be -Wl,-rpath... # # The Sun Ld fails in a weird way when you pass it -rpath instead of -R: # # ld: Software Generation Utilities - Solaris Link Editors: 5.9-1.382 # ld: fatal: option -dn and -P are incompatible # # On IA32 Linux, at least, GNU ld takes -rpath. It also has a -R option, # but it is something else. # # Alan Fry and Snowcrash demonstrated in 2006.11 that neither -rpath # nor -R are recognized options on Mac OS X 'ld'. # # http://developer.apple.com/releasenotes/DeveloperTools/RN-dyld/index.html # says that on Mac OS X, libraries aren't searched for in directories, # but rather specified by full name, so that rpath doesn't make any # sense. On Mac OS X, you use -install_name when you linkedit shared # library S to give the complete installed name of S. This goes into # S so that when something linkedits with S, the complete installed # name of S goes into the object that uses S. ifeq ($(NEED_RUNTIME_PATH),Y) ifneq ($(NETPBMLIB_RUNTIME_PATH)x,x) ifeq ($(LINKERISCOMPILER),Y) # Before Netpbm 10.14 (March 2003), it looks like we used -R # instead of -Wl,-rpath on all but a few selected platforms as configured # by Configure. But that doesn't make sense, because we also used # LD=$(CC) always. Beebe's notes and Saunders' observation above # above indicate that we need # -Wl,... everywhere that a compiler is used, whether native or GNU, # to link. RPATH = -Wl,$(RPATHOPTNAME),$(NETPBMLIB_RUNTIME_PATH) else RPATH = $(RPATHOPTNAME)$(NETPBMLIB_RUNTIME_PATH) endif endif endif # Rules for conventional single-object file executables # Before Netpbm 10.21 (March 2004), we kept separate lists of binaries # that require the math library and those that don't, so the binaries # that don't need it wouldn't have to link it. But now libnetpbm # contains gamma correction routines, so it needs the math library, # and that means every Netpbm binary needs the math library, whether # it calls those routines or not. So we will phase out the separate # lists, and for now we treat them identically. # Note that GNU C library sometimes defines math functions as inline # functions, so linking the math library isn't really necessary. Late # model GNU C libraries do this only if you specify the -ffast-math # Gcc option (as told by the __FAST_MATH__ preprocessor macro). # Earlier ones do it regardless of __FAST_MATH__. MATHLIB ?= -lm # Note that LDFLAGS might contain -L options, so order is important. # LDFLAGS is commonly set as an environment variable. LDFLAGS_ALL = $(shell $(LIBOPT) $(NETPBMLIB)) \ $(LDFLAGS) $(LDLIBS) $(MATHLIB) $(RPATH) $(LADD) $(PORTBINARIES) $(MATHBINARIES): %: %.o $(NETPBMLIB) $(LIBOPT) $(LD) -o $@ $@.o $(LDFLAGS_ALL) # MERGE STUFF # .o2 is our suffix for an object file that has had it's main() changed # to e.g. main_pamcut(). We use them for the merge build. %.o2: %.c importinc # Note that the user may have configured -I options into CFLAGS. $(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) \ "-Dmain=main_$*" \ $(CFLAGS_MERGE) $(CFLAGS_PERSONAL) $(CADD) -o $@ $< # The "merge try list" is a file full of TRY macro invocations, one for # each Netpbm program in this directory or any subdirectory that can be # invoked via the merged Netpbm program. You will find it #included in # netpbm.c. mergetrylist: $(SUBDIRS:%=%/mergetrylist) cat /dev/null $(SUBDIRS:%=%/mergetrylist) >$@ $(SRCDIR)/buildtools/make_merge.sh $(MERGEBINARIES) >>$@ # The "merge list" is a list of all the object files from this directory and # any subdirectories that have to be linked into the merged Netpbm program. # They are absolute paths. mergelist: $(SUBDIRS:%=%/mergelist) $(MERGE_OBJECTS) cat /dev/null $(SUBDIRS:%=%/mergelist) >$@ echo $(MERGE_OBJECTS:%=$(CURDIR)/%) >>$@ # merge.o is the object file that contains all the code in this directory # that needs to be linked into the merged Netpbm program. This is not used # today, but some day it will be used instead of mergelist (above). ifeq ($(MERGE_OBJECTS),) MERGE_O_OBJECTS = empty.o else MERGE_O_OBJECTS = $(MERGE_OBJECTS) endif merge.o: $(SUBDIRS:%=%/merge.o) $(MERGE_O_OBJECTS) $(LDRELOC) -o $@ $^ # empty.o is useful in doing a merge build. Every directory must be able to # produce a merge.o file, but not every directory has anything to contribute # to the merge. empty.o: %.o: %.c $(CC) -c $(CFLAGS_PERSONAL) $(CADD) $< -o $@ empty.c: cat /dev/null >empty.c ########################################################################### # PACKAGING / INSTALLING ########################################################################### # Some maintenance notes about $(INSTALL): Some install programs can # install multiple files in one shot; others can take only one file at # a time. Some have a -c option; others ignore -c. Some can take # permissions in mnemonic form (u=rwx,go=rx); others can't, but all # take the encoded form (755). Some have a -d option to install # directories and never install them implicitly. Others create # directories only implicitly. Installbsd and OSF1 Install need a # space in "-m 755". Others don't care. 2000.05.17. OSF1 Install # takes only one parameter: the source file. It picks a destination # directory by default, or you can specify it with a -f option. # 2000.06.15 # DJGPP can do SYMKINKs for programs but not for ordinary files, so # it define SYMLINKEXE, other system don't need it ifeq ($(SYMLINKEXE)x,x) SYMLINKEXE := $(SYMLINK) endif # An implicit rule for $(PKGDIR)/% does not work because it causes Make # sometimes to believe the directory it creates from this rule is an unneeded # intermediate file and try to delete it later. So we explicitly list the # possible directories under $(PKGDIR): PKGMANSUBDIRS = man1 man3 man5 web PKGSUBDIRS = bin include include/netpbm lib link misc \ $(PKGMANSUBDIRS:%=$(PKGMANDIR)/%) $(PKGSUBDIRS:%=$(PKGDIR)/%): $(SRCDIR)/buildtools/mkinstalldirs $@ .PHONY: install.merge install.merge: $(NOMERGEBINARIES:%=%_installbin) $(SCRIPTS:%=%_installscript) \ $(MERGEBINARIES:%=%_installmerge) $(SUBDIRS:%=%/install.merge) %_installmerge: $(PKGDIR)/bin cd $(PKGDIR)/bin ; rm -f $(@:%_installmerge=%) cd $(PKGDIR)/bin ; $(SYMLINKEXE) netpbm$(EXE) $(@:%_installmerge=%) .PHONY: install.bin install.bin: $(BINARIES:%=%_installbin) $(SCRIPTS:%=%_installscript) \ $(SUBDIRS:%=%/install.bin) # Note that on Cygwin, the executables are actually pbmmake.exe, etc. # Make and Install know that pbmmake.exe counts as pbmmake. INSTALLBIN_TARGETS = $(BINARIES:%=%_installbin) netpbm_installbin .PHONY: $(INSTALLBIN_TARGETS) $(INSTALLBIN_TARGETS): $(PKGDIR)/bin $(INSTALL) -c $(STRIPFLAG) -m $(INSTALL_PERM_BIN) \ $(@:%_installbin=%) $< $(SCRIPTS:%=%_installscript): $(PKGDIR)/bin $(INSTALL) -c -m $(INSTALL_PERM_BIN) \ $(SRCDIR)/$(SUBDIR)/$(@:%_installscript=%) $< .PHONY: install.data install.data: $(DATAFILES:%=%_installdata) $(SUBDIRS:%=%/install.data) .PHONY: $(DATAFILES:%=%_installdata) $(DATAFILES:%=%_installdata): $(PKGDIR)/misc $(INSTALL) -c -m $(INSTALL_PERM_DATA) \ $(SRCDIR)/$(SUBDIR)/$(@:%_installdata=%) $< .PHONY: install.man install.man1 install.man3 install.man5 install.man: install.man1 install.man3 install.man5 \ $(SUBDIRS:%=%/install.man) MANUALS1 = $(BINARIES) $(SCRIPTS) PKGMANDIR = man install.man1: $(MANUALS1:%=%_installman1) install.man3: $(MANUALS3:%=%_installman3) install.man5: $(MANUALS5:%=%_installman5) install.manweb: $(MANUALS1:%=%_installmanweb) $(SUBDIRS:%=%/install.manweb) %_installman1: $(PKGDIR)/$(PKGMANDIR)/man1 perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman1=%) \ $(NETPBM_DOCURL) $< 1 $(MANPAGE_FORMAT) $(INSTALL_PERM_MAN) %_installman3: $(PKGDIR)/$(PKGMANDIR)/man3 perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman3=%) \ $(NETPBM_DOCURL) $< 3 $(MANPAGE_FORMAT) $(INSTALL_PERM_MAN) %_installman5: $(PKGDIR)/$(PKGMANDIR)/man5 perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman5=%) \ $(NETPBM_DOCURL) $< 5 $(MANPAGE_FORMAT) $(INSTALL_PERM_MAN) %_installmanweb: $(PKGDIR)/$(PKGMANDIR)/web echo $(NETPBM_DOCURL)$(@:%_installmanweb=%).html \ >$depend.mk endif # Note: if I stack all these subdirectory targets into one rule, I get # weird behavior where e.g. make install-nonmerge causes all the # %/install.bin makes to happen recursively, but then lib/install.lib # is considered up to date and doesn't get rebuilt. %/install.bin: $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) %/install.lib: $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) %/install.man: $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) %/install.manweb: $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) %/install.data: $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) %/install.merge: $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) $(SUBDIRS:%=%/all): %/all: $(CURDIR)/% $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) $(SUBDIRS:%=%/mergetrylist): %/mergetrylist: $(CURDIR)/% FORCE $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) $(SUBDIRS:%=%/mergelist): %/mergelist: $(CURDIR)/% FORCE $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) $(SUBDIRS:%=%/merge.o): %/merge.o: $(CURDIR)/% FORCE $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) $(SUBDIRS:%=%/clean): %/clean: $(CURDIR)/% $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) $(SUBDIRS:%=%/distclean): %/distclean: $(CURDIR)/% $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) $(SUBDIRS:%=%/dep): %/dep: $(CURDIR)/% $(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \ SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) #Here is the rule to create the subdirectories. If you're building in the #source tree, they already exist, but in a separate build directory, they may #not. ifneq ($(SUBDIR)x,x) # This hack stops us from having a warning due to the same target twice # when we're in the top level directory (because buildtools, etc are in # SUBDIRS). DIRS2 = $(BUILDDIR)/buildtools $(BUILDDIR)/lib $(BUILDDIR)/urt endif $(SUBDIRS:%=$(CURDIR)/%) $(DIRS2): mkdir $@ # The automatic dependency generation is a pain in the butt and # totally unnecessary for people just installing the distributed code, # so to avoid needless failures in the field and a complex build, the # rule to generate depend.mk automatically simply creates an # empty file. A developer may do 'make dep' to create a # depend.mk full of real dependencies. depend.mk: cat /dev/null >$@ include depend.mk FORCE: advanced/lib/0000755000175000001440000000000012347602012012227 5ustar ericusersadvanced/lib/colorname.c0000644000175000001440000001767112347602012014366 0ustar ericusers/* 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 600 /* 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 arg) { /*---------------------------------------------------------------------------- Modify string 'arg' to canonical form: lower case, no white space. -----------------------------------------------------------------------------*/ const char * srcCursor; char * dstCursor; for (srcCursor = arg, dstCursor = arg; *srcCursor; ++srcCursor) { if (!ISSPACE(*srcCursor)) { *dstCursor++ = ISUPPER(*srcCursor) ? tolower(*srcCursor) : *srcCursor; } } } static void openColornameFileSearch(const char * const searchPath, FILE ** const filePP) { /*---------------------------------------------------------------------------- Open the color name file, finding it via the search path 'searchPath'. Return as *filePP the stream handle for it, but if we don't find it (or just can open it) anywhere, return *filePP == NULL. -----------------------------------------------------------------------------*/ char * buffer; buffer = strdup(searchPath); if (buffer) { char * cursor; bool eol; cursor = &buffer[0]; eol = FALSE; /* initial value */ *filePP = NULL; /* initial value */ while (!eol && !*filePP) { const char * token; token = pm_strsep(&cursor, ":"); if (token) { *filePP = fopen(token, "r"); } else eol = TRUE; } free(buffer); } else *filePP = NULL; } FILE * pm_openColornameFile(const char * const fileName, const int must_open) { /*---------------------------------------------------------------------------- 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. -----------------------------------------------------------------------------*/ FILE *f; if (fileName == NULL) { const char * rgbdef = getenv(RGBENV); if (rgbdef) { /* The environment variable is set */ f = fopen(rgbdef, "r"); if (f == NULL && must_open) pm_error("Can't open the color names dictionary file " "named %s, per the %s environment variable. " "errno = %d (%s)", rgbdef, RGBENV, errno, strerror(errno)); } else { /* The environment variable isn't set, so try the hardcoded default color name dictionary locations. */ openColornameFileSearch(RGB_DB_PATH, &f); if (f == NULL && must_open) { pm_error("can't open color names dictionary file from the " "path '%s' " "and Environment variable %s not set. Set %s to " "the pathname of your rgb.txt file or don't use " "color names.", RGB_DB_PATH, RGBENV, RGBENV); } } } 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); } advanced/lib/dithers.h0000644000175000001440000000712612347602012014050 0ustar ericusers#ifndef DITHERS_H_INCLUDED #define DITHERS_H_INCLUDED /* ** dithers.h ** ** Here are some dithering matrices. They are all taken from "Digital ** Halftoning" by Robert Ulichney, MIT Press, ISBN 0-262-21009-6. */ #if 0 /* ** Order-6 ordered dithering matrix. Note that smaller ordered dithers ** have no advantage over larger ones, so use dither8 instead. */ static int const dither6[8][8] = { { 1, 59, 15, 55, 2, 56, 12, 52 }, { 33, 17, 47, 31, 34, 18, 44, 28 }, { 9, 49, 5, 63, 10, 50, 6, 60 }, { 41, 25, 37, 21, 42, 26, 38, 22 }, { 3, 57, 13, 53, 0, 58, 14, 54 }, { 35, 19, 45, 29, 32, 16, 46, 30 }, { 11, 51, 7, 61, 8, 48, 4, 62 }, { 43, 27, 39, 23, 40, 24, 36, 20 } }; #endif /* Order-8 ordered dithering matrix. */ static int const dither8[16][16] = { { 1,235, 59,219, 15,231, 55,215, 2,232, 56,216, 12,228, 52,212}, { 129, 65,187,123,143, 79,183,119,130, 66,184,120,140, 76,180,116}, { 33,193, 17,251, 47,207, 31,247, 34,194, 18,248, 44,204, 28,244}, { 161, 97,145, 81,175,111,159, 95,162, 98,146, 82,172,108,156, 92}, { 9,225, 49,209, 5,239, 63,223, 10,226, 50,210, 6,236, 60,220}, { 137, 73,177,113,133, 69,191,127,138, 74,178,114,134, 70,188,124}, { 41,201, 25,241, 37,197, 21,255, 42,202, 26,242, 38,198, 22,252}, { 169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86}, { 3,233, 57,217, 13,229, 53,213, 0,234, 58,218, 14,230, 54,214}, { 131, 67,185,121,141, 77,181,117,128, 64,186,122,142, 78,182,118}, { 35,195, 19,249, 45,205, 29,245, 32,192, 16,250, 46,206, 30,246}, { 163, 99,147, 83,173,109,157, 93,160, 96,144, 80,174,110,158, 94}, { 11,227, 51,211, 7,237, 61,221, 8,224, 48,208, 4,238, 62,222}, { 139, 75,179,115,135, 71,189,125,136, 72,176,112,132, 68,190,126}, { 43,203, 27,243, 39,199, 23,253, 40,200, 24,240, 36,196, 20,254}, { 171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84} }; /* Order-3 clustered dithering matrix. */ static int const cluster3[6][6] = { { 9,11,10, 8, 6, 7}, { 12,17,16, 5, 0, 1}, { 13,14,15, 4, 3, 2}, { 8, 6, 7, 9,11,10}, { 5, 0, 1,12,17,16}, { 4, 3, 2,13,14,15} }; /* Order-4 clustered dithering matrix. */ static int const cluster4[8][8] = { { 18,20,19,16,13,11,12,15}, { 27,28,29,22, 4, 3, 2, 9}, { 26,31,30,21, 5, 0, 1,10}, { 23,25,24,17, 8, 6, 7,14}, { 13,11,12,15,18,20,19,16}, { 4, 3, 2, 9,27,28,29,22}, { 5, 0, 1,10,26,31,30,21}, { 8, 6, 7,14,23,25,24,17} }; /* Order-8 clustered dithering matrix. */ static int const cluster8[16][16] = { { 64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60}, { 70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52}, { 78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44}, { 88,110,123,124,125,118,107, 85, 39, 17, 4, 3, 2, 9, 20, 42}, { 89,111,122,127,126,117,106, 84, 38, 16, 5, 0, 1, 10, 21, 43}, { 79,102,119,121,120,113, 97, 82, 48, 25, 8, 6, 7, 14, 30, 45}, { 71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53}, { 65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61}, { 63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67}, { 57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75}, { 49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83}, { 39, 17, 4, 3, 2, 9, 20, 42, 88,110,123,124,125,118,107, 85}, { 38, 16, 5, 0, 1, 10, 21, 43, 89,111,122,127,126,117,106, 84}, { 48, 25, 8, 6, 7, 14, 30, 45, 79,102,119,121,120,113, 97, 82}, { 56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74}, { 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66} }; #endif advanced/lib/libpbm3.c0000644000175000001440000003122112347602012013722 0ustar ericusers/* 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 #include "pm_c_util.h" #include "pbm.h" #ifndef PACKBITS_SSE #if WANT_SSE && defined(__SSE2__) && HAVE_GCC_BSWAP #define PACKBITS_SSE 2 #else #define PACKBITS_SSE 0 #endif #endif /* WANT_SSE means we want to use SSE CPU facilities to make PBM raster processing faster. This implies it's actually possible - i.e. the build environment has . The GNU Compiler -msse2 option makes SSE/SSE2 available, and is evidenced by __SSE2__. For x86-32 with SSE, "-msse2" must be explicitly given. For x86-64 and AMD64, "-msse2" is the default (from Gcc v.4.) */ #if PACKBITS_SSE == 2 #include #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); } 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."); } #if PACKBITS_SSE == 2 static void packBitsWithSse2( FILE * const fileP, const bit * const bitrow, unsigned char * const packedBits, unsigned int const cols) { /*---------------------------------------------------------------------------- Pack the bits of bitrow[] into bytes at 'packedBits'. Use the SSE2 facilities to pack the bits quickly, but perform the exact same function as the simpler packBitsGeneric() + packPartialBytes() Unlike packBitsGeneric(), the whole row is converted. -----------------------------------------------------------------------------*/ /* We use 2 SSE registers. The key machine instructions are: PCMPGTB128 Packed CoMPare Greater Than Byte Compares 16 bytes in parallel Result is x00 if greater than, xFF if not for each byte PMOVMSKB128 Packed MOVe MaSK Byte Result is 16 bits, the MSBs of 16 bytes x00 xFF x00 xFF xFF xFF x00 x00 xFF xFF xFF xFF x00 x00 x00 x00 --> 0101110011110000B = 0x5CF0 The result is actually a 64 bit int, but the higher bits are always 0. We use SSE instructions in "_mm_" form in favor of "__builtin_". In GCC the "__builtin_" form is documented but "_mm_" is not. Former versions of this source file used "__builtin_". This was changed to make possible compilation with clang, which does not implement some "__builtin_" forms. __builtin_ia32_pcmpgtb128 : _mm_cmpgt_epi8 __builtin_ia32_pmovmskb128 : _mm_movemask_epi8 The conversion requires . */ typedef char v16qi __attribute__ ((vector_size(16))); unsigned int col; union { v16qi v16; uint64_t i64[2]; unsigned char byte[16]; } bit128; v16qi zero128; zero128 = zero128 ^ zero128; /* clear to zero */ for (col = 0; col + 15 < cols; col += 16) { bit128.i64[0]=__builtin_bswap64( *(uint64_t*) &bitrow[col]); bit128.i64[1]=__builtin_bswap64( *(uint64_t*) &bitrow[col+8]); { v16qi const compare = (v16qi) _mm_cmpgt_epi8((__m128i)bit128.v16, (__m128i) zero128); uint16_t const blackMask = _mm_movemask_epi8 ((__m128i)compare); *(uint16_t *) & packedBits[col/8] = blackMask; } } if (cols % 16 > 0) { unsigned int i, j; bit128.v16 = bit128.v16 ^ bit128.v16; for (i = 0, j = col ; j < cols; ++i, ++j) bit128.byte[ (i&8) + 7-(i&7) ] = bitrow[j]; { v16qi const compare = (v16qi) _mm_cmpgt_epi8((__m128i)bit128.v16, (__m128i) zero128); uint16_t const blackMask = _mm_movemask_epi8 ((__m128i)compare); if ( cols%16 >8 ) /* Two partial bytes */ *(uint16_t *) & packedBits[col/8] = blackMask; else /* One partial byte */ packedBits[col/8] = (unsigned char) blackMask ; } } } #else /* Avoid undefined function warning; never actually called */ #define packBitsWithSse2(a,b,c,d) packBitsGeneric((a),(b),(c),(d),NULL) #endif static unsigned int bitValue(unsigned char const byteValue) { return byteValue == 0 ? 0 : 1; } static void packBitsGeneric(FILE * const fileP, const bit * const bitrow, unsigned char * const packedBits, unsigned int const cols, unsigned int * const nextColP) { /*---------------------------------------------------------------------------- Pack the bits of bitrow[] into bytes at 'packedBits'. Going left to right, stop when there aren't enough bits left to fill a whole byte. Return 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. -----------------------------------------------------------------------------*/ unsigned int col; for (col = 0; col + 7 < cols; col += 8) packedBits[col/8] = ( bitValue(bitrow[col+0]) << 7 | bitValue(bitrow[col+1]) << 6 | bitValue(bitrow[col+2]) << 5 | bitValue(bitrow[col+3]) << 4 | bitValue(bitrow[col+4]) << 3 | bitValue(bitrow[col+5]) << 2 | bitValue(bitrow[col+6]) << 1 | bitValue(bitrow[col+7]) << 0 ); *nextColP = col; } static void packPartialBytes(const bit * const bitrow, unsigned int const cols, unsigned int const nextCol, unsigned char * const packedBits) { /* routine for partial byte at the end of packedBits[] Prior to addition of the above enhancement, this method was used for the entire process */ unsigned int col; int bitshift; unsigned char item; bitshift = 7; /* initial value */ item = 0; /* initial value */ for (col = nextCol; col < cols; ++col, --bitshift) if (bitrow[col] != 0) item |= 1 << bitshift; packedBits[col/8] = item; } static void writePbmRowRaw(FILE * const fileP, const bit * const bitrow, int const cols) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; unsigned char * packedBits; packedBits = pbm_allocrow_packed(cols); if (setjmp(jmpbuf) != 0) { pbm_freerow_packed(packedBits); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { pm_setjmpbufsave(&jmpbuf, &origJmpbufP); switch (PACKBITS_SSE) { case 2: packBitsWithSse2(fileP, bitrow, packedBits, cols); break; default: { unsigned int nextCol; packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol); if (cols % 8 > 0) packPartialBytes(bitrow, cols, nextCol, packedBits); } } writePackedRawRow(fileP, packedBits, cols); pm_setjmpbuf(origJmpbufP); } pbm_freerow_packed(packedBits); } static void writePbmRowPlain(FILE * const fileP, const 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, const 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 packedBits, int const cols, int const forceplain) { if (!forceplain && !pm_plain_output) writePackedRawRow(fileP, packedBits, cols); else { jmp_buf jmpbuf; jmp_buf * origJmpbufP; bit * bitrow; bitrow = pbm_allocrow(cols); if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (col = 0; col < cols; ++col) bitrow[col] = packedBits[col/8] & (0x80 >> (col%8)) ? PBM_BLACK : PBM_WHITE; writePbmRowPlain(fileP, bitrow, cols); pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); } } static unsigned char leftBits(unsigned char const x, unsigned int const n) { /*---------------------------------------------------------------------------- Clear rightmost (8-n) bits, retain leftmost (=high) n bits. -----------------------------------------------------------------------------*/ unsigned char buffer; assert(n < 8); buffer = x; buffer >>= (8-n); buffer <<= (8-n); return buffer; } void pbm_writepbmrow_bitoffset(FILE * const fileP, unsigned char * const packedBits, unsigned int const cols, int const format, unsigned int const offset) { /*---------------------------------------------------------------------------- Write PBM row from a packed bit buffer 'packedBits, starting at the specified offset 'offset' in the buffer. We destroy the buffer. -----------------------------------------------------------------------------*/ unsigned int const rsh = offset % 8; unsigned int const lsh = (8 - rsh) % 8; unsigned int const csh = cols % 8; unsigned char * const window = &packedBits[offset/8]; /* Area of packed row buffer from which we take the image data. Aligned to nearest byte boundary to the left, so the first few bits might be irrelvant. Also our work buffer, in which we shift bits and from which we ultimately write the bits to the file. */ unsigned int const colByteCnt = pbm_packed_bytes(cols); unsigned int const last = colByteCnt - 1; /* Position within window of rightmost byte after shift */ bool const carryover = (csh == 0 || rsh + csh > 8); /* TRUE: Input comes from colByteCnt bytes and one extra byte. FALSE: Input comes from colByteCnt bytes. For example: TRUE: xxxxxxii iiiiiiii iiiiiiii iiixxxxx cols=21, offset=6 FALSE: xiiiiiii iiiiiiii iiiiiixx ________ cols=21, offset=1 We treat these differently for in the FALSE case the byte after last (indicated by ________) may not exist. */ if (rsh > 0) { unsigned int const shiftBytes = carryover ? colByteCnt : colByteCnt-1; unsigned int i; for (i = 0; i < shiftBytes; ++i) window[i] = window[i] << rsh | window[i+1] >> lsh; if (!carryover) window[last] = window[last] << rsh; } if (csh > 0) window[last] = leftBits(window[last], csh); pbm_writepbmrow_packed(fileP, window, cols, 0); } void pbm_writepbm(FILE * const fileP, bit ** const bits, 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); } advanced/lib/pbm.h0000644000175000001440000000714112347602012013161 0ustar ericusers#ifndef PBM_H_INCLUDED #define PBM_H_INCLUDED #include #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(int * const argcP, char ** const argv); void pbm_nextimage(FILE *file, int * const eofP); bit * pbm_allocrow(unsigned int const cols); #define pbm_allocarray(cols, rows) \ ((bit**) pm_allocarray(cols, rows, sizeof(bit))) #define pbm_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 packedBits, int const cols, int const format); void pbm_readpbmrow_bitoffset(FILE * const fileP, unsigned char * const packedBits, int const cols, int const format, unsigned int const offset); void pbm_writepbminit(FILE * const fileP, 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, const 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_writepbmrow_bitoffset(FILE * const ifP, unsigned char * const packedBits, unsigned int const cols, int const format, unsigned int const offset); void pbm_check(FILE * file, const enum pm_check_type check_type, const int format, const int cols, const int rows, enum pm_check_code * const retval_p); bit pbm_backgroundbitrow(const unsigned char * const packedBits, unsigned int const cols, unsigned int const offset); #ifdef __cplusplus } #endif #endif advanced/lib/libsystem_dummy.c0000644000175000001440000000414312347602012015623 0ustar ericusers/*============================================================================= 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, programs 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_vp(const char * const progName, const char ** const argArray, void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm) { } void pm_system_lp(const char * const progName, void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm, ...) { } void pm_system(void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm, const char * const shellCommand) { 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 */ } advanced/lib/pgm.h0000644000175000001440000000772312347602012013174 0ustar ericusers/* pgm.h - header file for libpgm portable graymap library */ #ifndef _PGM_H_ #define _PGM_H_ #include #include #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) pm_freerow(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_*/ advanced/lib/libpbm1.c0000644000175000001440000001253112347602012013723 0ustar ericusers/* 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 pmfileio.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 "mallocvar.h" #include "shhopt.h" #include "pbm.h" bit * pbm_allocrow(unsigned int const cols) { bit * bitrow; MALLOCARRAY(bitrow, cols); if (bitrow == NULL) pm_error("Unable to allocate space for a %u-column bit row", cols); return bitrow; } void pbm_init(int * const argcP, char ** const argv) { pm_proginit(argcP, (const char **)argv); } void pbm_nextimage(FILE *file, int * const eofP) { pm_nextimage(file, eofP); } void pbm_check(FILE * const fileP, enum pm_check_type const checkType, int const format, int const cols, int const rows, enum pm_check_code * const retvalP) { 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 (checkType != PM_CHECK_BASIC) { if (retvalP) *retvalP = PM_CHECK_UNKNOWN_TYPE; } else if (format != RPBM_FORMAT) { if (retvalP) *retvalP = PM_CHECK_UNCHECKABLE; } else { pm_filepos const bytesPerRow = (cols+7)/8; pm_filepos const needRasterSize = rows * bytesPerRow; pm_check(fileP, checkType, needRasterSize, retvalP); } } static unsigned int bitpop8(unsigned char const x) { /*---------------------------------------------------------------------------- Return the number of 1 bits in 'x' -----------------------------------------------------------------------------*/ static unsigned int const p[256] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; return p[x]; } static int bitpop(const unsigned char * const packedRow, unsigned int const cols, unsigned int const offset) { /*---------------------------------------------------------------------------- Return the number of 1 bits in 'packedRow', ignoring 0 to 7 bits at the row start (= on the left edge), indicated by offset. -----------------------------------------------------------------------------*/ unsigned int const fullLength = cols + offset; unsigned int sum; if (fullLength <= 8) { /* All bits are in a single byte */ sum = bitpop8((packedRow[0] << offset ) & (0xff << (8 - cols))); } else { unsigned int const colByteCnt = pbm_packed_bytes(fullLength); unsigned int const fullByteCnt = fullLength/8; unsigned int i; /* First byte, whether it is full or not */ sum = bitpop8(packedRow[0] << offset ); /* Second byte to last full byte */ for (i = 1; i < fullByteCnt; ++i) sum += bitpop8(packedRow[i]); /* Partial byte at the right end */ if (colByteCnt > fullByteCnt) sum += bitpop8(packedRow[i] >> (8 - fullLength%8)); } return sum; } bit pbm_backgroundbitrow(unsigned const char * const packedBits, unsigned int const cols, unsigned int const offset) { /*---------------------------------------------------------------------------- PBM version of pnm_backgroundxelrow() with additional offset parameter. When offset == 0, produces the same return value as does pnm_backgroundxelrow(promoted_bitrow, cols, ...) -----------------------------------------------------------------------------*/ const unsigned char * const row = &packedBits[offset/8]; unsigned int const rs = offset % 8; unsigned int const last = pbm_packed_bytes(cols + rs) - 1; unsigned int retval; bool const firstbit = (row[0] >> (7-rs)) & 0x01; bool const lastbit = (row[last] >> (7- (cols+rs-1)%8)) & 0x01; if (firstbit == lastbit) retval = firstbit; else { if (bitpop(row, cols, rs) >= cols/2) retval = PBM_BLACK; else retval = PBM_WHITE; } return retval; } advanced/lib/pm_system.h0000644000175000001440000000270212347602012014421 0ustar ericusers#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_vp(const char * const progName, const char ** const argArray, void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm); void pm_system_lp(const char * const progName, void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm, ...); void pm_system(void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm, const char * const shellCommand); struct bufferDesc { /* This is just a parameter for the routines below */ unsigned int size; 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 advanced/lib/libpm.c0000644000175000001440000005134112347602012013502 0ustar ericusers/************************************************************************** libpm.c *************************************************************************** This file contains fundamental libnetpbm services. Some of the subroutines in this library are intended and documented for use by Netpbm users, but most of them are just used by other Netpbm library subroutines. **************************************************************************/ #define _BSD_SOURCE /* Make sure strdup is in string.h */ #define _XOPEN_SOURCE 600 /* Make sure ftello, fseeko are defined */ #include "netpbm/pm_config.h" #include #include #include #include #include #include #include #include #include #if HAVE_FORK #include #endif #include #include "netpbm/pm_c_util.h" #include "netpbm/mallocvar.h" #include "netpbm/version.h" #include "netpbm/nstring.h" #include "netpbm/shhopt.h" #include "compile.h" #include "netpbm/pm.h" /* The following are set by pm_init(), then used by subsequent calls to other pm_xxx() functions. */ const char * pm_progname; int pm_plain_output; /* Boolean: programs should produce output in plain format */ static bool pm_showmessages; /* Programs should display informational messages (because the user didn't specify the --quiet option). */ static jmp_buf * pm_jmpbufP = NULL; /* A description of the point to which the program should hyperjump if a libnetpbm function encounters an error (libnetpbm functions 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. */ static pm_usererrormsgfn * userErrorMsgFn = NULL; /* A function to call to issue an error message. NULL means use the library default: print to Standard Error */ static pm_usermessagefn * userMessageFn = NULL; /* A function to call to issue an error message. NULL means use the library default: print to Standard Error */ 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_fork(int * const iAmParentP, pid_t * const childPidP, const char ** const errorP) { /*---------------------------------------------------------------------------- Same as POSIX fork, except with a nicer interface and works (fails cleanly) on systems that don't have POSIX fork(). -----------------------------------------------------------------------------*/ #if HAVE_FORK int rc; rc = fork(); if (rc < 0) { pm_asprintf(errorP, "Failed to fork a process. errno=%d (%s)", errno, strerror(errno)); } else { *errorP = NULL; if (rc == 0) { *iAmParentP = FALSE; } else { *iAmParentP = TRUE; *childPidP = rc; } } #else pm_asprintf(errorP, "Cannot fork a process, because this system does " "not have POSIX fork()"); #endif } void pm_waitpid(pid_t const pid, int * const statusP, int const options, pid_t * const exitedPidP, const char ** const errorP) { #if HAVE_FORK pid_t rc; rc = waitpid(pid, statusP, options); if (rc == (pid_t)-1) { pm_asprintf(errorP, "Failed to wait for process exit. " "waitpid() errno = %d (%s)", errno, strerror(errno)); } else { *exitedPidP = rc; *errorP = NULL; } #else pm_error("INTERNAL ERROR: Attempt to wait for a process we created on " "a system on which we can't create processes"); #endif } void pm_waitpidSimple(pid_t const pid) { int status; pid_t exitedPid; const char * error; pm_waitpid(pid, &status, 0, &exitedPid, &error); if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } else { assert(exitedPid != 0); } } void pm_setusererrormsgfn(pm_usererrormsgfn * fn) { userErrorMsgFn = fn; } void pm_setusermessagefn(pm_usermessagefn * fn) { userMessageFn = fn; } void pm_usage(const char usage[]) { pm_error("usage: %s %s", pm_progname, usage); } void PM_GNU_PRINTF_ATTR(1,2) pm_message(const char format[], ...) { va_list args; va_start(args, format); if (pm_showmessages) { const char * msg; pm_vasprintf(&msg, format, args); if (userMessageFn) userMessageFn(msg); else fprintf(stderr, "%s: %s\n", pm_progname, msg); pm_strfree(msg); } va_end(args); } static void errormsg(const char * const msg) { if (userErrorMsgFn) userErrorMsgFn(msg); else fprintf(stderr, "%s: %s\n", pm_progname, msg); } void PM_GNU_PRINTF_ATTR(1,2) pm_errormsg(const char format[], ...) { va_list args; const char * msg; va_start(args, format); pm_vasprintf(&msg, format, args); errormsg(msg); pm_strfree(msg); va_end(args); } void PM_GNU_PRINTF_ATTR(1,2) pm_error(const char format[], ...) { va_list args; const char * msg; va_start(args, format); pm_vasprintf(&msg, format, args); errormsg(msg); pm_strfree(msg); va_end(args); pm_longjmp(); } static void * mallocz(size_t const size) { return malloc(MAX(1, size)); } void * pm_allocrow(unsigned int const cols, unsigned int const size) { unsigned char * itrow; if (cols != 0 && UINT_MAX / cols < size) pm_error("Arithmetic overflow multiplying %u by %u to get the " "size of a row to allocate.", cols, size); itrow = mallocz(cols * size); if (itrow == NULL) pm_error("out of memory allocating a row"); return itrow; } void pm_freerow(void * const itrow) { free(itrow); } char ** pm_allocarray(int const cols, int const rows, int const elementSize ) { /*---------------------------------------------------------------------------- This is for backward compatibility. MALLOCARRAY2 is usually better. A problem with pm_allocarray() is that its return type is char ** even though 'elementSize' can be other than 1. So users have traditionally type cast the result. In the old days, that was just messy; modern compilers can produce the wrong code if you do that. -----------------------------------------------------------------------------*/ char ** retval; void * result; pm_mallocarray2(&result, rows, cols, elementSize); if (result == NULL) pm_error("Failed to allocate a raster array of %u columns x %u rows", cols, rows); retval = result; return retval; } void pm_freearray(char ** const rowIndex, int const rows) { void * const rowIndexVoid = rowIndex; pm_freearray2(rowIndexVoid); } /* Case-insensitive keyword matcher. */ int pm_keymatch(const char * const strarg, const char * const keywordarg, int const minchars) { int len; const char * keyword; const 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; } 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 pm_message( "SYSV defined" ); #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); fclose(netpbmConfigFile); } 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, const 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. -----------------------------------------------------------------------------*/ const char * const progname = pm_arg0toprogname(argv[0]); /* points to static memory in this library */ int argn, i; 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. */ 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; } int pm_getMessage(void) { return pm_showmessages; } static void extractAfterLastSlash(const char * const fullPath, char * const retval, size_t const retvalSize) { char * slashPos; /* Chop any directories off the left end */ slashPos = strrchr(fullPath, '/'); if (slashPos == NULL) { strncpy(retval, fullPath, retvalSize); retval[retvalSize-1] = '\0'; } else { strncpy(retval, slashPos +1, retvalSize); retval[retvalSize-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; } 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 it 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 NUL-terminated, but truncated at 64 characters. -----------------------------------------------------------------------------*/ #define MAX_RETVAL_SIZE 64 #if MSVCRT /* Note that there exists _splitpath_s, which takes a size argument, but it is only in "secure" extensions of MSVCRT that come only with MSVC; but _splitpath() comes with Windows. MinGW has a header file for it. */ static char retval[_MAX_FNAME]; _splitpath(arg0, 0, 0, retval, 0); if (MAX_RETVAL_SIZE < _MAX_FNAME) retval[MAX_RETVAL_SIZE] = '\0'; #else static char retval[MAX_RETVAL_SIZE+1]; extractAfterLastSlash(arg0, retval, sizeof(retval)); #endif return retval; } unsigned int pm_randseed(void) { return time(NULL) ^ getpid(); } unsigned int pm_parse_width(const char * const arg) { /*---------------------------------------------------------------------------- Return the image width represented by the decimal ASCIIZ string 'arg'. Fail if it doesn't validly represent a width or represents a width that can't be conveniently used in computation. -----------------------------------------------------------------------------*/ unsigned int width; const char * error; pm_interpret_uint(arg, &width, &error); if (error) { pm_error("'%s' is invalid as an image width. %s", arg, error); pm_strfree(error); } else { if (width > INT_MAX-10) pm_error("Width %u is too large for computations.", width); if (width == 0) pm_error("Width argument must be a positive number. You " "specified 0."); } return width; } unsigned int pm_parse_height(const char * const arg) { /*---------------------------------------------------------------------------- Same as pm_parse_width(), but for height. -----------------------------------------------------------------------------*/ unsigned int height; const char * error; pm_interpret_uint(arg, &height, &error); if (error) { pm_error("'%s' is invalid as an image height. %s", arg, error); pm_strfree(error); } else { if (height > INT_MAX-10) pm_error("Height %u is too large for computations.", height); if (height == 0) pm_error("Height argument must be a positive number. You " "specified 0."); } return height; } advanced/lib/ppmdfont.h0000644000175000001440000000325212347602012014231 0ustar ericusers#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 advanced/lib/pmfileio.c0000644000175000001440000006154412347602012014211 0ustar ericusers/************************************************************************** pmfileio.c *************************************************************************** This file contains fundamental file I/O stuff for libnetpbm. These are external functions, unlike 'fileio.c', but are not particular to any Netpbm format. **************************************************************************/ #define _SVID_SOURCE /* Make sure P_tmpdir is defined in GNU libc 2.0.7 (_XOPEN_SOURCE 500 does it in other libc's). pm_config.h defines TMPDIR as P_tmpdir in some environments. */ #define _XOPEN_SOURCE 600 /* 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 #include #include #ifdef __DJGPP__ #include #endif #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "pm.h" /* File open/close that handles "-" as stdin/stdout and checks errors. */ FILE * pm_openr(const char * const name) { FILE * f; if (streq(name, "-")) f = stdin; else { f = fopen(name, "rb"); 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 (streq(name, "-")) f = stdout; else { f = fopen(name, "wb"); 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 tempFileOpenFlags(void) { /*---------------------------------------------------------------------------- Open flags (argument to open()) suitable for a new temporary file -----------------------------------------------------------------------------*/ int retval; retval = 0 | O_CREAT | O_RDWR #if !MSVCRT | O_EXCL #endif #if MSVCRT | O_BINARY #endif ; return retval; } 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, tempFileOpenFlags(), PM_S_IWUSR | PM_S_IRUSR); if (rc >= 0) { fd = rc; gotFile = TRUE; } else { if (errno == EEXIST) { /* We'll just have to keep trying */ } else error = TRUE; } } } if (gotFile) retval = fd; else retval = -1; return retval; } static int mkstemp2(char * const filenameBuffer) { #if HAVE_MKSTEMP if (0) mkstempx(NULL); /* defeat compiler unused function warning */ return mkstemp(filenameBuffer); #else return mkstempx(filenameBuffer); #endif } static void makeTmpfileWithTemplate(const char * const filenameTemplate, int * const fdP, const char ** const filenameP, const char ** const errorP) { char * filenameBuffer; /* malloc'ed */ filenameBuffer = strdup(filenameTemplate); if (filenameBuffer == NULL) pm_asprintf(errorP, "Unable to allocate storage for temporary " "file name"); else { int rc; rc = mkstemp2(filenameBuffer); if (rc < 0) pm_asprintf(errorP, "Unable to create temporary file according to name " "pattern '%s'. mkstemp() failed with errno %d (%s)", filenameTemplate, errno, strerror(errno)); else { *fdP = rc; *filenameP = filenameBuffer; *errorP = NULL; } if (*errorP) pm_strfree(filenameBuffer); } } void pm_make_tmpfile_fd(int * const fdP, const char ** const filenameP) { const char * filenameTemplate; unsigned int fnamelen; const char * tmpdir; const char * dirseparator; const char * error; fnamelen = strlen(pm_progname) + 10; /* "/" + "_XXXXXX\0" */ tmpdir = tmpDir(); if (tmpdir[strlen(tmpdir) - 1] == '/') dirseparator = ""; else dirseparator = "/"; pm_asprintf(&filenameTemplate, "%s%s%s%s", tmpdir, dirseparator, pm_progname, "_XXXXXX"); if (filenameTemplate == pm_strsol) pm_asprintf(&error, "Unable to allocate storage for temporary file name"); else { makeTmpfileWithTemplate(filenameTemplate, fdP, filenameP, &error); pm_strfree(filenameTemplate); } if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } } void pm_make_tmpfile(FILE ** const filePP, const char ** const filenameP) { int fd; pm_make_tmpfile_fd(&fd, filenameP); *filePP = fdopen(fd, "w+b"); if (*filePP == NULL) { close(fd); unlink(*filenameP); pm_strfree(*filenameP); pm_error("Unable to create temporary file. " "fdopen() failed with errno %d (%s)", errno, strerror(errno)); } } FILE * pm_tmpfile(void) { FILE * fileP; const char * tmpfile; pm_make_tmpfile(&fileP, &tmpfile); unlink(tmpfile); pm_strfree(tmpfile); return fileP; } int pm_tmpfile_fd(void) { int fd; const char * tmpfile; pm_make_tmpfile_fd(&fd, &tmpfile); unlink(tmpfile); pm_strfree(tmpfile); return fd; } FILE * pm_openr_seekable(const char name[]) { /*---------------------------------------------------------------------------- Open the file named by name[] such that it is seekable (i.e. it can be rewound and read in multiple passes with fseek()). If the file is actually seekable, this reduces to the same as pm_openr(). If not, we copy the named file to a temporary file and return that file's stream descriptor. We use a file that the operating system recognizes as temporary, so it picks the filename and deletes the file when Caller closes it. -----------------------------------------------------------------------------*/ int stat_rc; int seekable; /* logical: file is seekable */ struct stat statbuf; FILE * original_file; FILE * seekable_file; original_file = pm_openr((char *) name); /* I would use fseek() to determine if the file is seekable and be a little more general than checking the type of file, but I don't have reliable information on how to do that. I have seen streams be partially seekable -- you can, for example seek to 0 if the file is positioned at 0 but you can't actually back up to 0. I have seen documentation that says the errno for an unseekable stream is EBADF and in practice seen ESPIPE. On the other hand, regular files are always seekable and even if some other file is, it doesn't hurt much to assume it isn't. */ stat_rc = fstat(fileno(original_file), &statbuf); if (stat_rc == 0 && S_ISREG(statbuf.st_mode)) seekable = TRUE; else seekable = FALSE; if (seekable) { seekable_file = original_file; } else { seekable_file = pm_tmpfile(); /* Copy the input into the temporary seekable file */ while (!feof(original_file) && !ferror(original_file) && !ferror(seekable_file)) { char buffer[4096]; int bytes_read; bytes_read = fread(buffer, 1, sizeof(buffer), original_file); fwrite(buffer, 1, bytes_read, seekable_file); } if (ferror(original_file)) pm_error("Error reading input file into temporary file. " "Errno = %s (%d)", strerror(errno), errno); if (ferror(seekable_file)) pm_error("Error writing input into temporary file. " "Errno = %s (%d)", strerror(errno), errno); pm_close(original_file); { int seek_rc; seek_rc = fseek(seekable_file, 0, SEEK_SET); if (seek_rc != 0) pm_error("fseek() failed to rewind temporary file. " "Errno = %s (%d)", strerror(errno), errno); } } return seekable_file; } void pm_close(FILE * const f) { fflush(f); if (ferror(f)) pm_message("A file read or write error occurred at some point"); if (f != stdin) if (fclose(f) != 0) pm_error("close of file failed with errno %d (%s)", errno, strerror(errno)); } /* The pnmtopng package uses pm_closer() and pm_closew() instead of pm_close(), apparently because the 1999 Pbmplus package has them. I don't know what the difference is supposed to be. */ void pm_closer(FILE * const f) { pm_close(f); } void pm_closew(FILE * const f) { pm_close(f); } /* Endian I/O. Before Netpbm 10.27 (March 2005), these would return failure on EOF or I/O failure. For backward compatibility, they still have the return code, but it is always zero and the routines abort the program in case of EOF or I/O failure. A program that wants to handle failure differently must use lower level (C library) interfaces. But that level of detail is uncharacteristic of a Netpbm program; the ease of programming that comes with not checking a return code is more Netpbm. It is also for historical reasons that these return signed values, when clearly unsigned would make more sense. */ static void abortWithReadError(FILE * const ifP) { if (feof(ifP)) pm_error("Unexpected end of input file"); else pm_error("Error (not EOF) reading file."); } static unsigned char getcNofail(FILE * const ifP) { int c; c = getc(ifP); if (c == EOF) abortWithReadError(ifP); return (unsigned char)c; } void pm_readchar(FILE * const ifP, char * const cP) { *cP = (char)getcNofail(ifP); } void pm_writechar(FILE * const ofP, char const c) { putc(c, ofP); } int pm_readbigshort(FILE * const ifP, short * const sP) { unsigned short s; s = getcNofail(ifP) << 8; s |= getcNofail(ifP) << 0; *sP = s; return 0; } int pm_writebigshort(FILE * const ofP, short const s) { putc((s >> 8) & 0xff, ofP); putc(s & 0xff, ofP); return 0; } int pm_readbiglong(FILE * const ifP, long * const lP) { unsigned long l; l = getcNofail(ifP) << 24; l |= getcNofail(ifP) << 16; l |= getcNofail(ifP) << 8; l |= getcNofail(ifP) << 0; *lP = l; return 0; } int pm_readbiglong2(FILE * const ifP, int32_t * const lP) { int rc; long l; rc = pm_readbiglong(ifP, &l); assert((int32_t)l == l); *lP = (int32_t)l; return rc; } int pm_writebiglong(FILE * const ofP, long const l) { putc((l >> 24) & 0xff, ofP); putc((l >> 16) & 0xff, ofP); putc((l >> 8) & 0xff, ofP); putc((l >> 0) & 0xff, ofP); return 0; } int pm_readlittleshort(FILE * const ifP, short * const sP) { unsigned short s; s = getcNofail(ifP) << 0; s |= getcNofail(ifP) << 8; *sP = s; return 0; } int pm_writelittleshort(FILE * const ofP, short const s) { putc((s >> 0) & 0xff, ofP); putc((s >> 8) & 0xff, ofP); return 0; } int pm_readlittlelong(FILE * const ifP, long * const lP) { unsigned long l; l = getcNofail(ifP) << 0; l |= getcNofail(ifP) << 8; l |= getcNofail(ifP) << 16; l |= getcNofail(ifP) << 24; *lP = l; return 0; } int pm_readlittlelong2(FILE * const ifP, int32_t * const lP) { int rc; long l; rc = pm_readlittlelong(ifP, &l); assert((int32_t)l == l); *lP = (int32_t)l; return rc; } int pm_writelittlelong(FILE * const ofP, long const l) { putc((l >> 0) & 0xff, ofP); putc((l >> 8) & 0xff, ofP); putc((l >> 16) & 0xff, ofP); putc((l >> 24) & 0xff, ofP); return 0; } int pm_readmagicnumber(FILE * const ifP) { int ich1, ich2; ich1 = getc(ifP); ich2 = getc(ifP); if (ich1 == EOF || ich2 == EOF) pm_error( "Error reading magic number from Netpbm image stream. " "Most often, this " "means your input file is empty." ); return ich1 * 256 + ich2; } /* Read a file of unknown size to a buffer. Return the number of bytes read. Allocate more memory as we need it. The calling routine has to free() the buffer. Oliver Trepte, oliver@fysik4.kth.se, 930613 */ #define PM_BUF_SIZE 16384 /* First try this size of the buffer, then double this until we reach PM_MAX_BUF_INC */ #define PM_MAX_BUF_INC 65536 /* Don't allocate more memory in larger blocks than this. */ char * pm_read_unknown_size(FILE * const file, long * const nread) { long nalloc; char * buf; bool eof; *nread = 0; nalloc = PM_BUF_SIZE; MALLOCARRAY(buf, nalloc); eof = FALSE; /* initial value */ while(!eof) { int val; if (*nread >= nalloc) { /* We need a larger buffer */ if (nalloc > PM_MAX_BUF_INC) nalloc += PM_MAX_BUF_INC; else nalloc += nalloc; REALLOCARRAY_NOFAIL(buf, nalloc); } val = getc(file); if (val == EOF) eof = TRUE; else buf[(*nread)++] = val; } return buf; } union cheat { uint32_t l; short s; unsigned char c[4]; }; short pm_bs_short(short const s) { union cheat u; unsigned char t; u.s = s; t = u.c[0]; u.c[0] = u.c[1]; u.c[1] = t; return u.s; } long pm_bs_long(long const l) { union cheat u; unsigned char t; u.l = l; t = u.c[0]; u.c[0] = u.c[3]; u.c[3] = t; t = u.c[1]; u.c[1] = u.c[2]; u.c[2] = t; return u.l; } void pm_tell2(FILE * const fileP, void * const fileposP, unsigned int const fileposSize) { /*---------------------------------------------------------------------------- Return the current file position as *filePosP, which is a buffer 'fileposSize' bytes long. Abort the program if error, including if *fileP isn't a file that has a position. -----------------------------------------------------------------------------*/ /* Note: FTELLO() is either ftello() or ftell(), depending on the capabilities of the underlying C library. It is defined in pm_config.h. ftello(), in turn, may be either ftell() or ftello64(), as implemented by the C library. */ pm_filepos const filepos = FTELLO(fileP); if (filepos < 0) pm_error("ftello() to get current file position failed. " "Errno = %s (%d)\n", strerror(errno), errno); if (fileposSize == sizeof(pm_filepos)) { pm_filepos * const fileposP_filepos = fileposP; *fileposP_filepos = filepos; } else if (fileposSize == sizeof(long)) { if (sizeof(pm_filepos) > sizeof(long) && filepos >= (pm_filepos) 1 << (sizeof(long)*8)) pm_error("File size is too large to represent in the %u bytes " "that were provided to pm_tell2()", fileposSize); else { long * const fileposP_long = fileposP; *fileposP_long = (long)filepos; } } else pm_error("File position size passed to pm_tell() is invalid: %u. " "Valid sizes are %u and %u", fileposSize, (unsigned int)sizeof(pm_filepos), (unsigned int) sizeof(long)); } unsigned int pm_tell(FILE * const fileP) { long filepos; pm_tell2(fileP, &filepos, sizeof(filepos)); return filepos; } void pm_seek2(FILE * const fileP, const pm_filepos * const fileposP, unsigned int const fileposSize) { /*---------------------------------------------------------------------------- Position file *fileP to position *fileposP. Abort if error, including if *fileP isn't a seekable file. -----------------------------------------------------------------------------*/ if (fileposSize == sizeof(pm_filepos)) /* Note: FSEEKO() is either fseeko() or fseek(), depending on the capabilities of the underlying C library. It is defined in pm_config.h. fseeko(), in turn, may be either fseek() or fseeko64(), as implemented by the C library. */ FSEEKO(fileP, *fileposP, SEEK_SET); else if (fileposSize == sizeof(long)) { long const fileposLong = *(long *)fileposP; fseek(fileP, fileposLong, SEEK_SET); } else pm_error("File position size passed to pm_seek() is invalid: %u. " "Valid sizes are %u and %u", fileposSize, (unsigned int)sizeof(pm_filepos), (unsigned int) sizeof(long)); } void pm_seek(FILE * const fileP, unsigned long filepos) { /*---------------------------------------------------------------------------- -----------------------------------------------------------------------------*/ pm_filepos fileposBuff; fileposBuff = filepos; pm_seek2(fileP, &fileposBuff, sizeof(fileposBuff)); } void pm_nextimage(FILE * const file, int * const eofP) { /*---------------------------------------------------------------------------- Position the file 'file' to the next image in the stream, assuming it is now positioned just after the current image. I.e. read off any white space at the end of the current image's raster. Note that the raw formats don't permit such white space, but this routine tolerates it anyway, because the plain formats do permit white space after the raster. Iff there is no next image, return *eofP == TRUE. Note that in practice, we will not normally see white space here in a plain PPM or plain PGM stream because the routine to read a sample from the image reads one character of white space after the sample in order to know where the sample ends. There is not normally more than one character of white space (a newline) after the last sample in the raster. But plain PBM is another story. No white space is required between samples of a plain PBM image. But the raster normally ends with a newline nonetheless. Since the sample reading code will not have read that newline, it is there for us to read now. -----------------------------------------------------------------------------*/ bool eof; bool nonWhitespaceFound; eof = FALSE; nonWhitespaceFound = FALSE; while (!eof && !nonWhitespaceFound) { int c; c = getc(file); if (c == EOF) { if (feof(file)) eof = TRUE; else pm_error("File error on getc() to position to image"); } else { if (!isspace(c)) { int rc; nonWhitespaceFound = TRUE; /* Have to put the non-whitespace character back in the stream -- it's part of the next image. */ rc = ungetc(c, file); if (rc == EOF) pm_error("File error doing ungetc() " "to position to image."); } } } *eofP = eof; } void pm_check(FILE * const file, enum pm_check_type const check_type, pm_filepos const need_raster_size, enum pm_check_code * const retval_p) { /*---------------------------------------------------------------------------- This is not defined for use outside of libnetpbm. -----------------------------------------------------------------------------*/ struct stat statbuf; pm_filepos curpos; /* Current position of file; -1 if none */ int rc; /* Note: FTELLO() is either ftello() or ftell(), depending on the capabilities of the underlying C library. It is defined in pm_config.h. ftello(), in turn, may be either ftell() or ftello64(), as implemented by the C library. */ curpos = FTELLO(file); if (curpos >= 0) { /* This type of file has a current position */ rc = fstat(fileno(file), &statbuf); if (rc != 0) pm_error("fstat() failed to get size of file, though ftello() " "successfully identified\n" "the current position. Errno=%s (%d)", strerror(errno), errno); else if (!S_ISREG(statbuf.st_mode)) { /* Not a regular file; we can't know its size */ if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; } else { pm_filepos const have_raster_size = statbuf.st_size - curpos; if (have_raster_size < need_raster_size) pm_error("File has invalid format. The raster should " "contain %u bytes, but\n" "the file ends after only %u bytes.", (unsigned int) need_raster_size, (unsigned int) have_raster_size); else if (have_raster_size > need_raster_size) { if (retval_p) *retval_p = PM_CHECK_TOO_LONG; } else { if (retval_p) *retval_p = PM_CHECK_OK; } } } else if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; } void pm_drain(FILE * const fileP, unsigned int const limit, unsigned int * const bytesReadP) { /*---------------------------------------------------------------------------- Read bytes from *fileP until EOF and return as *bytesReadP how many there were. But don't read any more than 'limit'. This is a good thing to call after reading an input file to be sure you didn't leave some input behind, which could mean you didn't properly interpret the file. -----------------------------------------------------------------------------*/ unsigned int bytesRead; bool eof; for (bytesRead = 0, eof = false; !eof && bytesRead < limit;) { int rc; rc = fgetc(fileP); eof = (rc == EOF); if (!eof) ++bytesRead; } *bytesReadP = bytesRead; } advanced/lib/util/0000755000175000001440000000000012347602012013204 5ustar ericusersadvanced/lib/util/wordintclz.h0000644000175000001440000000455612347602012015566 0ustar ericusers#ifndef WORDINTCLZ_H_INCLUDED #define WORDINTCLZ_H_INCLUDED #if (!defined(WORDACCESS_GENERIC) && HAVE_GCC_BITCOUNT ) /* Compiler is GCC and has __builtin_clz() wordint is long __builtin_clz is available on GCC 3.4 and above Note that the clz scheme does not work and requires adjustment if long type does not make use of all bits for data storage. This is unlikely. According to GNU MP (http://www.swox.com/gmp/), in rare cases such as Cray, there are smaller data types that take up the same space as long, but leave the higher bits silent. Currently, there are no known such cases for data type long. */ static __inline__ unsigned int wordintClz(wordint const x){ assert(sizeof(unsigned long int) == sizeof(wordint)); if (x == 0) return sizeof(wordint) * 8; else return (__builtin_clzl( (unsigned long int) x )); } #else /* wordint is uint32_t: exactly 32 bits wide */ static unsigned char const clz8[256]= { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static __inline__ unsigned int clz16(wordint const x) { if (x >> 8 != 0) return clz8[x >> 8]; else return clz8[x] + 8; } static __inline__ unsigned int clz32(wordint const x) { if (x >> 16 != 0) return clz16(x >> 16); else return clz16(x) + 16; } static __inline__ unsigned int wordintClz(wordint const x) { assert(sizeof(wordint) == 4); return clz32(x); } /* Another way to calculate leading zeros: x == 0 ? 32 : 31 - floor(log(x)/log(2)) (Beware: insufficient precision will cause errors) */ #endif #endif advanced/lib/util/nstring.c0000644000175000001440000011200712347602012015035 0ustar ericusers/* * 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, f (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: 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 _GNU_SOURCE /* Because of conditional compilation, this is GNU source only if the C library is GNU. */ #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 pm_vsnprintf(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; size_t precision = 0; bool precision_specified; bool justify_left; bool zero_padding; bool alternate_form; bool force_sign; bool space_for_positive; /* 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 */ justify_left = false; /* initial value */ zero_padding = false; /* initial value */ alternate_form = false; /* initial value */ force_sign = false; /* initial value */ space_for_positive = false; /* initial value */ while (*p == '0' || *p == '-' || *p == '+' || *p == ' ' || *p == '#' || *p == '\'') { switch (*p) { case '0': zero_padding = true; break; case '-': justify_left = true; break; case '+': force_sign = true; space_for_positive = false; break; case ' ': force_sign = true; break; /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ case '#': alternate_form = true; 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; justify_left = false; } else { min_field_width = -j; justify_left = true; } } 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; } else min_field_width = 0; /* parse precision */ if (*p == '.') { ++p; precision_specified = true; if (*p == '*') { int j = va_arg(ap, int); p++; if (j >= 0) precision = j; else { precision_specified = false; 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; } } else precision_specified = false; /* 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 = false; /* 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 = false; 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 = true; } } /* 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; case 'f': { char f[10]; if (precision_specified) snprintf(f, ARRAY_SIZE(f), "%%%u.%uf", (unsigned)min_field_width, (unsigned)precision); else snprintf(f, ARRAY_SIZE(f), "%%%uf", (unsigned)min_field_width); str_arg_l = sprintf(tmp, f, va_arg(ap, double)); str_arg = &tmp[0]; min_field_width = 0; zero_padding_insertion_ind = 0; } break; default: /* Unrecognized conversion specifier. Discard the unrecognized conversion, just keep the unrecognized conversion character. */ zero_padding = false; /* turn zero padding off for non-numeric convers. */ /* reset flags */ justify_left = true; min_field_width = 0; 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 const n = str_arg_l - zero_padding_insertion_ind; if (n > 0) { if (str_l < str_m) { size_t const 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 pm_snprintf(char * const dest, size_t const str_m, const char * const fmt, ...) { size_t size; va_list ap; va_start(ap, fmt); pm_vsnprintf(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 'pm_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. 'pm_strsol' is an external symbol, so if Caller wants to detect the out-of-memory failure, he certainly can. */ const char * const pm_strsol = "NO MEMORY TO CREATE STRING!"; const char * pm_strdup(const char * const arg) { const char * const dup = strdup(arg); return dup ? dup : pm_strsol; } void PM_GNU_PRINTF_ATTR(2,3) pm_asprintf(const char ** const resultP, const char * const fmt, ...) { const char * result; va_list varargs; #if HAVE_VASPRINTF int rc; va_start(varargs, fmt); rc = vasprintf((char **)&result, fmt, varargs); va_end(varargs); if (rc < 0) result = pm_strsol; #else size_t dryRunLen; va_start(varargs, fmt); pm_vsnprintf(NULL, 0, fmt, varargs, &dryRunLen); va_end(varargs); if (dryRunLen + 1 < dryRunLen) /* arithmetic overflow */ result = pm_strsol; else { size_t const allocSize = dryRunLen + 1; char * buffer; buffer = malloc(allocSize); if (buffer != NULL) { va_list varargs; size_t realLen; va_start(varargs, fmt); pm_vsnprintf(buffer, allocSize, fmt, varargs, &realLen); assert(realLen == dryRunLen); va_end(varargs); result = buffer; } } #endif if (result == NULL) *resultP = pm_strsol; else *resultP = result; } void pm_strfree(const char * const string) { if (string != pm_strsol) free((void *) string); } const char * pm_strsep(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 pm_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 void * pm_memmem(const void * const haystackArg, size_t const haystacklen, const void * const needleArg, size_t const needlelen) { const unsigned char * const haystack = haystackArg; const unsigned char * const needle = needleArg; /* This does the same as the function of the same name in the GNU C library */ const unsigned char * p; for (p = haystack; p <= haystack + haystacklen - needlelen; ++p) if (memeq(p, needle, needlelen)) return p; return NULL; } bool pm_strishex(const char * const subject) { bool retval; unsigned int i; retval = TRUE; /* initial assumption */ for (i = 0; i < strlen(subject); ++i) if (!ISXDIGIT(subject[i])) retval = FALSE; return retval; } void pm_interpret_uint(const char * const string, unsigned int * const valueP, const char ** const errorP) { if (string[0] == '\0') pm_asprintf(errorP, "Null string."); else { /* strtoul() does a bizarre thing where if the number is out of range, it returns a clamped value but tells you about it by setting errno = ERANGE. If it is not out of range, strtoul() leaves errno alone. */ char * tail; unsigned long ulongValue; errno = 0; /* So we can tell if strtoul() overflowed */ ulongValue = strtoul(string, &tail, 10); if (tail[0] != '\0') pm_asprintf(errorP, "Non-digit stuff in string: %s", tail); else if (errno == ERANGE) pm_asprintf(errorP, "Number too large"); else if (ulongValue > UINT_MAX) pm_asprintf(errorP, "Number too large"); else if (string[0] == '-') pm_asprintf(errorP, "Negative number"); /* Sleazy code; string may have leading spaces. */ else { *valueP = ulongValue; *errorP = NULL; } } } advanced/lib/util/shhopt.h0000644000175000001440000002017412347602012014666 0ustar ericusers#if 0 ============================================================================= HERE IS AN EXAMPLE OF THE USE OF SHHOPT: #include int main ( int argc, char **argv ) { /* initial values here are just to demonstrate what gets set and what doesn't by the code below. */ int help_flag = 7; unsigned int help_spec =7; unsigned int height_spec =7; 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; MALLOCARRAY(option_def, 100); 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; pm_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 If your program takes no options (so you have no OPTENT3 macro invocations), you need an OPTENTINIT call to establish the empty option table: optEntry * option_def; MALLOCARRAY(option_def, 1); OPTENTINIT; ======================================================================== #endif /* 0 */ #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_STRINGLIST, /* list like "arg1,arg2.arg3" */ 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 pm_optParseOptions() or pm_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 pm_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 pm_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 pm_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). This is a pointer to a variable that the parser will set to the number of times that the option appears in the command line. Here is an example: unsigned int option_def_index = 0; MALLOCARRAY_NOFAIL(option_def, 100); 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) \ } #define OPTENTINIT OPTION_DEF[0].type = OPT_END struct optNameValue { const char * name; const char * value; }; void pm_optSetFatalFunc(void (*f)(const char *, ...)); void pm_optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum); void pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, const unsigned long flags); void pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, const unsigned int optStructSize, const unsigned long flags); void pm_optDestroyNameValueList(struct optNameValue * const list); #ifdef __cplusplus } #endif #endif advanced/lib/util/wordaccess.h0000644000175000001440000000475412347602012015524 0ustar ericusers#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. HAVE_GCC_BITCOUNT and HAVE_GCC_BSWAP are set in pm_config.h BITS_PER_LONG is the number of bits in long int. */ #include "pm_config.h" #if defined(WORDACCESS_GENERIC) /* User wants this, regardless of whether machine can do better */ #include "wordaccess_generic.h" #elif BYTE_ORDER == BIG_ENDIAN #if UNALIGNED_OK #include "wordaccess_be_unaligned.h" #else /* Sparc */ #include "wordaccess_be_aligned.h" #endif #elif HAVE_GCC_BITCOUNT #if (BITS_PER_LONG == 64) /* AMD Athlon 64, Intel x86_64, Intel Itanium, etc. */ #include "wordaccess_64_le.h" #elif (BITS_PER_LONG == 32) /* Intel x86_32 (80386, 80486, Pentium), etc. */ #include "wordaccess_generic.h" #else /* Extremely rare case. If long is neither 32 nor 64 bits, (say, 128) it comes here. */ #include "wordaccess_generic.h" #endif #else /* Non GCC or GCC prior to v.3.4; little-endian */ #include "wordaccess_generic.h" #endif #endif advanced/lib/util/floatcode.h0000644000175000001440000001107312347602012015317 0ustar ericusers#ifndef FLOATCODE_H_INCLUDED #define FLOATCODE_H_INCLUDED #include "pm_config.h" /* BYTE_ORDER */ unsigned int const pm_byteOrder = BYTE_ORDER; typedef struct { /*---------------------------------------------------------------------------- This is a big-endian representation of a 32 bit floating point number. I.e. bytes[0] contains the sign bit, etc. On a big-endian machines, this is bit for bit identical to 'float'. On a little-endian machine, it isn't. This is an important data type because decent file formats use big-endian -- they don't care if some CPU happens to use some other code for its own work. -----------------------------------------------------------------------------*/ unsigned char bytes[4]; } pm_bigendFloat; static __inline__ float pm_floatFromBigendFloat(pm_bigendFloat const arg) { float retval; switch (pm_byteOrder) { case BIG_ENDIAN: { union { pm_bigendFloat bigend; float native; } converter; converter.bigend = arg; retval = converter.native; }; break; case LITTLE_ENDIAN: { union { unsigned char bytes[4]; float native; } converter; converter.bytes[0] = arg.bytes[3]; converter.bytes[1] = arg.bytes[2]; converter.bytes[2] = arg.bytes[1]; converter.bytes[3] = arg.bytes[0]; retval = converter.native; } break; } return retval; } static __inline__ pm_bigendFloat pm_bigendFloatFromFloat(float const arg) { pm_bigendFloat retval; switch (pm_byteOrder) { case BIG_ENDIAN: { union { pm_bigendFloat bigend; float native; } converter; converter.native = arg; retval = converter.bigend; } break; case LITTLE_ENDIAN: { union { unsigned char bytes[4]; float native; } converter; converter.native = arg; retval.bytes[0] = converter.bytes[3]; retval.bytes[1] = converter.bytes[2]; retval.bytes[2] = converter.bytes[1]; retval.bytes[3] = converter.bytes[0]; } break; } return retval; } typedef struct { /*---------------------------------------------------------------------------- This is a big-endian representation of a 64 bit floating point number. I.e. bytes[0] contains the sign bit, etc. On a big-endian machines, this is bit for bit identical to 'float'. On a little-endian machine, it isn't. This is an important data type because decent file formats use big-endian -- they don't care if some CPU happens to use some other code for its own work. -----------------------------------------------------------------------------*/ unsigned char bytes[8]; } pm_bigendDouble; static __inline__ double pm_doubleFromBigendDouble(pm_bigendDouble const arg) { double retval; switch (pm_byteOrder) { case BIG_ENDIAN: { union { pm_bigendDouble bigend; double native; } converter; converter.bigend = arg; retval = converter.native; }; break; case LITTLE_ENDIAN: { union { unsigned char bytes[8]; double native; } converter; converter.bytes[0] = arg.bytes[7]; converter.bytes[1] = arg.bytes[6]; converter.bytes[2] = arg.bytes[5]; converter.bytes[3] = arg.bytes[4]; converter.bytes[4] = arg.bytes[3]; converter.bytes[5] = arg.bytes[2]; converter.bytes[6] = arg.bytes[1]; converter.bytes[7] = arg.bytes[0]; retval = converter.native; } break; } return retval; } static __inline__ pm_bigendDouble pm_bigendDoubleFromDouble(double const arg) { pm_bigendDouble retval; switch (pm_byteOrder) { case BIG_ENDIAN: { union { pm_bigendDouble bigend; double native; } converter; converter.native = arg; retval = converter.bigend; } break; case LITTLE_ENDIAN: { union { unsigned char bytes[8]; double native; } converter; converter.native = arg; retval.bytes[0] = converter.bytes[7]; retval.bytes[1] = converter.bytes[6]; retval.bytes[2] = converter.bytes[5]; retval.bytes[3] = converter.bytes[4]; retval.bytes[4] = converter.bytes[3]; retval.bytes[5] = converter.bytes[2]; retval.bytes[6] = converter.bytes[1]; retval.bytes[7] = converter.bytes[0]; } break; } return retval; } #endif advanced/lib/util/bitreverse.h0000644000175000001440000000453212347602012015533 0ustar ericusers/* ** 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_*/ advanced/lib/util/filename.c0000644000175000001440000000130212347602012015124 0ustar ericusers#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; } pm_asprintf(&retval, "%s", &fileName[basenameStart]); return retval; } advanced/lib/util/bitarith.h0000644000175000001440000000200212347602012015155 0ustar ericusers#ifndef BITARITH_H_INCLUDED #define BITARITH_H_INCLUDED #include "pm_config.h" static __inline__ unsigned char pm_byteLeftBits(unsigned char const x, unsigned int const n) { /*---------------------------------------------------------------------------- Clear rightmost (8-n) bits, retain leftmost (=high) n bits. Return arbitrary value if n > 8. -----------------------------------------------------------------------------*/ unsigned char retval; retval = x; retval >>= (8-n); retval <<= (8-n); return retval; } static __inline__ unsigned char pm_byteRightBits(unsigned char const x, unsigned int const n){ /*---------------------------------------------------------------------------- Return rightmost (=low) n bits of x. Return arbitrary value if n > 8. -----------------------------------------------------------------------------*/ unsigned char retval; retval = x; retval <<= (8-n); retval >>= (8-n); return retval; } #endif advanced/lib/util/mallocvar.h0000644000175000001440000000735212347602012015344 0ustar ericusers/* 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) { void * const oldBlockP = *blockP; void * newBlockP; if (UINT_MAX / factor2 < factor1) newBlockP = NULL; else newBlockP = realloc(oldBlockP, factor1 * factor2); if (newBlockP) *blockP = newBlockP; else { free(oldBlockP); *blockP = NULL; } } #define MALLOCARRAY(arrayName, nElements) do { \ void * array; \ mallocProduct(&array, nElements, sizeof(arrayName[0])); \ arrayName = array; \ } while (0) #define REALLOCARRAY(arrayName, nElements) do { \ void * array; \ array = arrayName; \ reallocProduct(&array, nElements, sizeof(arrayName[0])); \ if (!array && arrayName) \ free(arrayName); \ 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 MALLOCARRAY2(arrayName, nRows, nCols) do { \ void * array; \ pm_mallocarray2(&array, nRows, nCols, sizeof(arrayName[0][0])); \ arrayName = array; \ } while (0) #define MALLOCARRAY2_NOFAIL(arrayName, nRows, nCols) do { \ MALLOCARRAY2(arrayName, nRows, nCols); \ if ((arrayName) == NULL) \ abort(); \ } while (0) void pm_freearray2(void ** const rowIndex); #define MALLOCVAR(varName) \ varName = malloc(sizeof(*varName)) #define MALLOCVAR_NOFAIL(varName) \ do {if ((varName = malloc(sizeof(*varName))) == NULL) abort();} while(0) void pm_mallocarray2(void ** const resultP, unsigned int const cols, unsigned int const rows, unsigned int const elementSize); #ifdef __cplusplus } #endif #endif advanced/lib/util/shhopt.README0000644000175000001440000001475412347602012015403 0ustar ericusersShhopt 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! advanced/lib/util/wordaccess_be_aligned.h0000644000175000001440000000235212347602012017645 0ustar ericusers/*============================================================================= This file is the part of wordaccess.h for use under with big-endian machines that require word accesses to be word-aligned. *===========================================================================*/ typedef unsigned long int wordint; typedef unsigned char wordintBytes[sizeof(wordint)]; static __inline__ wordint bytesToWordint(wordintBytes bytes) { uint16_t const hi = *((uint16_t *) (bytes + 0)); uint16_t const mh = *((uint16_t *) (bytes + 2)); uint16_t const ml = *((uint16_t *) (bytes + 4)); uint16_t const lo = *((uint16_t *) (bytes + 6)); return (((wordint) hi) << 48) | (((wordint) mh) << 32) | (((wordint) ml) << 24) | (((wordint) lo) << 0); } static __inline__ void wordintToBytes(wordintBytes * const bytesP, wordint const wordInt) { uint16_t const hi = ((wordInt >> 48) & 0xFF) uint16_t const mh = ((wordInt >> 32) & 0xFF); uint16_t const ml = ((wordInt >> 24) & 0xFF); uint16_t const lo = ((wordInt >> 0) & 0xFF); *(uint16_t *)(bytesP + 0) = hi; *(uint16_t *)(bytesP + 2) = mh; *(uint16_t *)(bytesP + 4) = ml; *(uint16_t *)(bytesP + 6) = lo; } advanced/lib/util/nsleep.c0000644000175000001440000000101312347602012014631 0ustar ericusers#include "pm_config.h" #if MSVCRT #include #include #else #include #endif #include "nsleep.h" void pm_sleep(unsigned int const milliseconds) { #if MSVCRT SleepEx(milliseconds, TRUE); #else /* We could use usleep() here if millisecond resolution is really important, but since Netpbm has no need for it today, we don't want to deal with the possibility that usleep() doesn't exist. 08.08.01. */ sleep((milliseconds + 999)/1000); #endif } advanced/lib/util/matrix.h0000644000175000001440000000042612347602012014663 0ustar ericusers#ifndef MATRIX_H_INCLUDED #define MATRIX_H_INCLUDED void pm_solvelineareq(double ** const aArg, double * const x, double * const cArg, unsigned int const n, const char ** const errorP); #endif advanced/lib/util/filename.h0000644000175000001440000000017012347602012015133 0ustar ericusers#ifndef FILENAME_H_INCLUDED #define FILENAME_H_INCLUDED const char * pm_basename(const char * const fileName); #endif advanced/lib/util/testnstring.c0000644000175000001440000000112312347602012015731 0ustar ericusers#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; } advanced/lib/util/shhopt-1.1.6.lsm0000644000175000001440000000115712347602012015673 0ustar ericusersBegin3 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 advanced/lib/util/token.h0000644000175000001440000000041512347602012014475 0ustar ericusers#ifndef TOKEN_H_INCLUDE #define TOKEN_H_INCLUDE void pm_gettoken(const char * const tokenStart, char const delimiter, const char ** const tokenP, const char ** const nextP, const char ** const errorP); #endif advanced/lib/util/pm_c_util.h0000644000175000001440000000572012347602012015334 0ustar ericusers#ifndef PM_C_UTIL_INCLUDED #define PM_C_UTIL_INCLUDED /* Note that for MAX and MIN, if either of the operands is a floating point Not-A-Number, the result is the second operand. So if you're computing a running maximum and want to ignore the NaNs in the computation, put the running maximum variable second. */ #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)) /* ROUNDUP rounds up to a specified multiple. E.g. ROUNDUP(22, 8) == 24 */ #undef ROUNDUP #define ROUNDUP(X,M) (((X)+(M)-1)/(M)*(M)) #undef ROUNDDN #define ROUNDDN(X,M) ((X)/(M)*(M)) #define ROUNDDIV(DIVIDEND,DIVISOR) (((DIVIDEND) + (DIVISOR)/2)/(DIVISOR)) #undef SQR #define SQR(a) ((a)*(a)) /* 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 will probably never again see a system that does not have , but just in case, we have our own alternative here. Evidence shows that the compiler actually produces better code with than with bool simply typedefed to int. */ #ifdef __cplusplus /* C++ has a bool type and false and true constants built in. */ #else /* The test for __STDC__ is paranoid. It is there just in case some nonstandard compiler defines __STDC_VERSION__ in an arbitrary manner. */ #if ( defined(__GNUC__) && (__GNUC__ >= 3) ) || \ ( defined(__STDC__) && (__STDC__ ==1) && \ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ) #include #else /* 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. */ #ifndef HAVE_BOOL #define HAVE_BOOL 1 typedef int bool; #endif #ifndef true enum boolvalue {false=0, true=1}; #endif #endif #endif #ifndef TRUE #define TRUE true #endif #ifndef FALSE #define FALSE false #endif #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #endif advanced/lib/util/LICENSE.txt0000644000175000001440000001236212347602012015033 0ustar ericusersThe 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 advanced/lib/util/mallocvar.c0000644000175000001440000001301412347602012015327 0ustar ericusers#include #include #include "pm_c_util.h" #include "nstring.h" #include "mallocvar.h" static void * mallocz(size_t const size) { /*---------------------------------------------------------------------------- Same as malloc(), except it is legal to allocate zero bytes. -----------------------------------------------------------------------------*/ return malloc(MAX(1, size)); } static void allocarrayNoHeap(void ** const rowIndex, unsigned int const rows, unsigned int const cols, unsigned int const elementSize, bool * const failedP) { unsigned int rowsDone; for (rowsDone = 0, *failedP = false; rowsDone < rows && !*failedP; ) { void * rowSpace; mallocProduct(&rowSpace, cols, elementSize); if (rowSpace == NULL) { unsigned int row; *failedP = true; /* unwind partially completed job */ for (row = 0; row < rowsDone; ++row) free(rowIndex[row]); } else rowIndex[rowsDone++] = rowSpace; } } static unsigned char * allocRowHeap(unsigned int const rows, unsigned int const cols, unsigned int const size) { /*---------------------------------------------------------------------------- Allocate a row heap. That's a chunk of memory for use in a pm_mallocarray2 two-dimensional array to contain the rows. The heap must fit 'rows' rows of 'cols' columns each of elements 'size' bytes in size. Return NULL if we can't get the memory. -----------------------------------------------------------------------------*/ unsigned char * retval; if (cols != 0 && rows != 0 && UINT_MAX / cols / rows < size) /* Too big even to request the memory ! */ retval = NULL; else retval = mallocz(rows * cols * size); return retval; } void pm_mallocarray2(void ** const resultP, unsigned int const rows, unsigned int const cols, unsigned int const elementSize) { /*---------------------------------------------------------------------------- Allocate an array of 'rows' rows of 'cols' columns each, with each element 'size' bytes. We use the C multidimensional array paradigm: The array is a row index (array of pointers to rows) plus an array of elements for each of those rows. So a[row][col] gives you the element of the two dimensional array at Row 'row', Column 'col'. Each array element is ideally aligned to an 'elementSize' boundary. But we guarantee this only for 1, 2, 4, 8, and 16, because of limitations of libc malloc() (which we use to allocate all the memory). We tack on two extra elements to the end of the row index, transparent to the user, for use in memory management (in particular, in destroying the array). The first is a NULL pointer, so you can tell the vertical dimension of the array. The second points to the row heap (see below). We have two ways of allocating the space: fragmented and unfragmented. In both, the row index (plus the extra elements) is in one block of memory. In the fragmented format, each row is also in an independent memory block, and the row heap pointer (see above) is NULL. In the unfragmented format, all the rows are in a single block of memory called the row heap and the row heap pointer points to that. We use unfragmented format if possible, but if the allocation of the row heap fails, we fall back to fragmented. -----------------------------------------------------------------------------*/ void ** rowIndex; bool failed; MALLOCARRAY(rowIndex, rows + 1 + 1); if (rowIndex == NULL) failed = true; else { unsigned char * rowheap; rowheap = allocRowHeap(cols, rows, elementSize); if (rowheap) { /* It's unfragmented format */ rowIndex[rows+1] = rowheap; /* Declare it unfragmented format */ if (rowheap) { unsigned int row; for (row = 0; row < rows; ++row) rowIndex[row] = &(rowheap[row * cols * elementSize]); } failed = false; } else { /* We couldn't get the whole heap in one block, so try fragmented format. */ rowIndex[rows+1] = NULL; /* Declare it fragmented format */ allocarrayNoHeap(rowIndex, rows, cols, elementSize, &failed); } rowIndex[rows+0] = NULL; /* mark end of rows */ } if (failed) *resultP = NULL; else *resultP = rowIndex; } static unsigned int array2RowCount(void ** const rowIndex) { /*---------------------------------------------------------------------------- Return the number of rows in the 2-dimensional array. -----------------------------------------------------------------------------*/ /* The end of the rows is marked by a null pointer where a row pointer otherwise would be. */ unsigned int row; for (row = 0; rowIndex[row]; ++row); return row; } void pm_freearray2(void ** const rowIndex) { unsigned int const rows = array2RowCount(rowIndex); void * const rowheap = rowIndex[rows+1]; if (rowheap != NULL) free(rowheap); else { /* Free each individually malloced row */ unsigned int row; for (row = 0; row < rows; ++row) pm_freerow(rowIndex[row]); } free(rowIndex); } advanced/lib/util/nsleep.h0000644000175000001440000000015512347602012014644 0ustar ericusers#ifndef NSLEEP_H_INCLUDED #define NSLEEP_H_INCLUDED void pm_sleep(unsigned int const milliseconds); #endif advanced/lib/util/io.c0000644000175000001440000000474612347602012013772 0ustar ericusers#include #include #include #include #include "mallocvar.h" #include "nstring.h" #include "io.h" void pm_freadline(FILE * const fileP, const char ** const lineP, const char ** const errorP) { /*---------------------------------------------------------------------------- Read a line (assuming the file is text with lines delimited by newlines) for file *fileP. Return that line in newly malloced storage as *lineP. The newline delimiter is not part of the line. As a special case, if the file doesn't end in a newline, the characters after the last newline are a line. If there are no more lines in the file, return *lineP == NULL. -----------------------------------------------------------------------------*/ char * buffer; size_t bufferSize; size_t cursor; bool gotLine; bool eof; bufferSize = 1024; /* initial value */ *errorP = NULL; /* initial value */ MALLOCARRAY(buffer, bufferSize); for (cursor = 0, gotLine = false, eof = false; !gotLine && !eof && !*errorP; ) { if (cursor + 1 >= bufferSize) { if (bufferSize > INT_MAX/2) { free(buffer); buffer = NULL; } else { bufferSize *= 2; REALLOCARRAY(buffer, bufferSize); } } if (!buffer) pm_asprintf(errorP, "Couldn't get memory for a %u-byte file read buffer.", (unsigned int)bufferSize); else { int const rc = getc(fileP); if (rc < 0) { if (feof(fileP)) eof = true; else pm_asprintf(errorP, "Failed to read a character from file. " "Errno = %d (%s)", errno, strerror(errno)); } else { char const c = (char)rc; if (c == '\n') gotLine = true; else { buffer[cursor++] = c; } } } } if (*errorP) { if (buffer) free(buffer); } else { if (eof && cursor == 0) { *lineP = NULL; free(buffer); } else { assert(cursor < bufferSize); buffer[cursor++] = '\0'; *lineP = buffer; } } } advanced/lib/util/io.h0000644000175000001440000000031012347602012013756 0ustar ericusers#ifndef IO_H_INCLUDED #define IO_H_INCLUDED #include void pm_freadline(FILE * const fileP, const char ** const lineP, const char ** const errorP); #endif advanced/lib/util/vasprintf.c0000644000175000001440000000276512347602012015376 0ustar ericusers#define _GNU_SOURCE /* Because of conditional compilation, this is GNU source only if the C library is GNU. */ #include #include #include "pm_config.h" #include "nstring.h" void pm_vasprintf(const char ** const resultP, const char * const format, va_list varargs) { char * result; #if HAVE_VASPRINTF int rc; rc = vasprintf(&result, format, varargs); if (rc < 0) *resultP = pm_strsol; else *resultP = result; #else /* We have a big compromise here. To do this right, without a huge amount of work, we need to go through the variable arguments twice: once to determine how much memory to allocate, and once to format the string. On some machines, you can simply make two copies of the va_list variable in normal C fashion, but on others you need va_copy, which is a relatively recent invention. In particular, the simple va_list copy failed on an AMD64 Gcc Linux system in March 2006. So instead, we just allocate 4K and truncate or waste as necessary. */ size_t const allocSize = 4096; result = malloc(allocSize); if (result == NULL) *resultP = pm_strsol; else { size_t realLen; pm_vsnprintf(result, allocSize, format, varargs, &realLen); if (realLen >= allocSize) strcpy(result + allocSize - 15, "<<=3.4) (__builtin_clz appears in GCC 3.4) * Little-Endian machines (IA64, X86-64, AMD64) * 64 bit long =============================================================================*/ #include "intcode.h" typedef uint64_t wordint; typedef unsigned char wordintBytes[sizeof(wordint)]; static __inline__ wordint bytesToWordint(wordintBytes const bytes) { return (wordint) pm_uintFromBigend64(*(bigend64*)bytes); } static __inline__ void wordintToBytes(wordintBytes * const bytesP, wordint const wordInt) { *(bigend64*)bytesP = pm_bigendFromUint64(wordInt); } advanced/lib/util/Makefile0000644000175000001440000000127212347602012014646 0ustar ericusersifeq ($(SRCDIR)x,x) SRCDIR = $(CURDIR)/../.. BUILDDIR = $(SRCDIR) endif SUBDIR = lib/util VPATH=.:$(SRCDIR)/$(SUBDIR) default:all include $(BUILDDIR)/config.mk # nstring is required for asprintf(), etc. Also some systems don't have # snprintf(), e.g. Solaris 2.5.1. 2002.03.29. UTILOBJECTS = \ filename.o \ io.o \ mallocvar.o \ matrix.o \ nsleep.o \ nstring.o \ shhopt.o \ token.o \ vasprintf.o \ MERGE_OBJECTS = include $(SRCDIR)/common.mk all: $(UTILOBJECTS) $(UTILOBJECTS): CFLAGS_TARGET=$(CFLAGS_SHLIB) $(UTILOBJECTS):%.o:%.c importinc $(CC) -c $(INCLUDES) $(CFLAGS_ALL) -o $@ $< testnstring: test.c nstring.h nstring.o $(CC) $(CFLAGS_ALL) -o $@ nstring.o $< advanced/lib/util/intcode.h0000644000175000001440000001637112347602012015012 0ustar ericusers#ifndef INTCODE_H_INCLUDED #define INTCODE_H_INCLUDED #include "pm_config.h" /* For uint32_t, BYTE_ORDER, HAVE_INT64 */ static unsigned int const pm_byteOrder = BYTE_ORDER; typedef struct { /*---------------------------------------------------------------------------- This is a big-endian representation of a 16 bit integer. I.e. bytes[0] is the most significant 8 bits; bytes[1] is the least significant 8 bits of the number in pure binary. On a big-endian machines, this is bit for bit identical to uint16_t. On a little-endian machine, it isn't. This is an important data type because decent file formats use big-endian -- they don't care if some CPU happens to use some other code for its own work. -----------------------------------------------------------------------------*/ unsigned char bytes[2]; } bigend16; static __inline__ uint16_t pm_uintFromBigend16(bigend16 const arg) { uint16_t retval; switch (pm_byteOrder) { case BIG_ENDIAN: { union { bigend16 bigend; uint16_t native; } converter; converter.bigend = arg; retval = converter.native; } break; default: { retval = (arg.bytes[0] << 8) | (arg.bytes[1] << 0); } } return retval; } static __inline__ bigend16 pm_bigendFromUint16(uint16_t const arg) { bigend16 retval; switch (pm_byteOrder) { case BIG_ENDIAN: { union { bigend16 bigend; uint16_t native; } converter; converter.native = arg; retval = converter.bigend; } break; default: { uint16_t shift; shift = arg; retval.bytes[1] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[0] = shift; /* Takes lower 8 bits */ } } return retval; } typedef struct { /*---------------------------------------------------------------------------- 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; 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; #if HAVE_GCC_BSWAP case LITTLE_ENDIAN: { /* Use GCC built-in */ union { bigend32 bigend; uint32_t native; } converter; converter.bigend = arg; retval = __builtin_bswap32(converter.native); } break; #endif default: retval = (arg.bytes[0] << 24) | (arg.bytes[1] << 16) | (arg.bytes[2] << 8) | (arg.bytes[3] << 0); } 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; #if HAVE_GCC_BSWAP case LITTLE_ENDIAN: { /* Use GCC built-in */ union { bigend32 bigend; uint32_t native; } converter; converter.native = __builtin_bswap32(arg); retval = converter.bigend; } break; #endif default: { uint32_t shift; shift = arg; retval.bytes[3] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[2] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[1] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[0] = shift; /* Takes lower 8 bits */ } } return retval; } #if HAVE_INT64 typedef struct { /*---------------------------------------------------------------------------- This is a big-endian representation of a 64 bit integer. I.e. bytes[0] is the most significant 8 bits; bytes[7] is the least significant 8 bits of the number in pure binary. On a big-endian machines, this is bit for bit identical to uint64_t. On a little-endian machine, it isn't. This is an important data type because decent file formats use big-endian -- they don't care if some CPU happens to use some other code for its own work. uint64_t is supported only when available. -----------------------------------------------------------------------------*/ unsigned char bytes[8]; } bigend64; static __inline__ uint64_t pm_uintFromBigend64(bigend64 const arg) { uint64_t retval; switch (pm_byteOrder) { case BIG_ENDIAN: { union { bigend64 bigend; uint64_t native; } converter; converter.bigend = arg; retval = converter.native; } break; #if HAVE_GCC_BSWAP case LITTLE_ENDIAN: { /* Use GCC built-in */ union { bigend64 bigend; uint64_t native; } converter; converter.bigend = arg; retval = __builtin_bswap64(converter.native); } break; #endif default: retval = ((uint64_t)arg.bytes[0] << 56) | ((uint64_t)arg.bytes[1] << 48) | ((uint64_t)arg.bytes[2] << 40) | ((uint64_t)arg.bytes[3] << 32) | ((uint64_t)arg.bytes[4] << 24) | ((uint64_t)arg.bytes[5] << 16) | ((uint64_t)arg.bytes[6] << 8) | ((uint64_t)arg.bytes[7] << 0); } return retval; } static __inline__ bigend64 pm_bigendFromUint64(uint64_t const arg) { bigend64 retval; switch (pm_byteOrder) { case BIG_ENDIAN: { union { bigend64 bigend; uint64_t native; } converter; converter.native = arg; retval = converter.bigend; } break; #if HAVE_GCC_BSWAP case LITTLE_ENDIAN: { /* Use GCC built-in */ union { bigend64 bigend; uint64_t native; } converter; converter.native = __builtin_bswap64(arg); retval = converter.bigend; } break; #endif default: { uint64_t shift; shift = arg; retval.bytes[7] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[6] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[5] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[4] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[3] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[2] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[1] = shift; /* Takes lower 8 bits */ shift >>= 8; retval.bytes[0] = shift; /* Takes lower 8 bits */ } } return retval; } #endif /* HAVE_INT64 */ #endif advanced/lib/util/wordaccess_generic.h0000644000175000001440000000141012347602012017202 0ustar ericusers/*============================================================================= This file is the part of wordaccess.h for use under any of these conditions: * Compiler other than GCC * GCC before version 3.4 * Requested by the user with WORDACCESS_GENERIC =============================================================================*/ #include "intcode.h" typedef uint32_t wordint; typedef unsigned char wordintBytes[sizeof(wordint)]; static __inline__ wordint bytesToWordint(wordintBytes const bytes) { return (wordint) pm_uintFromBigend32( * (bigend32*) bytes); } static __inline__ void wordintToBytes(wordintBytes * const bytesP, wordint const wordInt) { * (bigend32*) bytesP = pm_bigendFromUint32((uint32_t)wordInt); } advanced/lib/util/lexheader0000644000175000001440000000051712347602012015073 0ustar ericusers/* 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 */ advanced/lib/util/matrix.c0000644000175000001440000001374712347602012014670 0ustar ericusers/*============================================================================= matrix =============================================================================== Matrix math. =============================================================================*/ #include #include #include #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "matrix.h" static double const epsilon = 1e-10; static void swap(double * const aP, double * const bP) { double const oldA = *aP; *aP = *bP; *bP = oldA; } static void initializeWorkMatrices(unsigned int const n, double ** const aInit, const double * const cInit, double *** const aP, double ** const cP, const char ** const errorP) { /*---------------------------------------------------------------------------- Allocate memory for an n x n matrix, initialize it to the value of aInit[], and return it as *aP. Allocate memory for an n x 1 matrix, initialize it to the value of cInit[], and return it as *cP. -----------------------------------------------------------------------------*/ double ** a; double * c; MALLOCARRAY2(a, n, n); if (a == NULL) pm_asprintf(errorP, "Could not get memory for a %u x %u matrix", n, n); else { unsigned int i; for (i = 0; i < n; ++i) { unsigned int j; for (j = 0; j < n; ++j) a[i][j] = aInit[i][j]; } MALLOCARRAY(c, n); if (c == NULL) pm_asprintf(errorP, "Could not get memory for a %u x 1 matrix", n); else { unsigned int i; for (i = 0; i < n; ++i) c[i] = cInit[i]; *errorP = NULL; } if (*errorP) free(a); } *aP = a; *cP = c; } static void findLargestIthCoeff(unsigned int const n, double ** const a, unsigned int const i, unsigned int * const istarP, const char ** const errorP) { /*---------------------------------------------------------------------------- Among the 'i'th and following rows in 'a' (which has 'n' total rows), find the one with the largest 'i'th column. And it had better be greater than zero; if not, we fail (return *errorP non-null). Return its index as *istarP. -----------------------------------------------------------------------------*/ double maxSoFar; unsigned int maxIdx; unsigned int ii; for (ii = i, maxSoFar = 0.0; ii < n; ++ii) { double const thisA = fabs(a[ii][i]); if (thisA >= maxSoFar) { maxIdx = ii; maxSoFar = thisA; } } if (maxSoFar < epsilon) pm_asprintf(errorP, "Matrix equation has no unique solution. " "(debug: coeff %u %e < %e)", i, maxSoFar, epsilon); else { *istarP = maxIdx; *errorP = NULL; } } static void eliminateOneUnknown(unsigned int const i, unsigned int const n, double ** const a, double * const c, const char ** const errorP) { unsigned int maxRow; findLargestIthCoeff(n, a, i, &maxRow, errorP); if (!*errorP) { /* swap rows 'i' and 'maxRow' in 'a' and 'c', so that the ith row has the largest ith coefficient. */ unsigned int j; for (j = 0; j < n; j++) swap(&a[maxRow][j], &a[i][j]); swap(&c[maxRow], &c[i]); /* Combine rows so that the ith coefficient in every row below the ith is zero. */ { unsigned int ii; for (ii = i+1; ii < n; ++ii) { double const multiplier = a[ii][i] / a[i][i]; /* This is what we multiply the whole ith row by to make its ith coefficient equal to that in the iith row. */ unsigned int j; /* Combine ith row into iith row so that the ith coefficient in the iith is zero. */ c[ii] = c[ii] - multiplier * c[i]; for (j = 0; j < n; ++j) a[ii][j] = a[ii][j] - multiplier * a[i][j]; assert(a[ii][i] < epsilon); } } *errorP = NULL; } } void pm_solvelineareq(double ** const aArg, double * const x, double * const cArg, unsigned int const n, const char ** const errorP) { /*---------------------------------------------------------------------------- Solve the matrix equation 'a' * 'x' = 'c' for 'x'. 'n' is the dimension of the matrices. 'a' is 'n' x 'n', while 'x' and 'c' are 'n' x 1. -----------------------------------------------------------------------------*/ /* We use Gaussian reduction. */ double ** a; double * c; initializeWorkMatrices(n, aArg, cArg, &a, &c, errorP); if (!*errorP) { unsigned int i; for (i = 0, *errorP = NULL; i < n && !*errorP; ++i) eliminateOneUnknown(i, n, a, c, errorP); if (!*errorP) { /* a[] now has all zeros in the lower left triangle. */ /* Work from the bottom up to solve for the unknowns x[], from the a and c rows in question and all the x[] below it */ unsigned int k; for (k = 0; k < n; ++k) { unsigned int const m = n - k - 1; unsigned int j; double xwork; for (j = m+1, xwork = c[m]; j < n; ++j) xwork -= a[m][j] * x[j]; x[m] = xwork / a[m][m]; } } } pm_freearray2((void**)a); free(c); } advanced/lib/util/wordaccess_be_unaligned.h0000644000175000001440000000116512347602012020211 0ustar ericusers/*============================================================================= This file is the part of wordaccess.h for use on a big-endian machine that does not require word accesses to be word-aligned. *===========================================================================*/ 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; } advanced/lib/util/token.c0000644000175000001440000000435012347602012014472 0ustar ericusers#include #include #include "nstring.h" #include "token.h" void pm_gettoken(const char * const tokenStart, char const delimiter, const char ** const tokenP, const char ** const nextP, const char ** const errorP) { /*---------------------------------------------------------------------------- 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; /* initial value */ cursor = tokenStart; /* initial value */ *errorP = NULL; /* initial value */ while (*cursor != delimiter && *cursor != '\0' && !*errorP) { if (*cursor == '\\') { ++cursor; if (*cursor == '\0') pm_asprintf(errorP, "string ends with an escape character (\\)"); } else { ++cursor; ++charCount; } } if (!*errorP) { token = malloc(charCount + 1); if (token == NULL) pm_asprintf(errorP, "Could not allocate %u bytes of memory " "to parse a string", charCount + 1); else { /* 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; } } } advanced/lib/util/shhopt.c0000644000175000001440000007751412347602012014673 0ustar ericusers/*------------------------------------------------------------------------ | 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 "token.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 structure 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; } static int optMatch(optEntry const opt[], const char * const s, int const lng) { /*------------------------------------------------------------------------ | 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. */ unsigned int const nopt = optStructCount(opt); unsigned int q; unsigned int matchlen; const char * p; matchlen = 0; /* initial value */ if (lng) { if ((p = strchr(s, '=')) != NULL) matchlen = p - s; else matchlen = strlen(s); } for (q = 0; q < nopt; ++q) { if (lng) { if (opt[q].longName) { if (strncmp(s, opt[q].longName, matchlen) == 0) return q; } } else { if (opt[q].shortName) { if (s[0] == 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 || opt.type == OPT_STRINGLIST ; } /*------------------------------------------------------------------------ | 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. -----------------------------------------------------------------------------*/ const char * error; pm_gettoken(tokenStart, delimiter, tokenP, nextP, &error); if (error) optFatal("error parsing a token: %s", error); } 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; } static void parseStringList(const char * const listText, const char *** const listP) { unsigned int const maxStringCount = 100; const char * cursor; unsigned int stringCount; const char ** list; MALLOCARRAY_NOFAIL(list, maxStringCount+1); cursor = &listText[0]; /* initial value */ stringCount = 0; /* initial value */ while (stringCount < maxStringCount && *cursor != '\0') { const char * next; getToken(cursor, ',', &list[stringCount++], &next); cursor = next; if (*cursor != '\0') { assert(*cursor == ','); ++cursor; } } list[stringCount] = 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 * tailPtr; if (arg == NULL) optFatal("internal error: optExecute() called with NULL argument " "'%s'", optString(opt, lng)); if (arg[0] == '-' || arg[1] == '+') optFatal("unsigned number '%s' has a sign ('%c')", arg, arg[0]); tmp = strtoul(arg, &tailPtr, 10); if (*tailPtr) optFatal("invalid number `%s'", arg); if (errno == ERANGE || (opt.type == OPT_UINT && tmp > UINT_MAX)) 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; case OPT_STRINGLIST: { if (arg == NULL) optFatal("internal error: optExecute() called with NULL argument " "'%s'", optString(opt, lng)); if (opt.arg) parseStringList(arg, (const char ***)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 pm_optSetFatalFunc(void (*f)(const char *, ...)) { optFatal = f; } /*------------------------------------------------------------------------ | NAME pm_optParseOptions | | FUNCTION Parse commandline options. | | SYNOPSIS #include "shhopt.h" | void pm_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 pm_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) { /*---------------------------------------------------------------------------- Parse a cluster of short options, e.g. -walne . The last option in the cluster might take an argument, and we parse that as well. e.g. -cf myfile or -cfmyfile . argv[] and argc describe the whole program argument set. 'ai' is the index of the argument that is the short option cluster. -----------------------------------------------------------------------------*/ char *o; /* A short option character */ char *arg; int mi; /* index into option table */ 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 fatalUnrecognizedLongOption(const char * const optionName, optEntry const optTable[]) { unsigned int const nopt = optStructCount(optTable); unsigned int q; char optList[1024]; optList[0] = '\0'; /* initial value */ for (q = 0; q < nopt && strlen(optList) + 1 <= sizeof(optList); ++q) { const optEntry * const optEntryP = &optTable[q]; const char * entry; if (optEntryP->longName) pm_asprintf(&entry, "-%s ", optEntryP->longName); else pm_asprintf(&entry, "-%c ", optEntryP->shortName); strncat(optList, entry, sizeof(optList) - strlen(optList) - 1); pm_strfree(entry); if (strlen(optList) + 1 == sizeof(optList)) { /* Buffer is full. Overwrite end of list with ellipsis */ strcpy(&optList[sizeof(optList) - 4], "..."); } } optFatal("unrecognized option '%s'. Recognized options are: %s", optionName, optList); } static void parse_long_option(char * const argv[], int const argc, int const ai, int const namepos, optEntry const opt_table[], int * const tokens_consumed_p) { /*---------------------------------------------------------------------------- Parse a long option, e.g. -verbose or --verbose. The option might take an argument, and we parse that as well. e.g. -file=myfile or -file myfile . argv[] and argc describe the whole program argument set. 'ai' is the index of the argument that is the long option. -----------------------------------------------------------------------------*/ char *equals_arg; /* The argument of an option, included in the same token, after a "=". NULL if no "=" in the token. */ 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) fatalUnrecognizedLongOption(argv[ai], opt_table); /* 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, but you " "have specified it in the form name=value", optString(opt_table[mi], 1)); else arg = NULL; } /* perform the action of this option. */ optExecute(opt_table[mi], arg, 1); } /*------------------------------------------------------------------------ | NAME pm_optParseOptions2 | | FUNCTION Parse commandline options. | | SYNOPSIS #include "shhopt.h" | void pm_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 pm_optParseOptions in that it accepts | long options with just one hyphen and doesn't accept | any short options. It also has accommodations 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 pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, const unsigned long flags) /*---------------------------------------------------------------------------- This does the same thing as pm_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)"); pm_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 pm_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 pm_optParseOptions in that it accepts | long options with just one hyphen and doesn't accept | any short options. It also has accommodations 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 pm_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 pm_optDestroyNameValueList(struct optNameValue * const list) { unsigned int i; for (i = 0; list[i].name; ++i) { pm_strfree(list[i].name); pm_strfree(list[i].value); } free(list); } advanced/lib/util/nstring.h0000644000175000001440000001366412347602012015053 0ustar ericusers#ifndef _NSTRING_H #define _NSTRING_H #include #include #include #include "pm_c_util.h" #include "pm.h" /* For PM_GNU_PRINTF_ATTR, __inline__ */ #ifdef __cplusplus extern "C" { #endif #if 0 } /* to fake out automatic code indenters */ #endif /* Here are string functions that respect the size of the array into which you are copying -- E.g. STRSCPY truncates the source string as required so that it fits, with the terminating null, in the destination array. */ #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 STRSEQ(A, B) \ (strneq((A), (B), sizeof(A))) #define MEMEQ(a,b,c) (memcmp(a, b, c) == 0) #define MEMSEQ(a,b) (memeq(a, b, sizeof(*(a))) == 0) #define MEMSSET(a,b) (memset(a, b, sizeof(*(a)))) #define MEMSCPY(a,b) (memcpy(a, b, sizeof(*(a)))) #define MEMSZERO(a) (MEMSSET(a, 0)) static __inline__ int streq(const char * const comparand, const char * const comparator) { return strcmp(comparand, comparator) == 0; } static __inline__ int strneq(const char * const comparand, const char * const comparator, size_t const size) { return strncmp(comparand, comparator, size) == 0; } static __inline__ int memeq(const void * const comparand, const void * const comparator, size_t const size) { return memcmp(comparand, comparator, size) == 0; } /* The Standard C Library may not declare strcasecmp() if the including source file doesn't request BSD functions, with _BSD_SOURCE. So we don't define functions that use strcasecmp() in that case. */ #ifdef _BSD_SOURCE static __inline__ int strcaseeq(const char * const comparand, const char * const comparator) { return strcasecmp(comparand, comparator) == 0; } static __inline__ int strncaseeq(const char * const comparand, const char * const comparator, size_t const size) { return strncasecmp(comparand, comparator, size) == 0; } #endif /* The standard C library routines isdigit(), for some weird 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 'pm_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 pm_asprintf(). */ extern const char * const pm_strsol; int pm_snprintf(char * const dest, size_t const str_m, const char * const fmt, ...) PM_GNU_PRINTF_ATTR(3,4); void pm_vsnprintf(char * const str, size_t const str_m, const char * const fmt, va_list ap, size_t * const sizeP); const char * pm_strdup(const char * const arg); void pm_asprintf(const char ** const resultP, const char * const fmt, ...) PM_GNU_PRINTF_ATTR(2,3); void pm_vasprintf(const char ** const resultP, const char * const format, va_list args); void pm_strfree(const char * const string); const char * pm_strsep(char ** const stringP, const char * const delim); int pm_stripeq(const char * const comparand, const char * const comparator); const void * pm_memmem(const void * const haystackArg, size_t const haystacklen, const void * const needleArg, size_t const needlelen); bool pm_strishex(const char * const subject); void pm_interpret_uint(const char * const string, unsigned int * const valueP, const char ** const errorP); #ifdef __cplusplus } #endif #endif advanced/lib/standard.ppmdfont0000644000175000001440000000747312347602012015613 0ustar ericusersppmdfont_ ûôÿ ýôõôóôöøù ô õô ó ô ö øù ö ðúðúýýùö þðþ ð ÷õôþôûõù÷ùùúûûüýýÿ þ ûùô  ô÷ üôþöþøýúûûùû÷ù÷÷øõúôüôþõööõ ô    "ó  ý ü ûûüþþ ú ø÷öö÷øÿüûù÷õþôüõû÷ûùüüþÿ    ûöÿõôõ÷ùÿú ùðòõþùýþýþ  ùüðþòõùþ þüøúûýýûó ÷ ÷ û ÿ  ÿ ó ÷ ûÿ õ  ð÷ö ÿôüõúøùýùúüÿ  ýøõôÿôö üøþ÷ô ö úùúøûöüõþôôõöøúüÿù  ö ûôôüüýþ þ ûúùö ôùô ö ôûôúýûüþûûüþ þ ûúùö ÷õôôýõûøúýúûý  ÿýüüýýûÿúö ôý ùôôö þôûõú÷úùûûýüýþ þ ûúùùúüþÿýüûù÷õôþôö ûþÿüúþùûùúú÷üõÿôôõ÷û þ ûú ûûÿüýüûÿ ûûÿüýüû ÿ  ÿ ô ÷ø ó ÷ý ý÷ ô ø÷ø ÷ úùúøûöüõþôôõöøúüýÿÿ 7óüúùÿùýúüûûþûüþÿùýûüþüýþù  ÿ ý ú øöõôÿôüõúöøø÷úöýö÷øúüÿ  ù÷ ôø ô ûõ ùôù ùôôõöøúüýþùþþÿ ù ö ù÷õôÿôýõû÷úùùüùúûýÿ  õ ùôù ùôôõ÷ùü ù ö úôú úôôúþþú  öúôú úôôúþþö ù÷õôÿôýõû÷úùùüùúûýÿ  õ ùôù ô ùþþüô øô þ üûúúõ ùôù ôùþý öúôú  ô øôø øô ô ô õ ùôù ùô ô õ þôüõú÷ùùøüøùúüþ  üù÷õôþô õ ùôù ùôôõöøûýþÿùÿõ þôüõú÷ùùøüøùúüþ  üù÷õôþô õ ùôù ùôôõöøúüýþùþþ ö ÷õôþôûõù÷ùùúûûüýýÿ þ ûùøô ùôô õ ùôùúüÿ  ô÷ øô ô ô öôû ôû ô  ô ö ùô ôù ÷ øôþ ôþö ôù ùôôù  ùýðýþðþýððýõ  ÷ð ùððüððüõ øýøþý  ûôõÿ÷ÿùúùø÷ û þüûÿûýüûþúúûýÿ  ö úôú úþüüþûûüþ þ üú÷ þüûÿûýüûþúúûýÿ  ÷ ô þüûÿûýüûþúúûýÿ  ÷ úÿýüûÿûýüûþúúûýÿ  ûôôõø ýûû÷ û ÿýþüûÿûýüûþúúûýÿ   ÷ ûôû ûÿþüûûüÿ üÿôõôóÿôû ûôõôóôû þü÷ûôû ûûÿ üô ñõûõ õÿøüúûýûÿüÿ ÿüûû ü ÿ ÷ ûûû ûÿþüûûüÿ ÷ ÿûýüûþúúûýÿ  þüûÿûö úûúúþüüþûûüþ þ üú÷ ûþüûÿûýüûþúúûýÿ  ùýûý ýþþüûûø þüûÿûüüûþüþ ÿ üûûô  ýûû ÷ ûûûüþ  û øúû û õ øûü ûü û û ø ûû ûû øúû û þ üúùø ûû ûûûû  'ùðñÿòþôþöÿøùûýÿÿñÿóÿõ÷øúüþý ÿ ÿ ÿÿþ þ ÿüð'ùþðñòôöøùÿûÿýÿñóõ÷ÿøþúþüÿþÿþþÿ   ÿÿ  þÿÿýûûü ÿ ÿþþýýþ  ÿüadvanced/lib/ppmcmap.h0000644000175000001440000000720512347602012014041 0ustar ericusers#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(pixel * const colorrow, int const ncolors); pixel * ppm_computecolorrow(pixel ** const pixels, int const cols, int const rows, int const maxcolors, int * const ncolorsP); pixel * ppm_mapfiletocolorrow(FILE * const fileP, int const maxcolors, int * const ncolorsP, pixval * const maxvalP); void ppm_colorrowtomapfile(FILE * const ofP, pixel * const colormap, int const ncolors, pixval const maxval); void ppm_sortcolorrow(pixel * const colorrow, int const ncolors, int (*cmpfunc)(pixel *, pixel *)); int ppm_addtocolorrow(pixel * const colorrow, int * const ncolorsP, int const maxcolors, pixel * const pixelP); int ppm_findclosestcolor(const pixel * const colormap, int const ncolors, const pixel * const pP); /* standard sort function for ppm_sortcolorrow() */ #define PPM_STDSORT (int (*)(pixel *, pixel *))0 #endif #ifdef __cplusplus } #endif advanced/lib/libpgm1.c0000644000175000001440000002666212347602012013742 0ustar ericusers/* 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 pmfileio.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" #include "nstring.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. It is very normal to allocate space for a pixel row, so we make sure the size of a pixel row, in bytes, can be represented by an 'int'. 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 / (sizeof(gray)) || 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 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 PGM_TYPE: *formatP = realFormat; pgm_readpgminitrest(fileP, colsP, rowsP, maxvalP); break; case PPM_TYPE: pm_error("Input file is a PPM, which this program cannot process. " "You may want to convert it to PGM with 'ppmtopgm'"); case PAM_TYPE: pnm_readpaminitrestaspnm(fileP, colsP, rowsP, maxvalP, formatP); 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 Netpbm file"); } validateComputableSize(*colsP, *rowsP); } static void validateRpgmRow(gray * const grayrow, unsigned int const cols, gray const maxval, const char ** const errorP) { /*---------------------------------------------------------------------------- Check for sample values above maxval in input. Note: a program that wants to deal with invalid sample values itself can simply make sure it uses a sufficiently high maxval on the read function call, so this validation never fails. -----------------------------------------------------------------------------*/ if (maxval == 255 || maxval == 65535) { /* There's no way a sample can be invalid, so we don't need to look at the samples individually. */ *errorP = NULL; } else { unsigned int col; for (col = 0; col < cols; ++col) { if (grayrow[col] > maxval) { pm_asprintf(errorP, "gray value %u is greater than maxval (%u)", grayrow[col], maxval); return; } } *errorP = NULL; } } static void readRpgmRow(FILE * const fileP, gray * const grayrow, int const cols, gray const maxval, int const format) { unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; int const bytesPerRow = cols * bytesPerSample; unsigned char * rowBuffer; const char * error; MALLOCARRAY(rowBuffer, bytesPerRow); if (rowBuffer == NULL) pm_asprintf(&error, "Unable to allocate memory for row buffer " "for %u columns", cols); else { size_t rc; rc = fread(rowBuffer, 1, bytesPerRow, fileP); if (rc == 0) pm_asprintf(&error, "Error reading row. fread() errno=%d (%s)", errno, strerror(errno)); else if (rc != bytesPerRow) pm_asprintf(&error, "Error reading row. Short read of %u bytes " "instead of %u", (unsigned)rc, bytesPerRow); else { 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; } } validateRpgmRow(grayrow, cols, maxval, &error); } free(rowBuffer); } if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } } static void readPbmRow(FILE * const fileP, gray * const grayrow, int const cols, gray const maxval, int const format) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; bit * bitrow; bitrow = pbm_allocrow_packed(cols); if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pbm_readpbmrow_packed(fileP, bitrow, cols, format); for (col = 0; col < cols; ++col) { grayrow[col] = ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ? maxval : 0 ; } pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); } void pgm_readpgmrow(FILE * const fileP, 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(fileP); if (grayrow[col] > maxval) pm_error("value out of bounds (%u > %u)", grayrow[col], maxval); } } break; case RPGM_FORMAT: readRpgmRow(fileP, grayrow, cols, maxval, format); break; case PBM_FORMAT: case RPBM_FORMAT: readPbmRow(fileP, grayrow, cols, maxval, format); break; default: pm_error("can't happen"); } } gray ** pgm_readpgm(FILE * const fileP, int * const colsP, int * const rowsP, gray * const maxvalP) { gray ** grays; int rows, cols; gray maxval; int format; jmp_buf jmpbuf; jmp_buf * origJmpbufP; pgm_readpgminit(fileP, &cols, &rows, &maxval, &format); grays = pgm_allocarray(cols, rows); if (setjmp(jmpbuf) != 0) { pgm_freearray(grays, rows); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int row; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (row = 0; row < rows; ++row) pgm_readpgmrow(fileP, grays[row], cols, maxval, format); pm_setjmpbuf(origJmpbufP); } *colsP = cols; *rowsP = rows; *maxvalP = maxval; return grays; } void pgm_check(FILE * const file, enum pm_check_type const checkType, int const format, int const cols, int const rows, gray const maxval, enum pm_check_code * const retvalP) { 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 (checkType != PM_CHECK_BASIC) { if (retvalP) *retvalP = PM_CHECK_UNKNOWN_TYPE; } else if (PGM_FORMAT_TYPE(format) == PBM_TYPE) { pbm_check(file, checkType, format, cols, rows, retvalP); } else if (format != RPGM_FORMAT) { if (retvalP) *retvalP = PM_CHECK_UNCHECKABLE; } else { pm_filepos const bytesPerRow = cols * (maxval > 255 ? 2 : 1); pm_filepos const needRasterSize = rows * bytesPerRow; pm_check(file, checkType, needRasterSize, retvalP); } } advanced/lib/pm.h0000644000175000001440000002311212347602012013013 0ustar ericusers/* 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 #include #include #include #include #include #include #include #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(a,b) #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 /* S_IRUSR is POSIX, defined in Some old BSD systems and Windows systems have S_IREAD instead. Most Unix today (2011) has both. In 2011, Android has S_IRUSR and not S_IREAD. Some Windows has _S_IREAD. We're ignoring S_IREAD now to see if anyone misses it. If there are still users that need it, we can handle it here. */ #if MSVCRT #define PM_S_IWUSR _S_IWRITE #define PM_S_IRUSR _S_IREAD #else #define PM_S_IWUSR S_IWUSR #define PM_S_IRUSR S_IRUSR #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. */ extern const char * pm_progname; void pm_init(const char * const progname, unsigned int const flags); void pm_proginit(int * const argcP, const char * argv[]); void pm_setMessage(int const newState, int * const oldStateP); int pm_getMessage(void); FILE * pm_tmpfile(void); int pm_tmpfile_fd(void); void pm_make_tmpfile(FILE ** const filePP, const char ** const filenameP); void pm_make_tmpfile_fd(int * const fdP, const char ** const filenameP); void pm_nextimage(FILE * const file, int * const eofP); /* Variable-sized arrays definitions. */ char** pm_allocarray (int const cols, int const rows, int const size ); void * pm_allocrow(unsigned int const cols, unsigned int const size); void pm_freearray (char** const its, int const rows); void pm_freerow(void * const row); /* Obsolete -- use shhopt instead */ int pm_keymatch(const 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_fork(int * const iAmParentP, pid_t * const childPidP, const char ** const errorP); void pm_waitpid(pid_t const pid, int * const statusP, int const options, pid_t * const exitedPidP, const char ** const errorP); void pm_waitpidSimple(pid_t const pid); typedef void pm_usermessagefn(const char * msg); void pm_setusermessagefn(pm_usermessagefn * fn); typedef void pm_usererrormsgfn(const char * msg); void pm_setusererrormsgfn(pm_usererrormsgfn * fn); void PM_GNU_PRINTF_ATTR(1,2) pm_message (const char format[], ...); void PM_GNU_PRINTF_ATTR(1,2) pm_errormsg(const char format[], ...); void PM_GNU_PRINTF_ATTR(1,2) pm_error (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_readbiglong2(FILE * const ifP, int32_t * const lP); static __inline__ int pm_readbiglongu2(FILE * const ifP, uint32_t * const lP) { return pm_readbiglong2(ifP, (int32_t *) 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_readlittlelong2(FILE * const ifP, int32_t * const lP); static __inline__ int pm_readlittlelong2u(FILE * const ifP, uint32_t * const lP) { return pm_readlittlelong2(ifP, (int32_t *) 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); void pm_drain(FILE * const fileP, unsigned int const limit, unsigned int * const bytesReadP); char * pm_arg0toprogname(const char arg0[]); unsigned int pm_randseed(void); unsigned int pm_parse_width(const char * const arg); unsigned int pm_parse_height(const char * const arg); #ifdef __cplusplus } #endif #endif advanced/lib/ppmdraw.h0000644000175000001440000003075512347602012014064 0ustar ericusers/* 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: */ #include #ifdef __cplusplus extern "C" { #endif #if 0 } /* to fake out automatic code indenters */ #endif typedef struct { int x; int y; } ppmd_point; static __inline__ ppmd_point ppmd_makePoint(int const x, int const y) { ppmd_point p; p.x = x; p.y = y; return p; } void ppmd_validateCoord(int const c); void ppmd_validatePoint(ppmd_point const p); typedef enum { PPMD_PATHLEG_LINE } ppmd_pathlegtype; 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_drawprocp(pixel **, unsigned int, unsigned int, pixval, ppmd_point, const void *); typedef void ppmd_drawproc(pixel **, int, int, pixval, int, int, const void *); ppmd_drawprocp ppmd_point_drawprocp; ppmd_drawproc ppmd_point_drawproc; /* ** 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_linep(pixel ** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const p0, ppmd_point const p1, ppmd_drawprocp drawProc, const void * const clientdata); /* Draws a line from p0 to p1. */ void ppmd_line(pixel** const pixels, int const cols, int const rows, 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_spline3p(pixel ** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const p0, ppmd_point const p1, ppmd_point const p2, ppmd_drawprocp drawProc, const void * const clientdata); /* Draws a three-point spline from p0 to p2, with p1 as the control point. All drawing is done via ppmd_linep(), so the routines that control it control ppmd_spline3p() as well. */ void ppmd_spline3(pixel ** const pixels, 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); void ppmd_polysplinep(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p0, unsigned int const nc, ppmd_point * const c, ppmd_point const p1, ppmd_drawprocp drawProc, const void * const clientdata); /* Draws a bunch of splines end to end. p0 and p1 are the initial and final points, and the c[] are the intermediate control points. nc is the number of these control points. */ void 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); void ppmd_spline4p(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const endPt0, ppmd_point const endPt1, ppmd_point const ctlPt0, ppmd_point const ctlPt1, ppmd_drawprocp drawproc, const void * const clientdata); void ppmd_circlep(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const center, unsigned int const radius, ppmd_drawprocp drawProc, const void * const clientData); /* Draws a circle centered at 'center' with radius 'radius' */ void ppmd_circle(pixel ** const pixels, 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); /* 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); /* For backward compatibility only: */ char * ppmd_fill_init(void); void ppmd_fill_drawprocp(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p, const void * const clientdata); /* Use this drawproc to trace the outline you want filled. Use the fillhandle as the clientdata. */ void ppmd_fill_drawproc(pixel ** const pixels, int const cols, int const rows, pixval const maxval, int const x, int const y, const void * const clientdata); void ppmd_fill(pixel ** const pixels, int const cols, 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_textp(pixel** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const pos, int const height, int const angle, const char * const sArg, ppmd_drawprocp drawProc, const void * const clientdata); void ppmd_text(pixel** const pixels, int const cols, int const rows, pixval const maxval, int const xpos, int const ypos, int const height, int const angle, const char * const sArg, ppmd_drawproc drawProc, const void * const clientdata); /* Draws the null-terminated string 's' left justified at the point ('x', 'y'). The text will be 'height' pixels high and will be aligned on a baseline inclined 'angle' degrees with the X axis. The supplied 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 advanced/lib/libpamread.c0000644000175000001440000002523312347602012014500 0ustar ericusers/*============================================================================= 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). This file was originally written by Bryan Henderson and is contributed to the public domain by him and subsequent authors. =============================================================================*/ /* See pmfileio.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 "pm_config.h" #include "fileio.h" #include "nstring.h" #include "pam.h" static void readPbmRow(const struct pam * const pamP, tuple * const tuplerow) { if (pamP->depth != 1) pm_error("Invalid pam structure passed to pnm_readpamrow(). " "It says PBM format, but 'depth' member is not 1."); else { jmp_buf jmpbuf; jmp_buf * origJmpbufP; unsigned char * bitrow; bitrow = (unsigned char *) pbm_allocrow(pbm_packed_bytes(pamP->width)); if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width, pamP->format); if (tuplerow) { unsigned int col; for (col = 0; col < pamP->width; ++col) { tuplerow[col][0] = ( ((bitrow[col/8] >> (7-col%8)) & 1 ) == PBM_BLACK) ? PAM_PBM_BLACK : PAM_PBM_WHITE ; } } pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); } } 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); if (tuplerow[col][plane] > pamP->maxval) pm_error("Plane %u sample value %lu exceeds the " "image maxval of %lu", plane, tuplerow[col][plane], pamP->maxval); } 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 validatePamRow(const struct pam * const pamP, tuple * const tuplerow, const char ** const errorP) { /*---------------------------------------------------------------------------- Check for sample values above maxval in input. Note: a program that wants to deal with invalid sample values itself can simply make sure it sets pamP->maxval sufficiently high, so this validation never fails. -----------------------------------------------------------------------------*/ /* To save time, skip the test for if the maxval is a saturated value (255, 65535) or format is PBM. This is an expensive test, but is skipped in most cases: in practice maxvals other than 255 or 65535 are uncommon. Thus we do this in a separate pass through the row rather than while reading in the row. */ if (pamP->maxval == (((sample) 0x1) << pamP->bytes_per_sample*8) - 1 || PAM_FORMAT_TYPE(pamP->format) == PBM_FORMAT) { /* There's no way a sample can be invalid, so we don't need to look at the samples individually. */ *errorP = NULL; } else { unsigned int col; for (col = 0; col < pamP->width; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) { if (tuplerow[col][plane] > pamP->maxval) { pm_asprintf(errorP, "Plane %u sample value %lu exceeds the " "image maxval of %lu", plane, tuplerow[col][plane], pamP->maxval); return; } } } *errorP = NULL; } } 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; const char * error; inbuf = pnm_allocrowimage(pamP); bytesRead = fread(inbuf, 1, rowImageSize, pamP->file); if (bytesRead != rowImageSize) { if (feof(pamP->file)) pm_asprintf(&error, "End of file encountered " "when trying to read a row from input file."); else pm_asprintf(&error, "Error reading a row from input file. " "fread() fails with errno=%d (%s)", errno, strerror(errno)); } else { error = NULL; /* initial assumption */ if (tuplerow) { switch (pamP->bytes_per_sample) { case 1: parse1BpsRow(pamP, tuplerow, inbuf); break; case 2: parse2BpsRow(pamP, tuplerow, inbuf); break; case 3: parse3BpsRow(pamP, tuplerow, inbuf); break; case 4: parse4BpsRow(pamP, tuplerow, inbuf); break; default: pm_asprintf(&error, "invalid bytes per sample passed to " "pnm_formatpamrow(): %u", pamP->bytes_per_sample); } if (error == NULL) validatePamRow(pamP, tuplerow, &error); } } pnm_freerowimage(inbuf); if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } } 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_readpaminit(). */ /* 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) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; tuple ** tuplearray; pnm_readpaminit(fileP, pamP, size); tuplearray = pnm_allocpamarray(pamP); if (setjmp(jmpbuf) != 0) { pnm_freepamarray(tuplearray, pamP); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int row; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (row = 0; row < pamP->height; ++row) pnm_readpamrow(pamP, tuplearray[row]); pm_setjmpbuf(origJmpbufP); } return tuplearray; } advanced/lib/libpgm.h0000644000175000001440000000050012347602012013645 0ustar ericusers/* 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); #endif advanced/lib/libpbm2.c0000644000175000001440000002042712347602012013727 0ustar ericusers/* 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 #include "pbm.h" #include "libpbm.h" #include "fileio.h" #include "pam.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."); } static void validateComputableSize(unsigned int const cols, unsigned int const rows) { /*---------------------------------------------------------------------------- Validate that the dimensions of the image are such that it can be processed in typical ways on this machine without worrying about overflows. Note that in C, arithmetic is always modulus arithmetic, so if your values are too big, the result is not what you expect. That failed expectation can be disastrous if you use it to allocate memory. A common operation is adding 1 or 2 to the highest row or column number in the image, so we make sure that's possible. -----------------------------------------------------------------------------*/ if (cols > INT_MAX - 2) pm_error("image width (%u) too large to be processed", cols); if (rows > INT_MAX - 2) pm_error("image height (%u) too large to be processed", rows); } void pbm_readpbminit(FILE * const ifP, int * const colsP, int * const rowsP, int * const formatP) { *formatP = pm_readmagicnumber(ifP); switch (PAM_FORMAT_TYPE(*formatP)) { case PBM_TYPE: pbm_readpbminitrest(ifP, colsP, rowsP); break; case PGM_TYPE: pm_error("The input file is a PGM, not a PBM. You may want to " "convert it to PBM with 'pamditherbw | pamtopnm' or " "'pamthreshold | pamtopnm'"); case PPM_TYPE: pm_error("The input file is a PPM, not a PBM. You may want to " "convert it to PBM with 'ppmtopgm', 'pamditherbw', and " "'pamtopnm'"); case PAM_TYPE: pm_error("The input file is a PAM, not a PBM. " "If it is a black and white image, you can convert it " "to PBM with 'pamtopnm'"); break; default: pm_error("bad magic number - not a Netpbm file"); } validateComputableSize(*colsP, *rowsP); } 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 fileP, unsigned char * const packedBits, 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) packedBits[byteIndex] = 0x00; for (col = 0; col < cols; ++col) { unsigned char mask; mask = getbit(fileP) << (7 - col % 8); packedBits[col / 8] |= mask; } } break; case RPBM_FORMAT: { int bytes_read; bytes_read = fread(packedBits, 1, pbm_packed_bytes(cols), fileP); if (bytes_read < pbm_packed_bytes(cols)) { if (feof(fileP)) 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." ); } } void pbm_readpbmrow_bitoffset(FILE * const ifP, unsigned char * const packedBits, int const cols, int const format, unsigned int const offset) { /*---------------------------------------------------------------------------- Read PBM packed bitrow from file 'ifP' (raster format given by 'cols' and 'format') and shift right 'offset' bits. Read it into packedBits[], preserving surrounding image data. Logic not tested for negative offsets. -----------------------------------------------------------------------------*/ unsigned int const rsh = offset % 8; unsigned int const lsh = (8 - rsh) % 8; unsigned char * const window = &packedBits[offset/8]; /* Area of packed row buffer into which we read the image data. Aligned to nearest byte boundary to the left, so the first few bits might contain original data, not output. */ unsigned int const last = pbm_packed_bytes(cols+rsh) - 1; /* Position within window of rightmost byte after shift */ /* The original leftmost and rightmost chars. */ unsigned char const origHead = window[0]; unsigned char const origEnd = window[last]; pbm_readpbmrow_packed(ifP, window, cols, format); if (rsh > 0) { /* Target slot doesn't start on byte boundary; right-shift. */ unsigned char carryover; unsigned int i; carryover = (origHead >> lsh) << lsh; for (i = 0; i <= last; ++i) { unsigned char const t = window[i] << lsh; window[i] = carryover | window[i] >> rsh; carryover = t; } } if ((cols + rsh) % 8 > 0) { /* Adjust rightmost char */ unsigned int const trs = (cols + rsh) % 8; unsigned int const tls = 8 - trs; unsigned char const rightBits = ((unsigned char)(origEnd << trs) >> trs); unsigned char const leftBits = ((unsigned char)(window[last] >> tls) << tls); window[last] = leftBits | rightBits; } } bit** pbm_readpbm( file, colsP, rowsP ) FILE* file; 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; } advanced/lib/libppmfloyd.c0000644000175000001440000001713012347602012014716 0ustar ericusers/* 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(unsigned int const cols, pixval const maxval, unsigned int const flags) { ppm_fs_info * fiP; fiP = allocateFi(cols); fiP->lefttoright = 1; fiP->cols = cols; fiP->maxval = maxval; fiP->flags = flags; if (flags & FS_RANDOMINIT) { unsigned int i; srand(pm_randseed()); for (i = 0; i < cols +2; ++i) { /* random errors in [-1..+1] */ fiP->thisrederr[i] = rand() % 32 - 16; fiP->thisgreenerr[i] = rand() % 32 - 16; fiP->thisblueerr[i] = rand() % 32 - 16; } } else { unsigned int i; for (i = 0; i < cols + 2; ++i) fiP->thisrederr[i] = fiP->thisgreenerr[i] = fiP->thisblueerr[i] = 0; } return fiP; } 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 */ } } } advanced/lib/libpnm1.c0000644000175000001440000001610512347602012013740 0ustar ericusers/* 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. It is very normal to allocate space for a pixel row, so we make sure the size of a pixel row, in bytes, can be represented by an 'int'. 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/(sizeof(pixval) * 3) || 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); } static void readpgmrow(FILE * const fileP, xel * const xelrow, int const cols, xelval const maxval, int const format) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; gray * grayrow; grayrow = pgm_allocrow(cols); if (setjmp(jmpbuf) != 0) { pgm_freerow(grayrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pgm_readpgmrow(fileP, grayrow, cols, (gray) maxval, format); for (col = 0; col < cols; ++col) PNM_ASSIGN1(xelrow[col], grayrow[col]); pm_setjmpbuf(origJmpbufP); } pgm_freerow(grayrow); } static void readpbmrow(FILE * const fileP, xel * const xelrow, int const cols, xelval const maxval, int const format) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; bit * bitrow; bitrow = pbm_allocrow_packed(cols); if (setjmp(jmpbuf) != 0) { pbm_freerow_packed(bitrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pbm_readpbmrow_packed(fileP, bitrow, cols, format); for (col = 0; col < cols; ++col) { pixval const g = ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ? maxval : 0; PNM_ASSIGN1(xelrow[col], g); } pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); } 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: readpgmrow(fileP, xelrow, cols, maxval, format); break; case PBM_TYPE: readpbmrow(fileP, xelrow, cols, maxval, format); 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) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; int cols, rows; xelval maxval; int format; xel ** xels; pnm_readpnminit(fileP, &cols, &rows, &maxval, &format); xels = pnm_allocarray(cols, rows); if (setjmp(jmpbuf) != 0) { pnm_freearray(xels, rows); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int row; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (row = 0; row < rows; ++row) pnm_readpnmrow(fileP, xels[row], cols, maxval, format); pm_setjmpbuf(origJmpbufP); } *colsP = cols; *rowsP = rows; *maxvalP = maxval; *formatP = format; return xels; } 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"); } } advanced/lib/fileio.c0000644000175000001440000001157612347602012013654 0ustar ericusers/* 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 fileP) { int ich; char ch; ich = getc(fileP); if (ich == EOF) pm_error("EOF / read error reading a byte"); ch = (char) ich; if (ch == '#') { do { ich = getc(fileP); 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) pm_error("ASCII decimal integer in file is " "too large to be processed. "); i *= 10; if (i > INT_MAX - digitVal) pm_error("ASCII decimal integer in file is " "too large to be processed. "); i += digitVal; ch = pm_getc(ifP); } while (ch >= '0' && ch <= '9'); 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); } } advanced/lib/lum.h0000644000175000001440000001436512347602012013206 0ustar ericusers#include /* 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; } advanced/lib/libppmfuzzy.c0000644000175000001440000002204212347602012014766 0ustar ericusers/*============================================================================= 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, 20, hue); } static fzLog hueIsAround015(double const hue) { return memberZ(20, 40, hue); } static fzLog hueIsAround030(double const hue) { return memberTrapez(10, 20, 40, 60, hue); } static fzLog hueIsAround060(double const hue) { return memberTrapez(40, 50, 60, 70, hue); } static fzLog hueIsAround120(double const hue) { return memberTrapez(60, 70, 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.03, 0.08, sat); } static fzLog satIsLow(double const sat) { return memberTrapez(0.03, 0.08, 0.17, 0.2, sat); } static fzLog satIsMedium(double const sat) { return memberTrapez(0.17, 0.2, 0.6, 0.8, sat); } static fzLog satIsHigh(double const sat) { return memberS(0.6, 0.8, sat); } /*---------------------------------------------------------------------------- Value membership functions -----------------------------------------------------------------------------*/ static fzLog valIsVeryLow(double const val) { return memberZ(0.05, 0.2, val); } static fzLog valIsLow(double const val) { return memberTrapez(0.05, 0.2, 0.25, 0.3, val); } static fzLog valIsMedium(double const val) { return memberTrapez(0.25, 0.3, 0.6, 0.7, val); } static fzLog valIsHigh(double const val) { return memberTrapez(0.6, 0.7, 0.95, 0.97, val); } static fzLog valIsVeryHigh(double const val) { return memberS(0.95, 0.97, val); } /*---------------------------------------------------------------------------- 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 valVeryHigh = valIsVeryHigh(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), fzOr(valVeryLow, valLow)); (*bkMatchP)[BKCOLOR_GRAY] = fzAnd(satVeryLow, fzAnd(fzNot(valVeryLow), fzNot(valVeryHigh))); (*bkMatchP)[BKCOLOR_WHITE] = fzAnd(satVeryLow, valVeryHigh); (*bkMatchP)[BKCOLOR_RED] = fzAnd(fzAnd(fzOr(hueAround000, hueAround360), fzNot(satVeryLow)), fzOr(valMedium, valHigh) ); (*bkMatchP)[BKCOLOR_ORANGE] = fzAnd(fzAnd(hueAround030, fzOr(satMedium, satHigh)), fzOr(fzOr(valMedium, valHigh), valVeryHigh) ); (*bkMatchP)[BKCOLOR_YELLOW] = fzAnd(fzAnd(hueAround060, fzOr(satMedium, satHigh)), fzOr(valHigh, valVeryHigh) ); (*bkMatchP)[BKCOLOR_GREEN] = fzAnd(fzAnd(hueAround120, fzOr(satMedium, satHigh)), fzAnd(fzNot(valVeryLow), fzNot(valLow)) ); (*bkMatchP)[BKCOLOR_BLUE] = fzAnd(fzAnd(hueAround180, fzNot(satVeryLow)), fzNot(valVeryLow) ); (*bkMatchP)[BKCOLOR_VIOLET] = fzAnd(fzAnd(hueAround270, fzOr(satMedium, satHigh)), fzOr(valMedium, valHigh) ); (*bkMatchP)[BKCOLOR_PURPLE] = fzAnd(fzAnd(hueAround320, fzOr(satMedium, satHigh)), fzOr(valMedium, valHigh) ); (*bkMatchP)[BKCOLOR_BROWN] = fzOr( fzAnd(fzOr(hueAround015, hueAround360), fzAnd(fzNot(satVeryLow), fzOr(valLow, valMedium))), fzAnd(hueAround015, satLow) ); } 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] = { {174, 174, 174}, /* BKCOLOR_GRAY */ {128, 42, 42}, /* BKCOLOR_BROWN */ {255, 128, 0}, /* BKCOLOR_ORANGE */ {255, 0, 0}, /* BKCOLOR_RED */ {255, 255, 0}, /* BKCOLOR_YELLOW */ { 0, 255, 0}, /* BKCOLOR_GREEN */ { 0, 0, 255}, /* BKCOLOR_BLUE */ {143, 94, 153}, /* BKCOLOR_VIOLET */ {160, 32, 240}, /* BKCOLOR_PURPLE */ {255, 255, 255}, /* BKCOLOR_WHITE */ { 0, 0, 0} /* BKCOLOR_BLACK */ }; 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] = { "gray", "brown", "orange", "red", "yellow", "green", "blue", "violet", "purple", "white", "black" }; 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]; } advanced/lib/libpamn.c0000644000175000001440000004702012347602012014020 0ustar ericusers/*============================================================================= 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. This file was originally written by Bryan Henderson and is contributed to the public domain by him and subsequent authors. =============================================================================*/ #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "pam.h" #include "fileio.h" #include "pm_gamma.h" #define EPSILON 1e-7 static void allocpamrown(const struct pam * const pamP, tuplen ** const tuplerownP, const char ** const errorP) { /*---------------------------------------------------------------------------- We assume that the dimensions of the image are such that arithmetic overflow will not occur in our calculations. NOTE: pnm_readpaminit() ensures this assumption is valid. -----------------------------------------------------------------------------*/ int const bytes_per_tuple = pamP->depth * sizeof(samplen); tuplen * tuplerown; const char * error; /* The tuple row data structure starts with 'width' pointers to the tuples, immediately followed by the 'width' tuples themselves. Each tuple consists of 'depth' samples. */ tuplerown = malloc(pamP->width * (sizeof(tuplen *) + bytes_per_tuple)); if (tuplerown == NULL) pm_asprintf(&error, "Out of memory allocating space for a tuple row of" "%u tuples by %u samples per tuple " "by %u bytes per sample.", pamP->width, pamP->depth, (unsigned)sizeof(samplen)); else { /* Now we initialize the pointers to the individual tuples to make this a regulation C two dimensional array. */ unsigned char * p; unsigned int i; p = (unsigned char*) (tuplerown + pamP->width); /* location of Tuple 0 */ for (i = 0; i < pamP->width; ++i) { tuplerown[i] = (tuplen) p; p += bytes_per_tuple; } *errorP = NULL; *tuplerownP = tuplerown; } } tuplen * pnm_allocpamrown(const struct pam * const pamP) { /*---------------------------------------------------------------------------- We assume that the dimensions of the image are such that arithmetic overflow will not occur in our calculations. NOTE: pnm_readpaminit() ensures this assumption is valid. -----------------------------------------------------------------------------*/ const char * error; tuplen * tuplerown; allocpamrown(pamP, &tuplerown, &error); if (error) { pm_errormsg("pnm_allocpamrown() failed. %s", error); pm_strfree(error); pm_longjmp(); } return tuplerown; } static void readpbmrow(const struct pam * const pamP, tuplen * const tuplenrow) { bit * bitrow; jmp_buf jmpbuf; jmp_buf * origJmpbufP; bitrow = pbm_allocrow(pamP->width); if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pbm_readpbmrow(pamP->file, bitrow, pamP->width, pamP->format); for (col = 0; col < pamP->width; ++col) tuplenrow[col][0] = bitrow[col] == PBM_BLACK ? 0.0 : 1.0; pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); } static void readpamrow(const struct pam * const pamP, tuplen * const tuplenrow) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; tuple * tuplerow; tuplerow = pnm_allocpamrow(pamP); if (setjmp(jmpbuf) != 0) { pnm_freepamrow(tuplerow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { float const scaler = 1.0 / pamP->maxval; /* Note: multiplication is faster than division, so we divide once here so we can multiply many times later. */ unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); 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; } pm_setjmpbuf(origJmpbufP); } pnm_freepamrow(tuplerow); } 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) { if (pamP->depth != 1) pm_error("Invalid pam structure passed to pnm_readpamrow(). " "It says PBM format, but 'depth' member is not 1."); readpbmrow(pamP, tuplenrow); } else readpamrow(pamP, tuplenrow); } static void writepbmrow(const struct pam * const pamP, const tuplen * const tuplenrow) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; bit * bitrow; bitrow = pbm_allocrow(pamP->width); if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (col = 0; col < pamP->width; ++col) bitrow[col] = tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE; pbm_writepbmrow(pamP->file, bitrow, pamP->width, pamP->format == PBM_FORMAT); pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); } static void writepamrow(const struct pam * const pamP, const tuplen * const tuplenrow) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; tuple * tuplerow; tuplerow = pnm_allocpamrow(pamP); if (setjmp(jmpbuf) != 0) { pnm_freepamrow(tuplerow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); 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); pm_setjmpbuf(origJmpbufP); } pnm_freepamrow(tuplerow); } void pnm_writepamrown(const struct pam * const pamP, const tuplen * const tuplenrow) { /* For speed, we don't check any of the inputs for consistency here (unless it's necessary to avoid crashing). Any consistency checking should have been done by a prior call to pnm_writepaminit(). */ assert(pamP->maxval != 0); /* Need a special case for raw PBM because it has multiple tuples (8) packed into one byte. */ if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) writepbmrow(pamP, tuplenrow); else writepamrow(pamP, tuplenrow); } tuplen ** pnm_allocpamarrayn(const struct pam * const pamP) { tuplen ** tuplenarray; const char * error; /* If the speed of this is ever an issue, it might be sped up a little by allocating one large chunk. */ MALLOCARRAY(tuplenarray, pamP->height); if (tuplenarray == NULL) pm_asprintf(&error, "Out of memory allocating the row pointer section of " "a %u row array", pamP->height); else { unsigned int rowsDone; rowsDone = 0; error = NULL; while (rowsDone < pamP->height && !error) { allocpamrown(pamP, &tuplenarray[rowsDone], &error); if (!error) ++rowsDone; } if (error) { unsigned int row; for (row = 0; row < rowsDone; ++row) pnm_freepamrown(tuplenarray[rowsDone]); free(tuplenarray); } } if (error) { pm_errormsg("pnm_allocpamarrayn() failed. %s", error); pm_strfree(error); pm_longjmp(); } 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; jmp_buf jmpbuf; jmp_buf * origJmpbufP; pnm_readpaminit(file, pamP, size); tuplenarray = pnm_allocpamarrayn(pamP); if (setjmp(jmpbuf) != 0) { pnm_freepamarrayn(tuplenarray, pamP); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int row; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (row = 0; row < pamP->height; ++row) pnm_readpamrown(pamP, tuplenarray[row]); pm_setjmpbuf(origJmpbufP); } return tuplenarray; } void pnm_writepamn(struct pam * const pamP, tuplen ** const tuplenarray) { unsigned 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; int 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) { /*---------------------------------------------------------------------------- Either apply or unapply opacity to the row tuplenrow[], per 'applyUnapply'. Apply means to multiply each foreground sample by the opacity value for that pixel; Unapply means to do the inverse, as if the foreground values had already been so multiplied. -----------------------------------------------------------------------------*/ unsigned int opacityPlane; int 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. This never throws an error. Return value NULL means failed. -----------------------------------------------------------------------------*/ pnm_transformMap * retval; pnm_transformMap ungammaTransformMap; MALLOCARRAY(retval, pamP->depth); if (retval != NULL) { MALLOCARRAY(ungammaTransformMap, pamP->maxval+1); if (ungammaTransformMap != NULL) { int 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); } advanced/lib/libpam.h0000644000175000001440000000060412347602012013644 0ustar ericusers/* This is the intra-libnetpbm interface header file for libpam.c */ #ifndef LIBPAM_H_INCLUDED #define LIBPAM_H_INCLUDED #include "pgm.h" void pnm_readpaminitrestaspnm(FILE * const fileP, int * const colsP, int * const rowsP, gray * const maxvalP, int * const formatP); #endif advanced/lib/libppm1.c0000644000175000001440000003361612347602012013750 0ustar ericusers/* 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 pmfileio.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" #include "nstring.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 format 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. It is very normal to allocate space for a pixel row, so we make sure the size of a pixel row, in bytes, can be represented by an 'int'. 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/(sizeof(pixval) * 3) || 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; /* See comment in pgm_readpgminit() about this maxval */ *maxvalP = PPM_MAXMAXVAL; 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); } static void readPpmRow(FILE * const fileP, pixel * const pixelrow, unsigned int const cols, pixval const maxval, int const format) { unsigned int col; for (col = 0; col < cols; ++col) { pixval const r = pm_getuint(fileP); pixval const g = pm_getuint(fileP); pixval const b = pm_getuint(fileP); if (r > maxval) pm_error("Red sample value %u is greater than maxval (%u)", r, maxval); if (g > maxval) pm_error("Green sample value %u is greater than maxval (%u)", g, maxval); if (b > maxval) pm_error("Blue sample value %u is greater than maxval (%u)", b, maxval); PPM_ASSIGN(pixelrow[col], r, g, b); } } static void interpRasterRowRaw(const unsigned char * const rowBuffer, pixel * const pixelrow, unsigned int const cols, unsigned int const bytesPerSample) { unsigned int bufferCursor; 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); } } } static void validateRppmRow(pixel * const pixelrow, unsigned int const cols, pixval const maxval, const char ** const errorP) { /*---------------------------------------------------------------------------- Check for sample values above maxval in input. Note: a program that wants to deal with invalid sample values itself can simply make sure it uses a sufficiently high maxval on the read function call, so this validation never fails. -----------------------------------------------------------------------------*/ if (maxval == 255 || maxval == 65535) { /* There's no way a sample can be invalid, so we don't need to look at the samples individually. */ *errorP = NULL; } else { unsigned int col; for (col = 0, *errorP = NULL; col < cols && !*errorP; ++col) { pixval const r = PPM_GETR(pixelrow[col]); pixval const g = PPM_GETG(pixelrow[col]); pixval const b = PPM_GETB(pixelrow[col]); if (r > maxval) pm_asprintf( errorP, "Red sample value %u is greater than maxval (%u)", r, maxval); else if (g > maxval) pm_asprintf( errorP, "Green sample value %u is greater than maxval (%u)", g, maxval); else if (b > maxval) pm_asprintf( errorP, "Blue sample value %u is greater than maxval (%u)", b, maxval); } } } static void readRppmRow(FILE * const fileP, pixel * const pixelrow, unsigned int const cols, pixval const maxval, int const format) { unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; unsigned int const bytesPerRow = cols * 3 * bytesPerSample; unsigned char * rowBuffer; const char * error; MALLOCARRAY(rowBuffer, bytesPerRow); if (rowBuffer == NULL) pm_asprintf(&error, "Unable to allocate memory for row buffer " "for %u columns", cols); else { ssize_t rc; rc = fread(rowBuffer, 1, bytesPerRow, fileP); if (feof(fileP)) pm_asprintf(&error, "Unexpected EOF reading row of PPM image."); else if (ferror(fileP)) pm_asprintf(&error, "Error reading row. fread() errno=%d (%s)", errno, strerror(errno)); else { size_t const bytesRead = rc; if (bytesRead != bytesPerRow) pm_asprintf(&error, "Error reading row. Short read of %u bytes " "instead of %u", (unsigned)bytesRead, bytesPerRow); else { interpRasterRowRaw(rowBuffer, pixelrow, cols, bytesPerSample); validateRppmRow(pixelrow, cols, maxval, &error); } } free(rowBuffer); } if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } } static void readPgmRow(FILE * const fileP, pixel * const pixelrow, unsigned int const cols, pixval const maxval, int const format) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; gray * grayrow; grayrow = pgm_allocrow(cols); if (setjmp(jmpbuf) != 0) { pgm_freerow(grayrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pgm_readpgmrow(fileP, grayrow, cols, maxval, format); for (col = 0; col < cols; ++col) { pixval const g = grayrow[col]; PPM_ASSIGN(pixelrow[col], g, g, g); } pm_setjmpbuf(origJmpbufP); } pgm_freerow(grayrow); } static void readPbmRow(FILE * const fileP, pixel * const pixelrow, unsigned int const cols, pixval const maxval, int const format) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; bit * bitrow; bitrow = pbm_allocrow_packed(cols); if (setjmp(jmpbuf) != 0) { pbm_freerow_packed(bitrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pbm_readpbmrow_packed(fileP, bitrow, cols, format); for (col = 0; col < cols; ++col) { pixval const g = ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ? maxval : 0; PPM_ASSIGN(pixelrow[col], g, g, g); } pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); } void ppm_readppmrow(FILE * const fileP, pixel * const pixelrow, int const cols, pixval const maxval, int const format) { switch (format) { case PPM_FORMAT: readPpmRow(fileP, pixelrow, cols, maxval, format); break; /* For PAM, we require a depth of 3, which means the raster format is identical to Raw PPM! How convenient. */ case PAM_FORMAT: case RPPM_FORMAT: readRppmRow(fileP, pixelrow, cols, maxval, format); break; case PGM_FORMAT: case RPGM_FORMAT: readPgmRow(fileP, pixelrow, cols, maxval, format); break; case PBM_FORMAT: case RPBM_FORMAT: readPbmRow(fileP, pixelrow, cols, maxval, format); break; default: pm_error("Invalid format code"); } } pixel** ppm_readppm(FILE * const fileP, int * const colsP, int * const rowsP, pixval * const maxvalP) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; pixel ** pixels; int cols, rows; pixval maxval; int format; ppm_readppminit(fileP, &cols, &rows, &maxval, &format); pixels = ppm_allocarray(cols, rows); if (setjmp(jmpbuf) != 0) { ppm_freearray(pixels, rows); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int row; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (row = 0; row < rows; ++row) ppm_readppmrow(fileP, pixels[row], cols, maxval, format); *colsP = cols; *rowsP = rows; *maxvalP = maxval; pm_setjmpbuf(origJmpbufP); } return pixels; } void ppm_check(FILE * const fileP, enum pm_check_type const checkType, int const format, int const cols, int const rows, pixval const maxval, enum pm_check_code * const retvalP) { 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 (checkType != PM_CHECK_BASIC) { if (retvalP) *retvalP = PM_CHECK_UNKNOWN_TYPE; } else if (PPM_FORMAT_TYPE(format) == PBM_TYPE) { pbm_check(fileP, checkType, format, cols, rows, retvalP); } else if (PPM_FORMAT_TYPE(format) == PGM_TYPE) { pgm_check(fileP, checkType, format, cols, rows, maxval, retvalP); } else if (format != RPPM_FORMAT) { if (retvalP) *retvalP = PM_CHECK_UNCHECKABLE; } else { pm_filepos const bytesPerRow = cols * 3 * (maxval > 255 ? 2 : 1); pm_filepos const needRasterSize = rows * bytesPerRow; pm_check(fileP, checkType, needRasterSize, retvalP); } } advanced/lib/libppm2.c0000644000175000001440000001252712347602012013747 0ustar ericusers/* 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" 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 ); } 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 { size_t const bytesWritten = rc; if (bytesWritten != bytesPerRow) pm_error("Error writing row. Short write of %u bytes " "instead of %u", (unsigned)bytesWritten, bytesPerRow); } free(rowBuffer); } static void ppm_writeppmrowplain(FILE * const fileP, const 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, const pixel * const pixelrow, int const cols, pixval const maxval, int const forceplain) { if (forceplain || pm_plain_output || maxval >= 1<<16) ppm_writeppmrowplain(fileP, pixelrow, cols, maxval); 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); } advanced/lib/bitio.h0000644000175000001440000000373112347602012013512 0ustar ericusers/*\ * $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 #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 advanced/lib/libpammap.c0000644000175000001440000005632012347602012014343 0ustar ericusers/*============================================================================= 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. This file was originally written by Bryan Henderson and is contributed to the public domain by him and subsequent authors. =============================================================================*/ #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.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. -----------------------------------------------------------------------------*/ unsigned int const hash_factor[] = {1, 33, 33*33}; unsigned int i; unsigned int hash; hash = 0; /* initial value */ for (i = 0; i < MIN(pamP->depth, 3); ++i) { hash += tuple[i] * hash_factor[i]; } 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 occurrence */ ++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 occurrence */ ++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, unsigned int const newDepth, 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; freqPam.depth = newDepth; assert(freqPam.depth <= pamP->depth); *tuplefreqhashP = pnm_createtuplehash(); *sizeP = 0; /* initial value */ *rowbufferP = pnm_allocpamrow(pamP); *colorP = pnm_allocpamtuple(pamP); full = FALSE; /* initial value */ /* Go through the entire raster, building a hash table of tuple values. */ for (row = 0; row < pamP->height && !full; ++row) { unsigned 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, unsigned int const newDepth, 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 have depth 'newDepth'. We look at only the first 'newDepth' planes of the input. Caler must ensure that the input has at least that many planes. The tuple values that index the hash are scaled to a new maxval of 'newMaxval'. E.g. if the input has maxval 100 and 'newMaxval' is 50, and a particular tuple has sample value 50, it would be counted 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) { if (color) pnm_freepamtuple(color); if (rowbuffer) pnm_freepamrow(rowbuffer); if (tuplefreqhash) pnm_destroytuplehash(tuplefreqhash); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { pm_setjmpbufsave(&jmpbuf, &origJmpbufP); computehashrecoverable(pamP, tupleArray, maxsize, newDepth, newMaxval, sizeP, &tuplefreqhash, &rowbuffer, &color); pm_setjmpbuf(origJmpbufP); } return tuplefreqhash; } 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->depth, pamP->maxval, sizeP); } static void alloctupletable(const struct pam * const pamP, unsigned int const size, tupletable * const tupletableP, const char ** const errorP) { if (UINT_MAX / sizeof(struct tupleint) < size) pm_asprintf(errorP, "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_asprintf(errorP, "size %u is too big for arithmetic", size); else { unsigned int const allocSize = mainTableSize + size * tupleIntSize; void * pool; pool = malloc(allocSize); if (!pool) pm_asprintf(errorP, "Unable to allocate %u bytes for a %u-entry " "tuple table", allocSize, size); else { tupletable const tbl = (tupletable) pool; unsigned int i; *errorP = NULL; for (i = 0; i < size; ++i) tbl[i] = (struct tupleint *) ((char*)pool + mainTableSize + i * tupleIntSize); *tupletableP = tbl; } } } } tupletable pnm_alloctupletable(const struct pam * const pamP, unsigned int const size) { tupletable retval; const char * error; alloctupletable(pamP, size, &retval, &error); if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } return retval; } void pnm_freetupletable(const struct pam * const pamP, tupletable const tupletable) { /* Note that the address 'tupletable' is, to the operating system, the address of a larger block of memory that contains not only tupletable, but all the samples to which it points (e.g. tupletable[0].tuple[0]) */ free(tupletable); } void pnm_freetupletable2(const 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; const char * error; alloctupletable(pamP, allocsize, &tupletable, &error); if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } else { 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; int 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_computetuplefreqtable3(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, unsigned int const newDepth, 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. The tuples in the table have depth 'newDepth'. We look at only the first 'newDepth' planes of the input. If the input doesn't have that many planes, we throw an error. Scale the tuple values to a new maxval of 'newMaxval' before processing them. E.g. if the input has maxval 100 and 'newMaxval' 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; if (newDepth > pamP->depth) pm_error("pnm_computetuplefreqtable3 called with 'newDepth' " "argument (%u) greater than input depth (%u)", newDepth, pamP->depth); tuplefreqhash = computetuplefreqhash(pamP, tupleArray, maxsize, newDepth, 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_computetuplefreqtable2(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, sample const newMaxval, unsigned int * const countP) { return pnm_computetuplefreqtable3(pamP, tupleArray, maxsize, pamP->depth, newMaxval, countP); } tupletable pnm_computetuplefreqtable(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, 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; } advanced/lib/libppmd.c0000644000175000001440000014470712347602012014037 0ustar ericusers/* ** ** 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_config.h" #include "pm_c_util.h" #include "mallocvar.h" #include "ppm.h" #include "ppmdfont.h" #include "ppmdraw.h" struct penpos { int x; int y; }; struct rectangle { /* ((0,0),(0,0)) means empty. */ /* 'lr' is guaranteed not to be left of or above 'ul' */ struct penpos ul; struct penpos lr; }; static struct rectangle const emptyRectangle = { {0, 0}, {0, 0}, }; static ppmd_point makePoint(int const x, int const y) { return ppmd_makePoint(x, y); } static ppmd_point middlePoint(ppmd_point const a, ppmd_point const b) { ppmd_point retval; retval.x = (a.x + b.x) / 2; retval.y = (a.y + b.y) / 2; return retval; } static bool pointsEqual(ppmd_point const a, ppmd_point const b) { return a.x == b.x && a.y == b.y; } static bool pointIsWithinBounds(ppmd_point const p, unsigned int const cols, unsigned int const rows) { return (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows); } static ppmd_point vectorSum(ppmd_point const a, ppmd_point const b) { return makePoint(a.x + b.x, a.y + b.y); } static long int const DDA_SCALE = 8192; #define PPMD_MAXCOORD 32767 /* Several factors govern the limit of x, y coordination values. The limit must be representable as (signed) int for coordinates to be carried in struct penpos (immediately above). The following calculation, done with long ints, must not overflow: cy0 = cy0 + (y1 - cy0) * (cols - 1 - cx0) / (x1 - cx0); The following must not overflow when DDA_SCALE is set to 8092: dy = (y1 - y0) * DDA_SCALE / abs(x1 - x0); Overflow conditions for ppmd_text are rather complicated, for commands come from an external PPMD font file. See comments below. */ void ppmd_validateCoord(int const c) { if (c < -PPMD_MAXCOORD || c > PPMD_MAXCOORD) pm_error("Coordinate out of bounds: %d", c); } void ppmd_validatePoint(ppmd_point const p) { if (p.x < -PPMD_MAXCOORD || p.x > PPMD_MAXCOORD) pm_error("x coordinate of (%d, %d) out of bounds", p.x, p.y); if (p.y < -PPMD_MAXCOORD || p.y > PPMD_MAXCOORD) pm_error("y coordinate of (%d, %d) out of bounds", p.x, p.y); } static void drawPoint(ppmd_drawprocp drawproc, const void * const clientdata, pixel ** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const p) { /*---------------------------------------------------------------------------- Draw a single point, assuming that it is within the bounds of the image. -----------------------------------------------------------------------------*/ if (drawproc == PPMD_NULLDRAWPROC) { const pixel * const pixelP = clientdata; assert(p.x >= 0); assert(p.x < cols); assert(p.y >= 0); assert(p.y < rows); pixels[p.y][p.x] = *pixelP; } else drawproc(pixels, cols, rows, maxval, p, clientdata); } struct drawProcXY { ppmd_drawproc * drawProc; const void * clientData; }; static struct drawProcXY makeDrawProcXY(ppmd_drawproc * const drawProc, const void * const clientData) { struct drawProcXY retval; retval.drawProc = drawProc; retval.clientData = clientData; return retval; } static ppmd_drawprocp drawProcPointXY; static void drawProcPointXY(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p, const void * const clientdata) { const struct drawProcXY * const xyP = clientdata; if (xyP->drawProc == PPMD_NULLDRAWPROC) drawPoint(PPMD_NULLDRAWPROC, xyP->clientData, pixels, cols, rows, maxval, p); else xyP->drawProc(pixels, cols, rows, maxval, p.x, p.y, xyP->clientData); } void ppmd_point_drawprocp(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p, const void * const clientdata) { if (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows) pixels[p.y][p.x] = *((pixel*)clientdata); } 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) { ppmd_point p; p.x = x; p.y = y; ppmd_point_drawprocp(pixels, cols, rows, maxval, p, clientdata); } static void findRectangleIntersection(struct rectangle const rect1, struct rectangle const rect2, struct rectangle * const intersectionP) { /*---------------------------------------------------------------------------- Find the intersection between rectangles 'rect1' and 'rect2'. Return it as *intersectionP. -----------------------------------------------------------------------------*/ struct penpos tentativeUl, tentativeLr; tentativeUl.x = MAX(rect1.ul.x, rect2.ul.x); tentativeUl.y = MAX(rect1.ul.y, rect2.ul.y); tentativeLr.x = MIN(rect1.lr.x, rect2.lr.x); tentativeLr.y = MIN(rect1.lr.y, rect2.lr.y); if (tentativeLr.x <= tentativeUl.x || tentativeLr.y <= tentativeUl.y) { /* No intersection */ *intersectionP = emptyRectangle; } else { intersectionP->ul = tentativeUl; intersectionP->lr = tentativeLr; } } 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) { struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata); struct rectangle image, request, intersection; unsigned int row; if (width < 0) pm_error("negative width %d passed to ppmd_filledrectangle", width); if (height < 0) pm_error("negative height %d passed to ppmd_filledrectangle", height); if (cols < 0) pm_error("negative image width %d passed to ppmd_filledrectangle", cols); if (rows < 0) pm_error("negative image height %d passed to ppmd_filledrectangle", rows); request.ul.x = x; request.ul.y = y; request.lr.x = x + width; request.lr.y = y + height; image.ul.x = 0; image.ul.y = 0; image.lr.x = cols; image.lr.y = rows; findRectangleIntersection(image, request, &intersection); /* Draw. */ for (row = intersection.ul.y; row < intersection.lr.y; ++row) { unsigned int col; for (col = intersection.ul.x; col < intersection.lr.x; ++col) drawPoint(drawProcPointXY, &xy, pixels, cols, rows, maxval, makePoint(col, row)); } } /* 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(ppmd_point const p0, ppmd_point const p1, int const cols, int const rows, ppmd_point * const c0P, bool * const noLineP) { /*---------------------------------------------------------------------------- Given a line that goes from p0 to p1, where any of these coordinates may be anywhere in space -- not just in the frame, clip the p0 end to bring it into the frame. Return the clipped-to location as *c0P. Iff this is not possible because the entire line described is outside the frame, return *nolineP == true. The frame is 'cols' columns starting at 0, by 'rows' rows starting at 0. -----------------------------------------------------------------------------*/ ppmd_point c0; bool noLine; c0 = p0; /* initial value */ noLine = FALSE; /* initial value */ /* Clip End 0 of the line horizontally */ if (c0.x < 0) { if (p1.x < 0) noLine = TRUE; else { c0.y = c0.y + (p1.y - c0.y) * (-c0.x) / (p1.x - c0.x); c0.x = 0; } } else if (c0.x >= cols) { if (p1.x >= cols) noLine = TRUE; else { c0.y = c0.y + (p1.y - c0.y) * (cols - 1 - c0.x) / (p1.x - c0.x); c0.x = cols - 1; } } /* Clip End 0 of the line vertically */ if (c0.y < 0) { if (p1.y < 0) noLine = TRUE; else { c0.x = c0.x + (p1.x - c0.x) * (-c0.y) / (p1.y - c0.y); c0.y = 0; } } else if (c0.y >= rows) { if (p1.y >= rows) noLine = TRUE; else { c0.x = c0.x + (p1.x - c0.x) * (rows - 1 - c0.y) / (p1.y - c0.y); c0.y = rows - 1; } } /* Clipping vertically may have moved the endpoint out of frame horizontally. If so, we know the other endpoint is also out of frame horizontally and the line misses the frame entirely. */ if (c0.x < 0 || c0.x >= cols) { assert(p1.x < 0 || p1.x >= cols); noLine = TRUE; } *c0P = c0; *noLineP = noLine; } static void clipEnd1(ppmd_point const p0, ppmd_point const p1, int const cols, int const rows, ppmd_point * const c1P) { /*---------------------------------------------------------------------------- Given a line that goes from p0 to p1, where p0 is within the frame, but p1 can be anywhere in space, clip the p1 end to bring it into the frame. Return the clipped-to location as *c1P. This is guaranteed to be possible, since we already know at least one point (i.e. p0) is in the frame. The frame is 'cols' columns starting at 0, by 'rows' rows starting at 0. -----------------------------------------------------------------------------*/ ppmd_point c1; /* The current clipped location of p1; we clip it multile times to get the final location. */ /* p0 is in the frame: */ assert(p0.x >= 0 && p0.x < cols); assert(p0.y >= 0 && p0.y < rows); /* Clip End 1 of the line horizontally */ c1 = p1; /* initial value */ if (c1.x < 0) { /* We know the line isn't vertical, since End 0 is in the frame and End 1 is left of frame. */ c1.y = c1.y + (p0.y - c1.y) * (-c1.x) / (p0.x - c1.x); c1.x = 0; } else if (c1.x >= cols) { /* We know the line isn't vertical, since End 0 is in the frame and End 1 is right of frame. */ c1.y = c1.y + (p0.y - c1.y) * (cols - 1 - c1.x) / (p0.x - c1.x); c1.x = cols - 1; } /* Clip End 1 of the line vertically */ if (c1.y < 0) { /* We know the line isn't horizontal, since End 0 is in the frame and End 1 is above frame. */ c1.x = c1.x + (p0.x - c1.x) * (-c1.y) / (p0.y - c1.y); c1.y = 0; } else if (c1.y >= rows) { /* We know the line isn't horizontal, since End 0 is in the frame and End 1 is below frame. */ c1.x = c1.x + (p0.x - c1.x) * (rows - 1 - c1.y) / (p0.y - c1.y); c1.y = rows - 1; } *c1P = c1; } static void clipLine(ppmd_point const p0, ppmd_point const p1, int const cols, int const rows, ppmd_point * const c0P, ppmd_point * const c1P, bool * const noLineP) { /*---------------------------------------------------------------------------- Clip the line that goes from p0 to p1 so that none of it is outside the boundaries of the raster with width 'cols' and height 'rows' The clipped line goes from *c0P to *c1P. But if the entire line is outside the boundaries (i.e. we clip the entire line), return *noLineP true and the other values undefined. -----------------------------------------------------------------------------*/ ppmd_point c0, c1; /* The line we successively modify. Starts out as the input line and ends up as the output line. */ bool noLine; clipEnd0(p0, p1, cols, rows, &c0, &noLine); if (!noLine) { /* p0 is in the frame: */ assert(c0.x >= 0 && c0.x < cols); assert(c0.y >= 0 && c0.y < rows); clipEnd1(c0, p1, cols, rows, &c1); } *c0P = c0; *c1P = c1; *noLineP = noLine; } static void drawShallowLine(ppmd_drawprocp drawProc, const void * const clientdata, pixel ** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const p0, ppmd_point const p1) { /*---------------------------------------------------------------------------- Draw a line that is more horizontal than vertical. 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 (p1.x > p0.x) dx = 1; else dx = -1; dy = (p1.y - p0.y) * DDA_SCALE / abs(p1.x - p0.x); prevrow = row = p0.y; srow = row * DDA_SCALE + DDA_SCALE / 2; col = p0.x; for ( ; ; ) { if (linetype == PPMD_LINETYPE_NODIAGS && row != prevrow) { drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, makePoint(col, prevrow)); prevrow = row; } drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, makePoint(col, row)); if (col == p1.x) break; srow += dy; row = srow / DDA_SCALE; col += dx; } } static void drawSteepLine(ppmd_drawprocp drawProc, const void * const clientdata, pixel ** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const p0, ppmd_point const p1) { /*---------------------------------------------------------------------------- Draw a line that is more vertical than horizontal. 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 (p1.y > p0.y) dy = 1; else dy = -1; dx = (p1.x - p0.x) * DDA_SCALE / abs(p1.y - p0.y); row = p0.y; prevcol = col = p0.x; scol = col * DDA_SCALE + DDA_SCALE / 2; for ( ; ; ) { if (linetype == PPMD_LINETYPE_NODIAGS && col != prevcol) { drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, makePoint(prevcol, row)); prevcol = col; } drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, makePoint(col, row)); if (row == p1.y) break; row += dy; scol += dx; col = scol / DDA_SCALE; } } void ppmd_linep(pixel ** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const p0, ppmd_point const p1, ppmd_drawprocp drawProc, const void * const clientdata) { ppmd_point c0, c1; bool noLine; /* There's no line left after clipping */ ppmd_validateCoord(cols); ppmd_validateCoord(rows); ppmd_validatePoint(p0); ppmd_validatePoint(p1); if (lineclip) { clipLine(p0, p1, cols, rows, &c0, &c1, &noLine); } else { c0 = p0; c1 = p1; noLine = FALSE; } if (noLine) { /* Nothing to draw */ } else if (pointsEqual(c0, c1)) { /* This line is just a point. Because there aren't two distinct endpoints, we have a special case. */ drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, c0); } else { /* Draw, using a simple DDA. */ if (abs(c1.x - c0.x) > abs(c1.y - c0.y)) drawShallowLine(drawProc, clientdata, pixels, cols, rows, maxval, c0, c1); else drawSteepLine(drawProc, clientdata, pixels, cols, rows, maxval, c0, c1); } } void ppmd_line(pixel ** const pixels, int const cols, int const rows, pixval const maxval, int const x0, int const y0, int const x1, int const y1, ppmd_drawproc drawProc, const void * const clientData) { struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData); ppmd_linep(pixels, cols, rows, maxval, makePoint(x0, y0), makePoint(x1, y1), drawProcPointXY, &xy); } static unsigned int distanceFromLine(ppmd_point const p, ppmd_point const l0, ppmd_point const l1) { /*---------------------------------------------------------------------------- Compute, sort of, the distance between point 'p' and the line through 'l0' and 'l1'. I don't really know the signficance of this measurement. -----------------------------------------------------------------------------*/ ppmd_point const middle = middlePoint(l0, l1); return (abs(p.x - middle.x) + abs(p.y - middle.y)); } void ppmd_spline3p(pixel ** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const p0, ppmd_point const ctl, ppmd_point const p1, ppmd_drawprocp drawProc, const void * const clientdata) { static unsigned int const splineThresh = 3; /* The limit of recursion */ if (distanceFromLine(ctl, p0, p1) <= splineThresh) { /* The control point is pretty close to the straight line that joins the endpoints, so we'll just draw a straight line. */ ppmd_linep( pixels, cols, rows, maxval, p0, p1, drawProc, clientdata); } else { /* We want some curvature, so pick a point (b) sort of between the two endpoints and the control point and then draw a spline between each of the endpoints and (b): */ ppmd_point const a = middlePoint(p0, ctl); ppmd_point const c = middlePoint(ctl, p1); ppmd_point const b = middlePoint(a, c); ppmd_spline3p( pixels, cols, rows, maxval, p0, a, b, drawProc, clientdata); ppmd_spline3p( pixels, cols, rows, maxval, b, c, p1, drawProc, clientdata); } } 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) { struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata); ppmd_spline3p(pixels, cols, rows, maxval, makePoint(x0, y0), makePoint(x1, y1), makePoint(x2, y2), drawProcPointXY, &xy); } void ppmd_polysplinep(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p0, unsigned int const nc, ppmd_point * const c, ppmd_point const p1, ppmd_drawprocp drawProc, const void * const clientdata) { ppmd_point p; unsigned int i; assert(nc > 0); p = p0; for (i = 0; i < nc - 1; ++i) { ppmd_point const n = middlePoint(c[i], c[i+1]); ppmd_spline3p( pixels, cols, rows, maxval, p, c[i], n, drawProc, clientdata); p = n; } ppmd_spline3p( pixels, cols, rows, maxval, p, c[nc - 1], p1, drawProc, clientdata); } 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) { ppmd_point const p1 = makePoint(x1, y1); ppmd_point const p0 = makePoint(x0, y0); struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata); ppmd_point p; unsigned int i; p = p0; /* initial value */ assert(nc > 0); for (i = 0; i < nc - 1; ++i) { ppmd_point const n = middlePoint(makePoint(xc[i], yc[i]), makePoint(xc[i+1], yc[i+1])); ppmd_spline3p( pixels, cols, rows, maxval, p, makePoint(xc[i], yc[i]), n, drawProcPointXY, &xy); p = n; } ppmd_spline3p( pixels, cols, rows, maxval, p, makePoint(xc[nc - 1], yc[nc - 1]), p1, drawProcPointXY, &xy); } void ppmd_spline4p(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const endPt0, ppmd_point const endPt1, ppmd_point const ctlPt0, ppmd_point const ctlPt1, ppmd_drawprocp drawproc, const void * const clientdata) { /*---------------------------------------------------------------------------- Draw a cubic spline from 'endPt0' to 'endPt1', using 'ctlPt0' and 'ctlPt1' as control points in the classic way: a line through 'endPt0' and 'ctlPt0' is tangent to the curve at 'entPt0' and the length of that line controls "enthusiasm," whatever that is. Same for 'endPt1' and 'ctlPt1'. -----------------------------------------------------------------------------*/ pm_error("ppmd_spline4p() has not been written yet!"); } void ppmd_circlep(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const center, unsigned int const radius, ppmd_drawprocp drawProc, const void * const clientData) { /*---------------------------------------------------------------------------- If lineclip mode is on, draw only points within the image. If lineclip is off, "draw" all points (by designated drawproc). Note that the drawproc can't actually draw a point outside the image, but it might maintain state that is affected by imaginary points outside the image. Initial point is 3 o'clock. -----------------------------------------------------------------------------*/ if (radius >= DDA_SCALE) pm_error("Error drawing circle. Radius %d is too large.", radius); ppmd_validateCoord(center.x + radius); ppmd_validateCoord(center.y + radius); ppmd_validateCoord(center.x - radius); ppmd_validateCoord(center.y - radius); if (radius > 0) { long const e = DDA_SCALE / radius; ppmd_point const p0 = makePoint(radius, 0); /* 3 o'clock */ /* The starting point around the circle, assuming (0, 0) center */ ppmd_point p; /* Current drawing position in the circle, assuming (0,0) center */ bool onFirstPoint; bool prevPointExists; ppmd_point prevPoint; /* Previous drawing position, assuming (0, 0) center*/ long sx, sy; /* 'p', scaled by DDA_SCALE */ p = p0; sx = p.x * DDA_SCALE + DDA_SCALE / 2; sy = p.y * DDA_SCALE + DDA_SCALE / 2; onFirstPoint = TRUE; prevPointExists = FALSE; while (onFirstPoint || !pointsEqual(p, p0)) { if (prevPointExists && pointsEqual(p, prevPoint)) { /* We're on the same point we were on last time (we moved less than a point's worth). Just keep moving. */ } else { ppmd_point const imagePoint = vectorSum(center,p); if (!lineclip || pointIsWithinBounds(imagePoint, cols, rows)) drawPoint(drawProc, clientData, pixels, cols, rows, maxval, imagePoint); prevPoint = p; prevPointExists = TRUE; } if (!pointsEqual(p, p0)) onFirstPoint = FALSE; sx += e * sy / DDA_SCALE; sy -= e * sx / DDA_SCALE; p = makePoint(sx / DDA_SCALE, sy / DDA_SCALE); } } } 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) { if (radius < 0) pm_error("Error drawing circle. Radius %d is negative.", radius); else { struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData); ppmd_circlep(pixels, cols, rows, maxval, makePoint(cx, cy), radius, drawProcPointXY, &xy); } } /* Arbitrary fill stuff. */ typedef struct { ppmd_point point; int edge; } coord; typedef struct fillState { int n; /* Number of elements in 'coords' */ int size; int curedge; int segstart; int ydir; int startydir; coord * coords; } fillState; typedef struct fillobj { /* The only reason we have a struct fillState separate from struct fillobj is that the drawproc interface is defined to have drawing not modify the fillobj, i.e. it passed const fillobj * to the drawing program. */ struct fillState * stateP; } fillobj; #define SOME 1000 static int oldclip; struct fillobj * ppmd_fill_create(void) { fillobj * fillObjP; struct fillState * stateP; MALLOCVAR(fillObjP); if (fillObjP == NULL) pm_error("out of memory allocating a fillhandle"); MALLOCVAR(stateP); if (stateP == NULL) pm_error("out of memory allocating a fillhandle"); stateP->n = 0; stateP->size = SOME; MALLOCARRAY(stateP->coords, stateP->size); if (stateP->coords == NULL) pm_error("out of memory allocating a fillhandle"); stateP->curedge = 0; fillObjP->stateP = stateP; /* Turn off line clipping. */ /* UGGH! We must eliminate this global variable */ oldclip = ppmd_setlineclip(0); return fillObjP; } char * ppmd_fill_init(void) { /*---------------------------------------------------------------------------- Backward compatibility interface. This is what was used before ppmd_fill_create() existed. Note that old programs treat a fill handle as a pointer to char rather than a pointer to fillObj, and backward compatibility depends upon the fact that these are implemented as identical types (an address). -----------------------------------------------------------------------------*/ return (char *)ppmd_fill_create(); } void ppmd_fill_destroy(struct fillobj * const fillObjP) { free(fillObjP->stateP->coords); free(fillObjP->stateP); free(fillObjP); } static void addCoord(struct fillState * const stateP, ppmd_point const point) { stateP->coords[stateP->n].point = point; stateP->coords[stateP->n].edge = stateP->curedge; ++stateP->n; } static void startNewSegment(struct fillState * const stateP) { /*---------------------------------------------------------------------------- Close off the segment we're currently building and start a new one. -----------------------------------------------------------------------------*/ if (stateP->startydir != 0 && stateP->ydir != 0) { /* There's stuff in the current segment. */ if (stateP->startydir == stateP->ydir) { /* Oops, first edge and last edge of current segment are the same. Change all points in the first edge to be in the last. */ int const firstEdge = stateP->coords[stateP->segstart].edge; int const lastEdge = stateP->coords[stateP->n - 1].edge; coord * const segStartCoordP = &stateP->coords[stateP->segstart]; coord * const segEndCoordP = &stateP->coords[stateP->n]; coord * fcP; for (fcP = segStartCoordP; fcP < segEndCoordP && fcP->edge == firstEdge; ++fcP) fcP->edge = lastEdge; } } /* And start new segment. */ ++stateP->curedge; stateP->segstart = stateP->n; stateP->ydir = 0; stateP->startydir = 0; } static void continueSegment(struct fillState * const stateP, int const dy) { /*---------------------------------------------------------------------------- 'dy' is how much the current point is above the previous one. -----------------------------------------------------------------------------*/ if (dy != 0) { if (stateP->ydir != 0 && stateP->ydir != dy) { /* Direction changed. Insert a fake coord, old position but new edge number. */ ++stateP->curedge; addCoord(stateP, stateP->coords[stateP->n - 1].point); } stateP->ydir = dy; if (stateP->startydir == 0) stateP->startydir = dy; } } /* ppmd_fill_drawprocp() is a drawproc that turns an outline drawing function into a filled shape function. This is a somewhat off-label application of a drawproc: A drawproc is intended just to draw a point. So e.g. you might draw a circle with a fat brush by calling ppmd_circle with a drawproc that draws a point as a 10-pixel disk. But ppmd_fill_drawproc() just draws a point the trivial way: as one pixel. However, it tracks every point that is drawn in a form that a subsequent ppmd_fill() call can use to to fill in the shape drawn, assuming it turns out to be a closed shape. */ void ppmd_fill_drawprocp(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p, const void * const clientdata) { const fillobj * const fillObjP = clientdata; struct fillState * const stateP = fillObjP->stateP; /* Make room for two more coords, the max we might add. */ if (stateP->n + 2 > stateP->size) { stateP->size += SOME; REALLOCARRAY(stateP->coords, stateP->size); if (stateP->coords == NULL) pm_error("out of memory enlarging a fillhandle"); } if (stateP->n == 0) { /* Start first segment. */ stateP->segstart = stateP->n; stateP->ydir = 0; stateP->startydir = 0; addCoord(stateP, p); } else { ppmd_point const prevPoint = stateP->coords[stateP->n - 1].point; int const dx = p.x - prevPoint.x; int const dy = p.y - prevPoint.y; if (dx == 0 && dy == 0) { /* These are the same coords we had last time; don't bother */ } else { if (abs(dx) > 1 || abs(dy) > 1) startNewSegment(stateP); else continueSegment(stateP, dy); addCoord(stateP, p); } } } void ppmd_fill_drawproc(pixel** const pixels, int const cols, int const rows, pixval const maxval, int const x, int const y, const void * const clientData) { ppmd_fill_drawprocp(pixels, cols, rows, maxval, makePoint(x, y), clientData); } #ifndef LITERAL_FN_DEF_MATCH static qsort_comparison_fn yxCompare; #endif static int yxCompare(const void * const c1Arg, const void * const c2Arg) { const coord * const c1P = c1Arg; const coord * const c2P = c2Arg; ppmd_point const p1 = c1P->point; ppmd_point const p2 = c2P->point; int retval; if (p1.y > p2.y) retval = 1; else if (p1.y < p2.y) retval = -1; else if (p1.x > p2.x) retval = 1; else if (p1.x < p2.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 fillObjP, ppmd_drawproc drawProc, const void * const clientdata) { struct fillState * const fh = fillObjP->stateP; 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; const coord * const fcpLast = & (fh->coords[fh->n - 1]); int lastedge, oldedge; lastedge = fh->coords[fh->n - 1].edge; fcp = &(fh->coords[fh->segstart]); oldedge = fcp->edge; for ( ; fcp<=fcpLast && 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), yxCompare); /* 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->point.x == lx && cp->point.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->point.x; py = cp->point.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->point.x; py = cp->point.y; edge = cp->edge; leftside = TRUE; } else { if (cp->point.y != py) { /* Row changed. Emit old span and start a new one. */ ppmd_filledrectangle( pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1, drawProc, clientdata); lx = rx = cp->point.x; py = cp->point.y; edge = cp->edge; leftside = TRUE; } else { if (cp->edge == edge) { /* Continuation of side. */ rx = cp->point.x; } else { /* Edge changed. Is it a span? */ if (leftside) { rx = cp->point.x; leftside = FALSE; } else { /* Got a span to fill. */ ppmd_filledrectangle( pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1, drawProc, clientdata); lx = rx = cp->point.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. */ static long isin(int deg) { /* 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. */ static long icos(int deg) { return isin(deg + 90); } static int twosCompByteValue(unsigned char const c) { /*---------------------------------------------------------------------------- E.g. if 'c' is 0x5, return 5. If 'c' is 0xF0, return -16. -----------------------------------------------------------------------------*/ return (char)c; } static int glyphSkipBefore(const struct ppmd_glyph * const glyphP) { return twosCompByteValue(glyphP->header.skipBefore); } static int glyphWidth(const struct ppmd_glyph * const glyphP) { return twosCompByteValue(glyphP->header.skipAfter) - twosCompByteValue(glyphP->header.skipBefore); } static ppmd_point commandPoint(const struct ppmd_glyphCommand * const commandP) { /*---------------------------------------------------------------------------- Return the point which is the argument of glyph drawing command *commandP. The origin of the coordinate system for this point is the center of the glyph cell and the scale is the scale of the font, so (-10, -10) means the upper left corner of the glyph cell. -----------------------------------------------------------------------------*/ return makePoint(twosCompByteValue(commandP->x), twosCompByteValue(commandP->y)); } #define Scalef 21 /* Font design size */ #define Descend 9 /* Descender offset */ static ppmd_point textPosFromFontPos(ppmd_point const fontPos, ppmd_point const textBoxOrigin, ppmd_point const center, ppmd_point const glyphOrigin, unsigned int const height, long const rotcos, long const rotsin) { /*---------------------------------------------------------------------------- 'fontPos' is a position within a glyph as told by the font definition. It is relative to the center of the glyph, in units of font pixels (1/21 of a glyph cell). We return the position on the canvas of that point. That takes into account where in the text box we are, where the text box is on the canvas, the size of the characters, and the rotation of the text box. -----------------------------------------------------------------------------*/ ppmd_point const ptl = vectorSum(center, fontPos); /* Position relative to the top left of the standard glyph cell */ ppmd_point const pl = vectorSum(glyphOrigin, ptl); /* Position relative to the top left of the whole text box, assuming the text box is horizontal and has font scale. */ ppmd_point const ps = makePoint((pl.x * (int)height) / Scalef, (pl.y * (int)height) / Scalef); /* Same as above, but with the text box its actual size */ ppmd_point const retval = makePoint(textBoxOrigin.x + (ps.x * rotcos - (ps.y-(int)height) * rotsin) / 65536, textBoxOrigin.y + (ps.x * rotsin + (ps.y-(int)height) * rotcos) / 65536); ppmd_validatePoint(retval); return retval; } static void drawGlyph(const struct ppmd_glyph * const glyphP, ppmd_point const glyphOrigin, pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, int const height, ppmd_point const textBoxOrigin, long const rotcos, long const rotsin, ppmd_drawprocp drawProc, const void * const clientdata, unsigned int * const cursorAdvanceP ) { /*---------------------------------------------------------------------------- 'glyphOrigin' is the position relative to the upper left corner of the text box of the upper left corner of this glyph cell. It is in units of font pixels (so you have to scale it by the font size to actual distance on the canvas). We return as *cursorAdvanceP the amount to the right of this glyph cell the next glyph cell on the line (if any) should be. The actual glyph cell may be a little to the left of the nominal position because of kerning. The font says how much to shift the cell left. 'textBoxOrigin' is the left end of the baseline of the top line in the text box, in the coordinate system of the canvas. 'rotcos' and 'rotsin' tell how that text box is rotated with respect to the horizontal on the canvas. 'height' is the height in canvas pixels of a glyph. This is a scale factor to convert font coordinates to canvas coordinates. -----------------------------------------------------------------------------*/ ppmd_point const center = makePoint(-glyphSkipBefore(glyphP), Scalef/2); /* This is what you have to add to the coordinates in a glyph command, which are relative to the center of the glyph, to get coordinates relative to the upper left corner of the glyph */ ppmd_point p; /* Current drawing position within the glyph. Origin is the top left of the glyph cell. Units are font pixels. */ unsigned int commandNum; p = textPosFromFontPos(makePoint(0, 0), textBoxOrigin, center, glyphOrigin, height, rotcos, rotsin); /* initial value */ 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: { ppmd_point const n = textPosFromFontPos(commandPoint(commandP), textBoxOrigin, center, glyphOrigin, height, rotcos, rotsin); ppmd_linep(pixels, cols, rows, maxval, p, n, drawProc, clientdata); p = n; } break; case CMD_MOVEPEN: p = textPosFromFontPos(commandPoint(commandP), textBoxOrigin, center, glyphOrigin, height, rotcos, rotsin); break; } } *cursorAdvanceP = glyphWidth(glyphP); } void ppmd_textp(pixel** const pixels, int const cols, int const rows, pixval const maxval, ppmd_point const pos, int const height, int const angle, const char * const sArg, ppmd_drawprocp drawProc, const void * const clientdata) { /*---------------------------------------------------------------------------- Draw the zero-terminated string 'sArg', with its baseline starting at point 'pos', inclined by 'angle' degrees to the X axis, with letters 'height' pixels high (descenders will extend below the baseline). We pass the supplied drawproc and clientdata to ppmd_linep, which performs the actual drawing. There may be multiple lines of text. The baseline of the topmost line starts at 'pos'. -----------------------------------------------------------------------------*/ const struct ppmd_font * const fontP = ppmd_get_font(); long rotsin, rotcos; ppmd_point p; const char * s; ppmd_validatePoint(pos); p = makePoint(0, 0); rotsin = isin(-angle); rotcos = icos(-angle); for (s = &sArg[0]; *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]; unsigned int cursorAdvance; ppmd_validatePoint(p); drawGlyph(glyphP, p, pixels, cols, rows, maxval, height, pos, rotcos, rotsin, drawProc, clientdata, &cursorAdvance); p.x += cursorAdvance; } else if (ch == '\n') { /* Move to the left edge of the next line down */ p.y += Scalef + Descend; p.x = 0; } } } void ppmd_text(pixel** const pixels, int const cols, int const rows, pixval const maxval, int const xpos, int const ypos, int const height, int const angle, const char * const sArg, ppmd_drawproc drawProc, const void * const clientData) { struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData); ppmd_textp(pixels, cols, rows, maxval, makePoint(xpos, ypos), height, angle, sArg, drawProcPointXY, &xy); } /* EXTENTS_DRAWPROC -- Drawproc which just accumulates the extents rectangle bounding the text. */ 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; } advanced/lib/standardppmdfont.c0000644000175000001440000022243112347602012015747 0ustar ericusers/* THIS FILE WAS GENERATED BY 'ppmdcfont' from a ppmdfont 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 }; advanced/lib/libpamd.c0000644000175000001440000013353112347602012014011 0ustar ericusers/* ** ** This library module contains the pamdraw routines. ** ** Copyright (C) 1989, 1991 by Jef Poskanzer. ** ** Modified from ppm to pam by Willem van Schaik, Feb 2011 ** ** 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_config.h" #include "pm_c_util.h" #include "mallocvar.h" #include "pam.h" #include "ppmdfont.h" #include "pamdraw.h" struct penpos { int x; int y; }; struct rectangle { /* ((0,0),(0,0)) means empty. */ /* 'lr' is guaranteed not to be left of or above 'ul' */ struct penpos ul; struct penpos lr; }; static struct rectangle const emptyRectangle = { {0, 0}, {0, 0}, }; static pamd_point makePoint(int const x, int const y) { return pamd_makePoint(x, y); } static pamd_point middlePoint(pamd_point const a, pamd_point const b) { pamd_point retval; retval.x = (a.x + b.x) / 2; retval.y = (a.y + b.y) / 2; return retval; } static bool pointsEqual(pamd_point const a, pamd_point const b) { return a.x == b.x && a.y == b.y; } static bool pointIsWithinBounds(pamd_point const p, unsigned int const cols, unsigned int const rows) { return (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows); } static pamd_point vectorSum(pamd_point const a, pamd_point const b) { return makePoint(a.x + b.x, a.y + b.y); } static long int const DDA_SCALE = 8192; #define PAMD_MAXCOORD 32767 /* Several factors govern the limit of x, y coordination values. The limit must be representable as (signed) int for coordinates to be carried in struct penpos (immediately above). The following calculation, done with long ints, must not overflow: cy0 = cy0 + (y1 - cy0) * (cols - 1 - cx0) / (x1 - cx0); The following must not overflow when DDA_SCALE is set to 8092: dy = (y1 - y0) * DDA_SCALE / abs(x1 - x0); Overflow conditions for pamd_text are rather complicated, for commands come from an external PPMD font file. See comments below. */ void pamd_validateCoord(int const c) { if (c < -PAMD_MAXCOORD || c > PAMD_MAXCOORD) pm_error("Coordinate out of bounds: %d", c); } void pamd_validatePoint(pamd_point const p) { if (p.x < -PAMD_MAXCOORD || p.x > PAMD_MAXCOORD) pm_error("x coordinate of (%d, %d) out of bounds", p.x, p.y); if (p.y < -PAMD_MAXCOORD || p.y > PAMD_MAXCOORD) pm_error("y coordinate of (%d, %d) out of bounds", p.x, p.y); } static void drawPoint(pamd_drawproc drawproc, const void * const clientdata, tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const p) { /*---------------------------------------------------------------------------- Draw a single point, assuming that it is within the bounds of the image. -----------------------------------------------------------------------------*/ int i; if (drawproc == PAMD_NULLDRAWPROC) { assert(p.x >= 0); assert(p.x < cols); assert(p.y >= 0); assert(p.y < rows); for (i = 0; i < depth; i++) tuples[p.y][p.x][i] = (sample) *((tuple *) clientdata + i); } else { drawproc(tuples, cols, rows, depth, maxval, p, clientdata); } } void pamd_point_drawproc(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const p, const void * const clientdata) { unsigned int i; if ((p.x >= 0) && (p.x < cols) && (p.y >= 0) && (p.y < rows)) for (i = 0; i < depth; ++i) tuples[p.y][p.x][i] = (sample) *((tuple *) clientdata + i); } static void findRectangleIntersection(struct rectangle const rect1, struct rectangle const rect2, struct rectangle * const intersectionP) { /*---------------------------------------------------------------------------- Find the intersection between rectangles 'rect1' and 'rect2'. Return it as *intersectionP. -----------------------------------------------------------------------------*/ struct penpos tentativeUl, tentativeLr; tentativeUl.x = MAX(rect1.ul.x, rect2.ul.x); tentativeUl.y = MAX(rect1.ul.y, rect2.ul.y); tentativeLr.x = MIN(rect1.lr.x, rect2.lr.x); tentativeLr.y = MIN(rect1.lr.y, rect2.lr.y); if (tentativeLr.x <= tentativeUl.x || tentativeLr.y <= tentativeUl.y) { /* No intersection */ *intersectionP = emptyRectangle; } else { intersectionP->ul = tentativeUl; intersectionP->lr = tentativeLr; } } void pamd_filledrectangle(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, int const left, int const top, int const width, int const height, pamd_drawproc drawProc, const void * const clientdata) { struct rectangle image, request, intersection; unsigned int row; if (width < 0) pm_error("negative width %d passed to pamd_filledrectanglep", width); if (height < 0) pm_error("negative height %d passed to pamd_filledrectanglep", height); if (cols < 0) pm_error("negative image width %d passed to pamd_filledrectanglep", cols); if (rows < 0) pm_error("negative image height %d passed to pamd_filledrectanglep", rows); request.ul.x = left; request.ul.y = top; request.lr.x = left + width; request.lr.y = top + height; image.ul.x = 0; image.ul.y = 0; image.lr.x = cols; image.lr.y = rows; findRectangleIntersection(image, request, &intersection); /* Draw. */ for (row = intersection.ul.y; row < intersection.lr.y; ++row) { unsigned int col; for (col = intersection.ul.x; col < intersection.lr.x; ++col) drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval, makePoint(col, row)); } } /* Outline drawing stuff. */ static int linetype = PAMD_LINETYPE_NORMAL; int pamd_setlinetype(int const type) { int old; old = linetype; linetype = type; return old; } static bool lineclip = TRUE; int pamd_setlineclip(int const newSetting) { bool previousSetting; previousSetting = lineclip; lineclip = newSetting; return previousSetting; } static void clipEnd0(pamd_point const p0, pamd_point const p1, int const cols, int const rows, pamd_point * const c0P, bool * const noLineP) { /*---------------------------------------------------------------------------- Given a line that goes from p0 to p1, where any of these coordinates may be anywhere in space -- not just in the frame, clip the p0 end to bring it into the frame. Return the clipped-to location as *c0P. Iff this is not possible because the entire line described is outside the frame, return *nolineP == true. The frame is 'cols' columns starting at 0, by 'rows' rows starting at 0. -----------------------------------------------------------------------------*/ pamd_point c0; bool noLine; c0 = p0; /* initial value */ noLine = FALSE; /* initial value */ /* Clip End 0 of the line horizontally */ if (c0.x < 0) { if (p1.x < 0) noLine = TRUE; else { c0.y = c0.y + (p1.y - c0.y) * (-c0.x) / (p1.x - c0.x); c0.x = 0; } } else if (c0.x >= cols) { if (p1.x >= cols) noLine = TRUE; else { c0.y = c0.y + (p1.y - c0.y) * (cols - 1 - c0.x) / (p1.x - c0.x); c0.x = cols - 1; } } /* Clip End 0 of the line vertically */ if (c0.y < 0) { if (p1.y < 0) noLine = TRUE; else { c0.x = c0.x + (p1.x - c0.x) * (-c0.y) / (p1.y - c0.y); c0.y = 0; } } else if (c0.y >= rows) { if (p1.y >= rows) noLine = TRUE; else { c0.x = c0.x + (p1.x - c0.x) * (rows - 1 - c0.y) / (p1.y - c0.y); c0.y = rows - 1; } } /* Clipping vertically may have moved the endpoint out of frame horizontally. If so, we know the other endpoint is also out of frame horizontally and the line misses the frame entirely. */ if (c0.x < 0 || c0.x >= cols) { assert(p1.x < 0 || p1.x >= cols); noLine = TRUE; } *c0P = c0; *noLineP = noLine; } static void clipEnd1(pamd_point const p0, pamd_point const p1, int const cols, int const rows, pamd_point * const c1P) { /*---------------------------------------------------------------------------- Given a line that goes from p0 to p1, where p0 is within the frame, but p1 can be anywhere in space, clip the p1 end to bring it into the frame. Return the clipped-to location as *c1P. This is guaranteed to be possible, since we already know at least one point (i.e. p0) is in the frame. The frame is 'cols' columns starting at 0, by 'rows' rows starting at 0. -----------------------------------------------------------------------------*/ pamd_point c1; /* The current clipped location of p1; we clip it multile times to get the final location. */ /* p0 is in the frame: */ assert(p0.x >= 0 && p0.x < cols); assert(p0.y >= 0 && p0.y < rows); /* Clip End 1 of the line horizontally */ c1 = p1; /* initial value */ if (c1.x < 0) { /* We know the line isn't vertical, since End 0 is in the frame and End 1 is left of frame. */ c1.y = c1.y + (p0.y - c1.y) * (-c1.x) / (p0.x - c1.x); c1.x = 0; } else if (c1.x >= cols) { /* We know the line isn't vertical, since End 0 is in the frame and End 1 is right of frame. */ c1.y = c1.y + (p0.y - c1.y) * (cols - 1 - c1.x) / (p0.x - c1.x); c1.x = cols - 1; } /* Clip End 1 of the line vertically */ if (c1.y < 0) { /* We know the line isn't horizontal, since End 0 is in the frame and End 1 is above frame. */ c1.x = c1.x + (p0.x - c1.x) * (-c1.y) / (p0.y - c1.y); c1.y = 0; } else if (c1.y >= rows) { /* We know the line isn't horizontal, since End 0 is in the frame and End 1 is below frame. */ c1.x = c1.x + (p0.x - c1.x) * (rows - 1 - c1.y) / (p0.y - c1.y); c1.y = rows - 1; } *c1P = c1; } static void clipLine(pamd_point const p0, pamd_point const p1, int const cols, int const rows, pamd_point * const c0P, pamd_point * const c1P, bool * const noLineP) { /*---------------------------------------------------------------------------- Clip the line that goes from p0 to p1 so that none of it is outside the boundaries of the raster with width 'cols' and height 'rows' The clipped line goes from *c0P to *c1P. But if the entire line is outside the boundaries (i.e. we clip the entire line), return *noLineP true and the other values undefined. -----------------------------------------------------------------------------*/ pamd_point c0, c1; /* The line we successively modify. Starts out as the input line and ends up as the output line. */ bool noLine; clipEnd0(p0, p1, cols, rows, &c0, &noLine); if (!noLine) { /* p0 is in the frame: */ assert(c0.x >= 0 && c0.x < cols); assert(c0.y >= 0 && c0.y < rows); clipEnd1(c0, p1, cols, rows, &c1); } *c0P = c0; *c1P = c1; *noLineP = noLine; } static void drawShallowLine(pamd_drawproc drawProc, const void * const clientdata, tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const p0, pamd_point const p1) { /*---------------------------------------------------------------------------- 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 (p1.x > p0.x) dx = 1; else dx = -1; dy = (p1.y - p0.y) * DDA_SCALE / abs(p1.x - p0.x); prevrow = row = p0.y; srow = row * DDA_SCALE + DDA_SCALE / 2; col = p0.x; for ( ; ; ) { if (linetype == PAMD_LINETYPE_NODIAGS && row != prevrow) { drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval, makePoint(col, prevrow)); prevrow = row; } drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval, makePoint(col, row)); if (col == p1.x) break; srow += dy; row = srow / DDA_SCALE; col += dx; } } static void drawSteepLine(pamd_drawproc drawProc, const void * const clientdata, tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const p0, pamd_point const p1) { /*---------------------------------------------------------------------------- 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 (p1.y > p0.y) dy = 1; else dy = -1; dx = (p1.x - p0.x) * DDA_SCALE / abs(p1.y - p0.y); row = p0.y; prevcol = col = p0.x; scol = col * DDA_SCALE + DDA_SCALE / 2; for ( ; ; ) { if (linetype == PAMD_LINETYPE_NODIAGS && col != prevcol) { drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval, makePoint(prevcol, row)); prevcol = col; } drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval, makePoint(col, row)); if (row == p1.y) break; row += dy; scol += dx; col = scol / DDA_SCALE; } } void pamd_line(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const p0, pamd_point const p1, pamd_drawproc drawProc, const void * const clientdata) { pamd_point c0, c1; bool noLine; /* There's no line left after clipping */ pamd_validateCoord(cols); pamd_validateCoord(rows); pamd_validatePoint(p0); pamd_validatePoint(p1); if (lineclip) { clipLine(p0, p1, cols, rows, &c0, &c1, &noLine); } else { c0 = p0; c1 = p1; noLine = FALSE; } if (noLine) { /* Nothing to draw */ } else if (pointsEqual(c0, c1)) { /* This line is just a point. Because there aren't two distinct endpoints, we have a special case. */ drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval, c0); } else { /* Draw, using a simple DDA. */ if (abs(c1.x - c0.x) > abs(c1.y - c0.y)) drawShallowLine(drawProc, clientdata, tuples, cols, rows, depth, maxval, c0, c1); else drawSteepLine(drawProc, clientdata, tuples, cols, rows, depth, maxval, c0, c1); } } static unsigned int distanceFromLine(pamd_point const p, pamd_point const l0, pamd_point const l1) { /*---------------------------------------------------------------------------- Compute, sort of, the distance between point 'p' and the line through 'l0' and 'l1'. I don't really know the signficance of this measurement. -----------------------------------------------------------------------------*/ pamd_point const middle = middlePoint(l0, l1); return (abs(p.x - middle.x) + abs(p.y - middle.y)); } void pamd_spline3(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const p0, pamd_point const ctl, pamd_point const p1, pamd_drawproc drawProc, const void * const clientdata) { static unsigned int const splineThresh = 3; /* The limit of recursion */ if (distanceFromLine(ctl, p0, p1) <= splineThresh) { /* The control point is pretty close to the straight line that joins the endpoints, so we'll just draw a straight line. */ pamd_line( tuples, cols, rows, depth, maxval, p0, p1, drawProc, clientdata); } else { /* We want some curvature, so pick a point (b) sort of between the two endpoints and the control point and then draw a spline between each of the endpoints and (b): */ pamd_point const a = middlePoint(p0, ctl); pamd_point const c = middlePoint(ctl, p1); pamd_point const b = middlePoint(a, c); pamd_spline3( tuples, cols, rows, depth, maxval, p0, a, b, drawProc, clientdata); pamd_spline3( tuples, cols, rows, depth, maxval, b, c, p1, drawProc, clientdata); } } void pamd_polyspline(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const p0, unsigned int const nc, pamd_point * const c, pamd_point const p1, pamd_drawproc drawProc, const void * const clientdata) { pamd_point p; unsigned int i; assert(nc > 0); p = p0; for (i = 0; i < nc - 1; ++i) { pamd_point const n = middlePoint(c[i], c[i+1]); pamd_spline3( tuples, cols, rows, depth, maxval, p, c[i], n, drawProc, clientdata); p = n; } pamd_spline3( tuples, cols, rows, depth, maxval, p, c[nc - 1], p1, drawProc, clientdata); } void pamd_spline4(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const endPt0, pamd_point const endPt1, pamd_point const ctlPt0, pamd_point const ctlPt1, pamd_drawproc drawproc, const void * const clientdata) { /*---------------------------------------------------------------------------- Draw a cubic spline from 'endPt0' to 'endPt1', using 'ctlPt0' and 'ctlPt1' as control points in the classic way: a line through 'endPt0' and 'ctlPt0' is tangent to the curve at 'entPt0' and the length of that line controls "enthusiasm," whatever that is. Same for 'endPt1' and 'ctlPt1'. -----------------------------------------------------------------------------*/ pm_error("pamd_spline4() has not been written yet!"); } void pamd_circle(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const center, unsigned int const radius, pamd_drawproc drawProc, const void * const clientData) { /*---------------------------------------------------------------------------- If lineclip mode is on, draw only points within the image. If lineclip is off, "draw" all points (by designated drawproc). Note that the drawproc can't actually draw a point outside the image, but it might maintain state that is affected by imaginary points outside the image. Initial point is 3 o'clock. -----------------------------------------------------------------------------*/ if (radius >= DDA_SCALE) pm_error("Error drawing circle. Radius %d is too large.", radius); pamd_validateCoord(center.x + radius); pamd_validateCoord(center.y + radius); pamd_validateCoord(center.x - radius); pamd_validateCoord(center.y - radius); if (radius > 0) { long const e = DDA_SCALE / radius; pamd_point const p0 = makePoint(radius, 0); /* 3 o'clock */ /* The starting point around the circle, assuming (0, 0) center */ pamd_point p; /* Current drawing position in the circle, assuming (0,0) center */ bool onFirstPoint; bool prevPointExists; pamd_point prevPoint; /* Previous drawing position, assuming (0, 0) center*/ long sx, sy; /* 'p', scaled by DDA_SCALE */ p = p0; sx = p.x * DDA_SCALE + DDA_SCALE / 2; sy = p.y * DDA_SCALE + DDA_SCALE / 2; onFirstPoint = TRUE; prevPointExists = FALSE; while (onFirstPoint || !pointsEqual(p, p0)) { if (prevPointExists && pointsEqual(p, prevPoint)) { /* We're on the same point we were on last time (we moved less than a point's worth). Just keep moving. */ } else { pamd_point const imagePoint = vectorSum(center,p); if (!lineclip || pointIsWithinBounds(imagePoint, cols, rows)) drawPoint(drawProc, clientData, tuples, cols, rows, depth, maxval, imagePoint); prevPoint = p; prevPointExists = TRUE; } if (!pointsEqual(p, p0)) onFirstPoint = FALSE; sx += e * sy / DDA_SCALE; sy -= e * sx / DDA_SCALE; p = makePoint(sx / DDA_SCALE, sy / DDA_SCALE); } } } /* Arbitrary fill stuff. */ typedef struct { pamd_point point; int edge; } coord; typedef struct fillState { int n; /* Number of elements in 'coords' */ int size; int curedge; int segstart; int ydir; int startydir; coord * coords; } fillState; typedef struct fillobj { /* The only reason we have a struct fillState separate from struct fillobj is that the drawproc interface is defined to have drawing not modify the fillobj, i.e. it passed const fillobj * to the drawing program. */ struct fillState * stateP; } fillobj; #define SOME 1000 static int oldclip; struct fillobj * pamd_fill_create(void) { fillobj * fillObjP; struct fillState * stateP; MALLOCVAR(fillObjP); if (fillObjP == NULL) pm_error("out of memory allocating a fillhandle"); MALLOCVAR(stateP); if (stateP == NULL) pm_error("out of memory allocating a fillhandle"); stateP->n = 0; stateP->size = SOME; MALLOCARRAY(stateP->coords, stateP->size); if (stateP->coords == NULL) pm_error("out of memory allocating a fillhandle"); stateP->curedge = 0; fillObjP->stateP = stateP; /* Turn off line clipping. */ /* UGGH! We must eliminate this global variable */ oldclip = pamd_setlineclip(0); return fillObjP; } void pamd_fill_destroy(struct fillobj * const fillObjP) { free(fillObjP->stateP->coords); free(fillObjP->stateP); free(fillObjP); } static void addCoord(struct fillState * const stateP, pamd_point const point) { stateP->coords[stateP->n].point = point; stateP->coords[stateP->n].edge = stateP->curedge; ++stateP->n; } static void startNewSegment(struct fillState * const stateP) { /*---------------------------------------------------------------------------- Close off the segment we're currently building and start a new one. -----------------------------------------------------------------------------*/ if (stateP->startydir != 0 && stateP->ydir != 0) { /* There's stuff in the current segment. */ if (stateP->startydir == stateP->ydir) { /* Oops, first edge and last edge of current segment are the same. Change all points in the first edge to be in the last. */ int const firstEdge = stateP->coords[stateP->segstart].edge; int const lastEdge = stateP->coords[stateP->n - 1].edge; coord * const segStartCoordP = &stateP->coords[stateP->segstart]; coord * const segEndCoordP = &stateP->coords[stateP->n]; coord * fcP; for (fcP = segStartCoordP; fcP < segEndCoordP && fcP->edge == firstEdge; ++fcP) fcP->edge = lastEdge; } } /* And start new segment. */ ++stateP->curedge; stateP->segstart = stateP->n; stateP->ydir = 0; stateP->startydir = 0; } static void continueSegment(struct fillState * const stateP, int const dy) { /*---------------------------------------------------------------------------- 'dy' is how much the current point is above the previous one. -----------------------------------------------------------------------------*/ if (dy != 0) { if (stateP->ydir != 0 && stateP->ydir != dy) { /* Direction changed. Insert a fake coord, old position but new edge number. */ ++stateP->curedge; addCoord(stateP, stateP->coords[stateP->n - 1].point); } stateP->ydir = dy; if (stateP->startydir == 0) stateP->startydir = dy; } } /* pamd_fill_drawproc() is a drawproc that turns an outline drawing function into a filled shape function. This is a somewhat off-label application of a drawproc: A drawproc is intended just to draw a point. So e.g. you might draw a circle with a fat brush by calling pamd_circle with a drawproc that draws a point as a 10-tuple disk. But pamd_fill_drawproc() just draws a point the trivial way: as one tuple. However, it tracks every point that is drawn in a form that a subsequent pamd_fill() call can use to to fill in the shape drawn, assuming it turns out to be a closed shape. */ void pamd_fill_drawproc(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const p, const void * const clientdata) { const fillobj * const fillObjP = clientdata; struct fillState * const stateP = fillObjP->stateP; /* Make room for two more coords, the max we might add. */ if (stateP->n + 2 > stateP->size) { stateP->size += SOME; REALLOCARRAY(stateP->coords, stateP->size); if (stateP->coords == NULL) pm_error("out of memory enlarging a fillhandle"); } if (stateP->n == 0) { /* Start first segment. */ stateP->segstart = stateP->n; stateP->ydir = 0; stateP->startydir = 0; addCoord(stateP, p); } else { pamd_point const prevPoint = stateP->coords[stateP->n - 1].point; int const dx = p.x - prevPoint.x; int const dy = p.y - prevPoint.y; if (dx == 0 && dy == 0) { /* These are the same coords we had last time; don't bother */ } else { if (abs(dx) > 1 || abs(dy) > 1) startNewSegment(stateP); else continueSegment(stateP, dy); addCoord(stateP, p); } } } #ifndef LITERAL_FN_DEF_MATCH static qsort_comparison_fn yxCompare; #endif static int yxCompare(const void * const c1Arg, const void * const c2Arg) { const coord * const c1P = c1Arg; const coord * const c2P = c2Arg; pamd_point const p1 = c1P->point; pamd_point const p2 = c2P->point; int retval; if (p1.y > p2.y) retval = 1; else if (p1.y < p2.y) retval = -1; else if (p1.x > p2.x) retval = 1; else if (p1.x < p2.x) retval = -1; else retval = 0; return retval; } void pamd_fill(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, struct fillobj * const fillObjP, pamd_drawproc drawProc, const void * const clientdata) { struct fillState * const fh = fillObjP->stateP; 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; const coord * const fcpLast = & (fh->coords[fh->n - 1]); int lastedge, oldedge; lastedge = fh->coords[fh->n - 1].edge; fcp = &(fh->coords[fh->segstart]); oldedge = fcp->edge; for ( ; fcp<=fcpLast && fcp->edge == oldedge; ++fcp ) fcp->edge = lastedge; } } /* Restore clipping now. */ pamd_setlineclip(oldclip); /* Sort the coords by Y, secondarily by X. */ qsort((char*) fh->coords, fh->n, sizeof(coord), yxCompare); /* 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->point.x == lx && cp->point.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->point.x; py = cp->point.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->point.x; py = cp->point.y; edge = cp->edge; leftside = TRUE; } else { if (cp->point.y != py) { /* Row changed. Emit old span and start a new one. */ pamd_filledrectangle( tuples, cols, rows, depth, maxval, lx, py, rx - lx + 1, 1, drawProc, clientdata); lx = rx = cp->point.x; py = cp->point.y; edge = cp->edge; leftside = TRUE; } else { if (cp->edge == edge) { /* Continuation of side. */ rx = cp->point.x; } else { /* Edge changed. Is it a span? */ if (leftside) { rx = cp->point.x; leftside = FALSE; } else { /* Got a span to fill. */ pamd_filledrectangle( tuples, cols, rows, depth, maxval, lx, py, rx - lx + 1, 1, drawProc, clientdata); lx = rx = cp->point.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 */ static long isin(int const argDeg) { /*---------------------------------------------------------------------------- Return sine of an angle in integral degrees. The value returned is 65536 times the sine. -----------------------------------------------------------------------------*/ int deg360; /* The argument reduced to the range 0-360 degrees */ if (argDeg < 0) { deg360 = (360 - ((- argDeg) % 360)) % 360; } else if (argDeg >= 360) { deg360 = argDeg % 360; } else deg360 = argDeg; /* Now look up from table according to quadrant. */ if (deg360 <= 90) { return sintab[deg360]; } else if (deg360 <= 180) { return sintab[180 - deg360]; } else if (deg360 <= 270) { return -sintab[deg360 - 180]; } return -sintab[360 - deg360]; } static long icos(int const deg) { /*---------------------------------------------------------------------------- Return cosine of an angle in integral degrees. The value returned is 65536 times the cosine -----------------------------------------------------------------------------*/ return isin(deg + 90); } static int twosCompByteValue(unsigned char const c) { /*---------------------------------------------------------------------------- E.g. if 'c' is 0x5, return 5. If 'c' is 0xF0, return -16. -----------------------------------------------------------------------------*/ return (char)c; } static int glyphSkipBefore(const struct ppmd_glyph * const glyphP) { return twosCompByteValue(glyphP->header.skipBefore); } static int glyphWidth(const struct ppmd_glyph * const glyphP) { return twosCompByteValue(glyphP->header.skipAfter) - twosCompByteValue(glyphP->header.skipBefore); } static pamd_point commandPoint(const struct ppmd_glyphCommand * const commandP) { /*---------------------------------------------------------------------------- Return the point which is the argument of glyph drawing command *commandP. The origin of the coordinate system for this point is the center of the glyph cell and the scale is the scale of the font, so (-10, -10) means the upper left corner of the glyph cell. -----------------------------------------------------------------------------*/ return makePoint(twosCompByteValue(commandP->x), twosCompByteValue(commandP->y)); } #define Scalef 21 /* Font design size */ #define Descend 9 /* Descender offset */ static pamd_point textPosFromFontPos(pamd_point const fontPos, pamd_point const textBoxOrigin, pamd_point const center, pamd_point const glyphOrigin, unsigned int const height, long const rotcos, long const rotsin) { /*---------------------------------------------------------------------------- 'fontPos' is a position within a glyph as told by the font definition. It is relative to the center of the glyph, in units of font tuples (1/21 of a glyph cell). We return the position on the canvas of that point. That takes into account where in the text box we are, where the text box is on the canvas, the size of the characters, and the rotation of the text box. -----------------------------------------------------------------------------*/ pamd_point const ptl = vectorSum(center, fontPos); /* Position relative to the top left of the standard glyph cell */ pamd_point const pl = vectorSum(glyphOrigin, ptl); /* Position relative to the top left of the whole text box, assuming the text box is horizontal and has font scale. */ pamd_point const ps = makePoint((pl.x * (int)height) / Scalef, (pl.y * (int)height) / Scalef); /* Same as above, but with the text box its actual size */ pamd_point const retval = makePoint(textBoxOrigin.x + (ps.x * rotcos - (ps.y-(int)height) * rotsin) / 65536, textBoxOrigin.y + (ps.x * rotsin + (ps.y-(int)height) * rotcos) / 65536); pamd_validatePoint(retval); return retval; } static void drawGlyph(const struct ppmd_glyph * const glyphP, pamd_point const glyphOrigin, tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, int const height, pamd_point const textBoxOrigin, long const rotcos, long const rotsin, pamd_drawproc drawProc, const void * const clientdata, unsigned int * const cursorAdvanceP ) { /*---------------------------------------------------------------------------- 'glyphOrigin' is the position relative to the upper left corner of the text box of the upper left corner of this glyph cell. It is in units of font tuples (so you have to scale it by the font size to actual distance on the canvas). We return as *cursorAdvanceP the amount to the right of this glyph cell the next glyph cell on the line (if any) should be. The actual glyph cell may be a little to the left of the nominal position because of kerning. The font says how much to shift the cell left. 'textBoxOrigin' is the left end of the baseline of the top line in the text box, in the coordinate system of the canvas. 'rotcos' and 'rotsin' tell how that text box is rotated with respect to the horizontal on the canvas. 'height' is the height in canvas tuples of a glyph. This is a scale factor to convert font coordinates to canvas coordinates. -----------------------------------------------------------------------------*/ pamd_point const center = makePoint(-glyphSkipBefore(glyphP), Scalef/2); /* This is what you have to add to the coordinates in a glyph command, which are relative to the center of the glyph, to get coordinates relative to the upper left corner of the glyph */ pamd_point p; /* Current drawing position within the glyph. Origin is the top left of the glyph cell. Units are font tuples. */ unsigned int commandNum; p = textPosFromFontPos(makePoint(0, 0), textBoxOrigin, center, glyphOrigin, height, rotcos, rotsin); /* initial value */ 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: { pamd_point const n = textPosFromFontPos(commandPoint(commandP), textBoxOrigin, center, glyphOrigin, height, rotcos, rotsin); pamd_line(tuples, cols, rows, depth, maxval, p, n, drawProc, clientdata); p = n; } break; case CMD_MOVEPEN: p = textPosFromFontPos(commandPoint(commandP), textBoxOrigin, center, glyphOrigin, height, rotcos, rotsin); break; } } *cursorAdvanceP = glyphWidth(glyphP); } void pamd_text(tuple** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const pos, int const height, int const angle, const char * const sArg, pamd_drawproc drawProc, const void * const clientdata) { /*---------------------------------------------------------------------------- Draw the zero-terminated string 'sArg', with its baseline starting at point 'pos', inclined by 'angle' degrees to the X axis, with letters 'height' tuples high (descenders will extend below the baseline). We pass the supplied drawproc and clientdata to pamd_linep, which performs the actual drawing. There may be multiple lines of text. The baseline of the topmost line starts at 'pos'. -----------------------------------------------------------------------------*/ const struct ppmd_font * const fontP = ppmd_get_font(); long rotsin, rotcos; pamd_point p; const char * s; pamd_validatePoint(pos); p = makePoint(0, 0); rotsin = isin(-angle); rotcos = icos(-angle); for (s = &sArg[0]; *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]; unsigned int cursorAdvance; pamd_validatePoint(p); drawGlyph(glyphP, p, tuples, cols, rows, depth, maxval, height, pos, rotcos, rotsin, drawProc, clientdata, &cursorAdvance); p.x += cursorAdvance; } else if (ch == '\n') { /* Move to the left edge of the next line down */ p.y += Scalef + Descend; p.x = 0; } } } #ifndef LITERAL_FN_DEF_MATCH static pamd_drawproc extetnsDrawproc; #endif static void extentsDrawproc(tuple** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const p, const void * const clientdata) { /*---------------------------------------------------------------------------- Drawproc which just accumulates the extents rectangle bounding the text. -----------------------------------------------------------------------------*/ extleft = MIN(extleft, p.x); exttop = MIN(exttop, p.y); extright = MAX(extright, p.x); extbottom = MAX(extbottom, p.y); } void pamd_text_box(int const height, int const angle, const char * const s, int * const leftP, int * const topP, int * const rightP, int * const bottomP) { /*---------------------------------------------------------------------------- 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. -----------------------------------------------------------------------------*/ extleft = 32767; exttop = 32767; extright = -32767; extbottom = -32767; pamd_text(NULL, 32767, 32767, 255, 255, makePoint(1000, 1000), height, angle, s, extentsDrawproc, NULL); *leftP = extleft - 1000; *topP = exttop - 1000; *rightP = extright - 1000; *bottomP = extbottom - 1000; } advanced/lib/libpam.c0000644000175000001440000013117612347602012013650 0ustar ericusers/*============================================================================= libpam.c =============================================================================== These are the library functions, which belong in the libnetpbm library, that deal with the PAM (Portable Arbitrary Format) image format. This file was originally written by Bryan Henderson and is contributed to the public domain by him and subsequent authors. =============================================================================*/ /* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 #define _LARGE_FILES #define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ #define _XOPEN_SOURCE 600 /* 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. -----------------------------------------------------------------------------*/ if (pamP->width == 0) pm_error("Width is zero. Image must be at least one pixel wide"); else if (pamP->height == 0) pm_error("Height is zero. Image must be at least one pixel high"); else { unsigned int const depth = allocationDepth(pamP); if (depth > INT_MAX/sizeof(sample)) pm_error("image depth (%u) too large to be processed", depth); else if (depth * sizeof(sample) > INT_MAX/pamP->width) pm_error("image width and depth (%u, %u) too large " "to be processed.", pamP->width, depth); else if (pamP->width * (depth * sizeof(sample)) > INT_MAX - depth * sizeof(tuple *)) pm_error("image width and depth (%u, %u) too large " "to be processed.", pamP->width, depth); if (depth > INT_MAX - 2) pm_error("image depth (%u) too large to be processed", depth); if (pamP->width > INT_MAX - 2) pm_error("image width (%u) too large to be processed", pamP->width); if (pamP->height > INT_MAX - 2) pm_error("image height (%u) too large to be processed", pamP->height); } } 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. -----------------------------------------------------------------------------*/ unsigned int i; *blackTupleP = pnm_allocpamtuple(pamP); for (i = 0; i < pamP->depth; ++i) (*blackTupleP)[i] = 0; } 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 pointers to the tuples, immediately followed by the tuples themselves. */ unsigned 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 " "%d tuples by %d samples per tuple by %u bytes per sample.", pamP->width, allocationDepth(pamP), (unsigned)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, (unsigned)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 parseHeaderLine(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 parseHeaderUint(const char * const valueString, unsigned int * const valueNumP, const char * const name) { /*---------------------------------------------------------------------------- Interpret 'valueString' as the number in a header such as "WIDTH 200". 'name' is the header name ("WIDTH" in the example). -----------------------------------------------------------------------------*/ if (strlen(valueString) == 0) pm_error("Missing value for %s in PAM file header.", name); else { char * endptr; long int valueNum; errno = 0; /* Clear errno so we can detect strtol() failure */ valueNum = strtol(valueString, &endptr, 10); if (errno != 0) pm_error("Too-large value for %s in " "PAM file header: '%s'", name, valueString); else if (*endptr != '\0') pm_error("Non-numeric value for %s in " "PAM file header: '%s'", name, valueString); else if (valueNum < 0) pm_error("Negative value for %s in " "PAM file header: '%s'", name, valueString); else if ((unsigned int)valueNum != valueNum) pm_error("Ridiculously large value for %s in " "PAM file header: %lu", name, valueNum); else *valueNumP = (unsigned int)valueNum; } } static void parseHeaderInt(const char * const valueString, int * const valueNumP, const char * const name) { /*---------------------------------------------------------------------------- This is not what it seems. It is the same thing as parseHeaderUint, except that the type of the value it returns is "int" instead of "unsigned int". But that doesn't mean the value can be negative. We throw an error is it is not positive. -----------------------------------------------------------------------------*/ unsigned int valueNum; parseHeaderUint(valueString, &valueNum, name); if ((int)valueNum != valueNum) pm_error("Ridiculously large value for %s in " "PAM file header: %u", name, valueNum); else *valueNumP = (int)valueNum; } static void processHeaderLine(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]; parseHeaderLine(buffer, label, value); if (streq(label, "ENDHDR")) headerSeenP->endhdr = TRUE; else if (streq(label, "WIDTH")) { parseHeaderInt(value, &pamP->width, label); headerSeenP->width = TRUE; } else if (streq(label, "HEIGHT")) { parseHeaderInt(value, &pamP->height, label); headerSeenP->height = TRUE; } else if (streq(label, "DEPTH")) { parseHeaderUint(value, &pamP->depth, label); headerSeenP->depth = TRUE; } else if (streq(label, "MAXVAL")) { unsigned int maxval; parseHeaderUint(value, &maxval, label); if (maxval >= (1<<16)) pm_error("Maxval too large: %u. Max is 65535", maxval); pamP->maxval = maxval; headerSeenP->maxval = TRUE; } else if (streq(label, "TUPLTYPE")) { if (strlen(value) == 0) pm_error("TUPLTYPE header does not have any tuple type text"); else { size_t const oldLen = strlen(pamP->tuple_type); if (oldLen + strlen(value) + 1 > sizeof(pamP->tuple_type)-1) pm_error("TUPLTYPE value too long in PAM header"); if (oldLen == 0) strcpy(pamP->tuple_type, value); else { strcat(pamP->tuple_type, " "); strcat(pamP->tuple_type, value); } pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0'; } } else pm_error("Unrecognized header line type: '%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", (unsigned)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 pm_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 (pm_stripeq(buffer, "")); /* Ignore it; it's a blank line */ else processHeaderLine(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 = sizeof(struct pam); pam.file = fileP; pam.len = PAM_STRUCT_SIZE(tuple_type); 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) { /*---------------------------------------------------------------------------- Return the number of bytes per sample in the PAM raster of a PAM image with maxval 'maxval'. It's defined to be the minimum number of bytes needed for that maxval, i.e. 1 for maxval < 256, 2 otherwise. -----------------------------------------------------------------------------*/ /* The PAM format requires maxval to be greater than zero and less than 1<<16, but since that is a largely arbitrary restriction, we don't want to rely on it. */ unsigned int i; sample a; for (i = 0, a = maxval; i <= sizeof(maxval); ++i) { if (a == 0) return i; a >>= 8; } return 0; /* silence compiler warning */ } static void validateMinDepth(const struct pam * const pamP, unsigned int const minDepth) { if (pamP->depth < minDepth) pm_error("Depth %u is insufficient for tuple type '%s'. " "Minimum depth is %u", pamP->depth, pamP->tuple_type, minDepth); } static void interpretTupleType(struct pam * const pamP) { /*---------------------------------------------------------------------------- Fill in redundant convenience fields in *pamP with information implied by the pamP->tuple_type implies: visual colorDepth haveOpacity opacityPlane Validate the tuple type against the depth and maxval as well. -----------------------------------------------------------------------------*/ const char * const tupleType = pamP->len >= PAM_STRUCT_SIZE(tuple_type) ? pamP->tuple_type : ""; bool visual; unsigned int colorDepth; bool haveOpacity; unsigned int opacityPlane; assert(pamP->depth > 0); switch (PAM_FORMAT_TYPE(pamP->format)) { case PAM_TYPE: { if (streq(tupleType, "BLACKANDWHITE")) { visual = true; colorDepth = 1; haveOpacity = false; if (pamP->maxval != 1) pm_error("maxval %u is not consistent with tuple type " "BLACKANDWHITE (should be 1)", (unsigned)pamP->maxval); } else if (streq(tupleType, "GRAYSCALE")) { visual = true; colorDepth = 1; haveOpacity = false; } else if (streq(tupleType, "GRAYSCALE_ALPHA")) { visual = true; colorDepth = 1; haveOpacity = true; opacityPlane = PAM_GRAY_TRN_PLANE; validateMinDepth(pamP, 2); } else if (streq(tupleType, "RGB")) { visual = true; colorDepth = 3; haveOpacity = false; validateMinDepth(pamP, 3); } else if (streq(tupleType, "RGB_ALPHA")) { visual = true; colorDepth = 3; haveOpacity = true; opacityPlane = PAM_TRN_PLANE; validateMinDepth(pamP, 4); } else { visual = false; } } break; case PPM_TYPE: visual = true; colorDepth = 3; haveOpacity = false; assert(pamP->depth == 3); break; case PGM_TYPE: visual = true; colorDepth = 1; haveOpacity = false; break; case PBM_TYPE: visual = true; colorDepth = 1; haveOpacity = false; break; default: assert(false); } if (pamP->size >= PAM_STRUCT_SIZE(visual)) pamP->visual = visual; if (pamP->size >= PAM_STRUCT_SIZE(color_depth)) pamP->color_depth = colorDepth; if (pamP->size >= PAM_STRUCT_SIZE(have_opacity)) pamP->have_opacity = haveOpacity; if (pamP->size >= PAM_STRUCT_SIZE(opacity_plane)) pamP->opacity_plane = opacityPlane; } 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 " "enough to hold at least up to the " "'tuple_type' member, but according " "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 0x%x - not a PAM, PPM, PGM, or PBM file", pamP->format); } pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval); pamP->plainformat = FALSE; /* See below for complex explanation of why this is FALSE. */ interpretTupleType(pamP); 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 " "(%u bytes, according to its 'size' element) " "than the amount of data in it " "(%u bytes, according to its 'len' element).", pamP->size, pamP->len); if (pamP->size < PAM_STRUCT_SIZE(bytes_per_sample)) pm_error("pam object passed to pnm_writepaminit() is too small. " "It must be large " "enough to hold at least up through the " "'bytes_per_sample' member, but according " "to its 'size' member, it is only %u bytes long.", pamP->size); if (pamP->len < PAM_STRUCT_SIZE(maxval)) pm_error("pam object must contain members at least through 'maxval', " "but according to the 'len' member, it is only %u bytes " "long.", pamP->len); if (pamP->maxval > PAM_OVERALL_MAXVAL) pm_error("maxval (%lu) passed to pnm_writepaminit() " "is greater than %u", pamP->maxval, PAM_OVERALL_MAXVAL); if (pamP->len < PAM_STRUCT_SIZE(tuple_type)) { tupleType = ""; if (pamP->size >= PAM_STRUCT_SIZE(tuple_type)) pamP->tuple_type[0] = '\0'; } else tupleType = pamP->tuple_type; pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval); if (pamP->size >= PAM_STRUCT_SIZE(comment_p) && pamP->len < PAM_STRUCT_SIZE(comment_p)) pamP->comment_p = NULL; if (pamP->size >= PAM_STRUCT_SIZE(allocation_depth) && pamP->len < PAM_STRUCT_SIZE(allocation_depth)) pamP->allocation_depth = 0; interpretTupleType(pamP); pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(opacity_plane)); switch (PAM_FORMAT_TYPE(pamP->format)) { case PAM_TYPE: /* See explanation below of why we ignore 'pm_plain_output' here. */ 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 (!pm_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); } } /* EFFECT OF -plain WHEN WRITING PAM FORMAT: Before Netpbm 10.63 (June 2013), pnm_writepaminit() did a pm_error() here if 'pm_plain_output' was set (i.e. the user said -plain). But this isn't really logical, because -plain is a global option for the program and here we are just writing one image. As a global option, -plain must be defined to have effect where it makes sense and have no effect where it doesn't. Note that a program that generates GIF just ignores -plain. Note also that a program could conceivably generate both a PPM image and a PAM image. Note also how we handle the other a user can request plain format: the 'plainformat' member of the PAM struct. In the case of PAM, we ignore that member. */ 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_makerowrgba(const struct pam * const pamP, tuple * const tuplerow) { /*---------------------------------------------------------------------------- Make the tuples 'tuplerow' the RGBA equivalent of what they are now, which is described by *pamP. This means afterward, *pamP no longer correctly describes these tuples; Caller must be sure to update *pamP it or not use it anymore. We fail if Caller did not supply enough allocated space in 'tuplerow' for the extra planes (tuple allocation depth). -----------------------------------------------------------------------------*/ if (pamP->len < PAM_STRUCT_SIZE(opacity_plane)) { pm_message("struct pam length %u is too small for pnm_makerowrgba(). " "This function requires struct pam fields through " "'opacity_plane'", pamP->len); abort(); } else { if (!pamP->visual) pm_error("Non-visual tuples given to pnm_addopacityrow()"); if (pamP->color_depth >= 3 && pamP->have_opacity) { /* It's already in RGBA format. Leave it alone. */ } else { unsigned int col; if (allocationDepth(pamP) < 4) pm_error("allocation depth %u passed to pnm_makerowrgba(). " "Must be at least 4.", allocationDepth(pamP)); for (col = 0; col < pamP->width; ++col) { tuple const thisTuple = tuplerow[col]; thisTuple[PAM_TRN_PLANE] = pamP->have_opacity ? thisTuple[pamP->opacity_plane] : pamP->maxval; assert(PAM_RED_PLANE == 0); thisTuple[PAM_BLU_PLANE] = thisTuple[0]; thisTuple[PAM_GRN_PLANE] = thisTuple[0]; } } } } void pnm_addopacityrow(const struct pam * const pamP, tuple * const tuplerow) { /*---------------------------------------------------------------------------- Add an opacity plane to the tuples in 'tuplerow', if one isn't already there. This means afterward, *pamP no longer correctly describes these tuples; Caller must be sure to update *pamP it or not use it anymore. We fail if Caller did not supply enough allocated space in 'tuplerow' for the extra plane (tuple allocation depth). -----------------------------------------------------------------------------*/ if (pamP->len < PAM_STRUCT_SIZE(opacity_plane)) { pm_message("struct pam length %u is too small for pnm_makerowrgba(). " "This function requires struct pam fields through " "'opacity_plane'", pamP->len); abort(); } else { if (!pamP->visual) pm_error("Non-visual tuples given to pnm_addopacityrow()"); if (pamP->have_opacity) { /* It already has opacity. Leave it alone. */ } else { unsigned int const opacityPlane = pamP->color_depth; unsigned int col; if (allocationDepth(pamP) < opacityPlane + 1) pm_error("allocation depth %u passed to pnm_addopacityrow(). " "Must be at least %u.", allocationDepth(pamP), opacityPlane + 1); for (col = 0; col < pamP->width; ++col) tuplerow[col][opacityPlane] = pamP->maxval; } } } void pnm_getopacity(const struct pam * const pamP, int * const haveOpacityP, unsigned int * const opacityPlaneP) { /* Usage note: this is obsolete since we added 'have_opacity', etc. to struct pam. */ if (streq(pamP->tuple_type, "RGB_ALPHA")) { *haveOpacityP = TRUE; *opacityPlaneP = PAM_TRN_PLANE; } else if (streq(pamP->tuple_type, "GRAYSCALE_ALPHA")) { *haveOpacityP = TRUE; *opacityPlaneP = PAM_GRAY_TRN_PLANE; } else *haveOpacityP = FALSE; } tuple pnm_backgroundtuple(struct pam * const pamP, tuple ** const tuples) { /*-------------------------------------------------------------------- This function was copied from libpnm3.c's pnm_backgroundxel() and modified to use tuples instead of xels. ----------------------------------------------------------------------*/ tuple tuplePtr, bgtuple, ul, ur, ll, lr; /* Guess a good background value. */ ul = tuples[0][0]; ur = tuples[0][pamP->width-1]; ll = tuples[pamP->height-1][0]; lr = tuples[pamP->height-1][pamP->width-1]; bgtuple = NULL; /* We first recognize three corners equal. If not, we look for any two. If not, we just average all four. */ if (pnm_tupleequal(pamP, ul, ur) && pnm_tupleequal(pamP, ur, ll)) tuplePtr = ul; else if (pnm_tupleequal(pamP, ul, ur) && pnm_tupleequal(pamP, ur, lr)) tuplePtr = ul; else if (pnm_tupleequal(pamP, ul, ll) && pnm_tupleequal(pamP, ll, lr)) tuplePtr = ul; else if (pnm_tupleequal(pamP, ur, ll) && pnm_tupleequal(pamP, ll, lr)) tuplePtr = ur; else if (pnm_tupleequal(pamP, ul, ur)) tuplePtr = ul; else if (pnm_tupleequal(pamP, ul, ll)) tuplePtr = ul; else if (pnm_tupleequal(pamP, ul, lr)) tuplePtr = ul; else if (pnm_tupleequal(pamP, ur, ll)) tuplePtr = ur; else if (pnm_tupleequal(pamP, ur, lr)) tuplePtr = ur; else if (pnm_tupleequal(pamP, ll, lr)) tuplePtr = ll; else { /* Reimplement libpnm3.c's mean4() but for tuples. */ unsigned int plane; bgtuple = pnm_allocpamtuple(pamP); for (plane = 0; plane < pamP->depth; ++plane) bgtuple[plane] = (ul[plane] + ur[plane] + ll[plane] + lr[plane]) / 4; } if (!bgtuple) { unsigned int plane; bgtuple = pnm_allocpamtuple(pamP); for (plane = 0; plane < pamP->depth; ++plane) bgtuple[plane] = tuplePtr[plane]; } return bgtuple; } /*============================================================================= 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 the data. */ 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 * const inpamP = outputTuplesP->pamP; *outputTuplesP->tuplesP = pnm_readpam(fdopen(pipeToSuckFd, "r"), inpamP, PAM_STRUCT_SIZE(tuple_type)); pm_close(inpamP->file); } advanced/lib/libpamwrite.c0000644000175000001440000003113412347602012014714 0ustar ericusers/*============================================================================ 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). This file was originally written by Bryan Henderson and is contributed to the public domain by him and subsequent authors. =============================================================================*/ /* See pmfileio.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 "pm_config.h" #include "pm_c_util.h" #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. -----------------------------------------------------------------------------*/ jmp_buf jmpbuf; jmp_buf * origJmpbufP; unsigned int rowImageSize; unsigned char * outbuf; /* malloc'ed */ outbuf = pnm_allocrowimage(pamP); pnm_formatpamrow(pamP, tuplerow, outbuf, &rowImageSize); if (setjmp(jmpbuf) != 0) { pnm_freerowimage(outbuf); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int i; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (i = 0; i < count; ++i) { size_t bytesWritten; bytesWritten = fwrite(outbuf, 1, rowImageSize, pamP->file); if (bytesWritten != rowImageSize) pm_error("fwrite() failed to write an image row to the file. " "errno=%d (%s)", errno, strerror(errno)); } pm_setjmpbuf(origJmpbufP); } pnm_freerowimage(outbuf); } 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 (pamP->format == PAM_FORMAT || !(pm_plain_output || pamP->plainformat)) writePamRawRow(pamP, tuplerow, 1); else { 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: assert(false); break; default: pm_error("Invalid 'format' value %u in pam structure", pamP->format); } } } 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]); } advanced/lib/libpbm.h0000644000175000001440000000035712347602012013652 0ustar ericusers/* 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 advanced/lib/bitio.c0000644000175000001440000001031612347602012013502 0ustar ericusers/*\ * $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; } advanced/lib/libpgm2.c0000644000175000001440000001143012347602012013726 0ustar ericusers/* 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" 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 ); } static void putus(unsigned short const n, FILE * const fileP) { if (n >= 10) putus(n / 10, fileP); putc('0' + n % 10, fileP); } static void format1bpsRow(const gray * const grayrow, unsigned int const cols, unsigned char * const rowBuffer) { /* single byte samples. */ unsigned int col; unsigned int bufferCursor; bufferCursor = 0; for (col = 0; col < cols; ++col) rowBuffer[bufferCursor++] = grayrow[col]; } static void format2bpsRow(const gray * const grayrow, unsigned int const cols, unsigned char * const rowBuffer) { /* two byte samples. */ unsigned int col; unsigned int bufferCursor; bufferCursor = 0; for (col = 0; col < cols; ++col) { gray const val = grayrow[col]; rowBuffer[bufferCursor++] = val >> 8; rowBuffer[bufferCursor++] = (unsigned char) val; } } static void writepgmrowraw(FILE * const fileP, const gray * const grayrow, unsigned int const cols, gray const maxval) { unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; unsigned int const bytesPerRow = cols * bytesPerSample; unsigned char * rowBuffer; ssize_t rc; MALLOCARRAY(rowBuffer, bytesPerRow); if (rowBuffer == NULL) pm_error("Unable to allocate memory for row buffer " "for %u columns", cols); if (maxval < 256) format1bpsRow(grayrow, cols, rowBuffer); else format2bpsRow(grayrow, cols, rowBuffer); rc = fwrite(rowBuffer, 1, bytesPerRow, fileP); if (rc < 0) pm_error("Error writing row. fwrite() errno=%d (%s)", errno, strerror(errno)); else { size_t const bytesWritten = rc; if (bytesWritten != bytesPerRow) pm_error("Error writing row. Short write of %u bytes " "instead of %u", (unsigned)bytesWritten, bytesPerRow); } free(rowBuffer); } static void writepgmrowplain(FILE * const fileP, const gray * const grayrow, unsigned int const cols, gray const maxval) { int col, charcount; charcount = 0; for (col = 0; col < cols; ++col) { if (charcount >= 65) { putc('\n', fileP); charcount = 0; } else if (charcount > 0) { putc(' ', fileP); ++charcount; } #ifdef DEBUG if (grayrow[col] > maxval) pm_error("value out of bounds (%u > %u)", grayrow[col], maxval); #endif /*DEBUG*/ putus((unsigned short)grayrow[col], fileP); charcount += 3; } if (charcount > 0) putc('\n', fileP); } void pgm_writepgmrow(FILE * const fileP, const gray * const grayrow, int const cols, gray const maxval, int const forceplain) { if (forceplain || pm_plain_output || maxval >= 1<<16) writepgmrowplain(fileP, grayrow, cols, maxval); else writepgmrowraw(fileP, grayrow, cols, maxval); } void pgm_writepgm(FILE * const fileP, gray ** const grays, int const cols, int const rows, gray const maxval, int const forceplain) { unsigned int row; pgm_writepgminit(fileP, cols, rows, maxval, forceplain); for (row = 0; row < rows; ++row) pgm_writepgmrow(fileP, grays[row], cols, maxval, forceplain); } advanced/lib/Makefile0000644000175000001440000002150212347602012013667 0ustar ericusersifeq ($(SRCDIR)x,x) SRCDIR = $(CURDIR)/.. BUILDDIR = $(SRCDIR) endif SUBDIR = lib VPATH=.:$(SRCDIR)/$(SUBDIR) DLLTOOL=dlltool include $(BUILDDIR)/config.mk 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 pmfileio.o fileio.o bitio.o colorname.o \ libpamd.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) \ # Library objects to be linked but not built by common.mk: LIBOBJECTS_X = \ util/filename.o \ util/io.o \ util/mallocvar.o \ util/matrix.o \ util/nsleep.o \ util/nstring.o \ util/shhopt.o \ util/token.o \ util/vasprintf.o \ MANUALS3 = libnetpbm MANUALS5 = pbm pgm ppm pnm pam INTERFACE_HEADERS = bitio.h colorname.h \ pam.h pamdraw.h pammap.h pbm.h pbmfont.h \ pgm.h pm.h pm_gamma.h pm_system.h pnm.h \ ppm.h ppmcmap.h ppmdfont.h ppmdraw.h ppmfloyd.h \ util/mallocvar.h util/shhopt.h \ DATAFILES = rgb.txt .PHONY: all all: libnetpbm extra_staticlib SUBDIRS = util SCRIPTS = BINARIES = OMIT_LIBRARY_RULE = 1 include $(SRCDIR)/common.mk # The following must go after common.mk 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): CFLAGS_TARGET=$(CFLAGS_SHLIB) libpbm3.o: CFLAGS_TARGET+=$(CFLAGS_SSE) $(LIBOBJECTS): %.o: %.c importinc $(CC) -c $(INCLUDES) $(CFLAGS_ALL) -o $@ $< MAJ = 11 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 <$< >$@ # 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. .PHONY: $(INTERFACE_HEADERS:%=%_installhdr) $(INTERFACE_HEADERS:%=%_installhdr): $(PKGDIR)/include/netpbm $(INSTALL) -c -m $(INSTALL_PERM_HDR) \ $(SRCDIR)/lib/$(@:%_installhdr=%) $(PKGDIR)/include/netpbm/ .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 distclean clean: localclean .PHONY: localclean localclean: rm -f compile.h FORCE: advanced/lib/pbmfont.h0000644000175000001440000000521412347602012014047 0ustar ericusers/* pbmfont.h - header file for font routines in libpbm */ #include "pbm.h" #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. */ unsigned int width; unsigned int 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. Can be negative. */ unsigned 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. Can be negative. */ struct glyph * glyph[256]; /* glyph[i] is the glyph for code point i */ const bit ** oldfont; /* for compatibility with old pbmtext routines */ /* oldfont is NULL if the font is BDF derived */ int fcols, frows; }; struct font* pbm_defaultfont(const char* const which); struct font* pbm_dissectfont(const bit ** const font, unsigned int const frows, unsigned int const fcols); struct font* pbm_loadfont(const char * const filename); struct font* pbm_loadpbmfont(const char * const filename); struct font* pbm_loadbdffont(const char * const filename); void pbm_dumpfont(struct font * const fnP); #ifdef __cplusplus } #endif advanced/lib/pammap.h0000644000175000001440000001002212347602012013646 0ustar ericusers/*============================================================================= pammap.h =============================================================================== Interface header file for hash table and lookup table pam functions in libpnm. =============================================================================*/ #ifndef PAMMAP_H #define PAMMAP_H #include #include #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(const struct pam * const pamP, tupletable const tupletable); void pnm_freetupletable2(const 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); tupletable pnm_computetuplefreqtable3(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, unsigned int const newDepth, sample const newMaxval, unsigned int * const countP); tuplehash pnm_computetuplefreqhash(struct pam * const pamP, tuple ** const tupleArray, 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 advanced/lib/libpbmfont.c0000644000175000001440000017235712347602012014546 0ustar ericusers/* 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 #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "pbmfont.h" #include "pbm.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((const bit **)defaultfont, DEFAULTFONT_ROWS, DEFAULTFONT_COLS); } return retval; } static void findFirstBlankRow(const bit ** const font, unsigned int const fcols, unsigned int const frows, unsigned int * const browP) { unsigned int row; bool foundBlank; for (row = 0, foundBlank = false; row < frows / 6 && !foundBlank; ++row) { unsigned int col; bit col0Value = font[row][0]; bool rowIsBlank; rowIsBlank = true; /* initial assumption */ for (col = 1; col < fcols; ++col) if (font[row][col] != col0Value) rowIsBlank = false; if (rowIsBlank) { foundBlank = true; *browP = row; } } if (!foundBlank) pm_error("couldn't find blank pixel row in font"); } static void findFirstBlankCol(const bit ** const font, unsigned int const fcols, unsigned int const frows, unsigned int * const bcolP) { unsigned int col; bool foundBlank; for (col = 0, foundBlank = false; col < fcols / 6 && !foundBlank; ++col) { unsigned int row; bit row0Value = font[0][col]; bool colIsBlank; colIsBlank = true; /* initial assumption */ for (row = 1; row < frows; ++row) if (font[row][col] != row0Value) colIsBlank = false; if (colIsBlank) { foundBlank = true; *bcolP = col; } } if (!foundBlank) pm_error("couldn't find blank pixel column in font"); } static void computeCharacterSize(const bit ** const font, unsigned int const fcols, unsigned int const frows, unsigned int * const cellWidthP, unsigned int * const cellHeightP, unsigned int * const charWidthP, unsigned int * const charHeightP) { unsigned int firstBlankRow; unsigned int firstBlankCol; unsigned int heightLast11Rows; findFirstBlankRow(font, fcols, frows, &firstBlankRow); findFirstBlankCol(font, fcols, frows, &firstBlankCol); heightLast11Rows = frows - firstBlankRow; if (heightLast11Rows % 11 != 0) pm_error("The rows of characters in the font do not appear to " "be all the same height. The last 11 rows are %u pixel " "rows high (from pixel row %u up to %u), " "which is not a multiple of 11.", heightLast11Rows, firstBlankRow, frows); else { unsigned int widthLast15Cols; *cellHeightP = heightLast11Rows / 11; widthLast15Cols = fcols - firstBlankCol; if (widthLast15Cols % 15 != 0) pm_error("The columns of characters in the font do not appear to " "be all the same width. " "The last 15 columns are %u pixel " "columns wide (from pixel col %u up to %u), " "which is not a multiple of 15.", widthLast15Cols, firstBlankCol, fcols); else { *cellWidthP = widthLast15Cols / 15; *charWidthP = firstBlankCol; *charHeightP = firstBlankRow; } } } struct font* pbm_dissectfont(const bit ** const font, unsigned int const frows, unsigned int const fcols) { /* This routine expects a font bitmap representing the following text: (0,0) M ",/^_[`jpqy| M / !"#$%&'()*+ / < ,-./01234567 < > 89:;<=>?@ABC > @ DEFGHIJKLMNO @ _ PQRSTUVWXYZ[ _ { \]^_`abcdefg { } hijklmnopqrs } ~ tuvwxyz{|}~ ~ M ",/^_[`jpqy| M The bitmap must be cropped exactly to the edges. The characters in the border you see are irrelevant except for character size compuations. The 12 x 8 array in the center is the font. The top left character there belongs to code point 0, and the code points increase in standard reading order, so the bottom right character is code point 127. You can't define code points < 32 or > 127 with this font format. The characters in the top and bottom border rows must include a character with the lowest reach of any in the font (e.g. "y", "_") and one with the highest reach (e.g. '"'). The characters in the left and right border columns must include characters with the rightmost and leftmost reach of any in the font (e.g. "M" for both). The border must be separated from the font by one blank text row or text column. The dissection works by finding the first blank row and column; i.e the lower right corner of the "M" in the upper left corner of the matrix. That gives the height and width of the maximum-sized character, which is not too useful. But the distance from there to the opposite side is an integral multiple of the cell size, and that's what we need. Then it's just a matter of filling in all the coordinates. */ unsigned int cellWidth, cellHeight; /* Dimensions in pixels of each cell of the font -- that includes the glyph and the white space above and to the right of it. Each cell is a tile of the font image. The top character row and left character row don't count -- those cells are smaller because they are missing the white space. */ unsigned int charWidth, charHeight; /* Maximum dimensions of glyph itself, inside its cell */ int row, col, ch, r, c, i; struct font * fn; struct glyph * glyph; char* bmap; computeCharacterSize(font, fcols, frows, &cellWidth, &cellHeight, &charWidth, &charHeight); /* Now convert to a general font */ MALLOCVAR(fn); if (fn == NULL) pm_error("out of memory allocating font structure"); fn->maxwidth = charWidth; fn->maxheight = charHeight; 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 = cellHeight * 2; col = cellWidth * 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 = cellWidth; 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 += cellWidth; if ( col >= cellWidth * 14 ) { col = cellWidth * 2; row += cellHeight; } } 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((const bit **)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 */ typedef struct { /*---------------------------------------------------------------------------- This is an object for reading lines of a font file. It reads and tokenizes them into words. -----------------------------------------------------------------------------*/ FILE * ifP; char line[1024]; /* This is the storage space for the words of the line. The words go in here, one after another, separated by NULs. It also functions as a work area for readline_read(). */ const char * arg[32]; /* These are the words; each entry is a pointer into line[] (above) */ } readline; static void readline_init(readline * const readlineP, FILE * const ifP) { readlineP->ifP = ifP; readlineP->arg[0] = NULL; } static void tokenize(char * const s, const char ** const words, unsigned int const maxWordCt) { /*---------------------------------------------------------------------------- Chop up 's' into words by changing space characters to NUL. Return as 'words' pointer to the beginning of those words in 's'. If there are more than 'maxWordCt' words in 's', ignore the excess on the right. -----------------------------------------------------------------------------*/ unsigned int n; char * p; p = &s[0]; n = 0; while (*p) { if (ISSPACE(*p)) *p++ = '\0'; else { words[n++] = p; if (n >= maxWordCt) break; while (*p && !ISSPACE(*p)) ++p; } } words[n] = NULL; } static void readline_read(readline * const readlineP, bool * const eofP) { /*---------------------------------------------------------------------------- Read a nonblank line from the file. Make its contents available as readlineP->arg[]. Return *eofP == true iff there is no nonblank line before EOF or we are unable to read the file. -----------------------------------------------------------------------------*/ bool gotLine; bool error; for (gotLine = false, error = false; !gotLine && !error; ) { char * rc; rc = fgets(readlineP->line, 1024, readlineP->ifP); if (rc == NULL) error = true; else { tokenize(readlineP->line, readlineP->arg, ARRAY_SIZE(readlineP->arg)); if (readlineP->arg[0] != NULL) gotLine = true; } } *eofP = error; } static void parseBitmapRow(const char * const hex, unsigned int const glyphWidth, unsigned char * const bmap, unsigned int const origBmapIndex, unsigned int * const newBmapIndexP, const char ** const errorP) { /*---------------------------------------------------------------------------- Parse one row of the bitmap for a glyph, from the hexadecimal string for that row in the font file, 'hex'. The glyph is 'glyphWidth' pixels wide. We place our result in 'bmap' at *bmapIndexP and advanced *bmapIndexP. -----------------------------------------------------------------------------*/ unsigned int bmapIndex; int i; /* dot counter */ const char * p; bmapIndex = origBmapIndex; for (i = glyphWidth, p = &hex[0], *errorP = NULL; i > 0 && !*errorP; i -= 4) { if (*p == '\0') pm_asprintf(errorP, "Not enough hexadecimal digits for glyph " "of width %u in '%s'", glyphWidth, hex); else { char const hdig = *p++; unsigned int hdigValue; if (hdig >= '0' && hdig <= '9') hdigValue = hdig - '0'; else if (hdig >= 'a' && hdig <= 'f') hdigValue = 10 + (hdig - 'a'); else if (hdig >= 'A' && hdig <= 'F') hdigValue = 10 + (hdig - 'A'); else pm_asprintf(errorP, "Invalid hex digit x%02x (%c) in bitmap data '%s'", (unsigned int)(unsigned char)hdig, isprint(hdig) ? hdig : '.', hex); if (!*errorP) { if (i > 0) bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0; if (i > 1) bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0; if (i > 2) bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0; if (i > 3) bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0; } } } *newBmapIndexP = bmapIndex; } static void readBitmap(readline * const readlineP, unsigned int const glyphWidth, unsigned int const glyphHeight, const char * const charName, unsigned char * const bmap) { int n; unsigned int bmapIndex; bmapIndex = 0; for (n = glyphHeight; n > 0; --n) { bool eof; const char * error; readline_read(readlineP, &eof); if (eof) pm_error("End of file in bitmap for character '%s' in BDF " "font file.", charName); if (!readlineP->arg[0]) pm_error("A line that is supposed to contain bitmap data, " "in hexadecimal, for character '%s' is empty", charName); parseBitmapRow(readlineP->arg[0], glyphWidth, bmap, bmapIndex, &bmapIndex, &error); if (error) { pm_error("Error in line %d of bitmap for character '%s': %s", n, charName, error); pm_strfree(error); } } } static void createBmap(unsigned int const glyphWidth, unsigned int const glyphHeight, readline * const readlineP, const char * const charName, const char ** const bmapP) { unsigned char * bmap; bool eof; if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight) pm_error("Ridiculously large glyph"); MALLOCARRAY(bmap, glyphWidth * glyphHeight); if (!bmap) pm_error("no memory for font glyph byte map"); readline_read(readlineP, &eof); if (eof) pm_error("End of file encountered reading font glyph byte map from " "BDF font file."); if (streq(readlineP->arg[0], "ATTRIBUTES")) { bool eof; readline_read(readlineP, &eof); if (eof) pm_error("End of file encountered after ATTRIBUTES in BDF " "font file."); } if (!streq(readlineP->arg[0], "BITMAP")) pm_error("'%s' found where BITMAP expected in definition of " "character '%s' in BDF font file.", readlineP->arg[0], charName); assert(streq(readlineP->arg[0], "BITMAP")); readBitmap(readlineP, glyphWidth, glyphHeight, charName, bmap); *bmapP = (char *)bmap; } static void readExpectedStatement(readline * const readlineP, const char * const expected) { /*---------------------------------------------------------------------------- Have the readline object *readlineP read the next line from the file, but expect it to be a line of type 'expected' (i.e. the verb token at the beginning of the line is that, e.g. "STARTFONT"). If it isn't, fail the program. -----------------------------------------------------------------------------*/ bool eof; readline_read(readlineP, &eof); if (eof) pm_error("EOF in BDF font file where '%s' expected", expected); else if (!streq(readlineP->arg[0], expected)) pm_error("Statement of type '%s' where '%s' expected in BDF font file", readlineP->arg[0], expected); } static void skipCharacter(readline * const readlineP) { /*---------------------------------------------------------------------------- In the BDF font file being read by readline object *readlineP, skip through the end of the character we are presently in. -----------------------------------------------------------------------------*/ bool endChar; endChar = FALSE; while (!endChar) { bool eof; readline_read(readlineP, &eof); if (eof) pm_error("End of file in the middle of a character (before " "ENDCHAR) in BDF font file."); endChar = streq(readlineP->arg[0], "ENDCHAR"); } } static void interpEncoding(const char ** const arg, unsigned int * const codepointP, bool * const badCodepointP) { /*---------------------------------------------------------------------------- With arg[] being the ENCODING statement from the font, return as *codepointP the codepoint that it indicates (code point is the character code, e.g. in ASCII, 48 is '0'). But if the statement doesn't give an acceptable codepoint return *badCodepointP == TRUE. -----------------------------------------------------------------------------*/ bool gotCodepoint; bool badCodepoint; unsigned int codepoint; if (atoi(arg[1]) >= 0) { codepoint = atoi(arg[1]); gotCodepoint = true; } else { if (arg[2]) { codepoint = atoi(arg[2]); gotCodepoint = true; } else gotCodepoint = false; } if (gotCodepoint) { if (codepoint > 255) badCodepoint = true; else badCodepoint = false; } else badCodepoint = true; *badCodepointP = badCodepoint; *codepointP = codepoint; } static void readEncoding(readline * const readlineP, unsigned int * const codepointP, bool * const badCodepointP) { readExpectedStatement(readlineP, "ENCODING"); interpEncoding(readlineP->arg, codepointP, badCodepointP); } static void processChars(readline * const readlineP, struct font * const fontP) { /*---------------------------------------------------------------------------- Process the CHARS block in a BDF font file, assuming the file is positioned just after the CHARS line. Read the rest of the block and apply its contents to *fontP. -----------------------------------------------------------------------------*/ unsigned int const nCharacters = atoi(readlineP->arg[1]); unsigned int nCharsDone; nCharsDone = 0; while (nCharsDone < nCharacters) { bool eof; readline_read(readlineP, &eof); if (eof) pm_error("End of file after CHARS reading BDF font file"); if (streq(readlineP->arg[0], "COMMENT")) { /* ignore */ } else if (!streq(readlineP->arg[0], "STARTCHAR")) pm_error("no STARTCHAR after CHARS in BDF font file"); else { const char * const charName = readlineP->arg[1]; struct glyph * glyphP; unsigned int codepoint; bool badCodepoint; assert(streq(readlineP->arg[0], "STARTCHAR")); MALLOCVAR(glyphP); if (glyphP == NULL) pm_error("no memory for font glyph for '%s' character", charName); readEncoding(readlineP, &codepoint, &badCodepoint); if (badCodepoint) skipCharacter(readlineP); else { readExpectedStatement(readlineP, "SWIDTH"); readExpectedStatement(readlineP, "DWIDTH"); glyphP->xadd = atoi(readlineP->arg[1]); readExpectedStatement(readlineP, "BBX"); glyphP->width = atoi(readlineP->arg[1]); glyphP->height = atoi(readlineP->arg[2]); glyphP->x = atoi(readlineP->arg[3]); glyphP->y = atoi(readlineP->arg[4]); createBmap(glyphP->width, glyphP->height, readlineP, charName, &glyphP->bmap); readExpectedStatement(readlineP, "ENDCHAR"); assert(codepoint < 256); /* Ensured by readEncoding() */ fontP->glyph[codepoint] = glyphP; } ++nCharsDone; } } } static void processBdfFontLine(readline * const readlineP, struct font * const fontP, bool * const endOfFontP) { /*---------------------------------------------------------------------------- Process a nonblank line just read from a BDF font file. This processing may involve reading more lines. -----------------------------------------------------------------------------*/ *endOfFontP = FALSE; /* initial assumption */ assert(readlineP->arg[0] != NULL); /* Entry condition */ if (streq(readlineP->arg[0], "COMMENT")) { /* ignore */ } else if (streq(readlineP->arg[0], "SIZE")) { /* ignore */ } else if (streq(readlineP->arg[0], "STARTPROPERTIES")) { /* Read off the properties and ignore them all */ unsigned int const propCount = atoi(readlineP->arg[1]); unsigned int i; for (i = 0; i < propCount; ++i) { bool eof; readline_read(readlineP, &eof); if (eof) pm_error("End of file after STARTPROPERTIES in BDF font file"); } } else if (streq(readlineP->arg[0], "FONTBOUNDINGBOX")) { fontP->maxwidth = atoi(readlineP->arg[1]); fontP->maxheight = atoi(readlineP->arg[2]); fontP->x = atoi(readlineP->arg[3]); fontP->y = atoi(readlineP->arg[4]); } else if (streq(readlineP->arg[0], "ENDFONT")) { *endOfFontP = true; } else if (streq(readlineP->arg[0], "CHARS")) { processChars(readlineP, fontP); } else { /* ignore */ } } struct font * pbm_loadbdffont(const char * const name) { FILE * ifP; readline readline; struct font * fontP; bool endOfFont; ifP = fopen(name, "rb"); if (!ifP) pm_error("Unable to open BDF font file name '%s'. errno=%d (%s)", name, errno, strerror(errno)); readline_init(&readline, ifP); MALLOCVAR(fontP); if (fontP == NULL) pm_error("no memory for font"); fontP->oldfont = 0; { /* Initialize all characters to nonexistent; we will fill the ones we find in the bdf file later. */ unsigned int i; for (i = 0; i < 256; ++i) fontP->glyph[i] = NULL; } fontP->x = fontP->y = 0; readExpectedStatement(&readline, "STARTFONT"); endOfFont = FALSE; while (!endOfFont) { bool eof; readline_read(&readline, &eof); if (eof) pm_error("End of file before ENDFONT statement in BDF font file"); processBdfFontLine(&readline, fontP, &endOfFont); } return fontP; } advanced/lib/pam.h0000644000175000001440000004372512347602012013170 0ustar ericusers/*============================================================================= These are declarations for use with the Portable Arbitrary Map (PAM) format and the Netpbm library functions specific to them. This file was originally written by Bryan Henderson and is contributed to the public domain by him and subsequent authors. =============================================================================*/ #ifndef PAM_H #define PAM_H #include #include #include #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' Use PAM_STRUCT_SIZE() to compute or interpret a value for this. */ FILE * file; int format; /* The format code of the 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. For output, only the format _type_ is significant, e.g. PBM_FORMAT and RPBM_FORMAT have identical effect. This is because on output, 'plainformat' determines whether the output is the raw or plain format of the type given by 'format'. */ unsigned int plainformat; /* Logical: On output, use plain version of the format type indicated by 'format'. Otherwise, use the raw version. (i.e., on output, the plainness information in 'format' is irrelevant). Input functions set this to FALSE, for the 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'. This is meaningless when 'format' is PAM_FORMAT, as PAM does not have plain and raw variations. */ 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 places the pointer at *comment_p. Caller must free it. NULL means libnetpbm does not return comments and does not allocate any storage. */ int visual; /* boolean */ /* tuple_type is one of the PAM-defined tuple types for visual images ("GRAYSCALE", "RGB_ALPHA", etc.). */ unsigned int color_depth; /* Number of color planes (i.e. 'depth', but without transparency). The color planes are the lowest numbered ones. Meaningless if 'visual' is false. */ int have_opacity; /* boolean */ /* The tuples have an opacity (transparency, alpha) plane. Meaningless if 'visual' is false. */ unsigned int opacity_plane; /* The plane number of the opacity plane; meaningless if 'haveOpacity' is false or 'visual' is false. */ }; #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. */ /* Some compilers are really vigilant and recognize it as an error to cast a 64 bit address to a 32 bit type. Hence the roundabout casting in PAM_MEMBER_OFFSET. */ #define PAM_MEMBER_OFFSET(mbrname) \ ((size_t)(unsigned long)(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_makerowrgba(const struct pam * const pamP, tuple * const tuplerow); void pnm_addopacityrow(const struct pam * const pamP, tuple * const tuplerow); 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); 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); const char * pnm_colorname(struct pam * const pamP, tuple const color, int const hexok); 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]) tuple pnm_backgroundtuple(struct pam * const pamP, tuple ** const tuples); /*---------------------------------------------------------------------------- These are meant for passing to pm_system() as Standard Input feeder and Standard Output accepter. The 'feederParm' or 'accepterParm' is a pointer to a struct pamtuples. -----------------------------------------------------------------------------*/ 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 advanced/lib/pm_gamma.h0000644000175000001440000000322012347602012014153 0ustar ericusers#ifndef _PM_GAMMA_H_ #define _PM_GAMMA_H_ #include #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 advanced/lib/ppmfloyd.h0000644000175000001440000000263612347602012014241 0ustar ericusers/* 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(unsigned int const cols, pixval const maxval, unsigned int const 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 advanced/lib/libppmcmap.c0000644000175000001440000005377212347602012014535 0ustar ericusers/* 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 "pm_config.h" #include "pm_c_util.h" #include "nstring.h" #include "mallocvar.h" #include "ppm.h" #include "ppmcmap.h" #define HASH_SIZE 20023 static __inline__ unsigned int ppm_hashpixel(pixel const p) { return (unsigned int) (PPM_GETR(p) * 33 * 33 + PPM_GETG(p) * 33 + PPM_GETB(p)) % 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); } } static colorhash_table alloccolorhash(void) { colorhash_table cht; int i; MALLOCARRAY(cht, HASH_SIZE); if (cht) { for (i = 0; i < HASH_SIZE; ++i) cht[i] = NULL; } return cht; } colorhash_table ppm_alloccolorhash(void) { colorhash_table cht; cht = alloccolorhash(); if (cht == NULL) pm_error( "out of memory allocating hash table" ); return cht; } static void readppmrow(FILE * const fileP, pixel * const pixelrow, int const cols, pixval const maxval, int const format, const char ** const errorP) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; if (setjmp(jmpbuf) != 0) { pm_setjmpbuf(origJmpbufP); pm_asprintf(errorP, "Failed to read row of image."); } else { pm_setjmpbufsave(&jmpbuf, &origJmpbufP); ppm_readppmrow(fileP, pixelrow, cols, maxval, format); *errorP = NULL; /* Would have longjmped if anything went wrong */ pm_setjmpbuf(origJmpbufP); } } static void buildHashTable(FILE * const ifP, pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, int const format, unsigned int const maxcolors, colorhash_table const cht, pixel * const rowbuffer, int * const nColorsP, bool * const tooManyColorsP, const char ** const errorP) { /*---------------------------------------------------------------------------- Look at all the colors in the file *ifP or array pixels[][] and add them to the hash table 'cht'. Even if we fail, we may add some colors to 'cht'. As soon as we've seen more that 'maxcolors' colors, we quit. In that case, only, we return *tooManyColorsP == true. That is not a failure. 'maxcolors' == 0 means infinity. -----------------------------------------------------------------------------*/ unsigned int row; unsigned int nColors; nColors = 0; /* initial value */ *tooManyColorsP = FALSE; /* initial value */ *errorP = NULL; /* initial value */ /* Go through the entire image, building a hash table of colors. */ for (row = 0; row < rows && !*tooManyColorsP && !*errorP; ++row) { unsigned int col; pixel * pixelrow; /* The row of pixels we are processing */ if (ifP) { readppmrow(ifP, rowbuffer, cols, maxval, format, errorP); pixelrow = rowbuffer; } else pixelrow = pixels[row]; for (col = 0; col < cols && !*tooManyColorsP && !*errorP; ++col) { const pixel apixel = pixelrow[col]; const int hash = ppm_hashpixel(apixel); colorhist_list chl; for (chl = cht[hash]; chl && !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) */ ++nColors; if (maxcolors > 0 && nColors > maxcolors) *tooManyColorsP = TRUE; else { MALLOCVAR(chl); if (chl == NULL) pm_asprintf(errorP, "out of memory computing hash table"); chl->ch.color = apixel; chl->ch.value = 1; chl->next = cht[hash]; cht[hash] = chl; } } } } *nColorsP = nColors; } static void computecolorhash(pixel ** const pixels, unsigned int const cols, unsigned int const rows, unsigned int const maxcolors, int * const nColorsP, FILE * const ifP, pixval const maxval, int const format, colorhash_table * const chtP, const char ** const errorP) { /*---------------------------------------------------------------------------- Compute a color histogram from an image. The input is one of two types: 1) a two-dimensional array of pixels 'pixels'; In this case, 'pixels' is non-NULL and 'ifP' is NULL. 2) an open file, positioned to the image data. In this case, 'pixels' is NULL and 'ifP' is non-NULL. ifP is the stream descriptor for the input file, and 'maxval' and 'format' are parameters of the image data in it. We return with the file still open and its position undefined. In either case, the image is 'cols' by 'rows'. Return the number of colors found as *colorsP. However, if 'maxcolors' is nonzero and the number of colors is greater than 'maxcolors', return a null return value and *colorsP undefined. -----------------------------------------------------------------------------*/ pixel * rowbuffer; /* malloc'ed */ /* Buffer for a row read from the input file; undefined (but still allocated) if input is not from a file. */ MALLOCARRAY(rowbuffer, cols); if (rowbuffer == NULL) pm_asprintf(errorP, "Unable to allocate %u-column row buffer.", cols); else { colorhash_table cht; bool tooManyColors; cht = alloccolorhash(); if (cht == NULL) pm_asprintf(errorP, "Unable to allocate color hash."); else { buildHashTable(ifP, pixels, cols, rows, maxval, format, maxcolors, cht, rowbuffer, nColorsP, &tooManyColors, errorP); if (tooManyColors) { ppm_freecolorhash(cht); *chtP = NULL; } else *chtP = cht; if (*errorP) ppm_freecolorhash(cht); } free(rowbuffer); } } colorhash_table ppm_computecolorhash(pixel ** const pixels, int const cols, int const rows, int const maxcolors, int * const colorsP) { colorhash_table cht; const char * error; computecolorhash(pixels, cols, rows, maxcolors, colorsP, NULL, 0, 0, &cht, &error); if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } return cht; } colorhash_table ppm_computecolorhash2(FILE * const ifP, int const cols, int const rows, pixval const maxval, int const format, int const maxcolors, int * const colorsP ) { colorhash_table cht; const char * error; computecolorhash(NULL, cols, rows, maxcolors, colorsP, ifP, maxval, format, &cht, &error); if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } return cht; } 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 retval; colorhash_table cht; const char * error; cht = alloccolorhash( ); /* Initializes to NULLs */ if (cht == NULL) pm_asprintf(&error, "Unable to allocate color hash"); else { unsigned int i; for (i = 0, error = NULL; i < colors && !error; ++i) { pixel const color = chv[i].color; int const hash = ppm_hashpixel(color); colorhist_list chl; for (chl = cht[hash]; chl && !error; chl = chl->next) if (PPM_EQUAL(chl->ch.color, color)) pm_asprintf(&error, "same color found twice: (%u %u %u)", PPM_GETR(color), PPM_GETG(color), PPM_GETB(color)); MALLOCVAR(chl); if (chl == NULL) pm_asprintf(&error, "out of memory"); else { chl->ch.color = color; chl->ch.value = i; chl->next = cht[hash]; cht[hash] = chl; } } if (error) ppm_freecolorhash(cht); } if (error) { pm_errormsg("%s", error); pm_strfree(error); pm_longjmp(); } else retval = cht; return retval; } 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 (*customCmp)(pixel *, pixel *); #ifndef LITERAL_FN_DEF_MATCH static qsort_comparison_fn customStub; #endif static int customStub(const void * const a, const void * const b) { return (*customCmp)((pixel *)a, (pixel *)b); } #ifndef LITERAL_FN_DEF_MATCH static qsort_comparison_fn pixelCmp; #endif static int pixelCmp(const void * const a, const void * const b) { const pixel * const p1 = (const pixel *)a; const pixel * const 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; } void ppm_sortcolorrow(pixel * const colorrow, int const ncolors, int (*cmpfunc)(pixel *, pixel *)) { if (cmpfunc) { customCmp = cmpfunc; qsort((void *)colorrow, ncolors, sizeof(pixel), customStub); } else qsort((void *)colorrow, ncolors, sizeof(pixel), pixelCmp); } 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 ppm_colorrowtomapfile(FILE *ofp, pixel *colormap, int ncolors, pixval maxval) { int i; ppm_writeppminit(ofp, ncolors, 1, maxval, 1); for( i = 0; i < ncolors; i++ ) ppm_writeppmrow(ofp, &colormap[i], 1, maxval, 1); } advanced/lib/ppm.h0000644000175000001440000002172212347602012013200 0ustar ericusers/* Interface header file for PPM-related functions in libnetpbm */ #ifndef _PPM_H_ #define _PPM_H_ #include #include #include #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 /* 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)) static __inline__ pixel ppm_whitepixel(pixval maxval) { pixel retval; PPM_ASSIGN(retval, maxval, maxval, maxval); return retval; } static __inline__ pixel ppm_blackpixel(void) { pixel const retval = {0, 0, 0}; return retval; } void ppm_init(int * argcP, char* argv[]); #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) pm_freerow(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, const 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_luminosity(pixel const p) { return (pixval)(PPM_LUMIN(p) + 0.5); } 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. Algorithms in libnetpbm depend on the numerical representations of these values being as follows. */ BKCOLOR_GRAY = 0, BKCOLOR_BROWN, BKCOLOR_ORANGE, BKCOLOR_RED, BKCOLOR_YELLOW, BKCOLOR_GREEN, BKCOLOR_BLUE, BKCOLOR_VIOLET, BKCOLOR_PURPLE, BKCOLOR_WHITE, BKCOLOR_BLACK } bk_color; #define BKCOLOR_COUNT (BKCOLOR_BLACK+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_*/ advanced/lib/colorname.h0000644000175000001440000000143212347602012014357 0ustar ericusers#ifndef COLORNAME_H #define COLORNAME_H #include #include #include #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 advanced/lib/rgb.txt0000644000175000001440000004710512347602012013551 0ustar ericusers# 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 # From Wikipedia article on Ochre, 10.04.21 204 119 34 Ochre # These were more or less invented for use with Netpbm: 255 255 255 D65 advanced/lib/libpnm2.c0000644000175000001440000001047712347602012013747 0ustar ericusers/* 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 "pgm.h" #include "pbm.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); } } static void writepgmrow(FILE * const fileP, const xel * const xelrow, unsigned int const cols, xelval const maxval, int const format, bool const plainFormat) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; gray * grayrow; grayrow = pgm_allocrow(cols); if (setjmp(jmpbuf) != 0) { pgm_freerow(grayrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (col = 0; col < cols; ++col) grayrow[col] = PNM_GET1(xelrow[col]); pgm_writepgmrow(fileP, grayrow, cols, (gray) maxval, plainFormat); pm_setjmpbuf(origJmpbufP); } pgm_freerow(grayrow); } static void writepbmrow(FILE * const fileP, const xel * const xelrow, unsigned int const cols, bool const plainFormat) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; bit * bitrow; bitrow = pbm_allocrow(cols); if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (col = 0; col < cols; ++col) bitrow[col] = PNM_GET1(xelrow[col]) == 0 ? PBM_BLACK : PBM_WHITE; pbm_writepbmrow(fileP, bitrow, cols, plainFormat); pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); } void pnm_writepnmrow(FILE * const fileP, const xel * const xelrow, int const cols, xelval const maxval, int const format, int const forceplain) { bool const plainFormat = forceplain || pm_plain_output; switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, plainFormat); break; case PGM_TYPE: writepgmrow(fileP, xelrow, cols, maxval, format, plainFormat); break; case PBM_TYPE: writepbmrow(fileP, xelrow, cols, plainFormat); break; default: pm_error("invalid format argument received by pnm_writepnmrow(): %d" "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); } advanced/lib/libsystem.c0000644000175000001440000004125312347602012014413 0ustar ericusers/*============================================================================= 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 #define _BSD_SOURCE /* Make SIGWINCH defined on OpenBSD */ #include #include #include #include #include #include #include #include #include "pm_c_util.h" #include "mallocvar.h" #include "pm.h" #include "pm_system.h" #define STDIN 0 #define STDOUT 1 static void closeUninheritableFds(int const stdinFd, int const stdoutFd) { /*---------------------------------------------------------------------------- Close all the file descriptors that we declare uninheritable -- files Parent has open that Child has no business accessing. Closing an extra file descriptor is essential to allow the file to close when Parent closes it. We define uninheritable as less than 64 and not Standard Input, Output, or Error, or 'stdinFd' or 'stdoutFd'. -----------------------------------------------------------------------------*/ int fd; for (fd = 0; fd < 64; ++fd) { if (fd == stdinFd) { } else if (fd == stdoutFd) { } else if (fd == STDIN_FILENO) { } else if (fd == STDOUT_FILENO) { } else if (fd == STDERR_FILENO) { } else { close(fd); } } } static void execProgram(const char * const progName, const char ** const argArray, int const stdinFd, int const stdoutFd) { /*---------------------------------------------------------------------------- Exec the program 'progName' with arguments argArray[], with 'stdinFd' as its Standard Input and 'stdoutFd' as its Standard Output. But if the exec fails, leave all file descriptors as we found them. Note that stdinFd or stdoutFd may actually be Standard Input and Standard Output already. -----------------------------------------------------------------------------*/ int stdinSaveFd, stdoutSaveFd; int rc; int execErrno; /* Make stdinFd Standard Input. Make stdoutFd Standard Output. */ if (stdinFd != STDIN) { stdinSaveFd = dup(STDIN); close(STDIN); dup2(stdinFd, STDIN); close(stdinFd); } if (stdoutFd != STDOUT) { stdoutSaveFd = dup(STDOUT); close(STDOUT); dup2(stdoutFd, STDOUT); close(stdoutFd); } rc = execvp(progName, (char **)argArray); execErrno = errno; if (stdinFd != STDIN) { close(STDIN); dup2(stdinSaveFd, STDIN); close(stdinSaveFd); } if (stdoutFd != STDOUT) { close(STDOUT); dup2(stdoutSaveFd, STDOUT); close(stdoutSaveFd); } if (rc < 0) pm_error("Unable to exec '%s' " "(i.e. the program did not run at all). " "execvp() errno=%d (%s)", progName, execErrno, strerror(execErrno)); else pm_error("INTERNAL ERROR. execvp() 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 rc; pm_pipe(pipeToFeed); rc = fork(); if (rc < 0) { pm_error("fork() of stdin feeder failed. errno=%d (%s)", errno, strerror(errno)); } else if (rc == 0) { /* This is the child -- the stdin feeder process */ close(pipeToFeed[0]); (*pipeFeederRtn)(pipeToFeed[1], feederParm); exit(0); } else { /* This is the parent */ pid_t const feederPid = rc; close(pipeToFeed[1]); *fdP = pipeToFeed[0]; *pidP = feederPid; } } static void spawnProcessor(const char * const progName, const char ** const argArray, int const stdinFd, int * const stdoutFdP, pid_t * const pidP) { /*---------------------------------------------------------------------------- Create a process to run program 'progName' with arguments argArray[] (terminated by NULL element). Pass file descriptor 'stdinFd' to the shell as Standard Input. if 'stdoutFdP' is NULL, have that process write its Standard Output to the current process' Standard Output. If 'stdoutFdP' is non-NULL, set up a pipe and pass it to the new process as Standard Output. Return as *stdoutFdP the file descriptor of the other end of that pipe, from which Caller can suck the program's Standard Output. -----------------------------------------------------------------------------*/ bool const pipeStdout = !!stdoutFdP; int stdoutpipe[2]; pid_t rc; if (pipeStdout) pm_pipe(stdoutpipe); rc = fork(); if (rc < 0) { pm_error("fork() of processor process failed. errno=%d (%s)", errno, strerror(errno)); } else if (rc == 0) { /* The program child */ int stdoutFd; if (pipeStdout) { close(stdoutpipe[0]); stdoutFd = stdoutpipe[1]; } else stdoutFd = STDOUT; closeUninheritableFds(stdinFd, stdoutFd); execProgram(progName, argArray, stdinFd, stdoutFd); close(stdinFd); close(stdoutpipe[1]); pm_error("INTERNAL ERROR: execProgram() returns."); } else { /* The parent */ pid_t const processorpid = rc; if (pipeStdout) { close(stdoutpipe[1]); *stdoutFdP = stdoutpipe[0]; } *pidP = processorpid; } } static const char * signalName(unsigned int const signalClass) { /* There are various signal classes that are not universally defined, so we make a half-hearted attempt to determine whether they are and not try to recognize the ones that aren't. We do this by testing whether a macro is defind with the signal class name. That could give a false negative, because the signal class name isn't necessarily defined as a macro, but it's a really, really small problem to miss one of these signal classes here, so we don't bother with all the work it would take to do it right. OpenBSD does not have SIGWINCH and SIGIO in 2013. Everyone else seems to have it. */ switch (signalClass) { case SIGHUP: /* POSIX.1 */ return "SIGHUP"; case SIGINT: /* POSIX.1 */ return "SIGINT"; case SIGQUIT: /* POSIX.1 */ return "SIGQUIT"; case SIGILL: /* POSIX.1 */ return "SIGILL"; case SIGTRAP: return "SIGTRAP"; case SIGABRT: /* POSIX.1 */ return "SIGABRT"; case SIGBUS: return "SIGBUS"; case SIGFPE: /* POSIX.1 */ return "SIGFPE"; case SIGKILL: /* POSIX.1 */ return "SIGKILL"; case SIGUSR1: /* POSIX.1 */ return "SIGUSR1"; case SIGSEGV: /* POSIX.1 */ return "SIGSEGV"; case SIGUSR2: /* POSIX.1 */ return "SIGUSR2"; case SIGPIPE: /* POSIX.1 */ return "SIGPIPE"; case SIGALRM: /* POSIX.1 */ return "SIGALRM"; case SIGTERM: /* POSIX.1 */ return "SIGTERM"; case SIGCHLD: /* POSIX.1 */ return "SIGCHLD"; case SIGCONT: /* POSIX.1 */ return "SIGCONT"; case SIGSTOP: /* POSIX.1 */ return "SIGSTOP"; case SIGTSTP: /* POSIX.1 */ return "SIGTSTP"; case SIGTTIN: /* POSIX.1 */ return "SIGTTIN"; case SIGTTOU: /* POSIX.1 */ return "SIGTTOU"; case SIGURG: return "SIGURG"; case SIGXCPU: return "SIGXCPU"; case SIGXFSZ: return "SIGXFSZ"; case SIGVTALRM: return "SIGVTALRM"; case SIGPROF: return "SIGPROF"; #ifdef SIGWINCH case SIGWINCH: return "SIGWINCH"; #endif #ifdef SIGIO case SIGIO: return "SIGIO"; #endif #ifdef SIGPWR case SIGPWR: return "SIGPWR"; #endif case SIGSYS: return "SIGSYS"; default: return "???"; } } static void cleanupProcessorProcess(pid_t const processorPid) { int terminationStatus; waitpid(processorPid, &terminationStatus, 0); if (WIFEXITED(terminationStatus)) { int const exitStatus = WEXITSTATUS(terminationStatus); if (exitStatus != 0) pm_message("Shell process exited with abnormal exit status %u. ", exitStatus); } else if (WIFSIGNALED(terminationStatus)) { pm_message("Shell process was killed by a Class %u (%s) signal.", WTERMSIG(terminationStatus), signalName(WTERMSIG(terminationStatus))); } else { pm_message("Shell process died, but its termination status " "0x%x doesn't make sense", terminationStatus); } } 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_vp(const char * const progName, const char ** const argArray, void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm) { /*---------------------------------------------------------------------------- Run a program in a child process. Feed its Standard Input with a pipe, which is fed by the routine 'stdinFeeder' with parameter 'feederParm'. Process its Standard Output with the routine 'stdoutAccepter' with parameter 'accepterParm'. But if 'stdinFeeder' is NULL, just feed the program our own Standard Input. And if 'stdoutFeeder' is NULL, just send its Standard Output to our own Standard Output. Run the program 'progName' with arguments argArray[] (terminated by NULL element). That includes arg0. -----------------------------------------------------------------------------*/ /* If 'stdinFeeder' is non-NULL, we create a child process to run 'stdinFeeder' and create a pipe from that process as the program's Standard Input. We create another child process to run the program. If 'stdoutFeeder' is non-NULL, we create a pipe between the program process and the current process and have the program write its Standard Output to that pipe. The current process runs 'stdoutAccepter' to read the data from that pipe. But if 'stdoutFeeder' is NULL, we just tell the program process to write to the current process' Standard Output. So there are two processes when stdinFeeder is NULL and three when stdinFeeder is non-null. */ int progStdinFd; pid_t feederPid; pid_t processorPid; if (stdinFeeder) createPipeFeeder(stdinFeeder, feederParm, &progStdinFd, &feederPid); else { progStdinFd = STDIN; feederPid = 0; } if (stdoutAccepter) { int progStdoutFd; /* Make a child process to run the program and pipe back to us its Standard Output */ spawnProcessor(progName, argArray, progStdinFd, &progStdoutFd, &processorPid); /* The child process has cloned our 'progStdinFd'; we have no more use for our copy. */ close(progStdinFd); /* Dispose of the stdout from that child */ (*stdoutAccepter)(progStdoutFd, accepterParm); close(progStdoutFd); } else { /* Run a child process for the program that sends its Standard Output to our Standard Output */ spawnProcessor(progName, argArray, progStdinFd, NULL, &processorPid); } cleanupProcessorProcess(processorPid); if (feederPid) cleanupFeederProcess(feederPid); } void pm_system_lp(const char * const progName, void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm, ...) { /*---------------------------------------------------------------------------- same as pm_system_vp() except with arguments as variable arguments instead of an array. -----------------------------------------------------------------------------*/ va_list args; bool endOfArgs; const char ** argArray; unsigned int n; va_start(args, accepterParm); endOfArgs = FALSE; argArray = NULL; for (endOfArgs = FALSE, argArray = NULL, n = 0; !endOfArgs; ) { const char * const arg = va_arg(args, const char *); REALLOCARRAY(argArray, n+1); argArray[n++] = arg; if (!arg) endOfArgs = TRUE; } va_end(args); pm_system_vp(progName, argArray, stdinFeeder, feederParm, stdoutAccepter, accepterParm); free(argArray); } void pm_system(void stdinFeeder(int, void *), void * const feederParm, void stdoutAccepter(int, void *), void * const accepterParm, const char * const shellCommand) { /*---------------------------------------------------------------------------- Run a shell and have it run command 'shellCommand'. Feed its Standard Input with a pipe, which is fed by the routine 'stdinFeeder' with parameter 'feederParm'. Process its Standard Output with the routine 'stdoutAccepter' with parameter 'accepterParm'. But if 'stdinFeeder' is NULL, just feed the shell our own Standard Input. And if 'stdoutFeeder' is NULL, just send its Standard Output to our own Standard Output. -----------------------------------------------------------------------------*/ pm_system_lp("/bin/sh", stdinFeeder, feederParm, stdoutAccepter, accepterParm, "sh", "-c", shellCommand, NULL); } void pm_feed_from_memory(int const pipeToFeedFd, void * const feederParm) { struct bufferDesc * const inputBufferP = feederParm; FILE * const outFileP = fdopen(pipeToFeedFd, "w"); size_t 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, outFileP); if (inputBufferP->bytesTransferredP) *(inputBufferP->bytesTransferredP) = bytesTransferred; fclose(outFileP); } void pm_accept_to_memory(int const pipetosuckFd, void * const accepterParm ) { struct bufferDesc * const outputBufferP = accepterParm; FILE * const inFileP = fdopen(pipetosuckFd, "r"); size_t bytesTransferred; bytesTransferred = fread(outputBufferP->buffer, 1, outputBufferP->size, inFileP); fclose(inFileP); if (outputBufferP->bytesTransferredP) *(outputBufferP->bytesTransferredP) = bytesTransferred; } advanced/lib/pnm.h0000644000175000001440000000717712347602012013206 0ustar ericusers/* pnm.h - header file for libpnm portable anymap library */ #ifndef _PNM_H_ #define _PNM_H_ #include #include #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_GETR(x) PPM_GETR(x) #define PNM_GETG(x) PPM_GETG(x) #define PNM_GETB(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(int * const argcP, char ** const argv); void pnm_nextimage(FILE *file, int * const eofP); xel * pnm_allocrow(unsigned int const cols); #define pnm_freerow(xelrow) pm_freerow(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, const 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 pnm_xeltopixel(xel const inputxel, int const format); xel pnm_parsecolorxel(const char * const colorName, xelval const maxval, int const format); #ifdef __cplusplus } #endif #endif /*_PNM_H_*/ advanced/lib/path.c0000644000175000001440000003361612347602012013340 0ustar ericusers/* 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); } advanced/lib/pamdraw.h0000644000175000001440000002425612347602012014044 0ustar ericusers/* pamdraw.h - header file for simple drawing routines in libnetpbm. ** ** 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: */ #include #ifdef __cplusplus extern "C" { #endif #if 0 } /* to fake out automatic code indenters */ #endif typedef struct { int x; int y; } pamd_point; static __inline__ pamd_point pamd_makePoint(int const x, int const y) { pamd_point p; p.x = x; p.y = y; return p; } void pamd_validateCoord(int const c); void pamd_validatePoint(pamd_point const p); typedef enum { PAMD_PATHLEG_LINE } pamd_pathlegtype; struct pamd_linelegparms { pamd_point end; }; typedef struct { /*---------------------------------------------------------------------------- A leg of a pamd_path. -----------------------------------------------------------------------------*/ pamd_pathlegtype type; union { struct pamd_linelegparms linelegparms; } u; } pamd_pathleg; typedef struct { /*---------------------------------------------------------------------------- A closed path -----------------------------------------------------------------------------*/ unsigned int version; /* Must be zero. For future expansion. */ pamd_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 pamd_pathleg. I.e. sizeof(pamd_pathleg). Used for binary backward compatibility between callers and libpamd as the definition of pamd_pathleg changes. */ pamd_pathleg * legs; } pamd_path; typedef void pamd_drawproc(tuple **, unsigned int, unsigned int, unsigned int, sample, pamd_point, const void *); pamd_drawproc pamd_point_drawproc; /* ** So, you call a drawing routine, e.g. pamd_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 pamd_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 PAMD_NULLDRAWPROC NULL /* ** Just like pamd_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 tuple 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 pamd_setlinetype(int const type); #define PAMD_LINETYPE_NORMAL 0 #define PAMD_LINETYPE_NODIAGS 1 /* If you set NODIAGS, all tuples drawn by pamd_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 pamd_setlineclip(int const clip); #define pamd_setlineclipping(x) pamd_setlineclip(x) /* Normally, pamd_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 pamd_line(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const p0, pamd_point const p1, pamd_drawproc drawProc, const void * const clientdata); /* Draws a line from p0 to p1. */ void pamd_spline3(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const p0, pamd_point const p1, pamd_point const p2, pamd_drawproc drawProc, const void * const clientdata); /* Draws a three-point spline from p0 to p2, with p1 as the control point. All drawing is done via pamd_line(), so the routines that control it control pamd_spline3p() as well. */ void pamd_polyspline(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const p0, unsigned int const nc, pamd_point * const c, pamd_point const p1, pamd_drawproc drawProc, const void * const clientdata); /* Draws a bunch of splines end to end. p0 and p1 are the initial and final points, and the c[] are the intermediate control points. nc is the number of these control points. */ void pamd_spline4(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const endPt0, pamd_point const endPt1, pamd_point const ctlPt0, pamd_point const ctlPt1, pamd_drawproc drawProc, const void * const clientdata); void pamd_circle(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const center, unsigned int const radius, pamd_drawproc drawProc, const void * const clientData); /* Draws a circle centered at 'center' with radius 'radius' */ /* Simple filling routines. */ void pamd_filledrectangle(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, int const left, int const top, int const width, int const height, pamd_drawproc drawProc, const void * const clientdata); /* Fills in the rectangle [left, top, width, height]. */ void pamd_fill_path(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_path * const pathP, tuple const color); /* Fills in a closed path. Not much different from pamd_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 * pamd_fill_create(void); /* Returns a blank fillhandle. */ void pamd_fill_destroy(struct fillobj * fillObjP); void pamd_fill_drawproc(tuple ** const tuples, unsigned int const cols, unsigned int const rows, unsigned int const depth, sample const maxval, pamd_point const p, const void * const clientdata); /* Use this drawproc to trace the outline you want filled. Use the fillhandle as the clientdata. */ void pamd_fill(tuple ** const tuples, int const cols, int const rows, int const depth, sample const maxval, struct fillobj * const fillObjP, pamd_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 pamd_text(tuple** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const pos, int const height, int const angle, const char * const sArg, pamd_drawproc drawProc, const void * const clientdata); /* Draws the null-terminated string 's' left justified at the point ('x', 'y'). The text will be 'height' tuples high and will be aligned on a baseline inclined 'angle' degrees with the X axis. The supplied drawproc and clientdata are passed to pamd_line() which performs the actual drawing. */ void pamd_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 pamd_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 pamd_text_box with a nonzero angle. */ #ifdef __cplusplus } #endif advanced/lib/ppmdfont.c0000644000175000001440000000643212347602012014227 0ustar ericusers#include #include #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) { size_t rc; rc = fread(&fontHeaderP->signature, 1, sizeof(fontHeaderP->signature), ifP); if (rc != sizeof(fontHeaderP->signature)) pm_error("Unable to read the header from the font file. " "errno=%d (%s)", errno, strerror(errno)); 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); } advanced/lib/libpnm3.c0000644000175000001440000002523012347602012013741 0ustar ericusers/* 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 "pgm.h" #include "pbm.h" static xel mean4(int const format, xel const a, xel const b, xel const c, xel const d) { /*---------------------------------------------------------------------------- Return cartesian mean of the 4 colors. -----------------------------------------------------------------------------*/ xel retval; switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: PPM_ASSIGN( retval, (PPM_GETR(a) + PPM_GETR(b) + PPM_GETR(c) + PPM_GETR(d)) / 4, (PPM_GETG(a) + PPM_GETG(b) + PPM_GETG(c) + PPM_GETG(d)) / 4, (PPM_GETB(a) + PPM_GETB(b) + PPM_GETB(c) + PPM_GETB(d)) / 4); break; case PGM_TYPE: case PBM_TYPE: PNM_ASSIGN1( retval, (PNM_GET1(a) + PNM_GET1(b) + PNM_GET1(c) + PNM_GET1(d))/4); break; default: pm_error("Invalid format passed to pnm_backgroundxel()"); } return retval; } xel pnm_backgroundxel(xel** const xels, int const cols, int const rows, xelval const maxval, int const format) { xel bgxel, ul, ur, ll, lr; /* Guess a good background value. */ ul = xels[0][0]; ur = xels[0][cols-1]; ll = xels[rows-1][0]; lr = xels[rows-1][cols-1]; /* We first recognize three corners equal. If not, we look for any two. If not, we just average all four. */ if (PNM_EQUAL(ul, ur) && PNM_EQUAL(ur, ll)) bgxel = ul; else if (PNM_EQUAL(ul, ur) && PNM_EQUAL(ur, lr)) bgxel = ul; else if (PNM_EQUAL(ul, ll) && PNM_EQUAL(ll, lr)) bgxel = ul; else if (PNM_EQUAL(ur, ll) && PNM_EQUAL(ll, lr)) bgxel = ur; else if (PNM_EQUAL(ul, ur)) bgxel = ul; else if (PNM_EQUAL(ul, ll)) bgxel = ul; else if (PNM_EQUAL(ul, lr)) bgxel = ul; else if (PNM_EQUAL(ur, ll)) bgxel = ur; else if (PNM_EQUAL(ur, lr)) bgxel = ur; else if (PNM_EQUAL(ll, lr)) bgxel = ll; else bgxel = mean4(format, ul, ur, ll, lr); return bgxel; } xel pnm_backgroundxelrow(xel * const xelrow, int const cols, xelval const maxval, int const format) { xel bgxel, l, r; /* Guess a good background value. */ l = xelrow[0]; r = xelrow[cols-1]; if (PNM_EQUAL(l, r)) /* Both corners are same color, so that's the background color, without any extra computation. */ bgxel = l; else { /* Corners are different, so use cartesian mean of them */ switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: PPM_ASSIGN(bgxel, (PPM_GETR(l) + PPM_GETR(r)) / 2, (PPM_GETG(l) + PPM_GETG(r)) / 2, (PPM_GETB(l) + PPM_GETB(r)) / 2 ); break; case PGM_TYPE: PNM_ASSIGN1(bgxel, (PNM_GET1(l) + PNM_GET1(r)) / 2); break; case PBM_TYPE: { unsigned int col, blackCnt; /* One black, one white. Gotta count. */ for (col = 0, blackCnt = 0; col < cols; ++col) { if (PNM_GET1(xelrow[col] ) == 0) ++blackCnt; } if (blackCnt >= cols / 2) PNM_ASSIGN1(bgxel, 0); else PNM_ASSIGN1(bgxel, maxval); break; } default: pm_error("Invalid format passed to pnm_backgroundxelrow()"); } } return bgxel; } xel pnm_whitexel(xelval const maxval, int const format) { xel retval; switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: PPM_ASSIGN(retval, maxval, maxval, maxval); break; case PGM_TYPE: case PBM_TYPE: PNM_ASSIGN1(retval, maxval); break; default: pm_error("Invalid format %d passed to pnm_whitexel()", format); } return retval; } xel pnm_blackxel(xelval const maxval, int const format) { xel retval; switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: PPM_ASSIGN(retval, 0, 0, 0); break; case PGM_TYPE: case PBM_TYPE: PNM_ASSIGN1(retval, 0); break; default: pm_error("Invalid format %d passed to pnm_blackxel()", format); } return retval; } 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()"); } } void pnm_promoteformat( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat ) { int row; for ( row = 0; row < rows; ++row ) pnm_promoteformatrow( xels[row], cols, maxval, format, newmaxval, newformat ); } void pnm_promoteformatrow( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat ) { 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 pnm_xeltopixel(xel const inputxel, int const format) { pixel outputpixel; switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: PPM_ASSIGN(outputpixel, PPM_GETR(inputxel), PPM_GETG(inputxel), PPM_GETB(inputxel)); break; case PGM_TYPE: case PBM_TYPE: PPM_ASSIGN(outputpixel, PNM_GET1(inputxel), PNM_GET1(inputxel), PNM_GET1(inputxel)); break; default: pm_error("Invalid format code %d passed to pnm_xeltopixel()", format); } return outputpixel; } xel pnm_parsecolorxel(const char * const colorName, xelval const maxval, int const format) { pixel const bgColor = ppm_parsecolor(colorName, maxval); xel retval; switch(PNM_FORMAT_TYPE(format)) { case PPM_TYPE: PNM_ASSIGN(retval, PPM_GETR(bgColor), PPM_GETG(bgColor), PPM_GETB(bgColor)); break; case PGM_TYPE: if (PPM_ISGRAY(bgColor)) PNM_ASSIGN1(retval, PPM_GETB(bgColor)); else pm_error("Non-gray color '%s' specified for a " "grayscale (PGM) image", colorName); break; case PBM_TYPE: if (PPM_EQUAL(bgColor, ppm_whitepixel(maxval))) PNM_ASSIGN1(retval, maxval); else if (PPM_EQUAL(bgColor, ppm_blackpixel())) PNM_ASSIGN1(retval, 0); else pm_error ("Color '%s', which is neither black nor white, " "specified for a black and white (PBM) image", colorName); break; default: pm_error("Invalid format code %d passed to pnm_parsecolorxel()", format); } return retval; } advanced/lib/libppm.h0000644000175000001440000000051012347602012013657 0ustar ericusers/* 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 advanced/lib/fileio.h0000644000175000001440000000063312347602012013651 0ustar ericusers#ifndef FILEIO_H_INCLUDED #define FILEIO_H_INCLUDED #include 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 advanced/lib/libpamcolor.c0000644000175000001440000000755212347602012014707 0ustar ericusers/*============================================================================ libpamcolor.c ============================================================================== These are the library functions, which belong in the libnetpbm library, that deal with colors in the PAM image format. This file was originally written by Bryan Henderson and is contributed to the public domain by him and subsequent authors. =============================================================================*/ /* See pmfileio.c for the complicated explanation of this 32/64 bit file offset stuff. */ #define _FILE_OFFSET_BITS 64 #define _LARGE_FILES #define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ #define _XOPEN_SOURCE 600 /* Make sure strdup() is in string.h */ #include #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; } const char * pnm_colorname(struct pam * const pamP, tuple const color, int const hexok) { const char * retval; pixel colorp; char * colorname; if (pamP->depth < 3) PPM_ASSIGN(colorp, color[0], color[0], color[0]); else PPM_ASSIGN(colorp, color[PAM_RED_PLANE], color[PAM_GRN_PLANE], color[PAM_BLU_PLANE]); colorname = ppm_colorname(&colorp, pamP->maxval, hexok); retval = strdup(colorname); if (retval == NULL) pm_error("Couldn't get memory for color name string"); return retval; } double pnm_lumin_factor[3] = {PPM_LUMINR, PPM_LUMING, PPM_LUMINB}; void 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; } advanced/lib/libppmcolor.c0000644000175000001440000006655012347602012014731 0ustar ericusers/* ** 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 600 /* Make sure strdup() is in string.h */ #include #include #include #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "ppm.h" #include "colorname.h" static void computeHexTable(int hexit[]) { unsigned int i; for ( i = 0; i < 256; ++i ) hexit[i] = -1; 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" and the maxval is 100, then rgb is 0xff, n is 3, and our result is 0xff / (16**3-1) * 100 = 6. -----------------------------------------------------------------------------*/ pixval retval; switch (hexDigitCount) { case 0: pm_error("A hexadecimal color specifier in color '%s' is " "an empty string", colorname); break; 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 parseHexDigits(const char * const string, char const delim, int const hexit[], pixval * const nP, unsigned int * const digitCountP) { unsigned int digitCount; pixval n; digitCount = 0; /* initial value */ n = 0; /* initial value */ while (string[digitCount] != delim) { char const digit = string[digitCount]; if (digit == '\0') pm_error("rgb: color spec ends prematurely"); else { int const hexval = hexit[(unsigned int)digit]; if (hexval == -1) pm_error("Invalid hex digit in rgb: color spec: 0x%02x", digit); n = n * 16 + hexval; ++digitCount; } } *nP = n; *digitCountP = digitCount; } static void parseNewHexX11(char const colorname[], pixval const maxval, bool const closeOk, pixel * const colorP) { /*---------------------------------------------------------------------------- Determine what color colorname[] specifies in the new style hex color specification format (e.g. rgb:55/40/55). Return that color as *colorP. Assume colorname[] starts with "rgb:", but otherwise it might be gibberish. -----------------------------------------------------------------------------*/ int hexit[256]; const char * cp; pixval n; unsigned int digitCount; pixval rNorm, gNorm, bNorm; computeHexTable(hexit); cp = &colorname[4]; parseHexDigits(cp, '/', hexit, &n, &digitCount); rNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname); cp += digitCount; ++cp; /* Skip the slash */ parseHexDigits(cp, '/', hexit, &n, &digitCount); gNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname); cp += digitCount; ++cp; /* Skip the slash */ parseHexDigits(cp, '\0', hexit, &n, &digitCount); bNorm = rgbnorm(n, maxval, digitCount, 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(char const colorname[], pixval const maxval, bool const closeOk, pixel * const colorP) { /*---------------------------------------------------------------------------- Return as *colorP the color specified by the old X11 style color specififier colorname[] (e.g. #554055). -----------------------------------------------------------------------------*/ int hexit[256]; long r,g,b; pixval rNorm, gNorm, bNorm; computeHexTable(hexit); if (!pm_strishex(&colorname[1])) pm_error("Non-hexadecimal characters in #-type color specification"); 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 const char ** allocColorNames() { const char ** colornames; MALLOCARRAY(colornames, MAXCOLORNAMES); if (colornames) { unsigned int i; for (i = 0; i < MAXCOLORNAMES; ++i) colornames[i] = NULL; } return colornames; } static colorhash_table allocColorHash(void) { colorhash_table cht; jmp_buf jmpbuf; jmp_buf * origJmpbufP; if (setjmp(jmpbuf) != 0) cht = NULL; else { pm_setjmpbufsave(&jmpbuf, &origJmpbufP); cht = ppm_alloccolorhash(); } pm_setjmpbuf(origJmpbufP); return cht; } static void processColorfileEntry(struct colorfile_entry const ce, colorhash_table const cht, const char ** const colornames, pixel * const colors, unsigned int * const colornameIndexP, const char ** const errorP) { if (*colornameIndexP >= MAXCOLORNAMES) pm_asprintf(errorP, "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. */ *errorP = NULL; } else { ppm_addtocolorhash(cht, &color, *colornameIndexP); colornames[*colornameIndexP] = strdup(ce.colorname); colors[*colornameIndexP] = color; if (colornames[*colornameIndexP] == NULL) pm_asprintf(errorP, "Unable to allocate space for color name"); else { *errorP = NULL; ++(*colornameIndexP); } } } } static void openColornameFile(const char * const fileName, bool const mustOpen, FILE ** const filePP, const char ** const errorP) { jmp_buf jmpbuf; jmp_buf * origJmpbufP; if (setjmp(jmpbuf) != 0) { pm_asprintf(errorP, "Failed to open color name file"); pm_setjmpbuf(origJmpbufP); pm_longjmp(); } else { *filePP = pm_openColornameFile(fileName, mustOpen); *errorP = NULL; /* Would have longjmped if there were a problem */ pm_setjmpbuf(origJmpbufP); } } static void readOpenColorFile(FILE * const colorFileP, unsigned int * const nColorsP, const char ** const colornames, pixel * const colors, colorhash_table const cht, const char ** const errorP) { /*---------------------------------------------------------------------------- Read the color dictionary file *colorFileP and add the colors in it to colornames[], colors[], and 'cht'. colornames[] and colors[] must be allocated with MAXCOLORNAMES entries at entry. We may add colors to 'cht' even if we fail. -----------------------------------------------------------------------------*/ unsigned int nColorsDone; bool done; nColorsDone = 0; done = FALSE; *errorP = NULL; while (!done && !*errorP) { struct colorfile_entry const ce = pm_colorget(colorFileP); if (!ce.colorname) done = TRUE; else processColorfileEntry(ce, cht, colornames, colors, &nColorsDone, errorP); } *nColorsP = nColorsDone; if (*errorP) { unsigned int colorIndex; for (colorIndex = 0; colorIndex < nColorsDone; ++colorIndex) pm_strfree(colornames[colorIndex]); } } static void readColorFile(const char * const fileName, bool const mustOpen, unsigned int * const nColorsP, const char ** const colornames, pixel * const colors, colorhash_table const cht, const char ** const errorP) { /*---------------------------------------------------------------------------- Read the color dictionary file named 'fileName' and add the colors in it to colornames[], colors[], and 'cht'. Return as *nColorsP the number of colors in it. If the file is not openable (e.g. not file by that name exists), abort the program if 'mustOpen' is true; otherwise, return values indicating a dictionary with no colors. colornames[] and colors[] must be allocated with MAXCOLORNAMES entries at entry. We may add colors to 'cht' even if we fail. -----------------------------------------------------------------------------*/ FILE * colorFileP; openColornameFile(fileName, mustOpen, &colorFileP, errorP); if (!*errorP) { if (colorFileP == NULL) { /* Couldn't open it, but Caller says treat same as empty file */ *nColorsP = 0; *errorP = NULL; } else { readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht, errorP); fclose(colorFileP); } } } static void readcolordict(const char * const fileName, bool const mustOpen, unsigned int * const nColorsP, const char *** const colornamesP, pixel ** const colorsP, colorhash_table * const chtP, const char ** const errorP) { const char ** colornames; colornames = allocColorNames(); if (colornames == NULL) pm_asprintf(errorP, "Unable to allocate space for colorname table."); else { pixel * colors; MALLOCARRAY(colors, MAXCOLORNAMES); if (colors == NULL) pm_asprintf(errorP, "Unable to allocate space for color table."); else { colorhash_table cht; cht = allocColorHash(); if (cht == NULL) pm_asprintf(errorP, "Unable to allocate space for color hash"); else { readColorFile(fileName, mustOpen, nColorsP, colornames, colors, cht, errorP); if (*errorP) ppm_freecolorhash(cht); else *chtP = cht; } if (*errorP) free(colors); else *colorsP = colors; } if (*errorP) free(colornames); else *colornamesP = colornames; } } 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) { /*---------------------------------------------------------------------------- Read the color dictionary from the file named 'fileName'. If we can't open the file (e.g. because it does not exist), and 'mustOpen' is false, return an empty dictionary (it contains no colors). But if 'mustOpen' is true, abort the program instead of returning an empty dictionary. Return as *nColorsP the number of colors in the dictionary. Return as *colornamesP the names of those colors. *colornamesP is a malloced array that Caller must free with ppm_freecolornames(). The first *nColorsP entries are valid; *chtP contains indices into this array. Return as *colorsP the colors. *colorsP is a malloced array of size MAXCOLORS with the first elements filled in and the rest undefined. Return as *chtP a color hash table mapping each color in the dictionary to the index into *colornamesP for the name of the color. Each of 'nColorsP, 'colornamesP', and 'colorsP' may be null, in which case we do not return the corresponding information (or allocate memory for it). -----------------------------------------------------------------------------*/ colorhash_table cht; const char ** colornames; pixel * colors; unsigned int nColors; const char * error; readcolordict(fileName, mustOpen, &nColors, &colornames, &colors, &cht, &error); if (error) { pm_errormsg("%s", error); pm_strfree(error); ppm_freecolorhash(cht); } else { if (chtP) *chtP = cht; else ppm_freecolorhash(cht); if (colornamesP) *colornamesP = colornames; else ppm_freecolornames(colornames); if (colorsP) *colorsP = colors; else ppm_freerow(colors); if (nColorsP) *nColorsP = nColors; } } 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; } advanced/generator/0000755000175000001440000000000012347602010013445 5ustar ericusersadvanced/generator/pamseq.c0000644000175000001440000001477012347602010015110 0ustar ericusers#include #include #include #include #include "pm_c_util.h" #include "pam.h" #include "shhopt.h" struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int depth; sample maxval; const char * tupletype; }; static void parseCommandLine(int argc, char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry *option_def = malloc(100*sizeof(optEntry)); /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int tupletypeSpec; unsigned int option_def_index; option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "tupletype", OPT_STRING, &cmdlineP->tupletype, &tupletypeSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!tupletypeSpec) cmdlineP->tupletype = ""; else { struct pam pam; if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type)) pm_error("The tuple type you specified is too long. " "Maximum %u characters.", (unsigned)sizeof(pam.tuple_type)-1); } if (argc-1 < 2) pm_error("Need two arguments: depth and maxval."); else if (argc-1 > 2) pm_error("Only two argumeents allowed: depth and maxval. " "You specified %d", argc-1); else { cmdlineP->depth = atoi(argv[1]); cmdlineP->maxval = atoi(argv[2]); if (cmdlineP->depth <= 0) pm_error("depth argument must be a positive number. You " "specified '%s'", argv[1]); if (cmdlineP->maxval <= 0) pm_error("maxval argument must be a positive number. You " "specified '%s'", argv[2]); if (cmdlineP->maxval > PNM_OVERALLMAXVAL) pm_error("The maxval you specified (%u) is too big. " "Maximum is %u", (unsigned int) cmdlineP->maxval, PNM_OVERALLMAXVAL); if (pm_maxvaltobits(cmdlineP->maxval) + pm_maxvaltobits(cmdlineP->depth-1) > sizeof(unsigned int)*8) pm_error("The maxval (%u) and depth (%u) you specified result " "in a larger number of tuples than this program can " "handle (roughly %u)", (unsigned int) cmdlineP->maxval, cmdlineP->depth, (unsigned int) -1); } } static unsigned int powint(unsigned int base, unsigned int exponent) { /*---------------------------------------------------------------------------- This is standard pow(), but for integers and optimized for small exponents. -----------------------------------------------------------------------------*/ unsigned int result; unsigned int i; result = 1; /* initial value */ for (i = 0; i < exponent; ++i) result *= base; return(result); } static void permuteHigherPlanes(struct pam const pam, int const nextplane, tuple * const tuplerow, int * const colP, tuple const lowerPlanes) { /*---------------------------------------------------------------------------- Create all the possible permutations of tuples whose lower-numbered planes contain the values from 'lowerPlanes'. I.e. vary the higher-numbered planes between zero and maxval. Write them sequentially into *tuplerow, starting at *colP. Adjust *colP to next the column after the ones we write. lower-numbered means with plane numbers less than 'nextplane'. We modify 'lowerPlanes' in the higher planes to undefined values. -----------------------------------------------------------------------------*/ if (nextplane == pam.depth - 1) { sample value; for (value = 0; value <= pam.maxval; ++value) { unsigned int plane; for (plane = 0; plane < nextplane; ++plane) tuplerow[*colP][plane] = lowerPlanes[plane]; tuplerow[*colP][nextplane] = value; ++(*colP); } } else { sample value; for (value = 0; value <= pam.maxval; ++value) { /* We do something sleazy here and use Caller's lowerPlanes[] variable as a local variable, modifying it in the higher plane positions. That's just for speed. */ lowerPlanes[nextplane] = value; permuteHigherPlanes(pam, nextplane+1, tuplerow, colP, lowerPlanes); } } } int main(int argc, char **argv) { struct cmdlineInfo cmdline; struct pam pam; int col; tuple lowerPlanes; /* This is working storage passed to permuteHigherPlanes(), which we call. Note that because we always pass zero as the "planes" argument to permuteHigherPlanes(), none of the "lower planes" value is defined as an input to permuteHigherPlanes(). */ tuple * tuplerow; pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); pam.size = sizeof(pam); pam.len = PAM_STRUCT_SIZE(tuple_type); pam.file = stdout; pam.format = PAM_FORMAT; pam.plainformat = 0; pam.width = powint(cmdline.maxval+1, cmdline.depth); pam.height = 1; pam.depth = cmdline.depth; pam.maxval = cmdline.maxval; strcpy(pam.tuple_type, cmdline.tupletype); pnm_writepaminit(&pam); tuplerow = pnm_allocpamrow(&pam); lowerPlanes = pnm_allocpamtuple(&pam); col = 0; permuteHigherPlanes(pam, 0, tuplerow, &col, lowerPlanes); if (col != pam.width) pm_error("INTERNAL ERROR: Wrote %d columns; should have written %d.", col, pam.width); pnm_writepamrow(&pam, tuplerow); pnm_freepamrow(tuplerow); return 0; } advanced/generator/pgmramp.c0000644000175000001440000001375712347602010015271 0ustar ericusers/* pgmramp.c - generate a grayscale ramp ** ** 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 "pm_c_util.h" #include "pgm.h" #include "shhopt.h" enum ramptype {RT_LR, RT_TB, RT_DIAG, RT_RECT, RT_ELLIP}; struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ enum ramptype ramptype; unsigned int cols; unsigned int rows; gray maxval; }; static void parseCommandLine(int argc, char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry *option_def = malloc(100*sizeof(optEntry)); /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int lrSpec, tbSpec, diagonalSpec, rectangleSpec, ellipseSpec; unsigned int maxvalSpec; unsigned int option_def_index; option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "lr", OPT_FLAG, NULL, &lrSpec, 0); OPTENT3(0, "tb", OPT_FLAG, NULL, &tbSpec, 0); OPTENT3(0, "diagonal", OPT_FLAG, NULL, &diagonalSpec, 0); OPTENT3(0, "rectangle", OPT_FLAG, NULL, &rectangleSpec, 0); OPTENT3(0, "ellipse", OPT_FLAG, NULL, &ellipseSpec, 0); OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ free (option_def); if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec == 0) pm_error("You must specify one of " "-lr, -tb, -diagonal, -rectangle, or -ellipse"); if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec > 1) pm_error("You may specify at most one of " "-lr, -tb, -diagonal, -rectangle, or -ellipse"); if (lrSpec) cmdlineP->ramptype = RT_LR; else if (tbSpec) cmdlineP->ramptype = RT_TB; else if (diagonalSpec) cmdlineP->ramptype = RT_DIAG; else if (rectangleSpec) cmdlineP->ramptype = RT_RECT; else if (ellipseSpec) cmdlineP->ramptype = RT_ELLIP; else pm_error("INTERNAL ERROR - no ramp type option found"); if (!maxvalSpec) cmdlineP->maxval = PGM_MAXMAXVAL; else { if (cmdlineP->maxval > PGM_OVERALLMAXVAL) pm_error("The value you specified for -maxval (%u) is too big. " "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL); if (cmdlineP->maxval < 1) pm_error("You cannot specify 0 for -maxval"); } if (argc-1 < 2) pm_error("Need two arguments: width and height."); else if (argc-1 > 2) pm_error("Only two arguments allowed: width and height. " "You specified %d", argc-1); else { cmdlineP->cols = pm_parse_width(argv[1]); cmdlineP->rows = pm_parse_height(argv[2]); } } int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; gray *grayrow; int rowso2, colso2; unsigned int row; pgm_init( &argc, argv ); parseCommandLine(argc, argv, &cmdline); colso2 = MAX(1, cmdline.cols / 2); rowso2 = MAX(1, cmdline.rows / 2); pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0); grayrow = pgm_allocrow(cmdline.cols); for (row = 0; row < cmdline.rows; ++row) { unsigned int col; for (col = 0; col < cmdline.cols; ++col) { switch (cmdline.ramptype) { case RT_LR: /* Fill row buffer once. All rows are identical. */ if (row == 0) grayrow[col] = (float) col * cmdline.maxval / MAX(cmdline.cols-1, 1); break; case RT_TB: grayrow[col] = (float) row * cmdline.maxval / MAX(cmdline.rows-1, 1); break; case RT_DIAG: grayrow[col] = ((float) col + row) * cmdline.maxval / MAX((float) cmdline.cols + cmdline.rows-2, 1); break; case RT_RECT: { float const r = fabs((int)(rowso2 - row)) / rowso2; float const c = fabs((int)(colso2 - col)) / colso2; grayrow[col] = cmdline.maxval - (r + c) / 2.0 * cmdline.maxval; } break; case RT_ELLIP: { float const r = fabs((int)(rowso2 - row)) / rowso2; float const c = fabs((int)(colso2 - col)) / colso2; float v; v = r * r + c * c; if ( v < 0.0 ) v = 0.0; else if ( v > 1.0 ) v = 1.0; grayrow[col] = cmdline.maxval - v * cmdline.maxval; } break; } } pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0); } pgm_freerow(grayrow); pm_close(stdout); return 0; } advanced/generator/pbmtextps.c0000644000175000001440000002675412347602010015655 0ustar ericusers/* * pbmtextps.c - render text into a bitmap using a postscript interpreter * * Copyright (C) 2002 by James McCann. * * 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. * * PostScript is a registered trademark of Adobe Systems International. * * Additions by Bryan Henderson contributed to public domain by author. * */ #define _XOPEN_SOURCE /* Make sure popen() is in stdio.h */ #define _BSD_SOURCE /* Make sure stdrup() is in string.h */ #include #include #include #include #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" #include "pbm.h" #define BUFFER_SIZE 2048 struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int res; /* resolution, DPI */ unsigned int fontsize; /* Size of font in points */ const char * font; /* Name of postscript font */ float stroke; /* Width of stroke in points (only for outline font) */ unsigned int verbose; const char * text; }; static void writeFileToStdout(const char * const fileName){ /* simple pbmtopbm */ FILE * ifP; int format; int cols, rows, row ; unsigned char * bitrow; ifP = pm_openr(fileName); pbm_readpbminit(ifP, &cols, &rows, &format); if (cols==0 || rows==0 || cols>INT_MAX-10 || rows>INT_MAX-10) pm_error("Abnormal output from gs program. " "width x height = %u x %u", cols, rows); pbm_writepbminit(stdout, cols, rows, 0); bitrow = pbm_allocrow_packed(cols); for (row = 0; row < rows; ++row) { pbm_readpbmrow_packed(ifP, bitrow, cols, format); pbm_writepbmrow_packed(stdout, bitrow, cols, 0); } pbm_freerow_packed(bitrow); } static void buildTextFromArgs(int const argc, char ** const argv, const char ** const textP) { char * text; unsigned int totalTextSize; unsigned int i; text = strdup(""); totalTextSize = 1; for (i = 1; i < argc; ++i) { if (i > 1) { totalTextSize += 1; text = realloc(text, totalTextSize); if (text == NULL) pm_error("out of memory"); strcat(text, " "); } totalTextSize += strlen(argv[i]); text = realloc(text, totalTextSize); if (text == NULL) pm_error("out of memory"); strcat(text, argv[i]); } *textP = text; } static void parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP) { /*--------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. ---------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; MALLOCARRAY(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "resolution", OPT_UINT, &cmdlineP->res, NULL, 0); OPTENT3(0, "font", OPT_STRING, &cmdlineP->font, NULL, 0); OPTENT3(0, "fontsize", OPT_UINT, &cmdlineP->fontsize, NULL, 0); OPTENT3(0, "stroke", OPT_FLOAT, &cmdlineP->stroke, NULL, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); /* Set the defaults */ cmdlineP->res = 150; cmdlineP->fontsize = 24; cmdlineP->font = "Times-Roman"; cmdlineP->stroke = -1; opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); buildTextFromArgs(argc, argv, &cmdlineP->text); } static const char * construct_postscript(struct cmdlineInfo const cmdline) { const char * retval; const char * template; if (cmdline.stroke < 0) template = "/%s findfont\n" "%d scalefont\n" "setfont\n" "12 36 moveto\n" "(%s) show\n" "showpage\n"; else template = "/%s findfont\n" "%d scalefont\n" "setfont\n" "12 36 moveto\n" "%f setlinewidth\n" "0 setgray\n" "(%s) true charpath\n" "stroke\n" "showpage\n"; if (cmdline.stroke < 0) pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, cmdline.text); else pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, cmdline.stroke, cmdline.text); return retval; } static const char * gsExecutableName() { const char * const which = "which gs"; static char buffer[BUFFER_SIZE]; FILE * f; memset(buffer, 0, BUFFER_SIZE); f = popen(which, "r"); if (!f) pm_error("Can't find ghostscript"); fread(buffer, 1, BUFFER_SIZE, f); if (buffer[strlen(buffer) - 1] == '\n') buffer[strlen(buffer) - 1] = '\0'; pclose(f); if (buffer[0] != '/' && buffer[0] != '.') pm_error("Can't find ghostscript"); return buffer; } static const char * cropExecutableName(void) { const char * const which = "which pnmcrop"; static char buffer[BUFFER_SIZE]; const char * retval; FILE * f; memset(buffer, 0, BUFFER_SIZE); f = popen(which, "r"); if (!f) retval = NULL; else { fread(buffer, 1, BUFFER_SIZE, f); if (buffer[strlen(buffer) - 1] == '\n') buffer[strlen(buffer) - 1] = 0; pclose(f); if (buffer[0] != '/' && buffer[0] != '.') { retval = NULL; pm_message("Can't find pnmcrop"); } else retval = buffer; } return retval; } static const char * gsCommand(const char * const psFname, const char * const outputFilename, struct cmdlineInfo const cmdline) { const char * retval; double const x = (double) cmdline.res * 11; double const y = (double) cmdline.res * ((double) cmdline.fontsize * 2 + 72) / 72; if (cmdline.res <= 0) pm_error("Resolution (dpi) must be positive."); if (cmdline.fontsize <= 0) pm_error("Font size must be positive."); /* The following checks are for guarding against overflows in this function. Huge x,y values that pass these checks may be rejected by the 'gs' program. */ if (x > (double) INT_MAX-10) pm_error("Absurdly fine resolution: %u. Output width too large.", cmdline.res ); if (y > (double) INT_MAX-10) pm_error("Absurdly fine resolution (%u) and/or huge font size (%u). " "Output height too large.", cmdline.res, cmdline.fontsize); pm_asprintf(&retval, "%s -g%dx%d -r%d -sDEVICE=pbm " "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s " "/dev/null", gsExecutableName(), (int) x, (int) y, cmdline.res, outputFilename, psFname); return retval; } static const char * cropCommand(const char * const inputFileName) { const char * retval; const char * plainOpt = pm_plain_output ? "-plain" : "" ; if (cropExecutableName()) { pm_asprintf(&retval, "%s -top -right %s %s", cropExecutableName(), plainOpt, inputFileName); if (retval == pm_strsol) pm_error("Unable to allocate memory"); } else retval = NULL; return retval; } static void writeProgram(const char * const psFname, struct cmdlineInfo const cmdline) { const char * ps; FILE * psfile; psfile = fopen(psFname, "w"); if (psfile == NULL) pm_error("Can't open temp file '%s'. Errno=%d (%s)", psFname, errno, strerror(errno)); ps = construct_postscript(cmdline); if (cmdline.verbose) pm_message("Postscript program = '%s'", ps); if (fwrite(ps, 1, strlen(ps), psfile) != strlen(ps)) pm_error("Can't write postscript to temp file"); fclose(psfile); pm_strfree(ps); } static void executeProgram(const char * const psFname, const char * const outputFname, struct cmdlineInfo const cmdline) { const char * com; int rc; com = gsCommand(psFname, outputFname, cmdline); if (com == NULL) pm_error("Can't allocate memory for a 'ghostscript' command"); if (cmdline.verbose) pm_message("Running Postscript interpreter '%s'", com); rc = system(com); if (rc != 0) pm_error("Failed to run Ghostscript process. rc=%d", rc); pm_strfree(com); } static void cropToStdout(const char * const inputFileName, bool const verbose) { const char * com; com = cropCommand(inputFileName); if (com == NULL) { /* No pnmcrop. So don't crop. */ pm_message("Can't find pnmcrop command, image will be large"); writeFileToStdout(inputFileName); } else { FILE * pnmcrop; if (verbose) pm_message("Running crop command '%s'", com); pnmcrop = popen(com, "r"); if (pnmcrop == NULL) pm_error("Can't run pnmcrop process"); else { char buf[2048]; bool eof; eof = FALSE; while (!eof) { int bytesRead; bytesRead = fread(buf, 1, sizeof(buf), pnmcrop); if (bytesRead > 0) { int rc; rc = fwrite(buf, 1, bytesRead, stdout); if (rc != bytesRead) pm_error("Can't write to stdout"); } else if (bytesRead == 0) eof = TRUE; else pm_error("Failed to read output of Pnmcrop process. " "Errno=%d (%s)", errno, strerror(errno)); } fclose(pnmcrop); } pm_strfree(com); } } static void createOutputFile(struct cmdlineInfo const cmdline) { const char * const template = "./pstextpbm.%d.tmp.%s"; const char * psFname; const char * uncroppedPbmFname; pm_asprintf(&psFname, template, getpid(), "ps"); if (psFname == NULL) pm_error("Unable to allocate memory"); writeProgram(psFname, cmdline); pm_asprintf(&uncroppedPbmFname, template, getpid(), "pbm"); if (uncroppedPbmFname == NULL) pm_error("Unable to allocate memory"); executeProgram(psFname, uncroppedPbmFname, cmdline); unlink(psFname); pm_strfree(psFname); cropToStdout(uncroppedPbmFname, cmdline.verbose); unlink(uncroppedPbmFname); pm_strfree(uncroppedPbmFname); } int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; pbm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); createOutputFile(cmdline); return 0; } advanced/generator/ppmcie.c0000644000175000001440000012527412347602010015101 0ustar ericusers/* Generate a PPM file representing a CIE color gamut chart by John Walker -- kelvin@@fourmilab.ch WWW home page: http://www.fourmilab.ch/ Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, without any conditions or restrictions. This software is provided "as is" without express or implied warranty. This program was called cietoppm in Walker's original work. Because "cie" is not a graphics format, Bryan changed the name when he integrated it into the Netpbm package in March 2000. */ /* Modified by Andrew J. S. Hamilton 21 May 1999 Andrew.Hamilton@Colorado.EDU http://casa.colorado.edu/~ajsh/ Corrected XYZ -> RGB transform. Introduced gamma correction. Introduced option to plot 1976 u' v' chromaticities. */ #include #include #include "pm_c_util.h" #include "ppm.h" #include "ppmdraw.h" #include "nstring.h" #define CLAMP(v, l, h) ((v) < (l) ? (l) : (v) > (h) ? (h) : (v)) pixval const cieMaxval = 255; /* Maxval to use in generated pixmaps */ /* A color system is defined by the CIE x and y coordinates of its three primary illuminants and the x and y coordinates of the white point. */ struct colorSystem { const char *name; /* Color system name */ double xRed, yRed, /* Red primary illuminant */ xGreen, yGreen, /* Green primary illuminant */ xBlue, yBlue, /* Blue primary illuminant */ xWhite, yWhite, /* White point */ gamma; /* gamma of nonlinear correction */ }; /* The following table gives the CIE color matching functions \bar{x}(\lambda), \bar{y}(\lambda), and \bar{z}(\lambda), for wavelengths \lambda at 5 nanometre increments from 380 nm through 780 nm. This table is used in conjunction with Planck's law for the energy spectrum of a black body at a given temperature to plot the black body curve on the CIE chart. */ static double cie_color_match[][3] = { { 0.0014, 0.0000, 0.0065 }, /* 380 nm */ { 0.0022, 0.0001, 0.0105 }, { 0.0042, 0.0001, 0.0201 }, { 0.0076, 0.0002, 0.0362 }, { 0.0143, 0.0004, 0.0679 }, { 0.0232, 0.0006, 0.1102 }, { 0.0435, 0.0012, 0.2074 }, { 0.0776, 0.0022, 0.3713 }, { 0.1344, 0.0040, 0.6456 }, { 0.2148, 0.0073, 1.0391 }, { 0.2839, 0.0116, 1.3856 }, { 0.3285, 0.0168, 1.6230 }, { 0.3483, 0.0230, 1.7471 }, { 0.3481, 0.0298, 1.7826 }, { 0.3362, 0.0380, 1.7721 }, { 0.3187, 0.0480, 1.7441 }, { 0.2908, 0.0600, 1.6692 }, { 0.2511, 0.0739, 1.5281 }, { 0.1954, 0.0910, 1.2876 }, { 0.1421, 0.1126, 1.0419 }, { 0.0956, 0.1390, 0.8130 }, { 0.0580, 0.1693, 0.6162 }, { 0.0320, 0.2080, 0.4652 }, { 0.0147, 0.2586, 0.3533 }, { 0.0049, 0.3230, 0.2720 }, { 0.0024, 0.4073, 0.2123 }, { 0.0093, 0.5030, 0.1582 }, { 0.0291, 0.6082, 0.1117 }, { 0.0633, 0.7100, 0.0782 }, { 0.1096, 0.7932, 0.0573 }, { 0.1655, 0.8620, 0.0422 }, { 0.2257, 0.9149, 0.0298 }, { 0.2904, 0.9540, 0.0203 }, { 0.3597, 0.9803, 0.0134 }, { 0.4334, 0.9950, 0.0087 }, { 0.5121, 1.0000, 0.0057 }, { 0.5945, 0.9950, 0.0039 }, { 0.6784, 0.9786, 0.0027 }, { 0.7621, 0.9520, 0.0021 }, { 0.8425, 0.9154, 0.0018 }, { 0.9163, 0.8700, 0.0017 }, { 0.9786, 0.8163, 0.0014 }, { 1.0263, 0.7570, 0.0011 }, { 1.0567, 0.6949, 0.0010 }, { 1.0622, 0.6310, 0.0008 }, { 1.0456, 0.5668, 0.0006 }, { 1.0026, 0.5030, 0.0003 }, { 0.9384, 0.4412, 0.0002 }, { 0.8544, 0.3810, 0.0002 }, { 0.7514, 0.3210, 0.0001 }, { 0.6424, 0.2650, 0.0000 }, { 0.5419, 0.2170, 0.0000 }, { 0.4479, 0.1750, 0.0000 }, { 0.3608, 0.1382, 0.0000 }, { 0.2835, 0.1070, 0.0000 }, { 0.2187, 0.0816, 0.0000 }, { 0.1649, 0.0610, 0.0000 }, { 0.1212, 0.0446, 0.0000 }, { 0.0874, 0.0320, 0.0000 }, { 0.0636, 0.0232, 0.0000 }, { 0.0468, 0.0170, 0.0000 }, { 0.0329, 0.0119, 0.0000 }, { 0.0227, 0.0082, 0.0000 }, { 0.0158, 0.0057, 0.0000 }, { 0.0114, 0.0041, 0.0000 }, { 0.0081, 0.0029, 0.0000 }, { 0.0058, 0.0021, 0.0000 }, { 0.0041, 0.0015, 0.0000 }, { 0.0029, 0.0010, 0.0000 }, { 0.0020, 0.0007, 0.0000 }, { 0.0014, 0.0005, 0.0000 }, { 0.0010, 0.0004, 0.0000 }, { 0.0007, 0.0002, 0.0000 }, { 0.0005, 0.0002, 0.0000 }, { 0.0003, 0.0001, 0.0000 }, { 0.0002, 0.0001, 0.0000 }, { 0.0002, 0.0001, 0.0000 }, { 0.0001, 0.0000, 0.0000 }, { 0.0001, 0.0000, 0.0000 }, { 0.0001, 0.0000, 0.0000 }, { 0.0000, 0.0000, 0.0000 } /* 780 nm */ }; /* The following table gives the spectral chromaticity co-ordinates x(\lambda) and y(\lambda) for wavelengths in 5 nanometre increments from 380 nm through 780 nm. These co-ordinates represent the position in the CIE x-y space of pure spectral colors of the given wavelength, and thus define the outline of the CIE "tongue" diagram. */ static double spectral_chromaticity[81][3] = { { 0.1741, 0.0050 }, /* 380 nm */ { 0.1740, 0.0050 }, { 0.1738, 0.0049 }, { 0.1736, 0.0049 }, { 0.1733, 0.0048 }, { 0.1730, 0.0048 }, { 0.1726, 0.0048 }, { 0.1721, 0.0048 }, { 0.1714, 0.0051 }, { 0.1703, 0.0058 }, { 0.1689, 0.0069 }, { 0.1669, 0.0086 }, { 0.1644, 0.0109 }, { 0.1611, 0.0138 }, { 0.1566, 0.0177 }, { 0.1510, 0.0227 }, { 0.1440, 0.0297 }, { 0.1355, 0.0399 }, { 0.1241, 0.0578 }, { 0.1096, 0.0868 }, { 0.0913, 0.1327 }, { 0.0687, 0.2007 }, { 0.0454, 0.2950 }, { 0.0235, 0.4127 }, { 0.0082, 0.5384 }, { 0.0039, 0.6548 }, { 0.0139, 0.7502 }, { 0.0389, 0.8120 }, { 0.0743, 0.8338 }, { 0.1142, 0.8262 }, { 0.1547, 0.8059 }, { 0.1929, 0.7816 }, { 0.2296, 0.7543 }, { 0.2658, 0.7243 }, { 0.3016, 0.6923 }, { 0.3373, 0.6589 }, { 0.3731, 0.6245 }, { 0.4087, 0.5896 }, { 0.4441, 0.5547 }, { 0.4788, 0.5202 }, { 0.5125, 0.4866 }, { 0.5448, 0.4544 }, { 0.5752, 0.4242 }, { 0.6029, 0.3965 }, { 0.6270, 0.3725 }, { 0.6482, 0.3514 }, { 0.6658, 0.3340 }, { 0.6801, 0.3197 }, { 0.6915, 0.3083 }, { 0.7006, 0.2993 }, { 0.7079, 0.2920 }, { 0.7140, 0.2859 }, { 0.7190, 0.2809 }, { 0.7230, 0.2770 }, { 0.7260, 0.2740 }, { 0.7283, 0.2717 }, { 0.7300, 0.2700 }, { 0.7311, 0.2689 }, { 0.7320, 0.2680 }, { 0.7327, 0.2673 }, { 0.7334, 0.2666 }, { 0.7340, 0.2660 }, { 0.7344, 0.2656 }, { 0.7346, 0.2654 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 }, { 0.7347, 0.2653 } /* 780 nm */ }; static pixel **pixels; /* Pixel map */ static int pixcols, pixrows; /* Pixel map size */ static int sxsize = 512, sysize = 512; /* X, Y size */ /* Standard white point chromaticities. */ #define IlluminantC 0.3101, 0.3162 /* For NTSC television */ #define IlluminantD65 0.3127, 0.3291 /* For EBU and SMPTE */ /* Gamma of nonlinear correction. See Charles Poynton's ColorFAQ Item 45 and GammaFAQ Item 6 at http://www.inforamp.net/~poynton/ColorFAQ.html http://www.inforamp.net/~poynton/GammaFAQ.html */ #define GAMMA_REC709 0. /* Rec. 709 */ static struct colorSystem const NTSCsystem = { "NTSC", 0.67, 0.33, 0.21, 0.71, 0.14, 0.08, IlluminantC, GAMMA_REC709 }, EBUsystem = { "EBU (PAL/SECAM)", 0.64, 0.33, 0.29, 0.60, 0.15, 0.06, IlluminantD65, GAMMA_REC709 }, SMPTEsystem = { "SMPTE", 0.630, 0.340, 0.310, 0.595, 0.155, 0.070, IlluminantD65, GAMMA_REC709 }, HDTVsystem = { "HDTV", 0.670, 0.330, 0.210, 0.710, 0.150, 0.060, IlluminantD65, GAMMA_REC709 }, /* Huh? -ajsh CIEsystem = { "CIE", 0.7355, 0.2645, 0.2658, 0.7243, 0.1669, 0.0085, 0.3894, 0.3324, GAMMA_REC709 }, */ CIEsystem = { "CIE", 0.7355,0.2645,0.2658,0.7243,0.1669,0.0085, 0.33333333, 0.33333333, GAMMA_REC709 }, Rec709system = { "CIE REC 709", 0.64, 0.33, 0.30, 0.60, 0.15, 0.06, IlluminantD65, GAMMA_REC709 }; /* Customsystem is a variable that is initialized to CIE Rec 709, but we modify it with information specified by the user's options. */ static struct colorSystem Customsystem = { "Custom", 0.64, 0.33, 0.30, 0.60, 0.15, 0.06, IlluminantD65, GAMMA_REC709 }; static void upvp_to_xy(double const up, double const vp, double * const xc, double * const yc) { /*---------------------------------------------------------------------------- Given 1976 coordinates u', v', determine 1931 chromaticities x, y -----------------------------------------------------------------------------*/ *xc = 9*up / (6*up - 16*vp + 12); *yc = 4*vp / (6*up - 16*vp + 12); } static void xy_to_upvp(double const xc, double const yc, double * const up, double * const vp) { /*---------------------------------------------------------------------------- Given 1931 chromaticities x, y, determine 1976 coordinates u', v' -----------------------------------------------------------------------------*/ *up = 4*xc / (- 2*xc + 12*yc + 3); *vp = 9*yc / (- 2*xc + 12*yc + 3); } static void xyz_to_rgb(const struct colorSystem * const cs, double const xc, double const yc, double const zc, double * const r, double * const g, double * const b) { /*---------------------------------------------------------------------------- Given an additive tricolor system CS, defined by the CIE x and y chromaticities of its three primaries (z is derived trivially as 1-(x+y)), and a desired chromaticity (XC, YC, ZC) in CIE space, determine the contribution of each primary in a linear combination which sums to the desired chromaticity. If the requested chromaticity falls outside the Maxwell triangle (color gamut) formed by the three primaries, one of the r, g, or b weights will be negative. Caller can use constrain_rgb() to desaturate an outside-gamut color to the closest representation within the available gamut. -----------------------------------------------------------------------------*/ double xr, yr, zr, xg, yg, zg, xb, yb, zb; double xw, yw, zw; double rx, ry, rz, gx, gy, gz, bx, by, bz; double rw, gw, bw; xr = cs->xRed; yr = cs->yRed; zr = 1 - (xr + yr); xg = cs->xGreen; yg = cs->yGreen; zg = 1 - (xg + yg); xb = cs->xBlue; yb = cs->yBlue; zb = 1 - (xb + yb); xw = cs->xWhite; yw = cs->yWhite; zw = 1 - (xw + yw); /* xyz -> rgb matrix, before scaling to white. */ rx = yg*zb - yb*zg; ry = xb*zg - xg*zb; rz = xg*yb - xb*yg; gx = yb*zr - yr*zb; gy = xr*zb - xb*zr; gz = xb*yr - xr*yb; bx = yr*zg - yg*zr; by = xg*zr - xr*zg; bz = xr*yg - xg*yr; /* White scaling factors. Dividing by yw scales the white luminance to unity, as conventional. */ rw = (rx*xw + ry*yw + rz*zw) / yw; gw = (gx*xw + gy*yw + gz*zw) / yw; bw = (bx*xw + by*yw + bz*zw) / yw; /* xyz -> rgb matrix, correctly scaled to white. */ rx = rx / rw; ry = ry / rw; rz = rz / rw; gx = gx / gw; gy = gy / gw; gz = gz / gw; bx = bx / bw; by = by / bw; bz = bz / bw; /* rgb of the desired point */ *r = rx*xc + ry*yc + rz*zc; *g = gx*xc + gy*yc + gz*zc; *b = bx*xc + by*yc + bz*zc; } static int constrain_rgb(double * const r, double * const g, double * const b) { /*---------------------------------------------------------------------------- If the requested RGB shade contains a negative weight for one of the primaries, it lies outside the color gamut accessible from the given triple of primaries. Desaturate it by adding white, equal quantities of R, G, and B, enough to make RGB all positive. -----------------------------------------------------------------------------*/ double w; /* Amount of white needed is w = - min(0, *r, *g, *b) */ w = (0 < *r) ? 0 : *r; w = (w < *g) ? w : *g; w = (w < *b) ? w : *b; w = - w; /* Add just enough white to make r, g, b all positive. */ if (w > 0) { *r += w; *g += w; *b += w; return 1; /* Color modified to fit RGB gamut */ } return 0; /* Color within RGB gamut */ } static void gamma_correct(const struct colorSystem * const cs, double * const c) { /*---------------------------------------------------------------------------- Transform linear RGB values to nonlinear RGB values. Rec. 709 is ITU-R Recommendation BT. 709 (1990) ``Basic Parameter Values for the HDTV Standard for the Studio and for International Programme Exchange'', formerly CCIR Rec. 709. For details see http://www.inforamp.net/~poynton/ColorFAQ.html http://www.inforamp.net/~poynton/GammaFAQ.html -----------------------------------------------------------------------------*/ double gamma; gamma = cs->gamma; if (gamma == 0.) { /* Rec. 709 gamma correction. */ double cc = 0.018; if (*c < cc) { *c *= (1.099 * pow(cc, 0.45) - 0.099) / cc; } else { *c = 1.099 * pow(*c, 0.45) - 0.099; } } else { /* Nonlinear color = (Linear color)^(1/gamma) */ *c = pow(*c, 1./gamma); } } static void gamma_correct_rgb(const struct colorSystem * const cs, double * const r, double * const g, double * const b) { gamma_correct(cs, r); gamma_correct(cs, g); gamma_correct(cs, b); } /* Sz(X) is the displacement in pixels of a displacement of X normalized distance units. (A normalized distance unit is 1/512 of the smaller dimension of the canvas) */ #define Sz(x) (((x) * (int)MIN(pixcols, pixrows)) / 512) /* B(X, Y) is a pair of function arguments (for a libppmd function) that give a pixel position on the canvas. That position is (X, Y), biased horizontally 'xBias' pixels. */ #define B(x, y) ((x) + xBias), (y) #define Bixels(y, x) pixels[y][x + xBias] static void computeMonochromeColorLocation( double const waveLength, int const pxcols, int const pxrows, bool const upvp, int * const xP, int * const yP) { int const ix = (waveLength - 380) / 5; double const px = spectral_chromaticity[ix][0]; double const py = spectral_chromaticity[ix][1]; if (upvp) { double up, vp; xy_to_upvp(px, py, &up, &vp); *xP = up * (pxcols - 1); *yP = (pxrows - 1) - vp * (pxrows - 1); } else { *xP = px * (pxcols - 1); *yP = (pxrows - 1) - py * (pxrows - 1); } } static void makeAllBlack(pixel ** const pixels, unsigned int const cols, unsigned int const rows) { unsigned int row; for (row = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) PPM_ASSIGN(pixels[row][col], 0, 0, 0); } } static void drawTongueOutline(pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, bool const upvp, int const xBias, int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; pixel rgbcolor; int wavelength; int lx, ly; int fx, fy; PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); for (wavelength = 380; wavelength <= 700; wavelength += 5) { int icx, icy; computeMonochromeColorLocation(wavelength, pxcols, pxrows, upvp, &icx, &icy); if (wavelength > 380) ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly), B(icx, icy), PPMD_NULLDRAWPROC, (char *) &rgbcolor); else { fx = icx; fy = icy; } lx = icx; ly = icy; } ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly), B(fx, fy), PPMD_NULLDRAWPROC, (char *) &rgbcolor); } static void findTongue(pixel ** const pixels, int const pxcols, int const xBias, int const row, bool * const presentP, int * const leftEdgeP, int * const rightEdgeP) { /*---------------------------------------------------------------------------- Find out if there is any tongue on row 'row' of image 'pixels', and if so, where. We assume the image consists of all black except a white outline of the tongue. -----------------------------------------------------------------------------*/ int i; for (i = 0; i < pxcols && PPM_GETR(Bixels(row, i)) == 0; ++i); if (i >= pxcols) *presentP = false; else { int j; int const leftEdge = i; *presentP = true; for (j = pxcols - 1; j >= leftEdge && PPM_GETR(Bixels(row, j)) == 0; --j); *rightEdgeP = j; *leftEdgeP = leftEdge; } } static void fillInTongue(pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, const struct colorSystem * const cs, bool const upvp, int const xBias, int const yBias, bool const highlightGamut) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; int y; /* Scan the image line by line and fill the tongue outline with the RGB values determined by the color system for the x-y co-ordinates within the tongue. */ for (y = 0; y < pxrows; ++y) { bool present; /* There is some tongue on this line */ int leftEdge; /* x position of leftmost pixel in tongue on this line */ int rightEdge; /* same, but rightmost */ findTongue(pixels, pxcols, xBias, y, &present, &leftEdge, &rightEdge); if (present) { int x; for (x = leftEdge; x <= rightEdge; ++x) { double cx, cy, cz, jr, jg, jb, jmax; int r, g, b, mx; if (upvp) { double up, vp; up = ((double) x) / (pxcols - 1); vp = 1.0 - ((double) y) / (pxrows - 1); upvp_to_xy(up, vp, &cx, &cy); cz = 1.0 - (cx + cy); } else { cx = ((double) x) / (pxcols - 1); cy = 1.0 - ((double) y) / (pxrows - 1); cz = 1.0 - (cx + cy); } xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb); mx = maxval; /* Check whether the requested color is within the gamut achievable with the given color system. If not, draw it in a reduced intensity, interpolated by desaturation to the closest within-gamut color. */ if (constrain_rgb(&jr, &jg, &jb)) mx = highlightGamut ? maxval : ((maxval + 1) * 3) / 4; /* Scale to max(rgb) = 1. */ jmax = MAX(jr, MAX(jg, jb)); if (jmax > 0) { jr = jr / jmax; jg = jg / jmax; jb = jb / jmax; } /* gamma correct from linear rgb to nonlinear rgb. */ gamma_correct_rgb(cs, &jr, &jg, &jb); r = mx * jr; g = mx * jg; b = mx * jb; PPM_ASSIGN(Bixels(y, x), (pixval) r, (pixval) g, (pixval) b); } } } } static void drawYAxis(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, unsigned int const xBias, unsigned int const yBias, pixel const axisColor) { unsigned int const pxrows = pixrows - yBias; ppmd_line(pixels, pixcols, pixrows, maxval, B(0, 0), B(0, pxrows - 1), PPMD_NULLDRAWPROC, (char *) &axisColor); } static void drawXAxis(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, unsigned int const xBias, unsigned int const yBias, pixel const axisColor) { unsigned int const pxcols = pixcols - xBias; unsigned int const pxrows = pixrows - yBias; ppmd_line(pixels, pixcols, pixrows, maxval, B(0, pxrows - 1), B(pxcols - 1, pxrows - 1), PPMD_NULLDRAWPROC, (char *) &axisColor); } static void tickX(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, unsigned int const xBias, unsigned int const yBias, pixel const axisColor, unsigned int const tenth) { /*---------------------------------------------------------------------------- Put a tick mark 'tenth' tenths of the way along the X axis and label it. 'pixels' is the canvas on which to draw it; its dimensions are 'pixcols' by 'pixrows' and has maxval 'maxval'. -----------------------------------------------------------------------------*/ unsigned int const pxcols = pixcols - xBias; unsigned int const pxrows = pixrows - yBias; unsigned int const tickCol = (tenth * (pxcols - 1)) / 10; /* Pixel column where the left edge of the tick goes */ unsigned int const tickThickness = Sz(3); /* Thickness of the tick in pixels */ unsigned int const tickBottom = pxrows - Sz(1); /* Pixel row of the bottom of the tick */ char s[20]; assert(tenth < 10); sprintf(s, "0.%u", tenth); ppmd_line(pixels, pixcols, pixrows, maxval, B(tickCol, tickBottom), B(tickCol, tickBottom - tickThickness), PPMD_NULLDRAWPROC, (char *) &axisColor); ppmd_text(pixels, pixcols, pixrows, maxval, B(tickCol - Sz(11), pxrows + Sz(12)), Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &axisColor); } static void tickY(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, unsigned int const xBias, unsigned int const yBias, pixel const axisColor, unsigned int const tenth) { /*---------------------------------------------------------------------------- Put a tick mark 'tenth' tenths of the way along the Y axis and label it. 'pixels' is the canvas on which to draw it; its dimensions are 'pixcols' by 'pixrows' and has maxval 'maxval'. -----------------------------------------------------------------------------*/ unsigned int const pxrows = pixrows - yBias; unsigned int const tickRow = (tenth * (pxrows - 1)) / 10; /* Pixel row where the top of the tick goes */ unsigned int const tickThickness = Sz(3); /* Thickness of the tick in pixels */ char s[20]; assert(tenth < 10); sprintf(s, "0.%d", 10 - tenth); ppmd_line(pixels, pixcols, pixrows, maxval, B(0, tickRow), B(tickThickness, tickRow), PPMD_NULLDRAWPROC, (char *) &axisColor); ppmd_text(pixels, pixcols, pixrows, maxval, B(Sz(-30), tickRow + Sz(5)), Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &axisColor); } static void labelAxes(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, unsigned int const xBias, unsigned int const yBias, pixel const axisColor, bool const upvp) { /*---------------------------------------------------------------------------- Label the axes "x" and "y" or "u" and "v". -----------------------------------------------------------------------------*/ unsigned int const pxcols = pixcols - xBias; unsigned int const pxrows = pixrows - yBias; ppmd_text(pixels, pixcols, pixrows, maxval, B((98 * (pxcols - 1)) / 100 - Sz(11), pxrows + Sz(12)), Sz(10), 0, (upvp ? "u'" : "x"), PPMD_NULLDRAWPROC, (char *) &axisColor); ppmd_text(pixels, pixcols, pixrows, maxval, B(Sz(-22), (2 * (pxrows - 1)) / 100 + Sz(5)), Sz(10), 0, (upvp ? "v'" : "y"), PPMD_NULLDRAWPROC, (char *) &axisColor); } static void drawAxes(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, bool const upvp, unsigned int const xBias, unsigned int const yBias) { /*---------------------------------------------------------------------------- Draw the axes, with tick marks every .1 units and labels. -----------------------------------------------------------------------------*/ pixel axisColor; /* Color of axes and labels */ unsigned int i; PPM_ASSIGN(axisColor, maxval, maxval, maxval); drawYAxis(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor); drawXAxis(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor); for (i = 1; i <= 9; i += 1) { tickX(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, i); tickY(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, i); } labelAxes(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, upvp); } static void plotWhitePoint(pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, const struct colorSystem * const cs, bool const upvp, int const xBias, int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; int wx, wy; pixel rgbcolor; /* Color of the white point mark */ PPM_ASSIGN(rgbcolor, 0, 0, 0); if (upvp) { double wup, wvp; xy_to_upvp(cs->xWhite, cs->yWhite, &wup, &wvp); wx = wup; wy = wvp; wx = (pxcols - 1) * wup; wy = (pxrows - 1) - ((int) ((pxrows - 1) * wvp)); } else { wx = (pxcols - 1) * cs->xWhite; wy = (pxrows - 1) - ((int) ((pxrows - 1) * cs->yWhite)); } PPM_ASSIGN(rgbcolor, 0, 0, 0); /* We draw the four arms of the cross separately so as to leave the pixel representing the precise white point undisturbed. */ ppmd_line(pixels, pixcols, pixrows, maxval, B(wx + Sz(3), wy), B(wx + Sz(10), wy), PPMD_NULLDRAWPROC, (char *) &rgbcolor); ppmd_line(pixels, pixcols, pixrows, maxval, B(wx - Sz(3), wy), B(wx - Sz(10), wy), PPMD_NULLDRAWPROC, (char *) &rgbcolor); ppmd_line(pixels, pixcols, pixrows, maxval, B(wx, wy + Sz(3)), B(wx, wy + Sz(10)), PPMD_NULLDRAWPROC, (char *) &rgbcolor); ppmd_line(pixels, pixcols, pixrows, maxval, B(wx, wy - Sz(3)), B(wx, wy - Sz(10)), PPMD_NULLDRAWPROC, (char *) &rgbcolor); } static void plotBlackBodyCurve(pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, bool const upvp, int const xBias, int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; double t; /* temperature of black body */ pixel rgbcolor; /* color of the curve */ int lx, ly; /* Last point we've drawn on curve so far */ PPM_ASSIGN(rgbcolor, 0, 0, 0); /* Plot black body curve from 1000 to 30000 kelvins. */ for (t = 1000.0; t < 30000.0; t += 50.0) { double lambda, X = 0, Y = 0, Z = 0; int xb, yb; int ix; /* Determine X, Y, and Z for blackbody by summing color match functions over the visual range. */ for (ix = 0, lambda = 380; lambda <= 780.0; ix++, lambda += 5) { double Me; /* Evaluate Planck's black body equation for the power at this wavelength. */ Me = 3.74183e-16 * pow(lambda * 1e-9, -5.0) / (exp(1.4388e-2/(lambda * 1e-9 * t)) - 1.0); X += Me * cie_color_match[ix][0]; Y += Me * cie_color_match[ix][1]; Z += Me * cie_color_match[ix][2]; } if (upvp) { double up, vp; xy_to_upvp(X / (X + Y + Z), Y / (X + Y + Z), &up, &vp); xb = (pxcols - 1) * up; yb = (pxrows - 1) - ((pxrows - 1) * vp); } else { xb = (pxcols - 1) * X / (X + Y + Z); yb = (pxrows - 1) - ((pxrows - 1) * Y / (X + Y + Z)); } if (t > 1000) { ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly), B(xb, yb), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* Draw tick mark every 1000 kelvins */ if ((((int) t) % 1000) == 0) { ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly - Sz(2)), B(lx, ly + Sz(2)), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* Label selected tick marks with decreasing density. */ if (t <= 5000.1 || (t > 5000.0 && ((((int) t) % 5000) == 0) && t != 20000.0)) { char bb[20]; sprintf(bb, "%g", t); ppmd_text(pixels, pixcols, pixrows, maxval, B(lx - Sz(12), ly - Sz(4)), Sz(6), 0, bb, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } } } lx = xb; ly = yb; } } static bool overlappingLegend(bool const upvp, int const waveLength) { bool retval; if (upvp) retval = (waveLength == 430 || waveLength == 640); else retval = (waveLength == 460 || waveLength == 630 || waveLength == 640); return retval; } static void plotMonochromeWavelengths( pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, const struct colorSystem * const cs, bool const upvp, int const xBias, int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; int x; /* The wavelength we're plotting */ for (x = (upvp? 420 : 450); x <= 650; x += (upvp? 10 : (x > 470 && x < 600) ? 5 : 10)) { pixel rgbcolor; /* Ick. Drop legends that overlap and twiddle position so they appear at reasonable positions with respect to the tongue. */ if (!overlappingLegend(upvp, x)) { double cx, cy, cz, jr, jg, jb, jmax; char wl[20]; int bx = 0, by = 0, tx, ty, r, g, b; int icx, icy; /* Location of the color on the tongue */ if (x < 520) { bx = Sz(-22); by = Sz(2); } else if (x < 535) { bx = Sz(-8); by = Sz(-6); } else { bx = Sz(4); } computeMonochromeColorLocation(x, pxcols, pxrows, upvp, &icx, &icy); /* Draw the tick mark */ PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); tx = icx + ((x < 520) ? Sz(-2) : ((x >= 535) ? Sz(2) : 0)); ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2))); ppmd_line(pixels, pixcols, pixrows, maxval, B(icx, icy), B(tx, ty), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* The following flailing about sets the drawing color to the hue corresponding to the pure wavelength (constrained to the display gamut). */ if (upvp) { double up, vp; up = ((double) icx) / (pxcols - 1); vp = 1.0 - ((double) icy) / (pxrows - 1); upvp_to_xy(up, vp, &cx, &cy); cz = 1.0 - (cx + cy); } else { cx = ((double) icx) / (pxcols - 1); cy = 1.0 - ((double) icy) / (pxrows - 1); cz = 1.0 - (cx + cy); } xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb); (void) constrain_rgb(&jr, &jg, &jb); /* Scale to max(rgb) = 1 */ jmax = MAX(jr, MAX(jg, jb)); if (jmax > 0) { jr = jr / jmax; jg = jg / jmax; jb = jb / jmax; } /* gamma correct from linear rgb to nonlinear rgb. */ gamma_correct_rgb(cs, &jr, &jg, &jb); r = maxval * jr; g = maxval * jg; b = maxval * jb; PPM_ASSIGN(rgbcolor, (pixval) r, (pixval) g, (pixval) b); sprintf(wl, "%d", x); ppmd_text(pixels, pixcols, pixrows, maxval, B(icx + bx, icy + by), Sz(6), 0, wl, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } } } static void writeLabel(pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, const struct colorSystem * const cs) { pixel rgbcolor; /* color of text */ char sysdesc[256]; PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); pm_snprintf(sysdesc, sizeof(sysdesc), "System: %s\n" "Primary illuminants (X, Y)\n" " Red: %0.4f, %0.4f\n" " Green: %0.4f, %0.4f\n" " Blue: %0.4f, %0.4f\n" "White point (X, Y): %0.4f, %0.4f", cs->name, cs->xRed, cs->yRed, cs->xGreen, cs->yGreen, cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite); sysdesc[sizeof(sysdesc)-1] = '\0'; /* for robustness */ ppmd_text(pixels, pixcols, pixrows, maxval, pixcols / 3, Sz(24), Sz(12), 0, sysdesc, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } int main(int argc, const char * argv[]) { int argn; const char * const usage = "[-[no]black] [-[no]wpoint] [-[no]label] [-no[axes]] [-full]\n\ [-xy|-upvp] [-rec709|-ntsc|-ebu|-smpte|-hdtv|-cie]\n\ [-red ] [-green ] [-blue ]\n\ [-white ] [-gamma ]\n\ [-size ] [-xsize|-width ] [-ysize|-height ]"; const struct colorSystem *cs; bool widspec = false, hgtspec = false; unsigned int xBias, yBias; bool upvp = false; /* xy or u'v' color coordinates? */ bool showWhite = true; /* Show white point ? */ bool showBlack = true; /* Show black body curve ? */ bool fullChart = false; /* Fill entire tongue ? */ bool showLabel = true; /* Show labels ? */ bool showAxes = true; /* Plot axes ? */ pm_proginit(&argc, argv); argn = 1; cs = &Rec709system; /* default */ while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { if (pm_keymatch(argv[argn], "-xy", 2)) { upvp = false; } else if (pm_keymatch(argv[argn], "-upvp", 1)) { upvp = true; } else if (pm_keymatch(argv[argn], "-xsize", 1) || pm_keymatch(argv[argn], "-width", 2)) { if (widspec) { pm_error("already specified a size/width/xsize"); } argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1)) pm_usage(usage); widspec = true; } else if (pm_keymatch(argv[argn], "-ysize", 1) || pm_keymatch(argv[argn], "-height", 2)) { if (hgtspec) { pm_error("already specified a size/height/ysize"); } argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) pm_usage(usage); hgtspec = true; } else if (pm_keymatch(argv[argn], "-size", 2)) { if (hgtspec || widspec) { pm_error("already specified a size/height/ysize"); } argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) pm_usage(usage); sxsize = sysize; hgtspec = widspec = true; } else if (pm_keymatch(argv[argn], "-rec709", 1)) { cs = &Rec709system; } else if (pm_keymatch(argv[argn], "-ntsc", 1)) { cs = &NTSCsystem; } else if (pm_keymatch(argv[argn], "-ebu", 1)) { cs = &EBUsystem; } else if (pm_keymatch(argv[argn], "-smpte", 2)) { cs = &SMPTEsystem; } else if (pm_keymatch(argv[argn], "-hdtv", 2)) { cs = &HDTVsystem; } else if (pm_keymatch(argv[argn], "-cie", 1)) { cs = &CIEsystem; } else if (pm_keymatch(argv[argn], "-black", 3)) { showBlack = true; /* Show black body curve */ } else if (pm_keymatch(argv[argn], "-wpoint", 2)) { showWhite = true; /* Show white point of color system */ } else if (pm_keymatch(argv[argn], "-noblack", 3)) { showBlack = false; /* Don't show black body curve */ } else if (pm_keymatch(argv[argn], "-nowpoint", 3)) { showWhite = false; /* Don't show white point of system */ } else if (pm_keymatch(argv[argn], "-label", 1)) { showLabel = true; /* Show labels. */ } else if (pm_keymatch(argv[argn], "-nolabel", 3)) { showLabel = false; /* Don't show labels */ } else if (pm_keymatch(argv[argn], "-axes", 1)) { showAxes = true; /* Show axes. */ } else if (pm_keymatch(argv[argn], "-noaxes", 3)) { showAxes = false; /* Don't show axes */ } else if (pm_keymatch(argv[argn], "-full", 1)) { fullChart = true; /* Fill whole tongue full-intensity */ } else if (pm_keymatch(argv[argn], "-gamma", 2)) { cs = &Customsystem; argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.gamma) != 1)) pm_usage(usage); } else if (pm_keymatch(argv[argn], "-red", 1)) { cs = &Customsystem; argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.xRed) != 1)) pm_usage(usage); argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.yRed) != 1)) pm_usage(usage); } else if (pm_keymatch(argv[argn], "-green", 1)) { cs = &Customsystem; argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.xGreen) != 1)) pm_usage(usage); argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.yGreen) != 1)) pm_usage(usage); } else if (pm_keymatch(argv[argn], "-blue", 1)) { cs = &Customsystem; argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.xBlue) != 1)) pm_usage(usage); argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.yBlue) != 1)) pm_usage(usage); } else if (pm_keymatch(argv[argn], "-white", 1)) { cs = &Customsystem; argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.xWhite) != 1)) pm_usage(usage); argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &Customsystem.yWhite) != 1)) pm_usage(usage); } else { pm_usage(usage); } argn++; } if (argn != argc) { /* Extra bogus arguments ? */ pm_usage(usage); } pixcols = sxsize; pixrows = sysize; pixels = ppm_allocarray(pixcols, pixrows); /* Partition into plot area and axes and establish subwindow. */ xBias = Sz(32); yBias = Sz(20); makeAllBlack(pixels, pixcols, pixrows); drawTongueOutline(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); fillInTongue(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias, fullChart); if (showAxes) drawAxes(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); if (showWhite) plotWhitePoint(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias); if (showBlack) plotBlackBodyCurve(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); /* Plot wavelengths around periphery of the tongue. */ if (showAxes) plotMonochromeWavelengths(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias); if (showLabel) writeLabel(pixels, pixcols, pixrows, cieMaxval, cs); ppm_writeppm(stdout, pixels, pixcols, pixrows, cieMaxval, 0); return 0; } advanced/generator/pgmkernel.c0000644000175000001440000001705512347602010015605 0ustar ericusers/* pgmkernel.c - generate a PGM convolution kernel ** ** Creates a PGM image containing a convolution filter with max value = 255 ** and minimum value > 127 that can be used as a smoothing kernel for ** pnmconvol. ** ** Copyright (C) 1992 by Alberto Accomazzi, Smithsonian Astrophysical ** Observatory. ** ** 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 "shhopt.h" #include "mallocvar.h" #include "pgm.h" struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int cols; unsigned int rows; float weight; gray maxval; }; static void parseCommandLine(int argc, const char ** argv, struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry *option_def; /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int weightSpec, maxvalSpec; unsigned int option_def_index; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "weight", OPT_FLOAT, &cmdlineP->weight, &weightSpec, 0); OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!weightSpec) cmdlineP->weight = 6.0; if (cmdlineP->weight < 0.0) pm_error("-weight cannot be negative. You specified %f", cmdlineP->weight); if (!maxvalSpec) cmdlineP->maxval = PGM_MAXMAXVAL; if (cmdlineP->maxval > PGM_OVERALLMAXVAL) pm_error("-maxval is too large: %u. Maximum is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL); if (cmdlineP->maxval == 0) pm_error("-maxval cannot be zero"); if (argc-1 < 1) pm_error("Need at least one argument: size of (square) kernel"); else if (argc-1 == 1) { if (atoi(argv[1]) <= 0) pm_error("Dimension must be a positive number. " "You specified '%s'", argv[1]); cmdlineP->cols = atoi(argv[1]); cmdlineP->rows = atoi(argv[1]); } else if (argc-1 == 2) { if (atoi(argv[1]) <= 0) pm_error("Width must be a positive number. " "You specified '%s'", argv[1]); if (atoi(argv[2]) <= 0) pm_error("Height must be a positive number. " "You specified '%s'", argv[2]); cmdlineP->cols = atoi(argv[1]); cmdlineP->rows = atoi(argv[2]); } else pm_error("At most two arguments allowed. " "You specified %u", argc-1); } static double t(double const dx2, double const dy2, double const weight) { /*---------------------------------------------------------------------------- The t value for a pixel that is (dx, dy) pixels away from the center of the kernel, where 'dx2' is SQR(dx) and 'dy2' is SQR(dy), if the distance is weighted by 'weight'. -----------------------------------------------------------------------------*/ return 1.0 / (1.0 + weight * sqrt(dx2 + dy2)); } static double tMaxAllKernel(unsigned int const cols, unsigned int const rows, double const weight) { /*---------------------------------------------------------------------------- The maximum t value over all pixels in the kernel, if the kernel is 'cols' by 'rows' pixels and distance is weighted by 'weight'. -----------------------------------------------------------------------------*/ /* It depends upon whether there is an even or odd number of rows and columns. If both dimensions are odd, there is a pixel right at the center, and it has the greatest t value. If both dimensions are even, the center of the image is in the center of a 4-pixel square and each of those 4 pixels has the greatest t value. If one dimension is even and the other odd, the center of the kernel is midway between two pixels, horizontally or vertically, and one of those two pixels has the greatest t value. */ double dxMax, dyMax; switch (cols % 2 + rows % 2) { case 0: dxMax = 0.5; dyMax = 0.5; break; case 1: dxMax = 0.5; dyMax = 0.0; break; case 2: dxMax = 0.0; dyMax = 0.0; } return t(SQR(dxMax), SQR(dyMax), weight); } static void writeKernel(FILE * const ofP, unsigned int const cols, unsigned int const rows, gray const maxval, gray ** const halfKernel, unsigned int const halfRows) { unsigned int row; pgm_writepgminit(stdout, cols, rows, maxval, 0); for (row = 0; row < halfRows; ++row) pgm_writepgmrow(stdout, halfKernel[row], cols, maxval, 0); /* Now write out the same rows in reverse order. */ for (; row < rows; ++row) pgm_writepgmrow(stdout, halfKernel[rows-1-row], cols, maxval, 0); } int main(int argc, const char * argv[]) { struct CmdlineInfo cmdline; unsigned int arows; unsigned int arow; double xcenter, ycenter; /* row, column "number" of center of kernel */ double tMax; /* The maximum t value over all pixels */ gray ** halfKernel; /* The upper half of the kernel we generate. The lower half is just the mirror image of this. */ pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); xcenter = ((double) cmdline.cols - 1) / 2.0; ycenter = ((double) cmdline.rows - 1) / 2.0; tMax = tMaxAllKernel(cmdline.cols, cmdline.rows, cmdline.weight); /* Output matrix is symmetric vertically and horizontally. */ arows = (cmdline.rows + 1) / 2; /* Half the number of rows. Add 1 if odd. */ halfKernel = pgm_allocarray(cmdline.cols, arows); for (arow = 0; arow < arows; ++arow) { double const dy2 = SQR(arow - ycenter); unsigned int col; for (col = 0; col < (cmdline.cols +1) / 2; ++col) { double const dx2 = SQR(col - xcenter); double const normalized = t(dx2, dy2, cmdline.weight) / 2 / tMax; gray const grayval = ROUNDU(cmdline.maxval * (0.5 + normalized)); halfKernel[arow][col ] = grayval; halfKernel[arow][cmdline.cols - col - 1] = grayval; } } writeKernel(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, halfKernel, arows); pgm_freearray(halfKernel, arows); return 0; } advanced/generator/ppmcolors.c0000644000175000001440000000475712347602010015644 0ustar ericusers/****************************************************************************** ppmcolors ******************************************************************************* Generate a color map containing all the colors representable with a certain maxval. THIS PROGRAM IS OBSOLETE. USE PAMSEQ INSTEAD. WE KEEP THIS AROUND FOR BACKWARD COMPATIBILITY. ******************************************************************************/ #include "pm_c_util.h" #include "ppm.h" #include "shhopt.h" #include "nstring.h" struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int maxval; }; static void parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optStruct3 opt; /* set by OPTENT3 */ optEntry *option_def = malloc(100*sizeof(optEntry)); unsigned int option_def_index; option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, NULL, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ /* defaults */ cmdlineP->maxval = 5; pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (cmdlineP->maxval < 1) pm_error("-maxval must be at least 1"); else if (cmdlineP->maxval > PPM_OVERALLMAXVAL) pm_error("-maxval too high. The maximum allowable value is %u", PPM_OVERALLMAXVAL); if (argc-1 > 0) pm_error("This program takes no arguments. You specified %d", argc-1); } int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; const char * cmd; /* malloc'ed */ int rc; ppm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); pm_asprintf(&cmd, "pamseq 3 %u -tupletype=RGB | pamtopnm", cmdline.maxval); rc = system(cmd); if (rc != 0) pm_error("pamseq|pamtopnm pipeline failed. system() rc = %d", rc); pm_strfree(cmd); return rc; } advanced/generator/pamstereogram.test0000644000175000001440000000440212347602010017214 0ustar ericusers# Make some input files pamdepth -quiet 255 ../testgrid.pbm >/tmp/testgrid.pgm # Random pattern echo Test 01. Should print 610673698 293: ./pamstereogram -randomseed=1 ../testgrid.pbm | cksum echo Test 02. Should print 610673698 293: ./pamstereogram -randomseed=1 -blackandwhite ../testgrid.pbm | cksum echo Test 03. Should print 3439084201 170: pamseq -tupletype=GRAYSCALE 1 100 | ./pamstereogram -randomseed=1 | cksum echo Test 04. Should print 2484923390 1070: pamgauss 100 10 -maxval=10000 -sigma 20 | pamfunc -multiplier=500 | \ ./pamstereogram -randomseed=1 -dpi=10 | cksum # Makemask echo Test 10. Should print 1266273778 293: ./pamstereogram -randomseed=1 -makemask ../testgrid.pbm | cksum echo Test 11. Should print 3034751595 1070: pamgauss 100 10 -maxval=10000 -sigma 20 | pamfunc -multiplier=500 | \ ./pamstereogram -randomseed=1 -dpi=10 -makemask | cksum # Grayscale echo Test 20. Should print 2468969328 289: ./pamstereogram -randomseed=1 -grayscale ../testgrid.pbm | cksum echo Test 21. Should print 1946982115 4068: pamseq 1 100 | pnmtile 200 20 | \ ./pamstereogram -randomseed=1 -dpi=10 -grayscale | \ cksum echo Test 22. Should print 2078013430 4068: pamseq 1 100 | pnmtile 200 20 | \ ./pamstereogram -randomseed=1 -dpi=10 -grayscale -maxval 255 | \ cksum # Color echo Test 30. Should print 1319392622 731: ./pamstereogram -randomseed=1 -color ../testgrid.pbm | cksum echo Test 31. Should print 389886159 12062: pamseq 1 100 | pnmtile 200 20 | \ ./pamstereogram -randomseed=1 -dpi=10 -color | \ cksum # Pattern file echo Test 40. Should print 1834916830 660: pamgradient black gray50 white gray50 100 50 | \ ./pamstereogram -patfile ../testgrid.pbm -eyesep=.1 -crosseyed | cksum echo Test 41. Should print 4016818756 5014: pamgradient black gray50 white gray50 100 50 | \ ./pamstereogram -patfile /tmp/testgrid.pgm -eyesep=.1 -crosseyed | cksum # drawguides echo Test 51. Should print 2365956562 11071: pamgradient black gray50 white gray50 100 50 | \ ./pamstereogram -randomseed=1 -dpi 10 -guidesize=20 | cksum echo Test 51. Should print 3502025270 1441: pamgradient black gray50 white gray50 100 50 | \ ./pamstereogram -patfile=../testgrid.pbm -dpi 10 -guidesize=20 | cksum # Clean up files we created rm /tmp/testgrid.pgm advanced/generator/ppmforge.c0000644000175000001440000010564512347602010015443 0ustar ericusers/* Fractal forgery generator for the PPM toolkit Originally designed and implemented in December of 1989 by John Walker as a stand-alone program for the Sun and MS-DOS under Turbo C. Adapted in September of 1991 for use with Jef Poskanzer's raster toolkit. References cited in the comments are: Foley, J. D., and Van Dam, A., Fundamentals of Interactive Computer Graphics, Reading, Massachusetts: Addison Wesley, 1984. Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal Images, New York: Springer Verlag, 1988. Press, W. H., Flannery, B. P., Teukolsky, S. A., Vetterling, W. T., Numerical Recipes In C, New Rochelle: Cambridge University Press, 1988. Author: John Walker http://www.fourmilab.ch/ Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, without any conditions or restrictions. This software is provided "as is" without express or implied warranty. */ #define _XOPEN_SOURCE /* get M_PI in math.h */ #include #include #include "pm_c_util.h" #include "ppm.h" #include "mallocvar.h" #include "shhopt.h" static double const hugeVal = 1e50; /* Definitions used to address real and imaginary parts in a two-dimensional array of complex numbers as stored by fourn(). */ #define Real(v, x, y) v[1 + (((x) * meshsize) + (y)) * 2] #define Imag(v, x, y) v[2 + (((x) * meshsize) + (y)) * 2] /* Co-ordinate indices within arrays. */ typedef struct { double x; double y; double z; } vector; /* Definition for obtaining random numbers. */ #define nrand 4 /* Gauss() sample count */ #define Cast(low, high) ((low)+(((high)-(low)) * ((rand() & 0x7FFF) / arand))) /* prototypes */ static void fourn ARGS((float data[], int nn[], int ndim, int isign)); static void initgauss ARGS((unsigned int seed)); static double gauss ARGS((void)); static void spectralsynth ARGS((float **x, unsigned int n, double h)); static void temprgb ARGS((double temp, double *r, double *g, double *b)); static void etoile ARGS((pixel *pix)); /* Local variables */ static double arand, gaussadd, gaussfac; /* Gaussian random parameters */ static double fracdim; /* Fractal dimension */ static double powscale; /* Power law scaling exponent */ static int meshsize = 256; /* FFT mesh size */ static double inclangle, hourangle; /* Star position relative to planet */ static bool inclspec = FALSE; /* No inclination specified yet */ static bool hourspec = FALSE; /* No hour specified yet */ static double icelevel; /* Ice cap theshold */ static double glaciers; /* Glacier level */ static int starfraction; /* Star fraction */ static int starcolor; /* Star color saturation */ struct CmdlineInfo { unsigned int clouds; unsigned int night; float dimension; float hourAngle; unsigned int hourSpec; float inclAngle; unsigned int inclinationSpec; unsigned int meshSize; unsigned int meshSpec; float power; float glaciers; float ice; int saturation; unsigned int seed; int stars; unsigned int starsSpec; unsigned int width; unsigned int height; }; static void parseCommandLine(int argc, const char **argv, struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; unsigned int dimensionSpec, seedSpec, meshSpec, powerSpec, glaciersSpec, iceSpec, saturationSpec, starsSpec, widthSpec, heightSpec; float hour; float inclination; unsigned int mesh; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; OPTENT3(0, "clouds", OPT_FLAG, NULL, &cmdlineP->clouds, 0); OPTENT3(0, "night", OPT_FLAG, NULL, &cmdlineP->night, 0); OPTENT3(0, "dimension", OPT_FLOAT, &cmdlineP->dimension, &dimensionSpec, 0); OPTENT3(0, "hour", OPT_FLOAT, &hour, &cmdlineP->hourSpec, 0); OPTENT3(0, "inclination", OPT_FLOAT, &inclination, &cmdlineP->inclinationSpec, 0); OPTENT3(0, "tilt", OPT_FLOAT, &inclination, &cmdlineP->inclinationSpec, 0); OPTENT3(0, "mesh", OPT_UINT, &mesh, &meshSpec, 0); OPTENT3(0, "power", OPT_FLOAT, &cmdlineP->power, &powerSpec, 0); OPTENT3(0, "glaciers", OPT_FLOAT, &cmdlineP->glaciers, &glaciersSpec, 0); OPTENT3(0, "ice", OPT_FLOAT, &cmdlineP->ice, &iceSpec, 0); OPTENT3(0, "saturation", OPT_INT, &cmdlineP->saturation, &saturationSpec, 0); OPTENT3(0, "seed", OPT_UINT, &cmdlineP->seed, &seedSpec, 0); OPTENT3(0, "stars", OPT_INT, &cmdlineP->stars, &starsSpec, 0); OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, &widthSpec, 0); OPTENT3(0, "xsize", OPT_UINT, &cmdlineP->width, &widthSpec, 0); OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, &heightSpec, 0); OPTENT3(0, "ysize", OPT_UINT, &cmdlineP->height, &heightSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (dimensionSpec) { if (cmdlineP->dimension <= 0.0) pm_error("-dimension must be greater than zero. " "You specified %f", cmdlineP->dimension); } else cmdlineP->dimension = cmdlineP->clouds ? 2.15 : 2.4; if (cmdlineP->hourSpec) cmdlineP->hourAngle = (M_PI / 12.0) * (hour + 12.0); if (cmdlineP->inclinationSpec) cmdlineP->inclAngle = (M_PI / 180.0) * inclination; if (meshSpec) { unsigned int i; if (mesh < 2) pm_error("-mesh value must be at least 2. " "You specified %u", mesh); /* Force FFT mesh to the next larger power of 2. */ for (i = 2; i < mesh; i <<= 1); cmdlineP->meshSize = i; } else cmdlineP->meshSize = 256; if (powerSpec) { if (cmdlineP->power <= 0.0) pm_error("-power must be greater than zero. " "You specified %f", cmdlineP->power); } else cmdlineP->power = cmdlineP->clouds ? 0.75 : 1.2; if (iceSpec) { if (cmdlineP->ice <= 0.0) pm_error("-ice must be greater than zero. " "You specified %f", cmdlineP->ice); } else cmdlineP->ice = 0.4; if (glaciersSpec) { if (cmdlineP->glaciers <= 0.0) pm_error("-glaciers must be greater than 0. " "You specified %f", cmdlineP->glaciers); } else cmdlineP->glaciers = 0.75; if (!starsSpec) cmdlineP->stars = 100; if (!saturationSpec) cmdlineP->saturation = 125; if (!seedSpec) cmdlineP->seed = pm_randseed(); if (!widthSpec) cmdlineP->width = 256; if (!heightSpec) cmdlineP->height = 256; if (argc-1 > 0) pm_error("There are no non-option arguments. " "You specified %u", argc-1); free(option_def); } /* FOURN -- Multi-dimensional fast Fourier transform Called with arguments: data A one-dimensional array of floats (NOTE!!! NOT DOUBLES!!), indexed from one (NOTE!!! NOT ZERO!!), containing pairs of numbers representing the complex valued samples. The Fourier transformed results are returned in the same array. nn An array specifying the edge size in each dimension. THIS ARRAY IS INDEXED FROM ONE, AND ALL THE EDGE SIZES MUST BE POWERS OF TWO!!! ndim Number of dimensions of FFT to perform. Set to 2 for two dimensional FFT. isign If 1, a Fourier transform is done; if -1 the inverse transformation is performed. This function is essentially as given in Press et al., "Numerical Recipes In C", Section 12.11, pp. 467-470. */ static void fourn(data, nn, ndim, isign) float data[]; int nn[], ndim, isign; { register int i1, i2, i3; int i2rev, i3rev, ip1, ip2, ip3, ifp1, ifp2; int ibit, idim, k1, k2, n, nprev, nrem, ntot; float tempi, tempr; double theta, wi, wpi, wpr, wr, wtemp; #define SWAP(a,b) tempr=(a); (a) = (b); (b) = tempr ntot = 1; for (idim = 1; idim <= ndim; idim++) ntot *= nn[idim]; nprev = 1; for (idim = ndim; idim >= 1; idim--) { n = nn[idim]; nrem = ntot / (n * nprev); ip1 = nprev << 1; ip2 = ip1 * n; ip3 = ip2 * nrem; i2rev = 1; for (i2 = 1; i2 <= ip2; i2 += ip1) { if (i2 < i2rev) { for (i1 = i2; i1 <= i2 + ip1 - 2; i1 += 2) { for (i3 = i1; i3 <= ip3; i3 += ip2) { i3rev = i2rev + i3 - i2; SWAP(data[i3], data[i3rev]); SWAP(data[i3 + 1], data[i3rev + 1]); } } } ibit = ip2 >> 1; while (ibit >= ip1 && i2rev > ibit) { i2rev -= ibit; ibit >>= 1; } i2rev += ibit; } ifp1 = ip1; while (ifp1 < ip2) { ifp2 = ifp1 << 1; theta = isign * (M_PI * 2) / (ifp2 / ip1); wtemp = sin(0.5 * theta); wpr = -2.0 * wtemp * wtemp; wpi = sin(theta); wr = 1.0; wi = 0.0; for (i3 = 1; i3 <= ifp1; i3 += ip1) { for (i1 = i3; i1 <= i3 + ip1 - 2; i1 += 2) { for (i2 = i1; i2 <= ip3; i2 += ifp2) { k1 = i2; k2 = k1 + ifp1; tempr = wr * data[k2] - wi * data[k2 + 1]; tempi = wr * data[k2 + 1] + wi * data[k2]; data[k2] = data[k1] - tempr; data[k2 + 1] = data[k1 + 1] - tempi; data[k1] += tempr; data[k1 + 1] += tempi; } } wr = (wtemp = wr) * wpr - wi * wpi + wr; wi = wi * wpr + wtemp * wpi + wi; } ifp1 = ifp2; } nprev *= n; } } #undef SWAP /* INITGAUSS -- Initialize random number generators. As given in Peitgen & Saupe, page 77. */ static void initgauss(seed) unsigned int seed; { /* Range of random generator */ arand = pow(2.0, 15.0) - 1.0; gaussadd = sqrt(3.0 * nrand); gaussfac = 2 * gaussadd / (nrand * arand); srand(seed); } /* GAUSS -- Return a Gaussian random number. As given in Peitgen & Saupe, page 77. */ static double gauss() { int i; double sum = 0.0; for (i = 1; i <= nrand; i++) { sum += (rand() & 0x7FFF); } return gaussfac * sum - gaussadd; } /* SPECTRALSYNTH -- Spectrally synthesized fractal motion in two dimensions. This algorithm is given under the name SpectralSynthesisFM2D on page 108 of Peitgen & Saupe. */ static void spectralsynth(x, n, h) float **x; unsigned int n; double h; { unsigned bl; int i, j, i0, j0, nsize[3]; double rad, phase, rcos, rsin; float *a; bl = ((((unsigned long) n) * n) + 1) * 2 * sizeof(float); a = (float *) calloc(bl, 1); if (a == (float *) 0) { pm_error("Cannot allocate %d x %d result array (% d bytes).", n, n, bl); } *x = a; for (i = 0; i <= n / 2; i++) { for (j = 0; j <= n / 2; j++) { phase = 2 * M_PI * ((rand() & 0x7FFF) / arand); if (i != 0 || j != 0) { rad = pow((double) (i * i + j * j), -(h + 1) / 2) * gauss(); } else { rad = 0; } rcos = rad * cos(phase); rsin = rad * sin(phase); Real(a, i, j) = rcos; Imag(a, i, j) = rsin; i0 = (i == 0) ? 0 : n - i; j0 = (j == 0) ? 0 : n - j; Real(a, i0, j0) = rcos; Imag(a, i0, j0) = - rsin; } } Imag(a, n / 2, 0) = 0; Imag(a, 0, n / 2) = 0; Imag(a, n / 2, n / 2) = 0; for (i = 1; i <= n / 2 - 1; i++) { for (j = 1; j <= n / 2 - 1; j++) { phase = 2 * M_PI * ((rand() & 0x7FFF) / arand); rad = pow((double) (i * i + j * j), -(h + 1) / 2) * gauss(); rcos = rad * cos(phase); rsin = rad * sin(phase); Real(a, i, n - j) = rcos; Imag(a, i, n - j) = rsin; Real(a, n - i, j) = rcos; Imag(a, n - i, j) = - rsin; } } nsize[0] = 0; nsize[1] = nsize[2] = n; /* Dimension of frequency domain array */ fourn(a, nsize, 2, -1); /* Take inverse 2D Fourier transform */ } /* TEMPRGB -- Calculate the relative R, G, and B components for a black body emitting light at a given temperature. The Planck radiation equation is solved directly for the R, G, and B wavelengths defined for the CIE 1931 Standard Colorimetric Observer. The color temperature is specified in degrees Kelvin. */ static void temprgb(temp, r, g, b) double temp; double *r, *g, *b; { double c1 = 3.7403e10, c2 = 14384.0, er, eg, eb, es; /* Lambda is the wavelength in microns: 5500 angstroms is 0.55 microns. */ #define Planck(lambda) ((c1 * pow((double) lambda, -5.0)) / \ (pow(M_E, c2 / (lambda * temp)) - 1)) er = Planck(0.7000); eg = Planck(0.5461); eb = Planck(0.4358); #undef Planck es = 1.0 / MAX(er, MAX(eg, eb)); *r = er * es; *g = eg * es; *b = eb * es; } /* ETOILE -- Set a pixel in the starry sky. */ static void etoile(pix) pixel *pix; { if ((rand() % 1000) < starfraction) { #define StarQuality 0.5 /* Brightness distribution exponent */ #define StarIntensity 8 /* Brightness scale factor */ #define StarTintExp 0.5 /* Tint distribution exponent */ double v = StarIntensity * pow(1 / (1 - Cast(0, 0.9999)), (double) StarQuality), temp, r, g, b; if (v > 255) { v = 255; } /* We make a special case for star color of zero in order to prevent floating point roundoff which would otherwise result in more than 256 star colors. We can guarantee that if you specify no star color, you never get more than 256 shades in the image. */ if (starcolor == 0) { int vi = v; PPM_ASSIGN(*pix, vi, vi, vi); } else { temp = 5500 + starcolor * pow(1 / (1 - Cast(0, 0.9999)), StarTintExp) * ((rand() & 7) ? -1 : 1); /* Constrain temperature to a reasonable value: >= 2600K (S Cephei/R Andromedae), <= 28,000 (Spica). */ temp = MAX(2600, MIN(28000, temp)); temprgb(temp, &r, &g, &b); PPM_ASSIGN(*pix, (int) (r * v + 0.499), (int) (g * v + 0.499), (int) (b * v + 0.499)); } } else { PPM_ASSIGN(*pix, 0, 0, 0); } } static double uprj(unsigned int const a, unsigned int const size) { return (double)a/(size-1); } static double atSat(double const x, double const y, double const dsat) { return x*(1.0-dsat) + y*dsat; } static unsigned char * makeCp(float * const a, unsigned int const n, pixval const maxval) { /* Prescale the grid points into intensities. */ unsigned char * cp; unsigned char * ap; if (UINT_MAX / n < n) pm_error("arithmetic overflow squaring %u", n); cp = malloc(n * n); if (cp == NULL) pm_error("Unable to allocate %u bytes for cp array", n); ap = cp; { unsigned int i; for (i = 0; i < n; i++) { unsigned int j; for (j = 0; j < n; j++) *ap++ = ((double)maxval * (Real(a, i, j) + 1.0)) / 2.0; } } return cp; } static void createPlanetStuff(bool const clouds, float * const a, unsigned int const n, double ** const uP, double ** const u1P, unsigned int ** const bxfP, unsigned int ** const bxcP, unsigned char ** const cpP, vector * const sunvecP, unsigned int const cols, pixval const maxval) { double *u, *u1; unsigned int *bxf, *bxc; unsigned char * cp; double shang, siang; bool flipped; /* Compute incident light direction vector. */ shang = hourspec ? hourangle : Cast(0, 2 * M_PI); siang = inclspec ? inclangle : Cast(-M_PI * 0.12, M_PI * 0.12); sunvecP->x = sin(shang) * cos(siang); sunvecP->y = sin(siang); sunvecP->z = cos(shang) * cos(siang); /* initial value */ /* Allow only 25% of random pictures to be crescents */ if (!hourspec && ((rand() % 100) < 75)) { flipped = (sunvecP->z < 0); sunvecP->z = fabs(sunvecP->z); } else flipped = FALSE; if (!clouds) { pm_message( " -inclination %.0f -hour %d -ice %.2f -glaciers %.2f", (siang * (180.0 / M_PI)), (int) (((shang * (12.0 / M_PI)) + 12 + (flipped ? 12 : 0)) + 0.5) % 24, icelevel, glaciers); pm_message(" -stars %d -saturation %d.", starfraction, starcolor); } cp = makeCp(a, n, maxval); /* Fill the screen from the computed intensity grid by mapping screen points onto the grid, then calculating each pixel value by bilinear interpolation from the surrounding grid points. (N.b. the pictures would undoubtedly look better when generated with small grids if a more well-behaved interpolation were used.) Also compute the line-level interpolation parameters that caller will need every time around his inner loop. */ MALLOCARRAY(u, cols); MALLOCARRAY(u1, cols); MALLOCARRAY(bxf, cols); MALLOCARRAY(bxc, cols); if (u == NULL || u1 == NULL || bxf == NULL || bxc == NULL) pm_error("Cannot allocate %d element interpolation tables.", cols); { unsigned int j; for (j = 0; j < cols; j++) { double const bx = (n - 1) * uprj(j, cols); bxf[j] = floor(bx); bxc[j] = MIN(bxf[j] + 1, n - 1); u[j] = bx - bxf[j]; u1[j] = 1 - u[j]; } } *uP = u; *u1P = u1; *bxfP = bxf; *bxcP = bxc; *cpP = cp; } static void generateStarrySkyRow(pixel * const pixels, unsigned int const cols) { /*---------------------------------------------------------------------------- Generate a starry sky. Note that no FFT is performed; the output is generated directly from a power law mapping of a pseudorandom sequence into intensities. -----------------------------------------------------------------------------*/ unsigned int j; for (j = 0; j < cols; j++) etoile(pixels + j); } static void generateCloudRow(pixel * const pixels, unsigned int const cols, double const t, double const t1, double * const u, double * const u1, unsigned char * const cp, unsigned int * const bxc, unsigned int * const bxf, int const byc, int const byf, pixval const maxval) { /* Render the FFT output as clouds. */ unsigned int col; for (col = 0; col < cols; ++col) { double r; pixval w; r = 0.0; /* initial value */ /* Note that where t1 and t are zero, the cp[] element referenced below does not exist. */ if (t1 > 0.0) r += t1 * u1[col] * cp[byf + bxf[col]] + t1 * u[col] * cp[byf + bxc[col]]; if (t > 0.0) r += t * u1[col] * cp[byc + bxf[col]] + t * u[col] * cp[byc + bxc[col]]; w = (r > 127.0) ? (maxval * ((r - 127.0) / 128.0)) : 0; PPM_ASSIGN(pixels[col], w, w, maxval); } } static void makeLand(int * const irP, int * const igP, int * const ibP, double const r) { /*---------------------------------------------------------------------------- Land area. Look up color based on elevation from precomputed color map table. -----------------------------------------------------------------------------*/ static unsigned char pgnd[][3] = { {206, 205, 0}, {208, 207, 0}, {211, 208, 0}, {214, 208, 0}, {217, 208, 0}, {220, 208, 0}, {222, 207, 0}, {225, 205, 0}, {227, 204, 0}, {229, 202, 0}, {231, 199, 0}, {232, 197, 0}, {233, 194, 0}, {234, 191, 0}, {234, 188, 0}, {233, 185, 0}, {232, 183, 0}, {231, 180, 0}, {229, 178, 0}, {227, 176, 0}, {225, 174, 0}, {223, 172, 0}, {221, 170, 0}, {219, 168, 0}, {216, 166, 0}, {214, 164, 0}, {212, 162, 0}, {210, 161, 0}, {207, 159, 0}, {205, 157, 0}, {203, 156, 0}, {200, 154, 0}, {198, 152, 0}, {195, 151, 0}, {193, 149, 0}, {190, 148, 0}, {188, 147, 0}, {185, 145, 0}, {183, 144, 0}, {180, 143, 0}, {177, 141, 0}, {175, 140, 0}, {172, 139, 0}, {169, 138, 0}, {167, 137, 0}, {164, 136, 0}, {161, 135, 0}, {158, 134, 0}, {156, 133, 0}, {153, 132, 0}, {150, 132, 0}, {147, 131, 0}, {145, 130, 0}, {142, 130, 0}, {139, 129, 0}, {136, 128, 0}, {133, 128, 0}, {130, 127, 0}, {127, 127, 0}, {125, 127, 0}, {122, 127, 0}, {119, 127, 0}, {116, 127, 0}, {113, 127, 0}, {110, 128, 0}, {107, 128, 0}, {104, 128, 0}, {102, 127, 0}, { 99, 126, 0}, { 97, 124, 0}, { 95, 122, 0}, { 93, 120, 0}, { 92, 117, 0}, { 92, 114, 0}, { 92, 111, 0}, { 93, 108, 0}, { 94, 106, 0}, { 96, 104, 0}, { 98, 102, 0}, {100, 100, 0}, {103, 99, 0}, {106, 99, 0}, {109, 99, 0}, {111, 100, 0}, {114, 101, 0}, {117, 102, 0}, {120, 103, 0}, {123, 102, 0}, {125, 102, 0}, {128, 100, 0}, {130, 98, 0}, {132, 96, 0}, {133, 94, 0}, {134, 91, 0}, {134, 88, 0}, {134, 85, 0}, {133, 82, 0}, {131, 80, 0}, {129, 78, 0} }; unsigned int const ix = ((r - 128) * (ARRAY_SIZE(pgnd) - 1)) / 127; *irP = pgnd[ix][0]; *igP = pgnd[ix][1]; *ibP = pgnd[ix][2]; } static void makeWater(int * const irP, int * const igP, int * const ibP, double const r, pixval const maxval) { /* Water. Generate clouds above water based on elevation. */ *irP = *igP = r > 64 ? (r - 64) * 4 : 0; *ibP = maxval; } static void addIce(int * const irP, int * const igP, int * const ibP, double const r, double const azimuth, double const icelevel, double const glaciers, pixval const maxval) { /* Generate polar ice caps. */ double const icet = pow(fabs(sin(azimuth)), 1.0 / icelevel) - 0.5; double const ice = MAX(0.0, (icet + glaciers * MAX(-0.5, (r - 128) / 128.0))); if (ice > 0.125) { *irP = maxval; *igP = maxval; *ibP = maxval; } } static void limbDarken(int * const irP, int * const igP, int * const ibP, unsigned int const col, unsigned int const row, unsigned int const cols, unsigned int const rows, vector const sunvec, pixval const maxval) { /* With Gcc 2.95.3 compiler optimization level > 1, I have seen this function confuse all the variables and ultimately generate a completely black image. Adding an extra reference to 'rows' seems to put things back in order, and the assert() below does that. Take it out, and the problem comes back! 04.02.21. */ /* Apply limb darkening by cosine rule. */ double const atthick = 1.03; double const atSatFac = 1.0; double const athfac = sqrt(atthick * atthick - 1.0); /* Atmosphere thickness as a percentage of planet's diameter */ double const dy = 2 * ((double)rows/2 - row) / rows; double const dysq = dy * dy; /* Note: we are in fact normalizing this horizontal position by the vertical size of the picture. And we know rows >= cols. */ double const dx = 2 * ((double)cols/2 - col) / rows; double const dxsq = dx * dx; double const ds = MIN(1.0, sqrt(dxsq + dysq)); /* Calculate atmospheric absorption based on the thickness of atmosphere traversed by light on its way to the surface. */ double const dsq = ds * ds; double const dsat = atSatFac * ((sqrt(atthick * atthick - dsq) - sqrt(1.0 * 1.0 - dsq)) / athfac); assert(rows >= cols); /* An input requirement */ *irP = atSat(*irP, maxval/2, dsat); *igP = atSat(*igP, maxval/2, dsat); *ibP = atSat(*ibP, maxval, dsat); { double const PlanetAmbient = 0.05; double const sqomdysq = sqrt(1.0 - dysq); double const svx = sunvec.x; double const svy = sunvec.y * dy; double const svz = sunvec.z * sqomdysq; double const di = MAX(0, MIN(1.0, svx * dx + svy + svz * sqrt(1.0 - dxsq))); double const inx = PlanetAmbient * 1.0 + (1.0 - PlanetAmbient) * di; *irP *= inx; *igP *= inx; *ibP *= inx; } } static void generatePlanetRow(pixel * const pixelrow, unsigned int const row, unsigned int const rows, unsigned int const cols, double const t, double const t1, double * const u, double * const u1, unsigned char * const cp, unsigned int * const bxc, unsigned int * const bxf, int const byc, int const byf, vector const sunvec, pixval const maxval) { unsigned int const StarClose = 2; double const azimuth = asin(((((double) row) / (rows - 1)) * 2) - 1); unsigned int const lcos = (rows / 2) * fabs(cos(azimuth)); unsigned int col; for (col = 0; col < cols; ++col) PPM_ASSIGN(pixelrow[col], 0, 0, 0); for (col = cols/2 - lcos; col <= cols/2 + lcos; ++col) { double r; int ir, ig, ib; r = 0.0; /* initial value */ /* Note that where t1 and t are zero, the cp[] element referenced below does not exist. */ if (t1 > 0.0) r += t1 * u1[col] * cp[byf + bxf[col]] + t1 * u[col] * cp[byf + bxc[col]]; if (t > 0.0) r += t * u1[col] * cp[byc + bxf[col]] + t * u[col] * cp[byc + bxc[col]]; if (r >= 128) makeLand(&ir, &ig, &ib, r); else makeWater(&ir, &ig, &ib, r, maxval); addIce(&ir, &ig, &ib, r, azimuth, icelevel, glaciers, maxval); limbDarken(&ir, &ig, &ib, col, row, cols, rows, sunvec, maxval); PPM_ASSIGN(pixelrow[col], ir, ig, ib); } /* Left stars */ for (col = 0; (int)col < (int)(cols/2 - (lcos + StarClose)); ++col) etoile(&pixelrow[col]); /* Right stars */ for (col = cols/2 + (lcos + StarClose); col < cols; ++col) etoile(&pixelrow[col]); } static void genplanet(bool const stars, bool const clouds, float * const a, unsigned int const cols, unsigned int const rows, unsigned int const n, unsigned int const rseed) { /*---------------------------------------------------------------------------- Generate planet from elevation array. If 'stars' is true, a is undefined. Otherwise, it is defined. -----------------------------------------------------------------------------*/ pixval const maxval = PPM_MAXMAXVAL; unsigned char *cp; double *u, *u1; unsigned int *bxf, *bxc; pixel *pixelrow; unsigned int row; vector sunvec; ppm_writeppminit(stdout, cols, rows, maxval, FALSE); if (stars) { pm_message("night: -seed %d -stars %d -saturation %d.", rseed, starfraction, starcolor); cp = NULL; u = NULL; u1 = NULL; bxf = NULL; bxc = NULL; } else { pm_message("%s: -seed %d -dimension %.2f -power %.2f -mesh %d", clouds ? "clouds" : "planet", rseed, fracdim, powscale, meshsize); createPlanetStuff(clouds, a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, cols, maxval); } pixelrow = ppm_allocrow(cols); for (row = 0; row < rows; ++row) { if (stars) generateStarrySkyRow(pixelrow, cols); else { double const by = (n - 1) * uprj(row, rows); int const byf = floor(by) * n; int const byc = byf + n; double const t = by - floor(by); double const t1 = 1 - t; if (clouds) generateCloudRow(pixelrow, cols, t, t1, u, u1, cp, bxc, bxf, byc, byf, maxval); else generatePlanetRow(pixelrow, row, rows, cols, t, t1, u, u1, cp, bxc, bxf, byc, byf, sunvec, maxval); } ppm_writeppmrow(stdout, pixelrow, cols, maxval, FALSE); } pm_close(stdout); ppm_freerow(pixelrow); if (cp) free(cp); if (u) free(u); if (u1) free(u1); if (bxf) free(bxf); if (bxc) free(bxc); } static void applyPowerLawScaling(float * const a, int const meshsize, double const powscale) { /* Apply power law scaling if non-unity scale is requested. */ if (powscale != 1.0) { unsigned int i; for (i = 0; i < meshsize; i++) { unsigned int j; for (j = 0; j < meshsize; j++) { double const r = Real(a, i, j); if (r > 0) Real(a, i, j) = pow(r, powscale); } } } } static void computeExtremeReal(const float * const a, int const meshsize, double * const rminP, double * const rmaxP) { /* Compute extrema for autoscaling. */ double rmin, rmax; unsigned int i; rmin = hugeVal; rmax = -hugeVal; for (i = 0; i < meshsize; i++) { unsigned int j; for (j = 0; j < meshsize; j++) { double r = Real(a, i, j); rmin = MIN(rmin, r); rmax = MAX(rmax, r); } } *rminP = rmin; *rmaxP = rmax; } static void replaceWithSpread(float * const a, int const meshsize) { /*---------------------------------------------------------------------------- Replace the real part of each element of the 'a' array with a measure of how far the real is from the middle; sort of a standard deviation. -----------------------------------------------------------------------------*/ double rmin, rmax; double rmean, rrange; unsigned int i; computeExtremeReal(a, meshsize, &rmin, &rmax); rmean = (rmin + rmax) / 2; rrange = (rmax - rmin) / 2; for (i = 0; i < meshsize; i++) { unsigned int j; for (j = 0; j < meshsize; j++) { Real(a, i, j) = (Real(a, i, j) - rmean) / rrange; } } } static bool planet(unsigned int const cols, unsigned int const rows, bool const stars, bool const clouds, unsigned int const rseed) { /*---------------------------------------------------------------------------- Make a planet. -----------------------------------------------------------------------------*/ float * a; bool error; initgauss(rseed); if (stars) { a = NULL; error = FALSE; } else { spectralsynth(&a, meshsize, 3.0 - fracdim); if (a == NULL) { error = TRUE; } else { applyPowerLawScaling(a, meshsize, powscale); replaceWithSpread(a, meshsize); error = FALSE; } } if (!error) genplanet(stars, clouds, a, cols, rows, meshsize, rseed); if (a != NULL) free(a); return !error; } int main(int argc, const char ** argv) { struct CmdlineInfo cmdline; bool success; unsigned int cols, rows; /* Dimensions of our output image */ pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); fracdim = cmdline.dimension; hourspec = cmdline.hourSpec; hourangle = cmdline.hourAngle; inclspec = cmdline.inclinationSpec; inclangle = cmdline.inclAngle; meshsize = cmdline.meshSize; powscale = cmdline.power; icelevel = cmdline.ice; glaciers = cmdline.glaciers; starfraction = cmdline.stars; starcolor = cmdline.saturation; /* Force screen to be at least as wide as it is high. Long, skinny screens cause crashes because picture width is calculated based on height. */ cols = (MAX(cmdline.height, cmdline.width) + 1) & (~1); rows = cmdline.height; success = planet(cols, rows, cmdline.night, cmdline.clouds, cmdline.seed); exit(success ? 0 : 1); } advanced/generator/pamgradient.c0000644000175000001440000001446512347602010016116 0ustar ericusers#include #include "pm_c_util.h" #include "pam.h" #include "shhopt.h" #include "mallocvar.h" struct cmdlineInfo { tuple colorTopLeft; tuple colorTopRight; tuple colorBottomLeft; tuple colorBottomRight; unsigned int cols; unsigned int rows; unsigned int maxval; }; static void parseCommandLine(int argc, const char **argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int maxvalSpec; unsigned int option_def_index; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!maxvalSpec) cmdlineP->maxval = 255; else { if (cmdlineP->maxval > PAM_OVERALL_MAXVAL) pm_error("The value you specified for -maxval (%u) is too big. " "Max allowed is %u", cmdlineP->maxval, PAM_OVERALL_MAXVAL); if (cmdlineP->maxval < 1) pm_error("You cannot specify 0 for -maxval"); } if (argc-1 != 6) { pm_error("Need 6 arguments: colorTopLeft, colorTopRight, " "colorBottomLeft, colorBottomRight, width, height"); } else { cmdlineP->colorTopLeft = pnm_parsecolor(argv[1], cmdlineP->maxval); cmdlineP->colorTopRight = pnm_parsecolor(argv[2], cmdlineP->maxval); cmdlineP->colorBottomLeft = pnm_parsecolor(argv[3], cmdlineP->maxval); cmdlineP->colorBottomRight = pnm_parsecolor(argv[4], cmdlineP->maxval); cmdlineP->cols = atoi(argv[5]); cmdlineP->rows = atoi(argv[6]); if (cmdlineP->cols <= 0) pm_error("width argument must be a positive number. You " "specified '%s'", argv[5]); if (cmdlineP->rows <= 0) pm_error("height argument must be a positive number. You " "specified '%s'", argv[6]); } } static void freeCmdline(struct cmdlineInfo const cmdline) { pnm_freepamtuple(cmdline.colorTopLeft); pnm_freepamtuple(cmdline.colorTopRight); pnm_freepamtuple(cmdline.colorBottomLeft); pnm_freepamtuple(cmdline.colorBottomRight); } static void interpolate(struct pam * const pamP, tuple * const tuplerow, tuple const first, tuple const last) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) { int const spread = last[plane] - first[plane]; int col; if (INT_MAX / pamP->width < abs(spread)) pm_error("Arithmetic overflow. You must reduce the width of " "the image (now %u) or the range of color values " "(%u in plane %u) so that their " "product is less than %d", pamP->width, abs(spread), plane, INT_MAX); for (col = 0; col < pamP->width; ++col) tuplerow[col][plane] = first[plane] + (spread * col / (int)pamP->width); } } static int isgray(tuple const color) { return (color[PAM_RED_PLANE] == color[PAM_GRN_PLANE]) && (color[PAM_RED_PLANE] == color[PAM_BLU_PLANE]); } static tuple * createEdge(const struct pam * const pamP, tuple const topColor, tuple const bottomColor) { /*---------------------------------------------------------------------------- Create a left or right edge, interpolating from top to bottom. -----------------------------------------------------------------------------*/ struct pam interpPam; tuple * tupleRow; interpPam = *pamP; /* initial value */ interpPam.width = pamP->height; interpPam.height = 1; tupleRow = pnm_allocpamrow(&interpPam); interpolate(&interpPam, tupleRow, topColor, bottomColor); return tupleRow; } int main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; struct pam pam; tuple * tupleRow; tuple * leftEdge; tuple * rightEdge; unsigned int row; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); pam.size = sizeof pam; pam.len = PAM_STRUCT_SIZE(tuple_type); pam.file = stdout; pam.plainformat = 0; pam.width = cmdline.cols; pam.height = cmdline.rows; pam.maxval = cmdline.maxval; pam.bytes_per_sample = pnm_bytespersample(pam.maxval); pam.format = PAM_FORMAT; if (isgray(cmdline.colorTopLeft) && isgray(cmdline.colorTopRight) && isgray(cmdline.colorBottomLeft) && isgray(cmdline.colorBottomRight)) { pam.depth = 1; strcpy(pam.tuple_type, PAM_PGM_TUPLETYPE); } else { pam.depth = 3; strcpy(pam.tuple_type, PAM_PPM_TUPLETYPE); } pnm_writepaminit(&pam); tupleRow = pnm_allocpamrow(&pam); leftEdge = createEdge(&pam, cmdline.colorTopLeft, cmdline.colorBottomLeft); rightEdge = createEdge(&pam, cmdline.colorTopRight, cmdline.colorBottomRight); /* interpolate each row between the left edge and the right edge */ for (row = 0; row < pam.height; ++row) { interpolate(&pam, tupleRow, leftEdge[row], rightEdge[row]); pnm_writepamrow(&pam, tupleRow); } pm_close(stdout); pnm_freepamrow(rightEdge); pnm_freepamrow(leftEdge); pnm_freepamrow(tupleRow); freeCmdline(cmdline); return 0; } advanced/generator/pbmtext.c0000644000175000001440000007310012347602010015275 0ustar ericusers/* pbmtext.c - render text into a bitmap ** ** 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. */ #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 "pm_c_util.h" #include "mallocvar.h" #include "shhopt.h" #include "pbm.h" #include "pbmfont.h" struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * text; /* text from command line or NULL if none */ const char * font; /* -font option value or NULL if none */ const char * builtin; /* -builtin option value or NULL if none */ unsigned int dump; /* undocumented dump option for installing a new built-in font */ float space; /* -space option value or default */ unsigned int width; /* -width option value or zero */ int lspace; /* lspace option value or default */ unsigned int nomargins; /* -nomargins */ unsigned int verbose; }; static void parseCommandLine(int argc, const char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "font", OPT_STRING, &cmdlineP->font, NULL, 0); OPTENT3(0, "builtin", OPT_STRING, &cmdlineP->builtin, NULL, 0); OPTENT3(0, "dump", OPT_FLAG, NULL, &cmdlineP->dump, 0); OPTENT3(0, "space", OPT_FLOAT, &cmdlineP->space, NULL, 0); OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, NULL, 0); OPTENT3(0, "lspace", OPT_INT, &cmdlineP->lspace, NULL, 0); OPTENT3(0, "nomargins", OPT_FLAG, NULL, &cmdlineP->nomargins, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); /* Set the defaults */ cmdlineP->font = NULL; cmdlineP->builtin = NULL; cmdlineP->space = 0.0; cmdlineP->width = 0; cmdlineP->lspace = 0; opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (argc-1 == 0) cmdlineP->text = NULL; else { char *text; int i; int totaltextsize; totaltextsize = 1; /* initial value */ text = malloc(totaltextsize); /* initial allocation */ text[0] = '\0'; for (i = 1; i < argc; ++i) { if (i > 1) { totaltextsize += 1; text = realloc(text, totaltextsize); if (text == NULL) pm_error("out of memory allocating space for input text"); strcat(text, " "); } totaltextsize += strlen(argv[i]); text = realloc(text, totaltextsize); if (text == NULL) pm_error("out of memory allocating space for input text"); strcat(text, argv[i]); } cmdlineP->text = text; } } static void reportFont(struct font * const fontP) { unsigned int n; unsigned int c; pm_message("FONT:"); pm_message(" character dimensions: %uw x %uh", fontP->maxwidth, fontP->maxheight); pm_message(" Additional vert white space: %d pixels", fontP->y); for (c = 0, n = 0; c < ARRAY_SIZE(fontP->glyph); ++c) if (fontP->glyph[c]) ++n; pm_message(" # characters: %u", n); } static void computeFont(struct cmdlineInfo const cmdline, struct font ** const fontPP) { struct font * fontP; if (cmdline.font) fontP = pbm_loadfont(cmdline.font); else { if (cmdline.builtin) fontP = pbm_defaultfont(cmdline.builtin); else fontP = pbm_defaultfont("bdf"); } if (cmdline.verbose) reportFont(fontP); if (cmdline.dump) { pbm_dumpfont(fontP); exit(0); } *fontPP = fontP; } struct text { char ** textArray; /* malloc'ed */ unsigned int allocatedLineCount; unsigned int lineCount; }; static void allocTextArray(struct text * const textP, unsigned int const maxLineCount, unsigned int const maxColumnCount) { unsigned int line; textP->allocatedLineCount = maxLineCount; MALLOCARRAY_NOFAIL(textP->textArray, maxLineCount); for (line = 0; line < maxLineCount; ++line) MALLOCARRAY_NOFAIL(textP->textArray[line], maxColumnCount+1); textP->lineCount = 0; } static void freeTextArray(struct text const text) { unsigned int line; for (line = 0; line < text.allocatedLineCount; ++line) free((char **)text.textArray[line]); free(text.textArray); } static void fixControlChars(const char * const input, struct font * const fontP, const char ** const outputP) { /*---------------------------------------------------------------------------- Return a translation of input[] that can be rendered as glyphs in the font 'fontP'. Return it as newly malloced *outputP. Expand tabs to spaces. Remove any trailing newline. (But leave intermediate ones as line delimiters). Turn anything that isn't a code point in the font to a single space (which isn't guaranteed to be in the font either, of course). -----------------------------------------------------------------------------*/ /* We don't know in advance how big the output will be because of the tab expansions. So we make sure before processing each input character that there is space in the output buffer for a worst case tab expansion, plus a terminating NUL, reallocating as necessary. And we originally allocate enough for the entire line assuming no tabs. */ unsigned int const tabSize = 8; unsigned int inCursor, outCursor; char * output; /* Output buffer. Malloced */ size_t outputSize; /* Currently allocated size of 'output' */ outputSize = strlen(input) + 1 + tabSize; /* Leave room for one worst case tab expansion and NUL terminator */ MALLOCARRAY(output, outputSize); if (output == NULL) pm_error("Couldn't allocate %u bytes for a line of text.", (unsigned)outputSize); for (inCursor = 0, outCursor = 0; input[inCursor] != '\0'; ++inCursor) { if (outCursor + 1 + tabSize > outputSize) { outputSize = outCursor + 1 + 4 * tabSize; REALLOCARRAY(output, outputSize); if (output == NULL) pm_error("Couldn't allocate %u bytes for a line of text.", (unsigned)outputSize); } if (input[inCursor] == '\n' && input[inCursor+1] == '\0') { /* This is a terminating newline. We don't do those. */ } else if (input[inCursor] == '\t') { /* Expand this tab into the right number of spaces. */ unsigned int const nextTabStop = (outCursor + tabSize) / tabSize * tabSize; while (outCursor < nextTabStop) output[outCursor++] = ' '; } else if (!fontP->glyph[(unsigned char)input[inCursor]]) { /* Turn this unknown char into a single space. */ output[outCursor++] = ' '; } else output[outCursor++] = input[inCursor]; assert(outCursor <= outputSize); } output[outCursor++] = '\0'; assert(outCursor <= outputSize); *outputP = output; } static void fill_rect(bit** const bits, int const row0, int const col0, int const height, int const width, bit const color) { int row; for (row = row0; row < row0 + height; ++row) { int col; for (col = col0; col < col0 + width; ++col) bits[row][col] = color; } } static void get_line_dimensions(const char line[], const struct font * const font_p, const float intercharacter_space, double * const bwidP, int * const backup_space_needed_p) { /*---------------------------------------------------------------------------- Determine the width in pixels of the line of text line[] in the font *font_p, and return it as *bwidP. Also determine how much of this width goes to the left of the nominal starting point of the line because the first character in the line has a "backup" distance. Return that as *backup_space_needed_p. -----------------------------------------------------------------------------*/ int cursor; /* cursor into the line of text */ double accumulatedIcs; /* accumulated intercharacter space so far in the line we are stepping through. Because the intercharacter space might not be an integer, we accumulate it here and realize full pixels whenever we have more than one pixel. Note that this can be negative (which means were crowding, rather than spreading, text). */ double bwid; bool no_chars_yet; /* We haven't seen any renderable characters yet in the line. */ struct glyph * lastGlyphP; /* Glyph of last character processed so far. Undefined if 'no_chars_yet'. */ no_chars_yet = TRUE; /* initial value */ accumulatedIcs = 0.0; /* initial value */ bwid = 0.0; /* initial value */ for (cursor = 0; line[cursor] != '\0'; cursor++) { struct glyph * const glyphP = font_p->glyph[(unsigned char)line[cursor]]; if (glyphP) { if (no_chars_yet) { no_chars_yet = FALSE; if (glyphP->x < 0) *backup_space_needed_p = -glyphP->x; else { *backup_space_needed_p = 0; bwid += glyphP->x; } } else { /* handle extra intercharacter space (-space option) */ accumulatedIcs += intercharacter_space; if (accumulatedIcs >= INT_MAX) pm_error("Image width too large."); if (accumulatedIcs <= INT_MIN) pm_error("Absurdly large negative -space value."); { int const fullPixels = (int) accumulatedIcs; bwid += fullPixels; accumulatedIcs -= fullPixels; } } lastGlyphP = glyphP; bwid += glyphP->xadd; } } if (no_chars_yet) /* Line has no renderable characters */ *backup_space_needed_p = 0; else { /* Line has at least one renderable character. Recalculate width of last character in line so it ends right at the right edge of the glyph (no extra space to anticipate another character). */ bwid -= lastGlyphP->xadd; bwid += lastGlyphP->width + lastGlyphP->x; } if (bwid > INT_MAX) pm_error("Image width too large."); else *bwidP = bwid; } static void insert_character(const struct glyph * const glyph, int const toprow, int const leftcol, bit ** const bits) { /*---------------------------------------------------------------------------- Insert one character (whose glyph is 'glyph') into the image bits[]. Its top left corner shall be row 'toprow', column 'leftcol'. -----------------------------------------------------------------------------*/ int glyph_y, glyph_x; /* position within the glyph */ for (glyph_y = 0; glyph_y < glyph->height; glyph_y++) { for (glyph_x = 0; glyph_x < glyph->width; glyph_x++) { if (glyph->bmap[glyph_y * glyph->width + glyph_x]) bits[toprow+glyph_y][leftcol+glyph->x+glyph_x] = PBM_BLACK; } } } static void insert_characters(bit ** const bits, struct text const lp, struct font * const fontP, int const topmargin, int const leftmargin, float const intercharacter_space, int const lspace) { /*---------------------------------------------------------------------------- Render the text 'lp' into the image 'bits' using font *fontP and putting 'intercharacter_space' pixels between characters and 'lspace' pixels between the lines. -----------------------------------------------------------------------------*/ int line; /* Line number in input text */ for (line = 0; line < lp.lineCount; ++line) { int row; /* row in image of top of current typeline */ int leftcol; /* Column in image of left edge of current glyph */ int cursor; /* cursor into a line of input text */ float accumulated_ics; /* accumulated intercharacter space so far in the line we are building. Because the intercharacter space might not be an integer, we accumulate it here and realize full pixels whenever we have more than one pixel. */ row = topmargin + line * (fontP->maxheight + lspace); leftcol = leftmargin; accumulated_ics = 0.0; /* initial value */ for (cursor = 0; lp.textArray[line][cursor] != '\0'; ++cursor) { unsigned int const glyphIndex = (unsigned char)lp.textArray[line][cursor]; struct glyph* glyph; /* the glyph for this character */ glyph = fontP->glyph[glyphIndex]; if (glyph != NULL) { const int toprow = row + fontP->maxheight + fontP->y - glyph->height - glyph->y; /* row number in image of top row in glyph */ insert_character(glyph, toprow, leftcol, bits); leftcol += glyph->xadd; { /* handle extra intercharacter space (-space option) */ int full_pixels; /* integer part of accumulated_ics */ accumulated_ics += intercharacter_space; full_pixels = (int) accumulated_ics; if (full_pixels > 0) { leftcol += full_pixels; accumulated_ics -= full_pixels; } } } } } } struct outputTextCursor { struct text text; /* The output text. The lineCount field of this represents the number of lines we have completed. The line after that is the one we are currently filling. */ unsigned int maxWidth; /* A line of output can't be wider than this many pixels */ float intercharacterSpace; /* The amount of extra space, in characters, that should be added between every two characters (Pbmtext -space option) */ unsigned int columnNo; /* The column Number (starting at 0) in the current line that we are filling where the next character goes. */ bool noCharsYet; /* We haven't put any renderable characters yet in the output line. */ unsigned int widthSoFar; /* The accumulated width, in pixels, of all the characters now in the current output line */ float accumulatedIcs; /* accumulated intercharacter space so far in the line we are stepping through. Because the intercharacter space might not be an integer, we accumulate it here and realize full pixels whenever we have more than one pixel. Note that this is negative if we're crowding, rather than spreading, characters. */ }; static void initializeFlowedOutputLine(struct outputTextCursor * const cursorP) { cursorP->columnNo = 0; cursorP->noCharsYet = TRUE; cursorP->widthSoFar = 0.0; cursorP->accumulatedIcs = 0.0; } static void initializeFlowedOutput(struct outputTextCursor * const cursorP, unsigned int const maxLines, unsigned int const maxWidth, float const intercharacterSpace) { allocTextArray(&cursorP->text, maxLines, maxWidth); cursorP->maxWidth = maxWidth; cursorP->intercharacterSpace = intercharacterSpace; initializeFlowedOutputLine(cursorP); } static void finishOutputLine(struct outputTextCursor * const cursorP) { if (cursorP->text.lineCount < cursorP->text.allocatedLineCount) { char * const currentLine = cursorP->text.textArray[cursorP->text.lineCount]; currentLine[cursorP->columnNo++] = '\0'; ++cursorP->text.lineCount; } } static void placeCharacterInOutput(char const lastch, struct font * const fontP, struct outputTextCursor * const cursorP) { /*---------------------------------------------------------------------------- Place a character of text in the text array at the position indicated by *cursorP, keeping track of what space this character will occupy when this text array is ultimately rendered using font *fontP. Note that while we compute how much space the character will take when rendered, we don't render it. -----------------------------------------------------------------------------*/ if (cursorP->text.lineCount < cursorP->text.allocatedLineCount) { unsigned int const glyphIndex = (unsigned char)lastch; if (fontP->glyph[glyphIndex]) { if (cursorP->noCharsYet) { cursorP->noCharsYet = FALSE; if (fontP->glyph[glyphIndex]->x > 0) cursorP->widthSoFar += fontP->glyph[glyphIndex]->x; } else { /* handle extra intercharacter space (-space option) */ cursorP->accumulatedIcs += cursorP->intercharacterSpace; { int const fullPixels = (int)cursorP->accumulatedIcs; cursorP->widthSoFar += fullPixels; cursorP->accumulatedIcs -= fullPixels; } } cursorP->widthSoFar += fontP->glyph[glyphIndex]->xadd; } if (cursorP->widthSoFar < cursorP->maxWidth) { char * const currentLine = cursorP->text.textArray[cursorP->text.lineCount]; currentLine[cursorP->columnNo++] = lastch; } else { /* Line is full; finish it off, start the next one, and place the character there. */ /* TODO: We really should back up to the previous white space character and move the rest of the line to the next line */ finishOutputLine(cursorP); initializeFlowedOutputLine(cursorP); placeCharacterInOutput(lastch, fontP, cursorP); } } } static void flowText(struct text const inputText, int const width, struct font * const fontP, float const intercharacterSpace, struct text * const outputTextP) { unsigned int const maxLineCount = 50; unsigned int inputLine; /* Input line number on which we are currently working */ struct outputTextCursor outputCursor; for (inputLine = 0; inputLine < inputText.lineCount; ++inputLine) { unsigned int incursor; /* cursor into the line we are reading */ initializeFlowedOutput(&outputCursor, maxLineCount, width, intercharacterSpace); for (incursor = 0; inputText.textArray[inputLine][incursor] != '\0'; ++incursor) placeCharacterInOutput(inputText.textArray[inputLine][incursor], fontP, &outputCursor); finishOutputLine(&outputCursor); } *outputTextP = outputCursor.text; } static void truncateText(struct text const inputText, unsigned int const width, struct font * const fontP, float const intercharacterSpace, struct text * const outputTextP) { struct text truncatedText; int line; /* Line number on which we are currently working */ allocTextArray(&truncatedText, inputText.lineCount, width); for (line = 0; line < inputText.lineCount; ++line){ int cursor; /* cursor into the line of text */ unsigned char lastch; /* line[cursor] */ int widthSoFar; /* How long the line we've built, in pixels, is so far */ float accumulatedIcs; /* accumulated intercharacter space so far in the line we are stepping through. Because the intercharacter space might not be an integer, we accumulate it here and realize full pixels whenever we have more than one pixel. Note that this is negative if we're crowding, not spreading, characters. */ int noCharsYet; /* logical: we haven't seen any renderable characters yet in the line. */ noCharsYet = TRUE; /* initial value */ widthSoFar = 0; /* initial value */ accumulatedIcs = 0.0; /* initial value */ truncatedText.textArray[line][0] = '\0'; /* Start with empty line */ for (cursor = 0; inputText.textArray[line][cursor] != '\0' && widthSoFar < width; cursor++) { lastch = inputText.textArray[line][cursor]; if (fontP->glyph[(unsigned char)lastch]) { if (noCharsYet) { noCharsYet = FALSE; if (fontP->glyph[lastch]->x > 0) widthSoFar += fontP->glyph[lastch]->x; } else { /* handle extra intercharacter space (-space option) */ accumulatedIcs += intercharacterSpace; { int const fullPixels = (int) intercharacterSpace; widthSoFar += fullPixels; accumulatedIcs -= fullPixels; } } widthSoFar += fontP->glyph[lastch]->xadd; } if (widthSoFar < width) { truncatedText.textArray[line][cursor] = inputText.textArray[line][cursor]; truncatedText.textArray[line][cursor+1] = '\0'; } } } truncatedText.lineCount = inputText.lineCount; *outputTextP = truncatedText; } static void getText(const char cmdline_text[], struct font * const fontP, struct text * const input_textP) { struct text input_text; if (cmdline_text) { MALLOCARRAY_NOFAIL(input_text.textArray, 1); input_text.allocatedLineCount = 1; input_text.lineCount = 1; fixControlChars(cmdline_text, fontP, (const char**)&input_text.textArray[0]); } else { /* Read text from stdin. */ unsigned int maxlines; /* Maximum number of lines for which we presently have space in the text array */ char buf[5000]; char ** text_array; unsigned int lineCount; maxlines = 50; /* initial value */ MALLOCARRAY_NOFAIL(text_array, maxlines); lineCount = 0; /* initial value */ while (fgets(buf, sizeof(buf), stdin) != NULL) { if (strlen(buf) + 1 >= sizeof(buf)) pm_error("A line of input text is longer than %u characters." "Cannot process.", (unsigned)sizeof(buf)-1); if (lineCount >= maxlines) { maxlines *= 2; REALLOCARRAY(text_array, maxlines); if (text_array == NULL) pm_error("out of memory"); } fixControlChars(buf, fontP, (const char **)&text_array[lineCount]); if (text_array[lineCount] == NULL) pm_error("out of memory"); ++lineCount; } input_text.textArray = text_array; input_text.lineCount = lineCount; input_text.allocatedLineCount = lineCount; } *input_textP = input_text; } static void computeImageHeight(struct text const formattedText, const struct font * const fontP, int const interlineSpace, unsigned int const vmargin, unsigned int * const rowsP) { if (interlineSpace < 0 && fontP->maxheight < -interlineSpace) pm_error("-lspace value (%d) negative and exceeds font height.", interlineSpace); else { double const rowsD = 2 * (double) vmargin + (double) formattedText.lineCount * fontP->maxheight + (double) (formattedText.lineCount-1) * interlineSpace; if (rowsD > INT_MAX-10) pm_error("Image height too large."); else *rowsP = (unsigned int) rowsD; } } static void computeImageWidth(struct text const formattedText, const struct font * const fontP, float const intercharacterSpace, unsigned int const hmargin, unsigned int * const colsP, int * const maxleftbP) { if (intercharacterSpace < 0 && fontP->maxwidth < -intercharacterSpace) pm_error("-space value (%f) negative; exceeds font width.", intercharacterSpace); else { /* Find the widest line, and the one that backs up the most past the nominal start of the line. */ unsigned int line; double maxwidth; int maxleftb; double colsD; for (line = 0, maxwidth = 0.0, maxleftb = 0; line < formattedText.lineCount; ++line) { double bwid; int backupSpaceNeeded; get_line_dimensions(formattedText.textArray[line], fontP, intercharacterSpace, &bwid, &backupSpaceNeeded); maxwidth = MAX(maxwidth, bwid); maxleftb = MAX(maxleftb, backupSpaceNeeded); } colsD = 2 * (double) hmargin + (double) maxwidth; if (colsD > INT_MAX-10) pm_error("Image width too large."); else *colsP = (unsigned int) colsD; *maxleftbP = maxleftb; } } int main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; bit ** bits; unsigned int rows, cols; struct font * fontP; unsigned int vmargin, hmargin; struct text inputText; struct text formattedText; int maxleftb; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); computeFont(cmdline, &fontP); getText(cmdline.text, fontP, &inputText); if (cmdline.nomargins) { vmargin = 0; hmargin = 0; } else { if (inputText.lineCount == 1) { vmargin = fontP->maxheight / 2; hmargin = fontP->maxwidth; } else { vmargin = fontP->maxheight; hmargin = 2 * fontP->maxwidth; } } if (cmdline.width > 0) { if (cmdline.width > INT_MAX -10) pm_error("-width value too large: %u", cmdline.width); /* Flow or truncate lines to meet user's width request */ if (inputText.lineCount == 1) flowText(inputText, cmdline.width, fontP, cmdline.space, &formattedText); else truncateText(inputText, cmdline.width, fontP, cmdline.space, &formattedText); freeTextArray(inputText); } else formattedText = inputText; if (formattedText.lineCount == 0) pm_error("No input text."); computeImageHeight(formattedText, fontP, cmdline.lspace, vmargin, &rows); computeImageWidth(formattedText, fontP, cmdline.space, hmargin, &cols, &maxleftb); if (cols == 0 || rows == 0) pm_error("Input is all whitespace and/or non-renderable characters."); bits = pbm_allocarray(cols, rows); /* Fill background with white */ fill_rect(bits, 0, 0, rows, cols, PBM_WHITE); /* Put the text in */ insert_characters(bits, formattedText, fontP, vmargin, hmargin + maxleftb, cmdline.space, cmdline.lspace); pbm_writepbm(stdout, bits, cols, rows, 0); pbm_freearray(bits, rows); freeTextArray(formattedText); pm_close(stdout); return 0; } advanced/generator/pgmcrater.c0000644000175000001440000003045712347602010015606 0ustar ericusers/* Fractal cratering Designed and implemented in November of 1989 by: John Walker Autodesk SA Avenue des Champs-Montants 14b CH-2074 MARIN Switzerland Usenet: kelvin@Autodesk.com Fax: 038/33 88 15 Voice: 038/33 76 33 The algorithm used to determine crater size is as described on pages 31 and 32 of: Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal Images, New York: Springer Verlag, 1988. The mathematical technique used to calculate crater radii that obey the proper area law distribution from a uniformly distributed pseudorandom sequence was developed by Rudy Rucker. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, without any conditions or restrictions. This software is provided "as is" without express or implied warranty. PLUGWARE! If you like this kind of stuff, you may also enjoy "James Gleick's Chaos--The Software" for MS-DOS, available for $59.95 from your local software store or directly from Autodesk, Inc., Attn: Science Series, 2320 Marinship Way, Sausalito, CA 94965, USA. Telephone: (800) 688-2344 toll-free or, outside the U.S. (415) 332-2344 Ext 4886. Fax: (415) 289-4718. "Chaos--The Software" includes a more comprehensive fractal forgery generator which creates three-dimensional landscapes as well as clouds and planets, plus five more modules which explore other aspects of Chaos. The user guide of more than 200 pages includes an introduction by James Gleick and detailed explanations by Rudy Rucker of the mathematics and algorithms used by each program. */ /* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at right * edge. Make craters wrap around the image (enables tiling of image). */ #define _XOPEN_SOURCE /* get M_PI in math.h */ #include #include #include "pm_c_util.h" #include "pgm.h" #include "mallocvar.h" #include "shhopt.h" struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int number; unsigned int height; unsigned int width; float gamma; unsigned int randomseed; unsigned int randomseedSpec; }; static void parseCommandLine(int argc, const char ** const argv, struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; unsigned int numberSpec, heightSpec, widthSpec, gammaSpec; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "number", OPT_UINT, &cmdlineP->number, &numberSpec, 0); OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, &heightSpec, 0); OPTENT3(0, "ysize", OPT_UINT, &cmdlineP->height, &heightSpec, 0); OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, &widthSpec, 0); OPTENT3(0, "xsize", OPT_UINT, &cmdlineP->width, &widthSpec, 0); OPTENT3(0, "gamma", OPT_FLOAT, &cmdlineP->gamma, &gammaSpec, 0); OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (argc-1 > 0) pm_error("There are no non-option arguments. You specified %u", argc-1); if (!numberSpec) cmdlineP->number = 50000; if (cmdlineP->number == 0) pm_error("-number must be positive"); if (!heightSpec) cmdlineP->height = 256; if (cmdlineP->height == 0) pm_error("-height must be positive"); if (!widthSpec) cmdlineP->width = 256; if (cmdlineP->width == 0) pm_error("-width must be positive"); if (!gammaSpec) cmdlineP->gamma = 1.0; if (cmdlineP->gamma <= 0.0) pm_error("gamma correction must be greater than 0"); free(option_def); } /* Definitions for obtaining random numbers. */ /* Display parameters */ #define SCRX screenxsize /* Screen width */ #define SCRY screenysize /* Screen height */ #define SCRGAMMA 1.0 /* Display gamma */ #define RGBQuant 255 static double const ImageGamma = 0.5; /* Inherent gamma of mapped image */ static double const arand = 32767.0; /* Random number parameters */ static double const CdepthPower = 1.5; /* Crater depth power factor */ static double DepthBias; /* sqrt(.5) */ static int const slopemin = -52; static int const slopemax = 52; static double const Cast(double const low, double const high) { return low + (high - low) * ((rand() & 0x7FFF) / arand); } static int modulo(int const t, int const n) { int m; assert(n > 0); m = t % n; while (m < 0) { m += n; } return m; } #define Auxadr(x, y) &aux[modulo(y, screenysize)*screenxsize+modulo(x, screenxsize)] static void generateScreenImage(const unsigned short * const aux, unsigned int const screenxsize, unsigned int const screenysize, unsigned char * const slopemap) { unsigned int row; gray * pixrow; pgm_writepgminit(stdout, screenxsize, screenysize, RGBQuant, FALSE); pixrow = pgm_allocrow(screenxsize); for (row = 0; row < screenysize; ++row) { unsigned int col; for (col = 0; col < screenxsize; ++col) { int j; j = *Auxadr(col+1, row) - *Auxadr(col, row); j = MIN(MAX(slopemin, j), slopemax); pixrow[col] = slopemap[j - slopemin]; } pgm_writepgmrow(stdout, pixrow, screenxsize, RGBQuant, FALSE); } pm_close(stdout); pgm_freerow(pixrow); } static void gencraters(struct CmdlineInfo const cmdline) { /*---------------------------------------------------------------------------- Generate cratered terrain -----------------------------------------------------------------------------*/ unsigned int const screenxsize = cmdline.width; /* screen X size */ unsigned int const screenysize = cmdline.height; /* screen Y size */ double const dgamma = cmdline.gamma; /* display gamma */ unsigned int const ncraters = cmdline.number; /* num craters to gen */ int i, j; unsigned int l; unsigned short * aux; unsigned char * slopemap; /* Slope to pixel map */ /* Acquire the elevation array and initialize it to mean surface elevation. */ MALLOCARRAY(aux, SCRX * SCRY); if (aux == NULL) pm_error("out of memory allocating elevation array"); /* Acquire the elevation buffer and initialize to mean initial elevation. */ for (i = 0; i < SCRY; i++) { unsigned short *zax = aux + (((long) SCRX) * i); for (j = 0; j < SCRX; j++) { *zax++ = 32767; } } /* Every time we go around this loop we plop another crater on the surface. */ for (l = 0; l < ncraters; l++) { double g; int cx = Cast(0.0, ((double) SCRX - 1)), cy = Cast(0.0, ((double) SCRY - 1)), gx, gy, x, y; unsigned int amptot = 0, axelev; unsigned int npatch = 0; /* Phase 1. Compute the mean elevation of the impact area. We assume the impact area is a fraction of the total crater size. */ /* Thanks, Rudy, for this equation that maps the uniformly distributed numbers from Cast into an area-law distribution as observed on cratered bodies. */ g = sqrt(1 / (M_PI * (1 - Cast(0, 0.9999)))); /* If the crater is tiny, handle it specially. */ if (g < 3) { /* Set pixel to the average of its Moore neighbourhood. */ for (y = cy - 1; y <= cy + 1; y++) { for (x = cx - 1; x <= cx + 1; x++) { amptot += *Auxadr(x, y); npatch++; } } axelev = amptot / npatch; /* Perturb the mean elevation by a small random factor. */ x = (g >= 1) ? ((rand() >> 8) & 3) - 1 : 0; *Auxadr(cx, cy) = axelev + x; /* Jam repaint sizes to correct patch. */ gx = 1; gy = 0; } else { /* Regular crater. Generate an impact feature of the correct size and shape. */ /* Determine mean elevation around the impact area. */ gx = MAX(2, (g / 3)); gy = MAX(2, g / 3); for (y = cy - gy; y <= cy + gy; y++) { for (x = cx-gx; x <= cx + gx; x++) { amptot += *Auxadr(x,y); npatch++; } } axelev = amptot / npatch; gy = MAX(2, g); g = gy; gx = MAX(2, g); for (y = cy - gy; y <= cy + gy; y++) { double dy = (cy - y) / (double) gy, dysq = dy * dy; for (x = cx - gx; x <= cx + gx; x++) { double dx = ((cx - x) / (double) gx), cd = (dx * dx) + dysq, cd2 = cd * 2.25, tcz = DepthBias - sqrt(fabs(1 - cd2)), cz = MAX((cd2 > 1) ? 0.0 : -10, tcz), roll, iroll; unsigned short av; cz *= pow(g, CdepthPower); if (dy == 0 && dx == 0 && ((int) cz) == 0) { cz = cz < 0 ? -1 : 1; } #define rollmin 0.9 roll = (((1 / (1 - MIN(rollmin, cd))) / (1 / (1 - rollmin))) - (1 - rollmin)) / rollmin; iroll = 1 - roll; av = (axelev + cz) * iroll + (*Auxadr(x,y) + cz) * roll; av = MAX(1000, MIN(64000, av)); *Auxadr(x,y) = av; } } } if ((l % 5000) == 4999) { pm_message( "%u craters generated of %u (%u%% done)", l + 1, ncraters, ((l + 1) * 100) / ncraters); } } i = MAX((slopemax - slopemin) + 1, 1); MALLOCARRAY(slopemap, i); if (slopemap == NULL) pm_error("out of memory allocating slope map"); for (i = slopemin; i <= slopemax; i++) { /* Confused? OK, we're using the left-to-right slope to calculate a shade based on the sine of the angle with respect to the vertical (light incident from the left). Then, with one exponentiation, we account for both the inherent gamma of the image (ad-hoc), and the user-specified display gamma, using the identity: (x^y)^z = (x^(y*z)) */ slopemap[i - slopemin] = i > 0 ? (128 + 127.0 * pow(sin((M_PI / 2) * i / slopemax), dgamma * ImageGamma)) : (128 - 127.0 * pow(sin((M_PI / 2) * i / slopemin), dgamma * ImageGamma)); } generateScreenImage(aux, screenxsize, screenysize, slopemap); free((char *) slopemap); free((char *) aux); } int main(int argc, const char ** argv) { struct CmdlineInfo cmdline; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); DepthBias = sqrt(0.5); gencraters(cmdline); exit(0); } advanced/generator/pgmnoise.c0000644000175000001440000001366112347602010015441 0ustar ericusers/********************************************************************* pgmnoise - create a PGM with white noise Frank Neumann, October 1993 *********************************************************************/ #include "pm_c_util.h" #include "mallocvar.h" #include "shhopt.h" #include "pgm.h" #include struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int width; unsigned int height; unsigned int maxval; unsigned int randomseed; unsigned int randomseedSpec; }; static void parseCommandLine(int argc, const char ** const argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; unsigned int maxvalSpec; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ free(option_def); if (maxvalSpec) { if (cmdlineP->maxval > PGM_OVERALLMAXVAL) pm_error("Maxval too large: %u. Maximu is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL); else if (cmdlineP->maxval == 0) pm_error("Maxval must not be zero"); } else cmdlineP->maxval = PGM_MAXMAXVAL; if (argc-1 != 2) pm_error("Wrong number of arguments: %u. " "Arguments are width and height of image, in pixels", argc-1); else { int const width = atoi(argv[1]); int const height = atoi(argv[2]); if (width <= 0) pm_error("Width must be positive, not %d", width); else cmdlineP->width = width; if (height <= 0) pm_error("Height must be positive, not %d", width); else cmdlineP->height = height; } } static unsigned int randPool(unsigned int const digits) { /*---------------------------------------------------------------------------- Draw 'digits' bits from pool of random bits. If the number of random bits in pool is insufficient, call rand() and add 31 bits to it. 'digits' must be at most 16. We assume that each call to rand() generates 31 bits, or RAND_MAX == 2147483647. The underlying logic is flexible and endian-free. The above conditions can be relaxed. -----------------------------------------------------------------------------*/ static unsigned long int hold=0; /* entropy pool */ static unsigned int len=0; /* number of valid bits in pool */ unsigned int const mask = (1 << digits) - 1; unsigned int retval; assert(RAND_MAX == 2147483647 && digits <= 16); retval = hold; /* initial value */ if (len > digits) { /* Enough bits in hold to satisfy request */ hold >>= digits; len -= digits; } else { /* Load another 31 bits into hold */ hold = rand(); retval |= (hold << len); hold >>= (digits - len); len = 31 - digits + len; } return (retval & mask); } static void pgmnoise(FILE * const ofP, unsigned int const cols, unsigned int const rows, gray const maxval) { bool const usingPool = !(RAND_MAX==2147483647 && (maxval & (maxval+1))); unsigned int const bitLen = pm_maxvaltobits(maxval); unsigned int row; gray * destrow; /* If maxval is 2^n-1, we draw exactly n bits from the pool. Otherwise call rand() and determine gray value by modulo. In the latter case, there is a miniscule skew toward 0 (=black) because smaller numbers are produced more frequently by modulo. Thus we employ the pool method only when it is certain that no skew will ensue. To illustrate the point, consider converting the outcome of one roll of a fair, six-sided die to 5 values (0 to 4) by N % 5. The probability for values 1, 2, 3, 4 are 1/6, but 0 alone is 2/6. Average is 10/6 or 1.6667, compared to 2.0 from an ideal generator which produces exactly 5 values. With two dice average improves to 70/36 or 1.9444. The more (distinct) dice we roll, or the more binary digits we draw, the smaller the skew. */ destrow = pgm_allocrow(cols); pgm_writepgminit(ofP, cols, rows, maxval, 0); for (row = 0; row < rows; ++row) { if (usingPool) { unsigned int col; for (col = 0; col < cols; ++col) destrow[col] = randPool(bitLen); } else { unsigned int col; for (col = 0; col < cols; ++col) destrow[col] = rand() % (maxval + 1); } pgm_writepgmrow(ofP, destrow, cols, maxval, 0); } pgm_freerow(destrow); } int main(int argc, const char * argv[]) { struct cmdlineInfo cmdline; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval); return 0; } advanced/generator/pamgauss.c0000644000175000001440000001433612347602010015440 0ustar ericusers#include #include #include #include #include #include "pm_c_util.h" #include "shhopt.h" #include "mallocvar.h" #include "pam.h" struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int width; unsigned int height; unsigned int maxval; float sigma; const char * tupletype; }; static void parseCommandLine(int argc, const char ** argv, struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry *option_def; /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int tupletypeSpec, maxvalSpec, sigmaSpec; unsigned int option_def_index; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "tupletype", OPT_STRING, &cmdlineP->tupletype, &tupletypeSpec, 0); OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); OPTENT3(0, "sigma", OPT_FLOAT, &cmdlineP->sigma, &sigmaSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!tupletypeSpec) cmdlineP->tupletype = ""; else { struct pam pam; if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type)) pm_error("The tuple type you specified is too long. " "Maximum %u characters.", (unsigned)sizeof(pam.tuple_type)-1); } if (!sigmaSpec) pm_error("You must specify the -sigma option."); else if (cmdlineP->sigma <= 0.0) pm_error("-sigma must be positive. You specified %f", cmdlineP->sigma); if (!maxvalSpec) cmdlineP->maxval = PNM_MAXMAXVAL; else { if (cmdlineP->maxval > PNM_OVERALLMAXVAL) pm_error("The maxval you specified (%u) is too big. " "Maximum is %u", cmdlineP->maxval, PNM_OVERALLMAXVAL); if (cmdlineP->maxval < 1) pm_error("-maxval must be at least 1"); } if (argc-1 < 2) pm_error("Need two arguments: width and height."); else if (argc-1 > 2) pm_error("Only two arguments allowed: width and height. " "You specified %d", argc-1); else { cmdlineP->width = atoi(argv[1]); cmdlineP->height = atoi(argv[2]); if (cmdlineP->width <= 0) pm_error("width argument must be a positive number. You " "specified '%s'", argv[1]); if (cmdlineP->height <= 0) pm_error("height argument must be a positive number. You " "specified '%s'", argv[2]); } free(option_def); } static double distFromCenter(struct pam * const pamP, int const col, int const row) { return sqrt(SQR(col - pamP->width/2) + SQR(row - pamP->height/2)); } static double gauss(double const arg, double const sigma) { /*---------------------------------------------------------------------------- Compute the value of the gaussian function with sigma parameter 'sigma' and mu parameter zero of argument 'arg'. -----------------------------------------------------------------------------*/ double const pi = 3.14159; double const coefficient = 1 / (sigma * sqrt(2*pi)); double const exponent = - SQR(arg-0) / (2 * SQR(sigma)); return coefficient * exp(exponent); } static double imageNormalizer(struct pam * const pamP, double const sigma) { /*---------------------------------------------------------------------------- Compute the value that has to be multiplied by the value of the one-dimensional gaussian function of the distance from center in order to get the value for a normalized two-dimensional gaussian function. Normalized here means that the volume under the whole curve is 1, just as the area under a whole one-dimensional gaussian function is 1. -----------------------------------------------------------------------------*/ double volume; unsigned int row; volume = 0.0; /* initial value */ for (row = 0; row < pamP->height; ++row) { unsigned int col; for (col = 0; col < pamP->width; ++col) volume += gauss(distFromCenter(pamP, col, row), sigma); } return 1.0 / volume; } int main(int argc, const char **argv) { struct CmdlineInfo cmdline; struct pam pam; int row; double normalizer; tuplen * tuplerown; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); pam.size = sizeof(pam); pam.len = PAM_STRUCT_SIZE(tuple_type); pam.file = stdout; pam.format = PAM_FORMAT; pam.plainformat = 0; pam.width = cmdline.width; pam.height = cmdline.height; pam.depth = 1; pam.maxval = cmdline.maxval; strcpy(pam.tuple_type, cmdline.tupletype); normalizer = imageNormalizer(&pam, cmdline.sigma); pnm_writepaminit(&pam); tuplerown = pnm_allocpamrown(&pam); for (row = 0; row < pam.height; ++row) { int col; for (col = 0; col < pam.width; ++col) { double const gauss1 = gauss(distFromCenter(&pam, col, row), cmdline.sigma); tuplerown[col][0] = gauss1 * normalizer; } pnm_writepamrown(&pam, tuplerown); } pnm_freepamrown(tuplerown); return 0; } advanced/generator/ppmrough.c0000644000175000001440000002275312347602010015463 0ustar ericusers/* ppmrough.c - create a PPM image containing two colors with a ragged border between them ** ** Copyright (C) 2002 by Eckard Specht. ** ** 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 #include "pm_c_util.h" #include "shhopt.h" #include "ppm.h" static pixel** PIX; static pixval BG_RED, BG_GREEN, BG_BLUE; struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int left, right, top, bottom; unsigned int width, height, var; const char * bg_rgb; const char * fg_rgb; unsigned int randomseed; unsigned int randomseedSpec; unsigned int verbose; }; static void parseCommandLine(int argc, const char ** argv, struct CmdlineInfo * const cmdlineP) { optEntry *option_def = malloc(100*sizeof(optEntry)); /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, NULL, 0); OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, NULL, 0); OPTENT3(0, "left", OPT_UINT, &cmdlineP->left, NULL, 0); OPTENT3(0, "right", OPT_UINT, &cmdlineP->right, NULL, 0); OPTENT3(0, "top", OPT_UINT, &cmdlineP->top, NULL, 0); OPTENT3(0, "bottom", OPT_UINT, &cmdlineP->bottom, NULL, 0); OPTENT3(0, "bg", OPT_STRING, &cmdlineP->bg_rgb, NULL, 0); OPTENT3(0, "fg", OPT_STRING, &cmdlineP->fg_rgb, NULL, 0); OPTENT3(0, "var", OPT_UINT, &cmdlineP->var, NULL, 0); OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); OPTENT3(0, "init", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); /* Set the defaults */ cmdlineP->width = 100; cmdlineP->height = 100; cmdlineP->left = cmdlineP->right = cmdlineP->top = cmdlineP->bottom = -1; cmdlineP->bg_rgb = NULL; cmdlineP->fg_rgb = NULL; cmdlineP->var = 10; opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); if (argc-1 != 0) pm_error("There are no arguments. You specified %d.", argc-1); free(option_def); } static void procLeft(int const r1, int const r2, int const c1, int const c2, unsigned int const var) { int cm, rm, c; if (r1 + 1 == r2) return; rm = (r1 + r2) >> 1; cm = (c1 + c2) >> 1; cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); for (c = 0; c < cm; c++) PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE); procLeft(r1, rm, c1, cm, var); procLeft(rm, r2, cm, c2, var); } static void procRight(int const r1, int const r2, int const c1, int const c2, unsigned int const width, unsigned int const var) { int cm, rm, c; if (r1 + 1 == r2) return; rm = (r1 + r2) >> 1; cm = (c1 + c2) >> 1; cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); for (c = cm; c < width; c++) PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE); procRight(r1, rm, c1, cm, width, var); procRight(rm, r2, cm, c2, width, var); } static void procTop(int const c1, int const c2, int const r1, int const r2, unsigned int const var) { int rm, cm, r; if (c1 + 1 == c2) return; cm = (c1 + c2) >> 1; rm = (r1 + r2) >> 1; rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); for (r = 0; r < rm; r++) PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE); procTop(c1, cm, r1, rm, var); procTop(cm, c2, rm, r2, var); } static void procBottom(int const c1, int const c2, int const r1, int const r2, unsigned int const height, unsigned int const var) { int rm, cm, r; if (c1 + 1 == c2) return; cm = (c1 + c2) >> 1; rm = (r1 + r2) >> 1; rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); for (r = rm; r < height; r++) PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE); procBottom(c1, cm, r1, rm, height, var); procBottom(cm, c2, rm, r2, height, var); } int main(int argc, const char * argv[]) { struct CmdlineInfo cmdline; pixel bgcolor, fgcolor; pixval fg_red, fg_green, fg_blue; int rows, cols, row; int left, right, top, bottom; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); cols = cmdline.width; rows = cmdline.height; left = cmdline.left; right = cmdline.right; top = cmdline.top; bottom = cmdline.bottom; if (cmdline.bg_rgb) bgcolor = ppm_parsecolor(cmdline.bg_rgb, PPM_MAXMAXVAL); else PPM_ASSIGN(bgcolor, 0, 0, 0); BG_RED = PPM_GETR(bgcolor); BG_GREEN = PPM_GETG(bgcolor); BG_BLUE = PPM_GETB(bgcolor); if (cmdline.fg_rgb) fgcolor = ppm_parsecolor(cmdline.fg_rgb, PPM_MAXMAXVAL); else PPM_ASSIGN(fgcolor, PPM_MAXMAXVAL, PPM_MAXMAXVAL, PPM_MAXMAXVAL); fg_red = PPM_GETR(fgcolor); fg_green = PPM_GETG(fgcolor); fg_blue = PPM_GETB(fgcolor); if (cmdline.verbose) { pm_message("width is %d, height is %d, variance is %d.", cols, rows, cmdline.var); if (left >= 0) pm_message("ragged left border is required"); if (right >= 0) pm_message("ragged right border is required"); if (top >= 0) pm_message("ragged top border is required"); if (bottom >= 0) pm_message("ragged bottom border is required"); pm_message("background is %s", ppm_colorname(&bgcolor, PPM_MAXMAXVAL, 1)); pm_message("foreground is %s", ppm_colorname(&fgcolor, PPM_MAXMAXVAL, 1)); if (cmdline.randomseedSpec) pm_message("srand() initialized with seed %u", cmdline.randomseed); } /* Allocate memory for the whole pixmap */ PIX = ppm_allocarray(cols, rows); /* First, set all pixel to foreground color */ for (row = 0; row < rows; row++) { unsigned int col; for (col = 0; col < cols; ++col) PPM_ASSIGN(PIX[row][col], fg_red, fg_green, fg_blue); } /* Make a ragged left border */ if (left >= 0) { int const left_c1 = left; int const left_c2 = left; int const left_r1 = 0; int const left_r2 = rows - 1; unsigned int col; for (col = 0; col < left_c1; ++col) PPM_ASSIGN(PIX[left_r1][col], BG_RED, BG_GREEN, BG_BLUE); for (col = 0; col < left_c2; ++col) PPM_ASSIGN(PIX[left_r2][col], BG_RED, BG_GREEN, BG_BLUE); procLeft(left_r1, left_r2, left_c1, left_c2, cmdline.var); } /* Make a ragged right border */ if (right >= 0) { int const right_c1 = cols - right - 1; int const right_c2 = cols - right - 1; int const right_r1 = 0; int const right_r2 = rows - 1; unsigned int col; for (col = right_c1; col < cols; col++) PPM_ASSIGN(PIX[right_r1][col], BG_RED, BG_GREEN, BG_BLUE); for (col = right_c2; col < cols; col++) PPM_ASSIGN(PIX[right_r2][col], BG_RED, BG_GREEN, BG_BLUE); procRight(right_r1, right_r2, right_c1, right_c2, cmdline.width, cmdline.var); } /* Make a ragged top border */ if (top >= 0) { int const top_r1 = top; int const top_r2 = top; int const top_c1 = 0; int const top_c2 = cols - 1; unsigned int row; for (row = 0; row < top_r1; ++row) PPM_ASSIGN(PIX[row][top_c1], BG_RED, BG_GREEN, BG_BLUE); for (row = 0; row < top_r2; ++row) PPM_ASSIGN(PIX[row][top_c2], BG_RED, BG_GREEN, BG_BLUE); procTop(top_c1, top_c2, top_r1, top_r2, cmdline.var); } /* Make a ragged bottom border */ if (bottom >= 0) { int const bottom_r1 = rows - bottom - 1; int const bottom_r2 = rows - bottom - 1; int const bottom_c1 = 0; int const bottom_c2 = cols - 1; unsigned int row; for (row = bottom_r1; row < rows; ++row) PPM_ASSIGN(PIX[row][bottom_c1], BG_RED, BG_GREEN, BG_BLUE); for (row = bottom_r2; row < rows; ++row) PPM_ASSIGN(PIX[row][bottom_c2], BG_RED, BG_GREEN, BG_BLUE); procBottom(bottom_c1, bottom_c2, bottom_r1, bottom_r2, cmdline.height, cmdline.var); } /* Write pixmap */ ppm_writeppm(stdout, PIX, cols, rows, PPM_MAXMAXVAL, 0); ppm_freearray(PIX, rows); pm_close(stdout); return 0; } advanced/generator/ppmrainbow0000755000175000001440000000567312347602010015564 0ustar ericusers#!/bin/sh ############################################################################## # This is essentially a Perl program. We exec the Perl interpreter specifying # this same file as the Perl program and use the -x option to cause the Perl # interpreter to skip down to the Perl code. The reason we do this instead of # just making /usr/bin/perl the script interpreter (instead of /bin/sh) is # that the user may have multiple Perl interpreters and the one he wants to # use is properly located in the PATH. The user's choice of Perl interpreter # may be crucial, such as when the user also has a PERL5LIB environment # variable and it selects modules that work with only a certain main # interpreter program. # # An alternative some people use is to have /usr/bin/env as the script # interpreter. We don't do that because we think the existence and # compatibility of /bin/sh is more reliable. # # Note that we aren't concerned about efficiency because the user who needs # high efficiency can use directly the programs that this program invokes. # ############################################################################## exec perl -w -x -S -- "$0" "$@" #!/usr/bin/perl use strict; use Getopt::Long; my ($FALSE, $TRUE) = (0,1); (my $myname = $0) =~ s#\A.*/##; sub fatal($) { my ($msg) = @_; print(STDERR "$msg\n"); exit(1); } my ($Twid, $Thgt, $tmpdir, $norepeat, $verbose); # set defaults $Twid = 600; $Thgt = 8; $tmpdir = $ENV{"TMPDIR"} || "/tmp"; $norepeat = $FALSE; $verbose = $FALSE; GetOptions("width=i" => \$Twid, "height=i" => \$Thgt, "tmpdir=s" => \$tmpdir, "norepeat!" => \$norepeat, "verbose!" => \$verbose); if ($Twid < 1 || $Thgt < 1) { fatal("invalid width and/or height"); } my $verboseCommand = $verbose ? "set -x;" : ""; if (@ARGV < 1) { fatal("You must specify at least one color as an argument"); } elsif (@ARGV < 2 && $norepeat) { fatal("With the -norepeat option, you must specify at least two colors " . "as arguments."); } my @colorlist; @colorlist = @ARGV; if (!$norepeat) { push @colorlist, $ARGV[0]; } my $ourtmp = "$tmpdir/ppmrainbow$$"; mkdir($ourtmp, 0777) or die("Unable to create directory for temporary files '$ourtmp"); my $widthRemaining; my $n; my @outlist; $n = 0; $widthRemaining = $Twid; @outlist = (); while (@colorlist >= 2) { my $outfile = sprintf("%s/file.%03u.ppm", $ourtmp, $n); push(@outlist, $outfile); my $w = int(($widthRemaining-1)/(@colorlist-1))+1; my $rc = system("$verboseCommand pgmramp -lr $w $Thgt | " . "pgmtoppm \"$colorlist[0]-$colorlist[1]\" >$outfile"); if ($rc != 0) { fatal("pgmramp|pgmtoppm failed."); } $widthRemaining -= $w; $n++; shift @colorlist; } 0 == system qq{$verboseCommand pnmcat -lr @outlist} or exit 1; exit 0; END { if (@outlist) { unlink(@outlist); } if (defined($ourtmp)) { rmdir($ourtmp); } } advanced/generator/pbmupc.c0000644000175000001440000004604612347602010015111 0ustar ericusers/* pbmupc.c - create a Universal Product Code bitmap ** ** 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 "pbm.h" #define MARGIN 20 #define DIGIT_WIDTH 14 #define DIGIT_HEIGHT 23 #define LINE1_WIDTH 2 #define LINE2_WIDTH ( 2 * LINE1_WIDTH ) #define LINE3_WIDTH ( 3 * LINE1_WIDTH ) #define LINE4_WIDTH ( 4 * LINE1_WIDTH ) #define LINES_WIDTH ( 7 * LINE1_WIDTH ) #define SHORT_HEIGHT ( 8 * LINES_WIDTH ) #define TALL_HEIGHT ( SHORT_HEIGHT + DIGIT_HEIGHT / 2 ) static int alldig ARGS(( char* cp )); static void putdigit ARGS(( int d, bit** bits, int row0, int col0 )); static int addlines ARGS(( int d, bit** bits, int row0, int col0, int height, bit color )); static int rect ARGS(( bit** bits, int row0, int col0, int height, int width, bit color )); int main( argc, argv ) int argc; char* argv[]; { register bit** bits; int argn, style, rows, cols, row, digrow, col, digcolofs; char* typecode; char* manufcode; char* prodcode; int sum, p, lc0, lc1, lc2, lc3, lc4, rc0, rc1, rc2, rc3, rc4; const char* const usage = "[-s1|-s2] "; pbm_init( &argc, argv ); argn = 1; style = 1; /* Check for flags. */ while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { if ( pm_keymatch( argv[argn], "-s1", 3 ) ) style = 1; else if ( pm_keymatch( argv[argn], "-s2", 3 ) ) style = 2; else pm_usage( usage ); argn++; } if ( argn + 3 < argc ) pm_usage( usage ); typecode = argv[argn]; manufcode = argv[argn + 1]; prodcode = argv[argn + 2]; argn += 3; if ( argn != argc ) pm_usage( usage ); if ( strlen( typecode ) != 1 || ( ! alldig( typecode ) ) || strlen( manufcode ) != 5 || ( ! alldig ( manufcode ) ) || strlen( prodcode ) != 5 || ( ! alldig ( prodcode ) ) ) pm_error( "type code must be one digit, and\n manufacturer and product codes must be five digits" ); p = typecode[0] - '0'; lc0 = manufcode[0] - '0'; lc1 = manufcode[1] - '0'; lc2 = manufcode[2] - '0'; lc3 = manufcode[3] - '0'; lc4 = manufcode[4] - '0'; rc0 = prodcode[0] - '0'; rc1 = prodcode[1] - '0'; rc2 = prodcode[2] - '0'; rc3 = prodcode[3] - '0'; rc4 = prodcode[4] - '0'; sum = ( 10 - ( ( ( p + lc1 + lc3 + rc0 + rc2 + rc4 ) * 3 + lc0 + lc2 + lc4 + rc1 + rc3 ) % 10 ) ) % 10; rows = 2 * MARGIN + SHORT_HEIGHT + DIGIT_HEIGHT; cols = 2 * MARGIN + 12 * LINES_WIDTH + 11 * LINE1_WIDTH; bits = pbm_allocarray( cols, rows ); (void) rect( bits, 0, 0, rows, cols, PBM_WHITE ); row = MARGIN; digrow = row + SHORT_HEIGHT; col = MARGIN; digcolofs = ( LINES_WIDTH - DIGIT_WIDTH ) / 2; if ( style == 1 ) putdigit( p, bits, digrow, col - DIGIT_WIDTH - LINE1_WIDTH ); else if ( style == 2 ) putdigit( p, bits, row + SHORT_HEIGHT / 2, col - DIGIT_WIDTH - LINE1_WIDTH ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_BLACK ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_WHITE ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_BLACK ); col = addlines( p, bits, row, col, TALL_HEIGHT, PBM_WHITE ); putdigit( lc0, bits, digrow, col + digcolofs ); col = addlines( lc0, bits, row, col, SHORT_HEIGHT, PBM_WHITE ); putdigit( lc1, bits, digrow, col + digcolofs ); col = addlines( lc1, bits, row, col, SHORT_HEIGHT, PBM_WHITE ); putdigit( lc2, bits, digrow, col + digcolofs ); col = addlines( lc2, bits, row, col, SHORT_HEIGHT, PBM_WHITE ); putdigit( lc3, bits, digrow, col + digcolofs ); col = addlines( lc3, bits, row, col, SHORT_HEIGHT, PBM_WHITE ); putdigit( lc4, bits, digrow, col + digcolofs ); col = addlines( lc4, bits, row, col, SHORT_HEIGHT, PBM_WHITE ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_WHITE ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_BLACK ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_WHITE ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_BLACK ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_WHITE ); putdigit( rc0, bits, digrow, col + digcolofs ); col = addlines( rc0, bits, row, col, SHORT_HEIGHT, PBM_BLACK ); putdigit( rc1, bits, digrow, col + digcolofs ); col = addlines( rc1, bits, row, col, SHORT_HEIGHT, PBM_BLACK ); putdigit( rc2, bits, digrow, col + digcolofs ); col = addlines( rc2, bits, row, col, SHORT_HEIGHT, PBM_BLACK ); putdigit( rc3, bits, digrow, col + digcolofs ); col = addlines( rc3, bits, row, col, SHORT_HEIGHT, PBM_BLACK ); putdigit( rc4, bits, digrow, col + digcolofs ); col = addlines( rc4, bits, row, col, SHORT_HEIGHT, PBM_BLACK ); col = addlines( sum, bits, row, col, TALL_HEIGHT, PBM_BLACK ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_BLACK ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_WHITE ); col = rect( bits, row, col, TALL_HEIGHT, LINE1_WIDTH, PBM_BLACK ); if ( style == 1 ) putdigit( sum, bits, digrow, col + LINE1_WIDTH ); pbm_writepbm( stdout, bits, cols, rows, 0 ); pm_close( stdout ); exit( 0 ); } static int alldig( cp ) char* cp; { for ( ; *cp != '\0'; cp++ ) if ( *cp < '0' || *cp > '9' ) return 0; return 1; } static void putdigit( d, bits, row0, col0 ) int d; bit** bits; int row0, col0; { int row, col; static bit digits[10][DIGIT_HEIGHT][DIGIT_WIDTH] = { /* 0 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,1,1,1,1,1,1,0,0,0,0}, {0,0,0,1,1,1,1,1,1,1,1,0,0,0}, {0,0,1,1,1,0,0,0,0,1,1,1,0,0}, {0,0,1,1,0,0,0,0,0,0,1,1,0,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,0,1,1,0,0,0,0,0,0,1,1,0,0}, {0,0,1,1,1,0,0,0,0,1,1,1,0,0}, {0,0,0,1,1,1,1,1,1,1,1,0,0,0}, {0,0,0,0,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} }, /* 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,1,1,0,0,0,0,0,0}, {0,0,0,0,0,1,1,1,0,0,0,0,0,0}, {0,0,0,0,1,1,1,1,0,0,0,0,0,0}, {0,0,0,1,1,1,1,1,0,0,0,0,0,0}, {0,0,1,1,1,0,1,1,0,0,0,0,0,0}, {0,0,1,1,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,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} }, /* 2 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,1,1,1,1,0,0,0,0,0}, {0,0,0,1,1,1,1,1,1,1,1,0,0,0}, {0,0,1,1,1,1,0,0,1,1,1,1,0,0}, {0,1,1,1,0,0,0,0,0,0,1,1,0,0}, {0,1,1,0,0,0,0,0,0,0,1,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,1,1,0}, {0,0,0,0,0,0,0,0,0,0,1,1,1,0}, {0,0,0,0,0,0,0,0,0,1,1,1,0,0}, {0,0,0,0,0,0,0,0,1,1,1,0,0,0}, {0,0,0,0,0,0,0,1,1,1,0,0,0,0}, {0,0,0,0,0,0,1,1,1,0,0,0,0,0}, {0,0,0,0,0,1,1,1,0,0,0,0,0,0}, {0,0,0,0,1,1,1,0,0,0,0,0,0,0}, {0,0,0,1,1,1,0,0,0,0,0,0,0,0}, {0,0,1,1,1,0,0,0,0,0,0,0,0,0}, {0,1,1,1,0,0,0,0,0,0,0,0,0,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,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} }, /* 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,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,0,0,0,0,0,0,0,0,0,1,1,1,0}, {0,0,0,0,0,0,0,0,0,1,1,1,0,0}, {0,0,0,0,0,0,0,0,1,1,1,0,0,0}, {0,0,0,0,0,0,0,1,1,1,0,0,0,0}, {0,0,0,0,0,0,1,1,1,0,0,0,0,0}, {0,0,0,0,0,1,1,1,1,0,0,0,0,0}, {0,0,0,0,0,1,1,1,1,1,1,0,0,0}, {0,0,0,0,0,0,0,0,1,1,1,1,0,0}, {0,0,0,0,0,0,0,0,0,0,1,1,0,0}, {0,0,0,0,0,0,0,0,0,0,1,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,1,1,1,0}, {0,1,1,1,0,0,0,0,0,0,1,1,0,0}, {0,0,1,1,1,1,0,0,1,1,1,1,0,0}, {0,0,0,1,1,1,1,1,1,1,1,0,0,0}, {0,0,0,0,0,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} }, /* 4 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,1,1,1,0,0,0,0,0,0}, {0,0,0,0,0,1,1,0,0,0,0,0,0,0}, {0,0,0,0,1,1,1,0,0,0,0,0,0,0}, {0,0,0,0,1,1,0,0,0,0,0,0,0,0}, {0,0,0,1,1,1,0,0,0,0,0,0,0,0}, {0,0,0,1,1,0,0,0,1,1,0,0,0,0}, {0,0,1,1,1,0,0,0,1,1,0,0,0,0}, {0,0,1,1,0,0,0,0,1,1,0,0,0,0}, {0,1,1,1,0,0,0,0,1,1,0,0,0,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,0,0,0,0,0,0,0,1,1,0,0,0,0}, {0,0,0,0,0,0,0,0,1,1,0,0,0,0}, {0,0,0,0,0,0,0,0,1,1,0,0,0,0}, {0,0,0,0,0,0,0,0,1,1,0,0,0,0}, {0,0,0,0,0,0,0,0,1,1,0,0,0,0}, {0,0,0,0,0,0,0,0,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} }, /* 5 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,1,1,1,1,1,1,0,0,0,0,0}, {0,1,1,1,1,1,1,1,1,1,1,0,0,0}, {0,0,0,0,0,0,0,0,1,1,1,1,0,0}, {0,0,0,0,0,0,0,0,0,0,1,1,0,0}, {0,0,0,0,0,0,0,0,0,0,1,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,1,1,1,0}, {0,1,1,1,0,0,0,0,0,0,1,1,0,0}, {0,0,1,1,1,1,0,0,1,1,1,1,0,0}, {0,0,0,1,1,1,1,1,1,1,1,0,0,0}, {0,0,0,0,0,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} }, /* 6 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,1,1,0,0,0,0,0}, {0,0,0,0,0,0,1,1,1,0,0,0,0,0}, {0,0,0,0,0,1,1,1,0,0,0,0,0,0}, {0,0,0,0,1,1,1,0,0,0,0,0,0,0}, {0,0,0,1,1,1,0,0,0,0,0,0,0,0}, {0,0,0,1,1,0,0,0,0,0,0,0,0,0}, {0,0,1,1,1,0,0,0,0,0,0,0,0,0}, {0,0,1,1,0,1,1,1,1,0,0,0,0,0}, {0,0,1,1,1,1,1,1,1,1,1,0,0,0}, {0,1,1,1,1,1,0,0,1,1,1,1,0,0}, {0,1,1,1,0,0,0,0,0,0,1,1,0,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,0,1,1,0,0,0,0,0,0,1,1,0,0}, {0,0,1,1,1,1,0,0,1,1,1,1,0,0}, {0,0,0,1,1,1,1,1,1,1,1,0,0,0}, {0,0,0,0,0,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} }, /* 7 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,0,0,0,0,0,0,0,0,0,1,1,1,0}, {0,0,0,0,0,0,0,0,0,0,1,1,0,0}, {0,0,0,0,0,0,0,0,0,1,1,1,0,0}, {0,0,0,0,0,0,0,0,0,1,1,0,0,0}, {0,0,0,0,0,0,0,0,1,1,1,0,0,0}, {0,0,0,0,0,0,0,0,1,1,0,0,0,0}, {0,0,0,0,0,0,0,1,1,1,0,0,0,0}, {0,0,0,0,0,0,0,1,1,0,0,0,0,0}, {0,0,0,0,0,0,1,1,1,0,0,0,0,0}, {0,0,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,1,1,1,0,0,0,0,0,0}, {0,0,0,0,0,1,1,0,0,0,0,0,0,0}, {0,0,0,0,0,1,1,0,0,0,0,0,0,0}, {0,0,0,0,1,1,1,0,0,0,0,0,0,0}, {0,0,0,0,1,1,0,0,0,0,0,0,0,0}, {0,0,0,0,1,1,0,0,0,0,0,0,0,0}, {0,0,0,0,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} }, /* 8 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,1,1,1,1,1,1,1,1,0,0,0}, {0,0,1,1,1,1,1,1,1,1,1,1,0,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,0,1,1,1,0,0,0,0,1,1,1,0,0}, {0,0,0,1,1,1,0,0,1,1,1,0,0,0}, {0,0,0,0,1,1,1,1,1,1,0,0,0,0}, {0,0,0,0,1,1,1,1,1,1,0,0,0,0}, {0,0,0,1,1,1,0,0,1,1,1,0,0,0}, {0,0,1,1,1,0,0,0,0,1,1,1,0,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,0,1,1,1,1,1,1,1,1,1,1,0,0}, {0,0,0,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} }, /* 9 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,1,1,1,1,0,0,0,0,0}, {0,0,0,1,1,1,1,1,1,1,1,0,0,0}, {0,0,1,1,1,1,0,0,1,1,1,1,0,0}, {0,0,1,1,0,0,0,0,0,0,1,1,0,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,0,0,0,0,0,0,0,0,1,1,0}, {0,1,1,1,0,0,0,0,0,0,1,1,1,0}, {0,0,1,1,0,0,0,0,0,0,1,1,1,0}, {0,0,1,1,1,1,0,0,1,1,1,1,1,0}, {0,0,0,1,1,1,1,1,1,1,1,1,0,0}, {0,0,0,0,0,1,1,1,1,0,1,1,0,0}, {0,0,0,0,0,0,0,0,0,1,1,1,0,0}, {0,0,0,0,0,0,0,0,0,1,1,0,0,0}, {0,0,0,0,0,0,0,0,1,1,1,0,0,0}, {0,0,0,0,0,0,0,1,1,1,0,0,0,0}, {0,0,0,0,0,0,1,1,1,0,0,0,0,0}, {0,0,0,0,0,1,1,1,0,0,0,0,0,0}, {0,0,0,0,0,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} } }; for ( row = 0; row < DIGIT_HEIGHT; row++ ) for ( col = 0; col < DIGIT_WIDTH; col++ ) bits[row0 + row][col0 + col] = digits[d][row][col]; } static int addlines( int d, bit** bits, int row0, int col0, int height, bit color ) { switch ( d ) { case 0: col0 = rect( bits, row0, col0, height, LINE3_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, 1 - color ); break; case 1: col0 = rect( bits, row0, col0, height, LINE2_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, 1 - color ); break; case 2: col0 = rect( bits, row0, col0, height, LINE2_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, 1 - color ); break; case 3: col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE4_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, 1 - color ); break; case 4: col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE3_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, 1 - color ); break; case 5: col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE3_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, 1 - color ); break; case 6: col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE4_WIDTH, 1 - color ); break; case 7: col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE3_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, 1 - color ); break; case 8: col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE3_WIDTH, 1 - color ); break; case 9: col0 = rect( bits, row0, col0, height, LINE3_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, 1 - color ); col0 = rect( bits, row0, col0, height, LINE1_WIDTH, color ); col0 = rect( bits, row0, col0, height, LINE2_WIDTH, 1 - color ); break; default: pm_error( "can't happen" ); } return col0; } static int rect( bit** bits, int row0, int col0, int height, int width, bit color ) { int row, col; for ( row = row0; row < row0 + height; row++ ) for ( col = col0; col < col0 + width; col++ ) bits[row][col] = color; return col0 + width; } advanced/generator/ppmwheel.c0000644000175000001440000001050712347602010015435 0ustar ericusers/* ppmwheel.c - create a color circle of a specified size ** ** This was adapted by Bryan Henderson in January 2003 from ppmcirc.c by ** Peter Kirchgessner: ** ** Copyright (C) 1995 by Peter Kirchgessner. ** ** 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 "ppm.h" #ifndef PI #define PI 3.14159265358979323846 #endif #ifndef ABS #define ABS(a) ((a) < 0 ? -(a) : (a)) #endif static void hsv_rgb(double const in_h, double const in_s, double const in_v, double * const r, double * const g, double * const b) { /*---------------------------------------------------------------------------- This is a stripped down hsv->rgb converter that works only for Saturation of zero. -----------------------------------------------------------------------------*/ double h, s, v; h = in_h < 0.0 ? 0.0 : in_h > 360.0 ? 360.0 : in_h; v = in_v < 0.0 ? 0.0 : in_v > 1.0 ? 1.0 : in_v; s = in_s < 0.0 ? 0.0 : in_s > 1.0 ? 1.0 : in_s; if (s != 0.0) pm_error("Internal error: non-zero saturation"); if (h <= 60.0) { /* from red to yellow */ *r = 1.0; *g = h / 60.0; *b = 0.0; } else if ( h <= 120.0 ) { /* from yellow to green */ *r = 1.0 - (h - 60.0) / 60.0; *g = 1.0; *b = 0.0; } else if ( h <= 180.0 ) { /* from green to cyan */ *r = 0.0; *g = 1.0; *b = (h - 120.0) / 60.0; } else if ( h <= 240.0 ) { /* from cyan to blue */ *r = 0.0; *g = 1.0 - (h - 180.0) / 60.0; *b = 1.0; } else if ( h <= 300.0) { /* from blue to magenta */ *r = (h - 240.0) / 60.0; *g = 0.0; *b = 1.0; } else { /* from magenta to red */ *r = 1.0; *g = 0.0; *b = 1.0 - (h - 300.0) / 60.0; } if ( v >= 0.5) { v = 2.0 - 2.0 * v; v = sqrt (v); *r = 1.0 + v * (*r - 1.0); *g = 1.0 + v * (*g - 1.0); *b = 1.0 + v * (*b - 1.0); } else { v *= 2.0; v = sqrt (sqrt ( sqrt (v))); *r *= v; *g *= v; *b *= v; } } int main(int argc, char *argv[]) { pixel *orow; int rows, cols; pixval maxval; unsigned int row; unsigned int xcenter, ycenter, radius; long diameter; char * tailptr; ppm_init( &argc, argv ); if (argc-1 != 1) pm_error("Program takes one argument: diameter of color wheel"); diameter = strtol(argv[1], &tailptr, 10); if (strlen(argv[1]) == 0 || *tailptr != '\0') pm_error("You specified an invalid diameter: '%s'", argv[1]); if (diameter <= 0) pm_error("Diameter must be positive. You specified %ld.", diameter); if (diameter < 4) pm_error("Diameter must be at least 4. You specified %ld", diameter); cols = rows = diameter; orow = ppm_allocrow(cols); maxval = PPM_MAXMAXVAL; ppm_writeppminit(stdout, cols, rows, maxval, 0); radius = diameter/2 - 1; xcenter = cols / 2; ycenter = rows / 2; for (row = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) { double const dx = (int)col - (int)xcenter; double const dy = (int)row - (int)ycenter; double const dist = sqrt(dx*dx + dy*dy); pixval r, g, b; if (dist > radius) { r = g = b = maxval; } else { double hue, sat, val; double dr, dg, db; hue = atan2(dx, dy) / PI * 180.0; if (hue < 0.0) hue = 360.0 + hue; sat = 0.0; val = dist / radius; hsv_rgb(hue, sat, val, &dr, &dg, &db); r = (pixval)(maxval * dr); g = (pixval)(maxval * dg); b = (pixval)(maxval * db); } PPM_ASSIGN (orow[col], r, g, b ); } ppm_writeppmrow(stdout, orow, cols, maxval, 0); } pm_close(stdout); exit(0); } advanced/generator/Makefile0000644000175000001440000000221012347602010015100 0ustar ericusersifeq ($(SRCDIR)x,x) SRCDIR = $(CURDIR)/.. BUILDDIR = $(SRCDIR) endif SUBDIR = generator VPATH=.:$(SRCDIR)/$(SUBDIR) include $(BUILDDIR)/config.mk # We tend to separate out the build targets so that we don't have # any more dependencies for a given target than it really needs. # That way, if there is a problem with a dependency, we can still # successfully build all the stuff that doesn't depend upon it. # This package is so big, it's useful even when some parts won't # build. PORTBINARIES = pamgauss pamgradient pamseq pamstereogram \ pbmpage pbmmake pbmtext pbmtextps pbmupc \ pgmcrater pgmkernel pgmmake pgmnoise pgmramp \ ppmcie ppmcolors ppmforge ppmmake ppmpat ppmrough ppmwheel \ # We don't include programs that have special library dependencies in the # merge scheme, because we don't want those dependencies to prevent us # from building all the other programs. NOMERGEBINARIES = MERGEBINARIES = $(PORTBINARIES) BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES) SCRIPTS = ppmrainbow OBJECTS = $(BINARIES:%=%.o) MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2) .PHONY: all all: $(BINARIES) include $(SRCDIR)/common.mk advanced/generator/ppmmake.c0000644000175000001440000000666212347602010015255 0ustar ericusers/* ppmmake.c - create a pixmap of a specified color and size ** ** 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 "pm_c_util.h" #include "mallocvar.h" #include "shhopt.h" #include "ppm.h" struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ pixel color; unsigned int cols; unsigned int rows; pixval maxval; }; static void parseCommandLine(int argc, char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int maxvalSpec; unsigned int option_def_index; MALLOCARRAY(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); opt.opt_table = option_def; opt.short_allowed = false; /* We have no short (old-fashioned) options */ opt.allowNegNum = false; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ free (option_def); if (!maxvalSpec) cmdlineP->maxval = PPM_MAXMAXVAL; else { if (cmdlineP->maxval > PPM_OVERALLMAXVAL) pm_error("The value you specified for -maxval (%u) is too big. " "Max allowed is %u", cmdlineP->maxval, PPM_OVERALLMAXVAL); if (cmdlineP->maxval < 1) pm_error("You cannot specify 0 for -maxval"); } if (argc-1 < 3) pm_error("Need 3 arguments: color, width, height."); else if (argc-1 > 3) pm_error("Only 3 arguments allowed: color, width, height. " "You specified %d", argc-1); else { cmdlineP->color = ppm_parsecolor(argv[1], cmdlineP->maxval); cmdlineP->cols = pm_parse_width(argv[2]); cmdlineP->rows = pm_parse_height(argv[3]); } } int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; pixel * pixrow; unsigned int row, col; ppm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ppm_writeppminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0); pixrow = ppm_allocrow(cmdline.cols); /* All rows are identical. Fill once. */ for (col = 0; col < cmdline.cols; ++col) pixrow[col] = cmdline.color; for (row = 0; row < cmdline.rows; ++row) ppm_writeppmrow(stdout, pixrow, cmdline.cols, cmdline.maxval, 0); ppm_freerow(pixrow); pm_close(stdout); return 0; } advanced/generator/pamstereogram.c0000644000175000001440000013173012347602010016464 0ustar ericusers/* ---------------------------------------------------------------------- * * Create a single image stereogram from a height map. * by Scott Pakin * Adapted to Netbpm conventions by Bryan Henderson. * Revised by Scott Pakin. * * The core of this program is a simple adaptation of the code in * "Displaying 3D Images: Algorithms for Single Image Random Dot * Stereograms" by Harold W. Thimbleby, Stuart Inglis, and Ian * H. Witten in IEEE Computer, 27(10):38-48, October 1994. See that * paper for a thorough explanation of what's going on here. * * ---------------------------------------------------------------------- * * Copyright (C) 2006-2012 Scott Pakin * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ---------------------------------------------------------------------- */ #define _ISOC99_SOURCE /* Make sure strtof() is in */ #include #include #include #include #include #include "pm_config.h" #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" #include "pam.h" enum outputType {OUTPUT_BW, OUTPUT_GRAYSCALE, OUTPUT_COLOR}; /* ---------------------------------------------------------------------- */ struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * inputFilespec; /* '-' if stdin */ unsigned int verbose; /* -verbose option */ unsigned int crosseyed; /* -crosseyed option */ unsigned int makemask; /* -makemask option */ unsigned int dpi; /* -dpi option */ float eyesep; /* -eyesep option */ float depth; /* -depth option */ unsigned int maxvalSpec; /* -maxval option count */ unsigned int maxval; /* -maxval option value */ unsigned int guidetop; /* -guidetop option count */ unsigned int guidebottom; /* -guidebottom option count */ unsigned int guidesize; /* -guidesize option value */ unsigned int magnifypat; /* -magnifypat option */ unsigned int xshift; /* -xshift option */ unsigned int yshift; /* -yshift option */ const char * patfile; /* -patfile option. Null if none */ const char * texfile; /* -texfile option. Null if none */ const char * bgcolor; /* -bgcolor option */ unsigned int smoothing; /* -smoothing option */ unsigned int randomseed; /* -randomseed option */ unsigned int randomseedSpec; /* -randomseed option count */ enum outputType outputType; /* Type of output file */ }; static void parseNearFarPlanes(const char ** const nearFarPlanes, float * const nearPlaneP, float * const farPlaneP) { /*---------------------------------------------------------------------------- Parse nearFarPlanes option value into exactly two positive numbers -----------------------------------------------------------------------------*/ float nearPlane, farPlane; if (nearFarPlanes == NULL || nearFarPlanes[0] == NULL || nearFarPlanes[1] == NULL || nearFarPlanes[2] != NULL) pm_error("-planes requires exactly two positive numbers"); errno = 0; nearPlane = strtof(nearFarPlanes[0], NULL); if (errno != 0 || nearPlane <= 0.0) pm_error("-planes requires exactly two positive numbers"); farPlane = strtof(nearFarPlanes[1], NULL); if (errno != 0 || farPlane <= 0.0) pm_error("-planes requires exactly two positive numbers"); if (nearPlane >= farPlane) pm_error("-planes requires the near-plane value " "to be less than the far-plane value"); *nearPlaneP = nearPlane; *farPlaneP = farPlane; } static void parseCommandLine(int argc, const char ** argv, struct cmdlineInfo * cmdlineP ) { /*---------------------------------------------------------------------------- Parse program command line described in Unix standard form by argc and argv. Return the information in the options as *cmdlineP. If command line is internally inconsistent (invalid options, etc.), issue error message to stderr and abort program. Note that the strings we return are stored in the storage that was passed to us as the argv array. We also trash *argv. -----------------------------------------------------------------------------*/ optEntry *option_def; /* Instructions to pm_optParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; unsigned int patfileSpec, texfileSpec, dpiSpec, eyesepSpec, depthSpec, guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, bgcolorSpec, smoothingSpec, planesSpec; unsigned int blackandwhite, grayscale, color; const char ** planes; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "verbose", OPT_FLAG, NULL, (unsigned int *)&cmdlineP->verbose, 0); OPTENT3(0, "crosseyed", OPT_FLAG, NULL, &cmdlineP->crosseyed, 0); OPTENT3(0, "makemask", OPT_FLAG, NULL, &cmdlineP->makemask, 0); OPTENT3(0, "blackandwhite", OPT_FLAG, NULL, &blackandwhite, 0); OPTENT3(0, "grayscale", OPT_FLAG, NULL, &grayscale, 0); OPTENT3(0, "color", OPT_FLAG, NULL, &color, 0); OPTENT3(0, "dpi", OPT_UINT, &cmdlineP->dpi, &dpiSpec, 0); OPTENT3(0, "eyesep", OPT_FLOAT, &cmdlineP->eyesep, &eyesepSpec, 0); OPTENT3(0, "depth", OPT_FLOAT, &cmdlineP->depth, &depthSpec, 0); OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &cmdlineP->maxvalSpec, 0); OPTENT3(0, "guidetop", OPT_FLAG, NULL, &cmdlineP->guidetop, 0); OPTENT3(0, "guidebottom", OPT_FLAG, NULL, &cmdlineP->guidebottom, 0); OPTENT3(0, "guidesize", OPT_UINT, &cmdlineP->guidesize, &guidesizeSpec, 0); OPTENT3(0, "magnifypat", OPT_UINT, &cmdlineP->magnifypat, &magnifypatSpec, 0); OPTENT3(0, "xshift", OPT_UINT, &cmdlineP->xshift, &xshiftSpec, 0); OPTENT3(0, "yshift", OPT_UINT, &cmdlineP->yshift, &yshiftSpec, 0); OPTENT3(0, "patfile", OPT_STRING, &cmdlineP->patfile, &patfileSpec, 0); OPTENT3(0, "texfile", OPT_STRING, &cmdlineP->texfile, &texfileSpec, 0); OPTENT3(0, "bgcolor", OPT_STRING, &cmdlineP->bgcolor, &bgcolorSpec, 0); OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); OPTENT3(0, "smoothing", OPT_UINT, &cmdlineP->smoothing, &smoothingSpec, 0); OPTENT3(0, "planes", OPT_STRINGLIST, &planes, &planesSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (blackandwhite + grayscale + color == 0) cmdlineP->outputType = OUTPUT_BW; else if (blackandwhite + grayscale + color > 1) pm_error("You may specify only one of -blackandwhite, -grayscale, " "and -color"); else { if (blackandwhite) cmdlineP->outputType = OUTPUT_BW; else if (grayscale) cmdlineP->outputType = OUTPUT_GRAYSCALE; else { assert(color); cmdlineP->outputType = OUTPUT_COLOR; } } if (!patfileSpec) cmdlineP->patfile = NULL; if (!texfileSpec) cmdlineP->texfile = NULL; if (!bgcolorSpec) cmdlineP->bgcolor = NULL; if (!smoothingSpec) cmdlineP->smoothing = 0; if (!dpiSpec) cmdlineP->dpi = 100; else if (cmdlineP->dpi < 1) pm_error("The argument to -dpi must be a positive integer"); if (!eyesepSpec) cmdlineP->eyesep = 2.56; else if (cmdlineP->eyesep <= 0.0) pm_error("The argument to -eyesep must be a positive number"); if (!depthSpec) cmdlineP->depth = (1.0/3.0); else if (cmdlineP->depth < 0.0 || cmdlineP->depth > 1.0) pm_error("The argument to -depth must be a number from 0.0 to 1.0"); if (cmdlineP->maxvalSpec) { if (cmdlineP->maxval < 1) pm_error("-maxval must be at least 1"); else if (cmdlineP->maxval > PNM_OVERALLMAXVAL) pm_error("-maxval must be at most %u. You specified %u", PNM_OVERALLMAXVAL, cmdlineP->maxval); } if (bgcolorSpec && !texfileSpec) pm_message("warning: -bgcolor has no effect " "except in conjunction with -texfile"); if (guidesizeSpec && !(cmdlineP->guidetop || cmdlineP->guidebottom)) pm_error("-guidesize has no meaning " "without -guidetop or -guidebottom"); if (!guidesizeSpec) cmdlineP->guidesize = 20; if (!magnifypatSpec) cmdlineP->magnifypat = 1; else if (cmdlineP->magnifypat < 1) pm_error("The argument to -magnifypat must be a positive integer"); if (!xshiftSpec) cmdlineP->xshift = 0; if (!yshiftSpec) cmdlineP->yshift = 0; if (xshiftSpec && !cmdlineP->patfile) pm_error("-xshift is valid only with -patfile"); if (yshiftSpec && !cmdlineP->patfile) pm_error("-yshift is valid only with -patfile"); if (cmdlineP->makemask && cmdlineP->patfile) pm_error("You may not specify both -makemask and -patfile"); if (cmdlineP->patfile && blackandwhite) pm_error("-blackandwhite is not valid with -patfile"); if (cmdlineP->patfile && grayscale) pm_error("-grayscale is not valid with -patfile"); if (cmdlineP->patfile && color) pm_error("-color is not valid with -patfile"); if (cmdlineP->patfile && cmdlineP->maxvalSpec) pm_error("-maxval is not valid with -patfile"); if (cmdlineP->texfile && blackandwhite) pm_error("-blackandwhite is not valid with -texfile"); if (cmdlineP->texfile && grayscale) pm_error("-grayscale is not valid with -texfile"); if (cmdlineP->texfile && color) pm_error("-color is not valid with -texfile"); if (cmdlineP->texfile && cmdlineP->maxvalSpec) pm_error("-maxval is not valid with -texfile"); if (planesSpec && eyesepSpec) pm_error("-planes is not valid with -eyesep"); if (planesSpec && depthSpec) pm_error("-planes is not valid with -depth"); if (planesSpec) { float nearPlane, farPlane; parseNearFarPlanes(planes, &nearPlane, &farPlane); cmdlineP->eyesep = 2.0*farPlane/cmdlineP->dpi; cmdlineP->depth = 2.0*(farPlane-nearPlane) / (2.0*farPlane-nearPlane); } if (argc-1 < 1) cmdlineP->inputFilespec = "-"; else if (argc-1 == 1) cmdlineP->inputFilespec = argv[1]; else pm_error("Too many non-option arguments: %d. Only argument is " "input file name", argc-1); } static unsigned int separation(double const dist, double const eyesep, unsigned int const dpi, double const dof /* depth of field */ ) { /*---------------------------------------------------------------------------- Return a separation in pixels which corresponds to a 3-D distance between the viewer's eyes and a point on an object. -----------------------------------------------------------------------------*/ unsigned int const pixelEyesep = ROUNDU(eyesep * dpi); return ROUNDU((1.0 - dof * dist) * pixelEyesep / (2.0 - dof * dist)); } static void reportImageParameters(const char * const fileDesc, const struct pam * const pamP) { pm_message("%s: tuple type '%s', %d wide x %d high x %d deep, maxval %lu", fileDesc, pamP->tuple_type, pamP->width, pamP->height, pamP->depth, pamP->maxval); } /*---------------------------------------------------------------------------- Background generators -----------------------------------------------------------------------------*/ struct outGenerator; typedef tuple coord2Color(struct outGenerator *, int, int); /* A type to use for functions that map a 2-D coordinate to a color. */ typedef void outGenStateTerm(struct outGenerator *); typedef struct { struct pam pam; tuple ** imageData; tuple bgColor; bool replaceBgColor; /* replace background color with pattern color */ unsigned int smoothing; /* Number of background-smoothing iterations to perform */ } texState; typedef struct outGenerator { struct pam pam; coord2Color * getTuple; /* Map from a height-map (x,y) coordinate to a tuple */ outGenStateTerm * terminateState; void * stateP; texState * textureP; /* Mapped-texture part of the state of operation. Null means we are doing an ordinary stereogram instead of a mapped-texture one. */ } outGenerator; struct randomState { /* The state of a randomColor generator. */ unsigned int magnifypat; tuple * currentRow; unsigned int prevy; }; #ifndef LITERAL_FN_DEF_MATCH static coord2Color randomColor; #endif static tuple randomColor(outGenerator * const outGenP, int const x, int const y) { /*---------------------------------------------------------------------------- Return a random RGB value. -----------------------------------------------------------------------------*/ struct randomState * const stateP = outGenP->stateP; /* Every time we start a new row, we select a new sequence of random colors. */ if (y/stateP->magnifypat != stateP->prevy/stateP->magnifypat) { unsigned int const modulus = outGenP->pam.maxval + 1; int col; for (col = 0; col < outGenP->pam.width; ++col) { tuple const thisTuple = stateP->currentRow[col]; unsigned int plane; for (plane = 0; plane < outGenP->pam.depth; ++plane) { unsigned int const randval = rand(); thisTuple[plane] = randval % modulus; } } } /* Return the appropriate column from the pregenerated color row. */ stateP->prevy = y; return stateP->currentRow[x/stateP->magnifypat]; } #ifndef LITERAL_FN_DEF_MATCH static outGenStateTerm termRandomColor; #endif static void termRandomColor(outGenerator * const outGenP) { struct randomState * const stateP = outGenP->stateP; pnm_freepamrow(stateP->currentRow); } static void initRandomColor(outGenerator * const outGenP, const struct pam * const inPamP, struct cmdlineInfo const cmdline) { struct randomState * stateP; outGenP->pam.format = PAM_FORMAT; outGenP->pam.plainformat = 0; switch (cmdline.outputType) { case OUTPUT_BW: strcpy(outGenP->pam.tuple_type, PAM_PBM_TUPLETYPE); outGenP->pam.maxval = 1; outGenP->pam.depth = 1; break; case OUTPUT_GRAYSCALE: strcpy(outGenP->pam.tuple_type, PAM_PGM_TUPLETYPE); outGenP->pam.maxval = cmdline.maxvalSpec ? cmdline.maxval : inPamP->maxval; outGenP->pam.depth = 1; break; case OUTPUT_COLOR: strcpy(outGenP->pam.tuple_type, PAM_PPM_TUPLETYPE); outGenP->pam.maxval = cmdline.maxvalSpec ? cmdline.maxval : inPamP->maxval; outGenP->pam.depth = 3; break; } MALLOCVAR_NOFAIL(stateP); stateP->currentRow = pnm_allocpamrow(&outGenP->pam); stateP->magnifypat = cmdline.magnifypat; stateP->prevy = (unsigned int)(-cmdline.magnifypat); outGenP->stateP = stateP; outGenP->getTuple = &randomColor; outGenP->terminateState = &termRandomColor; } struct patternPixelState { /* This is the state of a patternPixel generator.*/ struct pam patPam; /* Descriptor of pattern image */ tuple ** patTuples; /* Entire image read from the pattern file */ unsigned int xshift; unsigned int yshift; unsigned int magnifypat; }; #ifndef LITERAL_FN_DEF_MATCH static coord2Color patternPixel; #endif static tuple patternPixel(outGenerator * const outGenP, int const x, int const y) { /*---------------------------------------------------------------------------- Return a pixel from the pattern file. -----------------------------------------------------------------------------*/ struct patternPixelState * const stateP = outGenP->stateP; struct pam * const patPamP = &stateP->patPam; int patx, paty; paty = ((y - stateP->yshift) / stateP->magnifypat) % patPamP->height; if (paty < 0) paty += patPamP->height; patx = ((x - stateP->xshift) / stateP->magnifypat) % patPamP->width; if (patx < 0) patx += patPamP->width; return stateP->patTuples[paty][patx]; } #ifndef LITERAL_FN_DEF_MATCH static outGenStateTerm termPatternPixel; #endif static void termPatternPixel(outGenerator * const outGenP) { struct patternPixelState * const stateP = outGenP->stateP; pnm_freepamarray(stateP->patTuples, &stateP->patPam); } static void initPatternPixel(outGenerator * const outGenP, struct cmdlineInfo const cmdline) { /*---------------------------------------------------------------------------- Initialize parts of output generator *outGenP that are based on the supplied pattern file, assuming there is one. -----------------------------------------------------------------------------*/ struct patternPixelState * stateP; FILE * patternFileP; MALLOCVAR_NOFAIL(stateP); assert(cmdline.patfile); patternFileP = pm_openr(cmdline.patfile); stateP->patTuples = pnm_readpam(patternFileP, &stateP->patPam, PAM_STRUCT_SIZE(tuple_type)); pm_close(patternFileP); stateP->xshift = cmdline.xshift; stateP->yshift = cmdline.yshift; stateP->magnifypat = cmdline.magnifypat; outGenP->stateP = stateP; outGenP->getTuple = &patternPixel; outGenP->terminateState = &termPatternPixel; outGenP->pam.format = stateP->patPam.format; outGenP->pam.plainformat = stateP->patPam.plainformat; outGenP->pam.depth = stateP->patPam.depth; outGenP->pam.maxval = stateP->patPam.maxval; strcpy(outGenP->pam.tuple_type, stateP->patPam.tuple_type); if (cmdline.verbose) reportImageParameters("Pattern file", &stateP->patPam); } static void readTextureImage(struct cmdlineInfo const cmdline, const struct pam * const inpamP, const struct pam * const outpamP, texState ** const texturePP) { FILE * textureFileP; texState * textureP; struct pam * texPamP; MALLOCVAR_NOFAIL(textureP); texPamP = &textureP->pam; textureFileP = pm_openr(cmdline.texfile); textureP->imageData = pnm_readpam(textureFileP, texPamP, PAM_STRUCT_SIZE(tuple_type)); pm_close(textureFileP); if (cmdline.bgcolor) textureP->bgColor = pnm_parsecolor(cmdline.bgcolor, texPamP->maxval); else textureP->bgColor = pnm_backgroundtuple(texPamP, textureP->imageData); textureP->replaceBgColor = (cmdline.patfile != NULL); textureP->smoothing = cmdline.smoothing; if (cmdline.verbose) { const char * const colorname = pnm_colorname(texPamP, textureP->bgColor, 1); reportImageParameters("Texture file", texPamP); if (cmdline.bgcolor && strcmp(colorname, cmdline.bgcolor)) pm_message("Texture background color: %s (%s)", cmdline.bgcolor, colorname); else pm_message("Texture background color: %s", colorname); pm_strfree(colorname); } if (texPamP->width != inpamP->width || texPamP->height != inpamP->height) pm_error("The texture image must have the same width and height " "as the input image"); if (cmdline.patfile && (!streq(texPamP->tuple_type, outpamP->tuple_type) || texPamP->maxval != outpamP->maxval)) pm_error("The texture image must be of the same tuple type " "and maxval as the pattern image"); textureP->pam.file = outpamP->file; *texturePP = textureP; } static unsigned int totalGuideHeight(struct cmdlineInfo const cmdline) { /* Each pair of guides is cmdline.guidesize high, and we add that much white above and below as well, so the total vertical space is three times cmdline.giudesize. */ return (cmdline.guidetop ? 3 * cmdline.guidesize : 0) + (cmdline.guidebottom ? 3 * cmdline.guidesize : 0); } static void createoutputGenerator(struct cmdlineInfo const cmdline, const struct pam * const inPamP, outGenerator ** const outputGeneratorPP) { outGenerator * outGenP; MALLOCVAR_NOFAIL(outGenP); outGenP->pam.size = sizeof(struct pam); outGenP->pam.len = PAM_STRUCT_SIZE(tuple_type); outGenP->pam.file = stdout; outGenP->pam.height = inPamP->height + totalGuideHeight(cmdline); outGenP->pam.width = inPamP->width; if (cmdline.patfile) { /* Background pixels should come from the pattern file. */ initPatternPixel(outGenP, cmdline); } else { /* Background pixels should be generated randomly */ initRandomColor(outGenP, inPamP, cmdline); } outGenP->pam.bytes_per_sample = pnm_bytespersample(outGenP->pam.maxval); if (cmdline.texfile) { readTextureImage(cmdline, inPamP, &outGenP->pam, &outGenP->textureP); outGenP->pam = outGenP->textureP->pam; } else outGenP->textureP = NULL; *outputGeneratorPP = outGenP; } static void destroyoutputGenerator(outGenerator * const outputGeneratorP) { outputGeneratorP->terminateState(outputGeneratorP); free(outputGeneratorP); } /* End of background generators */ /* ---------------------------------------------------------------------- */ static void makeWhiteRow(const struct pam * const pamP, tuple * const tuplerow) { unsigned int col; for (col = 0; col < pamP->width; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) tuplerow[col][plane] = pamP->maxval; } } static void writeRowCopies(const struct pam * const outPamP, const tuple * const outrow, unsigned int const copyCount) { unsigned int i; for (i = 0; i < copyCount; ++i) pnm_writepamrow(outPamP, outrow); } static void writeWhiteRows(const struct pam * const outPamP, unsigned int const count) { tuple * outrow; /* One row of output data */ outrow = pnm_allocpamrow(outPamP); makeWhiteRow(outPamP, outrow); writeRowCopies(outPamP, outrow, count); pnm_freerow(outrow); } static void drawguides(unsigned int const guidesize, const struct pam * const outPamP, double const eyesep, unsigned int const dpi, double const depthOfField) { /*---------------------------------------------------------------------------- Draw a pair of guide boxes, left and right. -----------------------------------------------------------------------------*/ unsigned int const far = separation(0, eyesep, dpi, depthOfField); /* Space between the two guide boxes. */ unsigned int const width = outPamP->width; /* Width of the output image */ tuple * outrow; /* One row of output data */ tuple blackTuple; unsigned int col; pnm_createBlackTuple(outPamP, &blackTuple); outrow = pnm_allocpamrow(outPamP); /* Put some white rows before the guides */ writeWhiteRows(outPamP, guidesize); /* Initialize the row buffer to white */ makeWhiteRow(outPamP, outrow); if (far > width + guidesize) pm_message("warning: the guide boxes are completely out of bounds " "at %u DPI", dpi); else { unsigned int leftBeg, leftEnd, rightBeg, rightEnd; assert(far <= width + guidesize); leftEnd = (width - far + guidesize)/2; assert(guidesize <= width + far); rightBeg = (width + far - guidesize)/2; if (far + guidesize > width) { pm_message("warning: the guide boxes are partially out of bounds " "at %u DPI", dpi); leftBeg = 0; rightEnd = width; } else { assert(far + guidesize <= width); leftBeg = (width - far - guidesize)/2; rightEnd = (width + far + guidesize)/2; } /* Draw the left guide black in the buffer */ assert(leftEnd < outPamP->width); for (col = leftBeg; col < leftEnd; ++col) pnm_assigntuple(outPamP, outrow[col], blackTuple); /* Draw the right guide black in the buffer */ assert(rightEnd <= outPamP->width); for (col = rightBeg; col < rightEnd; ++col) pnm_assigntuple(outPamP, outrow[col], blackTuple); } /* Write out the guide rows */ writeRowCopies(outPamP, outrow, guidesize); /* Put some white rows after the guides */ writeWhiteRows(outPamP, guidesize); pnm_freerow(outrow); } static void makeStereoRow(const struct pam * const inPamP, tuple * const inRow, unsigned int * const same, double const depthOfField, double const eyesep, unsigned int const dpi, unsigned int const optWidth, unsigned int const smoothing) { /* Do the bulk of the work. See the paper cited above for code * comments. All I (Scott) did was transcribe the code and make * minimal changes for Netpbm. And some style changes by Bryan to * match Netpbm style. */ #define Z(X) (inRow[X][0]/(double)inPamP->maxval) unsigned int const pixelEyesep = ROUNDU(eyesep * dpi); /* Separation in pixels between the viewer's eyes */ unsigned int col; for (col = 0; col < inPamP->width; ++col) same[col] = col; for (col = 0; col < inPamP->width; ++col) { unsigned int const s = separation(Z(col), eyesep, dpi, depthOfField); if (col >= s/2 && col + s/2 < inPamP->width) { unsigned int left, right; bool visible; unsigned int t; double zt; left = col - s/2; /* initial value */ right = col + s/2; /* initial value */ t = 1; /* initial value */ do { double const dof = depthOfField; zt = Z(col) + 2.0*(2.0 - dof*Z(col))*t/(dof*pixelEyesep); assert(col >= t); assert(col + t < inPamP->width); visible = Z(col-t) < zt && Z(col+t) < zt; ++t; } while (visible && zt < 1); if (visible) { unsigned int l; l = same[left]; while (l != left && l != right) { if (l < right) { left = l; l = same[left]; } else { same[left] = right; left = right; l = same[left]; right = l; } } same[left] = right; } } } /* If smoothing is enabled, replace each non-duplicate pixel with the pixel adjacent to its right neighbor. */ if (smoothing > 0) { int const baseCol = inPamP->width - optWidth - 1; int col; for (col = inPamP->width - 1; col >= 0; --col) same[col] = same[same[col]]; for (col = baseCol; col >= 0; --col) { if (same[col] == col) same[col] = same[col+1] - 1; } } } static void makeMaskRow(const struct pam * const outPamP, const unsigned int * const same, const tuple * const outRow) { int col; for (col = outPamP->width-1; col >= 0; --col) { bool const duplicate = (same[col] != col); unsigned int plane; for (plane = 0; plane < outPamP->depth; ++plane) outRow[col][plane] = duplicate ? outPamP->maxval : 0; } } static void computeFixedPoint(const unsigned int * const same, unsigned int * const sameFp, unsigned int const width) { /*---------------------------------------------------------------------------- Compute the fixed point of same[] (i.e., sameFp[x] is same[same[same[...[same[x]]...]]]). -----------------------------------------------------------------------------*/ int col; for (col = width-1; col >= 0; --col) { if (same[col] != col) sameFp[col] = sameFp[same[col]]; else { if (col < width-1) sameFp[col] = sameFp[col + 1] - 1; else sameFp[col] = col; } } } static void averageFromPattern(struct pam * const pamP, tuple const bgColor, const tuple * const textureRow, const unsigned int * const same, unsigned int * const sameFp, const tuple * const outRow, unsigned int * const tuplesInCol) { /*---------------------------------------------------------------------------- Average the color of each non-background pattern tuple to every column that should have the same color. -----------------------------------------------------------------------------*/ int col; /* Initialize the tuple sums to zero. */ for (col = 0; col < pamP->width; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) outRow[col][plane] = 0; tuplesInCol[col] = 0; } /* Accumulate the color of each non-background pattern tuple to every column that should have the same color. */ for (col = pamP->width-1; col >= 0; --col) { tuple const onetuple = textureRow[(col+same[col])/2]; unsigned int const targetcol = sameFp[col]; int eqcol; if (!pnm_tupleequal(pamP, onetuple, bgColor)) { for (eqcol = pamP->width-1; eqcol >= 0; --eqcol) { if (sameFp[eqcol] == targetcol) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) outRow[eqcol][plane] += onetuple[plane]; tuplesInCol[eqcol]++; } } } } /* Take the average of all colors associated with each column. Tuples that can be any color are assigned the same color as was previously assigned to their fixed-point column. */ for (col = 0; col < pamP->width; ++col) { if (tuplesInCol[col] > 0) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) outRow[col][plane] /= tuplesInCol[col]; } else pnm_assigntuple(pamP, outRow[col], bgColor); } } static void smoothOutSpeckles(struct pam * const pamP, tuple const bgColor, unsigned int const smoothing, unsigned int * const tuplesInCol, tuple * const rowBuffer, const tuple * const outRow) { /*---------------------------------------------------------------------------- Smooth out small speckles of the background color lying between other colors. -----------------------------------------------------------------------------*/ unsigned int i; for (i = 0; i < smoothing; ++i) { int col; tuple * const scratchrow = rowBuffer; for (col = pamP->width-2; col >= 1; --col) { if (tuplesInCol[col] == 0) { /* Replace a background tuple with the average of its left and right neighbors. */ unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) scratchrow[col][plane] = 0; if (!pnm_tupleequal(pamP, outRow[col-1], bgColor)) { for (plane = 0; plane < pamP->depth; ++plane) scratchrow[col][plane] += outRow[col-1][plane]; ++tuplesInCol[col]; } if (!pnm_tupleequal(pamP, outRow[col+1], bgColor)) { for (plane = 0; plane < pamP->depth; ++plane) scratchrow[col][plane] += outRow[col+1][plane]; ++tuplesInCol[col]; } if (tuplesInCol[col] > 0) for (plane = 0; plane < pamP->depth; ++plane) scratchrow[col][plane] /= tuplesInCol[col]; else pnm_assigntuple(pamP, scratchrow[col], outRow[col]); } else pnm_assigntuple(pamP, scratchrow[col], outRow[col]); } for (col = 1; col < pamP->width-1; ++col) pnm_assigntuple(pamP, outRow[col], scratchrow[col]); } } static void replaceRemainingBackgroundWithPattern(outGenerator * const outGenP, const unsigned int * const same, unsigned int const row, const tuple * const outRow) { const struct pam * const pamP = &outGenP->pam; tuple const bgColor = outGenP->textureP->bgColor; if (outGenP->textureP->replaceBgColor) { int col; for (col = outGenP->pam.width-1; col >= 0; --col) { if (pnm_tupleequal(pamP, outRow[col], bgColor)) { bool const duplicate = (same[col] != col); tuple newtuple; if (duplicate) { assert(same[col] > col); assert(same[col] < outGenP->pam.width); newtuple = outRow[same[col]]; } else newtuple = outGenP->getTuple(outGenP, col, row); pnm_assigntuple(pamP, outRow[col], newtuple); } } } } static void makeImageRowMts(outGenerator * const outGenP, unsigned int const row, const unsigned int * const same, unsigned int * const sameFp, tuple * const rowBuffer, const tuple * const outRow) { /*---------------------------------------------------------------------------- Make a row of a mapped-texture stereogram. -----------------------------------------------------------------------------*/ unsigned int * tuplesInCol; /* tuplesInCol[C] is the number of tuples averaged together to make Column C. */ MALLOCARRAY(tuplesInCol, outGenP->pam.width); if (tuplesInCol == NULL) pm_error("Unable to allocate space for \"tuplesInCol\" array."); assert(outGenP->textureP); /* This is an original algorithm by Scott Pakin. */ /* Compute the fixed point of same[] (i.e., sameFp[x] is same[same[same[...[same[x]]...]]]). */ computeFixedPoint(same, sameFp, outGenP->pam.width); /* Average the color of each non-background pattern tuple to every column that should have the same color. */ averageFromPattern(&outGenP->pam, outGenP->textureP->bgColor, outGenP->textureP->imageData[row], same, sameFp, outRow, tuplesInCol); /* Smooth out small speckles of the background color lying between other colors. */ smoothOutSpeckles(&outGenP->pam, outGenP->textureP->bgColor, outGenP->textureP->smoothing, tuplesInCol, rowBuffer, outRow); /* Replace any remaining background tuples with a pattern tuple. */ replaceRemainingBackgroundWithPattern(outGenP, same, row, outRow); free(tuplesInCol); } static void makeImageRow(outGenerator * const outGenP, unsigned int const row, const unsigned int * const same, const tuple * const outRow) { /*---------------------------------------------------------------------------- same[N] is one of two things: same[N] == N means to generate a value for Column N independent of other columns in the row. same[N] > N means Column N should be identical to Column same[N]. same[N] < N is not allowed. -----------------------------------------------------------------------------*/ int col; for (col = outGenP->pam.width-1; col >= 0; --col) { bool const duplicate = (same[col] != col); /* This column is a duplicate of an earlier (farther to the right) column, to wit Column same[col] */ tuple newtuple; if (duplicate) { assert(same[col] > col); assert(same[col] < outGenP->pam.width); newtuple = outRow[same[col]]; } else newtuple = outGenP->getTuple(outGenP, col, row); pnm_assigntuple(&outGenP->pam, outRow[col], newtuple); } } static void invertHeightRow(const struct pam * const heightPamP, tuple * const tupleRow) { int col; for (col = 0; col < heightPamP->width; ++col) tupleRow[col][0] = heightPamP->maxval - tupleRow[col][0]; } static void makeImageRows(const struct pam * const inPamP, outGenerator * const outputGeneratorP, double const depthOfField, double const eyesep, unsigned int const dpi, bool const crossEyed, bool const makeMask, unsigned int const magnifypat, unsigned int const smoothing) { tuple * inRow; /* One row of pixels read from the height-map file */ tuple * outRow; /* One row of pixels to write to the height-map file */ unsigned int * same; /* Malloced array: same[N] is the column number of a pixel to the right forced to have the same color as the one in column N */ unsigned int * sameFp; /* Malloced array: Fixed point of same[] */ tuple * rowBuffer; /* Scratch row needed for texture manipulation */ unsigned int row; /* Current row in the input and output files */ inRow = pnm_allocpamrow(inPamP); outRow = pnm_allocpamrow(&outputGeneratorP->pam); MALLOCARRAY(same, inPamP->width); if (same == NULL) pm_error("Unable to allocate space for \"same\" array."); MALLOCARRAY(sameFp, inPamP->width); if (sameFp == NULL) pm_error("Unable to allocate space for \"sameFp\" array."); rowBuffer = pnm_allocpamrow(&outputGeneratorP->pam); for (row = 0; row < inPamP->height; ++row) { pnm_readpamrow(inPamP, inRow); if (crossEyed) /* Invert heights for cross-eyed (as opposed to wall-eyed) people. */ invertHeightRow(inPamP, inRow); /* Determine color constraints. */ makeStereoRow(inPamP, inRow, same, depthOfField, eyesep, dpi, ROUNDU(eyesep * dpi)/(magnifypat * 2), smoothing); if (makeMask) makeMaskRow(&outputGeneratorP->pam, same, outRow); else { if (outputGeneratorP->textureP) makeImageRowMts(outputGeneratorP, row, same, sameFp, rowBuffer, outRow); else makeImageRow(outputGeneratorP, row, same, outRow); } /* Write the resulting row. */ pnm_writepamrow(&outputGeneratorP->pam, outRow); } pnm_freepamrow(rowBuffer); free(sameFp); free(same); pnm_freepamrow(outRow); pnm_freepamrow(inRow); } static void produceStereogram(FILE * const ifP, struct cmdlineInfo const cmdline) { struct pam inPam; /* PAM information for the height-map file */ outGenerator * outputGeneratorP; /* Handle of an object that generates background pixels */ pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type)); createoutputGenerator(cmdline, &inPam, &outputGeneratorP); if (cmdline.verbose) { reportImageParameters("Input (height map) file", &inPam); if (inPam.depth > 1) pm_message("Ignoring all but the first plane of input."); reportImageParameters("Output (stereogram) file", &outputGeneratorP->pam); } pnm_writepaminit(&outputGeneratorP->pam); if (cmdline.guidetop) drawguides(cmdline.guidesize, &outputGeneratorP->pam, cmdline.eyesep, cmdline.dpi, cmdline.depth); makeImageRows(&inPam, outputGeneratorP, cmdline.depth, cmdline.eyesep, cmdline.dpi, cmdline.crosseyed, cmdline.makemask, cmdline.magnifypat, cmdline.smoothing); if (cmdline.guidebottom) drawguides(cmdline.guidesize, &outputGeneratorP->pam, cmdline.eyesep, cmdline.dpi, cmdline.depth); if (cmdline.texfile) { pnm_freepamarray(outputGeneratorP->textureP->imageData, &outputGeneratorP->textureP->pam); free(outputGeneratorP->textureP); } destroyoutputGenerator(outputGeneratorP); } static void reportParameters(struct cmdlineInfo const cmdline) { unsigned int const pixelEyesep = ROUNDU(cmdline.eyesep * cmdline.dpi); unsigned int const sep0 = separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth); unsigned int const sep1 = separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth); pm_message("Eye separation: %.4g inch * %d DPI = %u pixels", cmdline.eyesep, cmdline.dpi, pixelEyesep); if (cmdline.magnifypat > 1) pm_message("Background magnification: %uX * %uX", cmdline.magnifypat, cmdline.magnifypat); pm_message("\"Optimal\" (far) pattern width: %u / (%u * 2) = %u pixels", pixelEyesep, cmdline.magnifypat, pixelEyesep/(cmdline.magnifypat * 2)); pm_message("Near pattern width: %u / %u = %u pixels", sep1, cmdline.magnifypat, sep1 / cmdline.magnifypat); pm_message("Unique 3-D depth levels possible: %u", sep0 - sep1 + 1); if (cmdline.patfile && (cmdline.xshift || cmdline.yshift)) pm_message("Pattern shift: (%u, %u)", cmdline.xshift, cmdline.yshift); } int main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; /* Parsed command line */ FILE * ifP; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); if (cmdline.verbose) reportParameters(cmdline); srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); ifP = pm_openr(cmdline.inputFilespec); /* Produce a stereogram. */ produceStereogram(ifP, cmdline); pm_close(ifP); return 0; } advanced/generator/pgmmake.c0000644000175000001440000000640512347602010015237 0ustar ericusers#include "pm_c_util.h" #include "mallocvar.h" #include "shhopt.h" #include "pgm.h" struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ gray grayLevel; unsigned int cols; unsigned int rows; gray maxval; }; static void parseCommandLine(int argc, char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int maxvalSpec; unsigned int option_def_index; MALLOCARRAY(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); opt.opt_table = option_def; opt.short_allowed = false; /* We have no short (old-fashioned) options */ opt.allowNegNum = false; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ free (option_def); if (!maxvalSpec) cmdlineP->maxval = PGM_MAXMAXVAL; else { if (cmdlineP->maxval > PGM_OVERALLMAXVAL) pm_error("The value you specified for -maxval (%u) is too big. " "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL); if (cmdlineP->maxval < 1) pm_error("You cannot specify 0 for -maxval"); } if (argc-1 < 3) pm_error("Need 3 arguments: gray level, width, height."); else if (argc-1 > 3) pm_error("Only 3 arguments allowed: gray level, width, height. " "You specified %d", argc-1); else { double const grayLevel = atof(argv[1]); if (grayLevel < 0.0) pm_error("You can't have a negative gray level (%f)", grayLevel); if (grayLevel > 1.0) pm_error("Gray level must be in the range [0.0, 1.0]. " "You specified %f", grayLevel); cmdlineP->grayLevel = ROUNDU(grayLevel * cmdlineP->maxval); cmdlineP->cols = pm_parse_width(argv[2]); cmdlineP->rows = pm_parse_height(argv[3]); } } int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; gray * grayrow; unsigned int col, row; pgm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0); grayrow = pgm_allocrow(cmdline.cols); /* All rows are identical. Fill once. */ for (col = 0; col < cmdline.cols; ++col) grayrow[col] = cmdline.grayLevel; for (row = 0; row < cmdline.rows; ++row) pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0); pgm_freerow(grayrow); pm_close(stdout); return 0; } advanced/generator/pbmmake.c0000644000175000001440000001172312347602010015231 0ustar ericusers/* pbmmake.c - create a blank bitmap of a specified size * * Akira Urushibata ("Douso") wrote some of the core code that went * into the Netpbm 10.23 (July 2004) version of this program and licenses * that code to the public under GPL. * * Bryan Henderson wrote the rest of that version and contributed his * work to the public domain. * * See doc/HISTORY for a full history of this program. ** */ #include "pm_c_util.h" #include "shhopt.h" #include "mallocvar.h" #include "pbm.h" enum color {COLOR_BLACK, COLOR_WHITE, COLOR_GRAY}; struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ unsigned int width; unsigned int height; enum color color; }; static void parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optEntry *option_def; /* Instructions to pm_optParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; unsigned int blackOpt, whiteOpt, grayOpt; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "black", OPT_FLAG, NULL, &blackOpt, 0); OPTENT3(0, "white", OPT_FLAG, NULL, &whiteOpt, 0); OPTENT3(0, "gray", OPT_FLAG, NULL, &grayOpt, 0); OPTENT3(0, "grey", OPT_FLAG, NULL, &grayOpt, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (blackOpt + whiteOpt + grayOpt > 1) pm_error("You can specify only one of -black, -white, and -gray"); if (blackOpt) cmdlineP->color = COLOR_BLACK; else if (whiteOpt) cmdlineP->color = COLOR_WHITE; else if (grayOpt) cmdlineP->color = COLOR_GRAY; else cmdlineP->color = COLOR_WHITE; if (argc-1 != 2) pm_error("Wrong number of arguments (%d). There are two " "non-option arguments: width and height in pixels", argc-1); else { cmdlineP->width = pm_parse_width(argv[1]); cmdlineP->height = pm_parse_height(argv[2]); } } static void writeGrayRaster(unsigned int const cols, unsigned int const rows, FILE * const ofP) { unsigned int const lastCol = (cols-1)/8; unsigned char * bitrow0, * bitrow1; unsigned int i; bitrow0 = pbm_allocrow_packed(cols); bitrow1 = pbm_allocrow_packed(cols); for (i=0; i <= lastCol; ++i) { bitrow0[i] = (PBM_WHITE*0xaa) | (PBM_BLACK*0x55); bitrow1[i] = (PBM_WHITE*0x55) | (PBM_BLACK*0xaa); /* 0xaa = 10101010 ; 0x55 = 01010101 */ } if (cols % 8 > 0) { bitrow0[lastCol] >>= 8 - cols % 8; bitrow0[lastCol] <<= 8 - cols % 8; bitrow1[lastCol] >>= 8 - cols % 8; bitrow1[lastCol] <<= 8 - cols % 8; } if (rows > 1) { unsigned int row; for (row = 1; row < rows; row += 2) { pbm_writepbmrow_packed(ofP, bitrow0, cols, 0); pbm_writepbmrow_packed(ofP, bitrow1, cols, 0); } } if (rows % 2 == 1) pbm_writepbmrow_packed(ofP, bitrow0, cols, 0); pbm_freerow(bitrow0); pbm_freerow(bitrow1); } static void writeSingleColorRaster(unsigned int const cols, unsigned int const rows, bit const color, FILE * const ofP) { unsigned int const lastCol = (cols-1)/8; unsigned char * bitrow0; unsigned int i; bitrow0 = pbm_allocrow_packed(cols); for (i = 0; i <= lastCol; ++i) bitrow0[i] = color*0xff; if (cols % 8 > 0) { bitrow0[lastCol] >>= 8 - cols % 8; bitrow0[lastCol] <<= 8 - cols % 8; /* row end trimming, really not necessary with white */ } { unsigned int row; for (row = 0; row < rows; ++row) pbm_writepbmrow_packed(ofP, bitrow0, cols, 0); } pbm_freerow(bitrow0); } int main(int argc, char * argv[]) { struct cmdlineInfo cmdline; pbm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); pbm_writepbminit(stdout, cmdline.width, cmdline.height, 0); if (cmdline.color == COLOR_GRAY) writeGrayRaster(cmdline.width, cmdline.height, stdout); else { bit const color = cmdline.color == COLOR_WHITE ? PBM_WHITE : PBM_BLACK; writeSingleColorRaster(cmdline.width, cmdline.height, color, stdout); } pm_close(stdout); return 0; } advanced/generator/pbmpage.c0000644000175000001440000001645012347602010015232 0ustar ericusers/*************************************************************************** pbmpage This program produces a printed page test pattern in PBM format. This was adapted from Tim Norman's 'pbmtpg' program, part of his 'pbm2ppa' package, by Bryan Henderson on 2000.05.01. The only change was to make it use the Netpbm libraries to generate the output. For copyright and licensing information, see the pbmtoppa program, which was also derived from the same package. ****************************************************************************/ #include #include #include #include #include "pm_c_util.h" #include "pbm.h" /* US is 8.5 in by 11 in */ #define USWIDTH (5100) #define USHEIGHT (6600) /* A4 is 210 mm by 297 mm == 8.27 in by 11.69 in */ #define A4WIDTH (4960) #define A4HEIGHT (7016) struct bitmap { unsigned int Width; /* width and height in 600ths of an inch */ unsigned int Height; bit ** bitmap; }; static struct bitmap bitmap; static void setpixel(unsigned int const x, unsigned int const y, unsigned int const c) { char const bitmask = 128 >> (x % 8); if (x < 0 || x >= bitmap.Width) return; if (y < 0 || y >= bitmap.Height) return; if (c) bitmap.bitmap[y][x/8] |= bitmask; else bitmap.bitmap[y][x/8] &= ~bitmask; } static void setplus(unsigned int const x, unsigned int const y, unsigned int const s) { /*---------------------------------------------------------------------------- Draw a black plus sign centered at (x,y) with arms 's' pixels long. Leave the exact center of the plus white. -----------------------------------------------------------------------------*/ unsigned int i; for (i = 0; i < s; ++i) { setpixel(x+i, y, 1); setpixel(x-i, y, 1); setpixel(x, y+i, 1); setpixel(x, y-i, 1); } } static void setblock(unsigned int const x, unsigned int const y, unsigned int const s) { unsigned int i; for (i = 0; i < s; ++i) { unsigned int j; for (j = 0; j < s; ++j) setpixel(x+i, y+j, 1); } } static void setchar(unsigned int const x, unsigned int const y, char const c) { static char const charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e }, { 0x00, 0x42, 0x7f, 0x40, 0x00 }, { 0x42, 0x61, 0x51, 0x49, 0x46 }, { 0x22, 0x41, 0x49, 0x49, 0x36 }, { 0x18, 0x14, 0x12, 0x7f, 0x10 }, { 0x27, 0x45, 0x45, 0x45, 0x39 }, { 0x3e, 0x49, 0x49, 0x49, 0x32 }, { 0x01, 0x01, 0x61, 0x19, 0x07 }, { 0x36, 0x49, 0x49, 0x49, 0x36 }, { 0x26, 0x49, 0x49, 0x49, 0x3e } }; if (c <= '9' && c >= '0') { unsigned int xo; for (xo = 0; xo < 5; ++xo) { unsigned int yo; for (yo = 0; yo < 8; ++yo) { if ((charmap[c-'0'][xo] >> yo) & 0x01) setblock(x + xo*3, y + yo*3, 3); } } } } static void setstring(unsigned int const x, unsigned int const y, const char * const s) { const char * p; unsigned int xo; for (xo = 0, p = s; *p; xo += 21, ++p) setchar(x + xo, y, *p); } static void setCG(unsigned int const x, unsigned int const y) { unsigned int xo; for (xo = 0; xo <= 50; ++xo) { unsigned int const yo = sqrt(SQR(50.0) - SQR(xo)); unsigned int zo; setpixel(x + xo, y + yo, 1); setpixel(x+yo, y + xo, 1); setpixel(x-1-xo, y-1-yo, 1); setpixel(x-1-yo, y-1-xo, 1); setpixel(x+xo, y-1-yo, 1); setpixel(x-1-xo, y+yo, 1); for(zo = 0; zo < yo; ++zo) { setpixel(x + xo, y-1-zo, 1); setpixel(x-1-xo, y+zo, 1); } } } static void outputPbm(FILE * const ofP, struct bitmap const bitmap) { /*---------------------------------------------------------------------------- Create a pbm file containing the image from the global variable bitmap[]. -----------------------------------------------------------------------------*/ int const forceplain = 0; unsigned int row; pbm_writepbminit(ofP, bitmap.Width, bitmap.Height, forceplain); for (row = 0; row < bitmap.Height; ++row) { pbm_writepbmrow_packed(ofP, bitmap.bitmap[row], bitmap.Width, forceplain); } } static void framePerimeter(unsigned int const Width, unsigned int const Height) { unsigned int x, y; /* Top edge */ for (x = 0; x < Width; ++x) setpixel(x, 0, 1); /* Bottom edge */ for (x = 0; x < Width; ++x) setpixel(x, Height-1, 1); /* Left edge */ for (y = 0; y < Height; ++y) setpixel(0, y, 1); /* Right edge */ for (y = 0; y < Height; ++y) setpixel(Width-1, y, 1); } int main(int argc, const char** argv) { int TP; unsigned int x, y; char buf[128]; /* width and height in 600ths of an inch */ unsigned int Width; unsigned int Height; pm_proginit(&argc, argv); if (argc > 1 && strcmp(argv[1], "-a4") == 0) { Width = A4WIDTH; Height = A4HEIGHT; --argc; ++argv; } else { Width = USWIDTH; Height = USHEIGHT; } if (argc > 1) TP = atoi(argv[1]); else TP = 1; bitmap.Width = Width; bitmap.Height = Height; bitmap.bitmap = pbm_allocarray_packed(Width, bitmap.Height); for (y = 0; y < bitmap.Height; ++y) { unsigned int x; for (x = 0; x < pbm_packed_bytes(bitmap.Width); ++x) bitmap.bitmap[y][x] = 0x00; } switch (TP) { case 1: framePerimeter(Width, Height); for (x = 0; x < Width; x += 100) { unsigned int y; for(y = 0; y < Height; y += 100) setplus(x, y, 4); } for(x = 0; x < Width; x += 100) { sprintf(buf,"%d", x); setstring(x + 3, (Height/200) * 100 + 3, buf); } for (y = 0; y < Height; y += 100) { sprintf(buf, "%d", y); setstring((Width/200) * 100 + 3, y + 3, buf); } for (x = 0; x < Width; x += 10) for (y = 0; y < Height; y += 100) setplus(x, y, ((x%100) == 50) ? 2 : 1); for (x = 0; x < Width; x += 100) { unsigned int y; for (y = 0; y < Height; y += 10) setplus(x, y, ((y%100) == 50) ? 2 : 1); } setCG(Width/2, Height/2); break; case 2: for (y = 0; y < 300; ++y) setpixel(Width/2, Height/2-y, 1); break; case 3: for (y = 0; y < 300; ++y) { setpixel(y, y, 1); setpixel(Width-1-y, Height-1-y, 1); } break; default: pm_error("unknown test pattern (%d)", TP); } outputPbm(stdout, bitmap); pbm_freearray(bitmap.bitmap, Height); pm_close(stdout); return 0; } advanced/generator/ppmpat.c0000644000175000001440000010610212347602010015112 0ustar ericusers/* ppmpat.c - make a pixmap ** ** 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. */ #define _XOPEN_SOURCE /* get M_PI in math.h */ #include #include #include #include "pm_c_util.h" #include "mallocvar.h" #include "shhopt.h" #include "ppm.h" #include "ppmdraw.h" typedef enum { PAT_GINGHAM2, PAT_GINGHAM3, PAT_MADRAS, PAT_TARTAN, PAT_POLES, PAT_SQUIG, PAT_CAMO, PAT_ANTICAMO } pattern; struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ pattern basePattern; unsigned int width; unsigned int height; unsigned int randomseed; unsigned int randomseedSpec; }; static void parseCommandLine(int argc, const char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; unsigned int basePatternCount; unsigned int gingham2; unsigned int gingham3; unsigned int madras; unsigned int tartan; unsigned int poles; unsigned int squig; unsigned int camo; unsigned int anticamo; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "gingham2", OPT_FLAG, NULL, &gingham2, 0); OPTENT3(0, "g2", OPT_FLAG, NULL, &gingham2, 0); OPTENT3(0, "gingham3", OPT_FLAG, NULL, &gingham3, 0); OPTENT3(0, "g3", OPT_FLAG, NULL, &gingham3, 0); OPTENT3(0, "madras", OPT_FLAG, NULL, &madras, 0); OPTENT3(0, "tartan", OPT_FLAG, NULL, &tartan, 0); OPTENT3(0, "poles", OPT_FLAG, NULL, &poles, 0); OPTENT3(0, "squig", OPT_FLAG, NULL, &squig, 0); OPTENT3(0, "camo", OPT_FLAG, NULL, &camo, 0); OPTENT3(0, "anticamo", OPT_FLAG, NULL, &anticamo, 0); OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ basePatternCount = gingham2 + gingham3 + madras + tartan + poles + squig + camo + anticamo; if (basePatternCount < 1) pm_error("You must specify a base pattern option such as -gingham2"); else if (basePatternCount > 1) pm_error("You may not specify more than one base pattern option. " "You specified %u", basePatternCount); else { if (gingham2) cmdlineP->basePattern = PAT_GINGHAM2; else if (gingham3) cmdlineP->basePattern = PAT_GINGHAM3; else if (madras) cmdlineP->basePattern = PAT_MADRAS; else if (tartan) cmdlineP->basePattern = PAT_TARTAN; else if (poles) cmdlineP->basePattern = PAT_POLES; else if (squig) cmdlineP->basePattern = PAT_SQUIG; else if (camo) cmdlineP->basePattern = PAT_CAMO; else if (anticamo) cmdlineP->basePattern = PAT_ANTICAMO; else assert(false); /* Every possibility is accounted for */ } if (argc-1 != 2) pm_error("You must specify 2 non-option arguments: width and height " "in pixels. You specified %u", argc-1); else { cmdlineP->width = atoi(argv[1]); cmdlineP->height = atoi(argv[2]); if (cmdlineP->width < 1) pm_error("Width must be at least 1 pixel"); if (cmdlineP->height < 1) pm_error("Height must be at least 1 pixel"); } free(option_def); } static void validateComputableDimensions(unsigned int const cols, unsigned int const rows) { /* Notes on width and height limits: cols * 3, rows * 3 appear in madras, tartan cols*rows appears in poles cols+rows appears in squig PPMD functions use signed integers for pixel positions (because they allow you to specify points off the canvas). */ if (cols > INT_MAX/4 || rows > INT_MAX/4 || rows > INT_MAX/cols) pm_error("Width and/or height are way too large: %u x %u", cols, rows); } static pixel randomColor(pixval const maxval) { pixel p; PPM_ASSIGN(p, rand() % (maxval + 1), rand() % (maxval + 1), rand() % (maxval + 1) ); return p; } #define DARK_THRESH 0.25 static pixel randomBrightColor(pixval const maxval) { pixel p; do { p = randomColor(maxval); } while (PPM_LUMIN(p) <= maxval * DARK_THRESH); return p; } static pixel randomDarkColor(pixval const maxval) { pixel p; do { p = randomColor(maxval); } while (PPM_LUMIN(p) > maxval * DARK_THRESH); return p; } static pixel averageTwoColors(pixel const p1, pixel const p2) { pixel p; PPM_ASSIGN(p, (PPM_GETR(p1) + PPM_GETR(p2)) / 2, (PPM_GETG(p1) + PPM_GETG(p2)) / 2, (PPM_GETB(p1) + PPM_GETB(p2)) / 2); return p; } static ppmd_drawproc average_drawproc; static void average_drawproc(pixel ** const pixels, int const cols, int const rows, pixval const maxval, int const col, int const row, const void * const clientdata) { if (col >= 0 && col < cols && row >= 0 && row < rows) pixels[row][col] = averageTwoColors(pixels[row][col], *((const pixel*) clientdata)); } /*---------------------------------------------------------------------------- Camouflage stuff -----------------------------------------------------------------------------*/ static pixel randomAnticamoColor(pixval const maxval) { int v1, v2, v3; pixel p; v1 = (maxval + 1) / 4; v2 = (maxval + 1) / 2; v3 = 3 * v1; switch (rand() % 15) { case 0: case 1: PPM_ASSIGN(p, rand() % v1 + v3, rand() % v2, rand() % v2); break; case 2: case 3: PPM_ASSIGN(p, rand() % v2, rand() % v1 + v3, rand() % v2); break; case 4: case 5: PPM_ASSIGN(p, rand() % v2, rand() % v2, rand() % v1 + v3); break; case 6: case 7: case 8: PPM_ASSIGN(p, rand() % v2, rand() % v1 + v3, rand() % v1 + v3); break; case 9: case 10: case 11: PPM_ASSIGN(p, rand() % v1 + v3, rand() % v2, rand() % v1 + v3); break; case 12: case 13: case 14: PPM_ASSIGN(p, rand() % v1 + v3, rand() % v1 + v3, rand() % v2); break; } return p; } static pixel randomCamoColor(pixval const maxval) { int const v1 = (maxval + 1 ) / 8; int const v2 = (maxval + 1 ) / 4; int const v3 = (maxval + 1 ) / 2; pixel p; switch (rand() % 10) { case 0: case 1: case 2: /* light brown */ PPM_ASSIGN(p, rand() % v3 + v3, rand() % v3 + v2, rand() % v3 + v2); break; case 3: case 4: case 5: /* dark green */ PPM_ASSIGN(p, rand() % v2, rand() % v2 + 3 * v1, rand() % v2); break; case 6: case 7: /* brown */ PPM_ASSIGN(p, rand() % v2 + v2, rand() % v2, rand() % v2); break; case 8: case 9: /* dark brown */ PPM_ASSIGN(p, rand() % v1 + v1, rand() % v1, rand() % v1); break; } return p; } static float rnduni(void) { return rand() % 32767 / 32767.0; } static void clearBackground(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, bool const antiflag) { pixel color; if (antiflag) color = randomAnticamoColor(maxval); else color = randomCamoColor(maxval); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC, &color); } static void camoFill(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, struct fillobj * const fh, bool const antiflag) { pixel color; if (antiflag) color = randomAnticamoColor(maxval); else color = randomCamoColor(maxval); ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color); } #define BLOBRAD 50 #define MIN_POINTS 7 #define MAX_POINTS 13 #define MIN_ELLIPSE_FACTOR 0.5 #define MAX_ELLIPSE_FACTOR 2.0 #define MIN_POINT_FACTOR 0.5 #define MAX_POINT_FACTOR 2.0 static void computeXsYs(int * const xs, int * const ys, unsigned int const cols, unsigned int const rows, unsigned int const pointCt) { unsigned int const cx = rand() % cols; unsigned int const cy = rand() % rows; double const a = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) + MIN_ELLIPSE_FACTOR; double const b = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) + MIN_ELLIPSE_FACTOR; double const theta = rnduni() * 2.0 * M_PI; unsigned int p; for (p = 0; p < pointCt; ++p) { double const c = rnduni() * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) + MIN_POINT_FACTOR; double const tx = a * sin(p * 2.0 * M_PI / pointCt); double const ty = b * cos(p * 2.0 * M_PI / pointCt); double const tang = atan2(ty, tx) + theta; xs[p] = MAX(0, MIN(cols-1, cx + BLOBRAD * c * sin(tang))); ys[p] = MAX(0, MIN(rows-1, cy + BLOBRAD * c * cos(tang))); } } static void camo(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, bool const antiflag) { unsigned int const n = (rows * cols) / (BLOBRAD * BLOBRAD) * 5; unsigned int i; clearBackground(pixels, cols, rows, maxval, antiflag); for (i = 0; i < n; ++i) { unsigned int const pointCt = rand() % (MAX_POINTS - MIN_POINTS + 1) + MIN_POINTS; int xs[MAX_POINTS], ys[MAX_POINTS]; int x0, y0; struct fillobj * fh; computeXsYs(xs, ys, cols, rows, pointCt); x0 = (xs[0] + xs[pointCt - 1]) / 2; y0 = (ys[0] + ys[pointCt - 1]) / 2; fh = ppmd_fill_create(); ppmd_polyspline( pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0, ppmd_fill_drawproc, fh); camoFill(pixels, cols, rows, maxval, fh, antiflag); ppmd_fill_destroy(fh); } } /*---------------------------------------------------------------------------- Gingham stuff -----------------------------------------------------------------------------*/ static void gingham2(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval) { pixel const backcolor = randomDarkColor(maxval); pixel const forecolor = randomBrightColor(maxval); unsigned int const colso2 = cols / 2; unsigned int const rowso2 = rows / 2; /* Warp. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, colso2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, colso2, 0, cols - colso2, rows, PPMD_NULLDRAWPROC, &forecolor); /* Woof. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rowso2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rowso2, cols, rows - rowso2, average_drawproc, &forecolor); } static void gingham3(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval) { unsigned int const colso4 = cols / 4; unsigned int const rowso4 = rows / 4; pixel const backcolor = randomDarkColor(maxval); pixel const fore1color = randomBrightColor(maxval); pixel const fore2color = randomBrightColor(maxval); /* Warp. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, colso4, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, colso4, 0, colso4, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 2 * colso4, 0, colso4, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 3 * colso4, 0, cols - colso4, rows, PPMD_NULLDRAWPROC, &fore1color); /* Woof. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rowso4, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rowso4, cols, rowso4, average_drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 2 * rowso4, cols, rowso4, average_drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 3 * rowso4, cols, rows - rowso4, average_drawproc, &fore1color); } static void madras(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval) { unsigned int const cols2 = cols * 2 / 44; unsigned int const rows2 = rows * 2 / 44; unsigned int const cols3 = cols * 3 / 44; unsigned int const rows3 = rows * 3 / 44; unsigned int const cols12 = cols - 10 * cols2 - 4 * cols3; unsigned int const rows12 = rows - 10 * rows2 - 4 * rows3; unsigned int const cols6a = cols12 / 2; unsigned int const rows6a = rows12 / 2; unsigned int const cols6b = cols12 - cols6a; unsigned int const rows6b = rows12 - rows6a; pixel const backcolor = randomDarkColor(maxval); pixel const fore1color = randomBrightColor(maxval); pixel const fore2color = randomBrightColor(maxval); /* Warp. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, cols2, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, cols2 + cols3, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 2 * cols2 + cols3, 0, cols2, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 3 * cols2 + cols3, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 4 * cols2 + cols3, 0, cols6a, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 4 * cols2 + cols3 + cols6a, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 5 * cols2 + cols3 + cols6a, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 5 * cols2 + 2 * cols3 + cols6a, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 6 * cols2 + 2 * cols3 + cols6a, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 6 * cols2 + 3 * cols3 + cols6a, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 7 * cols2 + 3 * cols3 + cols6a, 0, cols6b, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 7 * cols2 + 3 * cols3 + cols6a + cols6b, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 8 * cols2 + 3 * cols3 + cols6a + cols6b, 0, cols2, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 9 * cols2 + 3 * cols3 + cols6a + cols6b, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color); /* Woof. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows2, cols, rows3, average_drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows2 + rows3, cols, rows2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 2 * rows2 + rows3, cols, rows2, average_drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 3 * rows2 + rows3, cols, rows2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 4 * rows2 + rows3, cols, rows6a, average_drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 4 * rows2 + rows3 + rows6a, cols, rows2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 5 * rows2 + rows3 + rows6a, cols, rows3, average_drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 5 * rows2 + 2 * rows3 + rows6a, cols, rows2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 6 * rows2 + 2 * rows3 + rows6a, cols, rows3, average_drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 6 * rows2 + 3 * rows3 + rows6a, cols, rows2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 7 * rows2 + 3 * rows3 + rows6a, cols, rows6b, average_drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 7 * rows2 + 3 * rows3 + rows6a + rows6b, cols, rows2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 8 * rows2 + 3 * rows3 + rows6a + rows6b, cols, rows2, average_drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 9 * rows2 + 3 * rows3 + rows6a + rows6b, cols, rows2, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 10 * rows2 + 3 * rows3 + rows6a + rows6b, cols, rows3, average_drawproc, &fore2color); } static void tartan(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval) { unsigned int const cols1 = cols / 22; unsigned int const rows1 = rows / 22; unsigned int const cols3 = cols * 3 / 22; unsigned int const rows3 = rows * 3 / 22; unsigned int const cols10 = cols - 3 * cols1 - 3 * cols3; unsigned int const rows10 = rows - 3 * rows1 - 3 * rows3; unsigned int const cols5a = cols10 / 2; unsigned int const rows5a = rows10 / 2; unsigned int const cols5b = cols10 - cols5a; unsigned int const rows5b = rows10 - rows5a; pixel const backcolor = randomDarkColor(maxval); pixel const fore1color = randomBrightColor(maxval); pixel const fore2color = randomBrightColor(maxval); /* Warp. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols5a, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, cols5a, 0, cols1, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, cols5a + cols1, 0, cols5b, rows, PPMD_NULLDRAWPROC, &backcolor ); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + cols1, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + cols1 + cols3, 0, cols1, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + 2 * cols1 + cols3, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + 2 * cols1 + 2 * cols3, 0, cols1, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + 3 * cols1 + 2 * cols3, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); /* Woof. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows5a, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows5a, cols, rows1, average_drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows5a + rows1, cols, rows5b, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + rows1, cols, rows3, average_drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + rows1 + rows3, cols, rows1, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + 2 * rows1 + rows3, cols, rows3, average_drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + 2 * rows1 + 2 * rows3, cols, rows1, average_drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + 3 * rows1 + 2 * rows3, cols, rows3, average_drawproc, &fore2color); } /*---------------------------------------------------------------------------- Poles stuff -----------------------------------------------------------------------------*/ #define MAXPOLES 500 static void placeAndColorPolesRandomly(int * const xs, int * const ys, pixel * const colors, unsigned int const cols, unsigned int const rows, pixval const maxval, unsigned int const poleCt) { unsigned int i; for (i = 0; i < poleCt; ++i) { xs[i] = rand() % cols; ys[i] = rand() % rows; colors[i] = randomBrightColor(maxval); } } static void assignInterpolatedColor(pixel * const resultP, pixel const color1, double const dist1, pixel const color2, double const dist2) { if (dist1 == 0) /* pixel is a pole */ *resultP = color1; else { double const sum = dist1 + dist2; pixval const r = (PPM_GETR(color1)*dist2 + PPM_GETR(color2)*dist1)/sum; pixval const g = (PPM_GETG(color1)*dist2 + PPM_GETG(color2)*dist1)/sum; pixval const b = (PPM_GETB(color1)*dist2 + PPM_GETB(color2)*dist1)/sum; PPM_ASSIGN(*resultP, r, g, b); } } static void poles(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval) { unsigned int const poleCt = MAX(2, MIN(MAXPOLES, cols * rows / 30000)); int xs[MAXPOLES], ys[MAXPOLES]; pixel colors[MAXPOLES]; unsigned int row; placeAndColorPolesRandomly(xs, ys, colors, cols, rows, maxval, poleCt); /* Interpolate points */ for (row = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) { double dist1, dist2; pixel color1, color2; unsigned int i; /* Find two closest poles. */ dist1 = dist2 = (SQR((double)cols) + SQR((double)rows)); for (i = 0; i < poleCt; ++i) { double const newdist = (double)((int)col - xs[i]) * ((int)col - xs[i]) + (double)((int)row - ys[i]) * ((int)row - ys[i]); if (newdist < dist1) { dist2 = dist1; color2 = color1; dist1 = newdist; color1 = colors[i]; } else if (newdist < dist2) { dist2 = newdist; color2 = colors[i]; } } assignInterpolatedColor(&pixels[row][col], color1, dist1, color2, dist2); } } } /*---------------------------------------------------------------------------- Squig stuff -----------------------------------------------------------------------------*/ #define SQUIGS 5 #define SQ_POINTS 7 #define SQ_MAXCIRCLE_POINTS 5000 static int sq_circlecount; static pixel sq_colors[SQ_MAXCIRCLE_POINTS]; static ppmd_point sq_offs[SQ_MAXCIRCLE_POINTS]; static void validateSquigAspect(unsigned int const cols, unsigned int const rows) { if (cols / rows >= 25 || rows / cols >= 25) pm_error("Image too narrow. Aspect ratio: %u/%u=%f " "is outside accepted range: 0.04 - 25.0", cols, rows, (float)cols/rows ); } static ppmd_point vectorSum(ppmd_point const a, ppmd_point const b) { return ppmd_makePoint(a.x + b.x, a.y + b.y); } static ppmd_drawprocp sqMeasureCircleDrawproc; static void sqMeasureCircleDrawproc(pixel** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p, const void * const clientdata) { sq_offs[sq_circlecount++] = p; } static ppmd_drawprocp sqRainbowCircleDrawproc; static void sqRainbowCircleDrawproc(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p, const void * const clientdata) { unsigned int i; for (i = 0; i < sq_circlecount; ++i) ppmd_point_drawprocp( pixels, cols, rows, maxval, vectorSum(p, sq_offs[i]), &sq_colors[i]); } static void sqAssignColors(unsigned int const circlecount, pixval const maxval, pixel * const colors) { pixel const rc1 = randomBrightColor(maxval); pixel const rc2 = randomBrightColor(maxval); pixel const rc3 = randomBrightColor(maxval); float const cco3 = (circlecount - 1) / 3.0; unsigned int i; for (i = 0; i < circlecount ; ++i) { if (i < cco3) { float const frac = (float)i/cco3; PPM_ASSIGN(colors[i], (float) PPM_GETR(rc1) + ((float) PPM_GETR(rc2) - (float) PPM_GETR(rc1)) * frac, (float) PPM_GETG(rc1) + ((float) PPM_GETG(rc2) - (float) PPM_GETG(rc1)) * frac, (float) PPM_GETB(rc1) + ((float) PPM_GETB(rc2) - (float) PPM_GETB(rc1)) * frac ); } else if (i < 2.0 * cco3) { float const frac = (float)i/cco3 - 1.0; PPM_ASSIGN(colors[i], (float) PPM_GETR(rc2) + ((float) PPM_GETR(rc3) - (float) PPM_GETR(rc2)) * frac, (float) PPM_GETG(rc2) + ((float) PPM_GETG(rc3) - (float) PPM_GETG(rc2)) * frac, (float) PPM_GETB(rc2) + ((float) PPM_GETB(rc3) - (float) PPM_GETB(rc2)) * frac ); } else { float const frac = (float)i/cco3 - 2.0; PPM_ASSIGN(colors[i], (float) PPM_GETR(rc3) + ((float) PPM_GETR(rc1) - (float) PPM_GETR(rc3)) * frac, (float) PPM_GETG(rc3) + ((float) PPM_GETG(rc1) - (float) PPM_GETG(rc3)) * frac, (float) PPM_GETB(rc3) + ((float) PPM_GETB(rc1) - (float) PPM_GETB(rc3)) * frac ); } } } static void clearImageToBlack(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval) { pixel color; PPM_ASSIGN(color, 0, 0, 0); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC, &color); } static void chooseWrapAroundPoint(unsigned int const cols, unsigned int const rows, ppmd_point * const pFirstP, ppmd_point * const pLastP, ppmd_point * const p0P, ppmd_point * const p1P, ppmd_point * const p2P, ppmd_point * const p3P) { switch (rand() % 4) { case 0: p1P->x = rand() % cols; p1P->y = 0; if (p1P->x < cols / 2) pFirstP->x = rand() % (p1P->x * 2 + 1); else pFirstP->x = cols - 1 - rand() % ((cols - p1P->x) * 2); pFirstP->y = rand() % rows; p2P->x = p1P->x; p2P->y = rows - 1; pLastP->x = 2 * p2P->x - pFirstP->x; pLastP->y = p2P->y - pFirstP->y; p0P->x = pLastP->x; p0P->y = pLastP->y - rows; p3P->x = pFirstP->x; p3P->y = pFirstP->y + rows; break; case 1: p2P->x = rand() % cols; p2P->y = 0; if (p2P->x < cols / 2) pLastP->x = rand() % (p2P->x * 2 + 1); else pLastP->x = cols - 1 - rand() % ((cols - p2P->x) * 2); pLastP->y = rand() % rows; p1P->x = p2P->x; p1P->y = rows - 1; pFirstP->x = 2 * p1P->x - pLastP->x; pFirstP->y = p1P->y - pLastP->y; p0P->x = pLastP->x; p0P->y = pLastP->y + rows; p3P->x = pFirstP->x; p3P->y = pFirstP->y - rows; break; case 2: p1P->x = 0; p1P->y = rand() % rows; pFirstP->x = rand() % cols; if (p1P->y < rows / 2) pFirstP->y = rand() % (p1P->y * 2 + 1); else pFirstP->y = rows - 1 - rand() % ((rows - p1P->y) * 2); p2P->x = cols - 1; p2P->y = p1P->y; pLastP->x = p2P->x - pFirstP->x; pLastP->y = 2 * p2P->y - pFirstP->y; p0P->x = pLastP->x - cols; p0P->y = pLastP->y; p3P->x = pFirstP->x + cols; p3P->y = pFirstP->y; break; case 3: p2P->x = 0; p2P->y = rand() % rows; pLastP->x = rand() % cols; if (p2P->y < rows / 2) pLastP->y = rand() % (p2P->y * 2 + 1); else pLastP->y = rows - 1 - rand() % ((rows - p2P->y) * 2); p1P->x = cols - 1; p1P->y = p2P->y; pFirstP->x = p1P->x - pLastP->x; pFirstP->y = 2 * p1P->y - pLastP->y; p0P->x = pLastP->x + cols; p0P->y = pLastP->y; p3P->x = pFirstP->x - cols; p3P->y = pFirstP->y; break; } } static void squig(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval) { int i; validateSquigAspect(cols, rows); clearImageToBlack(pixels, cols, rows, maxval); /* Draw the squigs. */ ppmd_setlinetype(PPMD_LINETYPE_NODIAGS); ppmd_setlineclip(0); for (i = SQUIGS; i > 0; --i) { unsigned int const radius = (cols + rows) / 2 / (25 + i * 2); ppmd_point c[SQ_POINTS]; ppmd_point p0, p1, p2, p3; sq_circlecount = 0; ppmd_circlep(pixels, cols, rows, maxval, ppmd_makePoint(0, 0), radius, sqMeasureCircleDrawproc, NULL); sqAssignColors(sq_circlecount, maxval, sq_colors); chooseWrapAroundPoint(cols, rows, &c[0], &c[SQ_POINTS-1], &p0, &p1, &p2, &p3); { /* Do the middle points */ unsigned int j; for (j = 1; j < SQ_POINTS - 1; ++j) { /* validateSquigAspect() assures that cols - 2 * radius, rows -2 * radius are positive */ c[j].x = (rand() % (cols - 2 * radius)) + radius; c[j].y = (rand() % (rows - 2 * radius)) + radius; } } ppmd_linep( pixels, cols, rows, maxval, p0, p1, sqRainbowCircleDrawproc, NULL); ppmd_polysplinep( pixels, cols, rows, maxval, p1, SQ_POINTS, c, p2, sqRainbowCircleDrawproc, NULL); ppmd_linep( pixels, cols, rows, maxval, p2, p3, sqRainbowCircleDrawproc, NULL); } } int main(int argc, const char ** argv) { struct cmdlineInfo cmdline; pixel ** pixels; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); validateComputableDimensions(cmdline.width, cmdline.height); srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); pixels = ppm_allocarray(cmdline.width, cmdline.height); switch (cmdline.basePattern) { case PAT_GINGHAM2: gingham2(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL); break; case PAT_GINGHAM3: gingham3(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL); break; case PAT_MADRAS: madras(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL); break; case PAT_TARTAN: tartan(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL); break; case PAT_POLES: poles(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL); break; case PAT_SQUIG: squig(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL); break; case PAT_CAMO: camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 0); break; case PAT_ANTICAMO: camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 1); break; default: pm_error("can't happen!"); } ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 0); ppm_freearray(pixels, cmdline.height); return 0; } advanced/doc/0000755000175000001440000000000012347602011012225 5ustar ericusersadvanced/doc/COPYRIGHT.PATENT0000644000175000001440000000364712347602011014524 0ustar ericusersNetpbm consists of code contributed by many authors. In most of the source code files, there is a copyright notice and license, telling you what you may or may not do with the code. All authors have granted you the right to use and distribute their code without having to pay them, as long as you meet some simple requirements. Most of the components require you to include a copy of their copyright notices and warranty disclaimers in any copies or derivations you distribute. Another restriction that some of the software has is that in order to have permission to copy it (which includes writing anything derived from it), you must distribute source code for your copy or derivation and propagate the same restriction to people who would copy your derivation. In other words, the price the author wants for the use of his proprietary work is your contribution to the free software cause. One component prohibits you from selling it or using it in a commercial way: hpcdtoppm. Some components are contributed to the public domain. The copyrights on individual components of this package are detailed at appropriate places within the package. A slightly out of date summary of all the copyrights is in the file 'doc/copyright_summary' in the Netpbm source tree. As with most public open source software, no one really knows for sure where the code came from. It is possible that a contributor copied it without license to do so. That might mean any user of the code owes someone royalties. The Netpbm maintainer in particular has received no warranties regarding any of the code in the package. So consider all the above to be modified by "to the best of the Netpbm maintainer's knowledge." Netpbm may practice valid patents, which would mean that you owe someone royalties if you use the code. This is a miniscule risk, though. What is known about patents related to Netpbm is in the file 'doc/patent_summary' in the Netpbm source tree. advanced/doc/copyright_summary0000644000175000001440000004632212347602011015744 0ustar ericusersHere is an analysys of Netpbm copyrights done by Steve McIntyre finishing on October 7, 2001. It is based on Netpbm 9.20, so it doesn't cover material added since then. Copyright: ========== Netpbm has a very complicated licensing setup, as it is a collection of hundreds of small utility programs, each with an individual copyright/license. From inspection of the source and documentation in the package, I believe the following programs are DFSG-free. They have a variety of licenses, which I have summarised below for convenience. A: The most common netpbm license - clearly the original from 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 core libp[gbnp]m library code all also seems to follow this license. B: The standard BSD license. See /usr/share/common-licenses/BSD C: GNU General Public License v2. See /usr/share/common-licenses/GPL. D: The Independent JPEG Group license: Permission is hereby granted to use, copy, modify, and distribute this software (or portions thereof) for any purpose, without fee, subject to these conditions: (1) If any part of the source code for this software is distributed, then this README file must be included, with this copyright and no-warranty notice unaltered; and any additions, deletions, or changes to the original files must be clearly indicated in accompanying documentation. (2) If only executable code is distributed, then the accompanying documentation must state that "this software is based in part on the work of the Independent JPEG Group". (3) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind. These conditions apply to any software derived from or based on the IJG code, not just to the unmodified library. If you use our work, you ought to acknowledge us. Permission is NOT granted for the use of any IJG author's name or company name in advertising or publicity relating to this software or products derived from it. This software may be referred to only as "the Independent JPEG Group's software". We specifically permit and encourage the use of this software as the basis of commercial products, provided that all warranty or liability claims are assumed by the product vendor. E: This software is copyrighted as noted below. It may be freely copied, modified, and redistributed, provided that the copyright notice is preserved on all copies. There is no warranty or other guarantee of fitness for this software, it is provided solely "as is". Bug reports or fixes may be sent to the author, who may or may not act on them as he desires. You may not include this software in a program or other software product without supplying the source, or without informing the end-user that the source is available for no extra charge. If you modify this software, you should include a notice giving the name of the person performing the modification, the date of modification, and the reason for such modification. F: The program and documentation may be freely distributed by anyone in source or binary format. Please clearly note any changes. G: Public domain: This software is in the public domain. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, without any conditions or restrictions. H: Public domain, slightly different wording: Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, without any conditions or restrictions. This software is provided "as is" without express or implied warranty. I: A variation on the BSD license Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the Smithsonian Astrophysical Observatory not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The Smithsonian Astrophysical Observatory makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE SMITHSONIAN ASTROPHYSICAL OBSERVATORY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE SMITHSONIAN ASTROPHYSICAL OBSERVATORY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 411toppm: (B) 2001 Steve Allen anytopnm: (A) 1991 Jef Poskanzer asciitopgm: (A) 1989 Wilson H. Bent, Jr atktopbm: (A) 1991 Bill Janssen bioradtopgm: (A) 1993 Oliver Trepte bmptoppm: (A) 1992 David W. Sanderson brushtopbm: (A) 1988 Jef Poskanzer cmuwmtopbm: (A) 1989 Jef Poskanzer eyuvtoppm: (B) 1995 The Regents of the University of California fiascotopnm: (C) 1994-2000 Ullrich Hafner fitstopnm: (A) 1989 Jef Poskanzer fstopgm: (A) 1989 Jef Poskanzer g3topbm: (A) 1989 Paul Haeberli gemtopbm: (A) 1988 Diomidis D. Spinellis gemtopnm: (A) 1988 Diomidis D. Spinellis giftopnm: (A) 1990-1993 David Koblas gouldtoppm: (A) 1990 Stephen P. Lesniewski hipstopgm: (A) 1989 Jef Poskanzer icontopbm: (A) 1988 Jef Poskanzer ilbmtoppm: (A) 1989 Jef Poskanzer imgtoppm: (A) 1989 Jef Poskanzer jbigtopnm: (C) 2000 Markus Kuhn jpegtopnm: (D) 1991-2000 Thomas G. Lane, Bryan Henderson leaftoppm: (A) 1994 Bill O'Donnell lispmtopgm: (A) 1988 Jef Poskanzer, Jamie Zawinski macptopbm: (A) 1988 Jef Poskanzer mdatopbm: (C) 1999 John Elliott mgrtopbm: (A) 1989 Jef Poskanzer mtvtoppm: (A) 1989 Jef Poskanzer neotoppm: (D) 2001 Teemu Hukkanen palmtopnm: (A) 1995-2000 Ian Goldberg, Bill Janssen pamcut: (A) 1989 Jef Poskanzer pamfile: (A) 1991 Jef Poskanzer pamoil: (A) 1990 Wilson Bent pbmclean: (A) 1989-1990 Jef Poskanzer, Angus Duggan pbmlife: (A) 1988-1991 Jef Poskanzer pbmmake: (A) 1988 Jef Poskanzer pbmmask: (A) 1989-1991 Jef Poskanzer pbmpage: (C) 1998 Tim Norman pbmpscale: (A) 1989-1990 Jef Poskanzer, Angus Duggan pbmreduce: (A) 1989 Jef Poskanzer pbmtext: (A) 1991 Jef Poskanzer pbmto10x: (A) 1990-1994 Ken Yap pbmtoascii: (A) 1988-1992 Jef Poskanzer pbmtoatk: (A) 1991 Bill Janssen pbmtobbnbg: (A) 1989 Mike Parker pbmtocmuwm: (A) 1989 Jef Poskanzer pbmtoepsi: (A) 1988 Jef Poskanzer pbmtoepson: (A) 1990 Jef Poskanzer, John Tiller pbmtog3: (A) 1989 Paul Haeberli pbmtogem: (A) 1988 Jef Poskanzer, David Beckemeyer pbmtogo: (A) 1988-1989 Jef Poskanzer, Michael Haberler, Bo Thide' pbmtoicon: (A) 1988 Jef Poskanzer pbmtolj: (A) 1988 Jef Poskanzer, Michael Haberler pbmtomacp: (A) 1988 Douwe vand der Schaaf pbmtomda: (C) 1999 John Elliott pbmtomgr: (A) 1989 Jef Poskanzer pbmtonokia: (A) 2001 OMS Open Media System GmbH, Tim Rhsen pbmtopgm: (A) 1989-1990 Jef Poskanzer, Angus Duggan pbmtopi3: (A) 1988 David Beckemeyer and Jef Poskanzer pbmtoplot: (A) 1990 Arthur David Olson pbmtoppa: (C) 1998 Tim Norman pbmtopsg3: (C) 2001 Kristof Koehler pbmtoptx: (A) 1988 Jef Poskanzer pbmtowbmp: (A) 1999 Terje Sannum pbmtox10bm: (A) 1988 Jef Poskanzer pbmtoxbm: (A) 1988 Jef Poskanzer pbmtoybm: (A) 1991 Jamie Zawinski and Jef Poskanzer pbmtozinc: (A) 1988 James Darrell McCauley , Jef Poskanzer pbmupc: (A) 1988 Jef Poskanzer pcxtoppm: (A) 1990 Michael Davidson pgmbentley: (A) 1990 Wilson Bent pgmcrater: (H) 1989 John Walker pgmedge: (A) 1989 Jef Poskanzer pgmenhance: (A) 1989-1991 Jef Poskanzer pgmhist: (A) 1989 Jef Poskanzer pgmkernel: (A) 1992 Alberto Accomazzi, Smithsonian Astrophysical Observatory pgmnoise: (A) 1993 Frank Neumann pgmnorm: (A) 1989-1991 Jef Poskanzer pgmramp: (A) 1989 Jef Poskanzer pgmslice: (C) 2000 Jos Dingjan pgmtexture: (A) 1991 Texas Agricultural Experiment Station, James Darrell McCauley pgmtofs: (A) 1991 Jef Poskanzer pgmtolispm: (A) 1991 Jamie Zawinski and Jef Poskanzer pgmtopbm: (A) 1989 Jef Poskanzer pgmtoppm: (A) 1991 Jef Poskanzer pi1toppm: (A) 1991 Steve Belczyk and Jef Poskanzer pi3topbm: (A) 1988 David Beckemeyer and Diomidis D. Spinellis picttoppm: (A) 1989-1993 George Phillips pjtoppm: (A) 1990 Christos Zoulas pngtopnm: (A) 1995-1998 Alexander Lehmann , Willem van Schaik pnmalias: (A) 1992 Alberto Accomazzi, Smithsonian Astrophysical Observatory pnmarith: (A) 1989-1991 Jef Poskanzer pnmcat: (A) 1989-1991 Jef Poskanzer pnmcomp: (A) 1992 David Koblas pnmconvol: (A) 1989-1995 Jef Poskanzer, Mike Burns pnmcrop: (A) 1988 Jef Poskanzer pnmcut: (A) 1989 Jef Poskanzer pnmdepth: (A) 1989-1991 Jef Poskanzer pnmenlarge: (A) 1989 Jef Poskanzer pnmfile: (A) 1991 Jef Poskanzer pnmflip: (A) 1989 Jef Poskanzer pnmgamma: (A) 1991 Jef Poskanzer and Bill Davidson pnmhisteq: (H) 1995 John Walker pnmhistmap: (A) 1993 Wilson H. Bent, Jr pnmindex: (A) 1991 Jef Poskanzer pnminterp: (C) 1998-2000 Russell Marks pnminterp-gen: (C) 1998-2000 Russell Marks pnminvert: (A) 1989 Jef Poskanzer pnmmargin: (A) 1991 Jef Poskanzer pnmmontage: (A) 2000 Ben Olmstead pnmnlfilt: (A) 1993 Graeme W. Gill pnmnoraw: (A) 1991 Jef Poskanzer pnmpad: (A) 1989-1990 Jef Poskanzer, Angus Duggan pnmpaste: (A) 1989 Jef Poskanzer pnmpsnr: (C) 1994-2000 Ullrich Hafner pnmrotate: (A) 1989-1991 Jef Poskanzer pnmscale: (A) 1989-1991 Jef Poskanzer pnmscalefixed: (A) 1989-1991 Jef Poskanzer pnmshear: (A) 1989-1991 Jef Poskanzer pnmsmooth: (A) 1984 Mike Burns pnmsplit: (?) 2000 Bryan Henderson pnmtile: (A) 1989 Jef Poskanzer pnmtoddif: (A) 1992 Digital Equipment Corporation, Burkhard Neidecker-Lutz pnmtofiasco: (C) 1994-2000 Ullrich Hafner pnmtofits: (C) 1989 Wilson H. Bent pnmtojbig: (C) 2000 Markus Kuhn pnmtojpeg: (D) 1991-2001 Thomas G. Lane, Bryan Henderson pnmtopalm: (A) 1995-2000 Ian Goldberg, Bill Janssen pnmtoplainpnm: (A) 1991 Jef Poskanzer pnmtopng: (A) 1995-1998 Alexander Lehmann , Willem van Schaik pnmtops: (A) 1989 Jef Poskanzer pnmtorast: (A) 1989-1991 Jef Poskanzer pnmtorle: (E) 1994 Minnesota Supercomputer Center, Inc, Wes Barris pnmtosgi: (A) 1994 Ingo Wilken pnmtosir: (A) 1991 Marvin Landis pnmtotiff: (A) 1990 Sun Microsystems, Inc, Jef Poskanzer pnmtotiffcmyk: (C) 1999 Andrew Cooke (Jara Software) pnmtoxwd: (A) 1989-1991 Jef Poskanzer ppm3d: (A) 1989 Jef Poskanzer ppmbrighten: (A) 1989-1990 Jef Poskanzer, Brian Moffet ppmchange: (A) 1991 Wilson H. Bent, Jr ppmcie: (H) 1995 John Walker ppmcolormask: (?) 2000 Bryan Henderson ppmdim: (A) 1993 Frank Neumann ppmdist: (A) 1993 Dan Stromberg ppmdither: (A) 1991 Christos Zoulas ppmfade: (?) 1994 Minnesota Supercomputer Center, Inc, Wes Barris ppmflash: (A) 1993 Frank Neumann ppmforge: (H) 1989 John Walker ppmhist: (A) 1989 Jef Poskanzer ppmlabel: (H) 1995 John Walker ppmmake: (A) 1989 Jef Poskanzer ppmmix: (A) 1993 Frank Neumann ppmnorm: (A) 1989-1991 Jef Poskanzer, Wilson H. Bent, Jr ppmntsc: (E) 1993 Minnesota Supercomputer Center, Inc, Wes Barris ppmpat: (A) 1989-1991 Jef Poskanzer ppmquant: (A) 1989-1991 Jef Poskanzer ppmquantall: (A) 1991 Jef Poskanzer ppmqvga: (F) 1991-1992 Bill Davidsen, Lyle Rains ppmrainbow: (A) 2001 Arjen Bax, Bryan Henderson ppmrelief: (A) 1990 Wilson H. Bent, Jr ppmshadow: (G) 1997 John Walker ppmshift: (A) 1993 Frank Neumann ppmspread: (A) 1993 Frank Neumann ppmtoacad: (H) 1991 John Walker ppmtobmp: (A) 1992 David W. Sanderson ppmtoeyuv: (B) 1995 The Regents of the University of California ppmtoicr: (A) 1990 Kanthan Pillay ppmtoilbm: (A) 1989 Jef Poskanzer ppmtoleaf: (A) 1994 Bill O'Donnell ppmtolj: (A) 2000 Jonathan Melvin ppmtomap: (A) 1989 Jef Poskanzer ppmtomitsu: (A) 1992-1993 S.Petra Zeidler ppmtompeg: (B) 1995 The Regents of the University of California ppmtoneo: (D) 2001 Teemu Hukkanen ppmtopcx: (B) 1994 Ingo Wilken ppmtopgm: (A) 1989 Jef Poskanzer ppmtopi1: (A) 1991 Jef Poskanzer and Steve Belczyk ppmtopict: (A) 1990 Ken Yap ppmtopj: (A) 1990 Christos Zoulas ppmtopuzz: (A) 1991 Jef Poskanzer ppmtorgb3: (A) 1991 Jef Poskanzer ppmtosixel: (A) 1991 Rick Vinci ppmtotga: (A) 1989-1991 Mark Shand and Jef Poskanzer ppmtouil: (A) 1990 Mark W. Snitily, Jef Poskanzer ppmtowinicon: (A) 2000 Lee Benfield ppmtoxpm: (A) 1990 Mark W. Snitily ppmtoyuv: (A) 1987-1991 Abekas Video Systems Inc, DHD PostImage Inc ppmtoyuvsplit: (A) 1993 Andre Beck ppmtv: (A) 1993 Frank Neumann psidtopgm: (A) 1989 Jef Poskanzer pstopnm: (I) 1992 Smithsonian Astrophysical Observatory, Alberto Accomazzi qrttoppm: (A) 1989 Jef Poskanzer rasttopnm: (A) 1989-1991 Jef Poskanzer rawtopgm: (A) 1989 Jef Poskanzer rawtoppm: (A) 1991 Jef Poskanzer rgb3toppm: (A) 1991 Jef Poskanzer rletopnm: (E) 1994 Minnesota Supercomputer Center, Inc, Wes Barris sbigtopgm: (A) 1998 John Walker sgitopnm: (A) 1994 Ingo Wilken sirtopnm: (A) 1991 Marvin Landis sldtoppm: (H) 1991 John Walker spctoppm: (A) 1991 Jef Poskanzer and Steve Belczyk sputoppm: (A) 1991 Jef Poskanzer and Steve Belczyk tgatoppm: (A) 1989 Jef Poskanzer tifftopnm: (A) 1990 Sun Microsystems, Inc, Jef Poskanzer thinkjettopbm: (A) 2001 W. Eric Norum wbmptopbm: (A) 1999 Terje Sannum winicontoppm: (A) 2000 Lee Benfield xbmtopbm: (A) 1988 Jef Poskanzer ximtoppm: (A) 1991 Jef Poskanzer xpmtoppm: (A) 1991 Jef Poskanzer xvminitoppm: (A) 1993 Ingo Wilken xwdtopnm: (A) 1989-1991 Jef Poskanzer ybmtopbm: (A) 1988 Jamie Zawinski and Jef Poskanzer yuvsplittoppm: (A) 1993 Marcel Wijkstra yuvtoppm: (A) 1987-1991 Jef Poskanzer, Abekas Video Systems Inc., DHD PostImage Inc. zeisstopnm: (A) 1993 Oliver Trepte ============================================================================== NON-FREE: The following programs have restrictions on their use or source: hpcdtoppm package: ================== hpcdtoppm pcdindex * Copyright (c) 1992, 1993, 1994 by Hadmut Danisch (danisch@ira.uka.de). * Permission to use and distribute this software and its * documentation for noncommercial use 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. It is not allowed to sell this software in * any way. This software is not public domain. Note that Hpcdtoppm and Pcdindex are not distributed in the main Netpbm package on Sourceforge, as Sourceforge does not offer distribution services for software licensed in this way. These programs are distributed in the supplemental Netpbm package on Ibiblio. filter to create GIFs - patent restricted. (http://www.unisys.com/unisys/lzw/) ppmtogif: (A) 1989 Jef Poskanzer The Unisys patent, at least in the US, expired in 2003. ============================================================================= UNKNOWN, SO NOT DISTRIBUTED: The following programs are in the upstream source package, but do not have sufficient copyright and license information to be redistributed: pamchannel: No copyright / license text found pamtopnm: No copyright / license text found pbmto4425: No copyright / license text found pbmtoln03: No copyright / license text found pbmtolps: No copyright / license text found pbmtopk: No copyright / license text found pktopbm: No copyright / license text found ppmtopjxl: No copyright / license text found spottopgm: No copyright / license text found advanced/doc/GPL_LICENSE.txt0000644000175000001440000004310112347602011014551 0ustar ericusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. advanced/doc/Netpbm.programming0000644000175000001440000003374012347602011015725 0ustar ericusersThis file is an attempt to give some basic guidelines for those who wish to write new Netpbm programs. The guidelines ensure: A) Your program functions consistently with the rest of the package. B) Your program works in myriad environments on which you can't test it. C) You don't miss an important detail of the Netpbm formats. D) Your program is immune to small changes in the Netpbm formats. The easiest way to write your own Netpbm program is to take an existing one, similar to the one you want to write, and to modify it. This saves a lot of time, and ensures conformity with these rules. But pick a recent one (check the file modification date and the doc/HISTORY file), because many things you see in old programs are grandfathered in. Pamtopnm is good example of a thoroughly modern Netpbm program. Pamcut is another good one. COLLECTION PARAMETERS --------------------- The philosophy that guides what goes into Netpbm and what doesn't is one of maximum distribution of function. Contributions to Netpbm are refused very rarely, and usually because there is already some better way to do what the contribution attempts to do, so that the contribution would just make the package more confusing and thus harder to use. The second most common reason for declining a contribution is that it falls well outside Netpbm's scope. That too would make the package more confusing and thus harder to use. "Nobody will use that" is not a reason for leaving something out of Netpbm. "That's different from how other Netpbm programs work" is similarly not an excuse for refusing distribution of a feature. (But it's a good reason to accept a change to make a feature consistent!) The standard for adding something to Netpbm isn't that it be perfect, or even a net improvement. The standard is that it not make Netpbm worse. Poor quality additions are made all the time, because a program that doesn't work well is usually no worse than no program at all. DEVELOPMENT PROCESS ------------------- Just email your code to the Netpbm maintainer. Base it on the most recent Netpbm Development release if possible. The preferred form for changes to existing files is a unified diff patch -- the conventional Unix means of communicating code changes. A Subversion 'svn diff' creates that easily. You should update or create documentation too. But if you don't, the Netpbm maintainer will do the update before releasing your code. The source files for the documentation are the HTML files in the 'userguide' directory of the Netpbm Subversion repository: http://netpbm.svn.sourceforge.net/svnroot/netpbm/userguide . The identical files are at http://netpbm.sourceforge.net/doc/ . There are some automated tests in the package - shell scripts in files named such as "pbmtog3.test". You can use those to verify your changes. You should also add to these tests and create new ones. But most developers don't. CODING GUIDELINES ----------------- The Netpbm maintainer accepts programs that do not meet these guidelines, so don't feel you need to hold back a contribution because you wrote it before you read these. * See the Netpbm library documentation to see what library functions you should be using and how. You can find it at: http://netpbm.sourceforge.net/doc/libnetpbm.html * See the specifications of the Netpbm formats at: http://netpbm.sourceforge.net/doc/pbm.html http://netpbm.sourceforge.net/doc/pgm.html http://netpbm.sourceforge.net/doc/ppm.html http://netpbm.sourceforge.net/doc/pam.html but don't depend very much on these; use the library functions instead to read and write the image files. * You should try to use the "pam" library functions, which are newer than the "pbm", "pgm", and "pnm" ones. The "pam" functions read and write all four Netpbm formats and make code easier to write and to read. * A new program should generate PAM output. Note that any program that uses the modern Netpbm library can read PAM even if it was designed for the older formats. For other programs, one can convert PAM to the older formats with 'pamtopnm'. * If your program involves transparency (alpha masks), you have two alternatives. The older method is to use a separate PGM file for the alpha mask, in the style of Pngtopnm/Pnmtopng and Giftopnm/Pnmtogif, and use command syntax like those programs. See the PGM format spec for details. A newer method involves a PAM image with an alpha plane (tuple type RGB_ALPHA, etc.). This is preferred because it's easier for users to pipe stuff around. Pamtotga is an example of this. * Declare all your symbols except 'main' as static so that they won't cause problems to other programs when you do a "merge build" of Netpbm. * Always start the code in main() with a call to p?m_init(). * Use shhopt for option processing. i.e. call optParseOptions3(). This is really easy if you just copy the parseCommandLine() function and struct cmdlineInfo declaration from pamcut.c and adapt it to your program. When you do this, you get a command line syntax consistent with all the other Netpbm programs, you save coding time and debugging time, and it is trivial to add options later on. Do not use shhopt's short option alternative unless you need to be compatible with another program that has short options. Short options are traditional one-character Unix options, which can be stacked up like "foo -cderx myfile", and they are far too unfriendly to be accepted by the Netpbm philosophy. Note that long options in shhopt can always be abbreviated to the shortest unique prefix, even one character. In messages and examples in documentation, always refer to an option by its full name, not the abbreviation you usually use. E.g. if you have a "-width" option which can be abbreviated "-w", don't use -w in documentation. -width is far clearer. * Use pm_error() and pm_message() for error messages and other messages. Note that the universal -quiet option (processed by p?m_init()) causes messages issued via pm_message() to be suppressed. And that's what Netpbm's architecture requires. * The argument to pm_error() and pm_message() is a string of text, not a formatted message. Don't put newlines or other formatting characters in it. These subroutines are designed to be flexible in how they issue the messages. * Use MALLOCARRAY() to allocate space for an array and MALLOCVAR to allocate space for a non-array variable. In fact, you usually want to save some programming tedium and use the NOFAIL versions of these (they never fail because they abort the program if memory is not available). These avoid array bounds violations. * Use pm_tmpfile() to make a temporary file. This avoids races that can be used to compromise security. * Properly use maxvals. As explained in the format specifications, every sample value in an image must be interpreted relative to the image's maxval. For example, a pixel with value 24 in an image with maxval 24 is the same brightness as a pixel with value 255 in an image with a maxval of 255. 255 is a popular maxval (use the PPM_MAXMAXVAL etc. macros) because it makes samples fit in a single byte and at one time was the maximum possible maxval in the format. Note that the Pamdepth program converts an image from one maxval to another. * Don't include extra function. If you can already do something by piping the input or output of your program through another Netpbm program, don't make an option on your program to do it. That's the Netpbm philosophy -- simple building blocks. Similarly, if your program does two things which would be useful separately, write two programs and advise users to pipe them together. Or add a third program that simply runs the first two. * Your program should, if appropriate, allow the user to use Standard Input and Output for images. This is because Netpbm users commonly use pipes. As an alternative to Standard Input, the user should be able to name the input file as a non-option program argument. But the user should not be able to name the output file if Standard Output is feasible. That's just the Netpbm tradition. * Don't forget to write a proper html documentation page. Get an example to use as a template from . * No Netpbm source code may contain tab characters. If you generate such a file, the Netpbm maintainer will convert it to spaces and possibly change all your indenting at the same time. Tabs are not appropriate for code that multiple people must edit because they don't necessarily look the same in every editing environment, and in most editing environments, you can't even tell which spaces are actually in the file and which came from the editor, expanding tabs. Spaces, on the other hand, look the same for everyone. Modern editors let you compose code just as easily with spaces as with tabs. * Limit your C code to original ANSI C. Not C99. Not C89. Not C++. Don't use // for comments or put declarations in the interior of block. Interpreted languages are OK, with Perl being preferred. DISCONTINUED CODING GUIDELINES ------------------------------ Here are some things you will see in old Netpbm programs, but they are obsolete and you shouldn't propagate them into a new program: * Tolerating non-standard C libraries. You may assume all users have ANSI and POSIX compliant C libraries. E.g. use strrchr() and forget about rindex(). * pm_keymatch() for option processing. Use shhopt instead, as described above. * optParseOptions() and optParseOptions2(). These are obsoleted by optParseOptions3(), which is easier to use and more powerful. * K&R C function declarations. Always use ANSI function declarations exclusively (e.g. use void foo(int arg1){} instead of void foo(arg1) int arg1; {} * for (col=0, xP = row; col < cols; col++, xP++) foo(*xP); This was done in the days before optimizing compilers because it ran faster than the more readable: for (col=0; col < cols; col++) foo(row[col]); Now, just use the latter. CODING STYLE ------------ We do not generally mandate any basic coding style in these guidelines. Where you put your braces is a matter of personal style and other people working with your code will just have to live with it. However, people working with your code might just recode it into another style if it makes it easier for them to read the code and have confidence in their changes. But if you have no preference, the following is what the Netpbm maintainer currently prefers. Essentially, it is clean, elegant computer science-type code rather than brute force engineering-type code. Modular and structured above all. * No gotos. This includes all variations of goto: break, continue, leave, mid-function return. An exception: aborting the entire program in the middle of something is allowed. * No functions with side effects. This is a tough one, since functions with side effects is highly traditional C. In fact, the creators of C didn't even have a concept of a subroutine. However, the average human brain, especially one trained in math, cannot easily follow code where a function both computes a value and does other stuff. For the purpose of this discussion, we say that a C function whose return type is void is not a "function," but a "subroutine." So a function should never change anything in the program. All it does is compute a value. Where you have to call an external function that has side effects (virtually anything in the standard C library, for example), put it in a simple assignment function that captures its return value and otherwise consider it a subroutine: rc = fopen(...) if (rc ...) * No reuse of variables. Most variables should be set at most once. Don't say "A is 5" and then later on, "no, A is 6." A reader should be able to take that first "A is 5" as a statement of fact and not hunt for all the places it might be contradicted. A variable that represents the state of execution of an algorithm is an obvious exception. Use "const" everywhere you possibly can. Especially never modify the argument of a function. All function arguments should be "const". Don't use initializers except for with constants. The first value a variable has is a property of the algorithm that uses the variable, not of the variable. A reader expects to see the whole algorithm in one place, as opposed to having part of it hidden in the declaration of the algorithm variables. * Avoid global variables. Sometimes, a value is truly global and passing it as a parameter just muddies the code. But most of the time, any external value to which a function refers should be one of its arguments. Declare a variable in the most local scope possible. * Keep subroutines small. Generally under 50 lines. But if the routine is a long sequence of simple, similar things, it's OK for it run on ad infinitem. * Use the type "bool" for boolean variables. "bool" is defined in Netpbm headers. Use TRUE and FALSE as its values. * Do not say "if (a)" when you mean "if (a != 0)". Use "if (a)" only if a is a boolean variable. Or where it's defined so that zero is a special value that means ("doesn't exist"). * Do multiword variable names in camel case: "multiWordName". Underscores waste valuable screen real estate. * If a variable's value is a pointer to something, it's name should reflect that, as opposed to lying and saying the value is the thing pointed to. A "P" on the end is the conventional way to say "pointer to." E.g. if a variable's value is a pointer to a color value, name it "colorP", not "color". * Put "const" as close as possible to the thing that is constant. "int const width", not "const int width". When pointers to constants are involved, it makes it much easier to read. * Free something in the same subroutine that allocates it. Have exactly one free per allocate (this comes naturally if you eliminate gotos). advanced/doc/USERDOC0000644000175000001440000001522212347602011013256 0ustar ericusersSince May 2002, Netpbm does not have traditional man pages for documentation. BUT YOU CAN CONFIGURE NETPBM, IF YOU WANT, SO YOU GET ESSENTIALLY THE SAME 'MAN' FUNCTION AS WITH A TRADITIONAL UNIX PACKAGE. Netpbm's maintainer believes man pages are obsolete and too limiting, and doesn't have time to maintain the documentation in multiple formats. So instead of classic nroff man page format, the Netpbm documentation is available as HTML, with one HTML file per program, plus some others. The current user manual is accessible on the World Wide Web at , and if it's practical for you, you should access it there instead of making a local copy. This manual is always up to date. It is not maintained on a release schedule like the source code is, but rather updated continuously. The user manual describes past Netpbm function as well as the present, so you can use the current manual with old Netpbm code. INSTALLING A LOCAL COPY OF DOCUMENTATION ---------------------------------------- If accessing the manual on the World Wide Web is not convenient for you (for example, if you want to access it from a computer that is not always connected to the Internet), just make a local copy of the HTML files. You can get the files from the Subversion repository using the Subversion client program 'svn': URL=https://netpbm.svn.sourceforge.net/svnroot/netpbm/userguide svn export $URL This creates a directory "userguide" in your current directory containing all the same files that are on the web site. And a sometimes more convenient way to get those files from the Subversion repository is to get Sourceforge's Viewvc service to make a tarball of them: wget http://netpbm.svn.sourceforge.net/viewvc/netpbm/userguide.tar.gz?view=tar --output-document=/tmp/userguide.tgz An apparent alternative is just to copy the web site with something like GNU Wget. However, the Sourceforge web server has limitations on how much you can download. In a February 2012 experiment, Sourceforge started refusing HTTP requests (with an Internal Server Error indication and a message saying there had been too many requests) before all the files could be fetched. GETTING COMMAND HELP WITH A "MAN" COMMAND ----------------------------------------- You can get the same quick access to program documentation with this HTML setup as with traditional man pages, using the Manweb program. This works whether you use the www copy or a local copy of the HTML files. Manweb is distributed with Netpbm. With Manweb and Netpbm installed and configured appropriately (see below), you can type man netpbm and get the top level page of the Netpbm user manual (with hyperlinks to all the other pages), or man netpbm ppmtogif or man ppmtogif to go straight to the Ppmtogif documentation. Installnetpbm normally installs Manweb and the netpbm.url file that Manweb needs to find the Netpbm documentation. Through the Configure dialog, or editing config.mk, you determine whether Manweb accesses the master web copy or a local copy you installed. Installnetpbm installs the program as 'manweb'. If you want to invoke it as 'man', you'll have to set that up yourself. Perhaps with a symbolic link from 'man' to 'manweb'. Note that 'manweb' is mostly backward compatible with 'man' so that this is a reasonable thing to do. Manweb can find documentation on the web, in HTML files, in GNU info files, and in traditional man pages. In a standard installation of Netpbm, Installnetpbm also creates a traditional man page for every Netpbm program it installs, but the man page just tells you to go to the HTML file. This way, even if the "man" program isn't capable of reading the HTML documentation and even if the user doesn't know specifically where Netpbm documentation lives, he isn't stranded without information. VIEWING NETPBM DOC WITH TRADITIONAL MAN PROGRAM ----------------------------------------------- Some people want to be able to access the Netpbm documentation with an existing man program that doesn't know HTML. You can install the documentation that way, with some loss of quality. There are two ways: 1) convert the HTML to troff with the 'makeman' program in the 'buildtools' directory of the Netpbm source tree. This is a Python program. 2) convert the HTML to formatted plain text (suitable as man "cat" pages) with the 'makecat' program in the 'buildtools' directory of the Netpbm source tree. This program just does a 'lynx -dump'. The "loss of quality" mentioned above is because: - The classic Unix manual format isn't as expressive as the worldwide web format; you can't convert down losslessly. - There is less maintenance effort put into maintaining the secondary non-web format. It requires certain idioms to be followed in the HTML source and lists of man pages to be separately maintained. This maintenance is essentially done on a fault basis -- when someone notices the Unix man pages aren't right, he fixes something. Bear in mind that the person who writes most of the Netpbm documentation updates never sees the troff versions; he uses Manweb, which renders directly from the HTML. Also, these methods require manual effort, and technical understanding, on your part to set up. Setting it up is too complex for an automated process to do it for you with any significant integrity. The examples are guidelines and you shouldn't expect them to work literally in your situtation. Here is an old example of making troff pages, which doesn't actually work anymore, but might be helpful. You'll probably want to use a Subversion export of the manual instead of a Wget download (see above). mkdir netpbmdoc cd netpbmdoc wget --recursive --relative http://netpbm.sourceforge.net/doc/ cd netpbm.sourceforge.net/doc make MAKEMAN=/usr/src/netpbm/buildtools/makeman \ -f /usr/src/netpbm/buildtools/manpage.mk manpages make -f /usr/src/netpbm/buildtools/manpage.mk installman cd ../../.. rm -r netpbmdoc man ppmtogif Here is an example of making "cat" pages: mkdir netpbmdoc cd netpbmdoc wget --recursive --relative http://netpbm.sourceforge.net/doc/ cd netpbm.sourceforge.net/doc /usr/src/netpbm/buildtools/makecat *.html cp *.1 /usr/man/cat1/ cd ../../.. rm -r netpbmdoc man ppmtogif DOCBOOK ------- You can turn the Netpbm user manual into Docbook XML pages using Doclifter. Because Doclifter works on troff pages, you have to convert the documentation down to troff first, which means the Docbook pages are of lower quality than the main HTML documentation. To create Docbook XML, follow the example above for creating troff pages, and use 'make xmlpages' instead of 'make manpages'. advanced/doc/TESTS0000644000175000001440000002043112347602011013052 0ustar ericusersContents ======== 1. Running the tests 1.1 Standard test procedure 1.2 Summary report 1.3 Prerequisites 1.4 Repeatability 1.5 Execution time 1.6 Testing package in designated directory 1.7 Post-install check 1.8 Skipping test items 1.9 Valgrind 2. Troubleshooting 2.1 Missing programs 2.2 Broken programs 2.3 Color name file 2.4 Multiple versions 2.5 System dependent issues 3. Reporting test failures ------------------------------------------------------------------------------ 1. Running the tests ==================== 1.1 Standard test procedure =========================== The recommended method of running the tests is after packaging: make make package make check To capture the output do: make check 2>&1 | less Or: make check > check.log 2>&1 1.2 Summary report ================== Like most other test routines, these produce much output. A summary will appear at the end: Test summary: ================== SUCCESS 83 FAILURE 1 TOTAL TESTABLE 84 ================== All tests done. Sat, 08 Jun 2013 09:30:33 +0000 make: *** [check] Error 1 1.3 Prerequisites ================= The tests require the Bash command shell. The script Execute-Tests has some expressions unique to bash. Quite old versions work, at least back to bash v. 2.05b. The tests also use the following utilities: - awk - cat - cmp - egrep - head - mktemp - sed - seq - tee 1.4 Repeatability ================= The current version of the test package produces identical results if you run "make check" repeatedly. The tests contain no random elements; some Netpbm programs use randomness internally, but the tests seed their random number generators with fixed values so that they too have repeatable results. 1.5 Execution time ================== Currently "make check" takes no more time to execute than "make package", and much less than "make". 1.6 Testing package in designated directory =========================================== If you specify the package directory when you do "make package", you must do the same with "make check": make make package pkgdir=/tmp/package make check pkgdir=/tmp/package 1.7 Post-install check ====================== You can run the tests after installation as well as from the package directory. Just set $PBM_TESTPREFIX to the bin directory. For example, if the Netpbm executables are in /usr/local/bin/ do PBM_TESTPREFIX=/usr/local/bin make check If you want to test the executables in the default search path do PBM_TESTPREFIX="" make check If the color dictionary file rgb.txt is in a non-standard location, you must set RGBDEF. If you don't, the tests will report several false positives. PBM_TESTPREFIX=/usr/local/bin RGBDEF=/etc/colors/rgb.txt make check 1.8 Skipping test items ======================= The file test/Test-Order is a list of tests which are run. If you want to skip any test, remove the line or comment it out with a "#". 1.9 Valgrind ============ You can run the whole test under Valgrind. This is an advanced feature intended for programmers who work on Netpbm code. To turn on Valgrind, uncomment this line in test/Execute: # export PBM_TESTPREFIX="valgrind --log-fd=4 "${PBM_TESTPREFIX} make check 4> valgrind.log Note that Valgrind output is directed to file descriptor 4. Valgrind version 3.5.0 or newer is recommended. Older versions do not report the command that is being executed. You can add any options for Valgrind should by editing the above mentioned line. To run "valgrind --track-origins=yes", you must make two changes in config.mk: - Add -g to CFLAGS. - Turn stripping off. Valgrind significantly increases execution time. You should consider paring down the items in Test-Order. You probably don't need to run "all-in-place.test", which has restrictions in the way errors are reported when PBM_TESTPREFIX is not a simple directory path. Another test "pamslice-roundtrip.test" is time-consuming, you may want to skip this one too. 2. Troubleshooting ================== 2.1 Missing programs ==================== The first test run, "all-in-place.test" detects missing programs. If you work around a build glitch with "make --keep-going" you will get a few errors here. A wholesale failure with "all-in-place.test" indicates a systemic problem, such as a misconfigured dynamic library or a directory-wide permission issue. This kind of failure is known to happen when N is set too high with "make -jN" (overambitious parallel make.) The current test routines assume a typical build configuration - they are not aware of the actual configuration you chose. If a choice you make during configure causes "make" to skip compilation of certain programs, the test routines won't know and will report failures. For details read 'Netpbm Library Prerequisites': http://netpbm.sourceforge.net/prereq.html . 2.2 Broken programs =================== Broken programs will invariably lead to failures. Certain programs (for instance, image generators 'pbmmake' and 'pgmmake') are used in numerous test scripts. Problems in them will lead to multiple failures. To aid you in this situation each test routine lists the necessary programs near the top. Each test routine comes in two parts, a ".test" file which is a shell script and a ".ok" file which denotes its proper output. When a test fails, a ".out" file will be produced in the /tmp/netpbm-test/ directory. By comparing ".ok" and ".out" you can tell exactly what went wrong. Often one does not need to go this far; the error messages tell enough. 2.3 Color dictionary file ========================= If you get the following error message, it indicates a problem with the color dictionary file rgb.txt. ppmmake: can't open color names dictionary file from the path '/usr/share/ netpbm/rgb.txt:/usr/lib/X11/rgb.txt:/usr/share/X11/rgb.txt:/usr/X11R6/lib/ X11/rgb.txt' and Environment variable RGBDEF not set. Set RGBDEF to the pathname of your rgb.txt file or don't use color names. This is highly unlikely to occur with "make check" right after packaging, but may appear after installation. To check manually after installation, execute the following and see whether the proper output or the error message appears: ppmmake red 1 1 -plain Proper output: P3 1 1 255 255 0 0 The simple remedy is properly setting the environment value RGBDEF to the location of rgb.txt. If you want to hardcode the path, modify RGB_DB_PATH in pm_config.h and run "make" again. Note that running "configure" will cause pm_config.h to be overwritten; changes by editing will be lost. 2.4 Multiple versions ===================== If multiple versions of Netpbm executables are installed on your system, you should do a post-installation check to ensure that the newly built version is in place and in working condition. The test routines can test binaries other than the intended target, for example pre-compiled binaries distributed in .rpm or .deb format. If PBM_TESTPREFIX is explicitly set to a directory that contains programs from such a source, you should expect multiple failures due to missing programs, missing features, etc. If PBM_TEXTPREFIX is set to "" (null), the outcome will depend heavily on what version has precedence in PATH. Netpbm distributed with Debian or Ubuntu is called "Netpbm-Free" and is based on a fork which broke off in 2002. There are many differences. Many tests will fail. However, the test framework itself is valid for these distributions. 2.5 System dependent issues =========================== The tests have worked on x86 and x86_64 GNU/Linux systems, with several versions of GCC and Clang. Reports from users of other systems including Mac OS, Sun SPARC and BSD and compilers other than GCC are highly welcome. Floating point math precision seems to be an issue. Some discrepancies have been observed between x86 32 bit and 64 bit; the tests are written to work around them as much as possible. The use of the "--fast-math" flag by default may also be a factor. The current test framework checks whether the random number generator is the one from glibc and skips certain tests if a different one is detected. 3. Reporting test failures ========================== When reporting problems with the tests, please give both the output of "make check" and the contents of the "netpbm-test" directory. advanced/doc/netpbm.10000644000175000001440000000025012347602011013571 0ustar ericusersAre you looking for the man pages? Netpbm does not have man pages in the conventional Unix form. Please see the file doc/USERDOC for a complete explanation of this. advanced/doc/INSTALL0000644000175000001440000002760612347602011013271 0ustar ericusersHOW TO INSTALL NETPBM --------------------- For most typical platforms, you can just do ./configure followed by make To build all the programs. Then make package to gather all the installable parts into a specified directory, and finally ./installnetpbm to install it into your system. If you have a Debian-like system, that uses Dpkg for package management, it's better to create a Debian package file and install it as follows in place of the 'installnetpbm' step above. make deb dpkg --install netpbm-sf-*.deb More information on building and installing on Debian are in the file buildtools/debian/README in the source tree. The 'configure' program is not GNU Autoconf -- it is a simple program specific to Netpbm that prompts you for information about your system. If your system is not typical enough, you'll have to do a little more work, as described below under "custom installation." You need to use GNU Make even if nothing else on your system is GNU, because the Netpbm make files exploit many advanced features of GNU Make. Often, systems have both GNU Make and a native Make. In this case, GNU Make is named 'gmake'. If you don't have it yet, see www.gnu.org/software. GNU Make is free, easy to install, and works just about anywhere. The only tricky part about installing is setting up the shared libraries that are part of Netpbm. Simply putting the library files in place may not be enough. If you get mysterious "file not found" kinds of errors and are not an expert with shared libraries, see the section "SHARED LIBRARIES" below. The --keep-going option to Make is handy, because it causes Make to make anything it can, as opposed to quitting as soon as something goes wrong. With so many parts having so many requirements, it's not unusual for a few things to fail to build, but that doesn't affect everything else. You can work on the failed parts and repeat the make and it will attempt to build whatever it hasn't successfully built yet. CHECKING THE BUILD ------------------ The package comes with a test facility, which you can run after packaging, against the package you created. The typical sequence is make make package make check ./installnetpbm To capture all the messages from "make check" do: make check > test.log 2>&1 The test facility was new in Netpbm 10.61 (December 2012). For further information on the tests read the document TESTS. AUTOMATING THE BUILD -------------------- The build is already as automatic as Netpbm developers know how to make it without sacrificing your ability to build it a wide variety of ways. But if you're building for a specific range of applications, e.g. a particular standard system configuration, then you may want to write programs to automate the build further. PLEASE don't do this by writing a program that invokes Netpbm's Configure program and tries to maintain a dialogue with Configure. This is more work than you need to do and you will be disappointed with Configure's unpredictability, especially from one release to the next. Configure is specifically intended to talk to an intelligent human being. Rather, just write a program to generate the file config.mk. That's all Configure does in the end anyway. Like Configure, your program can simply copy config.mk.in and add overrides to the bottom. Or you can just package up a complete config.mk and not run any program at all at build time. Comments in config.mk.in explain the entire contents. You can also run Configure interactively and use its output as an example. THE PREREQUISITE LIBRARIES -------------------------- Netpbm uses lots of external libraries, but you don't necessarily have to install them all. See http://netpbm.sourceforge.net/prereq.html . You do, however, have to tell Configure accurately whether you have the library installed and if so, where. INSTALLATION - WINDOWS ---------------------- For notes on building Netpbm on Windows using Cygwin, see the file README.CYGWIN. With Cygwin, you can build Netpbm programs that use Cygwin or Netpbm programs that use Mingw. Netpbm has also been built for Windows using Djgpp, as late as 2001. See also the general installation instructions above. INSTALLATION - MAKING ONLY THE PARTS YOU NEED --------------------------------------------- If you don't need the whole package, but just want one tool in it that you heard about, you can make just that one. For example, to make Pnmtojpeg, just do configure cd converter/other make pnmtojpeg It will build Pnmtojpeg and any of its dependencies, but nothing else. You have to install it manually. When you build just one program, you should request a static Netpbm library in the configure step, because for just one program, the shared library would be pure masochism. CUSTOM BUILDING --------------- This section explains how to customize the installation in the case that your platform is, or your requirements are, not among the simple cases that 'configure' understands. 'configure' is really just a convenient way to build the config.mk file, and in the most general case, you build that file manually. config.mk contains settings for various things that vary from one system to the next, like file paths. Start with the distributed file config.mk.in. Copy it as config.mk, then edit it. Search for your platform name (Solaris, SunOS, NetBSD, Cygwin, BeOS, and Tru64 are among those mentioned) to see recommended settings for your platform. If your system is even too exotic to accommodate with settings in config.mk, you may need to modify things in the main make files or pm_config.h.in. If you figure out how to install on other platforms, contact the Netpbm maintainer to have the 'configure' program or these instructions improved for the next person. CUSTOM INSTALLATION ------------------- If the Installnetpbm program doesn't install the way you want, it is easy to install it manually using the package that 'make package' generates. That package is just a directory full of files, and you should be able to tell by inspection what to do with those files (copy to /bin, etc). If not, there will be a README file in the package to explain everything. INSTALLATION - SHARED LIBRARIES ------------------------------- There are over 240 programs in the Netpbm package and they do a lot of common things. In order to avoid the expense of copying the code for those common things into every program, Netpbm places them in a shared library: libnetpbm. When you invoke a Netpbm program, your system notices that it needs this library and accesses it too. The tricky part of installing the shared (runtime) library is telling your system where to find it in the event that you invoke a Netpbm program. And that varies from one system to the next. On a GNU libc system (essentially, any Linux system), if you put the Netpbm shared library in a conventional spot (say, /lib) and reboot your system, chances are you will have no trouble running Netpbm programs. But if you want to fine tune things, read up on ld-linux.so (GNU libc's dynamic linker) and Ldconfig and consider the /etc/ld.so.conf file and LD_LIBRARY_PATH environment variables. Use 'ldd' to see if you have a shared library issue. If it shows any shared library as "not found", you've got library trouble. On a Solaris system, use 'crle' to set the default search path for shared libraries (which is kept in /var/ld/ld.config). Make sure that path includes the directory in which you installed the Netpbm shared library. You can also use the LD_LIBRARY_PATH environment variable. On AIX, the environment variable LIBPATH affects the shared library search path. On AIX 5.3 and newer, you can use LD_LIBRARY_PATH as well. Besides the Netpbm shared library, libnetpbm, several of the converter programs, e.g. Jpegtopnm, use separately distributed libraries that understand the graphics format involved. You need to make sure your system knows how to find those libraries at run time too (or cause the Netpbm build to statically bind the libraries into the Netpbm programs). Another thing you can do is forget about library search paths and just build into each Netpbm executable the location of the Netpbm shared library. (I'm talking about the classic -R linker option) You set this up with variables in config.mk. If you use Configure to build config.mk, then for some platforms where this method is common, the Configure dialog asks you what directory, if any, you want built into Netpbm executables. One final note: New Netpbm executables often can run OK with an old Netpbm shared library. This means if you don't correctly install the new library, you may run what you think is a new Netpbm program, but in actuality be accessing the old Netpbm library, and you may not even notice a problem right away. But eventually, you may find some things not working the way they should. Again, 'ldd' will give you peace of mind. INSTALLATION WITHOUT SHARED LIBRARIES ------------------------------------- Since shared libraries can be such a pain, and in fact some systems don't even have them, you can build Netpbm with a static library instead. Just answer "static" to the static/shared question when you run 'configure' (if you don't use 'configure', set NETPBMLIBTYPE as directed in config.mk.in). If you do this, you probably want to do a merge build instead of the normal build (there's a question for that in the Configure program). See below. SEPARATE BUILD TREE ------------------- While it's traditional to build a Unix package by adding object files to the same tree with the source files, it's actually much cleaner to keep your source tree exactly as you got it and put the built files in a separate directory, called the build tree. To do this, just create an empty directory and run 'configure' in it, then 'make': mkdir netpbmbuild cd netpbmbuild /usr/src/netpbm/configure ... make But if you plan to work on Netpbm source code, you'll probably find it more convenient to build the traditional way, with a single tree for source and build. In the source tree, you can type 'make' in any directory to do the default make for that directory, or make FILENAME to make the file of that name there. In the separate build tree, there are special facilities to allow you to do a simple make from the _top level directory_, but if you want to make a subcomponent or individual part, you have to have a -f option and set SRCDIR and BLDDIR on your 'make' command. MERGE BUILD ----------- There are two ways to build Netpbm: the standard or nonmerge build, and the merge build. There are different make file targets for them and which one is default is controlled by the DEFAULT_TARGET make variable in config.mk, and its value is one of the choices you make via the Configure dialog. The standard build is the conventional one. The merge build is a way to reduce disk space and other resource usage in some configurations. These are rare configurations, mostly antique ones. The advent of shared libraries largely obsoleted the merge build. In some configurations, Netpbm uses _more_ resources with the merge build. In the standard build, hundreds of separate programs get built: ppmtogif, pamcomp, etc. But the merge build creates a single large program called 'netpbm' instead. That single 'netpbm' program contains the functions of all the individual programs that the standard build builds, and selects one of them to execute based on what command name you use to invoke 'netpbm'. For example, if you install 'netpbm' with a symbolic link to it named 'ppmtogif' and you invoke the program by typing 'ppmtogif' at a shell prompt, 'netpbm' executes Ppmtogif. Hence, 'make package' creates a package containing the one 'netpbm' program and lots of symbolic links to it. With all these links properly installed, a typical user cannot tell the merge build from the standard build. DOCUMENTATION ------------- Documentation is not packaged with the program source code. See the file doc/USERDOC for information on installing documentation. advanced/doc/patent_summary0000644000175000001440000001031012347602011015213 0ustar ericusersThese are the patents the Netpbm maintainer knows about that relate to Netpbm. It is basically just information the maintainer has stumbled over at some point -- no search has been done. No licenses have been granted by patent owners to the maintainer of Netpbm. Therefore, if you need a patent to use something in Netpbm, you need your own license. A note about patents in general: A patent gives an inventor the exclusive right to make, sell, or use the invention. If you independently invent something without knowing that the patent holder already did, that makes no difference -- the patent holder still has the exclusive right. It makes no difference if you give the original inventor credit. The patent applies to a method, not its expression, so writing a program from scratch to implement a certain method is still a patent infringement. Infringing a patent is not a crime per se, but to the extent that it costs the patent holder money, the infringer has to make it up. The original purpose of patents is probably perverted when patents are applied to things you implement in computer programs. This is one of the Free Software Foundation's causes. See . The Jbigtopnm and Pnmtojbig programs use arithmetic coding patents and other patents covering various aspects of the "front end." JPEG patents ------------ The Pnmtojpeg and possibly Jpegtopnm programs in some cases may use the arithmetic coding patents owned by IBM, AT&T, and Mitsubishi. There is difference of opinion on whether they do. Forgent owns a patent it believes covers JPEG compression. This patent was virtually unknown before July 2002, when Forgent began to enforce it. It has successfully enforced it against two companies (Sony and an unnamed Japanese digital camera maker), but without court ruling. This patent, U.S. Patent No. 4,698,672, expires in 2006. Philips and Lucent Technologies also own patents they claim cover JPEG. The following Netpbm components may be restricted by these patents: Jpegtopnm, Pnmtojpeg, Ppmtompeg, Tifftopnm, Pnmtotiff. These all do their JPEG work via a JPEG library not distributed with Netpbm. Your JPEG-related liability for using Netpbm is limited to your liability for using your JPEG library. Note that it is possible to use Ppmtompeg without involving JPEG and to build it without the ability to involve JPEG. The next best alternative to JPEG is probably PNG and maybe JBIG for bilevel (black and white) images. http://burnalljpegs.org contains information on this issue. MPEG patents ------------ The original University of California distributeion of the Ppmtompeg code conatins this statement in a README file: ... patents are held by several companies on various aspects of the MPEG video standard. Companies or individuals who want to develop commercial products that include this code must acquire licenses from these companies. For information on licensing, see Appendix F in the standard. Expired LZW patents ------------------- Unisys owns patents on LZW compression, which is used by Ppmtogif, and maybe on LZW decompression, which is used by Giftopnm. IBM also owns a patent that may cover the GIF tools. Unisys offers a license of the patent for trivial use for $5000. Its U.S. patent (Number 4,558,302) expired June 20, 2003. In most of Europe, the patent expired June 18, 2004. In Japan, it was June 20, 2004 and in Canada, July 7, 2004. IBM's U.S. patent expired August 11, 2006. Neither company has ever enforced the patent against trivial users of it. is an article dated April 18, 2000 on the issue. http://www.unisys.com/about__unisys/lzw/> is Unisys' view of the matter. For information from another perspective, see . The following Netpbm components may be restricted by these patents: Ppmtogif, Giftopnm. A good substitute for GIF if the patents are a problem is PNG (see pngtopnm, pnmtopng), which was developed with a primary purpose of not using any patented technology. You can also use the -nolzw option on ppmtogif to avoid using the LZW patent. The images so generated are larger than traditional LZW-compressed GIFs, but any GIF decoder can decode them just the same. advanced/doc/README.CYGWIN0000644000175000001440000000576112347602011014115 0ustar ericusersCygwin is a software package that sets up a Unix-like platform on a Windows (win32) system. Here are some specific things you need in that environment to build and use Netpbm. All these programs and libraries are optional parts of the cygwin package. Programs: 1) gcc suite 2) binutils 3) bash 4) dlltool 5) flex & byacc 6) patch 7) install 8) rm, ln, cp and other file utilities Libraries: 1) libtiff (cygtiff3.dll) 2) libpng (cygpng2.dll) 3) libjpeg6b (cygjpeg6b.dll) 4) libz (cygz.dll) Find Cygwin at http://sources.redhat.com/cygwin/ . One problem special to Windows is the common existence of directories with space in their names (e.g. Windows 2000's "Documents and Settings" directory. (Such filenames are possible on non-Windows systems, but are highly unconventional). Don't try to build Netpbm in such a directory or with files in such a directory. It ought to work, but it just doesn't. And the error messages are far from helpful, since those spaces completely change the nature of the commands that include them. One way to deal with this is to use the Cygwin "mount" facility to map the Windows path "c:/Documents and Settings/aaa/bbb/cccc/Distributions" to something short and friendly, such as /Distributions. MINGW ------ You can use Cygwin on Windows to build Netpbm to run on Windows, but have the Netpbm programs use Mingw code instead of Cygwin code. Two reasons to do this: - We don't appear to have a way to statically link Cygwin library routines into Netpbm, so you have to have a Cygwin library (DLL) installed on a system as well as Netpbm to run Netpbm. - If you link Netpbm with a Cygwin library (some believe even if the program links itself to a Cygwin library at run time), you cannot distribute the resulting Netpbm, or any extension of your own, unless you distribute it under GPL. That's because Cygwin is publicly distributed under GPL, so you probably got your permission to distribute Cygwin as part of Netpbm from GPL. A condition of that permission was that you distribute everything you wrap around Cygwin under GPL as well. Mingw is a small POSIX emulation library for Windows. See . Mingw comes with tools you can use to build Netpbm with the Mingw library, but you can just use the Cygwin tools if you prefer. tells how. There isn't much to it -- it's just a matter of telling the compiler to search for include files and link libraries in a different place, which is basically just a -mno-cygwin option and -I and -L options. You can easily add the compiler options you need at the bottom of config.mk after you create it with the automatic configurator ('configure'). Mingw does not have the Unix process management facilities (fork/wait, etc.), so Netpbm will build without some of its function if you use Mingw. Areas of Netpbm that require those facilities are so arcane you probably will not miss them. advanced/doc/HISTORY0000644000175000001440000054666012347602011013332 0ustar ericusersSee http://netpbm.sourceforge.net/history.html for a general history of Netpbm. CHANGE HISTORY -------------- 14.06.15 BJH Release 10.66.03 sgitopnm: fix bug: no output if input is RLE compressed. Broken in Netpbm 10.53 (December 2010). Thanks Prophet of the Way . Build: fix bug in which null value is not taken to mean "in the system search path" for JBIGHDR_DIR and JASPERHDR_DIR. 14.04.24 BJH Release 10.66.02 pamsharpness: put primary output on Standard Output instead of on Standard Error as a Netpbm message. jpegtopnm -dumpexif: fix incorrect display of resolution. Always broken. (-dumpexif was new in Netpbm 9.18 September 2001). jpegtopnm -dumpexif: fix truncated make, model, or datetime. Always broken. (-dumpexif was new in Netpbm 9.18 September 2001). jpegtopnm -dumpexif: fix wild pointer with invalid EXIF data. Always broken. (-dumpexif was new in Netpbm 9.18 September 2001). 14.04.02 BJH Release 10.66.01 Build: Fix inconsistent use of upper and lower case Y and N in make variables, causing static library not to get built. Introduced in 10.66.00. 14.03.30 BJH Release 10.66.00 Add pamvalidate. Thanks Prophet of the Way . Add pamfix: Does what pamfixtrunc did, plus repairs excessive sample values. pamfixtrunc: implement as call to new pamfix. pgmhist, ppmhist: Add -forensic: Analyze invalid >maxval pixels. Thanks Prophet of the Way . pgmramp: add -diagonal. Thanks Prophet of the Way . libnetpbm: Read functions validate that sample values do not exceed maxval. Thanks Prophet of the Way . libnetpbm: Validate image dimensions are small enough that you can allocate a row buffer. Thanks Prophet of the Way . pgmhist: fix incorrect report of quantiles or crash due to array bounds violation in some builds. Thanks Prophet of the Way . Always broken. Quantile reporting was new in Netpbm 10.61 (December 2012). pgmhist: fix buffer overrun with -median. Always broken. -median was new in Netpbm 10.61 (December 2012). pnmmargin: fix for size 0 and superfluous "unexpected operator" message with size != 0. Introduced in 10.42. pstopnm: fix wrong interpretations of -xsize and -ysize when rendering image in landscape (rotated). This can appear as stretching and squashing. Probably always broken. pstopnm: fix wrong orientation sometimes when you specify both -xsize and -ysize. Introduced in 10.65. pgmramp: fix bogus output with really large images. Thanks Prophet of the Way . ppmrelief: fix out-of-bound values in output. Always broken. ppmrelief was new in primordial Netpbm in 1989. Thanks Prophet of the Way . ppmrelief: fix crash when input image is too small. Always broken. ppmrelief was new in primordial Netpbm in 1989. Thanks Prophet of the Way . pgmtexture: fix buffer overflow. Always broken. (Program was added in primordial Netpbm in 1991). pamdeinterlace: fix incorrect output with -takeodd and image has only one row. Always broken (pamdeinterlace was introduced in Netpbm 9.21 (January 2001)). Thanks Prophet of the Way . configure: warn if user says JPEG library is in the linker's default search path, but it isn't. build/install: add tools for creating a Debian package. make package: Include template for pkgconfig file. make package: Include a man/web directory with .url files for each manual page. test: Add -portrait to invocations of pstopnm in order to get proper round trips. Windows build: fix Ppmtompeg build failure in non-Cygwin build due to missing sys/utsname.h. Windows build: fix missing .exe on copies of programs under their old names. 13.12.26 BJH Release 10.65.00 pamfunc: add -changemaxval. pgmkernel: add -maxval. Recognize SIGPWR on systems that have it in messages about signal received. pstopnm: More rational default for landscape/portrait choice. In particular, if the image or page is square, image will always be in portrait (not rotated). brushtopbm: check for read errors, extraneous data after apparent end of image. pnmtops: Fix spurious blank line in asciihex encoding of the image raster. Probably harmless. Introduced in 10.56 (September 2011). pnmtops: Fix crash with 12 bits per sample. Introduced in 10.53 (December 2010). Thanks Prophet of the Way . pnmtops: Fix bug: wrong output with -ascii85. Introduced in 10.63 (June 2013). pnmtops: Fix bug: wrong output with -rle. Introduced in 10.63 (June 2013). pnmtops: Fix bug: program hangs if it inherits lots of open files. Introduced in 10.56 (September 2011). pnmtops: Fix bug: fails with message about waitpid() failing if invoked with SIGCHLD ignored. Introduced in 10.56 (September 2011). pbmtoepsi: fix handling of all-white image. Always broken. Thanks Prophet of the Way . pbmtoepsi: fix excessively long raster line. Always broken. Thanks Prophet of the Way . pnmshear: fix incorrect determination of background color. Always broken. ppmpat: fix crash with -squig with aspect ratio < 1:25 or > 25:1. Thanks Prophet of the Way . Always broken. pgmkernel: fix some pixels 1 less than they should be. pamgauss: Fix typo in message. Fix wild pointer dereference when memory allocation for a string fails. Broken since 10.36 (September 2006). Build for big-endian machines: fix syntax error so it compiles. Introduced in 10.63 (June 2013). Fix compile failure on system such as OpenBSD that don't have SIGWINCH and SIGIO. Broken since 10.49 (December 2009). Build: Use SSE2 vector instructions when compiling with Clang, as done already with GCC. Build: Use interface for SSE intrinsics instead of GCC-specific versions. Thanks Prophet of the Way . Build on system without vasprintf (not GNU libc): fix compiler warning. Apple build: use vasprintf. 13.09.28 BJH Release 10.64.00 pngtopam: fix bug: ignores -gamma. Introduced in 10.48 (September 2009). libnetpbm, pnmpsnr, ppmcie on systems that don't use GNU C library: fix bogus printing of floating point numbers. test: fix ppmhist lack of sorting. 13.06.29 BJH Release 10.63.00 Add pamtowinicon, winicontopam. Thanks Ludolf Holzheid (lholzheid@bihl-wiedemann.de). pgmnoise: add -maxval, speed up. Thanks Prophet of the Way . Perl programs: make them shell programs that reinvoke themselves as perl so we can get the Perl interpreter from the PATH. yuvtoppm: check for inconsistencies between specified width and height and size of input. 411toppm: check for inconsistencies between specified width and height and size of input. Ignore -plain when program generates PAM. Before, programs failed if the user specified -plain to a program that generates a PAM image. giftopnm: fix bug: erroneously claims GIF ends prematurely. Broken in Netpbm 10.38 (March 2007). This affects all GIFs, but the problem does not manifest when Netpbm was built with Gcc from 2007 and later. pnmtops: Fix bug: only first image gets converted. Broken in Netpbm 10.56 (September 2011). pnmtopng: fix bug: incorrect output when output should have an alpha mask. Broken in Netpbm 10.55 (June 2011). Thanks Ludolf Holzheid (lholzheid@bihl-wiedemann.de). pnmtopng: fix bug: output bigger than it needs to be when the input is a color format image that contains only gray. Broken since at least Netpbm 10.26 (May 2001), but after 10.18 (September 2003). Thanks Ludolf Holzheid (lholzheid@bihl-wiedemann.de). pnmremap (ergo pnmquant): fix bug: incorrect output with -norandom and floyd-steinberg dithering. Always broken. (-norandom was introduced in Netpbm 10.39 (June 2007)). pamtilt: fix bug: incorrect output or invalid memory access crash. Always broken (program was new in Netpbm 10.30 (October 2005)). pnmpsnr: fix bug: says types aren't the same when they are. Introduced in Netpbm 10.61 (December 2012). ppmtowinicon: fix bug: sometimes creates image truncated in the middle of the AND mask. Always broken (program was new in Netpbm 9.3 (June 2000)). ppmtoxpm: fix bug: ignores inability to open the specified color dictionary file (-rgb) and just doesn't use color names. Introduced in Netpbm 10.15 (April 2003). libnetpbm: fix bug: ppm_freecolornames() has wild pointer dereference when the color name table was generated empty because the color dictionary file was not openable. ppmtoxpm suffers from this. Broken in 10.15 (April 2003). libnetpbm: fix bug: pnm_readpaminit and pnm_writepaminit set 'opacity_plane' member of struct pam incorrectly. No Netpbm programs are affected. Always broken (member was added in Netpbm 10.56 (September 2011)). sparc64 pbmtog3: fix bug that causes crash due to unaligned memory access. ppmforge. fix crash when -mesh is 1 or less. Always broken (program was added in Pbmplus (October 1991). Thanks Prophet of the Way . ppmforge: fix array bounds violation. Always broken (program was added in Pbmplus (October 1991). Thanks Prophet of the Way . install: fix Perl warning in installnetpbm. Broken in Netpbm 10.61. build: Use when available. build: fix problem with creating lib/util that already exists. Broken in Netpbm 10.62. 13.03.28 BJH Release 10.62.00 pnmtorast: set don't care bytes to zero to make output repeatable. rasttopnm: add -dumpheader, dumpcolormap. pamstereogram: change -guidesize default from 10 to 20 (relevant since Netpbm 10.61). rasttopnm: fix bug: incorrect output due to used-before-set variable. Introduced in Netpbm 10.56 (September 2011). pamstereogram: fix bug: doesn't reject negative guidesize. Broken since Netpbm 10.61. pamstereogram: fix bug: garbage in -verbose listing. Broken since Netpbm 10.61 Windows MinGW build: various fixes. 12.12.30 BJH Release 10.61.00 pgmhist: Add -machine option. pgmhist: Add -median, -quartile, and -decile options. pamstereogram: Add -guidetop and -guidebottom options to replace trick where negative -guidesize means top, positive means bottom, absent means none. pamtojpeg2k: default to no compression ratio constraint and allow compression ratios less than 1. Because of compression metadata, small images do require loss of quality in order to get down to a compression ratio of 1. pamstereogram: -smoothing smooths images even without -texfile. Thanks Scott Pakin (scott@pakin.org). pnmcat: set don't care bits in packed PBM output to zero so they are predictable. libpam, pamcomp: fix bug: treats tuple type GRAYSCALE_ALPHA like GRAYSCALE on 32-bit machine. Actually, looks only at first 4 characters (8 characters on machine with 64 bit addressess) of the tuple type. Broken since Netpbm 10.56 (September 2011). pngtopam -alphapam with grayscale input: fix bug: generates invalid output: tuple type GRAYSCALE_ALPHA, but depth 1. Depth should be 2. Always broken (pngtopam was created in Netpbm 10.44 (September 2008)). pamtotiff: fix bug: XRESOLUTION, YRESOLUTION, and RESOLUTIONUNIT not allowed in -tags. Broken at least since Netpbm 10.35. pnmpsnr: fix crash when invoked (invalidly) with only one argument. Always broken. leaftoppm: fix incorrect determination of whether input is Interleaf on 64 bit systems. Always broken. cmuwmtopbm: fix incorrect determination of whether input is a CMU window manager bitmap on 64 bit systems. Always broken. pnmmontage: fix totally wrong output. Broken in Netpbm 10.44 (August 2008). pnmmontage: fix random stuff placed in unoccupied space in the output image. Always broken (program was new in Netpbm 9.10 (January 2001). pbmpscale, pgmhist, pampick, pamtompfont: fix wild pointer in command line parsing. Bug has always been there in pampick, pamtompfont, since Netpbm 10.50 (March 2010) in pbmpscale, since 10.44 (September 2008) in pgmhist. xbmtopbm: fix incorrect output, memory leak. Thanks Prophet of the Way . sunicontopnm: Fix incorrect output for depth 8. Always broken (depth = 8 capability was added in Netpbm 10.53 (December 2010). pamgauss: Fix bug: erroneously says -maxval is too big on 64 bit system. Always broken (Pamgauss was added in Netpbm 10.23 (July 2004). ppmpat: Fix bug: wrong output with -poles. Broken in Netpbm 10.47 (June 2009). Add tests. Thanks Prophet of the Way . 12.09.30 BJH Release 10.60.00 xpmtoppm: major speedup for 3-character-per pixel files, memory reduction for all files: use hash table instead of linear search or direct index, go row by row. xpmtoppm: fix bogus "color number too large" failure. Broken in Netpbm 10.49 (December 2009). pnm_hashtuple: slight performance improvement from new hash function. Thanks Prophet of the Way . ppmtospu: wild memory accesses. Always broken (program added in Netpbm 10.58 (March 2012). pamtosrf: fix storage corruption. Always broken (program added in Netpbm 10.55 (June 2011). Build: change _XOPEN_SOURCE 500 to 600 because on a Mac OSX Netpbm 10.8 system, this is necessary to get 'strdup' into . Build: rename getline() in xpmtoppm.c to avoid collision with some libc. Always broken. Build: Don't expect GCC facilities to exist when compiler is Clang. (Note that Clang identifies itself as GCC). 12.06.28 BJH Release 10.59.00 pamtogif: Make data blocks 255 bytes instead of 254. 255 is the maximum allowed by GIF. 254 was intended to not trigger potential bugs in programs that read the file. ppmdraw: Add 'filledcircle' command class. Thanks Elijah Griffin . pamstereogram: Add -planes . Thanks Scott Pakin (scott@pakin.org). pamstereogram: improve verbose output. Thanks Scott Pakin (scott@pakin.org). pamstereogram: fix crash introduced in Netpbm 10.54 (March 2011). pamstereogram: fix crash introduced in Netpbm 10.53 (December 2010). Build: fix bug: declines to build pnmtops. Broken in Netpbm 10.58. Build: move -lm to end of -l's. Broken at least since Netpbm 10.35. 12.03.29 BJH Release 10.58.00 Add ppmtospu, 22 years late. pngtopam: fix incorrect output when PNG has 16 bits per pixel. Broken in Netpbm 10.56. pgmtexture: fix integer overflow in difference variance. Always broken. pgmtexture: fix array bounds violations in various calculations. Always broken. xpmtoppm: Make it work on XPM with zero characters per pixel. pm_make_tmpfile_fd: fix improper handling when unable to get memory to construct file name. ppmquantall: replace with pnmquantall and change from Bash to Perl. Windows/Mingw: fix bug: temporary file creation fails consistently. Broken in Netpbm 10.34. Windows/Mingw: set binary mode on files. Windows: compute program name correctly. configure: work around Perl bug that causes 'configure' to falsely conclude Svgalib is installed. Build: Use rand/srand instead of random/srandom because the latter are not present in Mingw builds. Build: fix bug finding X libraries for merge buildl Build: redo conditional compilation of Windows to use MSVCRT, based on _WIN32, instead of WIN32. Build, Windows: various fixes. Build: use Pkgconfig to find libxml2 if it works (fall back to xml2-config). 11.12.30 BJH Release 10.57.00 pnmnorm: add -midvalue, -middle . pngtopam: fix crash with invalid tIME chunk. Always broken. pamarith: fix wrong result with -multiply. Broken in Neptbm 10.41. pamscale: fix all black output with resampling. Always broken. Build: don't use . 11.09.28 BJH Release 10.56.00 Add pamexec. Thanks Michael Pot . pbmclean: add -extended. Idea from kugland@gmail.com. rasttopnm: add -index. pamcomp: Retain opacity information from underlying image. pnmtops: Add PBM fast path. Thanks Prophet of the Way . libnetpbm and most programs that use color maps: speedup with new color hash function. Thanks Prophet of the Way . pnmquant: use File::Temp::tempfile() instead of local approximation if it is available. pnmquant: work with older Perl that doesn't have 3-argument open. Thanks Slaven Rezic . pamscale: Issue error message instead of ignoring extraneous arguments. libnetpbm, many programs: fix bug in pm_allocarray(): returns NULL when can't get memory. Calling programs thus crash on very large images. Introduced in Netpbm 10.51. pnmtopng: fix bug: with -alpha specifying a mask which contains no fully transparent area, output PNG is fully opaque. Introduced in Netpbm 10.29. pnmtops: fix message: says "from top edge" where it means "from bottom edge." ppmcie: fix bug: fails with "X coordinate out of range" error. Introduced in Neptbm 10.51. bmptopnm: Fail properly with BMP that indicates an illegal bits per pixel. Build: To find libpng, use the Pkgconfig database entry instead of libpng-config if it is available. Build: pnmtops: Instead of omitting pnmtops from the build when there is no libz, just omit ability to create flate-compressed output from pnmtops. 11.06.29 BJH Release 10.55.00 Add pamtosrf, srftopam. Thanks Mike Frysinger (vapier@gentoo.org). pgmtexture: fix wrong sum variance result. Wrong since the beginning. Thanks Francois P. S. Luus . pamrubber: fix crash. Introduced in Netpbm 10.54. libnetpbm: pm_system(): fix bug - program always takes Standard Input from invoker's Standard Input when you don't supply a Standard Output accepter. Introduced in Netpbm 10.40. ppmtobmp: fix bug: crash ("internal error") or bogus failure or incorrect output on image without palette (e.g. black and white). Introduced in Netpbm 10.54. pnmtops: fix floating point exception or bogus width/height too large error. Introduced in Netpbm 10.53. pnmcat: Fix garbage output when multiple input image are from Standard Input (now it just fails gracefully). Build: Use 'pkg-config' to find X libraries if possible (This works with modern Xorg installations). Build: pnmtopng, pamrgbatopng compile with newer libpng, as they no longer refer to private members of the pnginfo structure. Build: fix undefined "strsol" and "vsnprintfN" failure on systems without vasprintf(). Introduced in Netpbm 10.53. Build: fix compiler warning in pbmtocmuwm. configure: fix selection of mingw compiler. Introduced in Netpbm 10.48. 11.03.30 BJH Release 10.54.00 Add pamrubber. Thanks Willem van Schaik . Add ppmtoapplevol. Thanks Matthew Garrett . Add pamwipeout. Thanks Willem van Schaik . pngtopam: add -byrow. pnmtopclxl: add -embedded. pgmtoascii: Truncates instead of undefined behavior if input image is wider than specified width. Add libpamd (PAM version of classic libppmd drawing routines). Rename pbmtoicon to pbmtosunicon, to go with change of icontopbm to sunicontopnm in Netpbm 10.53. g3topbm: correct error message: codes go up to 13 bits. pamstereogram: fix crash when not doing texture. pgmtoascii: Fix numerous output bugs. pngtopam: fix bug: -verbose reports history chunk present when it's really a palette. pnmpsnr: fix bug: says PGM images differ when they don't and vice versa. 10.12.30 BJH Release 10.53.00 Add pammosaicknit. Thanks Scott Pakin. pstopnm: Add -textalphabits, default Ghostscript TextAlphaBits to 4. pngtopam: include tuple type in output. Thanks Thomas Henlich . sunicontopnm: Understands Depth=8. Thanks Prophet of the Way . sunicontopnm: Validate header. Thanks Prophet of the Way . bmptopnm: Don't crash on invalid zero value of image height in the BMP header of a compressed file. Always broken. bmptopnm: don't crash on large invalid value of 'colorsused' in the BMP header. Introduced in Netpbm 9.11. pm_mallocarray2: fix wild pointers when image is too big to fit in a single chunk of malloc memory. Affects many Netpbm programs. Introduced in Netpbm 10.51.00. ilbmtoppm: Don't crash on image that has a transparent color index, but no color map. sunicontopnm: fix for 32 bit items in input. Thanks Prophet of the Way . sunicontopnm: fix arithmetic overflows. Thanks Prophet of the Way . icontopbm: rename to sunicontopnm. pamstereogram: Add mapped texture stereogram option. Thanks Scott Pakin. pamstereogram: slight change to dpi and eye separation defaults. invert near/far dark/light association. Thanks Scott Pakin. pnmtops: Fix incorrect output with -flate. Thanks Prophet of the Way . sgitopnm: Fix arithmetic overflow on -channel option. Thanks Prophet of the Way . sgitopnm: Fix crash with -channel on verbatim SGI image. Thanks Prophet of the Way . fitstopnm: Deal properly with NaN in input image. pm_floatFromBigendFloat, pm_doubleFromBigendFloat, fitstopnm: fix corrupted output. Broken in Netpbm 10.46. pamtopdbimg: fix corrupted output image. Thanks Scott Pakin. pdbimgtopam: fix corrupted output image. Thanks Scott Pakin. pnmtops: fix arithmetic overflows. Thanks Prophet of the Way . Rename all external symbols that don't have a Netpbm prefix to start with "pm_": shhopt, nstring, nsleep. Eliminate createBlackTuple -- pnmCreateBlackTuple already existed. libnetpbm: Change shared library major number to 11 because of above renaming. build: include -lm in build of pamtopdbimg, pdbimgtopam. configure: Make warnings stand out more. configure: Fix crash when libpng test compile fails. 10.09.26 BJH Release 10.52.00 Add pamtopdbimg, pdbimgtopam. Add pamrecolor. Thanks Scott Pakin. anytopnm: Use 'pngtopam' shell command instead of 'pngtopnm'. pnmtopng: -libversion doesn't report level of linked libz. It was a modularity violation and caused build failures on Mac OS X, because pnmtopng per se doesn't know about libz -- it's used by libpng. libnetpbm: add pm_readbiglong2, pm_readbiglong2u, pm_readlittlelong2, pm_readlittlelong2u: These use the proper 32 bit integer types instead of "long". (But the old ones still work in legacy code because long is always at least 32 bits). pnmconvol: fix reversed sense of -nooffset. Introduced in Netpbm 10.49. ppmtompeg: fix crash with free of unallocated memory. Broken after Netpbm 10.18, not later than 10.26. Build: fix parallel make - multiple simultaneous attempts to build lib/util/importinc. Build: don't fail due to SIGRTMIN, SIGRTMAX being undefined. Build: don't expect snprintf() to exist. Build: don't use or bzero(). Build: fix PNGVER. Thanks Matthew Fischer . 10.06.27 BJH Release 10.51.00 Add ppmtoascii. Thanks "Frank Ch. Eigler" . pnmtops: Add -bitsperpixel option. pamx: Make exit status 0 instead of 10 when window manager requests termination. pnmsmooth: Respect -plain. Thanks Prophet of the Way . pnmsmooth: Don't display pnmconvol messages (i.e. run pnmconvol with -quiet). pamflip: speedup for PBM. Use SSE2 and skip some idempotent pixel movement. Thanks Prophet of the Way . anytopnm: recognize "PC bitmap" in 'file' response as BMP. (in addition to existing "PC bitmap data"). libnetpbm, various PBM programs: Use SSE insted of MMX. Thanks Prophet of the Way . pbmtext/libpbmfont: Fix wild pointer; probably asymptomatic. Introduced in Netpbm 10.39. pbmtext/libpbmfont: Fix some error messages for bad fonts. pbmtext/libpbmfont: fix crash with a BDF font with negative left or bottom border. Introduced in Netpbm 10.39. pamarith: fix memory leak. Introduced in Netpbm 10.41. pnm_bytespersample(): fix bogus assertion on 64 bit system. pnmtops: fix bug: 12 bits per sample output when 8 would do. Introduced in Netpbm 10.40. palmtopnm: fix for pixel size 16. Thanks Paul Bolle . pnmsmooth: Fail politely when convolution matrix is so large as to bust the system's program parameter size limit on the invocation of pnmconvol. avstopam: fix incorrect output. pnmsmooth: fix arithmetic overflow with absurdly large convolution matrix dimensions. Thanks Prophet of the Way . pnmsmooth: fix wild pointer: causes wrong arguments to 'pnmconvol'. Introduced in Netpbm 10.50. Thanks Prophet of the Way . pamscale: fix -reduce. Introduced in Netpbm 10.27. pampaintspill: fix incorrect output. libnetpbm text drawing: fix incorrect output in all cases. Introduced in Netpbm 10.47. libnetpbm line drawing: fix bogus assertion, introduced in Netpbm 10.47. build: fix incorrect determination of when vasprintf() exists in libc. configure: don't default to /usr/X11R6/lib/libX11.so just because /usr/X11R6 exists. /usr/X11R6/lib must exist. 10.03.27 BJH Release 10.50.00 Add pamtoavs, avstopam. Thanks Scott Pakin. Add pampaintspill. pnmconvol: Add -normalize . pm_system(): Close extraneous file descriptors that, among other things, prevent child from seeing EOF. libnetpbm: Add PNM_GETR(), PNM_GETG(), PNM_GETB(). Same as PPM_GETR(), etc. libnetpbm: Add ppm_luminosity(). Same as PPM_LUMIN, but returns pixval. pnmhisteq: Equalize based on luminosity alone, rather than a strange combination of luminosity and HSV value. pamenlarge: Make special fast path for scale factors up to 10 (2, 3, and 5 already existed). Thanks Prophet of the Way . pamflip: Speed up for most images. Thanks Prophet of the Way . ybmtopbm: Assume YBM format has raster in natural order instead of byte-reversed. This is what pbmtoybm creates, and is most logical. We don't know if there are any existing YBM images or generators of them other than pbmtoybm. pbmpscale: speedup. pbmclean: speedup. pbmtext: fix crash when BDF font file contains spurious blank line. Ignore such blank lines. pbmpscale: fix arithmetic overflow on output image dimensions. pbmtogem, pbmtoybm, pgmtolispm, ppmtoilbm, pnmtosgi: fix arithmetic overflow with image dimension represented as 16 bit integer. pbmpage: fix garbage output. pnmhistmap: Fix crash with -width. Always broken. libppmd/ppmpat: fix wild pointer in ppmd_fill_drawprocp(); broken in Netpbm 10.47. palmtopnm: fix incorrect "PALM_DIRECT_COLOR_FLAG is not valid for version 3 encoding type" failure. Thanks Paul Bolle . pamtosvg: fix bug: occasional crash with out of range error. Introduced in Netpbm 10.42. palmtopnm: fix incorrect output with version 3 direct color. Thanks Paul Bolle . Build: don't fail due to SIGPWR being undefined. 09.12.30 BJH Release 10.49.00 Add pnmmercator. pnmconvol: Add -matrix, -matrixfile. pambayer: add -nointerpolate. jpeg2ktopam: Work with JP2 input as well as JPC. pamscale: Work on multi image stream. ppmrainbow: Make new directory for temporary files. Prevents interference by other user who shares the main temporary file directory. libnetpbm: When reading plain format PNM with PAM routines, validate pixel against maxval (necessary for integer non-overrun guarantees). pnmsmooth: change -dump option to output a new pnmconvol -matrix option instead of a PGM file (because pnmsmooth now uses -matrix). pnmtofiasco: fix bug: doesn't work with Standard Input. pnmsmooth: fix incorrect call to pm_system_lp() that makes it never work (but it wouldn't anyway because pm_system_lp() was broken -- see below). pm_system*: fix various bugs making it never work. xpmtoppm: fix wild pointer with color index > 127. xpmtoppm: fix wild pointer when the input has a color index value that is too large to be consistent with the number of colors and bytes per pixel. pamtouil: fix crash when -name option doesn't contain an underscore. pamtosvg: fix some bug with unset Z coordinate. pnmtofiasco, fiascotopnm: fix bug on bigendian machine. pngtopam: use png_create_read_struct() instead of png_create_write_struct(). Broken since Netpbm 10.48. configure: advise if adding -lz -lm fixes libpng link problem. makeman: some fixes or enhancements. 09.09.27 BJH Release 10.48.00 ppmsvgalib: Wait to initialize Svgalib to prevent it from interfering with error messages early code might issue, and leaving the console in an undesirable state if the failures cause the program to abort early. tifftopnm: wait for Pamflip processes to terminate before exiting. Remove pngtopnm from the package. Pngtopnm is now an alias for Pngtopam. pngtopam, pnmtopng: Compiles with libpng 1.4 beta. pamtotiff: do miniswhite properly with 8 and 16 bit samples. pamsumm: fix syntax error message. pambayer: fix unconditional crash/failure when you aren't using Standard Input. Add pbmtocis, cistopbm. Thanks John Elliott . Build: tifftopnm builds on systems without fork(). Build: work with JPEG library Version 7. Build: Configure recognizes libvga.a installed without libvga.so and offers that as default. Build: Configure recognizes the Mingw-64 compiler. Build: fix failure of a merge build on a system that doesn't have the PNG library. Build: fix failure to recognize static library in omitting -R from link. Build: use AR from config.mk instead of "ar" to build static libraries: libnetpbm, librle, libjasper. Build: don't use "uint". Package: fix installation of pnmplain on Windows (.exe suffix). 09.06.27 BJH Release 10.47.00 Add pamsistoaglyph. Thanks Scott Pakin. pnmpsnr: Use more precise math to conclude that images are identical. Add /usr/share/netpbm/rgb.txt to search path for color dictionary. Remove nstring.h from exported interfaces. giftopnm: improve efficiency. libppmd: Add ppmd_point-based functions (ppmd_linep(), etc). pnmremap: fix: -firstisdefault uses an arbitrary color from the map as default. pnmremap: fix -missingcolor: where map file (ergo output) is not depth 3, uses an arbitrary color. pnmremap: fix loss of dithering when the map (and thus the output) has lower maxval than the input. (Old code scales down to the new maxval before dithering). giftopnm: fix for unlikely case of a block smaller than a code. ppmpat: fix crash in -squig. ppmpat: fix crash when width or height is zero. ppmpat: fix wrong output with -poles. libppmd (ppmpat, ppmdraw): Fix various out of bounds number problems. ilbmtoppm: fix some bug in interpreting ILBM input. (due to wrong pm_uintFromBigend16()). From Netpbm 10.46. ximtoppm: fix crash in command line processing. ppmrainbow: exit with proper exit status upon failure. ppmfade: eliminate reference to undeclared subroutine. ppmquantall: don't use 'set' to set Bourne shell variable. pnmtile: fix reference to arbitrary storage in option processing. Introduced in Netpbm 10.42. pamstereogram: fix tuple type in output file (and crash with -verbose) with -patfile . pamstereogram: fix uninitialized bytes per sample value. pbmtomacp: fix buffer overflow. libnetpbm (ppmd): fix: don't ignore failure to read font file. Fix: remove inclusion of pm_c_util.h from sshopt.h, which is an exported interface header file, whereas pm_c_util.h is not. Lots of changes to integer code, bit access, word access, etc. None functional. Some speedup. make distclean: remove lib/compile.h. configure: use TEMP environment variable for TMPDIR if TMPDIR is not set. Don't try different temp file names forever; the problem may not be the filename. configure: offer Gcc as an optional compiler for HP-UX. 09.03.29 BJH Release 10.46.00 xwdtopnm: Add -cmapdump . xwdtopnm: Change maxval calculation for truecolor to fit X11 spec. Now it's determined by bits_per_rgb. xwdtopnm: Add warning about 2 byte sample output. pnmcrop: fast path for PBM. pnmcrop: Use border file instead of original image to determine the background color (i.e. the color of added margins). pnmscale: finally make it just an alias of pamscale, which obsoleted it in Netpbm 10.20. pnmcut: finally make it just an alias of pamcut, which obsoleted it in Netpbm 9.20. tifftopnm: fix orientation problem on big-endian machines. Introduced in Netpbm 10.42. pnmcrop: various fixes. g3topbm: fix array bound violation. Introduced in Netpbm 10.32. pnmcat: fix array bound violation with PBM top/bottom concatenation. Introduced in Netpbm 10.44. ilbmtoppm: Fix array bound violation with compressed ILBM. Introduced in Netpbm 10.18. fitstopnm: fix garbage output when input is little endian floating point FITS. picttoppm: Fix crash with -fullres and all-text PICT image. Also issue warning that program omits text with -fullres. picttoppm: Improve error/informational messages. picttoppm: Don't fail if 'fontdir' file doesn't exist. Bug from Netpbm 10.44. ppmtopict: Use two-byte length field when image width > 200 instead of > 250. Former is what Picttoppm has been assuming since March 2004, when it was found to work better than 250, which is the number documented as right. ilbmtoppm: fix bug: appends color map PPM to output if input has color map; fails if input doesn't have color map. Broken in Netpbm 10.18. leaftoppm: fix bug: uses red channel as all three channels; (produces grayscale output). Broken between Netpbm 10.19 and 10.26, inclusive. pbmtomrf, mrftopbm: fix crashes, incorrect output in all cases. Broken forever. pnm_alloctupletable, pnm_tuplehashtotable, pnm_computetuplefreqtable3: fix crash when out of memory, memory leak with uncomputably large numbers. build: rename getline() in xvminitoppm.c to avoid collision with libc. Source tree: split out some of editor/ directory into new editor/specialty directory. 08.12.28 BJH Release 10.45.00 pgmnoise: Add -randomseed. ppmtobmp: Add -mapfile option. pnm_createBlackTuple(): Fix array bounds violation with PBM, PGM. pnmtoddif: Fix crash with any PGM input. pgmnoise: Fix bug: never generates full white pixel. pamditherbw: Fix -value other than .5 with -fs, -atkinson. Configure: fix crash when Libxml2 is installed, but not new enough version. Build: Use GNU compile options for Darwin. Build: Move CFLAGS later in libjasper and librle build so that a -I in CFLAGS doesn't interfere with finding the local libjasper and Netpbm header files. Build: Move LDFLAGS later so that a -L in LDFLAGS doesn't interfere with finding the built libnetpbm. (The common link rule is already this way in Netpbm 10.44.00; all the other link rules are now the same). Build: Rename Makefile.common, Makefile.config, to common.mk, etc. Build: Add source type declarations to make -ansi compile work. 08.09.27 BJH Release 10.44.00 Add pngtopam as a replacement for pngtopnm. Has ability to generate image with alpha channel. Thanks David Jones . pnmcolormap (pnmquant): round instead of truncating when computing means. pamcut, pnmcat, pnmpaste, pnmpad, g3topbm: Add fast PBM path. Thanks Prophet of the Way . pnmpaste: fail if user specified stdin for both images. libppmd/ppmdraw: fix crash when line is completely out of frame. pamgradient: fix bug: always produces color, not grayscale. pnm_backgroundxel(), pnm_backgroundxelrow() (affects pnmrotate, pnmshear, pnmcrop, pnmcat: correctly average corner colors to determine background (fill) color. pamcut: don't crash when cutting a region entirely to the left or right of the input image, with -pad. Thanks Prophet of the Way . pamcut: don't crash when left > right or top > bottom with -pad. Thanks Prophet of the Way . pgmhist: arbitrary output when total pixels doesn't fit in an integer. pamcomp: fix bug: arbitrary output when combined number of rows doesn't fit in an integer. pamtosvg: remove "needed exchange" debug trace. Add pbmminkowski (source code has been in package since Netpbm 10.12 but not documented or built by default). pnmmontage: don't corrupt file names when they contain colons. pamflip: fix bug: garbage output for PBMs, since Netpbm 10.42. pngtopnm: fix gamma correction. pngtopnm: fix bug: when background is color, output should be PPM. pamperspective: fix bug: array bounds violation crash. Build: add dependencies so parallel make of top level "all" works. 08.06.27 BJH Release 10.43.00 Add pamtompfont: Mplayer bitmap font. pamthreshold: copy transparency information from input to output. pnmnorm: adjust bvalue, wvalue when -bpercent and/or -wpercent would cause an overlap, instead of failing. pamscale: fix PBM input + -nomix. anytopnm: fix parsing of type description. pamtilt: fix crash with excessive angle. pbmtext: remove meaningless "no output" from error messages. pbm_loadbdffont(): handle zero size glyph. Affects pbmtext. pbmtext: fail properly if input contains no text. pbmtext: fix buffer overrun with tab characters. pnmnorm: fix -colorvalue, -saturation, -keephues options. pnmnorm: fix resolution of conflicting -wpercent and -wvalue. pamtohtmltbl: fix output error: contains holes due to uninitialized memory. xvminitoppm: fix. Totally broken since Netpbm 10.34. pnmtopclxl: fix crash with Standard Input input. pbmtogo: fix buffer overrun with too wide image. tgatoppm: fix standard input invocation. pamtotga: fix free of unallocated memory with no -name option. pamtouil: fix double free of memory. ppmd_filledrectangle: fail properly on negative image height, width. Add back ppmd_fill_init() for backward compatibility; removed in Netpbm 10.29. Build: make it work with Gcc 4.3 and -sse. 08.03.26 BJH Release 10.42.00 anytopnm: Recognize xwd image by 'file' type. pamflip: Rewrite low-memory algorithm; use temp files and conserve virtual as well as real memory. tifftopnm: orient raster properly in row-by-row operation when TIFF uses nonstandard raster orientation. (ORIENTATION tag). pbmtext: fail cleanly if -width, -space, or -lspace is too large for computation. Thanks Prophet of the Way . pnmmargin: don't crash with zero margin request. pnmtile: deal with zero width/height. pbmtext: fix negative -space. pbmtext: fail cleanly if input line is > 5000 characters. pamundice: Fix bogus error about missing "-instem" option. pgmdeshadow: fix crash in option processing. pgmmedian: fix crash. pgmbentley: fix output; black areas not black. pamtosvg: fix various bugs: crash, poor tracing, memory leaks. pamtosvg: fix log file name with Standard Input. ppmtoarbtxt: fix array bounds violation with really large replacement sequence. ppmtoarbtxt: fix missing right parenthesis with impostor replacement sequence. pnmnlfilt: fix internal error "impossible alpha value". Add pm_tmpfile_fd() and pm_make_tmpfile_fd(). Various things to make it build on Windows without POSIX emulation. Build: Include LDFLAGS environment variable in link of build tools. 07.12.27 BJH Release 10.41.00 pamenlarge: much faster for PBM. Thanks Prophet of the Way . pamenlarge: remove arithmetic overflow. pnmmontage: new search algorithm to choose square output quickly. Thanks "Nic Roets" . bmptopnm: fix crash with 16 bit images. palmcolor8.map: Make it a 256 color map sorted by index, per Palm's spec. Thanks Paul Bolle . xwdtopnm: deal with too-large bits per pixel value in XWD header. pamditherbw, pamsharpness, pamsharpmask, pamtopfm: fix crash due to write to arbitrary memory. pamtotiff: fix incorrect pixels with PNM maxval != TIFF maxval. pnmcrop: fix -verbose message about background color with -white. pbmmake: handle ridiculously large height, width arguments. pnmcat: fix arithmetic overflow. libnetpbm: Add arithmetic overflow protection to PBM routines, like PGM/PPM/PNM have had for a long time. libnetpbm: make all row free operations go through pm_freerow(); change row buffer type from char * to void * for pm_allocrow(), pm_freerow(). set _XOPEN_SOURCE=600 so jpeg2ktopam and pamtojpeg2k compile on AIX (_XOPEN_SOURCE=500 keeps int_fast32_t from being defined). pamperspective: Eliminate build-time symbol conflict with 'nearest' on AIX. Fix bug: ppmdraw.h, ppmdfont.h not installed. Add -fPIC to all Gcc compiles of library modules, so it works on AMD64, IA64. Add Windows version of gethostname to ppmtompeg; normal build does not use it. package, install: install interface header files as instead of just . 07.09.26 BJH Release 10.40.00 pamfunc: Add -andmask, -ormask, -xormask, -not, -shiftleft, -shiftright. pbmtonokia: fail if input is > 255 x 255. Thanks Paul Bolle . pnmmargin: add -plain option. Thanks Prophet of the Way . pnmgamma: improve error messages. pamstack: accept Standard Input properly. pnmmargin: recognize invalid options better. Thanks Prophet of the Way . anytopnm, pnmmargin, pamstretch-gen, ppmquantall: fix small temporary file security exposure. pnmtopalm: fix typo in error message. Thanks Paul Bolle . pnmtops: fix crash when program attempts to write 12 bits per sample Postscript. It doesn't really know how to do that, so now it just writes 8 unless you do -psfilter. pnmtopng: Sort palette properly when alpha maxval != 255. pnmtopng: Fix array bound violation with = 256 colors and -alpha. pnmtopng: Fix array bound violation with > 256 alpha mask levels. pamtopfm: Fix wrong selection of default endianness. libnetpbm: Add pm_system_lp(), pm_system_vp(). libnetpbm: Make pbm_readpbminit() and pgm_readpgminit() recognize incompatible Netpbm formats and issue a helpful error message. libnetpbm: pbm_readppminit: read PBM as maxval of 255, not 1. libnetpbm: Fix pm_drain() to use the specified limit. Thanks Paul Bolle . libnetpbm: Fix multi-line TUPLTYPE interpretation: separate lines with blank, not newline. Thanks Paul Bolle . libnetpbm: Reject empty TUPLTYPE header. libnetpbm: Fix uninitialized variable in pnm_allocpamrown(). libnetpbm: fix crash in REALLOCARRAY with advanced optimization. libnetpbm: REALLOCARRAY frees memory if it fails. Rename pamtooctave to pamtooctaveimg. Build: don't build pstopnm on system without unix process management. Build: put -l's after .o's in test links. Build: don't use 'uint' type. Solaris 10 apparently doesn't have it. Build: Add missing to ppmrough.c. 07.06.26 BJH Release 10.39.00 Add pamtooctave. Thanks Scott Pakin (scott@pakin.org). Add pamundice. fitstopnm: add -omaxval. pnmremap: add -norand. pbmtext: improve error messages about fonts. pamtofits: fix -min, -max. fitstopnm: fix BITPIX = -32. PAM_STRUCT_SIZE: cast pointer to ulong instead of uint. pamthreshold: fix totally bogus threshold selection with simple thresholding. Configure: do test compile for missing Libxml2 and too old Libxml2. Configure: fix bug detecting presence of libvga with Ldconfig. Configure: build properly for Mac OSX when user says libnetpbm will be in the default search path. Build: don't use 'uint' type. Mac OSX apparently doesn't have it. 07.03.30 BJH Release 10.38.00 Add pamfixtrunc. pamtogif: Add -aspect. Thanks Prophet of the Way . pamditherbw: Add -atkinson. pammixinterlace: Add -filter and fir and ffmpeg filters. Thanks Bruce Guenter . pammixinterlace: Add -adaptive. Thanks Bruce Guenter . pambackground: recognize mid-row background. ppm3d: Change default offset to zero columns. ppm3d: Add -color option. ppm3d: Add -offset option as alternative to offset argument. jpegtopnm: Add -repair option. giftopnm: Add -repair option. xwdtopnm: use pm_drain() to catch some format misinterpretations. pamtogif: Speed up for monochrome images. Thanks Prophet of the Way . pamtogif: Speed up for small images by using smaller hash table (so smaller memory footprint). Thanks Prophet of the Way . libnetpbm: add pm_drain(). libnetpbm: shhopt: reject signed number as value for OPT_UINT option. libnetpbm: in the "no such option" error message, say what the valid options are. libnetpbm: Embellish "bad magic number" error message. pnmremap: fix incorrect output with map file deeper than input file. xwdtopnm: fix right edge padding for 24 bit per pixel, 32 bit per item images. xwdtopnm: update assumptions about format for direct color images to match an image we found. pnmtopclxl: fix -feeder, -outtray options. Thanks "Eric K. Olson" . picttoppm: Fix wild memory reference in all use cases. picttoppm: Fix for multi-pixel-per-byte image in which it says the image has a row that is too long (because of padding). pamtogif: fix crash with -mapfile. ppmtogif: handle case that map file and input file are not the same depth, as was the case with the original ppmtogif. Run the input through pnmremap. pamtoxvmini: fix bug: produces garbage when maxval is not 255. pamtoxvmini: fix crash. libnetpbm: fix buffer overrun with PBM images < 8 columns. libnetpbm: fix crash with PBM images < 8 columns on MMX/SSE machine. pamtogif: fail properly if image to wide or high for GIF. ppmdraw: fix crash with use of freed storage. Thanks John Walker . libnetpbm: fix crash with PAM read as PNM. installnetpbm: use 2-argument open() for old Perl. configure: fix choice of default library suffix. configure: default to 'none' for Svgalib if it doesn't appear to be installed. 06.12.31 BJH Release 10.37.00 Add pambackground. But doesn't find mid-row background yet. pnmcrop, pamtopnm: work on multi-image stream. Thanks Erik Auerswald . pnmshear: add -background. Thanks Erik Auerswald . ppmtogif: replace with pamtogif. Fix garbage output with -interlace. pbmtoxbm: add -name option. Thanks Prophet of the Way . ppmshift, ppmspread, ppmforge, pgmnoise, pgmcrater: better randomization; won't produce the same image if you run it twice within the same second. pbmtoxbm: Use packed PBM functions for efficiency. Thanks Prophet of the Way . xbmtopbm: Use packed PBM functions for efficiency. Thanks Prophet of the Way . cmuwmtopbm, mgrtopbm, pbmtocmuwm, pbmtoicon, pbmtomgr: Use packed PBM functions for efficiency. Thanks Prophet of the Way . libnetpbm: add pnm_colorname(). libnetpbm: add pnm_computetuplefreqtable3(). Has ability to limit number of planes of input considered. libpam: better checking of sufficient size of struct pam in pnm_writepaminit(). pnm_readpaminit(): abort instead of crash if width == 0. pnmtopng: fix transparency optimization on PPM input. pnmrotate: fix -background option with PGM/PBM input. pamx: fix bug with X depth = 0. pamx: fix bug: incorrect display of one-plane input image. libnetpbm: fix bogus results from ppm_parsecolor() of rgb:0/0/0 style color name. pbmtoxbm, pbmtox10bm: merge. Thanks Prophet of the Way . pbmtox10bm: Fix generation of name in XBM file. Thanks Prophet of the Way . pbmtextps: Fix buffer overrun -- typical symptom: extra text generated. ppmchange: Fix bug with -closeness and not -remainder or vice versa. pgmtoppm: Fix garbage output with -map and input maxval != map maxval. pbmtext: Fix crash when there is only one character. pnmnlfilt: Give proper error message for syntax errors. ppmtompeg: fix reading of GOPs. Thanks Alun Jones. Fix pamcomp: due to use of undefined value, sometimes produces plain format. Fix pamperspective: read all of input. Fix libnetpbm pm_system(): don't hang if shell command quits before taking all the input. Build: Configure does test links to detect broken libpng-config --ldflags. Build: Fix some compile failures with Irix IDO compiler. Build: fix Darwin (Mac OS X) build. Configure: fix default for X library location. Build: remove unnecessary limit of 200 characters on argument length in Libopt. Build: fix build with separate build directory. Build: fix merge build with no X11 library. 06.09.21 BJH Release 10.36.00 pbmtonokia: Add plain NPM output capability. Thanks Paul Bolle . pbmtonokia: Pad to 8 bytes. Thanks Paul Bolle . pbmtonokia: Limit text to 120 bytes. Thanks Paul Bolle . pamthreshold: don't gather global information, and reread file, for local thresholding (to save time, resources). Thanks Erik Auerswald . libnetpbm: BK color matching adjustments, change of enum bk_color. Thanks "Kenan Kalajdzic" . Fix version number control. pbmtonokia: fix headers of NGG and NOL to include 3 character magic. Release allocated memory before longjmping from libnetpbm. Eliminate pm_perror() in favor of informative error messages. libnetpbm: fix rgb:r/g/b color name parsing for invalid syntax. Add pm_errormsg(), pm_setusererrormsg(), pm_setusermessage(). pm_accept_to_pamtuples: fix bug: fill in pam structure. Thanks "Christian Schlotter" . Fix selection of MMX_SSE fastpath based on GNU compiler version number. Properly clean thinkjettopbm.c with 'make distclean'. Remove some generated files from release so build works. Remove general history from change file 06.08.19 BJH Release 10.35.00 Add pgmdeshadow. giftopnm: add -quitearly. pamfile: add -comments. ppmdraw: remove limitation on size of script. pnm_readpaminit(), pnm_writepaminit(): Add comment control. ppmtogif: do one row at a time. Pnmtopng: improve validation of -modtime option. Look in /usr/share/X11 instead of /usr/openwin/lib for rgb.txt. ppmtompeg: die properly when frame is less than 16x16. ppmdraw: fix bug with semicolon in script not followed by white space. libppmd (ppmdraw, ppmlabel): don't crash with horizontal or vertical line that is entirely out of frame. picttoppm: Fix 32 bit per pixel conversion, broken in 10.34. pamthreshold: fix read from pipe. Fix typo in make file that makes svgtopam get built when Libxml2 is not available. pnm_computetuplefreqhash(), pnm_computetuplefreqtable2(): Don't crash on error without pm_setjmp(). Change memmem() to memmem_internal() in cameratopam/identify.c to avoid collision with system library. Use __MINGW32__ to determine HAVE_MKSTEMP (instead of manual). Eliminate use of network functions for endianness computations. Use "inttypes_netpbm.h" on a system that doesn't have the int_fast32_t, etc. types. Like Solaris 8. configure, installnetpbm: autoflush stdout. Build: Use libpng-config if it exists. Build: assume no Lex program if neither 'flex' or 'lex' shell command verb exists. Remove test source file ppmdtexttest. Ppmdraw should be sufficient now. 06.06.18 BJH Release 10.34 Add pamthreshold. Thanks Erik Auerswald . Add pamx. Add pamtoxvmini. pammasksharpen: Add -threshold. pnmtopng: make "N colors found" message verbose-only. pnmtopng: make "no room in palette" message non-verbose. picttoppm: Tolerate various PICT file corruptions. picttoppm: Don't issue warning message when file named 'fontdir' doesn't exist. libnetpbm: Add ppm_bk_color_from_color(), ppm_bk_color_from_name(), ppm_name_from_bk_color(). Thanks "Kenan Kalajdzic" . libnetpbm: Add ppmd_fill_path(). ppmtobmp: Fix for PBM input. bmptopnm: Don't crash on BMP with no color map. bmptopnm: Fix wrong file name in error messages. ppmtogif: fix bug: always produces garbage output. ppmtompeg: fix input from Standard Input. pnmflip: fix bug: -rotate90, -rotate180, and -rotate270 (and synonyms) don't work when followed by other rotation options. ppmtoilbm: Fig bug: generates more planes than necessary. pamtofits: fix buffer overflow in asembling header. picttoppm: fix bug - interprets some images wrong because of bogus "rowBytes" value. libnetpbm: add ppm_fastlumin(). Change ppmtopgm, ppmtoilbm, and ppm3d to use this, which results in higher quality output, because ppm_fastlumin() rounds to nearest, whereas these programs rounded down. Otherwise, it's the same calculation. Redo asprintfN(), etc. so as not to use va_list in a way that doesn't work on some machines. cameratopam: remove definition of memmem() so it doesn't collide with same in some C libraries. Add memmemN() and MEMEQ to libnetpbm. Fix build of filename.o. Build: Use local version of mkstemp() based on mktemp() when mkstemp() isn't available. No automatic determination that mkstemp() isn't available yet. Build: Include dummy pm_system() on a system that doesn't have regular Unix process management. Add -Wundef to Gcc compile options. 06.03.26 BJH Release 10.33 Add pamtosvg. g3topbm: Add -width, -paper_size. libnetpbm / most newer programs: Fix bug that produces plain format output when it should be raw because pnm_readpaminit() does not set 'plainformat' and most programs just copy the input pam to the output pam. pamflip: fix bug with left/right flip of PBM that has width an even multiple of 8 plus something less than 8. pnmquant: turn on autoflush when creating seekable file. install: fix symbolic link pnmdepth -> pamdepth. build: fix some importinc dependencies. 06.02.25 BJH Release 10.32 Add rlatopam. Thanks Simon Walton . Add pgmmake. bmptopnm: Understands RLE4/RLE8 compressed BMP. Thanks Prophet of the Way . pnmgamma: Add -bt709tosrgb -srgbtobt709, -bt709tolinear, -lineartobt709, -gamma, -rgamma, -ggamma, -bgamma (gammma values were formerly specified as parameters). pnmgamma: Rename -cieramp to -bt709ramp. pnmnorm: add -maxexpand option. ppmdraw: work on multi-image streams. anytopnm: add mime time image/x-ms-bmp for BMP pamchannel: works on multi-image streams. pamstack: works on multi-image streams. Convert pnmdepth to pamdepth. Add multi-image stream capability. pamcut: works on multi-image streams. pnmtops: allow -flate and -rle together. pnmtops: overhaul of -psfilter output. Thanks Chapman Flack . pnmrotate: fine adjustment to arithmetic (rounding, pixel quantization). pbmtopsg3: Put currentfile ... in exec block. Thanks Chapman Flack pbmtopsg3: Add missing /EndOfBlock . Thanks Chapman Flack pnmtops: Don't claim EPSF if using setpagedevice. Thanks Chapman Flack giftopnm: do "reading image sequence N" message only if being verbose. ppmtobmp, bmptopnm: major speed improvement for PBM. Thanks Prophet of the Way . pbmtog3: Use new GCC features instead of assembly language for superspeed operations. Thanks Prophet of the Way . pm_make_tmpfile(): Use TEMP and TMP environment variables if TMPDIR not set. pm_make_tempfile(): improve error message. libpam: pnm_writepamrowmult() respects pam.plainformat. libpam: pnm_writepaminit() checks -plain option for PAM format (recognizes the error). ppmtoglobe: Fix wild pointer bug. Fix wild pointer in REALLOCARRAY(). Affects ppmdraw, escp2topbm, ppmtowinicon, ppmtompeg, pnmtopalm, pnmtopng. Thanks Steve Summit . pnmtopng: fix bug with undefined cmdline.modtimeSpec. Thanks Mike Frysinger . pnmquant: Use Perl sysseek() instead of seek() to avoid a mispositioned file disaster on some platforms. pamditherbw, pgmtopbm: fix bug: overly dark with cluster methods because only 1/4 of the dither matrix is used. Thanks Mark Williams . pnmrotate: fix bug: garbage in lower right corner of background with -noantialias. pnmsmooth: change to regular Netpbm syntax. Replace -size with -width and -height. pnmsmooth: fix bug: free of temp file name even when there is no temp file (-dump option). pnm_scaletuplerow() fix bug: does nothing when old maxval == new maxval. Affects pamedge only. ppmtompeg: Fix array/pointer degeneration mess with LumBlock arguments; fix crash. pamstereogram: Fix crippling bugs. Thanks Scott Pakin . giftopnm: Handle case of a clear code at the end of a block. ppmtogif: Fix bug with interlaced GIF < 5 rows. cameratopam: Fix segmentation fault from undefined 'ifp'. Thanks Bernard Hatt . xwdtopnm: Fix bug from 10.31: LSB-first XWDs convert to all black. ppmglobe: fix bug: always says stripcount is zero because it looks at the wrong argument. Thanks Urs Enke . pnmtotiffcmyk: Fix inttypes conflict on AIX. Stop exporting stripeq(). Add -lm to link of libnetpbm shared library, to express the fact that code in libnetpbm requires it. Helps GNU Ld --as-needed. Build: move symbolic links to header files into 'importinc' directories to keep directories cleaner. Fully populate 'importinc' in every directory so we don't have to maintain a list of header files for each. 05.12.23 BJH Release 10.31 Add pamgradient. libnetpbm/everything: speed up ppm_readppmrow() and pgm_readpgmrow() by doing one fread per row instead of a pgm_readrawsample() per sample. Thanks "Ariel Berkman" . libnetpbm: Make PGM, PPM, and PNM routines read suitable PAM images. pnmsplit: upgrade to pamsplit. xwdtopnm: Add ability to work with bits per pixel > bits per item. Replace whole pixel reader. pamtotiff: Use TIFFDefaultStripSize() for ROWSPERSTRIP default. pamtotiff: add -tag option. Thanks Gary Gorbet . ppmglobe: add -background, -closeok, filename argument. pnmcolormap: Work on multi-image stream (one map for all). pnmtopclxl: read/convert/write one row at a time. Thanks Martin Buck . ppmtolj: read/convert/write one row at a time. Thanks Martin Buck . ppmtoxpm: fix bug: produced incorrect output when number of colors is a power of 92. Thanks Mark Weyer. pnmcolormap: fix bug: crashes with input depth > 3. pambayer: fix bug: doesn't actually read input file. ppmtompeg build: add missing const to work around TRU64 compile failure. xwdtopnm: Fix handling of padding at end of XWD row. pamtotiff: fix bug with PAMs with > 3 planes. Remove PPM_PACKCOLORS. pnmremap: fix arithmetic overflow bug with maxval > 255 that caused random pixels. remove global "cmdline" variables from all programs (4 had them). pnmtops: remove extra %%%%Page from 10.27. mkinstalldirs: remove chmod 755 to match Automake's mkinstalldirs. libopt: add -quiet option ppmtojpeg: fix prototype mismatch when building without the JPEG function. libnetpbm: fix: PPM_DISTANCE uses red twice instead of and green. pnmnlfilt: fix alpha ranges. cameratopam: replace setenv with putenv so it works on Solaris. cameratopam: put in workaround for Solaris header file bug. pngtopnm: fix bug: grayscale PNG produces PPM. s/b PGM. anytopnm: Use sed instead of Awk -- more universal. anytopnm: fix "INTERNAL ERROR" crash due to "filetype" variable not set. rletopnm: fix crash when input file is empty or there are various other problems reading its header. rletopnm: fix universal crash in option parser. tifftopnm: fix crash due to uninitialized variable. libnetpbm: Fix bug from 10.30: erroneously says a PAM header line is missing. 05.10.16 BJH Release 10.30 Add pambayer. Add pamrgbatopng. Add pamtilt. Thanks Gregg Townsend . pamtotiff: create from pnmtotiff. pamtofits: create from pnmtofits. pamaddnoise: create from pnmaddnoise. pamarith: Add -divide. pammasksharpen: Add -sharpness. pnmtopng: add -comp_mem_level, -comp_strategy, -comp_method, -comp_window_bits, -comp_buffer_size. pnmtopng: use Shhopt command line processor; replace -chroma with -rgb, -phys with -size, -time with -modtime. pnmremap: Handle multiple image stream. xpmtoppm: Expand capacity from 2K character input lines to 8K character input lines. libnetpbm: improved messages for "color xxx cannot be represented precisely with maxval M" libnetpbm color specification parsing: Improve warning message about rounding. libnetpbm/everything: speed up ppm_writeppmrow() and pgm_writepgmrow() by doing one fwrite per row instead of a pgm_writerawsample() per sample. pnmtopng: fix "meaningful bits" optimization of PGM so it optimizes all the way instead of stopping at 4 bits. pnmtopng: fix crash when no -alpha option, due to dereferencing of undefined alpha_mask. pnmtopng: fix bug: arbitrary color made transparent in paletted PNG when nothing should be transparent. pnmtops: bug: selects wrong Postscript level. ppmtowinicon: use REALLOCARRAY instead of arithmetic-overflowing realloc(). anytopnm: fix bug: fails when 'file' database doesn't have mime type but does have human-readable type. manweb: recognize directory as not executable in search of PATH. pgm_allocrow(), ppm_allocrow(), pnm_allocrow(): cure arithmetic overflow. libnetpbm: On plain formats, check for sample value exceeding maxval. libnetpbm: reject maxval > 65535 in "pam" functions. pamarith: Use normalized libnetpbm facilities to get proper accuracy. Thanks Thomas Henlich . pamarith: fix bug: shift functions don't work when maxvals are not identical. pamarith: fix rounding error on multiply. pgmtexture: correct calculation of normalizing constants for d > 1. Thanks Marc Breithecker . pnmnorm: fix divide by zero crash. pnmnorm: fix bug that doesn't let you specify -luminosity, -colorvalue, or -saturation. ilbmtoppm: fix variable used before set. pnmtofits: fix 16 bit sample values > 2**15. pnmtopsnr: minor adjustments to messages. pnmnorm: don't crash when wvalue == bvalue. Thanks Thomas Henlich . pnmnorm: fix rounding of output values. Thanks Thomas Henlich . pnmhisteq: Fix external map to have width maxval + 1 instead of maxval, so full intensity input pixels map properly. pnmhisteq: Fix -rmap. Fix typos in error messages in various programs. pgmmedian: change syntax to Netpbm standard. pammasksharpen: fix signedness bug that caused random bright pixels at edges. pammasksharpen: fix bug with maxval != 255. Clean up a bunch of function prototypes so some broken compilers don't complain about missing "const" in them. Remove dependency of pnmtops on basename(). It isn't in some C libraries and we don't want to mess with linking extra libraries. Build of ppmsvgalib: work properly when LINUXSVGALIB is a "default path" value such as "libvga.so". 05.08.15 BJH Release 10.29 Add ppmdraw. Add pammixinterlace. Thanks Bruce Guenter . Add pgmmedian. Add pnmaddnoise. pnmtopclxl: add -rendergray option. pnmtopclxl: add -jobsetup option. pstopnm: add -dSAFER Ghostscript option. pnmcrop: add -margin option. pnmcrop: add -borderfile option. pnmnorm: add -luminosity (which was already default), -colorvalue, and -saturation. pnmtopng: Don't include any alpha info if supplied alpha mask is all opaque. ppmcie: Make white point mark more visible. pamdice: Remove restriction of 100 slices in each direction. pnmtopng: Fix bug: erroneous transparent pixels when supplied alpha mask is all opaque. Fix bytesToWordInt(): converts incorrectly on a machine that has pointer size != word size and uses the general case code. libnetpbm: fix basic pm_readlittleshort() bug introduced in 10.27. Affects mdatopbm, rawtopgm, lispmtopgm, pcxtoppm, winicontoppm, bmptopnm, sirtopnm, xwdtopnm, cameratopam. pamlookup: fix universal crash. Thanks "Colley, Anthony W." pbmtolj: fix bug from 10.28: crashes with a line which is a multiple of 8 plus 7 pixels wide not counting white right margin. ppmtompeg: fix uname() rc test (sometimes > 0 is success). 05.06.06 BJH Release 10.28 Add cameratopam (Dave Coffin's dcraw). ppmtoxpm: ignore empty or similar malformed XPM line instead of giving up. pbmtolj: do multi-image stream. pnmindex: rewrite in C. Fix Gcc 3 warnings. pamcomp: fix out of memory problem due to uninitialized pam.allocation_depth. anytopnm: remove apparently redundant traps of signals. pnm_alloctupletable(): fix bug from 10.27: returns random value. (affects pnmcolormap, pnmremap, pnmquant, others). jpeg2ktopam: fix array bounds violation on 64 bit machines. Thanks Frederic Devernay . rle_open: remove extraneous errno declaration. Thanks Joerg Sonnenberger . pm_tmpfile, pm_make_tmpfile: rearrange code. ppmtompeg: fix insecure temp file. Thanks Alexey Tourbin . libnetpbm: fix basic readlittlelong() bug introduced in 10.27. Affects xwdtopnm, bmptopnm, winicontoppm, lispmtopgm. pbmtext/libnetpbm: fix crash with -builtin=fixed. pbmtoppa: fix floating point exception always due to undefined DPI. fix jpeg2000 build failure on system without inttypes.h, etc. Build now excludes jpeg2000 if you don't have 64 bit capability, which practically means you have inttypes.h, etc. jpeg2000: don't do unsafe temporary files. ppmshadow: make it work with old Perl. pnmquant: remove use of Fcntl::seek, not available in Perl 5.00503. pnmquant: make -quiet option work. pnmgamma: Correct math in -srgbramp. Add proper rounding. Thanks Horst J. Wobig (hjw.fsw@wobig.de). ppmshadow: fix blurring of non-translucent shadow. ppmtoxpm: fix use of uninitialized variable that results in invalid color map. Fix backward pnmtopnm - pamtopnm symlink. 05.03.28 BJH Release 10.27 tifftopnm: do multi-image streams. pnmtotiff: add -append option. pnmtotiff: do multi-image streams. pnmtopclxl: fix bug with invalid palette in SetColorSpace command. pnmtopng: don't accept -background without -mix. Eliminate pnmtopnm program -- install 'pnmtopnm' as an alternate name for 'pamtopnm' instead. pamscale: refilter as part of resampling for better quality downsampling. ppmcolormask: fix segfault with stdin input. Add pm_setjmpbuf(). pnmindex, ppmquantall: use pnmquant instead of ppmquant. pnmquant: add -quiet, -plain options ppmfade, pnmshadow, pnmindex, pamlookup: Use pamscale instead of pnmscale. ppmtoxpm: Don't include superfluous entries for colors of transparent pixels, or for transparency when there are all colors are opaque, in the XPM color map. pnmquant: remove dependency on File::Temp, not available before Perl 5.6.1. pamfunc: use multiplication instead of division for -divisor, for speed. pamfunc: fix bug in -adder and -subtractor with signed arithmetic. Thanks David Jones . pamscale: fix bug in converting negative floats to integers. Thanks David Jones . pamscale: fix bug with -filter: scanbuf too small, thus some output rows/columns can never be created and output raster is invalid. build: fix build errors in ppmtompeg with OMIT_NETWORK. jpeg2ktopam: fix uninitialized memory problem in allocating jasperCmptNo. pbmclean: fix -minneighbors so it isn't really -minneighbor. pnmremap: allow RGB map and BLACKANDWHITE/GRAYSCALE input. pamstereogram: fix bug with -eyesep (float/double). pamtojpeg2k: fix bug with -compression (float/double). pamtojpeg2k/jpeg2ktopam: work on 64 bit machine; fix definition of int32fast_t, etc. in libjasper. libnetpbm: Respect plainness of specified format when writing. pnmremap: fix handling of arithmetic overflow in color distance calculation. pnmremap: fix bug with palette of different depth from image. libnetpbm: fix 2 byte per pixel pnm_pamwriterow(), pnm_formatpamrow(). pbmmake: fix bug: requires color option. (Introduced ca. 10.23). -plain works with PAM write functions. common -plain option causes failure instead of wrong output when used with PAM output. pnmremap: fix rounding error where map maxval is small and unequal to image maxval. Add pnm_normalizeRow(), pnm_unnormalizeRow(), pnm_creategammatransform(), pnm_createungammatransform(), pnm_freegammatransform(), pnm_freeungammatransform(). Idea and prototype from "Scott T. Smith" . pamscale: Use pnm_noramlizeRow() to speed up gamma adjustments. palmtopnm: Don't assume Palm Bitmap is valid. Thanks Paul Bolle . giftopnm: fix crash with input that has bogus color indices in the raster. giftopnm: fix crash with input that has a bogus minimum code length field. Thanks deekoo@tentacle.net (Deekoo L.). pnm_readpamrown(): use pnm_readpamrow() instead of pm_getraw() for speed. Similar for pnm_writepamrown(). pnm_readpamrown(): use multiplication instead of division for speed. Thanks "Scott T. Smith" . pm_readbiglong(), etc.: pm_error() in case of failure. Add pm_readchar(), pm_writechar(), pm_readcharu(), pm_writecharu(). pnmtopalm: Add packbits compression. pnmtopalm: Add version 3 capability (-density, -withdummy). pnmtopalm: fix bug: incorrect between-image pad amount. pnmtopalm: fix compressed images. use 0 compressed size field - not totally correct, but passable. palmtopnm: fix incorrect handling of some combinations of PALM_IS_COMPRESSED flag and compression type field. Thanks Paul Bolle . palmtopnm: fix totally broken RLE and scanline compression. Thanks Paul Bolle . palmtopnm: handle PackBits compression. Thanks Paul Bolle . palmtopnm: handle Version 3 encoding. Thanks Paul Bolle . Make makeman put in missing newline. Thanks Eric S. Raymond . Include libz in build of programs that use TIFF library. palmtopnm: fix wrong check for disallowed combination of -transparent and -rendition. pnmtops: Add -psfilter, -flate, -ascii85, -level options. pnmtops: Add -dict, -vmreclaim options. pnmtops: improve %%Title pamcomp: fix bug that duplicates last row of overlay image. Thanks achurch@achurch.org (Andrew Church). pamcut: fix bug that fills the rightmost column with black. Thanks achurch@achurch.org (Andrew Church). pnmtops: Add -noshowpage option. pnmtops: Add %%%%Page. (whoops, this actually added a second %%%%Page. Removed in 10.31). pnmtopalm: fix bug with -offset option. xwdtopnm: recognize TrueColor visual class in header dump. ppmforge: fix bug: crash due to wild pointer with -night. Thanks John Walker . libnetpbm: add pm_make_tmpfile(). 05.01.01 BJH Release 10.26 pnmhistmap: Add -dots, -nmax, -red, -green, -blue, -lval, -rval, -height, and -width options. Thanks "John H. DuBois III" . pnmnorm: accept -wpercent and -wvalue (or -bpercent and -bvalue) together. Thanks Gregg Townsend . xwdtopnm: Add -headerdump option. pnmtoxwd: Include 256 color color maps in direct color instead of zero. Zero has a weird effect. pamtotga: recognize RGB_ALPHA instead of RGBA. palmtopnm: fix bug in determination of compression type. palmtopnm: fix bug in handling of multi-image streams. Thanks Paul Bolle . pnmtopalm: fix bugs in computation of nextDepthOffset. Thanks Paul Bolle . pamtotga: fix TGA pixel size for TGA with alpha. Don't do colormap if input has transparency. pamtotga: fix crash due to uninitialized "id" field in TGA header. pbm_writepbmrow(): Fix bug in MMX/SSE code that causes segmentation fault. pnmhistmap: Fix bug: doesn't work with maxval != 255. Thanks "John H. DuBois III" . pbmtext: fix crash with code point > 127. ppmtompeg: major cleanup. pnmtopng: fix "internal error" and crash bug with -transparent. pnmtoxwd: fix endianness on littleendian machines. pnmtoxwd: fix bug: doesn't work if you specify an option. jpegtopnm: fix bug: -dumpexif all wrong. jpegtopnm: fix bug: newline written to stdout instead of stderr with -dumpexif. Add missing 'col' declaration so -msse compile works. pcdovtoppm: fix syntax error pnmtojbig/jbigtopnm: add newline to "usage" message. pamperspective: fix bugs with -frame_include, etc. build: Jpeg2k programs build properly with newer Jasper library. build: Fix bug: "no rule to build xxx.so" with external Jasper or Jbig library. Add NAME and DESCRIPTION section to pointer man pages. 04.10.16 BJH Release 10.25 Add pcxstd.ppm. Add pbmtoibm23xx. Thanks Jo Fahlke . Add pamedge, replacement for pnmedge. pamperspective: add -frame_include, -*margin, -include. pamcomp: Handle RGB_ALPHA and GRAYSCALE_ALPHA images, using the opacity plane. pamscale: do transparency pixel mixing properly. ppmtompeg: cleanups and additional verbosity. anytopnm: Use 'nawk' instead of 'awk' where available. anytopnm: Don't use -e. (not available everywhere). tifftopnm: Add warning when Whole Image method reduces resolution to 8 bit. pamtopnm: accept extra planes (ignore them). pamcut: major speedup. Thanks Prophet of the Way (Akira Urushibata ("Douso")). libnetpbm: Add pnm_getopacity(). libnetpbm: Add pnm_applyopacityrown(), pnm_unapplyopacityrown(). libnetpbm: "pam" read and write routines much more efficient. Thanks Prophet of the Way (Akira Urushibata ("Douso")). Add pnm_formatpamrow(), pnm_allocrowimage(), pnm_freerowimage(). Thanks Thanks Prophet of the Way (Akira Urushibata ("Douso")). pnm_readpamrow(): Add option to have "tuplerow" argument NULL and not get the row's contents. Thanks Thanks Prophet of the Way (Akira Urushibata ("Douso")). Add pnm_writepamrowmult(). Thanks Thanks Prophet of the Way (Akira Urushibata ("Douso")). libnetpbm: format plain format images more nicely. Thanks Thanks Prophet of the Way (Akira Urushibata ("Douso")). pamcut: fix bug: Blows up instead of failing cleanly when you try to cut beyond the bounds of the image. Thanks Thanks Prophet of the Way (Akira Urushibata ("Douso")). fix bug: MMX/SSE fast PBM writing (with gcc -msse) all wrong. pamscale; fix bug: -linear option sense reversed. pamscale: fix bug: everything too dark when using resampling to enlarge. Speed up pbm_writepbmrow() (and all PBM output programs) by going a byte at a time. Thanks Prophet of the Way . pamperspective: fix bug that can cause memory corruption and crashes. Thanks Mark Weyer . ppmtompeg: put in proper marshalling/unmarshalling so as not to depend on a 32 bit integer type. ppmtopcx: add -palette option. pnmconvol: fix bug: green and blue convolution matrices exchanged, gray exchanged with random. ppmtowinicon: fix bug: crashes on 32 bit images. Use or where possible, dynamically built "inttypes.h" where not. Eliminate uint32n. Handle BITS_PER_WORD=64 in wordaccess.h so it builds on 64 bit platforms. 04.08.11 BJH Release 10.24 g3topbm: Add -stop_error. Add makeman to build tools. Thanks Eric S. Raymond . pamflip: Large speed, memory improvements for non-diagonal flips. Thanks Prophet of the Way . jbigtopnm, pnmtojbig: Use packed PBM functions to speed up greatly. Thanks Prophet of the Way . g3topbm: Don't fail on premature EOF; produce partial output instead. add pnm_maketuplergb(), pnm_makerowrgb(), pnm_makearrayrgb(). Fix a bunch of programs that, starting with 10.23, have too large a "len" field in the pam structure, causing storage overwrites. pbmtext: fix free of unallocated storage bug with stdin input text. pnm_writepaminit(): deal correctly with pam structure that doesn't contain tuple_type member. Fix pamP->allocation_depth == 0. pamstretch-gen: use pamscale instead of pnmscale. Generate pm_config.h at make time. Determine endianness and word size stuff dynamically. Use uint32n instead of uint32_t in infotopam.c. fitstopnm: fix MIN/MAX transposition in maxval bounding. Fix bug: "wordn" in place of "wordint" in wordaccess.h. Fix bug: -plain option on "pam" programs generated plain header, but raw raster. 04.07.17 BJH Release 10.23 Add pamgauss. Add pammasksharpen. Replace pnmtoplainpnm with pnmtopnm. pnmconvol: Add -nooffset. pamdice: add -hoverlap, -voverlap. Thanks Geoff Kuenning . pbmtoepson: Add -dpi, -adjacent, -noadjacent, and -protocol options. pstopnm: recognize Encapsulated Postscript by presence of "EPSF-" in the header instead of a whole specific header line. Thanks Philip Hallstrom . pnmpad: Allow -left/-right along with -width, -bottom/-top along with -height. Thanks David Jones . pnmpad: round to nearest instead of lowest in interpretation of -halign, -valign. pnmsplit: Add -padname option. pnmenlarge: work on multi-image streams. libnetpbm: add allocation_depth to PAM structure. Add maketuplergb(), makerowrgb(), makearrayrgb(). pnminvert: much faster for PBMs. Thanks Prophet of the Way . pbmmake: use packed bits to speed up. Thanks Prophet of the Way (Akira Urushibata ("Douso")). pbmtog3: speedups. Thanks Prophet of the Way (Akira Urushibata ("Douso")). Fix gamma value (from .45 to 2.2) in pm_gamma709(), pm_ungamma709(). pgmramp: Fix signedness bug with -rectangle, -ellipse. pnmtojpeg: fix bug with large exif files. (negative malloc) pnmtopng: Fix estimation of whether it's better to use a colormap or not. Add pamditherbw, replacement for pgmtopbm. pamperspective: Fix bug. Incorrect output. Thanks . ppmtompeg: fix crash with input that is not a multiple of 16 pixels high and wide. pamtotga: fix crash due to freeing nonexistent color table. pamtotga: fix bug: invalid TGA when you use -rgb with a non-color input. pgmtopbm: fix calculations that divide by maxval+1; should divide by maxval. Thanks David Jones . 04.05.01 BJH Release 10.22 Add pamperspective. Thanks Mark Weyer . Add pamstereogram. Thanks Scott Pakin . Add pc1toppm. Thanks Roine Gustafsson . Add pbmtodjvurle. Thanks Scott Pakin . Add infotopam. Thanks Rich Griswold . Add pamtopfm, pfmtopam. ppmtowinicon: Add -truetransparent. ppmtowinicon: remove old, wrong, undocumented -andppm synonym of -andpgm. ppmtopcx: Add -stdpalette option. pamsumm: Add -brief, -normalize. All programs that interpret color specifiers (ppmchange, etc.): warn if the specified color can't be represented, to the precision specified, in the operative maxval. ppmchange: fail if the specified color can't be represented in the maxval of the input image. pcxtoppm: Imply standard palette when palette in PCX header is all black. This function apparently got lost some time since it was added in 1994. pcxtoppm: Add the color palette to -verbose output. ppmtopcx: Fix bug: wrong 256 color PCX output. From 10.21. Thanks Timothy Borgeaud . Convert ppmtodjvurle into pamtodjvurle. pngtopnm: Dump additional info about PNG with -verbose. pngtopnm: Use Netpbm command line parser. pbm_readpbmrowpacked(): Fix bug: ORs bits into return value instead of setting them. Affects pbmtoescp2. ppmtopcx: Fix bug: don't add black to a palette if it's not already there. pnm_tuplehashtotable(): fix allocsize (maxsize) = 0 case. pamflip: fix bug with random memory accesses due to signed/unsigned comparison. 04.03.28 BJH Release 10.21 Add pamsumm. Add pamsummcol. Add pamsharpness, pamsharpmap. Add ppmtodjvurle. Thanks Scott Pakin . pstopnm: add -dpi option. ppmtopcx: add -planes option. pbmtext: add -width option. replace pnmcomp with pamcomp, do transparency right wrt gamma. tifftopnm: Use better CMYK->RGB algorithm: y=1-(b+k) instead of y=(1-k)*(1-b) in Row By Row mode. pbmtext/libnetpbm: Allow pbmfont fonts to have 96 characters (32-128) instead of 95. pnmtopng: fix bug with -transparent. pcxtoppm: remove debugging footsteps. pnmflip: pass through -plain, etc. to pamflip. Include pm_system.h, pm_gamma.h in package. ppmtomap: Change #/bin/sh to #!/bin/sh. ppmtogif: Don't violate array bounds when GIF color map is is larger than internal color map. yuvsplittoppm: Fix -ccir601 option; clean up. ppmtobmp: Fix bug: Generates invalid bits per pixel (e.g. 3) when input image has small number of colors. Thanks David Jones . pbmtog3: fix buffer overrun when image > 1728 columns. Thanks Prophet of the Way . pnmsvgalib: Correct error message - too wide -> too tall. pnmtotiffcmyk: Correct abs to fabs so -theta works. sldtoppm: Correct abs to fabs. pgmramp: Correct abs to fabs so -rectangle and -ellipse work. pgmramp: Don't crash with -rectangle and height or width = 1. ppmforge: Correct abs to fabs so the planet is more than 2 pixels wide. Also recode much of program in high level C. pnmtojpeg: interpret "length" field in exif data as unsigned 16 bit instead of signed. pnmrotate: fix bug: always crashes in memory free pnmrotate: remove debugging messages. Declare _XOPEN_SOURCE=500 in programs that use strdup() so C libraries that don't have _BSD_SOURCE will declare it. Use FTELLO macro so things compile on a system that doesn't have ftello. Don't assume _LARGE_FILE_API means ftello is available -- _LARGE_FILE_API is input to libc, not output from it. 04.01.30 BJH Release 10.20 pamscale: derive from pnmscale. pamscale: Add resampling/convolution code. Thanks Michael Reinelt . pamscale: Do math in linear intensities instead of gamma-adjusted. pamscale: Add -xyfill. pnmtopng: Remove global variables. Solves a problem with conflicting name with libc: "gamma". pbmtomda: make program identifier 'MicroDesignPCW'. Thanks John Elliott . fitstopnm: Produce maxval 1 instead of 0 when input is all one value. Use maxval 255 when samples are floating point. pamstretch-gen: be robust to invalid PNM input. anytopnm: do safe temporary files. ppmquantall: do safe temporary files. pnmindex: do safe temporary files. pnmmargin: do safe temporary files. pamstretch-gen: do safe temporary files. Thanks Stefan Nordhausen . tifftopnm: Do better validation of number of channels. libnetpbm: Add pm_tmpfile(). libnetpbm: Add "normalized" (floating point) read/write routines: pnm_readpamrown(), pnm_writepamrown(), pnm_allocpamrown(), pnm_freepamrown(). libnetpbm: Add pm_gamma709, pm_ungamma709(). libnetpbm: rename createBlackTuple() to pnm_createBlackTuple(). pm_openr_seekable(): use TMPFILE environment variable. Thanks Kai Strieder . libnetpbm: Fail when input maxval is 0. This wreaks havoc with such things as rescaling to a new maxval. pnmquant: Fix bug wherein all options are rejected. ppmshadow: Fix crash in all invocations. pnmtopng: fix bug with array bounds violation with -alpha. Usual symptom: "internal inconsistency" message. anytopnm: Remove "function" so it works on FreeBSD. configure: Do a better job of choosing between -R and -rpath. configure: Add test compile for PNG, Z libraries. 03.11.15 BJH Release 10.19 ppmhist: Add -float option. ppmhist: speed up -colorname by reading color dictionary only once. bmptopnm: fix bug with BMPs that end prematurely. giftopnm: Properly generate alpha mask when the same color is both transparent and opaque in the GIF. Thanks Karl Zilles . rgb.txt: move D65 to the end, so "white" is preferred. xwdtopnm: change interpretation of bitmap_pad and bitmap_unit to accommodate Xfree86 direct color 24 bit xwd. pbmtextps: fix bug where intermediate file gets truncated. pstopnm: fix bug in which Standard Input never works. ppmtobmp: change 'char' to 'unsigned char' to work around a Solaris C library bug. ppmtobmp: handle maxval properly for maxval != 256 truecolor BMP. Thanks Michael Buchholz . ppmquant: replace with wrapper for pnmquant, because ppmquant had some really hard bug in computing the new color set. Fix stack corruption in pXm_check() on systems that have both 32 and 64 bit file offsets. Add 'makecat' build tool and description to USERDOC of using lynx -dump to get man-able documentation. Make AIX shared library build work, with -qmkshrobj. configure: Add intelligence to default location for zlib, libpng headers. configure: Make shared libraries work on AIX. Add netpbm-config. installnetpbm: don't crash on mkdir on old Perl. 03.09.19 BJH Release 10.18 Add pbmtomrf, mrftopbm. Add escp2topbm, pbmtoescp2. Thanks Ulrich Walcher Add pbmtomatrixorbital. Add ddbugtopbm by Russell Marks. bmptopnm: Handle transparency, arbitrary pixel format (rgba masks in BMP header). bmptopnm: Handle top-down BMP. bmptopnm: Handle 32 bit BMP. Thanks Aaron Orenstein . ppmtopcx: add -8bit option. Thanks Holger Schemel . ilbmtoppm: recognize (and ignore) DPPS and TINY chunks. pbmpage: Add solid perimeter box to Pattern 1. anytopnm: use 'file --mime' where available to determine file type more robustly. manweb: make 'webdir' a list of directories instead of just one. pnmpsnr: fix bug: Cr and Cb interchanged. Thanks "Uwe Utecht" . pnmstitch: fix crash. Thanks "Paul" . manweb: add ability to read info files. configure: missing lineends for Solaris. anytopnm, ppmquantall: remove non-POSIX tail -N and egrep. pgmtexture: Fix Measure of Correlation-2 so small values don't turn into zero (replace abs() with fabs()). Add and use MALLOCVAR and MALLOCARRAY utility macros. Allocate rows individually if can't get all rows in a single memory block. Remove hpcdtoppm from the main package because it isn't licensed properly to be distributed on Sourceforge. Remove "magic" file -- the version that comes with Christos Zoulas' 'file' package now contains everything that file had. configure: put space after -o in test compiles. Explicitly set SCRIPTS make variable to null so environment variable of the same name doesn't screw things up. Remove -DALL_SOURCE from AIX compile; define it in files that require it. Add -ffast-math option to recommended Gcc options. stamp_date: don't use LOGNAME as a variable (on AIX, you can't). 03.07.05 BJH Release 10.17 Various code cleanups based on TRU64 compiler warnings. Configure: print message when $OSNAME not recognized. Fix Makefile.common so LIBS and LOADLIBES work. Fix make file for Ppmglobe so it links the math library. Fix bug in xxx_check(): function/prototype mismatch causes bogus wrong-filesize error on systems where off_t is sometimes not 64 bits. 03.07.03 BJH Release 10.16 Add ppmglobe. Thanks Max Gensthaler . bmptopnm: Add ability to convert 16 bits per pixel Windows BMP. giftopnm: add -image=all. pnmtiff: Add -resolutionunit option. pnmtopng: fix bugs, remove limitations with -text. pgmabel: Declare internal subroutines 'static'. pstopnm: fix bug: encapsulated postscript confused with regular postscript, so all pages get overlaid as one page. pnmrotate: Use smaller real memory working set. winicontoppm: use standard option parser. pnmflip: fix bug causing -r180 and -r270 to be ignored. Thanks YANO Hirokuni . configure: fix bug with missing space when using CFLAGS environment variable. Add CPPFLAGS and LOADLIBES to rules and 'configure' test compiles. Issue error message if you do 'make package' before 'make'. Add VERSION file to package. Use explicit library file name on a link instead of -L/-l where possible. 03.04.27 BJH Release 10.15 Add pamtohtmltbl. Thanks Alexander B. Ivanov . Add pampop9. Thanks Robert Tinsley, . winicontoppm: Handle 24 bit direct color winicons. Thanks Lee Benfield . pnmrotate: add -background option. pnmtojpeg: Add -density option. pamtotga: Add alpha capability, via "RGBA" tuple type. libnetpbm: Add ppm_readcolornamefile(). Make library and programs work with files > 2GB. At least on GNU and AIX. pbmtext: Add -momargin option. Thanks "Slaven Rezic" . pbmtoepsi: fix bug: non six-digit lines between %%BeginPreview: ... and %%EndImage . Thanks "K.Nakano" . pnmrotate: fix bug: background color computed wrong. pnmrotate: fix bug: PBMs without -noantialias not properly promoted to PGM. pnmtops: fix bug: 1 bit per sample output when maxval is 2 or 3. Should be 4 bits per sample. pnmtops: fix bug: everything too dark when input maxval less than Postscript maxval. pm_nextimage(): fix bug: doesn't read past white space at the end of a plain-format raster. pnmremap: fix performance bug: don't put same color into hash twice. Thanks Hannu Koivisto . ppmtoxpm: Add -hexonly. Default to system color dictionary. Use standard library routines to access color dictionary. pnmcolormap: Keep only one row at a time in memory. bmptopnm: fix buffer overrun with wild ColorsUsed value. Change ordering of include files in parallel.c so it compiles on AIX 5. configure: change -O2 to -O0 for broken gcc compiler. One more chop at the -R/-rpath issue: new defaults for Solaris. 03.02.13 BJH Release 10.14 Add pamedge, adapted from pnmedge by Peter Kirchgessner, 1995. Add ppmwheel, adapted from ppmcirc by Peter Kirchgessner, 1995. Add ppmtoarbtxt, renamed from ppmtotxt by Peter Kirchgessner, 1995. pnmflip: add pnmflip directory to search path for pamflip. picttoppm: Recognize DirectBitsRgn opcode. Thanks . ppmbrighten: -normalize works with nonseekable input. jpegtopnm: Update exif stuff to match Jhead 1.9. bmptopnm: fix for the case of ColorsUsed != 0 in non-colormapped (24 bit) image. pbmtopgm: limit the maxval to legal values. pamarith: minimum output maxval of 2 for -compare. rgb.txt: remove redundant lines, clean up. pnmgamma: fix bug in -ungamma -cieramp. 1/gamma vs gamma. pnmtopng: fix bug: background color = transparent color in colormapped image causes transparent color not to be transparent. Also make bilevel images with background color use 1 bit per pixel instead of 2. pngtopnm: Fix bug: wrong exit code. Fix broken ISUPPER; affects sbigtopgm and color name functions pamflip: Use bundled strsepN() instead of strsep(). giftopnm: fix bug with GIFs that (invalidly) contain translation cycles. Clean up LZW decompression so it is readable and more robust. Add strsepN() to libnetpbm, because strsep() is not available everywhere. Change --rpath to -rpath. Change LINKERISGCC to LINKERISCOMPILER and default to Y. configure: Handle explicit "none" response to svgalib prompt. Add merge.o (vs mergelist) method of doing merge build. Fix bug: jbig converters missing from merge build. 03.01.09 BJH Release 10.13 Add pamlookup. Add pamflip: replacement of pnmflip. Does PAM images and has -xform option. Add pm_system(). Ppmrainbow: Add -norepeat option. tifftopnm: handle 32 bits per sample -- Use upper 16 bits. tifftopnm: add -byrow. pamarith: Add -compare operator. ppmtompeg: Check for missing YUV_SIZE option with -combine_frames and -combine_gops. ppmtompeg: fix buffer overrun with invalid message received by slave processor. pamseq: Fix crash/memory corruption. pamseq: Remove debugging statement. Add some protection against Netpbm images that indicate dimensions too big to process without arithmetic overflow. Replace isdigit() with ISDIGIT(), etc. pnmnorm: malloc histogram array instead of using stack variable to avoid stack overflow. thinkjettopbm: works with Solaris Lex. Add PKGDIR_DEFAULT Makefile.config variable. Configure: Test for old JPEG library without jpeg_marker_struct. Configure: Add -I/usr/local/include and -I/usr/local/lib to OpenBSD and NetBSD compiles and links. Change make variable from INCLUDE to INCLUDES to avoid collision with Cygwin environment variable. 02.11.09 BJH Release 10.12 Add pamtojpeg2k, jpeg2ktopam. pnmtotiff: add test for seekable output. pnmflip: use packed PBM format to save memory. Thanks Ivan Karski . thinkjettopbm: replace getopt with shhopt to avoid header file problem (getopt.h) on some systems. fix multiple definition problem with pm_plain_output. pgmminkowski, pgmmorphconv: fix bug: programs missing. These were formally added in 10.0, but the code never actually went in. pbmtomda: fix crash with input image not multiple of 4 rows. tgatoppm: fix segfault in option processing from recent option processor change. Slight correction to coefficients in pnm_YCbCrtuple. Add pnm_YCbCr_to_rgbtuple() to libnetpbm. ppmtompeg: fix build failure with NETWORKLD=NONE. ppmtompeg: fix build error due to broken C library header files on SunOS. Fix missing alias symlinks in merge build. 02.10.17 BJH Release 10.11 Fix nstring.h, ppmcmap.h dependency in analyzer/. pnmtojpeg: Add -rgb option. jpegtopnm: Handle a stream of consecutive JPEGs. tifftopnm: use TIFFRGBAImageGet() so it works on compressed images. 'make distclean' deletes all symbolic links, even if I forget to put them in HEADERLINKS so that 'make clean' gets them. 02.10.10 BJH Release 10.10 Add -plain common option. Thanks "John H. DuBois III" . ppmhist: Add -colorname option pnmcomp: Add -align=beyondleft, -align=beyondright, -valign=above, -valign=below and make -xoff and -yoff work in conjunction with -align and -valign. Idea from "John H. DuBois III" . manweb: Add ability to search through PATH for documentation so you can just say 'manweb bmptopnm'. bmptopnm: fix bug from 10.9: 24 bit truecolor should be BGR, not RGB. ppmtobmp: fix bug from 10.9: 24 bit truecolor should be BGR, not RGB. pamarith: Fix bug with mixed depth inputs. pamarith: Fix bug with -subtract of larger # from smaller. pnmstitch: fix bug causing segfault at end. pgmtopgm, ppmtoppm: Issue error message if arguments given. giftopnm: remove debugging messages. Fix line number in color dictionary error messages. Fix build errors on systems that don't do "echo -n" by using cat /dev/null instead. Fix build errors that cause the data files not to get installed. Merge build: fix install of other/ programs and add NETWORKLD to the link. Merge build: add missing backward compatibility 'pnmfile' link. Add SCO OpenServer to configure dialog. Thanks John H. DuBois III Add FreeBSD to the configure dialog. 02.09.21 BJH Release 10.9 Add pgmtopgm. Add ppmtoppm. Add ppmtoterm. Thanks Ero Carrera . Add ppmrough. Thanks Eckard Specht . Add rgb.txt pnmquant: make it work with pipe input; fix crash due to half-finished work accidentally released a few releases ago. bmptopnm: Use way less memory by inverting image while still in the BMP raster format (as little as 1 bit per pixel), instead of in the libnetpbm format (96 bits per pixel). ppmhist: Reads image into memory one row at a time instead of entire image in memory at once. ppmshadow: make it work with non-PPM input. ppmshadow: Don't run a shell for every Netpbm program. xwdtopnm, pnmtoxwd: make them work on 64-bit-long machines. ppmdither: Fix subscript out of bounds bug which caused random intensity values for the most intense pixels (and very slightly too-intense values everywhere). ppmtobmp: Fix bug in 24 bit truecolor: was writing B, G, R but standard appears to be R, G, B (per bmptopnm and xli). pamarith: restore pnmarith capability of having one of the inputs be depth 1 while the other is not. pnmtopclxl: fine tuning of PBM input case. remove pnmfile; obsoleted by pamfile. ppmfade, ppmshadow: Change #!/bin/perl to #!/usr/bin/perl. Add a bunch of missing #include and fix scanf type mismatches, per gcc 3.2 compiler warnings. Add some missing -R options to links. 02.08.16 BJH Release 10.8 Build: Fix Configure bug with JPEG headers in default path. Pnmtopclxl: fix bug with PBM input not multiple of 8 across. 02.08.15 BJH Release 10.7 Pnmtopclxl: remove padBytesMultiple attribute, which doesn't exist on old printers. Pnmtopclxl: fix MediaDestination parameter value. Pnmtopclxl: make options parsed by standard Netpbm option parser; remove unconventional (and broken) --output option. Fix crash with Standard Input input. Check whole length given for option values. pnmflip: Add -memsize and -pagesize. Make other options conventional - only one transformation allowed. Make 'configure' a Bourne Shell program that calls configure.pl. Add pnmstitch. Thanks Mark Salyzyn . Fix -R stuff to fix Solaris build. Add lots of consts, remove dead code, with the help of GNU compiler warnings. 02.07.29 BJH Release 10.6 Add pnmtopclxl. Thanks Jochen Karrer . Add pnmstitch code; not working yet. pnmmontage: add -data option. Thanks Ben . pnmtotga: put "image ID" in TGA output. ppmtotga: Correct name to pnmtotga. pnmcomp: Add -opacity option. pbmtog3: Add -nofixedwidth option. Vladimir Nadvornik ppmtopgm: upgrade to handle multi-image PPM file. icontopbm: fix bugs with invalid input formats. Thanks Vladimir Nadvornik ppmtompeg: fix crash with logarithmic psearch algorithm and non-power-of-2 search range. configure: fix use of compiler other than cc. Thanks Ben . Fix Cygwin library build for default DLLVER. Fix file handle problem with Configure on old Perl. Don't use .defs file/run dlltool (Windows). 02.07.14 BJH Release 10.5 Make library link on Solaris with GNU Ld work. Thanks Russel Winder . Make Configure not depend on File::Temp. 02.07.09 BJH Release 10.4 Fix space after -I bug in Makefile.common, lib/Makefile. Change a bunch of macros to upper case; avoid name collision of round() with math library. Configure detects broken gcc, sets -O2. Configure detects jpeg headers not in default search path. 02.07.02 BJH Release 10.3 Add pamfunc. Add pgmabel. Thanks Volker Schmidt . pamslice: add -xmgr option. pgmslice: upgrade to pamslice. pamarith: add -mean option. pnmarith: upgrade to pamarith. pnmtopng: add -palette option. Functional copy from "Pnmtopng" package. pbmtoepsi: Add -dpi option. Thanks Bill Cheng tifftopnm: make -respectfillorder work. pnmtopng: fix bugs in handling of text comment files. Taken from "Pnmtopng" package. pnmtopng: fix -hist option. Cygwin build accommodations. Thanks Charles Wilson . Fix ppmtompeg build failure when JPEGLIB = NONE Fix missing -R link options. 02.06.14 BJH Release 10.2 Works with separate build directory. pnmnorm: fix precision error causes whites to become black Fix bug: Pbmpage needs math library 02.06.07 BJH Release 10.1 ppmmake: add -maxval option pgmramp: add -maxval option Add pamseq. ppmcolors: base on pamseq. pnmtotiff: Fix bug in -indexbits. Fix runtime library paths for Darwin, Netbsd installnetpbm: Fix infinite loop when you refuse manweb.conf. installnetpbm: Add mode to mkdir for older Perl. installnetpbm: Use BSD option on Cp if not GNU. 02.06.01 BJH Release 10.0 Man pages replaced by web pages -- either private copies or straight from Netpbm Central. Documentation distributed via Worldwide Web, not in the Netpbm source code tarball. Reorganize source tree. Combine 4 Netpbm libraries into 1. Make merge build merge ALL programs into one. Add pamendian. Add pamstack. Add pamtohdiff, hdifftopam. Add pbmtextps. Thanks James McCann . Add pgmminkowski, pgmmorphconv. Thanks Luuk van Dijk pnmtotiff: Add -indexbits option to allow fewer than 8 bits per sample in apletted image. Thanks Thomas Henlich. . pnmtotiff: put zeroes in unused colormap entries. Thanks Thomas Henlich . pbmtext: Add -lspace option. Thanks Denis . ppmqvga: remove; appears to be obsoleted by Pnmquant. pnmtops: remove setpagedevice by default. Add -setpage option. ppmtouil: rename to pamtouil, add transparency. pamchannel: Add -tupletype option. pstopnm: Add a showpage in case the EPS source doesn't have it. Thanks "J. Milgram" . pstopnm: Run Ghostscript directly instead of via a shell. Some Windows environments don't have a Bourne-compatible shell. pnmtotiff: Don't put bogus colormap in grayscale TIFF. Thanks Thomas Henlich . picttoppm, ppmtompeg, rletoppm: fix bug with invalid parameter ("b" modes) on popen(). pnmtopng: fix bug with 'mayscale'. pnmnorm: Fix bug with bvalue == 0. Thanks "Blake, Chris" pnmtopng: restructure palette generation to make it easier to read. configure: Add --help. Add Windows DLL versioning. Thanks Charles Wilson . Add Nstring library, so Netpbm programs can use asprintf(), etc. 02.03.15 BJH Release 9.25 Add pamdice. pnmgamma: add -srgbramp. ppmnorm, pgmnorm: replace with pnmnorm ppmnorm: Read only a row at a time into memory. ppmnorm: Add -keephues option. pnmtotiff: Add -miniswhite option. pnmtopsnr: Read only a row at a time into memory. Convert bmptoppm to bmptopnm. ppmtopcx: Add -xpos, -ypos options. pcxtoppm: Don't require seekable input. pcxtoppm: Improve messages; add some PCX header validation. ppmtowinicon: Use standard alpha mask (PGM image) instead of PPM image (black/not black) for and file. pnmpad: Add -width, -height, -halign, -valign options. Thanks M. van Beilen . ppmhist: remove 100,000 color limit. pnmnlfilt: works with multi-image streams. Thanks "Steven M. Schultz" . pcxtoppm: Fix bug with negative image position. pnmtopsnr: fix unsigned integer bug giving wrong results on PGMs. libpnm: fix bug: PAM functions write 16 bit samples wrong. Tifftopnm: fix bug: transparency mask all wrong. Thanks Heikki Suonsivu . Makefile improvements for cross compiling. Thanks "Maciej W. Rozycki" . Allow build with existing JBIG library instead of the bundled one. Thanks "Maciej W. Rozycki" . ppmtompeg Makefile: include -R option where needed. Thanks Hans Werner Strube . Add BSD/OS to configure program. Thanks seebs@plethora.net (Peter Seebach) install uses supplied install.sh instead of an 'install program it finds on your system. configure: Fix missing newline bug for IRIX. 02.01.27 BJH Release 9.24 Add ppmsvgalib. pnmremap: Use full color resolution of input instead of that of color map to do Floyd-Steinberg dithering. pnmremap: fix bug where Floyd-Steinberg dithering of very dark areas creates spots of maximal brightness. ppmquant: fix same bug as pnmremap. pnmscale: add -nomix option rawtopgm: fix bug. Thanks Joe Krahn . pnmquant: replace with a Perl program that calls pnmcolormap and pnmremap. Fix bug: PAM library routines fail to read 2-byte samples. fix bug: install-dev doesn't install mappam.h. configure sets up install paths. make install does Ldconfig where required. pnmtops: Add "XxY" form of -dpi option. Thanks Vladimir Nadvornik ppmtomap: rewrite to call the new pnmcolormap. pnmscale: clean up code a little; now it appears to produce somewhat better results -- don't know why. exif.h: remove C++ comments various fixes to please Irix compiler. Change some "long" to "uint32n" so it works on 64 bit machines. 02.01.04 BJH Release 9.23 tifftopnm: fix bug from 9.22: always crashes. xpmtoppm: fix bug from 9.21: XPM 3 files not recognized because /* XPM */ comment read twice. xwdtopnm: Use bitmap_pad instead of bitmap_unit on X11 pixmap files with depth > 1. Thanks "Ben Kelley" . pnmtopalm: fix bug: crashes with -colormap and non-color input. ppmforge: fix bug: array subscripts out of bounds. Thanks Rob Menke . giftopnm: fix bug with extraneous "bogus character 0x00" message tifftopnm: add -respectfillorder option. Ignore FILLORDER by default. make install-dev: install shared library stub files (symlinks). ppmtogif: Fix bug: color resolution in wrong bits in logical screen descriptor. Thanks Elmue . Add -lz to Tiff programs; libtiff requires it now. Add ppmcolors. Add pnmremap. Add pnmcolormap. pnmtops: Include setpagedevice directive in Postscript output. Thanks A Braunsdorf . pnmtops: turn-to-fit logic works properly where paper is wider than tall. Thanks A Braunsdorf . giftopnm: cleanup 01.12.10 BJH Release 9.22 Fix bug: pnm_luminosity_factor must be "extern" in pam.h pbmtonokia: can create picture messages. -txt, -net options. tifftopnm: Accept invalid FILLORDER value - assume msb-to-lsb. 01.12.01 BJH Release 9.21 Add pamdeinterlace. Add pnmquant. pnmtops: Accept maxval > 255 input. Rename pnminterp to the more informative pnmstretch. pnmstretch: convert from pnm to pam - pamstretch. pamstretch: add -xscale, -yscale options pbmtolj: Add -copies option ppmtogif: Add secret -h option to make Latex2html configure program work. ppmquant: output maxval controlled by colormap maxval, not input maxval. pbmclean: Add -black and -white options. Thanks Michael Sternberg . xpmtoppm: Don't require input to start with a comment. Thanks MINAMI . xpmtoppm: Fix bug introduced with 9.20 with XPM 1 files. pnmcut, pamcut: fix bug with cut that begins past right edge of image. pamcut: Same fix as in pnmcut in 9.9. Also fix black fill. pnmtotiff: Process one row at a time instead of reading entire image into memory. Add -color, -truecolor. 01.10.05 BJH Release 9.20 ppmtogif: Add -nolzw option. ppmntsc: do multiple images pnmtojpeg: fix bug from 9.19 causing crash on most images. ilbmtoppm: Correct cmyk -> rgb transformation. ppmtompeg: Fix bug due to missing "breaks". Thanks Wolfgang Mueller eyuvtoppm: fix memory leak, speed up by allocating working storage only once. libopt.c: replace strtok_r() with strtok() so it compiles on Mac OS X. 01.09.18 BJH Release 9.19 pnmtotiff: fix bug from 9.17 causing compile to fail with old Tiff library pnmtojpeg: Add -exif option. ppmtojpeg: Change to pnmtojpeg; PBM/PGM input causes grayscale output. pnmtotiff: Handle LSB2MSB FILLORDER. pnmtotiff: Fix -msb2lsb, -lsb2msb. Used to do nothing but set FILLORDER tag value. Make maxval of a PBM image read as a PGM image 255 instead of 1. libpbm: eliminate external data symbol pm_show_version so it works with Windows. Use subplatform-specific prefixes on Windows shared libraries. Thanks Charles Wilson . 01.09.04 BJH Release 9.18 Fix bug from 9.17: -lm now needed in jpegtopnm link. jpegtopnm: Add -dumpexif and -exif options. pnmtotiff: Fix bug: 16 bit samples byte-swapped on little- endian machines. Remove C++ comments from exif.c 01.09.03 BJH Release 9.17 Add ppmtoneo, neotoppm. Thanks Teemu Hukkanen . pnmtotiff: Add -flate, -adobeflate. Thanks Peter Creath. pnmindex: Fix bug with -size option. Thanks André Majorel pnmflip: Fix memory leak, plus make special case for PBM images that uses 12 times less memory. Thanks Stefan Roellin . tifftopnm: Fix incorrect interpretation of FILLORDER tag causing incorrect output with 16 bit samples and FILLORDER not msb2lsb. Thanks Don Reid . tifftopnm: Fix PHOTOMETRIC_MIN_IS_WHITE case. Thanks Don Reid . giftopnm: Fix bug with malformed GIFs that end prematurely. pbmtonokia: Convert C++ style comments to C style so Sun C compiler can handle them. Thanks Jon Parise . Install development package (header files, static libraries) by default. Add ability to build both static and shared libraries. Add merge/nonmerge to Makefile.config/configure, in place of special make targets. Fix Mac OSX build for gcc 3.0. Thanks m-sekino@mb.kcom.ne.jp (Masanori Sekino) Fix make install.hdr. Thanks m-sekino@mb.kcom.ne.jp (Masanori Sekino) 01.07.24 BJH Release 9.16 Builds on Darwin/Mac OS. Thanks m-sekino@mb.kcom.ne.jp (Masanori Sekino) Add pbmtopsg3. (Postscript G3 fax) Thanks Kristof Koehler Add pbmtonokia. Thanks Tim.Ruehsen@OpenMediaSystem.de. Add ppmrainbow. Thanks Arjen Bax . Add pamoil, as replacement for pgmoil. Based on ppmoil by Chris . ppmtogif: Add -alpha option. ppmtojpeg: fix bug: option takes as input filename. xpmtoppm: fix bug: Bogus EOF failure. Thanks "Dr. Larry Pyeatt" . pnmtopng/pngtopnm: update to Release 2.37.5 of the pnmtopng package. Make -transparent option work like ppmtogif -- you can add an "=" before the color to specify "exact". fix bug with background color and maxval = 65535. Fix a raft of bugs in pnmtopng from Netpbm 9.15. Remove Tiff library from the package. Proper naming of AIX and HP-UX shared libraries. giftopnm: Add color index to transparency message. Add some -R link options in make files. ppm3d: fix bug: ignores offset argument. tifftopnm: fix bug: shifted right one pixel. pnmshear: remove sscanf so it works better with Cygwin, convert to shhopt, clean up. pgmcrater: Remove black vertical line at right edge. Make craters wrap around the image (enables tiling of image). Thanks Arjen Bax. pgmtopbm: remove sscanf so it works better with Cygwin. Thanks Arjen Bax. Treat all white space, including DOS carriage returns, in color name db file as white space; Thanks Arjen Bax. 01.06.24 BJH Release 9.15 Change format specs to state that sample values are nonlinear instead of linear. pnmtopalm/palmtopnm: Handle PalmOS 4.0 16 bit direct color format. Thanks Bill Janssen . pnmgamma: Add CIE Rec 709 gamma correction option. pnmgamma: Add -ungamma option ppmcie: Add -rec709 color system option as default so that it produces true PPM output (per the new spec) and carefully document what the program generates. ppmcie (work done by Andrew Hamilton in May 1999 but not distributed): 1. Corrected the XYZ -> RGB transformation; 2. Eliminated the -interpwp option, a fudge which had been necessitated by the incorrect XYZ -> RGB transformation; 3. Added a Gamma correction, missing in the original; 4. Added the option to plot u' v' chromaticity instead of x y; 5. Added options to suppress labels and/or axes. pnmscale, pnmscalefixed: Fix bug with unwanted -verbose behavior. pnmscale, pnmscalefixed: Add -reduce option. Thanks Christopher W. Boyd ppmtoeyuv: Speed up by not freeing/reallocating storage for each image. Thanks "Steven M. Schultz" ppmtoeyuv: Fix memory leak. Eliminate pgm_pbmmaxval, ppm_pbmmaxval, and pnm_pbmmaxval from libpgm, libppm, and libpnm. 'maxval' argument to pgm_readpgmrow() and ppm_readppmrow() already accomplishes this. pnm_readpnmrow() changed to do same. external data symbols like this do not work with Mingw. various changes to accommodate Mingw (native Windows) and DLLs with Cygwin (Windows). eyuvtoppm: rewrite. Uses Netpbm libraries now. pstopnm: Remove gratuitous 5% margin. Add -equalpixels, -imageheight, and -imagewidth options. pnmtopng: Read only one row at a time, not the whole image, into memory. Do multiple passes through the (hopefully fully cached) file. This saves memory because pnmtopng's internal format may use 96 times as much space per pixel as the file. libpbm: Add pm_openr_seekable(), pm_seek(), pm_tell(). libppm: Add ppm_computecolorhash2(), ppm_computecolorhist2(). configure: fix bug with Solaris/Sun compiler. Split pbmplus.h into pm_config.h and pbm/pm.h to prepare for automatic generation of pm_config.h. Move pm_*() subroutines from libpbm1.c to new libpm.c for clarity. Remove malloc.h from 411toppm. malloc is included by pm_config.h, in platform-dependent way. 01.05.13 BJH Release 9.14 fix bug in shhopt causing wild branches. winicontoppm: Make alpha mask ("and") output PBM instead of PPM; correct sense so white is opaque, black transparent. ppmtoxpm: Add --alphamask option. Remove 256 color limit. xpmtoppm: Add --alphaout option ppmtogif: Add "=" to -transparent option to specify exact color. configure: change /bin/perl to /usr/bin/perl. Red Hat Linux doesn't have /bin/perl. pnm/Makefile: correct bugs in link. Thanks J Scott Berg Make it build on Unixware. Make it build on OpenBSD. document color indexing and histogram functions. 01.04.22 BJH Release 9.13 ppmtogif: fix divide by zero move much of README file into a new master man page, netpbm.1. pnmcomp: add -align and -valign options. Add thinkjettopbm. Thanks Eric Norum . Builds with Sun compiler. 01.03.25 BJH Release 9.12 Add 411toppm. Thanks Steve Allen . pnmcrop: add -sides option (does what was default behavior until July 2000) ppmtoeyuv: Use netpbm libraries, deal with multi-image input. Add color row color map and Floyd-Steinberg functions from ilbm package to libppm. ilbmtoppm, ppmtoilbm: replace with Ingo Wilken's version from April 13, 1995. jpegtopnm: fix crash with B&W JPEG input. pnmcomp: fix bug: invalid output image when overlay image is "higher" format than underlying image. ppmchange: fix bug: wouldn't work with maxval != 255. pnmtojbig: fix bug: it always says invalid maxval. Thanks Bill Davidsen. pbmtext: fix bug: output is sometimes "not implemented" because of uninitialized variable. pnmpsnr: rewritten so as not to use Fiasco library. Correctly handles maxval != 255. Works with PAM. Add IRIX to the configure program. Thanks Neil Franklin . make files: add ZLIB_DIR and ZHDR_DIR variables to Makefile.config. Thanks Bill Janssen. pcdindex: convert from csh to sh. Thanks Steve McIntyre. 01.02.20 BJH Release 9.11 pnmtopalm: Handle 16 bit direct color format. pnmtotiff: Add -xresolution and -yresolution options. Thanks Tim Ruehsen. Add error message to GNUMakefile for when GNU Make is too old. pnmpsnr: fix bug: PGM files always compared equal. libpbm: improve --version to reflect that libraries are usually dynamically linked now, so it's the version of libpbm, not the program itself. pbmtext: add -space option. asciitopgm: fix crash due to memcpy that should be memset. Thanks Philipp Knirsch . pktopbm: fix numerous bugs. Copied from Red Hat. By jcn 1998.09.22. pstopnm.csh: fix bomb when xres=yres. This program is obsolete (use pstopnm.c instead), but the patch was free, so why not? Copied from Red Hat. By Nalin Dahyabhai 2000.02.14. pnmtotiff: Make photometric for G3/G4 fax MINISWHITE instead of MINISBLACK. Add -minisblack option. Thanks Eric Smith . libppm/ppm_parsecolor: fix rounding error for very small maxvals (e.g. PBM files -- maxval = 1). pnmtopalm, palmtopnm: minor updates bmptoppm: handle BMPs that use ColorsUsed instead of bits per pixel to determine color map size. bmptoppm: add -verbose option. ppmquant: fix bug with maxval > 255 causing arithmetic overflow and arbitrary colors in output. 01.01.10 BJH Release 9.10 giftopnm: add -alphaout. ppmchange: Add -remainder option. Add pnmtopalm, palmtopnm. Thanks Bill Janssen . Add pnmmontage. Thanks Ben Olmstead . ppmtogif: Add -comment option. ppmtogif: fix bug: created GIF89 when it should have created GIF87. giftopnm: fix bug in displaying of comment extensions. jpegtopnm: Add -comments option. ppmtojpeg: Add -comment option. ppmtompeg: fix crash. Thanks Roger Southwick More work on separating source and build directories. GNU Make 3.77 or better now required. 00.11.20 BJH Release 9.9 add ppmtolj: color HP Laserjet graphics (PCL). Thanks Jonathan.Melvin@heywood.co.uk. ppmfade: add -mix option. ppmhist: add -noheader option. rawtopgm: Add --bpp, --maxval, and --littleendian options. pnmindex: Add -noquant, -title options. pnmtotiff: create 16 bits-per-sample Tiff files when maxval > 255 instead of crash. tifftopnm: handle 16 bits-per-sample Tiff files. pnmscale: Use floating point arithmetic instead of 12 bit fixed point to reduce distortion at right and bottom edge of large images with weird scaling factors. Add pnmscalefixed, which is the old fixed point pnmscale, which goes faster. But also fix bug in the fixed point version so the distortion isn't as bad. Add -verbose option. pnmcut: fix bug: right edge wrong or subscript out of bounds. Thanks MURAKAMI Masahiko , Sven Over , Frederic Vivien , Pete Weisz . xpmtoppm: fix reading of 4-character color codes; handles "NONE" color (transparent background); add -v. Thanks Martin Vermeer . ppm/Makefile: fix bug: ppmtompeg, hpcdtoppm not installed. Thanks Mike Castle . ppmpspread: fix bug: invalid memory reference crash. Thanks digger , john joseph iii casey . ppmchange: fix bug: every other argument pair ignored. Thanks Sven Over . Fix error message in pXmmerge (no pm_error()). Thanks Pete Weisz , libppm: add /usr/X11R6/lib/X11/rgb.txt as default rgb.txt file. Thanks ceder@lysator.liu.se (Per Cederqvist). Replace hpcdtoppm version 0.3 (1992) with 0.6 (1994). Add pcdindex. Build fixes for Tru64. Thanks Phil Benchoff . Makefile.config: option to not strip binaries on install. 00.09.01 BJH Release 9.8 jpegtopnm: Add -adobe, -notadobe options. ppmchange: Add -closeness. giftopnm: report transparency information. jpegtopnm: Accept single-hyphen options. Make it build on DJGPP. 00.08.12 BJH Release 9.7 Add PAM format, Pamchannel, Pamtopnm. sgitopnm: add -channel option to access more than 3 channels. Thanks Smarasderagd. pnmcut: Add -pad option ppmtobmp: change default from -os2 to -windows. pnmcut: fix bug: height argument misinterpreted. ppmquantall: fix bug: don't crop white borders off images yuvtoppm: fix bugs: don't depend on bigendian representation of integers. reject odd-width images. ppmtoyuv: fix bug: reject odd-width images. anytopnm: fix bug: required nonstandard shell feature. giftopnm: fix bug: when image consists of two gray colors, program converted them to black or white and created PBM file. Thanks Smarasderagd. Also fix memory leak. Make changes since 9.2 build on Cygwin. 00.07.12 BJH Release 9.6 Add pnmtofiasco, fiascotopnm, psnpsnr. Thanks to Ullrich Hafner. Make Pnmcrop use a temporary file instead of huge amounts of memory. bmptoppm: fix crash when OS/2 BMP file has > 8 bits per pixel. anytopnm: fix bug (from 9.0) wherein program almost always crashed. Add gzip, bzip, bzip2 capability. Thanks Charles Howes. parallel.c: Work around SunOS libc problem. Define some newer libtiff macros so it compiles against older libtiff. 00.07.01 BJH Release 9.5 Extend formats to allow multiple images per file. Add -allimages option to pnmfile. Add pnmsplit. Extend pnmtops. Add pbmtowbmp, wbmptopbm. Thanks Terje Sannum. ppmtobmp: Add 24 bit (truecolor) capability. Remove Release 8.3 colormap size update. pnmcut: easier, more expressive syntax for specifying what to cut. pnminterp: handle maxval != 255, convert to use Netpbm libraries. Translate pstopnm from Csh to C, eliminate dependency on the 'bc' program. Fix bug in tiff library build which caused null soname. Add optParseopt2() to shhopt.a so you can use long options with one hyphen instead of two. giftopnm: fix bug with variable used before set. Fix bug (coding error -- 0=>i). Unknown impact. bmptoppm: fix bug with 24 bit (truecolor) Windows BMPs. xwdtopnm: fix one more bug with bits_per_item <> bits_per_pixel. Fix ppmtompeg build bug with static libraries. Make build clean with gcc -ansi (albeit with some files that need extensions declaring the _BSD_SOURCE etc.). 00.06.04 BJH Release 9.4 Rebase pnmtopng, pngtopnm on Pnmtopng 2.37.4. pbmtolj: fix bug where blank lines get discarded. Thanks Charles Howes. ppmtompeg: fix bug with "PNM" format files with maxval != 255. Get rid of built-in PPM file parsing. Fix 'make install' where mkinstalldirs is not found. Fix bug where rle_global.c would not compile with GNU C Library 2. 00.06.01 BJH Release 9.3 pnmfile: check for file size error. ppmhist: handle larger maxvals, image sizes xwdtopnm: Use color map with DirectColor to fix wrong color bug on some DirectColor xwds. xwdtopnm: Correct bug with 24/32 DirectColor LSBfirst xwds. Add pXmcheck() library function. Add leaftoppm and ppmtoleaf. Thanks Bill O'Donnell. Add winicontoppm and ppmtowinicon. Thanks Lee Benfield . Add pgmslice. Thanks to Jos Dingjan. Include subset of RLE library in the package. stamp-date doesn't rely on whoami. Make file fixes to accommodate more install programs. Replace tmpnam() with mkstemp(). Add pXm_init() into all programs that didn't have it. 00.05.15 BJH Release 9.2 Shared libraries now build properly for Solaris, SunOS, NetBSD. Add jbigtopnm, pnmtojbig. Thanks to Markus Kuhn. Add pnminterp, pnminterp-gen by Russell Marks. Add pbm_writepbmrow_packed() and pbm_readpbmrow_packed() to libpbm. ppmdither: fix bug with input maxval != 255. Make output maxval the LCM of the requested numbers of primary levels. xwdtopnm: works with files where there aren't an integeral number of pixels per storage unit (e.g. 24 bits per pixel) Add some missing pXm_init() Make pXmmerge source code automatically generated. 00.05.06 BJH Release 9.1 Add pbmtoppa (renamed from pbm2ppa, renamed from print-pbm). Thanks Tim Norman. Add pbmpage. Thanks Tim Norman. Add pbmtomda, mdatopbm. Thanks John Elliott. Replace gemtopbm with gemtopnm. Thanks to John Elliott. Add ppmntsc: change colors to those acceptable for ntsc or pal. Rename old ppmntsc to ppmtv. pbmtolj: Add compression (-delta, -packbits, -compress) by Dave Platt Look for both /usr/lib/X11/rgb.txt and /usr/openwin/lib/rgb.txt by default. Improved error messages if file not found. pbmtext: renders character codes 0x80-0xff and 0x00-0x1f as whatever the font says, rather than always blank. Thanks Helge Oldach. Fix bug in parsing of rgb.txt file in ppm_colorname(). Add optional static library build to make files. Fix a bunch of build bugs for Solaris, SunOS. Still not all the way there. Thanks Richard Curnow, Philippe Brieu, Benjamin Kuit. Make it build on Cygwin. Thanks Pierre Humblet. Use rm -f;ls instead of ls -sf in make files; some systems don't have ln -f. 00.04.15 BJH Release 9.0 Add the 16-bit-per-sample format for maxval > 255. Change size of samples in the library interface from 8 bits to 32 bits. Library write routines now create the new 16 bit format instead of plain format when you request a maxval > 255 and not 'forceplain'. Make all programs read the new format, and all programs that convert to PNM from a >8 bit input generate it instead of failing or normalizing. Make pnmdepth generate it. pnm_readpnminit(), etc. fails if you request a maxval > 65535 and not 'forceplain'. It used to generate plain format in that case, but you couldn't actually specify a sample value > 255. Add pnmtorle and rletopnm from the Army High Performance Computing Research Center. add ppmcolormask - creates a mask of areas of a certain color in an image. anytopnm: fix infinite loop pbmtext: Ignore non-8-bit characters in bdf files instead of crashing. ppmdither: fix crash with large dithering matrix dimensions. Rename ppmtompeg/headers/search.h to motion_search.h to avoid possible confusion with the OSF system file search.h Fix erroneous shhopt.h dependency in build. 00.04.03 BJH Release 8.4 Add ppmtompeg, adapted from Berkeley's mpeg_encode. Add eyuvtoppm and ppmtoeyuv, and vidtoppm, from Berkeley mpeg tools. Add ppmfade, adapted from Wesley C. Barris' pbmfade. Add ability to extract the alpha channel to tifftopnm, tgatoppm, and ximtoppm. (It's already in pngtoppm, and still missing from ilbmtoppm). pnmtotiff: Change default compression to none, due to removal of LZW capability from Tiff library. Make merge build use symbolic links instead of hard links. Include dependent libraries in the link of the libraries. This is necessary on some systems, and a good idea on others. Use strerror() instead of sys_errlist[] everywhere. If some systems do not have the former, we need to do some work, because strerror() was already used in some places. We do have a report of a BeOS system with no sys_errlist[]. 00.03.24 BJH Release 8.3 Add ppmshadow, by John Walker , dated 1997.08.15. Add ppmlabel, by John Walker, dated June 1995. Add ppmcie by John Walker, dated September 1994. Add character drawing routines by John Walker to libppm. Add sbigtopgm by John Walker, dated January 1998. Handle BMP file color map size parameter in ppmtobmp, bmptoppm. Thanks Marc Moorcroft. Rewrite make files, fix lots of make install bugs. 00.03.20 BJH Release 8.2 Add ppmtojpeg and jpegtopnm. Rename pnmnoraw to pnmtoplainpnm. Add CMYK capability to tifftopnm. Major make file rewrite, especially making install work. Pnmmerge is no longer the default. Shared libraries are. Add pnmtotiffcmyk. Written by Andrew Cooke (Jara Software) jara@andrewcooke.free-online.co.uk 00.03.02 BJH Release 8.1 00.03.02 BJH Add pnmtopng and pngtopnm. I got these from ftp://ftp.au.netbsd.org/pub/NetBSD/packages/distfiles today. Release 2.37.3. I added an "unsigned" and changed the type of 'scaleval' to quiet compiler warnings. 00.03.01 BJH Fix xwdtopnm interpretation of 16 bit TrueColor files. Thanks to Martin Kroeker, mk@daveg.com. 99.09.23 BJH Update xbmtopbm to recognize newer xbm format. CHANGES TO NETPBM THROUGH MARCH 1994 ------------------------------------ Functional changes to Netpbm since 13 October 1993. Minor bug fixes and compatibility fixes are not documented in this file. PBM libpbm1.c strstr() added to libpbm1.c. libpbm5.c BDF font functions added. pbmtext Ability to use BDF fonts added. pbmto4425 New filter. pbmtoln03 Command line parsing changed to Pbmplus standard. PGM pgmnoise New filter. PPM picttoppm Updated ppm3d New facility. ppmchange New filter. ppmdim New filter. ppmflash New filter. ppmmix New filter. ppmntsc New filter. ppmqvga Option parsing changed to Pbmplus standard. ppmshift New filter. ppmspread New filter. ppmtoxpm Prototypes added. xpmtoppm Prototypes added. ilbmtoppm Updated. ppmtoilbm Updated. PNM pnmtoddif New filter. pnmhistmap New facility. pnmtops New option (-nocenter) added. Functional changes to Netpbm since 7 December 1993. Minor bug fixes and compatibility fixes are not documented in this file. PGM asciitopgm New filter. fitstopgm Replaced by fitstopnm. pgmtofits Replaced by pnmtofits. pgmtopbm Upgraded. pgmkernel New filter. PPM ppmchange Upgraded. xvminitoppm New filter. PNM pnmalias New filter. pnmtofits Replacement for pgmtofits. fitstopnm Replacement for fitstopgm. pnmtosgi New filter. sgitopnm New filter. pstopnm New filter. CHANGES BETWEEN PBMPLUS AND THE ORIGINAL NETPBM ----------------------------------------------- The following is new in Netpbm (compared to Pbmplus): PBM pbmtext Ability to use BDF fonts added. pbmto4425 Display on an AT&T 4425 Ascii terminal. pbmtoascii A new improved version. pbmtoln03 Convert to DEC LN03+. pbmtolps Fast PostScript creator. pbmtopk Conversion to/from a packed (PK) format font. pktopbm pbmclean Flip isolated pixels. pbmpscale Enlarge pbm image with edge smoothing. PGM asciitopgm Convert an ascii image into pgm. pbmtopgm Convert pbm to pgm by averaging areas. rawtopgm Handles input files without specification of the file size, assuming the input image is quadratic. It also has a -tb (top bottom flip) option. bioradtopgm Conversion utility for files created by Biorad confocal microscopes. spottopgm Convert SPOT satellite images to pgm. pgmkernel Generate a convolution kernel. pgmnoise Create a pgm file with random pixels. PPM bmptoppm Conversion to/from windows bitmap format. ppmtobmp ppmtogif Updated version. giftoppm Removed (see giftopnm). ppmtoilbm Updated version. ilbmtoppm picttoppm Updated version. ppmtopict ppmtoxpm Updated version, which understands xpm version 3. xpmtoppm ppmtomap Extract all colors from a ppm file. ppmtomitsu Convert to Mitsubishi S340-10 printer format. xvminitoppm Convert an XV thumbnail picture to ppm. ppmtoyuvsplit Conversion to/from YUV triplets. (MPEG / JPEG). yuvsplittoppm ppm3d Create a red/blue stereo image. ppmbrighten Change image saturation and value on an HSV map. ppmchange Change all pixels of one color to another in a portable pixmap ppmdim Dim a ppm file down to total blackness. ppmdist Simplistic grayscale assignment for machine generated color images. ppmflash Brighten a picture up to complete white-out ppmmix Blend together two portable pixmaps. ppmnorm Normalize the contrast in a portable pixmap. ppmntsc Make a portable pixmap look like taken from an American TV. ppmqvga Eight plane quantization. ppmshift Shift lines of a portable pixmap left or right by a random amount. ppmspread Displace a portable pixmap's pixels by a random amount. ppmtopjxl Convert a ppm file into an HP PaintJet XL PCL file. PNM pnmtops New option (-nocenter) added. pnmtofits Replacement for pgmtofits/fitstopgm fitstopnm pnmtosgi Conversion to/from sgi image format. sgitopnm pnmtosir Conversion to/from Solitaire image recorder format. sirtopnm giftopnm Replaces giftoppm. Examines the input image and produces a pbm, pgm, or ppm output. pstopnm Convert PostScript to pnm. Requires Ghostscript. zeisstopnm Conversion utility for files created by Zeiss confocal microscopes (the old standard). pnmalias Anti aliasing filter. pnmcomp Composite two portable anymaps together. pnmcrop New options added. pnmpad Add borders to anymap. LIBTIFF A new release of libtiff is included. Please read its supporting documentation. CHANGE LOG FROM PBMPLUS ----------------------- Changes since the 30oct91 patch version: Fixed uninitialized variable in ppmtotga. (John Walker) Added pgmcrater, ppmforge, ppmtoacad, sldtoppm. (John Walker) Slight change to the p?mmerge.c front-ends to allow for main programs that return instead of exitting. Minor clarifications to the pnmconvol man page. Fixed xwdtopnm to read some 16-bit True/Direct files. (David Elliott) Fixed uninitialized variables in pnmtotiff. (Larry Rosenstein, Bayles Holt) Added a couple of checks for \r while reading whitespace. (Larry Rosenstein) Removed all the BROKENPUTC stuff. Now we ignore all return values from putc(), and check ferror() in pm_close(). Added pm_close() calls to many of the filters. Fixed pnmdepth to check for too-large newmaxvals. Improved rounding in pnmdepth and PPM_DEPTH macro. (Tom Lane) Minor fix to ppmtouil. (Mohsen Banan) Added new Imakefiles. (Rainer Klute) Changes since the 05oct91 X11R5 contrib tape version: Fixed minor SysV config error in pbmplus.h. (Tom Lane) Fixed tifftopnm so that BITSPERSAMPLE and SAMPLESPERPIXEL default correctly. Fixed possible bug in tgatoppm line-interleaving code. (Arthur David Olson) Fixed tifftopnm so that colormapped files are read correctly. (PauL Drews, Mike Wade) Corrected use of DefaultRGBDatabase / RGB_DB for imake sites. (Randal L. Schwartz) Bugfix to pnmtops color PostScript in -rle mode. (Angus Duggan) Added auto-turning to pnmtops. Added run-length encoding to ppmtotga. Space optimization to pgmoil. Changes since the 27sep91 comp.sources.misc distribution: Fixed spelling error in giftoppm. Fixed ppmrelief to not shrink the image by 2 rows and columns. Minor fix to pgmramp. Fixed off-by-one error in pnmtoxwd. Man page fix for pnmgamma. (Arthur David Olson) Converted ANSI trigraphs in ppmtosixel into good old octal. (Jeff Glover) Fix to 24/32 bit case in rasttopnm. (Behr de Ruiter) Float/double portability fix to libppm4. (Bruce Holmer, Ronald Khoo) Fixed typo in compat.ksh. (Larry Virden) Fixed int/short incompatibility in tifftopnm. (Salik Rafiq) Changes during the extended beta test period, starting on 15jan91: Lots of fixes from: Anthony A. Datri, Arthur David Olson, David Brooks, David Elliott, Doug Claar, Duncan Sinclair, Francois Pinard, Gerard Leurs, Jim Hanko, Ken Laprade, Klaus U. Schallhorn, Markus Bolz, Mike Hench, Philip Gladstone, R C Smith, Selden E. Ball, Jr., Stephen Uitti, Steve Allen, Tom Lane, update.kpj-jaakkola@athena.dsv.su.se, Charles Karney, Unmesh Agarwala, Ed Pendzik, Juha Sarlin, Tom Tulinsky, Phillip Smith, Lai-King Mau, David Koblas, Mark Donovan. Added a global -version flag. Added bunches of statics and prototypes. Now compiles with zero warnings under gcc -ansi -pedantic. Changed #ifdef __STDC__ to #if __STDC__, since some non-compliant compilers define it as 0. Changed pm_message and pm_error to be varargs routines. Added a portable version of vfprintf for those systems which don't have it. Removed the option of not compiling the pgm and ppm parts. Very few people used it, and it added amazing complexity to the pnm programs, turning them into maintenance nightmares. Merged pbmpaste into pnmpaste. Merged pgmtops and ppmtops into pnmtops. Added auto-scaling and dpi / page size flags to pnmtops. Changed the interpretation of bits in pbmlife to conform with other tools. Changed xwdtopnm to ignore the pixel number in the xwd color structure. Added a -pseudodepth flag to pnmtoxwd. Updated tifftopnm for libtiff 2.4. Added many option flags to pnmtotiff. (J.T. Conklin) Added recognition of X11R5's new color specifiers rgb: and rgbi:. Added pgmtexture. (James Darrell McCauley) Added ppmtopj, pjtoppm, and ppmdither. (Christos Zoulas) Added ppmtotga. (Mark Shand) Added ppmtosixel. (Rick Vinci) Added pbmtoatk and atktopbm. (Bill Janssen) Added ppmtoyuv and yuvtoppm. (Marc Boucher) Fixes to picttoppm. (George Phillips) Added recognition of 24-bit images to ilbmtoppm. (Mark Thompson) Changes since the X.V11R4 / comp.sources.misc distribution of 22nov89: Added pgmramp, pgmedge, pgmtoppm, rgb3toppm, ppmtoxpm, pnmgamma, ximtoppm, pgmtofs, picttoppm, ppmtopict, ppmquantall, anytopnm, pi1toppm, ppmtopi1, sputoppm, spctoppm, pbmto10x, ppmtoicr, ppmmake, xpmtoppm, ppmtopuzz, ppmtouil, ybmtopbm, pbmtoybm, lispmtopgm, pgmtolispm, pbmtogem, pi3topbm, pbmtopi3, pbmtoepson, pbmtoplot, pbmtozinc, pbmtext, pnmnoraw, pnmmargin, pnmfile, pnmindex, ppmtorgb3, gouldtoppm, pgmbentley, pgmoil, ppmrelief, pnmtotiff, ppmtopcx. Merged some filters: rasttopbm and rasttoppm into rasttopnm; pbmtorast and ppmtorast into pnmtorast; xwdtopbm and xwdtoppm into xwdtopnm; pbmtoxwd and ppmtoxwd into pnmtoxwd. Promoted some filters: pcxtopbm to pcxtoppm; ppmarith to pnmarith; ppmconvol to pnmconvol; ppmcscale to pnmdepth; ppmrotate to pnmrotate; ppmscale to pnmscale; ppmshear to pnmshear; ppmsmooth to pnmsmooth; tifftopgm to tifftopnm - new version based on Sam Leffler's libtiff. Bugfixes to pbmtoicon, ppmtops. The Makefiles now have a "merge" option. All flags are now case-insensitive. Added $(MAKE) stuff to Makefile. Changed pnmsmooth from a csh script to a sh script. Made macro use in pbmtox10bm and pbmtoxbm more portable. Moved compataliases to compat.csh, and added compat.ksh. Made ppmtoilbm less Amiga-specific. Added -headerskip and -rowskip flags to rawtopgm. Enhanced rasttopnm to interpret 8-bit rasters with no colormap as grayscale. Changed sscanf %g to %f - some systems can't handle %g on input. Added -expand flag to pbmmask. Speedup to pnmflip - don't buffer if possible. Added color-name-to-value routine to ppm - uses X11's rgb.txt if present. Updated Imakefile function to reflect X.V11R4. Removed picttopbm. Improved pnmcut argument syntax so that negative coords work like pnmpaste. Added "magic" file, for use with the "file" program. 40% speedup for pgmnorm from Robert Stockton (rgs@cs.cmu.edu). Fixed long-standing bug involving colormaps on SPARCstations when compiled with gcc -- had to do with passing structs by value. Removed the -x flag from pnmtorast -- it's not really needed. Fixed subtle bug in the pnm reading code that caused pnmcat to blow it on images that differed greatly in width or height. New version of giftoppm that handles the GIF89a standard, and doesn't use fseek. Fixed fitstopgm to handle three-axis images, such as the Hubble pix. Xwdtopnm and pnmtoxwd finally handle byte-order properly. Added -xysize flag to pnmscale. Added conditional ANSI function prototypes to library routines. Added -noantialias flag to pnmrotate and pnmshear. Removed the TIPS file. No one ever sent in any new tips, so I just moved the few I had into the relevant man pages. That's probably where they belonged in the first place. Added justification flags to pnmcat. Added -map flag to ppmquant - user-specifiable colormap. Also, the Floyd-Steinberg error diffusion finally works right. Added -map flag to pgmtoppm. Added DirectColor capability to xwdtopnm and pnmtoxwd. Speedup to pgmtolj from Arthur David Olson: avoid sending whitespace. Fix to pbmtogo from Bo Thide': 2D compression now works. Patch 1 to the X.V11R4 / comp.sources.misc distribution of 22nov89: Fixed bug in pgmtops -rle. Changes since the alt.sources distribution of 13sep89: Small corrections to ppmtorast, pgmtops. Moved pbm/tifftopbm to pgm/tifftopgm - it now handles grayscale TIFF files. Fixed tifftopgm to handle non-native byte order. Changes to tifftopgm to handle bogus AppleScan TIFF files, to have better command syntax, and to use stdio. Optimizations to xbmtopbm, pbmtoxbm, and pbmtox10bm, courtesy of Juha Sarlin, to make them go about three times as fast. Optimization to pgmtops and ppmtops to make them go three times as fast. Optimization to pnmcrop. Added PBMPLUS_BROKENPUTC defines in pbmplus.h to handle systems (such as ULTRIX) which have broken putc() macros. Rewrote ppmscale, pnmcat, and pgmhist to operate line-by-line, instead of reading in the whole image. Rewrote pnmflip to keep only one copy of the image in memory. Added pgmtofits, courtesy of Wilson H. Bent, plus bugfixes to fitstopgm and a patch to giftoppm to handle black&white GIF files. Added picttopbm and rawtopgm. Fixes to xwdtoppm and ppmtoxwd so they compile with SunOS cc as well as gcc. Another small change to ppmtoxwd having to do with colormap size. Changed macptopbm's -headersize flag to be -extraskip. Changed tgatoppm to read color values as BGRA instead of ARGB; the Targa documentation is apparently wrong about the order. Some changes to the Makefiles, partially to work around bugs in gnumake. Got g3topbm working, and added pbmtog3, courtesy of Paul Haeberli. Added some pixrect work-alike code so that rasttopbm, pbmtorast, rasttoppm, and ppmtorast can be used on non-Sun systems. This also provides a final solution to the persistent byte- and bit-order problems on 386's. Moved the SYSV-checking #ifdefs to *after* the include of pbm.h, which defines SYSV. Made all the #else's and #endif's ANSI-compliant. Added manual pages for libpbm, libpgm, libppm, and libpnm, courtesy of Tony Hansen. Changed man page installation so that pages from different sections can go in different directories. Fixed Imakefiles. Changes since the expo.lcs.mit.edu FTP distribution of 06sep89: Added #ifdefs to pnm/libpnm3.c to allow the PBM-PNM-only configuration. Small corrections to TIPS, pnm/Makefile, pnm/Imakefile, ppm/ppmrotate.1, ppm/ppmshear.1, ppm/ppmtoilbm.c, pbm/xwdtopbm.c, ppm/xwdtoppm.c, ppm/ppmtoxwd.c, ppm/ppmtoxwd.1, pbm/x11wd.h. Changes since the comp.sources.misc distribution of 31oct88: Added pbmreduce, pbmlife, pbmmask, and pbmupc. Added gemtopbm, tifftopbm, pcxtopbm, pbmtogo, mgrtopbm, pbmtomgr, cmuwmtopbm, pbmtocmuwm, g3topbm, and pbmtobg. Minor bugfix to pbmtolj. Slight restructuring of most of the programs to use vastly less memory. Various other minor optimizations. Fixed pbmtorast and rasttopbm to handle byte-swapped big-endian 386 boxes. Slight changes to argument syntax of pbmcrop, pbmmake, pbmreduce. Moved to the new PGM package: pbmtops (which now produces Conforming PS). Moved to the new PPM package: giftopbm. Moved to the new PNM package: pbmcrop pbmcut pbmenlarge pbminvert. Consolidated into a single pnmflip tool: pbmfliplr pbmfliptb pbmtrnspos. Consolidated into a single pnmcat tool: pbmcatlr pbmcattb. Added compataliases script for upward compatibility with changed tools. Removed xxxtopbm. Added a -headersize flag to macptopbm, to help get around annoying problems in MacPaint file format. Added the RAWBITS compilation-time option, to use a more compact and much faster (but less portable) external format. Removed the CBM format - use compress(1) and / or RAWBITS instead. Pbmpaste (and the new pnmpaste) now accepts negative x and y coords, which are interpreted relative to the right and bottom sides. Changed all programs to accept a "-" file argument as meaning standard input. Removed pbmtox10wd, since it was never very useful (X10 doesn't have xwud). Added Imakefiles, for X11 types to use. Changes since the X.V11R3 distribution of 31aug88: The cbm format has been revised to include run-length encoding. Pbmtops now does run-length encoding. Major changes since the X.V11R2 distribution of 28mar88: The pbm format now has a "magic number". New conversion filters: brushtopbm, giftopbm, pbmtolj, pbmtomacp, pbmtoxwd, and pbmtox10wd. Icontopbm converter has a better parser -- it knows to skip over any extraneous comments at the beginning of the icon file. Pbmtops generates a different PostScript wrapper program -- it should handle huge bitmaps better. Xwdtopbm now handles byte-swapping correctly. Pbmmake takes a flag to specify the color of the new bitmap. Pbmpaste now implements 'or', 'and', and 'xor' operations as well as the default 'replace'. advanced/doc/lgpl_v21.txt0000644000175000001440000006347612347602011014434 0ustar ericusers GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! advanced/doc/netpbm.html0000644000175000001440000000016412347602011014401 0ustar ericusersAre you looking for the Netpbm user manual? It's not in the source code package. Please see the file doc/USERDOC. advanced/buildtools/0000755000175000001440000000000012347602001013637 5ustar ericusersadvanced/buildtools/typegen.c0000644000175000001440000000571712347602001015470 0ustar ericusers/*---------------------------------------------------------------------------- typegen ------------------------------------------------------------------------------ This is a program to create an inttypes.h file for use in building Netpbm. Ordinarily we use the inttypes.h that comes with the C library or C compiler, specified by Single Unix Specification. When that isn't available there is usually some other system header file we should use. But when there isn't any system header file to define the needed types, the file we generate is better than nothing. The main problem with the inttypes.h we generate is that when we figure out which type is a 32 bit integer, we aren't necessarily using the same compile environment as what will actually get used to build Netpbm, and thus the one where our inttypes.h will be used. Right now, it simply generates either typedef int int32_t; or typedef long int32_t; based on which type, int, or long, is 32 bits. We also include the uint32_t and int_fast32_t typedefs. We also include the multiple inclusion guard ifdef. -----------------------------------------------------------------------------*/ #include #include #include static void createTypedefForUint32_t(void) { if (sizeof(unsigned int) == 4) printf("typedef unsigned int uint32_t;\n"); else if (sizeof(unsigned long) == 4) printf("typedef unsigned long uint32_t;\n"); else { fprintf(stderr, "Cannot find a 32 bit unsigned integer type!\n"); exit(1); } } static void createTypedefForUintFast32_t(void) { if (sizeof(unsigned int) == 4) printf("typedef unsigned int uint_fast32_t;\n"); else if (sizeof(unsigned long) == 4) printf("typedef unsigned long uint_fast32_t;\n"); else { fprintf(stderr, "Cannot find a 32 bit unsigned integer type!\n"); exit(1); } } static void createTypedefForInt32_t(void) { if (sizeof(signed int) == 4) printf("typedef signed int int32_t;\n"); else if (sizeof(signed long) == 4) printf("typedef signed long int32_t;\n"); else { fprintf(stderr, "Cannot find a 32 bit signed integer type!\n"); exit(1); } } static void createTypedefForIntFast32_t(void) { if (sizeof(signed int) == 4) printf("typedef signed int int_fast32_t;\n"); else if (sizeof(signed long) == 4) printf("typedef signed long int_fast32_t;\n"); else { fprintf(stderr, "Cannot find a 32 bit signed integer type!\n"); exit(1); } } int main(int argc, char **argv) { printf("/* This was generated by the program 'typegen' */\n"); printf("#ifndef INTTYPES_H_NETPBM\n"); printf("#define INTTYPES_H_NETPBM\n"); createTypedefForUint32_t(); createTypedefForInt32_t(); createTypedefForIntFast32_t(); createTypedefForUintFast32_t(); printf("#endif\n"); return 0; } advanced/buildtools/install.sh0000755000175000001440000001276512347602000015656 0ustar ericusers#! /bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. # # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then if [ -f "$1.exe" ] then src=$1.exe else src=$1 fi else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 advanced/buildtools/make_merge.sh0000755000175000001440000000045212347602001016273 0ustar ericusers#!/bin/sh # This program is called by the make files. It creates the merge.h # file which is included by netpbm.c. echo "/* File generated by make_merge.sh */" echo "/* in directory" `pwd` "*/" echo for MERGE_BINARY in $*; do echo "TRY(\"${MERGE_BINARY}\", main_${MERGE_BINARY});" done advanced/buildtools/makepointerman0000755000175000001440000000715212347602001016604 0ustar ericusers#!/usr/bin/perl -w ############################################################################# # makepointerman ############################################################################# # # This program creates a Netpbm man page that says nothing except to use a # web browser to look at a particular URL. # # In Netpbm, we believe that man pages, and the Nroff/Troff formats, are # obsolete; that HTML and web browsers and the world wide web long ago replaced # them as the best way to deliver documentation. However, documentation is # useless when people don't know where it is. People are very accustomed to # typing "man" to get information on a Unix program or library or file type, # so in the standard Netpbm installation, we install a conventional man page # for every command, library, and file type, but all it says is to use your # web browser to look at the real documentation. # What would be ideal is if the user simply had Manweb (or something like # it) installed as the 'man' command and configured to find the Netpbm # web documentation. # But because of the high probability that Netpbm installers will not # install Manweb, pointer man pages are necessary. # Besides making the web documentation accessible, pointer man pages serve # another important purpose: Installing them causes obsolete man pages from # before web documentation existed to be discarded. use strict; use Time::gmtime; if (@ARGV < 5) { die("Need 6 arguments: filename, directory URL, man page directory, " . "man section, format, octal permissions"); } my ($filename, $directoryUrl, $mandir, $section, $format, $permissions) = @ARGV; if ($format ne "nroff" && $format ne "cat") { die("format argument must be 'nroff' or 'cat', not '$format'."); } my $manPageFileName = "$mandir/$filename.$section"; unlink($manPageFileName); open(MANPAGE, ">$manPageFileName") or die("Unable to create file '$manPageFileName'"); my ($wday, $mon, $mday, $tod, $year) = split(/ /,gmctime()); if ($format eq "nroff") { print(MANPAGE ".TH $filename $section Netpbm \"$mday $mon $year\" " . "\"Netpbm pointer man pages\"\n\n"); # NAME and DESCRIPTION section headings help some automated processors, # such as Doclifter. From ESR 2004.11.14. # avoid a double slash in the URL (my $cleanedUpDirectoryUrl = $directoryUrl) =~ s{^(.*?)/*$}{$1}; print(MANPAGE ".SH NAME\n"); print(MANPAGE "$filename \\- see $cleanedUpDirectoryUrl/$filename.html\n"); print(MANPAGE ".SH DESCRIPTION\n"); } else { print(MANPAGE " NETPBM POINTER MAN PAGES \n\n"); } print(MANPAGE "$filename is part of the Netpbm package.\n"); print(MANPAGE "Netpbm documentation is kept in HTML format.\n"); print(MANPAGE "\n"); print(MANPAGE "Please refer to <$directoryUrl/$filename.html>.\n\n"); print(MANPAGE "If that doesn't work, also try " . " and\n"); print(MANPAGE "emailing Bryan Henderson, bryanh\@giraffe-data.com.\n"); print(MANPAGE "\n"); print(MANPAGE "Note that making the documentation available this way was\n"); print(MANPAGE "a choice of the person who installed Netpbm on this system.\n"); print(MANPAGE "It is also possible to install Netpbm such that you would\n"); print(MANPAGE "simply see the documentation instead of the message you are\n"); print(MANPAGE "reading now.\n"); print(MANPAGE "\n"); if ($format eq "nroff") { print(MANPAGE ".\\\" This file was generated by the program " . "'makepointerman',\n"); print(MANPAGE ".\\\" as part of Netpbm installation\n"); } chmod(oct("0$permissions"),$manPageFileName); close(MANPAGE); advanced/buildtools/pkgconfig_template0000644000175000001440000000021412347602001017421 0ustar ericusersName: Netpbm Description: Graphics utilities Version: @VERSION@ Requires: Libs: -L@LINKDIR@ -lnetpbm Libs.private: Cflags: -I@INCLUDEDIR@ advanced/buildtools/manpage.mk0000644000175000001440000001660412347602001015607 0ustar ericusers# Make Unix man pages from Netpbm HTML user manual MAKEMAN = makeman MANDIR = /usr/share/man/ # These can convert to man pages cleanly MAN1 = \ 411toppm.1 \ anytopnm.1 \ asciitopgm.1 \ atktopbm.1 \ bioradtopgm.1 \ bmptopnm.1 \ bmptoppm.1 \ brushtopbm.1 \ cameratopam.1 \ cmuwmtopbm.1 \ ddbugtopbm.1 \ escp2topbm.1 \ eyuvtoppm.1 \ fiascotopnm.1 \ fitstopnm.1 \ fstopgm.1 \ g3topbm.1 \ gemtopbm.1 \ gemtopnm.1 \ giftopnm.1 \ gouldtoppm.1 \ hdifftopam.1 \ hipstopgm.1 \ hpcdtoppm.1 \ icontopbm.1 \ ilbmtoppm.1 \ imgtoppm.1 \ infotopam.1 \ jbigtopnm.1 \ jpeg2ktopam.1 \ jpegtopnm.1 \ leaftoppm.1 \ lispmtopgm.1 \ macptopbm.1 \ manweb.1 \ mdatopbm.1 \ mgrtopbm.1 \ mrf.1 \ mrftopbm.1 \ mtvtoppm.1 \ neotoppm.1 \ palmtopnm.1 \ pamarith.1 \ pamchannel.1 \ pamcomp.1 \ pamcut.1 \ pamdeinterlace.1 \ pamdice.1 \ pamditherbw.1 \ pamedge.1 \ pamendian.1 \ pamfile.1 \ pamfixtrunc.1 \ pamflip.1 \ pamfunc.1 \ pamgauss.1 \ pamlookup.1 \ pamoil.1 \ pamperspective.1 \ pampop9.1 \ pamscale.1 \ pamseq.1 \ pamsharpmap.1 \ pamsharpness.1 \ pamslice.1 \ pamstack.1 \ pamstereogram.1 \ pamstretch-gen.1 \ pamstretch.1 \ pamsumm.1 \ pamsummcol.1 \ pamtodjvurle.1 \ pamtohdiff.1 \ pamtohtmltbl.1 \ pamtojpeg2k.1 \ pamtopfm.1 \ pamtopnm.1 \ pamtotga.1 \ pamtouil.1 \ pbmclean.1 \ pbmlife.1 \ pbmmake.1 \ pbmmask.1 \ pbmpage.1 \ pbmpscale.1 \ pbmreduce.1 \ pbmtext.1 \ pbmtextps.1 \ pbmto10x.1 \ pbmto4425.1 \ pbmtoascii.1 \ pbmtoatk.1 \ pbmtobbnbg.1 \ pbmtocmuwm.1 \ pbmtodjvurle.1 \ pbmtoepsi.1 \ pbmtoepson.1 \ pbmtoescp2.1 \ pbmtog3.1 \ pbmtogem.1 \ pbmtogo.1 \ pbmtoicon.1 \ pbmtolj.1 \ pbmtoln03.1 \ pbmtolps.1 \ pbmtomacp.1 \ pbmtomda.1 \ pbmtomgr.1 \ pbmtomrf.1 \ pbmtonokia.1 \ pbmtopgm.1 \ pbmtopi3.1 \ pbmtopk.1 \ pbmtoplot.1 \ pbmtoppa.1 \ pbmtopsg3.1 \ pbmtoptx.1 \ pbmtowbmp.1 \ pbmtox10bm.1 \ pbmtoxbm.1 \ pbmtoybm.1 \ pbmtozinc.1 \ pbmupc.1 \ pc1toppm.1 \ pcdovtoppm.1 \ pcxtoppm.1 \ pfmtopam.1 \ pgmabel.1 \ pgmbentley.1 \ pgmcrater.1 \ pgmedge.1 \ pgmenhance.1 \ pgmhist.1 \ pgmkernel.1 \ pgmminkowski.1 \ pgmmorphconv.1 \ pgmnoise.1 \ pgmnorm.1 \ pgmoil.1 \ pgmramp.1 \ pgmslice.1 \ pgmtexture.1 \ pgmtofs.1 \ pgmtolispm.1 \ pgmtopbm.1 \ pgmtopgm.1 \ pgmtoppm.1 \ pi1toppm.1 \ pi3topbm.1 \ picttoppm.1 \ pjtoppm.1 \ pktopbm.1 \ pngtopnm.1 \ pnmalias.1 \ pnmarith.1 \ pnmcat.1 \ pnmcolormap.1 \ pnmcomp.1 \ pnmconvol.1 \ pnmcrop.1 \ pnmcut.1 \ pnmdepth.1 \ pnmenlarge.1 \ pnmfile.1 \ pnmgamma.1 \ pnmhisteq.1 \ pnmhistmap.1 \ pnmindex.1 \ pnminterp.1 \ pnminvert.1 \ pnmmargin.1 \ pnmmontage.1 \ pnmnlfilt.1 \ pnmnoraw.1 \ pnmnorm.1 \ pnmpad.1 \ pnmpaste.1 \ pnmpsnr.1 \ pnmquant.1 \ pnmremap.1 \ pnmrotate.1 \ pnmscale.1 \ pnmscalefixed.1 \ pnmshear.1 \ pnmsmooth.1 \ pnmsplit.1 \ pnmstitch.1 \ pnmtile.1 \ pnmtoddif.1 \ pnmtofiasco.1 \ pnmtofits.1 \ pnmtojbig.1 \ pnmtojpeg.1 \ pnmtopalm.1 \ pnmtopclxl.1 \ pnmtoplainpnm.1 \ pnmtopng.1 \ pnmtopnm.1 \ pnmtops.1 \ pnmtorast.1 \ pnmtorle.1 \ pnmtosgi.1 \ pnmtosir.1 \ pnmtotiff.1 \ pnmtotiffcmyk.1 \ pnmtoxwd.1 \ ppm3d.1 \ ppmbrighten.1 \ ppmchange.1 \ ppmcie.1 \ ppmcolormask.1 \ ppmdim.1 \ ppmdist.1 \ ppmdither.1 \ ppmfade.1 \ ppmflash.1 \ ppmforge.1 \ ppmglobe.1 \ ppmhist.1 \ ppmlabel.1 \ ppmmake.1 \ ppmmix.1 \ ppmnorm.1 \ ppmntsc.1 \ ppmpat.1 \ ppmquant.1 \ ppmquantall.1 \ ppmrainbow.1 \ ppmrelief.1 \ ppmrough.1 \ ppmshadow.1 \ ppmshift.1 \ ppmspread.1 \ ppmsvgalib.1 \ ppmtoacad.1 \ ppmtoarbtxt.1 \ ppmtobmp.1 \ ppmtoeyuv.1 \ ppmtoicr.1 \ ppmtoilbm.1 \ ppmtojpeg.1 \ ppmtoleaf.1 \ ppmtolj.1 \ ppmtomap.1 \ ppmtomitsu.1 \ ppmtompeg.1 \ ppmtoneo.1 \ ppmtopcx.1 \ ppmtopgm.1 \ ppmtopi1.1 \ ppmtopict.1 \ ppmtopj.1 \ ppmtopjxl.1 \ ppmtoppm.1 \ ppmtopuzz.1 \ ppmtorgb3.1 \ ppmtosixel.1 \ ppmtoterm.1 \ ppmtotga.1 \ ppmtouil.1 \ ppmtowinicon.1 \ ppmtoxpm.1 \ ppmtoyuv.1 \ ppmtoyuvsplit.1 \ ppmtv.1 \ ppmwheel.1 \ psidtopgm.1 \ pstopnm.1 \ qrttoppm.1 \ rasttopnm.1 \ rawtopgm.1 \ rawtoppm.1 \ rgb3toppm.1 \ rletopnm.1 \ sbigtopgm.1 \ sgitopnm.1 \ sirtopnm.1 \ sldtoppm.1 \ spctoppm.1 \ spottopgm.1 \ sputoppm.1 \ tgatoppm.1 \ thinkjettopbm.1 \ tifftopnm.1 \ vidtoppm.1 \ wbmptopbm.1 \ winicontoppm.1 \ xbmtopbm.1 \ ximtoppm.1 \ xpmtoppm.1 \ xvminitoppm.1 \ xwdtopnm.1 \ ybmtopbm.1 \ yuvsplittoppm.1 \ yuvtoppm.1 \ zeisstopnm.1 \ pamaddnoise.1 \ pambackground.1 \ pambayer.1 \ pamdepth.1 \ pamenlarge.1 \ pamgradient.1 \ pammasksharpen.1 \ pammixinterlace.1 \ pampick.1 \ pamrgbatopng.1 \ pamsplit.1 \ pamthreshold.1 \ pamtilt.1 \ pamtofits.1 \ pamtogif.1 \ pamtosvg.1 \ pamtotiff.1 \ pamtoxvmini.1 \ pamx.1 \ pbmtoibm23xx.1 \ pbmtomatrixorbital.1 \ pgmdeshadow.1 \ pgmmake.1 \ pgmmedian.1 \ ppmdcfont.1 \ ppmddumpfont.1 \ ppmdmkfont.1 \ ppmdraw.1 \ rlatopam.1 \ MAN3 = \ libnetpbm.3 \ libnetpbm_image.3 \ libnetpbm_ug.3 \ libpbm.3 \ libpgm.3 \ libpm.3 \ libpnm.3 \ libppm.3 \ libsystem.3 \ libtmpfile.3 \ MAN5 = \ extendedopacity.5 \ pam.5 \ pbm.5 \ pfm.5 \ pgm.5 \ pnm.5 \ ppm.5 \ # These things do get converted to man pages and installed. MANPAGES = $(MAN1) netpbm.1 $(MAN3) $(MAN5) HTMLMANUALS = $(MAN1:.1=.html) $(MAN3:.3=.html) $(MAN5:.5=.html) # These things don't get converted to manual pages. EXCEPTIONS = directory.html libnetpbm_dir.html libnetpbm_draw.html error.html STUBS = pcdindex.1 ppmcolors.1 pnmflip.1 ppmtogif.1 # This works if you've done a full SVN checkout. USERGUIDE= ../../userguide XML = $(HTMLMANUALS:.html=.xml) netpbm.xml # List everything in the userguide directory that is not categorized above. # Use this to check that 'make manpages' converts as much as possible # of the HTML documentation. uncategorized: @echo $(HTMLMANUALS) $(EXCEPTIONS) $(STUBS) | tr " " "\n" | sort >LIST1 @(cd $(USERGUIDE); ls | sort) >LIST2 @comm -3 LIST1 LIST2 @rm LIST1 LIST2 # Make man pages -- reports bad lines to standard error. manpages: @python $(MAKEMAN) -d $(USERGUIDE) index.html $(HTMLMANUALS) @mv index.1 netpbm.1 # Make XML pages, and validate them. xmlpages: @for x in $(MANPAGES); do doclifter -v $$x; done @for x in $(MANPAGES); do xmllint -xinclude --postvalid $$x.xml >/dev/null; done # This will install the generated man pages installman: set -x for f in $(MAN1); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man1/$$f.gz; fi; done for f in $(MAN3); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man3/$$f.gz; fi; done for f in $(MAN5); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man5/$$f.gz; fi; done # This will uninstall them uninstallman: for f in $(MAN1); do rm -f $(MANDIR)/man1/$$f.gz; fi; done for f in $(MAN3); do rm -f $(MANDIR)/man3/$$f.gz; fi; done for f in $(MAN5); do rm -f $(MANDIR)/man5/$$f.gz; fi; done oldclean: # Clean up old locations on Fedora Core 2 rm -f $(MANDIR)/man1/extendedopacity.1.gz rm -f $(MANDIR)/man3/directory.3.gz rm -f $(MANDIR)/man3/libnetpbm_dir.3.gz # remove pointer man pages (that say, "The man page isn't here") # which might have been installed previously for f in $(MAN1); do rm -f $(MANDIR)/man1/$$f; done for f in $(MAN3); do rm -f $(MANDIR)/man3/$$f; done for f in $(MAN5); do rm -f $(MANDIR)/man5/$$f; done clean: @rm -f *.[135] $(XML) advanced/buildtools/README.pkg0000644000175000001440000002036512347602001015305 0ustar ericusersTHESE ARE THE MANUAL INSTALLATION INSTRUCTIONS FOR NETPBM Most people install using the interactive install program 'installnetpbm' that is in the top level directory of the Netpbm source tree. But it isn't right for everyone. If it doesn't do what you need, you can use these instructions instead. Once you have built and packaged Netpbm, installing is pretty straightforward. If you browse the package directory, you can probably figure it out without reading any further. The parts to be installed are: Executables These are the basic Netpbm programs, such as 'jpegtopnm'. You will find these in the 'bin' subdirectory of the package directory. You normally want to copy all of these into a directory that is in your default program search path (which is controlled by your PATH environment variable). Typical directories for this are /bin, /usr/bin, and /usr/local/bin. Shared Library This is the library that all Netpbm programs need to load and link to at run time. It is in the 'lib' subdirectory of the package directory. Building a shared library is optional; if you didn't do it (which means you built executables that don't require it), you don't have a 'lib' subdirectory. Shared libraries are also known as dynamic libraries and DLLs. You normally want to copy the shared library to a directory that is in your system's default shared library search path. On systems that have an 'ldconfig' program, that program controls the shared library search path, and you must run it after copying the Netpbm shared library to its directory. Often, simply rebooting will cause it to run cleanly. Typical directories for this are /lib, /usr/lib, and /usr/local/lib. On Windows, the DLLs are treated like executables, so you should find the 'lib' directory empty and you should find the Netpbm DLL in the 'bin' directory. You'll probably want to install the DLL and the executables in the same directory, because the shared library and executable search paths are the same. Link Library This is a static link library. You don't need it to run Netpbm. You need it only if you want to build your own programs that use the Netpbm library. It is in the 'link' subdirectory of the package directory. You normally want to copy the link library into a directory that is in the default search path of your linker. Typical directories for this are /usr/lib and /usr/local/lib. Interface Headers These are the files that declare the interface to the Netpbm programming library. You don't need them to run Netpbm. You need them only if you want to build your own programs that use the Netpbm library. They are in the 'include' subdirectory of the package directory. You normally want to copy the interface header files into a directory that is in the default search path of your compiler. Typical directories for this are /usr/include and /usr/local/include. All of the files are meant to be named like for C #include purposes, which means on an ordinary system, they are installed in a directory named "netpbm". Thus, they are organized this way in the package. BUT: until Netpbm 10.41 (December 2007), they were packaged for, and customarily used, just as . Therefore, for backward compatibility, it is a good idea to make symbolic links from the parent directory into the netpbm/ directory. But as that may pollute your namespace, you may prefer just to make all users migrate to the form. Data Files These are files that you can use for various purposes as input to Netpbm programs. People rarely have uses for them, actually. They are in the 'data' subdirectory of the package directory. Put these somewhere that users will be able to find them. /usr/lib/netpbm and /usr/share/netpbm are typical choices. Man Pages Netpbm does not have the typical Unix form of documentation. The documentation is not in files that you can read with the common 'man' program. Instead, it is in HTML form, and it is not in the package directory at all. You must install it separately. But so that a user who is not familiar with Netpbm documentation doesn't find himself out in the cold with no access to documentation, the package directory contains traditional man pages that do nothing but tell you where to get the real documentation. (Exactly where that is depends on the options with which you built and packaged Netpbm). We call these "pointer man pages." You will find these man pages in the 'man' subdirectory of the package directory. You should copy the contents of this directory wherever your 'man' program looks for man pages. Typically, this is /usr/man, which has subdirectories equivalent to those in the package directory's 'man' subdirectory. One of the Netpbm programs is Manweb, which is designed to be a replacement for the classic Man program that can access both traditional man pages and worldwide web documentation in the Netpbm style with the familiar 'man jpegtopnm' kind of command. The package contains the files necessary to use Manweb to access Netpbm documention on the web (on netpbm.sourceforge.net). These files are the contents of the man/web directory and the file 'bin/doc.url'. You should install the 'bin/doc.url' file if you are installing the Netpbm executables in a directory all their own. You should install the 'man/web' files if you are installing the Netpbm executables in some global directory such as 'usr/bin'. You should install neither of these if you don't intend to use Manweb. If you install Manweb as 'man', there is no point to installing the pointer man pages -- they will never be used. If you want to use Manweb to view the Netpbm manual but use a local copy of the manual instead of accessing it on the web, you can get the HTML files from the Sourceforge Subversion repository and install those instead of the .url files that appear in the 'man/web' directory of the package. The instructions above suggest putting the Netpbm parts in common directories such as /usr/bin, mingled with other packages. This is usually the easiest way to get Netpbm working. But also consider putting all Netpbm parts in separate Netpbm directories, such as /usr/bin/netpbm/ and /usr/link/netpbm. In fact, you can just copy the entire package directory in one piece to some place such as /usr/local/netpbm. You'll have to take care to set up search paths and such to make this kind of configuration work. The advantage of keeping Netpbm separate is that it makes it easy to wipe out the entire installation when you don't want it anymore, and to keep multiple versions around. netpbm.pkgconfig ---------------- You should create a file named 'netpbm.pkgconfig' out of the template file 'pkgconfig_template' in the package directory, and install netpbm.pkgconfig in your pkg-config directory (typically /usr/lib/pkgconfig). Programs that want to find out where you installed some part of Netpbm can use a 'pkg-config netpbm ...' command to find out. For example, a make file for a program that uses the Netpbm programming library might use this facility to generate the necessary compiler and linker options to access that library. The pkg-config facility is fairly widely used, especially with things related to the X Window System. netpbm-config ------------- You should create a shell script named 'netpbm-config' out of the template file 'config_template' in the package directory, and install netpbm-config in your executable search path. Programs that want to find out where you installed some part of Netpbm can invoke netpbm-config and it will tell them. For example, a make file for a program that uses the Netpbm programming library might use netpbm-config to generate the necessary compiler and linker options to access that library. Using netpbm-config, it's possible to have a viable Netpbm installation where netpbm.config is the only file in any default search path. The xxx-config concept (in general, not just Netpbm) has largely been replaced by the pkg-config concept (see netpbm.pkgconfig above). advanced/buildtools/libopt.c0000644000175000001440000004546112347602001015306 0ustar ericusers/*---------------------------------------------------------------------------- libopt ------------------------------------------------------------------------------ This is a program to convert link library filepaths to linker options that select them. E.g. ../lib/libnetpbm.so becomes -L../lib -lnetpbm . Each argument is a library filepath. The option string to identify all of those library filepaths goes to Standard Output. If there is no slash in the library filepath, we assume it is just a filename to be searched for in the linker's default search path, and generate a -l option, but no -L. If an argument doesn't make sense as a library filespec, we copy it verbatim, blank delimited, to the output string. The "lib" part of the library name, which we call the prefix, may be other than "lib". The list of recognized values is compiled in as the macro SHLIBPREFIXLIST (see below). There is no newline or null character or anything after the output. If you specify the option "-runtime", the output includes a -R option in addition, for every library after "-runtime" in the arguments. -R tells the buildtime linker to include the specified library's directory in the search path that the runtime linker uses to find dynamically linked libraries. But if you compile this program with the EXPLICIT macro defined, and a library filepath contains a slash, then it skips all that -L/-l nonsense and just outputs the input library filepath verbatim (plus any -R option). ------------------------------------------------------------------------------ Why would you want to use this? On some systems, the -L/-l output of this program has exactly the same effect as the filepath input when used in the arguments to a link command. A GNU/Linux system, for example. On others (Solaris, for example), if you include /tmp/lib/libnetpbm.so in the link as a link object, the executable gets built in such a way that the system accesses the shared library /tmp/lib/libnetpbm.so at run time. On the other hand, if you instead put the options -L/tmp/lib -lnetpbm on the link command, the executable gets built so that the system accesses libnetpbm.so in its actual installed directory at runtime (that location might be determined by a --rpath linker option or a LD_LIBRARY_PATH environment variable at run time). In a make file, it is nice to use the same variable as the dependency of a rule that builds an executable and as the thing that the rule's command uses to identify its input. Here is an example of using libopt for that: NETPBMLIB=../lib/libnetpbm.so ... pbmmake: pbmmake.o $(NETPBMLIB) ld -o pbmmake pbmmake.o `libopt $(NETPBMLIB)` --rpath=/lib/netpbm Caveat: "-L../lib -lnetpbm" is NOT exactly the same as "../lib/libnetpbm.so" on any system. All of the -l libraries are searched for in all of the -L directories. So you just might get a different library with the -L/-l version than if you specify the library file explicitly. That's why you should compile with -DEXPLICIT if your linker can handle explicit file names. -----------------------------------------------------------------------------*/ #define _BSD_SOURCE 1 /* Make sure strdup() is in stdio.h */ #define _XOPEN_SOURCE 600 /* Make sure strdup() is in string.h */ #define MAX_PREFIXES 10 /* Here's how to use SHLIBPREFIXLIST: Use a -D compile option to pass in a value appropriate for the platform on which you are linking libraries. It's a blank-delimited list of prefixes that library names might have. "lib" is traditionally the only prefix, as in libc or libnetpbm. However, on Windows there is a convention of using different prefixes to distinguish different co-existent versions of the same library (kind of like a major number in some unices). E.g. the value "cyg lib" is appropriate for a Cygwin system. */ #ifndef SHLIBPREFIXLIST # define SHLIBPREFIXLIST "lib" #endif #include #include #include typedef unsigned char bool; #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #ifdef DLLVERSTR static const char * dllverstr = DLLVERSTR; #else static const char * dllverstr = ""; #endif bool const explicit = #ifdef EXPLICIT TRUE #else FALSE #endif ; static void strfree(const char * const arg) { free((void*) arg); } static void parse_prefixlist(const char * const prefixlist, char * parsed_prefixes[MAX_PREFIXES+1], bool * const errorP) { /*---------------------------------------------------------------------------- Given a space-separated list of tokens (suffixes), return an argv-style array of pointers, each to a newly malloc'ed storage area the prefix as a null-terminated string. Return a null string for the unused entries at the end of the array We never return more than MAX_PREFIXES prefixes from the input, so there is guaranteed always to be one null string at the end of the array. In case of error, return *errorP == TRUE and don't allocate any storage. Otherwise, return *errorP = FALSE. -----------------------------------------------------------------------------*/ char * prlist; prlist = strdup(prefixlist); if (prlist == NULL) *errorP = TRUE; else { if (strlen(prlist) <= 0) *errorP = TRUE; else { /* NOTE: Mac OS X, at least, does not have strtok_r(). 2001.09.24 */ char * token; int num_tokens; int i; for (i=0; i < MAX_PREFIXES + 1; i++) { parsed_prefixes[i] = NULL; } num_tokens = 0; token = strtok(prlist, " "); *errorP = FALSE; /* initial value */ while (token != NULL && num_tokens < MAX_PREFIXES && !*errorP) { parsed_prefixes[num_tokens] = strdup (token); if (parsed_prefixes[num_tokens] == NULL) *errorP = TRUE; num_tokens++; token = strtok(NULL, " "); } for (i = num_tokens; i < MAX_PREFIXES + 1 && !*errorP; i++) { parsed_prefixes[i] = strdup(""); if (parsed_prefixes[i] == NULL) *errorP = TRUE; } } if (*errorP) { /* Deallocate any array entries we successfully did */ int i; for (i = 0; i < MAX_PREFIXES + 1; i++) if (parsed_prefixes[i]) free(parsed_prefixes[i]); } free(prlist); } } static void parse_prefix(const char * const filename, bool * const prefix_good_p, unsigned int * const prefix_length_p, bool * const error_p) { /*---------------------------------------------------------------------------- Find the library name prefix (e.g. "lib") in the library filename 'filename'. Return the length of the prefix, in characters, as *prefix_length_p. (The prefix always starts at the beginning of the filename). Iff we don't find a valid library name prefix, return *prefix_good_p == FALSE. The list of valid prefixes is compiled in as the blank-delimited string which is the value of the SHLIBPREFIXLIST macro. -----------------------------------------------------------------------------*/ char * shlibprefixlist[MAX_PREFIXES+1]; /* An argv-style array of prefix strings in the first entries, null strings in the later entries. At most MAX_PREFIXES prefixes, so at least one null string. */ char * prefix; /* The prefix that the filename actually uses. e.g. if shlibprefixlist = { "lib", "cyg", "", ... } and the filename is "path/to/cyg.", then prefix = "cyg". String is in the same storage as pointed to by shlibprefixlist (shlibprefixlist[1] in this example). */ bool prefix_good; /* The first part of the filename matched one of the prefixes in shlibprefixlist[]. */ int prefix_length; int i; parse_prefixlist(SHLIBPREFIXLIST , shlibprefixlist, error_p); if (!*error_p) { if (strcmp(shlibprefixlist[0], "") == 0) { fprintf(stderr, "libopt was compiled with an invalid value " "of the SHLIBPREFIX macro. It seems to have no " "tokens. SHLIBPREFIX = '%s'", SHLIBPREFIXLIST); exit(100); } i = 0; /* start with the first entry in shlibprefixlist[] */ prefix_length = 0; /* initial value */ prefix = shlibprefixlist[i]; prefix_good = FALSE; /* initial value */ while ( (*prefix != '\0' ) && !prefix_good ) { /* stop condition: shlibprefixlist has MAX_PREFIXES+1 entries. * we only ever put tokens in the 0..MAX_PREFIXES-1 positions. * Then, we fill DOWN from the MAX_PREFIXES position with '\0' * so we insure that the shlibprefixlist array contains at * least one final '\0' string, but probably many '\0' * strings (depending on how many tokens there were). */ prefix_length = strlen(prefix); if (strncmp(filename, prefix, prefix_length) == 0) { prefix_good = TRUE; /* at this point, prefix is pointing to the correct * entry, and prefix_length has the correct value. * When we bail out of the while loop because of the * !prefix_good clause, we can then use these * vars (prefix, prefix_length) */ } else { prefix = shlibprefixlist[++i]; } } *prefix_length_p = prefix_length; *prefix_good_p = prefix_good; { int i; for (i=0; i < MAX_PREFIXES + 1; i++) free (shlibprefixlist[i]); } } } static void parse_filename(const char * const filename, const char ** const libname_p, bool * const valid_library_p, bool * const static_p, bool * const error_p) { /*---------------------------------------------------------------------------- Extract the library name root component of the filename 'filename'. This is just a filename, not a whole pathname. Return it in newly malloc'ed storage pointed to by '*libname_p'. E.g. for "libxyz.so", return "xyz". return *valid_library_p == TRUE iff 'filename' validly names a library that can be expressed in a -l linker option. return *static_p == TRUE iff 'filename' indicates a static library. (but undefined if *valid_library_p != TRUE). return *error_p == TRUE iff some error such as out of memory prevents parsing. Do not allocate any memory if *error_p == TRUE or *valid_library_p == FALSE. -----------------------------------------------------------------------------*/ char *lastdot; /* Pointer to last period in 'filename'. Null if none */ /* We accept any period-delimited suffix as a library type suffix. It's probably .so or .a, but is could be .kalamazoo for all we care. (HOWEVER, the double-suffixed import lib used on cygwin (.dll.a) is NOT understood). */ char *p; lastdot = strrchr(filename, '.'); if (lastdot == NULL) { /* This filename doesn't have any suffix, so we don't understand it as a library filename. */ *valid_library_p = FALSE; *error_p = FALSE; } else { unsigned int prefix_length; bool prefix_good; if (strcmp(lastdot + 1, "a") == 0) *static_p = TRUE; else *static_p = FALSE; parse_prefix(filename, &prefix_good, &prefix_length, error_p); if (!*error_p) { if (!prefix_good) { *valid_library_p = FALSE; } else { /* Extract everything between and "." as the library name root. */ char * libname; libname = strdup(filename + prefix_length); if (libname == NULL) *error_p = TRUE; else { libname[lastdot - filename - prefix_length] = '\0'; if (strlen(dllverstr) > 0) { p = strstr(libname, dllverstr); if (p) { if (libname + strlen(libname) - strlen(dllverstr) == p) { *p = '\0'; } } } if (strlen(libname) == 0) { *valid_library_p = FALSE; strfree(libname); } else *valid_library_p = TRUE; } *libname_p = libname; } } } } static void parse_filepath(const char * const filepath, const char ** const directory_p, const char ** const filename_p, bool * const error_p) { /*---------------------------------------------------------------------------- Extract the directory and filename components of the filepath 'filepath' and return them in newly malloc'ed storage pointed to by '*directory_p' and '*filename_p'. If there is no directory component, return a null string for it. -----------------------------------------------------------------------------*/ char *directory; char *lastslash; /* Pointer to last slash in 'filepath', or null if none */ lastslash = strrchr(filepath, '/'); if (lastslash == NULL) { /* There's no directory component; the filename starts at the beginning of the filepath */ *filename_p = strdup(filepath); if (*filename_p == NULL) *error_p = TRUE; else { directory = strdup(""); if (directory == NULL) { *error_p = TRUE; strfree(*filename_p); } else *error_p = FALSE; } } else { /* Split the string at the slash we just found, into filename and directory */ *filename_p = strdup(lastslash+1); if (*filename_p == NULL) *error_p = TRUE; else { directory = strdup(filepath); if (directory == NULL) { *error_p = TRUE; strfree(*filename_p); } else { *error_p = FALSE; directory[lastslash - filepath] = '\0'; } } } *directory_p = directory; } static void doOptions(const char * const filepath, const char * const directory, const char * const libname, bool const runtime, bool const explicit, bool const staticlib, const char ** const optionsP) { char * options; char * linkopt; if (strlen(directory) == 0) { linkopt = malloc(strlen(libname) + 10); sprintf(linkopt, "-l%s", libname); } else { if (explicit) linkopt = strdup(filepath); else { linkopt = malloc(strlen(directory) + strlen(libname) + 10); sprintf(linkopt, "-L%s -l%s", directory, libname); } } if (runtime && !staticlib && strlen(directory) > 0) { options = malloc(strlen(linkopt) + strlen(directory) + 10); sprintf(options, "%s -R%s", linkopt, directory); } else options = strdup(linkopt); strfree(linkopt); *optionsP = options; } static void processOneLibrary(const char * const filepath, bool const runtime, bool const explicit, const char ** const optionsP, bool * const errorP) { /*---------------------------------------------------------------------------- Process the library with filepath 'filepath'. Return the resulting linker option string as a newly malloced null-terminated string at *optionsP. -----------------------------------------------------------------------------*/ const char * directory; /* Directory component of 'filepath' */ const char * filename; /* Filename component of 'filepath' */ parse_filepath(filepath, &directory, &filename, errorP); if (!*errorP) { const char *libname; /* Library name component of 'filename'. e.g. xyz in libxyz.so */ bool valid_library; /* Our argument is a valid library filepath that can be converted to -l/-L notation. */ bool staticlib; /* Our argument appears to name a static library. */ parse_filename(filename, &libname, &valid_library, &staticlib, errorP); if (!*errorP) { if (valid_library) { doOptions(filepath, directory, libname, runtime, explicit, staticlib, optionsP); strfree(libname); } else *optionsP = strdup(filepath); } strfree(directory); strfree(filename); } } int main(int argc, char **argv) { bool error; bool runtime; /* -runtime option has been seen */ bool quiet; /* -quiet option has been seen */ int retval; unsigned int arg; /* Index into argv[] of argument we're processing */ char outputLine[1024]; strcpy(outputLine, ""); /* initial value */ runtime = FALSE; /* initial value */ quiet = FALSE; /* initial value */ error = FALSE; /* no error yet */ for (arg = 1; arg < argc && !error; arg++) { if (strcmp(argv[arg], "-runtime") == 0) runtime = TRUE; else if (strcmp(argv[arg], "-quiet") == 0) quiet = TRUE; else { const char * options; processOneLibrary(argv[arg], runtime, explicit, &options, &error); if (!error) { if (strlen(outputLine) + strlen(options) + 1 + 1 > sizeof(outputLine)) error = TRUE; else { strcat(outputLine, " "); strcat(outputLine, options); } strfree(options); } } } if (error) { fprintf(stderr, "serious libopt error prevented parsing library " "names. Invalid input to libopt is NOT the problem.\n"); retval = 10; } else { fputs(outputLine, stdout); retval = 0; } return retval; } advanced/buildtools/configure.pl0000755000175000001440000022605612347602001016173 0ustar ericusers#!/usr/bin/perl -w require 5.000; use strict; use English; use File::Basename; use Cwd 'abs_path'; use Fcntl; use Config; #use File::Temp "tempfile"; Not available before Perl 5.6.1 my ($TRUE, $FALSE) = (1,0); # This program generates config.mk, which is included by all of the # Netpbm makefiles. You run this program as the first step in building # Netpbm. (The second step is 'make'). # This program is only a convenience. It is supported to create # config.mk any way you want. In fact, an easy way is to copy # config.mk.in and follow the instructions in the comments therein # to uncomment certain lines and make other changes. # Note that if you invoke 'make' without having first run 'configure', # the make will call 'configure' itself when it finds # 'config.mk' missing. That might look a little messy to the # user, but it isn't the normal build process. # The argument to this program is the filepath of the config.mk.in # file. If unspecified, the default is 'config.mk.in' in the # Netpbm source directory. # For explanations of the stuff we put in the make files, see the comments # in config.mk.in. # $testCc is the command we use to do test compiles. Note that test # compiles are never more than heuristics, because we may be configuring # a build that will happen on a whole different system, which will build # programs to run on a third system. my $testCc; # $warned is logical. It means we have issued a warning to Standard Output. my $warned; ############################################################################## # # Implementation note: # # At one time, we thought we had to add /usr/local/lib and /usr/local/include # to the path on some platforms because they needed it and didn't include # it in the default compiler search path. But then we had reason to doubt # that was really required, so removed the function on 04.03.15 and never # had any complaint in the next 3 years. ############################################################################## #****************************************************************************** # # SUBROUTINES # #***************************************************************************** sub autoFlushStdout() { my $oldFh = select(STDOUT); $OUTPUT_AUTOFLUSH = $TRUE; select($oldFh); } sub prompt($$) { my ($prompt, $default) = @_; my $defaultPrompt = defined($default) ? $default : "?"; print("$prompt [$defaultPrompt] ==> "); my $response = ; if (defined($response)) { chomp($response); if ($response eq "" && defined($default)) { $response = $default; } } else { print("\n"); die("End of file on Standard Input when expecting response to prompt"); } return $response; } sub promptYesNo($) { my ($default) = @_; my $retval; while (!defined($retval)) { my $response = prompt("(y)es or (n)o", $default); if (uc($response) =~ m{ ^ (Y|YES) $ }x) { $retval = $TRUE; } elsif (uc($response) =~ m{ ^ (N|NO) $ }x) { $retval = $FALSE; } else { print("'$response' isn't one of the choices. \n" . "You must choose 'yes' or 'no' (or 'y' or 'n').\n"); } } return $retval; } sub flow($) { my ($unflowed) = @_; #----------------------------------------------------------------------------- # Return the text $unflowed, split with newlines into 72 character lines. # We assum $unflowed is pure text, without any kind of formatting characters # such as newlines. #----------------------------------------------------------------------------- my $retval; my @words = split(m{\s+}, $unflowed); my $currentLine; $currentLine = ""; foreach my $word (@words) { my $mustSpill; if (length($currentLine) == 0) { $currentLine = $word; $mustSpill = $FALSE; } else { my $separator; if (substr($currentLine, -1) eq '.') { $separator = " "; } else { $separator = " "; } if (length($currentLine) + length($separator) + length($word) <= 72) { $currentLine .= $separator; $currentLine .= $word; $mustSpill = $FALSE; } else { $mustSpill = $TRUE; } } if ($mustSpill) { $retval .= $currentLine; $retval .= "\n"; $currentLine = $word; } } $retval .= $currentLine; $retval .= "\n"; return $retval; } sub warnUser($) { my ($warning) = @_; #----------------------------------------------------------------------------- # Warn the user the build might fail, with explanation "$warning". #----------------------------------------------------------------------------- print("************************************" . "************************************\n"); print flow("WARNING: $warning"); print("************************************" . "************************************\n"); $warned = $TRUE; } sub tmpdir() { # This is our approximation of File::Spec->tmpdir(), which became part of # basic Perl some time after Perl 5.005_03. my $retval; if ($ENV{'TMPDIR'}) { $retval = $ENV{'TMPDIR'}; } elsif ($ENV{'TEMP'}) { $retval = $ENV{'TEMP'}; } else { if ($Config{'osvers'} eq 'djgpp') { $retval = '/dev/env/DJDIR/tmp'; } else { if (-d('/tmp')) { $retval = '/tmp'; } } } return $retval; } sub tempFile($) { # Here's what we'd do if we could expect Perl 5.6.1 or later, instead # of calling this subroutine: # my ($cFile, $cFileName) = tempfile("netpbmXXXX", # SUFFIX=>".c", # DIR=>File::Spec->tmpdir(), # UNLINK=>0); my ($suffix) = @_; my $fileName; local *file; # For some inexplicable reason, must be local, not my my $tmpdir = tmpdir(); if (!defined($tmpdir)) { print STDERR ("Cannot determine what directory to use for " . "temporary files. " . "Set TMPDIR environment variable to fix this.\n"); exit(1); } else { if (!-d($tmpdir)) { print STDERR ("Temporary file directory '$tmpdir' does not " . "exist. Create it or set TMPDIR environment " . "variable appropriately\n"); exit(1); } else { for (my $i = 0; $i < 50 && !defined($fileName); ++$i) { my $trialFileName = tmpdir() . "/netpbm" . $i . $suffix; my $success = sysopen(*file, $trialFileName, O_RDWR|O_CREAT|O_EXCL); if ($success) { $fileName = $trialFileName; } } if (!defined($fileName)) { print STDERR ("Unable to create a temporary file in " . "directory '$tmpdir'\n"); exit(1); } } } return(*file, $fileName); } sub commandExists($) { my ($command) = @_; #----------------------------------------------------------------------------- # Return TRUE iff a shell command $command exists. #----------------------------------------------------------------------------- # Note that it's significant that the redirection on the following # causes it to be executed in a shell. That makes the return code # from system() a lot different than if system() were to try to # execute the program directly. return(system("$command 1/dev/null 2>/dev/null")/256 != 127); } sub chooseTestCompiler($$) { my ($compiler, $testCcR) = @_; my $cc; if ($ENV{'CC'}) { $cc = $ENV{'CC'}; } else { if (commandExists('cc')) { $cc = 'cc'; } elsif (commandExists("gcc")) { $cc = 'gcc'; } } $$testCcR = $cc; } sub testCflags() { my $cflags; $cflags = ""; # initial value if ($ENV{"CPPFLAGS"}) { $cflags = $ENV{"CPPFLAGS"}; } else { $cflags = ""; } if ($ENV{"CFLAGS"}) { $cflags .= " " . $ENV{"CFLAGS"}; } return $cflags; } sub testCompile($$$) { my ($cflags, $cSourceCodeR, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile of the program in @{$cSourceCodeR}. # # Return $$successR == $TRUE iff the compile succeeds (exit code 0). #----------------------------------------------------------------------------- my ($cFile, $cFileName) = tempFile(".c"); print $cFile @{$cSourceCodeR}; my ($oFile, $oFileName) = tempFile(".o"); # Note: we tried using /dev/null for the output file and got complaints # from the Sun compiler that it has the wrong suffix. 2002.08.09. my $compileCommand = "$testCc -c -o $oFileName $cflags $cFileName"; print ("Doing test compile: $compileCommand\n"); my $rc = system($compileCommand); unlink($oFileName); close($oFile); unlink($cFileName); close($cFile); $$successR = ($rc == 0); } sub testCompileLink($$$) { my ($flags, $cSourceCodeR, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile and link of the program in @{$cSourceCodeR}. # # Return $$successR == $TRUE iff the compile succeeds (exit code 0). #----------------------------------------------------------------------------- my ($cFile, $cFileName) = tempFile('.c'); print $cFile @{$cSourceCodeR}; my ($oFile, $oFileName) = tempFile(''); # Note that $flags may contain -l options, which where static linking # is involved have to go _after_ the base object file ($oFileName). my $compileCommand = "$testCc -o $oFileName $cFileName $flags"; print ("Doing test compile/link: $compileCommand\n"); my $rc = system($compileCommand); unlink($oFileName); close($oFile); unlink($cFileName); close($cFile); $$successR = ($rc == 0); } sub displayIntroduction() { print("This is the Netpbm configurator. It is an interactive dialog " . "that\n"); print("helps you build the file 'config.mk' and prepare to build "); print("Netpbm.\n"); print("\n"); print("Do not be put off by all the questions. Configure gives you " . "the \n"); print("opportunity to make a lot of choices, but you don't have to. " . "If \n"); print("you don't have reason to believe you're smarter than Configure,\n"); print("just take the defaults (hit ENTER) and don't sweat it.\n"); print("\n"); print("If you are considering having a program feed answers to the " . "questions\n"); print("below, please read doc/INSTALL, because that's probably the " . "wrong thing to do.\n"); print("\n"); print("Hit ENTER to begin.\n"); my $response = ; } sub askAboutCygwin() { print("Are you building in/for the Cygwin environment?\n"); print("\n"); my $default; if ($OSNAME eq "cygwin") { $default = "y"; } else { $default = "n"; } my $retval = promptYesNo($default); return $retval; } sub askAboutDjgpp() { print("Are you building in/for the DJGPP environment?\n"); print("\n"); my $default; if ($OSNAME eq "dos") { $default = "y"; } else { $default = "n"; } my $retval = promptYesNo($default); } sub computePlatformDefault($) { my ($defaultP) = @_; if ($OSNAME eq "linux") { $$defaultP = "gnu"; } elsif ($OSNAME eq "cygwin") { $$defaultP = "win"; } elsif ($OSNAME eq "dos") { # DJGPP says "dos" $$defaultP = "win"; } elsif ($OSNAME eq "aix" || $OSNAME eq "freebsd" || $OSNAME eq "darwin" || $OSNAME eq "amigaos") { $$defaultP = $OSNAME; } elsif ($OSNAME eq "solaris") { $$defaultP = "sun"; } elsif ($OSNAME eq "dec_osf") { $$defaultP = "tru64"; } else { print("Unrecognized OSNAME='$OSNAME'. No default possible\n"); } # OK - if you know what $OSNAME is on any other platform, send me a patch! } sub getPlatform() { my $platform; my $default; computePlatformDefault(\$default); print("Which of the following best describes your platform?\n"); print("gnu GNU/Linux\n"); print("win Windows/DOS (Cygwin, DJGPP, Mingw32)\n"); print("sun Solaris or SunOS\n"); print("hp HP-UX\n"); print("aix AIX\n"); print("tru64 Tru64\n"); print("irix Irix\n"); print("bsd NetBSD, BSD/OS\n"); print("openbsd OpenBSD\n"); print("freebsd FreeBSD\n"); print("darwin Darwin or Mac OS X\n"); print("amigaos Amiga\n"); print("unixware Unixware\n"); print("sco SCO OpenServer\n"); print("beos BeOS\n"); print("none none of these are even close\n"); print("\n"); my $response = prompt("Platform", $default); my %platform = ("gnu" => "GNU", "sun" => "SOLARIS", "hp" => "HP-UX", "aix" => "AIX", "tru64" => "TRU64", "irix" => "IRIX", "win" => "WINDOWS", "beos" => "BEOS", "bsd" => "NETBSD", "openbsd" => "OPENBSD", "freebsd" => "FREEBSD", "unixware" => "UNIXWARE", "sco" => "SCO", "darwin" => "DARWIN", "amigaos" => "AMIGA", "none" => "NONE" ); $platform = $platform{$response}; if (!defined($platform)) { print("'$response' isn't one of the choices.\n"); exit 8; } my $subplatform; if ($platform eq "WINDOWS") { my ($djgpp, $cygwin); if ($OSNAME eq "dos") { $djgpp = askAboutDjgpp(); if ($djgpp) { $cygwin = $FALSE; } else { $cygwin = askAboutCygwin(); } } else { $cygwin = askAboutCygwin(); if ($cygwin) { $djgpp = $FALSE; } else { $djgpp = askAboutDjgpp(); } } if ($cygwin) { $subplatform = "cygwin"; } elsif ($djgpp) { $subplatform = "djgpp"; } else { $subplatform = "other"; } } return($platform, $subplatform); } sub getGccChoiceFromUser($) { my ($platform) = @_; my $retval; print("GNU compiler or native operating system compiler (cc)?\n"); print("\n"); my $default; if ($platform eq "SOLARIS" || $platform eq "SCO" ) { $default = "gcc"; } else { $default = "cc"; } while (!defined($retval)) { my $response = prompt("gcc or cc", $default); if ($response eq "gcc") { $retval = "gcc"; } elsif ($response eq "cc") { $retval = "cc"; } else { print("'$response' isn't one of the choices. \n" . "You must choose 'gcc' or 'cc'.\n"); } } if ($retval eq 'gcc' && !commandExists('gcc')) { warnUser("WARNING: You selected the GNU compiler, " . "but do not appear to have a program " . "named 'gcc' in your PATH. This may " . "cause trouble later. You may need to " . "set the CC environment variable or CC " . "makefile variable or install 'gcc'"); } print("\n"); return $retval; } sub getCompiler($$$) { my ($platform, $subplatform, $compilerR) = @_; #----------------------------------------------------------------------------- # Here are some of the issues surrounding choosing a compiler: # # - It's not just the name of the program we need -- different compilers # need different options. # # - There are basically two choices on any system: native compiler or # GNU compiler. That's all this program recognizes, anyway. On some, # native _is_ GNU, and we return 'gcc'. # # - A user may well have various compilers. Different releases, using # different standard libraries, for different target machines, etc. # # - The CC environment variable tells the default compiler. # # - In the absence of a CC environment variable, 'cc' is the default # compiler. # # - The user must be able to specify the compiler by overriding the CC # make variable (e.g. make CC=gcc2). # # - Configure needs to do test compiles. The test is best if it uses # the same compiler that the build eventually will use, but it's # useful even if not. # # The value this subroutine returns is NOT the command name to invoke the # compiler. It is simply "cc" to mean native compiler or "gcc" to mean # GNU compiler. #----------------------------------------------------------------------------- my %gccOptionalPlatform = ("SOLARIS" => 1, "TRU64" => 1, "SCO" => 1, "AIX" => 1, "HP-UX" => 1); my %gccUsualPlatform = ("GNU" => 1, "NETBSD" => 1, "OPENBSD" => 1, "FREEBSD" => 1, "DARWIN" => 1, ); if (commandExists('x86_64-w64-mingw32-gcc')) { printf("Do you want to use the Mingw-w64 Cross-Compiler?\n"); if (promptYesNo('y') eq "y") { $$compilerR = 'x86_64-w64-mingw32-gcc'; } } if (!defined($$compilerR)) { if ($gccUsualPlatform{$platform}) { $$compilerR = "gcc"; } elsif ($platform eq "WINDOWS" && $subplatform eq "cygwin") { $$compilerR = "gcc"; } elsif ($gccOptionalPlatform{$platform}) { $$compilerR = getGccChoiceFromUser($platform); } else { $$compilerR = 'cc'; } } } sub gccLinker() { #----------------------------------------------------------------------------- # Determine what linker Gcc on this system appears to use. # # Return either "gnu" or "sun" # # For now, we assume it must be either a GNU linker or Sun linker and # that all Sun linkers are fungible. # # If we can't tell, we assume it is the GNU linker. #----------------------------------------------------------------------------- # First, we assume that the compiler calls 'collect2' as the linker # front end. The specs file might specify some other program, but # it usually says 'collect2'. my $retval; my $collect2 = qx{gcc --print-prog-name=collect2}; if (defined($collect2)) { chomp($collect2); my $linker = qx{$collect2 -v 2>&1}; if (defined($linker) && $linker =~ m{GNU ld}) { $retval = "gnu"; } else { $retval = "sun"; } } else { $retval = "gnu"; } return $retval; } sub getLinker($$$$) { my ($platform, $compiler, $baseLinkerR, $viaCompilerR) = @_; my $baseLinker; if ($platform eq "SOLARIS") { $$viaCompilerR = $TRUE; while (!defined($$baseLinkerR)) { print("GNU linker or SUN linker?\n"); print("\n"); my $default; if ($compiler eq "gcc") { $default = gccLinker(); } else { $default = "sun"; } my $response = prompt("sun or gnu", $default); if ($response eq "gnu") { $$baseLinkerR = "GNU"; } elsif ($response eq "sun") { $$baseLinkerR = "SUN"; } else { print("'$response' isn't one of the choices. \n" . "You must choose 'sun' or 'gnu'.\n"); } print("\n"); } } else { $$viaCompilerR = $TRUE; $$baseLinkerR = "?"; } } sub libSuffix($) { my ($platform) = @_; #----------------------------------------------------------------------------- # Return the traditional suffix for the link-time library on platform # type $platform. # # Note that this information is used mainly for cosmetic purposes in this # this program and the Netpbm build, because the build typically removes # any suffix and uses link options such as "-ltiff" to link the library. # This leaves it up to the linker to supply the actual suffix. #----------------------------------------------------------------------------- my $suffix; if ($platform eq 'WINDOWS') { $suffix = '.dll'; } elsif ($platform eq 'AIX') { $suffix = '.a'; } elsif ($platform eq 'DARWIN') { $suffix = '.dylib'; } else { $suffix = '.so'; } } sub getLibTypes($$$$$$$$) { my ($platform, $subplatform, $default_target, $netpbmlibtypeR, $netpbmlibsuffixR, $shlibprefixlistR, $willBuildSharedR, $staticlib_tooR) = @_; print("Do you want libnetpbm to be statically linked or shared?\n"); print("\n"); my $default = ($default_target eq "merge") ? "static" : "shared"; my ($netpbmlibtype, $netpbmlibsuffix, $shlibprefixlist, $willBuildShared, $staticlib_too); my $response = prompt("static or shared", $default); if ($response eq "shared") { $willBuildShared = $TRUE; if ($platform eq "WINDOWS") { $netpbmlibtype = "dll"; $netpbmlibsuffix = "dll"; if ($subplatform eq "cygwin") { $shlibprefixlist = "cyg lib"; } } elsif ($platform eq "DARWIN") { $netpbmlibtype = "dylib"; $netpbmlibsuffix = "dylib"; } else { if ($platform eq "IRIX") { $netpbmlibtype = "irixshared"; } else { $netpbmlibtype = "unixshared"; } if ($platform eq "AIX") { $netpbmlibsuffix = "a"; } elsif ($platform eq "HP-UX") { $netpbmlibsuffix = "sl"; } else { $netpbmlibsuffix = "so"; } } } elsif ($response eq "static") { $willBuildShared = $FALSE; $netpbmlibtype = "unixstatic"; $netpbmlibsuffix = "a"; # targets, but needed for building # libopt } else { print("'$response' isn't one of the choices. \n" . "You must choose 'static' or 'shared'.\n"); exit 12; } print("\n"); # Note that we can't do both a static and shared library for AIX, because # they both have the same name: libnetpbm.a. if (($netpbmlibtype eq "unixshared" or $netpbmlibtype eq "irixshared" or $netpbmlibtype eq "dll") and $netpbmlibsuffix ne "a") { print("Do you want to build static libraries too (for linking to \n"); print("programs not in the Netpbm package?\n"); print("\n"); my $default = "y"; my $response = prompt("(y)es or (n)o", $default); if (uc($response) =~ /^(Y|YES)$/) { $staticlib_too = "Y"; } elsif (uc($response) =~ /^(N|NO)$/) { $staticlib_too = "N"; } else { print("'$response' isn't one of the choices. \n" . "You must choose 'yes' or 'no' (or 'y' or 'n').\n"); exit 12; } } else { $staticlib_too = "N"; } print("\n"); $$netpbmlibtypeR = $netpbmlibtype; $$netpbmlibsuffixR = $netpbmlibsuffix; $$shlibprefixlistR = $shlibprefixlist; $$willBuildSharedR = $willBuildShared; $$staticlib_tooR = $staticlib_too; } sub inttypesDefault() { my $retval; if (defined($testCc)) { print("(Doing test compiles to choose a default for you -- " . "ignore errors)\n"); my $cflags = testCflags(); my $works; # We saw a system (Irix 5.3 with native IDO, December 2005) on # which sys/types.h defines uint32_t, but not int32_t and other # similar types. We saw a Mac OS X system (January 2006) on which # sys/types sometimes defines uint32_t, but sometimes doesn't. # So we make those last resorts. # int_fastXXX_t are the least likely of all the types to be # defined, so we look for that. # Solaris 8, for example, has a that defines # int32_t and uint32_t, but nothing the defines int_fast32_t. my @candidateList = ("", "", "", ""); for (my $i = 0; $i < @candidateList && !$works; ++$i) { my $candidate = $candidateList[$i]; my @cSourceCode = ( "#include $candidate\n", "int_fast32_t testvar;\n" ); testCompile($cflags, \@cSourceCode, \my $success); if ($success) { $works = $candidate; } } if ($works) { $retval = $works; } else { testCompile($cflags, ["int_fast32_t testvar;"], \my $success); if ($success) { $retval = "NONE"; } else { $retval = '"inttypes_netpbm.h"'; } } print("\n"); } else { $retval = ''; } return $retval; } sub getInttypes($) { my ($inttypesHeaderFileR) = @_; my $gotit; print("What header file defines uint32_t, etc.?\n"); print("\n"); my $default = inttypesDefault(); while (!$gotit) { my $response = prompt("'#include' argument or NONE", $default); if ($response eq "NONE") { $$inttypesHeaderFileR = ''; $gotit = $TRUE; } else { if ($response !~ m{<.+>} && $response !~ m{".+"}) { print("'$response' is not a legal argument of a C #include " . "statement. It must be something in <> or \"\".\n"); } else { $gotit = $TRUE; $$inttypesHeaderFileR = $response; } } } } sub getInt64($$) { my ($inttypes_h, $haveInt64R) = @_; if (defined($testCc)) { print("(Doing test compiles to determine if you have int64 type -- " . "ignore errors)\n"); my $cflags = testCflags(); my $works; my @cSourceCode = ( "#include $inttypes_h\n", "int64_t testvar;\n" ); testCompile($cflags, \@cSourceCode, \my $success); if ($success) { print("You do.\n"); $$haveInt64R = 'Y'; } else { print("You do not. 64-bit code won't be built.\n"); $$haveInt64R = 'N'; } print("\n"); } else { $$haveInt64R = "N"; } } sub determineSseCapability($) { my ($haveEmmintrinR) = @_; if (defined($testCc)) { print("(Doing test compiles to determine if your compiler has SSE " . "intrinsics -- ignore errors)\n"); my $cflags = testCflags(); my $works; my @cSourceCode = ( "#include \n", ); testCompile($cflags, \@cSourceCode, \my $success); if ($success) { print("It does.\n"); $$haveEmmintrinR = $TRUE; } else { print("It does not. Programs will not exploit fast SSE " . "instructions.\n"); $$haveEmmintrinR = $FALSE; } print("\n"); } else { # We conservatively estimate the facility isn't there $$haveEmmintrinR = $FALSE; } } sub getSse($) { my ($wantSseR) = @_; determineSseCapability(\my $haveEmmintrin); my $gotit; print("Use SSE instructions?\n"); print("\n"); my $default = $haveEmmintrin ? "y" : "n"; $$wantSseR = promptYesNo($default); # Another complication in the SSE world is that GNU compiler options # -msse, -msse2, and -march=xxx affect whether the compiler can or will # generate the instructions. When compiling for older processors, the # default for these options is negative ; for newer processors, it is # affirmative. -[no]msse2 determines whether macro __SSE2__ macro is # defined. If it is not, #include fails ( # checks __SSE2__. # The Netpbm build does not mess with these compiler options. If the # user wants something other than the default, he can put it in CFLAGS # in config.mk manually or on the make command line on in CFLAGS_PERSONAL. } # TODO: These should do test compiles to see if the headers are in the # default search path, both to create a default to offer and to issue a # warning after user has chosen. Also test links to test the link library. # It looks like these should all be in the default search paths and were there # just to override defaults in config.mk.in. Since Configure now # creates a default of "standard search path," I'm guessing we don't need # to set these anymore. sub getTiffLibrary($@) { my ($platform, @suggestedHdrDir) = @_; my $tifflib; { my $default = "libtiff" . libSuffix($platform); print("What is your TIFF (graphics format) library?\n"); my $response = prompt("library filename or 'none'", $default); if ($response ne "none") { $tifflib = $response; } if (defined($tifflib) and $tifflib =~ m{/} and !-f($tifflib)) { warnUser("No regular file named '$tifflib' exists."); } } my $tiffhdr_dir; if (defined($tifflib)) { my $default; if (-d("/usr/include/tiff")) { $default = "/usr/include/tiff"; } elsif (-d("/usr/include/libtiff")) { $default = "/usr/include/libtiff"; } else { $default = "default"; } print("Where are the interface headers for it?\n"); my $response = prompt("TIFF header directory", $default); if ($response ne "default") { $tiffhdr_dir = $response; } if (defined($tiffhdr_dir) and !-d($tiffhdr_dir)) { warnUser("No directory named '$tiffhdr_dir' exists."); } } return($tifflib, $tiffhdr_dir); } sub getJpegLibrary($@) { my ($platform, @suggestedHdrDir) = @_; my $jpeglib; { my $default = "libjpeg" . libSuffix($platform); print("What is your JPEG (graphics format) library?\n"); my $response = prompt("library filename or 'none'", $default); if ($response ne "none") { $jpeglib = $response; } } my $jpeghdr_dir; if (defined($jpeglib)) { my $default; if (-d("/usr/include/jpeg")) { $default = "/usr/include/jpeg"; } else { $default = "default"; } print("Where are the interface headers for it?\n"); my $response = prompt("JPEG header directory", $default); if ($response ne "default") { $jpeghdr_dir = $response; } if (defined($jpeghdr_dir) and !-d($jpeghdr_dir)) { warnUser("No directory named '$jpeghdr_dir' exists."); } } return($jpeglib, $jpeghdr_dir); } sub getPngLibrary($@) { my ($platform, @suggestedHdrDir) = @_; my ($pnglib, $pnghdr_dir); if (commandExists('libpng-config')) { # We don't need to ask where Libpng is; there's a 'libpng-config' # That tells exactly how to access it, and the make files will use # that. } else { { my $default = "libpng" . libSuffix($platform); print("What is your PNG (graphics format) library?\n"); my $response = prompt("library filename or 'none'", $default); if ($response ne "none") { $pnglib = $response; } } if (defined($pnglib)) { my $default; if (-d("/usr/include/png")) { $default = "/usr/include/libpng"; } else { $default = "default"; } print("Where are the interface headers for it?\n"); my $response = prompt("PNG header directory", $default); if ($response ne "default") { $pnghdr_dir = $response; } } } return($pnglib, $pnghdr_dir); } sub getZLibrary($@) { my ($platform, @suggestedHdrDir) = @_; my ($zlib, $zhdr_dir); { my $default = "libz" . libSuffix($platform); print("What is your Z (compression) library?\n"); my $response = prompt("library filename or 'none'", $default); if ($response ne "none") { $zlib = $response; } } if (defined($zlib)) { my $default; if (-d("/usr/include/zlib")) { $default = "/usr/include/zlib"; } else { $default = "default"; } print("Where are the interface headers for it?\n"); my $response = prompt("Z header directory", $default); if ($response ne "default") { $zhdr_dir = $response; } } return($zlib, $zhdr_dir); } sub getX11Library($@) { my ($platform, @suggestedHdrDir) = @_; my $x11lib; my $x11hdr_dir; if (system('pkg-config x11 --exists') == 0) { # We don't need to ask where X libraries are; pkg-config knows and the # make files will use that. } else { { my $default; if (-d('/usr/link/X11')) { $default = '/usr/link/X11/libX11' . libSuffix($platform); } elsif (-d('/usr/X11R6/lib')) { $default = '/usr/X11R6/lib/libX11' . libSuffix($platform); } else { $default = "libX11" . libSuffix($platform); } print("What is your X11 (X client) library?\n"); my $response = prompt("library filename or 'none'", $default); if ($response ne "none") { $x11lib = $response; } } if (defined($x11lib)) { my $default; $default = "default"; print("Where are the interface headers for it?\n"); my $response = prompt("X11 header directory", $default); if ($response ne "default") { $x11hdr_dir = $response; } if (defined($x11hdr_dir)) { if (!-d($x11hdr_dir)) { warnUser("No directory named '$x11hdr_dir' exists."); } elsif (!-d("$x11hdr_dir/X11")) { warnUser("Directory '$x11hdr_dir' does not contain " . "the requisite 'X11' subdirectory"); } } } } return($x11lib, $x11hdr_dir); } sub getLinuxsvgaLibrary($@) { my ($platform, @suggestedHdrDir) = @_; my ($svgalib, $svgalibhdr_dir); if ($platform eq "GNU") { my $default; if (-d('/usr/link/svgalib')) { $default = '/usr/link/svgalib/libvga.so'; } elsif (-d('/usr/lib/svgalib')) { $default = '/usr/lib/svgalib/libvga.so'; } elsif (system('ldconfig -p | grep libvga >/dev/null 2>&1') == 0) { # &>/dev/null should work above, but on 12.03.26, it caused the # return value of system() always to be zero! $default = 'libvga.so'; } elsif (-f('/usr/lib/libvga.a')) { $default = '/usr/lib/libvga.a'; } else { $default = 'none'; } print("What is your Svgalib library?\n"); my $response = prompt("library filename or 'none'", $default); if ($response ne 'none') { $svgalib = $response; } } if (defined($svgalib) && $svgalib ne 'none') { my $default; if (-d('/usr/include/svgalib')) { $default = '/usr/include/svgalib'; } else { $default = "default"; } print("Where are the interface headers for it?\n"); my $response = prompt("Svgalib header directory", $default); if ($response ne "default") { $svgalibhdr_dir = $response; } if (defined($svgalibhdr_dir)) { if (!-d($svgalibhdr_dir)) { warnUser("No directory named '$svgalibhdr_dir' exists."); } } } return($svgalib, $svgalibhdr_dir); } sub symlink_command() { my $retval; # Some Windows environments don't have symbolic links (or even a # simulation via a "ln" command, but have a "cp" command which works # in a pinch. Some Windows environments have "ln", but it won't do # symbolic links. if (commandExists("ln")) { # We assume if Perl can do symbolic links, so can Ln, and vice # versa. my $symlink_exists = eval { symlink("",""); 1 }; if ($symlink_exists) { $retval = "ln -s"; } else { $retval = "ln"; } } elsif (commandExists("cp")) { $retval = "cp"; } else { # Well, maybe we just made a mistake. $retval = "ln -s"; } return $retval; } sub help() { print("This is the Netpbm custom configuration program. \n"); print("It is not GNU Configure.\n"); print("\n"); print("There is one optional argument to this program: The " . "name of the file to use as the basis for the config.mk " . "file. Default is 'config.mk.in'\n"); print("\n"); print("Otherwise, the program is interactive.\n"); } sub gnuOptimizeOpt($) { my ($gccCommandName) = @_; #----------------------------------------------------------------------------- # Compute the -O compiler flag appropriate for a GNU system. Ordinarily, # this is just -O3. But many popular GNU systems have a broken compiler # that causes -O3 to generate incorrect code (symptom: pnmtojpeg --quality=95 # generates a syntax error message from shhopt). #----------------------------------------------------------------------------- # I don't know what are exactly the cases that Gcc is broken. I know # Red Hat 7.1 and 7.2 and Mandrake 8.2, running gcc 2.96.1, commonly have # the problem. But it may be limited to a certain subrelease level or # CPU type or other environment. People who have reported the problem have # reported that Gcc 3.0 doesn't have it. Gcc 2.95.3 doesn't have it. # Note that automatic inlining happens at -O3 level, but there are some # subroutines in Netpbm marked for explicit inlining, and we need to disable # that inlining too, so we must go all the way down to -O0. my @gccVerboseResp = `$gccCommandName --verbose 2>&1`; my $brokenCompiler; if (@gccVerboseResp ==2) { if ($gccVerboseResp[1] =~ m{gcc version 2.96}) { $brokenCompiler = $TRUE; } else { $brokenCompiler = $FALSE; } } else { $brokenCompiler = $FALSE; } my $oOpt; if ($brokenCompiler) { print("You appear to have a broken compiler which would produce \n"); print("incorrect code if requested to do inline optimization.\n"); print("Therefore, I am configuring the build to not do inline \n"); print("optimization. This will make some Netpbm programs \n"); print("noticeably slower. If I am wrong about your compiler, just\n"); print("edit config.mk and change -O0 to -O3 near the bottom.\n"); print("\n"); print("The problem is known to exist in the GNU Compiler \n"); print("release 2.96. If you upgrade, you will not have this \n"); print("problem.\n"); print("---------------------------------------------\n"); print("\n"); $oOpt = "-O0"; } else { $oOpt = "-O3"; } return $oOpt; } sub gnuCflags($) { my ($gccCommandName) = @_; return("CFLAGS = " . gnuOptimizeOpt($gccCommandName) . " -ffast-math " . " -pedantic -fno-common " . "-Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit " . "-Wwrite-strings -Wmissing-prototypes -Wundef " . "-Wno-unknown-pragmas\n"); } sub makeCompilerGcc($) { my ($config_mkR) = @_; my $compileCommand = 'gcc'; push(@{$config_mkR}, "CC = $compileCommand\n"); push(@{$config_mkR}, gnuCflags($compileCommand)); } sub findProcessManagement($) { my ($dontHaveProcessMgmtR) = @_; #----------------------------------------------------------------------------- # Return $TRUE iff the system does not have in its default # search path. #----------------------------------------------------------------------------- my $cflags = testCflags(); my @cSourceCode = ( "#include \n", ); testCompile($cflags, \@cSourceCode, \my $success); if (!$success) { print("Your system does not appear to have in its " . "standard compiler include path. Therefore, we will build " . "Netpbm with some function missing (e.g. the pm_system() " . "function in libnetpbm and most of 'pamlookup'\n"); $$dontHaveProcessMgmtR = $TRUE; } else { $$dontHaveProcessMgmtR = $FALSE; } } sub validateLibraries(@) { my @libList = @_; #----------------------------------------------------------------------------- # Check each library name in the list @libList for viability. #----------------------------------------------------------------------------- foreach my $libname (@libList) { if (defined($libname)) { if ($libname =~ m{/} and !-f($libname)) { warnUser("No regular file named '$libname' exists."); } elsif (!($libname =~ m{ .* \. (so|a|sa|sl|dll|dylib) $ }x)) { warnUser("The library name '$libname' does not have " . "a conventional suffix (.so, .a, .dll, etc.)"); } } } } sub warnJpegTiffDependency($$) { my ($jpeglib, $tifflib) = @_; if (defined($tifflib) && !defined($jpeglib)) { warnUser("You say you have a Tiff library, but no Jpeg library. " . "Sometimes the Tiff library prerequires the " . "Jpeg library. If that is the case on your system, " . "you will have some links fail with " . "missing 'jpeg...' symbols. If so, rerun " . "Configure and say you have no Tiff library either."); } } sub testCompileJpeglibH($$) { my ($cflags, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile to see if we can see jpeglib.h. #----------------------------------------------------------------------------- my @cSourceCode = ( "#include \n", "#include \n", "#include \n", ); testCompile($cflags, \@cSourceCode, $successR); } sub testCompileJpegMarkerStruct($$) { my ($cflags, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile to see if struct jpeg_marker_struct is defined in # jpeglib.h. Assume it is already established that the compiler works # and can find jpeglib.h. #----------------------------------------------------------------------------- my @cSourceCode = ( "#include \n", "#include \n", "#include \n", "struct jpeg_marker_struct test;\n", ); testCompile($cflags, \@cSourceCode, $successR); } sub printMissingHdrWarning($$) { my ($name, $hdr_dir) = @_; warnUser("You said the compile-time part of the $name library " . "(the header files) is in " . (defined($hdr_dir) ? "directory '$hdr_dir', " : "the compiler's default search path, ") . "but a test compile failed to confirm that. " . "If your configuration is exotic, the test compile might" . "just be wrong, but otherwise the Netpbm build will fail. " . "To fix this, either install the $name library there " . "or re-run Configure and answer the question about the $name " . "library differently." ); } sub printOldJpegWarning() { warnUser("Your JPEG library appears to be too old for Netpbm. " . "We base this conclusion on the fact that jpeglib.h apparently" . "does not define struct jpeg_marker_struct. " . "If the JPEG library is not " . "Independent Jpeg Group's Version 6b" . "or better, the Netpbm build will fail when it attempts " . "to build the parts that use the JPEG library. " . "If your configuration is exotic, " . "this test may just be wrong. " . "Otherwise, either upgrade your JPEG library " . "or re-run Configure and say you don't have a JPEG library." ); } sub testJpegHdr($) { my ($jpeghdr_dir) = @_; if (defined($testCc)) { my $generalCflags = testCflags(); my $jpegIOpt = $jpeghdr_dir ? "-I$jpeghdr_dir" : ""; testCompileJpeglibH("$generalCflags $jpegIOpt", \my $success); if (!$success) { print("\n"); printMissingHdrWarning("JPEG", $jpeghdr_dir); } else { # We can get to something named jpeglib.h, but maybe it's an old # version we can't use. Check it out. testCompileJpegMarkerStruct("$generalCflags $jpegIOpt", \my $success); if (!$success) { print("\n"); printOldJpegWarning(); } } } } sub warnJpegNotInDefaultPath($) { my ($jpegLib) = @_; print("You said your JPEG library is '$jpegLib', which says it is " . "in the linker's default search path, but a test link we did " . "failed as if it is not. If it isn't, the build will fail\n"); } sub testJpegLink($) { my ($jpegLib) = @_; #----------------------------------------------------------------------------- # See if we can link the JPEG library with the information user gave us. # $jpegLib is the answer to the "what is your JPEG library" prompt, so # it is either a library file name such as "libjpeg.so", in the linker's # default search path, or it is an absolute path name such as # "/usr/jpeg/lib/libjpeg.so". # # We actually test only the default search path case, since users often # incorrectly specify that by taking the default. #----------------------------------------------------------------------------- if ($jpegLib =~ m{( lib | cyg ) (.+) \. ( so | a )$}x) { my $libName = $2; # It's like "libjpeg.so", so is a library in the default search path. # $libName is "jpeg" in this example. # First we test our test tool. We can do this only with GCC. my @emptySource; testCompileLink('-nostartfiles', \@emptySource, \my $controlWorked); if ($controlWorked) { # The "control" case worked. Now see if it still works when we add # the JPEG library. testCompileLink("-nostartfiles -l$libName", \@emptySource, \my $workedWithJpeg); if (!$workedWithJpeg) { warnJpegNotInDefaultPath($jpegLib); } } } } sub testCompileZlibH($$) { my ($cflags, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile to see if we can see zlib.h. #----------------------------------------------------------------------------- my @cSourceCode = ( "#include \n", ); testCompile($cflags, \@cSourceCode, $successR); } sub testCompilePngH($$) { my ($cflags, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile to see if we can see png.h, assuming we can see # zlib.h, which png.h #includes. #----------------------------------------------------------------------------- my @cSourceCode = ( "#include \n", ); testCompile($cflags, \@cSourceCode, $successR); } sub testPngHdr($$) { #----------------------------------------------------------------------------- # Issue a warning if the compiler can't find png.h. #----------------------------------------------------------------------------- my ($pnghdr_dir, $zhdr_dir) = @_; if (defined($testCc)) { my $generalCflags = testCflags(); my $zlibIOpt = $zhdr_dir ? "-I$zhdr_dir" : ""; testCompileZlibH("$generalCflags $zlibIOpt", \my $success); if (!$success) { print("\n"); printMissingHdrWarning("Zlib", $zhdr_dir); } else { my $pngIOpt = $pnghdr_dir ? "-I$pnghdr_dir" : ""; testCompilePngH("$generalCflags $zlibIOpt $pngIOpt", \my $success); if (!$success) { print("\n"); printMissingHdrWarning("PNG", $pnghdr_dir); } } } } sub printBadPngConfigCflagsWarning($) { my ($pngCFlags) = @_; warnUser("'libpng-config' in this environment (a program in your PATH) " . "gives instructions that don't work for compiling for " . "(not linking with) the PNG library. " . "Our test compile failed. " . "This indicates Libpng is installed incorrectly " . "on this system. If so, your Netpbm build, " . "which uses 'libpng-config', will fail. " . "But it might also just be our test that is broken."); } sub pngLinkWorksWithLzLmMsg() { return "When we added \"-lz -lm\" to the linker flags, the link worked. " . "That means the fix for this may be to modify 'libpng-config' " . "so that 'libpng-config --ldflags' includes \"-lz -lm\" " . "in its output. But the right fix may actually " . "be to build libpng differently so that it specifies its dependency " . "on those libraries, or to put those libraries in a different " . "place, or to create missing symbolic links."; } sub printBadPngConfigLdflagsWarning($$) { my ($pngLdFlags, $lzLmSuccess) = @_; warnUser( "'libpng-config' in this environment (a program in your PATH) " . "gives instructions that don't work for linking " . "with the PNG library. Our test link failed. " . "This indicates Libpng is installed incorrectly on this system. " . "If so, your Netpbm build, which uses 'libpng-config', will fail. " . "But it might also just be our test that is broken. " . ($lzLmSuccess ? pngLinkWorksWithLzLmMsg : "") ); } sub testLinkPnglib($$) { my ($generalCflags, $pngCflags) = @_; my @cSourceCode = ( "#include \n", "int main() {\n", "png_create_write_struct(0, NULL, NULL, NULL);\n", "}\n", ); testCompile("$generalCflags $pngCflags", \@cSourceCode, \my $success); if (!$success) { # Since it won't compile, we can't test the link } else { my $pngLdflags = qx{libpng-config --ldflags}; chomp($pngLdflags); testCompileLink("$generalCflags $pngCflags $pngLdflags", \@cSourceCode, \my $success); if (!$success) { testCompileLink("$generalCflags $pngCflags $pngLdflags -lz -lm", \@cSourceCode, \my $lzLmSuccess); printBadPngConfigLdflagsWarning($pngLdflags, $lzLmSuccess); } } } sub testLibpngConfig() { #----------------------------------------------------------------------------- # Issue a warning if the instructions 'libpng-config' give for compiling # with Libpng don't work. #----------------------------------------------------------------------------- my $generalCflags = testCflags(); my $pngCflags = qx{libpng-config --cflags}; chomp($pngCflags); testCompilePngH("$generalCflags $pngCflags", \my $success); if (!$success) { printBadPngConfigCflagsWarning($pngCflags); } else { testLinkPnglib($generalCflags, $pngCflags); } } sub testCompileXmlreaderH($$) { my ($cflags, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile to see if we can see xmlreader.h. #----------------------------------------------------------------------------- my @cSourceCode = ( "#include \n", ); testCompile($cflags, \@cSourceCode, $successR); } sub printBadXml2CFlagsWarning($) { my ($xml2CFlags) = @_; warnUser( "'xml2-config' in this environment (a program in your PATH) " . "gives instructions that don't work for compiling for the " . "Libxml2 library. Our test compile failed. " . "This indicates Libxml2 is installed incorrectly on this system. " . "If so, your Netpbm build, which uses 'xml2-config', will fail. " . "But it might also just be our test that is broken. ". "'xml2-config' says to use compiler options '$xml2CFlags'. " ); } sub testCompileXmlReaderTypes($$) { my ($cflags, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile to see if xmlreader.h defines xmlReaderTypes, # assuming we can compile with xmlreader.h in general. #----------------------------------------------------------------------------- my @cSourceCode = ( "#include \n", "xmlReaderTypes dummy;\n", ); testCompile($cflags, \@cSourceCode, $successR); } sub printMissingXmlReaderTypesWarning() { warnUser( "Your libxml2 interface header file does not define the type " . "'xmlReaderTypes', which Netpbm needs. " . "In order to build Netpbm successfully, " . "you must install a more recent version of Libxml2. " ); } sub printNoLibxml2Warning() { warnUser( "You appear not to have Libxml2 installed ('xml2-config' does not " . "exist in your program search PATH). " . "If this is the case at build time, " . "the build will skip building 'svgtopam'." ); } sub testLibxml2Hdr() { #----------------------------------------------------------------------------- # Issue a warning if the instructions 'xml2-config' give for compiling # with Libxml2 don't work. In particular, note whether they get us a # modern enough Libxml2 to have the 'xmlReaderTypes' type. #----------------------------------------------------------------------------- if (commandExists('xml2-config')) { my $generalCflags = testCflags(); my $xml2Cflags = qx{xml2-config --cflags}; chomp($xml2Cflags); testCompileXmlreaderH("$generalCflags $xml2Cflags", \my $success); if (!$success) { printBadXml2CflagsWarning($xml2Cflags); } else { testCompileXmlReaderTypes("$generalCflags $xml2Cflags", \my $success); if (!$success) { printMissingXmlReaderTypesWarning(); } } } else { printNoLibxml2Warning(); } } sub testConfiguration($$$$$$) { my ($jpeglib, $jpeghdr_dir, $pnglib, $pnghdr_dir, $zlib, $zhdr_dir) = @_; if (defined($jpeglib)) { testJpegHdr($jpeghdr_dir); testJpegLink($jpeglib); } if (defined($pnglib) && defined($zlib)) { testPngHdr($pnghdr_dir, $zhdr_dir); } elsif (commandExists('libpng-config')) { testLibpngConfig(); } testLibxml2Hdr(); # TODO: We ought to validate other libraries too. But it's not # that important, because in the vast majority of cases where the # user incorrectly identifies any library, it affects all the # libraries and if the user can get a handle on the JPEG library # problem, he will also solve problems with any other library. } #****************************************************************************** # # MAINLINE # #***************************************************************************** autoFlushStdout(); my $configInPathArg; if (@ARGV > 0) { if ($ARGV[0] =~ "^-") { if ($ARGV[0] eq "--help") { help(); exit(0); } else { die("Unrecognized option: $ARGV[0]"); } } $configInPathArg = $ARGV[0]; } if (stat("config.mk")) { print("Discard existing config.mk?\n"); print("Y or N (N) ==> "); my $answer = ; if (!defined($answer)) { die("\nEnd of file on Standard Input"); } chomp($answer); if (uc($answer) ne "Y") { print("Aborting at user request.\n"); exit(1); } } print("\n"); displayIntroduction(); my ($platform, $subplatform) = getPlatform(); print("\n"); if ($platform eq "NONE") { print("You will have to construct config.mk manually. To do \n"); print("this, copy config.mk.in as config.mk, and then \n"); print("edit it. Follow the instructions and examples in the file. \n"); print("Please report your results to the Netpbm maintainer so he \n"); print("can improve the configure program. \n"); exit; } getCompiler($platform, $subplatform, \my $compiler); getLinker($platform, $compiler, \my $baseLinker, \my $linkViaCompiler); chooseTestCompiler($compiler, \$testCc); my $netpbmlib_runtime_path; # Undefined if the default from config.mk.in is acceptable. if ($platform eq "SOLARIS" or $platform eq "IRIX" or $platform eq "DARWIN" or $platform eq "NETBSD" or $platform eq "AMIGA") { print("Where will the Netpbm shared library reside once installed?\n"); print("Enter 'default' if it will reside somewhere that the shared\n"); print("library loader will find it automatically. Otherwise, \n"); print("this directory will get built into the Netpbm programs.\n"); print("\n"); my $default = "default"; my $response = prompt("Netpbm shared library directory", $default); if ($response eq "default") { $netpbmlib_runtime_path = ""; } else { $netpbmlib_runtime_path = $response; } } my $default_target; print("Do you want a regular build or a merge build?\n"); print("If you don't know what this means, " . "take the default or see doc/INSTALL\n"); print("\n"); { my $default = "regular"; my $response = prompt("regular or merge", $default); if ($response eq "regular") { $default_target = "nonmerge"; } elsif ($response eq "merge") { $default_target = "merge"; } else { print("'$response' isn't one of the choices. \n" . "You must choose 'regular' or 'merge'.\n"); exit 12; } } print("\n"); getLibTypes($platform, $subplatform, $default_target, \my $netpbmlibtype, \my $netpbmlibsuffix, \my $shlibprefixlist, \my $willBuildShared, \my $staticlib_too); getInttypes(\my $inttypesHeaderFile); getInt64($inttypesHeaderFile, \my $haveInt64); getSse(\my $wantSse); findProcessManagement(\my $dontHaveProcessMgmt); #****************************************************************************** # # FIND THE PREREQUISITE LIBRARIES # #***************************************************************************** print << 'EOF'; The following questions concern the subroutine libraries that are Netpbm prerequisites. Every library has a compile-time part (header files) and a link-time part. In the case of a shared library, these are both part of the "development" component of the library, which may be separately installable from the runtime shared library. For each library, you must give the filename of the link library. If it is not in your linker's default search path, give the absolute pathname of the file. In addition, you will be asked for the directory in which the library's interface headers reside, and you can respond 'default' if they are in your compiler's default search path. If you don't have the library on your system, you can enter 'none' as the library filename and the builder will skip any part that requires that library. EOF my ($jpeglib, $jpeghdr_dir) = getJpegLibrary($platform); print("\n"); my ($tifflib, $tiffhdr_dir) = getTiffLibrary($platform, $jpeghdr_dir); print("\n"); my ($pnglib, $pnghdr_dir) = getPngLibrary($platform, $tiffhdr_dir, $jpeghdr_dir); print("\n"); my ($zlib, $zhdr_dir) = getZLibrary($platform, $pnghdr_dir, $tiffhdr_dir, $jpeghdr_dir); print("\n"); my ($x11lib, $x11hdr_dir) = getX11Library($platform); print("\n"); my ($linuxsvgalib, $linuxsvgahdr_dir) = getLinuxsvgaLibrary($platform); print("\n"); # We should add the JBIG and URT libraries here too. They're a little # more complicated because there are versions shipped with Netpbm. #****************************************************************************** # # CONFIGURE DOCUMENTATION # #***************************************************************************** print("What URL will you use for the main Netpbm documentation page?\n"); print("This information does not get built into any programs or libraries.\n"); print("It does not make anything actually install that web page.\n"); print("It is just for including in legacy man pages.\n"); print("\n"); my $default = "http://netpbm.sourceforge.net/doc/"; my $netpbm_docurl = prompt("Documentation URL", $default); print("\n"); #****************************************************************************** # # VALIDATE THE CONFIGURATION USER HAS SELECTED # #***************************************************************************** validateLibraries($jpeglib, $tifflib, $pnglib, $zlib); warnJpegTiffDependency($jpeglib, $tifflib); testConfiguration($jpeglib, $jpeghdr_dir, $pnglib, $pnghdr_dir, $zlib, $zhdr_dir, ); #****************************************************************************** # # FIND THE NETPBM SOURCE TREE AND INITIALIZE BUILD TREE # #***************************************************************************** my $defaultConfigInPath; if (-f("GNUmakefile")) { # He's apparently running us in the source tree or an already set up # build directory. $defaultConfigInPath = "config.mk.in"; } else { my $srcdir; my $done; $done = $FALSE; while (!$done) { print("Where is the Netpbm source code?\n"); $srcdir = prompt("Netpbm source directory", abs_path(dirname($0) . "/..")); if (-f("$srcdir/GNUmakefile")) { $done = $TRUE; } else { print("That doesn't appear to contain Netpbm source code.\n"); print("There is no file named 'GNUmakefile' in it.\n"); print("\n"); } } unlink("GNUmakefile"); symlink("$srcdir/GNUmakefile", "GNUmakefile"); unlink("Makefile"); symlink("$srcdir/Makefile", "Makefile"); open(SRCDIR, ">srcdir.mk"); print(SRCDIR "SRCDIR = $srcdir\n"); close(SRCDIR); $defaultConfigInPath = "$srcdir/config.mk.in"; } #****************************************************************************** # # BUILD config.mk # #***************************************************************************** my @config_mk; # This is the complete config.mk contents. We construct it here # and ultimately write the whole thing out as config.mk. # First, we just read the 'config.mk.in' in my $configInPath; if (defined($configInPathArg)) { $configInPath = $configInPathArg; } else { $configInPath = $defaultConfigInPath; } open (CONFIG_IN,"<$configInPath") or die("Unable to open file '$configInPath' for input."); @config_mk = ; unshift(@config_mk, "####This file was automatically created by 'configure.'\n", "####Many variables are set twice -- a generic setting, then \n", "####a system-specific override at the bottom of the file.\n", "####\n"); close(CONFIG_IN); # Now, add the variable settings that override the default settings that are # done by the config.mk.in contents. push(@config_mk, "\n\n\n\n"); push(@config_mk, "####Lines above were copied from config.mk.in " . "by 'configure'.\n"); push(@config_mk, "####Lines below were added by 'configure' based on " . "the $platform platform.\n"); if (defined($subplatform)) { push(@config_mk, "####subplatform '$subplatform'\n"); } push(@config_mk, "DEFAULT_TARGET = $default_target\n"); push(@config_mk, "NETPBMLIBTYPE=$netpbmlibtype\n"); push(@config_mk, "NETPBMLIBSUFFIX=$netpbmlibsuffix\n"); if (defined($shlibprefixlist)) { push(@config_mk, "SHLIBPREFIXLIST=$shlibprefixlist\n"); } push(@config_mk, "STATICLIB_TOO=$staticlib_too\n"); if (defined($netpbmlib_runtime_path)) { push(@config_mk, "NETPBMLIB_RUNTIME_PATH=$netpbmlib_runtime_path\n"); } if ($platform eq "GNU") { if (!commandExists("cc") && commandExists("gcc")) { makeCompilerGcc(\@config_mk); } else { push(@config_mk, gnuCflags('cc')); } # The merged programs have a main_XXX subroutine instead of main(), # which would cause a warning with -Wmissing-declarations or # -Wmissing-prototypes. push(@config_mk, "CFLAGS_MERGE = " . "-Wno-missing-declarations -Wno-missing-prototypes\n"); push(@config_mk, "LDRELOC = ld --reloc\n"); push(@config_mk, "LINKER_CAN_DO_EXPLICIT_LIBRARY=Y\n"); } elsif ($platform eq "SOLARIS") { push(@config_mk, 'LDSHLIB = -Wl,-Bdynamic,-G,-h,$(SONAME)', "\n"); push(@config_mk, 'NEED_RUNTIME_PATH = Y', "\n"); if ($compiler eq "cc") { push(@config_mk, "CFLAGS = -O\n"); push(@config_mk, "CFLAGS_SHLIB = -Kpic\n"); } else { makeCompilerGcc(\@config_mk); } # Before Netpbm 10.20 (January 2004), we set this to -R for # $compiler == cc and -rpath otherwise. But now we know that the GNU # compiler can also invoke a linker that needs -R, so we're more flexible. if ($baseLinker eq "GNU") { push(@config_mk, "RPATHOPTNAME = -rpath\n"); } else { push(@config_mk, "RPATHOPTNAME = -R\n"); } push(@config_mk, "NETWORKLD = -lsocket -lnsl\n"); } elsif ($platform eq "HP-UX") { if ($compiler eq "gcc") { makeCompilerGcc(\@config_mk); push(@config_mk, "CFLAGS += -fPIC\n"); push(@config_mk, "LDSHLIB = -shared -fPIC\n"); push(@config_mk, 'LDFLAGS += -Wl,+b,/usr/pubsw/lib', "\n"); } else { # We don't know what to do here. We used to (before 10.20) just # just assume the compiler was gcc. We know that the gcc stuff # above does NOT work for HP native compiler. push(@config_mk, "LDSHLIB =\n"); } } elsif ($platform eq "AIX") { push(@config_mk, 'LDFLAGS += -L /usr/pubsw/lib', "\n"); if ($compiler eq "cc") { # Yes, the -L option implies the runtime as well as linktime library # search path. There's no way to specify runtime path independently. push(@config_mk, "RPATHOPTNAME = -L\n"); push(@config_mk, "LDSHLIB = -qmkshrobj\n"); } else { makeCompilerGcc(\@config_mk); push(@config_mk, "LDSHLIB = -shared\n"); } } elsif ($platform eq "TRU64") { # push(@config_mk, "INSTALL = installbsd\n"); if ($compiler eq "cc") { push(@config_mk, 'CFLAGS = -O2 -std1', "\n"); push(@config_mk, "LDFLAGS = -call_shared -oldstyle_liblookup " . "-L/usr/local/lib\n"); push(@config_mk, "LDSHLIB = -shared -expect_unresolved \"*\"\n"); } else { # We've never tested this. This is just here to give a user a # headstart on submitting to us the necessary information. 2002.07.04. push(@config_mk, "CC = gcc\n"); push(@config_mk, 'CFLAGS = -O3', "\n"); push(@config_mk, "LDSHLIB = -shared\n"); } # Between May 2000 and July 2003, we had -DLONG_32 in these options. # We took it out because it generated bad code for a TRU64 user in # July 2003 whose system has 64 bit long and 32 bit int. It affects # only Ppmtompeg and it isn't clear that using long instead of int is # ever right anyway. push(@config_mk, "OMIT_NETWORK = Y\n"); push(@config_mk, "LINKER_CAN_DO_EXPLICIT_LIBRARY=Y\n"); } elsif ($platform eq "IRIX") { # push(@config_mk, "INSTALL = install\n"); push(@config_mk, "MANPAGE_FORMAT = cat\n"); push(@config_mk, "RANLIB = true\n"); push(@config_mk, "CFLAGS = -n32 -O3 -fullwarn\n"); push(@config_mk, "LDFLAGS = -n32\n"); push(@config_mk, "LDSHLIB = -shared -n32\n"); } elsif ($platform eq "WINDOWS") { if ($subplatform eq "cygwin") { makeCompilerGcc(\@config_mk); } push(@config_mk, "EXE = .exe\n"); push(@config_mk, "OMIT_NETWORK = Y\n"); # # Though it may not have the link as "ginstall", "install" in a Windows # # Unix environment is usually GNU install. # my $ginstall_result = `ginstall --version 2>/dev/null`; # if (!$ginstall_result) { # # System doesn't have 'ginstall', so use 'install' instead. # push(@config_mk, "INSTALL = install\n"); # } push(@config_mk, 'SYMLINK = ', symlink_command(), "\n"); push(@config_mk, 'DLLVER=$(NETPBM_MAJOR_RELEASE)', "\n"); push(@config_mk, "LDSHLIB = " . '-shared -Wl,--image-base=0x10000000 -Wl,--enable-auto-import', "\n"); if ($subplatform ne "cygwin") { push(@config_mk, "MSVCRT = Y\n"); } } elsif ($platform eq "BEOS") { push(@config_mk, "LDSHLIB = -nostart\n"); } elsif ($platform eq "OPENBSD") { # vedge@vedge.com.ar says on 2001.04.29 that there are a ton of # undefined symbols in the Fiasco stuff on OpenBSD. So we'll just # cut it out of the build until someone feels like fixing it. push(@config_mk, "BUILD_FIASCO = N\n"); } elsif ($platform eq "FREEBSD") { } elsif ($platform eq "AMIGA") { push(@config_mk, "CFLAGS = -m68020-60 -ffast-math -mstackextend\n"); } elsif ($platform eq "UNIXWARE") { # Nothing to do. } elsif ($platform eq "SCO") { # Got this from "John H. DuBois III" 2002.09.27: push(@config_mk, "RANLIB = true\n"); if ($compiler eq "cc") { push(@config_mk, "CFLAGS = -O\n"); push(@config_mk, "CFLAGS_SHLIB = -O -K pic\n"); push(@config_mk, "LDSHLIB = -G\n"); push(@config_mk, "SHLIB_CLIB =\n"); } else { makeCompilerGcc(\@config_mk); push(@config_mk, "LDSHLIB = -shared\n"); } push(@config_mk, "NETWORKLD = -lsocket -lresolve\n"); } elsif ($platform eq "DARWIN") { push(@config_mk, "CC = cc -no-cpp-precomp\n"); push(@config_mk, gnuCflags('cc')); push(@config_mk, 'CFLAGS_SHLIB = -fno-common', "\n"); my $installNameOpt; if ($netpbmlib_runtime_path eq '') { $installNameOpt = ''; } else { $installNameOpt = '-install_name $(NETPBMLIB_RUNTIME_PATH)/libnetpbm.$(MAJ).dylib'; } push(@config_mk, "LDSHLIB = -dynamiclib $installNameOpt\n"); # push(@config_mk, "INSTALL = install\n"); } else { die ("Internal error: invalid value for \$platform: '$platform'\n"); } if ($linkViaCompiler) { push(@config_mk, "LINKERISCOMPILER = Y\n"); } my $flex_result = `flex --version`; if (!$flex_result) { # System doesn't have 'flex'. Maybe 'lex' will work. See the # make rules for Thinkjettopbm for information on our experiences # with Lexes besides Flex. my $systemRc = system('lex /dev/null 2>&1'); if ($systemRc >> 8 == 127) { print("\n"); print("You do not appear to have the 'flex' or 'lex' pattern \n"); print("matcher generator on your system, so we will not build \n"); print("programs that need it (Thinkjettopbm)\n"); print("\n"); print("Press ENTER to continue.\n"); my $key = ; print("\n"); push(@config_mk, "LEX=\n"); } else { print("\n"); print("Using 'lex' as the pattern matcher generator, " . "since we cannot\n"); print("find 'flex' on your system.\n"); print("\n"); push(@config_mk, "LEX = lex\n"); } } if ($compiler eq 'gcc') { push(@config_mk, "CFLAGS_SHLIB += -fPIC\n"); } if (defined($tiffhdr_dir)) { push(@config_mk, "TIFFHDR_DIR = $tiffhdr_dir\n"); } if (defined($tifflib)) { push(@config_mk, "TIFFLIB = $tifflib\n"); } if (defined($jpeghdr_dir)) { push(@config_mk, "JPEGHDR_DIR = $jpeghdr_dir\n"); } if (defined($jpeglib)) { push(@config_mk, "JPEGLIB = $jpeglib\n"); } if (defined($pnghdr_dir)) { push(@config_mk, "PNGHDR_DIR = $pnghdr_dir\n"); } if (defined($pnglib)) { push(@config_mk, "PNGLIB = $pnglib\n"); } if (defined($zhdr_dir)) { push(@config_mk, "ZHDR_DIR = $zhdr_dir\n"); } if (defined($zlib)) { push(@config_mk, "ZLIB = $zlib\n"); } if (defined($x11hdr_dir)) { push(@config_mk, "X11HDR_DIR = $x11hdr_dir\n"); } if (defined($x11lib)) { push(@config_mk, "X11LIB = $x11lib\n"); } if (defined($linuxsvgahdr_dir)) { push(@config_mk, "LINUXSVGAHDR_DIR = $linuxsvgahdr_dir\n"); } if (defined($linuxsvgalib)) { push(@config_mk, "LINUXSVGALIB = $linuxsvgalib\n"); } if (defined($netpbm_docurl)) { push(@config_mk, "NETPBM_DOCURL = $netpbm_docurl\n"); } if ($inttypesHeaderFile ne '') { push(@config_mk, "INTTYPES_H = $inttypesHeaderFile\n"); } if ($haveInt64 ne 'Y') { push(@config_mk, "HAVE_INT64 = $haveInt64\n"); } if ($wantSse) { push(@config_mk, "WANT_SSE = Y\n"); } if ($dontHaveProcessMgmt) { push(@config_mk, "DONT_HAVE_PROCESS_MGMT = Y\n"); } #****************************************************************************** # # WRITE OUT THE FILE # #***************************************************************************** open(config_mk, ">config.mk") or die("Unable to open config.mk for writing in the current " . "directory."); print config_mk @config_mk; close(config_mk) or die("Error: Close of config.mk failed.\n"); print("\n"); print("We have created the file 'config.mk'. You may want to look \n"); print("at it and edit it to your requirements and taste before doing the \n"); print("make.\n"); print("\n"); print("Now you may proceed with 'make'\n"); if ($warned) { print("BUT: per the previous WARNINGs, don't be surprised if it fails\n"); } print("\n"); exit 0; advanced/buildtools/Makefile0000644000175000001440000000150212347602001015275 0ustar ericusersifeq ($(SRCDIR)x,x) SRCDIR = $(CURDIR)/.. BUILDDIR = $(SRCDIR) endif SUBDIR = buildtools VPATH = .:$(SRCDIR)/$(SUBDIR) include $(BUILDDIR)/config.mk MERGE_OBJECTS = PROGS = libopt typegen endiangen all: $(PROGS) OMIT_BUILDTOOL_RULE = 1 include $(SRCDIR)/common.mk ifdef DLLVER STRIP_DLL_VERSION=-DDLLVERSTR="\"$(DLLVER)\"" endif ifeq ($(LINKER_CAN_DO_EXPLICIT_LIBRARY),Y) EXPLICIT=-DEXPLICIT endif libopt.o: libopt.c $(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) \ -DSHLIBPREFIXLIST="\"$(SHLIBPREFIXLIST)\"" \ $(STRIP_DLL_VERSION) $(EXPLICIT) \ $(CFLAGS_PERSONAL) $(CFLAGS) $(CADD) \ $< typegen.o endiangen.o:%.o:%.c $(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) $< $(BUILDPROGS):%:%.o $(LD_FOR_BUILD) -o $@ $(LDFLAGS_FOR_BUILD) $< distclean clean: cleanlocal .PHONY: cleanlocal cleanlocal: rm -f $(PROGS) advanced/buildtools/installosf0000755000175000001440000000141512347602001015744 0ustar ericusers#!/bin/sh # This program takes parameters as a Netpbm make file might pass to # its $(INSTALL) program and invokes OSF1 Install with the proper # parameters to effect what the make file wants. If your system has # OSF1 Install on it, you can just set the INSTALL variable in # config.mk to "installosf" and 'make install' will work for # you. # Of course, you could also just install Ginstall and forget about this # program. PERMISSIONS=444 while [ $# -gt 0 ]; do if [ "$1" = "-c" ]; then x=x; elif [ "$1" = "-s" ]; then x=x; elif [ "$1" = "-m" ]; then shift PERMISSIONS=$1 elif [ ${SOURCE_FILE}x == x ]; then SOURCE_FILE=$1 else TARGET_DIR=$1 fi shift; done install -f $TARGET_DIR -m $PERMISSIONS $SOURCE_FILE advanced/buildtools/stamp-date0000755000175000001440000000147112347602001015627 0ustar ericusers#!/bin/sh # # Copyright (C) 1993 by Oliver Trepte. # # 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. # DATE=`date` LOGNAME_OR_UNKNOWN=${LOGNAME:-UNKNOWN} USER=${USER:-$LOGNAME_OR_UNKNOWN} if [ $USER = "UNKNOWN" ]; then USER=`whoami` fi echo "/* This file tells the package when it was compiled */" echo "/* DO NOT EDIT - THIS FILE IS MAINTAINED AUTOMATICALLY */" echo "/* Created by the program 'stamp-date' */" echo "#define COMPILE_TIME \"$DATE\"" echo "#define COMPILED_BY \"$USER\"" advanced/buildtools/config_template0000644000175000001440000000217212347602001016724 0ustar ericusers@ This is a template to be processed into a Bourne shell program. @ #!/bin/sh # This program was generated by the Netpbm installer. Its purpose is to # allow other programs to find out about how Netpbm is installed on this # system. This program is supposed to be invoked via the PATH. @ The following @xxx@ strings get replaced by Installnetpbm when it turns @ this template into an actual Bourne shell program. version='@VERSION@' datadir='@DATADIR@' linkdir='@LINKDIR@' includedir='@INCLUDEDIR@' bindir='@BINDIR@' if test $# -eq 0; then echo >&2 "You need to specify one of these options:" echo >&2 " --version" echo >&2 " --datadir" echo >&2 " --linkdir" echo >&2 " --includedir" echo >&2 " --bindir" exit 100 fi case "$1" in --version) echo $version exit 0 ;; --datadir) echo $datadir exit 0 ;; --linkdir) echo $linkdir exit 0 ;; --includedir) echo $includedir exit 0 ;; --bindir) echo $bindir exit 0 ;; *) echo >&2 "Unrecognized option to $0: $1" exit 100 esac advanced/buildtools/endiangen.c0000644000175000001440000000442712347602001015742 0ustar ericusers/*---------------------------------------------------------------------------- endiangen ------------------------------------------------------------------------------ This is a program to create C code that declares the endianness of the machine on which it is run. It generates something like this: #ifndef LITTLE_ENDIAN #define LITTLE_ENDIAN 1234 #endif #ifndef BIG_ENDIAN #define BIG_ENDIAN 4321 #endif #ifndef BYTE_ORDER #define BYTE_ORDER LITTLE_ENDIAN #endif #define BITS_PER_WORD 32 #endif Really good code usually is not sensitive to endianness. But fast, not-so-good code often is. The best way for code to determine endianness is for it to do a runtime cast of an integer to an array of characters and see where the bytes land. But if speed requires even sleazier code than that, use these macros. -----------------------------------------------------------------------------*/ #include #include #define MAX(a,b) ((a) > (b) ? (a) : (b)) enum endianness {ENDIAN_LITTLE, ENDIAN_BIG}; static enum endianness byteOrder(void) { enum endianness retval; union { unsigned char arrayval[2]; unsigned short numval; } testunion; testunion.numval = 3; if (testunion.arrayval[0] == 3) retval = ENDIAN_LITTLE; else retval = ENDIAN_BIG; return retval; } static unsigned int bitsPerLong(void) { return sizeof(long) * 8; } int main(int argc, char **argv) { printf("/* This was generated by the program 'endiangen' */\n"); printf("\n"); printf("/* LITTLE_ENDIAN, BIG_ENDIAN, and BYTE_ORDER " "may come from the C library\n"); printf("via ctype.h. */\n"); printf("#include \n"); printf("#ifndef LITTLE_ENDIAN\n"); printf("#define LITTLE_ENDIAN 1234\n"); printf("#endif\n"); printf("#ifndef BIG_ENDIAN\n"); printf("#define BIG_ENDIAN 4321\n"); printf("#endif\n"); printf("\n"); printf("#ifndef BYTE_ORDER\n"); printf("#define BYTE_ORDER %s\n", byteOrder() == ENDIAN_LITTLE ? "LITTLE_ENDIAN" : "BIG_ENDIAN"); printf("#endif\n"); printf("\n"); printf("#define BITS_PER_LONG %u\n", bitsPerLong()); return 0; } advanced/buildtools/installnetpbm.pl0000755000175000001440000010301112347602001017047 0ustar ericusers#!/usr/bin/perl -w require 5.000; #Note: mkdir() must have 2 arguments as late as 5.005. use strict; use English; use Fcntl; use File::Basename; my ($TRUE, $FALSE) = (1,0); my $cpCommand; #use vars qw($cpCommand); #****************************************************************************** # # SUBROUTINES # #***************************************************************************** sub autoFlushStdout() { my $oldFh = select(STDOUT); $OUTPUT_AUTOFLUSH = $TRUE; select($oldFh); } sub prompt($$) { my ($prompt, $default) = @_; print("$prompt ($default) ==> "); my $response = ; chomp($response); if ($response eq "") { $response = $default; } return $response; } sub getPkgdir() { #----------------------------------------------------------------------------- # Find out from the user where the Netpbm package is (i.e. where # 'make package' put it). #----------------------------------------------------------------------------- my $pkgdir; while (!$pkgdir) { print("Where is the install package you created with " . "'make package'?\n"); my $default = "/tmp/netpbm"; my $response = prompt("package directory", $default); if (!-f("$response/pkginfo")) { print("This does not appear to be a Netpbm install package. \n"); print("A file named $response/pkginfo does not exist.\n"); print("\n"); } else { $pkgdir = $response; } } print("\n"); return $pkgdir; } sub makePrefixDirectory($) { my ($prefixDir) = @_; if ($prefixDir ne "" and !-d($prefixDir)) { print("No directory named '$prefixDir' exists. Do you want " . "to create it?\n"); my $done; while (!$done) { my $response = prompt("Y(es) or N(o)", "Y"); if (uc($response) eq "Y") { my $success = mkdir($prefixDir, 0777); if (!$success) { print("Unable to create directory '$prefixDir'. " . "Error is $ERRNO\n"); } $done = $TRUE; } elsif (uc($response) eq "N") { $done = $TRUE; } } } } sub getPrefix() { print("Enter the default prefix for installation locations. " . "I will use \n"); print("this in generating defaults for the following prompts to " . "save you \n"); print("typing. If you plan to spread Netpbm across your system, \n" . "enter '/'.\n"); print("\n"); my $default; if ($OSNAME eq "cygwin") { $default = "/usr/local"; } elsif ($ENV{OSTYPE} && $ENV{OSTYPE} eq "msdosdjgpp") { $default = "/dev/env/DJDIR"; } else { $default = "/usr/local/netpbm"; } my $response = prompt("install prefix", $default); my $prefix; # Remove possible trailing / if (substr($response,-1,1) eq "/") { $prefix = substr($response, 0, -1); } else { $prefix = $response; } print("\n"); makePrefixDirectory($prefix); return $prefix; } sub getCpCommand() { #----------------------------------------------------------------------------- # Compute the command + options need to do a recursive copy, preserving # symbolic links and file attributes. #----------------------------------------------------------------------------- my $cpCommand; # We definitely need more intelligence here, but we'll need input from # users to do it. Maybe we should just bundle GNU Cp with Netpbm as an # install tool. Maybe we should write a small recursive copy program # that uses more invariant tools, like buildtools/install.sh does for # single files. if (`cp --version 2>/dev/null` =~ m/GNU/) { # It's GNU Cp -- we have options galore, and they're readable. $cpCommand = "cp --recursive --preserve --no-dereference"; } else { # This works on Cp from "4th Berkeley Distribution", July 1994. # Mac OSX has this. # -R means recursive with no dereferencing of symlinks # -p means preserve attributes $cpCommand = "cp -R -p"; } return($cpCommand); } sub getBinDir($) { #----------------------------------------------------------------------------- # Find out from the user where he wants the programs installed, and return # that. #----------------------------------------------------------------------------- my ($prefix) = @_; print("Where do you want the programs installed?\n"); print("\n"); my $binDir; while (!$binDir) { my $default = "$prefix/bin"; my $response = prompt("program directory", $default); if (-d($response)) { $binDir = $response; } else { my $succeeded = mkdir($response, 0777); if (!$succeeded) { print("Unable to create directory '$response'. " . "Error=$ERRNO\n"); } else { $binDir = $response; } } } print("\n"); return $binDir; } sub installProgram($$$) { my ($pkgdir, $prefix, $bindirR) = @_; my $binDir = getBinDir($prefix); print("Installing programs...\n"); my $rc = system("$cpCommand $pkgdir/bin/* $binDir/"); if ($rc != 0) { print("Copy of programs from $pkgdir/bin to $binDir failed.\n"); print("cp return code is $rc\n"); } else { print("Done.\n"); } $$bindirR = $binDir; } sub getLibDir($) { #----------------------------------------------------------------------------- # Find out from the user where he wants the runtime libraries installed and # return that. #----------------------------------------------------------------------------- my ($prefix) = @_; print("Where do you want the shared library installed?\n"); print("\n"); my $libDir; while (!$libDir) { my $default = "$prefix/lib"; my $response = prompt("shared library directory", $default); if (-d($response)) { $libDir = $response; } else { my $succeeded = mkdir($response, 0777); if (!$succeeded) { print("Unable to create directory '$response'. " . "Error=$ERRNO\n"); } else { $libDir = $response; } } } print("\n"); return $libDir; } sub ldconfigExists() { return (system("ldconfig -? >/dev/null 2>/dev/null") >> 8) != 127; } sub crleExists() { return (system("crle -? 2>/dev/null") >> 8) != 127; } sub dirName($) { my ($fileName) = @_; #----------------------------------------------------------------------------- # The directory component of file name $fileName. #----------------------------------------------------------------------------- my @components = split(m{/}, $fileName); pop(@components); if (@components == 1 && $components[0] eq '') { return '/'; } else { return join('/', @components); } } sub ldConfigKnowsDir($) { my ($shlibDir) = @_; #----------------------------------------------------------------------------- # Ldconfig appears to search $shlibDir for shared libraries. # # Our determination is approximate. We just look at whether 'ldconfig' # found anything in $shlibDir the last time it searched. If it searched # $shlibDir and just didn't find anything or $shlibDir has been added to # its search path since then, we'll wrongly conclue that it doesn't search # $shlibDir now. #----------------------------------------------------------------------------- my @ldconfigOutput = split(m{\n}, qx{ldconfig -p}); my $found; foreach (@ldconfigOutput) { if (m{ => \s (.*) $ }x) { my ($fileName) = ($1); if (dirName($fileName) eq $shlibDir) { $found = $TRUE; } } } return $found; } sub warnNonstandardShlibDirLdconfig($) { my ($shlibDir) = @_; #----------------------------------------------------------------------------- # Assuming this is a system that has an 'ldconfig' program, warn the user # if $shlibDir appears not to be in the system shared library search path. #----------------------------------------------------------------------------- # This appears to be a system that uses the GNU libc dynamic linker. # The list of system shared library directories is in /etc/ld.so.conf. # The program Ldconfig searches the directories in that list and # remembers all the shared libraries it found (and some informtaion # about them) in its cache /etc/ld.so.cache, which is what the # dynamic linker uses at run time to find the shared libraries. if (!ldConfigKnowsDir($shlibDir)) { print("You have installed shared libraries in " . "'$shlibDir',\n" . "which does not appear to be a system shared " . "library directory ('ldconfig -p' \n" . "doesn't show any other libraries in there). " . "Therefore, the system may not be\n" . "able to find the Netpbm shared libraries " . "when you run Netpbm programs.\n" . "\n" . "To fix this, you may need to update /etc/ld.so.conf\n" . "\n" . "You may need to use an LD_LIBRARY_PATH " . "environment variable when running Netpbm programs\n" . "\n"); } } sub warnNonstandardShlibDirCrle($) { my ($shlibDir) = @_; #----------------------------------------------------------------------------- # Assuming this is a system that has a 'crle' program, warn the user # if $shlibDir appears not to be in the system shared library search path. #----------------------------------------------------------------------------- # We should use 'crle' here to determine whether $shlibDir is a # system directory. But I don't have a Solaris system to reverse # engineer/test with. if ($shlibDir ne "/lib" && $shlibDir ne "/usr/lib") { print("You have installed shared libraries in " . "'$shlibDir',\n" . "which is not a conventional system shared " . "library directory.\n" . "Therefore, the system may not be able to " . "find the Netpbm\n" . "shared libraries when you run Netpbm programs.\n" . "\n" . "To fix this, you may need to run 'crle -l'.\n" . "\n" . "You may need to use an LD_LIBRARY_PATH " . "environment variable when running Netpbm programs\n" . "\n"); } } sub warnNonstandardShlibDirGeneric($) { my ($shlibDir) = @_; #----------------------------------------------------------------------------- # Without assuming any particular shared library search scheme on this # system, warn if $shlibDir appears not to be in the system shared library # search path. #----------------------------------------------------------------------------- if ($shlibDir ne "/lib" && $shlibDir ne "/usr/lib") { print("You have installed shared libraries in " . "'$shlibDir',\n" . "which is not a conventional system shared " . "library directory.\n" . "Therefore, the system may not be able to " . "find the Netpbm\n" . "shared libraries when you run Netpbm programs.\n" . "\n" . "You may need to use an LD_LIBRARY_PATH " . "environment variable when running Netpbm programs\n" . "\n"); } } sub warnNonstandardShlibDir($) { my ($shlibDir) = @_; if (ldconfigExists()) { warnNonstandardShlibDirLdconfig($shlibDir); } elsif (crleExists()) { warnNonstandardShlibDirCrle($shlibDir); } else { warnNonstandardShlibDirGeneric($shlibDir); } } sub execLdconfig() { #----------------------------------------------------------------------------- # Run Ldconfig. Try with the -X option first, and if that is an invalid # option (which we have seen on an openBSD system), try it without -X. # # -X means "don't create any symlinks." Any symlinks required should be # created as part of installing the library, so we don't need that function # from Ldconfig. And we want to tread as lightly as possible on the # system -- we don't want creating symlinks that have nothing to do with # Netpbm to be a hidden side effect of installing Netpbm. # # Note that this Ldconfig works only if the user installed the Netpbm # library in a standard directory that Ldconfig searches. Note that on # OpenBSD, Ldconfig is hardcoded to search only /usr/lib ever. We could # also do 'ldconfig DIR' to scan the particular directory in which we # installed the Netpbm library. But 1) the effects of this would disappear # the next time the user rebuilds the cache file; and 2) on OpenBSD, this # causes the cache file to be rebuilt from ONLY that directory. On OpenBSD, # you can add the -m option to cause it to ADD the contents of DIR to the # existing cache file. # #----------------------------------------------------------------------------- # Implementation note: We've seen varying completion codes and varying # error messages from different versions of Ldconfig when it fails. my $ldconfigSucceeded; my $ldconfigXResp = `ldconfig -X 2>&1`; if (!defined($ldconfigXResp)) { print("Unable to run Ldconfig.\n"); $ldconfigSucceeded = $FALSE; } elsif ($ldconfigXResp eq "") { $ldconfigSucceeded = $TRUE; } elsif ($ldconfigXResp =~ m{usage}i) { print("Trying Ldconfig again without the -X option...\n"); my $rc = system("ldconfig"); $ldconfigSucceeded = ($rc == 0); } else { print($ldconfigXResp); $ldconfigSucceeded = $FALSE; } if ($ldconfigSucceeded) { print("Ldconfig completed successfully.\n"); } else { print("Ldconfig failed. You will have to fix this later.\n"); } } sub doLdconfig() { #----------------------------------------------------------------------------- # Run Ldconfig where appropriate. #----------------------------------------------------------------------------- if ($OSNAME eq "linux" || ldconfigExists()) { # This is a system where Ldconfig makes sense print("In order for the Netpbm shared library to be found when " . "you invoke \n"); print("A Netpbm program, you must either set an environment " . "variable to \n"); print("tell where to look for it, or you must put its location " . "in the shared \n"); print("library location cache. Do you want to run Ldconfig now " . "to put the \n"); print("Netpbm shared library in the cache? This works only if " . "you have\n"); print("installed the library in a directory Ldconfig knows about.\n"); print("\n"); my $done; $done = $FALSE; while (!$done) { my $response = prompt("Y(es) or N(o)", "Y"); if (uc($response) eq "Y") { execLdconfig(); $done = $TRUE; } elsif (uc($response) eq "N") { $done = $TRUE; } else { print("Invalid response. Enter 'Y' or 'N'\n"); } } } } sub installSharedLib($$$) { my ($pkgdir, $prefix, $libdirR) = @_; if (-d("$pkgdir/lib")) { my $libDir = getLibDir($prefix); print("Installing shared libraries...\n"); my $rc = system("$cpCommand $pkgdir/lib/* $libDir/"); if ($rc != 0) { print("Copy of libraries from $pkgdir/lib to $libDir failed.\n"); print("cp return code is $rc\n"); } else { print("done.\n"); print("\n"); warnNonstandardShlibDir($libDir); doLdconfig(); } $$libdirR = $libDir; } else { print("You did not build a shared library, so I will not " . "install one.\n"); } print("\n"); } sub getLinkDir($) { #----------------------------------------------------------------------------- # Find out from the user where he wants the link-edit libraries installed and # return that. #----------------------------------------------------------------------------- my ($prefix) = @_; print("Where do you want the static link library installed?\n"); print("\n"); my $linkDir; while (!$linkDir) { my $default = "$prefix/lib"; my $response = prompt("static library directory", $default); if (-d($response)) { $linkDir = $response; } else { my $succeeded = mkdir($response, 0777); if (!$succeeded) { print("Unable to create directory '$response'. " . "Error=$ERRNO\n"); } else { $linkDir = $response; } } } print("\n"); return $linkDir; } sub installStaticLib($$$) { my ($pkgdir, $prefix, $linkdirR) = @_; if (-d("$pkgdir/link")) { my $linkDir = getLinkDir($prefix); print("Installing link libraries.\n"); my $rc = system("$cpCommand $pkgdir/link/* $linkDir/"); if ($rc != 0) { print("Copy of files from $pkgdir/link to $linkDir failed.\n"); print("cp return code is $rc\n"); } else { print("done.\n"); } $$linkdirR = $linkDir; } else { print("You did not build a static library, so I will not " . "install one \n"); } } sub getDataDir($) { #----------------------------------------------------------------------------- # Find out from the user where he wants the runtime data files installed and # return that. #----------------------------------------------------------------------------- my ($prefix) = @_; print("Where do you want the data files installed?\n"); print("\n"); my $dataDir; while (!$dataDir) { my $default = "$prefix/lib"; my $response = prompt("data file directory", $default); if (-d($response)) { $dataDir = $response; } else { my $succeeded = mkdir($response, 0777); if (!$succeeded) { print("Unable to create directory '$response'. " . "Error=$ERRNO\n"); } else { $dataDir = $response; } } } print("\n"); return $dataDir; } sub getHdrDir($) { #----------------------------------------------------------------------------- # Find out from the user where he wants the compile-time header files # installed and return that. #----------------------------------------------------------------------------- my ($prefix) = @_; print("Where do you want the library interface header files installed?\n"); print("\n"); my $hdrDir; while (!$hdrDir) { my $default = "$prefix/include"; my $response = prompt("header directory", $default); if (-d($response)) { $hdrDir = $response; } else { my $succeeded = mkdir($response, 0777); if (!$succeeded) { print("Unable to create directory '$response'. " . "Error=$ERRNO\n"); } else { $hdrDir = $response; } } } print("\n"); return $hdrDir; } sub installDataFile($$$) { my ($pkgdir, $prefix, $datadirR) = @_; my $dataDir = getDataDir($prefix); print("Installing data files...\n"); my $rc = system("$cpCommand $pkgdir/misc/* $dataDir/"); if ($rc != 0) { print("copy of data files from $pkgdir/misc to $dataDir " . "failed.\n"); print("cp exit code is $rc\n"); } else { $$datadirR = $dataDir; print("done.\n"); } } sub installHeader($$$) { my ($pkgdir, $prefix, $includedirR) = @_; my $hdrDir = getHdrDir($prefix); print("Installing interface header files...\n"); my $rc = system("$cpCommand $pkgdir/include/* $hdrDir/"); if ($rc != 0) { print("copy of header files from $pkgdir/include to $hdrDir " . "failed.\n"); print("cp exit code is $rc\n"); } else { # Install symbolic links for backward compatibility (because the # netpbm/ subdirectory wasn't used before Netpbm 10.41 (December # 2007). my $rc = system("cd $hdrDir; ln -s netpbm/* ."); if ($rc != 0) { print("Failed to create backward compatibilty symlinks from " . "$hdrDir into $hdrDir/netpbm\n"); print("ln exit code is $rc\n"); } else { print("done.\n"); } } $$includedirR = $hdrDir; } sub getManDir($) { #----------------------------------------------------------------------------- # Find out from the user where he wants the pointer man pages # installed and return that. #----------------------------------------------------------------------------- my ($prefix) = @_; print("Where do you want the man pages installed?\n"); print("\n"); my $manDir; while (!$manDir) { my $default = "$prefix/man"; my $response = prompt("man page directory", $default); if (-d($response)) { $manDir = $response; } else { my $succeeded = mkdir($response, 0777); if (!$succeeded) { print("Unable to create directory '$response'. " . "Error=$ERRNO\n"); } else { $manDir = $response; } } } print("\n"); return $manDir; } sub removeObsoleteManPage($) { my ($mandir) = @_; unlink("$mandir/man1/pgmoil"); unlink("$mandir/man1/pgmnorm"); unlink("$mandir/man1/ppmtojpeg"); unlink("$mandir/man1/bmptoppm"); unlink("$mandir/man1/ppmtonorm"); unlink("$mandir/man1/ppmtouil"); unlink("$mandir/man1/pnmnoraw"); unlink("$mandir/man1/gemtopbm"); unlink("$mandir/man1/pnminterp"); } sub tryToCreateManwebConf($) { my ($manweb_conf_filename) = $@; print("You don't have a /etc/manweb.conf, which is the " . "configuration\n"); print("file for the 'manweb' program, which is a quick way to " . "get to Netpbm\n"); print("documentation. Would you like to create one now?\n"); my $done; while (!$done) { my $response = prompt("create /etc/manweb.conf", "Y"); if (uc($response) eq "Y") { my $successful = open(MANWEB_CONF, ">/etc/manweb.conf"); if (!$successful) { print("Unable to create file /etc/manweb.conf. " . "error = $ERRNO\n"); } else { print(MANWEB_CONF "#Configuration file for Manweb\n"); print(MANWEB_CONF "webdir=/usr/man/web\n"); close(MANWEB_CONF); $done = $TRUE; } } else { $done = $TRUE; } } } sub getWebdir($) { my ($manweb_conf_filename) = @_; #----------------------------------------------------------------------------- # Return the value of the Manweb "web directory," as indicated by the # Manweb configuration file $manweb_conf_filename. # # If that file doesn't exist, or doesn't have a 'webdir' value, or # the 'webdir' value is a chain of directories instead of just one, # we return an undefined value. #----------------------------------------------------------------------------- my $webdir; my $success = open(MANWEB_CONF, "<$manweb_conf_filename"); if (!$success) { print("Unable to open file '$manweb_conf_filename' for reading. " . "error is $ERRNO\n"); } else { while () { chomp(); if (/^\s*#/) { #It's comment - ignore } elsif (/^\s*$/) { #It's a blank line - ignore } elsif (/\s*(\S+)\s*=\s*(\S+)/) { #It looks like "keyword=value" my ($keyword, $value) = ($1, $2); if ($keyword eq "webdir") { # We can't handle a multi-directory path; we're looking # only for a webdir statement naming a sole directory. if ($value !~ m{:}) { $webdir = $value; } } } } close(MANWEB_CONF); } return $webdir } sub userWantsManwebSymlink($$) { my ($webdir, $netpbmWebdir) = @_; print("Your manweb.conf file says top level documentation " . "is in $webdir, \n"); print("but you installed netpbm.url in $netpbmWebdir.\n"); print("Do you want to create a symlink in $webdir now?\n"); my $wants; my $done; while (!$done) { my $response = prompt("create symlink (Y/N)", "Y"); if (uc($response) eq "Y") { $wants = $TRUE; $done = $TRUE; } elsif (uc($response) eq "N") { $wants = $FALSE; $done = $TRUE; } } return $wants; } sub makeInManwebPath($) { my ($netpbmWebdir) = @_; # Now we need /etc/manweb.conf to point to the directory in which we # just installed netpbm.url. if (!-f("/etc/manweb.conf")) { tryToCreateManwebConf("/etc/manweb.conf"); } if (-f("/etc/manweb.conf")) { my $webdir = getWebdir("/etc/manweb.conf"); if (defined($webdir)) { if ($webdir ne $netpbmWebdir) { if (userWantsManwebSymlink($webdir, $netpbmWebdir)) { my $old = "$netpbmWebdir/netpbm.url"; my $new = "$webdir/netpbm.url"; mkdir($webdir, 0777); my $success = symlink($old, $new); if (!$success) { print("Failed to create symbolic link from $new to " . "$old. Error is $ERRNO\n"); } } } } } } sub installManPage($$$) { # Note: This installs the pointer man pages and the netpbm.url file for Manweb. my ($pkgdir, $prefix, $mandirR) = @_; my $manDir = getManDir($prefix); print("Installing man pages...\n"); my $rc = system("$cpCommand $pkgdir/man/* $manDir/"); if ($rc != 0) { print("copy of man pages from $pkgdir/man to $manDir failed.\n"); print("cp exit code is $rc\n"); } else { print("done.\n"); } print("\n"); removeObsoleteManPage($manDir); makeInManwebPath("$manDir/web"); $$mandirR = $manDir; } sub netpbmVersion($) { my ($pkgdir) = @_; my $versionOpened = open(VERSION, "<$pkgdir/VERSION"); my $version; my $error; if (!$versionOpened) { $error = "Unable to open $pkgdir/VERSION for reading. " . "Errno=$ERRNO\n"; } else { $version = ; chomp($version); close(VERSION); } if ($error) { print("Failed to determine the version of Netpbm from the package, " . "so that will not be correct in netpbm.config and netpbm.pc. " . $error . "\n"); $version = "???"; } return $version; } sub processTemplate($$$) { my ($templateR, $infoR, $outputR) = @_; my @output; foreach (@{$templateR}) { if (m{^@}) { # Comment -- ignore it. } else { if (defined($infoR->{VERSION})) { s/\@VERSION\@/$infoR->{VERSION}/; } if (defined($infoR->{BINDIR})) { s/\@BINDIR@/$infoR->{BINDIR}/; } if (defined($infoR->{LIBDIR})) { s/\@LIBDIR@/$infoR-.{LIBDIR}/; } if (defined($infoR->{LINKDIR})) { s/\@LINKDIR@/$infoR->{LINKDIR}/; } if (defined($infoR->{DATADIR})) { s/\@DATADIR@/$infoR->{DATADIR}/; } if (defined($infoR->{INCLUDEDIR})) { s/\@INCLUDEDIR@/$infoR->{INCLUDEDIR}/; } if (defined($infoR->{MANDIR})) { s/\@MANDIR@/$infoR->{MANDIR}/; } push(@output, $_); } } $$outputR = \@output; } sub installConfig($$) { my ($installdir, $templateSubsR) = @_; #----------------------------------------------------------------------------- # Install 'netpbm-config' -- a program you run to tell you things about # how Netpbm is installed. #----------------------------------------------------------------------------- my $error; my $configTemplateFilename = dirname($0) . "/config_template"; my $templateOpened = open(TEMPLATE, "<$configTemplateFilename"); if (!$templateOpened) { $error = "Can't open template file '$configTemplateFilename'.\n"; } else { my @template =